なかなか完全な在宅勤務にならず、今週は今日まで出勤でした。明日からは在宅予定。
先日、動画をアップしたLINEダッシュボタン ですが、WiFiのアクセスポイントとパスワード、LINE notifyのトークン、ボタンを押したときに送信するメッセージをスケッチ内に書くのではなく、microSDに格納するようにしました。プログラムを直さなくてもmicroSDの内容を書き換えれば、これらを変更できます。
平文でパスワードやトークンを書くのはリスキーなので簡単な暗号化を行いました。EXOR暗号とシーザー暗号のようなものを組み合わせています。以下のような感じで暗号化されます。
key :Open Sesame plain_ssid :WiFi_AccessPoint_SSID plain_passwd :SSID_password plain_token :LINE_TOKEN_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 crypted_ssid :)$GF$Z_5$D#'*D;B*SS#8#SY+%CL crypted_passwd:*%/O-J_F$##5)CK`(# crypted_token :#VGN-Z_*-FCG+VK1/E;M<47E1UCJ.C/`-U(S#F?C06CV)%C_0$$E8W$89(0[5(U
EXOR暗号は2回EXOR操作すると元に戻る性質を使って、暗号化と復号化を行うものです。まず、暗号化keyと平文テキストの文字のEXORを取ります。そのままだとascii文字で表現できないので、8ビット毎の文字列(をEXORした結果)から6ビット毎に取り出してascii文字に変換します。シーザー暗号は文字をずらして暗号化しますが、これは6ビット毎に取り出して文字をずらしています。復号はこの操作の逆を行います。
暗号化/復号化のスケッチは以下です。無保証です。暗号化はC言語などで書いてPCで実行すればよいですが、今回はarduino IDEで開発し、ESP32で動かしています。
//-------------------------------------------------------------- // 簡単なXOR暗号 '22.01.31 naka // 実行例) // key :Open Sesame // // plain_ssid :WiFi_AccessPoint_SSID // plain_passwd :SSID_password // plain_token :LINE_TOKEN_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 // // crypted_ssid :)$GF$Z_5$D#'*D;B*SS#8#SY+%CL // crypted_passwd:*%/O-J_F$##5)CK`(# // crypted_token :#VGN-Z_*-FCG+VK1/E;M<47E1UCJ.C/`-U(S#F?C06CV)%C_0$$E8W$89(0[5(U // // decrypt_ssid :WiFi_AccessPoint_SSID // decrypt_passwd:SSID_password // decrypt_token :LINE_TOKEN_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 //-------------------------------------------------------------- void setup() { char crypted_ssid[128],crypted_passwd[128],crypted_token[128]; char dec_ssid[128],dec_passwd[128],dec_token[128]; Serial.begin(115200); char* key = "Open Sesame"; char* plain_ssid = "WiFi_AccessPoint_SSID"; char* plain_passwd = "SSID_password"; char* plain_token = "LINE_TOKEN_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; encrypt(crypted_ssid,plain_ssid,key); encrypt(crypted_passwd,plain_passwd,key); encrypt(crypted_token,plain_token ,key); decrypt(dec_ssid,crypted_ssid,key); decrypt(dec_passwd,crypted_passwd,key); decrypt(dec_token,crypted_token,key); Serial.printf("key :%s\n\n",key); Serial.printf("plain_ssid :%s\n",plain_ssid); Serial.printf("plain_passwd :%s\n",plain_passwd); Serial.printf("plain_token :%s\n",plain_token); Serial.printf("\n"); Serial.printf("crypted_ssid :%s\n",crypted_ssid); Serial.printf("crypted_passwd:%s\n",crypted_passwd); Serial.printf("crypted_token :%s\n",crypted_token); Serial.printf("\n"); Serial.printf("decrypt_ssid :%s\n",dec_ssid); Serial.printf("decrypt_passwd:%s\n",dec_passwd); Serial.printf("decrypt_token :%s\n",dec_token); Serial.printf("\n"); sleep; } void loop() { } void encrypt( char*a, char*b, char* key) { char x[128]; int i,j,k,m; k = 0; int n = strlen(key); for (i=0; b[i]!=0; i++) { x[i] = b[i]^key[k++]; k %= n; } m = i; // 8bit -> 6bit // 8bit 0 1 2 // 000000|00, 0000|0000, 00|000000 // 6bit 0 1 2 3 j = 0; for (i=0; i<m; j++) { switch (j%4) { case 0: a[j] = (x[i]&0xFC)>>2; break; case 1: a[j] = (x[i]&0x03)<<4 | (x[i+1]&0xF0)>>4; i++; break; case 2: a[j] = (x[i]&0x0F)<<2 | (x[i+1]&0xC0)>>6; i++; break; case 3: a[j] = x[i]&0x3F; i++; break; } } // ascii文字に変換 for (i=0; i<j; i++) { a[i] += (int)'#'; } a[i] = 0; } void decrypt( char*a, char*b, char* key) { char x[128]; int i,j,k,m; // ascii文字をbinaryに戻す for(i=0; b[i]!=0; i++) { x[i] = b[i] - (int)'#'; } m = i - 1; // 6bit -> 8bit j = 0; for(i=0; j<m ; i++) { switch (i%3) { case 0: a[i] = (x[j]&0x3F)<<2 | (x[j+1]&0x30)>>4; j++; break; case 1: a[i] = (x[j]&0x0F)<<4 | (x[j+1]&0x3C)>>2; j++; break; case 2: a[i] = (x[j]&0x03)<<6 | x[j+1]&0x3F; j += 2; break; } } m = i; k = 0; int n = strlen(key); for (i=0; i<m; i++) { a[i] ^= key[k++]; k %= n; } a[i]= 0; }
microSDはESP32の起動時に読み込みます。エラーで読み込めない場合には赤色LEDが点滅したのち、点灯して止まります。正常に読み込めれば、ボタンを押すとWiFiに接続し(その間、緑LEDが点滅)、LINE notifyにメッセージを送り(赤色LED点灯)、WiFi接続を切ります(緑と赤LED消灯)。
回路図とスケッチです。無保証です。
//--------------------------------------------------------------- // 2022.02.02 naka // LINE Dash Button // // ・WiFi ssid,password,LINE notify tokenは、暗号化してmicroSDに格納 // ファイル名:setup.txt // 暗号化したWiFi ssid, password, LINE notify tokenをそれぞれ1行ずつ // 順に記載し、4行目に送るメッセージを記載。 // (例) // &D/)%X/C04S+#T'?+USM // *%/O-J_F$##5)CK`(% // #VGN-Z_*-FCG+VK1/E;M<47E1UCJ.C/`-U(S#F?C06CV)%C_0$$E8W$89(0[5(U // ボタンが押されました。 //--------------------------------------------------------------- #include <WiFi.h> #include <WiFiClientSecure.h> #include <SD.h> #define SD_CS 16 // SD card chip select #define SW 25 #define LED_G 33 #define LED_R 32 #define ON 1 #define OFF 0 #define FILENAME "/setup.txt" // ssid,pw,tokenを暗号化したときのキー char* crypt_key = "Open Sesame"; // LINE Notify const char* host = "notify-api.line.me"; char ssid[64],password[64],token[64],message[128]; int sw_newest; void setup() { Serial.begin(115200); pinMode(SW,INPUT_PULLUP); pinMode(LED_G,OUTPUT); pinMode(LED_R,OUTPUT); led_green(OFF); led_red(OFF); sw_newest = digitalRead(SW); if (read_setup_file(FILENAME)) { blink_red(10); led_red(ON); sleep; } } void loop(){ if (sw_FallingEdge()) { wifi_connect(); // WiFi接続 send_msg2line(); // LINE notifyにメッセージを送る wifi_disconnect(); // WiFi接続解除 while(!digitalRead(SW)); delay(100); // チャタリング対策 } } int sw_FallingEdge() { int prev = sw_newest; sw_newest = digitalRead(SW); return prev & ~sw_newest; // change 1 -> 0 } void led_green(int onoff) { if (onoff==ON) digitalWrite(LED_G,LOW); else digitalWrite(LED_G,HIGH); } void led_red(int onoff) { if (onoff==ON) digitalWrite(LED_R,LOW); else digitalWrite(LED_R,HIGH); } void wifi_connect() { WiFi.begin(ssid, password); int onoff = 0; while (WiFi.status() != WL_CONNECTED) { delay(500); if (onoff==0) led_green(ON); else led_green(OFF); onoff ^= 1; } // 以下5行は、普通は不要なハズ IPAddress gateway(192,168,0,1); IPAddress subnet(255,255,255,0); IPAddress dns1(8, 8, 8, 8); // Google DNS primery server IPAddress dns2(8, 8, 4, 4); // Google DNS secondary server WiFi.config(WiFi.localIP(),gateway,subnet,dns1,dns2); led_green(ON); } void wifi_disconnect() { WiFi.disconnect(true); led_green(OFF); } void send_msg2line() { WiFiClientSecure client; led_red(ON); client.setInsecure(); if (!client.connect(host, 443)) { blink_red(10); led_red(OFF); return; } String query = String("message=") + String(message); String request = String("") + "POST /api/notify HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Authorization: Bearer " + token + "\r\n" + "Content-Length: " + String(query.length()) + "\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n\r\n" + query + "\r\n"; client.print(request); while (client.connected()) { String line = client.readStringUntil('\n'); if (line == "\r") { break; } } String line = client.readStringUntil('\n'); led_red(OFF); } void blink_red(int no) { int onoff = 1; int i; for (i=0; i<no*2 ;i++) { delay(100); if (onoff==0) led_red(ON); else led_red(OFF); onoff ^= 1; } } int read_setup_file(char* fileName) { char buff[128]; if (!SD.begin(SD_CS, SPI, 24000000)) return 1; File setupFile = SD.open(fileName,FILE_READ); if (setupFile) { readLine(setupFile,buff); decrypt(ssid,buff,crypt_key); readLine(setupFile,buff); decrypt(password,buff,crypt_key); readLine(setupFile,buff); decrypt(token,buff,crypt_key); readLine(setupFile,message); setupFile.close(); } return 0; } // microSDから1行読み取る int readLine(File fp, char *buff){ int i = 0; if(!fp.available()) { return -1; } while (fp.available()) { char c = fp.read(); if (c=='\r'); else if (c=='\n') break; else buff[i++] = c; } buff[i] = 0; return i; } void decrypt( char*a, char*b, char* key) { char x[128]; int i,j,k,m; // ascii文字をbinaryに戻す for(i=0; b[i]!=0; i++) { x[i] = b[i] - (int)'#'; } m = i - 1; // 6bit -> 8bit j = 0; for(i=0; j<m ; i++) { switch (i%3) { case 0: a[i] = (x[j]&0x3F)<<2 | (x[j+1]&0x30)>>4; j++; break; case 1: a[i] = (x[j]&0x0F)<<4 | (x[j+1]&0x3C)>>2; j++; break; case 2: a[i] = (x[j]&0x03)<<6 | x[j+1]&0x3F; j += 2; break; } } m = i; k = 0; int n = strlen(key); for (i=0; i<m; i++) { a[i] ^= key[k++]; k %= n; } a[i]= 0; } //--------------------------------------------------------------- // EOF //---------------------------------------------------------------
0 件のコメント:
コメントを投稿