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 #include "Util.h"
22
23 #include <android/hardware/identity/support/IdentityCredentialSupport.h>
24
25 #include <string.h>
26
27 #include <android-base/logging.h>
28 #include <android-base/stringprintf.h>
29
30 #include <cppbor.h>
31 #include <cppbor_parse.h>
32
33 namespace aidl::android::hardware::identity {
34
35 using ::aidl::android::hardware::keymaster::Timestamp;
36 using ::android::base::StringPrintf;
37 using ::std::optional;
38
39 using namespace ::android::hardware::identity;
40
initialize()41 int IdentityCredential::initialize() {
42 if (credentialData_.size() == 0) {
43 LOG(ERROR) << "CredentialData is empty";
44 return IIdentityCredentialStore::STATUS_INVALID_DATA;
45 }
46 auto [item, _, message] = cppbor::parse(credentialData_);
47 if (item == nullptr) {
48 LOG(ERROR) << "CredentialData is not valid CBOR: " << message;
49 return IIdentityCredentialStore::STATUS_INVALID_DATA;
50 }
51
52 const cppbor::Array* arrayItem = item->asArray();
53 if (arrayItem == nullptr || arrayItem->size() != 3) {
54 LOG(ERROR) << "CredentialData is not an array with three elements";
55 return IIdentityCredentialStore::STATUS_INVALID_DATA;
56 }
57
58 const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
59 const cppbor::Bool* testCredentialItem =
60 ((*arrayItem)[1]->asSimple() != nullptr ? ((*arrayItem)[1]->asSimple()->asBool())
61 : nullptr);
62 const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
63 if (docTypeItem == nullptr || testCredentialItem == nullptr ||
64 encryptedCredentialKeysItem == nullptr) {
65 LOG(ERROR) << "CredentialData unexpected item types";
66 return IIdentityCredentialStore::STATUS_INVALID_DATA;
67 }
68
69 docType_ = docTypeItem->value();
70 testCredential_ = testCredentialItem->value();
71
72 vector<uint8_t> hardwareBoundKey;
73 if (testCredential_) {
74 hardwareBoundKey = support::getTestHardwareBoundKey();
75 } else {
76 hardwareBoundKey = getHardwareBoundKey();
77 }
78
79 const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
80 const vector<uint8_t> docTypeVec(docType_.begin(), docType_.end());
81 optional<vector<uint8_t>> decryptedCredentialKeys =
82 support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
83 if (!decryptedCredentialKeys) {
84 LOG(ERROR) << "Error decrypting CredentialKeys";
85 return IIdentityCredentialStore::STATUS_INVALID_DATA;
86 }
87
88 auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
89 if (dckItem == nullptr) {
90 LOG(ERROR) << "Decrypted CredentialKeys is not valid CBOR: " << dckMessage;
91 return IIdentityCredentialStore::STATUS_INVALID_DATA;
92 }
93 const cppbor::Array* dckArrayItem = dckItem->asArray();
94 if (dckArrayItem == nullptr || dckArrayItem->size() != 2) {
95 LOG(ERROR) << "Decrypted CredentialKeys is not an array with two elements";
96 return IIdentityCredentialStore::STATUS_INVALID_DATA;
97 }
98 const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
99 const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
100 if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
101 LOG(ERROR) << "CredentialKeys unexpected item types";
102 return IIdentityCredentialStore::STATUS_INVALID_DATA;
103 }
104 storageKey_ = storageKeyItem->value();
105 credentialPrivKey_ = credentialPrivKeyItem->value();
106
107 return IIdentityCredentialStore::STATUS_OK;
108 }
109
deleteCredential(vector<int8_t> * outProofOfDeletionSignature)110 ndk::ScopedAStatus IdentityCredential::deleteCredential(
111 vector<int8_t>* outProofOfDeletionSignature) {
112 cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_};
113 vector<uint8_t> proofOfDeletion = array.encode();
114
115 optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_,
116 proofOfDeletion, // payload
117 {}, // additionalData
118 {}); // certificateChain
119 if (!signature) {
120 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
121 IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
122 }
123
124 *outProofOfDeletionSignature = byteStringToSigned(signature.value());
125 return ndk::ScopedAStatus::ok();
126 }
127
createEphemeralKeyPair(vector<int8_t> * outKeyPair)128 ndk::ScopedAStatus IdentityCredential::createEphemeralKeyPair(vector<int8_t>* outKeyPair) {
129 optional<vector<uint8_t>> kp = support::createEcKeyPair();
130 if (!kp) {
131 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
132 IIdentityCredentialStore::STATUS_FAILED, "Error creating ephemeral key pair"));
133 }
134
135 // Stash public key of this key-pair for later check in startRetrieval().
136 optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(kp.value());
137 if (!publicKey) {
138 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
139 IIdentityCredentialStore::STATUS_FAILED,
140 "Error getting public part of ephemeral key pair"));
141 }
142 ephemeralPublicKey_ = publicKey.value();
143
144 *outKeyPair = byteStringToSigned(kp.value());
145 return ndk::ScopedAStatus::ok();
146 }
147
setReaderEphemeralPublicKey(const vector<int8_t> & publicKey)148 ndk::ScopedAStatus IdentityCredential::setReaderEphemeralPublicKey(
149 const vector<int8_t>& publicKey) {
150 readerPublicKey_ = byteStringToUnsigned(publicKey);
151 return ndk::ScopedAStatus::ok();
152 }
153
createAuthChallenge(int64_t * outChallenge)154 ndk::ScopedAStatus IdentityCredential::createAuthChallenge(int64_t* outChallenge) {
155 uint64_t challenge = 0;
156 while (challenge == 0) {
157 optional<vector<uint8_t>> bytes = support::getRandom(8);
158 if (!bytes) {
159 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
160 IIdentityCredentialStore::STATUS_FAILED,
161 "Error getting random data for challenge"));
162 }
163
164 challenge = 0;
165 for (size_t n = 0; n < bytes.value().size(); n++) {
166 challenge |= ((bytes.value())[n] << (n * 8));
167 }
168 }
169
170 *outChallenge = challenge;
171 authChallenge_ = challenge;
172 return ndk::ScopedAStatus::ok();
173 }
174
175 // TODO: this could be a lot faster if we did all the splitting and pubkey extraction
176 // ahead of time.
checkReaderAuthentication(const SecureAccessControlProfile & profile,const vector<uint8_t> & readerCertificateChain)177 bool checkReaderAuthentication(const SecureAccessControlProfile& profile,
178 const vector<uint8_t>& readerCertificateChain) {
179 optional<vector<uint8_t>> acpPubKey = support::certificateChainGetTopMostKey(
180 byteStringToUnsigned(profile.readerCertificate.encodedCertificate));
181 if (!acpPubKey) {
182 LOG(ERROR) << "Error extracting public key from readerCertificate in profile";
183 return false;
184 }
185
186 optional<vector<vector<uint8_t>>> certificatesInChain =
187 support::certificateChainSplit(readerCertificateChain);
188 if (!certificatesInChain) {
189 LOG(ERROR) << "Error splitting readerCertificateChain";
190 return false;
191 }
192 for (const vector<uint8_t>& certInChain : certificatesInChain.value()) {
193 optional<vector<uint8_t>> certPubKey = support::certificateChainGetTopMostKey(certInChain);
194 if (!certPubKey) {
195 LOG(ERROR)
196 << "Error extracting public key from certificate in chain presented by reader";
197 return false;
198 }
199 if (acpPubKey.value() == certPubKey.value()) {
200 return true;
201 }
202 }
203 return false;
204 }
205
checkUserAuthentication(const SecureAccessControlProfile & profile,const VerificationToken & verificationToken,const HardwareAuthToken & authToken,uint64_t authChallenge)206 bool checkUserAuthentication(const SecureAccessControlProfile& profile,
207 const VerificationToken& verificationToken,
208 const HardwareAuthToken& authToken, uint64_t authChallenge) {
209 if (profile.secureUserId != authToken.userId) {
210 LOG(ERROR) << "secureUserId in profile (" << profile.secureUserId
211 << ") differs from userId in authToken (" << authToken.userId << ")";
212 return false;
213 }
214
215 if (verificationToken.timestamp.milliSeconds == 0) {
216 LOG(ERROR) << "VerificationToken is not set";
217 return false;
218 }
219 if (authToken.timestamp.milliSeconds == 0) {
220 LOG(ERROR) << "AuthToken is not set";
221 return false;
222 }
223
224 if (profile.timeoutMillis == 0) {
225 if (authToken.challenge == 0) {
226 LOG(ERROR) << "No challenge in authToken";
227 return false;
228 }
229
230 if (authToken.challenge != int64_t(authChallenge)) {
231 LOG(ERROR) << "Challenge in authToken (" << uint64_t(authToken.challenge) << ") "
232 << "doesn't match the challenge we created (" << authChallenge << ")";
233 return false;
234 }
235 return true;
236 }
237
238 // Timeout-based user auth follows. The verification token conveys what the
239 // time is right now in the environment which generated the auth token. This
240 // is what makes it possible to do timeout-based checks.
241 //
242 const Timestamp now = verificationToken.timestamp;
243 if (authToken.timestamp.milliSeconds > now.milliSeconds) {
244 LOG(ERROR) << "Timestamp in authToken (" << authToken.timestamp.milliSeconds
245 << ") is in the future (now: " << now.milliSeconds << ")";
246 return false;
247 }
248 if (now.milliSeconds > authToken.timestamp.milliSeconds + profile.timeoutMillis) {
249 LOG(ERROR) << "Deadline for authToken (" << authToken.timestamp.milliSeconds << " + "
250 << profile.timeoutMillis << " = "
251 << (authToken.timestamp.milliSeconds + profile.timeoutMillis)
252 << ") is in the past (now: " << now.milliSeconds << ")";
253 return false;
254 }
255 return true;
256 }
257
setRequestedNamespaces(const vector<RequestNamespace> & requestNamespaces)258 ndk::ScopedAStatus IdentityCredential::setRequestedNamespaces(
259 const vector<RequestNamespace>& requestNamespaces) {
260 requestNamespaces_ = requestNamespaces;
261 return ndk::ScopedAStatus::ok();
262 }
263
setVerificationToken(const VerificationToken & verificationToken)264 ndk::ScopedAStatus IdentityCredential::setVerificationToken(
265 const VerificationToken& verificationToken) {
266 verificationToken_ = verificationToken;
267 return ndk::ScopedAStatus::ok();
268 }
269
startRetrieval(const vector<SecureAccessControlProfile> & accessControlProfiles,const HardwareAuthToken & authToken,const vector<int8_t> & itemsRequestS,const vector<int8_t> & signingKeyBlobS,const vector<int8_t> & sessionTranscriptS,const vector<int8_t> & readerSignatureS,const vector<int32_t> & requestCounts)270 ndk::ScopedAStatus IdentityCredential::startRetrieval(
271 const vector<SecureAccessControlProfile>& accessControlProfiles,
272 const HardwareAuthToken& authToken, const vector<int8_t>& itemsRequestS,
273 const vector<int8_t>& signingKeyBlobS, const vector<int8_t>& sessionTranscriptS,
274 const vector<int8_t>& readerSignatureS, const vector<int32_t>& requestCounts) {
275 auto sessionTranscript = byteStringToUnsigned(sessionTranscriptS);
276 auto itemsRequest = byteStringToUnsigned(itemsRequestS);
277 auto readerSignature = byteStringToUnsigned(readerSignatureS);
278
279 std::unique_ptr<cppbor::Item> sessionTranscriptItem;
280 if (sessionTranscript.size() > 0) {
281 auto [item, _, message] = cppbor::parse(sessionTranscript);
282 if (item == nullptr) {
283 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
284 IIdentityCredentialStore::STATUS_INVALID_DATA,
285 "SessionTranscript contains invalid CBOR"));
286 }
287 sessionTranscriptItem = std::move(item);
288 }
289 if (numStartRetrievalCalls_ > 0) {
290 if (sessionTranscript_ != sessionTranscript) {
291 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
292 IIdentityCredentialStore::STATUS_SESSION_TRANSCRIPT_MISMATCH,
293 "Passed-in SessionTranscript doesn't match previously used SessionTranscript"));
294 }
295 }
296 sessionTranscript_ = sessionTranscript;
297
298 // If there is a signature, validate that it was made with the top-most key in the
299 // certificate chain embedded in the COSE_Sign1 structure.
300 optional<vector<uint8_t>> readerCertificateChain;
301 if (readerSignature.size() > 0) {
302 readerCertificateChain = support::coseSignGetX5Chain(readerSignature);
303 if (!readerCertificateChain) {
304 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
305 IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
306 "Unable to get reader certificate chain from COSE_Sign1"));
307 }
308
309 if (!support::certificateChainValidate(readerCertificateChain.value())) {
310 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
311 IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
312 "Error validating reader certificate chain"));
313 }
314
315 optional<vector<uint8_t>> readerPublicKey =
316 support::certificateChainGetTopMostKey(readerCertificateChain.value());
317 if (!readerPublicKey) {
318 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
319 IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
320 "Unable to get public key from reader certificate chain"));
321 }
322
323 const vector<uint8_t>& itemsRequestBytes = itemsRequest;
324 vector<uint8_t> encodedReaderAuthentication =
325 cppbor::Array()
326 .add("ReaderAuthentication")
327 .add(std::move(sessionTranscriptItem))
328 .add(cppbor::Semantic(24, itemsRequestBytes))
329 .encode();
330 vector<uint8_t> encodedReaderAuthenticationBytes =
331 cppbor::Semantic(24, encodedReaderAuthentication).encode();
332 if (!support::coseCheckEcDsaSignature(readerSignature,
333 encodedReaderAuthenticationBytes, // detached content
334 readerPublicKey.value())) {
335 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
336 IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
337 "readerSignature check failed"));
338 }
339 }
340
341 // Here's where we would validate the passed-in |authToken| to assure ourselves
342 // that it comes from the e.g. biometric hardware and wasn't made up by an attacker.
343 //
344 // However this involves calculating the MAC. However this requires access
345 // to the key needed to a pre-shared key which we don't have...
346 //
347
348 // To prevent replay-attacks, we check that the public part of the ephemeral
349 // key we previously created, is present in the DeviceEngagement part of
350 // SessionTranscript as a COSE_Key, in uncompressed form.
351 //
352 // We do this by just searching for the X and Y coordinates.
353 if (sessionTranscript.size() > 0) {
354 auto [getXYSuccess, ePubX, ePubY] = support::ecPublicKeyGetXandY(ephemeralPublicKey_);
355 if (!getXYSuccess) {
356 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
357 IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
358 "Error extracting X and Y from ePub"));
359 }
360 if (sessionTranscript.size() > 0 &&
361 !(memmem(sessionTranscript.data(), sessionTranscript.size(), ePubX.data(),
362 ePubX.size()) != nullptr &&
363 memmem(sessionTranscript.data(), sessionTranscript.size(), ePubY.data(),
364 ePubY.size()) != nullptr)) {
365 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
366 IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
367 "Did not find ephemeral public key's X and Y coordinates in "
368 "SessionTranscript (make sure leading zeroes are not used)"));
369 }
370 }
371
372 // itemsRequest: If non-empty, contains request data that may be signed by the
373 // reader. The content can be defined in the way appropriate for the
374 // credential, but there are three requirements that must be met to work with
375 // this HAL:
376 if (itemsRequest.size() > 0) {
377 // 1. The content must be a CBOR-encoded structure.
378 auto [item, _, message] = cppbor::parse(itemsRequest);
379 if (item == nullptr) {
380 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
381 IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
382 "Error decoding CBOR in itemsRequest"));
383 }
384
385 // 2. The CBOR structure must be a map.
386 const cppbor::Map* map = item->asMap();
387 if (map == nullptr) {
388 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
389 IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
390 "itemsRequest is not a CBOR map"));
391 }
392
393 // 3. The map must contain a key "nameSpaces" whose value contains a map, as described in
394 // the example below.
395 //
396 // NameSpaces = {
397 // + NameSpace => DataElements ; Requested data elements for each NameSpace
398 // }
399 //
400 // NameSpace = tstr
401 //
402 // DataElements = {
403 // + DataElement => IntentToRetain
404 // }
405 //
406 // DataElement = tstr
407 // IntentToRetain = bool
408 //
409 // Here's an example of an |itemsRequest| CBOR value satisfying above requirements 1.
410 // through 3.:
411 //
412 // {
413 // 'docType' : 'org.iso.18013-5.2019',
414 // 'nameSpaces' : {
415 // 'org.iso.18013-5.2019' : {
416 // 'Last name' : false,
417 // 'Birth date' : false,
418 // 'First name' : false,
419 // 'Home address' : true
420 // },
421 // 'org.aamva.iso.18013-5.2019' : {
422 // 'Real Id' : false
423 // }
424 // }
425 // }
426 //
427 const cppbor::Map* nsMap = nullptr;
428 for (size_t n = 0; n < map->size(); n++) {
429 const auto& [keyItem, valueItem] = (*map)[n];
430 if (keyItem->type() == cppbor::TSTR && keyItem->asTstr()->value() == "nameSpaces" &&
431 valueItem->type() == cppbor::MAP) {
432 nsMap = valueItem->asMap();
433 break;
434 }
435 }
436 if (nsMap == nullptr) {
437 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
438 IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
439 "No nameSpaces map in top-most map"));
440 }
441
442 for (size_t n = 0; n < nsMap->size(); n++) {
443 auto [nsKeyItem, nsValueItem] = (*nsMap)[n];
444 const cppbor::Tstr* nsKey = nsKeyItem->asTstr();
445 const cppbor::Map* nsInnerMap = nsValueItem->asMap();
446 if (nsKey == nullptr || nsInnerMap == nullptr) {
447 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
448 IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
449 "Type mismatch in nameSpaces map"));
450 }
451 string requestedNamespace = nsKey->value();
452 set<string> requestedKeys;
453 for (size_t m = 0; m < nsInnerMap->size(); m++) {
454 const auto& [innerMapKeyItem, innerMapValueItem] = (*nsInnerMap)[m];
455 const cppbor::Tstr* nameItem = innerMapKeyItem->asTstr();
456 const cppbor::Simple* simple = innerMapValueItem->asSimple();
457 const cppbor::Bool* intentToRetainItem =
458 (simple != nullptr) ? simple->asBool() : nullptr;
459 if (nameItem == nullptr || intentToRetainItem == nullptr) {
460 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
461 IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
462 "Type mismatch in value in nameSpaces map"));
463 }
464 requestedKeys.insert(nameItem->value());
465 }
466 requestedNameSpacesAndNames_[requestedNamespace] = requestedKeys;
467 }
468 }
469
470 // Validate all the access control profiles in the requestData.
471 bool haveAuthToken = (authToken.timestamp.milliSeconds != int64_t(0));
472 for (const auto& profile : accessControlProfiles) {
473 if (!secureAccessControlProfileCheckMac(profile, storageKey_)) {
474 LOG(ERROR) << "Error checking MAC for profile";
475 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
476 IIdentityCredentialStore::STATUS_INVALID_DATA,
477 "Error checking MAC for profile"));
478 }
479 int accessControlCheck = IIdentityCredentialStore::STATUS_OK;
480 if (profile.userAuthenticationRequired) {
481 if (!haveAuthToken ||
482 !checkUserAuthentication(profile, verificationToken_, authToken, authChallenge_)) {
483 accessControlCheck = IIdentityCredentialStore::STATUS_USER_AUTHENTICATION_FAILED;
484 }
485 } else if (profile.readerCertificate.encodedCertificate.size() > 0) {
486 if (!readerCertificateChain ||
487 !checkReaderAuthentication(profile, readerCertificateChain.value())) {
488 accessControlCheck = IIdentityCredentialStore::STATUS_READER_AUTHENTICATION_FAILED;
489 }
490 }
491 profileIdToAccessCheckResult_[profile.id] = accessControlCheck;
492 }
493
494 deviceNameSpacesMap_ = cppbor::Map();
495 currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
496
497 requestCountsRemaining_ = requestCounts;
498 currentNameSpace_ = "";
499
500 itemsRequest_ = itemsRequest;
501 signingKeyBlob_ = byteStringToUnsigned(signingKeyBlobS);
502
503 // Finally, calculate the size of DeviceNameSpaces. We need to know it ahead of time.
504 expectedDeviceNameSpacesSize_ = calcDeviceNameSpacesSize();
505
506 numStartRetrievalCalls_ += 1;
507 return ndk::ScopedAStatus::ok();
508 }
509
cborNumBytesForLength(size_t length)510 size_t cborNumBytesForLength(size_t length) {
511 if (length < 24) {
512 return 0;
513 } else if (length <= 0xff) {
514 return 1;
515 } else if (length <= 0xffff) {
516 return 2;
517 } else if (length <= 0xffffffff) {
518 return 4;
519 }
520 return 8;
521 }
522
cborNumBytesForTstr(const string & value)523 size_t cborNumBytesForTstr(const string& value) {
524 return 1 + cborNumBytesForLength(value.size()) + value.size();
525 }
526
calcDeviceNameSpacesSize()527 size_t IdentityCredential::calcDeviceNameSpacesSize() {
528 /*
529 * This is how DeviceNameSpaces is defined:
530 *
531 * DeviceNameSpaces = {
532 * * NameSpace => DeviceSignedItems
533 * }
534 * DeviceSignedItems = {
535 * + DataItemName => DataItemValue
536 * }
537 *
538 * Namespace = tstr
539 * DataItemName = tstr
540 * DataItemValue = any
541 *
542 * This function will calculate its length using knowledge of how CBOR is
543 * encoded.
544 */
545 size_t ret = 0;
546 size_t numNamespacesWithValues = 0;
547 for (const RequestNamespace& rns : requestNamespaces_) {
548 vector<RequestDataItem> itemsToInclude;
549
550 for (const RequestDataItem& rdi : rns.items) {
551 // If we have a CBOR request message, skip if item isn't in it
552 if (itemsRequest_.size() > 0) {
553 const auto& it = requestedNameSpacesAndNames_.find(rns.namespaceName);
554 if (it == requestedNameSpacesAndNames_.end()) {
555 continue;
556 }
557 const set<string>& dataItemNames = it->second;
558 if (dataItemNames.find(rdi.name) == dataItemNames.end()) {
559 continue;
560 }
561 }
562
563 // Access is granted if at least one of the profiles grants access.
564 //
565 // If an item is configured without any profiles, access is denied.
566 //
567 bool authorized = false;
568 for (auto id : rdi.accessControlProfileIds) {
569 auto it = profileIdToAccessCheckResult_.find(id);
570 if (it != profileIdToAccessCheckResult_.end()) {
571 int accessControlForProfile = it->second;
572 if (accessControlForProfile == IIdentityCredentialStore::STATUS_OK) {
573 authorized = true;
574 break;
575 }
576 }
577 }
578 if (!authorized) {
579 continue;
580 }
581
582 itemsToInclude.push_back(rdi);
583 }
584
585 // If no entries are to be in the namespace, we don't include it...
586 if (itemsToInclude.size() == 0) {
587 continue;
588 }
589
590 // Key: NameSpace
591 ret += cborNumBytesForTstr(rns.namespaceName);
592
593 // Value: Open the DeviceSignedItems map
594 ret += 1 + cborNumBytesForLength(itemsToInclude.size());
595
596 for (const RequestDataItem& item : itemsToInclude) {
597 // Key: DataItemName
598 ret += cborNumBytesForTstr(item.name);
599
600 // Value: DataItemValue - entryData.size is the length of serialized CBOR so we use
601 // that.
602 ret += item.size;
603 }
604
605 numNamespacesWithValues++;
606 }
607
608 // Now that we now the nunber of namespaces with values, we know how many
609 // bytes the DeviceNamespaces map in the beginning is going to take up.
610 ret += 1 + cborNumBytesForLength(numNamespacesWithValues);
611
612 return ret;
613 }
614
startRetrieveEntryValue(const string & nameSpace,const string & name,int32_t entrySize,const vector<int32_t> & accessControlProfileIds)615 ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue(
616 const string& nameSpace, const string& name, int32_t entrySize,
617 const vector<int32_t>& accessControlProfileIds) {
618 if (name.empty()) {
619 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
620 IIdentityCredentialStore::STATUS_INVALID_DATA, "Name cannot be empty"));
621 }
622 if (nameSpace.empty()) {
623 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
624 IIdentityCredentialStore::STATUS_INVALID_DATA, "Name space cannot be empty"));
625 }
626
627 if (requestCountsRemaining_.size() == 0) {
628 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
629 IIdentityCredentialStore::STATUS_INVALID_DATA,
630 "No more name spaces left to go through"));
631 }
632
633 if (currentNameSpace_ == "") {
634 // First call.
635 currentNameSpace_ = nameSpace;
636 }
637
638 if (nameSpace == currentNameSpace_) {
639 // Same namespace.
640 if (requestCountsRemaining_[0] == 0) {
641 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
642 IIdentityCredentialStore::STATUS_INVALID_DATA,
643 "No more entries to be retrieved in current name space"));
644 }
645 requestCountsRemaining_[0] -= 1;
646 } else {
647 // New namespace.
648 if (requestCountsRemaining_[0] != 0) {
649 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
650 IIdentityCredentialStore::STATUS_INVALID_DATA,
651 "Moved to new name space but one or more entries need to be retrieved "
652 "in current name space"));
653 }
654 if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
655 deviceNameSpacesMap_.add(currentNameSpace_,
656 std::move(currentNameSpaceDeviceNameSpacesMap_));
657 }
658 currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
659
660 requestCountsRemaining_.erase(requestCountsRemaining_.begin());
661 currentNameSpace_ = nameSpace;
662 }
663
664 // It's permissible to have an empty itemsRequest... but if non-empty you can
665 // only request what was specified in said itemsRequest. Enforce that.
666 if (itemsRequest_.size() > 0) {
667 const auto& it = requestedNameSpacesAndNames_.find(nameSpace);
668 if (it == requestedNameSpacesAndNames_.end()) {
669 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
670 IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE,
671 "Name space was not requested in startRetrieval"));
672 }
673 const set<string>& dataItemNames = it->second;
674 if (dataItemNames.find(name) == dataItemNames.end()) {
675 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
676 IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE,
677 "Data item name in name space was not requested in startRetrieval"));
678 }
679 }
680
681 // Enforce access control.
682 //
683 // Access is granted if at least one of the profiles grants access.
684 //
685 // If an item is configured without any profiles, access is denied.
686 //
687 int accessControl = IIdentityCredentialStore::STATUS_NO_ACCESS_CONTROL_PROFILES;
688 for (auto id : accessControlProfileIds) {
689 auto search = profileIdToAccessCheckResult_.find(id);
690 if (search == profileIdToAccessCheckResult_.end()) {
691 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
692 IIdentityCredentialStore::STATUS_INVALID_DATA,
693 "Requested entry with unvalidated profile id"));
694 }
695 int accessControlForProfile = search->second;
696 if (accessControlForProfile == IIdentityCredentialStore::STATUS_OK) {
697 accessControl = IIdentityCredentialStore::STATUS_OK;
698 break;
699 }
700 accessControl = accessControlForProfile;
701 }
702 if (accessControl != IIdentityCredentialStore::STATUS_OK) {
703 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
704 int(accessControl), "Access control check failed"));
705 }
706
707 entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
708
709 currentName_ = name;
710 entryRemainingBytes_ = entrySize;
711 entryValue_.resize(0);
712
713 return ndk::ScopedAStatus::ok();
714 }
715
retrieveEntryValue(const vector<int8_t> & encryptedContentS,vector<int8_t> * outContent)716 ndk::ScopedAStatus IdentityCredential::retrieveEntryValue(const vector<int8_t>& encryptedContentS,
717 vector<int8_t>* outContent) {
718 auto encryptedContent = byteStringToUnsigned(encryptedContentS);
719 optional<vector<uint8_t>> content =
720 support::decryptAes128Gcm(storageKey_, encryptedContent, entryAdditionalData_);
721 if (!content) {
722 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
723 IIdentityCredentialStore::STATUS_INVALID_DATA, "Error decrypting data"));
724 }
725
726 size_t chunkSize = content.value().size();
727
728 if (chunkSize > entryRemainingBytes_) {
729 LOG(ERROR) << "Retrieved chunk of size " << chunkSize
730 << " is bigger than remaining space of size " << entryRemainingBytes_;
731 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
732 IIdentityCredentialStore::STATUS_INVALID_DATA,
733 "Retrieved chunk is bigger than remaining space"));
734 }
735
736 entryRemainingBytes_ -= chunkSize;
737 if (entryRemainingBytes_ > 0) {
738 if (chunkSize != IdentityCredentialStore::kGcmChunkSize) {
739 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
740 IIdentityCredentialStore::STATUS_INVALID_DATA,
741 "Retrieved non-final chunk of size which isn't kGcmChunkSize"));
742 }
743 }
744
745 entryValue_.insert(entryValue_.end(), content.value().begin(), content.value().end());
746
747 if (entryRemainingBytes_ == 0) {
748 auto [entryValueItem, _, message] = cppbor::parse(entryValue_);
749 if (entryValueItem == nullptr) {
750 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
751 IIdentityCredentialStore::STATUS_INVALID_DATA,
752 "Retrieved data which is invalid CBOR"));
753 }
754 currentNameSpaceDeviceNameSpacesMap_.add(currentName_, std::move(entryValueItem));
755 }
756
757 *outContent = byteStringToSigned(content.value());
758 return ndk::ScopedAStatus::ok();
759 }
760
finishRetrieval(vector<int8_t> * outMac,vector<int8_t> * outDeviceNameSpaces)761 ndk::ScopedAStatus IdentityCredential::finishRetrieval(vector<int8_t>* outMac,
762 vector<int8_t>* outDeviceNameSpaces) {
763 if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
764 deviceNameSpacesMap_.add(currentNameSpace_,
765 std::move(currentNameSpaceDeviceNameSpacesMap_));
766 }
767 vector<uint8_t> encodedDeviceNameSpaces = deviceNameSpacesMap_.encode();
768
769 if (encodedDeviceNameSpaces.size() != expectedDeviceNameSpacesSize_) {
770 LOG(ERROR) << "encodedDeviceNameSpaces is " << encodedDeviceNameSpaces.size() << " bytes, "
771 << "was expecting " << expectedDeviceNameSpacesSize_;
772 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
773 IIdentityCredentialStore::STATUS_INVALID_DATA,
774 StringPrintf(
775 "Unexpected CBOR size %zd for encodedDeviceNameSpaces, was expecting %zd",
776 encodedDeviceNameSpaces.size(), expectedDeviceNameSpacesSize_)
777 .c_str()));
778 }
779
780 // If there's no signing key or no sessionTranscript or no reader ephemeral
781 // public key, we return the empty MAC.
782 optional<vector<uint8_t>> mac;
783 if (signingKeyBlob_.size() > 0 && sessionTranscript_.size() > 0 &&
784 readerPublicKey_.size() > 0) {
785 vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
786 optional<vector<uint8_t>> signingKey =
787 support::decryptAes128Gcm(storageKey_, signingKeyBlob_, docTypeAsBlob);
788 if (!signingKey) {
789 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
790 IIdentityCredentialStore::STATUS_INVALID_DATA,
791 "Error decrypting signingKeyBlob"));
792 }
793
794 vector<uint8_t> sessionTranscriptBytes = cppbor::Semantic(24, sessionTranscript_).encode();
795 optional<vector<uint8_t>> eMacKey =
796 support::calcEMacKey(signingKey.value(), readerPublicKey_, sessionTranscriptBytes);
797 if (!eMacKey) {
798 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
799 IIdentityCredentialStore::STATUS_FAILED, "Error calculating EMacKey"));
800 }
801 mac = support::calcMac(sessionTranscript_, docType_, encodedDeviceNameSpaces,
802 eMacKey.value());
803 if (!mac) {
804 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
805 IIdentityCredentialStore::STATUS_FAILED, "Error MACing data"));
806 }
807 }
808
809 *outMac = byteStringToSigned(mac.value_or(vector<uint8_t>({})));
810 *outDeviceNameSpaces = byteStringToSigned(encodedDeviceNameSpaces);
811 return ndk::ScopedAStatus::ok();
812 }
813
generateSigningKeyPair(vector<int8_t> * outSigningKeyBlob,Certificate * outSigningKeyCertificate)814 ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair(
815 vector<int8_t>* outSigningKeyBlob, Certificate* outSigningKeyCertificate) {
816 string serialDecimal = "1";
817 string issuer = "Android Identity Credential Key";
818 string subject = "Android Identity Credential Authentication Key";
819 time_t validityNotBefore = time(nullptr);
820 time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
821
822 optional<vector<uint8_t>> signingKeyPKCS8 = support::createEcKeyPair();
823 if (!signingKeyPKCS8) {
824 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
825 IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey"));
826 }
827
828 optional<vector<uint8_t>> signingPublicKey =
829 support::ecKeyPairGetPublicKey(signingKeyPKCS8.value());
830 if (!signingPublicKey) {
831 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
832 IIdentityCredentialStore::STATUS_FAILED,
833 "Error getting public part of signingKey"));
834 }
835
836 optional<vector<uint8_t>> signingKey = support::ecKeyPairGetPrivateKey(signingKeyPKCS8.value());
837 if (!signingKey) {
838 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
839 IIdentityCredentialStore::STATUS_FAILED,
840 "Error getting private part of signingKey"));
841 }
842
843 optional<vector<uint8_t>> certificate = support::ecPublicKeyGenerateCertificate(
844 signingPublicKey.value(), credentialPrivKey_, serialDecimal, issuer, subject,
845 validityNotBefore, validityNotAfter);
846 if (!certificate) {
847 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
848 IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey"));
849 }
850
851 optional<vector<uint8_t>> nonce = support::getRandom(12);
852 if (!nonce) {
853 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
854 IIdentityCredentialStore::STATUS_FAILED, "Error getting random"));
855 }
856 vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
857 optional<vector<uint8_t>> encryptedSigningKey = support::encryptAes128Gcm(
858 storageKey_, nonce.value(), signingKey.value(), docTypeAsBlob);
859 if (!encryptedSigningKey) {
860 return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
861 IIdentityCredentialStore::STATUS_FAILED, "Error encrypting signingKey"));
862 }
863 *outSigningKeyBlob = byteStringToSigned(encryptedSigningKey.value());
864 *outSigningKeyCertificate = Certificate();
865 outSigningKeyCertificate->encodedCertificate = byteStringToSigned(certificate.value());
866 return ndk::ScopedAStatus::ok();
867 }
868
869 } // namespace aidl::android::hardware::identity
870