Skip to content

[RFC] Implement password-protected key envelope #335

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 126 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
126 commits
Select commit Hold shift + click to select a range
bc5900b
Implement cose content format
quexten May 9, 2025
2e905c4
Cargo fmt
quexten May 9, 2025
b41f5ef
Fix docs
quexten May 9, 2025
eb70dfc
Fix formatting
quexten May 9, 2025
1e2e95c
Merge main
quexten May 9, 2025
68be6e6
Fix formatting
quexten May 9, 2025
d6a18a4
Fix comment
quexten May 9, 2025
6e9e526
Cleanup
quexten May 21, 2025
50d8f70
Switch from pass by ref to pass by value for enum
quexten May 21, 2025
e5bd251
Add CompositeEncryptable trait
quexten May 21, 2025
b92010b
Cleanup
quexten May 21, 2025
9635098
Merge branch 'main' into km/cose-content-format
quexten May 21, 2025
b8056c2
Fix typo
quexten May 21, 2025
9440825
Typed encryptable
quexten May 21, 2025
f8cb804
Apply cargo fmt
quexten May 22, 2025
e6939e6
Fix build
quexten May 22, 2025
2a202c1
Remove unused imports
quexten May 22, 2025
ba9e2c3
Rename to primitiveencryptablewithcontenttype
quexten May 22, 2025
a42b1e7
Set correct content type for pkcs8
quexten May 22, 2025
5447cbb
Add cose keywrap
quexten May 22, 2025
66c345f
Fix keywrap
quexten May 22, 2025
9b2f6c9
Run cargo fmt
quexten May 22, 2025
88b96b7
Fix encryptable
quexten May 22, 2025
7e69b7b
Run cargo fmt
quexten May 22, 2025
b7d2bc8
Cleanup
quexten May 29, 2025
acb6d12
Rename to PrimitiveEncryptableWithoutContentFormat
quexten May 29, 2025
723ec63
Rename to PrimitiveEncryptable
quexten May 29, 2025
36d8de9
Merge branch 'main' into km/cose-content-format
quexten May 29, 2025
33da07b
Add documentation for the encryptable traits
quexten May 29, 2025
2861a9a
Merge branch 'km/cose-content-format' of github.com:bitwarden/sdk-intโ€ฆ
quexten May 29, 2025
1681df9
Fix clippy errors
quexten Jun 2, 2025
0cd85fa
Fix docs
quexten Jun 2, 2025
f91f0b8
Cargo fmt
quexten Jun 2, 2025
34ee00e
Fix docs
quexten Jun 2, 2025
485b6d8
Fix docs
quexten Jun 2, 2025
f122ff0
Fix docs
quexten Jun 2, 2025
f22531c
Update crates/bitwarden-crypto/src/keys/utils.rs
quexten Jun 4, 2025
ba10631
Update crates/bitwarden-crypto/src/keys/utils.rs
quexten Jun 4, 2025
69d8c57
Update crates/bitwarden-crypto/src/keys/utils.rs
quexten Jun 4, 2025
8833146
Merge branch 'main' into km/cose-content-format
quexten Jun 4, 2025
47ced5a
Merge branch 'main' into km/cose-content-format
quexten Jun 6, 2025
42dccb0
Add docs
quexten Jun 6, 2025
9d0ea55
Merge branch 'km/cose-content-format' of github.com:bitwarden/sdk-intโ€ฆ
quexten Jun 6, 2025
a3ed9d6
Merge branch 'main' into km/cose-content-format
quexten Jun 16, 2025
9eceb32
Cargo fmt
quexten Jun 16, 2025
f30c3ce
Apply fixes
quexten Jun 16, 2025
5f4dc3a
Cleanup
quexten Jun 16, 2025
7b17ca3
Cleanup
quexten Jun 16, 2025
38c8945
Cleanup
quexten Jun 16, 2025
b5dd862
Apply more fixes
quexten Jun 16, 2025
47c7764
Apply fixes
quexten Jun 16, 2025
b009c81
Apply fixes
quexten Jun 16, 2025
c9f6111
Update test vector to include content type
quexten Jun 17, 2025
b9b0f6e
Add bitwarden legacy content type
quexten Jun 17, 2025
67dd5e9
Move content format to separate file
quexten Jun 17, 2025
7635cd0
Remove unused import
quexten Jun 17, 2025
ec14e51
Fix missing parsing for content type
quexten Jun 17, 2025
75be6db
Merge branch 'main' into km/cose-content-format
quexten Jun 17, 2025
6d8bb8f
Fix doc error
quexten Jun 17, 2025
38b8958
Typed byte arrays
quexten Jun 19, 2025
9f334fb
Fix readme
quexten Jun 19, 2025
02cc7b3
Clippy cleanup
quexten Jun 19, 2025
f145eb3
Fix clippy errors
quexten Jun 19, 2025
99d909c
Fix clippy errors
quexten Jun 19, 2025
d47e8c0
Switch bitwarden symmetric crypto key bytes to serialized bytes generic
quexten Jun 19, 2025
1920a49
Rename to bytes
quexten Jun 19, 2025
625b830
Cargo fmt
quexten Jun 19, 2025
24f1431
Simplify encoded symmetric key
quexten Jun 19, 2025
9eb4ff8
Merge branch 'main' into km/cose-content-format
quexten Jun 19, 2025
916a46e
Remove unwrap in example
quexten Jun 19, 2025
8722077
Merge branch 'km/cose-content-format' of github.com:bitwarden/sdk-intโ€ฆ
quexten Jun 19, 2025
670c6a7
Cleanup
quexten Jun 19, 2025
6da4a0b
Fix docs
quexten Jun 19, 2025
4e5510b
Merge branch 'main' into km/cose-content-format
quexten Jun 23, 2025
b00f48b
Make content format trait sealed and add type aliases
quexten Jun 24, 2025
bf9f8c4
Merge branch 'km/cose-content-format' of github.com:bitwarden/sdk-intโ€ฆ
quexten Jun 24, 2025
235f3bc
Apply cargo fmt
quexten Jun 24, 2025
1953ba3
Apply fixes
quexten Jun 24, 2025
41bb1ad
Replace non type aliased Bytes references with type aliases
quexten Jun 24, 2025
c41e513
Apply clippy fixes
quexten Jun 24, 2025
90d2295
Update crates/bitwarden-crypto/src/enc_string/symmetric.rs
quexten Jun 25, 2025
de8f957
Update crates/bitwarden-crypto/src/keys/signed_public_key.rs
quexten Jun 25, 2025
f6ad513
Update crates/bitwarden-crypto/src/enc_string/symmetric.rs
quexten Jun 25, 2025
3c2984b
Update crates/bitwarden-crypto/src/fingerprint.rs
quexten Jun 25, 2025
bdc90b3
Update crates/bitwarden-crypto/src/signing/signed_object.rs
quexten Jun 25, 2025
80dde40
Update crates/bitwarden-crypto/src/signing/signed_object.rs
quexten Jun 25, 2025
1f30896
Update crates/bitwarden-crypto/README.md
quexten Jun 25, 2025
1817ae0
Update crates/bitwarden-core/src/key_management/crypto.rs
quexten Jun 25, 2025
416dbf5
Update crates/bitwarden-core/src/client/encryption_settings.rs
quexten Jun 25, 2025
aaad503
Fix build and cleanup
quexten Jun 25, 2025
2e21906
Remove to_vec from VerifyingKey usages
quexten Jun 25, 2025
dbdee15
Undo take
quexten Jun 25, 2025
375fd0d
Unapply allow missing docs
quexten Jun 25, 2025
fd22ded
Clean up KeyEncryptable or pin key
quexten Jun 25, 2025
7ee0278
Cleanup
quexten Jun 25, 2025
9b3549a
Apply cleanup
quexten Jun 25, 2025
a5abaae
Undo changes to crypto init
quexten Jun 25, 2025
81138ea
Apply allow private interfaces to content format
quexten Jun 25, 2025
4445aec
Cleanup
quexten Jun 25, 2025
25d1907
Typesafe base64 handling
quexten Jun 25, 2025
0d0f59b
Cleanup
quexten Jun 25, 2025
e3ee279
Cleanup cose sign1 types
quexten Jun 25, 2025
bf7bc82
Revert "Typesafe base64 handling"
quexten Jun 26, 2025
1da0173
Revert "Cleanup"
quexten Jun 26, 2025
87f87a6
Cleanup
quexten Jun 26, 2025
15ffca9
Clippy fix
quexten Jun 26, 2025
144620f
Move use under internal flag
quexten Jun 26, 2025
53aeeee
Merge branch 'main' into km/cose-content-format
quexten Jun 26, 2025
81b5a15
Update crates/bitwarden-core/src/client/encryption_settings.rs
quexten Jun 26, 2025
9427634
Update crates/bitwarden-core/src/client/encryption_settings.rs
quexten Jun 26, 2025
d1f8029
Move cose content format trait higher
quexten Jun 26, 2025
7f52fb0
Add docs
quexten Jun 26, 2025
53c528b
Update crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs
quexten Jun 26, 2025
c51e779
Update crates/bitwarden-crypto/src/keys/device_key.rs
quexten Jun 26, 2025
6c8092a
Update crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs
quexten Jun 26, 2025
954a6a5
Clean up symmetric crypto key
quexten Jun 26, 2025
948baa5
Merge branch 'km/cose-content-format' of github.com:bitwarden/sdk-intโ€ฆ
quexten Jun 26, 2025
b2f5211
Fix encryptable docs
quexten Jun 26, 2025
f0b6ec5
Cleanup
quexten Jun 26, 2025
390463c
Small cleanup
quexten Jun 26, 2025
a2e243e
Implement password-protected key envelope
quexten Jun 27, 2025
616786e
Add cfg(test) to function
quexten Jun 27, 2025
b913762
Cargo format
quexten Jun 27, 2025
120f8c6
Fix clippy errors
quexten Jun 27, 2025
b1a615a
Merge branch 'main' into km/beeep/safe-password-protected-key-envelope
quexten Jul 7, 2025
25700ef
Fix private const
quexten Jul 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions crates/bitwarden-crypto/examples/protect_key_with_password.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
//! This example demonstrates how to securely protect keys with a password using the
//! [PasswordProtectedKeyEnvelope].

