bitwarden_organization_crypto/
trust_proof.rs1use bitwarden_crypto::{
15 CoseKeyThumbprint, CoseKeyThumbprintExt, KeySlotIds, KeyStoreContext, PublicKey, fingerprint,
16};
17use uuid::Uuid;
18
19pub enum TrustProof<Ids: KeySlotIds> {
21 ManuallyTrustedFingerprint(FingerprintPhrase),
23 ManuallyTrustedThumbprint(CoseKeyThumbprint),
25 CryptographicallyTrusted(CryptographicProof<Ids>),
27 #[cfg(test)]
30 #[expect(private_interfaces)]
31 TestStub(Result<(), TrustProofError>),
32}
33
34pub struct FingerprintPhrase {
36 pub fingerprint_material: Uuid,
38 pub phrase: String,
40}
41
42pub enum CryptographicProof<Ids: KeySlotIds> {
44 OwnPrivateKey(Ids::Private),
47}
48
49#[derive(Clone, Copy)]
51pub(crate) enum TrustProofError {
52 PrivateKeyNotInContext,
54 UntrustedPublicKey,
57}
58
59impl<Ids: KeySlotIds> TrustProof<Ids> {
60 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 const TEST_VECTOR_V1_ORGANIZATION_ID: &str = "0790758b-61d1-4a4d-9839-bf6e51bdf1f0";
107 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}