みつきんのメモ

組み込みエンジニアです。Interface誌で「Yocto Projectではじめる 組み込みLinux開発入門」連載中

NuttXでmbedTLSを動かす

はじめに

NuttXでmbed TLSを動かす。

3年ほど前にMarceloさんが一度移植したらしいので、ここを参考にする。

ただし情報が古くその間にNuttXも新しくなっているのでこのままではうまく行かない。

今回使用するターゲットはSTM32F4Discovery。

NuttX

まずはNuttXのソースを取ってくる。

ディレクトリ構成

全体のディレクトリ構成が次のようになるようにする。

.
├── apps
├── mbedtls
└── nuttx

ソースの取得

$ git clone https://bitbucket.org/nuttx/nuttx.git
$ git clone https://bitbucket.org/nuttx/apps.git

mbedtls

ソースの取得

mbed TLSgithubでソースも公開しているのでこれを使う。

$ git clone https://github.com/ARMmbed/mbedtls.git

ファイルのコピー

mbed TLSのソースをNuttXのソースツリーに組み込む。

mbed TLSのツリー

mbed TLSのソースツリーは次のようになっている。

ここでは使用する分を抜粋する。

mbedtls
├── configs      // コンフィグヘッダ
├── include
│   └── mbedtls // ヘッダファイル
└── library      // ソースファイル

NuttX側

NuttXには次のようになるようにする。

nuttx
└── mbedtls           // ソースファイル
     └── mbedtls      // ヘッダファイル
          └── configs // コンフィグヘッダ

出力ディレクトリの作成

mbed TLSのソースディレクトリに移動し、出力先ディレクトリの作成。

$ cd mbedtls
$ mkdir ../nuttx/mbedtls

ソースファイルのコピー

インクルードファイルのコピー

$ cp -ra ./include/mbedtls ../nuttx/mbedtls/

コンフィグヘッダのコピー

configs以下のファイルから参照されることがあるためcheck_config.hconfigsにコピーする。 また、mbedtls/mbedtls/config.hはKconfigでの設定を反映できるように修正するため、もとのファイルをmbedtls/mbedtls/configs/config-default.hとしてコピーしておく。

$ cp -ra ./configs ../nuttx/mbedtls/mbedtls/
$ cp ./include/mbedtls/config.h ../nuttx/mbedtls/mbedtls/configs/config-default.h
$ cp ./include/mbedtls/check_config.h ../nuttx/mbedtls/mbedtls/configs/

ソースファイルのコピー

