1. はじめに
この記事では、製品組み込み向けTLSライブラリーwolfSSLを使って、TLSでポスト量子暗号を実際に動かして試してみます。前回の「鍵交換」に引き続き今回は「署名」を中心にみていきいます。ポスト量子暗号標準化(Post-Quantum Cryptography Standardization)の背景については「TLSでポスト量子暗号を試してみる (その1:鍵交換)」を参照してください。
2. ポスト量子暗号の署名アルゴリズム
米NISTでは、署名アルゴリズムとして現在(2024/8)、格子ベースと階層ハッシュベースの二つのアルゴリズムが標準化されています。
分類 | NIST標準名 | FIPS | 開発名 | 強度 |
---|---|---|---|---|
格子ベース | ML-DSA | FIPS-204 | CRYSTALS-Dilithium | ML-DSA-44 ML-DSA-65 ML-DSA-87 |
ハッシュ ベース |
FIPS-205 | SLH-DSA | SPHINCS+ |
表1: NISTで標準化されたPQCの署名アルゴリズム
署名のためのAPIとしてはwolfSSLでは、鍵ペアの生成、署名、検証などのAPIが用意されていて、概ねこれまでの公開鍵署名と同じようなイメージでプログラムすることができるようになっています。
API名 | 機能 |
---|---|
wc_dilithium_init | 初期化 |
wc_dilithium_make_key | 鍵ペアの生成 |
wc_dilithium_sign_msg | 署名 |
wc_dilithium_verify_msg | 検証 |
表2:主な署名用API
3. TLS1.3の署名と検証
下の図はTLSの公開鍵証明書を利用した公開鍵署名と検証の関連を示しています。ポスト量子暗号でもこの枠組みはそのまま踏襲します。
サーバ側はサーバ証明書にCAによる署名をもらいます。クライアント側はCA証明書を用意しておきます。TLSハンドシェーク時には、TLS1.3では"Certificate"メッセージでサーバ証明書を送り、"Certificate Verify" でそれまでのハンドシェークメッセージに対する署名を送ります。クライアント側では、受けとったサーバ証明書が真正であることをCA証明書の公開鍵で検証し、サーバ証明書の公開鍵で送られてきた署名を検証します。
図1: TLSの署名と検証
これらの全ての署名、検証をポスト量子アルゴリズムで実現することで、量子コンピューティングの時代において通信相手に対するピア認証が安全にできることになります。
4. サンプルプログラムを動かしてみる
4.1 サンプル証明書等のダウンロード
まずは、ポスト量子暗号を使ったX.509証明書や鍵のサンプルをwolfSSLのGithubレポジトリからダウンロードします。
$ git clone https://github.com/wolfssl/osp/oqs
$ cd osp/oqs
4.2 wolfSSLとサンプルのビルド
最新のwolfSSLライブラリーはポスト量子暗号に対応しているので、TLSによる通信自身については普通のTLSサーバとクライアントを使用してもよいのですがTLS1.3ではハンドシェークメッセージが暗号化されていてそのままでは見えません。これを復号してメッセージを見るためにはサンプルプログラム中にセッション鍵情報を取得する仕掛けを埋め込んでおいて、その情報をWiresharkで参照する必要があります。wolfSSLライブラリのほうもそのためのオプションを指定してビルドする必要があります。
wolfSSLのダウンロード、ビルド手順は "その1:鍵交換"で紹介した手順と基本的に同じですが、上記オプション指定を追加します。
$ git clone https://github.com/wolfssl/wolfssl
$ cd wolfssl
$ ./autogen.sh
$ ./configure --enable-kyber --enable-dilithium CFLAGS="-DHAVE_SECRET_CALLBACK"
$ make all
$ sudo make install
次にPQCのサーバ、クライアントのサンプルプログラムをダウンロード、ビルドします。
$ git clone https://github.com/wolfssl/wolfssl-examples
$ cd wolfssl-examples/pq
$ make CFLAGS=-DHAVE_SECRET_CALLBACK
4.3 TLSハンドシェークを見てみる
ビルドができたら、サンプルサーバとクライアントを動作させてTLSハンドシェークを見てみることにします。サンプルはwolfssl-examples/pq/server-pq-tls13.cとclient-pq-tls13.cを使います。
サンプルのサーバ、クライアントは次のようにオプションアーギュメントで証明書、鍵ファイル名を指定できるようになっています。
$ ./server-pq-tls13 [<サーバ証明書> <プライベート鍵>]
$ ./client-pq-tls13 IPアドレス [<CA証明書>]
Wireshark を立ち上げて、まず、サンプルサーバを起動します。
$ ./server-pq-tls13 <サンプル証明書ディレクトリ>/mldsa65_entity_cert.pem <サンプル証明書ディレクトリ>/mldsa65_entity_key.pem
次にクライアント側を起動し、サーバに送るメッセージを入力します。
$ ./client-pq-tls13 127.0.0.1 <サンプル証明書ディレクトリ>/mldsa65_root_cert.pem
Message for server: <適当なメッセージを入力>
Server: I hear ya fa shizzle!
その内容はサーバ側に表示されます。
Waiting for a connection...
Client connected successfully
Client: <クライアント側で入力したメッセージ>
Waiting for a connection...
この状態でWireshakeを "Filter:tls" で見てみると、下のように"Server Hello" が暗号化された状態で表示されます。
No. Time Source Destination Protocol Length Info
No. Time Source Port Destination Port Protocol Length Info
95 2025/058 02:33:25.833379 127.0.0.1 59212 127.0.0.1 11111 TLSv1.3 1952 Client Hello
7 2025/058 02:33:25.843167 127.0.0.1 11111 127.0.0.1 59212 TLSv1.3 1820 Server Hello
9 2025/058 02:33:25.843938 127.0.0.1 11111 127.0.0.1 59212 TLSv1.3 84 Application Data
11 2025/058 02:33:25.844119 127.0.0.1 11111 127.0.0.1 59212 TLSv1.3 6081 Application Data
13 2025/058 02:33:25.844580 127.0.0.1 11111 127.0.0.1 59212 TLSv1.3 3395 Application Data
15 2025/058 02:33:25.844739 127.0.0.1 11111 127.0.0.1 59212 TLSv1.3 130 Application Data
17 2025/058 02:33:25.848504 127.0.0.1 59212 127.0.0.1 11111 TLSv1.3 130 Application Data
21 2025/058 02:33:36.006824 127.0.0.1 59212 127.0.0.1 11111 TLSv1.3 91 Application Data
23 2025/058 02:33:36.007041 127.0.0.1 11111 127.0.0.1 59212 TLSv1.3 100 Application Data
25 2025/058 02:33:36.007083 127.0.0.1 11111 127.0.0.1 59212 TLSv1.3 80 Application Data
この時、サンプルプログラムを起動したディレクトリの直下に "sslkeylog.log" という名前でセッション鍵情報のファイルが生成されているはずです。Wirehsarkの設定→プロトコル→TLS→"(Pre)-Master-Secret log file name"でこのファイル名を指定すると下のように各メッセージが復号されて表示され普通のTLS1.3のハンドシェークであることがわかります。
No. Time Source Destination Protocol Length Info
5 2025/058 02:33:25.833379 127.0.0.1 59212 127.0.0.1 11111 TLSv1.3 1952 Client Hello
7 2025/058 02:33:25.843167 127.0.0.1 11111 127.0.0.1 59212 TLSv1.3 1820 Server Hello
9 2025/058 02:33:25.843938 127.0.0.1 11111 127.0.0.1 59212 TLSv1.3 84 Encrypted Extensions
11 2025/058 02:33:25.844119 127.0.0.1 11111 127.0.0.1 59212 TLSv1.3 6081 Certificate
13 2025/058 02:33:25.844580 127.0.0.1 11111 127.0.0.1 59212 TLSv1.3 3395 Certificate Verify
15 2025/058 02:33:25.844739 127.0.0.1 11111 127.0.0.1 59212 TLSv1.3 130 Finished
17 2025/058 02:33:25.848504 127.0.0.1 59212 127.0.0.1 11111 TLSv1.3 130 Finished
21 2025/058 02:33:36.006824 127.0.0.1 59212 127.0.0.1 11111 TLSv1.3 91 Application Data
23 2025/058 02:33:36.007041 127.0.0.1 11111 127.0.0.1 59212 TLSv1.3 100 Application Data
25 2025/058 02:33:36.007083 127.0.0.1 11111 127.0.0.1 59212 TLSv1.3 80 Alert (Level: Warning, Description: Close Notify)
まずは、 "Client Hello" の内容をみてみます。"Signature Algorithms" の "Signature Hash Algorithms" を見てみると "Unknown Unknown (0x0904, 0905, 0906)" のように生のID値が示されてりるものが三つほどあるのがわかります。wolfSSLのこのバージョンでサポートしているML-DSAなのですが、通常版のWiresharkではまだこれらのアルゴリズムのシンボル表示に対応していないのでこのように表示されます。
Handshake Protocol: Client Hello
...
Extension: signature_algorithms (len=34)
Type: signature_algorithms (13)
Length: 34
Signature Hash Algorithms Length: 32
Signature Hash Algorithms (16 algorithms)
Signature Algorithm: ecdsa_secp521r1_sha512 (0x0603)
...
Signature Algorithm: Unknown Unknown (0x0904) <--- ML-DSA44
Signature Algorithm: Unknown Unknown (0x0905) <--- ML-DSA65
Signature Algorithm: Unknown Unknown (0x0904) <--- ML-DSA87
...
次に、"Certificate" の内容を見てみると、subjectPublicKey, SignatureにはAlgorithm IDとして生のIDと値が表示されています。
Handshake Type: Certificate (11)
Length: 5999
Certificate Request Context Length: 0
Certificates Length: 3785
Certificates (3785 bytes)
Certificate Length: 3780
Certificate: 30820ec0308209baa00302010202020401300706052bce0f0304308196310b3009060355…
signedCertificate
version: v3 (2)
serialNumber: 0x0401
signature (iso.3.9999.3.4)
Algorithm Id: 1.3.9999.3.4 (iso.3.9999.3.4)
...
subjectPublicKeyInfo
algorithm (sigAlgs.18)
Algorithm Id: 2.16.840.1.101.3.4.3.18 (sigAlgs.18)
subjectPublicKey […]:8ccbc09958fe77551c7518a22f5ece343862ba700ec4ee
...
algorithmIdentifier (sigAlgs.18)
Algorithm Id: 2.16.840.1.101.3.4.3.18 (sigAlgs.18)
encrypted […]: 050d9bcd297ead848d8e63745962b22a74b9495116ee6ca3aab21fea96547d0...
5. まとめ
この記事では、ポスト量子暗号によるTLSプロトコルのうち署名部分ついてみてきました。記事中にもコメントしたように、従来のRSAやECCによる署名にくらべて署名サイズはかなり大きなものになってしまう点でアルゴリズムにはまだまだ改善の余地があります。しかし、現時点でTLSの枠組みののなかで実際に使用できるアルゴリズムができあがりつつあることが感じられるかと思います。