英文:
how do I properly sign a bitclout tx in golang vs typescript?
问题
我有一个在typescript中的工作示例:
signTransaction(seedHex: string, transactionHex: string): string {
const privateKey = this.cryptoService.seedHexToPrivateKey(seedHex);
const transactionBytes = new Buffer(transactionHex, 'hex');
const transactionHash = new Buffer(sha256.x2(transactionBytes), 'hex');
const signature = privateKey.sign(transactionHash);
const signatureBytes = new Buffer(signature.toDER());
const signatureLength = uvarint64ToBuf(signatureBytes.length);
const signedTransactionBytes = Buffer.concat([
// This slice is bad. We need to remove the existing signature length field prior to appending the new one.
// Once we have frontend transaction construction we won't need to do this.
transactionBytes.slice(0, -1),
signatureLength,
signatureBytes,
]);
return signedTransactionBytes.toString('hex');
}
我将其转换为Go语言的代码:
func signTransaction(seedHex, transactionHex string) string {
privateKey := seedHexToPrivateKey(seedHex)
transactionBytes, _ := hex.DecodeString(transactionHex)
first := sha256.Sum256(transactionBytes)
transactionHash := fmt.Sprintf("%x", sha256.Sum256(first[:]))
signature, _ := privateKey.Sign([]byte(transactionHash))
signatureBytes := signature.Serialize()
signatureLength := make([]byte, 8)
binary.LittleEndian.PutUint64(signatureLength, uint64(len(signatureBytes)))
buff := []byte{}
buff = append(buff, transactionBytes[0:len(transactionBytes)-1]...)
buff = append(buff, signatureLength...)
buff = append(buff, signatureBytes...)
return fmt.Sprintf("%x", buff)
}
我认为它们非常接近。当我提交并尝试调整一些内容时,我收到了各种有趣的错误消息。但它的工作方式与typescript版本不同。有人看到任何错误吗?请注意,privateKey是*btcec.PrivateKey,它是ecdsa.PrivateKey,而签名序列化是来自https://github.com/btcsuite/btcd/blob/master/btcec/signature.go#L40
英文:
I have a working example in typescript:
signTransaction(seedHex: string, transactionHex: string): string {
const privateKey = this.cryptoService.seedHexToPrivateKey(seedHex);
const transactionBytes = new Buffer(transactionHex, 'hex');
const transactionHash = new Buffer(sha256.x2(transactionBytes), 'hex');
const signature = privateKey.sign(transactionHash);
const signatureBytes = new Buffer(signature.toDER());
const signatureLength = uvarint64ToBuf(signatureBytes.length);
const signedTransactionBytes = Buffer.concat([
// This slice is bad. We need to remove the existing signature length field prior to appending the new one.
// Once we have frontend transaction construction we won't need to do this.
transactionBytes.slice(0, -1),
signatureLength,
signatureBytes,
]);
return signedTransactionBytes.toString('hex');
}
and converting to golang I wrote:
func signTransaction(seedHex, transactionHex string) string {
privateKey := seedHexToPrivateKey(seedHex)
transactionBytes, _ := hex.DecodeString(hexString)
first := sha256.Sum256(transactionBytes)
transactionHash := fmt.Sprintf("%x", sha256.Sum256(first[:]))
signature, _ := privateKey.Sign([]byte(transactionHash))
signatureBytes := signature.Serialize()
signatureLength := make([]byte, 8)
binary.LittleEndian.PutUint64(signatureLength, uint64(len(signatureBytes)))
buff := []byte{}
buff = append(buff, transactionBytes[0:len(transactionBytes)-1]...)
buff = append(buff, signatureLength...)
buff = append(buff, signatureBytes...)
return fmt.Sprintf("%x", buff)
}
It's very close I think. I get back all sorts of interesting error messages when submitting and trying to adjust stuff. But it's not working like the typescript one is. Anyone see any errors? FYI privateKey is *btcec.PrivateKey which is ecdsa.PrivateKey and the signture Serialized is from https://github.com/btcsuite/btcd/blob/master/btcec/signature.go#L40
答案1
得分: 1
那个签名错误是因为签名是使用ASN.1编码的X9.62格式,用于编码值R和S。如果随机值R或签名S以1位开始,根据ASN.1 INTEGER的BER/DER编码,它将被解释为负值。
由于R是随机的,而S依赖于它,当整数未正确编码时,它们有时会出错。如果最左边和最重要的位是1,则应在其前面加上一个00
字节,以使其成为正的有符号大端值。
请注意,编码整数值的大小也是动态的,使用过多的字节是不正确的。
要从静态大小的大端R和S值转换,您应首先从左侧删除所有值为00
的字节,然后如果第一个字节的值为0x80
或更高(假设是无符号字节),则再添加一个字节。这个函数通常被称为O2ISP(八位字节串到整数原语),它来自RSA PKCS#1规范,在该规范中对其进行了数学描述。在Cryptography上有一个更深入的有用问题。
英文:
That signature error is because the signature is X9.62 which uses ASN.1 to encode both value R and S. If the random value R or the signature S starts with a 1 bit it will be interpreted as a negative value according to the BER/DER encoding of an ASN.1 INTEGER.
As R is randomized and S depends on it they will sometimes error when the integer is not encoded correctly. If the first / leftmost and most significant bit is 1 then a 00
byte should be prefixed to it, so it makes a positive signed big endian value.
Note that the size of the encoded integer value is therefore dynamic as well, it is incorrect to use too many bytes for it.
To go from a statically sized, big endian R and S value you should first remove all 00
valued bytes from the left hand side, and then add one back again if the first byte value is 0x80
or higher (assuming unsigned bytes, obviously). This function is commonly referred to as O2ISP (octet-string to integer primitive), taken from the RSA PKCS#1 specifications where it is mathematically described. Here on Cryptography is a helpful qustion that is a bit more in depth
答案2
得分: 0
找到解决方案:
https://github.com/indutny/elliptic/blob/master/lib/elliptic/ec/signature.js#L139
上述的toDER是正确的,而这个:
https://github.com/btcsuite/btcd/blob/master/btcec/signature.go#L40
则不正确,现在我有:
https://github.com/andrewarrow/clout-cli/blob/main/keys/hdkey.go#L48
我将其从JavaScript移植到Go,并且它正常工作!
英文:
Found solution:
https://github.com/indutny/elliptic/blob/master/lib/elliptic/ec/signature.js#L139
That ^ toDER is correct and this:
https://github.com/btcsuite/btcd/blob/master/btcec/signature.go#L40
is not so now I have:
https://github.com/andrewarrow/clout-cli/blob/main/keys/hdkey.go#L48
which I ported from JavaScript to go and it's working!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论