Support for P256 curve in RKP for Strongbox
Test: Run Rkp Vts tests. Change-Id: Ic38fd2318dd8749ae125f1e78d25f2722bd367e5
This commit is contained in:
parent
1048b42ea6
commit
fb213d6031
5 changed files with 472 additions and 70 deletions
|
@ -169,6 +169,7 @@ class VtsRemotelyProvisionedComponentTests : public testing::TestWithParam<std::
|
|||
provisionable_ = IRemotelyProvisionedComponent::fromBinder(binder);
|
||||
}
|
||||
ASSERT_NE(provisionable_, nullptr);
|
||||
ASSERT_TRUE(provisionable_->getHardwareInfo(&rpcHardwareInfo).isOk());
|
||||
}
|
||||
|
||||
static vector<string> build_params() {
|
||||
|
@ -178,6 +179,7 @@ class VtsRemotelyProvisionedComponentTests : public testing::TestWithParam<std::
|
|||
|
||||
protected:
|
||||
std::shared_ptr<IRemotelyProvisionedComponent> provisionable_;
|
||||
RpcHardwareInfo rpcHardwareInfo;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -341,11 +343,10 @@ TEST_P(GenerateKeyTests, generateEcdsaP256Key_testMode) {
|
|||
class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests {
|
||||
protected:
|
||||
CertificateRequestTest() : eekId_(string_to_bytevec("eekid")), challenge_(randomBytes(32)) {
|
||||
generateTestEekChain(3);
|
||||
}
|
||||
|
||||
void generateTestEekChain(size_t eekLength) {
|
||||
auto chain = generateEekChain(eekLength, eekId_);
|
||||
auto chain = generateEekChain(rpcHardwareInfo.supportedEekCurve, eekLength, eekId_);
|
||||
EXPECT_TRUE(chain) << chain.message();
|
||||
if (chain) testEekChain_ = chain.moveValue();
|
||||
testEekLength_ = eekLength;
|
||||
|
@ -366,6 +367,17 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests {
|
|||
}
|
||||
}
|
||||
|
||||
ErrMsgOr<bytevec> getSessionKey(ErrMsgOr<std::pair<bytevec, bytevec>>& senderPubkey) {
|
||||
if (rpcHardwareInfo.supportedEekCurve == RpcHardwareInfo::CURVE_25519 ||
|
||||
rpcHardwareInfo.supportedEekCurve == RpcHardwareInfo::CURVE_NONE) {
|
||||
return x25519_HKDF_DeriveKey(testEekChain_.last_pubkey, testEekChain_.last_privkey,
|
||||
senderPubkey->first, false /* senderIsA */);
|
||||
} else {
|
||||
return ECDH_HKDF_DeriveKey(testEekChain_.last_pubkey, testEekChain_.last_privkey,
|
||||
senderPubkey->first, false /* senderIsA */);
|
||||
}
|
||||
}
|
||||
|
||||
void checkProtectedData(const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign,
|
||||
const bytevec& keysToSignMac, const ProtectedData& protectedData,
|
||||
std::vector<BccEntryData>* bccOutput = nullptr) {
|
||||
|
@ -378,9 +390,7 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests {
|
|||
ASSERT_TRUE(senderPubkey) << senderPubkey.message();
|
||||
EXPECT_EQ(senderPubkey->second, eekId_);
|
||||
|
||||
auto sessionKey =
|
||||
x25519_HKDF_DeriveKey(testEekChain_.last_pubkey, testEekChain_.last_privkey,
|
||||
senderPubkey->first, false /* senderIsA */);
|
||||
auto sessionKey = getSessionKey(senderPubkey);
|
||||
ASSERT_TRUE(sessionKey) << sessionKey.message();
|
||||
|
||||
auto protectedDataPayload =
|
||||
|
@ -390,7 +400,8 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests {
|
|||
auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(*protectedDataPayload);
|
||||
ASSERT_TRUE(parsedPayload) << "Failed to parse payload: " << payloadErrMsg;
|
||||
ASSERT_TRUE(parsedPayload->asArray());
|
||||
EXPECT_EQ(parsedPayload->asArray()->size(), 2U);
|
||||
// Strongbox may contain additional certificate chain.
|
||||
EXPECT_LE(parsedPayload->asArray()->size(), 3U);
|
||||
|
||||
auto& signedMac = parsedPayload->asArray()->get(0);
|
||||
auto& bcc = parsedPayload->asArray()->get(1);
|
||||
|
@ -474,6 +485,7 @@ TEST_P(CertificateRequestTest, NewKeyPerCallInTestMode) {
|
|||
bytevec keysToSignMac;
|
||||
DeviceInfo deviceInfo;
|
||||
ProtectedData protectedData;
|
||||
generateTestEekChain(3);
|
||||
auto status = provisionable_->generateCertificateRequest(
|
||||
testMode, {} /* keysToSign */, testEekChain_.chain, challenge_, &deviceInfo,
|
||||
&protectedData, &keysToSignMac);
|
||||
|
@ -513,8 +525,8 @@ TEST_P(CertificateRequestTest, DISABLED_EmptyRequest_prodMode) {
|
|||
DeviceInfo deviceInfo;
|
||||
ProtectedData protectedData;
|
||||
auto status = provisionable_->generateCertificateRequest(
|
||||
testMode, {} /* keysToSign */, getProdEekChain(), challenge_, &deviceInfo,
|
||||
&protectedData, &keysToSignMac);
|
||||
testMode, {} /* keysToSign */, getProdEekChain(rpcHardwareInfo.supportedEekCurve),
|
||||
challenge_, &deviceInfo, &protectedData, &keysToSignMac);
|
||||
EXPECT_TRUE(status.isOk());
|
||||
}
|
||||
|
||||
|
@ -554,8 +566,8 @@ TEST_P(CertificateRequestTest, DISABLED_NonEmptyRequest_prodMode) {
|
|||
DeviceInfo deviceInfo;
|
||||
ProtectedData protectedData;
|
||||
auto status = provisionable_->generateCertificateRequest(
|
||||
testMode, keysToSign_, getProdEekChain(), challenge_, &deviceInfo, &protectedData,
|
||||
&keysToSignMac);
|
||||
testMode, keysToSign_, getProdEekChain(rpcHardwareInfo.supportedEekCurve), challenge_,
|
||||
&deviceInfo, &protectedData, &keysToSignMac);
|
||||
EXPECT_TRUE(status.isOk());
|
||||
}
|
||||
|
||||
|
@ -570,6 +582,7 @@ TEST_P(CertificateRequestTest, NonEmptyRequestCorruptMac_testMode) {
|
|||
bytevec keysToSignMac;
|
||||
DeviceInfo deviceInfo;
|
||||
ProtectedData protectedData;
|
||||
generateTestEekChain(3);
|
||||
auto status = provisionable_->generateCertificateRequest(
|
||||
testMode, {keyWithCorruptMac}, testEekChain_.chain, challenge_, &deviceInfo,
|
||||
&protectedData, &keysToSignMac);
|
||||
|
@ -589,8 +602,8 @@ TEST_P(CertificateRequestTest, NonEmptyRequestCorruptMac_prodMode) {
|
|||
DeviceInfo deviceInfo;
|
||||
ProtectedData protectedData;
|
||||
auto status = provisionable_->generateCertificateRequest(
|
||||
testMode, {keyWithCorruptMac}, getProdEekChain(), challenge_, &deviceInfo,
|
||||
&protectedData, &keysToSignMac);
|
||||
testMode, {keyWithCorruptMac}, getProdEekChain(rpcHardwareInfo.supportedEekCurve),
|
||||
challenge_, &deviceInfo, &protectedData, &keysToSignMac);
|
||||
ASSERT_FALSE(status.isOk()) << status.getMessage();
|
||||
EXPECT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_MAC);
|
||||
}
|
||||
|
@ -603,7 +616,7 @@ TEST_P(CertificateRequestTest, NonEmptyCorruptEekRequest_prodMode) {
|
|||
bool testMode = false;
|
||||
generateKeys(testMode, 4 /* numKeys */);
|
||||
|
||||
auto prodEekChain = getProdEekChain();
|
||||
auto prodEekChain = getProdEekChain(rpcHardwareInfo.supportedEekCurve);
|
||||
auto [parsedChain, _, parseErr] = cppbor::parse(prodEekChain);
|
||||
ASSERT_NE(parsedChain, nullptr) << parseErr;
|
||||
ASSERT_NE(parsedChain->asArray(), nullptr);
|
||||
|
@ -634,7 +647,7 @@ TEST_P(CertificateRequestTest, NonEmptyIncompleteEekRequest_prodMode) {
|
|||
|
||||
// Build an EEK chain that omits the first self-signed cert.
|
||||
auto truncatedChain = cppbor::Array();
|
||||
auto [chain, _, parseErr] = cppbor::parse(getProdEekChain());
|
||||
auto [chain, _, parseErr] = cppbor::parse(getProdEekChain(rpcHardwareInfo.supportedEekCurve));
|
||||
ASSERT_TRUE(chain);
|
||||
auto eekChain = chain->asArray();
|
||||
ASSERT_NE(eekChain, nullptr);
|
||||
|
@ -662,6 +675,7 @@ TEST_P(CertificateRequestTest, NonEmptyRequest_prodKeyInTestCert) {
|
|||
bytevec keysToSignMac;
|
||||
DeviceInfo deviceInfo;
|
||||
ProtectedData protectedData;
|
||||
generateTestEekChain(3);
|
||||
auto status = provisionable_->generateCertificateRequest(
|
||||
true /* testMode */, keysToSign_, testEekChain_.chain, challenge_, &deviceInfo,
|
||||
&protectedData, &keysToSignMac);
|
||||
|
@ -680,6 +694,7 @@ TEST_P(CertificateRequestTest, NonEmptyRequest_testKeyInProdCert) {
|
|||
bytevec keysToSignMac;
|
||||
DeviceInfo deviceInfo;
|
||||
ProtectedData protectedData;
|
||||
generateTestEekChain(3);
|
||||
auto status = provisionable_->generateCertificateRequest(
|
||||
false /* testMode */, keysToSign_, testEekChain_.chain, challenge_, &deviceInfo,
|
||||
&protectedData, &keysToSignMac);
|
||||
|
|
|
@ -60,11 +60,15 @@ cc_library {
|
|||
export_include_dirs: [
|
||||
"include",
|
||||
],
|
||||
defaults: [
|
||||
"keymint_use_latest_hal_aidl_ndk_shared",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libcppbor_external",
|
||||
"libcppcose_rkp",
|
||||
"libcrypto",
|
||||
"libkeymaster_portable",
|
||||
"libjsoncpp",
|
||||
],
|
||||
}
|
||||
|
@ -76,6 +80,9 @@ cc_test {
|
|||
"libgmock",
|
||||
"libgtest_main",
|
||||
],
|
||||
defaults: [
|
||||
"keymint_use_latest_hal_aidl_ndk_shared",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libcppbor_external",
|
||||
|
|
|
@ -52,6 +52,34 @@ inline constexpr uint8_t kCoseEncodedGeekCert[] = {
|
|||
0x31, 0xbf, 0x6b, 0xe8, 0x1e, 0x35, 0xe2, 0xf0, 0x2d, 0xce, 0x6c, 0x2f, 0x4f, 0xf2,
|
||||
0xf5, 0x4f, 0xa5, 0xd4, 0x83, 0xad, 0x96, 0xa2, 0xf1, 0x87, 0x58, 0x04};
|
||||
|
||||
// The Google ECDSA P256 root key for the Endpoint Encryption Key chain, encoded as COSE_Sign1
|
||||
inline constexpr uint8_t kCoseEncodedEcdsa256RootCert[] = {
|
||||
0x84, 0x43, 0xa1, 0x01, 0x26, 0xa0, 0x58, 0x4d, 0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21,
|
||||
0x58, 0x20, 0xf7, 0x14, 0x8a, 0xdb, 0x97, 0xf4, 0xcc, 0x53, 0xef, 0xd2, 0x64, 0x11, 0xc4, 0xe3,
|
||||
0x75, 0x1f, 0x66, 0x1f, 0xa4, 0x71, 0x0c, 0x6c, 0xcf, 0xfa, 0x09, 0x46, 0x80, 0x74, 0x87, 0x54,
|
||||
0xf2, 0xad, 0x22, 0x58, 0x20, 0x5e, 0x7f, 0x5b, 0xf6, 0xec, 0xe4, 0xf6, 0x19, 0xcc, 0xff, 0x13,
|
||||
0x37, 0xfd, 0x0f, 0xa1, 0xc8, 0x93, 0xdb, 0x18, 0x06, 0x76, 0xc4, 0x5d, 0xe6, 0xd7, 0x6a, 0x77,
|
||||
0x86, 0xc3, 0x2d, 0xaf, 0x8f, 0x58, 0x40, 0x2f, 0x97, 0x8e, 0x42, 0xfb, 0xbe, 0x07, 0x2d, 0x95,
|
||||
0x47, 0x85, 0x47, 0x93, 0x40, 0xb0, 0x1f, 0xd4, 0x9b, 0x47, 0xa4, 0xc4, 0x44, 0xa9, 0xf2, 0xa1,
|
||||
0x07, 0x87, 0x10, 0xc7, 0x9f, 0xcb, 0x11, 0xf4, 0xbf, 0x9f, 0xe8, 0x3b, 0xe0, 0xe7, 0x34, 0x4c,
|
||||
0x15, 0xfc, 0x7b, 0xc3, 0x7e, 0x33, 0x05, 0xf4, 0xd1, 0x34, 0x3c, 0xed, 0x02, 0x04, 0x60, 0x7a,
|
||||
0x15, 0xe0, 0x79, 0xd3, 0x8a, 0xff, 0x24};
|
||||
|
||||
// The Google ECDSA P256 Endpoint Encryption Key certificate, encoded as COSE_Sign1
|
||||
inline constexpr uint8_t kCoseEncodedEcdsa256GeekCert[] = {
|
||||
0x84, 0x43, 0xa1, 0x01, 0x26, 0xa0, 0x58, 0x71, 0xa6, 0x01, 0x02, 0x02, 0x58, 0x20, 0x35, 0x73,
|
||||
0xb7, 0x3f, 0xa0, 0x8a, 0x80, 0x89, 0xb1, 0x26, 0x67, 0xe9, 0xcb, 0x7c, 0x75, 0xa1, 0xaf, 0x02,
|
||||
0x61, 0xfc, 0x6e, 0x65, 0x03, 0x91, 0x3b, 0xd3, 0x4b, 0x7d, 0x14, 0x94, 0x3e, 0x46, 0x03, 0x38,
|
||||
0x18, 0x20, 0x01, 0x21, 0x58, 0x20, 0xe0, 0x41, 0xcf, 0x2f, 0x0f, 0x34, 0x0f, 0x1c, 0x33, 0x2c,
|
||||
0x41, 0xb0, 0xcf, 0xd7, 0x0c, 0x30, 0x55, 0x35, 0xd2, 0x1e, 0x6a, 0x47, 0x13, 0x4b, 0x2e, 0xd1,
|
||||
0x48, 0x96, 0x7e, 0x24, 0x9c, 0x68, 0x22, 0x58, 0x20, 0x1f, 0xce, 0x45, 0xc5, 0xfb, 0x61, 0xba,
|
||||
0x81, 0x21, 0xf9, 0xe5, 0x05, 0x9b, 0x9b, 0x39, 0x0e, 0x76, 0x86, 0x86, 0x47, 0xb8, 0x1e, 0x2f,
|
||||
0x45, 0xf1, 0xce, 0xaf, 0xda, 0x3f, 0x80, 0x68, 0xdb, 0x58, 0x40, 0x8c, 0xb3, 0xba, 0x7e, 0x20,
|
||||
0x3e, 0x32, 0xb0, 0x68, 0xdf, 0x60, 0xd1, 0x1d, 0x7d, 0xf0, 0xac, 0x38, 0x8e, 0x51, 0xbc, 0xff,
|
||||
0x6c, 0xe1, 0x67, 0x3b, 0x4a, 0x79, 0xbc, 0x56, 0x78, 0xb3, 0x99, 0xd8, 0x7c, 0x8a, 0x07, 0xd8,
|
||||
0xda, 0xb5, 0xb5, 0x7f, 0x71, 0xf4, 0xd8, 0x6b, 0xdf, 0x33, 0x27, 0x34, 0x7b, 0x65, 0xd1, 0x2a,
|
||||
0xeb, 0x86, 0x99, 0x98, 0xab, 0x3a, 0xb4, 0x80, 0xaa, 0xbd, 0x50};
|
||||
|
||||
/**
|
||||
* Generates random bytes.
|
||||
*/
|
||||
|
@ -64,15 +92,15 @@ struct EekChain {
|
|||
};
|
||||
|
||||
/**
|
||||
* Generates an X25518 EEK with the specified eekId and an Ed25519 chain of the
|
||||
* specified length. All keys are generated randomly.
|
||||
* Based on the supportedEekCurve, Generates an X25519/ECDH with the specified eekId
|
||||
* and an Ed25519/ECDSA chain of the specified length. All keys are generated randomly.
|
||||
*/
|
||||
ErrMsgOr<EekChain> generateEekChain(size_t length, const bytevec& eekId);
|
||||
ErrMsgOr<EekChain> generateEekChain(int32_t supportedEekCurve, size_t length, const bytevec& eekId);
|
||||
|
||||
/**
|
||||
* Returns the CBOR-encoded, production Google Endpoint Encryption Key chain.
|
||||
*/
|
||||
bytevec getProdEekChain();
|
||||
bytevec getProdEekChain(int32_t supportedEekCurve);
|
||||
|
||||
struct BccEntryData {
|
||||
bytevec pubKey;
|
||||
|
|
|
@ -17,10 +17,16 @@
|
|||
#include <iterator>
|
||||
#include <tuple>
|
||||
|
||||
#include <aidl/android/hardware/security/keymint/RpcHardwareInfo.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <cppbor.h>
|
||||
#include <json/json.h>
|
||||
#include <keymaster/km_openssl/ec_key.h>
|
||||
#include <keymaster/km_openssl/ecdsa_operation.h>
|
||||
#include <keymaster/km_openssl/openssl_err.h>
|
||||
#include <keymaster/km_openssl/openssl_utils.h>
|
||||
#include <openssl/base64.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <remote_prov/remote_prov_utils.h>
|
||||
|
||||
|
@ -30,6 +36,166 @@ constexpr uint32_t kBccPayloadIssuer = 1;
|
|||
constexpr uint32_t kBccPayloadSubject = 2;
|
||||
constexpr int32_t kBccPayloadSubjPubKey = -4670552;
|
||||
constexpr int32_t kBccPayloadKeyUsage = -4670553;
|
||||
constexpr int kP256AffinePointSize = 32;
|
||||
|
||||
using EC_KEY_Ptr = bssl::UniquePtr<EC_KEY>;
|
||||
using EVP_PKEY_Ptr = bssl::UniquePtr<EVP_PKEY>;
|
||||
using EVP_PKEY_CTX_Ptr = bssl::UniquePtr<EVP_PKEY_CTX>;
|
||||
|
||||
ErrMsgOr<bytevec> ecKeyGetPrivateKey(const EC_KEY* ecKey) {
|
||||
// Extract private key.
|
||||
const BIGNUM* bignum = EC_KEY_get0_private_key(ecKey);
|
||||
if (bignum == nullptr) {
|
||||
return "Error getting bignum from private key";
|
||||
}
|
||||
// Pad with zeros in case the length is lesser than 32.
|
||||
bytevec privKey(32, 0);
|
||||
BN_bn2binpad(bignum, privKey.data(), privKey.size());
|
||||
return privKey;
|
||||
}
|
||||
|
||||
ErrMsgOr<bytevec> ecKeyGetPublicKey(const EC_KEY* ecKey) {
|
||||
// Extract public key.
|
||||
auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
|
||||
if (group.get() == nullptr) {
|
||||
return "Error creating EC group by curve name";
|
||||
}
|
||||
const EC_POINT* point = EC_KEY_get0_public_key(ecKey);
|
||||
if (point == nullptr) return "Error getting ecpoint from public key";
|
||||
|
||||
int size =
|
||||
EC_POINT_point2oct(group.get(), point, POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr);
|
||||
if (size == 0) {
|
||||
return "Error generating public key encoding";
|
||||
}
|
||||
|
||||
bytevec publicKey;
|
||||
publicKey.resize(size);
|
||||
EC_POINT_point2oct(group.get(), point, POINT_CONVERSION_UNCOMPRESSED, publicKey.data(),
|
||||
publicKey.size(), nullptr);
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
ErrMsgOr<std::tuple<bytevec, bytevec>> getAffineCoordinates(const bytevec& pubKey) {
|
||||
auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
|
||||
if (group.get() == nullptr) {
|
||||
return "Error creating EC group by curve name";
|
||||
}
|
||||
auto point = EC_POINT_Ptr(EC_POINT_new(group.get()));
|
||||
if (EC_POINT_oct2point(group.get(), point.get(), pubKey.data(), pubKey.size(), nullptr) != 1) {
|
||||
return "Error decoding publicKey";
|
||||
}
|
||||
BIGNUM_Ptr x(BN_new());
|
||||
BIGNUM_Ptr y(BN_new());
|
||||
BN_CTX_Ptr ctx(BN_CTX_new());
|
||||
if (!ctx.get()) return "Failed to create BN_CTX instance";
|
||||
|
||||
if (!EC_POINT_get_affine_coordinates_GFp(group.get(), point.get(), x.get(), y.get(),
|
||||
ctx.get())) {
|
||||
return "Failed to get affine coordinates from ECPoint";
|
||||
}
|
||||
bytevec pubX(kP256AffinePointSize);
|
||||
bytevec pubY(kP256AffinePointSize);
|
||||
if (BN_bn2binpad(x.get(), pubX.data(), kP256AffinePointSize) != kP256AffinePointSize) {
|
||||
return "Error in converting absolute value of x coordinate to big-endian";
|
||||
}
|
||||
if (BN_bn2binpad(y.get(), pubY.data(), kP256AffinePointSize) != kP256AffinePointSize) {
|
||||
return "Error in converting absolute value of y coordinate to big-endian";
|
||||
}
|
||||
return std::make_tuple(std::move(pubX), std::move(pubY));
|
||||
}
|
||||
|
||||
ErrMsgOr<std::tuple<bytevec, bytevec>> generateEc256KeyPair() {
|
||||
auto ec_key = EC_KEY_Ptr(EC_KEY_new());
|
||||
if (ec_key.get() == nullptr) {
|
||||
return "Failed to allocate ec key";
|
||||
}
|
||||
|
||||
auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
|
||||
if (group.get() == nullptr) {
|
||||
return "Error creating EC group by curve name";
|
||||
}
|
||||
|
||||
if (EC_KEY_set_group(ec_key.get(), group.get()) != 1 ||
|
||||
EC_KEY_generate_key(ec_key.get()) != 1 || EC_KEY_check_key(ec_key.get()) < 0) {
|
||||
return "Error generating key";
|
||||
}
|
||||
|
||||
auto privKey = ecKeyGetPrivateKey(ec_key.get());
|
||||
if (!privKey) return privKey.moveMessage();
|
||||
|
||||
auto pubKey = ecKeyGetPublicKey(ec_key.get());
|
||||
if (!pubKey) return pubKey.moveMessage();
|
||||
|
||||
return std::make_tuple(pubKey.moveValue(), privKey.moveValue());
|
||||
}
|
||||
|
||||
ErrMsgOr<std::tuple<bytevec, bytevec>> generateX25519KeyPair() {
|
||||
/* Generate X25519 key pair */
|
||||
bytevec pubKey(X25519_PUBLIC_VALUE_LEN);
|
||||
bytevec privKey(X25519_PRIVATE_KEY_LEN);
|
||||
X25519_keypair(pubKey.data(), privKey.data());
|
||||
return std::make_tuple(std::move(pubKey), std::move(privKey));
|
||||
}
|
||||
|
||||
ErrMsgOr<std::tuple<bytevec, bytevec>> generateED25519KeyPair() {
|
||||
/* Generate ED25519 key pair */
|
||||
bytevec pubKey(ED25519_PUBLIC_KEY_LEN);
|
||||
bytevec privKey(ED25519_PRIVATE_KEY_LEN);
|
||||
ED25519_keypair(pubKey.data(), privKey.data());
|
||||
return std::make_tuple(std::move(pubKey), std::move(privKey));
|
||||
}
|
||||
|
||||
ErrMsgOr<std::tuple<bytevec, bytevec>> generateKeyPair(int32_t supportedEekCurve, bool isEek) {
|
||||
switch (supportedEekCurve) {
|
||||
case RpcHardwareInfo::CURVE_25519:
|
||||
if (isEek) {
|
||||
return generateX25519KeyPair();
|
||||
}
|
||||
return generateED25519KeyPair();
|
||||
case RpcHardwareInfo::CURVE_P256:
|
||||
return generateEc256KeyPair();
|
||||
default:
|
||||
return "Unknown EEK Curve.";
|
||||
}
|
||||
}
|
||||
|
||||
ErrMsgOr<bytevec> constructCoseKey(int32_t supportedEekCurve, const bytevec& eekId,
|
||||
const bytevec& pubKey) {
|
||||
CoseKeyType keyType;
|
||||
CoseKeyAlgorithm algorithm;
|
||||
CoseKeyCurve curve;
|
||||
bytevec pubX;
|
||||
bytevec pubY;
|
||||
switch (supportedEekCurve) {
|
||||
case RpcHardwareInfo::CURVE_25519:
|
||||
keyType = OCTET_KEY_PAIR;
|
||||
algorithm = (eekId.empty()) ? EDDSA : ECDH_ES_HKDF_256;
|
||||
curve = (eekId.empty()) ? ED25519 : cppcose::X25519;
|
||||
pubX = pubKey;
|
||||
break;
|
||||
case RpcHardwareInfo::CURVE_P256: {
|
||||
keyType = EC2;
|
||||
algorithm = (eekId.empty()) ? ES256 : ECDH_ES_HKDF_256;
|
||||
curve = P256;
|
||||
auto affineCoordinates = getAffineCoordinates(pubKey);
|
||||
if (!affineCoordinates) return affineCoordinates.moveMessage();
|
||||
std::tie(pubX, pubY) = affineCoordinates.moveValue();
|
||||
} break;
|
||||
default:
|
||||
return "Unknown EEK Curve.";
|
||||
}
|
||||
cppbor::Map coseKey = cppbor::Map()
|
||||
.add(CoseKey::KEY_TYPE, keyType)
|
||||
.add(CoseKey::ALGORITHM, algorithm)
|
||||
.add(CoseKey::CURVE, curve)
|
||||
.add(CoseKey::PUBKEY_X, pubX);
|
||||
|
||||
if (!pubY.empty()) coseKey.add(CoseKey::PUBKEY_Y, pubY);
|
||||
if (!eekId.empty()) coseKey.add(CoseKey::KEY_ID, eekId);
|
||||
|
||||
return coseKey.canonicalize().encode();
|
||||
}
|
||||
|
||||
bytevec kTestMacKey(32 /* count */, 0 /* byte value */);
|
||||
|
||||
|
@ -39,7 +205,17 @@ bytevec randomBytes(size_t numBytes) {
|
|||
return retval;
|
||||
}
|
||||
|
||||
ErrMsgOr<EekChain> generateEekChain(size_t length, const bytevec& eekId) {
|
||||
ErrMsgOr<cppbor::Array> constructCoseSign1(int32_t supportedEekCurve, const bytevec& key,
|
||||
const bytevec& payload, const bytevec& aad) {
|
||||
if (supportedEekCurve == RpcHardwareInfo::CURVE_P256) {
|
||||
return constructECDSACoseSign1(key, {} /* protectedParams */, payload, aad);
|
||||
} else {
|
||||
return cppcose::constructCoseSign1(key, payload, aad);
|
||||
}
|
||||
}
|
||||
|
||||
ErrMsgOr<EekChain> generateEekChain(int32_t supportedEekCurve, size_t length,
|
||||
const bytevec& eekId) {
|
||||
if (length < 2) {
|
||||
return "EEK chain must contain at least 2 certs.";
|
||||
}
|
||||
|
@ -48,59 +224,62 @@ ErrMsgOr<EekChain> generateEekChain(size_t length, const bytevec& eekId) {
|
|||
|
||||
bytevec prev_priv_key;
|
||||
for (size_t i = 0; i < length - 1; ++i) {
|
||||
bytevec pub_key(ED25519_PUBLIC_KEY_LEN);
|
||||
bytevec priv_key(ED25519_PRIVATE_KEY_LEN);
|
||||
|
||||
ED25519_keypair(pub_key.data(), priv_key.data());
|
||||
auto keyPair = generateKeyPair(supportedEekCurve, false);
|
||||
if (!keyPair) keyPair.moveMessage();
|
||||
auto [pub_key, priv_key] = keyPair.moveValue();
|
||||
|
||||
// The first signing key is self-signed.
|
||||
if (prev_priv_key.empty()) prev_priv_key = priv_key;
|
||||
|
||||
auto coseSign1 = constructCoseSign1(prev_priv_key,
|
||||
cppbor::Map() /* payload CoseKey */
|
||||
.add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR)
|
||||
.add(CoseKey::ALGORITHM, EDDSA)
|
||||
.add(CoseKey::CURVE, ED25519)
|
||||
.add(CoseKey::PUBKEY_X, pub_key)
|
||||
.canonicalize()
|
||||
.encode(),
|
||||
{} /* AAD */);
|
||||
auto coseKey = constructCoseKey(supportedEekCurve, {}, pub_key);
|
||||
if (!coseKey) return coseKey.moveMessage();
|
||||
|
||||
auto coseSign1 =
|
||||
constructCoseSign1(supportedEekCurve, prev_priv_key, coseKey.moveValue(), {} /* AAD */);
|
||||
if (!coseSign1) return coseSign1.moveMessage();
|
||||
eekChain.add(coseSign1.moveValue());
|
||||
|
||||
prev_priv_key = priv_key;
|
||||
}
|
||||
auto keyPair = generateKeyPair(supportedEekCurve, true);
|
||||
if (!keyPair) keyPair.moveMessage();
|
||||
auto [pub_key, priv_key] = keyPair.moveValue();
|
||||
|
||||
bytevec pub_key(X25519_PUBLIC_VALUE_LEN);
|
||||
bytevec priv_key(X25519_PRIVATE_KEY_LEN);
|
||||
X25519_keypair(pub_key.data(), priv_key.data());
|
||||
auto coseKey = constructCoseKey(supportedEekCurve, eekId, pub_key);
|
||||
if (!coseKey) return coseKey.moveMessage();
|
||||
|
||||
auto coseSign1 = constructCoseSign1(prev_priv_key,
|
||||
cppbor::Map() /* payload CoseKey */
|
||||
.add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR)
|
||||
.add(CoseKey::KEY_ID, eekId)
|
||||
.add(CoseKey::ALGORITHM, ECDH_ES_HKDF_256)
|
||||
.add(CoseKey::CURVE, cppcose::X25519)
|
||||
.add(CoseKey::PUBKEY_X, pub_key)
|
||||
.canonicalize()
|
||||
.encode(),
|
||||
{} /* AAD */);
|
||||
auto coseSign1 =
|
||||
constructCoseSign1(supportedEekCurve, prev_priv_key, coseKey.moveValue(), {} /* AAD */);
|
||||
if (!coseSign1) return coseSign1.moveMessage();
|
||||
eekChain.add(coseSign1.moveValue());
|
||||
|
||||
if (supportedEekCurve == RpcHardwareInfo::CURVE_P256) {
|
||||
// convert ec public key to x and y co-ordinates.
|
||||
auto affineCoordinates = getAffineCoordinates(pub_key);
|
||||
if (!affineCoordinates) return affineCoordinates.moveMessage();
|
||||
auto [pubX, pubY] = affineCoordinates.moveValue();
|
||||
pub_key.clear();
|
||||
pub_key.insert(pub_key.begin(), pubX.begin(), pubX.end());
|
||||
pub_key.insert(pub_key.end(), pubY.begin(), pubY.end());
|
||||
}
|
||||
|
||||
return EekChain{eekChain.encode(), pub_key, priv_key};
|
||||
}
|
||||
|
||||
bytevec getProdEekChain() {
|
||||
bytevec prodEek;
|
||||
prodEek.reserve(1 + sizeof(kCoseEncodedRootCert) + sizeof(kCoseEncodedGeekCert));
|
||||
|
||||
// In CBOR encoding, 0x82 indicates an array of two items
|
||||
prodEek.push_back(0x82);
|
||||
prodEek.insert(prodEek.end(), std::begin(kCoseEncodedRootCert), std::end(kCoseEncodedRootCert));
|
||||
prodEek.insert(prodEek.end(), std::begin(kCoseEncodedGeekCert), std::end(kCoseEncodedGeekCert));
|
||||
|
||||
return prodEek;
|
||||
bytevec getProdEekChain(int32_t supportedEekCurve) {
|
||||
cppbor::Array chain;
|
||||
if (supportedEekCurve == RpcHardwareInfo::CURVE_P256) {
|
||||
chain.add(cppbor::EncodedItem(bytevec(std::begin(kCoseEncodedEcdsa256RootCert),
|
||||
std::end(kCoseEncodedEcdsa256RootCert))));
|
||||
chain.add(cppbor::EncodedItem(bytevec(std::begin(kCoseEncodedEcdsa256GeekCert),
|
||||
std::end(kCoseEncodedEcdsa256GeekCert))));
|
||||
} else {
|
||||
chain.add(cppbor::EncodedItem(
|
||||
bytevec(std::begin(kCoseEncodedRootCert), std::end(kCoseEncodedRootCert))));
|
||||
chain.add(cppbor::EncodedItem(
|
||||
bytevec(std::begin(kCoseEncodedGeekCert), std::end(kCoseEncodedGeekCert))));
|
||||
}
|
||||
return chain.encode();
|
||||
}
|
||||
|
||||
ErrMsgOr<bytevec> validatePayloadAndFetchPubKey(const cppbor::Map* payload) {
|
||||
|
@ -139,7 +318,8 @@ ErrMsgOr<bytevec> verifyAndParseCoseSign1Cwt(const cppbor::Array* coseSign1,
|
|||
}
|
||||
|
||||
auto& algorithm = parsedProtParams->asMap()->get(ALGORITHM);
|
||||
if (!algorithm || !algorithm->asInt() || algorithm->asInt()->value() != EDDSA) {
|
||||
if (!algorithm || !algorithm->asInt() ||
|
||||
(algorithm->asInt()->value() != EDDSA && algorithm->asInt()->value() != ES256)) {
|
||||
return "Unsupported signature algorithm";
|
||||
}
|
||||
|
||||
|
@ -152,15 +332,36 @@ ErrMsgOr<bytevec> verifyAndParseCoseSign1Cwt(const cppbor::Array* coseSign1,
|
|||
}
|
||||
|
||||
bool selfSigned = signingCoseKey.empty();
|
||||
auto key = CoseKey::parseEd25519(selfSigned ? *serializedKey : signingCoseKey);
|
||||
if (!key) return "Bad signing key: " + key.moveMessage();
|
||||
|
||||
bytevec signatureInput =
|
||||
cppbor::Array().add("Signature1").add(*protectedParams).add(aad).add(*payload).encode();
|
||||
cppbor::Array().add("Signature1").add(*protectedParams).add(aad).add(*payload).encode();
|
||||
|
||||
if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(),
|
||||
key->getBstrValue(CoseKey::PUBKEY_X)->data())) {
|
||||
return "Signature verification failed";
|
||||
if (algorithm->asInt()->value() == EDDSA) {
|
||||
auto key = CoseKey::parseEd25519(selfSigned ? *serializedKey : signingCoseKey);
|
||||
|
||||
if (!key) return "Bad signing key: " + key.moveMessage();
|
||||
|
||||
if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(),
|
||||
key->getBstrValue(CoseKey::PUBKEY_X)->data())) {
|
||||
return "Signature verification failed";
|
||||
}
|
||||
} else { // P256
|
||||
auto key = CoseKey::parseP256(selfSigned ? *serializedKey : signingCoseKey);
|
||||
if (!key || key->getBstrValue(CoseKey::PUBKEY_X)->empty() ||
|
||||
key->getBstrValue(CoseKey::PUBKEY_Y)->empty()) {
|
||||
return "Bad signing key: " + key.moveMessage();
|
||||
}
|
||||
auto publicKey = key->getEcPublicKey();
|
||||
if (!publicKey) return publicKey.moveMessage();
|
||||
|
||||
auto ecdsaDerSignature = ecdsaCoseSignatureToDer(signature->value());
|
||||
if (!ecdsaDerSignature) return ecdsaDerSignature.moveMessage();
|
||||
|
||||
// convert public key to uncompressed form.
|
||||
publicKey->insert(publicKey->begin(), 0x04);
|
||||
|
||||
if (!verifyEcdsaDigest(publicKey.moveValue(), sha256(signatureInput), *ecdsaDerSignature)) {
|
||||
return "Signature verification failed";
|
||||
}
|
||||
}
|
||||
|
||||
return serializedKey.moveValue();
|
||||
|
|
|
@ -14,8 +14,12 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "cppbor.h"
|
||||
#include "keymaster/cppcose/cppcose.h"
|
||||
#include <aidl/android/hardware/security/keymint/RpcHardwareInfo.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <cppbor_parse.h>
|
||||
#include <cstdint>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <keymaster/android_keymaster_utils.h>
|
||||
|
@ -23,25 +27,120 @@
|
|||
#include <keymaster/remote_provisioning_utils.h>
|
||||
#include <openssl/curve25519.h>
|
||||
#include <remote_prov/remote_prov_utils.h>
|
||||
#include <cstdint>
|
||||
#include "cppbor.h"
|
||||
#include "keymaster/cppcose/cppcose.h"
|
||||
|
||||
namespace aidl::android::hardware::security::keymint::remote_prov {
|
||||
namespace {
|
||||
|
||||
using ::keymaster::KeymasterBlob;
|
||||
using ::keymaster::validateAndExtractEekPubAndId;
|
||||
using ::keymaster::kStatusFailed;
|
||||
using ::keymaster::kStatusInvalidEek;
|
||||
using ::keymaster::StatusOr;
|
||||
using ::testing::ElementsAreArray;
|
||||
using byte_view = std::basic_string_view<uint8_t>;
|
||||
|
||||
struct KeyInfoEcdsa {
|
||||
CoseKeyCurve curve;
|
||||
byte_view pubKeyX;
|
||||
byte_view pubKeyY;
|
||||
|
||||
bool operator==(const KeyInfoEcdsa& other) const {
|
||||
return curve == other.curve && pubKeyX == other.pubKeyX && pubKeyY == other.pubKeyY;
|
||||
}
|
||||
};
|
||||
|
||||
// The production root signing key for Google ECDSA P256 Endpoint Encryption Key cert chains.
|
||||
inline constexpr uint8_t kEcdsa256GeekRootX[] = {
|
||||
0xf7, 0x14, 0x8a, 0xdb, 0x97, 0xf4, 0xcc, 0x53, 0xef, 0xd2, 0x64, 0x11, 0xc4, 0xe3, 0x75, 0x1f,
|
||||
0x66, 0x1f, 0xa4, 0x71, 0x0c, 0x6c, 0xcf, 0xfa, 0x09, 0x46, 0x80, 0x74, 0x87, 0x54, 0xf2, 0xad};
|
||||
|
||||
inline constexpr uint8_t kEcdsa256GeekRootY[] = {
|
||||
0x5e, 0x7f, 0x5b, 0xf6, 0xec, 0xe4, 0xf6, 0x19, 0xcc, 0xff, 0x13, 0x37, 0xfd, 0x0f, 0xa1, 0xc8,
|
||||
0x93, 0xdb, 0x18, 0x06, 0x76, 0xc4, 0x5d, 0xe6, 0xd7, 0x6a, 0x77, 0x86, 0xc3, 0x2d, 0xaf, 0x8f};
|
||||
|
||||
// Hard-coded set of acceptable public COSE_Keys that can act as roots of EEK chains.
|
||||
inline constexpr KeyInfoEcdsa kAuthorizedEcdsa256EekRoots[] = {
|
||||
{CoseKeyCurve::P256, byte_view(kEcdsa256GeekRootX, sizeof(kEcdsa256GeekRootX)),
|
||||
byte_view(kEcdsa256GeekRootY, sizeof(kEcdsa256GeekRootY))},
|
||||
};
|
||||
|
||||
static ErrMsgOr<CoseKey> parseEcdh256(const bytevec& coseKey) {
|
||||
auto key = CoseKey::parse(coseKey, EC2, ECDH_ES_HKDF_256, P256);
|
||||
if (!key) return key;
|
||||
|
||||
auto& pubkey_x = key->getMap().get(cppcose::CoseKey::PUBKEY_X);
|
||||
auto& pubkey_y = key->getMap().get(cppcose::CoseKey::PUBKEY_Y);
|
||||
if (!pubkey_x || !pubkey_y || !pubkey_x->asBstr() || !pubkey_y->asBstr() ||
|
||||
pubkey_x->asBstr()->value().size() != 32 || pubkey_y->asBstr()->value().size() != 32) {
|
||||
return "Invalid P256 public key";
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
StatusOr<std::tuple<std::vector<uint8_t> /* EEK pubX */, std::vector<uint8_t> /* EEK pubY */,
|
||||
std::vector<uint8_t> /* EEK ID */>>
|
||||
validateAndExtractEcdsa256EekPubAndId(bool testMode,
|
||||
const KeymasterBlob& endpointEncryptionCertChain) {
|
||||
auto [item, newPos, errMsg] =
|
||||
cppbor::parse(endpointEncryptionCertChain.begin(), endpointEncryptionCertChain.end());
|
||||
if (!item || !item->asArray()) {
|
||||
return kStatusFailed;
|
||||
}
|
||||
const cppbor::Array* certArr = item->asArray();
|
||||
std::vector<uint8_t> lastPubKey;
|
||||
for (size_t i = 0; i < certArr->size(); ++i) {
|
||||
auto cosePubKey =
|
||||
verifyAndParseCoseSign1(certArr->get(i)->asArray(), lastPubKey, {} /* AAD */);
|
||||
if (!cosePubKey) {
|
||||
return kStatusInvalidEek;
|
||||
}
|
||||
lastPubKey = *std::move(cosePubKey);
|
||||
|
||||
// In prod mode the first pubkey should match a well-known Google public key.
|
||||
if (!testMode && i == 0) {
|
||||
auto parsedPubKey = CoseKey::parse(lastPubKey);
|
||||
if (!parsedPubKey) {
|
||||
return kStatusFailed;
|
||||
}
|
||||
auto curve = parsedPubKey->getIntValue(CoseKey::CURVE);
|
||||
if (!curve) {
|
||||
return kStatusInvalidEek;
|
||||
}
|
||||
auto rawPubX = parsedPubKey->getBstrValue(CoseKey::PUBKEY_X);
|
||||
if (!rawPubX) {
|
||||
return kStatusInvalidEek;
|
||||
}
|
||||
auto rawPubY = parsedPubKey->getBstrValue(CoseKey::PUBKEY_Y);
|
||||
if (!rawPubY) {
|
||||
return kStatusInvalidEek;
|
||||
}
|
||||
KeyInfoEcdsa matcher = {static_cast<CoseKeyCurve>(*curve),
|
||||
byte_view(rawPubX->data(), rawPubX->size()),
|
||||
byte_view(rawPubY->data(), rawPubY->size())};
|
||||
if (std::find(std::begin(kAuthorizedEcdsa256EekRoots),
|
||||
std::end(kAuthorizedEcdsa256EekRoots),
|
||||
matcher) == std::end(kAuthorizedEcdsa256EekRoots)) {
|
||||
return kStatusInvalidEek;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto eek = parseEcdh256(lastPubKey);
|
||||
if (!eek) {
|
||||
return kStatusInvalidEek;
|
||||
}
|
||||
return std::make_tuple(eek->getBstrValue(CoseKey::PUBKEY_X).value(),
|
||||
eek->getBstrValue(CoseKey::PUBKEY_Y).value(),
|
||||
eek->getBstrValue(CoseKey::KEY_ID).value());
|
||||
}
|
||||
|
||||
TEST(RemoteProvUtilsTest, GenerateEekChainInvalidLength) {
|
||||
ASSERT_FALSE(generateEekChain(1, /*eekId=*/{}));
|
||||
ASSERT_FALSE(generateEekChain(RpcHardwareInfo::CURVE_25519, 1, /*eekId=*/{}));
|
||||
}
|
||||
|
||||
TEST(RemoteProvUtilsTest, GenerateEekChain) {
|
||||
bytevec kTestEekId = {'t', 'e', 's', 't', 'I', 'd', 0};
|
||||
for (size_t length : {2, 3, 31}) {
|
||||
auto get_eek_result = generateEekChain(length, kTestEekId);
|
||||
auto get_eek_result = generateEekChain(RpcHardwareInfo::CURVE_25519, length, kTestEekId);
|
||||
ASSERT_TRUE(get_eek_result) << get_eek_result.message();
|
||||
|
||||
auto& [chain, pubkey, privkey] = *get_eek_result;
|
||||
|
@ -57,7 +156,7 @@ TEST(RemoteProvUtilsTest, GenerateEekChain) {
|
|||
}
|
||||
|
||||
TEST(RemoteProvUtilsTest, GetProdEekChain) {
|
||||
auto chain = getProdEekChain();
|
||||
auto chain = getProdEekChain(RpcHardwareInfo::CURVE_25519);
|
||||
|
||||
auto validation_result = validateAndExtractEekPubAndId(
|
||||
/*testMode=*/false, KeymasterBlob(chain.data(), chain.size()));
|
||||
|
@ -97,5 +196,57 @@ TEST(RemoteProvUtilsTest, JsonEncodeCsr) {
|
|||
ASSERT_EQ(json, expected);
|
||||
}
|
||||
|
||||
TEST(RemoteProvUtilsTest, GenerateEcdsaEekChainInvalidLength) {
|
||||
ASSERT_FALSE(generateEekChain(RpcHardwareInfo::CURVE_P256, 1, /*eekId=*/{}));
|
||||
}
|
||||
|
||||
TEST(RemoteProvUtilsTest, GenerateEcdsaEekChain) {
|
||||
bytevec kTestEekId = {'t', 'e', 's', 't', 'I', 'd', 0};
|
||||
for (size_t length : {2, 3, 31}) {
|
||||
auto get_eek_result = generateEekChain(RpcHardwareInfo::CURVE_P256, length, kTestEekId);
|
||||
ASSERT_TRUE(get_eek_result) << get_eek_result.message();
|
||||
|
||||
auto& [chain, pubkey, privkey] = *get_eek_result;
|
||||
|
||||
auto validation_result = validateAndExtractEcdsa256EekPubAndId(
|
||||
/*testMode=*/true, KeymasterBlob(chain.data(), chain.size()));
|
||||
ASSERT_TRUE(validation_result.isOk());
|
||||
|
||||
auto& [eekPubX, eekPubY, eekId] = *validation_result;
|
||||
bytevec eekPub;
|
||||
eekPub.insert(eekPub.begin(), eekPubX.begin(), eekPubX.end());
|
||||
eekPub.insert(eekPub.end(), eekPubY.begin(), eekPubY.end());
|
||||
EXPECT_THAT(eekId, ElementsAreArray(kTestEekId));
|
||||
EXPECT_THAT(eekPub, ElementsAreArray(pubkey));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(RemoteProvUtilsTest, GetProdEcdsaEekChain) {
|
||||
auto chain = getProdEekChain(RpcHardwareInfo::CURVE_P256);
|
||||
|
||||
auto validation_result = validateAndExtractEcdsa256EekPubAndId(
|
||||
/*testMode=*/false, KeymasterBlob(chain.data(), chain.size()));
|
||||
ASSERT_TRUE(validation_result.isOk()) << "Error: " << validation_result.moveError();
|
||||
|
||||
auto& [eekPubX, eekPubY, eekId] = *validation_result;
|
||||
|
||||
auto [geekCert, ignoredNewPos, error] =
|
||||
cppbor::parse(kCoseEncodedEcdsa256GeekCert, sizeof(kCoseEncodedEcdsa256GeekCert));
|
||||
ASSERT_NE(geekCert, nullptr) << "Error: " << error;
|
||||
ASSERT_NE(geekCert->asArray(), nullptr);
|
||||
|
||||
auto& encodedGeekCoseKey = geekCert->asArray()->get(kCoseSign1Payload);
|
||||
ASSERT_NE(encodedGeekCoseKey, nullptr);
|
||||
ASSERT_NE(encodedGeekCoseKey->asBstr(), nullptr);
|
||||
|
||||
auto geek = CoseKey::parse(encodedGeekCoseKey->asBstr()->value());
|
||||
ASSERT_TRUE(geek) << "Error: " << geek.message();
|
||||
|
||||
const std::vector<uint8_t> empty;
|
||||
EXPECT_THAT(eekId, ElementsAreArray(geek->getBstrValue(CoseKey::KEY_ID).value_or(empty)));
|
||||
EXPECT_THAT(eekPubX, ElementsAreArray(geek->getBstrValue(CoseKey::PUBKEY_X).value_or(empty)));
|
||||
EXPECT_THAT(eekPubY, ElementsAreArray(geek->getBstrValue(CoseKey::PUBKEY_Y).value_or(empty)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace aidl::android::hardware::security::keymint::remote_prov
|
||||
|
|
Loading…
Reference in a new issue