ab3e565511
The credstore system daemon is sitting below the Identity Credential Framework APIs and on top of the Identity Credential HALs. Its main job is to store credential data and provide a way for applications to communicate with the secure hardware abstracted by the HAL. This daemon runs as an unprivileged user, credstore. The auth-tokens needed by credstore are supplied by keystore and this CL includes the requisite changes to keystore for this to work. Bug: 111446262 Test: CTS tests for Framework APIs Change-Id: Ieb4d59852a143482436a1c418c25ed96e25c0047
192 lines
7.2 KiB
C++
192 lines
7.2 KiB
C++
/*
|
|
* 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
|