Skip to main content

bitwarden_access_token_crypto/
material.rs

1//! Minting an access token's key material (the issuer side).
2
3use bitwarden_crypto::{EncString, KeySlotIds, KeyStoreContext, PrimitiveEncryptable};
4use zeroize::Zeroizing;
5
6use crate::{AccessTokenError, AccessTokenSeed, key::DERIVE_NAME, purpose::KeyPurpose};
7
8/// Key material for a new access-token registration.
9pub struct AccessTokenKeyMaterial {
10    /// The organization key, encrypted under the derived key, for the token holder.
11    pub encrypted_payload: EncString,
12    /// The derived key's base64, encrypted under the organization key, so the organization can
13    /// recover it without the token.
14    pub key: EncString,
15    /// Only reachable through [`Self::into_seed`].
16    seed: AccessTokenSeed,
17}
18
19impl AccessTokenKeyMaterial {
20    /// Consumes the key material to recover its seed.
21    pub fn into_seed(self) -> AccessTokenSeed {
22        self.seed
23    }
24
25    /// Makes a random seed, derives the key for `purpose`, and wraps the organization key for the
26    /// token holder and the derived key for the organization.
27    ///
28    /// A missing `organization_key` surfaces as [`AccessTokenError::Crypto`].
29    pub fn make<Ids: KeySlotIds>(
30        ctx: &mut KeyStoreContext<Ids>,
31        organization_key: Ids::Symmetric,
32        purpose: KeyPurpose,
33    ) -> Result<Self, AccessTokenError> {
34        let seed = AccessTokenSeed::make();
35
36        let derived_key = ctx.derive_shareable_key(
37            Zeroizing::new(*seed.as_bytes()),
38            DERIVE_NAME,
39            Some(purpose.as_str()),
40        )?;
41
42        // Encrypted under the derived key, which only the token holder can reproduce.
43        #[allow(deprecated)]
44        let organization_key_b64 = ctx
45            .dangerous_get_symmetric_key(organization_key)?
46            .to_base64();
47        let payload = serde_json::json!({ "encryptionKey": organization_key_b64.to_string() });
48        let encrypted_payload = payload.to_string().encrypt(ctx, derived_key)?;
49
50        #[allow(deprecated)]
51        let derived_key_b64 = ctx.dangerous_get_symmetric_key(derived_key)?.to_base64();
52        let key = derived_key_b64.to_string().encrypt(ctx, organization_key)?;
53
54        Ok(Self {
55            encrypted_payload,
56            key,
57            seed,
58        })
59    }
60}