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