IoTといえどもインターネットにつなぐのであれば、SSL(TLS)は必須な時代になったので、蟹さんもSSLを話せるようにしてみました。
前にも書きましたがaxTLSがいまいちだったので調べていたらBearSSLという新しい実装が見つかり、これをポートする事にしました。
SSL通信はTCP通信の中を通すように作られています。
BearSSLはもちろんCで書かれたライブラリなのですが、独自のプリプロセッサーを使ってCコードを生成している部分もあります。プリプロセッサーはCSで書かれていて、Windowsのバイナリのみの提供になります。
BearSSLはシステム依存が時間と乱数しか無くEmbedな環境にも非常に優しく出来ています。axTLSではソケットのread/writeがハードコードされてしまっていますが、BearSSLでは関数渡しで自前のコードが使えるようになっています。
乱数はもともとUNIX用とWindows用のコードが用意されているのですが、Embedでは使えないのでMT19937のコードを入れてみました。
時間はtime()関数が必要なのですが、BearSSLと自前のコードで読み込むヘッダーが違っていてtime_tが4バイトと8バイトになってしまい、X509の時間のチェックがうまくできなくてちょっとはまりました。
BearSSLのサンプルのクライアントコードをベースにlwIPのRAW TCPコードと融合させてどうにか動くようになりました。
入力処理の流れは以下のようになっています。
Etherパケット受信割り込み -> low_level_input() -> データをnetif.input()に渡す -> lwIPが処理 -> tcp_recv()で設定した関数が呼ばれる -> 受信データをローカルバッファに保存 --- BearSSLに設定したread関数でバッファデータを引き渡し -> ネゴシエーションなどはBearSSL内で処理され通信内容はbr_sslio_read()で読める
出力はリニアに処理されます。
br_sslio_write_all()で書く -> BearSSLが処理 -> BearSSLに設定したwrite関数 -> lwIP -> lwIPに設定したoutput関数でデバイスリングバッファに保存
tcpdumpでの通信内容
microserver % sudo tcpdump -i re0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on re0, link-type EN10MB (Ethernet), capture size 65535 bytes
09:54:04.457395 ARP, Request who-has 10.10.10.2 tell 10.10.10.2, length 46
09:54:04.457494 ARP, Request who-has 10.10.10.3 tell 10.10.10.2, length 46
09:54:04.457503 ARP, Reply 10.10.10.3 is-at 00:13:3b:12:81:04 (oui Unknown), len
gth 28
09:54:04.467366 IP 10.10.10.2.49153 > 10.10.10.3.8080: Flags [S], seq 6509, win
2144, options [mss 536], length 0
09:54:04.467410 IP 10.10.10.3.8080 > 10.10.10.2.49153: Flags [S.], seq 100541135
4, ack 6510, win 65535, options [mss 536], length 0
09:54:04.477530 IP 10.10.10.2.49153 > 10.10.10.3.8080: Flags [P.], seq 1:194, ac
k 1, win 2144, length 193
09:54:04.495526 IP 10.10.10.3.8080 > 10.10.10.2.49153: Flags [.], seq 1:537, ack
194, win 65535, length 536
09:54:04.495541 IP 10.10.10.3.8080 > 10.10.10.2.49153: Flags [.], seq 537:1073,
ack 194, win 65535, length 536
09:54:04.495548 IP 10.10.10.3.8080 > 10.10.10.2.49153: Flags [.], seq 1073:1609,
ack 194, win 65535, length 536
09:54:04.495554 IP 10.10.10.3.8080 > 10.10.10.2.49153: Flags [P.], seq 1609:2060
, ack 194, win 65535, length 451
09:54:04.497594 IP 10.10.10.2.49153 > 10.10.10.3.8080: Flags [.], ack 537, win 2
144, length 0
09:54:04.497731 IP 10.10.10.2.49153 > 10.10.10.3.8080: Flags [.], ack 1073, win
2144, length 0
09:54:04.749933 IP 10.10.10.3.8080 > 10.10.10.2.49153: Flags [.], seq 1073:1609,
ack 194, win 65535, length 536
09:54:04.757573 IP 10.10.10.2.49153 > 10.10.10.3.8080: Flags [.], ack 1609, win
2144, length 0
09:54:04.757621 IP 10.10.10.3.8080 > 10.10.10.2.49153: Flags [P.], seq 1609:2060
, ack 194, win 65535, length 451
09:54:04.757756 IP 10.10.10.2.49153 > 10.10.10.3.8080: Flags [.], ack 2060, win
1693, length 0
09:54:04.767515 IP 10.10.10.2.49153 > 10.10.10.3.8080: Flags [.], ack 2060, win
1693, length 0
09:54:05.236710 IP 10.10.10.2.49153 > 10.10.10.3.8080: Flags [P.], seq 194:236,
ack 2060, win 1693, length 42
09:54:05.337974 IP 10.10.10.3.8080 > 10.10.10.2.49153: Flags [.], ack 236, win 6
5535, length 0
09:54:05.347562 IP 10.10.10.2.49153 > 10.10.10.3.8080: Flags [P.], seq 236:279,
ack 2060, win 1693, length 43
09:54:05.347944 IP 10.10.10.3.8080 > 10.10.10.2.49153: Flags [P.], seq 2060:2066
, ack 279, win 65535, length 6
09:54:05.643961 IP 10.10.10.3.8080 > 10.10.10.2.49153: Flags [P.], seq 2060:2103
, ack 279, win 65535, length 43
09:54:05.647583 IP 10.10.10.2.49153 > 10.10.10.3.8080: Flags [.], ack 2103, win
1650, length 0
09:54:05.649108 IP 10.10.10.2.49153 > 10.10.10.3.8080: Flags [P.], seq 279:335,
ack 2103, win 1650, length 56
09:54:05.649297 IP 10.10.10.3.8080 > 10.10.10.2.49153: Flags [P.], seq 2103:2275
, ack 335, win 65535, length 172
09:54:05.657611 IP 10.10.10.2.49153 > 10.10.10.3.8080: Flags [.], ack 2275, win
2144, length 0
09:54:05.657659 IP 10.10.10.3.8080 > 10.10.10.2.49153: Flags [P.], seq 2275:2298
, ack 335, win 65535, length 23
09:54:05.701507 IP 10.10.10.2.49153 > 10.10.10.3.8080: Flags [P.], seq 335:358,
ack 2298, win 2121, length 23
09:54:05.701553 IP 10.10.10.2.49153 > 10.10.10.3.8080: Flags [F.], seq 358, ack
2298, win 2121, length 0
09:54:05.701583 IP 10.10.10.3.8080 > 10.10.10.2.49153: Flags [.], ack 359, win 6
5535, length 0
09:54:05.701716 IP 10.10.10.3.8080 > 10.10.10.2.49153: Flags [F.], seq 2298, ack
359, win 65535, length 0
09:54:05.707641 IP 10.10.10.2.49153 > 10.10.10.3.8080: Flags [.], ack 2299, win
2120, length 0
09:54:05.647583までがネゴシエーションでこれ以降が実際の通信になります。
もちろんmrubyから使えるようにするつもりですが、インターフェースはなにか有り物に似せて作ろうかとも思っています。
ポートして分かった事はCA(TA)をどう管理するかとある程度正確な時間が必要という事です。サンプルプログラムではTAをハードコードしてありますが、そのTAで認証できない証明書がサーバに貼られた場合はエラーになりますし、ある程度の柔軟性を持った仕組みが必要な気がします。時間についてはRTCをつむしかないかもしれませんね。
サーバー側が自分の管理下でオレオレ証明書で運用するのであればTAは固定でもよいかもしれないが、一般のサービスに接続する場合は証明書の発行元が変わる事も考えられTAの管理は柔軟にしなければいけない。
BearSSLって本当に素晴らしいです。