Loading security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp +29 −14 Original line number Diff line number Diff line Loading @@ -185,6 +185,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() { Loading @@ -194,6 +195,7 @@ class VtsRemotelyProvisionedComponentTests : public testing::TestWithParam<std:: protected: std::shared_ptr<IRemotelyProvisionedComponent> provisionable_; RpcHardwareInfo rpcHardwareInfo; }; /** Loading Loading @@ -357,11 +359,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; Loading @@ -382,6 +383,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) { Loading @@ -394,9 +406,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 = Loading @@ -406,7 +416,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); Loading Loading @@ -566,6 +577,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); Loading Loading @@ -605,8 +617,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()); } Loading Loading @@ -646,8 +658,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()); } Loading @@ -662,6 +674,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); Loading @@ -681,8 +694,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); } Loading @@ -695,7 +708,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); Loading Loading @@ -726,7 +739,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); Loading Loading @@ -754,6 +767,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); Loading @@ -772,6 +786,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); Loading security/keymint/support/Android.bp +7 −0 Original line number Diff line number Diff line Loading @@ -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", ], } Loading @@ -76,6 +80,9 @@ cc_test { "libgmock", "libgtest_main", ], defaults: [ "keymint_use_latest_hal_aidl_ndk_shared", ], shared_libs: [ "libbase", "libcppbor_external", Loading security/keymint/support/include/remote_prov/remote_prov_utils.h +32 −4 Original line number Diff line number Diff line Loading @@ -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. */ Loading @@ -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; Loading security/keymint/support/remote_prov_utils.cpp +247 −46 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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 */); Loading @@ -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."; } Loading @@ -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 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 coseKey = constructCoseKey(supportedEekCurve, eekId, 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()); return EekChain{eekChain.encode(), pub_key, priv_key}; 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()); } 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 EekChain{eekChain.encode(), pub_key, priv_key}; } 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) { Loading Loading @@ -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"; } Loading @@ -152,16 +332,37 @@ 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(); 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(); } Loading security/keymint/support/remote_prov_utils_test.cpp +158 −7 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp +29 −14 Original line number Diff line number Diff line Loading @@ -185,6 +185,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() { Loading @@ -194,6 +195,7 @@ class VtsRemotelyProvisionedComponentTests : public testing::TestWithParam<std:: protected: std::shared_ptr<IRemotelyProvisionedComponent> provisionable_; RpcHardwareInfo rpcHardwareInfo; }; /** Loading Loading @@ -357,11 +359,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; Loading @@ -382,6 +383,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) { Loading @@ -394,9 +406,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 = Loading @@ -406,7 +416,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); Loading Loading @@ -566,6 +577,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); Loading Loading @@ -605,8 +617,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()); } Loading Loading @@ -646,8 +658,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()); } Loading @@ -662,6 +674,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); Loading @@ -681,8 +694,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); } Loading @@ -695,7 +708,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); Loading Loading @@ -726,7 +739,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); Loading Loading @@ -754,6 +767,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); Loading @@ -772,6 +786,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); Loading
security/keymint/support/Android.bp +7 −0 Original line number Diff line number Diff line Loading @@ -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", ], } Loading @@ -76,6 +80,9 @@ cc_test { "libgmock", "libgtest_main", ], defaults: [ "keymint_use_latest_hal_aidl_ndk_shared", ], shared_libs: [ "libbase", "libcppbor_external", Loading
security/keymint/support/include/remote_prov/remote_prov_utils.h +32 −4 Original line number Diff line number Diff line Loading @@ -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. */ Loading @@ -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; Loading
security/keymint/support/remote_prov_utils.cpp +247 −46 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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 */); Loading @@ -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."; } Loading @@ -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 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 coseKey = constructCoseKey(supportedEekCurve, eekId, 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()); return EekChain{eekChain.encode(), pub_key, priv_key}; 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()); } 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 EekChain{eekChain.encode(), pub_key, priv_key}; } 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) { Loading Loading @@ -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"; } Loading @@ -152,16 +332,37 @@ 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(); 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(); } Loading
security/keymint/support/remote_prov_utils_test.cpp +158 −7 File changed.Preview size limit exceeded, changes collapsed. Show changes