diff options
author | Andrew Scull <ascull@google.com> | 2023-02-17 17:34:58 +0000 |
---|---|---|
committer | Andrew Scull <ascull@google.com> | 2023-02-23 21:23:10 +0000 |
commit | b1d26dda82a7f56f2907673d73d9525e32c6fec6 (patch) | |
tree | d440e2ad5f43a4c2d81760d054824896fc8324aa | |
parent | c2c3ed21299a517d83c01f677b1ac9a473ad11bf (diff) | |
download | keymaster-b1d26dda82a7f56f2907673d73d9525e32c6fec6.tar.gz |
Add support for P-384 CoseKeys
RKP allows P-384 to be used in the DICE chain and for signing CSRs. Add
support for this algorithm so that the VTS tests will pass on devices
that use P-384.
Bug: 261647022
Test: atest VtsHalRemotelyProvisionedComponentTargetTest
Change-Id: I2fb6f1ef1a111d7c6ee057217dcc1c4d36391a78
-rw-r--r-- | cppcose/cppcose.cpp | 124 | ||||
-rw-r--r-- | include/keymaster/cppcose/cppcose.h | 22 |
2 files changed, 96 insertions, 50 deletions
diff --git a/cppcose/cppcose.cpp b/cppcose/cppcose.cpp index 411dc01..2eb39de 100644 --- a/cppcose/cppcose.cpp +++ b/cppcose/cppcose.cpp @@ -27,6 +27,7 @@ namespace cppcose { constexpr int kP256AffinePointSize = 32; +constexpr int kP384AffinePointSize = 48; using EVP_PKEY_Ptr = bssl::UniquePtr<EVP_PKEY>; using EVP_PKEY_CTX_Ptr = bssl::UniquePtr<EVP_PKEY_CTX>; @@ -58,7 +59,7 @@ ErrMsgOr<bssl::UniquePtr<EVP_CIPHER_CTX>> aesGcmInitAndProcessAad(const bytevec& return std::move(ctx); } -ErrMsgOr<bytevec> signEcdsaDigest(const bytevec& key, const bytevec& data) { +ErrMsgOr<bytevec> signP256Digest(const bytevec& key, const bytevec& data) { auto bn = BIGNUM_Ptr(BN_bin2bn(key.data(), key.size(), nullptr)); if (bn.get() == nullptr) { return "Error creating BIGNUM"; @@ -141,19 +142,17 @@ ErrMsgOr<bytevec> ecdh(const bytevec& publicKey, const bytevec& privateKey) { return sharedSecret; } -} // namespace - -ErrMsgOr<bytevec> ecdsaCoseSignatureToDer(const bytevec& ecdsaCoseSignature) { - if (ecdsaCoseSignature.size() != 64) { +ErrMsgOr<bytevec> ecdsaCoseSignatureToDer(int point_size, const bytevec& ecdsaCoseSignature) { + if (ecdsaCoseSignature.size() != (size_t)(point_size * 2)) { return "COSE signature wrong length"; } - auto rBn = BIGNUM_Ptr(BN_bin2bn(ecdsaCoseSignature.data(), 32, nullptr)); + auto rBn = BIGNUM_Ptr(BN_bin2bn(ecdsaCoseSignature.data(), point_size, nullptr)); if (rBn.get() == nullptr) { return "Error creating BIGNUM for r"; } - auto sBn = BIGNUM_Ptr(BN_bin2bn(ecdsaCoseSignature.data() + 32, 32, nullptr)); + auto sBn = BIGNUM_Ptr(BN_bin2bn(ecdsaCoseSignature.data() + point_size, point_size, nullptr)); if (sBn.get() == nullptr) { return "Error creating BIGNUM for s"; } @@ -169,23 +168,58 @@ ErrMsgOr<bytevec> ecdsaCoseSignatureToDer(const bytevec& ecdsaCoseSignature) { return derSignature; } -ErrMsgOr<bytevec> ecdsaDerSignatureToCose(const bytevec& ecdsaSignature) { +ErrMsgOr<bytevec> ecdsaDerSignatureToCose(int point_size, const bytevec& ecdsaSignature) { const unsigned char* p = ecdsaSignature.data(); auto sig = ECDSA_SIG_Ptr(d2i_ECDSA_SIG(nullptr, &p, ecdsaSignature.size())); if (sig == nullptr) { return "Error decoding DER signature"; } - bytevec ecdsaCoseSignature(64, 0); - if (BN_bn2binpad(ECDSA_SIG_get0_r(sig.get()), ecdsaCoseSignature.data(), 32) != 32) { + bytevec ecdsaCoseSignature(point_size * 2, 0); + if (BN_bn2binpad(ECDSA_SIG_get0_r(sig.get()), ecdsaCoseSignature.data(), point_size) != + point_size) { return "Error encoding r"; } - if (BN_bn2binpad(ECDSA_SIG_get0_s(sig.get()), ecdsaCoseSignature.data() + 32, 32) != 32) { + if (BN_bn2binpad(ECDSA_SIG_get0_s(sig.get()), ecdsaCoseSignature.data() + point_size, + point_size) != point_size) { return "Error encoding s"; } return ecdsaCoseSignature; } +bool verifyEcdsaDigest(int curve_nid, const bytevec& key, const bytevec& digest, + const bytevec& signature) { + const unsigned char* p = (unsigned char*)signature.data(); + auto sig = ECDSA_SIG_Ptr(d2i_ECDSA_SIG(nullptr, &p, signature.size())); + if (sig.get() == nullptr) { + return false; + } + + auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(curve_nid)); + auto point = EC_POINT_Ptr(EC_POINT_new(group.get())); + if (EC_POINT_oct2point(group.get(), point.get(), key.data(), key.size(), nullptr) != 1) { + return false; + } + auto ecKey = EC_KEY_Ptr(EC_KEY_new()); + if (ecKey.get() == nullptr) { + return false; + } + if (EC_KEY_set_group(ecKey.get(), group.get()) != 1) { + return false; + } + if (EC_KEY_set_public_key(ecKey.get(), point.get()) != 1) { + return false; + } + + int rc = ECDSA_do_verify(digest.data(), digest.size(), sig.get(), ecKey.get()); + if (rc != 1) { + return false; + } + return true; +} + +} // namespace + ErrMsgOr<HmacSha256> generateHmacSha256(const bytevec& key, const bytevec& data) { HmacSha256 digest; unsigned int outLen; @@ -275,10 +309,10 @@ ErrMsgOr<bytevec> createECDSACoseSign1Signature(const bytevec& key, const byteve .add(aad) .add(payload) .encode(); - auto ecdsaSignature = signEcdsaDigest(key, sha256(signatureInput)); + auto ecdsaSignature = signP256Digest(key, sha256(signatureInput)); if (!ecdsaSignature) return ecdsaSignature.moveMessage(); - return ecdsaDerSignatureToCose(*ecdsaSignature); + return ecdsaDerSignatureToCose(kP256AffinePointSize, *ecdsaSignature); } ErrMsgOr<bytevec> createCoseSign1Signature(const bytevec& key, const bytevec& protectedParams, @@ -354,7 +388,8 @@ ErrMsgOr<bytevec> verifyAndParseCoseSign1(const cppbor::Array* coseSign1, auto& algorithm = parsedProtParams->asMap()->get(ALGORITHM); if (!algorithm || !algorithm->asInt() || - !(algorithm->asInt()->value() == EDDSA || algorithm->asInt()->value() == ES256)) { + !(algorithm->asInt()->value() == EDDSA || algorithm->asInt()->value() == ES256 || + algorithm->asInt()->value() == ES384)) { return "Unsupported signature algorithm"; } @@ -376,7 +411,7 @@ ErrMsgOr<bytevec> verifyAndParseCoseSign1(const cppbor::Array* coseSign1, key->getBstrValue(CoseKey::PUBKEY_X)->data())) { return "Signature verification failed"; } - } else { // P256 + } else if (algorithm->asInt()->value() == ES256) { auto key = CoseKey::parseP256(selfSigned ? payload->value() : signingCoseKey); if (!key || key->getBstrValue(CoseKey::PUBKEY_X)->empty() || key->getBstrValue(CoseKey::PUBKEY_Y)->empty()) { @@ -385,13 +420,33 @@ ErrMsgOr<bytevec> verifyAndParseCoseSign1(const cppbor::Array* coseSign1, auto publicKey = key->getEcPublicKey(); if (!publicKey) return publicKey.moveMessage(); - auto ecdsaDerSignature = ecdsaCoseSignatureToDer(signature->value()); + auto ecdsaDerSignature = ecdsaCoseSignatureToDer(kP256AffinePointSize, signature->value()); + if (!ecdsaDerSignature) return ecdsaDerSignature.moveMessage(); + + // convert public key to uncompressed form by prepending 0x04 at begin. + publicKey->insert(publicKey->begin(), 0x04); + + if (!verifyEcdsaDigest(NID_X9_62_prime256v1, publicKey.moveValue(), sha256(signatureInput), + *ecdsaDerSignature)) { + return "Signature verification failed"; + } + } else { // ES384 + auto key = CoseKey::parseP384(selfSigned ? payload->value() : 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(kP384AffinePointSize, signature->value()); if (!ecdsaDerSignature) return ecdsaDerSignature.moveMessage(); // convert public key to uncompressed form by prepending 0x04 at begin. publicKey->insert(publicKey->begin(), 0x04); - if (!verifyEcdsaDigest(publicKey.moveValue(), sha256(signatureInput), *ecdsaDerSignature)) { + if (!verifyEcdsaDigest(NID_secp384r1, publicKey.moveValue(), sha384(signatureInput), + *ecdsaDerSignature)) { return "Signature verification failed"; } } @@ -708,34 +763,13 @@ bytevec sha256(const bytevec& data) { return ret; } -bool verifyEcdsaDigest(const bytevec& key, const bytevec& digest, const bytevec& signature) { - const unsigned char* p = (unsigned char*)signature.data(); - auto sig = ECDSA_SIG_Ptr(d2i_ECDSA_SIG(nullptr, &p, signature.size())); - if (sig.get() == nullptr) { - return false; - } - - auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); - auto point = EC_POINT_Ptr(EC_POINT_new(group.get())); - if (EC_POINT_oct2point(group.get(), point.get(), key.data(), key.size(), nullptr) != 1) { - return false; - } - auto ecKey = EC_KEY_Ptr(EC_KEY_new()); - if (ecKey.get() == nullptr) { - return false; - } - if (EC_KEY_set_group(ecKey.get(), group.get()) != 1) { - return false; - } - if (EC_KEY_set_public_key(ecKey.get(), point.get()) != 1) { - return false; - } - - int rc = ECDSA_do_verify(digest.data(), digest.size(), sig.get(), ecKey.get()); - if (rc != 1) { - return false; - } - return true; +bytevec sha384(const bytevec& data) { + bytevec ret(SHA384_DIGEST_LENGTH); + SHA512_CTX ctx; + SHA384_Init(&ctx); + SHA384_Update(&ctx, data.data(), data.size()); + SHA384_Final((unsigned char*)ret.data(), &ctx); + return ret; } } // namespace cppcose diff --git a/include/keymaster/cppcose/cppcose.h b/include/keymaster/cppcose/cppcose.h index c000ebe..fa5916f 100644 --- a/include/keymaster/cppcose/cppcose.h +++ b/include/keymaster/cppcose/cppcose.h @@ -81,9 +81,10 @@ enum CoseKeyAlgorithm : int { ES256 = -7, // ECDSA with SHA-256 EDDSA = -8, ECDH_ES_HKDF_256 = -25, + ES384 = -35, // ECDSA with SHA-384 }; -enum CoseKeyCurve : int { P256 = 1, X25519 = 4, ED25519 = 6 }; +enum CoseKeyCurve : int { P256 = 1, P384 = 2, X25519 = 4, ED25519 = 6 }; enum CoseKeyType : int { OCTET_KEY_PAIR = 1, EC2 = 2, SYMMETRIC_KEY = 4 }; enum CoseKeyOps : int { SIGN = 1, VERIFY = 2, ENCRYPT = 3, DECRYPT = 4 }; @@ -213,6 +214,20 @@ class CoseKey { return key; } + static ErrMsgOr<CoseKey> parseP384(const bytevec& coseKey) { + auto key = parse(coseKey, EC2, ES384, P384); + if (!key) return key; + + auto& pubkey_x = key->getMap().get(PUBKEY_X); + auto& pubkey_y = key->getMap().get(PUBKEY_Y); + if (!pubkey_x || !pubkey_y || !pubkey_x->asBstr() || !pubkey_y->asBstr() || + pubkey_x->asBstr()->value().size() != 48 || pubkey_y->asBstr()->value().size() != 48) { + return "Invalid P384 public key"; + } + + return key; + } + static ErrMsgOr<bytevec> getEcPublicKey(const bytevec& pubX, const bytevec& pubY) { if (pubX.empty() || pubY.empty()) { return "Missing input parameters"; @@ -285,9 +300,6 @@ ErrMsgOr<cppbor::Array> constructECDSACoseSign1(const bytevec& key, cppbor::Map extraProtectedFields, const bytevec& payload, const bytevec& aad); -ErrMsgOr<bytevec> ecdsaCoseSignatureToDer(const bytevec& ecdsaCoseSignature); - -ErrMsgOr<bytevec> ecdsaDerSignatureToCose(const bytevec& ecdsaSignature); /** * Verify and parse a COSE_Sign1 message, returning the payload. * @@ -320,8 +332,8 @@ ErrMsgOr<bytevec> x25519_HKDF_DeriveKey(const bytevec& senderPubKey, const bytev const bytevec& recipientPubKey, bool senderIsA); ErrMsgOr<bytevec> ECDH_HKDF_DeriveKey(const bytevec& pubKeyA, const bytevec& privKeyA, const bytevec& pubKeyB, bool senderIsA); -bool verifyEcdsaDigest(const bytevec& key, const bytevec& digest, const bytevec& signature); bytevec sha256(const bytevec& data); +bytevec sha384(const bytevec& data); ErrMsgOr<bytevec /* ciphertextWithTag */> aesGcmEncrypt(const bytevec& key, const bytevec& nonce, const bytevec& aad, const bytevec& plaintext); |