blob: f58ec166315fa6ca2bd5ee1b083058ba587e4a5d [file] [log] [blame]
/*
* 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