简体   繁体   English

如何验证 trezor 钱包的签名

[英]How to verify a signature made by trezor wallet

I want to verify a message signed by my trezor hardware wallet.我想验证由我的 trezor 硬件钱包签名的消息。 Basically I have these information.基本上我有这些信息。

.venv/bin/trezorctl btc get-public-node -n 0
Passphrase required: 
Confirm your passphrase: 
node.depth: 1
node.fingerprint: ea66f037
node.child_num: 0
node.chain_code: e02030f2a7dfb474d53a96cb26febbbe3bd3b9756f4e0a820146ff1fb4e0bd99
node.public_key: 026b4cc594c849a0d9a124725997604bc6a0ec8f100b621b1eaed4c6094619fc46
xpub: xpub69cRfCiJ5BVzesfFdsTgEb29SskY74wYfjTRw5kdctGN2xp1HF4udTP21t68PAQ4CBq1Rn3wAsWr84wiDiRmmSZLwkEkv4qK5T5Y7EXebyQ

$ .venv/bin/trezorctl btc sign-message 'aaa' -n 0
Please confirm action on your Trezor device
Passphrase required: 
Confirm your passphrase: 
message: aaa
address: 17DB2Q3oZVkQAffkpFvF4cwsXggu39iKdQ
signature: IHQ7FDJy6zjwMImIsFcHGdhVxAH7ozoEoelN2EfgKZZ0JVAbvnGN/w8zxiMivqkO8ijw8fXeCMDt0K2OW7q2GF0=

I wanted to use python3-ecdsa.我想使用 python3-ecdsa。 When I want to verify the signature with any valid public key, I get an AssertionError: (65, 64), because the base64.b64decode of the signature is 65 bytes, but should be 64. When I want to load the node.public_key into a ecdsa.VerifyingKey, I get an AssertionError: (32, 64), because the bytes.fromhex return 32 bytes, but every example I found uses 64 bytes for the public key.当我想用任何有效的公钥验证签名时,我得到一个 AssertionError: (65, 64),因为签名的 base64.b64decode 是 65 个字节,但应该是 64。当我想加载 node.public_key 时进入 ecdsa.VerifyingKey,我得到一个 AssertionError: (32, 64),因为 bytes.fromhex 返回 32 个字节,但我发现的每个示例都使用 64 个字节作为公钥。 Probably I need to convert the bip32 xpub to a public key, but I really dont know how.可能我需要将 bip32 xpub 转换为公钥,但我真的不知道如何。

Solutiion解决方案

python-ecdsa needs to be at version 0.14 or greater to handle compressed format of the public key. python-ecdsa 需要 0.14 或更高版本才能处理公钥的压缩格式。

import ecdsa
import base64
import hashlib

class DoubleSha256:

    def __init__(self, *args, **kwargs):
        self._m = hashlib.sha256(*args, **kwargs)

    def __getattr__(self, attr):
        if attr == 'digest':
            return self.double_digest
        return getattr(self._m, attr)

    def double_digest(self):
        m = hashlib.sha256()
        m.update(self._m.digest())
        return m.digest()


def pad_message(message):
    return "\x18Bitcoin Signed Message:\n".encode('UTF-8') + bytes([len(message)]) + message.encode('UTF-8')


public_key_hex = '026b4cc594c849a0d9a124725997604bc6a0ec8f100b621b1eaed4c6094619fc46'
public_key = bytes.fromhex(public_key_hex)
message = pad_message('aaa')
sig = base64.b64decode('IHQ7FDJy6zjwMImIsFcHGdhVxAH7ozoEoelN2EfgKZZ0JVAbvnGN/w8zxiMivqkO8ijw8fXeCMDt0K2OW7q2GF0=')

vk = ecdsa.VerifyingKey.from_string(public_key, curve=ecdsa.SECP256k1)
print(vk.verify(sig[1:], message, hashfunc=DoubleSha256))

