• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #![no_std]
20 #![forbid(unsafe_code)]
21 #![deny(
22     missing_docs,
23     clippy::indexing_slicing,
24     clippy::unwrap_used,
25     clippy::panic,
26     clippy::expect_used
27 )]
28 
29 extern crate core;
30 
31 use core::marker;
32 use crypto_provider::{aes::Aes128Key, hkdf::Hkdf, hmac::Hmac, CryptoProvider};
33 
34 pub mod v1_salt;
35 
36 /// A wrapper around the common NP usage of HMAC-SHA256.
37 ///
38 /// These are generally derived via HKDF, but could be used for any HMAC-SHA256 key.
39 #[derive(Debug)]
40 pub struct NpHmacSha256Key<C: CryptoProvider> {
41     /// Nearby Presence uses 32-byte HMAC keys.
42     ///
43     /// Inside the HMAC algorithm they will be padded to 64 bytes.
44     key: [u8; 32],
45     c_phantom: marker::PhantomData<C>,
46 }
47 
48 impl<C: CryptoProvider> NpHmacSha256Key<C> {
49     /// Build a fresh HMAC instance.
50     ///
51     /// Since each HMAC is modified as data is fed to it, HMACs should not be reused.
52     ///
53     /// See also [Self::calculate_hmac] for simple use cases.
build_hmac(&self) -> C::HmacSha25654     pub fn build_hmac(&self) -> C::HmacSha256 {
55         C::HmacSha256::new_from_key(self.key)
56     }
57 
58     /// Returns a reference to the underlying key bytes.
as_bytes(&self) -> &[u8; 32]59     pub fn as_bytes(&self) -> &[u8; 32] {
60         &self.key
61     }
62 
63     /// Build an HMAC, update it with the provided `data`, and finalize it, returning the resulting
64     /// MAC. This is convenient for one-and-done HMAC usage rather than incrementally accumulating
65     /// the final MAC.
calculate_hmac(&self, data: &[u8]) -> [u8; 32]66     pub fn calculate_hmac(&self, data: &[u8]) -> [u8; 32] {
67         let mut hmac = self.build_hmac();
68         hmac.update(data);
69         hmac.finalize()
70     }
71 }
72 
73 impl<C: CryptoProvider> From<[u8; 32]> for NpHmacSha256Key<C> {
from(key: [u8; 32]) -> Self74     fn from(key: [u8; 32]) -> Self {
75         Self {
76             key,
77             c_phantom: Default::default(),
78         }
79     }
80 }
81 
82 impl<C: CryptoProvider> Clone for NpHmacSha256Key<C> {
clone(&self) -> Self83     fn clone(&self) -> Self {
84         Self {
85             key: self.key,
86             c_phantom: Default::default(),
87         }
88     }
89 }
90 
91 /// Salt use for all NP HKDFs
92 const NP_HKDF_SALT: &[u8] = b"Google Nearby";
93 
94 /// A wrapper around an NP key seed for deriving HKDF-SHA256 sub keys.
95 pub struct NpKeySeedHkdf<C: CryptoProvider> {
96     hkdf: C::HkdfSha256,
97 }
98 
99 impl<C: CryptoProvider> NpKeySeedHkdf<C> {
100     /// Build an HKDF from a NP credential key seed
new(key_seed: &[u8; 32]) -> Self101     pub fn new(key_seed: &[u8; 32]) -> Self {
102         Self {
103             hkdf: np_hkdf::<C>(key_seed),
104         }
105     }
106 
107     /// LDT key used to decrypt a legacy advertisement
108     #[allow(clippy::expect_used)]
legacy_ldt_key(&self) -> ldt::LdtKey<xts_aes::XtsAes128Key>109     pub fn legacy_ldt_key(&self) -> ldt::LdtKey<xts_aes::XtsAes128Key> {
110         ldt::LdtKey::from_concatenated(
111             &self
112                 .hkdf_array(b"Legacy LDT key")
113                 .expect("LDT key is a valid length"),
114         )
115     }
116 
117     /// HMAC key used when verifying the raw metadata key extracted from an advertisement
118     #[allow(clippy::expect_used)]
legacy_metadata_key_hmac_key(&self) -> NpHmacSha256Key<C>119     pub fn legacy_metadata_key_hmac_key(&self) -> NpHmacSha256Key<C> {
120         self.hkdf_array(b"Legacy metadata key verification HMAC key")
121             .expect("Hmac keys are a valid length")
122             .into()
123     }
124 
125     /// AES-GCM IV used when decrypting metadata
126     #[allow(clippy::expect_used)]
legacy_metadata_iv(&self) -> [u8; 12]127     pub fn legacy_metadata_iv(&self) -> [u8; 12] {
128         self.hkdf_array(b"Legacy Metadata IV")
129             .expect("IV is a valid length")
130     }
131 
132     /// AES-GCM IV used when decrypting metadata.
133     ///
134     /// Shared between signed and unsigned since they use the same credential.
135     #[allow(clippy::expect_used)]
extended_metadata_iv(&self) -> [u8; 12]136     pub fn extended_metadata_iv(&self) -> [u8; 12] {
137         self.hkdf_array(b"Metadata IV")
138             .expect("IV is a valid length")
139     }
140 
141     /// HMAC key used when verifying the raw metadata key extracted from an advertisement
142     #[allow(clippy::expect_used)]
extended_unsigned_metadata_key_hmac_key(&self) -> NpHmacSha256Key<C>143     pub fn extended_unsigned_metadata_key_hmac_key(&self) -> NpHmacSha256Key<C> {
144         self.hkdf_array(b"Unsigned Section metadata key HMAC key")
145             .expect("Hmac keys are a valid length")
146             .into()
147     }
148 
149     /// AES128 key used when decrypting an extended unsigned section
150     #[allow(clippy::expect_used)]
extended_unsigned_section_aes_key(&self) -> Aes128Key151     pub fn extended_unsigned_section_aes_key(&self) -> Aes128Key {
152         self.hkdf_array(b"Unsigned Section AES key")
153             .expect("AES128 keys are a valid length")
154             .into()
155     }
156 
157     /// HMAC-SHA256 key used when verifying an extended unsigned section
158     #[allow(clippy::expect_used)]
extended_unsigned_section_mic_hmac_key(&self) -> NpHmacSha256Key<C>159     pub fn extended_unsigned_section_mic_hmac_key(&self) -> NpHmacSha256Key<C> {
160         self.hkdf_array(b"Unsigned Section HMAC key")
161             .expect("Hmac keys are a valid length")
162             .into()
163     }
164 
165     /// HMAC key used when verifying the raw metadata key extracted from an extended signed advertisement
166     #[allow(clippy::expect_used)]
extended_signed_metadata_key_hmac_key(&self) -> NpHmacSha256Key<C>167     pub fn extended_signed_metadata_key_hmac_key(&self) -> NpHmacSha256Key<C> {
168         self.hkdf_array(b"Signed Section metadata key HMAC key")
169             .expect("Hmac keys are a valid length")
170             .into()
171     }
172 
173     /// AES128 key used when decrypting an extended signed section
174     #[allow(clippy::expect_used)]
extended_signed_section_aes_key(&self) -> Aes128Key175     pub fn extended_signed_section_aes_key(&self) -> Aes128Key {
176         self.hkdf_array(b"Signed Section AES key")
177             .expect("AES128 keys are a valid length")
178             .into()
179     }
180 
181     /// Derive a length `N` array using the provided `info`
182     /// Returns None if N > 255 * 32.
hkdf_array<const N: usize>(&self, info: &[u8]) -> Option<[u8; N]>183     fn hkdf_array<const N: usize>(&self, info: &[u8]) -> Option<[u8; N]> {
184         let mut arr = [0_u8; N];
185         self.hkdf.expand(info, &mut arr).map(|_| arr).ok()
186     }
187 }
188 
189 /// Expand a legacy salt into the expanded salt used with XOR padding in LDT.
190 #[allow(clippy::expect_used)]
legacy_ldt_expanded_salt<const B: usize, C: CryptoProvider>(salt: &[u8; 2]) -> [u8; B]191 pub fn legacy_ldt_expanded_salt<const B: usize, C: CryptoProvider>(salt: &[u8; 2]) -> [u8; B] {
192     simple_np_hkdf_expand::<B, C>(salt, b"Legacy LDT salt pad")
193         // the padded salt is the tweak size of a tweakable block cipher, which shouldn't be
194         // anywhere close to the max HKDF size (255 * 32)
195         .expect("Tweak size is a valid HKDF size")
196 }
197 
198 /// Expand a legacy (short) raw metadata key into an AES128 key.
199 #[allow(clippy::expect_used)]
legacy_metadata_expanded_key<C: CryptoProvider>(raw_metadata_key: &[u8; 14]) -> [u8; 16]200 pub fn legacy_metadata_expanded_key<C: CryptoProvider>(raw_metadata_key: &[u8; 14]) -> [u8; 16] {
201     simple_np_hkdf_expand::<16, C>(raw_metadata_key, b"Legacy metadata key expansion")
202         .expect("AES128 key is a valid HKDF size")
203 }
204 
205 /// Build an HKDF using the NP HKDF salt, calculate output, and discard the HKDF.
206 /// If using the NP key seed as IKM, see [NpKeySeedHkdf] instead.
207 ///
208 /// 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]>209 fn simple_np_hkdf_expand<const N: usize, C: CryptoProvider>(
210     ikm: &[u8],
211     info: &[u8],
212 ) -> Option<[u8; N]> {
213     let mut buf = [0; N];
214     let hkdf = np_hkdf::<C>(ikm);
215     hkdf.expand(info, &mut buf[..]).map(|_| buf).ok()
216 }
217 
218 /// Build an HKDF using the NP HKDF salt and supplied ikm
np_hkdf<C: CryptoProvider>(ikm: &[u8]) -> C::HkdfSha256219 fn np_hkdf<C: CryptoProvider>(ikm: &[u8]) -> C::HkdfSha256 {
220     C::HkdfSha256::new(Some(NP_HKDF_SALT), ikm)
221 }
222