Wycheproof加密测试Skill wycheproof

Wycheproof 是一个用于验证加密实现正确性的测试工具,通过提供测试向量来检测已知攻击和边缘情况。适用于加密代码测试、安全审计和CI/CD集成。关键词:加密测试、安全漏洞、测试向量、密码学、Wycheproof。

密码学 0 次安装 0 次浏览 更新于 3/14/2026

名称: wycheproof 类型: 领域 描述: > Wycheproof 提供测试向量用于验证加密实现。 在测试加密代码以应对已知攻击和边缘情况时使用。

Wycheproof

Wycheproof 是一个广泛的测试向量集合,旨在验证加密实现的正确性并测试已知攻击。最初由 Google 开发,现在是一个社区管理的项目,贡献者可以添加特定加密构造的测试向量。

背景

关键概念

概念 描述
测试向量 用于验证加密实现正确性的输入/输出对
测试组 共享属性(密钥大小、IV 大小、曲线)的测试向量集合
结果标志 指示测试应通过(有效)、失败(无效)或可接受
边缘情况测试 测试已知漏洞和攻击模式

为什么这很重要

加密实现 notoriously difficult to get right。即使小错误也可以:

  • 暴露私钥
  • 允许签名伪造
  • 启用消息解密
  • 当不同实现接受/拒绝相同输入时创建共识问题

Wycheproof 在主要库中发现了漏洞,包括 OpenJDK 的 SHA1withDSA、Bouncy Castle 的 ECDHC 和 elliptic npm 包。

何时使用

应用 Wycheproof 当:

  • 测试加密实现(AES-GCM、ECDSA、ECDH、RSA 等)
  • 验证加密代码正确处理边缘情况
  • 针对已知攻击向量验证实现
  • 为加密库设置 CI/CD
  • 审计第三方加密代码的正确性

考虑替代方案当:

  • 测试时序侧信道(使用 constant-time 测试工具代替)
  • 发现新的未知错误(使用模糊测试代替)
  • 测试自定义/实验性加密算法(Wycheproof 仅覆盖已建立的算法)

快速参考

场景 推荐方法 注释
AES-GCM 实现 使用 aes_gcm_test.json 316 个测试向量跨越 44 个测试组
ECDSA 验证 使用 ecdsa_*_test.json 用于特定曲线 测试签名可塑性、DER 编码
ECDH 密钥交换 使用 ecdh_*_test.json 测试无效曲线攻击
RSA 签名 使用 rsa_*_test.json 测试填充预言攻击
ChaCha20-Poly1305 使用 chacha20_poly1305_test.json 测试 AEAD 实现

测试工作流

阶段 1: 设置                阶段 2: 解析测试向量
┌─────────────────┐          ┌─────────────────┐
│ 添加 Wycheproof  │    →     │ 加载 JSON 文件  │
│ 作为子模块      │          │ 按参数过滤     │
└─────────────────┘          └─────────────────┘
         ↓                            ↓
阶段 4: CI 集成       阶段 3: 编写测试套件
┌─────────────────┐          ┌─────────────────┐
│ 自动更新       │    ←     │ 测试有效和     │
│ 测试向量       │          │ 无效案例       │
└─────────────────┘          └─────────────────┘

仓库结构

Wycheproof 仓库组织如下:

┣ 📜 README.md       : 项目概述
┣ 📂 doc             : 文档
┣ 📂 java            : Java JCE 接口测试套件
┣ 📂 javascript      : JavaScript 测试套件
┣ 📂 schemas         : 测试向量模式
┣ 📂 testvectors     : 测试向量
┗ 📂 testvectors_v1  : 更新后的测试向量(更详细)

基本文件夹是 testvectorstestvectors_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"         : 测试的算法名称
"schema"            : JSON 模式(在 schemas 文件夹中找到)
"generatorVersion"  : 版本号
"numberOfTests"     : 此文件中的测试向量总数
"header"            : 测试向量的详细描述
"notes"             : 测试向量中标志的深入解释
"testGroups"        : 一个或多个测试组的数组

测试组

测试组基于共享属性分组测试集,如:

  • 密钥大小
  • IV 大小
  • 公钥
  • 曲线

此分类允许提取符合正在测试构造特定标准的测试。

测试向量属性

共享属性

所有测试向量包含四个公共字段:

  • tcId: 测试向量在文件内的唯一标识符
  • comment: 测试案例的附加信息
  • flags: 特定测试案例类型和潜在危险的描述(在 notes 字段中引用)
  • result: 测试的预期结果

result 字段可以取三个值:

结果 含义
有效 测试案例应成功
可接受 测试案例允许成功但包含非理想属性
无效 测试案例应失败

唯一属性

唯一属性特定于正在测试的算法:

算法 唯一属性
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/"

