1 /*
2 * Copyright (C) 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 #define LOG_TAG "VtsHalIdentityEndToEndTest"
17
18 #include <aidl/Gtest.h>
19 #include <aidl/Vintf.h>
20 #include <android-base/logging.h>
21 #include <android/hardware/identity/IIdentityCredentialStore.h>
22 #include <android/hardware/identity/support/IdentityCredentialSupport.h>
23 #include <binder/IServiceManager.h>
24 #include <binder/ProcessState.h>
25 #include <cppbor.h>
26 #include <cppbor_parse.h>
27 #include <gtest/gtest.h>
28 #include <future>
29 #include <map>
30 #include <tuple>
31
32 #include "Util.h"
33
34 namespace android::hardware::identity {
35
36 using std::endl;
37 using std::make_tuple;
38 using std::map;
39 using std::optional;
40 using std::string;
41 using std::tuple;
42 using std::vector;
43
44 using ::android::sp;
45 using ::android::String16;
46 using ::android::binder::Status;
47
48 using ::android::hardware::keymaster::HardwareAuthToken;
49 using ::android::hardware::keymaster::VerificationToken;
50
51 using test_utils::validateAttestationCertificate;
52
53 class EndToEndTests : public testing::TestWithParam<std::string> {
54 public:
SetUp()55 virtual void SetUp() override {
56 credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
57 String16(GetParam().c_str()));
58 ASSERT_NE(credentialStore_, nullptr);
59 halApiVersion_ = credentialStore_->getInterfaceVersion();
60 }
61
62 sp<IIdentityCredentialStore> credentialStore_;
63 int halApiVersion_;
64 };
65
TEST_P(EndToEndTests,hardwareInformation)66 TEST_P(EndToEndTests, hardwareInformation) {
67 HardwareInformation info;
68 ASSERT_TRUE(credentialStore_->getHardwareInformation(&info).isOk());
69 ASSERT_GT(info.credentialStoreName.size(), 0);
70 ASSERT_GT(info.credentialStoreAuthorName.size(), 0);
71 ASSERT_GE(info.dataChunkSize, 256);
72 }
73
74 tuple<bool, string, vector<uint8_t>, vector<uint8_t>, vector<uint8_t>>
extractFromTestCredentialData(const vector<uint8_t> & credentialData)75 extractFromTestCredentialData(const vector<uint8_t>& credentialData) {
76 string docType;
77 vector<uint8_t> storageKey;
78 vector<uint8_t> credentialPrivKey;
79 vector<uint8_t> sha256Pop;
80
81 auto [item, _, message] = cppbor::parse(credentialData);
82 if (item == nullptr) {
83 return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
84 }
85
86 const cppbor::Array* arrayItem = item->asArray();
87 if (arrayItem == nullptr || arrayItem->size() != 3) {
88 return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
89 }
90
91 const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
92 const cppbor::Bool* testCredentialItem =
93 ((*arrayItem)[1]->asSimple() != nullptr ? ((*arrayItem)[1]->asSimple()->asBool())
94 : nullptr);
95 const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
96 if (docTypeItem == nullptr || testCredentialItem == nullptr ||
97 encryptedCredentialKeysItem == nullptr) {
98 return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
99 }
100
101 docType = docTypeItem->value();
102
103 vector<uint8_t> hardwareBoundKey = support::getTestHardwareBoundKey();
104 const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
105 const vector<uint8_t> docTypeVec(docType.begin(), docType.end());
106 optional<vector<uint8_t>> decryptedCredentialKeys =
107 support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
108 if (!decryptedCredentialKeys) {
109 return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
110 }
111
112 auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
113 if (dckItem == nullptr) {
114 return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
115 }
116 const cppbor::Array* dckArrayItem = dckItem->asArray();
117 if (dckArrayItem == nullptr) {
118 return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
119 }
120 if (dckArrayItem->size() < 2) {
121 return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
122 }
123 const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
124 const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
125 if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
126 return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
127 }
128 storageKey = storageKeyItem->value();
129 credentialPrivKey = credentialPrivKeyItem->value();
130 if (dckArrayItem->size() == 3) {
131 const cppbor::Bstr* sha256PopItem = (*dckArrayItem)[2]->asBstr();
132 if (sha256PopItem == nullptr) {
133 return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
134 }
135 sha256Pop = sha256PopItem->value();
136 }
137 return make_tuple(true, docType, storageKey, credentialPrivKey, sha256Pop);
138 }
139
TEST_P(EndToEndTests,createAndRetrieveCredential)140 TEST_P(EndToEndTests, createAndRetrieveCredential) {
141 // First, generate a key-pair for the reader since its public key will be
142 // part of the request data.
143 vector<uint8_t> readerKey;
144 optional<vector<uint8_t>> readerCertificate =
145 test_utils::generateReaderCertificate("1234", &readerKey);
146 ASSERT_TRUE(readerCertificate);
147
148 // Make the portrait image really big (just shy of 256 KiB) to ensure that
149 // the chunking code gets exercised.
150 vector<uint8_t> portraitImage;
151 test_utils::setImageData(portraitImage);
152
153 // Access control profiles:
154 const vector<test_utils::TestProfile> testProfiles = {// Profile 0 (reader authentication)
155 {0, readerCertificate.value(), false, 0},
156 // Profile 1 (no authentication)
157 {1, {}, false, 0}};
158
159 // It doesn't matter since no user auth is needed in this particular test,
160 // but for good measure, clear out the tokens we pass to the HAL.
161 HardwareAuthToken authToken;
162 VerificationToken verificationToken;
163 authToken.challenge = 0;
164 authToken.userId = 0;
165 authToken.authenticatorId = 0;
166 authToken.authenticatorType = ::android::hardware::keymaster::HardwareAuthenticatorType::NONE;
167 authToken.timestamp.milliSeconds = 0;
168 authToken.mac.clear();
169 verificationToken.challenge = 0;
170 verificationToken.timestamp.milliSeconds = 0;
171 verificationToken.securityLevel = ::android::hardware::keymaster::SecurityLevel::SOFTWARE;
172 verificationToken.mac.clear();
173
174 // Here's the actual test data:
175 const vector<test_utils::TestEntryData> testEntries = {
176 {"PersonalData", "Last name", string("Turing"), vector<int32_t>{0, 1}},
177 {"PersonalData", "Birth date", string("19120623"), vector<int32_t>{0, 1}},
178 {"PersonalData", "First name", string("Alan"), vector<int32_t>{0, 1}},
179 {"PersonalData", "Home address", string("Maida Vale, London, England"),
180 vector<int32_t>{0}},
181 {"Image", "Portrait image", portraitImage, vector<int32_t>{0, 1}},
182 };
183 const vector<int32_t> testEntriesEntryCounts = {static_cast<int32_t>(testEntries.size() - 1),
184 1u};
185 HardwareInformation hwInfo;
186 ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
187
188 string cborPretty;
189 sp<IWritableIdentityCredential> writableCredential;
190 ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
191 true /* testCredential */));
192
193 string challenge = "attestationChallenge";
194 test_utils::AttestationData attData(writableCredential, challenge,
195 {1} /* atteestationApplicationId */);
196 ASSERT_TRUE(attData.result.isOk())
197 << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
198
199 validateAttestationCertificate(attData.attestationCertificate, attData.attestationChallenge,
200 attData.attestationApplicationId, true);
201
202 // This is kinda of a hack but we need to give the size of
203 // ProofOfProvisioning that we'll expect to receive.
204 const int32_t expectedProofOfProvisioningSize = 262861 - 326 + readerCertificate.value().size();
205 // OK to fail, not available in v1 HAL
206 writableCredential->setExpectedProofOfProvisioningSize(expectedProofOfProvisioningSize);
207 ASSERT_TRUE(
208 writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts)
209 .isOk());
210
211 optional<vector<SecureAccessControlProfile>> secureProfiles =
212 test_utils::addAccessControlProfiles(writableCredential, testProfiles);
213 ASSERT_TRUE(secureProfiles);
214
215 // Uses TestEntryData* pointer as key and values are the encrypted blobs. This
216 // is a little hacky but it works well enough.
217 map<const test_utils::TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
218
219 for (const auto& entry : testEntries) {
220 ASSERT_TRUE(test_utils::addEntry(writableCredential, entry, hwInfo.dataChunkSize,
221 encryptedBlobs, true));
222 }
223
224 vector<uint8_t> credentialData;
225 vector<uint8_t> proofOfProvisioningSignature;
226 ASSERT_TRUE(
227 writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature)
228 .isOk());
229
230 // Validate the proofOfProvisioning which was returned
231 optional<vector<uint8_t>> proofOfProvisioning =
232 support::coseSignGetPayload(proofOfProvisioningSignature);
233 ASSERT_TRUE(proofOfProvisioning);
234 cborPretty = cppbor::prettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
235 EXPECT_EQ(
236 "[\n"
237 " 'ProofOfProvisioning',\n"
238 " 'org.iso.18013-5.2019.mdl',\n"
239 " [\n"
240 " {\n"
241 " 'id' : 0,\n"
242 " 'readerCertificate' : <not printed>,\n"
243 " },\n"
244 " {\n"
245 " 'id' : 1,\n"
246 " },\n"
247 " ],\n"
248 " {\n"
249 " 'PersonalData' : [\n"
250 " {\n"
251 " 'name' : 'Last name',\n"
252 " 'value' : 'Turing',\n"
253 " 'accessControlProfiles' : [0, 1, ],\n"
254 " },\n"
255 " {\n"
256 " 'name' : 'Birth date',\n"
257 " 'value' : '19120623',\n"
258 " 'accessControlProfiles' : [0, 1, ],\n"
259 " },\n"
260 " {\n"
261 " 'name' : 'First name',\n"
262 " 'value' : 'Alan',\n"
263 " 'accessControlProfiles' : [0, 1, ],\n"
264 " },\n"
265 " {\n"
266 " 'name' : 'Home address',\n"
267 " 'value' : 'Maida Vale, London, England',\n"
268 " 'accessControlProfiles' : [0, ],\n"
269 " },\n"
270 " ],\n"
271 " 'Image' : [\n"
272 " {\n"
273 " 'name' : 'Portrait image',\n"
274 " 'value' : <bstr size=262134 sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
275 " 'accessControlProfiles' : [0, 1, ],\n"
276 " },\n"
277 " ],\n"
278 " },\n"
279 " true,\n"
280 "]",
281 cborPretty);
282
283 optional<vector<uint8_t>> credentialPubKey = support::certificateChainGetTopMostKey(
284 attData.attestationCertificate[0].encodedCertificate);
285 ASSERT_TRUE(credentialPubKey);
286 EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
287 {}, // Additional data
288 credentialPubKey.value()));
289 writableCredential = nullptr;
290
291 // Extract doctype, storage key, and credentialPrivKey from credentialData... this works
292 // only because we asked for a test-credential meaning that the HBK is all zeroes.
293 auto [exSuccess, exDocType, exStorageKey, exCredentialPrivKey, exSha256Pop] =
294 extractFromTestCredentialData(credentialData);
295
296 ASSERT_TRUE(exSuccess);
297 ASSERT_EQ(exDocType, "org.iso.18013-5.2019.mdl");
298 // ... check that the public key derived from the private key matches what was
299 // in the certificate.
300 optional<vector<uint8_t>> exCredentialKeyPair =
301 support::ecPrivateKeyToKeyPair(exCredentialPrivKey);
302 ASSERT_TRUE(exCredentialKeyPair);
303 optional<vector<uint8_t>> exCredentialPubKey =
304 support::ecKeyPairGetPublicKey(exCredentialKeyPair.value());
305 ASSERT_TRUE(exCredentialPubKey);
306 ASSERT_EQ(exCredentialPubKey.value(), credentialPubKey.value());
307
308 // Starting with API version 3 (feature version 202101) we require SHA-256(ProofOfProvisioning)
309 // to be in CredentialKeys (which is stored encrypted in CredentialData). Check
310 // that it's there with the expected value.
311 if (halApiVersion_ >= 3) {
312 ASSERT_EQ(exSha256Pop, support::sha256(proofOfProvisioning.value()));
313 }
314
315 // Now that the credential has been provisioned, read it back and check the
316 // correct data is returned.
317 sp<IIdentityCredential> credential;
318 ASSERT_TRUE(credentialStore_
319 ->getCredential(
320 CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
321 credentialData, &credential)
322 .isOk());
323 ASSERT_NE(credential, nullptr);
324
325 optional<vector<uint8_t>> readerEphemeralKeyPair = support::createEcKeyPair();
326 ASSERT_TRUE(readerEphemeralKeyPair);
327 optional<vector<uint8_t>> readerEphemeralPublicKey =
328 support::ecKeyPairGetPublicKey(readerEphemeralKeyPair.value());
329 ASSERT_TRUE(credential->setReaderEphemeralPublicKey(readerEphemeralPublicKey.value()).isOk());
330
331 vector<uint8_t> ephemeralKeyPair;
332 ASSERT_TRUE(credential->createEphemeralKeyPair(&ephemeralKeyPair).isOk());
333 optional<vector<uint8_t>> ephemeralPublicKey = support::ecKeyPairGetPublicKey(ephemeralKeyPair);
334
335 // Calculate requestData field and sign it with the reader key.
336 auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ephemeralPublicKey.value());
337 ASSERT_TRUE(getXYSuccess);
338 cppbor::Map deviceEngagement = cppbor::Map().add("ephX", ephX).add("ephY", ephY);
339 vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
340 vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
341 cppbor::Array sessionTranscript = cppbor::Array()
342 .add(cppbor::SemanticTag(24, deviceEngagementBytes))
343 .add(cppbor::SemanticTag(24, eReaderPubBytes));
344 vector<uint8_t> sessionTranscriptEncoded = sessionTranscript.encode();
345
346 vector<uint8_t> itemsRequestBytes =
347 cppbor::Map("nameSpaces",
348 cppbor::Map()
349 .add("PersonalData", cppbor::Map()
350 .add("Last name", false)
351 .add("Birth date", false)
352 .add("First name", false)
353 .add("Home address", true))
354 .add("Image", cppbor::Map().add("Portrait image", false)))
355 .encode();
356 cborPretty = cppbor::prettyPrint(itemsRequestBytes, 32, {"EphemeralPublicKey"});
357 EXPECT_EQ(
358 "{\n"
359 " 'nameSpaces' : {\n"
360 " 'PersonalData' : {\n"
361 " 'Last name' : false,\n"
362 " 'Birth date' : false,\n"
363 " 'First name' : false,\n"
364 " 'Home address' : true,\n"
365 " },\n"
366 " 'Image' : {\n"
367 " 'Portrait image' : false,\n"
368 " },\n"
369 " },\n"
370 "}",
371 cborPretty);
372 vector<uint8_t> encodedReaderAuthentication =
373 cppbor::Array()
374 .add("ReaderAuthentication")
375 .add(sessionTranscript.clone())
376 .add(cppbor::SemanticTag(24, itemsRequestBytes))
377 .encode();
378 vector<uint8_t> encodedReaderAuthenticationBytes =
379 cppbor::SemanticTag(24, encodedReaderAuthentication).encode();
380 optional<vector<uint8_t>> readerSignature =
381 support::coseSignEcDsa(readerKey, {}, // content
382 encodedReaderAuthenticationBytes, // detached content
383 readerCertificate.value());
384 ASSERT_TRUE(readerSignature);
385
386 // Generate the key that will be used to sign AuthenticatedData.
387 vector<uint8_t> signingKeyBlob;
388 Certificate signingKeyCertificate;
389 ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
390 optional<vector<uint8_t>> signingPubKey =
391 support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
392 EXPECT_TRUE(signingPubKey);
393 test_utils::verifyAuthKeyCertificate(signingKeyCertificate.encodedCertificate);
394
395 // Since we're using a test-credential we know storageKey meaning we can get the
396 // private key. Do this, derive the public key from it, and check this matches what
397 // is in the certificate...
398 const vector<uint8_t> exDocTypeVec(exDocType.begin(), exDocType.end());
399 optional<vector<uint8_t>> exSigningPrivKey =
400 support::decryptAes128Gcm(exStorageKey, signingKeyBlob, exDocTypeVec);
401 ASSERT_TRUE(exSigningPrivKey);
402 optional<vector<uint8_t>> exSigningKeyPair =
403 support::ecPrivateKeyToKeyPair(exSigningPrivKey.value());
404 ASSERT_TRUE(exSigningKeyPair);
405 optional<vector<uint8_t>> exSigningPubKey =
406 support::ecKeyPairGetPublicKey(exSigningKeyPair.value());
407 ASSERT_TRUE(exSigningPubKey);
408 ASSERT_EQ(exSigningPubKey.value(), signingPubKey.value());
409
410 vector<RequestNamespace> requestedNamespaces = test_utils::buildRequestNamespaces(testEntries);
411 // OK to fail, not available in v1 HAL
412 credential->setRequestedNamespaces(requestedNamespaces);
413 // OK to fail, not available in v1 HAL
414 credential->setVerificationToken(verificationToken);
415 ASSERT_TRUE(credential
416 ->startRetrieval(secureProfiles.value(), authToken, itemsRequestBytes,
417 signingKeyBlob, sessionTranscriptEncoded,
418 readerSignature.value(), testEntriesEntryCounts)
419 .isOk());
420
421 for (const auto& entry : testEntries) {
422 ASSERT_TRUE(credential
423 ->startRetrieveEntryValue(entry.nameSpace, entry.name,
424 entry.valueCbor.size(), entry.profileIds)
425 .isOk());
426
427 auto it = encryptedBlobs.find(&entry);
428 ASSERT_NE(it, encryptedBlobs.end());
429 const vector<vector<uint8_t>>& encryptedChunks = it->second;
430
431 vector<uint8_t> content;
432 for (const auto& encryptedChunk : encryptedChunks) {
433 vector<uint8_t> chunk;
434 ASSERT_TRUE(credential->retrieveEntryValue(encryptedChunk, &chunk).isOk());
435 content.insert(content.end(), chunk.begin(), chunk.end());
436 }
437 EXPECT_EQ(content, entry.valueCbor);
438
439 // TODO: also use |exStorageKey| to decrypt data and check it's the same as whatt
440 // the HAL returns...
441 }
442
443 vector<uint8_t> mac;
444 vector<uint8_t> ecdsaSignature;
445 vector<uint8_t> deviceNameSpacesEncoded;
446 // API version 5 (feature version 202301) returns both MAC and ECDSA signature.
447 if (halApiVersion_ >= 5) {
448 ASSERT_TRUE(credential
449 ->finishRetrievalWithSignature(&mac, &deviceNameSpacesEncoded,
450 &ecdsaSignature)
451 .isOk());
452 ASSERT_GT(ecdsaSignature.size(), 0);
453 } else {
454 ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
455 }
456 cborPretty = cppbor::prettyPrint(deviceNameSpacesEncoded, 32, {});
457 ASSERT_EQ(
458 "{\n"
459 " 'PersonalData' : {\n"
460 " 'Last name' : 'Turing',\n"
461 " 'Birth date' : '19120623',\n"
462 " 'First name' : 'Alan',\n"
463 " 'Home address' : 'Maida Vale, London, England',\n"
464 " },\n"
465 " 'Image' : {\n"
466 " 'Portrait image' : <bstr size=262134 "
467 "sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
468 " },\n"
469 "}",
470 cborPretty);
471
472 string docType = "org.iso.18013-5.2019.mdl";
473 optional<vector<uint8_t>> readerEphemeralPrivateKey =
474 support::ecKeyPairGetPrivateKey(readerEphemeralKeyPair.value());
475 optional<vector<uint8_t>> eMacKey =
476 support::calcEMacKey(readerEphemeralPrivateKey.value(), // Private Key
477 signingPubKey.value(), // Public Key
478 cppbor::SemanticTag(24, sessionTranscript.encode())
479 .encode()); // SessionTranscriptBytes
480 optional<vector<uint8_t>> calculatedMac =
481 support::calcMac(sessionTranscript.encode(), // SessionTranscript
482 docType, // DocType
483 deviceNameSpacesEncoded, // DeviceNamespaces
484 eMacKey.value()); // EMacKey
485 ASSERT_TRUE(calculatedMac);
486 EXPECT_EQ(mac, calculatedMac);
487
488 if (ecdsaSignature.size() > 0) {
489 vector<uint8_t> encodedDeviceAuthentication =
490 cppbor::Array()
491 .add("DeviceAuthentication")
492 .add(sessionTranscript.clone())
493 .add(docType)
494 .add(cppbor::SemanticTag(24, deviceNameSpacesEncoded))
495 .encode();
496 vector<uint8_t> deviceAuthenticationBytes =
497 cppbor::SemanticTag(24, encodedDeviceAuthentication).encode();
498 EXPECT_TRUE(support::coseCheckEcDsaSignature(ecdsaSignature,
499 deviceAuthenticationBytes, // Detached content
500 signingPubKey.value()));
501 }
502
503 // Also perform an additional empty request. This is what mDL applications
504 // are envisioned to do - one call to get the data elements, another to get
505 // an empty DeviceSignedItems and corresponding MAC.
506 //
507 credential->setRequestedNamespaces({}); // OK to fail, not available in v1 HAL
508 ASSERT_TRUE(credential
509 ->startRetrieval(
510 secureProfiles.value(), authToken, {}, // itemsRequestBytes
511 signingKeyBlob, sessionTranscriptEncoded, {}, // readerSignature,
512 testEntriesEntryCounts)
513 .isOk());
514 // API version 5 (feature version 202301) returns both MAC and ECDSA signature.
515 if (halApiVersion_ >= 5) {
516 ASSERT_TRUE(credential
517 ->finishRetrievalWithSignature(&mac, &deviceNameSpacesEncoded,
518 &ecdsaSignature)
519 .isOk());
520 ASSERT_GT(ecdsaSignature.size(), 0);
521 } else {
522 ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
523 }
524 cborPretty = cppbor::prettyPrint(deviceNameSpacesEncoded, 32, {});
525 ASSERT_EQ("{}", cborPretty);
526 // Calculate DeviceAuthentication and MAC (MACing key hasn't changed)
527 calculatedMac = support::calcMac(sessionTranscript.encode(), // SessionTranscript
528 docType, // DocType
529 deviceNameSpacesEncoded, // DeviceNamespaces
530 eMacKey.value()); // EMacKey
531 ASSERT_TRUE(calculatedMac);
532 EXPECT_EQ(mac, calculatedMac);
533
534 if (ecdsaSignature.size() > 0) {
535 vector<uint8_t> encodedDeviceAuthentication =
536 cppbor::Array()
537 .add("DeviceAuthentication")
538 .add(sessionTranscript.clone())
539 .add(docType)
540 .add(cppbor::SemanticTag(24, deviceNameSpacesEncoded))
541 .encode();
542 vector<uint8_t> deviceAuthenticationBytes =
543 cppbor::SemanticTag(24, encodedDeviceAuthentication).encode();
544 EXPECT_TRUE(support::coseCheckEcDsaSignature(ecdsaSignature,
545 deviceAuthenticationBytes, // Detached content
546 signingPubKey.value()));
547 }
548
549 // Some mDL apps might send a request but with a single empty
550 // namespace. Check that too.
551 RequestNamespace emptyRequestNS;
552 emptyRequestNS.namespaceName = "PersonalData";
553 credential->setRequestedNamespaces({emptyRequestNS}); // OK to fail, not available in v1 HAL
554 ASSERT_TRUE(credential
555 ->startRetrieval(
556 secureProfiles.value(), authToken, {}, // itemsRequestBytes
557 signingKeyBlob, sessionTranscriptEncoded, {}, // readerSignature,
558 testEntriesEntryCounts)
559 .isOk());
560 // API version 5 (feature version 202301) returns both MAC and ECDSA signature.
561 if (halApiVersion_ >= 5) {
562 ASSERT_TRUE(credential
563 ->finishRetrievalWithSignature(&mac, &deviceNameSpacesEncoded,
564 &ecdsaSignature)
565 .isOk());
566 ASSERT_GT(ecdsaSignature.size(), 0);
567 } else {
568 ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
569 }
570 cborPretty = cppbor::prettyPrint(deviceNameSpacesEncoded, 32, {});
571 ASSERT_EQ("{}", cborPretty);
572 // Calculate DeviceAuthentication and MAC (MACing key hasn't changed)
573 calculatedMac = support::calcMac(sessionTranscript.encode(), // SessionTranscript
574 docType, // DocType
575 deviceNameSpacesEncoded, // DeviceNamespaces
576 eMacKey.value()); // EMacKey
577 ASSERT_TRUE(calculatedMac);
578 EXPECT_EQ(mac, calculatedMac);
579
580 if (ecdsaSignature.size() > 0) {
581 vector<uint8_t> encodedDeviceAuthentication =
582 cppbor::Array()
583 .add("DeviceAuthentication")
584 .add(sessionTranscript.clone())
585 .add(docType)
586 .add(cppbor::SemanticTag(24, deviceNameSpacesEncoded))
587 .encode();
588 vector<uint8_t> deviceAuthenticationBytes =
589 cppbor::SemanticTag(24, encodedDeviceAuthentication).encode();
590 EXPECT_TRUE(support::coseCheckEcDsaSignature(ecdsaSignature,
591 deviceAuthenticationBytes, // Detached content
592 signingPubKey.value()));
593 }
594 }
595
TEST_P(EndToEndTests,noSessionEncryption)596 TEST_P(EndToEndTests, noSessionEncryption) {
597 if (halApiVersion_ < 5) {
598 GTEST_SKIP() << "Need HAL API version 5, have " << halApiVersion_;
599 }
600
601 const vector<test_utils::TestProfile> testProfiles = {// Profile 0 (no authentication)
602 {0, {}, false, 0}};
603
604 HardwareAuthToken authToken;
605 VerificationToken verificationToken;
606 authToken.challenge = 0;
607 authToken.userId = 0;
608 authToken.authenticatorId = 0;
609 authToken.authenticatorType = ::android::hardware::keymaster::HardwareAuthenticatorType::NONE;
610 authToken.timestamp.milliSeconds = 0;
611 authToken.mac.clear();
612 verificationToken.challenge = 0;
613 verificationToken.timestamp.milliSeconds = 0;
614 verificationToken.securityLevel = ::android::hardware::keymaster::SecurityLevel::SOFTWARE;
615 verificationToken.mac.clear();
616
617 // Here's the actual test data:
618 const vector<test_utils::TestEntryData> testEntries = {
619 {"PersonalData", "Last name", string("Turing"), vector<int32_t>{0}},
620 {"PersonalData", "Birth date", string("19120623"), vector<int32_t>{0}},
621 {"PersonalData", "First name", string("Alan"), vector<int32_t>{0}},
622 };
623 const vector<int32_t> testEntriesEntryCounts = {3};
624 HardwareInformation hwInfo;
625 ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
626
627 string cborPretty;
628 sp<IWritableIdentityCredential> writableCredential;
629 ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
630 true /* testCredential */));
631
632 string challenge = "attestationChallenge";
633 test_utils::AttestationData attData(writableCredential, challenge,
634 {1} /* atteestationApplicationId */);
635 ASSERT_TRUE(attData.result.isOk())
636 << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
637
638 // This is kinda of a hack but we need to give the size of
639 // ProofOfProvisioning that we'll expect to receive.
640 const int32_t expectedProofOfProvisioningSize = 230;
641 // OK to fail, not available in v1 HAL
642 writableCredential->setExpectedProofOfProvisioningSize(expectedProofOfProvisioningSize);
643 ASSERT_TRUE(
644 writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts)
645 .isOk());
646
647 optional<vector<SecureAccessControlProfile>> secureProfiles =
648 test_utils::addAccessControlProfiles(writableCredential, testProfiles);
649 ASSERT_TRUE(secureProfiles);
650
651 // Uses TestEntryData* pointer as key and values are the encrypted blobs. This
652 // is a little hacky but it works well enough.
653 map<const test_utils::TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
654
655 for (const auto& entry : testEntries) {
656 ASSERT_TRUE(test_utils::addEntry(writableCredential, entry, hwInfo.dataChunkSize,
657 encryptedBlobs, true));
658 }
659
660 vector<uint8_t> credentialData;
661 vector<uint8_t> proofOfProvisioningSignature;
662 ASSERT_TRUE(
663 writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature)
664 .isOk());
665
666 // Validate the proofOfProvisioning which was returned
667 optional<vector<uint8_t>> proofOfProvisioning =
668 support::coseSignGetPayload(proofOfProvisioningSignature);
669 ASSERT_TRUE(proofOfProvisioning);
670 cborPretty = cppbor::prettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
671 EXPECT_EQ(
672 "[\n"
673 " 'ProofOfProvisioning',\n"
674 " 'org.iso.18013-5.2019.mdl',\n"
675 " [\n"
676 " {\n"
677 " 'id' : 0,\n"
678 " },\n"
679 " ],\n"
680 " {\n"
681 " 'PersonalData' : [\n"
682 " {\n"
683 " 'name' : 'Last name',\n"
684 " 'value' : 'Turing',\n"
685 " 'accessControlProfiles' : [0, ],\n"
686 " },\n"
687 " {\n"
688 " 'name' : 'Birth date',\n"
689 " 'value' : '19120623',\n"
690 " 'accessControlProfiles' : [0, ],\n"
691 " },\n"
692 " {\n"
693 " 'name' : 'First name',\n"
694 " 'value' : 'Alan',\n"
695 " 'accessControlProfiles' : [0, ],\n"
696 " },\n"
697 " ],\n"
698 " },\n"
699 " true,\n"
700 "]",
701 cborPretty);
702
703 optional<vector<uint8_t>> credentialPubKey = support::certificateChainGetTopMostKey(
704 attData.attestationCertificate[0].encodedCertificate);
705 ASSERT_TRUE(credentialPubKey);
706 EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
707 {}, // Additional data
708 credentialPubKey.value()));
709 writableCredential = nullptr;
710
711 // Extract doctype, storage key, and credentialPrivKey from credentialData... this works
712 // only because we asked for a test-credential meaning that the HBK is all zeroes.
713 auto [exSuccess, exDocType, exStorageKey, exCredentialPrivKey, exSha256Pop] =
714 extractFromTestCredentialData(credentialData);
715
716 ASSERT_TRUE(exSuccess);
717 ASSERT_EQ(exDocType, "org.iso.18013-5.2019.mdl");
718 // ... check that the public key derived from the private key matches what was
719 // in the certificate.
720 optional<vector<uint8_t>> exCredentialKeyPair =
721 support::ecPrivateKeyToKeyPair(exCredentialPrivKey);
722 ASSERT_TRUE(exCredentialKeyPair);
723 optional<vector<uint8_t>> exCredentialPubKey =
724 support::ecKeyPairGetPublicKey(exCredentialKeyPair.value());
725 ASSERT_TRUE(exCredentialPubKey);
726 ASSERT_EQ(exCredentialPubKey.value(), credentialPubKey.value());
727
728 sp<IIdentityCredential> credential;
729 ASSERT_TRUE(credentialStore_
730 ->getCredential(
731 CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
732 credentialData, &credential)
733 .isOk());
734 ASSERT_NE(credential, nullptr);
735
736 // Calculate sessionTranscript, make something that resembles what you'd use for
737 // an over-the-Internet presentation not using mdoc session encryption.
738 cppbor::Array sessionTranscript =
739 cppbor::Array()
740 .add(cppbor::Null()) // DeviceEngagementBytes isn't used.
741 .add(cppbor::Null()) // EReaderKeyBytes isn't used.
742 .add(cppbor::Array() // Proprietary handover structure follows.
743 .add(cppbor::Tstr("TestHandover"))
744 .add(cppbor::Bstr(vector<uint8_t>{1, 2, 3}))
745 .add(cppbor::Bstr(vector<uint8_t>{9, 8, 7, 6})));
746 vector<uint8_t> sessionTranscriptEncoded = sessionTranscript.encode();
747
748 // Generate the key that will be used to sign AuthenticatedData.
749 vector<uint8_t> signingKeyBlob;
750 Certificate signingKeyCertificate;
751 ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
752 optional<vector<uint8_t>> signingPubKey =
753 support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
754 EXPECT_TRUE(signingPubKey);
755 test_utils::verifyAuthKeyCertificate(signingKeyCertificate.encodedCertificate);
756
757 vector<RequestNamespace> requestedNamespaces = test_utils::buildRequestNamespaces(testEntries);
758 ASSERT_TRUE(credential->setRequestedNamespaces(requestedNamespaces).isOk());
759 ASSERT_TRUE(credential->setVerificationToken(verificationToken).isOk());
760 Status status = credential->startRetrieval(
761 secureProfiles.value(), authToken, {} /* itemsRequestBytes*/, signingKeyBlob,
762 sessionTranscriptEncoded, {} /* readerSignature */, testEntriesEntryCounts);
763 ASSERT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
764
765 for (const auto& entry : testEntries) {
766 ASSERT_TRUE(credential
767 ->startRetrieveEntryValue(entry.nameSpace, entry.name,
768 entry.valueCbor.size(), entry.profileIds)
769 .isOk());
770
771 auto it = encryptedBlobs.find(&entry);
772 ASSERT_NE(it, encryptedBlobs.end());
773 const vector<vector<uint8_t>>& encryptedChunks = it->second;
774
775 vector<uint8_t> content;
776 for (const auto& encryptedChunk : encryptedChunks) {
777 vector<uint8_t> chunk;
778 ASSERT_TRUE(credential->retrieveEntryValue(encryptedChunk, &chunk).isOk());
779 content.insert(content.end(), chunk.begin(), chunk.end());
780 }
781 EXPECT_EQ(content, entry.valueCbor);
782 }
783
784 vector<uint8_t> mac;
785 vector<uint8_t> ecdsaSignature;
786 vector<uint8_t> deviceNameSpacesEncoded;
787 status = credential->finishRetrievalWithSignature(&mac, &deviceNameSpacesEncoded,
788 &ecdsaSignature);
789 ASSERT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
790 // MACing should NOT work since we're not using session encryption
791 ASSERT_EQ(0, mac.size());
792
793 // ECDSA signatures should work, however. Check this.
794 ASSERT_GT(ecdsaSignature.size(), 0);
795
796 cborPretty = cppbor::prettyPrint(deviceNameSpacesEncoded, 32, {});
797 ASSERT_EQ(
798 "{\n"
799 " 'PersonalData' : {\n"
800 " 'Last name' : 'Turing',\n"
801 " 'Birth date' : '19120623',\n"
802 " 'First name' : 'Alan',\n"
803 " },\n"
804 "}",
805 cborPretty);
806
807 string docType = "org.iso.18013-5.2019.mdl";
808
809 vector<uint8_t> encodedDeviceAuthentication =
810 cppbor::Array()
811 .add("DeviceAuthentication")
812 .add(sessionTranscript.clone())
813 .add(docType)
814 .add(cppbor::SemanticTag(24, deviceNameSpacesEncoded))
815 .encode();
816 vector<uint8_t> deviceAuthenticationBytes =
817 cppbor::SemanticTag(24, encodedDeviceAuthentication).encode();
818 EXPECT_TRUE(support::coseCheckEcDsaSignature(ecdsaSignature,
819 deviceAuthenticationBytes, // Detached content
820 signingPubKey.value()));
821 }
822
823 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EndToEndTests);
824 INSTANTIATE_TEST_SUITE_P(
825 Identity, EndToEndTests,
826 testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
827 android::PrintInstanceNameToString);
828
829 } // namespace android::hardware::identity
830
main(int argc,char ** argv)831 int main(int argc, char** argv) {
832 ::testing::InitGoogleTest(&argc, argv);
833 ::android::ProcessState::self()->setThreadPoolMaxThreadCount(1);
834 ::android::ProcessState::self()->startThreadPool();
835 return RUN_ALL_TESTS();
836 }
837