Skip to content
Shop

STM32 LoRa 署名付き通信

Explore with your favorite AI

Gemini & other AI

Copy this page’s context into your AI to explore how it works and what you can build.

Open Gemini

Copy the prompt, then paste it into Gemini’s message box.

View prompt

This content is not available in your language yet.

このチュートリアルではAC07 LoRa Maryリーフに搭載されるATECC608Aを使用して、LoRaの通信に署名をつける方法を紹介します。 通信に署名をつけることで、データの送り元が、受信者が意図したデバイスであるかどうかを判定することができ、通信の安全性が向上します。

このチュートリアルではLoRa送信機と受信機のペアが必要です。下記の構成で組み立てたリーフを2セット用意してください。

  • AC07 LoRa Mary
  • AP03 STM32
  • AZ01 USB

ATECC608Aで署名付き通信を行う仕組み

Section titled “ATECC608Aで署名付き通信を行う仕組み”

送信機と受信機に搭載されているAC07 LoRa Maryリーフには、Microchip社の暗号化チップATECC608Aが搭載されています。 このチップは楕円曲線DSA署名方式(ECDSA)を使用した署名をハードウェアの演算回路を使用して発行することができます。 ATECC608Aを使用した署名の発行と、データ通信の流れを図に表したものを下記に示します。 この図をもとに署名付き通信の流れを解説します。

まず初めに、署名の発行を行うためにATECC608Aの初期化をする必要があります。 この初期化手順では、署名を行うためのキーペアをATECC608Aに発行してもらいます。 STM32リーフからI2CでATECC608Aにキーペア発行命令を送り、公開鍵と秘密鍵のキーペアを作成します。この時、公開鍵はI2C経由でSTM32リーフに伝えられ読み出すことができますが、秘密鍵はATECC608A内部のメモリーにのみ保存され、STM32側は読み出すことができない仕組みとなっています。

読み出した公開鍵は、署名の検証に使うため、あらかじめ受信機側が知っている必要があります。受信機側にはソースコードに記述するなどの何らかの方法で公開鍵を渡しておく必要があります。

2. 署名を発行してデータを送る

Section titled “2. 署名を発行してデータを送る”

送信機側は「本日は晴天なり」というメッセージを送信したいとします。このメッセージを受信機に対して送る前にまず、ATECC608Aに送り、メッセージに対する署名を計算してもらいます。署名はメッセージと秘密鍵を用いて計算されます。この時、ATECC608Aの仕様により、メッセージは32バイトごとに分割し、それに対する署名はそれぞれ64バイトのサイズになります。

署名を発行し終えたら、メッセージと署名を合わせてLoRa(UARTなど別の通信方法でも良い)で送信し受信機に伝えます。

署名付きメッセージを受信した受信機は、あらかじめ持っていた送信機の公開鍵と、受信したメッセージ、署名データをATECC608Aに渡します。ATECC608Aはこれらのデータから署名がこの公開鍵に対応する秘密鍵によって発行されたものかを検証します。

4. 不正なデータが送られてきた

Section titled “4. 不正なデータが送られてきた”

ここで、悪意を持った人が「本日は大荒れなり」というデータを送ってきたとします。これにはなんらかの署名がつけられていたとします。この不正なデータについた署名は、本来想定している送信機の秘密鍵を用いて作られた署名ではないため、受信機は公開鍵をもとに検証を行うと、キーペアの不一致によって検証結果は不正となります。

この仕組みを使うことで、悪意のあるデータを見抜くことができます。

以下のサンプルは順番に実行してください。すべてのサンプルで共通のリーフ構成(AC07 + AP03 + AZ01)を使用します。

PlatformIOを使用してSTM32にプログラムを書き込んでください。PlatformIOの利用方法はPlatformIO IDE for VSCode の設定を参考にしてください。

ATECC608Aを初期化しキーペアを発行する

Section titled “ATECC608Aを初期化しキーペアを発行する”

ATECC608Aは出荷時キーペアが保存されていないため、以降すべてのサンプルを実行する際に必要となるキーペアをあらかじめ発行しておきます。このサンプルは必ず実施してください。

ソースコード:

シリアルコンソール上に次のように表示されることを確認して、yを送信してください。

Serial Number: 000000000000000000
Rev Number: 00000000
Config Zone: NOT Locked
Data/OTP Zone: NOT Locked
Data Slot 0: NOT Locked
Successful wakeUp(). I2C connections are good.
Would you like to configure your Cryptographic Co-processor with SparkFun Standard settings? (y/n)
***Note, this is PERMANENT and cannot be changed later***
***If you do not want to do this, type an 'n' or unplug now.***

以下のように設定され、

  • 各データ領域がロックされている
  • 公開鍵が発行されている

状態で設定終了です。

Configuration beginning.
Write Config: Success!
Lock Config: Success!
Key Creation: Success!
Lock Data-OTP: Success!
Lock Slot 0: Success!
Configuration done.
Serial Number: 000000000000000000
Rev Number: 00000000
Config Zone: Locked
Data/OTP Zone: Locked
Data Slot 0: Locked
This device's Public Key:
uint8_t publicKey[64] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

