From 1922688c1e701f56c29a8408386d9349c67ad5dc Mon Sep 17 00:00:00 2001 From: Evgeny Margolis Date: Thu, 22 Oct 2020 07:46:49 -0700 Subject: [PATCH] Added new ECDSA Methods that Sign and Verify SHA256 Hash. (#3365) Currently implemented ECDSA methods operate on a message, i.e. they calculate SHA256 hash of the message and then generate/verify the signature. In some use cases, it is required to calculate hash and generate/verify signature separately. To support this use case new ECDSA methods that operate on a hash are introduced. --- src/crypto/CHIPCryptoPAL.h | 26 ++++++ src/crypto/CHIPCryptoPALOpenSSL.cpp | 93 +++++++++++++++++++ src/crypto/CHIPCryptoPALmbedTLS.cpp | 64 +++++++++++++ src/crypto/tests/CHIPCryptoPALTest.cpp | 120 +++++++++++++++++++++++-- 4 files changed, 294 insertions(+), 9 deletions(-) mode change 100644 => 100755 src/crypto/tests/CHIPCryptoPALTest.cpp diff --git a/src/crypto/CHIPCryptoPAL.h b/src/crypto/CHIPCryptoPAL.h index 01a78dc981a537..6a97704b17a0b0 100644 --- a/src/crypto/CHIPCryptoPAL.h +++ b/src/crypto/CHIPCryptoPAL.h @@ -115,6 +115,10 @@ class ECPKey { return CHIP_ERROR_NOT_IMPLEMENTED; } + virtual CHIP_ERROR ECDSA_validate_hash_signature(const uint8_t * hash, const size_t hash_length, const Sig & signature) const + { + return CHIP_ERROR_NOT_IMPLEMENTED; + } }; template @@ -166,6 +170,8 @@ class P256PublicKey : public ECPKey CHIP_ERROR ECDSA_validate_msg_signature(const uint8_t * msg, size_t msg_length, const P256ECDSASignature & signature) const override; + CHIP_ERROR ECDSA_validate_hash_signature(const uint8_t * hash, size_t hash_length, + const P256ECDSASignature & signature) const override; private: uint8_t bytes[kP256_PublicKey_Length]; @@ -193,6 +199,16 @@ class ECPKeypair **/ virtual CHIP_ERROR ECDSA_sign_msg(const uint8_t * msg, size_t msg_length, Sig & out_signature) = 0; + /** + * @brief A function to sign a hash using ECDSA + * @param hash Hash that needs to be signed + * @param hash_length Length of hash + * @param out_signature Buffer that will hold the output signature. The signature consists of: 2 EC elements (r and s), + *represented as ASN.1 DER integers, plus the ASN.1 sequence Header + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR ECDSA_sign_hash(const uint8_t * hash, size_t hash_length, Sig & out_signature) = 0; + /** @brief A function to derive a shared secret using ECDH * @param remote_public_key Public key of remote peer with which we are trying to establish secure channel. remote_public_key is * ASN.1 DER encoded as padded big-endian field elements as described in SEC 1: Elliptic Curve Cryptography @@ -251,6 +267,16 @@ class P256Keypair : public ECPKeypair(hash_length), Uint8::to_uchar(out_signature), &out_length, ec_key); + VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); + // This should not happen due to the check above. But check this nonetheless + SuccessOrExit(out_signature.SetLength(out_length)); + +exit: + if (error != CHIP_NO_ERROR) + { + _logSSLError(); + } + + return error; +} + CHIP_ERROR P256PublicKey::ECDSA_validate_msg_signature(const uint8_t * msg, const size_t msg_length, const P256ECDSASignature & signature) const { @@ -658,6 +691,66 @@ CHIP_ERROR P256PublicKey::ECDSA_validate_msg_signature(const uint8_t * msg, cons return error; } +CHIP_ERROR P256PublicKey::ECDSA_validate_hash_signature(const uint8_t * hash, const size_t hash_length, + const P256ECDSASignature & signature) const +{ + ERR_clear_error(); + CHIP_ERROR error = CHIP_ERROR_INTERNAL; + int nid = NID_undef; + EC_KEY * ec_key = nullptr; + EC_POINT * key_point = nullptr; + EC_GROUP * ec_group = nullptr; + int result = 0; + + VerifyOrExit(hash != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(hash_length == kSHA256_Hash_Length, error = CHIP_ERROR_INVALID_ARGUMENT); + nid = _nidForCurve(MapECName(Type())); + VerifyOrExit(nid != NID_undef, error = CHIP_ERROR_INVALID_ARGUMENT); + + ec_group = EC_GROUP_new_by_curve_name(nid); + VerifyOrExit(ec_group != nullptr, error = CHIP_ERROR_INTERNAL); + + key_point = EC_POINT_new(ec_group); + VerifyOrExit(key_point != nullptr, error = CHIP_ERROR_INTERNAL); + + result = EC_POINT_oct2point(ec_group, key_point, Uint8::to_const_uchar(*this), Length(), nullptr); + VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); + + ec_key = EC_KEY_new_by_curve_name(nid); + VerifyOrExit(ec_key != nullptr, error = CHIP_ERROR_INTERNAL); + + result = EC_KEY_set_public_key(ec_key, key_point); + VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); + + result = EC_KEY_check_key(ec_key); + VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); + + // The cast for length arguments is safe because values are small enough to fit. + result = ECDSA_verify(0, hash, static_cast(hash_length), Uint8::to_const_uchar(signature), + static_cast(signature.Length()), ec_key); + VerifyOrExit(result == 1, error = CHIP_ERROR_INVALID_SIGNATURE); + error = CHIP_NO_ERROR; + +exit: + _logSSLError(); + if (ec_group != nullptr) + { + EC_GROUP_free(ec_group); + ec_group = nullptr; + } + if (key_point != nullptr) + { + EC_POINT_clear_free(key_point); + key_point = nullptr; + } + if (ec_key != nullptr) + { + EC_KEY_free(ec_key); + ec_key = nullptr; + } + return error; +} + // helper function to populate octet key into EVP_PKEY out_evp_pkey. Caller must free out_evp_pkey static CHIP_ERROR _create_evp_key_from_binary_p256_key(const P256PublicKey & key, EVP_PKEY ** out_evp_pkey) { diff --git a/src/crypto/CHIPCryptoPALmbedTLS.cpp b/src/crypto/CHIPCryptoPALmbedTLS.cpp index 5d4d50ce884a67..12470d35246938 100644 --- a/src/crypto/CHIPCryptoPALmbedTLS.cpp +++ b/src/crypto/CHIPCryptoPALmbedTLS.cpp @@ -451,6 +451,36 @@ CHIP_ERROR P256Keypair::ECDSA_sign_msg(const uint8_t * msg, const size_t msg_len return error; } +CHIP_ERROR P256Keypair::ECDSA_sign_hash(const uint8_t * hash, const size_t hash_length, P256ECDSASignature & out_signature) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 0; + size_t siglen = out_signature.Capacity(); + + const mbedtls_ecp_keypair * keypair = to_const_keypair(&mKeypair); + + mbedtls_ecdsa_context ecdsa_ctxt; + mbedtls_ecdsa_init(&ecdsa_ctxt); + + VerifyOrExit(mInitialized, error = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(hash != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(hash_length == NUM_BYTES_IN_SHA256_HASH, error = CHIP_ERROR_INVALID_ARGUMENT); + + result = mbedtls_ecdsa_from_keypair(&ecdsa_ctxt, keypair); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + result = mbedtls_ecdsa_write_signature(&ecdsa_ctxt, MBEDTLS_MD_SHA256, hash, hash_length, Uint8::to_uchar(out_signature), + &siglen, CryptoRNG, nullptr); + SuccessOrExit(out_signature.SetLength(siglen)); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + +exit: + keypair = nullptr; + mbedtls_ecdsa_free(&ecdsa_ctxt); + _log_mbedTLS_error(result); + return error; +} + CHIP_ERROR P256PublicKey::ECDSA_validate_msg_signature(const uint8_t * msg, const size_t msg_length, const P256ECDSASignature & signature) const { @@ -489,6 +519,40 @@ CHIP_ERROR P256PublicKey::ECDSA_validate_msg_signature(const uint8_t * msg, cons return error; } +CHIP_ERROR P256PublicKey::ECDSA_validate_hash_signature(const uint8_t * hash, const size_t hash_length, + const P256ECDSASignature & signature) const +{ + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 0; + + mbedtls_ecp_keypair keypair; + mbedtls_ecp_keypair_init(&keypair); + + mbedtls_ecdsa_context ecdsa_ctxt; + mbedtls_ecdsa_init(&ecdsa_ctxt); + + VerifyOrExit(hash != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(hash_length == NUM_BYTES_IN_SHA256_HASH, error = CHIP_ERROR_INVALID_ARGUMENT); + + result = mbedtls_ecp_group_load(&keypair.grp, MapECPGroupId(Type())); + VerifyOrExit(result == 0, error = CHIP_ERROR_INVALID_ARGUMENT); + + result = mbedtls_ecp_point_read_binary(&keypair.grp, &keypair.Q, Uint8::to_const_uchar(*this), Length()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INVALID_ARGUMENT); + + result = mbedtls_ecdsa_from_keypair(&ecdsa_ctxt, &keypair); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + result = mbedtls_ecdsa_read_signature(&ecdsa_ctxt, hash, hash_length, Uint8::to_const_uchar(signature), signature.Length()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INVALID_SIGNATURE); + +exit: + mbedtls_ecp_keypair_free(&keypair); + mbedtls_ecdsa_free(&ecdsa_ctxt); + _log_mbedTLS_error(result); + return error; +} + CHIP_ERROR P256Keypair::ECDH_derive_secret(const P256PublicKey & remote_public_key, P256ECDHDerivedSecret & out_secret) const { CHIP_ERROR error = CHIP_NO_ERROR; diff --git a/src/crypto/tests/CHIPCryptoPALTest.cpp b/src/crypto/tests/CHIPCryptoPALTest.cpp old mode 100644 new mode 100755 index a69e62f56044f9..745798b03f510e --- a/src/crypto/tests/CHIPCryptoPALTest.cpp +++ b/src/crypto/tests/CHIPCryptoPALTest.cpp @@ -599,7 +599,7 @@ static void TestDRBG_Output(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, memcmp(out_buf, orig_buf, sizeof(out_buf)) != 0); } -static void TestECDSA_Signing_SHA256(nlTestSuite * inSuite, void * inContext) +static void TestECDSA_Signing_SHA256_Msg(nlTestSuite * inSuite, void * inContext) { const char * msg = "Hello World!"; size_t msg_length = strlen(msg); @@ -616,6 +616,23 @@ static void TestECDSA_Signing_SHA256(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, validation_error == CHIP_NO_ERROR); } +static void TestECDSA_Signing_SHA256_Hash(nlTestSuite * inSuite, void * inContext) +{ + const uint8_t hash[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F }; + size_t hash_length = sizeof(hash); + + P256Keypair keypair; + NL_TEST_ASSERT(inSuite, keypair.Initialize() == CHIP_NO_ERROR); + + P256ECDSASignature signature; + CHIP_ERROR signing_error = keypair.ECDSA_sign_hash(hash, hash_length, signature); + NL_TEST_ASSERT(inSuite, signing_error == CHIP_NO_ERROR); + + CHIP_ERROR validation_error = keypair.Pubkey().ECDSA_validate_hash_signature(hash, hash_length, signature); + NL_TEST_ASSERT(inSuite, validation_error == CHIP_NO_ERROR); +} + static void TestECDSA_ValidationFailsDifferentMessage(nlTestSuite * inSuite, void * inContext) { const char * msg = "Hello World!"; @@ -632,10 +649,31 @@ static void TestECDSA_ValidationFailsDifferentMessage(nlTestSuite * inSuite, voi size_t diff_msg_length = strlen(msg); CHIP_ERROR validation_error = keypair.Pubkey().ECDSA_validate_msg_signature(reinterpret_cast(diff_msg), diff_msg_length, signature); - NL_TEST_ASSERT(inSuite, validation_error != CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, validation_error == CHIP_ERROR_INVALID_SIGNATURE); +} + +static void TestECDSA_ValidationFailsDifferentHash(nlTestSuite * inSuite, void * inContext) +{ + const uint8_t hash[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F }; + size_t hash_length = sizeof(hash); + + P256Keypair keypair; + NL_TEST_ASSERT(inSuite, keypair.Initialize() == CHIP_NO_ERROR); + + P256ECDSASignature signature; + CHIP_ERROR signing_error = keypair.ECDSA_sign_hash(hash, hash_length, signature); + NL_TEST_ASSERT(inSuite, signing_error == CHIP_NO_ERROR); + + const uint8_t diff_hash[] = { 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x02, 0x00 }; + size_t diff_hash_length = sizeof(diff_hash); + + CHIP_ERROR validation_error = keypair.Pubkey().ECDSA_validate_hash_signature(diff_hash, diff_hash_length, signature); + NL_TEST_ASSERT(inSuite, validation_error == CHIP_ERROR_INVALID_SIGNATURE); } -static void TestECDSA_ValidationFailIncorrectSignature(nlTestSuite * inSuite, void * inContext) +static void TestECDSA_ValidationFailIncorrectMsgSignature(nlTestSuite * inSuite, void * inContext) { const char * msg = "Hello World!"; size_t msg_length = strlen(msg); @@ -653,7 +691,25 @@ static void TestECDSA_ValidationFailIncorrectSignature(nlTestSuite * inSuite, vo NL_TEST_ASSERT(inSuite, validation_error == CHIP_ERROR_INVALID_SIGNATURE); } -static void TestECDSA_SigningInvalidParams(nlTestSuite * inSuite, void * inContext) +static void TestECDSA_ValidationFailIncorrectHashSignature(nlTestSuite * inSuite, void * inContext) +{ + const uint8_t hash[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F }; + size_t hash_length = sizeof(hash); + + P256Keypair keypair; + NL_TEST_ASSERT(inSuite, keypair.Initialize() == CHIP_NO_ERROR); + + P256ECDSASignature signature; + CHIP_ERROR signing_error = keypair.ECDSA_sign_hash(hash, hash_length, signature); + NL_TEST_ASSERT(inSuite, signing_error == CHIP_NO_ERROR); + signature[0] = static_cast(~signature[0]); // Flipping bits should invalidate the signature. + + CHIP_ERROR validation_error = keypair.Pubkey().ECDSA_validate_hash_signature(hash, hash_length, signature); + NL_TEST_ASSERT(inSuite, validation_error == CHIP_ERROR_INVALID_SIGNATURE); +} + +static void TestECDSA_SigningMsgInvalidParams(nlTestSuite * inSuite, void * inContext) { const uint8_t * msg = reinterpret_cast("Hello World!"); size_t msg_length = strlen(reinterpret_cast(msg)); @@ -671,7 +727,26 @@ static void TestECDSA_SigningInvalidParams(nlTestSuite * inSuite, void * inConte signing_error = CHIP_NO_ERROR; } -static void TestECDSA_ValidationInvalidParam(nlTestSuite * inSuite, void * inContext) +static void TestECDSA_SigningHashInvalidParams(nlTestSuite * inSuite, void * inContext) +{ + const uint8_t hash[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F }; + size_t hash_length = sizeof(hash); + + P256Keypair keypair; + NL_TEST_ASSERT(inSuite, keypair.Initialize() == CHIP_NO_ERROR); + + P256ECDSASignature signature; + CHIP_ERROR signing_error = keypair.ECDSA_sign_hash(nullptr, hash_length, signature); + NL_TEST_ASSERT(inSuite, signing_error == CHIP_ERROR_INVALID_ARGUMENT); + signing_error = CHIP_NO_ERROR; + + signing_error = keypair.ECDSA_sign_hash(hash, hash_length - 5, signature); + NL_TEST_ASSERT(inSuite, signing_error == CHIP_ERROR_INVALID_ARGUMENT); + signing_error = CHIP_NO_ERROR; +} + +static void TestECDSA_ValidationMsgInvalidParam(nlTestSuite * inSuite, void * inContext) { const char * msg = "Hello World!"; size_t msg_length = strlen(msg); @@ -692,6 +767,28 @@ static void TestECDSA_ValidationInvalidParam(nlTestSuite * inSuite, void * inCon validation_error = CHIP_NO_ERROR; } +static void TestECDSA_ValidationHashInvalidParam(nlTestSuite * inSuite, void * inContext) +{ + const uint8_t hash[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F }; + size_t hash_length = sizeof(hash); + + P256Keypair keypair; + NL_TEST_ASSERT(inSuite, keypair.Initialize() == CHIP_NO_ERROR); + + P256ECDSASignature signature; + CHIP_ERROR signing_error = keypair.ECDSA_sign_hash(hash, hash_length, signature); + NL_TEST_ASSERT(inSuite, signing_error == CHIP_NO_ERROR); + + CHIP_ERROR validation_error = keypair.Pubkey().ECDSA_validate_hash_signature(nullptr, hash_length, signature); + NL_TEST_ASSERT(inSuite, validation_error == CHIP_ERROR_INVALID_ARGUMENT); + signing_error = CHIP_NO_ERROR; + + validation_error = keypair.Pubkey().ECDSA_validate_hash_signature(hash, hash_length - 5, signature); + NL_TEST_ASSERT(inSuite, validation_error == CHIP_ERROR_INVALID_ARGUMENT); + signing_error = CHIP_NO_ERROR; +} + static void TestECDH_EstablishSecret(nlTestSuite * inSuite, void * inContext) { P256Keypair keypair1; @@ -1235,11 +1332,16 @@ static const nlTest sTests[] = { NL_TEST_DEF("Test decrypting AES-CCM-256 invalid key", TestAES_CCM_256DecryptInvalidKey), NL_TEST_DEF("Test decrypting AES-CCM-256 invalid IV", TestAES_CCM_256DecryptInvalidIVLen), NL_TEST_DEF("Test decrypting AES-CCM-256 invalid vectors", TestAES_CCM_256DecryptInvalidTestVectors), - NL_TEST_DEF("Test ECDSA signing and validation using SHA256", TestECDSA_Signing_SHA256), + NL_TEST_DEF("Test ECDSA signing and validation message using SHA256", TestECDSA_Signing_SHA256_Msg), + NL_TEST_DEF("Test ECDSA signing and validation SHA256 Hash", TestECDSA_Signing_SHA256_Hash), NL_TEST_DEF("Test ECDSA signature validation fail - Different msg", TestECDSA_ValidationFailsDifferentMessage), - NL_TEST_DEF("Test ECDSA signature validation fail - Different signature", TestECDSA_ValidationFailIncorrectSignature), - NL_TEST_DEF("Test ECDSA sign msg invalid parameters", TestECDSA_SigningInvalidParams), - NL_TEST_DEF("Test ECDSA signature validation invalid parameters", TestECDSA_ValidationInvalidParam), + NL_TEST_DEF("Test ECDSA signature validation fail - Different hash", TestECDSA_ValidationFailsDifferentHash), + NL_TEST_DEF("Test ECDSA signature validation fail - Different msg signature", TestECDSA_ValidationFailIncorrectMsgSignature), + NL_TEST_DEF("Test ECDSA signature validation fail - Different hash signature", TestECDSA_ValidationFailIncorrectHashSignature), + NL_TEST_DEF("Test ECDSA sign msg invalid parameters", TestECDSA_SigningMsgInvalidParams), + NL_TEST_DEF("Test ECDSA sign hash invalid parameters", TestECDSA_SigningHashInvalidParams), + NL_TEST_DEF("Test ECDSA msg signature validation invalid parameters", TestECDSA_ValidationMsgInvalidParam), + NL_TEST_DEF("Test ECDSA hash signature validation invalid parameters", TestECDSA_ValidationHashInvalidParam), NL_TEST_DEF("Test Hash SHA 256", TestHash_SHA256), NL_TEST_DEF("Test Hash SHA 256 Stream", TestHash_SHA256_Stream), NL_TEST_DEF("Test HKDF SHA 256", TestHKDF_SHA256),