教程区块链区块链技术ch022.10 动手实验:密码学工具箱构建

本页目录

本节将所有密码学原语整合为一个可运行的验证工具箱。通过亲手组装这些组件,你将建立从"知道实现"到"能用密码学解决实际问题"的跨越。

2.10.1 工具箱架构

graph TD
    Tool[Crypto Toolset]
    Tool --> H[哈希层<br/>SHA-256 / Keccak-256]
    Tool --> ECC[ECC 层<br/>secp256k1 点运算]
    Tool --> K[密钥层<br/>BIP-39 助记词 + BIP-32 HD]
    Tool --> S[签名层<br/>ECDSA / Schnorr]
    Tool --> C[承诺层<br/>密码学承诺]
    H --> ECC
    ECC --> K
    K --> S
    S --> C
    style Tool fill:#bbdefb
    style S fill:#c8e6c9
text
┌─────────────────────────────────────────────┐
│          密码学工具箱 (Crypto Toolset)         │
├─────────────────────────────────────────────┤
│  [哈希层]                                    │
│    - SHA-256 从零实现                        │
│    - 雪崩效应测试器                          │
│    - 碰撞概率计算器                          │
├─────────────────────────────────────────────┤
│  [椭圆曲线层]                                │
│    - secp256k1 点运算                       │
│    - 标量乘法 (私钥→公钥)                   │
│    - 公钥压缩/解压                           │
├─────────────────────────────────────────────┤
│  [签名层]                                    │
│    - ECDSA 签名/验证                         │
│    - RFC 6979 确定性 k                      │
│    - 签名 malleability 检测                  │
├─────────────────────────────────────────────┤
│  [钱包层]                                    │
│    - 熵生成 → 助记词 → 种子 → 子密钥        │
│    - BIP-44 路径解析                         │
├─────────────────────────────────────────────┤
│  [Merkle 层]                               │
│    - 构建 Merkle 树                         │
│    - 生成/验证 Merkle 证明                   │
│    - 批量验证优化                           │
└─────────────────────────────────────────────┘

2.10.2 完整整合代码

typescript
// =====================================================
// 密码学工具箱整合实现
// 所有底层实现复用 2.1-2.7 节代码,此处展示整合调用
// =====================================================

/**
 * 工具箱:密码学验证套件
 */
class CryptoToolkit {
  // --- 1. 哈希层 ---
  
  sha256(message: string): string {
    return sha256(message); // 复用 2.2 节完整实现
  }
  
  /**
   * 雪崩效应测试:单 bit 变化导致多少输出位变化
   */
  avalancheTest(input: string, rounds: number = 20): {
    avgDistance: number;
    avgRate: number;
    results: number[];
  } {
    const baseHash = this.sha256(input);
    const results: number[] = [];
    
    for (let i = 0; i < rounds; i++) {
      const modified = this._flipRandomBit(input);
      const modifiedHash = this.sha256(modified);
      results.push(this._hammingDistance(baseHash, modifiedHash));
    }
    
    const avgDist = results.reduce((a, b) => a + b, 0) / results.length;
    return {
      avgDistance: avgDist,
      avgRate: (avgDist / 256) * 100,
      results,
    };
  }
  
  // --- 2. 密钥对生成层 ---
  
  generateKeyPair(): { privateKey: bigint; publicKey: ECPoint } {
    const d = generatePrivateKey(); // CSPRNG 生成
    const pub = scalarMultiply(d, new ECPoint(G.x, G.y), 0n, SECP256K1.p);
    return { privateKey: d, publicKey: pub };
  }
  
  /**
   * 公钥压缩:65 字节 → 33 字节
   */
  compressPublicKey(pubKey: ECPoint): Uint8Array {
    const isEven = (pubKey.y! % 2n) === 0n;
    const prefix = isEven ? 0x02 : 0x03;
    const bytes = new Uint8Array(33);
    bytes[0] = prefix;
    // ... 将 x 写入后 32 字节
    return bytes;
  }
  
  // --- 3. 签名层 ---
  
  sign(privateKey: bigint, message: string): { r: bigint; s: bigint } {
    return ecdsaSign(privateKey, message); // 复用 2.5 节实现
  }
  
