diff --git a/Android.mk b/Android.mk index 69a14d981..04ee0562a 100644 --- a/Android.mk +++ b/Android.mk @@ -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) diff --git a/src/core/crypto/ecdsa.cpp b/src/core/crypto/ecdsa.cpp index 3b50ee0f4..f9a63240a 100644 --- a/src/core/crypto/ecdsa.cpp +++ b/src/core/crypto/ecdsa.cpp @@ -33,6 +33,8 @@ #include "ecdsa.hpp" +#include + #include #include #include @@ -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(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(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 diff --git a/src/core/crypto/ecdsa.hpp b/src/core/crypto/ecdsa.hpp index 2215ab05d..1264bf64f 100644 --- a/src/core/crypto/ecdsa.hpp +++ b/src/core/crypto/ecdsa.hpp @@ -41,8 +41,13 @@ #include +#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 diff --git a/src/core/crypto/mbedtls.cpp b/src/core/crypto/mbedtls.cpp index 58b183c4e..a5ba8c3e3 100644 --- a/src/core/crypto/mbedtls.cpp +++ b/src/core/crypto/mbedtls.cpp @@ -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; } diff --git a/src/core/crypto/sha256.hpp b/src/core/crypto/sha256.hpp index 2534d007a..326f43553 100644 --- a/src/core/crypto/sha256.hpp +++ b/src/core/crypto/sha256.hpp @@ -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; } }; /** diff --git a/tests/toranj/openthread-core-toranj-config.h b/tests/toranj/openthread-core-toranj-config.h index 12743b0e6..d0534a330 100644 --- a/tests/toranj/openthread-core-toranj-config.h +++ b/tests/toranj/openthread-core-toranj-config.h @@ -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 * diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 307f98e47..cbc56b646 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -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 diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index aff58bf78..6ab946117 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -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 diff --git a/tests/unit/test_ecdsa.cpp b/tests/unit/test_ecdsa.cpp new file mode 100644 index 000000000..f8a3847ee --- /dev/null +++ b/tests/unit/test_ecdsa.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 + +#include "test_platform.h" +#include "test_util.hpp" + +#include "common/debug.hpp" +#include "common/random.hpp" +#include "crypto/ecdsa.hpp" + +#include +#include +#include + +#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(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(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; +} diff --git a/third_party/mbedtls/Makefile.am b/third_party/mbedtls/Makefile.am index fa4509009..0ec7f74e0 100644 --- a/third_party/mbedtls/Makefile.am +++ b/third_party/mbedtls/Makefile.am @@ -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 \ diff --git a/third_party/mbedtls/mbedtls-config.h b/third_party/mbedtls/mbedtls-config.h index 6de6cfd5d..e318e8f7f 100644 --- a/third_party/mbedtls/mbedtls-config.h +++ b/third_party/mbedtls/mbedtls-config.h @@ -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. */