diff options
Diffstat (limited to 'security/keymint/support/remote_prov_utils.cpp')
-rw-r--r-- | security/keymint/support/remote_prov_utils.cpp | 293 |
1 files changed, 247 insertions, 46 deletions
diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp index 35cb891dac..0776282b27 100644 --- a/security/keymint/support/remote_prov_utils.cpp +++ b/security/keymint/support/remote_prov_utils.cpp @@ -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 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()); + 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 (algorithm->asInt()->value() == EDDSA) { + auto key = CoseKey::parseEd25519(selfSigned ? *serializedKey : signingCoseKey); - if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(), - key->getBstrValue(CoseKey::PUBKEY_X)->data())) { - return "Signature verification failed"; + 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(); |