$ cp -ra ./library/*.c ../nuttx/mbedtls/

ライセンスとREADMEのコピー

$ cp -a ./LICENSE ../nuttx/mbedtls
$ cp -a ./apache-2.0.txt ../nuttx/mbedtls
$ cp -a ./README.md ../nuttx/mbedtls

ビルドに必要なファイルの作成

nuttx以下に次のファイルを作成する。

  • mbedtls/Kconfig
  • mbedtls/Makefile
  • mbedtls/mbedtls/config.h

Kconfig

mbedtls/Kconfigを次の内容で作成する。

config MBED_TLS_LIB
    bool "mbed TLS Support"
    default n
    ---help---
        Enable or disable mbed TLS support

if MBED_TLS_LIB

choice
    prompt "mbed Configuration Profile"
    default MBED_PROFILE_DEFAULT

config MBED_PROFILE_SUITE_B
    bool "Suite B"

config MBED_PROFILE_CCM_PSK_TLS_1_2
    bool "CCM, PSK and TLS 1.2"

config MBED_PROFILE_MINI_TLS_1_1
    bool "Mini TLS 1.1"

config MBED_PROFILE_THREAD
    bool "Thread"

config MBED_PROFILE_NOENTROPY
    bool "No entropy"

config MBED_PROFILE_DEFAULT
    bool "Default configuration"

endchoice

endif # MBED_TLS_LIB

Makefile

mbedtls/Makefileを次の内容で作成する。

-include $(TOPDIR)/Make.defs

ASRCS =
CSRCS =

ifeq ($(CONFIG_MBED_TLS_LIB),y)

CSRCS += aes.c          asn1parse.c asn1write.c             base64.c        \
        bignum.c        blowfish.c              camellia.c      \
        ccm.c           cipher.c                cipher_wrap.c   \
        ctr_drbg.c      des.c                   dhm.c           \
        ecdh.c          ecdsa.c                 ecjpake.c       \
        ecp.c           ecp_curves.c            entropy.c       \
        entropy_poll.c  error.c                 gcm.c           \
        havege.c        hmac_drbg.c             md.c            \
        md2.c           md4.c                   md5.c           \
        md_wrap.c       memory_buffer_alloc.c   oid.c           \
        padlock.c       pem.c                   pk.c            \
        pk_wrap.c       pkcs12.c                pkcs5.c         \
        pkparse.c       pkwrite.c               platform.c      \
        ripemd160.c     rsa.c                   sha1.c          \
        sha256.c        sha512.c                threading.c     \
        timing.c        version_features.c      version.c       \
        xtea.c          x509_create.c           x509_crl.c      \
        x509_crt.c      x509_csr.c              x509write_crt.c \
        x509write_csr.c ssl_ciphersuites.c      ssl_cli.c       \
        ssl_cookie.c    ssl_srv.c               ssl_ticket.c    \
        ssl_tls.c       platform_util.c


endif # CONFIG_MBED_TLS_LIB

AOBJS = $(ASRCS:.S=$(OBJEXT))
COBJS = $(CSRCS:.c=$(OBJEXT))

SRCS = $(ASRCS) $(CSRCS)
OBJS = $(AOBJS) $(COBJS)

BIN = libmbedtls$(LIBEXT)

all: $(BIN)
.PHONY: depend clean distclean

$(AOBJS): %$(OBJEXT): %.S
    $(call ASSEMBLE, $<, $@)

$(COBJS): %$(OBJEXT): %.c
    $(call COMPILE, $<, $@)

$(BIN): $(OBJS)
    $(call ARCHIVE, $@, $(OBJS))

.depend: Makefile $(SRCS)
ifeq ($(CONFIG_MBED_TLS_LIB),y)
    $(Q) $(MKDEP) --dep-path . "$(CC)" -- $(CFLAGS) -- $(SRCS) >Make.dep
endif
    $(Q) touch $@

depend: .depend

clean:
    $(call DELFILE, $(BIN))
    $(call CLEAN)

distclean: clean
    $(call DELFILE, Make.dep)
    $(call DELFILE, .depend)

-include Make.dep

config.h

Kconfigの内容を反映できるようにするために、既存のmbedtls/mbedtls/config.hを破棄し、次の内容で作成する。

#include <nuttx/config.h>

#if defined(CONFIG_MBED_PROFILE_SUITE_B)
#include "mbedtls/configs/config-suite-b.h"
#elif defined(CONFIG_MBED_PROFILE_CCM_PSK_TLS_1_2)
#include "mbedtls/configs/config-ccm-psk-tls1_2.h"
#elif defined(CONFIG_MBED_PROFILE_MINI_TLS_1_1)
#include "mbedtls/configs/config-mini-tls1_1.h"
#elif defined(CONFIG_MBED_PROFILE_THREAD)
#include "mbedtls/configs/config-thread.h"
#elif defined(CONFIG_MBED_PROFILE_NOENTROPY)
#include "mbedtls/configs/config-no-entropy.h"
#elif defined(CONFIG_MBED_PROFILE_DEFAULT)
#include "mbedtls/configs/config-default.h"
#else
#error "mbed TLS configuration file was not set"
#endif

config-default.h

先程コピーしたmbedtls/mbedtls/configs/config-default.hは、このままではビルドに失敗するため、次の2つのコンフィグを変更しておく。

diff --git a/mbedtls/mbedtls/configs/config-default.h b/mbedtls/mbedtls/configs/config-default.h
index fd91d7074c..878f4e67cf 100644
--- a/mbedtls/mbedtls/configs/config-default.h
+++ b/mbedtls/mbedtls/configs/config-default.h
@@ -1149,7 +1149,7 @@
  *
  * Uncomment this macro to disable the built-in platform entropy functions.
  */
-//#define MBEDTLS_NO_PLATFORM_ENTROPY
+#define MBEDTLS_NO_PLATFORM_ENTROPY
 
 /**
  * \def MBEDTLS_ENTROPY_FORCE_SHA256
@@ -3018,7 +3018,7 @@
  *
  * This module is used by the HAVEGE random number generator.
  */
