Merge "Keystore 2.0: Small performance gain"
diff --git a/OWNERS b/OWNERS
index fca66f8..93c024d 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,4 +1,8 @@
-swillden@google.com
cbrubaker@google.com
+hasinitg@google.com
+jbires@google.com
jdanis@google.com
-kroot@google.com
\ No newline at end of file
+kroot@google.com
+sethmo@google.com
+swillden@google.com
+zeuthen@google.com
diff --git a/fsverity_init/fsverity_init.cpp b/fsverity_init/fsverity_init.cpp
index b81fb22..7bc6022 100644
--- a/fsverity_init/fsverity_init.cpp
+++ b/fsverity_init/fsverity_init.cpp
@@ -37,18 +37,21 @@
return true;
}
-void LoadKeyFromStdin(key_serial_t keyring_id, const char* keyname) {
+bool LoadKeyFromStdin(key_serial_t keyring_id, const char* keyname) {
std::string content;
if (!android::base::ReadFdToString(STDIN_FILENO, &content)) {
LOG(ERROR) << "Failed to read key from stdin";
- return;
+ return false;
}
if (!LoadKeyToKeyring(keyring_id, keyname, content.c_str(), content.size())) {
LOG(ERROR) << "Failed to load key from stdin";
+ return false;
}
+ return true;
}
void LoadKeyFromFile(key_serial_t keyring_id, const char* keyname, const std::string& path) {
+ LOG(INFO) << "LoadKeyFromFile path=" << path << " keyname=" << keyname;
std::string content;
if (!android::base::ReadFileToString(path, &content)) {
LOG(ERROR) << "Failed to read key from " << path;
@@ -59,22 +62,24 @@
}
}
-void LoadKeyFromDirectory(key_serial_t keyring_id, const char* keyname, const char* dir) {
+void LoadKeyFromDirectory(key_serial_t keyring_id, const char* keyname_prefix, const char* dir) {
if (!std::filesystem::exists(dir)) {
return;
}
+ int counter = 0;
for (const auto& entry : std::filesystem::directory_iterator(dir)) {
if (!android::base::EndsWithIgnoreCase(entry.path().c_str(), ".der")) continue;
-
- LoadKeyFromFile(keyring_id, keyname, entry.path());
+ std::string keyname = keyname_prefix + std::to_string(counter);
+ counter++;
+ LoadKeyFromFile(keyring_id, keyname.c_str(), entry.path());
}
}
void LoadKeyFromVerifiedPartitions(key_serial_t keyring_id) {
// NB: Directories need to be synced with FileIntegrityService.java in
// frameworks/base.
- LoadKeyFromDirectory(keyring_id, "fsv_system", "/system/etc/security/fsverity");
- LoadKeyFromDirectory(keyring_id, "fsv_product", "/product/etc/security/fsverity");
+ LoadKeyFromDirectory(keyring_id, "fsv_system_", "/system/etc/security/fsverity");
+ LoadKeyFromDirectory(keyring_id, "fsv_product_", "/product/etc/security/fsverity");
}
int main(int argc, const char** argv) {
@@ -98,7 +103,9 @@
LOG(ERROR) << "--load-extra-key requires <key_name> argument.";
return -1;
}
- LoadKeyFromStdin(keyring_id, argv[2]);
+ if (!LoadKeyFromStdin(keyring_id, argv[2])) {
+ return -1;
+ }
} else if (command == "--lock") {
// Requires files backed by fs-verity to be verified with a key in .fs-verity
// keyring.
diff --git a/identity/Android.bp b/identity/Android.bp
index ed8ff2f..ecdf9a4 100644
--- a/identity/Android.bp
+++ b/identity/Android.bp
@@ -39,18 +39,22 @@
shared_libs: [
"libbase",
"libbinder",
- "libkeystore_aidl",
+ "libbinder_ndk",
+ "android.hardware.keymaster@4.0",
"libcredstore_aidl",
+ "libcrypto",
"libutils",
"libhidlbase",
"android.hardware.identity-support-lib",
"libkeymaster4support",
"libkeystore-attestation-application-id",
+ "android.hardware.security.keymint-V1-ndk",
+ "android.security.authorization-ndk",
],
static_libs: [
"android.hardware.identity-V3-cpp",
"android.hardware.keymaster-V3-cpp",
- "libcppbor",
+ "libcppbor_external",
]
}
diff --git a/identity/Credential.cpp b/identity/Credential.cpp
index 4a2bae1..7c75d8a 100644
--- a/identity/Credential.cpp
+++ b/identity/Credential.cpp
@@ -14,16 +14,14 @@
* limitations under the License.
*/
-#define LOG_TAG "Credential"
+#define LOG_TAG "credstore"
#include <android-base/logging.h>
-
+#include <android/binder_manager.h>
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
#include <android/security/identity/ICredentialStore.h>
-#include <android/security/keystore/BnCredstoreTokenCallback.h>
-#include <android/security/keystore/IKeystoreService.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <keymasterV4_0/keymaster_utils.h>
@@ -33,6 +31,11 @@
#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 "Credential.h"
#include "CredentialData.h"
#include "Util.h"
@@ -46,8 +49,6 @@
using std::promise;
using std::tuple;
-using android::security::keystore::IKeystoreService;
-
using ::android::hardware::identity::IWritableIdentityCredential;
using ::android::hardware::identity::support::ecKeyPairGetPkcs12;
@@ -55,11 +56,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,
@@ -117,73 +124,94 @@
"Error loading data for credential");
}
- selectedAuthKey_ = data->selectAuthKey(allowUsingExhaustedKeys, allowUsingExpiredKeys);
- if (selectedAuthKey_ == nullptr) {
+ // We just check if a key is available, we actually don't store it since we
+ // don't keep CredentialData around between binder calls.
+ const AuthKeyData* authKey =
+ data->selectAuthKey(allowUsingExhaustedKeys, allowUsingExpiredKeys);
+ if (authKey == nullptr) {
return Status::fromServiceSpecificError(
ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
"No suitable authentication key available");
}
- int64_t challenge;
- Status status = halBinder_->createAuthChallenge(&challenge);
- if (!status.isOk()) {
- return halStatusToGenericError(status);
- }
- if (challenge == 0) {
+ if (!ensureChallenge()) {
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
- "Returned challenge is 0 (bug in HAL or TA)");
+ "Error getting challenge (bug in HAL or TA)");
}
-
- selectedChallenge_ = challenge;
- *_aidl_return = challenge;
+ *_aidl_return = selectedChallenge_;
return Status::ok();
}
-class CredstoreTokenCallback : public android::security::keystore::BnCredstoreTokenCallback,
- public promise<tuple<bool, vector<uint8_t>, vector<uint8_t>>> {
- public:
- CredstoreTokenCallback() {}
- virtual Status onFinished(bool success, const vector<uint8_t>& authToken,
- const vector<uint8_t>& verificationToken) override {
- this->set_value({success, authToken, verificationToken});
- return Status::ok();
+bool Credential::ensureChallenge() {
+ if (selectedChallenge_ != 0) {
+ return true;
}
-};
+
+ int64_t challenge;
+ Status status = halBinder_->createAuthChallenge(&challenge);
+ if (!status.isOk()) {
+ LOG(ERROR) << "Error getting challenge: " << status.exceptionMessage();
+ return false;
+ }
+ if (challenge == 0) {
+ LOG(ERROR) << "Returned challenge is 0 (bug in HAL or TA)";
+ return false;
+ }
+
+ selectedChallenge_ = challenge;
+ return true;
+}
// 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) {
- sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
- sp<IKeystoreService> keystore = interface_cast<IKeystoreService>(binder);
- if (keystore == nullptr) {
- return false;
- }
+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;
- sp<CredstoreTokenCallback> callback = new CredstoreTokenCallback();
- auto future = callback->get_future();
-
- Status status =
- keystore->getTokensForCredstore(challenge, secureUserId, authTokenMaxAgeMillis, callback);
- if (!status.isOk()) {
+ // 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 receiving auth tokens from keystore2.
+ LOG(INFO) << "Auth tokens were not received due to: " << result.getDescription();
+ }
+ }
+ return true;
+ } else {
+ LOG(ERROR) << "Error connecting to IKeystoreAuthorization service";
return false;
}
-
- 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";
- return false;
- }
- auto [success, returnedAuthToken, returnedVerificationToken] = future.get();
- if (!success) {
- LOG(ERROR) << "Error getting tokens from credstore";
- return false;
- }
- authToken = returnedAuthToken;
- verificationToken = returnedVerificationToken;
- return true;
}
Status Credential::getEntries(const vector<uint8_t>& requestMessage,
@@ -279,13 +307,6 @@
}
}
- // If requesting a challenge-based authToken the idea is that authentication
- // happens as part of the transaction. As such, authTokenMaxAgeMillis should
- // be nearly zero. We'll use 10 seconds for this.
- if (userAuthNeeded && selectedChallenge_ != 0) {
- authTokenMaxAgeMillis = 10 * 1000;
- }
-
// Reset tokens and only get them if they're actually needed, e.g. if user authentication
// is needed in any of the access control profiles for data items being requested.
//
@@ -303,63 +324,58 @@
aidlVerificationToken.securityLevel = ::android::hardware::keymaster::SecurityLevel::SOFTWARE;
aidlVerificationToken.mac.clear();
if (userAuthNeeded) {
- vector<uint8_t> authTokenBytes;
- vector<uint8_t> verificationTokenBytes;
- if (!getTokensFromKeystore(selectedChallenge_, data->getSecureUserId(),
- authTokenMaxAgeMillis, authTokenBytes, verificationTokenBytes)) {
- LOG(ERROR) << "Error getting tokens from keystore";
+ // If user authentication is needed, always get a challenge from the
+ // HAL/TA since it'll need it to check the returned VerificationToken
+ // for freshness.
+ if (!ensureChallenge()) {
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
- "Error getting tokens from keystore");
+ "Error getting challenge (bug in HAL or TA)");
}
- // 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|.
+ // Note: if all selected profiles require auth-on-every-presentation
+ // then authTokenMaxAgeMillis will be 0 (because timeoutMillis for each
+ // profile is 0). Which means that keystore will only return an
+ // AuthToken if its challenge matches what we pass, regardless of its
+ // age. This is intended b/c the HAL/TA will check not care about
+ // the age in this case, it only cares that the challenge matches.
//
- 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;
- }
+ // Otherwise, if one or more of the profiles is auth-with-a-timeout then
+ // authTokenMaxAgeMillis will be set to the largest of those
+ // timeouts. We'll get an AuthToken which satisfies this deadline if it
+ // exists. This authToken _may_ have the requested challenge but it's
+ // not a guarantee and it's also not required.
+ //
- if (verificationTokenBytes.size() > 0) {
- optional<VerificationToken> token =
- android::hardware::keymaster::V4_0::support::deserializeVerificationToken(
- verificationTokenBytes);
- if (!token) {
- LOG(ERROR) << "Error deserializing verification token";
- return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
- "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;
+ if (!getTokensFromKeystore2(selectedChallenge_, data->getSecureUserId(),
+ authTokenMaxAgeMillis, aidlAuthToken, aidlVerificationToken)) {
+ LOG(ERROR) << "Error getting tokens from keystore2";
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Error getting tokens from keystore2");
}
}
// Note that the selectAuthKey() method is only called if a CryptoObject is involved at
// the Java layer. So we could end up with no previously selected auth key and we may
// need one.
- const AuthKeyData* authKey = selectedAuthKey_;
- if (sessionTranscript.size() > 0) {
- if (authKey == nullptr) {
- authKey = data->selectAuthKey(allowUsingExhaustedKeys, allowUsingExpiredKeys);
- if (authKey == nullptr) {
- return Status::fromServiceSpecificError(
- ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
- "No suitable authentication key available");
- }
+ //
+ const AuthKeyData* authKey =
+ data->selectAuthKey(allowUsingExhaustedKeys, allowUsingExpiredKeys);
+ if (authKey == nullptr) {
+ // If no authKey is available, consider it an error only when a
+ // SessionTranscript was provided.
+ //
+ // We allow no SessionTranscript to be provided because it makes
+ // the API simpler to deal with insofar it can be used without having
+ // to generate any authentication keys.
+ //
+ // In this "no SessionTranscript is provided" mode we don't return
+ // DeviceNameSpaces nor a MAC over DeviceAuthentication so we don't
+ // need a device key.
+ //
+ if (sessionTranscript.size() > 0) {
+ return Status::fromServiceSpecificError(
+ ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
+ "No suitable authentication key available and one is needed");
}
}
vector<uint8_t> signingKeyBlob;
@@ -750,31 +766,36 @@
//
// It is because of this we need to set the CredentialKey certificate chain,
// keyCount, and maxUsesPerKey below.
- sp<WritableCredential> writableCredential =
- new WritableCredential(dataPath_, credentialName_, docType.value(), true, hwInfo_,
- halWritableCredential, halApiVersion_);
+ sp<WritableCredential> writableCredential = new WritableCredential(
+ dataPath_, credentialName_, docType.value(), true, hwInfo_, halWritableCredential);
writableCredential->setAttestationCertificate(data->getAttestationCertificate());
auto [keyCount, maxUsesPerKey] = data->getAvailableAuthenticationKeys();
writableCredential->setAvailableAuthenticationKeys(keyCount, maxUsesPerKey);
- // Because its data has changed, we need to reconnect to the HAL when the
- // credential has been updated... otherwise the remote object will have
- // stale data for future calls (e.g. getAuthKeysNeedingCertification().
+ // Because its data has changed, we need to replace the binder for the
+ // IIdentityCredential when the credential has been updated... otherwise the
+ // remote object will have stale data for future calls, for example
+ // getAuthKeysNeedingCertification().
//
- // The joys and pitfalls of mutable objects...
+ // The way this is implemented is that setCredentialToReloadWhenUpdated()
+ // instructs the WritableCredential to call writableCredentialPersonalized()
+ // on |this|.
//
- writableCredential->setCredentialUpdatedCallback([this] {
- Status status = this->ensureOrReplaceHalBinder();
- if (!status.isOk()) {
- LOG(ERROR) << "Error loading credential";
- }
- });
+ //
+ writableCredential->setCredentialToReloadWhenUpdated(this);
*_aidl_return = writableCredential;
return Status::ok();
}
+void Credential::writableCredentialPersonalized() {
+ Status status = ensureOrReplaceHalBinder();
+ if (!status.isOk()) {
+ LOG(ERROR) << "Error reloading credential";
+ }
+}
+
} // namespace identity
} // namespace security
} // namespace android
diff --git a/identity/Credential.h b/identity/Credential.h
index 7f08515..a76f3cc 100644
--- a/identity/Credential.h
+++ b/identity/Credential.h
@@ -50,6 +50,7 @@
~Credential();
Status ensureOrReplaceHalBinder();
+ void writableCredentialPersonalized();
// ICredential overrides
Status createEphemeralKeyPair(vector<uint8_t>* _aidl_return) override;
@@ -94,12 +95,13 @@
HardwareInformation hwInfo_;
sp<IIdentityCredentialStore> halStoreBinder_;
- const AuthKeyData* selectedAuthKey_ = nullptr;
uint64_t selectedChallenge_ = 0;
sp<IIdentityCredential> halBinder_;
int halApiVersion_;
+ bool ensureChallenge();
+
ssize_t
calcExpectedDeviceNameSpacesSize(const vector<uint8_t>& requestMessage,
const vector<RequestNamespaceParcel>& requestNamespaces,
diff --git a/identity/CredentialData.cpp b/identity/CredentialData.cpp
index 96c436a..74b995d 100644
--- a/identity/CredentialData.cpp
+++ b/identity/CredentialData.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "CredentialData"
+#define LOG_TAG "credstore"
#include <chrono>
@@ -273,7 +273,7 @@
}
for (size_t n = 0; n < map->size(); n++) {
- auto [keyItem, valueItem] = (*map)[n];
+ auto& [keyItem, valueItem] = (*map)[n];
const cppbor::Tstr* tstr = keyItem->asTstr();
if (tstr == nullptr) {
LOG(ERROR) << "Key item in top-level map is not a tstr";
@@ -325,7 +325,7 @@
return false;
}
for (size_t m = 0; m < map->size(); m++) {
- auto [ecKeyItem, ecValueItem] = (*map)[m];
+ auto& [ecKeyItem, ecValueItem] = (*map)[m];
const cppbor::Tstr* ecTstr = ecKeyItem->asTstr();
if (ecTstr == nullptr) {
LOG(ERROR) << "Key item in encryptedChunks map is not a tstr";
diff --git a/identity/CredentialData.h b/identity/CredentialData.h
index b037997..24b55d3 100644
--- a/identity/CredentialData.h
+++ b/identity/CredentialData.h
@@ -55,7 +55,7 @@
vector<uint8_t> certificate;
vector<uint8_t> keyBlob;
- int64_t expirationDateMillisSinceEpoch;
+ int64_t expirationDateMillisSinceEpoch = 0;
vector<uint8_t> staticAuthenticationData;
vector<uint8_t> pendingCertificate;
vector<uint8_t> pendingKeyBlob;
diff --git a/identity/CredentialStore.cpp b/identity/CredentialStore.cpp
index f77294e..071cf24 100644
--- a/identity/CredentialStore.cpp
+++ b/identity/CredentialStore.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "CredentialStore"
+#define LOG_TAG "credstore"
#include <algorithm>
@@ -90,7 +90,7 @@
}
sp<IWritableCredential> writableCredential = new WritableCredential(
- dataPath_, credentialName, docType, false, hwInfo_, halWritableCredential, halApiVersion_);
+ dataPath_, credentialName, docType, false, hwInfo_, halWritableCredential);
*_aidl_return = writableCredential;
return Status::ok();
}
diff --git a/identity/CredentialStoreFactory.cpp b/identity/CredentialStoreFactory.cpp
index 5c3bf36..0e901ba 100644
--- a/identity/CredentialStoreFactory.cpp
+++ b/identity/CredentialStoreFactory.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "CredentialStoreFactory"
+#define LOG_TAG "credstore"
#include <android-base/logging.h>
diff --git a/identity/TEST_MAPPING b/identity/TEST_MAPPING
new file mode 100644
index 0000000..87707a8
--- /dev/null
+++ b/identity/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsIdentityTestCases"
+ }
+ ]
+}
diff --git a/identity/Util.cpp b/identity/Util.cpp
index cd29017..3a46bca 100644
--- a/identity/Util.cpp
+++ b/identity/Util.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "Util"
+#define LOG_TAG "credstore"
#include <fcntl.h>
#include <stdlib.h>
diff --git a/identity/WritableCredential.cpp b/identity/WritableCredential.cpp
index d0688b8..9827d75 100644
--- a/identity/WritableCredential.cpp
+++ b/identity/WritableCredential.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "WritableCredential"
+#define LOG_TAG "credstore"
#include <android-base/logging.h>
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
@@ -41,15 +41,14 @@
WritableCredential::WritableCredential(const string& dataPath, const string& credentialName,
const string& docType, bool isUpdate,
HardwareInformation hwInfo,
- sp<IWritableIdentityCredential> halBinder, int halApiVersion)
+ sp<IWritableIdentityCredential> halBinder)
: dataPath_(dataPath), credentialName_(credentialName), docType_(docType), isUpdate_(isUpdate),
- hwInfo_(std::move(hwInfo)), halBinder_(halBinder), halApiVersion_(halApiVersion) {}
+ hwInfo_(std::move(hwInfo)), halBinder_(halBinder) {}
WritableCredential::~WritableCredential() {}
-void WritableCredential::setCredentialUpdatedCallback(
- std::function<void()>&& onCredentialUpdatedCallback) {
- onCredentialUpdatedCallback_ = onCredentialUpdatedCallback;
+void WritableCredential::setCredentialToReloadWhenUpdated(sp<Credential> credential) {
+ credentialToReloadWhenUpdated_ = credential;
}
Status WritableCredential::ensureAttestationCertificateExists(const vector<uint8_t>& challenge) {
@@ -268,7 +267,10 @@
"Error saving credential data to disk");
}
- onCredentialUpdatedCallback_();
+ if (credentialToReloadWhenUpdated_) {
+ credentialToReloadWhenUpdated_->writableCredentialPersonalized();
+ credentialToReloadWhenUpdated_.clear();
+ }
*_aidl_return = proofOfProvisioningSignature;
return Status::ok();
diff --git a/identity/WritableCredential.h b/identity/WritableCredential.h
index 6ff31ae..838b956 100644
--- a/identity/WritableCredential.h
+++ b/identity/WritableCredential.h
@@ -24,6 +24,8 @@
#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include "Credential.h"
+
namespace android {
namespace security {
namespace identity {
@@ -38,13 +40,15 @@
public:
WritableCredential(const string& dataPath, const string& credentialName, const string& docType,
bool isUpdate, HardwareInformation hwInfo,
- sp<IWritableIdentityCredential> halBinder, int halApiVersion);
+ sp<IWritableIdentityCredential> halBinder);
~WritableCredential();
// Used when updating a credential
void setAttestationCertificate(const vector<uint8_t>& attestationCertificate);
void setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey);
- void setCredentialUpdatedCallback(std::function<void()>&& onCredentialUpdatedCallback);
+
+ // Used by Credential::update()
+ void setCredentialToReloadWhenUpdated(sp<Credential> credential);
// IWritableCredential overrides
Status getCredentialKeyCertificateChain(const vector<uint8_t>& challenge,
@@ -61,13 +65,12 @@
bool isUpdate_;
HardwareInformation hwInfo_;
sp<IWritableIdentityCredential> halBinder_;
- int halApiVersion_;
vector<uint8_t> attestationCertificate_;
int keyCount_ = 0;
int maxUsesPerKey_ = 1;
- std::function<void()> onCredentialUpdatedCallback_ = []() {};
+ sp<Credential> credentialToReloadWhenUpdated_;
ssize_t calcExpectedProofOfProvisioningSize(
const vector<AccessControlProfileParcel>& accessControlProfiles,
diff --git a/identity/main.cpp b/identity/main.cpp
index 8f4968d..2559789 100644
--- a/identity/main.cpp
+++ b/identity/main.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "android.security.identity"
+#define LOG_TAG "credstore"
#include <filesystem>
@@ -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]);
@@ -51,11 +51,10 @@
auto ret = sm->addService(String16("android.security.identity"), factory);
CHECK(ret == ::android::OK) << "Couldn't register binder service";
- LOG(ERROR) << "Registered binder service";
+ LOG(INFO) << "Registered binder service";
- // This is needed for binder callbacks from keystore on a ICredstoreTokenCallback binder.
- android::ProcessState::self()->startThreadPool();
-
+ // Credstore is a single-threaded process. So devote the main thread
+ // to handling binder messages.
IPCThreadState::self()->joinThreadPool();
return 0;
diff --git a/keystore-engine/Android.bp b/keystore-engine/Android.bp
index 9980765..cb75cde 100644
--- a/keystore-engine/Android.bp
+++ b/keystore-engine/Android.bp
@@ -26,7 +26,6 @@
srcs: [
"android_engine.cpp",
- "keystore_backend_binder.cpp",
"keystore2_engine.cpp",
],
@@ -37,15 +36,10 @@
],
shared_libs: [
- "android.system.keystore2-V1-ndk_platform",
- "libbinder",
+ "android.system.keystore2-V1-ndk",
"libbinder_ndk",
"libcrypto",
"libcutils",
- "libhidlbase",
- "libkeystore_aidl",
- "libkeystore_binder",
- "libkeystore_parcelables",
"liblog",
"libbase",
"libutils",
@@ -53,14 +47,15 @@
}
-// This builds a variant of libkeystore-engine that uses a HIDL HAL
-// owned by the WiFi user to perform signing operations.
+// This builds a variant of libkeystore-engine that is available vendor.
+// It used to use a HIDL interface to connect to keystore through wificond.
+// Now That Keystore 2.0 has a vintf stable interface this library is
+// actually identical to libkeystore-engine.
cc_library_shared {
name: "libkeystore-engine-wifi-hidl",
srcs: [
"android_engine.cpp",
- "keystore_backend_hidl.cpp",
"keystore2_engine.cpp",
],
@@ -68,17 +63,14 @@
"-fvisibility=hidden",
"-Wall",
"-Werror",
- "-DBACKEND_WIFI_HIDL",
],
shared_libs: [
- "android.system.keystore2-V1-ndk_platform",
- "android.system.wifi.keystore@1.0",
+ "android.system.keystore2-V1-ndk",
"libbase",
"libbinder_ndk",
"libcrypto",
"liblog",
- "libhidlbase",
"libcutils",
"libutils",
],
diff --git a/keystore-engine/android_engine.cpp b/keystore-engine/android_engine.cpp
index 5881523..e46204e 100644
--- a/keystore-engine/android_engine.cpp
+++ b/keystore-engine/android_engine.cpp
@@ -22,307 +22,10 @@
#define LOG_TAG "keystore-engine"
-#include <pthread.h>
-#include <string.h>
-
#include <log/log.h>
-#include <openssl/bn.h>
-#include <openssl/ec.h>
-#include <openssl/ec_key.h>
-#include <openssl/ecdsa.h>
-#include <openssl/engine.h>
-#include <openssl/evp.h>
-#include <openssl/rsa.h>
-#include <openssl/x509.h>
-
-#include <memory>
-
#include "keystore2_engine.h"
-#ifndef BACKEND_WIFI_HIDL
-#include "keystore_backend_binder.h"
-#else
-#include "keystore_backend_hidl.h"
-#endif
-
-namespace {
-KeystoreBackend *g_keystore_backend;
-void ensure_keystore_engine();
-
-/* key_id_dup is called when one of the RSA or EC_KEY objects is duplicated. */
-int key_id_dup(CRYPTO_EX_DATA* /* to */,
- const CRYPTO_EX_DATA* /* from */,
- void** from_d,
- int /* index */,
- long /* argl */,
- void* /* argp */) {
- char *key_id = reinterpret_cast<char *>(*from_d);
- if (key_id != nullptr) {
- *from_d = strdup(key_id);
- }
- return 1;
-}
-
-/* key_id_free is called when one of the RSA, DSA or EC_KEY object is freed. */
-void key_id_free(void* /* parent */,
- void* ptr,
- CRYPTO_EX_DATA* /* ad */,
- int /* index */,
- long /* argl */,
- void* /* argp */) {
- char *key_id = reinterpret_cast<char *>(ptr);
- free(key_id);
-}
-
-/* Many OpenSSL APIs take ownership of an argument on success but don't free
- * the argument on failure. This means we need to tell our scoped pointers when
- * we've transferred ownership, without triggering a warning by not using the
- * result of release(). */
-#define OWNERSHIP_TRANSFERRED(obj) auto _dummy __attribute__((unused)) = (obj).release()
-
-const char* rsa_get_key_id(const RSA* rsa);
-
-/* rsa_private_transform takes a big-endian integer from |in|, calculates the
- * d'th power of it, modulo the RSA modulus, and writes the result as a
- * big-endian integer to |out|. Both |in| and |out| are |len| bytes long. It
- * returns one on success and zero otherwise. */
-int rsa_private_transform(RSA *rsa, uint8_t *out, const uint8_t *in, size_t len) {
- ALOGV("rsa_private_transform(%p, %p, %p, %u)", rsa, out, in, (unsigned) len);
-
- ensure_keystore_engine();
-
- const char *key_id = rsa_get_key_id(rsa);
- if (key_id == nullptr) {
- ALOGE("key had no key_id!");
- return 0;
- }
-
- uint8_t* reply = nullptr;
- size_t reply_len;
- int32_t ret = g_keystore_backend->sign(key_id, in, len, &reply, &reply_len);
- if (ret < 0) {
- ALOGW("There was an error during rsa_decrypt: could not connect");
- return 0;
- } else if (ret != 0) {
- ALOGW("Error during sign from keystore: %d", ret);
- return 0;
- } else if (reply_len == 0 || reply == nullptr) {
- ALOGW("No valid signature returned");
- return 0;
- }
-
- if (reply_len > len) {
- /* The result of the RSA operation can never be larger than the size of
- * the modulus so we assume that the result has extra zeros on the
- * left. This provides attackers with an oracle, but there's nothing
- * that we can do about it here. */
- ALOGW("Reply len %zu greater than expected %zu", reply_len, len);
- memcpy(out, &reply[reply_len - len], len);
- } else if (reply_len < len) {
- /* If the Keystore implementation returns a short value we assume that
- * it's because it removed leading zeros from the left side. This is
- * bad because it provides attackers with an oracle but we cannot do
- * anything about a broken Keystore implementation here. */
- ALOGW("Reply len %zu lesser than expected %zu", reply_len, len);
- memset(out, 0, len);
- memcpy(out + len - reply_len, &reply[0], reply_len);
- } else {
- memcpy(out, &reply[0], len);
- }
-
- ALOGV("rsa=%p keystore_rsa_priv_dec successful", rsa);
- return 1;
-}
-
-const char* ecdsa_get_key_id(const EC_KEY* ec_key);
-
-/* ecdsa_sign signs |digest_len| bytes from |digest| with |ec_key| and writes
- * the resulting signature (an ASN.1 encoded blob) to |sig|. It returns one on
- * success and zero otherwise. */
-static int ecdsa_sign(const uint8_t* digest, size_t digest_len, uint8_t* sig,
- unsigned int* sig_len, EC_KEY* ec_key) {
- ALOGV("ecdsa_sign(%p, %u, %p)", digest, (unsigned) digest_len, ec_key);
-
- ensure_keystore_engine();
-
- const char *key_id = ecdsa_get_key_id(ec_key);
- if (key_id == nullptr) {
- ALOGE("key had no key_id!");
- return 0;
- }
-
- size_t ecdsa_size = ECDSA_size(ec_key);
-
- uint8_t* reply = nullptr;
- size_t reply_len;
- int32_t ret = g_keystore_backend->sign(
- key_id, digest, digest_len, &reply, &reply_len);
- if (ret < 0) {
- ALOGW("There was an error during ecdsa_sign: could not connect");
- return 0;
- } else if (reply_len == 0 || reply == nullptr) {
- ALOGW("No valid signature returned");
- return 0;
- } else if (reply_len > ecdsa_size) {
- ALOGW("Signature is too large");
- return 0;
- }
-
- // Reviewer: should't sig_len be checked here? Or is it just assumed that it is at least ecdsa_size?
- memcpy(sig, &reply[0], reply_len);
- *sig_len = reply_len;
-
- ALOGV("ecdsa_sign(%p, %u, %p) => success", digest, (unsigned)digest_len,
- ec_key);
- return 1;
-}
-
-/* KeystoreEngine is a BoringSSL ENGINE that implements RSA and ECDSA by
- * forwarding the requested operations to Keystore. */
-class KeystoreEngine {
- public:
- KeystoreEngine()
- : rsa_index_(RSA_get_ex_new_index(0 /* argl */,
- nullptr /* argp */,
- nullptr /* new_func */,
- key_id_dup,
- key_id_free)),
- ec_key_index_(EC_KEY_get_ex_new_index(0 /* argl */,
- nullptr /* argp */,
- nullptr /* new_func */,
- key_id_dup,
- key_id_free)),
- engine_(ENGINE_new()) {
- memset(&rsa_method_, 0, sizeof(rsa_method_));
- rsa_method_.common.is_static = 1;
- rsa_method_.private_transform = rsa_private_transform;
- rsa_method_.flags = RSA_FLAG_OPAQUE;
- ENGINE_set_RSA_method(engine_, &rsa_method_, sizeof(rsa_method_));
-
- memset(&ecdsa_method_, 0, sizeof(ecdsa_method_));
- ecdsa_method_.common.is_static = 1;
- ecdsa_method_.sign = ecdsa_sign;
- ecdsa_method_.flags = ECDSA_FLAG_OPAQUE;
- ENGINE_set_ECDSA_method(engine_, &ecdsa_method_, sizeof(ecdsa_method_));
- }
-
- int rsa_ex_index() const { return rsa_index_; }
- int ec_key_ex_index() const { return ec_key_index_; }
-
- const ENGINE* engine() const { return engine_; }
-
- private:
- const int rsa_index_;
- const int ec_key_index_;
- RSA_METHOD rsa_method_;
- ECDSA_METHOD ecdsa_method_;
- ENGINE* const engine_;
-};
-
-pthread_once_t g_keystore_engine_once = PTHREAD_ONCE_INIT;
-KeystoreEngine *g_keystore_engine;
-
-/* init_keystore_engine is called to initialize |g_keystore_engine|. This
- * should only be called by |pthread_once|. */
-void init_keystore_engine() {
- g_keystore_engine = new KeystoreEngine;
-#ifndef BACKEND_WIFI_HIDL
- g_keystore_backend = new KeystoreBackendBinder;
-#else
- g_keystore_backend = new KeystoreBackendHidl;
-#endif
-}
-
-/* ensure_keystore_engine ensures that |g_keystore_engine| is pointing to a
- * valid |KeystoreEngine| object and creates one if not. */
-void ensure_keystore_engine() {
- pthread_once(&g_keystore_engine_once, init_keystore_engine);
-}
-
-const char* rsa_get_key_id(const RSA* rsa) {
- return reinterpret_cast<char*>(
- RSA_get_ex_data(rsa, g_keystore_engine->rsa_ex_index()));
-}
-
-const char* ecdsa_get_key_id(const EC_KEY* ec_key) {
- return reinterpret_cast<char*>(
- EC_KEY_get_ex_data(ec_key, g_keystore_engine->ec_key_ex_index()));
-}
-
-/* wrap_rsa returns an |EVP_PKEY| that contains an RSA key where the public
- * part is taken from |public_rsa| and the private operations are forwarded to
- * KeyStore and operate on the key named |key_id|. */
-static EVP_PKEY *wrap_rsa(const char *key_id, const RSA *public_rsa) {
- bssl::UniquePtr<RSA> rsa(RSA_new_method(g_keystore_engine->engine()));
- if (rsa.get() == nullptr) {
- return nullptr;
- }
-
- char *key_id_copy = strdup(key_id);
- if (key_id_copy == nullptr) {
- return nullptr;
- }
-
- if (!RSA_set_ex_data(rsa.get(), g_keystore_engine->rsa_ex_index(),
- key_id_copy)) {
- free(key_id_copy);
- return nullptr;
- }
-
- rsa->n = BN_dup(public_rsa->n);
- rsa->e = BN_dup(public_rsa->e);
- if (rsa->n == nullptr || rsa->e == nullptr) {
- return nullptr;
- }
-
- bssl::UniquePtr<EVP_PKEY> result(EVP_PKEY_new());
- if (result.get() == nullptr ||
- !EVP_PKEY_assign_RSA(result.get(), rsa.get())) {
- return nullptr;
- }
- OWNERSHIP_TRANSFERRED(rsa);
-
- return result.release();
-}
-
-/* wrap_ecdsa returns an |EVP_PKEY| that contains an ECDSA key where the public
- * part is taken from |public_rsa| and the private operations are forwarded to
- * KeyStore and operate on the key named |key_id|. */
-static EVP_PKEY *wrap_ecdsa(const char *key_id, const EC_KEY *public_ecdsa) {
- bssl::UniquePtr<EC_KEY> ec(EC_KEY_new_method(g_keystore_engine->engine()));
- if (ec.get() == nullptr) {
- return nullptr;
- }
-
- if (!EC_KEY_set_group(ec.get(), EC_KEY_get0_group(public_ecdsa)) ||
- !EC_KEY_set_public_key(ec.get(), EC_KEY_get0_public_key(public_ecdsa))) {
- return nullptr;
- }
-
- char *key_id_copy = strdup(key_id);
- if (key_id_copy == nullptr) {
- return nullptr;
- }
-
- if (!EC_KEY_set_ex_data(ec.get(), g_keystore_engine->ec_key_ex_index(),
- key_id_copy)) {
- free(key_id_copy);
- return nullptr;
- }
-
- bssl::UniquePtr<EVP_PKEY> result(EVP_PKEY_new());
- if (result.get() == nullptr ||
- !EVP_PKEY_assign_EC_KEY(result.get(), ec.get())) {
- return nullptr;
- }
- OWNERSHIP_TRANSFERRED(ec);
-
- return result.release();
-}
-
-} /* anonymous namespace */
-
extern "C" {
EVP_PKEY* EVP_PKEY_from_keystore(const char* key_id) __attribute__((visibility("default")));
@@ -334,48 +37,7 @@
EVP_PKEY* EVP_PKEY_from_keystore(const char* key_id) {
ALOGV("EVP_PKEY_from_keystore(\"%s\")", key_id);
- if (auto ks2_key = EVP_PKEY_from_keystore2(key_id)) {
- return ks2_key;
- }
-
- ensure_keystore_engine();
-
- uint8_t *pubkey = nullptr;
- size_t pubkey_len;
- int32_t ret = g_keystore_backend->get_pubkey(key_id, &pubkey, &pubkey_len);
- if (ret < 0) {
- ALOGW("could not contact keystore");
- return nullptr;
- } else if (ret != 0 || pubkey == nullptr) {
- ALOGW("keystore reports error: %d", ret);
- return nullptr;
- }
-
- const uint8_t *inp = pubkey;
- bssl::UniquePtr<EVP_PKEY> pkey(d2i_PUBKEY(nullptr, &inp, pubkey_len));
- if (pkey.get() == nullptr) {
- ALOGW("Cannot convert pubkey");
- return nullptr;
- }
-
- EVP_PKEY *result;
- switch (EVP_PKEY_type(pkey->type)) {
- case EVP_PKEY_RSA: {
- bssl::UniquePtr<RSA> public_rsa(EVP_PKEY_get1_RSA(pkey.get()));
- result = wrap_rsa(key_id, public_rsa.get());
- break;
- }
- case EVP_PKEY_EC: {
- bssl::UniquePtr<EC_KEY> public_ecdsa(EVP_PKEY_get1_EC_KEY(pkey.get()));
- result = wrap_ecdsa(key_id, public_ecdsa.get());
- break;
- }
- default:
- ALOGE("Unsupported key type %d", EVP_PKEY_type(pkey->type));
- result = nullptr;
- }
-
- return result;
+ return EVP_PKEY_from_keystore2(key_id);
}
} // extern "C"
diff --git a/keystore-engine/keystore2_engine.cpp b/keystore-engine/keystore2_engine.cpp
index 18534a1..ee550ca 100644
--- a/keystore-engine/keystore2_engine.cpp
+++ b/keystore-engine/keystore2_engine.cpp
@@ -33,7 +33,7 @@
#define AT __func__ << ":" << __LINE__ << " "
-constexpr const char keystore2_service_name[] = "android.system.keystore2";
+constexpr const char keystore2_service_name[] = "android.system.keystore2.IKeystoreService/default";
const std::string keystore2_grant_id_prefix("ks2_keystore-engine_grant_id:");
/**
diff --git a/keystore-engine/keystore_backend.h b/keystore-engine/keystore_backend.h
deleted file mode 100644
index 88c94b3..0000000
--- a/keystore-engine/keystore_backend.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Copyright 2017 The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
-
-#ifndef ANDROID_KEYSTORE_BACKEND_H
-#define ANDROID_KEYSTORE_BACKEND_H
-
-#include <stdint.h>
-
-class KeystoreBackend {
- public:
- virtual ~KeystoreBackend() {}
- virtual int32_t sign(const char *key_id, const uint8_t* in, size_t len,
- uint8_t** reply, size_t* reply_len) = 0;
- virtual int32_t get_pubkey(const char *key_id, uint8_t** pubkey,
- size_t* reply_len) = 0;
-};
-
-#endif // ANDROID_KEYSTORE_BACKEND_H
diff --git a/keystore-engine/keystore_backend_binder.cpp b/keystore-engine/keystore_backend_binder.cpp
deleted file mode 100644
index 8b5a584..0000000
--- a/keystore-engine/keystore_backend_binder.cpp
+++ /dev/null
@@ -1,286 +0,0 @@
-/* Copyright 2017 The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
-
-#include "keystore_backend_binder.h"
-
-#include <android-base/logging.h>
-#include <android/security/keystore/IKeystoreService.h>
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
-#include <keystore/KeyCharacteristics.h>
-#include <keystore/KeymasterArguments.h>
-#include <keystore/KeymasterBlob.h>
-#include <keystore/KeystoreResponse.h>
-#include <keystore/OperationResult.h>
-#include <keystore/keymaster_types.h>
-#include <keystore/keystore.h>
-#include <keystore/keystore_hidl_support.h>
-#include <keystore/keystore_promises.h>
-#include <keystore/keystore_return_types.h>
-
-#include <future>
-#include <thread>
-
-using android::security::keystore::IKeystoreService;
-using namespace android;
-using keystore::hidl_vec;
-
-using android::hardware::keymaster::V4_0::Algorithm;
-using android::hardware::keymaster::V4_0::authorizationValue;
-using android::hardware::keymaster::V4_0::Digest;
-using android::hardware::keymaster::V4_0::KeyFormat;
-using android::hardware::keymaster::V4_0::KeyParameter;
-using android::hardware::keymaster::V4_0::KeyPurpose;
-using android::hardware::keymaster::V4_0::NullOr;
-using android::hardware::keymaster::V4_0::PaddingMode;
-using android::hardware::keymaster::V4_0::TAG_ALGORITHM;
-using android::hardware::keymaster::V4_0::TAG_DIGEST;
-using android::hardware::keymaster::V4_0::TAG_PADDING;
-using android::security::keymaster::ExportResult;
-using android::security::keymaster::KeyCharacteristics;
-using android::security::keymaster::KeymasterArguments;
-using android::security::keymaster::KeymasterBlob;
-using android::security::keymaster::OperationResult;
-
-using KSReturn = keystore::KeyStoreNativeReturnCode;
-
-namespace {
-const char keystore_service_name[] = "android.security.keystore";
-constexpr int32_t UID_SELF = -1;
-
-using keystore::KeyCharacteristicsPromise;
-using keystore::KeystoreExportPromise;
-using keystore::KeystoreResponsePromise;
-using keystore::OperationResultPromise;
-
-} // namespace
-
-#define AT __func__ << ":" << __LINE__ << " "
-
-static NullOr<const Algorithm&> getKeyAlgoritmFromKeyCharacteristics(
- const ::android::security::keymaster::KeyCharacteristics& characteristics) {
- for (const auto& param : characteristics.hardwareEnforced.getParameters()) {
- auto algo = authorizationValue(TAG_ALGORITHM, param);
- if (algo.isOk()) return algo;
- }
- for (const auto& param : characteristics.softwareEnforced.getParameters()) {
- auto algo = authorizationValue(TAG_ALGORITHM, param);
- if (algo.isOk()) return algo;
- }
- return {};
-}
-
-KeystoreBackendBinder::KeystoreBackendBinder() {
- android::ProcessState::self()->startThreadPool();
-}
-
-int32_t KeystoreBackendBinder::sign(const char* key_id, const uint8_t* in, size_t len,
- uint8_t** reply, size_t* reply_len) {
- sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder = sm->getService(String16(keystore_service_name));
- sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
-
- if (service == nullptr) {
- LOG(ERROR) << AT << "could not contact keystore";
- return -1;
- }
-
- String16 key_name16(key_id);
- int32_t error_code;
- android::sp<KeyCharacteristicsPromise> kc_promise(new KeyCharacteristicsPromise);
- auto kc_future = kc_promise->get_future();
- auto binder_result = service->getKeyCharacteristics(kc_promise, key_name16, KeymasterBlob(),
- KeymasterBlob(), UID_SELF, &error_code);
- if (!binder_result.isOk()) {
- LOG(ERROR) << AT << "communication error while calling keystore";
- return -1;
- }
- if (!KSReturn(error_code).isOk()) {
- LOG(ERROR) << AT << "getKeyCharacteristics failed: " << error_code;
- return -1;
- }
-
- auto [km_response, characteristics] = kc_future.get();
-
- if (!KSReturn(km_response.response_code()).isOk()) {
- LOG(ERROR) << AT << "getKeyCharacteristics failed: " << km_response.response_code();
- return -1;
- }
-
- auto algorithm = getKeyAlgoritmFromKeyCharacteristics(characteristics);
- if (!algorithm.isOk()) {
- LOG(ERROR) << AT << "could not get algorithm from key characteristics";
- return -1;
- }
-
- hidl_vec<KeyParameter> params(3);
- params[0] = Authorization(TAG_DIGEST, Digest::NONE);
- params[1] = Authorization(TAG_PADDING, PaddingMode::NONE);
- params[2] = Authorization(TAG_ALGORITHM, algorithm.value());
-
- android::sp<android::IBinder> token(new android::BBinder);
- sp<OperationResultPromise> promise(new OperationResultPromise());
- auto future = promise->get_future();
- binder_result = service->begin(promise, token, key_name16, (int)KeyPurpose::SIGN,
- true /*pruneable*/, KeymasterArguments(params),
- std::vector<uint8_t>() /* entropy */, UID_SELF, &error_code);
- if (!binder_result.isOk()) {
- LOG(ERROR) << AT << "communication error while calling keystore";
- return -1;
- }
-
- keystore::KeyStoreNativeReturnCode rc(error_code);
- if (!rc.isOk()) {
- LOG(ERROR) << AT << "Keystore begin returned: " << error_code;
- return -1;
- }
- OperationResult result = future.get();
-
- if (!result.resultCode.isOk()) {
- LOG(ERROR) << AT << "begin failed: " << result.resultCode;
- return -1;
- }
- auto handle = std::move(result.token);
-
- do {
- future = {};
- promise = new OperationResultPromise();
- future = promise->get_future();
- binder_result = service->update(promise, handle, KeymasterArguments(params),
- std::vector<uint8_t>(in, in + len), &error_code);
- if (!binder_result.isOk()) {
- LOG(ERROR) << AT << "communication error while calling keystore";
- return -1;
- }
-
- rc = keystore::KeyStoreNativeReturnCode(error_code);
- if (!rc.isOk()) {
- LOG(ERROR) << AT << "Keystore update returned: " << error_code;
- return -1;
- }
- result = future.get();
-
- if (!result.resultCode.isOk()) {
- LOG(ERROR) << AT << "update failed: " << result.resultCode;
- return -1;
- }
-
- if (result.inputConsumed > len) {
- LOG(ERROR) << AT << "update consumed more data than provided";
- sp<KeystoreResponsePromise> abortPromise(new KeystoreResponsePromise);
- auto abortFuture = abortPromise->get_future();
- binder_result = service->abort(abortPromise, handle, &error_code);
- if (!binder_result.isOk()) {
- LOG(ERROR) << AT << "communication error while calling keystore";
- return -1;
- }
- // This is mainly for logging since we already failed.
- // But if abort returned OK we have to wait untill abort calls the callback
- // hence the call to abortFuture.get().
- if (!KSReturn(error_code).isOk()) {
- LOG(ERROR) << AT << "abort failed: " << error_code;
- } else if (!(rc = KSReturn(abortFuture.get().response_code())).isOk()) {
- LOG(ERROR) << AT << "abort failed: " << rc;
- }
- return -1;
- }
- len -= result.inputConsumed;
- in += result.inputConsumed;
- } while (len > 0);
-
- future = {};
- promise = new OperationResultPromise();
- future = promise->get_future();
-
- binder_result = service->finish(
- promise, handle, KeymasterArguments(params), std::vector<uint8_t>() /* input */,
- std::vector<uint8_t>() /* signature */, std::vector<uint8_t>() /* entropy */, &error_code);
-
- if (!binder_result.isOk()) {
- LOG(ERROR) << AT << "communication error while calling keystore";
- return -1;
- }
-
- rc = keystore::KeyStoreNativeReturnCode(error_code);
- if (!rc.isOk()) {
- LOG(ERROR) << AT << "Keystore finish returned: " << error_code;
- return -1;
- }
- result = future.get();
-
- if (!result.resultCode.isOk()) {
- LOG(ERROR) << AT << "finish failed: " << result.resultCode;
- return -1;
- }
-
- hidl_vec<uint8_t> reply_hidl(result.data);
- if (reply_len) {
- *reply_len = reply_hidl.size();
- }
- if (reply) {
- *reply = reply_hidl.releaseData();
- }
- return 0;
-}
-
-int32_t KeystoreBackendBinder::get_pubkey(const char* key_id, uint8_t** pubkey,
- size_t* pubkey_len) {
- sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder = sm->getService(String16(keystore_service_name));
- sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
-
- if (service == nullptr) {
- LOG(ERROR) << AT << "could not contact keystore";
- return -1;
- }
-
- int32_t error_code;
- android::sp<KeystoreExportPromise> promise(new KeystoreExportPromise);
- auto future = promise->get_future();
- auto binder_result = service->exportKey(
- promise, String16(key_id), static_cast<int32_t>(KeyFormat::X509),
- KeymasterBlob() /* clientId */, KeymasterBlob() /* appData */, UID_SELF, &error_code);
- if (!binder_result.isOk()) {
- LOG(ERROR) << AT << "communication error while calling keystore";
- return -1;
- }
-
- KSReturn rc(error_code);
- if (!rc.isOk()) {
- LOG(ERROR) << AT << "exportKey failed: " << error_code;
- return -1;
- }
-
- auto export_result = future.get();
- if (!export_result.resultCode.isOk()) {
- LOG(ERROR) << AT << "exportKey failed: " << export_result.resultCode;
- return -1;
- }
-
- if (pubkey_len) {
- *pubkey_len = export_result.exportData.size();
- }
- if (pubkey) {
- *pubkey = export_result.exportData.releaseData();
- }
- return 0;
-}
diff --git a/keystore-engine/keystore_backend_binder.h b/keystore-engine/keystore_backend_binder.h
deleted file mode 100644
index 4c828c5..0000000
--- a/keystore-engine/keystore_backend_binder.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Copyright 2017 The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
-
-#ifndef ANDROID_KEYSTORE_BACKEND_BINDER_H
-#define ANDROID_KEYSTORE_BACKEND_BINDER_H
-
-#include "keystore_backend.h"
-
-class KeystoreBackendBinder : public KeystoreBackend {
- public:
- KeystoreBackendBinder();
- virtual ~KeystoreBackendBinder() {}
- int32_t sign(const char *key_id, const uint8_t* in, size_t len,
- uint8_t** reply, size_t* reply_len) override;
- int32_t get_pubkey(const char *key_id, uint8_t** pubkey,
- size_t* reply_len) override;
-};
-
-#endif // ANDROID_KEYSTORE_BACKEND_BINDER_H
diff --git a/keystore-engine/keystore_backend_hidl.cpp b/keystore-engine/keystore_backend_hidl.cpp
deleted file mode 100644
index 30cf890..0000000
--- a/keystore-engine/keystore_backend_hidl.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/* Copyright 2017 The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
-
-#include "keystore_backend_hidl.h"
-
-#include <android/system/wifi/keystore/1.0/IKeystore.h>
-#include <log/log.h>
-
-using android::hardware::hidl_vec;
-using android::hardware::Return;
-using android::sp;
-using android::system::wifi::keystore::V1_0::IKeystore;
-
-int32_t KeystoreBackendHidl::sign(
- const char *key_id, const uint8_t* in, size_t len, uint8_t** reply,
- size_t* reply_len) {
- if (key_id == nullptr || in == nullptr || reply == nullptr || reply_len == nullptr) {
- ALOGE("Null pointer argument passed");
- return -1;
- }
-
- sp<IKeystore> service = IKeystore::tryGetService();
- if (service == nullptr) {
- ALOGE("could not contact keystore HAL");
- return -1;
- }
-
- bool success = false;
- auto cb = [&](IKeystore::KeystoreStatusCode status,
- hidl_vec<uint8_t> signedData) {
- if (status == IKeystore::KeystoreStatusCode::SUCCESS) {
- *reply_len = signedData.size();
- *reply = signedData.releaseData();
- success = true;
- }
- };
- Return<void> ret = service->sign(
- key_id, std::vector<uint8_t>(in, in + len), cb);
- if (!ret.isOk() || !success) {
- return 1;
- }
- return 0;
-}
-
-int32_t KeystoreBackendHidl::get_pubkey(
- const char *key_id, uint8_t** pubkey, size_t* pubkey_len) {
- if (key_id == nullptr || pubkey == nullptr || pubkey_len == nullptr) {
- ALOGE("Null pointer argument passed");
- return -1;
- }
-
- sp<IKeystore> service = IKeystore::tryGetService();
- if (service == nullptr) {
- ALOGE("could not contact keystore HAL");
- return -1;
- }
-
- bool success = false;
- auto cb = [&](IKeystore::KeystoreStatusCode status,
- hidl_vec<uint8_t> publicKey) {
- if (status == IKeystore::KeystoreStatusCode::SUCCESS) {
- *pubkey_len = publicKey.size();
- *pubkey = publicKey.releaseData();
- success = true;
- }
- };
- Return<void> ret = service->getPublicKey(key_id, cb);
- if (!ret.isOk() || !success) {
- return 1;
- }
- return 0;
-}
diff --git a/keystore-engine/keystore_backend_hidl.h b/keystore-engine/keystore_backend_hidl.h
deleted file mode 100644
index fd38f69..0000000
--- a/keystore-engine/keystore_backend_hidl.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Copyright 2017 The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
-
-#ifndef ANDROID_KEYSTORE_BACKEND_HIDL_H
-#define ANDROID_KEYSTORE_BACKEND_HIDL_H
-
-#include "keystore_backend.h"
-
-class KeystoreBackendHidl : public KeystoreBackend {
- public:
- KeystoreBackendHidl() {}
- virtual ~KeystoreBackendHidl() {}
- int32_t sign(const char *key_id, const uint8_t* in, size_t len,
- uint8_t** reply, size_t* reply_len) override;
- int32_t get_pubkey(const char *key_id, uint8_t** pubkey,
- size_t* reply_len) override;
-};
-
-#endif // ANDROID_KEYSTORE_BACKEND_HIDL_H
diff --git a/keystore/Android.bp b/keystore/Android.bp
index 7278cee..ad4b4b1 100644
--- a/keystore/Android.bp
+++ b/keystore/Android.bp
@@ -35,97 +35,6 @@
}
cc_binary {
- name: "keystore",
- defaults: ["keystore_defaults"],
-
- srcs: [
- "KeyStore.cpp",
- "auth_token_table.cpp",
- "blob.cpp",
- "confirmation_manager.cpp",
- "grant_store.cpp",
- "key_creation_log_handler.cpp",
- "key_operation_log_handler.cpp",
- "key_attestation_log_handler.cpp",
- "key_store_service.cpp",
- "keyblob_utils.cpp",
- "keymaster_enforcement.cpp",
- "keymaster_worker.cpp",
- "keystore_main.cpp",
- "keystore_utils.cpp",
- "legacy_keymaster_device_wrapper.cpp",
- "operation.cpp",
- "permissions.cpp",
- "user_state.cpp",
- ],
- shared_libs: [
- "android.hardware.confirmationui@1.0",
- "android.hardware.keymaster@3.0",
- "android.hardware.keymaster@4.0",
- "android.hardware.keymaster@4.1",
- "libbase",
- "libbinder",
- "libcrypto",
- "libcutils",
- "libhardware",
- "libhidlbase",
- "libkeymaster4support",
- "libkeymaster4_1support",
- "libkeymaster_messages",
- "libkeymaster_portable",
- "libkeystore-attestation-application-id",
- "libkeystore_aidl",
- "libkeystore_binder",
- "libkeystore_parcelables",
- "liblog",
- "libprotobuf-cpp-lite",
- "libselinux",
- "libservices",
- "libsoftkeymasterdevice",
- "libutils",
- "libstatslog",
- ],
- init_rc: ["keystore.rc"],
- aidl: {
- include_dirs: ["frameworks/base/core/java/"],
- },
-
- product_variables: {
- pdk: {
- enabled: false,
- },
- debuggable: {
- cflags: [
- // Allow VTS tests running as root to have
- // additional permissions.
- "-DGRANT_ROOT_ALL_PERMISSIONS",
- ],
- },
- },
-
- required: ["keystore_cli_v2"],
-}
-
-cc_binary {
- name: "keystore_cli",
- defaults: ["keystore_defaults"],
-
- srcs: ["keystore_cli.cpp"],
- shared_libs: [
- "android.hardware.keymaster@4.0",
- "libbinder",
- "libcrypto",
- "libcutils",
- "libhidlbase",
- "libkeystore_aidl", // for IKeyStoreService.asInterface()
- "libkeystore_binder",
- "libkeystore_parcelables",
- "liblog",
- "libutils",
- ],
-}
-
-cc_binary {
name: "keystore_cli_v2",
defaults: ["keystore_defaults"],
@@ -133,95 +42,25 @@
"-DKEYMASTER_NAME_TAGS",
"-Wno-unused-parameter",
],
- srcs: ["keystore_cli_v2.cpp"],
+ srcs: [
+ "keystore_cli_v2.cpp",
+ "keystore_client.proto",
+ ],
shared_libs: [
- "android.hardware.confirmationui@1.0",
+ "android.security.apc-ndk",
+ "android.system.keystore2-V1-ndk",
"libbinder",
- "android.hardware.keymaster@4.0",
+ "libbinder_ndk",
"libchrome",
+ "libcrypto",
+ "libkeymint_support",
+ "libprotobuf-cpp-lite",
"libutils",
- "libhidlbase",
- "libkeymaster4support",
- "libkeystore_aidl",
- "libkeystore_binder",
- "libkeystore_parcelables",
],
local_include_dirs: ["include"],
}
-cc_library_shared {
- name: "libkeystore_parcelables",
- defaults: ["keystore_defaults"],
- export_include_dirs: ["include"],
- srcs: [
- "KeymasterArguments.cpp",
- "keystore_aidl_hidl_marshalling_utils.cpp",
- "KeystoreResponse.cpp",
- "OperationResult.cpp",
- ],
- shared_libs: [
- "android.hardware.keymaster@4.0",
- "android.hardware.keymaster@4.1",
- "libbinder",
- "libhardware",
- "libhidlbase",
- "libkeymaster4support",
- "libkeymaster4_1support",
- "liblog",
- "libprotobuf-cpp-lite",
- "libutils",
- "libkeystore-attestation-application-id",
- ],
- export_shared_lib_headers: [
- "android.hardware.keymaster@4.0",
- "android.hardware.keymaster@4.1",
- "libbinder",
- "libhidlbase",
- "libkeymaster4_1support",
- ],
-}
-// Library for keystore clients
-cc_library_shared {
- name: "libkeystore_binder",
- defaults: ["keystore_defaults"],
-
- srcs: [
- "keyblob_utils.cpp",
- "keystore_client.proto",
- "keystore_client_impl.cpp",
- "keystore_get.cpp",
- ],
- shared_libs: [
- "android.hardware.keymaster@4.0",
- "libbinder",
- "libhidlbase",
- "libkeymaster4support",
- "libkeystore_aidl",
- "libkeystore_parcelables",
- "liblog",
- "libprotobuf-cpp-lite",
- "libutils",
- ],
-
- proto: {
- type: "lite",
- export_proto_headers: true,
- },
- aidl: {
- export_aidl_headers: true,
- include_dirs: ["frameworks/base/core/java/"],
- },
- export_include_dirs: ["include"],
- export_shared_lib_headers: [
- "android.hardware.keymaster@4.0",
- "libbinder",
- "libhidlbase",
- "libkeystore_aidl",
- "libkeystore_parcelables",
- ],
-}
-
// Library used by both keystore and credstore for generating the ASN.1 stored
// in Tag::ATTESTATION_APPLICATION_ID
cc_library_shared {
@@ -248,7 +87,7 @@
}
// Library for keystore clients using the WiFi HIDL interface
-cc_library_shared {
+cc_library {
name: "libkeystore-wifi-hidl",
defaults: ["keystore_defaults"],
@@ -265,77 +104,3 @@
vendor: true,
}
-
-// Library for unit tests
-cc_library_static {
- name: "libkeystore_test",
- defaults: ["keystore_defaults"],
-
- srcs: [
- "auth_token_table.cpp",
- "blob.cpp",
- ],
- cflags: [ "-O0", ],
- static_libs: ["libgtest_main"],
- shared_libs: [
- "android.hardware.keymaster@4.0",
- "libbinder",
- "libcrypto",
- "libhidlbase",
- "libkeymaster4support",
- "libkeystore-attestation-application-id",
- "libutils",
- "libkeystore_aidl",
- "libkeystore_parcelables",
- ],
- export_shared_lib_headers: [
- "android.hardware.keymaster@4.0",
- "libhidlbase",
- "libkeymaster4support",
- ],
-
- aidl: {
- include_dirs: ["frameworks/base/core/java/"],
- },
- export_include_dirs: ["include"],
-}
-
-filegroup {
- name: "keystore_aidl",
- srcs: [
- "binder/android/security/IConfirmationPromptCallback.aidl",
- "binder/android/security/keystore/ICredstoreTokenCallback.aidl",
- "binder/android/security/keystore/IKeystoreCertificateChainCallback.aidl",
- "binder/android/security/keystore/IKeystoreExportKeyCallback.aidl",
- "binder/android/security/keystore/IKeystoreKeyCharacteristicsCallback.aidl",
- "binder/android/security/keystore/IKeystoreOperationResultCallback.aidl",
- "binder/android/security/keystore/IKeystoreResponseCallback.aidl",
- "binder/android/security/keystore/IKeystoreService.aidl",
- ],
- path: "binder",
-}
-
-cc_library_shared {
- name: "libkeystore_aidl",
- srcs: [":keystore_aidl"],
- aidl: {
- export_aidl_headers: true,
- include_dirs: [
- "system/security/keystore/binder",
- ],
- },
- shared_libs: [
- "libbinder",
- "libcutils",
- "libhardware",
- "libhidlbase",
- "libkeystore_parcelables",
- "liblog",
- "libselinux",
- "libutils",
- ],
- export_shared_lib_headers: [
- "libbinder",
- "libkeystore_parcelables",
- ],
-}
diff --git a/keystore/KeyStore.cpp b/keystore/KeyStore.cpp
deleted file mode 100644
index 1f80899..0000000
--- a/keystore/KeyStore.cpp
+++ /dev/null
@@ -1,512 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#define LOG_TAG "keystore"
-
-#include "KeyStore.h"
-
-#include <dirent.h>
-#include <fcntl.h>
-
-#include <openssl/bio.h>
-
-#include <utils/String16.h>
-#include <utils/String8.h>
-
-#include <android-base/scopeguard.h>
-#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
-#include <android/security/keystore/IKeystoreService.h>
-#include <log/log_event_list.h>
-
-#include <private/android_logger.h>
-
-#include "keystore_utils.h"
-#include "permissions.h"
-#include <keystore/keystore_hidl_support.h>
-
-#include "keymaster_worker.h"
-
-namespace keystore {
-
-const char* KeyStore::kOldMasterKey = ".masterkey";
-const char* KeyStore::kMetaDataFile = ".metadata";
-
-const android::String16 KeyStore::kRsaKeyType("RSA");
-const android::String16 KeyStore::kEcKeyType("EC");
-
-using android::String8;
-
-KeyStore::KeyStore(const KeymasterDevices& kmDevices,
- SecurityLevel minimalAllowedSecurityLevelForNewKeys)
- : mAllowNewFallback(minimalAllowedSecurityLevelForNewKeys == SecurityLevel::SOFTWARE),
- mConfirmationManager(new ConfirmationManager(this)) {
- memset(&mMetaData, '\0', sizeof(mMetaData));
-
- static_assert(std::tuple_size<std::decay_t<decltype(kmDevices)>>::value ==
- std::tuple_size<decltype(mKmDevices)>::value,
- "KmasterDevices and KeymasterWorkers must have the same size");
- for (size_t i = 0; i < kmDevices.size(); ++i) {
- if (kmDevices[SecurityLevel(i)]) {
- mKmDevices[SecurityLevel(i)] = std::make_shared<KeymasterWorker>(
- kmDevices[SecurityLevel(i)], this, SecurityLevel(i));
- }
- }
-}
-
-KeyStore::~KeyStore() {
-}
-
-ResponseCode KeyStore::initialize() {
- readMetaData();
- if (upgradeKeystore()) {
- writeMetaData();
- }
-
- return ResponseCode::NO_ERROR;
-}
-
-ResponseCode KeyStore::initializeUser(const android::String8& pw, uid_t userId) {
- auto userState = mUserStateDB.getUserState(userId);
- return userState->initialize(pw);
-}
-
-ResponseCode KeyStore::copyMasterKey(uid_t srcUser, uid_t dstUser) {
- auto userState = mUserStateDB.getUserState(dstUser);
- auto initState = mUserStateDB.getUserState(srcUser);
- return userState->copyMasterKey(&initState);
-}
-
-ResponseCode KeyStore::writeMasterKey(const android::String8& pw, uid_t userId) {
- auto userState = mUserStateDB.getUserState(userId);
- return userState->writeMasterKey(pw);
-}
-
-ResponseCode KeyStore::readMasterKey(const android::String8& pw, uid_t userId) {
- auto userState = mUserStateDB.getUserState(userId);
- return userState->readMasterKey(pw);
-}
-
-LockedKeyBlobEntry KeyStore::getLockedBlobEntryIfNotExists(const std::string& alias, uid_t uid) {
- KeyBlobEntry kbe(alias, mUserStateDB.getUserStateByUid(uid)->getUserDirName(), uid);
- auto result = LockedKeyBlobEntry::get(std::move(kbe));
- if (result->hasKeyBlob()) return {};
- return result;
-}
-
-std::optional<KeyBlobEntry> KeyStore::getBlobEntryIfExists(const std::string& alias, uid_t uid) {
- KeyBlobEntry kbe(alias, mUserStateDB.getUserStateByUid(uid)->getUserDirName(), uid);
- if (kbe.hasKeyBlob()) return kbe;
-
- // If this is one of the legacy UID->UID mappings, use it.
- uid_t euid = get_keystore_euid(uid);
- if (euid != uid) {
- kbe = KeyBlobEntry(alias, mUserStateDB.getUserStateByUid(euid)->getUserDirName(), euid);
- if (kbe.hasKeyBlob()) return kbe;
- }
-
- // They might be using a granted key.
- auto grant = mGrants.get(uid, alias);
- if (grant) {
- kbe = grant->entry_;
- if (kbe.hasKeyBlob()) return kbe;
- }
- return {};
-}
-LockedKeyBlobEntry KeyStore::getLockedBlobEntryIfExists(const std::string& alias, uid_t uid) {
- auto blobentry = getBlobEntryIfExists(alias, uid);
- if (!blobentry) return {};
- LockedKeyBlobEntry lockedentry = LockedKeyBlobEntry::get(std::move(*blobentry));
- if (!lockedentry || !lockedentry->hasKeyBlob()) return {};
- return lockedentry;
-}
-
-void KeyStore::resetUser(uid_t userId, bool keepUnenryptedEntries) {
- android::String8 prefix("");
- android::Vector<android::String16> aliases;
-
- auto userState = mUserStateDB.getUserState(userId);
- std::string userDirName = userState->getUserDirName();
- auto encryptionKey = userState->getEncryptionKey();
- auto state = userState->getState();
- // userState is a proxy that holds a lock which may be required by a worker.
- // LockedKeyBlobEntry::list has a fence that waits until all workers have finished which may
- // not happen if a user state lock is held. The following line relinquishes the lock.
- userState = {};
-
- ResponseCode rc;
- std::list<LockedKeyBlobEntry> matches;
-
- // must not be called by a keymaster worker. List waits for workers to relinquish all access
- // to blob entries
- std::tie(rc, matches) = LockedKeyBlobEntry::list(userDirName);
- if (rc != ResponseCode::NO_ERROR) {
- return;
- }
-
- for (LockedKeyBlobEntry& lockedEntry : matches) {
- bool shouldDelete = true;
-
- if (keepUnenryptedEntries) {
- Blob blob;
- Blob charBlob;
- ResponseCode rc;
-
- std::tie(rc, blob, charBlob) = lockedEntry.readBlobs(encryptionKey, state);
-
- switch (rc) {
- case ResponseCode::SYSTEM_ERROR:
- case ResponseCode::VALUE_CORRUPTED:
- // If we can't read blobs, delete them.
- shouldDelete = true;
- break;
-
- case ResponseCode::NO_ERROR:
- case ResponseCode::LOCKED:
- // Delete encrypted blobs but keep unencrypted blobs and super-encrypted blobs. We
- // need to keep super-encrypted blobs so we can report that the user is
- // unauthenticated if a caller tries to use them, rather than reporting that they
- // don't exist.
- shouldDelete = blob.isEncrypted();
- break;
-
- default:
- ALOGE("Got unexpected return code %d from readBlobs", rc);
- // This shouldn't happen. To be on the safe side, delete it.
- shouldDelete = true;
- break;
- }
- }
- if (shouldDelete) {
- del(lockedEntry);
- }
- }
-
- userState = mUserStateDB.getUserState(userId);
- if (!userState->deleteMasterKey()) {
- ALOGE("Failed to delete user %d's master key", userId);
- }
- if (!keepUnenryptedEntries) {
- if (!userState->reset()) {
- ALOGE("Failed to remove user %d's directory", userId);
- }
- }
-}
-
-bool KeyStore::isEmpty(uid_t userId) const {
- std::string userDirName;
- {
- // userState holds a lock which must be relinquished before list is called. This scope
- // prevents deadlocks.
- auto userState = mUserStateDB.getUserState(userId);
- if (!userState) {
- return true;
- }
- userDirName = userState->getUserDirName();
- }
-
- ResponseCode rc;
- std::list<LockedKeyBlobEntry> matches;
-
- // must not be called by a keymaster worker. List waits for workers to relinquish all access
- // to blob entries
- std::tie(rc, matches) = LockedKeyBlobEntry::list(userDirName);
-
- return rc == ResponseCode::SYSTEM_ERROR || matches.size() == 0;
-}
-
-void KeyStore::lock(uid_t userId) {
- auto userState = mUserStateDB.getUserState(userId);
- userState->zeroizeMasterKeysInMemory();
- userState->setState(STATE_LOCKED);
-}
-
-static void maybeLogKeyIntegrityViolation(const LockedKeyBlobEntry& lockedEntry,
- const BlobType type) {
- if (!__android_log_security() || (type != TYPE_KEY_PAIR && type != TYPE_KEYMASTER_10)) return;
- log_key_integrity_violation(lockedEntry->alias().c_str(), lockedEntry->uid());
-}
-
-std::tuple<ResponseCode, Blob, Blob> KeyStore::get(const LockedKeyBlobEntry& blobfile) {
- std::tuple<ResponseCode, Blob, Blob> result;
-
- uid_t userId = get_user_id(blobfile->uid());
- Blob& keyBlob = std::get<1>(result);
- ResponseCode& rc = std::get<0>(result);
-
- auto userState = mUserStateDB.getUserState(userId);
- BlobType type = BlobType::TYPE_ANY;
- auto logOnScopeExit = android::base::make_scope_guard([&] {
- if (rc == ResponseCode::VALUE_CORRUPTED) {
- maybeLogKeyIntegrityViolation(blobfile, type);
- }
- });
-
- result = blobfile.readBlobs(userState->getEncryptionKey(), userState->getState());
- if (rc != ResponseCode::NO_ERROR) {
- return result;
- }
-
- // update the type for logging (see scope_guard above)
- type = keyBlob.getType();
-
- const uint8_t version = keyBlob.getVersion();
- if (version < CURRENT_BLOB_VERSION) {
- /* If we upgrade the key, we need to write it to disk again. Then
- * it must be read it again since the blob is encrypted each time
- * it's written.
- */
- if (upgradeBlob(&keyBlob, version)) {
- if ((rc = this->put(blobfile, keyBlob, {})) != ResponseCode::NO_ERROR ||
- (result = blobfile.readBlobs(userState->getEncryptionKey(), userState->getState()),
- rc) != ResponseCode::NO_ERROR) {
- return result;
- }
- }
- }
-
- return result;
-}
-
-ResponseCode KeyStore::put(const LockedKeyBlobEntry& blobfile, Blob keyBlob,
- Blob characteristicsBlob) {
- auto userState = mUserStateDB.getUserStateByUid(blobfile->uid());
- return blobfile.writeBlobs(std::move(keyBlob), std::move(characteristicsBlob),
- userState->getEncryptionKey(), userState->getState());
-}
-
-ResponseCode KeyStore::del(const LockedKeyBlobEntry& blobfile) {
- Blob keyBlob;
- Blob charactaristicsBlob;
- ResponseCode rc;
- uid_t uid = blobfile->uid();
- std::string alias = blobfile->alias();
-
- std::tie(rc, keyBlob, charactaristicsBlob) = get(blobfile);
-
- // after getting the blob from the file system we scrub the filesystem.
- mGrants.removeAllGrantsToKey(uid, alias);
- auto result = blobfile.deleteBlobs();
-
- if (rc != ResponseCode::NO_ERROR) {
- LOG(ERROR) << "get keyblob failed " << int(rc);
- return rc;
- }
-
- // if we got the blob successfully, we try and delete it from the keymaster device
- auto dev = getDevice(keyBlob);
-
- if (keyBlob.getType() == ::TYPE_KEYMASTER_10) {
- dev->deleteKey(blob2hidlVec(keyBlob), [dev, alias, uid](Return<ErrorCode> rc) {
- auto ret = KS_HANDLE_HIDL_ERROR(dev, rc);
- // A device doesn't have to implement delete_key.
- bool success = ret == ErrorCode::OK || ret == ErrorCode::UNIMPLEMENTED;
- if (__android_log_security()) {
- android_log_event_list(SEC_TAG_KEY_DESTROYED)
- << int32_t(success) << alias << int32_t(uid) << LOG_ID_SECURITY;
- }
- if (!success) {
- LOG(ERROR) << "Keymaster delete for key " << alias << " of uid " << uid
- << " failed";
- }
- });
- }
-
- return result;
-}
-
-std::string KeyStore::addGrant(const LockedKeyBlobEntry& blobfile, uid_t granteeUid) {
- return mGrants.put(granteeUid, blobfile);
-}
-
-bool KeyStore::removeGrant(const LockedKeyBlobEntry& blobfile, const uid_t granteeUid) {
- return mGrants.removeByFileAlias(granteeUid, blobfile);
-}
-void KeyStore::removeAllGrantsToUid(const uid_t granteeUid) {
- mGrants.removeAllGrantsToUid(granteeUid);
-}
-
-bool KeyStore::isHardwareBacked(const android::String16& keyType) const {
- // if strongbox device is present TEE must also be present and of sufficiently high version
- // to support all keys in hardware
- if (getDevice(SecurityLevel::STRONGBOX)) return true;
- if (!getDevice(SecurityLevel::TRUSTED_ENVIRONMENT)) {
- ALOGW("can't get keymaster device");
- return false;
- }
-
- auto version = getDevice(SecurityLevel::TRUSTED_ENVIRONMENT)->halVersion();
- if (keyType == kRsaKeyType) return true; // All versions support RSA
- return keyType == kEcKeyType && version.supportsEc;
-}
-
-std::tuple<ResponseCode, Blob, Blob, LockedKeyBlobEntry>
-KeyStore::getKeyForName(const android::String8& keyName, const uid_t uid, const BlobType type) {
- std::tuple<ResponseCode, Blob, Blob, LockedKeyBlobEntry> result;
- auto& [rc, keyBlob, charBlob, lockedEntry] = result;
-
- lockedEntry = getLockedBlobEntryIfExists(keyName.string(), uid);
-
- if (!lockedEntry) return rc = ResponseCode::KEY_NOT_FOUND, std::move(result);
-
- std::tie(rc, keyBlob, charBlob) = get(lockedEntry);
-
- if (rc == ResponseCode::NO_ERROR) {
- if (keyBlob.getType() != type) return rc = ResponseCode::KEY_NOT_FOUND, std::move(result);
- }
- return result;
-}
-
-bool KeyStore::upgradeBlob(Blob* blob, const uint8_t oldVersion) {
- bool updated = false;
- uint8_t version = oldVersion;
-
- if (!blob || !(*blob)) return false;
-
- /* From V0 -> V1: All old types were unknown */
- if (version == 0) {
- ALOGE("Failed to upgrade key blob. Ancient blob version 0 is no longer supported");
-
- return false;
- }
-
- /* From V1 -> V2: All old keys were encrypted */
- if (version == 1) {
- ALOGV("upgrading to version 2");
-
- blob->setEncrypted(true);
- version = 2;
- updated = true;
- }
-
- /*
- * If we've updated, set the key blob to the right version
- * and write it.
- */
- if (updated) {
- blob->setVersion(version);
- }
-
- return updated;
-}
-
-void KeyStore::readMetaData() {
- int in = TEMP_FAILURE_RETRY(open(kMetaDataFile, O_RDONLY));
- if (in < 0) {
- return;
- }
- size_t fileLength = readFully(in, (uint8_t*)&mMetaData, sizeof(mMetaData));
- if (fileLength != sizeof(mMetaData)) {
- ALOGI("Metadata file is %zd bytes (%zd experted); upgrade?", fileLength, sizeof(mMetaData));
- }
- close(in);
-}
-
-void KeyStore::writeMetaData() {
- const char* tmpFileName = ".metadata.tmp";
- int out =
- TEMP_FAILURE_RETRY(open(tmpFileName, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR));
- if (out < 0) {
- ALOGE("couldn't write metadata file: %s", strerror(errno));
- return;
- }
- size_t fileLength = writeFully(out, (uint8_t*)&mMetaData, sizeof(mMetaData));
- if (fileLength != sizeof(mMetaData)) {
- ALOGI("Could only write %zd bytes to metadata file (%zd expected)", fileLength,
- sizeof(mMetaData));
- }
- close(out);
- rename(tmpFileName, kMetaDataFile);
-}
-
-bool KeyStore::upgradeKeystore() {
- bool upgraded = false;
-
- if (mMetaData.version == 0) {
- auto userState = getUserStateDB().getUserStateByUid(0);
-
- // Initialize first so the directory is made.
- userState->initialize();
-
- // Migrate the old .masterkey file to user 0.
- if (access(kOldMasterKey, R_OK) == 0) {
- if (rename(kOldMasterKey, userState->getMasterKeyFileName().c_str()) < 0) {
- ALOGE("couldn't migrate old masterkey: %s", strerror(errno));
- return false;
- }
- }
-
- // Initialize again in case we had a key.
- userState->initialize();
-
- // Try to migrate existing keys.
- DIR* dir = opendir(".");
- if (!dir) {
- // Give up now; maybe we can upgrade later.
- ALOGE("couldn't open keystore's directory; something is wrong");
- return false;
- }
-
- struct dirent* file;
- while ((file = readdir(dir)) != nullptr) {
- // We only care about files.
- if (file->d_type != DT_REG) {
- continue;
- }
-
- // Skip anything that starts with a "."
- if (file->d_name[0] == '.') {
- continue;
- }
-
- // Find the current file's user.
- char* end;
- unsigned long thisUid = strtoul(file->d_name, &end, 10);
- if (end[0] != '_' || end[1] == 0) {
- continue;
- }
- auto otherUser = getUserStateDB().getUserStateByUid(thisUid);
- if (otherUser->getUserId() != 0) {
- unlinkat(dirfd(dir), file->d_name, 0);
- }
-
- // Rename the file into user directory.
- DIR* otherdir = opendir(otherUser->getUserDirName().c_str());
- if (otherdir == nullptr) {
- ALOGW("couldn't open user directory for rename");
- continue;
- }
- if (renameat(dirfd(dir), file->d_name, dirfd(otherdir), file->d_name) < 0) {
- ALOGW("couldn't rename blob: %s: %s", file->d_name, strerror(errno));
- }
- closedir(otherdir);
- }
- closedir(dir);
-
- mMetaData.version = 1;
- upgraded = true;
- }
-
- return upgraded;
-}
-
-void KeyStore::binderDied(const ::android::wp<IBinder>& who) {
- for (unsigned i = 0; i < mKmDevices.size(); ++i) {
- if (mKmDevices[SecurityLevel(i)]) mKmDevices[SecurityLevel(i)]->binderDied(who);
- }
- getConfirmationManager().binderDied(who);
-}
-
-} // namespace keystore
diff --git a/keystore/KeyStore.h b/keystore/KeyStore.h
deleted file mode 100644
index 7841a80..0000000
--- a/keystore/KeyStore.h
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#ifndef KEYSTORE_KEYSTORE_H_
-#define KEYSTORE_KEYSTORE_H_
-
-#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
-#include <keymasterV4_1/Keymaster.h>
-#include <utils/Vector.h>
-
-#include <keystore/keymaster_types.h>
-
-#include "auth_token_table.h"
-#include "blob.h"
-#include "confirmation_manager.h"
-#include "grant_store.h"
-#include "keymaster_worker.h"
-#include "keystore_keymaster_enforcement.h"
-#include "operation.h"
-#include "user_state.h"
-
-#include <array>
-#include <optional>
-#include <tuple>
-
-namespace keystore {
-
-using ::android::sp;
-using keymaster::support::Keymaster;
-
-template <typename T, size_t count> class Devices : public std::array<T, count> {
- public:
- T& operator[](SecurityLevel secLevel) {
- static_assert(uint32_t(SecurityLevel::SOFTWARE) == 0 &&
- uint32_t(SecurityLevel::TRUSTED_ENVIRONMENT) == 1 &&
- uint32_t(SecurityLevel::STRONGBOX) == 2,
- "Numeric values of security levels have changed");
- return std::array<T, count>::at(static_cast<uint32_t>(secLevel));
- }
- T operator[](SecurityLevel secLevel) const {
- if (static_cast<uint32_t>(secLevel) > static_cast<uint32_t>(SecurityLevel::STRONGBOX)) {
- LOG(ERROR) << "Invalid security level requested";
- return {};
- }
- return (*const_cast<Devices*>(this))[secLevel];
- }
-};
-
-} // namespace keystore
-
-namespace std {
-template <typename T, size_t N> struct tuple_size<keystore::Devices<T, N>> {
- public:
- static constexpr size_t value = std::tuple_size<std::array<T, N>>::value;
-};
-} // namespace std
-
-namespace keystore {
-
-using KeymasterWorkers = Devices<std::shared_ptr<KeymasterWorker>, 3>;
-using KeymasterDevices = Devices<sp<Keymaster>, 3>;
-
-class KeyStore : public ::android::IBinder::DeathRecipient {
- public:
- KeyStore(const KeymasterDevices& kmDevices,
- SecurityLevel minimalAllowedSecurityLevelForNewKeys);
- ~KeyStore();
-
- std::shared_ptr<KeymasterWorker> getDevice(SecurityLevel securityLevel) const {
- return mKmDevices[securityLevel];
- }
-
- std::shared_ptr<KeymasterWorker> getFallbackDevice() const {
- // we only return the fallback device if the creation of new fallback key blobs is
- // allowed. (also see getDevice below)
- if (mAllowNewFallback) {
- return mKmDevices[SecurityLevel::SOFTWARE];
- } else {
- return nullptr;
- }
- }
-
- std::shared_ptr<KeymasterWorker> getDevice(const Blob& blob) {
- return mKmDevices[blob.getSecurityLevel()];
- }
-
- ResponseCode initialize();
-
- State getState(uid_t userId) { return mUserStateDB.getUserState(userId)->getState(); }
-
- ResponseCode initializeUser(const android::String8& pw, uid_t userId);
-
- ResponseCode copyMasterKey(uid_t srcUser, uid_t dstUser);
- ResponseCode writeMasterKey(const android::String8& pw, uid_t userId);
- ResponseCode readMasterKey(const android::String8& pw, uid_t userId);
-
- LockedKeyBlobEntry getLockedBlobEntryIfNotExists(const std::string& alias, uid_t uid);
- std::optional<KeyBlobEntry> getBlobEntryIfExists(const std::string& alias, uid_t uid);
- LockedKeyBlobEntry getLockedBlobEntryIfExists(const std::string& alias, uid_t uid);
- /*
- * Delete entries owned by userId. If keepUnencryptedEntries is true
- * then only encrypted entries will be removed, otherwise all entries will
- * be removed.
- */
- void resetUser(uid_t userId, bool keepUnenryptedEntries);
- bool isEmpty(uid_t userId) const;
-
- void lock(uid_t userId);
-
- std::tuple<ResponseCode, Blob, Blob> get(const LockedKeyBlobEntry& blobfile);
- ResponseCode put(const LockedKeyBlobEntry& blobfile, Blob keyBlob, Blob characteristicsBlob);
- ResponseCode del(const LockedKeyBlobEntry& blobfile);
-
- std::string addGrant(const LockedKeyBlobEntry& blobfile, uid_t granteeUid);
- bool removeGrant(const LockedKeyBlobEntry& blobfile, const uid_t granteeUid);
- void removeAllGrantsToUid(const uid_t granteeUid);
-
- ResponseCode importKey(const uint8_t* key, size_t keyLen, const LockedKeyBlobEntry& blobfile,
- uid_t userId, int32_t flags);
-
- bool isHardwareBacked(const android::String16& keyType) const;
-
- std::tuple<ResponseCode, Blob, Blob, LockedKeyBlobEntry>
- getKeyForName(const android::String8& keyName, const uid_t uid, const BlobType type);
-
- void binderDied(const ::android::wp<IBinder>& who) override;
-
- UserStateDB& getUserStateDB() { return mUserStateDB; }
- AuthTokenTable& getAuthTokenTable() { return mAuthTokenTable; }
- KeystoreKeymasterEnforcement& getEnforcementPolicy() { return mEnforcementPolicy; }
- ConfirmationManager& getConfirmationManager() { return *mConfirmationManager; }
-
- void addOperationDevice(sp<IBinder> token, std::shared_ptr<KeymasterWorker> dev) {
- std::lock_guard<std::mutex> lock(operationDeviceMapMutex_);
- operationDeviceMap_.emplace(std::move(token), std::move(dev));
- }
- std::shared_ptr<KeymasterWorker> getOperationDevice(const sp<IBinder>& token) {
- std::lock_guard<std::mutex> lock(operationDeviceMapMutex_);
- auto it = operationDeviceMap_.find(token);
- if (it != operationDeviceMap_.end()) {
- return it->second;
- }
- return {};
- }
- void removeOperationDevice(const sp<IBinder>& token) {
- std::lock_guard<std::mutex> lock(operationDeviceMapMutex_);
- operationDeviceMap_.erase(token);
- }
-
- private:
- static const char* kOldMasterKey;
- static const char* kMetaDataFile;
- static const android::String16 kRsaKeyType;
- static const android::String16 kEcKeyType;
-
- KeymasterWorkers mKmDevices;
-
- bool mAllowNewFallback;
-
- UserStateDB mUserStateDB;
- AuthTokenTable mAuthTokenTable;
- KeystoreKeymasterEnforcement mEnforcementPolicy;
- sp<ConfirmationManager> mConfirmationManager;
-
- ::keystore::GrantStore mGrants;
-
- typedef struct { uint32_t version; } keystore_metadata_t;
-
- keystore_metadata_t mMetaData;
-
- /**
- * Upgrade the key from the current version to whatever is newest.
- */
- bool upgradeBlob(Blob* blob, const uint8_t oldVersion);
-
- void readMetaData();
- void writeMetaData();
-
- bool upgradeKeystore();
-
- std::mutex operationDeviceMapMutex_;
- std::map<sp<IBinder>, std::shared_ptr<KeymasterWorker>> operationDeviceMap_;
-};
-
-} // namespace keystore
-
-#endif // KEYSTORE_KEYSTORE_H_
diff --git a/keystore/KeymasterArguments.cpp b/keystore/KeymasterArguments.cpp
deleted file mode 100644
index 60b86cc..0000000
--- a/keystore/KeymasterArguments.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
-**
-** Copyright 2017, 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.
-*/
-
-#include "include/keystore/KeymasterArguments.h"
-#include "keystore_aidl_hidl_marshalling_utils.h"
-
-#include <binder/Parcel.h>
-
-namespace android {
-namespace security {
-namespace keymaster {
-
-using ::android::status_t;
-status_t KeymasterArguments::readFromParcel(const android::Parcel* in) {
- data_ = keystore::readParamSetFromParcel(*in);
- return OK;
-};
-
-status_t KeymasterArguments::writeToParcel(android::Parcel* out) const {
- return keystore::writeParamSetToParcel(data_, out);
-};
-
-KeymasterArguments::KeymasterArguments(hardware::hidl_vec<keystore::KeyParameter>&& other)
- : data_(std::move(other)) {}
-
-KeymasterArguments::KeymasterArguments(const hardware::hidl_vec<keystore::KeyParameter>& other)
- : data_(other) {}
-
-} // namespace keymaster
-} // namespace security
-} // namespace android
diff --git a/keystore/KeystoreResponse.cpp b/keystore/KeystoreResponse.cpp
deleted file mode 100644
index c46973a..0000000
--- a/keystore/KeystoreResponse.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-**
-** Copyright 2018, 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.
-*/
-
-#include <binder/Parcel.h>
-#include <keystore/keymaster_types.h>
-#include <utility>
-#include <utils/String16.h>
-
-#include "include/keystore/KeystoreResponse.h"
-
-namespace android {
-namespace security {
-namespace keystore {
-
-status_t KeystoreResponse::readFromParcel(const Parcel* in) {
- auto rc = in->readInt32(&response_code_);
- if (rc != NO_ERROR) return rc;
- return in->readString16(&error_msg_);
-}
-
-status_t KeystoreResponse::writeToParcel(Parcel* out) const {
- auto rc = out->writeInt32(response_code_);
- if (rc != NO_ERROR) return rc;
- return out->writeString16(error_msg_);
-}
-
-} // namespace keystore
-} // namespace security
-} // namespace android
diff --git a/keystore/OperationResult.cpp b/keystore/OperationResult.cpp
deleted file mode 100644
index dec4d40..0000000
--- a/keystore/OperationResult.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
-**
-** Copyright 2017, 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.
-*/
-
-#include "include/keystore/OperationResult.h"
-
-#include <utility>
-
-#include <binder/Parcel.h>
-
-#include <keystore/keymaster_types.h>
-
-#include "keystore_aidl_hidl_marshalling_utils.h"
-
-namespace android {
-namespace security {
-namespace keymaster {
-
-using ::android::status_t;
-using ::keystore::ErrorCode;
-
-OperationResult::OperationResult() : resultCode(), token(), handle(0), inputConsumed(0), data() {}
-
-status_t OperationResult::readFromParcel(const Parcel* inn) {
- const Parcel& in = *inn;
- resultCode = ErrorCode(in.readInt32());
- token = in.readStrongBinder();
- handle = static_cast<uint64_t>(in.readInt64());
- inputConsumed = in.readInt32();
- data = keystore::readKeymasterBlob(in);
- outParams = keystore::readParamSetFromParcel(in);
- return OK;
-}
-
-status_t OperationResult::writeToParcel(Parcel* out) const {
- out->writeInt32(resultCode.getErrorCode());
- out->writeStrongBinder(token);
- out->writeInt64(handle);
- out->writeInt32(inputConsumed);
- keystore::writeKeymasterBlob(data, out);
- keystore::writeParamSetToParcel(outParams, out);
- return OK;
-}
-
-OperationResult operationFailed(const ::keystore::KeyStoreServiceReturnCode& error) {
- OperationResult opResult = {};
- opResult.resultCode = error;
- return opResult;
-}
-
-} // namespace keymaster
-} // namespace security
-} // namespace android
diff --git a/keystore/auth_token_table.cpp b/keystore/auth_token_table.cpp
deleted file mode 100644
index 5e6d572..0000000
--- a/keystore/auth_token_table.cpp
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#define LOG_TAG "keystore"
-
-#include "auth_token_table.h"
-
-#include <assert.h>
-#include <time.h>
-
-#include <algorithm>
-
-#include <log/log.h>
-
-namespace keystore {
-
-template <typename IntType, uint32_t byteOrder> struct choose_hton;
-
-template <typename IntType> struct choose_hton<IntType, __ORDER_LITTLE_ENDIAN__> {
- inline static IntType hton(const IntType& value) {
- IntType result = 0;
- const unsigned char* inbytes = reinterpret_cast<const unsigned char*>(&value);
- unsigned char* outbytes = reinterpret_cast<unsigned char*>(&result);
- for (int i = sizeof(IntType) - 1; i >= 0; --i) {
- *(outbytes++) = inbytes[i];
- }
- return result;
- }
-};
-
-template <typename IntType> struct choose_hton<IntType, __ORDER_BIG_ENDIAN__> {
- inline static IntType hton(const IntType& value) { return value; }
-};
-
-template <typename IntType> inline IntType hton(const IntType& value) {
- return choose_hton<IntType, __BYTE_ORDER__>::hton(value);
-}
-
-template <typename IntType> inline IntType ntoh(const IntType& value) {
- // same operation and hton
- return choose_hton<IntType, __BYTE_ORDER__>::hton(value);
-}
-
-//
-// Some trivial template wrappers around std algorithms, so they take containers not ranges.
-//
-template <typename Container, typename Predicate>
-typename Container::iterator find_if(Container& container, Predicate pred) {
- return std::find_if(container.begin(), container.end(), pred);
-}
-
-template <typename Container, typename Predicate>
-typename Container::iterator remove_if(Container& container, Predicate pred) {
- return std::remove_if(container.begin(), container.end(), pred);
-}
-
-template <typename Container> typename Container::iterator min_element(Container& container) {
- return std::min_element(container.begin(), container.end());
-}
-
-time_t clock_gettime_raw() {
- struct timespec time;
- clock_gettime(CLOCK_MONOTONIC_RAW, &time);
- return time.tv_sec;
-}
-
-void AuthTokenTable::AddAuthenticationToken(HardwareAuthToken&& auth_token) {
- Entry new_entry(std::move(auth_token), clock_function_());
- // STOPSHIP: debug only, to be removed
- ALOGD("AddAuthenticationToken: timestamp = %llu, time_received = %lld",
- static_cast<unsigned long long>(new_entry.token().timestamp),
- static_cast<long long>(new_entry.time_received()));
-
- std::lock_guard<std::mutex> lock(entries_mutex_);
- RemoveEntriesSupersededBy(new_entry);
- if (entries_.size() >= max_entries_) {
- ALOGW("Auth token table filled up; replacing oldest entry");
- *min_element(entries_) = std::move(new_entry);
- } else {
- entries_.push_back(std::move(new_entry));
- }
-}
-
-inline bool is_secret_key_operation(Algorithm algorithm, KeyPurpose purpose) {
- if ((algorithm != Algorithm::RSA && algorithm != Algorithm::EC)) return true;
- if (purpose == KeyPurpose::SIGN || purpose == KeyPurpose::DECRYPT) return true;
- return false;
-}
-
-inline bool KeyRequiresAuthentication(const AuthorizationSet& key_info, KeyPurpose purpose) {
- auto algorithm = defaultOr(key_info.GetTagValue(TAG_ALGORITHM), Algorithm::AES);
- return is_secret_key_operation(algorithm, purpose) &&
- key_info.find(Tag::NO_AUTH_REQUIRED) == -1;
-}
-
-inline bool KeyRequiresAuthPerOperation(const AuthorizationSet& key_info, KeyPurpose purpose) {
- auto algorithm = defaultOr(key_info.GetTagValue(TAG_ALGORITHM), Algorithm::AES);
- return is_secret_key_operation(algorithm, purpose) && key_info.find(Tag::AUTH_TIMEOUT) == -1;
-}
-
-std::tuple<AuthTokenTable::Error, HardwareAuthToken>
-AuthTokenTable::FindAuthorization(const AuthorizationSet& key_info, KeyPurpose purpose,
- uint64_t op_handle) {
-
- std::lock_guard<std::mutex> lock(entries_mutex_);
-
- if (!KeyRequiresAuthentication(key_info, purpose)) return {AUTH_NOT_REQUIRED, {}};
-
- auto auth_type =
- defaultOr(key_info.GetTagValue(TAG_USER_AUTH_TYPE), HardwareAuthenticatorType::NONE);
-
- std::vector<uint64_t> key_sids;
- ExtractSids(key_info, &key_sids);
-
- if (KeyRequiresAuthPerOperation(key_info, purpose))
- return FindAuthPerOpAuthorization(key_sids, auth_type, op_handle);
- else
- return FindTimedAuthorization(key_sids, auth_type, key_info);
-}
-
-std::tuple<AuthTokenTable::Error, HardwareAuthToken> AuthTokenTable::FindAuthPerOpAuthorization(
- const std::vector<uint64_t>& sids, HardwareAuthenticatorType auth_type, uint64_t op_handle) {
- if (op_handle == 0) return {OP_HANDLE_REQUIRED, {}};
-
- auto matching_op = find_if(
- entries_, [&](Entry& e) { return e.token().challenge == op_handle && !e.completed(); });
-
- if (matching_op == entries_.end()) return {AUTH_TOKEN_NOT_FOUND, {}};
-
- if (!matching_op->SatisfiesAuth(sids, auth_type)) return {AUTH_TOKEN_WRONG_SID, {}};
-
- return {OK, matching_op->token()};
-}
-
-std::tuple<AuthTokenTable::Error, HardwareAuthToken>
-AuthTokenTable::FindTimedAuthorization(const std::vector<uint64_t>& sids,
- HardwareAuthenticatorType auth_type,
- const AuthorizationSet& key_info) {
- Entry* newest_match = nullptr;
- for (auto& entry : entries_)
- if (entry.SatisfiesAuth(sids, auth_type) && entry.is_newer_than(newest_match))
- newest_match = &entry;
-
- if (!newest_match) return {AUTH_TOKEN_NOT_FOUND, {}};
-
- auto timeout = defaultOr(key_info.GetTagValue(TAG_AUTH_TIMEOUT), 0);
-
- time_t now = clock_function_();
- if (static_cast<int64_t>(newest_match->time_received()) + timeout < static_cast<int64_t>(now))
- return {AUTH_TOKEN_EXPIRED, {}};
-
- if (key_info.GetTagValue(TAG_ALLOW_WHILE_ON_BODY).isOk()) {
- if (static_cast<int64_t>(newest_match->time_received()) <
- static_cast<int64_t>(last_off_body_)) {
- return {AUTH_TOKEN_EXPIRED, {}};
- }
- }
-
- newest_match->UpdateLastUse(now);
- return {OK, newest_match->token()};
-}
-
-std::tuple<AuthTokenTable::Error, HardwareAuthToken>
-AuthTokenTable::FindAuthorizationForCredstore(uint64_t challenge, uint64_t secureUserId,
- int64_t authTokenMaxAgeMillis) {
- std::vector<uint64_t> sids = {secureUserId};
- HardwareAuthenticatorType auth_type = HardwareAuthenticatorType::ANY;
-
- time_t now = clock_function_();
-
- // challenge-based - the authToken has to contain the given challenge.
- if (challenge != 0) {
- auto matching_op = find_if(
- entries_, [&](Entry& e) { return e.token().challenge == challenge && !e.completed(); });
- if (matching_op == entries_.end()) {
- return {AUTH_TOKEN_NOT_FOUND, {}};
- }
-
- if (!matching_op->SatisfiesAuth(sids, auth_type)) {
- return {AUTH_TOKEN_WRONG_SID, {}};
- }
-
- if (authTokenMaxAgeMillis > 0) {
- if (static_cast<int64_t>(matching_op->time_received()) + authTokenMaxAgeMillis <
- static_cast<int64_t>(now)) {
- return {AUTH_TOKEN_EXPIRED, {}};
- }
- }
-
- return {OK, matching_op->token()};
- }
-
- // Otherwise, no challenge - any authToken younger than the specified maximum
- // age will do.
- Entry* newest_match = nullptr;
- for (auto& entry : entries_) {
- if (entry.SatisfiesAuth(sids, auth_type) && entry.is_newer_than(newest_match)) {
- newest_match = &entry;
- }
- }
-
- if (newest_match == nullptr) {
- return {AUTH_TOKEN_NOT_FOUND, {}};
- }
-
- if (authTokenMaxAgeMillis > 0) {
- if (static_cast<int64_t>(newest_match->time_received()) + authTokenMaxAgeMillis <
- static_cast<int64_t>(now)) {
- return {AUTH_TOKEN_EXPIRED, {}};
- }
- }
-
- newest_match->UpdateLastUse(now);
- return {OK, newest_match->token()};
-}
-
-void AuthTokenTable::ExtractSids(const AuthorizationSet& key_info, std::vector<uint64_t>* sids) {
- assert(sids);
- for (auto& param : key_info)
- if (param.tag == Tag::USER_SECURE_ID)
- sids->push_back(authorizationValue(TAG_USER_SECURE_ID, param).value());
-}
-
-void AuthTokenTable::RemoveEntriesSupersededBy(const Entry& entry) {
- entries_.erase(remove_if(entries_, [&](Entry& e) { return entry.Supersedes(e); }),
- entries_.end());
-}
-
-void AuthTokenTable::onDeviceOffBody() {
- last_off_body_ = clock_function_();
-}
-
-void AuthTokenTable::Clear() {
- std::lock_guard<std::mutex> lock(entries_mutex_);
-
- entries_.clear();
-}
-
-size_t AuthTokenTable::size() const {
- std::lock_guard<std::mutex> lock(entries_mutex_);
- return entries_.size();
-}
-
-bool AuthTokenTable::IsSupersededBySomeEntry(const Entry& entry) {
- return std::any_of(entries_.begin(), entries_.end(),
- [&](Entry& e) { return e.Supersedes(entry); });
-}
-
-void AuthTokenTable::MarkCompleted(const uint64_t op_handle) {
- std::lock_guard<std::mutex> lock(entries_mutex_);
-
- auto found = find_if(entries_, [&](Entry& e) { return e.token().challenge == op_handle; });
- if (found == entries_.end()) return;
-
- assert(!IsSupersededBySomeEntry(*found));
- found->mark_completed();
-
- if (IsSupersededBySomeEntry(*found)) entries_.erase(found);
-}
-
-AuthTokenTable::Entry::Entry(HardwareAuthToken&& token, time_t current_time)
- : token_(std::move(token)), time_received_(current_time), last_use_(current_time),
- operation_completed_(token_.challenge == 0) {}
-
-bool AuthTokenTable::Entry::SatisfiesAuth(const std::vector<uint64_t>& sids,
- HardwareAuthenticatorType auth_type) {
- for (auto sid : sids) {
- if (SatisfiesAuth(sid, auth_type)) return true;
- }
- return false;
-}
-
-void AuthTokenTable::Entry::UpdateLastUse(time_t time) {
- this->last_use_ = time;
-}
-
-bool AuthTokenTable::Entry::Supersedes(const Entry& entry) const {
- if (!entry.completed()) return false;
-
- return (token_.userId == entry.token_.userId &&
- token_.authenticatorType == entry.token_.authenticatorType &&
- token_.authenticatorId == entry.token_.authenticatorId && is_newer_than(&entry));
-}
-
-} // namespace keystore
diff --git a/keystore/auth_token_table.h b/keystore/auth_token_table.h
deleted file mode 100644
index 787b9b1..0000000
--- a/keystore/auth_token_table.h
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#include <memory>
-#include <mutex>
-#include <vector>
-
-#include <keystore/keymaster_types.h>
-
-#ifndef KEYSTORE_AUTH_TOKEN_TABLE_H_
-#define KEYSTORE_AUTH_TOKEN_TABLE_H_
-
-namespace keystore {
-
-namespace test {
-class AuthTokenTableTest;
-} // namespace test
-
-time_t clock_gettime_raw();
-
-/**
- * AuthTokenTable manages a set of received authorization tokens and can provide the appropriate
- * token for authorizing a key operation.
- *
- * To keep the table from growing without bound, superseded entries are removed when possible, and
- * least recently used entries are automatically pruned when when the table exceeds a size limit,
- * which is expected to be relatively small, since the implementation uses a linear search.
- */
-class AuthTokenTable {
- public:
- explicit AuthTokenTable(size_t max_entries = 32, time_t (*clock_function)() = clock_gettime_raw)
- : max_entries_(max_entries), last_off_body_(clock_function()),
- clock_function_(clock_function) {}
-
- enum Error {
- OK,
- AUTH_NOT_REQUIRED = -1,
- AUTH_TOKEN_EXPIRED = -2, // Found a matching token, but it's too old.
- AUTH_TOKEN_WRONG_SID = -3, // Found a token with the right challenge, but wrong SID. This
- // most likely indicates that the authenticator was updated
- // (e.g. new fingerprint enrolled).
- OP_HANDLE_REQUIRED = -4, // The key requires auth per use but op_handle was zero.
- AUTH_TOKEN_NOT_FOUND = -5,
- };
-
- /**
- * Add an authorization token to the table.
- */
- void AddAuthenticationToken(HardwareAuthToken&& auth_token);
-
- /**
- * Find an authorization token that authorizes the operation specified by \p operation_handle on
- * a key with the characteristics specified in \p key_info.
- *
- * This method is O(n * m), where n is the number of KM_TAG_USER_SECURE_ID entries in key_info
- * and m is the number of entries in the table. It could be made better, but n and m should
- * always be small.
- *
- * The table retains ownership of the returned object.
- */
- std::tuple<Error, HardwareAuthToken> FindAuthorization(const AuthorizationSet& key_info,
- KeyPurpose purpose, uint64_t op_handle);
-
- std::tuple<Error, HardwareAuthToken>
- FindAuthorizationForCredstore(uint64_t challenge, uint64_t secureUserId,
- int64_t authTokenMaxAgeMillis);
-
- /**
- * Mark operation completed. This allows tokens associated with the specified operation to be
- * superseded by new tokens.
- */
- void MarkCompleted(const uint64_t op_handle);
-
- /**
- * Update the last_off_body_ timestamp so that tokens which remain authorized only so long as
- * the device stays on body can be revoked.
- */
- void onDeviceOffBody();
-
- void Clear();
-
- /**
- * This function shall only be used for testing.
- *
- * BEWARE: Since the auth token table can be accessed
- * concurrently, the size may be out dated as soon as it returns.
- */
- size_t size() const;
-
- private:
- friend class AuthTokenTableTest;
-
- class Entry {
- public:
- Entry(HardwareAuthToken&& token, time_t current_time);
- Entry(Entry&& entry) noexcept { *this = std::move(entry); }
-
- void operator=(Entry&& rhs) noexcept {
- token_ = std::move(rhs.token_);
- time_received_ = rhs.time_received_;
- last_use_ = rhs.last_use_;
- operation_completed_ = rhs.operation_completed_;
- }
-
- bool operator<(const Entry& rhs) const { return last_use_ < rhs.last_use_; }
-
- void UpdateLastUse(time_t time);
-
- bool Supersedes(const Entry& entry) const;
- bool SatisfiesAuth(const std::vector<uint64_t>& sids, HardwareAuthenticatorType auth_type);
-
- bool is_newer_than(const Entry* entry) const {
- if (!entry) return true;
- uint64_t ts = token_.timestamp;
- uint64_t other_ts = entry->token_.timestamp;
- // Normally comparing timestamp_host_order alone is sufficient, but here is an
- // additional hack to compare time_received value for some devices where their auth
- // tokens contain fixed timestamp (due to the a stuck secure RTC on them)
- return (ts > other_ts) ||
- ((ts == other_ts) && (time_received_ > entry->time_received_));
- }
-
- void mark_completed() { operation_completed_ = true; }
-
- const HardwareAuthToken& token() const & { return token_; }
- time_t time_received() const { return time_received_; }
- bool completed() const { return operation_completed_; }
-
- private:
- bool SatisfiesAuth(uint64_t sid, HardwareAuthenticatorType auth_type) const {
- return (sid == token_.userId || sid == token_.authenticatorId) &&
- (auth_type & token_.authenticatorType) != 0;
- }
-
- HardwareAuthToken token_;
- time_t time_received_;
- time_t last_use_;
- bool operation_completed_;
- };
-
- std::tuple<Error, HardwareAuthToken>
- FindAuthPerOpAuthorization(const std::vector<uint64_t>& sids,
- HardwareAuthenticatorType auth_type, uint64_t op_handle);
- std::tuple<Error, HardwareAuthToken> FindTimedAuthorization(const std::vector<uint64_t>& sids,
- HardwareAuthenticatorType auth_type,
- const AuthorizationSet& key_info);
- void ExtractSids(const AuthorizationSet& key_info, std::vector<uint64_t>* sids);
- void RemoveEntriesSupersededBy(const Entry& entry);
- bool IsSupersededBySomeEntry(const Entry& entry);
-
- /**
- * Guards the entries_ vector against concurrent modification. All public facing methods
- * reading of modifying the vector must grab this mutex.
- */
- mutable std::mutex entries_mutex_;
- std::vector<Entry> entries_;
- size_t max_entries_;
- time_t last_off_body_;
- time_t (*clock_function_)();
-};
-
-} // namespace keystore
-
-#endif // KEYSTORE_AUTH_TOKEN_TABLE_H_
diff --git a/keystore/binder/android/security/IConfirmationPromptCallback.aidl b/keystore/binder/android/security/IConfirmationPromptCallback.aidl
deleted file mode 100644
index 96a1a04..0000000
--- a/keystore/binder/android/security/IConfirmationPromptCallback.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2017, 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;
-
-/**
- * This must be kept manually in sync with system/security/keystore until AIDL
- * can generate both Java and C++ bindings.
- *
- * @hide
- */
-interface IConfirmationPromptCallback {
- oneway void onConfirmationPromptCompleted(in int result, in byte[] dataThatWasConfirmed);
-}
diff --git a/keystore/binder/android/security/keymaster/ExportResult.aidl b/keystore/binder/android/security/keymaster/ExportResult.aidl
deleted file mode 100644
index 1748653..0000000
--- a/keystore/binder/android/security/keymaster/ExportResult.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 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.keymaster;
-
-/* @hide */
-parcelable ExportResult cpp_header "keystore/ExportResult.h";
diff --git a/keystore/binder/android/security/keymaster/KeyCharacteristics.aidl b/keystore/binder/android/security/keymaster/KeyCharacteristics.aidl
deleted file mode 100644
index 32e75ad..0000000
--- a/keystore/binder/android/security/keymaster/KeyCharacteristics.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 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.keymaster;
-
-/* @hide */
-parcelable KeyCharacteristics cpp_header "keystore/KeyCharacteristics.h";
diff --git a/keystore/binder/android/security/keymaster/KeymasterArguments.aidl b/keystore/binder/android/security/keymaster/KeymasterArguments.aidl
deleted file mode 100644
index 44d9f09..0000000
--- a/keystore/binder/android/security/keymaster/KeymasterArguments.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 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.keymaster;
-
-/* @hide */
-parcelable KeymasterArguments cpp_header "keystore/KeymasterArguments.h";
diff --git a/keystore/binder/android/security/keymaster/KeymasterBlob.aidl b/keystore/binder/android/security/keymaster/KeymasterBlob.aidl
deleted file mode 100644
index 5c5db9e..0000000
--- a/keystore/binder/android/security/keymaster/KeymasterBlob.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 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.keymaster;
-
-/* @hide */
-parcelable KeymasterBlob cpp_header "keystore/KeymasterBlob.h";
diff --git a/keystore/binder/android/security/keymaster/KeymasterCertificateChain.aidl b/keystore/binder/android/security/keymaster/KeymasterCertificateChain.aidl
deleted file mode 100644
index ddb5cae..0000000
--- a/keystore/binder/android/security/keymaster/KeymasterCertificateChain.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2016 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.keymaster;
-
-/* @hide */
-parcelable KeymasterCertificateChain cpp_header "keystore/KeymasterCertificateChain.h";
diff --git a/keystore/binder/android/security/keystore/IKeystoreCertificateChainCallback.aidl b/keystore/binder/android/security/keystore/IKeystoreCertificateChainCallback.aidl
deleted file mode 100644
index dca928d..0000000
--- a/keystore/binder/android/security/keystore/IKeystoreCertificateChainCallback.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2018, 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.keystore;
-
-import android.security.keystore.KeystoreResponse;
-import android.security.keymaster.KeymasterCertificateChain;
-
-/**
- * @hide
- */
-oneway interface IKeystoreCertificateChainCallback {
- void onFinished(in KeystoreResponse response, in KeymasterCertificateChain chain);
-}
\ No newline at end of file
diff --git a/keystore/binder/android/security/keystore/IKeystoreOperationResultCallback.aidl b/keystore/binder/android/security/keystore/IKeystoreOperationResultCallback.aidl
deleted file mode 100644
index f37b838..0000000
--- a/keystore/binder/android/security/keystore/IKeystoreOperationResultCallback.aidl
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * Copyright (c) 2018, 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.keystore;
-
-import android.security.keystore.KeystoreResponse;
-import android.security.keymaster.OperationResult;
-
-/**
- * @hide
- */
-@SensitiveData
-oneway interface IKeystoreOperationResultCallback {
- void onFinished(in OperationResult result);
-}
diff --git a/keystore/binder/android/security/keystore/IKeystoreResponseCallback.aidl b/keystore/binder/android/security/keystore/IKeystoreResponseCallback.aidl
deleted file mode 100644
index 912e605..0000000
--- a/keystore/binder/android/security/keystore/IKeystoreResponseCallback.aidl
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Copyright (c) 2018, 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.keystore;
-
-import android.security.keystore.KeystoreResponse;
-
-/**
- * @hide
- */
-oneway interface IKeystoreResponseCallback {
- void onFinished(in KeystoreResponse response);
-}
\ No newline at end of file
diff --git a/keystore/binder/android/security/keystore/IKeystoreService.aidl b/keystore/binder/android/security/keystore/IKeystoreService.aidl
deleted file mode 100644
index e0879dd..0000000
--- a/keystore/binder/android/security/keystore/IKeystoreService.aidl
+++ /dev/null
@@ -1,93 +0,0 @@
-/**
- * Copyright (c) 2018, 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.keystore;
-
-import android.security.keymaster.KeymasterArguments;
-import android.security.keymaster.KeymasterBlob;
-import android.security.keymaster.OperationResult;
-import android.security.keystore.ICredstoreTokenCallback;
-import android.security.keystore.IKeystoreResponseCallback;
-import android.security.keystore.IKeystoreKeyCharacteristicsCallback;
-import android.security.keystore.IKeystoreExportKeyCallback;
-import android.security.keystore.IKeystoreOperationResultCallback;
-import android.security.keystore.IKeystoreCertificateChainCallback;
-
-/**
- * @hide
- */
-@SensitiveData
-interface IKeystoreService {
- @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
- int getState(int userId);
- @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
- byte[] get(String name, int uid);
- @UnsupportedAppUsage
- int insert(String name, in byte[] item, int uid, int flags);
- @UnsupportedAppUsage
- int del(String name, int uid);
- @UnsupportedAppUsage
- int exist(String name, int uid);
- @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
- String[] list(String namePrefix, int uid);
- int onUserPasswordChanged(int userId, String newPassword);
- int lock(int userId);
- int unlock(int userId, String userPassword);
- int isEmpty(int userId);
- String grant(String name, int granteeUid);
- @UnsupportedAppUsage
- int ungrant(String name, int granteeUid);
- long getmtime(String name, int uid);
- @UnsupportedAppUsage
- int is_hardware_backed(String string);
- @UnsupportedAppUsage
- int clear_uid(long uid);
-
- int addRngEntropy(IKeystoreResponseCallback cb, in byte[] data, int flags);
- int generateKey(IKeystoreKeyCharacteristicsCallback cb, String alias, in KeymasterArguments arguments, in byte[] entropy, int uid,
- int flags);
- int getKeyCharacteristics (IKeystoreKeyCharacteristicsCallback cb, String alias, in KeymasterBlob clientId, in KeymasterBlob appData,
- int uid);
- int importKey(IKeystoreKeyCharacteristicsCallback cb, String alias, in KeymasterArguments arguments, int format,
- in byte[] keyData, int uid, int flags);
- int exportKey(IKeystoreExportKeyCallback cb, String alias, int format, in KeymasterBlob clientId,
- in KeymasterBlob appData, int uid);
- int begin(in IKeystoreOperationResultCallback cb, IBinder appToken, String alias, int purpose, boolean pruneable,
- in KeymasterArguments params, in byte[] entropy, int uid);
- int update(in IKeystoreOperationResultCallback cb, IBinder token, in KeymasterArguments params, in byte[] input);
- int finish(in IKeystoreOperationResultCallback cb, IBinder token, in KeymasterArguments params, in byte[] input, in byte[] signature,
- in byte[] entropy);
- int abort(in IKeystoreResponseCallback cb, IBinder token);
- int addAuthToken(in byte[] authToken);
- int onUserAdded(int userId, int parentId);
- int onUserRemoved(int userId);
- int attestKey(in IKeystoreCertificateChainCallback cb, String alias, in KeymasterArguments params);
- int attestDeviceIds(in IKeystoreCertificateChainCallback cb, in KeymasterArguments params);
- int onDeviceOffBody();
- int importWrappedKey(in IKeystoreKeyCharacteristicsCallback cb, String wrappedKeyAlias, in byte[] wrappedKey,
- in String wrappingKeyAlias, in byte[] maskingKey, in KeymasterArguments arguments,
- in long rootSid, in long fingerprintSid);
- int presentConfirmationPrompt(IBinder listener, String promptText, in byte[] extraData,
- in String locale, in int uiOptionsAsFlags);
- int cancelConfirmationPrompt(IBinder listener);
- boolean isConfirmationPromptSupported();
- int onKeyguardVisibilityChanged(in boolean isShowing, in int userId);
- int listUidsOfAuthBoundKeys(out @utf8InCpp List<String> uids);
-
- // Called by credstore (and only credstore).
- void getTokensForCredstore(in long challenge, in long secureUserId, in int authTokenMaxAgeMillis,
- in ICredstoreTokenCallback cb);
-}
diff --git a/keystore/binder/android/security/keystore/KeystoreResponse.aidl b/keystore/binder/android/security/keystore/KeystoreResponse.aidl
deleted file mode 100644
index 128b456..0000000
--- a/keystore/binder/android/security/keystore/KeystoreResponse.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2018 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.keystore;
-
-/* @hide */
-parcelable KeystoreResponse cpp_header "keystore/KeystoreResponse.h";
diff --git a/keystore/blob.cpp b/keystore/blob.cpp
deleted file mode 100644
index ffdb454..0000000
--- a/keystore/blob.cpp
+++ /dev/null
@@ -1,791 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#define LOG_TAG "keystore"
-
-#include <arpa/inet.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <string.h>
-
-#include <log/log.h>
-
-#include "blob.h"
-
-#include "keystore_utils.h"
-
-#include <openssl/evp.h>
-#include <openssl/rand.h>
-
-#include <istream>
-#include <ostream>
-#include <streambuf>
-#include <string>
-
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-
-namespace {
-
-constexpr size_t kGcmIvSizeBytes = 96 / 8;
-
-#if defined(__clang__)
-#define OPTNONE __attribute__((optnone))
-#elif defined(__GNUC__)
-#define OPTNONE __attribute__((optimize("O0")))
-#else
-#error Need a definition for OPTNONE
-#endif
-
-class ArrayEraser {
- public:
- ArrayEraser(uint8_t* arr, size_t size) : mArr(arr), mSize(size) {}
- OPTNONE ~ArrayEraser() { std::fill(mArr, mArr + mSize, 0); }
-
- private:
- volatile uint8_t* mArr;
- size_t mSize;
-};
-
-/**
- * Returns a EVP_CIPHER appropriate for the given key, based on the key's size.
- */
-const EVP_CIPHER* getAesCipherForKey(const std::vector<uint8_t>& key) {
- const EVP_CIPHER* cipher = EVP_aes_256_gcm();
- if (key.size() == kAes128KeySizeBytes) {
- cipher = EVP_aes_128_gcm();
- }
- return cipher;
-}
-
-/*
- * Encrypt 'len' data at 'in' with AES-GCM, using 128-bit or 256-bit key at 'key', 96-bit IV at
- * 'iv' and write output to 'out' (which may be the same location as 'in') and 128-bit tag to
- * 'tag'.
- */
-ResponseCode AES_gcm_encrypt(const uint8_t* in, uint8_t* out, size_t len,
- const std::vector<uint8_t>& key, const uint8_t* iv, uint8_t* tag) {
-
- // There can be 128-bit and 256-bit keys
- const EVP_CIPHER* cipher = getAesCipherForKey(key);
-
- bssl::UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new());
-
- EVP_EncryptInit_ex(ctx.get(), cipher, nullptr /* engine */, key.data(), iv);
- EVP_CIPHER_CTX_set_padding(ctx.get(), 0 /* no padding needed with GCM */);
-
- std::unique_ptr<uint8_t[]> out_tmp(new uint8_t[len]);
- uint8_t* out_pos = out_tmp.get();
- int out_len;
-
- EVP_EncryptUpdate(ctx.get(), out_pos, &out_len, in, len);
- out_pos += out_len;
- EVP_EncryptFinal_ex(ctx.get(), out_pos, &out_len);
- out_pos += out_len;
- if (out_pos - out_tmp.get() != static_cast<ssize_t>(len)) {
- ALOGD("Encrypted ciphertext is the wrong size, expected %zu, got %zd", len,
- out_pos - out_tmp.get());
- return ResponseCode::SYSTEM_ERROR;
- }
-
- std::copy(out_tmp.get(), out_pos, out);
- EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, kGcmTagLength, tag);
-
- return ResponseCode::NO_ERROR;
-}
-
-/*
- * Decrypt 'len' data at 'in' with AES-GCM, using 128-bit or 256-bit key at 'key', 96-bit IV at
- * 'iv', checking 128-bit tag at 'tag' and writing plaintext to 'out'(which may be the same
- * location as 'in').
- */
-ResponseCode AES_gcm_decrypt(const uint8_t* in, uint8_t* out, size_t len,
- const std::vector<uint8_t> key, const uint8_t* iv,
- const uint8_t* tag) {
-
- // There can be 128-bit and 256-bit keys
- const EVP_CIPHER* cipher = getAesCipherForKey(key);
-
- bssl::UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new());
-
- EVP_DecryptInit_ex(ctx.get(), cipher, nullptr /* engine */, key.data(), iv);
- EVP_CIPHER_CTX_set_padding(ctx.get(), 0 /* no padding needed with GCM */);
- EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, kGcmTagLength, const_cast<uint8_t*>(tag));
-
- std::unique_ptr<uint8_t[]> out_tmp(new uint8_t[len]);
- ArrayEraser out_eraser(out_tmp.get(), len);
- uint8_t* out_pos = out_tmp.get();
- int out_len;
-
- EVP_DecryptUpdate(ctx.get(), out_pos, &out_len, in, len);
- out_pos += out_len;
- if (!EVP_DecryptFinal_ex(ctx.get(), out_pos, &out_len)) {
- ALOGE("Failed to decrypt blob; ciphertext or tag is likely corrupted");
- return ResponseCode::VALUE_CORRUPTED;
- }
- out_pos += out_len;
- if (out_pos - out_tmp.get() != static_cast<ssize_t>(len)) {
- ALOGE("Encrypted plaintext is the wrong size, expected %zu, got %zd", len,
- out_pos - out_tmp.get());
- return ResponseCode::VALUE_CORRUPTED;
- }
-
- std::copy(out_tmp.get(), out_pos, out);
-
- return ResponseCode::NO_ERROR;
-}
-
-class ArrayStreamBuffer : public std::streambuf {
- public:
- template <typename T, size_t size> explicit ArrayStreamBuffer(const T (&data)[size]) {
- static_assert(sizeof(T) == 1, "Array element size too large");
- std::streambuf::char_type* d = const_cast<std::streambuf::char_type*>(
- reinterpret_cast<const std::streambuf::char_type*>(&data[0]));
- setg(d, d, d + size);
- setp(d, d + size);
- }
-
- protected:
- pos_type seekoff(off_type off, std::ios_base::seekdir dir,
- std::ios_base::openmode which = std::ios_base::in |
- std::ios_base::out) override {
- bool in = which & std::ios_base::in;
- bool out = which & std::ios_base::out;
- if ((!in && !out) || (in && out && dir == std::ios_base::cur)) return -1;
- std::streambuf::char_type* newPosPtr;
- switch (dir) {
- case std::ios_base::beg:
- newPosPtr = pbase();
- break;
- case std::ios_base::cur:
- // if dir == cur then in xor out due to
- // if ((!in && !out) || (in && out && dir == std::ios_base::cur)) return -1; above
- if (in)
- newPosPtr = gptr();
- else
- newPosPtr = pptr();
- break;
- case std::ios_base::end:
- // in and out bounds are the same and cannot change, so we can take either range
- // regardless of the value of "which"
- newPosPtr = epptr();
- break;
- }
- newPosPtr += off;
- if (newPosPtr < pbase() || newPosPtr > epptr()) return -1;
- if (in) {
- gbump(newPosPtr - gptr());
- }
- if (out) {
- pbump(newPosPtr - pptr());
- }
- return newPosPtr - pbase();
- }
-};
-
-} // namespace
-
-Blob::Blob(const uint8_t* value, size_t valueLength, const uint8_t* info, uint8_t infoLength,
- BlobType type) {
- mBlob = std::make_unique<blobv3>();
- memset(mBlob.get(), 0, sizeof(blobv3));
- if (valueLength > kValueSize) {
- valueLength = kValueSize;
- ALOGW("Provided blob length too large");
- }
- if (infoLength + valueLength > kValueSize) {
- infoLength = kValueSize - valueLength;
- ALOGW("Provided info length too large");
- }
- mBlob->length = valueLength;
- memcpy(mBlob->value, value, valueLength);
-
- mBlob->info = infoLength;
- memcpy(mBlob->value + valueLength, info, infoLength);
-
- mBlob->version = CURRENT_BLOB_VERSION;
- mBlob->type = uint8_t(type);
-
- if (type == TYPE_MASTER_KEY || type == TYPE_MASTER_KEY_AES256) {
- mBlob->flags = KEYSTORE_FLAG_ENCRYPTED;
- } else {
- mBlob->flags = KEYSTORE_FLAG_NONE;
- }
-}
-
-Blob::Blob(blobv3 b) {
- mBlob = std::make_unique<blobv3>(b);
-}
-
-Blob::Blob() {
- if (mBlob) *mBlob = {};
-}
-
-Blob::Blob(const Blob& rhs) {
- if (rhs.mBlob) {
- mBlob = std::make_unique<blobv3>(*rhs.mBlob);
- }
-}
-
-Blob::Blob(Blob&& rhs) : mBlob(std::move(rhs.mBlob)) {}
-
-Blob& Blob::operator=(const Blob& rhs) {
- if (&rhs != this) {
- if (mBlob) *mBlob = {};
- if (rhs) {
- mBlob = std::make_unique<blobv3>(*rhs.mBlob);
- } else {
- mBlob = {};
- }
- }
- return *this;
-}
-
-Blob& Blob::operator=(Blob&& rhs) {
- if (mBlob) *mBlob = {};
- mBlob = std::move(rhs.mBlob);
- return *this;
-}
-
-template <typename BlobType> static bool rawBlobIsEncrypted(const BlobType& blob) {
- if (blob.version < 2) return true;
-
- return blob.flags & (KEYSTORE_FLAG_ENCRYPTED | KEYSTORE_FLAG_SUPER_ENCRYPTED);
-}
-
-bool Blob::isEncrypted() const {
- if (mBlob->version < 2) {
- return true;
- }
-
- return mBlob->flags & KEYSTORE_FLAG_ENCRYPTED;
-}
-
-bool Blob::isSuperEncrypted() const {
- return mBlob->flags & KEYSTORE_FLAG_SUPER_ENCRYPTED;
-}
-
-bool Blob::isCriticalToDeviceEncryption() const {
- return mBlob->flags & KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION;
-}
-
-inline uint8_t setFlag(uint8_t flags, bool set, KeyStoreFlag flag) {
- return set ? (flags | flag) : (flags & ~flag);
-}
-
-void Blob::setEncrypted(bool encrypted) {
- mBlob->flags = setFlag(mBlob->flags, encrypted, KEYSTORE_FLAG_ENCRYPTED);
-}
-
-void Blob::setSuperEncrypted(bool superEncrypted) {
- mBlob->flags = setFlag(mBlob->flags, superEncrypted, KEYSTORE_FLAG_SUPER_ENCRYPTED);
-}
-
-void Blob::setCriticalToDeviceEncryption(bool critical) {
- mBlob->flags = setFlag(mBlob->flags, critical, KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION);
-}
-
-void Blob::setFallback(bool fallback) {
- if (fallback) {
- mBlob->flags |= KEYSTORE_FLAG_FALLBACK;
- } else {
- mBlob->flags &= ~KEYSTORE_FLAG_FALLBACK;
- }
-}
-
-static ResponseCode writeBlob(const std::string& filename, Blob blob, blobv3* rawBlob,
- const std::vector<uint8_t>& aes_key, State state) {
- ALOGV("writing blob %s", filename.c_str());
-
- const size_t dataLength = rawBlob->length;
- rawBlob->length = htonl(rawBlob->length);
-
- if (blob.isEncrypted() || blob.isSuperEncrypted()) {
- if (state != STATE_NO_ERROR) {
- ALOGD("couldn't insert encrypted blob while not unlocked");
- return ResponseCode::LOCKED;
- }
-
- memset(rawBlob->initialization_vector, 0, AES_BLOCK_SIZE);
- if (!RAND_bytes(rawBlob->initialization_vector, kGcmIvSizeBytes)) {
- ALOGW("Could not read random data for: %s", filename.c_str());
- return ResponseCode::SYSTEM_ERROR;
- }
-
- auto rc = AES_gcm_encrypt(rawBlob->value /* in */, rawBlob->value /* out */, dataLength,
- aes_key, rawBlob->initialization_vector, rawBlob->aead_tag);
- if (rc != ResponseCode::NO_ERROR) return rc;
- }
-
- size_t fileLength = offsetof(blobv3, value) + dataLength + rawBlob->info;
-
- char tmpFileName[] = ".tmpXXXXXX";
- {
- android::base::unique_fd out(TEMP_FAILURE_RETRY(mkstemp(tmpFileName)));
- if (out < 0) {
- LOG(ERROR) << "could not open temp file: " << tmpFileName
- << " for writing blob file: " << filename.c_str()
- << " because: " << strerror(errno);
- return ResponseCode::SYSTEM_ERROR;
- }
-
- const size_t writtenBytes =
- writeFully(out, reinterpret_cast<uint8_t*>(rawBlob), fileLength);
-
- if (writtenBytes != fileLength) {
- LOG(ERROR) << "blob not fully written " << writtenBytes << " != " << fileLength;
- unlink(tmpFileName);
- return ResponseCode::SYSTEM_ERROR;
- }
- }
-
- if (rename(tmpFileName, filename.c_str()) == -1) {
- LOG(ERROR) << "could not rename blob file to " << filename
- << " because: " << strerror(errno);
- unlink(tmpFileName);
- return ResponseCode::SYSTEM_ERROR;
- }
-
- fsyncDirectory(getContainingDirectory(filename));
-
- return ResponseCode::NO_ERROR;
-}
-
-ResponseCode LockedKeyBlobEntry::writeBlobs(Blob keyBlob, Blob characteristicsBlob,
- const std::vector<uint8_t>& aes_key,
- State state) const {
- if (entry_ == nullptr) {
- return ResponseCode::SYSTEM_ERROR;
- }
- ResponseCode rc;
- if (keyBlob) {
- blobv3* rawBlob = keyBlob.mBlob.get();
- rc = writeBlob(entry_->getKeyBlobPath(), std::move(keyBlob), rawBlob, aes_key, state);
- if (rc != ResponseCode::NO_ERROR) {
- return rc;
- }
- }
-
- if (characteristicsBlob) {
- blobv3* rawBlob = characteristicsBlob.mBlob.get();
- rc = writeBlob(entry_->getCharacteristicsBlobPath(), std::move(characteristicsBlob),
- rawBlob, aes_key, state);
- }
- return rc;
-}
-
-ResponseCode Blob::readBlob(const std::string& filename, const std::vector<uint8_t>& aes_key,
- State state) {
- ResponseCode rc;
- ALOGV("reading blob %s", filename.c_str());
- std::unique_ptr<blobv3> rawBlob = std::make_unique<blobv3>();
-
- const int in = TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY));
- if (in < 0) {
- return (errno == ENOENT) ? ResponseCode::KEY_NOT_FOUND : ResponseCode::SYSTEM_ERROR;
- }
-
- // fileLength may be less than sizeof(mBlob)
- const size_t fileLength = readFully(in, (uint8_t*)rawBlob.get(), sizeof(blobv3));
- if (close(in) != 0) {
- return ResponseCode::SYSTEM_ERROR;
- }
-
- if (fileLength == 0) {
- LOG(ERROR) << __func__ << " VALUE_CORRUPTED file length == 0";
- return ResponseCode::VALUE_CORRUPTED;
- }
-
- if (rawBlobIsEncrypted(*rawBlob)) {
- if (state == STATE_LOCKED) {
- mBlob = std::move(rawBlob);
- return ResponseCode::LOCKED;
- }
- if (state == STATE_UNINITIALIZED) return ResponseCode::UNINITIALIZED;
- }
-
- if (fileLength < offsetof(blobv3, value)) {
- LOG(ERROR) << __func__ << " VALUE_CORRUPTED blob file too short: " << fileLength;
- return ResponseCode::VALUE_CORRUPTED;
- }
-
- if (rawBlob->version == 3) {
- const ssize_t encryptedLength = ntohl(rawBlob->length);
-
- if (rawBlobIsEncrypted(*rawBlob)) {
- rc = AES_gcm_decrypt(rawBlob->value /* in */, rawBlob->value /* out */, encryptedLength,
- aes_key, rawBlob->initialization_vector, rawBlob->aead_tag);
- if (rc != ResponseCode::NO_ERROR) {
- // If the blob was superencrypted and decryption failed, it is
- // almost certain that decryption is failing due to a user's
- // changed master key.
- if ((rawBlob->flags & KEYSTORE_FLAG_SUPER_ENCRYPTED) &&
- (rc == ResponseCode::VALUE_CORRUPTED)) {
- return ResponseCode::KEY_PERMANENTLY_INVALIDATED;
- }
- LOG(ERROR) << __func__ << " AES_gcm_decrypt returned: " << uint32_t(rc);
-
- return rc;
- }
- }
- } else if (rawBlob->version < 3) {
- blobv2& v2blob = reinterpret_cast<blobv2&>(*rawBlob);
- const size_t headerLength = offsetof(blobv2, encrypted);
- const ssize_t encryptedLength = fileLength - headerLength - v2blob.info;
- if (encryptedLength < 0) {
- LOG(ERROR) << __func__ << " VALUE_CORRUPTED v2blob file too short";
- return ResponseCode::VALUE_CORRUPTED;
- }
-
- if (rawBlobIsEncrypted(*rawBlob)) {
- if (encryptedLength % AES_BLOCK_SIZE != 0) {
- LOG(ERROR) << __func__
- << " VALUE_CORRUPTED encrypted length is not a multiple"
- " of the AES block size";
- return ResponseCode::VALUE_CORRUPTED;
- }
-
- AES_KEY key;
- AES_set_decrypt_key(aes_key.data(), kAesKeySize * 8, &key);
- AES_cbc_encrypt(v2blob.encrypted, v2blob.encrypted, encryptedLength, &key,
- v2blob.vector, AES_DECRYPT);
- key = {}; // clear key
-
- uint8_t computedDigest[MD5_DIGEST_LENGTH];
- ssize_t digestedLength = encryptedLength - MD5_DIGEST_LENGTH;
- MD5(v2blob.digested, digestedLength, computedDigest);
- if (memcmp(v2blob.digest, computedDigest, MD5_DIGEST_LENGTH) != 0) {
- LOG(ERROR) << __func__ << " v2blob MD5 digest mismatch";
- return ResponseCode::VALUE_CORRUPTED;
- }
- }
- }
-
- const ssize_t maxValueLength = fileLength - offsetof(blobv3, value) - rawBlob->info;
- rawBlob->length = ntohl(rawBlob->length);
- if (rawBlob->length < 0 || rawBlob->length > maxValueLength ||
- rawBlob->length + rawBlob->info + AES_BLOCK_SIZE >
- static_cast<ssize_t>(sizeof(rawBlob->value))) {
- LOG(ERROR) << __func__ << " raw blob length is out of bounds";
- return ResponseCode::VALUE_CORRUPTED;
- }
-
- if (rawBlob->info != 0 && rawBlob->version < 3) {
- // move info from after padding to after data
- memmove(rawBlob->value + rawBlob->length, rawBlob->value + maxValueLength, rawBlob->info);
- }
-
- mBlob = std::move(rawBlob);
- return ResponseCode::NO_ERROR;
-}
-
-std::tuple<ResponseCode, Blob, Blob>
-LockedKeyBlobEntry::readBlobs(const std::vector<uint8_t>& aes_key, State state) const {
- std::tuple<ResponseCode, Blob, Blob> result;
- auto& [rc, keyBlob, characteristicsBlob] = result;
- if (entry_ == nullptr) return rc = ResponseCode::SYSTEM_ERROR, result;
-
- rc = keyBlob.readBlob(entry_->getKeyBlobPath(), aes_key, state);
- if (rc != ResponseCode::NO_ERROR && rc != ResponseCode::UNINITIALIZED) {
- return result;
- }
-
- if (entry_->hasCharacteristicsBlob()) {
- characteristicsBlob.readBlob(entry_->getCharacteristicsBlobPath(), aes_key, state);
- }
- return result;
-}
-
-ResponseCode LockedKeyBlobEntry::deleteBlobs() const {
- if (entry_ == nullptr) return ResponseCode::NO_ERROR;
-
- // always try to delete both
- ResponseCode rc1 = (unlink(entry_->getKeyBlobPath().c_str()) && errno != ENOENT)
- ? ResponseCode::SYSTEM_ERROR
- : ResponseCode::NO_ERROR;
- if (rc1 != ResponseCode::NO_ERROR) {
- ALOGW("Failed to delete key blob file \"%s\"", entry_->getKeyBlobPath().c_str());
- }
- ResponseCode rc2 = (unlink(entry_->getCharacteristicsBlobPath().c_str()) && errno != ENOENT)
- ? ResponseCode::SYSTEM_ERROR
- : ResponseCode::NO_ERROR;
- if (rc2 != ResponseCode::NO_ERROR) {
- ALOGW("Failed to delete key characteristics file \"%s\"",
- entry_->getCharacteristicsBlobPath().c_str());
- }
- // then report the first error that occured
- if (rc1 != ResponseCode::NO_ERROR) return rc1;
- return rc2;
-}
-
-keystore::SecurityLevel Blob::getSecurityLevel() const {
- return keystore::flagsToSecurityLevel(mBlob->flags);
-}
-
-void Blob::setSecurityLevel(keystore::SecurityLevel secLevel) {
- mBlob->flags &= ~(KEYSTORE_FLAG_FALLBACK | KEYSTORE_FLAG_STRONGBOX);
- mBlob->flags |= keystore::securityLevelToFlags(secLevel);
-}
-
-std::tuple<bool, keystore::AuthorizationSet, keystore::AuthorizationSet>
-Blob::getKeyCharacteristics() const {
- std::tuple<bool, keystore::AuthorizationSet, keystore::AuthorizationSet> result;
- auto& [success, hwEnforced, swEnforced] = result;
- success = false;
- ArrayStreamBuffer buf(mBlob->value);
- std::istream in(&buf);
-
- // only the characteristics cache has both sets
- if (getType() == TYPE_KEY_CHARACTERISTICS_CACHE) {
- hwEnforced.Deserialize(&in);
- } else if (getType() != TYPE_KEY_CHARACTERISTICS) {
- // if its not the cache and not the legacy characteristics file we have no business
- // here
- return result;
- }
- swEnforced.Deserialize(&in);
- success = !in.bad();
-
- return result;
-}
-bool Blob::putKeyCharacteristics(const keystore::AuthorizationSet& hwEnforced,
- const keystore::AuthorizationSet& swEnforced) {
- if (!mBlob) mBlob = std::make_unique<blobv3>();
- mBlob->version = CURRENT_BLOB_VERSION;
- ArrayStreamBuffer buf(mBlob->value);
- std::ostream out(&buf);
- hwEnforced.Serialize(&out);
- swEnforced.Serialize(&out);
- if (out.bad()) return false;
- setType(TYPE_KEY_CHARACTERISTICS_CACHE);
- mBlob->length = out.tellp();
- return true;
-}
-
-void LockedKeyBlobEntry::put(const KeyBlobEntry& entry) {
- std::unique_lock<std::mutex> lock(locked_blobs_mutex_);
- locked_blobs_.erase(entry);
- lock.unlock();
- locked_blobs_mutex_cond_var_.notify_all();
-}
-
-LockedKeyBlobEntry::~LockedKeyBlobEntry() {
- if (entry_ != nullptr) put(*entry_);
-}
-
-LockedKeyBlobEntry LockedKeyBlobEntry::get(KeyBlobEntry entry) {
- std::unique_lock<std::mutex> lock(locked_blobs_mutex_);
- locked_blobs_mutex_cond_var_.wait(
- lock, [&] { return locked_blobs_.find(entry) == locked_blobs_.end(); });
- auto [iterator, success] = locked_blobs_.insert(std::move(entry));
- if (!success) return {};
- return LockedKeyBlobEntry(*iterator);
-}
-
-std::set<KeyBlobEntry> LockedKeyBlobEntry::locked_blobs_;
-std::mutex LockedKeyBlobEntry::locked_blobs_mutex_;
-std::condition_variable LockedKeyBlobEntry::locked_blobs_mutex_cond_var_;
-
-/* Here is the encoding of key names. This is necessary in order to allow arbitrary
- * characters in key names. Characters in [0-~] are not encoded. Others are encoded
- * into two bytes. The first byte is one of [+-.] which represents the first
- * two bits of the character. The second byte encodes the rest of the bits into
- * [0-o]. Therefore in the worst case the length of a key gets doubled. Note
- * that Base64 cannot be used here due to the need of prefix match on keys. */
-
-std::string encodeKeyName(const std::string& keyName) {
- std::string encodedName;
- encodedName.reserve(keyName.size() * 2);
- auto in = keyName.begin();
- while (in != keyName.end()) {
- // Input character needs to be encoded.
- if (*in < '0' || *in > '~') {
- // Encode the two most-significant bits of the input char in the first
- // output character, by counting up from 43 ('+').
- encodedName.append(1, '+' + (uint8_t(*in) >> 6));
- // Encode the six least-significant bits of the input char in the second
- // output character, by counting up from 48 ('0').
- // This is safe because the maximum value is 112, which is the
- // character 'p'.
- encodedName.append(1, '0' + (*in & 0x3F));
- } else {
- // No need to encode input char - append as-is.
- encodedName.append(1, *in);
- }
- ++in;
- }
- return encodedName;
-}
-
-std::string decodeKeyName(const std::string& encodedName) {
- std::string decodedName;
- decodedName.reserve(encodedName.size());
- auto in = encodedName.begin();
- bool multichar = false;
- char c;
- while (in != encodedName.end()) {
- if (multichar) {
- // Second part of a multi-character encoding. Turn off the multichar
- // flag and set the six least-significant bits of c to the value originally
- // encoded by counting up from '0'.
- multichar = false;
- decodedName.append(1, c | (uint8_t(*in) - '0'));
- } else if (*in >= '+' && *in <= '.') {
- // First part of a multi-character encoding. Set the multichar flag
- // and set the two most-significant bits of c to be the two bits originally
- // encoded by counting up from '+'.
- multichar = true;
- c = (*in - '+') << 6;
- } else {
- // Regular character, append as-is.
- decodedName.append(1, *in);
- }
- ++in;
- }
- // mulitchars at the end get truncated
- return decodedName;
-}
-
-std::string KeyBlobEntry::getKeyBlobBaseName() const {
- std::stringstream s;
- if (masterkey_) {
- s << alias_;
- } else {
- s << uid_ << "_" << encodeKeyName(alias_);
- }
- return s.str();
-}
-
-std::string KeyBlobEntry::getKeyBlobPath() const {
- std::stringstream s;
- if (masterkey_) {
- s << user_dir_ << "/" << alias_;
- } else {
- s << user_dir_ << "/" << uid_ << "_" << encodeKeyName(alias_);
- }
- return s.str();
-}
-
-std::string KeyBlobEntry::getCharacteristicsBlobBaseName() const {
- std::stringstream s;
- if (!masterkey_) s << "." << uid_ << "_chr_" << encodeKeyName(alias_);
- return s.str();
-}
-
-std::string KeyBlobEntry::getCharacteristicsBlobPath() const {
- std::stringstream s;
- if (!masterkey_)
- s << user_dir_ << "/"
- << "." << uid_ << "_chr_" << encodeKeyName(alias_);
- return s.str();
-}
-
-bool KeyBlobEntry::hasKeyBlob() const {
- int trys = 3;
- while (trys--) {
- if (!access(getKeyBlobPath().c_str(), R_OK | W_OK)) return true;
- if (errno == ENOENT) return false;
- LOG(WARNING) << "access encountered " << strerror(errno) << " (" << errno << ")"
- << " while checking for key blob";
- if (errno != EAGAIN) break;
- }
- return false;
-}
-
-bool KeyBlobEntry::hasCharacteristicsBlob() const {
- int trys = 3;
- while (trys--) {
- if (!access(getCharacteristicsBlobPath().c_str(), R_OK | W_OK)) return true;
- if (errno == ENOENT) return false;
- LOG(WARNING) << "access encountered " << strerror(errno) << " (" << errno << ")"
- << " while checking for key characteristics blob";
- if (errno != EAGAIN) break;
- }
- return false;
-}
-
-static std::tuple<bool, uid_t, std::string> filename2UidAlias(const std::string& filepath) {
- std::tuple<bool, uid_t, std::string> result;
-
- auto& [success, uid, alias] = result;
-
- success = false;
-
- auto filenamebase = filepath.find_last_of('/');
- std::string filename =
- filenamebase == std::string::npos ? filepath : filepath.substr(filenamebase + 1);
-
- if (filename[0] == '.') return result;
-
- auto sep = filename.find('_');
- if (sep == std::string::npos) return result;
-
- std::stringstream s(filename.substr(0, sep));
- s >> uid;
- if (!s) return result;
-
- alias = decodeKeyName(filename.substr(sep + 1));
- success = true;
- return result;
-}
-
-std::tuple<ResponseCode, std::list<LockedKeyBlobEntry>>
-LockedKeyBlobEntry::list(const std::string& user_dir,
- std::function<bool(uid_t, const std::string&)> filter) {
- std::list<LockedKeyBlobEntry> matches;
-
- // This is a fence against any concurrent database accesses during database iteration.
- // Only the keystore thread can lock entries. So it cannot be starved
- // by workers grabbing new individual locks. We just wait here until all
- // workers have relinquished their locked files.
- std::unique_lock<std::mutex> lock(locked_blobs_mutex_);
- locked_blobs_mutex_cond_var_.wait(lock, [&] { return locked_blobs_.empty(); });
-
- DIR* dir = opendir(user_dir.c_str());
- if (!dir) {
- ALOGW("can't open directory for user: %s", strerror(errno));
- return std::tuple<ResponseCode, std::list<LockedKeyBlobEntry>&&>{ResponseCode::SYSTEM_ERROR,
- std::move(matches)};
- }
-
- struct dirent* file;
- while ((file = readdir(dir)) != nullptr) {
- // We only care about files.
- if (file->d_type != DT_REG) {
- continue;
- }
-
- // Skip anything that starts with a "."
- if (file->d_name[0] == '.') {
- continue;
- }
-
- auto [success, uid, alias] = filename2UidAlias(file->d_name);
-
- if (!success) {
- ALOGW("could not parse key filename \"%s\"", file->d_name);
- continue;
- }
-
- if (!filter(uid, alias)) continue;
-
- auto [iterator, dummy] = locked_blobs_.emplace(alias, user_dir, uid);
- matches.push_back(*iterator);
- }
- closedir(dir);
- return std::tuple<ResponseCode, std::list<LockedKeyBlobEntry>&&>{ResponseCode::NO_ERROR,
- std::move(matches)};
-}
diff --git a/keystore/blob.h b/keystore/blob.h
deleted file mode 100644
index e0bd146..0000000
--- a/keystore/blob.h
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#ifndef KEYSTORE_BLOB_H_
-#define KEYSTORE_BLOB_H_
-
-#include <stdint.h>
-
-#include <openssl/aes.h>
-#include <openssl/md5.h>
-
-#include <condition_variable>
-#include <functional>
-#include <keystore/keymaster_types.h>
-#include <keystore/keystore.h>
-#include <list>
-#include <mutex>
-#include <set>
-#include <sstream>
-#include <vector>
-
-constexpr size_t kValueSize = 32768;
-constexpr size_t kAesKeySize = 128 / 8;
-constexpr size_t kGcmTagLength = 128 / 8;
-constexpr size_t kGcmIvLength = 96 / 8;
-constexpr size_t kAes128KeySizeBytes = 128 / 8;
-constexpr size_t kAes256KeySizeBytes = 256 / 8;
-
-/* Here is the file format. There are two parts in blob.value, the secret and
- * the description. The secret is stored in ciphertext, and its original size
- * can be found in blob.length. The description is stored after the secret in
- * plaintext, and its size is specified in blob.info. The total size of the two
- * parts must be no more than kValueSize bytes. The first field is the version,
- * the second is the blob's type, and the third byte is flags. Fields other
- * than blob.info, blob.length, and blob.value are modified by encryptBlob()
- * and decryptBlob(). Thus they should not be accessed from outside. */
-
-struct __attribute__((packed)) blobv3 {
- uint8_t version;
- uint8_t type;
- uint8_t flags;
- uint8_t info;
- uint8_t initialization_vector[AES_BLOCK_SIZE]; // Only 96 bits is used, rest is zeroed.
- uint8_t aead_tag[kGcmTagLength];
- int32_t length; // in network byte order, only for backward compatibility
- uint8_t value[kValueSize + AES_BLOCK_SIZE];
-};
-
-struct __attribute__((packed)) blobv2 {
- uint8_t version;
- uint8_t type;
- uint8_t flags;
- uint8_t info;
- uint8_t vector[AES_BLOCK_SIZE];
- uint8_t encrypted[0]; // Marks offset to encrypted data.
- uint8_t digest[MD5_DIGEST_LENGTH];
- uint8_t digested[0]; // Marks offset to digested data.
- int32_t length; // in network byte order
- uint8_t value[kValueSize + AES_BLOCK_SIZE];
-};
-
-static_assert(sizeof(blobv3) == sizeof(blobv2) &&
- offsetof(blobv3, initialization_vector) == offsetof(blobv2, vector) &&
- offsetof(blobv3, aead_tag) == offsetof(blobv2, digest) &&
- offsetof(blobv3, aead_tag) == offsetof(blobv2, encrypted) &&
- offsetof(blobv3, length) == offsetof(blobv2, length) &&
- offsetof(blobv3, value) == offsetof(blobv2, value),
- "Oops. Blob layout changed.");
-
-static const uint8_t CURRENT_BLOB_VERSION = 3;
-
-typedef enum {
- TYPE_ANY = 0, // meta type that matches anything
- TYPE_GENERIC = 1,
- TYPE_MASTER_KEY = 2,
- TYPE_KEY_PAIR = 3,
- TYPE_KEYMASTER_10 = 4,
- TYPE_KEY_CHARACTERISTICS = 5,
- TYPE_KEY_CHARACTERISTICS_CACHE = 6,
- TYPE_MASTER_KEY_AES256 = 7,
-} BlobType;
-
-class LockedKeyBlobEntry;
-
-/**
- * The Blob represents the content of a KeyBlobEntry.
- *
- * BEWARE: It is only save to call any member function of a Blob b if bool(b) yields true.
- * Exceptions are putKeyCharacteristics(), the assignment operators and operator bool.
- */
-class Blob {
- friend LockedKeyBlobEntry;
-
- public:
- Blob(const uint8_t* value, size_t valueLength, const uint8_t* info, uint8_t infoLength,
- BlobType type);
- explicit Blob(blobv3 b);
- Blob();
- Blob(const Blob& rhs);
- Blob(Blob&& rhs);
-
- ~Blob() {
- if (mBlob) *mBlob = {};
- }
-
- Blob& operator=(const Blob& rhs);
- Blob& operator=(Blob&& rhs);
- explicit operator bool() const { return bool(mBlob); }
-
- const uint8_t* getValue() const { return mBlob->value; }
-
- int32_t getLength() const { return mBlob->length; }
-
- const uint8_t* getInfo() const { return mBlob->value + mBlob->length; }
- uint8_t getInfoLength() const { return mBlob->info; }
-
- uint8_t getVersion() const { return mBlob->version; }
-
- bool isEncrypted() const;
- void setEncrypted(bool encrypted);
-
- bool isSuperEncrypted() const;
- void setSuperEncrypted(bool superEncrypted);
-
- bool isCriticalToDeviceEncryption() const;
- void setCriticalToDeviceEncryption(bool critical);
-
- bool isFallback() const { return mBlob->flags & KEYSTORE_FLAG_FALLBACK; }
- void setFallback(bool fallback);
-
- void setVersion(uint8_t version) { mBlob->version = version; }
- BlobType getType() const { return BlobType(mBlob->type); }
- void setType(BlobType type) { mBlob->type = uint8_t(type); }
-
- keystore::SecurityLevel getSecurityLevel() const;
- void setSecurityLevel(keystore::SecurityLevel);
-
- std::tuple<bool, keystore::AuthorizationSet, keystore::AuthorizationSet>
- getKeyCharacteristics() const;
-
- bool putKeyCharacteristics(const keystore::AuthorizationSet& hwEnforced,
- const keystore::AuthorizationSet& swEnforced);
-
- private:
- std::unique_ptr<blobv3> mBlob;
-
- ResponseCode readBlob(const std::string& filename, const std::vector<uint8_t>& aes_key,
- State state);
-};
-
-/**
- * A KeyBlobEntry represents a full qualified key blob as known by Keystore. The key blob
- * is given by the uid of the owning app and the alias used by the app to refer to this key.
- * The user_dir_ is technically implied by the uid, but computation of the user directory is
- * done in the user state database. Which is why we also cache it here.
- *
- * The KeyBlobEntry knows the location of the key blob files (which may include a characteristics
- * cache file) but does not allow read or write access to the content. It also does not imply
- * the existence of the files.
- *
- * KeyBlobEntry abstracts, to some extent, from the the file system based storage of key blobs.
- * An evolution of KeyBlobEntry may be used for key blob storage based on a back end other than
- * file system, e.g., SQL database or other.
- *
- * For access to the key blob content the programmer has to acquire a LockedKeyBlobEntry (see
- * below).
- */
-class KeyBlobEntry {
- private:
- std::string alias_;
- std::string user_dir_;
- uid_t uid_;
- bool masterkey_;
-
- public:
- KeyBlobEntry(std::string alias, std::string user_dir, uid_t uid, bool masterkey = false)
- : alias_(std::move(alias)), user_dir_(std::move(user_dir)), uid_(uid),
- masterkey_(masterkey) {}
-
- std::string getKeyBlobBaseName() const;
- std::string getKeyBlobPath() const;
-
- std::string getCharacteristicsBlobBaseName() const;
- std::string getCharacteristicsBlobPath() const;
-
- bool hasKeyBlob() const;
- bool hasCharacteristicsBlob() const;
-
- bool operator<(const KeyBlobEntry& rhs) const {
- return std::tie(uid_, alias_, user_dir_) < std::tie(rhs.uid_, rhs.alias_, rhs.user_dir_);
- }
- bool operator==(const KeyBlobEntry& rhs) const {
- return std::tie(uid_, alias_, user_dir_) == std::tie(rhs.uid_, rhs.alias_, rhs.user_dir_);
- }
- bool operator!=(const KeyBlobEntry& rhs) const { return !(*this == rhs); }
-
- inline const std::string& alias() const { return alias_; }
- inline const std::string& user_dir() const { return user_dir_; }
- inline uid_t uid() const { return uid_; }
-};
-
-/**
- * The LockedKeyBlobEntry is a proxy object to KeyBlobEntry that expresses exclusive ownership
- * of a KeyBlobEntry. LockedKeyBlobEntries can be acquired by calling
- * LockedKeyBlobEntry::get() or LockedKeyBlobEntry::list().
- *
- * LockedKeyBlobEntries are movable but not copyable. By convention they can only
- * be taken by the dispatcher thread of keystore, but not by any keymaster worker thread.
- * The dispatcher thread may transfer ownership of a locked entry to a keymaster worker thread.
- *
- * Locked entries are tracked on the stack or as members of movable functor objects passed to the
- * keymaster worker request queues. Locks are relinquished as the locked entry gets destroyed, e.g.,
- * when it goes out of scope or when the owning request functor gets destroyed.
- *
- * LockedKeyBlobEntry::list(), which must only be called by the dispatcher, blocks until all
- * LockedKeyBlobEntries have been destroyed. Thereby list acts as a fence to make sure it gets a
- * consistent view of the key blob database. Under the assumption that keymaster worker requests
- * cannot run or block indefinitely and cannot grab new locked entries, progress is guaranteed.
- * It then grabs locked entries in accordance with the given filter rule.
- *
- * LockedKeyBlobEntry allow access to the proxied KeyBlobEntry interface through the operator->.
- * They add additional functionality to access and modify the key blob's content on disk.
- * LockedKeyBlobEntry ensures atomic operations on the persistently stored key blobs on a per
- * entry granularity.
- */
-class LockedKeyBlobEntry {
- private:
- static std::set<KeyBlobEntry> locked_blobs_;
- static std::mutex locked_blobs_mutex_;
- static std::condition_variable locked_blobs_mutex_cond_var_;
-
- const KeyBlobEntry* entry_;
- // NOLINTNEXTLINE(google-explicit-constructor)
- LockedKeyBlobEntry(const KeyBlobEntry& entry) : entry_(&entry) {}
-
- static void put(const KeyBlobEntry& entry);
- LockedKeyBlobEntry(const LockedKeyBlobEntry&) = delete;
- LockedKeyBlobEntry& operator=(const LockedKeyBlobEntry&) = delete;
-
- public:
- LockedKeyBlobEntry() : entry_(nullptr){};
- ~LockedKeyBlobEntry();
- LockedKeyBlobEntry(LockedKeyBlobEntry&& rhs) : entry_(rhs.entry_) { rhs.entry_ = nullptr; }
- LockedKeyBlobEntry& operator=(LockedKeyBlobEntry&& rhs) {
- // as dummy goes out of scope it relinquishes the lock on this
- LockedKeyBlobEntry dummy(std::move(*this));
- entry_ = rhs.entry_;
- rhs.entry_ = nullptr;
- return *this;
- }
- static LockedKeyBlobEntry get(KeyBlobEntry entry);
- static std::tuple<ResponseCode, std::list<LockedKeyBlobEntry>>
- list(const std::string& user_dir,
- std::function<bool(uid_t, const std::string&)> filter =
- [](uid_t, const std::string&) -> bool { return true; });
-
- ResponseCode writeBlobs(Blob keyBlob, Blob characteristicsBlob,
- const std::vector<uint8_t>& aes_key, State state) const;
- std::tuple<ResponseCode, Blob, Blob> readBlobs(const std::vector<uint8_t>& aes_key,
- State state) const;
- ResponseCode deleteBlobs() const;
-
- inline explicit operator bool() const { return entry_ != nullptr; }
- inline const KeyBlobEntry& operator*() const { return *entry_; }
- inline const KeyBlobEntry* operator->() const { return entry_; }
-};
-
-// Visible for testing
-std::string encodeKeyName(const std::string& keyName);
-std::string decodeKeyName(const std::string& encodedName);
-
-#endif // KEYSTORE_BLOB_H_
diff --git a/keystore/confirmation_manager.cpp b/keystore/confirmation_manager.cpp
deleted file mode 100644
index 76df1cc..0000000
--- a/keystore/confirmation_manager.cpp
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#define LOG_TAG "ConfirmationManager"
-
-#include "confirmation_manager.h"
-
-#include <android/hardware/confirmationui/1.0/IConfirmationResultCallback.h>
-#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
-#include <android/hardware/confirmationui/1.0/types.h>
-#include <android/security/BpConfirmationPromptCallback.h>
-#include <binder/BpBinder.h>
-#include <binder/IPCThreadState.h>
-#include <binder/Parcel.h>
-
-#include "keystore_aidl_hidl_marshalling_utils.h"
-
-namespace keystore {
-
-using android::IBinder;
-using android::sp;
-using android::String16;
-using android::String8;
-using android::wp;
-using android::binder::Status;
-using android::hardware::hidl_vec;
-using android::hardware::Return;
-using android::hardware::confirmationui::V1_0::IConfirmationResultCallback;
-using android::hardware::confirmationui::V1_0::IConfirmationUI;
-using android::hardware::confirmationui::V1_0::UIOption;
-
-using android::security::BpConfirmationPromptCallback;
-using std::lock_guard;
-using std::mutex;
-using std::vector;
-
-ConfirmationManager::ConfirmationManager(IBinder::DeathRecipient* deathRecipient)
- : IConfirmationResultCallback(), mDeathRecipient(deathRecipient) {}
-
-// Called by keystore main thread.
-Status ConfirmationManager::presentConfirmationPrompt(const sp<IBinder>& listener,
- const String16& promptText,
- const hidl_vec<uint8_t>& extraData,
- const String16& locale, int uiOptionsAsFlags,
- int32_t* aidl_return) {
- lock_guard<mutex> lock(mMutex);
-
- if (mCurrentListener != nullptr) {
- *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::OperationPending);
- return Status::ok();
- }
-
- sp<IConfirmationUI> confirmationUI = IConfirmationUI::tryGetService();
- if (confirmationUI == nullptr) {
- ALOGW("Error getting confirmationUI service\n");
- *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::Unimplemented);
- return Status::ok();
- }
-
- uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
- if (!mRateLimiting.tryPrompt(callingUid)) {
- *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::SystemError);
- return Status::ok();
- }
-
- String8 promptText8(promptText);
- String8 locale8(locale);
- vector<UIOption> uiOptionsVector;
- for (int n = 0; n < 32; n++) {
- if (uiOptionsAsFlags & (1 << n)) {
- uiOptionsVector.push_back(UIOption(n));
- }
- }
- ConfirmationResponseCode responseCode;
- responseCode = confirmationUI->promptUserConfirmation(sp<IConfirmationResultCallback>(this),
- promptText8.string(), extraData,
- locale8.string(), uiOptionsVector);
- if (responseCode != ConfirmationResponseCode::OK) {
- ALOGW("Unexpecxted responseCode %d from promptUserConfirmation\n", responseCode);
- *aidl_return = static_cast<int32_t>(responseCode);
- return Status::ok();
- }
-
- listener->linkToDeath(mDeathRecipient);
- confirmationUI->linkToDeath(this, 0);
- mCurrentListener = listener;
- mCurrentConfirmationUI = confirmationUI;
-
- *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::OK);
- return Status::ok();
-}
-
-// Called by keystore main thread.
-Status ConfirmationManager::cancelConfirmationPrompt(const sp<IBinder>& listener,
- int32_t* aidl_return) {
- mMutex.lock();
- if (mCurrentListener != listener) {
- // If the prompt was displayed by another application, return
- // OperationPending.
- mMutex.unlock();
- *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::OperationPending);
- return Status::ok();
- }
- mMutex.unlock();
-
- cancelPrompt();
-
- *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::OK);
- return Status::ok();
-}
-
-void ConfirmationManager::cancelPrompt() {
- mMutex.lock();
- mRateLimiting.cancelPrompt();
- sp<IConfirmationUI> confirmationUI = mCurrentConfirmationUI;
- mMutex.unlock();
- if (confirmationUI != nullptr) {
- confirmationUI->abort();
- }
-}
-
-// Called by keystore main thread.
-Status ConfirmationManager::isConfirmationPromptSupported(bool* aidl_return) {
- sp<IConfirmationUI> confirmationUI = IConfirmationUI::tryGetService();
- if (confirmationUI == nullptr) {
- ALOGW("Error getting confirmationUI service\n");
- *aidl_return = false;
- return Status::ok();
- }
-
- *aidl_return = true;
- return Status::ok();
-}
-
-void ConfirmationManager::finalizeTransaction(ConfirmationResponseCode responseCode,
- hidl_vec<uint8_t> dataThatWasConfirmed) {
- mMutex.lock();
- mRateLimiting.processResult(responseCode);
- sp<IBinder> listener = mCurrentListener;
- if (mCurrentListener != nullptr) {
- mCurrentListener->unlinkToDeath(mDeathRecipient);
- mCurrentListener = nullptr;
- }
- if (mCurrentConfirmationUI != nullptr) {
- mCurrentConfirmationUI->unlinkToDeath(this);
- mCurrentConfirmationUI = nullptr;
- }
- mMutex.unlock();
-
- // Deliver result to the application that started the operation.
- if (listener != nullptr) {
- sp<BpConfirmationPromptCallback> obj = new BpConfirmationPromptCallback(listener);
- Status status = obj->onConfirmationPromptCompleted(static_cast<int32_t>(responseCode),
- dataThatWasConfirmed);
- if (!status.isOk()) {
- ALOGW("Error sending onConfirmationPromptCompleted - status: %s\n",
- status.toString8().c_str());
- }
- }
-}
-
-// Called by hwbinder thread (not keystore main thread).
-Return<void> ConfirmationManager::result(ConfirmationResponseCode responseCode,
- const hidl_vec<uint8_t>& dataThatWasConfirmed,
- const hidl_vec<uint8_t>& confirmationToken) {
- finalizeTransaction(responseCode, dataThatWasConfirmed);
- lock_guard<mutex> lock(mMutex);
- mLatestConfirmationToken = confirmationToken;
- return Return<void>();
-}
-
-// Called by keystore main thread or keymaster worker
-hidl_vec<uint8_t> ConfirmationManager::getLatestConfirmationToken() {
- lock_guard<mutex> lock(mMutex);
- return mLatestConfirmationToken;
-}
-
-void ConfirmationManager::binderDied(const wp<IBinder>& who) {
- // This is also called for other binders so need to check it's for
- // us before acting on it.
- mMutex.lock();
- if (who == mCurrentListener) {
- // Clear this so we don't call back into the already dead
- // binder in finalizeTransaction().
- mCurrentListener->unlinkToDeath(mDeathRecipient);
- mCurrentListener = nullptr;
- mMutex.unlock();
- ALOGW("The process which requested the confirmation dialog died.\n");
- cancelPrompt();
- } else {
- mMutex.unlock();
- }
-}
-
-void ConfirmationManager::serviceDied(uint64_t /* cookie */,
- const wp<android::hidl::base::V1_0::IBase>& /* who */) {
- ALOGW("The ConfirmationUI HAL died.\n");
- finalizeTransaction(ConfirmationResponseCode::SystemError, {});
-}
-
-} // namespace keystore
diff --git a/keystore/confirmation_manager.h b/keystore/confirmation_manager.h
deleted file mode 100644
index 7f0a11d..0000000
--- a/keystore/confirmation_manager.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#ifndef KEYSTORE_CONFIRMATION_MANAGER_H_
-#define KEYSTORE_CONFIRMATION_MANAGER_H_
-
-#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
-#include <android/hardware/confirmationui/1.0/types.h>
-#include <binder/Binder.h>
-#include <binder/IBinder.h>
-#include <binder/Status.h>
-#include <keystore/keymaster_types.h>
-#include <map>
-#include <mutex>
-#include <utils/LruCache.h>
-#include <utils/StrongPointer.h>
-#include <vector>
-
-#include "confirmationui_rate_limiting.h"
-
-namespace keystore {
-
-using android::binder::Status;
-using android::hardware::confirmationui::V1_0::IConfirmationResultCallback;
-using ConfirmationResponseCode = android::hardware::confirmationui::V1_0::ResponseCode;
-
-class ConfirmationManager;
-
-class ConfirmationManager : public android::hardware::hidl_death_recipient,
- public IConfirmationResultCallback {
- public:
- explicit ConfirmationManager(android::IBinder::DeathRecipient* deathRecipient);
-
- // Calls into the confirmationui HAL to start a new prompt.
- //
- // Returns OperationPending if another application is already
- // showing a confirmation. Otherwise returns the return code from
- // the HAL.
- Status presentConfirmationPrompt(const android::sp<android::IBinder>& listener,
- const android::String16& promptText,
- const hidl_vec<uint8_t>& extraData,
- const android::String16& locale, int uiOptionsAsFlags,
- int32_t* aidl_return);
-
- // Calls into the confirmationui HAL to cancel displaying a
- // prompt.
- //
- // Returns OperatingPending if another application is showing a
- // confirmation. Otherwise returns the return code from the HAL.
- Status cancelConfirmationPrompt(const android::sp<android::IBinder>& listener,
- int32_t* aidl_return);
-
- // Checks if the confirmationUI HAL is available.
- Status isConfirmationPromptSupported(bool* aidl_return);
-
- // Gets the latest confirmation token received from the ConfirmationUI HAL.
- hidl_vec<uint8_t> getLatestConfirmationToken();
-
- // Called by KeyStoreService when a client binder has died.
- void binderDied(const android::wp<android::IBinder>& who);
-
- // hidl_death_recipient overrides:
- virtual void serviceDied(uint64_t cookie,
- const android::wp<android::hidl::base::V1_0::IBase>& who) override;
-
- // IConfirmationResultCallback overrides:
- android::hardware::Return<void> result(ConfirmationResponseCode responseCode,
- const hidl_vec<uint8_t>& dataThatWasConfirmed,
- const hidl_vec<uint8_t>& confirmationToken) override;
-
- private:
- friend class ConfirmationResultCallback;
-
- // Set rate limiting to not decrement on next abort and aborts
- // confirmationui.
- void cancelPrompt();
-
- void finalizeTransaction(ConfirmationResponseCode responseCode,
- hidl_vec<uint8_t> dataThatWasConfirmed);
-
- // This mutex protects all data below it.
- std::mutex mMutex;
-
- // The mCurrentListener and mCurrentConfirmationUI fields are set
- // if and only if a prompt is currently showing.
- android::sp<android::IBinder> mCurrentListener;
- android::sp<android::hardware::confirmationui::V1_0::IConfirmationUI> mCurrentConfirmationUI;
- android::IBinder::DeathRecipient* mDeathRecipient;
- hidl_vec<uint8_t> mLatestConfirmationToken;
- RateLimiting<> mRateLimiting;
-};
-
-} // namespace keystore
-
-#endif // KEYSTORE_CONFIRMATION_MANAGER_H_
diff --git a/keystore/confirmationui_rate_limiting.h b/keystore/confirmationui_rate_limiting.h
deleted file mode 100644
index 658bf41..0000000
--- a/keystore/confirmationui_rate_limiting.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
-**
-** Copyright 2018, 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.
-*/
-
-#ifndef KEYSTORE_CONFIRMATIONUI_RATE_LIMITING_H_
-#define KEYSTORE_CONFIRMATIONUI_RATE_LIMITING_H_
-
-#include <android/hardware/confirmationui/1.0/types.h>
-#include <chrono>
-#include <stdint.h>
-#include <sys/types.h>
-#include <tuple>
-#include <unordered_map>
-
-namespace keystore {
-
-using ConfirmationResponseCode = android::hardware::confirmationui::V1_0::ResponseCode;
-
-using std::chrono::duration;
-using std::chrono::time_point;
-
-template <typename Clock = std::chrono::steady_clock> class RateLimiting {
- private:
- struct Slot {
- Slot() : previous_start{}, prompt_start{}, counter(0) {}
- typename Clock::time_point previous_start;
- typename Clock::time_point prompt_start;
- uint32_t counter;
- };
-
- std::unordered_map<uid_t, Slot> slots_;
-
- uint_t latest_requester_;
-
- static std::chrono::seconds getBackoff(uint32_t counter) {
- using namespace std::chrono_literals;
- switch (counter) {
- case 0:
- case 1:
- case 2:
- return 0s;
- case 3:
- case 4:
- case 5:
- return 30s;
- default:
- return 60s * (1ULL << (counter - 6));
- }
- }
-
- public:
- // Exposes the number of used slots. This is only used by the test to verify the assumption
- // about used counter slots.
- size_t usedSlots() const { return slots_.size(); }
- void doGC() {
- using namespace std::chrono_literals;
- using std::chrono::system_clock;
- using std::chrono::time_point_cast;
- auto then = Clock::now() - 24h;
- auto iter = slots_.begin();
- while (iter != slots_.end()) {
- if (iter->second.prompt_start <= then) {
- iter = slots_.erase(iter);
- } else {
- ++iter;
- }
- }
- }
-
- bool tryPrompt(uid_t id) {
- using namespace std::chrono_literals;
- // remove slots that have not been touched in 24 hours
- doGC();
- auto& slot = slots_[id];
- auto now = Clock::now();
- if (!slot.counter || slot.prompt_start <= now - getBackoff(slot.counter)) {
- latest_requester_ = id;
- slot.counter += 1;
- slot.previous_start = slot.prompt_start;
- slot.prompt_start = now;
- return true;
- }
- return false;
- }
-
- // The app is penalized for cancelling a request. Request are rolled back only if
- // the prompt was cancelled by the system: e.g. a system error or asynchronous event.
- // When the user cancels the prompt, it is subject to rate limiting.
- static constexpr const uint_t kInvalidRequester = -1;
-
- void cancelPrompt() { latest_requester_ = kInvalidRequester; }
-
- void processResult(ConfirmationResponseCode rc) {
- if (latest_requester_ == kInvalidRequester) {
- return;
- }
- switch (rc) {
- case ConfirmationResponseCode::OK:
- // reset the counter slot
- slots_.erase(latest_requester_);
- return;
- case ConfirmationResponseCode::Canceled:
- // nothing to do here
- return;
- default:;
- }
-
- // roll back latest request
- auto& slot = slots_[latest_requester_];
- if (slot.counter <= 1) {
- slots_.erase(latest_requester_);
- return;
- }
- slot.counter -= 1;
- slot.prompt_start = slot.previous_start;
- }
-};
-
-} // namespace keystore
-
-#endif // KEYSTORE_CONFIRMATIONUI_RATE_LIMITING_H_
diff --git a/keystore/defaults.h b/keystore/defaults.h
deleted file mode 100644
index 6f7ff2d..0000000
--- a/keystore/defaults.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-
-#ifndef KEYSTORE_DEFAULTS_H_
-#define KEYSTORE_DEFAULTS_H_
-
-/*
- * These must be kept in sync with
- * frameworks/base/keystore/java/android/security/KeyPairGeneratorSpec.java
- */
-
-/* DSA */
-constexpr int32_t DSA_DEFAULT_KEY_SIZE = 1024;
-constexpr int32_t DSA_MIN_KEY_SIZE = 512;
-constexpr int32_t DSA_MAX_KEY_SIZE = 8192;
-
-/* EC */
-constexpr int32_t EC_DEFAULT_KEY_SIZE = 256;
-constexpr int32_t EC_MIN_KEY_SIZE = 192;
-constexpr int32_t EC_MAX_KEY_SIZE = 521;
-
-/* RSA */
-constexpr int32_t RSA_DEFAULT_KEY_SIZE = 2048;
-constexpr int32_t RSA_DEFAULT_EXPONENT = 0x10001;
-constexpr int32_t RSA_MIN_KEY_SIZE = 512;
-constexpr int32_t RSA_MAX_KEY_SIZE = 8192;
-
-#endif /* KEYSTORE_DEFAULTS_H_ */
diff --git a/keystore/grant_store.cpp b/keystore/grant_store.cpp
deleted file mode 100644
index 9e627ce..0000000
--- a/keystore/grant_store.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#include "grant_store.h"
-
-#include "blob.h"
-#include <algorithm>
-#include <sstream>
-
-namespace keystore {
-
-static constexpr uint64_t kInvalidGrantNo = std::numeric_limits<uint64_t>::max();
-static const char* kKeystoreGrantInfix = "_KEYSTOREGRANT_";
-static constexpr size_t kKeystoreGrantInfixLength = 15;
-
-Grant::Grant(const KeyBlobEntry& entry, const uint64_t grant_no)
- : entry_(entry), grant_no_(grant_no) {}
-
-static std::pair<uint64_t, std::string> parseGrantAlias(const std::string& grantAlias) {
- auto pos = grantAlias.rfind(kKeystoreGrantInfix);
- if (pos == std::string::npos) return {kInvalidGrantNo, ""};
- std::stringstream s(grantAlias.substr(pos + kKeystoreGrantInfixLength));
- std::string wrapped_alias = grantAlias.substr(0, pos);
- uint64_t grant_no = kInvalidGrantNo;
- s >> grant_no;
- if (s.fail() || grant_no == kInvalidGrantNo) return {kInvalidGrantNo, ""};
- return {grant_no, wrapped_alias};
-}
-
-std::string GrantStore::put(const uid_t uid, const LockedKeyBlobEntry& lockedEntry) {
- std::unique_lock<std::shared_mutex> lock(mutex_);
- std::stringstream s;
- KeyBlobEntry blobEntry = *lockedEntry;
- s << blobEntry.alias() << kKeystoreGrantInfix;
-
- std::set<Grant, std::less<>>& uid_grant_list = grants_[uid];
-
- bool success = false;
- auto iterator =
- std::find_if(uid_grant_list.begin(), uid_grant_list.end(),
- [&](const Grant& entry) { return success = entry.entry_ == blobEntry; });
- while (!success) {
- std::tie(iterator, success) = uid_grant_list.emplace(blobEntry, std::rand());
- }
- s << iterator->grant_no_;
- return s.str();
-}
-
-ReadLockedGrant GrantStore::get(const uid_t uid, const std::string& alias) const {
- std::shared_lock<std::shared_mutex> lock(mutex_);
- uint64_t grant_no;
- std::string wrappedAlias;
- std::tie(grant_no, wrappedAlias) = parseGrantAlias(alias);
- if (grant_no == kInvalidGrantNo) return {};
- auto uid_set_iter = grants_.find(uid);
- if (uid_set_iter == grants_.end()) return {};
- auto& uid_grant_list = uid_set_iter->second;
- auto grant = uid_grant_list.find(grant_no);
- if (grant == uid_grant_list.end()) return {};
- if (grant->entry_.alias() != wrappedAlias) return {};
- return {&(*grant), std::move(lock)};
-}
-
-bool GrantStore::removeByFileAlias(const uid_t granteeUid, const LockedKeyBlobEntry& lockedEntry) {
- std::unique_lock<std::shared_mutex> lock(mutex_);
- auto& uid_grant_list = grants_[granteeUid];
- for (auto i = uid_grant_list.begin(); i != uid_grant_list.end(); ++i) {
- if (i->entry_ == *lockedEntry) {
- uid_grant_list.erase(i);
- return true;
- }
- }
- return false;
-}
-
-void GrantStore::removeAllGrantsToKey(const uid_t granterUid, const std::string& alias) {
- std::unique_lock<std::shared_mutex> lock(mutex_);
- for (auto& uid_grant_list : grants_) {
- for (auto i = uid_grant_list.second.begin(); i != uid_grant_list.second.end(); ++i) {
- if (i->entry_.alias() == alias && i->entry_.uid() == granterUid) {
- uid_grant_list.second.erase(i);
- break;
- }
- }
- }
-}
-
-void GrantStore::removeAllGrantsToUid(const uid_t granteeUid) {
- std::unique_lock<std::shared_mutex> lock(mutex_);
- grants_.erase(granteeUid);
-}
-
-} // namespace keystore
diff --git a/keystore/grant_store.h b/keystore/grant_store.h
deleted file mode 100644
index 1baf32c..0000000
--- a/keystore/grant_store.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#ifndef KEYSTORE_GRANT_STORE_H_
-#define KEYSTORE_GRANT_STORE_H_
-
-#include <mutex>
-#include <set>
-#include <shared_mutex>
-#include <string>
-#include <unordered_map>
-
-#include <keystore/keystore_concurrency.h>
-
-#include "blob.h"
-
-namespace keystore {
-
-class Grant;
-
-using ReadLockedGrant =
- ProxyLock<MutexProxyLockHelper<const Grant, std::shared_mutex, std::shared_lock>>;
-
-/**
- * Grant represents a mapping from an alias to a key file.
- * Normally, key file names are derived from the alias chosen by the client
- * and the clients UID, to generate a per client name space.
- * Grants allow assotiating a key file with a new name, thereby making
- * it visible in another client's - the grantee's - namespace.
- */
-class Grant {
-public:
- Grant(const KeyBlobEntry& entry, const uint64_t grant_no);
- KeyBlobEntry entry_;
-
- uint64_t grant_no_; ///< numeric grant identifier - randomly assigned
-
- // NOLINTNEXTLINE(google-explicit-constructor)
- operator const uint64_t&() const { return grant_no_; }
-};
-
-/**
- * The GrantStore holds a set of sets of Grants. One set of Grants for each grantee.
- * The uid parameter to each of the GrantStore function determines the grantee's
- * name space. The methods put, get, and removeByAlias/ByFileName create, lookup, and
- * remove a Grant, respectively.
- * put also returns a new alias for the newly granted key which has to be returned
- * to the granter. The grantee, and only the grantee, can use the granted key
- * by this new alias.
- */
-class GrantStore {
-public:
- GrantStore() : grants_() {}
- std::string put(const uid_t uid, const LockedKeyBlobEntry& blobfile);
- ReadLockedGrant get(const uid_t uid, const std::string& alias) const;
- bool removeByFileAlias(const uid_t granteeUid, const LockedKeyBlobEntry& lockedEntry);
- void removeAllGrantsToKey(const uid_t granterUid, const std::string& alias);
- void removeAllGrantsToUid(const uid_t granteeUid);
-
- // GrantStore is neither copyable nor movable.
- GrantStore(const GrantStore&) = delete;
- GrantStore& operator=(const GrantStore&) = delete;
-private:
- std::unordered_map<uid_t, std::set<Grant, std::less<>>> grants_;
- mutable std::shared_mutex mutex_;
-};
-
-} // namespace keystore
-
-#endif // KEYSTORE_GRANT_STORE_H_
diff --git a/keystore/key_attestation_log_handler.cpp b/keystore/key_attestation_log_handler.cpp
deleted file mode 100644
index c3278cb..0000000
--- a/keystore/key_attestation_log_handler.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-#include <statslog.h>
-namespace keystore {
-
-void logKeystoreKeyAttestationEvent(bool wasSuccessful, int32_t errorCode) {
- // Due to a requirement in stats-write() method, the optional fields
- // which are not required for attestation logging, are marked with -1 for
- // non-repeated fields and 0 for repeated fields.
- android::util::stats_write(android::util::KEYSTORE_KEY_EVENT_REPORTED, -1, -1, -1, -1, -1, 0, 0,
- 0, 0, -1, -1,
- android::util::KEYSTORE_KEY_EVENT_REPORTED__TYPE__KEY_ATTESTATION,
- wasSuccessful, errorCode);
-}
-
-} // namespace keystore
\ No newline at end of file
diff --git a/keystore/key_attestation_log_handler.h b/keystore/key_attestation_log_handler.h
deleted file mode 100644
index a418bfa..0000000
--- a/keystore/key_attestation_log_handler.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#ifndef _KEY_ATTESTATION_LOG_HANDLER_H_
-#define _KEY_ATTESTATION_LOG_HANDLER_H_
-
-namespace keystore {
-
-void logKeystoreKeyAttestationEvent(bool wasSuccessful, int32_t errorCode);
-
-}
-
-#endif //_KEY_ATTESTATION_LOG_HANDLER_H_
diff --git a/keystore/key_creation_log_handler.cpp b/keystore/key_creation_log_handler.cpp
deleted file mode 100644
index d846257..0000000
--- a/keystore/key_creation_log_handler.cpp
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-#define LOG_TAG "KeystoreOperation"
-
-#include "key_creation_log_handler.h"
-#include <statslog.h>
-
-namespace keystore {
-
-template <typename Tag>
-int32_t getEnumTagValue(const AuthorizationSet& authorization_set, Tag tag) {
- auto tagValue = authorization_set.GetTagValue(tag);
- if (tagValue.isOk()) {
- static_assert(sizeof(decltype(tagValue.value())) <= sizeof(int32_t),
- "Tag type value will be truncated, if cast to int32_t");
- return static_cast<int32_t>(tagValue.value());
- }
- // Usually, if the value is not present, 0 is set. However, since 0 is a valid
- // enum value, -1 is set for single enum fields.
- return -1;
-}
-
-int32_t generateBitMapForPaddingModeValues(const AuthorizationSet& authorization_set) {
- int32_t bitMap = 0;
- int32_t tagValueCount = authorization_set.GetTagCount(TAG_PADDING);
- if (tagValueCount == 0) {
- // unlike in the single enum fields, if no value is provided,
- // 0 is set for the bitmap
- return bitMap;
- }
- int current_offset = -1;
- while (tagValueCount > 0) {
- current_offset = authorization_set.find(TAG_PADDING, current_offset);
- KeyParameter keyParam = authorization_set[current_offset];
- auto tagValue = accessTagValue(TAG_PADDING, keyParam);
- switch (tagValue) {
- case PaddingMode::NONE:
- bitMap |= (1 << NONE_BIT_POS);
- break;
- case PaddingMode::RSA_OAEP:
- bitMap |= (1 << PaddingModeBitPosition::RSA_OAEP_BIT_POS);
- break;
- case PaddingMode::RSA_PSS:
- bitMap |= (1 << PaddingModeBitPosition::RSA_PSS_BIT_POS);
- break;
- case PaddingMode::RSA_PKCS1_1_5_ENCRYPT:
- bitMap |= (1 << PaddingModeBitPosition::RSA_PKCS1_1_5_ENCRYPT_BIT_POS);
- break;
- case PaddingMode::RSA_PKCS1_1_5_SIGN:
- bitMap |= (1 << PaddingModeBitPosition::RSA_PKCS1_1_5_SIGN_BIT_POS);
- break;
- case PaddingMode::PKCS7:
- bitMap |= (1 << PaddingModeBitPosition::PKCS7_BIT_POS);
- break;
- default:
- break;
- }
- tagValueCount -= 1;
- }
- return bitMap;
-}
-
-int32_t generateBitMapForDigestValues(const AuthorizationSet& authorization_set) {
- int32_t bitMap = 0;
- int32_t tagValueCount = authorization_set.GetTagCount(TAG_DIGEST);
- if (tagValueCount == 0) {
- // unlike in the single enum fields, if no value is provided,
- // 0 is set for the bitmap
- return bitMap;
- }
- int current_offset = -1;
- while (tagValueCount > 0) {
- current_offset = authorization_set.find(TAG_DIGEST, current_offset);
- KeyParameter keyParam = authorization_set[current_offset];
- auto tagValue = accessTagValue(TAG_DIGEST, keyParam);
- switch (tagValue) {
- case Digest::NONE:
- bitMap |= (1 << NONE_BIT_POS);
- break;
- case Digest::MD5:
- bitMap |= (1 << DigestBitPosition::MD5_BIT_POS);
- break;
- case Digest::SHA1:
- bitMap |= (1 << DigestBitPosition::SHA1_BIT_POS);
- break;
- case Digest::SHA_2_224:
- bitMap |= (1 << DigestBitPosition::SHA_2_224_BIT_POS);
- break;
- case Digest::SHA_2_256:
- bitMap |= (1 << DigestBitPosition::SHA_2_256_BIT_POS);
- break;
- case Digest::SHA_2_384:
- bitMap |= (1 << DigestBitPosition::SHA_2_384_BIT_POS);
- break;
- case Digest::SHA_2_512:
- bitMap |= (1 << DigestBitPosition::SHA_2_512_BIT_POS);
- break;
- default:
- break;
- }
- tagValueCount -= 1;
- }
- return bitMap;
-}
-
-int32_t generateBitMapForBlockModeValues(const AuthorizationSet& authorization_set) {
- int32_t bitMap = 0;
- int32_t tagValueCount = authorization_set.GetTagCount(TAG_BLOCK_MODE);
- if (tagValueCount == 0) {
- // unlike in the single enum fields, if no value is provided,
- // 0 is set for the bitmap
- return bitMap;
- }
- int current_offset = -1;
- while (tagValueCount > 0) {
- current_offset = authorization_set.find(TAG_BLOCK_MODE, current_offset);
- KeyParameter keyParam = authorization_set[current_offset];
- auto tagValue = accessTagValue(TAG_BLOCK_MODE, keyParam);
- switch (tagValue) {
- case BlockMode::ECB:
- bitMap |= (1 << BlockModeBitPosition::ECB_BIT_POS);
- break;
- case BlockMode::CBC:
- bitMap |= (1 << BlockModeBitPosition::CBC_BIT_POS);
- break;
- case BlockMode::CTR:
- bitMap |= (1 << BlockModeBitPosition::CTR_BIT_POS);
- break;
- case BlockMode::GCM:
- bitMap |= (1 << BlockModeBitPosition::GCM_BIT_POS);
- break;
- default:
- break;
- }
- tagValueCount -= 1;
- }
- return bitMap;
-}
-
-int32_t generateBitMapForKeyPurposeValues(const AuthorizationSet& authorization_set) {
- int32_t bitMap = 0;
- int32_t tagValueCount = authorization_set.GetTagCount(TAG_PURPOSE);
- if (tagValueCount == 0) {
- // unlike in the single enum fields, if no value is provided,
- // 0 is set for the bitmap
- return bitMap;
- }
- int current_offset = -1;
- while (tagValueCount > 0) {
- current_offset = authorization_set.find(TAG_PURPOSE, current_offset);
- KeyParameter keyParam = authorization_set[current_offset];
- auto tagValue = accessTagValue(TAG_PURPOSE, keyParam);
- switch (tagValue) {
- case KeyPurpose::ENCRYPT:
- bitMap |= (1 << KeyPurposeBitPosition::ENCRYPT_BIT_POS);
- break;
- case KeyPurpose::DECRYPT:
- bitMap |= (1 << KeyPurposeBitPosition::DECRYPT_BIT_POS);
- break;
- case KeyPurpose::SIGN:
- bitMap |= (1 << KeyPurposeBitPosition::SIGN_BIT_POS);
- break;
- case KeyPurpose::VERIFY:
- bitMap |= (1 << KeyPurposeBitPosition::VERIFY_BIT_POS);
- break;
- case KeyPurpose::WRAP_KEY:
- bitMap |= (1 << KeyPurposeBitPosition::WRAP_KEY_BIT_POS);
- break;
- default:
- break;
- }
- tagValueCount -= 1;
- }
- return bitMap;
-}
-
-void logKeystoreKeyCreationEvent(const hidl_vec<KeyParameter>& keyParams,
- bool wasCreationSuccessful, int32_t errorCode) {
- AuthorizationSet authorization_set(keyParams);
- authorization_set.Deduplicate();
-
- android::util::stats_write(android::util::KEYSTORE_KEY_EVENT_REPORTED,
- getEnumTagValue(authorization_set, TAG_ALGORITHM),
- getEnumTagValue(authorization_set, TAG_KEY_SIZE),
- getEnumTagValue(authorization_set, TAG_ORIGIN),
- getEnumTagValue(authorization_set, TAG_USER_AUTH_TYPE),
- getEnumTagValue(authorization_set, TAG_AUTH_TIMEOUT),
- generateBitMapForPaddingModeValues(authorization_set),
- generateBitMapForDigestValues(authorization_set),
- generateBitMapForBlockModeValues(authorization_set),
- generateBitMapForKeyPurposeValues(authorization_set),
- getEnumTagValue(authorization_set, TAG_EC_CURVE),
- getEnumTagValue(authorization_set, TAG_BLOB_USAGE_REQUIREMENTS),
- android::util::KEYSTORE_KEY_EVENT_REPORTED__TYPE__KEY_CREATION,
- wasCreationSuccessful, errorCode);
-}
-
-} // namespace keystore
diff --git a/keystore/key_creation_log_handler.h b/keystore/key_creation_log_handler.h
deleted file mode 100644
index a314eb1..0000000
--- a/keystore/key_creation_log_handler.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#ifndef KEY_CREATION_LOG_HANDLER_H_
-#define KEY_CREATION_LOG_HANDLER_H_
-
-#include <keystore/keystore_hidl_support.h>
-
-namespace keystore {
-
-/**
- * Following enums are defined as a part of the workaround to log the repeated
- * values of ENUM_REP type. The workaround is to represent the repeated values
- * of ENUM_REP type as a bitmap and the following enums define their positions
- * in the bitmap.
- */
-
-enum PaddingModeBitPosition : int32_t {
- RSA_OAEP_BIT_POS = 1,
- RSA_PSS_BIT_POS = 2,
- RSA_PKCS1_1_5_ENCRYPT_BIT_POS = 3,
- RSA_PKCS1_1_5_SIGN_BIT_POS = 4,
- PKCS7_BIT_POS = 5,
-};
-
-enum DigestBitPosition : int32_t {
- MD5_BIT_POS = 1,
- SHA1_BIT_POS = 2,
- SHA_2_224_BIT_POS = 3,
- SHA_2_256_BIT_POS = 4,
- SHA_2_384_BIT_POS = 5,
- SHA_2_512_BIT_POS = 6,
-};
-
-enum BlockModeBitPosition : int32_t {
- ECB_BIT_POS = 1,
- CBC_BIT_POS = 2,
- CTR_BIT_POS = 3,
- GCM_BIT_POS = 4,
-};
-
-enum KeyPurposeBitPosition : int32_t {
- ENCRYPT_BIT_POS = 1,
- DECRYPT_BIT_POS = 2,
- SIGN_BIT_POS = 3,
- VERIFY_BIT_POS = 4,
- WRAP_KEY_BIT_POS = 5,
-};
-
-// None is an enum value for digest and a deprecated value for padding mode
-const int32_t NONE_BIT_POS = 0;
-
-void logKeystoreKeyCreationEvent(const hidl_vec<KeyParameter>& keyParams,
- bool wasCreationSuccessful, int32_t errorCode);
-
-} // namespace keystore
-
-#endif // KEY_CREATION_LOG_HANDLER_H_
diff --git a/keystore/key_operation_log_handler.cpp b/keystore/key_operation_log_handler.cpp
deleted file mode 100644
index e7f4345..0000000
--- a/keystore/key_operation_log_handler.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-#define LOG_TAG "KeystoreOperation"
-
-#include "key_operation_log_handler.h"
-#include "key_creation_log_handler.h"
-
-#include <keystore/keystore_hidl_support.h>
-#include <statslog.h>
-
-namespace keystore {
-
-template <typename Tag>
-int32_t getOptionalEnumTagValue(const AuthorizationSet& authorization_set, Tag tag) {
- auto tagValue = authorization_set.GetTagValue(tag);
- if (tagValue.isOk()) {
- static_assert(sizeof(decltype(tagValue.value())) <= sizeof(int32_t),
- "Tag type value will be truncated, if cast to int32_t");
- return static_cast<int32_t>(tagValue.value());
- }
- //-1 is an invalid value for all enum types.
- return -1;
-}
-
-int32_t generateBitMapForPaddingModeValue(const AuthorizationSet& authorization_set) {
- auto tagValue = authorization_set.GetTagValue(TAG_PADDING);
- if (tagValue.isOk()) {
- auto value = tagValue.value();
- switch (value) {
- case PaddingMode::NONE:
- return (1 << NONE_BIT_POS);
- case PaddingMode::RSA_OAEP:
- return (1 << PaddingModeBitPosition::RSA_OAEP_BIT_POS);
- case PaddingMode::RSA_PSS:
- return (1 << PaddingModeBitPosition::RSA_PSS_BIT_POS);
- case PaddingMode::RSA_PKCS1_1_5_ENCRYPT:
- return (1 << PaddingModeBitPosition::RSA_PKCS1_1_5_ENCRYPT_BIT_POS);
- case PaddingMode::RSA_PKCS1_1_5_SIGN:
- return (1 << PaddingModeBitPosition::RSA_PKCS1_1_5_SIGN_BIT_POS);
- case PaddingMode::PKCS7:
- return (1 << PaddingModeBitPosition::PKCS7_BIT_POS);
- default:
- break;
- }
- }
- // unlike in the single enum fields, if no value is provided,
- // 0 is set for the bitmap
- return 0;
-}
-
-int32_t generateBitMapForDigestValue(const AuthorizationSet& authorization_set) {
- auto tagValue = authorization_set.GetTagValue(TAG_DIGEST);
- if (tagValue.isOk()) {
- auto value = tagValue.value();
- switch (value) {
- case Digest::NONE:
- return (1 << NONE_BIT_POS);
- case Digest::MD5:
- return (1 << DigestBitPosition::MD5_BIT_POS);
- case Digest::SHA1:
- return (1 << DigestBitPosition::SHA1_BIT_POS);
- case Digest::SHA_2_224:
- return (1 << DigestBitPosition::SHA_2_224_BIT_POS);
- case Digest::SHA_2_256:
- return (1 << DigestBitPosition::SHA_2_256_BIT_POS);
- case Digest::SHA_2_384:
- return (1 << DigestBitPosition::SHA_2_384_BIT_POS);
- case Digest::SHA_2_512:
- return (1 << DigestBitPosition::SHA_2_512_BIT_POS);
- default:
- break;
- }
- }
- // unlike in the single enum fields, if no value is provided,
- // 0 is set for the bitmap
- return 0;
-}
-
-int32_t generateBitMapForBlockModeValue(const AuthorizationSet& authorization_set) {
- auto tagValue = authorization_set.GetTagValue(TAG_BLOCK_MODE);
- if (tagValue.isOk()) {
- auto value = tagValue.value();
- switch (value) {
- case BlockMode::ECB:
- return (1 << BlockModeBitPosition::ECB_BIT_POS);
- case BlockMode::CBC:
- return (1 << BlockModeBitPosition::CBC_BIT_POS);
- case BlockMode::CTR:
- return (1 << BlockModeBitPosition::CTR_BIT_POS);
- case BlockMode::GCM:
- return (1 << BlockModeBitPosition::GCM_BIT_POS);
- default:
- break;
- }
- }
- // unlike in the single enum fields, if no value is provided,
- // 0 is set for the bitmap
- return 0;
-}
-
-void logKeystoreKeyOperationEvent(const Operation& op, bool wasOperationSuccessful,
- int32_t responseCode) {
- AuthorizationSet authorization_set(op.characteristics.softwareEnforced);
- authorization_set.Union(op.characteristics.hardwareEnforced);
- AuthorizationSet operation_params(op.params);
-
- android::util::stats_write(
- android::util::KEYSTORE_KEY_EVENT_REPORTED,
- getOptionalEnumTagValue(authorization_set, TAG_ALGORITHM),
- getOptionalEnumTagValue(authorization_set, TAG_KEY_SIZE),
- getOptionalEnumTagValue(authorization_set, TAG_ORIGIN),
- getOptionalEnumTagValue(authorization_set, TAG_USER_AUTH_TYPE),
- getOptionalEnumTagValue(authorization_set, TAG_AUTH_TIMEOUT),
- generateBitMapForPaddingModeValue(operation_params),
- generateBitMapForDigestValue(operation_params),
- generateBitMapForBlockModeValue(operation_params), static_cast<int32_t>(op.purpose),
- getOptionalEnumTagValue(authorization_set, TAG_EC_CURVE),
- getOptionalEnumTagValue(authorization_set, TAG_BLOB_USAGE_REQUIREMENTS),
- android::util::KEYSTORE_KEY_EVENT_REPORTED__TYPE__KEY_OPERATION, wasOperationSuccessful,
- responseCode);
-}
-
-} // namespace keystore
\ No newline at end of file
diff --git a/keystore/key_operation_log_handler.h b/keystore/key_operation_log_handler.h
deleted file mode 100644
index ba27747..0000000
--- a/keystore/key_operation_log_handler.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#ifndef KEY_OPERATION_LOG_HANDLER_H_
-#define KEY_OPERATION_LOG_HANDLER_H_
-
-#include "operation_struct.h"
-
-namespace keystore {
-
-void logKeystoreKeyOperationEvent(const Operation& op, bool wasSuccessful, int32_t errorCode);
-
-} // namespace keystore
-
-#endif // KEY_OPERATION_LOG_HANDLER_H_
\ No newline at end of file
diff --git a/keystore/key_store_service.cpp b/keystore/key_store_service.cpp
deleted file mode 100644
index 4e5bc48..0000000
--- a/keystore/key_store_service.cpp
+++ /dev/null
@@ -1,1480 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#define LOG_TAG "keystore"
-
-#include "key_store_service.h"
-
-#include <fcntl.h>
-#include <sys/stat.h>
-
-#include <algorithm>
-#include <atomic>
-#include <sstream>
-
-#include <android-base/scopeguard.h>
-#include <binder/IInterface.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IPermissionController.h>
-#include <binder/IServiceManager.h>
-#include <cutils/multiuser.h>
-#include <log/log_event_list.h>
-
-#include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
-
-#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
-#include <android/hardware/keymaster/3.0/IHwKeymasterDevice.h>
-#include <keymasterV4_0/keymaster_utils.h>
-
-#include "defaults.h"
-#include "key_attestation_log_handler.h"
-#include "keystore_keymaster_enforcement.h"
-#include "keystore_utils.h"
-#include <keystore/keystore_attestation_id.h>
-#include <keystore/keystore_hidl_support.h>
-#include <keystore/keystore_return_types.h>
-
-#include <hardware/hw_auth_token.h>
-
-namespace keystore {
-
-using namespace android;
-
-namespace {
-
-using ::android::binder::Status;
-using android::hardware::keymaster::V4_0::support::authToken2HidlVec;
-using android::hardware::keymaster::V4_0::support::serializeVerificationToken;
-using android::security::keymaster::ExportResult;
-using android::security::keymaster::KeymasterArguments;
-using android::security::keymaster::KeymasterBlob;
-using android::security::keymaster::KeymasterCertificateChain;
-using android::security::keymaster::operationFailed;
-using android::security::keymaster::OperationResult;
-using ConfirmationResponseCode = android::hardware::confirmationui::V1_0::ResponseCode;
-using ::android::security::keystore::ICredstoreTokenCallback;
-using ::android::security::keystore::IKeystoreOperationResultCallback;
-using ::android::security::keystore::IKeystoreResponseCallback;
-using ::android::security::keystore::KeystoreResponse;
-
-constexpr double kIdRotationPeriod = 30 * 24 * 60 * 60; /* Thirty days, in seconds */
-const char* kTimestampFilePath = "timestamp";
-
-bool containsTag(const hidl_vec<KeyParameter>& params, Tag tag) {
- return params.end() !=
- std::find_if(params.begin(), params.end(),
- [&](const KeyParameter& param) { return param.tag == tag; });
-}
-
-#define AIDL_RETURN(rc) (*_aidl_return = KeyStoreServiceReturnCode(rc).getErrorCode(), Status::ok())
-
-std::pair<KeyStoreServiceReturnCode, bool> hadFactoryResetSinceIdRotation() {
- struct stat sbuf;
- if (stat(kTimestampFilePath, &sbuf) == 0) {
- double diff_secs = difftime(time(nullptr), sbuf.st_ctime);
- return {ResponseCode::NO_ERROR, diff_secs < kIdRotationPeriod};
- }
-
- if (errno != ENOENT) {
- ALOGE("Failed to stat \"timestamp\" file, with error %d", errno);
- return {ResponseCode::SYSTEM_ERROR, false /* don't care */};
- }
-
- int fd = creat(kTimestampFilePath, 0600);
- if (fd < 0) {
- ALOGE("Couldn't create \"timestamp\" file, with error %d", errno);
- return {ResponseCode::SYSTEM_ERROR, false /* don't care */};
- }
-
- if (close(fd)) {
- ALOGE("Couldn't close \"timestamp\" file, with error %d", errno);
- return {ResponseCode::SYSTEM_ERROR, false /* don't care */};
- }
-
- return {ResponseCode::NO_ERROR, true};
-}
-
-using ::android::security::KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE;
-
-KeyStoreServiceReturnCode updateParamsForAttestation(uid_t callingUid, AuthorizationSet* params) {
- KeyStoreServiceReturnCode responseCode;
- bool factoryResetSinceIdRotation;
- std::tie(responseCode, factoryResetSinceIdRotation) = hadFactoryResetSinceIdRotation();
-
- if (!responseCode.isOk()) return responseCode;
- if (factoryResetSinceIdRotation) params->push_back(TAG_RESET_SINCE_ID_ROTATION);
-
- auto asn1_attestation_id_result = security::gather_attestation_application_id(callingUid);
- if (!asn1_attestation_id_result.isOk()) {
- ALOGE("failed to gather attestation_id");
- // Couldn't get attestation ID; just use an empty one rather than failing.
- asn1_attestation_id_result = std::vector<uint8_t>();
- }
- std::vector<uint8_t>& asn1_attestation_id = asn1_attestation_id_result;
-
- /*
- * The attestation application ID must not be longer than
- * KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE, error out if gather_attestation_application_id
- * returned such an invalid vector.
- */
- if (asn1_attestation_id.size() > KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE) {
- ALOGE("BUG: Gathered Attestation Application ID is too big (%d)",
- static_cast<int32_t>(asn1_attestation_id.size()));
- return ErrorCode::CANNOT_ATTEST_IDS;
- }
-
- params->push_back(TAG_ATTESTATION_APPLICATION_ID, asn1_attestation_id);
-
- return ResponseCode::NO_ERROR;
-}
-
-} // anonymous namespace
-
-Status KeyStoreService::getState(int32_t userId, int32_t* aidl_return) {
- if (!checkBinderPermission(P_GET_STATE)) {
- *aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
- return Status::ok();
- }
- *aidl_return = mKeyStore->getState(userId);
- return Status::ok();
-}
-
-Status KeyStoreService::get(const String16& name, int32_t uid, ::std::vector<uint8_t>* item) {
- uid_t targetUid = getEffectiveUid(uid);
- if (!checkBinderPermission(P_GET, targetUid)) {
- // see keystore/keystore.h
- return Status::fromServiceSpecificError(
- static_cast<int32_t>(ResponseCode::PERMISSION_DENIED));
- }
-
- String8 name8(name);
- ResponseCode rc;
- Blob keyBlob;
- Blob charBlob;
- LockedKeyBlobEntry lockedEntry;
-
- std::tie(rc, keyBlob, charBlob, lockedEntry) =
- mKeyStore->getKeyForName(name8, targetUid, TYPE_GENERIC);
- if (rc != ResponseCode::NO_ERROR) {
- *item = ::std::vector<uint8_t>();
- // Return empty array if key is not found
- // TODO: consider having returned value nullable or parse exception on the client.
- return Status::fromServiceSpecificError(static_cast<int32_t>(rc));
- }
- auto resultBlob = blob2hidlVec(keyBlob);
- // The static_cast here is needed to prevent a move, forcing a deep copy.
- if (item) *item = static_cast<const hidl_vec<uint8_t>&>(blob2hidlVec(keyBlob));
- return Status::ok();
-}
-
-Status KeyStoreService::insert(const String16& name, const ::std::vector<uint8_t>& item,
- int targetUid, int32_t flags, int32_t* aidl_return) {
- targetUid = getEffectiveUid(targetUid);
- KeyStoreServiceReturnCode result =
- checkBinderPermissionAndKeystoreState(P_INSERT, targetUid, flags & KEYSTORE_FLAG_ENCRYPTED);
- if (!result.isOk()) {
- *aidl_return = result.getErrorCode();
- return Status::ok();
- }
-
- String8 name8(name);
- auto lockedEntry = mKeyStore->getLockedBlobEntryIfNotExists(name8.string(), targetUid);
-
- if (!lockedEntry) {
- ALOGE("failed to grab lock on blob entry %u_%s", targetUid, name8.string());
- *aidl_return = static_cast<int32_t>(ResponseCode::KEY_ALREADY_EXISTS);
- return Status::ok();
- }
-
- Blob keyBlob(&item[0], item.size(), nullptr, 0, ::TYPE_GENERIC);
- keyBlob.setEncrypted(flags & KEYSTORE_FLAG_ENCRYPTED);
-
- *aidl_return = static_cast<int32_t>(mKeyStore->put(lockedEntry, keyBlob, {}));
- return Status::ok();
-}
-
-Status KeyStoreService::del(const String16& name, int targetUid, int32_t* aidl_return) {
- targetUid = getEffectiveUid(targetUid);
- if (!checkBinderPermission(P_DELETE, targetUid)) {
- *aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
- return Status::ok();
- }
- String8 name8(name);
- ALOGI("del %s %d", name8.string(), targetUid);
- auto lockedEntry = mKeyStore->getLockedBlobEntryIfExists(name8.string(), targetUid);
- if (!lockedEntry) {
- *aidl_return = static_cast<int32_t>(ResponseCode::KEY_NOT_FOUND);
- return Status::ok();
- }
-
- ResponseCode result = mKeyStore->del(lockedEntry);
-
- *aidl_return = static_cast<int32_t>(result);
- return Status::ok();
-}
-
-Status KeyStoreService::exist(const String16& name, int targetUid, int32_t* aidl_return) {
- targetUid = getEffectiveUid(targetUid);
- if (!checkBinderPermission(P_EXIST, targetUid)) {
- *aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
- return Status::ok();
- }
-
- LockedKeyBlobEntry lockedEntry =
- mKeyStore->getLockedBlobEntryIfExists(String8(name).string(), targetUid);
- *aidl_return =
- static_cast<int32_t>(lockedEntry ? ResponseCode::NO_ERROR : ResponseCode::KEY_NOT_FOUND);
- return Status::ok();
-}
-
-Status KeyStoreService::list(const String16& prefix, int32_t targetUid,
- ::std::vector<::android::String16>* matches) {
- targetUid = getEffectiveUid(targetUid);
- if (!checkBinderPermission(P_LIST, targetUid)) {
- return Status::fromServiceSpecificError(
- static_cast<int32_t>(ResponseCode::PERMISSION_DENIED));
- }
- const String8 prefix8(prefix);
- const std::string stdPrefix(prefix8.string());
-
- ResponseCode rc;
- std::list<LockedKeyBlobEntry> internal_matches;
- auto userDirName = mKeyStore->getUserStateDB().getUserStateByUid(targetUid)->getUserDirName();
-
- std::tie(rc, internal_matches) =
- LockedKeyBlobEntry::list(userDirName, [&](uid_t uid, const std::string& alias) {
- std::mismatch(stdPrefix.begin(), stdPrefix.end(), alias.begin(), alias.end());
- return uid == static_cast<uid_t>(targetUid) &&
- std::mismatch(stdPrefix.begin(), stdPrefix.end(), alias.begin(), alias.end())
- .first == stdPrefix.end();
- });
-
- if (rc != ResponseCode::NO_ERROR) {
- return Status::fromServiceSpecificError(static_cast<int32_t>(rc));
- }
-
- for (LockedKeyBlobEntry& entry : internal_matches) {
- matches->push_back(String16(entry->alias().substr(prefix8.size()).c_str()));
- }
- return Status::ok();
-}
-
-/*
- * This method will return the uids of all auth bound keys for the calling user.
- * This is intended to be used for alerting the user about which apps will be affected
- * if the password/pin is removed. Only allowed to be called by system.
- * The output is bound by the initial size of uidsOut to be compatible with Java.
- */
-Status KeyStoreService::listUidsOfAuthBoundKeys(std::vector<std::string>* uidsOut,
- int32_t* aidl_return) {
- const int32_t callingUid = IPCThreadState::self()->getCallingUid();
- const int32_t userId = get_user_id(callingUid);
- const int32_t appId = get_app_id(callingUid);
- if (appId != AID_SYSTEM) {
- ALOGE("Permission listUidsOfAuthBoundKeys denied for aid %d", appId);
- *aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
- return Status::ok();
- }
-
- const String8 prefix8("");
- auto userState = mKeyStore->getUserStateDB().getUserState(userId);
- const std::string userDirName = userState->getUserDirName();
- auto encryptionKey = userState->getEncryptionKey();
- auto state = userState->getState();
- // unlock the user state
- userState = {};
-
- ResponseCode rc;
- std::list<LockedKeyBlobEntry> internal_matches;
- std::tie(rc, internal_matches) =
- LockedKeyBlobEntry::list(userDirName, [&](uid_t, const std::string&) {
- // Need to filter on auth bound state, so just return true.
- return true;
- });
- if (rc != ResponseCode::NO_ERROR) {
- ALOGE("Error listing blob entries for user %d", userId);
- return Status::fromServiceSpecificError(static_cast<int32_t>(rc));
- }
-
- for (LockedKeyBlobEntry& entry : internal_matches) {
- // Need to store uids as a list of strings because integer list output
- // parameters is not supported in aidl-cpp.
- std::string entryUid = std::to_string(entry->uid());
- if (std::find(uidsOut->begin(), uidsOut->end(), entryUid) != uidsOut->end()) {
- // uid already in list, skip
- continue;
- }
-
- auto [rc, blob, charBlob] = entry.readBlobs(encryptionKey, state);
- if (rc != ResponseCode::NO_ERROR && rc != ResponseCode::LOCKED) {
- ALOGE("Error reading blob for key %s", entry->alias().c_str());
- continue;
- }
-
- if (blob && blob.isEncrypted()) {
- uidsOut->push_back(entryUid);
- } else if (charBlob) {
- auto [success, hwEnforced, swEnforced] = charBlob.getKeyCharacteristics();
- if (!success) {
- ALOGE("Error reading blob characteristics for key %s", entry->alias().c_str());
- continue;
- }
- if (hwEnforced.Contains(TAG_USER_SECURE_ID) ||
- swEnforced.Contains(TAG_USER_SECURE_ID)) {
- uidsOut->push_back(entryUid);
- }
- }
- }
- *aidl_return = static_cast<int32_t>(ResponseCode::NO_ERROR);
- return Status::ok();
-}
-
-Status KeyStoreService::onUserPasswordChanged(int32_t userId, const String16& password,
- int32_t* aidl_return) {
- if (!checkBinderPermission(P_PASSWORD)) {
- *aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
- return Status::ok();
- }
-
- if (password.size() == 0) {
- ALOGI("Secure lockscreen for user %d removed, deleting encrypted entries", userId);
- mKeyStore->resetUser(userId, true);
- *aidl_return = static_cast<int32_t>(ResponseCode::NO_ERROR);
- return Status::ok();
- } else {
- const String8 password8(password);
- switch (mKeyStore->getState(userId)) {
- case ::STATE_UNINITIALIZED: {
- // generate master key, encrypt with password, write to file,
- // initialize mMasterKey*.
- *aidl_return = static_cast<int32_t>(mKeyStore->initializeUser(password8, userId));
- return Status::ok();
- }
- case ::STATE_NO_ERROR: {
- // rewrite master key with new password.
- *aidl_return = static_cast<int32_t>(mKeyStore->writeMasterKey(password8, userId));
- return Status::ok();
- }
- case ::STATE_LOCKED: {
- ALOGE("Changing user %d's password while locked, clearing old encryption", userId);
- mKeyStore->resetUser(userId, true);
- *aidl_return = static_cast<int32_t>(mKeyStore->initializeUser(password8, userId));
- return Status::ok();
- }
- }
- *aidl_return = static_cast<int32_t>(ResponseCode::SYSTEM_ERROR);
- return Status::ok();
- }
-}
-
-Status KeyStoreService::onUserAdded(int32_t userId, int32_t parentId, int32_t* aidl_return) {
- if (!checkBinderPermission(P_USER_CHANGED)) {
- *aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
- return Status::ok();
- }
-
- // Sanity check that the new user has an empty keystore.
- if (!mKeyStore->isEmpty(userId)) {
- ALOGW("New user %d's keystore not empty. Clearing old entries.", userId);
- }
- // Unconditionally clear the keystore, just to be safe.
- mKeyStore->resetUser(userId, false);
- if (parentId != -1) {
- // This profile must share the same master key password as the parent profile. Because the
- // password of the parent profile is not known here, the best we can do is copy the parent's
- // master key and master key file. This makes this profile use the same master key as the
- // parent profile, forever.
- *aidl_return = static_cast<int32_t>(mKeyStore->copyMasterKey(parentId, userId));
- return Status::ok();
- } else {
- *aidl_return = static_cast<int32_t>(ResponseCode::NO_ERROR);
- return Status::ok();
- }
-}
-
-Status KeyStoreService::onUserRemoved(int32_t userId, int32_t* aidl_return) {
- if (!checkBinderPermission(P_USER_CHANGED)) {
- *aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
- return Status::ok();
- }
-
- mKeyStore->resetUser(userId, false);
- *aidl_return = static_cast<int32_t>(ResponseCode::NO_ERROR);
- return Status::ok();
-}
-
-Status KeyStoreService::lock(int32_t userId, int32_t* aidl_return) {
- if (!checkBinderPermission(P_LOCK)) {
- *aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
- return Status::ok();
- }
-
- State state = mKeyStore->getState(userId);
- if (state != ::STATE_NO_ERROR) {
- ALOGD("calling lock in state: %d", state);
- *aidl_return = static_cast<int32_t>(ResponseCode(state));
- return Status::ok();
- }
-
- mKeyStore->getEnforcementPolicy().set_device_locked(true, userId);
- mKeyStore->lock(userId);
- *aidl_return = static_cast<int32_t>(ResponseCode::NO_ERROR);
- return Status::ok();
-}
-
-Status KeyStoreService::unlock(int32_t userId, const String16& pw, int32_t* aidl_return) {
- if (!checkBinderPermission(P_UNLOCK)) {
- *aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
- return Status::ok();
- }
-
- State state = mKeyStore->getState(userId);
- if (state != ::STATE_LOCKED) {
- switch (state) {
- case ::STATE_NO_ERROR:
- ALOGI("calling unlock when already unlocked, ignoring.");
- break;
- case ::STATE_UNINITIALIZED:
- ALOGE("unlock called on uninitialized keystore.");
- break;
- default:
- ALOGE("unlock called on keystore in unknown state: %d", state);
- break;
- }
- *aidl_return = static_cast<int32_t>(ResponseCode(state));
- return Status::ok();
- }
-
- mKeyStore->getEnforcementPolicy().set_device_locked(false, userId);
- const String8 password8(pw);
- // read master key, decrypt with password, initialize mMasterKey*.
- *aidl_return = static_cast<int32_t>(mKeyStore->readMasterKey(password8, userId));
- return Status::ok();
-}
-
-Status KeyStoreService::isEmpty(int32_t userId, int32_t* aidl_return) {
- if (!checkBinderPermission(P_IS_EMPTY)) {
- *aidl_return = static_cast<int32_t>(false);
- return Status::ok();
- }
-
- *aidl_return = static_cast<int32_t>(mKeyStore->isEmpty(userId));
- return Status::ok();
-}
-
-Status KeyStoreService::grant(const String16& name, int32_t granteeUid,
- ::android::String16* aidl_return) {
- uid_t callingUid = IPCThreadState::self()->getCallingUid();
- auto result =
- checkBinderPermissionAndKeystoreState(P_GRANT, /*targetUid=*/-1, /*checkUnlocked=*/false);
- if (!result.isOk()) {
- *aidl_return = String16();
- return Status::ok();
- }
-
- String8 name8(name);
- auto lockedEntry = mKeyStore->getLockedBlobEntryIfExists(name8.string(), callingUid);
- if (!lockedEntry) {
- *aidl_return = String16();
- return Status::ok();
- }
-
- *aidl_return = String16(mKeyStore->addGrant(lockedEntry, granteeUid).c_str());
- return Status::ok();
-}
-
-Status KeyStoreService::ungrant(const String16& name, int32_t granteeUid, int32_t* aidl_return) {
- uid_t callingUid = IPCThreadState::self()->getCallingUid();
- KeyStoreServiceReturnCode result =
- checkBinderPermissionAndKeystoreState(P_GRANT, /*targetUid=*/-1, /*checkUnlocked=*/false);
- if (!result.isOk()) {
- *aidl_return = result.getErrorCode();
- return Status::ok();
- }
-
- String8 name8(name);
-
- auto lockedEntry = mKeyStore->getLockedBlobEntryIfExists(name8.string(), callingUid);
- if (!lockedEntry) {
- *aidl_return = static_cast<int32_t>(ResponseCode::KEY_NOT_FOUND);
- }
-
- *aidl_return = mKeyStore->removeGrant(lockedEntry, granteeUid);
- return Status::ok();
-}
-
-Status KeyStoreService::getmtime(const String16& name, int32_t uid, int64_t* time) {
- uid_t targetUid = getEffectiveUid(uid);
- if (!checkBinderPermission(P_GET, targetUid)) {
- ALOGW("permission denied for %d: getmtime", targetUid);
- *time = -1L;
- return Status::ok();
- }
- String8 name8(name);
-
- auto lockedEntry = mKeyStore->getLockedBlobEntryIfExists(name8.string(), targetUid);
- if (!lockedEntry) {
- ALOGW("could not access key with alias %s for getmtime", name8.string());
- *time = -1L;
- return Status::ok();
- }
-
- std::string filename = lockedEntry->getKeyBlobPath();
-
- int fd = TEMP_FAILURE_RETRY(open(filename.c_str(), O_NOFOLLOW, O_RDONLY));
- if (fd < 0) {
- ALOGW("could not open %s for getmtime", filename.c_str());
- *time = -1L;
- return Status::ok();
- }
-
- struct stat s;
- int ret = fstat(fd, &s);
- close(fd);
- if (ret == -1) {
- ALOGW("could not stat %s for getmtime", filename.c_str());
- *time = -1L;
- return Status::ok();
- }
-
- *time = static_cast<int64_t>(s.st_mtime);
- return Status::ok();
-}
-
-Status KeyStoreService::is_hardware_backed(const String16& keyType, int32_t* aidl_return) {
- *aidl_return = static_cast<int32_t>(mKeyStore->isHardwareBacked(keyType) ? 1 : 0);
- return Status::ok();
-}
-
-Status KeyStoreService::clear_uid(int64_t targetUid64, int32_t* _aidl_return) {
- uid_t targetUid = getEffectiveUid(targetUid64);
- if (!checkBinderPermissionSelfOrSystem(P_CLEAR_UID, targetUid)) {
- return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
- }
- ALOGI("clear_uid %" PRId64, targetUid64);
-
- mKeyStore->removeAllGrantsToUid(targetUid);
-
- ResponseCode rc;
- std::list<LockedKeyBlobEntry> entries;
- auto userDirName = mKeyStore->getUserStateDB().getUserStateByUid(targetUid)->getUserDirName();
-
- // list has a fence making sure no workers are modifying blob files before iterating the
- // data base. All returned entries are locked.
- std::tie(rc, entries) = LockedKeyBlobEntry::list(
- userDirName, [&](uid_t uid, const std::string&) -> bool { return uid == targetUid; });
-
- if (rc != ResponseCode::NO_ERROR) {
- return AIDL_RETURN(rc);
- }
-
- for (LockedKeyBlobEntry& lockedEntry : entries) {
- if (get_app_id(targetUid) == AID_SYSTEM) {
- Blob keyBlob;
- Blob charBlob;
- std::tie(rc, keyBlob, charBlob) = mKeyStore->get(lockedEntry);
- if (rc == ResponseCode::NO_ERROR && keyBlob.isCriticalToDeviceEncryption()) {
- // Do not clear keys critical to device encryption under system uid.
- continue;
- }
- }
- mKeyStore->del(lockedEntry);
- }
- return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::addRngEntropy(
- const ::android::sp<::android::security::keystore::IKeystoreResponseCallback>& cb,
- const ::std::vector<uint8_t>& entropy, int32_t flags, int32_t* _aidl_return) {
- auto device = mKeyStore->getDevice(flagsToSecurityLevel(flags));
- if (!device) {
- return AIDL_RETURN(ErrorCode::HARDWARE_TYPE_UNAVAILABLE);
- }
-
- device->addRngEntropy(entropy, [device, cb](Return<ErrorCode> rc) {
- cb->onFinished(KeyStoreServiceReturnCode(KS_HANDLE_HIDL_ERROR(device, rc)));
- });
-
- return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::generateKey(
- const ::android::sp<::android::security::keystore::IKeystoreKeyCharacteristicsCallback>& cb,
- const String16& name, const KeymasterArguments& params, const ::std::vector<uint8_t>& entropy,
- int uid, int flags, int32_t* _aidl_return) {
- uid = getEffectiveUid(uid);
- auto logOnScopeExit = android::base::make_scope_guard([&] {
- if (__android_log_security()) {
- android_log_event_list(SEC_TAG_AUTH_KEY_GENERATED)
- << int32_t(*_aidl_return == static_cast<int32_t>(ResponseCode::NO_ERROR))
- << String8(name) << int32_t(uid) << LOG_ID_SECURITY;
- }
- });
- KeyStoreServiceReturnCode rc =
- checkBinderPermissionAndKeystoreState(P_INSERT, uid, flags & KEYSTORE_FLAG_ENCRYPTED);
- if (!rc.isOk()) {
- return AIDL_RETURN(rc);
- }
- if ((flags & KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION) && get_app_id(uid) != AID_SYSTEM) {
- ALOGE("Non-system uid %d cannot set FLAG_CRITICAL_TO_DEVICE_ENCRYPTION", uid);
- return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
- }
-
- if (containsTag(params.getParameters(), Tag::INCLUDE_UNIQUE_ID)) {
- if (!checkBinderPermission(P_GEN_UNIQUE_ID)) {
- return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
- }
- }
-
- SecurityLevel securityLevel = flagsToSecurityLevel(flags);
- auto dev = mKeyStore->getDevice(securityLevel);
- if (!dev) {
- return AIDL_RETURN(ErrorCode::HARDWARE_TYPE_UNAVAILABLE);
- }
-
- String8 name8(name);
- auto lockedEntry = mKeyStore->getLockedBlobEntryIfNotExists(name8.string(), uid);
- if (!lockedEntry) {
- return AIDL_RETURN(ResponseCode::KEY_ALREADY_EXISTS);
- }
-
- logOnScopeExit.Disable();
-
- dev->generateKey(
- std::move(lockedEntry), params.getParameters(), entropy, flags,
- [cb, uid, name](KeyStoreServiceReturnCode rc, KeyCharacteristics keyCharacteristics) {
- if (__android_log_security()) {
- android_log_event_list(SEC_TAG_AUTH_KEY_GENERATED)
- << rc.isOk() << String8(name) << int32_t(uid) << LOG_ID_SECURITY;
- }
- cb->onFinished(rc,
- android::security::keymaster::KeyCharacteristics(keyCharacteristics));
- });
-
- return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::getKeyCharacteristics(
- const ::android::sp<::android::security::keystore::IKeystoreKeyCharacteristicsCallback>& cb,
- const String16& name, const ::android::security::keymaster::KeymasterBlob& clientId,
- const ::android::security::keymaster::KeymasterBlob& appData, int32_t uid,
- int32_t* _aidl_return) {
-
- uid_t targetUid = getEffectiveUid(uid);
- uid_t callingUid = IPCThreadState::self()->getCallingUid();
- if (!is_granted_to(callingUid, targetUid)) {
- ALOGW("uid %d not permitted to act for uid %d in getKeyCharacteristics", callingUid,
- targetUid);
- return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
- }
-
- String8 name8(name);
-
- ResponseCode rc;
- Blob keyBlob;
- Blob charBlob;
- LockedKeyBlobEntry lockedEntry;
-
- std::tie(rc, keyBlob, charBlob, lockedEntry) =
- mKeyStore->getKeyForName(name8, targetUid, TYPE_KEYMASTER_10);
-
- if (rc != ResponseCode::NO_ERROR) {
- return AIDL_RETURN(rc);
- }
-
- auto dev = mKeyStore->getDevice(keyBlob);
- if (!dev) {
- return AIDL_RETURN(ResponseCode::SYSTEM_ERROR);
- }
-
- // If the charBlob is up to date, it simply moves the argument blobs to the returned blobs
- // and extracts the characteristics on the way. Otherwise it updates the cache file with data
- // from keymaster. It may also upgrade the key blob.
- dev->getKeyCharacteristics(
- std::move(lockedEntry), clientId.getData(), appData.getData(), std::move(keyBlob),
- std::move(charBlob),
- [cb](KeyStoreServiceReturnCode rc, KeyCharacteristics keyCharacteristics) {
- cb->onFinished(rc,
- android::security::keymaster::KeyCharacteristics(keyCharacteristics));
- });
-
- return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::importKey(
- const ::android::sp<::android::security::keystore::IKeystoreKeyCharacteristicsCallback>& cb,
- const String16& name, const KeymasterArguments& params, int32_t format,
- const ::std::vector<uint8_t>& keyData, int uid, int flags, int32_t* _aidl_return) {
- uid = getEffectiveUid(uid);
- auto logOnScopeExit = android::base::make_scope_guard([&] {
- if (__android_log_security()) {
- android_log_event_list(SEC_TAG_KEY_IMPORTED)
- << int32_t(*_aidl_return == static_cast<int32_t>(ResponseCode::NO_ERROR))
- << String8(name) << int32_t(uid) << LOG_ID_SECURITY;
- }
- });
- KeyStoreServiceReturnCode rc =
- checkBinderPermissionAndKeystoreState(P_INSERT, uid, flags & KEYSTORE_FLAG_ENCRYPTED);
- if (!rc.isOk()) {
- LOG(ERROR) << "permissission denied";
- return AIDL_RETURN(rc);
- }
- if ((flags & KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION) && get_app_id(uid) != AID_SYSTEM) {
- ALOGE("Non-system uid %d cannot set FLAG_CRITICAL_TO_DEVICE_ENCRYPTION", uid);
- return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
- }
-
- SecurityLevel securityLevel = flagsToSecurityLevel(flags);
- auto dev = mKeyStore->getDevice(securityLevel);
- if (!dev) {
- LOG(ERROR) << "importKey - cound not get keymaster device";
- return AIDL_RETURN(ErrorCode::HARDWARE_TYPE_UNAVAILABLE);
- }
-
- String8 name8(name);
- auto lockedEntry = mKeyStore->getLockedBlobEntryIfNotExists(name8.string(), uid);
- if (!lockedEntry) {
- LOG(ERROR) << "importKey - key: " << name8.string() << " " << int(uid)
- << " already exists.";
- return AIDL_RETURN(ResponseCode::KEY_ALREADY_EXISTS);
- }
-
- logOnScopeExit.Disable();
-
- dev->importKey(
- std::move(lockedEntry), params.getParameters(), KeyFormat(format), keyData, flags,
- [cb, uid, name](KeyStoreServiceReturnCode rc, KeyCharacteristics keyCharacteristics) {
- if (__android_log_security()) {
- android_log_event_list(SEC_TAG_KEY_IMPORTED)
- << rc.isOk() << String8(name) << int32_t(uid) << LOG_ID_SECURITY;
- }
- cb->onFinished(rc,
- android::security::keymaster::KeyCharacteristics(keyCharacteristics));
- });
-
- return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::exportKey(
- const ::android::sp<::android::security::keystore::IKeystoreExportKeyCallback>& cb,
- const String16& name, int32_t format,
- const ::android::security::keymaster::KeymasterBlob& clientId,
- const ::android::security::keymaster::KeymasterBlob& appData, int32_t uid,
- int32_t* _aidl_return) {
-
- uid_t targetUid = getEffectiveUid(uid);
- uid_t callingUid = IPCThreadState::self()->getCallingUid();
- if (!is_granted_to(callingUid, targetUid)) {
- ALOGW("uid %d not permitted to act for uid %d in exportKey", callingUid, targetUid);
- return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
- }
-
- String8 name8(name);
-
- KeyStoreServiceReturnCode rc;
- Blob keyBlob;
- Blob charBlob;
- LockedKeyBlobEntry lockedEntry;
-
- std::tie(rc, keyBlob, charBlob, lockedEntry) =
- mKeyStore->getKeyForName(name8, targetUid, TYPE_KEYMASTER_10);
- if (!rc.isOk()) {
- return AIDL_RETURN(rc);
- }
-
- auto dev = mKeyStore->getDevice(keyBlob);
-
- dev->exportKey(std::move(lockedEntry), KeyFormat(format), clientId.getData(), appData.getData(),
- std::move(keyBlob), std::move(charBlob),
- [cb](ExportResult exportResult) { cb->onFinished(exportResult); });
-
- return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::begin(const sp<IKeystoreOperationResultCallback>& cb,
- const sp<IBinder>& appToken, const String16& name, int32_t purpose,
- bool pruneable, const KeymasterArguments& params,
- const ::std::vector<uint8_t>& entropy, int32_t uid,
- int32_t* _aidl_return) {
- uid_t callingUid = IPCThreadState::self()->getCallingUid();
- uid_t targetUid = getEffectiveUid(uid);
- if (!is_granted_to(callingUid, targetUid)) {
- ALOGW("uid %d not permitted to act for uid %d in begin", callingUid, targetUid);
- return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
- }
- if (!pruneable && get_app_id(callingUid) != AID_SYSTEM) {
- ALOGE("Non-system uid %d trying to start non-pruneable operation", callingUid);
- return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
- }
- if (!checkAllowedOperationParams(params.getParameters())) {
- return AIDL_RETURN(ErrorCode::INVALID_ARGUMENT);
- }
-
- String8 name8(name);
- Blob keyBlob;
- Blob charBlob;
- LockedKeyBlobEntry lockedEntry;
- ResponseCode rc;
-
- std::tie(rc, keyBlob, charBlob, lockedEntry) =
- mKeyStore->getKeyForName(name8, targetUid, TYPE_KEYMASTER_10);
-
- if (rc == ResponseCode::LOCKED && keyBlob.isSuperEncrypted()) {
- return AIDL_RETURN(ErrorCode::KEY_USER_NOT_AUTHENTICATED);
- }
- if (rc != ResponseCode::NO_ERROR) return AIDL_RETURN(rc);
-
- auto dev = mKeyStore->getDevice(keyBlob);
- AuthorizationSet opParams = params.getParameters();
-
- dev->begin(std::move(lockedEntry), appToken, std::move(keyBlob), std::move(charBlob), pruneable,
- static_cast<KeyPurpose>(purpose), std::move(opParams), entropy,
- [this, cb, dev](OperationResult result_) {
- if (result_.resultCode.isOk() ||
- result_.resultCode == ResponseCode::OP_AUTH_NEEDED) {
- mKeyStore->addOperationDevice(result_.token, dev);
- }
- cb->onFinished(result_);
- });
-
- return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::update(const ::android::sp<IKeystoreOperationResultCallback>& cb,
- const ::android::sp<::android::IBinder>& token,
- const ::android::security::keymaster::KeymasterArguments& params,
- const ::std::vector<uint8_t>& input, int32_t* _aidl_return) {
- if (!checkAllowedOperationParams(params.getParameters())) {
- return AIDL_RETURN(ErrorCode::INVALID_ARGUMENT);
- }
-
- auto dev = mKeyStore->getOperationDevice(token);
- if (!dev) {
- return AIDL_RETURN(ErrorCode::INVALID_OPERATION_HANDLE);
- }
-
- dev->update(token, params.getParameters(), input, [this, cb, token](OperationResult result_) {
- if (!result_.resultCode.isOk()) {
- mKeyStore->removeOperationDevice(token);
- }
- cb->onFinished(result_);
- });
-
- return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::finish(const ::android::sp<IKeystoreOperationResultCallback>& cb,
- const ::android::sp<::android::IBinder>& token,
- const ::android::security::keymaster::KeymasterArguments& params,
- const ::std::vector<uint8_t>& input,
- const ::std::vector<uint8_t>& signature,
- const ::std::vector<uint8_t>& entropy, int32_t* _aidl_return) {
- if (!checkAllowedOperationParams(params.getParameters())) {
- return AIDL_RETURN(ErrorCode::INVALID_ARGUMENT);
- }
-
- auto dev = mKeyStore->getOperationDevice(token);
- if (!dev) {
- return AIDL_RETURN(ErrorCode::INVALID_OPERATION_HANDLE);
- }
-
- dev->finish(token, params.getParameters(), input, signature, entropy,
- [this, cb, token](OperationResult result_) {
- mKeyStore->removeOperationDevice(token);
- cb->onFinished(result_);
- });
-
- return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::abort(const ::android::sp<IKeystoreResponseCallback>& cb,
- const ::android::sp<::android::IBinder>& token,
- int32_t* _aidl_return) {
- auto dev = mKeyStore->getOperationDevice(token);
- if (!dev) {
- return AIDL_RETURN(ErrorCode::INVALID_OPERATION_HANDLE);
- }
-
- dev->abort(token, [this, cb, token](KeyStoreServiceReturnCode rc) {
- mKeyStore->removeOperationDevice(token);
- cb->onFinished(rc);
- });
-
- return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::addAuthToken(const ::std::vector<uint8_t>& authTokenAsVector,
- int32_t* aidl_return) {
-
- // TODO(swillden): When gatekeeper and fingerprint are ready, this should be updated to
- // receive a HardwareAuthToken, rather than an opaque byte array.
-
- if (!checkBinderPermission(P_ADD_AUTH)) {
- ALOGW("addAuthToken: permission denied for %d", IPCThreadState::self()->getCallingUid());
- *aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
- return Status::ok();
- }
- if (authTokenAsVector.size() != sizeof(hw_auth_token_t)) {
- *aidl_return = KeyStoreServiceReturnCode(ErrorCode::INVALID_ARGUMENT).getErrorCode();
- return Status::ok();
- }
-
- hw_auth_token_t authToken;
- memcpy(reinterpret_cast<void*>(&authToken), authTokenAsVector.data(), sizeof(hw_auth_token_t));
- if (authToken.version != 0) {
- *aidl_return = KeyStoreServiceReturnCode(ErrorCode::INVALID_ARGUMENT).getErrorCode();
- return Status::ok();
- }
-
- mKeyStore->getAuthTokenTable().AddAuthenticationToken(
- hidlVec2AuthToken(hidl_vec<uint8_t>(authTokenAsVector)));
- *aidl_return = static_cast<int32_t>(ResponseCode::NO_ERROR);
- return Status::ok();
-}
-
-Status KeyStoreService::getTokensForCredstore(int64_t challenge, int64_t secureUserId,
- int32_t authTokenMaxAgeMillis,
- const ::android::sp<ICredstoreTokenCallback>& cb) {
- uid_t callingUid = IPCThreadState::self()->getCallingUid();
- if (callingUid != AID_CREDSTORE) {
- return Status::fromServiceSpecificError(static_cast<int32_t>(0));
- }
-
- auto [err, authToken] = mKeyStore->getAuthTokenTable().FindAuthorizationForCredstore(
- challenge, secureUserId, authTokenMaxAgeMillis);
- // It's entirely possible we couldn't find an authToken (e.g. no user auth
- // happened within the requested deadline) and in that case, we just
- // callback immediately signaling success but just not returning any tokens.
- if (err != AuthTokenTable::OK) {
- cb->onFinished(true, {} /* serializedAuthToken */, {} /* serializedVerificationToken */);
- return Status::ok();
- }
-
- // If we did find an authToken, get a verificationToken as well...
- //
- std::vector<uint8_t> serializedAuthToken = authToken2HidlVec(authToken);
- std::vector<uint8_t> serializedVerificationToken;
- std::shared_ptr<KeymasterWorker> dev = mKeyStore->getDevice(SecurityLevel::TRUSTED_ENVIRONMENT);
- if (!dev) {
- LOG(ERROR) << "Unable to get KM device for SecurityLevel::TRUSTED_ENVIRONMENT";
- dev = mKeyStore->getDevice(SecurityLevel::SOFTWARE);
- if (!dev) {
- LOG(ERROR) << "Unable to get KM device for SecurityLevel::SOFTWARE";
- cb->onFinished(false, {}, {});
- return Status::fromServiceSpecificError(static_cast<int32_t>(0));
- }
- }
-
- dev->verifyAuthorization(
- challenge, {} /* params */, authToken,
- [serializedAuthToken, cb](KeyStoreServiceReturnCode rc, HardwareAuthToken,
- VerificationToken verificationToken) {
- if (rc != ErrorCode::OK) {
- LOG(ERROR) << "verifyAuthorization failed, rc=" << rc;
- cb->onFinished(false, {}, {});
- return;
- }
- std::optional<std::vector<uint8_t>> serializedVerificationToken =
- serializeVerificationToken(verificationToken);
- if (!serializedVerificationToken) {
- LOG(ERROR) << "Error serializing verificationToken";
- cb->onFinished(false, {}, {});
- return;
- }
- cb->onFinished(true, serializedAuthToken, serializedVerificationToken.value());
- });
-
- return Status::ok();
-}
-
-bool isDeviceIdAttestationTag(Tag tag) {
- switch (tag) {
- case Tag::ATTESTATION_ID_BRAND:
- case Tag::ATTESTATION_ID_DEVICE:
- case Tag::ATTESTATION_ID_MANUFACTURER:
- case Tag::ATTESTATION_ID_MODEL:
- case Tag::ATTESTATION_ID_PRODUCT:
- case Tag::ATTESTATION_ID_IMEI:
- case Tag::ATTESTATION_ID_MEID:
- case Tag::ATTESTATION_ID_SERIAL:
- return true;
- case Tag::INVALID:
- case Tag::PURPOSE:
- case Tag::ALGORITHM:
- case Tag::KEY_SIZE:
- case Tag::BLOCK_MODE:
- case Tag::DIGEST:
- case Tag::PADDING:
- case Tag::CALLER_NONCE:
- case Tag::MIN_MAC_LENGTH:
- case Tag::EC_CURVE:
- case Tag::RSA_PUBLIC_EXPONENT:
- case Tag::INCLUDE_UNIQUE_ID:
- case Tag::BLOB_USAGE_REQUIREMENTS:
- case Tag::BOOTLOADER_ONLY:
- case Tag::ROLLBACK_RESISTANCE:
- case Tag::HARDWARE_TYPE:
- case Tag::ACTIVE_DATETIME:
- case Tag::ORIGINATION_EXPIRE_DATETIME:
- case Tag::USAGE_EXPIRE_DATETIME:
- case Tag::MIN_SECONDS_BETWEEN_OPS:
- case Tag::MAX_USES_PER_BOOT:
- case Tag::USER_ID:
- case Tag::USER_SECURE_ID:
- case Tag::NO_AUTH_REQUIRED:
- case Tag::USER_AUTH_TYPE:
- case Tag::AUTH_TIMEOUT:
- case Tag::ALLOW_WHILE_ON_BODY:
- case Tag::TRUSTED_USER_PRESENCE_REQUIRED:
- case Tag::TRUSTED_CONFIRMATION_REQUIRED:
- case Tag::UNLOCKED_DEVICE_REQUIRED:
- case Tag::APPLICATION_ID:
- case Tag::APPLICATION_DATA:
- case Tag::CREATION_DATETIME:
- case Tag::ORIGIN:
- case Tag::ROOT_OF_TRUST:
- case Tag::OS_VERSION:
- case Tag::OS_PATCHLEVEL:
- case Tag::UNIQUE_ID:
- case Tag::ATTESTATION_CHALLENGE:
- case Tag::ATTESTATION_APPLICATION_ID:
- case Tag::VENDOR_PATCHLEVEL:
- case Tag::BOOT_PATCHLEVEL:
- case Tag::ASSOCIATED_DATA:
- case Tag::NONCE:
- case Tag::MAC_LENGTH:
- case Tag::RESET_SINCE_ID_ROTATION:
- case Tag::CONFIRMATION_TOKEN:
- return false;
- // no default, all values must be present in the switch, in this way the compiler ensures
- // that new values added in the Tag enum are also added here.
- }
-}
-
-// These are attestation id tags that are not unique per device and don't require special permission
-// to be attested. Any addition to this list needs privacy review and approval (PWG).
-bool isDevicePropertyAttestationTag(Tag tag) {
- switch (tag) {
- case Tag::ATTESTATION_ID_BRAND:
- case Tag::ATTESTATION_ID_DEVICE:
- case Tag::ATTESTATION_ID_MANUFACTURER:
- case Tag::ATTESTATION_ID_MODEL:
- case Tag::ATTESTATION_ID_PRODUCT:
- return true;
- default:
- return false;
- }
-}
-
-bool isDeviceIdAttestationRequested(const KeymasterArguments& params) {
- const hardware::hidl_vec<KeyParameter>& paramsVec = params.getParameters();
- for (size_t i = 0; i < paramsVec.size(); ++i) {
- if (isDeviceIdAttestationTag(paramsVec[i].tag)) {
- return true;
- }
- }
- return false;
-}
-
-// Device properties can be attested safely without special permission
-bool needsPermissionToAttestDeviceIds(const KeymasterArguments& params) {
- const hardware::hidl_vec<KeyParameter>& paramsVec = params.getParameters();
- for (size_t i = 0; i < paramsVec.size(); ++i) {
- if (isDeviceIdAttestationTag(paramsVec[i].tag) &&
- !isDevicePropertyAttestationTag(paramsVec[i].tag)) {
- return true;
- }
- }
- return false;
-}
-
-Status KeyStoreService::attestKey(
- const ::android::sp<::android::security::keystore::IKeystoreCertificateChainCallback>& cb,
- const String16& name, const KeymasterArguments& params, int32_t* _aidl_return) {
- // check null output if method signature is updated and return ErrorCode::OUTPUT_PARAMETER_NULL
- if (!checkAllowedOperationParams(params.getParameters())) {
- return AIDL_RETURN(ErrorCode::INVALID_ARGUMENT);
- }
-
- uid_t callingUid = IPCThreadState::self()->getCallingUid();
-
- if (needsPermissionToAttestDeviceIds(params) && (get_app_id(callingUid) != AID_SYSTEM)) {
- return AIDL_RETURN(KeyStoreServiceReturnCode(ErrorCode::INVALID_ARGUMENT));
- }
-
- AuthorizationSet mutableParams = params.getParameters();
- KeyStoreServiceReturnCode rc = updateParamsForAttestation(callingUid, &mutableParams);
-
- auto logErrorOnReturn = android::base::make_scope_guard(
- [&] { logKeystoreKeyAttestationEvent(false /*wasSuccessful*/, rc.getErrorCode()); });
-
- if (!rc.isOk()) {
- return AIDL_RETURN(rc);
- }
-
- String8 name8(name);
- Blob keyBlob;
- Blob charBlob;
- LockedKeyBlobEntry lockedEntry;
-
- std::tie(rc, keyBlob, charBlob, lockedEntry) =
- mKeyStore->getKeyForName(name8, callingUid, TYPE_KEYMASTER_10);
-
- if (!rc.isOk()) {
- return AIDL_RETURN(rc);
- }
-
- logErrorOnReturn.Disable();
-
- auto dev = mKeyStore->getDevice(keyBlob);
- auto hidlKey = blob2hidlVec(keyBlob);
- dev->attestKey(
- std::move(hidlKey), mutableParams.hidl_data(),
- [dev, cb](Return<void> rc,
- std::tuple<ErrorCode, hidl_vec<hidl_vec<uint8_t>>>&& hidlResult) {
- auto& [ret, certChain] = hidlResult;
- if (!rc.isOk()) {
- logKeystoreKeyAttestationEvent(false /*wasSuccessful*/,
- static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
- cb->onFinished(KeyStoreServiceReturnCode(ResponseCode::SYSTEM_ERROR), {});
- } else if (ret != ErrorCode::OK) {
- KeyStoreServiceReturnCode ksrc(ret);
- logKeystoreKeyAttestationEvent(false /*wasSuccessful*/, ksrc.getErrorCode());
- dev->logIfKeymasterVendorError(ret);
- cb->onFinished(ksrc, {});
- } else {
- KeyStoreServiceReturnCode ksrc(ret);
- logKeystoreKeyAttestationEvent(true /*wasSuccessful*/, ksrc.getErrorCode());
- cb->onFinished(ksrc, KeymasterCertificateChain(std::move(certChain)));
- }
- });
-
- return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-// My IDE defines "CAPTURE_MOVE(x) x" because it does not understand generalized lambda captures.
-// It should never be redefined by a build system though.
-#ifndef CAPTURE_MOVE
-#define CAPTURE_MOVE(x) x = std::move(x)
-#endif
-
-Status KeyStoreService::attestDeviceIds(
- const ::android::sp<::android::security::keystore::IKeystoreCertificateChainCallback>& cb,
- const KeymasterArguments& params, int32_t* _aidl_return) {
- // check null output if method signature is updated and return ErrorCode::OUTPUT_PARAMETER_NULL
-
- if (!checkAllowedOperationParams(params.getParameters())) {
- return AIDL_RETURN(ErrorCode::INVALID_ARGUMENT);
- }
-
- if (!isDeviceIdAttestationRequested(params)) {
- // There is an attestKey() method for attesting keys without device ID attestation.
- return AIDL_RETURN(ErrorCode::INVALID_ARGUMENT);
- }
-
- uid_t callingUid = IPCThreadState::self()->getCallingUid();
-
- // Request special permission only for unique ids
- if (needsPermissionToAttestDeviceIds(params)) {
- sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
- if (binder == nullptr) {
- return AIDL_RETURN(ErrorCode::CANNOT_ATTEST_IDS);
- }
-
- if (!interface_cast<IPermissionController>(binder)->checkPermission(
- String16("android.permission.READ_PRIVILEGED_PHONE_STATE"),
- IPCThreadState::self()->getCallingPid(), callingUid)) {
- return AIDL_RETURN(ErrorCode::CANNOT_ATTEST_IDS);
- }
- }
-
- AuthorizationSet mutableParams = params.getParameters();
- KeyStoreServiceReturnCode rc = updateParamsForAttestation(callingUid, &mutableParams);
- if (!rc.isOk()) {
- return AIDL_RETURN(rc);
- }
-
- // Generate temporary key.
- auto dev = mKeyStore->getDevice(SecurityLevel::TRUSTED_ENVIRONMENT);
-
- if (!dev) {
- return AIDL_RETURN(ResponseCode::SYSTEM_ERROR);
- }
-
-
- AuthorizationSet keyCharacteristics;
- keyCharacteristics.push_back(TAG_PURPOSE, KeyPurpose::VERIFY);
- keyCharacteristics.push_back(TAG_ALGORITHM, Algorithm::EC);
- keyCharacteristics.push_back(TAG_DIGEST, Digest::SHA_2_256);
- keyCharacteristics.push_back(TAG_NO_AUTH_REQUIRED);
- keyCharacteristics.push_back(TAG_EC_CURVE, EcCurve::P_256);
-
- std::promise<KeyStoreServiceReturnCode> resultPromise;
- auto resultFuture = resultPromise.get_future();
-
- dev->generateKey(
- keyCharacteristics.hidl_data(),
- [cb, dev, CAPTURE_MOVE(mutableParams)](
- Return<void> rc,
- std::tuple<ErrorCode, ::std::vector<uint8_t>, KeyCharacteristics>&& hidlResult) {
- auto& [ret, hidlKeyBlob_, dummyCharacteristics] = hidlResult;
- auto hidlKeyBlob = std::move(hidlKeyBlob_);
- if (!rc.isOk()) {
- cb->onFinished(KeyStoreServiceReturnCode(ResponseCode::SYSTEM_ERROR), {});
- return;
- }
- if (ret != ErrorCode::OK) {
- dev->logIfKeymasterVendorError(ret);
- cb->onFinished(KeyStoreServiceReturnCode(ret), {});
- return;
- }
- dev->attestKey(
- hidlKeyBlob, mutableParams.hidl_data(),
- [cb, dev,
- hidlKeyBlob](Return<void> rc,
- std::tuple<ErrorCode, hidl_vec<hidl_vec<uint8_t>>>&& hidlResult) {
- auto& [ret, certChain] = hidlResult;
- // schedule temp key for deletion
- dev->deleteKey(std::move(hidlKeyBlob), [dev](Return<ErrorCode> rc) {
- // log error but don't return an error
- KS_HANDLE_HIDL_ERROR(dev, rc);
- });
- if (!rc.isOk()) {
- cb->onFinished(KeyStoreServiceReturnCode(ResponseCode::SYSTEM_ERROR), {});
- return;
- }
- if (ret == ErrorCode::OK) {
- cb->onFinished(
- KeyStoreServiceReturnCode(ret),
- ::android::security::keymaster::KeymasterCertificateChain(certChain));
- } else {
- dev->logIfKeymasterVendorError(ret);
- cb->onFinished(KeyStoreServiceReturnCode(ret), {});
- }
- });
- });
-
- return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::onDeviceOffBody(int32_t* aidl_return) {
- // TODO(tuckeris): add permission check. This should be callable from ClockworkHome only.
- mKeyStore->getAuthTokenTable().onDeviceOffBody();
- *aidl_return = static_cast<int32_t>(ResponseCode::NO_ERROR);
- return Status::ok();
-}
-
-Status KeyStoreService::importWrappedKey(
- const ::android::sp<::android::security::keystore::IKeystoreKeyCharacteristicsCallback>& cb,
- const ::android::String16& wrappedKeyAlias, const ::std::vector<uint8_t>& wrappedKey,
- const ::android::String16& wrappingKeyAlias, const ::std::vector<uint8_t>& maskingKey,
- const KeymasterArguments& params, int64_t rootSid, int64_t fingerprintSid,
- int32_t* _aidl_return) {
-
- uid_t callingUid = IPCThreadState::self()->getCallingUid();
-
- if (!checkBinderPermission(P_INSERT, callingUid)) {
- return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
- }
-
- String8 wrappingKeyName8(wrappingKeyAlias);
-
- KeyStoreServiceReturnCode rc;
- Blob wrappingKeyBlob;
- Blob wrappingCharBlob;
- LockedKeyBlobEntry wrappingLockedEntry;
-
- std::tie(rc, wrappingKeyBlob, wrappingCharBlob, wrappingLockedEntry) =
- mKeyStore->getKeyForName(wrappingKeyName8, callingUid, TYPE_KEYMASTER_10);
- if (!rc.isOk()) {
- return AIDL_RETURN(rc);
- }
-
- String8 wrappedKeyName8(wrappedKeyAlias);
- auto wrappedLockedEntry =
- mKeyStore->getLockedBlobEntryIfNotExists(wrappedKeyName8.string(), callingUid);
- if (!wrappedLockedEntry) {
- return AIDL_RETURN(ResponseCode::KEY_ALREADY_EXISTS);
- }
-
- SecurityLevel securityLevel = wrappingKeyBlob.getSecurityLevel();
- auto dev = mKeyStore->getDevice(securityLevel);
- if (!dev) {
- return AIDL_RETURN(ErrorCode::HARDWARE_TYPE_UNAVAILABLE);
- }
-
- dev->importWrappedKey(
- std::move(wrappingLockedEntry), std::move(wrappedLockedEntry), wrappedKey, maskingKey,
- params.getParameters(), std::move(wrappingKeyBlob), std::move(wrappingCharBlob), rootSid,
- fingerprintSid, [cb](KeyStoreServiceReturnCode rc, KeyCharacteristics keyCharacteristics) {
- cb->onFinished(rc,
- ::android::security::keymaster::KeyCharacteristics(keyCharacteristics));
- });
-
- return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::presentConfirmationPrompt(const sp<IBinder>& listener,
- const String16& promptText,
- const ::std::vector<uint8_t>& extraData,
- const String16& locale, int32_t uiOptionsAsFlags,
- int32_t* aidl_return) {
- return mKeyStore->getConfirmationManager().presentConfirmationPrompt(
- listener, promptText, extraData, locale, uiOptionsAsFlags, aidl_return);
-}
-
-Status KeyStoreService::cancelConfirmationPrompt(const sp<IBinder>& listener,
- int32_t* aidl_return) {
- return mKeyStore->getConfirmationManager().cancelConfirmationPrompt(listener, aidl_return);
-}
-
-Status KeyStoreService::isConfirmationPromptSupported(bool* aidl_return) {
- return mKeyStore->getConfirmationManager().isConfirmationPromptSupported(aidl_return);
-}
-
-/**
- * Get the effective target uid for a binder operation that takes an
- * optional uid as the target.
- */
-uid_t KeyStoreService::getEffectiveUid(int32_t targetUid) {
- if (targetUid == UID_SELF) {
- return IPCThreadState::self()->getCallingUid();
- }
- return static_cast<uid_t>(targetUid);
-}
-
-/**
- * Check if the caller of the current binder method has the required
- * permission and if acting on other uids the grants to do so.
- */
-bool KeyStoreService::checkBinderPermission(perm_t permission, int32_t targetUid) {
- uid_t callingUid = IPCThreadState::self()->getCallingUid();
- pid_t spid = IPCThreadState::self()->getCallingPid();
- const char* ssid = IPCThreadState::self()->getCallingSid();
- if (!has_permission(callingUid, permission, spid, ssid)) {
- ALOGW("permission %s denied for %d", get_perm_label(permission), callingUid);
- return false;
- }
- if (!is_granted_to(callingUid, getEffectiveUid(targetUid))) {
- ALOGW("uid %d not granted to act for %d", callingUid, targetUid);
- return false;
- }
- return true;
-}
-
-/**
- * Check if the caller of the current binder method has the required
- * permission and the target uid is the caller or the caller is system.
- */
-bool KeyStoreService::checkBinderPermissionSelfOrSystem(perm_t permission, int32_t targetUid) {
- uid_t callingUid = IPCThreadState::self()->getCallingUid();
- pid_t spid = IPCThreadState::self()->getCallingPid();
- const char* ssid = IPCThreadState::self()->getCallingSid();
- if (!has_permission(callingUid, permission, spid, ssid)) {
- ALOGW("permission %s denied for %d", get_perm_label(permission), callingUid);
- return false;
- }
- return getEffectiveUid(targetUid) == callingUid || callingUid == AID_SYSTEM;
-}
-
-/**
- * Check if the caller of the current binder method has the required
- * permission or the target of the operation is the caller's uid. This is
- * for operation where the permission is only for cross-uid activity and all
- * uids are allowed to act on their own (ie: clearing all entries for a
- * given uid).
- */
-bool KeyStoreService::checkBinderPermissionOrSelfTarget(perm_t permission, int32_t targetUid) {
- uid_t callingUid = IPCThreadState::self()->getCallingUid();
- if (getEffectiveUid(targetUid) == callingUid) {
- return true;
- } else {
- return checkBinderPermission(permission, targetUid);
- }
-}
-
-/**
- * Helper method to check that the caller has the required permission as
- * well as the keystore is in the unlocked state if checkUnlocked is true.
- *
- * Returns NO_ERROR on success, PERMISSION_DENIED on a permission error and
- * otherwise the state of keystore when not unlocked and checkUnlocked is
- * true.
- */
-KeyStoreServiceReturnCode
-KeyStoreService::checkBinderPermissionAndKeystoreState(perm_t permission, int32_t targetUid,
- bool checkUnlocked) {
- if (!checkBinderPermission(permission, targetUid)) {
- return ResponseCode::PERMISSION_DENIED;
- }
- State state = mKeyStore->getState(get_user_id(getEffectiveUid(targetUid)));
- if (checkUnlocked && !isKeystoreUnlocked(state)) {
- // All State values coincide with ResponseCodes
- return static_cast<ResponseCode>(state);
- }
-
- return ResponseCode::NO_ERROR;
-}
-
-bool KeyStoreService::isKeystoreUnlocked(State state) {
- switch (state) {
- case ::STATE_NO_ERROR:
- return true;
- case ::STATE_UNINITIALIZED:
- case ::STATE_LOCKED:
- return false;
- }
- return false;
-}
-
-/**
- * Check that all KeyParameters provided by the application are allowed. Any parameter that keystore
- * adds itself should be disallowed here.
- */
-bool KeyStoreService::checkAllowedOperationParams(const hidl_vec<KeyParameter>& params) {
- for (size_t i = 0; i < params.size(); ++i) {
- switch (params[i].tag) {
- case Tag::ATTESTATION_APPLICATION_ID:
- case Tag::RESET_SINCE_ID_ROTATION:
- return false;
- default:
- break;
- }
- }
- return true;
-}
-
-Status KeyStoreService::onKeyguardVisibilityChanged(bool isShowing, int32_t userId,
- int32_t* _aidl_return) {
- if (isShowing) {
- if (!checkBinderPermission(P_LOCK, UID_SELF)) {
- LOG(WARNING) << "onKeyguardVisibilityChanged called with isShowing == true but "
- "without LOCK permission";
- return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
- }
- } else {
- if (!checkBinderPermission(P_UNLOCK, UID_SELF)) {
- LOG(WARNING) << "onKeyguardVisibilityChanged called with isShowing == false but "
- "without UNLOCK permission";
- return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
- }
- }
- mKeyStore->getEnforcementPolicy().set_device_locked(isShowing, userId);
- return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-} // namespace keystore
diff --git a/keystore/key_store_service.h b/keystore/key_store_service.h
deleted file mode 100644
index 5fdddb9..0000000
--- a/keystore/key_store_service.h
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#ifndef KEYSTORE_KEYSTORE_SERVICE_H_
-#define KEYSTORE_KEYSTORE_SERVICE_H_
-
-#include <android/security/keystore/BnKeystoreService.h>
-
-#include "auth_token_table.h"
-#include "confirmation_manager.h"
-
-#include "KeyStore.h"
-#include "keystore_keymaster_enforcement.h"
-#include "operation.h"
-#include "permissions.h"
-
-#include <keystore/ExportResult.h>
-#include <keystore/KeyCharacteristics.h>
-#include <keystore/KeymasterArguments.h>
-#include <keystore/KeymasterBlob.h>
-#include <keystore/KeymasterCertificateChain.h>
-#include <keystore/OperationResult.h>
-#include <keystore/keystore_return_types.h>
-
-#include <mutex>
-
-namespace keystore {
-
-// Class provides implementation for generated BnKeystoreService.h based on
-// gen/aidl/android/security/BnKeystoreService.h generated from
-// java/android/security/IKeystoreService.aidl Note that all generated methods return binder::Status
-// and use last arguments to send actual result to the caller. Private methods don't need to handle
-// binder::Status. Input parameters cannot be null unless annotated with @nullable in .aidl file.
-class KeyStoreService : public android::security::keystore::BnKeystoreService {
- public:
- explicit KeyStoreService(sp<KeyStore> keyStore) : mKeyStore(keyStore) {}
- virtual ~KeyStoreService() = default;
-
- void binderDied(const android::wp<android::IBinder>& who);
-
- ::android::binder::Status getState(int32_t userId, int32_t* _aidl_return) override;
- ::android::binder::Status get(const ::android::String16& name, int32_t uid,
- ::std::vector<uint8_t>* _aidl_return) override;
- ::android::binder::Status insert(const ::android::String16& name,
- const ::std::vector<uint8_t>& item, int32_t uid, int32_t flags,
- int32_t* _aidl_return) override;
- ::android::binder::Status del(const ::android::String16& name, int32_t uid,
- int32_t* _aidl_return) override;
- ::android::binder::Status exist(const ::android::String16& name, int32_t uid,
- int32_t* _aidl_return) override;
- ::android::binder::Status list(const ::android::String16& namePrefix, int32_t uid,
- ::std::vector<::android::String16>* _aidl_return) override;
- ::android::binder::Status listUidsOfAuthBoundKeys(std::vector<::std::string>* uids,
- int32_t* _aidl_return) override;
-
- ::android::binder::Status onUserPasswordChanged(int32_t userId,
- const ::android::String16& newPassword,
- int32_t* _aidl_return) override;
- ::android::binder::Status lock(int32_t userId, int32_t* _aidl_return) override;
- ::android::binder::Status unlock(int32_t userId, const ::android::String16& userPassword,
- int32_t* _aidl_return) override;
- ::android::binder::Status isEmpty(int32_t userId, int32_t* _aidl_return) override;
- ::android::binder::Status grant(const ::android::String16& name, int32_t granteeUid,
- ::android::String16* _aidl_return) override;
- ::android::binder::Status ungrant(const ::android::String16& name, int32_t granteeUid,
- int32_t* _aidl_return) override;
- ::android::binder::Status getmtime(const ::android::String16& name, int32_t uid,
- int64_t* _aidl_return) override;
- ::android::binder::Status is_hardware_backed(const ::android::String16& string,
- int32_t* _aidl_return) override;
- ::android::binder::Status clear_uid(int64_t uid, int32_t* _aidl_return) override;
- ::android::binder::Status
- addRngEntropy(const ::android::sp<::android::security::keystore::IKeystoreResponseCallback>& cb,
- const ::std::vector<uint8_t>& data, int32_t flags,
- int32_t* _aidl_return) override;
- ::android::binder::Status generateKey(
- const ::android::sp<::android::security::keystore::IKeystoreKeyCharacteristicsCallback>& cb,
- const ::android::String16& alias,
- const ::android::security::keymaster::KeymasterArguments& arguments,
- const ::std::vector<uint8_t>& entropy, int32_t uid, int32_t flags,
- int32_t* _aidl_return) override;
- ::android::binder::Status getKeyCharacteristics(
- const ::android::sp<::android::security::keystore::IKeystoreKeyCharacteristicsCallback>& cb,
- const ::android::String16& alias,
- const ::android::security::keymaster::KeymasterBlob& clientId,
- const ::android::security::keymaster::KeymasterBlob& appId, int32_t uid,
- int32_t* _aidl_return) override;
- ::android::binder::Status importKey(
- const ::android::sp<::android::security::keystore::IKeystoreKeyCharacteristicsCallback>& cb,
- const ::android::String16& alias,
- const ::android::security::keymaster::KeymasterArguments& arguments, int32_t format,
- const ::std::vector<uint8_t>& keyData, int32_t uid, int32_t flags,
- int32_t* _aidl_return) override;
- ::android::binder::Status
- exportKey(const ::android::sp<::android::security::keystore::IKeystoreExportKeyCallback>& cb,
- const ::android::String16& alias, int32_t format,
- const ::android::security::keymaster::KeymasterBlob& clientId,
- const ::android::security::keymaster::KeymasterBlob& appId, int32_t uid,
- int32_t* _aidl_return) override;
- ::android::binder::Status
- begin(const ::android::sp<::android::security::keystore::IKeystoreOperationResultCallback>& cb,
- const ::android::sp<::android::IBinder>& appToken, const ::android::String16& alias,
- int32_t purpose, bool pruneable,
- const ::android::security::keymaster::KeymasterArguments& params,
- const ::std::vector<uint8_t>& entropy, int32_t uid, int32_t* _aidl_return) override;
- ::android::binder::Status
- update(const ::android::sp<::android::security::keystore::IKeystoreOperationResultCallback>& cb,
- const ::android::sp<::android::IBinder>& token,
- const ::android::security::keymaster::KeymasterArguments& params,
- const ::std::vector<uint8_t>& input, int32_t* _aidl_return) override;
- ::android::binder::Status
- finish(const ::android::sp<::android::security::keystore::IKeystoreOperationResultCallback>& cb,
- const ::android::sp<::android::IBinder>& token,
- const ::android::security::keymaster::KeymasterArguments& params,
- const ::std::vector<uint8_t>& input, const ::std::vector<uint8_t>& signature,
- const ::std::vector<uint8_t>& entropy, int32_t* _aidl_return) override;
- ::android::binder::Status
- abort(const ::android::sp<::android::security::keystore::IKeystoreResponseCallback>& cb,
- const ::android::sp<::android::IBinder>& token, int32_t* _aidl_return) override;
- ::android::binder::Status addAuthToken(const ::std::vector<uint8_t>& authToken,
- int32_t* _aidl_return) override;
- ::android::binder::Status getTokensForCredstore(
- int64_t challenge, int64_t secureUserId, int32_t authTokenMaxAge,
- const ::android::sp<::android::security::keystore::ICredstoreTokenCallback>& cb) override;
- ::android::binder::Status onUserAdded(int32_t userId, int32_t parentId,
- int32_t* _aidl_return) override;
- ::android::binder::Status onUserRemoved(int32_t userId, int32_t* _aidl_return) override;
- ::android::binder::Status attestKey(
- const ::android::sp<::android::security::keystore::IKeystoreCertificateChainCallback>& cb,
- const ::android::String16& alias,
- const ::android::security::keymaster::KeymasterArguments& params,
- int32_t* _aidl_return) override;
- ::android::binder::Status attestDeviceIds(
- const ::android::sp<::android::security::keystore::IKeystoreCertificateChainCallback>& cb,
- const ::android::security::keymaster::KeymasterArguments& params,
- int32_t* _aidl_return) override;
- ::android::binder::Status onDeviceOffBody(int32_t* _aidl_return) override;
-
- ::android::binder::Status importWrappedKey(
- const ::android::sp<::android::security::keystore::IKeystoreKeyCharacteristicsCallback>& cb,
- const ::android::String16& wrappedKeyAlias, const ::std::vector<uint8_t>& wrappedKey,
- const ::android::String16& wrappingKeyAlias, const ::std::vector<uint8_t>& maskingKey,
- const ::android::security::keymaster::KeymasterArguments& params, int64_t rootSid,
- int64_t fingerprintSid, int32_t* _aidl_return) override;
-
- ::android::binder::Status presentConfirmationPrompt(
- const ::android::sp<::android::IBinder>& listener, const ::android::String16& promptText,
- const ::std::vector<uint8_t>& extraData, const ::android::String16& locale,
- int32_t uiOptionsAsFlags, int32_t* _aidl_return) override;
- ::android::binder::Status
- cancelConfirmationPrompt(const ::android::sp<::android::IBinder>& listener,
- int32_t* _aidl_return) override;
- ::android::binder::Status isConfirmationPromptSupported(bool* _aidl_return) override;
-
- ::android::binder::Status onKeyguardVisibilityChanged(bool isShowing, int32_t userId,
- int32_t* _aidl_return) override;
-
- private:
- static const int32_t UID_SELF = -1;
-
- /**
- * Get the effective target uid for a binder operation that takes an
- * optional uid as the target.
- */
- uid_t getEffectiveUid(int32_t targetUid);
-
- /**
- * Check if the caller of the current binder method has the required
- * permission and if acting on other uids the grants to do so.
- */
- bool checkBinderPermission(perm_t permission, int32_t targetUid = UID_SELF);
-
- /**
- * Check if the caller of the current binder method has the required
- * permission and the target uid is the caller or the caller is system.
- */
- bool checkBinderPermissionSelfOrSystem(perm_t permission, int32_t targetUid);
-
- /**
- * Check if the caller of the current binder method has the required
- * permission or the target of the operation is the caller's uid. This is
- * for operation where the permission is only for cross-uid activity and all
- * uids are allowed to act on their own (ie: clearing all entries for a
- * given uid).
- */
- bool checkBinderPermissionOrSelfTarget(perm_t permission, int32_t targetUid);
-
- /**
- * Helper method to check that the caller has the required permission as
- * well as the keystore is in the unlocked state if checkUnlocked is true.
- *
- * Returns NO_ERROR on success, PERMISSION_DENIED on a permission error and
- * otherwise the state of keystore when not unlocked and checkUnlocked is
- * true.
- */
- KeyStoreServiceReturnCode checkBinderPermissionAndKeystoreState(perm_t permission,
- int32_t targetUid = -1,
- bool checkUnlocked = true);
-
- bool isKeystoreUnlocked(State state);
-
- /**
- * Check that all keymaster_key_param_t's provided by the application are
- * allowed. Any parameter that keystore adds itself should be disallowed here.
- */
- bool checkAllowedOperationParams(const hidl_vec<KeyParameter>& params);
-
- void addLegacyBeginParams(const android::String16& name, AuthorizationSet* params);
-
- KeyStoreServiceReturnCode doLegacySignVerify(const android::String16& name,
- const hidl_vec<uint8_t>& data,
- hidl_vec<uint8_t>* out,
- const hidl_vec<uint8_t>& signature,
- KeyPurpose purpose);
-
- /**
- * Adds a Confirmation Token to the key parameters if needed.
- */
- void appendConfirmationTokenIfNeeded(const KeyCharacteristics& keyCharacteristics,
- std::vector<KeyParameter>* params);
-
- sp<KeyStore> mKeyStore;
-};
-
-}; // namespace keystore
-
-#endif // KEYSTORE_KEYSTORE_SERVICE_H_
diff --git a/keystore/keyblob_utils.cpp b/keystore/keyblob_utils.cpp
deleted file mode 100644
index 6c2fac9..0000000
--- a/keystore/keyblob_utils.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#include <stdint.h>
-#include <string.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <keystore/keystore.h>
-
-/**
- * When a key is being migrated from a software keymaster implementation
- * to a hardware keymaster implementation, the first 4 bytes of the key_blob
- * given to the hardware implementation will be equal to SOFT_KEY_MAGIC.
- * The hardware implementation should import these PKCS#8 format keys which
- * are encoded like this:
- *
- * 4-byte SOFT_KEY_MAGIC
- *
- * 4-byte 32-bit integer big endian for public_key_length. This may be zero
- * length which indicates the public key should be derived from the
- * private key.
- *
- * public_key_length bytes of public key (may be empty)
- *
- * 4-byte 32-bit integer big endian for private_key_length
- *
- * private_key_length bytes of private key
- */
-static const uint8_t SOFT_KEY_MAGIC[] = { 'P', 'K', '#', '8' };
-
-size_t get_softkey_header_size() {
- return sizeof(SOFT_KEY_MAGIC);
-}
-
-uint8_t* add_softkey_header(uint8_t* key_blob, size_t key_blob_length) {
- if (key_blob_length < sizeof(SOFT_KEY_MAGIC)) {
- return nullptr;
- }
-
- memcpy(key_blob, SOFT_KEY_MAGIC, sizeof(SOFT_KEY_MAGIC));
-
- return key_blob + sizeof(SOFT_KEY_MAGIC);
-}
-
-bool is_softkey(const uint8_t* key_blob, const size_t key_blob_length) {
- if (key_blob_length < sizeof(SOFT_KEY_MAGIC)) {
- return false;
- }
-
- return !memcmp(key_blob, SOFT_KEY_MAGIC, sizeof(SOFT_KEY_MAGIC));
-}
diff --git a/keystore/keymaster_enforcement.cpp b/keystore/keymaster_enforcement.cpp
deleted file mode 100644
index a17cd94..0000000
--- a/keystore/keymaster_enforcement.cpp
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-#define LOG_TAG "keystore"
-
-#include "keymaster_enforcement.h"
-
-#include <assert.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <string.h>
-
-#include <openssl/evp.h>
-
-#include <hardware/hw_auth_token.h>
-#include <log/log.h>
-
-#include <list>
-
-#include <keystore/keystore_hidl_support.h>
-
-namespace keystore {
-
-bool is_public_key_algorithm(const AuthorizationSet& auth_set) {
- auto algorithm = auth_set.GetTagValue(TAG_ALGORITHM);
- return algorithm.isOk() &&
- (algorithm.value() == Algorithm::RSA || algorithm.value() == Algorithm::EC);
-}
-
-static ErrorCode authorized_purpose(const KeyPurpose purpose, const AuthorizationSet& auth_set) {
- switch (purpose) {
- case KeyPurpose::VERIFY:
- case KeyPurpose::ENCRYPT:
- case KeyPurpose::SIGN:
- case KeyPurpose::DECRYPT:
- if (auth_set.Contains(TAG_PURPOSE, purpose)) return ErrorCode::OK;
- return ErrorCode::INCOMPATIBLE_PURPOSE;
-
- default:
- return ErrorCode::UNSUPPORTED_PURPOSE;
- }
-}
-
-inline bool is_origination_purpose(KeyPurpose purpose) {
- return purpose == KeyPurpose::ENCRYPT || purpose == KeyPurpose::SIGN;
-}
-
-inline bool is_usage_purpose(KeyPurpose purpose) {
- return purpose == KeyPurpose::DECRYPT || purpose == KeyPurpose::VERIFY;
-}
-
-KeymasterEnforcement::KeymasterEnforcement(uint32_t max_access_time_map_size,
- uint32_t max_access_count_map_size)
- : access_time_map_(max_access_time_map_size), access_count_map_(max_access_count_map_size) {}
-
-KeymasterEnforcement::~KeymasterEnforcement() {
-}
-
-ErrorCode KeymasterEnforcement::AuthorizeOperation(const KeyPurpose purpose, const km_id_t keyid,
- const AuthorizationSet& auth_set,
- const AuthorizationSet& operation_params,
- const HardwareAuthToken& auth_token,
- uint64_t op_handle, bool is_begin_operation) {
- if (is_public_key_algorithm(auth_set)) {
- switch (purpose) {
- case KeyPurpose::ENCRYPT:
- case KeyPurpose::VERIFY:
- /* Public key operations are always authorized. */
- return ErrorCode::OK;
-
- case KeyPurpose::DECRYPT:
- case KeyPurpose::SIGN:
- break;
-
- case KeyPurpose::WRAP_KEY:
- return ErrorCode::INCOMPATIBLE_PURPOSE;
- };
- };
-
- if (is_begin_operation)
- return AuthorizeBegin(purpose, keyid, auth_set, operation_params, auth_token);
- else
- return AuthorizeUpdateOrFinish(auth_set, auth_token, op_handle);
-}
-
-// For update and finish the only thing to check is user authentication, and then only if it's not
-// timeout-based.
-ErrorCode KeymasterEnforcement::AuthorizeUpdateOrFinish(const AuthorizationSet& auth_set,
- const HardwareAuthToken& auth_token,
- uint64_t op_handle) {
- int auth_type_index = -1;
- for (size_t pos = 0; pos < auth_set.size(); ++pos) {
- switch (auth_set[pos].tag) {
- case Tag::NO_AUTH_REQUIRED:
- case Tag::AUTH_TIMEOUT:
- // If no auth is required or if auth is timeout-based, we have nothing to check.
- return ErrorCode::OK;
-
- case Tag::USER_AUTH_TYPE:
- auth_type_index = pos;
- break;
-
- default:
- break;
- }
- }
-
- // Note that at this point we should be able to assume that authentication is required, because
- // authentication is required if KM_TAG_NO_AUTH_REQUIRED is absent. However, there are legacy
- // keys which have no authentication-related tags, so we assume that absence is equivalent to
- // presence of KM_TAG_NO_AUTH_REQUIRED.
- //
- // So, if we found KM_TAG_USER_AUTH_TYPE or if we find KM_TAG_USER_SECURE_ID then authentication
- // is required. If we find neither, then we assume authentication is not required and return
- // success.
- bool authentication_required = (auth_type_index != -1);
- for (auto& param : auth_set) {
- auto user_secure_id = authorizationValue(TAG_USER_SECURE_ID, param);
- if (user_secure_id.isOk()) {
- authentication_required = true;
- int auth_timeout_index = -1;
- if (auth_token.mac.size() &&
- AuthTokenMatches(auth_set, auth_token, user_secure_id.value(), auth_type_index,
- auth_timeout_index, op_handle, false /* is_begin_operation */))
- return ErrorCode::OK;
- }
- }
-
- if (authentication_required) return ErrorCode::KEY_USER_NOT_AUTHENTICATED;
-
- return ErrorCode::OK;
-}
-
-ErrorCode KeymasterEnforcement::AuthorizeBegin(const KeyPurpose purpose, const km_id_t keyid,
- const AuthorizationSet& auth_set,
- const AuthorizationSet& operation_params,
- NullOr<const HardwareAuthToken&> auth_token) {
- // Find some entries that may be needed to handle KM_TAG_USER_SECURE_ID
- int auth_timeout_index = -1;
- int auth_type_index = -1;
- int no_auth_required_index = -1;
- for (size_t pos = 0; pos < auth_set.size(); ++pos) {
- switch (auth_set[pos].tag) {
- case Tag::AUTH_TIMEOUT:
- auth_timeout_index = pos;
- break;
- case Tag::USER_AUTH_TYPE:
- auth_type_index = pos;
- break;
- case Tag::NO_AUTH_REQUIRED:
- no_auth_required_index = pos;
- break;
- default:
- break;
- }
- }
-
- ErrorCode error = authorized_purpose(purpose, auth_set);
- if (error != ErrorCode::OK) return error;
-
- // If successful, and if key has a min time between ops, this will be set to the time limit
- uint32_t min_ops_timeout = UINT32_MAX;
-
- bool update_access_count = false;
- bool caller_nonce_authorized_by_key = false;
- bool authentication_required = false;
- bool auth_token_matched = false;
- bool unlocked_device_required = false;
- int32_t user_id = -1;
-
- for (auto& param : auth_set) {
-
- // KM_TAG_PADDING_OLD and KM_TAG_DIGEST_OLD aren't actually members of the enum, so we can't
- // switch on them. There's nothing to validate for them, though, so just ignore them.
- if (int32_t(param.tag) == KM_TAG_PADDING_OLD || int32_t(param.tag) == KM_TAG_DIGEST_OLD)
- continue;
-
- switch (param.tag) {
-
- case Tag::ACTIVE_DATETIME: {
- auto date = authorizationValue(TAG_ACTIVE_DATETIME, param);
- if (date.isOk() && !activation_date_valid(date.value()))
- return ErrorCode::KEY_NOT_YET_VALID;
- break;
- }
- case Tag::ORIGINATION_EXPIRE_DATETIME: {
- auto date = authorizationValue(TAG_ORIGINATION_EXPIRE_DATETIME, param);
- if (is_origination_purpose(purpose) && date.isOk() &&
- expiration_date_passed(date.value()))
- return ErrorCode::KEY_EXPIRED;
- break;
- }
- case Tag::USAGE_EXPIRE_DATETIME: {
- auto date = authorizationValue(TAG_USAGE_EXPIRE_DATETIME, param);
- if (is_usage_purpose(purpose) && date.isOk() && expiration_date_passed(date.value()))
- return ErrorCode::KEY_EXPIRED;
- break;
- }
- case Tag::MIN_SECONDS_BETWEEN_OPS: {
- auto min_ops_timeout = authorizationValue(TAG_MIN_SECONDS_BETWEEN_OPS, param);
- if (min_ops_timeout.isOk() && !MinTimeBetweenOpsPassed(min_ops_timeout.value(), keyid))
- return ErrorCode::KEY_RATE_LIMIT_EXCEEDED;
- break;
- }
- case Tag::MAX_USES_PER_BOOT: {
- auto max_users = authorizationValue(TAG_MAX_USES_PER_BOOT, param);
- update_access_count = true;
- if (max_users.isOk() && !MaxUsesPerBootNotExceeded(keyid, max_users.value()))
- return ErrorCode::KEY_MAX_OPS_EXCEEDED;
- break;
- }
- case Tag::USER_SECURE_ID:
- if (no_auth_required_index != -1) {
- // Key has both KM_TAG_USER_SECURE_ID and KM_TAG_NO_AUTH_REQUIRED
- return ErrorCode::INVALID_KEY_BLOB;
- }
-
- if (auth_timeout_index != -1) {
- auto secure_id = authorizationValue(TAG_USER_SECURE_ID, param);
- authentication_required = true;
- if (secure_id.isOk() && auth_token.isOk() &&
- AuthTokenMatches(auth_set, auth_token.value(), secure_id.value(),
- auth_type_index, auth_timeout_index, 0 /* op_handle */,
- true /* is_begin_operation */))
- auth_token_matched = true;
- }
- break;
-
- case Tag::USER_ID:
- user_id = authorizationValue(TAG_USER_ID, param).value();
- break;
-
- case Tag::CALLER_NONCE:
- caller_nonce_authorized_by_key = true;
- break;
-
- case Tag::UNLOCKED_DEVICE_REQUIRED:
- unlocked_device_required = true;
- break;
-
- /* Tags should never be in key auths. */
- case Tag::INVALID:
- case Tag::ROOT_OF_TRUST:
- case Tag::APPLICATION_DATA:
- case Tag::ATTESTATION_CHALLENGE:
- case Tag::ATTESTATION_APPLICATION_ID:
- case Tag::ATTESTATION_ID_BRAND:
- case Tag::ATTESTATION_ID_DEVICE:
- case Tag::ATTESTATION_ID_PRODUCT:
- case Tag::ATTESTATION_ID_SERIAL:
- case Tag::ATTESTATION_ID_IMEI:
- case Tag::ATTESTATION_ID_MEID:
- case Tag::ATTESTATION_ID_MANUFACTURER:
- case Tag::ATTESTATION_ID_MODEL:
- return ErrorCode::INVALID_KEY_BLOB;
-
- /* Tags used for cryptographic parameters in keygen. Nothing to enforce. */
- case Tag::PURPOSE:
- case Tag::ALGORITHM:
- case Tag::KEY_SIZE:
- case Tag::BLOCK_MODE:
- case Tag::DIGEST:
- case Tag::MAC_LENGTH:
- case Tag::PADDING:
- case Tag::NONCE:
- case Tag::MIN_MAC_LENGTH:
- case Tag::EC_CURVE:
-
- /* Tags not used for operations. */
- case Tag::BLOB_USAGE_REQUIREMENTS:
-
- /* Algorithm specific parameters not used for access control. */
- case Tag::RSA_PUBLIC_EXPONENT:
-
- /* Informational tags. */
- case Tag::CREATION_DATETIME:
- case Tag::ORIGIN:
- case Tag::ROLLBACK_RESISTANCE:
-
- /* Tags handled when KM_TAG_USER_SECURE_ID is handled */
- case Tag::NO_AUTH_REQUIRED:
- case Tag::USER_AUTH_TYPE:
- case Tag::AUTH_TIMEOUT:
-
- /* Tag to provide data to operations. */
- case Tag::ASSOCIATED_DATA:
-
- /* Tags that are implicitly verified by secure side */
- case Tag::APPLICATION_ID:
- case Tag::BOOT_PATCHLEVEL:
- case Tag::OS_PATCHLEVEL:
- case Tag::OS_VERSION:
- case Tag::TRUSTED_USER_PRESENCE_REQUIRED:
- case Tag::VENDOR_PATCHLEVEL:
-
- /* TODO(swillden): Handle these */
- case Tag::INCLUDE_UNIQUE_ID:
- case Tag::UNIQUE_ID:
- case Tag::RESET_SINCE_ID_ROTATION:
- case Tag::ALLOW_WHILE_ON_BODY:
- case Tag::HARDWARE_TYPE:
- case Tag::TRUSTED_CONFIRMATION_REQUIRED:
- case Tag::CONFIRMATION_TOKEN:
- break;
-
- case Tag::BOOTLOADER_ONLY:
- return ErrorCode::INVALID_KEY_BLOB;
- }
- }
-
- if (unlocked_device_required && is_device_locked(user_id)) {
- switch (purpose) {
- case KeyPurpose::ENCRYPT:
- case KeyPurpose::VERIFY:
- /* These are okay */
- break;
- case KeyPurpose::DECRYPT:
- case KeyPurpose::SIGN:
- case KeyPurpose::WRAP_KEY:
- return ErrorCode::DEVICE_LOCKED;
- };
- }
-
- if (authentication_required && !auth_token_matched) {
- ALOGE("Auth required but no matching auth token found");
- return ErrorCode::KEY_USER_NOT_AUTHENTICATED;
- }
-
- if (!caller_nonce_authorized_by_key && is_origination_purpose(purpose) &&
- operation_params.Contains(Tag::NONCE))
- return ErrorCode::CALLER_NONCE_PROHIBITED;
-
- if (min_ops_timeout != UINT32_MAX) {
- if (!access_time_map_.UpdateKeyAccessTime(keyid, get_current_time(), min_ops_timeout)) {
- ALOGE("Rate-limited keys table full. Entries will time out.");
- return ErrorCode::TOO_MANY_OPERATIONS;
- }
- }
-
- if (update_access_count) {
- if (!access_count_map_.IncrementKeyAccessCount(keyid)) {
- ALOGE("Usage count-limited keys table full, until reboot.");
- return ErrorCode::TOO_MANY_OPERATIONS;
- }
- }
-
- return ErrorCode::OK;
-}
-
-class EvpMdCtx {
- public:
- EvpMdCtx() { EVP_MD_CTX_init(&ctx_); }
- ~EvpMdCtx() { EVP_MD_CTX_cleanup(&ctx_); }
-
- EVP_MD_CTX* get() { return &ctx_; }
-
- private:
- EVP_MD_CTX ctx_;
-};
-
-/* static */
-std::optional<km_id_t> KeymasterEnforcement::CreateKeyId(const hidl_vec<uint8_t>& key_blob) {
- EvpMdCtx ctx;
- km_id_t keyid;
-
- uint8_t hash[EVP_MAX_MD_SIZE];
- unsigned int hash_len;
- if (EVP_DigestInit_ex(ctx.get(), EVP_sha256(), nullptr /* ENGINE */) &&
- EVP_DigestUpdate(ctx.get(), &key_blob[0], key_blob.size()) &&
- EVP_DigestFinal_ex(ctx.get(), hash, &hash_len)) {
- assert(hash_len >= sizeof(keyid));
- memcpy(&keyid, hash, sizeof(keyid));
- return keyid;
- }
-
- return {};
-}
-
-bool KeymasterEnforcement::MinTimeBetweenOpsPassed(uint32_t min_time_between, const km_id_t keyid) {
- uint32_t last_access_time;
- if (!access_time_map_.LastKeyAccessTime(keyid, &last_access_time)) return true;
- return min_time_between <= static_cast<int64_t>(get_current_time()) - last_access_time;
-}
-
-bool KeymasterEnforcement::MaxUsesPerBootNotExceeded(const km_id_t keyid, uint32_t max_uses) {
- uint32_t key_access_count;
- if (!access_count_map_.KeyAccessCount(keyid, &key_access_count)) return true;
- return key_access_count < max_uses;
-}
-
-template <typename IntType, uint32_t byteOrder> struct choose_hton;
-
-template <typename IntType> struct choose_hton<IntType, __ORDER_LITTLE_ENDIAN__> {
- inline static IntType hton(const IntType& value) {
- IntType result = 0;
- const unsigned char* inbytes = reinterpret_cast<const unsigned char*>(&value);
- unsigned char* outbytes = reinterpret_cast<unsigned char*>(&result);
- for (int i = sizeof(IntType) - 1; i >= 0; --i) {
- *(outbytes++) = inbytes[i];
- }
- return result;
- }
-};
-
-template <typename IntType> struct choose_hton<IntType, __ORDER_BIG_ENDIAN__> {
- inline static IntType hton(const IntType& value) { return value; }
-};
-
-template <typename IntType> inline IntType hton(const IntType& value) {
- return choose_hton<IntType, __BYTE_ORDER__>::hton(value);
-}
-
-template <typename IntType> inline IntType ntoh(const IntType& value) {
- // same operation and hton
- return choose_hton<IntType, __BYTE_ORDER__>::hton(value);
-}
-
-bool KeymasterEnforcement::AuthTokenMatches(const AuthorizationSet& auth_set,
- const HardwareAuthToken& auth_token,
- const uint64_t user_secure_id,
- const int auth_type_index, const int auth_timeout_index,
- const uint64_t op_handle,
- bool is_begin_operation) const {
- assert(auth_type_index < static_cast<int>(auth_set.size()));
- assert(auth_timeout_index < static_cast<int>(auth_set.size()));
-
- if (!ValidateTokenSignature(auth_token)) {
- ALOGE("Auth token signature invalid");
- return false;
- }
-
- if (auth_timeout_index == -1 && op_handle && op_handle != auth_token.challenge) {
- ALOGE("Auth token has the challenge %" PRIu64 ", need %" PRIu64, auth_token.challenge,
- op_handle);
- return false;
- }
-
- if (user_secure_id != auth_token.userId && user_secure_id != auth_token.authenticatorId) {
- ALOGI("Auth token SIDs %" PRIu64 " and %" PRIu64 " do not match key SID %" PRIu64,
- auth_token.userId, auth_token.authenticatorId, user_secure_id);
- return false;
- }
-
- if (auth_type_index < 0 || auth_type_index > static_cast<int>(auth_set.size())) {
- ALOGE("Auth required but no auth type found");
- return false;
- }
-
- assert(auth_set[auth_type_index].tag == TAG_USER_AUTH_TYPE);
- auto key_auth_type_mask = authorizationValue(TAG_USER_AUTH_TYPE, auth_set[auth_type_index]);
- if (!key_auth_type_mask.isOk()) return false;
-
- if ((uint32_t(key_auth_type_mask.value()) & auth_token.authenticatorType) == 0) {
- ALOGE("Key requires match of auth type mask 0%uo, but token contained 0%uo",
- key_auth_type_mask.value(), auth_token.authenticatorType);
- return false;
- }
-
- if (auth_timeout_index != -1 && is_begin_operation) {
- assert(auth_set[auth_timeout_index].tag == TAG_AUTH_TIMEOUT);
- auto auth_token_timeout =
- authorizationValue(TAG_AUTH_TIMEOUT, auth_set[auth_timeout_index]);
- if (!auth_token_timeout.isOk()) return false;
-
- if (auth_token_timed_out(auth_token, auth_token_timeout.value())) {
- ALOGE("Auth token has timed out");
- return false;
- }
- }
-
- // Survived the whole gauntlet. We have authentage!
- return true;
-}
-
-bool AccessTimeMap::LastKeyAccessTime(km_id_t keyid, uint32_t* last_access_time) const {
- std::lock_guard<std::mutex> lock(list_lock_);
- for (auto& entry : last_access_list_)
- if (entry.keyid == keyid) {
- *last_access_time = entry.access_time;
- return true;
- }
- return false;
-}
-
-bool AccessTimeMap::UpdateKeyAccessTime(km_id_t keyid, uint32_t current_time, uint32_t timeout) {
- std::lock_guard<std::mutex> lock(list_lock_);
- for (auto iter = last_access_list_.begin(); iter != last_access_list_.end();) {
- if (iter->keyid == keyid) {
- iter->access_time = current_time;
- return true;
- }
-
- // Expire entry if possible.
- assert(current_time >= iter->access_time);
- if (current_time - iter->access_time >= iter->timeout)
- iter = last_access_list_.erase(iter);
- else
- ++iter;
- }
-
- if (last_access_list_.size() >= max_size_) return false;
-
- AccessTime new_entry;
- new_entry.keyid = keyid;
- new_entry.access_time = current_time;
- new_entry.timeout = timeout;
- last_access_list_.push_front(new_entry);
- return true;
-}
-
-bool AccessCountMap::KeyAccessCount(km_id_t keyid, uint32_t* count) const {
- std::lock_guard<std::mutex> lock(list_lock_);
- for (auto& entry : access_count_list_)
- if (entry.keyid == keyid) {
- *count = entry.access_count;
- return true;
- }
- return false;
-}
-
-bool AccessCountMap::IncrementKeyAccessCount(km_id_t keyid) {
- std::lock_guard<std::mutex> lock(list_lock_);
- for (auto& entry : access_count_list_)
- if (entry.keyid == keyid) {
- // Note that the 'if' below will always be true because KM_TAG_MAX_USES_PER_BOOT is a
- // uint32_t, and as soon as entry.access_count reaches the specified maximum value
- // operation requests will be rejected and access_count won't be incremented any more.
- // And, besides, UINT64_MAX is huge. But we ensure that it doesn't wrap anyway, out of
- // an abundance of caution.
- if (entry.access_count < UINT64_MAX) ++entry.access_count;
- return true;
- }
-
- if (access_count_list_.size() >= max_size_) return false;
-
- AccessCount new_entry;
- new_entry.keyid = keyid;
- new_entry.access_count = 1;
- access_count_list_.push_front(new_entry);
- return true;
-}
-}; /* namespace keystore */
diff --git a/keystore/keymaster_enforcement.h b/keystore/keymaster_enforcement.h
deleted file mode 100644
index 9bfb225..0000000
--- a/keystore/keymaster_enforcement.h
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-#ifndef KEYSTORE_KEYMASTER_ENFORCEMENT_H
-#define KEYSTORE_KEYMASTER_ENFORCEMENT_H
-
-#include <stdio.h>
-
-#include <keystore/keymaster_types.h>
-
-#include <list>
-#include <mutex>
-#include <optional>
-
-namespace keystore {
-
-typedef uint64_t km_id_t;
-
-class KeymasterEnforcementContext {
- public:
- virtual ~KeymasterEnforcementContext() {}
- /*
- * Get current time.
- */
-};
-
-class AccessTimeMap {
- public:
- explicit AccessTimeMap(uint32_t max_size) : max_size_(max_size) {}
-
- /* If the key is found, returns true and fills \p last_access_time. If not found returns
- * false. */
- bool LastKeyAccessTime(km_id_t keyid, uint32_t* last_access_time) const;
-
- /* Updates the last key access time with the currentTime parameter. Adds the key if
- * needed, returning false if key cannot be added because list is full. */
- bool UpdateKeyAccessTime(km_id_t keyid, uint32_t current_time, uint32_t timeout);
-
- private:
- mutable std::mutex list_lock_;
- struct AccessTime {
- km_id_t keyid;
- uint32_t access_time;
- uint32_t timeout;
- };
- std::list<AccessTime> last_access_list_;
- const uint32_t max_size_;
-};
-
-class AccessCountMap {
- public:
- explicit AccessCountMap(uint32_t max_size) : max_size_(max_size) {}
-
- /* If the key is found, returns true and fills \p count. If not found returns
- * false. */
- bool KeyAccessCount(km_id_t keyid, uint32_t* count) const;
-
- /* Increments key access count, adding an entry if the key has never been used. Returns
- * false if the list has reached maximum size. */
- bool IncrementKeyAccessCount(km_id_t keyid);
-
- private:
- mutable std::mutex list_lock_;
- struct AccessCount {
- km_id_t keyid;
- uint64_t access_count;
- };
- std::list<AccessCount> access_count_list_;
- const uint32_t max_size_;
-};
-
-class KeymasterEnforcement {
- public:
- /**
- * Construct a KeymasterEnforcement.
- */
- KeymasterEnforcement(uint32_t max_access_time_map_size, uint32_t max_access_count_map_size);
- virtual ~KeymasterEnforcement();
-
- /**
- * Iterates through the authorization set and returns the corresponding keymaster error. Will
- * return KM_ERROR_OK if all criteria is met for the given purpose in the authorization set with
- * the given operation params and handle. Used for encrypt, decrypt sign, and verify.
- */
- ErrorCode AuthorizeOperation(const KeyPurpose purpose, const km_id_t keyid,
- const AuthorizationSet& auth_set,
- const AuthorizationSet& operation_params,
- const HardwareAuthToken& auth_token, uint64_t op_handle,
- bool is_begin_operation);
-
- /**
- * Iterates through the authorization set and returns the corresponding keymaster error. Will
- * return KM_ERROR_OK if all criteria is met for the given purpose in the authorization set with
- * the given operation params. Used for encrypt, decrypt sign, and verify.
- */
- ErrorCode AuthorizeBegin(const KeyPurpose purpose, const km_id_t keyid,
- const AuthorizationSet& auth_set,
- const AuthorizationSet& operation_params,
- NullOr<const HardwareAuthToken&> auth_token);
-
- /**
- * Iterates through the authorization set and returns the corresponding keymaster error. Will
- * return KM_ERROR_OK if all criteria is met for the given purpose in the authorization set with
- * the given operation params and handle. Used for encrypt, decrypt sign, and verify.
- */
- ErrorCode AuthorizeUpdate(const AuthorizationSet& auth_set, const HardwareAuthToken& auth_token,
- uint64_t op_handle) {
- return AuthorizeUpdateOrFinish(auth_set, auth_token, op_handle);
- }
-
- /**
- * Iterates through the authorization set and returns the corresponding keymaster error. Will
- * return KM_ERROR_OK if all criteria is met for the given purpose in the authorization set with
- * the given operation params and handle. Used for encrypt, decrypt sign, and verify.
- */
- ErrorCode AuthorizeFinish(const AuthorizationSet& auth_set, const HardwareAuthToken& auth_token,
- uint64_t op_handle) {
- return AuthorizeUpdateOrFinish(auth_set, auth_token, op_handle);
- }
-
- /**
- * Creates a key ID for use in subsequent calls to AuthorizeOperation. Clients needn't use this
- * method of creating key IDs, as long as they use something consistent and unique. This method
- * hashes the key blob.
- *
- * Returns false if an error in the crypto library prevents creation of an ID.
- */
- static std::optional<km_id_t> CreateKeyId(const hidl_vec<uint8_t>& key_blob);
-
- //
- // Methods that must be implemented by subclasses
- //
- // The time-related methods address the fact that different enforcement contexts may have
- // different time-related capabilities. In particular:
- //
- // - They may or may not be able to check dates against real-world clocks.
- //
- // - They may or may not be able to check timestampls against authentication trustlets (minters
- // of hw_auth_token_t structs).
- //
- // - They must have some time source for relative times, but may not be able to provide more
- // than reliability and monotonicity.
-
- /*
- * Returns true if the specified activation date has passed, or if activation cannot be
- * enforced.
- */
- virtual bool activation_date_valid(uint64_t activation_date) const = 0;
-
- /*
- * Returns true if the specified expiration date has passed. Returns false if it has not, or if
- * expiration cannot be enforced.
- */
- virtual bool expiration_date_passed(uint64_t expiration_date) const = 0;
-
- /*
- * Returns true if the specified auth_token is older than the specified timeout.
- */
- virtual bool auth_token_timed_out(const HardwareAuthToken& token, uint32_t timeout) const = 0;
-
- /*
- * Get current time in seconds from some starting point. This value is used to compute relative
- * times between events. It must be monotonically increasing, and must not skip or lag. It
- * need not have any relation to any external time standard (other than the duration of
- * "second").
- *
- * On POSIX systems, it's recommented to use clock_gettime(CLOCK_MONOTONIC, ...) to implement
- * this method.
- */
- virtual uint32_t get_current_time() const = 0;
-
- /*
- * Returns true if the specified auth_token has a valid signature, or if signature validation is
- * not available.
- */
- virtual bool ValidateTokenSignature(const HardwareAuthToken& token) const = 0;
-
- /*
- * Returns true if the device screen is currently locked for the specified user.
- */
- virtual bool is_device_locked(int32_t userId) const = 0;
-
- private:
- ErrorCode AuthorizeUpdateOrFinish(const AuthorizationSet& auth_set,
- const HardwareAuthToken& auth_token, uint64_t op_handle);
-
- bool MinTimeBetweenOpsPassed(uint32_t min_time_between, const km_id_t keyid);
- bool MaxUsesPerBootNotExceeded(const km_id_t keyid, uint32_t max_uses);
- bool AuthTokenMatches(const AuthorizationSet& auth_set, const HardwareAuthToken& auth_token,
- const uint64_t user_secure_id, const int auth_type_index,
- const int auth_timeout_index, const uint64_t op_handle,
- bool is_begin_operation) const;
-
- AccessTimeMap access_time_map_;
- AccessCountMap access_count_map_;
-};
-
-}; /* namespace keystore */
-
-#endif // KEYSTORE_KEYMASTER_ENFORCEMENT_H
diff --git a/keystore/keymaster_worker.cpp b/keystore/keymaster_worker.cpp
deleted file mode 100644
index cbb184c..0000000
--- a/keystore/keymaster_worker.cpp
+++ /dev/null
@@ -1,1144 +0,0 @@
-/*
-**
-** Copyright 2018, 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.
-*/
-#define LOG_TAG "keymaster_worker"
-
-#include "keymaster_worker.h"
-
-#include "keystore_utils.h"
-
-#include <android-base/logging.h>
-
-#include <log/log_event_list.h>
-
-#include <private/android_logger.h>
-
-#include "KeyStore.h"
-#include "keymaster_enforcement.h"
-
-#include "key_creation_log_handler.h"
-#include "keystore_utils.h"
-
-#include <chrono>
-
-namespace keystore {
-
-using namespace std::chrono;
-
-constexpr size_t kMaxOperations = 15;
-
-using AndroidKeymasterArguments = android::security::keymaster::KeymasterArguments;
-using android::security::keymaster::ExportResult;
-using android::security::keymaster::operationFailed;
-using android::security::keymaster::OperationResult;
-
-Worker::Worker() {}
-Worker::~Worker() {
- std::unique_lock<std::mutex> lock(pending_requests_mutex_);
- terminate_ = true;
- pending_requests_cond_var_.notify_all();
- pending_requests_cond_var_.wait(lock, [this] { return !running_; });
-}
-void Worker::addRequest(WorkerTask request) {
- std::unique_lock<std::mutex> lock(pending_requests_mutex_);
- bool start_thread = !running_;
- running_ = true;
- pending_requests_.push(std::move(request));
- lock.unlock();
- pending_requests_cond_var_.notify_all();
- if (start_thread) {
- auto worker = std::thread([this] {
- std::unique_lock<std::mutex> lock(pending_requests_mutex_);
- while (running_) {
- // Wait for 30s if the request queue is empty, then kill die.
- // Die immediately if termiate_ was set which happens in the destructor.
- auto status = pending_requests_cond_var_.wait_for(
- lock, 30s, [this]() { return !pending_requests_.empty() || terminate_; });
- if (status && !terminate_) {
- auto request = std::move(pending_requests_.front());
- lock.unlock();
- request();
- lock.lock();
- pending_requests_.pop();
- } else {
- running_ = false;
- }
- pending_requests_cond_var_.notify_all();
- }
- });
- worker.detach();
- }
-}
-
-KeymasterWorker::KeymasterWorker(sp<Keymaster> keymasterDevice, KeyStore* keyStore,
- SecurityLevel internalSecurityLevel)
- : keymasterDevice_(std::move(keymasterDevice)), operationMap_(keyStore), keyStore_(keyStore),
- internalSecurityLevel_(internalSecurityLevel) {
- // make sure that hal version is cached.
- if (keymasterDevice_) keymasterDevice_->halVersion();
-}
-
-void KeymasterWorker::logIfKeymasterVendorError(ErrorCode ec) const {
- keymasterDevice_->logIfKeymasterVendorError(ec);
-}
-
-void KeymasterWorker::deleteOldKeyOnUpgrade(const LockedKeyBlobEntry& blobfile, Blob keyBlob) {
- // if we got the blob successfully, we try and delete it from the keymaster device
- auto& dev = keymasterDevice_;
- uid_t uid = blobfile->uid();
- const auto& alias = blobfile->alias();
-
- if (keyBlob.getType() == ::TYPE_KEYMASTER_10) {
- auto ret = KS_HANDLE_HIDL_ERROR(dev, dev->deleteKey(blob2hidlVec(keyBlob)));
- // A device doesn't have to implement delete_key.
- bool success = ret == ErrorCode::OK || ret == ErrorCode::UNIMPLEMENTED;
- if (__android_log_security()) {
- android_log_event_list(SEC_TAG_KEY_DESTROYED)
- << int32_t(success) << alias << int32_t(uid) << LOG_ID_SECURITY;
- }
- if (!success) {
- LOG(ERROR) << "Keymaster delete for key " << alias << " of uid " << uid << " failed";
- }
- }
-}
-
-std::tuple<KeyStoreServiceReturnCode, Blob>
-KeymasterWorker::upgradeKeyBlob(const LockedKeyBlobEntry& lockedEntry,
- const AuthorizationSet& params) {
- LOG(INFO) << "upgradeKeyBlob " << lockedEntry->alias() << " " << (uint32_t)lockedEntry->uid();
-
- std::tuple<KeyStoreServiceReturnCode, Blob> result;
-
- auto userState = keyStore_->getUserStateDB().getUserStateByUid(lockedEntry->uid());
-
- Blob& blob = std::get<1>(result);
- KeyStoreServiceReturnCode& error = std::get<0>(result);
-
- Blob charBlob;
- ResponseCode rc;
-
- std::tie(rc, blob, charBlob) =
- lockedEntry.readBlobs(userState->getEncryptionKey(), userState->getState());
-
- userState = {};
-
- if (rc != ResponseCode::NO_ERROR) {
- return error = rc, result;
- }
-
- auto hidlKey = blob2hidlVec(blob);
- auto& dev = keymasterDevice_;
-
- auto hidlCb = [&](ErrorCode ret, const ::std::vector<uint8_t>& upgradedKeyBlob) {
- dev->logIfKeymasterVendorError(ret);
- error = ret;
- if (!error.isOk()) {
- if (error == ErrorCode::INVALID_KEY_BLOB) {
- log_key_integrity_violation(lockedEntry->alias().c_str(), lockedEntry->uid());
- }
- return;
- }
-
- Blob newBlob(&upgradedKeyBlob[0], upgradedKeyBlob.size(), nullptr /* info */,
- 0 /* infoLength */, ::TYPE_KEYMASTER_10);
- newBlob.setSecurityLevel(blob.getSecurityLevel());
- newBlob.setEncrypted(blob.isEncrypted());
- newBlob.setSuperEncrypted(blob.isSuperEncrypted());
- newBlob.setCriticalToDeviceEncryption(blob.isCriticalToDeviceEncryption());
-
- error = keyStore_->put(lockedEntry, newBlob, charBlob);
- if (!error.isOk()) {
- ALOGI("upgradeKeyBlob keystore->put failed %d", error.getErrorCode());
- return;
- }
-
- deleteOldKeyOnUpgrade(lockedEntry, std::move(blob));
- blob = std::move(newBlob);
- };
-
- KeyStoreServiceReturnCode error2;
- error2 = KS_HANDLE_HIDL_ERROR(dev, dev->upgradeKey(hidlKey, params.hidl_data(), hidlCb));
- if (!error2.isOk()) {
- return error = error2, result;
- }
-
- return result;
-}
-
-std::tuple<KeyStoreServiceReturnCode, KeyCharacteristics, Blob, Blob>
-KeymasterWorker::createKeyCharacteristicsCache(const LockedKeyBlobEntry& lockedEntry,
- const hidl_vec<uint8_t>& clientId,
- const hidl_vec<uint8_t>& appData, Blob keyBlob,
- Blob charBlob) {
- std::tuple<KeyStoreServiceReturnCode, KeyCharacteristics, Blob, Blob> result;
-
-#if __cplusplus == 201703L
- auto& [rc, resultCharacteristics, outBlob, charOutBlob] = result;
-#else
- KeyStoreServiceReturnCode& rc = std::get<0>(result);
- KeyCharacteristics& resultCharacteristics = std::get<1>(result);
- Blob& outBlob = std::get<2>(result);
- Blob& charOutBlob = std::get<3>(result);
-#endif
-
- rc = ResponseCode::SYSTEM_ERROR;
- if (!keyBlob) return result;
- auto hidlKeyBlob = blob2hidlVec(keyBlob);
- auto& dev = keymasterDevice_;
-
- KeyStoreServiceReturnCode error;
-
- AuthorizationSet hwEnforced, swEnforced;
- bool success = true;
-
- if (charBlob) {
- std::tie(success, hwEnforced, swEnforced) = charBlob.getKeyCharacteristics();
- }
- if (!success) {
- LOG(ERROR) << "Failed to read cached key characteristics";
- return rc = ResponseCode::SYSTEM_ERROR, result;
- }
-
- auto hidlCb = [&](ErrorCode ret, const KeyCharacteristics& keyCharacteristics) {
- dev->logIfKeymasterVendorError(ret);
- error = ret;
- if (!error.isOk()) {
- if (error == ErrorCode::INVALID_KEY_BLOB) {
- log_key_integrity_violation(lockedEntry->alias().c_str(), lockedEntry->uid());
- }
- return;
- }
-
- // Replace the sw_enforced set with those persisted to disk, minus hw_enforced
- AuthorizationSet softwareEnforced = keyCharacteristics.softwareEnforced;
- hwEnforced = keyCharacteristics.hardwareEnforced;
- swEnforced.Union(softwareEnforced);
- softwareEnforced.Subtract(hwEnforced);
-
- // We only get the characteristics from keymaster if there was no cache file or the
- // the chach file was a legacy cache file. So lets write a new cache file for the next time.
- Blob newCharBlob;
- success = newCharBlob.putKeyCharacteristics(hwEnforced, swEnforced);
- if (!success) {
- error = ResponseCode::SYSTEM_ERROR;
- LOG(ERROR) << "Failed to serialize cached key characteristics";
- return;
- }
-
- error = keyStore_->put(lockedEntry, {}, newCharBlob);
- if (!error.isOk()) {
- ALOGE("Failed to write key characteristics cache");
- return;
- }
- charBlob = std::move(newCharBlob);
- };
-
- if (!charBlob || charBlob.getType() == TYPE_KEY_CHARACTERISTICS) {
- // this updates the key characteristics cache file to the new format or creates one in
- // in the first place
- rc = KS_HANDLE_HIDL_ERROR(
- dev, dev->getKeyCharacteristics(hidlKeyBlob, clientId, appData, hidlCb));
- if (!rc.isOk()) {
- return result;
- }
-
- if (error == ErrorCode::KEY_REQUIRES_UPGRADE) {
- AuthorizationSet upgradeParams;
- if (clientId.size()) {
- upgradeParams.push_back(TAG_APPLICATION_ID, clientId);
- }
- if (appData.size()) {
- upgradeParams.push_back(TAG_APPLICATION_DATA, appData);
- }
- std::tie(rc, keyBlob) = upgradeKeyBlob(lockedEntry, upgradeParams);
- if (!rc.isOk()) {
- return result;
- }
-
- auto upgradedHidlKeyBlob = blob2hidlVec(keyBlob);
-
- rc = KS_HANDLE_HIDL_ERROR(
- dev, dev->getKeyCharacteristics(upgradedHidlKeyBlob, clientId, appData, hidlCb));
- if (!rc.isOk()) {
- return result;
- }
- }
- }
-
- resultCharacteristics.hardwareEnforced = hwEnforced.hidl_data();
- resultCharacteristics.softwareEnforced = swEnforced.hidl_data();
-
- outBlob = std::move(keyBlob);
- charOutBlob = std::move(charBlob);
- rc = error;
- return result;
-}
-
-/**
- * Get the auth token for this operation from the auth token table.
- *
- * Returns ResponseCode::NO_ERROR if the auth token was set or none was required.
- * ::OP_AUTH_NEEDED if it is a per op authorization, no
- * authorization token exists for that operation and
- * failOnTokenMissing is false.
- * KM_ERROR_KEY_USER_NOT_AUTHENTICATED if there is no valid auth
- * token for the operation
- */
-std::pair<KeyStoreServiceReturnCode, HardwareAuthToken>
-KeymasterWorker::getAuthToken(const KeyCharacteristics& characteristics, uint64_t handle,
- KeyPurpose purpose, bool failOnTokenMissing) {
-
- AuthorizationSet allCharacteristics(characteristics.softwareEnforced);
- allCharacteristics.append(characteristics.hardwareEnforced.begin(),
- characteristics.hardwareEnforced.end());
-
- HardwareAuthToken authToken;
- AuthTokenTable::Error err;
- std::tie(err, authToken) = keyStore_->getAuthTokenTable().FindAuthorization(
- allCharacteristics, static_cast<KeyPurpose>(purpose), handle);
-
- KeyStoreServiceReturnCode rc;
-
- switch (err) {
- case AuthTokenTable::OK:
- case AuthTokenTable::AUTH_NOT_REQUIRED:
- rc = ResponseCode::NO_ERROR;
- break;
-
- case AuthTokenTable::AUTH_TOKEN_NOT_FOUND:
- case AuthTokenTable::AUTH_TOKEN_EXPIRED:
- case AuthTokenTable::AUTH_TOKEN_WRONG_SID:
- ALOGE("getAuthToken failed: %d", err); // STOPSHIP: debug only, to be removed
- rc = ErrorCode::KEY_USER_NOT_AUTHENTICATED;
- break;
-
- case AuthTokenTable::OP_HANDLE_REQUIRED:
- rc = failOnTokenMissing ? KeyStoreServiceReturnCode(ErrorCode::KEY_USER_NOT_AUTHENTICATED)
- : KeyStoreServiceReturnCode(ResponseCode::OP_AUTH_NEEDED);
- break;
-
- default:
- ALOGE("Unexpected FindAuthorization return value %d", err);
- rc = ErrorCode::INVALID_ARGUMENT;
- }
-
- return {rc, std::move(authToken)};
-}
-
-KeyStoreServiceReturnCode KeymasterWorker::abort(const sp<IBinder>& token,
- ResponseCode reason_for_abort) {
- auto op = operationMap_.removeOperation(token, false /* wasOpSuccessful */,
- static_cast<int32_t>(reason_for_abort));
- if (op) {
- keyStore_->getAuthTokenTable().MarkCompleted(op->handle);
- return KS_HANDLE_HIDL_ERROR(keymasterDevice_, keymasterDevice_->abort(op->handle));
- } else {
- return ErrorCode::INVALID_OPERATION_HANDLE;
- }
-}
-
-/**
- * Prune the oldest pruneable operation.
- */
-bool KeymasterWorker::pruneOperation() {
- sp<IBinder> oldest = operationMap_.getOldestPruneableOperation();
- ALOGD("Trying to prune operation %p", oldest.get());
- size_t op_count_before_abort = operationMap_.getOperationCount();
- // We mostly ignore errors from abort() because all we care about is whether at least
- // one operation has been removed.
- auto rc = abort(oldest, ResponseCode::PRUNED);
- keyStore_->removeOperationDevice(oldest);
- if (operationMap_.getOperationCount() >= op_count_before_abort) {
- ALOGE("Failed to abort pruneable operation %p, error: %d", oldest.get(), rc.getErrorCode());
- return false;
- }
- return true;
-}
-
-// My IDE defines "CAPTURE_MOVE(x) x" because it does not understand generalized lambda captures.
-// It should never be redefined by a build system though.
-#ifndef CAPTURE_MOVE
-#define CAPTURE_MOVE(x) x = std::move(x)
-#endif
-
-void KeymasterWorker::begin(LockedKeyBlobEntry lockedEntry, sp<IBinder> appToken, Blob keyBlob,
- Blob charBlob, bool pruneable, KeyPurpose purpose,
- AuthorizationSet opParams, hidl_vec<uint8_t> entropy,
- worker_begin_cb worker_cb) {
-
- Worker::addRequest([this, CAPTURE_MOVE(lockedEntry), CAPTURE_MOVE(appToken),
- CAPTURE_MOVE(keyBlob), CAPTURE_MOVE(charBlob), pruneable, purpose,
- CAPTURE_MOVE(opParams), CAPTURE_MOVE(entropy),
- CAPTURE_MOVE(worker_cb)]() mutable {
- // Concurrently executed
-
- auto& dev = keymasterDevice_;
-
- KeyCharacteristics characteristics;
-
- {
- hidl_vec<uint8_t> clientId;
- hidl_vec<uint8_t> appData;
- for (const auto& param : opParams) {
- if (param.tag == Tag::APPLICATION_ID) {
- clientId = authorizationValue(TAG_APPLICATION_ID, param).value();
- } else if (param.tag == Tag::APPLICATION_DATA) {
- appData = authorizationValue(TAG_APPLICATION_DATA, param).value();
- }
- }
- KeyStoreServiceReturnCode error;
- std::tie(error, characteristics, keyBlob, charBlob) = createKeyCharacteristicsCache(
- lockedEntry, clientId, appData, std::move(keyBlob), std::move(charBlob));
- if (!error.isOk()) {
- worker_cb(operationFailed(error));
- return;
- }
- }
-
- KeyStoreServiceReturnCode rc, authRc;
- HardwareAuthToken authToken;
- std::tie(authRc, authToken) = getAuthToken(characteristics, 0 /* no challenge */, purpose,
- /*failOnTokenMissing*/ false);
-
- // If per-operation auth is needed we need to begin the operation and
- // the client will need to authorize that operation before calling
- // update. Any other auth issues stop here.
- if (!authRc.isOk() && authRc != ResponseCode::OP_AUTH_NEEDED) {
- return worker_cb(operationFailed(authRc));
- }
-
- // Add entropy to the device first.
- if (entropy.size()) {
- rc = KS_HANDLE_HIDL_ERROR(dev, dev->addRngEntropy(entropy));
- if (!rc.isOk()) {
- return worker_cb(operationFailed(rc));
- }
- }
-
- // Create a keyid for this key.
- auto keyid = KeymasterEnforcement::CreateKeyId(blob2hidlVec(keyBlob));
- if (!keyid) {
- ALOGE("Failed to create a key ID for authorization checking.");
- return worker_cb(operationFailed(ErrorCode::UNKNOWN_ERROR));
- }
-
- // Check that all key authorization policy requirements are met.
- AuthorizationSet key_auths = characteristics.hardwareEnforced;
- key_auths.append(characteristics.softwareEnforced.begin(),
- characteristics.softwareEnforced.end());
-
- rc = keyStore_->getEnforcementPolicy().AuthorizeOperation(
- purpose, *keyid, key_auths, opParams, authToken, 0 /* op_handle */,
- true /* is_begin_operation */);
- if (!rc.isOk()) {
- return worker_cb(operationFailed(rc));
- }
-
- // If there are more than kMaxOperations, abort the oldest operation that was started as
- // pruneable.
- while (operationMap_.getOperationCount() >= kMaxOperations) {
- ALOGD("Reached or exceeded concurrent operations limit");
- if (!pruneOperation()) {
- break;
- }
- }
-
- android::security::keymaster::OperationResult result;
-
- auto hidlCb = [&](ErrorCode ret, const hidl_vec<KeyParameter>& outParams,
- uint64_t operationHandle) {
- dev->logIfKeymasterVendorError(ret);
- result.resultCode = ret;
- if (!result.resultCode.isOk()) {
- if (result.resultCode == ErrorCode::INVALID_KEY_BLOB) {
- log_key_integrity_violation(lockedEntry->alias().c_str(), lockedEntry->uid());
- }
- return;
- }
- result.handle = operationHandle;
- result.outParams = outParams;
- };
-
- do {
- rc = KS_HANDLE_HIDL_ERROR(dev, dev->begin(purpose, blob2hidlVec(keyBlob),
- opParams.hidl_data(), authToken, hidlCb));
- if (!rc.isOk()) {
- LOG(ERROR) << "Got error " << rc << " from begin()";
- return worker_cb(operationFailed(ResponseCode::SYSTEM_ERROR));
- }
-
- if (result.resultCode == ErrorCode::KEY_REQUIRES_UPGRADE) {
- std::tie(rc, keyBlob) = upgradeKeyBlob(lockedEntry, opParams);
- if (!rc.isOk()) {
- return worker_cb(operationFailed(rc));
- }
-
- rc = KS_HANDLE_HIDL_ERROR(dev, dev->begin(purpose, blob2hidlVec(keyBlob),
- opParams.hidl_data(), authToken, hidlCb));
- if (!rc.isOk()) {
- LOG(ERROR) << "Got error " << rc << " from begin()";
- return worker_cb(operationFailed(ResponseCode::SYSTEM_ERROR));
- }
- }
- // If there are too many operations abort the oldest operation that was
- // started as pruneable and try again.
- } while (result.resultCode == ErrorCode::TOO_MANY_OPERATIONS && pruneOperation());
-
- rc = result.resultCode;
- if (!rc.isOk()) {
- return worker_cb(operationFailed(rc));
- }
-
- // Note: The operation map takes possession of the contents of "characteristics".
- // It is safe to use characteristics after the following line but it will be empty.
- sp<IBinder> operationToken =
- operationMap_.addOperation(result.handle, *keyid, purpose, dev, appToken,
- std::move(characteristics), opParams.hidl_data(), pruneable);
- assert(characteristics.hardwareEnforced.size() == 0);
- assert(characteristics.softwareEnforced.size() == 0);
- result.token = operationToken;
-
- auto operation = operationMap_.getOperation(operationToken);
- if (!operation) {
- return worker_cb(operationFailed(ResponseCode::SYSTEM_ERROR));
- }
-
- if (authRc.isOk() && authToken.mac.size() &&
- dev->halVersion().securityLevel == SecurityLevel::STRONGBOX) {
- operation->authTokenFuture = operation->authTokenPromise.get_future();
- std::weak_ptr<Operation> weak_operation = operation;
-
- auto verifyTokenCB = [weak_operation](KeyStoreServiceReturnCode rc,
- HardwareAuthToken authToken,
- VerificationToken verificationToken) {
- auto operation = weak_operation.lock();
- if (!operation) {
- // operation aborted, nothing to do
- return;
- }
- if (rc.isOk()) {
- operation->authToken = std::move(authToken);
- operation->verificationToken = std::move(verificationToken);
- }
- operation->authTokenPromise.set_value(rc);
- };
- auto teeKmDevice = keyStore_->getDevice(SecurityLevel::TRUSTED_ENVIRONMENT);
- teeKmDevice->verifyAuthorization(result.handle, {}, std::move(authToken),
- std::move(verifyTokenCB));
- }
-
- // Return the authentication lookup result. If this is a per operation
- // auth'd key then the resultCode will be ::OP_AUTH_NEEDED and the
- // application should get an auth token using the handle before the
- // first call to update, which will fail if keystore hasn't received the
- // auth token.
- if (result.resultCode.isOk()) {
- result.resultCode = authRc;
- }
- return worker_cb(result);
- });
-}
-
-KeyStoreServiceReturnCode
-KeymasterWorker::getOperationAuthTokenIfNeeded(std::shared_ptr<Operation> op) {
- if (!op) return ErrorCode::INVALID_OPERATION_HANDLE;
-
- if (op->authTokenFuture.valid()) {
- LOG(INFO) << "Waiting for verification token";
- op->authTokenFuture.wait();
- auto rc = op->authTokenFuture.get();
- if (!rc.isOk()) {
- return rc;
- }
- op->authTokenFuture = {};
- } else if (!op->hasAuthToken()) {
- KeyStoreServiceReturnCode rc;
- HardwareAuthToken found;
- std::tie(rc, found) = getAuthToken(op->characteristics, op->handle, op->purpose);
- if (!rc.isOk()) return rc;
- op->authToken = std::move(found);
- }
-
- return ResponseCode::NO_ERROR;
-}
-
-namespace {
-
-class Finalize {
- private:
- std::function<void()> f_;
-
- public:
- explicit Finalize(std::function<void()> f) : f_(f) {}
- ~Finalize() {
- if (f_) f_();
- }
- void release() { f_ = {}; }
-};
-
-} // namespace
-
-void KeymasterWorker::update(sp<IBinder> token, AuthorizationSet params, hidl_vec<uint8_t> data,
- update_cb worker_cb) {
- Worker::addRequest([this, CAPTURE_MOVE(token), CAPTURE_MOVE(params), CAPTURE_MOVE(data),
- CAPTURE_MOVE(worker_cb)]() {
- KeyStoreServiceReturnCode rc;
- auto op = operationMap_.getOperation(token);
- if (!op) {
- return worker_cb(operationFailed(ErrorCode::INVALID_OPERATION_HANDLE));
- }
-
- Finalize abort_operation_in_case_of_error([&] {
- operationMap_.removeOperation(token, false, rc.getErrorCode());
- keyStore_->getAuthTokenTable().MarkCompleted(op->handle);
- KS_HANDLE_HIDL_ERROR(keymasterDevice_, keymasterDevice_->abort(op->handle));
- });
-
- rc = getOperationAuthTokenIfNeeded(op);
- if (!rc.isOk()) return worker_cb(operationFailed(rc));
-
- // Check that all key authorization policy requirements are met.
- AuthorizationSet key_auths(op->characteristics.hardwareEnforced);
- key_auths.append(op->characteristics.softwareEnforced.begin(),
- op->characteristics.softwareEnforced.end());
-
- rc = keyStore_->getEnforcementPolicy().AuthorizeOperation(op->purpose, op->keyid, key_auths,
- params, op->authToken, op->handle,
- false /* is_begin_operation */);
- if (!rc.isOk()) return worker_cb(operationFailed(rc));
-
- OperationResult result;
- auto hidlCb = [&](ErrorCode ret, uint32_t inputConsumed,
- const hidl_vec<KeyParameter>& outParams,
- const ::std::vector<uint8_t>& output) {
- op->device->logIfKeymasterVendorError(ret);
- result.resultCode = ret;
- if (result.resultCode.isOk()) {
- result.inputConsumed = inputConsumed;
- result.outParams = outParams;
- result.data = output;
- }
- };
-
- rc = KS_HANDLE_HIDL_ERROR(op->device,
- op->device->update(op->handle, params.hidl_data(), data,
- op->authToken, op->verificationToken, hidlCb));
-
- // just a reminder: on success result->resultCode was set in the callback. So we only
- // overwrite it if there was a communication error indicated by the ErrorCode.
- if (!rc.isOk()) result.resultCode = rc;
- if (result.resultCode.isOk()) {
- // if everything went well we don't abort the operation.
- abort_operation_in_case_of_error.release();
- }
- return worker_cb(std::move(result));
- });
-}
-
-/**
- * Check that all KeyParameters provided by the application are allowed. Any parameter that keystore
- * adds itself should be disallowed here.
- */
-template <typename ParamsIter>
-static bool checkAllowedOperationParams(ParamsIter begin, const ParamsIter end) {
- while (begin != end) {
- switch (begin->tag) {
- case Tag::ATTESTATION_APPLICATION_ID:
- case Tag::RESET_SINCE_ID_ROTATION:
- return false;
- default:
- break;
- }
- ++begin;
- }
- return true;
-}
-
-void KeymasterWorker::finish(sp<IBinder> token, AuthorizationSet params, hidl_vec<uint8_t> input,
- hidl_vec<uint8_t> signature, hidl_vec<uint8_t> entropy,
- finish_cb worker_cb) {
- Worker::addRequest([this, CAPTURE_MOVE(token), CAPTURE_MOVE(params), CAPTURE_MOVE(input),
- CAPTURE_MOVE(signature), CAPTURE_MOVE(entropy),
- CAPTURE_MOVE(worker_cb)]() mutable {
- KeyStoreServiceReturnCode rc;
- auto op = operationMap_.getOperation(token);
- if (!op) {
- return worker_cb(operationFailed(ErrorCode::INVALID_OPERATION_HANDLE));
- }
-
- bool finished = false;
- Finalize abort_operation_in_case_of_error([&] {
- operationMap_.removeOperation(token, finished && rc.isOk(), rc.getErrorCode());
- keyStore_->getAuthTokenTable().MarkCompleted(op->handle);
- if (!finished)
- KS_HANDLE_HIDL_ERROR(keymasterDevice_, keymasterDevice_->abort(op->handle));
- });
-
- if (!checkAllowedOperationParams(params.begin(), params.end())) {
- return worker_cb(operationFailed(ErrorCode::INVALID_ARGUMENT));
- }
-
- rc = getOperationAuthTokenIfNeeded(op);
- if (!rc.isOk()) return worker_cb(operationFailed(rc));
-
- // Check that all key authorization policy requirements are met.
- AuthorizationSet key_auths(op->characteristics.hardwareEnforced);
- key_auths.append(op->characteristics.softwareEnforced.begin(),
- op->characteristics.softwareEnforced.end());
-
- if (key_auths.Contains(Tag::TRUSTED_CONFIRMATION_REQUIRED)) {
- hidl_vec<uint8_t> confirmationToken =
- keyStore_->getConfirmationManager().getLatestConfirmationToken();
- if (confirmationToken.size() == 0) {
- LOG(ERROR) << "Confirmation token required but none found";
- return worker_cb(operationFailed(ErrorCode::NO_USER_CONFIRMATION));
- }
- params.push_back(keymaster::TAG_CONFIRMATION_TOKEN, std::move(confirmationToken));
- }
-
- rc = keyStore_->getEnforcementPolicy().AuthorizeOperation(op->purpose, op->keyid, key_auths,
- params, op->authToken, op->handle,
- false /* is_begin_operation */);
- if (!rc.isOk()) return worker_cb(operationFailed(rc));
-
- if (entropy.size()) {
- rc = KS_HANDLE_HIDL_ERROR(op->device, op->device->addRngEntropy(entropy));
- if (!rc.isOk()) {
- return worker_cb(operationFailed(rc));
- }
- }
-
- OperationResult result;
- auto hidlCb = [&](ErrorCode ret, const hidl_vec<KeyParameter>& outParams,
- const ::std::vector<uint8_t>& output) {
- op->device->logIfKeymasterVendorError(ret);
- result.resultCode = ret;
- if (result.resultCode.isOk()) {
- result.outParams = outParams;
- result.data = output;
- }
- };
-
- rc = KS_HANDLE_HIDL_ERROR(op->device, op->device->finish(op->handle, params.hidl_data(),
- input, signature, op->authToken,
- op->verificationToken, hidlCb));
-
- if (rc.isOk()) {
- // inform the finalizer that the finish call went through
- finished = true;
- // and what the result was
- rc = result.resultCode;
- } else {
- return worker_cb(operationFailed(rc));
- }
- return worker_cb(std::move(result));
- });
-}
-
-void KeymasterWorker::abort(sp<IBinder> token, abort_cb worker_cb) {
- Worker::addRequest([this, CAPTURE_MOVE(token), CAPTURE_MOVE(worker_cb)]() {
- return worker_cb(abort(token, ResponseCode::ABORT_CALLED));
- });
-}
-
-void KeymasterWorker::verifyAuthorization(uint64_t challenge, hidl_vec<KeyParameter> params,
- HardwareAuthToken token,
- verifyAuthorization_cb worker_cb) {
- Worker::addRequest([this, challenge, CAPTURE_MOVE(params), CAPTURE_MOVE(token),
- CAPTURE_MOVE(worker_cb)]() {
- KeyStoreServiceReturnCode error;
- VerificationToken verificationToken;
- KeyStoreServiceReturnCode rc = KS_HANDLE_HIDL_ERROR(
- keymasterDevice_,
- keymasterDevice_->verifyAuthorization(
- challenge, params, token, [&](ErrorCode ret, const VerificationToken& vToken) {
- keymasterDevice_->logIfKeymasterVendorError(ret);
- error = ret;
- verificationToken = vToken;
- }));
- worker_cb(rc.isOk() ? error : rc, std::move(token), std::move(verificationToken));
- });
-}
-
-void KeymasterWorker::addRngEntropy(hidl_vec<uint8_t> data, addRngEntropy_cb _hidl_cb) {
- addRequest(&Keymaster::addRngEntropy, std::move(_hidl_cb), std::move(data));
-}
-
-namespace {
-bool containsTag(const hidl_vec<KeyParameter>& params, Tag tag) {
- return params.end() !=
- std::find_if(params.begin(), params.end(),
- [&](const KeyParameter& param) { return param.tag == tag; });
-}
-
-bool isAuthenticationBound(const hidl_vec<KeyParameter>& params) {
- return !containsTag(params, Tag::NO_AUTH_REQUIRED);
-}
-} // namespace
-
-void KeymasterWorker::generateKey(LockedKeyBlobEntry lockedEntry, hidl_vec<KeyParameter> keyParams,
- hidl_vec<uint8_t> entropy, int flags, generateKey_cb worker_cb) {
- Worker::addRequest([this, CAPTURE_MOVE(lockedEntry), CAPTURE_MOVE(keyParams),
- CAPTURE_MOVE(entropy), CAPTURE_MOVE(worker_cb), flags]() mutable {
- KeyStoreServiceReturnCode rc =
- KS_HANDLE_HIDL_ERROR(keymasterDevice_, keymasterDevice_->addRngEntropy(entropy));
- if (!rc.isOk()) {
- return worker_cb(rc, {});
- }
-
- SecurityLevel securityLevel = keymasterDevice_->halVersion().securityLevel;
-
- // Fallback cannot be considered for Strongbox. Further versions restrictions are enforced
- // by KeyStore::getFallbackDevice()
- bool consider_fallback = securityLevel == SecurityLevel::TRUSTED_ENVIRONMENT;
-
- Finalize logOnFail([&] {
- logKeystoreKeyCreationEvent(keyParams, false /*wasCreationSuccessful*/,
- rc.getErrorCode());
- });
-
- KeyCharacteristics outCharacteristics;
- KeyStoreServiceReturnCode error;
- auto hidl_cb = [&](ErrorCode ret, const hidl_vec<uint8_t>& hidlKeyBlob,
- const KeyCharacteristics& keyCharacteristics) {
- keymasterDevice_->logIfKeymasterVendorError(ret);
- error = ret;
- if (!error.isOk()) {
- return;
- }
- consider_fallback = false;
- outCharacteristics = keyCharacteristics;
-
- Blob keyBlob(&hidlKeyBlob[0], hidlKeyBlob.size(), nullptr, 0, ::TYPE_KEYMASTER_10);
- keyBlob.setSecurityLevel(internalSecurityLevel_);
- keyBlob.setCriticalToDeviceEncryption(flags &
- KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION);
- if (isAuthenticationBound(keyParams) && !keyBlob.isCriticalToDeviceEncryption()) {
- keyBlob.setSuperEncrypted(true);
- }
- keyBlob.setEncrypted(flags & KEYSTORE_FLAG_ENCRYPTED);
-
- AuthorizationSet sw_enforced = keyParams;
- sw_enforced.Subtract(outCharacteristics.hardwareEnforced);
- sw_enforced.Union(outCharacteristics.softwareEnforced);
- sw_enforced.Filter([](const KeyParameter& param) -> bool {
- return !(param.tag == Tag::APPLICATION_DATA || param.tag == Tag::APPLICATION_ID);
- });
- if (!sw_enforced.Contains(Tag::USER_ID)) {
- // Most Java processes don't have access to this tag
- sw_enforced.push_back(keymaster::TAG_USER_ID, get_user_id(lockedEntry->uid()));
- }
- Blob keyCharBlob;
- keyCharBlob.putKeyCharacteristics(outCharacteristics.hardwareEnforced, sw_enforced);
- error = keyStore_->put(lockedEntry, std::move(keyBlob), std::move(keyCharBlob));
- };
-
- rc = KS_HANDLE_HIDL_ERROR(keymasterDevice_,
- keymasterDevice_->generateKey(keyParams, hidl_cb));
- if (!rc.isOk()) {
- return worker_cb(rc, {});
- }
-
- if (consider_fallback && !error.isOk()) {
- auto fallback = keyStore_->getFallbackDevice();
- if (!fallback) {
- return worker_cb(error, {});
- }
- // No fallback for 3DES
- for (auto& param : keyParams) {
- auto algorithm = authorizationValue(TAG_ALGORITHM, param);
- if (algorithm.isOk() && algorithm.value() == Algorithm::TRIPLE_DES) {
- return worker_cb(ErrorCode::UNSUPPORTED_ALGORITHM, {});
- }
- }
-
- // delegate to fallback worker
- fallback->generateKey(std::move(lockedEntry), std::move(keyParams), std::move(entropy),
- flags, std::move(worker_cb));
- // let fallback do the logging
- logOnFail.release();
- return;
- }
-
- if (!error.isOk()) return worker_cb(error, {});
-
- // log on success
- logOnFail.release();
- logKeystoreKeyCreationEvent(keyParams, true /*wasCreationSuccessful*/,
- error.getErrorCode());
-
- return worker_cb(error, std::move(outCharacteristics));
- });
-}
-
-void KeymasterWorker::generateKey(hidl_vec<KeyParameter> keyParams, generateKey2_cb worker_cb) {
- addRequest(&Keymaster::generateKey, std::move(worker_cb), std::move(keyParams));
-}
-
-void KeymasterWorker::getKeyCharacteristics(LockedKeyBlobEntry lockedEntry,
- hidl_vec<uint8_t> clientId, hidl_vec<uint8_t> appData,
- Blob keyBlob, Blob charBlob,
- getKeyCharacteristics_cb worker_cb) {
- Worker::addRequest([this, CAPTURE_MOVE(lockedEntry), CAPTURE_MOVE(clientId),
- CAPTURE_MOVE(appData), CAPTURE_MOVE(keyBlob), CAPTURE_MOVE(charBlob),
- CAPTURE_MOVE(worker_cb)]() {
- auto result = createKeyCharacteristicsCache(lockedEntry, clientId, appData,
- std::move(keyBlob), std::move(charBlob));
- return worker_cb(std::get<0>(result), std::move(std::get<1>(result)));
- });
-}
-
-void KeymasterWorker::importKey(LockedKeyBlobEntry lockedEntry, hidl_vec<KeyParameter> keyParams,
- KeyFormat keyFormat, hidl_vec<uint8_t> keyData, int flags,
- importKey_cb worker_cb) {
- Worker::addRequest([this, CAPTURE_MOVE(lockedEntry), CAPTURE_MOVE(keyParams), keyFormat,
- CAPTURE_MOVE(keyData), flags, CAPTURE_MOVE(worker_cb)]() mutable {
- SecurityLevel securityLevel = keymasterDevice_->halVersion().securityLevel;
-
- // Fallback cannot be considered for Strongbox. Further versions restrictions are enforced
- // by KeyStore::getFallbackDevice()
- bool consider_fallback = securityLevel == SecurityLevel::TRUSTED_ENVIRONMENT;
-
- KeyStoreServiceReturnCode error;
- Finalize logOnFail([&] {
- logKeystoreKeyCreationEvent(keyParams, false /*wasCreationSuccessful*/,
- error.getErrorCode());
- });
-
- KeyCharacteristics outCharacteristics;
- auto hidl_cb = [&](ErrorCode ret, const hidl_vec<uint8_t>& hidlKeyBlob,
- const KeyCharacteristics& keyCharacteristics) {
- keymasterDevice_->logIfKeymasterVendorError(ret);
- error = ret;
- if (!error.isOk()) {
- LOG(INFO) << "importKey failed";
- return;
- }
- consider_fallback = false;
- outCharacteristics = keyCharacteristics;
-
- Blob keyBlob(&hidlKeyBlob[0], hidlKeyBlob.size(), nullptr, 0, ::TYPE_KEYMASTER_10);
- keyBlob.setSecurityLevel(internalSecurityLevel_);
- keyBlob.setCriticalToDeviceEncryption(flags &
- KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION);
- if (isAuthenticationBound(keyParams) && !keyBlob.isCriticalToDeviceEncryption()) {
- keyBlob.setSuperEncrypted(true);
- }
- keyBlob.setEncrypted(flags & KEYSTORE_FLAG_ENCRYPTED);
-
- AuthorizationSet sw_enforced = keyParams;
- sw_enforced.Subtract(outCharacteristics.hardwareEnforced);
- sw_enforced.Union(outCharacteristics.softwareEnforced);
- sw_enforced.Filter([](const KeyParameter& param) -> bool {
- return !(param.tag == Tag::APPLICATION_DATA || param.tag == Tag::APPLICATION_ID);
- });
- if (!sw_enforced.Contains(Tag::USER_ID)) {
- // Most Java processes don't have access to this tag
- sw_enforced.push_back(keymaster::TAG_USER_ID, get_user_id(lockedEntry->uid()));
- }
- Blob keyCharBlob;
- keyCharBlob.putKeyCharacteristics(outCharacteristics.hardwareEnforced, sw_enforced);
- error = keyStore_->put(lockedEntry, std::move(keyBlob), std::move(keyCharBlob));
- };
-
- KeyStoreServiceReturnCode rc = KS_HANDLE_HIDL_ERROR(
- keymasterDevice_, keymasterDevice_->importKey(keyParams, keyFormat, keyData, hidl_cb));
- if (!rc.isOk()) {
- return worker_cb(rc, {});
- }
-
- if (consider_fallback && !error.isOk()) {
- auto fallback = keyStore_->getFallbackDevice();
- if (!fallback) {
- return worker_cb(error, {});
- }
- // No fallback for 3DES
- for (auto& param : keyParams) {
- auto algorithm = authorizationValue(TAG_ALGORITHM, param);
- if (algorithm.isOk() && algorithm.value() == Algorithm::TRIPLE_DES) {
- return worker_cb(ErrorCode::UNSUPPORTED_ALGORITHM, {});
- }
- }
-
- // delegate to fallback worker
- fallback->importKey(std::move(lockedEntry), std::move(keyParams), keyFormat,
- std::move(keyData), flags, std::move(worker_cb));
- // let fallback to the logging
- logOnFail.release();
- return;
- }
-
- if (!error.isOk()) return worker_cb(error, {});
-
- // log on success
- logOnFail.release();
- logKeystoreKeyCreationEvent(keyParams, true /*wasCreationSuccessful*/,
- error.getErrorCode());
-
- return worker_cb(error, std::move(outCharacteristics));
- });
-}
-
-void KeymasterWorker::importWrappedKey(LockedKeyBlobEntry wrappingLockedEntry,
- LockedKeyBlobEntry wrapppedLockedEntry,
- hidl_vec<uint8_t> wrappedKeyData,
- hidl_vec<uint8_t> maskingKey,
- hidl_vec<KeyParameter> unwrappingParams, Blob wrappingBlob,
- Blob wrappingCharBlob, uint64_t passwordSid,
- uint64_t biometricSid, importWrappedKey_cb worker_cb) {
- Worker::addRequest([this, CAPTURE_MOVE(wrappingLockedEntry), CAPTURE_MOVE(wrapppedLockedEntry),
- CAPTURE_MOVE(wrappedKeyData), CAPTURE_MOVE(maskingKey),
- CAPTURE_MOVE(unwrappingParams), CAPTURE_MOVE(wrappingBlob),
- CAPTURE_MOVE(wrappingCharBlob), passwordSid, biometricSid,
- CAPTURE_MOVE(worker_cb)]() mutable {
- auto hidlWrappingKey = blob2hidlVec(wrappingBlob);
-
- KeyCharacteristics outCharacteristics;
- KeyStoreServiceReturnCode error;
-
- auto hidlCb = [&](ErrorCode ret, const hidl_vec<uint8_t>& hidlKeyBlob,
- const KeyCharacteristics& keyCharacteristics) {
- keymasterDevice_->logIfKeymasterVendorError(ret);
- error = ret;
- if (!error.isOk()) {
- return;
- }
- outCharacteristics = keyCharacteristics;
-
- Blob keyBlob(hidlKeyBlob.data(), hidlKeyBlob.size(), nullptr, 0, ::TYPE_KEYMASTER_10);
- keyBlob.setSecurityLevel(internalSecurityLevel_);
- if (isAuthenticationBound(keyCharacteristics.hardwareEnforced)) {
- keyBlob.setSuperEncrypted(true);
- }
-
- AuthorizationSet sw_enforced = outCharacteristics.softwareEnforced;
- if (!sw_enforced.Contains(Tag::USER_ID)) {
- // Most Java processes don't have access to this tag
- sw_enforced.push_back(keymaster::TAG_USER_ID,
- get_user_id(wrapppedLockedEntry->uid()));
- }
- Blob keyCharBlob;
- keyCharBlob.putKeyCharacteristics(outCharacteristics.hardwareEnforced, sw_enforced);
- error = keyStore_->put(wrapppedLockedEntry, std::move(keyBlob), std::move(keyCharBlob));
- };
-
- KeyStoreServiceReturnCode rc = KS_HANDLE_HIDL_ERROR(
- keymasterDevice_, keymasterDevice_->importWrappedKey(
- wrappedKeyData, hidlWrappingKey, maskingKey, unwrappingParams,
- passwordSid, biometricSid, hidlCb));
-
- // possible hidl error
- if (!rc.isOk()) {
- return worker_cb(rc, {});
- }
-
- if (error == ErrorCode::KEY_REQUIRES_UPGRADE) {
- std::tie(rc, wrappingBlob) = upgradeKeyBlob(wrappingLockedEntry, {});
- if (!rc.isOk()) {
- return worker_cb(rc, {});
- }
-
- auto upgradedHidlKeyBlob = blob2hidlVec(wrappingBlob);
-
- rc = KS_HANDLE_HIDL_ERROR(keymasterDevice_,
- keymasterDevice_->importWrappedKey(
- wrappedKeyData, upgradedHidlKeyBlob, maskingKey,
- unwrappingParams, passwordSid, biometricSid, hidlCb));
- if (!rc.isOk()) {
- error = rc;
- }
- }
- return worker_cb(error, std::move(outCharacteristics));
- });
-}
-
-void KeymasterWorker::exportKey(LockedKeyBlobEntry lockedEntry, KeyFormat exportFormat,
- hidl_vec<uint8_t> clientId, hidl_vec<uint8_t> appData, Blob keyBlob,
- Blob charBlob, exportKey_cb worker_cb) {
- Worker::addRequest([this, CAPTURE_MOVE(lockedEntry), exportFormat, CAPTURE_MOVE(clientId),
- CAPTURE_MOVE(appData), CAPTURE_MOVE(keyBlob), CAPTURE_MOVE(charBlob),
- CAPTURE_MOVE(worker_cb)]() mutable {
- auto key = blob2hidlVec(keyBlob);
-
- ExportResult result;
- auto hidlCb = [&](ErrorCode ret,
- const ::android::hardware::hidl_vec<uint8_t>& keyMaterial) {
- keymasterDevice_->logIfKeymasterVendorError(ret);
- result.resultCode = ret;
- if (!result.resultCode.isOk()) {
- if (result.resultCode == ErrorCode::INVALID_KEY_BLOB) {
- log_key_integrity_violation(lockedEntry->alias().c_str(), lockedEntry->uid());
- }
- return;
- }
- result.exportData = keyMaterial;
- };
- KeyStoreServiceReturnCode rc = KS_HANDLE_HIDL_ERROR(
- keymasterDevice_,
- keymasterDevice_->exportKey(exportFormat, key, clientId, appData, hidlCb));
-
- // Overwrite result->resultCode only on HIDL error. Otherwise we want the result set in the
- // callback hidlCb.
- if (!rc.isOk()) {
- result.resultCode = rc;
- }
-
- if (result.resultCode == ErrorCode::KEY_REQUIRES_UPGRADE) {
- AuthorizationSet upgradeParams;
- if (clientId.size()) {
- upgradeParams.push_back(TAG_APPLICATION_ID, clientId);
- }
- if (appData.size()) {
- upgradeParams.push_back(TAG_APPLICATION_DATA, appData);
- }
- std::tie(rc, keyBlob) = upgradeKeyBlob(lockedEntry, upgradeParams);
- if (!rc.isOk()) {
- return worker_cb(std::move(result));
- }
-
- auto upgradedHidlKeyBlob = blob2hidlVec(keyBlob);
-
- rc = KS_HANDLE_HIDL_ERROR(keymasterDevice_,
- keymasterDevice_->exportKey(exportFormat, upgradedHidlKeyBlob,
- clientId, appData, hidlCb));
- if (!rc.isOk()) {
- result.resultCode = rc;
- }
- }
- return worker_cb(std::move(result));
- });
-}
-void KeymasterWorker::attestKey(hidl_vec<uint8_t> keyToAttest, hidl_vec<KeyParameter> attestParams,
- attestKey_cb worker_cb) {
- addRequest(&Keymaster::attestKey, std::move(worker_cb), std::move(keyToAttest),
- std::move(attestParams));
-}
-
-void KeymasterWorker::deleteKey(hidl_vec<uint8_t> keyBlob, deleteKey_cb _hidl_cb) {
- addRequest(&Keymaster::deleteKey, std::move(_hidl_cb), std::move(keyBlob));
-}
-
-void KeymasterWorker::binderDied(android::wp<IBinder> who) {
- Worker::addRequest([this, who]() {
- auto operations = operationMap_.getOperationsForToken(who.unsafe_get());
- for (const auto& token : operations) {
- abort(token, ResponseCode::BINDER_DIED);
- keyStore_->removeOperationDevice(token);
- }
- });
-}
-
-} // namespace keystore
diff --git a/keystore/keymaster_worker.h b/keystore/keymaster_worker.h
deleted file mode 100644
index fbd52b4..0000000
--- a/keystore/keymaster_worker.h
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
-**
-** Copyright 2018, 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.
-*/
-
-#ifndef KEYSTORE_KEYMASTER_WORKER_H_
-#define KEYSTORE_KEYMASTER_WORKER_H_
-
-#include <condition_variable>
-#include <functional>
-#include <keymasterV4_1/Keymaster.h>
-#include <memory>
-#include <mutex>
-#include <optional>
-#include <queue>
-#include <thread>
-#include <tuple>
-
-#include <keystore/ExportResult.h>
-#include <keystore/KeyCharacteristics.h>
-#include <keystore/KeymasterBlob.h>
-#include <keystore/OperationResult.h>
-#include <keystore/keymaster_types.h>
-#include <keystore/keystore_return_types.h>
-
-#include "blob.h"
-#include "operation.h"
-
-namespace keystore {
-
-using android::sp;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using android::hardware::keymaster::V4_1::support::Keymaster;
-using ::android::security::keymaster::KeymasterBlob;
-
-class KeyStore;
-
-class Worker {
-
- /*
- * NonCopyableFunction works similar to std::function in that it wraps callable objects and
- * erases their type. The rationale for using a custom class instead of
- * std::function is that std::function requires the wrapped object to be copy contructible.
- * NonCopyableFunction is itself not copyable and never attempts to copy the wrapped object.
- * TODO use similar optimization as std::function to remove the extra make_unique allocation.
- */
- template <typename Fn> class NonCopyableFunction;
-
- template <typename Ret, typename... Args> class NonCopyableFunction<Ret(Args...)> {
-
- class NonCopyableFunctionBase {
- public:
- NonCopyableFunctionBase() = default;
- virtual ~NonCopyableFunctionBase() {}
- virtual Ret operator()(Args... args) = 0;
- NonCopyableFunctionBase(const NonCopyableFunctionBase&) = delete;
- NonCopyableFunctionBase& operator=(const NonCopyableFunctionBase&) = delete;
- };
-
- template <typename Fn>
- class NonCopyableFunctionTypeEraser : public NonCopyableFunctionBase {
- private:
- Fn f_;
-
- public:
- NonCopyableFunctionTypeEraser() = default;
- explicit NonCopyableFunctionTypeEraser(Fn f) : f_(std::move(f)) {}
- Ret operator()(Args... args) override { return f_(std::move(args)...); }
- };
-
- private:
- std::unique_ptr<NonCopyableFunctionBase> f_;
-
- public:
- NonCopyableFunction() = default;
- // NOLINTNEXTLINE(google-explicit-constructor)
- template <typename F> NonCopyableFunction(F f) {
- f_ = std::make_unique<NonCopyableFunctionTypeEraser<F>>(std::move(f));
- }
- NonCopyableFunction(NonCopyableFunction&& other) = default;
- NonCopyableFunction& operator=(NonCopyableFunction&& other) = default;
- NonCopyableFunction(const NonCopyableFunction& other) = delete;
- NonCopyableFunction& operator=(const NonCopyableFunction& other) = delete;
-
- Ret operator()(Args... args) {
- if (f_) return (*f_)(std::move(args)...);
- }
- };
-
- using WorkerTask = NonCopyableFunction<void()>;
-
- std::queue<WorkerTask> pending_requests_;
- std::mutex pending_requests_mutex_;
- std::condition_variable pending_requests_cond_var_;
- bool running_ = false;
- bool terminate_ = false;
-
- public:
- Worker();
- ~Worker();
- void addRequest(WorkerTask request);
-};
-
-template <typename... Args> struct MakeKeymasterWorkerCB;
-
-template <typename ErrorType, typename... Args>
-struct MakeKeymasterWorkerCB<ErrorType, std::function<void(Args...)>> {
- using type = std::function<void(ErrorType, std::tuple<std::decay_t<Args>...>&&)>;
-};
-
-template <typename ErrorType> struct MakeKeymasterWorkerCB<ErrorType> {
- using type = std::function<void(ErrorType)>;
-};
-
-template <typename... Args>
-using MakeKeymasterWorkerCB_t = typename MakeKeymasterWorkerCB<Args...>::type;
-
-class KeymasterWorker : protected Worker {
- private:
- sp<Keymaster> keymasterDevice_;
- OperationMap operationMap_;
- KeyStore* keyStore_;
-
- /**
- * Models the security level of this worker internal to KeyStore.
- *
- * When the device has only a software Keymaster, KeyStore will set it on the TEE slot and
- * instantiate a new in-process software Keymaster. In that case there is a mismatch between the
- * security level used by KeyStore and what is reported from the HAL. This represents the level
- * used internally by KeyStore.
- *
- * This value is used to associate blobs to the corresponding Keymaster backend. It does not
- * indicate an actual Keymaster HAL security level and should never be exposed to users.
- */
- SecurityLevel internalSecurityLevel_;
-
- template <typename KMFn, typename ErrorType, typename... Args, size_t... I>
- void unwrap_tuple(KMFn kmfn, std::function<void(ErrorType)> cb,
- const std::tuple<Args...>& tuple, std::index_sequence<I...>) {
- cb(((*keymasterDevice_).*kmfn)(std::get<I>(tuple)...));
- }
-
- template <typename KMFn, typename ErrorType, typename... ReturnTypes, typename... Args,
- size_t... I>
- void unwrap_tuple(KMFn kmfn, std::function<void(ErrorType, std::tuple<ReturnTypes...>&&)> cb,
- const std::tuple<Args...>& tuple, std::index_sequence<I...>) {
- std::tuple<ReturnTypes...> returnValue;
- auto result = ((*keymasterDevice_).*kmfn)(
- std::get<I>(tuple)...,
- [&returnValue](const ReturnTypes&... args) { returnValue = std::make_tuple(args...); });
- cb(std::move(result), std::move(returnValue));
- }
-
- template <typename KMFn, typename ErrorType, typename... Args>
- void addRequest(KMFn kmfn, std::function<void(ErrorType)> cb, Args&&... args) {
- Worker::addRequest([this, kmfn, cb = std::move(cb),
- tuple = std::make_tuple(std::forward<Args>(args)...)]() {
- unwrap_tuple(kmfn, std::move(cb), tuple, std::index_sequence_for<Args...>{});
- });
- }
-
- template <typename KMFn, typename ErrorType, typename... ReturnTypes, typename... Args>
- void addRequest(KMFn kmfn, std::function<void(ErrorType, std::tuple<ReturnTypes...>&&)> cb,
- Args&&... args) {
- Worker::addRequest([this, kmfn, cb = std::move(cb),
- tuple = std::make_tuple(std::forward<Args>(args)...)]() {
- unwrap_tuple(kmfn, std::move(cb), tuple, std::index_sequence_for<Args...>{});
- });
- }
-
- void deleteOldKeyOnUpgrade(const LockedKeyBlobEntry& blobfile, Blob keyBlob);
- std::tuple<KeyStoreServiceReturnCode, Blob>
- upgradeKeyBlob(const LockedKeyBlobEntry& lockedEntry, const AuthorizationSet& params);
- std::tuple<KeyStoreServiceReturnCode, KeyCharacteristics, Blob, Blob>
- createKeyCharacteristicsCache(const LockedKeyBlobEntry& lockedEntry,
- const hidl_vec<uint8_t>& clientId,
- const hidl_vec<uint8_t>& appData, Blob keyBlob, Blob charBlob);
-
- /**
- * Get the auth token for this operation from the auth token table.
- *
- * Returns NO_ERROR if the auth token was found or none was required. If not needed, the
- * token will be empty (which keymaster interprets as no auth token).
- * OP_AUTH_NEEDED if it is a per op authorization, no authorization token exists for
- * that operation and failOnTokenMissing is false.
- * KM_ERROR_KEY_USER_NOT_AUTHENTICATED if there is no valid auth token for the operation
- */
- std::pair<KeyStoreServiceReturnCode, HardwareAuthToken>
- getAuthToken(const KeyCharacteristics& characteristics, uint64_t handle, KeyPurpose purpose,
- bool failOnTokenMissing = true);
-
- KeyStoreServiceReturnCode abort(const sp<IBinder>& token, ResponseCode reason_for_abort);
-
- bool pruneOperation();
-
- KeyStoreServiceReturnCode getOperationAuthTokenIfNeeded(std::shared_ptr<Operation> op);
-
- void appendConfirmationTokenIfNeeded(const KeyCharacteristics& keyCharacteristics,
- hidl_vec<KeyParameter>* params);
-
- public:
- KeymasterWorker(sp<Keymaster> keymasterDevice, KeyStore* keyStore,
- SecurityLevel internalSecurityLevel);
-
- void logIfKeymasterVendorError(ErrorCode ec) const;
-
- using worker_begin_cb = std::function<void(::android::security::keymaster::OperationResult)>;
- void begin(LockedKeyBlobEntry, sp<IBinder> appToken, Blob keyBlob, Blob charBlob,
- bool pruneable, KeyPurpose purpose, AuthorizationSet opParams,
- hidl_vec<uint8_t> entropy, worker_begin_cb worker_cb);
-
- using update_cb = std::function<void(::android::security::keymaster::OperationResult)>;
- void update(sp<IBinder> token, AuthorizationSet params, hidl_vec<uint8_t> data,
- update_cb _hidl_cb);
-
- using finish_cb = std::function<void(::android::security::keymaster::OperationResult)>;
- void finish(sp<IBinder> token, AuthorizationSet params, hidl_vec<uint8_t> input,
- hidl_vec<uint8_t> signature, hidl_vec<uint8_t> entorpy, finish_cb worker_cb);
-
- using abort_cb = std::function<void(KeyStoreServiceReturnCode)>;
- void abort(sp<IBinder> token, abort_cb _hidl_cb);
-
- using getHardwareInfo_cb = MakeKeymasterWorkerCB_t<Return<void>, Keymaster::getHardwareInfo_cb>;
- void getHardwareInfo(getHardwareInfo_cb _hidl_cb);
-
- using getHmacSharingParameters_cb =
- MakeKeymasterWorkerCB_t<Return<void>, Keymaster::getHmacSharingParameters_cb>;
- void getHmacSharingParameters(getHmacSharingParameters_cb _hidl_cb);
-
- using computeSharedHmac_cb =
- MakeKeymasterWorkerCB_t<Return<void>, Keymaster::computeSharedHmac_cb>;
- void computeSharedHmac(hidl_vec<HmacSharingParameters> params, computeSharedHmac_cb _hidl_cb);
-
- using verifyAuthorization_cb =
- std::function<void(KeyStoreServiceReturnCode ec, HardwareAuthToken, VerificationToken)>;
- void verifyAuthorization(uint64_t challenge, hidl_vec<KeyParameter> params,
- HardwareAuthToken token, verifyAuthorization_cb _hidl_cb);
-
- using addRngEntropy_cb = MakeKeymasterWorkerCB_t<Return<ErrorCode>>;
- void addRngEntropy(hidl_vec<uint8_t> data, addRngEntropy_cb _hidl_cb);
-
- using generateKey_cb = std::function<void(
- KeyStoreServiceReturnCode, ::android::hardware::keymaster::V4_0::KeyCharacteristics)>;
- void generateKey(LockedKeyBlobEntry, hidl_vec<KeyParameter> keyParams,
- hidl_vec<uint8_t> entropy, int flags, generateKey_cb _hidl_cb);
-
- using generateKey2_cb = MakeKeymasterWorkerCB_t<Return<void>, Keymaster::generateKey_cb>;
- void generateKey(hidl_vec<KeyParameter> keyParams, generateKey2_cb _hidl_cb);
-
- using getKeyCharacteristics_cb = std::function<void(
- KeyStoreServiceReturnCode, ::android::hardware::keymaster::V4_0::KeyCharacteristics)>;
- void getKeyCharacteristics(LockedKeyBlobEntry lockedEntry, hidl_vec<uint8_t> clientId,
- hidl_vec<uint8_t> appData, Blob keyBlob, Blob charBlob,
- getKeyCharacteristics_cb _hidl_cb);
-
- using importKey_cb = std::function<void(
- KeyStoreServiceReturnCode, ::android::hardware::keymaster::V4_0::KeyCharacteristics)>;
- void importKey(LockedKeyBlobEntry lockedEntry, hidl_vec<KeyParameter> params,
- KeyFormat keyFormat, hidl_vec<uint8_t> keyData, int flags,
- importKey_cb _hidl_cb);
-
- using importWrappedKey_cb = std::function<void(
- KeyStoreServiceReturnCode, ::android::hardware::keymaster::V4_0::KeyCharacteristics)>;
- void importWrappedKey(LockedKeyBlobEntry wrappingLockedEntry,
- LockedKeyBlobEntry wrapppedLockedEntry, hidl_vec<uint8_t> wrappedKeyData,
- hidl_vec<uint8_t> maskingKey, hidl_vec<KeyParameter> unwrappingParams,
- Blob wrappingBlob, Blob wrappingCharBlob, uint64_t passwordSid,
- uint64_t biometricSid, importWrappedKey_cb worker_cb);
-
- using exportKey_cb = std::function<void(::android::security::keymaster::ExportResult)>;
- void exportKey(LockedKeyBlobEntry lockedEntry, KeyFormat exportFormat,
- hidl_vec<uint8_t> clientId, hidl_vec<uint8_t> appData, Blob keyBlob,
- Blob charBlob, exportKey_cb _hidl_cb);
-
- using attestKey_cb = MakeKeymasterWorkerCB_t<Return<void>, Keymaster::attestKey_cb>;
- void attestKey(hidl_vec<uint8_t> keyToAttest, hidl_vec<KeyParameter> attestParams,
- attestKey_cb _hidl_cb);
-
- using deleteKey_cb = MakeKeymasterWorkerCB_t<Return<ErrorCode>>;
- void deleteKey(hidl_vec<uint8_t> keyBlob, deleteKey_cb _hidl_cb);
-
- using begin_cb = MakeKeymasterWorkerCB_t<Return<void>, Keymaster::begin_cb>;
- void begin(KeyPurpose purpose, hidl_vec<uint8_t> key, hidl_vec<KeyParameter> inParams,
- HardwareAuthToken authToken, begin_cb _hidl_cb);
-
- void binderDied(android::wp<IBinder> who);
-
- const Keymaster::VersionResult& halVersion() { return keymasterDevice_->halVersion(); }
-};
-
-} // namespace keystore
-
-#endif // KEYSTORE_KEYMASTER_WORKER_H_
diff --git a/keystore/keystore.rc b/keystore/keystore.rc
deleted file mode 100644
index 132039a..0000000
--- a/keystore/keystore.rc
+++ /dev/null
@@ -1,5 +0,0 @@
-service keystore /system/bin/keystore /data/misc/keystore
- class main
- user keystore
- group keystore drmrpc readproc log
- writepid /dev/cpuset/foreground/tasks
diff --git a/keystore/keystore_aidl_hidl_marshalling_utils.cpp b/keystore/keystore_aidl_hidl_marshalling_utils.cpp
deleted file mode 100644
index 823ca58..0000000
--- a/keystore/keystore_aidl_hidl_marshalling_utils.cpp
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
-**
-** Copyright 2016, 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.
-*/
-
-#include "keystore_aidl_hidl_marshalling_utils.h"
-
-#include <keystore/ExportResult.h>
-#include <keystore/KeyCharacteristics.h>
-#include <keystore/KeymasterBlob.h>
-#include <keystore/KeymasterCertificateChain.h>
-#include <keystore/keymaster_types.h>
-#include <keystore/keystore_hidl_support.h>
-
-namespace keystore {
-
-// reads byte[]
-hidl_vec<uint8_t> readKeymasterBlob(const android::Parcel& in) {
-
- ssize_t length = in.readInt32();
- if (length <= 0) {
- return {};
- }
-
- const void* buf = in.readInplace(length);
- if (!buf) return {};
-
- return blob2hidlVec(reinterpret_cast<const uint8_t*>(buf), size_t(length));
-}
-
-android::status_t writeKeymasterBlob(const hidl_vec<uint8_t>& blob, android::Parcel* out) {
- int32_t size = int32_t(std::min<size_t>(blob.size(), std::numeric_limits<int32_t>::max()));
-
- auto rc = out->writeInt32(size);
- if (rc != ::android::OK) return rc;
-
- if (!size) return ::android::OK;
-
- return out->write(blob.data(), size);
-}
-
-android::status_t writeKeymasterBlob(const ::std::vector<int32_t>& blob, android::Parcel* out) {
-
- int32_t size = int32_t(std::min<size_t>(blob.size(), std::numeric_limits<int32_t>::max()));
-
- auto rc = out->writeInt32(size);
- if (rc != ::android::OK) return rc;
-
- if (!size) return ::android::OK;
-
- return out->write(blob.data(), size);
-}
-
-NullOr<KeyParameter> readKeyParameterFromParcel(const android::Parcel& in) {
- // Method must be in sync with KeymasterArgument.java
- if (in.readInt32() == 0) {
- return {};
- }
- KeyParameter result;
-
- Tag tag = static_cast<Tag>(in.readInt32());
- result.tag = tag;
- switch (typeFromTag(tag)) {
- case TagType::ENUM:
- case TagType::ENUM_REP:
- case TagType::UINT:
- case TagType::UINT_REP:
- result.f.integer = in.readInt32();
- break;
- case TagType::ULONG:
- case TagType::ULONG_REP:
- case TagType::DATE:
- result.f.longInteger = in.readInt64();
- break;
- case TagType::BOOL:
- result.f.boolValue = true;
- break;
- case TagType::BIGNUM:
- case TagType::BYTES:
- result.blob = readKeymasterBlob(in); // byte array
- break;
- default:
- ALOGE("Unsupported KeyParameter tag %d", tag);
- return {};
- }
- return result;
-}
-
-android::status_t writeKeyParameterToParcel(const KeyParameter& param, android::Parcel* out) {
- // Method must be in sync with with KeymasterArgument.java
- // Presence flag must be written by caller.
-
- auto tag = param.tag;
- auto rc = out->writeInt32(uint32_t(tag));
- if (rc != ::android::OK) return rc;
- switch (typeFromTag(param.tag)) {
- case TagType::ENUM:
- case TagType::ENUM_REP:
- case TagType::UINT:
- case TagType::UINT_REP:
- rc = out->writeInt32(param.f.integer);
- break;
- case TagType::ULONG:
- case TagType::ULONG_REP:
- case TagType::DATE:
- rc = out->writeInt64(param.f.longInteger);
- break;
- case TagType::BOOL:
- // nothing to do here presence indicates true
- break;
- case TagType::BIGNUM:
- case TagType::BYTES:
- rc = writeKeymasterBlob(param.blob, out);
- break;
- default:
- ALOGE("Failed to write KeyParameter: Unsupported tag %d", param.tag);
- rc = android::BAD_VALUE;
- break;
- }
- return rc;
-}
-
-hidl_vec<KeyParameter> readParamSetFromParcel(const android::Parcel& in) {
-
- ssize_t length = in.readInt32(); // -1 for null
- size_t ulength = (size_t)length;
- if (length < 0) {
- ulength = 0;
- }
- hidl_vec<KeyParameter> result;
- result.resize(ulength);
- for (size_t i = 0; i < ulength; ++i) {
- auto param = readKeyParameterFromParcel(in);
- if (!param.isOk()) {
- ALOGE("Error reading KeyParameter from parcel");
- return {};
- }
- result[i] = param.value();
- }
- return result;
-}
-
-android::status_t writeParamSetToParcel(const hidl_vec<KeyParameter>& params,
- android::Parcel* out) {
- int32_t size = int32_t(std::min<size_t>(params.size(), std::numeric_limits<int32_t>::max()));
-
- auto rc = out->writeInt32(size);
- if (rc != ::android::OK) return rc;
- for (int32_t i = 0; i < size; ++i) {
- rc = out->writeInt32(1); // writeTypedObject presence flag.
- if (rc != ::android::OK) return rc;
- rc = writeKeyParameterToParcel(params[i], out);
- if (rc != ::android::OK) return rc;
- }
- return rc;
-}
-
-hidl_vec<hidl_vec<uint8_t>> readCertificateChainFromParcel(const android::Parcel& in) {
- hidl_vec<hidl_vec<uint8_t>> result;
-
- ssize_t count = in.readInt32();
- size_t ucount = count;
- if (count <= 0) {
- return result;
- }
-
- result.resize(ucount);
-
- for (size_t i = 0; i < ucount; ++i) {
- result[i] = readKeymasterBlob(in);
- }
- return result;
-};
-
-android::status_t writeCertificateChainToParcel(const hidl_vec<hidl_vec<uint8_t>>& certs,
- android::Parcel* out) {
- int32_t count = int32_t(std::min<size_t>(certs.size(), std::numeric_limits<int32_t>::max()));
- auto rc = out->writeInt32(count);
-
- for (int32_t i = 0; i < count; ++i) {
- rc = writeKeymasterBlob(certs[i], out);
- if (rc != ::android::OK) return rc;
- }
- return rc;
-}
-
-}; // namespace keystore
-
-// Implementation for keystore parcelables.
-// TODO: split implementation into separate classes
-namespace android {
-namespace security {
-namespace keymaster {
-
-using ::android::status_t;
-using ::keystore::ErrorCode;
-
-ExportResult::ExportResult() : resultCode() {}
-
-ExportResult::~ExportResult() {}
-
-status_t ExportResult::readFromParcel(const Parcel* inn) {
- const Parcel& in = *inn;
- resultCode = ErrorCode(in.readInt32());
- exportData = keystore::readKeymasterBlob(in);
- return OK;
-}
-
-status_t ExportResult::writeToParcel(Parcel* out) const {
- out->writeInt32(resultCode.getErrorCode());
- return keystore::writeKeymasterBlob(exportData, out);
-}
-
-status_t KeyCharacteristics::readFromParcel(const Parcel* in) {
- softwareEnforced.readFromParcel(in);
- return hardwareEnforced.readFromParcel(in);
-}
-
-status_t KeyCharacteristics::writeToParcel(Parcel* out) const {
- softwareEnforced.writeToParcel(out);
- return hardwareEnforced.writeToParcel(out);
-}
-
-status_t KeymasterBlob::readFromParcel(const Parcel* in) {
- data_ = keystore::readKeymasterBlob(*in);
- return OK;
-}
-
-status_t KeymasterBlob::writeToParcel(Parcel* out) const {
- return keystore::writeKeymasterBlob(data_, out);
-}
-
-status_t KeymasterCertificateChain::readFromParcel(const Parcel* in) {
- chain = keystore::readCertificateChainFromParcel(*in);
- return OK;
-}
-
-status_t KeymasterCertificateChain::writeToParcel(Parcel* out) const {
- return keystore::writeCertificateChainToParcel(chain, out);
-}
-
-} // namespace keymaster
-} // namespace security
-
-} // namespace android
diff --git a/keystore/keystore_aidl_hidl_marshalling_utils.h b/keystore/keystore_aidl_hidl_marshalling_utils.h
deleted file mode 100644
index ea72197..0000000
--- a/keystore/keystore_aidl_hidl_marshalling_utils.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
-**
-** Copyright 2016, 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.
-*/
-
-#ifndef KEYSTORE_KEYSTORE_AIDL_HIDL_MARSHALLING_UTILS_H_
-#define KEYSTORE_KEYSTORE_AIDL_HIDL_MARSHALLING_UTILS_H_
-
-#include <utility>
-
-#include <binder/Parcel.h>
-
-#include <keystore/keymaster_types.h>
-
-namespace keystore {
-
-template <typename Fn, typename... Args>
-inline auto nullable(Fn fn, const android::Parcel& in, Args&&... args)
- -> NullOr<decltype(fn(in, std::forward<Args>(args)...))> {
- if (in.readInt32() != 1) {
- return {};
- }
-
- return fn(in, std::forward<Args>(args)...);
-}
-template <typename Fn, typename Arg>
-inline android::status_t nullable(Fn fn, const NullOr<Arg>& arg, android::Parcel* out) {
- if (!arg.isOk()) {
- return out->writeInt32(0);
- }
- auto rc = out->writeInt32(1);
- if (rc != ::android::OK) return rc;
-
- return fn(arg.value(), out);
-}
-template <typename Fn, typename Arg>
-inline android::status_t nullable(Fn fn, Arg&& arg, android::Parcel* out) {
- auto rc = out->writeInt32(1);
- if (rc != ::android::OK) return rc;
-
- return fn(std::forward<Arg>(arg), out);
-}
-
-inline android::status_t nullable(android::Parcel* out) {
- return out->writeInt32(0);
-}
-
-/**
- * makes a copy only if inPlace is false
- */
-hidl_vec<uint8_t> readKeymasterBlob(const android::Parcel& in);
-android::status_t writeKeymasterBlob(const hidl_vec<uint8_t>& blob, android::Parcel* out);
-
-NullOr<hidl_vec<uint8_t>> readBlobAsByteArray(const android::Parcel& in, bool inPlace = true);
-android::status_t writeBlobAsByteArray(const NullOr<const hidl_vec<uint8_t>&>& blob,
- android::Parcel* out);
-
-NullOr<KeyParameter> readKeyParameterFromParcel(const android::Parcel& in);
-android::status_t writeKeyParameterToParcel(const KeyParameter& param, android::Parcel* out);
-
-hidl_vec<KeyParameter> readParamSetFromParcel(const android::Parcel& in);
-android::status_t writeParamSetToParcel(const hidl_vec<KeyParameter>& params, android::Parcel* out);
-
-hidl_vec<hidl_vec<uint8_t>> readCertificateChainFromParcel(const android::Parcel& in);
-}
-
-#endif // KEYSTORE_KEYSTORE_AIDL_HIDL_MARSHALLING_UTILS_H_
diff --git a/keystore/keystore_cli.cpp b/keystore/keystore_cli.cpp
deleted file mode 100644
index 428a9bc..0000000
--- a/keystore/keystore_cli.cpp
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (C) 2009 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.
- */
-
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/types.h>
-#include <vector>
-
-#include <android/security/keystore/IKeystoreService.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-
-#include <keystore/keystore.h>
-
-using namespace android;
-using namespace keystore;
-using android::security::keystore::IKeystoreService;
-
-static const char* responses[] = {
- nullptr,
- /* [NO_ERROR] = */ "No error",
- /* [LOCKED] = */ "Locked",
- /* [UNINITIALIZED] = */ "Uninitialized",
- /* [SYSTEM_ERROR] = */ "System error",
- /* [PROTOCOL_ERROR] = */ "Protocol error",
- /* [PERMISSION_DENIED] = */ "Permission denied",
- /* [KEY_NOT_FOUND] = */ "Key not found",
- /* [VALUE_CORRUPTED] = */ "Value corrupted",
- /* [UNDEFINED_ACTION] = */ "Undefined action",
- /* [WRONG_PASSWORD] = */ "Wrong password (last chance)",
- /* [WRONG_PASSWORD + 1] = */ "Wrong password (2 tries left)",
- /* [WRONG_PASSWORD + 2] = */ "Wrong password (3 tries left)",
- /* [WRONG_PASSWORD + 3] = */ "Wrong password (4 tries left)",
-};
-
-#define SINGLE_ARG_INT_RETURN(cmd) \
- do { \
- if (strcmp(argv[1], #cmd) == 0) { \
- if (argc < 3) { \
- fprintf(stderr, "Usage: %s " #cmd " <name>\n", argv[0]); \
- return 1; \
- } \
- int32_t ret = -1; \
- service->cmd(String16(argv[2]), &ret); \
- if (ret < 0) { \
- fprintf(stderr, "%s: could not connect: %d\n", argv[0], ret); \
- return 1; \
- } else { \
- printf(#cmd ": %s (%d)\n", responses[ret], ret); \
- return 0; \
- } \
- } \
- } while (0)
-
-#define SINGLE_INT_ARG_INT_RETURN(cmd) \
- do { \
- if (strcmp(argv[1], #cmd) == 0) { \
- if (argc < 3) { \
- fprintf(stderr, "Usage: %s " #cmd " <name>\n", argv[0]); \
- return 1; \
- } \
- int32_t ret = -1; \
- service->cmd(atoi(argv[2]), &ret); \
- if (ret < 0) { \
- fprintf(stderr, "%s: could not connect: %d\n", argv[0], ret); \
- return 1; \
- } else { \
- printf(#cmd ": %s (%d)\n", responses[ret], ret); \
- return 0; \
- } \
- } \
- } while (0)
-
-#define SINGLE_ARG_PLUS_UID_INT_RETURN(cmd) \
- do { \
- if (strcmp(argv[1], #cmd) == 0) { \
- if (argc < 3) { \
- fprintf(stderr, "Usage: %s " #cmd " <name> <uid>\n", argv[0]); \
- return 1; \
- } \
- int uid = -1; \
- if (argc > 3) { \
- uid = atoi(argv[3]); \
- fprintf(stderr, "Running as uid %d\n", uid); \
- } \
- int32_t ret = -1; \
- service->cmd(String16(argv[2]), uid, &ret); \
- if (ret < 0) { \
- fprintf(stderr, "%s: could not connect: %d\n", argv[0], ret); \
- return 1; \
- } else { \
- printf(#cmd ": %s (%d)\n", responses[ret], ret); \
- return 0; \
- } \
- } \
- } while (0)
-
-#define SINGLE_ARG_PLUS_UID_DATA_RETURN(cmd) \
- do { \
- if (strcmp(argv[1], #cmd) == 0) { \
- if (argc < 3) { \
- fprintf(stderr, "Usage: %s " #cmd " <name> <uid>\n", argv[0]); \
- return 1; \
- } \
- std::vector<uint8_t> data; \
- int uid = -1; \
- if (argc > 3) { \
- uid = atoi(argv[3]); \
- fprintf(stderr, "Running as uid %d\n", uid); \
- } \
- ::android::binder::Status ret = service->cmd(String16(argv[2]), uid, &data); \
- if (!ret.isOk()) { \
- fprintf(stderr, "Exception code: %d\n", ret.exceptionCode()); \
- return 1; \
- } else { \
- fwrite(&data[0], data.size(), 1, stdout); \
- fflush(stdout); \
- return 0; \
- } \
- } \
- } while (0)
-
-#define STRING_ARG_DATA_STDIN_INT_RETURN(cmd) \
- do { \
- if (strcmp(argv[1], #cmd) == 0) { \
- if (argc < 3) { \
- fprintf(stderr, "Usage: %s " #cmd " <name>\n", argv[0]); \
- return 1; \
- } \
- uint8_t* data; \
- size_t dataSize; \
- read_input(&data, &dataSize); \
- int32_t ret = -1; \
- service->cmd(String16(argv[2]), data, dataSize, &ret); \
- if (ret < 0) { \
- fprintf(stderr, "%s: could not connect: %d\n", argv[0], ret); \
- return 1; \
- } else { \
- printf(#cmd ": %s (%d)\n", responses[ret], ret); \
- return 0; \
- } \
- } \
- } while (0)
-
-#define SINGLE_ARG_DATA_RETURN(cmd) \
- do { \
- if (strcmp(argv[1], #cmd) == 0) { \
- if (argc < 3) { \
- fprintf(stderr, "Usage: %s " #cmd " <name>\n", argv[0]); \
- return 1; \
- } \
- std::vector<uint8_t> data; \
- ::android::binder::Status ret = service->cmd(String16(argv[2]), &data); \
- if (!ret.isOk()) { \
- fprintf(stderr, "Exception code: %d\n", ret.exceptionCode()); \
- return 1; \
- } else { \
- fwrite(&data[0], data.size(), 1, stdout); \
- fflush(stdout); \
- return 0; \
- } \
- } \
- } while (0)
-
-static int list(const sp<IKeystoreService>& service, const String16& name, int uid) {
- std::vector<String16> matches;
- ::android::binder::Status ret = service->list(name, uid, &matches);
-
- if (!ret.isOk()) {
- fprintf(stderr, "list: exception (%d)\n", ret.exceptionCode());
- return 1;
- } else {
- std::vector<String16>::const_iterator it = matches.begin();
- for (; it != matches.end(); ++it) {
- printf("%s\n", String8(*it).string());
- }
- return 0;
- }
-}
-
-int main(int argc, char* argv[])
-{
- if (argc < 2) {
- fprintf(stderr, "Usage: %s action [parameter ...]\n", argv[0]);
- return 1;
- }
-
- sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
- sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
-
- if (service == nullptr) {
- fprintf(stderr, "%s: error: could not connect to keystore service\n", argv[0]);
- return 1;
- }
-
- /*
- * All the commands should return a value
- */
-
- SINGLE_INT_ARG_INT_RETURN(getState);
-
- SINGLE_ARG_PLUS_UID_DATA_RETURN(get);
-
- // TODO: insert
-
- SINGLE_ARG_PLUS_UID_INT_RETURN(del);
-
- SINGLE_ARG_PLUS_UID_INT_RETURN(exist);
-
- if (strcmp(argv[1], "list") == 0) {
- return list(service, argc < 3 ? String16("") : String16(argv[2]),
- argc < 4 ? -1 : atoi(argv[3]));
- }
-
- // TODO: notifyUserPasswordChanged
-
- SINGLE_INT_ARG_INT_RETURN(lock);
-
- // TODO: unlock
-
- SINGLE_INT_ARG_INT_RETURN(isEmpty);
-
- // TODO: generate
-
- // TODO: grant
-
- // TODO: ungrant
-
- // TODO: getmtime
-
- fprintf(stderr, "%s: unknown command: %s\n", argv[0], argv[1]);
- return 1;
-}
diff --git a/keystore/keystore_cli_v2.cpp b/keystore/keystore_cli_v2.cpp
index 4f69eb0..43f72a9 100644
--- a/keystore/keystore_cli_v2.cpp
+++ b/keystore/keystore_cli_v2.cpp
@@ -15,6 +15,8 @@
#include <chrono>
#include <cstdio>
#include <future>
+#include <iomanip>
+#include <iostream>
#include <memory>
#include <string>
#include <vector>
@@ -24,38 +26,56 @@
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
-#include <base/strings/utf_string_conversions.h>
-#include <base/threading/platform_thread.h>
-#include <keystore/keymaster_types.h>
-#include <keystore/keystore_client_impl.h>
-#include <android/hardware/confirmationui/1.0/types.h>
-#include <android/security/BnConfirmationPromptCallback.h>
-#include <android/security/keystore/IKeystoreService.h>
+#include <aidl/android/security/apc/BnConfirmationCallback.h>
+#include <aidl/android/security/apc/IProtectedConfirmation.h>
+#include <aidl/android/system/keystore2/IKeystoreService.h>
+#include <aidl/android/system/keystore2/ResponseCode.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <keymint_support/authorization_set.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+#include <openssl/x509.h>
-//#include <keystore/keystore.h>
+#include "keystore_client.pb.h"
+
+namespace apc = ::aidl::android::security::apc;
+namespace keymint = ::aidl::android::hardware::security::keymint;
+namespace ks2 = ::aidl::android::system::keystore2;
using base::CommandLine;
-using keystore::KeystoreClient;
-
-using android::sp;
-using android::String16;
-using android::security::keystore::IKeystoreService;
-using base::CommandLine;
-using ConfirmationResponseCode = android::hardware::confirmationui::V1_0::ResponseCode;
+using keystore::EncryptedData;
namespace {
-using namespace keystore;
struct TestCase {
std::string name;
bool required_for_brillo_pts;
- AuthorizationSet parameters;
+ keymint::AuthorizationSet parameters;
};
+constexpr const char keystore2_service_name[] = "android.system.keystore2.IKeystoreService/default";
+
+int unwrapError(const ndk::ScopedAStatus& status) {
+ if (status.isOk()) return 0;
+ if (status.getExceptionCode() == EX_SERVICE_SPECIFIC) {
+ return status.getServiceSpecificError();
+ } else {
+ return static_cast<int>(ks2::ResponseCode::SYSTEM_ERROR);
+ }
+}
+
+ks2::KeyDescriptor keyDescriptor(const std::string& alias) {
+ return {
+ .domain = ks2::Domain::APP,
+ .nspace = -1, // ignored - should be -1.
+ .alias = alias,
+ .blob = {},
+ };
+}
+
void PrintUsageAndExit() {
printf("Usage: keystore_client_v2 <command> [options]\n");
printf("Commands: brillo-platform-test [--prefix=<test_name_prefix>] [--test_for_0_3]\n"
@@ -78,52 +98,487 @@
exit(1);
}
-std::unique_ptr<KeystoreClient> CreateKeystoreInstance() {
- return std::unique_ptr<KeystoreClient>(
- static_cast<KeystoreClient*>(new keystore::KeystoreClientImpl));
+std::shared_ptr<ks2::IKeystoreService> CreateKeystoreInstance() {
+ ::ndk::SpAIBinder keystoreBinder(AServiceManager_checkService(keystore2_service_name));
+ auto result = ks2::IKeystoreService::fromBinder(keystoreBinder);
+ if (result) return result;
+ std::cerr << "Unable to connect to Keystore.";
+ exit(-1);
}
-void PrintTags(const AuthorizationSet& parameters) {
- for (auto iter = parameters.begin(); iter != parameters.end(); ++iter) {
- auto tag_str = toString(iter->tag);
- printf(" %s\n", tag_str.c_str());
+std::shared_ptr<ks2::IKeystoreSecurityLevel>
+GetSecurityLevelInterface(std::shared_ptr<ks2::IKeystoreService> keystore,
+ keymint::SecurityLevel securitylevel) {
+ std::shared_ptr<ks2::IKeystoreSecurityLevel> sec_level;
+ auto rc = keystore->getSecurityLevel(securitylevel, &sec_level);
+ if (rc.isOk()) return sec_level;
+ std::cerr << "Unable to get security level interface from Keystore: " << rc.getDescription();
+ exit(-1);
+}
+
+bool isHardwareEnforced(const ks2::Authorization& a) {
+ return !(a.securityLevel == keymint::SecurityLevel::SOFTWARE ||
+ a.securityLevel == keymint::SecurityLevel::KEYSTORE);
+}
+
+void PrintTags(const std::vector<ks2::Authorization>& characteristics, bool printHardwareEnforced) {
+ for (const auto& a : characteristics) {
+ if (isHardwareEnforced(a) == printHardwareEnforced) {
+ std::cout << toString(a.keyParameter.tag) << "\n";
+ }
}
}
-void PrintKeyCharacteristics(const AuthorizationSet& hardware_enforced_characteristics,
- const AuthorizationSet& software_enforced_characteristics) {
+void PrintKeyCharacteristics(const std::vector<ks2::Authorization>& characteristics) {
printf("Hardware:\n");
- PrintTags(hardware_enforced_characteristics);
+ PrintTags(characteristics, true /* printHardwareEnforced */);
printf("Software:\n");
- PrintTags(software_enforced_characteristics);
+ PrintTags(characteristics, false /* printHardwareEnforced */);
}
-bool TestKey(const std::string& name, bool required, const AuthorizationSet& parameters) {
- std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
- AuthorizationSet hardware_enforced_characteristics;
- AuthorizationSet software_enforced_characteristics;
- auto result =
- keystore->generateKey("tmp", parameters, 0 /*flags*/, &hardware_enforced_characteristics,
- &software_enforced_characteristics);
+const char kEncryptSuffix[] = "_ENC";
+const char kAuthenticateSuffix[] = "_AUTH";
+constexpr uint32_t kAESKeySize = 256; // bits
+constexpr uint32_t kHMACKeySize = 256; // bits
+constexpr uint32_t kHMACOutputSize = 256; // bits
+
+bool verifyEncryptionKeyAttributes(const std::vector<ks2::Authorization> authorizations) {
+ bool verified = true;
+ verified =
+ verified &&
+ std::any_of(authorizations.begin(), authorizations.end(), [&](const ks2::Authorization& a) {
+ return a.keyParameter.tag == keymint::Tag::ALGORITHM &&
+ a.keyParameter.value ==
+ keymint::KeyParameterValue::make<keymint::KeyParameterValue::algorithm>(
+ keymint::Algorithm::AES);
+ });
+
+ verified =
+ verified &&
+ std::any_of(authorizations.begin(), authorizations.end(), [&](const ks2::Authorization& a) {
+ return a.keyParameter.tag == keymint::Tag::KEY_SIZE &&
+ a.keyParameter.value ==
+ keymint::KeyParameterValue::make<keymint::KeyParameterValue::integer>(
+ kAESKeySize);
+ });
+
+ verified =
+ verified &&
+ std::any_of(authorizations.begin(), authorizations.end(), [&](const ks2::Authorization& a) {
+ return a.keyParameter.tag == keymint::Tag::BLOCK_MODE &&
+ a.keyParameter.value ==
+ keymint::KeyParameterValue::make<keymint::KeyParameterValue::blockMode>(
+ keymint::BlockMode::CBC);
+ });
+
+ verified =
+ verified &&
+ std::any_of(authorizations.begin(), authorizations.end(), [&](const ks2::Authorization& a) {
+ return a.keyParameter.tag == keymint::Tag::PADDING &&
+ a.keyParameter.value ==
+ keymint::KeyParameterValue::make<keymint::KeyParameterValue::paddingMode>(
+ keymint::PaddingMode::PKCS7);
+ });
+
+ return verified;
+}
+
+bool verifyAuthenticationKeyAttributes(const std::vector<ks2::Authorization> authorizations) {
+ bool verified = true;
+ verified =
+ verified &&
+ std::any_of(authorizations.begin(), authorizations.end(), [&](const ks2::Authorization& a) {
+ return a.keyParameter.tag == keymint::Tag::ALGORITHM &&
+ a.keyParameter.value ==
+ keymint::KeyParameterValue::make<keymint::KeyParameterValue::algorithm>(
+ keymint::Algorithm::HMAC);
+ });
+
+ verified =
+ verified &&
+ std::any_of(authorizations.begin(), authorizations.end(), [&](const ks2::Authorization& a) {
+ return a.keyParameter.tag == keymint::Tag::KEY_SIZE &&
+ a.keyParameter.value ==
+ keymint::KeyParameterValue::make<keymint::KeyParameterValue::integer>(
+ kHMACKeySize);
+ });
+
+ verified =
+ verified &&
+ std::any_of(authorizations.begin(), authorizations.end(), [&](const ks2::Authorization& a) {
+ return a.keyParameter.tag == keymint::Tag::MIN_MAC_LENGTH &&
+ a.keyParameter.value ==
+ keymint::KeyParameterValue::make<keymint::KeyParameterValue::integer>(
+ kHMACOutputSize);
+ });
+
+ verified =
+ verified &&
+ std::any_of(authorizations.begin(), authorizations.end(), [&](const ks2::Authorization& a) {
+ return a.keyParameter.tag == keymint::Tag::DIGEST &&
+ a.keyParameter.value ==
+ keymint::KeyParameterValue::make<keymint::KeyParameterValue::digest>(
+ keymint::Digest::SHA_2_256);
+ });
+ return verified;
+}
+
+std::variant<int, ks2::KeyEntryResponse>
+loadOrCreateAndVerifyEncryptionKey(const std::string& name, keymint::SecurityLevel securityLevel,
+ bool create) {
+ auto keystore = CreateKeystoreInstance();
+
+ ks2::KeyEntryResponse keyEntryResponse;
+
+ bool foundKey = true;
+ auto rc = keystore->getKeyEntry(keyDescriptor(name), &keyEntryResponse);
+ if (!rc.isOk()) {
+ auto error = unwrapError(rc);
+ if (ks2::ResponseCode(error) == ks2::ResponseCode::KEY_NOT_FOUND && create) {
+ foundKey = false;
+ } else {
+ std::cerr << "Failed to get key entry: " << rc.getDescription() << std::endl;
+ return error;
+ }
+ }
+
+ if (!foundKey) {
+ auto sec_level = GetSecurityLevelInterface(keystore, securityLevel);
+ auto params = keymint::AuthorizationSetBuilder()
+ .AesEncryptionKey(kAESKeySize)
+ .Padding(keymint::PaddingMode::PKCS7)
+ .Authorization(keymint::TAG_BLOCK_MODE, keymint::BlockMode::CBC)
+ .Authorization(keymint::TAG_NO_AUTH_REQUIRED);
+
+ ks2::KeyMetadata keyMetadata;
+
+ rc = sec_level->generateKey(keyDescriptor(name), {} /* attestationKey */,
+ params.vector_data(), 0 /* flags */, {} /* entropy */,
+ &keyMetadata);
+ if (!rc.isOk()) {
+ std::cerr << "Failed to generate key: " << rc.getDescription() << std::endl;
+ return unwrapError(rc);
+ }
+
+ rc = keystore->getKeyEntry(keyDescriptor(name), &keyEntryResponse);
+ if (!rc.isOk()) {
+ std::cerr << "Failed to get key entry (second try): " << rc.getDescription()
+ << std::endl;
+ return unwrapError(rc);
+ }
+ }
+
+ if (!verifyEncryptionKeyAttributes(keyEntryResponse.metadata.authorizations)) {
+ std::cerr << "Key has wrong set of parameters." << std::endl;
+ return static_cast<int>(ks2::ResponseCode::INVALID_ARGUMENT);
+ }
+
+ return keyEntryResponse;
+}
+
+std::variant<int, ks2::KeyEntryResponse>
+loadOrCreateAndVerifyAuthenticationKey(const std::string& name,
+ keymint::SecurityLevel securityLevel, bool create) {
+ auto keystore = CreateKeystoreInstance();
+
+ ks2::KeyEntryResponse keyEntryResponse;
+
+ bool foundKey = true;
+ auto rc = keystore->getKeyEntry(keyDescriptor(name), &keyEntryResponse);
+ if (!rc.isOk()) {
+ auto error = unwrapError(rc);
+ if (ks2::ResponseCode(error) == ks2::ResponseCode::KEY_NOT_FOUND && create) {
+ foundKey = false;
+ } else {
+ std::cerr << "Failed to get HMAC key entry: " << rc.getDescription() << std::endl;
+ return error;
+ }
+ }
+
+ if (!foundKey) {
+ auto sec_level = GetSecurityLevelInterface(keystore, securityLevel);
+ auto params = keymint::AuthorizationSetBuilder()
+ .HmacKey(kHMACKeySize)
+ .Digest(keymint::Digest::SHA_2_256)
+ .Authorization(keymint::TAG_MIN_MAC_LENGTH, kHMACOutputSize)
+ .Authorization(keymint::TAG_NO_AUTH_REQUIRED);
+
+ ks2::KeyMetadata keyMetadata;
+
+ rc = sec_level->generateKey(keyDescriptor(name), {} /* attestationKey */,
+ params.vector_data(), 0 /* flags */, {} /* entropy */,
+ &keyMetadata);
+ if (!rc.isOk()) {
+ std::cerr << "Failed to generate HMAC key: " << rc.getDescription() << std::endl;
+ return unwrapError(rc);
+ }
+
+ rc = keystore->getKeyEntry(keyDescriptor(name), &keyEntryResponse);
+ if (!rc.isOk()) {
+ std::cerr << "Failed to get HMAC key entry (second try): " << rc.getDescription()
+ << std::endl;
+ return unwrapError(rc);
+ }
+ }
+
+ if (!verifyAuthenticationKeyAttributes(keyEntryResponse.metadata.authorizations)) {
+ std::cerr << "Key has wrong set of parameters." << std::endl;
+ return static_cast<int>(ks2::ResponseCode::INVALID_ARGUMENT);
+ }
+
+ return keyEntryResponse;
+}
+
+std::variant<int, std::vector<uint8_t>>
+encryptWithAuthentication(const std::string& name, const std::vector<uint8_t>& data,
+ keymint::SecurityLevel securityLevel) {
+ // The encryption algorithm is AES-256-CBC with PKCS #7 padding and a random
+ // IV. The authentication algorithm is HMAC-SHA256 and is computed over the
+ // cipher-text (i.e. Encrypt-then-MAC approach). This was chosen over AES-GCM
+ // because hardware support for GCM is not mandatory for all Brillo devices.
+ std::string encryption_key_name = name + kEncryptSuffix;
+ auto encryption_key_result =
+ loadOrCreateAndVerifyEncryptionKey(encryption_key_name, securityLevel, true /* create */);
+ if (auto error = std::get_if<int>(&encryption_key_result)) {
+ return *error;
+ }
+ auto encryption_key = std::get<ks2::KeyEntryResponse>(encryption_key_result);
+
+ std::string authentication_key_name = name + kAuthenticateSuffix;
+ auto authentication_key_result = loadOrCreateAndVerifyAuthenticationKey(
+ authentication_key_name, securityLevel, true /* create */);
+ if (auto error = std::get_if<int>(&authentication_key_result)) {
+ return *error;
+ }
+ auto authentication_key = std::get<ks2::KeyEntryResponse>(authentication_key_result);
+
+ ks2::CreateOperationResponse encOperationResponse;
+ auto encrypt_params = keymint::AuthorizationSetBuilder()
+ .Authorization(keymint::TAG_PURPOSE, keymint::KeyPurpose::ENCRYPT)
+ .Padding(keymint::PaddingMode::PKCS7)
+ .Authorization(keymint::TAG_BLOCK_MODE, keymint::BlockMode::CBC);
+
+ auto rc = encryption_key.iSecurityLevel->createOperation(
+ encryption_key.metadata.key, encrypt_params.vector_data(), false /* forced */,
+ &encOperationResponse);
+ if (!rc.isOk()) {
+ std::cerr << "Failed to begin encryption operation: " << rc.getDescription() << std::endl;
+ return unwrapError(rc);
+ }
+
+ std::optional<std::vector<uint8_t>> optCiphertext;
+
+ rc = encOperationResponse.iOperation->finish(data, {}, &optCiphertext);
+ if (!rc.isOk()) {
+ std::cerr << "Failed to finish encryption operation: " << rc.getDescription() << std::endl;
+ return unwrapError(rc);
+ }
+
+ std::vector<uint8_t> initVector;
+ if (auto params = encOperationResponse.parameters) {
+ for (auto& p : params->keyParameter) {
+ if (auto iv = keymint::authorizationValue(keymint::TAG_NONCE, p)) {
+ initVector = std::move(iv->get());
+ break;
+ }
+ }
+ if (initVector.empty()) {
+ std::cerr << "Encryption operation did not return an IV." << std::endl;
+ return static_cast<int>(ks2::ResponseCode::SYSTEM_ERROR);
+ }
+ }
+
+ if (!optCiphertext) {
+ std::cerr << "Encryption succeeded but no ciphertext returned." << std::endl;
+ return static_cast<int>(ks2::ResponseCode::SYSTEM_ERROR);
+ }
+
+ auto ciphertext = std::move(*optCiphertext);
+ auto toBeSigned = initVector;
+ toBeSigned.insert(toBeSigned.end(), ciphertext.begin(), ciphertext.end());
+
+ ks2::CreateOperationResponse signOperationResponse;
+ auto sign_params = keymint::AuthorizationSetBuilder()
+ .Authorization(keymint::TAG_PURPOSE, keymint::KeyPurpose::SIGN)
+ .Digest(keymint::Digest::SHA_2_256)
+ .Authorization(keymint::TAG_MAC_LENGTH, kHMACOutputSize);
+
+ rc = authentication_key.iSecurityLevel->createOperation(
+ authentication_key.metadata.key, sign_params.vector_data(), false /* forced */,
+ &signOperationResponse);
+ if (!rc.isOk()) {
+ std::cerr << "Failed to begin signing operation: " << rc.getDescription() << std::endl;
+ return unwrapError(rc);
+ }
+
+ std::optional<std::vector<uint8_t>> optMac;
+
+ rc = signOperationResponse.iOperation->finish(toBeSigned, {}, &optMac);
+ if (!rc.isOk()) {
+ std::cerr << "Failed to finish encryption operation: " << rc.getDescription() << std::endl;
+ return unwrapError(rc);
+ }
+
+ if (!optMac) {
+ std::cerr << "Signing succeeded but no MAC returned." << std::endl;
+ return static_cast<int>(ks2::ResponseCode::SYSTEM_ERROR);
+ }
+
+ auto mac = std::move(*optMac);
+
+ EncryptedData protobuf;
+ protobuf.set_init_vector(initVector.data(), initVector.size());
+ protobuf.set_authentication_data(mac.data(), mac.size());
+ protobuf.set_encrypted_data(ciphertext.data(), ciphertext.size());
+ std::string resultString;
+ if (!protobuf.SerializeToString(&resultString)) {
+ std::cerr << "Encrypt: Failed to serialize EncryptedData protobuf.";
+ return static_cast<int>(ks2::ResponseCode::SYSTEM_ERROR);
+ }
+
+ std::vector<uint8_t> result(reinterpret_cast<const uint8_t*>(resultString.data()),
+ reinterpret_cast<const uint8_t*>(resultString.data()) +
+ resultString.size());
+ return result;
+}
+
+std::variant<int, std::vector<uint8_t>>
+decryptWithAuthentication(const std::string& name, const std::vector<uint8_t>& data) {
+
+ // Decode encrypted data
+ EncryptedData protobuf;
+ if (!protobuf.ParseFromArray(data.data(), data.size())) {
+ std::cerr << "Decrypt: Failed to parse EncryptedData protobuf." << std::endl;
+ return static_cast<int>(ks2::ResponseCode::SYSTEM_ERROR);
+ }
+
+ // Load encryption and authentication keys.
+ std::string encryption_key_name = name + kEncryptSuffix;
+ auto encryption_key_result = loadOrCreateAndVerifyEncryptionKey(
+ encryption_key_name, keymint::SecurityLevel::KEYSTORE /* ignored */, false /* create */);
+ if (auto error = std::get_if<int>(&encryption_key_result)) {
+ return *error;
+ }
+ auto encryption_key = std::get<ks2::KeyEntryResponse>(encryption_key_result);
+
+ std::string authentication_key_name = name + kAuthenticateSuffix;
+ auto authentication_key_result = loadOrCreateAndVerifyAuthenticationKey(
+ authentication_key_name, keymint::SecurityLevel::KEYSTORE /* ignored */,
+ false /* create */);
+ if (auto error = std::get_if<int>(&authentication_key_result)) {
+ return *error;
+ }
+ auto authentication_key = std::get<ks2::KeyEntryResponse>(authentication_key_result);
+
+ // Begin authentication operation
+ ks2::CreateOperationResponse signOperationResponse;
+ auto sign_params = keymint::AuthorizationSetBuilder()
+ .Authorization(keymint::TAG_PURPOSE, keymint::KeyPurpose::VERIFY)
+ .Digest(keymint::Digest::SHA_2_256)
+ .Authorization(keymint::TAG_MAC_LENGTH, kHMACOutputSize);
+
+ auto rc = authentication_key.iSecurityLevel->createOperation(
+ authentication_key.metadata.key, sign_params.vector_data(), false /* forced */,
+ &signOperationResponse);
+ if (!rc.isOk()) {
+ std::cerr << "Failed to begin verify operation: " << rc.getDescription() << std::endl;
+ return unwrapError(rc);
+ }
+
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(protobuf.init_vector().data());
+ std::vector<uint8_t> toBeVerified(p, p + protobuf.init_vector().size());
+
+ p = reinterpret_cast<const uint8_t*>(protobuf.encrypted_data().data());
+ toBeVerified.insert(toBeVerified.end(), p, p + protobuf.encrypted_data().size());
+
+ p = reinterpret_cast<const uint8_t*>(protobuf.authentication_data().data());
+ std::vector<uint8_t> signature(p, p + protobuf.authentication_data().size());
+
+ std::optional<std::vector<uint8_t>> optOut;
+ rc = signOperationResponse.iOperation->finish(toBeVerified, signature, &optOut);
+ if (!rc.isOk()) {
+ std::cerr << "Decrypt: HMAC verification failed: " << rc.getDescription() << std::endl;
+ return unwrapError(rc);
+ }
+
+ // Begin decryption operation
+ ks2::CreateOperationResponse encOperationResponse;
+ auto encrypt_params = keymint::AuthorizationSetBuilder()
+ .Authorization(keymint::TAG_PURPOSE, keymint::KeyPurpose::DECRYPT)
+ .Authorization(keymint::TAG_NONCE, protobuf.init_vector().data(),
+ protobuf.init_vector().size())
+ .Padding(keymint::PaddingMode::PKCS7)
+ .Authorization(keymint::TAG_BLOCK_MODE, keymint::BlockMode::CBC);
+
+ rc = encryption_key.iSecurityLevel->createOperation(encryption_key.metadata.key,
+ encrypt_params.vector_data(),
+ false /* forced */, &encOperationResponse);
+ if (!rc.isOk()) {
+ std::cerr << "Failed to begin encryption operation: " << rc.getDescription() << std::endl;
+ return unwrapError(rc);
+ }
+
+ std::optional<std::vector<uint8_t>> optPlaintext;
+
+ p = reinterpret_cast<const uint8_t*>(protobuf.encrypted_data().data());
+ std::vector<uint8_t> cyphertext(p, p + protobuf.encrypted_data().size());
+
+ rc = encOperationResponse.iOperation->finish(cyphertext, {}, &optPlaintext);
+ if (!rc.isOk()) {
+ std::cerr << "Failed to finish encryption operation: " << rc.getDescription() << std::endl;
+ return unwrapError(rc);
+ }
+
+ if (!optPlaintext) {
+ std::cerr << "Decryption succeeded but no plaintext returned." << std::endl;
+ return static_cast<int>(ks2::ResponseCode::SYSTEM_ERROR);
+ }
+
+ return *optPlaintext;
+}
+
+bool TestKey(const std::string& name, bool required,
+ const std::vector<keymint::KeyParameter>& parameters) {
+ auto keystore = CreateKeystoreInstance();
+ auto sec_level =
+ GetSecurityLevelInterface(keystore, keymint::SecurityLevel::TRUSTED_ENVIRONMENT);
+
+ ks2::KeyDescriptor keyDescriptor = {
+ .domain = ks2::Domain::APP,
+ .nspace = -1,
+ .alias = "tmp",
+ .blob = {},
+ };
+
+ ks2::KeyMetadata keyMetadata;
+
+ auto rc = sec_level->generateKey(keyDescriptor, {} /* attestationKey */, parameters,
+ 0 /* flags */, {} /* entropy */, &keyMetadata);
const char kBoldRedAbort[] = "\033[1;31mABORT\033[0m";
- if (!result.isOk()) {
- LOG(ERROR) << "Failed to generate key: " << result;
+ if (!rc.isOk()) {
+ LOG(ERROR) << "Failed to generate key: " << rc.getDescription();
printf("[%s] %s\n", kBoldRedAbort, name.c_str());
return false;
}
- result = keystore->deleteKey("tmp");
- if (!result.isOk()) {
- LOG(ERROR) << "Failed to delete key: " << result;
+
+ rc = keystore->deleteKey(keyDescriptor);
+ if (!rc.isOk()) {
+ LOG(ERROR) << "Failed to delete key: " << rc.getDescription();
printf("[%s] %s\n", kBoldRedAbort, name.c_str());
return false;
}
printf("===============================================================\n");
printf("%s Key Characteristics:\n", name.c_str());
- PrintKeyCharacteristics(hardware_enforced_characteristics, software_enforced_characteristics);
- bool hardware_backed = (hardware_enforced_characteristics.size() > 0);
- if (software_enforced_characteristics.GetTagCount(TAG_ALGORITHM) > 0 ||
- software_enforced_characteristics.GetTagCount(TAG_KEY_SIZE) > 0 ||
- software_enforced_characteristics.GetTagCount(TAG_RSA_PUBLIC_EXPONENT) > 0) {
+ PrintKeyCharacteristics(keyMetadata.authorizations);
+ bool hardware_backed = std::any_of(keyMetadata.authorizations.begin(),
+ keyMetadata.authorizations.end(), isHardwareEnforced);
+ if (std::any_of(keyMetadata.authorizations.begin(), keyMetadata.authorizations.end(),
+ [&](const auto& a) {
+ return !isHardwareEnforced(a) &&
+ (a.keyParameter.tag == keymint::Tag::ALGORITHM ||
+ a.keyParameter.tag == keymint::Tag::KEY_SIZE ||
+ a.keyParameter.tag == keymint::Tag::RSA_PUBLIC_EXPONENT);
+ })) {
VLOG(1) << "Hardware-backed key but required characteristics enforced in software.";
hardware_backed = false;
}
@@ -137,60 +592,64 @@
return (hardware_backed || !required);
}
-AuthorizationSet GetRSASignParameters(uint32_t key_size, bool sha256_only) {
- AuthorizationSetBuilder parameters;
+keymint::AuthorizationSet GetRSASignParameters(uint32_t key_size, bool sha256_only) {
+ keymint::AuthorizationSetBuilder parameters;
parameters.RsaSigningKey(key_size, 65537)
- .Digest(Digest::SHA_2_256)
- .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
- .Padding(PaddingMode::RSA_PSS)
- .Authorization(TAG_NO_AUTH_REQUIRED);
+ .Digest(keymint::Digest::SHA_2_256)
+ .Padding(keymint::PaddingMode::RSA_PKCS1_1_5_SIGN)
+ .Padding(keymint::PaddingMode::RSA_PSS)
+ .Authorization(keymint::TAG_NO_AUTH_REQUIRED);
if (!sha256_only) {
- parameters.Digest(Digest::SHA_2_224).Digest(Digest::SHA_2_384).Digest(Digest::SHA_2_512);
+ parameters.Digest(keymint::Digest::SHA_2_224)
+ .Digest(keymint::Digest::SHA_2_384)
+ .Digest(keymint::Digest::SHA_2_512);
}
return std::move(parameters);
}
-AuthorizationSet GetRSAEncryptParameters(uint32_t key_size) {
- AuthorizationSetBuilder parameters;
+keymint::AuthorizationSet GetRSAEncryptParameters(uint32_t key_size) {
+ keymint::AuthorizationSetBuilder parameters;
parameters.RsaEncryptionKey(key_size, 65537)
- .Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT)
- .Padding(PaddingMode::RSA_OAEP)
- .Authorization(TAG_NO_AUTH_REQUIRED);
+ .Padding(keymint::PaddingMode::RSA_PKCS1_1_5_ENCRYPT)
+ .Padding(keymint::PaddingMode::RSA_OAEP)
+ .Authorization(keymint::TAG_NO_AUTH_REQUIRED);
return std::move(parameters);
}
-AuthorizationSet GetECDSAParameters(uint32_t key_size, bool sha256_only) {
- AuthorizationSetBuilder parameters;
+keymint::AuthorizationSet GetECDSAParameters(uint32_t key_size, bool sha256_only) {
+ keymint::AuthorizationSetBuilder parameters;
parameters.EcdsaSigningKey(key_size)
- .Digest(Digest::SHA_2_256)
- .Authorization(TAG_NO_AUTH_REQUIRED);
+ .Digest(keymint::Digest::SHA_2_256)
+ .Authorization(keymint::TAG_NO_AUTH_REQUIRED);
if (!sha256_only) {
- parameters.Digest(Digest::SHA_2_224).Digest(Digest::SHA_2_384).Digest(Digest::SHA_2_512);
+ parameters.Digest(keymint::Digest::SHA_2_224)
+ .Digest(keymint::Digest::SHA_2_384)
+ .Digest(keymint::Digest::SHA_2_512);
}
return std::move(parameters);
}
-AuthorizationSet GetAESParameters(uint32_t key_size, bool with_gcm_mode) {
- AuthorizationSetBuilder parameters;
- parameters.AesEncryptionKey(key_size).Authorization(TAG_NO_AUTH_REQUIRED);
+keymint::AuthorizationSet GetAESParameters(uint32_t key_size, bool with_gcm_mode) {
+ keymint::AuthorizationSetBuilder parameters;
+ parameters.AesEncryptionKey(key_size).Authorization(keymint::TAG_NO_AUTH_REQUIRED);
if (with_gcm_mode) {
- parameters.Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
- .Authorization(TAG_MIN_MAC_LENGTH, 128);
+ parameters.Authorization(keymint::TAG_BLOCK_MODE, keymint::BlockMode::GCM)
+ .Authorization(keymint::TAG_MIN_MAC_LENGTH, 128);
} else {
- parameters.Authorization(TAG_BLOCK_MODE, BlockMode::ECB);
- parameters.Authorization(TAG_BLOCK_MODE, BlockMode::CBC);
- parameters.Authorization(TAG_BLOCK_MODE, BlockMode::CTR);
- parameters.Padding(PaddingMode::NONE);
+ parameters.Authorization(keymint::TAG_BLOCK_MODE, keymint::BlockMode::ECB);
+ parameters.Authorization(keymint::TAG_BLOCK_MODE, keymint::BlockMode::CBC);
+ parameters.Authorization(keymint::TAG_BLOCK_MODE, keymint::BlockMode::CTR);
+ parameters.Padding(keymint::PaddingMode::NONE);
}
return std::move(parameters);
}
-AuthorizationSet GetHMACParameters(uint32_t key_size, Digest digest) {
- AuthorizationSetBuilder parameters;
+keymint::AuthorizationSet GetHMACParameters(uint32_t key_size, keymint::Digest digest) {
+ keymint::AuthorizationSetBuilder parameters;
parameters.HmacKey(key_size)
.Digest(digest)
- .Authorization(TAG_MIN_MAC_LENGTH, 224)
- .Authorization(TAG_NO_AUTH_REQUIRED);
+ .Authorization(keymint::TAG_MIN_MAC_LENGTH, 224)
+ .Authorization(keymint::TAG_NO_AUTH_REQUIRED);
return std::move(parameters);
}
@@ -212,12 +671,12 @@
{"AES-256", true, GetAESParameters(256, false)},
{"AES-128-GCM", false, GetAESParameters(128, true)},
{"AES-256-GCM", false, GetAESParameters(256, true)},
- {"HMAC-SHA256-16", true, GetHMACParameters(16, Digest::SHA_2_256)},
- {"HMAC-SHA256-32", true, GetHMACParameters(32, Digest::SHA_2_256)},
- {"HMAC-SHA256-64", false, GetHMACParameters(64, Digest::SHA_2_256)},
- {"HMAC-SHA224-32", false, GetHMACParameters(32, Digest::SHA_2_224)},
- {"HMAC-SHA384-32", false, GetHMACParameters(32, Digest::SHA_2_384)},
- {"HMAC-SHA512-32", false, GetHMACParameters(32, Digest::SHA_2_512)},
+ {"HMAC-SHA256-16", true, GetHMACParameters(16, keymint::Digest::SHA_2_256)},
+ {"HMAC-SHA256-32", true, GetHMACParameters(32, keymint::Digest::SHA_2_256)},
+ {"HMAC-SHA256-64", false, GetHMACParameters(64, keymint::Digest::SHA_2_256)},
+ {"HMAC-SHA224-32", false, GetHMACParameters(32, keymint::Digest::SHA_2_224)},
+ {"HMAC-SHA384-32", false, GetHMACParameters(32, keymint::Digest::SHA_2_384)},
+ {"HMAC-SHA512-32", false, GetHMACParameters(32, keymint::Digest::SHA_2_512)},
};
return std::vector<TestCase>(&test_cases[0], &test_cases[arraysize(test_cases)]);
}
@@ -243,7 +702,8 @@
continue;
}
++test_count;
- if (!TestKey(test_case.name, test_case.required_for_brillo_pts, test_case.parameters)) {
+ if (!TestKey(test_case.name, test_case.required_for_brillo_pts,
+ test_case.parameters.vector_data())) {
VLOG(1) << "Test failed: " << test_case.name;
++fail_count;
}
@@ -262,248 +722,274 @@
return 0;
}
-std::string ReadFile(const std::string& filename) {
+std::vector<uint8_t> ReadFile(const std::string& filename) {
std::string content;
base::FilePath path(filename);
if (!base::ReadFileToString(path, &content)) {
printf("Failed to read file: %s\n", filename.c_str());
exit(1);
}
- return content;
+ std::vector<uint8_t> buffer(reinterpret_cast<const uint8_t*>(content.data()),
+ reinterpret_cast<const uint8_t*>(content.data()) + content.size());
+ return buffer;
}
-void WriteFile(const std::string& filename, const std::string& content) {
+void WriteFile(const std::string& filename, const std::vector<uint8_t>& content) {
base::FilePath path(filename);
int size = content.size();
- if (base::WriteFile(path, content.data(), size) != size) {
+ if (base::WriteFile(path, reinterpret_cast<const char*>(content.data()), size) != size) {
printf("Failed to write file: %s\n", filename.c_str());
exit(1);
}
}
-int AddEntropy(const std::string& input, int32_t flags) {
- std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
- int32_t result = keystore->addRandomNumberGeneratorEntropy(input, flags).getErrorCode();
- printf("AddEntropy: %d\n", result);
- return result;
-}
-
// Note: auth_bound keys created with this tool will not be usable.
-int GenerateKey(const std::string& name, int32_t flags, bool auth_bound) {
- std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
- AuthorizationSetBuilder params;
+int GenerateKey(const std::string& name, keymint::SecurityLevel securityLevel, bool auth_bound) {
+ auto keystore = CreateKeystoreInstance();
+ auto sec_level = GetSecurityLevelInterface(keystore, securityLevel);
+ keymint::AuthorizationSetBuilder params;
params.RsaSigningKey(2048, 65537)
- .Digest(Digest::SHA_2_224)
- .Digest(Digest::SHA_2_256)
- .Digest(Digest::SHA_2_384)
- .Digest(Digest::SHA_2_512)
- .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
- .Padding(PaddingMode::RSA_PSS);
+ .Digest(keymint::Digest::SHA_2_224)
+ .Digest(keymint::Digest::SHA_2_256)
+ .Digest(keymint::Digest::SHA_2_384)
+ .Digest(keymint::Digest::SHA_2_512)
+ .Padding(keymint::PaddingMode::RSA_PKCS1_1_5_SIGN)
+ .Padding(keymint::PaddingMode::RSA_PSS);
if (auth_bound) {
// Gatekeeper normally generates the secure user id.
// Using zero allows the key to be created, but it will not be usuable.
- params.Authorization(TAG_USER_SECURE_ID, 0);
+ params.Authorization(keymint::TAG_USER_SECURE_ID, 0);
} else {
- params.Authorization(TAG_NO_AUTH_REQUIRED);
+ params.Authorization(keymint::TAG_NO_AUTH_REQUIRED);
}
- AuthorizationSet hardware_enforced_characteristics;
- AuthorizationSet software_enforced_characteristics;
- auto result = keystore->generateKey(name, params, flags, &hardware_enforced_characteristics,
- &software_enforced_characteristics);
- printf("GenerateKey: %d\n", result.getErrorCode());
- if (result.isOk()) {
- PrintKeyCharacteristics(hardware_enforced_characteristics,
- software_enforced_characteristics);
+
+ ks2::KeyMetadata keyMetadata;
+
+ auto rc =
+ sec_level->generateKey(keyDescriptor(name), {} /* attestationKey */, params.vector_data(),
+ 0 /* flags */, {} /* entropy */, &keyMetadata);
+
+ if (!rc.isOk()) {
+ std::cerr << "GenerateKey failed: " << rc.getDescription() << std::endl;
+ return unwrapError(rc);
}
- return result.getErrorCode();
+ std::cout << "GenerateKey: success" << std::endl;
+ PrintKeyCharacteristics(keyMetadata.authorizations);
+ return 0;
}
int GetCharacteristics(const std::string& name) {
- std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
- AuthorizationSet hardware_enforced_characteristics;
- AuthorizationSet software_enforced_characteristics;
- auto result = keystore->getKeyCharacteristics(name, &hardware_enforced_characteristics,
- &software_enforced_characteristics);
- printf("GetCharacteristics: %d\n", result.getErrorCode());
- if (result.isOk()) {
- PrintKeyCharacteristics(hardware_enforced_characteristics,
- software_enforced_characteristics);
+ auto keystore = CreateKeystoreInstance();
+
+ ks2::KeyEntryResponse keyEntryResponse;
+
+ auto rc = keystore->getKeyEntry(keyDescriptor(name), &keyEntryResponse);
+ if (!rc.isOk()) {
+ std::cerr << "Failed to get key entry: " << rc.getDescription() << std::endl;
+ return unwrapError(rc);
}
- return result.getErrorCode();
+
+ std::cout << "GetCharacteristics: success" << std::endl;
+ PrintKeyCharacteristics(keyEntryResponse.metadata.authorizations);
+ return 0;
}
int ExportKey(const std::string& name) {
- std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
- std::string data;
- int32_t result = keystore->exportKey(KeyFormat::X509, name, &data).getErrorCode();
- printf("ExportKey: %d (%zu)\n", result, data.size());
- return result;
+ auto keystore = CreateKeystoreInstance();
+
+ ks2::KeyEntryResponse keyEntryResponse;
+
+ auto rc = keystore->getKeyEntry(keyDescriptor(name), &keyEntryResponse);
+ if (!rc.isOk()) {
+ std::cerr << "Failed to get key entry: " << rc.getDescription() << std::endl;
+ return unwrapError(rc);
+ }
+
+ if (auto cert = keyEntryResponse.metadata.certificate) {
+ std::cout << "ExportKey: Got certificate of length (" << cert->size() << ")" << std::endl;
+ } else {
+ std::cout << "ExportKey: Key entry does not have a public component.\n";
+ std::cout << "Possibly a symmetric key?" << std::endl;
+ }
+ return 0;
}
int DeleteKey(const std::string& name) {
- std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
- int32_t result = keystore->deleteKey(name).getErrorCode();
- printf("DeleteKey: %d\n", result);
- return result;
-}
+ auto keystore = CreateKeystoreInstance();
-int DeleteAllKeys() {
- std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
- int32_t result = keystore->deleteAllKeys().getErrorCode();
- printf("DeleteAllKeys: %d\n", result);
- return result;
+ auto rc = keystore->deleteKey(keyDescriptor(name));
+ if (!rc.isOk()) {
+ std::cerr << "Failed to delete key: " << rc.getDescription();
+ return unwrapError(rc);
+ }
+ std::cout << "Successfully deleted key." << std::endl;
+ return 0;
}
int DoesKeyExist(const std::string& name) {
- std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
- printf("DoesKeyExist: %s\n", keystore->doesKeyExist(name) ? "yes" : "no");
+ auto keystore = CreateKeystoreInstance();
+ ks2::KeyEntryResponse keyEntryResponse;
+
+ bool keyExists = true;
+ auto rc = keystore->getKeyEntry(keyDescriptor(name), &keyEntryResponse);
+ if (!rc.isOk()) {
+ auto responseCode = unwrapError(rc);
+ if (ks2::ResponseCode(responseCode) == ks2::ResponseCode::KEY_NOT_FOUND) {
+ keyExists = false;
+ } else {
+ std::cerr << "Failed to get key entry: " << rc.getDescription() << std::endl;
+ return unwrapError(rc);
+ }
+ }
+ std::cout << "DoesKeyExists: " << (keyExists ? "yes" : "no") << std::endl;
return 0;
}
-int List(const std::string& prefix) {
- std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
- std::vector<std::string> key_list;
- if (!keystore->listKeys(prefix, &key_list)) {
- printf("ListKeys failed.\n");
- return 1;
+int List() {
+ auto keystore = CreateKeystoreInstance();
+ std::vector<ks2::KeyDescriptor> key_list;
+ auto rc = keystore->listEntries(ks2::Domain::APP, -1 /* nspace ignored */, &key_list);
+ if (!rc.isOk()) {
+ std::cerr << "ListKeys failed: " << rc.getDescription() << std::endl;
+ return unwrapError(rc);
}
- printf("Keys:\n");
- for (const auto& key_name : key_list) {
- printf(" %s\n", key_name.c_str());
- }
- return 0;
-}
-
-int ListAppsWithKeys() {
-
- sp<android::IServiceManager> sm = android::defaultServiceManager();
- sp<android::IBinder> binder = sm->getService(String16("android.security.keystore"));
- sp<IKeystoreService> service = android::interface_cast<IKeystoreService>(binder);
- if (service == nullptr) {
- fprintf(stderr, "Error connecting to keystore service.\n");
- return 1;
- }
- int32_t aidl_return;
- ::std::vector<::std::string> uids;
- android::binder::Status status = service->listUidsOfAuthBoundKeys(&uids, &aidl_return);
- if (!status.isOk()) {
- fprintf(stderr, "Requesting uids of auth bound keys failed with error %s.\n",
- status.toString8().c_str());
- return 1;
- }
- if (!KeyStoreNativeReturnCode(aidl_return).isOk()) {
- fprintf(stderr, "Requesting uids of auth bound keys failed with code %d.\n", aidl_return);
- return 1;
- }
- printf("Apps with auth bound keys:\n");
- for (auto i = uids.begin(); i != uids.end(); ++i) {
- printf("%s\n", i->c_str());
+ std::cout << "Keys:\n";
+ for (const auto& key : key_list) {
+ std::cout << " "
+ << (key.alias ? *key.alias : "Whoopsi - no alias, this should not happen.")
+ << std::endl;
}
return 0;
}
int SignAndVerify(const std::string& name) {
- std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
- AuthorizationSetBuilder sign_params;
- sign_params.Padding(PaddingMode::RSA_PKCS1_1_5_SIGN);
- sign_params.Digest(Digest::SHA_2_256);
- AuthorizationSet output_params;
- uint64_t handle;
- auto result =
- keystore->beginOperation(KeyPurpose::SIGN, name, sign_params, &output_params, &handle);
- if (!result.isOk()) {
- printf("Sign: BeginOperation failed: %d\n", result.getErrorCode());
- return result.getErrorCode();
+ auto keystore = CreateKeystoreInstance();
+ auto sign_params = keymint::AuthorizationSetBuilder()
+ .Authorization(keymint::TAG_PURPOSE, keymint::KeyPurpose::SIGN)
+ .Padding(keymint::PaddingMode::RSA_PKCS1_1_5_SIGN)
+ .Digest(keymint::Digest::SHA_2_256);
+
+ keymint::AuthorizationSet output_params;
+
+ ks2::KeyEntryResponse keyEntryResponse;
+
+ auto rc = keystore->getKeyEntry(keyDescriptor(name), &keyEntryResponse);
+ if (!rc.isOk()) {
+ std::cerr << "Failed to get key entry: " << rc.getDescription() << std::endl;
+ return unwrapError(rc);
}
- AuthorizationSet empty_params;
- std::string output_data;
- result = keystore->finishOperation(handle, empty_params, "data_to_sign",
- std::string() /*signature_to_verify*/, &output_params,
- &output_data);
- if (!result.isOk()) {
- printf("Sign: FinishOperation failed: %d\n", result.getErrorCode());
- return result.getErrorCode();
+
+ ks2::CreateOperationResponse operationResponse;
+
+ rc = keyEntryResponse.iSecurityLevel->createOperation(keyEntryResponse.metadata.key,
+ sign_params.vector_data(),
+ false /* forced */, &operationResponse);
+ if (!rc.isOk()) {
+ std::cerr << "Failed to create operation: " << rc.getDescription() << std::endl;
+ return unwrapError(rc);
}
- printf("Sign: %zu bytes.\n", output_data.size());
- // We have a signature, now verify it.
- std::string signature_to_verify = output_data;
- output_data.clear();
- result =
- keystore->beginOperation(KeyPurpose::VERIFY, name, sign_params, &output_params, &handle);
- result = keystore->finishOperation(handle, empty_params, "data_to_sign", signature_to_verify,
- &output_params, &output_data);
- if (result == ErrorCode::VERIFICATION_FAILED) {
- printf("Verify: Failed to verify signature.\n");
- return result.getErrorCode();
+
+ const std::vector<uint8_t> data_to_sign{0x64, 0x61, 0x74, 0x61, 0x5f, 0x74,
+ 0x6f, 0x5f, 0x73, 0x69, 0x67, 0x6e};
+ std::optional<std::vector<uint8_t>> output_data;
+ rc = operationResponse.iOperation->finish(data_to_sign, {}, &output_data);
+ if (!rc.isOk()) {
+ std::cerr << "Failed to finalize operation: " << rc.getDescription() << std::endl;
+ return unwrapError(rc);
}
- if (!result.isOk()) {
- printf("Verify: FinishOperation failed: %d\n", result.getErrorCode());
- return result.getErrorCode();
+
+ if (!output_data) {
+ std::cerr << "Odd signing succeeded but no signature was returned." << std::endl;
+ return static_cast<int>(ks2::ResponseCode::SYSTEM_ERROR);
}
- printf("Verify: OK\n");
+ auto signature = std::move(*output_data);
+
+ std::cout << "Sign: " << signature.size() << " bytes." << std::endl;
+
+ if (auto cert = keyEntryResponse.metadata.certificate) {
+ const uint8_t* p = cert->data();
+ bssl::UniquePtr<X509> decoded_cert(d2i_X509(nullptr, &p, (long)cert->size()));
+ bssl::UniquePtr<EVP_PKEY> decoded_pkey(X509_get_pubkey(decoded_cert.get()));
+ bssl::UniquePtr<EVP_MD_CTX> ctx(EVP_MD_CTX_new());
+ if (!ctx) {
+ std::cerr << "Failed to created EVP_MD context. << std::endl";
+ return static_cast<int>(ks2::ResponseCode::SYSTEM_ERROR);
+ }
+
+ if (!EVP_DigestVerifyInit(ctx.get(), nullptr, EVP_sha256(), nullptr, decoded_pkey.get()) ||
+ !EVP_DigestVerifyUpdate(ctx.get(), data_to_sign.data(), data_to_sign.size()) ||
+ EVP_DigestVerifyFinal(ctx.get(), signature.data(), signature.size()) != 1) {
+ std::cerr << "Failed to verify signature." << std::endl;
+ return static_cast<int>(ks2::ResponseCode::SYSTEM_ERROR);
+ }
+ } else {
+ std::cerr << "No public key to check signature against." << std::endl;
+ return static_cast<int>(ks2::ResponseCode::SYSTEM_ERROR);
+ }
+
+ std::cout << "Verify: OK" << std::endl;
return 0;
}
int Encrypt(const std::string& key_name, const std::string& input_filename,
- const std::string& output_filename, int32_t flags) {
- std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
- std::string input = ReadFile(input_filename);
- std::string output;
- if (!keystore->encryptWithAuthentication(key_name, input, flags, &output)) {
- printf("EncryptWithAuthentication failed.\n");
- return 1;
+ const std::string& output_filename, keymint::SecurityLevel securityLevel) {
+ auto input = ReadFile(input_filename);
+ auto result = encryptWithAuthentication(key_name, input, securityLevel);
+ if (auto error = std::get_if<int>(&result)) {
+ std::cerr << "EncryptWithAuthentication failed." << std::endl;
+ return *error;
}
- WriteFile(output_filename, output);
+ WriteFile(output_filename, std::get<std::vector<uint8_t>>(result));
return 0;
}
int Decrypt(const std::string& key_name, const std::string& input_filename,
const std::string& output_filename) {
- std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
- std::string input = ReadFile(input_filename);
- std::string output;
- if (!keystore->decryptWithAuthentication(key_name, input, &output)) {
- printf("DecryptWithAuthentication failed.\n");
- return 1;
+ auto input = ReadFile(input_filename);
+ auto result = decryptWithAuthentication(key_name, input);
+ if (auto error = std::get_if<int>(&result)) {
+ std::cerr << "DecryptWithAuthentication failed." << std::endl;
+ return *error;
}
- WriteFile(output_filename, output);
+ WriteFile(output_filename, std::get<std::vector<uint8_t>>(result));
return 0;
}
-uint32_t securityLevelOption2Flags(const CommandLine& cmd) {
+keymint::SecurityLevel securityLevelOption2SecurlityLevel(const CommandLine& cmd) {
if (cmd.HasSwitch("seclevel")) {
auto str = cmd.GetSwitchValueASCII("seclevel");
if (str == "strongbox") {
- return KEYSTORE_FLAG_STRONGBOX;
- } else if (str == "software") {
- return KEYSTORE_FLAG_FALLBACK;
+ return keymint::SecurityLevel::STRONGBOX;
+ } else if (str == "tee") {
+ return keymint::SecurityLevel::TRUSTED_ENVIRONMENT;
}
+ std::cerr << "Unknown Security level: " << str << std::endl;
+ std::cerr << "Supported security levels: \"strongbox\" or \"tee\" (default)" << std::endl;
}
- return KEYSTORE_FLAG_NONE;
+ return keymint::SecurityLevel::TRUSTED_ENVIRONMENT;
}
class ConfirmationListener
- : public android::security::BnConfirmationPromptCallback,
- public std::promise<std::tuple<ConfirmationResponseCode, std::vector<uint8_t>>> {
+ : public apc::BnConfirmationCallback,
+ public std::promise<std::tuple<apc::ResponseCode, std::optional<std::vector<uint8_t>>>> {
public:
ConfirmationListener() {}
- virtual ::android::binder::Status
- onConfirmationPromptCompleted(int32_t result,
- const ::std::vector<uint8_t>& dataThatWasConfirmed) override {
- this->set_value({static_cast<ConfirmationResponseCode>(result), dataThatWasConfirmed});
- return ::android::binder::Status::ok();
- }
+ virtual ::ndk::ScopedAStatus
+ onCompleted(::aidl::android::security::apc::ResponseCode result,
+ const std::optional<std::vector<uint8_t>>& dataConfirmed) override {
+ this->set_value({result, dataConfirmed});
+ return ::ndk::ScopedAStatus::ok();
+ };
};
int Confirmation(const std::string& promptText, const std::string& extraDataHex,
const std::string& locale, const std::string& uiOptionsStr,
const std::string& cancelAfter) {
- sp<android::IServiceManager> sm = android::defaultServiceManager();
- sp<android::IBinder> binder = sm->getService(String16("android.security.keystore"));
- sp<IKeystoreService> service = android::interface_cast<IKeystoreService>(binder);
- if (service == nullptr) {
- printf("error: could not connect to keystore service.\n");
+ ::ndk::SpAIBinder apcBinder(AServiceManager_getService("android.security.apc"));
+ auto apcService = apc::IProtectedConfirmation::fromBinder(apcBinder);
+ if (!apcService) {
+ std::cerr << "Error: could not connect to apc service." << std::endl;
return 1;
}
@@ -537,44 +1023,28 @@
return 1;
}
- String16 promptText16(promptText.data(), promptText.size());
- String16 locale16(locale.data(), locale.size());
-
- sp<ConfirmationListener> listener = new ConfirmationListener();
+ auto listener = std::make_shared<ConfirmationListener>();
auto future = listener->get_future();
- int32_t aidl_return;
- android::binder::Status status = service->presentConfirmationPrompt(
- listener, promptText16, extraData, locale16, uiOptionsAsFlags, &aidl_return);
- if (!status.isOk()) {
- printf("Presenting confirmation prompt failed with binder status '%s'.\n",
- status.toString8().c_str());
+ auto rc = apcService->presentPrompt(listener, promptText, extraData, locale, uiOptionsAsFlags);
+
+ if (!rc.isOk()) {
+ std::cerr << "Presenting confirmation prompt failed: " << rc.getDescription() << std::endl;
return 1;
}
- ConfirmationResponseCode responseCode = static_cast<ConfirmationResponseCode>(aidl_return);
- if (responseCode != ConfirmationResponseCode::OK) {
- printf("Presenting confirmation prompt failed with response code %d.\n", responseCode);
- return 1;
- }
- printf("Waiting for prompt to complete - use Ctrl+C to abort...\n");
+
+ std::cerr << "Waiting for prompt to complete - use Ctrl+C to abort..." << std::endl;
if (cancelAfterValue > 0.0) {
- printf("Sleeping %.1f seconds before canceling prompt...\n", cancelAfterValue);
+ std::cerr << "Sleeping " << cancelAfterValue << " seconds before canceling prompt..."
+ << std::endl;
auto fstatus =
future.wait_for(std::chrono::milliseconds(uint64_t(cancelAfterValue * 1000)));
if (fstatus == std::future_status::timeout) {
- status = service->cancelConfirmationPrompt(listener, &aidl_return);
- if (!status.isOk()) {
- printf("Canceling confirmation prompt failed with binder status '%s'.\n",
- status.toString8().c_str());
- return 1;
- }
- responseCode = static_cast<ConfirmationResponseCode>(aidl_return);
- if (responseCode == ConfirmationResponseCode::Ignored) {
- // The confirmation was completed by the user so take the response
- } else if (responseCode != ConfirmationResponseCode::OK) {
- printf("Canceling confirmation prompt failed with response code %d.\n",
- responseCode);
+ rc = apcService->cancelPrompt(listener);
+ if (!rc.isOk()) {
+ std::cerr << "Canceling confirmation prompt failed: " << rc.getDescription()
+ << std::endl;
return 1;
}
}
@@ -582,27 +1052,28 @@
future.wait();
- auto [rc, dataThatWasConfirmed] = future.get();
+ auto [responseCode, dataThatWasConfirmed] = future.get();
- printf("Confirmation prompt completed\n"
- "responseCode = %d\n",
- rc);
- printf("dataThatWasConfirmed[%zd] = {", dataThatWasConfirmed.size());
+ std::cerr << "Confirmation prompt completed\n"
+ << "responseCode = " << toString(responseCode);
size_t newLineCountDown = 16;
bool hasPrinted = false;
- for (uint8_t element : dataThatWasConfirmed) {
- if (hasPrinted) {
- printf(", ");
- }
- if (newLineCountDown == 0) {
- printf("\n ");
- newLineCountDown = 32;
- }
- printf("0x%02x", element);
- hasPrinted = true;
- }
- printf("}\n");
+ if (dataThatWasConfirmed) {
+ std::cerr << "dataThatWasConfirmed[" << dataThatWasConfirmed->size() << "] = {";
+ for (uint8_t element : *dataThatWasConfirmed) {
+ if (hasPrinted) {
+ std::cerr << ", ";
+ }
+ if (newLineCountDown == 0) {
+ std::cerr << "\n ";
+ newLineCountDown = 32;
+ }
+ std::cerr << "0x" << std::hex << std::setw(2) << std::setfill('0') << (unsigned)element;
+ hasPrinted = true;
+ }
+ }
+ std::cerr << std::endl;
return 0;
}
@@ -613,7 +1084,7 @@
CommandLine* command_line = CommandLine::ForCurrentProcess();
CommandLine::StringVector args = command_line->GetArgs();
- android::ProcessState::self()->startThreadPool();
+ ABinderProcess_startThreadPool();
if (args.empty()) {
PrintUsageAndExit();
@@ -623,12 +1094,9 @@
command_line->HasSwitch("test_for_0_3"));
} else if (args[0] == "list-brillo-tests") {
return ListTestCases();
- } else if (args[0] == "add-entropy") {
- return AddEntropy(command_line->GetSwitchValueASCII("input"),
- securityLevelOption2Flags(*command_line));
} else if (args[0] == "generate") {
return GenerateKey(command_line->GetSwitchValueASCII("name"),
- securityLevelOption2Flags(*command_line),
+ securityLevelOption2SecurlityLevel(*command_line),
command_line->HasSwitch("auth_bound"));
} else if (args[0] == "get-chars") {
return GetCharacteristics(command_line->GetSwitchValueASCII("name"));
@@ -636,20 +1104,17 @@
return ExportKey(command_line->GetSwitchValueASCII("name"));
} else if (args[0] == "delete") {
return DeleteKey(command_line->GetSwitchValueASCII("name"));
- } else if (args[0] == "delete-all") {
- return DeleteAllKeys();
} else if (args[0] == "exists") {
return DoesKeyExist(command_line->GetSwitchValueASCII("name"));
} else if (args[0] == "list") {
- return List(command_line->GetSwitchValueASCII("prefix"));
- } else if (args[0] == "list-apps-with-keys") {
- return ListAppsWithKeys();
+ return List();
} else if (args[0] == "sign-verify") {
return SignAndVerify(command_line->GetSwitchValueASCII("name"));
} else if (args[0] == "encrypt") {
- return Encrypt(
- command_line->GetSwitchValueASCII("name"), command_line->GetSwitchValueASCII("in"),
- command_line->GetSwitchValueASCII("out"), securityLevelOption2Flags(*command_line));
+ return Encrypt(command_line->GetSwitchValueASCII("name"),
+ command_line->GetSwitchValueASCII("in"),
+ command_line->GetSwitchValueASCII("out"),
+ securityLevelOption2SecurlityLevel(*command_line));
} else if (args[0] == "decrypt") {
return Decrypt(command_line->GetSwitchValueASCII("name"),
command_line->GetSwitchValueASCII("in"),
diff --git a/keystore/keystore_client_impl.cpp b/keystore/keystore_client_impl.cpp
deleted file mode 100644
index f888683..0000000
--- a/keystore/keystore_client_impl.cpp
+++ /dev/null
@@ -1,629 +0,0 @@
-// Copyright 2015 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.
-
-#define LOG_TAG "keystore_client"
-
-#include "keystore/keystore_client_impl.h"
-
-#include <future>
-#include <optional>
-#include <string>
-#include <vector>
-
-#include <android/security/keystore/IKeystoreService.h>
-#include <binder/IBinder.h>
-#include <binder/IInterface.h>
-#include <binder/IServiceManager.h>
-#include <keystore/keystore.h>
-#include <log/log.h>
-#include <utils/String16.h>
-#include <utils/String8.h>
-
-#include <keystore/keymaster_types.h>
-#include <keystore/keystore_hidl_support.h>
-#include <keystore/keystore_promises.h>
-
-#include "keystore_client.pb.h"
-
-namespace {
-
-// Use the UID of the current process.
-const int kDefaultUID = -1;
-const char kEncryptSuffix[] = "_ENC";
-const char kAuthenticateSuffix[] = "_AUTH";
-constexpr uint32_t kAESKeySize = 256; // bits
-constexpr uint32_t kHMACKeySize = 256; // bits
-constexpr uint32_t kHMACOutputSize = 256; // bits
-
-using android::String16;
-using android::security::keymaster::ExportResult;
-using android::security::keymaster::OperationResult;
-using android::security::keystore::KeystoreResponse;
-using keystore::AuthorizationSet;
-using keystore::AuthorizationSetBuilder;
-using keystore::KeyCharacteristics;
-using keystore::KeyStoreServiceReturnCode;
-} // namespace
-
-namespace keystore {
-
-KeystoreClientImpl::KeystoreClientImpl() {
- service_manager_ = android::defaultServiceManager();
- keystore_binder_ = service_manager_->getService(String16("android.security.keystore"));
- keystore_ =
- android::interface_cast<android::security::keystore::IKeystoreService>(keystore_binder_);
-}
-
-bool KeystoreClientImpl::encryptWithAuthentication(const std::string& key_name,
- const std::string& data, int32_t flags,
- std::string* encrypted_data) {
- // The encryption algorithm is AES-256-CBC with PKCS #7 padding and a random
- // IV. The authentication algorithm is HMAC-SHA256 and is computed over the
- // cipher-text (i.e. Encrypt-then-MAC approach). This was chosen over AES-GCM
- // because hardware support for GCM is not mandatory for all Brillo devices.
- std::string encryption_key_name = key_name + kEncryptSuffix;
- if (!createOrVerifyEncryptionKey(encryption_key_name, flags)) {
- return false;
- }
- std::string authentication_key_name = key_name + kAuthenticateSuffix;
- if (!createOrVerifyAuthenticationKey(authentication_key_name, flags)) {
- return false;
- }
- AuthorizationSetBuilder encrypt_params;
- encrypt_params.Padding(PaddingMode::PKCS7);
- encrypt_params.Authorization(TAG_BLOCK_MODE, BlockMode::CBC);
- AuthorizationSet output_params;
- std::string raw_encrypted_data;
- if (!oneShotOperation(KeyPurpose::ENCRYPT, encryption_key_name, encrypt_params, data,
- std::string(), /* signature_to_verify */
- &output_params, &raw_encrypted_data)) {
- ALOGE("Encrypt: AES operation failed.");
- return false;
- }
- auto init_vector_blob = output_params.GetTagValue(TAG_NONCE);
- if (!init_vector_blob.isOk()) {
- ALOGE("Encrypt: Missing initialization vector.");
- return false;
- }
- std::string init_vector = hidlVec2String(init_vector_blob.value());
-
- AuthorizationSetBuilder authenticate_params;
- authenticate_params.Digest(Digest::SHA_2_256);
- authenticate_params.Authorization(TAG_MAC_LENGTH, kHMACOutputSize);
- std::string raw_authentication_data;
- if (!oneShotOperation(KeyPurpose::SIGN, authentication_key_name, authenticate_params,
- init_vector + raw_encrypted_data, std::string(), /* signature_to_verify */
- &output_params, &raw_authentication_data)) {
- ALOGE("Encrypt: HMAC operation failed.");
- return false;
- }
- EncryptedData protobuf;
- protobuf.set_init_vector(init_vector);
- protobuf.set_authentication_data(raw_authentication_data);
- protobuf.set_encrypted_data(raw_encrypted_data);
- if (!protobuf.SerializeToString(encrypted_data)) {
- ALOGE("Encrypt: Failed to serialize EncryptedData protobuf.");
- return false;
- }
- return true;
-}
-
-bool KeystoreClientImpl::decryptWithAuthentication(const std::string& key_name,
- const std::string& encrypted_data,
- std::string* data) {
- EncryptedData protobuf;
- if (!protobuf.ParseFromString(encrypted_data)) {
- ALOGE("Decrypt: Failed to parse EncryptedData protobuf.");
- }
- // Verify authentication before attempting decryption.
- std::string authentication_key_name = key_name + kAuthenticateSuffix;
- AuthorizationSetBuilder authenticate_params;
- authenticate_params.Digest(Digest::SHA_2_256);
- AuthorizationSet output_params;
- std::string output_data;
- if (!oneShotOperation(KeyPurpose::VERIFY, authentication_key_name, authenticate_params,
- protobuf.init_vector() + protobuf.encrypted_data(),
- protobuf.authentication_data(), &output_params, &output_data)) {
- ALOGE("Decrypt: HMAC operation failed.");
- return false;
- }
- std::string encryption_key_name = key_name + kEncryptSuffix;
- AuthorizationSetBuilder encrypt_params;
- encrypt_params.Padding(PaddingMode::PKCS7);
- encrypt_params.Authorization(TAG_BLOCK_MODE, BlockMode::CBC);
- encrypt_params.Authorization(TAG_NONCE, protobuf.init_vector().data(),
- protobuf.init_vector().size());
- if (!oneShotOperation(KeyPurpose::DECRYPT, encryption_key_name, encrypt_params,
- protobuf.encrypted_data(), std::string(), /* signature_to_verify */
- &output_params, data)) {
- ALOGE("Decrypt: AES operation failed.");
- return false;
- }
- return true;
-}
-
-bool KeystoreClientImpl::oneShotOperation(KeyPurpose purpose, const std::string& key_name,
- const AuthorizationSet& input_parameters,
- const std::string& input_data,
- const std::string& signature_to_verify,
- AuthorizationSet* output_parameters,
- std::string* output_data) {
- uint64_t handle;
- auto result = beginOperation(purpose, key_name, input_parameters, output_parameters, &handle);
- if (!result.isOk()) {
- ALOGE("BeginOperation failed: %d", result.getErrorCode());
- return false;
- }
- AuthorizationSet empty_params;
- AuthorizationSet ignored_params;
- result = finishOperation(handle, empty_params, input_data, signature_to_verify, &ignored_params,
- output_data);
- if (!result.isOk()) {
- ALOGE("FinishOperation failed: %d", result.getErrorCode());
- return false;
- }
- return true;
-}
-
-KeyStoreNativeReturnCode
-KeystoreClientImpl::addRandomNumberGeneratorEntropy(const std::string& entropy, int32_t flags) {
- int32_t error_code;
-
- android::sp<KeystoreResponsePromise> promise(new KeystoreResponsePromise());
- auto future = promise->get_future();
-
- auto binder_result =
- keystore_->addRngEntropy(promise, blob2hidlVec(entropy), flags, &error_code);
- if (!binder_result.isOk()) return ResponseCode::SYSTEM_ERROR;
-
- KeyStoreNativeReturnCode rc(error_code);
- if (!rc.isOk()) return rc;
-
- auto result = future.get();
-
- return KeyStoreNativeReturnCode(result.response_code());
-}
-
-KeyStoreNativeReturnCode
-KeystoreClientImpl::generateKey(const std::string& key_name, const AuthorizationSet& key_parameters,
- int32_t flags, AuthorizationSet* hardware_enforced_characteristics,
- AuthorizationSet* software_enforced_characteristics) {
- String16 key_name16(key_name.data(), key_name.size());
- int32_t error_code;
- android::sp<KeyCharacteristicsPromise> promise(new KeyCharacteristicsPromise);
- auto future = promise->get_future();
- auto binder_result = keystore_->generateKey(
- promise, key_name16,
- ::android::security::keymaster::KeymasterArguments(key_parameters.hidl_data()),
- hidl_vec<uint8_t>() /* entropy */, kDefaultUID, flags, &error_code);
- if (!binder_result.isOk()) return ResponseCode::SYSTEM_ERROR;
-
- KeyStoreNativeReturnCode rc(error_code);
- if (!rc.isOk()) return rc;
-
- auto [km_response, characteristics] = future.get();
-
- /* assignment (hidl_vec<KeyParameter> -> AuthorizationSet) makes a deep copy.
- * There are no references to Parcel memory after that, and ownership of the newly acquired
- * memory is with the AuthorizationSet objects. */
- *hardware_enforced_characteristics = characteristics.hardwareEnforced.getParameters();
- *software_enforced_characteristics = characteristics.softwareEnforced.getParameters();
- return KeyStoreNativeReturnCode(km_response.response_code());
-}
-
-KeyStoreNativeReturnCode
-KeystoreClientImpl::getKeyCharacteristics(const std::string& key_name,
- AuthorizationSet* hardware_enforced_characteristics,
- AuthorizationSet* software_enforced_characteristics) {
- String16 key_name16(key_name.data(), key_name.size());
- int32_t error_code;
- android::sp<KeyCharacteristicsPromise> promise(new KeyCharacteristicsPromise);
- auto future = promise->get_future();
- auto binder_result = keystore_->getKeyCharacteristics(
- promise, key_name16, android::security::keymaster::KeymasterBlob(),
- android::security::keymaster::KeymasterBlob(), kDefaultUID, &error_code);
- if (!binder_result.isOk()) return ResponseCode::SYSTEM_ERROR;
-
- KeyStoreNativeReturnCode rc(error_code);
- if (!rc.isOk()) return rc;
-
- auto [km_response, characteristics] = future.get();
-
- /* assignment (hidl_vec<KeyParameter> -> AuthorizationSet) makes a deep copy.
- * There are no references to Parcel memory after that, and ownership of the newly acquired
- * memory is with the AuthorizationSet objects. */
- *hardware_enforced_characteristics = characteristics.hardwareEnforced.getParameters();
- *software_enforced_characteristics = characteristics.softwareEnforced.getParameters();
- return KeyStoreNativeReturnCode(km_response.response_code());
-}
-
-KeyStoreNativeReturnCode
-KeystoreClientImpl::importKey(const std::string& key_name, const AuthorizationSet& key_parameters,
- KeyFormat key_format, const std::string& key_data,
- AuthorizationSet* hardware_enforced_characteristics,
- AuthorizationSet* software_enforced_characteristics) {
- String16 key_name16(key_name.data(), key_name.size());
- auto hidlKeyData = blob2hidlVec(key_data);
- int32_t error_code;
- android::sp<KeyCharacteristicsPromise> promise(new KeyCharacteristicsPromise);
- auto future = promise->get_future();
- auto binder_result = keystore_->importKey(
- promise, key_name16,
- ::android::security::keymaster::KeymasterArguments(key_parameters.hidl_data()),
- (int)key_format, hidlKeyData, kDefaultUID, KEYSTORE_FLAG_NONE, &error_code);
- if (!binder_result.isOk()) return ResponseCode::SYSTEM_ERROR;
-
- KeyStoreNativeReturnCode rc(error_code);
- if (!rc.isOk()) return rc;
-
- auto [km_response, characteristics] = future.get();
-
- /* assignment (hidl_vec<KeyParameter> -> AuthorizationSet) makes a deep copy.
- * There are no references to Parcel memory after that, and ownership of the newly acquired
- * memory is with the AuthorizationSet objects. */
- *hardware_enforced_characteristics = characteristics.hardwareEnforced.getParameters();
- *software_enforced_characteristics = characteristics.softwareEnforced.getParameters();
- return KeyStoreNativeReturnCode(km_response.response_code());
-}
-
-KeyStoreNativeReturnCode KeystoreClientImpl::exportKey(KeyFormat export_format,
- const std::string& key_name,
- std::string* export_data) {
- String16 key_name16(key_name.data(), key_name.size());
- int32_t error_code;
- android::sp<KeystoreExportPromise> promise(new KeystoreExportPromise);
- auto future = promise->get_future();
- auto binder_result = keystore_->exportKey(
- promise, key_name16, (int)export_format, android::security::keymaster::KeymasterBlob(),
- android::security::keymaster::KeymasterBlob(), kDefaultUID, &error_code);
- if (!binder_result.isOk()) return ResponseCode::SYSTEM_ERROR;
-
- KeyStoreNativeReturnCode rc(error_code);
- if (!rc.isOk()) return rc;
-
- auto export_result = future.get();
- if (!export_result.resultCode.isOk()) return export_result.resultCode;
-
- *export_data = hidlVec2String(export_result.exportData);
-
- return export_result.resultCode;
-}
-
-KeyStoreNativeReturnCode KeystoreClientImpl::deleteKey(const std::string& key_name) {
- String16 key_name16(key_name.data(), key_name.size());
- int32_t result;
- auto binder_result = keystore_->del(key_name16, kDefaultUID, &result);
- if (!binder_result.isOk()) return ResponseCode::SYSTEM_ERROR;
- return KeyStoreNativeReturnCode(result);
-}
-
-KeyStoreNativeReturnCode KeystoreClientImpl::deleteAllKeys() {
- int32_t result;
- auto binder_result = keystore_->clear_uid(kDefaultUID, &result);
- if (!binder_result.isOk()) return ResponseCode::SYSTEM_ERROR;
- return KeyStoreNativeReturnCode(result);
-}
-
-KeyStoreNativeReturnCode
-KeystoreClientImpl::beginOperation(KeyPurpose purpose, const std::string& key_name,
- const AuthorizationSet& input_parameters,
- AuthorizationSet* output_parameters, uint64_t* handle) {
- android::sp<android::IBinder> token(new android::BBinder);
- String16 key_name16(key_name.data(), key_name.size());
- int32_t error_code;
- android::sp<OperationResultPromise> promise(new OperationResultPromise{});
- auto future = promise->get_future();
- auto binder_result = keystore_->begin(
- promise, token, key_name16, (int)purpose, true /*pruneable*/,
- android::security::keymaster::KeymasterArguments(input_parameters.hidl_data()),
- hidl_vec<uint8_t>() /* entropy */, kDefaultUID, &error_code);
- if (!binder_result.isOk()) return ResponseCode::SYSTEM_ERROR;
- KeyStoreNativeReturnCode rc(error_code);
- if (!rc.isOk()) return rc;
-
- OperationResult result = future.get();
- if (result.resultCode.isOk()) {
- *handle = getNextVirtualHandle();
- active_operations_[*handle] = result.token;
- if (result.outParams.size()) {
- *output_parameters = result.outParams;
- }
- }
- return result.resultCode;
-}
-
-KeyStoreNativeReturnCode
-KeystoreClientImpl::updateOperation(uint64_t handle, const AuthorizationSet& input_parameters,
- const std::string& input_data, size_t* num_input_bytes_consumed,
- AuthorizationSet* output_parameters, std::string* output_data) {
- if (active_operations_.count(handle) == 0) {
- return ErrorCode::INVALID_OPERATION_HANDLE;
- }
- auto hidlInputData = blob2hidlVec(input_data);
- int32_t error_code;
- android::sp<OperationResultPromise> promise(new OperationResultPromise{});
- auto future = promise->get_future();
- auto binder_result = keystore_->update(
- promise, active_operations_[handle],
- android::security::keymaster::KeymasterArguments(input_parameters.hidl_data()),
- hidlInputData, &error_code);
- if (!binder_result.isOk()) return ResponseCode::SYSTEM_ERROR;
- KeyStoreNativeReturnCode rc(error_code);
- if (!rc.isOk()) return rc;
-
- OperationResult result = future.get();
-
- if (result.resultCode.isOk()) {
- *num_input_bytes_consumed = result.inputConsumed;
- if (result.outParams.size()) {
- *output_parameters = result.outParams;
- }
- // TODO verify that append should not be assign
- output_data->append(hidlVec2String(result.data));
- }
- return result.resultCode;
-}
-
-KeyStoreNativeReturnCode
-KeystoreClientImpl::finishOperation(uint64_t handle, const AuthorizationSet& input_parameters,
- const std::string& input_data,
- const std::string& signature_to_verify,
- AuthorizationSet* output_parameters, std::string* output_data) {
- if (active_operations_.count(handle) == 0) {
- return ErrorCode::INVALID_OPERATION_HANDLE;
- }
- int32_t error_code;
- auto hidlSignature = blob2hidlVec(signature_to_verify);
- auto hidlInput = blob2hidlVec(input_data);
- android::sp<OperationResultPromise> promise(new OperationResultPromise{});
- auto future = promise->get_future();
- auto binder_result = keystore_->finish(
- promise, active_operations_[handle],
- android::security::keymaster::KeymasterArguments(input_parameters.hidl_data()),
- (std::vector<uint8_t>)hidlInput, (std::vector<uint8_t>)hidlSignature, hidl_vec<uint8_t>(),
- &error_code);
- if (!binder_result.isOk()) return ResponseCode::SYSTEM_ERROR;
- KeyStoreNativeReturnCode rc(error_code);
- if (!rc.isOk()) return rc;
-
- OperationResult result = future.get();
- if (result.resultCode.isOk()) {
- if (result.outParams.size()) {
- *output_parameters = result.outParams;
- }
- // TODO verify that append should not be assign
- output_data->append(hidlVec2String(result.data));
- active_operations_.erase(handle);
- }
- return result.resultCode;
-}
-
-KeyStoreNativeReturnCode KeystoreClientImpl::abortOperation(uint64_t handle) {
- if (active_operations_.count(handle) == 0) {
- return ErrorCode::INVALID_OPERATION_HANDLE;
- }
- int32_t result;
- android::sp<KeystoreResponsePromise> promise(new KeystoreResponsePromise{});
- auto future = promise->get_future();
- // Current implementation does not return exceptions in android::binder::Status
- auto binder_result = keystore_->abort(promise, active_operations_[handle], &result);
- if (!binder_result.isOk()) return ResponseCode::SYSTEM_ERROR;
- KeyStoreNativeReturnCode rc(result);
- if (!rc.isOk()) return rc;
- rc = KeyStoreNativeReturnCode(future.get().response_code());
- if (rc.isOk()) {
- active_operations_.erase(handle);
- }
- return rc;
-}
-
-bool KeystoreClientImpl::doesKeyExist(const std::string& key_name) {
- String16 key_name16(key_name.data(), key_name.size());
- int32_t result;
- auto binder_result = keystore_->exist(key_name16, kDefaultUID, &result);
- if (!binder_result.isOk()) return false; // binder error
- return result == static_cast<int32_t>(ResponseCode::NO_ERROR);
-}
-
-bool KeystoreClientImpl::listKeys(const std::string& prefix,
- std::vector<std::string>* key_name_list) {
- return listKeysOfUid(prefix, kDefaultUID, key_name_list);
-}
-
-bool KeystoreClientImpl::listKeysOfUid(const std::string& prefix, int uid,
- std::vector<std::string>* key_name_list) {
- String16 prefix16(prefix.data(), prefix.size());
- std::vector<::android::String16> matches;
- auto binder_result = keystore_->list(prefix16, uid, &matches);
- if (!binder_result.isOk()) return false;
-
- for (const auto& match : matches) {
- android::String8 key_name(match);
- key_name_list->push_back(prefix + std::string(key_name.string(), key_name.size()));
- }
- return true;
-}
-
-std::optional<std::vector<uint8_t>> KeystoreClientImpl::getKey(const std::string& alias, int uid) {
- String16 alias16(alias.data(), alias.size());
- std::vector<uint8_t> output;
- auto binder_result = keystore_->get(alias16, uid, &output);
- if (!binder_result.isOk()) return std::nullopt;
- return output;
-}
-
-uint64_t KeystoreClientImpl::getNextVirtualHandle() {
- return next_virtual_handle_++;
-}
-
-bool KeystoreClientImpl::createOrVerifyEncryptionKey(const std::string& key_name, int32_t flags) {
- bool key_exists = doesKeyExist(key_name);
- if (key_exists) {
- bool verified = false;
- if (!verifyEncryptionKeyAttributes(key_name, &verified)) {
- return false;
- }
- if (!verified) {
- auto result = deleteKey(key_name);
- if (!result.isOk()) {
- ALOGE("Failed to delete invalid encryption key: %d", result.getErrorCode());
- return false;
- }
- key_exists = false;
- }
- }
- if (!key_exists) {
- AuthorizationSetBuilder key_parameters;
- key_parameters.AesEncryptionKey(kAESKeySize)
- .Padding(PaddingMode::PKCS7)
- .Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
- .Authorization(TAG_NO_AUTH_REQUIRED);
- AuthorizationSet hardware_enforced_characteristics;
- AuthorizationSet software_enforced_characteristics;
- auto result =
- generateKey(key_name, key_parameters, flags, &hardware_enforced_characteristics,
- &software_enforced_characteristics);
- if (!result.isOk()) {
- ALOGE("Failed to generate encryption key: %d", result.getErrorCode());
- return false;
- }
- if (hardware_enforced_characteristics.size() == 0) {
- ALOGW("WARNING: Encryption key is not hardware-backed.");
- }
- }
- return true;
-}
-
-bool KeystoreClientImpl::createOrVerifyAuthenticationKey(const std::string& key_name,
- int32_t flags) {
- bool key_exists = doesKeyExist(key_name);
- if (key_exists) {
- bool verified = false;
- if (!verifyAuthenticationKeyAttributes(key_name, &verified)) {
- return false;
- }
- if (!verified) {
- auto result = deleteKey(key_name);
- if (!result.isOk()) {
- ALOGE("Failed to delete invalid authentication key: %d", result.getErrorCode());
- return false;
- }
- key_exists = false;
- }
- }
- if (!key_exists) {
- AuthorizationSetBuilder key_parameters;
- key_parameters.HmacKey(kHMACKeySize)
- .Digest(Digest::SHA_2_256)
- .Authorization(TAG_MIN_MAC_LENGTH, kHMACOutputSize)
- .Authorization(TAG_NO_AUTH_REQUIRED);
- AuthorizationSet hardware_enforced_characteristics;
- AuthorizationSet software_enforced_characteristics;
- auto result =
- generateKey(key_name, key_parameters, flags, &hardware_enforced_characteristics,
- &software_enforced_characteristics);
- if (!result.isOk()) {
- ALOGE("Failed to generate authentication key: %d", result.getErrorCode());
- return false;
- }
- if (hardware_enforced_characteristics.size() == 0) {
- ALOGW("WARNING: Authentication key is not hardware-backed.");
- }
- }
- return true;
-}
-
-bool KeystoreClientImpl::verifyEncryptionKeyAttributes(const std::string& key_name,
- bool* verified) {
- AuthorizationSet hardware_enforced_characteristics;
- AuthorizationSet software_enforced_characteristics;
- auto result = getKeyCharacteristics(key_name, &hardware_enforced_characteristics,
- &software_enforced_characteristics);
- if (!result.isOk()) {
- ALOGE("Failed to query encryption key: %d", result.getErrorCode());
- return false;
- }
- *verified = true;
- auto algorithm = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_ALGORITHM),
- software_enforced_characteristics.GetTagValue(TAG_ALGORITHM));
- if (!algorithm.isOk() || algorithm.value() != Algorithm::AES) {
- ALOGW("Found encryption key with invalid algorithm.");
- *verified = false;
- }
- auto key_size = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_KEY_SIZE),
- software_enforced_characteristics.GetTagValue(TAG_KEY_SIZE));
- if (!key_size.isOk() || key_size.value() != kAESKeySize) {
- ALOGW("Found encryption key with invalid size.");
- *verified = false;
- }
- auto block_mode = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_BLOCK_MODE),
- software_enforced_characteristics.GetTagValue(TAG_BLOCK_MODE));
- if (!block_mode.isOk() || block_mode.value() != BlockMode::CBC) {
- ALOGW("Found encryption key with invalid block mode.");
- *verified = false;
- }
- auto padding_mode = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_PADDING),
- software_enforced_characteristics.GetTagValue(TAG_PADDING));
- if (!padding_mode.isOk() || padding_mode.value() != PaddingMode::PKCS7) {
- ALOGW("Found encryption key with invalid padding mode.");
- *verified = false;
- }
- if (hardware_enforced_characteristics.size() == 0) {
- ALOGW("WARNING: Encryption key is not hardware-backed.");
- }
- return true;
-}
-
-bool KeystoreClientImpl::verifyAuthenticationKeyAttributes(const std::string& key_name,
- bool* verified) {
- AuthorizationSet hardware_enforced_characteristics;
- AuthorizationSet software_enforced_characteristics;
- auto result = getKeyCharacteristics(key_name, &hardware_enforced_characteristics,
- &software_enforced_characteristics);
- if (!result.isOk()) {
- ALOGE("Failed to query authentication key: %d", result.getErrorCode());
- return false;
- }
- *verified = true;
- auto algorithm = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_ALGORITHM),
- software_enforced_characteristics.GetTagValue(TAG_ALGORITHM));
- if (!algorithm.isOk() || algorithm.value() != Algorithm::HMAC) {
- ALOGW("Found authentication key with invalid algorithm.");
- *verified = false;
- }
- auto key_size = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_KEY_SIZE),
- software_enforced_characteristics.GetTagValue(TAG_KEY_SIZE));
- if (!key_size.isOk() || key_size.value() != kHMACKeySize) {
- ALOGW("Found authentication key with invalid size.");
- *verified = false;
- }
- auto mac_size = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_MIN_MAC_LENGTH),
- software_enforced_characteristics.GetTagValue(TAG_MIN_MAC_LENGTH));
- if (!mac_size.isOk() || mac_size.value() != kHMACOutputSize) {
- ALOGW("Found authentication key with invalid minimum mac size.");
- *verified = false;
- }
- auto digest = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_DIGEST),
- software_enforced_characteristics.GetTagValue(TAG_DIGEST));
- if (!digest.isOk() || digest.value() != Digest::SHA_2_256) {
- ALOGW("Found authentication key with invalid digest list.");
- *verified = false;
- }
- if (hardware_enforced_characteristics.size() == 0) {
- ALOGW("WARNING: Authentication key is not hardware-backed.");
- }
- return true;
-}
-
-} // namespace keystore
diff --git a/keystore/keystore_main.cpp b/keystore/keystore_main.cpp
deleted file mode 100644
index 02c2139..0000000
--- a/keystore/keystore_main.cpp
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (C) 2009 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.
- */
-
-#define LOG_TAG "keystore"
-
-#include <android-base/logging.h>
-#include <android/hidl/manager/1.2/IServiceManager.h>
-#include <android/security/keystore/IKeystoreService.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <keymasterV4_1/Keymaster3.h>
-#include <keymasterV4_1/Keymaster4.h>
-#include <utils/StrongPointer.h>
-
-#include <keystore/keystore_hidl_support.h>
-#include <keystore/keystore_return_types.h>
-
-#include "KeyStore.h"
-#include "key_store_service.h"
-#include "legacy_keymaster_device_wrapper.h"
-#include "permissions.h"
-
-/* KeyStore is a secured storage for key-value pairs. In this implementation,
- * each file stores one key-value pair. Keys are encoded in file names, and
- * values are encrypted with checksums. The encryption key is protected by a
- * user-defined password. To keep things simple, buffers are always larger than
- * the maximum space we needed, so boundary checks on buffers are omitted. */
-
-using ::android::sp;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::keymaster::V4_0::ErrorCode;
-using ::android::hardware::keymaster::V4_0::HmacSharingParameters;
-using ::android::hardware::keymaster::V4_0::SecurityLevel;
-using ::android::hidl::manager::V1_2::IServiceManager;
-
-using ::keystore::keymaster::support::Keymaster;
-using ::keystore::keymaster::support::Keymaster3;
-using ::keystore::keymaster::support::Keymaster4;
-
-using keystore::KeymasterDevices;
-
-template <typename Wrapper>
-KeymasterDevices enumerateKeymasterDevices(IServiceManager* serviceManager) {
- KeymasterDevices result;
- serviceManager->listManifestByInterface(
- Wrapper::WrappedIKeymasterDevice::descriptor, [&](const hidl_vec<hidl_string>& names) {
- auto try_get_device = [&](const auto& name, bool fail_silent) {
- auto device = Wrapper::WrappedIKeymasterDevice::getService(name);
- if (fail_silent && !device) return;
- CHECK(device) << "Failed to get service for \""
- << Wrapper::WrappedIKeymasterDevice::descriptor
- << "\" with interface name \"" << name << "\"";
-
- sp<Keymaster> kmDevice(new Wrapper(device, name));
- auto halVersion = kmDevice->halVersion();
- SecurityLevel securityLevel = halVersion.securityLevel;
- LOG(INFO) << "found " << Wrapper::WrappedIKeymasterDevice::descriptor
- << " with interface name " << name << " and seclevel "
- << toString(securityLevel);
- CHECK(static_cast<uint32_t>(securityLevel) < result.size())
- << "Security level of \"" << Wrapper::WrappedIKeymasterDevice::descriptor
- << "\" with interface name \"" << name << "\" out of range";
- auto& deviceSlot = result[securityLevel];
- if (deviceSlot) {
- if (!fail_silent) {
- LOG(WARNING) << "Implementation of \""
- << Wrapper::WrappedIKeymasterDevice::descriptor
- << "\" with interface name \"" << name
- << "\" and security level: " << toString(securityLevel)
- << " Masked by other implementation of Keymaster";
- }
- } else {
- deviceSlot = kmDevice;
- }
- };
- bool has_default = false;
- for (auto& n : names) {
- try_get_device(n, false);
- if (n == "default") has_default = true;
- }
- // Make sure that we always check the default device. If we enumerate only what is
- // known to hwservicemanager, we miss a possible passthrough HAL.
- if (!has_default) {
- try_get_device("default", true /* fail_silent */);
- }
- });
- return result;
-}
-
-KeymasterDevices initializeKeymasters() {
- auto serviceManager = IServiceManager::getService();
- CHECK(serviceManager.get()) << "Failed to get ServiceManager";
- auto result = enumerateKeymasterDevices<Keymaster4>(serviceManager.get());
- auto softKeymaster = result[SecurityLevel::SOFTWARE];
- if (!result[SecurityLevel::TRUSTED_ENVIRONMENT]) {
- result = enumerateKeymasterDevices<Keymaster3>(serviceManager.get());
- }
- if (softKeymaster) result[SecurityLevel::SOFTWARE] = softKeymaster;
- if (result[SecurityLevel::SOFTWARE] && !result[SecurityLevel::TRUSTED_ENVIRONMENT]) {
- LOG(WARNING) << "No secure Keymaster implementation found, but device offers insecure"
- " Keymaster HAL. Using as default.";
- result[SecurityLevel::TRUSTED_ENVIRONMENT] = result[SecurityLevel::SOFTWARE];
- result[SecurityLevel::SOFTWARE] = nullptr;
- }
- if (!result[SecurityLevel::SOFTWARE]) {
- auto fbdev = android::keystore::makeSoftwareKeymasterDevice();
- CHECK(fbdev.get()) << "Unable to create Software Keymaster Device";
- result[SecurityLevel::SOFTWARE] = new Keymaster3(fbdev, "Software");
- }
- return result;
-}
-
-int main(int argc, char* argv[]) {
- using android::hardware::hidl_string;
- CHECK(argc >= 2) << "A directory must be specified!";
- CHECK(chdir(argv[1]) != -1) << "chdir: " << argv[1] << ": " << strerror(errno);
-
- auto kmDevices = initializeKeymasters();
-
- CHECK(kmDevices[SecurityLevel::SOFTWARE]) << "Missing software Keymaster device";
- CHECK(kmDevices[SecurityLevel::TRUSTED_ENVIRONMENT])
- << "Error no viable keymaster device found";
-
- CHECK(configure_selinux() != -1) << "Failed to configure SELinux.";
-
- auto halVersion = kmDevices[SecurityLevel::TRUSTED_ENVIRONMENT]->halVersion();
-
- // If the hardware is keymaster 2.0 or higher we will not allow the fallback device for import
- // or generation of keys. The fallback device is only used for legacy keys present on the
- // device.
- SecurityLevel minimalAllowedSecurityLevelForNewKeys =
- halVersion.majorVersion >= 2 ? SecurityLevel::TRUSTED_ENVIRONMENT : SecurityLevel::SOFTWARE;
-
- android::sp<keystore::KeyStore> keyStore(
- new keystore::KeyStore(kmDevices, minimalAllowedSecurityLevelForNewKeys));
- keyStore->initialize();
- android::sp<android::IServiceManager> sm = android::defaultServiceManager();
- android::sp<keystore::KeyStoreService> service = new keystore::KeyStoreService(keyStore);
- service->setRequestingSid(true);
- android::status_t ret = sm->addService(android::String16("android.security.keystore"), service);
- CHECK(ret == android::OK) << "Couldn't register binder service!";
-
- /*
- * This thread is just going to process Binder transactions.
- */
- android::IPCThreadState::self()->joinThreadPool();
- return 1;
-}
diff --git a/keystore/legacy_keymaster_device_wrapper.cpp b/keystore/legacy_keymaster_device_wrapper.cpp
deleted file mode 100644
index 052f394..0000000
--- a/keystore/legacy_keymaster_device_wrapper.cpp
+++ /dev/null
@@ -1,547 +0,0 @@
-/*
- **
- ** Copyright 2016, 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.
- */
-
-#define LOG_TAG "android.hardware.keymaster@3.0-impl"
-
-#include "legacy_keymaster_device_wrapper.h"
-
-#include <log/log.h>
-
-#include <hardware/keymaster2.h>
-#include <hardware/keymaster_defs.h>
-#include <keymaster/keymaster_configuration.h>
-#include <keymaster/soft_keymaster_device.h>
-
-namespace android {
-namespace keystore {
-
-using ::keymaster::SoftKeymasterDevice;
-
-LegacyKeymasterDeviceWrapper::LegacyKeymasterDeviceWrapper(keymaster2_device_t* dev)
- : keymaster_device_(dev) {}
-
-LegacyKeymasterDeviceWrapper::~LegacyKeymasterDeviceWrapper() {
- if (keymaster_device_) keymaster_device_->common.close(&keymaster_device_->common);
-}
-
-static inline keymaster_tag_type_t typeFromTag(const keymaster_tag_t tag) {
- return keymaster_tag_get_type(tag);
-}
-
-/**
- * legacy_enum_conversion converts enums from hidl to keymaster and back. Currently, this is just a
- * cast to make the compiler happy. One of two thigs should happen though:
- * TODO The keymaster enums should become aliases for the hidl generated enums so that we have a
- * single point of truth. Then this cast function can go away.
- */
-inline static keymaster_tag_t legacy_enum_conversion(const Tag value) {
- return keymaster_tag_t(value);
-}
-inline static Tag legacy_enum_conversion(const keymaster_tag_t value) {
- return Tag(value);
-}
-inline static keymaster_purpose_t legacy_enum_conversion(const KeyPurpose value) {
- return keymaster_purpose_t(value);
-}
-inline static keymaster_key_format_t legacy_enum_conversion(const KeyFormat value) {
- return keymaster_key_format_t(value);
-}
-inline static ErrorCode legacy_enum_conversion(const keymaster_error_t value) {
- return ErrorCode(value);
-}
-
-class KmParamSet : public keymaster_key_param_set_t {
- public:
- explicit KmParamSet(const hidl_vec<KeyParameter>& keyParams) {
- params = new keymaster_key_param_t[keyParams.size()];
- length = keyParams.size();
- for (size_t i = 0; i < keyParams.size(); ++i) {
- auto tag = legacy_enum_conversion(keyParams[i].tag);
- switch (typeFromTag(tag)) {
- case KM_ENUM:
- case KM_ENUM_REP:
- params[i] = keymaster_param_enum(tag, keyParams[i].f.integer);
- break;
- case KM_UINT:
- case KM_UINT_REP:
- params[i] = keymaster_param_int(tag, keyParams[i].f.integer);
- break;
- case KM_ULONG:
- case KM_ULONG_REP:
- params[i] = keymaster_param_long(tag, keyParams[i].f.longInteger);
- break;
- case KM_DATE:
- params[i] = keymaster_param_date(tag, keyParams[i].f.dateTime);
- break;
- case KM_BOOL:
- if (keyParams[i].f.boolValue)
- params[i] = keymaster_param_bool(tag);
- else
- params[i].tag = KM_TAG_INVALID;
- break;
- case KM_BIGNUM:
- case KM_BYTES:
- params[i] =
- keymaster_param_blob(tag, &keyParams[i].blob[0], keyParams[i].blob.size());
- break;
- case KM_INVALID:
- default:
- params[i].tag = KM_TAG_INVALID;
- /* just skip */
- break;
- }
- }
- }
- KmParamSet(KmParamSet&& other) noexcept
- : keymaster_key_param_set_t{other.params, other.length} {
- other.length = 0;
- other.params = nullptr;
- }
- KmParamSet(const KmParamSet&) = delete;
- ~KmParamSet() { delete[] params; }
-};
-
-inline static KmParamSet hidlParams2KmParamSet(const hidl_vec<KeyParameter>& params) {
- return KmParamSet(params);
-}
-
-inline static keymaster_blob_t hidlVec2KmBlob(const hidl_vec<uint8_t>& blob) {
- /* hidl unmarshals funny pointers if the the blob is empty */
- if (blob.size()) return {&blob[0], blob.size()};
- return {};
-}
-
-inline static keymaster_key_blob_t hidlVec2KmKeyBlob(const hidl_vec<uint8_t>& blob) {
- /* hidl unmarshals funny pointers if the the blob is empty */
- if (blob.size()) return {&blob[0], blob.size()};
- return {};
-}
-
-inline static hidl_vec<uint8_t> kmBlob2hidlVec(const keymaster_key_blob_t& blob) {
- if (blob.key_material == nullptr || blob.key_material_size == 0) {
- return {};
- } else {
- return hidl_vec<uint8_t>(blob.key_material, blob.key_material + blob.key_material_size);
- }
-}
-inline static hidl_vec<uint8_t> kmBlob2hidlVec(const keymaster_blob_t& blob) {
- if (blob.data == nullptr || blob.data_length == 0) {
- return {};
- } else {
- return hidl_vec<uint8_t>(blob.data, blob.data + blob.data_length);
- }
-}
-
-inline static hidl_vec<hidl_vec<uint8_t>>
-kmCertChain2Hidl(const keymaster_cert_chain_t* cert_chain) {
- hidl_vec<hidl_vec<uint8_t>> result;
- if (!cert_chain || cert_chain->entry_count == 0 || !cert_chain->entries) return result;
-
- result.resize(cert_chain->entry_count);
- for (size_t i = 0; i < cert_chain->entry_count; ++i) {
- auto& entry = cert_chain->entries[i];
- result[i] = kmBlob2hidlVec(entry);
- }
-
- return result;
-}
-
-static inline hidl_vec<KeyParameter> kmParamSet2Hidl(const keymaster_key_param_set_t& set) {
- hidl_vec<KeyParameter> result;
- if (set.length == 0 || set.params == nullptr) return result;
-
- result.resize(set.length);
- keymaster_key_param_t* params = set.params;
- for (size_t i = 0; i < set.length; ++i) {
- auto tag = params[i].tag;
- result[i].tag = legacy_enum_conversion(tag);
- switch (typeFromTag(tag)) {
- case KM_ENUM:
- case KM_ENUM_REP:
- result[i].f.integer = params[i].enumerated;
- break;
- case KM_UINT:
- case KM_UINT_REP:
- result[i].f.integer = params[i].integer;
- break;
- case KM_ULONG:
- case KM_ULONG_REP:
- result[i].f.longInteger = params[i].long_integer;
- break;
- case KM_DATE:
- result[i].f.dateTime = params[i].date_time;
- break;
- case KM_BOOL:
- result[i].f.boolValue = params[i].boolean;
- break;
- case KM_BIGNUM:
- case KM_BYTES:
- result[i].blob = kmBlob2hidlVec(params[i].blob);
- break;
- case KM_INVALID:
- default:
- params[i].tag = KM_TAG_INVALID;
- /* just skip */
- break;
- }
- }
- return result;
-}
-
-// Methods from ::android::hardware::keymaster::V3_0::IKeymasterDevice follow.
-Return<void> LegacyKeymasterDeviceWrapper::getHardwareFeatures(getHardwareFeatures_cb _hidl_cb) {
- _hidl_cb(false, false, false, false, false, "Fallback Device", "Google Android Security");
- return Void();
-}
-
-Return<ErrorCode> LegacyKeymasterDeviceWrapper::addRngEntropy(const hidl_vec<uint8_t>& data) {
- return legacy_enum_conversion(
- keymaster_device_->add_rng_entropy(keymaster_device_, &data[0], data.size()));
-}
-
-Return<void> LegacyKeymasterDeviceWrapper::generateKey(const hidl_vec<KeyParameter>& keyParams,
- generateKey_cb _hidl_cb) {
- // result variables for the wire
- KeyCharacteristics resultCharacteristics;
- hidl_vec<uint8_t> resultKeyBlob;
-
- // result variables the backend understands
- keymaster_key_blob_t key_blob{nullptr, 0};
- keymaster_key_characteristics_t key_characteristics{{nullptr, 0}, {nullptr, 0}};
-
- // convert the parameter set to something our backend understands
- auto kmParams = hidlParams2KmParamSet(keyParams);
-
- auto rc = keymaster_device_->generate_key(keymaster_device_, &kmParams, &key_blob,
- &key_characteristics);
-
- if (rc == KM_ERROR_OK) {
- // on success convert the result to wire format
- resultKeyBlob = kmBlob2hidlVec(key_blob);
- resultCharacteristics.softwareEnforced = kmParamSet2Hidl(key_characteristics.sw_enforced);
- resultCharacteristics.teeEnforced = kmParamSet2Hidl(key_characteristics.hw_enforced);
- }
-
- // send results off to the client
- _hidl_cb(legacy_enum_conversion(rc), resultKeyBlob, resultCharacteristics);
-
- // free buffers that we are responsible for
- if (key_blob.key_material) free(const_cast<uint8_t*>(key_blob.key_material));
- keymaster_free_characteristics(&key_characteristics);
-
- return Void();
-}
-
-Return<void> LegacyKeymasterDeviceWrapper::getKeyCharacteristics(
- const hidl_vec<uint8_t>& keyBlob, const hidl_vec<uint8_t>& clientId,
- const hidl_vec<uint8_t>& appData, getKeyCharacteristics_cb _hidl_cb) {
- // result variables for the wire
- KeyCharacteristics resultCharacteristics;
-
- // result variables the backend understands
- keymaster_key_characteristics_t key_characteristics{{nullptr, 0}, {nullptr, 0}};
-
- auto kmKeyBlob = hidlVec2KmKeyBlob(keyBlob);
- auto kmClientId = hidlVec2KmBlob(clientId);
- auto kmAppData = hidlVec2KmBlob(appData);
-
- auto rc = keymaster_device_->get_key_characteristics(
- keymaster_device_, keyBlob.size() ? &kmKeyBlob : nullptr,
- clientId.size() ? &kmClientId : nullptr, appData.size() ? &kmAppData : nullptr,
- &key_characteristics);
-
- if (rc == KM_ERROR_OK) {
- resultCharacteristics.softwareEnforced = kmParamSet2Hidl(key_characteristics.sw_enforced);
- resultCharacteristics.teeEnforced = kmParamSet2Hidl(key_characteristics.hw_enforced);
- }
-
- _hidl_cb(legacy_enum_conversion(rc), resultCharacteristics);
-
- keymaster_free_characteristics(&key_characteristics);
-
- return Void();
-}
-
-Return<void> LegacyKeymasterDeviceWrapper::importKey(const hidl_vec<KeyParameter>& params,
- KeyFormat keyFormat,
- const hidl_vec<uint8_t>& keyData,
- importKey_cb _hidl_cb) {
- // result variables for the wire
- KeyCharacteristics resultCharacteristics;
- hidl_vec<uint8_t> resultKeyBlob;
-
- // result variables the backend understands
- keymaster_key_blob_t key_blob{nullptr, 0};
- keymaster_key_characteristics_t key_characteristics{{nullptr, 0}, {nullptr, 0}};
-
- auto kmParams = hidlParams2KmParamSet(params);
- auto kmKeyData = hidlVec2KmBlob(keyData);
-
- auto rc = keymaster_device_->import_key(keymaster_device_, &kmParams,
- legacy_enum_conversion(keyFormat), &kmKeyData,
- &key_blob, &key_characteristics);
-
- if (rc == KM_ERROR_OK) {
- // on success convert the result to wire format
- resultKeyBlob = kmBlob2hidlVec(key_blob);
- resultCharacteristics.softwareEnforced = kmParamSet2Hidl(key_characteristics.sw_enforced);
- resultCharacteristics.teeEnforced = kmParamSet2Hidl(key_characteristics.hw_enforced);
- }
-
- _hidl_cb(legacy_enum_conversion(rc), resultKeyBlob, resultCharacteristics);
-
- // free buffers that we are responsible for
- if (key_blob.key_material) free(const_cast<uint8_t*>(key_blob.key_material));
- keymaster_free_characteristics(&key_characteristics);
-
- return Void();
-}
-
-Return<void> LegacyKeymasterDeviceWrapper::exportKey(KeyFormat exportFormat,
- const hidl_vec<uint8_t>& keyBlob,
- const hidl_vec<uint8_t>& clientId,
- const hidl_vec<uint8_t>& appData,
- exportKey_cb _hidl_cb) {
-
- // result variables for the wire
- hidl_vec<uint8_t> resultKeyBlob;
-
- // result variables the backend understands
- keymaster_blob_t out_blob = {};
-
- auto kmKeyBlob = hidlVec2KmKeyBlob(keyBlob);
- auto kmClientId = hidlVec2KmBlob(clientId);
- auto kmAppData = hidlVec2KmBlob(appData);
-
- auto rc = keymaster_device_->export_key(keymaster_device_, legacy_enum_conversion(exportFormat),
- keyBlob.size() ? &kmKeyBlob : nullptr,
- clientId.size() ? &kmClientId : nullptr,
- appData.size() ? &kmAppData : nullptr, &out_blob);
-
- if (rc == KM_ERROR_OK) {
- // on success convert the result to wire format
- // (Can we assume that key_blob is {nullptr, 0} or a valid buffer description?)
- resultKeyBlob = kmBlob2hidlVec(out_blob);
- }
-
- _hidl_cb(legacy_enum_conversion(rc), resultKeyBlob);
-
- // free buffers that we are responsible for
- if (out_blob.data) free(const_cast<uint8_t*>(out_blob.data));
-
- return Void();
-}
-
-Return<void> LegacyKeymasterDeviceWrapper::attestKey(const hidl_vec<uint8_t>& keyToAttest,
- const hidl_vec<KeyParameter>& attestParams,
- attestKey_cb _hidl_cb) {
-
- hidl_vec<hidl_vec<uint8_t>> resultCertChain;
-
- for (size_t i = 0; i < attestParams.size(); ++i) {
- switch (attestParams[i].tag) {
- case Tag::ATTESTATION_ID_BRAND:
- case Tag::ATTESTATION_ID_DEVICE:
- case Tag::ATTESTATION_ID_PRODUCT:
- case Tag::ATTESTATION_ID_SERIAL:
- case Tag::ATTESTATION_ID_IMEI:
- case Tag::ATTESTATION_ID_MEID:
- case Tag::ATTESTATION_ID_MANUFACTURER:
- case Tag::ATTESTATION_ID_MODEL:
- // Device id attestation may only be supported if the device is able to permanently
- // destroy its knowledge of the ids. This device is unable to do this, so it must
- // never perform any device id attestation.
- _hidl_cb(ErrorCode::CANNOT_ATTEST_IDS, resultCertChain);
- return Void();
- default:
- break;
- }
- }
-
- keymaster_cert_chain_t cert_chain = {};
-
- auto kmKeyToAttest = hidlVec2KmKeyBlob(keyToAttest);
- auto kmAttestParams = hidlParams2KmParamSet(attestParams);
-
- auto rc = keymaster_device_->attest_key(keymaster_device_, &kmKeyToAttest, &kmAttestParams,
- &cert_chain);
-
- if (rc == KM_ERROR_OK) {
- resultCertChain = kmCertChain2Hidl(&cert_chain);
- }
-
- _hidl_cb(legacy_enum_conversion(rc), resultCertChain);
-
- keymaster_free_cert_chain(&cert_chain);
-
- return Void();
-}
-
-Return<void> LegacyKeymasterDeviceWrapper::upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
- const hidl_vec<KeyParameter>& upgradeParams,
- upgradeKey_cb _hidl_cb) {
-
- // result variables for the wire
- hidl_vec<uint8_t> resultKeyBlob;
-
- // result variables the backend understands
- keymaster_key_blob_t key_blob = {};
-
- auto kmKeyBlobToUpgrade = hidlVec2KmKeyBlob(keyBlobToUpgrade);
- auto kmUpgradeParams = hidlParams2KmParamSet(upgradeParams);
-
- auto rc = keymaster_device_->upgrade_key(keymaster_device_, &kmKeyBlobToUpgrade,
- &kmUpgradeParams, &key_blob);
-
- if (rc == KM_ERROR_OK) {
- // on success convert the result to wire format
- resultKeyBlob = kmBlob2hidlVec(key_blob);
- }
-
- _hidl_cb(legacy_enum_conversion(rc), resultKeyBlob);
-
- if (key_blob.key_material) free(const_cast<uint8_t*>(key_blob.key_material));
-
- return Void();
-}
-
-Return<ErrorCode> LegacyKeymasterDeviceWrapper::deleteKey(const hidl_vec<uint8_t>& keyBlob) {
- auto kmKeyBlob = hidlVec2KmKeyBlob(keyBlob);
- return legacy_enum_conversion(keymaster_device_->delete_key(keymaster_device_, &kmKeyBlob));
-}
-
-Return<ErrorCode> LegacyKeymasterDeviceWrapper::deleteAllKeys() {
- return legacy_enum_conversion(keymaster_device_->delete_all_keys(keymaster_device_));
-}
-
-Return<ErrorCode> LegacyKeymasterDeviceWrapper::destroyAttestationIds() {
- return ErrorCode::UNIMPLEMENTED;
-}
-
-Return<void> LegacyKeymasterDeviceWrapper::begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
- const hidl_vec<KeyParameter>& inParams,
- begin_cb _hidl_cb) {
-
- // result variables for the wire
- hidl_vec<KeyParameter> resultParams;
- uint64_t resultOpHandle = 0;
-
- // result variables the backend understands
- keymaster_key_param_set_t out_params{nullptr, 0};
- keymaster_operation_handle_t& operation_handle = resultOpHandle;
-
- auto kmKey = hidlVec2KmKeyBlob(key);
- auto kmInParams = hidlParams2KmParamSet(inParams);
-
- auto rc = keymaster_device_->begin(keymaster_device_, legacy_enum_conversion(purpose), &kmKey,
- &kmInParams, &out_params, &operation_handle);
-
- if (rc == KM_ERROR_OK) resultParams = kmParamSet2Hidl(out_params);
-
- _hidl_cb(legacy_enum_conversion(rc), resultParams, resultOpHandle);
-
- keymaster_free_param_set(&out_params);
-
- return Void();
-}
-
-Return<void> LegacyKeymasterDeviceWrapper::update(uint64_t operationHandle,
- const hidl_vec<KeyParameter>& inParams,
- const hidl_vec<uint8_t>& input,
- update_cb _hidl_cb) {
- // result variables for the wire
- uint32_t resultConsumed = 0;
- hidl_vec<KeyParameter> resultParams;
- hidl_vec<uint8_t> resultBlob;
-
- // result variables the backend understands
- size_t consumed = 0;
- keymaster_key_param_set_t out_params = {};
- keymaster_blob_t out_blob = {};
-
- auto kmInParams = hidlParams2KmParamSet(inParams);
- auto kmInput = hidlVec2KmBlob(input);
-
- auto rc = keymaster_device_->update(keymaster_device_, operationHandle, &kmInParams, &kmInput,
- &consumed, &out_params, &out_blob);
-
- if (rc == KM_ERROR_OK) {
- resultConsumed = consumed;
- resultParams = kmParamSet2Hidl(out_params);
- resultBlob = kmBlob2hidlVec(out_blob);
- }
-
- _hidl_cb(legacy_enum_conversion(rc), resultConsumed, resultParams, resultBlob);
-
- keymaster_free_param_set(&out_params);
- if (out_blob.data) free(const_cast<uint8_t*>(out_blob.data));
-
- return Void();
-}
-
-Return<void> LegacyKeymasterDeviceWrapper::finish(uint64_t operationHandle,
- const hidl_vec<KeyParameter>& inParams,
- const hidl_vec<uint8_t>& input,
- const hidl_vec<uint8_t>& signature,
- finish_cb _hidl_cb) {
- // result variables for the wire
- hidl_vec<KeyParameter> resultParams;
- hidl_vec<uint8_t> resultBlob;
-
- // result variables the backend understands
- keymaster_key_param_set_t out_params = {};
- keymaster_blob_t out_blob = {};
-
- auto kmInParams = hidlParams2KmParamSet(inParams);
- auto kmInput = hidlVec2KmBlob(input);
- auto kmSignature = hidlVec2KmBlob(signature);
-
- auto rc = keymaster_device_->finish(keymaster_device_, operationHandle, &kmInParams, &kmInput,
- &kmSignature, &out_params, &out_blob);
-
- if (rc == KM_ERROR_OK) {
- resultParams = kmParamSet2Hidl(out_params);
- resultBlob = kmBlob2hidlVec(out_blob);
- }
-
- _hidl_cb(legacy_enum_conversion(rc), resultParams, resultBlob);
-
- keymaster_free_param_set(&out_params);
- if (out_blob.data) free(const_cast<uint8_t*>(out_blob.data));
-
- return Void();
-}
-
-Return<ErrorCode> LegacyKeymasterDeviceWrapper::abort(uint64_t operationHandle) {
- return legacy_enum_conversion(keymaster_device_->abort(keymaster_device_, operationHandle));
-}
-
-sp<IKeymasterDevice> makeSoftwareKeymasterDevice() {
- keymaster2_device_t* dev = nullptr;
- dev = (new SoftKeymasterDevice(keymaster::KmVersion::KEYMASTER_2))->keymaster2_device();
-
- auto kmrc = ::keymaster::ConfigureDevice(dev);
- if (kmrc != KM_ERROR_OK) {
- dev->common.close(&dev->common);
- return nullptr;
- }
-
- return new LegacyKeymasterDeviceWrapper(dev);
-}
-
-} // namespace keystore
-} // namespace android
diff --git a/keystore/legacy_keymaster_device_wrapper.h b/keystore/legacy_keymaster_device_wrapper.h
deleted file mode 100644
index cd2e5a7..0000000
--- a/keystore/legacy_keymaster_device_wrapper.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- **
- ** Copyright 2016, 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.
- */
-
-#ifndef LEGACY_KEYMASTER_DEVICE_WRAPPER_H_
-#define LEGACY_KEYMASTER_DEVICE_WRAPPER_H_
-
-#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
-#include <hidl/Status.h>
-#include <hidl/MQDescriptor.h>
-
-struct keymaster2_device;
-typedef struct keymaster2_device keymaster2_device_t;
-
-namespace android {
-namespace keystore {
-
-using ::android::hardware::keymaster::V3_0::ErrorCode;
-using ::android::hardware::keymaster::V3_0::IKeymasterDevice;
-using ::android::hardware::keymaster::V3_0::KeyCharacteristics;
-using ::android::hardware::keymaster::V3_0::KeyFormat;
-using ::android::hardware::keymaster::V3_0::KeyParameter;
-using ::android::hardware::keymaster::V3_0::KeyPurpose;
-using ::android::hardware::keymaster::V3_0::Tag;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::hidl_string;
-using ::android::sp;
-
-class LegacyKeymasterDeviceWrapper : public IKeymasterDevice {
- public:
- explicit LegacyKeymasterDeviceWrapper(keymaster2_device_t* dev);
- virtual ~LegacyKeymasterDeviceWrapper();
-
- // Methods from ::android::hardware::keymaster::V3_0::IKeymasterDevice follow.
- Return<void> getHardwareFeatures(getHardwareFeatures_cb _hidl_cb);
- Return<ErrorCode> addRngEntropy(const hidl_vec<uint8_t>& data) override;
- Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
- generateKey_cb _hidl_cb) override;
- Return<void> getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,
- const hidl_vec<uint8_t>& clientId,
- const hidl_vec<uint8_t>& appData,
- getKeyCharacteristics_cb _hidl_cb) override;
- Return<void> importKey(const hidl_vec<KeyParameter>& params, KeyFormat keyFormat,
- const hidl_vec<uint8_t>& keyData, importKey_cb _hidl_cb) override;
- Return<void> exportKey(KeyFormat exportFormat, const hidl_vec<uint8_t>& keyBlob,
- const hidl_vec<uint8_t>& clientId, const hidl_vec<uint8_t>& appData,
- exportKey_cb _hidl_cb) override;
- Return<void> attestKey(const hidl_vec<uint8_t>& keyToAttest,
- const hidl_vec<KeyParameter>& attestParams,
- attestKey_cb _hidl_cb) override;
- Return<void> upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
- const hidl_vec<KeyParameter>& upgradeParams,
- upgradeKey_cb _hidl_cb) override;
- Return<ErrorCode> deleteKey(const hidl_vec<uint8_t>& keyBlob) override;
- Return<ErrorCode> deleteAllKeys() override;
- Return<ErrorCode> destroyAttestationIds() override;
- Return<void> begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
- const hidl_vec<KeyParameter>& inParams, begin_cb _hidl_cb) override;
- Return<void> update(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
- const hidl_vec<uint8_t>& input, update_cb _hidl_cb) override;
- Return<void> finish(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
- const hidl_vec<uint8_t>& input, const hidl_vec<uint8_t>& signature,
- finish_cb _hidl_cb) override;
- Return<ErrorCode> abort(uint64_t operationHandle) override;
-
- private:
- keymaster2_device_t* keymaster_device_;
-};
-
-sp<IKeymasterDevice> makeSoftwareKeymasterDevice();
-
-} // namespace keystore
-} // namespace android
-
-#endif // LEGACY_KEYMASTER_DEVICE_WRAPPER_H_
diff --git a/keystore/operation.cpp b/keystore/operation.cpp
deleted file mode 100644
index bd4bd5e..0000000
--- a/keystore/operation.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-#define LOG_TAG "KeystoreOperation"
-
-#include "operation.h"
-#include "key_operation_log_handler.h"
-
-#include <algorithm>
-#include <android-base/logging.h>
-#include <mutex>
-
-namespace keystore {
-
-OperationMap::OperationMap(IBinder::DeathRecipient* deathRecipient)
- : mDeathRecipient(deathRecipient) {}
-
-sp<IBinder> OperationMap::addOperation(uint64_t handle, uint64_t keyid, KeyPurpose purpose,
- const sp<Keymaster>& dev, const sp<IBinder>& appToken,
- KeyCharacteristics&& characteristics,
- const hidl_vec<KeyParameter>& params, bool pruneable) {
- sp<IBinder> token = new ::android::BBinder();
- mMap.emplace(token, std::make_shared<Operation>(handle, keyid, purpose, dev,
- std::move(characteristics), appToken, params));
- if (pruneable) mLru.push_back(token);
- if (mAppTokenMap.find(appToken) == mAppTokenMap.end()) appToken->linkToDeath(mDeathRecipient);
- mAppTokenMap[appToken].push_back(token);
- return token;
-}
-
-std::shared_ptr<Operation> OperationMap::getOperation(const sp<IBinder>& token) {
- auto entry = mMap.find(token);
- if (entry == mMap.end()) return {};
-
- auto op = entry->second;
-
- updateLru(token);
- return op;
-}
-
-void OperationMap::updateLru(const sp<IBinder>& token) {
- auto lruEntry = std::find(mLru.begin(), mLru.end(), token);
- if (lruEntry != mLru.end()) {
- mLru.erase(lruEntry);
- mLru.push_back(token);
- }
-}
-
-std::shared_ptr<Operation> OperationMap::removeOperation(const sp<IBinder>& token,
- bool wasSuccessful, int32_t responseCode) {
- auto entry = mMap.find(token);
- if (entry == mMap.end()) return {};
-
- auto op = entry->second;
- logKeystoreKeyOperationEvent(*op, wasSuccessful, responseCode);
- mMap.erase(entry);
-
- auto lruEntry = std::find(mLru.begin(), mLru.end(), token);
- if (lruEntry != mLru.end()) mLru.erase(lruEntry);
- removeOperationTracking(token, op->appToken);
- return op;
-}
-
-void OperationMap::removeOperationTracking(const sp<IBinder>& token, const sp<IBinder>& appToken) {
- auto appEntry = mAppTokenMap.find(appToken);
- if (appEntry == mAppTokenMap.end()) {
- ALOGE("Entry for %p contains unmapped application token %p", token.get(), appToken.get());
- return;
- }
- auto tokenEntry = std::find(appEntry->second.begin(), appEntry->second.end(), token);
- appEntry->second.erase(tokenEntry);
- // Stop listening for death if all operations tied to the token have finished.
- if (appEntry->second.size() == 0) {
- appToken->unlinkToDeath(mDeathRecipient);
- mAppTokenMap.erase(appEntry);
- }
-}
-
-sp<IBinder> OperationMap::getOldestPruneableOperation() {
- if (mLru.size() == 0) return {};
-
- return {mLru.front()};
-}
-
-std::vector<sp<IBinder>> OperationMap::getOperationsForToken(const sp<IBinder>& appToken) {
- auto appEntry = mAppTokenMap.find(appToken);
- if (appEntry == mAppTokenMap.end()) return {};
- return appEntry->second;
-}
-
-} // namespace keystore
diff --git a/keystore/operation.h b/keystore/operation.h
deleted file mode 100644
index 8423db5..0000000
--- a/keystore/operation.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#ifndef KEYSTORE_OPERATION_H_
-#define KEYSTORE_OPERATION_H_
-
-#include <list>
-#include <map>
-#include <memory>
-#include <mutex>
-#include <optional>
-#include <vector>
-
-#include <binder/Binder.h>
-#include <binder/IBinder.h>
-#include <keymasterV4_1/Keymaster.h>
-#include <utils/StrongPointer.h>
-
-#include <keystore/keymaster_types.h>
-#include <keystore/keystore_concurrency.h>
-#include <keystore/keystore_hidl_support.h>
-
-#include "operation_struct.h"
-
-namespace keystore {
-
-using ::android::IBinder;
-using ::android::sp;
-using keymaster::support::Keymaster;
-
-/**
- * OperationMap handles the translation of uint64_t's and keymaster2_device_t's to opaque binder
- * tokens that can be used to reference that operation at a later time by applications. It also does
- * LRU tracking for operation pruning and keeps a mapping of clients to operations to allow for
- * graceful handling of application death.
- */
-
-class OperationMap {
- public:
- explicit OperationMap(IBinder::DeathRecipient* deathRecipient);
- sp<IBinder> addOperation(uint64_t handle, uint64_t keyid, KeyPurpose purpose,
- const sp<Keymaster>& dev, const sp<IBinder>& appToken,
- KeyCharacteristics&& characteristics,
- const hidl_vec<KeyParameter>& params, bool pruneable);
- std::shared_ptr<Operation> getOperation(const sp<IBinder>& token);
- std::shared_ptr<Operation> removeOperation(const sp<IBinder>& token, bool wasSuccessful,
- int32_t responseCode);
- size_t getOperationCount() const { return mMap.size(); }
- sp<IBinder> getOldestPruneableOperation();
- std::vector<sp<IBinder>> getOperationsForToken(const sp<IBinder>& appToken);
-
- private:
- void updateLru(const sp<IBinder>& token);
- void removeOperationTracking(const sp<IBinder>& token, const sp<IBinder>& appToken);
-
- std::map<sp<IBinder>, std::shared_ptr<Operation>> mMap;
- std::list<sp<IBinder>> mLru;
- std::map<sp<IBinder>, std::vector<sp<IBinder>>> mAppTokenMap;
- IBinder::DeathRecipient* mDeathRecipient;
-};
-
-} // namespace keystore
-
-#endif
diff --git a/keystore/operation_struct.h b/keystore/operation_struct.h
deleted file mode 100644
index 23e79fc..0000000
--- a/keystore/operation_struct.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#ifndef KEYSTORE_OPERATION_STRUCT_H_
-#define KEYSTORE_OPERATION_STRUCT_H_
-
-#include <binder/Binder.h>
-#include <binder/IBinder.h>
-#include <keymasterV4_1/Keymaster.h>
-#include <utils/StrongPointer.h>
-
-#include <keystore/keymaster_types.h>
-#include <keystore/keystore_hidl_support.h>
-#include <keystore/keystore_return_types.h>
-
-#include <future>
-
-namespace keystore {
-
-using ::android::IBinder;
-using ::android::sp;
-using keymaster::support::Keymaster;
-
-struct Operation {
- Operation() = default;
- Operation(uint64_t handle_, uint64_t keyid_, KeyPurpose purpose_, const sp<Keymaster>& device_,
- KeyCharacteristics&& characteristics_, sp<IBinder> appToken_,
- const hidl_vec<KeyParameter> params_)
- : handle(handle_), keyid(keyid_), purpose(purpose_), device(device_),
- characteristics(characteristics_), appToken(appToken_), authToken(), verificationToken(),
- params(params_) {}
- Operation(Operation&&) = default;
- Operation(const Operation&) = delete;
-
- bool hasAuthToken() const { return authToken.mac.size() != 0; }
-
- uint64_t handle;
- uint64_t keyid;
- KeyPurpose purpose;
- sp<Keymaster> device;
- KeyCharacteristics characteristics;
- sp<IBinder> appToken;
- std::promise<KeyStoreServiceReturnCode> authTokenPromise;
- std::future<KeyStoreServiceReturnCode> authTokenFuture;
- HardwareAuthToken authToken;
- VerificationToken verificationToken;
- const hidl_vec<KeyParameter> params;
-};
-
-} // namespace keystore
-
-#endif
diff --git a/keystore/permissions.cpp b/keystore/permissions.cpp
deleted file mode 100644
index 2cd42cf..0000000
--- a/keystore/permissions.cpp
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#define LOG_TAG "keystore"
-
-#include "permissions.h"
-
-#include <cutils/sockets.h>
-#include <log/log.h>
-#include <private/android_filesystem_config.h>
-
-#include <selinux/android.h>
-
-#include "keystore_utils.h"
-
-/* perm_labels associcated with keystore_key SELinux class verbs. */
-const char* perm_labels[] = {
- "get_state",
- "get",
- "insert",
- "delete",
- "exist",
- "list",
- "reset",
- "password",
- "lock",
- "unlock",
- "is_empty",
- "sign",
- "verify",
- "grant",
- "duplicate",
- "clear_uid",
- "add_auth",
- "user_changed",
- "gen_unique_id",
-};
-
-struct user_euid {
- uid_t uid;
- uid_t euid;
-};
-
-user_euid user_euids[] = {{AID_VPN, AID_SYSTEM},
- {AID_WIFI, AID_SYSTEM},
- {AID_ROOT, AID_SYSTEM},
-
-#ifdef GRANT_ROOT_ALL_PERMISSIONS
- // Allow VTS tests to act on behalf of the wifi user
- {AID_WIFI, AID_ROOT}
-#endif
-};
-
-struct user_perm {
- uid_t uid;
- perm_t perms;
-};
-
-static user_perm user_perms[] = {
- {AID_SYSTEM, static_cast<perm_t>((uint32_t)(~0))},
- {AID_VPN, static_cast<perm_t>(P_GET | P_SIGN | P_VERIFY)},
- {AID_WIFI, static_cast<perm_t>(P_GET | P_SIGN | P_VERIFY)},
- {AID_BLUETOOTH, static_cast<perm_t>(P_GET | P_INSERT | P_DELETE | P_EXIST | P_SIGN | P_VERIFY)},
-
-#ifdef GRANT_ROOT_ALL_PERMISSIONS
- // Allow VTS tests running as root to perform all operations
- {AID_ROOT, static_cast<perm_t>((uint32_t)(~0))},
-#else
- {AID_ROOT, static_cast<perm_t>(P_GET)},
-#endif
-};
-
-static const perm_t DEFAULT_PERMS = static_cast<perm_t>(
- P_GET_STATE | P_GET | P_INSERT | P_DELETE | P_EXIST | P_LIST | P_SIGN | P_VERIFY |
- P_GEN_UNIQUE_ID /* Only privileged apps can do this, but enforcement is done by SELinux */);
-
-struct audit_data {
- pid_t pid;
- uid_t uid;
- const char* sid;
-};
-
-const char* get_perm_label(perm_t perm) {
- unsigned int index = ffs(perm);
- if (index > 0 && index <= (sizeof(perm_labels) / sizeof(perm_labels[0]))) {
- return perm_labels[index - 1];
- } else {
- ALOGE("Keystore: Failed to retrieve permission label.\n");
- abort();
- }
-}
-
-static int audit_callback(void* data, security_class_t /* cls */, char* buf, size_t len) {
- struct audit_data* ad = reinterpret_cast<struct audit_data*>(data);
- if (!ad) {
- ALOGE("No keystore audit data");
- return 0;
- }
-
- const char* sid = ad->sid ? ad->sid : "N/A";
- snprintf(buf, len, "pid=%d uid=%d sid=%s", ad->pid, ad->uid, sid);
- return 0;
-}
-
-static char* tctx;
-
-int configure_selinux() {
- union selinux_callback cb;
- cb.func_audit = audit_callback;
- selinux_set_callback(SELINUX_CB_AUDIT, cb);
- cb.func_log = selinux_log_callback;
- selinux_set_callback(SELINUX_CB_LOG, cb);
- if (getcon(&tctx) != 0) {
- ALOGE("SELinux: Could not acquire target context. Aborting keystore.\n");
- return -1;
- }
-
- return 0;
-}
-
-static bool keystore_selinux_check_access(uid_t uid, perm_t perm, pid_t spid, const char* ssid) {
- audit_data ad;
- char* sctx = nullptr;
- const char* selinux_class = "keystore_key";
- const char* str_perm = get_perm_label(perm);
-
- if (!str_perm) {
- return false;
- }
-
- if (ssid == nullptr && getpidcon(spid, &sctx) != 0) {
- ALOGE("SELinux: Failed to get source pid context.\n");
- return false;
- }
-
- const char* use_sid = ssid ? ssid : sctx;
-
- ad.pid = spid;
- ad.uid = uid;
- ad.sid = use_sid;
-
- bool allowed = selinux_check_access(use_sid, tctx, selinux_class, str_perm,
- reinterpret_cast<void*>(&ad)) == 0;
- freecon(sctx);
- return allowed;
-}
-
-/**
- * Returns the UID that the callingUid should act as. This is here for
- * legacy support of the WiFi and VPN systems and should be removed
- * when WiFi can operate in its own namespace.
- */
-uid_t get_keystore_euid(uid_t uid) {
- for (size_t i = 0; i < sizeof(user_euids) / sizeof(user_euids[0]); i++) {
- struct user_euid user = user_euids[i];
- if (user.uid == uid) {
- return user.euid;
- }
- }
-
- return uid;
-}
-
-bool has_permission(uid_t uid, perm_t perm, pid_t spid, const char* sid) {
- // All system users are equivalent for multi-user support.
- if (get_app_id(uid) == AID_SYSTEM) {
- uid = AID_SYSTEM;
- }
-
- if (sid == nullptr) {
- android_errorWriteLog(0x534e4554, "121035042");
- }
-
- for (size_t i = 0; i < sizeof(user_perms) / sizeof(user_perms[0]); i++) {
- struct user_perm user = user_perms[i];
- if (user.uid == uid) {
- return (user.perms & perm) && keystore_selinux_check_access(uid, perm, spid, sid);
- }
- }
-
- return (DEFAULT_PERMS & perm) && keystore_selinux_check_access(uid, perm, spid, sid);
-}
-
-/**
- * Returns true if the callingUid is allowed to interact in the targetUid's
- * namespace.
- */
-bool is_granted_to(uid_t callingUid, uid_t targetUid) {
- if (callingUid == targetUid) {
- return true;
- }
- for (size_t i = 0; i < sizeof(user_euids) / sizeof(user_euids[0]); i++) {
- struct user_euid user = user_euids[i];
- if (user.euid == callingUid && user.uid == targetUid) {
- return true;
- }
- }
-
- return false;
-}
diff --git a/keystore/permissions.h b/keystore/permissions.h
deleted file mode 100644
index 1dd0089..0000000
--- a/keystore/permissions.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#ifndef KEYSTORE_PERMISSIONS_H_
-#define KEYSTORE_PERMISSIONS_H_
-
-#include <unistd.h>
-
-/* Here are the permissions, actions, users, and the main function. */
-enum perm_t {
- P_GET_STATE = 1 << 0,
- P_GET = 1 << 1,
- P_INSERT = 1 << 2,
- P_DELETE = 1 << 3,
- P_EXIST = 1 << 4,
- P_LIST = 1 << 5,
- P_RESET = 1 << 6,
- P_PASSWORD = 1 << 7,
- P_LOCK = 1 << 8,
- P_UNLOCK = 1 << 9,
- P_IS_EMPTY = 1 << 10,
- P_SIGN = 1 << 11,
- P_VERIFY = 1 << 12,
- P_GRANT = 1 << 13,
- P_DUPLICATE = 1 << 14,
- P_CLEAR_UID = 1 << 15,
- P_ADD_AUTH = 1 << 16,
- P_USER_CHANGED = 1 << 17,
- P_GEN_UNIQUE_ID = 1 << 18,
-};
-
-const char* get_perm_label(perm_t perm);
-
-/**
- * Returns the UID that the callingUid should act as. This is here for
- * legacy support of the WiFi and VPN systems and should be removed
- * when WiFi can operate in its own namespace.
- */
-uid_t get_keystore_euid(uid_t uid);
-
-/**
- * Returns true if the uid/pid/sid has a permission. Checks based on sid if available.
- *
- * sid may be null on older kernels
- */
-bool has_permission(uid_t uid, perm_t perm, pid_t spid, const char* sid);
-
-/**
- * Returns true if the callingUid is allowed to interact in the targetUid's
- * namespace.
- */
-bool is_granted_to(uid_t callingUid, uid_t targetUid);
-
-int configure_selinux();
-
-/*
- * Keystore grants.
- *
- * What are keystore grants?
- *
- * Keystore grants are a mechanism that allows an app to grant the permission to use one of its
- * keys to an other app.
- *
- * Liftime of a grant:
- *
- * A keystore grant is ephemeral in that is never persistently stored. When the keystore process
- * exits, all grants are lost. Also, grants can be explicitly revoked by the granter by invoking
- * the ungrant operation.
- *
- * What happens when a grant is created?
- *
- * The grant operation expects a valid key alias and the uid of the grantee, i.e., the app that
- * shall be allowed to use the key denoted by the alias. It then makes an entry in the grant store
- * which generates a new alias of the form <alias>_KEYSTOREGRANT_<random_grant_no_>. This grant
- * alias is returned to the caller which can pass the new alias to the grantee. For every grantee,
- * the grant store keeps a set of grants, an entry of which holds the following information:
- * - the owner of the key by uid, aka granter uid,
- * - the original alias of the granted key, and
- * - the random grant number.
- * (See "grant_store.h:class Grant")
- *
- * What happens when a grant is used?
- *
- * Upon any keystore operation that expects an alias, the alias and the caller's uid are used
- * to retrieve a key file. If that fails some operations try to retrieve a key file indirectly
- * through a grant. These operations include:
- * - attestKey
- * - begin
- * - exportKey
- * - get
- * - getKeyCharacteristics
- * - del
- * - exist
- * - getmtime
- * Operations that DO NOT follow the grant indirection are:
- * - import
- * - generate
- * - grant
- * - ungrant
- * Especially, the latter two mean that neither can a grantee transitively grant a granted key
- * to a third, nor can they relinquish access to the key or revoke access to the key by a third.
- */
-
-#endif // KEYSTORE_PERMISSIONS_H_
diff --git a/keystore/tests/Android.bp b/keystore/tests/Android.bp
index 327eb93..f51cc2f 100644
--- a/keystore/tests/Android.bp
+++ b/keystore/tests/Android.bp
@@ -18,10 +18,6 @@
],
srcs: [
"aaid_truncation_test.cpp",
- "auth_token_table_test.cpp",
- "auth_token_formatting_test.cpp",
- "blob_test.cpp",
- "confirmationui_rate_limiting_test.cpp",
"verification_token_seralization_test.cpp",
"gtest_main.cpp",
],
@@ -35,7 +31,6 @@
"libhidlbase",
"libkeymaster4support",
"libkeymaster4_1support",
- "libkeystore_test",
"liblog",
"libutils",
],
@@ -63,17 +58,14 @@
],
name: "confirmationui_invocation_test",
static_libs: [
- "android.hardware.confirmationui@1.0",
"libbase",
"libgtest_main",
"libutils",
"liblog",
+ "android.security.apc-ndk",
],
shared_libs: [
- "libbinder",
- "libkeystore_aidl", // for IKeyStoreService.asInterface()
- "libkeystore_binder",
- "libkeystore_parcelables",
+ "libbinder_ndk",
],
sanitize: {
cfi: false,
diff --git a/keystore/tests/confirmationui_invocation_test.cpp b/keystore/tests/confirmationui_invocation_test.cpp
index f5182b5..7f8a373 100644
--- a/keystore/tests/confirmationui_invocation_test.cpp
+++ b/keystore/tests/confirmationui_invocation_test.cpp
@@ -15,11 +15,10 @@
** limitations under the License.
*/
-#include <android/hardware/confirmationui/1.0/types.h>
-#include <android/security/BnConfirmationPromptCallback.h>
-#include <android/security/keystore/IKeystoreService.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
+#include <aidl/android/security/apc/BnConfirmationCallback.h>
+#include <aidl/android/security/apc/IProtectedConfirmation.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
#include <gtest/gtest.h>
@@ -28,65 +27,50 @@
#include <tuple>
#include <vector>
-using ConfirmationResponseCode = android::hardware::confirmationui::V1_0::ResponseCode;
-using android::IBinder;
-using android::IServiceManager;
-using android::sp;
-using android::String16;
-using android::security::keystore::IKeystoreService;
-
using namespace std::literals::chrono_literals;
+namespace apc = ::aidl::android::security::apc;
class ConfirmationListener
- : public android::security::BnConfirmationPromptCallback,
- public std::promise<std::tuple<ConfirmationResponseCode, std::vector<uint8_t>>> {
+ : public apc::BnConfirmationCallback,
+ public std::promise<std::tuple<apc::ResponseCode, std::optional<std::vector<uint8_t>>>> {
public:
ConfirmationListener() {}
- virtual ::android::binder::Status
- onConfirmationPromptCompleted(int32_t result,
- const ::std::vector<uint8_t>& dataThatWasConfirmed) override {
- this->set_value({static_cast<ConfirmationResponseCode>(result), dataThatWasConfirmed});
- return ::android::binder::Status::ok();
- }
+ virtual ::ndk::ScopedAStatus
+ onCompleted(::aidl::android::security::apc::ResponseCode result,
+ const std::optional<std::vector<uint8_t>>& dataConfirmed) override {
+ this->set_value({result, dataConfirmed});
+ return ::ndk::ScopedAStatus::ok();
+ };
};
TEST(ConfirmationInvocationTest, InvokeAndCancel) {
- android::ProcessState::self()->startThreadPool();
+ ABinderProcess_startThreadPool();
- sp<IServiceManager> sm = android::defaultServiceManager();
- sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
- sp<IKeystoreService> service = android::interface_cast<IKeystoreService>(binder);
- ASSERT_TRUE(service);
+ ::ndk::SpAIBinder apcBinder(AServiceManager_getService("android.security.apc"));
+ auto apcService = apc::IProtectedConfirmation::fromBinder(apcBinder);
+ ASSERT_TRUE(apcService);
- String16 promptText16("Just a little test!");
- String16 locale16("en");
+ std::string promptText("Just a little test!");
+ std::string locale("en");
std::vector<uint8_t> extraData{0xaa, 0xff, 0x00, 0x55};
- sp<ConfirmationListener> listener = new ConfirmationListener();
+ auto listener = std::make_shared<ConfirmationListener>();
auto future = listener->get_future();
- int32_t aidl_return;
- android::binder::Status status = service->presentConfirmationPrompt(
- listener, promptText16, extraData, locale16, 0, &aidl_return);
- ASSERT_TRUE(status.isOk()) << "Presenting confirmation prompt failed with binder status '"
- << status.toString8().c_str() << "'.\n";
- ConfirmationResponseCode responseCode = static_cast<ConfirmationResponseCode>(aidl_return);
- ASSERT_EQ(responseCode, ConfirmationResponseCode::OK)
- << "Presenting confirmation prompt failed with response code " << aidl_return << ".\n";
+ auto rc = apcService->presentPrompt(listener, promptText, extraData, locale, 0);
+
+ ASSERT_TRUE(rc.isOk());
auto fstatus = future.wait_for(2s);
EXPECT_EQ(fstatus, std::future_status::timeout);
- status = service->cancelConfirmationPrompt(listener, &aidl_return);
- ASSERT_TRUE(status.isOk());
-
- responseCode = static_cast<ConfirmationResponseCode>(aidl_return);
- ASSERT_EQ(responseCode, ConfirmationResponseCode::OK);
+ rc = apcService->cancelPrompt(listener);
+ ASSERT_TRUE(rc.isOk());
future.wait();
- auto [rc, dataThatWasConfirmed] = future.get();
+ auto [responseCode, dataThatWasConfirmed] = future.get();
- ASSERT_EQ(rc, ConfirmationResponseCode::Aborted);
+ ASSERT_EQ(responseCode, apc::ResponseCode::ABORTED);
}
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index 812d5e6..9c3ce6e 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -21,19 +21,22 @@
default_applicable_licenses: ["system_security_license"],
}
-rust_library {
- name: "libkeystore2",
+rust_defaults {
+ name: "libkeystore2_defaults",
crate_name: "keystore2",
srcs: ["src/lib.rs"],
rustlibs: [
"android.hardware.security.keymint-V1-rust",
"android.hardware.security.secureclock-V1-rust",
+ "android.hardware.security.sharedsecret-V1-rust",
+ "android.os.permissions_aidl-rust",
"android.security.apc-rust",
"android.security.authorization-rust",
"android.security.compat-rust",
+ "android.security.maintenance-rust",
+ "android.security.metrics-rust",
"android.security.remoteprovisioning-rust",
- "android.security.usermanager-rust",
"android.system.keystore2-V1-rust",
"libanyhow",
"libbinder_rs",
@@ -42,14 +45,28 @@
"libkeystore2_crypto_rust",
"libkeystore2_km_compat",
"libkeystore2_selinux",
+ "libkeystore2_vintf_rust",
"liblazy_static",
"liblibc",
"liblibsqlite3_sys",
+ "liblog_event_list",
"liblog_rust",
"librand",
"librusqlite",
+ "librustutils",
"libthiserror",
],
+ shared_libs: [
+ "libcutils",
+ ],
+ features: [
+ "watchdog",
+ ],
+}
+
+rust_library {
+ name: "libkeystore2",
+ defaults: ["libkeystore2_defaults"],
}
rust_library {
@@ -59,41 +76,24 @@
rustlibs: [
"liblog_rust",
"librand",
- ]
+ ],
}
rust_test {
name: "keystore2_test",
crate_name: "keystore2",
- srcs: ["src/lib.rs"],
test_suites: ["general-tests"],
auto_gen_config: true,
compile_multilib: "first",
+ defaults: ["libkeystore2_defaults"],
rustlibs: [
- "android.hardware.security.keymint-V1-rust",
- "android.hardware.security.secureclock-V1-rust",
- "android.security.apc-rust",
- "android.security.authorization-rust",
- "android.security.compat-rust",
- "android.security.remoteprovisioning-rust",
- "android.security.usermanager-rust",
- "android.system.keystore2-V1-rust",
"libandroid_logger",
- "libanyhow",
- "libbinder_rs",
- "libkeystore2_aaid-rust",
- "libkeystore2_apc_compat-rust",
- "libkeystore2_crypto_rust",
- "libkeystore2_km_compat",
- "libkeystore2_selinux",
"libkeystore2_test_utils",
- "liblazy_static",
- "liblibc",
- "liblibsqlite3_sys",
- "liblog_rust",
- "librand",
- "librusqlite",
- "libthiserror",
+ "libnix",
+ ],
+ // The test should always include watchdog.
+ features: [
+ "watchdog",
],
}
@@ -105,6 +105,43 @@
"libbinder_rs",
"libkeystore2",
"liblog_rust",
+ "liblegacykeystore-rust",
+ "librusqlite",
],
init_rc: ["keystore2.rc"],
+
+ // In S, keystore2 is the only process using dynamically linked Rust from
+ // /system. As a result, the usual savings from sharing libraries don't
+ // apply.
+ // Remove `prefer_rlib: true` once we have several processes, once a space
+ // calculation shows net RAM savings, or once we have automatic variant
+ // selection available in the build system.
+ prefer_rlib: true,
+
+ // TODO(b/187412695)
+ // This is a hack to work around the build system not installing
+ // dynamic dependencies of rlibs to the device. This section should
+ // be removed once that works correctly.
+ shared_libs: [
+ "android.hardware.confirmationui@1.0",
+ "android.hardware.security.sharedsecret-V1-ndk",
+ "android.security.compat-ndk",
+ "libc",
+ "libdl_android",
+ "libdl",
+ "libandroidicu",
+ "libkeymint",
+ "libkeystore2_aaid",
+ "libkeystore2_apc_compat",
+ "libkeystore2_crypto",
+ "libkm_compat_service",
+ "libkm_compat",
+ "libm",
+ "libstatspull",
+ "libstatssocket",
+ ],
+
+ vintf_fragments: ["android.system.keystore2-service.xml"],
+
+ required: ["keystore_cli_v2"],
}
diff --git a/keystore2/TEST_MAPPING b/keystore2/TEST_MAPPING
index 99a1e60..16b6f85 100644
--- a/keystore2/TEST_MAPPING
+++ b/keystore2/TEST_MAPPING
@@ -8,6 +8,9 @@
},
{
"name": "keystore2_test"
+ },
+ {
+ "name": "CtsIdentityTestCases"
}
]
}
diff --git a/keystore2/aaid/Android.bp b/keystore2/aaid/Android.bp
index d27fdf6..3417960 100644
--- a/keystore2/aaid/Android.bp
+++ b/keystore2/aaid/Android.bp
@@ -39,8 +39,8 @@
bindgen_flags: [
"--size_t-is-usize",
- "--whitelist-function=aaid_keystore_attestation_id",
- "--whitelist-var=KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE",
+ "--allowlist-function=aaid_keystore_attestation_id",
+ "--allowlist-var=KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE",
],
}
@@ -57,3 +57,13 @@
"libkeystore2_aaid",
],
}
+
+rust_test {
+ name: "libkeystore2_aaid_bindgen_test",
+ srcs: [":libkeystore2_aaid_bindgen"],
+ crate_name: "keystore2_aaid_bindgen_test",
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ clippy_lints: "none",
+ lints: "none",
+}
diff --git a/keystore2/aidl/Android.bp b/keystore2/aidl/Android.bp
index c92417b..4a7b7b4 100644
--- a/keystore2/aidl/Android.bp
+++ b/keystore2/aidl/Android.bp
@@ -24,15 +24,20 @@
aidl_interface {
name: "android.security.attestationmanager",
srcs: [ "android/security/attestationmanager/*.aidl", ],
- imports: [ "android.hardware.security.keymint" ],
+ imports: [ "android.hardware.security.keymint-V1" ],
unstable: true,
backend: {
java: {
- sdk_version: "module_current",
+ platform_apis: true,
+ srcs_available: true,
},
rust: {
enabled: true,
},
+ ndk: {
+ enabled: true,
+ apps_enabled: false,
+ }
},
}
@@ -40,19 +45,21 @@
name: "android.security.authorization",
srcs: [ "android/security/authorization/*.aidl" ],
imports: [
- "android.hardware.security.keymint",
- "android.hardware.security.secureclock",
+ "android.hardware.security.keymint-V1",
+ "android.hardware.security.secureclock-V1",
],
unstable: true,
backend: {
java: {
- sdk_version: "module_current",
+ platform_apis: true,
+ srcs_available: true,
},
rust: {
enabled: true,
},
ndk: {
enabled: true,
+ apps_enabled: false,
}
},
}
@@ -64,10 +71,14 @@
backend: {
java: {
enabled: true,
+ srcs_available: true,
},
rust: {
enabled: true,
},
+ ndk: {
+ enabled: true,
+ }
},
}
@@ -75,21 +86,23 @@
name: "android.security.compat",
srcs: [ "android/security/compat/*.aidl" ],
imports: [
- "android.hardware.security.keymint",
- "android.hardware.security.secureclock",
- "android.hardware.security.sharedsecret",
+ "android.hardware.security.keymint-V1",
+ "android.hardware.security.secureclock-V1",
+ "android.hardware.security.sharedsecret-V1",
],
unstable: true,
backend: {
java: {
- sdk_version: "module_current",
+ platform_apis: true,
+ srcs_available: true,
},
rust: {
enabled: true,
},
ndk: {
enabled: true,
- }
+ apps_enabled: false,
+ },
},
}
@@ -97,17 +110,17 @@
name: "android.security.remoteprovisioning",
srcs: [ "android/security/remoteprovisioning/*.aidl" ],
imports: [
- "android.hardware.security.keymint",
+ "android.hardware.security.keymint-V1",
],
unstable: true,
backend: {
java: {
- enabled: true,
- sdk_version: "module_current",
platform_apis: true,
+ srcs_available: true,
},
ndk: {
enabled: true,
+ apps_enabled: false,
},
rust: {
enabled: true,
@@ -116,21 +129,65 @@
}
aidl_interface {
- name: "android.security.usermanager",
- srcs: [ "android/security/usermanager/*.aidl" ],
+ name: "android.security.maintenance",
+ srcs: [ "android/security/maintenance/*.aidl" ],
imports: [
- "android.system.keystore2",
+ "android.system.keystore2-V1",
],
unstable: true,
backend: {
java: {
- sdk_version: "module_current",
+ platform_apis: true,
+ srcs_available: true,
},
rust: {
enabled: true,
},
ndk: {
enabled: true,
+ apps_enabled: false,
}
},
}
+
+aidl_interface {
+ name: "android.security.legacykeystore",
+ srcs: [ "android/security/legacykeystore/*.aidl" ],
+ unstable: true,
+ backend: {
+ java: {
+ platform_apis: true,
+ srcs_available: true,
+ },
+ rust: {
+ enabled: true,
+ },
+ ndk: {
+ enabled: true,
+ apps_enabled: false,
+ }
+ },
+}
+
+aidl_interface {
+ name: "android.security.metrics",
+ srcs: [ "android/security/metrics/*.aidl" ],
+ imports: [
+ "android.system.keystore2-V1",
+ ],
+ unstable: true,
+ backend: {
+ java: {
+ platform_apis: true,
+ srcs_available: true,
+ },
+ rust: {
+ enabled: true,
+ },
+ ndk: {
+ enabled: true,
+ apps_enabled: false,
+ }
+ },
+}
+
diff --git a/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl b/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl
index f47d7f5..277b9dd 100644
--- a/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl
+++ b/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl
@@ -21,6 +21,7 @@
/**
* This callback interface must be implemented by the client to receive the result of the user
* confirmation.
+ * @hide
*/
interface IConfirmationCallback {
/**
diff --git a/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl b/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl
index 26ccf0f..3162224 100644
--- a/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl
+++ b/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl
@@ -18,6 +18,7 @@
import android.security.apc.IConfirmationCallback;
+/** @hide */
interface IProtectedConfirmation {
/**
diff --git a/keystore2/aidl/android/security/apc/ResponseCode.aidl b/keystore2/aidl/android/security/apc/ResponseCode.aidl
index 7ae3e1c..9a3619f 100644
--- a/keystore2/aidl/android/security/apc/ResponseCode.aidl
+++ b/keystore2/aidl/android/security/apc/ResponseCode.aidl
@@ -19,6 +19,7 @@
/**
* Used as service specific exception code by IProtectedConfirmation and as result
* code by IConfirmationCallback
+ * @hide
*/
@Backing(type="int")
enum ResponseCode {
diff --git a/keystore2/aidl/android/security/attestationmanager/ByteArray.aidl b/keystore2/aidl/android/security/attestationmanager/ByteArray.aidl
index a1592ec..dc37b1b 100644
--- a/keystore2/aidl/android/security/attestationmanager/ByteArray.aidl
+++ b/keystore2/aidl/android/security/attestationmanager/ByteArray.aidl
@@ -18,7 +18,6 @@
/**
* Simple data holder for a byte array, allowing for multidimensional arrays in AIDL.
- *
* @hide
*/
parcelable ByteArray {
diff --git a/keystore2/aidl/android/security/attestationmanager/IAttestationManager.aidl b/keystore2/aidl/android/security/attestationmanager/IAttestationManager.aidl
index 85eee57..e77a21e 100644
--- a/keystore2/aidl/android/security/attestationmanager/IAttestationManager.aidl
+++ b/keystore2/aidl/android/security/attestationmanager/IAttestationManager.aidl
@@ -21,7 +21,6 @@
/**
* Internal interface for performing device attestation.
- *
* @hide
*/
interface IAttestationManager {
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..3f33431 100644
--- a/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
+++ b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
@@ -16,15 +16,17 @@
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).
/**
* IKeystoreAuthorization interface exposes the methods for other system components to
* provide keystore with the information required to enforce authorizations on key usage.
+ * @hide
*/
+ @SensitiveData
interface IKeystoreAuthorization {
-
/**
* Allows the Android authenticators to hand over an auth token to Keystore.
* Callers require 'AddAuth' permission.
@@ -45,6 +47,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.
@@ -53,7 +57,60 @@
* @param userId - Android user id
*
* @param password - synthetic password derived by the user denoted by the user id
+ *
+ * @param unlockingSids - list of biometric SIDs for this user. This will be null when
+ * lockScreenEvent is UNLOCK, but may be non-null when
+ * lockScreenEvent is LOCK.
+ *
+ * When the device is unlocked, Keystore stores in memory
+ * a super-encryption key that protects UNLOCKED_DEVICE_REQUIRED
+ * keys; this key is wiped from memory when the device is locked.
+ *
+ * If unlockingSids is non-empty on lock, then before the
+ * super-encryption key is wiped from memory, a copy of it
+ * is stored in memory encrypted with a fresh AES key.
+ * This key is then imported into KM, tagged such that it can be
+ * used given a valid, recent auth token for any of the
+ * unlockingSids.
+ *
+ * Then, when the device is unlocked again, if a suitable auth token
+ * has been sent to keystore, it is used to recover the
+ * super-encryption key, so that UNLOCKED_DEVICE_REQUIRED keys can
+ * be used once again.
*/
void onLockScreenEvent(in LockScreenEvent lockScreenEvent, in int userId,
- in @nullable byte[] password);
+ in @nullable byte[] password, in @nullable long[] unlockingSids);
+
+ /**
+ * 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/LockScreenEvent.aidl b/keystore2/aidl/android/security/authorization/LockScreenEvent.aidl
index 877a916..c7553a2 100644
--- a/keystore2/aidl/android/security/authorization/LockScreenEvent.aidl
+++ b/keystore2/aidl/android/security/authorization/LockScreenEvent.aidl
@@ -14,6 +14,7 @@
package android.security.authorization;
+/** @hide */
@Backing(type="int")
enum LockScreenEvent {
UNLOCK = 0,
diff --git a/keystore2/aidl/android/security/authorization/ResponseCode.aidl b/keystore2/aidl/android/security/authorization/ResponseCode.aidl
new file mode 100644
index 0000000..169dc7b
--- /dev/null
+++ b/keystore2/aidl/android/security/authorization/ResponseCode.aidl
@@ -0,0 +1,57 @@
+// 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.
+ * @hide
+ */
+@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/aidl/android/security/compat/IKeystoreCompatService.aidl b/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl
index 4b6a93b..50bfa19 100644
--- a/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl
+++ b/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl
@@ -25,6 +25,7 @@
* The compatibility service allows Keystore 2.0 to connect to legacy wrapper implementations that
* it hosts itself without registering them as a service. Keystore 2.0 would not be allowed to
* register a HAL service, so instead it registers this service which it can then connect to.
+ * @hide
*/
interface IKeystoreCompatService {
/**
diff --git a/keystore2/aidl/android/security/legacykeystore/ILegacyKeystore.aidl b/keystore2/aidl/android/security/legacykeystore/ILegacyKeystore.aidl
new file mode 100644
index 0000000..fe93673
--- /dev/null
+++ b/keystore2/aidl/android/security/legacykeystore/ILegacyKeystore.aidl
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 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.legacykeystore;
+
+/**
+ * Internal interface for accessing and storing legacy keystore blobs.
+ * Before Android S, Keystore offered a key-value store that was intended for storing
+ * data associated with certain types of keys. E.g., public certificates for asymmetric keys.
+ * This key value store no longer exists as part of the Keystore 2.0 protocol.
+ * However, there are some clients that used Keystore in an unintended way.
+ * This interface exists to give these clients a grace period to migrate their keys
+ * out of legacy keystore. In Android S, this legacy keystore may be used as keystore was
+ * used in earlier versions, and provides access to entries that were put into keystore
+ * before Android S.
+ *
+ * DEPRECATION NOTICE: In Android T, the `put` function is slated to be removed.
+ * This will allow clients to use the `get`, `list`, and `remove` API to migrate blobs out
+ * of legacy keystore.
+ * @hide
+ */
+interface ILegacyKeystore {
+
+ /**
+ * Special value indicating the callers uid.
+ */
+ const int UID_SELF = -1;
+
+ /**
+ * Service specific error code indicating that an unexpected system error occurred.
+ */
+ const int ERROR_SYSTEM_ERROR = 4;
+
+ /**
+ * Service specific error code indicating that the caller does not have the
+ * right to access the requested uid.
+ */
+ const int ERROR_PERMISSION_DENIED = 6;
+
+ /**
+ * Service specific error code indicating that the entry was not found.
+ */
+ const int ERROR_ENTRY_NOT_FOUND = 7;
+
+ /**
+ * Returns the blob stored under the given name.
+ *
+ * @param alias name of the blob entry.
+ * @param uid designates the legacy namespace. Specify UID_SELF for the caller's namespace.
+ * @return The unstructured blob that was passed as blob parameter into put()
+ */
+ byte[] get(in String alias, int uid);
+
+ /**
+ * Stores one entry as unstructured blob under the given alias.
+ * Overwrites existing entries with the same alias.
+ *
+ * @param alias name of the new entry.
+ * @param uid designates the legacy namespace. Specify UID_SELF for the caller's namespace.
+ * @param blob the payload of the new entry.
+ *
+ * IMPORTANT DEPRECATION NOTICE: This function is slated to be removed in Android T.
+ * Do not add new callers. The remaining functionality will remain for the purpose
+ * of migrating legacy configuration out.
+ */
+ void put(in String alias, int uid, in byte[] blob);
+
+ /**
+ * Deletes the entry under the given alias.
+ *
+ * @param alias name of the entry to be removed.
+ * @param uid designates the legacy namespace of the entry. Specify UID_SELF for the caller's
+ * namespace.
+ */
+ void remove(in String alias, int uid);
+
+ /**
+ * Returns a list of aliases of entries stored. The list is filtered by prefix.
+ * The resulting strings are the full aliases including the prefix.
+ *
+ * @param prefix used to filter results.
+ * @param uid legacy namespace to list. Specify UID_SELF for caller's namespace.
+ */
+ String[] list(in String prefix, int uid);
+}
diff --git a/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl b/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
new file mode 100644
index 0000000..6a37c78
--- /dev/null
+++ b/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
@@ -0,0 +1,134 @@
+// 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.maintenance;
+
+import android.system.keystore2.Domain;
+import android.system.keystore2.KeyDescriptor;
+import android.security.maintenance.UserState;
+
+/**
+ * IKeystoreMaintenance interface exposes the methods for adding/removing users and changing the
+ * user's password.
+ * @hide
+ */
+ @SensitiveData
+interface IKeystoreMaintenance {
+
+ /**
+ * Allows LockSettingsService to inform keystore about adding a new user.
+ * Callers require 'AddUser' permission.
+ *
+ * ## Error conditions:
+ * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'AddUser' permission.
+ * `ResponseCode::SYSTEM_ERROR` - if failed to delete the keys of an existing user with the same
+ * user id.
+ *
+ * @param userId - Android user id
+ */
+ void onUserAdded(in int userId);
+
+ /**
+ * Allows LockSettingsService to inform keystore about removing a user.
+ * Callers require 'RemoveUser' permission.
+ *
+ * ## Error conditions:
+ * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'RemoveUser' permission.
+ * `ResponseCode::SYSTEM_ERROR` - if failed to delete the keys of the user being deleted.
+ *
+ * @param userId - Android user id
+ */
+ void onUserRemoved(in int userId);
+
+ /**
+ * Allows LockSettingsService to inform keystore about password change of a user.
+ * Callers require 'ChangePassword' permission.
+ *
+ * ## Error conditions:
+ * `ResponseCode::PERMISSION_DENIED` - if the callers does not have the 'ChangePassword'
+ * permission.
+ * `ResponseCode::SYSTEM_ERROR` - if failed to delete the super encrypted keys of the user.
+ * `ResponseCode::Locked' - if the keystore is locked for the given user.
+ *
+ * @param userId - Android user id
+ * @param password - a secret derived from the synthetic password of the user
+ */
+ void onUserPasswordChanged(in int userId, in @nullable byte[] password);
+
+ /**
+ * This function deletes all keys within a namespace. It mainly gets called when an app gets
+ * removed and all resources of this app need to be cleaned up.
+ *
+ * @param domain - One of Domain.APP or Domain.SELINUX.
+ * @param nspace - The UID of the app that is to be cleared if domain is Domain.APP or
+ * the SEPolicy namespace if domain is Domain.SELINUX.
+ */
+ void clearNamespace(Domain domain, long nspace);
+
+ /**
+ * Allows querying user state, given user id.
+ * Callers require 'GetState' permission.
+ *
+ * ## Error conditions:
+ * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'GetState'
+ * permission.
+ * `ResponseCode::SYSTEM_ERROR` - if an error occurred when querying the user state.
+ *
+ * @param userId - Android user id
+ */
+ UserState getState(in int userId);
+
+ /**
+ * This function notifies the Keymint device of the specified securityLevel that
+ * early boot has ended, so that they no longer allow early boot keys to be used.
+ * ## Error conditions:
+ * `ResponseCode::PERMISSION_DENIED` - if the caller does not have the 'EarlyBootEnded'
+ * permission.
+ * A KeyMint ErrorCode may be returned indicating a backend diagnosed error.
+ */
+ void earlyBootEnded();
+
+ /**
+ * Informs Keystore 2.0 that the an off body event was detected.
+ *
+ * ## Error conditions:
+ * `ResponseCode::PERMISSION_DENIED` - if the caller does not have the `ReportOffBody`
+ * permission.
+ * `ResponseCode::SYSTEM_ERROR` - if an unexpected error occurred.
+ */
+ void onDeviceOffBody();
+
+ /**
+ * Migrate a key from one namespace to another. The caller must have use, grant, and delete
+ * permissions on the source namespace and rebind permissions on the destination namespace.
+ * The source may be specified by Domain::APP, Domain::SELINUX, or Domain::KEY_ID. The target
+ * may be specified by Domain::APP or Domain::SELINUX.
+ *
+ * ## Error conditions:
+ * `ResponseCode::PERMISSION_DENIED` - If the caller lacks any of the required permissions.
+ * `ResponseCode::KEY_NOT_FOUND` - If the source did not exist.
+ * `ResponseCode::INVALID_ARGUMENT` - If the target exists or if any of the above mentioned
+ * requirements for the domain parameter are not met.
+ * `ResponseCode::SYSTEM_ERROR` - An unexpected system error occurred.
+ */
+ void migrateKeyNamespace(in KeyDescriptor source, in KeyDescriptor destination);
+
+ /**
+ * Deletes all keys in all hardware keystores. Used when keystore is reset completely. After
+ * this function is called all keys with Tag::ROLLBACK_RESISTANCE in their hardware-enforced
+ * authorization lists must be rendered permanently unusable. Keys without
+ * Tag::ROLLBACK_RESISTANCE may or may not be rendered unusable.
+ */
+ void deleteAllKeys();
+}
diff --git a/keystore2/aidl/android/security/maintenance/UserState.aidl b/keystore2/aidl/android/security/maintenance/UserState.aidl
new file mode 100644
index 0000000..376f4fb
--- /dev/null
+++ b/keystore2/aidl/android/security/maintenance/UserState.aidl
@@ -0,0 +1,23 @@
+// 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.maintenance;
+
+/** @hide */
+@Backing(type="int")
+enum UserState {
+ UNINITIALIZED = 0,
+ LSKF_UNLOCKED = 1,
+ LSKF_LOCKED = 2,
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/Algorithm.aidl b/keystore2/aidl/android/security/metrics/Algorithm.aidl
new file mode 100644
index 0000000..8e8d107
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/Algorithm.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.metrics;
+
+/**
+ * Algorithm enum as defined in stats/enums/system/security/keystore2/enums.proto.
+ * @hide
+ */
+@Backing(type="int")
+enum Algorithm {
+ /** ALGORITHM is prepended because UNSPECIFIED exists in other enums as well. */
+ ALGORITHM_UNSPECIFIED = 0,
+
+ /** Asymmetric algorithms. */
+ RSA = 1,
+
+ /** 2 removed, do not reuse. */
+ EC = 3,
+
+ /** Block cipher algorithms. */
+ AES = 32,
+ TRIPLE_DES = 33,
+
+ /** MAC algorithms. */
+ HMAC = 128,
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/AtomID.aidl b/keystore2/aidl/android/security/metrics/AtomID.aidl
new file mode 100644
index 0000000..166e753
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/AtomID.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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.metrics;
+
+/**
+ * Atom IDs as defined in frameworks/proto_logging/stats/atoms.proto.
+ * @hide
+ */
+@Backing(type="int")
+enum AtomID {
+ STORAGE_STATS = 10103,
+ RKP_POOL_STATS = 10104,
+ KEY_CREATION_WITH_GENERAL_INFO = 10118,
+ KEY_CREATION_WITH_AUTH_INFO = 10119,
+ KEY_CREATION_WITH_PURPOSE_AND_MODES_INFO = 10120,
+ KEYSTORE2_ATOM_WITH_OVERFLOW = 10121,
+ KEY_OPERATION_WITH_PURPOSE_AND_MODES_INFO = 10122,
+ KEY_OPERATION_WITH_GENERAL_INFO = 10123,
+ RKP_ERROR_STATS = 10124,
+ CRASH_STATS = 10125,
+}
\ No newline at end of file
diff --git a/keystore/binder/android/security/keystore/ICredstoreTokenCallback.aidl b/keystore2/aidl/android/security/metrics/CrashStats.aidl
similarity index 69%
rename from keystore/binder/android/security/keystore/ICredstoreTokenCallback.aidl
rename to keystore2/aidl/android/security/metrics/CrashStats.aidl
index b42e3d4..8ca043b 100644
--- a/keystore/binder/android/security/keystore/ICredstoreTokenCallback.aidl
+++ b/keystore2/aidl/android/security/metrics/CrashStats.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2020, The Android Open Source Project
+/*
+ * 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.
@@ -14,12 +14,10 @@
* limitations under the License.
*/
-package android.security.keystore;
+package android.security.metrics;
-
-/**
- * @hide
- */
-oneway interface ICredstoreTokenCallback {
- void onFinished(boolean success, in byte[] authToken, in byte[] verificationToken);
-}
+/** @hide */
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable CrashStats {
+ int count_of_crash_events;
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/EcCurve.aidl b/keystore2/aidl/android/security/metrics/EcCurve.aidl
new file mode 100644
index 0000000..b190d83
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/EcCurve.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.metrics;
+
+/**
+ * EcCurve enum as defined in Keystore2KeyCreationWithGeneralInfo of
+ * frameworks/proto_logging/stats/atoms.proto.
+ * @hide
+ */
+@Backing(type="int")
+enum EcCurve {
+ /** Unspecified takes 0. Other values are incremented by 1 compared to the keymint spec. */
+ EC_CURVE_UNSPECIFIED = 0,
+ P_224 = 1,
+ P_256 = 2,
+ P_384 = 3,
+ P_521 = 4,
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/HardwareAuthenticatorType.aidl b/keystore2/aidl/android/security/metrics/HardwareAuthenticatorType.aidl
new file mode 100644
index 0000000..b13f6ea
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/HardwareAuthenticatorType.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.metrics;
+
+/**
+ * HardwareAuthenticatorType enum as defined in Keystore2KeyCreationWithAuthInfo of
+ * frameworks/proto_logging/stats/atoms.proto.
+ * @hide
+ */
+@Backing(type="int")
+enum HardwareAuthenticatorType {
+ /** Unspecified takes 0. Other values are incremented by 1 compared to keymint spec. */
+ AUTH_TYPE_UNSPECIFIED = 0,
+ NONE = 1,
+ PASSWORD = 2,
+ FINGERPRINT = 3,
+ ANY = 5,
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/IKeystoreMetrics.aidl b/keystore2/aidl/android/security/metrics/IKeystoreMetrics.aidl
new file mode 100644
index 0000000..342cf01
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/IKeystoreMetrics.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.metrics;
+
+import android.security.metrics.KeystoreAtom;
+import android.security.metrics.AtomID;
+
+/**
+ * IKeystoreMetrics interface exposes the method for system server to pull metrics from keystore.
+ * @hide
+ */
+interface IKeystoreMetrics {
+ /**
+ * Allows the metrics routing proxy to pull the metrics from keystore.
+ *
+ * @return an array of KeystoreAtom objects with the atomID. There can be multiple atom objects
+ * for the same atomID, encapsulating different combinations of values for the atom fields.
+ * If there is no atom object found for the atomID in the metrics store, an empty array is
+ * returned.
+ *
+ * Callers require 'PullMetrics' permission.
+ *
+ * @param atomID - ID of the atom to be pulled.
+ *
+ * Errors are reported as service specific errors.
+ */
+ KeystoreAtom[] pullMetrics(in AtomID atomID);
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/KeyCreationWithAuthInfo.aidl b/keystore2/aidl/android/security/metrics/KeyCreationWithAuthInfo.aidl
new file mode 100644
index 0000000..ff200bc
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/KeyCreationWithAuthInfo.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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.metrics;
+
+import android.security.metrics.HardwareAuthenticatorType;
+import android.security.metrics.SecurityLevel;
+
+/**
+ * Atom that encapsulates authentication related information in key creation events.
+ * @hide
+ */
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable KeyCreationWithAuthInfo {
+ HardwareAuthenticatorType user_auth_type;
+ /**
+ * Base 10 logarithm of time out in seconds.
+ * Logarithm is taken in order to reduce the cardinaltiy.
+ */
+ int log10_auth_key_timeout_seconds;
+ SecurityLevel security_level;
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/KeyCreationWithGeneralInfo.aidl b/keystore2/aidl/android/security/metrics/KeyCreationWithGeneralInfo.aidl
new file mode 100644
index 0000000..74cd9ef
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/KeyCreationWithGeneralInfo.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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.metrics;
+
+import android.security.metrics.Algorithm;
+import android.security.metrics.EcCurve;
+import android.security.metrics.KeyOrigin;
+
+/**
+ * Atom that encapsulates a set of general information in key creation events.
+ * @hide
+ */
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable KeyCreationWithGeneralInfo {
+ Algorithm algorithm;
+ int key_size;
+ EcCurve ec_curve;
+ KeyOrigin key_origin;
+ int error_code;
+ boolean attestation_requested = false;
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/KeyCreationWithPurposeAndModesInfo.aidl b/keystore2/aidl/android/security/metrics/KeyCreationWithPurposeAndModesInfo.aidl
new file mode 100644
index 0000000..dda61c4
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/KeyCreationWithPurposeAndModesInfo.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.metrics;
+
+import android.security.metrics.Algorithm;
+
+/**
+ * Atom that encapsulates the repeated fields in key creation events.
+ * @hide
+ */
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable KeyCreationWithPurposeAndModesInfo {
+ Algorithm algorithm;
+ int purpose_bitmap;
+ int padding_mode_bitmap;
+ int digest_bitmap;
+ int block_mode_bitmap;
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/KeyOperationWithGeneralInfo.aidl b/keystore2/aidl/android/security/metrics/KeyOperationWithGeneralInfo.aidl
new file mode 100644
index 0000000..d70aaf3
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/KeyOperationWithGeneralInfo.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.metrics;
+
+import android.security.metrics.Outcome;
+import android.security.metrics.SecurityLevel;
+
+/**
+ * Atom that encapsulates a set of general information in key operation events.
+ * @hide
+ */
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable KeyOperationWithGeneralInfo {
+ Outcome outcome;
+ int error_code;
+ boolean key_upgraded;
+ SecurityLevel security_level;
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/KeyOperationWithPurposeAndModesInfo.aidl b/keystore2/aidl/android/security/metrics/KeyOperationWithPurposeAndModesInfo.aidl
new file mode 100644
index 0000000..e3769e1
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/KeyOperationWithPurposeAndModesInfo.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.metrics;
+
+import android.security.metrics.Purpose;
+
+/**
+ * Atom that encapsulates the purpose, padding mode, digest and block mode fields in key operations.
+ * @hide
+ */
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable KeyOperationWithPurposeAndModesInfo {
+ Purpose purpose;
+ int padding_mode_bitmap;
+ int digest_bitmap;
+ int block_mode_bitmap;
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/KeyOrigin.aidl b/keystore2/aidl/android/security/metrics/KeyOrigin.aidl
new file mode 100644
index 0000000..b472bc3
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/KeyOrigin.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.metrics;
+
+/**
+ * KeyOrigin enum as defined in Keystore2KeyCreationWithGeneralInfo of
+ * frameworks/proto_logging/stats/atoms.proto.
+ * @hide
+ */
+@Backing(type="int")
+enum KeyOrigin {
+ /** Unspecified takes 0. Other values are incremented by 1 compared to keymint spec. */
+ ORIGIN_UNSPECIFIED = 0,
+
+ /** Generated in KeyMint. Should not exist outside the TEE. */
+ GENERATED = 1,
+
+ /** Derived inside KeyMint. Likely exists off-device. */
+ DERIVED = 2,
+
+ /** Imported into KeyMint. Existed as cleartext in Android. */
+ IMPORTED = 3,
+
+ /** Previously used for another purpose that is now obsolete. */
+ RESERVED = 4,
+
+ /** Securely imported into KeyMint. */
+ SECURELY_IMPORTED = 5,
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/Keystore2AtomWithOverflow.aidl b/keystore2/aidl/android/security/metrics/Keystore2AtomWithOverflow.aidl
new file mode 100644
index 0000000..f2ac399
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/Keystore2AtomWithOverflow.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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.metrics;
+
+import android.security.metrics.AtomID;
+
+/**
+ * Logs the atom id of the atoms associated with key creation/operation events, that have reached
+ * the maximum storage limit allocated for different atom objects of that atom,
+ * in keystore in-memory store.
+ *
+ * Size of the storage bucket for each atom is limited considering their expected cardinaltity.
+ * This limit may exceed if the dimensions of the atoms take a large number of unexpected
+ * combinations. This atom is used to track such cases.
+ * @hide
+ */
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable Keystore2AtomWithOverflow {
+ AtomID atom_id;
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/KeystoreAtom.aidl b/keystore2/aidl/android/security/metrics/KeystoreAtom.aidl
new file mode 100644
index 0000000..266267a
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/KeystoreAtom.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.metrics;
+
+import android.security.metrics.KeystoreAtomPayload;
+
+/**
+ * Encapsulates a particular atom object of type KeystoreAtomPayload its count. Note that
+ * the field: count is only relevant for the atom types that are stored in the
+ * in-memory metrics store. E.g. count field is not relevant for the atom types such as StorageStats
+ * and RkpPoolStats that are not stored in the metrics store.
+ * @hide
+ */
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable KeystoreAtom {
+ KeystoreAtomPayload payload;
+ int count;
+}
diff --git a/keystore2/aidl/android/security/metrics/KeystoreAtomPayload.aidl b/keystore2/aidl/android/security/metrics/KeystoreAtomPayload.aidl
new file mode 100644
index 0000000..a3e4dd6
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/KeystoreAtomPayload.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.metrics;
+
+import android.security.metrics.KeyCreationWithGeneralInfo;
+import android.security.metrics.KeyCreationWithPurposeAndModesInfo;
+import android.security.metrics.KeyCreationWithAuthInfo;
+import android.security.metrics.KeyOperationWithGeneralInfo;
+import android.security.metrics.KeyOperationWithPurposeAndModesInfo;
+import android.security.metrics.StorageStats;
+import android.security.metrics.Keystore2AtomWithOverflow;
+import android.security.metrics.RkpErrorStats;
+import android.security.metrics.RkpPoolStats;
+import android.security.metrics.CrashStats;
+
+/** @hide */
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+union KeystoreAtomPayload {
+ StorageStats storageStats;
+ RkpPoolStats rkpPoolStats;
+ KeyCreationWithGeneralInfo keyCreationWithGeneralInfo;
+ KeyCreationWithAuthInfo keyCreationWithAuthInfo;
+ KeyCreationWithPurposeAndModesInfo keyCreationWithPurposeAndModesInfo;
+ Keystore2AtomWithOverflow keystore2AtomWithOverflow;
+ KeyOperationWithPurposeAndModesInfo keyOperationWithPurposeAndModesInfo;
+ KeyOperationWithGeneralInfo keyOperationWithGeneralInfo;
+ RkpErrorStats rkpErrorStats;
+ CrashStats crashStats;
+}
\ No newline at end of file
diff --git a/keystore/binder/android/security/keystore/IKeystoreKeyCharacteristicsCallback.aidl b/keystore2/aidl/android/security/metrics/Outcome.aidl
similarity index 61%
copy from keystore/binder/android/security/keystore/IKeystoreKeyCharacteristicsCallback.aidl
copy to keystore2/aidl/android/security/metrics/Outcome.aidl
index e1f0ffe..006548b 100644
--- a/keystore/binder/android/security/keystore/IKeystoreKeyCharacteristicsCallback.aidl
+++ b/keystore2/aidl/android/security/metrics/Outcome.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
+/*
+ * 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.
@@ -14,14 +14,19 @@
* limitations under the License.
*/
-package android.security.keystore;
-
-import android.security.keystore.KeystoreResponse;
-import android.security.keymaster.KeyCharacteristics;
+package android.security.metrics;
/**
+ * Outcome enum as defined in Keystore2KeyOperationWithGeneralInfo of
+ * frameworks/proto_logging/stats/atoms.proto.
* @hide
*/
-oneway interface IKeystoreKeyCharacteristicsCallback {
- void onFinished(in KeystoreResponse response, in KeyCharacteristics charactersistics);
+@Backing(type="int")
+enum Outcome {
+ OUTCOME_UNSPECIFIED = 0,
+ DROPPED = 1,
+ SUCCESS = 2,
+ ABORT = 3,
+ PRUNED = 4,
+ ERROR = 5,
}
\ No newline at end of file
diff --git a/keystore/binder/android/security/keystore/IKeystoreExportKeyCallback.aidl b/keystore2/aidl/android/security/metrics/PoolStatus.aidl
similarity index 64%
rename from keystore/binder/android/security/keystore/IKeystoreExportKeyCallback.aidl
rename to keystore2/aidl/android/security/metrics/PoolStatus.aidl
index e42e927..3530163 100644
--- a/keystore/binder/android/security/keystore/IKeystoreExportKeyCallback.aidl
+++ b/keystore2/aidl/android/security/metrics/PoolStatus.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
+/*
+ * 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.
@@ -14,14 +14,17 @@
* limitations under the License.
*/
-package android.security.keystore;
-
-import android.security.keystore.KeystoreResponse;
-import android.security.keymaster.ExportResult;
+package android.security.metrics;
/**
+ * Status of the remotely provisioned keys, as defined in RkpPoolStats of
+ * frameworks/proto_logging/stats/atoms.proto.
* @hide
*/
-oneway interface IKeystoreExportKeyCallback {
- void onFinished(in ExportResult result);
+@Backing(type="int")
+enum PoolStatus {
+ EXPIRING = 1,
+ UNASSIGNED = 2,
+ ATTESTED = 3,
+ TOTAL = 4,
}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/Purpose.aidl b/keystore2/aidl/android/security/metrics/Purpose.aidl
new file mode 100644
index 0000000..f003cea
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/Purpose.aidl
@@ -0,0 +1,54 @@
+/*
+ * 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.metrics;
+
+/**
+ * Purpose enum as defined in Keystore2KeyOperationWithPurposeAndModesInfo of
+ * frameworks/proto_logging/stats/atoms.proto.
+ * @hide
+ */
+@Backing(type="int")
+enum Purpose {
+ /** Unspecified takes 0. Other values are incremented by 1 compared to keymint spec. */
+ KEY_PURPOSE_UNSPECIFIED = 0,
+
+ /** Usable with RSA, 3DES and AES keys. */
+ ENCRYPT = 1,
+
+ /** Usable with RSA, 3DES and AES keys. */
+ DECRYPT = 2,
+
+ /** Usable with RSA, EC and HMAC keys. */
+ SIGN = 3,
+
+ /** Usable with RSA, EC and HMAC keys. */
+ VERIFY = 4,
+
+ /** 4 is reserved */
+
+ /** Usable with RSA keys. */
+ WRAP_KEY = 6,
+
+ /** Key Agreement, usable with EC keys. */
+ AGREE_KEY = 7,
+
+ /**
+ * Usable as an attestation signing key. Keys with this purpose must not have any other
+ * purpose.
+ */
+ ATTEST_KEY = 8,
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/RkpError.aidl b/keystore2/aidl/android/security/metrics/RkpError.aidl
new file mode 100644
index 0000000..c33703d
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/RkpError.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.metrics;
+
+/**
+ * KeyOrigin enum as defined in RkpErrorStats of frameworks/proto_logging/stats/atoms.proto.
+ * @hide
+ */
+@Backing(type="int")
+enum RkpError {
+ RKP_ERROR_UNSPECIFIED = 0,
+
+ /** The key pool is out of keys. */
+ OUT_OF_KEYS = 1,
+
+ /** Falling back to factory provisioned keys during hybrid mode. */
+ FALL_BACK_DURING_HYBRID = 2,
+}
\ No newline at end of file
diff --git a/keystore/binder/android/security/keystore/IKeystoreKeyCharacteristicsCallback.aidl b/keystore2/aidl/android/security/metrics/RkpErrorStats.aidl
similarity index 62%
rename from keystore/binder/android/security/keystore/IKeystoreKeyCharacteristicsCallback.aidl
rename to keystore2/aidl/android/security/metrics/RkpErrorStats.aidl
index e1f0ffe..616d129 100644
--- a/keystore/binder/android/security/keystore/IKeystoreKeyCharacteristicsCallback.aidl
+++ b/keystore2/aidl/android/security/metrics/RkpErrorStats.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
+/*
+ * 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.
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package android.security.keystore;
+package android.security.metrics;
-import android.security.keystore.KeystoreResponse;
-import android.security.keymaster.KeyCharacteristics;
-
+import android.security.metrics.RkpError;
/**
+ * Atom that encapsulates error information in remote key provisioning events.
* @hide
*/
-oneway interface IKeystoreKeyCharacteristicsCallback {
- void onFinished(in KeystoreResponse response, in KeyCharacteristics charactersistics);
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable RkpErrorStats {
+ RkpError rkpError;
}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/RkpPoolStats.aidl b/keystore2/aidl/android/security/metrics/RkpPoolStats.aidl
new file mode 100644
index 0000000..016b6ff
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/RkpPoolStats.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.metrics;
+
+import android.security.metrics.SecurityLevel;
+
+/**
+ * Count of keys in the attestation key pool related to Remote Key Provisioning (RKP).
+ * @hide
+ */
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable RkpPoolStats {
+ SecurityLevel security_level;
+ int expiring;
+ int unassigned;
+ int attested;
+ int total;
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/SecurityLevel.aidl b/keystore2/aidl/android/security/metrics/SecurityLevel.aidl
new file mode 100644
index 0000000..f627be2
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/SecurityLevel.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.metrics;
+
+/**
+ * SecurityLevel enum as defined in stats/enums/system/security/keystore2/enums.proto.
+ * @hide
+ */
+@Backing(type="int")
+enum SecurityLevel {
+ /** Unspecified takes 0. Other values are incremented by 1 compared to keymint spec. */
+ SECURITY_LEVEL_UNSPECIFIED = 0,
+ SECURITY_LEVEL_SOFTWARE = 1,
+ SECURITY_LEVEL_TRUSTED_ENVIRONMENT = 2,
+ SECURITY_LEVEL_STRONGBOX = 3,
+ SECURITY_LEVEL_KEYSTORE = 4,
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/Storage.aidl b/keystore2/aidl/android/security/metrics/Storage.aidl
new file mode 100644
index 0000000..1ba6e1f
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/Storage.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.metrics;
+
+/**
+ * Storage enum as defined in Keystore2StorageStats of frameworks/proto_logging/stats/atoms.proto.
+ * @hide
+ */
+@Backing(type="int")
+enum Storage {
+ STORAGE_UNSPECIFIED = 0,
+ KEY_ENTRY = 1,
+ KEY_ENTRY_ID_INDEX = 2,
+ KEY_ENTRY_DOMAIN_NAMESPACE_INDEX = 3,
+ BLOB_ENTRY = 4,
+ BLOB_ENTRY_KEY_ENTRY_ID_INDEX = 5,
+ KEY_PARAMETER = 6,
+ KEY_PARAMETER_KEY_ENTRY_ID_INDEX = 7,
+ KEY_METADATA = 8,
+ KEY_METADATA_KEY_ENTRY_ID_INDEX = 9,
+ GRANT = 10,
+ AUTH_TOKEN = 11,
+ BLOB_METADATA = 12,
+ BLOB_METADATA_BLOB_ENTRY_ID_INDEX =13,
+ METADATA = 14,
+ DATABASE = 15,
+ LEGACY_STORAGE = 16,
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/StorageStats.aidl b/keystore2/aidl/android/security/metrics/StorageStats.aidl
new file mode 100644
index 0000000..6822e86
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/StorageStats.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.metrics;
+
+import android.security.metrics.Storage;
+
+/**
+ * Atom that encapsulates a set of general information in key creation events.
+ * @hide
+ */
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable StorageStats {
+ Storage storage_type;
+ int size;
+ int unused_size;
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl b/keystore2/aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl
index 0d4c30f..ecdc790 100644
--- a/keystore2/aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl
+++ b/keystore2/aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl
@@ -16,9 +16,11 @@
package android.security.remoteprovisioning;
+import android.hardware.security.keymint.DeviceInfo;
import android.hardware.security.keymint.ProtectedData;
import android.hardware.security.keymint.SecurityLevel;
import android.security.remoteprovisioning.AttestationPoolStatus;
+import android.security.remoteprovisioning.ImplInfo;
/**
* `IRemoteProvisioning` is the interface provided to use the remote provisioning functionality
@@ -90,7 +92,7 @@
* request.
*/
byte[] generateCsr(in boolean testMode, in int numCsr, in byte[] eek, in byte[] challenge,
- in SecurityLevel secLevel, out ProtectedData protectedData);
+ in SecurityLevel secLevel, out ProtectedData protectedData, out DeviceInfo deviceInfo);
/**
* This method provides a way for the returned attestation certificate chains to be provisioned
@@ -126,11 +128,21 @@
void generateKeyPair(in boolean is_test_mode, in SecurityLevel secLevel);
/**
- * This method returns the SecurityLevels of whichever instances of
+ * This method returns implementation information for whichever instances of
* IRemotelyProvisionedComponent are running on the device. The RemoteProvisioner app needs to
- * know which KM instances it should be generating and managing attestation keys for.
+ * know which KM instances it should be generating and managing attestation keys for, and which
+ * EC curves are supported in those instances.
*
- * @return The array of security levels.
+ * @return The array of ImplInfo parcelables.
*/
- SecurityLevel[] getSecurityLevels();
+ ImplInfo[] getImplementationInfo();
+
+ /**
+ * This method deletes all remotely provisioned attestation keys in the database, regardless
+ * of what state in their life cycle they are in. This is primarily useful to facilitate
+ * testing.
+ *
+ * @return Number of keys deleted
+ */
+ long deleteAllKeys();
}
diff --git a/keystore2/aidl/android/security/remoteprovisioning/ImplInfo.aidl b/keystore2/aidl/android/security/remoteprovisioning/ImplInfo.aidl
new file mode 100644
index 0000000..9baeb24
--- /dev/null
+++ b/keystore2/aidl/android/security/remoteprovisioning/ImplInfo.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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.remoteprovisioning;
+
+import android.hardware.security.keymint.SecurityLevel;
+
+/**
+ * This parcelable provides information about the underlying IRemotelyProvisionedComponent
+ * implementation.
+ * @hide
+ */
+parcelable ImplInfo {
+ /**
+ * The security level of the underlying implementation: TEE or StrongBox.
+ */
+ SecurityLevel secLevel;
+ /**
+ * An integer denoting which EC curve is supported in the underlying implementation. The current
+ * options are either P256 or 25519, with values defined in
+ * hardware/interfaces/security/keymint/aidl/.../RpcHardwareInfo.aidl
+ */
+ int supportedCurve;
+}
diff --git a/keystore2/aidl/android/security/usermanager/IKeystoreUserManager.aidl b/keystore2/aidl/android/security/usermanager/IKeystoreUserManager.aidl
deleted file mode 100644
index 83edb1a..0000000
--- a/keystore2/aidl/android/security/usermanager/IKeystoreUserManager.aidl
+++ /dev/null
@@ -1,78 +0,0 @@
-// 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.usermanager;
-
-import android.system.keystore2.Domain;
-
-// TODO: mark the interface with @SensitiveData when the annotation is ready (b/176110256).
-
-/**
- * IKeystoreUserManager interface exposes the methods for adding/removing users and changing the
- * user's password.
- * @hide
- */
-interface IKeystoreUserManager {
-
- /**
- * Allows LockSettingsService to inform keystore about adding a new user.
- * Callers require 'AddUser' permission.
- * ## Error conditions:
- * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'AddUser' permission.
- * `ResponseCode::SYSTEM_ERROR` - if failed to delete the keys of an existing user with the same
- * user id.
- *
- * @param userId - Android user id
- * @hide
- */
- void onUserAdded(in int userId);
-
- /**
- * Allows LockSettingsService to inform keystore about removing a user.
- * Callers require 'RemoveUser' permission.
- * ## Error conditions:
- * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'RemoveUser' permission.
- * `ResponseCode::SYSTEM_ERROR` - if failed to delete the keys of the user being deleted.
- *
- * @param userId - Android user id
- * @hide
- */
- void onUserRemoved(in int userId);
-
- /**
- * Allows LockSettingsService to inform keystore about password change of a user.
- * Callers require 'ChangePassword' permission.
- * ## Error conditions:
- * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'ChangePassword'
- * permission.
- * `ResponseCode::SYSTEM_ERROR` - if failed to delete the super encrypted keys of the user.
- * `ResponseCode::Locked' - if the keystore is locked for the given user.
- *
- * @param userId - Android user id
- * @param password - a secret derived from the synthetic password of the user
- * @hide
- */
- void onUserPasswordChanged(in int userId, in @nullable byte[] password);
-
- /**
- * This function deletes all keys within a namespace. It mainly gets called when an app gets
- * removed and all resources of this app need to be cleaned up.
- *
- * @param domain - One of Domain.APP or Domain.SELINUX.
- * @param nspace - The UID of the app that is to be cleared if domain is Domain.APP or
- * the SEPolicy namespace if domain is Domain.SELINUX.
- * @hide
- */
- void clearNamespace(Domain domain, long nspace);
-}
diff --git a/keystore2/android.system.keystore2-service.xml b/keystore2/android.system.keystore2-service.xml
new file mode 100644
index 0000000..6b8d0cb
--- /dev/null
+++ b/keystore2/android.system.keystore2-service.xml
@@ -0,0 +1,9 @@
+<manifest version="1.0" type="framework">
+ <hal format="aidl">
+ <name>android.system.keystore2</name>
+ <interface>
+ <name>IKeystoreService</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/keystore2/apc_compat/Android.bp b/keystore2/apc_compat/Android.bp
index 9519c8e..df7521e 100644
--- a/keystore2/apc_compat/Android.bp
+++ b/keystore2/apc_compat/Android.bp
@@ -41,12 +41,12 @@
source_stem: "bindings",
bindgen_flags: [
- "--whitelist-function=tryGetUserConfirmationService",
- "--whitelist-function=promptUserConfirmation",
- "--whitelist-function=abortUserConfirmation",
- "--whitelist-function=closeUserConfirmationService",
- "--whitelist-var=INVALID_SERVICE_HANDLE",
- "--whitelist-var=APC_COMPAT_.*",
+ "--allowlist-function=tryGetUserConfirmationService",
+ "--allowlist-function=promptUserConfirmation",
+ "--allowlist-function=abortUserConfirmation",
+ "--allowlist-function=closeUserConfirmationService",
+ "--allowlist-var=INVALID_SERVICE_HANDLE",
+ "--allowlist-var=APC_COMPAT_.*",
],
}
@@ -63,3 +63,13 @@
"libkeystore2_apc_compat",
],
}
+
+rust_test {
+ name: "libkeystore2_apc_compat_bindgen_test",
+ srcs: [":libkeystore2_apc_compat_bindgen"],
+ crate_name: "keystore2_apc_compat_bindgen_test",
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ clippy_lints: "none",
+ lints: "none",
+}
diff --git a/keystore2/keystore2.rc b/keystore2/keystore2.rc
index c5fc72a..82bf3b8 100644
--- a/keystore2/keystore2.rc
+++ b/keystore2/keystore2.rc
@@ -6,15 +6,8 @@
#
# See system/core/init/README.md for information on the init.rc language.
-# Start Keystore 2 conditionally
-# TODO b/171563717 Remove when Keystore 2 migration is complete.
-on nonencrypted && property:persist.android.security.keystore2.enable=true
- enable keystore2
-
service keystore2 /system/bin/keystore2 /data/misc/keystore
- class main
+ class early_hal
user keystore
group keystore readproc log
writepid /dev/cpuset/foreground/tasks
- # TODO b/171563717 Remove when Keystore 2 migration is complete.
- disabled
diff --git a/keystore2/legacykeystore/Android.bp b/keystore2/legacykeystore/Android.bp
new file mode 100644
index 0000000..da6aa8a
--- /dev/null
+++ b/keystore2/legacykeystore/Android.bp
@@ -0,0 +1,59 @@
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "system_security_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["system_security_license"],
+}
+
+rust_library {
+ name: "liblegacykeystore-rust",
+ crate_name: "legacykeystore",
+ srcs: [
+ "lib.rs",
+ ],
+ rustlibs: [
+ "android.security.legacykeystore-rust",
+ "libanyhow",
+ "libbinder_rs",
+ "libkeystore2",
+ "liblog_rust",
+ "librusqlite",
+ "librustutils",
+ "libthiserror",
+ ],
+}
+
+rust_test {
+ name: "legacykeystore_test",
+ crate_name: "legacykeystore",
+ srcs: ["lib.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ rustlibs: [
+ "android.security.legacykeystore-rust",
+ "libanyhow",
+ "libbinder_rs",
+ "libkeystore2",
+ "libkeystore2_test_utils",
+ "liblog_rust",
+ "librusqlite",
+ "librustutils",
+ "libthiserror",
+ ],
+}
diff --git a/keystore2/legacykeystore/TEST_MAPPING b/keystore2/legacykeystore/TEST_MAPPING
new file mode 100644
index 0000000..37d1439
--- /dev/null
+++ b/keystore2/legacykeystore/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "legacykeystore_test"
+ }
+ ]
+}
diff --git a/keystore2/legacykeystore/lib.rs b/keystore2/legacykeystore/lib.rs
new file mode 100644
index 0000000..da60297
--- /dev/null
+++ b/keystore2/legacykeystore/lib.rs
@@ -0,0 +1,724 @@
+// 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.
+
+//! Implements the android.security.legacykeystore interface.
+
+use android_security_legacykeystore::aidl::android::security::legacykeystore::{
+ ILegacyKeystore::BnLegacyKeystore, ILegacyKeystore::ILegacyKeystore,
+ ILegacyKeystore::ERROR_ENTRY_NOT_FOUND, ILegacyKeystore::ERROR_PERMISSION_DENIED,
+ ILegacyKeystore::ERROR_SYSTEM_ERROR, ILegacyKeystore::UID_SELF,
+};
+use android_security_legacykeystore::binder::{
+ BinderFeatures, ExceptionCode, Result as BinderResult, Status as BinderStatus, Strong,
+ ThreadState,
+};
+use anyhow::{Context, Result};
+use keystore2::{
+ async_task::AsyncTask, legacy_blob::LegacyBlobLoader, maintenance::DeleteListener,
+ maintenance::Domain, utils::watchdog as wd,
+};
+use rusqlite::{
+ params, Connection, OptionalExtension, Transaction, TransactionBehavior, NO_PARAMS,
+};
+use std::sync::Arc;
+use std::{
+ collections::HashSet,
+ path::{Path, PathBuf},
+};
+
+struct DB {
+ conn: Connection,
+}
+
+impl DB {
+ fn new(db_file: &Path) -> Result<Self> {
+ let mut db = Self {
+ conn: Connection::open(db_file).context("Failed to initialize SQLite connection.")?,
+ };
+
+ db.init_tables().context("Trying to initialize legacy keystore db.")?;
+ Ok(db)
+ }
+
+ fn with_transaction<T, F>(&mut self, behavior: TransactionBehavior, f: F) -> Result<T>
+ where
+ F: Fn(&Transaction) -> Result<T>,
+ {
+ loop {
+ match self
+ .conn
+ .transaction_with_behavior(behavior)
+ .context("In with_transaction.")
+ .and_then(|tx| f(&tx).map(|result| (result, tx)))
+ .and_then(|(result, tx)| {
+ tx.commit().context("In with_transaction: Failed to commit transaction.")?;
+ Ok(result)
+ }) {
+ Ok(result) => break Ok(result),
+ Err(e) => {
+ if Self::is_locked_error(&e) {
+ std::thread::sleep(std::time::Duration::from_micros(500));
+ continue;
+ } else {
+ return Err(e).context("In with_transaction.");
+ }
+ }
+ }
+ }
+ }
+
+ fn is_locked_error(e: &anyhow::Error) -> bool {
+ matches!(
+ e.root_cause().downcast_ref::<rusqlite::ffi::Error>(),
+ Some(rusqlite::ffi::Error { code: rusqlite::ErrorCode::DatabaseBusy, .. })
+ | Some(rusqlite::ffi::Error { code: rusqlite::ErrorCode::DatabaseLocked, .. })
+ )
+ }
+
+ fn init_tables(&mut self) -> Result<()> {
+ self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ tx.execute(
+ "CREATE TABLE IF NOT EXISTS profiles (
+ owner INTEGER,
+ alias BLOB,
+ profile BLOB,
+ UNIQUE(owner, alias));",
+ NO_PARAMS,
+ )
+ .context("Failed to initialize \"profiles\" table.")?;
+ Ok(())
+ })
+ }
+
+ fn list(&mut self, caller_uid: u32) -> Result<Vec<String>> {
+ self.with_transaction(TransactionBehavior::Deferred, |tx| {
+ let mut stmt = tx
+ .prepare("SELECT alias FROM profiles WHERE owner = ? ORDER BY alias ASC;")
+ .context("In list: Failed to prepare statement.")?;
+
+ let aliases = stmt
+ .query_map(params![caller_uid], |row| row.get(0))?
+ .collect::<rusqlite::Result<Vec<String>>>()
+ .context("In list: query_map failed.");
+ aliases
+ })
+ }
+
+ fn put(&mut self, caller_uid: u32, alias: &str, entry: &[u8]) -> Result<()> {
+ self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ tx.execute(
+ "INSERT OR REPLACE INTO profiles (owner, alias, profile) values (?, ?, ?)",
+ params![caller_uid, alias, entry,],
+ )
+ .context("In put: Failed to insert or replace.")?;
+ Ok(())
+ })
+ }
+
+ fn get(&mut self, caller_uid: u32, alias: &str) -> Result<Option<Vec<u8>>> {
+ self.with_transaction(TransactionBehavior::Deferred, |tx| {
+ tx.query_row(
+ "SELECT profile FROM profiles WHERE owner = ? AND alias = ?;",
+ params![caller_uid, alias],
+ |row| row.get(0),
+ )
+ .optional()
+ .context("In get: failed loading entry.")
+ })
+ }
+
+ fn remove(&mut self, caller_uid: u32, alias: &str) -> Result<bool> {
+ let removed = self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ tx.execute(
+ "DELETE FROM profiles WHERE owner = ? AND alias = ?;",
+ params![caller_uid, alias],
+ )
+ .context("In remove: Failed to delete row.")
+ })?;
+ Ok(removed == 1)
+ }
+
+ fn remove_uid(&mut self, uid: u32) -> Result<()> {
+ self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ tx.execute("DELETE FROM profiles WHERE owner = ?;", params![uid])
+ .context("In remove_uid: Failed to delete.")
+ })?;
+ Ok(())
+ }
+
+ fn remove_user(&mut self, user_id: u32) -> Result<()> {
+ self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ tx.execute(
+ "DELETE FROM profiles WHERE cast ( ( owner/? ) as int) = ?;",
+ params![rustutils::users::AID_USER_OFFSET, user_id],
+ )
+ .context("In remove_uid: Failed to delete.")
+ })?;
+ Ok(())
+ }
+}
+
+/// This is the main LegacyKeystore error type, it wraps binder exceptions and the
+/// LegacyKeystore errors.
+#[derive(Debug, thiserror::Error, PartialEq)]
+pub enum Error {
+ /// Wraps a LegacyKeystore error code.
+ #[error("Error::Error({0:?})")]
+ Error(i32),
+ /// Wraps a Binder exception code other than a service specific exception.
+ #[error("Binder exception code {0:?}, {1:?}")]
+ Binder(ExceptionCode, i32),
+}
+
+impl Error {
+ /// Short hand for `Error::Error(ERROR_SYSTEM_ERROR)`
+ pub fn sys() -> Self {
+ Error::Error(ERROR_SYSTEM_ERROR)
+ }
+
+ /// Short hand for `Error::Error(ERROR_ENTRY_NOT_FOUND)`
+ pub fn not_found() -> Self {
+ Error::Error(ERROR_ENTRY_NOT_FOUND)
+ }
+
+ /// Short hand for `Error::Error(ERROR_PERMISSION_DENIED)`
+ pub fn perm() -> Self {
+ Error::Error(ERROR_PERMISSION_DENIED)
+ }
+}
+
+/// This function should be used by legacykeystore service calls to translate error conditions
+/// into service specific exceptions.
+///
+/// All error conditions get logged by this function, except for ERROR_ENTRY_NOT_FOUND error.
+///
+/// `Error::Error(x)` variants get mapped onto a service specific error code of `x`.
+///
+/// All non `Error` error conditions get mapped onto `ERROR_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).
+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| {
+ let root_cause = e.root_cause();
+ let (rc, log_error) = match root_cause.downcast_ref::<Error>() {
+ // Make the entry not found errors silent.
+ Some(Error::Error(ERROR_ENTRY_NOT_FOUND)) => (ERROR_ENTRY_NOT_FOUND, false),
+ Some(Error::Error(e)) => (*e, true),
+ Some(Error::Binder(_, _)) | None => (ERROR_SYSTEM_ERROR, true),
+ };
+ if log_error {
+ log::error!("{:?}", e);
+ }
+ Err(BinderStatus::new_service_specific_error(rc, None))
+ },
+ handle_ok,
+ )
+}
+
+struct LegacyKeystoreDeleteListener {
+ legacy_keystore: Arc<LegacyKeystore>,
+}
+
+impl DeleteListener for LegacyKeystoreDeleteListener {
+ fn delete_namespace(&self, domain: Domain, namespace: i64) -> Result<()> {
+ self.legacy_keystore.delete_namespace(domain, namespace)
+ }
+ fn delete_user(&self, user_id: u32) -> Result<()> {
+ self.legacy_keystore.delete_user(user_id)
+ }
+}
+
+/// Implements ILegacyKeystore AIDL interface.
+pub struct LegacyKeystore {
+ db_path: PathBuf,
+ async_task: AsyncTask,
+}
+
+struct AsyncState {
+ recently_imported: HashSet<(u32, String)>,
+ legacy_loader: LegacyBlobLoader,
+ db_path: PathBuf,
+}
+
+impl LegacyKeystore {
+ /// Note: The filename was chosen before the purpose of this module was extended.
+ /// It is kept for backward compatibility with early adopters.
+ const LEGACY_KEYSTORE_FILE_NAME: &'static str = "vpnprofilestore.sqlite";
+
+ const WIFI_NAMESPACE: i64 = 102;
+ const AID_WIFI: u32 = 1010;
+
+ /// Creates a new LegacyKeystore instance.
+ pub fn new_native_binder(
+ path: &Path,
+ ) -> (Box<dyn DeleteListener + Send + Sync + 'static>, Strong<dyn ILegacyKeystore>) {
+ let mut db_path = path.to_path_buf();
+ db_path.push(Self::LEGACY_KEYSTORE_FILE_NAME);
+
+ let legacy_keystore = Arc::new(Self { db_path, async_task: Default::default() });
+ legacy_keystore.init_shelf(path);
+ let service = LegacyKeystoreService { legacy_keystore: legacy_keystore.clone() };
+ (
+ Box::new(LegacyKeystoreDeleteListener { legacy_keystore }),
+ BnLegacyKeystore::new_binder(service, BinderFeatures::default()),
+ )
+ }
+
+ fn open_db(&self) -> Result<DB> {
+ DB::new(&self.db_path).context("In open_db: Failed to open db.")
+ }
+
+ fn get_effective_uid(uid: i32) -> Result<u32> {
+ const AID_SYSTEM: u32 = 1000;
+ let calling_uid = ThreadState::get_calling_uid();
+ let uid = uid as u32;
+
+ if uid == UID_SELF as u32 || uid == calling_uid {
+ Ok(calling_uid)
+ } else if calling_uid == AID_SYSTEM && uid == Self::AID_WIFI {
+ // The only exception for legacy reasons is allowing SYSTEM to access
+ // the WIFI namespace.
+ // IMPORTANT: If you attempt to add more exceptions, it means you are adding
+ // more callers to this deprecated feature. DON'T!
+ Ok(Self::AID_WIFI)
+ } else {
+ Err(Error::perm()).with_context(|| {
+ format!("In get_effective_uid: caller: {}, requested uid: {}.", calling_uid, uid)
+ })
+ }
+ }
+
+ fn get(&self, alias: &str, uid: i32) -> Result<Vec<u8>> {
+ let mut db = self.open_db().context("In get.")?;
+ let uid = Self::get_effective_uid(uid).context("In get.")?;
+
+ if let Some(entry) = db.get(uid, alias).context("In get: Trying to load entry from DB.")? {
+ return Ok(entry);
+ }
+ if self.get_legacy(uid, alias).context("In get: Trying to migrate legacy blob.")? {
+ // If we were able to migrate a legacy blob try again.
+ if let Some(entry) =
+ db.get(uid, alias).context("In get: Trying to load entry from DB.")?
+ {
+ return Ok(entry);
+ }
+ }
+ Err(Error::not_found()).context("In get: No such entry.")
+ }
+
+ fn put(&self, alias: &str, uid: i32, entry: &[u8]) -> Result<()> {
+ let uid = Self::get_effective_uid(uid).context("In put.")?;
+ // In order to make sure that we don't have stale legacy entries, make sure they are
+ // migrated before replacing them.
+ let _ = self.get_legacy(uid, alias);
+ let mut db = self.open_db().context("In put.")?;
+ db.put(uid, alias, entry).context("In put: Trying to insert entry into DB.")
+ }
+
+ fn remove(&self, alias: &str, uid: i32) -> Result<()> {
+ let uid = Self::get_effective_uid(uid).context("In remove.")?;
+ let mut db = self.open_db().context("In remove.")?;
+ // In order to make sure that we don't have stale legacy entries, make sure they are
+ // migrated before removing them.
+ let _ = self.get_legacy(uid, alias);
+ let removed =
+ db.remove(uid, alias).context("In remove: Trying to remove entry from DB.")?;
+ if removed {
+ Ok(())
+ } else {
+ Err(Error::not_found()).context("In remove: No such entry.")
+ }
+ }
+
+ fn delete_namespace(&self, domain: Domain, namespace: i64) -> Result<()> {
+ let uid = match domain {
+ Domain::APP => namespace as u32,
+ Domain::SELINUX => {
+ if namespace == Self::WIFI_NAMESPACE {
+ // Namespace WIFI gets mapped to AID_WIFI.
+ Self::AID_WIFI
+ } else {
+ // Nothing to do for any other namespace.
+ return Ok(());
+ }
+ }
+ _ => return Ok(()),
+ };
+
+ if let Err(e) = self.bulk_delete_uid(uid) {
+ log::warn!("In LegacyKeystore::delete_namespace: {:?}", e);
+ }
+ let mut db = self.open_db().context("In LegacyKeystore::delete_namespace.")?;
+ db.remove_uid(uid).context("In LegacyKeystore::delete_namespace.")
+ }
+
+ fn delete_user(&self, user_id: u32) -> Result<()> {
+ if let Err(e) = self.bulk_delete_user(user_id) {
+ log::warn!("In LegacyKeystore::delete_user: {:?}", e);
+ }
+ let mut db = self.open_db().context("In LegacyKeystore::delete_user.")?;
+ db.remove_user(user_id).context("In LegacyKeystore::delete_user.")
+ }
+
+ fn list(&self, prefix: &str, uid: i32) -> Result<Vec<String>> {
+ let mut db = self.open_db().context("In list.")?;
+ let uid = Self::get_effective_uid(uid).context("In list.")?;
+ let mut result = self.list_legacy(uid).context("In list.")?;
+ result.append(&mut db.list(uid).context("In list: Trying to get list of entries.")?);
+ result = result.into_iter().filter(|s| s.starts_with(prefix)).collect();
+ result.sort_unstable();
+ result.dedup();
+ Ok(result)
+ }
+
+ fn init_shelf(&self, path: &Path) {
+ let mut db_path = path.to_path_buf();
+ self.async_task.queue_hi(move |shelf| {
+ let legacy_loader = LegacyBlobLoader::new(&db_path);
+ db_path.push(Self::LEGACY_KEYSTORE_FILE_NAME);
+
+ shelf.put(AsyncState { legacy_loader, db_path, recently_imported: Default::default() });
+ })
+ }
+
+ fn do_serialized<F, T: Send + 'static>(&self, f: F) -> Result<T>
+ where
+ F: FnOnce(&mut AsyncState) -> Result<T> + Send + 'static,
+ {
+ let (sender, receiver) = std::sync::mpsc::channel::<Result<T>>();
+ self.async_task.queue_hi(move |shelf| {
+ let state = shelf.get_downcast_mut::<AsyncState>().expect("Failed to get shelf.");
+ sender.send(f(state)).expect("Failed to send result.");
+ });
+ receiver.recv().context("In do_serialized: Failed to receive result.")?
+ }
+
+ fn list_legacy(&self, uid: u32) -> Result<Vec<String>> {
+ self.do_serialized(move |state| {
+ state
+ .legacy_loader
+ .list_legacy_keystore_entries_for_uid(uid)
+ .context("Trying to list legacy keystore entries.")
+ })
+ .context("In list_legacy.")
+ }
+
+ fn get_legacy(&self, uid: u32, alias: &str) -> Result<bool> {
+ let alias = alias.to_string();
+ self.do_serialized(move |state| {
+ if state.recently_imported.contains(&(uid, alias.clone())) {
+ return Ok(true);
+ }
+ let mut db = DB::new(&state.db_path).context("In open_db: Failed to open db.")?;
+ let migrated =
+ Self::migrate_one_legacy_entry(uid, &alias, &state.legacy_loader, &mut db)
+ .context("Trying to migrate legacy keystore entries.")?;
+ if migrated {
+ state.recently_imported.insert((uid, alias));
+ }
+ Ok(migrated)
+ })
+ .context("In get_legacy.")
+ }
+
+ fn bulk_delete_uid(&self, uid: u32) -> Result<()> {
+ self.do_serialized(move |state| {
+ let entries = state
+ .legacy_loader
+ .list_legacy_keystore_entries_for_uid(uid)
+ .context("In bulk_delete_uid: Trying to list entries.")?;
+ for alias in entries.iter() {
+ if let Err(e) = state.legacy_loader.remove_legacy_keystore_entry(uid, alias) {
+ log::warn!("In bulk_delete_uid: Failed to delete legacy entry. {:?}", e);
+ }
+ }
+ Ok(())
+ })
+ }
+
+ fn bulk_delete_user(&self, user_id: u32) -> Result<()> {
+ self.do_serialized(move |state| {
+ let entries = state
+ .legacy_loader
+ .list_legacy_keystore_entries_for_user(user_id)
+ .context("In bulk_delete_user: Trying to list entries.")?;
+ for (uid, entries) in entries.iter() {
+ for alias in entries.iter() {
+ if let Err(e) = state.legacy_loader.remove_legacy_keystore_entry(*uid, alias) {
+ log::warn!("In bulk_delete_user: Failed to delete legacy entry. {:?}", e);
+ }
+ }
+ }
+ Ok(())
+ })
+ }
+
+ fn migrate_one_legacy_entry(
+ uid: u32,
+ alias: &str,
+ legacy_loader: &LegacyBlobLoader,
+ db: &mut DB,
+ ) -> Result<bool> {
+ let blob = legacy_loader
+ .read_legacy_keystore_entry(uid, alias)
+ .context("In migrate_one_legacy_entry: Trying to read legacy keystore entry.")?;
+ if let Some(entry) = blob {
+ db.put(uid, alias, &entry)
+ .context("In migrate_one_legacy_entry: Trying to insert entry into DB.")?;
+ legacy_loader
+ .remove_legacy_keystore_entry(uid, alias)
+ .context("In migrate_one_legacy_entry: Trying to delete legacy keystore entry.")?;
+ Ok(true)
+ } else {
+ Ok(false)
+ }
+ }
+}
+
+struct LegacyKeystoreService {
+ legacy_keystore: Arc<LegacyKeystore>,
+}
+
+impl binder::Interface for LegacyKeystoreService {}
+
+impl ILegacyKeystore for LegacyKeystoreService {
+ fn get(&self, alias: &str, uid: i32) -> BinderResult<Vec<u8>> {
+ let _wp = wd::watch_millis("ILegacyKeystore::get", 500);
+ map_or_log_err(self.legacy_keystore.get(alias, uid), Ok)
+ }
+ fn put(&self, alias: &str, uid: i32, entry: &[u8]) -> BinderResult<()> {
+ let _wp = wd::watch_millis("ILegacyKeystore::put", 500);
+ map_or_log_err(self.legacy_keystore.put(alias, uid, entry), Ok)
+ }
+ fn remove(&self, alias: &str, uid: i32) -> BinderResult<()> {
+ let _wp = wd::watch_millis("ILegacyKeystore::remove", 500);
+ map_or_log_err(self.legacy_keystore.remove(alias, uid), Ok)
+ }
+ fn list(&self, prefix: &str, uid: i32) -> BinderResult<Vec<String>> {
+ let _wp = wd::watch_millis("ILegacyKeystore::list", 500);
+ map_or_log_err(self.legacy_keystore.list(prefix, uid), Ok)
+ }
+}
+
+#[cfg(test)]
+mod db_test {
+ use super::*;
+ use keystore2_test_utils::TempDir;
+ use std::sync::Arc;
+ use std::thread;
+ use std::time::Duration;
+ use std::time::Instant;
+
+ static TEST_ALIAS: &str = "test_alias";
+ static TEST_BLOB1: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
+ static TEST_BLOB2: &[u8] = &[2, 2, 3, 4, 5, 6, 7, 8, 9, 0];
+ static TEST_BLOB3: &[u8] = &[3, 2, 3, 4, 5, 6, 7, 8, 9, 0];
+ static TEST_BLOB4: &[u8] = &[3, 2, 3, 4, 5, 6, 7, 8, 9, 0];
+
+ #[test]
+ fn test_entry_db() {
+ let test_dir = TempDir::new("entrydb_test_").expect("Failed to create temp dir.");
+ let mut db = DB::new(&test_dir.build().push(LegacyKeystore::LEGACY_KEYSTORE_FILE_NAME))
+ .expect("Failed to open database.");
+
+ // Insert three entries for owner 2.
+ db.put(2, "test1", TEST_BLOB1).expect("Failed to insert test1.");
+ db.put(2, "test2", TEST_BLOB2).expect("Failed to insert test2.");
+ db.put(2, "test3", TEST_BLOB3).expect("Failed to insert test3.");
+
+ // Check list returns all inserted aliases.
+ assert_eq!(
+ vec!["test1".to_string(), "test2".to_string(), "test3".to_string(),],
+ db.list(2).expect("Failed to list entries.")
+ );
+
+ // There should be no entries for owner 1.
+ assert_eq!(Vec::<String>::new(), db.list(1).expect("Failed to list entries."));
+
+ // Check the content of the three entries.
+ assert_eq!(Some(TEST_BLOB1), db.get(2, "test1").expect("Failed to get entry.").as_deref());
+ assert_eq!(Some(TEST_BLOB2), db.get(2, "test2").expect("Failed to get entry.").as_deref());
+ assert_eq!(Some(TEST_BLOB3), db.get(2, "test3").expect("Failed to get entry.").as_deref());
+
+ // Remove test2 and check and check that it is no longer retrievable.
+ assert!(db.remove(2, "test2").expect("Failed to remove entry."));
+ assert!(db.get(2, "test2").expect("Failed to get entry.").is_none());
+
+ // test2 should now no longer be in the list.
+ assert_eq!(
+ vec!["test1".to_string(), "test3".to_string(),],
+ db.list(2).expect("Failed to list entries.")
+ );
+
+ // Put on existing alias replaces it.
+ // Verify test1 is TEST_BLOB1.
+ assert_eq!(Some(TEST_BLOB1), db.get(2, "test1").expect("Failed to get entry.").as_deref());
+ db.put(2, "test1", TEST_BLOB4).expect("Failed to replace test1.");
+ // Verify test1 is TEST_BLOB4.
+ assert_eq!(Some(TEST_BLOB4), db.get(2, "test1").expect("Failed to get entry.").as_deref());
+ }
+
+ #[test]
+ fn test_delete_uid() {
+ let test_dir = TempDir::new("test_delete_uid_").expect("Failed to create temp dir.");
+ let mut db = DB::new(&test_dir.build().push(LegacyKeystore::LEGACY_KEYSTORE_FILE_NAME))
+ .expect("Failed to open database.");
+
+ // Insert three entries for owner 2.
+ db.put(2, "test1", TEST_BLOB1).expect("Failed to insert test1.");
+ db.put(2, "test2", TEST_BLOB2).expect("Failed to insert test2.");
+ db.put(3, "test3", TEST_BLOB3).expect("Failed to insert test3.");
+
+ db.remove_uid(2).expect("Failed to remove uid 2");
+
+ assert_eq!(Vec::<String>::new(), db.list(2).expect("Failed to list entries."));
+
+ assert_eq!(vec!["test3".to_string(),], db.list(3).expect("Failed to list entries."));
+ }
+
+ #[test]
+ fn test_delete_user() {
+ let test_dir = TempDir::new("test_delete_user_").expect("Failed to create temp dir.");
+ let mut db = DB::new(&test_dir.build().push(LegacyKeystore::LEGACY_KEYSTORE_FILE_NAME))
+ .expect("Failed to open database.");
+
+ // Insert three entries for owner 2.
+ db.put(2 + 2 * rustutils::users::AID_USER_OFFSET, "test1", TEST_BLOB1)
+ .expect("Failed to insert test1.");
+ db.put(4 + 2 * rustutils::users::AID_USER_OFFSET, "test2", TEST_BLOB2)
+ .expect("Failed to insert test2.");
+ db.put(3, "test3", TEST_BLOB3).expect("Failed to insert test3.");
+
+ db.remove_user(2).expect("Failed to remove user 2");
+
+ assert_eq!(
+ Vec::<String>::new(),
+ db.list(2 + 2 * rustutils::users::AID_USER_OFFSET).expect("Failed to list entries.")
+ );
+
+ assert_eq!(
+ Vec::<String>::new(),
+ db.list(4 + 2 * rustutils::users::AID_USER_OFFSET).expect("Failed to list entries.")
+ );
+
+ assert_eq!(vec!["test3".to_string(),], db.list(3).expect("Failed to list entries."));
+ }
+
+ #[test]
+ fn concurrent_legacy_keystore_entry_test() -> Result<()> {
+ let temp_dir = Arc::new(
+ TempDir::new("concurrent_legacy_keystore_entry_test_")
+ .expect("Failed to create temp dir."),
+ );
+
+ let db_path = temp_dir.build().push(LegacyKeystore::LEGACY_KEYSTORE_FILE_NAME).to_owned();
+
+ let test_begin = Instant::now();
+
+ let mut db = DB::new(&db_path).expect("Failed to open database.");
+ const ENTRY_COUNT: u32 = 5000u32;
+ const ENTRY_DB_COUNT: u32 = 5000u32;
+
+ let mut actual_entry_count = ENTRY_COUNT;
+ // First insert ENTRY_COUNT entries.
+ for count in 0..ENTRY_COUNT {
+ if Instant::now().duration_since(test_begin) >= Duration::from_secs(15) {
+ actual_entry_count = count;
+ break;
+ }
+ let alias = format!("test_alias_{}", count);
+ db.put(1, &alias, TEST_BLOB1).expect("Failed to add entry (1).");
+ }
+
+ // Insert more keys from a different thread and into a different namespace.
+ let db_path1 = db_path.clone();
+ let handle1 = thread::spawn(move || {
+ let mut db = DB::new(&db_path1).expect("Failed to open database.");
+
+ for count in 0..actual_entry_count {
+ if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+ return;
+ }
+ let alias = format!("test_alias_{}", count);
+ db.put(2, &alias, TEST_BLOB2).expect("Failed to add entry (2).");
+ }
+
+ // Then delete them again.
+ for count in 0..actual_entry_count {
+ if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+ return;
+ }
+ let alias = format!("test_alias_{}", count);
+ db.remove(2, &alias).expect("Remove Failed (2).");
+ }
+ });
+
+ // And start deleting the first set of entries.
+ let db_path2 = db_path.clone();
+ let handle2 = thread::spawn(move || {
+ let mut db = DB::new(&db_path2).expect("Failed to open database.");
+
+ for count in 0..actual_entry_count {
+ if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+ return;
+ }
+ let alias = format!("test_alias_{}", count);
+ db.remove(1, &alias).expect("Remove Failed (1)).");
+ }
+ });
+
+ // While a lot of inserting and deleting is going on we have to open database connections
+ // successfully and then insert and delete a specific entry.
+ let db_path3 = db_path.clone();
+ let handle3 = thread::spawn(move || {
+ for _count in 0..ENTRY_DB_COUNT {
+ if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+ return;
+ }
+ let mut db = DB::new(&db_path3).expect("Failed to open database.");
+
+ db.put(3, TEST_ALIAS, TEST_BLOB3).expect("Failed to add entry (3).");
+
+ db.remove(3, TEST_ALIAS).expect("Remove failed (3).");
+ }
+ });
+
+ // While thread 3 is inserting and deleting TEST_ALIAS, we try to get the alias.
+ // This may yield an entry or none, but it must not fail.
+ let handle4 = thread::spawn(move || {
+ for _count in 0..ENTRY_DB_COUNT {
+ if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+ return;
+ }
+ let mut db = DB::new(&db_path).expect("Failed to open database.");
+
+ // This may return Some or None but it must not fail.
+ db.get(3, TEST_ALIAS).expect("Failed to get entry (4).");
+ }
+ });
+
+ handle1.join().expect("Thread 1 panicked.");
+ handle2.join().expect("Thread 2 panicked.");
+ handle3.join().expect("Thread 3 panicked.");
+ handle4.join().expect("Thread 4 panicked.");
+
+ Ok(())
+ }
+}
diff --git a/keystore2/selinux/Android.bp b/keystore2/selinux/Android.bp
index 18063d3..254f95e 100644
--- a/keystore2/selinux/Android.bp
+++ b/keystore2/selinux/Android.bp
@@ -34,6 +34,7 @@
rustlibs: [
"libanyhow",
+ "liblazy_static",
"liblog_rust",
"libselinux_bindgen",
"libthiserror",
@@ -56,8 +57,30 @@
rustlibs: [
"libandroid_logger",
"libanyhow",
+ "liblazy_static",
"liblog_rust",
"libselinux_bindgen",
"libthiserror",
],
}
+
+rust_test {
+ name: "keystore2_selinux_concurrency_test",
+ srcs: [
+ "src/concurrency_test.rs",
+ ],
+ crate_name: "keystore2_selinux_concurrency_test",
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+
+ rustlibs: [
+ "libandroid_logger",
+ "libanyhow",
+ "libkeystore2_selinux",
+ "liblazy_static",
+ "liblog_rust",
+ "libnix",
+ "libnum_cpus",
+ "libthiserror",
+ ],
+}
diff --git a/keystore2/selinux/src/concurrency_test.rs b/keystore2/selinux/src/concurrency_test.rs
new file mode 100644
index 0000000..a5d2df2
--- /dev/null
+++ b/keystore2/selinux/src/concurrency_test.rs
@@ -0,0 +1,190 @@
+// 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.
+
+use keystore2_selinux::{check_access, Context};
+use nix::sched::sched_setaffinity;
+use nix::sched::CpuSet;
+use nix::unistd::getpid;
+use std::thread;
+use std::{
+ sync::{atomic::AtomicU8, atomic::Ordering, Arc},
+ time::{Duration, Instant},
+};
+
+#[derive(Clone, Copy)]
+struct CatCount(u8, u8, u8, u8);
+
+impl CatCount {
+ fn next(&mut self) -> CatCount {
+ let result = *self;
+ if self.3 == 255 {
+ if self.2 == 254 {
+ if self.1 == 253 {
+ if self.0 == 252 {
+ self.0 = 255;
+ }
+ self.0 += 1;
+ self.1 = self.0;
+ }
+ self.1 += 1;
+ self.2 = self.1;
+ }
+ self.2 += 1;
+ self.3 = self.2;
+ }
+ self.3 += 1;
+ result
+ }
+
+ fn make_string(&self) -> String {
+ format!("c{},c{},c{},c{}", self.0, self.1, self.2, self.3)
+ }
+}
+
+impl Default for CatCount {
+ fn default() -> Self {
+ Self(0, 1, 2, 3)
+ }
+}
+
+/// This test calls selinux_check_access concurrently causing access vector cache misses
+/// in libselinux avc. The test then checks if any of the threads fails to report back
+/// after a burst of access checks. The purpose of the test is to draw out a specific
+/// access vector cache corruption that sends a calling thread into an infinite loop.
+/// This was observed when keystore2 used libselinux concurrently in a non thread safe
+/// way. See b/184006658.
+#[test]
+fn test_concurrent_check_access() {
+ android_logger::init_once(
+ android_logger::Config::default()
+ .with_tag("keystore2_selinux_concurrency_test")
+ .with_min_level(log::Level::Debug),
+ );
+
+ let cpus = num_cpus::get();
+ let turnpike = Arc::new(AtomicU8::new(0));
+ let complete_count = Arc::new(AtomicU8::new(0));
+ let mut threads: Vec<thread::JoinHandle<()>> = Vec::new();
+
+ for i in 0..cpus {
+ log::info!("Spawning thread {}", i);
+ let turnpike_clone = turnpike.clone();
+ let complete_count_clone = complete_count.clone();
+ threads.push(thread::spawn(move || {
+ let mut cpu_set = CpuSet::new();
+ cpu_set.set(i).unwrap();
+ sched_setaffinity(getpid(), &cpu_set).unwrap();
+ let mut cat_count: CatCount = Default::default();
+
+ log::info!("Thread 0 reached turnpike");
+ loop {
+ turnpike_clone.fetch_add(1, Ordering::Relaxed);
+ loop {
+ match turnpike_clone.load(Ordering::Relaxed) {
+ 0 => break,
+ 255 => return,
+ _ => {}
+ }
+ }
+
+ for _ in 0..250 {
+ let (tctx, sctx, perm, class) = (
+ Context::new("u:object_r:keystore:s0").unwrap(),
+ Context::new(&format!(
+ "u:r:untrusted_app:s0:{}",
+ cat_count.next().make_string()
+ ))
+ .unwrap(),
+ "use",
+ "keystore2_key",
+ );
+
+ check_access(&sctx, &tctx, class, perm).unwrap();
+ }
+
+ complete_count_clone.fetch_add(1, Ordering::Relaxed);
+ while complete_count_clone.load(Ordering::Relaxed) as usize != cpus {
+ thread::sleep(Duration::from_millis(5));
+ }
+ }
+ }));
+ }
+
+ let mut i = 0;
+ let run_time = Instant::now();
+
+ loop {
+ const TEST_ITERATIONS: u32 = 500;
+ const MAX_SLEEPS: u64 = 500;
+ const SLEEP_MILLISECONDS: u64 = 5;
+ let mut sleep_count: u64 = 0;
+ while turnpike.load(Ordering::Relaxed) as usize != cpus {
+ thread::sleep(Duration::from_millis(SLEEP_MILLISECONDS));
+ sleep_count += 1;
+ assert!(
+ sleep_count < MAX_SLEEPS,
+ "Waited too long to go ready on iteration {}, only {} are ready",
+ i,
+ turnpike.load(Ordering::Relaxed)
+ );
+ }
+
+ if i % 100 == 0 {
+ let elapsed = run_time.elapsed().as_secs();
+ println!("{:02}:{:02}: Iteration {}", elapsed / 60, elapsed % 60, i);
+ }
+
+ // Give the threads some time to reach and spin on the turn pike.
+ assert_eq!(turnpike.load(Ordering::Relaxed) as usize, cpus, "i = {}", i);
+ if i >= TEST_ITERATIONS {
+ turnpike.store(255, Ordering::Relaxed);
+ break;
+ }
+
+ // Now go.
+ complete_count.store(0, Ordering::Relaxed);
+ turnpike.store(0, Ordering::Relaxed);
+ i += 1;
+
+ // Wait for them to all complete.
+ sleep_count = 0;
+ while complete_count.load(Ordering::Relaxed) as usize != cpus {
+ thread::sleep(Duration::from_millis(SLEEP_MILLISECONDS));
+ sleep_count += 1;
+ if sleep_count >= MAX_SLEEPS {
+ // Enable the following block to park the thread to allow attaching a debugger.
+ if false {
+ println!(
+ "Waited {} seconds and we seem stuck. Going to sleep forever.",
+ (MAX_SLEEPS * SLEEP_MILLISECONDS) as f32 / 1000.0
+ );
+ loop {
+ thread::park();
+ }
+ } else {
+ assert!(
+ sleep_count < MAX_SLEEPS,
+ "Waited too long to complete on iteration {}, only {} are complete",
+ i,
+ complete_count.load(Ordering::Relaxed)
+ );
+ }
+ }
+ }
+ }
+
+ for t in threads {
+ t.join().unwrap();
+ }
+}
diff --git a/keystore2/selinux/src/lib.rs b/keystore2/selinux/src/lib.rs
index 2b5091d..cf6dfd3 100644
--- a/keystore2/selinux/src/lib.rs
+++ b/keystore2/selinux/src/lib.rs
@@ -20,6 +20,13 @@
//! * selabel_lookup for the keystore2_key backend.
//! And it provides an owning wrapper around context strings `Context`.
+use anyhow::Context as AnyhowContext;
+use anyhow::{anyhow, Result};
+use lazy_static::lazy_static;
+pub use selinux::pid_t;
+use selinux::SELABEL_CTX_ANDROID_KEYSTORE2_KEY;
+use selinux::SELINUX_CB_LOG;
+use selinux_bindgen as selinux;
use std::ffi::{CStr, CString};
use std::fmt;
use std::io;
@@ -29,18 +36,18 @@
use std::ptr;
use std::sync;
-use selinux_bindgen as selinux;
-
-use anyhow::Context as AnyhowContext;
-use anyhow::{anyhow, Result};
-
-use selinux::SELABEL_CTX_ANDROID_KEYSTORE2_KEY;
-use selinux::SELINUX_CB_LOG;
-
-pub use selinux::pid_t;
-
static SELINUX_LOG_INIT: sync::Once = sync::Once::new();
+lazy_static! {
+ /// `selinux_check_access` is only thread safe if avc_init was called with lock callbacks.
+ /// However, avc_init is deprecated and not exported by androids version of libselinux.
+ /// `selinux_set_callbacks` does not allow setting lock callbacks. So the only option
+ /// that remains right now is to put a big lock around calls into libselinux.
+ /// TODO b/188079221 It should suffice to protect `selinux_check_access` but until we are
+ /// certain of that, we leave the extra locks in place
+ static ref LIB_SELINUX_LOCK: sync::Mutex<()> = Default::default();
+}
+
fn redirect_selinux_logs_to_logcat() {
// `selinux_set_callback` assigns the static lifetime function pointer
// `selinux_log_callback` to a static lifetime variable.
@@ -123,7 +130,7 @@
fn deref(&self) -> &Self::Target {
match self {
Self::Raw(p) => unsafe { CStr::from_ptr(*p) },
- Self::CString(cstr) => &cstr,
+ Self::CString(cstr) => cstr,
}
}
}
@@ -164,6 +171,8 @@
/// `selinux_android_keystore2_key_context_handle`.
pub fn new() -> Result<Self> {
init_logger_once();
+ let _lock = LIB_SELINUX_LOCK.lock().unwrap();
+
let handle = unsafe { selinux::selinux_android_keystore2_key_context_handle() };
if handle.is_null() {
return Err(anyhow!(Error::sys("Failed to open KeystoreKeyBackend")));
@@ -192,6 +201,8 @@
match unsafe {
// No need to initialize the logger here because it cannot run unless
// KeystoreKeyBackend::new has run.
+ let _lock = LIB_SELINUX_LOCK.lock().unwrap();
+
selinux::selabel_lookup(self.handle, &mut con, c_key.as_ptr(), Self::BACKEND_TYPE)
} {
0 => {
@@ -219,6 +230,8 @@
/// * Err(io::Error::last_os_error()) if getcon failed.
pub fn getcon() -> Result<Context> {
init_logger_once();
+ let _lock = LIB_SELINUX_LOCK.lock().unwrap();
+
let mut con: *mut c_char = ptr::null_mut();
match unsafe { selinux::getcon(&mut con) } {
0 => {
@@ -241,6 +254,8 @@
/// * Err(io::Error::last_os_error()) if getpidcon failed.
pub fn getpidcon(pid: selinux::pid_t) -> Result<Context> {
init_logger_once();
+ let _lock = LIB_SELINUX_LOCK.lock().unwrap();
+
let mut con: *mut c_char = ptr::null_mut();
match unsafe { selinux::getpidcon(pid, &mut con) } {
0 => {
@@ -267,6 +282,7 @@
/// the access check.
pub fn check_access(source: &CStr, target: &CStr, tclass: &str, perm: &str) -> Result<()> {
init_logger_once();
+
let c_tclass = CString::new(tclass).with_context(|| {
format!("check_access: Failed to convert tclass \"{}\" to CString.", tclass)
})?;
@@ -275,6 +291,8 @@
})?;
match unsafe {
+ let _lock = LIB_SELINUX_LOCK.lock().unwrap();
+
selinux::selinux_check_access(
source.as_ptr(),
target.as_ptr(),
@@ -455,7 +473,6 @@
check_keystore_perm!(add_auth);
check_keystore_perm!(clear_ns);
- check_keystore_perm!(get_state);
check_keystore_perm!(lock);
check_keystore_perm!(reset);
check_keystore_perm!(unlock);
diff --git a/keystore2/src/apc.rs b/keystore2/src/apc.rs
index 767014e..0096686 100644
--- a/keystore2/src/apc.rs
+++ b/keystore2/src/apc.rs
@@ -21,17 +21,17 @@
sync::{mpsc::Sender, Arc, Mutex},
};
-use crate::utils::{compat_2_response_code, ui_opts_2_compat};
+use crate::utils::{compat_2_response_code, ui_opts_2_compat, watchdog as wd};
use android_security_apc::aidl::android::security::apc::{
IConfirmationCallback::IConfirmationCallback,
IProtectedConfirmation::{BnProtectedConfirmation, IProtectedConfirmation},
ResponseCode::ResponseCode,
};
use android_security_apc::binder::{
- ExceptionCode, Interface, Result as BinderResult, SpIBinder, Status as BinderStatus, Strong,
+ BinderFeatures, ExceptionCode, Interface, Result as BinderResult, SpIBinder,
+ Status as BinderStatus, Strong, ThreadState,
};
use anyhow::{Context, Result};
-use binder::{IBinder, ThreadState};
use keystore2_apc_compat::ApcHal;
use keystore2_selinux as selinux;
use std::time::{Duration, Instant};
@@ -203,11 +203,10 @@
pub fn new_native_binder(
confirmation_token_sender: Sender<Vec<u8>>,
) -> Result<Strong<dyn IProtectedConfirmation>> {
- let result = BnProtectedConfirmation::new_binder(Self {
- state: Arc::new(Mutex::new(ApcState::new(confirmation_token_sender))),
- });
- result.as_binder().set_requesting_sid(true);
- Ok(result)
+ Ok(BnProtectedConfirmation::new_binder(
+ Self { state: Arc::new(Mutex::new(ApcState::new(confirmation_token_sender))) },
+ BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
+ ))
}
fn result(
@@ -268,7 +267,7 @@
fn present_prompt(
&self,
- listener: &dyn IConfirmationCallback,
+ listener: &binder::Strong<dyn IConfirmationCallback>,
prompt_text: &str,
extra_data: &[u8],
locale: &str,
@@ -327,7 +326,7 @@
Ok(())
}
- fn cancel_prompt(&self, listener: &dyn IConfirmationCallback) -> Result<()> {
+ fn cancel_prompt(&self, listener: &binder::Strong<dyn IConfirmationCallback>) -> Result<()> {
let mut state = self.state.lock().unwrap();
let hal = match &mut state.session {
None => {
@@ -358,21 +357,28 @@
impl IProtectedConfirmation for ApcManager {
fn presentPrompt(
&self,
- listener: &dyn IConfirmationCallback,
+ listener: &binder::Strong<dyn IConfirmationCallback>,
prompt_text: &str,
extra_data: &[u8],
locale: &str,
ui_option_flags: i32,
) -> BinderResult<()> {
+ // presentPrompt can take more time than other operations.
+ let _wp = wd::watch_millis("IProtectedConfirmation::presentPrompt", 3000);
map_or_log_err(
self.present_prompt(listener, prompt_text, extra_data, locale, ui_option_flags),
Ok,
)
}
- fn cancelPrompt(&self, listener: &dyn IConfirmationCallback) -> BinderResult<()> {
+ fn cancelPrompt(
+ &self,
+ listener: &binder::Strong<dyn IConfirmationCallback>,
+ ) -> BinderResult<()> {
+ let _wp = wd::watch_millis("IProtectedConfirmation::cancelPrompt", 500);
map_or_log_err(self.cancel_prompt(listener), Ok)
}
fn isSupported(&self) -> BinderResult<bool> {
+ let _wp = wd::watch_millis("IProtectedConfirmation::isSupported", 500);
map_or_log_err(Self::is_supported(), Ok)
}
}
diff --git a/keystore2/src/async_task.rs b/keystore2/src/async_task.rs
index 9732e79..0515c8f 100644
--- a/keystore2/src/async_task.rs
+++ b/keystore2/src/async_task.rs
@@ -89,8 +89,10 @@
struct AsyncTaskState {
state: State,
thread: Option<thread::JoinHandle<()>>,
+ timeout: Duration,
hi_prio_req: VecDeque<Box<dyn FnOnce(&mut Shelf) + Send>>,
lo_prio_req: VecDeque<Box<dyn FnOnce(&mut Shelf) + Send>>,
+ idle_fns: Vec<Arc<dyn Fn(&mut Shelf) + Send + Sync>>,
/// The store allows tasks to store state across invocations. It is passed to each invocation
/// of each task. Tasks need to cooperate on the ids they use for storing state.
shelf: Option<Shelf>,
@@ -107,25 +109,32 @@
impl Default for AsyncTask {
fn default() -> Self {
+ Self::new(Duration::from_secs(30))
+ }
+}
+
+impl AsyncTask {
+ /// Construct an [`AsyncTask`] with a specific timeout value.
+ pub fn new(timeout: Duration) -> Self {
Self {
state: Arc::new((
Condvar::new(),
Mutex::new(AsyncTaskState {
state: State::Exiting,
thread: None,
+ timeout,
hi_prio_req: VecDeque::new(),
lo_prio_req: VecDeque::new(),
+ idle_fns: Vec::new(),
shelf: None,
}),
)),
}
}
-}
-impl AsyncTask {
- /// Adds a job to the high priority queue. High priority jobs are completed before
- /// low priority jobs and can also overtake low priority jobs. But they cannot
- /// preempt them.
+ /// Adds a one-off job to the high priority queue. High priority jobs are
+ /// completed before low priority jobs and can also overtake low priority
+ /// jobs. But they cannot preempt them.
pub fn queue_hi<F>(&self, f: F)
where
F: for<'r> FnOnce(&'r mut Shelf) + Send + 'static,
@@ -133,10 +142,10 @@
self.queue(f, true)
}
- /// Adds a job to the low priority queue. Low priority jobs are completed after
- /// high priority. And they are not executed as long as high priority jobs are
- /// present. Jobs always run to completion and are never preempted by high
- /// priority jobs.
+ /// Adds a one-off job to the low priority queue. Low priority jobs are
+ /// completed after high priority. And they are not executed as long as high
+ /// priority jobs are present. Jobs always run to completion and are never
+ /// preempted by high priority jobs.
pub fn queue_lo<F>(&self, f: F)
where
F: FnOnce(&mut Shelf) + Send + 'static,
@@ -144,12 +153,24 @@
self.queue(f, false)
}
+ /// Adds an idle callback. This will be invoked whenever the worker becomes
+ /// idle (all high and low priority jobs have been performed).
+ pub fn add_idle<F>(&self, f: F)
+ where
+ F: Fn(&mut Shelf) + Send + Sync + 'static,
+ {
+ let (ref _condvar, ref state) = *self.state;
+ let mut state = state.lock().unwrap();
+ state.idle_fns.push(Arc::new(f));
+ }
+
fn queue<F>(&self, f: F, hi_prio: bool)
where
F: for<'r> FnOnce(&'r mut Shelf) + Send + 'static,
{
let (ref condvar, ref state) = *self.state;
let mut state = state.lock().unwrap();
+
if hi_prio {
state.hi_prio_req.push_back(Box::new(f));
} else {
@@ -169,42 +190,345 @@
}
let cloned_state = self.state.clone();
+ let timeout_period = state.timeout;
state.thread = Some(thread::spawn(move || {
let (ref condvar, ref state) = *cloned_state;
+
+ enum Action {
+ QueuedFn(Box<dyn FnOnce(&mut Shelf) + Send>),
+ IdleFns(Vec<Arc<dyn Fn(&mut Shelf) + Send + Sync>>),
+ }
+ let mut done_idle = false;
+
// When the worker starts, it takes the shelf and puts it on the stack.
let mut shelf = state.lock().unwrap().shelf.take().unwrap_or_default();
loop {
- if let Some(f) = {
- let (mut state, timeout) = condvar
- .wait_timeout_while(
- state.lock().unwrap(),
- Duration::from_secs(30),
- |state| state.hi_prio_req.is_empty() && state.lo_prio_req.is_empty(),
- )
- .unwrap();
- match (
- state.hi_prio_req.pop_front(),
- state.lo_prio_req.is_empty(),
- timeout.timed_out(),
- ) {
- (Some(f), _, _) => Some(f),
- (None, false, _) => state.lo_prio_req.pop_front(),
- (None, true, true) => {
- // When the worker exits it puts the shelf back into the shared
- // state for the next worker to use. So state is preserved not
- // only across invocations but also across worker thread shut down.
- state.shelf = Some(shelf);
- state.state = State::Exiting;
- break;
+ if let Some(action) = {
+ let state = state.lock().unwrap();
+ if !done_idle && state.hi_prio_req.is_empty() && state.lo_prio_req.is_empty() {
+ // No jobs queued so invoke the idle callbacks.
+ Some(Action::IdleFns(state.idle_fns.clone()))
+ } else {
+ // Wait for either a queued job to arrive or a timeout.
+ let (mut state, timeout) = condvar
+ .wait_timeout_while(state, timeout_period, |state| {
+ state.hi_prio_req.is_empty() && state.lo_prio_req.is_empty()
+ })
+ .unwrap();
+ match (
+ state.hi_prio_req.pop_front(),
+ state.lo_prio_req.is_empty(),
+ timeout.timed_out(),
+ ) {
+ (Some(f), _, _) => Some(Action::QueuedFn(f)),
+ (None, false, _) => {
+ state.lo_prio_req.pop_front().map(|f| Action::QueuedFn(f))
+ }
+ (None, true, true) => {
+ // When the worker exits it puts the shelf back into the shared
+ // state for the next worker to use. So state is preserved not
+ // only across invocations but also across worker thread shut down.
+ state.shelf = Some(shelf);
+ state.state = State::Exiting;
+ break;
+ }
+ (None, true, false) => None,
}
- (None, true, false) => None,
}
} {
- f(&mut shelf)
+ // Now that the lock has been dropped, perform the action.
+ match action {
+ Action::QueuedFn(f) => {
+ f(&mut shelf);
+ done_idle = false;
+ }
+ Action::IdleFns(idle_fns) => {
+ for idle_fn in idle_fns {
+ idle_fn(&mut shelf);
+ }
+ done_idle = true;
+ }
+ }
}
}
}));
state.state = State::Running;
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::{AsyncTask, Shelf};
+ use std::sync::{
+ mpsc::{channel, sync_channel, RecvTimeoutError},
+ Arc,
+ };
+ use std::time::Duration;
+
+ #[test]
+ fn test_shelf() {
+ let mut shelf = Shelf::default();
+
+ let s = "A string".to_string();
+ assert_eq!(shelf.put(s), None);
+
+ let s2 = "Another string".to_string();
+ assert_eq!(shelf.put(s2), Some("A string".to_string()));
+
+ // Put something of a different type on the shelf.
+ #[derive(Debug, PartialEq, Eq)]
+ struct Elf {
+ pub name: String,
+ }
+ let e1 = Elf { name: "Glorfindel".to_string() };
+ assert_eq!(shelf.put(e1), None);
+
+ // The String value is still on the shelf.
+ let s3 = shelf.get_downcast_ref::<String>().unwrap();
+ assert_eq!(s3, "Another string");
+
+ // As is the Elf.
+ {
+ let e2 = shelf.get_downcast_mut::<Elf>().unwrap();
+ assert_eq!(e2.name, "Glorfindel");
+ e2.name = "Celeborn".to_string();
+ }
+
+ // Take the Elf off the shelf.
+ let e3 = shelf.remove_downcast_ref::<Elf>().unwrap();
+ assert_eq!(e3.name, "Celeborn");
+
+ assert_eq!(shelf.remove_downcast_ref::<Elf>(), None);
+
+ // No u64 value has been put on the shelf, so getting one gives the default value.
+ {
+ let i = shelf.get_mut::<u64>();
+ assert_eq!(*i, 0);
+ *i = 42;
+ }
+ let i2 = shelf.get_downcast_ref::<u64>().unwrap();
+ assert_eq!(*i2, 42);
+
+ // No i32 value has ever been seen near the shelf.
+ assert_eq!(shelf.get_downcast_ref::<i32>(), None);
+ assert_eq!(shelf.get_downcast_mut::<i32>(), None);
+ assert_eq!(shelf.remove_downcast_ref::<i32>(), None);
+ }
+
+ #[test]
+ fn test_async_task() {
+ let at = AsyncTask::default();
+
+ // First queue up a job that blocks until we release it, to avoid
+ // unpredictable synchronization.
+ let (start_sender, start_receiver) = channel();
+ at.queue_hi(move |shelf| {
+ start_receiver.recv().unwrap();
+ // Put a trace vector on the shelf
+ shelf.put(Vec::<String>::new());
+ });
+
+ // Queue up some high-priority and low-priority jobs.
+ for i in 0..3 {
+ let j = i;
+ at.queue_lo(move |shelf| {
+ let trace = shelf.get_mut::<Vec<String>>();
+ trace.push(format!("L{}", j));
+ });
+ let j = i;
+ at.queue_hi(move |shelf| {
+ let trace = shelf.get_mut::<Vec<String>>();
+ trace.push(format!("H{}", j));
+ });
+ }
+
+ // Finally queue up a low priority job that emits the trace.
+ let (trace_sender, trace_receiver) = channel();
+ at.queue_lo(move |shelf| {
+ let trace = shelf.get_downcast_ref::<Vec<String>>().unwrap();
+ trace_sender.send(trace.clone()).unwrap();
+ });
+
+ // Ready, set, go.
+ start_sender.send(()).unwrap();
+ let trace = trace_receiver.recv().unwrap();
+
+ assert_eq!(trace, vec!["H0", "H1", "H2", "L0", "L1", "L2"]);
+ }
+
+ #[test]
+ fn test_async_task_chain() {
+ let at = Arc::new(AsyncTask::default());
+ let (sender, receiver) = channel();
+ // Queue up a job that will queue up another job. This confirms
+ // that the job is not invoked with any internal AsyncTask locks held.
+ let at_clone = at.clone();
+ at.queue_hi(move |_shelf| {
+ at_clone.queue_lo(move |_shelf| {
+ sender.send(()).unwrap();
+ });
+ });
+ receiver.recv().unwrap();
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_async_task_panic() {
+ let at = AsyncTask::default();
+ at.queue_hi(|_shelf| {
+ panic!("Panic from queued job");
+ });
+ // Queue another job afterwards to ensure that the async thread gets joined.
+ let (done_sender, done_receiver) = channel();
+ at.queue_hi(move |_shelf| {
+ done_sender.send(()).unwrap();
+ });
+ done_receiver.recv().unwrap();
+ }
+
+ #[test]
+ fn test_async_task_idle() {
+ let at = AsyncTask::new(Duration::from_secs(3));
+ // Need a SyncSender as it is Send+Sync.
+ let (idle_done_sender, idle_done_receiver) = sync_channel::<()>(3);
+ at.add_idle(move |_shelf| {
+ idle_done_sender.send(()).unwrap();
+ });
+
+ // Queue up some high-priority and low-priority jobs that take time.
+ for _i in 0..3 {
+ at.queue_lo(|_shelf| {
+ std::thread::sleep(Duration::from_millis(500));
+ });
+ at.queue_hi(|_shelf| {
+ std::thread::sleep(Duration::from_millis(500));
+ });
+ }
+ // Final low-priority job.
+ let (done_sender, done_receiver) = channel();
+ at.queue_lo(move |_shelf| {
+ done_sender.send(()).unwrap();
+ });
+
+ // Nothing happens until the last job completes.
+ assert_eq!(
+ idle_done_receiver.recv_timeout(Duration::from_secs(1)),
+ Err(RecvTimeoutError::Timeout)
+ );
+ done_receiver.recv().unwrap();
+ // Now that the last low-priority job has completed, the idle task should
+ // fire pretty much immediately.
+ idle_done_receiver.recv_timeout(Duration::from_millis(50)).unwrap();
+
+ // Idle callback not executed again even if we wait for a while.
+ assert_eq!(
+ idle_done_receiver.recv_timeout(Duration::from_secs(3)),
+ Err(RecvTimeoutError::Timeout)
+ );
+
+ // However, if more work is done then there's another chance to go idle.
+ let (done_sender, done_receiver) = channel();
+ at.queue_hi(move |_shelf| {
+ std::thread::sleep(Duration::from_millis(500));
+ done_sender.send(()).unwrap();
+ });
+ // Idle callback not immediately executed, because the high priority
+ // job is taking a while.
+ assert_eq!(
+ idle_done_receiver.recv_timeout(Duration::from_millis(1)),
+ Err(RecvTimeoutError::Timeout)
+ );
+ done_receiver.recv().unwrap();
+ idle_done_receiver.recv_timeout(Duration::from_millis(50)).unwrap();
+ }
+
+ #[test]
+ fn test_async_task_multiple_idle() {
+ let at = AsyncTask::new(Duration::from_secs(3));
+ let (idle_sender, idle_receiver) = sync_channel::<i32>(5);
+ // Queue a high priority job to start things off
+ at.queue_hi(|_shelf| {
+ std::thread::sleep(Duration::from_millis(500));
+ });
+
+ // Multiple idle callbacks.
+ for i in 0..3 {
+ let idle_sender = idle_sender.clone();
+ at.add_idle(move |_shelf| {
+ idle_sender.send(i).unwrap();
+ });
+ }
+
+ // Nothing happens immediately.
+ assert_eq!(
+ idle_receiver.recv_timeout(Duration::from_millis(1)),
+ Err(RecvTimeoutError::Timeout)
+ );
+ // Wait for a moment and the idle jobs should have run.
+ std::thread::sleep(Duration::from_secs(1));
+
+ let mut results = Vec::new();
+ while let Ok(i) = idle_receiver.recv_timeout(Duration::from_millis(1)) {
+ results.push(i);
+ }
+ assert_eq!(results, [0, 1, 2]);
+ }
+
+ #[test]
+ fn test_async_task_idle_queues_job() {
+ let at = Arc::new(AsyncTask::new(Duration::from_secs(1)));
+ let at_clone = at.clone();
+ let (idle_sender, idle_receiver) = sync_channel::<i32>(100);
+ // Add an idle callback that queues a low-priority job.
+ at.add_idle(move |shelf| {
+ at_clone.queue_lo(|_shelf| {
+ // Slow things down so the channel doesn't fill up.
+ std::thread::sleep(Duration::from_millis(50));
+ });
+ let i = shelf.get_mut::<i32>();
+ idle_sender.send(*i).unwrap();
+ *i += 1;
+ });
+
+ // Nothing happens immediately.
+ assert_eq!(
+ idle_receiver.recv_timeout(Duration::from_millis(1500)),
+ Err(RecvTimeoutError::Timeout)
+ );
+
+ // Once we queue a normal job, things start.
+ at.queue_hi(|_shelf| {});
+ assert_eq!(0, idle_receiver.recv_timeout(Duration::from_millis(200)).unwrap());
+
+ // The idle callback queues a job, and completion of that job
+ // means the task is going idle again...so the idle callback will
+ // be called repeatedly.
+ assert_eq!(1, idle_receiver.recv_timeout(Duration::from_millis(100)).unwrap());
+ assert_eq!(2, idle_receiver.recv_timeout(Duration::from_millis(100)).unwrap());
+ assert_eq!(3, idle_receiver.recv_timeout(Duration::from_millis(100)).unwrap());
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_async_task_idle_panic() {
+ let at = AsyncTask::new(Duration::from_secs(1));
+ let (idle_sender, idle_receiver) = sync_channel::<()>(3);
+ // Add an idle callback that panics.
+ at.add_idle(move |_shelf| {
+ idle_sender.send(()).unwrap();
+ panic!("Panic from idle callback");
+ });
+ // Queue a job to trigger idleness and ensuing panic.
+ at.queue_hi(|_shelf| {});
+ idle_receiver.recv().unwrap();
+
+ // Queue another job afterwards to ensure that the async thread gets joined
+ // and the panic detected.
+ let (done_sender, done_receiver) = channel();
+ at.queue_hi(move |_shelf| {
+ done_sender.send(()).unwrap();
+ });
+ done_receiver.recv().unwrap();
+ }
+}
diff --git a/keystore2/src/attestation_key_utils.rs b/keystore2/src/attestation_key_utils.rs
new file mode 100644
index 0000000..b6a8e31
--- /dev/null
+++ b/keystore2/src/attestation_key_utils.rs
@@ -0,0 +1,128 @@
+// 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.
+
+//! Implements get_attestation_key_info which loads remote provisioned or user
+//! generated attestation keys.
+
+use crate::database::{BlobMetaData, KeyEntryLoadBits, KeyType};
+use crate::database::{KeyIdGuard, KeystoreDB};
+use crate::error::{Error, ErrorCode};
+use crate::permission::KeyPerm;
+use crate::remote_provisioning::RemProvState;
+use crate::utils::check_key_permission;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ AttestationKey::AttestationKey, Certificate::Certificate, KeyParameter::KeyParameter, Tag::Tag,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, KeyDescriptor::KeyDescriptor,
+};
+use anyhow::{Context, Result};
+use keystore2_crypto::parse_subject_from_certificate;
+
+/// KeyMint takes two different kinds of attestation keys. Remote provisioned keys
+/// and those that have been generated by the user. Unfortunately, they need to be
+/// handled quite differently, thus the different representations.
+pub enum AttestationKeyInfo {
+ RemoteProvisioned {
+ attestation_key: AttestationKey,
+ attestation_certs: Certificate,
+ },
+ UserGenerated {
+ key_id_guard: KeyIdGuard,
+ blob: Vec<u8>,
+ blob_metadata: BlobMetaData,
+ issuer_subject: Vec<u8>,
+ },
+}
+
+/// This function loads and, optionally, assigns the caller's remote provisioned
+/// attestation key if a challenge is present. Alternatively, if `attest_key_descriptor` is given,
+/// it loads the user generated attestation key from the database.
+pub fn get_attest_key_info(
+ key: &KeyDescriptor,
+ caller_uid: u32,
+ attest_key_descriptor: Option<&KeyDescriptor>,
+ params: &[KeyParameter],
+ rem_prov_state: &RemProvState,
+ db: &mut KeystoreDB,
+) -> Result<Option<AttestationKeyInfo>> {
+ let challenge_present = params.iter().any(|kp| kp.tag == Tag::ATTESTATION_CHALLENGE);
+ match attest_key_descriptor {
+ None if challenge_present => rem_prov_state
+ .get_remotely_provisioned_attestation_key_and_certs(key, caller_uid, params, db)
+ .context(concat!(
+ "In get_attest_key_and_cert_chain: ",
+ "Trying to get remotely provisioned attestation key."
+ ))
+ .map(|result| {
+ result.map(|(attestation_key, attestation_certs)| {
+ AttestationKeyInfo::RemoteProvisioned { attestation_key, attestation_certs }
+ })
+ }),
+ None => Ok(None),
+ Some(attest_key) => get_user_generated_attestation_key(attest_key, caller_uid, db)
+ .context("In get_attest_key_and_cert_chain: Trying to load attest key")
+ .map(Some),
+ }
+}
+
+fn get_user_generated_attestation_key(
+ key: &KeyDescriptor,
+ caller_uid: u32,
+ db: &mut KeystoreDB,
+) -> Result<AttestationKeyInfo> {
+ let (key_id_guard, blob, cert, blob_metadata) =
+ load_attest_key_blob_and_cert(key, caller_uid, db)
+ .context("In get_user_generated_attestation_key: Failed to load blob and cert")?;
+
+ let issuer_subject: Vec<u8> = parse_subject_from_certificate(&cert).context(
+ "In get_user_generated_attestation_key: Failed to parse subject from certificate.",
+ )?;
+
+ Ok(AttestationKeyInfo::UserGenerated { key_id_guard, blob, issuer_subject, blob_metadata })
+}
+
+fn load_attest_key_blob_and_cert(
+ key: &KeyDescriptor,
+ caller_uid: u32,
+ db: &mut KeystoreDB,
+) -> Result<(KeyIdGuard, Vec<u8>, Vec<u8>, BlobMetaData)> {
+ match key.domain {
+ Domain::BLOB => Err(Error::Km(ErrorCode::INVALID_ARGUMENT)).context(
+ "In load_attest_key_blob_and_cert: Domain::BLOB attestation keys not supported",
+ ),
+ _ => {
+ let (key_id_guard, mut key_entry) = db
+ .load_key_entry(
+ key,
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ caller_uid,
+ |k, av| check_key_permission(KeyPerm::use_(), k, &av),
+ )
+ .context("In load_attest_key_blob_and_cert: Failed to load key.")?;
+
+ let (blob, blob_metadata) =
+ key_entry.take_key_blob_info().ok_or_else(Error::sys).context(concat!(
+ "In load_attest_key_blob_and_cert: Successfully loaded key entry,",
+ " but KM blob was missing."
+ ))?;
+ let cert = key_entry.take_cert().ok_or_else(Error::sys).context(concat!(
+ "In load_attest_key_blob_and_cert: Successfully loaded key entry,",
+ " but cert was missing."
+ ))?;
+ Ok((key_id_guard, blob, cert, blob_metadata))
+ }
+ }
+}
diff --git a/keystore2/src/audit_log.rs b/keystore2/src/audit_log.rs
new file mode 100644
index 0000000..3d7d26e
--- /dev/null
+++ b/keystore2/src/audit_log.rs
@@ -0,0 +1,86 @@
+// 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.
+
+//! This module implements functions to log audit events to binary security log buffer for NIAP
+//! compliance.
+
+use crate::globals::LOGS_HANDLER;
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, KeyDescriptor::KeyDescriptor,
+};
+use libc::uid_t;
+use log_event_list::{LogContext, LogIdSecurity};
+
+const TAG_KEY_GENERATED: u32 = 210024;
+const TAG_KEY_IMPORTED: u32 = 210025;
+const TAG_KEY_DESTROYED: u32 = 210026;
+const TAG_KEY_INTEGRITY_VIOLATION: u32 = 210032;
+
+const FLAG_NAMESPACE: i64 = 0x80000000;
+
+/// Encode key owner as either uid or namespace with a flag.
+fn key_owner(domain: Domain, nspace: i64, uid: i32) -> i32 {
+ match domain {
+ Domain::APP => uid,
+ Domain::SELINUX => (nspace | FLAG_NAMESPACE) as i32,
+ _ => {
+ log::info!("Not logging audit event for key with unexpected domain");
+ 0
+ }
+ }
+}
+
+/// Logs key generation event to NIAP audit log.
+pub fn log_key_generated(key: &KeyDescriptor, calling_app: uid_t, success: bool) {
+ log_key_event(TAG_KEY_GENERATED, key, calling_app, success);
+}
+
+/// Logs key import event to NIAP audit log.
+pub fn log_key_imported(key: &KeyDescriptor, calling_app: uid_t, success: bool) {
+ log_key_event(TAG_KEY_IMPORTED, key, calling_app, success);
+}
+
+/// Logs key deletion event to NIAP audit log.
+pub fn log_key_deleted(key: &KeyDescriptor, calling_app: uid_t, success: bool) {
+ log_key_event(TAG_KEY_DESTROYED, key, calling_app, success);
+}
+
+/// Logs key integrity violation to NIAP audit log.
+pub fn log_key_integrity_violation(key: &KeyDescriptor) {
+ with_log_context(TAG_KEY_INTEGRITY_VIOLATION, |ctx| {
+ let owner = key_owner(key.domain, key.nspace, key.nspace as i32);
+ ctx.append_str(key.alias.as_ref().map_or("none", String::as_str)).append_i32(owner)
+ })
+}
+
+fn log_key_event(tag: u32, key: &KeyDescriptor, calling_app: uid_t, success: bool) {
+ with_log_context(tag, |ctx| {
+ let owner = key_owner(key.domain, key.nspace, calling_app as i32);
+ ctx.append_i32(if success { 1 } else { 0 })
+ .append_str(key.alias.as_ref().map_or("none", String::as_str))
+ .append_i32(owner)
+ })
+}
+
+fn with_log_context<F>(tag: u32, f: F)
+where
+ F: Fn(LogContext) -> LogContext,
+{
+ if let Some(ctx) = LogContext::new(LogIdSecurity, tag) {
+ let event = f(ctx);
+ LOGS_HANDLER.queue_lo(move |_| {
+ event.write();
+ });
+ }
+}
diff --git a/keystore2/src/authorization.rs b/keystore2/src/authorization.rs
index 02b19c4..777089f 100644
--- a/keystore2/src/authorization.rs
+++ b/keystore2/src/authorization.rs
@@ -15,22 +15,94 @@
//! 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;
-use crate::utils::check_keystore_permission;
+use crate::utils::{check_keystore_permission, watchdog as wd};
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::{BinderFeatures,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_crypto::Password;
+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.
@@ -39,16 +111,17 @@
impl AuthorizationManager {
/// Create a new instance of Keystore Authorization service.
pub fn new_native_binder() -> Result<Strong<dyn IKeystoreAuthorization>> {
- let result = BnKeystoreAuthorization::new_binder(Self);
- result.as_binder().set_requesting_sid(true);
- Ok(result)
+ Ok(BnKeystoreAuthorization::new_binder(
+ Self,
+ BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
+ ))
}
fn add_auth_token(&self, auth_token: &HardwareAuthToken) -> Result<()> {
- //check keystore permission
+ // Check keystore permission.
check_keystore_permission(KeystorePerm::add_auth()).context("In add_auth_token.")?;
- ENFORCEMENTS.add_auth_token(auth_token.clone())?;
+ ENFORCEMENTS.add_auth_token(auth_token.clone());
Ok(())
}
@@ -56,15 +129,33 @@
&self,
lock_screen_event: LockScreenEvent,
user_id: i32,
- password: Option<&[u8]>,
+ password: Option<Password>,
+ unlocking_sids: Option<&[i64]>,
) -> Result<()> {
+ log::info!(
+ "on_lock_screen_event({:?}, user_id={:?}, password.is_some()={}, unlocking_sids={:?})",
+ lock_screen_event,
+ user_id,
+ password.is_some(),
+ unlocking_sids
+ );
match (lock_screen_event, password) {
- (LockScreenEvent::UNLOCK, Some(user_password)) => {
- //This corresponds to the unlock() method in legacy keystore API.
- //check permission
+ (LockScreenEvent::UNLOCK, Some(password)) => {
+ // This corresponds to the unlock() method in legacy keystore API.
+ // check permission
check_keystore_permission(KeystorePerm::unlock())
.context("In on_lock_screen_event: Unlock with password.")?;
ENFORCEMENTS.set_device_locked(user_id, false);
+
+ DB.with(|db| {
+ SUPER_KEY.unlock_screen_lock_bound_key(
+ &mut db.borrow_mut(),
+ user_id as u32,
+ &password,
+ )
+ })
+ .context("In on_lock_screen_event: unlock_screen_lock_bound_key failed")?;
+
// Unlock super key.
if let UserState::Uninitialized = DB
.with(|db| {
@@ -73,7 +164,7 @@
&LEGACY_MIGRATOR,
&SUPER_KEY,
user_id as u32,
- user_password,
+ &password,
)
})
.context("In on_lock_screen_event: Unlock with password.")?
@@ -89,27 +180,61 @@
check_keystore_permission(KeystorePerm::unlock())
.context("In on_lock_screen_event: Unlock.")?;
ENFORCEMENTS.set_device_locked(user_id, false);
+ DB.with(|db| {
+ SUPER_KEY.try_unlock_user_with_biometric(&mut db.borrow_mut(), user_id as u32)
+ })
+ .context("In on_lock_screen_event: try_unlock_user_with_biometric failed")?;
Ok(())
}
(LockScreenEvent::LOCK, None) => {
check_keystore_permission(KeystorePerm::lock())
.context("In on_lock_screen_event: Lock")?;
ENFORCEMENTS.set_device_locked(user_id, true);
+ DB.with(|db| {
+ SUPER_KEY.lock_screen_lock_bound_key(
+ &mut db.borrow_mut(),
+ user_id as u32,
+ unlocking_sids.unwrap_or(&[]),
+ );
+ });
Ok(())
}
_ => {
// 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 {}
impl IKeystoreAuthorization for AuthorizationManager {
fn addAuthToken(&self, auth_token: &HardwareAuthToken) -> BinderResult<()> {
+ let _wp = wd::watch_millis("IKeystoreAuthorization::addAuthToken", 500);
map_or_log_err(self.add_auth_token(auth_token), Ok)
}
@@ -118,7 +243,37 @@
lock_screen_event: LockScreenEvent,
user_id: i32,
password: Option<&[u8]>,
+ unlocking_sids: Option<&[i64]>,
) -> BinderResult<()> {
- map_or_log_err(self.on_lock_screen_event(lock_screen_event, user_id, password), Ok)
+ let _wp =
+ wd::watch_millis_with("IKeystoreAuthorization::onLockScreenEvent", 500, move || {
+ format!("lock event: {}", lock_screen_event.0)
+ });
+ map_or_log_err(
+ self.on_lock_screen_event(
+ lock_screen_event,
+ user_id,
+ password.map(|pw| pw.into()),
+ unlocking_sids,
+ ),
+ Ok,
+ )
+ }
+
+ fn getAuthTokensForCredStore(
+ &self,
+ challenge: i64,
+ secure_user_id: i64,
+ auth_token_max_age_millis: i64,
+ ) -> binder::public_api::Result<AuthorizationTokens> {
+ let _wp = wd::watch_millis("IKeystoreAuthorization::getAuthTokensForCredStore", 500);
+ 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/boot_level_keys.rs b/keystore2/src/boot_level_keys.rs
new file mode 100644
index 0000000..08c52af
--- /dev/null
+++ b/keystore2/src/boot_level_keys.rs
@@ -0,0 +1,284 @@
+// 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.
+
+//! Offer keys based on the "boot level" for superencryption.
+
+use crate::{
+ database::{KeyType, KeystoreDB},
+ key_parameter::KeyParameterValue,
+ raw_device::KeyMintDevice,
+};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, Digest::Digest, KeyParameter::KeyParameter as KmKeyParameter,
+ KeyParameterValue::KeyParameterValue as KmKeyParameterValue, KeyPurpose::KeyPurpose,
+ SecurityLevel::SecurityLevel, Tag::Tag,
+};
+use anyhow::{Context, Result};
+use keystore2_crypto::{hkdf_expand, ZVec, AES_256_KEY_LENGTH};
+use std::{collections::VecDeque, convert::TryFrom};
+
+fn get_preferred_km_instance_for_level_zero_key() -> Result<KeyMintDevice> {
+ let tee = KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT)
+ .context("In get_preferred_km_instance_for_level_zero_key: Get TEE instance failed.")?;
+ if tee.version() >= KeyMintDevice::KEY_MASTER_V4_1 {
+ Ok(tee)
+ } else {
+ match KeyMintDevice::get_or_none(SecurityLevel::STRONGBOX).context(
+ "In get_preferred_km_instance_for_level_zero_key: Get Strongbox instance failed.",
+ )? {
+ Some(strongbox) if strongbox.version() >= KeyMintDevice::KEY_MASTER_V4_1 => {
+ Ok(strongbox)
+ }
+ _ => Ok(tee),
+ }
+ }
+}
+
+/// This is not thread safe; caller must hold a lock before calling.
+/// In practice the caller is SuperKeyManager and the lock is the
+/// Mutex on its internal state.
+pub fn get_level_zero_key(db: &mut KeystoreDB) -> Result<ZVec> {
+ let km_dev = get_preferred_km_instance_for_level_zero_key()
+ .context("In get_level_zero_key: get preferred KM instance failed")?;
+
+ let key_desc = KeyMintDevice::internal_descriptor("boot_level_key".to_string());
+ let mut params = vec![
+ KeyParameterValue::Algorithm(Algorithm::HMAC).into(),
+ KeyParameterValue::Digest(Digest::SHA_2_256).into(),
+ KeyParameterValue::KeySize(256).into(),
+ KeyParameterValue::MinMacLength(256).into(),
+ KeyParameterValue::KeyPurpose(KeyPurpose::SIGN).into(),
+ KeyParameterValue::NoAuthRequired.into(),
+ ];
+
+ let has_early_boot_only = km_dev.version() >= KeyMintDevice::KEY_MASTER_V4_1;
+
+ if has_early_boot_only {
+ params.push(KeyParameterValue::EarlyBootOnly.into());
+ } else {
+ params.push(KeyParameterValue::MaxUsesPerBoot(1).into())
+ }
+
+ let (key_id_guard, key_entry) = km_dev
+ .lookup_or_generate_key(db, &key_desc, KeyType::Client, ¶ms, |key_characteristics| {
+ key_characteristics.iter().any(|kc| {
+ if kc.securityLevel == km_dev.security_level() {
+ kc.authorizations.iter().any(|a| {
+ matches!(
+ (has_early_boot_only, a),
+ (
+ true,
+ KmKeyParameter {
+ tag: Tag::EARLY_BOOT_ONLY,
+ value: KmKeyParameterValue::BoolValue(true)
+ }
+ ) | (
+ false,
+ KmKeyParameter {
+ tag: Tag::MAX_USES_PER_BOOT,
+ value: KmKeyParameterValue::Integer(1)
+ }
+ )
+ )
+ })
+ } else {
+ false
+ }
+ })
+ })
+ .context("In get_level_zero_key: lookup_or_generate_key failed")?;
+
+ let params = [
+ KeyParameterValue::MacLength(256).into(),
+ KeyParameterValue::Digest(Digest::SHA_2_256).into(),
+ ];
+ let level_zero_key = km_dev
+ .use_key_in_one_step(
+ db,
+ &key_id_guard,
+ &key_entry,
+ KeyPurpose::SIGN,
+ ¶ms,
+ None,
+ b"Create boot level key",
+ )
+ .context("In get_level_zero_key: use_key_in_one_step failed")?;
+ // TODO: this is rather unsatisfactory, we need a better way to handle
+ // sensitive binder returns.
+ let level_zero_key = ZVec::try_from(level_zero_key)
+ .context("In get_level_zero_key: conversion to ZVec failed")?;
+ Ok(level_zero_key)
+}
+
+/// Holds the key for the current boot level, and a cache of future keys generated as required.
+/// When the boot level advances, keys prior to the current boot level are securely dropped.
+pub struct BootLevelKeyCache {
+ /// Least boot level currently accessible, if any is.
+ current: usize,
+ /// Invariant: cache entry *i*, if it exists, holds the HKDF key for boot level
+ /// *i* + `current`. If the cache is non-empty it can be grown forwards, but it cannot be
+ /// grown backwards, so keys below `current` are inaccessible.
+ /// `cache.clear()` makes all keys inaccessible.
+ cache: VecDeque<ZVec>,
+}
+
+impl BootLevelKeyCache {
+ const HKDF_ADVANCE: &'static [u8] = b"Advance KDF one step";
+ const HKDF_AES: &'static [u8] = b"Generate AES-256-GCM key";
+ const HKDF_KEY_SIZE: usize = 32;
+
+ /// Initialize the cache with the level zero key.
+ pub fn new(level_zero_key: ZVec) -> Self {
+ let mut cache: VecDeque<ZVec> = VecDeque::new();
+ cache.push_back(level_zero_key);
+ Self { current: 0, cache }
+ }
+
+ /// Report whether the key for the given level can be inferred.
+ pub fn level_accessible(&self, boot_level: usize) -> bool {
+ // If the requested boot level is lower than the current boot level
+ // or if we have reached the end (`cache.empty()`) we can't retrieve
+ // the boot key.
+ boot_level >= self.current && !self.cache.is_empty()
+ }
+
+ /// Get the HKDF key for boot level `boot_level`. The key for level *i*+1
+ /// is calculated from the level *i* key using `hkdf_expand`.
+ fn get_hkdf_key(&mut self, boot_level: usize) -> Result<Option<&ZVec>> {
+ if !self.level_accessible(boot_level) {
+ return Ok(None);
+ }
+ // `self.cache.len()` represents the first entry not in the cache,
+ // so `self.current + self.cache.len()` is the first boot level not in the cache.
+ let first_not_cached = self.current + self.cache.len();
+
+ // Grow the cache forwards until it contains the desired boot level.
+ for _level in first_not_cached..=boot_level {
+ // We check at the start that cache is non-empty and future iterations only push,
+ // so this must unwrap.
+ let highest_key = self.cache.back().unwrap();
+ let next_key = hkdf_expand(Self::HKDF_KEY_SIZE, highest_key, Self::HKDF_ADVANCE)
+ .context("In BootLevelKeyCache::get_hkdf_key: Advancing key one step")?;
+ self.cache.push_back(next_key);
+ }
+
+ // If we reach this point, we should have a key at index boot_level - current.
+ Ok(Some(self.cache.get(boot_level - self.current).unwrap()))
+ }
+
+ /// Drop keys prior to the given boot level, while retaining the ability to generate keys for
+ /// that level and later.
+ pub fn advance_boot_level(&mut self, new_boot_level: usize) -> Result<()> {
+ if !self.level_accessible(new_boot_level) {
+ log::error!(
+ concat!(
+ "In BootLevelKeyCache::advance_boot_level: ",
+ "Failed to advance boot level to {}, current is {}, cache size {}"
+ ),
+ new_boot_level,
+ self.current,
+ self.cache.len()
+ );
+ return Ok(());
+ }
+
+ // We `get` the new boot level for the side effect of advancing the cache to a point
+ // where the new boot level is present.
+ self.get_hkdf_key(new_boot_level)
+ .context("In BootLevelKeyCache::advance_boot_level: Advancing cache")?;
+
+ // Then we split the queue at the index of the new boot level and discard the front,
+ // keeping only the keys with the current boot level or higher.
+ self.cache = self.cache.split_off(new_boot_level - self.current);
+
+ // The new cache has the new boot level at index 0, so we set `current` to
+ // `new_boot_level`.
+ self.current = new_boot_level;
+
+ Ok(())
+ }
+
+ /// Drop all keys, effectively raising the current boot level to infinity; no keys can
+ /// be inferred from this point on.
+ pub fn finish(&mut self) {
+ self.cache.clear();
+ }
+
+ fn expand_key(
+ &mut self,
+ boot_level: usize,
+ out_len: usize,
+ info: &[u8],
+ ) -> Result<Option<ZVec>> {
+ self.get_hkdf_key(boot_level)
+ .context("In BootLevelKeyCache::expand_key: Looking up HKDF key")?
+ .map(|k| hkdf_expand(out_len, k, info))
+ .transpose()
+ .context("In BootLevelKeyCache::expand_key: Calling hkdf_expand")
+ }
+
+ /// Return the AES-256-GCM key for the current boot level.
+ pub fn aes_key(&mut self, boot_level: usize) -> Result<Option<ZVec>> {
+ self.expand_key(boot_level, AES_256_KEY_LENGTH, BootLevelKeyCache::HKDF_AES)
+ .context("In BootLevelKeyCache::aes_key: expand_key failed")
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_output_is_consistent() -> Result<()> {
+ let initial_key = b"initial key";
+ let mut blkc = BootLevelKeyCache::new(ZVec::try_from(initial_key as &[u8])?);
+ assert!(blkc.level_accessible(0));
+ assert!(blkc.level_accessible(9));
+ assert!(blkc.level_accessible(10));
+ assert!(blkc.level_accessible(100));
+ let v0 = blkc.aes_key(0).unwrap().unwrap();
+ let v10 = blkc.aes_key(10).unwrap().unwrap();
+ assert_eq!(Some(&v0), blkc.aes_key(0)?.as_ref());
+ assert_eq!(Some(&v10), blkc.aes_key(10)?.as_ref());
+ blkc.advance_boot_level(5)?;
+ assert!(!blkc.level_accessible(0));
+ assert!(blkc.level_accessible(9));
+ assert!(blkc.level_accessible(10));
+ assert!(blkc.level_accessible(100));
+ assert_eq!(None, blkc.aes_key(0)?);
+ assert_eq!(Some(&v10), blkc.aes_key(10)?.as_ref());
+ blkc.advance_boot_level(10)?;
+ assert!(!blkc.level_accessible(0));
+ assert!(!blkc.level_accessible(9));
+ assert!(blkc.level_accessible(10));
+ assert!(blkc.level_accessible(100));
+ assert_eq!(None, blkc.aes_key(0)?);
+ assert_eq!(Some(&v10), blkc.aes_key(10)?.as_ref());
+ blkc.advance_boot_level(0)?;
+ assert!(!blkc.level_accessible(0));
+ assert!(!blkc.level_accessible(9));
+ assert!(blkc.level_accessible(10));
+ assert!(blkc.level_accessible(100));
+ assert_eq!(None, blkc.aes_key(0)?);
+ assert_eq!(Some(v10), blkc.aes_key(10)?);
+ blkc.finish();
+ assert!(!blkc.level_accessible(0));
+ assert!(!blkc.level_accessible(9));
+ assert!(!blkc.level_accessible(10));
+ assert!(!blkc.level_accessible(100));
+ assert_eq!(None, blkc.aes_key(0)?);
+ assert_eq!(None, blkc.aes_key(10)?);
+ Ok(())
+ }
+}
diff --git a/keystore2/src/crypto/Android.bp b/keystore2/src/crypto/Android.bp
index e386735..4e76507 100644
--- a/keystore2/src/crypto/Android.bp
+++ b/keystore2/src/crypto/Android.bp
@@ -59,26 +59,27 @@
shared_libs: ["libcrypto"],
bindgen_flags: [
"--size_t-is-usize",
- "--whitelist-function", "randomBytes",
- "--whitelist-function", "AES_gcm_encrypt",
- "--whitelist-function", "AES_gcm_decrypt",
- "--whitelist-function", "CreateKeyId",
- "--whitelist-function", "generateKeyFromPassword",
- "--whitelist-function", "HKDFExtract",
- "--whitelist-function", "HKDFExpand",
- "--whitelist-function", "ECDHComputeKey",
- "--whitelist-function", "ECKEYGenerateKey",
- "--whitelist-function", "ECKEYDeriveFromSecret",
- "--whitelist-function", "EC_KEY_get0_public_key",
- "--whitelist-function", "ECPOINTPoint2Oct",
- "--whitelist-function", "ECPOINTOct2Point",
- "--whitelist-function", "EC_KEY_free",
- "--whitelist-function", "EC_POINT_free",
- "--whitelist-function", "extractSubjectFromCertificate",
- "--whitelist-type", "EC_KEY",
- "--whitelist-type", "EC_POINT",
- "--whitelist-var", "EC_MAX_BYTES",
- "--whitelist-var", "EVP_MAX_MD_SIZE",
+ "--allowlist-function", "randomBytes",
+ "--allowlist-function", "AES_gcm_encrypt",
+ "--allowlist-function", "AES_gcm_decrypt",
+ "--allowlist-function", "CreateKeyId",
+ "--allowlist-function", "generateKeyFromPassword",
+ "--allowlist-function", "HKDFExtract",
+ "--allowlist-function", "HKDFExpand",
+ "--allowlist-function", "ECDHComputeKey",
+ "--allowlist-function", "ECKEYGenerateKey",
+ "--allowlist-function", "ECKEYMarshalPrivateKey",
+ "--allowlist-function", "ECKEYParsePrivateKey",
+ "--allowlist-function", "EC_KEY_get0_public_key",
+ "--allowlist-function", "ECPOINTPoint2Oct",
+ "--allowlist-function", "ECPOINTOct2Point",
+ "--allowlist-function", "EC_KEY_free",
+ "--allowlist-function", "EC_POINT_free",
+ "--allowlist-function", "extractSubjectFromCertificate",
+ "--allowlist-type", "EC_KEY",
+ "--allowlist-type", "EC_POINT",
+ "--allowlist-var", "EC_MAX_BYTES",
+ "--allowlist-var", "EVP_MAX_MD_SIZE",
],
cflags: ["-DBORINGSSL_NO_CXX"],
}
@@ -124,3 +125,13 @@
"libcrypto",
],
}
+
+rust_test {
+ name: "libkeystore2_crypto_bindgen_test",
+ srcs: [":libkeystore2_crypto_bindgen"],
+ crate_name: "keystore2_crypto_bindgen_test",
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ clippy_lints: "none",
+ lints: "none",
+}
diff --git a/keystore2/src/crypto/certificate_utils.cpp b/keystore2/src/crypto/certificate_utils.cpp
index 31c7fb4..64bf1d0 100644
--- a/keystore2/src/crypto/certificate_utils.cpp
+++ b/keystore2/src/crypto/certificate_utils.cpp
@@ -19,14 +19,18 @@
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/mem.h>
+#include <openssl/ossl_typ.h>
#include <openssl/x509v3.h>
#include <functional>
#include <limits>
-#include <string>
#include <variant>
#include <vector>
+#ifndef __LP64__
+#include <time64.h>
+#endif
+
namespace keystore {
namespace {
@@ -167,45 +171,42 @@
return key_usage;
}
-template <typename Out, typename In> static Out saturate(In in) {
- if constexpr (std::is_signed_v<Out> == std::is_signed_v<In>) {
- if constexpr (sizeof(Out) >= sizeof(In)) {
- // Same sign, and In fits into Out. Cast is lossless.
- return static_cast<Out>(in);
- } else {
- // Out is smaller than In we may need to truncate.
- // We pick the smaller of `out::max()` and the greater of `out::min()` and `in`.
- return static_cast<Out>(
- std::min(static_cast<In>(std::numeric_limits<Out>::max()),
- std::max(static_cast<In>(std::numeric_limits<Out>::min()), in)));
- }
- } else {
- // So we have different signs. This puts the lower bound at 0 because either input or output
- // is unsigned. The upper bound is max of the smaller type or, if they are equal the max of
- // the signed type.
- if constexpr (std::is_signed_v<Out>) {
- if constexpr (sizeof(Out) > sizeof(In)) {
- return static_cast<Out>(in);
- } else {
- // Because `out` is the signed one, the lower bound of `in` is 0 and fits into
- // `out`. We just have to compare the maximum and we do it in type In because it has
- // a greater range than Out, so Out::max() is guaranteed to fit.
- return static_cast<Out>(
- std::min(static_cast<In>(std::numeric_limits<Out>::max()), in));
- }
- } else {
- // Out is unsigned. So we can return 0 if in is negative.
- if (in < 0) return 0;
- if constexpr (sizeof(Out) >= sizeof(In)) {
- // If Out is wider or equal we can assign lossless.
- return static_cast<Out>(in);
- } else {
- // Otherwise we have to take the minimum of Out::max() and `in`.
- return static_cast<Out>(
- std::min(static_cast<In>(std::numeric_limits<Out>::max()), in));
- }
- }
+// TODO Once boring ssl can take int64_t instead of time_t we can go back to using
+// ASN1_TIME_set: https://bugs.chromium.org/p/boringssl/issues/detail?id=416
+std::optional<std::array<char, 16>> toTimeString(int64_t timeMillis) {
+ struct tm time;
+ // If timeMillis is negative the rounding direction should still be to the nearest previous
+ // second.
+ if (timeMillis < 0 && __builtin_add_overflow(timeMillis, -999, &timeMillis)) {
+ return std::nullopt;
}
+#if defined(__LP64__)
+ time_t timeSeconds = timeMillis / 1000;
+ if (gmtime_r(&timeSeconds, &time) == nullptr) {
+ return std::nullopt;
+ }
+#else
+ time64_t timeSeconds = timeMillis / 1000;
+ if (gmtime64_r(&timeSeconds, &time) == nullptr) {
+ return std::nullopt;
+ }
+#endif
+ std::array<char, 16> buffer;
+ if (__builtin_add_overflow(time.tm_year, 1900, &time.tm_year)) {
+ return std::nullopt;
+ }
+ if (time.tm_year >= 1950 && time.tm_year < 2050) {
+ // UTCTime according to RFC5280 4.1.2.5.1.
+ snprintf(buffer.data(), buffer.size(), "%02d%02d%02d%02d%02d%02dZ", time.tm_year % 100,
+ time.tm_mon + 1, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec);
+ } else if (time.tm_year >= 0 && time.tm_year < 10000) {
+ // GeneralizedTime according to RFC5280 4.1.2.5.2.
+ snprintf(buffer.data(), buffer.size(), "%04d%02d%02d%02d%02d%02dZ", time.tm_year,
+ time.tm_mon + 1, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec);
+ } else {
+ return std::nullopt;
+ }
+ return buffer;
}
// Creates a rump certificate structure with serial, subject and issuer names, as well as
@@ -259,19 +260,24 @@
return std::get<CertUtilsError>(subjectName);
}
- time_t notBeforeTime = saturate<time_t>(activeDateTimeMilliSeconds / 1000);
+ auto notBeforeTime = toTimeString(activeDateTimeMilliSeconds);
+ if (!notBeforeTime) {
+ return CertUtilsError::TimeError;
+ }
// Set activation date.
ASN1_TIME_Ptr notBefore(ASN1_TIME_new());
- if (!notBefore || !ASN1_TIME_set(notBefore.get(), notBeforeTime) ||
+ if (!notBefore || !ASN1_TIME_set_string(notBefore.get(), notBeforeTime->data()) ||
!X509_set_notBefore(certificate.get(), notBefore.get() /* Don't release; copied */))
return CertUtilsError::BoringSsl;
// Set expiration date.
- time_t notAfterTime;
- notAfterTime = saturate<time_t>(usageExpireDateTimeMilliSeconds / 1000);
+ auto notAfterTime = toTimeString(usageExpireDateTimeMilliSeconds);
+ if (!notAfterTime) {
+ return CertUtilsError::TimeError;
+ }
ASN1_TIME_Ptr notAfter(ASN1_TIME_new());
- if (!notAfter || !ASN1_TIME_set(notAfter.get(), notAfterTime) ||
+ if (!notAfter || !ASN1_TIME_set_string(notAfter.get(), notAfterTime->data()) ||
!X509_set_notAfter(certificate.get(), notAfter.get() /* Don't release; copied */)) {
return CertUtilsError::BoringSsl;
}
@@ -512,10 +518,7 @@
return ASN1_STRING_Ptr(algo_str);
}
-CertUtilsError makeAndSetAlgo(X509_ALGOR* algo_field, Algo algo, Padding padding, Digest digest) {
- if (algo_field == nullptr) {
- return CertUtilsError::UnexpectedNullPointer;
- }
+std::variant<CertUtilsError, X509_ALGOR_Ptr> makeAlgo(Algo algo, Padding padding, Digest digest) {
ASN1_STRING_Ptr param;
int param_type = V_ASN1_UNDEF;
int nid = 0;
@@ -584,23 +587,29 @@
return CertUtilsError::InvalidArgument;
}
- if (!X509_ALGOR_set0(algo_field, OBJ_nid2obj(nid), param_type, param.get())) {
+ X509_ALGOR_Ptr result(X509_ALGOR_new());
+ if (!result) {
+ return CertUtilsError::MemoryAllocation;
+ }
+ if (!X509_ALGOR_set0(result.get(), OBJ_nid2obj(nid), param_type, param.get())) {
return CertUtilsError::Encoding;
}
// The X509 struct took ownership.
param.release();
- return CertUtilsError::Ok;
+ return result;
}
// This function allows for signing a
CertUtilsError signCertWith(X509* certificate,
std::function<std::vector<uint8_t>(const uint8_t*, size_t)> sign,
Algo algo, Padding padding, Digest digest) {
- if (auto error = makeAndSetAlgo(certificate->sig_alg, algo, padding, digest)) {
- return error;
+ auto algo_objV = makeAlgo(algo, padding, digest);
+ if (auto error = std::get_if<CertUtilsError>(&algo_objV)) {
+ return *error;
}
- if (auto error = makeAndSetAlgo(certificate->cert_info->signature, algo, padding, digest)) {
- return error;
+ auto& algo_obj = std::get<X509_ALGOR_Ptr>(algo_objV);
+ if (!X509_set1_signature_algo(certificate, algo_obj.get())) {
+ return CertUtilsError::BoringSsl;
}
uint8_t* cert_buf = nullptr;
@@ -615,13 +624,10 @@
return CertUtilsError::SignatureFailed;
}
- if (!ASN1_STRING_set(certificate->signature, signature.data(), signature.size())) {
+ if (!X509_set1_signature_value(certificate, signature.data(), signature.size())) {
return CertUtilsError::BoringSsl;
}
- certificate->signature->flags &= ~(0x07);
- certificate->signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
-
return CertUtilsError::Ok;
}
diff --git a/keystore2/src/crypto/crypto.cpp b/keystore2/src/crypto/crypto.cpp
index 2e613fd..5d360a1 100644
--- a/keystore2/src/crypto/crypto.cpp
+++ b/keystore2/src/crypto/crypto.cpp
@@ -225,7 +225,7 @@
EC_KEY* ECKEYGenerateKey() {
EC_KEY* key = EC_KEY_new();
- EC_GROUP* group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
+ EC_GROUP* group = EC_GROUP_new_by_curve_name(NID_secp521r1);
EC_KEY_set_group(key, group);
auto result = EC_KEY_generate_key(key);
if (result == 0) {
@@ -236,15 +236,33 @@
return key;
}
-EC_KEY* ECKEYDeriveFromSecret(const uint8_t* secret, size_t secret_len) {
- EC_GROUP* group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
- auto result = EC_KEY_derive_from_secret(group, secret, secret_len);
+size_t ECKEYMarshalPrivateKey(const EC_KEY* priv_key, uint8_t* buf, size_t len) {
+ CBB cbb;
+ size_t out_len;
+ if (!CBB_init_fixed(&cbb, buf, len) ||
+ !EC_KEY_marshal_private_key(&cbb, priv_key, EC_PKEY_NO_PARAMETERS | EC_PKEY_NO_PUBKEY) ||
+ !CBB_finish(&cbb, nullptr, &out_len)) {
+ return 0;
+ } else {
+ return out_len;
+ }
+}
+
+EC_KEY* ECKEYParsePrivateKey(const uint8_t* buf, size_t len) {
+ CBS cbs;
+ CBS_init(&cbs, buf, len);
+ EC_GROUP* group = EC_GROUP_new_by_curve_name(NID_secp521r1);
+ auto result = EC_KEY_parse_private_key(&cbs, group);
EC_GROUP_free(group);
+ if (result != nullptr && CBS_len(&cbs) != 0) {
+ EC_KEY_free(result);
+ return nullptr;
+ }
return result;
}
size_t ECPOINTPoint2Oct(const EC_POINT* point, uint8_t* buf, size_t len) {
- EC_GROUP* group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
+ EC_GROUP* group = EC_GROUP_new_by_curve_name(NID_secp521r1);
point_conversion_form_t form = POINT_CONVERSION_UNCOMPRESSED;
auto result = EC_POINT_point2oct(group, point, form, buf, len, nullptr);
EC_GROUP_free(group);
@@ -252,7 +270,7 @@
}
EC_POINT* ECPOINTOct2Point(const uint8_t* buf, size_t len) {
- EC_GROUP* group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
+ EC_GROUP* group = EC_GROUP_new_by_curve_name(NID_secp521r1);
EC_POINT* point = EC_POINT_new(group);
auto result = EC_POINT_oct2point(group, point, buf, len, nullptr);
EC_GROUP_free(group);
diff --git a/keystore2/src/crypto/crypto.hpp b/keystore2/src/crypto/crypto.hpp
index 1b8971f..f841eb3 100644
--- a/keystore2/src/crypto/crypto.hpp
+++ b/keystore2/src/crypto/crypto.hpp
@@ -55,7 +55,9 @@
EC_KEY* ECKEYGenerateKey();
- EC_KEY* ECKEYDeriveFromSecret(const uint8_t *secret, size_t secret_len);
+ size_t ECKEYMarshalPrivateKey(const EC_KEY *priv_key, uint8_t *buf, size_t len);
+
+ EC_KEY* ECKEYParsePrivateKey(const uint8_t *buf, size_t len);
size_t ECPOINTPoint2Oct(const EC_POINT *point, uint8_t *buf, size_t len);
@@ -67,7 +69,7 @@
// cert_len, extract the subject, DER-encode it and write the result to
// subject_buf, which has subject_buf_len capacity.
//
-// Because the length of the issuer is unknown, and becaue we'd like to (a) be
+// Because the length of the subject is unknown, and because we'd like to (a) be
// able to handle subjects of any size and (b) avoid parsing the certificate
// twice most of the time, once to discover the length and once to parse it, the
// return value is overloaded.
diff --git a/keystore2/src/crypto/error.rs b/keystore2/src/crypto/error.rs
index 1eec321..a369012 100644
--- a/keystore2/src/crypto/error.rs
+++ b/keystore2/src/crypto/error.rs
@@ -74,9 +74,13 @@
#[error("Failed to generate key.")]
ECKEYGenerateKeyFailed,
- /// This is returned if the C implementation of ECKEYDeriveFromSecret returned null.
- #[error("Failed to derive key.")]
- ECKEYDeriveFailed,
+ /// This is returned if the C implementation of ECKEYMarshalPrivateKey returned 0.
+ #[error("Failed to marshal private key.")]
+ ECKEYMarshalPrivateKeyFailed,
+
+ /// This is returned if the C implementation of ECKEYParsePrivateKey returned null.
+ #[error("Failed to parse private key.")]
+ ECKEYParsePrivateKeyFailed,
/// This is returned if the C implementation of ECPOINTPoint2Oct returned 0.
#[error("Failed to convert point to oct.")]
diff --git a/keystore2/src/crypto/include/certificate_utils.h b/keystore2/src/crypto/include/certificate_utils.h
index 6c25b9a..cad82b6 100644
--- a/keystore2/src/crypto/include/certificate_utils.h
+++ b/keystore2/src/crypto/include/certificate_utils.h
@@ -39,6 +39,7 @@
DEFINE_OPENSSL_OBJECT_POINTER(ASN1_TIME);
DEFINE_OPENSSL_OBJECT_POINTER(EVP_PKEY);
DEFINE_OPENSSL_OBJECT_POINTER(X509);
+DEFINE_OPENSSL_OBJECT_POINTER(X509_ALGOR);
DEFINE_OPENSSL_OBJECT_POINTER(X509_EXTENSION);
DEFINE_OPENSSL_OBJECT_POINTER(X509_NAME);
DEFINE_OPENSSL_OBJECT_POINTER(EVP_PKEY_CTX);
@@ -53,6 +54,7 @@
InvalidArgument,
UnexpectedNullPointer,
SignatureFailed,
+ TimeError,
};
private:
@@ -137,6 +139,16 @@
};
/**
+ * Takes an int64_t representing UNIX epoch time in milliseconds and turns it into a UTCTime
+ * or GeneralizedTime string depending on whether the year is in the interval [1950 .. 2050).
+ * Note: The string returned in the array buffer is NUL terminated and of length 13 (UTCTime)
+ * or 15 (GeneralizedTime).
+ * @param timeMillis
+ * @return UTCTime or GeneralizedTime string.
+ */
+std::optional<std::array<char, 16>> toTimeString(int64_t timeMillis);
+
+/**
* Sets the signature specifier of the certificate and the signature according to the parameters
* c. Then it signs the certificate with the `sign` callback.
* IMPORTANT: The parameters `algo`, `padding`, and `digest` do not control the actual signing
diff --git a/keystore2/src/crypto/lib.rs b/keystore2/src/crypto/lib.rs
index f23778c..5f8a2ef 100644
--- a/keystore2/src/crypto/lib.rs
+++ b/keystore2/src/crypto/lib.rs
@@ -20,9 +20,9 @@
pub use error::Error;
use keystore2_crypto_bindgen::{
extractSubjectFromCertificate, generateKeyFromPassword, randomBytes, AES_gcm_decrypt,
- AES_gcm_encrypt, ECDHComputeKey, ECKEYDeriveFromSecret, ECKEYGenerateKey, ECPOINTOct2Point,
- ECPOINTPoint2Oct, EC_KEY_free, EC_KEY_get0_public_key, EC_POINT_free, HKDFExpand, HKDFExtract,
- EC_KEY, EC_MAX_BYTES, EC_POINT, EVP_MAX_MD_SIZE,
+ AES_gcm_encrypt, ECDHComputeKey, ECKEYGenerateKey, ECKEYMarshalPrivateKey,
+ ECKEYParsePrivateKey, ECPOINTOct2Point, ECPOINTPoint2Oct, EC_KEY_free, EC_KEY_get0_public_key,
+ EC_POINT_free, HKDFExpand, HKDFExtract, EC_KEY, EC_MAX_BYTES, EC_POINT, EVP_MAX_MD_SIZE,
};
use std::convert::TryFrom;
use std::convert::TryInto;
@@ -30,7 +30,7 @@
pub use zvec::ZVec;
/// Length of the expected initialization vector.
-pub const IV_LENGTH: usize = 16;
+pub const GCM_IV_LENGTH: usize = 12;
/// Length of the expected AEAD TAG.
pub const TAG_LENGTH: usize = 16;
/// Length of an AES 256 key in bytes.
@@ -40,9 +40,9 @@
/// Length of the expected salt for key from password generation.
pub const SALT_LENGTH: usize = 16;
-// This is the number of bytes of the GCM IV that is expected to be initialized
-// with random bytes.
-const GCM_IV_LENGTH: usize = 12;
+/// Older versions of keystore produced IVs with four extra
+/// ignored zero bytes at the end; recognise and trim those.
+pub const LEGACY_IV_LENGTH: usize = 16;
/// Generate an AES256 key, essentially 32 random bytes from the underlying
/// boringssl library discretely stuffed into a ZVec.
@@ -58,10 +58,15 @@
/// Generate a salt.
pub fn generate_salt() -> Result<Vec<u8>, Error> {
- // Safety: salt has the same length as the requested number of random bytes.
- let mut salt = vec![0; SALT_LENGTH];
- if unsafe { randomBytes(salt.as_mut_ptr(), SALT_LENGTH) } {
- Ok(salt)
+ generate_random_data(SALT_LENGTH)
+}
+
+/// Generate random data of the given size.
+pub fn generate_random_data(size: usize) -> Result<Vec<u8>, Error> {
+ // Safety: data has the same length as the requested number of random bytes.
+ let mut data = vec![0; size];
+ if unsafe { randomBytes(data.as_mut_ptr(), size) } {
+ Ok(data)
} else {
Err(Error::RandomNumberGenerationFailed)
}
@@ -75,10 +80,13 @@
/// freed. Input key is taken as a slice for flexibility, but it is recommended that it is held
/// in a ZVec as well.
pub fn aes_gcm_decrypt(data: &[u8], iv: &[u8], tag: &[u8], key: &[u8]) -> Result<ZVec, Error> {
- if iv.len() != IV_LENGTH {
- return Err(Error::InvalidIvLength);
- }
-
+ // Old versions of aes_gcm_encrypt produced 16 byte IVs, but the last four bytes were ignored
+ // so trim these to the correct size.
+ let iv = match iv.len() {
+ GCM_IV_LENGTH => iv,
+ LEGACY_IV_LENGTH => &iv[..GCM_IV_LENGTH],
+ _ => return Err(Error::InvalidIvLength),
+ };
if tag.len() != TAG_LENGTH {
return Err(Error::InvalidAeadTagLength);
}
@@ -91,8 +99,8 @@
let mut result = ZVec::new(data.len())?;
// Safety: The first two arguments must point to buffers with a size given by the third
- // argument. The key must have a size of 16 or 32 bytes which we check above.
- // The iv and tag arguments must be 16 bytes, which we also check above.
+ // argument. We pass the length of the key buffer along with the key.
+ // The `iv` buffer must be 12 bytes and the `tag` buffer 16, which we check above.
match unsafe {
AES_gcm_decrypt(
data.as_ptr(),
@@ -113,10 +121,9 @@
/// This function accepts 128 and 256-bit keys and uses AES128 and AES256 respectively based on
/// the key length. The function generates an initialization vector. The return value is a tuple
/// of `(ciphertext, iv, tag)`.
-pub fn aes_gcm_encrypt(data: &[u8], key: &[u8]) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>), Error> {
- let mut iv = vec![0; IV_LENGTH];
- // Safety: iv is longer than GCM_IV_LENGTH, which is 12 while IV_LENGTH is 16.
- // The iv needs to be 16 bytes long, but the last 4 bytes remain zeroed.
+pub fn aes_gcm_encrypt(plaintext: &[u8], key: &[u8]) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>), Error> {
+ let mut iv = vec![0; GCM_IV_LENGTH];
+ // Safety: iv is GCM_IV_LENGTH bytes long.
if !unsafe { randomBytes(iv.as_mut_ptr(), GCM_IV_LENGTH) } {
return Err(Error::RandomNumberGenerationFailed);
}
@@ -126,60 +133,90 @@
_ => return Err(Error::InvalidKeyLength),
}
- let mut result: Vec<u8> = vec![0; data.len()];
+ let mut ciphertext: Vec<u8> = vec![0; plaintext.len()];
let mut tag: Vec<u8> = vec![0; TAG_LENGTH];
- match unsafe {
+ // Safety: The first two arguments must point to buffers with a size given by the third
+ // argument. We pass the length of the key buffer along with the key.
+ // The `iv` buffer must be 12 bytes and the `tag` buffer 16, which we check above.
+ if unsafe {
AES_gcm_encrypt(
- data.as_ptr(),
- result.as_mut_ptr(),
- data.len(),
+ plaintext.as_ptr(),
+ ciphertext.as_mut_ptr(),
+ plaintext.len(),
key.as_ptr(),
key.len(),
iv.as_ptr(),
tag.as_mut_ptr(),
)
} {
- true => Ok((result, iv, tag)),
- false => Err(Error::EncryptionFailed),
+ Ok((ciphertext, iv, tag))
+ } else {
+ Err(Error::EncryptionFailed)
}
}
-/// Generates a key from the given password and salt.
-/// The salt must be exactly 16 bytes long.
-/// Two key sizes are accepted: 16 and 32 bytes.
-pub fn derive_key_from_password(
- pw: &[u8],
- salt: Option<&[u8]>,
- key_length: usize,
-) -> Result<ZVec, Error> {
- let salt: *const u8 = match salt {
- Some(s) => {
- if s.len() != SALT_LENGTH {
- return Err(Error::InvalidSaltLength);
- }
- s.as_ptr()
- }
- None => std::ptr::null(),
- };
+/// Represents a "password" that can be used to key the PBKDF2 algorithm.
+pub enum Password<'a> {
+ /// Borrow an existing byte array
+ Ref(&'a [u8]),
+ /// Use an owned ZVec to store the key
+ Owned(ZVec),
+}
- match key_length {
- AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {}
- _ => return Err(Error::InvalidKeyLength),
+impl<'a> From<&'a [u8]> for Password<'a> {
+ fn from(pw: &'a [u8]) -> Self {
+ Self::Ref(pw)
+ }
+}
+
+impl<'a> Password<'a> {
+ fn get_key(&'a self) -> &'a [u8] {
+ match self {
+ Self::Ref(b) => b,
+ Self::Owned(z) => &*z,
+ }
}
- let mut result = ZVec::new(key_length)?;
+ /// Generate a key from the given password and salt.
+ /// The salt must be exactly 16 bytes long.
+ /// Two key sizes are accepted: 16 and 32 bytes.
+ pub fn derive_key(&self, salt: Option<&[u8]>, key_length: usize) -> Result<ZVec, Error> {
+ let pw = self.get_key();
- unsafe {
- generateKeyFromPassword(
- result.as_mut_ptr(),
- result.len(),
- pw.as_ptr() as *const std::os::raw::c_char,
- pw.len(),
- salt,
- )
- };
+ let salt: *const u8 = match salt {
+ Some(s) => {
+ if s.len() != SALT_LENGTH {
+ return Err(Error::InvalidSaltLength);
+ }
+ s.as_ptr()
+ }
+ None => std::ptr::null(),
+ };
- Ok(result)
+ match key_length {
+ AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {}
+ _ => return Err(Error::InvalidKeyLength),
+ }
+
+ let mut result = ZVec::new(key_length)?;
+
+ unsafe {
+ generateKeyFromPassword(
+ result.as_mut_ptr(),
+ result.len(),
+ pw.as_ptr() as *const std::os::raw::c_char,
+ pw.len(),
+ salt,
+ )
+ };
+
+ Ok(result)
+ }
+
+ /// Try to make another Password object with the same data.
+ pub fn try_clone(&self) -> Result<Password<'static>, Error> {
+ Ok(Password::Owned(ZVec::try_from(self.get_key())?))
+ }
}
/// Calls the boringssl HKDF_extract function.
@@ -307,14 +344,32 @@
Ok(ECKey(key))
}
-/// Calls the boringssl EC_KEY_derive_from_secret function.
-pub fn ec_key_derive_from_secret(secret: &[u8]) -> Result<ECKey, Error> {
- // Safety: secret is a valid buffer.
- let result = unsafe { ECKEYDeriveFromSecret(secret.as_ptr(), secret.len()) };
- if result.is_null() {
- return Err(Error::ECKEYDeriveFailed);
+/// Calls the boringssl EC_KEY_marshal_private_key function.
+pub fn ec_key_marshal_private_key(key: &ECKey) -> Result<ZVec, Error> {
+ let len = 73; // Empirically observed length of private key
+ let mut buf = ZVec::new(len)?;
+ // Safety: the key is valid.
+ // This will not write past the specified length of the buffer; if the
+ // len above is too short, it returns 0.
+ let written_len =
+ unsafe { ECKEYMarshalPrivateKey(key.0, buf.as_mut_ptr(), buf.len()) } as usize;
+ if written_len == len {
+ Ok(buf)
+ } else {
+ Err(Error::ECKEYMarshalPrivateKeyFailed)
}
- Ok(ECKey(result))
+}
+
+/// Calls the boringssl EC_KEY_parse_private_key function.
+pub fn ec_key_parse_private_key(buf: &[u8]) -> Result<ECKey, Error> {
+ // Safety: this will not read past the specified length of the buffer.
+ // It fails if less than the whole buffer is consumed.
+ let key = unsafe { ECKEYParsePrivateKey(buf.as_ptr(), buf.len()) };
+ if key.is_null() {
+ Err(Error::ECKEYParsePrivateKeyFailed)
+ } else {
+ Ok(ECKey(key))
+ }
}
/// Calls the boringssl EC_KEY_get0_public_key function.
@@ -326,8 +381,8 @@
/// Calls the boringssl EC_POINT_point2oct.
pub fn ec_point_point_to_oct(point: &EC_POINT) -> Result<Vec<u8>, Error> {
- // We fix the length to 65 (1 + 2 * field_elem_size), as we get an error if it's too small.
- let len = 65;
+ // We fix the length to 133 (1 + 2 * field_elem_size), as we get an error if it's too small.
+ let len = 133;
let mut buf = vec![0; len];
// Safety: EC_POINT_point2oct writes at most len bytes. The point is valid.
let result = unsafe { ECPOINTPoint2Oct(point, buf.as_mut_ptr(), len) };
@@ -354,11 +409,13 @@
Ok(OwnedECPoint(result))
}
-/// Uses BoringSSL to extract the DER-encoded issuer subject from a
-/// DER-encoded X.509 certificate.
-pub fn parse_issuer_subject_from_certificate(cert_buf: &[u8]) -> Result<Vec<u8>, Error> {
+/// Uses BoringSSL to extract the DER-encoded subject from a DER-encoded X.509 certificate.
+pub fn parse_subject_from_certificate(cert_buf: &[u8]) -> Result<Vec<u8>, Error> {
// Try with a 200-byte output buffer, should be enough in all but bizarre cases.
let mut retval = vec![0; 200];
+
+ // Safety: extractSubjectFromCertificate reads at most cert_buf.len() bytes from cert_buf and
+ // writes at most retval.len() bytes to retval.
let mut size = unsafe {
extractSubjectFromCertificate(
cert_buf.as_ptr(),
@@ -374,12 +431,11 @@
if size < 0 {
// Our buffer wasn't big enough. Make one that is just the right size and try again.
- let negated_size = usize::try_from(-size);
- retval = match negated_size.ok() {
- None => return Err(Error::ExtractSubjectFailed),
- Some(size) => vec![0; size],
- };
+ let negated_size = usize::try_from(-size).map_err(|_e| Error::ExtractSubjectFailed)?;
+ retval = vec![0; negated_size];
+ // Safety: extractSubjectFromCertificate reads at most cert_buf.len() bytes from cert_buf
+ // and writes at most retval.len() bytes to retval.
size = unsafe {
extractSubjectFromCertificate(
cert_buf.as_ptr(),
@@ -395,14 +451,8 @@
}
// Reduce buffer size to the amount written.
- let safe_size = usize::try_from(size);
- retval.resize(
- match safe_size.ok() {
- None => return Err(Error::ExtractSubjectFailed),
- Some(size) => size,
- },
- 0,
- );
+ let safe_size = usize::try_from(size).map_err(|_e| Error::ExtractSubjectFailed)?;
+ retval.truncate(safe_size);
Ok(retval)
}
@@ -493,26 +543,26 @@
}
#[test]
- fn test_ec() {
- let key = ec_key_generate_key();
- assert!(key.is_ok());
- assert!(!key.unwrap().0.is_null());
+ fn test_ec() -> Result<(), Error> {
+ let priv0 = ec_key_generate_key()?;
+ assert!(!priv0.0.is_null());
+ let pub0 = ec_key_get0_public_key(&priv0);
- let key = ec_key_derive_from_secret(&[42; 16]);
- assert!(key.is_ok());
- let key = key.unwrap();
- assert!(!key.0.is_null());
+ let priv1 = ec_key_generate_key()?;
+ let pub1 = ec_key_get0_public_key(&priv1);
- let point = ec_key_get0_public_key(&key);
+ let priv0s = ec_key_marshal_private_key(&priv0)?;
+ let pub0s = ec_point_point_to_oct(pub0.get_point())?;
+ let pub1s = ec_point_point_to_oct(pub1.get_point())?;
- let result = ecdh_compute_key(point.get_point(), &key);
- assert!(result.is_ok());
+ let priv0 = ec_key_parse_private_key(&priv0s)?;
+ let pub0 = ec_point_oct_to_point(&pub0s)?;
+ let pub1 = ec_point_oct_to_point(&pub1s)?;
- let oct = ec_point_point_to_oct(point.get_point());
- assert!(oct.is_ok());
- let oct = oct.unwrap();
+ let left_key = ecdh_compute_key(pub0.get_point(), &priv1)?;
+ let right_key = ecdh_compute_key(pub1.get_point(), &priv0)?;
- let point2 = ec_point_oct_to_point(oct.as_slice());
- assert!(point2.is_ok());
+ assert_eq!(left_key, right_key);
+ Ok(())
}
}
diff --git a/keystore2/src/crypto/tests/certificate_utils_test.cpp b/keystore2/src/crypto/tests/certificate_utils_test.cpp
index 119c3fa..bd94928 100644
--- a/keystore2/src/crypto/tests/certificate_utils_test.cpp
+++ b/keystore2/src/crypto/tests/certificate_utils_test.cpp
@@ -315,3 +315,23 @@
EVP_PKEY_Ptr decoded_pkey(X509_get_pubkey(decoded_cert.get()));
ASSERT_TRUE(X509_verify(decoded_cert.get(), decoded_pkey.get()));
}
+
+TEST(TimeStringTests, toTimeStringTest) {
+ // Two test vectors that need to result in UTCTime
+ ASSERT_EQ(std::string(toTimeString(1622758591000)->data()), std::string("210603221631Z"));
+ ASSERT_EQ(std::string(toTimeString(0)->data()), std::string("700101000000Z"));
+ // Two test vectors that need to result in GeneralizedTime.
+ ASSERT_EQ(std::string(toTimeString(16227585910000)->data()), std::string("24840325064510Z"));
+ ASSERT_EQ(std::string(toTimeString(-1622758591000)->data()), std::string("19180731014329Z"));
+
+ // Highest possible UTCTime
+ ASSERT_EQ(std::string(toTimeString(2524607999999)->data()), "491231235959Z");
+ // And one millisecond later must be GeneralizedTime.
+ ASSERT_EQ(std::string(toTimeString(2524608000000)->data()), "20500101000000Z");
+
+ // Earliest possible UTCTime
+ ASSERT_EQ(std::string(toTimeString(-631152000000)->data()), "500101000000Z");
+ // And one millisecond earlier must be GeneralizedTime.
+ // This also checks that the rounding direction does not flip when the input is negative.
+ ASSERT_EQ(std::string(toTimeString(-631152000001)->data()), "19491231235959Z");
+}
diff --git a/keystore2/src/crypto/zvec.rs b/keystore2/src/crypto/zvec.rs
index e75e1dc..78b474e 100644
--- a/keystore2/src/crypto/zvec.rs
+++ b/keystore2/src/crypto/zvec.rs
@@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#![allow(dead_code)]
-
use crate::error::Error;
use nix::sys::mman::{mlock, munlock};
use std::convert::TryFrom;
@@ -106,12 +104,16 @@
impl TryFrom<Vec<u8>> for ZVec {
type Error = Error;
- fn try_from(v: Vec<u8>) -> Result<Self, Self::Error> {
+ fn try_from(mut v: Vec<u8>) -> Result<Self, Self::Error> {
+ let len = v.len();
+ // into_boxed_slice calls shrink_to_fit, which may move the pointer.
+ // But sometimes the contents of the Vec are already sensitive and
+ // mustn't be copied. So ensure the shrink_to_fit call is a NOP.
+ v.resize(v.capacity(), 0);
let b = v.into_boxed_slice();
if !b.is_empty() {
unsafe { mlock(b.as_ptr() as *const std::ffi::c_void, b.len()) }?;
}
- let len = b.len();
Ok(Self { elems: b, len })
}
}
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 40860be..ae2875c 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -41,31 +41,40 @@
//! from the database module these functions take permission check
//! callbacks.
-use crate::error::{Error as KsError, ErrorCode, ResponseCode};
+mod perboot;
+pub(crate) mod utils;
+mod versioning;
+
use crate::impl_metadata; // This is in db_utils.rs
use crate::key_parameter::{KeyParameter, Tag};
+use crate::metrics_store::log_rkp_error_stats;
use crate::permission::KeyPermSet;
-use crate::utils::{get_current_time_in_seconds, AID_USER_OFFSET};
+use crate::utils::{get_current_time_in_milliseconds, watchdog as wd, AID_USER_OFFSET};
use crate::{
- db_utils::{self, SqlField},
- gc::Gc,
+ error::{Error as KsError, ErrorCode, ResponseCode},
+ super_key::SuperKeyType,
};
+use crate::{gc::Gc, super_key::USER_SUPER_KEY};
use anyhow::{anyhow, Context, Result};
use std::{convert::TryFrom, convert::TryInto, ops::Deref, time::SystemTimeError};
+use utils as db_utils;
+use utils::SqlField;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
HardwareAuthToken::HardwareAuthToken,
HardwareAuthenticatorType::HardwareAuthenticatorType, SecurityLevel::SecurityLevel,
};
-use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
- Timestamp::Timestamp,
-};
use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, KeyDescriptor::KeyDescriptor,
};
use android_security_remoteprovisioning::aidl::android::security::remoteprovisioning::{
AttestationPoolStatus::AttestationPoolStatus,
};
+use android_security_metrics::aidl::android::security::metrics::{
+ StorageStats::StorageStats,
+ Storage::Storage as MetricsStorage,
+ RkpError::RkpError as MetricsRkpError,
+};
use keystore2_crypto::ZVec;
use lazy_static::lazy_static;
@@ -73,7 +82,7 @@
#[cfg(not(test))]
use rand::prelude::random;
use rusqlite::{
- params,
+ params, params_from_iter,
types::FromSql,
types::FromSqlResult,
types::ToSqlOutput,
@@ -84,7 +93,7 @@
use std::{
collections::{HashMap, HashSet},
path::Path,
- sync::{Condvar, Mutex},
+ sync::{Arc, Condvar, Mutex},
time::{Duration, SystemTime},
};
@@ -108,6 +117,8 @@
/// Vector representing the raw public key so results from the server can be matched
/// to the right entry
AttestationRawPubKey(Vec<u8>) with accessor attestation_raw_pub_key,
+ /// SEC1 public key for ECDH encryption
+ Sec1PublicKey(Vec<u8>) with accessor sec1_public_key,
// --- ADD NEW META DATA FIELDS HERE ---
// For backwards compatibility add new entries only to
// end of this list and above this comment.
@@ -131,7 +142,7 @@
let db_tag: i64 = row.get(0).context("Failed to read tag.")?;
metadata.insert(
db_tag,
- KeyMetaEntry::new_from_sql(db_tag, &SqlField::new(1, &row))
+ KeyMetaEntry::new_from_sql(db_tag, &SqlField::new(1, row))
.context("Failed to read KeyMetaEntry.")?,
);
Ok(())
@@ -178,6 +189,11 @@
AeadTag(Vec<u8>) with accessor aead_tag,
/// The uuid of the owning KeyMint instance.
KmUuid(Uuid) with accessor km_uuid,
+ /// If the key is ECDH encrypted, this is the ephemeral public key
+ PublicKey(Vec<u8>) with accessor public_key,
+ /// If the key is encrypted with a MaxBootLevel key, this is the boot level
+ /// of that key
+ MaxBootLevel(i32) with accessor max_boot_level,
// --- ADD NEW META DATA FIELDS HERE ---
// For backwards compatibility add new entries only to
// end of this list and above this comment.
@@ -201,7 +217,7 @@
let db_tag: i64 = row.get(0).context("Failed to read tag.")?;
metadata.insert(
db_tag,
- BlobMetaEntry::new_from_sql(db_tag, &SqlField::new(1, &row))
+ BlobMetaEntry::new_from_sql(db_tag, &SqlField::new(1, row))
.context("Failed to read BlobMetaEntry.")?,
);
Ok(())
@@ -372,12 +388,12 @@
}
/// Returns unix epoch time in milliseconds.
- pub fn to_millis_epoch(&self) -> i64 {
+ pub fn to_millis_epoch(self) -> i64 {
self.0
}
/// Returns unix epoch time in seconds.
- pub fn to_secs_epoch(&self) -> i64 {
+ pub fn to_secs_epoch(self) -> i64 {
self.0 / 1000
}
}
@@ -581,11 +597,14 @@
/// This type represents a certificate chain with a private key corresponding to the leaf
/// certificate. TODO(jbires): This will be used in a follow-on CL, for now it's used in the tests.
-#[allow(dead_code)]
pub struct CertificateChain {
- private_key: ZVec,
- batch_cert: ZVec,
- cert_chain: ZVec,
+ /// A KM key blob
+ pub private_key: ZVec,
+ /// A batch cert for private_key
+ pub batch_cert: Vec<u8>,
+ /// A full certificate chain from root signing authority to private_key, including batch_cert
+ /// for convenience.
+ pub cert_chain: Vec<u8>,
}
/// This type represents a Keystore 2.0 key entry.
@@ -716,23 +735,29 @@
/// ownership. It also implements all of Keystore 2.0's database functionality.
pub struct KeystoreDB {
conn: Connection,
- gc: Option<Gc>,
+ gc: Option<Arc<Gc>>,
+ perboot: Arc<perboot::PerbootDB>,
}
/// Database representation of the monotonic time retrieved from the system call clock_gettime with
-/// CLOCK_MONOTONIC_RAW. Stores monotonic time as i64 in seconds.
+/// CLOCK_MONOTONIC_RAW. Stores monotonic time as i64 in milliseconds.
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd)]
pub struct MonotonicRawTime(i64);
impl MonotonicRawTime {
/// Constructs a new MonotonicRawTime
pub fn now() -> Self {
- Self(get_current_time_in_seconds())
+ Self(get_current_time_in_milliseconds())
+ }
+
+ /// Returns the value of MonotonicRawTime in milliseconds as i64
+ pub fn milliseconds(&self) -> i64 {
+ self.0
}
/// Returns the integer value of MonotonicRawTime as i64
pub fn seconds(&self) -> i64 {
- self.0
+ self.0 / 1000
}
/// Like i64::checked_sub.
@@ -755,8 +780,10 @@
/// This struct encapsulates the information to be stored in the database about the auth tokens
/// received by keystore.
+#[derive(Clone)]
pub struct AuthTokenEntry {
auth_token: HardwareAuthToken,
+ // Time received in milliseconds
time_received: MonotonicRawTime,
}
@@ -787,6 +814,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.
@@ -796,47 +828,52 @@
impl KeystoreDB {
const UNASSIGNED_KEY_ID: i64 = -1i64;
- const PERBOOT_DB_FILE_NAME: &'static str = &"file:perboot.sqlite?mode=memory&cache=shared";
+ const CURRENT_DB_VERSION: u32 = 1;
+ const UPGRADERS: &'static [fn(&Transaction) -> Result<u32>] = &[Self::from_0_to_1];
- /// The alias of the user super key.
- pub const USER_SUPER_KEY_ALIAS: &'static str = &"USER_SUPER_KEY";
-
- /// This creates a PerBootDbKeepAlive object to keep the per boot database alive.
- pub fn keep_perboot_db_alive() -> Result<PerBootDbKeepAlive> {
- let conn = Connection::open_in_memory()
- .context("In keep_perboot_db_alive: Failed to initialize SQLite connection.")?;
-
- conn.execute("ATTACH DATABASE ? as perboot;", params![Self::PERBOOT_DB_FILE_NAME])
- .context("In keep_perboot_db_alive: Failed to attach database perboot.")?;
- Ok(PerBootDbKeepAlive(conn))
- }
+ /// Name of the file that holds the cross-boot persistent database.
+ pub const PERSISTENT_DB_FILENAME: &'static str = "persistent.sqlite";
/// This will create a new database connection connecting the two
/// files persistent.sqlite and perboot.sqlite in the given directory.
/// It also attempts to initialize all of the tables.
/// KeystoreDB cannot be used by multiple threads.
/// Each thread should open their own connection using `thread_local!`.
- pub fn new(db_root: &Path, gc: Option<Gc>) -> Result<Self> {
- // Build the path to the sqlite file.
- let mut persistent_path = db_root.to_path_buf();
- persistent_path.push("persistent.sqlite");
+ pub fn new(db_root: &Path, gc: Option<Arc<Gc>>) -> Result<Self> {
+ let _wp = wd::watch_millis("KeystoreDB::new", 500);
- // Now convert them to strings prefixed with "file:"
- let mut persistent_path_str = "file:".to_owned();
- persistent_path_str.push_str(&persistent_path.to_string_lossy());
+ let persistent_path = Self::make_persistent_path(db_root)?;
+ let conn = Self::make_connection(&persistent_path)?;
- let conn = Self::make_connection(&persistent_path_str, &Self::PERBOOT_DB_FILE_NAME)?;
-
- // On busy fail Immediately. It is unlikely to succeed given a bug in sqlite.
- conn.busy_handler(None).context("In KeystoreDB::new: Failed to set busy handler.")?;
-
- let mut db = Self { conn, gc };
+ let mut db = Self { conn, gc, perboot: perboot::PERBOOT_DB.clone() };
db.with_transaction(TransactionBehavior::Immediate, |tx| {
+ versioning::upgrade_database(tx, Self::CURRENT_DB_VERSION, Self::UPGRADERS)
+ .context("In KeystoreDB::new: trying to upgrade database.")?;
Self::init_tables(tx).context("Trying to initialize tables.").no_gc()
})?;
Ok(db)
}
+ // This upgrade function deletes all MAX_BOOT_LEVEL keys, that were generated before
+ // cryptographic binding to the boot level keys was implemented.
+ fn from_0_to_1(tx: &Transaction) -> Result<u32> {
+ tx.execute(
+ "UPDATE persistent.keyentry SET state = ?
+ WHERE
+ id IN (SELECT keyentryid FROM persistent.keyparameter WHERE tag = ?)
+ AND
+ id NOT IN (
+ SELECT keyentryid FROM persistent.blobentry
+ WHERE id IN (
+ SELECT blobentryid FROM persistent.blobmetadata WHERE tag = ?
+ )
+ );",
+ params![KeyLifeCycle::Unreferenced, Tag::MAX_BOOT_LEVEL.0, BlobMetaData::MaxBootLevel],
+ )
+ .context("In from_0_to_1: Failed to delete logical boot level keys.")?;
+ Ok(1)
+ }
+
fn init_tables(tx: &Transaction) -> Result<()> {
tx.execute(
"CREATE TABLE IF NOT EXISTS persistent.keyentry (
@@ -944,41 +981,22 @@
)
.context("Failed to initialize \"grant\" table.")?;
- //TODO: only drop the following two perboot tables if this is the first start up
- //during the boot (b/175716626).
- // tx.execute("DROP TABLE IF EXISTS perboot.authtoken;", NO_PARAMS)
- // .context("Failed to drop perboot.authtoken table")?;
- tx.execute(
- "CREATE TABLE IF NOT EXISTS perboot.authtoken (
- id INTEGER PRIMARY KEY,
- challenge INTEGER,
- user_id INTEGER,
- auth_id INTEGER,
- authenticator_type INTEGER,
- timestamp INTEGER,
- mac BLOB,
- time_received INTEGER,
- UNIQUE(user_id, auth_id, authenticator_type));",
- NO_PARAMS,
- )
- .context("Failed to initialize \"authtoken\" table.")?;
-
- // tx.execute("DROP TABLE IF EXISTS perboot.metadata;", NO_PARAMS)
- // .context("Failed to drop perboot.metadata table")?;
- // metadata table stores certain miscellaneous information required for keystore functioning
- // during a boot cycle, as key-value pairs.
- tx.execute(
- "CREATE TABLE IF NOT EXISTS perboot.metadata (
- key TEXT,
- value BLOB,
- UNIQUE(key));",
- NO_PARAMS,
- )
- .context("Failed to initialize \"metadata\" table.")?;
Ok(())
}
- fn make_connection(persistent_file: &str, perboot_file: &str) -> Result<Connection> {
+ fn make_persistent_path(db_root: &Path) -> Result<String> {
+ // Build the path to the sqlite file.
+ let mut persistent_path = db_root.to_path_buf();
+ persistent_path.push(Self::PERSISTENT_DB_FILENAME);
+
+ // Now convert them to strings prefixed with "file:"
+ let mut persistent_path_str = "file:".to_owned();
+ persistent_path_str.push_str(&persistent_path.to_string_lossy());
+
+ Ok(persistent_path_str)
+ }
+
+ fn make_connection(persistent_file: &str) -> Result<Connection> {
let conn =
Connection::open_in_memory().context("Failed to initialize SQLite connection.")?;
@@ -996,70 +1014,178 @@
}
break;
}
- loop {
- if let Err(e) = conn
- .execute("ATTACH DATABASE ? as perboot;", params![perboot_file])
- .context("Failed to attach database perboot.")
- {
- if Self::is_locked_error(&e) {
- std::thread::sleep(std::time::Duration::from_micros(500));
- continue;
- } else {
- return Err(e);
- }
- }
- break;
- }
+
+ // Drop the cache size from default (2M) to 0.5M
+ conn.execute("PRAGMA persistent.cache_size = -500;", params![])
+ .context("Failed to decrease cache size for persistent db")?;
Ok(conn)
}
+ fn do_table_size_query(
+ &mut self,
+ storage_type: MetricsStorage,
+ query: &str,
+ params: &[&str],
+ ) -> Result<StorageStats> {
+ let (total, unused) = self.with_transaction(TransactionBehavior::Deferred, |tx| {
+ tx.query_row(query, params_from_iter(params), |row| Ok((row.get(0)?, row.get(1)?)))
+ .with_context(|| {
+ format!("get_storage_stat: Error size of storage type {}", storage_type.0)
+ })
+ .no_gc()
+ })?;
+ Ok(StorageStats { storage_type, size: total, unused_size: unused })
+ }
+
+ fn get_total_size(&mut self) -> Result<StorageStats> {
+ self.do_table_size_query(
+ MetricsStorage::DATABASE,
+ "SELECT page_count * page_size, freelist_count * page_size
+ FROM pragma_page_count('persistent'),
+ pragma_page_size('persistent'),
+ persistent.pragma_freelist_count();",
+ &[],
+ )
+ }
+
+ fn get_table_size(
+ &mut self,
+ storage_type: MetricsStorage,
+ schema: &str,
+ table: &str,
+ ) -> Result<StorageStats> {
+ self.do_table_size_query(
+ storage_type,
+ "SELECT pgsize,unused FROM dbstat(?1)
+ WHERE name=?2 AND aggregate=TRUE;",
+ &[schema, table],
+ )
+ }
+
+ /// Fetches a storage statisitics atom for a given storage type. For storage
+ /// types that map to a table, information about the table's storage is
+ /// returned. Requests for storage types that are not DB tables return None.
+ pub fn get_storage_stat(&mut self, storage_type: MetricsStorage) -> Result<StorageStats> {
+ let _wp = wd::watch_millis("KeystoreDB::get_storage_stat", 500);
+
+ match storage_type {
+ MetricsStorage::DATABASE => self.get_total_size(),
+ MetricsStorage::KEY_ENTRY => {
+ self.get_table_size(storage_type, "persistent", "keyentry")
+ }
+ MetricsStorage::KEY_ENTRY_ID_INDEX => {
+ self.get_table_size(storage_type, "persistent", "keyentry_id_index")
+ }
+ MetricsStorage::KEY_ENTRY_DOMAIN_NAMESPACE_INDEX => {
+ self.get_table_size(storage_type, "persistent", "keyentry_domain_namespace_index")
+ }
+ MetricsStorage::BLOB_ENTRY => {
+ self.get_table_size(storage_type, "persistent", "blobentry")
+ }
+ MetricsStorage::BLOB_ENTRY_KEY_ENTRY_ID_INDEX => {
+ self.get_table_size(storage_type, "persistent", "blobentry_keyentryid_index")
+ }
+ MetricsStorage::KEY_PARAMETER => {
+ self.get_table_size(storage_type, "persistent", "keyparameter")
+ }
+ MetricsStorage::KEY_PARAMETER_KEY_ENTRY_ID_INDEX => {
+ self.get_table_size(storage_type, "persistent", "keyparameter_keyentryid_index")
+ }
+ MetricsStorage::KEY_METADATA => {
+ self.get_table_size(storage_type, "persistent", "keymetadata")
+ }
+ MetricsStorage::KEY_METADATA_KEY_ENTRY_ID_INDEX => {
+ self.get_table_size(storage_type, "persistent", "keymetadata_keyentryid_index")
+ }
+ MetricsStorage::GRANT => self.get_table_size(storage_type, "persistent", "grant"),
+ MetricsStorage::AUTH_TOKEN => {
+ // Since the table is actually a BTreeMap now, unused_size is not meaningfully
+ // reportable
+ // Size provided is only an approximation
+ Ok(StorageStats {
+ storage_type,
+ size: (self.perboot.auth_tokens_len() * std::mem::size_of::<AuthTokenEntry>())
+ as i32,
+ unused_size: 0,
+ })
+ }
+ MetricsStorage::BLOB_METADATA => {
+ self.get_table_size(storage_type, "persistent", "blobmetadata")
+ }
+ MetricsStorage::BLOB_METADATA_BLOB_ENTRY_ID_INDEX => {
+ self.get_table_size(storage_type, "persistent", "blobmetadata_blobentryid_index")
+ }
+ _ => Err(anyhow::Error::msg(format!("Unsupported storage type: {}", storage_type.0))),
+ }
+ }
+
/// This function is intended to be used by the garbage collector.
- /// It deletes the blob given by `blob_id_to_delete`. It then tries to find a superseded
- /// key blob that might need special handling by the garbage collector.
+ /// It deletes the blobs given by `blob_ids_to_delete`. It then tries to find up to `max_blobs`
+ /// superseded key blobs that might need special handling by the garbage collector.
/// If no further superseded blobs can be found it deletes all other superseded blobs that don't
/// need special handling and returns None.
- pub fn handle_next_superseded_blob(
+ pub fn handle_next_superseded_blobs(
&mut self,
- blob_id_to_delete: Option<i64>,
- ) -> Result<Option<(i64, Vec<u8>, BlobMetaData)>> {
+ blob_ids_to_delete: &[i64],
+ max_blobs: usize,
+ ) -> Result<Vec<(i64, Vec<u8>, BlobMetaData)>> {
+ let _wp = wd::watch_millis("KeystoreDB::handle_next_superseded_blob", 500);
self.with_transaction(TransactionBehavior::Immediate, |tx| {
- // Delete the given blob if one was given.
- if let Some(blob_id_to_delete) = blob_id_to_delete {
+ // Delete the given blobs.
+ for blob_id in blob_ids_to_delete {
tx.execute(
"DELETE FROM persistent.blobmetadata WHERE blobentryid = ?;",
- params![blob_id_to_delete],
+ params![blob_id],
)
.context("Trying to delete blob metadata.")?;
- tx.execute(
- "DELETE FROM persistent.blobentry WHERE id = ?;",
- params![blob_id_to_delete],
- )
- .context("Trying to blob.")?;
+ tx.execute("DELETE FROM persistent.blobentry WHERE id = ?;", params![blob_id])
+ .context("Trying to blob.")?;
}
- // Find another superseded keyblob load its metadata and return it.
- if let Some((blob_id, blob)) = tx
- .query_row(
- "SELECT id, blob FROM persistent.blobentry
- WHERE subcomponent_type = ?
- AND (
- id NOT IN (
- SELECT MAX(id) FROM persistent.blobentry
- WHERE subcomponent_type = ?
- GROUP BY keyentryid, subcomponent_type
- )
- OR keyentryid NOT IN (SELECT id FROM persistent.keyentry)
- );",
- params![SubComponentType::KEY_BLOB, SubComponentType::KEY_BLOB],
- |row| Ok((row.get(0)?, row.get(1)?)),
- )
- .optional()
- .context("Trying to query superseded blob.")?
- {
- let blob_metadata = BlobMetaData::load_from_db(blob_id, tx)
- .context("Trying to load blob metadata.")?;
- return Ok(Some((blob_id, blob, blob_metadata))).no_gc();
+ Self::cleanup_unreferenced(tx).context("Trying to cleanup unreferenced.")?;
+
+ // Find up to max_blobx more superseded key blobs, load their metadata and return it.
+ let result: Vec<(i64, Vec<u8>)> = {
+ let mut stmt = tx
+ .prepare(
+ "SELECT id, blob FROM persistent.blobentry
+ WHERE subcomponent_type = ?
+ AND (
+ id NOT IN (
+ SELECT MAX(id) FROM persistent.blobentry
+ WHERE subcomponent_type = ?
+ GROUP BY keyentryid, subcomponent_type
+ )
+ OR keyentryid NOT IN (SELECT id FROM persistent.keyentry)
+ ) LIMIT ?;",
+ )
+ .context("Trying to prepare query for superseded blobs.")?;
+
+ let rows = stmt
+ .query_map(
+ params![
+ SubComponentType::KEY_BLOB,
+ SubComponentType::KEY_BLOB,
+ max_blobs as i64,
+ ],
+ |row| Ok((row.get(0)?, row.get(1)?)),
+ )
+ .context("Trying to query superseded blob.")?;
+
+ rows.collect::<Result<Vec<(i64, Vec<u8>)>, rusqlite::Error>>()
+ .context("Trying to extract superseded blobs.")?
+ };
+
+ let result = result
+ .into_iter()
+ .map(|(blob_id, blob)| {
+ Ok((blob_id, blob, BlobMetaData::load_from_db(blob_id, tx)?))
+ })
+ .collect::<Result<Vec<(i64, Vec<u8>, BlobMetaData)>>>()
+ .context("Trying to load blob metadata.")?;
+ if !result.is_empty() {
+ return Ok(result).no_gc();
}
// We did not find any superseded key blob, so let's remove other superseded blob in
@@ -1078,9 +1204,9 @@
)
.context("Trying to purge superseded blobs.")?;
- Ok(None).no_gc()
+ Ok(vec![]).no_gc()
})
- .context("In handle_next_superseded_blob.")
+ .context("In handle_next_superseded_blobs.")
}
/// This maintenance function should be called only once before the database is used for the
@@ -1092,6 +1218,8 @@
/// Unlike with `mark_unreferenced`, we don't need to purge grants, because only keys that made
/// it to `KeyLifeCycle::Live` may have grants.
pub fn cleanup_leftovers(&mut self) -> Result<usize> {
+ let _wp = wd::watch_millis("KeystoreDB::cleanup_leftovers", 500);
+
self.with_transaction(TransactionBehavior::Immediate, |tx| {
tx.execute(
"UPDATE persistent.keyentry SET state = ? WHERE state = ?;",
@@ -1111,10 +1239,12 @@
alias: &str,
key_type: KeyType,
) -> Result<bool> {
+ let _wp = wd::watch_millis("KeystoreDB::key_exists", 500);
+
self.with_transaction(TransactionBehavior::Immediate, |tx| {
let key_descriptor =
KeyDescriptor { domain, nspace, alias: Some(alias.to_string()), blob: None };
- let result = Self::load_key_entry_id(&tx, &key_descriptor, key_type);
+ let result = Self::load_key_entry_id(tx, &key_descriptor, key_type);
match result {
Ok(_) => Ok(true),
Err(error) => match error.root_cause().downcast_ref::<KsError>() {
@@ -1131,8 +1261,13 @@
pub fn store_super_key(
&mut self,
user_id: u32,
- blob_info: &(&[u8], &BlobMetaData),
+ key_type: &SuperKeyType,
+ blob: &[u8],
+ blob_metadata: &BlobMetaData,
+ key_metadata: &KeyMetaData,
) -> Result<KeyEntry> {
+ let _wp = wd::watch_millis("KeystoreDB::store_super_key", 500);
+
self.with_transaction(TransactionBehavior::Immediate, |tx| {
let key_id = Self::insert_with_retry(|id| {
tx.execute(
@@ -1144,7 +1279,7 @@
KeyType::Super,
Domain::APP.0,
user_id as i64,
- Self::USER_SUPER_KEY_ALIAS,
+ key_type.alias,
KeyLifeCycle::Live,
&KEYSTORE_UUID,
],
@@ -1152,9 +1287,10 @@
})
.context("Failed to insert into keyentry table.")?;
- let (blob, blob_metadata) = *blob_info;
+ key_metadata.store_in_db(key_id, tx).context("KeyMetaData::store_in_db failed")?;
+
Self::set_blob_internal(
- &tx,
+ tx,
key_id,
SubComponentType::KEY_BLOB,
Some(blob),
@@ -1170,18 +1306,24 @@
}
/// Loads super key of a given user, if exists
- pub fn load_super_key(&mut self, user_id: u32) -> Result<Option<(KeyIdGuard, KeyEntry)>> {
+ pub fn load_super_key(
+ &mut self,
+ key_type: &SuperKeyType,
+ user_id: u32,
+ ) -> Result<Option<(KeyIdGuard, KeyEntry)>> {
+ let _wp = wd::watch_millis("KeystoreDB::load_super_key", 500);
+
self.with_transaction(TransactionBehavior::Immediate, |tx| {
let key_descriptor = KeyDescriptor {
domain: Domain::APP,
nspace: user_id as i64,
- alias: Some(String::from("USER_SUPER_KEY")),
+ alias: Some(key_type.alias.into()),
blob: None,
};
- let id = Self::load_key_entry_id(&tx, &key_descriptor, KeyType::Super);
+ let id = Self::load_key_entry_id(tx, &key_descriptor, KeyType::Super);
match id {
Ok(id) => {
- let key_entry = Self::load_key_components(&tx, KeyEntryLoadBits::KM, id)
+ let key_entry = Self::load_key_components(tx, KeyEntryLoadBits::KM, id)
.context("In load_super_key. Failed to load key entry.")?;
Ok(Some((KEY_ID_LOCK.get(id), key_entry)))
}
@@ -1210,6 +1352,8 @@
where
F: Fn() -> Result<(Vec<u8>, BlobMetaData)>,
{
+ let _wp = wd::watch_millis("KeystoreDB::get_or_create_key_with", 500);
+
self.with_transaction(TransactionBehavior::Immediate, |tx| {
let id = {
let mut stmt = tx
@@ -1239,7 +1383,7 @@
let (id, entry) = match id {
Some(id) => (
id,
- Self::load_key_components(&tx, KeyEntryLoadBits::KM, id)
+ Self::load_key_components(tx, KeyEntryLoadBits::KM, id)
.context("In get_or_create_key_with.")?,
),
@@ -1265,13 +1409,13 @@
let (blob, metadata) =
create_new_key().context("In get_or_create_key_with.")?;
Self::set_blob_internal(
- &tx,
+ tx,
id,
SubComponentType::KEY_BLOB,
Some(&blob),
Some(&metadata),
)
- .context("In get_of_create_key_with.")?;
+ .context("In get_or_create_key_with.")?;
(
id,
KeyEntry {
@@ -1288,18 +1432,6 @@
.context("In get_or_create_key_with.")
}
- /// SQLite3 seems to hold a shared mutex while running the busy handler when
- /// waiting for the database file to become available. This makes it
- /// impossible to successfully recover from a locked database when the
- /// transaction holding the device busy is in the same process on a
- /// different connection. As a result the busy handler has to time out and
- /// fail in order to make progress.
- ///
- /// Instead, we set the busy handler to None (return immediately). And catch
- /// Busy and Locked errors (the latter occur on in memory databases with
- /// shared cache, e.g., the per-boot database.) and restart the transaction
- /// after a grace period of half a millisecond.
- ///
/// Creates a transaction with the given behavior and executes f with the new transaction.
/// The transaction is committed only if f returns Ok and retried if DatabaseBusy
/// or DatabaseLocked is encountered.
@@ -1339,15 +1471,11 @@
}
fn is_locked_error(e: &anyhow::Error) -> bool {
- matches!(e.root_cause().downcast_ref::<rusqlite::ffi::Error>(),
- Some(rusqlite::ffi::Error {
- code: rusqlite::ErrorCode::DatabaseBusy,
- ..
- })
- | Some(rusqlite::ffi::Error {
- code: rusqlite::ErrorCode::DatabaseLocked,
- ..
- }))
+ matches!(
+ e.root_cause().downcast_ref::<rusqlite::ffi::Error>(),
+ Some(rusqlite::ffi::Error { code: rusqlite::ErrorCode::DatabaseBusy, .. })
+ | Some(rusqlite::ffi::Error { code: rusqlite::ErrorCode::DatabaseLocked, .. })
+ )
}
/// Creates a new key entry and allocates a new randomized id for the new key.
@@ -1360,10 +1488,13 @@
&mut self,
domain: &Domain,
namespace: &i64,
+ key_type: KeyType,
km_uuid: &Uuid,
) -> Result<KeyIdGuard> {
+ let _wp = wd::watch_millis("KeystoreDB::create_key_entry", 500);
+
self.with_transaction(TransactionBehavior::Immediate, |tx| {
- Self::create_key_entry_internal(tx, domain, namespace, km_uuid).no_gc()
+ Self::create_key_entry_internal(tx, domain, namespace, key_type, km_uuid).no_gc()
})
.context("In create_key_entry.")
}
@@ -1372,6 +1503,7 @@
tx: &Transaction,
domain: &Domain,
namespace: &i64,
+ key_type: KeyType,
km_uuid: &Uuid,
) -> Result<KeyIdGuard> {
match *domain {
@@ -1389,7 +1521,7 @@
VALUES(?, ?, ?, ?, NULL, ?, ?);",
params![
id,
- KeyType::Client,
+ key_type,
domain.0 as u32,
*namespace,
KeyLifeCycle::Existing,
@@ -1413,6 +1545,8 @@
private_key: &[u8],
km_uuid: &Uuid,
) -> Result<()> {
+ let _wp = wd::watch_millis("KeystoreDB::create_attestation_key_entry", 500);
+
self.with_transaction(TransactionBehavior::Immediate, |tx| {
let key_id = KEY_ID_LOCK.get(
Self::insert_with_retry(|id| {
@@ -1426,7 +1560,7 @@
.context("In create_key_entry")?,
);
Self::set_blob_internal(
- &tx,
+ tx,
key_id.0,
SubComponentType::KEY_BLOB,
Some(private_key),
@@ -1435,7 +1569,7 @@
let mut metadata = KeyMetaData::new();
metadata.add(KeyMetaEntry::AttestationMacedPublicKey(maced_public_key.to_vec()));
metadata.add(KeyMetaEntry::AttestationRawPubKey(raw_public_key.to_vec()));
- metadata.store_in_db(key_id.0, &tx)?;
+ metadata.store_in_db(key_id.0, tx)?;
Ok(()).no_gc()
})
.context("In create_attestation_key_entry")
@@ -1455,8 +1589,10 @@
blob: Option<&[u8]>,
blob_metadata: Option<&BlobMetaData>,
) -> Result<()> {
+ let _wp = wd::watch_millis("KeystoreDB::set_blob", 500);
+
self.with_transaction(TransactionBehavior::Immediate, |tx| {
- Self::set_blob_internal(&tx, key_id.0, sc_type, blob, blob_metadata).need_gc()
+ Self::set_blob_internal(tx, key_id.0, sc_type, blob, blob_metadata).need_gc()
})
.context("In set_blob.")
}
@@ -1466,9 +1602,11 @@
/// We use this to insert key blobs into the database which can then be garbage collected
/// lazily by the key garbage collector.
pub fn set_deleted_blob(&mut self, blob: &[u8], blob_metadata: &BlobMetaData) -> Result<()> {
+ let _wp = wd::watch_millis("KeystoreDB::set_deleted_blob", 500);
+
self.with_transaction(TransactionBehavior::Immediate, |tx| {
Self::set_blob_internal(
- &tx,
+ tx,
Self::UNASSIGNED_KEY_ID,
SubComponentType::KEY_BLOB,
Some(blob),
@@ -1561,7 +1699,7 @@
#[cfg(test)]
fn insert_key_metadata(&mut self, key_id: &KeyIdGuard, metadata: &KeyMetaData) -> Result<()> {
self.with_transaction(TransactionBehavior::Immediate, |tx| {
- metadata.store_in_db(key_id.0, &tx).no_gc()
+ metadata.store_in_db(key_id.0, tx).no_gc()
})
.context("In insert_key_metadata.")
}
@@ -1576,6 +1714,8 @@
expiration_date: i64,
km_uuid: &Uuid,
) -> Result<()> {
+ let _wp = wd::watch_millis("KeystoreDB::store_signed_attestation_certificate_chain", 500);
+
self.with_transaction(TransactionBehavior::Immediate, |tx| {
let mut stmt = tx
.prepare(
@@ -1621,16 +1761,16 @@
metadata.add(KeyMetaEntry::AttestationExpirationDate(DateTime::from_millis_epoch(
expiration_date,
)));
- metadata.store_in_db(key_id, &tx).context("Failed to insert key metadata.")?;
+ metadata.store_in_db(key_id, tx).context("Failed to insert key metadata.")?;
Self::set_blob_internal(
- &tx,
+ tx,
key_id,
SubComponentType::CERT_CHAIN,
Some(cert_chain),
None,
)
.context("Failed to insert cert chain")?;
- Self::set_blob_internal(&tx, key_id, SubComponentType::CERT, Some(batch_cert), None)
+ Self::set_blob_internal(tx, key_id, SubComponentType::CERT, Some(batch_cert), None)
.context("Failed to insert cert")?;
Ok(()).no_gc()
})
@@ -1645,6 +1785,8 @@
namespace: i64,
km_uuid: &Uuid,
) -> Result<()> {
+ let _wp = wd::watch_millis("KeystoreDB::assign_attestation_key", 500);
+
match domain {
Domain::APP | Domain::SELINUX => {}
_ => {
@@ -1688,11 +1830,12 @@
],
)
.context("Failed to assign attestation key")?;
- if result != 1 {
- return Err(KsError::sys()).context(format!(
- "Expected to update a single entry but instead updated {}.",
- result
- ));
+ if result == 0 {
+ log_rkp_error_stats(MetricsRkpError::OUT_OF_KEYS);
+ return Err(KsError::Rc(ResponseCode::OUT_OF_KEYS)).context("Out of keys.");
+ } else if result > 1 {
+ return Err(KsError::sys())
+ .context(format!("Expected to update 1 entry, instead updated {}", result));
}
Ok(()).no_gc()
})
@@ -1707,6 +1850,8 @@
num_keys: i32,
km_uuid: &Uuid,
) -> Result<Vec<Vec<u8>>> {
+ let _wp = wd::watch_millis("KeystoreDB::fetch_unsigned_attestation_keys", 500);
+
self.with_transaction(TransactionBehavior::Immediate, |tx| {
let mut stmt = tx
.prepare(
@@ -1732,7 +1877,7 @@
km_uuid,
num_keys
],
- |row| Ok(row.get(0)?),
+ |row| row.get(0),
)?
.collect::<rusqlite::Result<Vec<Vec<u8>>>>()
.context("Failed to execute statement")?;
@@ -1744,6 +1889,8 @@
/// Removes any keys that have expired as of the current time. Returns the number of keys
/// marked unreferenced that are bound to be garbage collected.
pub fn delete_expired_attestation_keys(&mut self) -> Result<i32> {
+ let _wp = wd::watch_millis("KeystoreDB::delete_expired_attestation_keys", 500);
+
self.with_transaction(TransactionBehavior::Immediate, |tx| {
let mut stmt = tx
.prepare(
@@ -1767,7 +1914,7 @@
);
let mut num_deleted = 0;
for id in key_ids_to_check.iter().filter(|kt| kt.1 < curr_time).map(|kt| kt.0) {
- if Self::mark_unreferenced(&tx, id)? {
+ if Self::mark_unreferenced(tx, id)? {
num_deleted += 1;
}
}
@@ -1776,6 +1923,35 @@
.context("In delete_expired_attestation_keys: ")
}
+ /// Deletes all remotely provisioned attestation keys in the system, regardless of the state
+ /// they are in. This is useful primarily as a testing mechanism.
+ pub fn delete_all_attestation_keys(&mut self) -> Result<i64> {
+ let _wp = wd::watch_millis("KeystoreDB::delete_all_attestation_keys", 500);
+
+ self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ let mut stmt = tx
+ .prepare(
+ "SELECT id FROM persistent.keyentry
+ WHERE key_type IS ?;",
+ )
+ .context("Failed to prepare statement")?;
+ let keys_to_delete = stmt
+ .query_map(params![KeyType::Attestation], |row| row.get(0))?
+ .collect::<rusqlite::Result<Vec<i64>>>()
+ .context("Failed to execute statement")?;
+ let num_deleted = keys_to_delete
+ .iter()
+ .map(|id| Self::mark_unreferenced(tx, *id))
+ .collect::<Result<Vec<bool>>>()
+ .context("Failed to execute mark_unreferenced on a keyid")?
+ .into_iter()
+ .filter(|result| *result)
+ .count() as i64;
+ Ok(num_deleted).do_gc(num_deleted != 0)
+ })
+ .context("In delete_all_attestation_keys: ")
+ }
+
/// Counts the number of keys that will expire by the provided epoch date and the number of
/// keys not currently assigned to a domain.
pub fn get_attestation_pool_status(
@@ -1783,6 +1959,8 @@
date: i64,
km_uuid: &Uuid,
) -> Result<AttestationPoolStatus> {
+ let _wp = wd::watch_millis("KeystoreDB::get_attestation_pool_status", 500);
+
self.with_transaction(TransactionBehavior::Immediate, |tx| {
let mut stmt = tx.prepare(
"SELECT data
@@ -1803,7 +1981,7 @@
km_uuid,
KeyLifeCycle::Live
],
- |row| Ok(row.get(0)?),
+ |row| row.get(0),
)?
.collect::<rusqlite::Result<Vec<DateTime>>>()
.context("Failed to execute metadata statement")?;
@@ -1850,6 +2028,8 @@
namespace: i64,
km_uuid: &Uuid,
) -> Result<Option<CertificateChain>> {
+ let _wp = wd::watch_millis("KeystoreDB::retrieve_attestation_key_and_cert_chain", 500);
+
match domain {
Domain::APP | Domain::SELINUX => {}
_ => {
@@ -1914,8 +2094,8 @@
}
Ok(Some(CertificateChain {
private_key: ZVec::try_from(km_blob)?,
- batch_cert: ZVec::try_from(batch_cert_blob)?,
- cert_chain: ZVec::try_from(cert_chain_blob)?,
+ batch_cert: batch_cert_blob,
+ cert_chain: cert_chain_blob,
}))
.no_gc()
})
@@ -1933,6 +2113,7 @@
alias: &str,
domain: &Domain,
namespace: &i64,
+ key_type: KeyType,
) -> Result<bool> {
match *domain {
Domain::APP | Domain::SELINUX => {}
@@ -1947,15 +2128,15 @@
.execute(
"UPDATE persistent.keyentry
SET alias = NULL, domain = NULL, namespace = NULL, state = ?
- WHERE alias = ? AND domain = ? AND namespace = ?;",
- params![KeyLifeCycle::Unreferenced, alias, domain.0 as u32, namespace],
+ WHERE alias = ? AND domain = ? AND namespace = ? AND key_type = ?;",
+ params![KeyLifeCycle::Unreferenced, alias, domain.0 as u32, namespace, key_type],
)
.context("In rebind_alias: Failed to rebind existing entry.")?;
let result = tx
.execute(
"UPDATE persistent.keyentry
SET alias = ?, state = ?
- WHERE id = ? AND domain = ? AND namespace = ? AND state = ?;",
+ WHERE id = ? AND domain = ? AND namespace = ? AND state = ? AND key_type = ?;",
params![
alias,
KeyLifeCycle::Live,
@@ -1963,6 +2144,7 @@
domain.0 as u32,
*namespace,
KeyLifeCycle::Existing,
+ key_type,
],
)
.context("In rebind_alias: Failed to set alias.")?;
@@ -1975,20 +2157,89 @@
Ok(updated != 0)
}
+ /// Moves the key given by KeyIdGuard to the new location at `destination`. If the destination
+ /// is already occupied by a key, this function fails with `ResponseCode::INVALID_ARGUMENT`.
+ pub fn migrate_key_namespace(
+ &mut self,
+ key_id_guard: KeyIdGuard,
+ destination: &KeyDescriptor,
+ caller_uid: u32,
+ check_permission: impl Fn(&KeyDescriptor) -> Result<()>,
+ ) -> Result<()> {
+ let _wp = wd::watch_millis("KeystoreDB::migrate_key_namespace", 500);
+
+ let destination = match destination.domain {
+ Domain::APP => KeyDescriptor { nspace: caller_uid as i64, ..(*destination).clone() },
+ Domain::SELINUX => (*destination).clone(),
+ domain => {
+ return Err(KsError::Rc(ResponseCode::INVALID_ARGUMENT))
+ .context(format!("Domain {:?} must be either APP or SELINUX.", domain));
+ }
+ };
+
+ // Security critical: Must return immediately on failure. Do not remove the '?';
+ check_permission(&destination)
+ .context("In migrate_key_namespace: Trying to check permission.")?;
+
+ let alias = destination
+ .alias
+ .as_ref()
+ .ok_or(KsError::Rc(ResponseCode::INVALID_ARGUMENT))
+ .context("In migrate_key_namespace: Alias must be specified.")?;
+
+ self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ // Query the destination location. If there is a key, the migration request fails.
+ if tx
+ .query_row(
+ "SELECT id FROM persistent.keyentry
+ WHERE alias = ? AND domain = ? AND namespace = ?;",
+ params![alias, destination.domain.0, destination.nspace],
+ |_| Ok(()),
+ )
+ .optional()
+ .context("Failed to query destination.")?
+ .is_some()
+ {
+ return Err(KsError::Rc(ResponseCode::INVALID_ARGUMENT))
+ .context("Target already exists.");
+ }
+
+ let updated = tx
+ .execute(
+ "UPDATE persistent.keyentry
+ SET alias = ?, domain = ?, namespace = ?
+ WHERE id = ?;",
+ params![alias, destination.domain.0, destination.nspace, key_id_guard.id()],
+ )
+ .context("Failed to update key entry.")?;
+
+ if updated != 1 {
+ return Err(KsError::sys())
+ .context(format!("Update succeeded, but {} rows were updated.", updated));
+ }
+ Ok(()).no_gc()
+ })
+ .context("In migrate_key_namespace:")
+ }
+
/// Store a new key in a single transaction.
/// The function creates a new key entry, populates the blob, key parameter, and metadata
/// fields, and rebinds the given alias to the new key.
/// The boolean returned is a hint for the garbage collector. If true, a key was replaced,
/// is now unreferenced and needs to be collected.
+ #[allow(clippy::too_many_arguments)]
pub fn store_new_key(
&mut self,
key: &KeyDescriptor,
+ key_type: KeyType,
params: &[KeyParameter],
blob_info: &(&[u8], &BlobMetaData),
cert_info: &CertificateInfo,
metadata: &KeyMetaData,
km_uuid: &Uuid,
) -> Result<KeyIdGuard> {
+ let _wp = wd::watch_millis("KeystoreDB::store_new_key", 500);
+
let (alias, domain, namespace) = match key {
KeyDescriptor { alias: Some(alias), domain: Domain::APP, nspace, blob: None }
| KeyDescriptor { alias: Some(alias), domain: Domain::SELINUX, nspace, blob: None } => {
@@ -2000,7 +2251,7 @@
}
};
self.with_transaction(TransactionBehavior::Immediate, |tx| {
- let key_id = Self::create_key_entry_internal(tx, &domain, namespace, km_uuid)
+ let key_id = Self::create_key_entry_internal(tx, &domain, namespace, key_type, km_uuid)
.context("Trying to create new key entry.")?;
let (blob, blob_metadata) = *blob_info;
Self::set_blob_internal(
@@ -2008,11 +2259,11 @@
key_id.id(),
SubComponentType::KEY_BLOB,
Some(blob),
- Some(&blob_metadata),
+ Some(blob_metadata),
)
.context("Trying to insert the key blob.")?;
if let Some(cert) = &cert_info.cert {
- Self::set_blob_internal(tx, key_id.id(), SubComponentType::CERT, Some(&cert), None)
+ Self::set_blob_internal(tx, key_id.id(), SubComponentType::CERT, Some(cert), None)
.context("Trying to insert the certificate.")?;
}
if let Some(cert_chain) = &cert_info.cert_chain {
@@ -2020,7 +2271,7 @@
tx,
key_id.id(),
SubComponentType::CERT_CHAIN,
- Some(&cert_chain),
+ Some(cert_chain),
None,
)
.context("Trying to insert the certificate chain.")?;
@@ -2028,7 +2279,7 @@
Self::insert_keyparameter_internal(tx, &key_id, params)
.context("Trying to insert key parameters.")?;
metadata.store_in_db(key_id.id(), tx).context("Trying to insert key metadata.")?;
- let need_gc = Self::rebind_alias(tx, &key_id, &alias, &domain, namespace)
+ let need_gc = Self::rebind_alias(tx, &key_id, alias, &domain, namespace, key_type)
.context("Trying to rebind alias.")?;
Ok(key_id).do_gc(need_gc)
})
@@ -2041,9 +2292,12 @@
pub fn store_new_certificate(
&mut self,
key: &KeyDescriptor,
+ key_type: KeyType,
cert: &[u8],
km_uuid: &Uuid,
) -> Result<KeyIdGuard> {
+ let _wp = wd::watch_millis("KeystoreDB::store_new_certificate", 500);
+
let (alias, domain, namespace) = match key {
KeyDescriptor { alias: Some(alias), domain: Domain::APP, nspace, blob: None }
| KeyDescriptor { alias: Some(alias), domain: Domain::SELINUX, nspace, blob: None } => {
@@ -2056,7 +2310,7 @@
}
};
self.with_transaction(TransactionBehavior::Immediate, |tx| {
- let key_id = Self::create_key_entry_internal(tx, &domain, namespace, km_uuid)
+ let key_id = Self::create_key_entry_internal(tx, &domain, namespace, key_type, km_uuid)
.context("Trying to create new key entry.")?;
Self::set_blob_internal(
@@ -2075,7 +2329,7 @@
metadata.store_in_db(key_id.id(), tx).context("Trying to insert key metadata.")?;
- let need_gc = Self::rebind_alias(tx, &key_id, &alias, &domain, namespace)
+ let need_gc = Self::rebind_alias(tx, &key_id, alias, &domain, namespace, key_type)
.context("Trying to rebind alias.")?;
Ok(key_id).do_gc(need_gc)
})
@@ -2144,7 +2398,7 @@
if access_key.domain == Domain::APP {
access_key.nspace = caller_uid as i64;
}
- let key_id = Self::load_key_entry_id(&tx, &access_key, key_type)
+ let key_id = Self::load_key_entry_id(tx, &access_key, key_type)
.with_context(|| format!("With key.domain = {:?}.", access_key.domain))?;
Ok((key_id, access_key, None))
@@ -2156,11 +2410,12 @@
let mut stmt = tx
.prepare(
"SELECT keyentryid, access_vector FROM persistent.grant
- WHERE grantee = ? AND id = ?;",
+ WHERE grantee = ? AND id = ? AND
+ (SELECT state FROM persistent.keyentry WHERE id = keyentryid) = ?;",
)
.context("Domain::GRANT prepare statement failed")?;
let mut rows = stmt
- .query(params![caller_uid as i64, key.nspace])
+ .query(params![caller_uid as i64, key.nspace, KeyLifeCycle::Live])
.context("Domain:Grant: query failed.")?;
let (key_id, access_vector): (i64, i32) =
db_utils::with_rows_extract_one(&mut rows, |row| {
@@ -2308,7 +2563,7 @@
let tag = Tag(row.get(0).context("Failed to read tag.")?);
let sec_level = SecurityLevel(row.get(2).context("Failed to read sec_level.")?);
parameters.push(
- KeyParameter::new_from_sql(tag, &SqlField::new(1, &row), sec_level)
+ KeyParameter::new_from_sql(tag, &SqlField::new(1, row), sec_level)
.context("Failed to read KeyParameter.")?,
);
Ok(())
@@ -2323,6 +2578,8 @@
/// zero, the key also gets marked unreferenced and scheduled for deletion.
/// Returns Ok(true) if the key was marked unreferenced as a hint to the garbage collector.
pub fn check_and_update_key_usage_count(&mut self, key_id: i64) -> Result<()> {
+ let _wp = wd::watch_millis("KeystoreDB::check_and_update_key_usage_count", 500);
+
self.with_transaction(TransactionBehavior::Immediate, |tx| {
let limit: Option<i32> = tx
.query_row(
@@ -2369,6 +2626,8 @@
caller_uid: u32,
check_permission: impl Fn(&KeyDescriptor, Option<KeyPermSet>) -> Result<()>,
) -> Result<(KeyIdGuard, KeyEntry)> {
+ let _wp = wd::watch_millis("KeystoreDB::load_key_entry", 500);
+
loop {
match self.load_key_entry_internal(
key,
@@ -2496,6 +2755,8 @@
caller_uid: u32,
check_permission: impl Fn(&KeyDescriptor, Option<KeyPermSet>) -> Result<()>,
) -> Result<()> {
+ let _wp = wd::watch_millis("KeystoreDB::unbind_key", 500);
+
self.with_transaction(TransactionBehavior::Immediate, |tx| {
let (key_id, access_key_descriptor, access_vector) =
Self::load_access_tuple(tx, key, key_type, caller_uid)
@@ -2525,6 +2786,8 @@
/// Delete all artifacts belonging to the namespace given by the domain-namespace tuple.
/// This leaves all of the blob entries orphaned for subsequent garbage collection.
pub fn unbind_keys_for_namespace(&mut self, domain: Domain, namespace: i64) -> Result<()> {
+ let _wp = wd::watch_millis("KeystoreDB::unbind_keys_for_namespace", 500);
+
if !(domain == Domain::APP || domain == Domain::SELINUX) {
return Err(KsError::Rc(ResponseCode::INVALID_ARGUMENT))
.context("In unbind_keys_for_namespace.");
@@ -2534,32 +2797,33 @@
"DELETE FROM persistent.keymetadata
WHERE keyentryid IN (
SELECT id FROM persistent.keyentry
- WHERE domain = ? AND namespace = ?
+ WHERE domain = ? AND namespace = ? AND key_type = ?
);",
- params![domain.0, namespace],
+ params![domain.0, namespace, KeyType::Client],
)
.context("Trying to delete keymetadata.")?;
tx.execute(
"DELETE FROM persistent.keyparameter
WHERE keyentryid IN (
SELECT id FROM persistent.keyentry
- WHERE domain = ? AND namespace = ?
+ WHERE domain = ? AND namespace = ? AND key_type = ?
);",
- params![domain.0, namespace],
+ params![domain.0, namespace, KeyType::Client],
)
.context("Trying to delete keyparameters.")?;
tx.execute(
"DELETE FROM persistent.grant
WHERE keyentryid IN (
SELECT id FROM persistent.keyentry
- WHERE domain = ? AND namespace = ?
+ WHERE domain = ? AND namespace = ? AND key_type = ?
);",
- params![domain.0, namespace],
+ params![domain.0, namespace, KeyType::Client],
)
.context("Trying to delete grants.")?;
tx.execute(
- "DELETE FROM persistent.keyentry WHERE domain = ? AND namespace = ?;",
- params![domain.0, namespace],
+ "DELETE FROM persistent.keyentry
+ WHERE domain = ? AND namespace = ? AND key_type = ?;",
+ params![domain.0, namespace, KeyType::Client],
)
.context("Trying to delete keyentry.")?;
Ok(()).need_gc()
@@ -2567,6 +2831,47 @@
.context("In unbind_keys_for_namespace")
}
+ fn cleanup_unreferenced(tx: &Transaction) -> Result<()> {
+ let _wp = wd::watch_millis("KeystoreDB::cleanup_unreferenced", 500);
+ {
+ tx.execute(
+ "DELETE FROM persistent.keymetadata
+ WHERE keyentryid IN (
+ SELECT id FROM persistent.keyentry
+ WHERE state = ?
+ );",
+ params![KeyLifeCycle::Unreferenced],
+ )
+ .context("Trying to delete keymetadata.")?;
+ tx.execute(
+ "DELETE FROM persistent.keyparameter
+ WHERE keyentryid IN (
+ SELECT id FROM persistent.keyentry
+ WHERE state = ?
+ );",
+ params![KeyLifeCycle::Unreferenced],
+ )
+ .context("Trying to delete keyparameters.")?;
+ tx.execute(
+ "DELETE FROM persistent.grant
+ WHERE keyentryid IN (
+ SELECT id FROM persistent.keyentry
+ WHERE state = ?
+ );",
+ params![KeyLifeCycle::Unreferenced],
+ )
+ .context("Trying to delete grants.")?;
+ tx.execute(
+ "DELETE FROM persistent.keyentry
+ WHERE state = ?;",
+ params![KeyLifeCycle::Unreferenced],
+ )
+ .context("Trying to delete keyentry.")?;
+ Result::<()>::Ok(())
+ }
+ .context("In cleanup_unreferenced")
+ }
+
/// Delete the keys created on behalf of the user, denoted by the user id.
/// Delete all the keys unless 'keep_non_super_encrypted_keys' set to true.
/// Returned boolean is to hint the garbage collector to delete the unbound keys.
@@ -2576,6 +2881,8 @@
user_id: u32,
keep_non_super_encrypted_keys: bool,
) -> Result<()> {
+ let _wp = wd::watch_millis("KeystoreDB::unbind_keys_for_user", 500);
+
self.with_transaction(TransactionBehavior::Immediate, |tx| {
let mut stmt = tx
.prepare(&format!(
@@ -2608,7 +2915,7 @@
// OR super key:
KeyType::Super,
user_id,
- Self::USER_SUPER_KEY_ALIAS,
+ USER_SUPER_KEY.alias,
KeyLifeCycle::Live
])
.context("In unbind_keys_for_user. Failed to query the keys created by apps.")?;
@@ -2634,7 +2941,7 @@
}
}
}
- notify_gc = Self::mark_unreferenced(&tx, key_id)
+ notify_gc = Self::mark_unreferenced(tx, key_id)
.context("In unbind_keys_for_user.")?
|| notify_gc;
}
@@ -2648,16 +2955,15 @@
load_bits: KeyEntryLoadBits,
key_id: i64,
) -> Result<KeyEntry> {
- let metadata = KeyMetaData::load_from_db(key_id, &tx).context("In load_key_components.")?;
+ let metadata = KeyMetaData::load_from_db(key_id, tx).context("In load_key_components.")?;
let (has_km_blob, key_blob_info, cert_blob, cert_chain_blob) =
- Self::load_blob_components(key_id, load_bits, &tx)
- .context("In load_key_components.")?;
+ Self::load_blob_components(key_id, load_bits, tx).context("In load_key_components.")?;
- let parameters = Self::load_key_parameters(key_id, &tx)
+ let parameters = Self::load_key_parameters(key_id, tx)
.context("In load_key_components: Trying to load key parameters.")?;
- let km_uuid = Self::get_key_km_uuid(&tx, key_id)
+ let km_uuid = Self::get_key_km_uuid(tx, key_id)
.context("In load_key_components: Trying to get KM uuid.")?;
Ok(KeyEntry {
@@ -2675,17 +2981,28 @@
/// Returns a list of KeyDescriptors in the selected domain/namespace.
/// The key descriptors will have the domain, nspace, and alias field set.
/// Domain must be APP or SELINUX, the caller must make sure of that.
- pub fn list(&mut self, domain: Domain, namespace: i64) -> Result<Vec<KeyDescriptor>> {
+ pub fn list(
+ &mut self,
+ domain: Domain,
+ namespace: i64,
+ key_type: KeyType,
+ ) -> Result<Vec<KeyDescriptor>> {
+ let _wp = wd::watch_millis("KeystoreDB::list", 500);
+
self.with_transaction(TransactionBehavior::Deferred, |tx| {
let mut stmt = tx
.prepare(
"SELECT alias FROM persistent.keyentry
- WHERE domain = ? AND namespace = ? AND alias IS NOT NULL AND state = ?;",
+ WHERE domain = ?
+ AND namespace = ?
+ AND alias IS NOT NULL
+ AND state = ?
+ AND key_type = ?;",
)
.context("In list: Failed to prepare.")?;
let mut rows = stmt
- .query(params![domain.0 as u32, namespace, KeyLifeCycle::Live])
+ .query(params![domain.0 as u32, namespace, KeyLifeCycle::Live, key_type])
.context("In list: Failed to query.")?;
let mut descriptors: Vec<KeyDescriptor> = Vec::new();
@@ -2717,6 +3034,8 @@
access_vector: KeyPermSet,
check_permission: impl Fn(&KeyDescriptor, &KeyPermSet) -> Result<()>,
) -> Result<KeyDescriptor> {
+ let _wp = wd::watch_millis("KeystoreDB::grant", 500);
+
self.with_transaction(TransactionBehavior::Immediate, |tx| {
// Load the key_id and complete the access control tuple.
// We ignore the access vector here because grants cannot be granted.
@@ -2728,7 +3047,7 @@
// But even if we load the access tuple by grant here, the permission
// check denies the attempt to create a grant by grant descriptor.
let (key_id, access_key_descriptor, _) =
- Self::load_access_tuple(&tx, key, KeyType::Client, caller_uid)
+ Self::load_access_tuple(tx, key, KeyType::Client, caller_uid)
.context("In grant")?;
// Perform access control. It is vital that we return here if the permission
@@ -2782,11 +3101,13 @@
grantee_uid: u32,
check_permission: impl Fn(&KeyDescriptor) -> Result<()>,
) -> Result<()> {
+ let _wp = wd::watch_millis("KeystoreDB::ungrant", 500);
+
self.with_transaction(TransactionBehavior::Immediate, |tx| {
// Load the key_id and complete the access control tuple.
// We ignore the access vector here because grants cannot be granted.
let (key_id, access_key_descriptor, _) =
- Self::load_access_tuple(&tx, key, KeyType::Client, caller_uid)
+ Self::load_access_tuple(tx, key, KeyType::Client, caller_uid)
.context("In ungrant.")?;
// Perform access control. We must return here if the permission
@@ -2831,100 +3152,59 @@
}
}
- /// Insert or replace the auth token based on the UNIQUE constraint of the auth token table
- pub fn insert_auth_token(&mut self, auth_token: &HardwareAuthToken) -> Result<()> {
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
- tx.execute(
- "INSERT OR REPLACE INTO perboot.authtoken (challenge, user_id, auth_id,
- authenticator_type, timestamp, mac, time_received) VALUES(?, ?, ?, ?, ?, ?, ?);",
- params![
- auth_token.challenge,
- auth_token.userId,
- auth_token.authenticatorId,
- auth_token.authenticatorType.0 as i32,
- auth_token.timestamp.milliSeconds as i64,
- auth_token.mac,
- MonotonicRawTime::now(),
- ],
- )
- .context("In insert_auth_token: failed to insert auth token into the database")?;
- Ok(()).no_gc()
- })
+ /// Insert or replace the auth token based on (user_id, auth_id, auth_type)
+ pub fn insert_auth_token(&mut self, auth_token: &HardwareAuthToken) {
+ self.perboot.insert_auth_token_entry(AuthTokenEntry::new(
+ auth_token.clone(),
+ MonotonicRawTime::now(),
+ ))
}
/// Find the newest auth token matching the given predicate.
- pub fn find_auth_token_entry<F>(
- &mut self,
- p: F,
- ) -> Result<Option<(AuthTokenEntry, MonotonicRawTime)>>
+ pub fn find_auth_token_entry<F>(&self, p: F) -> Option<(AuthTokenEntry, MonotonicRawTime)>
where
F: Fn(&AuthTokenEntry) -> bool,
{
- self.with_transaction(TransactionBehavior::Deferred, |tx| {
- let mut stmt = tx
- .prepare("SELECT * from perboot.authtoken ORDER BY time_received DESC;")
- .context("Prepare statement failed.")?;
-
- let mut rows = stmt.query(NO_PARAMS).context("Failed to query.")?;
-
- while let Some(row) = rows.next().context("Failed to get next row.")? {
- let entry = AuthTokenEntry::new(
- HardwareAuthToken {
- challenge: row.get(1)?,
- userId: row.get(2)?,
- authenticatorId: row.get(3)?,
- authenticatorType: HardwareAuthenticatorType(row.get(4)?),
- timestamp: Timestamp { milliSeconds: row.get(5)? },
- mac: row.get(6)?,
- },
- row.get(7)?,
- );
- if p(&entry) {
- return Ok(Some((
- entry,
- Self::get_last_off_body(tx)
- .context("In find_auth_token_entry: Trying to get last off body")?,
- )))
- .no_gc();
- }
- }
- Ok(None).no_gc()
- })
- .context("In find_auth_token_entry.")
+ self.perboot.find_auth_token_entry(p).map(|entry| (entry, self.get_last_off_body()))
}
/// Insert last_off_body into the metadata table at the initialization of auth token table
- pub fn insert_last_off_body(&mut self, last_off_body: MonotonicRawTime) -> Result<()> {
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
- tx.execute(
- "INSERT OR REPLACE INTO perboot.metadata (key, value) VALUES (?, ?);",
- params!["last_off_body", last_off_body],
- )
- .context("In insert_last_off_body: failed to insert.")?;
- Ok(()).no_gc()
- })
+ pub fn insert_last_off_body(&self, last_off_body: MonotonicRawTime) {
+ self.perboot.set_last_off_body(last_off_body)
}
/// Update last_off_body when on_device_off_body is called
- pub fn update_last_off_body(&mut self, last_off_body: MonotonicRawTime) -> Result<()> {
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
- tx.execute(
- "UPDATE perboot.metadata SET value = ? WHERE key = ?;",
- params![last_off_body, "last_off_body"],
- )
- .context("In update_last_off_body: failed to update.")?;
- Ok(()).no_gc()
- })
+ pub fn update_last_off_body(&self, last_off_body: MonotonicRawTime) {
+ self.perboot.set_last_off_body(last_off_body)
}
/// Get last_off_body time when finding auth tokens
- fn get_last_off_body(tx: &Transaction) -> Result<MonotonicRawTime> {
- tx.query_row(
- "SELECT value from perboot.metadata WHERE key = ?;",
- params!["last_off_body"],
- |row| Ok(row.get(0)?),
- )
- .context("In get_last_off_body: query_row failed.")
+ fn get_last_off_body(&self) -> MonotonicRawTime {
+ self.perboot.get_last_off_body()
+ }
+
+ /// Load descriptor of a key by key id
+ pub fn load_key_descriptor(&mut self, key_id: i64) -> Result<Option<KeyDescriptor>> {
+ let _wp = wd::watch_millis("KeystoreDB::load_key_descriptor", 500);
+
+ self.with_transaction(TransactionBehavior::Deferred, |tx| {
+ tx.query_row(
+ "SELECT domain, namespace, alias FROM persistent.keyentry WHERE id = ?;",
+ params![key_id],
+ |row| {
+ Ok(KeyDescriptor {
+ domain: Domain(row.get(0)?),
+ nspace: row.get(1)?,
+ alias: row.get(2)?,
+ blob: None,
+ })
+ },
+ )
+ .optional()
+ .context("Trying to load key descriptor")
+ .no_gc()
+ })
+ .context("In load_key_descriptor.")
}
}
@@ -2948,8 +3228,10 @@
Timestamp::Timestamp,
};
use rusqlite::NO_PARAMS;
- use rusqlite::{Error, TransactionBehavior};
+ use rusqlite::TransactionBehavior;
use std::cell::RefCell;
+ use std::collections::BTreeMap;
+ use std::fmt::Write;
use std::sync::atomic::{AtomicU8, Ordering};
use std::sync::Arc;
use std::thread;
@@ -2958,9 +3240,9 @@
use std::time::Instant;
fn new_test_db() -> Result<KeystoreDB> {
- let conn = KeystoreDB::make_connection("file::memory:", "file::memory:")?;
+ let conn = KeystoreDB::make_connection("file::memory:")?;
- let mut db = KeystoreDB { conn, gc: None };
+ let mut db = KeystoreDB { conn, gc: None, perboot: Arc::new(perboot::PerbootDB::new()) };
db.with_transaction(TransactionBehavior::Immediate, |tx| {
KeystoreDB::init_tables(tx).context("Failed to initialize tables.").no_gc()
})?;
@@ -2971,12 +3253,12 @@
where
F: Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static,
{
- let super_key = Arc::new(SuperKeyManager::new());
+ let super_key: Arc<SuperKeyManager> = Default::default();
let gc_db = KeystoreDB::new(path, None).expect("Failed to open test gc db_connection.");
let gc = Gc::new_init_with(Default::default(), move || (Box::new(cb), gc_db, super_key));
- KeystoreDB::new(path, Some(gc))
+ KeystoreDB::new(path, Some(Arc::new(gc)))
}
fn rebind_alias(
@@ -2987,7 +3269,7 @@
namespace: i64,
) -> Result<bool> {
db.with_transaction(TransactionBehavior::Immediate, |tx| {
- KeystoreDB::rebind_alias(tx, newid, alias, &domain, &namespace).no_gc()
+ KeystoreDB::rebind_alias(tx, newid, alias, &domain, &namespace, KeyType::Client).no_gc()
})
.context("In rebind_alias.")
}
@@ -3046,15 +3328,6 @@
assert_eq!(tables[3], "keyentry");
assert_eq!(tables[4], "keymetadata");
assert_eq!(tables[5], "keyparameter");
- let tables = db
- .conn
- .prepare("SELECT name from perboot.sqlite_master WHERE type='table' ORDER BY name;")?
- .query_map(params![], |row| row.get(0))?
- .collect::<rusqlite::Result<Vec<String>>>()?;
-
- assert_eq!(tables.len(), 2);
- assert_eq!(tables[0], "authtoken");
- assert_eq!(tables[1], "metadata");
Ok(())
}
@@ -3069,8 +3342,8 @@
timestamp: Timestamp { milliSeconds: 500 },
mac: String::from("mac").into_bytes(),
};
- db.insert_auth_token(&auth_token1)?;
- let auth_tokens_returned = get_auth_tokens(&mut db)?;
+ db.insert_auth_token(&auth_token1);
+ let auth_tokens_returned = get_auth_tokens(&db);
assert_eq!(auth_tokens_returned.len(), 1);
// insert another auth token with the same values for the columns in the UNIQUE constraint
@@ -3084,8 +3357,8 @@
mac: String::from("mac").into_bytes(),
};
- db.insert_auth_token(&auth_token2)?;
- let mut auth_tokens_returned = get_auth_tokens(&mut db)?;
+ db.insert_auth_token(&auth_token2);
+ let mut auth_tokens_returned = get_auth_tokens(&db);
assert_eq!(auth_tokens_returned.len(), 1);
if let Some(auth_token) = auth_tokens_returned.pop() {
@@ -3103,33 +3376,16 @@
mac: String::from("mac").into_bytes(),
};
- db.insert_auth_token(&auth_token3)?;
- let auth_tokens_returned = get_auth_tokens(&mut db)?;
+ db.insert_auth_token(&auth_token3);
+ let auth_tokens_returned = get_auth_tokens(&db);
assert_eq!(auth_tokens_returned.len(), 2);
Ok(())
}
// utility function for test_auth_token_table_invariant()
- fn get_auth_tokens(db: &mut KeystoreDB) -> Result<Vec<AuthTokenEntry>> {
- let mut stmt = db.conn.prepare("SELECT * from perboot.authtoken;")?;
-
- let auth_token_entries: Vec<AuthTokenEntry> = stmt
- .query_map(NO_PARAMS, |row| {
- Ok(AuthTokenEntry::new(
- HardwareAuthToken {
- challenge: row.get(1)?,
- userId: row.get(2)?,
- authenticatorId: row.get(3)?,
- authenticatorType: HardwareAuthenticatorType(row.get(4)?),
- timestamp: Timestamp { milliSeconds: row.get(5)? },
- mac: row.get(6)?,
- },
- row.get(7)?,
- ))
- })?
- .collect::<Result<Vec<AuthTokenEntry>, Error>>()?;
- Ok(auth_token_entries)
+ fn get_auth_tokens(db: &KeystoreDB) -> Vec<AuthTokenEntry> {
+ db.perboot.get_all_auth_token_entries()
}
#[test]
@@ -3137,7 +3393,7 @@
let temp_dir = TempDir::new("persistent_db_test")?;
let mut db = KeystoreDB::new(temp_dir.path(), None)?;
- db.create_key_entry(&Domain::APP, &100, &KEYSTORE_UUID)?;
+ db.create_key_entry(&Domain::APP, &100, KeyType::Client, &KEYSTORE_UUID)?;
let entries = get_keyentry(&db)?;
assert_eq!(entries.len(), 1);
@@ -3156,8 +3412,8 @@
let mut db = new_test_db()?;
- db.create_key_entry(&Domain::APP, &100, &KEYSTORE_UUID)?;
- db.create_key_entry(&Domain::SELINUX, &101, &KEYSTORE_UUID)?;
+ db.create_key_entry(&Domain::APP, &100, KeyType::Client, &KEYSTORE_UUID)?;
+ db.create_key_entry(&Domain::SELINUX, &101, KeyType::Client, &KEYSTORE_UUID)?;
let entries = get_keyentry(&db)?;
assert_eq!(entries.len(), 2);
@@ -3166,15 +3422,15 @@
// Test that we must pass in a valid Domain.
check_result_is_error_containing_string(
- db.create_key_entry(&Domain::GRANT, &102, &KEYSTORE_UUID),
+ db.create_key_entry(&Domain::GRANT, &102, KeyType::Client, &KEYSTORE_UUID),
"Domain Domain(1) must be either App or SELinux.",
);
check_result_is_error_containing_string(
- db.create_key_entry(&Domain::BLOB, &103, &KEYSTORE_UUID),
+ db.create_key_entry(&Domain::BLOB, &103, KeyType::Client, &KEYSTORE_UUID),
"Domain Domain(3) must be either App or SELinux.",
);
check_result_is_error_containing_string(
- db.create_key_entry(&Domain::KEY_ID, &104, &KEYSTORE_UUID),
+ db.create_key_entry(&Domain::KEY_ID, &104, KeyType::Client, &KEYSTORE_UUID),
"Domain Domain(4) must be either App or SELinux.",
);
@@ -3209,11 +3465,11 @@
load_attestation_key_pool(&mut db, expiration_date, namespace, base_byte)?;
let chain =
db.retrieve_attestation_key_and_cert_chain(Domain::APP, namespace, &KEYSTORE_UUID)?;
- assert_eq!(true, chain.is_some());
+ assert!(chain.is_some());
let cert_chain = chain.unwrap();
assert_eq!(cert_chain.private_key.to_vec(), loaded_values.priv_key);
- assert_eq!(cert_chain.batch_cert.to_vec(), loaded_values.batch_cert);
- assert_eq!(cert_chain.cert_chain.to_vec(), loaded_values.cert_chain);
+ assert_eq!(cert_chain.batch_cert, loaded_values.batch_cert);
+ assert_eq!(cert_chain.cert_chain, loaded_values.cert_chain);
Ok(())
}
@@ -3306,8 +3562,8 @@
db.retrieve_attestation_key_and_cert_chain(Domain::APP, namespace, &KEYSTORE_UUID)?;
assert!(cert_chain.is_some());
let value = cert_chain.unwrap();
- assert_eq!(entry_values.batch_cert, value.batch_cert.to_vec());
- assert_eq!(entry_values.cert_chain, value.cert_chain.to_vec());
+ assert_eq!(entry_values.batch_cert, value.batch_cert);
+ assert_eq!(entry_values.cert_chain, value.cert_chain);
assert_eq!(entry_values.priv_key, value.private_key.to_vec());
cert_chain = db.retrieve_attestation_key_and_cert_chain(
@@ -3338,6 +3594,23 @@
}
#[test]
+ fn test_delete_all_attestation_keys() -> Result<()> {
+ let mut db = new_test_db()?;
+ load_attestation_key_pool(&mut db, 45 /* expiration */, 1 /* namespace */, 0x02)?;
+ load_attestation_key_pool(&mut db, 80 /* expiration */, 2 /* namespace */, 0x03)?;
+ db.create_key_entry(&Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
+ let result = db.delete_all_attestation_keys()?;
+
+ // Give the garbage collector half a second to catch up.
+ std::thread::sleep(Duration::from_millis(500));
+
+ // Attestation keys should be deleted, and the regular key should remain.
+ assert_eq!(result, 2);
+
+ Ok(())
+ }
+
+ #[test]
fn test_rebind_alias() -> Result<()> {
fn extractor(
ke: &KeyEntryRow,
@@ -3346,8 +3619,8 @@
}
let mut db = new_test_db()?;
- db.create_key_entry(&Domain::APP, &42, &KEYSTORE_UUID)?;
- db.create_key_entry(&Domain::APP, &42, &KEYSTORE_UUID)?;
+ db.create_key_entry(&Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
+ db.create_key_entry(&Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
let entries = get_keyentry(&db)?;
assert_eq!(entries.len(), 2);
assert_eq!(
@@ -3683,6 +3956,7 @@
alias: Some(TEST_ALIAS.to_string()),
blob: None,
},
+ KeyType::Client,
TEST_CERT_BLOB,
&KEYSTORE_UUID,
)
@@ -4027,6 +4301,327 @@
Ok(())
}
+ // Creates a key migrates it to a different location and then tries to access it by the old
+ // and new location.
+ #[test]
+ fn test_migrate_key_app_to_app() -> Result<()> {
+ let mut db = new_test_db()?;
+ const SOURCE_UID: u32 = 1u32;
+ const DESTINATION_UID: u32 = 2u32;
+ static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
+ static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
+ let key_id_guard =
+ make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
+ .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
+
+ let source_descriptor: KeyDescriptor = KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(SOURCE_ALIAS.to_string()),
+ blob: None,
+ };
+
+ let destination_descriptor: KeyDescriptor = KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(DESTINATION_ALIAS.to_string()),
+ blob: None,
+ };
+
+ let key_id = key_id_guard.id();
+
+ db.migrate_key_namespace(key_id_guard, &destination_descriptor, DESTINATION_UID, |_k| {
+ Ok(())
+ })
+ .unwrap();
+
+ let (_, key_entry) = db
+ .load_key_entry(
+ &destination_descriptor,
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ DESTINATION_UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(DESTINATION_UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+ db.load_key_entry(
+ &source_descriptor,
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ SOURCE_UID,
+ |_k, _av| Ok(()),
+ )
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+
+ Ok(())
+ }
+
+ // Creates a key migrates it to a different location and then tries to access it by the old
+ // and new location.
+ #[test]
+ fn test_migrate_key_app_to_selinux() -> Result<()> {
+ let mut db = new_test_db()?;
+ const SOURCE_UID: u32 = 1u32;
+ const DESTINATION_UID: u32 = 2u32;
+ const DESTINATION_NAMESPACE: i64 = 1000i64;
+ static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
+ static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
+ let key_id_guard =
+ make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
+ .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
+
+ let source_descriptor: KeyDescriptor = KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(SOURCE_ALIAS.to_string()),
+ blob: None,
+ };
+
+ let destination_descriptor: KeyDescriptor = KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: DESTINATION_NAMESPACE,
+ alias: Some(DESTINATION_ALIAS.to_string()),
+ blob: None,
+ };
+
+ let key_id = key_id_guard.id();
+
+ db.migrate_key_namespace(key_id_guard, &destination_descriptor, DESTINATION_UID, |_k| {
+ Ok(())
+ })
+ .unwrap();
+
+ let (_, key_entry) = db
+ .load_key_entry(
+ &destination_descriptor,
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ DESTINATION_UID,
+ |k, av| {
+ assert_eq!(Domain::SELINUX, k.domain);
+ assert_eq!(DESTINATION_NAMESPACE as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+ db.load_key_entry(
+ &source_descriptor,
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ SOURCE_UID,
+ |_k, _av| Ok(()),
+ )
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+
+ Ok(())
+ }
+
+ // Creates two keys and tries to migrate the first to the location of the second which
+ // is expected to fail.
+ #[test]
+ fn test_migrate_key_destination_occupied() -> Result<()> {
+ let mut db = new_test_db()?;
+ const SOURCE_UID: u32 = 1u32;
+ const DESTINATION_UID: u32 = 2u32;
+ static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
+ static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
+ let key_id_guard =
+ make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
+ .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
+ make_test_key_entry(&mut db, Domain::APP, DESTINATION_UID as i64, DESTINATION_ALIAS, None)
+ .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
+
+ let destination_descriptor: KeyDescriptor = KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(DESTINATION_ALIAS.to_string()),
+ blob: None,
+ };
+
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::INVALID_ARGUMENT)),
+ db.migrate_key_namespace(
+ key_id_guard,
+ &destination_descriptor,
+ DESTINATION_UID,
+ |_k| Ok(())
+ )
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_upgrade_0_to_1() {
+ const ALIAS1: &str = "test_upgrade_0_to_1_1";
+ const ALIAS2: &str = "test_upgrade_0_to_1_2";
+ const ALIAS3: &str = "test_upgrade_0_to_1_3";
+ const UID: u32 = 33;
+ let temp_dir = Arc::new(TempDir::new("test_upgrade_0_to_1").unwrap());
+ let mut db = KeystoreDB::new(temp_dir.path(), None).unwrap();
+ let key_id_untouched1 =
+ make_test_key_entry(&mut db, Domain::APP, UID as i64, ALIAS1, None).unwrap().id();
+ let key_id_untouched2 =
+ make_bootlevel_key_entry(&mut db, Domain::APP, UID as i64, ALIAS2, false).unwrap().id();
+ let key_id_deleted =
+ make_bootlevel_key_entry(&mut db, Domain::APP, UID as i64, ALIAS3, true).unwrap().id();
+
+ let (_, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(ALIAS1.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id_untouched1, None));
+ let (_, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(ALIAS2.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+ assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_untouched2, false));
+ let (_, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(ALIAS3.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+ assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_deleted, true));
+
+ db.with_transaction(TransactionBehavior::Immediate, |tx| {
+ KeystoreDB::from_0_to_1(tx).no_gc()
+ })
+ .unwrap();
+
+ let (_, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(ALIAS1.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id_untouched1, None));
+ let (_, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(ALIAS2.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+ assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_untouched2, false));
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+ db.load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(ALIAS3.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+ }
+
static KEY_LOCK_TEST_ALIAS: &str = "my super duper locked key";
#[test]
@@ -4104,7 +4699,7 @@
}
#[test]
- fn teset_database_busy_error_code() {
+ fn test_database_busy_error_code() {
let temp_dir =
TempDir::new("test_database_busy_error_code_").expect("Failed to create temp dir.");
@@ -4145,8 +4740,9 @@
let test_begin = Instant::now();
- let mut db = KeystoreDB::new(temp_dir.path()).expect("Failed to open database.");
const KEY_COUNT: u32 = 500u32;
+ let mut db =
+ new_test_db_with_gc(temp_dir.path(), |_, _| Ok(())).expect("Failed to open database.");
const OPEN_DB_COUNT: u32 = 50u32;
let mut actual_key_count = KEY_COUNT;
@@ -4164,7 +4760,8 @@
// Insert more keys from a different thread and into a different namespace.
let temp_dir1 = temp_dir.clone();
let handle1 = thread::spawn(move || {
- let mut db = KeystoreDB::new(temp_dir1.path()).expect("Failed to open database.");
+ let mut db = new_test_db_with_gc(temp_dir1.path(), |_, _| Ok(()))
+ .expect("Failed to open database.");
for count in 0..actual_key_count {
if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
@@ -4193,7 +4790,8 @@
// And start unbinding the first set of keys.
let temp_dir2 = temp_dir.clone();
let handle2 = thread::spawn(move || {
- let mut db = KeystoreDB::new(temp_dir2.path()).expect("Failed to open database.");
+ let mut db = new_test_db_with_gc(temp_dir2.path(), |_, _| Ok(()))
+ .expect("Failed to open database.");
for count in 0..actual_key_count {
if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
@@ -4209,27 +4807,6 @@
}
});
- let stop_deleting = Arc::new(AtomicU8::new(0));
- let stop_deleting2 = stop_deleting.clone();
-
- // And delete anything that is unreferenced keys.
- let temp_dir3 = temp_dir.clone();
- let handle3 = thread::spawn(move || {
- let mut db = KeystoreDB::new(temp_dir3.path()).expect("Failed to open database.");
-
- while stop_deleting2.load(Ordering::Relaxed) != 1 {
- while let Some((key_guard, _key)) =
- db.get_unreferenced_key().expect("Failed to get unreferenced Key.")
- {
- if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
- return;
- }
- db.purge_key_entry(key_guard).expect("Failed to purge key.");
- }
- std::thread::sleep(std::time::Duration::from_millis(100));
- }
- });
-
// While a lot of inserting and deleting is going on we have to open database connections
// successfully and use them.
// This clone is not redundant, because temp_dir needs to be kept alive until db goes
@@ -4241,7 +4818,8 @@
if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
return;
}
- let mut db = KeystoreDB::new(temp_dir4.path()).expect("Failed to open database.");
+ let mut db = new_test_db_with_gc(temp_dir4.path(), |_, _| Ok(()))
+ .expect("Failed to open database.");
let alias = format!("test_alias_{}", count);
make_test_key_entry(&mut db, Domain::APP, 3, &alias, None)
@@ -4260,9 +4838,6 @@
handle2.join().expect("Thread 2 panicked.");
handle4.join().expect("Thread 4 panicked.");
- stop_deleting.store(1, Ordering::Relaxed);
- handle3.join().expect("Thread 3 panicked.");
-
Ok(())
}
@@ -4321,7 +4896,7 @@
})
.collect();
list_o_descriptors.sort();
- let mut list_result = db.list(*domain, *namespace)?;
+ let mut list_result = db.list(*domain, *namespace, KeyType::Client)?;
list_result.sort();
assert_eq!(list_o_descriptors, list_result);
@@ -4351,7 +4926,7 @@
loaded_entries.sort_unstable();
assert_eq!(list_o_ids, loaded_entries);
}
- assert_eq!(Vec::<KeyDescriptor>::new(), db.list(Domain::SELINUX, 101)?);
+ assert_eq!(Vec::<KeyDescriptor>::new(), db.list(Domain::SELINUX, 101, KeyType::Client)?);
Ok(())
}
@@ -4373,7 +4948,6 @@
}
#[derive(Debug, PartialEq)]
- #[allow(dead_code)]
struct KeyEntryRow {
id: i64,
key_type: KeyType,
@@ -4391,10 +4965,7 @@
Ok(KeyEntryRow {
id: row.get(0)?,
key_type: row.get(1)?,
- domain: match row.get(2)? {
- Some(i) => Some(Domain(i)),
- None => None,
- },
+ domain: row.get::<_, Option<_>>(2)?.map(Domain),
namespace: row.get(3)?,
alias: row.get(4)?,
state: row.get(5)?,
@@ -4670,7 +5241,7 @@
alias: &str,
max_usage_count: Option<i32>,
) -> Result<KeyIdGuard> {
- let key_id = db.create_key_entry(&domain, &namespace, &KEYSTORE_UUID)?;
+ let key_id = db.create_key_entry(&domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
let mut blob_metadata = BlobMetaData::new();
blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
blob_metadata.add(BlobMetaEntry::Salt(vec![1, 2, 3]));
@@ -4722,6 +5293,66 @@
}
}
+ fn make_bootlevel_key_entry(
+ db: &mut KeystoreDB,
+ domain: Domain,
+ namespace: i64,
+ alias: &str,
+ logical_only: bool,
+ ) -> Result<KeyIdGuard> {
+ let key_id = db.create_key_entry(&domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
+ let mut blob_metadata = BlobMetaData::new();
+ if !logical_only {
+ blob_metadata.add(BlobMetaEntry::MaxBootLevel(3));
+ }
+ blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+
+ db.set_blob(
+ &key_id,
+ SubComponentType::KEY_BLOB,
+ Some(TEST_KEY_BLOB),
+ Some(&blob_metadata),
+ )?;
+ db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
+ db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
+
+ let mut params = make_test_params(None);
+ params.push(KeyParameter::new(KeyParameterValue::MaxBootLevel(3), SecurityLevel::KEYSTORE));
+
+ db.insert_keyparameter(&key_id, ¶ms)?;
+
+ let mut metadata = KeyMetaData::new();
+ metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+ db.insert_key_metadata(&key_id, &metadata)?;
+ rebind_alias(db, &key_id, alias, domain, namespace)?;
+ Ok(key_id)
+ }
+
+ fn make_bootlevel_test_key_entry_test_vector(key_id: i64, logical_only: bool) -> KeyEntry {
+ let mut params = make_test_params(None);
+ params.push(KeyParameter::new(KeyParameterValue::MaxBootLevel(3), SecurityLevel::KEYSTORE));
+
+ let mut blob_metadata = BlobMetaData::new();
+ if !logical_only {
+ blob_metadata.add(BlobMetaEntry::MaxBootLevel(3));
+ }
+ blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+
+ let mut metadata = KeyMetaData::new();
+ metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+
+ KeyEntry {
+ id: key_id,
+ key_blob_info: Some((TEST_KEY_BLOB.to_vec(), blob_metadata)),
+ cert: Some(TEST_CERT_BLOB.to_vec()),
+ cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
+ km_uuid: KEYSTORE_UUID,
+ parameters: params,
+ metadata,
+ pure_cert: false,
+ }
+ }
+
fn debug_dump_keyentry_table(db: &mut KeystoreDB) -> Result<()> {
let mut stmt = db.conn.prepare(
"SELECT id, key_type, domain, namespace, alias, state, km_uuid FROM persistent.keyentry;",
@@ -4792,17 +5423,17 @@
#[test]
fn test_last_off_body() -> Result<()> {
let mut db = new_test_db()?;
- db.insert_last_off_body(MonotonicRawTime::now())?;
+ db.insert_last_off_body(MonotonicRawTime::now());
let tx = db.conn.transaction_with_behavior(TransactionBehavior::Immediate)?;
- let last_off_body_1 = KeystoreDB::get_last_off_body(&tx)?;
tx.commit()?;
+ let last_off_body_1 = db.get_last_off_body();
let one_second = Duration::from_secs(1);
thread::sleep(one_second);
- db.update_last_off_body(MonotonicRawTime::now())?;
+ db.update_last_off_body(MonotonicRawTime::now());
let tx2 = db.conn.transaction_with_behavior(TransactionBehavior::Immediate)?;
- let last_off_body_2 = KeystoreDB::get_last_off_body(&tx2)?;
tx2.commit()?;
- assert!(last_off_body_1.seconds() < last_off_body_2.seconds());
+ let last_off_body_2 = db.get_last_off_body();
+ assert!(last_off_body_1 < last_off_body_2);
Ok(())
}
@@ -4815,11 +5446,11 @@
make_test_key_entry(&mut db, Domain::APP, 110000, TEST_ALIAS, None)?;
db.unbind_keys_for_user(2, false)?;
- assert_eq!(1, db.list(Domain::APP, 110000)?.len());
- assert_eq!(0, db.list(Domain::APP, 210000)?.len());
+ assert_eq!(1, db.list(Domain::APP, 110000, KeyType::Client)?.len());
+ assert_eq!(0, db.list(Domain::APP, 210000, KeyType::Client)?.len());
db.unbind_keys_for_user(1, true)?;
- assert_eq!(0, db.list(Domain::APP, 110000)?.len());
+ assert_eq!(0, db.list(Domain::APP, 110000, KeyType::Client)?.len());
Ok(())
}
@@ -4827,31 +5458,281 @@
#[test]
fn test_store_super_key() -> Result<()> {
let mut db = new_test_db()?;
- let pw = "xyzabc".as_bytes();
+ let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
let super_key = keystore2_crypto::generate_aes256_key()?;
- let secret = String::from("keystore2 is great.");
- let secret_bytes = secret.into_bytes();
+ let secret_bytes = b"keystore2 is great.";
let (encrypted_secret, iv, tag) =
- keystore2_crypto::aes_gcm_encrypt(&secret_bytes, &super_key)?;
+ keystore2_crypto::aes_gcm_encrypt(secret_bytes, &super_key)?;
let (encrypted_super_key, metadata) =
SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
- db.store_super_key(1, &(&encrypted_super_key, &metadata))?;
+ db.store_super_key(
+ 1,
+ &USER_SUPER_KEY,
+ &encrypted_super_key,
+ &metadata,
+ &KeyMetaData::new(),
+ )?;
//check if super key exists
- assert!(db.key_exists(Domain::APP, 1, "USER_SUPER_KEY", KeyType::Super)?);
+ assert!(db.key_exists(Domain::APP, 1, USER_SUPER_KEY.alias, KeyType::Super)?);
- let (_, key_entry) = db.load_super_key(1)?.unwrap();
- let loaded_super_key = SuperKeyManager::extract_super_key_from_key_entry(key_entry, &pw)?;
-
- let decrypted_secret_bytes = keystore2_crypto::aes_gcm_decrypt(
- &encrypted_secret,
- &iv,
- &tag,
- &loaded_super_key.get_key(),
+ let (_, key_entry) = db.load_super_key(&USER_SUPER_KEY, 1)?.unwrap();
+ let loaded_super_key = SuperKeyManager::extract_super_key_from_key_entry(
+ USER_SUPER_KEY.algorithm,
+ key_entry,
+ &pw,
+ None,
)?;
- let decrypted_secret = String::from_utf8((&decrypted_secret_bytes).to_vec())?;
- assert_eq!(String::from("keystore2 is great."), decrypted_secret);
+
+ let decrypted_secret_bytes =
+ loaded_super_key.aes_gcm_decrypt(&encrypted_secret, &iv, &tag)?;
+ assert_eq!(secret_bytes, &*decrypted_secret_bytes);
+ Ok(())
+ }
+
+ fn get_valid_statsd_storage_types() -> Vec<MetricsStorage> {
+ vec![
+ MetricsStorage::KEY_ENTRY,
+ MetricsStorage::KEY_ENTRY_ID_INDEX,
+ MetricsStorage::KEY_ENTRY_DOMAIN_NAMESPACE_INDEX,
+ MetricsStorage::BLOB_ENTRY,
+ MetricsStorage::BLOB_ENTRY_KEY_ENTRY_ID_INDEX,
+ MetricsStorage::KEY_PARAMETER,
+ MetricsStorage::KEY_PARAMETER_KEY_ENTRY_ID_INDEX,
+ MetricsStorage::KEY_METADATA,
+ MetricsStorage::KEY_METADATA_KEY_ENTRY_ID_INDEX,
+ MetricsStorage::GRANT,
+ MetricsStorage::AUTH_TOKEN,
+ MetricsStorage::BLOB_METADATA,
+ MetricsStorage::BLOB_METADATA_BLOB_ENTRY_ID_INDEX,
+ ]
+ }
+
+ /// Perform a simple check to ensure that we can query all the storage types
+ /// that are supported by the DB. Check for reasonable values.
+ #[test]
+ fn test_query_all_valid_table_sizes() -> Result<()> {
+ const PAGE_SIZE: i32 = 4096;
+
+ let mut db = new_test_db()?;
+
+ for t in get_valid_statsd_storage_types() {
+ let stat = db.get_storage_stat(t)?;
+ // AuthToken can be less than a page since it's in a btree, not sqlite
+ // TODO(b/187474736) stop using if-let here
+ if let MetricsStorage::AUTH_TOKEN = t {
+ } else {
+ assert!(stat.size >= PAGE_SIZE);
+ }
+ assert!(stat.size >= stat.unused_size);
+ }
+
+ Ok(())
+ }
+
+ fn get_storage_stats_map(db: &mut KeystoreDB) -> BTreeMap<i32, StorageStats> {
+ get_valid_statsd_storage_types()
+ .into_iter()
+ .map(|t| (t.0, db.get_storage_stat(t).unwrap()))
+ .collect()
+ }
+
+ fn assert_storage_increased(
+ db: &mut KeystoreDB,
+ increased_storage_types: Vec<MetricsStorage>,
+ baseline: &mut BTreeMap<i32, StorageStats>,
+ ) {
+ for storage in increased_storage_types {
+ // Verify the expected storage increased.
+ let new = db.get_storage_stat(storage).unwrap();
+ let storage = storage;
+ let old = &baseline[&storage.0];
+ assert!(new.size >= old.size, "{}: {} >= {}", storage.0, new.size, old.size);
+ assert!(
+ new.unused_size <= old.unused_size,
+ "{}: {} <= {}",
+ storage.0,
+ new.unused_size,
+ old.unused_size
+ );
+
+ // Update the baseline with the new value so that it succeeds in the
+ // later comparison.
+ baseline.insert(storage.0, new);
+ }
+
+ // Get an updated map of the storage and verify there were no unexpected changes.
+ let updated_stats = get_storage_stats_map(db);
+ assert_eq!(updated_stats.len(), baseline.len());
+
+ for &k in baseline.keys() {
+ let stringify = |map: &BTreeMap<i32, StorageStats>| -> String {
+ let mut s = String::new();
+ for &k in map.keys() {
+ writeln!(&mut s, " {}: {}, {}", &k, map[&k].size, map[&k].unused_size)
+ .expect("string concat failed");
+ }
+ s
+ };
+
+ assert!(
+ updated_stats[&k].size == baseline[&k].size
+ && updated_stats[&k].unused_size == baseline[&k].unused_size,
+ "updated_stats:\n{}\nbaseline:\n{}",
+ stringify(&updated_stats),
+ stringify(baseline)
+ );
+ }
+ }
+
+ #[test]
+ fn test_verify_key_table_size_reporting() -> Result<()> {
+ let mut db = new_test_db()?;
+ let mut working_stats = get_storage_stats_map(&mut db);
+
+ let key_id = db.create_key_entry(&Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
+ assert_storage_increased(
+ &mut db,
+ vec![
+ MetricsStorage::KEY_ENTRY,
+ MetricsStorage::KEY_ENTRY_ID_INDEX,
+ MetricsStorage::KEY_ENTRY_DOMAIN_NAMESPACE_INDEX,
+ ],
+ &mut working_stats,
+ );
+
+ let mut blob_metadata = BlobMetaData::new();
+ blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
+ db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB), None)?;
+ assert_storage_increased(
+ &mut db,
+ vec![
+ MetricsStorage::BLOB_ENTRY,
+ MetricsStorage::BLOB_ENTRY_KEY_ENTRY_ID_INDEX,
+ MetricsStorage::BLOB_METADATA,
+ MetricsStorage::BLOB_METADATA_BLOB_ENTRY_ID_INDEX,
+ ],
+ &mut working_stats,
+ );
+
+ let params = make_test_params(None);
+ db.insert_keyparameter(&key_id, ¶ms)?;
+ assert_storage_increased(
+ &mut db,
+ vec![MetricsStorage::KEY_PARAMETER, MetricsStorage::KEY_PARAMETER_KEY_ENTRY_ID_INDEX],
+ &mut working_stats,
+ );
+
+ let mut metadata = KeyMetaData::new();
+ metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+ db.insert_key_metadata(&key_id, &metadata)?;
+ assert_storage_increased(
+ &mut db,
+ vec![MetricsStorage::KEY_METADATA, MetricsStorage::KEY_METADATA_KEY_ENTRY_ID_INDEX],
+ &mut working_stats,
+ );
+
+ let mut sum = 0;
+ for stat in working_stats.values() {
+ sum += stat.size;
+ }
+ let total = db.get_storage_stat(MetricsStorage::DATABASE)?.size;
+ assert!(sum <= total, "Expected sum <= total. sum: {}, total: {}", sum, total);
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_verify_auth_table_size_reporting() -> Result<()> {
+ let mut db = new_test_db()?;
+ let mut working_stats = get_storage_stats_map(&mut db);
+ db.insert_auth_token(&HardwareAuthToken {
+ challenge: 123,
+ userId: 456,
+ authenticatorId: 789,
+ authenticatorType: kmhw_authenticator_type::ANY,
+ timestamp: Timestamp { milliSeconds: 10 },
+ mac: b"mac".to_vec(),
+ });
+ assert_storage_increased(&mut db, vec![MetricsStorage::AUTH_TOKEN], &mut working_stats);
+ Ok(())
+ }
+
+ #[test]
+ fn test_verify_grant_table_size_reporting() -> Result<()> {
+ const OWNER: i64 = 1;
+ let mut db = new_test_db()?;
+ make_test_key_entry(&mut db, Domain::APP, OWNER, TEST_ALIAS, None)?;
+
+ let mut working_stats = get_storage_stats_map(&mut db);
+ db.grant(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ OWNER as u32,
+ 123,
+ key_perm_set![KeyPerm::use_()],
+ |_, _| Ok(()),
+ )?;
+
+ assert_storage_increased(&mut db, vec![MetricsStorage::GRANT], &mut working_stats);
+
+ Ok(())
+ }
+
+ #[test]
+ fn find_auth_token_entry_returns_latest() -> Result<()> {
+ let mut db = new_test_db()?;
+ db.insert_auth_token(&HardwareAuthToken {
+ challenge: 123,
+ userId: 456,
+ authenticatorId: 789,
+ authenticatorType: kmhw_authenticator_type::ANY,
+ timestamp: Timestamp { milliSeconds: 10 },
+ mac: b"mac0".to_vec(),
+ });
+ std::thread::sleep(std::time::Duration::from_millis(1));
+ db.insert_auth_token(&HardwareAuthToken {
+ challenge: 123,
+ userId: 457,
+ authenticatorId: 789,
+ authenticatorType: kmhw_authenticator_type::ANY,
+ timestamp: Timestamp { milliSeconds: 12 },
+ mac: b"mac1".to_vec(),
+ });
+ std::thread::sleep(std::time::Duration::from_millis(1));
+ db.insert_auth_token(&HardwareAuthToken {
+ challenge: 123,
+ userId: 458,
+ authenticatorId: 789,
+ authenticatorType: kmhw_authenticator_type::ANY,
+ timestamp: Timestamp { milliSeconds: 3 },
+ mac: b"mac2".to_vec(),
+ });
+ // All three entries are in the database
+ assert_eq!(db.perboot.auth_tokens_len(), 3);
+ // It selected the most recent timestamp
+ assert_eq!(db.find_auth_token_entry(|_| true).unwrap().0.auth_token.mac, b"mac2".to_vec());
+ Ok(())
+ }
+
+ #[test]
+ fn test_load_key_descriptor() -> Result<()> {
+ let mut db = new_test_db()?;
+ let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS, None)?.0;
+
+ let key = db.load_key_descriptor(key_id)?.unwrap();
+
+ assert_eq!(key.domain, Domain::APP);
+ assert_eq!(key.nspace, 1);
+ assert_eq!(key.alias, Some(TEST_ALIAS.to_string()));
+
+ // No such id
+ assert_eq!(db.load_key_descriptor(key_id + 1)?, None);
Ok(())
}
}
diff --git a/keystore2/src/database/perboot.rs b/keystore2/src/database/perboot.rs
new file mode 100644
index 0000000..7ff35fa
--- /dev/null
+++ b/keystore2/src/database/perboot.rs
@@ -0,0 +1,122 @@
+// 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.
+
+//! This module implements a per-boot, shared, in-memory storage of auth tokens
+//! and last-time-on-body for the main Keystore 2.0 database module.
+
+use super::{AuthTokenEntry, MonotonicRawTime};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ HardwareAuthToken::HardwareAuthToken, HardwareAuthenticatorType::HardwareAuthenticatorType,
+};
+use lazy_static::lazy_static;
+use std::collections::HashSet;
+use std::sync::atomic::{AtomicI64, Ordering};
+use std::sync::Arc;
+use std::sync::RwLock;
+
+#[derive(PartialEq, PartialOrd, Ord, Eq, Hash)]
+struct AuthTokenId {
+ user_id: i64,
+ auth_id: i64,
+ authenticator_type: HardwareAuthenticatorType,
+}
+
+impl AuthTokenId {
+ fn from_auth_token(tok: &HardwareAuthToken) -> Self {
+ AuthTokenId {
+ user_id: tok.userId,
+ auth_id: tok.authenticatorId,
+ authenticator_type: tok.authenticatorType,
+ }
+ }
+}
+
+//Implements Eq/Hash to only operate on the AuthTokenId portion
+//of the AuthTokenEntry. This allows a HashSet to DTRT.
+#[derive(Clone)]
+struct AuthTokenEntryWrap(AuthTokenEntry);
+
+impl std::hash::Hash for AuthTokenEntryWrap {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ AuthTokenId::from_auth_token(&self.0.auth_token).hash(state)
+ }
+}
+
+impl PartialEq<AuthTokenEntryWrap> for AuthTokenEntryWrap {
+ fn eq(&self, other: &AuthTokenEntryWrap) -> bool {
+ AuthTokenId::from_auth_token(&self.0.auth_token)
+ == AuthTokenId::from_auth_token(&other.0.auth_token)
+ }
+}
+
+impl Eq for AuthTokenEntryWrap {}
+
+/// Per-boot state structure. Currently only used to track auth tokens and
+/// last-off-body.
+#[derive(Default)]
+pub struct PerbootDB {
+ // We can use a .unwrap() discipline on this lock, because only panicking
+ // while holding a .write() lock will poison it. The only write usage is
+ // an insert call which inserts a pre-constructed pair.
+ auth_tokens: RwLock<HashSet<AuthTokenEntryWrap>>,
+ // Ordering::Relaxed is appropriate for accessing this atomic, since it
+ // does not currently need to be synchronized with anything else.
+ last_off_body: AtomicI64,
+}
+
+lazy_static! {
+ /// The global instance of the perboot DB. Located here rather than in globals
+ /// in order to restrict access to the database module.
+ pub static ref PERBOOT_DB: Arc<PerbootDB> = Arc::new(PerbootDB::new());
+}
+
+impl PerbootDB {
+ /// Construct a new perboot database. Currently just uses default values.
+ pub fn new() -> Self {
+ Default::default()
+ }
+ /// Add a new auth token + timestamp to the database, replacing any which
+ /// match all of user_id, auth_id, and auth_type.
+ pub fn insert_auth_token_entry(&self, entry: AuthTokenEntry) {
+ self.auth_tokens.write().unwrap().replace(AuthTokenEntryWrap(entry));
+ }
+ /// Locate an auth token entry which matches the predicate with the most
+ /// recent update time.
+ pub fn find_auth_token_entry<P: Fn(&AuthTokenEntry) -> bool>(
+ &self,
+ p: P,
+ ) -> Option<AuthTokenEntry> {
+ let reader = self.auth_tokens.read().unwrap();
+ let mut matches: Vec<_> = reader.iter().filter(|x| p(&x.0)).collect();
+ matches.sort_by_key(|x| x.0.time_received);
+ matches.last().map(|x| x.0.clone())
+ }
+ /// Get the last time the device was off the user's body
+ pub fn get_last_off_body(&self) -> MonotonicRawTime {
+ MonotonicRawTime(self.last_off_body.load(Ordering::Relaxed))
+ }
+ /// Set the last time the device was off the user's body
+ pub fn set_last_off_body(&self, last_off_body: MonotonicRawTime) {
+ self.last_off_body.store(last_off_body.0, Ordering::Relaxed)
+ }
+ /// Return how many auth tokens are currently tracked.
+ pub fn auth_tokens_len(&self) -> usize {
+ self.auth_tokens.read().unwrap().len()
+ }
+ #[cfg(test)]
+ /// For testing, return all auth tokens currently tracked.
+ pub fn get_all_auth_token_entries(&self) -> Vec<AuthTokenEntry> {
+ self.auth_tokens.read().unwrap().iter().cloned().map(|x| x.0).collect()
+ }
+}
diff --git a/keystore2/src/db_utils.rs b/keystore2/src/database/utils.rs
similarity index 98%
rename from keystore2/src/db_utils.rs
rename to keystore2/src/database/utils.rs
index 90f5616..b4590da 100644
--- a/keystore2/src/db_utils.rs
+++ b/keystore2/src/database/utils.rs
@@ -44,7 +44,7 @@
loop {
match rows.next().context("In with_rows_extract_all: Failed to unpack row")? {
Some(row) => {
- row_extractor(&row).context("In with_rows_extract_all.")?;
+ row_extractor(row).context("In with_rows_extract_all.")?;
}
None => break Ok(()),
}
diff --git a/keystore2/src/database/versioning.rs b/keystore2/src/database/versioning.rs
new file mode 100644
index 0000000..e3a95c8
--- /dev/null
+++ b/keystore2/src/database/versioning.rs
@@ -0,0 +1,379 @@
+// 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.
+
+use anyhow::{anyhow, Context, Result};
+use rusqlite::{params, OptionalExtension, Transaction, NO_PARAMS};
+
+pub fn create_or_get_version(tx: &Transaction, current_version: u32) -> Result<u32> {
+ tx.execute(
+ "CREATE TABLE IF NOT EXISTS persistent.version (
+ id INTEGER PRIMARY KEY,
+ version INTEGER);",
+ NO_PARAMS,
+ )
+ .context("In create_or_get_version: Failed to create version table.")?;
+
+ let version = tx
+ .query_row("SELECT version FROM persistent.version WHERE id = 0;", NO_PARAMS, |row| {
+ row.get(0)
+ })
+ .optional()
+ .context("In create_or_get_version: Failed to read version.")?;
+
+ let version = if let Some(version) = version {
+ version
+ } else {
+ // If no version table existed it could mean one of two things:
+ // 1) This database is completely new. In this case the version has to be set
+ // to the current version and the current version which also needs to be
+ // returned.
+ // 2) The database predates db versioning. In this case the version needs to be
+ // set to 0, and 0 needs to be returned.
+ let version = if tx
+ .query_row(
+ "SELECT name FROM persistent.sqlite_master
+ WHERE type = 'table' AND name = 'keyentry';",
+ NO_PARAMS,
+ |_| Ok(()),
+ )
+ .optional()
+ .context("In create_or_get_version: Failed to check for keyentry table.")?
+ .is_none()
+ {
+ current_version
+ } else {
+ 0
+ };
+
+ tx.execute("INSERT INTO persistent.version (id, version) VALUES(0, ?);", params![version])
+ .context("In create_or_get_version: Failed to insert initial version.")?;
+ version
+ };
+ Ok(version)
+}
+
+pub fn update_version(tx: &Transaction, new_version: u32) -> Result<()> {
+ let updated = tx
+ .execute("UPDATE persistent.version SET version = ? WHERE id = 0;", params![new_version])
+ .context("In update_version: Failed to update row.")?;
+ if updated == 1 {
+ Ok(())
+ } else {
+ Err(anyhow!("In update_version: No rows were updated."))
+ }
+}
+
+pub fn upgrade_database<F>(tx: &Transaction, current_version: u32, upgraders: &[F]) -> Result<()>
+where
+ F: Fn(&Transaction) -> Result<u32> + 'static,
+{
+ if upgraders.len() < current_version as usize {
+ return Err(anyhow!("In upgrade_database: Insufficient upgraders provided."));
+ }
+ let mut db_version = create_or_get_version(tx, current_version)
+ .context("In upgrade_database: Failed to get database version.")?;
+ while db_version < current_version {
+ db_version = upgraders[db_version as usize](tx).with_context(|| {
+ format!("In upgrade_database: Trying to upgrade from db version {}.", db_version)
+ })?;
+ }
+ update_version(tx, db_version).context("In upgrade_database.")
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use rusqlite::{Connection, TransactionBehavior, NO_PARAMS};
+
+ #[test]
+ fn upgrade_database_test() {
+ let mut conn = Connection::open_in_memory().unwrap();
+ conn.execute("ATTACH DATABASE 'file::memory:' as persistent;", NO_PARAMS).unwrap();
+
+ let upgraders: Vec<_> = (0..30_u32)
+ .map(move |i| {
+ move |tx: &Transaction| {
+ tx.execute(
+ "INSERT INTO persistent.test (test_field) VALUES(?);",
+ params![i + 1],
+ )
+ .with_context(|| format!("In upgrade_from_{}_to_{}.", i, i + 1))?;
+ Ok(i + 1)
+ }
+ })
+ .collect();
+
+ for legacy in &[false, true] {
+ if *legacy {
+ conn.execute(
+ "CREATE TABLE IF NOT EXISTS persistent.keyentry (
+ id INTEGER UNIQUE,
+ key_type INTEGER,
+ domain INTEGER,
+ namespace INTEGER,
+ alias BLOB,
+ state INTEGER,
+ km_uuid BLOB);",
+ NO_PARAMS,
+ )
+ .unwrap();
+ }
+ for from in 1..29 {
+ for to in from..30 {
+ conn.execute("DROP TABLE IF EXISTS persistent.version;", NO_PARAMS).unwrap();
+ conn.execute("DROP TABLE IF EXISTS persistent.test;", NO_PARAMS).unwrap();
+ conn.execute(
+ "CREATE TABLE IF NOT EXISTS persistent.test (
+ id INTEGER PRIMARY KEY,
+ test_field INTEGER);",
+ NO_PARAMS,
+ )
+ .unwrap();
+
+ {
+ let tx =
+ conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
+ create_or_get_version(&tx, from).unwrap();
+ tx.commit().unwrap();
+ }
+ {
+ let tx =
+ conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
+ upgrade_database(&tx, to, &upgraders).unwrap();
+ tx.commit().unwrap();
+ }
+
+ // In the legacy database case all upgraders starting from 0 have to run. So
+ // after the upgrade step, the expectations need to be adjusted.
+ let from = if *legacy { 0 } else { from };
+
+ // There must be exactly to - from rows.
+ assert_eq!(
+ to - from,
+ conn.query_row(
+ "SELECT COUNT(test_field) FROM persistent.test;",
+ NO_PARAMS,
+ |row| row.get(0)
+ )
+ .unwrap()
+ );
+ // Each row must have the correct relation between id and test_field. If this
+ // is not the case, the upgraders were not executed in the correct order.
+ assert_eq!(
+ to - from,
+ conn.query_row(
+ "SELECT COUNT(test_field) FROM persistent.test
+ WHERE id = test_field - ?;",
+ params![from],
+ |row| row.get(0)
+ )
+ .unwrap()
+ );
+ }
+ }
+ }
+ }
+
+ #[test]
+ fn create_or_get_version_new_database() {
+ let mut conn = Connection::open_in_memory().unwrap();
+ conn.execute("ATTACH DATABASE 'file::memory:' as persistent;", NO_PARAMS).unwrap();
+ {
+ let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
+ let version = create_or_get_version(&tx, 3).unwrap();
+ tx.commit().unwrap();
+ assert_eq!(version, 3);
+ }
+
+ // Was the version table created as expected?
+ assert_eq!(
+ Ok("version".to_owned()),
+ conn.query_row(
+ "SELECT name FROM persistent.sqlite_master
+ WHERE type = 'table' AND name = 'version';",
+ NO_PARAMS,
+ |row| row.get(0),
+ )
+ );
+
+ // There is exactly one row in the version table.
+ assert_eq!(
+ Ok(1),
+ conn.query_row("SELECT COUNT(id) from persistent.version;", NO_PARAMS, |row| row
+ .get(0))
+ );
+
+ // The version must be set to 3
+ assert_eq!(
+ Ok(3),
+ conn.query_row(
+ "SELECT version from persistent.version WHERE id = 0;",
+ NO_PARAMS,
+ |row| row.get(0)
+ )
+ );
+
+ // Will subsequent calls to create_or_get_version still return the same version even
+ // if the current version changes.
+ {
+ let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
+ let version = create_or_get_version(&tx, 5).unwrap();
+ tx.commit().unwrap();
+ assert_eq!(version, 3);
+ }
+
+ // There is still exactly one row in the version table.
+ assert_eq!(
+ Ok(1),
+ conn.query_row("SELECT COUNT(id) from persistent.version;", NO_PARAMS, |row| row
+ .get(0))
+ );
+
+ // Bump the version.
+ {
+ let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
+ update_version(&tx, 5).unwrap();
+ tx.commit().unwrap();
+ }
+
+ // Now the version should have changed.
+ {
+ let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
+ let version = create_or_get_version(&tx, 7).unwrap();
+ tx.commit().unwrap();
+ assert_eq!(version, 5);
+ }
+
+ // There is still exactly one row in the version table.
+ assert_eq!(
+ Ok(1),
+ conn.query_row("SELECT COUNT(id) from persistent.version;", NO_PARAMS, |row| row
+ .get(0))
+ );
+
+ // The version must be set to 5
+ assert_eq!(
+ Ok(5),
+ conn.query_row(
+ "SELECT version from persistent.version WHERE id = 0;",
+ NO_PARAMS,
+ |row| row.get(0)
+ )
+ );
+ }
+
+ #[test]
+ fn create_or_get_version_legacy_database() {
+ let mut conn = Connection::open_in_memory().unwrap();
+ conn.execute("ATTACH DATABASE 'file::memory:' as persistent;", NO_PARAMS).unwrap();
+ // A legacy (version 0) database is detected if the keyentry table exists but no
+ // version table.
+ conn.execute(
+ "CREATE TABLE IF NOT EXISTS persistent.keyentry (
+ id INTEGER UNIQUE,
+ key_type INTEGER,
+ domain INTEGER,
+ namespace INTEGER,
+ alias BLOB,
+ state INTEGER,
+ km_uuid BLOB);",
+ NO_PARAMS,
+ )
+ .unwrap();
+
+ {
+ let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
+ let version = create_or_get_version(&tx, 3).unwrap();
+ tx.commit().unwrap();
+ // In the legacy case, version 0 must be returned.
+ assert_eq!(version, 0);
+ }
+
+ // Was the version table created as expected?
+ assert_eq!(
+ Ok("version".to_owned()),
+ conn.query_row(
+ "SELECT name FROM persistent.sqlite_master
+ WHERE type = 'table' AND name = 'version';",
+ NO_PARAMS,
+ |row| row.get(0),
+ )
+ );
+
+ // There is exactly one row in the version table.
+ assert_eq!(
+ Ok(1),
+ conn.query_row("SELECT COUNT(id) from persistent.version;", NO_PARAMS, |row| row
+ .get(0))
+ );
+
+ // The version must be set to 0
+ assert_eq!(
+ Ok(0),
+ conn.query_row(
+ "SELECT version from persistent.version WHERE id = 0;",
+ NO_PARAMS,
+ |row| row.get(0)
+ )
+ );
+
+ // Will subsequent calls to create_or_get_version still return the same version even
+ // if the current version changes.
+ {
+ let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
+ let version = create_or_get_version(&tx, 5).unwrap();
+ tx.commit().unwrap();
+ assert_eq!(version, 0);
+ }
+
+ // There is still exactly one row in the version table.
+ assert_eq!(
+ Ok(1),
+ conn.query_row("SELECT COUNT(id) from persistent.version;", NO_PARAMS, |row| row
+ .get(0))
+ );
+
+ // Bump the version.
+ {
+ let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
+ update_version(&tx, 5).unwrap();
+ tx.commit().unwrap();
+ }
+
+ // Now the version should have changed.
+ {
+ let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
+ let version = create_or_get_version(&tx, 7).unwrap();
+ tx.commit().unwrap();
+ assert_eq!(version, 5);
+ }
+
+ // There is still exactly one row in the version table.
+ assert_eq!(
+ Ok(1),
+ conn.query_row("SELECT COUNT(id) from persistent.version;", NO_PARAMS, |row| row
+ .get(0))
+ );
+
+ // The version must be set to 5
+ assert_eq!(
+ Ok(5),
+ conn.query_row(
+ "SELECT version from persistent.version WHERE id = 0;",
+ NO_PARAMS,
+ |row| row.get(0)
+ )
+ );
+ }
+}
diff --git a/keystore2/src/ec_crypto.rs b/keystore2/src/ec_crypto.rs
new file mode 100644
index 0000000..0425d4a
--- /dev/null
+++ b/keystore2/src/ec_crypto.rs
@@ -0,0 +1,137 @@
+// 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.
+
+//! Implement ECDH-based encryption.
+
+use anyhow::{Context, Result};
+use keystore2_crypto::{
+ aes_gcm_decrypt, aes_gcm_encrypt, ec_key_generate_key, ec_key_get0_public_key,
+ ec_key_marshal_private_key, ec_key_parse_private_key, ec_point_oct_to_point,
+ ec_point_point_to_oct, ecdh_compute_key, generate_salt, hkdf_expand, hkdf_extract, ECKey, ZVec,
+ AES_256_KEY_LENGTH,
+};
+
+/// Private key for ECDH encryption.
+pub struct ECDHPrivateKey(ECKey);
+
+impl ECDHPrivateKey {
+ /// Randomly generate a fresh keypair.
+ pub fn generate() -> Result<ECDHPrivateKey> {
+ ec_key_generate_key()
+ .map(ECDHPrivateKey)
+ .context("In ECDHPrivateKey::generate: generation failed")
+ }
+
+ /// Deserialize bytes into an ECDH keypair
+ pub fn from_private_key(buf: &[u8]) -> Result<ECDHPrivateKey> {
+ ec_key_parse_private_key(buf)
+ .map(ECDHPrivateKey)
+ .context("In ECDHPrivateKey::from_private_key: parsing failed")
+ }
+
+ /// Serialize the ECDH key into bytes
+ pub fn private_key(&self) -> Result<ZVec> {
+ ec_key_marshal_private_key(&self.0)
+ .context("In ECDHPrivateKey::private_key: marshalling failed")
+ }
+
+ /// Generate the serialization of the corresponding public key
+ pub fn public_key(&self) -> Result<Vec<u8>> {
+ let point = ec_key_get0_public_key(&self.0);
+ ec_point_point_to_oct(point.get_point())
+ .context("In ECDHPrivateKey::public_key: marshalling failed")
+ }
+
+ /// Use ECDH to agree an AES key with another party whose public key we have.
+ /// Sender and recipient public keys are passed separately because they are
+ /// switched in encryption vs decryption.
+ fn agree_key(
+ &self,
+ salt: &[u8],
+ other_public_key: &[u8],
+ sender_public_key: &[u8],
+ recipient_public_key: &[u8],
+ ) -> Result<ZVec> {
+ let hkdf = hkdf_extract(sender_public_key, salt)
+ .context("In ECDHPrivateKey::agree_key: hkdf_extract on sender_public_key failed")?;
+ let hkdf = hkdf_extract(recipient_public_key, &hkdf)
+ .context("In ECDHPrivateKey::agree_key: hkdf_extract on recipient_public_key failed")?;
+ let other_public_key = ec_point_oct_to_point(other_public_key)
+ .context("In ECDHPrivateKey::agree_key: ec_point_oct_to_point failed")?;
+ let secret = ecdh_compute_key(other_public_key.get_point(), &self.0)
+ .context("In ECDHPrivateKey::agree_key: ecdh_compute_key failed")?;
+ let prk = hkdf_extract(&secret, &hkdf)
+ .context("In ECDHPrivateKey::agree_key: hkdf_extract on secret failed")?;
+
+ let aes_key = hkdf_expand(AES_256_KEY_LENGTH, &prk, b"AES-256-GCM key")
+ .context("In ECDHPrivateKey::agree_key: hkdf_expand failed")?;
+ Ok(aes_key)
+ }
+
+ /// Encrypt a message to the party with the given public key
+ pub fn encrypt_message(
+ recipient_public_key: &[u8],
+ message: &[u8],
+ ) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>)> {
+ let sender_key =
+ Self::generate().context("In ECDHPrivateKey::encrypt_message: generate failed")?;
+ let sender_public_key = sender_key
+ .public_key()
+ .context("In ECDHPrivateKey::encrypt_message: public_key failed")?;
+ let salt =
+ generate_salt().context("In ECDHPrivateKey::encrypt_message: generate_salt failed")?;
+ let aes_key = sender_key
+ .agree_key(&salt, recipient_public_key, &sender_public_key, recipient_public_key)
+ .context("In ECDHPrivateKey::encrypt_message: agree_key failed")?;
+ let (ciphertext, iv, tag) = aes_gcm_encrypt(message, &aes_key)
+ .context("In ECDHPrivateKey::encrypt_message: aes_gcm_encrypt failed")?;
+ Ok((sender_public_key, salt, iv, ciphertext, tag))
+ }
+
+ /// Decrypt a message sent to us
+ pub fn decrypt_message(
+ &self,
+ sender_public_key: &[u8],
+ salt: &[u8],
+ iv: &[u8],
+ ciphertext: &[u8],
+ tag: &[u8],
+ ) -> Result<ZVec> {
+ let recipient_public_key = self.public_key()?;
+ let aes_key = self
+ .agree_key(salt, sender_public_key, sender_public_key, &recipient_public_key)
+ .context("In ECDHPrivateKey::decrypt_message: agree_key failed")?;
+ aes_gcm_decrypt(ciphertext, iv, tag, &aes_key)
+ .context("In ECDHPrivateKey::decrypt_message: aes_gcm_decrypt failed")
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_crypto_roundtrip() -> Result<()> {
+ let message = b"Hello world";
+ let recipient = ECDHPrivateKey::generate()?;
+ let (sender_public_key, salt, iv, ciphertext, tag) =
+ ECDHPrivateKey::encrypt_message(&recipient.public_key()?, message)?;
+ let recipient = ECDHPrivateKey::from_private_key(&recipient.private_key()?)?;
+ let decrypted =
+ recipient.decrypt_message(&sender_public_key, &salt, &iv, &ciphertext, &tag)?;
+ let dc: &[u8] = &decrypted;
+ assert_eq!(message, dc);
+ Ok(())
+ }
+}
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index cc59c32..997e739 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -14,32 +14,35 @@
//! This is the Keystore 2.0 Enforcements module.
// TODO: more description to follow.
-use crate::database::{AuthTokenEntry, MonotonicRawTime};
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::{
- ISecureClock::ISecureClock, TimeStampToken::TimeStampToken,
+ 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,
+ Domain::Domain, IKeystoreSecurityLevel::KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING,
OperationChallenge::OperationChallenge,
};
-use android_system_keystore2::binder::Strong;
use anyhow::{Context, Result};
-use std::sync::{
- mpsc::{channel, Receiver, Sender},
- Arc, Mutex, Weak,
-};
-use std::time::SystemTime;
use std::{
collections::{HashMap, HashSet},
- sync::mpsc::TryRecvError,
+ sync::{
+ mpsc::{channel, Receiver, Sender, TryRecvError},
+ Arc, Mutex, Weak,
+ },
+ time::SystemTime,
};
#[derive(Debug)]
@@ -57,47 +60,54 @@
state: AuthRequestState,
/// This need to be set to Some to fulfill a AuthRequestState::OpAuth or
/// AuthRequestState::TimeStampedOpAuth.
- hat: Option<HardwareAuthToken>,
+ hat: Mutex<Option<HardwareAuthToken>>,
}
+unsafe impl Sync for AuthRequest {}
+
impl AuthRequest {
- fn op_auth() -> Arc<Mutex<Self>> {
- Arc::new(Mutex::new(Self { state: AuthRequestState::OpAuth, hat: None }))
+ 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<Mutex<Self>> {
- Arc::new(Mutex::new(Self {
+ fn timestamped_op_auth(receiver: Receiver<Result<TimeStampToken, Error>>) -> Arc<Self> {
+ Arc::new(Self {
state: AuthRequestState::TimeStampedOpAuth(receiver),
- hat: None,
- }))
+ hat: Mutex::new(None),
+ })
}
fn timestamp(
hat: HardwareAuthToken,
receiver: Receiver<Result<TimeStampToken, Error>>,
- ) -> Arc<Mutex<Self>> {
- Arc::new(Mutex::new(Self { state: AuthRequestState::TimeStamp(receiver), hat: Some(hat) }))
+ ) -> Arc<Self> {
+ Arc::new(Self { state: AuthRequestState::TimeStamp(receiver), hat: Mutex::new(Some(hat)) })
}
- fn add_auth_token(&mut self, hat: HardwareAuthToken) {
- self.hat = Some(hat)
+ fn add_auth_token(&self, hat: HardwareAuthToken) {
+ *self.hat.lock().unwrap() = Some(hat)
}
- fn get_auth_tokens(&mut self) -> Result<(HardwareAuthToken, Option<TimeStampToken>)> {
- match (&self.state, self.hat.is_some()) {
- (AuthRequestState::OpAuth, true) => Ok((self.hat.take().unwrap(), None)),
- (AuthRequestState::TimeStampedOpAuth(recv), true)
- | (AuthRequestState::TimeStamp(recv), true) => {
+ 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("In get_auth_tokens: 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.")?;
- let tst = result.context(concat!(
+ Some(result.context(concat!(
"In get_auth_tokens: Worker responded with error ",
"from generating timestamp token."
- ))?;
- Ok((self.hat.take().unwrap(), Some(tst)))
+ ))?)
}
- (_, false) => Err(Error::Km(ErrorCode::KEY_USER_NOT_AUTHENTICATED))
- .context("In get_auth_tokens: No operation auth token received."),
- }
+ AuthRequestState::OpAuth => None,
+ };
+ Ok((hat, tst))
}
}
@@ -123,7 +133,7 @@
/// 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<Mutex<AuthRequest>>),
+ 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>),
@@ -165,9 +175,15 @@
const CLEANUP_PERIOD: u8 = 25;
pub fn add_auth_token(&self, hat: HardwareAuthToken) {
- let mut map = self.map_and_cleanup_counter.lock().unwrap();
- let (ref mut map, _) = *map;
- if let Some((_, recv)) = map.remove_entry(&hat.challenge) {
+ 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);
}
}
@@ -187,7 +203,7 @@
}
#[derive(Debug)]
-struct TokenReceiver(Weak<Mutex<AuthRequest>>);
+struct TokenReceiver(Weak<AuthRequest>);
impl TokenReceiver {
fn is_obsolete(&self) -> bool {
@@ -196,20 +212,16 @@
fn add_auth_token(&self, hat: HardwareAuthToken) {
if let Some(state_arc) = self.0.upgrade() {
- let mut state = state_arc.lock().unwrap();
- state.add_auth_token(hat);
+ state_arc.add_auth_token(hat);
}
}
}
fn get_timestamp_token(challenge: i64) -> Result<TimeStampToken, Error> {
- let dev: Strong<dyn ISecureClock> = get_timestamp_service()
- .expect(concat!(
- "Secure Clock service must be present ",
- "if TimeStampTokens are required."
- ))
- .get_interface()
- .expect("Fatal: Timestamp service does not implement ISecureClock.");
+ let dev = get_timestamp_service().expect(concat!(
+ "Secure Clock service must be present ",
+ "if TimeStampTokens are required."
+ ));
map_binder_status(dev.generateTimeStamp(challenge))
}
@@ -217,7 +229,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
@@ -322,8 +334,7 @@
/// 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 {
- let mut state = auth_request.lock().unwrap();
- Some(state.get_auth_tokens().context("In AuthInfo::get_auth_tokens.")?)
+ Some(auth_request.get_auth_tokens().context("In AuthInfo::get_auth_tokens.")?)
} else {
None
};
@@ -352,6 +363,7 @@
}
/// 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.
@@ -368,15 +380,6 @@
}
impl Enforcements {
- /// Creates an enforcement object with the two data structures it holds and the sender as None.
- pub fn new() -> Self {
- Enforcements {
- device_unlocked_set: Mutex::new(HashSet::new()),
- op_auth_map: Default::default(),
- confirmation_token_receiver: Default::default(),
- }
- }
-
/// 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.
@@ -475,6 +478,7 @@
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
@@ -541,6 +545,9 @@
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
@@ -594,6 +601,13 @@
}
}
+ if let Some(level) = max_boot_level {
+ if !SUPER_KEY.level_accessible(level) {
+ return Err(Error::Km(Ec::BOOT_LEVEL_EXCEEDED))
+ .context("In authorize_create: boot level is too late.");
+ }
+ }
+
if !unlocked_device_required && no_auth_required {
return Ok((
None,
@@ -620,8 +634,7 @@
} else {
unlocked_device_required
}
- })
- .context("In authorize_create: Trying to get required auth token.")?;
+ });
Some(
hat_and_last_off_body
.ok_or(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
@@ -664,9 +677,10 @@
// 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) => {
- (None, DeferredAuthState::TimeStampRequired(hat.take_auth_token()))
- }
+ (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)
}
@@ -681,15 +695,11 @@
})
}
- fn find_auth_token<F>(p: F) -> Result<Option<(AuthTokenEntry, MonotonicRawTime)>>
+ fn find_auth_token<F>(p: F) -> Option<(AuthTokenEntry, MonotonicRawTime)>
where
F: Fn(&AuthTokenEntry) -> bool,
{
- DB.with(|db| {
- let mut db = db.borrow_mut();
- db.find_auth_token_entry(p).context("Trying to find auth token.")
- })
- .context("In find_auth_token.")
+ 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
@@ -733,11 +743,9 @@
/// 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) -> Result<()> {
- DB.with(|db| db.borrow_mut().insert_auth_token(&hat)).context("In add_auth_token.")?;
-
+ 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);
- Ok(())
}
/// This allows adding an entry to the op_auth_map, indexed by the operation challenge.
@@ -749,22 +757,98 @@
}
/// Given the set of key parameters and flags, check if super encryption is required.
- pub fn super_encryption_required(key_parameters: &[KeyParameter], flags: Option<i32>) -> bool {
- let auth_bound = key_parameters.iter().any(|kp| kp.get_tag() == Tag::USER_SECURE_ID);
-
- let skip_lskf_binding = if let Some(flags) = flags {
- (flags & KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING) != 0
- } else {
- false
- };
-
- auth_bound && !skip_lskf_binding
+ 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
}
-}
-impl Default for Enforcements {
- fn default() -> Self {
- Self::new()
+ /// 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("In get_auth_tokens: No auth token found.");
+ }
+ } else {
+ return Err(AuthzError::Rc(AuthzResponseCode::NO_AUTH_TOKEN_FOUND)).context(
+ concat!(
+ "In get_auth_tokens: 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("In get_auth_tokens. Error in getting timestamp token.")?;
+ Ok((auth_token, tst))
}
}
diff --git a/keystore2/src/entropy.rs b/keystore2/src/entropy.rs
new file mode 100644
index 0000000..de38187
--- /dev/null
+++ b/keystore2/src/entropy.rs
@@ -0,0 +1,98 @@
+// 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.
+
+//! This module holds functionality for retrieving and distributing entropy.
+
+use anyhow::{Context, Result};
+use log::error;
+use std::time::{Duration, Instant};
+
+static ENTROPY_SIZE: usize = 64;
+static MIN_FEED_INTERVAL_SECS: u64 = 30;
+
+#[derive(Default)]
+struct FeederInfo {
+ last_feed: Option<Instant>,
+}
+
+/// Register the entropy feeder as an idle callback.
+pub fn register_feeder() {
+ crate::globals::ASYNC_TASK.add_idle(|shelf| {
+ let mut info = shelf.get_mut::<FeederInfo>();
+ let now = Instant::now();
+ let feed_needed = match info.last_feed {
+ None => true,
+ Some(last) => now.duration_since(last) > Duration::from_secs(MIN_FEED_INTERVAL_SECS),
+ };
+ if feed_needed {
+ info.last_feed = Some(now);
+ feed_devices();
+ }
+ });
+}
+
+fn get_entropy(size: usize) -> Result<Vec<u8>> {
+ keystore2_crypto::generate_random_data(size).context("Retrieving entropy for KeyMint device")
+}
+
+/// Feed entropy to all known KeyMint devices.
+pub fn feed_devices() {
+ let km_devs = crate::globals::get_keymint_devices();
+ if km_devs.is_empty() {
+ return;
+ }
+ let data = match get_entropy(km_devs.len() * ENTROPY_SIZE) {
+ Ok(data) => data,
+ Err(e) => {
+ error!(
+ "Failed to retrieve {}*{} bytes of entropy: {:?}",
+ km_devs.len(),
+ ENTROPY_SIZE,
+ e
+ );
+ return;
+ }
+ };
+ for (i, km_dev) in km_devs.iter().enumerate() {
+ let offset = i * ENTROPY_SIZE;
+ let sub_data = &data[offset..(offset + ENTROPY_SIZE)];
+ if let Err(e) = km_dev.addRngEntropy(sub_data) {
+ error!("Failed to feed entropy to KeyMint device: {:?}", e);
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::collections::HashSet;
+
+ #[test]
+ fn test_entropy_size() {
+ for size in &[0, 1, 4, 8, 256, 4096] {
+ let data = get_entropy(*size).expect("failed to get entropy");
+ assert_eq!(data.len(), *size);
+ }
+ }
+ #[test]
+ fn test_entropy_uniqueness() {
+ let count = 10;
+ let mut seen = HashSet::new();
+ for _i in 0..count {
+ let data = get_entropy(16).expect("failed to get entropy");
+ seen.insert(data);
+ }
+ assert_eq!(seen.len(), count);
+ }
+}
diff --git a/keystore2/src/error.rs b/keystore2/src/error.rs
index d67f5f4..f969cb6 100644
--- a/keystore2/src/error.rs
+++ b/keystore2/src/error.rs
@@ -30,16 +30,13 @@
//! Keystore functions should use `anyhow::Result` to return error conditions, and
//! context should be added every time an error is forwarded.
-use std::cmp::PartialEq;
-
pub use android_hardware_security_keymint::aidl::android::hardware::security::keymint::ErrorCode::ErrorCode;
pub use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
-
-use keystore2_selinux as selinux;
-
use android_system_keystore2::binder::{
ExceptionCode, Result as BinderResult, Status as BinderStatus, StatusCode,
};
+use keystore2_selinux as selinux;
+use std::cmp::PartialEq;
/// This is the main Keystore error type. It wraps the Keystore `ResponseCode` generated
/// from AIDL in the `Rc` variant and Keymint `ErrorCode` in the Km variant.
@@ -140,7 +137,7 @@
/// This function should be used by Keystore service calls to translate error conditions
/// into service specific exceptions.
///
-/// All error conditions get logged by this function.
+/// All error conditions get logged by this function, except for KEY_NOT_FOUND error.
///
/// All `Error::Rc(x)` and `Error::Km(x)` variants get mapped onto a service specific error
/// code of x. This is possible because KeyMint `ErrorCode` errors are always negative and
@@ -171,31 +168,64 @@
where
F: FnOnce(U) -> BinderResult<T>,
{
+ map_err_with(
+ result,
+ |e| {
+ // Make the key not found errors silent.
+ if !matches!(
+ e.root_cause().downcast_ref::<Error>(),
+ Some(Error::Rc(ResponseCode::KEY_NOT_FOUND))
+ ) {
+ log::error!("{:?}", e);
+ }
+ e
+ },
+ handle_ok,
+ )
+}
+
+/// This function behaves similar to map_or_log_error, but it does not log the errors, instead
+/// it calls map_err on the error before mapping it to a binder result allowing callers to
+/// log or transform the error before mapping it.
+pub fn map_err_with<T, U, F1, F2>(
+ result: anyhow::Result<U>,
+ map_err: F1,
+ handle_ok: F2,
+) -> BinderResult<T>
+where
+ F1: FnOnce(anyhow::Error) -> anyhow::Error,
+ F2: FnOnce(U) -> BinderResult<T>,
+{
result.map_or_else(
|e| {
- log::error!("{:?}", e);
- let root_cause = e.root_cause();
- let rc = match root_cause.downcast_ref::<Error>() {
- Some(Error::Rc(rcode)) => rcode.0,
- Some(Error::Km(ec)) => ec.0,
- Some(Error::Rp(_)) => ResponseCode::SYSTEM_ERROR.0,
- // If an Error::Binder reaches this stage we report a system error.
- // The exception code and possible service specific error will be
- // printed in the error log above.
- Some(Error::Binder(_, _)) | Some(Error::BinderTransaction(_)) => {
- ResponseCode::SYSTEM_ERROR.0
- }
- None => match root_cause.downcast_ref::<selinux::Error>() {
- Some(selinux::Error::PermissionDenied) => ResponseCode::PERMISSION_DENIED.0,
- _ => ResponseCode::SYSTEM_ERROR.0,
- },
- };
+ let e = map_err(e);
+ let rc = get_error_code(&e);
Err(BinderStatus::new_service_specific_error(rc, None))
},
handle_ok,
)
}
+/// Returns the error code given a reference to the error
+pub fn get_error_code(e: &anyhow::Error) -> i32 {
+ let root_cause = e.root_cause();
+ match root_cause.downcast_ref::<Error>() {
+ Some(Error::Rc(rcode)) => rcode.0,
+ Some(Error::Km(ec)) => ec.0,
+ Some(Error::Rp(_)) => ResponseCode::SYSTEM_ERROR.0,
+ // If an Error::Binder reaches this stage we report a system error.
+ // The exception code and possible service specific error will be
+ // printed in the error log above.
+ Some(Error::Binder(_, _)) | Some(Error::BinderTransaction(_)) => {
+ ResponseCode::SYSTEM_ERROR.0
+ }
+ None => match root_cause.downcast_ref::<selinux::Error>() {
+ Some(selinux::Error::PermissionDenied) => ResponseCode::PERMISSION_DENIED.0,
+ _ => ResponseCode::SYSTEM_ERROR.0,
+ },
+ }
+}
+
#[cfg(test)]
pub mod tests {
diff --git a/keystore2/src/fuzzers/Android.bp b/keystore2/src/fuzzers/Android.bp
new file mode 100644
index 0000000..384ab77
--- /dev/null
+++ b/keystore2/src/fuzzers/Android.bp
@@ -0,0 +1,29 @@
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_fuzz {
+ name: "legacy_blob_fuzzer",
+ srcs: ["legacy_blob_fuzzer.rs"],
+ rustlibs: [
+ "libkeystore2",
+ ],
+ fuzz_config: {
+ fuzz_on_haiku_device: true,
+ fuzz_on_haiku_host: false,
+ },
+}
diff --git a/keystore2/src/fuzzers/legacy_blob_fuzzer.rs b/keystore2/src/fuzzers/legacy_blob_fuzzer.rs
new file mode 100644
index 0000000..7e3e848
--- /dev/null
+++ b/keystore2/src/fuzzers/legacy_blob_fuzzer.rs
@@ -0,0 +1,26 @@
+// 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.
+
+#![allow(missing_docs)]
+#![no_main]
+#[macro_use]
+extern crate libfuzzer_sys;
+use keystore2::legacy_blob::LegacyBlobLoader;
+
+fuzz_target!(|data: &[u8]| {
+ if !data.is_empty() {
+ let string = data.iter().filter_map(|c| std::char::from_u32(*c as u32)).collect::<String>();
+ let _res = LegacyBlobLoader::decode_alias(&string);
+ }
+});
diff --git a/keystore2/src/gc.rs b/keystore2/src/gc.rs
index 6cc0f27..25f08c8 100644
--- a/keystore2/src/gc.rs
+++ b/keystore2/src/gc.rs
@@ -20,22 +20,28 @@
use crate::{
async_task,
- database::{KeystoreDB, Uuid},
+ database::{BlobMetaData, KeystoreDB, Uuid},
super_key::SuperKeyManager,
};
use anyhow::{Context, Result};
use async_task::AsyncTask;
-use std::sync::Arc;
+use std::sync::{
+ atomic::{AtomicU8, Ordering},
+ Arc,
+};
pub struct Gc {
async_task: Arc<AsyncTask>,
+ notified: Arc<AtomicU8>,
}
impl Gc {
/// Creates a garbage collector using the given async_task.
- /// The garbage collector needs a function to invalidate key blobs and a database connection.
- /// Both are obtained from the init function. The function is only called if this is first
- /// time a garbage collector was initialized with the given AsyncTask instance.
+ /// The garbage collector needs a function to invalidate key blobs, a database connection,
+ /// and a reference to the `SuperKeyManager`. They are obtained from the init function.
+ /// The function is only called if this is first time a garbage collector was initialized
+ /// with the given AsyncTask instance.
+ /// Note: It is a logical error to initialize different Gc instances with the same `AsyncTask`.
pub fn new_init_with<F>(async_task: Arc<AsyncTask>, init: F) -> Self
where
F: FnOnce() -> (
@@ -46,34 +52,43 @@
+ 'static,
{
let weak_at = Arc::downgrade(&async_task);
+ let notified = Arc::new(AtomicU8::new(0));
+ let notified_clone = notified.clone();
// Initialize the task's shelf.
async_task.queue_hi(move |shelf| {
let (invalidate_key, db, super_key) = init();
+ let notified = notified_clone;
shelf.get_or_put_with(|| GcInternal {
- blob_id_to_delete: None,
+ deleted_blob_ids: vec![],
+ superseded_blobs: vec![],
invalidate_key,
db,
async_task: weak_at,
super_key,
+ notified,
});
});
- Self { async_task }
+ Self { async_task, notified }
}
/// Notifies the key garbage collector to iterate through orphaned and superseded blobs and
/// attempts their deletion. We only process one key at a time and then schedule another
/// attempt by queueing it in the async_task (low priority) queue.
pub fn notify_gc(&self) {
- self.async_task.queue_lo(|shelf| shelf.get_downcast_mut::<GcInternal>().unwrap().step())
+ if let Ok(0) = self.notified.compare_exchange(0, 1, Ordering::Relaxed, Ordering::Relaxed) {
+ self.async_task.queue_lo(|shelf| shelf.get_downcast_mut::<GcInternal>().unwrap().step())
+ }
}
}
struct GcInternal {
- blob_id_to_delete: Option<i64>,
+ deleted_blob_ids: Vec<i64>,
+ superseded_blobs: Vec<(i64, Vec<u8>, BlobMetaData)>,
invalidate_key: Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>,
db: KeystoreDB,
async_task: std::sync::Weak<AsyncTask>,
super_key: Arc<SuperKeyManager>,
+ notified: Arc<AtomicU8>,
}
impl GcInternal {
@@ -81,16 +96,23 @@
/// We process one key at a time, because deleting a key is a time consuming process which
/// may involve calling into the KeyMint backend and we don't want to hog neither the backend
/// nor the database for extended periods of time.
+ /// To limit the number of database transactions, which are also expensive and competing
+ /// with threads on the critical path, deleted blobs are loaded in batches.
fn process_one_key(&mut self) -> Result<()> {
- if let Some((blob_id, blob, blob_metadata)) = self
- .db
- .handle_next_superseded_blob(self.blob_id_to_delete.take())
- .context("In process_one_key: Trying to handle superseded blob.")?
- {
- // Set the blob_id as the next to be deleted blob. So it will be
+ if self.superseded_blobs.is_empty() {
+ let blobs = self
+ .db
+ .handle_next_superseded_blobs(&self.deleted_blob_ids, 20)
+ .context("In process_one_key: Trying to handle superseded blob.")?;
+ self.deleted_blob_ids = vec![];
+ self.superseded_blobs = blobs;
+ }
+
+ if let Some((blob_id, blob, blob_metadata)) = self.superseded_blobs.pop() {
+ // Add the next blob_id to the deleted blob ids list. So it will be
// removed from the database regardless of whether the following
// succeeds or not.
- self.blob_id_to_delete = Some(blob_id);
+ self.deleted_blob_ids.push(blob_id);
// If the key has a km_uuid we try to get the corresponding device
// and delete the key, unwrapping if necessary and possible.
@@ -101,7 +123,7 @@
.super_key
.unwrap_key_if_required(&blob_metadata, &blob)
.context("In process_one_key: Trying to unwrap to-be-deleted blob.")?;
- (self.invalidate_key)(&uuid, &*blob)
+ (self.invalidate_key)(uuid, &*blob)
.context("In process_one_key: Trying to invalidate key.")?;
}
}
@@ -110,13 +132,20 @@
/// Processes one key and then schedules another attempt until it runs out of blobs to delete.
fn step(&mut self) {
+ self.notified.store(0, Ordering::Relaxed);
if let Err(e) = self.process_one_key() {
log::error!("Error trying to delete blob entry. {:?}", e);
}
// Schedule the next step. This gives high priority requests a chance to interleave.
- if self.blob_id_to_delete.is_some() {
+ if !self.deleted_blob_ids.is_empty() {
if let Some(at) = self.async_task.upgrade() {
- at.queue_lo(move |shelf| shelf.get_downcast_mut::<GcInternal>().unwrap().step());
+ if let Ok(0) =
+ self.notified.compare_exchange(0, 1, Ordering::Relaxed, Ordering::Relaxed)
+ {
+ at.queue_lo(move |shelf| {
+ shelf.get_downcast_mut::<GcInternal>().unwrap().step()
+ });
+ }
}
}
}
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
index 9668ee3..eae5ad0 100644
--- a/keystore2/src/globals.rs
+++ b/keystore2/src/globals.rs
@@ -20,7 +20,7 @@
use crate::legacy_blob::LegacyBlobLoader;
use crate::legacy_migrator::LegacyMigrator;
use crate::super_key::SuperKeyManager;
-use crate::utils::Asp;
+use crate::utils::watchdog as wd;
use crate::{async_task::AsyncTask, database::MonotonicRawTime};
use crate::{
database::KeystoreDB,
@@ -32,11 +32,16 @@
IKeyMintDevice::IKeyMintDevice, IRemotelyProvisionedComponent::IRemotelyProvisionedComponent,
KeyMintHardwareInfo::KeyMintHardwareInfo, SecurityLevel::SecurityLevel,
};
+use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
+ ISecureClock::ISecureClock,
+};
use android_hardware_security_keymint::binder::{StatusCode, Strong};
use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
use anyhow::{Context, Result};
+use binder::FromIBinder;
+use keystore2_vintf::get_aidl_instances;
use lazy_static::lazy_static;
-use std::sync::{Arc, Mutex};
+use std::sync::{Arc, Mutex, RwLock};
use std::{cell::RefCell, sync::Once};
use std::{collections::HashMap, path::Path, path::PathBuf};
@@ -51,27 +56,13 @@
/// is run only once, as long as the ASYNC_TASK instance is the same. So only one additional
/// database connection is created for the garbage collector worker.
pub fn create_thread_local_db() -> KeystoreDB {
- let gc = Gc::new_init_with(ASYNC_TASK.clone(), || {
- (
- Box::new(|uuid, blob| {
- let km_dev: Strong<dyn IKeyMintDevice> =
- get_keymint_dev_by_uuid(uuid).map(|(dev, _)| dev)?.get_interface()?;
- map_km_error(km_dev.deleteKey(&*blob))
- .context("In invalidate key closure: Trying to invalidate key blob.")
- }),
- KeystoreDB::new(&DB_PATH.lock().expect("Could not get the database directory."), None)
- .expect("Failed to open database."),
- SUPER_KEY.clone(),
- )
- });
+ let db_path = DB_PATH.read().expect("Could not get the database directory.");
- let mut db =
- KeystoreDB::new(&DB_PATH.lock().expect("Could not get the database directory."), Some(gc))
- .expect("Failed to open database.");
+ let mut db = KeystoreDB::new(&db_path, Some(GC.clone())).expect("Failed to open database.");
+
DB_INIT.call_once(|| {
log::info!("Touching Keystore 2.0 database for this first time since boot.");
- db.insert_last_off_body(MonotonicRawTime::now())
- .expect("Could not initialize database with last off body.");
+ db.insert_last_off_body(MonotonicRawTime::now());
log::info!("Calling cleanup leftovers.");
let n = db.cleanup_leftovers().expect("Failed to cleanup database on startup.");
if n != 0 {
@@ -96,30 +87,33 @@
RefCell::new(create_thread_local_db());
}
-#[derive(Default)]
-struct DevicesMap {
- devices_by_uuid: HashMap<Uuid, (Asp, KeyMintHardwareInfo)>,
+struct DevicesMap<T: FromIBinder + ?Sized> {
+ devices_by_uuid: HashMap<Uuid, (Strong<T>, KeyMintHardwareInfo)>,
uuid_by_sec_level: HashMap<SecurityLevel, Uuid>,
}
-impl DevicesMap {
+impl<T: FromIBinder + ?Sized> DevicesMap<T> {
fn dev_by_sec_level(
&self,
sec_level: &SecurityLevel,
- ) -> Option<(Asp, KeyMintHardwareInfo, Uuid)> {
+ ) -> Option<(Strong<T>, KeyMintHardwareInfo, Uuid)> {
self.uuid_by_sec_level.get(sec_level).and_then(|uuid| self.dev_by_uuid(uuid))
}
- fn dev_by_uuid(&self, uuid: &Uuid) -> Option<(Asp, KeyMintHardwareInfo, Uuid)> {
+ fn dev_by_uuid(&self, uuid: &Uuid) -> Option<(Strong<T>, KeyMintHardwareInfo, Uuid)> {
self.devices_by_uuid
.get(uuid)
.map(|(dev, hw_info)| ((*dev).clone(), (*hw_info).clone(), *uuid))
}
+ fn devices(&self) -> Vec<Strong<T>> {
+ self.devices_by_uuid.values().map(|(dev, _)| dev.clone()).collect()
+ }
+
/// The requested security level and the security level of the actual implementation may
/// differ. So we map the requested security level to the uuid of the implementation
/// so that there cannot be any confusion as to which KeyMint instance is requested.
- fn insert(&mut self, sec_level: SecurityLevel, dev: Asp, hw_info: KeyMintHardwareInfo) {
+ fn insert(&mut self, sec_level: SecurityLevel, dev: Strong<T>, hw_info: KeyMintHardwareInfo) {
// For now we use the reported security level of the KM instance as UUID.
// TODO update this section once UUID was added to the KM hardware info.
let uuid: Uuid = sec_level.into();
@@ -128,45 +122,77 @@
}
}
-#[derive(Default)]
-struct RemotelyProvisionedDevicesMap {
- devices_by_sec_level: HashMap<SecurityLevel, Asp>,
+impl<T: FromIBinder + ?Sized> Default for DevicesMap<T> {
+ fn default() -> Self {
+ Self {
+ devices_by_uuid: HashMap::<Uuid, (Strong<T>, KeyMintHardwareInfo)>::new(),
+ uuid_by_sec_level: Default::default(),
+ }
+ }
}
-impl RemotelyProvisionedDevicesMap {
- fn dev_by_sec_level(&self, sec_level: &SecurityLevel) -> Option<Asp> {
+struct RemotelyProvisionedDevicesMap<T: FromIBinder + ?Sized> {
+ devices_by_sec_level: HashMap<SecurityLevel, Strong<T>>,
+}
+
+impl<T: FromIBinder + ?Sized> Default for RemotelyProvisionedDevicesMap<T> {
+ fn default() -> Self {
+ Self { devices_by_sec_level: HashMap::<SecurityLevel, Strong<T>>::new() }
+ }
+}
+
+impl<T: FromIBinder + ?Sized> RemotelyProvisionedDevicesMap<T> {
+ fn dev_by_sec_level(&self, sec_level: &SecurityLevel) -> Option<Strong<T>> {
self.devices_by_sec_level.get(sec_level).map(|dev| (*dev).clone())
}
- fn insert(&mut self, sec_level: SecurityLevel, dev: Asp) {
+ fn insert(&mut self, sec_level: SecurityLevel, dev: Strong<T>) {
self.devices_by_sec_level.insert(sec_level, dev);
}
}
lazy_static! {
/// The path where keystore stores all its keys.
- pub static ref DB_PATH: Mutex<PathBuf> = Mutex::new(
+ pub static ref DB_PATH: RwLock<PathBuf> = RwLock::new(
Path::new("/data/misc/keystore").to_path_buf());
/// Runtime database of unwrapped super keys.
pub static ref SUPER_KEY: Arc<SuperKeyManager> = Default::default();
/// Map of KeyMint devices.
- static ref KEY_MINT_DEVICES: Mutex<DevicesMap> = Default::default();
+ static ref KEY_MINT_DEVICES: Mutex<DevicesMap<dyn IKeyMintDevice>> = Default::default();
/// Timestamp service.
- static ref TIME_STAMP_DEVICE: Mutex<Option<Asp>> = Default::default();
+ static ref TIME_STAMP_DEVICE: Mutex<Option<Strong<dyn ISecureClock>>> = Default::default();
/// RemotelyProvisionedComponent HAL devices.
- static ref REMOTELY_PROVISIONED_COMPONENT_DEVICES: Mutex<RemotelyProvisionedDevicesMap> = Default::default();
+ static ref REMOTELY_PROVISIONED_COMPONENT_DEVICES:
+ Mutex<RemotelyProvisionedDevicesMap<dyn IRemotelyProvisionedComponent>> =
+ Default::default();
/// A single on-demand worker thread that handles deferred tasks with two different
/// priorities.
pub static ref ASYNC_TASK: Arc<AsyncTask> = Default::default();
/// Singleton for enforcements.
- pub static ref ENFORCEMENTS: Enforcements = Enforcements::new();
+ pub static ref ENFORCEMENTS: Enforcements = Default::default();
/// LegacyBlobLoader is initialized and exists globally.
/// The same directory used by the database is used by the LegacyBlobLoader as well.
pub static ref LEGACY_BLOB_LOADER: Arc<LegacyBlobLoader> = Arc::new(LegacyBlobLoader::new(
- &DB_PATH.lock().expect("Could not get the database path for legacy blob loader.")));
+ &DB_PATH.read().expect("Could not get the database path for legacy blob loader.")));
/// Legacy migrator. Atomically migrates legacy blobs to the database.
pub static ref LEGACY_MIGRATOR: Arc<LegacyMigrator> =
- Arc::new(LegacyMigrator::new(ASYNC_TASK.clone()));
+ Arc::new(LegacyMigrator::new(Arc::new(Default::default())));
+ /// Background thread which handles logging via statsd and logd
+ pub static ref LOGS_HANDLER: Arc<AsyncTask> = Default::default();
+
+ static ref GC: Arc<Gc> = Arc::new(Gc::new_init_with(ASYNC_TASK.clone(), || {
+ (
+ Box::new(|uuid, blob| {
+ let km_dev = get_keymint_dev_by_uuid(uuid).map(|(dev, _)| dev)?;
+ let _wp = wd::watch_millis("In invalidate key closure: calling deleteKey", 500);
+ map_km_error(km_dev.deleteKey(&*blob))
+ .context("In invalidate key closure: Trying to invalidate key blob.")
+ }),
+ KeystoreDB::new(&DB_PATH.read().expect("Could not get the database directory."), None)
+ .expect("Failed to open database."),
+ SUPER_KEY.clone(),
+ )
+ }));
}
static KEYMINT_SERVICE_NAME: &str = "android.hardware.security.keymint.IKeyMintDevice";
@@ -174,44 +200,76 @@
/// Make a new connection to a KeyMint device of the given security level.
/// If no native KeyMint device can be found this function also brings
/// up the compatibility service and attempts to connect to the legacy wrapper.
-fn connect_keymint(security_level: &SecurityLevel) -> Result<(Asp, KeyMintHardwareInfo)> {
+fn connect_keymint(
+ security_level: &SecurityLevel,
+) -> Result<(Strong<dyn IKeyMintDevice>, KeyMintHardwareInfo)> {
+ let keymint_instances =
+ get_aidl_instances("android.hardware.security.keymint", 1, "IKeyMintDevice");
+
let service_name = match *security_level {
- SecurityLevel::TRUSTED_ENVIRONMENT => format!("{}/default", KEYMINT_SERVICE_NAME),
- SecurityLevel::STRONGBOX => format!("{}/strongbox", KEYMINT_SERVICE_NAME),
+ SecurityLevel::TRUSTED_ENVIRONMENT => {
+ if keymint_instances.iter().any(|instance| *instance == "default") {
+ Some(format!("{}/default", KEYMINT_SERVICE_NAME))
+ } else {
+ None
+ }
+ }
+ SecurityLevel::STRONGBOX => {
+ if keymint_instances.iter().any(|instance| *instance == "strongbox") {
+ Some(format!("{}/strongbox", KEYMINT_SERVICE_NAME))
+ } else {
+ None
+ }
+ }
_ => {
return Err(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
.context("In connect_keymint.")
}
};
- let keymint = map_binder_status_code(binder::get_interface(&service_name))
- .context("In connect_keymint: Trying to connect to genuine KeyMint service.")
- .or_else(|e| {
- match e.root_cause().downcast_ref::<Error>() {
- Some(Error::BinderTransaction(StatusCode::NAME_NOT_FOUND)) => {
- // This is a no-op if it was called before.
- keystore2_km_compat::add_keymint_device_service();
+ let (keymint, hal_version) = if let Some(service_name) = service_name {
+ (
+ map_binder_status_code(binder::get_interface(&service_name))
+ .context("In connect_keymint: Trying to connect to genuine KeyMint service.")?,
+ Some(100i32), // The HAL version code for KeyMint V1 is 100.
+ )
+ } else {
+ // This is a no-op if it was called before.
+ keystore2_km_compat::add_keymint_device_service();
- let keystore_compat_service: Strong<dyn IKeystoreCompatService> =
- map_binder_status_code(binder::get_interface("android.security.compat"))
- .context("In connect_keymint: Trying to connect to compat service.")?;
- map_binder_status(keystore_compat_service.getKeyMintDevice(*security_level))
- .map_err(|e| match e {
- Error::BinderTransaction(StatusCode::NAME_NOT_FOUND) => {
- Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)
- }
- e => e,
- })
- .context("In connect_keymint: Trying to get Legacy wrapper.")
- }
- _ => Err(e),
- }
- })?;
+ let keystore_compat_service: Strong<dyn IKeystoreCompatService> =
+ map_binder_status_code(binder::get_interface("android.security.compat"))
+ .context("In connect_keymint: Trying to connect to compat service.")?;
+ (
+ map_binder_status(keystore_compat_service.getKeyMintDevice(*security_level))
+ .map_err(|e| match e {
+ Error::BinderTransaction(StatusCode::NAME_NOT_FOUND) => {
+ Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)
+ }
+ e => e,
+ })
+ .context("In connect_keymint: Trying to get Legacy wrapper.")?,
+ None,
+ )
+ };
- let hw_info = map_km_error(keymint.getHardwareInfo())
+ let wp = wd::watch_millis("In connect_keymint: calling getHardwareInfo()", 500);
+ let mut hw_info = map_km_error(keymint.getHardwareInfo())
.context("In connect_keymint: Failed to get hardware info.")?;
+ drop(wp);
- Ok((Asp::new(keymint.as_binder()), hw_info))
+ // The legacy wrapper sets hw_info.versionNumber to the underlying HAL version like so:
+ // 10 * <major> + <minor>, e.g., KM 3.0 = 30. So 30, 40, and 41 are the only viable values.
+ // For KeyMint the versionNumber is implementation defined and thus completely meaningless
+ // to Keystore 2.0. So at this point the versionNumber field is set to the HAL version, so
+ // that higher levels have a meaningful guide as to which feature set to expect from the
+ // implementation. As of this writing the only meaningful version number is 100 for KeyMint V1,
+ // and future AIDL versions should follow the pattern <AIDL version> * 100.
+ if let Some(hal_version) = hal_version {
+ hw_info.versionNumber = hal_version;
+ }
+
+ Ok((keymint, hw_info))
}
/// Get a keymint device for the given security level either from our cache or
@@ -219,9 +277,9 @@
/// TODO the latter can be removed when the uuid is part of the hardware info.
pub fn get_keymint_device(
security_level: &SecurityLevel,
-) -> Result<(Asp, KeyMintHardwareInfo, Uuid)> {
+) -> Result<(Strong<dyn IKeyMintDevice>, KeyMintHardwareInfo, Uuid)> {
let mut devices_map = KEY_MINT_DEVICES.lock().unwrap();
- if let Some((dev, hw_info, uuid)) = devices_map.dev_by_sec_level(&security_level) {
+ if let Some((dev, hw_info, uuid)) = devices_map.dev_by_sec_level(security_level) {
Ok((dev, hw_info, uuid))
} else {
let (dev, hw_info) = connect_keymint(security_level).context("In get_keymint_device.")?;
@@ -235,7 +293,9 @@
/// attempt to establish a new connection. It is assumed that the cache is already populated
/// when this is called. This is a fair assumption, because service.rs iterates through all
/// security levels when it gets instantiated.
-pub fn get_keymint_dev_by_uuid(uuid: &Uuid) -> Result<(Asp, KeyMintHardwareInfo)> {
+pub fn get_keymint_dev_by_uuid(
+ uuid: &Uuid,
+) -> Result<(Strong<dyn IKeyMintDevice>, KeyMintHardwareInfo)> {
let devices_map = KEY_MINT_DEVICES.lock().unwrap();
if let Some((dev, hw_info, _)) = devices_map.dev_by_uuid(uuid) {
Ok((dev, hw_info))
@@ -244,46 +304,53 @@
}
}
+/// Return all known keymint devices.
+pub fn get_keymint_devices() -> Vec<Strong<dyn IKeyMintDevice>> {
+ KEY_MINT_DEVICES.lock().unwrap().devices()
+}
+
static TIME_STAMP_SERVICE_NAME: &str = "android.hardware.security.secureclock.ISecureClock";
/// Make a new connection to a secure clock service.
/// If no native SecureClock device can be found brings up the compatibility service and attempts
/// to connect to the legacy wrapper.
-fn connect_secureclock() -> Result<Asp> {
- let secureclock = map_binder_status_code(binder::get_interface(TIME_STAMP_SERVICE_NAME))
- .context("In connect_secureclock: Trying to connect to genuine secure clock service.")
- .or_else(|e| {
- match e.root_cause().downcast_ref::<Error>() {
- Some(Error::BinderTransaction(StatusCode::NAME_NOT_FOUND)) => {
- // This is a no-op if it was called before.
- keystore2_km_compat::add_keymint_device_service();
+fn connect_secureclock() -> Result<Strong<dyn ISecureClock>> {
+ let secureclock_instances =
+ get_aidl_instances("android.hardware.security.secureclock", 1, "ISecureClock");
- let keystore_compat_service: Strong<dyn IKeystoreCompatService> =
- map_binder_status_code(binder::get_interface("android.security.compat"))
- .context(
- "In connect_secureclock: Trying to connect to compat service.",
- )?;
+ let secure_clock_available =
+ secureclock_instances.iter().any(|instance| *instance == "default");
- // Legacy secure clock services were only implemented by TEE.
- map_binder_status(keystore_compat_service.getSecureClock())
- .map_err(|e| match e {
- Error::BinderTransaction(StatusCode::NAME_NOT_FOUND) => {
- Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)
- }
- e => e,
- })
- .context("In connect_secureclock: Trying to get Legacy wrapper.")
+ let default_time_stamp_service_name = format!("{}/default", TIME_STAMP_SERVICE_NAME);
+
+ let secureclock = if secure_clock_available {
+ map_binder_status_code(binder::get_interface(&default_time_stamp_service_name))
+ .context("In connect_secureclock: Trying to connect to genuine secure clock service.")
+ } else {
+ // This is a no-op if it was called before.
+ keystore2_km_compat::add_keymint_device_service();
+
+ let keystore_compat_service: Strong<dyn IKeystoreCompatService> =
+ map_binder_status_code(binder::get_interface("android.security.compat"))
+ .context("In connect_secureclock: Trying to connect to compat service.")?;
+
+ // Legacy secure clock services were only implemented by TEE.
+ map_binder_status(keystore_compat_service.getSecureClock())
+ .map_err(|e| match e {
+ Error::BinderTransaction(StatusCode::NAME_NOT_FOUND) => {
+ Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)
}
- _ => Err(e),
- }
- })?;
+ e => e,
+ })
+ .context("In connect_secureclock: Trying to get Legacy wrapper.")
+ }?;
- Ok(Asp::new(secureclock.as_binder()))
+ Ok(secureclock)
}
/// Get the timestamp service that verifies auth token timeliness towards security levels with
/// different clocks.
-pub fn get_timestamp_service() -> Result<Asp> {
+pub fn get_timestamp_service() -> Result<Strong<dyn ISecureClock>> {
let mut ts_device = TIME_STAMP_DEVICE.lock().unwrap();
if let Some(dev) = &*ts_device {
Ok(dev.clone())
@@ -297,19 +364,31 @@
static REMOTE_PROVISIONING_HAL_SERVICE_NAME: &str =
"android.hardware.security.keymint.IRemotelyProvisionedComponent";
-fn connect_remotely_provisioned_component(security_level: &SecurityLevel) -> Result<Asp> {
+fn connect_remotely_provisioned_component(
+ security_level: &SecurityLevel,
+) -> Result<Strong<dyn IRemotelyProvisionedComponent>> {
+ let remotely_prov_instances =
+ get_aidl_instances("android.hardware.security.keymint", 1, "IRemotelyProvisionedComponent");
+
let service_name = match *security_level {
SecurityLevel::TRUSTED_ENVIRONMENT => {
- format!("{}/default", REMOTE_PROVISIONING_HAL_SERVICE_NAME)
+ if remotely_prov_instances.iter().any(|instance| *instance == "default") {
+ Some(format!("{}/default", REMOTE_PROVISIONING_HAL_SERVICE_NAME))
+ } else {
+ None
+ }
}
- SecurityLevel::STRONGBOX => format!("{}/strongbox", REMOTE_PROVISIONING_HAL_SERVICE_NAME),
- _ => {
- // Given the integration of IRemotelyProvisionedComponent with KeyMint, it is reasonable
- // to return HARDWARE_TYPE_UNAVAILABLE as a Km error if it cannot be found.
- return Err(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
- .context("In connect_remotely_provisioned_component.");
+ SecurityLevel::STRONGBOX => {
+ if remotely_prov_instances.iter().any(|instance| *instance == "strongbox") {
+ Some(format!("{}/strongbox", REMOTE_PROVISIONING_HAL_SERVICE_NAME))
+ } else {
+ None
+ }
}
- };
+ _ => None,
+ }
+ .ok_or(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
+ .context("In connect_remotely_provisioned_component.")?;
let rem_prov_hal: Strong<dyn IRemotelyProvisionedComponent> =
map_binder_status_code(binder::get_interface(&service_name))
@@ -318,14 +397,16 @@
" RemotelyProvisionedComponent service."
))
.map_err(|e| e)?;
- Ok(Asp::new(rem_prov_hal.as_binder()))
+ Ok(rem_prov_hal)
}
/// Get a remote provisiong component device for the given security level either from the cache or
/// by making a new connection. Returns the device.
-pub fn get_remotely_provisioned_component(security_level: &SecurityLevel) -> Result<Asp> {
+pub fn get_remotely_provisioned_component(
+ security_level: &SecurityLevel,
+) -> Result<Strong<dyn IRemotelyProvisionedComponent>> {
let mut devices_map = REMOTELY_PROVISIONED_COMPONENT_DEVICES.lock().unwrap();
- if let Some(dev) = devices_map.dev_by_sec_level(&security_level) {
+ if let Some(dev) = devices_map.dev_by_sec_level(security_level) {
Ok(dev)
} else {
let dev = connect_remotely_provisioned_component(security_level)
diff --git a/keystore2/src/id_rotation.rs b/keystore2/src/id_rotation.rs
new file mode 100644
index 0000000..e3992d8
--- /dev/null
+++ b/keystore2/src/id_rotation.rs
@@ -0,0 +1,122 @@
+// 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.
+
+//! This module implements the unique id rotation privacy feature. Certain system components
+//! have the ability to include a per-app unique id into the key attestation. The key rotation
+//! feature assures that the unique id is rotated on factory reset at least once in a 30 day
+//! key rotation period.
+//!
+//! It is assumed that the timestamp file does not exist after a factory reset. So the creation
+//! time of the timestamp file provides a lower bound for the time since factory reset.
+
+use anyhow::{Context, Result};
+use std::fs;
+use std::io::ErrorKind;
+use std::path::{Path, PathBuf};
+use std::time::Duration;
+
+const ID_ROTATION_PERIOD: Duration = Duration::from_secs(30 * 24 * 60 * 60); // Thirty days.
+static TIMESTAMP_FILE_NAME: &str = "timestamp";
+
+/// The IdRotationState stores the path to the timestamp file for deferred usage. The data
+/// partition is usually not available when Keystore 2.0 starts up. So this object is created
+/// and passed down to the users of the feature which can then query the timestamp on demand.
+#[derive(Debug, Clone)]
+pub struct IdRotationState {
+ timestamp_path: PathBuf,
+}
+
+impl IdRotationState {
+ /// Creates a new IdRotationState. It holds the path to the timestamp file for deferred usage.
+ pub fn new(keystore_db_path: &Path) -> Self {
+ let mut timestamp_path = keystore_db_path.to_owned();
+ timestamp_path.push(TIMESTAMP_FILE_NAME);
+ Self { timestamp_path }
+ }
+
+ /// Reads the metadata of or creates the timestamp file. It returns true if the timestamp
+ /// file is younger than `ID_ROTATION_PERIOD`, i.e., 30 days.
+ pub fn had_factory_reset_since_id_rotation(&self) -> Result<bool> {
+ match fs::metadata(&self.timestamp_path) {
+ Ok(metadata) => {
+ let duration_since_factory_reset = metadata
+ .modified()
+ .context("File creation time not supported.")?
+ .elapsed()
+ .context("Failed to compute time elapsed since factory reset.")?;
+ Ok(duration_since_factory_reset < ID_ROTATION_PERIOD)
+ }
+ Err(e) => match e.kind() {
+ ErrorKind::NotFound => {
+ fs::File::create(&self.timestamp_path)
+ .context("Failed to create timestamp file.")?;
+ Ok(true)
+ }
+ _ => Err(e).context("Failed to open timestamp file."),
+ },
+ }
+ .context("In had_factory_reset_since_id_rotation:")
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use keystore2_test_utils::TempDir;
+ use nix::sys::stat::utimes;
+ use nix::sys::time::{TimeVal, TimeValLike};
+ use std::convert::TryInto;
+ use std::time::UNIX_EPOCH;
+
+ #[test]
+ fn test_had_factory_reset_since_id_rotation() -> Result<()> {
+ let temp_dir = TempDir::new("test_had_factory_reset_since_id_rotation_")
+ .expect("Failed to create temp dir.");
+ let id_rotation_state = IdRotationState::new(temp_dir.path());
+
+ let mut temp_file_path = temp_dir.path().to_owned();
+ temp_file_path.push(TIMESTAMP_FILE_NAME);
+
+ // The timestamp file should not exist.
+ assert!(!temp_file_path.exists());
+
+ // This should return true.
+ assert!(id_rotation_state.had_factory_reset_since_id_rotation()?);
+
+ // Now the timestamp file should exist.
+ assert!(temp_file_path.exists());
+
+ // We should still return true because the timestamp file is young.
+ assert!(id_rotation_state.had_factory_reset_since_id_rotation()?);
+
+ // Now let's age the timestamp file by backdating the modification time.
+ let metadata = fs::metadata(&temp_file_path)?;
+ let mtime = metadata.modified()?;
+ let mtime = mtime.duration_since(UNIX_EPOCH)?;
+ let mtime =
+ mtime.checked_sub(ID_ROTATION_PERIOD).expect("Failed to subtract id rotation period");
+ let mtime = TimeVal::seconds(mtime.as_secs().try_into().unwrap());
+
+ let atime = metadata.accessed()?;
+ let atime = atime.duration_since(UNIX_EPOCH)?;
+ let atime = TimeVal::seconds(atime.as_secs().try_into().unwrap());
+
+ utimes(&temp_file_path, &atime, &mtime)?;
+
+ // Now that the file has aged we should see false.
+ assert!(!id_rotation_state.had_factory_reset_since_id_rotation()?);
+
+ Ok(())
+ }
+}
diff --git a/keystore2/src/key_parameter.rs b/keystore2/src/key_parameter.rs
index 117dea8..771d609 100644
--- a/keystore2/src/key_parameter.rs
+++ b/keystore2/src/key_parameter.rs
@@ -92,7 +92,7 @@
use std::convert::TryInto;
-use crate::db_utils::SqlField;
+use crate::database::utils::SqlField;
use crate::error::Error as KeystoreError;
use crate::error::ResponseCode;
@@ -599,9 +599,9 @@
], [$($in)*]
}};
(@into $enum_name:ident, [$($out:tt)*], []) => {
- impl Into<KmKeyParameter> for $enum_name {
- fn into(self) -> KmKeyParameter {
- match self {
+ impl From<$enum_name> for KmKeyParameter {
+ fn from(x: $enum_name) -> Self {
+ match x {
$($out)*
}
}
@@ -776,7 +776,7 @@
implement_key_parameter_value! {
/// KeyParameterValue holds a value corresponding to one of the Tags defined in
-/// the AIDL spec at hardware/interfaces/keymint
+/// the AIDL spec at hardware/interfaces/security/keymint
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum KeyParameterValue {
/// Associated with Tag:INVALID
@@ -825,6 +825,9 @@
/// When deleted, the key is guaranteed to be permanently deleted and unusable
#[key_param(tag = ROLLBACK_RESISTANCE, field = BoolValue)]
RollbackResistance,
+ /// The Key shall only be used during the early boot stage
+ #[key_param(tag = EARLY_BOOT_ONLY, field = BoolValue)]
+ EarlyBootOnly,
/// The date and time at which the key becomes active
#[key_param(tag = ACTIVE_DATETIME, field = DateTime)]
ActiveDateTime(i64),
@@ -965,6 +968,9 @@
/// Used to deliver the not after date in milliseconds to KeyMint during key generation/import.
#[key_param(tag = CERTIFICATE_NOT_AFTER, field = DateTime)]
CertificateNotAfter(i64),
+ /// Specifies a maximum boot level at which a key should function
+ #[key_param(tag = MAX_BOOT_LEVEL, field = Integer)]
+ MaxBootLevel(i32),
}
}
@@ -1384,11 +1390,11 @@
db.prepare("SELECT tag, data, security_level FROM persistent.keyparameter")?;
let mut rows = stmt.query(NO_PARAMS)?;
let row = rows.next()?.unwrap();
- Ok(KeyParameter::new_from_sql(
+ KeyParameter::new_from_sql(
Tag(row.get(0)?),
&SqlField::new(1, row),
SecurityLevel(row.get(2)?),
- )?)
+ )
}
}
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
index 9dc59a2..abab4b6 100644
--- a/keystore2/src/keystore2_main.rs
+++ b/keystore2/src/keystore2_main.rs
@@ -14,20 +14,27 @@
//! This crate implements the Keystore 2.0 service entry point.
-use keystore2::apc::ApcManager;
-use keystore2::authorization::AuthorizationManager;
+use keystore2::entropy;
use keystore2::globals::ENFORCEMENTS;
+use keystore2::maintenance::Maintenance;
+use keystore2::metrics::Metrics;
+use keystore2::metrics_store;
use keystore2::remote_provisioning::RemoteProvisioningService;
use keystore2::service::KeystoreService;
-use keystore2::user_manager::UserManager;
+use keystore2::{apc::ApcManager, shared_secret_negotiation};
+use keystore2::{authorization::AuthorizationManager, id_rotation::IdRotationState};
+use legacykeystore::LegacyKeystore;
use log::{error, info};
-use std::{panic, path::Path, sync::mpsc::channel};
+use rusqlite::trace as sqlite_trace;
+use std::{os::raw::c_int, panic, path::Path, sync::mpsc::channel};
-static KS2_SERVICE_NAME: &str = "android.system.keystore2";
+static KS2_SERVICE_NAME: &str = "android.system.keystore2.IKeystoreService/default";
static APC_SERVICE_NAME: &str = "android.security.apc";
static AUTHORIZATION_SERVICE_NAME: &str = "android.security.authorization";
+static METRICS_SERVICE_NAME: &str = "android.security.metrics";
static REMOTE_PROVISIONING_SERVICE_NAME: &str = "android.security.remoteprovisioning";
-static USER_MANAGER_SERVICE_NAME: &str = "android.security.usermanager";
+static USER_MANAGER_SERVICE_NAME: &str = "android.security.maintenance";
+static LEGACY_KEYSTORE_SERVICE_NAME: &str = "android.security.legacykeystore";
/// Keystore 2.0 takes one argument which is a path indicating its designated working directory.
fn main() {
@@ -43,32 +50,44 @@
// Saying hi.
info!("Keystore2 is starting.");
- // Initialize the per boot database.
- let _keep_me_alive = keystore2::database::KeystoreDB::keep_perboot_db_alive()
- .expect("Failed to initialize the perboot database.");
-
let mut args = std::env::args();
args.next().expect("That's odd. How is there not even a first argument?");
+ // This must happen early before any other sqlite operations.
+ log::info!("Setting up sqlite logging for keystore2");
+ fn sqlite_log_handler(err: c_int, message: &str) {
+ log::error!("[SQLITE3] {}: {}", err, message);
+ }
+ unsafe { sqlite_trace::config_log(Some(sqlite_log_handler)) }
+ .expect("Error setting sqlite log callback.");
+
+ // Write/update keystore.crash_count system property.
+ metrics_store::update_keystore_crash_sysprop();
+
// Keystore 2.0 cannot change to the database directory (typically /data/misc/keystore) on
// startup as Keystore 1.0 did because Keystore 2.0 is intended to run much earlier than
// Keystore 1.0. Instead we set a global variable to the database path.
// For the ground truth check the service startup rule for init (typically in keystore2.rc).
- if let Some(dir) = args.next() {
- *keystore2::globals::DB_PATH.lock().expect("Could not lock DB_PATH.") =
- Path::new(&dir).to_path_buf();
+ let id_rotation_state = if let Some(dir) = args.next() {
+ let db_path = Path::new(&dir);
+ *keystore2::globals::DB_PATH.write().expect("Could not lock DB_PATH.") =
+ db_path.to_path_buf();
+ IdRotationState::new(db_path)
} else {
- panic!("Must specify a working directory.");
- }
+ panic!("Must specify a database directory.");
+ };
let (confirmation_token_sender, confirmation_token_receiver) = channel();
ENFORCEMENTS.install_confirmation_token_receiver(confirmation_token_receiver);
+ entropy::register_feeder();
+ shared_secret_negotiation::perform_shared_secret_negotiation();
+
info!("Starting thread pool now.");
binder::ProcessState::start_thread_pool();
- let ks_service = KeystoreService::new_native_binder().unwrap_or_else(|e| {
+ let ks_service = KeystoreService::new_native_binder(id_rotation_state).unwrap_or_else(|e| {
panic!("Failed to create service {} because of {:?}.", KS2_SERVICE_NAME, e);
});
binder::add_service(KS2_SERVICE_NAME, ks_service.as_binder()).unwrap_or_else(|e| {
@@ -91,15 +110,26 @@
panic!("Failed to register service {} because of {:?}.", AUTHORIZATION_SERVICE_NAME, e);
});
- let usermanager_service = UserManager::new_native_binder().unwrap_or_else(|e| {
+ let (delete_listener, legacykeystore) = LegacyKeystore::new_native_binder(
+ &keystore2::globals::DB_PATH.read().expect("Could not get DB_PATH."),
+ );
+
+ let maintenance_service = Maintenance::new_native_binder(delete_listener).unwrap_or_else(|e| {
panic!("Failed to create service {} because of {:?}.", USER_MANAGER_SERVICE_NAME, e);
});
- binder::add_service(USER_MANAGER_SERVICE_NAME, usermanager_service.as_binder()).unwrap_or_else(
+ binder::add_service(USER_MANAGER_SERVICE_NAME, maintenance_service.as_binder()).unwrap_or_else(
|e| {
panic!("Failed to register service {} because of {:?}.", USER_MANAGER_SERVICE_NAME, e);
},
);
+ let metrics_service = Metrics::new_native_binder().unwrap_or_else(|e| {
+ panic!("Failed to create service {} because of {:?}.", METRICS_SERVICE_NAME, e);
+ });
+ binder::add_service(METRICS_SERVICE_NAME, metrics_service.as_binder()).unwrap_or_else(|e| {
+ panic!("Failed to register service {} because of {:?}.", METRICS_SERVICE_NAME, e);
+ });
+
// Devices with KS2 and KM 1.0 may not have any IRemotelyProvisionedComponent HALs at all. Do
// not panic if new_native_binder returns failure because it could not find the TEE HAL.
if let Ok(remote_provisioning_service) = RemoteProvisioningService::new_native_binder() {
@@ -114,6 +144,16 @@
);
});
}
+
+ binder::add_service(LEGACY_KEYSTORE_SERVICE_NAME, legacykeystore.as_binder()).unwrap_or_else(
+ |e| {
+ panic!(
+ "Failed to register service {} because of {:?}.",
+ LEGACY_KEYSTORE_SERVICE_NAME, e
+ );
+ },
+ );
+
info!("Successfully registered Keystore 2.0 service.");
info!("Joining thread pool now.");
diff --git a/keystore2/src/km_compat/Android.bp b/keystore2/src/km_compat/Android.bp
index 6b635ff..32406ae 100644
--- a/keystore2/src/km_compat/Android.bp
+++ b/keystore2/src/km_compat/Android.bp
@@ -57,16 +57,17 @@
"android.hardware.keymaster@3.0",
"android.hardware.keymaster@4.0",
"android.hardware.keymaster@4.1",
- "android.hardware.security.keymint-V1-ndk_platform",
- "android.hardware.security.secureclock-V1-ndk_platform",
- "android.hardware.security.sharedsecret-V1-ndk_platform",
- "android.security.compat-ndk_platform",
- "android.system.keystore2-V1-ndk_platform",
+ "android.hardware.security.keymint-V1-ndk",
+ "android.hardware.security.secureclock-V1-ndk",
+ "android.hardware.security.sharedsecret-V1-ndk",
+ "android.security.compat-ndk",
+ "android.system.keystore2-V1-ndk",
"libbase",
"libbinder_ndk",
"libcrypto",
"libhidlbase",
"libkeymaster4_1support",
+ "libkeymint",
"libkeymint_support",
"libkeystore2_crypto",
"libutils",
@@ -77,10 +78,10 @@
name: "libkm_compat_service",
srcs: ["km_compat_service.cpp"],
shared_libs: [
- "android.hardware.security.keymint-V1-ndk_platform",
- "android.hardware.security.secureclock-V1-ndk_platform",
- "android.hardware.security.sharedsecret-V1-ndk_platform",
- "android.security.compat-ndk_platform",
+ "android.hardware.security.keymint-V1-ndk",
+ "android.hardware.security.secureclock-V1-ndk",
+ "android.hardware.security.sharedsecret-V1-ndk",
+ "android.security.compat-ndk",
"libbinder_ndk",
"libcrypto",
"libkm_compat",
@@ -106,11 +107,11 @@
"android.hardware.keymaster@3.0",
"android.hardware.keymaster@4.0",
"android.hardware.keymaster@4.1",
- "android.hardware.security.keymint-V1-ndk_platform",
- "android.hardware.security.secureclock-V1-ndk_platform",
- "android.hardware.security.sharedsecret-V1-ndk_platform",
- "android.security.compat-ndk_platform",
- "android.system.keystore2-V1-ndk_platform",
+ "android.hardware.security.keymint-V1-ndk",
+ "android.hardware.security.secureclock-V1-ndk",
+ "android.hardware.security.sharedsecret-V1-ndk",
+ "android.security.compat-ndk",
+ "android.system.keystore2-V1-ndk",
"libbase",
"libbinder_ndk",
"libcrypto",
diff --git a/keystore2/src/km_compat/km_compat.cpp b/keystore2/src/km_compat/km_compat.cpp
index b25cb0c..40ca554 100644
--- a/keystore2/src/km_compat/km_compat.cpp
+++ b/keystore2/src/km_compat/km_compat.cpp
@@ -17,9 +17,11 @@
#include "km_compat.h"
#include "km_compat_type_conversion.h"
+#include <AndroidKeyMintDevice.h>
#include <aidl/android/hardware/security/keymint/Algorithm.h>
#include <aidl/android/hardware/security/keymint/Digest.h>
#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/hardware/security/keymint/KeyParameterValue.h>
#include <aidl/android/hardware/security/keymint/PaddingMode.h>
#include <aidl/android/system/keystore2/ResponseCode.h>
#include <android-base/logging.h>
@@ -35,7 +37,9 @@
#include "certificate_utils.h"
using ::aidl::android::hardware::security::keymint::Algorithm;
+using ::aidl::android::hardware::security::keymint::CreateKeyMintDevice;
using ::aidl::android::hardware::security::keymint::Digest;
+using ::aidl::android::hardware::security::keymint::KeyParameterValue;
using ::aidl::android::hardware::security::keymint::PaddingMode;
using ::aidl::android::hardware::security::keymint::Tag;
using ::aidl::android::system::keystore2::ResponseCode;
@@ -104,7 +108,6 @@
case Tag::EC_CURVE:
case Tag::RSA_PUBLIC_EXPONENT:
case Tag::RSA_OAEP_MGF_DIGEST:
- case Tag::BLOB_USAGE_REQUIREMENTS:
case Tag::BOOTLOADER_ONLY:
case Tag::ROLLBACK_RESISTANCE:
case Tag::EARLY_BOOT_ONLY:
@@ -134,6 +137,92 @@
}
}
+// Size of prefix for blobs, see keyBlobPrefix().
+//
+const size_t kKeyBlobPrefixSize = 8;
+
+// Magic used in blob prefix, see keyBlobPrefix().
+//
+const uint8_t kKeyBlobMagic[7] = {'p', 'K', 'M', 'b', 'l', 'o', 'b'};
+
+// Prefixes a keyblob returned by e.g. generateKey() with information on whether it
+// originated from the real underlying KeyMaster HAL or from soft-KeyMint.
+//
+// When dealing with a keyblob, use prefixedKeyBlobRemovePrefix() to remove the
+// prefix and prefixedKeyBlobIsSoftKeyMint() to determine its origin.
+//
+// Note how the prefix itself has a magic marker ("pKMblob") which can be used
+// to identify if a blob has a prefix at all (it's assumed that any valid blob
+// from KeyMint or KeyMaster HALs never starts with the magic). This is needed
+// because blobs persisted to disk prior to using this code will not have the
+// prefix and in that case we want prefixedKeyBlobRemovePrefix() to still work.
+//
+std::vector<uint8_t> keyBlobPrefix(const std::vector<uint8_t>& blob, bool isSoftKeyMint) {
+ std::vector<uint8_t> result;
+ result.reserve(blob.size() + kKeyBlobPrefixSize);
+ result.insert(result.begin(), kKeyBlobMagic, kKeyBlobMagic + sizeof kKeyBlobMagic);
+ result.push_back(isSoftKeyMint ? 1 : 0);
+ std::copy(blob.begin(), blob.end(), std::back_inserter(result));
+ return result;
+}
+
+// Helper for prefixedKeyBlobRemovePrefix() and prefixedKeyBlobIsSoftKeyMint().
+//
+// First bool is whether there's a valid prefix. If there is, the second bool is
+// the |isSoftKeyMint| value of the prefix
+//
+std::pair<bool, bool> prefixedKeyBlobParsePrefix(const std::vector<uint8_t>& prefixedBlob) {
+ // Having a unprefixed blob is not that uncommon, for example all devices
+ // upgrading to keystore2 (so e.g. upgrading to Android 12) will have
+ // unprefixed blobs. So don't spew warnings/errors in this case...
+ if (prefixedBlob.size() < kKeyBlobPrefixSize) {
+ return std::make_pair(false, false);
+ }
+ if (std::memcmp(prefixedBlob.data(), kKeyBlobMagic, sizeof kKeyBlobMagic) != 0) {
+ return std::make_pair(false, false);
+ }
+ if (prefixedBlob[kKeyBlobPrefixSize - 1] != 0 && prefixedBlob[kKeyBlobPrefixSize - 1] != 1) {
+ return std::make_pair(false, false);
+ }
+ bool isSoftKeyMint = (prefixedBlob[kKeyBlobPrefixSize - 1] == 1);
+ return std::make_pair(true, isSoftKeyMint);
+}
+
+// Removes the prefix from a blob. If there's no prefix, returns the passed-in blob.
+//
+std::vector<uint8_t> prefixedKeyBlobRemovePrefix(const std::vector<uint8_t>& prefixedBlob) {
+ auto parsed = prefixedKeyBlobParsePrefix(prefixedBlob);
+ if (!parsed.first) {
+ // Not actually prefixed, blob was probably persisted to disk prior to the
+ // prefixing code being introduced.
+ return prefixedBlob;
+ }
+ return std::vector<uint8_t>(prefixedBlob.begin() + kKeyBlobPrefixSize, prefixedBlob.end());
+}
+
+// Returns true if the blob's origin is soft-KeyMint, false otherwise or if there
+// is no prefix on the passed-in blob.
+//
+bool prefixedKeyBlobIsSoftKeyMint(const std::vector<uint8_t>& prefixedBlob) {
+ auto parsed = prefixedKeyBlobParsePrefix(prefixedBlob);
+ return parsed.second;
+}
+
+// Inspects the given blob for prefixes.
+// Returns the blob stripped of the prefix if present. The boolean argument is true if the blob was
+// a software blob.
+std::pair<std::vector<uint8_t>, bool>
+dissectPrefixedKeyBlob(const std::vector<uint8_t>& prefixedBlob) {
+ auto [hasPrefix, isSoftware] = prefixedKeyBlobParsePrefix(prefixedBlob);
+ if (!hasPrefix) {
+ // Not actually prefixed, blob was probably persisted to disk prior to the
+ // prefixing code being introduced.
+ return {prefixedBlob, false};
+ }
+ return {std::vector<uint8_t>(prefixedBlob.begin() + kKeyBlobPrefixSize, prefixedBlob.end()),
+ isSoftware};
+}
+
/*
* Returns true if the parameter is not understood by KM 4.1 and older but can be enforced by
* Keystore. These parameters need to be included in the returned KeyCharacteristics, but will not
@@ -141,6 +230,8 @@
*/
bool isNewAndKeystoreEnforceable(const KMV1::KeyParameter& param) {
switch (param.tag) {
+ case KMV1::Tag::MAX_BOOT_LEVEL:
+ return true;
case KMV1::Tag::USAGE_COUNT_LIMIT:
return true;
default:
@@ -213,54 +304,67 @@
static std::vector<KeyCharacteristics>
processLegacyCharacteristics(KeyMintSecurityLevel securityLevel,
const std::vector<KeyParameter>& genParams,
- const V4_0_KeyCharacteristics& legacyKc) {
+ const V4_0_KeyCharacteristics& legacyKc, bool kmEnforcedOnly = false) {
- KeyCharacteristics keystoreEnforced{KeyMintSecurityLevel::KEYSTORE,
- convertKeyParametersFromLegacy(legacyKc.softwareEnforced)};
+ KeyCharacteristics kmEnforced{securityLevel, convertKeyParametersFromLegacy(
+ securityLevel == KeyMintSecurityLevel::SOFTWARE
+ ? legacyKc.softwareEnforced
+ : legacyKc.hardwareEnforced)};
+
+ if (securityLevel == KeyMintSecurityLevel::SOFTWARE && legacyKc.hardwareEnforced.size() > 0) {
+ LOG(WARNING) << "Unexpected hardware enforced parameters.";
+ }
+
+ if (kmEnforcedOnly) {
+ return {kmEnforced};
+ }
+
+ KeyCharacteristics keystoreEnforced{KeyMintSecurityLevel::KEYSTORE, {}};
+
+ if (securityLevel != KeyMintSecurityLevel::SOFTWARE) {
+ // Don't include these tags on software backends, else they'd end up duplicated
+ // across both the keystore-enforced and software keymaster-enforced tags.
+ keystoreEnforced.authorizations = convertKeyParametersFromLegacy(legacyKc.softwareEnforced);
+ }
// Add all parameters that we know can be enforced by keystore but not by the legacy backend.
auto unsupported_requested = extractNewAndKeystoreEnforceableParams(genParams);
- std::copy(unsupported_requested.begin(), unsupported_requested.end(),
- std::back_insert_iterator(keystoreEnforced.authorizations));
+ keystoreEnforced.authorizations.insert(keystoreEnforced.authorizations.end(),
+ std::begin(unsupported_requested),
+ std::end(unsupported_requested));
- if (securityLevel == KeyMintSecurityLevel::SOFTWARE) {
- // If the security level of the backend is `software` we expect the hardware enforced list
- // to be empty. Log a warning otherwise.
- if (legacyKc.hardwareEnforced.size() != 0) {
- LOG(WARNING) << "Unexpected hardware enforced parameters.";
- }
- return {keystoreEnforced};
- }
-
- KeyCharacteristics hwEnforced{securityLevel,
- convertKeyParametersFromLegacy(legacyKc.hardwareEnforced)};
- return {hwEnforced, keystoreEnforced};
+ return {kmEnforced, keystoreEnforced};
}
static V4_0_KeyFormat convertKeyFormatToLegacy(const KeyFormat& kf) {
return static_cast<V4_0_KeyFormat>(kf);
}
-static V4_0_HardwareAuthToken convertAuthTokenToLegacy(const HardwareAuthToken& at) {
+static V4_0_HardwareAuthToken convertAuthTokenToLegacy(const std::optional<HardwareAuthToken>& at) {
+ if (!at) return {};
+
V4_0_HardwareAuthToken legacyAt;
- legacyAt.challenge = at.challenge;
- legacyAt.userId = at.userId;
- legacyAt.authenticatorId = at.authenticatorId;
+ legacyAt.challenge = at->challenge;
+ legacyAt.userId = at->userId;
+ legacyAt.authenticatorId = at->authenticatorId;
legacyAt.authenticatorType =
static_cast<::android::hardware::keymaster::V4_0::HardwareAuthenticatorType>(
- at.authenticatorType);
- legacyAt.timestamp = at.timestamp.milliSeconds;
- legacyAt.mac = at.mac;
+ at->authenticatorType);
+ legacyAt.timestamp = at->timestamp.milliSeconds;
+ legacyAt.mac = at->mac;
return legacyAt;
}
-static V4_0_VerificationToken convertTimestampTokenToLegacy(const TimeStampToken& tst) {
+static V4_0_VerificationToken
+convertTimestampTokenToLegacy(const std::optional<TimeStampToken>& tst) {
+ if (!tst) return {};
+
V4_0_VerificationToken legacyVt;
- legacyVt.challenge = tst.challenge;
- legacyVt.timestamp = tst.timestamp.milliSeconds;
+ legacyVt.challenge = tst->challenge;
+ legacyVt.timestamp = tst->timestamp.milliSeconds;
// Legacy verification tokens were always minted by TEE.
legacyVt.securityLevel = V4_0::SecurityLevel::TRUSTED_ENVIRONMENT;
- legacyVt.mac = tst.mac;
+ legacyVt.mac = tst->mac;
return legacyVt;
}
@@ -309,20 +413,13 @@
// KeyMintDevice implementation
ScopedAStatus KeyMintDevice::getHardwareInfo(KeyMintHardwareInfo* _aidl_return) {
- // TODO: What do I do about the version number? Is it the version of the device I get?
- auto result = mDevice->getHardwareInfo([&](auto securityLevel, const auto& keymasterName,
- const auto& keymasterAuthorName) {
- securityLevel_ =
- static_cast<::aidl::android::hardware::security::keymint::SecurityLevel>(securityLevel);
-
- _aidl_return->securityLevel = securityLevel_;
- _aidl_return->keyMintName = keymasterName;
- _aidl_return->keyMintAuthorName = keymasterAuthorName;
- });
- if (!result.isOk()) {
- LOG(ERROR) << __func__ << " transaction failed. " << result.description();
- return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
- }
+ auto result = mDevice->halVersion();
+ _aidl_return->versionNumber = result.majorVersion * 10 + result.minorVersion;
+ securityLevel_ = convert(result.securityLevel);
+ _aidl_return->securityLevel = securityLevel_;
+ _aidl_return->keyMintName = result.keymasterName;
+ _aidl_return->keyMintAuthorName = result.authorName;
+ _aidl_return->timestampTokenRequired = securityLevel_ == KMV1::SecurityLevel::STRONGBOX;
return ScopedAStatus::ok();
}
@@ -335,17 +432,39 @@
return convertErrorCode(result);
}
-ScopedAStatus
-KeyMintDevice::generateKey(const std::vector<KeyParameter>& inKeyParams,
- const std::optional<AttestationKey>& /* in_attestationKey */,
- KeyCreationResult* out_creationResult) {
+ScopedAStatus KeyMintDevice::generateKey(const std::vector<KeyParameter>& inKeyParams,
+ const std::optional<AttestationKey>& in_attestationKey,
+ KeyCreationResult* out_creationResult) {
+
+ // Since KeyMaster doesn't support ECDH, route all key creation requests to
+ // soft-KeyMint if and only an ECDH key is requested.
+ //
+ // For this to work we'll need to also route begin() and deleteKey() calls to
+ // soft-KM. In order to do that, we'll prefix all keyblobs with whether it was
+ // created by the real underlying KeyMaster HAL or whether it was created by
+ // soft-KeyMint.
+ //
+ // See keyBlobPrefix() for more discussion.
+ //
+ for (const auto& keyParam : inKeyParams) {
+ if (keyParam.tag == Tag::PURPOSE &&
+ keyParam.value.get<KeyParameterValue::Tag::keyPurpose>() == KeyPurpose::AGREE_KEY) {
+ auto ret =
+ softKeyMintDevice_->generateKey(inKeyParams, in_attestationKey, out_creationResult);
+ if (ret.isOk()) {
+ out_creationResult->keyBlob = keyBlobPrefix(out_creationResult->keyBlob, true);
+ }
+ return ret;
+ }
+ }
+
auto legacyKeyGenParams = convertKeyParametersToLegacy(extractGenerationParams(inKeyParams));
KMV1::ErrorCode errorCode;
auto result = mDevice->generateKey(
legacyKeyGenParams, [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
const V4_0_KeyCharacteristics& keyCharacteristics) {
errorCode = convert(error);
- out_creationResult->keyBlob = keyBlob;
+ out_creationResult->keyBlob = keyBlobPrefix(keyBlob, false);
out_creationResult->keyCharacteristics =
processLegacyCharacteristics(securityLevel_, inKeyParams, keyCharacteristics);
});
@@ -381,7 +500,8 @@
[&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
const V4_0_KeyCharacteristics& keyCharacteristics) {
errorCode = convert(error);
- out_creationResult->keyBlob = keyBlob;
+ out_creationResult->keyBlob =
+ keyBlobPrefix(keyBlob, false);
out_creationResult->keyCharacteristics =
processLegacyCharacteristics(
securityLevel_, inKeyParams, keyCharacteristics);
@@ -408,20 +528,28 @@
ScopedAStatus
KeyMintDevice::importWrappedKey(const std::vector<uint8_t>& in_inWrappedKeyData,
- const std::vector<uint8_t>& in_inWrappingKeyBlob, //
+ const std::vector<uint8_t>& in_inPrefixedWrappingKeyBlob,
const std::vector<uint8_t>& in_inMaskingKey,
const std::vector<KeyParameter>& in_inUnwrappingParams,
int64_t in_inPasswordSid, int64_t in_inBiometricSid,
KeyCreationResult* out_creationResult) {
+ const std::vector<uint8_t>& wrappingKeyBlob =
+ prefixedKeyBlobRemovePrefix(in_inPrefixedWrappingKeyBlob);
+ if (prefixedKeyBlobIsSoftKeyMint(in_inPrefixedWrappingKeyBlob)) {
+ return softKeyMintDevice_->importWrappedKey(
+ in_inWrappedKeyData, wrappingKeyBlob, in_inMaskingKey, in_inUnwrappingParams,
+ in_inPasswordSid, in_inBiometricSid, out_creationResult);
+ }
+
auto legacyUnwrappingParams = convertKeyParametersToLegacy(in_inUnwrappingParams);
KMV1::ErrorCode errorCode;
auto result = mDevice->importWrappedKey(
- in_inWrappedKeyData, in_inWrappingKeyBlob, in_inMaskingKey, legacyUnwrappingParams,
+ in_inWrappedKeyData, wrappingKeyBlob, in_inMaskingKey, legacyUnwrappingParams,
in_inPasswordSid, in_inBiometricSid,
[&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
const V4_0_KeyCharacteristics& keyCharacteristics) {
errorCode = convert(error);
- out_creationResult->keyBlob = keyBlob;
+ out_creationResult->keyBlob = keyBlobPrefix(keyBlob, false);
out_creationResult->keyCharacteristics =
processLegacyCharacteristics(securityLevel_, {}, keyCharacteristics);
});
@@ -437,11 +565,12 @@
std::vector<uint8_t>* _aidl_return) {
auto legacyUpgradeParams = convertKeyParametersToLegacy(in_inUpgradeParams);
V4_0_ErrorCode errorCode;
+
auto result =
- mDevice->upgradeKey(in_inKeyBlobToUpgrade, legacyUpgradeParams,
+ mDevice->upgradeKey(prefixedKeyBlobRemovePrefix(in_inKeyBlobToUpgrade), legacyUpgradeParams,
[&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& upgradedKeyBlob) {
errorCode = error;
- *_aidl_return = upgradedKeyBlob;
+ *_aidl_return = keyBlobPrefix(upgradedKeyBlob, false);
});
if (!result.isOk()) {
LOG(ERROR) << __func__ << " transaction failed. " << result.description();
@@ -450,8 +579,13 @@
return convertErrorCode(errorCode);
}
-ScopedAStatus KeyMintDevice::deleteKey(const std::vector<uint8_t>& in_inKeyBlob) {
- auto result = mDevice->deleteKey(in_inKeyBlob);
+ScopedAStatus KeyMintDevice::deleteKey(const std::vector<uint8_t>& prefixedKeyBlob) {
+ const std::vector<uint8_t>& keyBlob = prefixedKeyBlobRemovePrefix(prefixedKeyBlob);
+ if (prefixedKeyBlobIsSoftKeyMint(prefixedKeyBlob)) {
+ return softKeyMintDevice_->deleteKey(keyBlob);
+ }
+
+ auto result = mDevice->deleteKey(keyBlob);
if (!result.isOk()) {
LOG(ERROR) << __func__ << " transaction failed. " << result.description();
return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
@@ -475,13 +609,20 @@
}
ScopedAStatus KeyMintDevice::begin(KeyPurpose in_inPurpose,
- const std::vector<uint8_t>& in_inKeyBlob,
+ const std::vector<uint8_t>& prefixedKeyBlob,
const std::vector<KeyParameter>& in_inParams,
- const HardwareAuthToken& in_inAuthToken,
+ const std::optional<HardwareAuthToken>& in_inAuthToken,
BeginResult* _aidl_return) {
if (!mOperationSlots.claimSlot()) {
return convertErrorCode(V4_0_ErrorCode::TOO_MANY_OPERATIONS);
}
+
+ const std::vector<uint8_t>& in_inKeyBlob = prefixedKeyBlobRemovePrefix(prefixedKeyBlob);
+ if (prefixedKeyBlobIsSoftKeyMint(prefixedKeyBlob)) {
+ return softKeyMintDevice_->begin(in_inPurpose, in_inKeyBlob, in_inParams, in_inAuthToken,
+ _aidl_return);
+ }
+
auto legacyPurpose =
static_cast<::android::hardware::keymaster::V4_0::KeyPurpose>(in_inPurpose);
auto legacyParams = convertKeyParametersToLegacy(in_inParams);
@@ -530,81 +671,155 @@
}
}
-ScopedAStatus KeyMintOperation::update(const std::optional<KeyParameterArray>& in_inParams,
- const std::optional<std::vector<uint8_t>>& in_input,
- const std::optional<HardwareAuthToken>& in_inAuthToken,
- const std::optional<TimeStampToken>& in_inTimeStampToken,
- std::optional<KeyParameterArray>* out_outParams,
- std::optional<ByteArray>* out_output,
- int32_t* _aidl_return) {
- std::vector<V4_0_KeyParameter> legacyParams;
- if (in_inParams.has_value()) {
- legacyParams = convertKeyParametersToLegacy(in_inParams.value().params);
+ScopedAStatus
+KeyMintDevice::convertStorageKeyToEphemeral(const std::vector<uint8_t>& prefixedStorageKeyBlob,
+ std::vector<uint8_t>* ephemeralKeyBlob) {
+ KMV1::ErrorCode km_error;
+
+ /*
+ * Wrapped storage keys cannot be emulated (and they don't need to, because if a platform
+ * supports wrapped storage keys, then the legacy backend will support it too. So error out
+ * if the wrapped storage key given is a soft keymint key.
+ */
+ if (prefixedKeyBlobIsSoftKeyMint(prefixedStorageKeyBlob)) {
+ return convertErrorCode(KMV1::ErrorCode::UNIMPLEMENTED);
}
- auto input = in_input.value_or(std::vector<uint8_t>());
- V4_0_HardwareAuthToken authToken;
- if (in_inAuthToken.has_value()) {
- authToken = convertAuthTokenToLegacy(in_inAuthToken.value());
+
+ const std::vector<uint8_t>& storageKeyBlob =
+ prefixedKeyBlobRemovePrefix(prefixedStorageKeyBlob);
+
+ auto hidlCb = [&](V4_0_ErrorCode ret, const hidl_vec<uint8_t>& exportedKeyBlob) {
+ km_error = convert(ret);
+ if (km_error != KMV1::ErrorCode::OK) return;
+ /*
+ * This must return the blob without the prefix since it will be used directly
+ * as a storage encryption key. But this is alright, since this wrapped ephemeral
+ * key shouldn't/won't ever be used with keymint.
+ */
+ *ephemeralKeyBlob = exportedKeyBlob;
+ };
+
+ auto ret = mDevice->exportKey(V4_0_KeyFormat::RAW, storageKeyBlob, {}, {}, hidlCb);
+ if (!ret.isOk()) {
+ LOG(ERROR) << __func__ << " export_key failed: " << ret.description();
+ return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
}
- V4_0_VerificationToken verificationToken;
- if (in_inTimeStampToken.has_value()) {
- verificationToken = convertTimestampTokenToLegacy(in_inTimeStampToken.value());
+ if (km_error != KMV1::ErrorCode::OK)
+ LOG(ERROR) << __func__ << " export_key failed, code " << int32_t(km_error);
+
+ return convertErrorCode(km_error);
+}
+
+ScopedAStatus KeyMintDevice::getKeyCharacteristics(
+ const std::vector<uint8_t>& prefixedKeyBlob, const std::vector<uint8_t>& appId,
+ const std::vector<uint8_t>& appData, std::vector<KeyCharacteristics>* keyCharacteristics) {
+ auto [strippedKeyBlob, isSoftware] = dissectPrefixedKeyBlob(prefixedKeyBlob);
+ if (isSoftware) {
+ return softKeyMintDevice_->getKeyCharacteristics(strippedKeyBlob, appId, appData,
+ keyCharacteristics);
+ } else {
+ KMV1::ErrorCode km_error;
+ auto ret = mDevice->getKeyCharacteristics(
+ strippedKeyBlob, appId, appData,
+ [&](V4_0_ErrorCode errorCode, const V4_0_KeyCharacteristics& v40KeyCharacteristics) {
+ km_error = convert(errorCode);
+ *keyCharacteristics =
+ processLegacyCharacteristics(securityLevel_, {} /* getParams */,
+ v40KeyCharacteristics, true /* kmEnforcedOnly */);
+ });
+
+ if (!ret.isOk()) {
+ LOG(ERROR) << __func__ << " getKeyCharacteristics failed: " << ret.description();
+ return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
+ }
+ if (km_error != KMV1::ErrorCode::OK) {
+ LOG(ERROR) << __func__
+ << " getKeyCharacteristics failed with code: " << toString(km_error);
+ }
+
+ return convertErrorCode(km_error);
}
+}
+
+ScopedAStatus KeyMintOperation::updateAad(const std::vector<uint8_t>& input,
+ const std::optional<HardwareAuthToken>& optAuthToken,
+ const std::optional<TimeStampToken>& optTimeStampToken) {
+ V4_0_HardwareAuthToken authToken = convertAuthTokenToLegacy(optAuthToken);
+ V4_0_VerificationToken verificationToken = convertTimestampTokenToLegacy(optTimeStampToken);
KMV1::ErrorCode errorCode;
auto result = mDevice->update(
- mOperationHandle, legacyParams, input, authToken, verificationToken,
- [&](V4_0_ErrorCode error, uint32_t inputConsumed,
- const hidl_vec<V4_0_KeyParameter>& outParams, const hidl_vec<uint8_t>& output) {
- errorCode = convert(error);
- out_outParams->emplace();
- out_outParams->value().params = convertKeyParametersFromLegacy(outParams);
- out_output->emplace();
- out_output->value().data = output;
- *_aidl_return = inputConsumed;
- });
+ mOperationHandle, {V4_0::makeKeyParameter(V4_0::TAG_ASSOCIATED_DATA, input)}, {}, authToken,
+ verificationToken,
+ [&](V4_0_ErrorCode error, auto, auto, auto) { errorCode = convert(error); });
if (!result.isOk()) {
LOG(ERROR) << __func__ << " transaction failed. " << result.description();
errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
}
- if (errorCode != KMV1::ErrorCode::OK) {
- mOperationSlot.freeSlot();
- }
+ if (errorCode != KMV1::ErrorCode::OK) mOperationSlot.freeSlot();
+
return convertErrorCode(errorCode);
}
-ScopedAStatus KeyMintOperation::finish(const std::optional<KeyParameterArray>& in_inParams,
- const std::optional<std::vector<uint8_t>>& in_input,
- const std::optional<std::vector<uint8_t>>& in_inSignature,
- const std::optional<HardwareAuthToken>& in_authToken,
- const std::optional<TimeStampToken>& in_inTimeStampToken,
- std::optional<KeyParameterArray>* out_outParams,
- std::vector<uint8_t>* _aidl_return) {
- KMV1::ErrorCode errorCode;
- std::vector<V4_0_KeyParameter> legacyParams;
- if (in_inParams.has_value()) {
- legacyParams = convertKeyParametersToLegacy(in_inParams.value().params);
+ScopedAStatus KeyMintOperation::update(const std::vector<uint8_t>& input,
+ const std::optional<HardwareAuthToken>& optAuthToken,
+ const std::optional<TimeStampToken>& optTimeStampToken,
+ std::vector<uint8_t>* out_output) {
+ V4_0_HardwareAuthToken authToken = convertAuthTokenToLegacy(optAuthToken);
+ V4_0_VerificationToken verificationToken = convertTimestampTokenToLegacy(optTimeStampToken);
+
+ size_t inputPos = 0;
+ *out_output = {};
+ KMV1::ErrorCode errorCode = KMV1::ErrorCode::OK;
+
+ while (inputPos < input.size() && errorCode == KMV1::ErrorCode::OK) {
+ auto result =
+ mDevice->update(mOperationHandle, {} /* inParams */,
+ {input.begin() + inputPos, input.end()}, authToken, verificationToken,
+ [&](V4_0_ErrorCode error, uint32_t inputConsumed, auto /* outParams */,
+ const hidl_vec<uint8_t>& output) {
+ errorCode = convert(error);
+ out_output->insert(out_output->end(), output.begin(), output.end());
+ inputPos += inputConsumed;
+ });
+
+ if (!result.isOk()) {
+ LOG(ERROR) << __func__ << " transaction failed. " << result.description();
+ errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
+ }
}
+
+ if (errorCode != KMV1::ErrorCode::OK) mOperationSlot.freeSlot();
+
+ return convertErrorCode(errorCode);
+}
+
+ScopedAStatus
+KeyMintOperation::finish(const std::optional<std::vector<uint8_t>>& in_input,
+ const std::optional<std::vector<uint8_t>>& in_signature,
+ const std::optional<HardwareAuthToken>& in_authToken,
+ const std::optional<TimeStampToken>& in_timeStampToken,
+ const std::optional<std::vector<uint8_t>>& in_confirmationToken,
+ std::vector<uint8_t>* out_output) {
auto input = in_input.value_or(std::vector<uint8_t>());
- auto signature = in_inSignature.value_or(std::vector<uint8_t>());
- V4_0_HardwareAuthToken authToken;
- if (in_authToken.has_value()) {
- authToken = convertAuthTokenToLegacy(in_authToken.value());
+ auto signature = in_signature.value_or(std::vector<uint8_t>());
+ V4_0_HardwareAuthToken authToken = convertAuthTokenToLegacy(in_authToken);
+ V4_0_VerificationToken verificationToken = convertTimestampTokenToLegacy(in_timeStampToken);
+
+ std::vector<V4_0_KeyParameter> inParams;
+ if (in_confirmationToken) {
+ inParams.push_back(makeKeyParameter(V4_0::TAG_CONFIRMATION_TOKEN, *in_confirmationToken));
}
- V4_0_VerificationToken verificationToken;
- if (in_inTimeStampToken.has_value()) {
- verificationToken = convertTimestampTokenToLegacy(in_inTimeStampToken.value());
- }
+
+ KMV1::ErrorCode errorCode;
auto result = mDevice->finish(
- mOperationHandle, legacyParams, input, signature, authToken, verificationToken,
- [&](V4_0_ErrorCode error, const hidl_vec<V4_0_KeyParameter>& outParams,
- const hidl_vec<uint8_t>& output) {
+ mOperationHandle, inParams, input, signature, authToken, verificationToken,
+ [&](V4_0_ErrorCode error, auto /* outParams */, const hidl_vec<uint8_t>& output) {
errorCode = convert(error);
- out_outParams->emplace();
- out_outParams->value().params = convertKeyParametersFromLegacy(outParams);
- *_aidl_return = output;
+ *out_output = output;
});
+
mOperationSlot.freeSlot();
if (!result.isOk()) {
LOG(ERROR) << __func__ << " transaction failed. " << result.description();
@@ -841,7 +1056,8 @@
std::optional<KMV1::ErrorCode>
KeyMintDevice::signCertificate(const std::vector<KeyParameter>& keyParams,
- const std::vector<uint8_t>& keyBlob, X509* cert) {
+ const std::vector<uint8_t>& prefixedKeyBlob, X509* cert) {
+
auto algorithm = getParam(keyParams, KMV1::TAG_ALGORITHM);
auto algoOrError = getKeystoreAlgorithm(*algorithm);
if (std::holds_alternative<KMV1::ErrorCode>(algoOrError)) {
@@ -876,23 +1092,20 @@
kps.push_back(KMV1::makeKeyParameter(KMV1::TAG_PADDING, origPadding));
}
BeginResult beginResult;
- auto error = begin(KeyPurpose::SIGN, keyBlob, kps, HardwareAuthToken(), &beginResult);
+ auto error =
+ begin(KeyPurpose::SIGN, prefixedKeyBlob, kps, HardwareAuthToken(), &beginResult);
if (!error.isOk()) {
errorCode = toErrorCode(error);
return std::vector<uint8_t>();
}
- std::optional<KeyParameterArray> outParams;
- std::optional<ByteArray> outByte;
- int32_t status;
- error = beginResult.operation->update(std::nullopt, dataVec, std::nullopt, std::nullopt,
- &outParams, &outByte, &status);
- if (!error.isOk()) {
- errorCode = toErrorCode(error);
- return std::vector<uint8_t>();
- }
+
std::vector<uint8_t> result;
- error = beginResult.operation->finish(std::nullopt, std::nullopt, std::nullopt,
- std::nullopt, std::nullopt, &outParams, &result);
+ error = beginResult.operation->finish(dataVec, //
+ {} /* signature */, //
+ {} /* authToken */, //
+ {} /* timestampToken */, //
+ {} /* confirmationToken */, //
+ &result);
if (!error.isOk()) {
errorCode = toErrorCode(error);
return std::vector<uint8_t>();
@@ -913,7 +1126,9 @@
std::variant<std::vector<Certificate>, KMV1::ErrorCode>
KeyMintDevice::getCertificate(const std::vector<KeyParameter>& keyParams,
- const std::vector<uint8_t>& keyBlob) {
+ const std::vector<uint8_t>& prefixedKeyBlob) {
+ const std::vector<uint8_t>& keyBlob = prefixedKeyBlobRemovePrefix(prefixedKeyBlob);
+
// There are no certificates for symmetric keys.
auto algorithm = getParam(keyParams, KMV1::TAG_ALGORITHM);
if (!algorithm) {
@@ -1011,14 +1226,14 @@
// Copied from system/security/keystore/include/keystore/keymaster_types.h.
// Changing this namespace alias will change the keymaster version.
-namespace keymaster = ::android::hardware::keymaster::V4_1;
+namespace keymasterNs = ::android::hardware::keymaster::V4_1;
-using keymaster::SecurityLevel;
+using keymasterNs::SecurityLevel;
// Copied from system/security/keystore/KeyStore.h.
using ::android::sp;
-using keymaster::support::Keymaster;
+using keymasterNs::support::Keymaster;
template <typename T, size_t count> class Devices : public std::array<T, count> {
public:
@@ -1043,8 +1258,8 @@
// Copied from system/security/keystore/keystore_main.cpp.
using ::android::hardware::hidl_string;
-using keymaster::support::Keymaster3;
-using keymaster::support::Keymaster4;
+using keymasterNs::support::Keymaster3;
+using keymasterNs::support::Keymaster4;
template <typename Wrapper>
KeymasterDevices enumerateKeymasterDevices(IServiceManager* serviceManager) {
@@ -1099,7 +1314,7 @@
CHECK(serviceManager.get()) << "Failed to get ServiceManager";
auto result = enumerateKeymasterDevices<Keymaster4>(serviceManager.get());
auto softKeymaster = result[SecurityLevel::SOFTWARE];
- if (!result[SecurityLevel::TRUSTED_ENVIRONMENT]) {
+ if ((!result[SecurityLevel::TRUSTED_ENVIRONMENT]) && (!result[SecurityLevel::STRONGBOX])) {
result = enumerateKeymasterDevices<Keymaster3>(serviceManager.get());
}
if (softKeymaster) result[SecurityLevel::SOFTWARE] = softKeymaster;
@@ -1126,6 +1341,8 @@
} else {
setNumFreeSlots(15);
}
+
+ softKeyMintDevice_.reset(CreateKeyMintDevice(KeyMintSecurityLevel::SOFTWARE));
}
sp<Keymaster> getDevice(KeyMintSecurityLevel securityLevel) {
@@ -1181,8 +1398,7 @@
if (!device) {
return ScopedAStatus::fromStatus(STATUS_NAME_NOT_FOUND);
}
- bool inserted = false;
- std::tie(i, inserted) = mDeviceCache.insert({in_securityLevel, std::move(device)});
+ i = mDeviceCache.insert(i, {in_securityLevel, std::move(device)});
}
*_aidl_return = i->second;
return ScopedAStatus::ok();
@@ -1190,19 +1406,20 @@
ScopedAStatus KeystoreCompatService::getSharedSecret(KeyMintSecurityLevel in_securityLevel,
std::shared_ptr<ISharedSecret>* _aidl_return) {
- if (!mSharedSecret) {
+ auto i = mSharedSecretCache.find(in_securityLevel);
+ if (i == mSharedSecretCache.end()) {
auto secret = SharedSecret::createSharedSecret(in_securityLevel);
if (!secret) {
return ScopedAStatus::fromStatus(STATUS_NAME_NOT_FOUND);
}
- mSharedSecret = std::move(secret);
+ i = mSharedSecretCache.insert(i, {in_securityLevel, std::move(secret)});
}
- *_aidl_return = mSharedSecret;
+ *_aidl_return = i->second;
return ScopedAStatus::ok();
}
ScopedAStatus KeystoreCompatService::getSecureClock(std::shared_ptr<ISecureClock>* _aidl_return) {
- if (!mSharedSecret) {
+ if (!mSecureClock) {
// The legacy verification service was always provided by the TEE variant.
auto clock = SecureClock::createSecureClock(KeyMintSecurityLevel::TRUSTED_ENVIRONMENT);
if (!clock) {
diff --git a/keystore2/src/km_compat/km_compat.h b/keystore2/src/km_compat/km_compat.h
index 57a7bbd..2d892da 100644
--- a/keystore2/src/km_compat/km_compat.h
+++ b/keystore2/src/km_compat/km_compat.h
@@ -30,7 +30,6 @@
using ::aidl::android::hardware::security::keymint::AttestationKey;
using ::aidl::android::hardware::security::keymint::BeginResult;
-using ::aidl::android::hardware::security::keymint::ByteArray;
using ::aidl::android::hardware::security::keymint::Certificate;
using ::aidl::android::hardware::security::keymint::HardwareAuthToken;
using ::aidl::android::hardware::security::keymint::KeyCharacteristics;
@@ -38,7 +37,6 @@
using ::aidl::android::hardware::security::keymint::KeyFormat;
using ::aidl::android::hardware::security::keymint::KeyMintHardwareInfo;
using ::aidl::android::hardware::security::keymint::KeyParameter;
-using ::aidl::android::hardware::security::keymint::KeyParameterArray;
using ::aidl::android::hardware::security::keymint::KeyPurpose;
using KeyMintSecurityLevel = ::aidl::android::hardware::security::keymint::SecurityLevel;
using V4_0_ErrorCode = ::android::hardware::keymaster::V4_0::ErrorCode;
@@ -111,14 +109,22 @@
ScopedAStatus destroyAttestationIds() override;
ScopedAStatus begin(KeyPurpose in_inPurpose, const std::vector<uint8_t>& in_inKeyBlob,
const std::vector<KeyParameter>& in_inParams,
- const HardwareAuthToken& in_inAuthToken,
+ const std::optional<HardwareAuthToken>& in_inAuthToken,
BeginResult* _aidl_return) override;
ScopedAStatus deviceLocked(bool passwordOnly,
const std::optional<TimeStampToken>& timestampToken) override;
ScopedAStatus earlyBootEnded() override;
+
+ ScopedAStatus convertStorageKeyToEphemeral(const std::vector<uint8_t>& storageKeyBlob,
+ std::vector<uint8_t>* ephemeralKeyBlob) override;
+
+ ScopedAStatus
+ getKeyCharacteristics(const std::vector<uint8_t>& storageKeyBlob,
+ const std::vector<uint8_t>& appId, const std::vector<uint8_t>& appData,
+ std::vector<KeyCharacteristics>* keyCharacteristics) override;
+
// These are public to allow testing code to use them directly.
// This class should not be used publicly anyway.
-
std::variant<std::vector<Certificate>, KMV1_ErrorCode>
getCertificate(const std::vector<KeyParameter>& keyParams, const std::vector<uint8_t>& keyBlob);
@@ -128,6 +134,9 @@
std::optional<KMV1_ErrorCode> signCertificate(const std::vector<KeyParameter>& keyParams,
const std::vector<uint8_t>& keyBlob, X509* cert);
KeyMintSecurityLevel securityLevel_;
+
+ // Software-based KeyMint device used to implement ECDH.
+ std::shared_ptr<IKeyMintDevice> softKeyMintDevice_;
};
class KeyMintOperation : public aidl::android::hardware::security::keymint::BnKeyMintOperation {
@@ -142,19 +151,22 @@
: mDevice(device), mOperationHandle(operationHandle), mOperationSlot(slots, isActive) {}
~KeyMintOperation();
- ScopedAStatus update(const std::optional<KeyParameterArray>& in_inParams,
- const std::optional<std::vector<uint8_t>>& in_input,
- const std::optional<HardwareAuthToken>& in_inAuthToken,
- const std::optional<TimeStampToken>& in_inTimestampToken,
- std::optional<KeyParameterArray>* out_outParams,
- std::optional<ByteArray>* out_output, int32_t* _aidl_return);
- ScopedAStatus finish(const std::optional<KeyParameterArray>& in_inParams,
- const std::optional<std::vector<uint8_t>>& in_input,
- const std::optional<std::vector<uint8_t>>& in_inSignature,
- const std::optional<HardwareAuthToken>& in_authToken,
- const std::optional<TimeStampToken>& in_inTimestampToken,
- std::optional<KeyParameterArray>* out_outParams,
- std::vector<uint8_t>* _aidl_return);
+ ScopedAStatus updateAad(const std::vector<uint8_t>& input,
+ const std::optional<HardwareAuthToken>& authToken,
+ const std::optional<TimeStampToken>& timestampToken) override;
+
+ ScopedAStatus update(const std::vector<uint8_t>& input,
+ const std::optional<HardwareAuthToken>& authToken,
+ const std::optional<TimeStampToken>& timestampToken,
+ std::vector<uint8_t>* output) override;
+
+ ScopedAStatus finish(const std::optional<std::vector<uint8_t>>& input,
+ const std::optional<std::vector<uint8_t>>& signature,
+ const std::optional<HardwareAuthToken>& authToken,
+ const std::optional<TimeStampToken>& timeStampToken,
+ const std::optional<std::vector<uint8_t>>& confirmationToken,
+ std::vector<uint8_t>* output) override;
+
ScopedAStatus abort();
};
@@ -185,7 +197,7 @@
class KeystoreCompatService : public BnKeystoreCompatService {
private:
std::unordered_map<KeyMintSecurityLevel, std::shared_ptr<IKeyMintDevice>> mDeviceCache;
- std::shared_ptr<ISharedSecret> mSharedSecret;
+ std::unordered_map<KeyMintSecurityLevel, std::shared_ptr<ISharedSecret>> mSharedSecretCache;
std::shared_ptr<ISecureClock> mSecureClock;
public:
diff --git a/keystore2/src/km_compat/km_compat_type_conversion.h b/keystore2/src/km_compat/km_compat_type_conversion.h
index b36b78a..de09477 100644
--- a/keystore2/src/km_compat/km_compat_type_conversion.h
+++ b/keystore2/src/km_compat/km_compat_type_conversion.h
@@ -503,9 +503,6 @@
return V4_0::makeKeyParameter(V4_0::TAG_INCLUDE_UNIQUE_ID, v->get());
}
break;
- case KMV1::Tag::BLOB_USAGE_REQUIREMENTS:
- // This tag has been removed. Mapped on invalid.
- break;
case KMV1::Tag::BOOTLOADER_ONLY:
if (auto v = KMV1::authorizationValue(KMV1::TAG_BOOTLOADER_ONLY, kp)) {
return V4_0::makeKeyParameter(V4_0::TAG_BOOTLOADER_ONLY, v->get());
@@ -665,13 +662,19 @@
}
break;
case KMV1::Tag::ATTESTATION_ID_SERIAL:
- // TODO This tag is missing from 4.0 keymaster_tags.h
+ if (auto v = KMV1::authorizationValue(KMV1::TAG_ATTESTATION_ID_SERIAL, kp)) {
+ return V4_0::makeKeyParameter(V4_0::TAG_ATTESTATION_ID_SERIAL, v->get());
+ }
break;
case KMV1::Tag::ATTESTATION_ID_IMEI:
- // TODO This tag is missing from 4.0 keymaster_tags.h
+ if (auto v = KMV1::authorizationValue(KMV1::TAG_ATTESTATION_ID_IMEI, kp)) {
+ return V4_0::makeKeyParameter(V4_0::TAG_ATTESTATION_ID_IMEI, v->get());
+ }
break;
case KMV1::Tag::ATTESTATION_ID_MEID:
- // TODO This tag is missing from 4.0 keymaster_tags.h
+ if (auto v = KMV1::authorizationValue(KMV1::TAG_ATTESTATION_ID_MEID, kp)) {
+ return V4_0::makeKeyParameter(V4_0::TAG_ATTESTATION_ID_MEID, v->get());
+ }
break;
case KMV1::Tag::ATTESTATION_ID_MANUFACTURER:
if (auto v = KMV1::authorizationValue(KMV1::TAG_ATTESTATION_ID_MANUFACTURER, kp)) {
@@ -740,6 +743,9 @@
case KMV1::Tag::CERTIFICATE_NOT_AFTER:
// These tags do not exist in KM < KeyMint 1.0.
break;
+ case KMV1::Tag::MAX_BOOT_LEVEL:
+ // Does not exist in API level 30 or below.
+ break;
}
return V4_0::KeyParameter{.tag = V4_0::Tag::INVALID};
}
@@ -968,13 +974,19 @@
}
break;
case V4_0::Tag::ATTESTATION_ID_SERIAL:
- // TODO This tag is missing from 4.0 keymaster_tags.h
+ if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_ATTESTATION_ID_SERIAL, kp))) {
+ return KMV1::makeKeyParameter(KMV1::TAG_ATTESTATION_ID_SERIAL, v->get());
+ }
break;
case V4_0::Tag::ATTESTATION_ID_IMEI:
- // TODO This tag is missing from 4.0 keymaster_tags.h
+ if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_ATTESTATION_ID_IMEI, kp))) {
+ return KMV1::makeKeyParameter(KMV1::TAG_ATTESTATION_ID_IMEI, v->get());
+ }
break;
case V4_0::Tag::ATTESTATION_ID_MEID:
- // TODO This tag is missing from 4.0 keymaster_tags.h
+ if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_ATTESTATION_ID_MEID, kp))) {
+ return KMV1::makeKeyParameter(KMV1::TAG_ATTESTATION_ID_MEID, v->get());
+ }
break;
case V4_0::Tag::ATTESTATION_ID_MANUFACTURER:
if (auto v =
diff --git a/keystore2/src/km_compat/lib.rs b/keystore2/src/km_compat/lib.rs
index 22ddc31..8d7310b 100644
--- a/keystore2/src/km_compat/lib.rs
+++ b/keystore2/src/km_compat/lib.rs
@@ -12,13 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// TODO: Once this is stable, remove this and document everything public.
-#![allow(missing_docs)]
+//! Export into Rust a function to create a KeyMintDevice and add it as a service.
+#[allow(missing_docs)] // TODO remove this
extern "C" {
fn addKeyMintDeviceService() -> i32;
}
+#[allow(missing_docs)] // TODO remove this
pub fn add_keymint_device_service() -> i32 {
unsafe { addKeyMintDeviceService() }
}
@@ -29,10 +30,10 @@
use super::*;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, BeginResult::BeginResult, BlockMode::BlockMode, Digest::Digest,
- ErrorCode::ErrorCode, HardwareAuthToken::HardwareAuthToken, IKeyMintDevice::IKeyMintDevice,
- KeyCreationResult::KeyCreationResult, KeyFormat::KeyFormat, KeyParameter::KeyParameter,
- KeyParameterArray::KeyParameterArray, KeyParameterValue::KeyParameterValue,
- KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, SecurityLevel::SecurityLevel, Tag::Tag,
+ ErrorCode::ErrorCode, IKeyMintDevice::IKeyMintDevice, KeyCreationResult::KeyCreationResult,
+ KeyFormat::KeyFormat, KeyOrigin::KeyOrigin, KeyParameter::KeyParameter,
+ KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
+ SecurityLevel::SecurityLevel, Tag::Tag,
};
use android_hardware_security_keymint::binder::{self, Strong};
use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
@@ -259,7 +260,7 @@
if let Some(mut extras) = extra_params {
kps.append(&mut extras);
}
- let result = legacy.begin(purpose, &blob, &kps, &HardwareAuthToken::default());
+ let result = legacy.begin(purpose, blob, &kps, None);
assert!(result.is_ok(), "{:?}", result);
result.unwrap()
}
@@ -283,41 +284,53 @@
let begin_result = begin(legacy.as_ref(), &blob, KeyPurpose::ENCRYPT, None);
let operation = begin_result.operation.unwrap();
- let params = KeyParameterArray {
- params: vec![KeyParameter {
- tag: Tag::ASSOCIATED_DATA,
- value: KeyParameterValue::Blob(b"foobar".to_vec()),
- }],
- };
+
+ let update_aad_result = operation.updateAad(
+ &b"foobar".to_vec(),
+ None, /* authToken */
+ None, /* timestampToken */
+ );
+ assert!(update_aad_result.is_ok(), "{:?}", update_aad_result);
+
let message = [42; 128];
- let mut out_params = None;
- let result =
- operation.finish(Some(¶ms), Some(&message), None, None, None, &mut out_params);
+ let result = operation.finish(
+ Some(&message),
+ None, /* signature */
+ None, /* authToken */
+ None, /* timestampToken */
+ None, /* confirmationToken */
+ );
assert!(result.is_ok(), "{:?}", result);
let ciphertext = result.unwrap();
assert!(!ciphertext.is_empty());
- assert!(out_params.is_some());
let begin_result =
begin(legacy.as_ref(), &blob, KeyPurpose::DECRYPT, Some(begin_result.params));
+
let operation = begin_result.operation.unwrap();
- let mut out_params = None;
- let mut output = None;
+
+ let update_aad_result = operation.updateAad(
+ &b"foobar".to_vec(),
+ None, /* authToken */
+ None, /* timestampToken */
+ );
+ assert!(update_aad_result.is_ok(), "{:?}", update_aad_result);
+
let result = operation.update(
- Some(¶ms),
- Some(&ciphertext),
- None,
- None,
- &mut out_params,
- &mut output,
+ &ciphertext,
+ None, /* authToken */
+ None, /* timestampToken */
);
assert!(result.is_ok(), "{:?}", result);
- assert_eq!(result.unwrap(), message.len() as i32);
- assert!(output.is_some());
- assert_eq!(output.unwrap().data, message.to_vec());
- let result = operation.finish(Some(¶ms), None, None, None, None, &mut out_params);
+ assert_eq!(result.unwrap(), message);
+ let result = operation.finish(
+ None, /* input */
+ None, /* signature */
+ None, /* authToken */
+ None, /* timestampToken */
+ None, /* confirmationToken */
+ );
assert!(result.is_ok(), "{:?}", result);
- assert!(out_params.is_some());
}
#[test]
@@ -363,4 +376,85 @@
assert!(result.is_ok(), "{:?}", result);
assert_ne!(result.unwrap().len(), 0);
}
+
+ #[test]
+ fn test_get_key_characteristics() {
+ let legacy = get_device_or_skip_test!();
+ let hw_info = legacy.getHardwareInfo().expect("GetHardwareInfo");
+
+ let blob = generate_rsa_key(legacy.as_ref(), false, false);
+ let characteristics =
+ legacy.getKeyCharacteristics(&blob, &[], &[]).expect("GetKeyCharacteristics.");
+
+ assert!(characteristics.iter().any(|kc| kc.securityLevel == hw_info.securityLevel));
+ let sec_level_enforced = &characteristics
+ .iter()
+ .find(|kc| kc.securityLevel == hw_info.securityLevel)
+ .expect("There should be characteristics matching the device's security level.")
+ .authorizations;
+
+ assert!(sec_level_enforced.iter().any(|kp| matches!(
+ kp,
+ KeyParameter {
+ tag: Tag::PURPOSE,
+ value: KeyParameterValue::KeyPurpose(KeyPurpose::SIGN)
+ }
+ )));
+ assert!(sec_level_enforced.iter().any(|kp| matches!(
+ kp,
+ KeyParameter { tag: Tag::DIGEST, value: KeyParameterValue::Digest(Digest::SHA_2_256) }
+ )));
+ assert!(sec_level_enforced.iter().any(|kp| matches!(
+ kp,
+ KeyParameter {
+ tag: Tag::PADDING,
+ value: KeyParameterValue::PaddingMode(PaddingMode::RSA_PSS)
+ }
+ )));
+ assert!(sec_level_enforced.iter().any(|kp| matches!(
+ kp,
+ KeyParameter {
+ tag: Tag::ALGORITHM,
+ value: KeyParameterValue::Algorithm(Algorithm::RSA)
+ }
+ )));
+ assert!(sec_level_enforced.iter().any(|kp| matches!(
+ kp,
+ KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(2048) }
+ )));
+ assert!(sec_level_enforced.iter().any(|kp| matches!(
+ kp,
+ KeyParameter {
+ tag: Tag::RSA_PUBLIC_EXPONENT,
+ value: KeyParameterValue::LongInteger(65537)
+ }
+ )));
+ assert!(sec_level_enforced.iter().any(|kp| matches!(
+ kp,
+ KeyParameter { tag: Tag::NO_AUTH_REQUIRED, value: KeyParameterValue::BoolValue(true) }
+ )));
+ assert!(sec_level_enforced.iter().any(|kp| matches!(
+ kp,
+ KeyParameter {
+ tag: Tag::ORIGIN,
+ value: KeyParameterValue::Origin(KeyOrigin::GENERATED)
+ }
+ )));
+ assert!(sec_level_enforced.iter().any(|kp| matches!(
+ kp,
+ KeyParameter { tag: Tag::OS_VERSION, value: KeyParameterValue::Integer(_) }
+ )));
+ assert!(sec_level_enforced.iter().any(|kp| matches!(
+ kp,
+ KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KeyParameterValue::Integer(_) }
+ )));
+ assert!(sec_level_enforced.iter().any(|kp| matches!(
+ kp,
+ KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KeyParameterValue::Integer(_) }
+ )));
+ assert!(sec_level_enforced.iter().any(|kp| matches!(
+ kp,
+ KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KeyParameterValue::Integer(_) }
+ )));
+ }
}
diff --git a/keystore2/src/km_compat/slot_test.cpp b/keystore2/src/km_compat/slot_test.cpp
index 37e7b36..43f3bc6 100644
--- a/keystore2/src/km_compat/slot_test.cpp
+++ b/keystore2/src/km_compat/slot_test.cpp
@@ -24,7 +24,6 @@
using ::aidl::android::hardware::security::keymint::Algorithm;
using ::aidl::android::hardware::security::keymint::BlockMode;
-using ::aidl::android::hardware::security::keymint::ByteArray;
using ::aidl::android::hardware::security::keymint::Certificate;
using ::aidl::android::hardware::security::keymint::Digest;
using ::aidl::android::hardware::security::keymint::ErrorCode;
@@ -100,18 +99,19 @@
// Calling finish should free up a slot.
auto last = operations.back();
operations.pop_back();
- std::optional<KeyParameterArray> kpa;
std::vector<uint8_t> byteVec;
- auto status = last->finish(std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt,
- &kpa, &byteVec);
+ auto status = last->finish(std::nullopt /* input */, std::nullopt /* signature */,
+ std::nullopt /* authToken */, std::nullopt /* timestampToken */,
+ std::nullopt /* confirmationToken */, &byteVec);
ASSERT_TRUE(status.isOk());
result = begin(device, true);
ASSERT_TRUE(std::holds_alternative<BeginResult>(result));
operations.push_back(std::get<BeginResult>(result).operation);
// Calling finish and abort on an already-finished operation should not free up another slot.
- status = last->finish(std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt,
- &kpa, &byteVec);
+ status = last->finish(std::nullopt /* input */, std::nullopt /* signature */,
+ std::nullopt /* authToken */, std::nullopt /* timestampToken */,
+ std::nullopt /* confirmationToken */, &byteVec);
ASSERT_TRUE(!status.isOk());
status = last->abort();
ASSERT_TRUE(!status.isOk());
@@ -130,8 +130,9 @@
operations.push_back(std::get<BeginResult>(result).operation);
// Calling finish and abort on an already-aborted operation should not free up another slot.
- status = last->finish(std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt,
- &kpa, &byteVec);
+ status = last->finish(std::nullopt /* input */, std::nullopt /* signature */,
+ std::nullopt /* authToken */, std::nullopt /* timestampToken */,
+ std::nullopt /* confirmationToken */, &byteVec);
ASSERT_TRUE(!status.isOk());
status = last->abort();
ASSERT_TRUE(!status.isOk());
diff --git a/keystore2/src/legacy_blob.rs b/keystore2/src/legacy_blob.rs
index b51f644..7454cca 100644
--- a/keystore2/src/legacy_blob.rs
+++ b/keystore2/src/legacy_blob.rs
@@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#![allow(dead_code)]
-
//! This module implements methods to load legacy keystore key blob files.
use crate::{
@@ -26,7 +24,7 @@
SecurityLevel::SecurityLevel, Tag::Tag, TagType::TagType,
};
use anyhow::{Context, Result};
-use keystore2_crypto::{aes_gcm_decrypt, derive_key_from_password, ZVec};
+use keystore2_crypto::{aes_gcm_decrypt, Password, ZVec};
use std::collections::{HashMap, HashSet};
use std::{convert::TryInto, fs::File, path::Path, path::PathBuf};
use std::{
@@ -206,7 +204,7 @@
}
impl LegacyBlobLoader {
- const IV_SIZE: usize = keystore2_crypto::IV_LENGTH;
+ const IV_SIZE: usize = keystore2_crypto::LEGACY_IV_LENGTH;
const GCM_TAG_LENGTH: usize = keystore2_crypto::TAG_LENGTH;
const SALT_SIZE: usize = keystore2_crypto::SALT_LENGTH;
@@ -216,7 +214,7 @@
// flags (1 Byte)
// info (1 Byte)
// initialization_vector (16 Bytes)
- // integrity (MD5 digest or gcb tag) (16 Bytes)
+ // integrity (MD5 digest or gcm tag) (16 Bytes)
// length (4 Bytes)
const COMMON_HEADER_SIZE: usize = 4 + Self::IV_SIZE + Self::GCM_TAG_LENGTH + 4;
@@ -227,7 +225,7 @@
const LENGTH_OFFSET: usize = 4 + Self::IV_SIZE + Self::GCM_TAG_LENGTH;
const IV_OFFSET: usize = 4;
const AEAD_TAG_OFFSET: usize = Self::IV_OFFSET + Self::IV_SIZE;
- const DIGEST_OFFSET: usize = Self::IV_OFFSET + Self::IV_SIZE;
+ const _DIGEST_OFFSET: usize = Self::IV_OFFSET + Self::IV_SIZE;
/// Construct a new LegacyBlobLoader with a root path of `path` relative to which it will
/// expect legacy key blob files.
@@ -418,14 +416,14 @@
BlobValue::Encrypted { iv, tag, data } => Ok(Blob {
flags: blob.flags,
value: BlobValue::Decrypted(
- decrypt(&data, &iv, &tag, None, None)
+ decrypt(data, iv, tag, None, None)
.context("In new_from_stream_decrypt_with.")?,
),
}),
BlobValue::PwEncrypted { iv, tag, data, salt, key_size } => Ok(Blob {
flags: blob.flags,
value: BlobValue::Decrypted(
- decrypt(&data, &iv, &tag, Some(salt), Some(*key_size))
+ decrypt(data, iv, tag, Some(salt), Some(*key_size))
.context("In new_from_stream_decrypt_with.")?,
),
}),
@@ -484,7 +482,7 @@
let element_size =
read_ne_u32(stream).context("In read_key_parameters: While reading element size.")?;
- let elements_buffer = stream
+ let mut element_stream = stream
.get(0..element_size as usize)
.ok_or(KsError::Rc(ResponseCode::VALUE_CORRUPTED))
.context("In read_key_parameters: While reading elements buffer.")?;
@@ -492,8 +490,6 @@
// update the stream position.
*stream = &stream[element_size as usize..];
- let mut element_stream = &elements_buffer[..];
-
let mut params: Vec<KeyParameterValue> = Vec::new();
for _ in 0..element_count {
let tag = Tag(read_ne_i32(&mut element_stream).context("In read_key_parameters.")?);
@@ -603,6 +599,15 @@
// * USRCERT was used for public certificates of USRPKEY entries. But KeyChain also
// used this for user installed certificates without private key material.
+ const KNOWN_KEYSTORE_PREFIXES: &'static [&'static str] =
+ &["USRPKEY_", "USRSKEY_", "USRCERT_", "CACERT_"];
+
+ fn is_keystore_alias(encoded_alias: &str) -> bool {
+ // We can check the encoded alias because the prefixes we are interested
+ // in are all in the printable range that don't get mangled.
+ Self::KNOWN_KEYSTORE_PREFIXES.iter().any(|prefix| encoded_alias.starts_with(prefix))
+ }
+
fn read_km_blob_file(&self, uid: u32, alias: &str) -> Result<Option<(Blob, String)>> {
let mut iter = ["USRPKEY", "USRSKEY"].iter();
@@ -634,8 +639,138 @@
Ok(Some(Self::new_from_stream(&mut file).context("In read_generic_blob.")?))
}
+ /// Read a legacy keystore entry blob.
+ pub fn read_legacy_keystore_entry(&self, uid: u32, alias: &str) -> Result<Option<Vec<u8>>> {
+ let path = match self.make_legacy_keystore_entry_filename(uid, alias) {
+ Some(path) => path,
+ None => return Ok(None),
+ };
+
+ let blob = Self::read_generic_blob(&path)
+ .context("In read_legacy_keystore_entry: Failed to read blob.")?;
+
+ Ok(blob.and_then(|blob| match blob.value {
+ BlobValue::Generic(blob) => Some(blob),
+ _ => {
+ log::info!("Unexpected legacy keystore entry blob type. Ignoring");
+ None
+ }
+ }))
+ }
+
+ /// Remove a legacy keystore entry by the name alias with owner uid.
+ pub fn remove_legacy_keystore_entry(&self, uid: u32, alias: &str) -> Result<()> {
+ let path = match self.make_legacy_keystore_entry_filename(uid, alias) {
+ Some(path) => path,
+ None => return Ok(()),
+ };
+
+ if let Err(e) = Self::with_retry_interrupted(|| fs::remove_file(path.as_path())) {
+ match e.kind() {
+ ErrorKind::NotFound => return Ok(()),
+ _ => return Err(e).context("In remove_legacy_keystore_entry."),
+ }
+ }
+
+ let user_id = uid_to_android_user(uid);
+ self.remove_user_dir_if_empty(user_id)
+ .context("In remove_legacy_keystore_entry: Trying to remove empty user dir.")
+ }
+
+ /// List all entries belonging to the given uid.
+ pub fn list_legacy_keystore_entries_for_uid(&self, uid: u32) -> Result<Vec<String>> {
+ let mut path = self.path.clone();
+ let user_id = uid_to_android_user(uid);
+ path.push(format!("user_{}", user_id));
+ let uid_str = uid.to_string();
+ let dir = match Self::with_retry_interrupted(|| fs::read_dir(path.as_path())) {
+ Ok(dir) => dir,
+ Err(e) => match e.kind() {
+ ErrorKind::NotFound => return Ok(Default::default()),
+ _ => {
+ return Err(e).context(format!(
+ concat!(
+ "In list_legacy_keystore_entries_for_uid: ,",
+ "Failed to open legacy blob database: {:?}"
+ ),
+ path
+ ))
+ }
+ },
+ };
+ let mut result: Vec<String> = Vec::new();
+ for entry in dir {
+ let file_name = entry
+ .context("In list_legacy_keystore_entries_for_uid: Trying to access dir entry")?
+ .file_name();
+ if let Some(f) = file_name.to_str() {
+ let encoded_alias = &f[uid_str.len() + 1..];
+ if f.starts_with(&uid_str) && !Self::is_keystore_alias(encoded_alias) {
+ result.push(Self::decode_alias(encoded_alias).context(
+ "In list_legacy_keystore_entries_for_uid: Trying to decode alias.",
+ )?)
+ }
+ }
+ }
+ Ok(result)
+ }
+
+ fn extract_legacy_alias(encoded_alias: &str) -> Option<String> {
+ if !Self::is_keystore_alias(encoded_alias) {
+ Self::decode_alias(encoded_alias).ok()
+ } else {
+ None
+ }
+ }
+
+ /// Lists all keystore entries belonging to the given user. Returns a map of UIDs
+ /// to sets of decoded aliases. Only returns entries that do not begin with
+ /// KNOWN_KEYSTORE_PREFIXES.
+ pub fn list_legacy_keystore_entries_for_user(
+ &self,
+ user_id: u32,
+ ) -> Result<HashMap<u32, HashSet<String>>> {
+ let user_entries = self
+ .list_user(user_id)
+ .context("In list_legacy_keystore_entries_for_user: Trying to list user.")?;
+
+ let result =
+ user_entries.into_iter().fold(HashMap::<u32, HashSet<String>>::new(), |mut acc, v| {
+ if let Some(sep_pos) = v.find('_') {
+ if let Ok(uid) = v[0..sep_pos].parse::<u32>() {
+ if let Some(alias) = Self::extract_legacy_alias(&v[sep_pos + 1..]) {
+ let entry = acc.entry(uid).or_default();
+ entry.insert(alias);
+ }
+ }
+ }
+ acc
+ });
+ Ok(result)
+ }
+
+ /// This function constructs the legacy blob file name which has the form:
+ /// user_<android user id>/<uid>_<alias>. Legacy blob file names must not use
+ /// known keystore prefixes.
+ fn make_legacy_keystore_entry_filename(&self, uid: u32, alias: &str) -> Option<PathBuf> {
+ // Legacy entries must not use known keystore prefixes.
+ if Self::is_keystore_alias(alias) {
+ log::warn!(
+ "Known keystore prefixes cannot be used with legacy keystore -> ignoring request."
+ );
+ return None;
+ }
+
+ let mut path = self.path.clone();
+ let user_id = uid_to_android_user(uid);
+ let encoded_alias = Self::encode_alias(alias);
+ path.push(format!("user_{}", user_id));
+ path.push(format!("{}_{}", uid, encoded_alias));
+ Some(path)
+ }
+
/// This function constructs the blob file name which has the form:
- /// user_<android user id>/<uid>_<alias>.
+ /// user_<android user id>/<uid>_<prefix>_<alias>.
fn make_blob_filename(&self, uid: u32, alias: &str, prefix: &str) -> PathBuf {
let user_id = uid_to_android_user(uid);
let encoded_alias = Self::encode_alias(&format!("{}_{}", prefix, alias));
@@ -696,12 +831,12 @@
.is_none())
}
- fn extract_alias(encoded_alias: &str) -> Option<String> {
+ fn extract_keystore_alias(encoded_alias: &str) -> Option<String> {
// We can check the encoded alias because the prefixes we are interested
// in are all in the printable range that don't get mangled.
- for prefix in &["USRPKEY_", "USRSKEY_", "USRCERT_", "CACERT_"] {
+ for prefix in Self::KNOWN_KEYSTORE_PREFIXES {
if let Some(alias) = encoded_alias.strip_prefix(prefix) {
- return Self::decode_alias(&alias).ok();
+ return Self::decode_alias(alias).ok();
}
}
None
@@ -711,10 +846,18 @@
/// encoded with UID prefix.
fn list_user(&self, user_id: u32) -> Result<Vec<String>> {
let path = self.make_user_path_name(user_id);
- let dir =
- Self::with_retry_interrupted(|| fs::read_dir(path.as_path())).with_context(|| {
- format!("In list_user: Failed to open legacy blob database. {:?}", path)
- })?;
+ let dir = match Self::with_retry_interrupted(|| fs::read_dir(path.as_path())) {
+ Ok(dir) => dir,
+ Err(e) => match e.kind() {
+ ErrorKind::NotFound => return Ok(Default::default()),
+ _ => {
+ return Err(e).context(format!(
+ "In list_user: Failed to open legacy blob database. {:?}",
+ path
+ ))
+ }
+ },
+ };
let mut result: Vec<String> = Vec::new();
for entry in dir {
let file_name = entry.context("In list_user: Trying to access dir entry")?.file_name();
@@ -739,7 +882,7 @@
user_entries.into_iter().fold(HashMap::<u32, HashSet<String>>::new(), |mut acc, v| {
if let Some(sep_pos) = v.find('_') {
if let Ok(uid) = v[0..sep_pos].parse::<u32>() {
- if let Some(alias) = Self::extract_alias(&v[sep_pos + 1..]) {
+ if let Some(alias) = Self::extract_keystore_alias(&v[sep_pos + 1..]) {
let entry = acc.entry(uid).or_default();
entry.insert(alias);
}
@@ -767,7 +910,7 @@
return None;
}
let encoded_alias = &v[uid_str.len()..];
- Self::extract_alias(encoded_alias)
+ Self::extract_keystore_alias(encoded_alias)
})
.collect();
@@ -838,18 +981,24 @@
if something_was_deleted {
let user_id = uid_to_android_user(uid);
- if self
- .is_empty_user(user_id)
- .context("In remove_keystore_entry: Trying to check for empty user dir.")?
- {
- let user_path = self.make_user_path_name(user_id);
- Self::with_retry_interrupted(|| fs::remove_dir(user_path.as_path())).ok();
- }
+ self.remove_user_dir_if_empty(user_id)
+ .context("In remove_keystore_entry: Trying to remove empty user dir.")?;
}
Ok(something_was_deleted)
}
+ fn remove_user_dir_if_empty(&self, user_id: u32) -> Result<()> {
+ if self
+ .is_empty_user(user_id)
+ .context("In remove_user_dir_if_empty: Trying to check for empty user dir.")?
+ {
+ let user_path = self.make_user_path_name(user_id);
+ Self::with_retry_interrupted(|| fs::remove_dir(user_path.as_path())).ok();
+ }
+ Ok(())
+ }
+
/// Load a legacy key blob entry by uid and alias.
pub fn load_by_uid_alias(
&self,
@@ -869,8 +1018,7 @@
let decrypted = match key_manager
.get_per_boot_key_by_user_id(uid_to_android_user(uid))
{
- Some(key) => aes_gcm_decrypt(data, iv, tag, &(key.get_key()))
- .context(
+ Some(key) => key.aes_gcm_decrypt(data, iv, tag).context(
"In load_by_uid_alias: while trying to decrypt legacy blob.",
)?,
None => {
@@ -940,22 +1088,29 @@
}
/// Load and decrypt legacy super key blob.
- pub fn load_super_key(&self, user_id: u32, pw: &[u8]) -> Result<Option<ZVec>> {
+ pub fn load_super_key(&self, user_id: u32, pw: &Password) -> Result<Option<ZVec>> {
let path = self.make_super_key_filename(user_id);
let blob = Self::read_generic_blob(&path)
.context("In load_super_key: While loading super key.")?;
let blob = match blob {
Some(blob) => match blob {
- Blob {
- value: BlobValue::PwEncrypted { iv, tag, data, salt, key_size }, ..
- } => {
- let key = derive_key_from_password(pw, Some(&salt), key_size)
- .context("In load_super_key: Failed to derive key from password.")?;
- let blob = aes_gcm_decrypt(&data, &iv, &tag, &key).context(
- "In load_super_key: while trying to decrypt legacy super key blob.",
- )?;
- Some(blob)
+ Blob { flags, value: BlobValue::PwEncrypted { iv, tag, data, salt, key_size } } => {
+ if (flags & flags::ENCRYPTED) != 0 {
+ let key = pw
+ .derive_key(Some(&salt), key_size)
+ .context("In load_super_key: Failed to derive key from password.")?;
+ let blob = aes_gcm_decrypt(&data, &iv, &tag, &key).context(
+ "In load_super_key: while trying to decrypt legacy super key blob.",
+ )?;
+ Some(blob)
+ } else {
+ // In 2019 we had some unencrypted super keys due to b/141955555.
+ Some(
+ data.try_into()
+ .context("In load_super_key: Trying to convert key into ZVec")?,
+ )
+ }
}
_ => {
return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED)).context(
@@ -1038,7 +1193,7 @@
let encoded = LegacyBlobLoader::encode_alias(&alias_str);
let decoded = match LegacyBlobLoader::decode_alias(&encoded) {
Ok(d) => d,
- Err(_) => panic!(format!("random_alias: {:x?}\nencoded {}", random_alias, encoded)),
+ Err(_) => panic!("random_alias: {:x?}\nencoded {}", random_alias, encoded),
};
assert_eq!(random_alias.to_vec(), decoded.bytes().collect::<Vec<u8>>());
}
@@ -1185,7 +1340,7 @@
CACERT_NON_AUTHBOUND,
)?;
- let key_manager = crate::super_key::SuperKeyManager::new();
+ let key_manager: SuperKeyManager = Default::default();
let mut db = crate::database::KeystoreDB::new(temp_dir.path(), None)?;
let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
@@ -1198,7 +1353,7 @@
Some(&error::Error::Rc(ResponseCode::LOCKED))
);
- key_manager.unlock_user_key(&mut db, 0, PASSWORD, &legacy_blob_loader)?;
+ key_manager.unlock_user_key(&mut db, 0, &(PASSWORD.into()), &legacy_blob_loader)?;
if let (Some((Blob { flags, value: _ }, _params)), Some(cert), Some(chain)) =
legacy_blob_loader.load_by_uid_alias(10223, "authbound", Some(&key_manager))?
@@ -1250,4 +1405,24 @@
Ok(())
}
+
+ #[test]
+ fn list_non_existing_user() -> Result<()> {
+ let temp_dir = TempDir::new("list_non_existing_user")?;
+ let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
+
+ assert!(legacy_blob_loader.list_user(20)?.is_empty());
+
+ Ok(())
+ }
+
+ #[test]
+ fn list_legacy_keystore_entries_on_non_existing_user() -> Result<()> {
+ let temp_dir = TempDir::new("list_legacy_keystore_entries_on_non_existing_user")?;
+ let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
+
+ assert!(legacy_blob_loader.list_legacy_keystore_entries_for_user(20)?.is_empty());
+
+ Ok(())
+ }
}
diff --git a/keystore2/src/legacy_blob/test/legacy_blob_test_vectors.rs b/keystore2/src/legacy_blob/test/legacy_blob_test_vectors.rs
index aa99162..14bd40c 100644
--- a/keystore2/src/legacy_blob/test/legacy_blob_test_vectors.rs
+++ b/keystore2/src/legacy_blob/test/legacy_blob_test_vectors.rs
@@ -741,7 +741,7 @@
0xab, 0xae, 0x24, 0xe2, 0x44, 0x35, 0x16, 0x8d, 0x55, 0x3c, 0xe4,
];
-pub static DECRYPTED_USRPKEY_AUTHBOUND: &[u8] = &[
+pub static _DECRYPTED_USRPKEY_AUTHBOUND: &[u8] = &[
0x44, 0x4b, 0x4d, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0xc6, 0x15, 0x3a, 0x08, 0x1e, 0x43, 0xba, 0x7a, 0x0f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
diff --git a/keystore2/src/legacy_migrator.rs b/keystore2/src/legacy_migrator.rs
index 1ae8719..65f4b0b 100644
--- a/keystore2/src/legacy_migrator.rs
+++ b/keystore2/src/legacy_migrator.rs
@@ -14,24 +14,26 @@
//! This module acts as a bridge between the legacy key database and the keystore2 database.
-use crate::database::{
- BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, EncryptedBy, KeyMetaData, KeyMetaEntry,
- KeystoreDB, Uuid, KEYSTORE_UUID,
-};
-use crate::error::Error;
use crate::key_parameter::KeyParameterValue;
use crate::legacy_blob::BlobValue;
-use crate::utils::uid_to_android_user;
+use crate::utils::{uid_to_android_user, watchdog as wd};
use crate::{async_task::AsyncTask, legacy_blob::LegacyBlobLoader};
+use crate::{database::KeyType, error::Error};
+use crate::{
+ database::{
+ BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, EncryptedBy, KeyMetaData,
+ KeyMetaEntry, KeystoreDB, Uuid, KEYSTORE_UUID,
+ },
+ super_key::USER_SUPER_KEY,
+};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
};
use anyhow::{Context, Result};
use core::ops::Deref;
-use keystore2_crypto::ZVec;
+use keystore2_crypto::{Password, ZVec};
use std::collections::{HashMap, HashSet};
-use std::convert::TryInto;
use std::sync::atomic::{AtomicU8, Ordering};
use std::sync::mpsc::channel;
use std::sync::{Arc, Mutex};
@@ -194,6 +196,8 @@
/// List all aliases for uid in the legacy database.
pub fn list_uid(&self, domain: Domain, namespace: i64) -> Result<Vec<KeyDescriptor>> {
+ let _wp = wd::watch_millis("LegacyMigrator::list_uid", 500);
+
let uid = match (domain, namespace) {
(Domain::APP, namespace) => namespace as u32,
(Domain::SELINUX, Self::WIFI_NAMESPACE) => Self::AID_WIFI,
@@ -288,6 +292,8 @@
where
F: Fn() -> Result<T>,
{
+ let _wp = wd::watch_millis("LegacyMigrator::with_try_migrate", 500);
+
// Access the key and return on success.
match key_accessor() {
Ok(result) => return Ok(result),
@@ -334,23 +340,22 @@
pub fn with_try_migrate_super_key<F, T>(
&self,
user_id: u32,
- pw: &[u8],
+ pw: &Password,
mut key_accessor: F,
) -> Result<Option<T>>
where
F: FnMut() -> Result<Option<T>>,
{
+ let _wp = wd::watch_millis("LegacyMigrator::with_try_migrate_super_key", 500);
+
match key_accessor() {
Ok(Some(result)) => return Ok(Some(result)),
Ok(None) => {}
Err(e) => return Err(e),
}
-
- let pw: ZVec = pw
- .try_into()
- .context("In with_try_migrate_super_key: copying the password into a zvec.")?;
+ let pw = pw.try_clone().context("In with_try_migrate_super_key: Cloning password.")?;
let result = self.do_serialized(move |migrator_state| {
- migrator_state.check_and_migrate_super_key(user_id, pw)
+ migrator_state.check_and_migrate_super_key(user_id, &pw)
});
if let Some(result) = result {
@@ -365,6 +370,8 @@
/// Deletes all keys belonging to the given namespace, migrating them into the database
/// for subsequent garbage collection if necessary.
pub fn bulk_delete_uid(&self, domain: Domain, nspace: i64) -> Result<()> {
+ let _wp = wd::watch_millis("LegacyMigrator::bulk_delete_uid", 500);
+
let uid = match (domain, nspace) {
(Domain::APP, nspace) => nspace as u32,
(Domain::SELINUX, Self::WIFI_NAMESPACE) => Self::AID_WIFI,
@@ -386,6 +393,8 @@
user_id: u32,
keep_non_super_encrypted_keys: bool,
) -> Result<()> {
+ let _wp = wd::watch_millis("LegacyMigrator::bulk_delete_user", 500);
+
let result = self.do_serialized(move |migrator_state| {
migrator_state
.bulk_delete(BulkDeleteRequest::User(user_id), keep_non_super_encrypted_keys)
@@ -421,7 +430,7 @@
.context("In list_uid: Trying to list legacy entries.")
}
- /// This is a key migration request that can run in the migrator thread. This should
+ /// This is a key migration request that must run in the migrator thread. This must
/// be passed to do_serialized.
fn check_and_migrate(&mut self, uid: u32, mut key: KeyDescriptor) -> Result<()> {
let alias = key.alias.clone().ok_or_else(|| {
@@ -454,7 +463,7 @@
let super_key_id = match self
.db
- .load_super_key(user_id)
+ .load_super_key(&USER_SUPER_KEY, user_id)
.context("In check_and_migrate: Failed to load super key")?
{
Some((_, entry)) => entry.id(),
@@ -514,6 +523,7 @@
self.db
.store_new_key(
&key,
+ KeyType::Client,
¶ms,
&(&blob, &blob_metadata),
&CertificateInfo::new(user_cert, ca_cert),
@@ -526,7 +536,7 @@
None => {
if let Some(ca_cert) = ca_cert {
self.db
- .store_new_certificate(&key, &ca_cert, &KEYSTORE_UUID)
+ .store_new_certificate(&key, KeyType::Client, &ca_cert, &KEYSTORE_UUID)
.context("In check_and_migrate: Failed to insert new certificate.")?;
Ok(())
} else {
@@ -550,24 +560,32 @@
}
}
- fn check_and_migrate_super_key(&mut self, user_id: u32, pw: ZVec) -> Result<()> {
+ fn check_and_migrate_super_key(&mut self, user_id: u32, pw: &Password) -> Result<()> {
if self.recently_migrated_super_key.contains(&user_id) {
return Ok(());
}
if let Some(super_key) = self
.legacy_loader
- .load_super_key(user_id, &pw)
+ .load_super_key(user_id, pw)
.context("In check_and_migrate_super_key: Trying to load legacy super key.")?
{
let (blob, blob_metadata) =
- crate::super_key::SuperKeyManager::encrypt_with_password(&super_key, &pw)
+ crate::super_key::SuperKeyManager::encrypt_with_password(&super_key, pw)
.context("In check_and_migrate_super_key: Trying to encrypt super key.")?;
- self.db.store_super_key(user_id, &(&blob, &blob_metadata)).context(concat!(
- "In check_and_migrate_super_key: ",
- "Trying to insert legacy super_key into the database."
- ))?;
+ self.db
+ .store_super_key(
+ user_id,
+ &USER_SUPER_KEY,
+ &blob,
+ &blob_metadata,
+ &KeyMetaData::new(),
+ )
+ .context(concat!(
+ "In check_and_migrate_super_key: ",
+ "Trying to insert legacy super_key into the database."
+ ))?;
self.legacy_loader.remove_super_key(user_id);
self.recently_migrated_super_key.insert(user_id);
Ok(())
@@ -606,7 +624,7 @@
let super_key_id = self
.db
- .load_super_key(user_id)
+ .load_super_key(&USER_SUPER_KEY, user_id)
.context("In bulk_delete: Failed to load super key")?
.map(|(_, entry)| entry.id());
@@ -706,8 +724,8 @@
fn deref(&self) -> &Self::Target {
match self {
- Self::Vec(v) => &v,
- Self::ZVec(v) => &v,
+ Self::Vec(v) => v,
+ Self::ZVec(v) => v,
}
}
}
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index 358fce8..8b629b1 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -16,24 +16,37 @@
#![recursion_limit = "256"]
pub mod apc;
+pub mod async_task;
pub mod authorization;
+pub mod boot_level_keys;
pub mod database;
+pub mod ec_crypto;
pub mod enforcements;
+pub mod entropy;
pub mod error;
pub mod globals;
+pub mod id_rotation;
/// Internal Representation of Key Parameter and convenience functions.
pub mod key_parameter;
pub mod legacy_blob;
pub mod legacy_migrator;
+pub mod maintenance;
+pub mod metrics;
+pub mod metrics_store;
pub mod operation;
pub mod permission;
+pub mod raw_device;
pub mod remote_provisioning;
pub mod security_level;
pub mod service;
-pub mod user_manager;
+pub mod shared_secret_negotiation;
+pub mod try_insert;
pub mod utils;
-mod async_task;
-mod db_utils;
+mod attestation_key_utils;
+mod audit_log;
mod gc;
mod super_key;
+
+#[cfg(feature = "watchdog")]
+mod watchdog;
diff --git a/keystore2/src/maintenance.rs b/keystore2/src/maintenance.rs
new file mode 100644
index 0000000..7ce9042
--- /dev/null
+++ b/keystore2/src/maintenance.rs
@@ -0,0 +1,315 @@
+// 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.
+
+//! This module implements IKeystoreMaintenance AIDL interface.
+
+use crate::database::{KeyEntryLoadBits, KeyType, MonotonicRawTime};
+use crate::error::map_km_error;
+use crate::error::map_or_log_err;
+use crate::error::Error;
+use crate::globals::get_keymint_device;
+use crate::globals::{DB, LEGACY_MIGRATOR, SUPER_KEY};
+use crate::permission::{KeyPerm, KeystorePerm};
+use crate::super_key::UserState;
+use crate::utils::{check_key_permission, check_keystore_permission, watchdog as wd};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ IKeyMintDevice::IKeyMintDevice, SecurityLevel::SecurityLevel,
+};
+use android_security_maintenance::aidl::android::security::maintenance::{
+ IKeystoreMaintenance::{BnKeystoreMaintenance, IKeystoreMaintenance},
+ UserState::UserState as AidlUserState,
+};
+use android_security_maintenance::binder::{
+ BinderFeatures, Interface, Result as BinderResult, Strong, ThreadState,
+};
+use android_system_keystore2::aidl::android::system::keystore2::KeyDescriptor::KeyDescriptor;
+use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
+use anyhow::{Context, Result};
+use keystore2_crypto::Password;
+
+/// Reexport Domain for the benefit of DeleteListener
+pub use android_system_keystore2::aidl::android::system::keystore2::Domain::Domain;
+
+/// The Maintenance module takes a delete listener argument which observes user and namespace
+/// deletion events.
+pub trait DeleteListener {
+ /// Called by the maintenance module when an app/namespace is deleted.
+ fn delete_namespace(&self, domain: Domain, namespace: i64) -> Result<()>;
+ /// Called by the maintenance module when a user is deleted.
+ fn delete_user(&self, user_id: u32) -> Result<()>;
+}
+
+/// This struct is defined to implement the aforementioned AIDL interface.
+pub struct Maintenance {
+ delete_listener: Box<dyn DeleteListener + Send + Sync + 'static>,
+}
+
+impl Maintenance {
+ /// Create a new instance of Keystore Maintenance service.
+ pub fn new_native_binder(
+ delete_listener: Box<dyn DeleteListener + Send + Sync + 'static>,
+ ) -> Result<Strong<dyn IKeystoreMaintenance>> {
+ Ok(BnKeystoreMaintenance::new_binder(
+ Self { delete_listener },
+ BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
+ ))
+ }
+
+ fn on_user_password_changed(user_id: i32, password: Option<Password>) -> Result<()> {
+ //Check permission. Function should return if this failed. Therefore having '?' at the end
+ //is very important.
+ check_keystore_permission(KeystorePerm::change_password())
+ .context("In on_user_password_changed.")?;
+
+ if let Some(pw) = password.as_ref() {
+ DB.with(|db| {
+ SUPER_KEY.unlock_screen_lock_bound_key(&mut db.borrow_mut(), user_id as u32, pw)
+ })
+ .context("In on_user_password_changed: unlock_screen_lock_bound_key failed")?;
+ }
+
+ match DB
+ .with(|db| {
+ UserState::get_with_password_changed(
+ &mut db.borrow_mut(),
+ &LEGACY_MIGRATOR,
+ &SUPER_KEY,
+ user_id as u32,
+ password.as_ref(),
+ )
+ })
+ .context("In on_user_password_changed.")?
+ {
+ UserState::LskfLocked => {
+ // Error - password can not be changed when the device is locked
+ Err(Error::Rc(ResponseCode::LOCKED))
+ .context("In on_user_password_changed. Device is locked.")
+ }
+ _ => {
+ // LskfLocked is the only error case for password change
+ Ok(())
+ }
+ }
+ }
+
+ fn add_or_remove_user(&self, user_id: i32) -> Result<()> {
+ // Check permission. Function should return if this failed. Therefore having '?' at the end
+ // is very important.
+ check_keystore_permission(KeystorePerm::change_user()).context("In add_or_remove_user.")?;
+ DB.with(|db| {
+ UserState::reset_user(
+ &mut db.borrow_mut(),
+ &SUPER_KEY,
+ &LEGACY_MIGRATOR,
+ user_id as u32,
+ false,
+ )
+ })
+ .context("In add_or_remove_user: Trying to delete keys from db.")?;
+ self.delete_listener
+ .delete_user(user_id as u32)
+ .context("In add_or_remove_user: While invoking the delete listener.")
+ }
+
+ fn clear_namespace(&self, domain: Domain, nspace: i64) -> Result<()> {
+ // Permission check. Must return on error. Do not touch the '?'.
+ check_keystore_permission(KeystorePerm::clear_uid()).context("In clear_namespace.")?;
+
+ LEGACY_MIGRATOR
+ .bulk_delete_uid(domain, nspace)
+ .context("In clear_namespace: Trying to delete legacy keys.")?;
+ DB.with(|db| db.borrow_mut().unbind_keys_for_namespace(domain, nspace))
+ .context("In clear_namespace: Trying to delete keys from db.")?;
+ self.delete_listener
+ .delete_namespace(domain, nspace)
+ .context("In clear_namespace: While invoking the delete listener.")
+ }
+
+ fn get_state(user_id: i32) -> Result<AidlUserState> {
+ // Check permission. Function should return if this failed. Therefore having '?' at the end
+ // is very important.
+ check_keystore_permission(KeystorePerm::get_state()).context("In get_state.")?;
+ let state = DB
+ .with(|db| {
+ UserState::get(&mut db.borrow_mut(), &LEGACY_MIGRATOR, &SUPER_KEY, user_id as u32)
+ })
+ .context("In get_state. Trying to get UserState.")?;
+
+ match state {
+ UserState::Uninitialized => Ok(AidlUserState::UNINITIALIZED),
+ UserState::LskfUnlocked(_) => Ok(AidlUserState::LSKF_UNLOCKED),
+ UserState::LskfLocked => Ok(AidlUserState::LSKF_LOCKED),
+ }
+ }
+
+ fn call_with_watchdog<F>(sec_level: SecurityLevel, name: &'static str, op: &F) -> Result<()>
+ where
+ F: Fn(Strong<dyn IKeyMintDevice>) -> binder::public_api::Result<()>,
+ {
+ let (km_dev, _, _) = get_keymint_device(&sec_level)
+ .context("In call_with_watchdog: getting keymint device")?;
+
+ let _wp = wd::watch_millis_with("In call_with_watchdog", 500, move || {
+ format!("Seclevel: {:?} Op: {}", sec_level, name)
+ });
+ map_km_error(op(km_dev)).with_context(|| format!("In keymint device: calling {}", name))?;
+ Ok(())
+ }
+
+ fn call_on_all_security_levels<F>(name: &'static str, op: F) -> Result<()>
+ where
+ F: Fn(Strong<dyn IKeyMintDevice>) -> binder::public_api::Result<()>,
+ {
+ let sec_levels = [
+ (SecurityLevel::TRUSTED_ENVIRONMENT, "TRUSTED_ENVIRONMENT"),
+ (SecurityLevel::STRONGBOX, "STRONGBOX"),
+ ];
+ sec_levels.iter().fold(Ok(()), move |result, (sec_level, sec_level_string)| {
+ let curr_result = Maintenance::call_with_watchdog(*sec_level, name, &op);
+ match curr_result {
+ Ok(()) => log::info!(
+ "Call to {} succeeded for security level {}.",
+ name,
+ &sec_level_string
+ ),
+ Err(ref e) => log::error!(
+ "Call to {} failed for security level {}: {}.",
+ name,
+ &sec_level_string,
+ e
+ ),
+ }
+ result.and(curr_result)
+ })
+ }
+
+ fn early_boot_ended() -> Result<()> {
+ check_keystore_permission(KeystorePerm::early_boot_ended())
+ .context("In early_boot_ended. Checking permission")?;
+ log::info!("In early_boot_ended.");
+
+ if let Err(e) = DB.with(|db| SUPER_KEY.set_up_boot_level_cache(&mut db.borrow_mut())) {
+ log::error!("SUPER_KEY.set_up_boot_level_cache failed:\n{:?}\n:(", e);
+ }
+ Maintenance::call_on_all_security_levels("earlyBootEnded", |dev| dev.earlyBootEnded())
+ }
+
+ fn on_device_off_body() -> Result<()> {
+ // Security critical permission check. This statement must return on fail.
+ check_keystore_permission(KeystorePerm::report_off_body())
+ .context("In on_device_off_body.")?;
+
+ DB.with(|db| db.borrow_mut().update_last_off_body(MonotonicRawTime::now()));
+ Ok(())
+ }
+
+ fn migrate_key_namespace(source: &KeyDescriptor, destination: &KeyDescriptor) -> Result<()> {
+ let caller_uid = ThreadState::get_calling_uid();
+
+ DB.with(|db| {
+ let key_id_guard = match source.domain {
+ Domain::APP | Domain::SELINUX | Domain::KEY_ID => {
+ let (key_id_guard, _) = LEGACY_MIGRATOR
+ .with_try_migrate(source, caller_uid, || {
+ db.borrow_mut().load_key_entry(
+ source,
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ caller_uid,
+ |k, av| {
+ check_key_permission(KeyPerm::use_(), k, &av)?;
+ check_key_permission(KeyPerm::delete(), k, &av)?;
+ check_key_permission(KeyPerm::grant(), k, &av)
+ },
+ )
+ })
+ .context("In migrate_key_namespace: Failed to load key blob.")?;
+ key_id_guard
+ }
+ _ => {
+ return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(concat!(
+ "In migrate_key_namespace: ",
+ "Source domain must be one of APP, SELINUX, or KEY_ID."
+ ))
+ }
+ };
+
+ db.borrow_mut().migrate_key_namespace(key_id_guard, destination, caller_uid, |k| {
+ check_key_permission(KeyPerm::rebind(), k, &None)
+ })
+ })
+ }
+
+ fn delete_all_keys() -> Result<()> {
+ // Security critical permission check. This statement must return on fail.
+ check_keystore_permission(KeystorePerm::delete_all_keys())
+ .context("In delete_all_keys. Checking permission")?;
+ log::info!("In delete_all_keys.");
+
+ Maintenance::call_on_all_security_levels("deleteAllKeys", |dev| dev.deleteAllKeys())
+ }
+}
+
+impl Interface for Maintenance {}
+
+impl IKeystoreMaintenance for Maintenance {
+ fn onUserPasswordChanged(&self, user_id: i32, password: Option<&[u8]>) -> BinderResult<()> {
+ let _wp = wd::watch_millis("IKeystoreMaintenance::onUserPasswordChanged", 500);
+ map_or_log_err(Self::on_user_password_changed(user_id, password.map(|pw| pw.into())), Ok)
+ }
+
+ fn onUserAdded(&self, user_id: i32) -> BinderResult<()> {
+ let _wp = wd::watch_millis("IKeystoreMaintenance::onUserAdded", 500);
+ map_or_log_err(self.add_or_remove_user(user_id), Ok)
+ }
+
+ fn onUserRemoved(&self, user_id: i32) -> BinderResult<()> {
+ let _wp = wd::watch_millis("IKeystoreMaintenance::onUserRemoved", 500);
+ map_or_log_err(self.add_or_remove_user(user_id), Ok)
+ }
+
+ fn clearNamespace(&self, domain: Domain, nspace: i64) -> BinderResult<()> {
+ let _wp = wd::watch_millis("IKeystoreMaintenance::clearNamespace", 500);
+ map_or_log_err(self.clear_namespace(domain, nspace), Ok)
+ }
+
+ fn getState(&self, user_id: i32) -> BinderResult<AidlUserState> {
+ let _wp = wd::watch_millis("IKeystoreMaintenance::getState", 500);
+ map_or_log_err(Self::get_state(user_id), Ok)
+ }
+
+ fn earlyBootEnded(&self) -> BinderResult<()> {
+ let _wp = wd::watch_millis("IKeystoreMaintenance::earlyBootEnded", 500);
+ map_or_log_err(Self::early_boot_ended(), Ok)
+ }
+
+ fn onDeviceOffBody(&self) -> BinderResult<()> {
+ let _wp = wd::watch_millis("IKeystoreMaintenance::onDeviceOffBody", 500);
+ map_or_log_err(Self::on_device_off_body(), Ok)
+ }
+
+ fn migrateKeyNamespace(
+ &self,
+ source: &KeyDescriptor,
+ destination: &KeyDescriptor,
+ ) -> BinderResult<()> {
+ let _wp = wd::watch_millis("IKeystoreMaintenance::migrateKeyNamespace", 500);
+ map_or_log_err(Self::migrate_key_namespace(source, destination), Ok)
+ }
+
+ fn deleteAllKeys(&self) -> BinderResult<()> {
+ let _wp = wd::watch_millis("IKeystoreMaintenance::deleteAllKeys", 500);
+ map_or_log_err(Self::delete_all_keys(), Ok)
+ }
+}
diff --git a/keystore2/src/metrics.rs b/keystore2/src/metrics.rs
new file mode 100644
index 0000000..42295b7
--- /dev/null
+++ b/keystore2/src/metrics.rs
@@ -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.
+
+//! This module implements the IKeystoreMetrics AIDL interface, which exposes the API method for the
+//! proxy in the system server to pull the aggregated metrics in keystore.
+use crate::error::map_or_log_err;
+use crate::metrics_store::METRICS_STORE;
+use crate::permission::KeystorePerm;
+use crate::utils::{check_keystore_permission, watchdog as wd};
+use android_security_metrics::aidl::android::security::metrics::{
+ AtomID::AtomID,
+ IKeystoreMetrics::{BnKeystoreMetrics, IKeystoreMetrics},
+ KeystoreAtom::KeystoreAtom,
+};
+use android_security_metrics::binder::{BinderFeatures, Interface, Result as BinderResult, Strong};
+use anyhow::{Context, Result};
+
+/// This struct is defined to implement IKeystoreMetrics AIDL interface.
+pub struct Metrics;
+
+impl Metrics {
+ /// Create a new instance of Keystore Metrics service.
+ pub fn new_native_binder() -> Result<Strong<dyn IKeystoreMetrics>> {
+ Ok(BnKeystoreMetrics::new_binder(
+ Self,
+ BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
+ ))
+ }
+
+ fn pull_metrics(&self, atom_id: AtomID) -> Result<Vec<KeystoreAtom>> {
+ // Check permission. Function should return if this failed. Therefore having '?' at the end
+ // is very important.
+ check_keystore_permission(KeystorePerm::pull_metrics()).context("In pull_metrics.")?;
+ METRICS_STORE.get_atoms(atom_id)
+ }
+}
+
+impl Interface for Metrics {}
+
+impl IKeystoreMetrics for Metrics {
+ fn pullMetrics(&self, atom_id: AtomID) -> BinderResult<Vec<KeystoreAtom>> {
+ let _wp = wd::watch_millis("IKeystoreMetrics::pullMetrics", 500);
+ map_or_log_err(self.pull_metrics(atom_id), Ok)
+ }
+}
diff --git a/keystore2/src/metrics_store.rs b/keystore2/src/metrics_store.rs
new file mode 100644
index 0000000..741d65e
--- /dev/null
+++ b/keystore2/src/metrics_store.rs
@@ -0,0 +1,724 @@
+// 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.
+
+//! This is the metrics store module of keystore. It does the following tasks:
+//! 1. Processes the data about keystore events asynchronously, and
+//! stores them in an in-memory store.
+//! 2. Returns the collected metrics when requested by the statsd proxy.
+
+use crate::error::get_error_code;
+use crate::globals::DB;
+use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
+use crate::operation::Outcome;
+use crate::remote_provisioning::get_pool_status;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
+ HardwareAuthenticatorType::HardwareAuthenticatorType, KeyOrigin::KeyOrigin,
+ KeyParameter::KeyParameter, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
+ SecurityLevel::SecurityLevel,
+};
+use android_security_metrics::aidl::android::security::metrics::{
+ Algorithm::Algorithm as MetricsAlgorithm, AtomID::AtomID, CrashStats::CrashStats,
+ EcCurve::EcCurve as MetricsEcCurve,
+ HardwareAuthenticatorType::HardwareAuthenticatorType as MetricsHardwareAuthenticatorType,
+ KeyCreationWithAuthInfo::KeyCreationWithAuthInfo,
+ KeyCreationWithGeneralInfo::KeyCreationWithGeneralInfo,
+ KeyCreationWithPurposeAndModesInfo::KeyCreationWithPurposeAndModesInfo,
+ KeyOperationWithGeneralInfo::KeyOperationWithGeneralInfo,
+ KeyOperationWithPurposeAndModesInfo::KeyOperationWithPurposeAndModesInfo,
+ KeyOrigin::KeyOrigin as MetricsKeyOrigin, Keystore2AtomWithOverflow::Keystore2AtomWithOverflow,
+ KeystoreAtom::KeystoreAtom, KeystoreAtomPayload::KeystoreAtomPayload,
+ Outcome::Outcome as MetricsOutcome, Purpose::Purpose as MetricsPurpose,
+ RkpError::RkpError as MetricsRkpError, RkpErrorStats::RkpErrorStats,
+ RkpPoolStats::RkpPoolStats, SecurityLevel::SecurityLevel as MetricsSecurityLevel,
+ Storage::Storage as MetricsStorage,
+};
+use anyhow::{Context, Result};
+use lazy_static::lazy_static;
+use rustutils::system_properties::PropertyWatcherError;
+use std::collections::HashMap;
+use std::sync::Mutex;
+use std::time::{Duration, SystemTime, UNIX_EPOCH};
+
+// Note: Crash events are recorded at keystore restarts, based on the assumption that keystore only
+// gets restarted after a crash, during a boot cycle.
+const KEYSTORE_CRASH_COUNT_PROPERTY: &str = "keystore.crash_count";
+
+lazy_static! {
+ /// Singleton for MetricsStore.
+ pub static ref METRICS_STORE: MetricsStore = Default::default();
+}
+
+/// MetricsStore stores the <atom object, count> as <key, value> in the inner hash map,
+/// indexed by the atom id, in the outer hash map.
+/// There can be different atom objects with the same atom id based on the values assigned to the
+/// fields of the atom objects. When an atom object with a particular combination of field values is
+/// inserted, we first check if that atom object is in the inner hash map. If one exists, count
+/// is inceremented. Otherwise, the atom object is inserted with count = 1. Note that count field
+/// of the atom object itself is set to 0 while the object is stored in the hash map. When the atom
+/// objects are queried by the atom id, the corresponding atom objects are retrieved, cloned, and
+/// the count field of the cloned objects is set to the corresponding value field in the inner hash
+/// map before the query result is returned.
+#[derive(Default)]
+pub struct MetricsStore {
+ metrics_store: Mutex<HashMap<AtomID, HashMap<KeystoreAtomPayload, i32>>>,
+}
+
+impl MetricsStore {
+ /// There are some atoms whose maximum cardinality exceeds the cardinality limits tolerated
+ /// by statsd. Statsd tolerates cardinality between 200-300. Therefore, the in-memory storage
+ /// limit for a single atom is set to 250. If the number of atom objects created for a
+ /// particular atom exceeds this limit, an overflow atom object is created to track the ID of
+ /// such atoms.
+ const SINGLE_ATOM_STORE_MAX_SIZE: usize = 250;
+
+ /// Return a vector of atom objects with the given atom ID, if one exists in the metrics_store.
+ /// If any atom object does not exist in the metrics_store for the given atom ID, return an
+ /// empty vector.
+ pub fn get_atoms(&self, atom_id: AtomID) -> Result<Vec<KeystoreAtom>> {
+ // StorageStats is an original pulled atom (i.e. not a pushed atom converted to a
+ // pulledd atom). Therefore, it is handled separately.
+ if AtomID::STORAGE_STATS == atom_id {
+ return pull_storage_stats();
+ }
+
+ // Process and return RKP pool stats.
+ if AtomID::RKP_POOL_STATS == atom_id {
+ return pull_attestation_pool_stats();
+ }
+
+ // Process keystore crash stats.
+ if AtomID::CRASH_STATS == atom_id {
+ return Ok(vec![KeystoreAtom {
+ payload: KeystoreAtomPayload::CrashStats(CrashStats {
+ count_of_crash_events: read_keystore_crash_count()?,
+ }),
+ ..Default::default()
+ }]);
+ }
+
+ // It is safe to call unwrap here since the lock can not be poisoned based on its usage
+ // in this module and the lock is not acquired in the same thread before.
+ let metrics_store_guard = self.metrics_store.lock().unwrap();
+ metrics_store_guard.get(&atom_id).map_or(Ok(Vec::<KeystoreAtom>::new()), |atom_count_map| {
+ Ok(atom_count_map
+ .iter()
+ .map(|(atom, count)| KeystoreAtom { payload: atom.clone(), count: *count })
+ .collect())
+ })
+ }
+
+ /// Insert an atom object to the metrics_store indexed by the atom ID.
+ fn insert_atom(&self, atom_id: AtomID, atom: KeystoreAtomPayload) {
+ // It is ok to unwrap here since the mutex cannot be poisoned according to the way it is
+ // used in this module. And the lock is not acquired by this thread before.
+ let mut metrics_store_guard = self.metrics_store.lock().unwrap();
+ let atom_count_map = metrics_store_guard.entry(atom_id).or_insert_with(HashMap::new);
+ if atom_count_map.len() < MetricsStore::SINGLE_ATOM_STORE_MAX_SIZE {
+ let atom_count = atom_count_map.entry(atom).or_insert(0);
+ *atom_count += 1;
+ } else {
+ // Insert an overflow atom
+ let overflow_atom_count_map = metrics_store_guard
+ .entry(AtomID::KEYSTORE2_ATOM_WITH_OVERFLOW)
+ .or_insert_with(HashMap::new);
+
+ if overflow_atom_count_map.len() < MetricsStore::SINGLE_ATOM_STORE_MAX_SIZE {
+ let overflow_atom = Keystore2AtomWithOverflow { atom_id };
+ let atom_count = overflow_atom_count_map
+ .entry(KeystoreAtomPayload::Keystore2AtomWithOverflow(overflow_atom))
+ .or_insert(0);
+ *atom_count += 1;
+ } else {
+ // This is a rare case, if at all.
+ log::error!("In insert_atom: Maximum storage limit reached for overflow atom.")
+ }
+ }
+ }
+}
+
+/// Log key creation events to be sent to statsd.
+pub fn log_key_creation_event_stats<U>(
+ sec_level: SecurityLevel,
+ key_params: &[KeyParameter],
+ result: &Result<U>,
+) {
+ let (
+ key_creation_with_general_info,
+ key_creation_with_auth_info,
+ key_creation_with_purpose_and_modes_info,
+ ) = process_key_creation_event_stats(sec_level, key_params, result);
+
+ METRICS_STORE
+ .insert_atom(AtomID::KEY_CREATION_WITH_GENERAL_INFO, key_creation_with_general_info);
+ METRICS_STORE.insert_atom(AtomID::KEY_CREATION_WITH_AUTH_INFO, key_creation_with_auth_info);
+ METRICS_STORE.insert_atom(
+ AtomID::KEY_CREATION_WITH_PURPOSE_AND_MODES_INFO,
+ key_creation_with_purpose_and_modes_info,
+ );
+}
+
+// Process the statistics related to key creations and return the three atom objects related to key
+// creations: i) KeyCreationWithGeneralInfo ii) KeyCreationWithAuthInfo
+// iii) KeyCreationWithPurposeAndModesInfo
+fn process_key_creation_event_stats<U>(
+ sec_level: SecurityLevel,
+ key_params: &[KeyParameter],
+ result: &Result<U>,
+) -> (KeystoreAtomPayload, KeystoreAtomPayload, KeystoreAtomPayload) {
+ // In the default atom objects, fields represented by bitmaps and i32 fields
+ // will take 0, except error_code which defaults to 1 indicating NO_ERROR and key_size,
+ // and auth_time_out which defaults to -1.
+ // The boolean fields are set to false by default.
+ // Some keymint enums do have 0 as an enum variant value. In such cases, the corresponding
+ // enum variant value in atoms.proto is incremented by 1, in order to have 0 as the reserved
+ // value for unspecified fields.
+ let mut key_creation_with_general_info = KeyCreationWithGeneralInfo {
+ algorithm: MetricsAlgorithm::ALGORITHM_UNSPECIFIED,
+ key_size: -1,
+ ec_curve: MetricsEcCurve::EC_CURVE_UNSPECIFIED,
+ key_origin: MetricsKeyOrigin::ORIGIN_UNSPECIFIED,
+ error_code: 1,
+ // Default for bool is false (for attestation_requested field).
+ ..Default::default()
+ };
+
+ let mut key_creation_with_auth_info = KeyCreationWithAuthInfo {
+ user_auth_type: MetricsHardwareAuthenticatorType::AUTH_TYPE_UNSPECIFIED,
+ log10_auth_key_timeout_seconds: -1,
+ security_level: MetricsSecurityLevel::SECURITY_LEVEL_UNSPECIFIED,
+ };
+
+ let mut key_creation_with_purpose_and_modes_info = KeyCreationWithPurposeAndModesInfo {
+ algorithm: MetricsAlgorithm::ALGORITHM_UNSPECIFIED,
+ // Default for i32 is 0 (for the remaining bitmap fields).
+ ..Default::default()
+ };
+
+ if let Err(ref e) = result {
+ key_creation_with_general_info.error_code = get_error_code(e);
+ }
+
+ key_creation_with_auth_info.security_level = process_security_level(sec_level);
+
+ for key_param in key_params.iter().map(KsKeyParamValue::from) {
+ match key_param {
+ KsKeyParamValue::Algorithm(a) => {
+ let algorithm = match a {
+ Algorithm::RSA => MetricsAlgorithm::RSA,
+ Algorithm::EC => MetricsAlgorithm::EC,
+ Algorithm::AES => MetricsAlgorithm::AES,
+ Algorithm::TRIPLE_DES => MetricsAlgorithm::TRIPLE_DES,
+ Algorithm::HMAC => MetricsAlgorithm::HMAC,
+ _ => MetricsAlgorithm::ALGORITHM_UNSPECIFIED,
+ };
+ key_creation_with_general_info.algorithm = algorithm;
+ key_creation_with_purpose_and_modes_info.algorithm = algorithm;
+ }
+ KsKeyParamValue::KeySize(s) => {
+ key_creation_with_general_info.key_size = s;
+ }
+ KsKeyParamValue::KeyOrigin(o) => {
+ key_creation_with_general_info.key_origin = match o {
+ KeyOrigin::GENERATED => MetricsKeyOrigin::GENERATED,
+ KeyOrigin::DERIVED => MetricsKeyOrigin::DERIVED,
+ KeyOrigin::IMPORTED => MetricsKeyOrigin::IMPORTED,
+ KeyOrigin::RESERVED => MetricsKeyOrigin::RESERVED,
+ KeyOrigin::SECURELY_IMPORTED => MetricsKeyOrigin::SECURELY_IMPORTED,
+ _ => MetricsKeyOrigin::ORIGIN_UNSPECIFIED,
+ }
+ }
+ KsKeyParamValue::HardwareAuthenticatorType(a) => {
+ key_creation_with_auth_info.user_auth_type = match a {
+ HardwareAuthenticatorType::NONE => MetricsHardwareAuthenticatorType::NONE,
+ HardwareAuthenticatorType::PASSWORD => {
+ MetricsHardwareAuthenticatorType::PASSWORD
+ }
+ HardwareAuthenticatorType::FINGERPRINT => {
+ MetricsHardwareAuthenticatorType::FINGERPRINT
+ }
+ HardwareAuthenticatorType::ANY => MetricsHardwareAuthenticatorType::ANY,
+ _ => MetricsHardwareAuthenticatorType::AUTH_TYPE_UNSPECIFIED,
+ }
+ }
+ KsKeyParamValue::AuthTimeout(t) => {
+ key_creation_with_auth_info.log10_auth_key_timeout_seconds =
+ f32::log10(t as f32) as i32;
+ }
+ KsKeyParamValue::PaddingMode(p) => {
+ compute_padding_mode_bitmap(
+ &mut key_creation_with_purpose_and_modes_info.padding_mode_bitmap,
+ p,
+ );
+ }
+ KsKeyParamValue::Digest(d) => {
+ // key_creation_with_purpose_and_modes_info.digest_bitmap =
+ compute_digest_bitmap(
+ &mut key_creation_with_purpose_and_modes_info.digest_bitmap,
+ d,
+ );
+ }
+ KsKeyParamValue::BlockMode(b) => {
+ compute_block_mode_bitmap(
+ &mut key_creation_with_purpose_and_modes_info.block_mode_bitmap,
+ b,
+ );
+ }
+ KsKeyParamValue::KeyPurpose(k) => {
+ compute_purpose_bitmap(
+ &mut key_creation_with_purpose_and_modes_info.purpose_bitmap,
+ k,
+ );
+ }
+ KsKeyParamValue::EcCurve(e) => {
+ key_creation_with_general_info.ec_curve = match e {
+ EcCurve::P_224 => MetricsEcCurve::P_224,
+ EcCurve::P_256 => MetricsEcCurve::P_256,
+ EcCurve::P_384 => MetricsEcCurve::P_384,
+ EcCurve::P_521 => MetricsEcCurve::P_521,
+ _ => MetricsEcCurve::EC_CURVE_UNSPECIFIED,
+ }
+ }
+ KsKeyParamValue::AttestationChallenge(_) => {
+ key_creation_with_general_info.attestation_requested = true;
+ }
+ _ => {}
+ }
+ }
+ if key_creation_with_general_info.algorithm == MetricsAlgorithm::EC {
+ // Do not record key sizes if Algorithm = EC, in order to reduce cardinality.
+ key_creation_with_general_info.key_size = -1;
+ }
+
+ (
+ KeystoreAtomPayload::KeyCreationWithGeneralInfo(key_creation_with_general_info),
+ KeystoreAtomPayload::KeyCreationWithAuthInfo(key_creation_with_auth_info),
+ KeystoreAtomPayload::KeyCreationWithPurposeAndModesInfo(
+ key_creation_with_purpose_and_modes_info,
+ ),
+ )
+}
+
+/// Log key operation events to be sent to statsd.
+pub fn log_key_operation_event_stats(
+ sec_level: SecurityLevel,
+ key_purpose: KeyPurpose,
+ op_params: &[KeyParameter],
+ op_outcome: &Outcome,
+ key_upgraded: bool,
+) {
+ let (key_operation_with_general_info, key_operation_with_purpose_and_modes_info) =
+ process_key_operation_event_stats(
+ sec_level,
+ key_purpose,
+ op_params,
+ op_outcome,
+ key_upgraded,
+ );
+ METRICS_STORE
+ .insert_atom(AtomID::KEY_OPERATION_WITH_GENERAL_INFO, key_operation_with_general_info);
+ METRICS_STORE.insert_atom(
+ AtomID::KEY_OPERATION_WITH_PURPOSE_AND_MODES_INFO,
+ key_operation_with_purpose_and_modes_info,
+ );
+}
+
+// Process the statistics related to key operations and return the two atom objects related to key
+// operations: i) KeyOperationWithGeneralInfo ii) KeyOperationWithPurposeAndModesInfo
+fn process_key_operation_event_stats(
+ sec_level: SecurityLevel,
+ key_purpose: KeyPurpose,
+ op_params: &[KeyParameter],
+ op_outcome: &Outcome,
+ key_upgraded: bool,
+) -> (KeystoreAtomPayload, KeystoreAtomPayload) {
+ let mut key_operation_with_general_info = KeyOperationWithGeneralInfo {
+ outcome: MetricsOutcome::OUTCOME_UNSPECIFIED,
+ error_code: 1,
+ security_level: MetricsSecurityLevel::SECURITY_LEVEL_UNSPECIFIED,
+ // Default for bool is false (for key_upgraded field).
+ ..Default::default()
+ };
+
+ let mut key_operation_with_purpose_and_modes_info = KeyOperationWithPurposeAndModesInfo {
+ purpose: MetricsPurpose::KEY_PURPOSE_UNSPECIFIED,
+ // Default for i32 is 0 (for the remaining bitmap fields).
+ ..Default::default()
+ };
+
+ key_operation_with_general_info.security_level = process_security_level(sec_level);
+
+ key_operation_with_general_info.key_upgraded = key_upgraded;
+
+ key_operation_with_purpose_and_modes_info.purpose = match key_purpose {
+ KeyPurpose::ENCRYPT => MetricsPurpose::ENCRYPT,
+ KeyPurpose::DECRYPT => MetricsPurpose::DECRYPT,
+ KeyPurpose::SIGN => MetricsPurpose::SIGN,
+ KeyPurpose::VERIFY => MetricsPurpose::VERIFY,
+ KeyPurpose::WRAP_KEY => MetricsPurpose::WRAP_KEY,
+ KeyPurpose::AGREE_KEY => MetricsPurpose::AGREE_KEY,
+ KeyPurpose::ATTEST_KEY => MetricsPurpose::ATTEST_KEY,
+ _ => MetricsPurpose::KEY_PURPOSE_UNSPECIFIED,
+ };
+
+ key_operation_with_general_info.outcome = match op_outcome {
+ Outcome::Unknown | Outcome::Dropped => MetricsOutcome::DROPPED,
+ Outcome::Success => MetricsOutcome::SUCCESS,
+ Outcome::Abort => MetricsOutcome::ABORT,
+ Outcome::Pruned => MetricsOutcome::PRUNED,
+ Outcome::ErrorCode(e) => {
+ key_operation_with_general_info.error_code = e.0;
+ MetricsOutcome::ERROR
+ }
+ };
+
+ for key_param in op_params.iter().map(KsKeyParamValue::from) {
+ match key_param {
+ KsKeyParamValue::PaddingMode(p) => {
+ compute_padding_mode_bitmap(
+ &mut key_operation_with_purpose_and_modes_info.padding_mode_bitmap,
+ p,
+ );
+ }
+ KsKeyParamValue::Digest(d) => {
+ compute_digest_bitmap(
+ &mut key_operation_with_purpose_and_modes_info.digest_bitmap,
+ d,
+ );
+ }
+ KsKeyParamValue::BlockMode(b) => {
+ compute_block_mode_bitmap(
+ &mut key_operation_with_purpose_and_modes_info.block_mode_bitmap,
+ b,
+ );
+ }
+ _ => {}
+ }
+ }
+
+ (
+ KeystoreAtomPayload::KeyOperationWithGeneralInfo(key_operation_with_general_info),
+ KeystoreAtomPayload::KeyOperationWithPurposeAndModesInfo(
+ key_operation_with_purpose_and_modes_info,
+ ),
+ )
+}
+
+fn process_security_level(sec_level: SecurityLevel) -> MetricsSecurityLevel {
+ match sec_level {
+ SecurityLevel::SOFTWARE => MetricsSecurityLevel::SECURITY_LEVEL_SOFTWARE,
+ SecurityLevel::TRUSTED_ENVIRONMENT => {
+ MetricsSecurityLevel::SECURITY_LEVEL_TRUSTED_ENVIRONMENT
+ }
+ SecurityLevel::STRONGBOX => MetricsSecurityLevel::SECURITY_LEVEL_STRONGBOX,
+ SecurityLevel::KEYSTORE => MetricsSecurityLevel::SECURITY_LEVEL_KEYSTORE,
+ _ => MetricsSecurityLevel::SECURITY_LEVEL_UNSPECIFIED,
+ }
+}
+
+fn compute_padding_mode_bitmap(padding_mode_bitmap: &mut i32, padding_mode: PaddingMode) {
+ match padding_mode {
+ PaddingMode::NONE => {
+ *padding_mode_bitmap |= 1 << PaddingModeBitPosition::NONE_BIT_POSITION as i32;
+ }
+ PaddingMode::RSA_OAEP => {
+ *padding_mode_bitmap |= 1 << PaddingModeBitPosition::RSA_OAEP_BIT_POS as i32;
+ }
+ PaddingMode::RSA_PSS => {
+ *padding_mode_bitmap |= 1 << PaddingModeBitPosition::RSA_PSS_BIT_POS as i32;
+ }
+ PaddingMode::RSA_PKCS1_1_5_ENCRYPT => {
+ *padding_mode_bitmap |=
+ 1 << PaddingModeBitPosition::RSA_PKCS1_1_5_ENCRYPT_BIT_POS as i32;
+ }
+ PaddingMode::RSA_PKCS1_1_5_SIGN => {
+ *padding_mode_bitmap |= 1 << PaddingModeBitPosition::RSA_PKCS1_1_5_SIGN_BIT_POS as i32;
+ }
+ PaddingMode::PKCS7 => {
+ *padding_mode_bitmap |= 1 << PaddingModeBitPosition::PKCS7_BIT_POS as i32;
+ }
+ _ => {}
+ }
+}
+
+fn compute_digest_bitmap(digest_bitmap: &mut i32, digest: Digest) {
+ match digest {
+ Digest::NONE => {
+ *digest_bitmap |= 1 << DigestBitPosition::NONE_BIT_POSITION as i32;
+ }
+ Digest::MD5 => {
+ *digest_bitmap |= 1 << DigestBitPosition::MD5_BIT_POS as i32;
+ }
+ Digest::SHA1 => {
+ *digest_bitmap |= 1 << DigestBitPosition::SHA_1_BIT_POS as i32;
+ }
+ Digest::SHA_2_224 => {
+ *digest_bitmap |= 1 << DigestBitPosition::SHA_2_224_BIT_POS as i32;
+ }
+ Digest::SHA_2_256 => {
+ *digest_bitmap |= 1 << DigestBitPosition::SHA_2_256_BIT_POS as i32;
+ }
+ Digest::SHA_2_384 => {
+ *digest_bitmap |= 1 << DigestBitPosition::SHA_2_384_BIT_POS as i32;
+ }
+ Digest::SHA_2_512 => {
+ *digest_bitmap |= 1 << DigestBitPosition::SHA_2_512_BIT_POS as i32;
+ }
+ _ => {}
+ }
+}
+
+fn compute_block_mode_bitmap(block_mode_bitmap: &mut i32, block_mode: BlockMode) {
+ match block_mode {
+ BlockMode::ECB => {
+ *block_mode_bitmap |= 1 << BlockModeBitPosition::ECB_BIT_POS as i32;
+ }
+ BlockMode::CBC => {
+ *block_mode_bitmap |= 1 << BlockModeBitPosition::CBC_BIT_POS as i32;
+ }
+ BlockMode::CTR => {
+ *block_mode_bitmap |= 1 << BlockModeBitPosition::CTR_BIT_POS as i32;
+ }
+ BlockMode::GCM => {
+ *block_mode_bitmap |= 1 << BlockModeBitPosition::GCM_BIT_POS as i32;
+ }
+ _ => {}
+ }
+}
+
+fn compute_purpose_bitmap(purpose_bitmap: &mut i32, purpose: KeyPurpose) {
+ match purpose {
+ KeyPurpose::ENCRYPT => {
+ *purpose_bitmap |= 1 << KeyPurposeBitPosition::ENCRYPT_BIT_POS as i32;
+ }
+ KeyPurpose::DECRYPT => {
+ *purpose_bitmap |= 1 << KeyPurposeBitPosition::DECRYPT_BIT_POS as i32;
+ }
+ KeyPurpose::SIGN => {
+ *purpose_bitmap |= 1 << KeyPurposeBitPosition::SIGN_BIT_POS as i32;
+ }
+ KeyPurpose::VERIFY => {
+ *purpose_bitmap |= 1 << KeyPurposeBitPosition::VERIFY_BIT_POS as i32;
+ }
+ KeyPurpose::WRAP_KEY => {
+ *purpose_bitmap |= 1 << KeyPurposeBitPosition::WRAP_KEY_BIT_POS as i32;
+ }
+ KeyPurpose::AGREE_KEY => {
+ *purpose_bitmap |= 1 << KeyPurposeBitPosition::AGREE_KEY_BIT_POS as i32;
+ }
+ KeyPurpose::ATTEST_KEY => {
+ *purpose_bitmap |= 1 << KeyPurposeBitPosition::ATTEST_KEY_BIT_POS as i32;
+ }
+ _ => {}
+ }
+}
+
+fn pull_storage_stats() -> Result<Vec<KeystoreAtom>> {
+ let mut atom_vec: Vec<KeystoreAtom> = Vec::new();
+ let mut append = |stat| {
+ match stat {
+ Ok(s) => atom_vec.push(KeystoreAtom {
+ payload: KeystoreAtomPayload::StorageStats(s),
+ ..Default::default()
+ }),
+ Err(error) => {
+ log::error!("pull_metrics_callback: Error getting storage stat: {}", error)
+ }
+ };
+ };
+ DB.with(|db| {
+ let mut db = db.borrow_mut();
+ append(db.get_storage_stat(MetricsStorage::DATABASE));
+ append(db.get_storage_stat(MetricsStorage::KEY_ENTRY));
+ append(db.get_storage_stat(MetricsStorage::KEY_ENTRY_ID_INDEX));
+ append(db.get_storage_stat(MetricsStorage::KEY_ENTRY_DOMAIN_NAMESPACE_INDEX));
+ append(db.get_storage_stat(MetricsStorage::BLOB_ENTRY));
+ append(db.get_storage_stat(MetricsStorage::BLOB_ENTRY_KEY_ENTRY_ID_INDEX));
+ append(db.get_storage_stat(MetricsStorage::KEY_PARAMETER));
+ append(db.get_storage_stat(MetricsStorage::KEY_PARAMETER_KEY_ENTRY_ID_INDEX));
+ append(db.get_storage_stat(MetricsStorage::KEY_METADATA));
+ append(db.get_storage_stat(MetricsStorage::KEY_METADATA_KEY_ENTRY_ID_INDEX));
+ append(db.get_storage_stat(MetricsStorage::GRANT));
+ append(db.get_storage_stat(MetricsStorage::AUTH_TOKEN));
+ append(db.get_storage_stat(MetricsStorage::BLOB_METADATA));
+ append(db.get_storage_stat(MetricsStorage::BLOB_METADATA_BLOB_ENTRY_ID_INDEX));
+ });
+ Ok(atom_vec)
+}
+
+fn pull_attestation_pool_stats() -> Result<Vec<KeystoreAtom>> {
+ let mut atoms = Vec::<KeystoreAtom>::new();
+ for sec_level in &[SecurityLevel::TRUSTED_ENVIRONMENT, SecurityLevel::STRONGBOX] {
+ let expired_by = SystemTime::now()
+ .duration_since(UNIX_EPOCH)
+ .unwrap_or_else(|_| Duration::new(0, 0))
+ .as_secs() as i64;
+
+ let result = get_pool_status(expired_by, *sec_level);
+
+ if let Ok(pool_status) = result {
+ let rkp_pool_stats = RkpPoolStats {
+ security_level: process_security_level(*sec_level),
+ expiring: pool_status.expiring,
+ unassigned: pool_status.unassigned,
+ attested: pool_status.attested,
+ total: pool_status.total,
+ };
+ atoms.push(KeystoreAtom {
+ payload: KeystoreAtomPayload::RkpPoolStats(rkp_pool_stats),
+ ..Default::default()
+ });
+ } else {
+ log::error!(
+ concat!(
+ "In pull_attestation_pool_stats: Failed to retrieve pool status",
+ " for security level: {:?}"
+ ),
+ sec_level
+ );
+ }
+ }
+ Ok(atoms)
+}
+
+/// Log error events related to Remote Key Provisioning (RKP).
+pub fn log_rkp_error_stats(rkp_error: MetricsRkpError) {
+ let rkp_error_stats = KeystoreAtomPayload::RkpErrorStats(RkpErrorStats { rkpError: rkp_error });
+ METRICS_STORE.insert_atom(AtomID::RKP_ERROR_STATS, rkp_error_stats);
+}
+
+/// This function tries to read and update the system property: keystore.crash_count.
+/// If the property is absent, it sets the property with value 0. If the property is present, it
+/// increments the value. This helps tracking keystore crashes internally.
+pub fn update_keystore_crash_sysprop() {
+ let crash_count = read_keystore_crash_count();
+ let new_count = match crash_count {
+ Ok(count) => count + 1,
+ Err(error) => {
+ // If the property is absent, this is the first start up during the boot.
+ // Proceed to write the system property with value 0. Otherwise, log and return.
+ if !matches!(
+ error.root_cause().downcast_ref::<PropertyWatcherError>(),
+ Some(PropertyWatcherError::SystemPropertyAbsent)
+ ) {
+ log::warn!(
+ concat!(
+ "In update_keystore_crash_sysprop: ",
+ "Failed to read the existing system property due to: {:?}.",
+ "Therefore, keystore crashes will not be logged."
+ ),
+ error
+ );
+ return;
+ }
+ 0
+ }
+ };
+
+ if let Err(e) =
+ rustutils::system_properties::write(KEYSTORE_CRASH_COUNT_PROPERTY, &new_count.to_string())
+ {
+ log::error!(
+ concat!(
+ "In update_keystore_crash_sysprop:: ",
+ "Failed to write the system property due to error: {:?}"
+ ),
+ e
+ );
+ }
+}
+
+/// Read the system property: keystore.crash_count.
+pub fn read_keystore_crash_count() -> Result<i32> {
+ rustutils::system_properties::read("keystore.crash_count")
+ .context("In read_keystore_crash_count: Failed read property.")?
+ .parse::<i32>()
+ .map_err(std::convert::Into::into)
+}
+
+/// Enum defining the bit position for each padding mode. Since padding mode can be repeatable, it
+/// is represented using a bitmap.
+#[allow(non_camel_case_types)]
+#[repr(i32)]
+enum PaddingModeBitPosition {
+ ///Bit position in the PaddingMode bitmap for NONE.
+ NONE_BIT_POSITION = 0,
+ ///Bit position in the PaddingMode bitmap for RSA_OAEP.
+ RSA_OAEP_BIT_POS = 1,
+ ///Bit position in the PaddingMode bitmap for RSA_PSS.
+ RSA_PSS_BIT_POS = 2,
+ ///Bit position in the PaddingMode bitmap for RSA_PKCS1_1_5_ENCRYPT.
+ RSA_PKCS1_1_5_ENCRYPT_BIT_POS = 3,
+ ///Bit position in the PaddingMode bitmap for RSA_PKCS1_1_5_SIGN.
+ RSA_PKCS1_1_5_SIGN_BIT_POS = 4,
+ ///Bit position in the PaddingMode bitmap for RSA_PKCS7.
+ PKCS7_BIT_POS = 5,
+}
+
+/// Enum defining the bit position for each digest type. Since digest can be repeatable in
+/// key parameters, it is represented using a bitmap.
+#[allow(non_camel_case_types)]
+#[repr(i32)]
+enum DigestBitPosition {
+ ///Bit position in the Digest bitmap for NONE.
+ NONE_BIT_POSITION = 0,
+ ///Bit position in the Digest bitmap for MD5.
+ MD5_BIT_POS = 1,
+ ///Bit position in the Digest bitmap for SHA1.
+ SHA_1_BIT_POS = 2,
+ ///Bit position in the Digest bitmap for SHA_2_224.
+ SHA_2_224_BIT_POS = 3,
+ ///Bit position in the Digest bitmap for SHA_2_256.
+ SHA_2_256_BIT_POS = 4,
+ ///Bit position in the Digest bitmap for SHA_2_384.
+ SHA_2_384_BIT_POS = 5,
+ ///Bit position in the Digest bitmap for SHA_2_512.
+ SHA_2_512_BIT_POS = 6,
+}
+
+/// Enum defining the bit position for each block mode type. Since block mode can be repeatable in
+/// key parameters, it is represented using a bitmap.
+#[allow(non_camel_case_types)]
+#[repr(i32)]
+enum BlockModeBitPosition {
+ ///Bit position in the BlockMode bitmap for ECB.
+ ECB_BIT_POS = 1,
+ ///Bit position in the BlockMode bitmap for CBC.
+ CBC_BIT_POS = 2,
+ ///Bit position in the BlockMode bitmap for CTR.
+ CTR_BIT_POS = 3,
+ ///Bit position in the BlockMode bitmap for GCM.
+ GCM_BIT_POS = 4,
+}
+
+/// Enum defining the bit position for each key purpose. Since key purpose can be repeatable in
+/// key parameters, it is represented using a bitmap.
+#[allow(non_camel_case_types)]
+#[repr(i32)]
+enum KeyPurposeBitPosition {
+ ///Bit position in the KeyPurpose bitmap for Encrypt.
+ ENCRYPT_BIT_POS = 1,
+ ///Bit position in the KeyPurpose bitmap for Decrypt.
+ DECRYPT_BIT_POS = 2,
+ ///Bit position in the KeyPurpose bitmap for Sign.
+ SIGN_BIT_POS = 3,
+ ///Bit position in the KeyPurpose bitmap for Verify.
+ VERIFY_BIT_POS = 4,
+ ///Bit position in the KeyPurpose bitmap for Wrap Key.
+ WRAP_KEY_BIT_POS = 5,
+ ///Bit position in the KeyPurpose bitmap for Agree Key.
+ AGREE_KEY_BIT_POS = 6,
+ ///Bit position in the KeyPurpose bitmap for Attest Key.
+ ATTEST_KEY_BIT_POS = 7,
+}
diff --git a/keystore2/src/operation.rs b/keystore2/src/operation.rs
index c98a76b..7e08f4e 100644
--- a/keystore2/src/operation.rs
+++ b/keystore2/src/operation.rs
@@ -126,18 +126,18 @@
//! Either way, we have to revaluate the pruning scores.
use crate::enforcements::AuthInfo;
-use crate::error::{map_km_error, map_or_log_err, Error, ErrorCode, ResponseCode};
-use crate::utils::Asp;
+use crate::error::{map_err_with, map_km_error, map_or_log_err, Error, ErrorCode, ResponseCode};
+use crate::metrics_store::log_key_operation_event_stats;
+use crate::utils::watchdog as wd;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- ByteArray::ByteArray, IKeyMintOperation::IKeyMintOperation,
- KeyParameter::KeyParameter as KmParam, KeyParameterArray::KeyParameterArray,
- KeyParameterValue::KeyParameterValue as KmParamValue, Tag::Tag,
+ IKeyMintOperation::IKeyMintOperation, KeyParameter::KeyParameter, KeyPurpose::KeyPurpose,
+ SecurityLevel::SecurityLevel,
};
+use android_hardware_security_keymint::binder::{BinderFeatures, Strong};
use android_system_keystore2::aidl::android::system::keystore2::{
IKeystoreOperation::BnKeystoreOperation, IKeystoreOperation::IKeystoreOperation,
};
use anyhow::{anyhow, Context, Result};
-use binder::IBinder;
use std::{
collections::HashMap,
sync::{Arc, Mutex, MutexGuard, Weak},
@@ -149,12 +149,18 @@
/// to one of the other variants exactly once. The distinction in outcome is mainly
/// for the statistic.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
-enum Outcome {
+pub enum Outcome {
+ /// Operations have `Outcome::Unknown` as long as they are active.
Unknown,
+ /// Operation is successful.
Success,
+ /// Operation is aborted.
Abort,
+ /// Operation is dropped.
Dropped,
+ /// Operation is pruned.
Pruned,
+ /// Operation is failed with the error code.
ErrorCode(ErrorCode),
}
@@ -164,17 +170,41 @@
pub struct Operation {
// The index of this operation in the OperationDb.
index: usize,
- km_op: Asp,
+ km_op: Strong<dyn IKeyMintOperation>,
last_usage: Mutex<Instant>,
outcome: Mutex<Outcome>,
owner: u32, // Uid of the operation's owner.
auth_info: Mutex<AuthInfo>,
+ forced: bool,
+ logging_info: LoggingInfo,
+}
+
+/// Keeps track of the information required for logging operations.
+#[derive(Debug)]
+pub struct LoggingInfo {
+ sec_level: SecurityLevel,
+ purpose: KeyPurpose,
+ op_params: Vec<KeyParameter>,
+ key_upgraded: bool,
+}
+
+impl LoggingInfo {
+ /// Constructor
+ pub fn new(
+ sec_level: SecurityLevel,
+ purpose: KeyPurpose,
+ op_params: Vec<KeyParameter>,
+ key_upgraded: bool,
+ ) -> LoggingInfo {
+ Self { sec_level, purpose, op_params, key_upgraded }
+ }
}
struct PruningInfo {
last_usage: Instant,
owner: u32,
index: usize,
+ forced: bool,
}
// We don't except more than 32KiB of data in `update`, `updateAad`, and `finish`.
@@ -187,14 +217,18 @@
km_op: binder::Strong<dyn IKeyMintOperation>,
owner: u32,
auth_info: AuthInfo,
+ forced: bool,
+ logging_info: LoggingInfo,
) -> Self {
Self {
index,
- km_op: Asp::new(km_op.as_binder()),
+ km_op,
last_usage: Mutex::new(Instant::now()),
outcome: Mutex::new(Outcome::Unknown),
owner,
auth_info: Mutex::new(auth_info),
+ forced,
+ logging_info,
}
}
@@ -220,6 +254,7 @@
last_usage: *self.last_usage.lock().expect("In get_pruning_info."),
owner: self.owner,
index: self.index,
+ forced: self.forced,
})
}
@@ -247,17 +282,10 @@
}
*locked_outcome = Outcome::Pruned;
- let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
- match self.km_op.get_interface() {
- Ok(km_op) => km_op,
- Err(e) => {
- log::error!("In prune: Failed to get KeyMintOperation interface.\n {:?}", e);
- return Err(Error::sys());
- }
- };
+ let _wp = wd::watch_millis("In Operation::prune: calling abort()", 500);
// We abort the operation. If there was an error we log it but ignore it.
- if let Err(e) = map_km_error(km_op.abort()) {
+ if let Err(e) = map_km_error(self.km_op.abort()) {
log::error!("In prune: KeyMint::abort failed with {:?}.", e);
}
@@ -325,19 +353,6 @@
Self::check_input_length(aad_input).context("In update_aad")?;
self.touch();
- let params = KeyParameterArray {
- params: vec![KmParam {
- tag: Tag::ASSOCIATED_DATA,
- value: KmParamValue::Blob(aad_input.into()),
- }],
- };
-
- let mut out_params: Option<KeyParameterArray> = None;
- let mut output: Option<ByteArray> = None;
-
- let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
- self.km_op.get_interface().context("In update: Failed to get KeyMintOperation.")?;
-
let (hat, tst) = self
.auth_info
.lock()
@@ -345,17 +360,10 @@
.before_update()
.context("In update_aad: Trying to get auth tokens.")?;
- self.update_outcome(
- &mut *outcome,
- map_km_error(km_op.update(
- Some(¶ms),
- None,
- hat.as_ref(),
- tst.as_ref(),
- &mut out_params,
- &mut output,
- )),
- )
+ self.update_outcome(&mut *outcome, {
+ let _wp = wd::watch_millis("Operation::update_aad: calling updateAad", 500);
+ map_km_error(self.km_op.updateAad(aad_input, hat.as_ref(), tst.as_ref()))
+ })
.context("In update_aad: KeyMint::update failed.")?;
Ok(())
@@ -368,11 +376,6 @@
Self::check_input_length(input).context("In update")?;
self.touch();
- let mut out_params: Option<KeyParameterArray> = None;
-
- let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
- self.km_op.get_interface().context("In update: Failed to get KeyMintOperation.")?;
-
let (hat, tst) = self
.auth_info
.lock()
@@ -380,39 +383,17 @@
.before_update()
.context("In update: Trying to get auth tokens.")?;
- let mut result: Option<Vec<u8>> = None;
- let mut consumed = 0usize;
- loop {
- let mut output: Option<ByteArray> = None;
- consumed += self
- .update_outcome(
- &mut *outcome,
- map_km_error(km_op.update(
- None,
- Some(&input[consumed..]),
- hat.as_ref(),
- tst.as_ref(),
- &mut out_params,
- &mut output,
- )),
- )
- .context("In update: KeyMint::update failed.")? as usize;
+ let output = self
+ .update_outcome(&mut *outcome, {
+ let _wp = wd::watch_millis("Operation::update: calling update", 500);
+ map_km_error(self.km_op.update(input, hat.as_ref(), tst.as_ref()))
+ })
+ .context("In update: KeyMint::update failed.")?;
- match (output, &mut result) {
- (Some(blob), None) => {
- if !blob.data.is_empty() {
- result = Some(blob.data)
- }
- }
- (Some(mut blob), Some(ref mut result)) => {
- result.append(&mut blob.data);
- }
- (None, _) => {}
- }
-
- if consumed == input.len() {
- return Ok(result);
- }
+ if output.is_empty() {
+ Ok(None)
+ } else {
+ Ok(Some(output))
}
}
@@ -425,11 +406,6 @@
}
self.touch();
- let mut out_params: Option<KeyParameterArray> = None;
-
- let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
- self.km_op.get_interface().context("In finish: Failed to get KeyMintOperation.")?;
-
let (hat, tst, confirmation_token) = self
.auth_info
.lock()
@@ -437,25 +413,17 @@
.before_finish()
.context("In finish: Trying to get auth tokens.")?;
- let in_params = confirmation_token.map(|token| KeyParameterArray {
- params: vec![KmParam {
- tag: Tag::CONFIRMATION_TOKEN,
- value: KmParamValue::Blob(token),
- }],
- });
-
let output = self
- .update_outcome(
- &mut *outcome,
- map_km_error(km_op.finish(
- in_params.as_ref(),
+ .update_outcome(&mut *outcome, {
+ let _wp = wd::watch_millis("Operation::finish: calling finish", 500);
+ map_km_error(self.km_op.finish(
input,
signature,
hat.as_ref(),
tst.as_ref(),
- &mut out_params,
- )),
- )
+ confirmation_token.as_deref(),
+ ))
+ })
.context("In finish: KeyMint::finish failed.")?;
self.auth_info.lock().unwrap().after_finish().context("In finish.")?;
@@ -476,16 +444,26 @@
fn abort(&self, outcome: Outcome) -> Result<()> {
let mut locked_outcome = self.check_active().context("In abort")?;
*locked_outcome = outcome;
- let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
- self.km_op.get_interface().context("In abort: Failed to get KeyMintOperation.")?;
- map_km_error(km_op.abort()).context("In abort: KeyMint::abort failed.")
+ {
+ let _wp = wd::watch_millis("Operation::abort: calling abort", 500);
+ map_km_error(self.km_op.abort()).context("In abort: KeyMint::abort failed.")
+ }
}
}
impl Drop for Operation {
fn drop(&mut self) {
- if let Ok(Outcome::Unknown) = self.outcome.get_mut() {
+ let guard = self.outcome.lock().expect("In drop.");
+ log_key_operation_event_stats(
+ self.logging_info.sec_level,
+ self.logging_info.purpose,
+ &(self.logging_info.op_params),
+ &guard,
+ self.logging_info.key_upgraded,
+ );
+ if let Outcome::Unknown = *guard {
+ drop(guard);
// If the operation was still active we call abort, setting
// the outcome to `Outcome::Dropped`
if let Err(e) = self.abort(Outcome::Dropped) {
@@ -518,6 +496,8 @@
km_op: binder::public_api::Strong<dyn IKeyMintOperation>,
owner: u32,
auth_info: AuthInfo,
+ forced: bool,
+ logging_info: LoggingInfo,
) -> Arc<Operation> {
// We use unwrap because we don't allow code that can panic while locked.
let mut operations = self.operations.lock().expect("In create_operation.");
@@ -530,12 +510,26 @@
s.upgrade().is_none()
}) {
Some(free_slot) => {
- let new_op = Arc::new(Operation::new(index - 1, km_op, owner, auth_info));
+ let new_op = Arc::new(Operation::new(
+ index - 1,
+ km_op,
+ owner,
+ auth_info,
+ forced,
+ logging_info,
+ ));
*free_slot = Arc::downgrade(&new_op);
new_op
}
None => {
- let new_op = Arc::new(Operation::new(operations.len(), km_op, owner, auth_info));
+ let new_op = Arc::new(Operation::new(
+ operations.len(),
+ km_op,
+ owner,
+ auth_info,
+ forced,
+ logging_info,
+ ));
operations.push(Arc::downgrade(&new_op));
new_op
}
@@ -618,7 +612,7 @@
/// ## Update
/// We also allow callers to cannibalize their own sibling operations if no other
/// slot can be found. In this case the least recently used sibling is pruned.
- pub fn prune(&self, caller: u32) -> Result<(), Error> {
+ pub fn prune(&self, caller: u32, forced: bool) -> Result<(), Error> {
loop {
// Maps the uid of the owner to the number of operations that owner has
// (running_siblings). More operations per owner lowers the pruning
@@ -643,7 +637,8 @@
}
});
- let caller_malus = 1u64 + *owners.entry(caller).or_default();
+ // If the operation is forced, the caller has a malus of 0.
+ let caller_malus = if forced { 0 } else { 1u64 + *owners.entry(caller).or_default() };
// We iterate through all operations computing the malus and finding
// the candidate with the highest malus which must also be higher
@@ -657,7 +652,7 @@
let mut oldest_caller_op: Option<CandidateInfo> = None;
let candidate = pruning_info.iter().fold(
None,
- |acc: Option<CandidateInfo>, &PruningInfo { last_usage, owner, index }| {
+ |acc: Option<CandidateInfo>, &PruningInfo { last_usage, owner, index, forced }| {
// Compute the age of the current operation.
let age = now
.checked_duration_since(last_usage)
@@ -677,12 +672,17 @@
}
// Compute the malus of the current operation.
- // Expect safety: Every owner in pruning_info was counted in
- // the owners map. So this unwrap cannot panic.
- let malus = *owners
- .get(&owner)
- .expect("This is odd. We should have counted every owner in pruning_info.")
- + ((age.as_secs() + 1) as f64).log(6.0).floor() as u64;
+ let malus = if forced {
+ // Forced operations have a malus of 0. And cannot even be pruned
+ // by other forced operations.
+ 0
+ } else {
+ // Expect safety: Every owner in pruning_info was counted in
+ // the owners map. So this unwrap cannot panic.
+ *owners.get(&owner).expect(
+ "This is odd. We should have counted every owner in pruning_info.",
+ ) + ((age.as_secs() + 1) as f64).log(6.0).floor() as u64
+ };
// Now check if the current operation is a viable/better candidate
// the one currently stored in the accumulator.
@@ -768,16 +768,16 @@
impl KeystoreOperation {
/// Creates a new operation instance wrapped in a
- /// BnKeystoreOperation proxy object. It also
- /// calls `IBinder::set_requesting_sid` on the new interface, because
+ /// BnKeystoreOperation proxy object. It also enables
+ /// `BinderFeatures::set_requesting_sid` on the new interface, because
/// we need it for checking Keystore permissions.
pub fn new_native_binder(
operation: Arc<Operation>,
) -> binder::public_api::Strong<dyn IKeystoreOperation> {
- let result =
- BnKeystoreOperation::new_binder(Self { operation: Mutex::new(Some(operation)) });
- result.as_binder().set_requesting_sid(true);
- result
+ BnKeystoreOperation::new_binder(
+ Self { operation: Mutex::new(Some(operation)) },
+ BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
+ )
}
/// Grabs the outer operation mutex and calls `f` on the locked operation.
@@ -822,6 +822,7 @@
impl IKeystoreOperation for KeystoreOperation {
fn updateAad(&self, aad_input: &[u8]) -> binder::public_api::Result<()> {
+ let _wp = wd::watch_millis("IKeystoreOperation::updateAad", 500);
map_or_log_err(
self.with_locked_operation(
|op| op.update_aad(aad_input).context("In KeystoreOperation::updateAad"),
@@ -832,6 +833,7 @@
}
fn update(&self, input: &[u8]) -> binder::public_api::Result<Option<Vec<u8>>> {
+ let _wp = wd::watch_millis("IKeystoreOperation::update", 500);
map_or_log_err(
self.with_locked_operation(
|op| op.update(input).context("In KeystoreOperation::update"),
@@ -845,6 +847,7 @@
input: Option<&[u8]>,
signature: Option<&[u8]>,
) -> binder::public_api::Result<Option<Vec<u8>>> {
+ let _wp = wd::watch_millis("IKeystoreOperation::finish", 500);
map_or_log_err(
self.with_locked_operation(
|op| op.finish(input, signature).context("In KeystoreOperation::finish"),
@@ -855,11 +858,22 @@
}
fn abort(&self) -> binder::public_api::Result<()> {
- map_or_log_err(
+ let _wp = wd::watch_millis("IKeystoreOperation::abort", 500);
+ map_err_with(
self.with_locked_operation(
|op| op.abort(Outcome::Abort).context("In KeystoreOperation::abort"),
true,
),
+ |e| {
+ match e.root_cause().downcast_ref::<Error>() {
+ // Calling abort on expired operations is something very common.
+ // There is no reason to clutter the log with it. It is never the cause
+ // for a true problem.
+ Some(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)) => {}
+ _ => log::error!("{:?}", e),
+ };
+ e
+ },
Ok,
)
}
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
index 576ac3f..4392acf 100644
--- a/keystore2/src/permission.rs
+++ b/keystore2/src/permission.rs
@@ -149,16 +149,16 @@
}
}
- impl Into<$aidl_name> for $name {
- fn into(self) -> $aidl_name {
- self.0
+ impl From<$name> for $aidl_name {
+ fn from(p: $name) -> $aidl_name {
+ p.0
}
}
impl $name {
/// Returns a string representation of the permission as required by
/// `selinux::check_access`.
- pub fn to_selinux(&self) -> &'static str {
+ pub fn to_selinux(self) -> &'static str {
match self {
Self($aidl_name::$def_name) => stringify!($def_selinux_name),
$(Self($aidl_name::$element_name) => stringify!($selinux_name),)*
@@ -193,6 +193,7 @@
/// ```
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
KeyPerm from KeyPermission with default (NONE, none) {
+ CONVERT_STORAGE_KEY_TO_EPHEMERAL, selinux name: convert_storage_key_to_ephemeral;
DELETE, selinux name: delete;
GEN_UNIQUE_ID, selinux name: gen_unique_id;
GET_INFO, selinux name: get_info;
@@ -256,16 +257,16 @@
}
}
- impl Into<i32> for $name {
- fn into(self) -> i32 {
- self as i32
+ impl From<$name> for i32 {
+ fn from(p: $name) -> i32 {
+ p as i32
}
}
impl $name {
/// Returns a string representation of the permission as required by
/// `selinux::check_access`.
- pub fn to_selinux(&self) -> &'static str {
+ pub fn to_selinux(self) -> &'static str {
match self {
Self::$def_name => stringify!($def_selinux_name),
$(Self::$element_name => stringify!($selinux_name),)*
@@ -291,7 +292,7 @@
AddAuth = 1, selinux name: add_auth;
/// Checked when an app is uninstalled or wiped.
ClearNs = 2, selinux name: clear_ns;
- /// Checked when Keystore 2.0 gets locked.
+ /// Checked when the user state is queried from Keystore 2.0.
GetState = 4, selinux name: get_state;
/// Checked when Keystore 2.0 is asked to list a namespace that the caller
/// does not have the get_info permission for.
@@ -308,6 +309,16 @@
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;
+ /// Checked when earlyBootEnded() is called.
+ EarlyBootEnded = 0x800, selinux name: early_boot_ended;
+ /// Checked when IKeystoreMaintenance::onDeviceOffBody is called.
+ ReportOffBody = 0x1000, selinux name: report_off_body;
+ /// Checked when IkeystoreMetrics::pullMetris is called.
+ PullMetrics = 0x2000, selinux name: pull_metrics;
+ /// Checked when IKeystoreMaintenance::deleteAllKeys is called.
+ DeleteAllKeys = 0x4000, selinux name: delete_all_keys;
}
);
@@ -582,6 +593,7 @@
KeyPerm::rebind(),
KeyPerm::update(),
KeyPerm::use_(),
+ KeyPerm::convert_storage_key_to_ephemeral(),
];
const SYSTEM_SERVER_PERMISSIONS_NO_GRANT: KeyPermSet = key_perm_set![
@@ -605,6 +617,7 @@
KeyPerm::rebind(),
KeyPerm::update(),
KeyPerm::use_(),
+ KeyPerm::convert_storage_key_to_ephemeral(),
];
const UNPRIV_PERMS: KeyPermSet = key_perm_set![
@@ -673,7 +686,7 @@
let shell_ctx = Context::new("u:r:shell:s0")?;
assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::add_auth()));
assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::clear_ns()));
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::get_state()));
+ assert!(check_keystore_permission(&shell_ctx, KeystorePerm::get_state()).is_ok());
assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::list()));
assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::lock()));
assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::reset()));
@@ -841,23 +854,19 @@
blob: None,
};
+ assert!(check_key_permission(0, &sctx, KeyPerm::use_(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::delete(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::get_info(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::rebind(), &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::update(), &key, &None).is_ok());
+
if is_su {
- assert!(check_key_permission(0, &sctx, KeyPerm::use_(), &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::delete(), &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::get_info(), &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::rebind(), &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::update(), &key, &None).is_ok());
assert!(check_key_permission(0, &sctx, KeyPerm::grant(), &key, &None).is_ok());
assert!(check_key_permission(0, &sctx, KeyPerm::manage_blob(), &key, &None).is_ok());
assert!(check_key_permission(0, &sctx, KeyPerm::use_dev_id(), &key, &None).is_ok());
assert!(check_key_permission(0, &sctx, KeyPerm::gen_unique_id(), &key, &None).is_ok());
assert!(check_key_permission(0, &sctx, KeyPerm::req_forced_op(), &key, &None).is_ok());
} else {
- assert!(check_key_permission(0, &sctx, KeyPerm::use_(), &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::delete(), &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::get_info(), &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::rebind(), &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::update(), &key, &None).is_ok());
assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::grant(), &key, &None));
assert_perm_failed!(check_key_permission(
0,
diff --git a/keystore2/src/raw_device.rs b/keystore2/src/raw_device.rs
new file mode 100644
index 0000000..991535f
--- /dev/null
+++ b/keystore2/src/raw_device.rs
@@ -0,0 +1,333 @@
+// 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.
+
+//! Provide the [`KeyMintDevice`] wrapper for operating directly on a KeyMint device.
+
+use crate::{
+ database::{
+ BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, KeyEntry, KeyEntryLoadBits,
+ KeyIdGuard, KeyMetaData, KeyMetaEntry, KeyType, KeystoreDB, SubComponentType, Uuid,
+ },
+ error::{map_km_error, Error, ErrorCode},
+ globals::get_keymint_device,
+ super_key::KeyBlob,
+ utils::{key_characteristics_to_internal, watchdog as wd, AID_KEYSTORE},
+};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ HardwareAuthToken::HardwareAuthToken, IKeyMintDevice::IKeyMintDevice,
+ IKeyMintOperation::IKeyMintOperation, KeyCharacteristics::KeyCharacteristics,
+ KeyCreationResult::KeyCreationResult, KeyParameter::KeyParameter, KeyPurpose::KeyPurpose,
+ SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
+};
+use anyhow::{Context, Result};
+use binder::Strong;
+
+/// Wrapper for operating directly on a KeyMint device.
+/// These methods often mirror methods in [`crate::security_level`]. However
+/// the functions in [`crate::security_level`] make assumptions that hold, and has side effects
+/// that make sense, only if called by an external client through binder.
+/// In addition we are trying to maintain a separation between interface services
+/// so that the architecture is compatible with a future move to multiple thread pools.
+/// So the simplest approach today is to write new implementations of them for internal use.
+/// Because these methods run very early, we don't even try to cooperate with
+/// the operation slot database; we assume there will be plenty of slots.
+pub struct KeyMintDevice {
+ km_dev: Strong<dyn IKeyMintDevice>,
+ km_uuid: Uuid,
+ version: i32,
+ security_level: SecurityLevel,
+}
+
+impl KeyMintDevice {
+ /// Version number of KeyMasterDevice@V4_0
+ pub const KEY_MASTER_V4_0: i32 = 40;
+ /// Version number of KeyMasterDevice@V4_1
+ pub const KEY_MASTER_V4_1: i32 = 41;
+ /// Version number of KeyMintDevice@V1
+ pub const KEY_MINT_V1: i32 = 100;
+
+ /// Get a [`KeyMintDevice`] for the given [`SecurityLevel`]
+ pub fn get(security_level: SecurityLevel) -> Result<KeyMintDevice> {
+ let (km_dev, hw_info, km_uuid) = get_keymint_device(&security_level)
+ .context("In KeyMintDevice::get: get_keymint_device failed")?;
+
+ Ok(KeyMintDevice {
+ km_dev,
+ km_uuid,
+ version: hw_info.versionNumber,
+ security_level: hw_info.securityLevel,
+ })
+ }
+
+ /// Get a [`KeyMintDevice`] for the given [`SecurityLevel`], return
+ /// [`None`] if the error `HARDWARE_TYPE_UNAVAILABLE` is returned
+ pub fn get_or_none(security_level: SecurityLevel) -> Result<Option<KeyMintDevice>> {
+ KeyMintDevice::get(security_level).map(Some).or_else(|e| {
+ match e.root_cause().downcast_ref::<Error>() {
+ Some(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)) => Ok(None),
+ _ => Err(e),
+ }
+ })
+ }
+
+ /// Returns the version of the underlying KeyMint/KeyMaster device.
+ pub fn version(&self) -> i32 {
+ self.version
+ }
+
+ /// Returns the self advertised security level of the KeyMint device.
+ /// This may differ from the requested security level if the best security level
+ /// on the device is Software.
+ pub fn security_level(&self) -> SecurityLevel {
+ self.security_level
+ }
+
+ /// Create a KM key and store in the database.
+ pub fn create_and_store_key<F>(
+ &self,
+ db: &mut KeystoreDB,
+ key_desc: &KeyDescriptor,
+ key_type: KeyType,
+ creator: F,
+ ) -> Result<()>
+ where
+ F: FnOnce(&Strong<dyn IKeyMintDevice>) -> Result<KeyCreationResult, binder::Status>,
+ {
+ let creation_result = map_km_error(creator(&self.km_dev))
+ .context("In create_and_store_key: creator failed")?;
+ let key_parameters = key_characteristics_to_internal(creation_result.keyCharacteristics);
+
+ let creation_date =
+ DateTime::now().context("In create_and_store_key: DateTime::now() failed")?;
+
+ let mut key_metadata = KeyMetaData::new();
+ key_metadata.add(KeyMetaEntry::CreationDate(creation_date));
+ let mut blob_metadata = BlobMetaData::new();
+ blob_metadata.add(BlobMetaEntry::KmUuid(self.km_uuid));
+
+ db.store_new_key(
+ key_desc,
+ key_type,
+ &key_parameters,
+ &(&creation_result.keyBlob, &blob_metadata),
+ &CertificateInfo::new(None, None),
+ &key_metadata,
+ &self.km_uuid,
+ )
+ .context("In create_and_store_key: store_new_key failed")?;
+ Ok(())
+ }
+
+ /// Generate a KeyDescriptor for internal-use keys.
+ pub fn internal_descriptor(alias: String) -> KeyDescriptor {
+ KeyDescriptor {
+ domain: Domain::APP,
+ nspace: AID_KEYSTORE as i64,
+ alias: Some(alias),
+ blob: None,
+ }
+ }
+
+ /// Look up an internal-use key in the database given a key descriptor.
+ fn lookup_from_desc(
+ db: &mut KeystoreDB,
+ key_desc: &KeyDescriptor,
+ key_type: KeyType,
+ ) -> Result<(KeyIdGuard, KeyEntry)> {
+ db.load_key_entry(key_desc, key_type, KeyEntryLoadBits::KM, AID_KEYSTORE, |_, _| Ok(()))
+ .context("In lookup_from_desc: load_key_entry failed.")
+ }
+
+ /// Look up the key in the database, and return None if it is absent.
+ fn not_found_is_none(
+ lookup: Result<(KeyIdGuard, KeyEntry)>,
+ ) -> Result<Option<(KeyIdGuard, KeyEntry)>> {
+ match lookup {
+ Ok(result) => Ok(Some(result)),
+ Err(e) => match e.root_cause().downcast_ref::<Error>() {
+ Some(&Error::Rc(ResponseCode::KEY_NOT_FOUND)) => Ok(None),
+ _ => Err(e),
+ },
+ }
+ }
+
+ /// This does the lookup and store in separate transactions; caller must
+ /// hold a lock before calling.
+ pub fn lookup_or_generate_key<F>(
+ &self,
+ db: &mut KeystoreDB,
+ key_desc: &KeyDescriptor,
+ key_type: KeyType,
+ params: &[KeyParameter],
+ validate_characteristics: F,
+ ) -> Result<(KeyIdGuard, KeyBlob)>
+ where
+ F: FnOnce(&[KeyCharacteristics]) -> bool,
+ {
+ // We use a separate transaction for the lookup than for the store
+ // - to keep the code simple
+ // - because the caller needs to hold a lock in any case
+ // - because it avoids holding database locks during slow
+ // KeyMint operations
+ let lookup = Self::not_found_is_none(Self::lookup_from_desc(db, key_desc, key_type))
+ .context("In lookup_or_generate_key: first lookup failed")?;
+
+ if let Some((key_id_guard, mut key_entry)) = lookup {
+ // If the key is associated with a different km instance
+ // or if there is no blob metadata for some reason the key entry
+ // is considered corrupted and needs to be replaced with a new one.
+ let key_blob = key_entry.take_key_blob_info().and_then(|(key_blob, blob_metadata)| {
+ if Some(&self.km_uuid) == blob_metadata.km_uuid() {
+ Some(key_blob)
+ } else {
+ None
+ }
+ });
+
+ if let Some(key_blob_vec) = key_blob {
+ let (key_characteristics, key_blob) = self
+ .upgrade_keyblob_if_required_with(
+ db,
+ &key_id_guard,
+ KeyBlob::NonSensitive(key_blob_vec),
+ |key_blob| {
+ map_km_error({
+ let _wp = wd::watch_millis(
+ concat!(
+ "In KeyMintDevice::lookup_or_generate_key: ",
+ "calling getKeyCharacteristics."
+ ),
+ 500,
+ );
+ self.km_dev.getKeyCharacteristics(key_blob, &[], &[])
+ })
+ },
+ )
+ .context("In lookup_or_generate_key: calling getKeyCharacteristics")?;
+
+ if validate_characteristics(&key_characteristics) {
+ return Ok((key_id_guard, key_blob));
+ }
+
+ // If this point is reached the existing key is considered outdated or corrupted
+ // in some way. It will be replaced with a new key below.
+ };
+ }
+
+ self.create_and_store_key(db, key_desc, key_type, |km_dev| {
+ km_dev.generateKey(params, None)
+ })
+ .context("In lookup_or_generate_key: generate_and_store_key failed")?;
+ Self::lookup_from_desc(db, key_desc, key_type)
+ .and_then(|(key_id_guard, mut key_entry)| {
+ Ok((
+ key_id_guard,
+ key_entry
+ .take_key_blob_info()
+ .ok_or(Error::Rc(ResponseCode::KEY_NOT_FOUND))
+ .map(|(key_blob, _)| KeyBlob::NonSensitive(key_blob))
+ .context("Missing key blob info.")?,
+ ))
+ })
+ .context("In lookup_or_generate_key: second lookup failed")
+ }
+
+ /// Call the passed closure; if it returns `KEY_REQUIRES_UPGRADE`, call upgradeKey, and
+ /// write the upgraded key to the database.
+ fn upgrade_keyblob_if_required_with<'a, T, F>(
+ &self,
+ db: &mut KeystoreDB,
+ key_id_guard: &KeyIdGuard,
+ key_blob: KeyBlob<'a>,
+ f: F,
+ ) -> Result<(T, KeyBlob<'a>)>
+ where
+ F: Fn(&[u8]) -> Result<T, Error>,
+ {
+ match f(&key_blob) {
+ Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
+ let upgraded_blob = map_km_error({
+ let _wp = wd::watch_millis(
+ "In KeyMintDevice::upgrade_keyblob_if_required_with: calling upgradeKey.",
+ 500,
+ );
+ self.km_dev.upgradeKey(&key_blob, &[])
+ })
+ .context("In upgrade_keyblob_if_required_with: Upgrade failed")?;
+
+ let mut new_blob_metadata = BlobMetaData::new();
+ new_blob_metadata.add(BlobMetaEntry::KmUuid(self.km_uuid));
+
+ db.set_blob(
+ key_id_guard,
+ SubComponentType::KEY_BLOB,
+ Some(&upgraded_blob),
+ Some(&new_blob_metadata),
+ )
+ .context(concat!(
+ "In upgrade_keyblob_if_required_with: ",
+ "Failed to insert upgraded blob into the database"
+ ))?;
+
+ Ok((
+ f(&upgraded_blob).context(
+ "In upgrade_keyblob_if_required_with: Closure failed after upgrade",
+ )?,
+ KeyBlob::NonSensitive(upgraded_blob),
+ ))
+ }
+ result => Ok((
+ result.context("In upgrade_keyblob_if_required_with: Closure failed")?,
+ key_blob,
+ )),
+ }
+ }
+
+ /// Use the created key in an operation that can be done with
+ /// a call to begin followed by a call to finish.
+ #[allow(clippy::too_many_arguments)]
+ pub fn use_key_in_one_step(
+ &self,
+ db: &mut KeystoreDB,
+ key_id_guard: &KeyIdGuard,
+ key_blob: &[u8],
+ purpose: KeyPurpose,
+ operation_parameters: &[KeyParameter],
+ auth_token: Option<&HardwareAuthToken>,
+ input: &[u8],
+ ) -> Result<Vec<u8>> {
+ let key_blob = KeyBlob::Ref(key_blob);
+
+ let (begin_result, _) = self
+ .upgrade_keyblob_if_required_with(db, key_id_guard, key_blob, |blob| {
+ map_km_error({
+ let _wp = wd::watch_millis("In use_key_in_one_step: calling: begin", 500);
+ self.km_dev.begin(purpose, blob, operation_parameters, auth_token)
+ })
+ })
+ .context("In use_key_in_one_step: Failed to begin operation.")?;
+ let operation: Strong<dyn IKeyMintOperation> = begin_result
+ .operation
+ .ok_or_else(Error::sys)
+ .context("In use_key_in_one_step: Operation missing")?;
+ map_km_error({
+ let _wp = wd::watch_millis("In use_key_in_one_step: calling: finish", 500);
+ operation.finish(Some(input), None, None, None, None)
+ })
+ .context("In use_key_in_one_step: Failed to finish operation.")
+ }
+}
diff --git a/keystore2/src/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
index d606b6a..a19462b 100644
--- a/keystore2/src/remote_provisioning.rs
+++ b/keystore2/src/remote_provisioning.rs
@@ -22,24 +22,204 @@
use std::collections::HashMap;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- IRemotelyProvisionedComponent::IRemotelyProvisionedComponent, MacedPublicKey::MacedPublicKey,
- ProtectedData::ProtectedData, SecurityLevel::SecurityLevel,
+ Algorithm::Algorithm, AttestationKey::AttestationKey, Certificate::Certificate,
+ DeviceInfo::DeviceInfo, IRemotelyProvisionedComponent::IRemotelyProvisionedComponent,
+ KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue,
+ MacedPublicKey::MacedPublicKey, ProtectedData::ProtectedData, SecurityLevel::SecurityLevel,
+ Tag::Tag,
};
use android_security_remoteprovisioning::aidl::android::security::remoteprovisioning::{
AttestationPoolStatus::AttestationPoolStatus, IRemoteProvisioning::BnRemoteProvisioning,
- IRemoteProvisioning::IRemoteProvisioning,
+ IRemoteProvisioning::IRemoteProvisioning, ImplInfo::ImplInfo,
};
-use android_security_remoteprovisioning::binder::Strong;
+use android_security_remoteprovisioning::binder::{BinderFeatures, Strong};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, KeyDescriptor::KeyDescriptor,
+};
use anyhow::{Context, Result};
+use keystore2_crypto::parse_subject_from_certificate;
+use std::sync::atomic::{AtomicBool, Ordering};
-use crate::error::{self, map_or_log_err, map_rem_prov_error};
+use crate::database::{CertificateChain, KeystoreDB, Uuid};
+use crate::error::{self, map_or_log_err, map_rem_prov_error, Error};
use crate::globals::{get_keymint_device, get_remotely_provisioned_component, DB};
-use crate::utils::Asp;
+use crate::metrics_store::log_rkp_error_stats;
+use crate::utils::watchdog as wd;
+use android_security_metrics::aidl::android::security::metrics::RkpError::RkpError as MetricsRkpError;
+/// Contains helper functions to check if remote provisioning is enabled on the system and, if so,
+/// to assign and retrieve attestation keys and certificate chains.
+#[derive(Default)]
+pub struct RemProvState {
+ security_level: SecurityLevel,
+ km_uuid: Uuid,
+ is_hal_present: AtomicBool,
+}
+
+impl RemProvState {
+ /// Creates a RemProvState struct.
+ pub fn new(security_level: SecurityLevel, km_uuid: Uuid) -> Self {
+ Self { security_level, km_uuid, is_hal_present: AtomicBool::new(true) }
+ }
+
+ /// Checks if remote provisioning is enabled and partially caches the result. On a hybrid system
+ /// remote provisioning can flip from being disabled to enabled depending on responses from the
+ /// server, so unfortunately caching the presence or absence of the HAL is not enough to fully
+ /// make decisions about the state of remote provisioning during runtime.
+ fn check_rem_prov_enabled(&self, db: &mut KeystoreDB) -> Result<bool> {
+ if !self.is_hal_present.load(Ordering::Relaxed)
+ || get_remotely_provisioned_component(&self.security_level).is_err()
+ {
+ self.is_hal_present.store(false, Ordering::Relaxed);
+ return Ok(false);
+ }
+ // To check if remote provisioning is enabled on a system that supports both remote
+ // provisioning and factory provisioned keys, we only need to check if there are any
+ // keys at all generated to indicate if the app has gotten the signal to begin filling
+ // the key pool from the server.
+ let pool_status = db
+ .get_attestation_pool_status(0 /* date */, &self.km_uuid)
+ .context("In check_rem_prov_enabled: failed to get attestation pool status.")?;
+ Ok(pool_status.total != 0)
+ }
+
+ /// Fetches a remote provisioning attestation key and certificate chain inside of the
+ /// returned `CertificateChain` struct if one exists for the given caller_uid. If one has not
+ /// been assigned, this function will assign it. If there are no signed attestation keys
+ /// available to be assigned, it will return the ResponseCode `OUT_OF_KEYS`
+ fn get_rem_prov_attest_key(
+ &self,
+ key: &KeyDescriptor,
+ caller_uid: u32,
+ db: &mut KeystoreDB,
+ ) -> Result<Option<CertificateChain>> {
+ match key.domain {
+ Domain::APP => {
+ // Attempt to get an Attestation Key once. If it fails, then the app doesn't
+ // have a valid chain assigned to it. The helper function will return None after
+ // attempting to assign a key. An error will be thrown if the pool is simply out
+ // of usable keys. Then another attempt to fetch the just-assigned key will be
+ // made. If this fails too, something is very wrong.
+ self.get_rem_prov_attest_key_helper(key, caller_uid, db)
+ .context("In get_rem_prov_attest_key: Failed to get a key")?
+ .map_or_else(
+ || self.get_rem_prov_attest_key_helper(key, caller_uid, db),
+ |v| Ok(Some(v)),
+ )
+ .context(concat!(
+ "In get_rem_prov_attest_key: Failed to get a key after",
+ "attempting to assign one."
+ ))?
+ .map_or_else(
+ || {
+ Err(Error::sys()).context(concat!(
+ "In get_rem_prov_attest_key: Attempted to assign a ",
+ "key and failed silently. Something is very wrong."
+ ))
+ },
+ |cert_chain| Ok(Some(cert_chain)),
+ )
+ }
+ _ => Ok(None),
+ }
+ }
+
+ /// Returns None if an AttestationKey fails to be assigned. Errors if no keys are available.
+ fn get_rem_prov_attest_key_helper(
+ &self,
+ key: &KeyDescriptor,
+ caller_uid: u32,
+ db: &mut KeystoreDB,
+ ) -> Result<Option<CertificateChain>> {
+ let cert_chain = db
+ .retrieve_attestation_key_and_cert_chain(key.domain, caller_uid as i64, &self.km_uuid)
+ .context("In get_rem_prov_attest_key_helper: Failed to retrieve a key + cert chain")?;
+ match cert_chain {
+ Some(cert_chain) => Ok(Some(cert_chain)),
+ // Either this app needs to be assigned a key, or the pool is empty. An error will
+ // be thrown if there is no key available to assign. This will indicate that the app
+ // should be nudged to provision more keys so keystore can retry.
+ None => {
+ db.assign_attestation_key(key.domain, caller_uid as i64, &self.km_uuid)
+ .context("In get_rem_prov_attest_key_helper: Failed to assign a key")?;
+ Ok(None)
+ }
+ }
+ }
+
+ fn is_asymmetric_key(&self, params: &[KeyParameter]) -> bool {
+ params.iter().any(|kp| {
+ matches!(
+ kp,
+ KeyParameter {
+ tag: Tag::ALGORITHM,
+ value: KeyParameterValue::Algorithm(Algorithm::RSA)
+ } | KeyParameter {
+ tag: Tag::ALGORITHM,
+ value: KeyParameterValue::Algorithm(Algorithm::EC)
+ }
+ )
+ })
+ }
+
+ /// Checks to see (1) if the key in question should be attested to based on the algorithm and
+ /// (2) if remote provisioning is present and enabled on the system. If these conditions are
+ /// met, it makes an attempt to fetch the attestation key assigned to the `caller_uid`.
+ ///
+ /// It returns the ResponseCode `OUT_OF_KEYS` if there is not one key currently assigned to the
+ /// `caller_uid` and there are none available to assign.
+ pub fn get_remotely_provisioned_attestation_key_and_certs(
+ &self,
+ key: &KeyDescriptor,
+ caller_uid: u32,
+ params: &[KeyParameter],
+ db: &mut KeystoreDB,
+ ) -> Result<Option<(AttestationKey, Certificate)>> {
+ if !self.is_asymmetric_key(params) || !self.check_rem_prov_enabled(db)? {
+ // There is no remote provisioning component for this security level on the
+ // device. Return None so the underlying KM instance knows to use its
+ // factory provisioned key instead. Alternatively, it's not an asymmetric key
+ // and therefore will not be attested.
+ Ok(None)
+ } else {
+ match self.get_rem_prov_attest_key(key, caller_uid, db) {
+ Err(e) => {
+ log::error!(
+ concat!(
+ "In get_remote_provisioning_key_and_certs: Failed to get ",
+ "attestation key. {:?}"
+ ),
+ e
+ );
+ log_rkp_error_stats(MetricsRkpError::FALL_BACK_DURING_HYBRID);
+ Ok(None)
+ }
+ Ok(v) => match v {
+ Some(cert_chain) => Ok(Some((
+ AttestationKey {
+ keyBlob: cert_chain.private_key.to_vec(),
+ attestKeyParams: vec![],
+ issuerSubjectName: parse_subject_from_certificate(
+ &cert_chain.batch_cert,
+ )
+ .context(concat!(
+ "In get_remote_provisioning_key_and_certs: Failed to ",
+ "parse subject."
+ ))?,
+ },
+ Certificate { encodedCertificate: cert_chain.cert_chain },
+ ))),
+ None => Ok(None),
+ },
+ }
+ }
+ }
+}
/// Implementation of the IRemoteProvisioning service.
#[derive(Default)]
pub struct RemoteProvisioningService {
- device_by_sec_level: HashMap<SecurityLevel, Asp>,
+ device_by_sec_level: HashMap<SecurityLevel, Strong<dyn IRemotelyProvisionedComponent>>,
+ curve_by_sec_level: HashMap<SecurityLevel, i32>,
}
impl RemoteProvisioningService {
@@ -48,7 +228,7 @@
sec_level: &SecurityLevel,
) -> Result<Strong<dyn IRemotelyProvisionedComponent>> {
if let Some(dev) = self.device_by_sec_level.get(sec_level) {
- dev.get_interface().context("In get_dev_by_sec_level.")
+ Ok(dev.clone())
} else {
Err(error::Error::sys()).context(concat!(
"In get_dev_by_sec_level: Remote instance for requested security level",
@@ -62,31 +242,23 @@
let mut result: Self = Default::default();
let dev = get_remotely_provisioned_component(&SecurityLevel::TRUSTED_ENVIRONMENT)
.context("In new_native_binder: Failed to get TEE Remote Provisioner instance.")?;
+ result.curve_by_sec_level.insert(
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ dev.getHardwareInfo()
+ .context("In new_native_binder: Failed to get hardware info for the TEE.")?
+ .supportedEekCurve,
+ );
result.device_by_sec_level.insert(SecurityLevel::TRUSTED_ENVIRONMENT, dev);
if let Ok(dev) = get_remotely_provisioned_component(&SecurityLevel::STRONGBOX) {
+ result.curve_by_sec_level.insert(
+ SecurityLevel::STRONGBOX,
+ dev.getHardwareInfo()
+ .context("In new_native_binder: Failed to get hardware info for StrongBox.")?
+ .supportedEekCurve,
+ );
result.device_by_sec_level.insert(SecurityLevel::STRONGBOX, dev);
}
- Ok(BnRemoteProvisioning::new_binder(result))
- }
-
- /// Populates the AttestationPoolStatus parcelable with information about how many
- /// certs will be expiring by the date provided in `expired_by` along with how many
- /// keys have not yet been assigned.
- pub fn get_pool_status(
- &self,
- expired_by: i64,
- sec_level: SecurityLevel,
- ) -> Result<AttestationPoolStatus> {
- let (_, _, uuid) = get_keymint_device(&sec_level)?;
- DB.with::<_, Result<AttestationPoolStatus>>(|db| {
- let mut db = db.borrow_mut();
- // delete_expired_attestation_keys is always safe to call, and will remove anything
- // older than the date at the time of calling. No work should be done on the
- // attestation keys unless the pool status is checked first, so this call should be
- // enough to routinely clean out expired keys.
- db.delete_expired_attestation_keys()?;
- Ok(db.get_attestation_pool_status(expired_by, &uuid)?)
- })
+ Ok(BnRemoteProvisioning::new_binder(result, BinderFeatures::default()))
}
/// Generates a CBOR blob which will be assembled by the calling code into a larger
@@ -97,6 +269,7 @@
/// challenge will ensure freshness. A `test_mode` flag will instruct the remote provisioning
/// HAL if it is okay to accept EEKs that aren't signed by something that chains back to the
/// baked in root of trust in the underlying IRemotelyProvisionedComponent instance.
+ #[allow(clippy::too_many_arguments)]
pub fn generate_csr(
&self,
test_mode: bool,
@@ -105,6 +278,7 @@
challenge: &[u8],
sec_level: SecurityLevel,
protected_data: &mut ProtectedData,
+ device_info: &mut DeviceInfo,
) -> Result<Vec<u8>> {
let dev = self.get_dev_by_sec_level(&sec_level)?;
let (_, _, uuid) = get_keymint_device(&sec_level)?;
@@ -116,17 +290,39 @@
.map(|key| MacedPublicKey { macedKey: key.to_vec() })
.collect())
})?;
- let mut mac = Vec::<u8>::with_capacity(32);
- map_rem_prov_error(dev.generateCertificateRequest(
+ let mut mac = map_rem_prov_error(dev.generateCertificateRequest(
test_mode,
&keys_to_sign,
eek,
challenge,
- &mut mac,
+ device_info,
protected_data,
))
.context("In generate_csr: Failed to generate csr")?;
- Ok(mac)
+ // TODO(b/180392379): Replace this manual CBOR generation with the cbor-serde crate as well.
+ // This generates an array consisting of the mac and the public key Maps.
+ // Just generate the actual MacedPublicKeys structure when the crate is
+ // available.
+ let mut cose_mac_0: Vec<u8> = vec![
+ (0b100_00000 | (keys_to_sign.len() + 1)) as u8,
+ 0b010_11000, // mac
+ (mac.len() as u8),
+ ];
+ cose_mac_0.append(&mut mac);
+ // If this is a test mode key, there is an extra 6 bytes added as an additional entry in
+ // the COSE_Key struct to denote that.
+ let test_mode_entry_shift = if test_mode { 0 } else { 6 };
+ let byte_dist_mac0_payload = 8;
+ let cose_key_size = 83 - test_mode_entry_shift;
+ for maced_public_key in keys_to_sign {
+ if maced_public_key.macedKey.len() > cose_key_size + byte_dist_mac0_payload {
+ cose_mac_0.extend_from_slice(
+ &maced_public_key.macedKey
+ [byte_dist_mac0_payload..cose_key_size + byte_dist_mac0_payload],
+ );
+ }
+ }
+ Ok(cose_mac_0)
}
/// Provisions a certificate chain for a key whose CSR was included in generate_csr. The
@@ -145,13 +341,13 @@
DB.with::<_, Result<()>>(|db| {
let mut db = db.borrow_mut();
let (_, _, uuid) = get_keymint_device(&sec_level)?;
- Ok(db.store_signed_attestation_certificate_chain(
+ db.store_signed_attestation_certificate_chain(
public_key,
batch_cert,
certs, /* DER encoded certificate chain */
expiration_date,
&uuid,
- )?)
+ )
})
}
@@ -180,15 +376,44 @@
raw_key[32..64].clone_from_slice(&data[53..53 + 32]);
DB.with::<_, Result<()>>(|db| {
let mut db = db.borrow_mut();
- Ok(db.create_attestation_key_entry(&maced_key.macedKey, &raw_key, &priv_key, &uuid)?)
+ db.create_attestation_key_entry(&maced_key.macedKey, &raw_key, &priv_key, &uuid)
})
}
/// Checks the security level of each available IRemotelyProvisionedComponent hal and returns
/// all levels in an array to the caller.
- pub fn get_security_levels(&self) -> Result<Vec<SecurityLevel>> {
- Ok(self.device_by_sec_level.keys().cloned().collect())
+ pub fn get_implementation_info(&self) -> Result<Vec<ImplInfo>> {
+ Ok(self
+ .curve_by_sec_level
+ .iter()
+ .map(|(sec_level, curve)| ImplInfo { secLevel: *sec_level, supportedCurve: *curve })
+ .collect())
}
+
+ /// Deletes all attestation keys generated by the IRemotelyProvisionedComponent from the device,
+ /// regardless of what state of the attestation key lifecycle they were in.
+ pub fn delete_all_keys(&self) -> Result<i64> {
+ DB.with::<_, Result<i64>>(|db| {
+ let mut db = db.borrow_mut();
+ db.delete_all_attestation_keys()
+ })
+ }
+}
+
+/// Populates the AttestationPoolStatus parcelable with information about how many
+/// certs will be expiring by the date provided in `expired_by` along with how many
+/// keys have not yet been assigned.
+pub fn get_pool_status(expired_by: i64, sec_level: SecurityLevel) -> Result<AttestationPoolStatus> {
+ let (_, _, uuid) = get_keymint_device(&sec_level)?;
+ DB.with::<_, Result<AttestationPoolStatus>>(|db| {
+ let mut db = db.borrow_mut();
+ // delete_expired_attestation_keys is always safe to call, and will remove anything
+ // older than the date at the time of calling. No work should be done on the
+ // attestation keys unless the pool status is checked first, so this call should be
+ // enough to routinely clean out expired keys.
+ db.delete_expired_attestation_keys()?;
+ db.get_attestation_pool_status(expired_by, &uuid)
+ })
}
impl binder::Interface for RemoteProvisioningService {}
@@ -201,7 +426,8 @@
expired_by: i64,
sec_level: SecurityLevel,
) -> binder::public_api::Result<AttestationPoolStatus> {
- map_or_log_err(self.get_pool_status(expired_by, sec_level), Ok)
+ let _wp = wd::watch_millis("IRemoteProvisioning::getPoolStatus", 500);
+ map_or_log_err(get_pool_status(expired_by, sec_level), Ok)
}
fn generateCsr(
@@ -212,9 +438,19 @@
challenge: &[u8],
sec_level: SecurityLevel,
protected_data: &mut ProtectedData,
+ device_info: &mut DeviceInfo,
) -> binder::public_api::Result<Vec<u8>> {
+ let _wp = wd::watch_millis("IRemoteProvisioning::generateCsr", 500);
map_or_log_err(
- self.generate_csr(test_mode, num_csr, eek, challenge, sec_level, protected_data),
+ self.generate_csr(
+ test_mode,
+ num_csr,
+ eek,
+ challenge,
+ sec_level,
+ protected_data,
+ device_info,
+ ),
Ok,
)
}
@@ -227,6 +463,7 @@
expiration_date: i64,
sec_level: SecurityLevel,
) -> binder::public_api::Result<()> {
+ let _wp = wd::watch_millis("IRemoteProvisioning::provisionCertChain", 500);
map_or_log_err(
self.provision_cert_chain(public_key, batch_cert, certs, expiration_date, sec_level),
Ok,
@@ -238,10 +475,17 @@
is_test_mode: bool,
sec_level: SecurityLevel,
) -> binder::public_api::Result<()> {
+ let _wp = wd::watch_millis("IRemoteProvisioning::generateKeyPair", 500);
map_or_log_err(self.generate_key_pair(is_test_mode, sec_level), Ok)
}
- fn getSecurityLevels(&self) -> binder::public_api::Result<Vec<SecurityLevel>> {
- map_or_log_err(self.get_security_levels(), Ok)
+ fn getImplementationInfo(&self) -> binder::public_api::Result<Vec<ImplInfo>> {
+ let _wp = wd::watch_millis("IRemoteProvisioning::getSecurityLevels", 500);
+ map_or_log_err(self.get_implementation_info(), Ok)
+ }
+
+ fn deleteAllKeys(&self) -> binder::public_api::Result<i64> {
+ let _wp = wd::watch_millis("IRemoteProvisioning::deleteAllKeys", 500);
+ map_or_log_err(self.delete_all_keys(), Ok)
}
}
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index 5e1ce84..74aba3c 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -12,11 +12,35 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#![allow(unused_variables)]
-
//! This crate implements the IKeystoreSecurityLevel interface.
-use crate::globals::get_keymint_device;
+use crate::attestation_key_utils::{get_attest_key_info, AttestationKeyInfo};
+use crate::audit_log::{
+ log_key_deleted, log_key_generated, log_key_imported, log_key_integrity_violation,
+};
+use crate::database::{CertificateInfo, KeyIdGuard};
+use crate::error::{self, map_km_error, map_or_log_err, Error, ErrorCode};
+use crate::globals::{DB, ENFORCEMENTS, LEGACY_MIGRATOR, SUPER_KEY};
+use crate::key_parameter::KeyParameter as KsKeyParam;
+use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
+use crate::metrics_store::log_key_creation_event_stats;
+use crate::remote_provisioning::RemProvState;
+use crate::super_key::{KeyBlob, SuperKeyManager};
+use crate::utils::{
+ check_device_attestation_permissions, check_key_permission, is_device_id_attestation_tag,
+ key_characteristics_to_internal, uid_to_android_user, watchdog as wd,
+};
+use crate::{
+ database::{
+ BlobMetaData, BlobMetaEntry, DateTime, KeyEntry, KeyEntryLoadBits, KeyMetaData,
+ KeyMetaEntry, KeyType, SubComponentType, Uuid,
+ },
+ operation::KeystoreOperation,
+ operation::LoggingInfo,
+ operation::OperationDb,
+ permission::KeyPerm,
+};
+use crate::{globals::get_keymint_device, id_rotation::IdRotationState};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, AttestationKey::AttestationKey,
HardwareAuthenticatorType::HardwareAuthenticatorType, IKeyMintDevice::IKeyMintDevice,
@@ -24,45 +48,25 @@
KeyMintHardwareInfo::KeyMintHardwareInfo, KeyParameter::KeyParameter,
KeyParameterValue::KeyParameterValue, SecurityLevel::SecurityLevel, Tag::Tag,
};
+use android_hardware_security_keymint::binder::{BinderFeatures, Strong, ThreadState};
use android_system_keystore2::aidl::android::system::keystore2::{
AuthenticatorSpec::AuthenticatorSpec, CreateOperationResponse::CreateOperationResponse,
- Domain::Domain, IKeystoreOperation::IKeystoreOperation,
- IKeystoreSecurityLevel::BnKeystoreSecurityLevel,
+ Domain::Domain, EphemeralStorageKeyResponse::EphemeralStorageKeyResponse,
+ IKeystoreOperation::IKeystoreOperation, IKeystoreSecurityLevel::BnKeystoreSecurityLevel,
IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
KeyMetadata::KeyMetadata, KeyParameters::KeyParameters,
};
-
-use crate::database::{CertificateInfo, KeyIdGuard};
-use crate::globals::{DB, ENFORCEMENTS, LEGACY_MIGRATOR, SUPER_KEY};
-use crate::key_parameter::KeyParameter as KsKeyParam;
-use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
-use crate::super_key::{KeyBlob, SuperKeyManager};
-use crate::utils::{check_key_permission, uid_to_android_user, Asp};
-use crate::{
- database::{
- BlobMetaData, BlobMetaEntry, DateTime, KeyEntry, KeyEntryLoadBits, KeyMetaData,
- KeyMetaEntry, KeyType, SubComponentType, Uuid,
- },
- operation::KeystoreOperation,
- operation::OperationDb,
- permission::KeyPerm,
-};
-use crate::{
- error::{self, map_km_error, map_or_log_err, Error, ErrorCode},
- utils::key_characteristics_to_internal,
-};
use anyhow::{anyhow, Context, Result};
-use binder::{IBinder, Strong, ThreadState};
-use keystore2_crypto::parse_issuer_subject_from_certificate;
/// Implementation of the IKeystoreSecurityLevel Interface.
pub struct KeystoreSecurityLevel {
security_level: SecurityLevel,
- keymint: Asp,
- #[allow(dead_code)]
+ keymint: Strong<dyn IKeyMintDevice>,
hw_info: KeyMintHardwareInfo,
km_uuid: Uuid,
operation_db: OperationDb,
+ rem_prov_state: RemProvState,
+ id_rotation_state: IdRotationState,
}
// Blob of 32 zeroes used as empty masking key.
@@ -74,25 +78,35 @@
impl KeystoreSecurityLevel {
/// Creates a new security level instance wrapped in a
- /// BnKeystoreSecurityLevel proxy object. It also
- /// calls `IBinder::set_requesting_sid` on the new interface, because
+ /// BnKeystoreSecurityLevel proxy object. It also enables
+ /// `BinderFeatures::set_requesting_sid` on the new interface, because
/// we need it for checking keystore permissions.
pub fn new_native_binder(
security_level: SecurityLevel,
+ id_rotation_state: IdRotationState,
) -> Result<(Strong<dyn IKeystoreSecurityLevel>, Uuid)> {
let (dev, hw_info, km_uuid) = get_keymint_device(&security_level)
.context("In KeystoreSecurityLevel::new_native_binder.")?;
- let result = BnKeystoreSecurityLevel::new_binder(Self {
- security_level,
- keymint: dev,
- hw_info,
- km_uuid,
- operation_db: OperationDb::new(),
- });
- result.as_binder().set_requesting_sid(true);
+ let result = BnKeystoreSecurityLevel::new_binder(
+ Self {
+ security_level,
+ keymint: dev,
+ hw_info,
+ km_uuid,
+ operation_db: OperationDb::new(),
+ rem_prov_state: RemProvState::new(security_level, km_uuid),
+ id_rotation_state,
+ },
+ BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
+ );
Ok((result, km_uuid))
}
+ fn watch_millis(&self, id: &'static str, millis: u64) -> Option<wd::WatchPoint> {
+ let sec_level = self.security_level;
+ wd::watch_millis_with(id, millis, move || format!("SecurityLevel {:?}", sec_level))
+ }
+
fn store_new_key(
&self,
key: KeyDescriptor,
@@ -131,36 +145,38 @@
SecurityLevel::SOFTWARE,
));
- let (key_blob, mut blob_metadata) = DB
- .with(|db| {
- SUPER_KEY.handle_super_encryption_on_key_init(
- &mut db.borrow_mut(),
- &LEGACY_MIGRATOR,
- &(key.domain),
- &key_parameters,
- flags,
- user_id,
- &key_blob,
- )
- })
- .context("In store_new_key. Failed to handle super encryption.")?;
-
let creation_date = DateTime::now().context("Trying to make creation time.")?;
let key = match key.domain {
- Domain::BLOB => {
- KeyDescriptor { domain: Domain::BLOB, blob: Some(key_blob), ..Default::default() }
- }
+ Domain::BLOB => KeyDescriptor {
+ domain: Domain::BLOB,
+ blob: Some(key_blob.to_vec()),
+ ..Default::default()
+ },
_ => DB
.with::<_, Result<KeyDescriptor>>(|db| {
+ let mut db = db.borrow_mut();
+
+ let (key_blob, mut blob_metadata) = SUPER_KEY
+ .handle_super_encryption_on_key_init(
+ &mut db,
+ &LEGACY_MIGRATOR,
+ &(key.domain),
+ &key_parameters,
+ flags,
+ user_id,
+ &key_blob,
+ )
+ .context("In store_new_key. Failed to handle super encryption.")?;
+
let mut key_metadata = KeyMetaData::new();
key_metadata.add(KeyMetaEntry::CreationDate(creation_date));
blob_metadata.add(BlobMetaEntry::KmUuid(self.km_uuid));
- let mut db = db.borrow_mut();
let key_id = db
.store_new_key(
&key,
+ KeyType::Client,
&key_parameters,
&(&key_blob, &blob_metadata),
&cert_info,
@@ -202,6 +218,11 @@
Domain::BLOB => {
check_key_permission(KeyPerm::use_(), key, &None)
.context("In create_operation: checking use permission for Domain::BLOB.")?;
+ if forced {
+ check_key_permission(KeyPerm::req_forced_op(), key, &None).context(
+ "In create_operation: checking forced permission for Domain::BLOB.",
+ )?;
+ }
(
match &key.blob {
Some(blob) => blob,
@@ -220,13 +241,19 @@
_ => {
let (key_id_guard, mut key_entry) = DB
.with::<_, Result<(KeyIdGuard, KeyEntry)>>(|db| {
- LEGACY_MIGRATOR.with_try_migrate(&key, caller_uid, || {
+ LEGACY_MIGRATOR.with_try_migrate(key, caller_uid, || {
db.borrow_mut().load_key_entry(
- &key,
+ key,
KeyType::Client,
KeyEntryLoadBits::KM,
caller_uid,
- |k, av| check_key_permission(KeyPerm::use_(), k, &av),
+ |k, av| {
+ check_key_permission(KeyPerm::use_(), k, &av)?;
+ if forced {
+ check_key_permission(KeyPerm::req_forced_op(), k, &av)?;
+ }
+ Ok(())
+ },
)
})
})
@@ -258,48 +285,61 @@
},
)?;
+ // Remove Tag::PURPOSE from the operation_parameters, since some keymaster devices return
+ // an error on begin() if Tag::PURPOSE is in the operation_parameters.
+ let op_params: Vec<KeyParameter> =
+ operation_parameters.iter().filter(|p| p.tag != Tag::PURPOSE).cloned().collect();
+ let operation_parameters = op_params.as_slice();
+
let (immediate_hat, mut auth_info) = ENFORCEMENTS
.authorize_create(
purpose,
key_properties.as_ref(),
operation_parameters.as_ref(),
- // TODO b/178222844 Replace this with the configuration returned by
- // KeyMintDevice::getHardwareInfo.
- // For now we assume that strongbox implementations need secure timestamps.
- self.security_level == SecurityLevel::STRONGBOX,
+ self.hw_info.timestampTokenRequired,
)
.context("In create_operation.")?;
- let immediate_hat = immediate_hat.unwrap_or_default();
-
- let user_id = uid_to_android_user(caller_uid);
-
let km_blob = SUPER_KEY
.unwrap_key_if_required(&blob_metadata, km_blob)
.context("In create_operation. Failed to handle super encryption.")?;
- let km_dev: Strong<dyn IKeyMintDevice> = self
- .keymint
- .get_interface()
- .context("In create_operation: Failed to get KeyMint device")?;
-
let (begin_result, upgraded_blob) = self
.upgrade_keyblob_if_required_with(
- &*km_dev,
+ &*self.keymint,
key_id_guard,
- &(&km_blob, &blob_metadata),
- &operation_parameters,
+ &km_blob,
+ &blob_metadata,
+ operation_parameters,
|blob| loop {
- match map_km_error(km_dev.begin(
- purpose,
- blob,
- &operation_parameters,
- &immediate_hat,
- )) {
+ match map_km_error({
+ let _wp = self.watch_millis(
+ "In KeystoreSecurityLevel::create_operation: calling begin",
+ 500,
+ );
+ self.keymint.begin(
+ purpose,
+ blob,
+ operation_parameters,
+ immediate_hat.as_ref(),
+ )
+ }) {
Err(Error::Km(ErrorCode::TOO_MANY_OPERATIONS)) => {
- self.operation_db.prune(caller_uid)?;
+ self.operation_db.prune(caller_uid, forced)?;
continue;
}
+ v @ Err(Error::Km(ErrorCode::INVALID_KEY_BLOB)) => {
+ if let Some((key_id, _)) = key_properties {
+ if let Ok(Some(key)) =
+ DB.with(|db| db.borrow_mut().load_key_descriptor(key_id))
+ {
+ log_key_integrity_violation(&key);
+ } else {
+ log::error!("Failed to load key descriptor for audit log");
+ }
+ }
+ return v;
+ }
v => return v,
}
},
@@ -308,11 +348,22 @@
let operation_challenge = auth_info.finalize_create_authorization(begin_result.challenge);
+ let op_params: Vec<KeyParameter> = operation_parameters.to_vec();
+
let operation = match begin_result.operation {
- Some(km_op) => {
- self.operation_db.create_operation(km_op, caller_uid, auth_info)
- },
- None => return Err(Error::sys()).context("In create_operation: Begin operation returned successfully, but did not return a valid operation."),
+ Some(km_op) => self.operation_db.create_operation(
+ km_op,
+ caller_uid,
+ auth_info,
+ forced,
+ LoggingInfo::new(self.security_level, purpose, op_params, upgraded_blob.is_some()),
+ ),
+ None => {
+ return Err(Error::sys()).context(concat!(
+ "In create_operation: Begin operation returned successfully, ",
+ "but did not return a valid operation."
+ ))
+ }
};
let op_binder: binder::public_api::Strong<dyn IKeystoreOperation> =
@@ -328,10 +379,15 @@
0 => None,
_ => Some(KeyParameters { keyParameter: begin_result.params }),
},
+ // An upgraded blob should only be returned if the caller has permission
+ // to use Domain::BLOB keys. If we got to this point, we already checked
+ // that the caller had that permission.
+ upgradedBlob: if key.domain == Domain::BLOB { upgraded_blob } else { None },
})
}
fn add_certificate_parameters(
+ &self,
uid: u32,
params: &[KeyParameter],
key: &KeyDescriptor,
@@ -339,9 +395,19 @@
let mut result = params.to_vec();
// If there is an attestation challenge we need to get an application id.
if params.iter().any(|kp| kp.tag == Tag::ATTESTATION_CHALLENGE) {
- let aaid = keystore2_aaid::get_aaid(uid).map_err(|e| {
- anyhow!(format!("In add_certificate_parameters: get_aaid returned status {}.", e))
- })?;
+ let aaid = {
+ let _wp = self.watch_millis(
+ "In KeystoreSecurityLevel::add_certificate_parameters calling: get_aaid",
+ 500,
+ );
+ keystore2_aaid::get_aaid(uid).map_err(|e| {
+ anyhow!(format!(
+ "In add_certificate_parameters: get_aaid returned status {}.",
+ e
+ ))
+ })
+ }?;
+
result.push(KeyParameter {
tag: Tag::ATTESTATION_APPLICATION_ID,
value: KeyParameterValue::Blob(aaid),
@@ -351,7 +417,24 @@
if params.iter().any(|kp| kp.tag == Tag::INCLUDE_UNIQUE_ID) {
check_key_permission(KeyPerm::gen_unique_id(), key, &None).context(concat!(
"In add_certificate_parameters: ",
- "Caller does not have the permission for device unique attestation."
+ "Caller does not have the permission to generate a unique ID"
+ ))?;
+ if self.id_rotation_state.had_factory_reset_since_id_rotation().context(
+ "In add_certificate_parameters: Call to had_factory_reset_since_id_rotation failed."
+ )? {
+ result.push(KeyParameter{
+ tag: Tag::RESET_SINCE_ID_ROTATION,
+ value: KeyParameterValue::BoolValue(true),
+ })
+ }
+ }
+
+ // If the caller requests any device identifier attestation tag, check that they hold the
+ // correct Android permission.
+ if params.iter().any(|kp| is_device_id_attestation_tag(kp.tag)) {
+ check_device_attestation_permissions().context(concat!(
+ "In add_certificate_parameters: ",
+ "Caller does not have the permission to attest device identifiers."
))?;
}
@@ -384,7 +467,7 @@
attest_key_descriptor: Option<&KeyDescriptor>,
params: &[KeyParameter],
flags: i32,
- entropy: &[u8],
+ _entropy: &[u8],
) -> Result<KeyMetadata> {
if key.domain != Domain::BLOB && key.alias.is_none() {
return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
@@ -403,84 +486,100 @@
};
// generate_key requires the rebind permission.
+ // Must return on error for security reasons.
check_key_permission(KeyPerm::rebind(), &key, &None).context("In generate_key.")?;
- let attest_key = match attest_key_descriptor {
- None => None,
- Some(key) => Some(
- self.get_attest_key(key, caller_uid)
- .context("In generate_key: Trying to load attest key")?,
- ),
+ let attestation_key_info = match (key.domain, attest_key_descriptor) {
+ (Domain::BLOB, _) => None,
+ _ => DB
+ .with(|db| {
+ get_attest_key_info(
+ &key,
+ caller_uid,
+ attest_key_descriptor,
+ params,
+ &self.rem_prov_state,
+ &mut db.borrow_mut(),
+ )
+ })
+ .context("In generate_key: Trying to get an attestation key")?,
};
-
- let params = Self::add_certificate_parameters(caller_uid, params, &key)
+ let params = self
+ .add_certificate_parameters(caller_uid, params, &key)
.context("In generate_key: Trying to get aaid.")?;
- let km_dev: Strong<dyn IKeyMintDevice> = self.keymint.get_interface()?;
- map_km_error(km_dev.addRngEntropy(entropy))
- .context("In generate_key: Trying to add entropy.")?;
- let creation_result = map_km_error(km_dev.generateKey(¶ms, attest_key.as_ref()))
- .context("In generate_key: While generating Key")?;
+ let creation_result = match attestation_key_info {
+ Some(AttestationKeyInfo::UserGenerated {
+ key_id_guard,
+ blob,
+ blob_metadata,
+ issuer_subject,
+ }) => self
+ .upgrade_keyblob_if_required_with(
+ &*self.keymint,
+ Some(key_id_guard),
+ &KeyBlob::Ref(&blob),
+ &blob_metadata,
+ ¶ms,
+ |blob| {
+ let attest_key = Some(AttestationKey {
+ keyBlob: blob.to_vec(),
+ attestKeyParams: vec![],
+ issuerSubjectName: issuer_subject.clone(),
+ });
+ map_km_error({
+ let _wp = self.watch_millis(
+ concat!(
+ "In KeystoreSecurityLevel::generate_key (UserGenerated): ",
+ "calling generate_key."
+ ),
+ 5000, // Generate can take a little longer.
+ );
+ self.keymint.generateKey(¶ms, attest_key.as_ref())
+ })
+ },
+ )
+ .context("In generate_key: Using user generated attestation key.")
+ .map(|(result, _)| result),
+ Some(AttestationKeyInfo::RemoteProvisioned { attestation_key, attestation_certs }) => {
+ map_km_error({
+ let _wp = self.watch_millis(
+ concat!(
+ "In KeystoreSecurityLevel::generate_key (RemoteProvisioned): ",
+ "calling generate_key.",
+ ),
+ 5000, // Generate can take a little longer.
+ );
+ self.keymint.generateKey(¶ms, Some(&attestation_key))
+ })
+ .context("While generating Key with remote provisioned attestation key.")
+ .map(|mut creation_result| {
+ creation_result.certificateChain.push(attestation_certs);
+ creation_result
+ })
+ }
+ None => map_km_error({
+ let _wp = self.watch_millis(
+ concat!(
+ "In KeystoreSecurityLevel::generate_key (No attestation): ",
+ "calling generate_key.",
+ ),
+ 5000, // Generate can take a little longer.
+ );
+ self.keymint.generateKey(¶ms, None)
+ })
+ .context("While generating Key without explicit attestation key."),
+ }
+ .context("In generate_key.")?;
let user_id = uid_to_android_user(caller_uid);
self.store_new_key(key, creation_result, user_id, Some(flags)).context("In generate_key.")
}
- fn get_attest_key(&self, key: &KeyDescriptor, caller_uid: u32) -> Result<AttestationKey> {
- let (km_blob, cert) = self
- .load_attest_key_blob_and_cert(&key, caller_uid)
- .context("In get_attest_key: Failed to load blob and cert")?;
-
- let issuer_subject: Vec<u8> = parse_issuer_subject_from_certificate(&cert)
- .context("In get_attest_key: Failed to parse subject from certificate.")?;
-
- Ok(AttestationKey {
- keyBlob: km_blob.to_vec(),
- attestKeyParams: [].to_vec(),
- issuerSubjectName: issuer_subject,
- })
- }
-
- fn load_attest_key_blob_and_cert(
- &self,
- key: &KeyDescriptor,
- caller_uid: u32,
- ) -> Result<(Vec<u8>, Vec<u8>)> {
- match key.domain {
- Domain::BLOB => Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT)).context(
- "In load_attest_key_blob_and_cert: Domain::BLOB attestation keys not supported",
- ),
- _ => {
- let (key_id_guard, mut key_entry) = DB
- .with::<_, Result<(KeyIdGuard, KeyEntry)>>(|db| {
- db.borrow_mut().load_key_entry(
- &key,
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- caller_uid,
- |k, av| check_key_permission(KeyPerm::use_(), k, &av),
- )
- })
- .context("In load_attest_key_blob_and_cert: Failed to load key.")?;
-
- let (blob, _) =
- key_entry.take_key_blob_info().ok_or_else(Error::sys).context(concat!(
- "In load_attest_key_blob_and_cert: Successfully loaded key entry,",
- " but KM blob was missing."
- ))?;
- let cert = key_entry.take_cert().ok_or_else(Error::sys).context(concat!(
- "In load_attest_key_blob_and_cert: Successfully loaded key entry,",
- " but cert was missing."
- ))?;
- Ok((blob, cert))
- }
- }
- }
-
fn import_key(
&self,
key: &KeyDescriptor,
- attestation_key: Option<&KeyDescriptor>,
+ _attestation_key: Option<&KeyDescriptor>,
params: &[KeyParameter],
flags: i32,
key_data: &[u8],
@@ -504,7 +603,8 @@
// import_key requires the rebind permission.
check_key_permission(KeyPerm::rebind(), &key, &None).context("In import_key.")?;
- let params = Self::add_certificate_parameters(caller_uid, params, &key)
+ let params = self
+ .add_certificate_parameters(caller_uid, params, &key)
.context("In import_key: Trying to get aaid.")?;
let format = params
@@ -523,11 +623,13 @@
})
.context("In import_key.")?;
- let km_dev: Strong<dyn IKeyMintDevice> =
- self.keymint.get_interface().context("In import_key: Trying to get the KM device")?;
- let creation_result =
- map_km_error(km_dev.importKey(¶ms, format, key_data, None /* attestKey */))
- .context("In import_key: Trying to call importKey")?;
+ let km_dev = &self.keymint;
+ let creation_result = map_km_error({
+ let _wp =
+ self.watch_millis("In KeystoreSecurityLevel::import_key: calling importKey.", 500);
+ km_dev.importKey(¶ms, format, key_data, None /* attestKey */)
+ })
+ .context("In import_key: Trying to call importKey")?;
let user_id = uid_to_android_user(caller_uid);
self.store_new_key(key, creation_result, user_id, Some(flags)).context("In import_key.")
@@ -589,7 +691,7 @@
.with(|db| {
LEGACY_MIGRATOR.with_try_migrate(&key, caller_uid, || {
db.borrow_mut().load_key_entry(
- &wrapping_key,
+ wrapping_key,
KeyType::Client,
KeyEntryLoadBits::KM,
caller_uid,
@@ -631,19 +733,23 @@
let masking_key = masking_key.unwrap_or(ZERO_BLOB_32);
- let km_dev: Strong<dyn IKeyMintDevice> = self.keymint.get_interface()?;
let (creation_result, _) = self
.upgrade_keyblob_if_required_with(
- &*km_dev,
+ &*self.keymint,
Some(wrapping_key_id_guard),
- &(&wrapping_key_blob, &wrapping_blob_metadata),
+ &wrapping_key_blob,
+ &wrapping_blob_metadata,
&[],
|wrapping_blob| {
- let creation_result = map_km_error(km_dev.importWrappedKey(
+ let _wp = self.watch_millis(
+ "In KeystoreSecurityLevel::import_wrapped_key: calling importWrappedKey.",
+ 500,
+ );
+ let creation_result = map_km_error(self.keymint.importWrappedKey(
wrapped_data,
wrapping_blob,
masking_key,
- ¶ms,
+ params,
pw_sid,
fp_sid,
))?;
@@ -656,48 +762,71 @@
.context("In import_wrapped_key: Trying to store the new key.")
}
+ fn store_upgraded_keyblob(
+ key_id_guard: KeyIdGuard,
+ km_uuid: Option<&Uuid>,
+ key_blob: &KeyBlob,
+ upgraded_blob: &[u8],
+ ) -> Result<()> {
+ let (upgraded_blob_to_be_stored, new_blob_metadata) =
+ SuperKeyManager::reencrypt_if_required(key_blob, upgraded_blob)
+ .context("In store_upgraded_keyblob: Failed to handle super encryption.")?;
+
+ let mut new_blob_metadata = new_blob_metadata.unwrap_or_default();
+ if let Some(uuid) = km_uuid {
+ new_blob_metadata.add(BlobMetaEntry::KmUuid(*uuid));
+ }
+
+ DB.with(|db| {
+ let mut db = db.borrow_mut();
+ db.set_blob(
+ &key_id_guard,
+ SubComponentType::KEY_BLOB,
+ Some(&upgraded_blob_to_be_stored),
+ Some(&new_blob_metadata),
+ )
+ })
+ .context("In store_upgraded_keyblob: Failed to insert upgraded blob into the database.")
+ }
+
fn upgrade_keyblob_if_required_with<T, F>(
&self,
km_dev: &dyn IKeyMintDevice,
key_id_guard: Option<KeyIdGuard>,
- blob_info: &(&KeyBlob, &BlobMetaData),
+ key_blob: &KeyBlob,
+ blob_metadata: &BlobMetaData,
params: &[KeyParameter],
f: F,
) -> Result<(T, Option<Vec<u8>>)>
where
F: Fn(&[u8]) -> Result<T, Error>,
{
- match f(blob_info.0) {
+ match f(key_blob) {
Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
- let upgraded_blob = map_km_error(km_dev.upgradeKey(blob_info.0, params))
- .context("In upgrade_keyblob_if_required_with: Upgrade failed.")?;
+ let upgraded_blob = {
+ let _wp = self.watch_millis(
+ concat!(
+ "In KeystoreSecurityLevel::upgrade_keyblob_if_required_with: ",
+ "calling upgradeKey."
+ ),
+ 500,
+ );
+ map_km_error(km_dev.upgradeKey(key_blob, params))
+ }
+ .context("In upgrade_keyblob_if_required_with: Upgrade failed.")?;
- let (upgraded_blob_to_be_stored, blob_metadata) =
- SuperKeyManager::reencrypt_on_upgrade_if_required(blob_info.0, &upgraded_blob)
- .context(
- "In upgrade_keyblob_if_required_with: Failed to handle super encryption.",
+ if let Some(kid) = key_id_guard {
+ Self::store_upgraded_keyblob(
+ kid,
+ blob_metadata.km_uuid(),
+ key_blob,
+ &upgraded_blob,
+ )
+ .context(
+ "In upgrade_keyblob_if_required_with: store_upgraded_keyblob failed",
)?;
-
- let mut blob_metadata = blob_metadata.unwrap_or_else(BlobMetaData::new);
- if let Some(uuid) = blob_info.1.km_uuid() {
- blob_metadata.add(BlobMetaEntry::KmUuid(*uuid));
}
- key_id_guard.map_or(Ok(()), |key_id_guard| {
- DB.with(|db| {
- let mut db = db.borrow_mut();
- db.set_blob(
- &key_id_guard,
- SubComponentType::KEY_BLOB,
- Some(&upgraded_blob_to_be_stored),
- Some(&blob_metadata),
- )
- })
- .context(concat!(
- "In upgrade_keyblob_if_required_with: ",
- "Failed to insert upgraded blob into the database.",
- ))
- })?;
match f(&upgraded_blob) {
Ok(v) => Ok((v, Some(upgraded_blob))),
Err(e) => Err(e).context(concat!(
@@ -706,10 +835,114 @@
)),
}
}
- Err(e) => {
- Err(e).context("In upgrade_keyblob_if_required_with: Failed perform operation.")
+ result => {
+ if let Some(kid) = key_id_guard {
+ if key_blob.force_reencrypt() {
+ Self::store_upgraded_keyblob(
+ kid,
+ blob_metadata.km_uuid(),
+ key_blob,
+ key_blob,
+ )
+ .context(concat!(
+ "In upgrade_keyblob_if_required_with: ",
+ "store_upgraded_keyblob failed in forced reencrypt"
+ ))?;
+ }
+ }
+ result
+ .map(|v| (v, None))
+ .context("In upgrade_keyblob_if_required_with: Called closure failed.")
}
- Ok(v) => Ok((v, None)),
+ }
+ }
+
+ fn convert_storage_key_to_ephemeral(
+ &self,
+ storage_key: &KeyDescriptor,
+ ) -> Result<EphemeralStorageKeyResponse> {
+ if storage_key.domain != Domain::BLOB {
+ return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT)).context(concat!(
+ "In IKeystoreSecurityLevel convert_storage_key_to_ephemeral: ",
+ "Key must be of Domain::BLOB"
+ ));
+ }
+ let key_blob = storage_key
+ .blob
+ .as_ref()
+ .ok_or(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+ .context(
+ "In IKeystoreSecurityLevel convert_storage_key_to_ephemeral: No key blob specified",
+ )?;
+
+ // convert_storage_key_to_ephemeral requires the associated permission
+ check_key_permission(KeyPerm::convert_storage_key_to_ephemeral(), storage_key, &None)
+ .context("In convert_storage_key_to_ephemeral: Check permission")?;
+
+ let km_dev = &self.keymint;
+ match {
+ let _wp = self.watch_millis(
+ concat!(
+ "In IKeystoreSecurityLevel::convert_storage_key_to_ephemeral: ",
+ "calling convertStorageKeyToEphemeral (1)"
+ ),
+ 500,
+ );
+ map_km_error(km_dev.convertStorageKeyToEphemeral(key_blob))
+ } {
+ Ok(result) => {
+ Ok(EphemeralStorageKeyResponse { ephemeralKey: result, upgradedBlob: None })
+ }
+ Err(error::Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
+ let upgraded_blob = {
+ let _wp = self.watch_millis(
+ "In convert_storage_key_to_ephemeral: calling upgradeKey",
+ 500,
+ );
+ map_km_error(km_dev.upgradeKey(key_blob, &[]))
+ }
+ .context("In convert_storage_key_to_ephemeral: Failed to upgrade key blob.")?;
+ let ephemeral_key = {
+ let _wp = self.watch_millis(
+ "In convert_storage_key_to_ephemeral: calling convertStorageKeyToEphemeral (2)",
+ 500,
+ );
+ map_km_error(km_dev.convertStorageKeyToEphemeral(&upgraded_blob))
+ }
+ .context(concat!(
+ "In convert_storage_key_to_ephemeral: ",
+ "Failed to retrieve ephemeral key (after upgrade)."
+ ))?;
+ Ok(EphemeralStorageKeyResponse {
+ ephemeralKey: ephemeral_key,
+ upgradedBlob: Some(upgraded_blob),
+ })
+ }
+ Err(e) => Err(e)
+ .context("In convert_storage_key_to_ephemeral: Failed to retrieve ephemeral key."),
+ }
+ }
+
+ fn delete_key(&self, key: &KeyDescriptor) -> Result<()> {
+ if key.domain != Domain::BLOB {
+ return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+ .context("In IKeystoreSecurityLevel delete_key: Key must be of Domain::BLOB");
+ }
+
+ let key_blob = key
+ .blob
+ .as_ref()
+ .ok_or(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+ .context("In IKeystoreSecurityLevel delete_key: No key blob specified")?;
+
+ check_key_permission(KeyPerm::delete(), key, &None)
+ .context("In IKeystoreSecurityLevel delete_key: Checking delete permissions")?;
+
+ let km_dev = &self.keymint;
+ {
+ let _wp =
+ self.watch_millis("In KeystoreSecuritylevel::delete_key: calling deleteKey", 500);
+ map_km_error(km_dev.deleteKey(key_blob)).context("In keymint device deleteKey")
}
}
}
@@ -723,6 +956,7 @@
operation_parameters: &[KeyParameter],
forced: bool,
) -> binder::public_api::Result<CreateOperationResponse> {
+ let _wp = self.watch_millis("IKeystoreSecurityLevel::createOperation", 500);
map_or_log_err(self.create_operation(key, operation_parameters, forced), Ok)
}
fn generateKey(
@@ -733,7 +967,13 @@
flags: i32,
entropy: &[u8],
) -> binder::public_api::Result<KeyMetadata> {
- map_or_log_err(self.generate_key(key, attestation_key, params, flags, entropy), Ok)
+ // Duration is set to 5 seconds, because generateKey - especially for RSA keys, takes more
+ // time than other operations
+ let _wp = self.watch_millis("IKeystoreSecurityLevel::generateKey", 5000);
+ let result = self.generate_key(key, attestation_key, params, flags, entropy);
+ log_key_creation_event_stats(self.security_level, params, &result);
+ log_key_generated(key, ThreadState::get_calling_uid(), result.is_ok());
+ map_or_log_err(result, Ok)
}
fn importKey(
&self,
@@ -743,7 +983,11 @@
flags: i32,
key_data: &[u8],
) -> binder::public_api::Result<KeyMetadata> {
- map_or_log_err(self.import_key(key, attestation_key, params, flags, key_data), Ok)
+ let _wp = self.watch_millis("IKeystoreSecurityLevel::importKey", 500);
+ let result = self.import_key(key, attestation_key, params, flags, key_data);
+ log_key_creation_event_stats(self.security_level, params, &result);
+ log_key_imported(key, ThreadState::get_calling_uid(), result.is_ok());
+ map_or_log_err(result, Ok)
}
fn importWrappedKey(
&self,
@@ -753,9 +997,24 @@
params: &[KeyParameter],
authenticators: &[AuthenticatorSpec],
) -> binder::public_api::Result<KeyMetadata> {
- map_or_log_err(
- self.import_wrapped_key(key, wrapping_key, masking_key, params, authenticators),
- Ok,
- )
+ let _wp = self.watch_millis("IKeystoreSecurityLevel::importWrappedKey", 500);
+ let result =
+ self.import_wrapped_key(key, wrapping_key, masking_key, params, authenticators);
+ log_key_creation_event_stats(self.security_level, params, &result);
+ log_key_imported(key, ThreadState::get_calling_uid(), result.is_ok());
+ map_or_log_err(result, Ok)
+ }
+ fn convertStorageKeyToEphemeral(
+ &self,
+ storage_key: &KeyDescriptor,
+ ) -> binder::public_api::Result<EphemeralStorageKeyResponse> {
+ let _wp = self.watch_millis("IKeystoreSecurityLevel::convertStorageKeyToEphemeral", 500);
+ map_or_log_err(self.convert_storage_key_to_ephemeral(storage_key), Ok)
+ }
+ fn deleteKey(&self, key: &KeyDescriptor) -> binder::public_api::Result<()> {
+ let _wp = self.watch_millis("IKeystoreSecurityLevel::deleteKey", 500);
+ let result = self.delete_key(key);
+ log_key_deleted(key, ThreadState::get_calling_uid(), result.is_ok());
+ map_or_log_err(result, Ok)
}
}
diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs
index 3a4bf82..b35fe36 100644
--- a/keystore2/src/service.rs
+++ b/keystore2/src/service.rs
@@ -12,20 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// TODO remove when fully implemented.
-#![allow(unused_variables)]
-
//! This crate implement the core Keystore 2.0 service API as defined by the Keystore 2.0
//! AIDL spec.
use std::collections::HashMap;
-use crate::error::{self, map_or_log_err, ErrorCode};
+use crate::audit_log::log_key_deleted;
use crate::permission::{KeyPerm, KeystorePerm};
use crate::security_level::KeystoreSecurityLevel;
use crate::utils::{
check_grant_permission, check_key_permission, check_keystore_permission,
- key_parameters_to_authorizations, Asp,
+ key_parameters_to_authorizations, watchdog as wd,
};
use crate::{
database::Uuid,
@@ -36,41 +33,48 @@
database::{KeyEntryLoadBits, KeyType, SubComponentType},
error::ResponseCode,
};
+use crate::{
+ error::{self, map_or_log_err, ErrorCode},
+ id_rotation::IdRotationState,
+};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
+use android_hardware_security_keymint::binder::{BinderFeatures, Strong, ThreadState};
use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel,
IKeystoreService::BnKeystoreService, IKeystoreService::IKeystoreService,
KeyDescriptor::KeyDescriptor, KeyEntryResponse::KeyEntryResponse, KeyMetadata::KeyMetadata,
};
use anyhow::{Context, Result};
-use binder::{IBinder, Strong, ThreadState};
use error::Error;
use keystore2_selinux as selinux;
/// Implementation of the IKeystoreService.
#[derive(Default)]
pub struct KeystoreService {
- i_sec_level_by_uuid: HashMap<Uuid, Asp>,
+ i_sec_level_by_uuid: HashMap<Uuid, Strong<dyn IKeystoreSecurityLevel>>,
uuid_by_sec_level: HashMap<SecurityLevel, Uuid>,
}
impl KeystoreService {
/// Create a new instance of the Keystore 2.0 service.
- pub fn new_native_binder() -> Result<Strong<dyn IKeystoreService>> {
+ pub fn new_native_binder(
+ id_rotation_state: IdRotationState,
+ ) -> Result<Strong<dyn IKeystoreService>> {
let mut result: Self = Default::default();
- let (dev, uuid) =
- KeystoreSecurityLevel::new_native_binder(SecurityLevel::TRUSTED_ENVIRONMENT)
- .context(concat!(
- "In KeystoreService::new_native_binder: ",
- "Trying to construct mandatory security level TEE."
- ))
- .map(|(dev, uuid)| (Asp::new(dev.as_binder()), uuid))?;
+ let (dev, uuid) = KeystoreSecurityLevel::new_native_binder(
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ id_rotation_state.clone(),
+ )
+ .context(concat!(
+ "In KeystoreService::new_native_binder: ",
+ "Trying to construct mandatory security level TEE."
+ ))?;
result.i_sec_level_by_uuid.insert(uuid, dev);
result.uuid_by_sec_level.insert(SecurityLevel::TRUSTED_ENVIRONMENT, uuid);
// Strongbox is optional, so we ignore errors and turn the result into an Option.
- if let Ok((dev, uuid)) = KeystoreSecurityLevel::new_native_binder(SecurityLevel::STRONGBOX)
- .map(|(dev, uuid)| (Asp::new(dev.as_binder()), uuid))
+ if let Ok((dev, uuid)) =
+ KeystoreSecurityLevel::new_native_binder(SecurityLevel::STRONGBOX, id_rotation_state)
{
result.i_sec_level_by_uuid.insert(uuid, dev);
result.uuid_by_sec_level.insert(SecurityLevel::STRONGBOX, uuid);
@@ -85,9 +89,10 @@
"In KeystoreService::new_native_binder: Trying to initialize the legacy migrator.",
)?;
- let result = BnKeystoreService::new_binder(result);
- result.as_binder().set_requesting_sid(true);
- Ok(result)
+ Ok(BnKeystoreService::new_binder(
+ result,
+ BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
+ ))
}
fn uuid_to_sec_level(&self, uuid: &Uuid) -> SecurityLevel {
@@ -100,7 +105,7 @@
fn get_i_sec_level_by_uuid(&self, uuid: &Uuid) -> Result<Strong<dyn IKeystoreSecurityLevel>> {
if let Some(dev) = self.i_sec_level_by_uuid.get(uuid) {
- dev.get_interface().context("In get_i_sec_level_by_uuid.")
+ Ok(dev.clone())
} else {
Err(error::Error::sys())
.context("In get_i_sec_level_by_uuid: KeyMint instance for key not found.")
@@ -116,7 +121,7 @@
.get(&sec_level)
.and_then(|uuid| self.i_sec_level_by_uuid.get(uuid))
{
- dev.get_interface().context("In get_security_level.")
+ Ok(dev.clone())
} else {
Err(error::Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
.context("In get_security_level: No such security level.")
@@ -127,9 +132,9 @@
let caller_uid = ThreadState::get_calling_uid();
let (key_id_guard, mut key_entry) = DB
.with(|db| {
- LEGACY_MIGRATOR.with_try_migrate(&key, caller_uid, || {
+ LEGACY_MIGRATOR.with_try_migrate(key, caller_uid, || {
db.borrow_mut().load_key_entry(
- &key,
+ key,
KeyType::Client,
KeyEntryLoadBits::PUBLIC,
caller_uid,
@@ -178,9 +183,9 @@
) -> Result<()> {
let caller_uid = ThreadState::get_calling_uid();
DB.with::<_, Result<()>>(|db| {
- let entry = match LEGACY_MIGRATOR.with_try_migrate(&key, caller_uid, || {
+ let entry = match LEGACY_MIGRATOR.with_try_migrate(key, caller_uid, || {
db.borrow_mut().load_key_entry(
- &key,
+ key,
KeyType::Client,
KeyEntryLoadBits::NONE,
caller_uid,
@@ -199,7 +204,7 @@
.context("Failed to load key entry.")?;
let mut db = db.borrow_mut();
- if let Some((key_id_guard, key_entry)) = entry {
+ if let Some((key_id_guard, _key_entry)) = entry {
db.set_blob(&key_id_guard, SubComponentType::CERT, public_cert, None)
.context("Failed to update cert subcomponent.")?;
@@ -234,8 +239,13 @@
check_key_permission(KeyPerm::rebind(), &key, &None)
.context("Caller does not have permission to insert this certificate.")?;
- db.store_new_certificate(&key, certificate_chain.unwrap(), &KEYSTORE_UUID)
- .context("Failed to insert new certificate.")?;
+ db.store_new_certificate(
+ &key,
+ KeyType::Client,
+ certificate_chain.unwrap(),
+ &KEYSTORE_UUID,
+ )
+ .context("Failed to insert new certificate.")?;
Ok(())
})
.context("In update_subcomponent.")
@@ -284,7 +294,7 @@
&mut DB
.with(|db| {
let mut db = db.borrow_mut();
- db.list(k.domain, k.nspace)
+ db.list(k.domain, k.nspace, KeyType::Client)
})
.context("In list_entries: Trying to list keystore database.")?,
);
@@ -297,8 +307,8 @@
fn delete_key(&self, key: &KeyDescriptor) -> Result<()> {
let caller_uid = ThreadState::get_calling_uid();
DB.with(|db| {
- LEGACY_MIGRATOR.with_try_migrate(&key, caller_uid, || {
- db.borrow_mut().unbind_key(&key, KeyType::Client, caller_uid, |k, av| {
+ LEGACY_MIGRATOR.with_try_migrate(key, caller_uid, || {
+ db.borrow_mut().unbind_key(key, KeyType::Client, caller_uid, |k, av| {
check_key_permission(KeyPerm::delete(), k, &av).context("During delete_key.")
})
})
@@ -315,9 +325,9 @@
) -> Result<KeyDescriptor> {
let caller_uid = ThreadState::get_calling_uid();
DB.with(|db| {
- LEGACY_MIGRATOR.with_try_migrate(&key, caller_uid, || {
+ LEGACY_MIGRATOR.with_try_migrate(key, caller_uid, || {
db.borrow_mut().grant(
- &key,
+ key,
caller_uid,
grantee_uid as u32,
access_vector,
@@ -330,7 +340,7 @@
fn ungrant(&self, key: &KeyDescriptor, grantee_uid: i32) -> Result<()> {
DB.with(|db| {
- db.borrow_mut().ungrant(&key, ThreadState::get_calling_uid(), grantee_uid as u32, |k| {
+ db.borrow_mut().ungrant(key, ThreadState::get_calling_uid(), grantee_uid as u32, |k| {
check_key_permission(KeyPerm::grant(), k, &None)
})
})
@@ -347,9 +357,13 @@
&self,
security_level: SecurityLevel,
) -> binder::public_api::Result<Strong<dyn IKeystoreSecurityLevel>> {
+ let _wp = wd::watch_millis_with("IKeystoreService::getSecurityLevel", 500, move || {
+ format!("security_level: {}", security_level.0)
+ });
map_or_log_err(self.get_security_level(security_level), Ok)
}
fn getKeyEntry(&self, key: &KeyDescriptor) -> binder::public_api::Result<KeyEntryResponse> {
+ let _wp = wd::watch_millis("IKeystoreService::get_key_entry", 500);
map_or_log_err(self.get_key_entry(key), Ok)
}
fn updateSubcomponent(
@@ -358,6 +372,7 @@
public_cert: Option<&[u8]>,
certificate_chain: Option<&[u8]>,
) -> binder::public_api::Result<()> {
+ let _wp = wd::watch_millis("IKeystoreService::updateSubcomponent", 500);
map_or_log_err(self.update_subcomponent(key, public_cert, certificate_chain), Ok)
}
fn listEntries(
@@ -365,10 +380,14 @@
domain: Domain,
namespace: i64,
) -> binder::public_api::Result<Vec<KeyDescriptor>> {
+ let _wp = wd::watch_millis("IKeystoreService::listEntries", 500);
map_or_log_err(self.list_entries(domain, namespace), Ok)
}
fn deleteKey(&self, key: &KeyDescriptor) -> binder::public_api::Result<()> {
- map_or_log_err(self.delete_key(key), Ok)
+ let _wp = wd::watch_millis("IKeystoreService::deleteKey", 500);
+ let result = self.delete_key(key);
+ log_key_deleted(key, ThreadState::get_calling_uid(), result.is_ok());
+ map_or_log_err(result, Ok)
}
fn grant(
&self,
@@ -376,9 +395,11 @@
grantee_uid: i32,
access_vector: i32,
) -> binder::public_api::Result<KeyDescriptor> {
+ let _wp = wd::watch_millis("IKeystoreService::grant", 500);
map_or_log_err(self.grant(key, grantee_uid, access_vector.into()), Ok)
}
fn ungrant(&self, key: &KeyDescriptor, grantee_uid: i32) -> binder::public_api::Result<()> {
+ let _wp = wd::watch_millis("IKeystoreService::ungrant", 500);
map_or_log_err(self.ungrant(key, grantee_uid), Ok)
}
}
diff --git a/keystore2/src/shared_secret_negotiation.rs b/keystore2/src/shared_secret_negotiation.rs
new file mode 100644
index 0000000..1862f73
--- /dev/null
+++ b/keystore2/src/shared_secret_negotiation.rs
@@ -0,0 +1,280 @@
+// 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.
+
+//! This module implements the shared secret negotiation.
+
+use crate::error::{map_binder_status, map_binder_status_code, Error};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
+use android_hardware_security_keymint::binder::Strong;
+use android_hardware_security_sharedsecret::aidl::android::hardware::security::sharedsecret::{
+ ISharedSecret::ISharedSecret, SharedSecretParameters::SharedSecretParameters,
+};
+use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
+use anyhow::Result;
+use keystore2_vintf::{get_aidl_instances, get_hidl_instances};
+use std::fmt::{self, Display, Formatter};
+use std::time::Duration;
+
+/// This function initiates the shared secret negotiation. It starts a thread and then returns
+/// immediately. The thread consults the vintf manifest to enumerate expected negotiation
+/// participants. It then attempts to connect to all of these participants. If any connection
+/// fails the thread will retry once per second to connect to the failed instance(s) until all of
+/// the instances are connected. It then performs the negotiation.
+///
+/// During the first phase of the negotiation it will again try every second until
+/// all instances have responded successfully to account for instances that register early but
+/// are not fully functioning at this time due to hardware delays or boot order dependency issues.
+/// An error during the second phase or a checksum mismatch leads to a panic.
+pub fn perform_shared_secret_negotiation() {
+ std::thread::spawn(|| {
+ let participants = list_participants()
+ .expect("In perform_shared_secret_negotiation: Trying to list participants.");
+ let connected = connect_participants(participants);
+ negotiate_shared_secret(connected);
+ log::info!("Shared secret negotiation concluded successfully.");
+ });
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+enum SharedSecretParticipant {
+ /// Represents an instance of android.hardware.security.sharedsecret.ISharedSecret.
+ Aidl(String),
+ /// In the legacy case there can be at most one TEE and one Strongbox hal.
+ Hidl { is_strongbox: bool, version: (usize, usize) },
+}
+
+impl Display for SharedSecretParticipant {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ match self {
+ Self::Aidl(instance) => write!(
+ f,
+ "{}.{}/{}",
+ SHARED_SECRET_PACKAGE_NAME, SHARED_SECRET_INTERFACE_NAME, instance
+ ),
+ Self::Hidl { is_strongbox, version: (ma, mi) } => write!(
+ f,
+ "{}@V{}.{}::{}/{}",
+ KEYMASTER_PACKAGE_NAME,
+ ma,
+ mi,
+ KEYMASTER_INTERFACE_NAME,
+ if *is_strongbox { "strongbox" } else { "default" }
+ ),
+ }
+ }
+}
+
+#[derive(thiserror::Error, Debug)]
+enum SharedSecretError {
+ #[error("Shared parameter retrieval failed on instance {p} with error {e:?}.")]
+ ParameterRetrieval { e: Error, p: SharedSecretParticipant },
+ #[error("Shared secret computation failed on instance {p} with error {e:?}.")]
+ Computation { e: Error, p: SharedSecretParticipant },
+ #[error("Checksum comparison failed on instance {0}.")]
+ Checksum(SharedSecretParticipant),
+}
+
+fn filter_map_legacy_km_instances(
+ name: String,
+ version: (usize, usize),
+) -> Option<SharedSecretParticipant> {
+ match name.as_str() {
+ "default" => Some(SharedSecretParticipant::Hidl { is_strongbox: false, version }),
+ "strongbox" => Some(SharedSecretParticipant::Hidl { is_strongbox: true, version }),
+ _ => {
+ log::warn!("Found unexpected keymaster instance: \"{}\"", name);
+ log::warn!("Device is misconfigured. Allowed instances are:");
+ log::warn!(" * default");
+ log::warn!(" * strongbox");
+ None
+ }
+ }
+}
+
+static KEYMASTER_PACKAGE_NAME: &str = "android.hardware.keymaster";
+static KEYMASTER_INTERFACE_NAME: &str = "IKeymasterDevice";
+static SHARED_SECRET_PACKAGE_NAME: &str = "android.hardware.security.sharedsecret";
+static SHARED_SECRET_INTERFACE_NAME: &str = "ISharedSecret";
+static COMPAT_PACKAGE_NAME: &str = "android.security.compat";
+
+/// Lists participants.
+fn list_participants() -> Result<Vec<SharedSecretParticipant>> {
+ // 4.1 implementation always also register as 4.0. So only the highest version of each
+ // "default" and "strongbox" makes the cut.
+ let mut legacy_default_found: bool = false;
+ let mut legacy_strongbox_found: bool = false;
+ Ok([(4, 1), (4, 0)]
+ .iter()
+ .map(|(ma, mi)| {
+ get_hidl_instances(KEYMASTER_PACKAGE_NAME, *ma, *mi, KEYMASTER_INTERFACE_NAME)
+ .into_iter()
+ .filter_map(|name| {
+ filter_map_legacy_km_instances(name, (*ma, *mi)).and_then(|sp| {
+ if let SharedSecretParticipant::Hidl { is_strongbox: true, .. } = &sp {
+ if !legacy_strongbox_found {
+ legacy_strongbox_found = true;
+ return Some(sp);
+ }
+ } else if !legacy_default_found {
+ legacy_default_found = true;
+ return Some(sp);
+ }
+ None
+ })
+ })
+ .collect::<Vec<SharedSecretParticipant>>()
+ })
+ .into_iter()
+ .flatten()
+ .chain({
+ get_aidl_instances(SHARED_SECRET_PACKAGE_NAME, 1, SHARED_SECRET_INTERFACE_NAME)
+ .into_iter()
+ .map(SharedSecretParticipant::Aidl)
+ .collect::<Vec<_>>()
+ .into_iter()
+ })
+ .collect())
+}
+
+fn connect_participants(
+ mut participants: Vec<SharedSecretParticipant>,
+) -> Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)> {
+ let mut connected_participants: Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)> =
+ vec![];
+ loop {
+ let (connected, not_connected) = participants.into_iter().fold(
+ (connected_participants, vec![]),
+ |(mut connected, mut failed), e| {
+ match e {
+ SharedSecretParticipant::Aidl(instance_name) => {
+ let service_name = format!(
+ "{}.{}/{}",
+ SHARED_SECRET_PACKAGE_NAME, SHARED_SECRET_INTERFACE_NAME, instance_name
+ );
+ match map_binder_status_code(binder::get_interface(&service_name)) {
+ Err(e) => {
+ log::warn!(
+ "Unable to connect \"{}\" with error:\n{:?}\nRetrying later.",
+ service_name,
+ e
+ );
+ failed.push(SharedSecretParticipant::Aidl(instance_name));
+ }
+ Ok(service) => connected
+ .push((service, SharedSecretParticipant::Aidl(instance_name))),
+ }
+ }
+ SharedSecretParticipant::Hidl { is_strongbox, version } => {
+ // This is a no-op if it was called before.
+ keystore2_km_compat::add_keymint_device_service();
+
+ // If we cannot connect to the compatibility service there is no way to
+ // recover.
+ // PANIC! - Unless you brought your towel.
+ let keystore_compat_service: Strong<dyn IKeystoreCompatService> =
+ map_binder_status_code(binder::get_interface(COMPAT_PACKAGE_NAME))
+ .expect(
+ "In connect_participants: Trying to connect to compat service.",
+ );
+
+ match map_binder_status(keystore_compat_service.getSharedSecret(
+ if is_strongbox {
+ SecurityLevel::STRONGBOX
+ } else {
+ SecurityLevel::TRUSTED_ENVIRONMENT
+ },
+ )) {
+ Err(e) => {
+ log::warn!(
+ concat!(
+ "Unable to connect keymaster device \"{}\" ",
+ "with error:\n{:?}\nRetrying later."
+ ),
+ if is_strongbox { "strongbox" } else { "TEE" },
+ e
+ );
+ failed
+ .push(SharedSecretParticipant::Hidl { is_strongbox, version });
+ }
+ Ok(service) => connected.push((
+ service,
+ SharedSecretParticipant::Hidl { is_strongbox, version },
+ )),
+ }
+ }
+ }
+ (connected, failed)
+ },
+ );
+ participants = not_connected;
+ connected_participants = connected;
+ if participants.is_empty() {
+ break;
+ }
+ std::thread::sleep(Duration::from_millis(1000));
+ }
+ connected_participants
+}
+
+fn negotiate_shared_secret(
+ participants: Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)>,
+) {
+ // Phase 1: Get the sharing parameters from all participants.
+ let mut params = loop {
+ let result: Result<Vec<SharedSecretParameters>, SharedSecretError> = participants
+ .iter()
+ .map(|(s, p)| {
+ map_binder_status(s.getSharedSecretParameters())
+ .map_err(|e| SharedSecretError::ParameterRetrieval { e, p: (*p).clone() })
+ })
+ .collect();
+
+ match result {
+ Err(e) => {
+ log::warn!("{:?}", e);
+ log::warn!("Retrying in one second.");
+ std::thread::sleep(Duration::from_millis(1000));
+ }
+ Ok(params) => break params,
+ }
+ };
+
+ params.sort_unstable();
+
+ // Phase 2: Send the sorted sharing parameters to all participants.
+ let negotiation_result = participants.into_iter().try_fold(None, |acc, (s, p)| {
+ match (acc, map_binder_status(s.computeSharedSecret(¶ms))) {
+ (None, Ok(new_sum)) => Ok(Some(new_sum)),
+ (Some(old_sum), Ok(new_sum)) => {
+ if old_sum == new_sum {
+ Ok(Some(old_sum))
+ } else {
+ Err(SharedSecretError::Checksum(p))
+ }
+ }
+ (_, Err(e)) => Err(SharedSecretError::Computation { e, p }),
+ }
+ });
+
+ if let Err(e) = negotiation_result {
+ log::error!("In negotiate_shared_secret: {:?}.", e);
+ if let SharedSecretError::Checksum(_) = e {
+ log::error!(concat!(
+ "This means that this device is NOT PROVISIONED CORRECTLY.\n",
+ "User authorization and other security functions will not work\n",
+ "as expected. Please contact your OEM for instructions.",
+ ));
+ }
+ }
+}
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index 5ee685a..a1e4c48 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -12,29 +12,228 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#![allow(dead_code)]
-
use crate::{
- database::BlobMetaData, database::BlobMetaEntry, database::EncryptedBy, database::KeyEntry,
- database::KeyType, database::KeystoreDB, enforcements::Enforcements, error::Error,
- error::ResponseCode, key_parameter::KeyParameter, legacy_blob::LegacyBlobLoader,
+ boot_level_keys::{get_level_zero_key, BootLevelKeyCache},
+ database::BlobMetaData,
+ database::BlobMetaEntry,
+ database::EncryptedBy,
+ database::KeyEntry,
+ database::KeyType,
+ database::{KeyEntryLoadBits, KeyIdGuard, KeyMetaData, KeyMetaEntry, KeystoreDB},
+ ec_crypto::ECDHPrivateKey,
+ enforcements::Enforcements,
+ error::Error,
+ error::ResponseCode,
+ key_parameter::{KeyParameter, KeyParameterValue},
+ legacy_blob::LegacyBlobLoader,
legacy_migrator::LegacyMigrator,
+ raw_device::KeyMintDevice,
+ try_insert::TryInsert,
+ utils::watchdog as wd,
+ utils::AID_KEYSTORE,
};
-use android_system_keystore2::aidl::android::system::keystore2::Domain::Domain;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, BlockMode::BlockMode, HardwareAuthToken::HardwareAuthToken,
+ HardwareAuthenticatorType::HardwareAuthenticatorType, KeyFormat::KeyFormat,
+ KeyParameter::KeyParameter as KmKeyParameter, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
+ SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, KeyDescriptor::KeyDescriptor,
+};
use anyhow::{Context, Result};
use keystore2_crypto::{
- aes_gcm_decrypt, aes_gcm_encrypt, derive_key_from_password, generate_aes256_key, generate_salt,
- ZVec, AES_256_KEY_LENGTH,
+ aes_gcm_decrypt, aes_gcm_encrypt, generate_aes256_key, generate_salt, Password, ZVec,
+ AES_256_KEY_LENGTH,
};
-use std::ops::Deref;
+use rustutils::system_properties::PropertyWatcher;
use std::{
collections::HashMap,
sync::Arc,
sync::{Mutex, Weak},
};
+use std::{convert::TryFrom, ops::Deref};
+
+const MAX_MAX_BOOT_LEVEL: usize = 1_000_000_000;
+/// Allow up to 15 seconds between the user unlocking using a biometric, and the auth
+/// token being used to unlock in [`SuperKeyManager::try_unlock_user_with_biometric`].
+/// This seems short enough for security purposes, while long enough that even the
+/// very slowest device will present the auth token in time.
+const BIOMETRIC_AUTH_TIMEOUT_S: i32 = 15; // seconds
type UserId = u32;
+/// Encryption algorithm used by a particular type of superencryption key
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum SuperEncryptionAlgorithm {
+ /// Symmetric encryption with AES-256-GCM
+ Aes256Gcm,
+ /// Public-key encryption with ECDH P-521
+ EcdhP521,
+}
+
+/// A particular user may have several superencryption keys in the database, each for a
+/// different purpose, distinguished by alias. Each is associated with a static
+/// constant of this type.
+pub struct SuperKeyType {
+ /// Alias used to look the key up in the `persistent.keyentry` table.
+ pub alias: &'static str,
+ /// Encryption algorithm
+ pub algorithm: SuperEncryptionAlgorithm,
+}
+
+/// Key used for LskfLocked keys; the corresponding superencryption key is loaded in memory
+/// when the user first unlocks, and remains in memory until the device reboots.
+pub const USER_SUPER_KEY: SuperKeyType =
+ SuperKeyType { alias: "USER_SUPER_KEY", algorithm: SuperEncryptionAlgorithm::Aes256Gcm };
+/// Key used for ScreenLockBound keys; the corresponding superencryption key is loaded in memory
+/// each time the user enters their LSKF, and cleared from memory each time the device is locked.
+/// Symmetric.
+pub const USER_SCREEN_LOCK_BOUND_KEY: SuperKeyType = SuperKeyType {
+ alias: "USER_SCREEN_LOCK_BOUND_KEY",
+ algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
+};
+/// Key used for ScreenLockBound keys; the corresponding superencryption key is loaded in memory
+/// each time the user enters their LSKF, and cleared from memory each time the device is locked.
+/// Asymmetric, so keys can be encrypted when the device is locked.
+pub const USER_SCREEN_LOCK_BOUND_P521_KEY: SuperKeyType = SuperKeyType {
+ alias: "USER_SCREEN_LOCK_BOUND_P521_KEY",
+ algorithm: SuperEncryptionAlgorithm::EcdhP521,
+};
+
+/// Superencryption to apply to a new key.
+#[derive(Debug, Clone, Copy)]
+pub enum SuperEncryptionType {
+ /// Do not superencrypt this key.
+ None,
+ /// Superencrypt with a key that remains in memory from first unlock to reboot.
+ LskfBound,
+ /// Superencrypt with a key cleared from memory when the device is locked.
+ ScreenLockBound,
+ /// Superencrypt with a key based on the desired boot level
+ BootLevel(i32),
+}
+
+#[derive(Debug, Clone, Copy)]
+pub enum SuperKeyIdentifier {
+ /// id of the super key in the database.
+ DatabaseId(i64),
+ /// Boot level of the encrypting boot level key
+ BootLevel(i32),
+}
+
+impl SuperKeyIdentifier {
+ fn from_metadata(metadata: &BlobMetaData) -> Option<Self> {
+ if let Some(EncryptedBy::KeyId(key_id)) = metadata.encrypted_by() {
+ Some(SuperKeyIdentifier::DatabaseId(*key_id))
+ } else {
+ metadata.max_boot_level().map(|boot_level| SuperKeyIdentifier::BootLevel(*boot_level))
+ }
+ }
+
+ fn add_to_metadata(&self, metadata: &mut BlobMetaData) {
+ match self {
+ SuperKeyIdentifier::DatabaseId(id) => {
+ metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(*id)));
+ }
+ SuperKeyIdentifier::BootLevel(level) => {
+ metadata.add(BlobMetaEntry::MaxBootLevel(*level));
+ }
+ }
+ }
+}
+
+pub struct SuperKey {
+ algorithm: SuperEncryptionAlgorithm,
+ key: ZVec,
+ /// Identifier of the encrypting key, used to write an encrypted blob
+ /// back to the database after re-encryption eg on a key update.
+ id: SuperKeyIdentifier,
+ /// ECDH is more expensive than AES. So on ECDH private keys we set the
+ /// reencrypt_with field to point at the corresponding AES key, and the
+ /// keys will be re-encrypted with AES on first use.
+ reencrypt_with: Option<Arc<SuperKey>>,
+}
+
+impl SuperKey {
+ /// For most purposes `unwrap_key` handles decryption,
+ /// but legacy handling and some tests need to assume AES and decrypt directly.
+ pub fn aes_gcm_decrypt(&self, data: &[u8], iv: &[u8], tag: &[u8]) -> Result<ZVec> {
+ if self.algorithm == SuperEncryptionAlgorithm::Aes256Gcm {
+ aes_gcm_decrypt(data, iv, tag, &self.key)
+ .context("In aes_gcm_decrypt: decryption failed")
+ } else {
+ Err(Error::sys()).context("In aes_gcm_decrypt: Key is not an AES key")
+ }
+ }
+}
+
+/// A SuperKey that has been encrypted with an AES-GCM key. For
+/// encryption the key is in memory, and for decryption it is in KM.
+struct LockedKey {
+ algorithm: SuperEncryptionAlgorithm,
+ id: SuperKeyIdentifier,
+ nonce: Vec<u8>,
+ ciphertext: Vec<u8>, // with tag appended
+}
+
+impl LockedKey {
+ fn new(key: &[u8], to_encrypt: &Arc<SuperKey>) -> Result<Self> {
+ let (mut ciphertext, nonce, mut tag) = aes_gcm_encrypt(&to_encrypt.key, key)?;
+ ciphertext.append(&mut tag);
+ Ok(LockedKey { algorithm: to_encrypt.algorithm, id: to_encrypt.id, nonce, ciphertext })
+ }
+
+ fn decrypt(
+ &self,
+ db: &mut KeystoreDB,
+ km_dev: &KeyMintDevice,
+ key_id_guard: &KeyIdGuard,
+ key_entry: &KeyEntry,
+ auth_token: &HardwareAuthToken,
+ reencrypt_with: Option<Arc<SuperKey>>,
+ ) -> Result<Arc<SuperKey>> {
+ let key_blob = key_entry
+ .key_blob_info()
+ .as_ref()
+ .map(|(key_blob, _)| KeyBlob::Ref(key_blob))
+ .ok_or(Error::Rc(ResponseCode::KEY_NOT_FOUND))
+ .context("In LockedKey::decrypt: Missing key blob info.")?;
+ let key_params = vec![
+ KeyParameterValue::Algorithm(Algorithm::AES),
+ KeyParameterValue::KeySize(256),
+ KeyParameterValue::BlockMode(BlockMode::GCM),
+ KeyParameterValue::PaddingMode(PaddingMode::NONE),
+ KeyParameterValue::Nonce(self.nonce.clone()),
+ KeyParameterValue::MacLength(128),
+ ];
+ let key_params: Vec<KmKeyParameter> = key_params.into_iter().map(|x| x.into()).collect();
+ let key = ZVec::try_from(km_dev.use_key_in_one_step(
+ db,
+ key_id_guard,
+ &key_blob,
+ KeyPurpose::DECRYPT,
+ &key_params,
+ Some(auth_token),
+ &self.ciphertext,
+ )?)?;
+ Ok(Arc::new(SuperKey { algorithm: self.algorithm, key, id: self.id, reencrypt_with }))
+ }
+}
+
+/// Keys for unlocking UNLOCKED_DEVICE_REQUIRED keys, as LockedKeys, complete with
+/// a database descriptor for the encrypting key and the sids for the auth tokens
+/// that can be used to decrypt it.
+struct BiometricUnlock {
+ /// List of auth token SIDs that can be used to unlock these keys.
+ sids: Vec<i64>,
+ /// Database descriptor of key to use to unlock.
+ key_desc: KeyDescriptor,
+ /// Locked versions of the matching UserSuperKeys fields
+ screen_lock_bound: LockedKey,
+ screen_lock_bound_private: LockedKey,
+}
+
#[derive(Default)]
struct UserSuperKeys {
/// The per boot key is used for LSKF binding of authentication bound keys. There is one
@@ -42,34 +241,36 @@
/// secret, that is itself derived from the user's lock screen knowledge factor (LSKF).
/// When the user unlocks the device for the first time, this key is unlocked, i.e., decrypted,
/// and stays memory resident until the device reboots.
- per_boot: Option<SuperKey>,
+ per_boot: Option<Arc<SuperKey>>,
/// The screen lock key works like the per boot key with the distinction that it is cleared
/// from memory when the screen lock is engaged.
- /// TODO the life cycle is not fully implemented at this time.
- screen_lock: Option<Arc<ZVec>>,
-}
-
-#[derive(Default, Clone)]
-pub struct SuperKey {
- key: Arc<ZVec>,
- // id of the super key in the database.
- id: i64,
-}
-
-impl SuperKey {
- pub fn get_key(&self) -> &Arc<ZVec> {
- &self.key
- }
-
- pub fn get_id(&self) -> i64 {
- self.id
- }
+ screen_lock_bound: Option<Arc<SuperKey>>,
+ /// When the device is locked, screen-lock-bound keys can still be encrypted, using
+ /// ECDH public-key encryption. This field holds the decryption private key.
+ screen_lock_bound_private: Option<Arc<SuperKey>>,
+ /// Versions of the above two keys, locked behind a biometric.
+ biometric_unlock: Option<BiometricUnlock>,
}
#[derive(Default)]
struct SkmState {
user_keys: HashMap<UserId, UserSuperKeys>,
- key_index: HashMap<i64, Weak<ZVec>>,
+ key_index: HashMap<i64, Weak<SuperKey>>,
+ boot_level_key_cache: Option<BootLevelKeyCache>,
+}
+
+impl SkmState {
+ fn add_key_to_key_index(&mut self, super_key: &Arc<SuperKey>) -> Result<()> {
+ if let SuperKeyIdentifier::DatabaseId(id) = super_key.id {
+ self.key_index.insert(id, Arc::downgrade(super_key));
+ Ok(())
+ } else {
+ Err(Error::sys()).context(format!(
+ "In add_key_to_key_index: cannot add key with ID {:?}",
+ super_key.id
+ ))
+ }
+ }
}
#[derive(Default)]
@@ -78,22 +279,66 @@
}
impl SuperKeyManager {
- pub fn new() -> Self {
- Self { data: Mutex::new(Default::default()) }
+ pub fn set_up_boot_level_cache(self: &Arc<Self>, db: &mut KeystoreDB) -> Result<()> {
+ let mut data = self.data.lock().unwrap();
+ if data.boot_level_key_cache.is_some() {
+ log::info!("In set_up_boot_level_cache: called for a second time");
+ return Ok(());
+ }
+ let level_zero_key = get_level_zero_key(db)
+ .context("In set_up_boot_level_cache: get_level_zero_key failed")?;
+ data.boot_level_key_cache = Some(BootLevelKeyCache::new(level_zero_key));
+ log::info!("Starting boot level watcher.");
+ let clone = self.clone();
+ std::thread::spawn(move || {
+ clone
+ .watch_boot_level()
+ .unwrap_or_else(|e| log::error!("watch_boot_level failed:\n{:?}", e));
+ });
+ Ok(())
}
- pub fn forget_screen_lock_key_for_user(&self, user: UserId) {
- let mut data = self.data.lock().unwrap();
- if let Some(usk) = data.user_keys.get_mut(&user) {
- usk.screen_lock = None;
+ /// Watch the `keystore.boot_level` system property, and keep boot level up to date.
+ /// Blocks waiting for system property changes, so must be run in its own thread.
+ fn watch_boot_level(&self) -> Result<()> {
+ let mut w = PropertyWatcher::new("keystore.boot_level")
+ .context("In watch_boot_level: PropertyWatcher::new failed")?;
+ loop {
+ let level = w
+ .read(|_n, v| v.parse::<usize>().map_err(std::convert::Into::into))
+ .context("In watch_boot_level: read of property failed")?;
+ // watch_boot_level should only be called once data.boot_level_key_cache is Some,
+ // so it's safe to unwrap in the branches below.
+ if level < MAX_MAX_BOOT_LEVEL {
+ log::info!("Read keystore.boot_level value {}", level);
+ let mut data = self.data.lock().unwrap();
+ data.boot_level_key_cache
+ .as_mut()
+ .unwrap()
+ .advance_boot_level(level)
+ .context("In watch_boot_level: advance_boot_level failed")?;
+ } else {
+ log::info!(
+ "keystore.boot_level {} hits maximum {}, finishing.",
+ level,
+ MAX_MAX_BOOT_LEVEL
+ );
+ let mut data = self.data.lock().unwrap();
+ data.boot_level_key_cache.as_mut().unwrap().finish();
+ break;
+ }
+ w.wait().context("In watch_boot_level: property wait failed")?;
}
+ Ok(())
}
- pub fn forget_screen_lock_keys(&self) {
- let mut data = self.data.lock().unwrap();
- for (_, usk) in data.user_keys.iter_mut() {
- usk.screen_lock = None;
- }
+ pub fn level_accessible(&self, boot_level: i32) -> bool {
+ self.data
+ .lock()
+ .unwrap()
+ .boot_level_key_cache
+ .as_ref()
+ .map_or(false, |c| c.level_accessible(boot_level as usize))
}
pub fn forget_all_keys_for_user(&self, user: UserId) {
@@ -101,25 +346,39 @@
data.user_keys.remove(&user);
}
- pub fn forget_all_keys(&self) {
+ fn install_per_boot_key_for_user(&self, user: UserId, super_key: Arc<SuperKey>) -> Result<()> {
let mut data = self.data.lock().unwrap();
- data.user_keys.clear();
- data.key_index.clear();
- }
-
- fn install_per_boot_key_for_user(&self, user: UserId, super_key: SuperKey) {
- let mut data = self.data.lock().unwrap();
- data.key_index.insert(super_key.id, Arc::downgrade(&(super_key.key)));
+ data.add_key_to_key_index(&super_key)
+ .context("In install_per_boot_key_for_user: add_key_to_key_index failed")?;
data.user_keys.entry(user).or_default().per_boot = Some(super_key);
+ Ok(())
}
- fn get_key(&self, key_id: &i64) -> Option<Arc<ZVec>> {
- self.data.lock().unwrap().key_index.get(key_id).and_then(|k| k.upgrade())
+ fn lookup_key(&self, key_id: &SuperKeyIdentifier) -> Result<Option<Arc<SuperKey>>> {
+ let mut data = self.data.lock().unwrap();
+ Ok(match key_id {
+ SuperKeyIdentifier::DatabaseId(id) => data.key_index.get(id).and_then(|k| k.upgrade()),
+ SuperKeyIdentifier::BootLevel(level) => data
+ .boot_level_key_cache
+ .as_mut()
+ .map(|b| b.aes_key(*level as usize))
+ .transpose()
+ .context("In lookup_key: aes_key failed")?
+ .flatten()
+ .map(|key| {
+ Arc::new(SuperKey {
+ algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
+ key,
+ id: *key_id,
+ reencrypt_with: None,
+ })
+ }),
+ })
}
- pub fn get_per_boot_key_by_user_id(&self, user_id: u32) -> Option<SuperKey> {
+ pub fn get_per_boot_key_by_user_id(&self, user_id: UserId) -> Option<Arc<SuperKey>> {
let data = self.data.lock().unwrap();
- data.user_keys.get(&user_id).map(|e| e.per_boot.clone()).flatten()
+ data.user_keys.get(&user_id).and_then(|e| e.per_boot.as_ref().cloned())
}
/// This function unlocks the super keys for a given user.
@@ -130,14 +389,14 @@
&self,
db: &mut KeystoreDB,
user: UserId,
- pw: &[u8],
+ pw: &Password,
legacy_blob_loader: &LegacyBlobLoader,
) -> Result<()> {
let (_, entry) = db
.get_or_create_key_with(
Domain::APP,
user as u64 as i64,
- KeystoreDB::USER_SUPER_KEY_ALIAS,
+ USER_SUPER_KEY.alias,
crate::database::KEYSTORE_UUID,
|| {
// For backward compatibility we need to check if there is a super key present.
@@ -162,42 +421,74 @@
)
.context("In unlock_user_key: Failed to get key id.")?;
- self.populate_cache_from_super_key_blob(user, entry, pw).context("In unlock_user_key.")?;
+ self.populate_cache_from_super_key_blob(user, USER_SUPER_KEY.algorithm, entry, pw)
+ .context("In unlock_user_key.")?;
Ok(())
}
- /// Unwraps an encrypted key blob given metadata identifying the encryption key.
- /// The function queries `metadata.encrypted_by()` to determine the encryption key.
- /// It then check if the required key is memory resident, and if so decrypts the
- /// blob.
- pub fn unwrap_key<'a>(&self, blob: &'a [u8], metadata: &BlobMetaData) -> Result<KeyBlob<'a>> {
- match metadata.encrypted_by() {
- Some(EncryptedBy::KeyId(key_id)) => match self.get_key(key_id) {
- Some(key) => Ok(KeyBlob::Sensitive(
- Self::unwrap_key_with_key(blob, metadata, &key).context("In unwrap_key.")?,
- SuperKey { key: key.clone(), id: *key_id },
- )),
- None => Err(Error::Rc(ResponseCode::LOCKED))
- .context("In unwrap_key: Key is not usable until the user entered their LSKF."),
- },
- _ => Err(Error::Rc(ResponseCode::VALUE_CORRUPTED))
- .context("In unwrap_key: Cannot determined wrapping key."),
- }
+ /// Check if a given key is super-encrypted, from its metadata. If so, unwrap the key using
+ /// the relevant super key.
+ pub fn unwrap_key_if_required<'a>(
+ &self,
+ metadata: &BlobMetaData,
+ blob: &'a [u8],
+ ) -> Result<KeyBlob<'a>> {
+ Ok(if let Some(key_id) = SuperKeyIdentifier::from_metadata(metadata) {
+ let super_key = self
+ .lookup_key(&key_id)
+ .context("In unwrap_key: lookup_key failed")?
+ .ok_or(Error::Rc(ResponseCode::LOCKED))
+ .context("In unwrap_key: Required super decryption key is not in memory.")?;
+ KeyBlob::Sensitive {
+ key: Self::unwrap_key_with_key(blob, metadata, &super_key)
+ .context("In unwrap_key: unwrap_key_with_key failed")?,
+ reencrypt_with: super_key.reencrypt_with.as_ref().unwrap_or(&super_key).clone(),
+ force_reencrypt: super_key.reencrypt_with.is_some(),
+ }
+ } else {
+ KeyBlob::Ref(blob)
+ })
}
/// Unwraps an encrypted key blob given an encryption key.
- fn unwrap_key_with_key(blob: &[u8], metadata: &BlobMetaData, key: &[u8]) -> Result<ZVec> {
- match (metadata.iv(), metadata.aead_tag()) {
- (Some(iv), Some(tag)) => aes_gcm_decrypt(blob, iv, tag, key)
- .context("In unwrap_key_with_key: Failed to decrypt the key blob."),
- (iv, tag) => Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
- concat!(
- "In unwrap_key_with_key: Key has incomplete metadata.",
- "Present: iv: {}, aead_tag: {}."
- ),
- iv.is_some(),
- tag.is_some(),
- )),
+ fn unwrap_key_with_key(blob: &[u8], metadata: &BlobMetaData, key: &SuperKey) -> Result<ZVec> {
+ match key.algorithm {
+ SuperEncryptionAlgorithm::Aes256Gcm => match (metadata.iv(), metadata.aead_tag()) {
+ (Some(iv), Some(tag)) => key
+ .aes_gcm_decrypt(blob, iv, tag)
+ .context("In unwrap_key_with_key: Failed to decrypt the key blob."),
+ (iv, tag) => Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
+ concat!(
+ "In unwrap_key_with_key: Key has incomplete metadata.",
+ "Present: iv: {}, aead_tag: {}."
+ ),
+ iv.is_some(),
+ tag.is_some(),
+ )),
+ },
+ SuperEncryptionAlgorithm::EcdhP521 => {
+ match (metadata.public_key(), metadata.salt(), metadata.iv(), metadata.aead_tag()) {
+ (Some(public_key), Some(salt), Some(iv), Some(aead_tag)) => {
+ ECDHPrivateKey::from_private_key(&key.key)
+ .and_then(|k| k.decrypt_message(public_key, salt, iv, blob, aead_tag))
+ .context(
+ "In unwrap_key_with_key: Failed to decrypt the key blob with ECDH.",
+ )
+ }
+ (public_key, salt, iv, aead_tag) => {
+ Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
+ concat!(
+ "In unwrap_key_with_key: Key has incomplete metadata.",
+ "Present: public_key: {}, salt: {}, iv: {}, aead_tag: {}."
+ ),
+ public_key.is_some(),
+ salt.is_some(),
+ iv.is_some(),
+ aead_tag.is_some(),
+ ))
+ }
+ }
+ }
}
}
@@ -205,15 +496,10 @@
pub fn super_key_exists_in_db_for_user(
db: &mut KeystoreDB,
legacy_migrator: &LegacyMigrator,
- user_id: u32,
+ user_id: UserId,
) -> Result<bool> {
let key_in_db = db
- .key_exists(
- Domain::APP,
- user_id as u64 as i64,
- KeystoreDB::USER_SUPER_KEY_ALIAS,
- KeyType::Super,
- )
+ .key_exists(Domain::APP, user_id as u64 as i64, USER_SUPER_KEY.alias, KeyType::Super)
.context("In super_key_exists_in_db_for_user.")?;
if key_in_db {
@@ -232,17 +518,18 @@
&self,
db: &mut KeystoreDB,
legacy_migrator: &LegacyMigrator,
- user_id: u32,
- pw: &[u8],
+ user_id: UserId,
+ pw: &Password,
) -> Result<UserState> {
+ let alias = &USER_SUPER_KEY;
let result = legacy_migrator
- .with_try_migrate_super_key(user_id, pw, || db.load_super_key(user_id))
+ .with_try_migrate_super_key(user_id, pw, || db.load_super_key(alias, user_id))
.context("In check_and_unlock_super_key. Failed to load super key")?;
match result {
Some((_, entry)) => {
let super_key = self
- .populate_cache_from_super_key_blob(user_id, entry, pw)
+ .populate_cache_from_super_key_blob(user_id, alias.algorithm, entry, pw)
.context("In check_and_unlock_super_key.")?;
Ok(UserState::LskfUnlocked(super_key))
}
@@ -260,8 +547,8 @@
&self,
db: &mut KeystoreDB,
legacy_migrator: &LegacyMigrator,
- user_id: u32,
- pw: Option<&[u8]>,
+ user_id: UserId,
+ pw: Option<&Password>,
) -> Result<UserState> {
let super_key_exists_in_db =
Self::super_key_exists_in_db_for_user(db, legacy_migrator, user_id).context(
@@ -279,11 +566,22 @@
.context("In check_and_initialize_super_key.")?;
let key_entry = db
- .store_super_key(user_id, &(&encrypted_super_key, &blob_metadata))
+ .store_super_key(
+ user_id,
+ &USER_SUPER_KEY,
+ &encrypted_super_key,
+ &blob_metadata,
+ &KeyMetaData::new(),
+ )
.context("In check_and_initialize_super_key. Failed to store super key.")?;
let super_key = self
- .populate_cache_from_super_key_blob(user_id, key_entry, pw)
+ .populate_cache_from_super_key_blob(
+ user_id,
+ USER_SUPER_KEY.algorithm,
+ key_entry,
+ pw,
+ )
.context("In check_and_initialize_super_key.")?;
Ok(UserState::LskfUnlocked(super_key))
} else {
@@ -294,19 +592,26 @@
//helper function to populate super key cache from the super key blob loaded from the database
fn populate_cache_from_super_key_blob(
&self,
- user_id: u32,
+ user_id: UserId,
+ algorithm: SuperEncryptionAlgorithm,
entry: KeyEntry,
- pw: &[u8],
- ) -> Result<SuperKey> {
- let super_key = Self::extract_super_key_from_key_entry(entry, pw).context(
- "In populate_cache_from_super_key_blob. Failed to extract super key from key entry",
- )?;
- self.install_per_boot_key_for_user(user_id, super_key.clone());
+ pw: &Password,
+ ) -> Result<Arc<SuperKey>> {
+ let super_key = Self::extract_super_key_from_key_entry(algorithm, entry, pw, None)
+ .context(
+ "In populate_cache_from_super_key_blob. Failed to extract super key from key entry",
+ )?;
+ self.install_per_boot_key_for_user(user_id, super_key.clone())?;
Ok(super_key)
}
/// Extracts super key from the entry loaded from the database
- pub fn extract_super_key_from_key_entry(entry: KeyEntry, pw: &[u8]) -> Result<SuperKey> {
+ pub fn extract_super_key_from_key_entry(
+ algorithm: SuperEncryptionAlgorithm,
+ entry: KeyEntry,
+ pw: &Password,
+ reencrypt_with: Option<Arc<SuperKey>>,
+ ) -> Result<Arc<SuperKey>> {
if let Some((blob, metadata)) = entry.key_blob_info() {
let key = match (
metadata.encrypted_by(),
@@ -315,9 +620,10 @@
metadata.aead_tag(),
) {
(Some(&EncryptedBy::Password), Some(salt), Some(iv), Some(tag)) => {
- let key = derive_key_from_password(pw, Some(salt), AES_256_KEY_LENGTH).context(
- "In extract_super_key_from_key_entry: Failed to generate key from password.",
- )?;
+ // Note that password encryption is AES no matter the value of algorithm
+ let key = pw.derive_key(Some(salt), AES_256_KEY_LENGTH).context(
+ "In extract_super_key_from_key_entry: Failed to generate key from password.",
+ )?;
aes_gcm_decrypt(blob, iv, tag, &key).context(
"In extract_super_key_from_key_entry: Failed to decrypt key blob.",
@@ -327,16 +633,21 @@
return Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
concat!(
"In extract_super_key_from_key_entry: Super key has incomplete metadata.",
- "Present: encrypted_by: {}, salt: {}, iv: {}, aead_tag: {}."
+ "encrypted_by: {:?}; Present: salt: {}, iv: {}, aead_tag: {}."
),
- enc_by.is_some(),
+ enc_by,
salt.is_some(),
iv.is_some(),
tag.is_some()
));
}
};
- Ok(SuperKey { key: Arc::new(key), id: entry.id() })
+ Ok(Arc::new(SuperKey {
+ algorithm,
+ key,
+ id: SuperKeyIdentifier::DatabaseId(entry.id()),
+ reencrypt_with,
+ }))
} else {
Err(Error::Rc(ResponseCode::VALUE_CORRUPTED))
.context("In extract_super_key_from_key_entry: No key blob info.")
@@ -344,9 +655,13 @@
}
/// Encrypts the super key from a key derived from the password, before storing in the database.
- pub fn encrypt_with_password(super_key: &[u8], pw: &[u8]) -> Result<(Vec<u8>, BlobMetaData)> {
+ pub fn encrypt_with_password(
+ super_key: &[u8],
+ pw: &Password,
+ ) -> Result<(Vec<u8>, BlobMetaData)> {
let salt = generate_salt().context("In encrypt_with_password: Failed to generate salt.")?;
- let derived_key = derive_key_from_password(pw, Some(&salt), AES_256_KEY_LENGTH)
+ let derived_key = pw
+ .derive_key(Some(&salt), AES_256_KEY_LENGTH)
.context("In encrypt_with_password: Failed to derive password.")?;
let mut metadata = BlobMetaData::new();
metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
@@ -366,14 +681,14 @@
&self,
db: &mut KeystoreDB,
legacy_migrator: &LegacyMigrator,
- user_id: u32,
+ user_id: UserId,
key_blob: &[u8],
) -> Result<(Vec<u8>, BlobMetaData)> {
match UserState::get(db, legacy_migrator, self, user_id)
.context("In super_encrypt. Failed to get user state.")?
{
UserState::LskfUnlocked(super_key) => {
- Self::encrypt_with_super_key(key_blob, &super_key)
+ Self::encrypt_with_aes_super_key(key_blob, &super_key)
.context("In super_encrypt_on_key_init. Failed to encrypt the key.")
}
UserState::LskfLocked => {
@@ -387,22 +702,26 @@
//Helper function to encrypt a key with the given super key. Callers should select which super
//key to be used. This is called when a key is super encrypted at its creation as well as at its
//upgrade.
- fn encrypt_with_super_key(
+ fn encrypt_with_aes_super_key(
key_blob: &[u8],
super_key: &SuperKey,
) -> Result<(Vec<u8>, BlobMetaData)> {
+ if super_key.algorithm != SuperEncryptionAlgorithm::Aes256Gcm {
+ return Err(Error::sys())
+ .context("In encrypt_with_aes_super_key: unexpected algorithm");
+ }
let mut metadata = BlobMetaData::new();
let (encrypted_key, iv, tag) = aes_gcm_encrypt(key_blob, &(super_key.key))
- .context("In encrypt_with_super_key: Failed to encrypt new super key.")?;
+ .context("In encrypt_with_aes_super_key: Failed to encrypt new super key.")?;
metadata.add(BlobMetaEntry::Iv(iv));
metadata.add(BlobMetaEntry::AeadTag(tag));
- metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(super_key.id)));
+ super_key.id.add_to_metadata(&mut metadata);
Ok((encrypted_key, metadata))
}
/// Check if super encryption is required and if so, super-encrypt the key to be stored in
/// the database.
- #[allow(clippy::clippy::too_many_arguments)]
+ #[allow(clippy::too_many_arguments)]
pub fn handle_super_encryption_on_key_init(
&self,
db: &mut KeystoreDB,
@@ -410,63 +729,331 @@
domain: &Domain,
key_parameters: &[KeyParameter],
flags: Option<i32>,
- user_id: u32,
+ user_id: UserId,
key_blob: &[u8],
) -> Result<(Vec<u8>, BlobMetaData)> {
- match (*domain, Enforcements::super_encryption_required(key_parameters, flags)) {
- (Domain::APP, true) => {
- self.super_encrypt_on_key_init(db, legacy_migrator, user_id, &key_blob).context(
- "In handle_super_encryption_on_key_init.
- Failed to super encrypt the key.",
- )
+ match Enforcements::super_encryption_required(domain, key_parameters, flags) {
+ SuperEncryptionType::None => Ok((key_blob.to_vec(), BlobMetaData::new())),
+ SuperEncryptionType::LskfBound => self
+ .super_encrypt_on_key_init(db, legacy_migrator, user_id, key_blob)
+ .context(concat!(
+ "In handle_super_encryption_on_key_init. ",
+ "Failed to super encrypt with LskfBound key."
+ )),
+ SuperEncryptionType::ScreenLockBound => {
+ let mut data = self.data.lock().unwrap();
+ let entry = data.user_keys.entry(user_id).or_default();
+ if let Some(super_key) = entry.screen_lock_bound.as_ref() {
+ Self::encrypt_with_aes_super_key(key_blob, super_key).context(concat!(
+ "In handle_super_encryption_on_key_init. ",
+ "Failed to encrypt with ScreenLockBound key."
+ ))
+ } else {
+ // Symmetric key is not available, use public key encryption
+ let loaded =
+ db.load_super_key(&USER_SCREEN_LOCK_BOUND_P521_KEY, user_id).context(
+ "In handle_super_encryption_on_key_init: load_super_key failed.",
+ )?;
+ let (key_id_guard, key_entry) = loaded.ok_or_else(Error::sys).context(
+ "In handle_super_encryption_on_key_init: User ECDH key missing.",
+ )?;
+ let public_key =
+ key_entry.metadata().sec1_public_key().ok_or_else(Error::sys).context(
+ "In handle_super_encryption_on_key_init: sec1_public_key missing.",
+ )?;
+ let mut metadata = BlobMetaData::new();
+ let (ephem_key, salt, iv, encrypted_key, aead_tag) =
+ ECDHPrivateKey::encrypt_message(public_key, key_blob).context(concat!(
+ "In handle_super_encryption_on_key_init: ",
+ "ECDHPrivateKey::encrypt_message failed."
+ ))?;
+ metadata.add(BlobMetaEntry::PublicKey(ephem_key));
+ metadata.add(BlobMetaEntry::Salt(salt));
+ metadata.add(BlobMetaEntry::Iv(iv));
+ metadata.add(BlobMetaEntry::AeadTag(aead_tag));
+ SuperKeyIdentifier::DatabaseId(key_id_guard.id())
+ .add_to_metadata(&mut metadata);
+ Ok((encrypted_key, metadata))
+ }
}
- _ => Ok((key_blob.to_vec(), BlobMetaData::new())),
- }
- }
-
- /// Check if a given key is super-encrypted, from its metadata. If so, unwrap the key using
- /// the relevant super key.
- pub fn unwrap_key_if_required<'a>(
- &self,
- metadata: &BlobMetaData,
- key_blob: &'a [u8],
- ) -> Result<KeyBlob<'a>> {
- if Self::key_super_encrypted(&metadata) {
- let unwrapped_key = self
- .unwrap_key(key_blob, metadata)
- .context("In unwrap_key_if_required. Error in unwrapping the key.")?;
- Ok(unwrapped_key)
- } else {
- Ok(KeyBlob::Ref(key_blob))
+ SuperEncryptionType::BootLevel(level) => {
+ let key_id = SuperKeyIdentifier::BootLevel(level);
+ let super_key = self
+ .lookup_key(&key_id)
+ .context("In handle_super_encryption_on_key_init: lookup_key failed")?
+ .ok_or(Error::Rc(ResponseCode::LOCKED))
+ .context("In handle_super_encryption_on_key_init: Boot stage key absent")?;
+ Self::encrypt_with_aes_super_key(key_blob, &super_key).context(concat!(
+ "In handle_super_encryption_on_key_init: ",
+ "Failed to encrypt with BootLevel key."
+ ))
+ }
}
}
/// Check if a given key needs re-super-encryption, from its KeyBlob type.
/// If so, re-super-encrypt the key and return a new set of metadata,
/// containing the new super encryption information.
- pub fn reencrypt_on_upgrade_if_required<'a>(
+ pub fn reencrypt_if_required<'a>(
key_blob_before_upgrade: &KeyBlob,
key_after_upgrade: &'a [u8],
) -> Result<(KeyBlob<'a>, Option<BlobMetaData>)> {
match key_blob_before_upgrade {
- KeyBlob::Sensitive(_, super_key) => {
- let (key, metadata) = Self::encrypt_with_super_key(key_after_upgrade, super_key)
- .context(concat!(
- "In reencrypt_on_upgrade_if_required. ",
- "Failed to re-super-encrypt key on key upgrade."
- ))?;
+ KeyBlob::Sensitive { reencrypt_with: super_key, .. } => {
+ let (key, metadata) =
+ Self::encrypt_with_aes_super_key(key_after_upgrade, super_key)
+ .context("In reencrypt_if_required: Failed to re-super-encrypt key.")?;
Ok((KeyBlob::NonSensitive(key), Some(metadata)))
}
_ => Ok((KeyBlob::Ref(key_after_upgrade), None)),
}
}
- // Helper function to decide if a key is super encrypted, given metadata.
- fn key_super_encrypted(metadata: &BlobMetaData) -> bool {
- if let Some(&EncryptedBy::KeyId(_)) = metadata.encrypted_by() {
- return true;
+ /// Fetch a superencryption key from the database, or create it if it doesn't already exist.
+ /// When this is called, the caller must hold the lock on the SuperKeyManager.
+ /// So it's OK that the check and creation are different DB transactions.
+ fn get_or_create_super_key(
+ db: &mut KeystoreDB,
+ user_id: UserId,
+ key_type: &SuperKeyType,
+ password: &Password,
+ reencrypt_with: Option<Arc<SuperKey>>,
+ ) -> Result<Arc<SuperKey>> {
+ let loaded_key = db.load_super_key(key_type, user_id)?;
+ if let Some((_, key_entry)) = loaded_key {
+ Ok(Self::extract_super_key_from_key_entry(
+ key_type.algorithm,
+ key_entry,
+ password,
+ reencrypt_with,
+ )?)
+ } else {
+ let (super_key, public_key) = match key_type.algorithm {
+ SuperEncryptionAlgorithm::Aes256Gcm => (
+ generate_aes256_key()
+ .context("In get_or_create_super_key: Failed to generate AES 256 key.")?,
+ None,
+ ),
+ SuperEncryptionAlgorithm::EcdhP521 => {
+ let key = ECDHPrivateKey::generate()
+ .context("In get_or_create_super_key: Failed to generate ECDH key")?;
+ (
+ key.private_key()
+ .context("In get_or_create_super_key: private_key failed")?,
+ Some(
+ key.public_key()
+ .context("In get_or_create_super_key: public_key failed")?,
+ ),
+ )
+ }
+ };
+ //derive an AES256 key from the password and re-encrypt the super key
+ //before we insert it in the database.
+ let (encrypted_super_key, blob_metadata) =
+ Self::encrypt_with_password(&super_key, password)
+ .context("In get_or_create_super_key.")?;
+ let mut key_metadata = KeyMetaData::new();
+ if let Some(pk) = public_key {
+ key_metadata.add(KeyMetaEntry::Sec1PublicKey(pk));
+ }
+ let key_entry = db
+ .store_super_key(
+ user_id,
+ key_type,
+ &encrypted_super_key,
+ &blob_metadata,
+ &key_metadata,
+ )
+ .context("In get_or_create_super_key. Failed to store super key.")?;
+ Ok(Arc::new(SuperKey {
+ algorithm: key_type.algorithm,
+ key: super_key,
+ id: SuperKeyIdentifier::DatabaseId(key_entry.id()),
+ reencrypt_with,
+ }))
}
- false
+ }
+
+ /// Decrypt the screen-lock bound keys for this user using the password and store in memory.
+ pub fn unlock_screen_lock_bound_key(
+ &self,
+ db: &mut KeystoreDB,
+ user_id: UserId,
+ password: &Password,
+ ) -> Result<()> {
+ let mut data = self.data.lock().unwrap();
+ let entry = data.user_keys.entry(user_id).or_default();
+ let aes = entry
+ .screen_lock_bound
+ .get_or_try_to_insert_with(|| {
+ Self::get_or_create_super_key(
+ db,
+ user_id,
+ &USER_SCREEN_LOCK_BOUND_KEY,
+ password,
+ None,
+ )
+ })?
+ .clone();
+ let ecdh = entry
+ .screen_lock_bound_private
+ .get_or_try_to_insert_with(|| {
+ Self::get_or_create_super_key(
+ db,
+ user_id,
+ &USER_SCREEN_LOCK_BOUND_P521_KEY,
+ password,
+ Some(aes.clone()),
+ )
+ })?
+ .clone();
+ data.add_key_to_key_index(&aes)?;
+ data.add_key_to_key_index(&ecdh)?;
+ Ok(())
+ }
+
+ /// Wipe the screen-lock bound keys for this user from memory.
+ pub fn lock_screen_lock_bound_key(
+ &self,
+ db: &mut KeystoreDB,
+ user_id: UserId,
+ unlocking_sids: &[i64],
+ ) {
+ log::info!("Locking screen bound for user {} sids {:?}", user_id, unlocking_sids);
+ let mut data = self.data.lock().unwrap();
+ let mut entry = data.user_keys.entry(user_id).or_default();
+ if !unlocking_sids.is_empty() {
+ if let (Some(aes), Some(ecdh)) = (
+ entry.screen_lock_bound.as_ref().cloned(),
+ entry.screen_lock_bound_private.as_ref().cloned(),
+ ) {
+ let res = (|| -> Result<()> {
+ let key_desc = KeyMintDevice::internal_descriptor(format!(
+ "biometric_unlock_key_{}",
+ user_id
+ ));
+ let encrypting_key = generate_aes256_key()?;
+ let km_dev: KeyMintDevice =
+ KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT)
+ .context("In lock_screen_lock_bound_key: KeyMintDevice::get failed")?;
+ let mut key_params = vec![
+ KeyParameterValue::Algorithm(Algorithm::AES),
+ KeyParameterValue::KeySize(256),
+ KeyParameterValue::BlockMode(BlockMode::GCM),
+ KeyParameterValue::PaddingMode(PaddingMode::NONE),
+ KeyParameterValue::CallerNonce,
+ KeyParameterValue::KeyPurpose(KeyPurpose::DECRYPT),
+ KeyParameterValue::MinMacLength(128),
+ KeyParameterValue::AuthTimeout(BIOMETRIC_AUTH_TIMEOUT_S),
+ KeyParameterValue::HardwareAuthenticatorType(
+ HardwareAuthenticatorType::FINGERPRINT,
+ ),
+ ];
+ for sid in unlocking_sids {
+ key_params.push(KeyParameterValue::UserSecureID(*sid));
+ }
+ let key_params: Vec<KmKeyParameter> =
+ key_params.into_iter().map(|x| x.into()).collect();
+ km_dev.create_and_store_key(
+ db,
+ &key_desc,
+ KeyType::Client, /* TODO Should be Super b/189470584 */
+ |dev| {
+ let _wp = wd::watch_millis(
+ "In lock_screen_lock_bound_key: calling importKey.",
+ 500,
+ );
+ dev.importKey(
+ key_params.as_slice(),
+ KeyFormat::RAW,
+ &encrypting_key,
+ None,
+ )
+ },
+ )?;
+ entry.biometric_unlock = Some(BiometricUnlock {
+ sids: unlocking_sids.into(),
+ key_desc,
+ screen_lock_bound: LockedKey::new(&encrypting_key, &aes)?,
+ screen_lock_bound_private: LockedKey::new(&encrypting_key, &ecdh)?,
+ });
+ Ok(())
+ })();
+ // There is no reason to propagate an error here upwards. We must discard
+ // entry.screen_lock_bound* in any case.
+ if let Err(e) = res {
+ log::error!("Error setting up biometric unlock: {:#?}", e);
+ }
+ }
+ }
+ entry.screen_lock_bound = None;
+ entry.screen_lock_bound_private = None;
+ }
+
+ /// User has unlocked, not using a password. See if any of our stored auth tokens can be used
+ /// to unlock the keys protecting UNLOCKED_DEVICE_REQUIRED keys.
+ pub fn try_unlock_user_with_biometric(
+ &self,
+ db: &mut KeystoreDB,
+ user_id: UserId,
+ ) -> Result<()> {
+ let mut data = self.data.lock().unwrap();
+ let mut entry = data.user_keys.entry(user_id).or_default();
+ if let Some(biometric) = entry.biometric_unlock.as_ref() {
+ let (key_id_guard, key_entry) = db
+ .load_key_entry(
+ &biometric.key_desc,
+ KeyType::Client, // This should not be a Client key.
+ KeyEntryLoadBits::KM,
+ AID_KEYSTORE,
+ |_, _| Ok(()),
+ )
+ .context("In try_unlock_user_with_biometric: load_key_entry failed")?;
+ let km_dev: KeyMintDevice = KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT)
+ .context("In try_unlock_user_with_biometric: KeyMintDevice::get failed")?;
+ for sid in &biometric.sids {
+ if let Some((auth_token_entry, _)) = db.find_auth_token_entry(|entry| {
+ entry.auth_token().userId == *sid || entry.auth_token().authenticatorId == *sid
+ }) {
+ let res: Result<(Arc<SuperKey>, Arc<SuperKey>)> = (|| {
+ let slb = biometric.screen_lock_bound.decrypt(
+ db,
+ &km_dev,
+ &key_id_guard,
+ &key_entry,
+ auth_token_entry.auth_token(),
+ None,
+ )?;
+ let slbp = biometric.screen_lock_bound_private.decrypt(
+ db,
+ &km_dev,
+ &key_id_guard,
+ &key_entry,
+ auth_token_entry.auth_token(),
+ Some(slb.clone()),
+ )?;
+ Ok((slb, slbp))
+ })();
+ match res {
+ Ok((slb, slbp)) => {
+ entry.screen_lock_bound = Some(slb.clone());
+ entry.screen_lock_bound_private = Some(slbp.clone());
+ data.add_key_to_key_index(&slb)?;
+ data.add_key_to_key_index(&slbp)?;
+ log::info!(concat!(
+ "In try_unlock_user_with_biometric: ",
+ "Successfully unlocked with biometric"
+ ));
+ return Ok(());
+ }
+ Err(e) => {
+ log::warn!("In try_unlock_user_with_biometric: attempt failed: {:?}", e)
+ }
+ }
+ }
+ }
+ }
+ Ok(())
}
}
@@ -475,7 +1062,7 @@
pub enum UserState {
// The user has registered LSKF and has unlocked the device by entering PIN/Password,
// and hence the per-boot super key is available in the cache.
- LskfUnlocked(SuperKey),
+ LskfUnlocked(Arc<SuperKey>),
// The user has registered LSKF, but has not unlocked the device using password, after reboot.
// Hence the per-boot super-key(s) is not available in the cache.
// However, the encrypted super key is available in the database.
@@ -490,7 +1077,7 @@
db: &mut KeystoreDB,
legacy_migrator: &LegacyMigrator,
skm: &SuperKeyManager,
- user_id: u32,
+ user_id: UserId,
) -> Result<UserState> {
match skm.get_per_boot_key_by_user_id(user_id) {
Some(super_key) => Ok(UserState::LskfUnlocked(super_key)),
@@ -513,8 +1100,8 @@
db: &mut KeystoreDB,
legacy_migrator: &LegacyMigrator,
skm: &SuperKeyManager,
- user_id: u32,
- password: Option<&[u8]>,
+ user_id: UserId,
+ password: Option<&Password>,
) -> Result<UserState> {
match skm.get_per_boot_key_by_user_id(user_id) {
Some(super_key) => {
@@ -547,8 +1134,8 @@
db: &mut KeystoreDB,
legacy_migrator: &LegacyMigrator,
skm: &SuperKeyManager,
- user_id: u32,
- password: &[u8],
+ user_id: UserId,
+ password: &Password,
) -> Result<UserState> {
match skm.get_per_boot_key_by_user_id(user_id) {
Some(super_key) => {
@@ -573,18 +1160,18 @@
db: &mut KeystoreDB,
skm: &SuperKeyManager,
legacy_migrator: &LegacyMigrator,
- user_id: u32,
+ user_id: UserId,
keep_non_super_encrypted_keys: bool,
) -> Result<()> {
// mark keys created on behalf of the user as unreferenced.
legacy_migrator
.bulk_delete_user(user_id, keep_non_super_encrypted_keys)
.context("In reset_user: Trying to delete legacy keys.")?;
- db.unbind_keys_for_user(user_id as u32, keep_non_super_encrypted_keys)
+ db.unbind_keys_for_user(user_id, keep_non_super_encrypted_keys)
.context("In reset user. Error in unbinding keys.")?;
//delete super key in cache, if exists
- skm.forget_all_keys_for_user(user_id as u32);
+ skm.forget_all_keys_for_user(user_id);
Ok(())
}
}
@@ -595,19 +1182,39 @@
/// `Ref` holds a reference to a key blob when it does not need to be modified if its
/// life time allows it.
pub enum KeyBlob<'a> {
- Sensitive(ZVec, SuperKey),
+ Sensitive {
+ key: ZVec,
+ /// If KeyMint reports that the key must be upgraded, we must
+ /// re-encrypt the key before writing to the database; we use
+ /// this key.
+ reencrypt_with: Arc<SuperKey>,
+ /// If this key was decrypted with an ECDH key, we want to
+ /// re-encrypt it on first use whether it was upgraded or not;
+ /// this field indicates that that's necessary.
+ force_reencrypt: bool,
+ },
NonSensitive(Vec<u8>),
Ref(&'a [u8]),
}
+impl<'a> KeyBlob<'a> {
+ pub fn force_reencrypt(&self) -> bool {
+ if let KeyBlob::Sensitive { force_reencrypt, .. } = self {
+ *force_reencrypt
+ } else {
+ false
+ }
+ }
+}
+
/// Deref returns a reference to the key material in any variant.
impl<'a> Deref for KeyBlob<'a> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
match self {
- Self::Sensitive(key, _) => &key,
- Self::NonSensitive(key) => &key,
+ Self::Sensitive { key, .. } => key,
+ Self::NonSensitive(key) => key,
Self::Ref(key) => key,
}
}
diff --git a/keystore2/src/try_insert.rs b/keystore2/src/try_insert.rs
new file mode 100644
index 0000000..6dd3962
--- /dev/null
+++ b/keystore2/src/try_insert.rs
@@ -0,0 +1,100 @@
+// 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.
+
+//! The TryInsert trait adds to Option<T> the method
+//! get_or_try_to_insert_with, which is analogous to
+//! get_or_insert_with, but allows the called function to fail and propagates the failure.
+
+/// The TryInsert trait adds to Option<T> the method
+/// get_or_try_to_insert_with, which is analogous to
+/// get_or_insert_with, but allows the called function to fail and propagates the failure.
+pub trait TryInsert {
+ /// Type of the Ok branch of the Result
+ type Item;
+ /// Inserts a value computed from `f` into the option if it is [`None`],
+ /// then returns a mutable reference to the contained value. If `f`
+ /// returns Err, the Option is unchanged.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// let mut x = None;
+ /// assert_eq!(x.get_or_try_to_insert_with(Err("oops".to_string())), Err("oops".to_string()))
+ /// {
+ /// let y: &mut u32 = x.get_or_try_to_insert_with(|| Ok(5))?;
+ /// assert_eq!(y, &5);
+ ///
+ /// *y = 7;
+ /// }
+ ///
+ /// assert_eq!(x, Some(7));
+ /// ```
+ fn get_or_try_to_insert_with<E, F: FnOnce() -> Result<Self::Item, E>>(
+ &mut self,
+ f: F,
+ ) -> Result<&mut Self::Item, E>;
+}
+
+impl<T> TryInsert for Option<T> {
+ type Item = T;
+ fn get_or_try_to_insert_with<E, F: FnOnce() -> Result<Self::Item, E>>(
+ &mut self,
+ f: F,
+ ) -> Result<&mut Self::Item, E> {
+ if self.is_none() {
+ *self = Some(f()?);
+ }
+
+ match self {
+ Some(v) => Ok(v),
+ // SAFETY: a `None` variant for `self` would have been replaced by a `Some`
+ // variant in the code above.
+ None => unsafe { std::hint::unreachable_unchecked() },
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ fn fails() -> Result<i32, String> {
+ Err("fail".to_string())
+ }
+
+ fn succeeds() -> Result<i32, String> {
+ Ok(99)
+ }
+
+ #[test]
+ fn test() {
+ let mut x = None;
+ assert_eq!(x.get_or_try_to_insert_with(fails), Err("fail".to_string()));
+ assert_eq!(x, None);
+ assert_eq!(*x.get_or_try_to_insert_with(succeeds).unwrap(), 99);
+ assert_eq!(x, Some(99));
+ x = Some(42);
+ assert_eq!(*x.get_or_try_to_insert_with(fails).unwrap(), 42);
+ assert_eq!(x, Some(42));
+ assert_eq!(*x.get_or_try_to_insert_with(succeeds).unwrap(), 42);
+ assert_eq!(x, Some(42));
+ *x.get_or_try_to_insert_with(fails).unwrap() = 2;
+ assert_eq!(x, Some(2));
+ *x.get_or_try_to_insert_with(succeeds).unwrap() = 3;
+ assert_eq!(x, Some(3));
+ x = None;
+ *x.get_or_try_to_insert_with(succeeds).unwrap() = 5;
+ assert_eq!(x, Some(5));
+ }
+}
diff --git a/keystore2/src/user_manager.rs b/keystore2/src/user_manager.rs
deleted file mode 100644
index 3c393c5..0000000
--- a/keystore2/src/user_manager.rs
+++ /dev/null
@@ -1,120 +0,0 @@
-// 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.
-
-//! This module implements IKeystoreUserManager AIDL interface.
-
-use crate::error::map_or_log_err;
-use crate::error::Error as KeystoreError;
-use crate::globals::{DB, LEGACY_MIGRATOR, SUPER_KEY};
-use crate::permission::KeystorePerm;
-use crate::super_key::UserState;
-use crate::utils::check_keystore_permission;
-use android_security_usermanager::aidl::android::security::usermanager::IKeystoreUserManager::{
- BnKeystoreUserManager, IKeystoreUserManager,
-};
-use android_security_usermanager::binder::{Interface, Result as BinderResult};
-use android_system_keystore2::aidl::android::system::keystore2::Domain::Domain;
-use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
-use anyhow::{Context, Result};
-use binder::{IBinder, Strong};
-
-/// This struct is defined to implement the aforementioned AIDL interface.
-/// As of now, it is an empty struct.
-pub struct UserManager;
-
-impl UserManager {
- /// Create a new instance of Keystore User Manager service.
- pub fn new_native_binder() -> Result<Strong<dyn IKeystoreUserManager>> {
- let result = BnKeystoreUserManager::new_binder(Self);
- result.as_binder().set_requesting_sid(true);
- Ok(result)
- }
-
- fn on_user_password_changed(user_id: i32, password: Option<&[u8]>) -> Result<()> {
- //Check permission. Function should return if this failed. Therefore having '?' at the end
- //is very important.
- check_keystore_permission(KeystorePerm::change_password())
- .context("In on_user_password_changed.")?;
-
- match DB
- .with(|db| {
- UserState::get_with_password_changed(
- &mut db.borrow_mut(),
- &LEGACY_MIGRATOR,
- &SUPER_KEY,
- user_id as u32,
- password,
- )
- })
- .context("In on_user_password_changed.")?
- {
- UserState::LskfLocked => {
- // Error - password can not be changed when the device is locked
- Err(KeystoreError::Rc(ResponseCode::LOCKED))
- .context("In on_user_password_changed. Device is locked.")
- }
- _ => {
- // LskfLocked is the only error case for password change
- Ok(())
- }
- }
- }
-
- fn add_or_remove_user(user_id: i32) -> Result<()> {
- // Check permission. Function should return if this failed. Therefore having '?' at the end
- // is very important.
- check_keystore_permission(KeystorePerm::change_user()).context("In add_or_remove_user.")?;
- DB.with(|db| {
- UserState::reset_user(
- &mut db.borrow_mut(),
- &SUPER_KEY,
- &LEGACY_MIGRATOR,
- user_id as u32,
- false,
- )
- })
- .context("In add_or_remove_user: Trying to delete keys from db.")
- }
-
- fn clear_namespace(domain: Domain, nspace: i64) -> Result<()> {
- // Permission check. Must return on error. Do not touch the '?'.
- check_keystore_permission(KeystorePerm::clear_uid()).context("In clear_namespace.")?;
-
- LEGACY_MIGRATOR
- .bulk_delete_uid(domain, nspace)
- .context("In clear_namespace: Trying to delete legacy keys.")?;
- DB.with(|db| db.borrow_mut().unbind_keys_for_namespace(domain, nspace))
- .context("In clear_namespace: Trying to delete keys from db.")
- }
-}
-
-impl Interface for UserManager {}
-
-impl IKeystoreUserManager for UserManager {
- fn onUserPasswordChanged(&self, user_id: i32, password: Option<&[u8]>) -> BinderResult<()> {
- map_or_log_err(Self::on_user_password_changed(user_id, password), Ok)
- }
-
- fn onUserAdded(&self, user_id: i32) -> BinderResult<()> {
- map_or_log_err(Self::add_or_remove_user(user_id), Ok)
- }
-
- fn onUserRemoved(&self, user_id: i32) -> BinderResult<()> {
- map_or_log_err(Self::add_or_remove_user(user_id), Ok)
- }
-
- fn clearNamespace(&self, domain: Domain, nspace: i64) -> BinderResult<()> {
- map_or_log_err(Self::clear_namespace(domain, nspace), Ok)
- }
-}
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
index 8e161b7..f6d92ee 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -15,12 +15,13 @@
//! This module implements utility functions used by the Keystore 2.0 service
//! implementation.
-use crate::error::Error;
+use crate::error::{map_binder_status, Error, ErrorCode};
use crate::permission;
use crate::permission::{KeyPerm, KeyPermSet, KeystorePerm};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- KeyCharacteristics::KeyCharacteristics,
+ KeyCharacteristics::KeyCharacteristics, Tag::Tag,
};
+use android_os_permissions_aidl::aidl::android::os::IPermissionController;
use android_security_apc::aidl::android::security::apc::{
IProtectedConfirmation::{FLAG_UI_OPTION_INVERTED, FLAG_UI_OPTION_MAGNIFIED},
ResponseCode::ResponseCode as ApcResponseCode,
@@ -28,15 +29,13 @@
use android_system_keystore2::aidl::android::system::keystore2::{
Authorization::Authorization, KeyDescriptor::KeyDescriptor,
};
-use anyhow::{anyhow, Context};
-use binder::{FromIBinder, SpIBinder, ThreadState};
+use anyhow::Context;
+use binder::{Strong, ThreadState};
use keystore2_apc_compat::{
ApcCompatUiOptions, APC_COMPAT_ERROR_ABORTED, APC_COMPAT_ERROR_CANCELLED,
APC_COMPAT_ERROR_IGNORED, APC_COMPAT_ERROR_OK, APC_COMPAT_ERROR_OPERATION_PENDING,
APC_COMPAT_ERROR_SYSTEM_ERROR,
};
-use std::convert::TryFrom;
-use std::sync::Mutex;
/// This function uses its namesake in the permission module and in
/// combination with with_calling_sid from the binder crate to check
@@ -44,7 +43,7 @@
pub fn check_keystore_permission(perm: KeystorePerm) -> anyhow::Result<()> {
ThreadState::with_calling_sid(|calling_sid| {
permission::check_keystore_permission(
- &calling_sid.ok_or_else(Error::sys).context(
+ calling_sid.ok_or_else(Error::sys).context(
"In check_keystore_permission: Cannot check permission without calling_sid.",
)?,
perm,
@@ -58,7 +57,7 @@
pub fn check_grant_permission(access_vec: KeyPermSet, key: &KeyDescriptor) -> anyhow::Result<()> {
ThreadState::with_calling_sid(|calling_sid| {
permission::check_grant_permission(
- &calling_sid.ok_or_else(Error::sys).context(
+ calling_sid.ok_or_else(Error::sys).context(
"In check_grant_permission: Cannot check permission without calling_sid.",
)?,
access_vec,
@@ -78,7 +77,7 @@
ThreadState::with_calling_sid(|calling_sid| {
permission::check_key_permission(
ThreadState::get_calling_uid(),
- &calling_sid
+ calling_sid
.ok_or_else(Error::sys)
.context("In check_key_permission: Cannot check permission without calling_sid.")?,
perm,
@@ -88,36 +87,43 @@
})
}
-/// Thread safe wrapper around SpIBinder. It is safe to have SpIBinder smart pointers to the
-/// same object in multiple threads, but cloning a SpIBinder is not thread safe.
-/// Keystore frequently hands out binder tokens to the security level interface. If this
-/// is to happen from a multi threaded thread pool, the SpIBinder needs to be protected by a
-/// Mutex.
-#[derive(Debug)]
-pub struct Asp(Mutex<SpIBinder>);
-
-impl Asp {
- /// Creates a new instance owning a SpIBinder wrapped in a Mutex.
- pub fn new(i: SpIBinder) -> Self {
- Self(Mutex::new(i))
- }
-
- /// Clones the owned SpIBinder and attempts to convert it into the requested interface.
- pub fn get_interface<T: FromIBinder + ?Sized>(&self) -> anyhow::Result<binder::Strong<T>> {
- // We can use unwrap here because we never panic when locked, so the mutex
- // can never be poisoned.
- let lock = self.0.lock().unwrap();
- (*lock)
- .clone()
- .into_interface()
- .map_err(|e| anyhow!(format!("get_interface failed with error code {:?}", e)))
- }
+/// This function checks whether a given tag corresponds to the access of device identifiers.
+pub fn is_device_id_attestation_tag(tag: Tag) -> bool {
+ matches!(
+ tag,
+ Tag::ATTESTATION_ID_IMEI
+ | Tag::ATTESTATION_ID_MEID
+ | Tag::ATTESTATION_ID_SERIAL
+ | Tag::DEVICE_UNIQUE_ATTESTATION
+ )
}
-impl Clone for Asp {
- fn clone(&self) -> Self {
- let lock = self.0.lock().unwrap();
- Self(Mutex::new((*lock).clone()))
+/// This function checks whether the calling app has the Android permissions needed to attest device
+/// identifiers. It throws an error if the permissions cannot be verified, or if the caller doesn't
+/// have the right permissions, and returns silently otherwise.
+pub fn check_device_attestation_permissions() -> anyhow::Result<()> {
+ let permission_controller: Strong<dyn IPermissionController::IPermissionController> =
+ binder::get_interface("permission")?;
+
+ let binder_result = {
+ let _wp = watchdog::watch_millis(
+ "In check_device_attestation_permissions: calling checkPermission.",
+ 500,
+ );
+ permission_controller.checkPermission(
+ "android.permission.READ_PRIVILEGED_PHONE_STATE",
+ ThreadState::get_calling_pid(),
+ ThreadState::get_calling_uid() as i32,
+ )
+ };
+ let has_permissions = map_binder_status(binder_result)
+ .context("In check_device_attestation_permissions: checkPermission failed")?;
+ match has_permissions {
+ true => Ok(()),
+ false => Err(Error::Km(ErrorCode::CANNOT_ATTEST_IDS)).context(concat!(
+ "In check_device_attestation_permissions: ",
+ "caller does not have the permission to attest device IDs"
+ )),
}
}
@@ -145,19 +151,15 @@
parameters.into_iter().map(|p| p.into_authorization()).collect()
}
-/// This returns the current time (in seconds) as an instance of a monotonic clock, by invoking the
-/// system call since Rust does not support getting monotonic time instance as an integer.
-pub fn get_current_time_in_seconds() -> i64 {
+/// This returns the current time (in milliseconds) as an instance of a monotonic clock,
+/// by invoking the system call since Rust does not support getting monotonic time instance
+/// as an integer.
+pub fn get_current_time_in_milliseconds() -> i64 {
let mut current_time = libc::timespec { tv_sec: 0, tv_nsec: 0 };
// Following unsafe block includes one system call to get monotonic time.
// Therefore, it is not considered harmful.
unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC_RAW, &mut current_time) };
- // It is safe to unwrap here because try_from() returns std::convert::Infallible, which is
- // defined to be an error that can never happen (i.e. the result is always ok).
- // This suppresses the compiler's complaint about converting tv_sec to i64 in method
- // get_current_time_in_seconds.
- #[allow(clippy::useless_conversion)]
- i64::try_from(current_time.tv_sec).unwrap()
+ current_time.tv_sec as i64 * 1000 + (current_time.tv_nsec as i64 / 1_000_000)
}
/// Converts a response code as returned by the Android Protected Confirmation HIDL compatibility
@@ -186,10 +188,80 @@
}
/// AID offset for uid space partitioning.
-/// TODO: Replace with bindgen generated from libcutils. b/175619259
-pub const AID_USER_OFFSET: u32 = 100000;
+pub const AID_USER_OFFSET: u32 = rustutils::users::AID_USER_OFFSET;
+
+/// AID of the keystore process itself, used for keys that
+/// keystore generates for its own use.
+pub const AID_KEYSTORE: u32 = rustutils::users::AID_KEYSTORE;
/// Extracts the android user from the given uid.
pub fn uid_to_android_user(uid: u32) -> u32 {
- uid / AID_USER_OFFSET
+ rustutils::users::multiuser_get_user_id(uid)
+}
+
+/// This module provides helpers for simplified use of the watchdog module.
+#[cfg(feature = "watchdog")]
+pub mod watchdog {
+ pub use crate::watchdog::WatchPoint;
+ use crate::watchdog::Watchdog;
+ use lazy_static::lazy_static;
+ use std::sync::Arc;
+ use std::time::Duration;
+
+ lazy_static! {
+ /// A Watchdog thread, that can be used to create watch points.
+ static ref WD: Arc<Watchdog> = Watchdog::new(Duration::from_secs(10));
+ }
+
+ /// Sets a watch point with `id` and a timeout of `millis` milliseconds.
+ pub fn watch_millis(id: &'static str, millis: u64) -> Option<WatchPoint> {
+ Watchdog::watch(&WD, id, Duration::from_millis(millis))
+ }
+
+ /// Like `watch_millis` but with a callback that is called every time a report
+ /// is printed about this watch point.
+ pub fn watch_millis_with(
+ id: &'static str,
+ millis: u64,
+ callback: impl Fn() -> String + Send + 'static,
+ ) -> Option<WatchPoint> {
+ Watchdog::watch_with(&WD, id, Duration::from_millis(millis), callback)
+ }
+}
+
+/// This module provides empty/noop implementations of the watch dog utility functions.
+#[cfg(not(feature = "watchdog"))]
+pub mod watchdog {
+ /// Noop watch point.
+ pub struct WatchPoint();
+ /// Sets a Noop watch point.
+ fn watch_millis(_: &'static str, _: u64) -> Option<WatchPoint> {
+ None
+ }
+
+ pub fn watch_millis_with(
+ _: &'static str,
+ _: u64,
+ _: impl Fn() -> String + Send + 'static,
+ ) -> Option<WatchPoint> {
+ None
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use anyhow::Result;
+
+ #[test]
+ fn check_device_attestation_permissions_test() -> Result<()> {
+ check_device_attestation_permissions().or_else(|error| {
+ match error.root_cause().downcast_ref::<Error>() {
+ // Expected: the context for this test might not be allowed to attest device IDs.
+ Some(Error::Km(ErrorCode::CANNOT_ATTEST_IDS)) => Ok(()),
+ // Other errors are unexpected
+ _ => Err(error),
+ }
+ })
+ }
}
diff --git a/keystore2/src/vintf/Android.bp b/keystore2/src/vintf/Android.bp
new file mode 100644
index 0000000..34719aa
--- /dev/null
+++ b/keystore2/src/vintf/Android.bp
@@ -0,0 +1,73 @@
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "system_security_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["system_security_license"],
+}
+
+rust_library {
+ name: "libkeystore2_vintf_rust",
+ crate_name: "keystore2_vintf",
+ srcs: ["lib.rs"],
+ rustlibs: [
+ "libcxx",
+ ],
+ shared_libs: [
+ "libvintf",
+ ],
+ static_libs: [
+ "libkeystore2_vintf_cpp",
+ ],
+}
+
+cc_library_static {
+ name: "libkeystore2_vintf_cpp",
+ srcs: ["vintf.cpp"],
+ generated_headers: ["cxx-bridge-header"],
+ generated_sources: ["vintf_bridge_code"],
+ shared_libs: [
+ "libvintf",
+ ],
+}
+
+genrule {
+ name: "vintf_bridge_code",
+ tools: ["cxxbridge"],
+ cmd: "$(location cxxbridge) $(in) >> $(out)",
+ srcs: ["lib.rs"],
+ out: ["vintf_cxx_generated.cc"],
+}
+
+rust_test {
+ name: "keystore2_vintf_test",
+ crate_name: "keystore2_vintf_test",
+ srcs: ["lib.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ rustlibs: [
+ "libcxx",
+ ],
+ static_libs: [
+ "libkeystore2_vintf_cpp",
+ ],
+ shared_libs: [
+ "libc++",
+ "libvintf",
+ ],
+}
diff --git a/keystore2/src/vintf/lib.rs b/keystore2/src/vintf/lib.rs
new file mode 100644
index 0000000..89e18eb
--- /dev/null
+++ b/keystore2/src/vintf/lib.rs
@@ -0,0 +1,62 @@
+// 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.
+
+//! Bindings for getting the list of HALs.
+
+#[cxx::bridge]
+mod ffi {
+ unsafe extern "C++" {
+ include!("vintf.hpp");
+
+ /// Gets all HAL names.
+ /// Note that this is not a zero-cost shim: it will make copies of the strings.
+ fn get_hal_names() -> Vec<String>;
+
+ /// Gets all HAL names and versions.
+ /// Note that this is not a zero-cost shim: it will make copies of the strings.
+ fn get_hal_names_and_versions() -> Vec<String>;
+
+ /// Gets the instances of the given package, version, and interface tuple.
+ /// Note that this is not a zero-cost shim: it will make copies of the strings.
+ fn get_hidl_instances(
+ package: &str,
+ major_version: usize,
+ minor_version: usize,
+ interface_name: &str,
+ ) -> Vec<String>;
+
+ /// Gets the instances of the given package, version, and interface tuple.
+ /// Note that this is not a zero-cost shim: it will make copies of the strings.
+ fn get_aidl_instances(package: &str, version: usize, interface_name: &str) -> Vec<String>;
+ }
+}
+
+pub use ffi::*;
+
+#[cfg(test)]
+mod tests {
+
+ use super::*;
+
+ #[test]
+ fn test() {
+ let names = get_hal_names();
+ assert_ne!(names.len(), 0);
+
+ let names_and_versions = get_hal_names_and_versions();
+ assert_ne!(names_and_versions.len(), 0);
+
+ assert!(names_and_versions.len() >= names.len());
+ }
+}
diff --git a/keystore2/src/vintf/vintf.cpp b/keystore2/src/vintf/vintf.cpp
new file mode 100644
index 0000000..00625bf
--- /dev/null
+++ b/keystore2/src/vintf/vintf.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <algorithm>
+#include <vintf/HalManifest.h>
+#include <vintf/VintfObject.h>
+
+#include "rust/cxx.h"
+
+rust::Vec<rust::String> convert(const std::set<std::string>& names) {
+ rust::Vec<rust::String> result;
+ std::copy(names.begin(), names.end(), std::back_inserter(result));
+ return result;
+}
+
+rust::Vec<rust::String> get_hal_names() {
+ const auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
+ const auto names = manifest->getHalNames();
+ return convert(names);
+}
+
+rust::Vec<rust::String> get_hal_names_and_versions() {
+ const auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
+ const auto names = manifest->getHalNamesAndVersions();
+ return convert(names);
+}
+
+rust::Vec<rust::String> get_hidl_instances(rust::Str package, size_t major_version,
+ size_t minor_version, rust::Str interfaceName) {
+ android::vintf::Version version(major_version, minor_version);
+ const auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
+ const auto names = manifest->getHidlInstances(static_cast<std::string>(package), version,
+ static_cast<std::string>(interfaceName));
+ return convert(names);
+}
+
+rust::Vec<rust::String> get_aidl_instances(rust::Str package, size_t version,
+ rust::Str interfaceName) {
+ const auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
+ const auto names = manifest->getAidlInstances(static_cast<std::string>(package), version,
+ static_cast<std::string>(interfaceName));
+ return convert(names);
+}
diff --git a/keystore2/src/vintf/vintf.hpp b/keystore2/src/vintf/vintf.hpp
new file mode 100644
index 0000000..dbc88f0
--- /dev/null
+++ b/keystore2/src/vintf/vintf.hpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include "rust/cxx.h"
+
+rust::Vec<rust::String> get_hal_names();
+rust::Vec<rust::String> get_hal_names_and_versions();
+rust::Vec<rust::String> get_hidl_instances(rust::Str package, size_t major_version,
+ size_t minor_version, rust::Str interfaceName);
+rust::Vec<rust::String> get_aidl_instances(rust::Str package, size_t version,
+ rust::Str interfaceName);
diff --git a/keystore2/src/watchdog.rs b/keystore2/src/watchdog.rs
new file mode 100644
index 0000000..9cca171
--- /dev/null
+++ b/keystore2/src/watchdog.rs
@@ -0,0 +1,326 @@
+// 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.
+
+// Can be removed when instrumentations are added to keystore.
+#![allow(dead_code)]
+
+//! This module implements a watchdog thread.
+
+use std::{
+ cmp::min,
+ collections::HashMap,
+ sync::Arc,
+ sync::{Condvar, Mutex, MutexGuard},
+ thread,
+};
+use std::{
+ marker::PhantomData,
+ time::{Duration, Instant},
+};
+
+/// Represents a Watchdog record. It can be created with `Watchdog::watch` or
+/// `Watchdog::watch_with`. It disarms the record when dropped.
+pub struct WatchPoint {
+ id: &'static str,
+ wd: Arc<Watchdog>,
+ not_send: PhantomData<*mut ()>, // WatchPoint must not be Send.
+}
+
+impl Drop for WatchPoint {
+ fn drop(&mut self) {
+ self.wd.disarm(self.id)
+ }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+enum State {
+ NotRunning,
+ Running,
+}
+
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+struct Index {
+ tid: thread::ThreadId,
+ id: &'static str,
+}
+
+struct Record {
+ started: Instant,
+ deadline: Instant,
+ callback: Option<Box<dyn Fn() -> String + Send + 'static>>,
+}
+
+struct WatchdogState {
+ state: State,
+ thread: Option<thread::JoinHandle<()>>,
+ timeout: Duration,
+ records: HashMap<Index, Record>,
+ last_report: Instant,
+ has_overdue: bool,
+}
+
+impl WatchdogState {
+ fn update_overdue_and_find_next_timeout(&mut self) -> (bool, Option<Duration>) {
+ let now = Instant::now();
+ let mut next_timeout: Option<Duration> = None;
+ let mut has_overdue = false;
+ for (_, r) in self.records.iter() {
+ let timeout = r.deadline.saturating_duration_since(now);
+ if timeout == Duration::new(0, 0) {
+ has_overdue = true;
+ continue;
+ }
+ next_timeout = match next_timeout {
+ Some(nt) => {
+ if timeout < nt {
+ Some(timeout)
+ } else {
+ Some(nt)
+ }
+ }
+ None => Some(timeout),
+ };
+ }
+ (has_overdue, next_timeout)
+ }
+
+ fn log_report(&mut self, has_overdue: bool) -> bool {
+ match (self.has_overdue, has_overdue) {
+ (true, true) => {
+ if self.last_report.elapsed() < Watchdog::NOISY_REPORT_TIMEOUT {
+ self.has_overdue = false;
+ return false;
+ }
+ }
+ (_, false) => {
+ self.has_overdue = false;
+ return false;
+ }
+ (false, true) => {}
+ }
+ self.last_report = Instant::now();
+ self.has_overdue = has_overdue;
+ log::warn!("Keystore Watchdog report:");
+ log::warn!("Overdue records:");
+ let now = Instant::now();
+ for (i, r) in self.records.iter() {
+ if r.deadline.saturating_duration_since(now) == Duration::new(0, 0) {
+ match &r.callback {
+ Some(cb) => {
+ log::warn!(
+ "{:?} {} Pending: {:?} Overdue {:?}: {}",
+ i.tid,
+ i.id,
+ r.started.elapsed(),
+ r.deadline.elapsed(),
+ (cb)()
+ );
+ }
+ None => {
+ log::warn!(
+ "{:?} {} Pending: {:?} Overdue {:?}",
+ i.tid,
+ i.id,
+ r.started.elapsed(),
+ r.deadline.elapsed()
+ );
+ }
+ }
+ }
+ }
+ true
+ }
+
+ fn disarm(&mut self, index: Index) {
+ self.records.remove(&index);
+ }
+
+ fn arm(&mut self, index: Index, record: Record) {
+ if self.records.insert(index.clone(), record).is_some() {
+ log::warn!("Recursive watchdog record at \"{:?}\" replaces previous record.", index);
+ }
+ }
+}
+
+/// Watchdog spawns a thread that logs records of all overdue watch points when a deadline
+/// is missed and at least every second as long as overdue watch points exist.
+/// The thread terminates when idle for a given period of time.
+pub struct Watchdog {
+ state: Arc<(Condvar, Mutex<WatchdogState>)>,
+}
+
+impl Watchdog {
+ /// If we have overdue records, we want to be noisy about it and log a report
+ /// at least every `NOISY_REPORT_TIMEOUT` interval.
+ const NOISY_REPORT_TIMEOUT: Duration = Duration::from_secs(1);
+
+ /// Construct a [`Watchdog`]. When `timeout` has elapsed since the watchdog thread became
+ /// idle, i.e., there are no more active or overdue watch points, the watchdog thread
+ /// terminates.
+ pub fn new(timeout: Duration) -> Arc<Self> {
+ Arc::new(Self {
+ state: Arc::new((
+ Condvar::new(),
+ Mutex::new(WatchdogState {
+ state: State::NotRunning,
+ thread: None,
+ timeout,
+ records: HashMap::new(),
+ last_report: Instant::now(),
+ has_overdue: false,
+ }),
+ )),
+ })
+ }
+
+ fn watch_with_optional(
+ wd: &Arc<Self>,
+ callback: Option<Box<dyn Fn() -> String + Send + 'static>>,
+ id: &'static str,
+ timeout: Duration,
+ ) -> Option<WatchPoint> {
+ let deadline = Instant::now().checked_add(timeout);
+ if deadline.is_none() {
+ log::warn!("Deadline computation failed for WatchPoint \"{}\"", id);
+ log::warn!("WatchPoint not armed.");
+ return None;
+ }
+ wd.arm(callback, id, deadline.unwrap());
+ Some(WatchPoint { id, wd: wd.clone(), not_send: Default::default() })
+ }
+
+ /// Create a new watch point. If the WatchPoint is not dropped before the timeout
+ /// expires, a report is logged at least every second, which includes the id string
+ /// and whatever string the callback returns.
+ pub fn watch_with(
+ wd: &Arc<Self>,
+ id: &'static str,
+ timeout: Duration,
+ callback: impl Fn() -> String + Send + 'static,
+ ) -> Option<WatchPoint> {
+ Self::watch_with_optional(wd, Some(Box::new(callback)), id, timeout)
+ }
+
+ /// Like `watch_with`, but without a callback.
+ pub fn watch(wd: &Arc<Self>, id: &'static str, timeout: Duration) -> Option<WatchPoint> {
+ Self::watch_with_optional(wd, None, id, timeout)
+ }
+
+ fn arm(
+ &self,
+ callback: Option<Box<dyn Fn() -> String + Send + 'static>>,
+ id: &'static str,
+ deadline: Instant,
+ ) {
+ let tid = thread::current().id();
+ let index = Index { tid, id };
+ let record = Record { started: Instant::now(), deadline, callback };
+
+ let (ref condvar, ref state) = *self.state;
+
+ let mut state = state.lock().unwrap();
+ state.arm(index, record);
+
+ if state.state != State::Running {
+ self.spawn_thread(&mut state);
+ }
+ drop(state);
+ condvar.notify_all();
+ }
+
+ fn disarm(&self, id: &'static str) {
+ let tid = thread::current().id();
+ let index = Index { tid, id };
+ let (_, ref state) = *self.state;
+
+ let mut state = state.lock().unwrap();
+ state.disarm(index);
+ // There is no need to notify condvar. There is no action required for the
+ // watchdog thread before the next deadline.
+ }
+
+ fn spawn_thread(&self, state: &mut MutexGuard<WatchdogState>) {
+ if let Some(t) = state.thread.take() {
+ t.join().expect("Watchdog thread panicked.");
+ }
+
+ let cloned_state = self.state.clone();
+
+ state.thread = Some(thread::spawn(move || {
+ let (ref condvar, ref state) = *cloned_state;
+
+ let mut state = state.lock().unwrap();
+
+ loop {
+ let (has_overdue, next_timeout) = state.update_overdue_and_find_next_timeout();
+ state.log_report(has_overdue);
+ let (next_timeout, idle) = match (has_overdue, next_timeout) {
+ (true, Some(next_timeout)) => {
+ (min(next_timeout, Self::NOISY_REPORT_TIMEOUT), false)
+ }
+ (false, Some(next_timeout)) => (next_timeout, false),
+ (true, None) => (Self::NOISY_REPORT_TIMEOUT, false),
+ (false, None) => (state.timeout, true),
+ };
+
+ let (s, timeout) = condvar.wait_timeout(state, next_timeout).unwrap();
+ state = s;
+
+ if idle && timeout.timed_out() && state.records.is_empty() {
+ state.state = State::NotRunning;
+ break;
+ }
+ }
+ log::info!("Watchdog thread idle -> terminating. Have a great day.");
+ }));
+ state.state = State::Running;
+ }
+}
+
+#[cfg(test)]
+mod tests {
+
+ use super::*;
+ use std::sync::atomic;
+ use std::thread;
+ use std::time::Duration;
+
+ #[test]
+ fn test_watchdog() {
+ android_logger::init_once(
+ android_logger::Config::default()
+ .with_tag("keystore2_watchdog_tests")
+ .with_min_level(log::Level::Debug),
+ );
+
+ let wd = Watchdog::new(Watchdog::NOISY_REPORT_TIMEOUT.checked_mul(3).unwrap());
+ let hit_count = Arc::new(atomic::AtomicU8::new(0));
+ let hit_count_clone = hit_count.clone();
+ let wp =
+ Watchdog::watch_with(&wd, "test_watchdog", Duration::from_millis(100), move || {
+ format!("hit_count: {}", hit_count_clone.fetch_add(1, atomic::Ordering::Relaxed))
+ });
+ assert_eq!(0, hit_count.load(atomic::Ordering::Relaxed));
+ thread::sleep(Duration::from_millis(500));
+ assert_eq!(1, hit_count.load(atomic::Ordering::Relaxed));
+ thread::sleep(Watchdog::NOISY_REPORT_TIMEOUT);
+ assert_eq!(2, hit_count.load(atomic::Ordering::Relaxed));
+ drop(wp);
+ thread::sleep(Watchdog::NOISY_REPORT_TIMEOUT.checked_mul(4).unwrap());
+ assert_eq!(2, hit_count.load(atomic::Ordering::Relaxed));
+ let (_, ref state) = *wd.state;
+ let state = state.lock().unwrap();
+ assert_eq!(state.state, State::NotRunning);
+ }
+}
diff --git a/ondevice-signing/Android.bp b/ondevice-signing/Android.bp
index 5db19b7..efa0389 100644
--- a/ondevice-signing/Android.bp
+++ b/ondevice-signing/Android.bp
@@ -11,8 +11,6 @@
// 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.
-// List of clang-tidy checks that are reported as errors.
-// Please keep this list ordered lexicographically.
package {
// See: http://go/android-license-faq
@@ -23,10 +21,11 @@
default_applicable_licenses: ["system_security_license"],
}
+// List of clang-tidy checks that are reported as errors.
+// Please keep this list ordered lexicographically.
tidy_errors = [
"cert-err34-c",
"google-default-arguments",
- "google-explicit-constructor",
"google-runtime-int",
"google-runtime-member-string-references",
"misc-move-const-arg",
@@ -85,27 +84,29 @@
srcs: [
"odsign_main.cpp",
"CertUtils.cpp",
- "Keymaster.cpp",
- "KeymasterSigningKey.cpp",
+ "KeystoreKey.cpp",
+ "KeystoreHmacKey.cpp",
"VerityUtils.cpp",
],
+ header_libs: ["odrefresh_headers"],
+
static_libs: [
- "libmini_keyctl_static", // TODO need static?
"libc++fs",
+ "lib_odsign_proto",
+ "lib_compos_proto",
],
shared_libs: [
- "android.hardware.keymaster@4.1",
+ "android.system.keystore2-V1-cpp",
+ "android.hardware.security.keymint-V1-cpp",
"libbase",
+ "libbinder",
"libcrypto",
"libcrypto_utils",
"libfsverity",
- "libhidlbase",
"liblogwrap",
- "libkeymaster4support", // For authorization_set
- "libkeymaster4_1support",
- "libkeyutils",
+ "libprotobuf-cpp-lite",
"libutils",
],
}
diff --git a/ondevice-signing/CertUtils.cpp b/ondevice-signing/CertUtils.cpp
index 6b24391..d67bea6 100644
--- a/ondevice-signing/CertUtils.cpp
+++ b/ondevice-signing/CertUtils.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "CertUtils.h"
+
#include <android-base/logging.h>
#include <android-base/result.h>
@@ -21,103 +23,285 @@
#include <openssl/crypto.h>
#include <openssl/pkcs7.h>
#include <openssl/rsa.h>
+#include <openssl/x509.h>
#include <openssl/x509v3.h>
-#include <fcntl.h>
+#include <optional>
#include <vector>
-const char kBasicConstraints[] = "CA:TRUE";
-const char kKeyUsage[] = "critical,keyCertSign,cRLSign,digitalSignature";
-const char kSubjectKeyIdentifier[] = "hash";
+
+#include "KeyConstants.h"
+
+// Common properties for all of our certificates.
constexpr int kCertLifetimeSeconds = 10 * 365 * 24 * 60 * 60;
+const char* const kIssuerCountry = "US";
+const char* const kIssuerOrg = "Android";
-using android::base::Result;
-// using android::base::ErrnoError;
+using android::base::ErrnoError;
using android::base::Error;
+using android::base::Result;
-static bool add_ext(X509* cert, int nid, const char* value) {
- size_t len = strlen(value) + 1;
- std::vector<char> mutableValue(value, value + len);
- X509V3_CTX context;
+static Result<bssl::UniquePtr<X509>> loadX509(const std::string& path) {
+ X509* rawCert;
+ auto f = fopen(path.c_str(), "re");
+ if (f == nullptr) {
+ return Error() << "Failed to open " << path;
+ }
+ if (!d2i_X509_fp(f, &rawCert)) {
+ fclose(f);
+ return Error() << "Unable to decode x509 cert at " << path;
+ }
+ bssl::UniquePtr<X509> cert(rawCert);
- X509V3_set_ctx_nodb(&context);
+ fclose(f);
+ return cert;
+}
- X509V3_set_ctx(&context, cert, cert, nullptr, nullptr, 0);
- X509_EXTENSION* ex = X509V3_EXT_nconf_nid(nullptr, &context, nid, mutableValue.data());
+static X509V3_CTX makeContext(X509* issuer, X509* subject) {
+ X509V3_CTX context = {};
+ X509V3_set_ctx(&context, issuer, subject, nullptr, nullptr, 0);
+ return context;
+}
+
+static bool add_ext(X509V3_CTX* context, X509* cert, int nid, const char* value) {
+ bssl::UniquePtr<X509_EXTENSION> ex(X509V3_EXT_nconf_nid(nullptr, context, nid, value));
if (!ex) {
return false;
}
- X509_add_ext(cert, ex, -1);
- X509_EXTENSION_free(ex);
+ X509_add_ext(cert, ex.get(), -1);
return true;
}
+static void addNameEntry(X509_NAME* name, const char* field, const char* value) {
+ X509_NAME_add_entry_by_txt(name, field, MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>(value), -1, -1, 0);
+}
+
+static Result<bssl::UniquePtr<RSA>> getRsaFromModulus(const std::vector<uint8_t>& publicKey) {
+ bssl::UniquePtr<BIGNUM> n(BN_new());
+ bssl::UniquePtr<BIGNUM> e(BN_new());
+ bssl::UniquePtr<RSA> rsaPubkey(RSA_new());
+ if (!n || !e || !rsaPubkey || !BN_bin2bn(publicKey.data(), publicKey.size(), n.get()) ||
+ !BN_set_word(e.get(), kRsaKeyExponent) ||
+ !RSA_set0_key(rsaPubkey.get(), n.get(), e.get(), /*d=*/nullptr)) {
+ return Error() << "Failed to create RSA key";
+ }
+ // RSA_set0_key takes ownership of |n| and |e| on success.
+ (void)n.release();
+ (void)e.release();
+
+ return rsaPubkey;
+}
+
+static Result<bssl::UniquePtr<RSA>>
+getRsaFromRsaPublicKey(const std::vector<uint8_t>& rsaPublicKey) {
+ auto derBytes = rsaPublicKey.data();
+ bssl::UniquePtr<RSA> rsaKey(d2i_RSAPublicKey(nullptr, &derBytes, rsaPublicKey.size()));
+ if (rsaKey.get() == nullptr) {
+ return Error() << "Failed to parse RsaPublicKey";
+ }
+ if (derBytes != rsaPublicKey.data() + rsaPublicKey.size()) {
+ return Error() << "Key has unexpected trailing data";
+ }
+
+ return rsaKey;
+}
+
+static Result<bssl::UniquePtr<EVP_PKEY>> modulusToRsaPkey(const std::vector<uint8_t>& publicKey) {
+ // "publicKey" corresponds to the raw public key bytes - need to create
+ // a new RSA key with the correct exponent.
+ auto rsaPubkey = getRsaFromModulus(publicKey);
+ if (!rsaPubkey.ok()) {
+ return rsaPubkey.error();
+ }
+
+ bssl::UniquePtr<EVP_PKEY> public_key(EVP_PKEY_new());
+ if (!EVP_PKEY_assign_RSA(public_key.get(), rsaPubkey->release())) {
+ return Error() << "Failed to assign key";
+ }
+ return public_key;
+}
+
+static Result<bssl::UniquePtr<EVP_PKEY>>
+rsaPublicKeyToRsaPkey(const std::vector<uint8_t>& rsaPublicKey) {
+ // rsaPublicKey contains both modulus and exponent, DER-encoded.
+ auto rsaKey = getRsaFromRsaPublicKey(rsaPublicKey);
+ if (!rsaKey.ok()) {
+ return rsaKey.error();
+ }
+
+ bssl::UniquePtr<EVP_PKEY> public_key(EVP_PKEY_new());
+ if (!EVP_PKEY_assign_RSA(public_key.get(), rsaKey->release())) {
+ return Error() << "Failed to assign key";
+ }
+ return public_key;
+}
+
+Result<void> verifySignature(const std::string& message, const std::string& signature,
+ const std::vector<uint8_t>& publicKey) {
+ auto rsaKey = getRsaFromModulus(publicKey);
+ if (!rsaKey.ok()) {
+ return rsaKey.error();
+ }
+ uint8_t hashBuf[SHA256_DIGEST_LENGTH];
+ SHA256(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(message.c_str())),
+ message.length(), hashBuf);
+
+ bool success = RSA_verify(NID_sha256, hashBuf, sizeof(hashBuf),
+ (const uint8_t*)signature.c_str(), signature.length(), rsaKey->get());
+
+ if (!success) {
+ return Error() << "Failed to verify signature";
+ }
+ return {};
+}
+
+Result<void> verifyRsaPublicKeySignature(const std::string& message, const std::string& signature,
+ const std::vector<uint8_t>& rsaPublicKey) {
+ auto rsaKey = getRsaFromRsaPublicKey(rsaPublicKey);
+ if (!rsaKey.ok()) {
+ return rsaKey.error();
+ }
+
+ uint8_t hashBuf[SHA256_DIGEST_LENGTH];
+ SHA256(reinterpret_cast<const uint8_t*>(message.data()), message.size(), hashBuf);
+
+ bool success = RSA_verify(NID_sha256, hashBuf, sizeof(hashBuf),
+ reinterpret_cast<const uint8_t*>(signature.data()), signature.size(),
+ rsaKey->get());
+ if (!success) {
+ return Error() << "Failed to verify signature";
+ }
+ return {};
+}
+
+static Result<void> createCertificate(
+ const CertSubject& subject, EVP_PKEY* publicKey,
+ const std::function<android::base::Result<std::string>(const std::string&)>& signFunction,
+ const std::optional<std::string>& issuerCertPath, const std::string& path) {
+
+ // If an issuer cert is specified, we are signing someone else's key.
+ // Otherwise we are signing our key - a self-signed certificate.
+ bool selfSigned = !issuerCertPath;
+
+ bssl::UniquePtr<X509> x509(X509_new());
+ if (!x509) {
+ return Error() << "Unable to allocate x509 container";
+ }
+ X509_set_version(x509.get(), 2);
+ X509_gmtime_adj(X509_get_notBefore(x509.get()), 0);
+ X509_gmtime_adj(X509_get_notAfter(x509.get()), kCertLifetimeSeconds);
+ ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), subject.serialNumber);
+
+ bssl::UniquePtr<X509_ALGOR> algor(X509_ALGOR_new());
+ if (!algor ||
+ !X509_ALGOR_set0(algor.get(), OBJ_nid2obj(NID_sha256WithRSAEncryption), V_ASN1_NULL,
+ NULL) ||
+ !X509_set1_signature_algo(x509.get(), algor.get())) {
+ return Error() << "Unable to set x509 signature algorithm";
+ }
+
+ if (!X509_set_pubkey(x509.get(), publicKey)) {
+ return Error() << "Unable to set x509 public key";
+ }
+
+ X509_NAME* subjectName = X509_get_subject_name(x509.get());
+ if (!subjectName) {
+ return Error() << "Unable to get x509 subject name";
+ }
+ addNameEntry(subjectName, "C", kIssuerCountry);
+ addNameEntry(subjectName, "O", kIssuerOrg);
+ addNameEntry(subjectName, "CN", subject.commonName);
+
+ if (selfSigned) {
+ if (!X509_set_issuer_name(x509.get(), subjectName)) {
+ return Error() << "Unable to set x509 issuer name";
+ }
+ } else {
+ X509_NAME* issuerName = X509_get_issuer_name(x509.get());
+ if (!issuerName) {
+ return Error() << "Unable to get x509 issuer name";
+ }
+ addNameEntry(issuerName, "C", kIssuerCountry);
+ addNameEntry(issuerName, "O", kIssuerOrg);
+ addNameEntry(issuerName, "CN", kRootSubject.commonName);
+ }
+
+ // Beware: context contains a pointer to issuerCert, so we need to keep it alive.
+ bssl::UniquePtr<X509> issuerCert;
+ X509V3_CTX context;
+
+ if (selfSigned) {
+ context = makeContext(x509.get(), x509.get());
+ } else {
+ auto certStatus = loadX509(*issuerCertPath);
+ if (!certStatus.ok()) {
+ return Error() << "Unable to load issuer cert: " << certStatus.error();
+ }
+ issuerCert = std::move(certStatus.value());
+ context = makeContext(issuerCert.get(), x509.get());
+ }
+
+ // If it's a self-signed cert we use it for signing certs, otherwise only for signing data.
+ const char* basicConstraints = selfSigned ? "CA:TRUE" : "CA:FALSE";
+ const char* keyUsage =
+ selfSigned ? "critical,keyCertSign,cRLSign,digitalSignature" : "critical,digitalSignature";
+
+ add_ext(&context, x509.get(), NID_basic_constraints, basicConstraints);
+ add_ext(&context, x509.get(), NID_key_usage, keyUsage);
+ add_ext(&context, x509.get(), NID_subject_key_identifier, "hash");
+ add_ext(&context, x509.get(), NID_authority_key_identifier, "keyid:always");
+
+ // Get the data to be signed
+ unsigned char* to_be_signed_buf(nullptr);
+ size_t to_be_signed_length = i2d_re_X509_tbs(x509.get(), &to_be_signed_buf);
+
+ auto signed_data = signFunction(
+ std::string(reinterpret_cast<const char*>(to_be_signed_buf), to_be_signed_length));
+ if (!signed_data.ok()) {
+ return signed_data.error();
+ }
+
+ if (!X509_set1_signature_value(x509.get(),
+ reinterpret_cast<const uint8_t*>(signed_data->data()),
+ signed_data->size())) {
+ return Error() << "Unable to set x509 signature";
+ }
+
+ auto f = fopen(path.c_str(), "wbe");
+ if (f == nullptr) {
+ return ErrnoError() << "Failed to open " << path;
+ }
+ i2d_X509_fp(f, x509.get());
+ if (fclose(f) != 0) {
+ return ErrnoError() << "Failed to close " << path;
+ }
+
+ return {};
+}
+
Result<void> createSelfSignedCertificate(
const std::vector<uint8_t>& publicKey,
const std::function<Result<std::string>(const std::string&)>& signFunction,
const std::string& path) {
- bssl::UniquePtr<X509> x509(X509_new());
- if (!x509) {
- return Error() << "Unable to allocate x509 container";
- }
- X509_set_version(x509.get(), 2);
-
- ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), 1);
- X509_gmtime_adj(X509_get_notBefore(x509.get()), 0);
- X509_gmtime_adj(X509_get_notAfter(x509.get()), kCertLifetimeSeconds);
-
- auto pubKeyData = publicKey.data();
- EVP_PKEY* public_key = d2i_PUBKEY(nullptr, &pubKeyData, publicKey.size());
- if (!X509_set_pubkey(x509.get(), public_key)) {
- return Error() << "Unable to set x509 public key";
+ auto rsa_pkey = modulusToRsaPkey(publicKey);
+ if (!rsa_pkey.ok()) {
+ return rsa_pkey.error();
}
- X509_NAME* name = X509_get_subject_name(x509.get());
- if (!name) {
- return Error() << "Unable to get x509 subject name";
- }
- X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
- reinterpret_cast<const unsigned char*>("US"), -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
- reinterpret_cast<const unsigned char*>("Android"), -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
- reinterpret_cast<const unsigned char*>("ODS"), -1, -1, 0);
- if (!X509_set_issuer_name(x509.get(), name)) {
- return Error() << "Unable to set x509 issuer name";
+ return createCertificate(kRootSubject, rsa_pkey.value().get(), signFunction, {}, path);
+}
+
+android::base::Result<void> createLeafCertificate(
+ const CertSubject& subject, const std::vector<uint8_t>& rsaPublicKey,
+ const std::function<android::base::Result<std::string>(const std::string&)>& signFunction,
+ const std::string& issuerCertPath, const std::string& path) {
+ auto rsa_pkey = rsaPublicKeyToRsaPkey(rsaPublicKey);
+ if (!rsa_pkey.ok()) {
+ return rsa_pkey.error();
}
- add_ext(x509.get(), NID_basic_constraints, kBasicConstraints);
- add_ext(x509.get(), NID_key_usage, kKeyUsage);
- add_ext(x509.get(), NID_subject_key_identifier, kSubjectKeyIdentifier);
- add_ext(x509.get(), NID_authority_key_identifier, "keyid:always");
-
- X509_ALGOR_set0(x509->cert_info->signature, OBJ_nid2obj(NID_sha256WithRSAEncryption),
- V_ASN1_NULL, NULL);
- X509_ALGOR_set0(x509->sig_alg, OBJ_nid2obj(NID_sha256WithRSAEncryption), V_ASN1_NULL, NULL);
-
- // Get the data to be signed
- char* to_be_signed_buf(nullptr);
- size_t to_be_signed_length = i2d_re_X509_tbs(x509.get(), (unsigned char**)&to_be_signed_buf);
-
- auto signed_data = signFunction(std::string(to_be_signed_buf, to_be_signed_length));
- if (!signed_data.ok()) {
- return signed_data.error();
- }
-
- // This is the only part that doesn't use boringssl default functions - we manually copy in the
- // signature that was provided to us.
- x509->signature->data = (unsigned char*)OPENSSL_malloc(signed_data->size());
- memcpy(x509->signature->data, signed_data->c_str(), signed_data->size());
- x509->signature->length = signed_data->size();
-
- x509->signature->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
- x509->signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
- auto f = fopen(path.c_str(), "wb");
- // TODO error checking
- i2d_X509_fp(f, x509.get());
- fclose(f);
-
- return {};
+ return createCertificate(subject, rsa_pkey.value().get(), signFunction, issuerCertPath, path);
}
Result<std::vector<uint8_t>> extractPublicKey(EVP_PKEY* pkey) {
@@ -125,15 +309,14 @@
return Error() << "Failed to extract public key from x509 cert";
}
- if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) {
+ if (EVP_PKEY_id(pkey) != EVP_PKEY_RSA) {
return Error() << "The public key is not an RSA key";
}
- RSA* rsa = EVP_PKEY_get1_RSA(pkey);
- auto num_bytes = BN_num_bytes(rsa->n);
+ RSA* rsa = EVP_PKEY_get0_RSA(pkey);
+ auto num_bytes = BN_num_bytes(RSA_get0_n(rsa));
std::vector<uint8_t> pubKey(num_bytes);
- int res = BN_bn2bin(rsa->n, pubKey.data());
- RSA_free(rsa);
+ int res = BN_bn2bin(RSA_get0_n(rsa), pubKey.data());
if (!res) {
return Error() << "Failed to convert public key to bytes";
@@ -142,25 +325,95 @@
return pubKey;
}
-Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::vector<uint8_t>& keyData) {
+Result<std::vector<uint8_t>>
+extractPublicKeyFromSubjectPublicKeyInfo(const std::vector<uint8_t>& keyData) {
auto keyDataBytes = keyData.data();
- EVP_PKEY* public_key = d2i_PUBKEY(nullptr, &keyDataBytes, keyData.size());
+ bssl::UniquePtr<EVP_PKEY> public_key(d2i_PUBKEY(nullptr, &keyDataBytes, keyData.size()));
- return extractPublicKey(public_key);
+ return extractPublicKey(public_key.get());
+}
+
+Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::vector<uint8_t>& derCert) {
+ auto derCertBytes = derCert.data();
+ bssl::UniquePtr<X509> decoded_cert(d2i_X509(nullptr, &derCertBytes, derCert.size()));
+ if (decoded_cert.get() == nullptr) {
+ return Error() << "Failed to decode X509 certificate.";
+ }
+ bssl::UniquePtr<EVP_PKEY> decoded_pkey(X509_get_pubkey(decoded_cert.get()));
+
+ return extractPublicKey(decoded_pkey.get());
}
Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::string& path) {
- X509* cert;
- auto f = fopen(path.c_str(), "r");
- if (!d2i_X509_fp(f, &cert)) {
- return Error() << "Unable to decode x509 cert at " << path;
+ auto cert = loadX509(path);
+ if (!cert.ok()) {
+ return cert.error();
}
-
- fclose(f);
- return extractPublicKey(X509_get_pubkey(cert));
+ return extractPublicKey(X509_get_pubkey(cert.value().get()));
}
-Result<std::vector<uint8_t>> createPkcs7(const std::vector<uint8_t>& signed_digest) {
+Result<std::vector<uint8_t>> extractRsaPublicKey(EVP_PKEY* pkey) {
+ RSA* rsa = EVP_PKEY_get0_RSA(pkey);
+ if (rsa == nullptr) {
+ return Error() << "The public key is not an RSA key";
+ }
+
+ uint8_t* out = nullptr;
+ int size = i2d_RSAPublicKey(rsa, &out);
+ if (size < 0 || !out) {
+ return Error() << "Failed to convert to RSAPublicKey";
+ }
+
+ bssl::UniquePtr<uint8_t> buffer(out);
+ std::vector<uint8_t> result(out, out + size);
+ return result;
+}
+
+Result<CertInfo> verifyAndExtractCertInfoFromX509(const std::string& path,
+ const std::vector<uint8_t>& publicKey) {
+ auto public_key = modulusToRsaPkey(publicKey);
+ if (!public_key.ok()) {
+ return public_key.error();
+ }
+
+ auto cert = loadX509(path);
+ if (!cert.ok()) {
+ return cert.error();
+ }
+ X509* x509 = cert.value().get();
+
+ // Make sure we signed it.
+ if (X509_verify(x509, public_key.value().get()) != 1) {
+ return Error() << "Failed to verify certificate.";
+ }
+
+ bssl::UniquePtr<EVP_PKEY> pkey(X509_get_pubkey(x509));
+ auto subject_key = extractRsaPublicKey(pkey.get());
+ if (!subject_key.ok()) {
+ return subject_key.error();
+ }
+
+ // The pointers here are all owned by x509, and each function handles an
+ // error return from the previous call correctly.
+ X509_NAME* name = X509_get_subject_name(x509);
+ int index = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
+ X509_NAME_ENTRY* entry = X509_NAME_get_entry(name, index);
+ ASN1_STRING* asn1cn = X509_NAME_ENTRY_get_data(entry);
+ unsigned char* utf8cn;
+ int length = ASN1_STRING_to_UTF8(&utf8cn, asn1cn);
+ if (length < 0) {
+ return Error() << "Failed to read subject CN";
+ }
+
+ bssl::UniquePtr<unsigned char> utf8owner(utf8cn);
+ std::string cn(reinterpret_cast<char*>(utf8cn), static_cast<size_t>(length));
+
+ CertInfo cert_info{std::move(cn), std::move(subject_key.value())};
+ return cert_info;
+}
+
+Result<std::vector<uint8_t>> createPkcs7(const std::vector<uint8_t>& signed_digest,
+ const CertSubject& signer) {
CBB out, outer_seq, wrapped_seq, seq, digest_algos_set, digest_algo, null;
CBB content_info, issuer_and_serial, signer_infos, signer_info, sign_algo, signature;
uint8_t *pkcs7_data, *name_der;
@@ -168,19 +421,20 @@
BIGNUM* serial = BN_new();
int sig_nid = NID_rsaEncryption;
- X509_NAME* name = X509_NAME_new();
- if (!name) {
- return Error() << "Unable to get x509 subject name";
+ X509_NAME* issuer_name = X509_NAME_new();
+ if (!issuer_name) {
+ return Error() << "Unable to create x509 subject name";
}
- X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
- reinterpret_cast<const unsigned char*>("US"), -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
- reinterpret_cast<const unsigned char*>("Android"), -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
- reinterpret_cast<const unsigned char*>("ODS"), -1, -1, 0);
+ X509_NAME_add_entry_by_txt(issuer_name, "C", MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>(kIssuerCountry), -1, -1, 0);
+ X509_NAME_add_entry_by_txt(issuer_name, "O", MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>(kIssuerOrg), -1, -1, 0);
+ X509_NAME_add_entry_by_txt(issuer_name, "CN", MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>(kRootSubject.commonName), -1,
+ -1, 0);
- BN_set_word(serial, 1);
- name_der_len = i2d_X509_NAME(name, &name_der);
+ BN_set_word(serial, signer.serialNumber);
+ name_der_len = i2d_X509_NAME(issuer_name, &name_der);
CBB_init(&out, 1024);
if (!CBB_add_asn1(&out, &outer_seq, CBS_ASN1_SEQUENCE) ||
diff --git a/ondevice-signing/CertUtils.h b/ondevice-signing/CertUtils.h
index d9172d0..fe703fa 100644
--- a/ondevice-signing/CertUtils.h
+++ b/ondevice-signing/CertUtils.h
@@ -16,14 +16,57 @@
#pragma once
+#include <functional>
+#include <string>
+#include <vector>
+
#include <android-base/result.h>
+// Information extracted from a certificate.
+struct CertInfo {
+ std::string subjectCn;
+ std::vector<uint8_t> subjectRsaPublicKey;
+};
+
+// Subjects of certificates we issue.
+struct CertSubject {
+ const char* commonName;
+ unsigned serialNumber;
+};
+
+// These are all the certificates we ever sign (the first one being our
+// self-signed cert). We shouldn't really re-use serial numbers for different
+// certificates for the same subject but we do; only one should be in use at a
+// time though.
+inline const CertSubject kRootSubject{"ODS", 1};
+inline const CertSubject kCompOsSubject{"CompOs", 2};
+
android::base::Result<void> createSelfSignedCertificate(
const std::vector<uint8_t>& publicKey,
const std::function<android::base::Result<std::string>(const std::string&)>& signFunction,
const std::string& path);
-android::base::Result<std::vector<uint8_t>> createPkcs7(const std::vector<uint8_t>& signedData);
+
+android::base::Result<void> createLeafCertificate(
+ const CertSubject& subject, const std::vector<uint8_t>& publicKey,
+ const std::function<android::base::Result<std::string>(const std::string&)>& signFunction,
+ const std::string& issuerCertPath, const std::string& outPath);
+
+android::base::Result<std::vector<uint8_t>> createPkcs7(const std::vector<uint8_t>& signedData,
+ const CertSubject& signer);
android::base::Result<std::vector<uint8_t>>
-extractPublicKeyFromX509(const std::vector<uint8_t>& path);
+extractPublicKeyFromX509(const std::vector<uint8_t>& x509);
+android::base::Result<std::vector<uint8_t>>
+extractPublicKeyFromSubjectPublicKeyInfo(const std::vector<uint8_t>& subjectKeyInfo);
android::base::Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::string& path);
+
+android::base::Result<CertInfo>
+verifyAndExtractCertInfoFromX509(const std::string& path, const std::vector<uint8_t>& publicKey);
+
+android::base::Result<void> verifySignature(const std::string& message,
+ const std::string& signature,
+ const std::vector<uint8_t>& publicKey);
+
+android::base::Result<void> verifyRsaPublicKeySignature(const std::string& message,
+ const std::string& signature,
+ const std::vector<uint8_t>& rsaPublicKey);
diff --git a/keystore/binder/android/security/keymaster/OperationResult.aidl b/ondevice-signing/KeyConstants.h
similarity index 70%
copy from keystore/binder/android/security/keymaster/OperationResult.aidl
copy to ondevice-signing/KeyConstants.h
index db689d4..ccc9251 100644
--- a/keystore/binder/android/security/keymaster/OperationResult.aidl
+++ b/ondevice-signing/KeyConstants.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 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.
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-package android.security.keymaster;
+static constexpr int kRsaKeySize = 2048;
+static constexpr int kRsaKeyExponent = 65537;
-/* @hide */
-parcelable OperationResult cpp_header "keystore/OperationResult.h";
+static constexpr int kHmacKeySize = 256;
+static constexpr int kHmacMinMacLength = 256;
diff --git a/ondevice-signing/Keymaster.cpp b/ondevice-signing/Keymaster.cpp
deleted file mode 100644
index 6cfb565..0000000
--- a/ondevice-signing/Keymaster.cpp
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#include <string>
-
-#include <android-base/logging.h>
-#include <keymasterV4_1/Keymaster.h>
-#include <keymasterV4_1/authorization_set.h>
-
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include "Keymaster.h"
-
-using AuthorizationSet = ::android::hardware::keymaster::V4_0::AuthorizationSet;
-using AuthorizationSetBuilder = ::android::hardware::keymaster::V4_0::AuthorizationSetBuilder;
-using Digest = ::android::hardware::keymaster::V4_0::Digest;
-using ErrorCode = ::android::hardware::keymaster::V4_0::ErrorCode;
-using HardwareAuthToken = ::android::hardware::keymaster::V4_0::HardwareAuthToken;
-using HidlBuf = ::android::hardware::hidl_vec<uint8_t>;
-using KeyCharacteristics = ::android::hardware::keymaster::V4_0::KeyCharacteristics;
-using KeyFormat = ::android::hardware::keymaster::V4_0::KeyFormat;
-using KeyParameter = ::android::hardware::keymaster::V4_0::KeyParameter;
-using KeyPurpose = ::android::hardware::keymaster::V4_0::KeyPurpose;
-using KmSupport = ::android::hardware::keymaster::V4_1::support::Keymaster;
-using KmDevice = ::android::hardware::keymaster::V4_1::IKeymasterDevice;
-using OperationHandle = ::android::hardware::keymaster::V4_0::OperationHandle;
-using PaddingMode = ::android::hardware::keymaster::V4_0::PaddingMode;
-using VerificationToken = ::android::hardware::keymaster::V4_0::VerificationToken;
-
-using android::sp;
-using android::base::Error;
-using android::base::Result;
-using android::base::unique_fd;
-using android::hardware::hidl_vec;
-
-Keymaster::Keymaster() {}
-
-bool Keymaster::initialize() {
- // TODO(b/165630556): Stop using Keymaster directly and migrate to keystore2
- // (once available).
- auto devices = KmSupport::enumerateAvailableDevices();
- sp<KmDevice> devToUse = nullptr;
- for (const auto& dev : devices) {
- auto version = dev->halVersion();
- if (version.majorVersion > 4 || (version.majorVersion == 4 && version.minorVersion >= 1)) {
- // TODO we probably have a preference for the SE, hoping Keystore2 will provide this
- LOG(INFO) << "Using keymaster " << version.keymasterName << " "
- << (int)version.majorVersion << "." << (int)version.minorVersion;
- devToUse = dev;
- break;
- }
- }
-
- if (devToUse == nullptr) {
- LOG(WARNING) << "Didn't find a keymaster to use.";
- }
- mDevice = devToUse;
-
- return mDevice != nullptr;
-}
-
-std::optional<Keymaster> Keymaster::getInstance() {
- static Keymaster keymaster;
-
- if (!keymaster.initialize()) {
- return {};
- } else {
- return {keymaster};
- }
-}
-
-Result<std::vector<uint8_t>> Keymaster::createKey() const {
- ErrorCode error;
- HidlBuf keyBlob;
-
- auto params = AuthorizationSetBuilder()
- .Authorization(::android::hardware::keymaster::V4_0::TAG_NO_AUTH_REQUIRED)
- // TODO MAKE SURE WE ADD THE EARLY_BOOT_ONLY FLAG here
- // currently doesn't work on cuttlefish (b/173618442)
- //.Authorization(::android::hardware::keymaster::V4_1::TAG_EARLY_BOOT_ONLY)
- .RsaSigningKey(2048, 65537)
- .Digest(Digest::SHA_2_256)
- .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN);
-
- mDevice->generateKey(params.hidl_data(), [&](ErrorCode hidl_error, const HidlBuf& hidl_key_blob,
- const KeyCharacteristics&
- /* hidl_key_characteristics */) {
- error = hidl_error;
- keyBlob = hidl_key_blob;
- });
-
- if (error != ErrorCode::OK) {
- return Error() << "Error creating keymaster signing key: "
- << static_cast<std::underlying_type<ErrorCode>::type>(error);
- }
-
- return keyBlob;
-}
-
-static ErrorCode Begin(const sp<KmDevice>& keymaster_, KeyPurpose purpose, const HidlBuf& key_blob,
- const AuthorizationSet& in_params, AuthorizationSet* out_params,
- OperationHandle* op_handle) {
- ErrorCode error;
- OperationHandle saved_handle = *op_handle;
- CHECK(keymaster_
- ->begin(purpose, key_blob, in_params.hidl_data(), HardwareAuthToken(),
- [&](ErrorCode hidl_error, const hidl_vec<KeyParameter>& hidl_out_params,
- uint64_t hidl_op_handle) {
- error = hidl_error;
- *out_params = hidl_out_params;
- *op_handle = hidl_op_handle;
- })
- .isOk());
- if (error != ErrorCode::OK) {
- // Some implementations may modify *op_handle on error.
- *op_handle = saved_handle;
- }
- return error;
-}
-
-static ErrorCode Update(const sp<KmDevice>& keymaster_, OperationHandle op_handle,
- const AuthorizationSet& in_params, const std::string& input,
- AuthorizationSet* out_params, std::string* output, size_t* input_consumed) {
- ErrorCode error;
- HidlBuf inputData(input.size());
- memcpy(inputData.data(), input.c_str(), input.size());
- CHECK(keymaster_
- ->update(op_handle, in_params.hidl_data(), inputData, HardwareAuthToken(),
- VerificationToken(),
- [&](ErrorCode hidl_error, uint32_t hidl_input_consumed,
- const hidl_vec<KeyParameter>& hidl_out_params,
- const HidlBuf& hidl_output) {
- error = hidl_error;
- out_params->push_back(AuthorizationSet(hidl_out_params));
- std::string retdata(reinterpret_cast<const char*>(hidl_output.data()),
- hidl_output.size());
- output->append(retdata);
- *input_consumed = hidl_input_consumed;
- })
- .isOk());
- return error;
-}
-
-static ErrorCode Finish(const sp<KmDevice>& keymaster_, OperationHandle op_handle,
- const AuthorizationSet& in_params, const std::string& input,
- const std::string& signature, AuthorizationSet* out_params,
- std::string* output) {
- ErrorCode error;
- HidlBuf inputData(input.size());
- memcpy(inputData.data(), input.c_str(), input.size());
- HidlBuf signatureData(signature.size());
- memcpy(signatureData.data(), signature.c_str(), signature.size());
- // TODO still need to handle error -62 - key requires upgrade
- CHECK(keymaster_
- ->finish(op_handle, in_params.hidl_data(), inputData, signatureData,
- HardwareAuthToken(), VerificationToken(),
- [&](ErrorCode hidl_error, const hidl_vec<KeyParameter>& hidl_out_params,
- const HidlBuf& hidl_output) {
- error = hidl_error;
- *out_params = hidl_out_params;
- std::string retdata(reinterpret_cast<const char*>(hidl_output.data()),
- hidl_output.size());
- output->append(retdata);
- })
- .isOk());
- return error;
-}
-
-static std::string ProcessMessage(const sp<KmDevice>& keymaster_, const HidlBuf& key_blob,
- KeyPurpose operation, const std::string& message,
- const AuthorizationSet& in_params, AuthorizationSet* out_params) {
- AuthorizationSet begin_out_params;
- OperationHandle op_handle_;
- ErrorCode ec =
- Begin(keymaster_, operation, key_blob, in_params, &begin_out_params, &op_handle_);
-
- std::string output;
- size_t consumed = 0;
- AuthorizationSet update_params;
- AuthorizationSet update_out_params;
- ec = Update(keymaster_, op_handle_, update_params, message, &update_out_params, &output,
- &consumed);
-
- std::string unused;
- AuthorizationSet finish_params;
- AuthorizationSet finish_out_params;
- ec = Finish(keymaster_, op_handle_, finish_params, message.substr(consumed), unused,
- &finish_out_params, &output);
-
- out_params->push_back(begin_out_params);
- out_params->push_back(finish_out_params);
- return output;
-}
-
-Result<std::vector<uint8_t>>
-Keymaster::extractPublicKey(const std::vector<uint8_t>& keyBlob) const {
- std::vector<uint8_t> publicKey;
- ErrorCode error;
-
- mDevice->exportKey(KeyFormat::X509, keyBlob, {} /* clientId */, {} /* appData */,
- [&](ErrorCode hidl_error, const HidlBuf& keyData) {
- error = hidl_error;
- publicKey = keyData;
- });
-
- if (error != ErrorCode::OK) {
- return Error() << "Error extracting public key: "
- << static_cast<std::underlying_type<ErrorCode>::type>(error);
- }
-
- return publicKey;
-}
-
-Result<KeymasterVerifyResult> Keymaster::verifyKey(const std::vector<uint8_t>& keyBlob) const {
- ErrorCode error;
- KeyCharacteristics characteristics;
-
- mDevice->getKeyCharacteristics(
- keyBlob, {} /* clientId */, {} /* appData */,
- [&](ErrorCode hidl_error, const KeyCharacteristics& hidl_characteristics) {
- error = hidl_error;
- characteristics = hidl_characteristics;
- });
-
- if (error == ErrorCode::KEY_REQUIRES_UPGRADE) {
- return KeymasterVerifyResult::UPGRADE;
- }
-
- if (error != ErrorCode::OK) {
- return Error() << "Error getting key characteristics: "
- << static_cast<std::underlying_type<ErrorCode>::type>(error);
- }
-
- // TODO(b/165630556)
- // Verify this is an early boot key and the other key parameters
- return KeymasterVerifyResult::OK;
-}
-
-Result<std::vector<uint8_t>> Keymaster::upgradeKey(const std::vector<uint8_t>& keyBlob) const {
- ErrorCode error;
- HidlBuf newKeyBlob;
-
- // TODO deduplicate
- auto params = AuthorizationSetBuilder()
- .Authorization(::android::hardware::keymaster::V4_0::TAG_NO_AUTH_REQUIRED)
- // TODO MAKE SURE WE ADD THE EARLY_BOOT_ONLY FLAG here
- // currently doesn't work on cuttlefish (b/173618442)
- //.Authorization(::android::hardware::keymaster::V4_1::TAG_EARLY_BOOT_ONLY)
- .RsaSigningKey(2048, 65537)
- .Digest(Digest::SHA_2_256)
- .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN);
-
- mDevice->upgradeKey(keyBlob, params.hidl_data(),
- [&](ErrorCode hidl_error, const HidlBuf& hidl_key_blob) {
- error = hidl_error;
- newKeyBlob = hidl_key_blob;
- });
-
- if (error != ErrorCode::OK) {
- return Error() << "Error upgrading keymaster signing key: "
- << static_cast<std::underlying_type<ErrorCode>::type>(error);
- }
-
- return newKeyBlob;
-}
-
-Result<std::string> Keymaster::sign(const std::vector<uint8_t>& keyBlob,
- const std::string& message) const {
- AuthorizationSet out_params;
- auto params = AuthorizationSetBuilder()
- .Digest(Digest::SHA_2_256)
- .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN);
- std::string signature =
- ProcessMessage(mDevice, keyBlob, KeyPurpose::SIGN, message, params, &out_params);
- if (!out_params.empty()) {
- return Error() << "Error signing key: expected empty out params.";
- }
- return signature;
-}
diff --git a/ondevice-signing/Keymaster.h b/ondevice-signing/Keymaster.h
deleted file mode 100644
index 455289f..0000000
--- a/ondevice-signing/Keymaster.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#pragma once
-
-#include <optional>
-
-#include <android-base/macros.h>
-#include <android-base/result.h>
-#include <android-base/unique_fd.h>
-
-#include <keymasterV4_1/Keymaster.h>
-
-#include <utils/StrongPointer.h>
-
-enum class KeymasterVerifyResult {
- OK = 0,
- UPGRADE = -1,
-};
-
-class Keymaster {
- using KmDevice = ::android::hardware::keymaster::V4_1::IKeymasterDevice;
-
- public:
- static std::optional<Keymaster> getInstance();
-
- android::base::Result<std::vector<uint8_t>> createKey() const;
-
- android::base::Result<std::vector<uint8_t>>
- extractPublicKey(const std::vector<uint8_t>& keyBlob) const;
-
- android::base::Result<KeymasterVerifyResult>
- verifyKey(const std::vector<uint8_t>& keyBlob) const;
-
- android::base::Result<std::vector<uint8_t>>
- upgradeKey(const std::vector<uint8_t>& keyBlob) const;
-
- /* Sign a message with an initialized signing key */
- android::base::Result<std::string> sign(const std::vector<uint8_t>& keyBlob,
- const std::string& message) const;
-
- private:
- Keymaster();
- bool initialize();
-
- android::sp<KmDevice> mDevice;
-};
diff --git a/ondevice-signing/KeymasterSigningKey.cpp b/ondevice-signing/KeymasterSigningKey.cpp
deleted file mode 100644
index 2b748e4..0000000
--- a/ondevice-signing/KeymasterSigningKey.cpp
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include "CertUtils.h"
-#include "Keymaster.h"
-#include "KeymasterSigningKey.h"
-
-using android::base::ErrnoError;
-using android::base::Error;
-using android::base::ReadFileToString;
-using android::base::Result;
-using android::base::unique_fd;
-
-KeymasterSigningKey::KeymasterSigningKey() {}
-
-Result<KeymasterSigningKey> KeymasterSigningKey::loadFromBlobAndVerify(const std::string& path) {
- KeymasterSigningKey signingKey;
-
- auto status = signingKey.initializeFromKeyblob(path);
-
- if (!status.ok()) {
- return status.error();
- }
-
- return std::move(signingKey);
-}
-
-Result<KeymasterSigningKey> KeymasterSigningKey::createNewKey() {
- KeymasterSigningKey signingKey;
-
- auto status = signingKey.createSigningKey();
-
- if (!status.ok()) {
- return status.error();
- }
-
- return std::move(signingKey);
-}
-
-Result<void> KeymasterSigningKey::createSigningKey() {
- KeymasterSigningKey signingKey;
- auto keymaster = Keymaster::getInstance();
- if (!keymaster.has_value()) {
- return Error() << "Failed to initialize keymaster.";
- }
- mKeymaster = keymaster;
-
- auto keyBlob = mKeymaster->createKey();
-
- if (!keyBlob.ok()) {
- return keyBlob.error();
- }
-
- mVerifiedKeyBlob.assign(keyBlob->begin(), keyBlob->end());
-
- return {};
-}
-
-Result<void> KeymasterSigningKey::saveKeyblob(const std::string& path) const {
- int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC;
-
- unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, 0600)));
- if (fd == -1) {
- return ErrnoError() << "Error creating key blob file " << path;
- }
-
- if (!android::base::WriteFully(fd, mVerifiedKeyBlob.data(), mVerifiedKeyBlob.size())) {
- return ErrnoError() << "Error writing key blob file " << path;
- } else {
- return {};
- }
-}
-
-Result<std::vector<uint8_t>> KeymasterSigningKey::getPublicKey() const {
- auto publicKeyX509 = mKeymaster->extractPublicKey(mVerifiedKeyBlob);
- if (!publicKeyX509.ok()) {
- return publicKeyX509.error();
- }
- return extractPublicKeyFromX509(publicKeyX509.value());
-}
-
-Result<void> KeymasterSigningKey::createX509Cert(const std::string& outPath) const {
- auto publicKey = mKeymaster->extractPublicKey(mVerifiedKeyBlob);
-
- if (!publicKey.ok()) {
- return publicKey.error();
- }
-
- auto keymasterSignFunction = [&](const std::string& to_be_signed) {
- return this->sign(to_be_signed);
- };
- createSelfSignedCertificate(*publicKey, keymasterSignFunction, outPath);
- return {};
-}
-
-Result<void> KeymasterSigningKey::initializeFromKeyblob(const std::string& path) {
- std::string keyBlobData;
- auto keymaster = Keymaster::getInstance();
- if (!keymaster.has_value()) {
- return Error() << "Failed to initialize keymaster.";
- }
- mKeymaster = keymaster;
-
- bool result = ReadFileToString(path, &keyBlobData);
- if (!result) {
- return ErrnoError() << "Failed to read " << path;
- }
-
- std::vector<uint8_t> keyBlob = {keyBlobData.begin(), keyBlobData.end()};
-
- auto verifyResult = mKeymaster->verifyKey(keyBlob);
- if (!verifyResult.ok()) {
- return Error() << "Failed to verify key: " << verifyResult.error().message();
- }
-
- if (*verifyResult == KeymasterVerifyResult::UPGRADE) {
- auto upgradeResult = mKeymaster->upgradeKey(keyBlob);
- if (!upgradeResult.ok()) {
- return Error() << "Failed to upgrade key: " << upgradeResult.error().message();
- }
- mVerifiedKeyBlob = *upgradeResult;
- // Make sure we persist the new blob
- auto saveResult = saveKeyblob(path);
- if (!saveResult.ok()) {
- return Error() << "Failed to store upgraded key";
- }
- } else {
- mVerifiedKeyBlob = keyBlob;
- }
-
- return {};
-}
-
-Result<std::string> KeymasterSigningKey::sign(const std::string& message) const {
- return mKeymaster->sign(mVerifiedKeyBlob, message);
-}
diff --git a/ondevice-signing/KeymasterSigningKey.h b/ondevice-signing/KeymasterSigningKey.h
deleted file mode 100644
index 7631059..0000000
--- a/ondevice-signing/KeymasterSigningKey.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#pragma once
-
-#include <android-base/macros.h>
-#include <android-base/result.h>
-#include <android-base/unique_fd.h>
-
-#include <utils/StrongPointer.h>
-
-#include "Keymaster.h"
-
-class KeymasterSigningKey {
- using KmDevice = ::android::hardware::keymaster::V4_1::IKeymasterDevice;
-
- public:
- // Allow the key to be moved around
- KeymasterSigningKey& operator=(KeymasterSigningKey&& other) = default;
- KeymasterSigningKey(KeymasterSigningKey&& other) = default;
-
- static android::base::Result<KeymasterSigningKey>
- loadFromBlobAndVerify(const std::string& path);
- static android::base::Result<KeymasterSigningKey> createNewKey();
-
- /* Sign a message with an initialized signing key */
- android::base::Result<std::string> sign(const std::string& message) const;
- android::base::Result<void> saveKeyblob(const std::string& path) const;
- android::base::Result<std::vector<uint8_t>> getPublicKey() const;
- android::base::Result<void> createX509Cert(const std::string& path) const;
-
- private:
- KeymasterSigningKey();
-
- android::base::Result<void> createSigningKey();
- android::base::Result<void> initializeFromKeyblob(const std::string& path);
-
- std::optional<Keymaster> mKeymaster;
- std::vector<uint8_t> mVerifiedKeyBlob;
-
- DISALLOW_COPY_AND_ASSIGN(KeymasterSigningKey);
-};
diff --git a/ondevice-signing/KeystoreHmacKey.cpp b/ondevice-signing/KeystoreHmacKey.cpp
new file mode 100644
index 0000000..09677d7
--- /dev/null
+++ b/ondevice-signing/KeystoreHmacKey.cpp
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <binder/IServiceManager.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "CertUtils.h"
+#include "KeyConstants.h"
+#include "KeystoreHmacKey.h"
+
+using android::sp;
+using android::String16;
+
+using android::hardware::security::keymint::Algorithm;
+using android::hardware::security::keymint::Digest;
+using android::hardware::security::keymint::KeyParameter;
+using android::hardware::security::keymint::KeyParameterValue;
+using android::hardware::security::keymint::KeyPurpose;
+using android::hardware::security::keymint::Tag;
+
+using android::system::keystore2::CreateOperationResponse;
+using android::system::keystore2::Domain;
+using android::system::keystore2::KeyDescriptor;
+using android::system::keystore2::KeyEntryResponse;
+using android::system::keystore2::KeyMetadata;
+
+using android::base::Error;
+using android::base::Result;
+
+using android::base::unique_fd;
+
+// Keystore boot level that the odsign key uses
+static const int kOdsignBootLevel = 30;
+
+static KeyDescriptor getHmacKeyDescriptor() {
+ // AIDL parcelable objects don't have constructor
+ static KeyDescriptor descriptor;
+ static std::once_flag flag;
+ std::call_once(flag, [&]() {
+ descriptor.domain = Domain::SELINUX;
+ descriptor.alias = String16("ondevice-signing-hmac");
+ descriptor.nspace = 101; // odsign_key
+ });
+
+ return descriptor;
+}
+
+Result<void> KeystoreHmacKey::createKey() {
+ std::vector<KeyParameter> params;
+
+ KeyParameter algo;
+ algo.tag = Tag::ALGORITHM;
+ algo.value = KeyParameterValue::make<KeyParameterValue::algorithm>(Algorithm::HMAC);
+ params.push_back(algo);
+
+ KeyParameter key_size;
+ key_size.tag = Tag::KEY_SIZE;
+ key_size.value = KeyParameterValue::make<KeyParameterValue::integer>(kHmacKeySize);
+ params.push_back(key_size);
+
+ KeyParameter min_mac_length;
+ min_mac_length.tag = Tag::MIN_MAC_LENGTH;
+ min_mac_length.value = KeyParameterValue::make<KeyParameterValue::integer>(256);
+ params.push_back(min_mac_length);
+
+ KeyParameter digest;
+ digest.tag = Tag::DIGEST;
+ digest.value = KeyParameterValue::make<KeyParameterValue::digest>(Digest::SHA_2_256);
+ params.push_back(digest);
+
+ KeyParameter purposeSign;
+ purposeSign.tag = Tag::PURPOSE;
+ purposeSign.value = KeyParameterValue::make<KeyParameterValue::keyPurpose>(KeyPurpose::SIGN);
+ params.push_back(purposeSign);
+
+ KeyParameter purposeVerify;
+ purposeVerify.tag = Tag::PURPOSE;
+ purposeVerify.value =
+ KeyParameterValue::make<KeyParameterValue::keyPurpose>(KeyPurpose::VERIFY);
+ params.push_back(purposeVerify);
+
+ KeyParameter auth;
+ auth.tag = Tag::NO_AUTH_REQUIRED;
+ auth.value = KeyParameterValue::make<KeyParameterValue::boolValue>(true);
+ params.push_back(auth);
+
+ KeyParameter boot_level;
+ boot_level.tag = Tag::MAX_BOOT_LEVEL;
+ boot_level.value = KeyParameterValue::make<KeyParameterValue::integer>(kOdsignBootLevel);
+ params.push_back(boot_level);
+
+ KeyMetadata metadata;
+ auto status = mSecurityLevel->generateKey(mDescriptor, {}, params, 0, {}, &metadata);
+ if (!status.isOk()) {
+ return Error() << "Failed to create new HMAC key: " << status;
+ }
+
+ return {};
+}
+
+Result<void> KeystoreHmacKey::initialize(sp<IKeystoreService> service,
+ sp<IKeystoreSecurityLevel> securityLevel) {
+ mService = std::move(service);
+ mSecurityLevel = std::move(securityLevel);
+
+ // See if we can fetch an existing key
+ KeyEntryResponse keyEntryResponse;
+ LOG(INFO) << "Trying to retrieve existing HMAC key...";
+ auto status = mService->getKeyEntry(mDescriptor, &keyEntryResponse);
+ bool keyValid = false;
+
+ if (status.isOk()) {
+ // Make sure this is an early boot key
+ for (const auto& auth : keyEntryResponse.metadata.authorizations) {
+ if (auth.keyParameter.tag == Tag::MAX_BOOT_LEVEL) {
+ if (auth.keyParameter.value.get<KeyParameterValue::integer>() == kOdsignBootLevel) {
+ keyValid = true;
+ break;
+ }
+ }
+ }
+ if (!keyValid) {
+ LOG(WARNING) << "Found invalid HMAC key without MAX_BOOT_LEVEL tag";
+ }
+ }
+
+ if (!keyValid) {
+ LOG(INFO) << "Existing HMAC key not found or invalid, creating new key";
+ return createKey();
+ } else {
+ return {};
+ }
+}
+
+KeystoreHmacKey::KeystoreHmacKey() {
+ mDescriptor = getHmacKeyDescriptor();
+}
+
+static std::vector<KeyParameter> getVerifyOpParameters() {
+ std::vector<KeyParameter> opParameters;
+
+ KeyParameter algo;
+ algo.tag = Tag::ALGORITHM;
+ algo.value = KeyParameterValue::make<KeyParameterValue::algorithm>(Algorithm::HMAC);
+ opParameters.push_back(algo);
+
+ KeyParameter digest;
+ digest.tag = Tag::DIGEST;
+ digest.value = KeyParameterValue::make<KeyParameterValue::digest>(Digest::SHA_2_256);
+ opParameters.push_back(digest);
+
+ KeyParameter purpose;
+ purpose.tag = Tag::PURPOSE;
+ purpose.value = KeyParameterValue::make<KeyParameterValue::keyPurpose>(KeyPurpose::VERIFY);
+ opParameters.push_back(purpose);
+
+ return opParameters;
+}
+
+static std::vector<KeyParameter> getSignOpParameters() {
+ std::vector<KeyParameter> opParameters;
+
+ KeyParameter algo;
+ algo.tag = Tag::ALGORITHM;
+ algo.value = KeyParameterValue::make<KeyParameterValue::algorithm>(Algorithm::HMAC);
+ opParameters.push_back(algo);
+
+ KeyParameter mac_length;
+ mac_length.tag = Tag::MAC_LENGTH;
+ mac_length.value = KeyParameterValue::make<KeyParameterValue::integer>(256);
+ opParameters.push_back(mac_length);
+
+ KeyParameter digest;
+ digest.tag = Tag::DIGEST;
+ digest.value = KeyParameterValue::make<KeyParameterValue::digest>(Digest::SHA_2_256);
+ opParameters.push_back(digest);
+
+ KeyParameter purpose;
+ purpose.tag = Tag::PURPOSE;
+ purpose.value = KeyParameterValue::make<KeyParameterValue::keyPurpose>(KeyPurpose::SIGN);
+ opParameters.push_back(purpose);
+
+ return opParameters;
+}
+
+Result<std::string> KeystoreHmacKey::sign(const std::string& message) const {
+ CreateOperationResponse opResponse;
+ static auto params = getSignOpParameters();
+
+ auto status = mSecurityLevel->createOperation(mDescriptor, params, false, &opResponse);
+ if (!status.isOk()) {
+ return Error() << "Failed to create keystore signing operation: " << status;
+ }
+ auto operation = opResponse.iOperation;
+
+ std::optional<std::vector<uint8_t>> out;
+ status = operation->update({message.begin(), message.end()}, &out);
+ if (!status.isOk()) {
+ return Error() << "Failed to call keystore update operation.";
+ }
+
+ std::optional<std::vector<uint8_t>> signature;
+ status = operation->finish({}, {}, &signature);
+ if (!status.isOk()) {
+ return Error() << "Failed to call keystore finish operation.";
+ }
+
+ if (!signature.has_value()) {
+ return Error() << "Didn't receive a signature from keystore finish operation.";
+ }
+
+ return std::string{signature.value().begin(), signature.value().end()};
+}
+
+Result<void> KeystoreHmacKey::verify(const std::string& message,
+ const std::string& signature) const {
+ CreateOperationResponse opResponse;
+ static auto params = getVerifyOpParameters();
+
+ auto status = mSecurityLevel->createOperation(mDescriptor, params, false, &opResponse);
+ if (!status.isOk()) {
+ return Error() << "Failed to create keystore verification operation: " << status;
+ }
+ auto operation = opResponse.iOperation;
+
+ std::optional<std::vector<uint8_t>> out;
+ status = operation->update({message.begin(), message.end()}, &out);
+ if (!status.isOk()) {
+ return Error() << "Failed to call keystore update operation.";
+ }
+
+ std::optional<std::vector<uint8_t>> out_signature;
+ std::vector<uint8_t> in_signature{signature.begin(), signature.end()};
+ status = operation->finish({}, in_signature, &out_signature);
+ if (!status.isOk()) {
+ return Error() << "Failed to call keystore finish operation.";
+ }
+
+ return {};
+}
+
+Result<void> KeystoreHmacKey::deleteKey() const {
+ auto status = mService->deleteKey(mDescriptor);
+ if (!status.isOk()) {
+ return Error() << "Failed to delete HMAC key: " << status;
+ }
+
+ return {};
+}
diff --git a/ondevice-signing/KeystoreHmacKey.h b/ondevice-signing/KeystoreHmacKey.h
new file mode 100644
index 0000000..782969a
--- /dev/null
+++ b/ondevice-signing/KeystoreHmacKey.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include <optional>
+
+#include <android-base/macros.h>
+#include <android-base/result.h>
+
+#include <utils/StrongPointer.h>
+
+#include <android/system/keystore2/IKeystoreService.h>
+
+class KeystoreHmacKey {
+ using IKeystoreService = ::android::system::keystore2::IKeystoreService;
+ using IKeystoreSecurityLevel = ::android::system::keystore2::IKeystoreSecurityLevel;
+ using KeyDescriptor = ::android::system::keystore2::KeyDescriptor;
+
+ public:
+ KeystoreHmacKey();
+ android::base::Result<void> initialize(android::sp<IKeystoreService> service,
+ android::sp<IKeystoreSecurityLevel> securityLevel);
+ android::base::Result<std::string> sign(const std::string& message) const;
+ android::base::Result<void> verify(const std::string& message,
+ const std::string& signature) const;
+ android::base::Result<void> deleteKey() const;
+
+ private:
+ android::base::Result<void> createKey();
+ KeyDescriptor mDescriptor;
+ android::sp<IKeystoreService> mService;
+ android::sp<IKeystoreSecurityLevel> mSecurityLevel;
+};
diff --git a/ondevice-signing/KeystoreKey.cpp b/ondevice-signing/KeystoreKey.cpp
new file mode 100644
index 0000000..03bb6d5
--- /dev/null
+++ b/ondevice-signing/KeystoreKey.cpp
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <binder/IServiceManager.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "CertUtils.h"
+#include "KeyConstants.h"
+#include "KeystoreKey.h"
+
+using android::defaultServiceManager;
+using android::IServiceManager;
+using android::sp;
+using android::String16;
+
+using android::hardware::security::keymint::Algorithm;
+using android::hardware::security::keymint::Digest;
+using android::hardware::security::keymint::KeyParameter;
+using android::hardware::security::keymint::KeyParameterValue;
+using android::hardware::security::keymint::KeyPurpose;
+using android::hardware::security::keymint::PaddingMode;
+using android::hardware::security::keymint::SecurityLevel;
+using android::hardware::security::keymint::Tag;
+
+using android::system::keystore2::CreateOperationResponse;
+using android::system::keystore2::Domain;
+using android::system::keystore2::KeyDescriptor;
+using android::system::keystore2::KeyEntryResponse;
+
+using android::base::Error;
+using android::base::Result;
+
+// Keystore boot level that the odsign key uses
+static const int kOdsignBootLevel = 30;
+
+const std::string kPublicKeySignature = "/data/misc/odsign/publickey.signature";
+
+static KeyDescriptor getKeyDescriptor() {
+ // AIDL parcelable objects don't have constructor
+ static KeyDescriptor descriptor;
+ static std::once_flag flag;
+ std::call_once(flag, [&]() {
+ descriptor.domain = Domain::SELINUX;
+ descriptor.alias = String16("ondevice-signing");
+ descriptor.nspace = 101; // odsign_key
+ });
+
+ return descriptor;
+}
+
+KeystoreKey::KeystoreKey() {
+ mDescriptor = getKeyDescriptor();
+}
+
+Result<std::vector<uint8_t>> KeystoreKey::createKey() {
+ std::vector<KeyParameter> params;
+
+ KeyParameter algo;
+ algo.tag = Tag::ALGORITHM;
+ algo.value = KeyParameterValue::make<KeyParameterValue::algorithm>(Algorithm::RSA);
+ params.push_back(algo);
+
+ KeyParameter key_size;
+ key_size.tag = Tag::KEY_SIZE;
+ key_size.value = KeyParameterValue::make<KeyParameterValue::integer>(kRsaKeySize);
+ params.push_back(key_size);
+
+ KeyParameter digest;
+ digest.tag = Tag::DIGEST;
+ digest.value = KeyParameterValue::make<KeyParameterValue::digest>(Digest::SHA_2_256);
+ params.push_back(digest);
+
+ KeyParameter padding;
+ padding.tag = Tag::PADDING;
+ padding.value =
+ KeyParameterValue::make<KeyParameterValue::paddingMode>(PaddingMode::RSA_PKCS1_1_5_SIGN);
+ params.push_back(padding);
+
+ KeyParameter exponent;
+ exponent.tag = Tag::RSA_PUBLIC_EXPONENT;
+ exponent.value = KeyParameterValue::make<KeyParameterValue::longInteger>(kRsaKeyExponent);
+ params.push_back(exponent);
+
+ KeyParameter purpose;
+ purpose.tag = Tag::PURPOSE;
+ purpose.value = KeyParameterValue::make<KeyParameterValue::keyPurpose>(KeyPurpose::SIGN);
+ params.push_back(purpose);
+
+ KeyParameter auth;
+ auth.tag = Tag::NO_AUTH_REQUIRED;
+ auth.value = KeyParameterValue::make<KeyParameterValue::boolValue>(true);
+ params.push_back(auth);
+
+ KeyParameter boot_level;
+ boot_level.tag = Tag::MAX_BOOT_LEVEL;
+ boot_level.value = KeyParameterValue::make<KeyParameterValue::integer>(kOdsignBootLevel);
+ params.push_back(boot_level);
+
+ KeyMetadata metadata;
+ auto status = mSecurityLevel->generateKey(mDescriptor, {}, params, 0, {}, &metadata);
+ if (!status.isOk()) {
+ return Error() << "Failed to create new key: " << status;
+ }
+
+ // Extract the public key from the certificate, HMAC it and store the signature
+ auto cert = metadata.certificate;
+ if (!cert) {
+ return Error() << "Key did not have a certificate.";
+ }
+ auto publicKey = extractPublicKeyFromX509(cert.value());
+ if (!publicKey.ok()) {
+ return publicKey.error();
+ }
+ std::string publicKeyString = {publicKey->begin(), publicKey->end()};
+ auto signature = mHmacKey.sign(publicKeyString);
+ if (!signature.ok()) {
+ return Error() << "Failed to sign public key.";
+ }
+
+ if (!android::base::WriteStringToFile(*signature, kPublicKeySignature)) {
+ return Error() << "Can't write public key signature.";
+ }
+
+ return *publicKey;
+}
+
+bool KeystoreKey::initialize() {
+ sp<IServiceManager> sm = defaultServiceManager();
+ if (sm == nullptr) {
+ return false;
+ }
+ auto service = sm->getService(String16("android.system.keystore2.IKeystoreService/default"));
+ if (service == nullptr) {
+ return false;
+ }
+ mService = interface_cast<android::system::keystore2::IKeystoreService>(service);
+ if (mService == nullptr) {
+ return false;
+ }
+
+ auto status = mService->getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT, &mSecurityLevel);
+ if (!status.isOk()) {
+ return false;
+ }
+
+ // Initialize the HMAC key we use to sign/verify information about this key
+ auto hmacStatus = mHmacKey.initialize(mService, mSecurityLevel);
+ if (!hmacStatus.ok()) {
+ LOG(ERROR) << hmacStatus.error().message();
+ return false;
+ }
+
+ auto key = getOrCreateKey();
+ if (!key.ok()) {
+ // Delete the HMAC, just in case signing failed, and we could recover by recreating it.
+ mHmacKey.deleteKey();
+ LOG(ERROR) << key.error().message();
+ return false;
+ }
+ mPublicKey = *key;
+ LOG(INFO) << "Initialized Keystore key.";
+ return true;
+}
+
+Result<std::vector<uint8_t>> KeystoreKey::verifyExistingKey() {
+ // See if we can fetch an existing key
+ KeyEntryResponse keyEntryResponse;
+ LOG(INFO) << "Trying to retrieve existing keystore key...";
+ auto status = mService->getKeyEntry(mDescriptor, &keyEntryResponse);
+
+ if (!status.isOk()) {
+ return Error() << "Failed to find keystore key...";
+ }
+
+ // On some earlier builds, we created this key on the Strongbox security level;
+ // we now use TEE keys instead (mostly for speed). It shouldn't matter since
+ // verified boot is protected by the TEE anyway. If the key happens to be on
+ // the wrong security level, delete it (this should happen just once).
+ if (keyEntryResponse.metadata.keySecurityLevel != SecurityLevel::TRUSTED_ENVIRONMENT) {
+ return Error() << "Found invalid keystore key with security level: "
+ << android::hardware::security::keymint::toString(
+ keyEntryResponse.metadata.keySecurityLevel);
+ }
+
+ // Make sure this is an early boot key
+ bool foundBootLevel = false;
+ for (const auto& auth : keyEntryResponse.metadata.authorizations) {
+ if (auth.keyParameter.tag == Tag::MAX_BOOT_LEVEL) {
+ if (auth.keyParameter.value.get<KeyParameterValue::integer>() == kOdsignBootLevel) {
+ foundBootLevel = true;
+ break;
+ }
+ }
+ }
+ if (!foundBootLevel) {
+ return Error() << "Found invalid keystore key without MAX_BOOT_LEVEL tag";
+ }
+
+ // If the key is still considered valid at this point, extract the public
+ // key from the certificate. Note that we cannot trust this public key,
+ // because it is a part of the keystore2 database, which can be modified by
+ // an attacker. So instead, when creating the key we HMAC the public key
+ // with a key of the same boot level, and verify the signature here.
+ auto cert = keyEntryResponse.metadata.certificate;
+ if (!cert) {
+ return Error() << "Key did not have a certificate.";
+ }
+ auto publicKey = extractPublicKeyFromX509(cert.value());
+ if (!publicKey.ok()) {
+ return publicKey.error();
+ }
+ std::string publicKeyString = {publicKey->begin(), publicKey->end()};
+
+ std::string signature;
+ if (!android::base::ReadFileToString(kPublicKeySignature, &signature)) {
+ return Error() << "Can't find signature for public key.";
+ }
+
+ auto signatureValid = mHmacKey.verify(publicKeyString, signature);
+ if (!signatureValid.ok()) {
+ return Error() << "Signature of public key did not match.";
+ }
+ LOG(INFO) << "Verified public key signature.";
+
+ return *publicKey;
+}
+
+Result<std::vector<uint8_t>> KeystoreKey::getOrCreateKey() {
+ auto existingKey = verifyExistingKey();
+ if (!existingKey.ok()) {
+ LOG(INFO) << existingKey.error().message();
+ LOG(INFO) << "Existing keystore key not found or invalid, creating new key";
+ return createKey();
+ }
+
+ return *existingKey;
+}
+
+Result<SigningKey*> KeystoreKey::getInstance() {
+ static KeystoreKey keystoreKey;
+
+ if (!keystoreKey.initialize()) {
+ return Error() << "Failed to initialize keystore key.";
+ } else {
+ return &keystoreKey;
+ }
+}
+
+static std::vector<KeyParameter> getSignOpParameters() {
+ std::vector<KeyParameter> opParameters;
+
+ KeyParameter algo;
+ algo.tag = Tag::ALGORITHM;
+ algo.value = KeyParameterValue::make<KeyParameterValue::algorithm>(Algorithm::RSA);
+ opParameters.push_back(algo);
+
+ KeyParameter digest;
+ digest.tag = Tag::DIGEST;
+ digest.value = KeyParameterValue::make<KeyParameterValue::digest>(Digest::SHA_2_256);
+ opParameters.push_back(digest);
+
+ KeyParameter padding;
+ padding.tag = Tag::PADDING;
+ padding.value =
+ KeyParameterValue::make<KeyParameterValue::paddingMode>(PaddingMode::RSA_PKCS1_1_5_SIGN);
+ opParameters.push_back(padding);
+
+ KeyParameter purpose;
+ purpose.tag = Tag::PURPOSE;
+ purpose.value = KeyParameterValue::make<KeyParameterValue::keyPurpose>(KeyPurpose::SIGN);
+ opParameters.push_back(purpose);
+
+ return opParameters;
+}
+
+Result<std::string> KeystoreKey::sign(const std::string& message) const {
+ static auto opParameters = getSignOpParameters();
+ CreateOperationResponse opResponse;
+
+ auto status = mSecurityLevel->createOperation(mDescriptor, opParameters, false, &opResponse);
+ if (!status.isOk()) {
+ return Error() << "Failed to create keystore signing operation: " << status;
+ }
+ auto operation = opResponse.iOperation;
+
+ std::optional<std::vector<uint8_t>> input{std::in_place, message.begin(), message.end()};
+ std::optional<std::vector<uint8_t>> signature;
+ status = operation->finish(input, {}, &signature);
+ if (!status.isOk()) {
+ return Error() << "Failed to call keystore finish operation.";
+ }
+
+ if (!signature.has_value()) {
+ return Error() << "Didn't receive a signature from keystore finish operation.";
+ }
+
+ return std::string{signature.value().begin(), signature.value().end()};
+}
+
+Result<std::vector<uint8_t>> KeystoreKey::getPublicKey() const {
+ return mPublicKey;
+}
diff --git a/ondevice-signing/KeystoreKey.h b/ondevice-signing/KeystoreKey.h
new file mode 100644
index 0000000..f2fbb70
--- /dev/null
+++ b/ondevice-signing/KeystoreKey.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include <optional>
+
+#include <android-base/macros.h>
+#include <android-base/result.h>
+
+#include <utils/StrongPointer.h>
+
+#include <android/system/keystore2/IKeystoreService.h>
+
+#include "KeystoreHmacKey.h"
+#include "SigningKey.h"
+
+class KeystoreKey : public SigningKey {
+ using IKeystoreService = ::android::system::keystore2::IKeystoreService;
+ using IKeystoreSecurityLevel = ::android::system::keystore2::IKeystoreSecurityLevel;
+ using KeyDescriptor = ::android::system::keystore2::KeyDescriptor;
+ using KeyMetadata = ::android::system::keystore2::KeyMetadata;
+
+ public:
+ virtual ~KeystoreKey(){};
+ static android::base::Result<SigningKey*> getInstance();
+
+ virtual android::base::Result<std::string> sign(const std::string& message) const;
+ virtual android::base::Result<std::vector<uint8_t>> getPublicKey() const;
+
+ private:
+ KeystoreKey();
+ bool initialize();
+ android::base::Result<std::vector<uint8_t>> verifyExistingKey();
+ android::base::Result<std::vector<uint8_t>> createKey();
+ android::base::Result<std::vector<uint8_t>> getOrCreateKey();
+
+ KeyDescriptor mDescriptor;
+ KeystoreHmacKey mHmacKey;
+ android::sp<IKeystoreService> mService;
+ android::sp<IKeystoreSecurityLevel> mSecurityLevel;
+ std::vector<uint8_t> mPublicKey;
+};
diff --git a/ondevice-signing/SigningKey.h b/ondevice-signing/SigningKey.h
new file mode 100644
index 0000000..89294fc
--- /dev/null
+++ b/ondevice-signing/SigningKey.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include <android-base/macros.h>
+#include <android-base/result.h>
+
+class SigningKey {
+ public:
+ virtual ~SigningKey(){};
+ /* Sign a message with an initialized signing key */
+ virtual android::base::Result<std::string> sign(const std::string& message) const = 0;
+ /* Retrieve the associated public key */
+ virtual android::base::Result<std::vector<uint8_t>> getPublicKey() const = 0;
+};
diff --git a/ondevice-signing/VerityUtils.cpp b/ondevice-signing/VerityUtils.cpp
index b4a6a54..2beb7eb 100644
--- a/ondevice-signing/VerityUtils.cpp
+++ b/ondevice-signing/VerityUtils.cpp
@@ -15,40 +15,49 @@
*/
#include <filesystem>
+#include <map>
+#include <span>
#include <string>
#include <fcntl.h>
#include <linux/fs.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/wait.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
+#include <asm/byteorder.h>
#include <libfsverity.h>
#include <linux/fsverity.h>
#include "CertUtils.h"
-#include "KeymasterSigningKey.h"
+#include "SigningKey.h"
+#include "compos_signature.pb.h"
+
+#define FS_VERITY_MAX_DIGEST_SIZE 64
using android::base::ErrnoError;
using android::base::Error;
using android::base::Result;
using android::base::unique_fd;
-#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-#define cpu_to_le16(v) ((__force __le16)(uint16_t)(v))
-#define le16_to_cpu(v) ((__force uint16_t)(__le16)(v))
-#else
-#define cpu_to_le16(v) ((__force __le16)__builtin_bswap16(v))
-#define le16_to_cpu(v) (__builtin_bswap16((__force uint16_t)(v)))
-#endif
+using compos::proto::Signature;
-struct fsverity_signed_digest {
- char magic[8]; /* must be "FSVerity" */
- __le16 digest_algorithm;
- __le16 digest_size;
- __u8 digest[];
-};
+static const char* kFsVerityInitPath = "/system/bin/fsverity_init";
+static const char* kSignatureExtension = ".signature";
+
+static bool isSignatureFile(const std::filesystem::path& path) {
+ return path.extension().native() == kSignatureExtension;
+}
+
+static std::string toHex(std::span<const uint8_t> data) {
+ std::stringstream ss;
+ for (auto it = data.begin(); it != data.end(); ++it) {
+ ss << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned>(*it);
+ }
+ return ss.str();
+}
static int read_callback(void* file, void* buf, size_t count) {
int* fd = (int*)file;
@@ -56,11 +65,12 @@
return 0;
}
-static Result<std::vector<uint8_t>> createDigest(const std::string& path) {
+Result<std::vector<uint8_t>> createDigest(int fd) {
struct stat filestat;
- unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
-
- stat(path.c_str(), &filestat);
+ int ret = fstat(fd, &filestat);
+ if (ret < 0) {
+ return ErrnoError() << "Failed to fstat";
+ }
struct libfsverity_merkle_tree_params params = {
.version = 1,
.hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
@@ -69,24 +79,58 @@
};
struct libfsverity_digest* digest;
- libfsverity_compute_digest(&fd, &read_callback, ¶ms, &digest);
-
- return std::vector<uint8_t>(&digest->digest[0], &digest->digest[32]);
+ ret = libfsverity_compute_digest(&fd, &read_callback, ¶ms, &digest);
+ if (ret < 0) {
+ return ErrnoError() << "Failed to compute fs-verity digest";
+ }
+ int expected_digest_size = libfsverity_get_digest_size(FS_VERITY_HASH_ALG_SHA256);
+ if (digest->digest_size != expected_digest_size) {
+ return Error() << "Digest does not have expected size: " << expected_digest_size
+ << " actual: " << digest->digest_size;
+ }
+ std::vector<uint8_t> digestVector(&digest->digest[0], &digest->digest[expected_digest_size]);
+ free(digest);
+ return digestVector;
}
-static Result<std::vector<uint8_t>> signDigest(const KeymasterSigningKey& key,
+Result<std::vector<uint8_t>> createDigest(const std::string& path) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (!fd.ok()) {
+ return ErrnoError() << "Unable to open";
+ }
+ return createDigest(fd.get());
+}
+
+namespace {
+template <typename T> struct DeleteAsPODArray {
+ void operator()(T* x) {
+ if (x) {
+ x->~T();
+ delete[](uint8_t*) x;
+ }
+ }
+};
+} // namespace
+
+template <typename T> using trailing_unique_ptr = std::unique_ptr<T, DeleteAsPODArray<T>>;
+
+template <typename T>
+static trailing_unique_ptr<T> makeUniqueWithTrailingData(size_t trailing_data_size) {
+ uint8_t* memory = new uint8_t[sizeof(T) + trailing_data_size];
+ T* ptr = new (memory) T;
+ return trailing_unique_ptr<T>{ptr};
+}
+
+static Result<std::vector<uint8_t>> signDigest(const SigningKey& key,
const std::vector<uint8_t>& digest) {
- fsverity_signed_digest* d;
- size_t signed_digest_size = sizeof(*d) + digest.size();
- std::unique_ptr<uint8_t[]> digest_buffer{new uint8_t[signed_digest_size]};
- d = (fsverity_signed_digest*)digest_buffer.get();
+ auto d = makeUniqueWithTrailingData<fsverity_formatted_digest>(digest.size());
memcpy(d->magic, "FSVerity", 8);
- d->digest_algorithm = cpu_to_le16(FS_VERITY_HASH_ALG_SHA256);
- d->digest_size = cpu_to_le16(digest.size());
+ d->digest_algorithm = __cpu_to_le16(FS_VERITY_HASH_ALG_SHA256);
+ d->digest_size = __cpu_to_le16(digest.size());
memcpy(d->digest, digest.data(), digest.size());
- auto signed_digest = key.sign(std::string((char*)d, signed_digest_size));
+ auto signed_digest = key.sign(std::string((char*)d.get(), sizeof(*d) + digest.size()));
if (!signed_digest.ok()) {
return signed_digest.error();
}
@@ -94,10 +138,32 @@
return std::vector<uint8_t>(signed_digest->begin(), signed_digest->end());
}
-Result<void> enableFsVerity(const std::string& path, const KeymasterSigningKey& key) {
- auto digest = createDigest(path);
+Result<void> enableFsVerity(int fd, std::span<uint8_t> pkcs7) {
+ struct fsverity_enable_arg arg = {.version = 1};
+
+ arg.sig_ptr = reinterpret_cast<uint64_t>(pkcs7.data());
+ arg.sig_size = pkcs7.size();
+ arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
+ arg.block_size = 4096;
+
+ int ret = ioctl(fd, FS_IOC_ENABLE_VERITY, &arg);
+
+ if (ret != 0) {
+ return ErrnoError() << "Failed to call FS_IOC_ENABLE_VERITY";
+ }
+
+ return {};
+}
+
+Result<std::string> enableFsVerity(const std::string& path, const SigningKey& key) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (!fd.ok()) {
+ return ErrnoError() << "Failed to open " << path;
+ }
+
+ auto digest = createDigest(fd.get());
if (!digest.ok()) {
- return digest.error();
+ return Error() << digest.error() << ": " << path;
}
auto signed_digest = signDigest(key, digest.value());
@@ -105,62 +171,73 @@
return signed_digest.error();
}
- auto pkcs7_data = createPkcs7(signed_digest.value());
-
- struct fsverity_enable_arg arg = {.version = 1};
-
- arg.sig_ptr = (uint64_t)pkcs7_data->data();
- arg.sig_size = pkcs7_data->size();
- arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
- arg.block_size = 4096;
-
- unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
- int ret = ioctl(fd, FS_IOC_ENABLE_VERITY, &arg);
-
- if (ret != 0) {
- return ErrnoError() << "Failed to call FS_IOC_ENABLE_VERITY on " << path;
+ auto pkcs7_data = createPkcs7(signed_digest.value(), kRootSubject);
+ if (!pkcs7_data.ok()) {
+ return pkcs7_data.error();
}
- return {};
+ auto enabled = enableFsVerity(fd.get(), pkcs7_data.value());
+ if (!enabled.ok()) {
+ return Error() << enabled.error() << ": " << path;
+ }
+
+ // Return the root hash as a hex string
+ return toHex(digest.value());
}
-Result<void> addFilesToVerityRecursive(const std::string& path, const KeymasterSigningKey& key) {
+Result<std::map<std::string, std::string>> addFilesToVerityRecursive(const std::string& path,
+ const SigningKey& key) {
+ std::map<std::string, std::string> digests;
+
std::error_code ec;
-
auto it = std::filesystem::recursive_directory_iterator(path, ec);
- auto end = std::filesystem::recursive_directory_iterator();
-
- while (!ec && it != end) {
+ for (auto end = std::filesystem::recursive_directory_iterator(); it != end; it.increment(ec)) {
if (it->is_regular_file()) {
LOG(INFO) << "Adding " << it->path() << " to fs-verity...";
auto result = enableFsVerity(it->path(), key);
if (!result.ok()) {
return result.error();
}
+ digests[it->path()] = *result;
}
- ++it;
+ }
+ if (ec) {
+ return Error() << "Failed to iterate " << path << ": " << ec.message();
}
- return {};
+ return digests;
}
-Result<bool> isFileInVerity(const std::string& path) {
- unsigned int flags;
+Result<std::string> isFileInVerity(int fd) {
+ auto d = makeUniqueWithTrailingData<fsverity_digest>(FS_VERITY_MAX_DIGEST_SIZE);
+ d->digest_size = FS_VERITY_MAX_DIGEST_SIZE;
+ auto ret = ioctl(fd, FS_IOC_MEASURE_VERITY, d.get());
+ if (ret < 0) {
+ if (errno == ENODATA) {
+ return Error() << "File is not in fs-verity";
+ } else {
+ return ErrnoError() << "Failed to FS_IOC_MEASURE_VERITY";
+ }
+ }
+ return toHex({&d->digest[0], &d->digest[d->digest_size]});
+}
+Result<std::string> isFileInVerity(const std::string& path) {
unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
- if (fd < 0) {
+ if (!fd.ok()) {
return ErrnoError() << "Failed to open " << path;
}
- int ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
- if (ret < 0) {
- return ErrnoError() << "Failed to FS_IOC_GETFLAGS for " << path;
+ auto digest = isFileInVerity(fd);
+ if (!digest.ok()) {
+ return Error() << digest.error() << ": " << path;
}
- return (flags & FS_VERITY_FL);
+ return digest;
}
-Result<void> verifyAllFilesInVerity(const std::string& path) {
+Result<std::map<std::string, std::string>> verifyAllFilesInVerity(const std::string& path) {
+ std::map<std::string, std::string> digests;
std::error_code ec;
auto it = std::filesystem::recursive_directory_iterator(path, ec);
@@ -168,17 +245,180 @@
while (!ec && it != end) {
if (it->is_regular_file()) {
- // Verify
+ // Verify the file is in fs-verity
auto result = isFileInVerity(it->path());
if (!result.ok()) {
return result.error();
}
- if (!*result) {
- return Error() << "File " << it->path() << " not in fs-verity";
- }
- } // TODO reject other types besides dirs?
+ digests[it->path()] = *result;
+ } else if (it->is_directory()) {
+ // These are fine to ignore
+ } else if (it->is_symlink()) {
+ return Error() << "Rejecting artifacts, symlink at " << it->path();
+ } else {
+ return Error() << "Rejecting artifacts, unexpected file type for " << it->path();
+ }
++it;
}
+ if (ec) {
+ return Error() << "Failed to iterate " << path << ": " << ec;
+ }
+
+ return digests;
+}
+
+Result<Signature> readSignature(const std::filesystem::path& signature_path) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(signature_path.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd == -1) {
+ return ErrnoError();
+ }
+ Signature signature;
+ if (!signature.ParseFromFileDescriptor(fd.get())) {
+ return Error() << "Failed to parse";
+ }
+ return signature;
+}
+
+Result<std::map<std::string, std::string>>
+verifyAllFilesUsingCompOs(const std::string& directory_path,
+ const std::vector<uint8_t>& compos_key) {
+ std::map<std::string, std::string> new_digests;
+ std::vector<std::filesystem::path> signature_files;
+
+ std::error_code ec;
+ auto it = std::filesystem::recursive_directory_iterator(directory_path, ec);
+ for (auto end = std::filesystem::recursive_directory_iterator(); it != end; it.increment(ec)) {
+ auto& path = it->path();
+ if (it->is_regular_file()) {
+ if (isSignatureFile(path)) {
+ continue;
+ }
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (!fd.ok()) {
+ return ErrnoError() << "Can't open " << path;
+ }
+
+ auto signature_path = path;
+ signature_path += kSignatureExtension;
+ auto signature = readSignature(signature_path);
+ if (!signature.ok()) {
+ return Error() << "Invalid signature " << signature_path << ": "
+ << signature.error();
+ }
+ signature_files.push_back(signature_path);
+
+ // Note that these values are not yet trusted.
+ auto& raw_digest = signature->digest();
+ auto& raw_signature = signature->signature();
+
+ // Re-construct the fsverity_formatted_digest that was signed, so we
+ // can verify the signature.
+ std::vector<uint8_t> buffer(sizeof(fsverity_formatted_digest) + raw_digest.size());
+ auto signed_data = new (buffer.data()) fsverity_formatted_digest;
+ memcpy(signed_data->magic, "FSVerity", sizeof signed_data->magic);
+ signed_data->digest_algorithm = __cpu_to_le16(FS_VERITY_HASH_ALG_SHA256);
+ signed_data->digest_size = __cpu_to_le16(raw_digest.size());
+ memcpy(signed_data->digest, raw_digest.data(), raw_digest.size());
+
+ // Make sure the signature matches the CompOs public key, and not some other
+ // fs-verity trusted key.
+ std::string to_verify(reinterpret_cast<char*>(buffer.data()), buffer.size());
+
+ auto verified = verifyRsaPublicKeySignature(to_verify, raw_signature, compos_key);
+ if (!verified.ok()) {
+ return Error() << verified.error() << ": " << path;
+ }
+
+ std::span<const uint8_t> digest_bytes(
+ reinterpret_cast<const uint8_t*>(raw_digest.data()), raw_digest.size());
+ std::string compos_digest = toHex(digest_bytes);
+
+ auto verity_digest = isFileInVerity(fd);
+ if (verity_digest.ok()) {
+ // The file is already in fs-verity. We need to make sure it was signed
+ // by CompOs, so we just check that it has the digest we expect.
+ if (verity_digest.value() != compos_digest) {
+ return Error() << "fs-verity digest does not match signature file: " << path;
+ }
+ } else {
+ // Not in fs-verity yet. But we have a valid signature of some
+ // digest. If it's not the correct digest for the file then
+ // enabling fs-verity will fail, so we don't need to check it
+ // explicitly ourselves. Otherwise we should be good.
+ std::vector<uint8_t> signature_bytes(raw_signature.begin(), raw_signature.end());
+ auto pkcs7 = createPkcs7(signature_bytes, kCompOsSubject);
+ if (!pkcs7.ok()) {
+ return Error() << pkcs7.error() << ": " << path;
+ }
+
+ LOG(INFO) << "Adding " << path << " to fs-verity...";
+ auto enabled = enableFsVerity(fd, pkcs7.value());
+ if (!enabled.ok()) {
+ return Error() << enabled.error() << ": " << path;
+ }
+ }
+
+ new_digests[path] = compos_digest;
+ } else if (it->is_directory()) {
+ // These are fine to ignore
+ } else if (it->is_symlink()) {
+ return Error() << "Rejecting artifacts, symlink at " << path;
+ } else {
+ return Error() << "Rejecting artifacts, unexpected file type for " << path;
+ }
+ }
+ if (ec) {
+ return Error() << "Failed to iterate " << directory_path << ": " << ec.message();
+ }
+
+ // Delete the signature files now that they have served their purpose. (ART
+ // has no use for them, and their presence could cause verification to fail
+ // on subsequent boots.)
+ for (auto& signature_path : signature_files) {
+ std::filesystem::remove(signature_path, ec);
+ if (ec) {
+ return Error() << "Failed to delete " << signature_path << ": " << ec.message();
+ }
+ }
+
+ return new_digests;
+}
+
+Result<void> addCertToFsVerityKeyring(const std::string& path, const char* keyName) {
+ const char* const argv[] = {kFsVerityInitPath, "--load-extra-key", keyName};
+
+ int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
+ if (fd == -1) {
+ return ErrnoError() << "Failed to open " << path;
+ }
+ pid_t pid = fork();
+ if (pid == 0) {
+ dup2(fd, STDIN_FILENO);
+ close(fd);
+ int argc = arraysize(argv);
+ char* argv_child[argc + 1];
+ memcpy(argv_child, argv, argc * sizeof(char*));
+ argv_child[argc] = nullptr;
+ execvp(argv_child[0], argv_child);
+ PLOG(ERROR) << "exec in ForkExecvp";
+ _exit(EXIT_FAILURE);
+ } else {
+ close(fd);
+ }
+ if (pid == -1) {
+ return ErrnoError() << "Failed to fork.";
+ }
+ int status;
+ if (waitpid(pid, &status, 0) == -1) {
+ return ErrnoError() << "waitpid() failed.";
+ }
+ if (!WIFEXITED(status)) {
+ return Error() << kFsVerityInitPath << ": abnormal process exit";
+ }
+ if (WEXITSTATUS(status) != 0) {
+ return Error() << kFsVerityInitPath << " exited with " << WEXITSTATUS(status);
+ }
return {};
}
diff --git a/ondevice-signing/VerityUtils.h b/ondevice-signing/VerityUtils.h
index 1eca5a6..8d8e62c 100644
--- a/ondevice-signing/VerityUtils.h
+++ b/ondevice-signing/VerityUtils.h
@@ -18,8 +18,15 @@
#include <android-base/result.h>
-#include "KeymasterSigningKey.h"
+#include "SigningKey.h"
-android::base::Result<void> verifyAllFilesInVerity(const std::string& path);
-android::base::Result<void> addFilesToVerityRecursive(const std::string& path,
- const KeymasterSigningKey& key);
+android::base::Result<void> addCertToFsVerityKeyring(const std::string& path, const char* keyName);
+android::base::Result<std::vector<uint8_t>> createDigest(const std::string& path);
+android::base::Result<std::map<std::string, std::string>>
+verifyAllFilesInVerity(const std::string& path);
+
+android::base::Result<std::map<std::string, std::string>>
+addFilesToVerityRecursive(const std::string& path, const SigningKey& key);
+
+android::base::Result<std::map<std::string, std::string>>
+verifyAllFilesUsingCompOs(const std::string& path, const std::vector<uint8_t>& compos_key);
diff --git a/ondevice-signing/odsign.rc b/ondevice-signing/odsign.rc
index 044bae7..de09fc0 100644
--- a/ondevice-signing/odsign.rc
+++ b/ondevice-signing/odsign.rc
@@ -2,5 +2,8 @@
class core
user root
group system
- oneshot
disabled # does not start with the core class
+
+# Note that odsign is not oneshot, but stopped manually when it exits. This
+# ensures that if odsign crashes during a module update, apexd will detect
+# those crashes and roll back the update.
diff --git a/ondevice-signing/odsign_main.cpp b/ondevice-signing/odsign_main.cpp
index 3baba68..4a7baad 100644
--- a/ondevice-signing/odsign_main.cpp
+++ b/ondevice-signing/odsign_main.cpp
@@ -16,89 +16,143 @@
#include <fcntl.h>
#include <filesystem>
+#include <fstream>
#include <iomanip>
#include <iostream>
+#include <iterator>
#include <sys/stat.h>
#include <sys/types.h>
-#include <sys/wait.h>
#include <unistd.h>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/scopeguard.h>
#include <logwrap/logwrap.h>
+#include <odrefresh/odrefresh.h>
#include "CertUtils.h"
-#include "KeymasterSigningKey.h"
+#include "KeystoreKey.h"
#include "VerityUtils.h"
+#include "odsign_info.pb.h"
+
using android::base::ErrnoError;
using android::base::Error;
+using android::base::GetProperty;
using android::base::Result;
+using android::base::SetProperty;
-const std::string kSigningKeyBlob = "/data/misc/odsign/key.blob";
+using OdsignInfo = ::odsign::proto::OdsignInfo;
+
const std::string kSigningKeyCert = "/data/misc/odsign/key.cert";
+const std::string kOdsignInfo = "/data/misc/odsign/odsign.info";
+const std::string kOdsignInfoSignature = "/data/misc/odsign/odsign.info.signature";
const std::string kArtArtifactsDir = "/data/misc/apexdata/com.android.art/dalvik-cache";
-static const char* kOdrefreshPath = "/apex/com.android.art/bin/odrefresh";
+constexpr const char* kOdrefreshPath = "/apex/com.android.art/bin/odrefresh";
+constexpr const char* kCompOsVerifyPath = "/apex/com.android.compos/bin/compos_verify_key";
+constexpr const char* kFsVerityProcPath = "/proc/sys/fs/verity";
+constexpr const char* kKvmDevicePath = "/dev/kvm";
-static const char* kFsVerityInitPath = "/system/bin/fsverity_init";
+constexpr bool kForceCompilation = false;
+constexpr bool kUseCompOs = true;
-static const bool kForceCompilation = false;
+const std::string kCompOsCert = "/data/misc/odsign/compos_key.cert";
-Result<void> addCertToFsVerityKeyring(const std::string& path) {
- const char* const argv[] = {kFsVerityInitPath, "--load-extra-key", "fsv_ods"};
+const std::string kCompOsCurrentPublicKey =
+ "/data/misc/apexdata/com.android.compos/current/key.pubkey";
+const std::string kCompOsPendingPublicKey =
+ "/data/misc/apexdata/com.android.compos/pending/key.pubkey";
- // NOLINTNEXTLINE(android-cloexec-open): Deliberately not O_CLOEXEC
- int fd = open(path.c_str(), O_RDONLY);
- pid_t pid = fork();
- if (pid == 0) {
- dup2(fd, STDIN_FILENO);
- close(fd);
- int argc = arraysize(argv);
- char* argv_child[argc + 1];
- memcpy(argv_child, argv, argc * sizeof(char*));
- argv_child[argc] = nullptr;
- execvp(argv_child[0], const_cast<char**>(argv_child));
- PLOG(ERROR) << "exec in ForkExecvp";
- _exit(EXIT_FAILURE);
+const std::string kCompOsPendingArtifactsDir = "/data/misc/apexdata/com.android.art/compos-pending";
+
+constexpr const char* kOdsignVerificationDoneProp = "odsign.verification.done";
+constexpr const char* kOdsignKeyDoneProp = "odsign.key.done";
+
+constexpr const char* kOdsignVerificationStatusProp = "odsign.verification.success";
+constexpr const char* kOdsignVerificationStatusValid = "1";
+constexpr const char* kOdsignVerificationStatusError = "0";
+
+constexpr const char* kStopServiceProp = "ctl.stop";
+
+enum class CompOsInstance { kCurrent, kPending };
+
+static std::vector<uint8_t> readBytesFromFile(const std::string& path) {
+ std::string str;
+ android::base::ReadFileToString(path, &str);
+ return std::vector<uint8_t>(str.begin(), str.end());
+}
+
+static bool rename(const std::string& from, const std::string& to) {
+ std::error_code ec;
+ std::filesystem::rename(from, to, ec);
+ if (ec) {
+ LOG(ERROR) << "Can't rename " << from << " to " << to << ": " << ec.message();
+ return false;
+ }
+ return true;
+}
+
+static int removeDirectory(const std::string& directory) {
+ std::error_code ec;
+ auto num_removed = std::filesystem::remove_all(directory, ec);
+ if (ec) {
+ LOG(ERROR) << "Can't remove " << directory << ": " << ec.message();
+ return 0;
} else {
- close(fd);
- }
- if (pid == -1) {
- return ErrnoError() << "Failed to fork.";
- }
- int status;
- if (waitpid(pid, &status, 0) == -1) {
- return ErrnoError() << "waitpid() failed.";
- }
- if (!WIFEXITED(status)) {
- return Error() << kFsVerityInitPath << ": abnormal process exit";
- }
- if (WEXITSTATUS(status)) {
- if (status != 0) {
- return Error() << kFsVerityInitPath << " exited with " << status;
+ if (num_removed > 0) {
+ LOG(INFO) << "Removed " << num_removed << " entries from " << directory;
}
+ return num_removed;
}
-
- return {};
}
-Result<KeymasterSigningKey> loadAndVerifyExistingKey() {
- if (access(kSigningKeyBlob.c_str(), F_OK) < 0) {
- return ErrnoError() << "Key blob not found: " << kSigningKeyBlob;
- }
- return KeymasterSigningKey::loadFromBlobAndVerify(kSigningKeyBlob);
+static bool directoryHasContent(const std::string& directory) {
+ std::error_code ec;
+ return std::filesystem::is_directory(directory, ec) &&
+ !std::filesystem::is_empty(directory, ec);
}
-Result<void> verifyExistingCert(const KeymasterSigningKey& key) {
+art::odrefresh::ExitCode compileArtifacts(bool force) {
+ const char* const argv[] = {kOdrefreshPath, force ? "--force-compile" : "--compile"};
+ const int exit_code =
+ logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr);
+ return static_cast<art::odrefresh::ExitCode>(exit_code);
+}
+
+art::odrefresh::ExitCode checkArtifacts() {
+ const char* const argv[] = {kOdrefreshPath, "--check"};
+ const int exit_code =
+ logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr);
+ return static_cast<art::odrefresh::ExitCode>(exit_code);
+}
+
+static std::string toHex(const std::vector<uint8_t>& digest) {
+ std::stringstream ss;
+ for (auto it = digest.begin(); it != digest.end(); ++it) {
+ ss << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned>(*it);
+ }
+ return ss.str();
+}
+
+bool compOsPresent() {
+ return access(kCompOsVerifyPath, X_OK) == 0 && access(kKvmDevicePath, F_OK) == 0;
+}
+
+bool isDebugBuild() {
+ std::string build_type = GetProperty("ro.build.type", "");
+ return build_type == "userdebug" || build_type == "eng";
+}
+
+Result<void> verifyExistingRootCert(const SigningKey& key) {
if (access(kSigningKeyCert.c_str(), F_OK) < 0) {
return ErrnoError() << "Key certificate not found: " << kSigningKeyCert;
}
auto trustedPublicKey = key.getPublicKey();
if (!trustedPublicKey.ok()) {
- return Error() << "Failed to retrieve signing public key.";
+ return Error() << "Failed to retrieve signing public key: " << trustedPublicKey.error();
}
auto publicKeyFromExistingCert = extractPublicKeyFromX509(kSigningKeyCert);
@@ -110,114 +164,491 @@
<< " does not match signing public key.";
}
- // At this point, we know the cert matches
+ // At this point, we know the cert is for our key; it's unimportant whether it's
+ // actually self-signed.
return {};
}
-Result<KeymasterSigningKey> createAndPersistKey(const std::string& path) {
- auto key = KeymasterSigningKey::createNewKey();
+Result<void> createX509RootCert(const SigningKey& key, const std::string& outPath) {
+ auto publicKey = key.getPublicKey();
- if (!key.ok()) {
- return key.error();
+ if (!publicKey.ok()) {
+ return publicKey.error();
}
- auto result = key->saveKeyblob(path);
+ auto keySignFunction = [&](const std::string& to_be_signed) { return key.sign(to_be_signed); };
+ return createSelfSignedCertificate(*publicKey, keySignFunction, outPath);
+}
+
+Result<std::vector<uint8_t>> extractRsaPublicKeyFromLeafCert(const SigningKey& key,
+ const std::string& certPath,
+ const std::string& expectedCn) {
+ if (access(certPath.c_str(), F_OK) < 0) {
+ return ErrnoError() << "Certificate not found: " << certPath;
+ }
+ auto trustedPublicKey = key.getPublicKey();
+ if (!trustedPublicKey.ok()) {
+ return Error() << "Failed to retrieve signing public key: " << trustedPublicKey.error();
+ }
+
+ auto existingCertInfo = verifyAndExtractCertInfoFromX509(certPath, trustedPublicKey.value());
+ if (!existingCertInfo.ok()) {
+ return Error() << "Failed to verify certificate at " << certPath << ": "
+ << existingCertInfo.error();
+ }
+
+ auto& actualCn = existingCertInfo.value().subjectCn;
+ if (actualCn != expectedCn) {
+ return Error() << "CN of existing certificate at " << certPath << " is " << actualCn
+ << ", should be " << expectedCn;
+ }
+
+ return existingCertInfo.value().subjectRsaPublicKey;
+}
+
+// Attempt to start a CompOS VM for the specified instance to get it to
+// verify ita public key & key blob.
+bool startCompOsAndVerifyKey(CompOsInstance instance) {
+ bool isCurrent = instance == CompOsInstance::kCurrent;
+ const std::string& keyPath = isCurrent ? kCompOsCurrentPublicKey : kCompOsPendingPublicKey;
+ if (access(keyPath.c_str(), R_OK) != 0) {
+ return false;
+ }
+
+ const char* const argv[] = {kCompOsVerifyPath, "--instance", isCurrent ? "current" : "pending"};
+ int result =
+ logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr);
+ if (result == 0) {
+ return true;
+ }
+
+ LOG(ERROR) << kCompOsVerifyPath << " returned " << result;
+ return false;
+}
+
+Result<std::vector<uint8_t>> verifyCompOsKey(const SigningKey& signingKey) {
+ bool verified = false;
+
+ // If a pending key has been generated we don't know if it is the correct
+ // one for the pending CompOS VM, so we need to start it and ask it.
+ if (startCompOsAndVerifyKey(CompOsInstance::kPending)) {
+ verified = true;
+ }
+
+ if (!verified) {
+ // Alternatively if we signed a cert for the key on a previous boot, then we
+ // can use that straight away.
+ auto existing_key =
+ extractRsaPublicKeyFromLeafCert(signingKey, kCompOsCert, kCompOsSubject.commonName);
+ if (existing_key.ok()) {
+ LOG(INFO) << "Found and verified existing CompOs public key certificate: "
+ << kCompOsCert;
+ return existing_key.value();
+ }
+ }
+
+ // Otherwise, if there is an existing key that we haven't signed yet, then we can sign
+ // it now if CompOS confirms it's OK.
+ if (!verified && startCompOsAndVerifyKey(CompOsInstance::kCurrent)) {
+ verified = true;
+ }
+
+ if (!verified) {
+ return Error() << "No valid CompOs key present.";
+ }
+
+ // If the pending key was verified it will have been promoted to current, so
+ // at this stage if there is a key it will be the current one.
+ auto publicKey = readBytesFromFile(kCompOsCurrentPublicKey);
+ if (publicKey.empty()) {
+ // This shouldn`t really happen.
+ return Error() << "Failed to read CompOs key.";
+ }
+
+ // One way or another we now have a valid public key. Persist a certificate so
+ // we can simplify the checks on subsequent boots.
+
+ auto signFunction = [&](const std::string& to_be_signed) {
+ return signingKey.sign(to_be_signed);
+ };
+ auto certStatus = createLeafCertificate(kCompOsSubject, publicKey, signFunction,
+ kSigningKeyCert, kCompOsCert);
+ if (!certStatus.ok()) {
+ return Error() << "Failed to create CompOs cert: " << certStatus.error();
+ }
+
+ LOG(INFO) << "Verified key, wrote new CompOs cert";
+
+ return publicKey;
+}
+
+Result<std::map<std::string, std::string>> computeDigests(const std::string& path) {
+ std::error_code ec;
+ std::map<std::string, std::string> digests;
+
+ auto it = std::filesystem::recursive_directory_iterator(path, ec);
+ auto end = std::filesystem::recursive_directory_iterator();
+
+ while (!ec && it != end) {
+ if (it->is_regular_file()) {
+ auto digest = createDigest(it->path());
+ if (!digest.ok()) {
+ return Error() << "Failed to compute digest for " << it->path() << ": "
+ << digest.error();
+ }
+ digests[it->path()] = toHex(*digest);
+ }
+ ++it;
+ }
+ if (ec) {
+ return Error() << "Failed to iterate " << path << ": " << ec;
+ }
+
+ return digests;
+}
+
+Result<void> verifyDigests(const std::map<std::string, std::string>& digests,
+ const std::map<std::string, std::string>& trusted_digests) {
+ for (const auto& path_digest : digests) {
+ auto path = path_digest.first;
+ auto digest = path_digest.second;
+ if ((trusted_digests.count(path) == 0)) {
+ return Error() << "Couldn't find digest for " << path;
+ }
+ if (trusted_digests.at(path) != digest) {
+ return Error() << "Digest mismatch for " << path;
+ }
+ }
+
+ // All digests matched!
+ if (digests.size() > 0) {
+ LOG(INFO) << "All root hashes match.";
+ }
+ return {};
+}
+
+Result<void> verifyIntegrityFsVerity(const std::map<std::string, std::string>& trusted_digests) {
+ // Just verify that the files are in verity, and get their digests
+ auto result = verifyAllFilesInVerity(kArtArtifactsDir);
if (!result.ok()) {
return result.error();
}
- return key;
+ return verifyDigests(*result, trusted_digests);
}
-bool compileArtifacts(bool force) {
- const char* const argv[] = {kOdrefreshPath, force ? "--force-compile" : "--compile"};
+Result<void> verifyIntegrityNoFsVerity(const std::map<std::string, std::string>& trusted_digests) {
+ // On these devices, just compute the digests, and verify they match the ones we trust
+ auto result = computeDigests(kArtArtifactsDir);
+ if (!result.ok()) {
+ return result.error();
+ }
- return logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr) ==
- 0;
+ return verifyDigests(*result, trusted_digests);
}
-bool validateArtifacts() {
- const char* const argv[] = {kOdrefreshPath, "--check"};
+Result<OdsignInfo> getOdsignInfo(const SigningKey& key) {
+ std::string persistedSignature;
+ OdsignInfo odsignInfo;
- return logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr) ==
- 0;
+ if (!android::base::ReadFileToString(kOdsignInfoSignature, &persistedSignature)) {
+ return ErrnoError() << "Failed to read " << kOdsignInfoSignature;
+ }
+
+ std::fstream odsign_info(kOdsignInfo, std::ios::in | std::ios::binary);
+ if (!odsign_info) {
+ return Error() << "Failed to open " << kOdsignInfo;
+ }
+ odsign_info.seekg(0);
+ // Verify the hash
+ std::string odsign_info_str((std::istreambuf_iterator<char>(odsign_info)),
+ std::istreambuf_iterator<char>());
+
+ auto publicKey = key.getPublicKey();
+ auto signResult = verifySignature(odsign_info_str, persistedSignature, *publicKey);
+ if (!signResult.ok()) {
+ return Error() << kOdsignInfoSignature << " does not match.";
+ } else {
+ LOG(INFO) << kOdsignInfoSignature << " matches.";
+ }
+
+ odsign_info.seekg(0);
+ if (!odsignInfo.ParseFromIstream(&odsign_info)) {
+ return Error() << "Failed to parse " << kOdsignInfo;
+ }
+
+ LOG(INFO) << "Loaded " << kOdsignInfo;
+ return odsignInfo;
+}
+
+Result<void> persistDigests(const std::map<std::string, std::string>& digests,
+ const SigningKey& key) {
+ OdsignInfo signInfo;
+ google::protobuf::Map<std::string, std::string> proto_hashes(digests.begin(), digests.end());
+ auto map = signInfo.mutable_file_hashes();
+ *map = proto_hashes;
+
+ std::fstream odsign_info(kOdsignInfo,
+ std::ios::in | std::ios::out | std::ios::trunc | std::ios::binary);
+ if (!signInfo.SerializeToOstream(&odsign_info)) {
+ return Error() << "Failed to persist root hashes in " << kOdsignInfo;
+ }
+
+ // Sign the signatures with our key itself, and write that to storage
+ odsign_info.seekg(0, std::ios::beg);
+ std::string odsign_info_str((std::istreambuf_iterator<char>(odsign_info)),
+ std::istreambuf_iterator<char>());
+ auto signResult = key.sign(odsign_info_str);
+ if (!signResult.ok()) {
+ return Error() << "Failed to sign " << kOdsignInfo;
+ }
+ android::base::WriteStringToFile(*signResult, kOdsignInfoSignature);
+ return {};
+}
+
+static Result<void> verifyArtifacts(const SigningKey& key, bool supportsFsVerity) {
+ auto signInfo = getOdsignInfo(key);
+ // Tell init we're done with the key; this is a boot time optimization
+ // in particular for the no fs-verity case, where we need to do a
+ // costly verification. If the files haven't been tampered with, which
+ // should be the common path, the verification will succeed, and we won't
+ // need the key anymore. If it turns out the artifacts are invalid (eg not
+ // in fs-verity) or the hash doesn't match, we won't be able to generate
+ // new artifacts without the key, so in those cases, remove the artifacts,
+ // and use JIT zygote for the current boot. We should recover automatically
+ // by the next boot.
+ SetProperty(kOdsignKeyDoneProp, "1");
+ if (!signInfo.ok()) {
+ return signInfo.error();
+ }
+ std::map<std::string, std::string> trusted_digests(signInfo->file_hashes().begin(),
+ signInfo->file_hashes().end());
+ Result<void> integrityStatus;
+
+ if (supportsFsVerity) {
+ integrityStatus = verifyIntegrityFsVerity(trusted_digests);
+ } else {
+ integrityStatus = verifyIntegrityNoFsVerity(trusted_digests);
+ }
+ if (!integrityStatus.ok()) {
+ return integrityStatus.error();
+ }
+
+ return {};
+}
+
+Result<std::vector<uint8_t>> addCompOsCertToFsVerityKeyring(const SigningKey& signingKey) {
+ auto publicKey = verifyCompOsKey(signingKey);
+ if (!publicKey.ok()) {
+ return publicKey.error();
+ }
+
+ auto cert_add_result = addCertToFsVerityKeyring(kCompOsCert, "fsv_compos");
+ if (!cert_add_result.ok()) {
+ // Best efforts only - nothing we can do if deletion fails.
+ unlink(kCompOsCert.c_str());
+ return Error() << "Failed to add CompOs certificate to fs-verity keyring: "
+ << cert_add_result.error();
+ }
+
+ return publicKey;
+}
+
+art::odrefresh::ExitCode checkCompOsPendingArtifacts(const std::vector<uint8_t>& compos_key,
+ const SigningKey& signingKey,
+ bool* digests_verified) {
+ if (!directoryHasContent(kCompOsPendingArtifactsDir)) {
+ return art::odrefresh::ExitCode::kCompilationRequired;
+ }
+
+ // CompOs has generated some artifacts that may, or may not, match the
+ // current state. But if there are already valid artifacts present the
+ // CompOs ones are redundant.
+ art::odrefresh::ExitCode odrefresh_status = checkArtifacts();
+ if (odrefresh_status != art::odrefresh::ExitCode::kCompilationRequired) {
+ if (odrefresh_status == art::odrefresh::ExitCode::kOkay) {
+ LOG(INFO) << "Current artifacts are OK, deleting pending artifacts";
+ removeDirectory(kCompOsPendingArtifactsDir);
+ }
+ return odrefresh_status;
+ }
+
+ // No useful current artifacts, lets see if the CompOs ones are ok
+ LOG(INFO) << "Current artifacts are out of date, switching to pending artifacts";
+ removeDirectory(kArtArtifactsDir);
+ if (!rename(kCompOsPendingArtifactsDir, kArtArtifactsDir)) {
+ removeDirectory(kCompOsPendingArtifactsDir);
+ return art::odrefresh::ExitCode::kCompilationRequired;
+ }
+
+ // TODO: Make sure that we check here that the contents of the artifacts
+ // correspond to their filenames (and extensions) - the CompOs signatures
+ // can't guarantee that.
+ odrefresh_status = checkArtifacts();
+ if (odrefresh_status != art::odrefresh::ExitCode::kOkay) {
+ LOG(WARNING) << "Pending artifacts are not OK";
+ return odrefresh_status;
+ }
+
+ // The artifacts appear to be up to date - but we haven't
+ // verified that they are genuine yet.
+ Result<std::map<std::string, std::string>> digests =
+ verifyAllFilesUsingCompOs(kArtArtifactsDir, compos_key);
+
+ if (digests.ok()) {
+ auto persisted = persistDigests(digests.value(), signingKey);
+
+ // Having signed the digests (or failed to), we're done with the signing key.
+ SetProperty(kOdsignKeyDoneProp, "1");
+
+ if (persisted.ok()) {
+ *digests_verified = true;
+ LOG(INFO) << "Pending artifacts successfully verified.";
+ return art::odrefresh::ExitCode::kOkay;
+ } else {
+ LOG(WARNING) << persisted.error();
+ }
+ } else {
+ LOG(WARNING) << "Pending artifact verification failed: " << digests.error();
+ }
+
+ // We can't use the existing artifacts, so we will need to generate new
+ // ones.
+ removeDirectory(kArtArtifactsDir);
+ return art::odrefresh::ExitCode::kCompilationRequired;
}
int main(int /* argc */, char** /* argv */) {
- auto removeArtifacts = []() {
- std::error_code ec;
- auto num_removed = std::filesystem::remove_all(kArtArtifactsDir, ec);
- if (ec) {
- // TODO can't remove artifacts, signal Zygote shouldn't use them
- LOG(ERROR) << "Can't remove " << kArtArtifactsDir << ": " << ec.message();
- } else {
- LOG(INFO) << "Removed " << num_removed << " entries from " << kArtArtifactsDir;
- }
+ auto errorScopeGuard = []() {
+ // In case we hit any error, remove the artifacts and tell Zygote not to use
+ // anything
+ removeDirectory(kArtArtifactsDir);
+ removeDirectory(kCompOsPendingArtifactsDir);
+ // Tell init we don't need to use our key anymore
+ SetProperty(kOdsignKeyDoneProp, "1");
+ // Tell init we're done with verification, and that it was an error
+ SetProperty(kOdsignVerificationStatusProp, kOdsignVerificationStatusError);
+ SetProperty(kOdsignVerificationDoneProp, "1");
+ // Tell init it shouldn't try to restart us - see odsign.rc
+ SetProperty(kStopServiceProp, "odsign");
};
- // Make sure we delete the artifacts in all early (error) exit paths
- auto scope_guard = android::base::make_scope_guard(removeArtifacts);
+ auto scope_guard = android::base::make_scope_guard(errorScopeGuard);
- auto key = loadAndVerifyExistingKey();
- if (!key.ok()) {
- LOG(WARNING) << key.error().message();
-
- key = createAndPersistKey(kSigningKeyBlob);
- if (!key.ok()) {
- LOG(ERROR) << "Failed to create or persist new key: " << key.error().message();
- return -1;
- }
- } else {
- LOG(INFO) << "Found and verified existing key: " << kSigningKeyBlob;
+ if (!android::base::GetBoolProperty("ro.apex.updatable", false)) {
+ LOG(INFO) << "Device doesn't support updatable APEX, exiting.";
+ return 0;
}
- auto existing_cert = verifyExistingCert(key.value());
- if (!existing_cert.ok()) {
- LOG(WARNING) << existing_cert.error().message();
+ auto keystoreResult = KeystoreKey::getInstance();
+ if (!keystoreResult.ok()) {
+ LOG(ERROR) << "Could not create keystore key: " << keystoreResult.error();
+ return -1;
+ }
+ SigningKey* key = keystoreResult.value();
- // Try to create a new cert
- auto new_cert = key->createX509Cert(kSigningKeyCert);
- if (!new_cert.ok()) {
- LOG(ERROR) << "Failed to create X509 certificate: " << new_cert.error().message();
- // TODO apparently the key become invalid - delete the blob / cert
+ bool supportsFsVerity = access(kFsVerityProcPath, F_OK) == 0;
+ if (!supportsFsVerity) {
+ LOG(INFO) << "Device doesn't support fsverity. Falling back to full verification.";
+ }
+
+ bool useCompOs = kUseCompOs && supportsFsVerity && compOsPresent() && isDebugBuild();
+
+ if (supportsFsVerity) {
+ auto existing_cert = verifyExistingRootCert(*key);
+ if (!existing_cert.ok()) {
+ LOG(WARNING) << existing_cert.error();
+
+ // Try to create a new cert
+ auto new_cert = createX509RootCert(*key, kSigningKeyCert);
+ if (!new_cert.ok()) {
+ LOG(ERROR) << "Failed to create X509 certificate: " << new_cert.error();
+ // TODO apparently the key become invalid - delete the blob / cert
+ return -1;
+ }
+ } else {
+ LOG(INFO) << "Found and verified existing public key certificate: " << kSigningKeyCert;
+ }
+ auto cert_add_result = addCertToFsVerityKeyring(kSigningKeyCert, "fsv_ods");
+ if (!cert_add_result.ok()) {
+ LOG(ERROR) << "Failed to add certificate to fs-verity keyring: "
+ << cert_add_result.error();
return -1;
}
- } else {
- LOG(INFO) << "Found and verified existing public key certificate: " << kSigningKeyCert;
}
- auto cert_add_result = addCertToFsVerityKeyring(kSigningKeyCert);
- if (!cert_add_result.ok()) {
- LOG(ERROR) << "Failed to add certificate to fs-verity keyring: "
- << cert_add_result.error().message();
+
+ art::odrefresh::ExitCode odrefresh_status = art::odrefresh::ExitCode::kCompilationRequired;
+ bool digests_verified = false;
+
+ if (useCompOs) {
+ auto compos_key = addCompOsCertToFsVerityKeyring(*key);
+ if (!compos_key.ok()) {
+ LOG(WARNING) << compos_key.error();
+ } else {
+ odrefresh_status =
+ checkCompOsPendingArtifacts(compos_key.value(), *key, &digests_verified);
+ }
+ }
+
+ if (odrefresh_status == art::odrefresh::ExitCode::kCompilationRequired) {
+ odrefresh_status = compileArtifacts(kForceCompilation);
+ }
+ if (odrefresh_status == art::odrefresh::ExitCode::kOkay) {
+ LOG(INFO) << "odrefresh said artifacts are VALID";
+ if (!digests_verified) {
+ // A post-condition of validating artifacts is that if the ones on /system
+ // are used, kArtArtifactsDir is removed. Conversely, if kArtArtifactsDir
+ // exists, those are artifacts that will be used, and we should verify them.
+ int err = access(kArtArtifactsDir.c_str(), F_OK);
+ // If we receive any error other than ENOENT, be suspicious
+ bool artifactsPresent = (err == 0) || (err < 0 && errno != ENOENT);
+ if (artifactsPresent) {
+ auto verificationResult = verifyArtifacts(*key, supportsFsVerity);
+ if (!verificationResult.ok()) {
+ LOG(ERROR) << verificationResult.error();
+ return -1;
+ }
+ }
+ }
+ } else if (odrefresh_status == art::odrefresh::ExitCode::kCompilationSuccess ||
+ odrefresh_status == art::odrefresh::ExitCode::kCompilationFailed) {
+ const bool compiled_all = odrefresh_status == art::odrefresh::ExitCode::kCompilationSuccess;
+ LOG(INFO) << "odrefresh compiled " << (compiled_all ? "all" : "partial")
+ << " artifacts, returned " << odrefresh_status;
+ Result<std::map<std::string, std::string>> digests;
+ if (supportsFsVerity) {
+ digests = addFilesToVerityRecursive(kArtArtifactsDir, *key);
+ } else {
+ // If we can't use verity, just compute the root hashes and store
+ // those, so we can reverify them at the next boot.
+ digests = computeDigests(kArtArtifactsDir);
+ }
+ if (!digests.ok()) {
+ LOG(ERROR) << digests.error();
+ return -1;
+ }
+ auto persistStatus = persistDigests(*digests, *key);
+ if (!persistStatus.ok()) {
+ LOG(ERROR) << persistStatus.error();
+ return -1;
+ }
+ } else if (odrefresh_status == art::odrefresh::ExitCode::kCleanupFailed) {
+ LOG(ERROR) << "odrefresh failed cleaning up existing artifacts";
+ return -1;
+ } else {
+ LOG(ERROR) << "odrefresh exited unexpectedly, returned " << odrefresh_status;
return -1;
}
- auto verityStatus = verifyAllFilesInVerity(kArtArtifactsDir);
- if (!verityStatus.ok()) {
- LOG(WARNING) << verityStatus.error().message() << ", removing " << kArtArtifactsDir;
- removeArtifacts();
- }
-
- bool artifactsValid = validateArtifacts();
-
- if (!artifactsValid || kForceCompilation) {
- removeArtifacts();
-
- LOG(INFO) << "Starting compilation... ";
- bool ret = compileArtifacts(kForceCompilation);
- LOG(INFO) << "Compilation done, returned " << ret;
-
- verityStatus = addFilesToVerityRecursive(kArtArtifactsDir, key.value());
-
- if (!verityStatus.ok()) {
- LOG(ERROR) << "Failed to add " << verityStatus.error().message();
- return -1;
- }
- }
-
- // TODO we want to make sure Zygote only picks up the artifacts if we deemed
- // everything was ok here. We could use a sysprop, or some other mechanism?
LOG(INFO) << "On-device signing done.";
scope_guard.Disable();
+ // At this point, we're done with the key for sure
+ SetProperty(kOdsignKeyDoneProp, "1");
+ // And we did a successful verification
+ SetProperty(kOdsignVerificationStatusProp, kOdsignVerificationStatusValid);
+ SetProperty(kOdsignVerificationDoneProp, "1");
+
+ // Tell init it shouldn't try to restart us - see odsign.rc
+ SetProperty(kStopServiceProp, "odsign");
return 0;
}
diff --git a/ondevice-signing/proto/Android.bp b/ondevice-signing/proto/Android.bp
new file mode 100644
index 0000000..c042b8e
--- /dev/null
+++ b/ondevice-signing/proto/Android.bp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library_static {
+ name: "lib_odsign_proto",
+ host_supported: true,
+ proto: {
+ export_proto_headers: true,
+ type: "lite",
+ },
+ srcs: ["odsign_info.proto"],
+}
+
+cc_library_static {
+ name: "lib_compos_proto",
+ host_supported: true,
+ proto: {
+ export_proto_headers: true,
+ type: "lite",
+ },
+ srcs: ["compos_signature.proto"],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.compos",
+ ],
+}
diff --git a/ondevice-signing/proto/compos_signature.proto b/ondevice-signing/proto/compos_signature.proto
new file mode 100644
index 0000000..2f7d09f
--- /dev/null
+++ b/ondevice-signing/proto/compos_signature.proto
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 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.
+ */
+
+syntax = "proto3";
+
+package compos.proto;
+
+// Data provided by CompOS to allow validation of a file it generated.
+message Signature {
+ // The fs-verity digest (which is derived from the root hash of
+ // the Merkle tree) of the file contents.
+ bytes digest = 1;
+
+ // Signature of a fsverity_formatted_digest structure containing
+ // the digest, signed using CompOS's private key.
+ bytes signature = 2;
+}
diff --git a/keystore/binder/android/security/keymaster/OperationResult.aidl b/ondevice-signing/proto/odsign_info.proto
similarity index 73%
rename from keystore/binder/android/security/keymaster/OperationResult.aidl
rename to ondevice-signing/proto/odsign_info.proto
index db689d4..9d49c6c 100644
--- a/keystore/binder/android/security/keymaster/OperationResult.aidl
+++ b/ondevice-signing/proto/odsign_info.proto
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 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.
@@ -14,7 +14,11 @@
* limitations under the License.
*/
-package android.security.keymaster;
+syntax = "proto3";
-/* @hide */
-parcelable OperationResult cpp_header "keystore/OperationResult.h";
+package odsign.proto;
+
+message OdsignInfo {
+ // Map of artifact files to their hashes
+ map<string, string> file_hashes = 1;
+}
diff --git a/provisioner/Android.bp b/provisioner/Android.bp
index d3f06fe..aac4878 100644
--- a/provisioner/Android.bp
+++ b/provisioner/Android.bp
@@ -43,11 +43,23 @@
},
}
-java_binary {
- name: "provisioner_cli",
- wrapper: "provisioner_cli",
- srcs: ["src/com/android/commands/provisioner/**/*.java"],
+cc_binary {
+ name: "rkp_factory_extraction_tool",
+ vendor: true,
+ srcs: ["rkp_factory_extraction_tool.cpp"],
+ shared_libs: [
+ "android.hardware.security.keymint-V1-ndk",
+ "libbinder",
+ "libbinder_ndk",
+ "libcrypto",
+ "liblog",
+ ],
static_libs: [
- "android.security.provisioner-java",
+ "libbase",
+ "libcppbor_external",
+ "libcppcose_rkp",
+ "libgflags",
+ "libjsoncpp",
+ "libkeymint_remote_prov_support",
],
}
diff --git a/provisioner/provisioner_cli b/provisioner/provisioner_cli
deleted file mode 100755
index 7b53d6e..0000000
--- a/provisioner/provisioner_cli
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/system/bin/sh
-#
-# Copyright (C) 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.
-#
-# Script to start "provisioner_cli" on the device.
-#
-base=/system
-export CLASSPATH=$base/framework/provisioner_cli.jar
-exec app_process $base/bin com.android.commands.provisioner.Cli "$@"
diff --git a/provisioner/rkp_factory_extraction_tool.cpp b/provisioner/rkp_factory_extraction_tool.cpp
new file mode 100644
index 0000000..2e59dbd
--- /dev/null
+++ b/provisioner/rkp_factory_extraction_tool.cpp
@@ -0,0 +1,190 @@
+/*
+ * 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.
+ */
+
+#include <string>
+#include <vector>
+
+#include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
+#include <android/binder_manager.h>
+#include <cppbor.h>
+#include <gflags/gflags.h>
+#include <keymaster/cppcose/cppcose.h>
+#include <openssl/base64.h>
+#include <remote_prov/remote_prov_utils.h>
+#include <sys/random.h>
+
+using aidl::android::hardware::security::keymint::DeviceInfo;
+using aidl::android::hardware::security::keymint::IRemotelyProvisionedComponent;
+using aidl::android::hardware::security::keymint::MacedPublicKey;
+using aidl::android::hardware::security::keymint::ProtectedData;
+using aidl::android::hardware::security::keymint::remote_prov::generateEekChain;
+using aidl::android::hardware::security::keymint::remote_prov::getProdEekChain;
+using aidl::android::hardware::security::keymint::remote_prov::jsonEncodeCsrWithBuild;
+
+using namespace cppbor;
+using namespace cppcose;
+
+DEFINE_bool(test_mode, false, "If enabled, a fake EEK key/cert are used.");
+
+DEFINE_string(output_format, "csr", "How to format the output. Defaults to 'csr'.");
+
+namespace {
+
+// Various supported --output_format values.
+constexpr std::string_view kBinaryCsrOutput = "csr"; // Just the raw csr as binary
+constexpr std::string_view kBuildPlusCsr = "build+csr"; // Text-encoded (JSON) build
+ // fingerprint plus CSR.
+
+constexpr size_t kChallengeSize = 16;
+
+std::string toBase64(const std::vector<uint8_t>& buffer) {
+ size_t base64Length;
+ int rc = EVP_EncodedLength(&base64Length, buffer.size());
+ if (!rc) {
+ std::cerr << "Error getting base64 length. Size overflow?" << std::endl;
+ exit(-1);
+ }
+
+ std::string base64(base64Length, ' ');
+ rc = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(base64.data()), buffer.data(), buffer.size());
+ ++rc; // Account for NUL, which BoringSSL does not for some reason.
+ if (rc != base64Length) {
+ std::cerr << "Error writing base64. Expected " << base64Length
+ << " bytes to be written, but " << rc << " bytes were actually written."
+ << std::endl;
+ exit(-1);
+ }
+ return base64;
+}
+
+std::vector<uint8_t> generateChallenge() {
+ std::vector<uint8_t> challenge(kChallengeSize);
+
+ ssize_t bytesRemaining = static_cast<ssize_t>(challenge.size());
+ uint8_t* writePtr = challenge.data();
+ while (bytesRemaining > 0) {
+ int bytesRead = getrandom(writePtr, bytesRemaining, /*flags=*/0);
+ if (bytesRead < 0 && errno != EINTR) {
+ std::cerr << errno << ": " << strerror(errno) << std::endl;
+ exit(-1);
+ }
+ bytesRemaining -= bytesRead;
+ writePtr += bytesRead;
+ }
+
+ return challenge;
+}
+
+Array composeCertificateRequest(const ProtectedData& protectedData,
+ const DeviceInfo& verifiedDeviceInfo,
+ const std::vector<uint8_t>& challenge,
+ const std::vector<uint8_t>& keysToSignMac) {
+ Array macedKeysToSign = Array()
+ .add(std::vector<uint8_t>(0)) // empty protected headers as bstr
+ .add(Map()) // empty unprotected headers
+ .add(Null()) // nil for the payload
+ .add(keysToSignMac); // MAC as returned from the HAL
+
+ Array deviceInfo =
+ Array().add(EncodedItem(verifiedDeviceInfo.deviceInfo)).add(Map()); // Empty device info
+
+ Array certificateRequest = Array()
+ .add(std::move(deviceInfo))
+ .add(challenge)
+ .add(EncodedItem(protectedData.protectedData))
+ .add(std::move(macedKeysToSign));
+ return certificateRequest;
+}
+
+std::vector<uint8_t> getEekChain() {
+ if (FLAGS_test_mode) {
+ const std::vector<uint8_t> kFakeEekId = {'f', 'a', 'k', 'e', 0};
+ auto eekOrErr = generateEekChain(3 /* chainlength */, kFakeEekId);
+ if (!eekOrErr) {
+ std::cerr << "Failed to generate test EEK somehow: " << eekOrErr.message() << std::endl;
+ exit(-1);
+ }
+ auto [eek, pubkey, privkey] = eekOrErr.moveValue();
+ std::cout << "EEK raw keypair:" << std::endl;
+ std::cout << " pub: " << toBase64(pubkey) << std::endl;
+ std::cout << " priv: " << toBase64(privkey) << std::endl;
+ return eek;
+ }
+
+ return getProdEekChain();
+}
+
+void writeOutput(const Array& csr) {
+ if (FLAGS_output_format == kBinaryCsrOutput) {
+ auto bytes = csr.encode();
+ std::copy(bytes.begin(), bytes.end(), std::ostream_iterator<char>(std::cout));
+ } else if (FLAGS_output_format == kBuildPlusCsr) {
+ auto [json, error] = jsonEncodeCsrWithBuild(csr);
+ if (!error.empty()) {
+ std::cerr << "Error JSON encoding the output: " << error;
+ exit(1);
+ }
+ std::cout << json << std::endl;
+ } else {
+ std::cerr << "Unexpected output_format '" << FLAGS_output_format << "'" << std::endl;
+ std::cerr << "Valid formats:" << std::endl;
+ std::cerr << " " << kBinaryCsrOutput << std::endl;
+ std::cerr << " " << kBuildPlusCsr << std::endl;
+ exit(1);
+ }
+}
+
+// Callback for AServiceManager_forEachDeclaredInstance that writes out a CSR
+// for every IRemotelyProvisionedComponent.
+void getCsrForInstance(const char* name, void* /*context*/) {
+ const std::vector<uint8_t> challenge = generateChallenge();
+
+ auto fullName = std::string(IRemotelyProvisionedComponent::descriptor) + "/" + name;
+ AIBinder* rkpAiBinder = AServiceManager_getService(fullName.c_str());
+ ::ndk::SpAIBinder rkp_binder(rkpAiBinder);
+ auto rkp_service = IRemotelyProvisionedComponent::fromBinder(rkp_binder);
+ if (!rkp_service) {
+ std::cerr << "Unable to get binder object for '" << fullName << "', skipping.";
+ return;
+ }
+
+ std::vector<uint8_t> keysToSignMac;
+ std::vector<MacedPublicKey> emptyKeys;
+ DeviceInfo verifiedDeviceInfo;
+ ProtectedData protectedData;
+ ::ndk::ScopedAStatus status = rkp_service->generateCertificateRequest(
+ FLAGS_test_mode, emptyKeys, getEekChain(), challenge, &verifiedDeviceInfo, &protectedData,
+ &keysToSignMac);
+ if (!status.isOk()) {
+ std::cerr << "Bundle extraction failed for '" << fullName
+ << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
+ exit(-1);
+ }
+ auto request =
+ composeCertificateRequest(protectedData, verifiedDeviceInfo, challenge, keysToSignMac);
+ writeOutput(request);
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+ gflags::ParseCommandLineFlags(&argc, &argv, /*remove_flags=*/true);
+
+ AServiceManager_forEachDeclaredInstance(IRemotelyProvisionedComponent::descriptor,
+ /*context=*/nullptr, getCsrForInstance);
+
+ return 0;
+}
diff --git a/provisioner/src/com/android/commands/provisioner/Cli.java b/provisioner/src/com/android/commands/provisioner/Cli.java
deleted file mode 100644
index 62afdac..0000000
--- a/provisioner/src/com/android/commands/provisioner/Cli.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.commands.provisioner;
-
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.security.provisioner.IProvisionerService;
-
-import com.android.internal.os.BaseCommand;
-
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.lang.IllegalArgumentException;
-
-/**
- * Contains the implementation of the remote provisioning command-line interface.
- */
-public class Cli extends BaseCommand {
- /**
- * Creates an instance of the command-line interface and runs it. This is the entry point of
- * the tool.
- */
- public static void main(String[] args) {
- new Cli().run(args);
- }
-
- /**
- * Runs the command requested by the invoker. It parses the very first required argument, which
- * is the command, and calls the appropriate handler.
- */
- @Override
- public void onRun() throws Exception {
- String cmd = nextArgRequired();
- switch (cmd) {
- case "get-req":
- getRequest();
- break;
-
- case "help":
- onShowUsage(System.out);
- break;
-
- default:
- throw new IllegalArgumentException("unknown command: " + cmd);
- }
- }
-
- /**
- * Retrieves a 'certificate request' from the provisioning service. The COSE-encoded
- * 'certificate chain' describing the endpoint encryption key (EEK) to use for encryption is
- * read from the standard input. The retrieved request is written to the standard output.
- */
- private void getRequest() throws Exception {
- // Process options.
- boolean test = false;
- byte[] challenge = null;
- int count = 0;
- String arg;
- while ((arg = nextArg()) != null) {
- switch (arg) {
- case "--test":
- test = true;
- break;
-
- case "--challenge":
- // TODO: We may need a different encoding of the challenge.
- challenge = nextArgRequired().getBytes();
- break;
-
- case "--count":
- count = Integer.parseInt(nextArgRequired());
- if (count < 0) {
- throw new IllegalArgumentException(
- "--count must be followed by non-negative number");
- }
- break;
-
- default:
- throw new IllegalArgumentException("unknown argument: " + arg);
- }
- }
-
- // Send the request over to the provisioning service and write the result to stdout.
- byte[] res = getService().getCertificateRequest(test, count, readAll(System.in), challenge);
- if (res != null) {
- System.out.write(res);
- }
- }
-
- /**
- * Retrieves an implementation of the IProvisionerService interface. It allows the caller to
- * call into the service via binder.
- */
- private static IProvisionerService getService() throws RemoteException {
- IBinder binder = ServiceManager.getService("remote-provisioner");
- if (binder == null) {
- throw new RemoteException("Provisioning service is inaccessible");
- }
- return IProvisionerService.Stub.asInterface(binder);
- }
-
- /** Reads all data from the provided input stream and returns it as a byte array. */
- private static byte[] readAll(InputStream in) throws IOException {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- byte[] buf = new byte[1024];
- int read;
- while ((read = in.read(buf)) != -1) {
- out.write(buf, 0, read);
- }
- return out.toByteArray();
- }
-
- /**
- * Writes the usage information to the given stream. This is displayed to users of the tool when
- * they ask for help or when they pass incorrect arguments to the tool.
- */
- @Override
- public void onShowUsage(PrintStream out) {
- out.println(
- "Usage: provisioner_cli <command> [options]\n" +
- "Commands: help\n" +
- " get-req [--count <n>] [--test] [--challenge <v>]");
- }
-}