Merge "Add credstore system daemon." am: 704895de56
Change-Id: Ib0998d5a9139577b2dd6f30e33d11f2ae4838e4f
This commit is contained in:
commit
b5e1b4aba1
34 changed files with 2772 additions and 0 deletions
86
identity/Android.bp
Normal file
86
identity/Android.bp
Normal file
|
@ -0,0 +1,86 @@
|
|||
cc_defaults {
|
||||
name: "identity_defaults",
|
||||
cflags: [
|
||||
"-Wall",
|
||||
// "-Werror",
|
||||
"-Wextra",
|
||||
"-Wunused",
|
||||
],
|
||||
sanitize: {
|
||||
misc_undefined : ["integer"],
|
||||
},
|
||||
clang : true,
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "credstore",
|
||||
defaults: ["identity_defaults"],
|
||||
|
||||
srcs: [
|
||||
"main.cpp",
|
||||
"CredentialStore.cpp",
|
||||
"CredentialStoreFactory.cpp",
|
||||
"WritableCredential.cpp",
|
||||
"Credential.cpp",
|
||||
"CredentialData.cpp",
|
||||
"Util.cpp",
|
||||
],
|
||||
init_rc: ["credstore.rc"],
|
||||
shared_libs: [
|
||||
"android.hardware.identity@1.0",
|
||||
"android.hardware.keymaster@4.0",
|
||||
"libbase",
|
||||
"libbinder",
|
||||
"libkeystore_aidl",
|
||||
"libcredstore_aidl",
|
||||
"libutils",
|
||||
"libhidlbase",
|
||||
"android.hardware.identity-support-lib",
|
||||
"libkeymaster4support",
|
||||
],
|
||||
static_libs: [
|
||||
"libcppbor",
|
||||
]
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "credstore_aidl",
|
||||
srcs: [
|
||||
"binder/android/security/identity/ICredential.aidl",
|
||||
"binder/android/security/identity/IWritableCredential.aidl",
|
||||
"binder/android/security/identity/ICredentialStore.aidl",
|
||||
"binder/android/security/identity/AccessControlProfileParcel.aidl",
|
||||
"binder/android/security/identity/EntryNamespaceParcel.aidl",
|
||||
"binder/android/security/identity/EntryParcel.aidl",
|
||||
"binder/android/security/identity/RequestNamespaceParcel.aidl",
|
||||
"binder/android/security/identity/RequestEntryParcel.aidl",
|
||||
"binder/android/security/identity/ResultNamespaceParcel.aidl",
|
||||
"binder/android/security/identity/ResultEntryParcel.aidl",
|
||||
"binder/android/security/identity/GetEntriesResultParcel.aidl",
|
||||
"binder/android/security/identity/AuthKeyParcel.aidl",
|
||||
"binder/android/security/identity/SecurityHardwareInfoParcel.aidl",
|
||||
"binder/android/security/identity/ICredentialStoreFactory.aidl",
|
||||
],
|
||||
path: "binder",
|
||||
}
|
||||
|
||||
cc_library_shared {
|
||||
name: "libcredstore_aidl",
|
||||
srcs: [
|
||||
":credstore_aidl",
|
||||
],
|
||||
aidl: {
|
||||
export_aidl_headers: true,
|
||||
include_dirs: [
|
||||
"system/security/identity/binder",
|
||||
],
|
||||
},
|
||||
shared_libs: [
|
||||
"libbinder",
|
||||
"libutils",
|
||||
"libkeymaster4support",
|
||||
],
|
||||
export_shared_lib_headers: [
|
||||
"libbinder",
|
||||
],
|
||||
}
|
494
identity/Credential.cpp
Normal file
494
identity/Credential.cpp
Normal file
|
@ -0,0 +1,494 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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 "Credential"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
|
||||
|
||||
#include <android/security/identity/ICredentialStore.h>
|
||||
|
||||
#include <android/security/keystore/IKeystoreService.h>
|
||||
#include <binder/IPCThreadState.h>
|
||||
#include <binder/IServiceManager.h>
|
||||
#include <keymasterV4_0/keymaster_utils.h>
|
||||
|
||||
#include <cppbor.h>
|
||||
#include <cppbor_parse.h>
|
||||
|
||||
#include "Credential.h"
|
||||
#include "CredentialData.h"
|
||||
#include "Util.h"
|
||||
|
||||
namespace android {
|
||||
namespace security {
|
||||
namespace identity {
|
||||
|
||||
using std::optional;
|
||||
|
||||
using android::security::keystore::IKeystoreService;
|
||||
|
||||
using ::android::hardware::hidl_vec;
|
||||
|
||||
using ::android::hardware::identity::V1_0::Result;
|
||||
using ::android::hardware::identity::V1_0::ResultCode;
|
||||
using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
|
||||
|
||||
using ::android::hardware::identity::support::ecKeyPairGetPkcs12;
|
||||
using ::android::hardware::identity::support::ecKeyPairGetPrivateKey;
|
||||
using ::android::hardware::identity::support::ecKeyPairGetPublicKey;
|
||||
using ::android::hardware::identity::support::sha256;
|
||||
|
||||
using android::hardware::keymaster::V4_0::HardwareAuthToken;
|
||||
|
||||
Credential::Credential(const std::string& dataPath, const std::string& credentialName)
|
||||
: dataPath_(dataPath), credentialName_(credentialName) {}
|
||||
|
||||
Credential::~Credential() {}
|
||||
|
||||
Status Credential::loadCredential(sp<IIdentityCredentialStore> halStoreBinder) {
|
||||
uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
|
||||
sp<CredentialData> data = new CredentialData(dataPath_, callingUid, credentialName_);
|
||||
if (!data->loadFromDisk()) {
|
||||
LOG(ERROR) << "Error loading data for credential";
|
||||
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
|
||||
"Error loading data for credential");
|
||||
}
|
||||
|
||||
data_ = data;
|
||||
|
||||
Result result;
|
||||
sp<IIdentityCredential> halBinder;
|
||||
halStoreBinder->getCredential(
|
||||
data_->getCredentialData(),
|
||||
[&](const Result& _result, const sp<IIdentityCredential>& _halBinder) {
|
||||
result = _result;
|
||||
halBinder = _halBinder;
|
||||
});
|
||||
if (result.code != ResultCode::OK) {
|
||||
LOG(ERROR) << "Error getting HAL binder";
|
||||
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC);
|
||||
}
|
||||
|
||||
halBinder_ = halBinder;
|
||||
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
Status Credential::getCredentialKeyCertificateChain(std::vector<uint8_t>* _aidl_return) {
|
||||
*_aidl_return = data_->getAttestationCertificate();
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
// Returns operation handle
|
||||
Status Credential::selectAuthKey(bool allowUsingExhaustedKeys, int64_t* _aidl_return) {
|
||||
|
||||
selectedAuthKey_ = data_->selectAuthKey(allowUsingExhaustedKeys);
|
||||
if (selectedAuthKey_ == nullptr) {
|
||||
return Status::fromServiceSpecificError(
|
||||
ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
|
||||
"No suitable authentication key available");
|
||||
}
|
||||
|
||||
Result result;
|
||||
uint64_t challenge;
|
||||
halBinder_->createAuthChallenge([&](const Result& _result, uint64_t _challenge) {
|
||||
result = _result;
|
||||
challenge = _challenge;
|
||||
});
|
||||
if (result.code != ResultCode::OK) {
|
||||
LOG(ERROR) << "createAuthChallenge() failed " << ((int)result.code) << ": "
|
||||
<< result.message;
|
||||
return halResultToGenericError(result);
|
||||
}
|
||||
if (challenge == 0) {
|
||||
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
|
||||
"Returned challenge is 0 (bug in HAL or TA)");
|
||||
}
|
||||
|
||||
selectedChallenge_ = challenge;
|
||||
*_aidl_return = challenge;
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
// Returns false if an error occurred communicating with keystore.
|
||||
//
|
||||
// Sets |authToken| to the empty vector if an auth token couldn't be obtained.
|
||||
//
|
||||
bool getAuthTokenFromKeystore(uint64_t challenge, uint64_t secureUserId,
|
||||
unsigned int authTokenMaxAgeMillis, vector<uint8_t>& authToken) {
|
||||
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;
|
||||
}
|
||||
|
||||
vector<uint8_t> returnedAuthToken;
|
||||
Status ret = keystore->getAuthTokenForCredstore(challenge, secureUserId, authTokenMaxAgeMillis,
|
||||
&returnedAuthToken);
|
||||
if (!ret.isOk()) {
|
||||
return false;
|
||||
}
|
||||
authToken = returnedAuthToken;
|
||||
return true;
|
||||
}
|
||||
|
||||
Status Credential::getEntries(const vector<uint8_t>& requestMessage,
|
||||
const vector<RequestNamespaceParcel>& requestNamespaces,
|
||||
const vector<uint8_t>& sessionTranscript,
|
||||
const vector<uint8_t>& readerSignature, bool allowUsingExhaustedKeys,
|
||||
GetEntriesResultParcel* _aidl_return) {
|
||||
GetEntriesResultParcel ret;
|
||||
|
||||
// Calculate requestCounts ahead of time and be careful not to include
|
||||
// elements that don't exist.
|
||||
//
|
||||
// Also go through and figure out which access control profiles to include
|
||||
// in the startRetrieval() call.
|
||||
vector<uint16_t> requestCounts;
|
||||
const vector<SecureAccessControlProfile>& allProfiles = data_->getSecureAccessControlProfiles();
|
||||
vector<bool> includeProfile(allProfiles.size());
|
||||
for (const RequestNamespaceParcel& rns : requestNamespaces) {
|
||||
size_t numEntriesInNsToRequest = 0;
|
||||
for (const RequestEntryParcel& rep : rns.entries) {
|
||||
if (data_->hasEntryData(rns.namespaceName, rep.name)) {
|
||||
numEntriesInNsToRequest++;
|
||||
}
|
||||
|
||||
optional<EntryData> data = data_->getEntryData(rns.namespaceName, rep.name);
|
||||
if (data) {
|
||||
for (uint16_t id : data.value().accessControlProfileIds) {
|
||||
if (id >= includeProfile.size()) {
|
||||
LOG(ERROR) << "Invalid accessControlProfileId " << id << " for "
|
||||
<< rns.namespaceName << ": " << rep.name;
|
||||
return Status::fromServiceSpecificError(
|
||||
ICredentialStore::ERROR_GENERIC, "Invalid accessProfileId for entry");
|
||||
}
|
||||
includeProfile[id] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
requestCounts.push_back(numEntriesInNsToRequest);
|
||||
}
|
||||
|
||||
// Now that we know which profiles are needed, send only those to the
|
||||
// HAL.
|
||||
vector<SecureAccessControlProfile> selectedProfiles;
|
||||
for (size_t n = 0; n < allProfiles.size(); n++) {
|
||||
if (includeProfile[n]) {
|
||||
selectedProfiles.push_back(allProfiles[n]);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the highest [1] non-zero timeout and if user-auth is needed
|
||||
// ... we need this to select an appropriate authToken.
|
||||
//
|
||||
// [1] : Why do we request the highest timeout and not the lowest? Well, we
|
||||
// return partial results in getEntries e.g. if some data elements
|
||||
// fail to authorize we'll still return the ones that did not fail. So
|
||||
// e.g. consider data elements A and B where A has an ACP with 60
|
||||
// seconds and B has an ACP with 3600 seconds. In this case we'll be
|
||||
// fine with getting an authToken for e.g. 2400 seconds which would
|
||||
// mean returning only B.
|
||||
//
|
||||
bool userAuthNeeded = false;
|
||||
unsigned int authTokenMaxAgeMillis = 0;
|
||||
for (auto& profile : selectedProfiles) {
|
||||
if (profile.userAuthenticationRequired) {
|
||||
userAuthNeeded = true;
|
||||
if (profile.timeoutMillis > 0) {
|
||||
if (profile.timeoutMillis > authTokenMaxAgeMillis) {
|
||||
authTokenMaxAgeMillis = profile.timeoutMillis;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Only get an authToken if it's actually needed.
|
||||
HardwareAuthToken authToken;
|
||||
if (userAuthNeeded) {
|
||||
vector<uint8_t> authTokenBytes;
|
||||
if (!getAuthTokenFromKeystore(selectedChallenge_, data_->getSecureUserId(),
|
||||
authTokenMaxAgeMillis, authTokenBytes)) {
|
||||
LOG(ERROR) << "Error getting auth token from keystore";
|
||||
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
|
||||
"Error getting auth token from keystore");
|
||||
}
|
||||
if (authTokenBytes.size() > 0) {
|
||||
authToken =
|
||||
android::hardware::keymaster::V4_0::support::hidlVec2AuthToken(authTokenBytes);
|
||||
}
|
||||
}
|
||||
|
||||
Result result;
|
||||
halBinder_->startRetrieval(selectedProfiles, authToken, requestMessage, sessionTranscript,
|
||||
readerSignature, requestCounts,
|
||||
[&](const Result& _result) { result = _result; });
|
||||
if (result.code == ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND) {
|
||||
LOG(ERROR) << "startRetrieval() failed " << ((int)result.code) << ": " << result.message;
|
||||
return Status::fromServiceSpecificError(
|
||||
ICredentialStore::ERROR_EPHEMERAL_PUBLIC_KEY_NOT_FOUND, result.message.c_str());
|
||||
} else if (result.code == ResultCode::READER_SIGNATURE_CHECK_FAILED) {
|
||||
LOG(ERROR) << "startRetrieval() failed " << ((int)result.code) << ": " << result.message;
|
||||
return Status::fromServiceSpecificError(ICredentialStore::ERROR_INVALID_READER_SIGNATURE,
|
||||
result.message.c_str());
|
||||
} else if (result.code == ResultCode::INVALID_ITEMS_REQUEST_MESSAGE) {
|
||||
LOG(ERROR) << "startRetrieval() failed " << ((int)result.code) << ": " << result.message;
|
||||
return Status::fromServiceSpecificError(
|
||||
ICredentialStore::ERROR_INVALID_ITEMS_REQUEST_MESSAGE, result.message.c_str());
|
||||
} else if (result.code == ResultCode::SESSION_TRANSCRIPT_MISMATCH) {
|
||||
LOG(ERROR) << "startRetrieval() failed " << ((int)result.code) << ": " << result.message;
|
||||
return Status::fromServiceSpecificError(ICredentialStore::ERROR_SESSION_TRANSCRIPT_MISMATCH,
|
||||
result.message.c_str());
|
||||
} else if (result.code != ResultCode::OK) {
|
||||
LOG(ERROR) << "startRetrieval() failed " << ((int)result.code) << ": " << result.message;
|
||||
return halResultToGenericError(result);
|
||||
}
|
||||
|
||||
for (const RequestNamespaceParcel& rns : requestNamespaces) {
|
||||
ResultNamespaceParcel resultNamespaceParcel;
|
||||
resultNamespaceParcel.namespaceName = rns.namespaceName;
|
||||
|
||||
for (const RequestEntryParcel& rep : rns.entries) {
|
||||
ResultEntryParcel resultEntryParcel;
|
||||
resultEntryParcel.name = rep.name;
|
||||
|
||||
optional<EntryData> data = data_->getEntryData(rns.namespaceName, rep.name);
|
||||
if (!data) {
|
||||
resultEntryParcel.status = STATUS_NO_SUCH_ENTRY;
|
||||
resultNamespaceParcel.entries.push_back(resultEntryParcel);
|
||||
continue;
|
||||
}
|
||||
|
||||
halBinder_->startRetrieveEntryValue(rns.namespaceName, rep.name, data.value().size,
|
||||
data.value().accessControlProfileIds,
|
||||
[&](const Result& _result) { result = _result; });
|
||||
if (result.code == ResultCode::USER_AUTHENTICATION_FAILED) {
|
||||
resultEntryParcel.status = STATUS_USER_AUTHENTICATION_FAILED;
|
||||
resultNamespaceParcel.entries.push_back(resultEntryParcel);
|
||||
continue;
|
||||
} else if (result.code == ResultCode::READER_AUTHENTICATION_FAILED) {
|
||||
resultEntryParcel.status = STATUS_READER_AUTHENTICATION_FAILED;
|
||||
resultNamespaceParcel.entries.push_back(resultEntryParcel);
|
||||
continue;
|
||||
} else if (result.code == ResultCode::NOT_IN_REQUEST_MESSAGE) {
|
||||
resultEntryParcel.status = STATUS_NOT_IN_REQUEST_MESSAGE;
|
||||
resultNamespaceParcel.entries.push_back(resultEntryParcel);
|
||||
continue;
|
||||
} else if (result.code == ResultCode::NO_ACCESS_CONTROL_PROFILES) {
|
||||
resultEntryParcel.status = STATUS_NO_ACCESS_CONTROL_PROFILES;
|
||||
resultNamespaceParcel.entries.push_back(resultEntryParcel);
|
||||
continue;
|
||||
} else if (result.code != ResultCode::OK) {
|
||||
LOG(ERROR) << "startRetrieveEntryValue() failed " << ((int)result.code) << ": "
|
||||
<< result.message;
|
||||
return halResultToGenericError(result);
|
||||
}
|
||||
|
||||
vector<uint8_t> value;
|
||||
for (const auto& encryptedChunk : data.value().encryptedChunks) {
|
||||
halBinder_->retrieveEntryValue(
|
||||
encryptedChunk, [&](const Result& _result, const hidl_vec<uint8_t>& chunk) {
|
||||
result = _result;
|
||||
value.insert(value.end(), chunk.begin(), chunk.end());
|
||||
});
|
||||
if (result.code != ResultCode::OK) {
|
||||
LOG(ERROR) << "retrieveEntryValue failed() " << ((int)result.code) << ": "
|
||||
<< result.message;
|
||||
return halResultToGenericError(result);
|
||||
}
|
||||
}
|
||||
|
||||
resultEntryParcel.status = STATUS_OK;
|
||||
resultEntryParcel.value = value;
|
||||
resultNamespaceParcel.entries.push_back(resultEntryParcel);
|
||||
}
|
||||
ret.resultNamespaces.push_back(resultNamespaceParcel);
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (authKey == nullptr) {
|
||||
return Status::fromServiceSpecificError(
|
||||
ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
|
||||
"No suitable authentication key available");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vector<uint8_t> signingKeyBlob;
|
||||
if (authKey != nullptr) {
|
||||
signingKeyBlob = authKey->keyBlob;
|
||||
}
|
||||
halBinder_->finishRetrieval(signingKeyBlob, [&](const Result& _result,
|
||||
const hidl_vec<uint8_t>& _mac,
|
||||
const hidl_vec<uint8_t>& _deviceNameSpaces) {
|
||||
result = _result;
|
||||
ret.mac = _mac;
|
||||
ret.deviceNameSpaces = _deviceNameSpaces;
|
||||
if (authKey != nullptr) {
|
||||
ret.staticAuthenticationData = authKey->staticAuthenticationData;
|
||||
}
|
||||
});
|
||||
if (result.code != ResultCode::OK) {
|
||||
LOG(ERROR) << "finishRetrieval failed() " << ((int)result.code) << ": " << result.message;
|
||||
return halResultToGenericError(result);
|
||||
}
|
||||
|
||||
// Ensure useCount is updated on disk.
|
||||
if (authKey != nullptr) {
|
||||
if (!data_->saveToDisk()) {
|
||||
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
|
||||
"Error saving data");
|
||||
}
|
||||
}
|
||||
|
||||
*_aidl_return = ret;
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
Status Credential::deleteCredential(vector<uint8_t>* _aidl_return) {
|
||||
Result result;
|
||||
halBinder_->deleteCredential(
|
||||
[&](const Result& _result, const hidl_vec<uint8_t>& _proofOfDeletionSignature) {
|
||||
result = _result;
|
||||
*_aidl_return = _proofOfDeletionSignature;
|
||||
});
|
||||
if (result.code != ResultCode::OK) {
|
||||
LOG(ERROR) << "deleteCredential failed() " << ((int)result.code) << ": " << result.message;
|
||||
return halResultToGenericError(result);
|
||||
}
|
||||
if (!data_->deleteCredential()) {
|
||||
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
|
||||
"Error deleting credential data on disk");
|
||||
}
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
Status Credential::createEphemeralKeyPair(vector<uint8_t>* _aidl_return) {
|
||||
Result result;
|
||||
|
||||
vector<uint8_t> keyPair;
|
||||
halBinder_->createEphemeralKeyPair(
|
||||
[&](const Result& _result, const hidl_vec<uint8_t>& _keyPair) {
|
||||
result = _result;
|
||||
keyPair = _keyPair;
|
||||
});
|
||||
if (result.code != ResultCode::OK) {
|
||||
LOG(ERROR) << "createEphemeralKeyPair failed() " << ((int)result.code) << ": "
|
||||
<< result.message;
|
||||
return halResultToGenericError(result);
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> pkcs12Bytes = ecKeyPairGetPkcs12(keyPair,
|
||||
"ephemeralKey", // Alias for key
|
||||
"0", // Serial, as a decimal number
|
||||
"Credstore", // Issuer
|
||||
"Ephemeral Key", // Subject
|
||||
0, // Validity Not Before
|
||||
24 * 60 * 60); // Validity Not After
|
||||
if (!pkcs12Bytes) {
|
||||
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
|
||||
"Error creating PKCS#12 structure for key pair");
|
||||
}
|
||||
*_aidl_return = pkcs12Bytes.value();
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
Status Credential::setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) {
|
||||
Result result;
|
||||
halBinder_->setReaderEphemeralPublicKey(publicKey,
|
||||
[&](const Result& _result) { result = _result; });
|
||||
if (result.code != ResultCode::OK) {
|
||||
LOG(ERROR) << "setReaderEphemeralPublicKey failed() " << ((int)result.code) << ": "
|
||||
<< result.message;
|
||||
return halResultToGenericError(result);
|
||||
}
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
Status Credential::setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxUsesPerKey) {
|
||||
data_->setAvailableAuthenticationKeys(keyCount, maxUsesPerKey);
|
||||
if (!data_->saveToDisk()) {
|
||||
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
|
||||
"Error saving data");
|
||||
}
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
Status Credential::getAuthKeysNeedingCertification(vector<AuthKeyParcel>* _aidl_return) {
|
||||
optional<vector<vector<uint8_t>>> keysNeedingCert =
|
||||
data_->getAuthKeysNeedingCertification(halBinder_);
|
||||
if (!keysNeedingCert) {
|
||||
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
|
||||
"Error getting auth keys neededing certification");
|
||||
}
|
||||
vector<AuthKeyParcel> authKeyParcels;
|
||||
for (const vector<uint8_t>& key : keysNeedingCert.value()) {
|
||||
AuthKeyParcel authKeyParcel;
|
||||
authKeyParcel.x509cert = key;
|
||||
authKeyParcels.push_back(authKeyParcel);
|
||||
}
|
||||
if (!data_->saveToDisk()) {
|
||||
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
|
||||
"Error saving data");
|
||||
}
|
||||
*_aidl_return = authKeyParcels;
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
Status Credential::storeStaticAuthenticationData(const AuthKeyParcel& authenticationKey,
|
||||
const vector<uint8_t>& staticAuthData) {
|
||||
if (!data_->storeStaticAuthenticationData(authenticationKey.x509cert, staticAuthData)) {
|
||||
return Status::fromServiceSpecificError(
|
||||
ICredentialStore::ERROR_AUTHENTICATION_KEY_NOT_FOUND,
|
||||
"Error finding authentication key to store static "
|
||||
"authentication data for");
|
||||
}
|
||||
if (!data_->saveToDisk()) {
|
||||
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
|
||||
"Error saving data");
|
||||
}
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
Status Credential::getAuthenticationDataUsageCount(vector<int32_t>* _aidl_return) {
|
||||
const vector<AuthKeyData>& authKeyDatas = data_->getAuthKeyDatas();
|
||||
vector<int32_t> ret;
|
||||
for (const AuthKeyData& authKeyData : authKeyDatas) {
|
||||
ret.push_back(authKeyData.useCount);
|
||||
}
|
||||
*_aidl_return = ret;
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
} // namespace identity
|
||||
} // namespace security
|
||||
} // namespace android
|
88
identity/Credential.h
Normal file
88
identity/Credential.h
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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 SYSTEM_SECURITY_CREDENTIAL_H_
|
||||
#define SYSTEM_SECURITY_CREDENTIAL_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android/security/identity/BnCredential.h>
|
||||
|
||||
#include <android/hardware/identity/1.0/IIdentityCredentialStore.h>
|
||||
#include <android/hardware/identity/1.0/types.h>
|
||||
|
||||
#include "CredentialData.h"
|
||||
|
||||
namespace android {
|
||||
namespace security {
|
||||
namespace identity {
|
||||
|
||||
using ::android::sp;
|
||||
using ::android::binder::Status;
|
||||
using ::std::string;
|
||||
using ::std::vector;
|
||||
|
||||
using ::android::hardware::identity::V1_0::IIdentityCredential;
|
||||
using ::android::hardware::identity::V1_0::IIdentityCredentialStore;
|
||||
|
||||
class Credential : public BnCredential {
|
||||
public:
|
||||
Credential(const string& dataPath, const string& credentialName);
|
||||
~Credential();
|
||||
|
||||
Status loadCredential(sp<IIdentityCredentialStore> halStoreBinder);
|
||||
|
||||
// ICredential overrides
|
||||
Status createEphemeralKeyPair(vector<uint8_t>* _aidl_return) override;
|
||||
|
||||
Status setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) override;
|
||||
|
||||
Status deleteCredential(vector<uint8_t>* _aidl_return) override;
|
||||
|
||||
Status getCredentialKeyCertificateChain(vector<uint8_t>* _aidl_return) override;
|
||||
|
||||
Status selectAuthKey(bool allowUsingExhaustedKeys, int64_t* _aidl_return) override;
|
||||
|
||||
Status getEntries(const vector<uint8_t>& requestMessage,
|
||||
const vector<RequestNamespaceParcel>& requestNamespaces,
|
||||
const vector<uint8_t>& sessionTranscript,
|
||||
const vector<uint8_t>& readerSignature, bool allowUsingExhaustedKeys,
|
||||
GetEntriesResultParcel* _aidl_return) override;
|
||||
|
||||
Status setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxUsesPerKey) override;
|
||||
Status getAuthKeysNeedingCertification(vector<AuthKeyParcel>* _aidl_return) override;
|
||||
Status storeStaticAuthenticationData(const AuthKeyParcel& authenticationKey,
|
||||
const vector<uint8_t>& staticAuthData) override;
|
||||
Status getAuthenticationDataUsageCount(vector<int32_t>* _aidl_return) override;
|
||||
|
||||
private:
|
||||
string dataPath_;
|
||||
string credentialName_;
|
||||
|
||||
const AuthKeyData* selectedAuthKey_ = nullptr;
|
||||
uint64_t selectedChallenge_ = 0;
|
||||
|
||||
sp<CredentialData> data_;
|
||||
|
||||
sp<IIdentityCredential> halBinder_;
|
||||
};
|
||||
|
||||
} // namespace identity
|
||||
} // namespace security
|
||||
} // namespace android
|
||||
|
||||
#endif // SYSTEM_SECURITY_IDENTITY_CREDENTIAL_H_
|
568
identity/CredentialData.cpp
Normal file
568
identity/CredentialData.cpp
Normal file
|
@ -0,0 +1,568 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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 "CredentialData"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include <cppbor.h>
|
||||
#include <cppbor_parse.h>
|
||||
|
||||
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
|
||||
|
||||
#include "CredentialData.h"
|
||||
#include "Util.h"
|
||||
|
||||
namespace android {
|
||||
namespace security {
|
||||
namespace identity {
|
||||
|
||||
using android::hardware::identity::V1_0::Result;
|
||||
using android::hardware::identity::V1_0::ResultCode;
|
||||
using std::optional;
|
||||
|
||||
string CredentialData::calculateCredentialFileName(const string& dataPath, uid_t ownerUid,
|
||||
const string& name) {
|
||||
return android::base::StringPrintf(
|
||||
"%s/%d-%s", dataPath.c_str(), (int)ownerUid,
|
||||
android::hardware::identity::support::encodeHex(name).c_str());
|
||||
}
|
||||
|
||||
CredentialData::CredentialData(const string& dataPath, uid_t ownerUid, const string& name)
|
||||
: dataPath_(dataPath), ownerUid_(ownerUid), name_(name), secureUserId_(0) {
|
||||
fileName_ = calculateCredentialFileName(dataPath_, ownerUid_, name_);
|
||||
}
|
||||
|
||||
void CredentialData::setSecureUserId(int64_t secureUserId) {
|
||||
secureUserId_ = secureUserId;
|
||||
}
|
||||
|
||||
void CredentialData::setCredentialData(const vector<uint8_t>& credentialData) {
|
||||
credentialData_ = credentialData;
|
||||
}
|
||||
|
||||
void CredentialData::setAttestationCertificate(const vector<uint8_t>& attestationCertificate) {
|
||||
attestationCertificate_ = attestationCertificate;
|
||||
}
|
||||
|
||||
void CredentialData::addSecureAccessControlProfile(
|
||||
const SecureAccessControlProfile& secureAccessControlProfile) {
|
||||
secureAccessControlProfiles_.push_back(secureAccessControlProfile);
|
||||
}
|
||||
|
||||
void CredentialData::addEntryData(const string& namespaceName, const string& entryName,
|
||||
const EntryData& data) {
|
||||
idToEncryptedChunks_[namespaceName + ":" + entryName] = data;
|
||||
}
|
||||
|
||||
bool CredentialData::saveToDisk() const {
|
||||
cppbor::Map map;
|
||||
|
||||
map.add("secureUserId", secureUserId_);
|
||||
|
||||
map.add("credentialData", credentialData_);
|
||||
|
||||
map.add("attestationCertificate", attestationCertificate_);
|
||||
|
||||
cppbor::Array sacpArray;
|
||||
for (const SecureAccessControlProfile& sacp : secureAccessControlProfiles_) {
|
||||
cppbor::Array array;
|
||||
array.add(sacp.id);
|
||||
array.add((const vector<uint8_t>&)sacp.readerCertificate);
|
||||
array.add(sacp.userAuthenticationRequired);
|
||||
array.add(sacp.timeoutMillis);
|
||||
array.add(sacp.secureUserId);
|
||||
vector<uint8_t> mac = sacp.mac;
|
||||
array.add(mac);
|
||||
sacpArray.add(std::move(array));
|
||||
}
|
||||
map.add("secureAccessControlProfiles", std::move(sacpArray));
|
||||
|
||||
cppbor::Map encryptedBlobsMap;
|
||||
for (auto const& [nsAndName, entryData] : idToEncryptedChunks_) {
|
||||
cppbor::Array encryptedChunkArray;
|
||||
for (const vector<uint8_t>& encryptedChunk : entryData.encryptedChunks) {
|
||||
encryptedChunkArray.add(encryptedChunk);
|
||||
}
|
||||
cppbor::Array entryDataArray;
|
||||
entryDataArray.add(entryData.size);
|
||||
cppbor::Array idsArray;
|
||||
for (uint16_t id : entryData.accessControlProfileIds) {
|
||||
idsArray.add(id);
|
||||
}
|
||||
entryDataArray.add(std::move(idsArray));
|
||||
entryDataArray.add(std::move(encryptedChunkArray));
|
||||
encryptedBlobsMap.add(nsAndName, std::move(entryDataArray));
|
||||
}
|
||||
map.add("entryData", std::move(encryptedBlobsMap));
|
||||
map.add("authKeyCount", keyCount_);
|
||||
map.add("maxUsesPerAuthKey", maxUsesPerKey_);
|
||||
|
||||
cppbor::Array authKeyDatasArray;
|
||||
for (const AuthKeyData& data : authKeyDatas_) {
|
||||
cppbor::Array array;
|
||||
array.add(data.certificate);
|
||||
array.add(data.keyBlob);
|
||||
array.add(data.staticAuthenticationData);
|
||||
array.add(data.pendingCertificate);
|
||||
array.add(data.pendingKeyBlob);
|
||||
array.add(data.useCount);
|
||||
authKeyDatasArray.add(std::move(array));
|
||||
}
|
||||
map.add("authKeyData", std::move(authKeyDatasArray));
|
||||
|
||||
vector<uint8_t> credentialData = map.encode();
|
||||
|
||||
return fileSetContents(fileName_, credentialData);
|
||||
}
|
||||
|
||||
optional<SecureAccessControlProfile> parseSacp(const cppbor::Item& item) {
|
||||
const cppbor::Array* array = item.asArray();
|
||||
if (array == nullptr || array->size() < 6) {
|
||||
LOG(ERROR) << "The SACP CBOR is not an array with at least six elements (size="
|
||||
<< (array != nullptr ? array->size() : -1) << ")";
|
||||
return {};
|
||||
}
|
||||
const cppbor::Int* itemId = ((*array)[0])->asInt();
|
||||
const cppbor::Bstr* itemReaderCertificate = ((*array)[1])->asBstr();
|
||||
const cppbor::Simple* simple = ((*array)[2])->asSimple();
|
||||
const cppbor::Bool* itemUserAuthenticationRequired =
|
||||
(simple != nullptr ? (simple->asBool()) : nullptr);
|
||||
const cppbor::Int* itemTimeoutMillis = ((*array)[3])->asInt();
|
||||
const cppbor::Int* itesecureUserId_ = ((*array)[4])->asInt();
|
||||
const cppbor::Bstr* itemMac = ((*array)[5])->asBstr();
|
||||
if (itemId == nullptr || itemReaderCertificate == nullptr ||
|
||||
itemUserAuthenticationRequired == nullptr || itemTimeoutMillis == nullptr ||
|
||||
itesecureUserId_ == nullptr || itemMac == nullptr) {
|
||||
LOG(ERROR) << "One or more items SACP array in CBOR is of wrong type";
|
||||
return {};
|
||||
}
|
||||
SecureAccessControlProfile sacp;
|
||||
sacp.id = itemId->value();
|
||||
sacp.readerCertificate = itemReaderCertificate->value();
|
||||
sacp.userAuthenticationRequired = itemUserAuthenticationRequired->value();
|
||||
sacp.timeoutMillis = itemTimeoutMillis->value();
|
||||
sacp.secureUserId = itesecureUserId_->value();
|
||||
sacp.mac = itemMac->value();
|
||||
return sacp;
|
||||
}
|
||||
|
||||
optional<AuthKeyData> parseAuthKeyData(const cppbor::Item& item) {
|
||||
const cppbor::Array* array = item.asArray();
|
||||
if (array == nullptr || array->size() < 6) {
|
||||
LOG(ERROR) << "The AuthKeyData CBOR is not an array with at least six elements";
|
||||
return {};
|
||||
}
|
||||
const cppbor::Bstr* itemCertificate = ((*array)[0])->asBstr();
|
||||
const cppbor::Bstr* itemKeyBlob = ((*array)[1])->asBstr();
|
||||
const cppbor::Bstr* itemStaticAuthenticationData = ((*array)[2])->asBstr();
|
||||
const cppbor::Bstr* itemPendingCertificate = ((*array)[3])->asBstr();
|
||||
const cppbor::Bstr* itemPendingKeyBlob = ((*array)[4])->asBstr();
|
||||
const cppbor::Int* itemUseCount = ((*array)[5])->asInt();
|
||||
if (itemCertificate == nullptr || itemKeyBlob == nullptr ||
|
||||
itemStaticAuthenticationData == nullptr || itemPendingCertificate == nullptr ||
|
||||
itemPendingKeyBlob == nullptr || itemUseCount == nullptr) {
|
||||
LOG(ERROR) << "One or more items in AuthKeyData array in CBOR is of wrong type";
|
||||
return {};
|
||||
}
|
||||
AuthKeyData authKeyData;
|
||||
authKeyData.certificate = itemCertificate->value();
|
||||
authKeyData.keyBlob = itemKeyBlob->value();
|
||||
authKeyData.staticAuthenticationData = itemStaticAuthenticationData->value();
|
||||
authKeyData.pendingCertificate = itemPendingCertificate->value();
|
||||
authKeyData.pendingKeyBlob = itemPendingKeyBlob->value();
|
||||
authKeyData.useCount = itemUseCount->value();
|
||||
return authKeyData;
|
||||
}
|
||||
|
||||
vector<uint16_t> parseAccessControlProfileIds(const cppbor::Item& item) {
|
||||
const cppbor::Array* array = item.asArray();
|
||||
if (array == nullptr) {
|
||||
LOG(ERROR) << "The accessControlProfileIds member is not an array";
|
||||
return {};
|
||||
}
|
||||
|
||||
vector<uint16_t> accessControlProfileIds;
|
||||
for (size_t n = 0; n < array->size(); n++) {
|
||||
const cppbor::Int* itemInt = ((*array)[n])->asInt();
|
||||
if (itemInt == nullptr) {
|
||||
LOG(ERROR) << "An item in the accessControlProfileIds array is not a bstr";
|
||||
return {};
|
||||
}
|
||||
accessControlProfileIds.push_back(itemInt->value());
|
||||
}
|
||||
return accessControlProfileIds;
|
||||
}
|
||||
|
||||
optional<vector<vector<uint8_t>>> parseEncryptedChunks(const cppbor::Item& item) {
|
||||
const cppbor::Array* array = item.asArray();
|
||||
if (array == nullptr) {
|
||||
LOG(ERROR) << "The encryptedChunks member is not an array";
|
||||
return {};
|
||||
}
|
||||
|
||||
vector<vector<uint8_t>> encryptedChunks;
|
||||
for (size_t n = 0; n < array->size(); n++) {
|
||||
const cppbor::Bstr* itemBstr = ((*array)[n])->asBstr();
|
||||
if (itemBstr == nullptr) {
|
||||
LOG(ERROR) << "An item in the encryptedChunks array is not a bstr";
|
||||
return {};
|
||||
}
|
||||
encryptedChunks.push_back(itemBstr->value());
|
||||
}
|
||||
return encryptedChunks;
|
||||
}
|
||||
|
||||
bool CredentialData::loadFromDisk() {
|
||||
|
||||
// Reset all data.
|
||||
credentialData_.clear();
|
||||
attestationCertificate_.clear();
|
||||
secureAccessControlProfiles_.clear();
|
||||
idToEncryptedChunks_.clear();
|
||||
authKeyDatas_.clear();
|
||||
keyCount_ = 0;
|
||||
maxUsesPerKey_ = 1;
|
||||
|
||||
optional<vector<uint8_t>> data = fileGetContents(fileName_);
|
||||
if (!data) {
|
||||
LOG(ERROR) << "Error loading data";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto [item, _ /* newPos */, message] = cppbor::parse(data.value());
|
||||
if (item == nullptr) {
|
||||
LOG(ERROR) << "Data loaded from " << fileName_ << " is not valid CBOR: " << message;
|
||||
return false;
|
||||
}
|
||||
|
||||
const cppbor::Map* map = item->asMap();
|
||||
if (map == nullptr) {
|
||||
LOG(ERROR) << "Top-level item is not a map";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t n = 0; n < map->size(); 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";
|
||||
return false;
|
||||
}
|
||||
const string& key = tstr->value();
|
||||
|
||||
if (key == "secureUserId") {
|
||||
const cppbor::Int* number = valueItem->asInt();
|
||||
if (number == nullptr) {
|
||||
LOG(ERROR) << "Value for secureUserId is not a number";
|
||||
return false;
|
||||
}
|
||||
secureUserId_ = number->value();
|
||||
} else if (key == "credentialData") {
|
||||
const cppbor::Bstr* valueBstr = valueItem->asBstr();
|
||||
if (valueBstr == nullptr) {
|
||||
LOG(ERROR) << "Value for credentialData is not a bstr";
|
||||
return false;
|
||||
}
|
||||
credentialData_ = valueBstr->value();
|
||||
} else if (key == "attestationCertificate") {
|
||||
const cppbor::Bstr* valueBstr = valueItem->asBstr();
|
||||
if (valueBstr == nullptr) {
|
||||
LOG(ERROR) << "Value for attestationCertificate is not a bstr";
|
||||
return false;
|
||||
}
|
||||
attestationCertificate_ = valueBstr->value();
|
||||
} else if (key == "secureAccessControlProfiles") {
|
||||
const cppbor::Array* array = valueItem->asArray();
|
||||
if (array == nullptr) {
|
||||
LOG(ERROR) << "Value for attestationCertificate is not an array";
|
||||
return false;
|
||||
}
|
||||
for (size_t m = 0; m < array->size(); m++) {
|
||||
const std::unique_ptr<cppbor::Item>& item = (*array)[m];
|
||||
optional<SecureAccessControlProfile> sacp = parseSacp(*item);
|
||||
if (!sacp) {
|
||||
LOG(ERROR) << "Error parsing SecureAccessControlProfile";
|
||||
return false;
|
||||
}
|
||||
secureAccessControlProfiles_.push_back(sacp.value());
|
||||
}
|
||||
|
||||
} else if (key == "entryData") {
|
||||
const cppbor::Map* map = valueItem->asMap();
|
||||
if (map == nullptr) {
|
||||
LOG(ERROR) << "Value for encryptedChunks is not an map";
|
||||
return false;
|
||||
}
|
||||
for (size_t m = 0; m < map->size(); 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";
|
||||
return false;
|
||||
}
|
||||
const string& ecId = ecTstr->value();
|
||||
|
||||
const cppbor::Array* ecEntryArrayItem = ecValueItem->asArray();
|
||||
if (ecEntryArrayItem == nullptr || ecEntryArrayItem->size() < 3) {
|
||||
LOG(ERROR) << "Value item in encryptedChunks map is an array with at least two "
|
||||
"elements";
|
||||
return false;
|
||||
}
|
||||
const cppbor::Int* ecEntrySizeItem = (*ecEntryArrayItem)[0]->asInt();
|
||||
if (ecEntrySizeItem == nullptr) {
|
||||
LOG(ERROR) << "Entry size not a number";
|
||||
return false;
|
||||
}
|
||||
uint64_t entrySize = ecEntrySizeItem->value();
|
||||
|
||||
optional<vector<uint16_t>> accessControlProfileIds =
|
||||
parseAccessControlProfileIds(*(*ecEntryArrayItem)[1]);
|
||||
if (!accessControlProfileIds) {
|
||||
LOG(ERROR) << "Error parsing access control profile ids";
|
||||
return false;
|
||||
}
|
||||
|
||||
optional<vector<vector<uint8_t>>> encryptedChunks =
|
||||
parseEncryptedChunks(*(*ecEntryArrayItem)[2]);
|
||||
if (!encryptedChunks) {
|
||||
LOG(ERROR) << "Error parsing encrypted chunks";
|
||||
return false;
|
||||
}
|
||||
|
||||
EntryData data;
|
||||
data.size = entrySize;
|
||||
data.accessControlProfileIds = accessControlProfileIds.value();
|
||||
data.encryptedChunks = encryptedChunks.value();
|
||||
idToEncryptedChunks_[ecId] = data;
|
||||
}
|
||||
|
||||
} else if (key == "authKeyData") {
|
||||
const cppbor::Array* array = valueItem->asArray();
|
||||
if (array == nullptr) {
|
||||
LOG(ERROR) << "Value for authData is not an array";
|
||||
return false;
|
||||
}
|
||||
for (size_t m = 0; m < array->size(); m++) {
|
||||
const std::unique_ptr<cppbor::Item>& item = (*array)[m];
|
||||
optional<AuthKeyData> authKeyData = parseAuthKeyData(*item);
|
||||
if (!authKeyData) {
|
||||
LOG(ERROR) << "Error parsing AuthKeyData";
|
||||
return false;
|
||||
}
|
||||
authKeyDatas_.push_back(authKeyData.value());
|
||||
}
|
||||
|
||||
} else if (key == "authKeyCount") {
|
||||
const cppbor::Int* number = valueItem->asInt();
|
||||
if (number == nullptr) {
|
||||
LOG(ERROR) << "Value for authKeyCount is not a number";
|
||||
return false;
|
||||
}
|
||||
keyCount_ = number->value();
|
||||
|
||||
} else if (key == "maxUsesPerAuthKey") {
|
||||
const cppbor::Int* number = valueItem->asInt();
|
||||
if (number == nullptr) {
|
||||
LOG(ERROR) << "Value for maxUsesPerAuthKey is not a number";
|
||||
return false;
|
||||
}
|
||||
maxUsesPerKey_ = number->value();
|
||||
}
|
||||
}
|
||||
|
||||
if (credentialData_.size() == 0 || attestationCertificate_.size() == 0) {
|
||||
LOG(ERROR) << "Missing credentialData or attestationCertificate";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (size_t(keyCount_) != authKeyDatas_.size()) {
|
||||
LOG(ERROR) << "keyCount_=" << keyCount_
|
||||
<< " != authKeyDatas_.size()=" << authKeyDatas_.size();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const vector<uint8_t>& CredentialData::getCredentialData() const {
|
||||
return credentialData_;
|
||||
}
|
||||
|
||||
int64_t CredentialData::getSecureUserId() {
|
||||
return secureUserId_;
|
||||
}
|
||||
|
||||
const vector<uint8_t>& CredentialData::getAttestationCertificate() const {
|
||||
return attestationCertificate_;
|
||||
}
|
||||
|
||||
const vector<SecureAccessControlProfile>& CredentialData::getSecureAccessControlProfiles() const {
|
||||
return secureAccessControlProfiles_;
|
||||
}
|
||||
|
||||
bool CredentialData::hasEntryData(const string& namespaceName, const string& entryName) const {
|
||||
string id = namespaceName + ":" + entryName;
|
||||
auto iter = idToEncryptedChunks_.find(id);
|
||||
if (iter == idToEncryptedChunks_.end()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
optional<EntryData> CredentialData::getEntryData(const string& namespaceName,
|
||||
const string& entryName) const {
|
||||
string id = namespaceName + ":" + entryName;
|
||||
auto iter = idToEncryptedChunks_.find(id);
|
||||
if (iter == idToEncryptedChunks_.end()) {
|
||||
return {};
|
||||
}
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
bool CredentialData::deleteCredential() {
|
||||
if (unlink(fileName_.c_str()) != 0) {
|
||||
PLOG(ERROR) << "Error deleting " << fileName_;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
optional<bool> CredentialData::credentialExists(const string& dataPath, uid_t ownerUid,
|
||||
const string& name) {
|
||||
struct stat statbuf;
|
||||
string filename = calculateCredentialFileName(dataPath, ownerUid, name);
|
||||
if (stat(filename.c_str(), &statbuf) != 0) {
|
||||
if (errno == ENOENT) {
|
||||
return false;
|
||||
}
|
||||
PLOG(ERROR) << "Error getting information about " << filename;
|
||||
return {};
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
void CredentialData::setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey) {
|
||||
keyCount_ = keyCount;
|
||||
maxUsesPerKey_ = maxUsesPerKey;
|
||||
|
||||
// If growing the number of auth keys (prevKeyCount < keyCount_ case) we'll add
|
||||
// new AuthKeyData structs to |authKeyDatas_| and each struct will have empty |certificate|
|
||||
// and |pendingCertificate| fields. Those will be filled out when the
|
||||
// getAuthKeysNeedingCertification() is called.
|
||||
//
|
||||
// If shrinking, we'll just delete the AuthKeyData structs at the end. There's nothing
|
||||
// else to do, the HAL doesn't need to know we're nuking these authentication keys.
|
||||
//
|
||||
// Therefore, in either case it's as simple as just resizing the vector.
|
||||
authKeyDatas_.resize(keyCount_);
|
||||
}
|
||||
|
||||
const vector<AuthKeyData>& CredentialData::getAuthKeyDatas() const {
|
||||
return authKeyDatas_;
|
||||
}
|
||||
|
||||
const AuthKeyData* CredentialData::selectAuthKey(bool allowUsingExhaustedKeys) {
|
||||
AuthKeyData* candidate = nullptr;
|
||||
|
||||
int n = 0;
|
||||
int candidateNum = -1;
|
||||
for (AuthKeyData& data : authKeyDatas_) {
|
||||
if (data.certificate.size() != 0) {
|
||||
if (candidate == nullptr || data.useCount < candidate->useCount) {
|
||||
candidate = &data;
|
||||
candidateNum = n;
|
||||
}
|
||||
}
|
||||
n++;
|
||||
}
|
||||
|
||||
if (candidate == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (candidate->useCount >= maxUsesPerKey_ && !allowUsingExhaustedKeys) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
candidate->useCount += 1;
|
||||
return candidate;
|
||||
}
|
||||
|
||||
optional<vector<vector<uint8_t>>> CredentialData::getAuthKeysNeedingCertification(
|
||||
const sp<android::hardware::identity::V1_0::IIdentityCredential>& halBinder) {
|
||||
|
||||
vector<vector<uint8_t>> keysNeedingCert;
|
||||
|
||||
for (AuthKeyData& data : authKeyDatas_) {
|
||||
bool newKeyNeeded = (data.certificate.size() == 0) || (data.useCount >= maxUsesPerKey_);
|
||||
bool certificationPending = (data.pendingCertificate.size() > 0);
|
||||
if (newKeyNeeded && !certificationPending) {
|
||||
Result result;
|
||||
vector<uint8_t> signingKeyBlob;
|
||||
vector<uint8_t> signingKeyCertificate;
|
||||
halBinder->generateSigningKeyPair(
|
||||
[&](const Result& _result,
|
||||
const android::hardware::hidl_vec<uint8_t> _signingKeyBlob,
|
||||
const android::hardware::hidl_vec<uint8_t> _signingKeyCertificate) {
|
||||
result = _result;
|
||||
signingKeyBlob = _signingKeyBlob;
|
||||
signingKeyCertificate = _signingKeyCertificate;
|
||||
});
|
||||
if (result.code != ResultCode::OK) {
|
||||
LOG(ERROR) << "Error generating signing key-pair";
|
||||
return {};
|
||||
}
|
||||
data.pendingCertificate = signingKeyCertificate;
|
||||
data.pendingKeyBlob = signingKeyBlob;
|
||||
certificationPending = true;
|
||||
}
|
||||
|
||||
if (certificationPending) {
|
||||
keysNeedingCert.push_back(data.pendingCertificate);
|
||||
}
|
||||
}
|
||||
return keysNeedingCert;
|
||||
}
|
||||
|
||||
bool CredentialData::storeStaticAuthenticationData(const vector<uint8_t>& authenticationKey,
|
||||
const vector<uint8_t>& staticAuthData) {
|
||||
for (AuthKeyData& data : authKeyDatas_) {
|
||||
if (data.pendingCertificate == authenticationKey) {
|
||||
data.certificate = data.pendingCertificate;
|
||||
data.keyBlob = data.pendingKeyBlob;
|
||||
data.staticAuthenticationData = staticAuthData;
|
||||
data.pendingCertificate.clear();
|
||||
data.pendingKeyBlob.clear();
|
||||
data.useCount = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace identity
|
||||
} // namespace security
|
||||
} // namespace android
|
144
identity/CredentialData.h
Normal file
144
identity/CredentialData.h
Normal file
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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 SYSTEM_SECURITY_CREDENTIAL_DATA_H_
|
||||
#define SYSTEM_SECURITY_CREDENTIAL_DATA_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <android/hardware/identity/1.0/IIdentityCredential.h>
|
||||
#include <android/hardware/identity/1.0/types.h>
|
||||
|
||||
namespace android {
|
||||
namespace security {
|
||||
namespace identity {
|
||||
|
||||
using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
|
||||
using ::std::map;
|
||||
using ::std::optional;
|
||||
using ::std::pair;
|
||||
using ::std::string;
|
||||
using ::std::tuple;
|
||||
using ::std::vector;
|
||||
|
||||
struct EntryData {
|
||||
EntryData() {}
|
||||
|
||||
uint64_t size = 0;
|
||||
vector<uint16_t> accessControlProfileIds;
|
||||
vector<vector<uint8_t>> encryptedChunks;
|
||||
};
|
||||
|
||||
struct AuthKeyData {
|
||||
AuthKeyData() {}
|
||||
|
||||
vector<uint8_t> certificate;
|
||||
vector<uint8_t> keyBlob;
|
||||
vector<uint8_t> staticAuthenticationData;
|
||||
vector<uint8_t> pendingCertificate;
|
||||
vector<uint8_t> pendingKeyBlob;
|
||||
int useCount = 0;
|
||||
};
|
||||
|
||||
class CredentialData : public RefBase {
|
||||
public:
|
||||
CredentialData(const string& dataPath, uid_t ownerUid, const string& name);
|
||||
|
||||
static string calculateCredentialFileName(const string& dataPath, uid_t ownerUid,
|
||||
const string& name);
|
||||
|
||||
static optional<bool> credentialExists(const string& dataPath, uid_t ownerUid,
|
||||
const string& name);
|
||||
|
||||
void setSecureUserId(int64_t secureUserId);
|
||||
|
||||
void setCredentialData(const vector<uint8_t>& credentialData);
|
||||
|
||||
void setAttestationCertificate(const vector<uint8_t>& attestationCertificate);
|
||||
|
||||
void
|
||||
addSecureAccessControlProfile(const SecureAccessControlProfile& secureAccessControlProfile);
|
||||
|
||||
void addEntryData(const string& namespaceName, const string& entryName, const EntryData& data);
|
||||
|
||||
bool saveToDisk() const;
|
||||
|
||||
bool loadFromDisk();
|
||||
|
||||
bool deleteCredential();
|
||||
|
||||
void setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey);
|
||||
|
||||
// Getters
|
||||
|
||||
int64_t getSecureUserId();
|
||||
|
||||
const vector<uint8_t>& getCredentialData() const;
|
||||
|
||||
const vector<uint8_t>& getAttestationCertificate() const;
|
||||
|
||||
const vector<SecureAccessControlProfile>& getSecureAccessControlProfiles() const;
|
||||
|
||||
bool hasEntryData(const string& namespaceName, const string& entryName) const;
|
||||
|
||||
optional<EntryData> getEntryData(const string& namespaceName, const string& entryName) const;
|
||||
|
||||
const vector<AuthKeyData>& getAuthKeyDatas() const;
|
||||
|
||||
// Returns |nullptr| if a suitable key cannot be found. Otherwise returns
|
||||
// the authentication and increases its use-count.
|
||||
const AuthKeyData* selectAuthKey(bool allowUsingExhaustedKeys);
|
||||
|
||||
optional<vector<vector<uint8_t>>> getAuthKeysNeedingCertification(
|
||||
const sp<android::hardware::identity::V1_0::IIdentityCredential>& halBinder);
|
||||
|
||||
bool storeStaticAuthenticationData(const vector<uint8_t>& authenticationKey,
|
||||
const vector<uint8_t>& staticAuthData);
|
||||
|
||||
private:
|
||||
// Set by constructor.
|
||||
//
|
||||
string dataPath_;
|
||||
uid_t ownerUid_;
|
||||
string name_;
|
||||
|
||||
// Calculated at construction time, from |dataPath_|, |ownerUid_|, |name_|.
|
||||
string fileName_;
|
||||
|
||||
// Data serialized in CBOR from here:
|
||||
//
|
||||
int64_t secureUserId_;
|
||||
vector<uint8_t> credentialData_;
|
||||
vector<uint8_t> attestationCertificate_;
|
||||
vector<SecureAccessControlProfile> secureAccessControlProfiles_;
|
||||
map<string, EntryData> idToEncryptedChunks_;
|
||||
|
||||
int keyCount_ = 0;
|
||||
int maxUsesPerKey_ = 1;
|
||||
vector<AuthKeyData> authKeyDatas_; // Always |keyCount_| long.
|
||||
};
|
||||
|
||||
} // namespace identity
|
||||
} // namespace security
|
||||
} // namespace android
|
||||
|
||||
#endif // SYSTEM_SECURITY_CREDENTIAL_DATA_H_
|
161
identity/CredentialStore.cpp
Normal file
161
identity/CredentialStore.cpp
Normal file
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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 "CredentialStore"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include <binder/IPCThreadState.h>
|
||||
|
||||
#include "Credential.h"
|
||||
#include "CredentialStore.h"
|
||||
#include "Util.h"
|
||||
#include "WritableCredential.h"
|
||||
|
||||
namespace android {
|
||||
namespace security {
|
||||
namespace identity {
|
||||
|
||||
using ::android::hardware::hidl_string;
|
||||
using ::android::hardware::hidl_vec;
|
||||
using ::android::hardware::identity::V1_0::Result;
|
||||
using ::android::hardware::identity::V1_0::ResultCode;
|
||||
|
||||
using ::android::hardware::identity::V1_0::IWritableIdentityCredential;
|
||||
|
||||
CredentialStore::CredentialStore(const std::string& dataPath, sp<IIdentityCredentialStore> hal)
|
||||
: dataPath_(dataPath), hal_(hal) {}
|
||||
|
||||
bool CredentialStore::init() {
|
||||
Result result;
|
||||
hal_->getHardwareInformation([&](const Result& _result, const hidl_string& credentialStoreName,
|
||||
const hidl_string& credentialStoreAuthorName,
|
||||
uint32_t _dataChunkSize, bool _isDirectAccess,
|
||||
const hidl_vec<hidl_string>& _supportedDocTypes) {
|
||||
result = _result;
|
||||
dataChunkSize_ = _dataChunkSize;
|
||||
isDirectAccess_ = _isDirectAccess;
|
||||
supportedDocTypes_.clear();
|
||||
for (auto& docType : _supportedDocTypes) {
|
||||
supportedDocTypes_.push_back(docType);
|
||||
}
|
||||
LOG(INFO) << "Connected to Identity Credential HAL with name '" << credentialStoreName
|
||||
<< "' authored by '" << credentialStoreAuthorName << "' with chunk size "
|
||||
<< _dataChunkSize << " and directoAccess set to "
|
||||
<< (_isDirectAccess ? "true" : "false");
|
||||
});
|
||||
if (result.code != ResultCode::OK) {
|
||||
LOG(ERROR) << "Error getting hardware information: " << (int)result.code << ": "
|
||||
<< result.message;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
CredentialStore::~CredentialStore() {}
|
||||
|
||||
Status CredentialStore::getSecurityHardwareInfo(SecurityHardwareInfoParcel* _aidl_return) {
|
||||
SecurityHardwareInfoParcel info;
|
||||
info.directAccess = isDirectAccess_;
|
||||
info.supportedDocTypes = supportedDocTypes_;
|
||||
*_aidl_return = info;
|
||||
return Status::ok();
|
||||
};
|
||||
|
||||
Status CredentialStore::createCredential(const std::string& credentialName,
|
||||
const std::string& docType,
|
||||
sp<IWritableCredential>* _aidl_return) {
|
||||
uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
|
||||
optional<bool> credentialExists =
|
||||
CredentialData::credentialExists(dataPath_, callingUid, credentialName);
|
||||
if (!credentialExists.has_value()) {
|
||||
return Status::fromServiceSpecificError(
|
||||
ERROR_GENERIC, "Error determining if credential with given name exists");
|
||||
}
|
||||
if (credentialExists.value()) {
|
||||
return Status::fromServiceSpecificError(ERROR_ALREADY_PERSONALIZED,
|
||||
"Credential with given name already exists");
|
||||
}
|
||||
|
||||
if (supportedDocTypes_.size() > 0) {
|
||||
if (std::find(supportedDocTypes_.begin(), supportedDocTypes_.end(), docType) ==
|
||||
supportedDocTypes_.end()) {
|
||||
return Status::fromServiceSpecificError(ERROR_DOCUMENT_TYPE_NOT_SUPPORTED,
|
||||
"No support for given document type");
|
||||
}
|
||||
}
|
||||
|
||||
Result result;
|
||||
sp<IWritableIdentityCredential> halWritableCredential;
|
||||
hal_->createCredential(
|
||||
docType, false,
|
||||
[&](const Result& _result, const sp<IWritableIdentityCredential>& _halWritableCredential) {
|
||||
result = _result;
|
||||
halWritableCredential = _halWritableCredential;
|
||||
});
|
||||
if (result.code != ResultCode::OK) {
|
||||
return halResultToGenericError(result);
|
||||
}
|
||||
|
||||
sp<IWritableCredential> writableCredential = new WritableCredential(
|
||||
dataPath_, credentialName, docType, dataChunkSize_, halWritableCredential);
|
||||
*_aidl_return = writableCredential;
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
// Keep in sync with IdentityCredentialStore.java
|
||||
//
|
||||
|
||||
const int CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 = 1;
|
||||
|
||||
Status CredentialStore::getCredentialByName(const std::string& credentialName, int32_t cipherSuite,
|
||||
sp<ICredential>* _aidl_return) {
|
||||
*_aidl_return = nullptr;
|
||||
|
||||
uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
|
||||
optional<bool> credentialExists =
|
||||
CredentialData::credentialExists(dataPath_, callingUid, credentialName);
|
||||
if (!credentialExists.has_value()) {
|
||||
return Status::fromServiceSpecificError(
|
||||
ERROR_GENERIC, "Error determining if credential with given name exists");
|
||||
}
|
||||
if (!credentialExists.value()) {
|
||||
return Status::fromServiceSpecificError(ERROR_NO_SUCH_CREDENTIAL,
|
||||
"Credential with given name doesn't exist");
|
||||
}
|
||||
|
||||
// We only support a single cipher-suite right now.
|
||||
if (cipherSuite != CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256) {
|
||||
return Status::fromServiceSpecificError(ERROR_CIPHER_SUITE_NOT_SUPPORTED,
|
||||
"Cipher suite not supported");
|
||||
}
|
||||
|
||||
sp<Credential> credential = new Credential(dataPath_, credentialName);
|
||||
|
||||
Status loadStatus = credential->loadCredential(hal_);
|
||||
if (!loadStatus.isOk()) {
|
||||
LOG(ERROR) << "Error loading credential";
|
||||
} else {
|
||||
*_aidl_return = credential;
|
||||
}
|
||||
return loadStatus;
|
||||
}
|
||||
|
||||
} // namespace identity
|
||||
} // namespace security
|
||||
} // namespace android
|
70
identity/CredentialStore.h
Normal file
70
identity/CredentialStore.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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 SYSTEM_SECURITY_CREDENTIAL_STORE_H_
|
||||
#define SYSTEM_SECURITY_CREDENTIAL_STORE_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android/hardware/identity/1.0/IIdentityCredentialStore.h>
|
||||
#include <android/hardware/identity/1.0/types.h>
|
||||
|
||||
#include <android/security/identity/BnCredentialStore.h>
|
||||
|
||||
namespace android {
|
||||
namespace security {
|
||||
namespace identity {
|
||||
|
||||
using ::android::sp;
|
||||
using ::android::binder::Status;
|
||||
using ::std::string;
|
||||
using ::std::unique_ptr;
|
||||
using ::std::vector;
|
||||
|
||||
using ::android::hardware::identity::V1_0::IIdentityCredentialStore;
|
||||
|
||||
class CredentialStore : public BnCredentialStore {
|
||||
public:
|
||||
CredentialStore(const string& dataPath, sp<IIdentityCredentialStore> hal);
|
||||
~CredentialStore();
|
||||
|
||||
bool init();
|
||||
|
||||
// ICredentialStore overrides
|
||||
Status getSecurityHardwareInfo(SecurityHardwareInfoParcel* _aidl_return) override;
|
||||
|
||||
Status createCredential(const string& credentialName, const string& docType,
|
||||
sp<IWritableCredential>* _aidl_return) override;
|
||||
|
||||
Status getCredentialByName(const string& credentialName, int32_t cipherSuite,
|
||||
sp<ICredential>* _aidl_return) override;
|
||||
|
||||
private:
|
||||
string dataPath_;
|
||||
|
||||
sp<IIdentityCredentialStore> hal_;
|
||||
|
||||
bool isDirectAccess_;
|
||||
vector<string> supportedDocTypes_;
|
||||
size_t dataChunkSize_;
|
||||
};
|
||||
|
||||
} // namespace identity
|
||||
} // namespace security
|
||||
} // namespace android
|
||||
|
||||
#endif // SYSTEM_SECURITY_CREDENTIAL_STORE_H_
|
91
identity/CredentialStoreFactory.cpp
Normal file
91
identity/CredentialStoreFactory.cpp
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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 "CredentialStoreFactory"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include <binder/IPCThreadState.h>
|
||||
|
||||
#include "CredentialStore.h"
|
||||
#include "CredentialStoreFactory.h"
|
||||
|
||||
namespace android {
|
||||
namespace security {
|
||||
namespace identity {
|
||||
|
||||
using ::android::hardware::hidl_string;
|
||||
using ::android::hardware::hidl_vec;
|
||||
using ::android::hardware::identity::V1_0::IIdentityCredentialStore;
|
||||
using ::android::hardware::identity::V1_0::Result;
|
||||
using ::android::hardware::identity::V1_0::ResultCode;
|
||||
|
||||
CredentialStoreFactory::CredentialStoreFactory(const std::string& dataPath) : dataPath_(dataPath) {}
|
||||
|
||||
CredentialStoreFactory::~CredentialStoreFactory() {}
|
||||
|
||||
CredentialStore* CredentialStoreFactory::createCredentialStore(const string& serviceName) {
|
||||
sp<IIdentityCredentialStore> hal = IIdentityCredentialStore::tryGetService(serviceName);
|
||||
if (hal.get() == nullptr) {
|
||||
LOG(ERROR) << "Error get hal for store with service name '" << serviceName << "'";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CredentialStore* store = new CredentialStore(dataPath_, hal);
|
||||
if (!store->init()) {
|
||||
LOG(ERROR) << "Error initializing CredentialStore with service name '" << serviceName
|
||||
<< "'";
|
||||
delete store;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
Status CredentialStoreFactory::getCredentialStore(int32_t credentialStoreType,
|
||||
sp<ICredentialStore>* _aidl_return) {
|
||||
switch (credentialStoreType) {
|
||||
case CREDENTIAL_STORE_TYPE_DEFAULT:
|
||||
if (defaultStore_.get() == nullptr) {
|
||||
defaultStore_ = createCredentialStore("default");
|
||||
}
|
||||
if (defaultStore_.get() == nullptr) {
|
||||
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
|
||||
"Error creating default store");
|
||||
}
|
||||
*_aidl_return = defaultStore_.get();
|
||||
return Status::ok();
|
||||
|
||||
case CREDENTIAL_STORE_TYPE_DIRECT_ACCESS:
|
||||
if (directAccessStore_.get() == nullptr) {
|
||||
directAccessStore_ = createCredentialStore("directAccess");
|
||||
}
|
||||
if (directAccessStore_.get() == nullptr) {
|
||||
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
|
||||
"Error creating direct access store");
|
||||
}
|
||||
*_aidl_return = directAccessStore_.get();
|
||||
return Status::ok();
|
||||
break;
|
||||
}
|
||||
|
||||
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
|
||||
"Unknown credential store type");
|
||||
}
|
||||
|
||||
} // namespace identity
|
||||
} // namespace security
|
||||
} // namespace android
|
53
identity/CredentialStoreFactory.h
Normal file
53
identity/CredentialStoreFactory.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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 SYSTEM_SECURITY_CREDENTIAL_STORE_FACTORY_H_
|
||||
#define SYSTEM_SECURITY_CREDENTIAL_STORE_FACTORY_H_
|
||||
|
||||
#include <android/security/identity/BnCredentialStoreFactory.h>
|
||||
|
||||
#include "CredentialStore.h"
|
||||
|
||||
namespace android {
|
||||
namespace security {
|
||||
namespace identity {
|
||||
|
||||
using ::android::sp;
|
||||
using ::android::binder::Status;
|
||||
using ::std::string;
|
||||
|
||||
class CredentialStoreFactory : public BnCredentialStoreFactory {
|
||||
public:
|
||||
explicit CredentialStoreFactory(const string& dataPath);
|
||||
~CredentialStoreFactory();
|
||||
|
||||
Status getCredentialStore(int32_t credentialStoreType,
|
||||
sp<ICredentialStore>* _aidl_return) override;
|
||||
|
||||
private:
|
||||
CredentialStore* createCredentialStore(const string& serviceName);
|
||||
|
||||
string dataPath_;
|
||||
|
||||
sp<CredentialStore> defaultStore_;
|
||||
sp<CredentialStore> directAccessStore_;
|
||||
};
|
||||
|
||||
} // namespace identity
|
||||
} // namespace security
|
||||
} // namespace android
|
||||
|
||||
#endif // SYSTEM_SECURITY_CREDENTIAL_STORE_FACTORY_H_
|
125
identity/Util.cpp
Normal file
125
identity/Util.cpp
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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 "Util"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include <android/security/identity/ICredentialStore.h>
|
||||
|
||||
#include "Util.h"
|
||||
|
||||
namespace android {
|
||||
namespace security {
|
||||
namespace identity {
|
||||
|
||||
using ::android::base::StringPrintf;
|
||||
|
||||
Status halResultToGenericError(const Result& result) {
|
||||
string message =
|
||||
StringPrintf("HAL failed with code %d: %s", int(result.code), result.message.c_str());
|
||||
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC, message.c_str());
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> fileGetContents(const string& path) {
|
||||
int fd = open(path.c_str(), O_RDONLY);
|
||||
if (fd == -1) {
|
||||
PLOG(ERROR) << "Error opening " << path;
|
||||
return {};
|
||||
}
|
||||
|
||||
struct stat statbuf;
|
||||
if (fstat(fd, &statbuf) != 0) {
|
||||
PLOG(ERROR) << "Error statting " << path;
|
||||
close(fd);
|
||||
return {};
|
||||
}
|
||||
vector<uint8_t> data;
|
||||
data.resize(statbuf.st_size);
|
||||
|
||||
uint8_t* p = data.data();
|
||||
size_t remaining = data.size();
|
||||
while (remaining > 0) {
|
||||
size_t numRead = TEMP_FAILURE_RETRY(read(fd, p, remaining));
|
||||
if (numRead <= 0) {
|
||||
PLOG(ERROR) << "Failed reading from '" << path << "'";
|
||||
close(fd);
|
||||
return {};
|
||||
}
|
||||
p += numRead;
|
||||
remaining -= numRead;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
bool fileSetContents(const string& path, const vector<uint8_t>& data) {
|
||||
char tempName[4096];
|
||||
int fd;
|
||||
|
||||
string tempNameStr = path + ".XXXXXX";
|
||||
if (tempNameStr.size() >= sizeof tempName - 1) {
|
||||
LOG(ERROR) << "Path name too long";
|
||||
return false;
|
||||
}
|
||||
strncpy(tempName, tempNameStr.c_str(), sizeof tempName);
|
||||
|
||||
fd = mkstemp(tempName);
|
||||
if (fd == -1) {
|
||||
PLOG(ERROR) << "Error creating temp file for '" << path << "'";
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t* p = data.data();
|
||||
size_t remaining = data.size();
|
||||
while (remaining > 0) {
|
||||
size_t numWritten = TEMP_FAILURE_RETRY(write(fd, p, remaining));
|
||||
if (numWritten <= 0) {
|
||||
PLOG(ERROR) << "Failed writing into temp file for '" << path << "'";
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
p += numWritten;
|
||||
remaining -= numWritten;
|
||||
}
|
||||
|
||||
if (TEMP_FAILURE_RETRY(fsync(fd) == -1)) {
|
||||
PLOG(ERROR) << "Failed fsyncing temp file for '" << path << "'";
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
if (rename(tempName, path.c_str()) != 0) {
|
||||
PLOG(ERROR) << "Error renaming temp file for '" << path << "'";
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace identity
|
||||
} // namespace security
|
||||
} // namespace android
|
56
identity/Util.h
Normal file
56
identity/Util.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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 SYSTEM_SECURITY_IDENTITY_UTIL_H_
|
||||
#define SYSTEM_SECURITY_IDENTITY_UTIL_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android/hardware/identity/1.0/IIdentityCredentialStore.h>
|
||||
#include <android/hardware/identity/1.0/types.h>
|
||||
#include <binder/Status.h>
|
||||
|
||||
namespace android {
|
||||
namespace security {
|
||||
namespace identity {
|
||||
|
||||
using ::std::optional;
|
||||
using ::std::string;
|
||||
using ::std::vector;
|
||||
|
||||
using ::android::binder::Status;
|
||||
using ::android::hardware::identity::V1_0::Result;
|
||||
|
||||
Status halResultToGenericError(const Result& result);
|
||||
|
||||
// Helper function to atomically write |data| into file at |path|.
|
||||
//
|
||||
// Returns true on success, false on error.
|
||||
//
|
||||
bool fileSetContents(const string& path, const vector<uint8_t>& data);
|
||||
|
||||
// Helper function which reads contents offile at |path| into |data|.
|
||||
//
|
||||
// Returns nothing on error, the content on success.
|
||||
//
|
||||
optional<vector<uint8_t>> fileGetContents(const string& path);
|
||||
|
||||
} // namespace identity
|
||||
} // namespace security
|
||||
} // namespace android
|
||||
|
||||
#endif // SYSTEM_SECURITY_IDENTITY_UTIL_H_
|
192
identity/WritableCredential.cpp
Normal file
192
identity/WritableCredential.cpp
Normal file
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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 "WritableCredential"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
|
||||
|
||||
#include <android/security/identity/ICredentialStore.h>
|
||||
|
||||
#include <binder/IPCThreadState.h>
|
||||
|
||||
#include <cppbor.h>
|
||||
#include <cppbor_parse.h>
|
||||
|
||||
#include "CredentialData.h"
|
||||
#include "Util.h"
|
||||
#include "WritableCredential.h"
|
||||
|
||||
namespace android {
|
||||
namespace security {
|
||||
namespace identity {
|
||||
|
||||
using ::std::pair;
|
||||
|
||||
using ::android::hardware::hidl_vec;
|
||||
|
||||
using ::android::hardware::identity::V1_0::Result;
|
||||
using ::android::hardware::identity::V1_0::ResultCode;
|
||||
using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
|
||||
|
||||
using ::android::hardware::identity::support::chunkVector;
|
||||
|
||||
WritableCredential::WritableCredential(const string& dataPath, const string& credentialName,
|
||||
const string& /*docType*/, size_t dataChunkSize,
|
||||
sp<IWritableIdentityCredential> halBinder)
|
||||
: dataPath_(dataPath), credentialName_(credentialName), dataChunkSize_(dataChunkSize),
|
||||
halBinder_(halBinder) {}
|
||||
|
||||
WritableCredential::~WritableCredential() {}
|
||||
|
||||
Status WritableCredential::ensureAttestationCertificateExists(const vector<uint8_t>& challenge) {
|
||||
vector<uint8_t> attestationCertificate;
|
||||
|
||||
if (!attestationCertificate_.empty()) {
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
Result result;
|
||||
halBinder_->getAttestationCertificate(
|
||||
challenge, [&](const Result& _result, const hidl_vec<uint8_t>& _attestationCertificate) {
|
||||
result = _result;
|
||||
attestationCertificate = _attestationCertificate;
|
||||
});
|
||||
if (result.code != ResultCode::OK) {
|
||||
LOG(ERROR) << "Error calling getAttestationCertificate()";
|
||||
return halResultToGenericError(result);
|
||||
}
|
||||
attestationCertificate_ = attestationCertificate;
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
Status WritableCredential::getCredentialKeyCertificateChain(const vector<uint8_t>& challenge,
|
||||
vector<uint8_t>* _aidl_return) {
|
||||
|
||||
Status ensureStatus = ensureAttestationCertificateExists(challenge);
|
||||
if (!ensureStatus.isOk()) {
|
||||
return ensureStatus;
|
||||
}
|
||||
|
||||
*_aidl_return = attestationCertificate_;
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
Status
|
||||
WritableCredential::personalize(const vector<AccessControlProfileParcel>& accessControlProfiles,
|
||||
const vector<EntryNamespaceParcel>& entryNamespaces,
|
||||
int64_t secureUserId, vector<uint8_t>* _aidl_return) {
|
||||
Status ensureStatus = ensureAttestationCertificateExists({});
|
||||
if (!ensureStatus.isOk()) {
|
||||
return ensureStatus;
|
||||
}
|
||||
|
||||
uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
|
||||
CredentialData data = CredentialData(dataPath_, callingUid, credentialName_);
|
||||
|
||||
// Note: The value 0 is used to convey that no user-authentication is needed for this
|
||||
// credential. This is to allow creating credentials w/o user authentication on devices
|
||||
// where Secure lock screen is not enabled.
|
||||
data.setSecureUserId(secureUserId);
|
||||
|
||||
data.setAttestationCertificate(attestationCertificate_);
|
||||
|
||||
vector<uint16_t> entryCounts;
|
||||
for (const EntryNamespaceParcel& ensParcel : entryNamespaces) {
|
||||
entryCounts.push_back(ensParcel.entries.size());
|
||||
}
|
||||
|
||||
Result result;
|
||||
halBinder_->startPersonalization(accessControlProfiles.size(), entryCounts,
|
||||
[&](const Result& _result) { result = _result; });
|
||||
if (result.code != ResultCode::OK) {
|
||||
return halResultToGenericError(result);
|
||||
}
|
||||
|
||||
for (const AccessControlProfileParcel& acpParcel : accessControlProfiles) {
|
||||
halBinder_->addAccessControlProfile(
|
||||
acpParcel.id, acpParcel.readerCertificate, acpParcel.userAuthenticationRequired,
|
||||
acpParcel.userAuthenticationTimeoutMillis, secureUserId,
|
||||
[&](const Result& _result, const SecureAccessControlProfile& profile) {
|
||||
data.addSecureAccessControlProfile(profile);
|
||||
result = _result;
|
||||
});
|
||||
if (result.code != ResultCode::OK) {
|
||||
return halResultToGenericError(result);
|
||||
}
|
||||
}
|
||||
|
||||
for (const EntryNamespaceParcel& ensParcel : entryNamespaces) {
|
||||
for (const EntryParcel& eParcel : ensParcel.entries) {
|
||||
vector<vector<uint8_t>> chunks = chunkVector(eParcel.value, dataChunkSize_);
|
||||
|
||||
vector<uint16_t> ids;
|
||||
std::copy(eParcel.accessControlProfileIds.begin(),
|
||||
eParcel.accessControlProfileIds.end(), std::back_inserter(ids));
|
||||
|
||||
halBinder_->beginAddEntry(ids, ensParcel.namespaceName, eParcel.name,
|
||||
eParcel.value.size(),
|
||||
[&](const Result& _result) { result = _result; });
|
||||
if (result.code != ResultCode::OK) {
|
||||
return halResultToGenericError(result);
|
||||
}
|
||||
|
||||
vector<vector<uint8_t>> encryptedChunks;
|
||||
for (const auto& chunk : chunks) {
|
||||
halBinder_->addEntryValue(
|
||||
chunk, [&](const Result& _result, const hidl_vec<uint8_t>& encryptedContent) {
|
||||
result = _result;
|
||||
encryptedChunks.push_back(encryptedContent);
|
||||
});
|
||||
if (result.code != ResultCode::OK) {
|
||||
return halResultToGenericError(result);
|
||||
}
|
||||
}
|
||||
EntryData eData;
|
||||
eData.size = eParcel.value.size();
|
||||
eData.accessControlProfileIds = std::move(ids);
|
||||
eData.encryptedChunks = std::move(encryptedChunks);
|
||||
data.addEntryData(ensParcel.namespaceName, eParcel.name, eData);
|
||||
}
|
||||
}
|
||||
|
||||
vector<uint8_t> credentialData;
|
||||
vector<uint8_t> proofOfProvisioningSignature;
|
||||
halBinder_->finishAddingEntries([&](const Result& _result,
|
||||
const hidl_vec<uint8_t>& _credentialData,
|
||||
const hidl_vec<uint8_t>& _proofOfProvisioningSignature) {
|
||||
data.setCredentialData(_credentialData);
|
||||
result = _result;
|
||||
credentialData = _credentialData;
|
||||
proofOfProvisioningSignature = _proofOfProvisioningSignature;
|
||||
});
|
||||
if (result.code != ResultCode::OK) {
|
||||
return halResultToGenericError(result);
|
||||
}
|
||||
|
||||
if (!data.saveToDisk()) {
|
||||
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
|
||||
"Error saving credential data to disk");
|
||||
}
|
||||
|
||||
*_aidl_return = proofOfProvisioningSignature;
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
} // namespace identity
|
||||
} // namespace security
|
||||
} // namespace android
|
65
identity/WritableCredential.h
Normal file
65
identity/WritableCredential.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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 SYSTEM_SECURITY_WRITABLE_CREDENTIAL_H_
|
||||
#define SYSTEM_SECURITY_WRITABLE_CREDENTIAL_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android/security/identity/BnWritableCredential.h>
|
||||
|
||||
#include <android/hardware/identity/1.0/IIdentityCredentialStore.h>
|
||||
#include <android/hardware/identity/1.0/types.h>
|
||||
|
||||
namespace android {
|
||||
namespace security {
|
||||
namespace identity {
|
||||
|
||||
using ::android::binder::Status;
|
||||
using ::android::hardware::identity::V1_0::IWritableIdentityCredential;
|
||||
using ::std::string;
|
||||
using ::std::vector;
|
||||
|
||||
class WritableCredential : public BnWritableCredential {
|
||||
public:
|
||||
WritableCredential(const string& dataPath, const string& credentialName, const string& docType,
|
||||
size_t dataChunkSize, sp<IWritableIdentityCredential> halBinder);
|
||||
~WritableCredential();
|
||||
|
||||
// IWritableCredential overrides
|
||||
Status getCredentialKeyCertificateChain(const vector<uint8_t>& challenge,
|
||||
vector<uint8_t>* _aidl_return) override;
|
||||
|
||||
Status personalize(const vector<AccessControlProfileParcel>& accessControlProfiles,
|
||||
const vector<EntryNamespaceParcel>& entryNamespaces, int64_t secureUserId,
|
||||
vector<uint8_t>* _aidl_return) override;
|
||||
|
||||
private:
|
||||
string dataPath_;
|
||||
string credentialName_;
|
||||
size_t dataChunkSize_;
|
||||
sp<IWritableIdentityCredential> halBinder_;
|
||||
vector<uint8_t> attestationCertificate_;
|
||||
|
||||
Status ensureAttestationCertificateExists(const vector<uint8_t>& challenge);
|
||||
};
|
||||
|
||||
} // namespace identity
|
||||
} // namespace security
|
||||
} // namespace android
|
||||
|
||||
#endif // SYSTEM_SECURITY_WRITABLE_CREDENTIAL_H_
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.identity;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
parcelable AccessControlProfileParcel {
|
||||
int id;
|
||||
byte[] readerCertificate;
|
||||
boolean userAuthenticationRequired;
|
||||
long userAuthenticationTimeoutMillis;
|
||||
}
|
24
identity/binder/android/security/identity/AuthKeyParcel.aidl
Normal file
24
identity/binder/android/security/identity/AuthKeyParcel.aidl
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.identity;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
parcelable AuthKeyParcel {
|
||||
byte[] x509cert;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.identity;
|
||||
|
||||
import android.security.identity.EntryParcel;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
parcelable EntryNamespaceParcel {
|
||||
@utf8InCpp String namespaceName;
|
||||
EntryParcel[] entries;
|
||||
}
|
26
identity/binder/android/security/identity/EntryParcel.aidl
Normal file
26
identity/binder/android/security/identity/EntryParcel.aidl
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.identity;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
parcelable EntryParcel {
|
||||
@utf8InCpp String name;
|
||||
byte[] value;
|
||||
int[] accessControlProfileIds;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.identity;
|
||||
|
||||
import android.security.identity.ResultNamespaceParcel;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
parcelable GetEntriesResultParcel {
|
||||
ResultNamespaceParcel[] resultNamespaces;
|
||||
byte[] deviceNameSpaces;
|
||||
byte[] mac;
|
||||
byte[] staticAuthenticationData;
|
||||
}
|
62
identity/binder/android/security/identity/ICredential.aidl
Normal file
62
identity/binder/android/security/identity/ICredential.aidl
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.identity;
|
||||
|
||||
import android.security.identity.RequestNamespaceParcel;
|
||||
import android.security.identity.GetEntriesResultParcel;
|
||||
import android.security.identity.AuthKeyParcel;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
interface ICredential {
|
||||
/* The STATUS_* constants are used in the status field in ResultEntryParcel.
|
||||
* Keep in sync with ResultNamespace.java.
|
||||
*/
|
||||
const int STATUS_OK = 0;
|
||||
const int STATUS_NO_SUCH_ENTRY = 1;
|
||||
const int STATUS_NOT_REQUESTED = 2;
|
||||
const int STATUS_NOT_IN_REQUEST_MESSAGE = 3;
|
||||
const int STATUS_USER_AUTHENTICATION_FAILED = 4;
|
||||
const int STATUS_READER_AUTHENTICATION_FAILED = 5;
|
||||
const int STATUS_NO_ACCESS_CONTROL_PROFILES = 6;
|
||||
|
||||
byte[] createEphemeralKeyPair();
|
||||
|
||||
void setReaderEphemeralPublicKey(in byte[] publicKey);
|
||||
|
||||
byte[] deleteCredential();
|
||||
|
||||
byte[] getCredentialKeyCertificateChain();
|
||||
|
||||
long selectAuthKey(in boolean allowUsingExhaustedKeys);
|
||||
|
||||
GetEntriesResultParcel getEntries(in byte[] requestMessage,
|
||||
in RequestNamespaceParcel[] requestNamespaces,
|
||||
in byte[] sessionTranscript,
|
||||
in byte[] readerSignature,
|
||||
in boolean allowUsingExhaustedKeys);
|
||||
|
||||
void setAvailableAuthenticationKeys(in int keyCount, in int maxUsesPerKey);
|
||||
|
||||
AuthKeyParcel[] getAuthKeysNeedingCertification();
|
||||
|
||||
void storeStaticAuthenticationData(in AuthKeyParcel authenticationKey, in byte[] staticAuthData);
|
||||
|
||||
int[] getAuthenticationDataUsageCount();
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.identity;
|
||||
|
||||
import android.security.identity.IWritableCredential;
|
||||
import android.security.identity.ICredential;
|
||||
import android.security.identity.SecurityHardwareInfoParcel;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
interface ICredentialStore {
|
||||
/* All binder calls may return a ServiceSpecificException
|
||||
* with the following error codes:
|
||||
*/
|
||||
const int ERROR_NONE = 0;
|
||||
const int ERROR_GENERIC = 1;
|
||||
const int ERROR_ALREADY_PERSONALIZED = 2;
|
||||
const int ERROR_NO_SUCH_CREDENTIAL = 3;
|
||||
const int ERROR_CIPHER_SUITE_NOT_SUPPORTED = 4;
|
||||
const int ERROR_EPHEMERAL_PUBLIC_KEY_NOT_FOUND = 5;
|
||||
const int ERROR_NO_AUTHENTICATION_KEY_AVAILABLE = 6;
|
||||
const int ERROR_INVALID_READER_SIGNATURE = 7;
|
||||
const int ERROR_DOCUMENT_TYPE_NOT_SUPPORTED = 8;
|
||||
const int ERROR_AUTHENTICATION_KEY_NOT_FOUND = 9;
|
||||
const int ERROR_INVALID_ITEMS_REQUEST_MESSAGE = 10;
|
||||
const int ERROR_SESSION_TRANSCRIPT_MISMATCH = 11;
|
||||
|
||||
SecurityHardwareInfoParcel getSecurityHardwareInfo();
|
||||
|
||||
IWritableCredential createCredential(in @utf8InCpp String credentialName,
|
||||
in @utf8InCpp String docType);
|
||||
ICredential getCredentialByName(in @utf8InCpp String credentialName,
|
||||
in int cipherSuite);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.identity;
|
||||
|
||||
import android.security.identity.ICredentialStore;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
interface ICredentialStoreFactory {
|
||||
const int CREDENTIAL_STORE_TYPE_DEFAULT = 0;
|
||||
const int CREDENTIAL_STORE_TYPE_DIRECT_ACCESS = 1;
|
||||
|
||||
ICredentialStore getCredentialStore(int credentialStoreType);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.identity;
|
||||
|
||||
import android.security.identity.AccessControlProfileParcel;
|
||||
import android.security.identity.EntryNamespaceParcel;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
interface IWritableCredential {
|
||||
byte[] getCredentialKeyCertificateChain(in byte[] challenge);
|
||||
byte[] personalize(in AccessControlProfileParcel[] accessControlProfiles,
|
||||
in EntryNamespaceParcel[] entryNamespaces,
|
||||
in long secureUserId);
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.identity;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
parcelable RequestEntryParcel {
|
||||
@utf8InCpp String name;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.identity;
|
||||
|
||||
import android.security.identity.RequestEntryParcel;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
parcelable RequestNamespaceParcel {
|
||||
@utf8InCpp String namespaceName;
|
||||
RequestEntryParcel[] entries;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.identity;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
parcelable ResultEntryParcel {
|
||||
int status;
|
||||
@utf8InCpp String name;
|
||||
byte[] value;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.identity;
|
||||
|
||||
import android.security.identity.ResultEntryParcel;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
parcelable ResultNamespaceParcel {
|
||||
@utf8InCpp String namespaceName;
|
||||
ResultEntryParcel[] entries;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.identity;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
parcelable SecurityHardwareInfoParcel {
|
||||
boolean directAccess;
|
||||
@utf8InCpp String[] supportedDocTypes;
|
||||
}
|
5
identity/credstore.rc
Normal file
5
identity/credstore.rc
Normal file
|
@ -0,0 +1,5 @@
|
|||
service credstore /system/bin/credstore /data/misc/credstore
|
||||
class main
|
||||
user keystore
|
||||
group keystore drmrpc readproc log
|
||||
writepid /dev/cpuset/foreground/tasks
|
59
identity/main.cpp
Normal file
59
identity/main.cpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.security.identity"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <binder/IPCThreadState.h>
|
||||
#include <binder/IServiceManager.h>
|
||||
|
||||
#include "CredentialStoreFactory.h"
|
||||
|
||||
#include <cppbor.h>
|
||||
|
||||
using ::std::string;
|
||||
|
||||
using ::android::IPCThreadState;
|
||||
using ::android::IServiceManager;
|
||||
using ::android::sp;
|
||||
using ::android::String16;
|
||||
using ::android::base::InitLogging;
|
||||
using ::android::base::StderrLogger;
|
||||
|
||||
using ::android::security::identity::CredentialStoreFactory;
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
InitLogging(argv, StderrLogger);
|
||||
|
||||
CHECK(argc == 2) << "A directory must be specified";
|
||||
string data_dir = string(argv[1]);
|
||||
CHECK(chdir(data_dir.c_str()) != -1) << "chdir: " << data_dir << ": " << strerror(errno);
|
||||
|
||||
sp<IServiceManager> sm = ::android::defaultServiceManager();
|
||||
sp<CredentialStoreFactory> factory = new CredentialStoreFactory(data_dir);
|
||||
|
||||
auto ret = sm->addService(String16("android.security.identity"), factory);
|
||||
CHECK(ret == ::android::OK) << "Couldn't register binder service";
|
||||
LOG(ERROR) << "Registered binder service";
|
||||
|
||||
IPCThreadState::self()->joinThreadPool();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -173,6 +173,60 @@ AuthTokenTable::FindTimedAuthorization(const std::vector<uint64_t>& sids,
|
|||
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)
|
||||
|
|
|
@ -76,6 +76,10 @@ class AuthTokenTable {
|
|||
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.
|
||||
|
|
|
@ -84,4 +84,7 @@ interface IKeystoreService {
|
|||
boolean isConfirmationPromptSupported();
|
||||
int onKeyguardVisibilityChanged(in boolean isShowing, in int userId);
|
||||
int listUidsOfAuthBoundKeys(out @utf8InCpp List<String> uids);
|
||||
|
||||
// Called by credstore (and only credstore).
|
||||
byte[] getAuthTokenForCredstore(in long challenge, in long secureUserId, in int authTokenMaxAgeMillis);
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
|
||||
#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_proto_handler.h"
|
||||
|
@ -946,6 +947,24 @@ Status KeyStoreService::addAuthToken(const ::std::vector<uint8_t>& authTokenAsVe
|
|||
return Status::ok();
|
||||
}
|
||||
|
||||
Status KeyStoreService::getAuthTokenForCredstore(int64_t challenge, int64_t secureUserId,
|
||||
int32_t authTokenMaxAgeMillis,
|
||||
std::vector<uint8_t>* _aidl_return) {
|
||||
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);
|
||||
std::vector<uint8_t> ret;
|
||||
if (err == AuthTokenTable::OK) {
|
||||
ret = android::hardware::keymaster::V4_0::support::authToken2HidlVec(authToken);
|
||||
}
|
||||
*_aidl_return = ret;
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
bool isDeviceIdAttestationRequested(const KeymasterArguments& params) {
|
||||
const hardware::hidl_vec<KeyParameter>& paramsVec = params.getParameters();
|
||||
for (size_t i = 0; i < paramsVec.size(); ++i) {
|
||||
|
|
|
@ -132,6 +132,9 @@ class KeyStoreService : public android::security::keystore::BnKeystoreService {
|
|||
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
|
||||
getAuthTokenForCredstore(int64_t challenge, int64_t secureUserId, int32_t authTokenMaxAge,
|
||||
::std::vector<uint8_t>* _aidl_return) 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;
|
||||
|
|
Loading…
Reference in a new issue