JavaScript

「ゼロ知識証明」で遊ぶ:Circom・SnarkJSのチュートリアル

目次

🔒 はじめに:ゼロ知識証明とは何か?

ゼロ知識証明(Zero-Knowledge Proof, ZKP)とは、ある情報の「正しさ」を、中身を一切明かさずに相手に証明できる技術です。

たとえば、以下のようなことができます:

  • 「私は秘密の値 x を知っている」ことを証明するが、x は明かさない
  • 「この計算式の答えは正しい」と示すが、式の詳細は伏せる

この魔法のような証明技術は、暗号学やブロックチェーン分野で超重要です。

例えば:

  • 個人情報を明かさずに本人確認する
  • トランザクションの整合性を保証しつつ、金額を隠す(Zcash)
  • Web3のID認証・匿名投票

ZK-SNARKsやZK-STARKsなどの技術が発展し、EthereumやPolygonでも実運用されています。


🧪 本記事でやること

この記事では、以下の2つのツールを使って実際にZK証明を構築・検証するチュートリアルを実施します。

ツール役割
Circomゼロ知識証明に必要な「計算回路(プログラム)」を記述
SnarkJSCircomで作った回路をもとに、証明と検証を行うツール

🛠 準備:開発環境の構築

1. Node.js / npm のインストール

macOS/Linux:

brew install node

Windows:


2. circom のインストール

npm install -g circom

または、公式ビルド:

git clone https://github.com/iden3/circom
cd circom && cargo build --release

3. snarkjs のインストール

npm install -g snarkjs

確認:

circom --version
snarkjs --help

🧮 1つめの例題:2つの数を足して10になることを証明する

✅ 回路ファイル add10.circom

pragma circom 2.0.0;

template Add10() {
  signal input a;
  signal input b;
  signal output out;

  out <== a + b;
  out === 10;
}

component main = Add10();

この回路は、「a + b = 10」という条件を満たす2つの数字を入力として与えると、正しい証明ができるという意味です。


✅ 回路のコンパイル

circom add10.circom --r1cs --wasm --sym -o build/

出力ファイル:

  • add10.r1cs: 計算回路の制約情報
  • add10.wasm: 証明に使うWASM実行ファイル
  • add10.sym: シンボルファイル(デバッグ用)

✅ 証明に必要な鍵(trusted setup)を生成

snarkjs powersoftau new bn128 12 pot12_0000.ptau -v
snarkjs powersoftau contribute pot12_0000.ptau pot12_final.ptau --name="First contribution"
snarkjs groth16 setup build/add10.r1cs pot12_final.ptau build/circuit_0000.zkey

✅ 入力ファイルの作成

{
  "a": 4,
  "b": 6
}

保存先:input.json


✅ witness(証拠)生成

cd build/add10_js
node generate_witness.js add10.wasm ../../input.json ../../witness.wtns

✅ 証明を作成

snarkjs groth16 prove ../circuit_0000.zkey ../witness.wtns proof.json public.json

✅ 証明を検証

snarkjs groth16 verify verification_key.json public.json proof.json

結果:OK → 正しい証明ができた!

🔐 第2章:秘密を知られずに「知っている」と証明する

✅ 回路ファイル square_check.circom

pragma circom 2.0.0;

template SquareCheck() {
  signal input x;
  signal output out;

  out <== x * x;
  out === 49;
}

component main = SquareCheck();

この回路は「x の 2乗 = 49 になるような x を自分は知っている」ということを、x を明かさずに証明するためのものです。


✅ 回路コンパイル・証明鍵作成

circom square_check.circom --r1cs --wasm --sym -o build/
snarkjs groth16 setup build/square_check.r1cs pot12_final.ptau build/square_zkey.zkey

✅ 入力ファイル(秘密含む)

{
  "x": 7
}

保存先:input.json


✅ witness 作成

cd build/square_check_js
node generate_witness.js square_check.wasm ../../input.json ../../witness.wtns

