blob: 50e6cceb4c812ae32ccefc5d7f8c515395cb7a63 [file] [log] [blame]
Shawn Willden274bb552020-09-30 22:39:22 -06001/*
2 * Copyright (C) 2020 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 "VtsRemotelyProvisionableComponentTests"
18
19#include <RemotelyProvisionedComponent.h>
20#include <aidl/Gtest.h>
21#include <aidl/Vintf.h>
22#include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
23#include <aidl/android/hardware/security/keymint/SecurityLevel.h>
24#include <android/binder_manager.h>
25#include <cppbor_parse.h>
26#include <cppcose/cppcose.h>
27#include <gmock/gmock.h>
28#include <gtest/gtest.h>
29#include <keymaster/keymaster_configuration.h>
30#include <remote_prov/remote_prov_utils.h>
31
32namespace aidl::android::hardware::security::keymint::test {
33
34using ::std::string;
35using ::std::vector;
36
37namespace {
38
39#define INSTANTIATE_REM_PROV_AIDL_TEST(name) \
40 INSTANTIATE_TEST_SUITE_P( \
41 PerInstance, name, \
42 testing::ValuesIn(VtsRemotelyProvisionedComponentTests::build_params()), \
43 ::android::PrintInstanceNameToString)
44
45using bytevec = std::vector<uint8_t>;
46using testing::MatchesRegex;
47using namespace remote_prov;
48using namespace keymaster;
49
50bytevec string_to_bytevec(const char* s) {
51 const uint8_t* p = reinterpret_cast<const uint8_t*>(s);
52 return bytevec(p, p + strlen(s));
53}
54
55} // namespace
56
57class VtsRemotelyProvisionedComponentTests : public testing::TestWithParam<std::string> {
58 public:
59 virtual void SetUp() override {
60 if (AServiceManager_isDeclared(GetParam().c_str())) {
61 ::ndk::SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
62 provisionable_ = IRemotelyProvisionedComponent::fromBinder(binder);
63 }
64 ASSERT_NE(provisionable_, nullptr);
65 }
66
67 static vector<string> build_params() {
68 auto params = ::android::getAidlHalInstanceNames(IRemotelyProvisionedComponent::descriptor);
69 return params;
70 }
71
72 protected:
73 std::shared_ptr<IRemotelyProvisionedComponent> provisionable_;
74};
75
76using GenerateKeyTests = VtsRemotelyProvisionedComponentTests;
77
78INSTANTIATE_REM_PROV_AIDL_TEST(GenerateKeyTests);
79
80/**
81 * Generate and validate a production-mode key. MAC tag can't be verified.
82 */
Max Bires126869a2021-02-21 18:32:59 -080083TEST_P(GenerateKeyTests, generateEcdsaP256Key_prodMode) {
Shawn Willden274bb552020-09-30 22:39:22 -060084 MacedPublicKey macedPubKey;
85 bytevec privateKeyBlob;
86 bool testMode = false;
87 auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
88 ASSERT_TRUE(status.isOk());
89
90 auto [coseMac0, _, mac0ParseErr] = cppbor::parse(macedPubKey.macedKey);
91 ASSERT_TRUE(coseMac0) << "COSE Mac0 parse failed " << mac0ParseErr;
92
93 ASSERT_NE(coseMac0->asArray(), nullptr);
94 ASSERT_EQ(coseMac0->asArray()->size(), kCoseMac0EntryCount);
95
96 auto protParms = coseMac0->asArray()->get(kCoseMac0ProtectedParams)->asBstr();
97 ASSERT_NE(protParms, nullptr);
98 ASSERT_EQ(cppbor::prettyPrint(protParms->value()), "{\n 1 : 5,\n}");
99
David Drysdale31a2b562021-03-15 14:36:57 +0000100 auto unprotParms = coseMac0->asArray()->get(kCoseMac0UnprotectedParams)->asMap();
Shawn Willden274bb552020-09-30 22:39:22 -0600101 ASSERT_NE(unprotParms, nullptr);
David Drysdale31a2b562021-03-15 14:36:57 +0000102 ASSERT_EQ(unprotParms->size(), 0);
Shawn Willden274bb552020-09-30 22:39:22 -0600103
104 auto payload = coseMac0->asArray()->get(kCoseMac0Payload)->asBstr();
105 ASSERT_NE(payload, nullptr);
106 auto [parsedPayload, __, payloadParseErr] = cppbor::parse(payload->value());
107 ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;
108 EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
109 MatchesRegex("{\n"
110 " 1 : 2,\n"
111 " 3 : -7,\n"
112 " -1 : 1,\n"
113 // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a sequence of
114 // 32 hexadecimal bytes, enclosed in braces and separated by commas.
115 // In this case, some Ed25519 public key.
116 " -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"
117 " -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"
118 "}"));
119
120 auto coseMac0Tag = coseMac0->asArray()->get(kCoseMac0Tag)->asBstr();
121 ASSERT_TRUE(coseMac0Tag);
122 auto extractedTag = coseMac0Tag->value();
123 EXPECT_EQ(extractedTag.size(), 32U);
124
125 // Compare with tag generated with kTestMacKey. Shouldn't match.
126 auto testTag = cppcose::generateCoseMac0Mac(remote_prov::kTestMacKey, {} /* external_aad */,
127 payload->value());
128 ASSERT_TRUE(testTag) << "Tag calculation failed: " << testTag.message();
129
130 EXPECT_NE(*testTag, extractedTag);
131}
132
133/**
134 * Generate and validate a test-mode key.
135 */
Max Bires126869a2021-02-21 18:32:59 -0800136TEST_P(GenerateKeyTests, generateEcdsaP256Key_testMode) {
Shawn Willden274bb552020-09-30 22:39:22 -0600137 MacedPublicKey macedPubKey;
138 bytevec privateKeyBlob;
139 bool testMode = true;
140 auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
141 ASSERT_TRUE(status.isOk());
142
143 auto [coseMac0, _, mac0ParseErr] = cppbor::parse(macedPubKey.macedKey);
144 ASSERT_TRUE(coseMac0) << "COSE Mac0 parse failed " << mac0ParseErr;
145
146 ASSERT_NE(coseMac0->asArray(), nullptr);
147 ASSERT_EQ(coseMac0->asArray()->size(), kCoseMac0EntryCount);
148
149 auto protParms = coseMac0->asArray()->get(kCoseMac0ProtectedParams)->asBstr();
150 ASSERT_NE(protParms, nullptr);
151 ASSERT_EQ(cppbor::prettyPrint(protParms->value()), "{\n 1 : 5,\n}");
152
David Drysdale31a2b562021-03-15 14:36:57 +0000153 auto unprotParms = coseMac0->asArray()->get(kCoseMac0UnprotectedParams)->asMap();
Shawn Willden274bb552020-09-30 22:39:22 -0600154 ASSERT_NE(unprotParms, nullptr);
David Drysdale31a2b562021-03-15 14:36:57 +0000155 ASSERT_EQ(unprotParms->size(), 0);
Shawn Willden274bb552020-09-30 22:39:22 -0600156
157 auto payload = coseMac0->asArray()->get(kCoseMac0Payload)->asBstr();
158 ASSERT_NE(payload, nullptr);
159 auto [parsedPayload, __, payloadParseErr] = cppbor::parse(payload->value());
160 ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;
161 EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
162 MatchesRegex("{\n"
163 " 1 : 2,\n"
164 " 3 : -7,\n"
165 " -1 : 1,\n"
166 // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a sequence of
167 // 32 hexadecimal bytes, enclosed in braces and separated by commas.
168 // In this case, some Ed25519 public key.
169 " -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"
170 " -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"
171 " -70000 : null,\n"
172 "}"));
173
174 auto coseMac0Tag = coseMac0->asArray()->get(kCoseMac0Tag)->asBstr();
175 ASSERT_TRUE(coseMac0);
176 auto extractedTag = coseMac0Tag->value();
177 EXPECT_EQ(extractedTag.size(), 32U);
178
179 // Compare with tag generated with kTestMacKey. Should match.
180 auto testTag = cppcose::generateCoseMac0Mac(remote_prov::kTestMacKey, {} /* external_aad */,
181 payload->value());
182 ASSERT_TRUE(testTag) << testTag.message();
183
184 EXPECT_EQ(*testTag, extractedTag);
185}
186
187class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests {
188 protected:
189 CertificateRequestTest() : eekId_(string_to_bytevec("eekid")) {
190 auto chain = generateEekChain(3, eekId_);
191 EXPECT_TRUE(chain) << chain.message();
192 if (chain) eekChain_ = chain.moveValue();
193 }
194
195 void generateKeys(bool testMode, size_t numKeys) {
196 keysToSign_ = std::vector<MacedPublicKey>(numKeys);
197 cborKeysToSign_ = cppbor::Array();
198
199 for (auto& key : keysToSign_) {
200 bytevec privateKeyBlob;
201 auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &key, &privateKeyBlob);
202 ASSERT_TRUE(status.isOk()) << status.getMessage();
203
204 auto [parsedMacedKey, _, parseErr] = cppbor::parse(key.macedKey);
205 ASSERT_TRUE(parsedMacedKey) << "Failed parsing MACed key: " << parseErr;
206 ASSERT_TRUE(parsedMacedKey->asArray()) << "COSE_Mac0 not an array?";
207 ASSERT_EQ(parsedMacedKey->asArray()->size(), kCoseMac0EntryCount);
208
209 auto& payload = parsedMacedKey->asArray()->get(kCoseMac0Payload);
210 ASSERT_TRUE(payload);
211 ASSERT_TRUE(payload->asBstr());
212
213 cborKeysToSign_.add(cppbor::EncodedItem(payload->asBstr()->value()));
214 }
215 }
216
217 bytevec eekId_;
218 EekChain eekChain_;
219 std::vector<MacedPublicKey> keysToSign_;
220 cppbor::Array cborKeysToSign_;
221};
222
223/**
224 * Generate an empty certificate request in test mode, and decrypt and verify the structure and
225 * content.
226 */
Max Bires126869a2021-02-21 18:32:59 -0800227TEST_P(CertificateRequestTest, EmptyRequest_testMode) {
Shawn Willden274bb552020-09-30 22:39:22 -0600228 bool testMode = true;
229 bytevec keysToSignMac;
230 ProtectedData protectedData;
231 auto challenge = randomBytes(32);
232 auto status = provisionable_->generateCertificateRequest(testMode, {} /* keysToSign */,
233 eekChain_.chain, challenge,
234 &keysToSignMac, &protectedData);
235 ASSERT_TRUE(status.isOk()) << status.getMessage();
236
237 auto [parsedProtectedData, _, protDataErrMsg] = cppbor::parse(protectedData.protectedData);
238 ASSERT_TRUE(parsedProtectedData) << protDataErrMsg;
239 ASSERT_TRUE(parsedProtectedData->asArray());
240 ASSERT_EQ(parsedProtectedData->asArray()->size(), kCoseEncryptEntryCount);
241
242 auto senderPubkey = getSenderPubKeyFromCoseEncrypt(parsedProtectedData);
243 ASSERT_TRUE(senderPubkey) << senderPubkey.message();
244 EXPECT_EQ(senderPubkey->second, eekId_);
245
246 auto sessionKey = x25519_HKDF_DeriveKey(eekChain_.last_pubkey, eekChain_.last_privkey,
247 senderPubkey->first, false /* senderIsA */);
248 ASSERT_TRUE(sessionKey) << sessionKey.message();
249
250 auto protectedDataPayload =
251 decryptCoseEncrypt(*sessionKey, parsedProtectedData.get(), bytevec{} /* aad */);
252 ASSERT_TRUE(protectedDataPayload) << protectedDataPayload.message();
253
254 auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(*protectedDataPayload);
255 ASSERT_TRUE(parsedPayload) << "Failed to parse payload: " << payloadErrMsg;
256 ASSERT_TRUE(parsedPayload->asArray());
257 EXPECT_EQ(parsedPayload->asArray()->size(), 2U);
258
259 auto& signedMac = parsedPayload->asArray()->get(0);
260 auto& bcc = parsedPayload->asArray()->get(1);
261 ASSERT_TRUE(signedMac && signedMac->asArray());
262 ASSERT_TRUE(bcc && bcc->asArray());
263
264 // BCC is [ pubkey, + BccEntry]
265 auto bccContents = validateBcc(bcc->asArray());
266 ASSERT_TRUE(bccContents) << "\n" << bccContents.message() << "\n" << prettyPrint(bcc.get());
267 ASSERT_GT(bccContents->size(), 0U);
268
269 auto& signingKey = bccContents->back().pubKey;
270 auto macKey = verifyAndParseCoseSign1(testMode, signedMac->asArray(), signingKey,
271 cppbor::Array() // DeviceInfo
272 .add(challenge) //
273 .add(cppbor::Map())
274 .encode());
275 ASSERT_TRUE(macKey) << macKey.message();
276
277 auto coseMac0 = cppbor::Array()
278 .add(cppbor::Map() // protected
279 .add(ALGORITHM, HMAC_256)
280 .canonicalize()
281 .encode())
David Drysdale31a2b562021-03-15 14:36:57 +0000282 .add(cppbor::Map()) // unprotected
Shawn Willden274bb552020-09-30 22:39:22 -0600283 .add(cppbor::Array().encode()) // payload (keysToSign)
284 .add(std::move(keysToSignMac)); // tag
285
286 auto macPayload = verifyAndParseCoseMac0(&coseMac0, *macKey);
287 ASSERT_TRUE(macPayload) << macPayload.message();
288}
289
290/**
291 * Generate an empty certificate request in prod mode. Generation will fail because we don't have a
292 * valid GEEK.
293 *
294 * TODO(swillden): Get a valid GEEK and use it so the generation can succeed, though we won't be
295 * able to decrypt.
296 */
Max Bires126869a2021-02-21 18:32:59 -0800297TEST_P(CertificateRequestTest, EmptyRequest_prodMode) {
Shawn Willden274bb552020-09-30 22:39:22 -0600298 bool testMode = false;
299 bytevec keysToSignMac;
300 ProtectedData protectedData;
301 auto challenge = randomBytes(32);
302 auto status = provisionable_->generateCertificateRequest(testMode, {} /* keysToSign */,
303 eekChain_.chain, challenge,
304 &keysToSignMac, &protectedData);
305 ASSERT_FALSE(status.isOk());
306 ASSERT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
307}
308
309/**
310 * Generate a non-empty certificate request in test mode. Decrypt, parse and validate the contents.
311 */
Max Bires126869a2021-02-21 18:32:59 -0800312TEST_P(CertificateRequestTest, NonEmptyRequest_testMode) {
Shawn Willden274bb552020-09-30 22:39:22 -0600313 bool testMode = true;
314 generateKeys(testMode, 4 /* numKeys */);
315
316 bytevec keysToSignMac;
317 ProtectedData protectedData;
318 auto challenge = randomBytes(32);
319 auto status = provisionable_->generateCertificateRequest(
320 testMode, keysToSign_, eekChain_.chain, challenge, &keysToSignMac, &protectedData);
321 ASSERT_TRUE(status.isOk()) << status.getMessage();
322
323 auto [parsedProtectedData, _, protDataErrMsg] = cppbor::parse(protectedData.protectedData);
324 ASSERT_TRUE(parsedProtectedData) << protDataErrMsg;
325 ASSERT_TRUE(parsedProtectedData->asArray());
326 ASSERT_EQ(parsedProtectedData->asArray()->size(), kCoseEncryptEntryCount);
327
328 auto senderPubkey = getSenderPubKeyFromCoseEncrypt(parsedProtectedData);
329 ASSERT_TRUE(senderPubkey) << senderPubkey.message();
330 EXPECT_EQ(senderPubkey->second, eekId_);
331
332 auto sessionKey = x25519_HKDF_DeriveKey(eekChain_.last_pubkey, eekChain_.last_privkey,
333 senderPubkey->first, false /* senderIsA */);
334 ASSERT_TRUE(sessionKey) << sessionKey.message();
335
336 auto protectedDataPayload =
337 decryptCoseEncrypt(*sessionKey, parsedProtectedData.get(), bytevec{} /* aad */);
338 ASSERT_TRUE(protectedDataPayload) << protectedDataPayload.message();
339
340 auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(*protectedDataPayload);
341 ASSERT_TRUE(parsedPayload) << "Failed to parse payload: " << payloadErrMsg;
342 ASSERT_TRUE(parsedPayload->asArray());
343 EXPECT_EQ(parsedPayload->asArray()->size(), 2U);
344
345 auto& signedMac = parsedPayload->asArray()->get(0);
346 auto& bcc = parsedPayload->asArray()->get(1);
347 ASSERT_TRUE(signedMac && signedMac->asArray());
348 ASSERT_TRUE(bcc);
349
350 auto bccContents = validateBcc(bcc->asArray());
351 ASSERT_TRUE(bccContents) << "\n" << prettyPrint(bcc.get());
352 ASSERT_GT(bccContents->size(), 0U);
353
354 auto& signingKey = bccContents->back().pubKey;
355 auto macKey = verifyAndParseCoseSign1(testMode, signedMac->asArray(), signingKey,
356 cppbor::Array() // DeviceInfo
357 .add(challenge) //
358 .add(cppbor::Array())
359 .encode());
360 ASSERT_TRUE(macKey) << macKey.message();
361
362 auto coseMac0 = cppbor::Array()
363 .add(cppbor::Map() // protected
364 .add(ALGORITHM, HMAC_256)
365 .canonicalize()
366 .encode())
David Drysdale31a2b562021-03-15 14:36:57 +0000367 .add(cppbor::Map()) // unprotected
Shawn Willden274bb552020-09-30 22:39:22 -0600368 .add(cborKeysToSign_.encode()) // payload
369 .add(std::move(keysToSignMac)); // tag
370
371 auto macPayload = verifyAndParseCoseMac0(&coseMac0, *macKey);
372 ASSERT_TRUE(macPayload) << macPayload.message();
373}
374
375/**
376 * Generate a non-empty certificate request in prod mode. Must fail because we don't have a valid
377 * GEEK.
378 *
379 * TODO(swillden): Get a valid GEEK and use it so the generation can succeed, though we won't be
380 * able to decrypt.
381 */
Max Bires126869a2021-02-21 18:32:59 -0800382TEST_P(CertificateRequestTest, NonEmptyRequest_prodMode) {
Shawn Willden274bb552020-09-30 22:39:22 -0600383 bool testMode = false;
384 generateKeys(testMode, 4 /* numKeys */);
385
386 bytevec keysToSignMac;
387 ProtectedData protectedData;
388 auto challenge = randomBytes(32);
389 auto status = provisionable_->generateCertificateRequest(
390 testMode, keysToSign_, eekChain_.chain, challenge, &keysToSignMac, &protectedData);
391 ASSERT_FALSE(status.isOk());
392 ASSERT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
393}
394
395/**
396 * Generate a non-empty certificate request in test mode, with prod keys. Must fail with
397 * STATUS_PRODUCTION_KEY_IN_TEST_REQUEST.
398 */
Max Bires126869a2021-02-21 18:32:59 -0800399TEST_P(CertificateRequestTest, NonEmptyRequest_prodKeyInTestCert) {
Shawn Willden274bb552020-09-30 22:39:22 -0600400 generateKeys(false /* testMode */, 2 /* numKeys */);
401
402 bytevec keysToSignMac;
403 ProtectedData protectedData;
404 auto challenge = randomBytes(32);
405 auto status = provisionable_->generateCertificateRequest(true /* testMode */, keysToSign_,
406 eekChain_.chain, challenge,
407 &keysToSignMac, &protectedData);
408 ASSERT_FALSE(status.isOk());
409 ASSERT_EQ(status.getServiceSpecificError(),
410 BnRemotelyProvisionedComponent::STATUS_PRODUCTION_KEY_IN_TEST_REQUEST);
411}
412
413/**
414 * Generate a non-empty certificate request in prod mode, with test keys. Must fail with
415 * STATUS_TEST_KEY_IN_PRODUCTION_REQUEST.
416 */
Max Bires126869a2021-02-21 18:32:59 -0800417TEST_P(CertificateRequestTest, NonEmptyRequest_testKeyInProdCert) {
Shawn Willden274bb552020-09-30 22:39:22 -0600418 generateKeys(true /* testMode */, 2 /* numKeys */);
419
420 bytevec keysToSignMac;
421 ProtectedData protectedData;
422 auto status = provisionable_->generateCertificateRequest(
423 false /* testMode */, keysToSign_, eekChain_.chain, randomBytes(32) /* challenge */,
424 &keysToSignMac, &protectedData);
425 ASSERT_FALSE(status.isOk());
426 ASSERT_EQ(status.getServiceSpecificError(),
427 BnRemotelyProvisionedComponent::STATUS_TEST_KEY_IN_PRODUCTION_REQUEST);
428}
429
430INSTANTIATE_REM_PROV_AIDL_TEST(CertificateRequestTest);
431
432} // namespace aidl::android::hardware::security::keymint::test