Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

シナリオ一覧

改ざんシナリオ S0〜S5 の定義と、実装上どこを改変するかを整理します。ここでは「zkVM 入力」「主張集計(claimed tally)」「ジャーナル統計(missing/invalid/excluded)」の関係を中心に説明します。

教育モードの目的

改ざんシナリオは、暗号的検証が実際に機能することを確認するために設計されています。

  • 正常ケース(S0)を基準として、検証パイプラインが通過する状態を確認する
  • 攻撃シナリオ(S1〜S5)を適用して、どの不変条件が破れると検証が失敗するかを確認する

攻撃の 2 類型

  • 入力改ざん (tamperMode=input): S1 / S3 / S5
  • 主張改ざん (tamperMode=claim): S2 / S4

この図は「改ざんがどこに入るか」の分類のみを示します。どのチェックで失敗するかの詳細は 検出メカニズム を参照してください。


実装上の共通前提

  • 1 回の finalize で選択されるシナリオは 1 つ(S0〜S5)
    • UI: /aggregate は single-select(S0〜S5 のラジオボタン)
    • API: POST /api/finalizescenarioId を 1 つ受け取る
  • totalExpected は 64(ユーザー 1 + ボット 63)
  • 掲示板(CT Merkle)は追記専用で、シナリオ適用で既存エントリは削除しない
  • tamperModenone / input / claim の 3 種

注意事項:

  • 本章は実 API 経路(/api/finalizefinalize-sessionfinalize-sync|async)を基準に説明する
  • finalize 実行モードは FINALIZE_ASYNC_MODE で切り替わる(false: 同期, true: 非同期)。AWS 運用では通常 true
  • NEXT_PUBLIC_USE_MOCK_API=true の mock API fixture は本章と異なるチェック結果を返すことがある
  • 本章の「主な失敗点」は STARK 検証が success の局面を前提とする(zkGate の詳細は検出メカニズムを参照)

tamperMode により、zkVM 入力へ反映されるかどうかが決まります。

flowchart TD
  A[シナリオ選択] --> B{tamperMode}
  B -->|none / claim| C[元の votes を zkVM 入力へ]
  B -->|input| D[modifiedVotes を zkVM 入力へ]
  C --> E[zkVM 実行]
  D --> E

S0: 正常(改ざんなし)

改ざんを適用しない基準シナリオです。

項目
tamperModenone
zkVM 入力票数64
claimed と verified一致
excludedSlots0

S1: ユーザー票の除外

ユーザー票(インデックス 0)を modifiedVotes から削除し、63 票を zkVM に渡します。

項目
tamperModeinput
zkVM 入力票数63
claimed と verified一致(どちらも 63 票入力ベース)
ジャーナル統計missingSlots=1, invalidPresentedSlots=0, excludedSlots=1

ポイント:

  • 掲示板上のユーザー票エントリは残る
  • 検出は主に完全性チェック(excludedSlots > 0
  • ビットマップ証明が利用可能なら counted_my_vote_included でも検出可能

S2: ユーザー票に関する主張集計の改ざん

ユーザー票に対する「主張集計(表示する tally)」のみ改ざんします。zkVM には元の 64 票を渡します。

項目
tamperModeclaim
zkVM 入力票数64(元データ)
claimed と verified不一致(ユーザー選択肢が -1、別候補が +1)
excludedSlots0(通常)
inputCommitmentzkVM 入力由来のため通常は一致

ポイント:

  • 「票の中身を zkVM 入力で差し替える」実装ではない
  • レシートや STARK 証明は通常どおり有効
  • 検出の主因は counted_tally_consistent の失敗

S3: ボット票の除外

現行実装ではボット票インデックス 1targetBotId 初期値)を削除し、63 票を zkVM に渡します。

項目
tamperModeinput
zkVM 入力票数63
claimed と verified一致(どちらも 63 票入力ベース)
ジャーナル統計missingSlots=1, invalidPresentedSlots=0, excludedSlots=1

S1 との違い:

  • S1: ユーザー自身の未集計をビットマップで直接示せる
  • S3: ユーザー票は含まれるが、集計全体の完全性違反で検出される

S4: ボット票に関する主張集計の改ざん

1 票のボット票に関する「主張集計」だけを改ざんします。zkVM 入力は元の 64 票のままです。

項目
tamperModeclaim
zkVM 入力票数64(元データ)
claimed と verified不一致(対象ボットの元候補が -1、別候補が +1)
excludedSlots0(通常)
inputCommitmentzkVM 入力由来のため通常は一致

ポイント:

  • S2 と同様に、改ざん対象は tally.counts
  • 検出の主因は counted_tally_consistent の失敗

S5: ランダムエラー注入

64 票からランダムに 1 票を選び、50% で「除外」または「再集計(別候補化)」を行います。

実装上の重要点:

  • tamperMode は常に input
  • そのため zkVM 入力は常に modifiedVotes が使われる
  • 除外パスでは missingSlots が増え、再集計パスでは不正票化により invalidPresentedSlots が増えるため、いずれも excludedSlots > 0 になる
  • 再集計パスでは counted_tally_consistent も失敗する(claimedCounts は 64 票ベース、verifiedTally は commitment 不一致の票を除外した 63 票ベース)
分岐zkVM 入力代表的な統計
除外パス63 票missingSlots=1, invalidPresentedSlots=0, excludedSlots=1
再集計パス64 票missingSlots=0, invalidPresentedSlots=1, excludedSlots=1

再集計パスでも excludedSlots が 0 にならないのは、zkVM 側で不正票として除外されるためです。


シナリオ一覧表

シナリオ類型tamperModezkVM 入力主な失敗点(STARK 解決後)
S0正常none元の 64 票なし
S1除外input63 票(ユーザー除外)excludedSlots > 0
S2主張改ざんclaim元の 64 票claimed ≠ verified
S3除外input63 票(ボット除外)excludedSlots > 0
S4主張改ざんclaim元の 64 票claimed ≠ verified
S5ランダム(実装上 input)input63 または 64 票excludedSlots > 0(再集計では claimed ≠ verified も発生)

ジャーナル統計の扱い(sync / async 共通)

現行実装では、missingSlots / invalidPresentedSlots / excludedSlots / validVotes などのジャーナル統計は、sync / async いずれも zkVM が返した proof-derived な値をそのまま使います

  • sync / async いずれも、finalize 後にジャーナル統計を上書きしない
  • async finalize はコールバックで bundle.zip から復元した journal をそのまま使う

シナリオ由来の ignoredCount / recountedCount / claimedCounts は presentation 用であり、journal の統計値を書き換えません。

  • ignoredCount / recountedCounttamperSummarytamperedCount に反映
  • claimedCounts → S2/S4 や S5 再集計分岐での表示用 tally

tamperMode=claim(S2/S4)でも同様に、journal 統計は zkVM の値のままです。


集計フローへの挿入点

flowchart TB
  A[セッション votes 読み込み] --> B[シナリオ適用]
  B --> C{tamperMode}
  C -- input --> D[modifiedVotes で zkVM 入力生成]
  C -- claim --> E[元の votes で zkVM 入力生成]
  D --> F[zkVM 実行]
  E --> F
  F --> G[finalizationResult 保存]
  B --> H[claimedCounts 計算]
  H --> G