blob: 9b797de802c4f14b127ae1c02a48aa479b4cacbd [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;
Max Biresfdbb9042021-03-23 12:43:38 -0700230 DeviceInfo deviceInfo;
Shawn Willden274bb552020-09-30 22:39:22 -0600231 ProtectedData protectedData;
232 auto challenge = randomBytes(32);
Max Biresfdbb9042021-03-23 12:43:38 -0700233 auto status = provisionable_->generateCertificateRequest(
234 testMode, {} /* keysToSign */, eekChain_.chain, challenge, &deviceInfo, &protectedData,
235 &keysToSignMac);
Shawn Willden274bb552020-09-30 22:39:22 -0600236 ASSERT_TRUE(status.isOk()) << status.getMessage();
237
238 auto [parsedProtectedData, _, protDataErrMsg] = cppbor::parse(protectedData.protectedData);
239 ASSERT_TRUE(parsedProtectedData) << protDataErrMsg;
240 ASSERT_TRUE(parsedProtectedData->asArray());
241 ASSERT_EQ(parsedProtectedData->asArray()->size(), kCoseEncryptEntryCount);
242
243 auto senderPubkey = getSenderPubKeyFromCoseEncrypt(parsedProtectedData);
244 ASSERT_TRUE(senderPubkey) << senderPubkey.message();
245 EXPECT_EQ(senderPubkey->second, eekId_);
246
247 auto sessionKey = x25519_HKDF_DeriveKey(eekChain_.last_pubkey, eekChain_.last_privkey,
248 senderPubkey->first, false /* senderIsA */);
249 ASSERT_TRUE(sessionKey) << sessionKey.message();
250
251 auto protectedDataPayload =
252 decryptCoseEncrypt(*sessionKey, parsedProtectedData.get(), bytevec{} /* aad */);
253 ASSERT_TRUE(protectedDataPayload) << protectedDataPayload.message();
254
255 auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(*protectedDataPayload);
256 ASSERT_TRUE(parsedPayload) << "Failed to parse payload: " << payloadErrMsg;
257 ASSERT_TRUE(parsedPayload->asArray());
258 EXPECT_EQ(parsedPayload->asArray()->size(), 2U);
259
260 auto& signedMac = parsedPayload->asArray()->get(0);
261 auto& bcc = parsedPayload->asArray()->get(1);
262 ASSERT_TRUE(signedMac && signedMac->asArray());
263 ASSERT_TRUE(bcc && bcc->asArray());
264
265 // BCC is [ pubkey, + BccEntry]
266 auto bccContents = validateBcc(bcc->asArray());
267 ASSERT_TRUE(bccContents) << "\n" << bccContents.message() << "\n" << prettyPrint(bcc.get());
268 ASSERT_GT(bccContents->size(), 0U);
269
270 auto& signingKey = bccContents->back().pubKey;
271 auto macKey = verifyAndParseCoseSign1(testMode, signedMac->asArray(), signingKey,
272 cppbor::Array() // DeviceInfo
273 .add(challenge) //
274 .add(cppbor::Map())
275 .encode());
276 ASSERT_TRUE(macKey) << macKey.message();
277
278 auto coseMac0 = cppbor::Array()
279 .add(cppbor::Map() // protected
280 .add(ALGORITHM, HMAC_256)
281 .canonicalize()
282 .encode())
David Drysdale31a2b562021-03-15 14:36:57 +0000283 .add(cppbor::Map()) // unprotected
Shawn Willden274bb552020-09-30 22:39:22 -0600284 .add(cppbor::Array().encode()) // payload (keysToSign)
285 .add(std::move(keysToSignMac)); // tag
286
287 auto macPayload = verifyAndParseCoseMac0(&coseMac0, *macKey);
288 ASSERT_TRUE(macPayload) << macPayload.message();
289}
290
291/**
292 * Generate an empty certificate request in prod mode. Generation will fail because we don't have a
293 * valid GEEK.
294 *
295 * TODO(swillden): Get a valid GEEK and use it so the generation can succeed, though we won't be
296 * able to decrypt.
297 */
Max Bires126869a2021-02-21 18:32:59 -0800298TEST_P(CertificateRequestTest, EmptyRequest_prodMode) {
Shawn Willden274bb552020-09-30 22:39:22 -0600299 bool testMode = false;
300 bytevec keysToSignMac;
Max Biresfdbb9042021-03-23 12:43:38 -0700301 DeviceInfo deviceInfo;
Shawn Willden274bb552020-09-30 22:39:22 -0600302 ProtectedData protectedData;
303 auto challenge = randomBytes(32);
Max Biresfdbb9042021-03-23 12:43:38 -0700304 auto status = provisionable_->generateCertificateRequest(
305 testMode, {} /* keysToSign */, eekChain_.chain, challenge, &deviceInfo, &protectedData,
306 &keysToSignMac);
Shawn Willden274bb552020-09-30 22:39:22 -0600307 ASSERT_FALSE(status.isOk());
308 ASSERT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
309}
310
311/**
312 * Generate a non-empty certificate request in test mode. Decrypt, parse and validate the contents.
313 */
Max Bires126869a2021-02-21 18:32:59 -0800314TEST_P(CertificateRequestTest, NonEmptyRequest_testMode) {
Shawn Willden274bb552020-09-30 22:39:22 -0600315 bool testMode = true;
316 generateKeys(testMode, 4 /* numKeys */);
317
318 bytevec keysToSignMac;
Max Biresfdbb9042021-03-23 12:43:38 -0700319 DeviceInfo deviceInfo;
Shawn Willden274bb552020-09-30 22:39:22 -0600320 ProtectedData protectedData;
321 auto challenge = randomBytes(32);
Max Biresfdbb9042021-03-23 12:43:38 -0700322 auto status = provisionable_->generateCertificateRequest(testMode, keysToSign_, eekChain_.chain,
323 challenge, &deviceInfo, &protectedData,
324 &keysToSignMac);
Shawn Willden274bb552020-09-30 22:39:22 -0600325 ASSERT_TRUE(status.isOk()) << status.getMessage();
326
327 auto [parsedProtectedData, _, protDataErrMsg] = cppbor::parse(protectedData.protectedData);
328 ASSERT_TRUE(parsedProtectedData) << protDataErrMsg;
329 ASSERT_TRUE(parsedProtectedData->asArray());
330 ASSERT_EQ(parsedProtectedData->asArray()->size(), kCoseEncryptEntryCount);
331
332 auto senderPubkey = getSenderPubKeyFromCoseEncrypt(parsedProtectedData);
333 ASSERT_TRUE(senderPubkey) << senderPubkey.message();
334 EXPECT_EQ(senderPubkey->second, eekId_);
335
336 auto sessionKey = x25519_HKDF_DeriveKey(eekChain_.last_pubkey, eekChain_.last_privkey,
337 senderPubkey->first, false /* senderIsA */);
338 ASSERT_TRUE(sessionKey) << sessionKey.message();
339
340 auto protectedDataPayload =
341 decryptCoseEncrypt(*sessionKey, parsedProtectedData.get(), bytevec{} /* aad */);
342 ASSERT_TRUE(protectedDataPayload) << protectedDataPayload.message();
343
344 auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(*protectedDataPayload);
345 ASSERT_TRUE(parsedPayload) << "Failed to parse payload: " << payloadErrMsg;
346 ASSERT_TRUE(parsedPayload->asArray());
347 EXPECT_EQ(parsedPayload->asArray()->size(), 2U);
348
349 auto& signedMac = parsedPayload->asArray()->get(0);
350 auto& bcc = parsedPayload->asArray()->get(1);
351 ASSERT_TRUE(signedMac && signedMac->asArray());
352 ASSERT_TRUE(bcc);
353
354 auto bccContents = validateBcc(bcc->asArray());
355 ASSERT_TRUE(bccContents) << "\n" << prettyPrint(bcc.get());
356 ASSERT_GT(bccContents->size(), 0U);
357
358 auto& signingKey = bccContents->back().pubKey;
359 auto macKey = verifyAndParseCoseSign1(testMode, signedMac->asArray(), signingKey,
360 cppbor::Array() // DeviceInfo
361 .add(challenge) //
362 .add(cppbor::Array())
363 .encode());
364 ASSERT_TRUE(macKey) << macKey.message();
365
366 auto coseMac0 = cppbor::Array()
367 .add(cppbor::Map() // protected
368 .add(ALGORITHM, HMAC_256)
369 .canonicalize()
370 .encode())
David Drysdale31a2b562021-03-15 14:36:57 +0000371 .add(cppbor::Map()) // unprotected
Shawn Willden274bb552020-09-30 22:39:22 -0600372 .add(cborKeysToSign_.encode()) // payload
373 .add(std::move(keysToSignMac)); // tag
374
375 auto macPayload = verifyAndParseCoseMac0(&coseMac0, *macKey);
376 ASSERT_TRUE(macPayload) << macPayload.message();
377}
378
379/**
380 * Generate a non-empty certificate request in prod mode. Must fail because we don't have a valid
381 * GEEK.
382 *
383 * TODO(swillden): Get a valid GEEK and use it so the generation can succeed, though we won't be
384 * able to decrypt.
385 */
Max Bires126869a2021-02-21 18:32:59 -0800386TEST_P(CertificateRequestTest, NonEmptyRequest_prodMode) {
Shawn Willden274bb552020-09-30 22:39:22 -0600387 bool testMode = false;
388 generateKeys(testMode, 4 /* numKeys */);
389
390 bytevec keysToSignMac;
Max Biresfdbb9042021-03-23 12:43:38 -0700391 DeviceInfo deviceInfo;
Shawn Willden274bb552020-09-30 22:39:22 -0600392 ProtectedData protectedData;
393 auto challenge = randomBytes(32);
Max Biresfdbb9042021-03-23 12:43:38 -0700394 auto status = provisionable_->generateCertificateRequest(testMode, keysToSign_, eekChain_.chain,
395 challenge, &deviceInfo, &protectedData,
396 &keysToSignMac);
Shawn Willden274bb552020-09-30 22:39:22 -0600397 ASSERT_FALSE(status.isOk());
398 ASSERT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
399}
400
401/**
402 * Generate a non-empty certificate request in test mode, with prod keys. Must fail with
403 * STATUS_PRODUCTION_KEY_IN_TEST_REQUEST.
404 */
Max Bires126869a2021-02-21 18:32:59 -0800405TEST_P(CertificateRequestTest, NonEmptyRequest_prodKeyInTestCert) {
Shawn Willden274bb552020-09-30 22:39:22 -0600406 generateKeys(false /* testMode */, 2 /* numKeys */);
407
408 bytevec keysToSignMac;
Max Biresfdbb9042021-03-23 12:43:38 -0700409 DeviceInfo deviceInfo;
Shawn Willden274bb552020-09-30 22:39:22 -0600410 ProtectedData protectedData;
411 auto challenge = randomBytes(32);
Max Biresfdbb9042021-03-23 12:43:38 -0700412 auto status = provisionable_->generateCertificateRequest(
413 true /* testMode */, keysToSign_, eekChain_.chain, challenge, &deviceInfo,
414 &protectedData, &keysToSignMac);
Shawn Willden274bb552020-09-30 22:39:22 -0600415 ASSERT_FALSE(status.isOk());
416 ASSERT_EQ(status.getServiceSpecificError(),
417 BnRemotelyProvisionedComponent::STATUS_PRODUCTION_KEY_IN_TEST_REQUEST);
418}
419
420/**
421 * Generate a non-empty certificate request in prod mode, with test keys. Must fail with
422 * STATUS_TEST_KEY_IN_PRODUCTION_REQUEST.
423 */
Max Bires126869a2021-02-21 18:32:59 -0800424TEST_P(CertificateRequestTest, NonEmptyRequest_testKeyInProdCert) {
Shawn Willden274bb552020-09-30 22:39:22 -0600425 generateKeys(true /* testMode */, 2 /* numKeys */);
426
427 bytevec keysToSignMac;
Max Biresfdbb9042021-03-23 12:43:38 -0700428 DeviceInfo deviceInfo;
Shawn Willden274bb552020-09-30 22:39:22 -0600429 ProtectedData protectedData;
430 auto status = provisionable_->generateCertificateRequest(
431 false /* testMode */, keysToSign_, eekChain_.chain, randomBytes(32) /* challenge */,
Max Biresfdbb9042021-03-23 12:43:38 -0700432 &deviceInfo, &protectedData, &keysToSignMac);
Shawn Willden274bb552020-09-30 22:39:22 -0600433 ASSERT_FALSE(status.isOk());
434 ASSERT_EQ(status.getServiceSpecificError(),
435 BnRemotelyProvisionedComponent::STATUS_TEST_KEY_IN_PRODUCTION_REQUEST);
436}
437
438INSTANTIATE_REM_PROV_AIDL_TEST(CertificateRequestTest);
439
440} // namespace aidl::android::hardware::security::keymint::test