ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

从‘我有个秘密’到‘我签个名’:用Python代码一步步还原Schnorr协议的核心流程

2026/8/13 23:10:11 拓冰建站 浏览量
从‘我有个秘密’到‘我签个名’:用Python代码一步步还原Schnorr协议的核心流程

从零构建Schnorr签名:Python实战与密码学本质解析

密码学工程师的日常工作中,最令人着迷的莫过于将数学公式转化为可运行的代码。当我第一次在比特币改进提案(BIP-340)中看到Schnorr签名时,那种想立刻动手实现的冲动至今记忆犹新。本文将带你用Python从零开始,完整实现Schnorr签名协议,并在代码层面揭示其作为零知识证明的精妙之处。

1. 环境准备与密码学基础

1.1 椭圆曲线密码学核心概念

在开始编码前,我们需要明确几个关键概念:

  • 私钥(Private Key):一个随机选取的大整数(通常256位),记作x
  • 公钥(Public Key):椭圆曲线上的点,由X = x*G计算得出,其中G是曲线上的基准点
  • 离散对数难题:已知XG,计算x在计算上不可行
from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives import hashes # 选择secp256k1曲线(比特币使用的曲线) curve = ec.SECP256K1()

1.2 Schnorr协议的三重身份

Schnorr协议在不同场景下呈现三种形态:

  1. 身份证明协议:证明你知道某个私钥而不泄露它
  2. 签名方案:将消息与身份绑定形成数字签名
  3. 零知识证明:验证方无法获取关于私钥的任何信息

2. 交互式Schnorr协议实现

2.1 协议流程代码化

让我们用Python模拟Alice和Bob的交互过程:

import secrets from cryptography.hazmat.primitives import serialization def generate_key_pair(): private_key = ec.generate_private_key(curve) public_key = private_key.public_key() return private_key, public_key # Alice的准备工作 alice_sk, alice_pk = generate_key_pair() r = secrets.randbelow(curve.key_size) R = r * curve.generator # 伪代码,实际需要椭圆曲线点乘法

2.2 关键步骤的密码学意义

步骤密码学意义Python实现要点
R = r*G承诺阶段:隐藏私钥的临时公钥使用密码学安全随机数
c = random挑战阶段:防止预计算攻击secrets模块生成
s = r + c*x响应阶段:知识证明的核心有限域运算
# Bob生成随机挑战 c = secrets.randbelow(curve.key_size) # Alice计算响应 x = alice_sk.private_numbers().private_value s = (r + c * x) % curve.order # Bob验证 sG = s * curve.generator # s*G right_side = R + c * alice_pk.public_numbers().public_key # R + c*X assert sG == right_side

注意:实际代码中椭圆曲线运算需要使用密码学库的专用方法,这里用伪代码表示数学运算

3. 非交互式Schnorr签名

3.1 Fiat-Shamir启发式转换

将交互式协议转换为非交互式的关键是用哈希函数模拟验证者的挑战:

def schnorr_sign(private_key, message): # 获取私钥数值 x = private_key.private_numbers().private_value # 生成随机数r及其承诺R r = secrets.randbelow(curve.order) R = r * curve.generator # 计算挑战c = H(R||message) hasher = hashes.Hash(hashes.SHA256()) hasher.update(R.to_bytes() + message) c = int.from_bytes(hasher.finalize(), 'big') % curve.order # 计算响应s = r + c*x s = (r + c * x) % curve.order return (c, s) # 优化后的签名格式

3.2 签名验证实现

验证过程只需要公钥、消息和签名:

def schnorr_verify(public_key, message, signature): c, s = signature X = public_key.public_numbers().public_key # 恢复R' = s*G - c*X sG = s * curve.generator cX = c * X R_prime = sG - cX # 计算c' = H(R'||message) hasher = hashes.Hash(hashes.SHA256()) hasher.update(R_prime.to_bytes() + message) c_prime = int.from_bytes(hasher.finalize(), 'big') % curve.order return c == c_prime

4. 安全性与优化实践

4.1 关键安全考量

  1. 随机数生成r必须密码学安全随机
  2. 哈希函数选择:SHA-256等抗碰撞哈希
  3. 参数大小c可缩减为128位以优化空间
# 安全随机数生成对比 unsafe_r = random.getrandbits(256) # 不安全! safe_r = secrets.randbelow(curve.order) # 安全

4.2 性能优化技巧

优化点传统实现优化实现收益
签名格式(R,s)(c,s)减少25%空间
批验证逐个验证合并验证方程提升30%速度
哈希计算多次哈希哈希一次减少IO开销

5. 真实场景应用案例

5.1 比特币中的Schnorr签名

比特币的Taproot升级采用了Schnorr签名,关键改进包括:

  • 签名聚合:多个签名合并为一个
  • 隐私增强:隐藏交易复杂逻辑
  • 效率提升:减少区块链空间占用
# 模拟签名聚合 def aggregate_signatures(signatures): aggregated_s = 0 for sig in signatures: c, s = sig aggregated_s = (aggregated_s + s) % curve.order return (signatures[0][0], aggregated_s)

5.2 多重签名方案

Schnorr天然支持高效的多重签名,这是传统ECDSA难以实现的:

  1. 各签名者使用相同随机点R
  2. 分别生成部分签名s_i
  3. 聚合所有s_i得到最终签名
def multisig_sign(participants, message): # 参与者协商共同的R R = sum(p['r'] for p in participants) * curve.generator # 各自计算c = H(R||message) hasher = hashes.Hash(hashes.SHA256()) hasher.update(R.to_bytes() + message) c = int.from_bytes(hasher.finalize(), 'big') % curve.order # 生成部分签名 signatures = [] for p in participants: s_i = (p['r'] + c * p['x_i']) % curve.order signatures.append(s_i) # 聚合签名 s = sum(signatures) % curve.order return (c, s)

在实现这些代码时,我经常遇到椭圆曲线库的接口限制问题。比如Python的cryptography库没有直接暴露点乘运算,这时可以考虑使用fastecdsa等专门库,或者在测试环境中使用简化实现。关键是要保证核心逻辑的正确性,特别是在有限域运算和随机数生成这些容易出错的地方。