blob: 51928c9656b7448a78f15064561b10a039c6927d [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
David Drysdalec8400772021-03-11 12:35:11 +000055void check_cose_key(const vector<uint8_t>& data, bool testMode) {
56 auto [parsedPayload, __, payloadParseErr] = cppbor::parse(data);
57 ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;
58
59 // The following check assumes that canonical CBOR encoding is used for the COSE_Key.
60 if (testMode) {
61 EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
62 MatchesRegex("{\n"
63 " 1 : 2,\n" // kty: EC2
64 " 3 : -7,\n" // alg: ES256
65 " -1 : 1,\n" // EC id: P256
66 // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a
67 // sequence of 32 hexadecimal bytes, enclosed in braces and
68 // separated by commas. In this case, some Ed25519 public key.
69 " -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_x: data
70 " -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_y: data
71 " -70000 : null,\n" // test marker
72 "}"));
73 } else {
74 EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
75 MatchesRegex("{\n"
76 " 1 : 2,\n" // kty: EC2
77 " 3 : -7,\n" // alg: ES256
78 " -1 : 1,\n" // EC id: P256
79 // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a
80 // sequence of 32 hexadecimal bytes, enclosed in braces and
81 // separated by commas. In this case, some Ed25519 public key.
82 " -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_x: data
83 " -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_y: data
84 "}"));
85 }
86}
87
88void check_maced_pubkey(const MacedPublicKey& macedPubKey, bool testMode,
89 vector<uint8_t>* payload_value) {
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
99 // Header label:value of 'alg': HMAC-256
100 ASSERT_EQ(cppbor::prettyPrint(protParms->value()), "{\n 1 : 5,\n}");
101
102 auto unprotParms = coseMac0->asArray()->get(kCoseMac0UnprotectedParams)->asMap();
103 ASSERT_NE(unprotParms, nullptr);
104 ASSERT_EQ(unprotParms->size(), 0);
105
106 // The payload is a bstr holding an encoded COSE_Key
107 auto payload = coseMac0->asArray()->get(kCoseMac0Payload)->asBstr();
108 ASSERT_NE(payload, nullptr);
109 check_cose_key(payload->value(), testMode);
110
111 auto coseMac0Tag = coseMac0->asArray()->get(kCoseMac0Tag)->asBstr();
112 ASSERT_TRUE(coseMac0Tag);
113 auto extractedTag = coseMac0Tag->value();
114 EXPECT_EQ(extractedTag.size(), 32U);
115
116 // Compare with tag generated with kTestMacKey. Should only match in test mode
117 auto testTag = cppcose::generateCoseMac0Mac(remote_prov::kTestMacKey, {} /* external_aad */,
118 payload->value());
119 ASSERT_TRUE(testTag) << "Tag calculation failed: " << testTag.message();
120
121 if (testMode) {
122 EXPECT_EQ(*testTag, extractedTag);
123 } else {
124 EXPECT_NE(*testTag, extractedTag);
125 }
126 if (payload_value != nullptr) {
127 *payload_value = payload->value();
128 }
129}
130
Shawn Willden274bb552020-09-30 22:39:22 -0600131} // namespace
132
133class VtsRemotelyProvisionedComponentTests : public testing::TestWithParam<std::string> {
134 public:
135 virtual void SetUp() override {
136 if (AServiceManager_isDeclared(GetParam().c_str())) {
137 ::ndk::SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
138 provisionable_ = IRemotelyProvisionedComponent::fromBinder(binder);
139 }
140 ASSERT_NE(provisionable_, nullptr);
141 }
142
143 static vector<string> build_params() {
144 auto params = ::android::getAidlHalInstanceNames(IRemotelyProvisionedComponent::descriptor);
145 return params;
146 }
147
148 protected:
149 std::shared_ptr<IRemotelyProvisionedComponent> provisionable_;
150};
151
152using GenerateKeyTests = VtsRemotelyProvisionedComponentTests;
153
154INSTANTIATE_REM_PROV_AIDL_TEST(GenerateKeyTests);
155
156/**
157 * Generate and validate a production-mode key. MAC tag can't be verified.
158 */
Max Bires126869a2021-02-21 18:32:59 -0800159TEST_P(GenerateKeyTests, generateEcdsaP256Key_prodMode) {
Shawn Willden274bb552020-09-30 22:39:22 -0600160 MacedPublicKey macedPubKey;
161 bytevec privateKeyBlob;
162 bool testMode = false;
163 auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
164 ASSERT_TRUE(status.isOk());
165
David Drysdalec8400772021-03-11 12:35:11 +0000166 check_maced_pubkey(macedPubKey, testMode, nullptr);
Shawn Willden274bb552020-09-30 22:39:22 -0600167}
168
169/**
170 * Generate and validate a test-mode key.
171 */
Max Bires126869a2021-02-21 18:32:59 -0800172TEST_P(GenerateKeyTests, generateEcdsaP256Key_testMode) {
Shawn Willden274bb552020-09-30 22:39:22 -0600173 MacedPublicKey macedPubKey;
174 bytevec privateKeyBlob;
175 bool testMode = true;
176 auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
177 ASSERT_TRUE(status.isOk());
178
David Drysdalec8400772021-03-11 12:35:11 +0000179 check_maced_pubkey(macedPubKey, testMode, nullptr);
Shawn Willden274bb552020-09-30 22:39:22 -0600180}
181
182class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests {
183 protected:
David Drysdalec8400772021-03-11 12:35:11 +0000184 CertificateRequestTest() : eekId_(string_to_bytevec("eekid")), challenge_(randomBytes(32)) {
Shawn Willden274bb552020-09-30 22:39:22 -0600185 auto chain = generateEekChain(3, eekId_);
186 EXPECT_TRUE(chain) << chain.message();
187 if (chain) eekChain_ = chain.moveValue();
188 }
189
190 void generateKeys(bool testMode, size_t numKeys) {
191 keysToSign_ = std::vector<MacedPublicKey>(numKeys);
192 cborKeysToSign_ = cppbor::Array();
193
194 for (auto& key : keysToSign_) {
195 bytevec privateKeyBlob;
196 auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &key, &privateKeyBlob);
197 ASSERT_TRUE(status.isOk()) << status.getMessage();
198
David Drysdalec8400772021-03-11 12:35:11 +0000199 vector<uint8_t> payload_value;
200 check_maced_pubkey(key, testMode, &payload_value);
201 cborKeysToSign_.add(cppbor::EncodedItem(payload_value));
Shawn Willden274bb552020-09-30 22:39:22 -0600202 }
203 }
204
David Drysdalec8400772021-03-11 12:35:11 +0000205 void checkProtectedData(bool testMode, const cppbor::Array& keysToSign,
206 const bytevec& keysToSignMac, const ProtectedData& protectedData) {
207 auto [parsedProtectedData, _, protDataErrMsg] = cppbor::parse(protectedData.protectedData);
208 ASSERT_TRUE(parsedProtectedData) << protDataErrMsg;
209 ASSERT_TRUE(parsedProtectedData->asArray());
210 ASSERT_EQ(parsedProtectedData->asArray()->size(), kCoseEncryptEntryCount);
211
212 auto senderPubkey = getSenderPubKeyFromCoseEncrypt(parsedProtectedData);
213 ASSERT_TRUE(senderPubkey) << senderPubkey.message();
214 EXPECT_EQ(senderPubkey->second, eekId_);
215
216 auto sessionKey = x25519_HKDF_DeriveKey(eekChain_.last_pubkey, eekChain_.last_privkey,
217 senderPubkey->first, false /* senderIsA */);
218 ASSERT_TRUE(sessionKey) << sessionKey.message();
219
220 auto protectedDataPayload =
221 decryptCoseEncrypt(*sessionKey, parsedProtectedData.get(), bytevec{} /* aad */);
222 ASSERT_TRUE(protectedDataPayload) << protectedDataPayload.message();
223
224 auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(*protectedDataPayload);
225 ASSERT_TRUE(parsedPayload) << "Failed to parse payload: " << payloadErrMsg;
226 ASSERT_TRUE(parsedPayload->asArray());
227 EXPECT_EQ(parsedPayload->asArray()->size(), 2U);
228
229 auto& signedMac = parsedPayload->asArray()->get(0);
230 auto& bcc = parsedPayload->asArray()->get(1);
231 ASSERT_TRUE(signedMac && signedMac->asArray());
232 ASSERT_TRUE(bcc && bcc->asArray());
233
234 // BCC is [ pubkey, + BccEntry]
235 auto bccContents = validateBcc(bcc->asArray());
236 ASSERT_TRUE(bccContents) << "\n" << bccContents.message() << "\n" << prettyPrint(bcc.get());
237 ASSERT_GT(bccContents->size(), 0U);
238
239 auto& signingKey = bccContents->back().pubKey;
240 auto macKey = verifyAndParseCoseSign1(testMode, signedMac->asArray(), signingKey,
241 cppbor::Array() // DeviceInfo
242 .add(challenge_)
243 .add(cppbor::Map())
244 .encode());
245 ASSERT_TRUE(macKey) << macKey.message();
246
247 auto coseMac0 = cppbor::Array()
248 .add(cppbor::Map() // protected
249 .add(ALGORITHM, HMAC_256)
250 .canonicalize()
251 .encode())
252 .add(cppbor::Map()) // unprotected
253 .add(keysToSign.encode()) // payload (keysToSign)
254 .add(keysToSignMac); // tag
255
256 auto macPayload = verifyAndParseCoseMac0(&coseMac0, *macKey);
257 ASSERT_TRUE(macPayload) << macPayload.message();
258 }
259
Shawn Willden274bb552020-09-30 22:39:22 -0600260 bytevec eekId_;
261 EekChain eekChain_;
David Drysdalec8400772021-03-11 12:35:11 +0000262 bytevec challenge_;
Shawn Willden274bb552020-09-30 22:39:22 -0600263 std::vector<MacedPublicKey> keysToSign_;
264 cppbor::Array cborKeysToSign_;
265};
266
267/**
268 * Generate an empty certificate request in test mode, and decrypt and verify the structure and
269 * content.
270 */
Max Bires126869a2021-02-21 18:32:59 -0800271TEST_P(CertificateRequestTest, EmptyRequest_testMode) {
Shawn Willden274bb552020-09-30 22:39:22 -0600272 bool testMode = true;
273 bytevec keysToSignMac;
Max Biresfdbb9042021-03-23 12:43:38 -0700274 DeviceInfo deviceInfo;
Shawn Willden274bb552020-09-30 22:39:22 -0600275 ProtectedData protectedData;
Max Biresfdbb9042021-03-23 12:43:38 -0700276 auto status = provisionable_->generateCertificateRequest(
David Drysdalec8400772021-03-11 12:35:11 +0000277 testMode, {} /* keysToSign */, eekChain_.chain, challenge_, &deviceInfo, &protectedData,
Max Biresfdbb9042021-03-23 12:43:38 -0700278 &keysToSignMac);
Shawn Willden274bb552020-09-30 22:39:22 -0600279 ASSERT_TRUE(status.isOk()) << status.getMessage();
280
David Drysdalec8400772021-03-11 12:35:11 +0000281 checkProtectedData(testMode, cppbor::Array(), keysToSignMac, protectedData);
Shawn Willden274bb552020-09-30 22:39:22 -0600282}
283
284/**
285 * Generate an empty certificate request in prod mode. Generation will fail because we don't have a
286 * valid GEEK.
287 *
288 * TODO(swillden): Get a valid GEEK and use it so the generation can succeed, though we won't be
289 * able to decrypt.
290 */
Max Bires126869a2021-02-21 18:32:59 -0800291TEST_P(CertificateRequestTest, EmptyRequest_prodMode) {
Shawn Willden274bb552020-09-30 22:39:22 -0600292 bool testMode = false;
293 bytevec keysToSignMac;
Max Biresfdbb9042021-03-23 12:43:38 -0700294 DeviceInfo deviceInfo;
Shawn Willden274bb552020-09-30 22:39:22 -0600295 ProtectedData protectedData;
Max Biresfdbb9042021-03-23 12:43:38 -0700296 auto status = provisionable_->generateCertificateRequest(
David Drysdalec8400772021-03-11 12:35:11 +0000297 testMode, {} /* keysToSign */, eekChain_.chain, challenge_, &deviceInfo, &protectedData,
Max Biresfdbb9042021-03-23 12:43:38 -0700298 &keysToSignMac);
Shawn Willden274bb552020-09-30 22:39:22 -0600299 ASSERT_FALSE(status.isOk());
300 ASSERT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
301}
302
303/**
304 * Generate a non-empty certificate request in test mode. Decrypt, parse and validate the contents.
305 */
Max Bires126869a2021-02-21 18:32:59 -0800306TEST_P(CertificateRequestTest, NonEmptyRequest_testMode) {
Shawn Willden274bb552020-09-30 22:39:22 -0600307 bool testMode = true;
308 generateKeys(testMode, 4 /* numKeys */);
309
310 bytevec keysToSignMac;
Max Biresfdbb9042021-03-23 12:43:38 -0700311 DeviceInfo deviceInfo;
Shawn Willden274bb552020-09-30 22:39:22 -0600312 ProtectedData protectedData;
Max Biresfdbb9042021-03-23 12:43:38 -0700313 auto status = provisionable_->generateCertificateRequest(testMode, keysToSign_, eekChain_.chain,
David Drysdalec8400772021-03-11 12:35:11 +0000314 challenge_, &deviceInfo,
315 &protectedData, &keysToSignMac);
Shawn Willden274bb552020-09-30 22:39:22 -0600316 ASSERT_TRUE(status.isOk()) << status.getMessage();
317
David Drysdalec8400772021-03-11 12:35:11 +0000318 checkProtectedData(testMode, cborKeysToSign_, keysToSignMac, protectedData);
Shawn Willden274bb552020-09-30 22:39:22 -0600319}
320
321/**
322 * Generate a non-empty certificate request in prod mode. Must fail because we don't have a valid
323 * GEEK.
324 *
325 * TODO(swillden): Get a valid GEEK and use it so the generation can succeed, though we won't be
326 * able to decrypt.
327 */
Max Bires126869a2021-02-21 18:32:59 -0800328TEST_P(CertificateRequestTest, NonEmptyRequest_prodMode) {
Shawn Willden274bb552020-09-30 22:39:22 -0600329 bool testMode = false;
330 generateKeys(testMode, 4 /* numKeys */);
331
332 bytevec keysToSignMac;
Max Biresfdbb9042021-03-23 12:43:38 -0700333 DeviceInfo deviceInfo;
Shawn Willden274bb552020-09-30 22:39:22 -0600334 ProtectedData protectedData;
Max Biresfdbb9042021-03-23 12:43:38 -0700335 auto status = provisionable_->generateCertificateRequest(testMode, keysToSign_, eekChain_.chain,
David Drysdalec8400772021-03-11 12:35:11 +0000336 challenge_, &deviceInfo,
337 &protectedData, &keysToSignMac);
Shawn Willden274bb552020-09-30 22:39:22 -0600338 ASSERT_FALSE(status.isOk());
339 ASSERT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
340}
341
342/**
343 * Generate a non-empty certificate request in test mode, with prod keys. Must fail with
344 * STATUS_PRODUCTION_KEY_IN_TEST_REQUEST.
345 */
Max Bires126869a2021-02-21 18:32:59 -0800346TEST_P(CertificateRequestTest, NonEmptyRequest_prodKeyInTestCert) {
Shawn Willden274bb552020-09-30 22:39:22 -0600347 generateKeys(false /* testMode */, 2 /* numKeys */);
348
349 bytevec keysToSignMac;
Max Biresfdbb9042021-03-23 12:43:38 -0700350 DeviceInfo deviceInfo;
Shawn Willden274bb552020-09-30 22:39:22 -0600351 ProtectedData protectedData;
Max Biresfdbb9042021-03-23 12:43:38 -0700352 auto status = provisionable_->generateCertificateRequest(
David Drysdalec8400772021-03-11 12:35:11 +0000353 true /* testMode */, keysToSign_, eekChain_.chain, challenge_, &deviceInfo,
Max Biresfdbb9042021-03-23 12:43:38 -0700354 &protectedData, &keysToSignMac);
Shawn Willden274bb552020-09-30 22:39:22 -0600355 ASSERT_FALSE(status.isOk());
356 ASSERT_EQ(status.getServiceSpecificError(),
357 BnRemotelyProvisionedComponent::STATUS_PRODUCTION_KEY_IN_TEST_REQUEST);
358}
359
360/**
361 * Generate a non-empty certificate request in prod mode, with test keys. Must fail with
362 * STATUS_TEST_KEY_IN_PRODUCTION_REQUEST.
363 */
Max Bires126869a2021-02-21 18:32:59 -0800364TEST_P(CertificateRequestTest, NonEmptyRequest_testKeyInProdCert) {
Shawn Willden274bb552020-09-30 22:39:22 -0600365 generateKeys(true /* testMode */, 2 /* numKeys */);
366
367 bytevec keysToSignMac;
Max Biresfdbb9042021-03-23 12:43:38 -0700368 DeviceInfo deviceInfo;
Shawn Willden274bb552020-09-30 22:39:22 -0600369 ProtectedData protectedData;
370 auto status = provisionable_->generateCertificateRequest(
David Drysdalec8400772021-03-11 12:35:11 +0000371 false /* testMode */, keysToSign_, eekChain_.chain, challenge_, &deviceInfo,
372 &protectedData, &keysToSignMac);
Shawn Willden274bb552020-09-30 22:39:22 -0600373 ASSERT_FALSE(status.isOk());
374 ASSERT_EQ(status.getServiceSpecificError(),
375 BnRemotelyProvisionedComponent::STATUS_TEST_KEY_IN_PRODUCTION_REQUEST);
376}
377
378INSTANTIATE_REM_PROV_AIDL_TEST(CertificateRequestTest);
379
380} // namespace aidl::android::hardware::security::keymint::test