LoRaで通信を行う前に、まずは送信機単体でデータに署名をつけてみましょう。このサンプルはLoRa通信を行わず署名発行部分のみを抽出したシンプルなプロジェクトです。

ソースコード:

まずはソースコード上の以下の32バイトデータを、送信したいテキストに編集してください。

// array to hold our 32 bytes of message. Note, it must be 32 bytes, no more or less.
uint8_t message[32] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
};

編集後、ビルドとアップロードを行い、接続動作を確認します。

以下のように

  • 編集したメッセージ
  • 公開鍵
  • 署名

の順で出力されます。

uint8_t message[32] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
};
This device's Public Key:
uint8_t publicKey[64] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
uint8_t signature[64] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
Signature created. Check the serial monitor for the signature.

署名がついたデータを検証する

Section titled “署名がついたデータを検証する”

LoRaで通信を行う前に、まずは送信機単体で署名の検証を行ってみましょう。自分自身が発行した署名を自分で検証できるかを確認してみます。署名検証を行うための最もシンプルなプロジェクトです。

ソースコード:

前のステップ「データに署名をつける」で取得した以下の値をメモしておいてください。

  • 元のメッセージ
  • 公開鍵
  • 署名

ソースコード上の以下の32バイト、64バイト、64バイトの各データに、メモした値を置き換えてください。

// copy result from the serial monitor and paste it here
uint8_t message[32] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
};
uint8_t publicKey[64] = {
0xDB, 0x6A, 0x74, 0x7D, 0xF1, 0x03, 0x8B, 0x67, 0x4A, 0xA6, 0x2B, 0x01, 0xDC, 0xDB, 0x68, 0x8D,
0x85, 0xFC, 0x95, 0xF7, 0xDA, 0x7D, 0x77, 0xDC, 0xDE, 0x6C, 0x74, 0x74, 0xB6, 0xD9, 0x98, 0xAA,
0x82, 0x39, 0x59, 0xFD, 0xC0, 0x88, 0x83, 0x57, 0xE9, 0x81, 0xB4, 0x00, 0xFD, 0xF2, 0x2C, 0x73,
0x3A, 0xB5, 0x6D, 0x9D, 0xCE, 0x96, 0x6E, 0xFF, 0x47, 0x00, 0xAF, 0x2A, 0x80, 0xB1, 0x17, 0x44
};
uint8_t signature[64] = {
0x79, 0xFF, 0x7A, 0x5C, 0x3C, 0xDB, 0x3D, 0xBE, 0x32, 0x36, 0x7D, 0x9E, 0xA6, 0x54, 0x38, 0x11,
0xD2, 0x9B, 0x9F, 0x94, 0x04, 0x42, 0x7E, 0x98, 0x28, 0x8B, 0x3B, 0x96, 0x49, 0x1F, 0x26, 0x62,
0xBA, 0xD4, 0x78, 0x3C, 0x12, 0xE4, 0x73, 0x39, 0x80, 0x8A, 0xE8, 0x9A, 0x60, 0x4E, 0x4A, 0x28,
0x67, 0xF4, 0x69, 0x40, 0x93, 0xC1, 0xA2, 0x29, 0x2B, 0x18, 0xC2, 0x41, 0xC6, 0x08, 0xF0, 0x33
};

編集後、ビルドとアップロードを行い、接続動作を確認します。

署名やメッセージを特に改変していないならば、次のように検証が成功します。

Success! Signature Verified.

例えばメッセージの末尾を署名作成時から変更すると、次のように検証が失敗します。

Verification failure.

署名付きデータをLoRaで送信する

Section titled “署名付きデータをLoRaで送信する”

実際にLoRaで送信するデータに署名をつけてみましょう。LoRaを使用した実用的なプロジェクトです。

ソースコード:

以下のソースコードは、便宜上公開鍵をパケットに載せて検証していますが、通常はこのようなことはせず、あらかじめ公開鍵のやり取りをした上で通信を行なってください。

起動すると、30分おきに以下のように公開鍵、メッセージ、署名が送信されます。

Sending packet: 0
uint8_t publicKey[64] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
uint8_t message[32] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
};
uint8_t signature[64] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
end

次のステップ「署名付きデータをLoRaで受信して検証する」でデータを受信してください。

署名付きデータをLoRaで受信して検証する

Section titled “署名付きデータをLoRaで受信して検証する”

実際にLoRaで受信した署名付きデータを検証してみましょう。LoRaを使用した実用的なプロジェクトです。

ソースコード:

前のステップ「署名付きデータをLoRaで送信する」で署名つきメッセージを送信してください。

データを受信し、検証が成功したら以下のように表示されます。

Packet Size; 161
uint8_t publicKey[64] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
uint8_t message[32] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
};
uint8_t signature[64] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
Success! Signature verified correctly!