David Drysdale | c0ed986 | 2023-07-05 07:11:39 +0100 | [diff] [blame] | 1 | // Copyright 2023, The Android Open Source Project |
| 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 | //! Code for parsing software-backed keyblobs, as emitted by the C++ reference implementation of |
| 16 | //! KeyMint. |
| 17 | |
David Drysdale | c0ed986 | 2023-07-05 07:11:39 +0100 | [diff] [blame] | 18 | use crate::error::Error; |
| 19 | use crate::ks_err; |
| 20 | use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{ |
| 21 | Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve, |
| 22 | ErrorCode::ErrorCode, HardwareAuthenticatorType::HardwareAuthenticatorType, |
| 23 | KeyFormat::KeyFormat, KeyOrigin::KeyOrigin, KeyParameter::KeyParameter, |
| 24 | KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, |
| 25 | Tag::Tag, TagType::TagType, |
| 26 | }; |
| 27 | use anyhow::Result; |
| 28 | use keystore2_crypto::hmac_sha256; |
| 29 | use std::mem::size_of; |
| 30 | |
David Drysdale | 2566fb3 | 2024-07-09 14:46:37 +0100 | [diff] [blame] | 31 | #[cfg(test)] |
| 32 | mod tests; |
| 33 | |
David Drysdale | c0ed986 | 2023-07-05 07:11:39 +0100 | [diff] [blame] | 34 | /// Root of trust value. |
| 35 | const SOFTWARE_ROOT_OF_TRUST: &[u8] = b"SW"; |
| 36 | |
| 37 | /// Error macro. |
| 38 | macro_rules! bloberr { |
| 39 | { $($arg:tt)+ } => { |
| 40 | anyhow::Error::new(Error::Km(ErrorCode::INVALID_KEY_BLOB)).context(ks_err!($($arg)+)) |
| 41 | }; |
| 42 | } |
| 43 | |
| 44 | /// Get the `KeyParameterValue` associated with a tag from a collection of `KeyParameter`s. |
| 45 | fn get_tag_value(params: &[KeyParameter], tag: Tag) -> Option<&KeyParameterValue> { |
| 46 | params.iter().find_map(|kp| if kp.tag == tag { Some(&kp.value) } else { None }) |
| 47 | } |
| 48 | |
| 49 | /// Get the [`TagType`] for a [`Tag`]. |
| 50 | fn tag_type(tag: &Tag) -> TagType { |
| 51 | TagType((tag.0 as u32 & 0xf0000000) as i32) |
| 52 | } |
| 53 | |
| 54 | /// Extract key material and combined key characteristics from a legacy authenticated keyblob. |
| 55 | pub fn export_key( |
| 56 | data: &[u8], |
| 57 | params: &[KeyParameter], |
| 58 | ) -> Result<(KeyFormat, Vec<u8>, Vec<KeyParameter>)> { |
| 59 | let hidden = hidden_params(params, &[SOFTWARE_ROOT_OF_TRUST]); |
| 60 | let KeyBlob { key_material, hw_enforced, sw_enforced } = |
| 61 | KeyBlob::new_from_serialized(data, &hidden)?; |
| 62 | |
| 63 | let mut combined = hw_enforced; |
| 64 | combined.extend_from_slice(&sw_enforced); |
| 65 | |
| 66 | let algo_val = |
| 67 | get_tag_value(&combined, Tag::ALGORITHM).ok_or_else(|| bloberr!("No algorithm found!"))?; |
| 68 | |
| 69 | let format = match algo_val { |
| 70 | KeyParameterValue::Algorithm(Algorithm::AES) |
| 71 | | KeyParameterValue::Algorithm(Algorithm::TRIPLE_DES) |
| 72 | | KeyParameterValue::Algorithm(Algorithm::HMAC) => KeyFormat::RAW, |
| 73 | KeyParameterValue::Algorithm(Algorithm::RSA) |
| 74 | | KeyParameterValue::Algorithm(Algorithm::EC) => KeyFormat::PKCS8, |
| 75 | _ => return Err(bloberr!("Unexpected algorithm {:?}", algo_val)), |
| 76 | }; |
David Drysdale | 746e1be | 2023-07-05 17:39:57 +0100 | [diff] [blame] | 77 | |
| 78 | let key_material = match (format, algo_val) { |
| 79 | (KeyFormat::PKCS8, KeyParameterValue::Algorithm(Algorithm::EC)) => { |
| 80 | // Key material format depends on the curve. |
| 81 | let curve = get_tag_value(&combined, Tag::EC_CURVE) |
| 82 | .ok_or_else(|| bloberr!("Failed to determine curve for EC key!"))?; |
| 83 | match curve { |
| 84 | KeyParameterValue::EcCurve(EcCurve::CURVE_25519) => key_material, |
| 85 | KeyParameterValue::EcCurve(EcCurve::P_224) => { |
| 86 | pkcs8_wrap_nist_key(&key_material, EcCurve::P_224)? |
| 87 | } |
| 88 | KeyParameterValue::EcCurve(EcCurve::P_256) => { |
| 89 | pkcs8_wrap_nist_key(&key_material, EcCurve::P_256)? |
| 90 | } |
| 91 | KeyParameterValue::EcCurve(EcCurve::P_384) => { |
| 92 | pkcs8_wrap_nist_key(&key_material, EcCurve::P_384)? |
| 93 | } |
| 94 | KeyParameterValue::EcCurve(EcCurve::P_521) => { |
| 95 | pkcs8_wrap_nist_key(&key_material, EcCurve::P_521)? |
| 96 | } |
| 97 | _ => { |
| 98 | return Err(bloberr!("Unexpected EC curve {curve:?}")); |
| 99 | } |
| 100 | } |
| 101 | } |
| 102 | (KeyFormat::RAW, _) => key_material, |
| 103 | (format, algo) => { |
| 104 | return Err(bloberr!( |
| 105 | "Unsupported combination of {format:?} format for {algo:?} algorithm" |
| 106 | )); |
| 107 | } |
| 108 | }; |
David Drysdale | c0ed986 | 2023-07-05 07:11:39 +0100 | [diff] [blame] | 109 | Ok((format, key_material, combined)) |
| 110 | } |
| 111 | |
David Drysdale | 746e1be | 2023-07-05 17:39:57 +0100 | [diff] [blame] | 112 | /// DER-encoded `AlgorithmIdentifier` for a P-224 key. |
| 113 | const DER_ALGORITHM_ID_P224: &[u8] = &[ |
| 114 | 0x30, 0x10, // SEQUENCE (AlgorithmIdentifier) { |
| 115 | 0x06, 0x07, // OBJECT IDENTIFIER (algorithm) |
| 116 | 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, // 1.2.840.10045.2.1 (ecPublicKey) |
| 117 | 0x06, 0x05, // OBJECT IDENTIFIER (param) |
| 118 | 0x2b, 0x81, 0x04, 0x00, 0x21, // 1.3.132.0.33 (secp224r1) } |
| 119 | ]; |
| 120 | |
| 121 | /// DER-encoded `AlgorithmIdentifier` for a P-256 key. |
| 122 | const DER_ALGORITHM_ID_P256: &[u8] = &[ |
| 123 | 0x30, 0x13, // SEQUENCE (AlgorithmIdentifier) { |
| 124 | 0x06, 0x07, // OBJECT IDENTIFIER (algorithm) |
| 125 | 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, // 1.2.840.10045.2.1 (ecPublicKey) |
| 126 | 0x06, 0x08, // OBJECT IDENTIFIER (param) |
| 127 | 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, // 1.2.840.10045.3.1.7 (secp256r1) } |
| 128 | ]; |
| 129 | |
| 130 | /// DER-encoded `AlgorithmIdentifier` for a P-384 key. |
| 131 | const DER_ALGORITHM_ID_P384: &[u8] = &[ |
| 132 | 0x30, 0x10, // SEQUENCE (AlgorithmIdentifier) { |
| 133 | 0x06, 0x07, // OBJECT IDENTIFIER (algorithm) |
| 134 | 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, // 1.2.840.10045.2.1 (ecPublicKey) |
| 135 | 0x06, 0x05, // OBJECT IDENTIFIER (param) |
| 136 | 0x2b, 0x81, 0x04, 0x00, 0x22, // 1.3.132.0.34 (secp384r1) } |
| 137 | ]; |
| 138 | |
| 139 | /// DER-encoded `AlgorithmIdentifier` for a P-384 key. |
| 140 | const DER_ALGORITHM_ID_P521: &[u8] = &[ |
| 141 | 0x30, 0x10, // SEQUENCE (AlgorithmIdentifier) { |
| 142 | 0x06, 0x07, // OBJECT IDENTIFIER (algorithm) |
| 143 | 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, // 1.2.840.10045.2.1 (ecPublicKey) |
| 144 | 0x06, 0x05, // OBJECT IDENTIFIER (param) |
| 145 | 0x2b, 0x81, 0x04, 0x00, 0x23, // 1.3.132.0.35 (secp521r1) } |
| 146 | ]; |
| 147 | |
| 148 | /// DER-encoded integer value zero. |
| 149 | const DER_VERSION_0: &[u8] = &[ |
| 150 | 0x02, // INTEGER |
| 151 | 0x01, // len |
| 152 | 0x00, // value 0 |
| 153 | ]; |
| 154 | |
| 155 | /// Given a NIST curve EC key in the form of a DER-encoded `ECPrivateKey` |
| 156 | /// (RFC 5915 s3), wrap it in a DER-encoded PKCS#8 format (RFC 5208 s5). |
| 157 | fn pkcs8_wrap_nist_key(nist_key: &[u8], curve: EcCurve) -> Result<Vec<u8>> { |
| 158 | let der_alg_id = match curve { |
| 159 | EcCurve::P_224 => DER_ALGORITHM_ID_P224, |
| 160 | EcCurve::P_256 => DER_ALGORITHM_ID_P256, |
| 161 | EcCurve::P_384 => DER_ALGORITHM_ID_P384, |
| 162 | EcCurve::P_521 => DER_ALGORITHM_ID_P521, |
| 163 | _ => return Err(bloberr!("unknown curve {curve:?}")), |
| 164 | }; |
| 165 | |
| 166 | // Output format is: |
| 167 | // |
| 168 | // PrivateKeyInfo ::= SEQUENCE { |
| 169 | // version INTEGER, |
| 170 | // privateKeyAlgorithm AlgorithmIdentifier, |
| 171 | // privateKey OCTET STRING, |
| 172 | // } |
| 173 | // |
| 174 | // Start by building the OCTET STRING so we know its length. |
| 175 | let mut nist_key_octet_string = Vec::new(); |
| 176 | nist_key_octet_string.push(0x04); // OCTET STRING |
| 177 | add_der_len(&mut nist_key_octet_string, nist_key.len())?; |
| 178 | nist_key_octet_string.extend_from_slice(nist_key); |
| 179 | |
| 180 | let mut buf = Vec::new(); |
| 181 | buf.push(0x30); // SEQUENCE |
| 182 | add_der_len(&mut buf, DER_VERSION_0.len() + der_alg_id.len() + nist_key_octet_string.len())?; |
| 183 | buf.extend_from_slice(DER_VERSION_0); |
| 184 | buf.extend_from_slice(der_alg_id); |
| 185 | buf.extend_from_slice(&nist_key_octet_string); |
| 186 | Ok(buf) |
| 187 | } |
| 188 | |
| 189 | /// Append a DER-encoded length value to the given buffer. |
| 190 | fn add_der_len(buf: &mut Vec<u8>, len: usize) -> Result<()> { |
| 191 | if len <= 0x7f { |
| 192 | buf.push(len as u8) |
| 193 | } else if len <= 0xff { |
| 194 | buf.push(0x81); // One length octet to come |
| 195 | buf.push(len as u8); |
| 196 | } else if len <= 0xffff { |
| 197 | buf.push(0x82); // Two length octets to come |
| 198 | buf.push((len >> 8) as u8); |
| 199 | buf.push((len & 0xff) as u8); |
| 200 | } else { |
| 201 | return Err(bloberr!("Unsupported DER length {len}")); |
| 202 | } |
| 203 | Ok(()) |
| 204 | } |
| 205 | |
David Drysdale | c0ed986 | 2023-07-05 07:11:39 +0100 | [diff] [blame] | 206 | /// Plaintext key blob, with key characteristics. |
| 207 | #[derive(PartialEq, Eq)] |
| 208 | struct KeyBlob { |
| 209 | /// Raw key material. |
| 210 | key_material: Vec<u8>, |
| 211 | /// Hardware-enforced key characteristics. |
| 212 | hw_enforced: Vec<KeyParameter>, |
| 213 | /// Software-enforced key characteristics. |
| 214 | sw_enforced: Vec<KeyParameter>, |
| 215 | } |
| 216 | |
| 217 | impl KeyBlob { |
| 218 | /// Key blob version. |
| 219 | const KEY_BLOB_VERSION: u8 = 0; |
| 220 | |
| 221 | /// Hard-coded HMAC key used for keyblob authentication. |
Chris Wailes | 2823433 | 2024-01-16 13:36:50 -0800 | [diff] [blame] | 222 | const LEGACY_HMAC_KEY: &'static [u8] = b"IntegrityAssuredBlob0\0"; |
David Drysdale | c0ed986 | 2023-07-05 07:11:39 +0100 | [diff] [blame] | 223 | |
| 224 | /// Size (in bytes) of appended MAC. |
| 225 | const MAC_LEN: usize = 8; |
| 226 | |
| 227 | /// Parse a serialized [`KeyBlob`]. |
| 228 | fn new_from_serialized(mut data: &[u8], hidden: &[KeyParameter]) -> Result<Self> { |
| 229 | // Keyblob needs to be at least long enough for: |
| 230 | // - version byte, |
| 231 | // - 4-byte len for key material |
| 232 | // - 4-byte len for hw_enforced params |
| 233 | // - 4-byte len for sw_enforced params |
| 234 | // - MAC tag. |
| 235 | if data.len() < (1 + 3 * size_of::<u32>() + Self::MAC_LEN) { |
| 236 | return Err(bloberr!("blob not long enough (len = {})", data.len())); |
| 237 | } |
| 238 | |
| 239 | // Check the HMAC in the last 8 bytes before doing anything else. |
| 240 | let mac = &data[data.len() - Self::MAC_LEN..]; |
| 241 | let computed_mac = Self::compute_hmac(&data[..data.len() - Self::MAC_LEN], hidden)?; |
| 242 | if mac != computed_mac { |
| 243 | return Err(bloberr!("invalid key blob")); |
| 244 | } |
| 245 | |
| 246 | let version = consume_u8(&mut data)?; |
| 247 | if version != Self::KEY_BLOB_VERSION { |
| 248 | return Err(bloberr!("unexpected blob version {}", version)); |
| 249 | } |
| 250 | let key_material = consume_vec(&mut data)?; |
| 251 | let hw_enforced = deserialize_params(&mut data)?; |
| 252 | let sw_enforced = deserialize_params(&mut data)?; |
| 253 | |
| 254 | // Should just be the (already-checked) MAC left. |
| 255 | let rest = &data[Self::MAC_LEN..]; |
| 256 | if !rest.is_empty() { |
| 257 | return Err(bloberr!("extra data (len {})", rest.len())); |
| 258 | } |
| 259 | Ok(KeyBlob { key_material, hw_enforced, sw_enforced }) |
| 260 | } |
| 261 | |
| 262 | /// Compute the authentication HMAC for a KeyBlob. This is built as: |
| 263 | /// HMAC-SHA256(HK, data || serialize(hidden)) |
| 264 | /// with HK = b"IntegrityAssuredBlob0\0". |
| 265 | fn compute_hmac(data: &[u8], hidden: &[KeyParameter]) -> Result<Vec<u8>> { |
| 266 | let hidden_data = serialize_params(hidden)?; |
| 267 | let mut combined = data.to_vec(); |
| 268 | combined.extend_from_slice(&hidden_data); |
| 269 | let mut tag = hmac_sha256(Self::LEGACY_HMAC_KEY, &combined)?; |
| 270 | tag.truncate(Self::MAC_LEN); |
| 271 | Ok(tag) |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | /// Build the parameters that are used as the hidden input to HMAC calculations: |
| 276 | /// - `ApplicationId(data)` if present |
| 277 | /// - `ApplicationData(data)` if present |
| 278 | /// - (repeated) `RootOfTrust(rot)` where `rot` is a hardcoded piece of root of trust information. |
| 279 | fn hidden_params(params: &[KeyParameter], rots: &[&[u8]]) -> Vec<KeyParameter> { |
| 280 | let mut results = Vec::new(); |
| 281 | if let Some(app_id) = get_tag_value(params, Tag::APPLICATION_ID) { |
| 282 | results.push(KeyParameter { tag: Tag::APPLICATION_ID, value: app_id.clone() }); |
| 283 | } |
| 284 | if let Some(app_data) = get_tag_value(params, Tag::APPLICATION_DATA) { |
| 285 | results.push(KeyParameter { tag: Tag::APPLICATION_DATA, value: app_data.clone() }); |
| 286 | } |
| 287 | for rot in rots { |
| 288 | results.push(KeyParameter { |
| 289 | tag: Tag::ROOT_OF_TRUST, |
| 290 | value: KeyParameterValue::Blob(rot.to_vec()), |
| 291 | }); |
| 292 | } |
| 293 | results |
| 294 | } |
| 295 | |
| 296 | /// Retrieve a `u8` from the start of the given slice, if possible. |
| 297 | fn consume_u8(data: &mut &[u8]) -> Result<u8> { |
| 298 | match data.first() { |
| 299 | Some(b) => { |
| 300 | *data = &(*data)[1..]; |
| 301 | Ok(*b) |
| 302 | } |
| 303 | None => Err(bloberr!("failed to find 1 byte")), |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | /// Move past a bool value from the start of the given slice, if possible. |
| 308 | /// Bool values should only be included if `true`, so fail if the value |
| 309 | /// is anything other than 1. |
| 310 | fn consume_bool(data: &mut &[u8]) -> Result<bool> { |
| 311 | let b = consume_u8(data)?; |
| 312 | if b == 0x01 { |
| 313 | Ok(true) |
| 314 | } else { |
| 315 | Err(bloberr!("bool value other than 1 encountered")) |
| 316 | } |
| 317 | } |
| 318 | |
| 319 | /// Retrieve a (host-ordered) `u32` from the start of the given slice, if possible. |
| 320 | fn consume_u32(data: &mut &[u8]) -> Result<u32> { |
| 321 | const LEN: usize = size_of::<u32>(); |
| 322 | if data.len() < LEN { |
| 323 | return Err(bloberr!("failed to find {LEN} bytes")); |
| 324 | } |
| 325 | let chunk: [u8; LEN] = data[..LEN].try_into().unwrap(); // safe: just checked |
| 326 | *data = &(*data)[LEN..]; |
| 327 | Ok(u32::from_ne_bytes(chunk)) |
| 328 | } |
| 329 | |
| 330 | /// Retrieve a (host-ordered) `i32` from the start of the given slice, if possible. |
| 331 | fn consume_i32(data: &mut &[u8]) -> Result<i32> { |
| 332 | const LEN: usize = size_of::<i32>(); |
| 333 | if data.len() < LEN { |
| 334 | return Err(bloberr!("failed to find {LEN} bytes")); |
| 335 | } |
| 336 | let chunk: [u8; LEN] = data[..LEN].try_into().unwrap(); // safe: just checked |
| 337 | *data = &(*data)[4..]; |
| 338 | Ok(i32::from_ne_bytes(chunk)) |
| 339 | } |
| 340 | |
| 341 | /// Retrieve a (host-ordered) `i64` from the start of the given slice, if possible. |
| 342 | fn consume_i64(data: &mut &[u8]) -> Result<i64> { |
| 343 | const LEN: usize = size_of::<i64>(); |
| 344 | if data.len() < LEN { |
| 345 | return Err(bloberr!("failed to find {LEN} bytes")); |
| 346 | } |
| 347 | let chunk: [u8; LEN] = data[..LEN].try_into().unwrap(); // safe: just checked |
| 348 | *data = &(*data)[LEN..]; |
| 349 | Ok(i64::from_ne_bytes(chunk)) |
| 350 | } |
| 351 | |
| 352 | /// Retrieve a vector of bytes from the start of the given slice, if possible, |
| 353 | /// with the length of the data expected to appear as a host-ordered `u32` prefix. |
| 354 | fn consume_vec(data: &mut &[u8]) -> Result<Vec<u8>> { |
| 355 | let len = consume_u32(data)? as usize; |
| 356 | if len > data.len() { |
| 357 | return Err(bloberr!("failed to find {} bytes", len)); |
| 358 | } |
| 359 | let result = data[..len].to_vec(); |
| 360 | *data = &(*data)[len..]; |
| 361 | Ok(result) |
| 362 | } |
| 363 | |
| 364 | /// Retrieve the contents of a tag of `TagType::Bytes`. The `data` parameter holds |
| 365 | /// the as-yet unparsed data, and a length and offset are read from this (and consumed). |
| 366 | /// This length and offset refer to a location in the combined `blob_data`; however, |
| 367 | /// the offset is expected to be the next unconsumed chunk of `blob_data`, as indicated |
| 368 | /// by `next_blob_offset` (which itself is updated as a result of consuming the data). |
| 369 | fn consume_blob( |
| 370 | data: &mut &[u8], |
| 371 | next_blob_offset: &mut usize, |
| 372 | blob_data: &[u8], |
| 373 | ) -> Result<Vec<u8>> { |
| 374 | let data_len = consume_u32(data)? as usize; |
| 375 | let data_offset = consume_u32(data)? as usize; |
| 376 | // Expect the blob data to come from the next offset in the initial blob chunk. |
| 377 | if data_offset != *next_blob_offset { |
| 378 | return Err(bloberr!("got blob offset {} instead of {}", data_offset, next_blob_offset)); |
| 379 | } |
| 380 | if (data_offset + data_len) > blob_data.len() { |
| 381 | return Err(bloberr!( |
| 382 | "blob at offset [{}..{}+{}] goes beyond blob data size {}", |
| 383 | data_offset, |
| 384 | data_offset, |
| 385 | data_len, |
| 386 | blob_data.len(), |
| 387 | )); |
| 388 | } |
| 389 | |
| 390 | let slice = &blob_data[data_offset..data_offset + data_len]; |
| 391 | *next_blob_offset += data_len; |
| 392 | Ok(slice.to_vec()) |
| 393 | } |
| 394 | |
| 395 | /// Deserialize a collection of [`KeyParam`]s in legacy serialized format. The provided slice is |
| 396 | /// modified to contain the unconsumed part of the data. |
| 397 | fn deserialize_params(data: &mut &[u8]) -> Result<Vec<KeyParameter>> { |
| 398 | let blob_data_size = consume_u32(data)? as usize; |
| 399 | if blob_data_size > data.len() { |
| 400 | return Err(bloberr!( |
| 401 | "blob data size {} bigger than data (len={})", |
| 402 | blob_data_size, |
| 403 | data.len() |
| 404 | )); |
| 405 | } |
| 406 | |
| 407 | let blob_data = &data[..blob_data_size]; |
| 408 | let mut next_blob_offset = 0; |
| 409 | |
| 410 | // Move past the blob data. |
| 411 | *data = &data[blob_data_size..]; |
| 412 | |
| 413 | let param_count = consume_u32(data)? as usize; |
| 414 | let param_size = consume_u32(data)? as usize; |
| 415 | if param_size > data.len() { |
| 416 | return Err(bloberr!( |
| 417 | "size mismatch 4+{}+4+4+{} > {}", |
| 418 | blob_data_size, |
| 419 | param_size, |
| 420 | data.len() |
| 421 | )); |
| 422 | } |
| 423 | |
| 424 | let mut results = Vec::new(); |
| 425 | for _i in 0..param_count { |
| 426 | let tag_num = consume_u32(data)? as i32; |
| 427 | let tag = Tag(tag_num); |
| 428 | let value = match tag_type(&tag) { |
| 429 | TagType::INVALID => return Err(bloberr!("invalid tag {:?} encountered", tag)), |
| 430 | TagType::ENUM | TagType::ENUM_REP => { |
| 431 | let val = consume_i32(data)?; |
| 432 | match tag { |
| 433 | Tag::ALGORITHM => KeyParameterValue::Algorithm(Algorithm(val)), |
| 434 | Tag::BLOCK_MODE => KeyParameterValue::BlockMode(BlockMode(val)), |
| 435 | Tag::PADDING => KeyParameterValue::PaddingMode(PaddingMode(val)), |
| 436 | Tag::DIGEST | Tag::RSA_OAEP_MGF_DIGEST => { |
| 437 | KeyParameterValue::Digest(Digest(val)) |
| 438 | } |
| 439 | Tag::EC_CURVE => KeyParameterValue::EcCurve(EcCurve(val)), |
| 440 | Tag::ORIGIN => KeyParameterValue::Origin(KeyOrigin(val)), |
| 441 | Tag::PURPOSE => KeyParameterValue::KeyPurpose(KeyPurpose(val)), |
| 442 | Tag::USER_AUTH_TYPE => { |
| 443 | KeyParameterValue::HardwareAuthenticatorType(HardwareAuthenticatorType(val)) |
| 444 | } |
| 445 | _ => KeyParameterValue::Integer(val), |
| 446 | } |
| 447 | } |
| 448 | TagType::UINT | TagType::UINT_REP => KeyParameterValue::Integer(consume_i32(data)?), |
| 449 | TagType::ULONG | TagType::ULONG_REP => { |
| 450 | KeyParameterValue::LongInteger(consume_i64(data)?) |
| 451 | } |
| 452 | TagType::DATE => KeyParameterValue::DateTime(consume_i64(data)?), |
| 453 | TagType::BOOL => KeyParameterValue::BoolValue(consume_bool(data)?), |
| 454 | TagType::BIGNUM | TagType::BYTES => { |
| 455 | KeyParameterValue::Blob(consume_blob(data, &mut next_blob_offset, blob_data)?) |
| 456 | } |
| 457 | _ => return Err(bloberr!("unexpected tag type for {:?}", tag)), |
| 458 | }; |
| 459 | results.push(KeyParameter { tag, value }); |
| 460 | } |
| 461 | |
| 462 | Ok(results) |
| 463 | } |
| 464 | |
| 465 | /// Serialize a collection of [`KeyParameter`]s into a format that is compatible with previous |
| 466 | /// implementations: |
| 467 | /// |
| 468 | /// ```text |
| 469 | /// [0..4] Size B of `TagType::Bytes` data, in host order. |
| 470 | /// [4..4+B] (*) Concatenated contents of each `TagType::Bytes` tag. |
| 471 | /// [4+B..4+B+4] Count N of the number of parameters, in host order. |
| 472 | /// [8+B..8+B+4] Size Z of encoded parameters. |
| 473 | /// [12+B..12+B+Z] Serialized parameters one after another. |
| 474 | /// ``` |
| 475 | /// |
| 476 | /// Individual parameters are serialized in the last chunk as: |
| 477 | /// |
| 478 | /// ```text |
| 479 | /// [0..4] Tag number, in host order. |
| 480 | /// Followed by one of the following depending on the tag's `TagType`; all integers in host order: |
| 481 | /// [4..5] Bool value (`TagType::Bool`) |
| 482 | /// [4..8] i32 values (`TagType::Uint[Rep]`, `TagType::Enum[Rep]`) |
| 483 | /// [4..12] i64 values, in host order (`TagType::UlongRep`, `TagType::Date`) |
| 484 | /// [4..8] + [8..12] Size + offset of data in (*) above (`TagType::Bytes`, `TagType::Bignum`) |
| 485 | /// ``` |
| 486 | fn serialize_params(params: &[KeyParameter]) -> Result<Vec<u8>> { |
| 487 | // First 4 bytes are the length of the combined [`TagType::Bytes`] data; come back to set that |
| 488 | // in a moment. |
| 489 | let mut result = vec![0; 4]; |
| 490 | |
| 491 | // Next append the contents of all of the [`TagType::Bytes`] data. |
| 492 | let mut blob_size = 0u32; |
| 493 | for param in params { |
| 494 | let tag_type = tag_type(¶m.tag); |
| 495 | if let KeyParameterValue::Blob(v) = ¶m.value { |
| 496 | if tag_type != TagType::BIGNUM && tag_type != TagType::BYTES { |
| 497 | return Err(bloberr!("unexpected tag type for tag {:?} with blob", param.tag)); |
| 498 | } |
| 499 | result.extend_from_slice(v); |
| 500 | blob_size += v.len() as u32; |
| 501 | } |
| 502 | } |
| 503 | // Go back and fill in the combined blob length in native order at the start. |
| 504 | result[..4].clone_from_slice(&blob_size.to_ne_bytes()); |
| 505 | |
| 506 | result.extend_from_slice(&(params.len() as u32).to_ne_bytes()); |
| 507 | |
| 508 | let params_size_offset = result.len(); |
| 509 | result.extend_from_slice(&[0u8; 4]); // placeholder for size of elements |
| 510 | let first_param_offset = result.len(); |
| 511 | let mut blob_offset = 0u32; |
| 512 | for param in params { |
| 513 | result.extend_from_slice(&(param.tag.0 as u32).to_ne_bytes()); |
| 514 | match ¶m.value { |
| 515 | KeyParameterValue::Invalid(_v) => { |
| 516 | return Err(bloberr!("invalid tag found in {:?}", param)) |
| 517 | } |
| 518 | |
| 519 | // Enum-holding variants. |
| 520 | KeyParameterValue::Algorithm(v) => { |
| 521 | result.extend_from_slice(&(v.0 as u32).to_ne_bytes()) |
| 522 | } |
| 523 | KeyParameterValue::BlockMode(v) => { |
| 524 | result.extend_from_slice(&(v.0 as u32).to_ne_bytes()) |
| 525 | } |
| 526 | KeyParameterValue::PaddingMode(v) => { |
| 527 | result.extend_from_slice(&(v.0 as u32).to_ne_bytes()) |
| 528 | } |
| 529 | KeyParameterValue::Digest(v) => result.extend_from_slice(&(v.0 as u32).to_ne_bytes()), |
| 530 | KeyParameterValue::EcCurve(v) => result.extend_from_slice(&(v.0 as u32).to_ne_bytes()), |
| 531 | KeyParameterValue::Origin(v) => result.extend_from_slice(&(v.0 as u32).to_ne_bytes()), |
| 532 | KeyParameterValue::KeyPurpose(v) => { |
| 533 | result.extend_from_slice(&(v.0 as u32).to_ne_bytes()) |
| 534 | } |
| 535 | KeyParameterValue::HardwareAuthenticatorType(v) => { |
| 536 | result.extend_from_slice(&(v.0 as u32).to_ne_bytes()) |
| 537 | } |
| 538 | |
| 539 | // Value-holding variants. |
| 540 | KeyParameterValue::Integer(v) => result.extend_from_slice(&(*v as u32).to_ne_bytes()), |
| 541 | KeyParameterValue::BoolValue(_v) => result.push(0x01u8), |
| 542 | KeyParameterValue::LongInteger(v) | KeyParameterValue::DateTime(v) => { |
| 543 | result.extend_from_slice(&(*v as u64).to_ne_bytes()) |
| 544 | } |
| 545 | KeyParameterValue::Blob(v) => { |
| 546 | let blob_len = v.len() as u32; |
| 547 | result.extend_from_slice(&blob_len.to_ne_bytes()); |
| 548 | result.extend_from_slice(&blob_offset.to_ne_bytes()); |
| 549 | blob_offset += blob_len; |
| 550 | } |
| 551 | |
| 552 | _ => return Err(bloberr!("unknown value found in {:?}", param)), |
| 553 | } |
| 554 | } |
| 555 | let serialized_size = (result.len() - first_param_offset) as u32; |
| 556 | |
| 557 | // Go back and fill in the total serialized size. |
| 558 | result[params_size_offset..params_size_offset + 4] |
| 559 | .clone_from_slice(&serialized_size.to_ne_bytes()); |
| 560 | Ok(result) |
| 561 | } |