bitwarden_crypto/store/
key_rotation.rs1use crate::{
2 CoseKeyBytes, CoseSerializable, CryptoError, EncString, KeyEncryptable, KeyIds,
3 KeyStoreContext, SignedPublicKey, SignedPublicKeyMessage, SpkiPublicKeyBytes,
4 SymmetricCryptoKey,
5};
6
7pub struct RotatedUserKeys {
9 pub user_key: SymmetricCryptoKey,
11 pub verifying_key: CoseKeyBytes,
13 pub signing_key: EncString,
15 pub signed_public_key: SignedPublicKey,
17 pub public_key: SpkiPublicKeyBytes,
19 pub private_key: EncString,
21}
22
23pub fn dangerous_get_v2_rotated_account_keys<Ids: KeyIds>(
25 current_user_private_key_id: Ids::Asymmetric,
26 current_user_signing_key_id: Ids::Signing,
27 ctx: &KeyStoreContext<Ids>,
28) -> Result<RotatedUserKeys, CryptoError> {
29 let user_key = SymmetricCryptoKey::make_xchacha20_poly1305_key();
30
31 let current_private_key = ctx.get_asymmetric_key(current_user_private_key_id)?;
32 let current_signing_key = ctx.get_signing_key(current_user_signing_key_id)?;
33
34 let current_public_key = ¤t_private_key.to_public_key();
35 let signed_public_key =
36 SignedPublicKeyMessage::from_public_key(current_public_key)?.sign(current_signing_key)?;
37
38 Ok(RotatedUserKeys {
39 verifying_key: current_signing_key.to_verifying_key().to_cose(),
40 signing_key: current_signing_key.to_cose().encrypt_with_key(&user_key)?,
41 signed_public_key,
42 public_key: current_public_key.to_der()?,
43 private_key: current_private_key.to_der()?.encrypt_with_key(&user_key)?,
44 user_key,
45 })
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51 use crate::{
52 traits::tests::{TestAsymmKey, TestIds, TestSigningKey, TestSymmKey},
53 AsymmetricCryptoKey, KeyDecryptable, KeyStore, Pkcs8PrivateKeyBytes,
54 PublicKeyEncryptionAlgorithm, SigningKey,
55 };
56
57 #[test]
58 fn test_account_key_rotation() {
59 let store: KeyStore<TestIds> = KeyStore::default();
60 let mut ctx = store.context_mut();
61
62 let current_user_private_key_id = TestAsymmKey::A(0);
64 let current_user_signing_key_id = TestSigningKey::A(0);
65
66 ctx.generate_symmetric_key(TestSymmKey::A(0)).unwrap();
68 ctx.make_signing_key(current_user_signing_key_id).unwrap();
69 #[allow(deprecated)]
70 ctx.set_asymmetric_key(
71 current_user_private_key_id,
72 AsymmetricCryptoKey::make(PublicKeyEncryptionAlgorithm::RsaOaepSha1),
73 )
74 .unwrap();
75
76 let rotated_keys = dangerous_get_v2_rotated_account_keys(
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(&rotated_keys.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(&rotated_keys.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 = rotated_keys.signed_public_key;
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}