ESP32でWireGuardを張ってHTTPS通信する

`ESP32`でもセキュアに繋がりたい。
生なんて許されない！（
そんなあなたのために試行錯誤の記録です。

# 起: 発端

`ESP32`で`WireGuard`のトンネルを繋げるのは先達がいて割と簡単にいきますが`HTTPS通信`しようとすると以下のエラーで失敗します。

```
connect(): start_ssl_client: -1
```

ちなみに、パケット見ると`TCP Retransmission`が溢れています。

### 偉大な先達

https://qiita.com/ma2shita/items/0f0dce8ff0e45cf9fbac

# 承: 原因

結論から言うと`MTU`の設定が不適切でした。
ライブラリを修正して、ついでにオリジナルレポジトリへの変更も反映させたので皆様のお役に立てていただければと思います。

https://github.com/genshibangou16/WireGuard-ESP32-Arduino

# 転

一応コードです。
私の環境は`Platformio`なのでパス等はご自身の環境に合わせて適当に変えてください。

```c++
#include <Arduino.h>
#include <WiFiClientSecure.h>
#include <..\lib\WireGuard-ESP32-Arduino-main\src\WireGuard-ESP32.h>
#include "lwip/dns.h"

WiFiClientSecure client;
static WireGuard wg;

extern const uint8_t rootca_crt_bundle_start[] asm("_binary_data_cert_x509_crt_bundle_bin_start");

const char* ssid = "ssid";
const char* password = "password";

const char* dest = "example.com";

// WireGuard configuration --- UPDATE this configuration from JSON
char private_key[] = "";                                              // [Interface] PrivateKey
IPAddress local_ip(10,9,0,2);                                         // [Interface] Address
char public_key[] = "";                                               // [Peer] PublicKey
char endpoint_address[] = "vpn.example.net";                          // [Peer] Endpoint
int endpoint_port = 1194;                                             // [Peer] Endpoint

ip_addr_t dnsserver = IPADDR4_INIT_BYTES(8,8,8,8);

void connectTest();

void setup() {
  Serial.begin(115200);
  SPIFFS.begin();
  delay(100);

  Serial.print("Attempting to connect to SSID: ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while(WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(1000);
  }

  configTime(9 * 60 * 60, 0, "ntp.jst.mfeed.ad.jp", "ntp.nict.jp", "time.google.com");

  Serial.println();
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());
  Serial.print("MAC Address: ");
  Serial.println(WiFi.macAddress());

  delay(3000);

  Serial.println("Attempting to connect to VPN");
  wg.begin(local_ip, private_key, endpoint_address, public_key, endpoint_port);
  while (!wg.is_initialized())
  {
    delay(1000);
  }
  Serial.println("Connected!");

  dns_setserver(0, &dnsserver);
  client.setCACertBundle(rootca_crt_bundle_start);
}

void loop() {
  connectTest();
  delay(10000);
}

void connectTest() {
  Serial.println("\nStarting connection to server...");
  if (!client.connect(dest, 443)) {
    Serial.println("Connection failed!");
  }
  else {
    Serial.println("Connected to server.");
    // Make a HTTP request:
    client.println("GET https://example.com/ HTTP/1.1");
    client.println("Host: example.com");
    client.println("Connection: close");
    client.println();
    client.flush();

    while (client.connected()) {
      String line = client.readStringUntil('\n');
      if (line == "\r") {
        Serial.println("headers received");
        break;
      }
    }
    // if there are incoming bytes available
    // from the server, read them and print them:
    while (client.available()) {
      char c = client.read();
      Serial.write(c);
    }

    Serial.println();
  }

  client.stop();
}
```

## おまけ: 証明書の話

`ESP32`でHTTPS通信をするにはルート証明書が必要です。
ネットを漁ると接続先の証明書をコピーする方法がありますがあまりに面倒ですね？
いちいち接続先の証明書をコピーするなんでやってられません。
容量を犠牲にしてでも利便性がほしい人はルート証明書のリストを使う方法が使用できます。
上のコードはこの方法で、⑥と⑦は上のコード内にも記載があります。

### 手順

下記の`README`のトレースです。

https://github.com/espressif/arduino-esp32/tree/master/libraries/WiFiClientSecure

**①下記サイトより証明書のリストをダウンロードします。**

https://curl.se/docs/caextract.html

**②ダウンロードした`cacert.pem`と下記`Pythonコード`を同ディレクトリに配置します。**

https://github.com/espressif/arduino-esp32/blob/master/tools/gen_crt_bundle.py

**③下記コマンドを実行します。**
多分Linuxじゃないと環境が無くてちゃんと動きません。

```bash
chmod a+x gen_crt_bundle.py
./gen_crt_bundle.py -i cacert.pem
```

**④生成された`x509_crt_bundle`を`data\cert\x509_crt_bundle.bin`にリネームして保存します。**

**⑤`platformio.ini`に下記を追記します。**

```:platformio.ini
board_build.embed_files = data/cert/x509_crt_bundle.bin
```

**⑥プロジェクトのグローバルで下記を宣言します。**

```c++
extern const uint8_t rootca_crt_bundle_start[] asm("_binary_data_cert_x509_crt_bundle_bin_start");
```

**⑦SSL接続の前に下記コマンドを呼び出します。**

```c++
# WiFiClientSecure my_client;
my_client.setCACertBundle(rootca_crt_bundle_start);
```

# 結

容量削減のための`WiregGuard`実装なのに`HTTPS`もとい`TLS`で保護してては何だか本末転倒ですが、まあ良いでしょう。
デバッグでは`WireShark`が大活躍してくれました。
ありがとう、神よ。

それでは皆さん、良い暗号化ライフをお送りください！