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

STH ダイジェスト

Signed Tree Head (STH) ダイジェストによる分割ビュー攻撃の緩和メカニズムを解説します。

ログ ID、ツリーサイズ、タイムスタンプ、掲示板ルートを束縛するダイジェストにより、サーバーが異なるクライアントに異なるツリー状態を提示する攻撃を検出可能にします。

概要

分割ビュー攻撃(split-view attack)とは、悪意あるサーバーが異なる検証者に対して異なる掲示板の状態を提示する攻撃です。例えば、投票者 A には「全 64 票が含まれたツリー」を見せながら、投票者 B には「特定の票が除外されたツリー」を見せることが考えられます。

STH ダイジェストは、掲示板の状態を(ログ ID、ツリーサイズ、タイムスタンプ、ルートハッシュ)の組として束縛し、独立した第三者ソースとの合意確認を通じてこの攻撃を検出します。

flowchart TD
  subgraph "STH ダイジェストの構成"
    LID[ログ ID<br/>32 バイト]
    TSZ[ツリーサイズ<br/>4 バイト]
    TS[タイムスタンプ<br/>8 バイト]
    BR[掲示板ルート<br/>32 バイト]
  end

  LID --> H[SHA-256]
  TSZ --> H
  TS --> H
  BR --> H
  H --> STH[STH ダイジェスト<br/>32 バイト]

  STH --> J[zkVM ジャーナルに記録]
  STH --> TP[第三者ソースに公開]

ダイジェストフォーマット

sth_digest = SHA-256(
    log_id        ← 32 バイト
    || tree_size  ← u32 リトルエンディアン (4 バイト)
    || timestamp  ← u64 リトルエンディアン (8 バイト, Unix 時刻ミリ秒)
    || bulletin_root ← 32 バイト
)

SHA-256 への入力は合計 76 バイトです。

各フィールドの仕様

フィールドサイズエンコーディング説明
ログ ID32 バイトハッシュ値掲示板インスタンスの識別子
ツリーサイズ4 バイトu32 LE掲示板のリーフ数
タイムスタンプ8 バイトu64 LEUnix 時刻(ミリ秒)
掲示板ルート32 バイトハッシュ値Merkle ツリーのルート

ログ ID

ログ ID は掲示板インスタンスを一意に識別するための値であり、以下のように生成されます:

log_id = SHA-256("stark-ballot:bulletin-log|v1.0" || seed)

ドメインタグ "stark-ballot:bulletin-log|v1.0" と任意のシード値を連結し、SHA-256 でハッシュします。ログ ID は掲示板のライフタイム中に変化しない固定値です。

ログ ID を STH ダイジェストに含めることで、異なる掲示板インスタンスの STH が偶然に衝突することを防止します。

分割ビュー攻撃と検出メカニズム

攻撃シナリオ

sequenceDiagram
    participant A as 投票者 A
    participant S as 悪意あるサーバー
    participant B as 投票者 B

    S->>A: ツリー状態 X<br/>(64 票、ルート R₁)
    S->>B: ツリー状態 Y<br/>(63 票、ルート R₂)
    Note over A,B: A と B は互いに異なる<br/>ツリー状態を見ている

この攻撃では、サーバーは投票者 B に対して特定の票を除外したツリーを見せています。投票者 B は自身に提示されたツリーに対する包含証明や整合性証明を検証できますが、投票者 A とは異なるツリーを見ていることに気づけません。

第三者合意による検出

検証者は、NEXT_PUBLIC_STH_SOURCES に設定された独立ソースへ問い合わせ、ジャーナル内の STH ダイジェストと照合することで分割ビューを検出できます。

sequenceDiagram
    participant V as 検証者
    participant S as サーバー
    participant T1 as 第三者ソース 1
    participant T2 as 第三者ソース 2

    V->>S: ジャーナルから STH ダイジェスト D' を取得
    V->>T1: STH を問い合わせ → D₁
    V->>T2: STH を問い合わせ → D₂
    V->>V: D' = D₁ = D₂ ?
    alt 全て一致
        V->>V: 合意成立 → 検証成功
    else 不一致あり
        V->>V: 分割ビューの疑い → 検証失敗
    end

現行実装は第三者ソースへの照会(fetch)を行うものであり、アプリケーションが第三者ソースへ STH を自動公開する機能は含みません。

合意ロジック

第三者 STH 検証は以下の条件をすべて満たした場合に成功します:

  1. 十分な一致数: 一致するソースの数が最小要求数(コードフォールバック値: 2)以上
  2. 全会一致: 応答可能なすべてのソースが一致すること(matchingSources = comparableSources

各ソースに対して以下のフィールドが照合されます:

照合フィールド条件
STH ダイジェスト必須一致
掲示板ルート提供されている場合は一致
ツリーサイズ提供されている場合は一致

zkVM との連携

zkVM ゲストプログラムは、入力として受け取ったログ ID、ツリーサイズ、タイムスタンプ、掲示板ルートから STH ダイジェストを再計算し、ジャーナルにコミットします。

この仕組みにより、STARK 証明が特定のツリー状態に対して生成されたことが保証されます。第三者はジャーナルの STH ダイジェストを独立ソースの値と照合することで、サーバーが証明と異なるツリー状態を提示していないかを確認できます。

検証パイプラインにおける役割

STH ダイジェストの第三者検証は、Recorded-as-Cast 段階のチェックとして実行されます。

チェック ID検証内容
recorded_sth_third_party独立ソースから取得した STH ダイジェストがジャーナルの値と一致するか

このチェックが有効化されている場合(STH ソースが設定されている場合)、合意が得られなければ「Verified」ステータスはブロックされます。

設定

第三者 STH 検証は環境変数で制御されます。

環境変数説明コードフォールバック値
NEXT_PUBLIC_STH_SOURCESカンマ区切りの STH ソース URL未設定(第三者照合を実行しない)
NEXT_PUBLIC_STH_MIN_MATCHES必要な最小一致ソース数2

STH ソースが未設定の場合、recorded_sth_third_partynot_run(未実行)となり、第三者照合は行いません。セキュリティ上は少なくとも 2 つ以上の独立ソースを設定することが推奨されます。

相対パス(例: /api/sth)はリクエスト元のオリジンに対して解決されます。

開発用の .env.local.example では次の値が設定されています:

  • NEXT_PUBLIC_STH_SOURCES=/api/sth
  • NEXT_PUBLIC_STH_MIN_MATCHES=1

この設定は PoC の動作確認を優先したものです。本番運用では、独立した複数ソースと NEXT_PUBLIC_STH_MIN_MATCHES >= 2 を推奨します。

PoC における制約

本 PoC の開発用テンプレート(.env.local.example)では、STH ソースとして同一サーバー上の API エンドポイント(/api/sth)を使用します。実運用環境では、独立した組織が運営する複数のソースを使用する必要があります。同一サーバー上のソースのみでは、分割ビュー攻撃に対する防御力が限定的です。