Skip to content

[PM-20361] Signature keys #207

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 21 commits into
base: km/cose
Choose a base branch
from
Draft
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/bitwarden-crypto/Cargo.toml
Original file line number Diff line number Diff line change
@@ -33,6 +33,8 @@ cbc = { version = ">=0.1.2, <0.2", features = ["alloc", "zeroize"] }
chacha20poly1305 = { version = "0.10.1" }
ciborium = { version = ">=0.2.2, <0.3" }
coset = { version = ">=0.3.8, <0.4" }
curve25519-dalek = ">=4.1.3, <4.2.0"
ed25519-dalek = { version = ">=2.1.1, <2.2.0", features = ["rand_core"] }
generic-array = { version = ">=0.14.7, <1.0", features = ["zeroize"] }
hkdf = ">=0.12.3, <0.13"
hmac = ">=0.12.1, <0.13"
7 changes: 7 additions & 0 deletions crates/bitwarden-crypto/src/cose.rs
Original file line number Diff line number Diff line change
@@ -2,7 +2,14 @@
//! Standardized values from <https://www.iana.org/assignments/cose/cose.xhtml> should always be preferred
//! unless there is a specific reason to use a private-use value.
// Algorithms
//
// XChaCha20 <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha-03> is used over ChaCha20
// to be able to randomly generate nonces, and to not have to worry about key wearout. Since
// the draft was never published as an RFC, we use a private-use value for the algorithm.
pub(crate) const XCHACHA20_POLY1305: i64 = -70000;

// Labels
//
// The label used for the namespace ensuring strong domain separation when using signatures.
pub(crate) const SIGNING_NAMESPACE: i64 = -80000;
6 changes: 6 additions & 0 deletions crates/bitwarden-crypto/src/error.rs
Original file line number Diff line number Diff line change
@@ -54,6 +54,12 @@ pub enum CryptoError {
#[error("Key algorithm does not match encrypted data type")]
WrongKeyType,

#[error("Invalid signature")]
InvalidSignature,

#[error("Invalid namespace")]
InvalidNamespace,

#[error("Invalid nonce length")]
InvalidNonceLength,
}
10 changes: 10 additions & 0 deletions crates/bitwarden-crypto/src/keys/key_id.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use rand::RngCore;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct KeyId([u8; 24]);

/// Fixed length identifiers for keys.
@@ -24,3 +25,12 @@ impl KeyId {
Self::from_bytes(key_id)
}
}

impl TryFrom<&[u8]> for KeyId {
type Error = std::array::TryFromSliceError;

fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
let key_id = <[u8; 24]>::try_from(value)?;
Ok(Self::from_bytes(key_id))
}
}
1 change: 1 addition & 0 deletions crates/bitwarden-crypto/src/keys/mod.rs
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@ mod pin_key;
pub use pin_key::PinKey;
mod kdf;
mod key_id;
mod signing_crypto_key;
pub use kdf::{
default_argon2_iterations, default_argon2_memory, default_argon2_parallelism,
default_pbkdf2_iterations, Kdf,
420 changes: 420 additions & 0 deletions crates/bitwarden-crypto/src/keys/signing_crypto_key.rs

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions crates/bitwarden-crypto/src/lib.rs
Original file line number Diff line number Diff line change
@@ -31,6 +31,8 @@ pub use wordlist::EFF_LONG_WORD_LIST;
mod store;
pub use store::{KeyStore, KeyStoreContext};
mod cose;
mod signing;
pub(crate) use signing::SigningNamespace;
mod traits;
mod xchacha20;
pub use traits::{Decryptable, Encryptable, IdentifyKey, KeyId, KeyIds};
27 changes: 27 additions & 0 deletions crates/bitwarden-crypto/src/signing/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use crate::CryptoError;

/// Signing is domain-separated within bitwarden, to prevent cross protocol attacks.
///
/// A new signed entity or protocol shall use a new signing namespace.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SigningNamespace {
#[allow(dead_code)]
EncryptionMetadata = 1,
#[cfg(test)]
Test = -1,
}

impl SigningNamespace {
pub fn as_i64(&self) -> i64 {
*self as i64
}

pub fn try_from_i64(value: i64) -> Result<Self, CryptoError> {
match value {
1 => Ok(Self::EncryptionMetadata),
#[cfg(test)]
-1 => Ok(Self::Test),
_ => Err(CryptoError::InvalidNamespace),
}
}
}