secp256k1/schnorr 图书馆不会互相交谈

secp256k1/schnorr libraries won't talk to each other

我目前正在浏览器中生成私钥并使用 noble-secp256k1 javascript 库派生其 public 密钥:

const privKey = secp.utils.randomPrivateKey()
const pubKey = Buffer.from(secp.schnorr.getPublicKey(privKey)).toString('hex')

然后我将 public 密钥发送到我的服务器,它也使用 secp256k1 库来验证我传递的有效载荷签名。当我尝试实例化 public 键时失败:

pub_key = secp256k1.PublicKey(binascii.unhexlify(hex_pub_key), raw=True)

如果我使用 python 库 (python -m secp256k1 privkey -p) 构建密钥对,这会起作用,但如果我发送在客户端生成的密钥,服务器会引发错误:

Exception: unknown public key size (expected 33 or 65)

python 库生成 66 个字符的十六进制编码 public 密钥。客户端使用 secp.schnorr.getPublicKey 方法生成一个 64 个字符的十六进制编码 public 密钥,并使用 secp.getPublicKey 方法生成一个 130 个字符的十六进制编码 public 密钥。有没有办法让我的 python 库接受在前端生成的 schnorr 公钥?有什么地方可以让我了解 secp256k1 和 schnorr 之间的这种半重叠是怎么回事?

NodeJS库(noble-secp256k1) has to be extended to 33 bytes with a leading 0x02 and can then be imported by the Python library (secp256k1)的32字节public键:

...we pick that option for P, and thus our X-only public keys become equivalent to a compressed public key that is the X-only key prefixed by the byte 0x02...

来自 BIP0340 的文档,秒。 设计隐式 Y 坐标


测试:

以下 NodeJS 代码使用 NodeJS 库并生成密钥对和 Schnorr 签名:

var secp = require("@noble/secp256k1");

(async () => {
    const privateKey = secp.utils.randomPrivateKey();
    const publicKey = secp.schnorr.getPublicKey(privateKey) 
    const msgHash = await secp.utils.sha256("hello world");
    const signature = await secp.schnorr.sign(msgHash, privateKey);
    const isValid = await secp.schnorr.verify(signature, msgHash, publicKey);  
    console.log("Public key (hex):", Buffer.from(publicKey).toString('hex'))
    console.log("Signature (hex) :", Buffer.from(signature).toString('hex'))
    console.log("Verified        :", isValid);
})();

具有以下可能的输出:

Public key (hex): f9a10a9bbb93e14a35d82c514f4eb052734ba55b93f6553f12366d6e887b76ee
Signature (hex) : b08a0e9d02da2bbb3e2220b90e591c82ebcfc337aaac36ebe2f91eec288c79b3b9513b1018126526f99697abb78c60041f0683bbce6760b8ff76cb53a4c87137
Verified        : true

以下Python代码使用Python库并使用NodeJS代码导出的密钥验证Schnorr签名,该代码由前导0x02扩展:

import secp256k1
import hashlib

publicKeyHexFromNodeJS = 'f9a10a9bbb93e14a35d82c514f4eb052734ba55b93f6553f12366d6e887b76ee'
signatureHexFromNodeJS = 'b08a0e9d02da2bbb3e2220b90e591c82ebcfc337aaac36ebe2f91eec288c79b3b9513b1018126526f99697abb78c60041f0683bbce6760b8ff76cb53a4c87137'

digest = hashlib.sha256()
digest.update(b'hello world')
msgHash = digest.digest()

publicKey = secp256k1.PublicKey(bytes.fromhex('02' + publicKeyHexFromNodeJS), raw=True)
signature = bytes.fromhex(signatureHexFromNodeJS)
isValid = publicKey.schnorr_verify(msgHash, signature, '', raw=True)
print("Verified: " + str(isValid))

输出:

Verified: True

即验证成功。


注意:即使前面加上 0x03(而不是 0x02),验证也会成功。可能库只检查密钥是否被压缩(0x02 或 0x03),如果是,前导字节被简单地截断。