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