Public-key.公钥。 Mathematically an elliptic curve public key is a point on the curve.从数学上讲,椭圆曲线公钥是曲线上的一个点。 For the elliptic curve used by Bitcoin, secp256k1, as well as other X9-style (Weierstrass form) curves, there are (in practice) two standard representations originally established by X9.62 and reused by many others:对于比特币使用的椭圆曲线 secp256k1 以及其他 X9 样式(Weierstrass 形式)曲线,(实际上)有两个标准表示最初由 X9.62 建立并被许多其他人重用:

  • uncompressed format: consists of one octet with value 0x04, followed by two blocks of size equal to the curve order size containing the (affine) X and Y coordinates.未压缩格式:由一个值为 0x04 的八位字节组成,后跟两个大小等于曲线顺序大小的块,包含(仿射)X 和 Y 坐标。 For secp256k1 this is 1+32x2 = 65 octets对于 secp256k1,这是 1+32x2 = 65 个八位字节

  • compressed format: consists of one octet with value 0x02 or 0x03 indicating the parity of the Y coordinate, followed by a block of size equal tot he curve order containing the X coordinate.压缩格式:由一个值为 0x02 或 0x03 的八位字节组成,指示 Y 坐标的奇偶校验,后跟一个大小等于包含 X 坐标的曲线顺序的块。 For secp256k1 this is 1+32 = 33 octets对于 secp256k1,这是 1+32 = 33 个八位字节

The public key output by your trezor is the second form, 0x02 + 32 octets = 33 octets.你的 trezor 输出的公钥是第二种形式,0x02 + 32 个八位字节 = 33 个八位字节。 Not 32.不是 32。

I've never seen an X9EC library (ECDSA and/or ECDH) that doesn't accept at least the standard uncompressed form, and usually both.我从来没有见过一个 X9EC 库(ECDSA 和/或 ECDH)至少不接受标准的未压缩形式,通常两者都接受。 It is conceivable your python library expects only the uncompressed form without the leading 0x04, but if so this gratuitous and rather risky nonstandardness, unless a very good explanation is provided in the doc or code, would make me suspicious of its quality.可以想象,您的 Python 库只需要没有前导 0x04 的未压缩形式,但如果是这样,这种无缘无故且相当危险的非标准性,除非在文档或代码中提供了很好的解释,否则会使我怀疑其质量。 If you do need to convert the compressed form to uncompressed you must implement the curve equation, which for secp256k1 can be found in standard references, not to mention many implementations.如果确实需要将压缩形式转换为未压缩形式,则必须实现曲线方程,对于 secp256k1 可以在标准参考中找到,更不用说许多实现了。 Compute x^3 + a*x + b , take the square root in F_p, and choose either the positive or negative value that has the correct parity (agreeing with the leading byte here 0x02).计算x^3 + a*x + b ,在 F_p 中取平方根,并选择具有正确奇偶校验的正值或负值(同意这里的前导字节 0x02)。

The 'xpub' is a base58check encoding of a hierarchical deterministic key, which is not just an EC(DSA) key but adds metadata for the key derivation process. “xpub”是分层确定性密钥的 base58check 编码,它不仅是 EC(DSA) 密钥,还为密钥派生过程添加了元数据。 If you base58 decode it and remove the check, you get (in hex):如果你对它进行 base58 解码并删除检查,你会得到(十六进制):

0488B21E01EA66F03700000000E02030F2A7DFB474D53A96CB26FEBBBE3BD3B9756F4E0A820146FF1FB4E0BD99026B4CC594C849A0D9A124725997604BC6A0EC8F100B621B1EAED4C6094619FC46good

which breaks down exactly as your display showed:完全按照您的显示显示:

0488B21E  fixed prefix 
01  .depth 
EA66F037  .fingerprint
00000000  .child_num
E02030F2A7DFB474D53A96CB26FEBBBE3BD3B9756F4E0A820146FF1FB4E0BD99  .chain_code
026B4CC594C849A0D9A124725997604BC6A0EC8F100B621B1EAED4C6094619FC46  .public_key