-#define MBEDTLS_TIMING_C
+//#define MBEDTLS_TIMING_C
 
 /**
  * \def MBEDTLS_VERSION_C

既存ファイルの修正

ビルド対象に含めるためにNuttX側のファイルを修正する。

今回は次のファイルを修正する。

  • Kconfig
  • tools/Directories.mk
  • tools/FlatLibs.mk
  • tools/KernelLibs.mk
  • tools/LibTargets.mk
  • tools/ProtectedLibs.mk

Kconfig

NuttXのソースツリーのルートにあるKconfigを次のように修正する。

diff --git a/Kconfig b/Kconfig
index dbcf9ee9ff..1ea6383698 100644
--- a/Kconfig
+++ b/Kconfig
@@ -1698,6 +1698,10 @@ source libs/libxx/Kconfig
 source libs/libdsp/Kconfig
 endmenu
 
+menu "mbed TLS Support"
+source mbedtls/Kconfig
+endmenu
+
 menu "Application Configuration"
 source "$APPSDIR/Kconfig"
 endmenu

Directories.mk

tools/Directories.mkを次のように修正する。

diff --git a/tools/Directories.mk b/tools/Directories.mk
index 7c17510789..8b66691dfa 100644
--- a/tools/Directories.mk
+++ b/tools/Directories.mk
@@ -183,3 +183,8 @@ ifeq ($(CONFIG_CRYPTO),y)
 KERNDEPDIRS += crypto
 endif
 CLEANDIRS += crypto
+
+ifeq ($(CONFIG_MBED_TLS_LIB),y)
+KERNDEPDIRS += mbedtls
+endif
+CLEANDIRS += mbedtls

FlatLibs.mk

tools/FlatLibs.mkを次のように修正する。

diff --git a/tools/FlatLibs.mk b/tools/FlatLibs.mk
index 0ad2cc335b..fde3dd8c6e 100644
--- a/tools/FlatLibs.mk
+++ b/tools/FlatLibs.mk
@@ -93,6 +93,12 @@ ifeq ($(CONFIG_NET),y)
 NUTTXLIBS += staging$(DELIM)libnet$(LIBEXT)
 endif
 
+# Add libraries for mbed TLS
+
+ifeq ($(CONFIG_MBED_TLS_LIB),y)
+NUTTXLIBS += staging$(DELIM)libmbedtls$(LIBEXT)
+endif
+
 # Add libraries for Crypto API support
 
 ifeq ($(CONFIG_CRYPTO),y)

KernelLibs.mk

tools/KernelLibs.mkを次のように修正する。

diff --git a/tools/KernelLibs.mk b/tools/KernelLibs.mk
index f36d8d6ce5..1f14159d87 100644
--- a/tools/KernelLibs.mk
+++ b/tools/KernelLibs.mk
@@ -91,6 +91,12 @@ ifeq ($(CONFIG_CRYPTO),y)
 NUTTXLIBS += staging$(DELIM)libcrypto$(LIBEXT)
 endif
 
+# Add libraries for mbed TLS
+
+ifeq ($(CONFIG_MBED_TLS_LIB),y)
+NUTTXLIBS += staging$(DELIM)libmbedtls$(LIBEXT)
+endif
+
 # Add libraries for file system support
 
 NUTTXLIBS += staging$(DELIM)libfs$(LIBEXT) staging$(DELIM)libbinfmt$(LIBEXT)

LibTargets.mk

tools/LibTargets.mkを次のように修正する。

diff --git a/tools/LibTargets.mk b/tools/LibTargets.mk
index 188e4fb403..df5421ae89 100644
--- a/tools/LibTargets.mk
+++ b/tools/LibTargets.mk
@@ -94,6 +94,12 @@ crypto$(DELIM)libcrypto$(LIBEXT): context
 staging$(DELIM)libcrypto$(LIBEXT): crypto$(DELIM)libcrypto$(LIBEXT)
        $(Q) $(call INSTALL_LIB,$<,$@)
 
+mbedtls$(DELIM)libmbedtls$(LIBEXT): context
+       $(Q) $(MAKE) -C mbedtls TOPDIR="$(TOPDIR)" libmbedtls$(LIBEXT) KERNEL=y EXTRADEFINES=$(KDEFINE)
+
+staging$(DELIM)libmbedtls$(LIBEXT): mbedtls$(DELIM)libmbedtls$(LIBEXT)
+       $(Q) $(call INSTALL_LIB,$<,$@)
+
 fs$(DELIM)libfs$(LIBEXT): context
        $(Q) $(MAKE) -C fs TOPDIR="$(TOPDIR)" libfs$(LIBEXT) KERNEL=y EXTRADEFINES=$(KDEFINE)
 

インデントはスペースではなくタブであることに注意すること。

ProtectedLibs.mk

tools/ProtectedLibs.mkを次のように修正する。

diff --git a/tools/ProtectedLibs.mk b/tools/ProtectedLibs.mk
index 4bb3d19424..8ed6e3e461 100644
--- a/tools/ProtectedLibs.mk
+++ b/tools/ProtectedLibs.mk
@@ -98,6 +98,12 @@ ifeq ($(CONFIG_CRYPTO),y)
 NUTTXLIBS += staging$(DELIM)libcrypto$(LIBEXT)
 endif
 
+# Add libraries for mbed TLS
+
+ifeq ($(CONFIG_MBED_TLS_LIB),y)
+NUTTXLIBS += staging$(DELIM)libmbedtls$(LIBEXT)
+endif
+
 # Add libraries for file system support
 
 NUTTXLIBS += staging$(DELIM)libfs$(LIBEXT) staging$(DELIM)libbinfmt$(LIBEXT)

サンプルプログラム

簡単に動作を確認するためMD5を動かしてみる。

プログラムはtlssampleとする。

新規に作成するアプリケーションのために、次のファイルをapps以下に追加する。

  • external/tlssample/Kconfig
  • external/tlssample/Make.defs
  • external/tlssample/Makefile
  • external/tlssample/tlssample_main.c

ディレクトリ作成

次のコマンドでプログラムのためのディレクトリを作成する。

$ cd apps
$ mkdir -p external/tlssample
$ ln -s external/tlssample tlssample

appsから1つ下の階層のディレクトリにあるMakefileは自動的にビルド対象に追加されるため、 external/tlssampleへのシンボリックリンクを作成する。

Kconfig

external/tlssample/Kconfigを次の内容で作成する。

config EXTERNAL_TLSSAMPLE
    tristate "\"Tlssample\" example"
    default n
    ---help---
        Enable the \"Tlssample\" example

if EXTERNAL_TLSSAMPLE

config EXTERNAL_TLSSAMPLE_PROGNAME
    string "Program name"
    default "tlssample"
    depends on BUILD_LOADABLE
    ---help---
        This is the name of the program that will be use when the NSH ELF
        program is installed.

config EXTERNAL_TLSSAMPLE_PRIORITY
    int "Tlssample task priority"
    default 100

config EXTERNAL_TLSSAMPLE_STACKSIZE
    int "Tlssample stack size"
    default 2048

endif

Make.defs

external/tlssample/Make.defsを次の内容で作成する。

ifneq ($(CONFIG_EXTERNAL_TLSSAMPLE),)
CONFIGURED_APPS += tlssample
endif

Makefile

external/tlssample/Makefileを次の内容で作成する。

-include $(TOPDIR)/Make.defs

# Tlssample, World! built-in application info

CONFIG_EXTERNAL_TLSSAMPLE_PRIORITY ?= SCHED_PRIORITY_DEFAULT
CONFIG_EXTERNAL_TLSSAMPLE_STACKSIZE ?= 2048

APPNAME = tlssample

PRIORITY  = $(CONFIG_EXTERNAL_TLSSAMPLE_PRIORITY)
STACKSIZE = $(CONFIG_EXTERNAL_TLSSAMPLE_STACKSIZE)

# Tlssample, World! Example

ASRCS =
CSRCS =
MAINSRC = tlssample_main.c

CFLAGS += -I$(TOPDIR)/mbedtls

CONFIG_EXTERNAL_TLSSAMPLE_PROGNAME ?= tlssample$(EXEEXT)
PROGNAME = $(CONFIG_EXTERNAL_TLSSAMPLE_PROGNAME)

MODULE = CONFIG_EXTERNAL_TLSSAMPLE

include $(APPDIR)/Application.mk

tlssample_main.c

external/tlssample/tlssample_main.cを次の内容で作成する。

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>
#include <stdio.h>
#include "mbedtls/md5.h"

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * tlssample_main
 ****************************************************************************/