✅ 証明の生成と検証

cd ../
snarkjs groth16 prove square_zkey.zkey witness.wtns proof.json public.json
snarkjs groth16 verify verification_key.json public.json proof.json

✅ 検証結果が OK になれば、「x = 7 を知っていて、その 2乗が 49 であること」を秘密のまま証明成功!

🌐 第3章:Webアプリからゼロ知識証明を実行する

第1章、第2章ではコマンドラインからCircomとSnarkJSを使って証明を行いましたが、実用的なアプリケーションを構築するには、ブラウザやWeb UIからZKPを作成・検証できる仕組みが必要になります。

ここでは、Next.js + TypeScript + WebAssemblyを活用し、ブラウザだけでZKPを生成・検証できる簡易Webアプリを構築します。


🧱 技術スタック

技術用途
Next.jsフロントエンドとAPIルーティング
SnarkJSWASMベースで証明生成・検証
circomlibjsCircomのユーティリティ関数
WebAssemblyadd.wasmなどでローカル処理

📁 ディレクトリ構成(最低限)

zkp-app/
├── public/
│   ├── add.wasm
│   └── zkey.json
├── pages/
│   └── index.tsx
├── utils/
│   └── snark.ts

🧠 SnarkJSブラウザ連携用のユーティリティ(utils/snark.ts)

import * as snarkjs from 'snarkjs';

export async function generateProof(input: any, wasmUrl: string, zkeyUrl: string) {
  const { groth16 } = snarkjs;
  const wasm = await fetch(wasmUrl).then(r => r.arrayBuffer());
  const zkey = await fetch(zkeyUrl).then(r => r.arrayBuffer());

  const { proof, publicSignals } = await groth16.fullProve(input, new Uint8Array(wasm), new Uint8Array(zkey));
  const vkey = await groth16.exportVerificationKey(new Uint8Array(zkey));

  const verified = await groth16.verify(vkey, publicSignals, proof);

  return { proof, publicSignals, verified };
}

🖥 フロントエンド画面(pages/index.tsx)

import { useState } from 'react';
import { generateProof } from '../utils/snark';

export default function Home() {
  const [input, setInput] = useState(4);
  const [result, setResult] = useState<string>('');

  async function handleProve() {
    const inputJson = { a: input, b: 6 }; // a + b = 10 の前提
    const { verified } = await generateProof(inputJson, '/add.wasm', '/zkey.json');
    setResult(verified ? '証明成功!' : '証明失敗');
  }

  return (
    <div className="p-4">
      <h1 className="text-xl font-bold mb-2">ZKP Web Demo</h1>
      <input type="number" value={input} onChange={e => setInput(Number(e.target.value))} className="border p-1" />
      <button onClick={handleProve} className="ml-2 px-4 py-1 bg-blue-600 text-white">証明する</button>
      <p className="mt-4">結果: {result}</p>
    </div>
  );
}

📦 WASM & ZKeyファイルの準備

circom add10.circom から生成した

  • add10.wasm
  • circuit_final.zkey

/public に置いてビルド不要で使えるようにしておきます。

cp build/add10_js/add10.wasm public/add.wasm
cp build/circuit_0000.zkey public/zkey.json

✅ 起動してテスト

npm run dev
# http://localhost:3000 にアクセスして、ブラウザで証明を実行

💡 補足:ブラウザZKPの可能性

  • フロントエンドだけで完結する証明
  • スマホやタブレットから証明&検証
  • アカウントレスな認証、KYCなどへの応用

ゼロ知識証明は、今後Web UIにとって「当たり前のプライバシー担保手段」になると期待されます。

🚀 第4章:ゼロ知識証明の実践応用シナリオ集

ゼロ知識証明(ZKP)は単なる暗号理論の学問ではなく、今まさに活用されつつあるリアルな技術です。本章では、ZKPが実際にどのような場面で使われているのか、そしてあなたのアプリにどのように活かせるのかを紹介していきます。


