// 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. //! TA functionality related to in-progress crypto operations. use alloc::{boxed::Box, vec::Vec}; use kmr_common::{ crypto, crypto::{aes, AadOperation, AccumulatingOperation, EmittingOperation, KeyMaterial}, get_bool_tag_value, get_opt_tag_value, get_tag_value, keyblob, km_err, tag, try_to_vec, Error, FallibleAllocExt, }; use kmr_wire::{ keymint::{ErrorCode, HardwareAuthToken, KeyParam, KeyPurpose}, secureclock::{TimeStampToken, Timestamp}, InternalBeginResult, }; use log::{error, info, warn}; /// A trusted confirmation token should be the size of HMAC-SHA256 output. const CONFIRMATION_TOKEN_SIZE: usize = 32; /// Trusted confirmation data prefix, from IConfirmationResultCallback.hal. const CONFIRMATION_DATA_PREFIX: &[u8] = b"confirmation token"; /// Maximum size of messages with `Tag::TrustedConfirmationRequired` set. /// See const CONFIRMATION_MESSAGE_MAX_LEN: usize = 6144; /// Union holder for in-progress cryptographic operations, each of which is an instance /// of the relevant trait. pub(crate) enum CryptoOperation { Aes(Box), AesGcm(Box), Des(Box), HmacSign(Box, usize), // tag length HmacVerify(Box, core::ops::Range), RsaDecrypt(Box), RsaSign(Box), EcAgree(Box), EcSign(Box), } /// Current state of an operation. pub(crate) struct Operation { /// Random handle used to identify the operation, also used as a challenge. pub handle: OpHandle, /// Whether update_aad() is allowed (only ever true for AEADs before data has arrived). pub aad_allowed: bool, /// Secure deletion slot to delete on successful completion of the operation. pub slot_to_delete: Option, /// Buffer to accumulate data being signed that must have a trusted confirmation. This /// data matches what was been fed into `crypto_op`'s `update` method (but has a size /// limit so will not grow unboundedly). pub trusted_conf_data: Option>, /// Authentication data to check. pub auth_info: Option, pub crypto_op: CryptoOperation, /// Accumulated input size. pub input_size: usize, } impl Operation { /// Check whether `len` additional bytes of data can be accommodated by the `Operation`. fn check_size(&mut self, len: usize) -> Result<(), Error> { self.input_size += len; let max_size = match &self.crypto_op { CryptoOperation::HmacSign(op, _) | CryptoOperation::HmacVerify(op, _) | CryptoOperation::RsaDecrypt(op) | CryptoOperation::RsaSign(op) | CryptoOperation::EcAgree(op) | CryptoOperation::EcSign(op) => op.max_input_size(), _ => None, }; if let Some(max_size) = max_size { if self.input_size > max_size { return Err(km_err!( InvalidInputLength, "too much input accumulated for operation" )); } } Ok(()) } } /// Newtype for operation handles. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) struct OpHandle(pub i64); /// Authentication requirements associated with an operation. pub(crate) struct AuthInfo { secure_ids: Vec, auth_type: u32, timeout_secs: Option, } impl AuthInfo { /// Optionally build an `AuthInfo` from key characteristics. If no authentication is needed on /// `update()`/`update_aad()`/`finish()`, return `None`. fn new(key_chars: &[KeyParam]) -> Result, Error> { let mut secure_ids = Vec::new(); let mut auth_type = None; let mut timeout_secs = None; let mut no_auth_required = false; for param in key_chars { match param { KeyParam::UserSecureId(sid) => secure_ids.try_push(*sid)?, KeyParam::UserAuthType(atype) => { if auth_type.is_none() { auth_type = Some(*atype); } else { return Err(km_err!(InvalidKeyBlob, "duplicate UserAuthType tag found")); } } KeyParam::AuthTimeout(secs) => { if timeout_secs.is_none() { timeout_secs = Some(*secs) } else { return Err(km_err!(InvalidKeyBlob, "duplicate AuthTimeout tag found")); } } KeyParam::NoAuthRequired => no_auth_required = true, _ => {} } } if secure_ids.is_empty() { Ok(None) } else if let Some(auth_type) = auth_type { if no_auth_required { Err(km_err!(InvalidKeyBlob, "found both NO_AUTH_REQUIRED and USER_SECURE_ID")) } else { Ok(Some(AuthInfo { secure_ids, auth_type, timeout_secs })) } } else { Err(km_err!(KeyUserNotAuthenticated, "found USER_SECURE_ID but no USER_AUTH_TYPE")) } } } impl crate::KeyMintTa { pub(crate) fn begin_operation( &mut self, purpose: KeyPurpose, key_blob: &[u8], params: Vec, auth_token: Option, ) -> Result { let op_idx = self.new_operation_index()?; // Parse and decrypt the keyblob, which requires extra hidden params. let (keyblob, sdd_slot) = self.keyblob_parse_decrypt(key_blob, ¶ms)?; let keyblob::PlaintextKeyBlob { characteristics, key_material } = keyblob; // Validate parameters. let key_chars = kmr_common::tag::characteristics_at(&characteristics, self.hw_info.security_level)?; tag::check_begin_params(key_chars, purpose, ¶ms)?; self.check_begin_auths(key_chars, key_blob)?; let trusted_conf_data = if purpose == KeyPurpose::Sign && get_bool_tag_value!(key_chars, TrustedConfirmationRequired)? { // Trusted confirmation is required; accumulate the signed data in an extra buffer, // starting with a prefix. Some(try_to_vec(CONFIRMATION_DATA_PREFIX)?) } else { None }; let slot_to_delete = if let Some(&1) = get_opt_tag_value!(key_chars, UsageCountLimit)? { warn!("single-use key will be deleted on operation completion"); sdd_slot } else { None }; // At most one operation involving proof of user presence can be in-flight at a time. let presence_required = get_bool_tag_value!(key_chars, TrustedUserPresenceRequired)?; if presence_required && self.presence_required_op.is_some() { return Err(km_err!( ConcurrentProofOfPresenceRequested, "additional op with proof-of-presence requested" )); } let mut op_auth_info = AuthInfo::new(key_chars)?; if let Some(auth_info) = &op_auth_info { // Authentication checks are required on begin() if there's a timeout that // we can check. if let Some(timeout_secs) = auth_info.timeout_secs { if let Some(clock) = &self.imp.clock { let now: Timestamp = clock.now().into(); let auth_token = auth_token.ok_or_else(|| { km_err!(KeyUserNotAuthenticated, "no auth token on begin()") })?; self.check_auth_token( auth_token, auth_info, Some(now), Some(timeout_secs), None, )?; // Auth already checked, nothing needed on subsequent calls op_auth_info = None; } else if let Some(auth_token) = auth_token { self.check_auth_token(auth_token, auth_info, None, None, None)?; } } } // Re-use the same random value for both: // - op_handle: the way to identify which operation is involved // - challenge: the value used as part of the input for authentication tokens let op_handle = self.new_op_handle(); let challenge = op_handle.0; let mut ret_params = Vec::new(); let op = match key_material { KeyMaterial::Aes(key) => { let caller_nonce = get_opt_tag_value!(¶ms, Nonce)?; let mode = aes::Mode::new(¶ms, caller_nonce, &mut *self.imp.rng)?; let dir = match purpose { KeyPurpose::Encrypt => crypto::SymmetricOperation::Encrypt, KeyPurpose::Decrypt => crypto::SymmetricOperation::Decrypt, _ => { return Err(km_err!( IncompatiblePurpose, "invalid purpose {:?} for AES key", purpose )) } }; if caller_nonce.is_none() { // Need to return any randomly-generated nonce to the caller. match &mode { aes::Mode::Cipher(aes::CipherMode::EcbNoPadding) | aes::Mode::Cipher(aes::CipherMode::EcbPkcs7Padding) => {} aes::Mode::Cipher(aes::CipherMode::CbcNoPadding { nonce: n }) | aes::Mode::Cipher(aes::CipherMode::CbcPkcs7Padding { nonce: n }) => { ret_params.try_push(KeyParam::Nonce(try_to_vec(n)?))? } aes::Mode::Cipher(aes::CipherMode::Ctr { nonce: n }) => { ret_params.try_push(KeyParam::Nonce(try_to_vec(n)?))? } aes::Mode::Aead(aes::GcmMode::GcmTag12 { nonce: n }) | aes::Mode::Aead(aes::GcmMode::GcmTag13 { nonce: n }) | aes::Mode::Aead(aes::GcmMode::GcmTag14 { nonce: n }) | aes::Mode::Aead(aes::GcmMode::GcmTag15 { nonce: n }) | aes::Mode::Aead(aes::GcmMode::GcmTag16 { nonce: n }) => { ret_params.try_push(KeyParam::Nonce(try_to_vec(n)?))? } } } match &mode { aes::Mode::Cipher(mode) => Operation { handle: op_handle, aad_allowed: false, input_size: 0, slot_to_delete, trusted_conf_data, auth_info: op_auth_info, crypto_op: CryptoOperation::Aes(self.imp.aes.begin(key, *mode, dir)?), }, aes::Mode::Aead(mode) => Operation { handle: op_handle, aad_allowed: true, input_size: 0, slot_to_delete, trusted_conf_data, auth_info: op_auth_info, crypto_op: CryptoOperation::AesGcm( self.imp.aes.begin_aead(key, *mode, dir)?, ), }, } } KeyMaterial::TripleDes(key) => { let caller_nonce = get_opt_tag_value!(¶ms, Nonce)?; let mode = crypto::des::Mode::new(¶ms, caller_nonce, &mut *self.imp.rng)?; let dir = match purpose { KeyPurpose::Encrypt => crypto::SymmetricOperation::Encrypt, KeyPurpose::Decrypt => crypto::SymmetricOperation::Decrypt, _ => { return Err(km_err!( IncompatiblePurpose, "invalid purpose {:?} for DES key", purpose )) } }; if caller_nonce.is_none() { // Need to return any randomly-generated nonce to the caller. match &mode { crypto::des::Mode::EcbNoPadding | crypto::des::Mode::EcbPkcs7Padding => {} crypto::des::Mode::CbcNoPadding { nonce: n } | crypto::des::Mode::CbcPkcs7Padding { nonce: n } => { ret_params.try_push(KeyParam::Nonce(try_to_vec(n)?))? } } } Operation { handle: op_handle, aad_allowed: false, input_size: 0, slot_to_delete, trusted_conf_data, auth_info: op_auth_info, crypto_op: CryptoOperation::Des(self.imp.des.begin(key, mode, dir)?), } } KeyMaterial::Hmac(key) => { let digest = tag::get_digest(¶ms)?; Operation { handle: op_handle, aad_allowed: false, input_size: 0, slot_to_delete, trusted_conf_data, auth_info: op_auth_info, crypto_op: match purpose { KeyPurpose::Sign => { let tag_len = get_tag_value!(¶ms, MacLength, ErrorCode::MissingMacLength)? as usize / 8; CryptoOperation::HmacSign(self.imp.hmac.begin(key, digest)?, tag_len) } KeyPurpose::Verify => { // Remember the acceptable tag lengths. let min_tag_len = get_tag_value!( key_chars, MinMacLength, ErrorCode::MissingMinMacLength )? as usize / 8; let max_tag_len = kmr_common::tag::digest_len(digest)? as usize; CryptoOperation::HmacVerify( self.imp.hmac.begin(key, digest)?, min_tag_len..max_tag_len, ) } _ => { return Err(km_err!( IncompatiblePurpose, "invalid purpose {:?} for HMAC key", purpose )) } }, } } KeyMaterial::Rsa(key) => Operation { handle: op_handle, aad_allowed: false, input_size: 0, slot_to_delete, trusted_conf_data, auth_info: op_auth_info, crypto_op: match purpose { KeyPurpose::Decrypt => { let mode = crypto::rsa::DecryptionMode::new(¶ms)?; CryptoOperation::RsaDecrypt(self.imp.rsa.begin_decrypt(key, mode)?) } KeyPurpose::Sign => { let mode = crypto::rsa::SignMode::new(¶ms)?; CryptoOperation::RsaSign(self.imp.rsa.begin_sign(key, mode)?) } _ => { return Err(km_err!( IncompatiblePurpose, "invalid purpose {:?} for RSA key", purpose )) } }, }, KeyMaterial::Ec(_, _, key) => Operation { handle: op_handle, aad_allowed: false, input_size: 0, slot_to_delete, trusted_conf_data, auth_info: op_auth_info, crypto_op: match purpose { KeyPurpose::AgreeKey => CryptoOperation::EcAgree(self.imp.ec.begin_agree(key)?), KeyPurpose::Sign => { let digest = tag::get_digest(¶ms)?; CryptoOperation::EcSign(self.imp.ec.begin_sign(key, digest)?) } _ => { return Err(km_err!( IncompatiblePurpose, "invalid purpose {:?} for EC key", purpose )) } }, }, }; self.operations[op_idx] = Some(op); if presence_required { info!("this operation requires proof-of-presence"); self.presence_required_op = Some(op_handle); } Ok(InternalBeginResult { challenge, params: ret_params, op_handle: op_handle.0 }) } pub(crate) fn op_update_aad( &mut self, op_handle: OpHandle, data: &[u8], auth_token: Option, timestamp_token: Option, ) -> Result<(), Error> { self.with_authed_operation(op_handle, auth_token, timestamp_token, |op| { if !op.aad_allowed { return Err(km_err!(InvalidTag, "update-aad not allowed")); } match &mut op.crypto_op { CryptoOperation::AesGcm(op) => op.update_aad(data), _ => Err(km_err!(InvalidOperation, "operation does not support update_aad")), } }) } pub(crate) fn op_update( &mut self, op_handle: OpHandle, data: &[u8], auth_token: Option, timestamp_token: Option, ) -> Result, Error> { let check_presence = if self.presence_required_op == Some(op_handle) { self.presence_required_op = None; true } else { false }; let tup_available = self.dev.tup.available(); self.with_authed_operation(op_handle, auth_token, timestamp_token, |op| { if check_presence && !tup_available { return Err(km_err!( ProofOfPresenceRequired, "trusted proof of presence required but not available" )); } if let Some(trusted_conf_data) = &mut op.trusted_conf_data { if trusted_conf_data.len() + data.len() > CONFIRMATION_DATA_PREFIX.len() + CONFIRMATION_MESSAGE_MAX_LEN { return Err(km_err!( InvalidArgument, "trusted confirmation data of size {} + {} too big", trusted_conf_data.len(), data.len() )); } trusted_conf_data.try_extend_from_slice(data)?; } op.aad_allowed = false; op.check_size(data.len())?; match &mut op.crypto_op { CryptoOperation::Aes(op) => op.update(data), CryptoOperation::AesGcm(op) => op.update(data), CryptoOperation::Des(op) => op.update(data), CryptoOperation::HmacSign(op, _) | CryptoOperation::HmacVerify(op, _) => { op.update(data)?; Ok(Vec::new()) } CryptoOperation::RsaDecrypt(op) => { op.update(data)?; Ok(Vec::new()) } CryptoOperation::RsaSign(op) => { op.update(data)?; Ok(Vec::new()) } CryptoOperation::EcAgree(op) => { op.update(data)?; Ok(Vec::new()) } CryptoOperation::EcSign(op) => { op.update(data)?; Ok(Vec::new()) } } }) } pub(crate) fn op_finish( &mut self, op_handle: OpHandle, data: Option<&[u8]>, signature: Option<&[u8]>, auth_token: Option, timestamp_token: Option, confirmation_token: Option<&[u8]>, ) -> Result, Error> { let mut op = self.take_operation(op_handle)?; self.check_subsequent_auth(&op, auth_token, timestamp_token)?; if self.presence_required_op == Some(op_handle) { self.presence_required_op = None; if !self.dev.tup.available() { return Err(km_err!( ProofOfPresenceRequired, "trusted proof of presence required but not available" )); } } if let (Some(trusted_conf_data), Some(data)) = (&mut op.trusted_conf_data, data) { if trusted_conf_data.len() + data.len() > CONFIRMATION_DATA_PREFIX.len() + CONFIRMATION_MESSAGE_MAX_LEN { return Err(km_err!( InvalidArgument, "data of size {} + {} too big", trusted_conf_data.len(), data.len() )); } trusted_conf_data.try_extend_from_slice(data)?; } op.check_size(data.map_or(0, |v| v.len()))?; let result = match op.crypto_op { CryptoOperation::Aes(mut op) => { let mut result = if let Some(data) = data { op.update(data)? } else { Vec::new() }; result.try_extend_from_slice(&op.finish()?)?; Ok(result) } CryptoOperation::AesGcm(mut op) => { let mut result = if let Some(data) = data { op.update(data)? } else { Vec::new() }; result.try_extend_from_slice(&op.finish()?)?; Ok(result) } CryptoOperation::Des(mut op) => { let mut result = if let Some(data) = data { op.update(data)? } else { Vec::new() }; result.try_extend_from_slice(&op.finish()?)?; Ok(result) } CryptoOperation::HmacSign(mut op, tag_len) => { if let Some(data) = data { op.update(data)?; }; let mut tag = op.finish()?; tag.truncate(tag_len); Ok(tag) } CryptoOperation::HmacVerify(mut op, tag_len_range) => { let sig = signature .ok_or_else(|| km_err!(InvalidArgument, "signature missing for HMAC verify"))?; if !tag_len_range.contains(&sig.len()) { return Err(km_err!( InvalidArgument, "signature length invalid: {} not in {:?}", sig.len(), tag_len_range )); } if let Some(data) = data { op.update(data)?; }; let got = op.finish()?; if self.imp.compare.eq(&got[..sig.len()], sig) { Ok(Vec::new()) } else { Err(km_err!(VerificationFailed, "HMAC verify failed")) } } CryptoOperation::RsaDecrypt(mut op) => { if let Some(data) = data { op.update(data)?; }; op.finish() } CryptoOperation::RsaSign(mut op) => { if let Some(data) = data { op.update(data)?; }; op.finish() } CryptoOperation::EcAgree(mut op) => { if let Some(data) = data { op.update(data)?; }; op.finish() } CryptoOperation::EcSign(mut op) => { if let Some(data) = data { op.update(data)?; }; op.finish() } }; if result.is_ok() { if let Some(trusted_conf_data) = op.trusted_conf_data { // Accumulated input must be checked against the trusted confirmation token. self.verify_confirmation_token(&trusted_conf_data, confirmation_token)?; } if let (Some(slot), Some(sdd_mgr)) = (op.slot_to_delete, &mut self.dev.sdd_mgr) { // A successful use of a key with UsageCountLimit(1) triggers deletion. warn!("Deleting single-use key after use"); if let Err(e) = sdd_mgr.delete_secret(slot) { error!("Failed to delete single-use key after use: {:?}", e); } } } result } pub(crate) fn op_abort(&mut self, op_handle: OpHandle) -> Result<(), Error> { if self.presence_required_op == Some(op_handle) { self.presence_required_op = None; } let _op = self.take_operation(op_handle)?; Ok(()) } /// Check TA-specific key authorizations on `begin()`. fn check_begin_auths(&mut self, key_chars: &[KeyParam], key_blob: &[u8]) -> Result<(), Error> { if self.dev.bootloader.done() && get_bool_tag_value!(key_chars, BootloaderOnly)? { return Err(km_err!( InvalidKeyBlob, "attempt to use bootloader-only key after bootloader done" )); } if !self.in_early_boot && get_bool_tag_value!(key_chars, EarlyBootOnly)? { return Err(km_err!(EarlyBootEnded, "attempt to use EARLY_BOOT key after early boot")); } if let Some(max_uses) = get_opt_tag_value!(key_chars, MaxUsesPerBoot)? { // Track the use count for this key. let key_id = self.key_id(key_blob)?; self.update_use_count(key_id, *max_uses)?; } Ok(()) } /// Validate a `[keymint::HardwareAuthToken`]. fn check_auth_token( &self, auth_token: HardwareAuthToken, auth_info: &AuthInfo, now: Option, timeout_secs: Option, challenge: Option, ) -> Result<(), Error> { // Common check: confirm the HMAC tag in the token is valid. let mac_input = crate::hardware_auth_token_mac_input(&auth_token)?; if !self.verify_device_hmac(&mac_input, &auth_token.mac)? { return Err(km_err!(KeyUserNotAuthenticated, "failed to authenticate auth_token")); } // Common check: token's auth type should match key's USER_AUTH_TYPE. if (auth_token.authenticator_type as u32 & auth_info.auth_type) == 0 { return Err(km_err!( KeyUserNotAuthenticated, "token auth type {:?} doesn't overlap with key auth type {:?}", auth_token.authenticator_type, auth_info.auth_type, )); } // Common check: token's authenticator or user ID should match key's USER_SECURE_ID. if !auth_info.secure_ids.iter().any(|sid| { auth_token.user_id == *sid as i64 || auth_token.authenticator_id == *sid as i64 }) { return Err(km_err!( KeyUserNotAuthenticated, "neither user id {:?} nor authenticator id {:?} matches key", auth_token.user_id, auth_token.authenticator_id )); } // Optional check: token is in time range. if let (Some(now), Some(timeout_secs)) = (now, timeout_secs) { if now.milliseconds > auth_token.timestamp.milliseconds + 1000 * timeout_secs as i64 { return Err(km_err!( KeyUserNotAuthenticated, "now {:?} is later than auth token time {:?} + {} seconds", now, auth_token.timestamp, timeout_secs, )); } } // Optional check: challenge matches. if let Some(challenge) = challenge { if auth_token.challenge != challenge { return Err(km_err!(KeyUserNotAuthenticated, "challenge mismatch")); } } Ok(()) } /// Verify that an optional confirmation token matches the provided `data`. fn verify_confirmation_token(&self, data: &[u8], token: Option<&[u8]>) -> Result<(), Error> { if let Some(token) = token { if token.len() != CONFIRMATION_TOKEN_SIZE { return Err(km_err!( InvalidArgument, "confirmation token wrong length {}", token.len() )); } if self.verify_device_hmac(data, token).map_err(|e| { km_err!(VerificationFailed, "failed to perform HMAC on confirmation token: {:?}", e) })? { Ok(()) } else { Err(km_err!(NoUserConfirmation, "trusted confirmation token did not match")) } } else { Err(km_err!(NoUserConfirmation, "no trusted confirmation token provided")) } } /// Return the index of a free slot in the operations table. fn new_operation_index(&mut self) -> Result { self.operations.iter().position(Option::is_none).ok_or_else(|| { km_err!(TooManyOperations, "current op count {} >= limit", self.operations.len()) }) } /// Return a new operation handle value that is not currently in use in the /// operations table. fn new_op_handle(&mut self) -> OpHandle { loop { let op_handle = OpHandle(self.imp.rng.next_u64() as i64); if self.op_index(op_handle).is_err() { return op_handle; } // op_handle already in use, go around again. } } /// Return the index into the operations table of an operation identified by `op_handle`. fn op_index(&self, op_handle: OpHandle) -> Result { self.operations .iter() .position(|op| match op { Some(op) if op.handle == op_handle => true, Some(_op) => false, None => false, }) .ok_or_else(|| km_err!(InvalidOperation, "operation handle {:?} not found", op_handle)) } /// Execute the provided lambda over the associated [`Operation`], handling /// errors. fn with_authed_operation( &mut self, op_handle: OpHandle, auth_token: Option, timestamp_token: Option, f: F, ) -> Result where F: FnOnce(&mut Operation) -> Result, { let op_idx = self.op_index(op_handle)?; let check_again = self.check_subsequent_auth( self.operations[op_idx].as_ref().unwrap(/* safe: op_index() checks */ ), auth_token, timestamp_token, )?; let op = self.operations[op_idx].as_mut().unwrap(/* safe: op_index() checks */); if !check_again { op.auth_info = None; } let result = f(op); if result.is_err() { // A failure destroys the operation. if self.presence_required_op == Some(op_handle) { self.presence_required_op = None; } self.operations[op_idx] = None; } result } /// Return the associated [`Operation`], removing it. fn take_operation(&mut self, op_handle: OpHandle) -> Result { let op_idx = self.op_index(op_handle)?; Ok(self.operations[op_idx].take().unwrap(/* safe: op_index() checks */)) } /// Check authentication for an operation that has already begun. Returns an indication as to /// whether future invocations also need to check authentication. fn check_subsequent_auth( &self, op: &Operation, auth_token: Option, timestamp_token: Option, ) -> Result { if let Some(auth_info) = &op.auth_info { let auth_token = auth_token.ok_or_else(|| { km_err!(KeyUserNotAuthenticated, "no auth token on subsequent op") })?; // Most auth checks happen on begin(), but there are two exceptions. // a) There is no AUTH_TIMEOUT: there should be a valid auth token on every invocation. // b) There is an AUTH_TIMEOUT but we have no clock: the first invocation on the // operation (after `begin()`) should check the timeout, based on a provided // timestamp token. if let Some(timeout_secs) = auth_info.timeout_secs { if self.imp.clock.is_some() { return Err(km_err!( InvalidAuthorizationTimeout, "attempt to check auth timeout after begin() on device with clock!" )); } // Check that the timestamp token is valid. let timestamp_token = timestamp_token .ok_or_else(|| km_err!(InvalidArgument, "no timestamp token provided"))?; if timestamp_token.challenge != op.handle.0 { return Err(km_err!(InvalidArgument, "timestamp challenge mismatch")); } let mac_input = crate::clock::timestamp_token_mac_input(×tamp_token)?; if !self.verify_device_hmac(&mac_input, ×tamp_token.mac)? { return Err(km_err!(InvalidArgument, "timestamp MAC not verified")); } self.check_auth_token( auth_token, auth_info, Some(timestamp_token.timestamp), Some(timeout_secs), Some(op.handle.0), )?; // No need to check again. Ok(false) } else { self.check_auth_token(auth_token, auth_info, None, None, Some(op.handle.0))?; // Check on every invocation Ok(true) } } else { Ok(false) } } }