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 "IdentityCredential"
18 
19 #include "IdentityCredential.h"
20 #include "IdentityCredentialStore.h"
21 
22 #include <android/hardware/identity/support/IdentityCredentialSupport.h>
23 
24 #include <string.h>
25 
26 #include <android-base/logging.h>
27 #include <android-base/stringprintf.h>
28 
29 #include <cppbor.h>
30 #include <cppbor_parse.h>
31 
32 #include "SecureHardwareProxy.h"
33 #include "WritableIdentityCredential.h"
34 
35 namespace aidl::android::hardware::identity {
36 
37 using ::aidl::android::hardware::keymaster::Timestamp;
38 using ::android::base::StringPrintf;
39 using ::std::optional;
40 
41 using namespace ::android::hardware::identity;
42 
initialize()43 int IdentityCredential::initialize() {
44   if (credentialData_.size() == 0) {
45     LOG(ERROR) << "CredentialData is empty";
46     return IIdentityCredentialStore::STATUS_INVALID_DATA;
47   }
48   auto [item, _, message] = cppbor::parse(credentialData_);
49   if (item == nullptr) {
50     LOG(ERROR) << "CredentialData is not valid CBOR: " << message;
51     return IIdentityCredentialStore::STATUS_INVALID_DATA;
52   }
53 
54   const cppbor::Array* arrayItem = item->asArray();
55   if (arrayItem == nullptr || arrayItem->size() != 3) {
56     LOG(ERROR) << "CredentialData is not an array with three elements";
57     return IIdentityCredentialStore::STATUS_INVALID_DATA;
58   }
59 
60   const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
61   const cppbor::Bool* testCredentialItem =
62       ((*arrayItem)[1]->asSimple() != nullptr
63            ? ((*arrayItem)[1]->asSimple()->asBool())
64            : nullptr);
65   const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
66   if (docTypeItem == nullptr || testCredentialItem == nullptr ||
67       encryptedCredentialKeysItem == nullptr) {
68     LOG(ERROR) << "CredentialData unexpected item types";
69     return IIdentityCredentialStore::STATUS_INVALID_DATA;
70   }
71 
72   docType_ = docTypeItem->value();
73   testCredential_ = testCredentialItem->value();
74 
75   encryptedCredentialKeys_ = encryptedCredentialKeysItem->value();
76   if (!hwProxy_->initialize(testCredential_, docType_,
77                             encryptedCredentialKeys_)) {
78     LOG(ERROR) << "hwProxy->initialize failed";
79     return false;
80   }
81 
82   return IIdentityCredentialStore::STATUS_OK;
83 }
84 
deleteCredential(vector<uint8_t> * outProofOfDeletionSignature)85 ndk::ScopedAStatus IdentityCredential::deleteCredential(
86     vector<uint8_t>* outProofOfDeletionSignature) {
87   return deleteCredentialCommon({}, false, outProofOfDeletionSignature);
88 }
89 
deleteCredentialWithChallenge(const vector<uint8_t> & challenge,vector<uint8_t> * outProofOfDeletionSignature)90 ndk::ScopedAStatus IdentityCredential::deleteCredentialWithChallenge(
91     const vector<uint8_t>& challenge,
92     vector<uint8_t>* outProofOfDeletionSignature) {
93   return deleteCredentialCommon(challenge, true, outProofOfDeletionSignature);
94 }
95 
deleteCredentialCommon(const vector<uint8_t> & challenge,bool includeChallenge,vector<uint8_t> * outProofOfDeletionSignature)96 ndk::ScopedAStatus IdentityCredential::deleteCredentialCommon(
97     const vector<uint8_t>& challenge, bool includeChallenge,
98     vector<uint8_t>* outProofOfDeletionSignature) {
99   if (challenge.size() > 32) {
100     return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
101         IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge too big"));
102   }
103 
104   cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_};
105   if (includeChallenge) {
106     array = {"ProofOfDeletion", docType_, challenge, testCredential_};
107   }
108 
109   vector<uint8_t> proofOfDeletionCbor = array.encode();
110   vector<uint8_t> podDigest = support::sha256(proofOfDeletionCbor);
111 
112   optional<vector<uint8_t>> signatureOfToBeSigned = hwProxy_->deleteCredential(
113       docType_, challenge, includeChallenge, proofOfDeletionCbor.size());
114   if (!signatureOfToBeSigned) {
115     return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
116         IIdentityCredentialStore::STATUS_FAILED,
117         "Error signing ProofOfDeletion"));
118   }
119 
120   optional<vector<uint8_t>> signature =
121       support::coseSignEcDsaWithSignature(signatureOfToBeSigned.value(),
122                                           proofOfDeletionCbor,  // data
123                                           {});  // certificateChain
124   if (!signature) {
125     return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
126         IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
127   }
128 
129   *outProofOfDeletionSignature = signature.value();
130   return ndk::ScopedAStatus::ok();
131 }
132 
proveOwnership(const vector<uint8_t> & challenge,vector<uint8_t> * outProofOfOwnershipSignature)133 ndk::ScopedAStatus IdentityCredential::proveOwnership(
134     const vector<uint8_t>& challenge,
135     vector<uint8_t>* outProofOfOwnershipSignature) {
136   if (challenge.size() > 32) {
137     return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
138         IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge too big"));
139   }
140 
141   cppbor::Array array;
142   array = {"ProofOfOwnership", docType_, challenge, testCredential_};
143   vector<uint8_t> proofOfOwnershipCbor = array.encode();
144   vector<uint8_t> podDigest = support::sha256(proofOfOwnershipCbor);
145 
146   optional<vector<uint8_t>> signatureOfToBeSigned = hwProxy_->proveOwnership(
147       docType_, testCredential_, challenge, proofOfOwnershipCbor.size());
148   if (!signatureOfToBeSigned) {
149     return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
150         IIdentityCredentialStore::STATUS_FAILED,
151         "Error signing ProofOfOwnership"));
152   }
153 
154   optional<vector<uint8_t>> signature =
155       support::coseSignEcDsaWithSignature(signatureOfToBeSigned.value(),
156                                           proofOfOwnershipCbor,  // data
157                                           {});  // certificateChain
158   if (!signature) {
159     return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
160         IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
161   }
162 
163   *outProofOfOwnershipSignature = signature.value();
164   return ndk::ScopedAStatus::ok();
165 }
166 
createEphemeralKeyPair(vector<uint8_t> * outKeyPair)167 ndk::ScopedAStatus IdentityCredential::createEphemeralKeyPair(
168     vector<uint8_t>* outKeyPair) {
169   optional<vector<uint8_t>> ephemeralPriv = hwProxy_->createEphemeralKeyPair();
170   if (!ephemeralPriv) {
171     return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
172         IIdentityCredentialStore::STATUS_FAILED,
173         "Error creating ephemeral key"));
174   }
175   optional<vector<uint8_t>> keyPair =
176       support::ecPrivateKeyToKeyPair(ephemeralPriv.value());
177   if (!keyPair) {
178     return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
179         IIdentityCredentialStore::STATUS_FAILED,
180         "Error creating ephemeral key-pair"));
181   }
182 
183   // Stash public key of this key-pair for later check in startRetrieval().
184   optional<vector<uint8_t>> publicKey =
185       support::ecKeyPairGetPublicKey(keyPair.value());
186   if (!publicKey) {
187     LOG(ERROR) << "Error getting public part of ephemeral key pair";
188     return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
189         IIdentityCredentialStore::STATUS_FAILED,
190         "Error getting public part of ephemeral key pair"));
191   }
192   ephemeralPublicKey_ = publicKey.value();
193 
194   *outKeyPair = keyPair.value();
195   return ndk::ScopedAStatus::ok();
196 }
197 
setReaderEphemeralPublicKey(const vector<uint8_t> & publicKey)198 ndk::ScopedAStatus IdentityCredential::setReaderEphemeralPublicKey(
199     const vector<uint8_t>& publicKey) {
200   readerPublicKey_ = publicKey;
201   return ndk::ScopedAStatus::ok();
202 }
203 
createAuthChallenge(int64_t * outChallenge)204 ndk::ScopedAStatus IdentityCredential::createAuthChallenge(
205     int64_t* outChallenge) {
206   optional<uint64_t> challenge = hwProxy_->createAuthChallenge();
207   if (!challenge) {
208     return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
209         IIdentityCredentialStore::STATUS_FAILED, "Error generating challenge"));
210   }
211   *outChallenge = challenge.value();
212   return ndk::ScopedAStatus::ok();
213 }
214 
setRequestedNamespaces(const vector<RequestNamespace> & requestNamespaces)215 ndk::ScopedAStatus IdentityCredential::setRequestedNamespaces(
216     const vector<RequestNamespace>& requestNamespaces) {
217   requestNamespaces_ = requestNamespaces;
218   return ndk::ScopedAStatus::ok();
219 }
220 
setVerificationToken(const VerificationToken & verificationToken)221 ndk::ScopedAStatus IdentityCredential::setVerificationToken(
222     const VerificationToken& verificationToken) {
223   verificationToken_ = verificationToken;
224   return ndk::ScopedAStatus::ok();
225 }
226 
startRetrieval(const vector<SecureAccessControlProfile> & accessControlProfiles,const HardwareAuthToken & authToken,const vector<uint8_t> & itemsRequest,const vector<uint8_t> & signingKeyBlob,const vector<uint8_t> & sessionTranscript,const vector<uint8_t> & readerSignature,const vector<int32_t> & requestCounts)227 ndk::ScopedAStatus IdentityCredential::startRetrieval(
228     const vector<SecureAccessControlProfile>& accessControlProfiles,
229     const HardwareAuthToken& authToken, const vector<uint8_t>& itemsRequest,
230     const vector<uint8_t>& signingKeyBlob,
231     const vector<uint8_t>& sessionTranscript,
232     const vector<uint8_t>& readerSignature,
233     const vector<int32_t>& requestCounts) {
234   std::unique_ptr<cppbor::Item> sessionTranscriptItem;
235   if (sessionTranscript.size() > 0) {
236     auto [item, _, message] = cppbor::parse(sessionTranscript);
237     if (item == nullptr) {
238       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
239           IIdentityCredentialStore::STATUS_INVALID_DATA,
240           "SessionTranscript contains invalid CBOR"));
241     }
242     sessionTranscriptItem = std::move(item);
243   }
244   if (numStartRetrievalCalls_ > 0) {
245     if (sessionTranscript_ != sessionTranscript) {
246       LOG(ERROR) << "Session Transcript changed";
247       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
248           IIdentityCredentialStore::STATUS_SESSION_TRANSCRIPT_MISMATCH,
249           "Passed-in SessionTranscript doesn't match previously used "
250           "SessionTranscript"));
251     }
252   }
253   sessionTranscript_ = sessionTranscript;
254 
255   // This resets various state in the TA...
256   if (!hwProxy_->startRetrieveEntries()) {
257     return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
258         IIdentityCredentialStore::STATUS_FAILED,
259         "Error starting retrieving entries"));
260   }
261 
262   optional<vector<uint8_t>> signatureOfToBeSigned;
263   if (readerSignature.size() > 0) {
264     signatureOfToBeSigned = support::coseSignGetSignature(readerSignature);
265     if (!signatureOfToBeSigned) {
266       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
267           IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
268           "Error extracting signatureOfToBeSigned from COSE_Sign1"));
269     }
270   }
271 
272   // Feed the auth token to secure hardware only if they're valid.
273   if (authToken.timestamp.milliSeconds != 0) {
274     if (!hwProxy_->setAuthToken(
275             authToken.challenge, authToken.userId, authToken.authenticatorId,
276             int(authToken.authenticatorType), authToken.timestamp.milliSeconds,
277             authToken.mac, verificationToken_.challenge,
278             verificationToken_.timestamp.milliSeconds,
279             int(verificationToken_.securityLevel), verificationToken_.mac)) {
280       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
281           IIdentityCredentialStore::STATUS_INVALID_DATA, "Invalid Auth Token"));
282     }
283   }
284 
285   // We'll be feeding ACPs interleaved with certificates from the reader
286   // certificate chain...
287   vector<SecureAccessControlProfile> remainingAcps = accessControlProfiles;
288 
289   // ... and we'll use those ACPs to build up a 32-bit mask indicating which
290   // of the possible 32 ACPs grants access.
291   uint32_t accessControlProfileMask = 0;
292 
293   // If there is a signature, validate that it was made with the top-most key in
294   // the certificate chain embedded in the COSE_Sign1 structure.
295   optional<vector<uint8_t>> readerCertificateChain;
296   if (readerSignature.size() > 0) {
297     readerCertificateChain = support::coseSignGetX5Chain(readerSignature);
298     if (!readerCertificateChain) {
299       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
300           IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
301           "Unable to get reader certificate chain from COSE_Sign1"));
302     }
303 
304     // First, feed all the reader certificates to the secure hardware. We start
305     // at the end..
306     optional<vector<vector<uint8_t>>> splitCerts =
307         support::certificateChainSplit(readerCertificateChain.value());
308     if (!splitCerts || splitCerts.value().size() == 0) {
309       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
310           IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
311           "Error splitting certificate chain from COSE_Sign1"));
312     }
313     for (ssize_t n = splitCerts.value().size() - 1; n >= 0; --n) {
314       const vector<uint8_t>& x509Cert = splitCerts.value()[n];
315       if (!hwProxy_->pushReaderCert(x509Cert)) {
316         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
317             IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
318             StringPrintf("Error validating reader certificate %zd", n)
319                 .c_str()));
320       }
321 
322       // If we have ACPs for that particular certificate, send them to the
323       // TA right now...
324       //
325       // Remember in this case certificate equality is done by comparing public
326       // keys, not bitwise comparison of the certificates.
327       //
328       optional<vector<uint8_t>> x509CertPubKey =
329           support::certificateChainGetTopMostKey(x509Cert);
330       if (!x509CertPubKey) {
331         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
332             IIdentityCredentialStore::STATUS_FAILED,
333             StringPrintf("Error getting public key from reader certificate %zd",
334                          n)
335                 .c_str()));
336       }
337       vector<SecureAccessControlProfile>::iterator it = remainingAcps.begin();
338       while (it != remainingAcps.end()) {
339         const SecureAccessControlProfile& profile = *it;
340         if (profile.readerCertificate.encodedCertificate.size() == 0) {
341           ++it;
342           continue;
343         }
344         optional<vector<uint8_t>> profilePubKey =
345             support::certificateChainGetTopMostKey(
346                 profile.readerCertificate.encodedCertificate);
347         if (!profilePubKey) {
348           return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
349               IIdentityCredentialStore::STATUS_FAILED,
350               "Error getting public key from profile"));
351         }
352         if (profilePubKey.value() == x509CertPubKey.value()) {
353           optional<bool> res = hwProxy_->validateAccessControlProfile(
354               profile.id, profile.readerCertificate.encodedCertificate,
355               profile.userAuthenticationRequired, profile.timeoutMillis,
356               profile.secureUserId, profile.mac);
357           if (!res) {
358             return ndk::ScopedAStatus(
359                 AStatus_fromServiceSpecificErrorWithMessage(
360                     IIdentityCredentialStore::STATUS_INVALID_DATA,
361                     "Error validating access control profile"));
362           }
363           if (res.value()) {
364             accessControlProfileMask |= (1 << profile.id);
365           }
366           it = remainingAcps.erase(it);
367         } else {
368           ++it;
369         }
370       }
371     }
372 
373     // ... then pass the request message and have the TA check it's signed by
374     // the key in last certificate we pushed.
375     if (sessionTranscript.size() > 0 && itemsRequest.size() > 0 &&
376         readerSignature.size() > 0) {
377       optional<vector<uint8_t>> tbsSignature =
378           support::coseSignGetSignature(readerSignature);
379       if (!tbsSignature) {
380         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
381             IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
382             "Error extracting toBeSigned from COSE_Sign1"));
383       }
384       optional<int> coseSignAlg = support::coseSignGetAlg(readerSignature);
385       if (!coseSignAlg) {
386         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
387             IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
388             "Error extracting signature algorithm from COSE_Sign1"));
389       }
390       if (!hwProxy_->validateRequestMessage(sessionTranscript, itemsRequest,
391                                             coseSignAlg.value(),
392                                             tbsSignature.value())) {
393         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
394             IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
395             "readerMessage is not signed by top-level certificate"));
396       }
397     }
398   }
399 
400   // Feed remaining access control profiles...
401   for (const SecureAccessControlProfile& profile : remainingAcps) {
402     optional<bool> res = hwProxy_->validateAccessControlProfile(
403         profile.id, profile.readerCertificate.encodedCertificate,
404         profile.userAuthenticationRequired, profile.timeoutMillis,
405         profile.secureUserId, profile.mac);
406     if (!res) {
407       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
408           IIdentityCredentialStore::STATUS_INVALID_DATA,
409           "Error validating access control profile"));
410     }
411     if (res.value()) {
412       accessControlProfileMask |= (1 << profile.id);
413     }
414   }
415 
416   // TODO: move this check to the TA
417 #if 1
418   // To prevent replay-attacks, we check that the public part of the ephemeral
419   // key we previously created, is present in the DeviceEngagement part of
420   // SessionTranscript as a COSE_Key, in uncompressed form.
421   //
422   // We do this by just searching for the X and Y coordinates.
423   if (sessionTranscript.size() > 0) {
424     auto [getXYSuccess, ePubX, ePubY] =
425         support::ecPublicKeyGetXandY(ephemeralPublicKey_);
426     if (!getXYSuccess) {
427       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
428           IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
429           "Error extracting X and Y from ePub"));
430     }
431     if (sessionTranscript.size() > 0 &&
432         !(memmem(sessionTranscript.data(), sessionTranscript.size(),
433                  ePubX.data(), ePubX.size()) != nullptr &&
434           memmem(sessionTranscript.data(), sessionTranscript.size(),
435                  ePubY.data(), ePubY.size()) != nullptr)) {
436       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
437           IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
438           "Did not find ephemeral public key's X and Y coordinates in "
439           "SessionTranscript (make sure leading zeroes are not used)"));
440     }
441   }
442 #endif
443 
444   // itemsRequest: If non-empty, contains request data that may be signed by the
445   // reader.  The content can be defined in the way appropriate for the
446   // credential, but there are three requirements that must be met to work with
447   // this HAL:
448   if (itemsRequest.size() > 0) {
449     // 1. The content must be a CBOR-encoded structure.
450     auto [item, _, message] = cppbor::parse(itemsRequest);
451     if (item == nullptr) {
452       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
453           IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
454           "Error decoding CBOR in itemsRequest"));
455     }
456 
457     // 2. The CBOR structure must be a map.
458     const cppbor::Map* map = item->asMap();
459     if (map == nullptr) {
460       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
461           IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
462           "itemsRequest is not a CBOR map"));
463     }
464 
465     // 3. The map must contain a key "nameSpaces" whose value contains a map, as
466     // described in
467     //    the example below.
468     //
469     //   NameSpaces = {
470     //     + NameSpace => DataElements ; Requested data elements for each
471     //     NameSpace
472     //   }
473     //
474     //   NameSpace = tstr
475     //
476     //   DataElements = {
477     //     + DataElement => IntentToRetain
478     //   }
479     //
480     //   DataElement = tstr
481     //   IntentToRetain = bool
482     //
483     // Here's an example of an |itemsRequest| CBOR value satisfying above
484     // requirements 1. through 3.:
485     //
486     //    {
487     //        'docType' : 'org.iso.18013-5.2019',
488     //        'nameSpaces' : {
489     //            'org.iso.18013-5.2019' : {
490     //                'Last name' : false,
491     //                'Birth date' : false,
492     //                'First name' : false,
493     //                'Home address' : true
494     //            },
495     //            'org.aamva.iso.18013-5.2019' : {
496     //                'Real Id' : false
497     //            }
498     //        }
499     //    }
500     //
501     const cppbor::Map* nsMap = nullptr;
502     for (size_t n = 0; n < map->size(); n++) {
503       const auto& [keyItem, valueItem] = (*map)[n];
504       if (keyItem->type() == cppbor::TSTR &&
505           keyItem->asTstr()->value() == "nameSpaces" &&
506           valueItem->type() == cppbor::MAP) {
507         nsMap = valueItem->asMap();
508         break;
509       }
510     }
511     if (nsMap == nullptr) {
512       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
513           IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
514           "No nameSpaces map in top-most map"));
515     }
516 
517     for (size_t n = 0; n < nsMap->size(); n++) {
518       auto& [nsKeyItem, nsValueItem] = (*nsMap)[n];
519       const cppbor::Tstr* nsKey = nsKeyItem->asTstr();
520       const cppbor::Map* nsInnerMap = nsValueItem->asMap();
521       if (nsKey == nullptr || nsInnerMap == nullptr) {
522         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
523             IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
524             "Type mismatch in nameSpaces map"));
525       }
526       string requestedNamespace = nsKey->value();
527       set<string> requestedKeys;
528       for (size_t m = 0; m < nsInnerMap->size(); m++) {
529         const auto& [innerMapKeyItem, innerMapValueItem] = (*nsInnerMap)[m];
530         const cppbor::Tstr* nameItem = innerMapKeyItem->asTstr();
531         const cppbor::Simple* simple = innerMapValueItem->asSimple();
532         const cppbor::Bool* intentToRetainItem =
533             (simple != nullptr) ? simple->asBool() : nullptr;
534         if (nameItem == nullptr || intentToRetainItem == nullptr) {
535           return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
536               IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
537               "Type mismatch in value in nameSpaces map"));
538         }
539         requestedKeys.insert(nameItem->value());
540       }
541       requestedNameSpacesAndNames_[requestedNamespace] = requestedKeys;
542     }
543   }
544 
545   deviceNameSpacesMap_ = cppbor::Map();
546   currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
547 
548   requestCountsRemaining_ = requestCounts;
549   currentNameSpace_ = "";
550 
551   itemsRequest_ = itemsRequest;
552   signingKeyBlob_ = signingKeyBlob;
553 
554   // calculate the size of DeviceNameSpaces. We need to know it ahead of time.
555   calcDeviceNameSpacesSize(accessControlProfileMask);
556 
557   // Count the number of non-empty namespaces
558   size_t numNamespacesWithValues = 0;
559   for (size_t n = 0; n < expectedNumEntriesPerNamespace_.size(); n++) {
560     if (expectedNumEntriesPerNamespace_[n] > 0) {
561       numNamespacesWithValues += 1;
562     }
563   }
564 
565   // Finally, pass info so the HMAC key can be derived and the TA can start
566   // creating the DeviceNameSpaces CBOR...
567   if (sessionTranscript_.size() > 0 && readerPublicKey_.size() > 0 &&
568       signingKeyBlob.size() > 0) {
569     // We expect the reader ephemeral public key to be same size and curve
570     // as the ephemeral key we generated (e.g. P-256 key), otherwise ECDH
571     // won't work. So its length should be 65 bytes and it should be
572     // starting with 0x04.
573     if (readerPublicKey_.size() != 65 || readerPublicKey_[0] != 0x04) {
574       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
575           IIdentityCredentialStore::STATUS_FAILED,
576           "Reader public key is not in expected format"));
577     }
578     vector<uint8_t> pubKeyP256(readerPublicKey_.begin() + 1,
579                                readerPublicKey_.end());
580     if (!hwProxy_->calcMacKey(sessionTranscript_, pubKeyP256, signingKeyBlob,
581                               docType_, numNamespacesWithValues,
582                               expectedDeviceNameSpacesSize_)) {
583       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
584           IIdentityCredentialStore::STATUS_FAILED,
585           "Error starting retrieving entries"));
586     }
587   }
588 
589   numStartRetrievalCalls_ += 1;
590   return ndk::ScopedAStatus::ok();
591 }
592 
cborNumBytesForLength(size_t length)593 size_t cborNumBytesForLength(size_t length) {
594   if (length < 24) {
595     return 0;
596   } else if (length <= 0xff) {
597     return 1;
598   } else if (length <= 0xffff) {
599     return 2;
600   } else if (length <= 0xffffffff) {
601     return 4;
602   }
603   return 8;
604 }
605 
cborNumBytesForTstr(const string & value)606 size_t cborNumBytesForTstr(const string& value) {
607   return 1 + cborNumBytesForLength(value.size()) + value.size();
608 }
609 
calcDeviceNameSpacesSize(uint32_t accessControlProfileMask)610 void IdentityCredential::calcDeviceNameSpacesSize(
611     uint32_t accessControlProfileMask) {
612   /*
613    * This is how DeviceNameSpaces is defined:
614    *
615    *        DeviceNameSpaces = {
616    *            * NameSpace => DeviceSignedItems
617    *        }
618    *        DeviceSignedItems = {
619    *            + DataItemName => DataItemValue
620    *        }
621    *
622    *        Namespace = tstr
623    *        DataItemName = tstr
624    *        DataItemValue = any
625    *
626    * This function will calculate its length using knowledge of how CBOR is
627    * encoded.
628    */
629   size_t ret = 0;
630   vector<unsigned int> numEntriesPerNamespace;
631   for (const RequestNamespace& rns : requestNamespaces_) {
632     vector<RequestDataItem> itemsToInclude;
633 
634     for (const RequestDataItem& rdi : rns.items) {
635       // If we have a CBOR request message, skip if item isn't in it
636       if (itemsRequest_.size() > 0) {
637         const auto& it = requestedNameSpacesAndNames_.find(rns.namespaceName);
638         if (it == requestedNameSpacesAndNames_.end()) {
639           continue;
640         }
641         const set<string>& dataItemNames = it->second;
642         if (dataItemNames.find(rdi.name) == dataItemNames.end()) {
643           continue;
644         }
645       }
646 
647       // Access is granted if at least one of the profiles grants access.
648       //
649       // If an item is configured without any profiles, access is denied.
650       //
651       bool authorized = false;
652       for (auto id : rdi.accessControlProfileIds) {
653         if (accessControlProfileMask & (1 << id)) {
654           authorized = true;
655           break;
656         }
657       }
658       if (!authorized) {
659         continue;
660       }
661 
662       itemsToInclude.push_back(rdi);
663     }
664 
665     numEntriesPerNamespace.push_back(itemsToInclude.size());
666 
667     // If no entries are to be in the namespace, we don't include it in
668     // the CBOR...
669     if (itemsToInclude.size() == 0) {
670       continue;
671     }
672 
673     // Key: NameSpace
674     ret += cborNumBytesForTstr(rns.namespaceName);
675 
676     // Value: Open the DeviceSignedItems map
677     ret += 1 + cborNumBytesForLength(itemsToInclude.size());
678 
679     for (const RequestDataItem& item : itemsToInclude) {
680       // Key: DataItemName
681       ret += cborNumBytesForTstr(item.name);
682 
683       // Value: DataItemValue - entryData.size is the length of serialized CBOR
684       // so we use that.
685       ret += item.size;
686     }
687   }
688 
689   // Now that we know the number of namespaces with values, we know how many
690   // bytes the DeviceNamespaces map in the beginning is going to take up.
691   ret += 1 + cborNumBytesForLength(numEntriesPerNamespace.size());
692 
693   expectedDeviceNameSpacesSize_ = ret;
694   expectedNumEntriesPerNamespace_ = numEntriesPerNamespace;
695 }
696 
startRetrieveEntryValue(const string & nameSpace,const string & name,int32_t entrySize,const vector<int32_t> & accessControlProfileIds)697 ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue(
698     const string& nameSpace, const string& name, int32_t entrySize,
699     const vector<int32_t>& accessControlProfileIds) {
700   if (name.empty()) {
701     return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
702         IIdentityCredentialStore::STATUS_INVALID_DATA, "Name cannot be empty"));
703   }
704   if (nameSpace.empty()) {
705     return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
706         IIdentityCredentialStore::STATUS_INVALID_DATA,
707         "Name space cannot be empty"));
708   }
709 
710   if (requestCountsRemaining_.size() == 0) {
711     return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
712         IIdentityCredentialStore::STATUS_INVALID_DATA,
713         "No more name spaces left to go through"));
714   }
715 
716   bool newNamespace;
717   if (currentNameSpace_ == "") {
718     // First call.
719     currentNameSpace_ = nameSpace;
720     newNamespace = true;
721   }
722 
723   if (nameSpace == currentNameSpace_) {
724     // Same namespace.
725     if (requestCountsRemaining_[0] == 0) {
726       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
727           IIdentityCredentialStore::STATUS_INVALID_DATA,
728           "No more entries to be retrieved in current name space"));
729     }
730     requestCountsRemaining_[0] -= 1;
731   } else {
732     // New namespace.
733     if (requestCountsRemaining_[0] != 0) {
734       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
735           IIdentityCredentialStore::STATUS_INVALID_DATA,
736           "Moved to new name space but one or more entries need to be "
737           "retrieved "
738           "in current name space"));
739     }
740     if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
741       deviceNameSpacesMap_.add(currentNameSpace_,
742                                std::move(currentNameSpaceDeviceNameSpacesMap_));
743     }
744     currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
745 
746     requestCountsRemaining_.erase(requestCountsRemaining_.begin());
747     currentNameSpace_ = nameSpace;
748     newNamespace = true;
749   }
750 
751   // It's permissible to have an empty itemsRequest... but if non-empty you can
752   // only request what was specified in said itemsRequest. Enforce that.
753   if (itemsRequest_.size() > 0) {
754     const auto& it = requestedNameSpacesAndNames_.find(nameSpace);
755     if (it == requestedNameSpacesAndNames_.end()) {
756       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
757           IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE,
758           "Name space was not requested in startRetrieval"));
759     }
760     const set<string>& dataItemNames = it->second;
761     if (dataItemNames.find(name) == dataItemNames.end()) {
762       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
763           IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE,
764           "Data item name in name space was not requested in startRetrieval"));
765     }
766   }
767 
768   unsigned int newNamespaceNumEntries = 0;
769   if (newNamespace) {
770     if (expectedNumEntriesPerNamespace_.size() == 0) {
771       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
772           IIdentityCredentialStore::STATUS_INVALID_DATA,
773           "No more populated name spaces left to go through"));
774     }
775     newNamespaceNumEntries = expectedNumEntriesPerNamespace_[0];
776     expectedNumEntriesPerNamespace_.erase(
777         expectedNumEntriesPerNamespace_.begin());
778   }
779 
780   // Access control is enforced in the secure hardware.
781   //
782   // ... except for STATUS_NOT_IN_REQUEST_MESSAGE, that's handled above (TODO:
783   // consolidate).
784   //
785   AccessCheckResult res =
786       hwProxy_->startRetrieveEntryValue(nameSpace, name, newNamespaceNumEntries,
787                                         entrySize, accessControlProfileIds);
788   switch (res) {
789     case AccessCheckResult::kOk:
790       /* Do nothing. */
791       break;
792     case AccessCheckResult::kFailed:
793       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
794           IIdentityCredentialStore::STATUS_FAILED,
795           "Access control check failed (failed)"));
796       break;
797     case AccessCheckResult::kNoAccessControlProfiles:
798       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
799           IIdentityCredentialStore::STATUS_NO_ACCESS_CONTROL_PROFILES,
800           "Access control check failed (no access control profiles)"));
801       break;
802     case AccessCheckResult::kUserAuthenticationFailed:
803       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
804           IIdentityCredentialStore::STATUS_USER_AUTHENTICATION_FAILED,
805           "Access control check failed (user auth)"));
806       break;
807     case AccessCheckResult::kReaderAuthenticationFailed:
808       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
809           IIdentityCredentialStore::STATUS_READER_AUTHENTICATION_FAILED,
810           "Access control check failed (reader auth)"));
811       break;
812   }
813 
814   currentName_ = name;
815   currentAccessControlProfileIds_ = accessControlProfileIds;
816   entryRemainingBytes_ = entrySize;
817   entryValue_.resize(0);
818 
819   return ndk::ScopedAStatus::ok();
820 }
821 
retrieveEntryValue(const vector<uint8_t> & encryptedContent,vector<uint8_t> * outContent)822 ndk::ScopedAStatus IdentityCredential::retrieveEntryValue(
823     const vector<uint8_t>& encryptedContent, vector<uint8_t>* outContent) {
824   optional<vector<uint8_t>> content = hwProxy_->retrieveEntryValue(
825       encryptedContent, currentNameSpace_, currentName_,
826       currentAccessControlProfileIds_);
827   if (!content) {
828     return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
829         IIdentityCredentialStore::STATUS_INVALID_DATA,
830         "Error decrypting data"));
831   }
832 
833   size_t chunkSize = content.value().size();
834 
835   if (chunkSize > entryRemainingBytes_) {
836     LOG(ERROR) << "Retrieved chunk of size " << chunkSize
837                << " is bigger than remaining space of size "
838                << entryRemainingBytes_;
839     return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
840         IIdentityCredentialStore::STATUS_INVALID_DATA,
841         "Retrieved chunk is bigger than remaining space"));
842   }
843 
844   entryRemainingBytes_ -= chunkSize;
845   if (entryRemainingBytes_ > 0) {
846     if (chunkSize != IdentityCredentialStore::kGcmChunkSize) {
847       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
848           IIdentityCredentialStore::STATUS_INVALID_DATA,
849           "Retrieved non-final chunk of size which isn't kGcmChunkSize"));
850     }
851   }
852 
853   entryValue_.insert(entryValue_.end(), content.value().begin(),
854                      content.value().end());
855 
856   if (entryRemainingBytes_ == 0) {
857     auto [entryValueItem, _, message] = cppbor::parse(entryValue_);
858     if (entryValueItem == nullptr) {
859       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
860           IIdentityCredentialStore::STATUS_INVALID_DATA,
861           "Retrieved data which is invalid CBOR"));
862     }
863     currentNameSpaceDeviceNameSpacesMap_.add(currentName_,
864                                              std::move(entryValueItem));
865   }
866 
867   *outContent = content.value();
868   return ndk::ScopedAStatus::ok();
869 }
870 
finishRetrieval(vector<uint8_t> * outMac,vector<uint8_t> * outDeviceNameSpaces)871 ndk::ScopedAStatus IdentityCredential::finishRetrieval(
872     vector<uint8_t>* outMac, vector<uint8_t>* outDeviceNameSpaces) {
873   if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
874     deviceNameSpacesMap_.add(currentNameSpace_,
875                              std::move(currentNameSpaceDeviceNameSpacesMap_));
876   }
877   vector<uint8_t> encodedDeviceNameSpaces = deviceNameSpacesMap_.encode();
878 
879   if (encodedDeviceNameSpaces.size() != expectedDeviceNameSpacesSize_) {
880     LOG(ERROR) << "encodedDeviceNameSpaces is "
881                << encodedDeviceNameSpaces.size() << " bytes, "
882                << "was expecting " << expectedDeviceNameSpacesSize_;
883     return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
884         IIdentityCredentialStore::STATUS_INVALID_DATA,
885         StringPrintf("Unexpected CBOR size %zd for encodedDeviceNameSpaces, "
886                      "was expecting %zd",
887                      encodedDeviceNameSpaces.size(),
888                      expectedDeviceNameSpacesSize_)
889             .c_str()));
890   }
891 
892   // If there's no signing key or no sessionTranscript or no reader ephemeral
893   // public key, we return the empty MAC.
894   optional<vector<uint8_t>> mac;
895   if (signingKeyBlob_.size() > 0 && sessionTranscript_.size() > 0 &&
896       readerPublicKey_.size() > 0) {
897     optional<vector<uint8_t>> digestToBeMaced = hwProxy_->finishRetrieval();
898     if (!digestToBeMaced || digestToBeMaced.value().size() != 32) {
899       return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
900           IIdentityCredentialStore::STATUS_INVALID_DATA,
901           "Error generating digestToBeMaced"));
902     }
903     // Now construct COSE_Mac0 from the returned MAC...
904     mac = support::coseMacWithDigest(digestToBeMaced.value(), {} /* data */);
905   }
906 
907   *outMac = mac.value_or(vector<uint8_t>({}));
908   *outDeviceNameSpaces = encodedDeviceNameSpaces;
909   return ndk::ScopedAStatus::ok();
910 }
911 
generateSigningKeyPair(vector<uint8_t> * outSigningKeyBlob,Certificate * outSigningKeyCertificate)912 ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair(
913     vector<uint8_t>* outSigningKeyBlob, Certificate* outSigningKeyCertificate) {
914   time_t now = time(NULL);
915   optional<pair<vector<uint8_t>, vector<uint8_t>>> pair =
916       hwProxy_->generateSigningKeyPair(docType_, now);
917   if (!pair) {
918     return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
919         IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey"));
920   }
921 
922   *outSigningKeyCertificate = Certificate();
923   outSigningKeyCertificate->encodedCertificate = pair->first;
924 
925   *outSigningKeyBlob = pair->second;
926   return ndk::ScopedAStatus::ok();
927 }
928 
updateCredential(shared_ptr<IWritableIdentityCredential> * outWritableCredential)929 ndk::ScopedAStatus IdentityCredential::updateCredential(
930     shared_ptr<IWritableIdentityCredential>* outWritableCredential) {
931   sp<SecureHardwareProvisioningProxy> hwProxy =
932       hwProxyFactory_->createProvisioningProxy();
933   shared_ptr<WritableIdentityCredential> wc =
934       ndk::SharedRefBase::make<WritableIdentityCredential>(hwProxy, docType_,
935                                                            testCredential_);
936   if (!wc->initializeForUpdate(encryptedCredentialKeys_)) {
937     return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
938         IIdentityCredentialStore::STATUS_FAILED,
939         "Error initializing WritableIdentityCredential for update"));
940   }
941   *outWritableCredential = wc;
942   return ndk::ScopedAStatus::ok();
943 }
944 
945 }  // namespace aidl::android::hardware::identity
946