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

検出メカニズム

各改ざんシナリオに対して、検証パイプラインがどのチェックで失敗するかを整理します。 このページは実 API の判定ロジックを基準にしており、STARK 検証が success になった後の挙動を前提とします。

前提

  • 実行モードと検証の前提は シナリオ一覧 > 実行モードと検証の前提 と同じ。
  • 現行の verification resource は GET /api/sessions/:sessionId/finalizations/:finalizationId/verification で取得する。
  • サーバー応答は castSource=client のため、raw な verification.checkscast_* は シナリオに関係なく not_run。ブラウザは保持している投票内容と乱数、サーバーから取得した exact receipt を使って local cast checks を評価し、この 4 チェックを overlay してから表示用の stage と最終判定を導出する。

Counted 系チェックの zkGate

verification resource の Counted 系チェックには、STARK 前に評価できる項目と、STARK 状態でゲートされる項目が混在します。

  • counted_input_sanity / counted_unique_indices / counted_unique_commitments は、publicInputArtifact から導出した内部 publicInputAuthority があれば STARK 未解決でも評価されます。
  • counted_tally_consistent / counted_missing_indices_zero / counted_expected_vs_tree_size / counted_election_manifest_consistent / counted_close_statement_consistent / counted_my_vote_included / counted_input_commitment_match は zkGate の対象です。
  • STARK 未解決(not_run/running)の間、zkGate 対象チェックは原則として not_run または pending になります。
  • 例外として、counted_missing_indices_zero は journal count の異常や excludedSlots > 0 が既に解決できる場合、STARK 未解決でも fail-closed に failed になり得ます。
  • verifierResult.status=failed では、zkGate 対象チェックも failed になり得ます。

zkGate の一般規則は ゲーティングロジック > zkGate を参照してください。

検出の 2 つの原理

  • 原理1: 完全性違反 (excludedSlots > 0) → counted_missing_indices_zero が失敗(主に S1/S3/S5)。
  • 原理2: 主張集計の不整合 (claimed ≠ verified) → counted_tally_consistent が失敗(主に S2/S4)。

シナリオ別の主な失敗チェック(STARK 解決後)

シナリオ主に失敗するチェック説明
S0なし正常系
S1counted_missing_indices_zeroユーザー票除外により excludedSlots=1
S2counted_tally_consistentclaimed tally と verified tally が不一致
S3counted_missing_indices_zero現行実装では botId=1 のボット票除外により excludedSlots=1
S4counted_tally_consistentclaimed tally と verified tally が不一致
S5counted_missing_indices_zeroランダムに 1 票を除外するため excludedSlots>0 が発生し、完全性違反として検出される

補足:

  • S1 では、ビットマップ証明が利用可能な場合 counted_my_vote_included も失敗し得ます。
  • S2/S4 では、zkVM 入力を改変していないため、counted_input_commitment_match は通常成功します。
  • S5 ではランダム対象がユーザー票の場合、ビットマップ証明が利用可能なら counted_my_vote_included も失敗し得ます。

4 段階検証モデルとの対応(raw サーバー応答)

検証段階S0S1S2S3S4S5
Cast-as-Intendednot_runnot_runnot_runnot_runnot_runnot_run
Recorded-as-Castsuccesssuccesssuccesssuccesssuccesssuccess
Counted-as-Recordedsuccessfailedfailedfailedfailedfailed
STARK Verificationsuccesssuccesssuccesssuccesssuccesssuccess

この表は「シナリオ適用による典型挙動」を示します。 running や追加の not_run は、運用状態や証拠不足により別途発生します。 ここでの値はサーバーが返す verification.steps の状態です。 verifierResult.status は STARK receipt 検証の状態であり、overall verdict そのものではありません。

ブラウザ表示では 前提 の overlay を適用します。 典型フローでは overlay 後の Cast-as-Intended は success になり、ローカル証拠が欠けるか不整合なら not_run または failed のままで、最終表示は Verified になりません。

