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