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