せっかくSHAとAESのハードウェアアクセラレータが使えるようになったので、WiFiを使ったリモートスイッチを暗号化して、セキュアなWiFiリモートスイッチを作ってみました。クライアント側のスケッチ、サーバ側のスケッチを2つ用意し、それぞれのフォルダに共通ライブラリをコピー&ペーストしてコンパイルしてください。
接続
クライアント側の25Pinにスイッチ、26PinにLEDと抵抗をつけます。LEDと抵抗は通信エラーが起きたときのインジケータなので、なくても大丈夫です。
サーバ側の25PinにLEDと抵抗をつけます。
クライアント側のスケッチ
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
secure WiFi remote control Client | |
Challenge-Handshake Authentication Protocol | |
https://en.wikipedia.org/wiki/Replay_attack | |
*/ | |
#include "ESP32E_AES_Hardware_Accelerator.h" | |
#include <WiFi.h> | |
const char* ssid = "your ssid"; | |
const char* password = "your password"; | |
char hostIP[] = "192.168.11.21"; | |
int hostPort = 4000; | |
uint32_t cipher_key[] = {0x2b7e1516, 0x28aed2a6, 0xabf71588, 0x09cf4f3c}; | |
RTC_DATA_ATTR int bootCount = 0; | |
WiFiClient client; | |
const int LED_PIN = 26; | |
AES_HardwareAccelerator AES; | |
void setup() { | |
uint8_t input[256]; | |
bootCount++; | |
pinMode(GPIO_NUM_25, INPUT_PULLUP); | |
Serial.begin(115200); | |
esp_sleep_wakeup_cause_t wakeup_reason; | |
wakeup_reason = esp_sleep_get_wakeup_cause(); | |
//Power on Reset or unknown wakeup reason | |
if (( bootCount == 0 ) || (wakeup_reason != ESP_SLEEP_WAKEUP_EXT0)) { | |
delay(1000); | |
Serial.println("initial boot"); | |
deepsleep(); | |
} | |
// WiFi connection | |
int timeout_count = 0; | |
WiFi.begin(ssid, password); | |
Serial.print("Connecting to WiFi"); | |
while (WiFi.status() != WL_CONNECTED) { | |
Serial.print("."); | |
timeout_count++; | |
delay(100); | |
if (timeout_count == 50) { | |
Serial.println("WiFi Connect failed."); | |
connectionfailed(); | |
deepsleep(); | |
} | |
} | |
Serial.println("WiFi Connected."); | |
Serial.printf("IP Address : "); | |
Serial.println(WiFi.localIP()); | |
Serial.println("Connect to server"); | |
uint32_t msg_LINK[] = {0x4C494E4B, 0x00000000, 0x00000000, 0x00000000}; //LINK | |
uint32_t msg_CHAL[] = {0x4348404C, 0x4C454E47, 0x00000000, 0x00000000}; //CHAL LENG | |
uint32_t msg_TGL[] = {0x4C454454, 0x474C0000, 0x00000000, 0x00000000}; //LEDTGL | |
uint32_t msg_ACK[] = {0x41434B00, 0x00000000, 0x00000000, 0x00000000}; //ACK | |
uint32_t msg_NACK[] = {0x4E41434B, 0x00000000, 0x00000000, 0x00000000}; //NACK | |
uint32_t declipt_msg[4]; | |
uint8_t senddata[16]; //output buffer for WiFi packet | |
uint8_t recvdata[16]; //input buffer for WiFi packet | |
//connect to server | |
int returncode = client.connect(hostIP, hostPort, 1000); | |
if (returncode != 1) { | |
Serial.print("connection failed"); | |
Serial.print(returncode); | |
connectionfailed(); | |
deepsleep(); | |
} | |
client.setTimeout(1000); | |
//1 : to Server, Link Request | |
encrypt_byte(cipher_key, msg_LINK, senddata); | |
client.write(senddata,sizeof(senddata)); | |
//2: from Server, get challange code from server | |
while (client.available() == 0) { } | |
client.read(recvdata, sizeof(recvdata)); | |
decrypt_byte(cipher_key, recvdata, declipt_msg); | |
if ((declipt_msg[0] != 0x4348404C) || (declipt_msg[1] != 0x4C454E47)) { | |
Serial.println("response error"); | |
connectionfailed(); | |
client.stop(); | |
deepsleep(); | |
} | |
//3: to Server, send "LED msg_TGL" command + challenge code | |
msg_TGL[3] = declipt_msg[3]; | |
encrypt_byte(cipher_key, msg_TGL, senddata); | |
client.write(senddata, sizeof(senddata)); | |
//4: from Server, get "ACK" | |
while (client.available() == 0) { } | |
client.read(recvdata, sizeof(recvdata)); | |
decrypt_byte(cipher_key, recvdata, declipt_msg); | |
if (declipt_msg[0] == 0x41434B00) { //ACK | |
Serial.println("SUCCESS"); | |
} else { | |
Serial.println("FAIL"); | |
} | |
client.stop(); | |
deepsleep(); | |
} | |
void loop() { | |
} | |
void deepsleep() { | |
WiFi.disconnect(true);//disconnect WiFi as it's no longer needed | |
WiFi.mode(WIFI_OFF); | |
while (digitalRead(25) == LOW) { } | |
esp_sleep_enable_ext0_wakeup(GPIO_NUM_25, LOW); | |
Serial.println("Going to sleep now"); | |
esp_deep_sleep_start(); | |
} | |
void connectionfailed() { | |
Serial.println("Connection failed."); | |
for (int i = 0; i < 5; i++) { | |
digitalWrite(LED_PIN, HIGH); | |
delay(200); | |
digitalWrite(LED_PIN, LOW); | |
delay(200); | |
} | |
} | |
void encrypt_byte(uint32_t * cipher_key, uint32_t * text, uint8_t * c_text) { | |
AES.enable(); | |
AES.setMode_AES128Encrypt(); | |
AES.setCipherKey(cipher_key); | |
AES.setTextReg(text); | |
AES.start(); | |
AES.getTextReg(c_text); | |
AES.disable(); | |
} | |
void decrypt_byte(uint32_t * cipher_key, uint8_t * c_text, uint32_t * text) { | |
AES.enable(); | |
AES.setMode_AES128Decrypt(); | |
AES.setCipherKey(cipher_key); | |
AES.setTextReg(c_text); | |
AES.start(); | |
AES.getTextReg(text); | |
AES.disable(); | |
} |
サーバ側のスケッチ
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
secure WiFi remote control Server | |
Challenge-Handshake Authentication Protocol | |
https://en.wikipedia.org/wiki/Replay_attack | |
*/ | |
#include "ESP32E_AES_Hardware_Accelerator.h" | |
#include <WiFi.h> | |
const char* ssid = "your ssid"; | |
const char* password = "your password"; | |
int hostPort = 4000; | |
WiFiServer server(hostPort); | |
const int LED_PIN = 25; | |
uint32_t cipher_key[] = {0x2b7e1516, 0x28aed2a6, 0xabf71588, 0x09cf4f3c}; | |
AES_HardwareAccelerator AES; | |
void setup() { | |
Serial.begin(115200); | |
pinMode(LED_PIN, OUTPUT); | |
digitalWrite(LED_PIN, LOW); | |
delay(500); | |
Serial.println(); | |
WiFi.begin(ssid, password); | |
Serial.print("connecting WiFi"); | |
while (WiFi.status() != WL_CONNECTED) { | |
Serial.print("."); | |
delay(100); | |
} | |
Serial.println(); | |
Serial.println("WiFi Connected."); | |
Serial.printf("IP Address : "); | |
Serial.println(WiFi.localIP()); | |
server.begin(); | |
Serial.println("start server"); | |
randomSeed(100); | |
} | |
void loop() { | |
uint32_t msg_LINK[] = {0x4C494E4B, 0x00000000, 0x00000000, 0x00000000}; //LINK | |
uint32_t msg_CHAL[] = {0x4348404C, 0x4C454E47, 0x00000000, 0x00000000}; //CHAL LENG | |
uint32_t msg_TGL[] = {0x4C454454, 0x474C0000, 0x00000000, 0x00000000}; //LEDTGL | |
uint32_t msg_ACK[] = {0x41434B00, 0x00000000, 0x00000000, 0x00000000}; //ACK | |
uint32_t msg_NACK[] = {0x4E41434B, 0x00000000, 0x00000000, 0x00000000}; //NACK | |
uint32_t declipt_msg[4]; | |
uint8_t senddata[16]; //output buffer for WiFi packet | |
uint8_t recvdata[16]; //input buffer for WiFi packet | |
WiFiClient client = server.available(); // listen for incoming clients | |
if (client) { | |
Serial.println("New Client connected."); | |
while (client.connected()) { // loop while the client's connected | |
client.setTimeout(1000); | |
while (client.available() == 0) { } | |
//1: from client, Link Request | |
client.read(recvdata, sizeof(recvdata)); | |
decrypt_byte(cipher_key, recvdata, declipt_msg); | |
if (declipt_msg[0] != msg_LINK[0]) { | |
client.println("NACK"); | |
client.stop(); | |
Serial.println("client disconnected."); | |
break; | |
} | |
//2: to client, generate charrange code and send | |
uint32_t challenge = random(0xFFFFFFFF); | |
msg_CHAL[3] = challenge; | |
encrypt_byte(cipher_key, msg_CHAL, senddata); | |
client.write(senddata, sizeof(senddata)); | |
//3: from client, command and challenge code | |
while (client.available() == 0) { } | |
client.read(recvdata, sizeof(recvdata)); | |
decrypt_byte(cipher_key, recvdata, declipt_msg); | |
//4 to client, ACK /NACK | |
if (declipt_msg[3] == challenge) { | |
if ((declipt_msg[0] == 0x4C454454) && (declipt_msg[1] == 0x474C0000)) { //LEDTGL | |
led_toggle(); | |
msg_ACK[3] = challenge; | |
encrypt_byte(cipher_key, msg_ACK, senddata); | |
} | |
} else { | |
msg_NACK[3] = challenge; | |
encrypt_byte(cipher_key, msg_NACK, senddata); | |
} | |
client.write(senddata, sizeof(senddata)); | |
client.stop(); | |
Serial.println("client disconnected."); | |
} | |
} | |
delay(1); | |
} | |
void encrypt_byte(uint32_t * cipher_key, uint32_t * text, uint8_t * c_text) { | |
AES.enable(); | |
AES.setMode_AES128Encrypt(); | |
AES.setCipherKey(cipher_key); | |
AES.setTextReg(text); | |
AES.start(); | |
AES.getTextReg(c_text); | |
AES.disable(); | |
} | |
void decrypt_byte(uint32_t * cipher_key, uint8_t * c_text, uint32_t * text) { | |
AES.enable(); | |
AES.setMode_AES128Decrypt(); | |
AES.setCipherKey(cipher_key); | |
AES.setTextReg(c_text); | |
AES.start(); | |
AES.getTextReg(text); | |
AES.disable(); | |
} | |
void led_toggle() { | |
if (digitalRead(LED_PIN) == HIGH) digitalWrite(LED_PIN, LOW); | |
else digitalWrite(LED_PIN, HIGH); | |
} |
共通ライブラリ
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* FILE: ESP32E_AES_Hardware_Accelerator.h | |
* AUTHOR: PiT | |
* PURPOSE: Arduino library for ESP32E AES Hardware Accelerator | |
* VERSION: 1.0.0 | |
*/ | |
#define DPORT_PERI_CLK_EN_REG *((volatile uint32_t *)0x3FF0001C) | |
#define DPORT_PERI_RST_EN_REG *((volatile uint32_t *)0x3FF00020) | |
#define AES_MODE_REG *((volatile uint32_t *)0x3FF01008) | |
#define AES_ENDIAN_REG *((volatile uint32_t *)0x3FF01040) | |
#define AES_KEY_0_REG *((volatile uint32_t *)0x3FF01010) | |
#define AES_KEY_1_REG *((volatile uint32_t *)0x3FF01014) | |
#define AES_KEY_2_REG *((volatile uint32_t *)0x3FF01018) | |
#define AES_KEY_3_REG *((volatile uint32_t *)0x3FF0101C) | |
#define AES_KEY_4_REG *((volatile uint32_t *)0x3FF01020) | |
#define AES_KEY_5_REG *((volatile uint32_t *)0x3FF01024) | |
#define AES_KEY_6_REG *((volatile uint32_t *)0x3FF01028) | |
#define AES_KEY_7_REG *((volatile uint32_t *)0x3FF0102C) | |
#define AES_TEXT_0_REG *((volatile uint32_t *)0x3FF01030) | |
#define AES_TEXT_1_REG *((volatile uint32_t *)0x3FF01034) | |
#define AES_TEXT_2_REG *((volatile uint32_t *)0x3FF01038) | |
#define AES_TEXT_3_REG *((volatile uint32_t *)0x3FF0103C) | |
#define AES_START_REG *((volatile uint32_t *)0x3FF01000) | |
#define AES_IDLE_REG *((volatile uint32_t *)0x3FF01004) | |
#define AES_MODE_AES128ENCRYPTION 0x00000000 | |
#define AES_MODE_AES192ENCRYPTION 0x00000001 | |
#define AES_MODE_AES256ENCRYPTION 0x00000002 | |
#define AES_MODE_AES128DECRYPTION 0x00000004 | |
#define AES_MODE_AES192DECRYPTION 0x00000005 | |
#define AES_MODE_AES256DECRYPTION 0x00000006 | |
#define AES_KEY_ENDIAN0 0x00000000 | |
#define AES_KEY_ENDIAN1 0x00000001 | |
#define AES_KEY_ENDIAN2 0x00000002 | |
#define AES_KEY_ENDIAN3 0x00000003 | |
#define AES_TEXT_ENDIAN0 0x00000000 | |
#define AES_TEXT_ENDIAN1 0x00000014 | |
#define AES_TEXT_ENDIAN2 0x00000028 | |
#define AES_TEXT_ENDIAN3 0x0000003C | |
class AES_HardwareAccelerator { | |
public: | |
void enable(); | |
void setMode_AES128Encrypt(); | |
void setMode_AES128Decrypt(); | |
void start(); | |
void setCipherKey(uint32_t * cipher_key); | |
void setTextReg(uint32_t * textblock); | |
void setTextReg(uint8_t * text); | |
void getTextReg(uint32_t * textblock); | |
void getTextReg(uint8_t * text); | |
void disable(); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* FILE: ESP32E_AES_Hardware_Accelerator.cpp | |
* AUTHOR: PiT | |
* PURPOSE: Arduino library for ESP32E AES Hardware Accelerator | |
* VERSION: 1.0.0 | |
*/ | |
#include <Arduino.h> | |
#include "ESP32E_AES_Hardware_Accelerator.h" | |
void AES_HardwareAccelerator::enable() { | |
DPORT_PERI_CLK_EN_REG = DPORT_PERI_CLK_EN_REG | 0x00000001; // peripheral clock enable | |
DPORT_PERI_RST_EN_REG = DPORT_PERI_RST_EN_REG & (~(0x00000001 | 0x00000008 | 0x00000010)); // peripheral reset | |
} | |
void AES_HardwareAccelerator::setMode_AES128Encrypt() { | |
AES_MODE_REG = AES_MODE_AES128ENCRYPTION; | |
AES_ENDIAN_REG = AES_KEY_ENDIAN2 | AES_TEXT_ENDIAN2; | |
} | |
void AES_HardwareAccelerator::setMode_AES128Decrypt() { | |
AES_MODE_REG = AES_MODE_AES128DECRYPTION; | |
AES_ENDIAN_REG = AES_KEY_ENDIAN2 | AES_TEXT_ENDIAN2; | |
} | |
void AES_HardwareAccelerator::start() { | |
AES_START_REG = 0x00000001; | |
while (AES_IDLE_REG) {} | |
} | |
void AES_HardwareAccelerator::setCipherKey(uint32_t * cipher_key) { | |
AES_KEY_0_REG = cipher_key[0]; | |
AES_KEY_1_REG = cipher_key[1]; | |
AES_KEY_2_REG = cipher_key[2]; | |
AES_KEY_3_REG = cipher_key[3]; | |
} | |
void AES_HardwareAccelerator::setTextReg(uint32_t * text) { | |
AES_TEXT_0_REG = text[0]; | |
AES_TEXT_1_REG = text[1]; | |
AES_TEXT_2_REG = text[2]; | |
AES_TEXT_3_REG = text[3]; | |
} | |
void AES_HardwareAccelerator::setTextReg(uint8_t * text) { | |
uint32_t temp; | |
temp = text[0]; | |
temp = (temp << 8) + text[1]; | |
temp = (temp << 8) + text[2]; | |
temp = (temp << 8) + text[3]; | |
AES_TEXT_0_REG = temp; | |
temp = text[4]; | |
temp = (temp << 8) + text[5]; | |
temp = (temp << 8) + text[6]; | |
temp = (temp << 8) + text[7]; | |
AES_TEXT_1_REG = temp; | |
temp = text[8]; | |
temp = (temp << 8) + text[9]; | |
temp = (temp << 8) + text[10]; | |
temp = (temp << 8) + text[11]; | |
AES_TEXT_2_REG = temp; | |
temp = text[12]; | |
temp = (temp << 8) + text[13]; | |
temp = (temp << 8) + text[14]; | |
temp = (temp << 8) + text[15]; | |
AES_TEXT_3_REG = temp; | |
} | |
void AES_HardwareAccelerator::getTextReg(uint32_t * text) { | |
text[0] = AES_TEXT_0_REG; | |
text[1] = AES_TEXT_1_REG; | |
text[2] = AES_TEXT_2_REG; | |
text[3] = AES_TEXT_3_REG; | |
} | |
void AES_HardwareAccelerator::getTextReg(uint8_t * text) { | |
uint32_t temp; | |
temp = AES_TEXT_0_REG; | |
text[0] = temp >> 24; | |
text[1] = temp >> 16; | |
text[2] = temp >> 8; | |
text[3] = temp; | |
temp = AES_TEXT_1_REG; | |
text[4] = temp >> 24; | |
text[5] = temp >> 16; | |
text[6] = temp >> 8; | |
text[7] = temp; | |
temp = AES_TEXT_2_REG; | |
text[8] = temp >> 24; | |
text[9] = temp >> 16; | |
text[10] = temp >> 8; | |
text[11] = temp; | |
temp = AES_TEXT_3_REG; | |
text[12] = temp >> 24; | |
text[13] = temp >> 16; | |
text[14] = temp >> 8; | |
text[15] = temp; | |
} | |
void AES_HardwareAccelerator::disable() { | |
DPORT_PERI_CLK_EN_REG = 0x00000000; // peripheral clock enable | |
DPORT_PERI_RST_EN_REG = 0x0000000F; // peripheral reset | |
} |
普通のWiFiリモコンとの違い
通信の内容をAES128を使って暗号化しています。
LEDを制御するコマンドがLEDTGLのみなので、通信するデータに乱数(Salt)をのせて、暗号文が通信毎に変わるようにしています。ただし、通信1発目のリンクリクエストについては、Saltを加えてもサーバは反応するしかないため、何もしていません。
サーバ側からワンタイムパスワード(Challenge)を送信し、クライアント側からはコマンドにワンタイムパスワードを加えた文字列を暗号化してサーバに送信します。サーバ側でワンタイムパスワードを比較することで、リプレイアタックされてもコマンドを受け付けないようにしています。
より詳しく知りたい方は、
リプレイアタック、チャレンジーハンドシェイク、CHAP
などを検索してください。
0 件のコメント:
コメントを投稿