Skip to content

Commit 7597dcf

Browse files
committed
Implement purecrypto functions, fingerprints, and signing key initialization
1 parent 2366f84 commit 7597dcf

File tree

16 files changed

+365
-77
lines changed

16 files changed

+365
-77
lines changed

Cargo.lock

Lines changed: 23 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/bitwarden-core/src/mobile/crypto.rs

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ use std::collections::HashMap;
88

99
use base64::{engine::general_purpose::STANDARD, Engine};
1010
use bitwarden_crypto::{
11-
AsymmetricCryptoKey, CryptoError, EncString, Kdf, KeyDecryptable, KeyEncryptable, MasterKey,
12-
SigningKey, SymmetricCryptoKey, UnsignedSharedKey, UserKey,
11+
AsymmetricCryptoKey, AsymmetricPublicCryptoKey, CryptoError, EncString, Kdf, KeyDecryptable, KeyEncryptable, MasterKey, SignatureAlgorithm, SignedPublicKeyOwnershipClaim, SigningKey, SymmetricCryptoKey, UnsignedSharedKey, UserKey
1312
};
1413
use schemars::JsonSchema;
1514
use serde::{Deserialize, Serialize};
@@ -18,7 +17,7 @@ use {tsify_next::Tsify, wasm_bindgen::prelude::*};
1817

1918
use crate::{
2019
client::{encryption_settings::EncryptionSettingsError, LoginMethod, UserLoginMethod},
21-
key_management::SymmetricKeyId,
20+
key_management::{AsymmetricKeyId, SymmetricKeyId},
2221
Client, NotAuthenticatedError, VaultLockedError, WrongPasswordError,
2322
};
2423