use bitwarden_crypto::{
key_ids, KeyStore, KeyStoreContext, PasswordProtectedKeyEnvelope,
PasswordProtectedKeyEnvelopeError,
};

fn main() {
let key_story = KeyStore::<ExampleIds>::default();
let mut ctx: KeyStoreContext<'_, ExampleIds> = key_story.context_mut();
let mut disk = MockDisk::new();

// Alice wants to protect a key with a password.
// For example to:
// - Protect her vault with a pin
// - Protect her exported vault with a password
// - Protect a send with a URL fragment secret
// For this, the `PasswordProtectedKeyEnvelope` is used.

// Alice has a vault protected with a symmetric key. She wants this protected with a PIN.
let vault_key = ctx
.generate_symmetric_key(ExampleSymmetricKey::VaultKey)
.unwrap();

// Seal the key with the PIN
// The KDF settings are chosen for you, and do not need to be separately tracked or synced
// Next, story this protected key envelope on disk.
let pin = "1234";
let envelope =
PasswordProtectedKeyEnvelope::seal(vault_key, pin, &ctx).expect("Sealing should work");
disk.save("vault_key_envelope", (&envelope).try_into().unwrap());

// Wipe the context to simulate new session
ctx.clear_local();

// Load the envelope from disk and unseal it with the PIN, and store it in the context.
let deserialized: PasswordProtectedKeyEnvelope<ExampleIds> =
PasswordProtectedKeyEnvelope::try_from(disk.load("vault_key_envelope").unwrap()).unwrap();
deserialized
.unseal(ExampleSymmetricKey::VaultKey, pin, &mut ctx)
.unwrap();

// Alice wants to change her password; also her KDF settings are below the minimums.
// Re-sealing will update the password, and KDF settings.
let envelope = envelope
.reseal(pin, "0000")
.expect("The password should be valid");
disk.save("vault_key_envelope", (&envelope).try_into().unwrap());

// Alice wants to change the protected key. This requires creating a new envelope
ctx.generate_symmetric_key(ExampleSymmetricKey::VaultKey)
.unwrap();
let envelope = PasswordProtectedKeyEnvelope::seal(ExampleSymmetricKey::VaultKey, "0000", &ctx)
.expect("Sealing should work");
disk.save("vault_key_envelope", (&envelope).try_into().unwrap());

// Alice tries the password but it is wrong
assert!(matches!(
envelope.unseal(ExampleSymmetricKey::VaultKey, "9999", &mut ctx),
Err(PasswordProtectedKeyEnvelopeError::WrongPassword)
));
}

