Skip to content

Commit

Permalink
Added new ECDSA Methods that Sign and Verify SHA256 Hash. (#3365)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
emargolis authored and pull[bot] committed Jan 21, 2021
1 parent 61607bc commit 1922688
Show file tree
Hide file tree
Showing 4 changed files with 294 additions and 9 deletions.
26 changes: 26 additions & 0 deletions src/crypto/CHIPCryptoPAL.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <size_t Cap>
Expand Down Expand Up @@ -166,6 +170,8 @@ class P256PublicKey : public ECPKey<P256ECDSASignature>

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];
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -251,6 +267,16 @@ class P256Keypair : public ECPKeypair<P256PublicKey, P256ECDHDerivedSecret, P256
**/
CHIP_ERROR ECDSA_sign_msg(const uint8_t * msg, size_t msg_length, P256ECDSASignature & out_signature) override;

/**
* @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
**/
CHIP_ERROR ECDSA_sign_hash(const uint8_t * hash, size_t hash_length, P256ECDSASignature & out_signature) override;

/** @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
Expand Down
93 changes: 93 additions & 0 deletions src/crypto/CHIPCryptoPALOpenSSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,39 @@ 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)
{
ERR_clear_error();

CHIP_ERROR error = CHIP_NO_ERROR;
int result = 0;
int nid = NID_undef;
EC_KEY * ec_key = nullptr;
uint out_length = 0;

VerifyOrExit(mInitialized, error = CHIP_ERROR_INCORRECT_STATE);
VerifyOrExit(hash != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrExit(hash_length == kSHA256_Hash_Length, error = CHIP_ERROR_INVALID_ARGUMENT);
nid = _nidForCurve(MapECName(mPublicKey.Type()));
VerifyOrExit(nid != NID_undef, error = CHIP_ERROR_INVALID_ARGUMENT);

ec_key = to_EC_KEY(&mKeypair);
VerifyOrExit(ec_key != nullptr, error = CHIP_ERROR_INTERNAL);

result = ECDSA_sign(0, hash, static_cast<int>(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
{
Expand Down Expand Up @@ -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<int>(hash_length), Uint8::to_const_uchar(signature),
static_cast<int>(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)
{
Expand Down
64 changes: 64 additions & 0 deletions src/crypto/CHIPCryptoPALmbedTLS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 1922688

Please sign in to comment.