mirror of
https://github.com/espressif/openthread.git
synced 2026-08-06 18:57:47 +00:00
[crypto] add ECDSA (P-256 curve) key-gen, signing, and verification (#5954)
This commit adds support for ECDSA using NIST P-256 curve and SHA-256 hash. `KeyPair` type is added which represents a set of public and private keys. It provides methods for generation a new key-pair and getting a DER format byte sequence representation of the key-pair. This commit also adds methods to calculate ECDSA signature and verify a signature for a hashed message. The underlying implementation uses `mbedtls` APIs. It also adds a unit-test `test-ecdsa` to cover behavior of the newly added types and methods. The unit test also verifies the signature calculation using the test vector example from RFC 6979.
This commit is contained in:
+23
-8
@@ -320,12 +320,10 @@ LOCAL_SRC_FILES := \
|
||||
src/posix/platform/system.cpp \
|
||||
src/posix/platform/uart.cpp \
|
||||
src/posix/platform/udp.cpp \
|
||||
third_party/mbedtls/repo/library/md.c \
|
||||
third_party/mbedtls/repo/library/md_wrap.c \
|
||||
third_party/mbedtls/repo/library/memory_buffer_alloc.c \
|
||||
third_party/mbedtls/repo/library/platform.c \
|
||||
third_party/mbedtls/repo/library/platform_util.c \
|
||||
third_party/mbedtls/repo/library/sha256.c \
|
||||
third_party/mbedtls/repo/library/aes.c \
|
||||
third_party/mbedtls/repo/library/asn1parse.c \
|
||||
third_party/mbedtls/repo/library/asn1write.c \
|
||||
third_party/mbedtls/repo/library/base64.c \
|
||||
third_party/mbedtls/repo/library/bignum.c \
|
||||
third_party/mbedtls/repo/library/ccm.c \
|
||||
third_party/mbedtls/repo/library/cipher.c \
|
||||
@@ -333,18 +331,35 @@ LOCAL_SRC_FILES := \
|
||||
third_party/mbedtls/repo/library/cmac.c \
|
||||
third_party/mbedtls/repo/library/ctr_drbg.c \
|
||||
third_party/mbedtls/repo/library/debug.c \
|
||||
third_party/mbedtls/repo/library/ecdh.c \
|
||||
third_party/mbedtls/repo/library/ecdsa.c \
|
||||
third_party/mbedtls/repo/library/ecjpake.c \
|
||||
third_party/mbedtls/repo/library/ecp.c \
|
||||
third_party/mbedtls/repo/library/ecp_curves.c \
|
||||
third_party/mbedtls/repo/library/entropy.c \
|
||||
third_party/mbedtls/repo/library/entropy_poll.c \
|
||||
third_party/mbedtls/repo/library/hmac_drbg.c \
|
||||
third_party/mbedtls/repo/library/md.c \
|
||||
third_party/mbedtls/repo/library/md_wrap.c \
|
||||
third_party/mbedtls/repo/library/memory_buffer_alloc.c \
|
||||
third_party/mbedtls/repo/library/oid.c \
|
||||
third_party/mbedtls/repo/library/pem.c \
|
||||
third_party/mbedtls/repo/library/pk.c \
|
||||
third_party/mbedtls/repo/library/pk_wrap.c \
|
||||
third_party/mbedtls/repo/library/pkparse.c \
|
||||
third_party/mbedtls/repo/library/pkwrite.c \
|
||||
third_party/mbedtls/repo/library/platform.c \
|
||||
third_party/mbedtls/repo/library/platform_util.c \
|
||||
third_party/mbedtls/repo/library/sha256.c \
|
||||
third_party/mbedtls/repo/library/ssl_cookie.c \
|
||||
third_party/mbedtls/repo/library/ssl_ciphersuites.c \
|
||||
third_party/mbedtls/repo/library/ssl_cli.c \
|
||||
third_party/mbedtls/repo/library/ssl_srv.c \
|
||||
third_party/mbedtls/repo/library/ssl_ticket.c \
|
||||
third_party/mbedtls/repo/library/ssl_tls.c \
|
||||
third_party/mbedtls/repo/library/aes.c \
|
||||
third_party/mbedtls/repo/library/ecp.c \
|
||||
third_party/mbedtls/repo/library/threading.c \
|
||||
third_party/mbedtls/repo/library/x509.c \
|
||||
third_party/mbedtls/repo/library/x509_crt.c \
|
||||
$(OPENTHREAD_PROJECT_SRC_FILES) \
|
||||
$(NULL)
|
||||
|
||||
|
||||
+151
-6
@@ -33,6 +33,8 @@
|
||||
|
||||
#include "ecdsa.hpp"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <mbedtls/ctr_drbg.h>
|
||||
#include <mbedtls/ecdsa.h>
|
||||
#include <mbedtls/pk.h>
|
||||
@@ -40,18 +42,160 @@
|
||||
#include "common/code_utils.hpp"
|
||||
#include "common/debug.hpp"
|
||||
#include "common/random.hpp"
|
||||
#include "crypto/mbedtls.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace Crypto {
|
||||
namespace Ecdsa {
|
||||
|
||||
#if OPENTHREAD_CONFIG_ECDSA_ENABLE
|
||||
|
||||
otError Ecdsa::Sign(uint8_t * aOutput,
|
||||
uint16_t & aOutputLength,
|
||||
const uint8_t *aInputHash,
|
||||
uint16_t aInputHashLength,
|
||||
const uint8_t *aPrivateKey,
|
||||
uint16_t aPrivateKeyLength)
|
||||
otError P256::KeyPair::Generate(void)
|
||||
{
|
||||
mbedtls_pk_context pk;
|
||||
int ret;
|
||||
|
||||
mbedtls_pk_init(&pk);
|
||||
|
||||
ret = mbedtls_pk_setup(&pk, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
|
||||
VerifyOrExit(ret == 0);
|
||||
|
||||
ret = mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_SECP256R1, mbedtls_pk_ec(pk), mbedtls_ctr_drbg_random,
|
||||
Random::Crypto::MbedTlsContextGet());
|
||||
VerifyOrExit(ret == 0);
|
||||
|
||||
ret = mbedtls_pk_write_key_der(&pk, mDerBytes, sizeof(mDerBytes));
|
||||
VerifyOrExit(ret > 0);
|
||||
|
||||
mDerLength = static_cast<uint8_t>(ret);
|
||||
|
||||
memmove(mDerBytes, mDerBytes + sizeof(mDerBytes) - mDerLength, mDerLength);
|
||||
|
||||
exit:
|
||||
mbedtls_pk_free(&pk);
|
||||
|
||||
return (ret >= 0) ? OT_ERROR_NONE : MbedTls::MapError(ret);
|
||||
}
|
||||
|
||||
otError P256::KeyPair::Parse(void *aContext) const
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
mbedtls_pk_context *pk = reinterpret_cast<mbedtls_pk_context *>(aContext);
|
||||
|
||||
mbedtls_pk_init(pk);
|
||||
|
||||
VerifyOrExit(mbedtls_pk_setup(pk, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)) == 0, error = OT_ERROR_FAILED);
|
||||
VerifyOrExit(mbedtls_pk_parse_key(pk, mDerBytes, mDerLength, nullptr, 0) == 0, error = OT_ERROR_PARSE);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
otError P256::KeyPair::GetPublicKey(PublicKey &aPublicKey) const
|
||||
{
|
||||
otError error;
|
||||
mbedtls_pk_context pk;
|
||||
mbedtls_ecp_keypair *keyPair;
|
||||
int ret;
|
||||
size_t len;
|
||||
|
||||
SuccessOrExit(error = Parse(&pk));
|
||||
|
||||
keyPair = mbedtls_pk_ec(pk);
|
||||
|
||||
ret = mbedtls_ecp_point_write_binary(&keyPair->grp, &keyPair->Q, MBEDTLS_ECP_PF_UNCOMPRESSED, &len,
|
||||
aPublicKey.mData, sizeof(aPublicKey.mData));
|
||||
VerifyOrExit(ret == 0, error = MbedTls::MapError(ret));
|
||||
VerifyOrExit(len == sizeof(aPublicKey.mData), error = OT_ERROR_PARSE);
|
||||
|
||||
exit:
|
||||
mbedtls_pk_free(&pk);
|
||||
return error;
|
||||
}
|
||||
|
||||
otError P256::KeyPair::Sign(const Sha256::Hash &aHash, Signature &aSignature) const
|
||||
{
|
||||
otError error;
|
||||
mbedtls_pk_context pk;
|
||||
mbedtls_ecp_keypair * keypair;
|
||||
mbedtls_ecdsa_context ecdsa;
|
||||
mbedtls_mpi r;
|
||||
mbedtls_mpi s;
|
||||
int ret;
|
||||
|
||||
mbedtls_ecdsa_init(&ecdsa);
|
||||
mbedtls_mpi_init(&r);
|
||||
mbedtls_mpi_init(&s);
|
||||
|
||||
SuccessOrExit(error = Parse(&pk));
|
||||
|
||||
keypair = mbedtls_pk_ec(pk);
|
||||
|
||||
ret = mbedtls_ecdsa_from_keypair(&ecdsa, keypair);
|
||||
VerifyOrExit(ret == 0, error = MbedTls::MapError(ret));
|
||||
|
||||
ret =
|
||||
mbedtls_ecdsa_sign_det(&ecdsa.grp, &r, &s, &ecdsa.d, aHash.GetBytes(), Sha256::Hash::kSize, MBEDTLS_MD_SHA256);
|
||||
VerifyOrExit(ret == 0, error = MbedTls::MapError(ret));
|
||||
|
||||
OT_ASSERT(mbedtls_mpi_size(&r) == kMpiSize);
|
||||
|
||||
ret = mbedtls_mpi_write_binary(&r, aSignature.mShared.mMpis.mR, kMpiSize);
|
||||
VerifyOrExit(ret == 0, error = MbedTls::MapError(ret));
|
||||
|
||||
ret = mbedtls_mpi_write_binary(&s, aSignature.mShared.mMpis.mS, kMpiSize);
|
||||
VerifyOrExit(ret == 0, error = MbedTls::MapError(ret));
|
||||
|
||||
exit:
|
||||
mbedtls_pk_free(&pk);
|
||||
mbedtls_mpi_free(&s);
|
||||
mbedtls_mpi_free(&r);
|
||||
mbedtls_ecdsa_free(&ecdsa);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
otError P256::PublicKey::Verify(const Sha256::Hash &aHash, const Signature &aSignature) const
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
mbedtls_ecdsa_context ecdsa;
|
||||
mbedtls_mpi r;
|
||||
mbedtls_mpi s;
|
||||
int ret;
|
||||
|
||||
mbedtls_ecdsa_init(&ecdsa);
|
||||
mbedtls_mpi_init(&r);
|
||||
mbedtls_mpi_init(&s);
|
||||
|
||||
ret = mbedtls_ecp_group_load(&ecdsa.grp, MBEDTLS_ECP_DP_SECP256R1);
|
||||
VerifyOrExit(ret == 0, error = MbedTls::MapError(ret));
|
||||
|
||||
ret = mbedtls_ecp_point_read_binary(&ecdsa.grp, &ecdsa.Q, mData, sizeof(mData));
|
||||
VerifyOrExit(ret == 0, error = MbedTls::MapError(ret));
|
||||
|
||||
ret = mbedtls_mpi_read_binary(&r, aSignature.mShared.mMpis.mR, kMpiSize);
|
||||
VerifyOrExit(ret == 0, error = MbedTls::MapError(ret));
|
||||
|
||||
ret = mbedtls_mpi_read_binary(&s, aSignature.mShared.mMpis.mS, kMpiSize);
|
||||
VerifyOrExit(ret == 0, error = MbedTls::MapError(ret));
|
||||
|
||||
ret = mbedtls_ecdsa_verify(&ecdsa.grp, aHash.GetBytes(), Sha256::Hash::kSize, &ecdsa.Q, &r, &s);
|
||||
VerifyOrExit(ret == 0, error = OT_ERROR_SECURITY);
|
||||
|
||||
exit:
|
||||
mbedtls_mpi_free(&s);
|
||||
mbedtls_mpi_free(&r);
|
||||
mbedtls_ecdsa_free(&ecdsa);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
otError Sign(uint8_t * aOutput,
|
||||
uint16_t & aOutputLength,
|
||||
const uint8_t *aInputHash,
|
||||
uint16_t aInputHashLength,
|
||||
const uint8_t *aPrivateKey,
|
||||
uint16_t aPrivateKeyLength)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
mbedtls_ecdsa_context ctx;
|
||||
@@ -100,5 +244,6 @@ exit:
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_ECDSA_ENABLE
|
||||
|
||||
} // namespace Ecdsa
|
||||
} // namespace Crypto
|
||||
} // namespace ot
|
||||
|
||||
+234
-20
@@ -41,8 +41,13 @@
|
||||
|
||||
#include <openthread/error.h>
|
||||
|
||||
#include "crypto/sha256.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace Crypto {
|
||||
namespace Ecdsa {
|
||||
|
||||
#if OPENTHREAD_CONFIG_ECDSA_ENABLE
|
||||
|
||||
/**
|
||||
* @addtogroup core-security
|
||||
@@ -52,41 +57,250 @@ namespace Crypto {
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class implements ECDSA signing.
|
||||
* This class implements ECDSA key-generation, signing, and verification for NIST P-256 curve using SHA-256 hash.
|
||||
*
|
||||
* NIST P-256 curve is also known as 256-bit Random ECP Group (RFC 5114 - 2.6), or secp256r1 (RFC 4492 - Appendix A).
|
||||
*
|
||||
*/
|
||||
class Ecdsa
|
||||
class P256
|
||||
{
|
||||
public:
|
||||
enum : uint16_t
|
||||
{
|
||||
kFieldBitLength = 256, ///< Prime field bit length used by the P-256 curve.
|
||||
};
|
||||
|
||||
enum : uint8_t
|
||||
{
|
||||
kMpiSize = kFieldBitLength / 8, ///< Max bytes in binary representation of an MPI (multi-precision int).
|
||||
};
|
||||
|
||||
class PublicKey;
|
||||
class KeyPair;
|
||||
|
||||
/**
|
||||
* This method creates ECDSA sign.
|
||||
* This class represents an ECDSA signature.
|
||||
*
|
||||
* @param[out] aOutput An output buffer where ECDSA sign should be stored.
|
||||
* @param[inout] aOutputLength The length of the @p aOutput buffer.
|
||||
* @param[in] aInputHash An input hash.
|
||||
* @param[in] aInputHashLength The length of the @p aInputHash buffer.
|
||||
* @param[in] aPrivateKey A private key in PEM format.
|
||||
* @param[in] aPrivateKeyLength The length of the @p aPrivateKey buffer.
|
||||
*
|
||||
* @retval OT_ERROR_NONE ECDSA sign has been created successfully.
|
||||
* @retval OT_ERROR_NO_BUFS Output buffer is too small.
|
||||
* @retval OT_ERROR_INVALID_ARGS Private key is not valid EC Private Key.
|
||||
* @retval OT_ERROR_FAILED Error during signing.
|
||||
* The signature is encoded as the concatenated binary representation of two MPIs `r` and `s` which are calculated
|
||||
* during signing (RFC 6605 - section 4).
|
||||
*
|
||||
*/
|
||||
static otError Sign(uint8_t * aOutput,
|
||||
uint16_t & aOutputLength,
|
||||
const uint8_t *aInputHash,
|
||||
uint16_t aInputHashLength,
|
||||
const uint8_t *aPrivateKey,
|
||||
uint16_t aPrivateKeyLength);
|
||||
OT_TOOL_PACKED_BEGIN
|
||||
class Signature
|
||||
{
|
||||
friend class KeyPair;
|
||||
friend class PublicKey;
|
||||
|
||||
public:
|
||||
enum : uint8_t
|
||||
{
|
||||
kSize = 2 * kMpiSize, ///< Size of the signature in bytes (two times the curve MPI size).
|
||||
};
|
||||
|
||||
/**
|
||||
* This method returns the signature as a byte array.
|
||||
*
|
||||
* @returns A pointer to the byte array containing the signature.
|
||||
*
|
||||
*/
|
||||
const uint8_t *GetBytes(void) const { return mShared.mKey; }
|
||||
|
||||
private:
|
||||
OT_TOOL_PACKED_BEGIN
|
||||
struct Mpis
|
||||
{
|
||||
uint8_t mR[kMpiSize];
|
||||
uint8_t mS[kMpiSize];
|
||||
} OT_TOOL_PACKED_END;
|
||||
|
||||
union OT_TOOL_PACKED_FIELD
|
||||
{
|
||||
Mpis mMpis;
|
||||
uint8_t mKey[kSize];
|
||||
} mShared;
|
||||
} OT_TOOL_PACKED_END;
|
||||
|
||||
/**
|
||||
* This class represents a key pair (public and private keys).
|
||||
*
|
||||
* The key pair is stored using Distinguished Encoding Rules (DER) format (per RFC 5915).
|
||||
*
|
||||
*/
|
||||
class KeyPair
|
||||
{
|
||||
public:
|
||||
enum : uint8_t
|
||||
{
|
||||
kMaxDerSize = 125, ///< Max buffer size (in bytes) for representing the key-pair in DER format.
|
||||
};
|
||||
|
||||
/**
|
||||
* This constructor initializes a `KeyPair` as empty (no key).
|
||||
*
|
||||
*/
|
||||
KeyPair(void)
|
||||
: mDerLength(0)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* This method generates and populates the `KeyPair` with a new public/private keys.
|
||||
*
|
||||
* @retval OT_ERROR_NONE A new key pair was generated successfully.
|
||||
* @retval OT_ERROR_NO_BUFS Failed to allocate buffer for key generation.
|
||||
* @retval OT_ERROR_NOT_CAPABLE Feature not supported.
|
||||
* @retval OT_ERROR_FAILED Failed to generate key.
|
||||
*
|
||||
*/
|
||||
otError Generate(void);
|
||||
|
||||
/**
|
||||
* This method gets the associated public key from the `KeyPair`.
|
||||
*
|
||||
* @param[out] aPublicKey A reference to a `PublicKey` to output the value.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Public key was retrieved successfully, and @p aPublicKey is updated.
|
||||
* @retval OT_ERROR_PARSE The key-pair DER format could not be parsed (invalid format).
|
||||
*
|
||||
*/
|
||||
otError GetPublicKey(PublicKey &aPublicKey) const;
|
||||
|
||||
/**
|
||||
* This method gets the pointer to start of the buffer containing the key-pair info in DER format.
|
||||
*
|
||||
* The length (number of bytes) of DER format is given by `GetDerLength()`.
|
||||
*
|
||||
* @returns The pointer to the start of buffer containing the key-pair in DER format.
|
||||
*
|
||||
*/
|
||||
const uint8_t *GetDerBytes(void) const { return mDerBytes; }
|
||||
|
||||
/**
|
||||
* This method gets the length of the byte sequence representation of the key-pair in DER format.
|
||||
*
|
||||
* @returns The length of byte sequence representation of the key-pair in DER format.
|
||||
*
|
||||
*/
|
||||
uint8_t GetDerLength(void) const { return mDerLength; }
|
||||
|
||||
/**
|
||||
* This method gets the pointer to start of the key-pair buffer in DER format.
|
||||
*
|
||||
* This method gives non-const pointer to the buffer and is intended for populating the buffer and setting
|
||||
* the key-pair (e.g., reading the key-pair from non-volatile settings). The buffer contains `kMaxDerSize`
|
||||
* bytes. After populating the buffer, `SetDerLength()` can be used to set the the number of bytes written.
|
||||
*
|
||||
* @returns The pointer to the start of key-pair buffer in DER format.
|
||||
*
|
||||
*/
|
||||
uint8_t *GetDerBytes(void) { return mDerBytes; }
|
||||
|
||||
/**
|
||||
* This method sets the length of the byte sequence representation of the key-pair in DER format.
|
||||
*
|
||||
* @param[in] aDerLength The length (number of bytes).
|
||||
*
|
||||
*/
|
||||
void SetDerLength(uint8_t aDerLength) { mDerLength = aDerLength; }
|
||||
|
||||
/**
|
||||
* This method calculates the ECDSA signature for a hashed message using the private key from `KeyPair`.
|
||||
*
|
||||
* This method uses the deterministic digital signature generation procedure from RFC 6979.
|
||||
*
|
||||
* @param[in] aHash The SHA-256 hash value of the message to use for signature calculation.
|
||||
* @param[out] aSignature A reference to a `Signature` to output the calculated signature value.
|
||||
*
|
||||
* @retval OT_ERROR_NONE The signature was calculated successfully and @p aSignature was updated.
|
||||
* @retval OT_ERROR_PARSE The key-pair DER format could not be parsed (invalid format).
|
||||
* @retval OT_ERROR_INVALID_ARGS The @p aHash is invalid.
|
||||
* @retval OT_ERROR_NO_BUFS Failed to allocate buffer for signature calculation.
|
||||
*
|
||||
*/
|
||||
otError Sign(const Sha256::Hash &aHash, Signature &aSignature) const;
|
||||
|
||||
private:
|
||||
otError Parse(void *aContext) const;
|
||||
|
||||
uint8_t mDerBytes[kMaxDerSize];
|
||||
uint8_t mDerLength;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class represents a public key.
|
||||
*
|
||||
* The public key is stored as a byte sequence representation of an uncompressed curve point (RFC 6605 - sec 4).
|
||||
*
|
||||
*/
|
||||
OT_TOOL_PACKED_BEGIN
|
||||
class PublicKey
|
||||
{
|
||||
friend class KeyPair;
|
||||
|
||||
public:
|
||||
enum
|
||||
{
|
||||
kSize = (kMpiSize * 2) + 1, ///< Size of the public key in bytes (two MPIs + one overhead byte).
|
||||
};
|
||||
|
||||
/**
|
||||
* This method gets the pointer to the buffer containing the public key (as an uncompressed curve point).
|
||||
*
|
||||
* @return The pointer to the buffer containing the public key (with `kSize` bytes).
|
||||
*
|
||||
*/
|
||||
const uint8_t *GetBytes(void) const { return mData; }
|
||||
|
||||
/**
|
||||
* This method uses the `PublicKey` to verify the ECDSA signature of a hashed message.
|
||||
*
|
||||
* @param[in] aHash The SHA-256 hash value of a message to use for signature verification.
|
||||
* @param[in] aSignature The signature value to verify.
|
||||
*
|
||||
* @retval OT_ERROR_NONE The signature was verified successfully.
|
||||
* @retval OT_ERROR_SECURITY The signature is invalid.
|
||||
* @retval OT_ERROR_INVALID_ARGS The key or has is invalid.
|
||||
* @retval OT_ERROR_NO_BUFS Failed to allocate buffer for signature verification
|
||||
*
|
||||
*/
|
||||
otError Verify(const Sha256::Hash &aHash, const Signature &aSignature) const;
|
||||
|
||||
private:
|
||||
uint8_t mData[kSize];
|
||||
} OT_TOOL_PACKED_END;
|
||||
};
|
||||
|
||||
/**
|
||||
* This function creates an ECDSA signature.
|
||||
*
|
||||
* @param[out] aOutput An output buffer where ECDSA sign should be stored.
|
||||
* @param[inout] aOutputLength The length of the @p aOutput buffer.
|
||||
* @param[in] aInputHash An input hash.
|
||||
* @param[in] aInputHashLength The length of the @p aInputHash buffer.
|
||||
* @param[in] aPrivateKey A private key in PEM format.
|
||||
* @param[in] aPrivateKeyLength The length of the @p aPrivateKey buffer.
|
||||
*
|
||||
* @retval OT_ERROR_NONE ECDSA sign has been created successfully.
|
||||
* @retval OT_ERROR_NO_BUFS Output buffer is too small.
|
||||
* @retval OT_ERROR_INVALID_ARGS Private key is not valid EC Private Key.
|
||||
* @retval OT_ERROR_FAILED Error during signing.
|
||||
*
|
||||
*/
|
||||
otError Sign(uint8_t * aOutput,
|
||||
uint16_t & aOutputLength,
|
||||
const uint8_t *aInputHash,
|
||||
uint16_t aInputHashLength,
|
||||
const uint8_t *aPrivateKey,
|
||||
uint16_t aPrivateKeyLength);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*
|
||||
*/
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_ECDSA_ENABLE
|
||||
|
||||
} // namespace Ecdsa
|
||||
} // namespace Crypto
|
||||
} // namespace ot
|
||||
|
||||
|
||||
@@ -80,6 +80,11 @@ otError MbedTls::MapError(int aMbedTlsError)
|
||||
|
||||
switch (aMbedTlsError)
|
||||
{
|
||||
#if OPENTHREAD_CONFIG_ECDSA_ENABLE
|
||||
case MBEDTLS_ERR_ECP_BAD_INPUT_DATA:
|
||||
case MBEDTLS_ERR_MPI_BAD_INPUT_DATA:
|
||||
case MBEDTLS_ERR_MPI_INVALID_CHARACTER:
|
||||
#endif
|
||||
#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
|
||||
case MBEDTLS_ERR_PK_TYPE_MISMATCH:
|
||||
case MBEDTLS_ERR_PK_FILE_IO_ERROR:
|
||||
@@ -114,12 +119,17 @@ otError MbedTls::MapError(int aMbedTlsError)
|
||||
error = OT_ERROR_INVALID_ARGS;
|
||||
break;
|
||||
|
||||
#if OPENTHREAD_CONFIG_ECDSA_ENABLE
|
||||
case MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL:
|
||||
case MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL:
|
||||
case MBEDTLS_ERR_MPI_ALLOC_FAILED:
|
||||
#endif
|
||||
#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
|
||||
case MBEDTLS_ERR_PEM_ALLOC_FAILED:
|
||||
case MBEDTLS_ERR_PK_ALLOC_FAILED:
|
||||
case MBEDTLS_ERR_X509_BUFFER_TOO_SMALL:
|
||||
case MBEDTLS_ERR_X509_ALLOC_FAILED:
|
||||
#endif // MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
|
||||
#endif
|
||||
case MBEDTLS_ERR_SSL_ALLOC_FAILED:
|
||||
case MBEDTLS_ERR_SSL_WANT_WRITE:
|
||||
case MBEDTLS_ERR_ENTROPY_MAX_SOURCES:
|
||||
@@ -146,15 +156,24 @@ otError MbedTls::MapError(int aMbedTlsError)
|
||||
case MBEDTLS_ERR_X509_FATAL_ERROR:
|
||||
error = OT_ERROR_FAILED;
|
||||
break;
|
||||
#endif // MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
|
||||
|
||||
#endif
|
||||
case MBEDTLS_ERR_SSL_TIMEOUT:
|
||||
case MBEDTLS_ERR_SSL_WANT_READ:
|
||||
error = OT_ERROR_BUSY;
|
||||
break;
|
||||
|
||||
#if OPENTHREAD_CONFIG_ECDSA_ENABLE
|
||||
case MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE:
|
||||
error = OT_ERROR_NOT_CAPABLE;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
OT_ASSERT(aMbedTlsError >= 0);
|
||||
if (aMbedTlsError < 0)
|
||||
{
|
||||
error = OT_ERROR_FAILED;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -80,12 +80,7 @@ public:
|
||||
* @returns A pointer to a byte array containing the hash.
|
||||
*
|
||||
*/
|
||||
const uint8_t *GetBytes(void) { return m8; }
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
kHashSize = 32, ///< SHA-256 hash size (bytes)
|
||||
const uint8_t *GetBytes(void) const { return m8; }
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -111,6 +111,14 @@
|
||||
*/
|
||||
#define OPENTHREAD_CONFIG_LEGACY_ENABLE 1
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_ECDSA_ENABLE
|
||||
*
|
||||
* Define to 1 to enable ECDSA support.
|
||||
*
|
||||
*/
|
||||
#define OPENTHREAD_CONFIG_ECDSA_ENABLE 1
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE
|
||||
*
|
||||
|
||||
@@ -194,6 +194,28 @@ target_link_libraries(test-dns
|
||||
|
||||
add_test(NAME test-dns COMMAND test-dns)
|
||||
|
||||
add_executable(test-ecdsa
|
||||
test_ecdsa.cpp
|
||||
)
|
||||
|
||||
target_include_directories(test-ecdsa
|
||||
PRIVATE
|
||||
${COMMON_INCLUDES}
|
||||
)
|
||||
|
||||
target_compile_options(test-ecdsa
|
||||
PRIVATE
|
||||
${COMMON_COMPILE_OPTIONS}
|
||||
)
|
||||
|
||||
target_link_libraries(test-ecdsa
|
||||
PRIVATE
|
||||
${COMMON_LIBS}
|
||||
)
|
||||
|
||||
add_test(NAME test-ecdsa COMMAND test-ecdsa)
|
||||
|
||||
|
||||
add_executable(test-flash
|
||||
test_flash.cpp
|
||||
)
|
||||
@@ -693,6 +715,7 @@ set_target_properties(
|
||||
test-child-table
|
||||
test-cmd-line-parser
|
||||
test-dns
|
||||
test-ecdsa
|
||||
test-flash
|
||||
test-heap
|
||||
test-hkdf-sha256
|
||||
|
||||
@@ -112,6 +112,7 @@ check_PROGRAMS += \
|
||||
test-child-table \
|
||||
test-cmd-line-parser \
|
||||
test-dns \
|
||||
test-ecdsa \
|
||||
test-flash \
|
||||
test-heap \
|
||||
test-hkdf-sha256 \
|
||||
@@ -193,6 +194,9 @@ test_cmd_line_parser_SOURCES = $(COMMON_SOURCES) test_cmd_line_parser.cpp
|
||||
test_dns_LDADD = $(COMMON_LDADD)
|
||||
test_dns_SOURCES = $(COMMON_SOURCES) test_dns.cpp
|
||||
|
||||
test_ecdsa_LDADD = $(COMMON_LDADD)
|
||||
test_ecdsa_SOURCES = $(COMMON_SOURCES) test_ecdsa.cpp
|
||||
|
||||
test_flash_LDADD = $(COMMON_LDADD)
|
||||
test_flash_SOURCES = $(COMMON_SOURCES) test_flash.cpp
|
||||
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright (c) 2020, The OpenThread Authors.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <openthread/config.h>
|
||||
|
||||
#include "test_platform.h"
|
||||
#include "test_util.hpp"
|
||||
|
||||
#include "common/debug.hpp"
|
||||
#include "common/random.hpp"
|
||||
#include "crypto/ecdsa.hpp"
|
||||
|
||||
#include <mbedtls/ctr_drbg.h>
|
||||
#include <mbedtls/ecdsa.h>
|
||||
#include <mbedtls/pk.h>
|
||||
|
||||
#if OPENTHREAD_CONFIG_ECDSA_ENABLE
|
||||
|
||||
namespace ot {
|
||||
namespace Crypto {
|
||||
|
||||
void TestEcdsaVector(void)
|
||||
{
|
||||
// This case is derived from the test vector from RFC 6979 - section A.2.5 (for NIST P-256 and SHA-256 hash).
|
||||
|
||||
const uint8_t kKeyPairInfo[] = {
|
||||
0x30, 0x78, 0x02, 0x01, 0x01, 0x04, 0x21, 0x00, 0xC9, 0xAF, 0xA9, 0xD8, 0x45, 0xBA, 0x75, 0x16, 0x6B, 0x5C,
|
||||
0x21, 0x57, 0x67, 0xB1, 0xD6, 0x93, 0x4E, 0x50, 0xC3, 0xDB, 0x36, 0xE8, 0x9B, 0x12, 0x7B, 0x8A, 0x62, 0x2B,
|
||||
0x12, 0x0F, 0x67, 0x21, 0xA0, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0xA1, 0x44,
|
||||
0x03, 0x42, 0x00, 0x04, 0x60, 0xFE, 0xD4, 0xBA, 0x25, 0x5A, 0x9D, 0x31, 0xC9, 0x61, 0xEB, 0x74, 0xC6, 0x35,
|
||||
0x6D, 0x68, 0xC0, 0x49, 0xB8, 0x92, 0x3B, 0x61, 0xFA, 0x6C, 0xE6, 0x69, 0x62, 0x2E, 0x60, 0xF2, 0x9F, 0xB6,
|
||||
0x79, 0x03, 0xFE, 0x10, 0x08, 0xB8, 0xBC, 0x99, 0xA4, 0x1A, 0xE9, 0xE9, 0x56, 0x28, 0xBC, 0x64, 0xF2, 0xF1,
|
||||
0xB2, 0x0C, 0x2D, 0x7E, 0x9F, 0x51, 0x77, 0xA3, 0xC2, 0x94, 0xD4, 0x46, 0x22, 0x99};
|
||||
|
||||
const uint8_t kPublicKey[] = {
|
||||
0x04, 0x60, 0xFE, 0xD4, 0xBA, 0x25, 0x5A, 0x9D, 0x31, 0xC9, 0x61, 0xEB, 0x74, 0xC6, 0x35, 0x6D, 0x68,
|
||||
0xC0, 0x49, 0xB8, 0x92, 0x3B, 0x61, 0xFA, 0x6C, 0xE6, 0x69, 0x62, 0x2E, 0x60, 0xF2, 0x9F, 0xB6, 0x79,
|
||||
0x03, 0xFE, 0x10, 0x08, 0xB8, 0xBC, 0x99, 0xA4, 0x1A, 0xE9, 0xE9, 0x56, 0x28, 0xBC, 0x64, 0xF2, 0xF1,
|
||||
0xB2, 0x0C, 0x2D, 0x7E, 0x9F, 0x51, 0x77, 0xA3, 0xC2, 0x94, 0xD4, 0x46, 0x22, 0x99,
|
||||
};
|
||||
|
||||
const uint8_t kMessage[] = {'s', 'a', 'm', 'p', 'l', 'e'};
|
||||
|
||||
const uint8_t kExpectedSignature[] = {
|
||||
0xEF, 0xD4, 0x8B, 0x2A, 0xAC, 0xB6, 0xA8, 0xFD, 0x11, 0x40, 0xDD, 0x9C, 0xD4, 0x5E, 0x81, 0xD6,
|
||||
0x9D, 0x2C, 0x87, 0x7B, 0x56, 0xAA, 0xF9, 0x91, 0xC3, 0x4D, 0x0E, 0xA8, 0x4E, 0xAF, 0x37, 0x16,
|
||||
0xF7, 0xCB, 0x1C, 0x94, 0x2D, 0x65, 0x7C, 0x41, 0xD4, 0x36, 0xC7, 0xA1, 0xB6, 0xE2, 0x9F, 0x65,
|
||||
0xF3, 0xE9, 0x00, 0xDB, 0xB9, 0xAF, 0xF4, 0x06, 0x4D, 0xC4, 0xAB, 0x2F, 0x84, 0x3A, 0xCD, 0xA8,
|
||||
};
|
||||
|
||||
Instance *instance = testInitInstance();
|
||||
|
||||
Ecdsa::P256::KeyPair keyPair;
|
||||
Ecdsa::P256::PublicKey publicKey;
|
||||
Ecdsa::P256::Signature signature;
|
||||
Sha256 sha256;
|
||||
Sha256::Hash hash;
|
||||
|
||||
VerifyOrQuit(instance != nullptr, "Null OpenThread instance");
|
||||
|
||||
printf("\n===========================================================================\n");
|
||||
printf("Test ECDA with test vector from RFC 6979 (A.2.5)\n");
|
||||
|
||||
printf("\nLoading key-pair ----------------------------------------------------------\n");
|
||||
memcpy(keyPair.GetDerBytes(), kKeyPairInfo, sizeof(kKeyPairInfo));
|
||||
keyPair.SetDerLength(sizeof(kKeyPairInfo));
|
||||
|
||||
DumpBuffer("KeyPair", keyPair.GetDerBytes(), keyPair.GetDerLength());
|
||||
|
||||
SuccessOrQuit(keyPair.GetPublicKey(publicKey), "KeyPair::GetPublicKey() failed");
|
||||
DumpBuffer("PublicKey", publicKey.GetBytes(), Ecdsa::P256::PublicKey::kSize);
|
||||
|
||||
VerifyOrQuit(sizeof(kPublicKey) == Ecdsa::P256::PublicKey::kSize, "Example public key is invalid");
|
||||
VerifyOrQuit(memcmp(publicKey.GetBytes(), kPublicKey, sizeof(kPublicKey)) == 0,
|
||||
"KeyPair::GetPublicKey() did not return the expected key");
|
||||
|
||||
printf("\nHash the message ----------------------------------------------------------\n");
|
||||
sha256.Start();
|
||||
sha256.Update(kMessage, sizeof(kMessage));
|
||||
sha256.Finish(hash);
|
||||
|
||||
DumpBuffer("Hash", hash.GetBytes(), sizeof(hash));
|
||||
|
||||
printf("\nSign the message ----------------------------------------------------------\n");
|
||||
SuccessOrQuit(keyPair.Sign(hash, signature), "KeyPair::Sign() failed");
|
||||
DumpBuffer("Signature", signature.GetBytes(), sizeof(signature));
|
||||
|
||||
printf("\nCheck signature against expected sequence----------------------------------\n");
|
||||
DumpBuffer("Expected signature", kExpectedSignature, sizeof(kExpectedSignature));
|
||||
|
||||
VerifyOrQuit(sizeof(kExpectedSignature) == sizeof(signature), "Signature length does not match expected size");
|
||||
VerifyOrQuit(memcmp(signature.GetBytes(), kExpectedSignature, sizeof(kExpectedSignature)) == 0,
|
||||
"Signature does not match expected value");
|
||||
|
||||
printf("Signature matches expected sequence.\n");
|
||||
|
||||
printf("\nVerify the signature ------------------------------------------------------\n");
|
||||
SuccessOrQuit(publicKey.Verify(hash, signature), "PublicKey::Verify() failed");
|
||||
printf("\nSignature was verified successfully.\n\n");
|
||||
|
||||
testFreeInstance(instance);
|
||||
}
|
||||
|
||||
void TestEdsaKeyGenerationSignAndVerify(void)
|
||||
{
|
||||
Instance *instance = testInitInstance();
|
||||
|
||||
const char *kMessage = "You are not a drop in the ocean. You are the entire ocean in a drop.";
|
||||
|
||||
Ecdsa::P256::KeyPair keyPair;
|
||||
Ecdsa::P256::PublicKey publicKey;
|
||||
Ecdsa::P256::Signature signature;
|
||||
Sha256 sha256;
|
||||
Sha256::Hash hash;
|
||||
|
||||
VerifyOrQuit(instance != nullptr, "Null OpenThread instance");
|
||||
|
||||
printf("\n===========================================================================\n");
|
||||
printf("Test ECDA key generation, signing and verification\n");
|
||||
|
||||
printf("\nGenerating key-pair -------------------------------------------------------\n");
|
||||
SuccessOrQuit(keyPair.Generate(), "KeyPair::Generate() failed");
|
||||
|
||||
DumpBuffer("KeyPair", keyPair.GetDerBytes(), keyPair.GetDerLength());
|
||||
|
||||
SuccessOrQuit(keyPair.GetPublicKey(publicKey), "KeyPair::GetPublicKey() failed");
|
||||
DumpBuffer("PublicKey", publicKey.GetBytes(), Ecdsa::P256::PublicKey::kSize);
|
||||
|
||||
printf("\nHash the message ----------------------------------------------------------\n");
|
||||
sha256.Start();
|
||||
sha256.Update(reinterpret_cast<const uint8_t *>(kMessage), sizeof(kMessage) - 1);
|
||||
sha256.Finish(hash);
|
||||
|
||||
DumpBuffer("Hash", hash.GetBytes(), sizeof(hash));
|
||||
|
||||
printf("\nSign the message ----------------------------------------------------------\n");
|
||||
SuccessOrQuit(keyPair.Sign(hash, signature), "KeyPair::Sign() failed");
|
||||
DumpBuffer("Signature", signature.GetBytes(), sizeof(signature));
|
||||
|
||||
printf("\nVerify the signature ------------------------------------------------------\n");
|
||||
SuccessOrQuit(publicKey.Verify(hash, signature), "PublicKey::Verify() failed");
|
||||
printf("\nSignature was verified successfully.");
|
||||
|
||||
sha256.Start();
|
||||
sha256.Update(reinterpret_cast<const uint8_t *>(kMessage), sizeof(kMessage)); // include null char
|
||||
sha256.Finish(hash);
|
||||
VerifyOrQuit(publicKey.Verify(hash, signature) != OT_ERROR_NONE,
|
||||
"PublicKey::Verify() passed for invalid signature");
|
||||
printf("\nSignature verification correctly failed with incorrect hash/signature.\n\n");
|
||||
|
||||
testFreeInstance(instance);
|
||||
}
|
||||
|
||||
} // namespace Crypto
|
||||
} // namespace ot
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_ECDSA_ENABLE
|
||||
|
||||
int main(void)
|
||||
{
|
||||
#if OPENTHREAD_CONFIG_ECDSA_ENABLE
|
||||
ot::Crypto::TestEcdsaVector();
|
||||
ot::Crypto::TestEdsaKeyGenerationSignAndVerify();
|
||||
printf("All tests passed\n");
|
||||
#else
|
||||
printf("ECDSA feature is not enabled\n");
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
Vendored
+1
@@ -66,6 +66,7 @@ libmbedcrypto_a_SOURCES = \
|
||||
repo/library/ecp_curves.c \
|
||||
repo/library/entropy.c \
|
||||
repo/library/entropy_poll.c \
|
||||
repo/library/hmac_drbg.c \
|
||||
repo/library/md.c \
|
||||
repo/library/md_wrap.c \
|
||||
repo/library/memory_buffer_alloc.c \
|
||||
|
||||
Vendored
+2
@@ -104,8 +104,10 @@
|
||||
#define MBEDTLS_BASE64_C
|
||||
#define MBEDTLS_ECDH_C
|
||||
#define MBEDTLS_ECDSA_C
|
||||
#define MBEDTLS_ECDSA_DETERMINISTIC
|
||||
#define MBEDTLS_OID_C
|
||||
#define MBEDTLS_PEM_PARSE_C
|
||||
#define MBEDTLS_PK_WRITE_C
|
||||
#endif
|
||||
|
||||
#define MBEDTLS_MPI_WINDOW_SIZE 1 /**< Maximum windows size used. */
|
||||
|
||||
Reference in New Issue
Block a user