続・mbedTLSの組込で分かったこと

現在、Particle Photon/Electron向けにMQTT/CoAP/TLS周辺のcommunity libraryを公開している。

利用しているアプリケーションの総数は上記のようになっている。わりと使っているアプリも多くてメンテを何度辞めようか…と思いながらも続けている。それぞれの用途は名前の通り、次のような機能になる。

MQTT : 素のMQTT
MQTT-TLS : TLS向けのMQTT
TlsTcpClient: TLS(TCP)クライアント
coap : 素のCoAP

TLSのベースはmbedTLSの現時点での最新版(2.6.0)を利用している。それぞれの説明はgithub上にて。まえに”mbedTLSの組込で分かったこと“にも記載したけど、今回もmbedTLSを実装して分かったことや、端末の特性上つらいポイントを少し残しておく。

■ 容量との果てしない戦い
ひたすらTLSの容量を削っていく。現時点でのTLS library単体のサイズは50kbytes台になっている。これをアプリに組み込むと…

TlsTcpClient : 63Kbyte

MQTT-TLS : 61Kbyte

証明書は2048bitでPEM形式でソースコードに埋め込んで利用する。その分やアプリサイズを引くと50Kbyte台になる。以前、商用のTLSライブラリは大体50Kbyte程度で、そのくらいまで落とすのも出来るはず…と言ったけど、本当にそういう事に。対応しているCipher SuiteはTLS_RSA_WITH_AES_256_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256だけになってしまった。寂しい…繋げる環境に依ってビルドを変えてターゲットを絞っていく感じを想定して、とりあえず最小にしている。

なぜ容量を減らすか?

これは非常に簡単な理由からで、TLSで容量を沢山使うと、他のlibraryやアプリの実装に容量を使えなくなってしまうから。issueやcommunity上で、「容量でかい・メモリの限界」と言われまくってきたけど、もうこれだけ減らせば十分でしょう。
メモリや容量が大きいと…アプリが書けない、他のlibraryが使えない、TLSのネゴシエーション中の処理でmalloc errorで落ちる、といった様々な問題が起きる。そもそも接続以前に、ネゴシエーション中に落ちるのは心が折れる。
アルゴリズムや実装の選定はmbedTLSの設定(config.h)で追加・除去が簡単に出来る。この設定を調整して実装の選定をしてく。

■ ソースコード修正/関数追加
Particleのビルドは arm-none-eabi-gcc で行われて基本的にはC++で記載していく。ただ、mbedTLSはCだからC++にしていく作業が必要になる。これは拡張子を変えて、キャストを変えたり、関数を変えたり…といった地味な作業になる。

そして必要な関数を用意する。
例えば、TLSで利用する証明書には有効期限という概念がある。ある時刻を過ぎれば証明書は失効する。その証明書が正しいかどうか?を判別するには、端末で時間をちゃんと保持しておく必要がある。ただし、time/gettimeofdayのような関数はもちろんParticleの環境には存在しない。そして、この手の端末だと追加で電池を搭載していないから(電源を切ってもPCとかが日時を保持しているのは、ハードウェアクロック(RTC: Real Time Clock)と電池が搭載されていて端末の電源を落としても時刻を保持してくれている)、起動してネットワークに接続した後に時間を同期して保持&読み出せる必要がある。

これらを用意して証明書をverifyする時に有効期限をちゃんと検証(verify)できるようにする。具体的にはmbedtls/timing.cpp内に、こんな感じのラッパを用意して叩けるようにしておく。

#include "rtc_hal.h"
#include "timer_hal.h"
extern "C" int _gettimeofday( struct timeval *tv, void *tzvp )
{
    uint32_t t = HAL_Timer_Milliseconds();  // get uptime
    tv->tv_sec = HAL_RTC_Get_UnixTime();    // get rtc time before Particle.syncTime()
    tv->tv_usec = ( t % 1000 )*1000;        // get remaining microseconds
    return 0;
} // end _gettimeofday()

そして起動してネットワークに接続後に時刻の同期を行うようにしておく。わりと苦肉の策で、秒については時刻を同期したタイミングで。ただしマイクロ秒については同期しても取れないから、仕方なくuptimeから取得してくることにした(なう)。verifyの時に有効期限を切ってOKにしてしまえば…というのも考えられるけど、さすがにそれはダメかなと。

ここで更に問題になるのが…uint32_tだ。そう、いわゆる2038年問題というやつだ。この秒を扱っているHAL_RTC_Get_UnixTime()の戻りはtime_t(uint32_t)で定義されている。これをuint64_tにすれば問題無しな訳だけど、さらに根っこのfirmware側とMCU側のSDKまで波及しているから、ここだけ修正してもなんともならないという訳ですな。まだこれは20年も先の話なわけだけど、サーバ側だけならまだしも、この手の組込み物だと近くなってきたらドキドキ感が上がってくると思われ(まだ20年も先だけど)。

■ オイコラ技適どーするよ?
これはメンテ上で発生する問題で地味に切ない。現時点でParticleが提供している(していた)端末は次の通り。

– Core(WiFi:技適:日本国内OK、ただしもう入手不能&売ってない)
– Photon(WiFi:技適なし)
– Electron(SIM Cellular 2G/3G:技適なし)

ベースになるライブラリMQTTは、Coreの技適がOKの時に作ってPublishした。その後、Photon、Electronという2つの端末が出てきて主力になっている。ただし、Coreは既に終了している。つまり…
技適がOKの時に作ったライブラリで、その後は技適が無い端末しか無い状態でメンテナンスする」という事が発生する。さらには、「2Gでも動くの?」という質問も来たりする。国内は無いからもちろん海外からだけだけど。そもそも2Gは日本国内には無いだが…

Electronは持ってないし、しょうがないから「検証よろしくね!!」「diffちょうだい!!」という事にしているけど、この辺はなんとかならないかなーと思ったりする。

地味にまだ使っているひと、まだまだ素のMQTTはもう4万アプリ越えてくるし、たまに「もー、絶対にやらん、やーめったっと!!」とかって思ったりするけど、仕方なくメンテを続けて行きそうな感じ。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください