Golang 的椭圆曲线库可以在给定具有 X 和 Y 值(未压缩坐标)的公共坐标的情况下导出密钥。
但是,当给定的点是 X9.62 压缩形式中具有给定 y 位的单个值时,如何解压缩它?
OpenSSL 使用以下方法处理这种情况:
似乎也有一个类似的问题解决了所涉及的数学问题,但不是 Go 的最佳实践,具体来说:
这应该如何在 Go 中完成?
Golang 的椭圆曲线库可以在给定具有 X 和 Y 值(未压缩坐标)的公共坐标的情况下导出密钥。
但是,当给定的点是 X9.62 压缩形式中具有给定 y 位的单个值时,如何解压缩它?
OpenSSL 使用以下方法处理这种情况:
似乎也有一个类似的问题解决了所涉及的数学问题,但不是 Go 的最佳实践,具体来说:
这应该如何在 Go 中完成?
据我所知,Go 标准库(或“x”包)中没有点解压功能,所以你必须自己做(或找到现有的实现)。
尽管有几件事需要注意,但实施并不难。
基本上,您需要将X
值插入曲线方程,然后使用符号位确定您想要的两个根中的哪一个。棘手的一点是要记住所有这些都需要以组的场素数为模来完成。Y2 = X3 + aX + b
我发现Go 的大整数包有时使用起来有点奇怪,因为它使用可变值,但它确实有一个模块化的平方根函数,这让我们的事情变得更容易。曲线参数在crypto/elliptic
包中可用,尽管您需要知道a
参数始终-3
适用于这些曲线。
假设您将压缩点作为[]byte
(带前导0x02
或0x03
) in 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.