  verify(
    publicKey: ECPoint,
    message: string,
    signature: { r: bigint; s: bigint },
  ): boolean {
    return ecdsaVerify(publicKey, message, signature); // 复用 2.5 节实现
  }
  
  /**
   * 检测签名可锻性(malleability)
   * 如果 s > n/2,攻击者可用 n-s 构造等价但不同的签名
   */
  isSignatureMalleable(signature: { r: bigint; s: bigint }): boolean {
    return signature.s > SECP256K1.n / 2n;
  }
  
  normalizeSignature(signature: { r: bigint; s: bigint }): { r: bigint; s: bigint } {
    if (this.isSignatureMalleable(signature)) {
      return { r: signature.r, s: SECP256K1.n - signature.s };
    }
    return signature;
  }
  
  // --- 4. Merkle 层 ---
  
  buildMerkleTree(txHashes: Uint8Array[]): MerkleTree {
    return new MerkleTree(txHashes); // 复用 2.7 节实现
  }
  
  // --- 私有辅助 ---
  
  private _flipRandomBit(s: string): string {
    const chars = s.split('');
    const pos = Math.floor(Math.random() * s.length);
    const bit = Math.floor(Math.random() * 8);
    const code = s.charCodeAt(pos);
    chars[pos] = String.fromCharCode(code ^ (1 << bit));
    return chars.join('');
  }
  
  private _hammingDistance(a: string, b: string): number {
    let dist = 0;
    for (let i = 0; i < a.length && i < b.length; i++) {
      const x = parseInt(a[i], 16);
      const y = parseInt(b[i], 16);
      let diff = x ^ y;
      while (diff) { dist++; diff &= diff - 1; }
    }
    return dist;
  }
}

// =====================================================
// 验证测试套件
// =====================================================

function runTests() {
  const kit = new CryptoToolkit();
  console.log("╔════════════════════════════════════════╗");
  console.log("║    密码学工具箱验证套件                ║");
  console.log("╚════════════════════════════════════════╝\n");
  
  // Test 1: 哈希雪崩效应
  console.log("[Test 1] 雪崩效应测试");
  const avalanche = kit.avalancheTest("Blockchain Crypto Toolkit v1.0", 10);
  console.log(`  平均翻转: ${avalanche.avgRate.toFixed(1)}% (目标: 50%)`);
  console.log(`  结果: ${avalanche.avgRate > 45 && avalanche.avgRate < 55 ? '✅ 通过' : '❌ 异常'}`);
  
  // Test 2: 密钥对生成
  console.log("\n[Test 2] 密钥对生成与验证");
  const { privateKey, publicKey } = kit.generateKeyPair();
  console.log(`  私钥位数: ${privateKey.toString(2).length} (目标: 256)`);
  console.log(`  公钥有效性: ${isValidCurvePoint(publicKey, 0n, 7n, SECP256K1.p) ? '✅ 在曲线上' : '❌ 无效'}`);
  
  // Test 3: ECDSA 全流程
  console.log("\n[Test 3] ECDSA 签名验证循环");
  const msg = "Transfer 1.0 BTC to 1A1z...";
  const sig = kit.sign(privateKey, msg);
  const valid = kit.verify(publicKey, msg, sig);
  console.log(`  原始消息: ${valid ? '✅ 验证通过' : '❌ 失败'}`);
  
  const tampered = kit.verify(publicKey, msg + "_tampered", sig);
  console.log(`  篡改消息: ${tampered ? '❌ 未检测出篡改' : '✅ 正确拒绝'}`);
  
  // Test 4: 签名规范化
  console.log("\n[Test 4] 签名可锻性检测");
  const highS = { r: sig.r, s: SECP256K1.n - sig.s };
  console.log(`  高 s 值: ${kit.isSignatureMalleable(highS) ? '✅ 检测到可锻性' : '❌ 未检测'}`);
  const normalized = kit.normalizeSignature(highS);
  console.log(`  规范化后: ${kit.isSignatureMalleable(normalized) ? '❌ 仍有问题' : '✅ 安全'}`);
  console.log(`  等价验证: ${kit.verify(publicKey, msg, normalized) ? '✅ 仍有效' : '❌ 被破坏'}`);
  
  // Test 5: Merkle 证明
  console.log("\n[Test 5] Merkle 树构建与验证");
  const txData = Array.from({ length: 8 }, (_, i) => `tx_${i}_data`);
  const txHashes = txData.map(tx => {
    const hex = kit.sha256(tx);
    const bytes = new Uint8Array(32);
    for (let j = 0; j < 32; j++) bytes[j] = parseInt(hex.slice(j * 2, j * 2 + 2), 16);
    return bytes;
  });
  
  const tree = kit.buildMerkleTree(txHashes);
  const root = tree.getRoot();
  if (root) {
    console.log(`  树根: ${Array.from(root).slice(0, 4).map(b => b.toString(16).padStart(2, '0')).join('')}...`);
    const proof = tree.getProof(3);
    const verified = verifyMerkleProof(txHashes[3], proof, root, 3);
    console.log(`  证明大小: proof.length个哈希×32字节={proof.length} 个哈希 × 32 字节 ={proof.length * 32} 字节`);
    console.log(`  验证结果: ${verified ? '✅ 通过' : '❌ 失败'}`);
  }
  
  console.log("\n╔════════════════════════════════════════╗");
  console.log("║    全部测试完成                       ║");
  console.log("╚════════════════════════════════════════╝");
}

