[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:
Abtin Keshavarzian
2020-12-17 15:13:31 -08:00
committed by GitHub
parent 572388d30e
commit c00cb8a4c4
11 changed files with 665 additions and 44 deletions
+23 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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
+23 -4
View File
@@ -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;
}
+1 -6
View File
@@ -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
*
+23
View File
@@ -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
+4
View File
@@ -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
+195
View File
@@ -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;
}
+1
View File
@@ -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 \
+2
View File
@@ -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. */