blob: 94779971b10db65368dfac16ccf63668cd4ead49 [file] [log] [blame]
David Zeuthen81603152020-02-11 22:04:24 -05001/*
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"
David Zeuthen81603152020-02-11 22:04:24 -050021
22#include <android/hardware/identity/support/IdentityCredentialSupport.h>
23
24#include <string.h>
25
26#include <android-base/logging.h>
David Zeuthen28edb102020-04-28 18:54:55 -040027#include <android-base/stringprintf.h>
David Zeuthen81603152020-02-11 22:04:24 -050028
29#include <cppbor.h>
30#include <cppbor_parse.h>
31
David Zeuthen630de2a2020-05-11 14:04:54 -040032#include "FakeSecureHardwareProxy.h"
David Zeuthen49f2d252020-10-16 11:27:24 -040033#include "WritableIdentityCredential.h"
David Zeuthen630de2a2020-05-11 14:04:54 -040034
David Zeuthen81603152020-02-11 22:04:24 -050035namespace aidl::android::hardware::identity {
36
37using ::aidl::android::hardware::keymaster::Timestamp;
David Zeuthen28edb102020-04-28 18:54:55 -040038using ::android::base::StringPrintf;
David Zeuthen81603152020-02-11 22:04:24 -050039using ::std::optional;
40
41using namespace ::android::hardware::identity;
42
43int IdentityCredential::initialize() {
David Zeuthen2e4533e2020-06-20 17:04:41 -040044 if (credentialData_.size() == 0) {
45 LOG(ERROR) << "CredentialData is empty";
46 return IIdentityCredentialStore::STATUS_INVALID_DATA;
47 }
David Zeuthen81603152020-02-11 22:04:24 -050048 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
David Zeuthen49f2d252020-10-16 11:27:24 -040074 encryptedCredentialKeys_ = encryptedCredentialKeysItem->value();
75 if (!hwProxy_->initialize(testCredential_, docType_, encryptedCredentialKeys_)) {
David Zeuthen630de2a2020-05-11 14:04:54 -040076 LOG(ERROR) << "hwProxy->initialize failed";
77 return false;
David Zeuthen81603152020-02-11 22:04:24 -050078 }
David Zeuthen81603152020-02-11 22:04:24 -050079
80 return IIdentityCredentialStore::STATUS_OK;
81}
82
83ndk::ScopedAStatus IdentityCredential::deleteCredential(
Jooyung Han17be89b2020-02-21 21:17:06 +090084 vector<uint8_t>* outProofOfDeletionSignature) {
David Zeuthen49f2d252020-10-16 11:27:24 -040085 return deleteCredentialCommon({}, false, outProofOfDeletionSignature);
86}
87
88ndk::ScopedAStatus IdentityCredential::deleteCredentialWithChallenge(
89 const vector<uint8_t>& challenge, vector<uint8_t>* outProofOfDeletionSignature) {
90 return deleteCredentialCommon(challenge, true, outProofOfDeletionSignature);
91}
92
93ndk::ScopedAStatus IdentityCredential::deleteCredentialCommon(
94 const vector<uint8_t>& challenge, bool includeChallenge,
95 vector<uint8_t>* outProofOfDeletionSignature) {
96 if (challenge.size() > 32) {
97 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
98 IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge too big"));
99 }
100
David Zeuthen81603152020-02-11 22:04:24 -0500101 cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_};
David Zeuthen49f2d252020-10-16 11:27:24 -0400102 if (includeChallenge) {
103 array = {"ProofOfDeletion", docType_, challenge, testCredential_};
104 }
105
David Zeuthen630de2a2020-05-11 14:04:54 -0400106 vector<uint8_t> proofOfDeletionCbor = array.encode();
107 vector<uint8_t> podDigest = support::sha256(proofOfDeletionCbor);
David Zeuthen81603152020-02-11 22:04:24 -0500108
David Zeuthen49f2d252020-10-16 11:27:24 -0400109 optional<vector<uint8_t>> signatureOfToBeSigned = hwProxy_->deleteCredential(
110 docType_, challenge, includeChallenge, proofOfDeletionCbor.size());
David Zeuthen630de2a2020-05-11 14:04:54 -0400111 if (!signatureOfToBeSigned) {
112 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
113 IIdentityCredentialStore::STATUS_FAILED, "Error signing ProofOfDeletion"));
114 }
115
116 optional<vector<uint8_t>> signature =
117 support::coseSignEcDsaWithSignature(signatureOfToBeSigned.value(),
118 proofOfDeletionCbor, // data
119 {}); // certificateChain
David Zeuthen81603152020-02-11 22:04:24 -0500120 if (!signature) {
121 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
122 IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
123 }
124
Jooyung Han17be89b2020-02-21 21:17:06 +0900125 *outProofOfDeletionSignature = signature.value();
David Zeuthen81603152020-02-11 22:04:24 -0500126 return ndk::ScopedAStatus::ok();
127}
128
David Zeuthen49f2d252020-10-16 11:27:24 -0400129ndk::ScopedAStatus IdentityCredential::proveOwnership(
130 const vector<uint8_t>& challenge, vector<uint8_t>* outProofOfOwnershipSignature) {
131 if (challenge.size() > 32) {
132 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
133 IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge too big"));
134 }
135
136 cppbor::Array array;
137 array = {"ProofOfOwnership", docType_, challenge, testCredential_};
138 vector<uint8_t> proofOfOwnershipCbor = array.encode();
139 vector<uint8_t> podDigest = support::sha256(proofOfOwnershipCbor);
140
141 optional<vector<uint8_t>> signatureOfToBeSigned = hwProxy_->proveOwnership(
142 docType_, testCredential_, challenge, proofOfOwnershipCbor.size());
143 if (!signatureOfToBeSigned) {
144 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
145 IIdentityCredentialStore::STATUS_FAILED, "Error signing ProofOfOwnership"));
146 }
147
148 optional<vector<uint8_t>> signature =
149 support::coseSignEcDsaWithSignature(signatureOfToBeSigned.value(),
150 proofOfOwnershipCbor, // data
151 {}); // certificateChain
152 if (!signature) {
153 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
154 IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
155 }
156
157 *outProofOfOwnershipSignature = signature.value();
158 return ndk::ScopedAStatus::ok();
159}
160
Jooyung Han17be89b2020-02-21 21:17:06 +0900161ndk::ScopedAStatus IdentityCredential::createEphemeralKeyPair(vector<uint8_t>* outKeyPair) {
David Zeuthen630de2a2020-05-11 14:04:54 -0400162 optional<vector<uint8_t>> ephemeralPriv = hwProxy_->createEphemeralKeyPair();
163 if (!ephemeralPriv) {
David Zeuthen81603152020-02-11 22:04:24 -0500164 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
David Zeuthen630de2a2020-05-11 14:04:54 -0400165 IIdentityCredentialStore::STATUS_FAILED, "Error creating ephemeral key"));
166 }
167 optional<vector<uint8_t>> keyPair = support::ecPrivateKeyToKeyPair(ephemeralPriv.value());
168 if (!keyPair) {
169 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
170 IIdentityCredentialStore::STATUS_FAILED, "Error creating ephemeral key-pair"));
David Zeuthen81603152020-02-11 22:04:24 -0500171 }
172
173 // Stash public key of this key-pair for later check in startRetrieval().
David Zeuthen630de2a2020-05-11 14:04:54 -0400174 optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(keyPair.value());
David Zeuthen81603152020-02-11 22:04:24 -0500175 if (!publicKey) {
David Zeuthen630de2a2020-05-11 14:04:54 -0400176 LOG(ERROR) << "Error getting public part of ephemeral key pair";
David Zeuthen81603152020-02-11 22:04:24 -0500177 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
178 IIdentityCredentialStore::STATUS_FAILED,
179 "Error getting public part of ephemeral key pair"));
180 }
181 ephemeralPublicKey_ = publicKey.value();
182
David Zeuthen630de2a2020-05-11 14:04:54 -0400183 *outKeyPair = keyPair.value();
David Zeuthen81603152020-02-11 22:04:24 -0500184 return ndk::ScopedAStatus::ok();
185}
186
187ndk::ScopedAStatus IdentityCredential::setReaderEphemeralPublicKey(
Jooyung Han17be89b2020-02-21 21:17:06 +0900188 const vector<uint8_t>& publicKey) {
189 readerPublicKey_ = publicKey;
David Zeuthen81603152020-02-11 22:04:24 -0500190 return ndk::ScopedAStatus::ok();
191}
192
193ndk::ScopedAStatus IdentityCredential::createAuthChallenge(int64_t* outChallenge) {
David Zeuthen630de2a2020-05-11 14:04:54 -0400194 optional<uint64_t> challenge = hwProxy_->createAuthChallenge();
195 if (!challenge) {
196 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
197 IIdentityCredentialStore::STATUS_FAILED, "Error generating challenge"));
David Zeuthen81603152020-02-11 22:04:24 -0500198 }
David Zeuthen630de2a2020-05-11 14:04:54 -0400199 *outChallenge = challenge.value();
David Zeuthen81603152020-02-11 22:04:24 -0500200 return ndk::ScopedAStatus::ok();
201}
202
David Zeuthen28edb102020-04-28 18:54:55 -0400203ndk::ScopedAStatus IdentityCredential::setRequestedNamespaces(
204 const vector<RequestNamespace>& requestNamespaces) {
205 requestNamespaces_ = requestNamespaces;
206 return ndk::ScopedAStatus::ok();
207}
208
David Zeuthena8ed82c2020-05-08 10:03:28 -0400209ndk::ScopedAStatus IdentityCredential::setVerificationToken(
210 const VerificationToken& verificationToken) {
211 verificationToken_ = verificationToken;
212 return ndk::ScopedAStatus::ok();
213}
214
David Zeuthen81603152020-02-11 22:04:24 -0500215ndk::ScopedAStatus IdentityCredential::startRetrieval(
216 const vector<SecureAccessControlProfile>& accessControlProfiles,
Jooyung Han17be89b2020-02-21 21:17:06 +0900217 const HardwareAuthToken& authToken, const vector<uint8_t>& itemsRequest,
218 const vector<uint8_t>& signingKeyBlob, const vector<uint8_t>& sessionTranscript,
219 const vector<uint8_t>& readerSignature, const vector<int32_t>& requestCounts) {
David Zeuthen34abaae2020-10-26 20:26:36 -0400220 std::unique_ptr<cppbor::Item> sessionTranscriptItem;
David Zeuthen81603152020-02-11 22:04:24 -0500221 if (sessionTranscript.size() > 0) {
222 auto [item, _, message] = cppbor::parse(sessionTranscript);
223 if (item == nullptr) {
224 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
225 IIdentityCredentialStore::STATUS_INVALID_DATA,
226 "SessionTranscript contains invalid CBOR"));
227 }
David Zeuthen34abaae2020-10-26 20:26:36 -0400228 sessionTranscriptItem = std::move(item);
David Zeuthen81603152020-02-11 22:04:24 -0500229 }
230 if (numStartRetrievalCalls_ > 0) {
231 if (sessionTranscript_ != sessionTranscript) {
David Zeuthen630de2a2020-05-11 14:04:54 -0400232 LOG(ERROR) << "Session Transcript changed";
David Zeuthen81603152020-02-11 22:04:24 -0500233 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
234 IIdentityCredentialStore::STATUS_SESSION_TRANSCRIPT_MISMATCH,
235 "Passed-in SessionTranscript doesn't match previously used SessionTranscript"));
236 }
237 }
238 sessionTranscript_ = sessionTranscript;
239
David Zeuthen630de2a2020-05-11 14:04:54 -0400240 // This resets various state in the TA...
241 if (!hwProxy_->startRetrieveEntries()) {
242 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
243 IIdentityCredentialStore::STATUS_FAILED, "Error starting retrieving entries"));
244 }
245
246 optional<vector<uint8_t>> signatureOfToBeSigned;
247 if (readerSignature.size() > 0) {
248 signatureOfToBeSigned = support::coseSignGetSignature(readerSignature);
249 if (!signatureOfToBeSigned) {
250 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
251 IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
252 "Error extracting signatureOfToBeSigned from COSE_Sign1"));
253 }
254 }
255
256 // Feed the auth token to secure hardware.
257 if (!hwProxy_->setAuthToken(authToken.challenge, authToken.userId, authToken.authenticatorId,
258 int(authToken.authenticatorType), authToken.timestamp.milliSeconds,
259 authToken.mac, verificationToken_.challenge,
260 verificationToken_.timestamp.milliSeconds,
261 int(verificationToken_.securityLevel), verificationToken_.mac)) {
262 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
263 IIdentityCredentialStore::STATUS_INVALID_DATA, "Invalid Auth Token"));
264 }
265
266 // We'll be feeding ACPs interleaved with certificates from the reader
267 // certificate chain...
268 vector<SecureAccessControlProfile> remainingAcps = accessControlProfiles;
269
270 // ... and we'll use those ACPs to build up a 32-bit mask indicating which
271 // of the possible 32 ACPs grants access.
272 uint32_t accessControlProfileMask = 0;
273
David Zeuthen81603152020-02-11 22:04:24 -0500274 // If there is a signature, validate that it was made with the top-most key in the
275 // certificate chain embedded in the COSE_Sign1 structure.
276 optional<vector<uint8_t>> readerCertificateChain;
277 if (readerSignature.size() > 0) {
278 readerCertificateChain = support::coseSignGetX5Chain(readerSignature);
279 if (!readerCertificateChain) {
280 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
281 IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
282 "Unable to get reader certificate chain from COSE_Sign1"));
283 }
284
David Zeuthen630de2a2020-05-11 14:04:54 -0400285 // First, feed all the reader certificates to the secure hardware. We start
286 // at the end..
287 optional<vector<vector<uint8_t>>> splitCerts =
288 support::certificateChainSplit(readerCertificateChain.value());
289 if (!splitCerts || splitCerts.value().size() == 0) {
David Zeuthen81603152020-02-11 22:04:24 -0500290 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
291 IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
David Zeuthen630de2a2020-05-11 14:04:54 -0400292 "Error splitting certificate chain from COSE_Sign1"));
293 }
294 for (ssize_t n = splitCerts.value().size() - 1; n >= 0; --n) {
295 const vector<uint8_t>& x509Cert = splitCerts.value()[n];
296 if (!hwProxy_->pushReaderCert(x509Cert)) {
297 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
298 IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
299 StringPrintf("Error validating reader certificate %zd", n).c_str()));
300 }
301
302 // If we have ACPs for that particular certificate, send them to the
303 // TA right now...
304 //
305 // Remember in this case certificate equality is done by comparing public keys,
306 // not bitwise comparison of the certificates.
307 //
308 optional<vector<uint8_t>> x509CertPubKey =
309 support::certificateChainGetTopMostKey(x509Cert);
310 if (!x509CertPubKey) {
311 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
312 IIdentityCredentialStore::STATUS_FAILED,
313 StringPrintf("Error getting public key from reader certificate %zd", n)
314 .c_str()));
315 }
316 vector<SecureAccessControlProfile>::iterator it = remainingAcps.begin();
317 while (it != remainingAcps.end()) {
318 const SecureAccessControlProfile& profile = *it;
319 if (profile.readerCertificate.encodedCertificate.size() == 0) {
320 ++it;
321 continue;
322 }
323 optional<vector<uint8_t>> profilePubKey = support::certificateChainGetTopMostKey(
324 profile.readerCertificate.encodedCertificate);
325 if (!profilePubKey) {
326 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
327 IIdentityCredentialStore::STATUS_FAILED,
328 "Error getting public key from profile"));
329 }
330 if (profilePubKey.value() == x509CertPubKey.value()) {
331 optional<bool> res = hwProxy_->validateAccessControlProfile(
332 profile.id, profile.readerCertificate.encodedCertificate,
333 profile.userAuthenticationRequired, profile.timeoutMillis,
334 profile.secureUserId, profile.mac);
335 if (!res) {
336 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
337 IIdentityCredentialStore::STATUS_INVALID_DATA,
338 "Error validating access control profile"));
339 }
340 if (res.value()) {
341 accessControlProfileMask |= (1 << profile.id);
342 }
343 it = remainingAcps.erase(it);
344 } else {
345 ++it;
346 }
347 }
David Zeuthen81603152020-02-11 22:04:24 -0500348 }
349
David Zeuthen630de2a2020-05-11 14:04:54 -0400350 // ... then pass the request message and have the TA check it's signed by the
351 // key in last certificate we pushed.
352 if (sessionTranscript.size() > 0 && itemsRequest.size() > 0 && readerSignature.size() > 0) {
353 optional<vector<uint8_t>> tbsSignature = support::coseSignGetSignature(readerSignature);
354 if (!tbsSignature) {
355 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
356 IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
357 "Error extracting toBeSigned from COSE_Sign1"));
358 }
359 optional<int> coseSignAlg = support::coseSignGetAlg(readerSignature);
360 if (!coseSignAlg) {
361 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
362 IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
363 "Error extracting signature algorithm from COSE_Sign1"));
364 }
365 if (!hwProxy_->validateRequestMessage(sessionTranscript, itemsRequest,
366 coseSignAlg.value(), tbsSignature.value())) {
367 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
368 IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
369 "readerMessage is not signed by top-level certificate"));
370 }
David Zeuthen81603152020-02-11 22:04:24 -0500371 }
372 }
373
David Zeuthen630de2a2020-05-11 14:04:54 -0400374 // Feed remaining access control profiles...
375 for (const SecureAccessControlProfile& profile : remainingAcps) {
376 optional<bool> res = hwProxy_->validateAccessControlProfile(
377 profile.id, profile.readerCertificate.encodedCertificate,
378 profile.userAuthenticationRequired, profile.timeoutMillis, profile.secureUserId,
379 profile.mac);
380 if (!res) {
381 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
382 IIdentityCredentialStore::STATUS_INVALID_DATA,
383 "Error validating access control profile"));
384 }
385 if (res.value()) {
386 accessControlProfileMask |= (1 << profile.id);
387 }
388 }
David Zeuthen81603152020-02-11 22:04:24 -0500389
David Zeuthen630de2a2020-05-11 14:04:54 -0400390 // TODO: move this check to the TA
391#if 1
David Zeuthen81603152020-02-11 22:04:24 -0500392 // To prevent replay-attacks, we check that the public part of the ephemeral
393 // key we previously created, is present in the DeviceEngagement part of
394 // SessionTranscript as a COSE_Key, in uncompressed form.
395 //
396 // We do this by just searching for the X and Y coordinates.
397 if (sessionTranscript.size() > 0) {
David Zeuthen81603152020-02-11 22:04:24 -0500398 auto [getXYSuccess, ePubX, ePubY] = support::ecPublicKeyGetXandY(ephemeralPublicKey_);
399 if (!getXYSuccess) {
400 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
401 IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
402 "Error extracting X and Y from ePub"));
403 }
404 if (sessionTranscript.size() > 0 &&
David Zeuthenef739512020-06-03 13:24:52 -0400405 !(memmem(sessionTranscript.data(), sessionTranscript.size(), ePubX.data(),
406 ePubX.size()) != nullptr &&
407 memmem(sessionTranscript.data(), sessionTranscript.size(), ePubY.data(),
408 ePubY.size()) != nullptr)) {
David Zeuthen81603152020-02-11 22:04:24 -0500409 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
410 IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
411 "Did not find ephemeral public key's X and Y coordinates in "
412 "SessionTranscript (make sure leading zeroes are not used)"));
413 }
414 }
David Zeuthen630de2a2020-05-11 14:04:54 -0400415#endif
David Zeuthen81603152020-02-11 22:04:24 -0500416
417 // itemsRequest: If non-empty, contains request data that may be signed by the
418 // reader. The content can be defined in the way appropriate for the
419 // credential, but there are three requirements that must be met to work with
420 // this HAL:
421 if (itemsRequest.size() > 0) {
422 // 1. The content must be a CBOR-encoded structure.
423 auto [item, _, message] = cppbor::parse(itemsRequest);
424 if (item == nullptr) {
425 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
426 IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
427 "Error decoding CBOR in itemsRequest"));
428 }
429
430 // 2. The CBOR structure must be a map.
431 const cppbor::Map* map = item->asMap();
432 if (map == nullptr) {
433 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
434 IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
435 "itemsRequest is not a CBOR map"));
436 }
437
438 // 3. The map must contain a key "nameSpaces" whose value contains a map, as described in
439 // the example below.
440 //
441 // NameSpaces = {
442 // + NameSpace => DataElements ; Requested data elements for each NameSpace
443 // }
444 //
445 // NameSpace = tstr
446 //
447 // DataElements = {
448 // + DataElement => IntentToRetain
449 // }
450 //
451 // DataElement = tstr
452 // IntentToRetain = bool
453 //
454 // Here's an example of an |itemsRequest| CBOR value satisfying above requirements 1.
455 // through 3.:
456 //
457 // {
458 // 'docType' : 'org.iso.18013-5.2019',
459 // 'nameSpaces' : {
460 // 'org.iso.18013-5.2019' : {
461 // 'Last name' : false,
462 // 'Birth date' : false,
463 // 'First name' : false,
464 // 'Home address' : true
465 // },
466 // 'org.aamva.iso.18013-5.2019' : {
467 // 'Real Id' : false
468 // }
469 // }
470 // }
471 //
472 const cppbor::Map* nsMap = nullptr;
473 for (size_t n = 0; n < map->size(); n++) {
474 const auto& [keyItem, valueItem] = (*map)[n];
475 if (keyItem->type() == cppbor::TSTR && keyItem->asTstr()->value() == "nameSpaces" &&
476 valueItem->type() == cppbor::MAP) {
477 nsMap = valueItem->asMap();
478 break;
479 }
480 }
481 if (nsMap == nullptr) {
482 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
483 IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
484 "No nameSpaces map in top-most map"));
485 }
486
487 for (size_t n = 0; n < nsMap->size(); n++) {
488 auto [nsKeyItem, nsValueItem] = (*nsMap)[n];
489 const cppbor::Tstr* nsKey = nsKeyItem->asTstr();
490 const cppbor::Map* nsInnerMap = nsValueItem->asMap();
491 if (nsKey == nullptr || nsInnerMap == nullptr) {
492 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
493 IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
494 "Type mismatch in nameSpaces map"));
495 }
496 string requestedNamespace = nsKey->value();
David Zeuthen28edb102020-04-28 18:54:55 -0400497 set<string> requestedKeys;
David Zeuthen81603152020-02-11 22:04:24 -0500498 for (size_t m = 0; m < nsInnerMap->size(); m++) {
499 const auto& [innerMapKeyItem, innerMapValueItem] = (*nsInnerMap)[m];
500 const cppbor::Tstr* nameItem = innerMapKeyItem->asTstr();
501 const cppbor::Simple* simple = innerMapValueItem->asSimple();
502 const cppbor::Bool* intentToRetainItem =
503 (simple != nullptr) ? simple->asBool() : nullptr;
504 if (nameItem == nullptr || intentToRetainItem == nullptr) {
505 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
506 IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
507 "Type mismatch in value in nameSpaces map"));
508 }
David Zeuthen28edb102020-04-28 18:54:55 -0400509 requestedKeys.insert(nameItem->value());
David Zeuthen81603152020-02-11 22:04:24 -0500510 }
511 requestedNameSpacesAndNames_[requestedNamespace] = requestedKeys;
512 }
513 }
514
David Zeuthen81603152020-02-11 22:04:24 -0500515 deviceNameSpacesMap_ = cppbor::Map();
516 currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
517
518 requestCountsRemaining_ = requestCounts;
519 currentNameSpace_ = "";
520
521 itemsRequest_ = itemsRequest;
Jooyung Han17be89b2020-02-21 21:17:06 +0900522 signingKeyBlob_ = signingKeyBlob;
David Zeuthen81603152020-02-11 22:04:24 -0500523
David Zeuthen630de2a2020-05-11 14:04:54 -0400524 // calculate the size of DeviceNameSpaces. We need to know it ahead of time.
525 calcDeviceNameSpacesSize(accessControlProfileMask);
526
527 // Count the number of non-empty namespaces
528 size_t numNamespacesWithValues = 0;
529 for (size_t n = 0; n < expectedNumEntriesPerNamespace_.size(); n++) {
530 if (expectedNumEntriesPerNamespace_[n] > 0) {
531 numNamespacesWithValues += 1;
532 }
533 }
534
535 // Finally, pass info so the HMAC key can be derived and the TA can start
536 // creating the DeviceNameSpaces CBOR...
537 if (sessionTranscript_.size() > 0 && readerPublicKey_.size() > 0 && signingKeyBlob.size() > 0) {
538 // We expect the reader ephemeral public key to be same size and curve
539 // as the ephemeral key we generated (e.g. P-256 key), otherwise ECDH
540 // won't work. So its length should be 65 bytes and it should be
541 // starting with 0x04.
542 if (readerPublicKey_.size() != 65 || readerPublicKey_[0] != 0x04) {
543 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
544 IIdentityCredentialStore::STATUS_FAILED,
545 "Reader public key is not in expected format"));
546 }
547 vector<uint8_t> pubKeyP256(readerPublicKey_.begin() + 1, readerPublicKey_.end());
548 if (!hwProxy_->calcMacKey(sessionTranscript_, pubKeyP256, signingKeyBlob, docType_,
549 numNamespacesWithValues, expectedDeviceNameSpacesSize_)) {
550 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
551 IIdentityCredentialStore::STATUS_FAILED, "Error starting retrieving entries"));
552 }
553 }
David Zeuthen28edb102020-04-28 18:54:55 -0400554
David Zeuthen81603152020-02-11 22:04:24 -0500555 numStartRetrievalCalls_ += 1;
556 return ndk::ScopedAStatus::ok();
557}
558
David Zeuthen28edb102020-04-28 18:54:55 -0400559size_t cborNumBytesForLength(size_t length) {
560 if (length < 24) {
561 return 0;
562 } else if (length <= 0xff) {
563 return 1;
564 } else if (length <= 0xffff) {
565 return 2;
566 } else if (length <= 0xffffffff) {
567 return 4;
568 }
569 return 8;
570}
571
572size_t cborNumBytesForTstr(const string& value) {
573 return 1 + cborNumBytesForLength(value.size()) + value.size();
574}
575
David Zeuthen630de2a2020-05-11 14:04:54 -0400576void IdentityCredential::calcDeviceNameSpacesSize(uint32_t accessControlProfileMask) {
David Zeuthen28edb102020-04-28 18:54:55 -0400577 /*
578 * This is how DeviceNameSpaces is defined:
579 *
580 * DeviceNameSpaces = {
581 * * NameSpace => DeviceSignedItems
582 * }
583 * DeviceSignedItems = {
584 * + DataItemName => DataItemValue
585 * }
586 *
587 * Namespace = tstr
588 * DataItemName = tstr
589 * DataItemValue = any
590 *
591 * This function will calculate its length using knowledge of how CBOR is
592 * encoded.
593 */
594 size_t ret = 0;
David Zeuthen630de2a2020-05-11 14:04:54 -0400595 vector<unsigned int> numEntriesPerNamespace;
David Zeuthen28edb102020-04-28 18:54:55 -0400596 for (const RequestNamespace& rns : requestNamespaces_) {
597 vector<RequestDataItem> itemsToInclude;
598
599 for (const RequestDataItem& rdi : rns.items) {
600 // If we have a CBOR request message, skip if item isn't in it
601 if (itemsRequest_.size() > 0) {
602 const auto& it = requestedNameSpacesAndNames_.find(rns.namespaceName);
603 if (it == requestedNameSpacesAndNames_.end()) {
604 continue;
605 }
606 const set<string>& dataItemNames = it->second;
607 if (dataItemNames.find(rdi.name) == dataItemNames.end()) {
608 continue;
609 }
610 }
611
612 // Access is granted if at least one of the profiles grants access.
613 //
614 // If an item is configured without any profiles, access is denied.
615 //
616 bool authorized = false;
617 for (auto id : rdi.accessControlProfileIds) {
David Zeuthen630de2a2020-05-11 14:04:54 -0400618 if (accessControlProfileMask & (1 << id)) {
619 authorized = true;
620 break;
David Zeuthen28edb102020-04-28 18:54:55 -0400621 }
622 }
623 if (!authorized) {
624 continue;
625 }
626
627 itemsToInclude.push_back(rdi);
628 }
629
David Zeuthen630de2a2020-05-11 14:04:54 -0400630 numEntriesPerNamespace.push_back(itemsToInclude.size());
631
632 // If no entries are to be in the namespace, we don't include it in
633 // the CBOR...
David Zeuthen28edb102020-04-28 18:54:55 -0400634 if (itemsToInclude.size() == 0) {
635 continue;
636 }
637
638 // Key: NameSpace
639 ret += cborNumBytesForTstr(rns.namespaceName);
640
641 // Value: Open the DeviceSignedItems map
642 ret += 1 + cborNumBytesForLength(itemsToInclude.size());
643
644 for (const RequestDataItem& item : itemsToInclude) {
645 // Key: DataItemName
646 ret += cborNumBytesForTstr(item.name);
647
648 // Value: DataItemValue - entryData.size is the length of serialized CBOR so we use
649 // that.
650 ret += item.size;
651 }
David Zeuthen28edb102020-04-28 18:54:55 -0400652 }
653
David Zeuthen630de2a2020-05-11 14:04:54 -0400654 // Now that we know the number of namespaces with values, we know how many
David Zeuthen28edb102020-04-28 18:54:55 -0400655 // bytes the DeviceNamespaces map in the beginning is going to take up.
David Zeuthen630de2a2020-05-11 14:04:54 -0400656 ret += 1 + cborNumBytesForLength(numEntriesPerNamespace.size());
David Zeuthen28edb102020-04-28 18:54:55 -0400657
David Zeuthen630de2a2020-05-11 14:04:54 -0400658 expectedDeviceNameSpacesSize_ = ret;
659 expectedNumEntriesPerNamespace_ = numEntriesPerNamespace;
David Zeuthen28edb102020-04-28 18:54:55 -0400660}
661
David Zeuthen81603152020-02-11 22:04:24 -0500662ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue(
663 const string& nameSpace, const string& name, int32_t entrySize,
664 const vector<int32_t>& accessControlProfileIds) {
665 if (name.empty()) {
666 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
667 IIdentityCredentialStore::STATUS_INVALID_DATA, "Name cannot be empty"));
668 }
669 if (nameSpace.empty()) {
670 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
671 IIdentityCredentialStore::STATUS_INVALID_DATA, "Name space cannot be empty"));
672 }
673
674 if (requestCountsRemaining_.size() == 0) {
675 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
676 IIdentityCredentialStore::STATUS_INVALID_DATA,
677 "No more name spaces left to go through"));
678 }
679
David Zeuthen630de2a2020-05-11 14:04:54 -0400680 bool newNamespace;
David Zeuthen81603152020-02-11 22:04:24 -0500681 if (currentNameSpace_ == "") {
682 // First call.
683 currentNameSpace_ = nameSpace;
David Zeuthen630de2a2020-05-11 14:04:54 -0400684 newNamespace = true;
David Zeuthen81603152020-02-11 22:04:24 -0500685 }
686
687 if (nameSpace == currentNameSpace_) {
688 // Same namespace.
689 if (requestCountsRemaining_[0] == 0) {
690 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
691 IIdentityCredentialStore::STATUS_INVALID_DATA,
692 "No more entries to be retrieved in current name space"));
693 }
694 requestCountsRemaining_[0] -= 1;
695 } else {
696 // New namespace.
697 if (requestCountsRemaining_[0] != 0) {
698 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
699 IIdentityCredentialStore::STATUS_INVALID_DATA,
700 "Moved to new name space but one or more entries need to be retrieved "
701 "in current name space"));
702 }
703 if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
704 deviceNameSpacesMap_.add(currentNameSpace_,
705 std::move(currentNameSpaceDeviceNameSpacesMap_));
706 }
707 currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
708
709 requestCountsRemaining_.erase(requestCountsRemaining_.begin());
710 currentNameSpace_ = nameSpace;
David Zeuthen630de2a2020-05-11 14:04:54 -0400711 newNamespace = true;
David Zeuthen81603152020-02-11 22:04:24 -0500712 }
713
714 // It's permissible to have an empty itemsRequest... but if non-empty you can
715 // only request what was specified in said itemsRequest. Enforce that.
716 if (itemsRequest_.size() > 0) {
717 const auto& it = requestedNameSpacesAndNames_.find(nameSpace);
718 if (it == requestedNameSpacesAndNames_.end()) {
719 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
720 IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE,
721 "Name space was not requested in startRetrieval"));
722 }
David Zeuthen28edb102020-04-28 18:54:55 -0400723 const set<string>& dataItemNames = it->second;
724 if (dataItemNames.find(name) == dataItemNames.end()) {
David Zeuthen81603152020-02-11 22:04:24 -0500725 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
726 IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE,
727 "Data item name in name space was not requested in startRetrieval"));
728 }
729 }
730
David Zeuthen630de2a2020-05-11 14:04:54 -0400731 unsigned int newNamespaceNumEntries = 0;
732 if (newNamespace) {
733 if (expectedNumEntriesPerNamespace_.size() == 0) {
David Zeuthen81603152020-02-11 22:04:24 -0500734 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
735 IIdentityCredentialStore::STATUS_INVALID_DATA,
David Zeuthen630de2a2020-05-11 14:04:54 -0400736 "No more populated name spaces left to go through"));
David Zeuthen81603152020-02-11 22:04:24 -0500737 }
David Zeuthen630de2a2020-05-11 14:04:54 -0400738 newNamespaceNumEntries = expectedNumEntriesPerNamespace_[0];
739 expectedNumEntriesPerNamespace_.erase(expectedNumEntriesPerNamespace_.begin());
David Zeuthen81603152020-02-11 22:04:24 -0500740 }
741
David Zeuthen630de2a2020-05-11 14:04:54 -0400742 // Access control is enforced in the secure hardware.
743 //
744 // ... except for STATUS_NOT_IN_REQUEST_MESSAGE, that's handled above (TODO:
745 // consolidate).
746 //
747 AccessCheckResult res = hwProxy_->startRetrieveEntryValue(
748 nameSpace, name, newNamespaceNumEntries, entrySize, accessControlProfileIds);
749 switch (res) {
750 case AccessCheckResult::kOk:
751 /* Do nothing. */
752 break;
753 case AccessCheckResult::kFailed:
754 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
755 IIdentityCredentialStore::STATUS_FAILED,
756 "Access control check failed (failed)"));
757 break;
758 case AccessCheckResult::kNoAccessControlProfiles:
759 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
760 IIdentityCredentialStore::STATUS_NO_ACCESS_CONTROL_PROFILES,
761 "Access control check failed (no access control profiles)"));
762 break;
763 case AccessCheckResult::kUserAuthenticationFailed:
764 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
765 IIdentityCredentialStore::STATUS_USER_AUTHENTICATION_FAILED,
766 "Access control check failed (user auth)"));
767 break;
768 case AccessCheckResult::kReaderAuthenticationFailed:
769 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
770 IIdentityCredentialStore::STATUS_READER_AUTHENTICATION_FAILED,
771 "Access control check failed (reader auth)"));
772 break;
773 }
David Zeuthen81603152020-02-11 22:04:24 -0500774
775 currentName_ = name;
David Zeuthen630de2a2020-05-11 14:04:54 -0400776 currentAccessControlProfileIds_ = accessControlProfileIds;
David Zeuthen81603152020-02-11 22:04:24 -0500777 entryRemainingBytes_ = entrySize;
778 entryValue_.resize(0);
779
780 return ndk::ScopedAStatus::ok();
781}
782
Jooyung Han17be89b2020-02-21 21:17:06 +0900783ndk::ScopedAStatus IdentityCredential::retrieveEntryValue(const vector<uint8_t>& encryptedContent,
784 vector<uint8_t>* outContent) {
David Zeuthen630de2a2020-05-11 14:04:54 -0400785 optional<vector<uint8_t>> content = hwProxy_->retrieveEntryValue(
786 encryptedContent, currentNameSpace_, currentName_, currentAccessControlProfileIds_);
David Zeuthen81603152020-02-11 22:04:24 -0500787 if (!content) {
788 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
789 IIdentityCredentialStore::STATUS_INVALID_DATA, "Error decrypting data"));
790 }
791
792 size_t chunkSize = content.value().size();
793
794 if (chunkSize > entryRemainingBytes_) {
795 LOG(ERROR) << "Retrieved chunk of size " << chunkSize
796 << " is bigger than remaining space of size " << entryRemainingBytes_;
797 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
798 IIdentityCredentialStore::STATUS_INVALID_DATA,
799 "Retrieved chunk is bigger than remaining space"));
800 }
801
802 entryRemainingBytes_ -= chunkSize;
803 if (entryRemainingBytes_ > 0) {
804 if (chunkSize != IdentityCredentialStore::kGcmChunkSize) {
805 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
806 IIdentityCredentialStore::STATUS_INVALID_DATA,
807 "Retrieved non-final chunk of size which isn't kGcmChunkSize"));
808 }
809 }
810
811 entryValue_.insert(entryValue_.end(), content.value().begin(), content.value().end());
812
813 if (entryRemainingBytes_ == 0) {
814 auto [entryValueItem, _, message] = cppbor::parse(entryValue_);
815 if (entryValueItem == nullptr) {
816 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
817 IIdentityCredentialStore::STATUS_INVALID_DATA,
818 "Retrieved data which is invalid CBOR"));
819 }
820 currentNameSpaceDeviceNameSpacesMap_.add(currentName_, std::move(entryValueItem));
821 }
822
Jooyung Han17be89b2020-02-21 21:17:06 +0900823 *outContent = content.value();
David Zeuthen81603152020-02-11 22:04:24 -0500824 return ndk::ScopedAStatus::ok();
825}
826
Jooyung Han17be89b2020-02-21 21:17:06 +0900827ndk::ScopedAStatus IdentityCredential::finishRetrieval(vector<uint8_t>* outMac,
828 vector<uint8_t>* outDeviceNameSpaces) {
David Zeuthen81603152020-02-11 22:04:24 -0500829 if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
830 deviceNameSpacesMap_.add(currentNameSpace_,
831 std::move(currentNameSpaceDeviceNameSpacesMap_));
832 }
833 vector<uint8_t> encodedDeviceNameSpaces = deviceNameSpacesMap_.encode();
834
David Zeuthen28edb102020-04-28 18:54:55 -0400835 if (encodedDeviceNameSpaces.size() != expectedDeviceNameSpacesSize_) {
836 LOG(ERROR) << "encodedDeviceNameSpaces is " << encodedDeviceNameSpaces.size() << " bytes, "
837 << "was expecting " << expectedDeviceNameSpacesSize_;
838 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
839 IIdentityCredentialStore::STATUS_INVALID_DATA,
840 StringPrintf(
841 "Unexpected CBOR size %zd for encodedDeviceNameSpaces, was expecting %zd",
842 encodedDeviceNameSpaces.size(), expectedDeviceNameSpacesSize_)
843 .c_str()));
844 }
845
David Zeuthen81603152020-02-11 22:04:24 -0500846 // If there's no signing key or no sessionTranscript or no reader ephemeral
847 // public key, we return the empty MAC.
848 optional<vector<uint8_t>> mac;
David Zeuthene35797f2020-02-27 14:25:54 -0500849 if (signingKeyBlob_.size() > 0 && sessionTranscript_.size() > 0 &&
850 readerPublicKey_.size() > 0) {
David Zeuthen630de2a2020-05-11 14:04:54 -0400851 optional<vector<uint8_t>> digestToBeMaced = hwProxy_->finishRetrieval();
852 if (!digestToBeMaced || digestToBeMaced.value().size() != 32) {
David Zeuthen81603152020-02-11 22:04:24 -0500853 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
854 IIdentityCredentialStore::STATUS_INVALID_DATA,
David Zeuthen630de2a2020-05-11 14:04:54 -0400855 "Error generating digestToBeMaced"));
David Zeuthen81603152020-02-11 22:04:24 -0500856 }
David Zeuthen630de2a2020-05-11 14:04:54 -0400857 // Now construct COSE_Mac0 from the returned MAC...
858 mac = support::coseMacWithDigest(digestToBeMaced.value(), {} /* data */);
David Zeuthen81603152020-02-11 22:04:24 -0500859 }
860
Jooyung Han17be89b2020-02-21 21:17:06 +0900861 *outMac = mac.value_or(vector<uint8_t>({}));
862 *outDeviceNameSpaces = encodedDeviceNameSpaces;
David Zeuthen81603152020-02-11 22:04:24 -0500863 return ndk::ScopedAStatus::ok();
864}
865
866ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair(
Jooyung Han17be89b2020-02-21 21:17:06 +0900867 vector<uint8_t>* outSigningKeyBlob, Certificate* outSigningKeyCertificate) {
David Zeuthen630de2a2020-05-11 14:04:54 -0400868 time_t now = time(NULL);
869 optional<pair<vector<uint8_t>, vector<uint8_t>>> pair =
870 hwProxy_->generateSigningKeyPair(docType_, now);
871 if (!pair) {
David Zeuthen81603152020-02-11 22:04:24 -0500872 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
873 IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey"));
874 }
875
David Zeuthen81603152020-02-11 22:04:24 -0500876 *outSigningKeyCertificate = Certificate();
David Zeuthen630de2a2020-05-11 14:04:54 -0400877 outSigningKeyCertificate->encodedCertificate = pair->first;
878
879 *outSigningKeyBlob = pair->second;
David Zeuthen81603152020-02-11 22:04:24 -0500880 return ndk::ScopedAStatus::ok();
881}
882
David Zeuthen49f2d252020-10-16 11:27:24 -0400883ndk::ScopedAStatus IdentityCredential::updateCredential(
884 shared_ptr<IWritableIdentityCredential>* outWritableCredential) {
885 sp<SecureHardwareProvisioningProxy> hwProxy = hwProxyFactory_->createProvisioningProxy();
886 shared_ptr<WritableIdentityCredential> wc =
887 ndk::SharedRefBase::make<WritableIdentityCredential>(hwProxy, docType_,
888 testCredential_);
889 if (!wc->initializeForUpdate(encryptedCredentialKeys_)) {
890 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
891 IIdentityCredentialStore::STATUS_FAILED,
892 "Error initializing WritableIdentityCredential for update"));
893 }
894 *outWritableCredential = wc;
895 return ndk::ScopedAStatus::ok();
896}
897
David Zeuthen81603152020-02-11 22:04:24 -0500898} // namespace aidl::android::hardware::identity