|  | /* | 
|  | * 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 |