1 /*
2  * Copyright 2019, The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "WritableIdentityCredential"
18 
19 #include "WritableIdentityCredential.h"
20 
21 #include <android/hardware/identity/support/IdentityCredentialSupport.h>
22 
23 #include <android-base/logging.h>
24 #include <android-base/stringprintf.h>
25 
26 #include <cppbor.h>
27 #include <cppbor_parse.h>
28 
29 #include <utility>
30 
31 #include "IdentityCredentialStore.h"
32 
33 #include "FakeSecureHardwareProxy.h"
34 
35 namespace aidl::android::hardware::identity {
36 
37 using ::android::base::StringPrintf;
38 using ::std::optional;
39 using namespace ::android::hardware::identity;
40 
initialize()41 bool WritableIdentityCredential::initialize() {
42     if (!hwProxy_->initialize(testCredential_)) {
43         LOG(ERROR) << "hwProxy->initialize() failed";
44         return false;
45     }
46     startPersonalizationCalled_ = false;
47     firstEntry_ = true;
48 
49     return true;
50 }
51 
52 // Used when updating a credential. Returns false on failure.
initializeForUpdate(const vector<uint8_t> & encryptedCredentialKeys)53 bool WritableIdentityCredential::initializeForUpdate(
54         const vector<uint8_t>& encryptedCredentialKeys) {
55     if (!hwProxy_->initializeForUpdate(testCredential_, docType_, encryptedCredentialKeys)) {
56         LOG(ERROR) << "hwProxy->initializeForUpdate() failed";
57         return false;
58     }
59     startPersonalizationCalled_ = false;
60     firstEntry_ = true;
61 
62     return true;
63 }
64 
~WritableIdentityCredential()65 WritableIdentityCredential::~WritableIdentityCredential() {}
66 
getAttestationCertificate(const vector<uint8_t> & attestationApplicationId,const vector<uint8_t> & attestationChallenge,vector<Certificate> * outCertificateChain)67 ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate(
68         const vector<uint8_t>& attestationApplicationId,
69         const vector<uint8_t>& attestationChallenge, vector<Certificate>* outCertificateChain) {
70     if (getAttestationCertificateAlreadyCalled_) {
71         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
72                 IIdentityCredentialStore::STATUS_FAILED,
73                 "Error attestation certificate previously generated"));
74     }
75     getAttestationCertificateAlreadyCalled_ = true;
76 
77     if (attestationChallenge.empty()) {
78         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
79                 IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge can not be empty"));
80     }
81 
82     optional<vector<uint8_t>> certChain;
83     if (attestationKeyBlob_ && attestationCertificateChain_) {
84         certChain = hwProxy_->createCredentialKeyUsingRkp(
85                 attestationChallenge, attestationApplicationId, *attestationKeyBlob_,
86                 attestationCertificateChain_->at(0));
87     } else {
88         certChain = hwProxy_->createCredentialKey(attestationChallenge, attestationApplicationId);
89     }
90 
91     if (!certChain) {
92         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
93                 IIdentityCredentialStore::STATUS_FAILED,
94                 "Error generating attestation certificate chain"));
95     }
96 
97     optional<vector<vector<uint8_t>>> certs = support::certificateChainSplit(certChain.value());
98     if (!certs) {
99         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
100                 IIdentityCredentialStore::STATUS_FAILED,
101                 "Error splitting chain into separate certificates"));
102     }
103 
104     *outCertificateChain = vector<Certificate>();
105     for (vector<uint8_t>& cert : certs.value()) {
106         Certificate c;
107         c.encodedCertificate = std::move(cert);
108         outCertificateChain->push_back(std::move(c));
109     }
110 
111     for (const vector<uint8_t>& cert : *attestationCertificateChain_) {
112         Certificate c;
113         c.encodedCertificate = cert;
114         outCertificateChain->push_back(std::move(c));
115     }
116 
117     return ndk::ScopedAStatus::ok();
118 }
119 
setExpectedProofOfProvisioningSize(int32_t expectedProofOfProvisioningSize)120 ndk::ScopedAStatus WritableIdentityCredential::setExpectedProofOfProvisioningSize(
121         int32_t expectedProofOfProvisioningSize) {
122     expectedProofOfProvisioningSize_ = expectedProofOfProvisioningSize;
123     return ndk::ScopedAStatus::ok();
124 }
125 
startPersonalization(int32_t accessControlProfileCount,const vector<int32_t> & entryCounts)126 ndk::ScopedAStatus WritableIdentityCredential::startPersonalization(
127         int32_t accessControlProfileCount, const vector<int32_t>& entryCounts) {
128     if (startPersonalizationCalled_) {
129         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
130                 IIdentityCredentialStore::STATUS_FAILED, "startPersonalization called already"));
131     }
132     startPersonalizationCalled_ = true;
133 
134     numAccessControlProfileRemaining_ = accessControlProfileCount;
135     remainingEntryCounts_ = entryCounts;
136     entryNameSpace_ = "";
137 
138     signedDataAccessControlProfiles_ = cppbor::Array();
139     signedDataNamespaces_ = cppbor::Map();
140     signedDataCurrentNamespace_ = cppbor::Array();
141 
142     if (!hwProxy_->startPersonalization(accessControlProfileCount, entryCounts, docType_,
143                                         expectedProofOfProvisioningSize_)) {
144         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
145                 IIdentityCredentialStore::STATUS_FAILED, "eicStartPersonalization"));
146     }
147 
148     return ndk::ScopedAStatus::ok();
149 }
150 
addAccessControlProfile(int32_t id,const Certificate & readerCertificate,bool userAuthenticationRequired,int64_t timeoutMillis,int64_t secureUserId,SecureAccessControlProfile * outSecureAccessControlProfile)151 ndk::ScopedAStatus WritableIdentityCredential::addAccessControlProfile(
152         int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired,
153         int64_t timeoutMillis, int64_t secureUserId,
154         SecureAccessControlProfile* outSecureAccessControlProfile) {
155     if (numAccessControlProfileRemaining_ == 0) {
156         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
157                 IIdentityCredentialStore::STATUS_INVALID_DATA,
158                 "numAccessControlProfileRemaining_ is 0 and expected non-zero"));
159     }
160 
161     if (accessControlProfileIds_.find(id) != accessControlProfileIds_.end()) {
162         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
163                 IIdentityCredentialStore::STATUS_INVALID_DATA,
164                 "Access Control Profile id must be unique"));
165     }
166     accessControlProfileIds_.insert(id);
167 
168     if (id < 0 || id >= 32) {
169         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
170                 IIdentityCredentialStore::STATUS_INVALID_DATA,
171                 "Access Control Profile id must be non-negative and less than 32"));
172     }
173 
174     // Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also
175     // be zero.
176     if (!userAuthenticationRequired && timeoutMillis != 0) {
177         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
178                 IIdentityCredentialStore::STATUS_INVALID_DATA,
179                 "userAuthenticationRequired is false but timeout is non-zero"));
180     }
181 
182     optional<vector<uint8_t>> mac = hwProxy_->addAccessControlProfile(
183             id, readerCertificate.encodedCertificate, userAuthenticationRequired, timeoutMillis,
184             secureUserId);
185     if (!mac) {
186         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
187                 IIdentityCredentialStore::STATUS_FAILED, "eicAddAccessControlProfile"));
188     }
189 
190     SecureAccessControlProfile profile;
191     profile.id = id;
192     profile.readerCertificate = readerCertificate;
193     profile.userAuthenticationRequired = userAuthenticationRequired;
194     profile.timeoutMillis = timeoutMillis;
195     profile.secureUserId = secureUserId;
196     profile.mac = mac.value();
197     cppbor::Map profileMap;
198     profileMap.add("id", profile.id);
199     if (profile.readerCertificate.encodedCertificate.size() > 0) {
200         profileMap.add("readerCertificate",
201                        cppbor::Bstr(profile.readerCertificate.encodedCertificate));
202     }
203     if (profile.userAuthenticationRequired) {
204         profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired);
205         profileMap.add("timeoutMillis", profile.timeoutMillis);
206     }
207     signedDataAccessControlProfiles_.add(std::move(profileMap));
208 
209     numAccessControlProfileRemaining_--;
210 
211     *outSecureAccessControlProfile = profile;
212     return ndk::ScopedAStatus::ok();
213 }
214 
beginAddEntry(const vector<int32_t> & accessControlProfileIds,const string & nameSpace,const string & name,int32_t entrySize)215 ndk::ScopedAStatus WritableIdentityCredential::beginAddEntry(
216         const vector<int32_t>& accessControlProfileIds, const string& nameSpace, const string& name,
217         int32_t entrySize) {
218     if (numAccessControlProfileRemaining_ != 0) {
219         LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_
220                    << " and expected zero";
221         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
222                 IIdentityCredentialStore::STATUS_INVALID_DATA,
223                 "numAccessControlProfileRemaining_ is not zero"));
224     }
225 
226     // Ensure passed-in profile ids reference valid access control profiles
227     for (const int32_t id : accessControlProfileIds) {
228         if (accessControlProfileIds_.find(id) == accessControlProfileIds_.end()) {
229             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
230                     IIdentityCredentialStore::STATUS_INVALID_DATA,
231                     "An id in accessControlProfileIds references non-existing ACP"));
232         }
233     }
234 
235     if (remainingEntryCounts_.size() == 0) {
236         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
237                 IIdentityCredentialStore::STATUS_INVALID_DATA, "No more namespaces to add to"));
238     }
239 
240     // Handle initial beginEntry() call.
241     if (firstEntry_) {
242         firstEntry_ = false;
243         entryNameSpace_ = nameSpace;
244         allNameSpaces_.insert(nameSpace);
245     }
246 
247     // If the namespace changed...
248     if (nameSpace != entryNameSpace_) {
249         if (allNameSpaces_.find(nameSpace) != allNameSpaces_.end()) {
250             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
251                     IIdentityCredentialStore::STATUS_INVALID_DATA,
252                     "Name space cannot be added in interleaving fashion"));
253         }
254 
255         // Then check that all entries in the previous namespace have been added..
256         if (remainingEntryCounts_[0] != 0) {
257             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
258                     IIdentityCredentialStore::STATUS_INVALID_DATA,
259                     "New namespace but a non-zero number of entries remain to be added"));
260         }
261         remainingEntryCounts_.erase(remainingEntryCounts_.begin());
262         remainingEntryCounts_[0] -= 1;
263         allNameSpaces_.insert(nameSpace);
264 
265         if (signedDataCurrentNamespace_.size() > 0) {
266             signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
267             signedDataCurrentNamespace_ = cppbor::Array();
268         }
269     } else {
270         // Same namespace...
271         if (remainingEntryCounts_[0] == 0) {
272             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
273                     IIdentityCredentialStore::STATUS_INVALID_DATA,
274                     "Same namespace but no entries remain to be added"));
275         }
276         remainingEntryCounts_[0] -= 1;
277     }
278 
279     entryRemainingBytes_ = entrySize;
280     entryNameSpace_ = nameSpace;
281     entryName_ = name;
282     entryAccessControlProfileIds_ = accessControlProfileIds;
283     entryBytes_.resize(0);
284     // LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
285 
286     if (!hwProxy_->beginAddEntry(accessControlProfileIds, nameSpace, name, entrySize)) {
287         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
288                 IIdentityCredentialStore::STATUS_FAILED, "eicBeginAddEntry"));
289     }
290 
291     return ndk::ScopedAStatus::ok();
292 }
293 
addEntryValue(const vector<uint8_t> & content,vector<uint8_t> * outEncryptedContent)294 ndk::ScopedAStatus WritableIdentityCredential::addEntryValue(const vector<uint8_t>& content,
295                                                              vector<uint8_t>* outEncryptedContent) {
296     size_t contentSize = content.size();
297 
298     if (contentSize > IdentityCredentialStore::kGcmChunkSize) {
299         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
300                 IIdentityCredentialStore::STATUS_INVALID_DATA,
301                 "Passed in chunk of is bigger than kGcmChunkSize"));
302     }
303     if (contentSize > entryRemainingBytes_) {
304         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
305                 IIdentityCredentialStore::STATUS_INVALID_DATA,
306                 "Passed in chunk is bigger than remaining space"));
307     }
308 
309     entryBytes_.insert(entryBytes_.end(), content.begin(), content.end());
310     entryRemainingBytes_ -= contentSize;
311     if (entryRemainingBytes_ > 0) {
312         if (contentSize != IdentityCredentialStore::kGcmChunkSize) {
313             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
314                     IIdentityCredentialStore::STATUS_INVALID_DATA,
315                     "Retrieved non-final chunk which isn't kGcmChunkSize"));
316         }
317     }
318 
319     optional<vector<uint8_t>> encryptedContent = hwProxy_->addEntryValue(
320             entryAccessControlProfileIds_, entryNameSpace_, entryName_, content);
321     if (!encryptedContent) {
322         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
323                 IIdentityCredentialStore::STATUS_FAILED, "eicAddEntryValue"));
324     }
325 
326     if (entryRemainingBytes_ == 0) {
327         // TODO: ideally do do this without parsing the data (but still validate data is valid
328         // CBOR).
329         auto [item, _, message] = cppbor::parse(entryBytes_);
330         if (item == nullptr) {
331             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
332                     IIdentityCredentialStore::STATUS_INVALID_DATA, "Data is not valid CBOR"));
333         }
334         cppbor::Map entryMap;
335         entryMap.add("name", entryName_);
336         entryMap.add("value", std::move(item));
337         cppbor::Array profileIdArray;
338         for (auto id : entryAccessControlProfileIds_) {
339             profileIdArray.add(id);
340         }
341         entryMap.add("accessControlProfiles", std::move(profileIdArray));
342         signedDataCurrentNamespace_.add(std::move(entryMap));
343     }
344 
345     *outEncryptedContent = encryptedContent.value();
346     return ndk::ScopedAStatus::ok();
347 }
348 
finishAddingEntries(vector<uint8_t> * outCredentialData,vector<uint8_t> * outProofOfProvisioningSignature)349 ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries(
350         vector<uint8_t>* outCredentialData, vector<uint8_t>* outProofOfProvisioningSignature) {
351     if (numAccessControlProfileRemaining_ != 0) {
352         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
353                 IIdentityCredentialStore::STATUS_INVALID_DATA,
354                 "numAccessControlProfileRemaining_ is not 0 and expected zero"));
355     }
356 
357     if (remainingEntryCounts_.size() > 1 || remainingEntryCounts_[0] != 0) {
358         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
359                 IIdentityCredentialStore::STATUS_INVALID_DATA,
360                 "More entry spaces remain than startPersonalization configured"));
361     }
362 
363     if (signedDataCurrentNamespace_.size() > 0) {
364         signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
365     }
366     cppbor::Array popArray;
367     popArray.add("ProofOfProvisioning")
368             .add(docType_)
369             .add(std::move(signedDataAccessControlProfiles_))
370             .add(std::move(signedDataNamespaces_))
371             .add(testCredential_);
372     vector<uint8_t> encodedCbor = popArray.encode();
373 
374     if (encodedCbor.size() != expectedProofOfProvisioningSize_) {
375         LOG(ERROR) << "CBOR for proofOfProvisioning is " << encodedCbor.size() << " bytes, "
376                    << "was expecting " << expectedProofOfProvisioningSize_;
377         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
378                 IIdentityCredentialStore::STATUS_INVALID_DATA,
379                 StringPrintf("Unexpected CBOR size %zd for proofOfProvisioning, was expecting %zd",
380                              encodedCbor.size(), expectedProofOfProvisioningSize_)
381                         .c_str()));
382     }
383 
384     optional<vector<uint8_t>> signatureOfToBeSigned = hwProxy_->finishAddingEntries();
385     if (!signatureOfToBeSigned) {
386         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
387                 IIdentityCredentialStore::STATUS_FAILED, "eicFinishAddingEntries"));
388     }
389 
390     optional<vector<uint8_t>> signature =
391             support::coseSignEcDsaWithSignature(signatureOfToBeSigned.value(),
392                                                 encodedCbor,  // data
393                                                 {});          // certificateChain
394     if (!signature) {
395         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
396                 IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
397     }
398 
399     optional<vector<uint8_t>> encryptedCredentialKeys = hwProxy_->finishGetCredentialData(docType_);
400     if (!encryptedCredentialKeys) {
401         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
402                 IIdentityCredentialStore::STATUS_FAILED,
403                 "Error generating encrypted CredentialKeys"));
404     }
405     cppbor::Array array;
406     array.add(docType_);
407     array.add(testCredential_);
408     array.add(encryptedCredentialKeys.value());
409     vector<uint8_t> credentialData = array.encode();
410 
411     *outCredentialData = credentialData;
412     *outProofOfProvisioningSignature = signature.value();
413     hwProxy_->shutdown();
414 
415     return ndk::ScopedAStatus::ok();
416 }
417 
setRemotelyProvisionedAttestationKey(const vector<uint8_t> & attestationKeyBlob,const vector<uint8_t> & attestationCertificateChain)418 ndk::ScopedAStatus WritableIdentityCredential::setRemotelyProvisionedAttestationKey(
419         const vector<uint8_t>& attestationKeyBlob,
420         const vector<uint8_t>& attestationCertificateChain) {
421     if (!hardwareInformation_.isRemoteKeyProvisioningSupported) {
422         return ndk::ScopedAStatus(AStatus_fromExceptionCodeWithMessage(
423                 EX_UNSUPPORTED_OPERATION, "Remote key provisioning is not supported"));
424     }
425 
426     if (attestationKeyBlob.empty() || attestationCertificateChain.empty()) {
427         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
428                 IIdentityCredentialStore::STATUS_FAILED,
429                 "Empty data passed to setRemotlyProvisionedAttestationKey"));
430     }
431 
432     if (attestationKeyBlob_.has_value()) {
433         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
434                 IIdentityCredentialStore::STATUS_FAILED, "Attestation key already set"));
435     }
436 
437     optional<vector<vector<uint8_t>>> certs =
438             support::certificateChainSplit(attestationCertificateChain);
439     if (!certs) {
440         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
441                 IIdentityCredentialStore::STATUS_FAILED,
442                 "Error splitting chain into separate certificates"));
443     }
444 
445     attestationKeyBlob_ = attestationKeyBlob;
446     attestationCertificateChain_ = *certs;
447     return ndk::ScopedAStatus::ok();
448 }
449 
450 }  // namespace aidl::android::hardware::identity
451