はじめてのゼロ知識証明 【TLSNotary 編】:Web 通信をゼロ知識で証明してみよう
ゼロ知識証明(Zero-Knowledge Proof, ZKP)って名前は聞いたことあるけど、
「実際に何ができるの?」「どうやって触ればいいの?」と思っていませんか?
この記事では TLSNotary というライブラリを使って、
「ある Web サーバーにアクセスしたこと」をゼロ知識で証明する体験をしてみましょう。
難しい数学や暗号の仕組みはライブラリが全部やってくれるので事前知識は不要です!
TLSNotary (zkTLS) とは?
TLSNotary は 通常の HTTPS 通信 をベースにして、
「サーバーとやりとりした内容が本物であること」を 第三者に証明できる仕組み です。
例えばこんなことができます:
- 「この API にアクセスした」ことを証明する
- 「レスポンスに特定の値が含まれている」ことだけを証明する(他の部分は秘密にできる)
- 「自分の秘密のリクエスト内容は隠したまま、正しい通信が行われた」ことを証明する
👉 ポイントは 公開したい部分だけを開示し、残りは秘密にできる ところです。
応用例としては:
- API 認証(API Key を秘密にしたまま「正しいレスポンスを得た」と証明)
- 匿名認証(サーバーにアクセスした事実だけを証明、リクエスト内容は隠す)
- 監査やログ検証(通信内容の改ざんがないことを第三者に示す)
などが挙げられます。
仕組み(ざっくり)
TLSNotary では Notary(公証人サーバー)が登場します。
- Prover(証明者)がサーバーと HTTPS 通信する
- その通信を Notary が立ち会い、公証データを発行する
- Prover は「通信の一部だけを開示する証明(Presentation)」を作る
- Verifier(検証者)はその証明を検証し、改ざんがないことを確認できる
つまり、「Notary が保証した TLS 通信ログ」を使って、部分的に開示可能な証明を作れる という仕組みです。
サンプルデータ
本チュートリアルでは、下記 web サイトにアクセスして取得できる JSON データを題材にします。
「通信が正しく行われたこと」と「レスポンスの name と street フィールドの値が正しいこと」を TLSNotary を使って証明してみましょう。
レスポンス例(一部省略)
{
"id": 1234567890,
"information": {
"name": "John Doe",
"address": {
"street": "123 Elm Street",
"city": "Anytown",
"state": "XY",
"postalCode": "12345"
},
"favoriteColors": ["blue", "red", ...],
"description": "John is a software engineer...",
"education": {
"degree": "Bachelor's in Computer Science",
"school": "Anytown University"
},
"family": {
"siblings": [
{
"name": "Jane Doe",
"relation": "Sister",
"age": 24
},
{...}
],
"parents": {
"father": {
"name": "James Doe",
"age": 55
},
"mother": {...}
}
}
},
"meta": {
"createdAt": "2022-01-15T14:52:55Z",
"lastUpdatedAt": "2023-01-12T16:42:10Z",
"version": 1.2
}
}
TLSNotary を触ってみる
ここからは実際に tlsn-js というライブラリを用いて「Web 通信のゼロ知識証明」を作ってみます。
このチュートリアルは tlsn-js 公式のデモコード をよりシンプルな構成に書き直したものです。ぜひ公式もご参照ください。
0. ディレクトリ構成
以下のように 5 つのファイルを作成していきます。
tlsnotary/
├─ app.tsx # デモ本体(React UI + 証明生成/検証ロジック)
├─ worker.ts # tlsn-js API を Web Worker 経由で公開するブリッジ
├─ webpack.js # 開発ビルド設定 (TS -> JS, HTML 生成, wasm 資産コピー/提供)
├─ package.json # 依存管理・起動スクリプト
├─ tsconfig.json # TypeScript 設定
1. サンプルコードを用意
上記のディレクトリ構成にならって各ファイルを配置します。
app.tsx
デモ本体(React UI + 証明生成/検証ロジック)
※プレビューでは最後の行まで表示されないことがあるようです。その場合は github からご確認ください!
worker.ts
tlsn-js API を Web Worker 経由で公開するブリッジ
webpack.js
開発ビルド設定 (TS -> JS, HTML 生成, wasm 資産コピー/提供)
package.json
パッケージ依存管理
tsconfig.json
TypeScript 設定
👉 フルコードはこちら:
3. アプリを起動する
$ npm install
$ npx webpack-dev-server --config webpack.js
以下のようなログが出て、他にエラーなどが出ていなければ OK です。
<i> [webpack-dev-server] Project is running at:
<i> [webpack-dev-server] Loopback: http://localhost:8080/, http://127.0.0.1:8080/
4. アプリ上で証明生成 & 検証
ブラウザで http://localhost:8080 にアクセスします。

