1 // Copyright 2022 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 //! Wrappers around NP's usage of HKDF.
16 //!
17 //! All HKDF calls should happen in this module and expose the correct result type for
18 //! each derived key use case.
19 
20 #![no_std]
21 
22 #[cfg(feature = "std")]
23 extern crate std;
24 
25 use crypto_provider::aead::Aead;
26 use crypto_provider::{aes, aes::Aes128Key, hkdf::Hkdf, hmac::Hmac, CryptoProvider};
27 
28 pub mod v1_salt;
29 
30 /// A wrapper around the common NP usage of HMAC-SHA256.
31 ///
32 /// These are generally derived via HKDF, but could be used for any HMAC-SHA256 key.
33 #[derive(Clone, Debug)]
34 pub struct NpHmacSha256Key {
35     /// Nearby Presence uses 32-byte HMAC keys.
36     ///
37     /// Inside the HMAC algorithm they will be padded to 64 bytes.
38     key: [u8; 32],
39 }
40 
41 impl NpHmacSha256Key {
42     /// Build a fresh HMAC instance.
43     ///
44     /// Since each HMAC is modified as data is fed to it, HMACs should not be reused.
45     ///
46     /// See also [Self::calculate_hmac] for simple use cases.
build_hmac<C: CryptoProvider>(&self) -> C::HmacSha25647     pub fn build_hmac<C: CryptoProvider>(&self) -> C::HmacSha256 {
48         C::HmacSha256::new_from_key(self.key)
49     }
50 
51     /// Returns a reference to the underlying key bytes.
as_bytes(&self) -> &[u8; 32]52     pub fn as_bytes(&self) -> &[u8; 32] {
53         &self.key
54     }
55 
56     /// Build an HMAC, update it with the provided `data`, and finalize it, returning the resulting
57     /// MAC. This is convenient for one-and-done HMAC usage rather than incrementally accumulating
58     /// the final MAC.
calculate_hmac<C: CryptoProvider>(&self, data: &[u8]) -> [u8; 32]59     pub fn calculate_hmac<C: CryptoProvider>(&self, data: &[u8]) -> [u8; 32] {
60         let mut hmac = self.build_hmac::<C>();
61         hmac.update(data);
62         hmac.finalize()
63     }
64 
65     /// Build an HMAC, update it with the provided `data`, and verify it.
66     ///
67     /// This is convenient for one-and-done HMAC usage rather than incrementally accumulating
68     /// the final MAC.
verify_hmac<C: CryptoProvider>( &self, data: &[u8], expected_mac: [u8; 32], ) -> Result<(), crypto_provider::hmac::MacError>69     pub fn verify_hmac<C: CryptoProvider>(
70         &self,
71         data: &[u8],
72         expected_mac: [u8; 32],
73     ) -> Result<(), crypto_provider::hmac::MacError> {
74         let mut hmac = self.build_hmac::<C>();
75         hmac.update(data);
76         hmac.verify(expected_mac)
77     }
78 }
79 
80 impl From<[u8; 32]> for NpHmacSha256Key {
from(key: [u8; 32]) -> Self81     fn from(key: [u8; 32]) -> Self {
82         Self { key }
83     }
84 }
85 
86 /// Salt use for all NP HKDFs
87 const NP_HKDF_SALT: &[u8] = b"Google Nearby";
88 
89 /// A wrapper around an NP key seed for deriving HKDF-SHA256 sub keys.
90 pub struct NpKeySeedHkdf<C: CryptoProvider> {
91     hkdf: NpHkdf<C>,
92 }
93 
94 impl<C: CryptoProvider> NpKeySeedHkdf<C> {
95     /// Build an HKDF from a NP credential key seed
new(key_seed: &[u8; 32]) -> Self96     pub fn new(key_seed: &[u8; 32]) -> Self {
97         Self { hkdf: NpHkdf::new(key_seed) }
98     }
99 
100     /// LDT key used to decrypt a legacy advertisement
101     #[allow(clippy::expect_used)]
v0_ldt_key(&self) -> ldt::LdtKey<xts_aes::XtsAes128Key>102     pub fn v0_ldt_key(&self) -> ldt::LdtKey<xts_aes::XtsAes128Key> {
103         ldt::LdtKey::from_concatenated(
104             &self.hkdf.derive_array(b"V0 LDT key").expect("LDT key is a valid length"),
105         )
106     }
107 
108     /// HMAC key used when verifying the raw identity token extracted from an advertisement
v0_identity_token_hmac_key(&self) -> NpHmacSha256Key109     pub fn v0_identity_token_hmac_key(&self) -> NpHmacSha256Key {
110         self.hkdf.derive_hmac_sha256_key(b"V0 Identity token verification HMAC key")
111     }
112 
113     /// AES-GCM nonce used when decrypting metadata
114     #[allow(clippy::expect_used)]
v0_metadata_nonce(&self) -> <C::Aes128Gcm as Aead>::Nonce115     pub fn v0_metadata_nonce(&self) -> <C::Aes128Gcm as Aead>::Nonce {
116         self.hkdf.derive_array(b"V0 Metadata nonce").expect("Nonce is a valid length")
117     }
118 
119     /// AES-GCM nonce used when decrypting metadata.
120     ///
121     /// Shared between signed and unsigned since they use the same credential.
122     #[allow(clippy::expect_used)]
v1_metadata_nonce(&self) -> <C::Aes128Gcm as Aead>::Nonce123     pub fn v1_metadata_nonce(&self) -> <C::Aes128Gcm as Aead>::Nonce {
124         self.hkdf.derive_array(b"V1 Metadata nonce").expect("Nonce is a valid length")
125     }
126 
127     /// Derived keys for MIC short salt sections
v1_mic_short_salt_keys(&self) -> MicShortSaltSectionKeys<'_, C>128     pub fn v1_mic_short_salt_keys(&self) -> MicShortSaltSectionKeys<'_, C> {
129         MicShortSaltSectionKeys { hkdf: &self.hkdf }
130     }
131 
132     /// Derived keys for MIC extended salt sections
v1_mic_extended_salt_keys(&self) -> MicExtendedSaltSectionKeys<'_, C>133     pub fn v1_mic_extended_salt_keys(&self) -> MicExtendedSaltSectionKeys<'_, C> {
134         MicExtendedSaltSectionKeys { hkdf: &self.hkdf }
135     }
136 
137     /// Derived keys for MIC signature sections
v1_signature_keys(&self) -> SignatureSectionKeys<'_, C>138     pub fn v1_signature_keys(&self) -> SignatureSectionKeys<'_, C> {
139         SignatureSectionKeys { hkdf: &self.hkdf }
140     }
141 }
142 
143 /// Derived keys for MIC short salt sections
144 pub struct MicShortSaltSectionKeys<'a, C: CryptoProvider> {
145     hkdf: &'a NpHkdf<C>,
146 }
147 
148 impl<'a, C: CryptoProvider> MicShortSaltSectionKeys<'a, C> {
149     /// HMAC-SHA256 key used when verifying a section's ciphertext
mic_hmac_key(&self) -> NpHmacSha256Key150     pub fn mic_hmac_key(&self) -> NpHmacSha256Key {
151         self.hkdf.derive_hmac_sha256_key(b"MIC Section short salt HMAC key")
152     }
153 }
154 
155 impl<'a, C: CryptoProvider> DerivedSectionKeys<C> for MicShortSaltSectionKeys<'a, C> {
aes_key(&self) -> Aes128Key156     fn aes_key(&self) -> Aes128Key {
157         self.hkdf.derive_aes128_key(b"MIC Section short salt AES key")
158     }
159 
identity_token_hmac_key(&self) -> NpHmacSha256Key160     fn identity_token_hmac_key(&self) -> NpHmacSha256Key {
161         self.hkdf.derive_hmac_sha256_key(b"MIC Section short salt identity token HMAC key")
162     }
163 }
164 
165 /// Derived keys for MIC extended salt sections
166 pub struct MicExtendedSaltSectionKeys<'a, C: CryptoProvider> {
167     hkdf: &'a NpHkdf<C>,
168 }
169 
170 impl<'a, C: CryptoProvider> MicExtendedSaltSectionKeys<'a, C> {
171     /// HMAC-SHA256 key used when verifying a section's ciphertext
mic_hmac_key(&self) -> NpHmacSha256Key172     pub fn mic_hmac_key(&self) -> NpHmacSha256Key {
173         self.hkdf.derive_hmac_sha256_key(b"MIC Section extended salt HMAC key")
174     }
175 }
176 
177 impl<'a, C: CryptoProvider> DerivedSectionKeys<C> for MicExtendedSaltSectionKeys<'a, C> {
aes_key(&self) -> Aes128Key178     fn aes_key(&self) -> Aes128Key {
179         self.hkdf.derive_aes128_key(b"MIC Section extended salt AES key")
180     }
181 
identity_token_hmac_key(&self) -> NpHmacSha256Key182     fn identity_token_hmac_key(&self) -> NpHmacSha256Key {
183         self.hkdf.derive_hmac_sha256_key(b"MIC Section extended salt identity token HMAC key")
184     }
185 }
186 
187 /// Derived keys for Signature sections
188 pub struct SignatureSectionKeys<'a, C: CryptoProvider> {
189     hkdf: &'a NpHkdf<C>,
190 }
191 
192 impl<'a, C: CryptoProvider> DerivedSectionKeys<C> for SignatureSectionKeys<'a, C> {
aes_key(&self) -> Aes128Key193     fn aes_key(&self) -> Aes128Key {
194         self.hkdf.derive_aes128_key(b"Signature Section AES key")
195     }
196 
identity_token_hmac_key(&self) -> NpHmacSha256Key197     fn identity_token_hmac_key(&self) -> NpHmacSha256Key {
198         self.hkdf.derive_hmac_sha256_key(b"Signature Section identity token HMAC key")
199     }
200 }
201 
202 /// Derived keys for encrypted V1 sections
203 pub trait DerivedSectionKeys<C: CryptoProvider> {
204     /// AES128 key used when encrypting a section's ciphertext
aes_key(&self) -> Aes128Key205     fn aes_key(&self) -> Aes128Key;
206 
207     /// HMAC-SHA256 key used when verifying a section's plaintext identity token
identity_token_hmac_key(&self) -> NpHmacSha256Key208     fn identity_token_hmac_key(&self) -> NpHmacSha256Key;
209 }
210 
211 /// Expand a legacy salt into the expanded salt used with XOR padding in LDT.
212 #[allow(clippy::expect_used)]
v0_ldt_expanded_salt<C: CryptoProvider>(salt: &[u8; 2]) -> [u8; aes::BLOCK_SIZE]213 pub fn v0_ldt_expanded_salt<C: CryptoProvider>(salt: &[u8; 2]) -> [u8; aes::BLOCK_SIZE] {
214     simple_np_hkdf_expand::<{ aes::BLOCK_SIZE }, C>(salt, b"V0 LDT salt pad")
215         // XTS tweak size is the block cipher's block size
216         .expect("Tweak size is a valid HKDF size")
217 }
218 
219 /// Expand a v0 identity token into an AES128 metadata key.
220 #[allow(clippy::expect_used)]
v0_metadata_expanded_key<C: CryptoProvider>(identity_token: &[u8; 14]) -> [u8; 16]221 pub fn v0_metadata_expanded_key<C: CryptoProvider>(identity_token: &[u8; 14]) -> [u8; 16] {
222     simple_np_hkdf_expand::<16, C>(identity_token, b"V0 Metadata key expansion")
223         .expect("AES128 key is a valid HKDF size")
224 }
225 
226 /// Expand an extended MIC section's short salt into an AES-CTR nonce.
227 #[allow(clippy::expect_used)]
extended_mic_section_short_salt_nonce<C: CryptoProvider>( salt: [u8; 2], ) -> aes::ctr::AesCtrNonce228 pub fn extended_mic_section_short_salt_nonce<C: CryptoProvider>(
229     salt: [u8; 2],
230 ) -> aes::ctr::AesCtrNonce {
231     simple_np_hkdf_expand::<{ aes::ctr::AES_CTR_NONCE_LEN }, C>(
232         salt.as_slice(),
233         b"MIC Section short salt nonce",
234     )
235     .expect("AES-CTR nonce is a valid HKDF size")
236 }
237 
238 /// Build an HKDF using the NP HKDF salt, calculate output, and discard the HKDF.
239 /// If using the NP key seed as IKM, see [NpKeySeedHkdf] instead.
240 ///
241 /// Returns None if the requested size is > 255 * 32 bytes.
simple_np_hkdf_expand<const N: usize, C: CryptoProvider>( ikm: &[u8], info: &[u8], ) -> Option<[u8; N]>242 fn simple_np_hkdf_expand<const N: usize, C: CryptoProvider>(
243     ikm: &[u8],
244     info: &[u8],
245 ) -> Option<[u8; N]> {
246     let mut buf = [0; N];
247     let hkdf = np_salt_hkdf::<C>(ikm);
248     hkdf.expand(info, &mut buf[..]).map(|_| buf).ok()
249 }
250 
251 /// Construct an HKDF with the Nearby Presence salt and provided `ikm`
np_salt_hkdf<C: CryptoProvider>(ikm: &[u8]) -> C::HkdfSha256252 fn np_salt_hkdf<C: CryptoProvider>(ikm: &[u8]) -> C::HkdfSha256 {
253     C::HkdfSha256::new(Some(NP_HKDF_SALT), ikm)
254 }
255 
256 /// NP-flavored HKDF operations for common derived output types
257 struct NpHkdf<C: CryptoProvider> {
258     hkdf: C::HkdfSha256,
259 }
260 
261 impl<C: CryptoProvider> NpHkdf<C> {
262     /// Build an HKDF using the NP HKDF salt and supplied `ikm`
new(ikm: &[u8]) -> Self263     pub fn new(ikm: &[u8]) -> Self {
264         Self { hkdf: np_salt_hkdf::<C>(ikm) }
265     }
266 
267     /// Derive a length `N` array using the provided `info`
268     /// Returns `None` if N > 255 * 32.
derive_array<const N: usize>(&self, info: &[u8]) -> Option<[u8; N]>269     pub fn derive_array<const N: usize>(&self, info: &[u8]) -> Option<[u8; N]> {
270         let mut arr = [0_u8; N];
271         self.hkdf.expand(info, &mut arr).map(|_| arr).ok()
272     }
273 
274     /// Derive an HMAC-SHA256 key using the provided `info`
275     #[allow(clippy::expect_used)]
derive_hmac_sha256_key(&self, info: &[u8]) -> NpHmacSha256Key276     pub fn derive_hmac_sha256_key(&self, info: &[u8]) -> NpHmacSha256Key {
277         self.derive_array(info).expect("HMAC-SHA256 keys are a valid length").into()
278     }
279     /// Derive an AES-128 key using the provided `info`
280     #[allow(clippy::expect_used)]
derive_aes128_key(&self, info: &[u8]) -> Aes128Key281     pub fn derive_aes128_key(&self, info: &[u8]) -> Aes128Key {
282         self.derive_array(info).expect("AES128 keys are a valid length").into()
283     }
284 }
285