python-ecdsa 签名大小是否正确?

Is python-ecdsa signature size correct?

在比特币维基上我发现比特币使用ECDSA算法,Secp256k1曲线。

相关链接:

第一个link说私钥应该是32字节,public密钥是64字节,签名一般在71-73字节之间。说签名可以小概率小

然而,当我运行以下python3代码

>>> from ecdsa import SigningKey, SECP256k1
>>> private_key = SigningKey.generate(curve=SECP256k1)
>>> public_key = private_key.get_verifying_key()
>>> signature = private_key.sign(b'message')
>>> print((len(private_key.to_string()), len(public_key.to_string()), len(signature)))

我得到 (32, 64, 64) 作为输出。我希望得到类似 (32, 64, 72) 的结果。

我认为正在发生以下情况之一:

前两个可能性较大。

任何人都可以向我解释为什么我的期望与实际得到的不匹配吗?

ECDSA 签名由两个数字组成,r 和 s 是 [1..n-1] 范围内的数字,其中 n 是曲线的阶数。 n 是 [2^(k-1)..2^k-1] 范围内的(已知)数字,其中 k 是密钥大小。所以 r 和 s 的大小通常与密钥大小(以字节为单位)相同,有时略小。

现在r和s可以有多种编码方式,其中常用的有两种:

  1. r 和 s 在 ASN.1 SEQUENCE 中被 DER 编码为两个 ASN.1 签名的 INTEGER 类型。
  2. r 和 s 被编码为两个静态大小的无符号整数,其大小与密钥大小(或顺序)相同八位字节或字节

所以大小的差异只是因为值 r 和 s 的编码不同。当然,你需要先知道编码类型,然后才能验证签名。

由于 r 和 s 完全相同,独立于编码,因此在两个版本之间进行转换相对简单(如果您可以调用任何需要生成或解析 DER 编码的 ASN.1 结构的内容 "simple").

类型 1 已在 ANSI X9.62 中标准化,类型 2 通常称为平面编码,常用于嵌入式平台或智能卡。


r 和 s 非常 可能 与 n / 密钥大小相同,但原则上,它们可以是例如数字 3。发生这种情况的可能性非常小。但是,您应该 对 r 和 s 的大小执行任何测试。如果它们中的任何一个小于 8 个字节,那么您可能会开始挠头,因为发生这种情况的可能性在 1/2^63 和 1/2^64 之间,即 非常 不太可能。


所以:

  • 我误解了维基文章。

不,wiki 文章采用 ANSI X9.62 的标准化编码。

  • 我使用 python-ecdsa 不正确

不,python-ecdsa 包只是使用了不同的编码,你会感到惊讶。

  • 比特币 wiki 不正确

不,比特币 wiki 假定为其协议选择了特定的编码。

  • python-ecdsa没有正确实现

绝对不是;至少在签名的大小方面不是。


现在介绍实施细节;以下内容在文档中:

There are also multiple ways to represent a signature. The default sk.sign() and vk.verify() methods present it as a short string, for simplicity and minimal overhead. To use a different scheme, use the sk.sign(sigencode=) and vk.verify(sigdecode=) arguments. There are helper functions in the "ecdsa.util" module that can be useful here.

所以尝试使用sigencode=sigencode_der来获得wiki文章所期望的格式。 util.py 来源包含您可能需要的所有转换。它使用 number_to_string 来创建静态大小的数字。此函数在 PKCS#1 (RSA) 中也称为 I2OSP 或整数到八位字节字符串原语。请注意,代码中的 "strings" 指的是 八位字节字符串 ,也称为 字节数组 - 而不是文本字符串。

python-ecdsa 的唯一问题是性能,因为它太慢了。

更好的库:starkbank-ecdsa

如何安装:

pip install starkbank-ecdsa

使用方法:

# Generate Keys
privateKey = PrivateKey()
publicKey = privateKey.publicKey()

message = "My test message"

# Generate Signature
signature = Ecdsa.sign(message, privateKey)

# Verify if signature is valid
print Ecdsa.verify(message, signature, publicKey)

完整参考:https://github.com/starkbank/ecdsa-python