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