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