@@ -558,27 +557,47 @@ pub(super) fn verify_asymmetric_keys(
558557
#[serde(rename_all = "camelCase", deny_unknown_fields)]
559558
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
560559
#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
561-
pub struct MakeSigningKeysResponse {
560+
pub struct MakeUserSigningKeysResponse {
562561
/// The verifying key
563562
verifying_key: String,
564563
/// Signing key, encrypted with a symmetric key (user key, org key)
565564
signing_key: EncString,
565+
566+
/// A signed object claiming ownership of a public key. This ties the public key to the signature key
567+
signed_public_key_ownership_claim: String,
566568
}
567569

568-
pub fn make_signing_keys(wrapping_key: String) -> Result<MakeSigningKeysResponse, CryptoError> {
569-
let wrapping_key = SymmetricCryptoKey::try_from(wrapping_key)?;
570+
/// Makes a new set of signing keys for a user. This also creates a signed public-key ownership claim for the currently used public key.
571+
#[allow(deprecated)]
572+
pub fn make_user_signing_keys(client: &Client) -> Result<MakeUserSigningKeysResponse, CryptoError> {
573+
let key_store = client.internal.get_key_store();
574+
let ctx = key_store.context();
575+
let public_key = ctx
576+
.dangerous_get_asymmetric_key(AsymmetricKeyId::UserPrivateKey)
577+
.map_err(|_| CryptoError::InvalidKey)?
578+
.to_public_der()?;
579+
let public_key = AsymmetricPublicCryptoKey::from_der(&public_key)
580+
.map_err(|_| CryptoError::InvalidKey)?;
581+
582+
let wrapping_key = ctx
583+
.dangerous_get_symmetric_key(SymmetricKeyId::User)
584+
.map_err(|_| CryptoError::InvalidKey)?;
570585
let signature_keypair = SigningKey::make_ed25519().unwrap();
571586
// This needs to be changed to use the correct cose content format before rolling out to real
572587
// accounts
573588
let encrypted_signing_key = signature_keypair
574-
.to_cose()?
575-
.encrypt_with_key(&wrapping_key)?;
589+
.to_cose()?;
576590
let serialized_verifying_key = signature_keypair.to_verifying_key().to_cose()?;
577591
let serialized_verifying_key_b64 = STANDARD.encode(serialized_verifying_key);
592+
let signed_public_key_ownership_claim = SignedPublicKeyOwnershipClaim::make_claim_with_key(
593+
&public_key,
594+
&signature_keypair,
595+
)?;
578596

579-
Ok(MakeSigningKeysResponse {
597+
Ok(MakeUserSigningKeysResponse {
580598
verifying_key: serialized_verifying_key_b64,
581-
signing_key: encrypted_signing_key,
599+
signing_key: encrypted_signing_key.encrypt_with_key(wrapping_key)?,
600+
signed_public_key_ownership_claim: STANDARD.encode(signed_public_key_ownership_claim.as_bytes()),
582601
})
583602
}
584603

@@ -867,14 +886,6 @@ mod tests {
867886
assert!(!private_key.is_empty());
868887
}
869888

870-
#[test]
871-
fn test_make_signing_keys() {
872-
let user_key = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
873-
let response = make_signing_keys(user_key.to_base64()).unwrap();
874-
assert!(!response.verifying_key.is_empty());
875-
let _: Vec<u8> = response.signing_key.decrypt_with_key(&user_key).unwrap();
876-
}
877-
878889
#[test]
879890
fn test_verify_asymmetric_keys_success() {
880891
let (user_key, key_pair) = setup_asymmetric_keys_test();

crates/bitwarden-core/src/mobile/crypto_client.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ use bitwarden_crypto::CryptoError;
33
use bitwarden_crypto::{EncString, UnsignedSharedKey};
44

55
use super::crypto::{
6-
derive_key_connector, make_key_pair, make_signing_keys, verify_asymmetric_keys,
6+
derive_key_connector, make_key_pair, make_user_signing_keys, verify_asymmetric_keys,
77
DeriveKeyConnectorError, DeriveKeyConnectorRequest, EnrollAdminPasswordResetError,
8-
MakeKeyPairResponse, MakeSigningKeysResponse, MobileCryptoError, VerifyAsymmetricKeysRequest,
8+
MakeKeyPairResponse, MakeUserSigningKeysResponse, MobileCryptoError, VerifyAsymmetricKeysRequest,
99
VerifyAsymmetricKeysResponse,
1010
};
1111
#[cfg(feature = "internal")]
@@ -105,9 +105,8 @@ impl CryptoClient {
105105

106106
pub fn make_signing_keys(
107107
&self,
108-
wrapping_key: String,
109-
) -> Result<MakeSigningKeysResponse, CryptoError> {
110-
make_signing_keys(wrapping_key)
108+
) -> Result<MakeUserSigningKeysResponse, CryptoError> {
109+
make_user_signing_keys(&self.client)
111110
}
112111
}
113112

crates/bitwarden-crypto/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ rayon = ">=1.8.1, <2.0"
4646
rsa = ">=0.9.2, <0.10"
4747
schemars = { workspace = true }
4848
serde = { workspace = true }
49+
serde_bytes = "0.11.17"
4950
sha1 = ">=0.10.5, <0.11"
5051
sha2 = ">=0.10.6, <0.11"
5152
subtle = ">=2.5.0, <3.0"

crates/bitwarden-crypto/src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ pub enum CryptoError {
6262

6363
#[error("Invalid namespace")]
6464
InvalidNamespace,
65+
66+
#[error("Invalid encoding")]
67+
InvalidEncoding,
6568
}
6669

6770
#[derive(Debug, Error)]

crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use std::pin::Pin;
22

3-
use rsa::{pkcs8::DecodePublicKey, RsaPrivateKey, RsaPublicKey};
3+
use rsa::{pkcs8::DecodePublicKey, traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey};
44

5-
use super::key_encryptable::CryptoKey;
5+
use super::{fingerprint::FingerprintableKey, key_encryptable::CryptoKey};
66
use crate::error::{CryptoError, Result};
77

88
/// Trait to allow both [`AsymmetricCryptoKey`] and [`AsymmetricPublicCryptoKey`] to be used to
@@ -34,6 +34,15 @@ impl AsymmetricEncryptable for AsymmetricPublicCryptoKey {
3434
}
3535
}
3636

37+
impl FingerprintableKey for AsymmetricPublicCryptoKey {
38+
fn fingerprint_parts(&self) -> Vec<Vec<u8>> {
39+
vec![
40+
self.key.n().to_bytes_le().as_slice().to_vec(),
41+
self.key.e().to_bytes_le().as_slice().to_vec(),
42+
]
43+
}
44+
}
45+
3746
/// An asymmetric encryption key. Contains both the public and private key. Can be used to both
3847
/// encrypt and decrypt [`UnsignedSharedKey`](crate::UnsignedSharedKey).
3948
#[derive(Clone)]

0 commit comments

Comments
 (0)