// 执行测试
runTests();

2.10.3 扩展挑战

完成基础工具箱后,以下扩展项目可以加深理解:

挑战 1:实现 RFC 6979 确定性 kk

替代随机 kk,实现基于 HMAC-SHA256 的确定性签名:

k=HMAC-SHA256(key=d,data=e0x00)k = \text{HMAC\text{-}SHA256}(\text{key} = d, \text{data} = e \parallel 0x00)

验证:同一 (d,m)(d, m) 总是产生相同的 (r,s)(r, s),且 Sony 攻击不再可能。

挑战 2:Schnorr 签名实现

实现 BIP-340 的 Schnorr 签名:

R=kG,e=H(RPm),s=k+ed,签名=(R,s)R = kG, \quad e = H(R \parallel P \parallel m), \quad s = k + ed, \quad \text{签名} = (R, s)

验证等式:sG=?R+ePsG \stackrel{?}{=} R + eP

比较:签名大小降为 64 字节(RRss 各 32 字节,不需要编码 rrss),无 DER 编码复杂性。

挑战 3:批量验证优化

Schnorr 的核心优势——线性——允许批量验证多个签名:

icisiG=?iciRi+icieiPi\sum_i c_i s_i G \stackrel{?}{=} \sum_i c_i R_i + \sum_i c_i e_i P_i

其中 cic_i 是随机挑战系数。将 nn 次独立验证的 n×Gn \times G 运算合并为少量群运算。

挑战 4:Patricia Trie 简化实现

实现一个键值存储的 Patricia Trie(16 进制前缀压缩),并扩展为 Merkle Patricia Trie——在每次修改后重新计算到根的路径哈希。这是以太坊状态树的简化模型。

2.10.4 最佳安全实践清单

  • [ ] 所有随机数使用 crypto.getRandomValuescrypto.randomBytes
  • [ ] ECDSA 使用 RFC 6979 确定性 kk 或 CSPRNG + 防重放机制
  • [ ] 所有外部输入的公钥在使用前验证曲线方程
  • [ ] 签名后检查 sn/2s \leq n/2,拒绝高 s 可锻性签名
  • [ ] 助记词不在任何联网设备存储
  • [ ] HD 钱包使用 BIP-44 标准路径,记录 hardened 索引
  • [ ] 消息签名前包含协议标识/链 ID 防止跨链重放
  • [ ] 密钥存储使用 AES-256-GCM 或 ChaCha20-Poly1305

本章小结:从 SHA-256 的 64 轮压缩函数,到 secp256k1 的标量乘法,到 ECDSA 的灵魂 kk,到 BIP-39 的 12 个记忆词——每个密码学原语都是"信任机器"的基石。理解它们的数学、实现和安全边界,是理解区块链"为什么可信"的必经之路。

评论

0

评论加载中…

发表评论

0/2000