1 //! Utilities for handling legacy KeyMaster/KeyMint key blobs.
2
3 use crate::tag::legacy::{consume_i32, consume_u32, consume_u8, consume_vec};
4 use crate::{
5 crypto, get_opt_tag_value, km_err, try_to_vec, vec_try_with_capacity, Error, FallibleAllocExt,
6 };
7 use alloc::vec::Vec;
8 use core::mem::size_of;
9 use kmr_wire::keymint::KeyParam;
10
11 #[cfg(test)]
12 mod tests;
13
14 /// Key blob version.
15 const KEY_BLOB_VERSION: u8 = 0;
16
17 /// Hard-coded HMAC key used for keyblob authentication.
18 const HMAC_KEY: &[u8] = b"IntegrityAssuredBlob0\0";
19
20 /// Format of encrypted key blob.
21 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
22 pub enum AuthEncryptedBlobFormat {
23 AesOcb = 0,
24 AesGcmWithSwEnforced = 1,
25 AesGcmWithSecureDeletion = 2,
26 AesGcmWithSwEnforcedVersioned = 3,
27 AesGcmWithSecureDeletionVersioned = 4,
28 }
29
30 impl AuthEncryptedBlobFormat {
requires_secure_deletion(&self) -> bool31 pub fn requires_secure_deletion(&self) -> bool {
32 matches!(self, Self::AesGcmWithSecureDeletion | Self::AesGcmWithSecureDeletionVersioned)
33 }
is_versioned(&self) -> bool34 pub fn is_versioned(&self) -> bool {
35 matches!(
36 self,
37 Self::AesGcmWithSwEnforcedVersioned | Self::AesGcmWithSecureDeletionVersioned
38 )
39 }
40 }
41
42 /// Encrypted key blob, including key characteristics.
43 #[derive(Debug, PartialEq, Eq)]
44 pub struct EncryptedKeyBlob {
45 pub format: AuthEncryptedBlobFormat,
46 // IV for encryption.
47 pub nonce: Vec<u8>,
48 // Encrypted key material.
49 pub ciphertext: Vec<u8>,
50 // Authenticated encryption tag.
51 pub tag: Vec<u8>,
52
53 // The following two fields are preset iff `format.is_versioned()`
54 pub kdf_version: Option<u32>,
55 pub addl_info: Option<i32>,
56
57 pub hw_enforced: Vec<KeyParam>,
58 pub sw_enforced: Vec<KeyParam>,
59 pub key_slot: Option<u32>,
60 }
61
62 impl EncryptedKeyBlob {
63 /// Serialize an [`EncryptedKeyBlob`].
serialize(&self) -> Result<Vec<u8>, Error>64 pub fn serialize(&self) -> Result<Vec<u8>, Error> {
65 let hw_enforced_data = crate::tag::legacy::serialize(&self.hw_enforced)?;
66 let sw_enforced_data = crate::tag::legacy::serialize(&self.sw_enforced)?;
67 let mut result = vec_try_with_capacity!(
68 size_of::<u8>()
69 + size_of::<u32>()
70 + self.nonce.len()
71 + size_of::<u32>()
72 + self.ciphertext.len()
73 + size_of::<u32>()
74 + self.tag.len()
75 + hw_enforced_data.len()
76 + sw_enforced_data.len()
77 + size_of::<u32>()
78 )?;
79 result.push(self.format as u8);
80 result.extend_from_slice(&(self.nonce.len() as u32).to_ne_bytes());
81 result.extend_from_slice(&self.nonce);
82 result.extend_from_slice(&(self.ciphertext.len() as u32).to_ne_bytes());
83 result.extend_from_slice(&self.ciphertext);
84 result.extend_from_slice(&(self.tag.len() as u32).to_ne_bytes());
85 result.extend_from_slice(&self.tag);
86 if self.format.is_versioned() {
87 let kdf_version = self.kdf_version.ok_or_else(|| {
88 km_err!(UnknownError, "keyblob of format {:?} missing kdf_version", self.format)
89 })?;
90 let addl_info = self.addl_info.ok_or_else(|| {
91 km_err!(UnknownError, "keyblob of format {:?} missing addl_info", self.format)
92 })? as u32;
93 result.extend_from_slice(&kdf_version.to_ne_bytes());
94 result.extend_from_slice(&addl_info.to_ne_bytes());
95 }
96 result.extend_from_slice(&hw_enforced_data);
97 result.extend_from_slice(&sw_enforced_data);
98 if let Some(slot) = self.key_slot {
99 result.extend_from_slice(&slot.to_ne_bytes());
100 }
101 Ok(result)
102 }
103
104 /// Parse a serialized [`KeyBlob`].
deserialize(mut data: &[u8]) -> Result<Self, Error>105 pub fn deserialize(mut data: &[u8]) -> Result<Self, Error> {
106 let format = match consume_u8(&mut data)? {
107 x if x == AuthEncryptedBlobFormat::AesOcb as u8 => AuthEncryptedBlobFormat::AesOcb,
108 x if x == AuthEncryptedBlobFormat::AesGcmWithSwEnforced as u8 => {
109 AuthEncryptedBlobFormat::AesGcmWithSwEnforced
110 }
111 x if x == AuthEncryptedBlobFormat::AesGcmWithSecureDeletion as u8 => {
112 AuthEncryptedBlobFormat::AesGcmWithSecureDeletion
113 }
114 x if x == AuthEncryptedBlobFormat::AesGcmWithSwEnforcedVersioned as u8 => {
115 AuthEncryptedBlobFormat::AesGcmWithSwEnforcedVersioned
116 }
117 x if x == AuthEncryptedBlobFormat::AesGcmWithSecureDeletionVersioned as u8 => {
118 AuthEncryptedBlobFormat::AesGcmWithSecureDeletionVersioned
119 }
120 x => return Err(km_err!(InvalidKeyBlob, "unexpected blob format {}", x)),
121 };
122
123 let nonce = consume_vec(&mut data)?;
124 let ciphertext = consume_vec(&mut data)?;
125 let tag = consume_vec(&mut data)?;
126 let mut kdf_version = None;
127 let mut addl_info = None;
128 if format.is_versioned() {
129 kdf_version = Some(consume_u32(&mut data)?);
130 addl_info = Some(consume_i32(&mut data)?);
131 }
132 let hw_enforced = crate::tag::legacy::deserialize(&mut data)?;
133 let sw_enforced = crate::tag::legacy::deserialize(&mut data)?;
134
135 let key_slot = match data.len() {
136 0 => None,
137 4 => Some(consume_u32(&mut data)?),
138 _ => return Err(km_err!(InvalidKeyBlob, "unexpected remaining length {}", data.len())),
139 };
140
141 Ok(EncryptedKeyBlob {
142 format,
143 nonce,
144 ciphertext,
145 tag,
146 kdf_version,
147 addl_info,
148 hw_enforced,
149 sw_enforced,
150 key_slot,
151 })
152 }
153 }
154
155 /// Plaintext key blob, with key characteristics.
156 #[derive(Debug, PartialEq, Eq)]
157 pub struct KeyBlob {
158 pub key_material: Vec<u8>,
159 pub hw_enforced: Vec<KeyParam>,
160 pub sw_enforced: Vec<KeyParam>,
161 }
162
163 impl KeyBlob {
164 /// Size (in bytes) of appended MAC.
165 pub const MAC_LEN: usize = 8;
166
167 /// Serialize a [`KeyBlob`].
serialize<H: crypto::Hmac>( &self, hmac: &H, hidden: &[KeyParam], ) -> Result<Vec<u8>, crate::Error>168 pub fn serialize<H: crypto::Hmac>(
169 &self,
170 hmac: &H,
171 hidden: &[KeyParam],
172 ) -> Result<Vec<u8>, crate::Error> {
173 let hw_enforced_data = crate::tag::legacy::serialize(&self.hw_enforced)?;
174 let sw_enforced_data = crate::tag::legacy::serialize(&self.sw_enforced)?;
175 let mut result = vec_try_with_capacity!(
176 size_of::<u8>()
177 + size_of::<u32>()
178 + self.key_material.len()
179 + hw_enforced_data.len()
180 + sw_enforced_data.len()
181 )?;
182 result.push(KEY_BLOB_VERSION);
183 result.extend_from_slice(&(self.key_material.len() as u32).to_ne_bytes());
184 result.extend_from_slice(&self.key_material);
185 result.extend_from_slice(&hw_enforced_data);
186 result.extend_from_slice(&sw_enforced_data);
187 let mac = Self::compute_hmac(hmac, &result, hidden)?;
188 result.extend_from_slice(&mac);
189 Ok(result)
190 }
191
192 /// Parse a serialized [`KeyBlob`].
deserialize<E: crypto::ConstTimeEq, H: crypto::Hmac>( hmac: &H, mut data: &[u8], hidden: &[KeyParam], comparator: E, ) -> Result<Self, Error>193 pub fn deserialize<E: crypto::ConstTimeEq, H: crypto::Hmac>(
194 hmac: &H,
195 mut data: &[u8],
196 hidden: &[KeyParam],
197 comparator: E,
198 ) -> Result<Self, Error> {
199 if data.len() < (Self::MAC_LEN + 4 + 4 + 4) {
200 return Err(km_err!(InvalidKeyBlob, "blob not long enough (len = {})", data.len()));
201 }
202
203 // Check the HMAC in the last 8 bytes before doing anything else.
204 let mac = &data[data.len() - Self::MAC_LEN..];
205 let computed_mac = Self::compute_hmac(hmac, &data[..data.len() - Self::MAC_LEN], hidden)?;
206 if comparator.ne(mac, &computed_mac) {
207 return Err(km_err!(InvalidKeyBlob, "invalid key blob"));
208 }
209
210 let version = consume_u8(&mut data)?;
211 if version != KEY_BLOB_VERSION {
212 return Err(km_err!(InvalidKeyBlob, "unexpected blob version {}", version));
213 }
214 let key_material = consume_vec(&mut data)?;
215 let hw_enforced = crate::tag::legacy::deserialize(&mut data)?;
216 let sw_enforced = crate::tag::legacy::deserialize(&mut data)?;
217
218 // Should just be the (already-checked) MAC left.
219 let rest = &data[Self::MAC_LEN..];
220 if !rest.is_empty() {
221 return Err(km_err!(InvalidKeyBlob, "extra data (len {})", rest.len()));
222 }
223 Ok(KeyBlob { key_material, hw_enforced, sw_enforced })
224 }
225
226 /// Compute the authentication HMAC for a KeyBlob. This is built as:
227 /// HMAC-SHA256(HK, data || serialize(hidden))
228 /// with HK = b"IntegrityAssuredBlob0\0".
compute_hmac<H: crypto::Hmac>( hmac: &H, data: &[u8], hidden: &[KeyParam], ) -> Result<Vec<u8>, crate::Error>229 pub fn compute_hmac<H: crypto::Hmac>(
230 hmac: &H,
231 data: &[u8],
232 hidden: &[KeyParam],
233 ) -> Result<Vec<u8>, crate::Error> {
234 let hidden_data = crate::tag::legacy::serialize(hidden)?;
235 let mut op = hmac.begin(
236 crypto::hmac::Key(try_to_vec(HMAC_KEY)?).into(),
237 kmr_wire::keymint::Digest::Sha256,
238 )?;
239 op.update(data)?;
240 op.update(&hidden_data)?;
241 let mut tag = op.finish()?;
242 tag.truncate(Self::MAC_LEN);
243 Ok(tag)
244 }
245 }
246
247 /// Build the parameters that are used as the hidden input to HMAC calculations:
248 /// - `ApplicationId(data)` if present
249 /// - `ApplicationData(data)` if present
250 /// - (repeated) `RootOfTrust(rot)` where `rot` is a hardcoded root of trust (expected to
251 /// be the CBOR serialization of a `RootOfTrustInfo` instance).
hidden(params: &[KeyParam], rots: &[&[u8]]) -> Result<Vec<KeyParam>, Error>252 pub fn hidden(params: &[KeyParam], rots: &[&[u8]]) -> Result<Vec<KeyParam>, Error> {
253 let mut results = Vec::new();
254 if let Ok(Some(app_id)) = get_opt_tag_value!(params, ApplicationId) {
255 results.try_push(KeyParam::ApplicationId(try_to_vec(app_id)?))?;
256 }
257 if let Ok(Some(app_data)) = get_opt_tag_value!(params, ApplicationData) {
258 results.try_push(KeyParam::ApplicationData(try_to_vec(app_data)?))?;
259 }
260 for rot in rots {
261 results.try_push(KeyParam::RootOfTrust(try_to_vec(rot)?))?;
262 }
263 Ok(results)
264 }
265