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