From 014514442d0e292daeda76a3d3e4fbcd91e7ecd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Duda?= Date: Tue, 30 Oct 2018 20:23:10 +0100 Subject: [PATCH] [crypto] add support for ECDSA signing (#3218) --- .travis/script.sh | 7 +- configure.ac | 34 ++++++++ examples/common-switches.mk | 4 + include/openthread/crypto.h | 24 ++++++ src/core/Makefile.am | 2 + src/core/api/crypto_api.cpp | 16 ++++ src/core/crypto/ecdsa.cpp | 112 +++++++++++++++++++++++++++ src/core/crypto/ecdsa.hpp | 95 +++++++++++++++++++++++ third_party/mbedtls/mbedtls-config.h | 10 ++- 9 files changed, 300 insertions(+), 4 deletions(-) create mode 100644 src/core/crypto/ecdsa.cpp create mode 100644 src/core/crypto/ecdsa.hpp diff --git a/.travis/script.sh b/.travis/script.sh index 8f24fab36..76b65a11f 100755 --- a/.travis/script.sh +++ b/.travis/script.sh @@ -62,6 +62,7 @@ python --version || die --enable-dhcp6-server \ --enable-diag \ --enable-dns-client \ + --enable-ecdsa \ --enable-ftd \ --enable-jam-detection \ --enable-joiner \ @@ -117,7 +118,7 @@ python --version || die git checkout -- . || die git clean -xfd || die ./bootstrap || die - BORDER_ROUTER=1 COAP=1 COMMISSIONER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 FULL_LOGS=1 JOINER=1 LINK_RAW=1 MAC_FILTER=1 MTD_NETDIAG=1 SERVICE=1 UDP_PROXY=1 make -f examples/Makefile-nrf52840 || die + BORDER_ROUTER=1 COAP=1 COMMISSIONER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 ECDSA=1 FULL_LOGS=1 JOINER=1 LINK_RAW=1 MAC_FILTER=1 MTD_NETDIAG=1 SERVICE=1 UDP_PROXY=1 make -f examples/Makefile-nrf52840 || die arm-none-eabi-size output/nrf52840/bin/ot-cli-ftd || die arm-none-eabi-size output/nrf52840/bin/ot-cli-mtd || die arm-none-eabi-size output/nrf52840/bin/ot-ncp-ftd || die @@ -195,7 +196,7 @@ python --version || die git checkout -- . || die git clean -xfd || die ./bootstrap || die - BORDER_ROUTER=1 COAP=1 COMMISSIONER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 FULL_LOGS=1 JOINER=1 MAC_FILTER=1 MTD_NETDIAG=1 SERVICE=1 UDP_PROXY=1 make -f examples/Makefile-nrf52840 || die + BORDER_ROUTER=1 COAP=1 COMMISSIONER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 ECDSA=1 FULL_LOGS=1 JOINER=1 MAC_FILTER=1 MTD_NETDIAG=1 SERVICE=1 UDP_PROXY=1 make -f examples/Makefile-nrf52840 || die arm-none-eabi-size output/nrf52840/bin/ot-cli-ftd || die arm-none-eabi-size output/nrf52840/bin/ot-cli-mtd || die arm-none-eabi-size output/nrf52840/bin/ot-ncp-ftd || die @@ -273,7 +274,7 @@ python --version || die git checkout -- . || die git clean -xfd || die ./bootstrap || die - BORDER_ROUTER=1 COAP=1 COMMISSIONER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 FULL_LOGS=1 JOINER=1 MAC_FILTER=1 MTD_NETDIAG=1 SERVICE=1 UDP_PROXY=1 make -f examples/Makefile-nrf52840 || die + BORDER_ROUTER=1 COAP=1 COMMISSIONER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 ECDSA=1 FULL_LOGS=1 JOINER=1 MAC_FILTER=1 MTD_NETDIAG=1 SERVICE=1 UDP_PROXY=1 make -f examples/Makefile-nrf52840 || die arm-none-eabi-size output/nrf52840/bin/ot-cli-ftd || die arm-none-eabi-size output/nrf52840/bin/ot-cli-mtd || die arm-none-eabi-size output/nrf52840/bin/ot-ncp-ftd || die diff --git a/configure.ac b/configure.ac index f796227d9..d58935180 100644 --- a/configure.ac +++ b/configure.ac @@ -1498,6 +1498,39 @@ AC_ARG_WITH( AC_SUBST(OPENTHREAD_CUSTOM_LINKER_FILE) AM_CONDITIONAL([OPENTHREAD_ENABLE_CUSTOM_LINKER_FILE], [test "${with_custom_linker_file}" = "yes"]) +# +# Support for ECDSA +# + +AC_MSG_CHECKING([whether to build with ECDSA support]) +AC_ARG_ENABLE(ecdsa, + [AS_HELP_STRING([--enable-ecdsa], [Enable ECDSA support @<:@default=no@:>@.])], + [ + case "${enableval}" in + + no|yes) + enable_ecdsa=${enableval} + ;; + + *) + AC_MSG_ERROR([Invalid value ${enable_ecdsa} for --enable-ecdsa]) + ;; + esac + ], + [enable_ecdsa=no]) + +if test "$enable_ecdsa" = "yes"; then + OPENTHREAD_ENABLE_ECDSA=1 +else + OPENTHREAD_ENABLE_ECDSA=0 +fi + +AC_MSG_RESULT(${enable_ecdsa}) +AC_SUBST(OPENTHREAD_ENABLE_ECDSA) +AM_CONDITIONAL([OPENTHREAD_ENABLE_ECDSA], [test "${enable_ecdsa}" = "yes"]) +AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_ECDSA], [${OPENTHREAD_ENABLE_ECDSA}], [Define to 1 if you want to enable ECDSA support]) + + # # Examples # @@ -1888,6 +1921,7 @@ AC_MSG_NOTICE([ OpenThread Border Agent support : ${enable_border_agent} OpenThread Border Router support : ${enable_border_router} OpenThread Service support : ${enable_service} + OpenThread ECDSA support : ${enable_ecdsa} OpenThread Examples : ${with_examples} OpenThread POSIX Application : ${enable_posix_app} diff --git a/examples/common-switches.mk b/examples/common-switches.mk index fa77c3521..90ad49e4e 100644 --- a/examples/common-switches.mk +++ b/examples/common-switches.mk @@ -87,6 +87,10 @@ ifeq ($(DNS_CLIENT),1) configure_OPTIONS += --enable-dns-client endif +ifeq ($(ECDSA),1) +configure_OPTIONS += --enable-ecdsa +endif + ifeq ($(JAM_DETECTION),1) configure_OPTIONS += --enable-jam-detection endif diff --git a/include/openthread/crypto.h b/include/openthread/crypto.h index 1690a0e02..5cbfc75c0 100644 --- a/include/openthread/crypto.h +++ b/include/openthread/crypto.h @@ -38,6 +38,8 @@ #include #include +#include + #ifdef __cplusplus extern "C" { #endif @@ -103,6 +105,28 @@ void otCryptoAesCcm(const uint8_t *aKey, bool aEncrypt, void * aTag); +/** + * This method creates ECDSA sign. + * + * @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 aClaims 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. + * @rerval OT_ERROR_FAILED Error during signing. + */ +otError otCryptoEcdsaSign(uint8_t * aOutput, + uint16_t * aOutputLength, + const uint8_t *aInputHash, + uint16_t aInputHashLength, + const uint8_t *aPrivateKey, + uint16_t aPrivateKeyLength); + /** * @} * diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 3e487ea7a..86657488e 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -147,6 +147,7 @@ SOURCES_COMMON = \ common/trickle_timer.cpp \ crypto/aes_ccm.cpp \ crypto/aes_ecb.cpp \ + crypto/ecdsa.cpp \ crypto/hmac_sha256.cpp \ crypto/mbedtls.cpp \ crypto/pbkdf2_cmac.cpp \ @@ -304,6 +305,7 @@ HEADERS_COMMON = \ common/trickle_timer.hpp \ crypto/aes_ccm.hpp \ crypto/aes_ecb.hpp \ + crypto/ecdsa.hpp \ crypto/hmac_sha256.hpp \ crypto/mbedtls.hpp \ crypto/pbkdf2_cmac.h \ diff --git a/src/core/api/crypto_api.cpp b/src/core/api/crypto_api.cpp index fd6ae54cc..2319880bd 100644 --- a/src/core/api/crypto_api.cpp +++ b/src/core/api/crypto_api.cpp @@ -33,10 +33,12 @@ #include "openthread-core-config.h" #include +#include #include "common/code_utils.hpp" #include "common/debug.hpp" #include "crypto/aes_ccm.hpp" +#include "crypto/ecdsa.hpp" #include "crypto/hmac_sha256.hpp" using namespace ot::Crypto; @@ -91,3 +93,17 @@ void otCryptoAesCcm(const uint8_t *aKey, exit: return; } + +#if OPENTHREAD_ENABLE_ECDSA + +otError otCryptoEcdsaSign(uint8_t * aOutput, + uint16_t * aOutputLength, + const uint8_t *aInputHash, + uint16_t aInputHashLength, + const uint8_t *aPrivateKey, + uint16_t aPrivateKeyLength) +{ + return Ecdsa::Sign(aOutput, aOutputLength, aInputHash, aInputHashLength, aPrivateKey, aPrivateKeyLength); +} + +#endif // OPENTHREAD_ENABLE_ECDSA diff --git a/src/core/crypto/ecdsa.cpp b/src/core/crypto/ecdsa.cpp new file mode 100644 index 000000000..575afc67b --- /dev/null +++ b/src/core/crypto/ecdsa.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2018, 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. + */ + +/** + * @file + * This file implements ECDSA signing. + */ + +#include "ecdsa.hpp" + +#include + +#include +#include + +#include "common/code_utils.hpp" +#include "common/random.hpp" + +namespace ot { +namespace Crypto { + +#if OPENTHREAD_ENABLE_ECDSA + +otError Ecdsa::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; + mbedtls_pk_context pkCtx; + mbedtls_ecp_keypair * keypair; + mbedtls_mpi rMpi; + mbedtls_mpi sMpi; + + mbedtls_pk_init(&pkCtx); + mbedtls_ecdsa_init(&ctx); + + // Parse a private key in PEM format. + VerifyOrExit(mbedtls_pk_parse_key(&pkCtx, aPrivateKey, aPrivateKeyLength, NULL, 0) == 0, + error = OT_ERROR_INVALID_ARGS); + VerifyOrExit(mbedtls_pk_get_type(&pkCtx) == MBEDTLS_PK_ECKEY, error = OT_ERROR_INVALID_ARGS); + + keypair = mbedtls_pk_ec(pkCtx); + assert(keypair != NULL); + + VerifyOrExit(mbedtls_ecdsa_from_keypair(&ctx, keypair) == 0, error = OT_ERROR_FAILED); + + mbedtls_mpi_init(&rMpi); + mbedtls_mpi_init(&sMpi); + + // Sign using ECDSA. + VerifyOrExit(mbedtls_ecdsa_sign(&ctx.grp, &rMpi, &sMpi, &ctx.d, aInputHash, aInputHashLength, FillRandom, NULL) == + 0, + error = OT_ERROR_FAILED); + VerifyOrExit(mbedtls_mpi_size(&rMpi) + mbedtls_mpi_size(&sMpi) <= *aOutputLength, error = OT_ERROR_NO_BUFS); + + // Concatenate the two octet sequences in the order R and then S. + VerifyOrExit(mbedtls_mpi_write_binary(&rMpi, aOutput, mbedtls_mpi_size(&rMpi)) == 0, error = OT_ERROR_FAILED); + *aOutputLength = mbedtls_mpi_size(&rMpi); + + VerifyOrExit(mbedtls_mpi_write_binary(&sMpi, aOutput + *aOutputLength, mbedtls_mpi_size(&sMpi)) == 0, + error = OT_ERROR_FAILED); + *aOutputLength += mbedtls_mpi_size(&sMpi); + +exit: + mbedtls_mpi_free(&rMpi); + mbedtls_mpi_free(&sMpi); + mbedtls_ecdsa_free(&ctx); + mbedtls_pk_free(&pkCtx); + + return error; +} + +int Ecdsa::FillRandom(void *, unsigned char *aBuffer, size_t aSize) +{ + Random::FillBuffer(aBuffer, aSize); + + return 0; +} + +#endif // OPENTHREAD_ENABLE_ECDSA + +} // namespace Crypto +} // namespace ot diff --git a/src/core/crypto/ecdsa.hpp b/src/core/crypto/ecdsa.hpp new file mode 100644 index 000000000..63d36e2c7 --- /dev/null +++ b/src/core/crypto/ecdsa.hpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2018, 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. + */ + +/** + * @file + * This file includes definitions for performing ECDSA signing. + */ + +#ifndef ECDSA_HPP_ +#define ECDSA_HPP_ + +#include "openthread-core-config.h" + +#include +#include "utils/wrap_stdint.h" + +#include + +namespace ot { +namespace Crypto { + +/** + * @addtogroup core-security + * + * @{ + * + */ + +/** + * This class implements ECDSA signing. + * + */ +class Ecdsa +{ +public: + /** + * This method creates ECDSA sign. + * + * @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. + * @rerval OT_ERROR_FAILED Error during signing. + */ + static otError Sign(uint8_t * aOutput, + uint16_t * aOutputLength, + const uint8_t *aInputHash, + uint16_t aInputHashLength, + const uint8_t *aPrivateKey, + uint16_t aPrivateKeyLength); + +private: + static int FillRandom(void *, unsigned char *aBuffer, size_t aSize); +}; + +/** + * @} + * + */ + +} // namespace Crypto +} // namespace ot + +#endif // ECDSA_HPP_ diff --git a/third_party/mbedtls/mbedtls-config.h b/third_party/mbedtls/mbedtls-config.h index dee9cc928..306338519 100644 --- a/third_party/mbedtls/mbedtls-config.h +++ b/third_party/mbedtls/mbedtls-config.h @@ -119,6 +119,14 @@ __inline int windows_kernel_snprintf(char * s, size_t n, const char * format, .. #define MBEDTLS_X509_CRT_PARSE_C #endif +#if OPENTHREAD_ENABLE_ECDSA +#define MBEDTLS_BASE64_C +#define MBEDTLS_ECDH_C +#define MBEDTLS_ECDSA_C +#define MBEDTLS_OID_C +#define MBEDTLS_PEM_PARSE_C +#endif + #define MBEDTLS_MPI_WINDOW_SIZE 1 /**< Maximum windows size used. */ #define MBEDTLS_MPI_MAX_SIZE 32 /**< Maximum number of bytes for usable MPIs. */ #define MBEDTLS_ECP_MAX_BITS 256 /**< Maximum bit size of groups */ @@ -127,7 +135,7 @@ __inline int windows_kernel_snprintf(char * s, size_t n, const char * format, .. #define MBEDTLS_ENTROPY_MAX_SOURCES 1 /**< Maximum number of sources supported */ #if OPENTHREAD_ENABLE_MULTIPLE_INSTANCES -#define MBEDTLS_PLATFORM_STD_CALLOC otPlatCAlloc /**< Default allocator to use, can be undefined */ +#define MBEDTLS_PLATFORM_STD_CALLOC otPlatCAlloc /**< Default allocator to use, can be undefined */ #define MBEDTLS_PLATFORM_STD_FREE otPlatFree /**< Default free to use, can be undefined */ #else #define MBEDTLS_MEMORY_BUFFER_ALLOC_C