#if defined(BUILD_MODULE)
int main(int argc, FAR char *argv[])
#else
int tlssample_main(int argc, char *argv[])
#endif
{
  unsigned char buf[32] = "hello world";
  unsigned char out[16];
  int i = 0;
  int n;


  n = strlen(buf);
  printf("source = %s, %d\n", buf, n);
  mbedtls_md5_ret(buf, n, out);
  printf("-----\n");
  for (i = 0; i < 16; ++i) {
    printf("%02x ", out[i]);
  }

  printf("\n-----\n");
  mbedtls_md5_self_test(1);

  return 0;
}

ビルド

ここまででmbedtlsを含んだNuttXのビルドは可能となった。

STM32F4Discoveryのusbnsh環境向けにビルドしてみる。

$ tools/configure.sh configs/stm32f4discovery/usbnsh -l
$ make menuconfig

mbed TLSの有効化

次のような感じで設定する。

f:id:mickey_happygolucky:20190423230242p:plain

f:id:mickey_happygolucky:20190423230302p:plain

f:id:mickey_happygolucky:20190423230314p:plain

現時点ではDefault configurationNo entropy以外では次のチェックで引っかかる。

#if !defined(MBEDTLS_NO_PLATFORM_ENTROPY)

#if !defined(unix) && !defined(__unix__) && !defined(__unix) && \
    !defined(__APPLE__) && !defined(_WIN32) && !defined(__QNXNTO__) && \
    !defined(__HAIKU__)
