bitwarden_crypto/store/
key_rotation.rs

1use crate::{
2    CoseKeyBytes, CoseSerializable, CoseSign1Bytes, CryptoError, EncString, KeyEncryptable, KeyIds,
3    KeyStoreContext, SignedPublicKeyMessage, SpkiPublicKeyBytes, SymmetricCryptoKey,
4};
5
6/// Rotated set of account keys
7pub struct RotatedUserKeys {
8    /// The verifying key
9    pub verifying_key: CoseKeyBytes,
10    /// Signing key, encrypted with a symmetric key (user key, org key)
11    pub signing_key: EncString,
12    /// The user's public key, signed by the signing key
13    pub signed_public_key: CoseSign1Bytes,
14    /// The user's public key, without signature
15    pub public_key: SpkiPublicKeyBytes,
16    /// The user's private key, encrypted with the user key
17    pub private_key: EncString,
18}
19
20/// Re-encrypts the user's keys with the provided symmetric key for a v2 user.
21pub fn dangerous_get_v2_rotated_account_keys<Ids: KeyIds>(
22    new_user_key: &SymmetricCryptoKey,
23    current_user_private_key_id: Ids::Asymmetric,
24    current_user_signing_key_id: Ids::Signing,
25    ctx: &KeyStoreContext<Ids>,
26) -> Result<RotatedUserKeys, CryptoError> {
27    let current_private_key = ctx.get_asymmetric_key(current_user_private_key_id)?;
28    let current_signing_key = ctx.get_signing_key(current_user_signing_key_id)?;
29
30    let signed_public_key =
31        SignedPublicKeyMessage::from_public_key(&current_private_key.to_public_key())?
32            .sign(current_signing_key)?;
33    Ok(RotatedUserKeys {
34        verifying_key: current_signing_key.to_verifying_key().to_cose(),
35        signing_key: current_signing_key
36            .to_cose()
37            .encrypt_with_key(new_user_key)?,
38        signed_public_key: signed_public_key.into(),
39        public_key: current_private_key.to_public_key().to_der()?,
40        private_key: current_private_key
41            .to_der()?
42            .encrypt_with_key(new_user_key)?,
43    })
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49    use crate::{
50        traits::tests::{TestAsymmKey, TestIds, TestSigningKey, TestSymmKey},
51        AsymmetricCryptoKey, KeyDecryptable, KeyStore, Pkcs8PrivateKeyBytes,
52        PublicKeyEncryptionAlgorithm, SignedPublicKey, SigningKey,
53    };
54
55    #[test]
56    fn test_account_key_rotation() {
57        let store: KeyStore<TestIds> = KeyStore::default();
58        let mut ctx = store.context_mut();
59
60        // Generate a new user key
61        let new_user_key = SymmetricCryptoKey::make_xchacha20_poly1305_key();
62        let current_user_private_key_id = TestAsymmKey::A(0);
63        let current_user_signing_key_id = TestSigningKey::A(0);
64
65        // Make the keys
66        ctx.generate_symmetric_key(TestSymmKey::A(0)).unwrap();
67        ctx.make_signing_key(current_user_signing_key_id).unwrap();
68        #[allow(deprecated)]
69        ctx.set_asymmetric_key(
70            current_user_private_key_id,
71            AsymmetricCryptoKey::make(PublicKeyEncryptionAlgorithm::RsaOaepSha1),
72        )
73        .unwrap();
74
75        // Get the rotated account keys
76        let rotated_keys = dangerous_get_v2_rotated_account_keys(
77            &new_user_key,
78            current_user_private_key_id,
79            current_user_signing_key_id,
80            &ctx,
81        )
82        .unwrap();
83
84        // Public/Private key
85        assert_eq!(
86            rotated_keys.public_key,
87            ctx.get_asymmetric_key(current_user_private_key_id)
88                .unwrap()
89                .to_public_key()
90                .to_der()
91                .unwrap()
92        );
93        let decrypted_private_key: Vec<u8> = rotated_keys
94            .private_key
95            .decrypt_with_key(&new_user_key)
96            .unwrap();
97        let private_key =
98            AsymmetricCryptoKey::from_der(&Pkcs8PrivateKeyBytes::from(decrypted_private_key))
99                .unwrap();
100        assert_eq!(
101            private_key.to_der().unwrap(),
102            ctx.get_asymmetric_key(current_user_private_key_id)
103                .unwrap()
104                .to_der()
105                .unwrap()
106        );
107
108        // Signing Key
109        let decrypted_signing_key: Vec<u8> = rotated_keys
110            .signing_key
111            .decrypt_with_key(&new_user_key)
112            .unwrap();
113        let signing_key =
114            SigningKey::from_cose(&CoseKeyBytes::from(decrypted_signing_key)).unwrap();
115        assert_eq!(
116            signing_key.to_cose(),
117            ctx.get_signing_key(current_user_signing_key_id)
118                .unwrap()
119                .to_cose(),
120        );
121
122        // Signed Public Key
123        let signed_public_key = SignedPublicKey::try_from(rotated_keys.signed_public_key).unwrap();
124        let unwrapped_key = signed_public_key
125            .verify_and_unwrap(
126                &ctx.get_signing_key(current_user_signing_key_id)
127                    .unwrap()
128                    .to_verifying_key(),
129            )
130            .unwrap();
131        assert_eq!(
132            unwrapped_key.to_der().unwrap(),
133            ctx.get_asymmetric_key(current_user_private_key_id)
134                .unwrap()
135                .to_public_key()
136                .to_der()
137                .unwrap()
138        );
139    }
140}