xref: /aosp_15_r20/hardware/interfaces/identity/aidl/vts/EndToEndTests.cpp (revision 4d7e907c777eeecc4c5bd7cf640a754fac206ff7)
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