#error "Platform entropy sources only work on Unix and Windows, see MBEDTLS_NO_PLATFORM_ENTROPY in config.h"
#endif

Linnux、Mac OSQNXHAIKUWindows以外のOSでは機能が足りないと判断されるためエラーとなる。

今回は試していないが、NuttXはPOSIX LikeなOSなのでここのチェックを騙して、 ちょっと修正すれば他のコンフィグレーションでも動作するようになると思われる。

サンプルプログラムの有効化

次のような感じで設定する。

f:id:mickey_happygolucky:20190423230327p:plain

f:id:mickey_happygolucky:20190423230402p:plain

実行

プログラムの書き込み

st-flashでnuttx.binを書き込む。

$ st-flash write nuttx.bin 0x8000000

プログラムの実行

STM32F4DiscoveryとPCをマイクロUSBケーブルで接続しボードをリセットすると、PC側に/dev/ttyACM0が見えるようになる。

このデバイスをminicomなどで開いてnshにアクセスする。

nsh> tlssample
source = hello world, 11
-----
5e b6 3b bb e0 1e ee d0 93 cb 22 bb 8f 5a cd c3 
-----
  MD5 test #1: passed
  MD5 test #2: passed
  MD5 test #3: passed
  MD5 test #4: passed
  MD5 test #5: passed
  MD5 test #6: passed
  MD5 test #7: passed

nsh> 

hello worldMD55e b6 3b bb e0 1e ee d0 93 cb 22 bb 8f 5a cd c3となる。

確認方法

Linuxで次のコマンドを実行し同じになることを確認する。

$ echo -n "hello world" | md5sum
5eb63bbbe01eeed093cb22bb8f5acdc3

同じ結果になった。

まとめ

既存のライブラリをNuttXのソースツリーに持っていきビルドする方法を確認した。 サンプルプログラムを作成し、きちんと機能することも確認できた。

以前にmbed TLSを移植したMarceloさんは各コンフィグをそれぞれ選択できるようにカスタムメニューを作成しているが、 今回は省略した。

今のところNuttXは割と素直に動いてくれていて、 Linux経験者がMCUに世界に飛び込むにはかなりとっつきやすい印象。

ただ、移植にはライブラリが求めている機能をNuttXが持っているかどうかなど条件があり、 これを満たすようにするためには、いろいろと修正しなければならない場合がある。

NuttXでOSSのライブラリを使用するには、事前にそのライブラリの移植性やその難易度を予め調査するのが良さそう。 以前にNuttXに移植された実績があるかどうかなども、結構重要なポイントとなる。

規模の大きいものや複雑なものを使用したい場合は注意が必要。

ただ、NuttXは楽しい。