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