ed25519.Public 结果不同

ed25519.Public result is different

使用包 https://github.com/golang/crypto/tree/master/ed25519 我正在尝试为给定的私钥获取 public 密钥。

这些数据来自 http://www.bittorrent.org/beps/bep_0044.html:测试 2(含盐可变)

问题是,ed25519.Public() 不会 return 使用给定的私钥输入相同的 public 密钥。 golang 实现 return 是 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 相同的 public 密钥?

这是由于 ed25519 私钥格式不同所致。 ed25519 密钥以 32 字节 seed 开头。这个种子用 SHA512 散列以产生 64 个字节(也有几个位被翻转)。其中前 32 个字节用于生成 public 密钥(也是 32 个字节),最后 32 个字节用于生成签名。

Golang 私钥格式是 32 字节种子与 32 字节 public 密钥连接。您正在使用的 Bittorrent 文档中的私钥是哈希的 64 字节结果(或者可能只是 64 个随机字节,其使用方式与哈希结果相同)。

由于无法反转散列,因此您无法将 Bittorrent 密钥转换为 Golang API 可以接受的格式。

您可以根据现有包生成一个版本的 Golang 库。

以下代码依赖于内部包 golang.org/x/crypto/ed25519/internal/edwards25519,因此如果您想使用它,您需要将该包复制出来,以便您的代码可以使用它。它也非常“粗糙且准备就绪”,我基本上只是从现有代码中复制了所需的代码块以使其工作。

请注意,public 密钥和签名格式是相同的,因此只要您不共享私钥,就不需要使用此代码来获得有效的实现。只有当你想检查测试向量时才需要它。

首先从私钥生成 public 密钥:

// 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[:]
}

下一步生成签名:

// 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
}

终于可以用这两个函数来演示测试向量了:

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)