From c3d809b6b3a0a9ebf9fe6bddeea10f54a3701062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= Date: Thu, 29 Feb 2024 17:31:56 +0100 Subject: [PATCH 01/15] Fix typo in `trust_device` (#640) ## Type of change ``` - [x] Bug fix - [ ] New feature development - [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc) - [ ] Build/deploy pipeline (DevOps) - [ ] Other ``` ## Objective Function was accidentally called `t` when it should have been called `trust_device` --- crates/bitwarden-uniffi/src/auth/mod.rs | 2 +- languages/kotlin/doc.md | 68 +++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/crates/bitwarden-uniffi/src/auth/mod.rs b/crates/bitwarden-uniffi/src/auth/mod.rs index 34d0de93f..2a451ffdf 100644 --- a/crates/bitwarden-uniffi/src/auth/mod.rs +++ b/crates/bitwarden-uniffi/src/auth/mod.rs @@ -130,7 +130,7 @@ impl ClientAuth { } /// Trust the current device - pub async fn t(&self) -> Result { + pub async fn trust_device(&self) -> Result { Ok(self.0 .0.write().await.auth().trust_device()?) } } diff --git a/languages/kotlin/doc.md b/languages/kotlin/doc.md index d69e134c4..c4b564a2e 100644 --- a/languages/kotlin/doc.md +++ b/languages/kotlin/doc.md @@ -46,6 +46,16 @@ Generator operations **Output**: Arc +### `exporters` + +Exporters + +**Arguments**: + +- self: Arc + +**Output**: Arc + ### `auth` Auth operations @@ -138,6 +148,23 @@ password, use the email OTP. **Output**: std::result::Result<,BitwardenError> +### `validate_password_user_key` + +Validate the user password without knowing the password hash + +Used for accounts that we know have master passwords but that have not logged in with a password. +Some example are login with device or TDE. + +This works by comparing the provided password against the encrypted user key. + +**Arguments**: + +- self: +- password: String +- encrypted_user_key: String + +**Output**: std::result::Result + ### `new_auth_request` Initialize a new auth request @@ -160,6 +187,16 @@ Approve an auth request **Output**: std::result::Result +### `trust_device` + +Trust the current device + +**Arguments**: + +- self: + +**Output**: std::result::Result + ## ClientAttachments ### `encrypt_buffer` @@ -1287,6 +1324,37 @@ implementations. + + deviceKey + object + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyTypeDescription
device_keystringThe device's DeviceKey
protected_device_private_keyThe Device Private Key
device_protected_user_keyThe user's symmetric crypto key, encrypted with the Device Key.
+ + ## `InitUserCryptoRequest` From e16cea0957d997374584477e2e4a633de27f3f9c Mon Sep 17 00:00:00 2001 From: tangowithfoxtrot <5676771+tangowithfoxtrot@users.noreply.github.com> Date: Thu, 29 Feb 2024 11:33:13 -0800 Subject: [PATCH 02/15] add get-by-ids to Go wrapper (#620) ## Type of change - [ ] Bug fix - [ ] New feature development - [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc) - [ ] Build/deploy pipeline (DevOps) - [x] Other ## Objective Allow secret fetching by a list of IDs, rather than requesting multiple secrets individually. Exposes the functionality from #150 in Go. ## Code changes - **languages/go/secrets.go:** Add `GetByIDS` func. - **languages/go/example/example.go:** Get secrets by ID. Added JSON output for QA/ease-of-use. --- languages/go/example/example.go | 25 +++++++++++++++++++++++++ languages/go/secrets.go | 17 +++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/languages/go/example/example.go b/languages/go/example/example.go index deb353565..eb4d34612 100644 --- a/languages/go/example/example.go +++ b/languages/go/example/example.go @@ -1,7 +1,9 @@ package main import ( + "encoding/json" "fmt" + "log" "os" sdk "github.com/bitwarden/sdk/languages/go" @@ -87,5 +89,28 @@ func main() { panic(err) } + secretIdentifiers, err := bitwardenClient.Secrets.List(organizationID.String()) + if err != nil { + panic(err) + } + + // Get secrets with a list of IDs + secretIDs := make([]string, len(secretIdentifiers.Data)) + for i, identifier := range secretIdentifiers.Data { + secretIDs[i] = identifier.ID + } + + secrets, err := bitwardenClient.Secrets.GetByIDS(secretIDs) + if err != nil { + log.Fatalf("Error getting secrets: %v", err) + } + + jsonSecrets, err := json.MarshalIndent(secrets, "", " ") + if err != nil { + log.Fatalf("Error marshalling secrets to JSON: %v", err) + } + + fmt.Println(string(jsonSecrets)) + defer bitwardenClient.Close() } diff --git a/languages/go/secrets.go b/languages/go/secrets.go index e6863c459..2c56538f7 100644 --- a/languages/go/secrets.go +++ b/languages/go/secrets.go @@ -4,6 +4,7 @@ type SecretsInterface interface { Create(key, value, note string, organizationID string, projectIDs []string) (*SecretResponse, error) List(organizationID string) (*SecretIdentifiersResponse, error) Get(secretID string) (*SecretResponse, error) + GetByIDS(secretIDs []string) (*SecretsResponse, error) Update(secretID string, key, value, note string, organizationID string, projectIDs []string) (*SecretResponse, error) Delete(secretIDs []string) (*SecretsDeleteResponse, error) } @@ -76,6 +77,22 @@ func (s *Secrets) Get(id string) (*SecretResponse, error) { return &response, nil } +func (s *Secrets) GetByIDS(ids []string) (*SecretsResponse, error) { + command := Command{ + Secrets: &SecretsCommand{ + GetByIDS: &SecretsGetRequest{ + IDS: ids, + }, + }, + } + + var response SecretsResponse + if err := s.executeCommand(command, &response); err != nil { + return nil, err + } + return &response, nil +} + func (s *Secrets) Update(id string, key, value, note string, organizationID string, projectIDs []string) (*SecretResponse, error) { command := Command{ Secrets: &SecretsCommand{ From 5d131f519651a2f6cfb9d27378a7f112ada7e4b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ch=C4=99ci=C5=84ski?= Date: Fri, 1 Mar 2024 10:41:20 +0100 Subject: [PATCH 03/15] [DEVOPS-1750] Fix release Go SDK pipeline (#642) ## Type of change ``` - [x] Bug fix - [ ] New feature development - [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc) - [x] Build/deploy pipeline (DevOps) - [ ] Other ``` ## Objective ## Code changes - **.github/workflows/release-go.yml:** Use `bitwarden-devops-bot` PAT to checkout `sm-sdk-go` repo. ## Before you submit - Please add **unit tests** where it makes sense to do so --- .github/workflows/release-go.yml | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release-go.yml b/.github/workflows/release-go.yml index 830e5f313..b9ff394be 100644 --- a/.github/workflows/release-go.yml +++ b/.github/workflows/release-go.yml @@ -1,4 +1,5 @@ -name: Release Go +name: Release Go SDK +run-name: Release Go SDK ${{ inputs.release_type }} on: workflow_dispatch: @@ -15,6 +16,7 @@ on: env: GO111MODULE: on GO_VERSION: "^1.18" + _KEY_VAULT: "bitwarden-ci" jobs: validate: @@ -47,7 +49,6 @@ jobs: runs-on: ubuntu-22.04 needs: validate env: - _KEY_VAULT: "bitwarden-ci" _BOT_EMAIL: 106330231+bitwarden-devops-bot@users.noreply.github.com _BOT_NAME: bitwarden-devops-bot _PKG_VERSION: ${{ needs.validate.outputs.version }} @@ -57,13 +58,6 @@ jobs: with: path: sdk - - name: Checkout SDK-Go repo - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - with: - repository: bitwarden/sm-sdk-go - path: sm-sdk-go - ref: main - - name: Login to Azure - Prod Subscription uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: @@ -76,6 +70,14 @@ jobs: keyvault: ${{ env._KEY_VAULT }} secrets: "github-pat-bitwarden-devops-bot-repo-scope" + - name: Checkout SDK-Go repo + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + with: + repository: bitwarden/sm-sdk-go + path: sm-sdk-go + ref: main + token: ${{ steps.retrieve-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }} + - name: Setup Git working-directory: sm-sdk-go run: | From 29089c576491c2424da178dc6a57f1a1f4596d34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= Date: Fri, 1 Mar 2024 13:18:50 +0100 Subject: [PATCH 04/15] [PM-6262] Add basic feature flags support to enable cipher key encryption (#638) ## Type of change ``` - [ ] Bug fix - [x] New feature development - [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc) - [ ] Build/deploy pipeline (DevOps) - [ ] Other ``` ## Objective Implement support for some basic feature flagging for the mobile clients, the only flag supported is `enableCipherKeyEncryption` --- crates/bitwarden-uniffi/src/platform/mod.rs | 6 ++ crates/bitwarden/src/client/client.rs | 24 +++++- crates/bitwarden/src/client/flags.rs | 43 +++++++++++ crates/bitwarden/src/client/mod.rs | 3 + .../src/mobile/vault/client_ciphers.rs | 15 +++- crates/bitwarden/src/vault/cipher/cipher.rs | 77 +++++++++++++++++++ languages/kotlin/doc.md | 11 +++ 7 files changed, 173 insertions(+), 6 deletions(-) create mode 100644 crates/bitwarden/src/client/flags.rs diff --git a/crates/bitwarden-uniffi/src/platform/mod.rs b/crates/bitwarden-uniffi/src/platform/mod.rs index 7200b6678..33b14d345 100644 --- a/crates/bitwarden-uniffi/src/platform/mod.rs +++ b/crates/bitwarden-uniffi/src/platform/mod.rs @@ -31,4 +31,10 @@ impl ClientPlatform { .platform() .user_fingerprint(fingerprint_material)?) } + + /// Load feature flags into the client + pub async fn load_flags(&self, flags: std::collections::HashMap) -> Result<()> { + self.0 .0.write().await.load_flags(flags); + Ok(()) + } } diff --git a/crates/bitwarden/src/client/client.rs b/crates/bitwarden/src/client/client.rs index 5b2c8b7e0..45f58444c 100644 --- a/crates/bitwarden/src/client/client.rs +++ b/crates/bitwarden/src/client/client.rs @@ -13,9 +13,12 @@ use super::AccessToken; #[cfg(feature = "secrets")] use crate::auth::login::{AccessTokenLoginRequest, AccessTokenLoginResponse}; #[cfg(feature = "internal")] -use crate::platform::{ - get_user_api_key, sync, SecretVerificationRequest, SyncRequest, SyncResponse, - UserApiKeyResponse, +use crate::{ + client::flags::Flags, + platform::{ + get_user_api_key, sync, SecretVerificationRequest, SyncRequest, SyncResponse, + UserApiKeyResponse, + }, }; use crate::{ client::{ @@ -77,6 +80,9 @@ pub struct Client { pub(crate) token_expires_on: Option, pub(crate) login_method: Option, + #[cfg(feature = "internal")] + flags: Flags, + /// Use Client::get_api_configurations() to access this. /// It should only be used directly in renew_token #[doc(hidden)] @@ -138,6 +144,8 @@ impl Client { refresh_token: None, token_expires_on: None, login_method: None, + #[cfg(feature = "internal")] + flags: Flags::default(), __api_configurations: ApiConfigurations { identity, api, @@ -148,6 +156,16 @@ impl Client { } } + #[cfg(feature = "internal")] + pub fn load_flags(&mut self, flags: std::collections::HashMap) { + self.flags = Flags::load_from_map(flags); + } + + #[cfg(feature = "mobile")] + pub(crate) fn get_flags(&self) -> &Flags { + &self.flags + } + pub(crate) async fn get_api_configurations(&mut self) -> &ApiConfigurations { // At the moment we ignore the error result from the token renewal, if it fails, // the token will end up expiring and the next operation is going to fail anyway. diff --git a/crates/bitwarden/src/client/flags.rs b/crates/bitwarden/src/client/flags.rs new file mode 100644 index 000000000..0fc17534b --- /dev/null +++ b/crates/bitwarden/src/client/flags.rs @@ -0,0 +1,43 @@ +#[derive(Debug, Default, Clone, serde::Deserialize)] +pub struct Flags { + #[serde(default, rename = "enableCipherKeyEncryption")] + pub enable_cipher_key_encryption: bool, +} + +impl Flags { + pub fn load_from_map(map: std::collections::HashMap) -> Self { + let map = map + .into_iter() + .map(|(k, v)| (k, serde_json::Value::Bool(v))) + .collect(); + serde_json::from_value(serde_json::Value::Object(map)).expect("Valid map") + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_load_empty_map() { + let map = std::collections::HashMap::new(); + let flags = Flags::load_from_map(map); + assert!(!flags.enable_cipher_key_encryption); + } + + #[test] + fn test_load_valid_map() { + let mut map = std::collections::HashMap::new(); + map.insert("enableCipherKeyEncryption".into(), true); + let flags = Flags::load_from_map(map); + assert!(flags.enable_cipher_key_encryption); + } + + #[test] + fn test_load_invalid_map() { + let mut map = std::collections::HashMap::new(); + map.insert("thisIsNotAFlag".into(), true); + let flags = Flags::load_from_map(map); + assert!(!flags.enable_cipher_key_encryption); + } +} diff --git a/crates/bitwarden/src/client/mod.rs b/crates/bitwarden/src/client/mod.rs index c3719fce2..f6d60e961 100644 --- a/crates/bitwarden/src/client/mod.rs +++ b/crates/bitwarden/src/client/mod.rs @@ -7,5 +7,8 @@ mod client; pub mod client_settings; pub(crate) mod encryption_settings; +#[cfg(feature = "internal")] +mod flags; + pub use access_token::AccessToken; pub use client::Client; diff --git a/crates/bitwarden/src/mobile/vault/client_ciphers.rs b/crates/bitwarden/src/mobile/vault/client_ciphers.rs index 4e34021ee..c35cf3080 100644 --- a/crates/bitwarden/src/mobile/vault/client_ciphers.rs +++ b/crates/bitwarden/src/mobile/vault/client_ciphers.rs @@ -1,8 +1,8 @@ -use bitwarden_crypto::{Decryptable, Encryptable}; +use bitwarden_crypto::{Decryptable, Encryptable, LocateKey}; use super::client_vault::ClientVault; use crate::{ - error::Result, + error::{Error, Result}, vault::{Cipher, CipherListView, CipherView}, Client, }; @@ -12,9 +12,18 @@ pub struct ClientCiphers<'a> { } impl<'a> ClientCiphers<'a> { - pub async fn encrypt(&self, cipher_view: CipherView) -> Result { + pub async fn encrypt(&self, mut cipher_view: CipherView) -> Result { let enc = self.client.get_encryption_settings()?; + // TODO: Once this flag is removed, the key generation logic should + // be moved directly into the KeyEncryptable implementation + if cipher_view.key.is_none() && self.client.get_flags().enable_cipher_key_encryption { + let key = cipher_view + .locate_key(enc, &None) + .ok_or(Error::VaultLocked)?; + cipher_view.generate_cipher_key(key)?; + } + let cipher = cipher_view.encrypt(enc, &None)?; Ok(cipher) diff --git a/crates/bitwarden/src/vault/cipher/cipher.rs b/crates/bitwarden/src/vault/cipher/cipher.rs index 9223462a1..24207251f 100644 --- a/crates/bitwarden/src/vault/cipher/cipher.rs +++ b/crates/bitwarden/src/vault/cipher/cipher.rs @@ -289,6 +289,18 @@ impl Cipher { } } +impl CipherView { + pub fn generate_cipher_key(&mut self, key: &SymmetricCryptoKey) -> Result<()> { + let ciphers_key = Cipher::get_cipher_key(key, &self.key)?; + let key = ciphers_key.as_ref().unwrap_or(key); + + let new_key = SymmetricCryptoKey::generate(rand::thread_rng()); + + self.key = Some(new_key.to_vec().encrypt_with_key(key)?); + Ok(()) + } +} + impl KeyDecryptable for Cipher { fn decrypt_with_key(&self, key: &SymmetricCryptoKey) -> Result { let ciphers_key = Cipher::get_cipher_key(key, &self.key)?; @@ -401,3 +413,68 @@ impl From for CipherRepromptType } } } + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn test_generate_cipher_key() { + let key = SymmetricCryptoKey::generate(rand::thread_rng()); + + fn generate_cipher() -> CipherView { + CipherView { + r#type: CipherType::Login, + login: Some(login::LoginView { + username: Some("test_username".to_string()), + password: Some("test_password".to_string()), + password_revision_date: None, + uris: None, + totp: None, + autofill_on_page_load: None, + }), + id: "fd411a1a-fec8-4070-985d-0e6560860e69".parse().ok(), + organization_id: None, + folder_id: None, + collection_ids: vec![], + key: None, + name: "My test login".to_string(), + notes: None, + identity: None, + card: None, + secure_note: None, + favorite: false, + reprompt: CipherRepromptType::None, + organization_use_totp: true, + edit: true, + view_password: true, + local_data: None, + attachments: None, + fields: None, + password_history: None, + creation_date: "2024-01-30T17:55:36.150Z".parse().unwrap(), + deleted_date: None, + revision_date: "2024-01-30T17:55:36.150Z".parse().unwrap(), + } + } + + let original_cipher = generate_cipher(); + + // Check that the cipher gets encrypted correctly without it's own key + let cipher = generate_cipher(); + let no_key_cipher_enc = cipher.encrypt_with_key(&key).unwrap(); + let no_key_cipher_dec: CipherView = no_key_cipher_enc.decrypt_with_key(&key).unwrap(); + assert!(no_key_cipher_dec.key.is_none()); + assert_eq!(no_key_cipher_dec.name, original_cipher.name); + + let mut cipher = generate_cipher(); + cipher.generate_cipher_key(&key).unwrap(); + + // Check that the cipher gets encrypted correctly when it's assigned it's own key + let key_cipher_enc = cipher.encrypt_with_key(&key).unwrap(); + let key_cipher_dec: CipherView = key_cipher_enc.decrypt_with_key(&key).unwrap(); + assert!(key_cipher_dec.key.is_some()); + assert_eq!(key_cipher_dec.name, original_cipher.name); + } +} diff --git a/languages/kotlin/doc.md b/languages/kotlin/doc.md index c4b564a2e..5dce0dbb4 100644 --- a/languages/kotlin/doc.md +++ b/languages/kotlin/doc.md @@ -531,6 +531,17 @@ Fingerprint using logged in user's public key **Output**: std::result::Result +### `load_flags` + +Load feature flags into the client + +**Arguments**: + +- self: +- flags: std::collections::HashMap + +**Output**: std::result::Result<,BitwardenError> + ## ClientSends ### `encrypt` From 621cb48908ad11cf490eb84124424a22ac8a13fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ch=C4=99ci=C5=84ski?= Date: Fri, 1 Mar 2024 14:31:04 +0100 Subject: [PATCH 05/15] [DEVOPS-1750] Fix release go pipeline (#643) ## Type of change ``` - [ ] Bug fix - [ ] New feature development - [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc) - [x] Build/deploy pipeline (DevOps) - [ ] Other ``` ## Objective ## Code changes - **.github/workflows/release-go.yml:** Fix files copy and repo name in create release ## Before you submit - Please add **unit tests** where it makes sense to do so --- .github/workflows/release-go.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-go.yml b/.github/workflows/release-go.yml index b9ff394be..5106cd5ee 100644 --- a/.github/workflows/release-go.yml +++ b/.github/workflows/release-go.yml @@ -87,7 +87,7 @@ jobs: - name: Update files run: | # Copy files to local sm-sdk-go repo path - cp --verbose -rf sdk/languages/go sm-sdk-go + cp --verbose -rf sdk/languages/go/. sm-sdk-go - name: Push changes working-directory: sm-sdk-go @@ -150,4 +150,5 @@ jobs: body: "" token: ${{ steps.retrieve-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }} draft: true - repo: bitwarden/sm-sdk-go + repo: sm-sdk-go + owner: bitwarden From 300c1f054a1b359e0937ad8cf701bc4652bc493a Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Mon, 4 Mar 2024 16:09:32 +0100 Subject: [PATCH 06/15] Exclude unecessary files from published bws (#650) Prevent unnecessary files from being included in the crates distributed build. --- crates/bws/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/bws/Cargo.toml b/crates/bws/Cargo.toml index 2a0a13f49..9dc6cb355 100644 --- a/crates/bws/Cargo.toml +++ b/crates/bws/Cargo.toml @@ -11,6 +11,7 @@ description = """ Bitwarden Secrets Manager CLI """ keywords = ["bitwarden", "secrets-manager", "cli"] +exclude = ["Dockerfile*", "entitlements.plist"] [dependencies] bat = { version = "0.24.0", features = [ From d18ac44226595a19ac8198b2b600f9e9dbf82d11 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Mar 2024 11:17:48 +0100 Subject: [PATCH 07/15] Bump mio from 0.8.10 to 0.8.11 (#651) Bumps [mio](https://github.com/tokio-rs/mio) from 0.8.10 to 0.8.11.
Changelog

Sourced from mio's changelog.

0.8.11

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=mio&package-manager=cargo&previous-version=0.8.10&new-version=0.8.11)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/bitwarden/sdk/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7efce8973..5c9d0fb58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2003,9 +2003,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", From d2c68979b012565dce9c29c542b9859f2604e5ac Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Tue, 5 Mar 2024 15:52:11 +0100 Subject: [PATCH 08/15] Provide a stable test user (#514) Provide a stable test user for use in tests. --- README.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/README.md b/README.md index bab08af9a..cdc52a7cb 100644 --- a/README.md +++ b/README.md @@ -86,5 +86,56 @@ This project uses customized templates which lives in the `support/openapi-templ These templates resolves some outstanding issues we've experienced with the rust generator. But we strive towards modifying the templates as little as possible to ease future upgrades. +## Tests + +Many of the SDK tests are based on encrypted data provided by the other Bitwarden clients. In order +to provide a consistent method of retrieving the data we provide a test account with user keys. + +**Disclaimer:** The server typically encrypts and protects certain fields. In order to allow +accounts to be used on other servers this protection was explicitly removed from these data dumps. + +### `test@bitwarden.com` + +- Email: `test@bitwarden.com` +- Password: `asdfasdfasdf` +- PBKDF2: `600_000` iterations + +```sql +INSERT INTO vault_dev.dbo.[User] ( + Id, Name, Email, EmailVerified, MasterPassword, + MasterPasswordHint, Culture, SecurityStamp, + TwoFactorProviders, TwoFactorRecoveryCode, + EquivalentDomains, ExcludedGlobalEquivalentDomains, + AccountRevisionDate, [Key], PublicKey, + PrivateKey, Premium, PremiumExpirationDate, + Storage, MaxStorageGb, Gateway, GatewayCustomerId, + GatewaySubscriptionId, LicenseKey, + CreationDate, RevisionDate, RenewalReminderDate, + Kdf, KdfIterations, ReferenceData, + ApiKey, ForcePasswordReset, UsesKeyConnector, + FailedLoginCount, LastFailedLoginDate, + AvatarColor, KdfMemory, KdfParallelism, + LastPasswordChangeDate, LastKdfChangeDate, + LastKeyRotationDate, LastEmailChangeDate +) +VALUES + ( + N 'b1fd4bf2-9643-4787-87f3-b0f00189c33b', + N 'Test', N 'test@bitwarden.com', + 0, N 'AQAAAAEAAYagAAAAEJ3ky9F/Zt5sy3/UAHVvBarMR+tBXYOM5IGgXy4/mx82uptgHgItauyCN+UZTvAqiA==', + null, N 'en-US', N 'F3KL7SCJKEXO4LJFVLGZITPEHM7SAVSZ', + null, null, null, null, N '2024-01-07 23:56:48.2600000', + N '2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=', + N 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Ww2chogqCpaAR7Uw448am4b7vDFXiM5kXjFlGfXBlrAdAqTTggEvTDlMNYqPlCo+mBM6iFmTTUY9rpZBvFskMnKvsvpJ47/fehAH2o2e3Ulv/5NFevaVCMCmpkBDtbMbO1A4a3btdRtCP8DsKWMefHauEpaoLxNTLWnOIZVfCMjsSgx2EvULHAZPTtbFwm4+UVKniM4ds4jvOsD85h4jn2aLs/jWJXFfxN8iVSqEqpC2TBvsPdyHb49xQoWWfF0Z6BiNqeNGKEU9Uos1pjL+kzhEzzSpH31PZT/ufJ/oo4+93wrUt57hb6f0jxiXhwd5yQ+9F6wVwpbfkq0IwhjOwIDAQAB', + N '2.yN7l00BOlUE0Sb0M//Q53w==|EwKG/BduQRQ33Izqc/ogoBROIoI5dmgrxSo82sgzgAMIBt3A2FZ9vPRMY+GWT85JiqytDitGR3TqwnFUBhKUpRRAq4x7rA6A1arHrFp5Tp1p21O3SfjtvB3quiOKbqWk6ZaU1Np9HwqwAecddFcB0YyBEiRX3VwF2pgpAdiPbSMuvo2qIgyob0CUoC/h4Bz1be7Qa7B0Xw9/fMKkB1LpOm925lzqosyMQM62YpMGkjMsbZz0uPopu32fxzDWSPr+kekNNyLt9InGhTpxLmq1go/pXR2uw5dfpXc5yuta7DB0EGBwnQ8Vl5HPdDooqOTD9I1jE0mRyuBpWTTI3FRnu3JUh3rIyGBJhUmHqGZvw2CKdqHCIrQeQkkEYqOeJRJVdBjhv5KGJifqT3BFRwX/YFJIChAQpebNQKXe/0kPivWokHWwXlDB7S7mBZzhaAPidZvnuIhalE2qmTypDwHy22FyqV58T8MGGMchcASDi/QXI6kcdpJzPXSeU9o+NC68QDlOIrMVxKFeE7w7PvVmAaxEo0YwmuAzzKy9QpdlK0aab/xEi8V4iXj4hGepqAvHkXIQd+r3FNeiLfllkb61p6WTjr5urcmDQMR94/wYoilpG5OlybHdbhsYHvIzYoLrC7fzl630gcO6t4nM24vdB6Ymg9BVpEgKRAxSbE62Tqacxqnz9AcmgItb48NiR/He3n3ydGjPYuKk/ihZMgEwAEZvSlNxYONSbYrIGDtOY+8Nbt6KiH3l06wjZW8tcmFeVlWv+tWotnTY9IqlAfvNVTjtsobqtQnvsiDjdEVtNy/s2ci5TH+NdZluca2OVEr91Wayxh70kpM6ib4UGbfdmGgCo74gtKvKSJU0rTHakQ5L9JlaSDD5FamBRyI0qfL43Ad9qOUZ8DaffDCyuaVyuqk7cz9HwmEmvWU3VQ+5t06n/5kRDXttcw8w+3qClEEdGo1KeENcnXCB32dQe3tDTFpuAIMLqwXs6FhpawfZ5kPYvLPczGWaqftIs/RXJ/EltGc0ugw2dmTLpoQhCqrcKEBDoYVk0LDZKsnzitOGdi9mOWse7Se8798ib1UsHFUjGzISEt6upestxOeupSTOh0v4+AjXbDzRUyogHww3V+Bqg71bkcMxtB+WM+pn1XNbVTyl9NR040nhP7KEf6e9ruXAtmrBC2ah5cFEpLIot77VFZ9ilLuitSz+7T8n1yAh1IEG6xxXxninAZIzi2qGbH69O5RSpOJuJTv17zTLJQIIc781JwQ2TTwTGnx5wZLbffhCasowJKd2EVcyMJyhz6ru0PvXWJ4hUdkARJs3Xu8dus9a86N8Xk6aAPzBDqzYb1vyFIfBxP0oO8xFHgd30Cgmz8UrSE3qeWRrF8ftrI6xQnFjHBGWD/JWSvd6YMcQED0aVuQkuNW9ST/DzQThPzRfPUoiL10yAmV7Ytu4fR3x2sF0Yfi87YhHFuCMpV/DsqxmUizyiJuD938eRcH8hzR/VO53Qo3UIsqOLcyXtTv6THjSlTopQ+JOLOnHm1w8dzYbLN44OG44rRsbihMUQp+wUZ6bsI8rrOnm9WErzkbQFbrfAINdoCiNa6cimYIjvvnMTaFWNymqY1vZxGztQiMiHiHYwTfwHTXrb9j0uPM=|09J28iXv9oWzYtzK2LBT6Yht4IT4MijEkk0fwFdrVQ4=', + 0, null, null, null, null, null, null, + null, N '2024-01-07 23:53:38.5900000', + N '2024-01-07 23:53:38.5900000', + null, 0, 600000, N '{"id":null}', N '7gp59kKHt9kMlks0BuNC4IjNXYkljR', + 0, 0, 0, null, null, null, null, null, + null, null, null + ); +``` + [secrets-manager]: https://bitwarden.com/products/secrets-manager/ [bws-help]: https://bitwarden.com/help/secrets-manager-cli/ From efeea62ecf3a68c7b1403803c1f5798d636d51c4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 13:51:01 -0500 Subject: [PATCH 09/15] [deps]: Update bitwarden/gh-actions digest to 4f37134 (#649) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/release-go.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-go.yml b/.github/workflows/release-go.yml index 5106cd5ee..418c1e30f 100644 --- a/.github/workflows/release-go.yml +++ b/.github/workflows/release-go.yml @@ -65,7 +65,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f + uses: bitwarden/gh-actions/get-keyvault-secrets@4f37134d838f21609c38cb56694d8605f176704c with: keyvault: ${{ env._KEY_VAULT }} secrets: "github-pat-bitwarden-devops-bot-repo-scope" @@ -136,7 +136,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f + uses: bitwarden/gh-actions/get-keyvault-secrets@4f37134d838f21609c38cb56694d8605f176704c with: keyvault: ${{ env._KEY_VAULT }} secrets: "github-pat-bitwarden-devops-bot-repo-scope" From b115e26580cd937b14845b22263d628825241b38 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 13:51:18 -0500 Subject: [PATCH 10/15] [deps]: Update actions/checkout digest to 1e31de5 (#648) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/release-go.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-go.yml b/.github/workflows/release-go.yml index 418c1e30f..a8bb82f88 100644 --- a/.github/workflows/release-go.yml +++ b/.github/workflows/release-go.yml @@ -54,7 +54,7 @@ jobs: _PKG_VERSION: ${{ needs.validate.outputs.version }} steps: - name: Checkout SDK repo - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@1e31de5234b9f8995739874a8ce0492dc87873e2 # v4.0.0 with: path: sdk @@ -71,7 +71,7 @@ jobs: secrets: "github-pat-bitwarden-devops-bot-repo-scope" - name: Checkout SDK-Go repo - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@1e31de5234b9f8995739874a8ce0492dc87873e2 # v4.0.0 with: repository: bitwarden/sm-sdk-go path: sm-sdk-go From 9d6fa34dc48e8aec50f5725c5d857bee9f3f6b0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= Date: Mon, 11 Mar 2024 11:36:42 +0100 Subject: [PATCH 11/15] [PM-6100] Test for memory leaks of secrets (#641) ## Type of change ``` - [ ] Bug fix - [x] New feature development - [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc) - [ ] Build/deploy pipeline (DevOps) - [ ] Other ``` ## Objective Added a small test framework to test for secret leaks in memory. This consists of a few parts: - Binary crate `memory-testing`, this compiles to a binary that uses `bitwarden_crypto` to create some keys in memory and then frees them. The execution of this program goes like this: - Starts, keys get defined in memory - Waits for input (This is where we create an initial core dump) - Program frees the keys - Waits for input (This is where we create a final core dump) - Program exits normally - A `capture_dumps.py` Python script, it's purpose is starting the program and orchestrating the core dumps and sending inputs to the program to continue. - A `Dockerfile` that will compile the program and run the `capture_dumps.py` script, this is needed because the dumps only work on a Linux environment. - A `test.py` script that analyzes the memory dumps for secrets in memory - A `run_tests.sh` script that builds and runs the docker container and the test script in one invocation I've tried other tools to run it natively on other operating systems like osxpmem on mac and they either don't work on ARM Macs or they require running as root and disabling System Integrity Protection. I've also added a small workflow to run these tests, as that runs on a linux environment, it's run directly without docker. The results are printed to a table now: ![image](https://github.com/bitwarden/sdk/assets/725423/699b25bb-b184-4d46-aec1-e785df53cc88) --- .github/codecov.yml | 1 + .github/workflows/memory-testing.yml | 43 ++++++ .gitignore | 1 - Cargo.lock | 18 +++ crates/memory-testing/.gitignore | 1 + crates/memory-testing/Cargo.toml | 13 ++ crates/memory-testing/Dockerfile | 26 ++++ crates/memory-testing/Dockerfile.dockerignore | 4 + crates/memory-testing/cases.json | 9 ++ crates/memory-testing/run_test.sh | 20 +++ .../memory-testing/src/bin/analyze-dumps.rs | 132 ++++++++++++++++++ .../memory-testing/src/bin/capture-dumps.rs | 70 ++++++++++ crates/memory-testing/src/lib.rs | 29 ++++ crates/memory-testing/src/main.rs | 44 ++++++ 14 files changed, 410 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/memory-testing.yml create mode 100644 crates/memory-testing/.gitignore create mode 100644 crates/memory-testing/Cargo.toml create mode 100644 crates/memory-testing/Dockerfile create mode 100644 crates/memory-testing/Dockerfile.dockerignore create mode 100644 crates/memory-testing/cases.json create mode 100755 crates/memory-testing/run_test.sh create mode 100644 crates/memory-testing/src/bin/analyze-dumps.rs create mode 100644 crates/memory-testing/src/bin/capture-dumps.rs create mode 100644 crates/memory-testing/src/lib.rs create mode 100644 crates/memory-testing/src/main.rs diff --git a/.github/codecov.yml b/.github/codecov.yml index 3228d009c..eb34abf9c 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -1,3 +1,4 @@ ignore: - "crates/sdk-schemas" # Tool - "crates/uniffi-bindgen" # Tool + - "crates/memory-testing" # Testing diff --git a/.github/workflows/memory-testing.yml b/.github/workflows/memory-testing.yml new file mode 100644 index 000000000..af5ef6b7c --- /dev/null +++ b/.github/workflows/memory-testing.yml @@ -0,0 +1,43 @@ +--- +name: Test for memory leaks + +on: + pull_request: + paths: + - "crates/bitwarden-crypto/**" + - "crates/memory-testing/**" + push: + paths: + - "crates/bitwarden-crypto/**" + - "crates/memory-testing/**" + branches: + - "main" + - "rc" + - "hotfix-rc" + +jobs: + memory-test: + name: Testing + runs-on: ubuntu-22.04 + + steps: + - name: Check out repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Set up gdb + run: | + sudo apt update + sudo apt -y install gdb + + - name: Install rust + uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # stable + with: + toolchain: stable + + - name: Cache cargo registry + uses: Swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2.7.3 + with: + key: memtest-cargo + + - name: Test + run: ./crates/memory-testing/run_test.sh no-docker diff --git a/.gitignore b/.gitignore index 63c5875a3..b13651d19 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,6 @@ x64/ x86/ build/ bld/ -[Bb]in/ [Oo]bj/ *.wasm diff --git a/Cargo.lock b/Cargo.lock index 5c9d0fb58..b9f383413 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1544,6 +1544,12 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hkdf" version = "0.12.4" @@ -1970,6 +1976,18 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memory-testing" +version = "0.1.0" +dependencies = [ + "bitwarden-crypto", + "comfy-table", + "hex", + "serde", + "serde_json", + "zeroize", +] + [[package]] name = "mime" version = "0.3.17" diff --git a/crates/memory-testing/.gitignore b/crates/memory-testing/.gitignore new file mode 100644 index 000000000..53752db25 --- /dev/null +++ b/crates/memory-testing/.gitignore @@ -0,0 +1 @@ +output diff --git a/crates/memory-testing/Cargo.toml b/crates/memory-testing/Cargo.toml new file mode 100644 index 000000000..c1ecbbf54 --- /dev/null +++ b/crates/memory-testing/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "memory-testing" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +bitwarden-crypto = { path = "../bitwarden-crypto", version = "=0.1.0" } +comfy-table = "7.1.0" +hex = "0.4.3" +serde = "1.0.196" +serde_json = "1.0.113" +zeroize = "1.7.0" diff --git a/crates/memory-testing/Dockerfile b/crates/memory-testing/Dockerfile new file mode 100644 index 000000000..fdcf5de00 --- /dev/null +++ b/crates/memory-testing/Dockerfile @@ -0,0 +1,26 @@ +############################################### +# Build stage # +############################################### +FROM rust:1.76 AS build + +# Copy required project files +COPY . /app + +# Build project +WORKDIR /app +RUN cargo build -p memory-testing + +############################################### +# App stage # +############################################### +FROM debian:bookworm-slim + +# This specifically needs to run as root to be able to capture core dumps +USER root + +RUN apt-get update && apt-get install -y --no-install-recommends gdb=13.1-3 && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Copy built project from the build stage +COPY --from=build /app/target/debug/memory-testing /app/target/debug/capture-dumps /app/crates/memory-testing/cases.json ./ + +CMD [ "/capture-dumps", "./memory-testing", "/output" ] diff --git a/crates/memory-testing/Dockerfile.dockerignore b/crates/memory-testing/Dockerfile.dockerignore new file mode 100644 index 000000000..50f4b1230 --- /dev/null +++ b/crates/memory-testing/Dockerfile.dockerignore @@ -0,0 +1,4 @@ +* +!crates/* +!Cargo.toml +!Cargo.lock diff --git a/crates/memory-testing/cases.json b/crates/memory-testing/cases.json new file mode 100644 index 000000000..eb85e2aca --- /dev/null +++ b/crates/memory-testing/cases.json @@ -0,0 +1,9 @@ +{ + "symmetric_key": [ + { + "key": "FfhVVP8fmFIZY1WmRszPmRmVCxXNWVcJffPrbkywTPtBNkgfhYGT+D9sVGizYXrPffuj2yoyWqMwF9iF5aMQhQ==", + "decrypted_key_hex": "15f85554ff1f9852196355a646cccf9919950b15cd5957097df3eb6e4cb04cfb", + "decrypted_mac_hex": "4136481f858193f83f6c5468b3617acf7dfba3db2a325aa33017d885e5a31085" + } + ] +} diff --git a/crates/memory-testing/run_test.sh b/crates/memory-testing/run_test.sh new file mode 100755 index 000000000..627e5dacd --- /dev/null +++ b/crates/memory-testing/run_test.sh @@ -0,0 +1,20 @@ +# Move to the root of the repository +cd "$(dirname "$0")" +cd ../../ + +BASE_DIR="./crates/memory-testing" + +mkdir -p $BASE_DIR/output +rm $BASE_DIR/output/* + +cargo build -p memory-testing + +if [ "$1" = "no-docker" ]; then + # This specifically needs to run as root to be able to capture core dumps + sudo ./target/debug/capture-dumps ./target/debug/memory-testing $BASE_DIR +else + docker build -f crates/memory-testing/Dockerfile -t bitwarden/memory-testing . + docker run --rm -it -v $BASE_DIR:/output bitwarden/memory-testing +fi + +./target/debug/analyze-dumps $BASE_DIR diff --git a/crates/memory-testing/src/bin/analyze-dumps.rs b/crates/memory-testing/src/bin/analyze-dumps.rs new file mode 100644 index 000000000..fee72f2e5 --- /dev/null +++ b/crates/memory-testing/src/bin/analyze-dumps.rs @@ -0,0 +1,132 @@ +use std::{env, fmt::Display, io, path::Path, process}; + +use memory_testing::*; + +fn find_subarrays(needle: &[u8], haystack: &[u8]) -> Vec { + let needle_len = needle.len(); + let haystack_len = haystack.len(); + let mut subarrays = vec![]; + + if needle_len == 0 || haystack_len == 0 || needle_len > haystack_len { + return vec![]; + } + + for i in 0..=(haystack_len - needle_len) { + if &haystack[i..i + needle_len] == needle { + subarrays.push(i); + } + } + + subarrays +} + +const OK: &str = "✅"; +const FAIL: &str = "❌"; + +fn comma_sep(nums: &[usize]) -> String { + nums.iter() + .map(ToString::to_string) + .collect::>() + .join(", ") +} + +fn add_row( + table: &mut comfy_table::Table, + name: N, + initial_pos: &[usize], + final_pos: &[usize], + ok_cond: bool, +) -> bool { + table.add_row(vec![ + name.to_string(), + comma_sep(initial_pos), + comma_sep(final_pos), + if ok_cond { + OK.to_string() + } else { + FAIL.to_string() + }, + ]); + !ok_cond +} + +fn main() -> io::Result<()> { + let args: Vec = env::args().collect(); + if args.len() < 2 { + println!("Usage: ./analyze-dumps "); + process::exit(1); + } + let base_dir: &Path = args[1].as_ref(); + + println!("Memory testing script started"); + + let initial_core = std::fs::read(base_dir.join("output/initial_dump.bin"))?; + let final_core = std::fs::read(base_dir.join("output/final_dump.bin"))?; + + let mut error = false; + let mut table = comfy_table::Table::new(); + table.set_header(vec!["Name", "Initial", "Final", "OK"]); + + let cases = memory_testing::load_cases(base_dir); + + let test_string: Vec = TEST_STRING.as_bytes().to_vec(); + let test_initial_pos = find_subarrays(&test_string, &initial_core); + let test_final_pos = find_subarrays(&test_string, &final_core); + + error |= add_row( + &mut table, + "Test String", + &test_initial_pos, + &test_final_pos, + !test_final_pos.is_empty(), + ); + + if test_initial_pos.is_empty() { + println!("ERROR: Test string not found in initial core dump, is the dump valid?"); + error = true; + } + + for (idx, case) in cases.symmetric_key.iter().enumerate() { + let key_part: Vec = hex::decode(&case.decrypted_key_hex).unwrap(); + let mac_part: Vec = hex::decode(&case.decrypted_mac_hex).unwrap(); + let key_in_b64: Vec = case.key.as_bytes().to_vec(); + + let key_initial_pos = find_subarrays(&key_part, &initial_core); + let mac_initial_pos = find_subarrays(&mac_part, &initial_core); + let b64_initial_pos = find_subarrays(&key_in_b64, &initial_core); + + let key_final_pos = find_subarrays(&key_part, &final_core); + let mac_final_pos = find_subarrays(&mac_part, &final_core); + let b64_final_pos = find_subarrays(&key_in_b64, &final_core); + + error |= add_row( + &mut table, + format!("Symm. Key, case {}", idx), + &key_initial_pos, + &key_final_pos, + key_final_pos.is_empty(), + ); + + error |= add_row( + &mut table, + format!("Symm. MAC, case {}", idx), + &mac_initial_pos, + &mac_final_pos, + mac_final_pos.is_empty(), + ); + + // TODO: At the moment we are not zeroizing the base64 key in from_str, so this test is + // ignored + add_row( + &mut table, + format!("Symm. Key in Base64, case {}", idx), + &b64_initial_pos, + &b64_final_pos, + b64_final_pos.is_empty(), + ); + } + + println!("{table}"); + + process::exit(if error { 1 } else { 0 }); +} diff --git a/crates/memory-testing/src/bin/capture-dumps.rs b/crates/memory-testing/src/bin/capture-dumps.rs new file mode 100644 index 000000000..f43905867 --- /dev/null +++ b/crates/memory-testing/src/bin/capture-dumps.rs @@ -0,0 +1,70 @@ +use std::{ + fs, + io::{self, prelude::*}, + path::Path, + process::{Command, Stdio}, + thread::sleep, + time::Duration, +}; + +fn dump_process_to_bytearray(pid: u32, output_dir: &Path, output_name: &Path) -> io::Result { + Command::new("gcore") + .args(["-a", &pid.to_string()]) + .output()?; + + let core_path = format!("core.{}", pid); + let output_path = output_dir.join(output_name); + let len = fs::copy(&core_path, output_path)?; + fs::remove_file(&core_path)?; + Ok(len) +} + +fn main() -> io::Result<()> { + let args: Vec = std::env::args().collect(); + if args.len() < 3 { + println!("Usage: ./capture_dumps "); + std::process::exit(1); + } + + let binary_path = &args[1]; + let base_dir: &Path = args[2].as_ref(); + + println!("Memory dump capture script started"); + + let mut proc = Command::new(binary_path) + .arg(base_dir) + .stdout(Stdio::inherit()) + .stdin(Stdio::piped()) + .spawn()?; + let id = proc.id(); + println!("Started memory testing process with PID: {}", id); + let stdin = proc.stdin.as_mut().expect("Valid stdin"); + + // Wait a bit for it to process + sleep(Duration::from_secs(3)); + + // Dump the process before the variables are freed + let initial_core = + dump_process_to_bytearray(id, &base_dir.join("output"), "initial_dump.bin".as_ref())?; + println!("Initial core dump file size: {}", initial_core); + + stdin.write_all(b".")?; + stdin.flush()?; + + // Wait a bit for it to process + sleep(Duration::from_secs(1)); + + // Dump the process after the variables are freed + let final_core = + dump_process_to_bytearray(id, &base_dir.join("output"), "final_dump.bin".as_ref())?; + println!("Final core dump file size: {}", final_core); + + stdin.write_all(b".")?; + stdin.flush()?; + + // Wait for the process to finish and print the output + let output = proc.wait()?; + println!("Return code: {}", output); + + std::process::exit(output.code().unwrap_or(1)); +} diff --git a/crates/memory-testing/src/lib.rs b/crates/memory-testing/src/lib.rs new file mode 100644 index 000000000..e633756d1 --- /dev/null +++ b/crates/memory-testing/src/lib.rs @@ -0,0 +1,29 @@ +use std::path::Path; + +use zeroize::Zeroize; + +pub const TEST_STRING: &str = "THIS IS USED TO CHECK THAT THE MEMORY IS DUMPED CORRECTLY"; + +pub fn load_cases(base_dir: &Path) -> Cases { + let mut json_str = std::fs::read_to_string(base_dir.join("cases.json")).unwrap(); + let cases: Cases = serde_json::from_str(&json_str).unwrap(); + + // Make sure that we don't leave extra copies of the data in memory + json_str.zeroize(); + cases +} + +// Note: We don't actively zeroize these structs here because we want the code in bitwarden_crypto +// to handle it for us +#[derive(serde::Deserialize)] +pub struct Cases { + pub symmetric_key: Vec, +} + +#[derive(serde::Deserialize)] +pub struct SymmetricKeyCases { + pub key: String, + + pub decrypted_key_hex: String, + pub decrypted_mac_hex: String, +} diff --git a/crates/memory-testing/src/main.rs b/crates/memory-testing/src/main.rs new file mode 100644 index 000000000..96e4ae175 --- /dev/null +++ b/crates/memory-testing/src/main.rs @@ -0,0 +1,44 @@ +use std::{env, io::Read, path::Path, process, str::FromStr}; + +use bitwarden_crypto::SymmetricCryptoKey; + +fn wait_for_dump() { + println!("Waiting for dump..."); + std::io::stdin().read_exact(&mut [1u8]).unwrap(); +} + +fn main() { + let args: Vec = env::args().collect(); + if args.len() < 2 { + println!("Usage: ./memory-testing "); + process::exit(1); + } + let base_dir: &Path = args[1].as_ref(); + + let test_string = String::from(memory_testing::TEST_STRING); + + let cases = memory_testing::load_cases(base_dir); + + let mut symmetric_keys = Vec::new(); + let mut symmetric_keys_as_vecs = Vec::new(); + + for case in &cases.symmetric_key { + let key = SymmetricCryptoKey::from_str(&case.key).unwrap(); + symmetric_keys_as_vecs.push(key.to_vec()); + symmetric_keys.push(key); + } + + // Make a memory dump before the variables are freed + wait_for_dump(); + + // Use all the variables so the compiler doesn't decide to remove them + println!("{test_string} {symmetric_keys:?} {symmetric_keys_as_vecs:?}"); + + drop(symmetric_keys); + drop(symmetric_keys_as_vecs); + + // After the variables are dropped, we want to make another dump + wait_for_dump(); + + println!("Done!") +} From a9c4bcb4ee031da966eb5d195136243ac5a7c55e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ch=C4=99ci=C5=84ski?= Date: Mon, 11 Mar 2024 17:43:34 +0100 Subject: [PATCH 12/15] [DEVOPS-1750] Fix go release (#646) ## Type of change ``` - [ ] Bug fix - [ ] New feature development - [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc) - [x] Build/deploy pipeline (DevOps) - [ ] Other ``` ## Objective ## Code changes - **.github/workflows/build-rust-cross-platform.yml:** Add ARM64 linux build - **.github/workflows/release-go.yml** Download precompiled C artifacts and place them in the repo ## Before you submit - Please add **unit tests** where it makes sense to do so --------- Co-authored-by: Vince Grassia <593223+vgrassia@users.noreply.github.com> --- .../workflows/build-rust-cross-platform.yml | 1 + .github/workflows/release-go.yml | 70 +++++++++++++++++-- 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-rust-cross-platform.yml b/.github/workflows/build-rust-cross-platform.yml index 67c41c6f9..0db2d0cde 100644 --- a/.github/workflows/build-rust-cross-platform.yml +++ b/.github/workflows/build-rust-cross-platform.yml @@ -8,6 +8,7 @@ on: - main - rc - hotfix-rc + pull_request: jobs: build_rust: diff --git a/.github/workflows/release-go.yml b/.github/workflows/release-go.yml index a8bb82f88..cc17a4905 100644 --- a/.github/workflows/release-go.yml +++ b/.github/workflows/release-go.yml @@ -29,7 +29,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Branch check - if: ${{ github.event.inputs.release_type != 'Dry Run' }} + if: ${{ inputs.release_type != 'Dry Run' }} run: | if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc" ]]; then echo "===================================" @@ -58,6 +58,15 @@ jobs: with: path: sdk + - name: Download artifacts + uses: bitwarden/gh-actions/download-artifacts@main + with: + workflow: generate_schemas.yml + path: sdk/languages/go/bitwarden_sdk_secrets/lib + workflow_conclusion: success + branch: ${{ inputs.release_type == 'Dry Run' && 'main' || github.ref_name }} + artifacts: schemas.go + - name: Login to Azure - Prod Subscription uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: @@ -89,13 +98,19 @@ jobs: # Copy files to local sm-sdk-go repo path cp --verbose -rf sdk/languages/go/. sm-sdk-go + - name: Replace repo name + working-directory: sm-sdk-go + run: | + find . -name '*' -exec \ + gsed -i -e 's/github.com\/bitwarden\/sdk\/languages\/go/github.com\/bitwarden\/sm-sdk-go/g' {} \; + - name: Push changes working-directory: sm-sdk-go run: | git add . git commit -m "Update Go SDK to ${{ github.sha }}" - if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then + if [[ "${{ inputs.release_type }}" == "Dry Run" ]]; then echo "===================================" echo "[!] Dry Run - Skipping push" echo "===================================" @@ -106,7 +121,7 @@ jobs: fi - name: Create release tag on SDK Go repo - if: ${{ github.event.inputs.release_type != 'Dry Run' }} + if: ${{ inputs.release_type != 'Dry Run' }} working-directory: sm-sdk-go run: | # Check if tag exists, set output then exit 0 if true. @@ -141,8 +156,51 @@ jobs: keyvault: ${{ env._KEY_VAULT }} secrets: "github-pat-bitwarden-devops-bot-repo-scope" + - name: Download x86_64-apple-darwin artifact + uses: bitwarden/gh-actions/download-artifacts@main + with: + workflow: build-rust-cross-platform.yml + workflow_conclusion: success + branch: ${{ inputs.release_type == 'Dry Run' && 'main' || github.ref_name }} + artifacts: libbitwarden_c_files-x86_64-apple-darwin + skip_unpack: true + + - name: Download aarch64-apple-darwin artifact + uses: bitwarden/gh-actions/download-artifacts@main + with: + workflow: build-rust-cross-platform.yml + workflow_conclusion: success + branch: ${{ inputs.release_type == 'Dry Run' && 'main' || github.ref_name }} + artifacts: libbitwarden_c_files-aarch64-apple-darwin + skip_unpack: true + + - name: Download x86_64-unknown-linux-gnu artifact + uses: bitwarden/gh-actions/download-artifacts@main + with: + workflow: build-rust-cross-platform.yml + workflow_conclusion: success + branch: ${{ inputs.release_type == 'Dry Run' && 'main' || github.ref_name }} + artifacts: libbitwarden_c_files-x86_64-unknown-linux-gnu + skip_unpack: true + + - name: Download x86_64-pc-windows-msvc artifact + uses: bitwarden/gh-actions/download-artifacts@main + with: + workflow: build-rust-cross-platform.yml + workflow_conclusion: success + branch: ${{ inputs.release_type == 'Dry Run' && 'main' || github.ref_name }} + artifacts: libbitwarden_c_files-x86_64-pc-windows-msvc + skip_unpack: true + + - name: Rename build artifacts + run: | + mv libbitwarden_c_files-x86_64-apple-darwin.zip libbitwarden_c_files-x86_64-apple-darwin-$_PKG_VERSION.zip + mv libbitwarden_c_files-aarch64-apple-darwin.zip libbitwarden_c_files-aarch64-apple-darwin-$_PKG_VERSION.zip + mv libbitwarden_c_files-x86_64-unknown-linux-gnu.zip libbitwarden_c_files-x86_64-unknown-linux-gnu-$_PKG_VERSION.zip + mv libbitwarden_c_files-x86_64-pc-windows-msvc.zip libbitwarden_c_files-x86_64-pc-windows-msvc-$_PKG_VERSION.zip + - name: Create release - if: ${{ github.event.inputs.release_type != 'Dry Run' }} + if: ${{ inputs.release_type != 'Dry Run' }} uses: ncipollo/release-action@6c75be85e571768fa31b40abf38de58ba0397db5 # v1.13.0 with: tag: v${{ env._PKG_VERSION }} @@ -152,3 +210,7 @@ jobs: draft: true repo: sm-sdk-go owner: bitwarden + artifacts: "libbitwarden_c_files-x86_64-apple-darwin-$_PKG_VERSION.zip, + libbitwarden_c_files-aarch64-apple-darwin-$_PKG_VERSION.zip, + libbitwarden_c_files-x86_64-unknown-linux-gnu-$_PKG_VERSION.zip, + libbitwarden_c_files-x86_64-pc-windows-msvc-$_PKG_VERSION.zip" From 0bf163659ce220fda6d8ff5267f0a2e240d9dbc6 Mon Sep 17 00:00:00 2001 From: Robyn MacCallum Date: Mon, 11 Mar 2024 19:43:29 -0400 Subject: [PATCH 13/15] Ruby updates (#654) ## Type of change ``` - [ ] Bug fix - [ ] New feature development - [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc) - [ ] Build/deploy pipeline (DevOps) - [x] Other ``` ## Objective Fix ruby release date and add state to the ruby example ## Before you submit - Please add **unit tests** where it makes sense to do so --- languages/ruby/CHANGELOG.md | 3 ++- languages/ruby/examples/example.rb | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/languages/ruby/CHANGELOG.md b/languages/ruby/CHANGELOG.md index 91c5ee3c2..0c842ecbb 100644 --- a/languages/ruby/CHANGELOG.md +++ b/languages/ruby/CHANGELOG.md @@ -1,5 +1,6 @@ ## [Unreleased] -## [0.1.0] - 2023-09-19 +## [0.1.0] - 2024-02-23 - Initial release + diff --git a/languages/ruby/examples/example.rb b/languages/ruby/examples/example.rb index 3c8e2045d..d1c7ce455 100644 --- a/languages/ruby/examples/example.rb +++ b/languages/ruby/examples/example.rb @@ -3,6 +3,7 @@ token = ENV['ACCESS_TOKEN'] organization_id = ENV['ORGANIZATION_ID'] +state_path = ENV['STATE_PATH'] # Configuring the URLS is optional, set them to nil to use the default values api_url = ENV['API_URL'] @@ -11,7 +12,7 @@ bitwarden_settings = BitwardenSDKSecrets::BitwardenSettings.new(api_url, identity_url) bw_client = BitwardenSDKSecrets::BitwardenClient.new(bitwarden_settings) -response = bw_client.access_token_login(token) +response = bw_client.access_token_login(token, state_path) puts response # CREATE project From ec54a8933b9dd0f56e0fa0b7ebb8b00b330e65a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ch=C4=99ci=C5=84ski?= Date: Tue, 12 Mar 2024 11:13:01 +0100 Subject: [PATCH 14/15] [DEVOPS-1750] Fix Go release artifacts (#655) ## Type of change ``` - [x] Bug fix - [ ] New feature development - [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc) - [x] Build/deploy pipeline (DevOps) - [ ] Other ``` ## Objective Fix version reference in release artifacts in Go release workflow. ## Code changes - **.github/workflows/release-go.yml:** Fix version reference in release artifacts ## Before you submit - Please add **unit tests** where it makes sense to do so --- .github/workflows/release-go.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release-go.yml b/.github/workflows/release-go.yml index cc17a4905..c7a198056 100644 --- a/.github/workflows/release-go.yml +++ b/.github/workflows/release-go.yml @@ -210,7 +210,7 @@ jobs: draft: true repo: sm-sdk-go owner: bitwarden - artifacts: "libbitwarden_c_files-x86_64-apple-darwin-$_PKG_VERSION.zip, - libbitwarden_c_files-aarch64-apple-darwin-$_PKG_VERSION.zip, - libbitwarden_c_files-x86_64-unknown-linux-gnu-$_PKG_VERSION.zip, - libbitwarden_c_files-x86_64-pc-windows-msvc-$_PKG_VERSION.zip" + artifacts: "libbitwarden_c_files-x86_64-apple-darwin-${{ env._PKG_VERSION }}.zip, + libbitwarden_c_files-aarch64-apple-darwin-${{ env._PKG_VERSION }}.zip, + libbitwarden_c_files-x86_64-unknown-linux-gnu-${{ env._PKG_VERSION }}.zip, + libbitwarden_c_files-x86_64-pc-windows-msvc-${{ env._PKG_VERSION }}.zip" From 07a4ceb663aab1b057beb54c6856073a98d9da54 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Tue, 12 Mar 2024 16:19:25 +0100 Subject: [PATCH 15/15] Move access token to auth (#656) Move access token from client to auth. --- crates/bitwarden/CHANGELOG.md | 3 ++- .../src/{client => auth}/access_token.rs | 6 ++---- crates/bitwarden/src/auth/login/access_token.rs | 4 ++-- crates/bitwarden/src/auth/mod.rs | 2 ++ crates/bitwarden/src/client/client.rs | 16 ++++++++-------- crates/bitwarden/src/client/mod.rs | 2 -- crates/bitwarden/src/secrets_manager/state.rs | 2 +- crates/bws/src/main.rs | 4 ++-- 8 files changed, 19 insertions(+), 20 deletions(-) rename crates/bitwarden/src/{client => auth}/access_token.rs (97%) diff --git a/crates/bitwarden/CHANGELOG.md b/crates/bitwarden/CHANGELOG.md index ff47c19d8..ee4c6033b 100644 --- a/crates/bitwarden/CHANGELOG.md +++ b/crates/bitwarden/CHANGELOG.md @@ -9,7 +9,8 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Changed -- Switched TLS backend to `rustls`, removing the dependency on `OpenSSL`. +- Switched TLS backend to `rustls`, removing the dependency on `OpenSSL`. (#374) +- `client::AccessToken` is now `auth::AccessToken`. (#656) ## [0.4.0] - 2023-12-21 diff --git a/crates/bitwarden/src/client/access_token.rs b/crates/bitwarden/src/auth/access_token.rs similarity index 97% rename from crates/bitwarden/src/client/access_token.rs rename to crates/bitwarden/src/auth/access_token.rs index d7cad2fca..667a78529 100644 --- a/crates/bitwarden/src/client/access_token.rs +++ b/crates/bitwarden/src/auth/access_token.rs @@ -65,12 +65,12 @@ impl FromStr for AccessToken { #[cfg(test)] mod tests { + use super::AccessToken; + #[test] fn can_decode_access_token() { use std::str::FromStr; - use crate::client::AccessToken; - let access_token = "0.ec2c1d46-6a4b-4751-a310-af9601317f2d.C2IgxjjLF7qSshsbwe8JGcbM075YXw:X8vbvA0bduihIDe/qrzIQQ=="; let token = AccessToken::from_str(access_token).unwrap(); @@ -86,8 +86,6 @@ mod tests { fn malformed_tokens() { use std::str::FromStr; - use crate::client::AccessToken; - // Encryption key without base64 padding, we generate it with padding but ignore it when // decoding let t = "0.ec2c1d46-6a4b-4751-a310-af9601317f2d.C2IgxjjLF7qSshsbwe8JGcbM075YXw:X8vbvA0bduihIDe/qrzIQQ"; diff --git a/crates/bitwarden/src/auth/login/access_token.rs b/crates/bitwarden/src/auth/login/access_token.rs index d540b31a8..46c3f2b50 100644 --- a/crates/bitwarden/src/auth/login/access_token.rs +++ b/crates/bitwarden/src/auth/login/access_token.rs @@ -11,9 +11,9 @@ use crate::{ auth::{ api::{request::AccessTokenRequest, response::IdentityTokenResponse}, login::{response::two_factor::TwoFactorProviders, PasswordLoginResponse}, - JWTToken, + AccessToken, JWTToken, }, - client::{AccessToken, LoginMethod, ServiceAccountLoginMethod}, + client::{LoginMethod, ServiceAccountLoginMethod}, error::{Error, Result}, secrets_manager::state::{self, ClientState}, Client, diff --git a/crates/bitwarden/src/auth/mod.rs b/crates/bitwarden/src/auth/mod.rs index 021c97c0f..7918694e2 100644 --- a/crates/bitwarden/src/auth/mod.rs +++ b/crates/bitwarden/src/auth/mod.rs @@ -1,3 +1,4 @@ +mod access_token; pub(super) mod api; pub mod client_auth; mod jwt_token; @@ -5,6 +6,7 @@ pub mod login; #[cfg(feature = "internal")] pub mod password; pub mod renew; +pub use access_token::AccessToken; pub use jwt_token::JWTToken; #[cfg(feature = "internal")] mod register; diff --git a/crates/bitwarden/src/client/client.rs b/crates/bitwarden/src/client/client.rs index 45f58444c..883720529 100644 --- a/crates/bitwarden/src/client/client.rs +++ b/crates/bitwarden/src/client/client.rs @@ -9,9 +9,16 @@ use chrono::Utc; use reqwest::header::{self, HeaderValue}; use uuid::Uuid; -use super::AccessToken; #[cfg(feature = "secrets")] use crate::auth::login::{AccessTokenLoginRequest, AccessTokenLoginResponse}; +use crate::{ + auth::AccessToken, + client::{ + client_settings::{ClientSettings, DeviceType}, + encryption_settings::EncryptionSettings, + }, + error::{Error, Result}, +}; #[cfg(feature = "internal")] use crate::{ client::flags::Flags, @@ -20,13 +27,6 @@ use crate::{ UserApiKeyResponse, }, }; -use crate::{ - client::{ - client_settings::{ClientSettings, DeviceType}, - encryption_settings::EncryptionSettings, - }, - error::{Error, Result}, -}; #[derive(Debug)] pub(crate) struct ApiConfigurations { diff --git a/crates/bitwarden/src/client/mod.rs b/crates/bitwarden/src/client/mod.rs index f6d60e961..0c703570f 100644 --- a/crates/bitwarden/src/client/mod.rs +++ b/crates/bitwarden/src/client/mod.rs @@ -1,7 +1,6 @@ //! Bitwarden SDK Client pub(crate) use client::*; -pub(crate) mod access_token; #[allow(clippy::module_inception)] mod client; pub mod client_settings; @@ -10,5 +9,4 @@ pub(crate) mod encryption_settings; #[cfg(feature = "internal")] mod flags; -pub use access_token::AccessToken; pub use client::Client; diff --git a/crates/bitwarden/src/secrets_manager/state.rs b/crates/bitwarden/src/secrets_manager/state.rs index 4efa4403b..b2b6f6a8e 100644 --- a/crates/bitwarden/src/secrets_manager/state.rs +++ b/crates/bitwarden/src/secrets_manager/state.rs @@ -4,7 +4,7 @@ use bitwarden_crypto::{EncString, KeyDecryptable, KeyEncryptable}; use serde::{Deserialize, Serialize}; use crate::{ - client::AccessToken, + auth::AccessToken, error::{Error, Result}, }; diff --git a/crates/bws/src/main.rs b/crates/bws/src/main.rs index cb130b52c..6ed78f7b7 100644 --- a/crates/bws/src/main.rs +++ b/crates/bws/src/main.rs @@ -1,8 +1,8 @@ use std::{path::PathBuf, process, str::FromStr}; use bitwarden::{ - auth::login::AccessTokenLoginRequest, - client::{client_settings::ClientSettings, AccessToken}, + auth::{login::AccessTokenLoginRequest, AccessToken}, + client::client_settings::ClientSettings, secrets_manager::{ projects::{ ProjectCreateRequest, ProjectGetRequest, ProjectPutRequest, ProjectsDeleteRequest,