// 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 tags. use crate::{ crypto, crypto::{rsa::DecryptionMode, *}, km_err, try_to_vec, vec_try_with_capacity, Error, FallibleAllocExt, }; use alloc::vec::Vec; use kmr_wire::{ keymint::{ Algorithm, BlockMode, Digest, EcCurve, ErrorCode, KeyCharacteristics, KeyFormat, KeyParam, KeyPurpose, PaddingMode, SecurityLevel, Tag, DEFAULT_CERT_SERIAL, DEFAULT_CERT_SUBJECT, }, KeySizeInBits, }; use log::{info, warn}; mod info; pub use info::*; pub mod legacy; #[cfg(test)] mod tests; /// The set of tags that are directly copied from key generation/import parameters to /// key characteristics without being checked. pub const UNPOLICED_COPYABLE_TAGS: &[Tag] = &[ Tag::RollbackResistance, Tag::EarlyBootOnly, Tag::MaxUsesPerBoot, Tag::UserSecureId, // repeatable Tag::NoAuthRequired, Tag::UserAuthType, Tag::AuthTimeout, Tag::TrustedUserPresenceRequired, Tag::TrustedConfirmationRequired, Tag::UnlockedDeviceRequired, Tag::StorageKey, ]; /// Indication of whether secure storage is available. #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum SecureStorage { /// Device has secure storage. Available, /// Device does not have secure storage. Unavailable, } /// Macro to retrieve a copy of the (single) value of a tag in a collection of `KeyParam`s. There /// can be only one. Only works for variants whose data type implements `Copy`. #[macro_export] macro_rules! get_tag_value { { $params:expr, $variant:ident, $err:expr } => { { let mut result = None; let mut count = 0; for param in $params { if let kmr_wire::keymint::KeyParam::$variant(v) = param { count += 1; result = Some(*v); } } match count { 0 => Err($crate::km_verr!($err, "missing tag {}", stringify!($variant))), 1 => Ok(result.unwrap()), /* safe: count=1 => exists */ _ => Err($crate::km_verr!($err, "duplicate tag {}", stringify!($variant))), } } } } /// Macro to retrieve the value of an optional single-valued tag in a collection of `KeyParam`s. It /// may or may not be present, but multiple instances of the tag are assumed to be invalid. #[macro_export] macro_rules! get_opt_tag_value { { $params:expr, $variant:ident } => { get_opt_tag_value!($params, $variant, InvalidTag) }; { $params:expr, $variant:ident, $dup_error:ident } => { { let mut result = None; let mut count = 0; for param in $params { if let kmr_wire::keymint::KeyParam::$variant(v) = param { count += 1; result = Some(v); } } match count { 0 => Ok(None), 1 => Ok(Some(result.unwrap())), /* safe: count=1 => exists */ _ => Err($crate::km_err!($dup_error, "duplicate tag {}", stringify!($variant))), } } } } /// Macro to retrieve a `bool` tag value, returning `false` if the tag is absent #[macro_export] macro_rules! get_bool_tag_value { { $params:expr, $variant:ident } => { { let mut count = 0; for param in $params { if let kmr_wire::keymint::KeyParam::$variant = param { count += 1; } } match count { 0 => Ok(false), 1 => Ok(true), _ => Err($crate::km_err!(InvalidTag, "duplicate tag {}", stringify!($variant))), } } } } /// Macro to check a collection of `KeyParam`s holds a value matching the given value. #[macro_export] macro_rules! contains_tag_value { { $params:expr, $variant:ident, $value:expr } => { { let mut found = false; for param in $params { if let kmr_wire::keymint::KeyParam::$variant(v) = param { if *v == $value { found = true; } } } found } } } /// Check that a set of [`KeyParam`]s is valid when considered as key characteristics. pub fn characteristics_valid(characteristics: &[KeyParam]) -> Result<(), Error> { let mut dup_checker = DuplicateTagChecker::default(); for param in characteristics { let tag = param.tag(); dup_checker.add(tag)?; if info(tag)?.characteristic == Characteristic::NotKeyCharacteristic { return Err( km_err!(InvalidKeyBlob, "tag {:?} is not a valid key characteristic", tag,), ); } } Ok(()) } /// Copy anything in `src` that matches `tags` into `dest`. Fails if any non-repeatable /// tags occur more than once with a different value. pub fn transcribe_tags( dest: &mut Vec, src: &[KeyParam], tags: &[Tag], ) -> Result<(), Error> { let mut dup_checker = DuplicateTagChecker::default(); for param in src { let tag = param.tag(); dup_checker.add(tag)?; if tags.iter().any(|t| *t == tag) { dest.try_push(param.clone())?; } } Ok(()) } /// Get the configured algorithm from a set of parameters. pub fn get_algorithm(params: &[KeyParam]) -> Result { get_tag_value!(params, Algorithm, ErrorCode::UnsupportedAlgorithm) } /// Get the configured block mode from a set of parameters. pub fn get_block_mode(params: &[KeyParam]) -> Result { get_tag_value!(params, BlockMode, ErrorCode::UnsupportedBlockMode) } /// Get the configured padding mode from a set of parameters. pub fn get_padding_mode(params: &[KeyParam]) -> Result { get_tag_value!(params, Padding, ErrorCode::UnsupportedPaddingMode) } /// Get the configured digest from a set of parameters. pub fn get_digest(params: &[KeyParam]) -> Result { get_tag_value!(params, Digest, ErrorCode::UnsupportedDigest) } /// Get the configured elliptic curve from a set of parameters. pub fn get_ec_curve(params: &[KeyParam]) -> Result { get_tag_value!(params, EcCurve, ErrorCode::UnsupportedKeySize) } /// Get the configured MGF digest from a set of parameters. If no MGF digest is specified, /// a default value of SHA1 is returned. pub fn get_mgf_digest(params: &[KeyParam]) -> Result { Ok(*get_opt_tag_value!(params, RsaOaepMgfDigest)?.unwrap_or(&Digest::Sha1)) } /// Get the certificate serial number from a set of parameters, falling back to default value of 1 /// if not specified pub fn get_cert_serial(params: &[KeyParam]) -> Result<&[u8], Error> { Ok(get_opt_tag_value!(params, CertificateSerial)? .map(Vec::as_ref) .unwrap_or(DEFAULT_CERT_SERIAL)) } /// Return the set of key parameters at the provided security level. pub fn characteristics_at( chars: &[KeyCharacteristics], sec_level: SecurityLevel, ) -> Result<&[KeyParam], Error> { let mut result: Option<&[KeyParam]> = None; for chars in chars { if chars.security_level != sec_level { continue; } if result.is_none() { result = Some(&chars.authorizations); } else { return Err(km_err!(InvalidKeyBlob, "multiple key characteristics at {:?}", sec_level)); } } result.ok_or_else(|| { km_err!(InvalidKeyBlob, "no parameters at security level {:?} found", sec_level) }) } /// Get the certificate subject from a set of parameters, falling back to a default if not /// specified. pub fn get_cert_subject(params: &[KeyParam]) -> Result<&[u8], Error> { Ok(get_opt_tag_value!(params, CertificateSubject)? .map(Vec::as_ref) .unwrap_or(DEFAULT_CERT_SUBJECT)) } /// Build the parameters that are used as the hidden input to KEK derivation calculations: /// - `ApplicationId(data)` if present /// - `ApplicationData(data)` if present /// - `RootOfTrust(rot)` where `rot` is a hardcoded root of trust pub fn hidden(params: &[KeyParam], rot: &[u8]) -> Result, Error> { let mut results = vec_try_with_capacity!(3)?; if let Ok(Some(app_id)) = get_opt_tag_value!(params, ApplicationId) { results.push(KeyParam::ApplicationId(try_to_vec(app_id)?)); } if let Ok(Some(app_data)) = get_opt_tag_value!(params, ApplicationData) { results.push(KeyParam::ApplicationData(try_to_vec(app_data)?)); } results.push(KeyParam::RootOfTrust(try_to_vec(rot)?)); Ok(results) } /// Build the set of key characteristics for a key that is about to be generated, /// checking parameter validity along the way. Also return the information needed for key /// generation. pub fn extract_key_gen_characteristics( secure_storage: SecureStorage, params: &[KeyParam], sec_level: SecurityLevel, ) -> Result<(Vec, KeyGenInfo), Error> { let keygen_info = match get_algorithm(params)? { Algorithm::Rsa => check_rsa_gen_params(params, sec_level), Algorithm::Ec => check_ec_gen_params(params, sec_level), Algorithm::Aes => check_aes_gen_params(params, sec_level), Algorithm::TripleDes => check_3des_gen_params(params), Algorithm::Hmac => check_hmac_gen_params(params, sec_level), }?; Ok((extract_key_characteristics(secure_storage, params, &[], sec_level)?, keygen_info)) } /// Build the set of key characteristics for a key that is about to be imported, /// checking parameter validity along the way. pub fn extract_key_import_characteristics( imp: &crypto::Implementation, secure_storage: SecureStorage, params: &[KeyParam], sec_level: SecurityLevel, key_format: KeyFormat, key_data: &[u8], ) -> Result<(Vec, KeyMaterial), Error> { let (deduced_params, key_material) = match get_algorithm(params)? { Algorithm::Rsa => { check_rsa_import_params(&*imp.rsa, params, sec_level, key_format, key_data) } Algorithm::Ec => check_ec_import_params(&*imp.ec, params, sec_level, key_format, key_data), Algorithm::Aes => { check_aes_import_params(&*imp.aes, params, sec_level, key_format, key_data) } Algorithm::TripleDes => check_3des_import_params(&*imp.des, params, key_format, key_data), Algorithm::Hmac => { check_hmac_import_params(&*imp.hmac, params, sec_level, key_format, key_data) } }?; Ok(( extract_key_characteristics(secure_storage, params, &deduced_params, sec_level)?, key_material, )) } /// Build the set of key characteristics for a key that is about to be generated or imported, /// checking parameter validity along the way. The `extra_params` argument provides additional /// parameters on top of `params`, such as those deduced from imported key material. fn extract_key_characteristics( secure_storage: SecureStorage, params: &[KeyParam], extra_params: &[KeyParam], sec_level: SecurityLevel, ) -> Result, Error> { // Separately accumulate any characteristics that are policed by Keystore. let mut chars = Vec::new(); let mut keystore_chars = Vec::new(); for param in params.iter().chain(extra_params) { let tag = param.tag(); // Input params should not contain anything that KeyMint adds itself. if AUTO_ADDED_CHARACTERISTICS.contains(&tag) { return Err(km_err!(InvalidTag, "KeyMint-added tag included on key generation/import")); } if sec_level == SecurityLevel::Strongbox && [Tag::MaxUsesPerBoot, Tag::RollbackResistance].contains(&tag) { // StrongBox does not support tags that require per-key storage. return Err(km_err!(InvalidTag, "tag {:?} not allowed in StrongBox", param.tag())); } // UsageCountLimit is peculiar. If its value is > 1, it should be Keystore-enforced. // If its value is = 1, then it is KeyMint-enforced if secure storage is available, // and Keystore-enforced otherwise. if let KeyParam::UsageCountLimit(use_limit) = param { match (use_limit, secure_storage) { (1, SecureStorage::Available) => { chars.try_push(KeyParam::UsageCountLimit(*use_limit))? } (1, SecureStorage::Unavailable) | (_, _) => { keystore_chars.try_push(KeyParam::UsageCountLimit(*use_limit))? } } } if KEYMINT_ENFORCED_CHARACTERISTICS.contains(&tag) { chars.try_push(param.clone())?; } else if KEYSTORE_ENFORCED_CHARACTERISTICS.contains(&tag) { keystore_chars.try_push(param.clone())?; } } reject_incompatible_auth(&chars)?; // Use the same sort order for tags as was previously used. chars.sort_by(legacy::param_compare); keystore_chars.sort_by(legacy::param_compare); let mut result = Vec::new(); result.try_push(KeyCharacteristics { security_level: sec_level, authorizations: chars })?; if !keystore_chars.is_empty() { result.try_push(KeyCharacteristics { security_level: SecurityLevel::Keystore, authorizations: keystore_chars, })?; } Ok(result) } /// Check that an RSA key size is valid. fn check_rsa_key_size(key_size: KeySizeInBits, sec_level: SecurityLevel) -> Result<(), Error> { // StrongBox only supports 2048-bit keys. match key_size { KeySizeInBits(512) if sec_level != SecurityLevel::Strongbox => Ok(()), KeySizeInBits(768) if sec_level != SecurityLevel::Strongbox => Ok(()), KeySizeInBits(1024) if sec_level != SecurityLevel::Strongbox => Ok(()), KeySizeInBits(2048) => Ok(()), KeySizeInBits(3072) if sec_level != SecurityLevel::Strongbox => Ok(()), KeySizeInBits(4096) if sec_level != SecurityLevel::Strongbox => Ok(()), _ => Err(km_err!(UnsupportedKeySize, "unsupported KEY_SIZE {:?} bits for RSA", key_size)), } } /// Check RSA key generation parameter validity. fn check_rsa_gen_params( params: &[KeyParam], sec_level: SecurityLevel, ) -> Result { // For key generation, size and public exponent must be explicitly specified. let key_size = get_tag_value!(params, KeySize, ErrorCode::UnsupportedKeySize)?; check_rsa_key_size(key_size, sec_level)?; let public_exponent = get_tag_value!(params, RsaPublicExponent, ErrorCode::InvalidArgument)?; check_rsa_params(params)?; Ok(KeyGenInfo::Rsa(key_size, public_exponent)) } /// Check RSA key import parameter validity. Return the key material along with any key generation /// parameters that have been deduced from the key material (but which are not present in the input /// key parameters). fn check_rsa_import_params( rsa: &dyn Rsa, params: &[KeyParam], sec_level: SecurityLevel, key_format: KeyFormat, key_data: &[u8], ) -> Result<(Vec, KeyMaterial), Error> { // Deduce key size and exponent from import data. if key_format != KeyFormat::Pkcs8 { return Err(km_err!( UnsupportedKeyFormat, "unsupported import format {:?}, expect PKCS8", key_format )); } let (key, key_size, public_exponent) = rsa.import_pkcs8_key(key_data, params)?; // If key size or exponent are explicitly specified, they must match. If they were not // specified, we emit them. let mut deduced_chars = Vec::new(); match get_opt_tag_value!(params, KeySize)? { Some(param_key_size) => { if *param_key_size != key_size { return Err(km_err!( ImportParameterMismatch, "specified KEY_SIZE {:?} bits != actual key size {:?} for PKCS8 import", param_key_size, key_size )); } } None => deduced_chars.try_push(KeyParam::KeySize(key_size))?, } match get_opt_tag_value!(params, RsaPublicExponent)? { Some(param_public_exponent) => { if *param_public_exponent != public_exponent { return Err(km_err!( ImportParameterMismatch, "specified RSA_PUBLIC_EXPONENT {:?} != actual exponent {:?} for PKCS8 import", param_public_exponent, public_exponent, )); } } None => deduced_chars.try_push(KeyParam::RsaPublicExponent(public_exponent))?, } check_rsa_key_size(key_size, sec_level)?; check_rsa_params(params)?; Ok((deduced_chars, key)) } /// Check the parameter validity for an RSA key that is about to be generated or imported. fn check_rsa_params(params: &[KeyParam]) -> Result<(), Error> { let mut seen_attest = false; let mut seen_non_attest = false; for param in params { if let KeyParam::Purpose(purpose) = param { match purpose { KeyPurpose::Sign | KeyPurpose::Decrypt | KeyPurpose::WrapKey => { seen_non_attest = true } KeyPurpose::AttestKey => seen_attest = true, KeyPurpose::Verify | KeyPurpose::Encrypt => {} // public key operations KeyPurpose::AgreeKey => { warn!("Generating RSA key with invalid purpose {:?}", purpose) } } } } if seen_attest && seen_non_attest { return Err(km_err!( IncompatiblePurpose, "keys with ATTEST_KEY must have no other purpose" )); } Ok(()) } /// Check EC key generation parameter validity. fn check_ec_gen_params(params: &[KeyParam], sec_level: SecurityLevel) -> Result { // For key generation, the curve must be explicitly specified. let ec_curve = get_ec_curve(params)?; let purpose = check_ec_params(ec_curve, params, sec_level)?; let keygen_info = match (ec_curve, purpose) { (EcCurve::Curve25519, Some(KeyPurpose::Sign)) => KeyGenInfo::Ed25519, (EcCurve::Curve25519, Some(KeyPurpose::AttestKey)) => KeyGenInfo::Ed25519, (EcCurve::Curve25519, Some(KeyPurpose::AgreeKey)) => KeyGenInfo::X25519, (EcCurve::Curve25519, _) => { return Err(km_err!( IncompatiblePurpose, "curve25519 keys with invalid purpose {:?}", purpose )) } (EcCurve::P224, _) => KeyGenInfo::NistEc(ec::NistCurve::P224), (EcCurve::P256, _) => KeyGenInfo::NistEc(ec::NistCurve::P256), (EcCurve::P384, _) => KeyGenInfo::NistEc(ec::NistCurve::P384), (EcCurve::P521, _) => KeyGenInfo::NistEc(ec::NistCurve::P521), }; Ok(keygen_info) } /// Find the first purpose value in the parameters. pub fn primary_purpose(params: &[KeyParam]) -> Result { params .iter() .find_map( |param| if let KeyParam::Purpose(purpose) = param { Some(*purpose) } else { None }, ) .ok_or_else(|| km_err!(IncompatiblePurpose, "no purpose found for key!")) } /// Check EC key import parameter validity. Return the key material along with any key generation /// parameters that have been deduced from the key material (but which are not present in the input /// key parameters). fn check_ec_import_params( ec: &dyn Ec, params: &[KeyParam], sec_level: SecurityLevel, key_format: KeyFormat, key_data: &[u8], ) -> Result<(Vec, KeyMaterial), Error> { // Curve25519 can be imported as PKCS8 or raw; all other curves must be PKCS8. // If we need to disinguish between Ed25519 and X25519, we need to examine the purpose for the // key -- look for `AgreeKey` as it cannot be combined with other purposes. let (key, curve) = match key_format { KeyFormat::Raw if get_ec_curve(params)? == EcCurve::Curve25519 => { // Raw key import must specify the curve (and the only valid option is Curve25519 // currently). if primary_purpose(params)? == KeyPurpose::AgreeKey { (ec.import_raw_x25519_key(key_data, params)?, EcCurve::Curve25519) } else { (ec.import_raw_ed25519_key(key_data, params)?, EcCurve::Curve25519) } } KeyFormat::Pkcs8 => { let key = ec.import_pkcs8_key(key_data, params)?; let curve = match &key { KeyMaterial::Ec(curve, CurveType::Nist, _) => *curve, KeyMaterial::Ec(EcCurve::Curve25519, CurveType::EdDsa, _) => { if primary_purpose(params)? == KeyPurpose::AgreeKey { return Err(km_err!( IncompatiblePurpose, "can't use EdDSA key for key agreement" )); } EcCurve::Curve25519 } KeyMaterial::Ec(EcCurve::Curve25519, CurveType::Xdh, _) => { if primary_purpose(params)? != KeyPurpose::AgreeKey { return Err(km_err!(IncompatiblePurpose, "can't use XDH key for signing")); } EcCurve::Curve25519 } _ => { return Err(km_err!( ImportParameterMismatch, "unexpected key type from EC import" )) } }; (key, curve) } _ => { return Err(km_err!( UnsupportedKeyFormat, "invalid import format ({:?}) for EC key", key_format, )); } }; // If curve was explicitly specified, it must match. If not specified, populate it in the // deduced characteristics. let mut deduced_chars = Vec::new(); match get_opt_tag_value!(params, EcCurve)? { Some(specified_curve) => { if *specified_curve != curve { return Err(km_err!( ImportParameterMismatch, "imported EC key claimed curve {:?} but is {:?}", specified_curve, curve )); } } None => deduced_chars.try_push(KeyParam::EcCurve(curve))?, } // If key size was explicitly specified, it must match. If not specified, populate it in the // deduced characteristics. let key_size = ec::curve_to_key_size(curve); match get_opt_tag_value!(params, KeySize)? { Some(param_key_size) => { if *param_key_size != key_size { return Err(km_err!( ImportParameterMismatch, "specified KEY_SIZE {:?} bits != actual key size {:?} for PKCS8 import", param_key_size, key_size )); } } None => deduced_chars.try_push(KeyParam::KeySize(key_size))?, } check_ec_params(curve, params, sec_level)?; Ok((deduced_chars, key)) } /// Check the parameter validity for an EC key that is about to be generated or imported. fn check_ec_params( curve: EcCurve, params: &[KeyParam], sec_level: SecurityLevel, ) -> Result, Error> { if sec_level == SecurityLevel::Strongbox && curve != EcCurve::P256 { return Err(km_err!(UnsupportedEcCurve, "invalid curve ({:?}) for StrongBox", curve)); } // Key size is not needed, but if present should match the curve. if let Some(key_size) = get_opt_tag_value!(params, KeySize)? { match curve { EcCurve::P224 if *key_size == KeySizeInBits(224) => {} EcCurve::P256 if *key_size == KeySizeInBits(256) => {} EcCurve::P384 if *key_size == KeySizeInBits(384) => {} EcCurve::P521 if *key_size == KeySizeInBits(521) => {} EcCurve::Curve25519 if *key_size == KeySizeInBits(256) => {} _ => { return Err(km_err!( InvalidArgument, "invalid curve ({:?}) / key size ({:?}) combination", curve, key_size )) } } } let mut seen_attest = false; let mut seen_sign = false; let mut seen_agree = false; let mut primary_purpose = None; for param in params { if let KeyParam::Purpose(purpose) = param { match purpose { KeyPurpose::Sign => seen_sign = true, KeyPurpose::AgreeKey => seen_agree = true, KeyPurpose::AttestKey => seen_attest = true, KeyPurpose::Verify => {} _ => warn!("Generating EC key with invalid purpose {:?}", purpose), } if primary_purpose.is_none() { primary_purpose = Some(*purpose); } } } // Keys with Purpose::ATTEST_KEY must have no other purpose. if seen_attest && (seen_sign || seen_agree) { return Err(km_err!( IncompatiblePurpose, "keys with ATTEST_KEY must have no other purpose" )); } // Curve25519 keys must be either signing/attesting keys (Ed25519), or key agreement // keys (X25519), not both. if curve == EcCurve::Curve25519 && seen_agree && (seen_sign || seen_attest) { return Err(km_err!( IncompatiblePurpose, "curve25519 keys must be either SIGN/ATTEST_KEY or AGREE_KEY, not both" )); } Ok(primary_purpose) } /// Check AES key generation parameter validity. fn check_aes_gen_params( params: &[KeyParam], sec_level: SecurityLevel, ) -> Result { // For key generation, the size must be explicitly specified. let key_size = get_tag_value!(params, KeySize, ErrorCode::UnsupportedKeySize)?; let keygen_info = match key_size { KeySizeInBits(128) => KeyGenInfo::Aes(aes::Variant::Aes128), KeySizeInBits(256) => KeyGenInfo::Aes(aes::Variant::Aes256), KeySizeInBits(192) if sec_level != SecurityLevel::Strongbox => { KeyGenInfo::Aes(aes::Variant::Aes192) } _ => { return Err(km_err!( UnsupportedKeySize, "unsupported KEY_SIZE {:?} bits for AES", key_size )) } }; check_aes_params(params)?; Ok(keygen_info) } /// Check AES key import parameter validity. Return the key material along with any key generation /// parameters that have been deduced from the key material (but which are not present in the input /// key parameters). fn check_aes_import_params( aes: &dyn Aes, params: &[KeyParam], sec_level: SecurityLevel, key_format: KeyFormat, key_data: &[u8], ) -> Result<(Vec, KeyMaterial), Error> { require_raw(key_format)?; let (key, key_size) = aes.import_key(key_data, params)?; if key_size == KeySizeInBits(192) && sec_level == SecurityLevel::Strongbox { return Err(km_err!( UnsupportedKeySize, "unsupported KEY_SIZE=192 bits for AES on StrongBox", )); } let deduced_chars = require_matching_key_size(params, key_size)?; check_aes_params(params)?; Ok((deduced_chars, key)) } /// Check the parameter validity for an AES key that is about to be generated or imported. fn check_aes_params(params: &[KeyParam]) -> Result<(), Error> { let gcm_support = params.iter().any(|p| *p == KeyParam::BlockMode(BlockMode::Gcm)); if gcm_support { let min_mac_len = get_tag_value!(params, MinMacLength, ErrorCode::MissingMinMacLength)?; if (min_mac_len % 8 != 0) || !(96..=128).contains(&min_mac_len) { return Err(km_err!( UnsupportedMinMacLength, "unsupported MIN_MAC_LENGTH {} bits", min_mac_len )); } } Ok(()) } /// Check triple DES key generation parameter validity. fn check_3des_gen_params(params: &[KeyParam]) -> Result { // For key generation, the size (168) must be explicitly specified. let key_size = get_tag_value!(params, KeySize, ErrorCode::UnsupportedKeySize)?; if key_size != KeySizeInBits(168) { return Err(km_err!( UnsupportedKeySize, "unsupported KEY_SIZE {:?} bits for TRIPLE_DES", key_size )); } Ok(KeyGenInfo::TripleDes) } /// Check triple DES key import parameter validity. Return the key material along with any key /// generation parameters that have been deduced from the key material (but which are not present in /// the input key parameters). fn check_3des_import_params( des: &dyn Des, params: &[KeyParam], key_format: KeyFormat, key_data: &[u8], ) -> Result<(Vec, KeyMaterial), Error> { require_raw(key_format)?; let key = des.import_key(key_data, params)?; // If the key size is specified as a parameter, it must be 168. Note that this // is not equal to 8 x 24 (the data size). let deduced_chars = require_matching_key_size(params, des::KEY_SIZE_BITS)?; Ok((deduced_chars, key)) } /// Check HMAC key generation parameter validity. fn check_hmac_gen_params( params: &[KeyParam], sec_level: SecurityLevel, ) -> Result { // For key generation the size must be explicitly specified. let key_size = get_tag_value!(params, KeySize, ErrorCode::UnsupportedKeySize)?; check_hmac_params(params, sec_level, key_size)?; Ok(KeyGenInfo::Hmac(key_size)) } /// Build the set of key characteristics for an HMAC key that is about to be imported, /// checking parameter validity along the way. fn check_hmac_import_params( hmac: &dyn Hmac, params: &[KeyParam], sec_level: SecurityLevel, key_format: KeyFormat, key_data: &[u8], ) -> Result<(Vec, KeyMaterial), Error> { require_raw(key_format)?; let (key, key_size) = hmac.import_key(key_data, params)?; let deduced_chars = require_matching_key_size(params, key_size)?; check_hmac_params(params, sec_level, key_size)?; Ok((deduced_chars, key)) } /// Check the parameter validity for an HMAC key that is about to be generated or imported. fn check_hmac_params( params: &[KeyParam], sec_level: SecurityLevel, key_size: KeySizeInBits, ) -> Result<(), Error> { if sec_level == SecurityLevel::Strongbox { hmac::valid_strongbox_hal_size(key_size)?; } else { hmac::valid_hal_size(key_size)?; } let digest = get_tag_value!(params, Digest, ErrorCode::UnsupportedDigest)?; if digest == Digest::None { return Err(km_err!(UnsupportedDigest, "unsupported digest {:?}", digest)); } let min_mac_len = get_tag_value!(params, MinMacLength, ErrorCode::MissingMinMacLength)?; if (min_mac_len % 8 != 0) || !(64..=512).contains(&min_mac_len) { return Err(km_err!( UnsupportedMinMacLength, "unsupported MIN_MAC_LENGTH {:?} bits", min_mac_len )); } Ok(()) } /// Check for `KeyFormat::RAW`. fn require_raw(key_format: KeyFormat) -> Result<(), Error> { if key_format != KeyFormat::Raw { return Err(km_err!( UnsupportedKeyFormat, "unsupported import format {:?}, expect RAW", key_format )); } Ok(()) } /// Check or populate a `Tag::KEY_SIZE` value. fn require_matching_key_size( params: &[KeyParam], key_size: KeySizeInBits, ) -> Result, Error> { let mut deduced_chars = Vec::new(); match get_opt_tag_value!(params, KeySize)? { Some(param_key_size) => { if *param_key_size != key_size { return Err(km_err!( ImportParameterMismatch, "specified KEY_SIZE {:?} bits != actual key size {:?}", param_key_size, key_size )); } } None => deduced_chars.try_push(KeyParam::KeySize(key_size))?, } Ok(deduced_chars) } /// Return an error if any of the `exclude` tags are found in `params`. fn reject_tags(params: &[KeyParam], exclude: &[Tag]) -> Result<(), Error> { for param in params { if exclude.contains(¶m.tag()) { return Err(km_err!(InvalidTag, "tag {:?} not allowed", param.tag())); } } Ok(()) } /// Return an error if non-None padding found. fn reject_some_padding(params: &[KeyParam]) -> Result<(), Error> { if let Some(padding) = get_opt_tag_value!(params, Padding)? { if *padding != PaddingMode::None { return Err(km_err!(InvalidTag, "padding {:?} not allowed", padding)); } } Ok(()) } /// Return an error if non-None digest found. fn reject_some_digest(params: &[KeyParam]) -> Result<(), Error> { if let Some(digest) = get_opt_tag_value!(params, Digest)? { if *digest != Digest::None { return Err(km_err!(InvalidTag, "digest {:?} not allowed", digest)); } } Ok(()) } /// Reject incompatible combinations of authentication tags. fn reject_incompatible_auth(params: &[KeyParam]) -> Result<(), Error> { let mut seen_user_secure_id = false; let mut seen_auth_type = false; let mut seen_no_auth = false; for param in params { match param { KeyParam::UserSecureId(_sid) => seen_user_secure_id = true, KeyParam::UserAuthType(_atype) => seen_auth_type = true, KeyParam::NoAuthRequired => seen_no_auth = true, _ => {} } } if seen_no_auth { if seen_user_secure_id { return Err(km_err!(InvalidTag, "found both NO_AUTH_REQUIRED and USER_SECURE_ID")); } if seen_auth_type { return Err(km_err!(InvalidTag, "found both NO_AUTH_REQUIRED and USER_AUTH_TYPE")); } } if seen_user_secure_id && !seen_auth_type { return Err(km_err!(InvalidTag, "found USER_SECURE_ID but no USER_AUTH_TYPE")); } Ok(()) } /// Indication of which parameters on a `begin` need to be checked against key authorizations. struct BeginParamsToCheck { block_mode: bool, padding: bool, digest: bool, mgf_digest: bool, } /// Check that an operation with the given `purpose` and `params` can validly be started /// using a key with characteristics `chars`. pub fn check_begin_params( chars: &[KeyParam], purpose: KeyPurpose, params: &[KeyParam], ) -> Result<(), Error> { // General checks for all algorithms. let algo = get_algorithm(chars)?; let valid_purpose = matches!( (algo, purpose), (Algorithm::Aes, KeyPurpose::Encrypt) | (Algorithm::Aes, KeyPurpose::Decrypt) | (Algorithm::TripleDes, KeyPurpose::Encrypt) | (Algorithm::TripleDes, KeyPurpose::Decrypt) | (Algorithm::Hmac, KeyPurpose::Sign) | (Algorithm::Hmac, KeyPurpose::Verify) | (Algorithm::Ec, KeyPurpose::Sign) | (Algorithm::Ec, KeyPurpose::AttestKey) | (Algorithm::Ec, KeyPurpose::AgreeKey) | (Algorithm::Rsa, KeyPurpose::Sign) | (Algorithm::Rsa, KeyPurpose::Decrypt) | (Algorithm::Rsa, KeyPurpose::AttestKey) ); if !valid_purpose { return Err(km_err!( UnsupportedPurpose, "invalid purpose {:?} for {:?} key", purpose, algo )); } if !contains_tag_value!(chars, Purpose, purpose) { return Err(km_err!( IncompatiblePurpose, "purpose {:?} not in key characteristics", purpose )); } if get_bool_tag_value!(chars, StorageKey)? { return Err(km_err!(StorageKeyUnsupported, "attempt to use storage key",)); } let nonce = get_opt_tag_value!(params, Nonce)?; if get_bool_tag_value!(chars, CallerNonce)? { // Caller-provided nonces are allowed. } else if nonce.is_some() && purpose == KeyPurpose::Encrypt { return Err(km_err!(CallerNonceProhibited, "caller nonce not allowed for encryption")); } // Further algorithm-specific checks. let check = match algo { Algorithm::Rsa => check_begin_rsa_params(chars, purpose, params), Algorithm::Ec => check_begin_ec_params(chars, purpose, params), Algorithm::Aes => check_begin_aes_params(chars, params, nonce.map(|v| v.as_ref())), Algorithm::TripleDes => check_begin_3des_params(params, nonce.map(|v| v.as_ref())), Algorithm::Hmac => check_begin_hmac_params(chars, purpose, params), }?; // For various parameters, if they are specified in the begin parameters and they // are relevant for the algorithm, then the same value must also exist in the key // characteristics. Also, there can be only one distinct value in the parameters. if check.block_mode { if let Some(bmode) = get_opt_tag_value!(params, BlockMode, UnsupportedBlockMode)? { if !contains_tag_value!(chars, BlockMode, *bmode) { return Err(km_err!( IncompatibleBlockMode, "block mode {:?} not in key characteristics {:?}", bmode, chars, )); } } } if check.padding { if let Some(pmode) = get_opt_tag_value!(params, Padding, UnsupportedPaddingMode)? { if !contains_tag_value!(chars, Padding, *pmode) { return Err(km_err!( IncompatiblePaddingMode, "padding mode {:?} not in key characteristics {:?}", pmode, chars, )); } } } if check.digest { if let Some(digest) = get_opt_tag_value!(params, Digest, UnsupportedDigest)? { if !contains_tag_value!(chars, Digest, *digest) { return Err(km_err!( IncompatibleDigest, "digest {:?} not in key characteristics", digest, )); } } } if check.mgf_digest { let mut mgf_digest_to_find = get_opt_tag_value!(params, RsaOaepMgfDigest, UnsupportedMgfDigest)?; let chars_have_mgf_digest = chars.iter().any(|param| matches!(param, KeyParam::RsaOaepMgfDigest(_))); if chars_have_mgf_digest && mgf_digest_to_find.is_none() { // The key characteristics include an explicit set of MGF digests, but the begin() // operation is using the default SHA1. Check that this default is in the // characteristics. mgf_digest_to_find = Some(&Digest::Sha1); } if let Some(mgf_digest) = mgf_digest_to_find { if !contains_tag_value!(chars, RsaOaepMgfDigest, *mgf_digest) { return Err(km_err!( IncompatibleMgfDigest, "MGF digest {:?} not in key characteristics", mgf_digest, )); } } } Ok(()) } /// Indicate whether a [`KeyPurpose`] is for encryption/decryption. fn for_encryption(purpose: KeyPurpose) -> bool { purpose == KeyPurpose::Encrypt || purpose == KeyPurpose::Decrypt || purpose == KeyPurpose::WrapKey } /// Indicate whether a [`KeyPurpose`] is for signing. fn for_signing(purpose: KeyPurpose) -> bool { purpose == KeyPurpose::Sign } /// Check that an RSA operation with the given `purpose` and `params` can validly be started /// using a key with characteristics `chars`. fn check_begin_rsa_params( chars: &[KeyParam], purpose: KeyPurpose, params: &[KeyParam], ) -> Result { let padding = get_padding_mode(params)?; let mut digest = None; if for_signing(purpose) || (for_encryption(purpose) && padding == PaddingMode::RsaOaep) { digest = Some(get_digest(params)?); } if for_signing(purpose) && padding == PaddingMode::None && digest != Some(Digest::None) { return Err(km_err!( IncompatibleDigest, "unpadded RSA sign requires Digest::None not {:?}", digest )); } match padding { PaddingMode::None => {} PaddingMode::RsaOaep if for_encryption(purpose) => { if digest.is_none() || digest == Some(Digest::None) { return Err(km_err!(IncompatibleDigest, "digest required for RSA-OAEP")); } let mgf_digest = get_mgf_digest(params)?; if mgf_digest == Digest::None { return Err(km_err!( UnsupportedMgfDigest, "MGF digest cannot be NONE for RSA-OAEP" )); } } PaddingMode::RsaPss if for_signing(purpose) => { if let Some(digest) = digest { let key_size_bits = get_tag_value!(chars, KeySize, ErrorCode::InvalidArgument)?; let d = digest_len(digest)?; if key_size_bits < KeySizeInBits(2 * d + 9) { return Err(km_err!( IncompatibleDigest, "key size {:?} < 2*8*D={} + 9", key_size_bits, d )); } } else { return Err(km_err!(IncompatibleDigest, "digest required for RSA-PSS")); } } PaddingMode::RsaPkcs115Encrypt if for_encryption(purpose) => { if digest.is_some() && digest != Some(Digest::None) { warn!( "ignoring digest {:?} provided for PKCS#1 v1.5 encryption/decryption", digest ); } } PaddingMode::RsaPkcs115Sign if for_signing(purpose) => { if digest.is_none() { return Err(km_err!(IncompatibleDigest, "digest required for RSA-PKCS_1_5_SIGN")); } } _ => { return Err(km_err!( UnsupportedPaddingMode, "purpose {:?} incompatible with padding {:?}", purpose, padding )) } } Ok(BeginParamsToCheck { block_mode: false, padding: true, digest: true, mgf_digest: true }) } /// Check that an EC operation with the given `purpose` and `params` can validly be started /// using a key with characteristics `chars`. fn check_begin_ec_params( chars: &[KeyParam], purpose: KeyPurpose, params: &[KeyParam], ) -> Result { let curve = get_ec_curve(chars)?; if purpose == KeyPurpose::Sign { let digest = get_digest(params)?; if digest == Digest::Md5 { return Err(km_err!(UnsupportedDigest, "Digest::MD5 unsupported for EC signing")); } if curve == EcCurve::Curve25519 && digest != Digest::None { return Err(km_err!( UnsupportedDigest, "Ed25519 only supports Digest::None not {:?}", digest )); } } Ok(BeginParamsToCheck { block_mode: false, padding: false, digest: true, mgf_digest: false }) } /// Check that an AES operation with the given `purpose` and `params` can validly be started /// using a key with characteristics `chars`. fn check_begin_aes_params( chars: &[KeyParam], params: &[KeyParam], caller_nonce: Option<&[u8]>, ) -> Result { reject_tags(params, &[Tag::RsaOaepMgfDigest])?; reject_some_digest(params)?; let bmode = get_block_mode(params)?; let padding = get_padding_mode(params)?; if bmode == BlockMode::Gcm { let mac_len = get_tag_value!(params, MacLength, ErrorCode::MissingMacLength)?; if mac_len % 8 != 0 || mac_len > 128 { return Err(km_err!(UnsupportedMacLength, "invalid mac len {}", mac_len)); } let min_mac_len = get_tag_value!(chars, MinMacLength, ErrorCode::MissingMinMacLength)?; if mac_len < min_mac_len { return Err(km_err!( InvalidMacLength, "mac len {} less than min {}", mac_len, min_mac_len )); } } match bmode { BlockMode::Gcm | BlockMode::Ctr => match padding { PaddingMode::None => {} _ => { return Err(km_err!( IncompatiblePaddingMode, "padding {:?} not valid for AES GCM/CTR", padding )) } }, BlockMode::Ecb | BlockMode::Cbc => match padding { PaddingMode::None | PaddingMode::Pkcs7 => {} _ => { return Err(km_err!( IncompatiblePaddingMode, "padding {:?} not valid for AES GCM/CTR", padding )) } }, } if let Some(nonce) = caller_nonce { match bmode { BlockMode::Cbc if nonce.len() == 16 => {} BlockMode::Ctr if nonce.len() == 16 => {} BlockMode::Gcm if nonce.len() == 12 => {} _ => { return Err(km_err!( InvalidNonce, "invalid caller nonce len {} for {:?}", nonce.len(), bmode )) } } } Ok(BeginParamsToCheck { block_mode: true, padding: true, digest: false, mgf_digest: false }) } /// Check that a 3-DES operation with the given `purpose` and `params` can validly be started /// using a key with characteristics `chars`. fn check_begin_3des_params( params: &[KeyParam], caller_nonce: Option<&[u8]>, ) -> Result { reject_tags(params, &[Tag::RsaOaepMgfDigest])?; reject_some_digest(params)?; let bmode = get_block_mode(params)?; let _padding = get_padding_mode(params)?; match bmode { BlockMode::Cbc | BlockMode::Ecb => {} _ => { return Err(km_err!(UnsupportedBlockMode, "block mode {:?} not valid for 3-DES", bmode)) } } if let Some(nonce) = caller_nonce { match bmode { BlockMode::Cbc if nonce.len() == 8 => {} _ => { return Err(km_err!( InvalidNonce, "invalid caller nonce len {} for {:?}", nonce.len(), bmode )) } } } Ok(BeginParamsToCheck { block_mode: true, padding: true, digest: false, mgf_digest: false }) } /// Check that an HMAC operation with the given `purpose` and `params` can validly be started /// using a key with characteristics `chars`. fn check_begin_hmac_params( chars: &[KeyParam], purpose: KeyPurpose, params: &[KeyParam], ) -> Result { reject_tags(params, &[Tag::BlockMode, Tag::RsaOaepMgfDigest])?; reject_some_padding(params)?; let digest = get_digest(params)?; if purpose == KeyPurpose::Sign { let mac_len = get_tag_value!(params, MacLength, ErrorCode::MissingMacLength)?; if mac_len % 8 != 0 || mac_len > digest_len(digest)? { return Err(km_err!(UnsupportedMacLength, "invalid mac len {}", mac_len)); } let min_mac_len = get_tag_value!(chars, MinMacLength, ErrorCode::MissingMinMacLength)?; if mac_len < min_mac_len { return Err(km_err!( InvalidMacLength, "mac len {} less than min {}", mac_len, min_mac_len )); } } Ok(BeginParamsToCheck { block_mode: false, padding: false, digest: true, mgf_digest: false }) } /// Return the length in bits of a [`Digest`] function. pub fn digest_len(digest: Digest) -> Result { match digest { Digest::Md5 => Ok(128), Digest::Sha1 => Ok(160), Digest::Sha224 => Ok(224), Digest::Sha256 => Ok(256), Digest::Sha384 => Ok(384), Digest::Sha512 => Ok(512), _ => Err(km_err!(IncompatibleDigest, "invalid digest {:?}", digest)), } } /// Check the required key params for an RSA wrapping key used in secure import and return the /// [`DecryptionMode`] constructed from the processed key characteristics. pub fn check_rsa_wrapping_key_params( chars: &[KeyParam], params: &[KeyParam], ) -> Result { // Check the purpose of the wrapping key if !contains_tag_value!(chars, Purpose, KeyPurpose::WrapKey) { return Err(km_err!(IncompatiblePurpose, "no wrap key purpose for the wrapping key")); } let padding_mode = get_tag_value!(params, Padding, ErrorCode::IncompatiblePaddingMode)?; if padding_mode != PaddingMode::RsaOaep { return Err(km_err!( IncompatiblePaddingMode, "invalid padding mode {:?} for RSA wrapping key", padding_mode )); } let msg_digest = get_tag_value!(params, Digest, ErrorCode::IncompatibleDigest)?; if msg_digest != Digest::Sha256 { return Err(km_err!( IncompatibleDigest, "invalid digest {:?} for RSA wrapping key", padding_mode )); } let opt_mgf_digest = get_opt_tag_value!(params, RsaOaepMgfDigest)?; if opt_mgf_digest == Some(&Digest::None) { return Err(km_err!(UnsupportedMgfDigest, "MGF digest cannot be NONE for RSA-OAEP")); } if !contains_tag_value!(chars, Padding, padding_mode) { return Err(km_err!( IncompatiblePaddingMode, "padding mode {:?} not in key characteristics {:?}", padding_mode, chars, )); } if !contains_tag_value!(chars, Digest, msg_digest) { return Err(km_err!( IncompatibleDigest, "digest {:?} not in key characteristics {:?}", msg_digest, chars, )); } if let Some(mgf_digest) = opt_mgf_digest { // MGF digest explicitly specified, check it is in key characteristics. if !contains_tag_value!(chars, RsaOaepMgfDigest, *mgf_digest) { return Err(km_err!( IncompatibleDigest, "MGF digest {:?} not in key characteristics {:?}", mgf_digest, chars, )); } } let mgf_digest = opt_mgf_digest.unwrap_or(&Digest::Sha1); let rsa_oaep_decrypt_mode = DecryptionMode::OaepPadding { msg_digest, mgf_digest: *mgf_digest }; Ok(rsa_oaep_decrypt_mode) } /// Calculate the [Luhn checksum](https://en.wikipedia.org/wiki/Luhn_algorithm) of the given number. fn luhn_checksum(mut val: u64) -> u64 { let mut ii = 0; let mut sum_digits = 0; while val != 0 { let curr_digit = val % 10; let multiplier = if ii % 2 == 0 { 2 } else { 1 }; let digit_multiplied = curr_digit * multiplier; sum_digits += (digit_multiplied % 10) + (digit_multiplied / 10); val /= 10; ii += 1; } (10 - (sum_digits % 10)) % 10 } /// Derive an IMEI value from a first IMEI value, by incrementing by one and re-calculating /// the Luhn checksum. Return an empty vector on any failure. pub fn increment_imei(imei: &[u8]) -> Vec { if imei.is_empty() { info!("empty IMEI"); return Vec::new(); } // Expect ASCII digits. let imei: &str = match core::str::from_utf8(imei) { Ok(v) => v, Err(_) => { warn!("IMEI is not UTF-8"); return Vec::new(); } }; let imei: u64 = match imei.parse() { Ok(v) => v, Err(_) => { warn!("IMEI is not numeric"); return Vec::new(); } }; // Drop trailing checksum digit, increment, and restore checksum. let imei2 = (imei / 10) + 1; let imei2 = (imei2 * 10) + luhn_checksum(imei2); // Convert back to bytes. alloc::format!("{}", imei2).into_bytes() }