diff options
author | subrahmanyaman <subrahmanyaman@google.com> | 2022-02-02 23:10:55 +0000 |
---|---|---|
committer | subrahmanyaman <subrahmanyaman@google.com> | 2022-02-23 05:02:40 +0000 |
commit | fb213d60310fdd7905be85856b25cf7c79084d9f (patch) | |
tree | 1f5047706f000245cbf6a50b3b98e614d6faa134 /security/keymint/support/remote_prov_utils_test.cpp | |
parent | 1048b42ea600458b224a325c52ba9d431356acf1 (diff) |
Support for P256 curve in RKP for Strongbox
Test: Run Rkp Vts tests.
Change-Id: Ic38fd2318dd8749ae125f1e78d25f2722bd367e5
Diffstat (limited to 'security/keymint/support/remote_prov_utils_test.cpp')
-rw-r--r-- | security/keymint/support/remote_prov_utils_test.cpp | 165 |
1 files changed, 158 insertions, 7 deletions
diff --git a/security/keymint/support/remote_prov_utils_test.cpp b/security/keymint/support/remote_prov_utils_test.cpp index 8697c5190f..e1c4467a64 100644 --- a/security/keymint/support/remote_prov_utils_test.cpp +++ b/security/keymint/support/remote_prov_utils_test.cpp @@ -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 |