Confirming this, the ripemd160 of sha256 of (the bytes that are shown in hex as) 026B4CC594C849A0D9A124725997604BC6A0EC8F100B621B1EAED4C6094619FC46 is (the bytes shown in hex as) 441e1d2adf9ff2a6075d71d0d8782228e0df47f8, and prefixing the version byte 00 for mainnet to that and base58check encoding gives the address 17DB2Q3oZVkQAffkpFvF4cwsXggu39iKdQ as shown.确认这一点,(即示于十六进制的字节)026B4CC594C849A0D9A124725997604BC6A0EC8F100B621B1EAED4C6094619FC46是SHA256的RIPEMD160(在示于十六进制字节为)441e1d2adf9ff2a60​​75d71d0d8782228e0df47f8,和前缀的版本字节00 mainnet到和base58check编码给出如所示的地址17DB2Q3oZVkQAffkpFvF4cwsXggu39iKdQ。

Signature.签名。 Mathematically an X9.62-type ECDSA signature is two integers, called r and s.在数学上,X9.62 类型的 ECDSA 签名是两个整数,称为 r 和 s。 There are two different standards for representing them, and Bitcoin uses both with variations:有两种不同的标准来表示它们,比特币同时使用这两种标准:

  • ASN.1 DER format. ASN.1 DER 格式。 DER is a general purpose encoding that contains 'tag' and 'length' metadata and variable length data depending on the numeric values, here r and s; DER 是一种通用编码,包含“标签”和“长度”元数据以及取决于数值的可变长度数据,这里是 r 和 s; for secp256k1 in general this encoding is usually 70 to 72 octets but occasionally less.对于 secp256k1,通常这种编码通常是 70 到 72 个八位字节,但偶尔会更少。 However, to avoid certain 'malleability' attacks current Bitcoin requires use of 's' values less than half the curve order, commonly called 'low-s', which reduces the maximum length of the ASN.1 DER encoding to 71 octets.然而,为了避免某些“延展性”攻击,当前的比特币需要使用小于曲线顺序一半的“s”值,通常称为“low-s”,这将 ASN.1 DER 编码的最大长度减少到 71 个八位字节。 Bitcoin uses this for transaction signatures, and adds a 'sighash' byte immediately following it (in the 'scriptsig' aka redeem script) indicating certain options on how the signature was computed (and thus should be verified).比特币将其用于交易签名,并在其后面添加一个“sighash”字节(在“scriptsig”又名赎回脚本中),指示有关签名计算方式的某些选项(因此应该进行验证)。

  • 'plain' or P1363 format. “普通”或 P1363 格式。 This is fixed length and consists simply of the r and s values as fixed-length blocks;这是固定长度的,仅由作为固定长度块的 r 和 s 值组成; for secp256k1 this is 64 octets.对于 secp256k1,这是 64 个八位字节。 Bitcoin uses this for message signatures but it adds a 'recovery' byte' to the beginning that allows determining the publickey from the message and signature if necessary, making the total 65 octets.比特币将此用于消息签名,但它在开头添加了一个“恢复”字节,以便在必要时从消息和签名中确定公钥,总共 65 个八位字节。

See https://bitcoin.stackexchange.com/questions/38351/ecdsa-vrs-what-is-v/38909 and https://bitcoin.stackexchange.com/questions/12554/why-the-signature-is-always-65-13232-bytes-long .https://bitcoin.stackexchange.com/questions/38351/ecdsa-vrs-what-is-v/38909https://bitcoin.stackexchange.com/questions/12554/why-the-signature-is-always -65-13232 字节长

If your python library is designed for general purpose ECDSA, not Bitcoin, and wants a 64-byte signature, that almost certainly is the 'plain' format which corresponds to the Bitcoin message signature (here decoded from base64) with the first byte removed.如果您的 python 库是为通用 ECDSA 而不是比特币设计的,并且需要 64 字节的签名,那几乎可以肯定是“普通”格式,它对应于删除第一个字节的比特币消息签名(此处从 base64 解码)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM