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