Azure Sphere MCUのセキュア構造
12/20にPlutonの構造、機能に関するブログがポストされました。
全文を訳すのは自信が無いので、、、気になるポイントだけ箇条書きにしました。
Pluton Key management
- Plutonは(シリコン)製造段階で自らのキーペアを生成しています。
- これらのキーはe-fusesに入っていて永続的に保持します。
- プライベートキーはソフトウェアで読み出すことはできません。(認められたファームウェアでも不可)
- Plutonは2つのECCキーペアを生成しており、1つはリモート認証用、もう1つは汎用暗号化用です。
- シリコン製造会社はマイクロソフトへパブリックキーを送付します。そうすることで、マイクロソフトは全てのAzure Sphere MCUを信頼します。
Pluton’s random number generator
- Plutonは真の乱数ジェネレータを実装しています。(環境情報を使っている?)
- Plutonの乱数ジェネレータはエントロピーを測定して、基準に満たない場合は乱数を配布しません。
Pluton’s cryptographic helpers
- 暗号化のタスクを(ハードウェアで実装することで?)高速化しています。
- Hashing(via SHA2), ECC, AES
The benefits of secure boot
Leveraging remote attestation
- Plutonは、測定されたブートとリモートアテステーションのサポートをシリコンで実装しています。
手順が書かれているが、、、よくわからんorz
デバイスがAS3をサーバー認証するだけでなく、AS3がデバイスを認証する、、、あたりの動きかな?
デバイスが不健全なときは、AS3しか接続できない。(ソフトウェアの更新で回復)
A future blog post will provide a more thorough deep dive on this topic
Silicon security beyond Pluton
mbed-os-example-wifiを動かしてみた
MbedでAZ3166をやってみる記事を書きましたが、、、
ここで使った、mbed-wifi-example、、、 というか、これの内部で使っているmbed-emw10xx-driverは、もう古いモノでしたorz
いまは、Officialのmbed-osにMTB_MXCHIP_EMW3166
としてマージされています。
ネットワークの部分は、WICEDがコンパイル済みの状態で同梱されていました。
mbed-os/targets/TARGET_WICED at master · ARMmbed/mbed-os · GitHub
で、動かし方ですが、、、
mbed import https://github.com/ARMmbed/mbed-os-example-wifi
して、mbed_app.jsonをこんな感じにして、
{ "config": { "wifi-ssid": { "help": "WiFi SSID", "value": "\"aterm-3b8988-g\"" }, "wifi-password": { "help": "WiFi Password", "value": "\"P@ssw0rd\"" } }, "target_overrides": { "*": { "platform.stdio-convert-newlines": true, "target.stdio_uart_tx": "PA_11", "target.stdio_uart_rx": "PA_12", "platform.stdio-baud-rate": 115200 } } }
コンパイルすればOK。
ざっくり調べた感じだと、邪魔だったbootloaderやMiCOが入っていないようで、スッキリとしています。
ちょっと注意が必要なのが、
mbed-wifi-exampleを動かしてみた
本記事は、同様の新しい記事があります。
新しい記事はこちらです。
MbedプラットフォームはAZ3166に対応しているみたいですが、
MxChip Board Support - Question | Mbed
情報が全然見当たらない。orz
GitHubにMXCHIPがサンプルを公開していたので、、、これが動くか(動かせるか)試してみました。
1回目
README.mdを参考に、mbed-cliでimportします。
C:\mbed>mbed import https://github.com/MXCHIP/mbed-wifi-example [mbed] Importing program "mbed-wifi-example" from "https://github.com/MXCHIP/mbed-wifi-example" at latest revision in the current branch [mbed] Adding library "emw10xx-driver" from "https://github.com/MXCHIP/mbed-emw10xx-driver" at rev #254df6a98312 [mbed] Adding library "mbed-os" from "https://github.com/MXCHIP/mbed-os" at rev #c1a665816660 C:\mbed>
何事も無く終了。
続いて、コンパイルします。
C:\mbed>cd mbed-wifi-example C:\mbed\mbed-wifi-example>mbed compile -m AZ3166 -t GCC_ARM Building project mbed-wifi-example (AZ3166, GCC_ARM) Scan: . Scan: FEATURE_BLE Scan: FEATURE_COMMON_PAL Scan: FEATURE_LWIP Scan: FEATURE_UVISOR Scan: FEATURE_ETHERNET_HOST Scan: FEATURE_LOWPAN_BORDER_ROUTER Scan: FEATURE_LOWPAN_HOST Scan: FEATURE_LOWPAN_ROUTER Scan: FEATURE_NANOSTACK Scan: FEATURE_NANOSTACK_FULL Scan: FEATURE_THREAD_BORDER_ROUTER Scan: FEATURE_THREAD_END_DEVICE Scan: FEATURE_THREAD_ROUTER Scan: FEATURE_STORAGE Scan: mbed Scan: env Compile [ 0.3%]: iperf_cli.c [Warning] cpu.h@21,0: "BYTE_ORDER" redefined [Error] sockets.h@317,8: redefinition of 'struct timeval' [Error] mico_socket.h@165,5: conflicting types for 'select' [ERROR] In file included from ./emw10xx-driver/mico/net/LwIP/lwip-sys/arch/cc.h:21:0, from ./emw10xx-driver/mico/net/LwIP/lwip-ver1.4.0.rc1/src/include/lwip/arch.h:43, from ./emw10xx-driver/mico/net/LwIP/lwip-ver1.4.0.rc1/src/include/lwip/debug.h:35, from ./emw10xx-driver/mico/net/LwIP/lwip-ver1.4.0.rc1/src/include/lwip/opt.h:46, from ./emw10xx-driver/mico/net/LwIP/lwip-ver1.4.0.rc1/src/include/lwip/sockets.h:37, from ./emw10xx-driver/mico/include/mico_socket.h:21, from ./emw10xx-driver/mico/include/mico.h:38, from .\app\iperf\iperf_cli.c:17: ...
struct timeval
を再定義してエラー...。
2回目
ソースを見たところ、LWIP_TIMEVAL_PRIVATE
マクロで定義するかしないか指定できるようになっていました。
mbed_app.jsonのmacros
に下記を追加して、再コンパイルします。
"LWIP_TIMEVAL_PRIVATE=0"
C:\mbed\mbed-wifi-example>mbed compile -m AZ3166 -t GCC_ARM Building project mbed-wifi-example (AZ3166, GCC_ARM) ... +-----------------------+--------+-------+-------+ | Module | .text | .data | .bss | +-----------------------+--------+-------+-------+ | Fill | 406 | 15 | 132 | | Misc | 158909 | 2824 | 51169 | | drivers | 2607 | 4 | 164 | | features/filesystem | 627 | 0 | 0 | | features/netsocket | 3922 | 85 | 60 | | hal | 536 | 0 | 8 | | platform | 1927 | 4 | 297 | | rtos | 213 | 4 | 4 | | rtos/rtx | 8079 | 20 | 6874 | | targets/TARGET_MXCHIP | 2578 | 4 | 0 | | targets/TARGET_STM | 21876 | 0 | 1424 | | Subtotals | 201680 | 2960 | 60132 | +-----------------------+--------+-------+-------+ Allocated Heap: unknown Allocated Stack: unknown Total Static RAM memory (data + bss): 63092 bytes Total RAM memory (data + bss + heap + stack): 63092 bytes Total Flash memory (text + data + misc): 204640 bytes Image: .\BUILD\AZ3166\GCC_ARM\mbed-wifi-example.bin C:\mbed\mbed-wifi-example>
Warningはいくつか出ましたが、とりあえずコンパイル通りました。やった!
3回目
このサンプル、複数のサンプルが同封されていて、main()で呼び出す部分を変更して切り替える作りになっていました。
どれにしようかな、、、
一番単純そうなmbed_wifi
を動かすことにします。
int main( void ) { RUN_APPLICATION( mbed_wifi ); return 0; }
mbed_wifi
は、Wi-FiアクセスポイントのSSIDとパスワードが必要なので、mbed_app.jsonのwifi-ssid
とwifi-password
に書いておきます。
"config": { "wifi-ssid": { "help": "WiFi SSID", "value": "\"aterm-3b8988-g\"" }, "wifi-password": { "help": "WiFi Password", "value": "\"p@ssw0rd\"" } }
で、コンパイルを。
C:\mbed\mbed-wifi-example>mbed compile -m AZ3166 -t GCC_ARM
実行
コンパイルで出来上がった.binファイルをAZ3166ドライブへコピーすれば、実行されます。
C:\mbed\mbed-wifi-example>copy .\BUILD\AZ3166\GCC_ARM\mbed-wifi-example.bin d:\ 1 個のファイルをコピーしました。 C:\mbed\mbed-wifi-example>
実行結果はこちら。
WiFi example Scan: [493][SYSTEM: .\emw10xx-driver\mico\system\system_misc.c: 230] Kernel version: 31620002.049 [501][SYSTEM: .\emw10xx-driver\mico\system\system_misc.c: 233] MiCO version: 3.0.0 [508][SYSTEM: .\emw10xx-driver\mico\system\system_misc.c: 235] Wi-Fi driver version wl0: Sep 10 2014 11:28:46 version 5.90.230.10 , mac C8:93:46:84:19:CC Network: aterm-3b8988-g secured: WPA2 BSSID: 1C:B1:7F:bf:61:ce RSSI: -63 Ch: 5 1 networks available. Connecting... [9332][SYSTEM: .\emw10xx-driver\mico\system\system_misc.c: 73] Station up Success MAC: c8:93:46:84:19:cc IP: 192.168.0.6 Netmask: 255.255.255.0 Gateway: 192.168.0.1 RSSI: -56 Sending HTTP request to www.arm.com... sent 38 [GET / HTTP/1.1] recv 64 [HTTP/1.1 301 Moved Permanently] Done [9733][SYSTEM: .\emw10xx-driver\mico\system\system_misc.c: 77] Station down
まとめ
Azure SphereのMutable storage
18.11で64Kバイトまでの永続データを保存できる、Mutable storageというBETA機能が追加されました。
18.11全容についてはこちら。
ちょっと使ってみようかなと思い、試してみました。
サンプルコード
Mutable storageを削除して、10回書き込みしてみます。
それぞれの処理時間をロジックアナライザで測定するので、GPIO0にHIGH/LOWを出力しましょう。
Mutable storageの関数の定義は、applibs/stoage.hです。
app_manifest.jsonにMutableStorageの設定が必要なので忘れずに。
また、コーディングの手間を減らすために、GPIOはeasyioライブラリを使いました。
#include <applibs/storage.h>
int main(int argc, char *argv[]) { Log_Debug("Application starting.\n"); void* led; void* button; led = DigitalOut_new(0, 0); button = DigitalIn_new(MT3620_RDB_BUTTON_A); Log_Debug("Please push button A.\n"); while (DigitalIn_read(button) == 1) {} Log_Debug("Executing...\n"); static uint8_t data[1]; for (int i = 0; i < sizeof(data); i++) data[i] = (uint8_t)(i % 256); DigitalOut_write(led, 1); // GPIO0 = HIGH Storage_DeleteMutableFile(); DigitalOut_write(led, 0); // GPIO0 = LOW wait_ms(10); // Sleep 10msec. for (int i = 0; i < 10; i++) { DigitalOut_write(led, 1); // GPIO0 = HIGH int fd = Storage_OpenMutableFile(); assert(fd >= 0); int size = write(fd, data, sizeof(data)); assert(size == sizeof(data)); close(fd); DigitalOut_write(led, 0); // GPIO0 = LOW wait_ms(10); // Sleep 10msec. } Log_Debug("Application exiting.\n"); return 0; }
{ "SchemaVersion": 1, "Name" : "Mt3620App1", "ComponentId" : "19c6015f-0e17-49c5-b687-xxxxxxxxxxxx", "EntryPoint": "/bin/app", "CmdArgs": [], "Capabilities": { "AllowedConnections": [], "AllowedTcpServerPorts": [], "AllowedUdpServerPorts": [], "Gpio": [ 0, 12 ], "Uart": [], "WifiConfig": false, "NetworkConfig": false, "SystemTime": false, "MutableStorage": { "SizeKB": 64 } } }
ロジックアナライザ測定
いつものロジアナ(saleae logic)で、GPIO0を測定します。
本来、Groveシールドは必要ありませんが、、、ピン番号探すのが面倒だったので、GroveシールドとGroveジャンパケーブルを使って接続しました。
測定した波形は、こんな感じ。
測定結果
書き込むサイズを1~64Kに変化させて測った結果、
- 削除は40ミリ秒
- 初回書き込みは117~1827ミリ秒
- 2回目以降書き込みは40~5975ミリ秒
でした。
まとめ
- Mutable storage、BETAだけど動いた。
- 2回目以降の書き込みが遅い。書き込むときは削除→書き込みが良さそう。
- 書き込み時間はデータ量にだいたい比例。ざっくりと30ミリ秒/バイト。
nanoFrameworkのファームウェアビルド
.NET Micro Framework後継の一つ、nanoFramework。
https://www.slideshare.net/TakashiMatsuoka2/tinyclr-os-on-wio-lte/9
何気なくドキュメントを見たら、ファームウェアのビルド手順が書かれていて、わたしでも出来るかも?と思い、やってみました。
参考にするドキュメントはこちら。
ターゲットボードは、手元に有るSTM32F4DISCOVERY
とします。
ビルド環境のセットアップ
ディレクトリを作成
C:\>mkdir C:\nanoFramework C:\>mkdir C:\nanoFramework_Tools
Visual Studio Codeをインストール
既にインストールされていました。
CMakeをコピー
CMakeサイトからcmake-3.13.1-win64-x64.zip
をダウンロードして、C:\nanoFramework_Tools\CMake
に解凍します。
Ninjaをコピー
ninjaのReleaseからv1.8.2のninja-win.zip
をダウンロードして、C:\nanoFramework_Tools\Ninja
に解凍します。
GNU ARM Embedded Toolchainをコピー
GNU-RM Downloadsからgcc-arm-none-eabi-7-2018-q2-update-win32.zip
をダウンロードして、C:\nanoFramework_Tools\GNU_ARM_Toolchain
に解凍します。
ChibiOSをコピー
ChibiOSのSourceForgeから
ChibiOS_18.2.1.zip
だと、nf-interpreterが想定しているものより古いバージョンのためコンパイルエラーになりました。(-.-) githubから取りましょう。
ChibiOSのstable_18.2.xブランチをzipダウンロードして、
C:\nanoFramework_Tools\ChibiOS-stable_18.2.x
に解凍します。
nf-interpreterをクローン
デフォルトのdevelopブランチだと、UART周辺でコンパイルエラーになりました。(-.-) masterブランチを明示的に指定しましょう。
githubのnanoframework/nf-interpreterをクローンします。
C:\>cd C:\nanoFramework C:\nanoFramework>git clone -b master https://github.com/nanoframework/nf-interpreter.git
Visual Studio Codeのセットアップ
Visual Studio Code拡張をインストール
下記URL先のInstallをクリックして、Visual Studio Code拡張をインストールします。
CMake-variants.jsonを設定
C:\nanoFramework\nf-interpreter\cmake-variants.json
を作成します。
{ "buildType": { "default": "MinSizeRel", "choices": { "debug": { "short": "Debug", "long": "Emit debug information without performing optimizations", "buildType": "Debug" }, "release": { "short": "Release", "long": "Enable optimizations, omit debug info", "buildType": "Release" }, "minsize": { "short": "MinSizeRel", "long": "Optimize for smallest binary size", "buildType": "MinSizeRel" }, "reldeb": { "short": "RelWithDebInfo", "long": "Perform optimizations AND include debugging information", "buildType": "RelWithDebInfo" } } }, "linkage": { "default": "", "choices": { "ST_STM32F4_DISCOVERY": { "short": "ST_STM32F4_DISCOVERY", "settings": { "BUILD_VERSION" : "0.9.99.999", "TOOLCHAIN_PREFIX" : "C:/nanoFramework_Tools/GNU_ARM_Toolchain", "TARGET_SERIES" : "STM32F4xx", "CHIBIOS_SOURCE" : "C:/nanoFramework_Tools/ChibiOS-stable_18.2.x", "CHIBIOS_BOARD" : "ST_STM32F4_DISCOVERY", "SWO_OUTPUT" : "ON", "USE_RNG" : "ON", "NF_FEATURE_DEBUGGER" : "ON", "NF_FEATURE_RTC" : "ON", "API_Windows.Devices.Adc" : "ON", "API_Windows.Devices.Gpio" : "ON", "API_Windows.Devices.Spi" : "ON", "API_Windows.Devices.I2c" : "ON", "API_Windows.Devices.Pwm" : "ON", "API_Windows.Devices.SerialCommunication" : "ON" } } } } }
settings.jsonを設定
C:\nanoFramework\nf-interpreter\.vscode\settings.json
を作成します。
{ "cmake.generator": "Ninja", "cmake.configureSettings": { "CMAKE_MAKE_PROGRAM":"C:/nanoFramework_Tools/ninja/ninja.exe" }, "cmake.cmakePath": "C:/nanoFramework_Tools/CMake/bin/cmake.exe" }
nanoBooterとnanoCLRをビルド
Visual Studio Codeを起動して、ファイル -> フォルダを開く、でC:\nanoFramework\nf-interpreter
を開きます。
Would you like to configure this project?
というメッセージウィンドウが表示されたら、Not now
をクリックします。
Never configure projects on opening?
というメッセージウィンドウが表示されたら、For this Workspace
をクリックします。
メッセージが何も表示されなくなったら、ステータスバーのNo Kit Selected
をクリックして、
[Unspecified]
を選択します。
ステータスバーのBuild
をクリックします。
2分ちょっとでビルド完了。
nanoBooter.hex
とnanoCLR.hex
が出来上がりました。
azure_iot_utilities.cのルートCA証明書
Azure SphereからAzure IoT Hubに接続するために、デバイスにルートCA証明書が必要です。
厳密には、Azure Sphereに限らず、Azure IoT Hubに接続するクライアントには、ルートCA証明書が必要。
この記事を書いたときはVisual Studioの接続済みサービス追加で自動生成されたazure_iot_utilities.cにあるルートCA証明書(Baltimore)をコピーしたのですが、、、
最近、証明書の有効期限が話題になっていてちょっと心配だったので、このルートCA証明書の有効期限を確認しました。
SSL証明書の内容を確認するには?
証明書の内容を表示する方法が分からず、ググってみたところ、、、
便利サイトがありました!
WebページにSSL証明書を貼り付けて、クリックするだけ!
なんと便利な、、、
例の証明書を確認
証明書を貼り付けて、ポチっと。
でました!
Baltimore CyberTrust Rootで、有効期限は2025/5。
Azure IoT Hub Sampleの調査メモ
Azure Sphere SDKに含まれている、Azure IoT Hub Sample for MT3620 RDB (Azure Sphere)プロジェクトのコードを調べます。
main()から、Azureに絡む関数のコールツリーはこんな感じ。
main() InitPeripheralsAndHandlers() AzureIoT_Initialize() AzureIoT_SetMessageReceivedCallback(&MessageReceived); AzureIoT_SetDeviceTwinUpdateCallback(&DeviceTwinUpdate); AzureIoT_SetDirectMethodCallback(&DirectMethodCall); AzureIoT_SetConnectionStatusCallback(&IoTHubConnectionStatusChanged); CreateTimerFdAndAddToEpoll(,, &azureIotEventData,); WaitForEventAndCallHandler() ClosePeripheralsAndHandlers() AzureIoT_DestroyClient(); AzureIoT_Deinitialize();
SetXxxCallback()で、コールバック関数を設定している。
あと、タイマーイベントでAzureIotDoWorkHandler()へのコールバックも有り。
static event_data_t azureIotEventData = {.eventHandler = &AzureIotDoWorkHandler};
static void AzureIotDoWorkHandler(event_data_t *eventData) { ... if (AzureIoT_SetupClient()) { AzureIoT_DoPeriodicTasks(); } }
ボタン押したらメッセージ送信している部分はこれ。
static void SendMessageToIotHub(void) { ... AzureIoT_SendMessage("Hello from Azure IoT sample!"); ... }
ざっと見た感じ、AzureIoT_xxxxという関数を呼んでいる。
AzureIoT_xxxxは、ウィザード?が生成したazure_iot_utilities.h/.cで定義されている。
使っている関数の、azure_iot_utilitiesの中を見るとこんな感じ。
AzureIoT_Initialize() IoTHub_Init() AzureIoT_SetupClient() IoTHubDeviceClient_LL_CreateFromConnectionString() IoTHubDeviceClient_LL_SetOption() IoTHubDeviceClient_LL_SetMessageCallback() IoTHubDeviceClient_LL_SetDeviceMethodCallback() IoTHubDeviceClient_LL_SetDeviceTwinCallback() IoTHubDeviceClient_LL_SetConnectionStatusCallback() IoTHubDeviceClient_LL_SetRetryPolicy() AzureIoT_DoPeriodicTasks() IoTHubDeviceClient_LL_DoWork() AzureIoT_SendMessage() IoTHubMessage_CreateFromString() IoTHubDeviceClient_LL_SendEventAsync() IoTHubMessage_Destroy() AzureIoT_DestroyClient() IoTHubDeviceClient_LL_Destroy() AzureIoT_Deinitialize() IoTHub_Deinit()