wycheproof について
Wycheproof は、暗号実装の正しさや既知の攻撃への対応を検証するテストベクトル集です。暗号ライブラリの開発者や監査担当者向けに、アルゴリズムやパラメーター別の分類と期待結果を提供します。
主な機能
- 01複数のアルゴリズムと曲線のベクトル
- 02valid、acceptable、invalid の結果区分
- 03パラメーター別のテストグループ
- 04JSON 形式のテストベクトル
- 05テストハーネスと CI の統合ガイド
活用シーン
- 01AES-GCM の暗号化と復号を検証
- 02ECDSA の署名エンコーディングを確認
- 03ECDH が無効な曲線点を拒否するかテスト
- 04CI で暗号実装の回帰を検出
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ trailofbits/ skills --skill wycheproofインストール後、Claude Code で/wycheproofを入力して読み込まれたか確認します。
同梱ファイルagents/openai.yamlassets/trail-of-bits-mark.svg
詳細
Wycheproof
Wycheproof は、暗号実装の正確性を検証し、既知の攻撃に対するテストを行うために設計された、広範なテストベクトル集です。Google が開発した当初から、現在はコミュニティが管理するプロジェクトとなっており、貢献者は特定の暗号構成に対するテストベクトルを追加できます。
背景
主な概念
| 概念 | 説明 |
|---|---|
| テストベクトル | 暗号実装の正確性を検証するための入力と出力のペア |
| テストグループ | 属性(鍵サイズ、IV サイズ、曲線)を共有するテストベクトルの集まり |
| 結果フラグ | テストが成功(有効)、失敗(無効)、または許容されるかを示します |
| エッジケーステスト | 既知の脆弱性や攻撃パターンを対象としたテスト |
重要な理由
暗号実装を正しく実装するのは非常に困難です。小さなバグでも、次のような問題を引き起こす可能性があります。
- 秘密鍵が漏えいする
- 署名の偽造が可能になる
- メッセージが復号される
- 実装によって同じ入力を受け入れるか拒否するかが異なり、コンセンサスの問題が発生する
Wycheproof は、OpenJDK の SHA1withDSA、Bouncy Castle の ECDHC、npm の elliptic パッケージなど、主要なライブラリの脆弱性を発見してきました。
使用する場面
Wycheproof を使用する場面:
- 暗号実装(AES-GCM、ECDSA、ECDH、RSA など)をテストする
- 暗号コードがエッジケースを正しく処理することを検証する
- 既知の攻撃ベクトルに対する実装の動作を検証する
- 暗号ライブラリ向けの CI/CD を設定する
- サードパーティ製の暗号コードの正確性を監査する
代替手段を検討する場面:
- タイミングサイドチャネルをテストする場合(代わりに定時間テストツールを使用してください)
- 未知の新たなバグを見つける場合(代わりにファジングを使用してください)
- 独自または実験的な暗号アルゴリズムをテストする場合(Wycheproof は確立されたアルゴリズムのみを対象としています)
クイックリファレンス
| シナリオ | 推奨される方法 | 備考 |
|---|---|---|
| AES-GCM の実装 | aes_gcm_test.json を使用します | 44 個のテストグループにわたる 316 個のテストベクトル |
| ECDSA の検証 | 特定の曲線に対応する ecdsa_*_test.json を使用します | 署名の可鍛性と DER エンコーディングをテストします |
| ECDH 鍵交換 | ecdh_*_test.json を使用します | 不正な曲線を利用した攻撃をテストします |
| RSA 署名 | rsa_*_test.json を使用します | パディングオラクル攻撃をテストします |
| ChaCha20-Poly1305 | chacha20_poly1305_test.json を使用します | AEAD の実装をテストします |
テストのワークフロー
Phase 1: Setup Phase 2: Parse Test Vectors
┌─────────────────┐ ┌─────────────────┐
│ Add Wycheproof │ → │ Load JSON file │
│ as submodule │ │ Filter by params│
└─────────────────┘ └─────────────────┘
↓ ↓
Phase 4: CI Integration Phase 3: Write Harness
┌─────────────────┐ ┌─────────────────┐
│ Auto-update │ ← │ Test valid & │
│ test vectors │ │ invalid cases │
└─────────────────┘ └─────────────────┘
リポジトリの構成
Wycheproof リポジトリは、次のように構成されています。
┣ 📜 README.md : Project overview
┣ 📂 doc : Documentation
┣ 📂 java : Java JCE interface testing harness
┣ 📂 javascript : JavaScript testing harness
┣ 📂 schemas : Test vector schemas
┣ 📂 testvectors : Test vectors
┗ 📂 testvectors_v1 : Updated test vectors (more detailed)
主要なフォルダーは testvectors と testvectors_v1 です。どちらにも類似したファイルが含まれていますが、testvectors_v1 にはより詳細な情報が含まれているため、新しい統合にはこちらを推奨します。
サポート対象のアルゴリズム
Wycheproof は、幅広い暗号アルゴリズムのテストベクトルを提供しています。
| カテゴリ | アルゴリズム |
|---|---|
| 共通鍵暗号 | AES-GCM、AES-EAX、ChaCha20-Poly1305 |
| 署名 | ECDSA、EdDSA、RSA-PSS、RSA-PKCS1 |
| 鍵交換 | ECDH、X25519、X448 |
| ハッシュ | HMAC、HKDF |
| 曲線 | secp256k1、secp256r1、secp384r1、secp521r1、ed25519、ed448 |
テストファイルの構成
各 JSON テストファイルは、特定の暗号構成をテストします。すべてのテストファイルに共通の属性があります。
"algorithm" : The name of the algorithm tested
"schema" : The JSON schema (found in schemas folder)
"generatorVersion" : The version number
"numberOfTests" : The total number of test vectors in this file
"header" : Detailed description of test vectors
"notes" : In-depth explanation of flags in test vectors
"testGroups" : Array of one or multiple test groups
テストグループ
テストグループは、次のような共通属性に基づいてテストをグループ化します。
- 鍵サイズ
- IV サイズ
- 公開鍵
- 曲線
この分類を利用すると、テスト対象の構成に関連する特定の条件を満たすテストを抽出できます。
テストベクトルの属性
共通属性
すべてのテストベクトルには、次の4つの共通フィールドがあります。
- tcId:ファイル内でテストベクトルを一意に識別するID
- comment:テストケースに関する追加情報
- flags:特定のテストケースの種類や潜在的な危険性に関する説明(
notesフィールドで参照されます) - result:テストの期待される結果
result フィールドには、次の3つの値を指定できます。
| 結果 | 意味 |
|---|---|
| valid | テストケースは成功するはずです |
| acceptable | テストケースは成功しても問題ありませんが、理想的ではない属性が含まれています |
| invalid | テストケースは失敗するはずです |
固有属性
固有属性は、テスト対象のアルゴリズムによって異なります。
| アルゴリズム | 固有属性 |
|---|---|
| AES-GCM | key、iv、aad、msg、ct、tag |
| ECDH secp256k1 | public、private、shared |
| ECDSA | msg、sig、result |
| EdDSA | msg、sig、pk |
実装ガイド
フェーズ1:Wycheproofをプロジェクトに追加する
オプション1:Gitサブモジュール(推奨)
WycheproofをGitサブモジュールとして追加すると、自動更新が可能になります。
git submodule add https://github.com/C2SP/wycheproof.git
オプション2:特定のテストベクトルを取得する
サブモジュールを利用できない場合は、特定のJSONファイルを取得します。
#!/bin/bash
TMP_WYCHEPROOF_FOLDER=".wycheproof/"
TEST_VECTORS=('aes_gcm_test.json' 'aes_eax_test.json')
BASE_URL="https://raw.githubusercontent.com/C2SP/wycheproof/master/testvectors_v1/"
# Create wycheproof folder
mkdir -p $TMP_WYCHEPROOF_FOLDER
# Request all test vector files if they don't exist
for i in "${TEST_VECTORS[@]}"; do
if [ ! -f "${TMP_WYCHEPROOF_FOLDER}${i}" ]; then
curl -o "${TMP_WYCHEPROOF_FOLDER}${i}" "${BASE_URL}${i}"
if [ $? -ne 0 ]; then
echo "Failed to download ${i}"
exit 1
fi
fi
done
フェーズ2:テストベクトルを解析する
使用するアルゴリズムのテストファイルを特定し、JSONを解析します。
Pythonの例:
import json
def load_wycheproof_test_vectors(path: str):
testVectors = []
try:
with open(path, "r") as f:
wycheproof_json = json.loads(f.read())
except FileNotFoundError:
print(f"No Wycheproof file found at: {path}")
return testVectors
# Attributes that need hex-to-bytes conversion
convert_attr = {"key", "aad", "iv", "msg", "ct", "tag"}
for testGroup in wycheproof_json["testGroups"]:
# Filter test groups based on implementation constraints
if testGroup["ivSize"] < 64 or testGroup["ivSize"] > 1024:
continue
for tv in testGroup["tests"]:
# Convert hex strings to bytes
for attr in convert_attr:
if attr in tv:
tv[attr] = bytes.fromhex(tv[attr])
testVectors.append(tv)
return testVectors
JavaScriptの例:
const fs = require('fs').promises;
async function loadWycheproofTestVectors(path) {
const tests = [];
try {
const fileContent = await fs.readFile(path);
const data = JSON.parse(fileContent.toString());
data.testGroups.forEach(testGroup => {
testGroup.tests.forEach(test => {
// Add shared test group properties to each test
test['pk'] = testGroup.publicKey.pk;
tests.push(test);
});
});
} catch (err) {
console.error('Error reading or parsing file:', err);
throw err;
}
return tests;
}
フェーズ3:テストハーネスを作成する
有効なテストケースと無効なテストケースの両方を処理するテスト関数を作成します。
Python/pytestの例:
import pytest
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
tvs = load_wycheproof_test_vectors("wycheproof/testvectors_v1/aes_gcm_test.json")
@pytest.mark.parametrize("tv", tvs, ids=[str(tv['tcId']) for tv in tvs])
def test_encryption(tv):
try:
aesgcm = AESGCM(tv['key'])
ct = aesgcm.encrypt(tv['iv'], tv['msg'], tv['aad'])
except ValueError as e:
# Implementation raised error - verify test was expected to fail
assert tv['result'] != 'valid', tv['comment']
return
if tv['result'] == 'valid':
assert ct[:-16] == tv['ct'], f"Ciphertext mismatch: {tv['comment']}"
assert ct[-16:] == tv['tag'], f"Tag mismatch: {tv['comment']}"
elif tv['result'] == 'invalid' or tv['result'] == 'acceptable':
assert ct[:-16] != tv['ct'] or ct[-16:] != tv['tag']
@pytest.mark.parametrize("tv", tvs, ids=[str(tv['tcId']) for tv in tvs])
def test_decryption(tv):
try:
aesgcm = AESGCM(tv['key'])
decrypted_msg = aesgcm.decrypt(tv['iv'], tv['ct'] + tv['tag'], tv['aad'])
except ValueError:
assert tv['result'] != 'valid', tv['comment']
return
except InvalidTag:
assert tv['result'] != 'valid', tv['comment']
assert 'ModifiedTag' in tv['flags'], f"Expected 'ModifiedTag' flag: {tv['comment']}"
return
assert tv['result'] == 'valid', f"No invalid test case should pass: {tv['comment']}"
assert decrypted_msg == tv['msg'], f"Decryption mismatch: {tv['comment']}"
JavaScript/Mochaの例:
const assert = require('assert');
function testFactory(tcId, tests) {
it(`[${tcId + 1}] ${tests[tcId].comment}`, function () {
const test = tests[tcId];
const ed25519 = new eddsa('ed25519');
const key = ed25519.keyFromPublic(toArray(test.pk, 'hex'));
let sig;
if (test.result === 'valid') {
sig = key.verify(test.msg, test.sig);
assert.equal(sig, true, `[${test.tcId}] ${test.comment}`);
} else if (test.result === 'invalid') {
try {
sig = key.verify(test.msg, test.sig);
} catch (err) {
// Point could not be decoded
sig = false;
}
assert.equal(sig, false, `[${test.tcId}] ${test.comment}`);
}
});
}
// Generate tests for all test vectors
for (var tcId = 0; tcId < tests.length; tcId++) {
testFactory(tcId, tests);
}
フェーズ4:CIとの統合
テストベクトルを最新の状態に保つには、次の方法があります。
- Gitサブモジュールを使用する:テスト実行前に、CIでサブモジュールを更新します
- 最新のテストベクトルを取得する:テスト実行前に取得スクリプトを実行します
- 定期更新を設定する:新しいテストベクトルを検出できるよう、毎週または毎月の更新を設定します
検出される一般的な脆弱性
Wycheproofのテストベクトルは、特定の脆弱性パターンを検出するように設計されています。
| 脆弱性 | 説明 | 影響を受けるアルゴリズム | CVEの例 |
|---|---|---|---|
| 署名の可鍛性 | 同じメッセージに対して複数の有効な署名が存在する | ECDSA、EdDSA | CVE-2024-42459 |
| 無効なDERエンコーディング | 正規形ではないDER署名を受け入れる | ECDSA | CVE-2024-42460、CVE-2024-42461 |
| 無効な曲線を利用した攻撃 | 無効な曲線上の点を使ったECDH | ECDH | 多くのライブラリで一般的 |
| パディングオラクル | パディング検証時のタイミング情報漏えい | RSA-PKCS1 | OpenSSLで過去に発生した問題 |
| タグの偽造 | 改変された認証タグを受け入れる | AES-GCM、ChaCha20-Poly1305 | 各種実装 |
署名の可鍛性:詳細
問題: 署名エンコーディングを検証しない実装は、同じメッセージに対する複数の有効な署名を受け入れる可能性があります。
例(EdDSA): 署名にゼロを追加または削除する:
Valid signature: ...6a5c51eb6f946b30d
Invalid signature: ...6a5c51eb6f946b30d0000 (should be rejected)
検出方法:
# Add signature length check
if len(sig) != 128: # EdDSA signatures must be exactly 64 bytes (128 hex chars)
return False
影響: 異なる実装が同じ署名を受け入れたり拒否したりすることで、コンセンサスの問題につながる可能性があります。
関連するWycheproofのテスト:
- EdDSA:tcId 37 - 「署名から0バイトを削除」
- ECDSA:tcId 06 - 「レガシー:rのASNエンコーディングで先頭の0が欠落」
ケーススタディ:Elliptic npmパッケージ
このケーススタディでは、Wycheproofが人気の高いelliptic npmパッケージ(依存先3000以上、週に数百万回ダウンロード)で3件のCVEを発見した経緯を紹介します。
概要
ellipticライブラリは、JavaScriptで記述された楕円曲線暗号ライブラリで、ECDH、ECDSA、EdDSAをサポートしています。バージョン6.5.6にWycheproofのテストベクトルを適用した結果、複数の脆弱性が明らかになりました。
- CVE-2024-42459:EdDSA署名の可鍛性(ゼロの追加/削除)
- CVE-2024-42460:ECDSA DERエンコーディング - 無効なビット配置
- CVE-2024-42461:ECDSA DERエンコーディング - 長さフィールドの先頭のゼロ
方法論
- サポート対象の曲線を特定する:EdDSA では ed25519
- テストベクトルを見つける:
testvectors_v1/ed25519_test.json - テストベクトルを解析する:JSON を読み込み、テストを抽出します
- テストハーネスを作成する:パラメーター化されたテストを作成します
- テストを実行する:失敗を特定します
- 根本原因を分析する:実装コードを調べます
- 修正を提案する:検証チェックを追加します
主な調査結果
EdDSA の問題(CVE-2024-42459):
- 署名の長さ検証が不足していました
- 署名の末尾のゼロが許容されていました
- 修正:
if(sig.length !== 128) return false;を追加します
ECDSA の問題 1(CVE-2024-42460):
- DER エンコードされた r 値と s 値の先頭ビットがゼロであることを確認していませんでした
- 修正:
if ((data[p.place] & 128) !== 0) return false;を追加します
ECDSA の問題 2(CVE-2024-42461):
- DER の長さフィールドで先頭のゼロが許容されていました
- 修正:
if(buf[p.place] === 0x00) return false;を追加します
影響
これら3つの脆弱性により、単一のメッセージに対して複数の有効な署名が可能になり、実装間でコンセンサスの問題が発生しました。
得られた教訓:
- Wycheproof は、見つけにくいエンコードのバグを検出します
- 再利用可能なテストハーネスは大きな効果をもたらします
- テストベクトルのコメントやフラグは、問題の診断に役立ちます
- 広く使われているライブラリでも、テストベクトルを体系的に検証するメリットがあります
高度な使い方
ヒントとテクニック
| ヒント | 役立つ理由 |
|---|---|
| パラメーターでテストグループを絞り込む | 実装の制約に関連するテストベクトルに集中できます |
| テストベクトルのフラグを使用する | テスト対象の脆弱性の具体的なパターンを把握できます |
notes フィールドを確認する | フラグの意味について詳しい説明を確認できます |
| 暗号化/復号と署名/検証の両方をテストする | 双方向の正しさを確認できます |
| CI でテストを実行する | リグレッションを検出し、新しいテストベクトルを活用できます |
| パラメーター化されたテストを使用する | tcId とコメントを含む明確な失敗メッセージを取得できます |
よくある間違い
| 間違い | 問題となる理由 | 正しい方法 |
|---|---|---|
| 有効なケースだけをテストする | 無効な入力が受け入れられる脆弱性を見逃します | 有効、無効、許容可能のすべての結果タイプをテストします |
| 「許容可能」な結果を無視する | 実装に見つけにくいバグがある可能性があります | 許容可能な結果も、調査すべき警告として扱います |
| テストグループを絞り込まない | サポートしていないパラメーターのテストに時間を浪費します | 実装に応じて keySize、ivSize などで絞り込みます |
| テストベクトルを更新しない | 新たな脆弱性パターンを見逃します | サブモジュールまたはスケジュールされたフェッチを使用します |
| 一方向だけをテストする | 暗号化/署名は動作しても、復号/検証は失敗する場合があります | 両方の操作をテストします |
関連スキル
ツールスキル
| スキル | Wycheproof テストでの主な用途 |
|---|---|
| pytest | パラメーター化されたテストに使う Python テストフレームワーク |
| mocha | テストの生成に使う JavaScript テストフレームワーク |
| constant-time-testing | Wycheproof をタイミングサイドチャネルテストで補完します |
| cryptofuzz | 追加のバグを見つけるファズベースの暗号テスト |
テクニックのスキル
| スキル | 適用する場面 |
|---|---|
| coverage-analysis | テストベクトルが暗号実装のすべてのコードパスをカバーしていることを確認します |
| property-based-testing | 数学的な性質(暗号化/復号のラウンドトリップなど)をテストします |
| fuzz-harness-writing | 暗号パーサー向けのハーネスを作成します(Wycheproof を補完します) |
関連するドメインスキル
| スキル | 関連性 |
|---|---|
| crypto-testing | Wycheproof は、包括的な暗号テスト手法における重要なツールです |
| fuzzing | Wycheproof ではカバーされないバグ(新たなエッジケース)をファジングで見つけます |
スキルの依存関係マップ
┌─────────────────────┐
│ wycheproof │
│ (this skill) │
└──────────┬──────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ pytest/mocha │ │ constant-time │ │ cryptofuzz │
│ (test framework)│ │ testing │ │ (fuzzing) │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
└───────────────────┼───────────────────┘
│
▼
┌──────────────────────────┐
│ Technique Skills │
│ coverage, harness, PBT │
└──────────────────────────┘
リソース
公式リポジトリ
公式リポジトリには、以下が含まれています:
testvectors/とtestvectors_v1/にあるすべてのテストベクトルschemas/にある JSON スキーマ- Java と JavaScript のリファレンス実装
doc/にあるドキュメント
実際の活用例
pycryptodome ライブラリは、Wycheproof のテストベクトルをテストスイートに統合しており、Python の暗号実装におけるベストプラクティスを示しています。
コミュニティリソース
- C2SP Community - Wycheproof を維持管理する暗号仕様および標準のコミュニティ
- Wycheproof issues tracker - テストベクトルのバグを報告したり、新しい構成を提案したりできます
まとめ
Wycheproof は、既知の攻撃ベクトルやエッジケースに対して暗号実装を検証するために欠かせないツールです。Wycheproof のテストベクトルをテストワークフローに統合することで、次のことが可能になります。
- 微妙なエンコードや検証のバグを発見する
- 署名の可鍛性に関する問題を防ぐ
- 実装間で一貫した動作を確保する
- コミュニティから提供されたテストベクトルを活用する
- 既知の暗号脆弱性から保護する
再利用可能なテストハーネスを作成するための投資は、Wycheproof リポジトリに新しいテストベクトルが追加されるたびに継続的な検証を行えるため、大きな成果につながります。