1️⃣ ZK-Login:ウォレットアドレスで本人確認

用途:Web3アプリにおける本人認証

仕組み:MetaMaskやSui Walletなどを用いたZKログインは、「秘密鍵を持っていること」をZKPで証明することで、ウォレット連携や匿名ログインを実現します。

- ユーザーがZK証明でログイン署名を生成
- サーバー側は秘密鍵を知らなくても正当性を検証
- 本人性は担保されるが、実名やメールは非公開のまま!

✅ 代表例:Sui Wallet、Polygon ID


2️⃣ ZK-Vote:匿名アンケート・投票システム

用途:社内アンケート、DAO投票、コンテスト評価

メリット

  • 誰が投票したかを伏せたまま
  • 1人1票や資格者制限などを守れる
- 回路で「許可された投票者IDか?」を検証
- 投票結果だけを集計
- 不正や多重投票はZKPで排除

✅ 実例:MACI(Minimum Anti-Collusion Infrastructure)


3️⃣ ZK-Access:資格証明・年齢確認・KYC代替

用途

  • サービス利用資格(例:18歳以上、会員など)
  • オンライン試験の受験権限
  • 匿名状態でのKYCチェック

「私は18歳以上です(生年月日から計算済み)」を証明するが、
実際の年齢・誕生日は一切公開しない。

Circomで簡易的に以下のような回路が書けます:

signal input birthYear;
signal output isAdult;
component yearCheck = LessThan();
yearCheck.in[0] <== birthYear;
yearCheck.in <== 2006;  // 現在年 - 18
isAdult <== yearCheck.out;

✅ 代表例:zkKYC、Polygon ID、Worldcoin Proof of Personhood


4️⃣ ZK-Asset:残高や所有権のプライバシー保護

用途:ZK-Rollup、プライバシーコイン、NFT非公開送付

機能例

  • 「私は十分なETHを持っている」ことを証明し送金
  • NFTを所有しているかを証明し、画像閲覧を許可

✅ 実例:Zcash(ZKによる完全匿名通貨)、Aztec、Railgun


5️⃣ ZK-Captcha / Proof of Human

用途:ボット検出、Web3投票、AI判定の補助など

特徴

  • Google reCAPTCHAを使わずに「人間らしさ」をZKで証明
  • プライバシーを漏らさず、AI/自動化スパムをブロック

✅ 実例:Proof of Humanity、zkCaptcha、BrightID


💬 まとめ:ZKPは未来のインフラになる

ゼロ知識証明は、以下の2つを同時に叶える魔法の技術です:

  • 🔏 プライバシーの保持
  • ✅ 信頼の証明(正しさの保証)

今後のインターネットやWeb3、メタバース、AIアシスタント社会において、「余計な情報は明かさず、必要なことだけ証明する」ZKPの思想は、標準的な存在になることが予想されます。

🎁 おまけ:匿名アンケートUIのサンプルコード(簡易版)

<!-- 匿名アンケートフォーム(ZKP未接続) -->
<form id="zkp-vote" onsubmit="submitVote(event)">
  <label>あなたの意見:</label><br>
  <input type="radio" name="vote" value="yes"> はい<br>
  <input type="radio" name="vote" value="no"> いいえ<br>
  <button type="submit">送信する</button>
</form>
<div id="result"></div>

<script>
async function submitVote(e) {
  e.preventDefault();
  const vote = document.querySelector('input[name="vote"]:checked').value;

  // 将来的にはここで ZKP を生成
  const fakeZKProof = { proof: '0xabc123...', valid: true };

  document.getElementById('result').innerHTML =
    `あなたの投票「${vote}」は匿名で送信されました!ZK検証結果:${fakeZKProof.valid}`;
}
</script>

このサンプルはZKPとの接続こそしていませんが、UI構造や「ZKで投票を隠す」フローを模倣したものです。 SnarkJSの証明生成と publicSignals を取得して送信すれば、簡易なZK投票がWeb UIだけで実現可能です。