bitwarden_crypto/keys/
rotateable_key_set.rs1use serde::{Deserialize, Serialize};
2
3use crate::{
4 AsymmetricCryptoKey, AsymmetricPublicCryptoKey, CryptoError, EncString, KeyDecryptable,
5 KeyEncryptable, KeyIds, KeyStoreContext, Pkcs8PrivateKeyBytes, SpkiPublicKeyBytes,
6 SymmetricCryptoKey, UnsignedSharedKey,
7};
8
9#[derive(Serialize, Deserialize, Debug)]
20#[serde(rename_all = "camelCase", deny_unknown_fields)]
21#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
22#[cfg_attr(
23 feature = "wasm",
24 derive(tsify::Tsify),
25 tsify(into_wasm_abi, from_wasm_abi)
26)]
27pub struct RotateableKeySet {
28 encapsulated_downstream_key: UnsignedSharedKey,
30 encrypted_encapsulation_key: EncString,
32 encrypted_decapsulation_key: EncString,
34}
35
36impl RotateableKeySet {
37 pub fn new<Ids: KeyIds>(
40 ctx: &KeyStoreContext<Ids>,
41 upstream_key: &SymmetricCryptoKey,
42 downstream_key_id: Ids::Symmetric,
43 ) -> Result<Self, CryptoError> {
44 let key_pair = AsymmetricCryptoKey::make(crate::PublicKeyEncryptionAlgorithm::RsaOaepSha1);
45
46 #[allow(deprecated)]
50 let downstream_key = ctx.dangerous_get_symmetric_key(downstream_key_id)?;
51 let encapsulated_downstream_key =
53 UnsignedSharedKey::encapsulate_key_unsigned(downstream_key, &key_pair.to_public_key())?;
54
55 let encrypted_decapsulation_key = key_pair.to_der()?.encrypt_with_key(upstream_key)?;
57
58 let encrypted_encapsulation_key = key_pair
64 .to_public_key()
65 .to_der()?
66 .encrypt_with_key(downstream_key)?;
67
68 Ok(RotateableKeySet {
69 encapsulated_downstream_key,
70 encrypted_encapsulation_key,
71 encrypted_decapsulation_key,
72 })
73 }
74
75 #[allow(dead_code)]
78 fn unlock<Ids: KeyIds>(
79 &self,
80 ctx: &mut KeyStoreContext<Ids>,
81 upstream_key: &SymmetricCryptoKey,
82 downstream_key_id: Ids::Symmetric,
83 ) -> Result<(), CryptoError> {
84 let priv_key_bytes: Vec<u8> = self
85 .encrypted_decapsulation_key
86 .decrypt_with_key(upstream_key)?;
87 let decapsulation_key =
88 AsymmetricCryptoKey::from_der(&Pkcs8PrivateKeyBytes::from(priv_key_bytes))?;
89 let downstream_key = self
90 .encapsulated_downstream_key
91 .decapsulate_key_unsigned(&decapsulation_key)?;
92 #[allow(deprecated)]
93 ctx.set_symmetric_key(downstream_key_id, downstream_key)?;
94 Ok(())
95 }
96}
97
98#[allow(dead_code)]
99fn rotate_key_set<Ids: KeyIds>(
100 ctx: &KeyStoreContext<Ids>,
101 key_set: RotateableKeySet,
102 old_downstream_key_id: Ids::Symmetric,
103 new_downstream_key_id: Ids::Symmetric,
104) -> Result<RotateableKeySet, CryptoError> {
105 let pub_key_bytes = ctx.decrypt_data_with_symmetric_key(
106 old_downstream_key_id,
107 &key_set.encrypted_encapsulation_key,
108 )?;
109 let pub_key = SpkiPublicKeyBytes::from(pub_key_bytes);
110 let encapsulation_key = AsymmetricPublicCryptoKey::from_der(&pub_key)?;
111 #[allow(deprecated)]
114 let new_downstream_key = ctx.dangerous_get_symmetric_key(new_downstream_key_id)?;
115 let new_encapsulated_key =
116 UnsignedSharedKey::encapsulate_key_unsigned(new_downstream_key, &encapsulation_key)?;
117 let new_encrypted_encapsulation_key = pub_key.encrypt_with_key(new_downstream_key)?;
118 Ok(RotateableKeySet {
119 encapsulated_downstream_key: new_encapsulated_key,
120 encrypted_encapsulation_key: new_encrypted_encapsulation_key,
121 encrypted_decapsulation_key: key_set.encrypted_decapsulation_key,
122 })
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128 use crate::{
129 KeyStore,
130 traits::tests::{TestIds, TestSymmKey},
131 };
132
133 #[test]
134 fn test_rotateable_key_set_can_unlock() {
135 let upstream_key = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
137 let store: KeyStore<TestIds> = KeyStore::default();
139 let mut ctx = store.context_mut();
140 let original_downstream_key_id = ctx.generate_symmetric_key();
141
142 let key_set =
144 RotateableKeySet::new(&ctx, &upstream_key, original_downstream_key_id).unwrap();
145
146 let unwrapped_downstream_key_id = TestSymmKey::A(1);
148 key_set
149 .unlock(&mut ctx, &upstream_key, unwrapped_downstream_key_id)
150 .unwrap();
151
152 #[allow(deprecated)]
153 let original_downstream_key = ctx
154 .dangerous_get_symmetric_key(original_downstream_key_id)
155 .unwrap();
156 #[allow(deprecated)]
157 let unwrapped_downstream_key = ctx
158 .dangerous_get_symmetric_key(unwrapped_downstream_key_id)
159 .unwrap();
160 assert_eq!(original_downstream_key, unwrapped_downstream_key);
161 }
162
163 #[test]
164 fn test_rotateable_key_set_rotation() {
165 let upstream_key = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
167 let store: KeyStore<TestIds> = KeyStore::default();
169 let mut ctx = store.context_mut();
170 let original_downstream_key_id = ctx.generate_symmetric_key();
171
172 let key_set =
174 RotateableKeySet::new(&ctx, &upstream_key, original_downstream_key_id).unwrap();
175
176 let new_downstream_key_id = ctx.generate_symmetric_key();
178 let new_key_set = rotate_key_set(
179 &ctx,
180 key_set,
181 original_downstream_key_id,
182 new_downstream_key_id,
183 )
184 .unwrap();
185
186 let unwrapped_downstream_key_id = TestSymmKey::A(2_2);
189 new_key_set
190 .unlock(&mut ctx, &upstream_key, unwrapped_downstream_key_id)
191 .unwrap();
192 #[allow(deprecated)]
193 let new_downstream_key = ctx
194 .dangerous_get_symmetric_key(new_downstream_key_id)
195 .unwrap();
196 #[allow(deprecated)]
197 let unwrapped_downstream_key = ctx
198 .dangerous_get_symmetric_key(unwrapped_downstream_key_id)
199 .unwrap();
200 assert_eq!(new_downstream_key, unwrapped_downstream_key);
201 }
202}