From c5bf1c771fb757e3f04b73b40bd3093d5c24710f Mon Sep 17 00:00:00 2001 From: Justin Baur <19896123+justindbaur@users.noreply.github.com> Date: Tue, 26 Nov 2024 12:42:55 -0500 Subject: [PATCH 1/3] Add more docs to `bitwarden_core` --- crates/bitwarden-core/src/client/client.rs | 13 ++++++ .../src/client/encryption_settings.rs | 10 +++++ crates/bitwarden-core/src/client/flags.rs | 2 + crates/bitwarden-core/src/client/internal.rs | 3 ++ .../src/client/test_accounts.rs | 6 +++ .../src/platform/client_platform.rs | 40 +++++++++++++++++++ .../src/platform/generate_fingerprint.rs | 3 ++ 7 files changed, 77 insertions(+) diff --git a/crates/bitwarden-core/src/client/client.rs b/crates/bitwarden-core/src/client/client.rs index b9bf4c51..788ee99f 100644 --- a/crates/bitwarden-core/src/client/client.rs +++ b/crates/bitwarden-core/src/client/client.rs @@ -18,6 +18,19 @@ pub struct Client { } impl Client { + /// Constructs a new `Client` with the given `settings_input`. + /// + /// # Examples + /// ```rust + /// use bitwarden_core::{Client, ClientSettings, DeviceType}; + /// + /// let client = Client::new(Some(ClientSettings { + /// identity_url: "https://identity.bitwarden.com".to_owned(), + /// api_url: "https://api.bitwarden.com".to_owned(), + /// user_agent: "Bitwarden Rust-SDK".to_owned(), + /// device_type: DeviceType::ChromeBrowser, + /// })); + /// ``` pub fn new(settings_input: Option) -> Self { let settings = settings_input.unwrap_or_default(); diff --git a/crates/bitwarden-core/src/client/encryption_settings.rs b/crates/bitwarden-core/src/client/encryption_settings.rs index 467ad61c..a8e34844 100644 --- a/crates/bitwarden-core/src/client/encryption_settings.rs +++ b/crates/bitwarden-core/src/client/encryption_settings.rs @@ -30,10 +30,20 @@ pub enum EncryptionSettingsError { MissingPrivateKey, } +/// A struct containing the core keys of a Bitwarden user. #[derive(Clone)] pub struct EncryptionSettings { + /// The users symmetric key, stored on the server after being + /// encrypted with another key. user_key: SymmetricCryptoKey, + /// The users private key, stored on the server + /// encrypted with the users symmetric key. pub(crate) private_key: Option, + /// A map of the users organization keys with the key being + /// the ID of the organization and the value being the symmetric + /// key. This map may be empty if the user is not a part of + /// any organizations. These keys are stored on the server + /// encrypted with the users private key. org_keys: HashMap, } diff --git a/crates/bitwarden-core/src/client/flags.rs b/crates/bitwarden-core/src/client/flags.rs index 0fc17534..ea03bdf9 100644 --- a/crates/bitwarden-core/src/client/flags.rs +++ b/crates/bitwarden-core/src/client/flags.rs @@ -1,5 +1,7 @@ +/// A struct containing fields of all known feature flags and their values. #[derive(Debug, Default, Clone, serde::Deserialize)] pub struct Flags { + /// A `bool` indicating whether or not cipher key encryption is enabled. #[serde(default, rename = "enableCipherKeyEncryption")] pub enable_cipher_key_encryption: bool, } diff --git a/crates/bitwarden-core/src/client/internal.rs b/crates/bitwarden-core/src/client/internal.rs index d64ac75b..026748d6 100644 --- a/crates/bitwarden-core/src/client/internal.rs +++ b/crates/bitwarden-core/src/client/internal.rs @@ -41,6 +41,9 @@ pub(crate) struct Tokens { pub(crate) refresh_token: Option, } +/// A struct contains various internal actions. Everything on this type +/// should be considered unstable and subject to change at any time. Use +/// with caution. #[derive(Debug)] pub struct InternalClient { pub(crate) tokens: RwLock, diff --git a/crates/bitwarden-core/src/client/test_accounts.rs b/crates/bitwarden-core/src/client/test_accounts.rs index 858e1d2f..3370231d 100644 --- a/crates/bitwarden-core/src/client/test_accounts.rs +++ b/crates/bitwarden-core/src/client/test_accounts.rs @@ -12,6 +12,12 @@ use crate::{ }; impl Client { + /// Creates a client initialized with a hard coded test account. + /// Useful for examples. + pub async fn test_account() -> Self { + Self::init_test_account(test_bitwarden_com_account()).await + } + pub async fn init_test_account(account: TestAccount) -> Self { let client = Client::new(None); diff --git a/crates/bitwarden-core/src/platform/client_platform.rs b/crates/bitwarden-core/src/platform/client_platform.rs index 1f117d5f..3cfbb4f7 100644 --- a/crates/bitwarden-core/src/platform/client_platform.rs +++ b/crates/bitwarden-core/src/platform/client_platform.rs @@ -5,15 +5,54 @@ use super::{ }; use crate::{error::Result, Client}; +/// A struct containing platform utilities. pub struct ClientPlatform<'a> { pub(crate) client: &'a Client, } impl<'a> ClientPlatform<'a> { + /// Will generate a fingerprint based on the `input`. Given the same `input` This + /// method will result in the exact same output. + /// + /// # Examples + /// ```rust + /// use bitwarden_core::{Client, platform::FingerprintRequest}; + /// + /// async fn test() { + /// let client = Client::test_account().await; + /// let fingerprint_response = client.platform() + /// .fingerprint(&FingerprintRequest { + /// fingerprint_material: "my_material".to_owned(), + /// public_key: "...public key...".to_owned(), + /// }) + /// .unwrap(); + /// + /// println!("{}", fingerprint_response.fingerprint); + /// } + /// ``` pub fn fingerprint(&self, input: &FingerprintRequest) -> Result { generate_fingerprint(input) } + /// Will generate a fingerprint based on the given `fingerprint_material` + /// and the users public key. Given the same `fingerprint_material` and + /// the same user. This method will result in the exact same output. + /// + /// The returned fingerprint is a string of 5 words seperated by hyphens. + /// + /// # Examples + /// ```rust + /// use bitwarden_core::Client; + /// + /// async fn test() { + /// let client = Client::test_account().await; + /// let fingerprint = client.platform() + /// .user_fingerprint("my_material".to_owned()) + /// .unwrap(); + /// + /// assert_eq!(fingerprint, "dreamland-desecrate-corrosive-ecard-retry"); + /// } + /// ``` pub fn user_fingerprint(self, fingerprint_material: String) -> Result { generate_user_fingerprint(self.client, fingerprint_material) } @@ -27,6 +66,7 @@ impl<'a> ClientPlatform<'a> { } impl<'a> Client { + /// Retrieves a [`ClientPlatform`] for accessing Platform APIs. pub fn platform(&'a self) -> ClientPlatform<'a> { ClientPlatform { client: self } } diff --git a/crates/bitwarden-core/src/platform/generate_fingerprint.rs b/crates/bitwarden-core/src/platform/generate_fingerprint.rs index 5d31a1af..f632cafc 100644 --- a/crates/bitwarden-core/src/platform/generate_fingerprint.rs +++ b/crates/bitwarden-core/src/platform/generate_fingerprint.rs @@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize}; use crate::error::Result; +/// A struct containing the parts for making a fingerprint request. #[derive(Serialize, Deserialize, Debug, JsonSchema)] #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "uniffi", derive(uniffi::Record))] @@ -16,9 +17,11 @@ pub struct FingerprintRequest { pub public_key: String, } +/// A struct containing the details of a successful request to generate a fingerprint. #[derive(Serialize, Deserialize, Debug, JsonSchema)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct FingerprintResponse { + /// A `String` containing 5 words seperated by hyphens. pub fingerprint: String, } From e92c8e98113edfce5221738896e3dd509795f4f1 Mon Sep 17 00:00:00 2001 From: Justin Baur <19896123+justindbaur@users.noreply.github.com> Date: Wed, 27 Nov 2024 13:21:04 -0500 Subject: [PATCH 2/3] Make `fingerprint` Example Runnable --- crates/bitwarden-core/src/client/test_accounts.rs | 10 ++++++++++ .../bitwarden-core/src/platform/client_platform.rs | 12 ++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/crates/bitwarden-core/src/client/test_accounts.rs b/crates/bitwarden-core/src/client/test_accounts.rs index 3370231d..44d9090f 100644 --- a/crates/bitwarden-core/src/client/test_accounts.rs +++ b/crates/bitwarden-core/src/client/test_accounts.rs @@ -193,3 +193,13 @@ pub fn test_legacy_user_key_account() -> TestAccount { org: None, } } + +pub const PUBLIC_KEY: &str = "-----BEGIN PUBLIC KEY----- +MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQB6x0WIywZ7ys/5lFBNOWqP +uwtnCuX58vPVOOUttuaK1AfgZnCIpTbkaJUPKqZbeEi2uWlrDBQ5K0gEX9ASTXCw +i3ij1mVifAJgjI698VTS2Xn9vitYhBT8V0EQstUW0jIlng7qRyrHQ9owBzGUsv4s +1pGHKhgJcbQkh+hagu0s8cpA0BQl6L2BFi/H4DjD94m3LcJVj+z6FepQYJlLte+W +T3DjuHhwXWWRXiYP0/d/QeCBMMP72N4Xw25LmXOxN035JlKHuRazg0lHDj5C1BDY +nH4lWuWbVrLUBNzETLUAGJX6JEuVbwLWEb4R1yJHXecUueyPSCSxksY4rUedSGoP +AgMBAAE= +-----END PUBLIC KEY-----"; diff --git a/crates/bitwarden-core/src/platform/client_platform.rs b/crates/bitwarden-core/src/platform/client_platform.rs index 3cfbb4f7..959556cd 100644 --- a/crates/bitwarden-core/src/platform/client_platform.rs +++ b/crates/bitwarden-core/src/platform/client_platform.rs @@ -16,19 +16,23 @@ impl<'a> ClientPlatform<'a> { /// /// # Examples /// ```rust + /// # use bitwarden_core::client::test_accounts::PUBLIC_KEY; + /// use base64::{engine::general_purpose::STANDARD, Engine}; /// use bitwarden_core::{Client, platform::FingerprintRequest}; /// - /// async fn test() { - /// let client = Client::test_account().await; + /// fn test(client: Client) { /// let fingerprint_response = client.platform() /// .fingerprint(&FingerprintRequest { /// fingerprint_material: "my_material".to_owned(), - /// public_key: "...public key...".to_owned(), + /// public_key: STANDARD.encode(PUBLIC_KEY), /// }) /// .unwrap(); /// - /// println!("{}", fingerprint_response.fingerprint); + /// assert_eq!(fingerprint_response.fingerprint, "unsure-unethical-bruising-semester-subscript"); /// } + /// + /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on( + /// # async { test(Client::test_account().await) }); /// ``` pub fn fingerprint(&self, input: &FingerprintRequest) -> Result { generate_fingerprint(input) From b800b80a56467437c2ef05c9deef6d16976de6d3 Mon Sep 17 00:00:00 2001 From: Justin Baur <19896123+justindbaur@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:09:50 -0500 Subject: [PATCH 3/3] Apply suggestions from code review Co-authored-by: Andreas Coroiu --- crates/bitwarden-core/src/platform/client_platform.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bitwarden-core/src/platform/client_platform.rs b/crates/bitwarden-core/src/platform/client_platform.rs index 959556cd..b2395d6c 100644 --- a/crates/bitwarden-core/src/platform/client_platform.rs +++ b/crates/bitwarden-core/src/platform/client_platform.rs @@ -12,7 +12,7 @@ pub struct ClientPlatform<'a> { impl<'a> ClientPlatform<'a> { /// Will generate a fingerprint based on the `input`. Given the same `input` This - /// method will result in the exact same output. + /// method will always result in the exact same output. /// /// # Examples /// ```rust @@ -40,7 +40,7 @@ impl<'a> ClientPlatform<'a> { /// Will generate a fingerprint based on the given `fingerprint_material` /// and the users public key. Given the same `fingerprint_material` and - /// the same user. This method will result in the exact same output. + /// the same user this method will always result in the exact same output. /// /// The returned fingerprint is a string of 5 words seperated by hyphens. ///