Skip to main content

bitwarden_organization_crypto/
organization_private_key.rs

1//! The organization's key pair, held in a key store context.
2//!
3//! The organization's private key is stored wrapped with the organization key.
4//! [`OrganizationPrivateKey`] unwraps it into the key store context, to encapsulate keys to the
5//! organization and to open keys that were encapsulated to it.
6
7use bitwarden_crypto::{
8    CryptoError, EncString, KeySlotIds, KeyStoreContext, PublicKey, UnsignedSharedKey,
9};
10use thiserror::Error;
11
12use crate::trust_proof::{CryptographicProof, TrustProof};
13
14/// Errors that can occur when unwrapping the organization's private key.
15#[derive(Debug, Error)]
16pub enum OrganizationPrivateKeyUnwrapError {
17    /// The wrapped private key belongs to a different organization key, or the stored value is
18    /// corrupt.
19    #[error("The organization's private key could not be unwrapped with the organization key")]
20    UnwrapFailed,
21}
22
23/// Errors that can occur when deriving the organization's public key.
24#[derive(Debug, Error)]
25pub enum OrganizationPublicKeyError {
26    /// The private key of [`OrganizationPrivateKey`] is not in the key store context.
27    #[error("The organization's private key does not exist in the key store context")]
28    MissingPrivateKey,
29}
30
31/// The organization's private key, unwrapped into the key store context.
32pub struct OrganizationPrivateKey<Ids: KeySlotIds> {
33    private_key_id: Ids::Private,
34}
35
36impl<Ids: KeySlotIds> OrganizationPrivateKey<Ids> {
37    /// Unwraps the organization's private key with the organization key.
38    ///
39    /// `wrapped_organization_private_key` is the wrapped private key shared with the server.
40    pub fn unwrap_with_organization_key(
41        organization_key: Ids::Symmetric,
42        wrapped_organization_private_key: &EncString,
43        ctx: &mut KeyStoreContext<Ids>,
44    ) -> Result<Self, OrganizationPrivateKeyUnwrapError> {
45        let private_key_id = ctx
46            .unwrap_private_key(organization_key, wrapped_organization_private_key)
47            .map_err(|_| OrganizationPrivateKeyUnwrapError::UnwrapFailed)?;
48
49        Ok(Self { private_key_id })
50    }
51
52    /// The organization's private key under `private_key_id`, or `None` when the key store context
53    /// does not hold it.
54    pub fn from_context(private_key_id: Ids::Private, ctx: &KeyStoreContext<Ids>) -> Option<Self> {
55        ctx.has_private_key(private_key_id)
56            .then_some(Self { private_key_id })
57    }
58
59    /// The organization's public key.
60    pub fn public_key(
61        &self,
62        ctx: &KeyStoreContext<Ids>,
63    ) -> Result<PublicKey, OrganizationPublicKeyError> {
64        ctx.get_public_key(self.private_key_id)
65            .map_err(|_| OrganizationPublicKeyError::MissingPrivateKey)
66    }
67
68    /// The proof that the organization's public key is trusted: the caller holds the private key.
69    pub fn trust_proof(&self) -> TrustProof<Ids> {
70        TrustProof::CryptographicallyTrusted(CryptographicProof::OwnPrivateKey(self.private_key_id))
71    }
72
73    /// Opens a key that was encapsulated to the organization, returning the id it was added to the
74    /// context under.
75    pub(crate) fn decapsulate_key(
76        &self,
77        encapsulated_key: &UnsignedSharedKey,
78        ctx: &mut KeyStoreContext<Ids>,
79    ) -> Result<Ids::Symmetric, CryptoError> {
80        encapsulated_key.decapsulate(self.private_key_id, ctx)
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use bitwarden_crypto::{
87        KeyStore, PublicKeyEncryptionAlgorithm, SpkiPublicKeyBytes,
88        SymmetricKeyAlgorithm::Aes256CbcHmac,
89    };
90    use bitwarden_encoding::B64;
91
92    use super::*;
93    use crate::test_support::{
94        TEST_VECTOR_V1_WRAPPED_PRIVATE_KEY, TestIds, TestPrivateKey, TestSymmKey,
95        load_v1_test_vectors, unwrap_v1_organization_private_key,
96    };
97
98    /// The public key of the pinned organization private key, as base64 SPKI.
99    const TEST_VECTOR_ORGANIZATION_PUBLIC_KEY: &str = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoNoakr8gAXszoM1FQ1kz3rCXbd/ROcb8JQveiAmYuSCvA3Wx8zLqmYo0Jq6C3gPGxCHLVI4pYA6Pt0M59AlvK4jOiGwSPh1Wt6M+HlGUuBkW3Q3fJ0OwwKDT0WwzuZlmateoxg1Jq91jaVevc7bfsJMD8YFIY/c5nWTWIs5HG/v8ZwQMrdqAO/f7O3SJ4SvetkZqQynbTAL7AfPSVhIQt85mxyvkcAsqnwnmqjmlZsSXas2Ghp9v/5Y1eyLEgNUHyFghrNqbm99LTWnwgUVPhx8ALPkI2H8r4MMEPidZ1vEFURpL513nvFYTqfe2Vzg6JUXNu0w8ikFZh/6CNeXLTQIDAQAB";
100
101    #[test]
102    fn test_unwrapping_with_the_wrong_organization_key_fails() {
103        let key_store = KeyStore::<TestIds>::default();
104        let mut ctx = key_store.context_mut();
105        let wrapped_private_key = load_v1_test_vectors(&mut ctx);
106        let other_organization_key = ctx.make_symmetric_key(Aes256CbcHmac);
107
108        let result = OrganizationPrivateKey::unwrap_with_organization_key(
109            other_organization_key,
110            &wrapped_private_key,
111            &mut ctx,
112        );
113
114        assert!(matches!(
115            result,
116            Err(OrganizationPrivateKeyUnwrapError::UnwrapFailed)
117        ));
118    }
119
120    #[test]
121    fn test_public_key_in_another_context_fails() {
122        let key_store = KeyStore::<TestIds>::default();
123        let mut ctx = key_store.context_mut();
124        let wrapped_private_key = load_v1_test_vectors(&mut ctx);
125        let organization_private_key = OrganizationPrivateKey::unwrap_with_organization_key(
126            TestSymmKey::Organization,
127            &wrapped_private_key,
128            &mut ctx,
129        )
130        .unwrap();
131        let other_key_store = KeyStore::<TestIds>::default();
132        let other_ctx = other_key_store.context();
133
134        let result = organization_private_key.public_key(&other_ctx);
135
136        assert!(matches!(
137            result,
138            Err(OrganizationPublicKeyError::MissingPrivateKey)
139        ));
140    }
141
142    #[test]
143    fn test_unwrapping_a_wrapped_value_that_is_not_a_private_key_fails() {
144        let key_store = KeyStore::<TestIds>::default();
145        let mut ctx = key_store.context_mut();
146        load_v1_test_vectors(&mut ctx);
147        let symmetric_key = ctx.make_symmetric_key(Aes256CbcHmac);
148        let wrapped_symmetric_key = ctx
149            .wrap_symmetric_key(TestSymmKey::Organization, symmetric_key)
150            .unwrap();
151
152        let result = OrganizationPrivateKey::unwrap_with_organization_key(
153            TestSymmKey::Organization,
154            &wrapped_symmetric_key,
155            &mut ctx,
156        );
157
158        assert!(matches!(
159            result,
160            Err(OrganizationPrivateKeyUnwrapError::UnwrapFailed)
161        ));
162    }
163
164    #[test]
165    fn test_persisted_private_key_is_the_organization_private_key() {
166        let key_store = KeyStore::<TestIds>::default();
167        {
168            let mut ctx = key_store.context_mut();
169            let wrapped_private_key = load_v1_test_vectors(&mut ctx);
170            let private_key_id = ctx
171                .unwrap_private_key(TestSymmKey::Organization, &wrapped_private_key)
172                .unwrap();
173            ctx.persist_private_key(private_key_id, TestPrivateKey::Organization)
174                .unwrap();
175        }
176        let ctx = key_store.context();
177
178        let organization_private_key =
179            OrganizationPrivateKey::from_context(TestPrivateKey::Organization, &ctx).unwrap();
180
181        let expected = SpkiPublicKeyBytes::from(
182            B64::try_from(TEST_VECTOR_ORGANIZATION_PUBLIC_KEY)
183                .unwrap()
184                .as_bytes()
185                .to_vec(),
186        );
187        assert_eq!(
188            organization_private_key
189                .public_key(&ctx)
190                .unwrap()
191                .to_der()
192                .unwrap(),
193            expected
194        );
195    }
196
197    #[test]
198    fn test_private_key_missing_from_the_context_is_no_organization_private_key() {
199        let key_store = KeyStore::<TestIds>::default();
200        let ctx = key_store.context();
201
202        let result = OrganizationPrivateKey::from_context(TestPrivateKey::Organization, &ctx);
203
204        assert!(result.is_none());
205    }
206
207    #[test]
208    fn test_unwrapping_with_a_missing_organization_key_fails() {
209        let key_store = KeyStore::<TestIds>::default();
210        let mut ctx = key_store.context_mut();
211        let wrapped_private_key: EncString = TEST_VECTOR_V1_WRAPPED_PRIVATE_KEY.parse().unwrap();
212
213        let result = OrganizationPrivateKey::unwrap_with_organization_key(
214            TestSymmKey::Organization,
215            &wrapped_private_key,
216            &mut ctx,
217        );
218
219        assert!(matches!(
220            result,
221            Err(OrganizationPrivateKeyUnwrapError::UnwrapFailed)
222        ));
223    }
224
225    #[test]
226    fn test_trust_proof_verifies_the_organization_public_key() {
227        let key_store = KeyStore::<TestIds>::default();
228        let mut ctx = key_store.context_mut();
229        let organization_private_key = unwrap_v1_organization_private_key(&mut ctx);
230        let organization_public_key = organization_private_key.public_key(&ctx).unwrap();
231
232        let result = organization_private_key
233            .trust_proof()
234            .verify(&organization_public_key, &ctx);
235
236        assert!(matches!(result, Ok(())));
237    }
238
239    #[test]
240    fn test_decapsulating_a_key_encapsulated_to_the_organization_public_key_opens_the_key() {
241        let key_store = KeyStore::<TestIds>::default();
242        let mut ctx = key_store.context_mut();
243        let organization_private_key = unwrap_v1_organization_private_key(&mut ctx);
244        let symmetric_key = ctx.make_symmetric_key(Aes256CbcHmac);
245        let encapsulated_key = UnsignedSharedKey::encapsulate(
246            symmetric_key,
247            &organization_private_key.public_key(&ctx).unwrap(),
248            &ctx,
249        )
250        .unwrap();
251
252        let decapsulated_key = organization_private_key
253            .decapsulate_key(&encapsulated_key, &mut ctx)
254            .unwrap();
255
256        ctx.assert_symmetric_keys_equal(symmetric_key, decapsulated_key);
257    }
258
259    #[test]
260    fn test_decapsulating_with_a_non_matching_private_key_fails() {
261        let key_store = KeyStore::<TestIds>::default();
262        let mut ctx = key_store.context_mut();
263        let organization_private_key = unwrap_v1_organization_private_key(&mut ctx);
264        let other_private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
265        let symmetric_key = ctx.make_symmetric_key(Aes256CbcHmac);
266        let encapsulated_key = UnsignedSharedKey::encapsulate(
267            symmetric_key,
268            &ctx.get_public_key(other_private_key).unwrap(),
269            &ctx,
270        )
271        .unwrap();
272
273        let result = organization_private_key.decapsulate_key(&encapsulated_key, &mut ctx);
274
275        assert!(matches!(result, Err(CryptoError::KeyDecrypt)));
276    }
277
278    #[test]
279    fn test_decapsulating_in_another_context_fails() {
280        let key_store = KeyStore::<TestIds>::default();
281        let mut ctx = key_store.context_mut();
282        let organization_private_key = unwrap_v1_organization_private_key(&mut ctx);
283        let symmetric_key = ctx.make_symmetric_key(Aes256CbcHmac);
284        let encapsulated_key = UnsignedSharedKey::encapsulate(
285            symmetric_key,
286            &organization_private_key.public_key(&ctx).unwrap(),
287            &ctx,
288        )
289        .unwrap();
290        let other_key_store = KeyStore::<TestIds>::default();
291        let mut other_ctx = other_key_store.context_mut();
292
293        let result = organization_private_key.decapsulate_key(&encapsulated_key, &mut other_ctx);
294
295        assert!(matches!(result, Err(CryptoError::MissingKeyId(_))));
296    }
297
298    #[test]
299    #[ignore = "Manual test to generate test vectors"]
300    fn generate_test_vectors() {
301        let key_store = KeyStore::<TestIds>::default();
302        let mut ctx = key_store.context_mut();
303        let wrapped_private_key = load_v1_test_vectors(&mut ctx);
304        let organization_private_key = OrganizationPrivateKey::unwrap_with_organization_key(
305            TestSymmKey::Organization,
306            &wrapped_private_key,
307            &mut ctx,
308        )
309        .unwrap();
310
311        let public_key = organization_private_key.public_key(&ctx).unwrap();
312
313        println!(
314            "const TEST_VECTOR_ORGANIZATION_PUBLIC_KEY: &str = \"{}\";",
315            B64::from(public_key.to_der().unwrap())
316        );
317    }
318
319    #[test]
320    fn test_organization_private_key_test_vector() {
321        let key_store = KeyStore::<TestIds>::default();
322        let mut ctx = key_store.context_mut();
323        let wrapped_private_key = load_v1_test_vectors(&mut ctx);
324
325        let organization_private_key = OrganizationPrivateKey::unwrap_with_organization_key(
326            TestSymmKey::Organization,
327            &wrapped_private_key,
328            &mut ctx,
329        )
330        .unwrap();
331
332        let expected = SpkiPublicKeyBytes::from(
333            B64::try_from(TEST_VECTOR_ORGANIZATION_PUBLIC_KEY)
334                .unwrap()
335                .as_bytes()
336                .to_vec(),
337        );
338        assert_eq!(
339            organization_private_key
340                .public_key(&ctx)
341                .unwrap()
342                .to_der()
343                .unwrap(),
344            expected
345        );
346    }
347}