blob: 8d5e9855a1d3988e9bed6a2fe3fc51aa71080851 [file] [log] [blame]
// Copyright 2020, 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.
//! This is the Keystore 2.0 Enforcements module.
// TODO: more description to follow.
use crate::ks_err;
use crate::error::{map_binder_status, Error, ErrorCode};
use crate::globals::{get_timestamp_service, ASYNC_TASK, DB, ENFORCEMENTS};
use crate::key_parameter::{KeyParameter, KeyParameterValue};
use crate::{authorization::Error as AuthzError, super_key::SuperEncryptionType};
use crate::{
database::{AuthTokenEntry, MonotonicRawTime},
globals::SUPER_KEY,
};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, ErrorCode::ErrorCode as Ec, HardwareAuthToken::HardwareAuthToken,
HardwareAuthenticatorType::HardwareAuthenticatorType,
KeyParameter::KeyParameter as KmKeyParameter, KeyPurpose::KeyPurpose, Tag::Tag,
};
use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
TimeStampToken::TimeStampToken,
};
use android_security_authorization::aidl::android::security::authorization::ResponseCode::ResponseCode as AuthzResponseCode;
use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, IKeystoreSecurityLevel::KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING,
OperationChallenge::OperationChallenge,
};
use anyhow::{Context, Result};
use std::{
collections::{HashMap, HashSet},
sync::{
mpsc::{channel, Receiver, Sender, TryRecvError},
Arc, Mutex, Weak,
},
time::SystemTime,
};
#[derive(Debug)]
enum AuthRequestState {
/// An outstanding per operation authorization request.
OpAuth,
/// An outstanding request for per operation authorization and secure timestamp.
TimeStampedOpAuth(Receiver<Result<TimeStampToken, Error>>),
/// An outstanding request for a timestamp token.
TimeStamp(Receiver<Result<TimeStampToken, Error>>),
}
#[derive(Debug)]
struct AuthRequest {
state: AuthRequestState,
/// This need to be set to Some to fulfill a AuthRequestState::OpAuth or
/// AuthRequestState::TimeStampedOpAuth.
hat: Mutex<Option<HardwareAuthToken>>,
}
unsafe impl Sync for AuthRequest {}
impl AuthRequest {
fn op_auth() -> Arc<Self> {
Arc::new(Self { state: AuthRequestState::OpAuth, hat: Mutex::new(None) })
}
fn timestamped_op_auth(receiver: Receiver<Result<TimeStampToken, Error>>) -> Arc<Self> {
Arc::new(Self {
state: AuthRequestState::TimeStampedOpAuth(receiver),
hat: Mutex::new(None),
})
}
fn timestamp(
hat: HardwareAuthToken,
receiver: Receiver<Result<TimeStampToken, Error>>,
) -> Arc<Self> {
Arc::new(Self { state: AuthRequestState::TimeStamp(receiver), hat: Mutex::new(Some(hat)) })
}
fn add_auth_token(&self, hat: HardwareAuthToken) {
*self.hat.lock().unwrap() = Some(hat)
}
fn get_auth_tokens(&self) -> Result<(HardwareAuthToken, Option<TimeStampToken>)> {
let hat = self
.hat
.lock()
.unwrap()
.take()
.ok_or(Error::Km(ErrorCode::KEY_USER_NOT_AUTHENTICATED))
.context(ks_err!("No operation auth token received."))?;
let tst = match &self.state {
AuthRequestState::TimeStampedOpAuth(recv) | AuthRequestState::TimeStamp(recv) => {
let result = recv.recv().context("In get_auth_tokens: Sender disconnected.")?;
Some(result.context(ks_err!(
"Worker responded with error \
from generating timestamp token.",
))?)
}
AuthRequestState::OpAuth => None,
};
Ok((hat, tst))
}
}
/// DeferredAuthState describes how auth tokens and timestamp tokens need to be provided when
/// updating and finishing an operation.
#[derive(Debug)]
enum DeferredAuthState {
/// Used when an operation does not require further authorization.
NoAuthRequired,
/// Indicates that the operation requires an operation specific token. This means we have
/// to return an operation challenge to the client which should reward us with an
/// operation specific auth token. If it is not provided before the client calls update
/// or finish, the operation fails as not authorized.
OpAuthRequired,
/// Indicates that the operation requires a time stamp token. The auth token was already
/// loaded from the database, but it has to be accompanied by a time stamp token to inform
/// the target KM with a different clock about the time on the authenticators.
TimeStampRequired(HardwareAuthToken),
/// Indicates that both an operation bound auth token and a verification token are
/// before the operation can commence.
TimeStampedOpAuthRequired,
/// In this state the auth info is waiting for the deferred authorizations to come in.
/// We block on timestamp tokens, because we can always make progress on these requests.
/// The per-op auth tokens might never come, which means we fail if the client calls
/// update or finish before we got a per-op auth token.
Waiting(Arc<AuthRequest>),
/// In this state we have gotten all of the required tokens, we just cache them to
/// be used when the operation progresses.
Token(HardwareAuthToken, Option<TimeStampToken>),
}
/// Auth info hold all of the authorization related information of an operation. It is stored
/// in and owned by the operation. It is constructed by authorize_create and stays with the
/// operation until it completes.
#[derive(Debug)]
pub struct AuthInfo {
state: DeferredAuthState,
/// An optional key id required to update the usage count if the key usage is limited.
key_usage_limited: Option<i64>,
confirmation_token_receiver: Option<Arc<Mutex<Option<Receiver<Vec<u8>>>>>>,
}
struct TokenReceiverMap {
/// The map maps an outstanding challenge to a TokenReceiver. If an incoming Hardware Auth
/// Token (HAT) has the map key in its challenge field, it gets passed to the TokenReceiver
/// and the entry is removed from the map. In the case where no HAT is received before the
/// corresponding operation gets dropped, the entry goes stale. So every time the cleanup
/// counter (second field in the tuple) turns 0, the map is cleaned from stale entries.
/// The cleanup counter is decremented every time a new receiver is added.
/// and reset to TokenReceiverMap::CLEANUP_PERIOD + 1 after each cleanup.
map_and_cleanup_counter: Mutex<(HashMap<i64, TokenReceiver>, u8)>,
}
impl Default for TokenReceiverMap {
fn default() -> Self {
Self { map_and_cleanup_counter: Mutex::new((HashMap::new(), Self::CLEANUP_PERIOD + 1)) }
}
}
impl TokenReceiverMap {
/// There is a chance that receivers may become stale because their operation is dropped
/// without ever being authorized. So occasionally we iterate through the map and throw
/// out obsolete entries.
/// This is the number of calls to add_receiver between cleanups.
const CLEANUP_PERIOD: u8 = 25;
pub fn add_auth_token(&self, hat: HardwareAuthToken) {
let recv = {
// Limit the scope of the mutex guard, so that it is not held while the auth token is
// added.
let mut map = self.map_and_cleanup_counter.lock().unwrap();
let (ref mut map, _) = *map;
map.remove_entry(&hat.challenge)
};
if let Some((_, recv)) = recv {
recv.add_auth_token(hat);
}
}
pub fn add_receiver(&self, challenge: i64, recv: TokenReceiver) {
let mut map = self.map_and_cleanup_counter.lock().unwrap();
let (ref mut map, ref mut cleanup_counter) = *map;
map.insert(challenge, recv);
*cleanup_counter -= 1;
if *cleanup_counter == 0 {
map.retain(|_, v| !v.is_obsolete());
map.shrink_to_fit();
*cleanup_counter = Self::CLEANUP_PERIOD + 1;
}
}
}
#[derive(Debug)]
struct TokenReceiver(Weak<AuthRequest>);
impl TokenReceiver {
fn is_obsolete(&self) -> bool {
self.0.upgrade().is_none()
}
fn add_auth_token(&self, hat: HardwareAuthToken) {
if let Some(state_arc) = self.0.upgrade() {
state_arc.add_auth_token(hat);
}
}
}
fn get_timestamp_token(challenge: i64) -> Result<TimeStampToken, Error> {
let dev = get_timestamp_service().expect(concat!(
"Secure Clock service must be present ",
"if TimeStampTokens are required."
));
map_binder_status(dev.generateTimeStamp(challenge))
}
fn timestamp_token_request(challenge: i64, sender: Sender<Result<TimeStampToken, Error>>) {
if let Err(e) = sender.send(get_timestamp_token(challenge)) {
log::info!(
concat!("Receiver hung up ", "before timestamp token could be delivered. {:?}"),
e
);
}
}
impl AuthInfo {
/// This function gets called after an operation was successfully created.
/// It makes all the preparations required, so that the operation has all the authentication
/// related artifacts to advance on update and finish.
pub fn finalize_create_authorization(&mut self, challenge: i64) -> Option<OperationChallenge> {
match &self.state {
DeferredAuthState::OpAuthRequired => {
let auth_request = AuthRequest::op_auth();
let token_receiver = TokenReceiver(Arc::downgrade(&auth_request));
ENFORCEMENTS.register_op_auth_receiver(challenge, token_receiver);
self.state = DeferredAuthState::Waiting(auth_request);
Some(OperationChallenge { challenge })
}
DeferredAuthState::TimeStampedOpAuthRequired => {
let (sender, receiver) = channel::<Result<TimeStampToken, Error>>();
let auth_request = AuthRequest::timestamped_op_auth(receiver);
let token_receiver = TokenReceiver(Arc::downgrade(&auth_request));
ENFORCEMENTS.register_op_auth_receiver(challenge, token_receiver);
ASYNC_TASK.queue_hi(move |_| timestamp_token_request(challenge, sender));
self.state = DeferredAuthState::Waiting(auth_request);
Some(OperationChallenge { challenge })
}
DeferredAuthState::TimeStampRequired(hat) => {
let hat = (*hat).clone();
let (sender, receiver) = channel::<Result<TimeStampToken, Error>>();
let auth_request = AuthRequest::timestamp(hat, receiver);
ASYNC_TASK.queue_hi(move |_| timestamp_token_request(challenge, sender));
self.state = DeferredAuthState::Waiting(auth_request);
None
}
_ => None,
}
}
/// This function is the authorization hook called before operation update.
/// It returns the auth tokens required by the operation to commence update.
pub fn before_update(&mut self) -> Result<(Option<HardwareAuthToken>, Option<TimeStampToken>)> {
self.get_auth_tokens()
}
/// This function is the authorization hook called before operation finish.
/// It returns the auth tokens required by the operation to commence finish.
/// The third token is a confirmation token.
pub fn before_finish(
&mut self,
) -> Result<(Option<HardwareAuthToken>, Option<TimeStampToken>, Option<Vec<u8>>)> {
let mut confirmation_token: Option<Vec<u8>> = None;
if let Some(ref confirmation_token_receiver) = self.confirmation_token_receiver {
let locked_receiver = confirmation_token_receiver.lock().unwrap();
if let Some(ref receiver) = *locked_receiver {
loop {
match receiver.try_recv() {
// As long as we get tokens we loop and discard all but the most
// recent one.
Ok(t) => confirmation_token = Some(t),
Err(TryRecvError::Empty) => break,
Err(TryRecvError::Disconnected) => {
log::error!(concat!(
"We got disconnected from the APC service, ",
"this should never happen."
));
break;
}
}
}
}
}
self.get_auth_tokens().map(|(hat, tst)| (hat, tst, confirmation_token))
}
/// This function is the authorization hook called after finish succeeded.
/// As of this writing it checks if the key was a limited use key. If so it updates the
/// use counter of the key in the database. When the use counter is depleted, the key gets
/// marked for deletion and the garbage collector is notified.
pub fn after_finish(&self) -> Result<()> {
if let Some(key_id) = self.key_usage_limited {
// On the last successful use, the key gets deleted. In this case we
// have to notify the garbage collector.
DB.with(|db| {
db.borrow_mut()
.check_and_update_key_usage_count(key_id)
.context("Trying to update key usage count.")
})
.context(ks_err!())?;
}
Ok(())
}
/// This function returns the auth tokens as needed by the ongoing operation or fails
/// with ErrorCode::KEY_USER_NOT_AUTHENTICATED. If this was called for the first time
/// after a deferred authorization was requested by finalize_create_authorization, this
/// function may block on the generation of a time stamp token. It then moves the
/// tokens into the DeferredAuthState::Token state for future use.
fn get_auth_tokens(&mut self) -> Result<(Option<HardwareAuthToken>, Option<TimeStampToken>)> {
let deferred_tokens = if let DeferredAuthState::Waiting(ref auth_request) = self.state {
Some(auth_request.get_auth_tokens().context("In AuthInfo::get_auth_tokens.")?)
} else {
None
};
if let Some((hat, tst)) = deferred_tokens {
self.state = DeferredAuthState::Token(hat, tst);
}
match &self.state {
DeferredAuthState::NoAuthRequired => Ok((None, None)),
DeferredAuthState::Token(hat, tst) => Ok((Some((*hat).clone()), (*tst).clone())),
DeferredAuthState::OpAuthRequired
| DeferredAuthState::TimeStampedOpAuthRequired
| DeferredAuthState::TimeStampRequired(_) => {
Err(Error::Km(ErrorCode::KEY_USER_NOT_AUTHENTICATED)).context(ks_err!(
"No operation auth token requested??? \
This should not happen."
))
}
// This should not be reachable, because it should have been handled above.
DeferredAuthState::Waiting(_) => {
Err(Error::sys()).context(ks_err!("AuthInfo::get_auth_tokens: Cannot be reached.",))
}
}
}
}
/// Enforcements data structure
#[derive(Default)]
pub struct Enforcements {
/// This hash set contains the user ids for whom the device is currently unlocked. If a user id
/// is not in the set, it implies that the device is locked for the user.
device_unlocked_set: Mutex<HashSet<i32>>,
/// This field maps outstanding auth challenges to their operations. When an auth token
/// with the right challenge is received it is passed to the map using
/// TokenReceiverMap::add_auth_token() which removes the entry from the map. If an entry goes
/// stale, because the operation gets dropped before an auth token is received, the map
/// is cleaned up in regular intervals.
op_auth_map: TokenReceiverMap,
/// The enforcement module will try to get a confirmation token from this channel whenever
/// an operation that requires confirmation finishes.
confirmation_token_receiver: Arc<Mutex<Option<Receiver<Vec<u8>>>>>,
}
impl Enforcements {
/// Install the confirmation token receiver. The enforcement module will try to get a
/// confirmation token from this channel whenever an operation that requires confirmation
/// finishes.
pub fn install_confirmation_token_receiver(
&self,
confirmation_token_receiver: Receiver<Vec<u8>>,
) {
*self.confirmation_token_receiver.lock().unwrap() = Some(confirmation_token_receiver);
}
/// Checks if a create call is authorized, given key parameters and operation parameters.
/// It returns an optional immediate auth token which can be presented to begin, and an
/// AuthInfo object which stays with the authorized operation and is used to obtain
/// auth tokens and timestamp tokens as required by the operation.
/// With regard to auth tokens, the following steps are taken:
///
/// If no key parameters are given (typically when the client is self managed
/// (see Domain.Blob)) nothing is enforced.
/// If the key is time-bound, find a matching auth token from the database.
/// If the above step is successful, and if requires_timestamp is given, the returned
/// AuthInfo will provide a Timestamp token as appropriate.
pub fn authorize_create(
&self,
purpose: KeyPurpose,
key_properties: Option<&(i64, Vec<KeyParameter>)>,
op_params: &[KmKeyParameter],
requires_timestamp: bool,
) -> Result<(Option<HardwareAuthToken>, AuthInfo)> {
let (key_id, key_params) = match key_properties {
Some((key_id, key_params)) => (*key_id, key_params),
None => {
return Ok((
None,
AuthInfo {
state: DeferredAuthState::NoAuthRequired,
key_usage_limited: None,
confirmation_token_receiver: None,
},
));
}
};
match purpose {
// Allow SIGN, DECRYPT for both symmetric and asymmetric keys.
KeyPurpose::SIGN | KeyPurpose::DECRYPT => {}
// Rule out WRAP_KEY purpose
KeyPurpose::WRAP_KEY => {
return Err(Error::Km(Ec::INCOMPATIBLE_PURPOSE))
.context(ks_err!("WRAP_KEY purpose is not allowed here.",));
}
// Allow AGREE_KEY for EC keys only.
KeyPurpose::AGREE_KEY => {
for kp in key_params.iter() {
if kp.get_tag() == Tag::ALGORITHM
&& *kp.key_parameter_value() != KeyParameterValue::Algorithm(Algorithm::EC)
{
return Err(Error::Km(Ec::UNSUPPORTED_PURPOSE))
.context(ks_err!("key agreement is only supported for EC keys.",));
}
}
}
KeyPurpose::VERIFY | KeyPurpose::ENCRYPT => {
// We do not support ENCRYPT and VERIFY (the remaining two options of purpose) for
// asymmetric keys.
for kp in key_params.iter() {
match *kp.key_parameter_value() {
KeyParameterValue::Algorithm(Algorithm::RSA)
| KeyParameterValue::Algorithm(Algorithm::EC) => {
return Err(Error::Km(Ec::UNSUPPORTED_PURPOSE)).context(ks_err!(
"public operations on asymmetric keys are \
not supported."
));
}
_ => {}
}
}
}
_ => {
return Err(Error::Km(Ec::UNSUPPORTED_PURPOSE))
.context(ks_err!("authorize_create: specified purpose is not supported."));
}
}
// The following variables are to record information from key parameters to be used in
// enforcements, when two or more such pieces of information are required for enforcements.
// There is only one additional variable than what legacy keystore has, but this helps
// reduce the number of for loops on key parameters from 3 to 1, compared to legacy keystore
let mut key_purpose_authorized: bool = false;
let mut user_auth_type: Option<HardwareAuthenticatorType> = None;
let mut no_auth_required: bool = false;
let mut caller_nonce_allowed = false;
let mut user_id: i32 = -1;
let mut user_secure_ids = Vec::<i64>::new();
let mut key_time_out: Option<i64> = None;
let mut allow_while_on_body = false;
let mut unlocked_device_required = false;
let mut key_usage_limited: Option<i64> = None;
let mut confirmation_token_receiver: Option<Arc<Mutex<Option<Receiver<Vec<u8>>>>>> = None;
let mut max_boot_level: Option<i32> = None;
// iterate through key parameters, recording information we need for authorization
// enforcements later, or enforcing authorizations in place, where applicable
for key_param in key_params.iter() {
match key_param.key_parameter_value() {
KeyParameterValue::NoAuthRequired => {
no_auth_required = true;
}
KeyParameterValue::AuthTimeout(t) => {
key_time_out = Some(*t as i64);
}
KeyParameterValue::HardwareAuthenticatorType(a) => {
user_auth_type = Some(*a);
}
KeyParameterValue::KeyPurpose(p) => {
// The following check has the effect of key_params.contains(purpose)
// Also, authorizing purpose can not be completed here, if there can be multiple
// key parameters for KeyPurpose.
key_purpose_authorized = key_purpose_authorized || *p == purpose;
}
KeyParameterValue::CallerNonce => {
caller_nonce_allowed = true;
}
KeyParameterValue::ActiveDateTime(a) => {
if !Enforcements::is_given_time_passed(*a, true) {
return Err(Error::Km(Ec::KEY_NOT_YET_VALID))
.context(ks_err!("key is not yet active."));
}
}
KeyParameterValue::OriginationExpireDateTime(o) => {
if (purpose == KeyPurpose::ENCRYPT || purpose == KeyPurpose::SIGN)
&& Enforcements::is_given_time_passed(*o, false)
{
return Err(Error::Km(Ec::KEY_EXPIRED)).context(ks_err!("key is expired."));
}
}
KeyParameterValue::UsageExpireDateTime(u) => {
if (purpose == KeyPurpose::DECRYPT || purpose == KeyPurpose::VERIFY)
&& Enforcements::is_given_time_passed(*u, false)
{
return Err(Error::Km(Ec::KEY_EXPIRED)).context(ks_err!("key is expired."));
}
}
KeyParameterValue::UserSecureID(s) => {
user_secure_ids.push(*s);
}
KeyParameterValue::UserID(u) => {
user_id = *u;
}
KeyParameterValue::UnlockedDeviceRequired => {
unlocked_device_required = true;
}
KeyParameterValue::AllowWhileOnBody => {
allow_while_on_body = true;
}
KeyParameterValue::UsageCountLimit(_) => {
// We don't examine the limit here because this is enforced on finish.
// Instead, we store the key_id so that finish can look up the key
// in the database again and check and update the counter.
key_usage_limited = Some(key_id);
}
KeyParameterValue::TrustedConfirmationRequired => {
confirmation_token_receiver = Some(self.confirmation_token_receiver.clone());
}
KeyParameterValue::MaxBootLevel(level) => {
max_boot_level = Some(*level);
}
// NOTE: as per offline discussion, sanitizing key parameters and rejecting
// create operation if any non-allowed tags are present, is not done in
// authorize_create (unlike in legacy keystore where AuthorizeBegin is rejected if
// a subset of non-allowed tags are present). Because sanitizing key parameters
// should have been done during generate/import key, by KeyMint.
_ => { /*Do nothing on all the other key parameters, as in legacy keystore*/ }
}
}
// authorize the purpose
if !key_purpose_authorized {
return Err(Error::Km(Ec::INCOMPATIBLE_PURPOSE))
.context(ks_err!("the purpose is not authorized."));
}
// if both NO_AUTH_REQUIRED and USER_SECURE_ID tags are present, return error
if !user_secure_ids.is_empty() && no_auth_required {
return Err(Error::Km(Ec::INVALID_KEY_BLOB))
.context(ks_err!("key has both NO_AUTH_REQUIRED and USER_SECURE_ID tags."));
}
// if either of auth_type or secure_id is present and the other is not present, return error
if (user_auth_type.is_some() && user_secure_ids.is_empty())
|| (user_auth_type.is_none() && !user_secure_ids.is_empty())
{
return Err(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(ks_err!(
"Auth required, but either auth type or secure ids \
are not present."
));
}
// validate caller nonce for origination purposes
if (purpose == KeyPurpose::ENCRYPT || purpose == KeyPurpose::SIGN)
&& !caller_nonce_allowed
&& op_params.iter().any(|kp| kp.tag == Tag::NONCE)
{
return Err(Error::Km(Ec::CALLER_NONCE_PROHIBITED))
.context(ks_err!("NONCE is present, although CALLER_NONCE is not present"));
}
if unlocked_device_required {
// check the device locked status. If locked, operations on the key are not
// allowed.
if self.is_device_locked(user_id) {
return Err(Error::Km(Ec::DEVICE_LOCKED)).context(ks_err!("device is locked."));
}
}
if let Some(level) = max_boot_level {
if !SUPER_KEY.read().unwrap().level_accessible(level) {
return Err(Error::Km(Ec::BOOT_LEVEL_EXCEEDED))
.context(ks_err!("boot level is too late."));
}
}
if !unlocked_device_required && no_auth_required {
return Ok((
None,
AuthInfo {
state: DeferredAuthState::NoAuthRequired,
key_usage_limited,
confirmation_token_receiver,
},
));
}
let has_sids = !user_secure_ids.is_empty();
let timeout_bound = key_time_out.is_some() && has_sids;
let per_op_bound = key_time_out.is_none() && has_sids;
let need_auth_token = timeout_bound || unlocked_device_required;
let hat_and_last_off_body = if need_auth_token {
let hat_and_last_off_body = Self::find_auth_token(|hat: &AuthTokenEntry| {
if let (Some(auth_type), true) = (user_auth_type, timeout_bound) {
hat.satisfies(&user_secure_ids, auth_type)
} else {
unlocked_device_required
}
});
Some(
hat_and_last_off_body
.ok_or(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
.context(ks_err!("No suitable auth token found."))?,
)
} else {
None
};
// Now check the validity of the auth token if the key is timeout bound.
let hat = match (hat_and_last_off_body, key_time_out) {
(Some((hat, last_off_body)), Some(key_time_out)) => {
let now = MonotonicRawTime::now();
let token_age = now
.checked_sub(&hat.time_received())
.ok_or_else(Error::sys)
.context(ks_err!(
"Overflow while computing Auth token validity. \
Validity cannot be established."
))?;
let on_body_extended = allow_while_on_body && last_off_body < hat.time_received();
if token_age.seconds() > key_time_out && !on_body_extended {
return Err(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
.context(ks_err!("matching auth token is expired."));
}
Some(hat)
}
(Some((hat, _)), None) => Some(hat),
// If timeout_bound is true, above code must have retrieved a HAT or returned with
// KEY_USER_NOT_AUTHENTICATED. This arm should not be reachable.
(None, Some(_)) => panic!("Logical error."),
_ => None,
};
Ok(match (hat, requires_timestamp, per_op_bound) {
// Per-op-bound and Some(hat) can only happen if we are both per-op bound and unlocked
// device required. In addition, this KM instance needs a timestamp token.
// So the HAT cannot be presented on create. So on update/finish we present both
// an per-op-bound auth token and a timestamp token.
(Some(_), true, true) => (None, DeferredAuthState::TimeStampedOpAuthRequired),
(Some(hat), true, false) => (
Some(hat.auth_token().clone()),
DeferredAuthState::TimeStampRequired(hat.take_auth_token()),
),
(Some(hat), false, true) => {
(Some(hat.take_auth_token()), DeferredAuthState::OpAuthRequired)
}
(Some(hat), false, false) => {
(Some(hat.take_auth_token()), DeferredAuthState::NoAuthRequired)
}
(None, _, true) => (None, DeferredAuthState::OpAuthRequired),
(None, _, false) => (None, DeferredAuthState::NoAuthRequired),
})
.map(|(hat, state)| {
(hat, AuthInfo { state, key_usage_limited, confirmation_token_receiver })
})
}
fn find_auth_token<F>(p: F) -> Option<(AuthTokenEntry, MonotonicRawTime)>
where
F: Fn(&AuthTokenEntry) -> bool,
{
DB.with(|db| db.borrow().find_auth_token_entry(p))
}
/// Checks if the time now since epoch is greater than (or equal, if is_given_time_inclusive is
/// set) the given time (in milliseconds)
fn is_given_time_passed(given_time: i64, is_given_time_inclusive: bool) -> bool {
let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH);
let time_since_epoch = match duration_since_epoch {
Ok(duration) => duration.as_millis(),
Err(_) => return false,
};
if is_given_time_inclusive {
time_since_epoch >= (given_time as u128)
} else {
time_since_epoch > (given_time as u128)
}
}
/// Check if the device is locked for the given user. If there's no entry yet for the user,
/// we assume that the device is locked
fn is_device_locked(&self, user_id: i32) -> bool {
// unwrap here because there's no way this mutex guard can be poisoned and
// because there's no way to recover, even if it is poisoned.
let set = self.device_unlocked_set.lock().unwrap();
!set.contains(&user_id)
}
/// Sets the device locked status for the user. This method is called externally.
pub fn set_device_locked(&self, user_id: i32, device_locked_status: bool) {
// unwrap here because there's no way this mutex guard can be poisoned and
// because there's no way to recover, even if it is poisoned.
let mut set = self.device_unlocked_set.lock().unwrap();
if device_locked_status {
set.remove(&user_id);
} else {
set.insert(user_id);
}
}
/// Add this auth token to the database.
/// Then present the auth token to the op auth map. If an operation is waiting for this
/// auth token this fulfills the request and removes the receiver from the map.
pub fn add_auth_token(&self, hat: HardwareAuthToken) {
DB.with(|db| db.borrow_mut().insert_auth_token(&hat));
self.op_auth_map.add_auth_token(hat);
}
/// This allows adding an entry to the op_auth_map, indexed by the operation challenge.
/// This is to be called by create_operation, once it has received the operation challenge
/// from keymint for an operation whose authorization decision is OpAuthRequired, as signalled
/// by the DeferredAuthState.
fn register_op_auth_receiver(&self, challenge: i64, recv: TokenReceiver) {
self.op_auth_map.add_receiver(challenge, recv);
}
/// Given the set of key parameters and flags, check if super encryption is required.
pub fn super_encryption_required(
domain: &Domain,
key_parameters: &[KeyParameter],
flags: Option<i32>,
) -> SuperEncryptionType {
if let Some(flags) = flags {
if (flags & KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING) != 0 {
return SuperEncryptionType::None;
}
}
// Each answer has a priority, numerically largest priority wins.
struct Candidate {
priority: u32,
enc_type: SuperEncryptionType,
}
let mut result = Candidate { priority: 0, enc_type: SuperEncryptionType::None };
for kp in key_parameters {
let t = match kp.key_parameter_value() {
KeyParameterValue::MaxBootLevel(level) => {
Candidate { priority: 3, enc_type: SuperEncryptionType::BootLevel(*level) }
}
KeyParameterValue::UnlockedDeviceRequired if *domain == Domain::APP => {
Candidate { priority: 2, enc_type: SuperEncryptionType::ScreenLockBound }
}
KeyParameterValue::UserSecureID(_) if *domain == Domain::APP => {
Candidate { priority: 1, enc_type: SuperEncryptionType::LskfBound }
}
_ => Candidate { priority: 0, enc_type: SuperEncryptionType::None },
};
if t.priority > result.priority {
result = t;
}
}
result.enc_type
}
/// Finds a matching auth token along with a timestamp token.
/// This method looks through auth-tokens cached by keystore which satisfy the given
/// authentication information (i.e. |secureUserId|).
/// The most recent matching auth token which has a |challenge| field which matches
/// the passed-in |challenge| parameter is returned.
/// In this case the |authTokenMaxAgeMillis| parameter is not used.
///
/// Otherwise, the most recent matching auth token which is younger than |authTokenMaxAgeMillis|
/// is returned.
pub fn get_auth_tokens(
&self,
challenge: i64,
secure_user_id: i64,
auth_token_max_age_millis: i64,
) -> Result<(HardwareAuthToken, TimeStampToken)> {
let auth_type = HardwareAuthenticatorType::ANY;
let sids: Vec<i64> = vec![secure_user_id];
// Filter the matching auth tokens by challenge
let result = Self::find_auth_token(|hat: &AuthTokenEntry| {
(challenge == hat.challenge()) && hat.satisfies(&sids, auth_type)
});
let auth_token = if let Some((auth_token_entry, _)) = result {
auth_token_entry.take_auth_token()
} else {
// Filter the matching auth tokens by age.
if auth_token_max_age_millis != 0 {
let now_in_millis = MonotonicRawTime::now();
let result = Self::find_auth_token(|auth_token_entry: &AuthTokenEntry| {
let token_valid = now_in_millis
.checked_sub(&auth_token_entry.time_received())
.map_or(false, |token_age_in_millis| {
auth_token_max_age_millis > token_age_in_millis.milliseconds()
});
token_valid && auth_token_entry.satisfies(&sids, auth_type)
});
if let Some((auth_token_entry, _)) = result {
auth_token_entry.take_auth_token()
} else {
return Err(AuthzError::Rc(AuthzResponseCode::NO_AUTH_TOKEN_FOUND))
.context(ks_err!("No auth token found."));
}
} else {
return Err(AuthzError::Rc(AuthzResponseCode::NO_AUTH_TOKEN_FOUND)).context(
ks_err!(
"No auth token found for \
the given challenge and passed-in auth token max age is zero."
),
);
}
};
// Wait and obtain the timestamp token from secure clock service.
let tst =
get_timestamp_token(challenge).context(ks_err!("Error in getting timestamp token."))?;
Ok((auth_token, tst))
}
}
// TODO: Add tests to enforcement module (b/175578618).