blob: 62cff32b03ebbb298855c1fe9da157be592d1109 [file]
// Copyright 2022, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Helper functionality for working with legacy tag serialization.
use crate::{km_err, try_to_vec, vec_try, vec_try_with_capacity, Error, FallibleAllocExt};
use alloc::vec::Vec;
use core::cmp::Ordering;
use core::convert::{TryFrom, TryInto};
use kmr_wire::{
keymint::{
Algorithm, BlockMode, DateTime, Digest, EcCurve, KeyOrigin, KeyParam, KeyPurpose,
PaddingMode, Tag,
},
KeySizeInBits, RsaExponent,
};
/// Retrieve a `u8` from the start of the given slice, if possible.
pub fn consume_u8(data: &mut &[u8]) -> Result<u8, Error> {
match data.first() {
Some(b) => {
*data = &(*data)[1..];
Ok(*b)
}
None => Err(km_err!(InvalidKeyBlob, "failed to find 1 byte")),
}
}
/// Move past a bool value from the start of the given slice, if possible.
/// Bool values should only be included if `true`, so fail if the value
/// is anything other than 1.
pub fn consume_bool(data: &mut &[u8]) -> Result<(), Error> {
let b = consume_u8(data)?;
if b == 0x01 {
Ok(())
} else {
Err(km_err!(InvalidKeyBlob, "bool value other than 1 encountered"))
}
}
/// Retrieve a (host-ordered) `u32` from the start of the given slice, if possible.
pub fn consume_u32(data: &mut &[u8]) -> Result<u32, Error> {
if data.len() < 4 {
return Err(km_err!(InvalidKeyBlob, "failed to find 4 bytes"));
}
let chunk: [u8; 4] = data[..4].try_into().unwrap(); // safe: just checked
*data = &(*data)[4..];
Ok(u32::from_ne_bytes(chunk))
}
/// Retrieve a (host-ordered) `i32` from the start of the given slice, if possible.
pub fn consume_i32(data: &mut &[u8]) -> Result<i32, Error> {
if data.len() < 4 {
return Err(km_err!(InvalidKeyBlob, "failed to find 4 bytes"));
}
let chunk: [u8; 4] = data[..4].try_into().unwrap(); // safe: just checked
*data = &(*data)[4..];
Ok(i32::from_ne_bytes(chunk))
}
/// Retrieve a (host-ordered) `u64` from the start of the given slice, if possible.
pub fn consume_u64(data: &mut &[u8]) -> Result<u64, Error> {
if data.len() < 8 {
return Err(km_err!(InvalidKeyBlob, "failed to find 8 bytes"));
}
let chunk: [u8; 8] = data[..8].try_into().unwrap(); // safe: just checked
*data = &(*data)[8..];
Ok(u64::from_ne_bytes(chunk))
}
/// Retrieve a (host-ordered) `i64` from the start of the given slice, if possible.
pub fn consume_i64(data: &mut &[u8]) -> Result<i64, Error> {
if data.len() < 8 {
return Err(km_err!(InvalidKeyBlob, "failed to find 8 bytes"));
}
let chunk: [u8; 8] = data[..8].try_into().unwrap(); // safe: just checked
*data = &(*data)[8..];
Ok(i64::from_ne_bytes(chunk))
}
/// Retrieve a vector of bytes from the start of the given slice, if possible,
/// with the length of the data is expected to appear as a host-ordered `u32` prefix.
pub fn consume_vec(data: &mut &[u8]) -> Result<Vec<u8>, Error> {
let len = consume_u32(data)? as usize;
if len > data.len() {
return Err(km_err!(InvalidKeyBlob, "failed to find {} bytes", len));
}
let result = try_to_vec(&data[..len])?;
*data = &(*data)[len..];
Ok(result)
}
/// Serialize a collection of [`KeyParam`]s into a format that is compatible with previous
/// implementations:
///
/// ```text
/// [0..4] Size B of `TagType::Bytes` data, in host order.
/// [4..4+B] (*) Concatenated contents of each `TagType::Bytes` tag.
/// [4+B..4+B+4] Count N of the number of parameters, in host order.
/// [8+B..8+B+4] Size Z of encoded parameters.
/// [12+B..12+B+Z] Serialized parameters one after another.
/// ```
///
/// Individual parameters are serialized in the last chunk as:
///
/// ```text
/// [0..4] Tag number, in host order.
/// Followed by one of the following depending on the tag's `TagType`; all integers in host order:
/// [4..5] Bool value (`TagType::Bool`)
/// [4..8] i32 values (`TagType::Uint[Rep]`, `TagType::Enum[Rep]`)
/// [4..12] i64 values, in host order (`TagType::UlongRep`, `TagType::Date`)
/// [4..8] + [8..12] Size + offset of data in (*) above (`TagType::Bytes`, `TagType::Bignum`)
/// ```
pub fn serialize(params: &[KeyParam]) -> Result<Vec<u8>, Error> {
// First 4 bytes are the length of the combined [`TagType::Bytes`] data; come back to set that
// in a moment.
let mut result = vec_try![0; 4]?;
// Next append the contents of all of the [`TagType::Bytes`] data.
let mut blob_size = 0u32;
for param in params {
match param {
// Variants that hold `Vec<u8>`.
KeyParam::ApplicationId(v)
| KeyParam::ApplicationData(v)
| KeyParam::AttestationChallenge(v)
| KeyParam::AttestationApplicationId(v)
| KeyParam::AttestationIdBrand(v)
| KeyParam::AttestationIdDevice(v)
| KeyParam::AttestationIdProduct(v)
| KeyParam::AttestationIdSerial(v)
| KeyParam::AttestationIdImei(v)
| KeyParam::AttestationIdSecondImei(v)
| KeyParam::AttestationIdMeid(v)
| KeyParam::AttestationIdManufacturer(v)
| KeyParam::AttestationIdModel(v)
| KeyParam::Nonce(v)
| KeyParam::RootOfTrust(v)
| KeyParam::CertificateSerial(v)
| KeyParam::CertificateSubject(v)
| KeyParam::ModuleHash(v) => {
result.try_extend_from_slice(v)?;
blob_size += v.len() as u32;
}
_ => {}
}
}
// Go back and fill in the combined blob length in native order at the start.
result[..4].clone_from_slice(&blob_size.to_ne_bytes());
result.try_extend_from_slice(&(params.len() as u32).to_ne_bytes())?;
let params_size_offset = result.len();
result.try_extend_from_slice(&[0u8; 4])?; // placeholder for size of elements
let first_param_offset = result.len();
let mut blob_offset = 0u32;
for param in params {
result.try_extend_from_slice(&(param.tag() as u32).to_ne_bytes())?;
match &param {
// Enum-holding variants.
KeyParam::Purpose(v) => result.try_extend_from_slice(&(*v as u32).to_ne_bytes())?,
KeyParam::Algorithm(v) => result.try_extend_from_slice(&(*v as u32).to_ne_bytes())?,
KeyParam::BlockMode(v) => result.try_extend_from_slice(&(*v as u32).to_ne_bytes())?,
KeyParam::Digest(v) => result.try_extend_from_slice(&(*v as u32).to_ne_bytes())?,
KeyParam::Padding(v) => result.try_extend_from_slice(&(*v as u32).to_ne_bytes())?,
KeyParam::EcCurve(v) => result.try_extend_from_slice(&(*v as u32).to_ne_bytes())?,
KeyParam::RsaOaepMgfDigest(v) => {
result.try_extend_from_slice(&(*v as u32).to_ne_bytes())?
}
KeyParam::Origin(v) => result.try_extend_from_slice(&(*v as u32).to_ne_bytes())?,
// `u32`-holding variants.
KeyParam::KeySize(v) => result.try_extend_from_slice(&(v.0).to_ne_bytes())?,
KeyParam::MinMacLength(v)
| KeyParam::MaxUsesPerBoot(v)
| KeyParam::UsageCountLimit(v)
| KeyParam::UserId(v)
| KeyParam::UserAuthType(v)
| KeyParam::AuthTimeout(v)
| KeyParam::OsVersion(v)
| KeyParam::OsPatchlevel(v)
| KeyParam::VendorPatchlevel(v)
| KeyParam::BootPatchlevel(v)
| KeyParam::MacLength(v)
| KeyParam::MaxBootLevel(v) => result.try_extend_from_slice(&v.to_ne_bytes())?,
// `u64`-holding variants.
KeyParam::RsaPublicExponent(v) => result.try_extend_from_slice(&(v.0).to_ne_bytes())?,
KeyParam::UserSecureId(v) => result.try_extend_from_slice(&(*v).to_ne_bytes())?,
// `true`-holding variants.
KeyParam::CallerNonce
| KeyParam::IncludeUniqueId
| KeyParam::BootloaderOnly
| KeyParam::RollbackResistance
| KeyParam::EarlyBootOnly
| KeyParam::AllowWhileOnBody
| KeyParam::NoAuthRequired
| KeyParam::TrustedUserPresenceRequired
| KeyParam::TrustedConfirmationRequired
| KeyParam::UnlockedDeviceRequired
| KeyParam::DeviceUniqueAttestation
| KeyParam::StorageKey
| KeyParam::ResetSinceIdRotation => result.try_push(0x01u8)?,
// `DateTime`-holding variants.
KeyParam::ActiveDatetime(v)
| KeyParam::OriginationExpireDatetime(v)
| KeyParam::UsageExpireDatetime(v)
| KeyParam::CreationDatetime(v)
| KeyParam::CertificateNotBefore(v)
| KeyParam::CertificateNotAfter(v) => {
result.try_extend_from_slice(&(v.ms_since_epoch as u64).to_ne_bytes())?
}
// `Vec<u8>`-holding variants.
KeyParam::ApplicationId(v)
| KeyParam::ApplicationData(v)
| KeyParam::AttestationChallenge(v)
| KeyParam::AttestationApplicationId(v)
| KeyParam::AttestationIdBrand(v)
| KeyParam::AttestationIdDevice(v)
| KeyParam::AttestationIdProduct(v)
| KeyParam::AttestationIdSerial(v)
| KeyParam::AttestationIdImei(v)
| KeyParam::AttestationIdSecondImei(v)
| KeyParam::AttestationIdMeid(v)
| KeyParam::AttestationIdManufacturer(v)
| KeyParam::AttestationIdModel(v)
| KeyParam::Nonce(v)
| KeyParam::RootOfTrust(v)
| KeyParam::CertificateSerial(v)
| KeyParam::CertificateSubject(v)
| KeyParam::ModuleHash(v) => {
let blob_len = v.len() as u32;
result.try_extend_from_slice(&blob_len.to_ne_bytes())?;
result.try_extend_from_slice(&blob_offset.to_ne_bytes())?;
blob_offset += blob_len;
}
}
}
let serialized_size = (result.len() - first_param_offset) as u32;
// Go back and fill in the total serialized size.
result[params_size_offset..params_size_offset + 4]
.clone_from_slice(&serialized_size.to_ne_bytes());
Ok(result)
}
/// Retrieve the contents of a tag of `TagType::Bytes`. The `data` parameter holds
/// the as-yet unparsed data, and a length and offset are read from this (and consumed).
/// This length and offset refer to a location in the combined `blob_data`; however,
/// the offset is expected to be the next unconsumed chunk of `blob_data`, as indicated
/// by `next_blob_offset` (which itself is updated as a result of consuming the data).
fn consume_blob(
data: &mut &[u8],
next_blob_offset: &mut usize,
blob_data: &[u8],
) -> Result<Vec<u8>, Error> {
let data_len = consume_u32(data)? as usize;
let data_offset = consume_u32(data)? as usize;
// Expect the blob data to come from the next offset in the initial blob chunk.
if data_offset != *next_blob_offset {
return Err(km_err!(
InvalidKeyBlob,
"got blob offset {} instead of {}",
data_offset,
next_blob_offset
));
}
if (data_offset + data_len) > blob_data.len() {
return Err(km_err!(
InvalidKeyBlob,
"blob at offset [{}..{}+{}] goes beyond blob data size {}",
data_offset,
data_offset,
data_len,
blob_data.len(),
));
}
let slice = &blob_data[data_offset..data_offset + data_len];
*next_blob_offset += data_len;
try_to_vec(slice)
}
/// Deserialize a collection of [`KeyParam`]s in legacy serialized format. The provided slice is
/// modified to contain the unconsumed part of the data.
pub fn deserialize(data: &mut &[u8]) -> Result<Vec<KeyParam>, Error> {
let blob_data_size = consume_u32(data)? as usize;
let blob_data = &data[..blob_data_size];
let mut next_blob_offset = 0;
// Move past the blob data.
*data = &data[blob_data_size..];
let param_count = consume_u32(data)? as usize;
let param_size = consume_u32(data)? as usize;
if param_size > data.len() {
return Err(km_err!(
InvalidKeyBlob,
"size mismatch 4+{}+4+4+{} > {}",
blob_data_size,
param_size,
data.len()
));
}
let mut results = vec_try_with_capacity!(param_count)?;
for _i in 0..param_count {
let tag_num = consume_u32(data)? as i32;
let tag = <Tag>::try_from(tag_num)
.map_err(|_e| km_err!(InvalidKeyBlob, "unknown tag {} encountered", tag_num))?;
let enum_err = |_e| km_err!(InvalidKeyBlob, "unknown enum value for {:?}", tag);
results.try_push(match tag {
// Enum-holding variants.
Tag::Purpose => {
KeyParam::Purpose(<KeyPurpose>::try_from(consume_i32(data)?).map_err(enum_err)?)
}
Tag::Algorithm => {
KeyParam::Algorithm(<Algorithm>::try_from(consume_i32(data)?).map_err(enum_err)?)
}
Tag::BlockMode => {
KeyParam::BlockMode(<BlockMode>::try_from(consume_i32(data)?).map_err(enum_err)?)
}
Tag::Digest => {
KeyParam::Digest(<Digest>::try_from(consume_i32(data)?).map_err(enum_err)?)
}
Tag::Padding => {
KeyParam::Padding(<PaddingMode>::try_from(consume_i32(data)?).map_err(enum_err)?)
}
Tag::EcCurve => {
KeyParam::EcCurve(<EcCurve>::try_from(consume_i32(data)?).map_err(enum_err)?)
}
Tag::RsaOaepMgfDigest => KeyParam::RsaOaepMgfDigest(
<Digest>::try_from(consume_i32(data)?).map_err(enum_err)?,
),
Tag::Origin => {
KeyParam::Origin(<KeyOrigin>::try_from(consume_i32(data)?).map_err(enum_err)?)
}
// `u32`-holding variants.
Tag::KeySize => KeyParam::KeySize(KeySizeInBits(consume_u32(data)?)),
Tag::MinMacLength => KeyParam::MinMacLength(consume_u32(data)?),
Tag::MaxUsesPerBoot => KeyParam::MaxUsesPerBoot(consume_u32(data)?),
Tag::UsageCountLimit => KeyParam::UsageCountLimit(consume_u32(data)?),
Tag::UserId => KeyParam::UserId(consume_u32(data)?),
Tag::UserAuthType => KeyParam::UserAuthType(consume_u32(data)?),
Tag::AuthTimeout => KeyParam::AuthTimeout(consume_u32(data)?),
Tag::OsVersion => KeyParam::OsVersion(consume_u32(data)?),
Tag::OsPatchlevel => KeyParam::OsPatchlevel(consume_u32(data)?),
Tag::VendorPatchlevel => KeyParam::VendorPatchlevel(consume_u32(data)?),
Tag::BootPatchlevel => KeyParam::BootPatchlevel(consume_u32(data)?),
Tag::MacLength => KeyParam::MacLength(consume_u32(data)?),
Tag::MaxBootLevel => KeyParam::MaxBootLevel(consume_u32(data)?),
// `u64`-holding variants.
Tag::RsaPublicExponent => KeyParam::RsaPublicExponent(RsaExponent(consume_u64(data)?)),
Tag::UserSecureId => KeyParam::UserSecureId(consume_u64(data)?),
// `true`-holding variants.
Tag::CallerNonce => {
consume_bool(data)?;
KeyParam::CallerNonce
}
Tag::IncludeUniqueId => {
consume_bool(data)?;
KeyParam::IncludeUniqueId
}
Tag::BootloaderOnly => {
consume_bool(data)?;
KeyParam::BootloaderOnly
}
Tag::RollbackResistance => {
consume_bool(data)?;
KeyParam::RollbackResistance
}
Tag::EarlyBootOnly => {
consume_bool(data)?;
KeyParam::EarlyBootOnly
}
Tag::AllowWhileOnBody => {
consume_bool(data)?;
KeyParam::AllowWhileOnBody
}
Tag::NoAuthRequired => {
consume_bool(data)?;
KeyParam::NoAuthRequired
}
Tag::TrustedUserPresenceRequired => {
consume_bool(data)?;
KeyParam::TrustedUserPresenceRequired
}
Tag::TrustedConfirmationRequired => {
consume_bool(data)?;
KeyParam::TrustedConfirmationRequired
}
Tag::UnlockedDeviceRequired => {
consume_bool(data)?;
KeyParam::UnlockedDeviceRequired
}
Tag::DeviceUniqueAttestation => {
consume_bool(data)?;
KeyParam::DeviceUniqueAttestation
}
Tag::StorageKey => {
consume_bool(data)?;
KeyParam::StorageKey
}
Tag::ResetSinceIdRotation => {
consume_bool(data)?;
KeyParam::ResetSinceIdRotation
}
// `DateTime`-holding variants.
Tag::ActiveDatetime => {
KeyParam::ActiveDatetime(DateTime { ms_since_epoch: consume_i64(data)? })
}
Tag::OriginationExpireDatetime => {
KeyParam::OriginationExpireDatetime(DateTime { ms_since_epoch: consume_i64(data)? })
}
Tag::UsageExpireDatetime => {
KeyParam::UsageExpireDatetime(DateTime { ms_since_epoch: consume_i64(data)? })
}
Tag::CreationDatetime => {
KeyParam::CreationDatetime(DateTime { ms_since_epoch: consume_i64(data)? })
}
Tag::CertificateNotBefore => {
KeyParam::CertificateNotBefore(DateTime { ms_since_epoch: consume_i64(data)? })
}
Tag::CertificateNotAfter => {
KeyParam::CertificateNotAfter(DateTime { ms_since_epoch: consume_i64(data)? })
}
// `Vec<u8>`-holding variants.
Tag::ApplicationId => {
KeyParam::ApplicationId(consume_blob(data, &mut next_blob_offset, blob_data)?)
}
Tag::ApplicationData => {
KeyParam::ApplicationData(consume_blob(data, &mut next_blob_offset, blob_data)?)
}
Tag::AttestationChallenge => KeyParam::AttestationChallenge(consume_blob(
data,
&mut next_blob_offset,
blob_data,
)?),
Tag::AttestationApplicationId => KeyParam::AttestationApplicationId(consume_blob(
data,
&mut next_blob_offset,
blob_data,
)?),
Tag::AttestationIdBrand => {
KeyParam::AttestationIdBrand(consume_blob(data, &mut next_blob_offset, blob_data)?)
}
Tag::AttestationIdDevice => {
KeyParam::AttestationIdDevice(consume_blob(data, &mut next_blob_offset, blob_data)?)
}
Tag::AttestationIdProduct => KeyParam::AttestationIdProduct(consume_blob(
data,
&mut next_blob_offset,
blob_data,
)?),
Tag::AttestationIdSerial => {
KeyParam::AttestationIdSerial(consume_blob(data, &mut next_blob_offset, blob_data)?)
}
Tag::AttestationIdImei => {
KeyParam::AttestationIdImei(consume_blob(data, &mut next_blob_offset, blob_data)?)
}
Tag::AttestationIdSecondImei => KeyParam::AttestationIdSecondImei(consume_blob(
data,
&mut next_blob_offset,
blob_data,
)?),
Tag::AttestationIdMeid => {
KeyParam::AttestationIdMeid(consume_blob(data, &mut next_blob_offset, blob_data)?)
}
Tag::AttestationIdManufacturer => KeyParam::AttestationIdManufacturer(consume_blob(
data,
&mut next_blob_offset,
blob_data,
)?),
Tag::AttestationIdModel => {
KeyParam::AttestationIdModel(consume_blob(data, &mut next_blob_offset, blob_data)?)
}
Tag::Nonce => KeyParam::Nonce(consume_blob(data, &mut next_blob_offset, blob_data)?),
Tag::RootOfTrust => {
KeyParam::RootOfTrust(consume_blob(data, &mut next_blob_offset, blob_data)?)
}
Tag::CertificateSerial => {
KeyParam::CertificateSerial(consume_blob(data, &mut next_blob_offset, blob_data)?)
}
Tag::CertificateSubject => {
KeyParam::CertificateSubject(consume_blob(data, &mut next_blob_offset, blob_data)?)
}
Tag::ModuleHash => {
KeyParam::ModuleHash(consume_blob(data, &mut next_blob_offset, blob_data)?)
}
// Invalid variants.
Tag::Invalid
| Tag::HardwareType
| Tag::MinSecondsBetweenOps
| Tag::UniqueId
| Tag::IdentityCredentialKey
| Tag::AssociatedData
| Tag::ConfirmationToken => {
return Err(km_err!(InvalidKeyBlob, "invalid tag {:?} encountered", tag));
}
})?;
}
Ok(results)
}
/// Determine ordering of two [`KeyParam`] values for legacy key blob ordering.
/// Invalid parameters are likely to compare equal.
pub fn param_compare(left: &KeyParam, right: &KeyParam) -> Ordering {
match (left, right) {
(KeyParam::Purpose(l), KeyParam::Purpose(r)) => l.cmp(r),
(KeyParam::Algorithm(l), KeyParam::Algorithm(r)) => l.cmp(r),
(KeyParam::KeySize(l), KeyParam::KeySize(r)) => l.cmp(r),
(KeyParam::BlockMode(l), KeyParam::BlockMode(r)) => l.cmp(r),
(KeyParam::Digest(l), KeyParam::Digest(r)) => l.cmp(r),
(KeyParam::Padding(l), KeyParam::Padding(r)) => l.cmp(r),
(KeyParam::CallerNonce, KeyParam::CallerNonce) => Ordering::Equal,
(KeyParam::MinMacLength(l), KeyParam::MinMacLength(r)) => l.cmp(r),
(KeyParam::EcCurve(l), KeyParam::EcCurve(r)) => l.cmp(r),
(KeyParam::RsaPublicExponent(l), KeyParam::RsaPublicExponent(r)) => l.cmp(r),
(KeyParam::IncludeUniqueId, KeyParam::IncludeUniqueId) => Ordering::Equal,
(KeyParam::RsaOaepMgfDigest(l), KeyParam::RsaOaepMgfDigest(r)) => l.cmp(r),
(KeyParam::BootloaderOnly, KeyParam::BootloaderOnly) => Ordering::Equal,
(KeyParam::RollbackResistance, KeyParam::RollbackResistance) => Ordering::Equal,
(KeyParam::EarlyBootOnly, KeyParam::EarlyBootOnly) => Ordering::Equal,
(KeyParam::ActiveDatetime(l), KeyParam::ActiveDatetime(r)) => l.cmp(r),
(KeyParam::OriginationExpireDatetime(l), KeyParam::OriginationExpireDatetime(r)) => {
l.cmp(r)
}
(KeyParam::UsageExpireDatetime(l), KeyParam::UsageExpireDatetime(r)) => l.cmp(r),
(KeyParam::MaxUsesPerBoot(l), KeyParam::MaxUsesPerBoot(r)) => l.cmp(r),
(KeyParam::UsageCountLimit(l), KeyParam::UsageCountLimit(r)) => l.cmp(r),
(KeyParam::UserId(l), KeyParam::UserId(r)) => l.cmp(r),
(KeyParam::UserSecureId(l), KeyParam::UserSecureId(r)) => l.cmp(r),
(KeyParam::NoAuthRequired, KeyParam::NoAuthRequired) => Ordering::Equal,
(KeyParam::UserAuthType(l), KeyParam::UserAuthType(r)) => l.cmp(r),
(KeyParam::AuthTimeout(l), KeyParam::AuthTimeout(r)) => l.cmp(r),
(KeyParam::AllowWhileOnBody, KeyParam::AllowWhileOnBody) => Ordering::Equal,
(KeyParam::TrustedUserPresenceRequired, KeyParam::TrustedUserPresenceRequired) => {
Ordering::Equal
}
(KeyParam::TrustedConfirmationRequired, KeyParam::TrustedConfirmationRequired) => {
Ordering::Equal
}
(KeyParam::UnlockedDeviceRequired, KeyParam::UnlockedDeviceRequired) => Ordering::Equal,
(KeyParam::ApplicationId(l), KeyParam::ApplicationId(r)) => l.cmp(r),
(KeyParam::ApplicationData(l), KeyParam::ApplicationData(r)) => l.cmp(r),
(KeyParam::CreationDatetime(l), KeyParam::CreationDatetime(r)) => l.cmp(r),
(KeyParam::Origin(l), KeyParam::Origin(r)) => l.cmp(r),
(KeyParam::RootOfTrust(l), KeyParam::RootOfTrust(r)) => l.cmp(r),
(KeyParam::OsVersion(l), KeyParam::OsVersion(r)) => l.cmp(r),
(KeyParam::OsPatchlevel(l), KeyParam::OsPatchlevel(r)) => l.cmp(r),
(KeyParam::AttestationChallenge(l), KeyParam::AttestationChallenge(r)) => l.cmp(r),
(KeyParam::AttestationApplicationId(l), KeyParam::AttestationApplicationId(r)) => l.cmp(r),
(KeyParam::AttestationIdBrand(l), KeyParam::AttestationIdBrand(r)) => l.cmp(r),
(KeyParam::AttestationIdDevice(l), KeyParam::AttestationIdDevice(r)) => l.cmp(r),
(KeyParam::AttestationIdProduct(l), KeyParam::AttestationIdProduct(r)) => l.cmp(r),
(KeyParam::AttestationIdSerial(l), KeyParam::AttestationIdSerial(r)) => l.cmp(r),
(KeyParam::AttestationIdImei(l), KeyParam::AttestationIdImei(r)) => l.cmp(r),
(KeyParam::AttestationIdSecondImei(l), KeyParam::AttestationIdSecondImei(r)) => l.cmp(r),
(KeyParam::AttestationIdMeid(l), KeyParam::AttestationIdMeid(r)) => l.cmp(r),
(KeyParam::AttestationIdManufacturer(l), KeyParam::AttestationIdManufacturer(r)) => {
l.cmp(r)
}
(KeyParam::AttestationIdModel(l), KeyParam::AttestationIdModel(r)) => l.cmp(r),
(KeyParam::VendorPatchlevel(l), KeyParam::VendorPatchlevel(r)) => l.cmp(r),
(KeyParam::BootPatchlevel(l), KeyParam::BootPatchlevel(r)) => l.cmp(r),
(KeyParam::DeviceUniqueAttestation, KeyParam::DeviceUniqueAttestation) => Ordering::Equal,
(KeyParam::StorageKey, KeyParam::StorageKey) => Ordering::Equal,
(KeyParam::Nonce(l), KeyParam::Nonce(r)) => l.cmp(r),
(KeyParam::MacLength(l), KeyParam::MacLength(r)) => l.cmp(r),
(KeyParam::ResetSinceIdRotation, KeyParam::ResetSinceIdRotation) => Ordering::Equal,
(KeyParam::CertificateSerial(l), KeyParam::CertificateSerial(r)) => l.cmp(r),
(KeyParam::CertificateSubject(l), KeyParam::CertificateSubject(r)) => l.cmp(r),
(KeyParam::CertificateNotBefore(l), KeyParam::CertificateNotBefore(r)) => l.cmp(r),
(KeyParam::CertificateNotAfter(l), KeyParam::CertificateNotAfter(r)) => l.cmp(r),
(KeyParam::MaxBootLevel(l), KeyParam::MaxBootLevel(r)) => l.cmp(r),
(KeyParam::ModuleHash(l), KeyParam::ModuleHash(r)) => l.cmp(r),
(left, right) => left.tag().cmp(&right.tag()),
}
}