bitwarden_organization_crypto/
organization_shared_key.rs1use std::str::FromStr;
21
22use bitwarden_crypto::{CryptoError, KeySlotIds, KeyStoreContext, PublicKey, UnsignedSharedKey};
23use thiserror::Error;
24
25use crate::trust_proof::{TrustProof, TrustProofError};
26
27#[derive(Debug, Error)]
29pub enum OrganizationSharedKeyParseError {
30 #[error("The organization shared key is malformed")]
32 Malformed,
33}
34
35#[derive(Debug, Error, PartialEq)]
37pub enum OrganizationSharedKeyMakeError {
38 #[error("The private key of the trust proof is not in the key store context")]
42 PrivateKeyNotInContext,
43 #[error("The organization key could not be encapsulated to the member's public key")]
45 EncapsulationFailed,
46 #[error("The member's public key could not be trusted")]
48 UntrustedPublicKey,
49}
50
51impl From<TrustProofError> for OrganizationSharedKeyMakeError {
52 fn from(error: TrustProofError) -> Self {
53 match error {
54 TrustProofError::PrivateKeyNotInContext => Self::PrivateKeyNotInContext,
55 TrustProofError::UntrustedPublicKey => Self::UntrustedPublicKey,
56 }
57 }
58}
59
60#[derive(Debug, Error)]
62pub enum OrganizationSharedKeyMakeUntrustedError {
63 #[error("The organization key could not be encapsulated to the member's public key")]
65 EncapsulationFailed,
66}
67
68#[derive(Debug, Error)]
70pub enum OrganizationSharedKeyUnsealError {
71 #[error("The organization shared key could not be decapsulated with the member's private key")]
74 DecapsulationFailed,
75 #[error("The member's private key does not exist in the key store context")]
77 PrivateKeyNotInContext,
78}
79
80pub enum OrganizationSharedKey {
82 V1(UnsignedSharedKey),
88}
89
90impl OrganizationSharedKey {
91 pub fn make<Ids: KeySlotIds>(
94 organization_key: Ids::Symmetric,
95 member_public_key: &PublicKey,
96 trust_proof: &TrustProof<Ids>,
97 ctx: &KeyStoreContext<Ids>,
98 ) -> Result<Self, OrganizationSharedKeyMakeError> {
99 trust_proof.verify(member_public_key, ctx)?;
100
101 let encapsulated_key =
102 UnsignedSharedKey::encapsulate(organization_key, member_public_key, ctx)
103 .map_err(|_| OrganizationSharedKeyMakeError::EncapsulationFailed)?;
104
105 Ok(Self::V1(encapsulated_key))
106 }
107
108 pub fn make_untrusted<Ids: KeySlotIds>(
114 organization_key: Ids::Symmetric,
115 member_public_key: &PublicKey,
116 ctx: &KeyStoreContext<Ids>,
117 ) -> Result<Self, OrganizationSharedKeyMakeUntrustedError> {
118 let encapsulated_key =
119 UnsignedSharedKey::encapsulate(organization_key, member_public_key, ctx)
120 .map_err(|_| OrganizationSharedKeyMakeUntrustedError::EncapsulationFailed)?;
121
122 Ok(Self::V1(encapsulated_key))
123 }
124
125 pub fn unseal<Ids: KeySlotIds>(
128 &self,
129 own_private_key: Ids::Private,
130 ctx: &mut KeyStoreContext<Ids>,
131 ) -> Result<Ids::Symmetric, OrganizationSharedKeyUnsealError> {
132 let Self::V1(encapsulated_key) = self;
133
134 encapsulated_key
135 .decapsulate(own_private_key, ctx)
136 .map_err(|error| match error {
137 CryptoError::MissingKeyId(_) => {
138 OrganizationSharedKeyUnsealError::PrivateKeyNotInContext
139 }
140 _ => OrganizationSharedKeyUnsealError::DecapsulationFailed,
141 })
142 }
143}
144
145impl FromStr for OrganizationSharedKey {
146 type Err = OrganizationSharedKeyParseError;
147
148 fn from_str(value: &str) -> Result<Self, Self::Err> {
149 value
150 .parse()
151 .map(Self::V1)
152 .map_err(|_| OrganizationSharedKeyParseError::Malformed)
153 }
154}
155
156impl From<&OrganizationSharedKey> for String {
157 fn from(value: &OrganizationSharedKey) -> Self {
158 let OrganizationSharedKey::V1(encapsulated_key) = value;
159
160 encapsulated_key.to_string()
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use bitwarden_crypto::{
167 EncString, KeyStore, PublicKeyEncryptionAlgorithm, SymmetricCryptoKey,
168 SymmetricKeyAlgorithm::Aes256CbcHmac,
169 };
170
171 use super::*;
172 use crate::test_support::{
173 TEST_VECTOR_V1_MEMBER_USER_KEY, TEST_VECTOR_V1_ORG_KEY, TestIds, TestPrivateKey,
174 add_symmetric_key,
175 };
176
177 const TEST_VECTOR_MEMBER_WRAPPED_PRIVATE_KEY: &str = "2.Fk2Qw03uGzzqZ64D3yKYEw==|VFfJlJ9/bMF9usPNP1Czzpc57hS/mVublKvpUsgxbWohkunlBxz1lVwOknAxtVprt9a4W9rKFjT+xDY1ImpQijt+MgGRAsXqURlGWY2W7CUs7tN9f5M598R5SXopVXhIYv8uVvMPv7zMEa6sl2p0XvMNkjn/02h2UHm1R9Wpnvb8MeGkXVBwGVfP53FaBR8iHsDQu7pR6MP8nYqRUg9+ORbqId453BpUycJt6TD59JEEUOG90T57UQRWIBOPmjtN8eF/EaVMK9fzYwCbKqa187x7B7BCuqKXSzjfFdSt+xNLmZMPRRB1DZB9NvabsQF87175wsnHOFqrcTXhAORvVpse1EpVSWR+u8K3p9chqjzlGCthb72OEET1hV1MSYWv1osDnqhNl981zz9oDToRk3ZEhktZyA7Uox9g2Abuf3xVpkCCO/bb3rcpTmmhk5BP3npfluUyEbpCRNcyi1kYpAF6vtY+UYaAv4XB25Dcp5td/c2yrlIUJeM17GFCm/TQGU7kMo7S3CFcM9nmFFG60aM8eQiwF1HQZV9cMPLNiNFwDFdmUcv+m/eZ/4ZZM8WJv4raevjoFUPgb8saqOSLKYGisjGoyJ7Z9T1eZisyALEfgIVbsJrINWjjW1pVxDNMiatn8ZDA4S90rUYl2eejNie+2e+41KAYl3JSZlkqr636A2gkLzToufXpX+n655hz9yNMhv5TnfenzTaMDg1XCETqiixKQBxV8m4VsJbGBIzNVzghoJUuf8xZ90FmhWeH6z8It6f/uVwlnBLG1m690AWJXVtMmEejrwJAcmJKYntFHtZI54SUJfXyW33vaQ7tQbQ699BqU9nGQcTnDbTmdA2iyDLuJ2n06B8PYK4odSfCQJrOWHsdZpRd7P1lDp7ksxejqafJJfXsD0BvoUbw8CGe5eGotGlyv85poSduLeURqyai6y4eW0UClXJJiaUXMlXNbzrTCSeWb6aWJTxQfW/fQ0QNjIqDOB5B4xByh/mzESOiyTEnNV61Ln2HOGlpfkhARLpgGdotYvZtFQRrApqvWwRtIerP2gZfpubAN29J5lW2DHAkd6fvEFokjQ4wofqymwNxVVd5WBjueQ1RbFdo1LMIiKApEMTpQ5E4eipmtDc6foFJo26FIuXvHduKOBZm0rYfd8Iw5EgQMXyKaUE1KxU9HHdR4BFzT9JEacNPkdF96DSbV/l6lVADDc0xfRe7AAXAmPxRlHDMw4HGlTbOXlhNZQtkX7mUMyVheRudwsmOvH/BvbJajn1E+xYy4r1GdTbvJRvzAjhK1Xdniq4bAD1brVPdnkk7yhTEHT1GnGEMn3kPtBhuBIKhNSwYZdG88NcwDzwDhrssU77jSIB72RjtOpO4nlMnqmjGpwFakJmLgaonYgIArIBqaJQ7Kt/JfIUGiARkO6IVF3WF8cL5/GUI7KpyZATbBIX4bfuJXBlmEPBneKduLCt+svBW1XVctOwArsYfypCsQojmMRNQkHC8EgCumQxc8Wutl7jXh8OwIXDHZjQ6UJw8XK2vmuS6oBRQg9SwzeQlIbsz1sQRpuAx/IY7TwTZOOi0bp5KdnZzSCx6SbUP7ZDdbGQb4ZH3GCXkOgvK0MsH5GEUjnEa8afSJGh/hEulDEsFE5g=|HA79iaX6imq4ipukZLyw6Nd1PhIT7/pCInWPmgr4YKA=";
179 const TEST_VECTOR_ORGANIZATION_SHARED_KEY: &str = "4.HtHhO4krfvZnoJRJFkXpf9BLLyrOO0ovfxTsmt0GNKkT7clpMVNR0W7B8VOEiautOVW6zAMDVzO2ZlFhrVR6kt5ATXL1KCqjipHBI2u/6Ww3Dh2jlUZuRTFcbZ4bYN3sOVQPkdwFaM1H++SzauUWP7pmzSc+u4sQIUKAB/KJ5OSkKn3tFYZuwUycB+wgcA4Ew8qU3c46lCbKqXY8dI3Z1ttvC5vO81cM4pryNGoHnaI7ONeEIuGIA5py8/WP58R6mQhyQWwC2uq77caFRX8GLGJd3c3yYfH1em5NvF8BZzZ1TZ/Nd+6fS4T8awUjs4IxkxJw6eU6Wn8+y4tUM9gAxw==";
181
182 fn load_member_private_key(ctx: &mut KeyStoreContext<'_, TestIds>) -> TestPrivateKey {
184 let member_user_key = add_symmetric_key(TEST_VECTOR_V1_MEMBER_USER_KEY, ctx);
185 let wrapped_private_key: EncString =
186 TEST_VECTOR_MEMBER_WRAPPED_PRIVATE_KEY.parse().unwrap();
187 ctx.unwrap_private_key(member_user_key, &wrapped_private_key)
188 .unwrap()
189 }
190
191 #[test]
192 fn test_organization_shared_key_unseals_to_the_organization_key() {
193 let key_store = KeyStore::<TestIds>::default();
194 let mut ctx = key_store.context_mut();
195 let organization_key = ctx.make_symmetric_key(Aes256CbcHmac);
196 let own_private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
197 let own_public_key = ctx.get_public_key(own_private_key).unwrap();
198
199 let shared_key = OrganizationSharedKey::make(
200 organization_key,
201 &own_public_key,
202 &TrustProof::TestStub(Ok(())),
203 &ctx,
204 )
205 .unwrap();
206 let unsealed = shared_key.unseal(own_private_key, &mut ctx).unwrap();
207
208 ctx.assert_symmetric_keys_equal(organization_key, unsealed);
209 }
210
211 #[test]
212 fn test_untrusted_organization_shared_key_unseals_to_the_organization_key() {
213 let key_store = KeyStore::<TestIds>::default();
214 let mut ctx = key_store.context_mut();
215 let organization_key = ctx.make_symmetric_key(Aes256CbcHmac);
216 let member_private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
217 let member_public_key = ctx.get_public_key(member_private_key).unwrap();
218
219 let shared_key =
220 OrganizationSharedKey::make_untrusted(organization_key, &member_public_key, &ctx)
221 .unwrap();
222 let unsealed = shared_key.unseal(member_private_key, &mut ctx).unwrap();
223
224 ctx.assert_symmetric_keys_equal(organization_key, unsealed);
225 }
226
227 #[test]
228 fn test_organization_shared_key_survives_a_string_round_trip() {
229 let key_store = KeyStore::<TestIds>::default();
230 let mut ctx = key_store.context_mut();
231 let organization_key = ctx.make_symmetric_key(Aes256CbcHmac);
232 let own_private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
233 let own_public_key = ctx.get_public_key(own_private_key).unwrap();
234 let shared_key = OrganizationSharedKey::make(
235 organization_key,
236 &own_public_key,
237 &TrustProof::TestStub(Ok(())),
238 &ctx,
239 )
240 .unwrap();
241
242 let parsed: OrganizationSharedKey = String::from(&shared_key).parse().unwrap();
243
244 let unsealed = parsed.unseal(own_private_key, &mut ctx).unwrap();
245 ctx.assert_symmetric_keys_equal(organization_key, unsealed);
246 }
247
248 #[test]
249 fn test_parsing_a_key_in_no_known_format_fails() {
250 let result: Result<OrganizationSharedKey, _> = "not an encapsulated key".parse();
251
252 assert!(matches!(
253 result,
254 Err(OrganizationSharedKeyParseError::Malformed)
255 ));
256 }
257
258 #[test]
259 fn test_making_with_a_failed_trust_proof_fails_with_the_matching_error() {
260 let key_store = KeyStore::<TestIds>::default();
261 let mut ctx = key_store.context_mut();
262 let organization_key = ctx.make_symmetric_key(Aes256CbcHmac);
263 let member_private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
264 let member_public_key = ctx.get_public_key(member_private_key).unwrap();
265
266 for (trust_proof_error, expected_error) in [
267 (
268 TrustProofError::PrivateKeyNotInContext,
269 OrganizationSharedKeyMakeError::PrivateKeyNotInContext,
270 ),
271 (
272 TrustProofError::UntrustedPublicKey,
273 OrganizationSharedKeyMakeError::UntrustedPublicKey,
274 ),
275 ] {
276 let result = OrganizationSharedKey::make(
277 organization_key,
278 &member_public_key,
279 &TrustProof::TestStub(Err(trust_proof_error)),
280 &ctx,
281 );
282
283 assert_eq!(result.err(), Some(expected_error));
284 }
285 }
286
287 #[test]
288 fn test_making_with_a_missing_organization_key_fails() {
289 let key_store = KeyStore::<TestIds>::default();
290 let mut ctx = key_store.context_mut();
291 let own_private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
292 let own_public_key = ctx.get_public_key(own_private_key).unwrap();
293 let other_key_store = KeyStore::<TestIds>::default();
294 let mut other_ctx = other_key_store.context_mut();
295 let missing_organization_key = other_ctx.make_symmetric_key(Aes256CbcHmac);
296
297 let result = OrganizationSharedKey::make(
298 missing_organization_key,
299 &own_public_key,
300 &TrustProof::TestStub(Ok(())),
301 &ctx,
302 );
303
304 assert!(matches!(
305 result,
306 Err(OrganizationSharedKeyMakeError::EncapsulationFailed)
307 ));
308 }
309
310 #[test]
311 fn test_making_untrusted_with_a_missing_organization_key_fails() {
312 let key_store = KeyStore::<TestIds>::default();
313 let mut ctx = key_store.context_mut();
314 let member_private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
315 let member_public_key = ctx.get_public_key(member_private_key).unwrap();
316 let other_key_store = KeyStore::<TestIds>::default();
317 let mut other_ctx = other_key_store.context_mut();
318 let missing_organization_key = other_ctx.make_symmetric_key(Aes256CbcHmac);
319
320 let result = OrganizationSharedKey::make_untrusted(
321 missing_organization_key,
322 &member_public_key,
323 &ctx,
324 );
325
326 assert!(matches!(
327 result,
328 Err(OrganizationSharedKeyMakeUntrustedError::EncapsulationFailed)
329 ));
330 }
331
332 #[test]
333 fn test_unsealing_with_another_private_key_fails() {
334 let key_store = KeyStore::<TestIds>::default();
335 let mut ctx = key_store.context_mut();
336 let organization_key = ctx.make_symmetric_key(Aes256CbcHmac);
337 let member_private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
338 let member_public_key = ctx.get_public_key(member_private_key).unwrap();
339 let other_private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
340 let shared_key =
341 OrganizationSharedKey::make_untrusted(organization_key, &member_public_key, &ctx)
342 .unwrap();
343
344 let result = shared_key.unseal(other_private_key, &mut ctx);
345
346 assert!(matches!(
347 result,
348 Err(OrganizationSharedKeyUnsealError::DecapsulationFailed)
349 ));
350 }
351
352 #[test]
353 fn test_unsealing_with_a_private_key_from_another_context_fails() {
354 let key_store = KeyStore::<TestIds>::default();
355 let mut ctx = key_store.context_mut();
356 let organization_key = ctx.make_symmetric_key(Aes256CbcHmac);
357 let member_private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
358 let member_public_key = ctx.get_public_key(member_private_key).unwrap();
359 let shared_key =
360 OrganizationSharedKey::make_untrusted(organization_key, &member_public_key, &ctx)
361 .unwrap();
362 let other_key_store = KeyStore::<TestIds>::default();
363 let mut other_ctx = other_key_store.context_mut();
364
365 let result = shared_key.unseal(member_private_key, &mut other_ctx);
366
367 assert!(matches!(
368 result,
369 Err(OrganizationSharedKeyUnsealError::PrivateKeyNotInContext)
370 ));
371 }
372
373 #[test]
374 #[ignore = "Manual test to generate test vectors"]
375 fn generate_test_vectors() {
376 let key_store = KeyStore::<TestIds>::default();
377 let mut ctx = key_store.context_mut();
378 let member_user_key = SymmetricCryptoKey::make(Aes256CbcHmac);
379 let member_user_key_b64 = member_user_key.to_base64();
380 let member_user_key = ctx.add_local_symmetric_key(member_user_key);
381 let member_private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
382 let wrapped_member_private_key = ctx
383 .wrap_private_key(member_user_key, member_private_key)
384 .unwrap();
385 let member_public_key = ctx.get_public_key(member_private_key).unwrap();
386 let organization_key = add_symmetric_key(TEST_VECTOR_V1_ORG_KEY, &mut ctx);
387
388 let shared_key =
389 OrganizationSharedKey::make_untrusted(organization_key, &member_public_key, &ctx)
390 .unwrap();
391
392 println!("const TEST_VECTOR_V1_MEMBER_USER_KEY: &str = \"{member_user_key_b64}\";");
393 println!(
394 "const TEST_VECTOR_MEMBER_WRAPPED_PRIVATE_KEY: &str = \"{}\";",
395 wrapped_member_private_key.to_string()
396 );
397 println!(
398 "const TEST_VECTOR_ORGANIZATION_SHARED_KEY: &str = \"{}\";",
399 String::from(&shared_key)
400 );
401 }
402
403 #[test]
404 fn test_organization_shared_key_test_vector() {
405 let key_store = KeyStore::<TestIds>::default();
406 let mut ctx = key_store.context_mut();
407 let member_private_key = load_member_private_key(&mut ctx);
408 let organization_key = add_symmetric_key(TEST_VECTOR_V1_ORG_KEY, &mut ctx);
409
410 let shared_key: OrganizationSharedKey =
411 TEST_VECTOR_ORGANIZATION_SHARED_KEY.parse().unwrap();
412 let unsealed = shared_key.unseal(member_private_key, &mut ctx).unwrap();
413
414 ctx.assert_symmetric_keys_equal(organization_key, unsealed);
415 }
416}