blob: 88b06df0466fb34d1b828d091c6bd61c244ef5f2 [file] [log] [blame]
David Zeuthenc75ac312019-10-28 13:16:45 -04001/*
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
17#define LOG_TAG "IdentityCredentialHidlHalTest"
18
19#include <map>
20
21#include <android-base/logging.h>
22#include <android/hardware/identity/1.0/IIdentityCredentialStore.h>
23#include <android/hardware/identity/1.0/types.h>
24#include <android/hardware/identity/support/IdentityCredentialSupport.h>
25
26#include <cppbor.h>
27#include <cppbor_parse.h>
28#include <gtest/gtest.h>
29#include <hidl/GtestPrinter.h>
30#include <hidl/ServiceManagement.h>
31
32using std::map;
33using std::optional;
34using std::string;
35using std::vector;
36
37namespace android {
38namespace hardware {
39namespace identity {
40namespace test {
41
42using ::android::hardware::identity::V1_0::IIdentityCredential;
43using ::android::hardware::identity::V1_0::IIdentityCredentialStore;
44using ::android::hardware::identity::V1_0::IWritableIdentityCredential;
45using ::android::hardware::identity::V1_0::Result;
46using ::android::hardware::identity::V1_0::ResultCode;
47using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
48using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
49
50// ---------------------------------------------------------------------------
51// Test Data.
52// ---------------------------------------------------------------------------
53
54struct TestEntryData {
55 TestEntryData(string nameSpace, string name, vector<uint16_t> profileIds)
56 : nameSpace(nameSpace), name(name), profileIds(profileIds) {}
57
58 TestEntryData(string nameSpace, string name, const string& value, vector<uint16_t> profileIds)
59 : TestEntryData(nameSpace, name, profileIds) {
60 valueCbor = cppbor::Tstr(((const char*)value.data())).encode();
61 }
62 TestEntryData(string nameSpace, string name, const vector<uint8_t>& value,
63 vector<uint16_t> profileIds)
64 : TestEntryData(nameSpace, name, profileIds) {
65 valueCbor = cppbor::Bstr(value).encode();
66 }
67 TestEntryData(string nameSpace, string name, bool value, vector<uint16_t> profileIds)
68 : TestEntryData(nameSpace, name, profileIds) {
69 valueCbor = cppbor::Bool(value).encode();
70 }
71 TestEntryData(string nameSpace, string name, int64_t value, vector<uint16_t> profileIds)
72 : TestEntryData(nameSpace, name, profileIds) {
73 if (value >= 0) {
74 valueCbor = cppbor::Uint(value).encode();
75 } else {
76 valueCbor = cppbor::Nint(-value).encode();
77 }
78 }
79
80 string nameSpace;
81 string name;
82 vector<uint8_t> valueCbor;
83 vector<uint16_t> profileIds;
84};
85
86struct TestProfile {
87 uint16_t id;
88 hidl_vec<uint8_t> readerCertificate;
89 bool userAuthenticationRequired;
90 uint64_t timeoutMillis;
91};
92
93/************************************
94 * TEST DATA FOR AUTHENTICATION
95 ************************************/
96// Test authentication token for user authentication
97
98class IdentityCredentialStoreHidlTest : public ::testing::TestWithParam<std::string> {
99 public:
100 virtual void SetUp() override {
101 string serviceName = GetParam();
102 ASSERT_FALSE(serviceName.empty());
103 credentialStore_ = IIdentityCredentialStore::getService(serviceName);
104 ASSERT_NE(credentialStore_, nullptr);
105
106 credentialStore_->getHardwareInformation(
107 [&](const Result& result, const hidl_string& credentialStoreName,
108 const hidl_string& credentialStoreAuthorName, uint32_t chunkSize,
109 bool /* isDirectAccess */,
110 const hidl_vec<hidl_string> /* supportedDocTypes */) {
111 EXPECT_EQ("", result.message);
112 ASSERT_EQ(ResultCode::OK, result.code);
113 ASSERT_GT(credentialStoreName.size(), 0u);
114 ASSERT_GT(credentialStoreAuthorName.size(), 0u);
115 ASSERT_GE(chunkSize, 256u); // Chunk sizes < APDU buffer won't be supported
116 dataChunkSize_ = chunkSize;
117 });
118 }
119 virtual void TearDown() override {}
120
121 uint32_t dataChunkSize_ = 0;
122
123 sp<IIdentityCredentialStore> credentialStore_;
124};
125
126TEST_P(IdentityCredentialStoreHidlTest, HardwareConfiguration) {
127 credentialStore_->getHardwareInformation(
128 [&](const Result& result, const hidl_string& credentialStoreName,
129 const hidl_string& credentialStoreAuthorName, uint32_t chunkSize,
130 bool /* isDirectAccess */, const hidl_vec<hidl_string> /* supportedDocTypes */) {
131 EXPECT_EQ("", result.message);
132 ASSERT_EQ(ResultCode::OK, result.code);
133 ASSERT_GT(credentialStoreName.size(), 0u);
134 ASSERT_GT(credentialStoreAuthorName.size(), 0u);
135 ASSERT_GE(chunkSize, 256u); // Chunk sizes < APDU buffer won't be supported
136 });
137}
138
139TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) {
140 // First, generate a key-pair for the reader since its public key will be
141 // part of the request data.
142 optional<vector<uint8_t>> readerKeyPKCS8 = support::createEcKeyPair();
143 ASSERT_TRUE(readerKeyPKCS8);
144 optional<vector<uint8_t>> readerPublicKey =
145 support::ecKeyPairGetPublicKey(readerKeyPKCS8.value());
146 optional<vector<uint8_t>> readerKey = support::ecKeyPairGetPrivateKey(readerKeyPKCS8.value());
147 string serialDecimal = "1234";
148 string issuer = "Android Open Source Project";
149 string subject = "Android IdentityCredential VTS Test";
150 time_t validityNotBefore = time(nullptr);
151 time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
152 optional<vector<uint8_t>> readerCertificate = support::ecPublicKeyGenerateCertificate(
153 readerPublicKey.value(), readerKey.value(), serialDecimal, issuer, subject,
154 validityNotBefore, validityNotAfter);
155 ASSERT_TRUE(readerCertificate);
156
157 // Make the portrait image really big (just shy of 256 KiB) to ensure that
158 // the chunking code gets exercised.
159 vector<uint8_t> portraitImage;
160 portraitImage.resize(256 * 1024 - 10);
161 for (size_t n = 0; n < portraitImage.size(); n++) {
162 portraitImage[n] = (uint8_t)n;
163 }
164
165 // Access control profiles:
166 const vector<TestProfile> testProfiles = {// Profile 0 (reader authentication)
167 {0, readerCertificate.value(), false, 0},
168 // Profile 1 (no authentication)
169 {1, {}, false, 0}};
170
171 HardwareAuthToken authToken = {};
172
173 // Here's the actual test data:
174 const vector<TestEntryData> testEntries = {
175 {"PersonalData", "Last name", string("Turing"), vector<uint16_t>{0, 1}},
176 {"PersonalData", "Birth date", string("19120623"), vector<uint16_t>{0, 1}},
177 {"PersonalData", "First name", string("Alan"), vector<uint16_t>{0, 1}},
178 {"PersonalData", "Home address", string("Maida Vale, London, England"),
179 vector<uint16_t>{0}},
180 {"Image", "Portrait image", portraitImage, vector<uint16_t>{0, 1}},
181 };
182 const vector<uint16_t> testEntriesEntryCounts = {static_cast<uint16_t>(testEntries.size() - 1),
183 1u};
184
185 string cborPretty;
186 sp<IWritableIdentityCredential> writableCredential;
187
188 hidl_vec<uint8_t> empty{0};
189
190 string docType = "org.iso.18013-5.2019.mdl";
191 bool testCredential = true;
192 Result result;
193 credentialStore_->createCredential(
194 docType, testCredential,
195 [&](const Result& _result, const sp<IWritableIdentityCredential>& _writableCredential) {
196 result = _result;
197 writableCredential = _writableCredential;
198 });
199 EXPECT_EQ("", result.message);
200 ASSERT_EQ(ResultCode::OK, result.code);
201 ASSERT_NE(writableCredential, nullptr);
202
203 string challenge = "attestationChallenge";
David Zeuthen87cb07b2020-01-30 16:15:50 -0500204 // TODO: set it to something random and check it's in the cert chain
205 vector<uint8_t> attestationApplicationId = {};
David Zeuthenc75ac312019-10-28 13:16:45 -0400206 vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
207 vector<uint8_t> attestationCertificate;
208 writableCredential->getAttestationCertificate(
David Zeuthen87cb07b2020-01-30 16:15:50 -0500209 attestationApplicationId, attestationChallenge,
210 [&](const Result& _result, const hidl_vec<hidl_vec<uint8_t>>& _splitCertChain) {
David Zeuthenc75ac312019-10-28 13:16:45 -0400211 result = _result;
David Zeuthen87cb07b2020-01-30 16:15:50 -0500212 vector<vector<uint8_t>> splitCerts;
213 std::copy(_splitCertChain.begin(), _splitCertChain.end(),
214 std::back_inserter(splitCerts));
215 attestationCertificate = support::certificateChainJoin(splitCerts);
David Zeuthenc75ac312019-10-28 13:16:45 -0400216 });
217 EXPECT_EQ("", result.message);
218 ASSERT_EQ(ResultCode::OK, result.code);
219
220 writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts,
221 [&](const Result& _result) { result = _result; });
222 EXPECT_EQ("", result.message);
223 ASSERT_EQ(ResultCode::OK, result.code);
224
225 vector<SecureAccessControlProfile> returnedSecureProfiles;
226 for (const auto& testProfile : testProfiles) {
227 SecureAccessControlProfile profile;
228 writableCredential->addAccessControlProfile(
229 testProfile.id, testProfile.readerCertificate,
230 testProfile.userAuthenticationRequired, testProfile.timeoutMillis,
231 0, // secureUserId
232 [&](const Result& _result, const SecureAccessControlProfile& _profile) {
233 result = _result;
234 profile = _profile;
235 });
236 EXPECT_EQ("", result.message);
237 ASSERT_EQ(ResultCode::OK, result.code);
238 ASSERT_EQ(testProfile.id, profile.id);
239 ASSERT_EQ(testProfile.readerCertificate, profile.readerCertificate);
240 ASSERT_EQ(testProfile.userAuthenticationRequired, profile.userAuthenticationRequired);
241 ASSERT_EQ(testProfile.timeoutMillis, profile.timeoutMillis);
242 ASSERT_EQ(support::kAesGcmTagSize + support::kAesGcmIvSize, profile.mac.size());
243 returnedSecureProfiles.push_back(profile);
244 }
245
246 // Uses TestEntryData* pointer as key and values are the encrypted blobs. This
247 // is a little hacky but it works well enough.
248 map<const TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
249
250 for (const auto& entry : testEntries) {
251 vector<vector<uint8_t>> chunks = support::chunkVector(entry.valueCbor, dataChunkSize_);
252
253 writableCredential->beginAddEntry(entry.profileIds, entry.nameSpace, entry.name,
254 entry.valueCbor.size(),
255 [&](const Result& _result) { result = _result; });
256 EXPECT_EQ("", result.message);
257 ASSERT_EQ(ResultCode::OK, result.code);
258
259 vector<vector<uint8_t>> encryptedChunks;
260 for (const auto& chunk : chunks) {
261 writableCredential->addEntryValue(
262 chunk, [&](const Result& result, hidl_vec<uint8_t> encryptedContent) {
263 EXPECT_EQ("", result.message);
264 ASSERT_EQ(ResultCode::OK, result.code);
265 ASSERT_GT(encryptedContent.size(), 0u);
266 encryptedChunks.push_back(encryptedContent);
267 });
268 }
269 encryptedBlobs[&entry] = encryptedChunks;
270 }
271
272 vector<uint8_t> credentialData;
273 vector<uint8_t> proofOfProvisioningSignature;
274 writableCredential->finishAddingEntries(
275 [&](const Result& _result, const hidl_vec<uint8_t>& _credentialData,
276 const hidl_vec<uint8_t>& _proofOfProvisioningSignature) {
277 result = _result;
278 credentialData = _credentialData;
279 proofOfProvisioningSignature = _proofOfProvisioningSignature;
280 });
281 EXPECT_EQ("", result.message);
282 ASSERT_EQ(ResultCode::OK, result.code);
283
284 optional<vector<uint8_t>> proofOfProvisioning =
285 support::coseSignGetPayload(proofOfProvisioningSignature);
286 ASSERT_TRUE(proofOfProvisioning);
287 cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
288 EXPECT_EQ(
289 "[\n"
290 " 'ProofOfProvisioning',\n"
291 " 'org.iso.18013-5.2019.mdl',\n"
292 " [\n"
293 " {\n"
294 " 'id' : 0,\n"
295 " 'readerCertificate' : <not printed>,\n"
296 " },\n"
297 " {\n"
298 " 'id' : 1,\n"
299 " },\n"
300 " ],\n"
301 " {\n"
302 " 'PersonalData' : [\n"
303 " {\n"
304 " 'name' : 'Last name',\n"
305 " 'value' : 'Turing',\n"
306 " 'accessControlProfiles' : [0, 1, ],\n"
307 " },\n"
308 " {\n"
309 " 'name' : 'Birth date',\n"
310 " 'value' : '19120623',\n"
311 " 'accessControlProfiles' : [0, 1, ],\n"
312 " },\n"
313 " {\n"
314 " 'name' : 'First name',\n"
315 " 'value' : 'Alan',\n"
316 " 'accessControlProfiles' : [0, 1, ],\n"
317 " },\n"
318 " {\n"
319 " 'name' : 'Home address',\n"
320 " 'value' : 'Maida Vale, London, England',\n"
321 " 'accessControlProfiles' : [0, ],\n"
322 " },\n"
323 " ],\n"
324 " 'Image' : [\n"
325 " {\n"
326 " 'name' : 'Portrait image',\n"
327 " 'value' : <bstr size=262134 sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
328 " 'accessControlProfiles' : [0, 1, ],\n"
329 " },\n"
330 " ],\n"
331 " },\n"
332 " true,\n"
333 "]",
334 cborPretty);
335
336 optional<vector<uint8_t>> credentialPubKey =
337 support::certificateChainGetTopMostKey(attestationCertificate);
338 ASSERT_TRUE(credentialPubKey);
339 EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
340 {}, // Additional data
341 credentialPubKey.value()));
342 writableCredential = nullptr;
343
344 // Now that the credential has been provisioned, read it back and check the
345 // correct data is returned.
346 sp<IIdentityCredential> credential;
347 credentialStore_->getCredential(
348 credentialData, [&](const Result& _result, const sp<IIdentityCredential>& _credential) {
349 result = _result;
350 credential = _credential;
351 });
352 EXPECT_EQ("", result.message);
353 ASSERT_EQ(ResultCode::OK, result.code);
354 ASSERT_NE(credential, nullptr);
355
356 optional<vector<uint8_t>> readerEphemeralKeyPair = support::createEcKeyPair();
357 ASSERT_TRUE(readerEphemeralKeyPair);
358 optional<vector<uint8_t>> readerEphemeralPublicKey =
359 support::ecKeyPairGetPublicKey(readerEphemeralKeyPair.value());
360 credential->setReaderEphemeralPublicKey(readerEphemeralPublicKey.value(),
361 [&](const Result& _result) { result = _result; });
362 EXPECT_EQ("", result.message);
363 ASSERT_EQ(ResultCode::OK, result.code);
364
365 vector<uint8_t> ephemeralKeyPair;
366 credential->createEphemeralKeyPair(
367 [&](const Result& _result, const hidl_vec<uint8_t>& _ephemeralKeyPair) {
368 result = _result;
369 ephemeralKeyPair = _ephemeralKeyPair;
370 });
371 EXPECT_EQ("", result.message);
372 ASSERT_EQ(ResultCode::OK, result.code);
373 optional<vector<uint8_t>> ephemeralPublicKey = support::ecKeyPairGetPublicKey(ephemeralKeyPair);
374
375 // Calculate requestData field and sign it with the reader key.
376 auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ephemeralPublicKey.value());
377 ASSERT_TRUE(getXYSuccess);
378 cppbor::Map deviceEngagement = cppbor::Map().add("ephX", ephX).add("ephY", ephY);
379 vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
380 vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
381 cppbor::Array sessionTranscript = cppbor::Array()
382 .add(cppbor::Semantic(24, deviceEngagementBytes))
383 .add(cppbor::Semantic(24, eReaderPubBytes));
384 vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode();
385
386 vector<uint8_t> itemsRequestBytes =
387 cppbor::Map("nameSpaces",
388 cppbor::Map()
389 .add("PersonalData", cppbor::Map()
390 .add("Last name", false)
391 .add("Birth date", false)
392 .add("First name", false)
393 .add("Home address", true))
394 .add("Image", cppbor::Map().add("Portrait image", false)))
395 .encode();
396 cborPretty = support::cborPrettyPrint(itemsRequestBytes, 32, {"EphemeralPublicKey"});
397 EXPECT_EQ(
398 "{\n"
399 " 'nameSpaces' : {\n"
400 " 'PersonalData' : {\n"
401 " 'Last name' : false,\n"
402 " 'Birth date' : false,\n"
403 " 'First name' : false,\n"
404 " 'Home address' : true,\n"
405 " },\n"
406 " 'Image' : {\n"
407 " 'Portrait image' : false,\n"
408 " },\n"
409 " },\n"
410 "}",
411 cborPretty);
412 vector<uint8_t> dataToSign = cppbor::Array()
413 .add("ReaderAuthentication")
414 .add(sessionTranscript.clone())
415 .add(cppbor::Semantic(24, itemsRequestBytes))
416 .encode();
417 optional<vector<uint8_t>> readerSignature =
418 support::coseSignEcDsa(readerKey.value(), {}, // content
419 dataToSign, // detached content
420 readerCertificate.value());
421 ASSERT_TRUE(readerSignature);
422
423 credential->startRetrieval(returnedSecureProfiles, authToken, itemsRequestBytes,
424 sessionTranscriptBytes, readerSignature.value(),
425 testEntriesEntryCounts,
426 [&](const Result& _result) { result = _result; });
427 EXPECT_EQ("", result.message);
428 ASSERT_EQ(ResultCode::OK, result.code);
429
430 for (const auto& entry : testEntries) {
431 credential->startRetrieveEntryValue(entry.nameSpace, entry.name, entry.valueCbor.size(),
432 entry.profileIds,
433 [&](const Result& _result) { result = _result; });
434 EXPECT_EQ("", result.message);
435 ASSERT_EQ(ResultCode::OK, result.code);
436
437 auto it = encryptedBlobs.find(&entry);
438 ASSERT_NE(it, encryptedBlobs.end());
439 const vector<vector<uint8_t>>& encryptedChunks = it->second;
440
441 vector<uint8_t> content;
442 for (const auto& encryptedChunk : encryptedChunks) {
443 vector<uint8_t> chunk;
444 credential->retrieveEntryValue(
445 encryptedChunk, [&](const Result& _result, const hidl_vec<uint8_t>& _chunk) {
446 result = _result;
447 chunk = _chunk;
448 });
449 EXPECT_EQ("", result.message);
450 ASSERT_EQ(ResultCode::OK, result.code);
451 content.insert(content.end(), chunk.begin(), chunk.end());
452 }
453 EXPECT_EQ(content, entry.valueCbor);
454 }
455
456 // Generate the key that will be used to sign AuthenticatedData.
457 vector<uint8_t> signingKeyBlob;
458 vector<uint8_t> signingKeyCertificate;
459 credential->generateSigningKeyPair([&](const Result& _result,
460 const hidl_vec<uint8_t> _signingKeyBlob,
461 const hidl_vec<uint8_t> _signingKeyCertificate) {
462 result = _result;
463 signingKeyBlob = _signingKeyBlob;
464 signingKeyCertificate = _signingKeyCertificate;
465 });
466 EXPECT_EQ("", result.message);
467 ASSERT_EQ(ResultCode::OK, result.code);
468
469 vector<uint8_t> mac;
470 vector<uint8_t> deviceNameSpacesBytes;
471 credential->finishRetrieval(signingKeyBlob,
472 [&](const Result& _result, const hidl_vec<uint8_t> _mac,
473 const hidl_vec<uint8_t> _deviceNameSpacesBytes) {
474 result = _result;
475 mac = _mac;
476 deviceNameSpacesBytes = _deviceNameSpacesBytes;
477 });
478 EXPECT_EQ("", result.message);
479 ASSERT_EQ(ResultCode::OK, result.code);
480 cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {});
481 ASSERT_EQ(
482 "{\n"
483 " 'PersonalData' : {\n"
484 " 'Last name' : 'Turing',\n"
485 " 'Birth date' : '19120623',\n"
486 " 'First name' : 'Alan',\n"
487 " 'Home address' : 'Maida Vale, London, England',\n"
488 " },\n"
489 " 'Image' : {\n"
490 " 'Portrait image' : <bstr size=262134 "
491 "sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
492 " },\n"
493 "}",
494 cborPretty);
495 // The data that is MACed is ["DeviceAuthentication", sessionTranscriptBytes, docType,
496 // deviceNameSpacesBytes] so build up that structure
497 cppbor::Array deviceAuthentication;
498 deviceAuthentication.add("DeviceAuthentication");
499 deviceAuthentication.add(sessionTranscript.clone());
500 deviceAuthentication.add(docType);
501 deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
502 vector<uint8_t> encodedDeviceAuthentication = deviceAuthentication.encode();
503 optional<vector<uint8_t>> signingPublicKey =
504 support::certificateChainGetTopMostKey(signingKeyCertificate);
505 EXPECT_TRUE(signingPublicKey);
506
507 // Derive the key used for MACing.
508 optional<vector<uint8_t>> readerEphemeralPrivateKey =
509 support::ecKeyPairGetPrivateKey(readerEphemeralKeyPair.value());
510 optional<vector<uint8_t>> sharedSecret =
511 support::ecdh(signingPublicKey.value(), readerEphemeralPrivateKey.value());
512 ASSERT_TRUE(sharedSecret);
513 vector<uint8_t> salt = {0x00};
514 vector<uint8_t> info = {};
515 optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
516 ASSERT_TRUE(derivedKey);
517 optional<vector<uint8_t>> calculatedMac =
518 support::coseMac0(derivedKey.value(), {}, // payload
519 encodedDeviceAuthentication); // detached content
520 ASSERT_TRUE(calculatedMac);
521 EXPECT_EQ(mac, calculatedMac);
522}
523
524INSTANTIATE_TEST_SUITE_P(PerInstance, IdentityCredentialStoreHidlTest,
525 testing::ValuesIn(android::hardware::getAllHalInstanceNames(
526 IIdentityCredentialStore::descriptor)),
527 android::hardware::PrintInstanceNameToString);
528
529} // namespace test
530} // namespace identity
531} // namespace hardware
532} // namespace android