主要チェック ID マトリクス(STARK 解決後の raw サーバーチェック)

チェック IDS0S1S2S3S4S5
cast_commitment_matchnot_runnot_runnot_runnot_runnot_runnot_run
counted_tally_consistentsuccesssuccessfailedsuccessfailedsuccess
counted_missing_indices_zerosuccessfailedsuccessfailedsuccessfailed
counted_my_vote_includedsuccessfailed または not_runsuccesssuccesssuccess対象依存
counted_input_commitment_matchsuccesssuccesssuccesssuccesssuccesssuccess
  • 証拠不足時の counted_my_vote_includednot_run になります。
  • S5 の counted_tally_consistent が通常成功するのは、claimed tally と verified tally がどちらも除外後の 63 票入力ベースだからです。
  • cast_commitment_match を含む 4 つの cast_*not_run は raw サーバー値であり、前提 の overlay 後は通常 success になります。

S2/S4 の主張集計改ざん

S2/S4 は「入力改ざん」ではなく「主張集計改ざん」です。

flowchart TD
  A["tamperMode=claim (S2/S4)"]
  A --> V1["元の votes を zkVM 入力へ"]
  V1 --> V2["verifiedTally"]
  A --> C1["claimedCounts を改変"]
  C1 --> C2["API の presentation.tally.counts"]
  V2 --> X{"claimed と verified は一致?"}
  C2 --> X
  X -->|不一致| F["counted_tally_consistent = failed"]

このため、counted_input_commitment_match の失敗は通常発生しません。 zkVM 入力は元票であり、投票レシートと STARK 証明も有効なままです。

S5 の除外処理

S5 の定義と処理内容(tamperMode=inputmodifiedVotes、ジャーナル統計の扱い)はシナリオ一覧 > S5を参照してください。 検出の主因は、missingSlots=1 / excludedSlots=1 による counted_missing_indices_zero の失敗です。

ビットマップ証明の役割

counted_my_vote_included は、チェック定義上 required のユーザー包含チェックです。

  • S1(ユーザー票除外)では、証明が利用可能なら失敗して「自票が未集計」であることを直接示せる。
  • 証拠不足で not_run になる場合でも、最終判定は Verified になりません:
    • 完全性違反が同時にある場合は votes_excluded_unknown になります。
    • 完全性違反がなく required evidence が欠ける場合は missing_evidence になります。

最終判定(Verified 表示)

最終表示は共有 verification-presentation-policy が決定します(厳密な判定条件は ゲーティングロジック を参照)。 ブラウザが policy に渡すのは、前提 の overlay 後の完全なチェック集合です。 policy は verification-summary に加えて、resource の availability、fail-closed exclusion signal、明示的な proof failure、検証シーケンスの完了状態を統合します。

flowchart TB
  A[raw サーバーチェック] --> B[browser-local cast checks を overlay]
  B --> C[共有 presentation policy]
  C --> D[summary + hard failure + explicit proof failure]
  D --> E{表示準備完了?}
  E -- no --> W[Verified を表示しない]
  E -- yes --> F{fullyVerifiedLabelEligible?}
  F -- no --> N[failed / warning / demo_only]
  F -- yes --> V[Verified]

fullyVerifiedLabelEligible が true になるのは、summary が fully_verified、hard failure がなく、 明示的な proof failure もなく、表示 readiness が ready で rendered status が verified の場合だけです。 summary 内の分類順序は ゲーティングロジック > ステータスの判定順序 を参照してください。

代表的な失敗ステータス:

  • user_vote_excluded / votes_excluded / votes_excluded_unknown: 完全性違反(S1/S3/S5)
  • published_tally_mismatch: claimed と verified の不一致(S2/S4)
  • counted_integrity_failed: Counted 系必須チェック失敗の一般ケース

これらの検出経路は、単体、結合、E2E テスト の CLI と E2E フローで補強しています。 Merkle と journal の不変条件は、Property-based Testing でも補強しています。