英文:
ed25519.Public result is different
问题
使用https://github.com/golang/crypto/tree/master/ed25519包,我正在尝试为给定的私钥获取公钥。
这些数据来自http://www.bittorrent.org/beps/bep_0044.html:测试2(带盐的可变数据)
问题是,当我用给定的私钥输入ed25519.Public()时,它不会返回相同的公钥。
Golang实现返回PVK的最后32个字节。但在我的测试数据中,这是意外的。
以下是代码示例:https://play.golang.org/p/UJNPCyuGQB
package main
import (
"encoding/hex"
"golang.org/x/crypto/ed25519"
"log"
)
func main() {
priv := "e06d3183d14159228433ed599221b80bd0a5ce8352e4bdf0262f76786ef1c74db7e7a9fea2c0eb269d61e3b38e450a22e754941ac78479d6c54e1faf6037881d"
pub := "77ff84905a91936367c01360803104f92432fcd904a43511876df5cdf3e7e548"
sig := "6834284b6b24c3204eb2fea824d82f88883a3d95e8b4a21b8c0ded553d17d17ddf9a8a7104b1258f30bed3787e6cb896fca78c58f8e03b5f18f14951a87d9a08"
// d := hex.EncodeToString([]byte(priv))
privb, _ := hex.DecodeString(priv)
pvk := ed25519.PrivateKey(privb)
buffer := []byte("4:salt6:foobar3:seqi1e1:v12:Hello World!")
sigb := ed25519.Sign(pvk, buffer)
pubb, _ := hex.DecodeString(pub)
sigb2, _ := hex.DecodeString(sig)
log.Println(ed25519.Verify(pubb, buffer, sigb))
log.Printf("%x\n", pvk.Public())
log.Printf("%x\n", sigb)
log.Printf("%x\n", sigb2)
}
如何使用Golang生成与bep相同的公钥?
英文:
Using the package https://github.com/golang/crypto/tree/master/ed25519 i m trying to get a public key for a given private key.
Those data are from http://www.bittorrent.org/beps/bep_0044.html: test 2 (mutable with salt)
Problem is, ed25519.Public() won t return the same public key when i fed it with the given private key.
The golang implementation returns the last 32 bytes of the PVK. But in my test data this is unexpected.
The code here https://play.golang.org/p/UJNPCyuGQB
package main
import (
"encoding/hex"
"golang.org/x/crypto/ed25519"
"log"
)
func main() {
priv := "e06d3183d14159228433ed599221b80bd0a5ce8352e4bdf0262f76786ef1c74db7e7a9fea2c0eb269d61e3b38e450a22e754941ac78479d6c54e1faf6037881d"
pub := "77ff84905a91936367c01360803104f92432fcd904a43511876df5cdf3e7e548"
sig := "6834284b6b24c3204eb2fea824d82f88883a3d95e8b4a21b8c0ded553d17d17ddf9a8a7104b1258f30bed3787e6cb896fca78c58f8e03b5f18f14951a87d9a08"
// d := hex.EncodeToString([]byte(priv))
privb, _ := hex.DecodeString(priv)
pvk := ed25519.PrivateKey(privb)
buffer := []byte("4:salt6:foobar3:seqi1e1:v12:Hello World!")
sigb := ed25519.Sign(pvk, buffer)
pubb, _ := hex.DecodeString(pub)
sigb2, _ := hex.DecodeString(sig)
log.Println(ed25519.Verify(pubb, buffer, sigb))
log.Printf("%x\n", pvk.Public())
log.Printf("%x\n", sigb)
log.Printf("%x\n", sigb2)
}
How to generate the same public key than the bep using golang ?
答案1
得分: 12
这是由于不同的ed25519私钥格式引起的。ed25519私钥起始于一个32字节的种子。该种子与SHA512哈希运算后生成64字节(还翻转了一些位)。其中前32字节用于生成公钥(也是32字节),后32字节用于生成签名。
Golang的私钥格式是将32字节的种子与32字节的公钥连接在一起。你正在使用的Bittorrent文档中的私钥是哈希的结果,长度为64字节(或者可能只是64个用于与哈希结果相同方式使用的随机字节)。
由于无法逆向哈希运算,你无法将Bittorrent密钥转换为Golang API可接受的格式。
你可以基于现有包生成一个基于Golang的版本。
以下代码依赖于内部包golang.org/x/crypto/ed25519/internal/edwards25519
,所以如果你想使用它,你需要将该包复制出来,以便在你的代码中可用。这段代码非常简单粗糙,我基本上只是从现有代码中复制了所需的代码块,以使其能够工作。
请注意,公钥和签名格式是相同的,所以只要你不共享私钥,你不需要使用这段代码来获得一个可工作的实现。只有当你想要检查测试向量时才需要它。
首先从私钥生成公钥:
// 生成与已哈希私钥对应的公钥。
//
// 此代码大部分是从golang.org/x/crypto/ed25519包中的GenerateKey函数复制而来,从SHA512计算种子之后。
func getPublicKey(privateKey []byte) []byte {
var A edwards25519.ExtendedGroupElement
var hBytes [32]byte
copy(hBytes[:], privateKey)
edwards25519.GeScalarMultBase(&A, &hBytes)
var publicKeyBytes [32]byte
A.ToBytes(&publicKeyBytes)
return publicKeyBytes[:]
}
接下来生成签名:
// 从(预先哈希的)私钥、公钥和消息计算签名。
//
// 此代码大部分是从golang.org/x/crypto/ed25519中的Sign函数复制而来,从SHA512计算种子之后。
func sign(privateKey, publicKey, message []byte) []byte {
var privateKeyA [32]byte
copy(privateKeyA[:], privateKey) // 我们稍后需要将其放入数组中
var messageDigest, hramDigest [64]byte
h := sha512.New()
h.Write(privateKey[32:])
h.Write(message)
h.Sum(messageDigest[:0])
var messageDigestReduced [32]byte
edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
var R edwards25519.ExtendedGroupElement
edwards25519.GeScalarMultBase(&R, &messageDigestReduced)
var encodedR [32]byte
R.ToBytes(&encodedR)
h.Reset()
h.Write(encodedR[:])
h.Write(publicKey)
h.Write(message)
h.Sum(hramDigest[:0])
var hramDigestReduced [32]byte
edwards25519.ScReduce(&hramDigestReduced, &hramDigest)
var s [32]byte
edwards25519.ScMulAdd(&s, &hramDigestReduced, &privateKeyA, &messageDigestReduced)
signature := make([]byte, 64)
copy(signature[:], encodedR[:])
copy(signature[32:], s[:])
return signature
}
最后,我们可以使用这两个函数来演示测试向量:
privateKeyHex := "e06d3183d14159228433ed599221b80bd0a5ce8352e4bdf0262f76786ef1c74db7e7a9fea2c0eb269d61e3b38e450a22e754941ac78479d6c54e1faf6037881d"
expectedPublicKey := "77ff84905a91936367c01360803104f92432fcd904a43511876df5cdf3e7e548"
expectedSig := "6834284b6b24c3204eb2fea824d82f88883a3d95e8b4a21b8c0ded553d17d17ddf9a8a7104b1258f30bed3787e6cb896fca78c58f8e03b5f18f14951a87d9a08"
privateKey, _ := hex.DecodeString(privateKeyHex)
publicKey := getPublicKey(privateKey)
fmt.Printf("Calculated key: %x\n", publicKey)
fmt.Printf("Expected key: %s\n", expectedPublicKey)
keyMatches := expectedPublicKey == hex.EncodeToString(publicKey)
fmt.Printf("Public key matches expected: %v\n", keyMatches)
buffer := []byte("4:salt6:foobar3:seqi1e1:v12:Hello World!")
calculatedSig := sign(privateKey, publicKey, buffer)
fmt.Printf("Calculated sig: %x\n", calculatedSig)
fmt.Printf("Expected sig: %s\n", expectedSig)
sigMatches := expectedSig == hex.EncodeToString(calculatedSig)
fmt.Printf("Signature matches expected: %v\n", sigMatches)
英文:
This is due to different ed25519 private key formats. An ed25519 key starts out as a 32 byte seed. This seed is hashed with SHA512 to produce 64 bytes (a couple of bits are flipped too). The first 32 bytes of these are used to generate the public key (which is also 32 bytes), and the last 32 bytes are used in the generation of the signature.
The Golang private key format is the 32 byte seed concatenated with the 32 byte public key. The private keys in the Bittorrent document you are using are the 64 byte result of the hash (or possibly just 64 random bytes that are used the same way as the hash result).
Since it’s not possible to reverse the hash, you can’t convert the Bittorrent keys to a format that the Golang API will accept.
You can produce a version of the Golang lib based on the existing package.
The following code depends on the internal package golang.org/x/crypto/ed25519/internal/edwards25519
, so if you want to use it you will need to copy that package out so that it is available to you code. It’s also very “rough and ready”, I’ve basically just copied the chunks of code needed from the existing code to get this to work.
Note that the public key and signature formats are the same, so as long as you are not sharing private keys you don’t need to use this code to get a working implementation. You will only need it if you want to check the test vectors.
First generating the public key from a private key:
// Generate the public key corresponding to the already hashed private
// key.
//
// This code is mostly copied from GenerateKey in the
// golang.org/x/crypto/ed25519 package, from after the SHA512
// calculation of the seed.
func getPublicKey(privateKey []byte) []byte {
var A edwards25519.ExtendedGroupElement
var hBytes [32]byte
copy(hBytes[:], privateKey)
edwards25519.GeScalarMultBase(&A, &hBytes)
var publicKeyBytes [32]byte
A.ToBytes(&publicKeyBytes)
return publicKeyBytes[:]
}
Next generating a signature:
// Calculate the signature from the (pre hashed) private key, public key
// and message.
//
// This code is mostly copied from the Sign function from
// golang.org/x/crypto/ed25519, from after the SHA512 calculation of the
// seed.
func sign(privateKey, publicKey, message []byte) []byte {
var privateKeyA [32]byte
copy(privateKeyA[:], privateKey) // we need this in an array later
var messageDigest, hramDigest [64]byte
h := sha512.New()
h.Write(privateKey[32:])
h.Write(message)
h.Sum(messageDigest[:0])
var messageDigestReduced [32]byte
edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
var R edwards25519.ExtendedGroupElement
edwards25519.GeScalarMultBase(&R, &messageDigestReduced)
var encodedR [32]byte
R.ToBytes(&encodedR)
h.Reset()
h.Write(encodedR[:])
h.Write(publicKey)
h.Write(message)
h.Sum(hramDigest[:0])
var hramDigestReduced [32]byte
edwards25519.ScReduce(&hramDigestReduced, &hramDigest)
var s [32]byte
edwards25519.ScMulAdd(&s, &hramDigestReduced, &privateKeyA, &messageDigestReduced)
signature := make([]byte, 64)
copy(signature[:], encodedR[:])
copy(signature[32:], s[:])
return signature
}
Finally we can use these two functions to demonstrate the test vectors:
privateKeyHex := "e06d3183d14159228433ed599221b80bd0a5ce8352e4bdf0262f76786ef1c74db7e7a9fea2c0eb269d61e3b38e450a22e754941ac78479d6c54e1faf6037881d"
expectedPublicKey := "77ff84905a91936367c01360803104f92432fcd904a43511876df5cdf3e7e548"
expectedSig := "6834284b6b24c3204eb2fea824d82f88883a3d95e8b4a21b8c0ded553d17d17ddf9a8a7104b1258f30bed3787e6cb896fca78c58f8e03b5f18f14951a87d9a08"
privateKey, _ := hex.DecodeString(privateKeyHex)
publicKey := getPublicKey(privateKey)
fmt.Printf("Calculated key: %x\n", publicKey)
fmt.Printf("Expected key: %s\n", expectedPublicKey)
keyMatches := expectedPublicKey == hex.EncodeToString(publicKey)
fmt.Printf("Public key matches expected: %v\n", keyMatches)
buffer := []byte("4:salt6:foobar3:seqi1e1:v12:Hello World!")
calculatedSig := sign(privateKey, publicKey, buffer)
fmt.Printf("Calculated sig: %x\n", calculatedSig)
fmt.Printf("Expected sig: %s\n", expectedSig)
sigMatches := expectedSig == hex.EncodeToString(calculatedSig)
fmt.Printf("Signature matches expected: %v\n", sigMatches)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论