Azure Sphere MCUのセキュア構造

12/20にPlutonの構造、機能に関するブログがポストされました。

azure.microsoft.com

全文を訳すのは自信が無いので、、、気になるポイントだけ箇条書きにしました。

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

  • ECDSA(ECCキーペアを使用してデジタル署名をチェックするためのアルゴリズム)でチェックしている?
  • 全てのソフトウェア(ピース)はMicrosoftの署名が必要です。

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

  • セキュリティは、Plutonを構成するシリコン実装にとどまらない。
  • 5コアによる多層防御。
  • A7コアはArmのTrust Zoneテクノロジーを利用している。
  • ファイアウォール、全てのリソースはSecure Worldにマップされている。
  • 一度設定されたマップはロックされます。

mbed-os-example-wifiを動かしてみた

MbedでAZ3166をやってみる記事を書きましたが、、、

matsujirushi.hatenablog.jp

ここで使った、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オンラインコンパイラMTB_MXCHIP_EMW3166は指定できません。mbed-cliを使います。
  • MTB_MXCHIP_EMW3166はEMW3166用なので、AZ3166のときは適切にピンを変更します。(先のstdio\uart_xxがソレ)

mbed-wifi-exampleを動かしてみた

本記事は、同様の新しい記事があります。
新しい記事はこちらです。

MbedプラットフォームはAZ3166に対応しているみたいですが、

MxChip Board Support - Question | Mbed

情報が全然見当たらない。orz

GitHubにMXCHIPがサンプルを公開していたので、、、これが動くか(動かせるか)試してみました。

github.com

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.jsonmacrosに下記を追加して、再コンパイルします。

    "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.jsonwifi-ssidwifi-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

まとめ

  • mbed-wifi-example、少し手を加えればコンパイル、実行できる。
  • mbed-osはMXCHIPがforkしたものを使っている。AZ3166は本家にマージされていない。
  • ネットワークドライバはmbed-emw10xx-driverの、ほぼ最新を使っている。OwnerはMXCHIP。

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ジャンパケーブルを使って接続しました。

f:id:matsujirushix:20181216183333p:plain

測定した波形は、こんな感じ。

f:id:matsujirushix:20181216183501p:plain

測定結果

書き込むサイズを1~64Kに変化させて測った結果、

  • 削除は40ミリ秒
  • 初回書き込みは117~1827ミリ秒
  • 2回目以降書き込みは40~5975ミリ秒

でした。

f:id:matsujirushix:20181216183606p:plain

まとめ

  • Mutable storage、BETAだけど動いた。
  • 2回目以降の書き込みが遅い。書き込むときは削除→書き込みが良さそう。
  • 書き込み時間はデータ量にだいたい比例。ざっくりと30ミリ秒/バイト。

nanoFrameworkのファームウェアビルド

.NET Micro Framework後継の一つ、nanoFramework。

https://www.slideshare.net/TakashiMatsuoka2/tinyclr-os-on-wio-lte/9

何気なくドキュメントを見たら、ファームウェアのビルド手順が書かれていて、わたしでも出来るかも?と思い、やってみました。

参考にするドキュメントはこちら。

How to Build, Flash and Debug the STM32 nanoBooter and nanoCLR on Windows using Visual Studio Code | nanoFramework Documentation

ターゲットボードは、手元に有るSTM32F4DISCOVERYとします。

ビルド環境のセットアップ

Setting up the build environment for STM32

ディレクトリを作成

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に解凍します。

f:id:matsujirushix:20181208194914p:plain

Ninjaをコピー

ninjaのReleaseからv1.8.2のninja-win.zipをダウンロードして、C:\nanoFramework_Tools\Ninjaに解凍します。

f:id:matsujirushix:20181208195150p:plain

GNU ARM Embedded Toolchainをコピー

GNU-RM Downloadsからgcc-arm-none-eabi-7-2018-q2-update-win32.zipをダウンロードして、C:\nanoFramework_Tools\GNU_ARM_Toolchainに解凍します。

f:id:matsujirushix:20181208195939p:plain

ChibiOSをコピー

ChibiOSのSourceForgeからChibiOS_18.2.1.zipだと、nf-interpreterが想定しているものより古いバージョンのためコンパイルエラーになりました。(-.-) githubから取りましょう。

ChibiOSのstable_18.2.xブランチをzipダウンロードして、

f:id:matsujirushix:20181208200453p:plain

C:\nanoFramework_Tools\ChibiOS-stable_18.2.xに解凍します。

f:id:matsujirushix:20181208201046p:plain

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をクリックして、

f:id:matsujirushix:20181208203150p:plain

[Unspecified]を選択します。

f:id:matsujirushix:20181208203312p:plain

ステータスバーのBuildをクリックします。

f:id:matsujirushix:20181208211555p:plain

2分ちょっとでビルド完了。

f:id:matsujirushix:20181208211937p:plain

nanoBooter.hexnanoCLR.hexが出来上がりました。

f:id:matsujirushix:20181208212108p:plain

azure_iot_utilities.cのルートCA証明書

Azure SphereからAzure IoT Hubに接続するために、デバイスにルートCA証明書が必要です。

qiita.com

厳密には、Azure Sphereに限らず、Azure IoT Hubに接続するクライアントには、ルートCA証明書が必要。

この記事を書いたときはVisual Studioの接続済みサービス追加で自動生成されたazure_iot_utilities.cにあるルートCA証明書(Baltimore)をコピーしたのですが、、、

最近、証明書の有効期限が話題になっていてちょっと心配だったので、このルートCA証明書の有効期限を確認しました。

SSL証明書の内容を確認するには?

証明書の内容を表示する方法が分からず、ググってみたところ、、、
便利サイトがありました!

tech-unlimited.com

WebページにSSL証明書を貼り付けて、クリックするだけ!
なんと便利な、、、

例の証明書を確認

証明書を貼り付けて、ポチっと。

f:id:matsujirushix:20181129185903p:plain

でました!

Baltimore CyberTrust Rootで、有効期限は2025/5。

f:id:matsujirushix:20181129190033p:plain

Azure IoT Hub Sampleの調査メモ

Azure Sphere SDKに含まれている、Azure IoT Hub Sample for MT3620 RDB (Azure Sphere)プロジェクトのコードを調べます。

f:id:matsujirushix:20181125121854p:plain

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()