【问题标题】:Is python-ecdsa signature size correct?python-ecdsa 签名大小是否正确?
【发布时间】:2018-10-22 14:10:04
【问题描述】:

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

相关链接:

在第一个链接上,它说私钥应该是 32 个字节,公钥应该是 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) 的东西。

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

  • 我误解了 wiki 文章。
  • 我错误地使用了 python-ecdsa
  • 比特币维基不正确
  • python-ecdsa 未正确实现

前两个是最有可能的。

谁能向我解释为什么我的预期与实际得到的不匹配?

【问题讨论】:

    标签: python cryptography bitcoin ecdsa


    【解决方案1】:

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

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

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

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

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

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


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


    所以:

    • 我误解了 wiki 文章。

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

    • 我错误地使用了 python-ecdsa

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

    • 比特币维基不正确

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

    • python-ecdsa 未正确实现

    绝对不是;至少与签名的大小无关。


    现在了解实现细节;文档中有以下内容:

    还有多种表示签名的方式。默认的 sk.sign()vk.verify() 方法将其显示为一个短字符串,以简化和最小化开销。要使用不同的方案,请使用 sk.sign(sigencode=) 和 vk.verify(sigdecode=) 参数。 “ecdsa.util”模块中有一些帮助函数在这里很有用。

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

    【讨论】:

    • 该 wiki 已过时且不完整。对于交易签名,secp256k1 的 ASN.1 DER 编码通常为 72、71 或 70 个八位字节,很少更少,比特币为“sighash”添加一个八位字节。但由于 2014 年的 BIP62 比特币仅使用 low-S 签名,这不包括 72 字节的情况,通常会留下 71 或 70,而且很少会少加 1。对于 message 签名比特币使用纯/P1363/PKSC11/CVC 格式,正好是 64 个八位字节加上一个八位字节用于密钥恢复。一般n是generator and subgroup阶,不一定是曲线阶,而是所有X9/Certicom/NIST素数曲线...
    • ... 被选为具有辅因子 1。#E(Fp) 在 p 的 Hasse 范围内,但由于 p 在 2^k 下被选择得非常接近,因此 可以 i> 超过 2^k,并且对于 secp224k1(但不是 secp256k1)。私钥是 n 的大小,但公钥坐标是 p 的大小。
    • 一如既往的感谢您的评论!不过,在这种情况下,我想知道答案是否更合适(?)
    【解决方案2】:

    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

    【讨论】:

    • 虽然这些信息可能对其他人有用,但它如何回答关于签名大小的问题根本
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-01
    • 2018-01-17
    • 1970-01-01
    • 1970-01-01
    • 2016-03-30
    • 1970-01-01
    相关资源
    最近更新 更多