python ecdsa NIST-256 p是否提供恢复代码(字节)?

8fq7wneg  于 2023-01-27  发布在  Python
关注(0)|答案(1)|浏览(195)

我正在使用python ecdsa库为区块链交易签署消息,在区块链规范中,对于secp256r1,签名长度应为65字节,其中:
签名的长度必须为65字节,格式为[r,s,v],其中前32个字节为r,后32个字节为s,最后一个字节为v。
以及
v表示恢复ID,必须将其标准化为0、1、2或3。请注意,与EIP-155不同,链ID不用于计算v值
我总是使用以下方法获取前64个字节:sign_deterministic(data, hashfunc=hashlib.sha256)
但是不确定ecdsa保存在哪里,或者我可以计算出v字节?
我在区块链上使用的源代码(Rust fastcrypto)中看到它正在做的事情:

// Compute recovery id and normalize signature
        let is_r_odd = y.is_odd();
        let is_s_high = sig.s().is_high();
        let is_y_odd = is_r_odd ^ is_s_high;
        let sig_low = sig.normalize_s().unwrap_or(sig);
        let recovery_id = RecoveryId::new(is_y_odd.into(), false);

但是在ecdsa中,所有这些都隐藏在上面提到的签名函数后面。

fwzugrvs

fwzugrvs1#

例如,这里解释了使用ECDSA签名。在下面,本文中使用的术语适用:

k:                  secret number when generating the signature
R=k*G =(R.x, R.y):  associated point, G: generator
(r=R.x,s):          signature
  • ecdsa* 库不支持在创建签名时额外确定恢复ID。但是,可以随后确定恢复ID。为此,必须首先确定创建签名时使用的k值,这是可能的,因为您正在根据RFC 6979使用 deterministic ECDSA。此处k取决于散列的定义。消息和私有签名密钥。

运行sign_deterministic(data, hashfunc=hashlib.sha256)时,将执行以下逻辑来确定k

from ecdsa import SigningKey, NIST256p, rfc6979, ecdsa
from hashlib import sha256
...
def get_k(signing_key, message, hash):  
    def simple_r_s(r, s, order):
        return r, s, order    
    retry_gen = 0
    while True:
        digest = hash(message).digest()
        k = rfc6979.generate_k(
            NIST256p.generator.order(),
            signing_key.privkey.secret_multiplier,
            hash,
            digest,
            retry_gen=retry_gen,
            extra_entropy=b"")
        try:
            r, s, order = signing_key.sign_digest(
                digest,
                sigencode=simple_r_s,
                k=k,
                allow_truncate=True)
            break
        except ecdsa.RSZeroError:
                retry_gen += 1
    return k

对应的代码位置可以在这里找到。注意sign_deterministic(data, hashfunc=hashlib.sha256)调用使用额外熵的默认值(extra_entropy=b""),并允许截断太大的哈希值(allow_truncate=True)。
对于kR可以被确定为k和生成点G的乘积,形式上,k可以被认为是原始私钥,而R可以被认为是原始公钥,因此可以使用密钥的现有功能,并且R可以被容易地确定如下:

def get_r(signing_key, message, hash):
    k = get_k(signing_key, message, hash)
    r_sk = SigningKey.from_secret_exponent(k, curve=NIST256p)
    r_vk = r_sk.get_verifying_key()
    return (r_vk.pubkey.point.x(), r_vk.pubkey.point.y())

对于已知的R,恢复ID可以如下导出:

def get_recovery_id(r):
    r_x, r_y = r
    if r_x > NIST256p.generator.order():
        if r_y % 2 == 0: recId = 2
        else: recId = 3
    else:
        if r_y % 2 == 0: recId = 0
        else: recId = 1
    return recId

有关最后一个逻辑的解释,请参见here(包括注解)。注意,恢复ID为2和3的概率非常小。
然后,可以使用 ecdsa 库生成包括恢复ID的签名,例如如下:

signing_key = SigningKey.generate(curve=NIST256p)
message = b'This is a text message'
signature = signing_key.sign_deterministic(message, hashfunc=sha256)
R = get_r(signing_key, message, sha256)
rec_id = get_recovery_id(R)
print("Rec-Id:", str(rec_id))
print("r:", signature[:32].hex())
print("s:", signature[32:].hex())

为了验证结果,也为了证明存在用于生成具有恢复ID的签名的更方便的库,使用以下pycoin代码,该代码仅需要库函数sign_with_recid(),该函数返回具有rs和恢复ID的元组。此方法内部使用来自RFC6979的k派生(即,也应用于 ecdsa 示例中的逻辑):

from pycoin.ecdsa.secp256r1 import secp256r1_generator
from hashlib import sha256
...
signing_key = SigningKey.generate(curve=NIST256p)
message = b'This is a text message'
private_key = signing_key.privkey.secret_multiplier 
digest = sha256(message).digest()
(r, s, rec_id) = secp256r1_generator.sign_with_recid(private_key, int.from_bytes(digest, 'big'))
print("Rec-Id:", str(rec_id))
print("r:", r.to_bytes(32, 'big').hex())
print("s:", s.to_bytes(32, 'big').hex())

当使用相同的签名密钥、相同的消息和相同的摘要时,这些值是相同的。
当前的问题是关于确定性ECDSA的,但即使对于非确定性ECDSA,ecdsa库也允许确定恢复ID(并且比确定性ECDSA的情况下容易得多)。由于不能访问签名中使用的随机kk的生成被简单地移到外部,然后使用该k生成签名(支持该k,参见sign()):

import os
import ecdsa
import hashlib

from ecdsa import SigningKey, NIST256p

message = b'This is a text message'
sk = SigningKey.generate(curve=NIST256p)
k = ecdsa.util.randrange(NIST256p.generator.order(), os.urandom) # shift the internal k generation to the outside
signature = sk.sign(message, k=k, hashfunc=hashlib.sha256)

对于已知的kR和最终的恢复ID,可以如上所述确定。

相关问题