bitwarden_crypto/store/
key_rotation.rs1use crate::{
2 CoseKeyBytes, CoseSerializable, CoseSign1Bytes, CryptoError, EncString, KeyEncryptable, KeyIds,
3 KeyStoreContext, SignedPublicKeyMessage, SpkiPublicKeyBytes, SymmetricCryptoKey,
4};
5
6pub struct RotatedUserKeys {
8 pub verifying_key: CoseKeyBytes,
10 pub signing_key: EncString,
12 pub signed_public_key: CoseSign1Bytes,
14 pub public_key: SpkiPublicKeyBytes,
16 pub private_key: EncString,
18}
19
20pub 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(¤t_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 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 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 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 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 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 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}