blob: b0a5e568f24a2cb5c6907bc69ded730f93cbdfcc [file] [log] [blame]
David Zeuthenc75ac312019-10-28 13:16:45 -04001/*
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
28#include <cppbor.h>
29#include <cppbor_parse.h>
30
31namespace android {
32namespace hardware {
33namespace identity {
34namespace implementation {
35
36using ::android::hardware::keymaster::V4_0::Timestamp;
37using ::std::optional;
38
39Return<void> IdentityCredential::deleteCredential(deleteCredential_cb _hidl_cb) {
40 cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_};
41 vector<uint8_t> proofOfDeletion = array.encode();
42
43 optional<vector<uint8_t>> proofOfDeletionSignature =
44 support::coseSignEcDsa(credentialPrivKey_,
45 proofOfDeletion, // payload
46 {}, // additionalData
47 {}); // certificateChain
48 if (!proofOfDeletionSignature) {
49 _hidl_cb(support::result(ResultCode::FAILED, "Error signing data"), {});
50 return Void();
51 }
52
53 _hidl_cb(support::resultOK(), proofOfDeletionSignature.value());
54 return Void();
55}
56
57Return<void> IdentityCredential::createEphemeralKeyPair(createEphemeralKeyPair_cb _hidl_cb) {
58 optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
59 if (!keyPair) {
60 _hidl_cb(support::result(ResultCode::FAILED, "Error creating ephemeral key pair"), {});
61 return Void();
62 }
63
64 // Stash public key of this key-pair for later check in startRetrieval().
65 optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(keyPair.value());
66 if (!publicKey) {
67 _hidl_cb(support::result(ResultCode::FAILED,
68 "Error getting public part of ephemeral key pair"),
69 {});
70 return Void();
71 }
72 ephemeralPublicKey_ = publicKey.value();
73
74 _hidl_cb(support::resultOK(), keyPair.value());
75 return Void();
76}
77
78Return<void> IdentityCredential::setReaderEphemeralPublicKey(
79 const hidl_vec<uint8_t>& publicKey, setReaderEphemeralPublicKey_cb _hidl_cb) {
80 readerPublicKey_ = publicKey;
81 _hidl_cb(support::resultOK());
82 return Void();
83}
84
85ResultCode IdentityCredential::initialize() {
86 auto [item, _, message] = cppbor::parse(credentialData_);
87 if (item == nullptr) {
88 LOG(ERROR) << "CredentialData is not valid CBOR: " << message;
89 return ResultCode::INVALID_DATA;
90 }
91
92 const cppbor::Array* arrayItem = item->asArray();
93 if (arrayItem == nullptr || arrayItem->size() != 3) {
94 LOG(ERROR) << "CredentialData is not an array with three elements";
95 return ResultCode::INVALID_DATA;
96 }
97
98 const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
99 const cppbor::Bool* testCredentialItem =
100 ((*arrayItem)[1]->asSimple() != nullptr ? ((*arrayItem)[1]->asSimple()->asBool())
101 : nullptr);
102 const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
103 if (docTypeItem == nullptr || testCredentialItem == nullptr ||
104 encryptedCredentialKeysItem == nullptr) {
105 LOG(ERROR) << "CredentialData unexpected item types";
106 return ResultCode::INVALID_DATA;
107 }
108
109 docType_ = docTypeItem->value();
110 testCredential_ = testCredentialItem->value();
111
112 vector<uint8_t> hardwareBoundKey;
113 if (testCredential_) {
114 hardwareBoundKey = support::getTestHardwareBoundKey();
115 } else {
116 hardwareBoundKey = support::getHardwareBoundKey();
117 }
118
119 const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
120 const vector<uint8_t> docTypeVec(docType_.begin(), docType_.end());
121 optional<vector<uint8_t>> decryptedCredentialKeys =
122 support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
123 if (!decryptedCredentialKeys) {
124 LOG(ERROR) << "Error decrypting CredentialKeys";
125 return ResultCode::INVALID_DATA;
126 }
127
128 auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
129 if (dckItem == nullptr) {
130 LOG(ERROR) << "Decrypted CredentialKeys is not valid CBOR: " << dckMessage;
131 return ResultCode::INVALID_DATA;
132 }
133 const cppbor::Array* dckArrayItem = dckItem->asArray();
134 if (dckArrayItem == nullptr || dckArrayItem->size() != 2) {
135 LOG(ERROR) << "Decrypted CredentialKeys is not an array with two elements";
136 return ResultCode::INVALID_DATA;
137 }
138 const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
139 const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
140 if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
141 LOG(ERROR) << "CredentialKeys unexpected item types";
142 return ResultCode::INVALID_DATA;
143 }
144 storageKey_ = storageKeyItem->value();
145 credentialPrivKey_ = credentialPrivKeyItem->value();
146
147 return ResultCode::OK;
148}
149
150Return<void> IdentityCredential::createAuthChallenge(createAuthChallenge_cb _hidl_cb) {
151 uint64_t challenge = 0;
152 while (challenge == 0) {
153 optional<vector<uint8_t>> bytes = support::getRandom(8);
154 if (!bytes) {
155 _hidl_cb(support::result(ResultCode::FAILED, "Error getting random data for challenge"),
156 0);
157 return Void();
158 }
159
160 challenge = 0;
161 for (size_t n = 0; n < bytes.value().size(); n++) {
162 challenge |= ((bytes.value())[n] << (n * 8));
163 }
164 }
165
166 authChallenge_ = challenge;
167 _hidl_cb(support::resultOK(), challenge);
168 return Void();
169}
170
171// TODO: this could be a lot faster if we did all the splitting and pubkey extraction
172// ahead of time.
173bool checkReaderAuthentication(const SecureAccessControlProfile& profile,
174 const vector<uint8_t>& readerCertificateChain) {
175 optional<vector<uint8_t>> acpPubKey =
176 support::certificateChainGetTopMostKey(profile.readerCertificate);
177 if (!acpPubKey) {
178 LOG(ERROR) << "Error extracting public key from readerCertificate in profile";
179 return false;
180 }
181
182 optional<vector<vector<uint8_t>>> certificatesInChain =
183 support::certificateChainSplit(readerCertificateChain);
184 if (!certificatesInChain) {
185 LOG(ERROR) << "Error splitting readerCertificateChain";
186 return false;
187 }
188 for (const vector<uint8_t>& certInChain : certificatesInChain.value()) {
189 optional<vector<uint8_t>> certPubKey = support::certificateChainGetTopMostKey(certInChain);
190 if (!certPubKey) {
191 LOG(ERROR)
192 << "Error extracting public key from certificate in chain presented by reader";
193 return false;
194 }
195 if (acpPubKey.value() == certPubKey.value()) {
196 return true;
197 }
198 }
199 return false;
200}
201
202Timestamp clockGetTime() {
203 struct timespec time;
204 clock_gettime(CLOCK_MONOTONIC, &time);
205 return time.tv_sec * 1000 + time.tv_nsec / 1000000;
206}
207
208bool checkUserAuthentication(const SecureAccessControlProfile& profile,
209 const HardwareAuthToken& authToken, uint64_t authChallenge) {
210 if (profile.secureUserId != authToken.userId) {
211 LOG(ERROR) << "secureUserId in profile (" << profile.secureUserId
212 << ") differs from userId in authToken (" << authToken.userId << ")";
213 return false;
214 }
215
216 if (profile.timeoutMillis == 0) {
217 if (authToken.challenge == 0) {
218 LOG(ERROR) << "No challenge in authToken";
219 return false;
220 }
221
222 if (authToken.challenge != authChallenge) {
223 LOG(ERROR) << "Challenge in authToken doesn't match the challenge we created";
224 return false;
225 }
226 return true;
227 }
228
229 // Note that the Epoch for timestamps in HardwareAuthToken is at the
230 // discretion of the vendor:
231 //
232 // "[...] since some starting point (generally the most recent device
233 // boot) which all of the applications within one secure environment
234 // must agree upon."
235 //
236 // Therefore, if this software implementation is used on a device which isn't
237 // the emulator then the assumption that the epoch is the same as used in
238 // clockGetTime above will not hold. This is OK as this software
239 // implementation should never be used on a real device.
240 //
241 Timestamp now = clockGetTime();
242 if (authToken.timestamp > now) {
243 LOG(ERROR) << "Timestamp in authToken (" << authToken.timestamp
244 << ") is in the future (now: " << now << ")";
245 return false;
246 }
247 if (now > authToken.timestamp + profile.timeoutMillis) {
248 LOG(ERROR) << "Deadline for authToken (" << authToken.timestamp << " + "
249 << profile.timeoutMillis << " = "
250 << (authToken.timestamp + profile.timeoutMillis)
251 << ") is in the past (now: " << now << ")";
252 return false;
253 }
254
255 return true;
256}
257
258Return<void> IdentityCredential::startRetrieval(
259 const hidl_vec<SecureAccessControlProfile>& accessControlProfiles,
260 const HardwareAuthToken& authToken, const hidl_vec<uint8_t>& itemsRequest,
261 const hidl_vec<uint8_t>& sessionTranscript, const hidl_vec<uint8_t>& readerSignature,
262 const hidl_vec<uint16_t>& requestCounts, startRetrieval_cb _hidl_cb) {
263 if (sessionTranscript.size() > 0) {
264 auto [item, _, message] = cppbor::parse(sessionTranscript);
265 if (item == nullptr) {
266 _hidl_cb(support::result(ResultCode::INVALID_DATA,
267 "SessionTranscript contains invalid CBOR"));
268 return Void();
269 }
270 sessionTranscriptItem_ = std::move(item);
271 }
272 if (numStartRetrievalCalls_ > 0) {
273 if (sessionTranscript_ != vector<uint8_t>(sessionTranscript)) {
274 _hidl_cb(support::result(
275 ResultCode::SESSION_TRANSCRIPT_MISMATCH,
276 "Passed-in SessionTranscript doesn't match previously used SessionTranscript"));
277 return Void();
278 }
279 }
280 sessionTranscript_ = sessionTranscript;
281
282 // If there is a signature, validate that it was made with the top-most key in the
283 // certificate chain embedded in the COSE_Sign1 structure.
284 optional<vector<uint8_t>> readerCertificateChain;
285 if (readerSignature.size() > 0) {
286 readerCertificateChain = support::coseSignGetX5Chain(readerSignature);
287 if (!readerCertificateChain) {
288 _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
289 "Unable to get reader certificate chain from COSE_Sign1"));
290 return Void();
291 }
292
293 if (!support::certificateChainValidate(readerCertificateChain.value())) {
294 _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
295 "Error validating reader certificate chain"));
296 return Void();
297 }
298
299 optional<vector<uint8_t>> readerPublicKey =
300 support::certificateChainGetTopMostKey(readerCertificateChain.value());
301 if (!readerPublicKey) {
302 _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
303 "Unable to get public key from reader certificate chain"));
304 return Void();
305 }
306
307 const vector<uint8_t>& itemsRequestBytes = itemsRequest;
308 vector<uint8_t> dataThatWasSigned = cppbor::Array()
309 .add("ReaderAuthentication")
310 .add(sessionTranscriptItem_->clone())
311 .add(cppbor::Semantic(24, itemsRequestBytes))
312 .encode();
313 if (!support::coseCheckEcDsaSignature(readerSignature,
314 dataThatWasSigned, // detached content
315 readerPublicKey.value())) {
316 _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
317 "readerSignature check failed"));
318 return Void();
319 }
320 }
321
322 // Here's where we would validate the passed-in |authToken| to assure ourselves
323 // that it comes from the e.g. biometric hardware and wasn't made up by an attacker.
324 //
325 // However this involves calculating the MAC. However this requires access
326 // to the key needed to a pre-shared key which we don't have...
327 //
328
329 // To prevent replay-attacks, we check that the public part of the ephemeral
330 // key we previously created, is present in the DeviceEngagement part of
331 // SessionTranscript as a COSE_Key, in uncompressed form.
332 //
333 // We do this by just searching for the X and Y coordinates.
334 if (sessionTranscript.size() > 0) {
335 const cppbor::Array* array = sessionTranscriptItem_->asArray();
336 if (array == nullptr || array->size() != 2) {
337 _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
338 "SessionTranscript is not an array with two items"));
339 return Void();
340 }
341 const cppbor::Semantic* taggedEncodedDE = (*array)[0]->asSemantic();
342 if (taggedEncodedDE == nullptr || taggedEncodedDE->value() != 24) {
343 _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
344 "First item in SessionTranscript array is not a "
345 "semantic with value 24"));
346 return Void();
347 }
348 const cppbor::Bstr* encodedDE = (taggedEncodedDE->child())->asBstr();
349 if (encodedDE == nullptr) {
350 _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
351 "Child of semantic in first item in SessionTranscript "
352 "array is not a bstr"));
353 return Void();
354 }
355 const vector<uint8_t>& bytesDE = encodedDE->value();
356
357 auto [getXYSuccess, ePubX, ePubY] = support::ecPublicKeyGetXandY(ephemeralPublicKey_);
358 if (!getXYSuccess) {
359 _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
360 "Error extracting X and Y from ePub"));
361 return Void();
362 }
363 if (sessionTranscript.size() > 0 &&
364 !(memmem(bytesDE.data(), bytesDE.size(), ePubX.data(), ePubX.size()) != nullptr &&
365 memmem(bytesDE.data(), bytesDE.size(), ePubY.data(), ePubY.size()) != nullptr)) {
366 _hidl_cb(support::result(ResultCode::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 return Void();
370 }
371 }
372
373 // itemsRequest: If non-empty, contains request data that may be signed by the
374 // reader. The content can be defined in the way appropriate for the
375 // credential, but there are three requirements that must be met to work with
376 // this HAL:
377 if (itemsRequest.size() > 0) {
378 // 1. The content must be a CBOR-encoded structure.
379 auto [item, _, message] = cppbor::parse(itemsRequest);
380 if (item == nullptr) {
381 _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
382 "Error decoding CBOR in itemsRequest: %s", message.c_str()));
383 return Void();
384 }
385
386 // 2. The CBOR structure must be a map.
387 const cppbor::Map* map = item->asMap();
388 if (map == nullptr) {
389 _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
390 "itemsRequest is not a CBOR map"));
391 return Void();
392 }
393
394 // 3. The map must contain a key "nameSpaces" whose value contains a map, as described in
395 // the example below.
396 //
397 // NameSpaces = {
398 // + NameSpace => DataElements ; Requested data elements for each NameSpace
399 // }
400 //
401 // NameSpace = tstr
402 //
403 // DataElements = {
404 // + DataElement => IntentToRetain
405 // }
406 //
407 // DataElement = tstr
408 // IntentToRetain = bool
409 //
410 // Here's an example of an |itemsRequest| CBOR value satisfying above requirements 1.
411 // through 3.:
412 //
413 // {
414 // 'docType' : 'org.iso.18013-5.2019',
415 // 'nameSpaces' : {
416 // 'org.iso.18013-5.2019' : {
417 // 'Last name' : false,
418 // 'Birth date' : false,
419 // 'First name' : false,
420 // 'Home address' : true
421 // },
422 // 'org.aamva.iso.18013-5.2019' : {
423 // 'Real Id' : false
424 // }
425 // }
426 // }
427 //
428 const cppbor::Map* nsMap = nullptr;
429 for (size_t n = 0; n < map->size(); n++) {
430 const auto& [keyItem, valueItem] = (*map)[n];
431 if (keyItem->type() == cppbor::TSTR && keyItem->asTstr()->value() == "nameSpaces" &&
432 valueItem->type() == cppbor::MAP) {
433 nsMap = valueItem->asMap();
434 break;
435 }
436 }
437 if (nsMap == nullptr) {
438 _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
439 "No nameSpaces map in top-most map"));
440 return Void();
441 }
442
443 for (size_t n = 0; n < nsMap->size(); n++) {
444 auto [nsKeyItem, nsValueItem] = (*nsMap)[n];
445 const cppbor::Tstr* nsKey = nsKeyItem->asTstr();
446 const cppbor::Map* nsInnerMap = nsValueItem->asMap();
447 if (nsKey == nullptr || nsInnerMap == nullptr) {
448 _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
449 "Type mismatch in nameSpaces map"));
450 return Void();
451 }
452 string requestedNamespace = nsKey->value();
453 vector<string> requestedKeys;
454 for (size_t m = 0; m < nsInnerMap->size(); m++) {
455 const auto& [innerMapKeyItem, innerMapValueItem] = (*nsInnerMap)[m];
456 const cppbor::Tstr* nameItem = innerMapKeyItem->asTstr();
457 const cppbor::Simple* simple = innerMapValueItem->asSimple();
458 const cppbor::Bool* intentToRetainItem =
459 (simple != nullptr) ? simple->asBool() : nullptr;
460 if (nameItem == nullptr || intentToRetainItem == nullptr) {
461 _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
462 "Type mismatch in value in nameSpaces map"));
463 return Void();
464 }
465 requestedKeys.push_back(nameItem->value());
466 }
467 requestedNameSpacesAndNames_[requestedNamespace] = requestedKeys;
468 }
469 }
470
471 // Finally, validate all the access control profiles in the requestData.
472 bool haveAuthToken = (authToken.mac.size() > 0);
473 for (const auto& profile : accessControlProfiles) {
474 if (!support::secureAccessControlProfileCheckMac(profile, storageKey_)) {
475 _hidl_cb(support::result(ResultCode::INVALID_DATA,
476 "Error checking MAC for profile with id %d", int(profile.id)));
477 return Void();
478 }
479 ResultCode accessControlCheck = ResultCode::OK;
480 if (profile.userAuthenticationRequired) {
481 if (!haveAuthToken || !checkUserAuthentication(profile, authToken, authChallenge_)) {
482 accessControlCheck = ResultCode::USER_AUTHENTICATION_FAILED;
483 }
484 } else if (profile.readerCertificate.size() > 0) {
485 if (!readerCertificateChain ||
486 !checkReaderAuthentication(profile, readerCertificateChain.value())) {
487 accessControlCheck = ResultCode::READER_AUTHENTICATION_FAILED;
488 }
489 }
490 profileIdToAccessCheckResult_[profile.id] = accessControlCheck;
491 }
492
493 deviceNameSpacesMap_ = cppbor::Map();
494 currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
495
496 requestCountsRemaining_ = requestCounts;
497 currentNameSpace_ = "";
498
499 itemsRequest_ = itemsRequest;
500
501 numStartRetrievalCalls_ += 1;
502 _hidl_cb(support::resultOK());
503 return Void();
504}
505
506Return<void> IdentityCredential::startRetrieveEntryValue(
507 const hidl_string& nameSpace, const hidl_string& name, uint32_t entrySize,
508 const hidl_vec<uint16_t>& accessControlProfileIds, startRetrieveEntryValue_cb _hidl_cb) {
509 if (name.empty()) {
510 _hidl_cb(support::result(ResultCode::INVALID_DATA, "Name cannot be empty"));
511 return Void();
512 }
513 if (nameSpace.empty()) {
514 _hidl_cb(support::result(ResultCode::INVALID_DATA, "Name space cannot be empty"));
515 return Void();
516 }
517
518 if (requestCountsRemaining_.size() == 0) {
519 _hidl_cb(support::result(ResultCode::INVALID_DATA,
520 "No more name spaces left to go through"));
521 return Void();
522 }
523
524 if (currentNameSpace_ == "") {
525 // First call.
526 currentNameSpace_ = nameSpace;
527 }
528
529 if (nameSpace == currentNameSpace_) {
530 // Same namespace.
531 if (requestCountsRemaining_[0] == 0) {
532 _hidl_cb(support::result(ResultCode::INVALID_DATA,
533 "No more entries to be retrieved in current name space"));
534 return Void();
535 }
536 requestCountsRemaining_[0] -= 1;
537 } else {
538 // New namespace.
539 if (requestCountsRemaining_[0] != 0) {
540 _hidl_cb(support::result(ResultCode::INVALID_DATA,
541 "Moved to new name space but %d entries need to be retrieved "
542 "in current name space",
543 int(requestCountsRemaining_[0])));
544 return Void();
545 }
546 if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
547 deviceNameSpacesMap_.add(currentNameSpace_,
548 std::move(currentNameSpaceDeviceNameSpacesMap_));
549 }
550 currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
551
552 requestCountsRemaining_.erase(requestCountsRemaining_.begin());
553 currentNameSpace_ = nameSpace;
554 }
555
556 // It's permissible to have an empty itemsRequest... but if non-empty you can
557 // only request what was specified in said itemsRequest. Enforce that.
558 if (itemsRequest_.size() > 0) {
559 const auto& it = requestedNameSpacesAndNames_.find(nameSpace);
560 if (it == requestedNameSpacesAndNames_.end()) {
561 _hidl_cb(support::result(ResultCode::NOT_IN_REQUEST_MESSAGE,
562 "Name space '%s' was not requested in startRetrieval",
563 nameSpace.c_str()));
564 return Void();
565 }
566 const auto& dataItemNames = it->second;
567 if (std::find(dataItemNames.begin(), dataItemNames.end(), name) == dataItemNames.end()) {
568 _hidl_cb(support::result(
569 ResultCode::NOT_IN_REQUEST_MESSAGE,
570 "Data item name '%s' in name space '%s' was not requested in startRetrieval",
571 name.c_str(), nameSpace.c_str()));
572 return Void();
573 }
574 }
575
576 // Enforce access control.
577 //
578 // Access is granted if at least one of the profiles grants access.
579 //
580 // If an item is configured without any profiles, access is denied.
581 //
582 ResultCode accessControl = ResultCode::NO_ACCESS_CONTROL_PROFILES;
583 for (auto id : accessControlProfileIds) {
584 auto search = profileIdToAccessCheckResult_.find(id);
585 if (search == profileIdToAccessCheckResult_.end()) {
586 _hidl_cb(support::result(ResultCode::INVALID_DATA,
587 "Requested entry with unvalidated profile id %d", (int(id))));
588 return Void();
589 }
590 ResultCode accessControlForProfile = search->second;
591 if (accessControlForProfile == ResultCode::OK) {
592 accessControl = ResultCode::OK;
593 break;
594 }
595 accessControl = accessControlForProfile;
596 }
597 if (accessControl != ResultCode::OK) {
598 _hidl_cb(support::result(accessControl, "Access control check failed"));
599 return Void();
600 }
601
602 entryAdditionalData_ =
603 support::entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
604
605 currentName_ = name;
606 entryRemainingBytes_ = entrySize;
607 entryValue_.resize(0);
608
609 _hidl_cb(support::resultOK());
610 return Void();
611}
612
613Return<void> IdentityCredential::retrieveEntryValue(const hidl_vec<uint8_t>& encryptedContent,
614 retrieveEntryValue_cb _hidl_cb) {
615 optional<vector<uint8_t>> content =
616 support::decryptAes128Gcm(storageKey_, encryptedContent, entryAdditionalData_);
617 if (!content) {
618 _hidl_cb(support::result(ResultCode::INVALID_DATA, "Error decrypting data"), {});
619 return Void();
620 }
621
622 size_t chunkSize = content.value().size();
623
624 if (chunkSize > entryRemainingBytes_) {
625 LOG(ERROR) << "Retrieved chunk of size " << chunkSize
626 << " is bigger than remaining space of size " << entryRemainingBytes_;
627 _hidl_cb(support::result(ResultCode::INVALID_DATA,
628 "Retrieved chunk of size %zd is bigger than remaining space "
629 "of size %zd",
630 chunkSize, entryRemainingBytes_),
631 {});
632 return Void();
633 }
634
635 entryRemainingBytes_ -= chunkSize;
636 if (entryRemainingBytes_ > 0) {
637 if (chunkSize != IdentityCredentialStore::kGcmChunkSize) {
638 _hidl_cb(support::result(ResultCode::INVALID_DATA,
639 "Retrieved non-final chunk of size %zd but expected "
640 "kGcmChunkSize which is %zd",
641 chunkSize, IdentityCredentialStore::kGcmChunkSize),
642 {});
643 return Void();
644 }
645 }
646
647 entryValue_.insert(entryValue_.end(), content.value().begin(), content.value().end());
648
649 if (entryRemainingBytes_ == 0) {
650 auto [entryValueItem, _, message] = cppbor::parse(entryValue_);
651 if (entryValueItem == nullptr) {
652 _hidl_cb(support::result(ResultCode::INVALID_DATA, "Retrieved data invalid CBOR"), {});
653 return Void();
654 }
655 currentNameSpaceDeviceNameSpacesMap_.add(currentName_, std::move(entryValueItem));
656 }
657
658 _hidl_cb(support::resultOK(), content.value());
659 return Void();
660}
661
662Return<void> IdentityCredential::finishRetrieval(const hidl_vec<uint8_t>& signingKeyBlob,
663 finishRetrieval_cb _hidl_cb) {
664 if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
665 deviceNameSpacesMap_.add(currentNameSpace_,
666 std::move(currentNameSpaceDeviceNameSpacesMap_));
667 }
668 vector<uint8_t> encodedDeviceNameSpaces = deviceNameSpacesMap_.encode();
669
670 // If there's no signing key or no sessionTranscript or no reader ephemeral
671 // public key, we return the empty MAC.
672 optional<vector<uint8_t>> mac;
673 if (signingKeyBlob.size() > 0 && sessionTranscript_.size() > 0 && readerPublicKey_.size() > 0) {
674 cppbor::Array array;
675 array.add("DeviceAuthentication");
676 array.add(sessionTranscriptItem_->clone());
677 array.add(docType_);
678 array.add(cppbor::Semantic(24, encodedDeviceNameSpaces));
679 vector<uint8_t> encodedDeviceAuthentication = array.encode();
680
681 vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
682 optional<vector<uint8_t>> signingKey =
683 support::decryptAes128Gcm(storageKey_, signingKeyBlob, docTypeAsBlob);
684 if (!signingKey) {
685 _hidl_cb(support::result(ResultCode::INVALID_DATA, "Error decrypting signingKeyBlob"),
686 {}, {});
687 return Void();
688 }
689
690 optional<vector<uint8_t>> sharedSecret =
691 support::ecdh(readerPublicKey_, signingKey.value());
692 if (!sharedSecret) {
693 _hidl_cb(support::result(ResultCode::FAILED, "Error doing ECDH"), {}, {});
694 return Void();
695 }
696
697 vector<uint8_t> salt = {0x00};
698 vector<uint8_t> info = {};
699 optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
700 if (!derivedKey) {
701 _hidl_cb(support::result(ResultCode::FAILED, "Error deriving key from shared secret"),
702 {}, {});
703 return Void();
704 }
705
706 mac = support::coseMac0(derivedKey.value(), {}, // payload
707 encodedDeviceAuthentication); // additionalData
708 if (!mac) {
709 _hidl_cb(support::result(ResultCode::FAILED, "Error MACing data"), {}, {});
710 return Void();
711 }
712 }
713
714 _hidl_cb(support::resultOK(), mac.value_or(vector<uint8_t>({})), encodedDeviceNameSpaces);
715 return Void();
716}
717
718Return<void> IdentityCredential::generateSigningKeyPair(generateSigningKeyPair_cb _hidl_cb) {
719 string serialDecimal = "0"; // TODO: set serial to something unique
720 string issuer = "Android Open Source Project";
721 string subject = "Android IdentityCredential Reference Implementation";
722 time_t validityNotBefore = time(nullptr);
723 time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
724
725 optional<vector<uint8_t>> signingKeyPKCS8 = support::createEcKeyPair();
726 if (!signingKeyPKCS8) {
727 _hidl_cb(support::result(ResultCode::FAILED, "Error creating signingKey"), {}, {});
728 return Void();
729 }
730
731 optional<vector<uint8_t>> signingPublicKey =
732 support::ecKeyPairGetPublicKey(signingKeyPKCS8.value());
733 if (!signingPublicKey) {
734 _hidl_cb(support::result(ResultCode::FAILED, "Error getting public part of signingKey"), {},
735 {});
736 return Void();
737 }
738
739 optional<vector<uint8_t>> signingKey = support::ecKeyPairGetPrivateKey(signingKeyPKCS8.value());
740 if (!signingKey) {
741 _hidl_cb(support::result(ResultCode::FAILED, "Error getting private part of signingKey"),
742 {}, {});
743 return Void();
744 }
745
746 optional<vector<uint8_t>> certificate = support::ecPublicKeyGenerateCertificate(
747 signingPublicKey.value(), credentialPrivKey_, serialDecimal, issuer, subject,
748 validityNotBefore, validityNotAfter);
749 if (!certificate) {
750 _hidl_cb(support::result(ResultCode::FAILED, "Error creating signingKey"), {}, {});
751 return Void();
752 }
753
754 optional<vector<uint8_t>> nonce = support::getRandom(12);
755 if (!nonce) {
756 _hidl_cb(support::result(ResultCode::FAILED, "Error getting random"), {}, {});
757 return Void();
758 }
759 vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
760 optional<vector<uint8_t>> encryptedSigningKey = support::encryptAes128Gcm(
761 storageKey_, nonce.value(), signingKey.value(), docTypeAsBlob);
762 if (!encryptedSigningKey) {
763 _hidl_cb(support::result(ResultCode::FAILED, "Error encrypting signingKey"), {}, {});
764 return Void();
765 }
766 _hidl_cb(support::resultOK(), encryptedSigningKey.value(), certificate.value());
767 return Void();
768}
769
770} // namespace implementation
771} // namespace identity
772} // namespace hardware
773} // namespace android