Go でこんなコードを見て、すぐに違和感を持てるだろうか。
switch mode {
case modeBase | modeAuth:
// PSK は不要
case modePSK | modeAuthPSK:
// PSK が必須
}
| はビット論理和だ。HPKE のモード値は RFC 9180 で modeBase=0x00、modePSK=0x01、modeAuth=0x02、modeAuthPSK=0x03 と決まっている。すると modeBase | modeAuth は 0x02、modePSK | modeAuthPSK は 0x03 に潰れる。書いた本人は「4つのモードを2つのケースにまとめた」つもりでも、この switch が実際に一致するのは modeAuth と modeAuthPSK だけだ。modePSK(0x01)は素通りし、PSK(事前共有鍵)必須のはずの経路で鍵の検証が丸ごと抜け落ちる。認証の弱いモードへ静かに落ちる、地味だが本物のバグである。
これは Cloudflare の暗号ライブラリ CIRCL に実在したもので、監査会社 zkSecurity が AI を使った監査で見つけた7件のうちの1件だ。全て修正済みで、この HPKE の件は verifyPSKInputs() を仕様に合わせるコミット a3b4fa3 で case modeBase, modeAuth: のようにカンマ区切りへ直された(ついでに空スライスを有効な PSK と誤認する psk != nil も len(psk) != 0 に修正されている)。
🔍 1000件の候補を7件に絞る、というワークフロー
CIRCL は Cloudflare が TLS やポスト量子暗号の実験に使う Go 製ライブラリで、README 自身が用途を選ぶよう釘を刺している。
This library is offered as-is, and without a guarantee. ... We recommend to take caution before using this library in a production application since part of its content is experimental.
zkSecurity の記事によれば、彼らは200以上の暗号プロジェクトに AI 監査をかけ、1000件を超える「候補」を生成し、そこから人手のトリアージで本物だけを残した。使ったのは Claude Opus 4.6 と GPT-5.3 で、素のプロンプトを投げる構成と、暗号の専門知識を「スキル」として与えた構成の2通り。CIRCL に残ったのが次の7件だ。
| 箇所 | バグの性質 | 影響 |
|---|---|---|
tss/rsa/rsa_threshold.go |
float64 へのキャストで精度喪失(10^52 が 53bit 仮数を超える) |
閾値 RSA の鍵シェアが壊れる |
zk/qndleq |
SecParam が Proof 構造体内にあり検証者が使ってしまう |
チャレンジを1bit に縮められ健全性が崩壊 |
sign/bls |
集約署名で鍵の相異チェック欠落 | rogue key 攻撃 |
zk/qndleq |
FillBytes が符号を落とし衝突 |
特定の証明で偽造成功率50% |
hpke/util.go |
switch の ` | ` 誤用(上記) |
tss/rsa/rsa_threshold.go |
Lagrange 係数の桁溢れと除算順序 | 署名再構成の破損 |
abe/cpabe/.../formula.go |
AND ゲートのシェア計算ミス | 葉ノード1つで秘密を全復元、ポリシー無効化 |
zk/qndleq の件は特に暗号らしい落とし穴だった。証明の健全性を決める安全パラメータが証明そのものに同梱され、検証側がそれを信じて再計算していた。攻撃者が SecParam = 1 を送れば、コイン投げ1回ぶんの確率で偽造が通る。修正コミット 757dde4 では構造体フィールドを非公開化し、128bit 未満なら the security parameter must be greater than 128 を返して弾くようになった。パラメータを外から触らせない、という基本を AI が突いた形だ。
🤖 AI の判断はどこで当たり、どこで外したか
面白いのは、バグそのものより「AI の目利き」の癖のほうだ。記事は深刻度評価が非対称にズレたと報告している。5件は AI が過大評価した一方、BLS の rogue key 攻撃は medium と過小評価した(Cloudflare は high)。逆に閾値 RSA の float64 は AI が critical、Cloudflare は実際の起こりやすさから low とした。深刻度は「そのコードを誰がどう呼ぶか」という library の外側の文脈に依存する。単体のコードだけ見せられた AI が最も苦手にする部分がそこに出た、という説明には納得感がある。
もう2つ、実務者として頭に入れておきたい傾向がある。ひとつは、AI が個々の欠陥を拾っても連鎖させないこと。Lagrange 係数のバグは桁溢れと除算順序という独立した2つの問題を含んでいたのに、AI は両者を並べただけで相互作用は説明しなかった。もうひとつは逆に、離れたコードをまたぐ推論は見せること。FillBytes の件では「(-1)^偶数=1」という代数の性質と、符号を捨てる直列化の実装を別々の場所から結びつけて健全性の崩れを指摘したという。得意と不得意がはっきり分かれている。
そして地味に重いのがモデル順位の不安定さだ。最初の試行では Opus 4.6 が発見役、GPT-5.3 が検証役だったのが、数週間後には GPT-5.4 が発見役、Opus 4.7 が検証役に入れ替わったと記事は書く。「今どのモデルが一番か」という結論の賞味期限は短い。自動監査を組むなら特定モデルに寄せず、発見と検証で別モデルを当てて多数決を取る構成のほうが現実的だろう。
🛡️ 暗号ライブラリが AI エージェント向けの作法を明文化した
もう一点、書いておきたい変化がある。CIRCL のリポジトリには AGENTS.md が置かれ、AI エージェントが暗号コードを触るときの掟が明文化された。定数時間の維持(秘密データに依存する分岐・メモリアクセス・ループ境界を作らない)、API 境界での入力検証、直列化フォーマットを黙って変えないこと、バグ修正には必ず回帰テストを足すこと。締めの一文が象徴的だ。
Treat every diff as if a cryptographer will read it on Monday.
AI にバグを見つけさせる話と、AI に安全なコードを書かせる話は表裏一体で、後者には人間が読める規約が要る。zkSecurity 自身も「AI が出すのは最終レポートではなく候補で、悪用可能性の検証と開示は人が担った」と繰り返している。1000件を7件に絞ったのは AI ではなく人のトリアージだ。
現場への持ち帰りは単純だと思う。AI コードレビューは、見落としを拾う網としては既に十分に鋭い。ただし深刻度と優先順位づけはまだ信用しきれないので、そこは人が握る。暗号やプロトコルのような「呼び出し側次第で致命度が変わる」領域ではなおさらだ。網は AI に張らせ、どの魚を捌くかは自分で決める。しばらくはこの分業が正解に見える。