bitwarden_access_connector/
crypto.rs1use bitwarden_access_token::{AccessToken, AccessTokenError};
7use bitwarden_core::{
8 OrganizationId,
9 key_management::{KeySlotIds, SymmetricKeySlotId},
10};
11use bitwarden_crypto::KeyStore;
12use thiserror::Error;
13
14#[derive(Debug, Error)]
16pub(crate) enum CryptoModuleError {
17 #[error("org-key payload is invalid")]
19 InvalidPayload,
20
21 #[error("org-key payload does not contain a valid encryption key")]
23 InvalidOrgKey,
24
25 #[error("cryptographic operation failed")]
27 Crypto(#[from] bitwarden_crypto::CryptoError),
28}
29
30pub(crate) fn unwrap_org_key(
33 store: &KeyStore<KeySlotIds>,
34 organization_id: OrganizationId,
35 token: &AccessToken,
36 encrypted_payload: &str,
37) -> Result<(), CryptoModuleError> {
38 let mut ctx = store.context_mut();
39
40 token
41 .open_payload(
42 &mut ctx,
43 encrypted_payload,
44 SymmetricKeySlotId::Organization(organization_id),
45 )
46 .map_err(|e| match e {
47 AccessTokenError::InvalidPayload => CryptoModuleError::InvalidPayload,
48 AccessTokenError::InvalidOrgKey => CryptoModuleError::InvalidOrgKey,
49 AccessTokenError::Crypto(c) => CryptoModuleError::Crypto(c),
50 })
51}
52
53#[cfg(test)]
54mod tests {
55 use bitwarden_access_token::{AccessTokenKind, make_access_token_secrets};
56 use bitwarden_crypto::{
57 KeyDecryptable, PrimitiveEncryptable, SymmetricCryptoKey, SymmetricKeyAlgorithm,
58 };
59 use uuid::uuid;
60
61 use super::*;
62 use crate::test_support::test_org_id as org_id;
63
64 fn org_slot() -> SymmetricKeySlotId {
65 SymmetricKeySlotId::Organization(org_id())
66 }
67
68 fn make_store_with_org_key() -> (KeyStore<KeySlotIds>, SymmetricCryptoKey) {
69 let store: KeyStore<KeySlotIds> = KeyStore::default();
70 let org_key = SymmetricCryptoKey::make(SymmetricKeyAlgorithm::Aes256CbcHmac);
71
72 #[allow(deprecated)]
73 store
74 .context_mut()
75 .set_symmetric_key(org_slot(), org_key.clone())
76 .expect("set_symmetric_key");
77
78 (store, org_key)
79 }
80
81 fn mint_token_and_payload(issuer_store: &KeyStore<KeySlotIds>) -> (AccessToken, String) {
84 let secrets = {
85 let mut ctx = issuer_store.context_mut();
86 make_access_token_secrets(&mut ctx, org_slot(), AccessTokenKind::AccessConnector)
87 .expect("mint secrets")
88 };
89 let encrypted_payload = secrets.encrypted_payload.to_string();
90 let token_str = secrets.into_token(uuid!("22222222-2222-2222-2222-222222222222"), "secret");
91 let token = AccessToken::parse(&token_str, AccessTokenKind::AccessConnector)
92 .expect("the token parses");
93 (token, encrypted_payload)
94 }
95
96 #[test]
97 fn unwrap_org_key_round_trip() {
98 let (issuer_store, org_key) = make_store_with_org_key();
99 let (token, encrypted_payload) = mint_token_and_payload(&issuer_store);
100
101 let store: KeyStore<KeySlotIds> = KeyStore::default();
102 assert!(!store.context().has_symmetric_key(org_slot()));
103
104 unwrap_org_key(&store, org_id(), &token, &encrypted_payload).expect("unwrap_org_key");
105
106 let probe = "probe value";
108 let encrypted_probe = {
109 let mut ctx = store.context();
110 probe.encrypt(&mut ctx, org_slot()).expect("encrypt probe")
111 };
112
113 let decrypted: String = encrypted_probe
114 .decrypt_with_key(&org_key)
115 .expect("decrypt probe");
116 assert_eq!(decrypted, probe);
117 }
118
119 #[test]
120 fn unwrap_org_key_bad_payload_returns_error() {
121 let (issuer_store, _org_key) = make_store_with_org_key();
122 let (token, _payload) = mint_token_and_payload(&issuer_store);
123 let store: KeyStore<KeySlotIds> = KeyStore::default();
124
125 let result = unwrap_org_key(&store, org_id(), &token, "not-an-enc-string");
126 assert!(
127 matches!(result, Err(CryptoModuleError::InvalidPayload)),
128 "expected InvalidPayload, got {result:?}",
129 );
130 }
131}