ed25519.Общий результат другой

Используя пакет https://github.com/golang/crypto/tree/master/ed25519 пытаюсь получить открытый ключ для данного закрытого ключа.

Эти данные взяты из http://www.bittorrent.org/beps/bep_0044.html: тест 2 (изменяемый с солью)

Проблема в том, что ed25519.Public() не возвращает тот же открытый ключ, когда я передаю его с данным закрытым ключом. Реализация golang возвращает последние 32 байта PVK. Но в моих тестовых данных это неожиданно.

Код здесь 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)
}

Как сгенерировать тот же открытый ключ, что и bep, используя golang?

1 ответ

Решение

Это связано с различными форматами закрытых ключей 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, поэтому, если вы хотите использовать его, вам нужно будет скопировать этот пакет, чтобы он был доступен для вашего кода. Это также очень "грубо и готово", я просто скопировал куски кода, необходимые из существующего кода, чтобы заставить это работать.

Обратите внимание, что открытый ключ и форматы подписи одинаковы, поэтому, пока вы не делитесь закрытыми ключами, вам не нужно использовать этот код для получения работающей реализации. Он понадобится вам только в том случае, если вы хотите проверить векторы испытаний.

Сначала генерируем открытый ключ из закрытого ключа:

// 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)
Другие вопросы по тегам