blob: 5abe5a2f66adf044b0cc868146706f17984e7f16 [file] [log] [blame]
David Zeuthen81603152020-02-11 22:04:24 -05001/*
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 "VtsHalIdentityTargetTest"
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
31namespace android::hardware::identity {
32
33using std::map;
34using std::optional;
35using std::string;
36using std::vector;
37
38using ::android::sp;
39using ::android::String16;
40using ::android::binder::Status;
41
42using ::android::hardware::keymaster::HardwareAuthToken;
43
44// ---------------------------------------------------------------------------
45// Test Data.
46// ---------------------------------------------------------------------------
47
48struct TestEntryData {
49 TestEntryData(string nameSpace, string name, vector<int32_t> profileIds)
50 : nameSpace(nameSpace), name(name), profileIds(profileIds) {}
51
52 TestEntryData(string nameSpace, string name, const string& value, vector<int32_t> profileIds)
53 : TestEntryData(nameSpace, name, profileIds) {
54 valueCbor = cppbor::Tstr(((const char*)value.data())).encode();
55 }
56 TestEntryData(string nameSpace, string name, const vector<uint8_t>& value,
57 vector<int32_t> profileIds)
58 : TestEntryData(nameSpace, name, profileIds) {
59 valueCbor = cppbor::Bstr(value).encode();
60 }
61 TestEntryData(string nameSpace, string name, bool value, vector<int32_t> profileIds)
62 : TestEntryData(nameSpace, name, profileIds) {
63 valueCbor = cppbor::Bool(value).encode();
64 }
65 TestEntryData(string nameSpace, string name, int64_t value, vector<int32_t> profileIds)
66 : TestEntryData(nameSpace, name, profileIds) {
67 if (value >= 0) {
68 valueCbor = cppbor::Uint(value).encode();
69 } else {
70 valueCbor = cppbor::Nint(-value).encode();
71 }
72 }
73
74 string nameSpace;
75 string name;
76 vector<uint8_t> valueCbor;
77 vector<int32_t> profileIds;
78};
79
80struct TestProfile {
81 uint16_t id;
82 vector<uint8_t> readerCertificate;
83 bool userAuthenticationRequired;
84 uint64_t timeoutMillis;
85};
86
87// ----------------------------------------------------------------
88
89class IdentityAidl : public testing::TestWithParam<std::string> {
90 public:
91 virtual void SetUp() override {
92 credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
93 String16(GetParam().c_str()));
94 ASSERT_NE(credentialStore_, nullptr);
95 }
96
97 sp<IIdentityCredentialStore> credentialStore_;
98};
99
100TEST_P(IdentityAidl, hardwareInformation) {
101 HardwareInformation info;
102 ASSERT_TRUE(credentialStore_->getHardwareInformation(&info).isOk());
103 ASSERT_GT(info.credentialStoreName.size(), 0);
104 ASSERT_GT(info.credentialStoreAuthorName.size(), 0);
105 ASSERT_GE(info.dataChunkSize, 256);
106}
107
108TEST_P(IdentityAidl, createAndRetrieveCredential) {
109 // First, generate a key-pair for the reader since its public key will be
110 // part of the request data.
111 optional<vector<uint8_t>> readerKeyPKCS8 = support::createEcKeyPair();
112 ASSERT_TRUE(readerKeyPKCS8);
113 optional<vector<uint8_t>> readerPublicKey =
114 support::ecKeyPairGetPublicKey(readerKeyPKCS8.value());
115 optional<vector<uint8_t>> readerKey = support::ecKeyPairGetPrivateKey(readerKeyPKCS8.value());
116 string serialDecimal = "1234";
117 string issuer = "Android Open Source Project";
118 string subject = "Android IdentityCredential VTS Test";
119 time_t validityNotBefore = time(nullptr);
120 time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
121 optional<vector<uint8_t>> readerCertificate = support::ecPublicKeyGenerateCertificate(
122 readerPublicKey.value(), readerKey.value(), serialDecimal, issuer, subject,
123 validityNotBefore, validityNotAfter);
124 ASSERT_TRUE(readerCertificate);
125
126 // Make the portrait image really big (just shy of 256 KiB) to ensure that
127 // the chunking code gets exercised.
128 vector<uint8_t> portraitImage;
129 portraitImage.resize(256 * 1024 - 10);
130 for (size_t n = 0; n < portraitImage.size(); n++) {
131 portraitImage[n] = (uint8_t)n;
132 }
133
134 // Access control profiles:
135 const vector<TestProfile> testProfiles = {// Profile 0 (reader authentication)
136 {0, readerCertificate.value(), false, 0},
137 // Profile 1 (no authentication)
138 {1, {}, false, 0}};
139
140 HardwareAuthToken authToken;
141
142 // Here's the actual test data:
143 const vector<TestEntryData> testEntries = {
144 {"PersonalData", "Last name", string("Turing"), vector<int32_t>{0, 1}},
145 {"PersonalData", "Birth date", string("19120623"), vector<int32_t>{0, 1}},
146 {"PersonalData", "First name", string("Alan"), vector<int32_t>{0, 1}},
147 {"PersonalData", "Home address", string("Maida Vale, London, England"),
148 vector<int32_t>{0}},
149 {"Image", "Portrait image", portraitImage, vector<int32_t>{0, 1}},
150 };
151 const vector<int32_t> testEntriesEntryCounts = {static_cast<int32_t>(testEntries.size() - 1),
152 1u};
153 HardwareInformation hwInfo;
154 ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
155
156 string cborPretty;
157 sp<IWritableIdentityCredential> writableCredential;
158 string docType = "org.iso.18013-5.2019.mdl";
159 bool testCredential = true;
160 ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &writableCredential)
161 .isOk());
162 ASSERT_NE(writableCredential, nullptr);
163
164 string challenge = "attestationChallenge";
165 // TODO: set it to something random and check it's in the cert chain
166 vector<uint8_t> attestationApplicationId = {};
167 vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
168 vector<Certificate> attestationCertificates;
169 ASSERT_TRUE(writableCredential
170 ->getAttestationCertificate(attestationApplicationId, attestationChallenge,
171 &attestationCertificates)
172 .isOk());
173 ASSERT_GE(attestationCertificates.size(), 2);
174
175 ASSERT_TRUE(
176 writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts)
177 .isOk());
178
179 vector<SecureAccessControlProfile> returnedSecureProfiles;
180 for (const auto& testProfile : testProfiles) {
181 SecureAccessControlProfile profile;
182 Certificate cert;
183 cert.encodedCertificate = testProfile.readerCertificate;
184 ASSERT_TRUE(writableCredential
185 ->addAccessControlProfile(testProfile.id, cert,
186 testProfile.userAuthenticationRequired,
187 testProfile.timeoutMillis,
188 0, // secureUserId
189 &profile)
190 .isOk());
191 ASSERT_EQ(testProfile.id, profile.id);
192 ASSERT_EQ(testProfile.readerCertificate, profile.readerCertificate.encodedCertificate);
193 ASSERT_EQ(testProfile.userAuthenticationRequired, profile.userAuthenticationRequired);
194 ASSERT_EQ(testProfile.timeoutMillis, profile.timeoutMillis);
195 ASSERT_EQ(support::kAesGcmTagSize + support::kAesGcmIvSize, profile.mac.size());
196 returnedSecureProfiles.push_back(profile);
197 }
198
199 // Uses TestEntryData* pointer as key and values are the encrypted blobs. This
200 // is a little hacky but it works well enough.
201 map<const TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
202
203 for (const auto& entry : testEntries) {
204 vector<vector<uint8_t>> chunks =
205 support::chunkVector(entry.valueCbor, hwInfo.dataChunkSize);
206
207 ASSERT_TRUE(writableCredential
208 ->beginAddEntry(entry.profileIds, entry.nameSpace, entry.name,
209 entry.valueCbor.size())
210 .isOk());
211
212 vector<vector<uint8_t>> encryptedChunks;
213 for (const auto& chunk : chunks) {
214 vector<uint8_t> encryptedChunk;
215 ASSERT_TRUE(writableCredential->addEntryValue(chunk, &encryptedChunk).isOk());
216 encryptedChunks.push_back(encryptedChunk);
217 }
218 encryptedBlobs[&entry] = encryptedChunks;
219 }
220
221 vector<uint8_t> credentialData;
222 vector<uint8_t> proofOfProvisioningSignature;
223 ASSERT_TRUE(
224 writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature)
225 .isOk());
226
227 optional<vector<uint8_t>> proofOfProvisioning =
228 support::coseSignGetPayload(proofOfProvisioningSignature);
229 ASSERT_TRUE(proofOfProvisioning);
230 cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
231 EXPECT_EQ(
232 "[\n"
233 " 'ProofOfProvisioning',\n"
234 " 'org.iso.18013-5.2019.mdl',\n"
235 " [\n"
236 " {\n"
237 " 'id' : 0,\n"
238 " 'readerCertificate' : <not printed>,\n"
239 " },\n"
240 " {\n"
241 " 'id' : 1,\n"
242 " },\n"
243 " ],\n"
244 " {\n"
245 " 'PersonalData' : [\n"
246 " {\n"
247 " 'name' : 'Last name',\n"
248 " 'value' : 'Turing',\n"
249 " 'accessControlProfiles' : [0, 1, ],\n"
250 " },\n"
251 " {\n"
252 " 'name' : 'Birth date',\n"
253 " 'value' : '19120623',\n"
254 " 'accessControlProfiles' : [0, 1, ],\n"
255 " },\n"
256 " {\n"
257 " 'name' : 'First name',\n"
258 " 'value' : 'Alan',\n"
259 " 'accessControlProfiles' : [0, 1, ],\n"
260 " },\n"
261 " {\n"
262 " 'name' : 'Home address',\n"
263 " 'value' : 'Maida Vale, London, England',\n"
264 " 'accessControlProfiles' : [0, ],\n"
265 " },\n"
266 " ],\n"
267 " 'Image' : [\n"
268 " {\n"
269 " 'name' : 'Portrait image',\n"
270 " 'value' : <bstr size=262134 sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
271 " 'accessControlProfiles' : [0, 1, ],\n"
272 " },\n"
273 " ],\n"
274 " },\n"
275 " true,\n"
276 "]",
277 cborPretty);
278
279 optional<vector<uint8_t>> credentialPubKey =
280 support::certificateChainGetTopMostKey(attestationCertificates[0].encodedCertificate);
281 ASSERT_TRUE(credentialPubKey);
282 EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
283 {}, // Additional data
284 credentialPubKey.value()));
285 writableCredential = nullptr;
286
287 // Now that the credential has been provisioned, read it back and check the
288 // correct data is returned.
289 sp<IIdentityCredential> credential;
290 ASSERT_TRUE(credentialStore_
291 ->getCredential(
292 CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
293 credentialData, &credential)
294 .isOk());
295 ASSERT_NE(credential, nullptr);
296
297 optional<vector<uint8_t>> readerEphemeralKeyPair = support::createEcKeyPair();
298 ASSERT_TRUE(readerEphemeralKeyPair);
299 optional<vector<uint8_t>> readerEphemeralPublicKey =
300 support::ecKeyPairGetPublicKey(readerEphemeralKeyPair.value());
301 ASSERT_TRUE(credential->setReaderEphemeralPublicKey(readerEphemeralPublicKey.value()).isOk());
302
303 vector<uint8_t> ephemeralKeyPair;
304 ASSERT_TRUE(credential->createEphemeralKeyPair(&ephemeralKeyPair).isOk());
305 optional<vector<uint8_t>> ephemeralPublicKey = support::ecKeyPairGetPublicKey(ephemeralKeyPair);
306
307 // Calculate requestData field and sign it with the reader key.
308 auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ephemeralPublicKey.value());
309 ASSERT_TRUE(getXYSuccess);
310 cppbor::Map deviceEngagement = cppbor::Map().add("ephX", ephX).add("ephY", ephY);
311 vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
312 vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
313 cppbor::Array sessionTranscript = cppbor::Array()
314 .add(cppbor::Semantic(24, deviceEngagementBytes))
315 .add(cppbor::Semantic(24, eReaderPubBytes));
316 vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode();
317
318 vector<uint8_t> itemsRequestBytes =
319 cppbor::Map("nameSpaces",
320 cppbor::Map()
321 .add("PersonalData", cppbor::Map()
322 .add("Last name", false)
323 .add("Birth date", false)
324 .add("First name", false)
325 .add("Home address", true))
326 .add("Image", cppbor::Map().add("Portrait image", false)))
327 .encode();
328 cborPretty = support::cborPrettyPrint(itemsRequestBytes, 32, {"EphemeralPublicKey"});
329 EXPECT_EQ(
330 "{\n"
331 " 'nameSpaces' : {\n"
332 " 'PersonalData' : {\n"
333 " 'Last name' : false,\n"
334 " 'Birth date' : false,\n"
335 " 'First name' : false,\n"
336 " 'Home address' : true,\n"
337 " },\n"
338 " 'Image' : {\n"
339 " 'Portrait image' : false,\n"
340 " },\n"
341 " },\n"
342 "}",
343 cborPretty);
344 vector<uint8_t> dataToSign = cppbor::Array()
345 .add("ReaderAuthentication")
346 .add(sessionTranscript.clone())
347 .add(cppbor::Semantic(24, itemsRequestBytes))
348 .encode();
349 optional<vector<uint8_t>> readerSignature =
350 support::coseSignEcDsa(readerKey.value(), {}, // content
351 dataToSign, // detached content
352 readerCertificate.value());
353 ASSERT_TRUE(readerSignature);
354
355 ASSERT_TRUE(credential
356 ->startRetrieval(returnedSecureProfiles, authToken, itemsRequestBytes,
357 sessionTranscriptBytes, readerSignature.value(),
358 testEntriesEntryCounts)
359 .isOk());
360
361 for (const auto& entry : testEntries) {
362 ASSERT_TRUE(credential
363 ->startRetrieveEntryValue(entry.nameSpace, entry.name,
364 entry.valueCbor.size(), entry.profileIds)
365 .isOk());
366
367 auto it = encryptedBlobs.find(&entry);
368 ASSERT_NE(it, encryptedBlobs.end());
369 const vector<vector<uint8_t>>& encryptedChunks = it->second;
370
371 vector<uint8_t> content;
372 for (const auto& encryptedChunk : encryptedChunks) {
373 vector<uint8_t> chunk;
374 ASSERT_TRUE(credential->retrieveEntryValue(encryptedChunk, &chunk).isOk());
375 content.insert(content.end(), chunk.begin(), chunk.end());
376 }
377 EXPECT_EQ(content, entry.valueCbor);
378 }
379
380 // Generate the key that will be used to sign AuthenticatedData.
381 vector<uint8_t> signingKeyBlob;
382 Certificate signingKeyCertificate;
383 ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
384
385 vector<uint8_t> mac;
386 vector<uint8_t> deviceNameSpacesBytes;
387 ASSERT_TRUE(credential->finishRetrieval(signingKeyBlob, &mac, &deviceNameSpacesBytes).isOk());
388 cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {});
389 ASSERT_EQ(
390 "{\n"
391 " 'PersonalData' : {\n"
392 " 'Last name' : 'Turing',\n"
393 " 'Birth date' : '19120623',\n"
394 " 'First name' : 'Alan',\n"
395 " 'Home address' : 'Maida Vale, London, England',\n"
396 " },\n"
397 " 'Image' : {\n"
398 " 'Portrait image' : <bstr size=262134 "
399 "sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
400 " },\n"
401 "}",
402 cborPretty);
403 // The data that is MACed is ["DeviceAuthentication", sessionTranscriptBytes, docType,
404 // deviceNameSpacesBytes] so build up that structure
405 cppbor::Array deviceAuthentication;
406 deviceAuthentication.add("DeviceAuthentication");
407 deviceAuthentication.add(sessionTranscript.clone());
408 deviceAuthentication.add(docType);
409 deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
410 vector<uint8_t> encodedDeviceAuthentication = deviceAuthentication.encode();
411 optional<vector<uint8_t>> signingPublicKey =
412 support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
413 EXPECT_TRUE(signingPublicKey);
414
415 // Derive the key used for MACing.
416 optional<vector<uint8_t>> readerEphemeralPrivateKey =
417 support::ecKeyPairGetPrivateKey(readerEphemeralKeyPair.value());
418 optional<vector<uint8_t>> sharedSecret =
419 support::ecdh(signingPublicKey.value(), readerEphemeralPrivateKey.value());
420 ASSERT_TRUE(sharedSecret);
421 vector<uint8_t> salt = {0x00};
422 vector<uint8_t> info = {};
423 optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
424 ASSERT_TRUE(derivedKey);
425 optional<vector<uint8_t>> calculatedMac =
426 support::coseMac0(derivedKey.value(), {}, // payload
427 encodedDeviceAuthentication); // detached content
428 ASSERT_TRUE(calculatedMac);
429 EXPECT_EQ(mac, calculatedMac);
430}
431
432INSTANTIATE_TEST_SUITE_P(
433 Identity, IdentityAidl,
434 testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
435 android::PrintInstanceNameToString);
436// INSTANTIATE_TEST_SUITE_P(Identity, IdentityAidl,
437// testing::Values("android.hardware.identity.IIdentityCredentialStore/default"));
438
439} // namespace android::hardware::identity
440
441int main(int argc, char** argv) {
442 ::testing::InitGoogleTest(&argc, argv);
443 ::android::ProcessState::self()->setThreadPoolMaxThreadCount(1);
444 ::android::ProcessState::self()->startThreadPool();
445 return RUN_ALL_TESTS();
446}