I'm working on an implementation of an EST(Enrollment over Secure Transport)-client over CoAPs for the OpenThread stack. For this, i want to write a CSR (Certificate Signing Request) by using mbedTLS, which is part of the stack as a third party software. My problem now is that i get some "undefined reference" error from the linker when i build the code (i'm using GCC on an Ubuntu 18.04.2 LTS machine).
As there are multiple functions for which the error occurs, i will provide code for just one example. here is my source file:
openthread/src/core/crypto/ecp.cpp:
#include "ecp.hpp"
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/ecp.h>
#include <mbedtls/pk.h>
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/random.hpp"
#include "openthread/entropy.h"
#include "openthread/random_crypto.h"
namespace ot {
namespace Crypto {
#if OPENTHREAD_ENABLE_EST_CLIENT && OPENTHREAD_ENABLE_APPLICATION_COAP_SECURE
otError Ecp::KeyPairGeneration(const uint8_t *aPersonalSeed,
uint32_t aPersonalSeedLength,
uint8_t * aPrivateKey,
uint32_t * aPrivateKeyLength,
uint8_t * aPublicKey,
uint32_t * aPublicKeyLength)
{
otError error = OT_ERROR_NONE;
mbedtls_pk_context keypair;
OT_UNUSED_VARIABLE(aPersonalSeed);
OT_UNUSED_VARIABLE(aPersonalSeedLength);
mbedtls_pk_init(&keypair);
// Generate keypair
VerifyOrExit(mbedtls_pk_setup(&keypair, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)),
error = OT_ERROR_FAILED);
VerifyOrExit(mbedtls_ecp_group_load(&mbedtls_pk_ec(keypair)->grp, MBEDTLS_ECP_DP_SECP256R1) == 0,
error = OT_ERROR_FAILED);
VerifyOrExit(mbedtls_ecp_gen_keypair(&mbedtls_pk_ec(keypair)->grp, &mbedtls_pk_ec(keypair)->d,
&mbedtls_pk_ec(keypair)->Q, mbedtls_ctr_drbg_random,
Random::Crypto::MbedTlsContextGet()) == 0,
error = OT_ERROR_FAILED);
VerifyOrExit(mbedtls_pk_write_pubkey_pem(&keypair, (unsigned char*)aPublicKey,
*aPublicKeyLength) == 0,
error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(mbedtls_pk_write_key_pem(&keypair, (unsigned char*)aPrivateKey,
*aPrivateKeyLength) == 0,
error = OT_ERROR_INVALID_ARGS);
exit:
mbedtls_pk_free(&keypair);
return error;
}
#endif // OPENTHREAD_ENABLE_EST_CLIENT
} // namespace Crypto
} // namespace ot
my header file:
openthread/src/core/crypto/ecp.hpp
#ifndef ECP_HPP_
#define ECP_HPP_
#include "openthread-core-config.h"
#include <stdint.h>
#include <stdlib.h>
#include <openthread/error.h>
namespace ot {
namespace Crypto {
/**
* @addtogroup core-security
*
* @{
*
*/
/**
* This class implements elliptic curve key generation.
*
*/
class Ecp
{
public:
/**
* This method generate a Elliptic Curve key pair.
*
* @param[in] aPersonalSeed An additional seed for the entropy. Can be NULL.
* @param[in] aPersonalSeedLengh The length of the @p aPersonalSeed.
* @param[out] aPrivateKey An output buffer where the private key should be stored.
* @param[inout] aPrivateKeyLength The length of the @p aPrivateKey buffer.
* @param[out] aPublicKey An output buffer where the private key should be stored.
* @param[inout] aPublicKeyLength The length of the @p aPublicKey buffer.
*
* @retval OT_ERROR_NONE EC key pairs has been created successfully.
* OT_ERROR_NO_BUFS Key buffers are too small or mbedtls heap too small.
*/
static otError KeyPairGeneration(const uint8_t *aPersonalSeed,
uint32_t aPersonalSeedLength,
uint8_t * aPrivateKey,
uint32_t * aPrivateKeyLength,
uint8_t * aPublicKey,
uint32_t * aPublicKeyLength);
};
/**
* @}
*
*/
} // namespace Crypto
} // namespace ot
#endif // ECP_HPP_
The functions which cause the error are here mbedtls_pk_write_pubkey_pem and mbedtls_pk_write_key_pem.
Here is also a part of the console output:
Making all in apps
Making all in cli
CC ot_cli_ftd-main.o
CC ot_cli_mtd-main.o
CCLD ot-cli-ftd
CCLD ot-cli-mtd
/opt/gcc-arm-none-eabi-8-2018-q4-major/bin/../lib/gcc/arm-none-eabi/8.2.1/../../../../arm-none-eabi/bin/ld: ../../../src/core/libopenthread-mtd.a(libopenthread_mtd_a-ecp.o): in function `ot::Crypto::Ecp::KeyPairGeneration(unsigned char const*, unsigned long, unsigned char*, unsigned long*, unsigned char*, unsigned long*)':
/home/scnm/eclipse-workspace/openthread/examples/../src/core/crypto/ecp.cpp:79: undefined reference to `mbedtls_pk_write_pubkey_pem'
/opt/gcc-arm-none-eabi-8-2018-q4-major/bin/../lib/gcc/arm-none-eabi/8.2.1/../../../../arm-none-eabi/bin/ld: /home/scnm/eclipse-workspace/openthread/examples/../src/core/crypto/ecp.cpp:83: undefined reference to `mbedtls_pk_write_key_pem'
collect2: error: ld returned 1 exit status
Makefile:1249: recipe for target 'ot-cli-mtd' failed
make[5]: *** [ot-cli-mtd] Error 1
I first thougth it was because i was missing some #define to actually use these functions, but i compared it with other OpenThread code which uses mbedtls and i can't see what i did wrong. As far as i've understood it, i have to modify the "openthread/third_party/mbedtls/mbedtls-config.h" file so that these funtions are build, so this is what i did:
#ifndef MBEDTLS_CONFIG_H
#define MBEDTLS_CONFIG_H
#include <stdio.h>
#include <stdlib.h>
#include <openthread/config.h>
#include <openthread/platform/logging.h>
#include <openthread/platform/memory.h>
#define MBEDTLS_PLATFORM_SNPRINTF_MACRO snprintf
#define MBEDTLS_AES_C
#define MBEDTLS_AES_ROM_TABLES
#define MBEDTLS_ASN1_PARSE_C
#define MBEDTLS_ASN1_WRITE_C
#define MBEDTLS_BIGNUM_C
#define MBEDTLS_CCM_C
#define MBEDTLS_CIPHER_C
#define MBEDTLS_CMAC_C
#define MBEDTLS_CTR_DRBG_C
#define MBEDTLS_ECJPAKE_C
#define MBEDTLS_ECP_C
#define MBEDTLS_ECP_DP_SECP256R1_ENABLED
#define MBEDTLS_ECP_NIST_OPTIM
#define MBEDTLS_ENTROPY_C
#define MBEDTLS_HAVE_ASM
#define MBEDTLS_HMAC_DRBG_C
#define MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED
#define MBEDTLS_MD_C
#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES
#define MBEDTLS_NO_PLATFORM_ENTROPY
#define MBEDTLS_PK_C
#define MBEDTLS_PK_PARSE_C
#define MBEDTLS_PLATFORM_C
#define MBEDTLS_PLATFORM_MEMORY
#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS
#define MBEDTLS_SHA256_C
#define MBEDTLS_SHA256_SMALLER
#define MBEDTLS_SSL_CLI_C
#define MBEDTLS_SSL_DTLS_ANTI_REPLAY
#define MBEDTLS_SSL_DTLS_HELLO_VERIFY
#define MBEDTLS_SSL_EXPORT_KEYS
#define MBEDTLS_SSL_MAX_FRAGMENT_LENGTH
#define MBEDTLS_SSL_PROTO_TLS1_2
#define MBEDTLS_SSL_PROTO_DTLS
#define MBEDTLS_SSL_TLS_C
#if OPENTHREAD_ENABLE_BORDER_AGENT || OPENTHREAD_ENABLE_COMMISSIONER || OPENTHREAD_ENABLE_APPLICATION_COAP_SECURE
#define MBEDTLS_SSL_COOKIE_C
#define MBEDTLS_SSL_SRV_C
#endif
#if OPENTHREAD_ENABLE_APPLICATION_COAP_SECURE
#define MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
#define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
#if OPENTHREAD_ENABLE_EST_CLIENT
#define MBEDTLS_PEM_WRITE_C
#define MBEDTLS_PK_WRITE_C
#define MBEDTLS_X509_CSR_WRITE_C
#define MBEDTLS_X509_CREATE_C
#endif
#endif
#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
#define MBEDTLS_BASE64_C
#define MBEDTLS_ECDH_C
#define MBEDTLS_ECDSA_C
#define MBEDTLS_OID_C
#define MBEDTLS_PEM_PARSE_C
#define MBEDTLS_X509_USE_C
#define MBEDTLS_X509_CRT_PARSE_C
#define MBEDTLS_X509_USE_C
#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 */
#define MBEDTLS_ECP_WINDOW_SIZE 2 /**< Maximum window size used */
#define MBEDTLS_ECP_FIXED_POINT_OPTIM 0 /**< Enable fixed-point speed-up */
#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_FREE otPlatFree /**< Default free to use, can be undefined */
#else
#define MBEDTLS_MEMORY_BUFFER_ALLOC_C
#endif
#if OPENTHREAD_ENABLE_APPLICATION_COAP_SECURE
#define MBEDTLS_SSL_MAX_CONTENT_LEN 900 /**< Maxium fragment length in bytes */
#else
#define MBEDTLS_SSL_MAX_CONTENT_LEN 768 /**< Maxium fragment length in bytes */
#endif
#define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8
#if defined(MBEDTLS_USER_CONFIG_FILE)
#include MBEDTLS_USER_CONFIG_FILE
#endif
#if defined(MBEDTLS_ECP_ALT) && !defined(MBEDTLS_ECP_RESTARTABLE)
typedef void mbedtls_ecp_restart_ctx;
#endif
#include "mbedtls/check_config.h"
#endif /* MBEDTLS_CONFIG_H */
It resolved the "not defined in this scope" errors i've had before, but instead i have now the above described errors.
Here is what i've edited in the common-switch file:
openthread/examples/common-switches.mk
ECDSA ?= 0
// my code begin
EST_CLIENT ?= 0
// my code end
JAM_DETECTION ?= 0
ifeq ($(ECDSA),1)
configure_OPTIONS += --enable-ecdsa
endif
// my code begin
ifeq ($(EST_CLIENT),1)
configure_OPTIONS += --enable-est-client --enable-application-coap-secure
endif
// my code end
ifeq ($(JAM_DETECTION),1)
configure_OPTIONS += --enable-jam-detection
endif
and her what i've added to configure:
openthread/configure.ac
#
# EST Client
#
AC_ARG_ENABLE(est_client,
[AS_HELP_STRING([--enable-est-client],[Enable EST client support @<:@default=no@:>@.])],
[
case "${enableval}" in
no|yes)
enable_est_client=${enableval}
;;
*)
AC_MSG_ERROR([Invalid value ${enable_est_client} for --enable-est-client])
;;
esac
],
[enable_est_client=no])
if test "$enable_est_client" = "yes"; then
OPENTHREAD_ENABLE_EST_CLIENT=1
else
OPENTHREAD_ENABLE_EST_CLIENT=0
fi
AC_SUBST(OPENTHREAD_ENABLE_EST_CLIENT)
AM_CONDITIONAL([OPENTHREAD_ENABLE_EST_CLIENT], [test "${enable_est_client}" = "yes"])
AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_EST_CLIENT],[${OPENTHREAD_ENABLE_EST_CLIENT}],[Define to 1 if you want to enable EST Client])
OpenThread DNS Client support : ${enable_dns_client}
// my code begin
OpenThread EST Client support : ${enable_est_client}
// my code end
OpenThread SNTP Client support : ${enable_sntp_client}
I've also edited the makefile:
openthread/src/core/Makefile.am
crypto/ecdsa.hpp \
crypto/ecp.hpp \
crypto/hmac_sha256.hpp \
crypto/ecdsa.cpp \
crypto/ecp.cpp \
crypto/hmac_sha256.cpp \
My build command is "make -f examples/Makefile-nrf52840 EST_CLIENT=1".
I think once this problem is solved, i can solve the others by myself as the root of the problem seems to be the same.
Thanks.