# 创建 wycheproof 文件夹
mkdir -p $TMP_WYCHEPROOF_FOLDER

# 如果不存在,请求所有测试向量文件
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

    # 需要十六进制到字节转换的属性
    convert_attr = {"key", "aad", "iv", "msg", "ct", "tag"}

    for testGroup in wycheproof_json["testGroups"]:
        # 基于实现约束过滤测试组
        if testGroup["ivSize"] < 64 or testGroup["ivSize"] > 1024:
            continue

        for tv in testGroup["tests"]:
            # 将十六进制字符串转换为字节
            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 => {
        // 将共享测试组属性添加到每个测试
        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:
        # 实现引发错误 - 验证测试预期失败
        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) {
        // 点无法解码
        sig = false;
      }
      assert.equal(sig, false, `[${test.tcId}] ${test.comment}`);
    }
  });
}

// 为所有测试向量生成测试
for (var tcId = 0; tcId < tests.length; tcId++) {
  testFactory(tcId, tests);
}

阶段 4: CI 集成

确保测试向量保持最新通过:

  1. 使用 git 子模块:在运行测试前更新 CI 中的子模块
  2. 获取最新向量:在测试执行前运行获取脚本
  3. 计划更新:设置每周/每月更新以捕获新测试向量

常见检测到的漏洞

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): 向签名添加或删除零:

有效签名:   ...6a5c51eb6f946b30d
无效签名: ...6a5c51eb6f946b30d0000  (应拒绝)

如何检测:

# 添加签名长度检查
if len(sig) != 128:  # EdDSA 签名必须正好是 64 字节(128 十六进制字符)
    return False

影响: 当不同实现接受/拒绝相同签名时,可能导致共识问题。

相关 Wycheproof 测试:

  • EdDSA: tcId 37 - “从签名中移除 0 字节”
  • ECDSA: tcId 06 - “传统:r 的 ASN 编码缺少前导 0”

案例研究:Elliptic npm 包

此案例研究展示了 Wycheproof 如何在流行的 elliptic npm 包(3000+ 依赖,数百万周下载)中发现三个 CVE。

概述

elliptic 库是一个用 JavaScript 编写的椭圆曲线加密库,支持 ECDH、ECDSA 和 EdDSA。在版本 6.5.6 上使用 Wycheproof 测试向量揭示了多个漏洞:

  • CVE-2024-42459: EdDSA 签名可塑性(添加/删除零)
  • CVE-2024-42460: ECDSA DER 编码 - 无效位放置
  • CVE-2024-42461: ECDSA DER 编码 - 长度字段中的前导零

方法论

  1. 识别支持的曲线:ed25519 用于 EdDSA
  2. 找到测试向量testvectors_v1/ed25519_test.json
  3. 解析测试向量:加载 JSON 并提取测试
  4. 编写测试套件:创建参数化测试
  5. 运行测试:识别失败
  6. 分析根本原因:检查实现代码
  7. 提出修复:添加验证检查

关键发现

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;

影响

所有三个漏洞都允许单个消息的多个有效签名,导致跨实现的共识问题。

学到的教训:

  • 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       │
                    │   (此技能)          │
                    └──────────┬──────────┘
                               │
           ┌───────────────────┼───────────────────┐
           │                   │                   │
           ▼                   ▼                   ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│  pytest/mocha   │ │ constant-time   │ │   cryptofuzz    │
│ (测试框架)      │ │   测试         │ │   (模糊测试)     │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
         │                   │                   │
         └───────────────────┼───────────────────┘
                             │
                             ▼
              ┌──────────────────────────┐
              │   技术技能              │
              │ 覆盖、套件、PBT        │
              └──────────────────────────┘

资源

官方仓库

Wycheproof GitHub 仓库

官方仓库包含:

  • 所有测试向量在 testvectors/testvectors_v1/
  • JSON 模式在 schemas/
  • Java 和 JavaScript 中的参考实现
  • 文档在 doc/

现实世界示例

pycryptodome

pycryptodome 库在其测试套件中集成了 Wycheproof 测试向量,展示了 Python 加密实现的最佳实践。

社区资源

  • C2SP 社区 - 维护 Wycheproof 的加密规范和标准社区
  • Wycheproof 问题跟踪器 - 报告测试向量中的错误或建议新构造

总结

Wycheproof 是验证加密实现对抗已知攻击向量和边缘情况的重要工具。通过将 Wycheproof 测试向量集成到您的测试工作流中:

  1. 捕获微妙的编码和验证错误
  2. 防止签名可塑性问题
  3. 确保跨实现的一致行为
  4. 受益于社区贡献的测试向量
  5. 保护免受已知加密漏洞

投资编写可重用测试套件通过 Wycheproof 仓库添加新测试向量时的持续验证带来回报。