Как распаковать одну сжатую точку X9.62 на кривой ECDH P256 в Go?

Библиотека эллиптических кривых Голанга может получить секретный ключ по открытой координате со значениями X и Y (несжатые координаты).

Однако когда указанная точка представляет собой одно значение в сжатой форме X9.62 с заданным y-битом, как мне распаковать его?

OpenSSL обрабатывает этот сценарий с помощью этого метода:

https://github.com/openssl/openssl/blob/4e9b720e90ec154c9708139e96ec0ff8e2796c82/include/openssl/ec.h#L494

Также, похоже, существует аналогичный вопрос, касающийся математики, но это не лучшая практика для Go, а именно:

https://crypto.stackexchange.com/questions/8914/ecdsa-compressed-public-key-point-back-to-uncompressed-public-key-point

Как это сделать в Go?

2 ответа

Насколько я знаю, в стандартной библиотеке Go нет функции распаковки точек (или пакетов "x"), поэтому вам придется делать это самостоятельно (или находить существующую реализацию).

Реализация не слишком сложна, хотя есть несколько вещей, на которые стоит обратить внимание.

По сути, вам нужно подключить X значение в уравнении кривой Y2 = X3 + aX + b и затем определите, какой из двух корней вы хотите использовать, используя бит знака. Сложно вспомнить, что все это должно быть сделано по модулю простого поля группы.

Я нахожу, что большой целочисленный пакет Go может быть немного странным, потому что он использует изменяемые значения, но у него есть модульная функция квадратного корня, которая значительно упрощает нам задачу. Параметры кривой доступны в crypto/elliptic пакет, хотя вам нужно знать a параметр всегда -3 для этих кривых.

Предполагая, что у вас есть сжатая точка как []byte (с ведущим 0x02 или же 0x03) в compressed_bytes, следующее должно работать. Это довольно прямая реализация уравнения, разбитая на комментарии и множество именованных переменных, чтобы попытаться объяснить, что происходит. Посмотрите на источник CurveParams.IsOnCurve для немного более эффективной (и более короткой) реализации. Это в основном то же самое до модульного квадратного корня.

compressed_bytes := //...

// Split the sign byte from the rest
sign_byte := uint(compressed_bytes[0])
x_bytes := compressed_bytes[1:]

// Convert to big Int.
x := new(big.Int).SetBytes(x_bytes)

// We use 3 a couple of times
three := big.NewInt(3)

// and we need the curve params for P256
c := elliptic.P256().Params()

// The equation is y^2 = x^3 - 3x + b
// First, x^3, mod P
x_cubed := new(big.Int).Exp(x, three, c.P)

// Next, 3x, mod P
three_X := new(big.Int).Mul(x, three)
three_X.Mod(three_X, c.P)

// x^3 - 3x ...
y_squared := new(big.Int).Sub(x_cubed, three_X)

// ... + b mod P
y_squared.Add(y_squared, c.B)
y_squared.Mod(y_squared, c.P)

// Now we need to find the square root mod P.
// This is where Go's big int library redeems itself.
y := new(big.Int).ModSqrt(y_squared, c.P)
if y == nil {
    // If this happens then you're dealing with an invalid point.
    // Panic, return an error, whatever you want here.
}

// Finally, check if you have the correct root by comparing
// the low bit with the low bit of the sign byte. If it’s not
// the same you want -y mod P instead of y.
if y.Bit(0) != sign_byte & 1 {
    y.Neg(y)
    y.Mod(y, c.P)
}

// Now your y coordinate is in y, for all your ScalarMult needs.

В более поздних версиях Go ситуация улучшилась. Теперь можно использовать метод UnmarshalCompressed .Это было введено в Go 1.15.

Пример:

      x,y := elliptic.UnmarshalCompressed(elliptic.P224(), compressed)
Другие вопросы по тегам