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