Merge "Add credstore system daemon." am: 704895de56 am: b5e1b4aba1 am: a850bc5bcb

Change-Id: I4c65286ca7e3b0d4e8f33796b16b8e63740fb638
This commit is contained in:
Automerger Merge Worker 2020-01-17 03:44:26 +00:00
commit 1281bcdb01
34 changed files with 2772 additions and 0 deletions

86
identity/Android.bp Normal file
View 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
View 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
View 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
View 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
View 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_

View 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

View 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_

View 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

View 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
View 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
View 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_

View 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

View 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_

View file

@ -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;
}

View 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;
}

View file

@ -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;
}

View 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;
}

View file

@ -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;
}

View 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();
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View 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 RequestEntryParcel {
@utf8InCpp String name;
}

View file

@ -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;
}

View 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 ResultEntryParcel {
int status;
@utf8InCpp String name;
byte[] value;
}

View file

@ -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;
}

View file

@ -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
View 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
View 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;
}

View file

@ -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)

View file

@ -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.

View file

@ -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);
}

View file

@ -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) {

View file

@ -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;