Merge changes from topic "keystore_api_for_credstore"
* changes:
Integrate getAuthTokensForCredStore method with credstore.
Add getAuthTokensForCredStore method to Authorization aidl.
Add get_auth_token permission to the permission module.
diff --git a/identity/Android.bp b/identity/Android.bp
index ed8ff2f..5841bf6 100644
--- a/identity/Android.bp
+++ b/identity/Android.bp
@@ -39,6 +39,7 @@
shared_libs: [
"libbase",
"libbinder",
+ "libbinder_ndk",
"libkeystore_aidl",
"libcredstore_aidl",
"libutils",
@@ -46,6 +47,9 @@
"android.hardware.identity-support-lib",
"libkeymaster4support",
"libkeystore-attestation-application-id",
+ "android.hardware.security.keymint-V1-ndk_platform",
+ "android.security.authorization-ndk_platform",
+ "PlatformProperties",
],
static_libs: [
"android.hardware.identity-V3-cpp",
diff --git a/identity/Credential.cpp b/identity/Credential.cpp
index a3c72ed..183ddeb 100644
--- a/identity/Credential.cpp
+++ b/identity/Credential.cpp
@@ -17,7 +17,7 @@
#define LOG_TAG "Credential"
#include <android-base/logging.h>
-
+#include <android/binder_manager.h>
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
#include <android/security/identity/ICredentialStore.h>
@@ -33,6 +33,12 @@
#include <future>
#include <tuple>
+#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>
+#include <aidl/android/hardware/security/secureclock/TimeStampToken.h>
+#include <aidl/android/security/authorization/AuthorizationTokens.h>
+#include <aidl/android/security/authorization/IKeystoreAuthorization.h>
+#include <android/sysprop/Keystore2Properties.sysprop.h>
+
#include "Credential.h"
#include "CredentialData.h"
#include "Util.h"
@@ -47,6 +53,7 @@
using std::tuple;
using android::security::keystore::IKeystoreService;
+using namespace android::sysprop;
using ::android::hardware::identity::IWritableIdentityCredential;
@@ -55,11 +62,17 @@
using ::android::hardware::identity::support::ecKeyPairGetPublicKey;
using ::android::hardware::identity::support::sha256;
+using android::hardware::keymaster::SecurityLevel;
using android::hardware::keymaster::V4_0::HardwareAuthToken;
using android::hardware::keymaster::V4_0::VerificationToken;
using AidlHardwareAuthToken = android::hardware::keymaster::HardwareAuthToken;
using AidlVerificationToken = android::hardware::keymaster::VerificationToken;
+using KeyMintAuthToken = ::aidl::android::hardware::security::keymint::HardwareAuthToken;
+using ::aidl::android::hardware::security::secureclock::TimeStampToken;
+using ::aidl::android::security::authorization::AuthorizationTokens;
+using ::aidl::android::security::authorization::IKeystoreAuthorization;
+
Credential::Credential(CipherSuite cipherSuite, const std::string& dataPath,
const std::string& credentialName, uid_t callingUid,
HardwareInformation hwInfo, sp<IIdentityCredentialStore> halStoreBinder,
@@ -168,9 +181,10 @@
// Returns false if an error occurred communicating with keystore.
//
-bool getTokensFromKeystore(uint64_t challenge, uint64_t secureUserId,
- unsigned int authTokenMaxAgeMillis, vector<uint8_t>& authToken,
- vector<uint8_t>& verificationToken) {
+bool getTokensFromKeystore1(uint64_t challenge, uint64_t secureUserId,
+ unsigned int authTokenMaxAgeMillis,
+ AidlHardwareAuthToken& aidlAuthToken,
+ AidlVerificationToken& aidlVerificationToken) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
sp<IKeystoreService> keystore = interface_cast<IKeystoreService>(binder);
@@ -189,19 +203,100 @@
auto fstatus = future.wait_for(std::chrono::milliseconds(5000));
if (fstatus != std::future_status::ready) {
- LOG(ERROR) << "Waited 5 seconds from tokens for credstore, aborting";
+ LOG(ERROR) << "Waited 5 seconds for tokens from keystore, aborting";
return false;
}
auto [success, returnedAuthToken, returnedVerificationToken] = future.get();
if (!success) {
- LOG(ERROR) << "Error getting tokens from credstore";
+ LOG(ERROR) << "Error getting tokens from keystore1";
return false;
}
- authToken = returnedAuthToken;
- verificationToken = returnedVerificationToken;
+ // It's entirely possible getTokensFromKeystore() succeeded but didn't
+ // return any tokens (in which case the returned byte-vectors are
+ // empty). For example, this can happen if no auth token is available
+ // which satifies e.g. |authTokenMaxAgeMillis|.
+ //
+ if (returnedAuthToken.size() > 0) {
+ HardwareAuthToken authToken =
+ android::hardware::keymaster::V4_0::support::hidlVec2AuthToken(returnedAuthToken);
+ // Convert from HIDL to AIDL...
+ aidlAuthToken.challenge = int64_t(authToken.challenge);
+ aidlAuthToken.userId = int64_t(authToken.userId);
+ aidlAuthToken.authenticatorId = int64_t(authToken.authenticatorId);
+ aidlAuthToken.authenticatorType = ::android::hardware::keymaster::HardwareAuthenticatorType(
+ int32_t(authToken.authenticatorType));
+ aidlAuthToken.timestamp.milliSeconds = int64_t(authToken.timestamp);
+ aidlAuthToken.mac = authToken.mac;
+ }
+
+ if (returnedVerificationToken.size() > 0) {
+ optional<VerificationToken> token =
+ android::hardware::keymaster::V4_0::support::deserializeVerificationToken(
+ returnedVerificationToken);
+ if (!token) {
+ LOG(ERROR) << "Error deserializing verification token";
+ }
+ aidlVerificationToken.challenge = token->challenge;
+ aidlVerificationToken.timestamp.milliSeconds = token->timestamp;
+ aidlVerificationToken.securityLevel =
+ ::android::hardware::keymaster::SecurityLevel(token->securityLevel);
+ aidlVerificationToken.mac = token->mac;
+ }
return true;
}
+// Returns false if an error occurred communicating with keystore.
+//
+bool getTokensFromKeystore2(uint64_t challenge, uint64_t secureUserId,
+ unsigned int authTokenMaxAgeMillis,
+ AidlHardwareAuthToken& aidlAuthToken,
+ AidlVerificationToken& aidlVerificationToken) {
+ // try to connect to IKeystoreAuthorization AIDL service first.
+ AIBinder* authzAIBinder = AServiceManager_checkService("android.security.authorization");
+ ::ndk::SpAIBinder authzBinder(authzAIBinder);
+ auto authzService = IKeystoreAuthorization::fromBinder(authzBinder);
+ if (authzService) {
+ AuthorizationTokens authzTokens;
+ auto result = authzService->getAuthTokensForCredStore(challenge, secureUserId,
+ authTokenMaxAgeMillis, &authzTokens);
+ // Convert KeyMint auth token to KeyMaster authtoken, only if tokens are
+ // returned
+ if (result.isOk()) {
+ KeyMintAuthToken keymintAuthToken = authzTokens.authToken;
+ aidlAuthToken.challenge = keymintAuthToken.challenge;
+ aidlAuthToken.userId = keymintAuthToken.userId;
+ aidlAuthToken.authenticatorId = keymintAuthToken.authenticatorId;
+ aidlAuthToken.authenticatorType =
+ ::android::hardware::keymaster::HardwareAuthenticatorType(
+ int32_t(keymintAuthToken.authenticatorType));
+ aidlAuthToken.timestamp.milliSeconds = keymintAuthToken.timestamp.milliSeconds;
+ aidlAuthToken.mac = keymintAuthToken.mac;
+
+ // Convert timestamp token to KeyMaster verification token
+ TimeStampToken timestampToken = authzTokens.timestampToken;
+ aidlVerificationToken.challenge = timestampToken.challenge;
+ aidlVerificationToken.timestamp.milliSeconds = timestampToken.timestamp.milliSeconds;
+ // Legacy verification tokens were always minted by TEE.
+ aidlVerificationToken.securityLevel = SecurityLevel::TRUSTED_ENVIRONMENT;
+ aidlVerificationToken.mac = timestampToken.mac;
+ } else {
+ if (result.getServiceSpecificError() == 0) {
+ // Here we differentiate the errors occurred during communication
+ // from the service specific errors.
+ LOG(ERROR) << "Error getting tokens from keystore2: " << result.getDescription();
+ return false;
+ } else {
+ // Log the reason for not finding auth tokens.
+ LOG(INFO) << "Auth tokens not found: " << result.getDescription();
+ }
+ }
+ return true;
+ } else {
+ LOG(ERROR) << "Error connecting to IKeystoreAuthorization service";
+ return false;
+ }
+}
+
Status Credential::getEntries(const vector<uint8_t>& requestMessage,
const vector<RequestNamespaceParcel>& requestNamespaces,
const vector<uint8_t>& sessionTranscript,
@@ -334,49 +429,23 @@
// not a guarantee and it's also not required.
//
- vector<uint8_t> authTokenBytes;
- vector<uint8_t> verificationTokenBytes;
- if (!getTokensFromKeystore(selectedChallenge_, data->getSecureUserId(),
- authTokenMaxAgeMillis, authTokenBytes, verificationTokenBytes)) {
- LOG(ERROR) << "Error getting tokens from keystore";
- return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
- "Error getting tokens from keystore");
- }
-
- // It's entirely possible getTokensFromKeystore() succeeded but didn't
- // return any tokens (in which case the returned byte-vectors are
- // empty). For example, this can happen if no auth token is available
- // which satifies e.g. |authTokenMaxAgeMillis|.
- //
- if (authTokenBytes.size() > 0) {
- HardwareAuthToken authToken =
- android::hardware::keymaster::V4_0::support::hidlVec2AuthToken(authTokenBytes);
-
- // Convert from HIDL to AIDL...
- aidlAuthToken.challenge = int64_t(authToken.challenge);
- aidlAuthToken.userId = int64_t(authToken.userId);
- aidlAuthToken.authenticatorId = int64_t(authToken.authenticatorId);
- aidlAuthToken.authenticatorType =
- ::android::hardware::keymaster::HardwareAuthenticatorType(
- int32_t(authToken.authenticatorType));
- aidlAuthToken.timestamp.milliSeconds = int64_t(authToken.timestamp);
- aidlAuthToken.mac = authToken.mac;
- }
-
- if (verificationTokenBytes.size() > 0) {
- optional<VerificationToken> token =
- android::hardware::keymaster::V4_0::support::deserializeVerificationToken(
- verificationTokenBytes);
- if (!token) {
- LOG(ERROR) << "Error deserializing verification token";
+ auto keystore2_status = Keystore2Properties::keystore2_enabled();
+ if (keystore2_status.has_value() && keystore2_status.value()) {
+ if (!getTokensFromKeystore2(selectedChallenge_, data->getSecureUserId(),
+ authTokenMaxAgeMillis, aidlAuthToken,
+ aidlVerificationToken)) {
+ LOG(ERROR) << "Error getting tokens from keystore2";
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
- "Error deserializing verification token");
+ "Error getting tokens from keystore2");
}
- aidlVerificationToken.challenge = token->challenge;
- aidlVerificationToken.timestamp.milliSeconds = token->timestamp;
- aidlVerificationToken.securityLevel =
- ::android::hardware::keymaster::SecurityLevel(token->securityLevel);
- aidlVerificationToken.mac = token->mac;
+ } else {
+ if (!getTokensFromKeystore1(selectedChallenge_, data->getSecureUserId(),
+ authTokenMaxAgeMillis, aidlAuthToken,
+ aidlVerificationToken)) {
+ LOG(ERROR) << "Error getting tokens from keystore";
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Error getting tokens from keystore");
+ }
}
}
diff --git a/identity/main.cpp b/identity/main.cpp
index 8f4968d..b51bd28 100644
--- a/identity/main.cpp
+++ b/identity/main.cpp
@@ -40,7 +40,7 @@
using ::android::security::identity::CredentialStoreFactory;
int main(int argc, char* argv[]) {
- InitLogging(argv, StderrLogger);
+ InitLogging(argv);
CHECK(argc == 2) << "A directory must be specified";
string data_dir = string(argv[1]);
diff --git a/keystore2/aidl/android/security/authorization/AuthorizationTokens.aidl b/keystore2/aidl/android/security/authorization/AuthorizationTokens.aidl
new file mode 100644
index 0000000..9061998
--- /dev/null
+++ b/keystore2/aidl/android/security/authorization/AuthorizationTokens.aidl
@@ -0,0 +1,33 @@
+// Copyright 2021, 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.
+
+package android.security.authorization;
+
+import android.hardware.security.keymint.HardwareAuthToken;
+import android.hardware.security.secureclock.TimeStampToken;
+
+/**
+ * This parcelable is returned by `IKeystoreAuthorization::getAuthTokensForCredStore`.
+ * @hide
+ */
+parcelable AuthorizationTokens {
+ /**
+ * HardwareAuthToken provided by an authenticator.
+ */
+ HardwareAuthToken authToken;
+ /**
+ * TimeStampToken provided by a SecureClock.
+ */
+ TimeStampToken timestampToken;
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
index df64401..6dc172e 100644
--- a/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
+++ b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
@@ -16,6 +16,7 @@
import android.hardware.security.keymint.HardwareAuthToken;
import android.security.authorization.LockScreenEvent;
+import android.security.authorization.AuthorizationTokens;
// TODO: mark the interface with @SensitiveData when the annotation is ready (b/176110256).
@@ -45,6 +46,8 @@
* ## Error conditions:
* `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'Unlock' permission.
* `ResponseCode::SYSTEM_ERROR` - if failed to perform lock/unlock operations due to various
+ * `ResponseCode::VALUE_CORRUPTED` - if the super key can not be decrypted.
+ * `ResponseCode::KEY_NOT_FOUND` - if the super key is not found.
*
* @lockScreenEvent - Indicates what happened.
* * LockScreenEvent.UNLOCK if the screen was unlocked.
@@ -56,4 +59,37 @@
*/
void onLockScreenEvent(in LockScreenEvent lockScreenEvent, in int userId,
in @nullable byte[] password);
+
+ /**
+ * Allows Credstore to retrieve a HardwareAuthToken and a TimestampToken.
+ * Identity Credential Trusted App can run either in the TEE or in other secure Hardware.
+ * So, credstore always need to retrieve a TimestampToken along with a HardwareAuthToken.
+ *
+ * The passed in |challenge| parameter must always be non-zero.
+ *
+ * The returned TimestampToken will always have its |challenge| field set to
+ * the |challenge| parameter.
+ *
+ * This method looks through auth-tokens cached by keystore which match
+ * the passed-in |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.
+ *
+ * This method is called by credstore (and only credstore).
+ *
+ * The caller requires 'get_auth_token' permission.
+ *
+ * ## Error conditions:
+ * `ResponseCode::PERMISSION_DENIED` - if the caller does not have the 'get_auth_token'
+ * permission.
+ * `ResponseCode::SYSTEM_ERROR` - if failed to obtain an authtoken from the database.
+ * `ResponseCode::NO_AUTH_TOKEN_FOUND` - a matching auth token is not found.
+ * `ResponseCode::INVALID_ARGUMENT` - if the passed-in |challenge| parameter is zero.
+ */
+ AuthorizationTokens getAuthTokensForCredStore(in long challenge, in long secureUserId,
+ in long authTokenMaxAgeMillis);
}
diff --git a/keystore2/aidl/android/security/authorization/ResponseCode.aidl b/keystore2/aidl/android/security/authorization/ResponseCode.aidl
new file mode 100644
index 0000000..94f1120
--- /dev/null
+++ b/keystore2/aidl/android/security/authorization/ResponseCode.aidl
@@ -0,0 +1,56 @@
+// Copyright 2021, 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.
+
+package android.security.authorization;
+
+/**
+ * Used as exception codes by IKeystoreAuthorization.
+ */
+@Backing(type="int")
+enum ResponseCode {
+ /**
+ * A matching auth token is not found.
+ */
+ NO_AUTH_TOKEN_FOUND = 1,
+ /**
+ * The matching auth token is expired.
+ */
+ AUTH_TOKEN_EXPIRED = 2,
+ /**
+ * Same as in keystore2/ResponseCode.aidl.
+ * Any unexpected Error such as IO or communication errors.
+ */
+ SYSTEM_ERROR = 4,
+ /**
+ * Same as in keystore2/ResponseCode.aidl.
+ * Indicates that the caller does not have the permissions for the attempted request.
+ */
+ PERMISSION_DENIED = 6,
+ /**
+ * Same as in keystore2/ResponseCode.aidl.
+ * Indicates that the requested key does not exist.
+ */
+ KEY_NOT_FOUND = 7,
+ /**
+ * Same as in keystore2/ResponseCode.aidl.
+ * Indicates that a value being processed is corrupted.
+ */
+ VALUE_CORRUPTED = 8,
+ /**
+ * Same as in keystore2/ResponseCode.aidl.
+ * Indicates that an invalid argument was passed to an API call.
+ */
+ INVALID_ARGUMENT = 20,
+
+ }
\ No newline at end of file
diff --git a/keystore2/src/authorization.rs b/keystore2/src/authorization.rs
index 02b19c4..75c5add 100644
--- a/keystore2/src/authorization.rs
+++ b/keystore2/src/authorization.rs
@@ -15,7 +15,6 @@
//! This module implements IKeystoreAuthorization AIDL interface.
use crate::error::Error as KeystoreError;
-use crate::error::map_or_log_err;
use crate::globals::{ENFORCEMENTS, SUPER_KEY, DB, LEGACY_MIGRATOR};
use crate::permission::KeystorePerm;
use crate::super_key::UserState;
@@ -23,14 +22,87 @@
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
HardwareAuthToken::HardwareAuthToken,
};
-use android_security_authorization::binder::{Interface, Result as BinderResult, Strong};
-use android_security_authorization::aidl::android::security::authorization::IKeystoreAuthorization::{
- BnKeystoreAuthorization, IKeystoreAuthorization,
+use android_security_authorization::binder::{ExceptionCode, Interface, Result as BinderResult,
+ Strong, Status as BinderStatus};
+use android_security_authorization::aidl::android::security::authorization::{
+ IKeystoreAuthorization::BnKeystoreAuthorization, IKeystoreAuthorization::IKeystoreAuthorization,
+ LockScreenEvent::LockScreenEvent, AuthorizationTokens::AuthorizationTokens,
+ ResponseCode::ResponseCode,
};
-use android_security_authorization:: aidl::android::security::authorization::LockScreenEvent::LockScreenEvent;
-use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
+use android_system_keystore2::aidl::android::system::keystore2::{
+ ResponseCode::ResponseCode as KsResponseCode };
use anyhow::{Context, Result};
use binder::IBinder;
+use keystore2_selinux as selinux;
+
+/// This is the Authorization error type, it wraps binder exceptions and the
+/// Authorization ResponseCode
+#[derive(Debug, thiserror::Error, PartialEq)]
+pub enum Error {
+ /// Wraps an IKeystoreAuthorization response code as defined by
+ /// android.security.authorization AIDL interface specification.
+ #[error("Error::Rc({0:?})")]
+ Rc(ResponseCode),
+ /// Wraps a Binder exception code other than a service specific exception.
+ #[error("Binder exception code {0:?}, {1:?}")]
+ Binder(ExceptionCode, i32),
+}
+
+/// This function should be used by authorization service calls to translate error conditions
+/// into service specific exceptions.
+///
+/// All error conditions get logged by this function.
+///
+/// `Error::Rc(x)` variants get mapped onto a service specific error code of `x`.
+/// Certain response codes may be returned from keystore/ResponseCode.aidl by the keystore2 modules,
+/// which are then converted to the corresponding response codes of android.security.authorization
+/// AIDL interface specification.
+///
+/// `selinux::Error::perm()` is mapped on `ResponseCode::PERMISSION_DENIED`.
+///
+/// All non `Error` error conditions get mapped onto ResponseCode::SYSTEM_ERROR`.
+///
+/// `handle_ok` will be called if `result` is `Ok(value)` where `value` will be passed
+/// as argument to `handle_ok`. `handle_ok` must generate a `BinderResult<T>`, but it
+/// typically returns Ok(value).
+pub fn map_or_log_err<T, U, F>(result: Result<U>, handle_ok: F) -> BinderResult<T>
+where
+ F: FnOnce(U) -> BinderResult<T>,
+{
+ result.map_or_else(
+ |e| {
+ log::error!("{:#?}", e);
+ let root_cause = e.root_cause();
+ if let Some(KeystoreError::Rc(ks_rcode)) = root_cause.downcast_ref::<KeystoreError>() {
+ let rc = match *ks_rcode {
+ // Although currently keystore2/ResponseCode.aidl and
+ // authorization/ResponseCode.aidl share the same integer values for the
+ // common response codes, this may deviate in the future, hence the
+ // conversion here.
+ KsResponseCode::SYSTEM_ERROR => ResponseCode::SYSTEM_ERROR.0,
+ KsResponseCode::KEY_NOT_FOUND => ResponseCode::KEY_NOT_FOUND.0,
+ KsResponseCode::VALUE_CORRUPTED => ResponseCode::VALUE_CORRUPTED.0,
+ KsResponseCode::INVALID_ARGUMENT => ResponseCode::INVALID_ARGUMENT.0,
+ // If the code paths of IKeystoreAuthorization aidl's methods happen to return
+ // other error codes from KsResponseCode in the future, they should be converted
+ // as well.
+ _ => ResponseCode::SYSTEM_ERROR.0,
+ };
+ return Err(BinderStatus::new_service_specific_error(rc, None));
+ }
+ let rc = match root_cause.downcast_ref::<Error>() {
+ Some(Error::Rc(rcode)) => rcode.0,
+ Some(Error::Binder(_, _)) => ResponseCode::SYSTEM_ERROR.0,
+ None => match root_cause.downcast_ref::<selinux::Error>() {
+ Some(selinux::Error::PermissionDenied) => ResponseCode::PERMISSION_DENIED.0,
+ _ => ResponseCode::SYSTEM_ERROR.0,
+ },
+ };
+ Err(BinderStatus::new_service_specific_error(rc, None))
+ },
+ handle_ok,
+ )
+}
/// This struct is defined to implement the aforementioned AIDL interface.
/// As of now, it is an empty struct.
@@ -99,11 +171,33 @@
}
_ => {
// Any other combination is not supported.
- Err(KeystoreError::Rc(ResponseCode::INVALID_ARGUMENT))
+ Err(Error::Rc(ResponseCode::INVALID_ARGUMENT))
.context("In on_lock_screen_event: Unknown event.")
}
}
}
+
+ fn get_auth_tokens_for_credstore(
+ &self,
+ challenge: i64,
+ secure_user_id: i64,
+ auth_token_max_age_millis: i64,
+ ) -> Result<AuthorizationTokens> {
+ // Check permission. Function should return if this failed. Therefore having '?' at the end
+ // is very important.
+ check_keystore_permission(KeystorePerm::get_auth_token())
+ .context("In get_auth_tokens_for_credstore.")?;
+
+ // if the challenge is zero, return error
+ if challenge == 0 {
+ return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT))
+ .context("In get_auth_tokens_for_credstore. Challenge can not be zero.");
+ }
+ // Obtain the auth token and the timestamp token from the enforcement module.
+ let (auth_token, ts_token) =
+ ENFORCEMENTS.get_auth_tokens(challenge, secure_user_id, auth_token_max_age_millis)?;
+ Ok(AuthorizationTokens { authToken: auth_token, timestampToken: ts_token })
+ }
}
impl Interface for AuthorizationManager {}
@@ -121,4 +215,20 @@
) -> BinderResult<()> {
map_or_log_err(self.on_lock_screen_event(lock_screen_event, user_id, password), Ok)
}
+
+ fn getAuthTokensForCredStore(
+ &self,
+ challenge: i64,
+ secure_user_id: i64,
+ auth_token_max_age_millis: i64,
+ ) -> binder::public_api::Result<AuthorizationTokens> {
+ map_or_log_err(
+ self.get_auth_tokens_for_credstore(
+ challenge,
+ secure_user_id,
+ auth_token_max_age_millis,
+ ),
+ Ok,
+ )
+ }
}
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 57ca7aa..5f19cf0 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -738,6 +738,11 @@
self.0
}
+ /// Returns the value of MonotonicRawTime in milli seconds as i64
+ pub fn milli_seconds(&self) -> i64 {
+ self.0 * 1000
+ }
+
/// Like i64::checked_sub.
pub fn checked_sub(&self, other: &Self) -> Option<Self> {
self.0.checked_sub(other.0).map(Self)
@@ -790,6 +795,11 @@
pub fn time_received(&self) -> MonotonicRawTime {
self.time_received
}
+
+ /// Returns the challenge value of the auth token.
+ pub fn challenge(&self) -> i64 {
+ self.auth_token.challenge
+ }
}
/// Shared in-memory databases get destroyed as soon as the last connection to them gets closed.
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index aa852f4..06648f1 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -14,6 +14,7 @@
//! This is the Keystore 2.0 Enforcements module.
// TODO: more description to follow.
+use crate::authorization::Error as AuthzError;
use crate::database::{AuthTokenEntry, MonotonicRawTime};
use crate::error::{map_binder_status, Error, ErrorCode};
use crate::globals::{get_timestamp_service, ASYNC_TASK, DB, ENFORCEMENTS};
@@ -26,6 +27,7 @@
use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
ISecureClock::ISecureClock, TimeStampToken::TimeStampToken,
};
+use android_security_authorization::aidl::android::security::authorization::ResponseCode::ResponseCode as AuthzResponseCode;
use android_system_keystore2::aidl::android::system::keystore2::{
IKeystoreSecurityLevel::KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING,
OperationChallenge::OperationChallenge,
@@ -218,7 +220,7 @@
if let Err(e) = sender.send(get_timestamp_token(challenge)) {
log::info!(
concat!(
- "In timestamp_token_request: Operation hung up ",
+ "In timestamp_token_request: Receiver hung up ",
"before timestamp token could be delivered. {:?}"
),
e
@@ -767,6 +769,66 @@
auth_bound && !skip_lskf_binding
}
+ /// 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)
+ })
+ .context(
+ "In get_auth_tokens: Failed to get a matching auth token filtered by challenge.",
+ )?;
+
+ 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().milli_seconds();
+ let result = Self::find_auth_token(|auth_token_entry: &AuthTokenEntry| {
+ let token_valid = now_in_millis
+ .checked_sub(auth_token_entry.time_received().milli_seconds())
+ .map_or(false, |token_age_in_millis| {
+ token_age_in_millis > auth_token_max_age_millis
+ });
+ token_valid && auth_token_entry.satisfies(&sids, auth_type)
+ })
+ .context(
+ "In get_auth_tokens: Failed to get a matching auth token filtered by age.",
+ )?;
+
+ if let Some((auth_token_entry, _)) = result {
+ auth_token_entry.take_auth_token()
+ } else {
+ return Err(AuthzError::Rc(AuthzResponseCode::NO_AUTH_TOKEN_FOUND))
+ .context("In get_auth_tokens: No auth token found.");
+ }
+ } else {
+ return Err(AuthzError::Rc(AuthzResponseCode::NO_AUTH_TOKEN_FOUND))
+ .context("In get_auth_tokens: 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("In get_auth_tokens. Error in getting timestamp token.")?;
+ Ok((auth_token, tst))
+ }
+
/// Watch the `keystore.boot_level` system property, and keep self.boot_level up to date.
/// Blocks waiting for system property changes, so must be run in its own thread.
pub fn watch_boot_level(&self) -> Result<()> {
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
index e2ee75f..905b460 100644
--- a/keystore2/src/permission.rs
+++ b/keystore2/src/permission.rs
@@ -308,6 +308,8 @@
ChangePassword = 0x100, selinux name: change_password;
/// Checked when a UID is cleared.
ClearUID = 0x200, selinux name: clear_uid;
+ /// Checked when Credstore calls IKeystoreAuthorization to obtain auth tokens.
+ GetAuthToken = 0x400, selinux name: get_auth_token;
}
);