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             hwProxy_->createCredentialKey(attestationChallenge, attestationApplicationId);
84     if (!certChain) {
85         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
86                 IIdentityCredentialStore::STATUS_FAILED,
87                 "Error generating attestation certificate chain"));
88     }
89 
90     optional<vector<vector<uint8_t>>> certs = support::certificateChainSplit(certChain.value());
91     if (!certs) {
92         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
93                 IIdentityCredentialStore::STATUS_FAILED,
94                 "Error splitting chain into separate certificates"));
95     }
96 
97     *outCertificateChain = vector<Certificate>();
98     for (const vector<uint8_t>& cert : certs.value()) {
99         Certificate c = Certificate();
100         c.encodedCertificate = cert;
101         outCertificateChain->push_back(std::move(c));
102     }
103 
104     return ndk::ScopedAStatus::ok();
105 }
106 
setExpectedProofOfProvisioningSize(int32_t expectedProofOfProvisioningSize)107 ndk::ScopedAStatus WritableIdentityCredential::setExpectedProofOfProvisioningSize(
108         int32_t expectedProofOfProvisioningSize) {
109     expectedProofOfProvisioningSize_ = expectedProofOfProvisioningSize;
110     return ndk::ScopedAStatus::ok();
111 }
112 
startPersonalization(int32_t accessControlProfileCount,const vector<int32_t> & entryCounts)113 ndk::ScopedAStatus WritableIdentityCredential::startPersonalization(
114         int32_t accessControlProfileCount, const vector<int32_t>& entryCounts) {
115     if (startPersonalizationCalled_) {
116         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
117                 IIdentityCredentialStore::STATUS_FAILED, "startPersonalization called already"));
118     }
119     startPersonalizationCalled_ = true;
120 
121     numAccessControlProfileRemaining_ = accessControlProfileCount;
122     remainingEntryCounts_ = entryCounts;
123     entryNameSpace_ = "";
124 
125     signedDataAccessControlProfiles_ = cppbor::Array();
126     signedDataNamespaces_ = cppbor::Map();
127     signedDataCurrentNamespace_ = cppbor::Array();
128 
129     if (!hwProxy_->startPersonalization(accessControlProfileCount, entryCounts, docType_,
130                                         expectedProofOfProvisioningSize_)) {
131         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
132                 IIdentityCredentialStore::STATUS_FAILED, "eicStartPersonalization"));
133     }
134 
135     return ndk::ScopedAStatus::ok();
136 }
137 
addAccessControlProfile(int32_t id,const Certificate & readerCertificate,bool userAuthenticationRequired,int64_t timeoutMillis,int64_t secureUserId,SecureAccessControlProfile * outSecureAccessControlProfile)138 ndk::ScopedAStatus WritableIdentityCredential::addAccessControlProfile(
139         int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired,
140         int64_t timeoutMillis, int64_t secureUserId,
141         SecureAccessControlProfile* outSecureAccessControlProfile) {
142     if (numAccessControlProfileRemaining_ == 0) {
143         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
144                 IIdentityCredentialStore::STATUS_INVALID_DATA,
145                 "numAccessControlProfileRemaining_ is 0 and expected non-zero"));
146     }
147 
148     if (accessControlProfileIds_.find(id) != accessControlProfileIds_.end()) {
149         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
150                 IIdentityCredentialStore::STATUS_INVALID_DATA,
151                 "Access Control Profile id must be unique"));
152     }
153     accessControlProfileIds_.insert(id);
154 
155     if (id < 0 || id >= 32) {
156         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
157                 IIdentityCredentialStore::STATUS_INVALID_DATA,
158                 "Access Control Profile id must be non-negative and less than 32"));
159     }
160 
161     // Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also
162     // be zero.
163     if (!userAuthenticationRequired && timeoutMillis != 0) {
164         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
165                 IIdentityCredentialStore::STATUS_INVALID_DATA,
166                 "userAuthenticationRequired is false but timeout is non-zero"));
167     }
168 
169     optional<vector<uint8_t>> mac = hwProxy_->addAccessControlProfile(
170             id, readerCertificate.encodedCertificate, userAuthenticationRequired, timeoutMillis,
171             secureUserId);
172     if (!mac) {
173         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
174                 IIdentityCredentialStore::STATUS_FAILED, "eicAddAccessControlProfile"));
175     }
176 
177     SecureAccessControlProfile profile;
178     profile.id = id;
179     profile.readerCertificate = readerCertificate;
180     profile.userAuthenticationRequired = userAuthenticationRequired;
181     profile.timeoutMillis = timeoutMillis;
182     profile.secureUserId = secureUserId;
183     profile.mac = mac.value();
184     cppbor::Map profileMap;
185     profileMap.add("id", profile.id);
186     if (profile.readerCertificate.encodedCertificate.size() > 0) {
187         profileMap.add("readerCertificate",
188                        cppbor::Bstr(profile.readerCertificate.encodedCertificate));
189     }
190     if (profile.userAuthenticationRequired) {
191         profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired);
192         profileMap.add("timeoutMillis", profile.timeoutMillis);
193     }
194     signedDataAccessControlProfiles_.add(std::move(profileMap));
195 
196     numAccessControlProfileRemaining_--;
197 
198     *outSecureAccessControlProfile = profile;
199     return ndk::ScopedAStatus::ok();
200 }
201 
beginAddEntry(const vector<int32_t> & accessControlProfileIds,const string & nameSpace,const string & name,int32_t entrySize)202 ndk::ScopedAStatus WritableIdentityCredential::beginAddEntry(
203         const vector<int32_t>& accessControlProfileIds, const string& nameSpace, const string& name,
204         int32_t entrySize) {
205     if (numAccessControlProfileRemaining_ != 0) {
206         LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_
207                    << " and expected zero";
208         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
209                 IIdentityCredentialStore::STATUS_INVALID_DATA,
210                 "numAccessControlProfileRemaining_ is not zero"));
211     }
212 
213     // Ensure passed-in profile ids reference valid access control profiles
214     for (const int32_t id : accessControlProfileIds) {
215         if (accessControlProfileIds_.find(id) == accessControlProfileIds_.end()) {
216             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
217                     IIdentityCredentialStore::STATUS_INVALID_DATA,
218                     "An id in accessControlProfileIds references non-existing ACP"));
219         }
220     }
221 
222     if (remainingEntryCounts_.size() == 0) {
223         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
224                 IIdentityCredentialStore::STATUS_INVALID_DATA, "No more namespaces to add to"));
225     }
226 
227     // Handle initial beginEntry() call.
228     if (firstEntry_) {
229         firstEntry_ = false;
230         entryNameSpace_ = nameSpace;
231         allNameSpaces_.insert(nameSpace);
232     }
233 
234     // If the namespace changed...
235     if (nameSpace != entryNameSpace_) {
236         if (allNameSpaces_.find(nameSpace) != allNameSpaces_.end()) {
237             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
238                     IIdentityCredentialStore::STATUS_INVALID_DATA,
239                     "Name space cannot be added in interleaving fashion"));
240         }
241 
242         // Then check that all entries in the previous namespace have been added..
243         if (remainingEntryCounts_[0] != 0) {
244             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
245                     IIdentityCredentialStore::STATUS_INVALID_DATA,
246                     "New namespace but a non-zero number of entries remain to be added"));
247         }
248         remainingEntryCounts_.erase(remainingEntryCounts_.begin());
249         remainingEntryCounts_[0] -= 1;
250         allNameSpaces_.insert(nameSpace);
251 
252         if (signedDataCurrentNamespace_.size() > 0) {
253             signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
254             signedDataCurrentNamespace_ = cppbor::Array();
255         }
256     } else {
257         // Same namespace...
258         if (remainingEntryCounts_[0] == 0) {
259             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
260                     IIdentityCredentialStore::STATUS_INVALID_DATA,
261                     "Same namespace but no entries remain to be added"));
262         }
263         remainingEntryCounts_[0] -= 1;
264     }
265 
266     entryRemainingBytes_ = entrySize;
267     entryNameSpace_ = nameSpace;
268     entryName_ = name;
269     entryAccessControlProfileIds_ = accessControlProfileIds;
270     entryBytes_.resize(0);
271     // LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
272 
273     if (!hwProxy_->beginAddEntry(accessControlProfileIds, nameSpace, name, entrySize)) {
274         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
275                 IIdentityCredentialStore::STATUS_FAILED, "eicBeginAddEntry"));
276     }
277 
278     return ndk::ScopedAStatus::ok();
279 }
280 
addEntryValue(const vector<uint8_t> & content,vector<uint8_t> * outEncryptedContent)281 ndk::ScopedAStatus WritableIdentityCredential::addEntryValue(const vector<uint8_t>& content,
282                                                              vector<uint8_t>* outEncryptedContent) {
283     size_t contentSize = content.size();
284 
285     if (contentSize > IdentityCredentialStore::kGcmChunkSize) {
286         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
287                 IIdentityCredentialStore::STATUS_INVALID_DATA,
288                 "Passed in chunk of is bigger than kGcmChunkSize"));
289     }
290     if (contentSize > entryRemainingBytes_) {
291         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
292                 IIdentityCredentialStore::STATUS_INVALID_DATA,
293                 "Passed in chunk is bigger than remaining space"));
294     }
295 
296     entryBytes_.insert(entryBytes_.end(), content.begin(), content.end());
297     entryRemainingBytes_ -= contentSize;
298     if (entryRemainingBytes_ > 0) {
299         if (contentSize != IdentityCredentialStore::kGcmChunkSize) {
300             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
301                     IIdentityCredentialStore::STATUS_INVALID_DATA,
302                     "Retrieved non-final chunk which isn't kGcmChunkSize"));
303         }
304     }
305 
306     optional<vector<uint8_t>> encryptedContent = hwProxy_->addEntryValue(
307             entryAccessControlProfileIds_, entryNameSpace_, entryName_, content);
308     if (!encryptedContent) {
309         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
310                 IIdentityCredentialStore::STATUS_FAILED, "eicAddEntryValue"));
311     }
312 
313     if (entryRemainingBytes_ == 0) {
314         // TODO: ideally do do this without parsing the data (but still validate data is valid
315         // CBOR).
316         auto [item, _, message] = cppbor::parse(entryBytes_);
317         if (item == nullptr) {
318             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
319                     IIdentityCredentialStore::STATUS_INVALID_DATA, "Data is not valid CBOR"));
320         }
321         cppbor::Map entryMap;
322         entryMap.add("name", entryName_);
323         entryMap.add("value", std::move(item));
324         cppbor::Array profileIdArray;
325         for (auto id : entryAccessControlProfileIds_) {
326             profileIdArray.add(id);
327         }
328         entryMap.add("accessControlProfiles", std::move(profileIdArray));
329         signedDataCurrentNamespace_.add(std::move(entryMap));
330     }
331 
332     *outEncryptedContent = encryptedContent.value();
333     return ndk::ScopedAStatus::ok();
334 }
335 
finishAddingEntries(vector<uint8_t> * outCredentialData,vector<uint8_t> * outProofOfProvisioningSignature)336 ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries(
337         vector<uint8_t>* outCredentialData, vector<uint8_t>* outProofOfProvisioningSignature) {
338     if (numAccessControlProfileRemaining_ != 0) {
339         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
340                 IIdentityCredentialStore::STATUS_INVALID_DATA,
341                 "numAccessControlProfileRemaining_ is not 0 and expected zero"));
342     }
343 
344     if (remainingEntryCounts_.size() > 1 || remainingEntryCounts_[0] != 0) {
345         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
346                 IIdentityCredentialStore::STATUS_INVALID_DATA,
347                 "More entry spaces remain than startPersonalization configured"));
348     }
349 
350     if (signedDataCurrentNamespace_.size() > 0) {
351         signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
352     }
353     cppbor::Array popArray;
354     popArray.add("ProofOfProvisioning")
355             .add(docType_)
356             .add(std::move(signedDataAccessControlProfiles_))
357             .add(std::move(signedDataNamespaces_))
358             .add(testCredential_);
359     vector<uint8_t> encodedCbor = popArray.encode();
360 
361     if (encodedCbor.size() != expectedProofOfProvisioningSize_) {
362         LOG(ERROR) << "CBOR for proofOfProvisioning is " << encodedCbor.size() << " bytes, "
363                    << "was expecting " << expectedProofOfProvisioningSize_;
364         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
365                 IIdentityCredentialStore::STATUS_INVALID_DATA,
366                 StringPrintf("Unexpected CBOR size %zd for proofOfProvisioning, was expecting %zd",
367                              encodedCbor.size(), expectedProofOfProvisioningSize_)
368                         .c_str()));
369     }
370 
371     optional<vector<uint8_t>> signatureOfToBeSigned = hwProxy_->finishAddingEntries();
372     if (!signatureOfToBeSigned) {
373         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
374                 IIdentityCredentialStore::STATUS_FAILED, "eicFinishAddingEntries"));
375     }
376 
377     optional<vector<uint8_t>> signature =
378             support::coseSignEcDsaWithSignature(signatureOfToBeSigned.value(),
379                                                 encodedCbor,  // data
380                                                 {});          // certificateChain
381     if (!signature) {
382         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
383                 IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
384     }
385 
386     optional<vector<uint8_t>> encryptedCredentialKeys = hwProxy_->finishGetCredentialData(docType_);
387     if (!encryptedCredentialKeys) {
388         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
389                 IIdentityCredentialStore::STATUS_FAILED,
390                 "Error generating encrypted CredentialKeys"));
391     }
392     cppbor::Array array;
393     array.add(docType_);
394     array.add(testCredential_);
395     array.add(encryptedCredentialKeys.value());
396     vector<uint8_t> credentialData = array.encode();
397 
398     *outCredentialData = credentialData;
399     *outProofOfProvisioningSignature = signature.value();
400     hwProxy_->shutdown();
401 
402     return ndk::ScopedAStatus::ok();
403 }
404 
405 }  // namespace aidl::android::hardware::identity
406