pub(crate) struct MockDisk {
map: std::collections::HashMap<String, Vec<u8>>,
}

impl MockDisk {
pub(crate) fn new() -> Self {
MockDisk {
map: std::collections::HashMap::new(),
}
}

pub(crate) fn save(&mut self, key: &str, value: Vec<u8>) {
self.map.insert(key.to_string(), value);
}

pub(crate) fn load(&self, key: &str) -> Option<&Vec<u8>> {
self.map.get(key)
}
}

key_ids! {
#[symmetric]
pub enum ExampleSymmetricKey {
#[local]
VaultKey
}

#[asymmetric]
pub enum ExampleAsymmetricKey {
Key(u8),
}

#[signing]
pub enum ExampleSigningKey {
Key(u8),
}

pub ExampleIds => ExampleSymmetricKey, ExampleAsymmetricKey, ExampleSigningKey;
}
8 changes: 7 additions & 1 deletion crates/bitwarden-crypto/src/cose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,16 @@ use crate::{
pub(crate) const XCHACHA20_POLY1305: i64 = -70000;
const XCHACHA20_TEXT_PAD_BLOCK_SIZE: usize = 32;

pub(crate) const ALG_ARGON2ID13: i64 = -71000;
pub(crate) const ARGON2_SALT: i64 = -71001;
pub(crate) const ARGON2_ITERATIONS: i64 = -71002;
pub(crate) const ARGON2_MEMORY: i64 = -71003;
pub(crate) const ARGON2_PARALLELISM: i64 = -71004;

// Note: These are in the "unregistered" tree: https://datatracker.ietf.org/doc/html/rfc6838#section-3.4
// These are only used within Bitwarden, and not meant for exchange with other systems.
const CONTENT_TYPE_PADDED_UTF8: &str = "application/x.bitwarden.utf8-padded";
const CONTENT_TYPE_BITWARDEN_LEGACY_KEY: &str = "application/x.bitwarden.legacy-key";
pub(crate) const CONTENT_TYPE_BITWARDEN_LEGACY_KEY: &str = "application/x.bitwarden.legacy-key";
const CONTENT_TYPE_SPKI_PUBLIC_KEY: &str = "application/x.bitwarden.spki-public-key";

// Labels
Expand Down
2 changes: 1 addition & 1 deletion crates/bitwarden-crypto/src/keys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ mod symmetric_crypto_key;
#[cfg(test)]
pub use symmetric_crypto_key::derive_symmetric_key;
pub use symmetric_crypto_key::{
Aes256CbcHmacKey, Aes256CbcKey, SymmetricCryptoKey, XChaCha20Poly1305Key,
Aes256CbcHmacKey, Aes256CbcKey, EncodedSymmetricKey, SymmetricCryptoKey, XChaCha20Poly1305Key,
};
mod asymmetric_crypto_key;
pub use asymmetric_crypto_key::{
Expand Down
1 change: 1 addition & 0 deletions crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ impl From<EncodedSymmetricKey> for Vec<u8> {
}
}
impl EncodedSymmetricKey {
/// Returns the content format of the encoded symmetric key.
#[allow(private_interfaces)]
pub fn content_format(&self) -> ContentFormat {
match self {
Expand Down
2 changes: 2 additions & 0 deletions crates/bitwarden-crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ pub use store::{
};
mod cose;
pub use cose::CoseSerializable;
mod safe;
pub use safe::*;
mod signing;
pub use signing::*;
mod traits;
Expand Down
2 changes: 2 additions & 0 deletions crates/bitwarden-crypto/src/safe/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod password_protected_key_envelope;
pub use password_protected_key_envelope::*;
Loading
Loading