• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Cryptographic materials for v0 advertisement-format credentials.
16 use crate::credential::{protocol_version_seal, DiscoveryMetadataCryptoMaterial, ProtocolVersion};
17 use crypto_provider::{aead::Aead, aes, aes::ctr, CryptoProvider};
18 use ldt_np_adv::V0IdentityToken;
19 use np_hkdf::NpKeySeedHkdf;
20 
21 /// Cryptographic information about a particular V0 discovery credential
22 /// necessary to match and decrypt encrypted V0 advertisements.
23 #[derive(Clone, Debug, PartialEq, Eq)]
24 pub struct V0DiscoveryCredential {
25     /// The 32-byte key-seed used for generating other key material.
26     pub key_seed: [u8; 32],
27 
28     /// The (LDT-variant) HMAC of the identity token.
29     pub expected_identity_token_hmac: [u8; 32],
30 }
31 
32 impl V0DiscoveryCredential {
33     /// Construct an [V0DiscoveryCredential] from the provided identity data.
new(key_seed: [u8; 32], expected_identity_token_hmac: [u8; 32]) -> Self34     pub fn new(key_seed: [u8; 32], expected_identity_token_hmac: [u8; 32]) -> Self {
35         Self { key_seed, expected_identity_token_hmac }
36     }
37 }
38 
39 impl DiscoveryMetadataCryptoMaterial<V0> for V0DiscoveryCredential {
metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12]40     fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
41         np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed).v0_metadata_nonce()
42     }
43 }
44 
45 impl V0DiscoveryCryptoMaterial for V0DiscoveryCredential {
ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C>46     fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C> {
47         let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed);
48         ldt_np_adv::build_np_adv_decrypter(
49             &hkdf.v0_ldt_key(),
50             self.expected_identity_token_hmac,
51             hkdf.v0_identity_token_hmac_key(),
52         )
53     }
54 }
55 
56 /// Type-level identifier for the V0 protocol version.
57 #[derive(Debug, Clone)]
58 pub enum V0 {}
59 
60 impl protocol_version_seal::ProtocolVersionSeal for V0 {}
61 
62 impl ProtocolVersion for V0 {
63     type DiscoveryCredential = V0DiscoveryCredential;
64     type IdentityToken = V0IdentityToken;
65 
metadata_nonce_from_key_seed<C: CryptoProvider>( hkdf: &NpKeySeedHkdf<C>, ) -> <C::Aes128Gcm as Aead>::Nonce66     fn metadata_nonce_from_key_seed<C: CryptoProvider>(
67         hkdf: &NpKeySeedHkdf<C>,
68     ) -> <C::Aes128Gcm as Aead>::Nonce {
69         hkdf.v0_metadata_nonce()
70     }
71 
72     // TODO should be IdP specific
extract_metadata_key<C: CryptoProvider>(metadata_key: V0IdentityToken) -> aes::Aes128Key73     fn extract_metadata_key<C: CryptoProvider>(metadata_key: V0IdentityToken) -> aes::Aes128Key {
74         np_hkdf::v0_metadata_expanded_key::<C>(&metadata_key.bytes()).into()
75     }
76 }
77 
78 /// Trait which exists purely to be able to restrict the protocol
79 /// version of certain type-bounds to V0.
80 pub trait V0ProtocolVersion: ProtocolVersion {}
81 
82 impl V0ProtocolVersion for V0 {}
83 
84 /// Cryptographic material for an individual NP credential used to decrypt v0 advertisements.
85 /// Unlike [`V0DiscoveryCredential`], derived information about cryptographic materials may
86 /// be stored in implementors of this trait.
87 // Space-time tradeoffs:
88 // - LDT keys (64b) take about 1.4us.
89 pub trait V0DiscoveryCryptoMaterial: DiscoveryMetadataCryptoMaterial<V0> {
90     /// Returns an LDT NP advertisement cipher built with the provided `Aes`
ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C>91     fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C>;
92 }
93 
94 /// [`V0DiscoveryCryptoMaterial`] that minimizes CPU time when providing key material at
95 /// the expense of occupied memory.
96 pub struct PrecalculatedV0DiscoveryCryptoMaterial {
97     pub(crate) legacy_ldt_key: ldt::LdtKey<xts_aes::XtsAes128Key>,
98     pub(crate) legacy_identity_token_hmac: [u8; 32],
99     pub(crate) legacy_identity_token_hmac_key: [u8; 32],
100     pub(crate) metadata_nonce: ctr::AesCtrNonce,
101 }
102 
103 impl PrecalculatedV0DiscoveryCryptoMaterial {
104     /// Construct a new instance from the provided credential material.
new<C: CryptoProvider>(discovery_credential: &V0DiscoveryCredential) -> Self105     pub(crate) fn new<C: CryptoProvider>(discovery_credential: &V0DiscoveryCredential) -> Self {
106         let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&discovery_credential.key_seed);
107         Self {
108             legacy_ldt_key: hkdf.v0_ldt_key(),
109             legacy_identity_token_hmac: discovery_credential.expected_identity_token_hmac,
110             legacy_identity_token_hmac_key: *hkdf.v0_identity_token_hmac_key().as_bytes(),
111             metadata_nonce: hkdf.v0_metadata_nonce(),
112         }
113     }
114 }
115 
116 impl DiscoveryMetadataCryptoMaterial<V0> for PrecalculatedV0DiscoveryCryptoMaterial {
metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12]117     fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
118         self.metadata_nonce
119     }
120 }
121 
122 impl V0DiscoveryCryptoMaterial for PrecalculatedV0DiscoveryCryptoMaterial {
ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C>123     fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C> {
124         ldt_np_adv::build_np_adv_decrypter(
125             &self.legacy_ldt_key,
126             self.legacy_identity_token_hmac,
127             self.legacy_identity_token_hmac_key.into(),
128         )
129     }
130 }
131 
132 // Implementations for reference types -- we don't provide a blanket impl for references
133 // due to the potential to conflict with downstream crates' implementations.
134 
135 impl<'a> DiscoveryMetadataCryptoMaterial<V0> for &'a V0DiscoveryCredential {
metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12]136     fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
137         (*self).metadata_nonce::<C>()
138     }
139 }
140 
141 impl<'a> V0DiscoveryCryptoMaterial for &'a V0DiscoveryCredential {
ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C>142     fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C> {
143         (*self).ldt_adv_cipher::<C>()
144     }
145 }
146 
147 impl<'a> DiscoveryMetadataCryptoMaterial<V0> for &'a PrecalculatedV0DiscoveryCryptoMaterial {
metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12]148     fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
149         (*self).metadata_nonce::<C>()
150     }
151 }
152 
153 impl<'a> V0DiscoveryCryptoMaterial for &'a PrecalculatedV0DiscoveryCryptoMaterial {
ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C>154     fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C> {
155         (*self).ldt_adv_cipher::<C>()
156     }
157 }
158 
159 /// Crypto material for creating V1 advertisements.
160 #[derive(Clone, Debug, PartialEq, Eq)]
161 pub struct V0BroadcastCredential {
162     /// The 32-byte key-seed used for generating other key material.
163     pub key_seed: [u8; 32],
164 
165     /// The 14-byte identity-token which identifies the sender.
166     pub identity_token: V0IdentityToken,
167 }
168 
169 impl V0BroadcastCredential {
170     /// Builds some simple broadcast crypto-materials out of
171     /// the provided key-seed and version-specific metadata-key.
new(key_seed: [u8; 32], identity_token: V0IdentityToken) -> Self172     pub fn new(key_seed: [u8; 32], identity_token: V0IdentityToken) -> Self {
173         Self { key_seed, identity_token }
174     }
175 
176     /// Key seed from which other keys are derived.
key_seed(&self) -> [u8; 32]177     pub(crate) fn key_seed(&self) -> [u8; 32] {
178         self.key_seed
179     }
180 
181     /// Identity token that will be incorporated into encrypted advertisements.
identity_token(&self) -> V0IdentityToken182     pub(crate) fn identity_token(&self) -> V0IdentityToken {
183         self.identity_token
184     }
185 
186     /// Derive a discovery credential with the data necessary to discover advertisements produced
187     /// by this broadcast credential.
derive_discovery_credential<C: CryptoProvider>(&self) -> V0DiscoveryCredential188     pub fn derive_discovery_credential<C: CryptoProvider>(&self) -> V0DiscoveryCredential {
189         let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed);
190 
191         V0DiscoveryCredential::new(
192             self.key_seed,
193             hkdf.v0_identity_token_hmac_key().calculate_hmac::<C>(&self.identity_token.bytes()),
194         )
195     }
196 }
197