画面の 「Start Demo」ボタン を押すと、TLSNotary の証明フローが始まります。
(結果が表示されるまで数分かかることがあります。途中の処理を見たい方は開発者ツール [F12] → Console をご確認ください。)
処理の流れは以下の通りです:
- Prover(証明者) が Notary(公証人サーバー)を経由して serverUrl にアクセス
- 通信ログ(送信データと受信データ)が記録される
- その中から「開示したい部分」だけを指定(今回は name と street)
- Notary が通信の正しさを保証する 公証データ を返す
- それをもとに Presentation(証明データ) が作成され、画面に JSON として表示される
さらに、この Presentation を検証者(Verifier)に渡すと、次のことが確認できます:
- 通信相手のサーバー名
- 通信が行われた時刻
- Notary(公証人)の公開鍵
- 開示を許可したリクエスト・レスポンスの一部
👉 つまり、「確かにこの通信が行われ、その中の特定の情報が正しい」ということを 第三者に安全に証明できる のです。

今回の例では、verification: の出力の recv フィールドの中で name と street の値のみが開示されていることが確認できます。
コードのポイントを抜粋
TLSNotary のコアの部分は以下のコードになります。
// Prover の準備
const notary = NotaryServer.from(notaryUrl);
const prover = (await new Prover({ serverDns, maxRecvData: 2048 })) as TProver;
await prover.setup(await notary.sessionUrl());
// リクエスト送信
await prover.sendRequest(websocketProxyUrl, {
url: serverUrl,
method: "GET",
headers: { "Content-Type": "application/json", secret: "test_secret" },
});
// 通信ログを取得
const { sent, recv } = await prover.transcript();
// 公証データを生成
const commit: Commit = {
sent: subtractRanges(...), // 秘密は除外
recv: mapStringToRange([...], Buffer.from(recv).toString("utf-8")),
};
const notarizationOutputs = await prover.notarize(commit);
// Presentation(証明データ)を作成
const presentation = (await new Presentation({
attestationHex: notarizationOutputs.attestation,
secretsHex: notarizationOutputs.secrets,
notaryUrl: notarizationOutputs.notaryUrl,
websocketProxyUrl: notarizationOutputs.websocketProxyUrl,
reveal: { ...commit, server_identity: false },
})) as TPresentation;
// 検証者が証明データを検証
const proof = (await new Presentation(presentationJSON.data)) as TPresentation;
const verifierOutput = await proof.verify();
まとめ
- TLSNotary を使うと HTTPS 通信を材料にしたゼロ知識証明 が体験できる
- 秘密のリクエストやレスポンスを隠しつつ、一部だけを証明できる
- ライブラリが暗号処理を隠蔽してくれるので、React/Node.js から簡単に試せる
ZK Email の記事 では「メール」を題材にしていたのに対して、TLSNotary は「Web 通信」を題材にしている、というイメージを持つと分かりやすいと思います!
TLSNotary の公式ドキュメントも充実しています。仕組みなどのより詳細な解説もあるのでぜひ見てみてください。
参考リンク
本記事について
この記事は ZK Core Program 2025 の活動の一環として執筆しました。
Discussion