Skip to main content

bitwarden_organization_crypto/
trust_proof.rs

1//! Trust proofs: how the caller knows that a key belongs to the intended recipient.
2//!
3//! Org Membership V1 encapsulates a shared key to the recipient's public encryption key, so whoever
4//! holds the matching private key gets the shared key. A public key that the server supplies can be
5//! swapped by the server, so the caller must trust the public key before the key is shared. In V1,
6//! the trust is manual: a person compared the fingerprint phrase of the public key, or the caller
7//! has the thumbprint of the public key from a trusted source. In some cases the trust is
8//! cryptographic, because the caller holds the matching private key.
9//!
10//! Org Membership V2 seals keys between identities, which authenticates the sender. An identity
11//! that the caller has not trusted before still needs a manual check, such as a comparison of the
12//! fingerprint phrase.
13
14use bitwarden_crypto::{
15    CoseKeyThumbprint, CoseKeyThumbprintExt, KeySlotIds, KeyStoreContext, PublicKey, fingerprint,
16};
17use uuid::Uuid;
18
19/// The proof that the caller trusts the recipient's public key.
20pub enum TrustProof<Ids: KeySlotIds> {
21    /// The fingerprint phrase that a person compared.
22    ManuallyTrustedFingerprint(FingerprintPhrase),
23    /// The thumbprint of a public key from a trusted source.
24    ManuallyTrustedThumbprint(CoseKeyThumbprint),
25    /// A cryptographic proof that the public key belongs to the recipient.
26    CryptographicallyTrusted(CryptographicProof<Ids>),
27    /// Verifies with a fixed result, so that the tests of callers do not repeat the key setup of
28    /// each proof.
29    #[cfg(test)]
30    #[expect(private_interfaces)]
31    TestStub(Result<(), TrustProofError>),
32}
33
34/// The fingerprint phrase of a public key, as shown to a person.
35pub struct FingerprintPhrase {
36    /// The id of the key owner: the user id of a member, or the id of an organization.
37    pub fingerprint_material: Uuid,
38    /// The words of the fingerprint, joined with `-`.
39    pub phrase: String,
40}
41
42/// The kinds of cryptographic proof.
43pub enum CryptographicProof<Ids: KeySlotIds> {
44    /// The caller holds the matching private key in the key store context, for example the
45    /// user's own key pair, or the organization's key pair held by an admin.
46    OwnPrivateKey(Ids::Private),
47}
48
49/// The ways a public key can fail its trust proof.
50#[derive(Clone, Copy)]
51pub(crate) enum TrustProofError {
52    /// The private key of [`CryptographicProof::OwnPrivateKey`] is not in the key store context.
53    PrivateKeyNotInContext,
54    /// The public key does not match the trusted fingerprint phrase or thumbprint, or does not
55    /// belong to the key pair of the proof.
56    UntrustedPublicKey,
57}
58
59impl<Ids: KeySlotIds> TrustProof<Ids> {
60    /// Verifies that `public_key` satisfies the proof.
61    ///
62    /// A public key without a fingerprint phrase or a thumbprint cannot match a trusted one, so it
63    /// is untrusted.
64    pub(crate) fn verify(
65        &self,
66        public_key: &PublicKey,
67        ctx: &KeyStoreContext<Ids>,
68    ) -> Result<(), TrustProofError> {
69        match self {
70            Self::ManuallyTrustedFingerprint(trusted_fingerprint) => {
71                let phrase = public_key.to_der().and_then(|spki| {
72                    fingerprint(&trusted_fingerprint.fingerprint_material.to_string(), &spki)
73                });
74                match phrase {
75                    Ok(phrase) if phrase == trusted_fingerprint.phrase => Ok(()),
76                    _ => Err(TrustProofError::UntrustedPublicKey),
77                }
78            }
79            Self::ManuallyTrustedThumbprint(trusted_thumbprint) => match public_key.thumbprint() {
80                Ok(thumbprint) if &thumbprint == trusted_thumbprint => Ok(()),
81                _ => Err(TrustProofError::UntrustedPublicKey),
82            },
83            Self::CryptographicallyTrusted(CryptographicProof::OwnPrivateKey(private_key)) => {
84                let own_public_key = ctx
85                    .get_public_key(*private_key)
86                    .map_err(|_| TrustProofError::PrivateKeyNotInContext)?;
87                if &own_public_key != public_key {
88                    return Err(TrustProofError::UntrustedPublicKey);
89                }
90                Ok(())
91            }
92            #[cfg(test)]
93            Self::TestStub(result) => *result,
94        }
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use bitwarden_crypto::{KeyStore, PublicKeyEncryptionAlgorithm};
101
102    use super::*;
103    use crate::test_support::{TestIds, unwrap_v1_organization_private_key};
104
105    /// The id of the organization whose key pair is in the shared V1 test vectors.
106    const TEST_VECTOR_V1_ORGANIZATION_ID: &str = "0790758b-61d1-4a4d-9839-bf6e51bdf1f0";
107    /// The fingerprint phrase of the V1 organization public key, derived with
108    /// [`TEST_VECTOR_V1_ORGANIZATION_ID`].
109    const TEST_VECTOR_V1_ORGANIZATION_FINGERPRINT_PHRASE: &str =
110        "overlaid-reference-concierge-sharper-scorpion";
111
112    fn test_vector_v1_organization_fingerprint_phrase() -> FingerprintPhrase {
113        FingerprintPhrase {
114            fingerprint_material: TEST_VECTOR_V1_ORGANIZATION_ID.parse().unwrap(),
115            phrase: TEST_VECTOR_V1_ORGANIZATION_FINGERPRINT_PHRASE.to_string(),
116        }
117    }
118
119    #[test]
120    #[ignore = "Manual test to generate test vectors"]
121    fn generate_test_vectors() {
122        let key_store = KeyStore::<TestIds>::default();
123        let mut ctx = key_store.context_mut();
124        let public_key = unwrap_v1_organization_private_key(&mut ctx)
125            .public_key(&ctx)
126            .unwrap();
127
128        println!(
129            "const TEST_VECTOR_V1_ORGANIZATION_FINGERPRINT_PHRASE: &str = \"{}\";",
130            fingerprint(
131                TEST_VECTOR_V1_ORGANIZATION_ID,
132                &public_key.to_der().unwrap()
133            )
134            .unwrap()
135        );
136    }
137
138    #[test]
139    fn test_manually_trusted_fingerprint_of_the_public_key_verifies() {
140        let key_store = KeyStore::<TestIds>::default();
141        let mut ctx = key_store.context_mut();
142        let public_key = unwrap_v1_organization_private_key(&mut ctx)
143            .public_key(&ctx)
144            .unwrap();
145        let trust_proof = TrustProof::ManuallyTrustedFingerprint(
146            test_vector_v1_organization_fingerprint_phrase(),
147        );
148
149        let result = trust_proof.verify(&public_key, &ctx);
150
151        assert!(matches!(result, Ok(())));
152    }
153
154    #[test]
155    fn test_manually_trusted_fingerprint_of_another_public_key_fails() {
156        let key_store = KeyStore::<TestIds>::default();
157        let mut ctx = key_store.context_mut();
158        let other_private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
159        let trust_proof = TrustProof::ManuallyTrustedFingerprint(
160            test_vector_v1_organization_fingerprint_phrase(),
161        );
162
163        let result = trust_proof.verify(&ctx.get_public_key(other_private_key).unwrap(), &ctx);
164
165        assert!(matches!(result, Err(TrustProofError::UntrustedPublicKey)));
166    }
167
168    #[test]
169    fn test_manually_trusted_fingerprint_of_other_fingerprint_material_fails() {
170        let key_store = KeyStore::<TestIds>::default();
171        let mut ctx = key_store.context_mut();
172        let public_key = unwrap_v1_organization_private_key(&mut ctx)
173            .public_key(&ctx)
174            .unwrap();
175        let trust_proof = TrustProof::ManuallyTrustedFingerprint(FingerprintPhrase {
176            fingerprint_material: Uuid::new_v4(),
177            phrase: TEST_VECTOR_V1_ORGANIZATION_FINGERPRINT_PHRASE.to_string(),
178        });
179
180        let result = trust_proof.verify(&public_key, &ctx);
181
182        assert!(matches!(result, Err(TrustProofError::UntrustedPublicKey)));
183    }
184
185    #[test]
186    fn test_manually_trusted_thumbprint_of_the_public_key_verifies() {
187        let key_store = KeyStore::<TestIds>::default();
188        let mut ctx = key_store.context_mut();
189        let private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
190        let public_key = ctx.get_public_key(private_key).unwrap();
191        let trust_proof = TrustProof::ManuallyTrustedThumbprint(public_key.thumbprint().unwrap());
192
193        let result = trust_proof.verify(&public_key, &ctx);
194
195        assert!(matches!(result, Ok(())));
196    }
197
198    #[test]
199    fn test_manually_trusted_thumbprint_of_another_public_key_fails() {
200        let key_store = KeyStore::<TestIds>::default();
201        let mut ctx = key_store.context_mut();
202        let private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
203        let other_private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
204        let other_public_key = ctx.get_public_key(other_private_key).unwrap();
205        let trust_proof =
206            TrustProof::ManuallyTrustedThumbprint(other_public_key.thumbprint().unwrap());
207
208        let result = trust_proof.verify(&ctx.get_public_key(private_key).unwrap(), &ctx);
209
210        assert!(matches!(result, Err(TrustProofError::UntrustedPublicKey)));
211    }
212
213    #[test]
214    fn test_cryptographically_trusted_own_private_key_of_the_public_key_verifies() {
215        let key_store = KeyStore::<TestIds>::default();
216        let mut ctx = key_store.context_mut();
217        let private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
218        let trust_proof =
219            TrustProof::CryptographicallyTrusted(CryptographicProof::OwnPrivateKey(private_key));
220
221        let result = trust_proof.verify(&ctx.get_public_key(private_key).unwrap(), &ctx);
222
223        assert!(matches!(result, Ok(())));
224    }
225
226    #[test]
227    fn test_cryptographically_trusted_own_private_key_missing_from_the_context_fails() {
228        let key_store = KeyStore::<TestIds>::default();
229        let mut ctx = key_store.context_mut();
230        let private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
231        let public_key = ctx.get_public_key(private_key).unwrap();
232        let trust_proof =
233            TrustProof::CryptographicallyTrusted(CryptographicProof::OwnPrivateKey(private_key));
234        let other_key_store = KeyStore::<TestIds>::default();
235        let other_ctx = other_key_store.context();
236
237        let result = trust_proof.verify(&public_key, &other_ctx);
238
239        assert!(matches!(
240            result,
241            Err(TrustProofError::PrivateKeyNotInContext)
242        ));
243    }
244
245    #[test]
246    fn test_cryptographically_trusted_own_private_key_of_another_key_pair_fails() {
247        let key_store = KeyStore::<TestIds>::default();
248        let mut ctx = key_store.context_mut();
249        let private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
250        let other_private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
251        let trust_proof = TrustProof::CryptographicallyTrusted(CryptographicProof::OwnPrivateKey(
252            other_private_key,
253        ));
254
255        let result = trust_proof.verify(&ctx.get_public_key(private_key).unwrap(), &ctx);
256
257        assert!(matches!(result, Err(TrustProofError::UntrustedPublicKey)));
258    }
259}