blob: 807feabf80689221e4e1628c9442d173bc2b0f6a [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;
46
David Zeuthen81603152020-02-11 22:04:24 -050047class IdentityAidl : public testing::TestWithParam<std::string> {
48 public:
49 virtual void SetUp() override {
50 credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
51 String16(GetParam().c_str()));
52 ASSERT_NE(credentialStore_, nullptr);
53 }
54
55 sp<IIdentityCredentialStore> credentialStore_;
56};
57
58TEST_P(IdentityAidl, hardwareInformation) {
59 HardwareInformation info;
60 ASSERT_TRUE(credentialStore_->getHardwareInformation(&info).isOk());
61 ASSERT_GT(info.credentialStoreName.size(), 0);
62 ASSERT_GT(info.credentialStoreAuthorName.size(), 0);
63 ASSERT_GE(info.dataChunkSize, 256);
64}
65
66TEST_P(IdentityAidl, createAndRetrieveCredential) {
67 // First, generate a key-pair for the reader since its public key will be
68 // part of the request data.
Selene Huang92b61d62020-03-04 02:24:16 -080069 vector<uint8_t> readerKey;
70 optional<vector<uint8_t>> readerCertificate =
71 test_utils::GenerateReaderCertificate("1234", readerKey);
David Zeuthen81603152020-02-11 22:04:24 -050072 ASSERT_TRUE(readerCertificate);
73
74 // Make the portrait image really big (just shy of 256 KiB) to ensure that
75 // the chunking code gets exercised.
76 vector<uint8_t> portraitImage;
Selene Huang92b61d62020-03-04 02:24:16 -080077 test_utils::SetImageData(portraitImage);
David Zeuthen81603152020-02-11 22:04:24 -050078
79 // Access control profiles:
Selene Huang92b61d62020-03-04 02:24:16 -080080 const vector<test_utils::TestProfile> testProfiles = {// Profile 0 (reader authentication)
81 {0, readerCertificate.value(), false, 0},
82 // Profile 1 (no authentication)
83 {1, {}, false, 0}};
David Zeuthen81603152020-02-11 22:04:24 -050084
85 HardwareAuthToken authToken;
86
87 // Here's the actual test data:
Selene Huang92b61d62020-03-04 02:24:16 -080088 const vector<test_utils::TestEntryData> testEntries = {
David Zeuthen81603152020-02-11 22:04:24 -050089 {"PersonalData", "Last name", string("Turing"), vector<int32_t>{0, 1}},
90 {"PersonalData", "Birth date", string("19120623"), vector<int32_t>{0, 1}},
91 {"PersonalData", "First name", string("Alan"), vector<int32_t>{0, 1}},
92 {"PersonalData", "Home address", string("Maida Vale, London, England"),
93 vector<int32_t>{0}},
94 {"Image", "Portrait image", portraitImage, vector<int32_t>{0, 1}},
95 };
96 const vector<int32_t> testEntriesEntryCounts = {static_cast<int32_t>(testEntries.size() - 1),
97 1u};
98 HardwareInformation hwInfo;
99 ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
100
101 string cborPretty;
102 sp<IWritableIdentityCredential> writableCredential;
Selene Huang92b61d62020-03-04 02:24:16 -0800103 ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_));
David Zeuthen81603152020-02-11 22:04:24 -0500104
105 string challenge = "attestationChallenge";
Selene Huang92b61d62020-03-04 02:24:16 -0800106 test_utils::AttestationData attData(writableCredential, challenge, {});
107 ASSERT_TRUE(attData.result.isOk())
108 << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
109 ASSERT_EQ(binder::Status::EX_NONE, attData.result.exceptionCode());
110 ASSERT_EQ(IIdentityCredentialStore::STATUS_OK, attData.result.serviceSpecificErrorCode());
111
David Zeuthen81603152020-02-11 22:04:24 -0500112 // TODO: set it to something random and check it's in the cert chain
Selene Huang92b61d62020-03-04 02:24:16 -0800113 ASSERT_GE(attData.attestationCertificate.size(), 2);
David Zeuthen81603152020-02-11 22:04:24 -0500114
David Zeuthen28edb102020-04-28 18:54:55 -0400115 // This is kinda of a hack but we need to give the size of
116 // ProofOfProvisioning that we'll expect to receive.
117 const int32_t expectedProofOfProvisioningSize = 262861 - 326 + readerCertificate.value().size();
118 // OK to fail, not available in v1 HAL
119 writableCredential->setExpectedProofOfProvisioningSize(expectedProofOfProvisioningSize);
David Zeuthen81603152020-02-11 22:04:24 -0500120 ASSERT_TRUE(
121 writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts)
122 .isOk());
123
Selene Huang92b61d62020-03-04 02:24:16 -0800124 optional<vector<SecureAccessControlProfile>> secureProfiles =
125 test_utils::AddAccessControlProfiles(writableCredential, testProfiles);
126 ASSERT_TRUE(secureProfiles);
David Zeuthen81603152020-02-11 22:04:24 -0500127
128 // Uses TestEntryData* pointer as key and values are the encrypted blobs. This
129 // is a little hacky but it works well enough.
Selene Huang92b61d62020-03-04 02:24:16 -0800130 map<const test_utils::TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
David Zeuthen81603152020-02-11 22:04:24 -0500131
132 for (const auto& entry : testEntries) {
Selene Huang92b61d62020-03-04 02:24:16 -0800133 ASSERT_TRUE(test_utils::AddEntry(writableCredential, entry, hwInfo.dataChunkSize,
134 encryptedBlobs, true));
David Zeuthen81603152020-02-11 22:04:24 -0500135 }
136
137 vector<uint8_t> credentialData;
138 vector<uint8_t> proofOfProvisioningSignature;
139 ASSERT_TRUE(
140 writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature)
141 .isOk());
142
143 optional<vector<uint8_t>> proofOfProvisioning =
144 support::coseSignGetPayload(proofOfProvisioningSignature);
145 ASSERT_TRUE(proofOfProvisioning);
146 cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
147 EXPECT_EQ(
148 "[\n"
149 " 'ProofOfProvisioning',\n"
150 " 'org.iso.18013-5.2019.mdl',\n"
151 " [\n"
152 " {\n"
153 " 'id' : 0,\n"
154 " 'readerCertificate' : <not printed>,\n"
155 " },\n"
156 " {\n"
157 " 'id' : 1,\n"
158 " },\n"
159 " ],\n"
160 " {\n"
161 " 'PersonalData' : [\n"
162 " {\n"
163 " 'name' : 'Last name',\n"
164 " 'value' : 'Turing',\n"
165 " 'accessControlProfiles' : [0, 1, ],\n"
166 " },\n"
167 " {\n"
168 " 'name' : 'Birth date',\n"
169 " 'value' : '19120623',\n"
170 " 'accessControlProfiles' : [0, 1, ],\n"
171 " },\n"
172 " {\n"
173 " 'name' : 'First name',\n"
174 " 'value' : 'Alan',\n"
175 " 'accessControlProfiles' : [0, 1, ],\n"
176 " },\n"
177 " {\n"
178 " 'name' : 'Home address',\n"
179 " 'value' : 'Maida Vale, London, England',\n"
180 " 'accessControlProfiles' : [0, ],\n"
181 " },\n"
182 " ],\n"
183 " 'Image' : [\n"
184 " {\n"
185 " 'name' : 'Portrait image',\n"
186 " 'value' : <bstr size=262134 sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
187 " 'accessControlProfiles' : [0, 1, ],\n"
188 " },\n"
189 " ],\n"
190 " },\n"
191 " true,\n"
192 "]",
193 cborPretty);
194
Selene Huang92b61d62020-03-04 02:24:16 -0800195 optional<vector<uint8_t>> credentialPubKey = support::certificateChainGetTopMostKey(
196 attData.attestationCertificate[0].encodedCertificate);
David Zeuthen81603152020-02-11 22:04:24 -0500197 ASSERT_TRUE(credentialPubKey);
198 EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
199 {}, // Additional data
200 credentialPubKey.value()));
201 writableCredential = nullptr;
202
203 // Now that the credential has been provisioned, read it back and check the
204 // correct data is returned.
205 sp<IIdentityCredential> credential;
206 ASSERT_TRUE(credentialStore_
207 ->getCredential(
208 CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
209 credentialData, &credential)
210 .isOk());
211 ASSERT_NE(credential, nullptr);
212
213 optional<vector<uint8_t>> readerEphemeralKeyPair = support::createEcKeyPair();
214 ASSERT_TRUE(readerEphemeralKeyPair);
215 optional<vector<uint8_t>> readerEphemeralPublicKey =
216 support::ecKeyPairGetPublicKey(readerEphemeralKeyPair.value());
217 ASSERT_TRUE(credential->setReaderEphemeralPublicKey(readerEphemeralPublicKey.value()).isOk());
218
219 vector<uint8_t> ephemeralKeyPair;
220 ASSERT_TRUE(credential->createEphemeralKeyPair(&ephemeralKeyPair).isOk());
221 optional<vector<uint8_t>> ephemeralPublicKey = support::ecKeyPairGetPublicKey(ephemeralKeyPair);
222
223 // Calculate requestData field and sign it with the reader key.
224 auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ephemeralPublicKey.value());
225 ASSERT_TRUE(getXYSuccess);
226 cppbor::Map deviceEngagement = cppbor::Map().add("ephX", ephX).add("ephY", ephY);
227 vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
228 vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
229 cppbor::Array sessionTranscript = cppbor::Array()
230 .add(cppbor::Semantic(24, deviceEngagementBytes))
231 .add(cppbor::Semantic(24, eReaderPubBytes));
232 vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode();
233
234 vector<uint8_t> itemsRequestBytes =
235 cppbor::Map("nameSpaces",
236 cppbor::Map()
237 .add("PersonalData", cppbor::Map()
238 .add("Last name", false)
239 .add("Birth date", false)
240 .add("First name", false)
241 .add("Home address", true))
242 .add("Image", cppbor::Map().add("Portrait image", false)))
243 .encode();
244 cborPretty = support::cborPrettyPrint(itemsRequestBytes, 32, {"EphemeralPublicKey"});
245 EXPECT_EQ(
246 "{\n"
247 " 'nameSpaces' : {\n"
248 " 'PersonalData' : {\n"
249 " 'Last name' : false,\n"
250 " 'Birth date' : false,\n"
251 " 'First name' : false,\n"
252 " 'Home address' : true,\n"
253 " },\n"
254 " 'Image' : {\n"
255 " 'Portrait image' : false,\n"
256 " },\n"
257 " },\n"
258 "}",
259 cborPretty);
260 vector<uint8_t> dataToSign = cppbor::Array()
261 .add("ReaderAuthentication")
262 .add(sessionTranscript.clone())
263 .add(cppbor::Semantic(24, itemsRequestBytes))
264 .encode();
265 optional<vector<uint8_t>> readerSignature =
Selene Huang92b61d62020-03-04 02:24:16 -0800266 support::coseSignEcDsa(readerKey, {}, // content
267 dataToSign, // detached content
David Zeuthen81603152020-02-11 22:04:24 -0500268 readerCertificate.value());
269 ASSERT_TRUE(readerSignature);
270
David Zeuthene35797f2020-02-27 14:25:54 -0500271 // Generate the key that will be used to sign AuthenticatedData.
272 vector<uint8_t> signingKeyBlob;
273 Certificate signingKeyCertificate;
274 ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
275
David Zeuthen28edb102020-04-28 18:54:55 -0400276 vector<RequestNamespace> requestedNamespaces = test_utils::buildRequestNamespaces(testEntries);
277 ASSERT_TRUE(credential->setRequestedNamespaces(requestedNamespaces).isOk());
David Zeuthen81603152020-02-11 22:04:24 -0500278 ASSERT_TRUE(credential
Selene Huang92b61d62020-03-04 02:24:16 -0800279 ->startRetrieval(secureProfiles.value(), authToken, itemsRequestBytes,
David Zeuthene35797f2020-02-27 14:25:54 -0500280 signingKeyBlob, sessionTranscriptBytes,
281 readerSignature.value(), testEntriesEntryCounts)
David Zeuthen81603152020-02-11 22:04:24 -0500282 .isOk());
283
284 for (const auto& entry : testEntries) {
285 ASSERT_TRUE(credential
286 ->startRetrieveEntryValue(entry.nameSpace, entry.name,
287 entry.valueCbor.size(), entry.profileIds)
288 .isOk());
289
290 auto it = encryptedBlobs.find(&entry);
291 ASSERT_NE(it, encryptedBlobs.end());
292 const vector<vector<uint8_t>>& encryptedChunks = it->second;
293
294 vector<uint8_t> content;
295 for (const auto& encryptedChunk : encryptedChunks) {
296 vector<uint8_t> chunk;
297 ASSERT_TRUE(credential->retrieveEntryValue(encryptedChunk, &chunk).isOk());
298 content.insert(content.end(), chunk.begin(), chunk.end());
299 }
300 EXPECT_EQ(content, entry.valueCbor);
301 }
302
David Zeuthen81603152020-02-11 22:04:24 -0500303 vector<uint8_t> mac;
304 vector<uint8_t> deviceNameSpacesBytes;
David Zeuthene35797f2020-02-27 14:25:54 -0500305 ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesBytes).isOk());
David Zeuthen81603152020-02-11 22:04:24 -0500306 cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {});
307 ASSERT_EQ(
308 "{\n"
309 " 'PersonalData' : {\n"
310 " 'Last name' : 'Turing',\n"
311 " 'Birth date' : '19120623',\n"
312 " 'First name' : 'Alan',\n"
313 " 'Home address' : 'Maida Vale, London, England',\n"
314 " },\n"
315 " 'Image' : {\n"
316 " 'Portrait image' : <bstr size=262134 "
317 "sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
318 " },\n"
319 "}",
320 cborPretty);
321 // The data that is MACed is ["DeviceAuthentication", sessionTranscriptBytes, docType,
322 // deviceNameSpacesBytes] so build up that structure
323 cppbor::Array deviceAuthentication;
324 deviceAuthentication.add("DeviceAuthentication");
325 deviceAuthentication.add(sessionTranscript.clone());
Selene Huang92b61d62020-03-04 02:24:16 -0800326
327 string docType = "org.iso.18013-5.2019.mdl";
David Zeuthen81603152020-02-11 22:04:24 -0500328 deviceAuthentication.add(docType);
329 deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
330 vector<uint8_t> encodedDeviceAuthentication = deviceAuthentication.encode();
331 optional<vector<uint8_t>> signingPublicKey =
332 support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
333 EXPECT_TRUE(signingPublicKey);
334
335 // Derive the key used for MACing.
336 optional<vector<uint8_t>> readerEphemeralPrivateKey =
337 support::ecKeyPairGetPrivateKey(readerEphemeralKeyPair.value());
338 optional<vector<uint8_t>> sharedSecret =
339 support::ecdh(signingPublicKey.value(), readerEphemeralPrivateKey.value());
340 ASSERT_TRUE(sharedSecret);
341 vector<uint8_t> salt = {0x00};
342 vector<uint8_t> info = {};
343 optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
344 ASSERT_TRUE(derivedKey);
345 optional<vector<uint8_t>> calculatedMac =
346 support::coseMac0(derivedKey.value(), {}, // payload
347 encodedDeviceAuthentication); // detached content
348 ASSERT_TRUE(calculatedMac);
349 EXPECT_EQ(mac, calculatedMac);
350}
351
352INSTANTIATE_TEST_SUITE_P(
353 Identity, IdentityAidl,
354 testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
355 android::PrintInstanceNameToString);
356// INSTANTIATE_TEST_SUITE_P(Identity, IdentityAidl,
357// testing::Values("android.hardware.identity.IIdentityCredentialStore/default"));
358
359} // namespace android::hardware::identity
360
361int main(int argc, char** argv) {
362 ::testing::InitGoogleTest(&argc, argv);
363 ::android::ProcessState::self()->setThreadPoolMaxThreadCount(1);
364 ::android::ProcessState::self()->startThreadPool();
365 return RUN_ALL_TESTS();
366}