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 | |
| 18 | #![allow(dead_code)] |
| 19 | |
| 20 | use crate::error::Error; |
| 21 | use crate::ks_err; |
| 22 | use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{ |
| 23 | Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve, |
| 24 | ErrorCode::ErrorCode, HardwareAuthenticatorType::HardwareAuthenticatorType, |
| 25 | KeyFormat::KeyFormat, KeyOrigin::KeyOrigin, KeyParameter::KeyParameter, |
| 26 | KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, |
| 27 | Tag::Tag, TagType::TagType, |
| 28 | }; |
| 29 | use anyhow::Result; |
| 30 | use keystore2_crypto::hmac_sha256; |
| 31 | use std::mem::size_of; |
| 32 | |
| 33 | /// Root of trust value. |
| 34 | const SOFTWARE_ROOT_OF_TRUST: &[u8] = b"SW"; |
| 35 | |
| 36 | /// Error macro. |
| 37 | macro_rules! bloberr { |
| 38 | { $($arg:tt)+ } => { |
| 39 | anyhow::Error::new(Error::Km(ErrorCode::INVALID_KEY_BLOB)).context(ks_err!($($arg)+)) |
| 40 | }; |
| 41 | } |
| 42 | |
| 43 | /// Get the `KeyParameterValue` associated with a tag from a collection of `KeyParameter`s. |
| 44 | fn get_tag_value(params: &[KeyParameter], tag: Tag) -> Option<&KeyParameterValue> { |
| 45 | params.iter().find_map(|kp| if kp.tag == tag { Some(&kp.value) } else { None }) |
| 46 | } |
| 47 | |
| 48 | /// Get the [`TagType`] for a [`Tag`]. |
| 49 | fn tag_type(tag: &Tag) -> TagType { |
| 50 | TagType((tag.0 as u32 & 0xf0000000) as i32) |
| 51 | } |
| 52 | |
| 53 | /// Extract key material and combined key characteristics from a legacy authenticated keyblob. |
| 54 | pub fn export_key( |
| 55 | data: &[u8], |
| 56 | params: &[KeyParameter], |
| 57 | ) -> Result<(KeyFormat, Vec<u8>, Vec<KeyParameter>)> { |
| 58 | let hidden = hidden_params(params, &[SOFTWARE_ROOT_OF_TRUST]); |
| 59 | let KeyBlob { key_material, hw_enforced, sw_enforced } = |
| 60 | KeyBlob::new_from_serialized(data, &hidden)?; |
| 61 | |
| 62 | let mut combined = hw_enforced; |
| 63 | combined.extend_from_slice(&sw_enforced); |
| 64 | |
| 65 | let algo_val = |
| 66 | get_tag_value(&combined, Tag::ALGORITHM).ok_or_else(|| bloberr!("No algorithm found!"))?; |
| 67 | |
| 68 | let format = match algo_val { |
| 69 | KeyParameterValue::Algorithm(Algorithm::AES) |
| 70 | | KeyParameterValue::Algorithm(Algorithm::TRIPLE_DES) |
| 71 | | KeyParameterValue::Algorithm(Algorithm::HMAC) => KeyFormat::RAW, |
| 72 | KeyParameterValue::Algorithm(Algorithm::RSA) |
| 73 | | KeyParameterValue::Algorithm(Algorithm::EC) => KeyFormat::PKCS8, |
| 74 | _ => return Err(bloberr!("Unexpected algorithm {:?}", algo_val)), |
| 75 | }; |
| 76 | Ok((format, key_material, combined)) |
| 77 | } |
| 78 | |
| 79 | /// Plaintext key blob, with key characteristics. |
| 80 | #[derive(PartialEq, Eq)] |
| 81 | struct KeyBlob { |
| 82 | /// Raw key material. |
| 83 | key_material: Vec<u8>, |
| 84 | /// Hardware-enforced key characteristics. |
| 85 | hw_enforced: Vec<KeyParameter>, |
| 86 | /// Software-enforced key characteristics. |
| 87 | sw_enforced: Vec<KeyParameter>, |
| 88 | } |
| 89 | |
| 90 | impl KeyBlob { |
| 91 | /// Key blob version. |
| 92 | const KEY_BLOB_VERSION: u8 = 0; |
| 93 | |
| 94 | /// Hard-coded HMAC key used for keyblob authentication. |
| 95 | const LEGACY_HMAC_KEY: &[u8] = b"IntegrityAssuredBlob0\0"; |
| 96 | |
| 97 | /// Size (in bytes) of appended MAC. |
| 98 | const MAC_LEN: usize = 8; |
| 99 | |
| 100 | /// Parse a serialized [`KeyBlob`]. |
| 101 | fn new_from_serialized(mut data: &[u8], hidden: &[KeyParameter]) -> Result<Self> { |
| 102 | // Keyblob needs to be at least long enough for: |
| 103 | // - version byte, |
| 104 | // - 4-byte len for key material |
| 105 | // - 4-byte len for hw_enforced params |
| 106 | // - 4-byte len for sw_enforced params |
| 107 | // - MAC tag. |
| 108 | if data.len() < (1 + 3 * size_of::<u32>() + Self::MAC_LEN) { |
| 109 | return Err(bloberr!("blob not long enough (len = {})", data.len())); |
| 110 | } |
| 111 | |
| 112 | // Check the HMAC in the last 8 bytes before doing anything else. |
| 113 | let mac = &data[data.len() - Self::MAC_LEN..]; |
| 114 | let computed_mac = Self::compute_hmac(&data[..data.len() - Self::MAC_LEN], hidden)?; |
| 115 | if mac != computed_mac { |
| 116 | return Err(bloberr!("invalid key blob")); |
| 117 | } |
| 118 | |
| 119 | let version = consume_u8(&mut data)?; |
| 120 | if version != Self::KEY_BLOB_VERSION { |
| 121 | return Err(bloberr!("unexpected blob version {}", version)); |
| 122 | } |
| 123 | let key_material = consume_vec(&mut data)?; |
| 124 | let hw_enforced = deserialize_params(&mut data)?; |
| 125 | let sw_enforced = deserialize_params(&mut data)?; |
| 126 | |
| 127 | // Should just be the (already-checked) MAC left. |
| 128 | let rest = &data[Self::MAC_LEN..]; |
| 129 | if !rest.is_empty() { |
| 130 | return Err(bloberr!("extra data (len {})", rest.len())); |
| 131 | } |
| 132 | Ok(KeyBlob { key_material, hw_enforced, sw_enforced }) |
| 133 | } |
| 134 | |
| 135 | /// Compute the authentication HMAC for a KeyBlob. This is built as: |
| 136 | /// HMAC-SHA256(HK, data || serialize(hidden)) |
| 137 | /// with HK = b"IntegrityAssuredBlob0\0". |
| 138 | fn compute_hmac(data: &[u8], hidden: &[KeyParameter]) -> Result<Vec<u8>> { |
| 139 | let hidden_data = serialize_params(hidden)?; |
| 140 | let mut combined = data.to_vec(); |
| 141 | combined.extend_from_slice(&hidden_data); |
| 142 | let mut tag = hmac_sha256(Self::LEGACY_HMAC_KEY, &combined)?; |
| 143 | tag.truncate(Self::MAC_LEN); |
| 144 | Ok(tag) |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | /// Build the parameters that are used as the hidden input to HMAC calculations: |
| 149 | /// - `ApplicationId(data)` if present |
| 150 | /// - `ApplicationData(data)` if present |
| 151 | /// - (repeated) `RootOfTrust(rot)` where `rot` is a hardcoded piece of root of trust information. |
| 152 | fn hidden_params(params: &[KeyParameter], rots: &[&[u8]]) -> Vec<KeyParameter> { |
| 153 | let mut results = Vec::new(); |
| 154 | if let Some(app_id) = get_tag_value(params, Tag::APPLICATION_ID) { |
| 155 | results.push(KeyParameter { tag: Tag::APPLICATION_ID, value: app_id.clone() }); |
| 156 | } |
| 157 | if let Some(app_data) = get_tag_value(params, Tag::APPLICATION_DATA) { |
| 158 | results.push(KeyParameter { tag: Tag::APPLICATION_DATA, value: app_data.clone() }); |
| 159 | } |
| 160 | for rot in rots { |
| 161 | results.push(KeyParameter { |
| 162 | tag: Tag::ROOT_OF_TRUST, |
| 163 | value: KeyParameterValue::Blob(rot.to_vec()), |
| 164 | }); |
| 165 | } |
| 166 | results |
| 167 | } |
| 168 | |
| 169 | /// Retrieve a `u8` from the start of the given slice, if possible. |
| 170 | fn consume_u8(data: &mut &[u8]) -> Result<u8> { |
| 171 | match data.first() { |
| 172 | Some(b) => { |
| 173 | *data = &(*data)[1..]; |
| 174 | Ok(*b) |
| 175 | } |
| 176 | None => Err(bloberr!("failed to find 1 byte")), |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | /// Move past a bool value from the start of the given slice, if possible. |
| 181 | /// Bool values should only be included if `true`, so fail if the value |
| 182 | /// is anything other than 1. |
| 183 | fn consume_bool(data: &mut &[u8]) -> Result<bool> { |
| 184 | let b = consume_u8(data)?; |
| 185 | if b == 0x01 { |
| 186 | Ok(true) |
| 187 | } else { |
| 188 | Err(bloberr!("bool value other than 1 encountered")) |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | /// Retrieve a (host-ordered) `u32` from the start of the given slice, if possible. |
| 193 | fn consume_u32(data: &mut &[u8]) -> Result<u32> { |
| 194 | const LEN: usize = size_of::<u32>(); |
| 195 | if data.len() < LEN { |
| 196 | return Err(bloberr!("failed to find {LEN} bytes")); |
| 197 | } |
| 198 | let chunk: [u8; LEN] = data[..LEN].try_into().unwrap(); // safe: just checked |
| 199 | *data = &(*data)[LEN..]; |
| 200 | Ok(u32::from_ne_bytes(chunk)) |
| 201 | } |
| 202 | |
| 203 | /// Retrieve a (host-ordered) `i32` from the start of the given slice, if possible. |
| 204 | fn consume_i32(data: &mut &[u8]) -> Result<i32> { |
| 205 | const LEN: usize = size_of::<i32>(); |
| 206 | if data.len() < LEN { |
| 207 | return Err(bloberr!("failed to find {LEN} bytes")); |
| 208 | } |
| 209 | let chunk: [u8; LEN] = data[..LEN].try_into().unwrap(); // safe: just checked |
| 210 | *data = &(*data)[4..]; |
| 211 | Ok(i32::from_ne_bytes(chunk)) |
| 212 | } |
| 213 | |
| 214 | /// Retrieve a (host-ordered) `i64` from the start of the given slice, if possible. |
| 215 | fn consume_i64(data: &mut &[u8]) -> Result<i64> { |
| 216 | const LEN: usize = size_of::<i64>(); |
| 217 | if data.len() < LEN { |
| 218 | return Err(bloberr!("failed to find {LEN} bytes")); |
| 219 | } |
| 220 | let chunk: [u8; LEN] = data[..LEN].try_into().unwrap(); // safe: just checked |
| 221 | *data = &(*data)[LEN..]; |
| 222 | Ok(i64::from_ne_bytes(chunk)) |
| 223 | } |
| 224 | |
| 225 | /// Retrieve a vector of bytes from the start of the given slice, if possible, |
| 226 | /// with the length of the data expected to appear as a host-ordered `u32` prefix. |
| 227 | fn consume_vec(data: &mut &[u8]) -> Result<Vec<u8>> { |
| 228 | let len = consume_u32(data)? as usize; |
| 229 | if len > data.len() { |
| 230 | return Err(bloberr!("failed to find {} bytes", len)); |
| 231 | } |
| 232 | let result = data[..len].to_vec(); |
| 233 | *data = &(*data)[len..]; |
| 234 | Ok(result) |
| 235 | } |
| 236 | |
| 237 | /// Retrieve the contents of a tag of `TagType::Bytes`. The `data` parameter holds |
| 238 | /// the as-yet unparsed data, and a length and offset are read from this (and consumed). |
| 239 | /// This length and offset refer to a location in the combined `blob_data`; however, |
| 240 | /// the offset is expected to be the next unconsumed chunk of `blob_data`, as indicated |
| 241 | /// by `next_blob_offset` (which itself is updated as a result of consuming the data). |
| 242 | fn consume_blob( |
| 243 | data: &mut &[u8], |
| 244 | next_blob_offset: &mut usize, |
| 245 | blob_data: &[u8], |
| 246 | ) -> Result<Vec<u8>> { |
| 247 | let data_len = consume_u32(data)? as usize; |
| 248 | let data_offset = consume_u32(data)? as usize; |
| 249 | // Expect the blob data to come from the next offset in the initial blob chunk. |
| 250 | if data_offset != *next_blob_offset { |
| 251 | return Err(bloberr!("got blob offset {} instead of {}", data_offset, next_blob_offset)); |
| 252 | } |
| 253 | if (data_offset + data_len) > blob_data.len() { |
| 254 | return Err(bloberr!( |
| 255 | "blob at offset [{}..{}+{}] goes beyond blob data size {}", |
| 256 | data_offset, |
| 257 | data_offset, |
| 258 | data_len, |
| 259 | blob_data.len(), |
| 260 | )); |
| 261 | } |
| 262 | |
| 263 | let slice = &blob_data[data_offset..data_offset + data_len]; |
| 264 | *next_blob_offset += data_len; |
| 265 | Ok(slice.to_vec()) |
| 266 | } |
| 267 | |
| 268 | /// Deserialize a collection of [`KeyParam`]s in legacy serialized format. The provided slice is |
| 269 | /// modified to contain the unconsumed part of the data. |
| 270 | fn deserialize_params(data: &mut &[u8]) -> Result<Vec<KeyParameter>> { |
| 271 | let blob_data_size = consume_u32(data)? as usize; |
| 272 | if blob_data_size > data.len() { |
| 273 | return Err(bloberr!( |
| 274 | "blob data size {} bigger than data (len={})", |
| 275 | blob_data_size, |
| 276 | data.len() |
| 277 | )); |
| 278 | } |
| 279 | |
| 280 | let blob_data = &data[..blob_data_size]; |
| 281 | let mut next_blob_offset = 0; |
| 282 | |
| 283 | // Move past the blob data. |
| 284 | *data = &data[blob_data_size..]; |
| 285 | |
| 286 | let param_count = consume_u32(data)? as usize; |
| 287 | let param_size = consume_u32(data)? as usize; |
| 288 | if param_size > data.len() { |
| 289 | return Err(bloberr!( |
| 290 | "size mismatch 4+{}+4+4+{} > {}", |
| 291 | blob_data_size, |
| 292 | param_size, |
| 293 | data.len() |
| 294 | )); |
| 295 | } |
| 296 | |
| 297 | let mut results = Vec::new(); |
| 298 | for _i in 0..param_count { |
| 299 | let tag_num = consume_u32(data)? as i32; |
| 300 | let tag = Tag(tag_num); |
| 301 | let value = match tag_type(&tag) { |
| 302 | TagType::INVALID => return Err(bloberr!("invalid tag {:?} encountered", tag)), |
| 303 | TagType::ENUM | TagType::ENUM_REP => { |
| 304 | let val = consume_i32(data)?; |
| 305 | match tag { |
| 306 | Tag::ALGORITHM => KeyParameterValue::Algorithm(Algorithm(val)), |
| 307 | Tag::BLOCK_MODE => KeyParameterValue::BlockMode(BlockMode(val)), |
| 308 | Tag::PADDING => KeyParameterValue::PaddingMode(PaddingMode(val)), |
| 309 | Tag::DIGEST | Tag::RSA_OAEP_MGF_DIGEST => { |
| 310 | KeyParameterValue::Digest(Digest(val)) |
| 311 | } |
| 312 | Tag::EC_CURVE => KeyParameterValue::EcCurve(EcCurve(val)), |
| 313 | Tag::ORIGIN => KeyParameterValue::Origin(KeyOrigin(val)), |
| 314 | Tag::PURPOSE => KeyParameterValue::KeyPurpose(KeyPurpose(val)), |
| 315 | Tag::USER_AUTH_TYPE => { |
| 316 | KeyParameterValue::HardwareAuthenticatorType(HardwareAuthenticatorType(val)) |
| 317 | } |
| 318 | _ => KeyParameterValue::Integer(val), |
| 319 | } |
| 320 | } |
| 321 | TagType::UINT | TagType::UINT_REP => KeyParameterValue::Integer(consume_i32(data)?), |
| 322 | TagType::ULONG | TagType::ULONG_REP => { |
| 323 | KeyParameterValue::LongInteger(consume_i64(data)?) |
| 324 | } |
| 325 | TagType::DATE => KeyParameterValue::DateTime(consume_i64(data)?), |
| 326 | TagType::BOOL => KeyParameterValue::BoolValue(consume_bool(data)?), |
| 327 | TagType::BIGNUM | TagType::BYTES => { |
| 328 | KeyParameterValue::Blob(consume_blob(data, &mut next_blob_offset, blob_data)?) |
| 329 | } |
| 330 | _ => return Err(bloberr!("unexpected tag type for {:?}", tag)), |
| 331 | }; |
| 332 | results.push(KeyParameter { tag, value }); |
| 333 | } |
| 334 | |
| 335 | Ok(results) |
| 336 | } |
| 337 | |
| 338 | /// Serialize a collection of [`KeyParameter`]s into a format that is compatible with previous |
| 339 | /// implementations: |
| 340 | /// |
| 341 | /// ```text |
| 342 | /// [0..4] Size B of `TagType::Bytes` data, in host order. |
| 343 | /// [4..4+B] (*) Concatenated contents of each `TagType::Bytes` tag. |
| 344 | /// [4+B..4+B+4] Count N of the number of parameters, in host order. |
| 345 | /// [8+B..8+B+4] Size Z of encoded parameters. |
| 346 | /// [12+B..12+B+Z] Serialized parameters one after another. |
| 347 | /// ``` |
| 348 | /// |
| 349 | /// Individual parameters are serialized in the last chunk as: |
| 350 | /// |
| 351 | /// ```text |
| 352 | /// [0..4] Tag number, in host order. |
| 353 | /// Followed by one of the following depending on the tag's `TagType`; all integers in host order: |
| 354 | /// [4..5] Bool value (`TagType::Bool`) |
| 355 | /// [4..8] i32 values (`TagType::Uint[Rep]`, `TagType::Enum[Rep]`) |
| 356 | /// [4..12] i64 values, in host order (`TagType::UlongRep`, `TagType::Date`) |
| 357 | /// [4..8] + [8..12] Size + offset of data in (*) above (`TagType::Bytes`, `TagType::Bignum`) |
| 358 | /// ``` |
| 359 | fn serialize_params(params: &[KeyParameter]) -> Result<Vec<u8>> { |
| 360 | // First 4 bytes are the length of the combined [`TagType::Bytes`] data; come back to set that |
| 361 | // in a moment. |
| 362 | let mut result = vec![0; 4]; |
| 363 | |
| 364 | // Next append the contents of all of the [`TagType::Bytes`] data. |
| 365 | let mut blob_size = 0u32; |
| 366 | for param in params { |
| 367 | let tag_type = tag_type(¶m.tag); |
| 368 | if let KeyParameterValue::Blob(v) = ¶m.value { |
| 369 | if tag_type != TagType::BIGNUM && tag_type != TagType::BYTES { |
| 370 | return Err(bloberr!("unexpected tag type for tag {:?} with blob", param.tag)); |
| 371 | } |
| 372 | result.extend_from_slice(v); |
| 373 | blob_size += v.len() as u32; |
| 374 | } |
| 375 | } |
| 376 | // Go back and fill in the combined blob length in native order at the start. |
| 377 | result[..4].clone_from_slice(&blob_size.to_ne_bytes()); |
| 378 | |
| 379 | result.extend_from_slice(&(params.len() as u32).to_ne_bytes()); |
| 380 | |
| 381 | let params_size_offset = result.len(); |
| 382 | result.extend_from_slice(&[0u8; 4]); // placeholder for size of elements |
| 383 | let first_param_offset = result.len(); |
| 384 | let mut blob_offset = 0u32; |
| 385 | for param in params { |
| 386 | result.extend_from_slice(&(param.tag.0 as u32).to_ne_bytes()); |
| 387 | match ¶m.value { |
| 388 | KeyParameterValue::Invalid(_v) => { |
| 389 | return Err(bloberr!("invalid tag found in {:?}", param)) |
| 390 | } |
| 391 | |
| 392 | // Enum-holding variants. |
| 393 | KeyParameterValue::Algorithm(v) => { |
| 394 | result.extend_from_slice(&(v.0 as u32).to_ne_bytes()) |
| 395 | } |
| 396 | KeyParameterValue::BlockMode(v) => { |
| 397 | result.extend_from_slice(&(v.0 as u32).to_ne_bytes()) |
| 398 | } |
| 399 | KeyParameterValue::PaddingMode(v) => { |
| 400 | result.extend_from_slice(&(v.0 as u32).to_ne_bytes()) |
| 401 | } |
| 402 | KeyParameterValue::Digest(v) => result.extend_from_slice(&(v.0 as u32).to_ne_bytes()), |
| 403 | KeyParameterValue::EcCurve(v) => result.extend_from_slice(&(v.0 as u32).to_ne_bytes()), |
| 404 | KeyParameterValue::Origin(v) => result.extend_from_slice(&(v.0 as u32).to_ne_bytes()), |
| 405 | KeyParameterValue::KeyPurpose(v) => { |
| 406 | result.extend_from_slice(&(v.0 as u32).to_ne_bytes()) |
| 407 | } |
| 408 | KeyParameterValue::HardwareAuthenticatorType(v) => { |
| 409 | result.extend_from_slice(&(v.0 as u32).to_ne_bytes()) |
| 410 | } |
| 411 | |
| 412 | // Value-holding variants. |
| 413 | KeyParameterValue::Integer(v) => result.extend_from_slice(&(*v as u32).to_ne_bytes()), |
| 414 | KeyParameterValue::BoolValue(_v) => result.push(0x01u8), |
| 415 | KeyParameterValue::LongInteger(v) | KeyParameterValue::DateTime(v) => { |
| 416 | result.extend_from_slice(&(*v as u64).to_ne_bytes()) |
| 417 | } |
| 418 | KeyParameterValue::Blob(v) => { |
| 419 | let blob_len = v.len() as u32; |
| 420 | result.extend_from_slice(&blob_len.to_ne_bytes()); |
| 421 | result.extend_from_slice(&blob_offset.to_ne_bytes()); |
| 422 | blob_offset += blob_len; |
| 423 | } |
| 424 | |
| 425 | _ => return Err(bloberr!("unknown value found in {:?}", param)), |
| 426 | } |
| 427 | } |
| 428 | let serialized_size = (result.len() - first_param_offset) as u32; |
| 429 | |
| 430 | // Go back and fill in the total serialized size. |
| 431 | result[params_size_offset..params_size_offset + 4] |
| 432 | .clone_from_slice(&serialized_size.to_ne_bytes()); |
| 433 | Ok(result) |
| 434 | } |
| 435 | |
| 436 | #[cfg(test)] |
| 437 | mod tests { |
| 438 | use super::*; |
| 439 | use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{ |
| 440 | Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve, |
| 441 | KeyOrigin::KeyOrigin, KeyParameter::KeyParameter, |
| 442 | KeyParameterValue::KeyParameterValue as KPV, KeyPurpose::KeyPurpose, |
| 443 | PaddingMode::PaddingMode, Tag::Tag, |
| 444 | }; |
| 445 | |
| 446 | macro_rules! expect_err { |
| 447 | ($result:expr, $err_msg:expr) => { |
| 448 | assert!( |
| 449 | $result.is_err(), |
| 450 | "Expected error containing '{}', got success {:?}", |
| 451 | $err_msg, |
| 452 | $result |
| 453 | ); |
| 454 | let err = $result.err(); |
| 455 | assert!( |
| 456 | format!("{:?}", err).contains($err_msg), |
| 457 | "Unexpected error {:?}, doesn't contain '{}'", |
| 458 | err, |
| 459 | $err_msg |
| 460 | ); |
| 461 | }; |
| 462 | } |
| 463 | |
| 464 | #[test] |
| 465 | fn test_consume_u8() { |
| 466 | let buffer = [1, 2]; |
| 467 | let mut data = &buffer[..]; |
| 468 | assert_eq!(1u8, consume_u8(&mut data).unwrap()); |
| 469 | assert_eq!(2u8, consume_u8(&mut data).unwrap()); |
| 470 | let result = consume_u8(&mut data); |
| 471 | expect_err!(result, "failed to find 1 byte"); |
| 472 | } |
| 473 | |
| 474 | #[test] |
| 475 | fn test_consume_u32() { |
| 476 | // All supported platforms are little-endian. |
| 477 | let buffer = [ |
| 478 | 0x01, 0x02, 0x03, 0x04, // little-endian u32 |
| 479 | 0x04, 0x03, 0x02, 0x01, // little-endian u32 |
| 480 | 0x11, 0x12, 0x13, |
| 481 | ]; |
| 482 | let mut data = &buffer[..]; |
| 483 | assert_eq!(0x04030201u32, consume_u32(&mut data).unwrap()); |
| 484 | assert_eq!(0x01020304u32, consume_u32(&mut data).unwrap()); |
| 485 | let result = consume_u32(&mut data); |
| 486 | expect_err!(result, "failed to find 4 bytes"); |
| 487 | } |
| 488 | |
| 489 | #[test] |
| 490 | fn test_consume_i64() { |
| 491 | // All supported platforms are little-endian. |
| 492 | let buffer = [ |
| 493 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // little-endian i64 |
| 494 | 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // little-endian i64 |
| 495 | 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, |
| 496 | ]; |
| 497 | let mut data = &buffer[..]; |
| 498 | assert_eq!(0x0807060504030201i64, consume_i64(&mut data).unwrap()); |
| 499 | assert_eq!(0x0102030405060708i64, consume_i64(&mut data).unwrap()); |
| 500 | let result = consume_i64(&mut data); |
| 501 | expect_err!(result, "failed to find 8 bytes"); |
| 502 | } |
| 503 | |
| 504 | #[test] |
| 505 | fn test_consume_vec() { |
| 506 | let buffer = [ |
| 507 | 0x01, 0x00, 0x00, 0x00, 0xaa, // |
| 508 | 0x00, 0x00, 0x00, 0x00, // |
| 509 | 0x01, 0x00, 0x00, 0x00, 0xbb, // |
| 510 | 0x07, 0x00, 0x00, 0x00, 0xbb, // not enough data |
| 511 | ]; |
| 512 | let mut data = &buffer[..]; |
| 513 | assert_eq!(vec![0xaa], consume_vec(&mut data).unwrap()); |
| 514 | assert_eq!(Vec::<u8>::new(), consume_vec(&mut data).unwrap()); |
| 515 | assert_eq!(vec![0xbb], consume_vec(&mut data).unwrap()); |
| 516 | let result = consume_vec(&mut data); |
| 517 | expect_err!(result, "failed to find 7 bytes"); |
| 518 | |
| 519 | let buffer = [ |
| 520 | 0x01, 0x00, 0x00, // |
| 521 | ]; |
| 522 | let mut data = &buffer[..]; |
| 523 | let result = consume_vec(&mut data); |
| 524 | expect_err!(result, "failed to find 4 bytes"); |
| 525 | } |
| 526 | |
| 527 | #[test] |
| 528 | fn test_key_new_from_serialized() { |
| 529 | let hidden = hidden_params(&[], &[SOFTWARE_ROOT_OF_TRUST]); |
| 530 | // Test data originally generated by instrumenting Cuttlefish C++ KeyMint while running VTS |
| 531 | // tests. |
| 532 | let tests = [ |
| 533 | ( |
| 534 | concat!( |
| 535 | "0010000000d43c2f04f948521b81bdbf001310f5920000000000000000000000", |
| 536 | "00000000000c0000006400000002000010200000000300003080000000010000", |
| 537 | "2000000000010000200100000004000020020000000600002001000000be0200", |
| 538 | "1000000000c1020030b0ad0100c20200307b150300bd020060a8bb52407b0100", |
| 539 | "00ce02003011643401cf020030000000003b06b13ae6ae6671", |
| 540 | ), |
| 541 | KeyBlob { |
| 542 | key_material: hex::decode("d43c2f04f948521b81bdbf001310f592").unwrap(), |
| 543 | hw_enforced: vec![], |
| 544 | sw_enforced: vec![ |
| 545 | KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::AES) }, |
| 546 | KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(128) }, |
| 547 | KeyParameter { |
| 548 | tag: Tag::PURPOSE, |
| 549 | value: KPV::KeyPurpose(KeyPurpose::ENCRYPT), |
| 550 | }, |
| 551 | KeyParameter { |
| 552 | tag: Tag::PURPOSE, |
| 553 | value: KPV::KeyPurpose(KeyPurpose::DECRYPT), |
| 554 | }, |
| 555 | KeyParameter { |
| 556 | tag: Tag::BLOCK_MODE, |
| 557 | value: KPV::BlockMode(BlockMode::CBC), |
| 558 | }, |
| 559 | KeyParameter { |
| 560 | tag: Tag::PADDING, |
| 561 | value: KPV::PaddingMode(PaddingMode::NONE), |
| 562 | }, |
| 563 | KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) }, |
| 564 | KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) }, |
| 565 | KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) }, |
| 566 | KeyParameter { |
| 567 | tag: Tag::CREATION_DATETIME, |
| 568 | value: KPV::DateTime(1628871769000), |
| 569 | }, |
| 570 | KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) }, |
| 571 | KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) }, |
| 572 | ], |
| 573 | }, |
| 574 | Some(KeyFormat::RAW), |
| 575 | ), |
| 576 | ( |
| 577 | concat!( |
| 578 | "00df0000003081dc020101044200b6ce876b947e263d61b8e3998d50dc0afb6b", |
| 579 | "a14e46ab7ca532fbe2a379b155d0a5bb99265402857b1601fb20be6c244bf654", |
| 580 | "e9e79413cd503eae3d9cf68ed24f47a00706052b81040023a181890381860004", |
| 581 | "006b840f0db0b12f074ab916c7773cfa7d42967c9e5b4fae09cf999f7e116d14", |
| 582 | "0743bdd028db0a3fcc670e721b9f00bc7fb70aa401c7d6de6582fc26962a29b7", |
| 583 | "45e30142e90685646661550344113aaf28bdee6cb02d19df1faab4398556a909", |
| 584 | "7d6f64b95209601a549389a311231c6cce78354f2cdbc3a904abf70686f5f0c3", |
| 585 | "b877984d000000000000000000000000000000000c0000006400000002000010", |
| 586 | "030000000a000010030000000100002002000000010000200300000005000020", |
| 587 | "000000000300003009020000be02001000000000c1020030b0ad0100c2020030", |
| 588 | "7b150300bd02006018d352407b010000ce02003011643401cf02003000000000", |
| 589 | "2f69002e55e9b0a3" |
| 590 | ), |
| 591 | KeyBlob { |
| 592 | key_material: hex::decode(concat!( |
| 593 | "3081dc020101044200b6ce876b947e263d61b8e3998d50dc0afb6ba14e46ab7c", |
| 594 | "a532fbe2a379b155d0a5bb99265402857b1601fb20be6c244bf654e9e79413cd", |
| 595 | "503eae3d9cf68ed24f47a00706052b81040023a181890381860004006b840f0d", |
| 596 | "b0b12f074ab916c7773cfa7d42967c9e5b4fae09cf999f7e116d140743bdd028", |
| 597 | "db0a3fcc670e721b9f00bc7fb70aa401c7d6de6582fc26962a29b745e30142e9", |
| 598 | "0685646661550344113aaf28bdee6cb02d19df1faab4398556a9097d6f64b952", |
| 599 | "09601a549389a311231c6cce78354f2cdbc3a904abf70686f5f0c3b877984d", |
| 600 | )) |
| 601 | .unwrap(), |
| 602 | hw_enforced: vec![], |
| 603 | sw_enforced: vec![ |
| 604 | KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::EC) }, |
| 605 | KeyParameter { tag: Tag::EC_CURVE, value: KPV::EcCurve(EcCurve::P_521) }, |
| 606 | KeyParameter { |
| 607 | tag: Tag::PURPOSE, |
| 608 | value: KPV::KeyPurpose(KeyPurpose::SIGN), |
| 609 | }, |
| 610 | KeyParameter { |
| 611 | tag: Tag::PURPOSE, |
| 612 | value: KPV::KeyPurpose(KeyPurpose::VERIFY), |
| 613 | }, |
| 614 | KeyParameter { tag: Tag::DIGEST, value: KPV::Digest(Digest::NONE) }, |
| 615 | KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(521) }, |
| 616 | KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) }, |
| 617 | KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) }, |
| 618 | KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) }, |
| 619 | KeyParameter { |
| 620 | tag: Tag::CREATION_DATETIME, |
| 621 | value: KPV::DateTime(1628871775000), |
| 622 | }, |
| 623 | KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) }, |
| 624 | KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) }, |
| 625 | ], |
| 626 | }, |
| 627 | Some(KeyFormat::PKCS8), |
| 628 | ), |
| 629 | ( |
| 630 | concat!( |
| 631 | "0037000000541d4c440223650d5f51753c1abd80c725034485551e874d62327c", |
| 632 | "65f6247a057f1218bd6c8cd7d319103ddb823fc11fb6c2c7268b5acc00000000", |
| 633 | "0000000000000000000000000c00000064000000020000108000000003000030", |
| 634 | "b801000001000020020000000100002003000000050000200400000008000030", |
| 635 | "00010000be02001000000000c1020030b0ad0100c20200307b150300bd020060", |
| 636 | "00d752407b010000ce02003011643401cf0200300000000036e6986ffc45fbb0", |
| 637 | ), |
| 638 | KeyBlob { |
| 639 | key_material: hex::decode(concat!( |
| 640 | "541d4c440223650d5f51753c1abd80c725034485551e874d62327c65f6247a05", |
| 641 | "7f1218bd6c8cd7d319103ddb823fc11fb6c2c7268b5acc" |
| 642 | )) |
| 643 | .unwrap(), |
| 644 | hw_enforced: vec![], |
| 645 | sw_enforced: vec![ |
| 646 | KeyParameter { |
| 647 | tag: Tag::ALGORITHM, |
| 648 | value: KPV::Algorithm(Algorithm::HMAC), |
| 649 | }, |
| 650 | KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(440) }, |
| 651 | KeyParameter { |
| 652 | tag: Tag::PURPOSE, |
| 653 | value: KPV::KeyPurpose(KeyPurpose::SIGN), |
| 654 | }, |
| 655 | KeyParameter { |
| 656 | tag: Tag::PURPOSE, |
| 657 | value: KPV::KeyPurpose(KeyPurpose::VERIFY), |
| 658 | }, |
| 659 | KeyParameter { tag: Tag::DIGEST, value: KPV::Digest(Digest::SHA_2_256) }, |
| 660 | KeyParameter { tag: Tag::MIN_MAC_LENGTH, value: KPV::Integer(256) }, |
| 661 | KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) }, |
| 662 | KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) }, |
| 663 | KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) }, |
| 664 | KeyParameter { |
| 665 | tag: Tag::CREATION_DATETIME, |
| 666 | value: KPV::DateTime(1628871776000), |
| 667 | }, |
| 668 | KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) }, |
| 669 | KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) }, |
| 670 | ], |
| 671 | }, |
| 672 | Some(KeyFormat::RAW), |
| 673 | ), |
| 674 | ( |
| 675 | concat!( |
| 676 | "00a8040000308204a40201000282010100bc47b5c71116766669b91fa747df87", |
| 677 | "a1963df83956569d4ac232aeba8a246c0ec73bf606374a6d07f30c2162f97082", |
| 678 | "825c7c6e482a2841dfeaec1429d84e52c54a6b2f760dec952c9c44a3c3a80f31", |
| 679 | "c1ced84878edd4858059071c4d20d9ab0aae978bd68c1eb448e174a9736c3973", |
| 680 | "6838151642eda8215107375865a99a57f29467c74c40f37b0221b93ec3f4f22d", |
| 681 | "5337c8bf9245d56936196a92b1dea315ecce8785f9fa9b7d159ca207612cc0de", |
| 682 | "b0957d61dbba5d9bd38784f4fecbf233b04e686a340528665ecd03db8e8a09b2", |
| 683 | "540c84e45c4a99fb338b76bba7722856b5113341c349708937228f167d238ed8", |
| 684 | "efb9cc19547dd620f6a90d95f07e50bfe102030100010282010002f91b69d9af", |
| 685 | "59fe87421af9ba60f15c77f9c1c90effd6634332876f8ee5a116b126f55d3703", |
| 686 | "8bf9f588ae20c8d951d842e35c9ef35a7822d3ebf72c0b7c3e229b289ae2e178", |
| 687 | "a848e06d558c2e03d26871ee98a35f370d461ff1c4acc39d684de680a25ec88e", |
| 688 | "e610260e406c400bdeb2893b2d0330cb483e662fa5abd24c2b82143e85dfe30a", |
| 689 | "e7a31f8262da2903d882b35a34a26b699ff2d812bad4b126a0065ec0e101d73a", |
| 690 | "e6f8b29a9144eb83f54940a371fc7416c2c0370df6a41cb5391f17ba33239e1b", |
| 691 | "4217c8db50db5c6bf77ccf621354ecc652a4f7196054c254566fd7b3bc0f3817", |
| 692 | "d9380b190bd382aaffa37785759f285194c11a188bccde0e2e2902818100fb23", |
| 693 | "3335770c9f3cbd4b6ede5f12d03c449b1997bce06a8249bc3de99972fd0d0a63", |
| 694 | "3f7790d1011bf5eedee16fa45a9107a910656ecaee364ce9edb4369843be71f2", |
| 695 | "7a74852d6c7215a6cc60d9803bcac544922f806d8e5844e0ddd914bd78009490", |
| 696 | "4c2856d2b944fade3fb1d67d4a33fb7663a9ab660ab372c2e4868a0f45990281", |
| 697 | "8100bfecf2bb4012e880fd065a0b088f2d757af2878d3f1305f21ce7a7158458", |
| 698 | "18e01181ff06b2f406239fc50808ce3dbe7b68ec01174913c0f237feb3c8c7eb", |
| 699 | "0078b77fb5b8f214b72f6d3835b1a7ebe8b132feb6cb34ab09ce22b98160fc84", |
| 700 | "20fcbf48d1eee49f874e902f049b206a61a095f0405a4935e7c5e49757ab7b57", |
| 701 | "298902818100ec0049383e16f3716de5fc5b2677148efe5dceb02483b43399bd", |
| 702 | "3765559994a9f3900eed7a7e9e8f3b0eee0e660eca392e3cb736cae612f39e55", |
| 703 | "dad696d3821def10d1f8bbca52f5e6d8e7893ffbdcb491aafdc17bebf86f84d2", |
| 704 | "d8480ed07a7bf9209d20ef6e79429489d4cb7768281a2f7e32ec1830fd6f6332", |
| 705 | "38f521ba764902818100b2c3ce5751580b4e51df3fb175387f5c24b79040a4d6", |
| 706 | "603c6265f70018b441ff3aef7d8e4cd2f480ec0906f1c4c0481304e8861f9d46", |
| 707 | "93fa48e3a9abc362859eeb343e1c5507ac94b5439ce7ac04154a2fb886a4819b", |
| 708 | "2a57e18a2e131b412ac4a09b004766959cdf357745f003e272aab3de02e2d5bc", |
| 709 | "2af4ed75760858ab181902818061d19c2a8dcacde104b97f7c4fae11216157c1", |
| 710 | "c0a258d882984d12383a73dc56fe2ac93512bb321df9706ecdb2f70a44c949c4", |
| 711 | "340a9fae64a0646cf51f37c58c08bebde91667b3b2fa7c895f7983d4786c5526", |
| 712 | "1941b3654533b0598383ebbcffcdf28b6cf13d376e3a70b49b14d8d06e8563a2", |
| 713 | "47f56a337e3b9845b4f2b61356000000000000000000000000000000000d0000", |
| 714 | "007000000002000010010000000300003000080000c800005001000100000000", |
| 715 | "0001000020020000000100002003000000050000200000000006000020010000", |
| 716 | "00be02001000000000c1020030b0ad0100c20200307b150300bd020060a8bb52", |
| 717 | "407b010000ce02003011643401cf02003000000000544862e9c961e857", |
| 718 | ), |
| 719 | KeyBlob { |
| 720 | key_material: hex::decode(concat!( |
| 721 | "308204a40201000282010100bc47b5c71116766669b91fa747df87a1963df839", |
| 722 | "56569d4ac232aeba8a246c0ec73bf606374a6d07f30c2162f97082825c7c6e48", |
| 723 | "2a2841dfeaec1429d84e52c54a6b2f760dec952c9c44a3c3a80f31c1ced84878", |
| 724 | "edd4858059071c4d20d9ab0aae978bd68c1eb448e174a9736c39736838151642", |
| 725 | "eda8215107375865a99a57f29467c74c40f37b0221b93ec3f4f22d5337c8bf92", |
| 726 | "45d56936196a92b1dea315ecce8785f9fa9b7d159ca207612cc0deb0957d61db", |
| 727 | "ba5d9bd38784f4fecbf233b04e686a340528665ecd03db8e8a09b2540c84e45c", |
| 728 | "4a99fb338b76bba7722856b5113341c349708937228f167d238ed8efb9cc1954", |
| 729 | "7dd620f6a90d95f07e50bfe102030100010282010002f91b69d9af59fe87421a", |
| 730 | "f9ba60f15c77f9c1c90effd6634332876f8ee5a116b126f55d37038bf9f588ae", |
| 731 | "20c8d951d842e35c9ef35a7822d3ebf72c0b7c3e229b289ae2e178a848e06d55", |
| 732 | "8c2e03d26871ee98a35f370d461ff1c4acc39d684de680a25ec88ee610260e40", |
| 733 | "6c400bdeb2893b2d0330cb483e662fa5abd24c2b82143e85dfe30ae7a31f8262", |
| 734 | "da2903d882b35a34a26b699ff2d812bad4b126a0065ec0e101d73ae6f8b29a91", |
| 735 | "44eb83f54940a371fc7416c2c0370df6a41cb5391f17ba33239e1b4217c8db50", |
| 736 | "db5c6bf77ccf621354ecc652a4f7196054c254566fd7b3bc0f3817d9380b190b", |
| 737 | "d382aaffa37785759f285194c11a188bccde0e2e2902818100fb233335770c9f", |
| 738 | "3cbd4b6ede5f12d03c449b1997bce06a8249bc3de99972fd0d0a633f7790d101", |
| 739 | "1bf5eedee16fa45a9107a910656ecaee364ce9edb4369843be71f27a74852d6c", |
| 740 | "7215a6cc60d9803bcac544922f806d8e5844e0ddd914bd780094904c2856d2b9", |
| 741 | "44fade3fb1d67d4a33fb7663a9ab660ab372c2e4868a0f459902818100bfecf2", |
| 742 | "bb4012e880fd065a0b088f2d757af2878d3f1305f21ce7a715845818e01181ff", |
| 743 | "06b2f406239fc50808ce3dbe7b68ec01174913c0f237feb3c8c7eb0078b77fb5", |
| 744 | "b8f214b72f6d3835b1a7ebe8b132feb6cb34ab09ce22b98160fc8420fcbf48d1", |
| 745 | "eee49f874e902f049b206a61a095f0405a4935e7c5e49757ab7b572989028181", |
| 746 | "00ec0049383e16f3716de5fc5b2677148efe5dceb02483b43399bd3765559994", |
| 747 | "a9f3900eed7a7e9e8f3b0eee0e660eca392e3cb736cae612f39e55dad696d382", |
| 748 | "1def10d1f8bbca52f5e6d8e7893ffbdcb491aafdc17bebf86f84d2d8480ed07a", |
| 749 | "7bf9209d20ef6e79429489d4cb7768281a2f7e32ec1830fd6f633238f521ba76", |
| 750 | "4902818100b2c3ce5751580b4e51df3fb175387f5c24b79040a4d6603c6265f7", |
| 751 | "0018b441ff3aef7d8e4cd2f480ec0906f1c4c0481304e8861f9d4693fa48e3a9", |
| 752 | "abc362859eeb343e1c5507ac94b5439ce7ac04154a2fb886a4819b2a57e18a2e", |
| 753 | "131b412ac4a09b004766959cdf357745f003e272aab3de02e2d5bc2af4ed7576", |
| 754 | "0858ab181902818061d19c2a8dcacde104b97f7c4fae11216157c1c0a258d882", |
| 755 | "984d12383a73dc56fe2ac93512bb321df9706ecdb2f70a44c949c4340a9fae64", |
| 756 | "a0646cf51f37c58c08bebde91667b3b2fa7c895f7983d4786c55261941b36545", |
| 757 | "33b0598383ebbcffcdf28b6cf13d376e3a70b49b14d8d06e8563a247f56a337e", |
| 758 | "3b9845b4f2b61356", |
| 759 | )) |
| 760 | .unwrap(), |
| 761 | hw_enforced: vec![], |
| 762 | sw_enforced: vec![ |
| 763 | KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::RSA) }, |
| 764 | KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(2048) }, |
| 765 | KeyParameter { |
| 766 | tag: Tag::RSA_PUBLIC_EXPONENT, |
| 767 | value: KPV::LongInteger(65537), |
| 768 | }, |
| 769 | KeyParameter { |
| 770 | tag: Tag::PURPOSE, |
| 771 | value: KPV::KeyPurpose(KeyPurpose::SIGN), |
| 772 | }, |
| 773 | KeyParameter { |
| 774 | tag: Tag::PURPOSE, |
| 775 | value: KPV::KeyPurpose(KeyPurpose::VERIFY), |
| 776 | }, |
| 777 | KeyParameter { tag: Tag::DIGEST, value: KPV::Digest(Digest::NONE) }, |
| 778 | KeyParameter { |
| 779 | tag: Tag::PADDING, |
| 780 | value: KPV::PaddingMode(PaddingMode::NONE), |
| 781 | }, |
| 782 | KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) }, |
| 783 | KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) }, |
| 784 | KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) }, |
| 785 | KeyParameter { |
| 786 | tag: Tag::CREATION_DATETIME, |
| 787 | value: KPV::DateTime(1628871769000), |
| 788 | }, |
| 789 | KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) }, |
| 790 | KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) }, |
| 791 | ], |
| 792 | }, |
| 793 | // No support for RSA keys in export_key(). |
| 794 | None, |
| 795 | ), |
| 796 | ]; |
| 797 | |
| 798 | for (input, want, want_format) in tests { |
| 799 | let input = hex::decode(input).unwrap(); |
| 800 | let got = KeyBlob::new_from_serialized(&input, &hidden).expect("invalid keyblob!"); |
| 801 | assert!(got == want); |
| 802 | |
| 803 | if let Some(want_format) = want_format { |
| 804 | let (got_format, _key_material, params) = |
| 805 | export_key(&input, &[]).expect("invalid keyblob!"); |
| 806 | assert_eq!(got_format, want_format); |
| 807 | // All the test cases are software-only keys. |
| 808 | assert_eq!(params, got.sw_enforced); |
| 809 | } |
| 810 | } |
| 811 | } |
| 812 | } |