ゼロ知識証明(ZKP:Zero-Knowledge Proof)は、ある事実が正しいという一点だけを相手に伝え、その根拠となる秘密は渡さずに済ませる暗号技術です。「20歳以上である」ことを生年月日を見せずに示す、といった使い方がわかりやすい例になります。この記事では定義と3条件から始めて、シュノアプロトコルとFiat-Shamir変換という中身の仕組みを追い、最後にCircomとsnarkjsで実際にzk-SNARKを生成・検証した実測値まで示します。概念の説明で止めず、手元で動かすと何バイトの証明が何ミリ秒で検証できるのかまで確認します。
まとめ:ゼロ知識証明の要点
ゼロ知識証明は、秘密そのものを送らずに「その秘密を知っている」「その条件を満たしている」という主張だけを検証可能にする技術です。成立条件は完全性・健全性・ゼロ知識性の3つで、この3つを同時に満たすために確率的な検証と乱数を使います。
実用の中心は、ブロックチェーンでの取引秘匿(Zcashのシールデッド取引)と処理の圧縮(ZKロールアップ)、そして身元確認の3つです。身元確認では、Googleが2025年7月にオープンソース化したLongfellow ZKが、ISO MDOC・JWT・W3C Verifiable Credentialsという既存の身分証規格へゼロ知識証明を適用します。
実装の負荷は回路の規模で決まります。この記事で実測した最小回路(Poseidonハッシュの原像を知っていることの証明)では、制約数415に対して証明生成が113〜139ミリ秒、検証が20〜42ミリ秒、証明データは724バイトでした。ただし回路が複雑になるほど証明生成コストは跳ね上がります。「秘密を隠したい」という要件すべてにゼロ知識証明を当てるのは誤りで、採用を見送るべき条件は最後の章で示します。
ゼロ知識証明とは:秘密の中身を渡さずに真偽だけを伝える暗号技術
従来、何かを証明するには証拠そのものを相手に渡す必要がありました。年齢を示すには運転免許証を見せ、パスワードを知っていることを示すにはパスワードをサーバーへ送ります。証明のたびに本来不要な情報(住所、本籍、パスワード本体)まで相手の手元に残る構造です。ゼロ知識証明はここを切り離し、検証者が受け取る情報を「主張が真である」という一点に限定します。
完全性・健全性・ゼロ知識性の3条件
ゼロ知識証明と呼べるかどうかは、次の3つの性質で決まります。どれか1つでも欠ければ、それは別の何かです。
| 性質 | 内容 | 欠けたときに起きること |
|---|---|---|
| 完全性(Completeness) | 主張が真で双方が手順に従うなら、検証者は高い確率で受理する | 正しい人が拒否される |
| 健全性(Soundness) | 主張が偽なら、不正な証明者はごく小さい確率でしか通せない | 嘘が通る |
| ゼロ知識性(Zero-Knowledge) | 検証者は真偽以外の情報を一切得られない | 秘密が漏れる |
完全性も健全性も確率で語られる点に注意してください。真の主張が必ず受理される構成は完全完全性(perfect completeness)と呼ばれる特別な場合で、一般には高い確率での受理を要求します。健全性の側も「必ず失敗する」ではなく「ごく小さい確率でしか成功しない」です。不正が通る確率を運用上無視できる水準まで下げることで安全性を担保する設計で、後述するシュノアプロトコルではこの確率がチャレンジの空間の大きさで決まります。
アリババの洞窟が示す「確率で詰める」証明
3条件がどう同時に成り立つのかは、Jean-Jacques Quisquaterらが示した「アリババの洞窟」の例え話が直感的です。環状の洞窟にAとBの2本の道があり、奥の扉は合言葉を知っている人だけが通れます。証明者が先に洞窟へ入ってどちらかの道を選び、検証者は姿が見えなくなってから「Aから出てこい」または「Bから出てこい」と指示します。合言葉を知っていれば扉を通れるので、どちらを指定されても必ずその道から出られます。知らなければ、最初に入った道と指定が偶然一致したときだけです。
1回の試行で、知らない人が通る確率は2分の1です。20回繰り返せば約100万分の1、30回で約10億分の1まで落ちます。検証者が得た情報は「毎回指定どおりに出てきた」という事実だけで、合言葉そのものは最後まで洞窟の中に残ります。確率で詰めることと、秘密を渡さないことが両立している構造がここにあります。
パスワード認証・電子証明書との違い
入力したパスワードをサーバーへ送って照合する方式では、サーバー側にハッシュしか保存していなくても、通信経路と受信サーバーには一度パスワード本体が到達します(OPAQUEのようなPAKEはこの限りではなく、パスワードをサーバーへ渡さずに認証します)。電子証明書(X.509など)は秘密鍵そのものは送りませんが、その証明書に記載された属性は署名対象としてまとめて開示されます。記載される属性はプロファイルや用途で異なるものの、氏名と生年月日が載っている証明書で年齢だけを示したい場合、氏名も一緒に渡ることになります。
ゼロ知識証明は、この「まとめて渡す」構造を分解します。生年月日を含む身分証を持ったまま、そこから導かれる「20歳以上である」という述語だけを証明に変換して提示できます。属性の選択的開示という点では匿名クレデンシャルの系譜とも重なり、ブラインド署名と閾値発行で同じ課題に取り組むCoconut認証(ココナッツ認証)は、ゼロ知識証明と組み合わせて使われる代表的な方式です。
ゼロ知識証明の仕組み:対話型プロトコルとFiat-Shamir変換
シュノア識別方式の3ステップ
ゼロ知識証明の実体は、証明者と検証者のあいだで交わされる手続きです。実際に動く最小の形として広く使われるのが、Claus-Peter Schnorrが提案した識別方式です。舞台は素数位数 q の巡回群で、生成元を g とします。証明したいのは「離散対数 x を知っている」こと、つまり公開値 y = g^x に対する x の知識です。以下の指数と応答の計算は q を法として行います。
| ステップ | 送る側 | 内容 |
|---|---|---|
| 1. コミットメント | 証明者 | 乱数 r を選び t = g^r を送る |
| 2. チャレンジ | 検証者 | 乱数 c を選んで送り返す |
| 3. レスポンス | 証明者 | s = r + c・x を計算して送る |
検証者は、受け取った t と y が正しい群の要素であることを確かめたうえで、g^s = t・y^c が成り立つかを確認します。このとき x は s の中に乱数 r で覆い隠されるため、検証者は x を復元できません。一方で証明者が x を知らなければ、c を受け取った後に辻褄の合う s を作ることは基本的にできません。ただし洞窟の例え話と同じで、チャレンジを事前に言い当てておけば通せます。RFC 8235はこの成功確率をチャレンジ長 t に対する 2 の −t 乗と説明しており、t を十分に大きく取ることで無視できる水準にします。洞窟の例え話でいう「先に道を選ばせてから指定する」が、乱数 r を先にコミットさせてから c を投げる構造として数式化されています。
実装で最も危険なのは、この r の扱いです。r は証明のたびに独立に生成し、二度と使ってはいけません。同じ r で異なるチャレンジ c1、c2 に応答すると、検証者は s1 − s2 = (c1 − c2)・x という関係から x をそのまま計算できます。乱数の使い回しは、秘密を隠すどころか秘密を渡す操作になります。
コミットメント・チャレンジ・レスポンスという3手のやり取りはΣプロトコルと総称されます。ただし3手であれば何でもΣプロトコルというわけではなく、検証者のメッセージが公開のランダム値であること、完全性に加えて特殊健全性と、正直な検証者に対するゼロ知識性を満たすことが条件です。
非対話型(NIZK)への変換とブロックチェーンでの必然性
対話型には運用上の弱点があります。検証者がその場にいて乱数を投げなければ証明が成立せず、証明を後から第三者に見せても「検証者と証明者が共謀して作った偽物ではない」と示せません。ブロックチェーンのように、不特定多数のノードが後から同じ証明を検証する環境では、この形は使えません。
そこでAmos FiatとAdi Shamirが示した変換を使います。検証者が選ぶはずだったチャレンジ c を、ハッシュ値で置き換えるという発想です。このときハッシュに入れるのはコミットメントだけではありません。Schnorr NIZK証明を規定するRFC 8235は、チャレンジを c = H(g || V || A || UserID || OtherInfo) と定義し、生成元・コミットメント・公開鍵・利用者識別子・用途情報までを入力に含めます。証明対象と利用文脈を結び付けないと、別の文脈で作られた証明を使い回される余地が残るためです。
この置き換えが「検証者が乱数を投げたのと同じ」と言えるのは、ハッシュ関数を理想的なランダム関数とみなす仮定(ランダムオラクルモデル)の上でのことです。証明者は入力を変えて何度でもハッシュを計算できるので、無条件に予測不可能なわけではありません。RFC 8235も同じ前提を「安全な暗号学的ハッシュ関数が存在すると仮定する(いわゆるランダムオラクルモデル)」と明示しています。この仮定のもとで、証明は証明者が単独で生成する1個のデータになり、誰でも、いつでも、同じ手順で検証できるようになりました。
この非対話型ゼロ知識証明(NIZK)が、ブロックチェーンでゼロ知識証明が使われる技術的な前提です。zk-SNARKもzk-STARKも、実用化されている方式はすべて非対話型です。
zk-SNARKとzk-STARKの使い分け:証明サイズ・検証コスト・信頼できるセットアップ
非対話型の中でも、実装で選ぶことになる系統は大きく2つです。名前が似ているため混同されがちですが、選定を左右するのは信頼できるセットアップ(trusted setup)の要否と、前提にしている数学的な仮定です。
注意したいのは、zk-SNARKもzk-STARKも方式の総称であり、「SNARKは何バイト、STARKは何KB」という単一の代表値は存在しないことです。そこで下表では、具体的な実装2つの一次情報を並べます。異なる計算・異なる実装・異なるハードウェアの数値なので、方式間の性能比として読むことはできません。それぞれの桁感をつかむための参考値です。
| 項目 | Groth16(snarkjs・Poseidon回路) | STARK(Winterfell・Rescueチェーン) |
|---|---|---|
| 信頼できるセットアップ | 回路ごとの第2段階が必要 | 不要(検証者の乱数は公開) |
| 証明の構成 | 群要素3個 | ハッシュとFRIのクエリ応答 |
| 証明サイズ | 約724バイト(本記事の実測) | 102〜252KB(公式ベンチマーク) |
| 検証時間 | 20〜42ミリ秒(本記事の実測) | 2〜6ミリ秒(公式ベンチマーク) |
| 安全性の根拠 | 楕円曲線ペアリングの数論的仮定 | ハッシュ関数(耐量子を明示) |
| ゼロ知識性 | Groth16として具備 | 完全ゼロ知識は現行実装で未対応 |
Groth16の原論文は「証明はわずか3個の群要素である」と述べており、この記事の実測で証明データが724バイト前後に収まったのはその構成によります。STARK側の数値はMetaのWinterfellの公式ベンチマーク(128ビット安全性、Rescueハッシュチェーン、Intel Core i9-9980KH 8コア)で、証明サイズは計算規模に応じて102KBから252KB、検証時間は全ベンチマークで2〜6ミリ秒と公開されています。README自身が「これらのベンチマークは方向性を示すものと見るべき」と注意を促しており、比較表の数値もその性格のものです。
Winterfellについては重要な但し書きがあります。READMEは「現在の実装は簡潔な証明を提供するが、完全ゼロ知識は提供しない。そのため現状では、証明が秘密入力に関する情報を一切漏らしてはならない用途には適さない可能性がある」と明記しています。STARKを秘密保持の目的で採用するなら、実装がゼロ知識性をどこまで満たすかを個別に確認する必要があります。方式名に「zk」が付いていることは、実装がゼロ知識であることを意味しません。
耐量子性の根拠も分かれます。STARKの原論文はタイトル自体が「Scalable, transparent, and post-quantum secure computational integrity」で、耐量子安全を設計目標に掲げました。透明性についても、StarkWareのethSTARK実装は「検証者側のメッセージはすべて公開ランダムコインであり、信頼できるセットアップを必要としない」と説明しています。一方、ここで比較しているGroth16は楕円曲線ペアリングの困難性に依存するため、その仮定が量子計算で崩れれば安全性を失う側です。ただしzk-SNARKという括り全体が同じ仮定に乗るわけではなく、ハッシュベースで耐量子性を掲げる構成も研究されています。
信頼できるセットアップとは、秘密の乱数(有害廃棄物、toxic waste と呼ばれます)から公開パラメータを生成する初期儀式のことです。Groth16の場合は、回路に依存しない第1段階(Powers of Tau)と、回路ごとにやり直す第2段階に分かれます。この乱数が誰かの手元に残っていると、その人は偽の証明を自由に作れてしまいます。実運用では多数の参加者が順番に寄与し「全員が共謀しない限り安全」という形に緩和しますが、儀式そのものを運用し続ける負担は残ります。
この負担を設計で消しにいったのがZcashです。SproutとSaplingの回路はGroth16を使うため信頼できるセットアップを必要としており、ZIP 224はその構造を「回路パラメータは隠れた構造を持つStructured Reference String(SRS)であり、それが知られれば偽の証明を作って貨幣を偽造できる」と説明しています。同ZIP 224で定義されたOrchardシールデッドプロトコルは、Groth16とR1CSに代えてHalo 2証明システムとPLONKish算術化を採用しました。Halo 2はElectric Coin Companyが「信頼できるセットアップの必要性を排除する」と説明する実装で、これにより初期儀式への依存が外れています。なおHalo 2が備える再帰的証明の機能は、ZIP 224の時点では使われていません(将来のプロトコル更新で活用される見込みと明記されています)。Electric Coin Companyはリリース告知でも「Orchardシールデッド決済プロトコルを立ち上げ、Halo証明システムを用いて複雑なセットアップ儀式への依存を取り除く」と説明しています。Orchardを含むNU5ネットワークアップグレードは、ZIP 252が定めるメインネットのブロック高1,687,104、2022年5月31日に有効になりました。方式選定は性能表だけでなく、「初期儀式を将来にわたって運用できるか」という組織の問題でもあります。
Circom・snarkjsによる最小zk-SNARKの実装と実測
ここまでの説明は、実際に動かすと数十行で確認できます。回路記述言語のCircomと、証明の生成・検証を担うsnarkjsを使い、「あるハッシュ値の原像を知っている」ことを証明する最小の回路を作ります。以下の数値は、Poseidonの最小回路についてコンパイルから証明生成・検証までを実行した測定結果です(macOS・Intel x86_64、Node.js v26.5.0、circom 2.2.3、snarkjs 0.7.6、circomlib 2.0.5)。
回路の記述とコンパイル結果
準備として、circom本体をRustでビルドして PATH に通し、作業ディレクトリで npm i [email protected] [email protected] [email protected] を実行しておきます(版を固定しないと制約数や出力が変わる可能性があります)。そのうえで、証明したい命題を回路(制約の集合)として knows.circom に書きます。公開するのはハッシュ値、隠すのはその原像です。ハッシュにはゼロ知識証明向けに設計されたPoseidonを使います。SHA-256のようなビット演算中心のハッシュは回路にすると制約数が跳ね上がるためです。
pragma circom 2.1.9;
include "node_modules/circomlib/circuits/poseidon.circom";
template KnowsPreimage() {
signal input preimage; // 秘密(ウィットネス)
signal input hash; // 公開(ステートメント)
component h = Poseidon(1);
h.inputs[0] <== preimage;
hash === h.out;
}
component main {public [hash]} = KnowsPreimage();
最終行の {public [hash]} が、どの入力を公開するかの宣言です。ここに preimage を書かなければ、原像は公開入力として出力されません。コンパイルすると制約の規模がそのまま出力されます。
$ circom knows.circom --r1cs --wasm --sym
template instances: 71
non-linear constraints: 216
linear constraints: 199
public inputs: 1
private inputs: 1
wires: 417
非線形制約216・線形制約199で、合計415の制約です。証明生成の重さはこの制約数に強く影響されますが、回路の構成・実装・並列化・実行環境にも左右されるため、制約数だけで所要時間が決まるわけではありません。Poseidonを1回呼ぶだけでこの規模なので、たとえば署名検証やマークル木の包含証明を回路に載せると制約数は数万から数百万の桁へ移ります。
Groth16のセットアップから証明生成・検証まで
次にsnarkjsで信頼できるセットアップを行います。Powers of Tauと呼ばれる回路非依存の第1段階と、回路ごとの第2段階に分かれます。前の章で述べた「初期儀式」が、コマンドの形で目の前に現れる場面です。
$ npx snarkjs powersoftau new bn128 12 pot12_0000.ptau -v
$ npx snarkjs powersoftau contribute pot12_0000.ptau pot12_0001.ptau --name="1st" -v
$ npx snarkjs powersoftau prepare phase2 pot12_0001.ptau pot12_final.ptau -v
$ npx snarkjs groth16 setup knows.r1cs pot12_final.ptau knows_0000.zkey
$ npx snarkjs zkey contribute knows_0000.zkey knows_final.zkey --name="1st" -v
$ npx snarkjs zkey export verificationkey knows_final.zkey verification_key.json
contribute が参加者による寄与で、実運用では別々の組織が順番に実行し、各自が使った乱数を破棄します。安全性はこの破棄が確実に行われるかに懸かっています。1人で全段階を回した場合、その人が乱数を確実に破棄していなければ偽の証明を作れてしまいます。検証用鍵 verification_key.json は2,929バイトで、これだけを検証側へ配布すれば済みます。
入力は、秘密の原像と、それに対応する公開ハッシュ値をJSONで与えます。ハッシュ値はcircomlibjsで同じPoseidonを計算して求めたものです。
$ cat input.json
{
"preimage": "12345678901234567890",
"hash": "17610922722311195426938483481431943255028223790571250909270476711880232282197"
}
ウィットネス生成・証明生成・検証の3コマンドを順に実行します。
$ node knows_js/generate_witness.js knows_js/knows.wasm input.json witness.wtns
$ npx snarkjs groth16 prove knows_final.zkey witness.wtns proof.json public.json
$ npx snarkjs groth16 verify verification_key.json public.json proof.json
[INFO] snarkJS: OK!
実測値が示す証明サイズと検証コスト
計測にはsnarkjsのAPIを直接呼びました。CLI経由の時間にはNode.jsの起動分が含まれるため、純粋な処理時間を見るにはAPI呼び出しで測る必要があります。ウォームアップとして fullProve を1回実行して除外し、groth16.prove と groth16.verify をそれぞれ5回計測しています。測定環境はIntel Core i9-9880H @ 2.30GHz、計測日は2026年9月21日、証明サイズは JSON.stringify による空白なし表現のバイト数です。
const snarkjs = require("snarkjs");
await snarkjs.groth16.fullProve(input, "knows_js/knows.wasm", "knows_final.zkey"); // warm-up
for (let i = 0; i < 5; i++) {
const t = Date.now();
await snarkjs.groth16.prove("knows_final.zkey", "witness.wtns");
console.log("prove ms =", Date.now()-t);
}
| 測定項目 | 実測値 |
|---|---|
| 制約数(非線形+線形) | 216 + 199 = 415 |
| 証明生成時間(5回) | 113 / 113 / 116 / 133 / 139 ミリ秒 |
| 検証時間(5回) | 20 / 21 / 38 / 39 / 42 ミリ秒 |
| 証明データ(空白なしJSON・5回) | 720〜724 バイト |
| proof.json(整形済み・保存時) | 803 バイト |
| 証明鍵 knows_final.zkey | 186,569 バイト(約182KiB) |
| 検証鍵 verification_key.json | 2,929 バイト |
証明サイズが固定値でない点に注意してください。群要素の数は3個で一定ですが、各要素を十進文字列としてJSONに書き出すため、値によって桁数が変わります。整形して保存した proof.json は空白を含むため803バイトになります。
Groth16の特徴がそのまま数字に出ています。証明は1KB未満と小さく、検証は数十ミリ秒で終わります。逆に証明鍵は約182KiBあり、制約数が増えるほど大きくなります。実運用で効いてくるのは「証明者側に重い鍵と計算を寄せ、検証者側は小さなデータを短時間で確認する」という非対称性です。ブロックチェーンのように検証が全ノードで何度も走る環境と、この性質はよく噛み合います。
生成された public.json を見ると、公開側に何が出力されたのかがわかります。
$ cat public.json
[
"17610922722311195426938483481431943255028223790571250909270476711880232282197"
]
公開入出力として出ているのはハッシュ値1つだけで、原像はここに現れません。検証者はこのファイルと1KB未満の証明を受け取り、「この人はこのハッシュの原像を知っている」という事実を確認します。ただしこれで確認できるのは公開入出力の内容までで、証明データ自体から秘密の情報が漏れないというゼロ知識性の保証は、証明システムの暗号学的な性質として別に与えられるものです。回路で非公開入力に指定したことと、公開情報から原像を推測できないことも別問題で、原像のエントロピーが低ければ公開ハッシュから総当たりで特定されます。
誤った原像に対するウィットネス生成の拒否
回路が条件をどう強制しているかは、誤った入力を与えると体感できます。公開ハッシュはそのままに、原像だけを別の値(「99999999999999999999」)へ差し替えた input_bad.json を用意し、ウィットネスを生成してみます。
$ node knows_js/generate_witness.js knows_js/knows.wasm input_bad.json witness_bad.wtns
Error: Error: Assert Failed.
証明の提出前、ウィットネス生成の段階で hash === h.out の制約が破れて止まります。ここで確認できたのは「正規のウィットネス生成プログラムが、制約を満たさない入力を拒否した」という挙動です。攻撃者が正規の手順に従うとは限らないため、これ自体が不正な証明を作れないことの証明ではありません。回路の等式制約は「何を満たせば真とみなすか」の定義であり、それを満たさない証明が検証を通らないという健全性は、証明システム・セットアップ・検証処理の安全性まで含めて成り立ちます。
実用化の現在地:Zcash・ZKロールアップ・Googleの身元証明ライブラリ
ブロックチェーンでのプライバシー保護とスケーラビリティ
Zcashのシールデッド取引は、送信者・受信者・金額を公開台帳に載せずに、取引が規則どおりであること(二重支払いがなく、収支が合っていること)だけをゼロ知識証明で示します。前述のとおりOrchardプロトコルではHalo 2を使い、信頼できるセットアップへの依存を外しました。
スケーラビリティ側の代表がZKロールアップです。多数の取引をレイヤー2でまとめて実行し、その実行が正しかったことを示す証明と、状態を再構成するために必要なデータをメインチェーンへ公開します。Ethereum公式ドキュメントは、Ethereumが状態更新の正当性を強制するだけでなく「あらゆる更新の背後にあるデータの可用性を保証する」と説明しており、このデータ公開こそが、証明だけをオンチェーンに置くvalidiumとの分かれ目です。メインチェーン側は個々の取引を再実行せず、証明の検証だけで済むため処理能力が上がります。ここで使われているのは「実行が正しい」ことを簡潔に示す性質であって、ゼロ知識性ではありません。取引内容を秘匿するかどうかは別の設計判断で、多くのZKロールアップは取引データを公開しています。前節の実測で見た「証明は小さく検証は軽いが、生成は重い」という非対称性を、そのまま経済設計に落とし込んだ構造です。レイヤー2を含む処理能力向上の選択肢全体はブロックチェーンのスケーリングソリューションで比較しています。ゼロ知識証明をチェーンの基盤側に組み込み、アプリケーションの実行そのものを扱うレイヤー1についてはAleo(アレオ)のゼロ知識証明L1の仕組みで開発言語レベルまで解説しています。
デジタルID・年齢確認向けのLongfellow ZKと標準化の状況
ブロックチェーンの外で動きが速いのが身元確認です。Googleは2025年7月3日の公式ブログで「本日、当社のゼロ知識証明(ZKP)ライブラリをオープンソース化しました」と発表し、年齢確認におけるプライバシー保護を用途として挙げました。公開されたLongfellow ZKは、既存の身分証規格に後付けでゼロ知識証明を載せるためのC++ライブラリです(リポジトリは公開当初の google 配下から longfellow-zk 組織へ移り、旧URLは転送されます)。READMEは対象規格を「ISO MDOC standard, the JWT standard, and W3 Verifiable Credentials」と明記しており、新しい規格を作るのではなく、すでに普及している運転免許証のデジタル版(mdoc)やJWTをそのまま扱う点が特徴です。
| 項目 | 内容 |
|---|---|
| オープンソース公開 | 2025年7月3日 |
| リポジトリ | github.com/longfellow-zk/longfellow-zk |
| 最新リリース | v0.9(2026年3月31日・2026年9月21日確認) |
| ライセンス | Apache-2.0 |
| 対象規格 | ISO MDOC / JWT / W3C Verifiable Credentials |
| 関連文書 | IACR ePrint 2024/2010、IETF draft-google-cfrg-libzk |
実運用に向けた課題は証明者側の負荷です。v0.9のリリースノートは「zk_mdoc proverのメモリ使用量を600MBから200MB未満へ削減した」と記しており、スマートフォン上で身分証の証明を作る現実的な制約と向き合っている段階だとわかります。IETFへdraft-google-cfrg-libzkとしてドラフトが提出され、リポジトリでは複数の独立したセキュリティレビューが進行中であることも公開されています。
規格側の整備状況も押さえておく価値があります。W3CのVerifiable Credentials Data Model v2.0は2025年5月15日に勧告となり、BBS暗号スイートによる連結不能な選択的開示の例を含みます。ただしそのData Integrity BBS Cryptosuites v1.0は2026年9月2日版でも勧告候補ドラフトの段階で、勧告には到達していません。EUのデジタルIDウォレットでは、アーキテクチャ参照フレームワーク(ARF)が2026年7月23日公開のv3.0.0まで進む一方、ゼロ知識証明の技術仕様TS13は「最終仕様となることを意図したものではない」と自ら明記する探索的作業の段階で、規範要件はETSI TS 119 476-2へ引き継ぐ予定とされています。年齢確認へのゼロ知識証明の適用は、実装が先行し規範要件の確定がこれから、という位置にあります。
認証の文脈では、既存のIDプロバイダとゼロ知識証明を組み合わせる方式も実装されています。OAuthのIDトークンを使いながらブロックチェーン上のアドレスと利用者を結びつけないzkLoginの認証の仕組みは、Web2の認証基盤を捨てずにプライバシーを足す設計の例です。
ゼロ知識証明を採用すべきでない場面
ここまで見てきた性質は、裏返すと適用範囲の制約でもあります。「秘密を守りたい」という要件があるだけでゼロ知識証明を選ぶのは、多くの場合で過剰です。次の条件に当てはまるなら、採用を見送るか、別の手段を先に検討してください。
検証者が自分でデータを持っている場合は不要です。自社のサーバーに保存されている会員情報を自社が確認するだけなら、証明する相手と証明される内容の持ち主が同じであり、隠す相手がいません。判断の軸は組織の数ではなく、情報の開示境界があるか、そして検証負荷を簡潔な証明に置き換える価値があるかです。どちらも無いなら、通常の照合で足ります。
回路が頻繁に変わるうえ、Groth16を選ぶ場合は向きません。回路はコンパイル時に構造が確定している必要があり、Groth16では回路が変わるたびに第2段階のセットアップをやり直して鍵を配り直します。ここで誤解しやすいのは、閾値の変更がすべて回路の変更になるわけではない点です。circomlibの比較回路は比較対象を入力信号として受け取るので、与信の閾値のような値は公開入力として渡せば回路を変えずに済みます。再セットアップが必要なのは、閾値を回路内に定数として埋め込んだ場合や、判定ロジックの構造自体を変える場合です。判定ロジックの改訂が続く見込みなら、回路ごとの儀式を毎回運用できる体制があるかで判断してください。体制を用意できないなら、snarkjsが同じく対応するPLONKやFFLONKのように、回路ごとの第2段階を必要としない構成を検討する方が現実的です。
証明生成の遅延が許されない大規模な計算も要注意です。実測したPoseidon1回の回路(制約415)で証明生成は113ミリ秒でしたが、これは最小構成の値です。署名検証やマークル木を含む実用回路では制約が数万から数百万に達し、生成時間も相応に伸びます。実際、Googleが身分証向けに公開したLongfellow ZKでも、証明側のメモリ使用量の削減がリリースノートの主題になっています。利用者の端末で証明を作らせる設計なら、対象回路でのベンチマークを先に取り、その待ち時間をUIでどう吸収するかを決める必要があります。
暗号化したまま計算したいだけなら、別の技術が適しています。ゼロ知識証明が扱うのは「主張の真偽の検証」であり、暗号文に対する演算ではありません。データを復号せずに集計や推論を行いたいのであれば準同型暗号の領域で、暗号文のままAIモデルを動かすGoogle HEIRの仕組みのような別系統の選択肢があります。目的が「検証」なのか「計算」なのかで、先に分岐してください。
逆に採用が正当化されるのは、組織をまたいで属性を提示する場面、検証が多数回・多地点で行われる場面、そして開示した情報が後から取り返せない場面です。年齢確認とブロックチェーンの取引検証がゼロ知識証明の主戦場になっているのは、この3条件を同時に満たすからです。
よくある質問
ゼロ知識証明の具体例には何がありますか?
身近な例は年齢確認です。生年月日を見せずに「20歳以上である」という条件だけを証明します。実装が動いている例としては、取引内容を隠したまま正当性を検証するZcashのシールデッド取引、多数の取引を1つの証明へ圧縮するZKロールアップ、既存の身分証規格に証明を載せるGoogleのLongfellow ZKがあります。
ゼロ知識証明とzk-SNARKは何が違うのですか?
ゼロ知識証明は性質の名前で、zk-SNARKはその性質を満たす具体的な方式の一種です。zk-SNARKは非対話型で証明が小さいことを特徴とし、Groth16のような構成では回路ごとに信頼できるセットアップが必要になります。ゼロ知識証明にはほかにzk-STARKやBulletproofsなどの方式があります。
信頼できるセットアップ(trusted setup)とは何ですか?
証明システムの公開パラメータを、秘密の乱数から生成する初期手続きです。この乱数(有害廃棄物)が残っていると偽の証明を作れるため、複数の参加者が順に寄与する形で運用し、各自に破棄を求めます。安全性の条件は全員の破棄ではなく、少なくとも1人が正直に破棄していることです。zk-STARKやHalo 2を使うZcashのOrchardでは、この手続き自体が不要です。
ゼロ知識証明は量子コンピュータに耐えられますか?
方式によります。楕円曲線ペアリングなど数論的な仮定に依存するzk-SNARKは、その仮定が量子計算で破られると安全性を失います。一方zk-STARKは数論的な仮定を使わず、ハッシュ関数を基礎に置く設計のため、量子計算機の影響を受けにくいとされます。ただし安全性はハッシュの衝突耐性だけで決まるわけではなく、対話型証明(IOP)の健全性、Merkle木によるコミットメント、そして非対話化のためのFiat-Shamir変換を組み合わせ、ランダムオラクルモデルの下で解析されます。長期間検証され続ける証明を扱うなら、この違いは設計時点で考慮する価値があります。
ゼロ知識証明の実装に必要な環境は何ですか?
回路のコンパイル後は、JavaScriptライブラリのsnarkjsを使うためNode.js環境だけで一通り試せます。ただしcircom本体はRust製のため、配布バイナリを入手するか、Rustツールチェーンを用意してソースからビルドする準備が別に必要です。この記事の実測ではcircom 2.2.3とNode.js v26.5.0を使い、Poseidonハッシュの回路をコンパイルしてからGroth16のセットアップ・証明生成・検証まで実行しました。