blob: 17145b415439db0ed0171546c6b188de03224229 [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
Selene Huang92b61d62020-03-04 02:24:16 -080031#include "VtsIdentityTestUtils.h"
32
David Zeuthen81603152020-02-11 22:04:24 -050033namespace android::hardware::identity {
34
Selene Huang92b61d62020-03-04 02:24:16 -080035using std::endl;
David Zeuthen81603152020-02-11 22:04:24 -050036using std::map;
37using std::optional;
38using std::string;
39using std::vector;
40
41using ::android::sp;
42using ::android::String16;
43using ::android::binder::Status;
44
45using ::android::hardware::keymaster::HardwareAuthToken;
David Zeuthena8ed82c2020-05-08 10:03:28 -040046using ::android::hardware::keymaster::VerificationToken;
David Zeuthen81603152020-02-11 22:04:24 -050047
David Zeuthen81603152020-02-11 22:04:24 -050048class IdentityAidl : public testing::TestWithParam<std::string> {
49 public:
50 virtual void SetUp() override {
51 credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
52 String16(GetParam().c_str()));
53 ASSERT_NE(credentialStore_, nullptr);
54 }
55
56 sp<IIdentityCredentialStore> credentialStore_;
57};
58
59TEST_P(IdentityAidl, hardwareInformation) {
60 HardwareInformation info;
61 ASSERT_TRUE(credentialStore_->getHardwareInformation(&info).isOk());
62 ASSERT_GT(info.credentialStoreName.size(), 0);
63 ASSERT_GT(info.credentialStoreAuthorName.size(), 0);
64 ASSERT_GE(info.dataChunkSize, 256);
65}
66
67TEST_P(IdentityAidl, createAndRetrieveCredential) {
68 // First, generate a key-pair for the reader since its public key will be
69 // part of the request data.
Selene Huang92b61d62020-03-04 02:24:16 -080070 vector<uint8_t> readerKey;
71 optional<vector<uint8_t>> readerCertificate =
72 test_utils::GenerateReaderCertificate("1234", readerKey);
David Zeuthen81603152020-02-11 22:04:24 -050073 ASSERT_TRUE(readerCertificate);
74
75 // Make the portrait image really big (just shy of 256 KiB) to ensure that
76 // the chunking code gets exercised.
77 vector<uint8_t> portraitImage;
Selene Huang92b61d62020-03-04 02:24:16 -080078 test_utils::SetImageData(portraitImage);
David Zeuthen81603152020-02-11 22:04:24 -050079
80 // Access control profiles:
Selene Huang92b61d62020-03-04 02:24:16 -080081 const vector<test_utils::TestProfile> testProfiles = {// Profile 0 (reader authentication)
82 {0, readerCertificate.value(), false, 0},
83 // Profile 1 (no authentication)
84 {1, {}, false, 0}};
David Zeuthen81603152020-02-11 22:04:24 -050085
David Zeuthena8ed82c2020-05-08 10:03:28 -040086 // It doesn't matter since no user auth is needed in this particular test,
87 // but for good measure, clear out the tokens we pass to the HAL.
David Zeuthen81603152020-02-11 22:04:24 -050088 HardwareAuthToken authToken;
David Zeuthena8ed82c2020-05-08 10:03:28 -040089 VerificationToken verificationToken;
90 authToken.challenge = 0;
91 authToken.userId = 0;
92 authToken.authenticatorId = 0;
93 authToken.authenticatorType = ::android::hardware::keymaster::HardwareAuthenticatorType::NONE;
94 authToken.timestamp.milliSeconds = 0;
95 authToken.mac.clear();
96 verificationToken.challenge = 0;
97 verificationToken.timestamp.milliSeconds = 0;
98 verificationToken.securityLevel = ::android::hardware::keymaster::SecurityLevel::SOFTWARE;
99 verificationToken.mac.clear();
David Zeuthen81603152020-02-11 22:04:24 -0500100
101 // Here's the actual test data:
Selene Huang92b61d62020-03-04 02:24:16 -0800102 const vector<test_utils::TestEntryData> testEntries = {
David Zeuthen81603152020-02-11 22:04:24 -0500103 {"PersonalData", "Last name", string("Turing"), vector<int32_t>{0, 1}},
104 {"PersonalData", "Birth date", string("19120623"), vector<int32_t>{0, 1}},
105 {"PersonalData", "First name", string("Alan"), vector<int32_t>{0, 1}},
106 {"PersonalData", "Home address", string("Maida Vale, London, England"),
107 vector<int32_t>{0}},
108 {"Image", "Portrait image", portraitImage, vector<int32_t>{0, 1}},
109 };
110 const vector<int32_t> testEntriesEntryCounts = {static_cast<int32_t>(testEntries.size() - 1),
111 1u};
112 HardwareInformation hwInfo;
113 ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
114
115 string cborPretty;
116 sp<IWritableIdentityCredential> writableCredential;
Selene Huang92b61d62020-03-04 02:24:16 -0800117 ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_));
David Zeuthen81603152020-02-11 22:04:24 -0500118
119 string challenge = "attestationChallenge";
Selene Huang92b61d62020-03-04 02:24:16 -0800120 test_utils::AttestationData attData(writableCredential, challenge, {});
121 ASSERT_TRUE(attData.result.isOk())
122 << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
123 ASSERT_EQ(binder::Status::EX_NONE, attData.result.exceptionCode());
124 ASSERT_EQ(IIdentityCredentialStore::STATUS_OK, attData.result.serviceSpecificErrorCode());
125
David Zeuthen81603152020-02-11 22:04:24 -0500126 // TODO: set it to something random and check it's in the cert chain
Selene Huang92b61d62020-03-04 02:24:16 -0800127 ASSERT_GE(attData.attestationCertificate.size(), 2);
David Zeuthen81603152020-02-11 22:04:24 -0500128
David Zeuthen28edb102020-04-28 18:54:55 -0400129 // This is kinda of a hack but we need to give the size of
130 // ProofOfProvisioning that we'll expect to receive.
131 const int32_t expectedProofOfProvisioningSize = 262861 - 326 + readerCertificate.value().size();
132 // OK to fail, not available in v1 HAL
133 writableCredential->setExpectedProofOfProvisioningSize(expectedProofOfProvisioningSize);
David Zeuthen81603152020-02-11 22:04:24 -0500134 ASSERT_TRUE(
135 writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts)
136 .isOk());
137
Selene Huang92b61d62020-03-04 02:24:16 -0800138 optional<vector<SecureAccessControlProfile>> secureProfiles =
139 test_utils::AddAccessControlProfiles(writableCredential, testProfiles);
140 ASSERT_TRUE(secureProfiles);
David Zeuthen81603152020-02-11 22:04:24 -0500141
142 // Uses TestEntryData* pointer as key and values are the encrypted blobs. This
143 // is a little hacky but it works well enough.
Selene Huang92b61d62020-03-04 02:24:16 -0800144 map<const test_utils::TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
David Zeuthen81603152020-02-11 22:04:24 -0500145
146 for (const auto& entry : testEntries) {
Selene Huang92b61d62020-03-04 02:24:16 -0800147 ASSERT_TRUE(test_utils::AddEntry(writableCredential, entry, hwInfo.dataChunkSize,
148 encryptedBlobs, true));
David Zeuthen81603152020-02-11 22:04:24 -0500149 }
150
151 vector<uint8_t> credentialData;
152 vector<uint8_t> proofOfProvisioningSignature;
153 ASSERT_TRUE(
154 writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature)
155 .isOk());
156
157 optional<vector<uint8_t>> proofOfProvisioning =
158 support::coseSignGetPayload(proofOfProvisioningSignature);
159 ASSERT_TRUE(proofOfProvisioning);
160 cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
161 EXPECT_EQ(
162 "[\n"
163 " 'ProofOfProvisioning',\n"
164 " 'org.iso.18013-5.2019.mdl',\n"
165 " [\n"
166 " {\n"
167 " 'id' : 0,\n"
168 " 'readerCertificate' : <not printed>,\n"
169 " },\n"
170 " {\n"
171 " 'id' : 1,\n"
172 " },\n"
173 " ],\n"
174 " {\n"
175 " 'PersonalData' : [\n"
176 " {\n"
177 " 'name' : 'Last name',\n"
178 " 'value' : 'Turing',\n"
179 " 'accessControlProfiles' : [0, 1, ],\n"
180 " },\n"
181 " {\n"
182 " 'name' : 'Birth date',\n"
183 " 'value' : '19120623',\n"
184 " 'accessControlProfiles' : [0, 1, ],\n"
185 " },\n"
186 " {\n"
187 " 'name' : 'First name',\n"
188 " 'value' : 'Alan',\n"
189 " 'accessControlProfiles' : [0, 1, ],\n"
190 " },\n"
191 " {\n"
192 " 'name' : 'Home address',\n"
193 " 'value' : 'Maida Vale, London, England',\n"
194 " 'accessControlProfiles' : [0, ],\n"
195 " },\n"
196 " ],\n"
197 " 'Image' : [\n"
198 " {\n"
199 " 'name' : 'Portrait image',\n"
200 " 'value' : <bstr size=262134 sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
201 " 'accessControlProfiles' : [0, 1, ],\n"
202 " },\n"
203 " ],\n"
204 " },\n"
205 " true,\n"
206 "]",
207 cborPretty);
208
Selene Huang92b61d62020-03-04 02:24:16 -0800209 optional<vector<uint8_t>> credentialPubKey = support::certificateChainGetTopMostKey(
210 attData.attestationCertificate[0].encodedCertificate);
David Zeuthen81603152020-02-11 22:04:24 -0500211 ASSERT_TRUE(credentialPubKey);
212 EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
213 {}, // Additional data
214 credentialPubKey.value()));
215 writableCredential = nullptr;
216
217 // Now that the credential has been provisioned, read it back and check the
218 // correct data is returned.
219 sp<IIdentityCredential> credential;
220 ASSERT_TRUE(credentialStore_
221 ->getCredential(
222 CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
223 credentialData, &credential)
224 .isOk());
225 ASSERT_NE(credential, nullptr);
226
227 optional<vector<uint8_t>> readerEphemeralKeyPair = support::createEcKeyPair();
228 ASSERT_TRUE(readerEphemeralKeyPair);
229 optional<vector<uint8_t>> readerEphemeralPublicKey =
230 support::ecKeyPairGetPublicKey(readerEphemeralKeyPair.value());
231 ASSERT_TRUE(credential->setReaderEphemeralPublicKey(readerEphemeralPublicKey.value()).isOk());
232
233 vector<uint8_t> ephemeralKeyPair;
234 ASSERT_TRUE(credential->createEphemeralKeyPair(&ephemeralKeyPair).isOk());
235 optional<vector<uint8_t>> ephemeralPublicKey = support::ecKeyPairGetPublicKey(ephemeralKeyPair);
236
237 // Calculate requestData field and sign it with the reader key.
238 auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ephemeralPublicKey.value());
239 ASSERT_TRUE(getXYSuccess);
240 cppbor::Map deviceEngagement = cppbor::Map().add("ephX", ephX).add("ephY", ephY);
241 vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
242 vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
243 cppbor::Array sessionTranscript = cppbor::Array()
244 .add(cppbor::Semantic(24, deviceEngagementBytes))
245 .add(cppbor::Semantic(24, eReaderPubBytes));
246 vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode();
247
248 vector<uint8_t> itemsRequestBytes =
249 cppbor::Map("nameSpaces",
250 cppbor::Map()
251 .add("PersonalData", cppbor::Map()
252 .add("Last name", false)
253 .add("Birth date", false)
254 .add("First name", false)
255 .add("Home address", true))
256 .add("Image", cppbor::Map().add("Portrait image", false)))
257 .encode();
258 cborPretty = support::cborPrettyPrint(itemsRequestBytes, 32, {"EphemeralPublicKey"});
259 EXPECT_EQ(
260 "{\n"
261 " 'nameSpaces' : {\n"
262 " 'PersonalData' : {\n"
263 " 'Last name' : false,\n"
264 " 'Birth date' : false,\n"
265 " 'First name' : false,\n"
266 " 'Home address' : true,\n"
267 " },\n"
268 " 'Image' : {\n"
269 " 'Portrait image' : false,\n"
270 " },\n"
271 " },\n"
272 "}",
273 cborPretty);
274 vector<uint8_t> dataToSign = cppbor::Array()
275 .add("ReaderAuthentication")
276 .add(sessionTranscript.clone())
277 .add(cppbor::Semantic(24, itemsRequestBytes))
278 .encode();
279 optional<vector<uint8_t>> readerSignature =
Selene Huang92b61d62020-03-04 02:24:16 -0800280 support::coseSignEcDsa(readerKey, {}, // content
281 dataToSign, // detached content
David Zeuthen81603152020-02-11 22:04:24 -0500282 readerCertificate.value());
283 ASSERT_TRUE(readerSignature);
284
David Zeuthene35797f2020-02-27 14:25:54 -0500285 // Generate the key that will be used to sign AuthenticatedData.
286 vector<uint8_t> signingKeyBlob;
287 Certificate signingKeyCertificate;
288 ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
289
David Zeuthen28edb102020-04-28 18:54:55 -0400290 vector<RequestNamespace> requestedNamespaces = test_utils::buildRequestNamespaces(testEntries);
David Zeuthena8ed82c2020-05-08 10:03:28 -0400291 // OK to fail, not available in v1 HAL
292 credential->setRequestedNamespaces(requestedNamespaces).isOk();
293 // OK to fail, not available in v1 HAL
294 credential->setVerificationToken(verificationToken);
David Zeuthen81603152020-02-11 22:04:24 -0500295 ASSERT_TRUE(credential
Selene Huang92b61d62020-03-04 02:24:16 -0800296 ->startRetrieval(secureProfiles.value(), authToken, itemsRequestBytes,
David Zeuthene35797f2020-02-27 14:25:54 -0500297 signingKeyBlob, sessionTranscriptBytes,
298 readerSignature.value(), testEntriesEntryCounts)
David Zeuthen81603152020-02-11 22:04:24 -0500299 .isOk());
300
301 for (const auto& entry : testEntries) {
302 ASSERT_TRUE(credential
303 ->startRetrieveEntryValue(entry.nameSpace, entry.name,
304 entry.valueCbor.size(), entry.profileIds)
305 .isOk());
306
307 auto it = encryptedBlobs.find(&entry);
308 ASSERT_NE(it, encryptedBlobs.end());
309 const vector<vector<uint8_t>>& encryptedChunks = it->second;
310
311 vector<uint8_t> content;
312 for (const auto& encryptedChunk : encryptedChunks) {
313 vector<uint8_t> chunk;
314 ASSERT_TRUE(credential->retrieveEntryValue(encryptedChunk, &chunk).isOk());
315 content.insert(content.end(), chunk.begin(), chunk.end());
316 }
317 EXPECT_EQ(content, entry.valueCbor);
318 }
319
David Zeuthen81603152020-02-11 22:04:24 -0500320 vector<uint8_t> mac;
321 vector<uint8_t> deviceNameSpacesBytes;
David Zeuthene35797f2020-02-27 14:25:54 -0500322 ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesBytes).isOk());
David Zeuthen81603152020-02-11 22:04:24 -0500323 cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {});
324 ASSERT_EQ(
325 "{\n"
326 " 'PersonalData' : {\n"
327 " 'Last name' : 'Turing',\n"
328 " 'Birth date' : '19120623',\n"
329 " 'First name' : 'Alan',\n"
330 " 'Home address' : 'Maida Vale, London, England',\n"
331 " },\n"
332 " 'Image' : {\n"
333 " 'Portrait image' : <bstr size=262134 "
334 "sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
335 " },\n"
336 "}",
337 cborPretty);
338 // The data that is MACed is ["DeviceAuthentication", sessionTranscriptBytes, docType,
339 // deviceNameSpacesBytes] so build up that structure
340 cppbor::Array deviceAuthentication;
341 deviceAuthentication.add("DeviceAuthentication");
342 deviceAuthentication.add(sessionTranscript.clone());
Selene Huang92b61d62020-03-04 02:24:16 -0800343
344 string docType = "org.iso.18013-5.2019.mdl";
David Zeuthen81603152020-02-11 22:04:24 -0500345 deviceAuthentication.add(docType);
346 deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
347 vector<uint8_t> encodedDeviceAuthentication = deviceAuthentication.encode();
348 optional<vector<uint8_t>> signingPublicKey =
349 support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
350 EXPECT_TRUE(signingPublicKey);
351
352 // Derive the key used for MACing.
353 optional<vector<uint8_t>> readerEphemeralPrivateKey =
354 support::ecKeyPairGetPrivateKey(readerEphemeralKeyPair.value());
355 optional<vector<uint8_t>> sharedSecret =
356 support::ecdh(signingPublicKey.value(), readerEphemeralPrivateKey.value());
357 ASSERT_TRUE(sharedSecret);
358 vector<uint8_t> salt = {0x00};
359 vector<uint8_t> info = {};
360 optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
361 ASSERT_TRUE(derivedKey);
362 optional<vector<uint8_t>> calculatedMac =
363 support::coseMac0(derivedKey.value(), {}, // payload
364 encodedDeviceAuthentication); // detached content
365 ASSERT_TRUE(calculatedMac);
366 EXPECT_EQ(mac, calculatedMac);
367}
368
369INSTANTIATE_TEST_SUITE_P(
370 Identity, IdentityAidl,
371 testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
372 android::PrintInstanceNameToString);
373// INSTANTIATE_TEST_SUITE_P(Identity, IdentityAidl,
374// testing::Values("android.hardware.identity.IIdentityCredentialStore/default"));
375
376} // namespace android::hardware::identity
377
378int main(int argc, char** argv) {
379 ::testing::InitGoogleTest(&argc, argv);
380 ::android::ProcessState::self()->setThreadPoolMaxThreadCount(1);
381 ::android::ProcessState::self()->startThreadPool();
382 return RUN_ALL_TESTS();
383}