1 回答

TA贡献1875条经验 获得超3个赞
我发现我的 C++ 椭圆曲线库在将公钥(点)压缩(编组)为SEC 1,版本 2.0,第 2.3.3 节形式时有一个错误。有些点第一个字节错误(从奇数到偶数,或从偶数到奇数)。
1*G GO buffer: 0x036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296 C++ buffer: 0x026b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296
2*G GO buffer: 0x037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978 C++ buffer: 0x037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978
3*G GO buffer: 0x025ecbe4d1a6330a44c8f7ef951d4bf165e6c6b721efada985fb41661bc6e7fd6c C++ buffer: 0x035ecbe4d1a6330a44c8f7ef951d4bf165e6c6b721efada985fb41661bc6e7fd6c
4*G GO buffer: 0x02e2534a3532d08fbba02dde659ee62bd0031fe2db785596ef509302446b030852 C++ buffer: 0x02e2534a3532d08fbba02dde659ee62bd0031fe2db785596ef509302446b030852
5*G GO buffer: 0x0251590b7a515140d2d784c85608668fdfef8c82fd1f5be52421554a0dc3d033ed C++ buffer: 0x0251590b7a515140d2d784c85608668fdfef8c82fd1f5be52421554a0dc3d033ed
6*G GO buffer: 0x02b01a172a76a4602c92d3242cb897dde3024c740debb215b4c6b0aae93c2291a9 C++ buffer: 0x03b01a172a76a4602c92d3242cb897dde3024c740debb215b4c6b0aae93c2291a9
7*G GO buffer: 0x028e533b6fa0bf7b4625bb30667c01fb607ef9f8b8a80fef5b300628703187b2a3 C++ buffer: 0x038e533b6fa0bf7b4625bb30667c01fb607ef9f8b8a80fef5b300628703187b2a3
8*G GO buffer: 0x0262d9779dbee9b0534042742d3ab54cadc1d238980fce97dbb4dd9dc1db6fb393 C++ buffer: 0x0362d9779dbee9b0534042742d3ab54cadc1d238980fce97dbb4dd9dc1db6fb393
9*G GO buffer: 0x02ea68d7b6fedf0b71878938d51d71f8729e0acb8c2c6df8b3d79e8a4b90949ee0 C++ buffer: 0x02ea68d7b6fedf0b71878938d51d71f8729e0acb8c2c6df8b3d79e8a4b90949ee0
因此,一些公钥(点)被导出为而{x,-y} 不是{x,y}
原来的
椭圆曲线 (EC) 公钥遵循高效密码术标准 (SEC),SEC 1:椭圆曲线密码术部分中的 2.3.3 椭圆曲线点到八位字节字符串转换。
如果 y 坐标为偶数,则压缩形式(作为字节)的公钥的第一个字节为0x02 。否则,如果 y 坐标为奇数,则为 0x03。
在数学上,只给定一个 x 坐标
y^2 = x^3 + ax + b (mod p)
上面的等式有两个解,(x, y)和(x, -y)因为 y^2 = (-y)^2
此外,-y 等于 (p - y),因为它是整数模素数p的字段。
由于p 是素数且 p!=2,因此p 是奇数。然后,如果y是奇数,则-y(或(py))是偶数,反之亦然。
ECDSA 验证算法不需要公钥的 y 坐标,因为它只需要它的 x 坐标。因此,当我检查签名元组{r,s}时,我将使用相同 x 坐标但不同 y 坐标(从奇数翻转到偶数,反之亦然)的公钥对其进行检查。我真的不知道为什么 GO 语言 ECDSA 包只允许两个 y 坐标值之一。
下面是我的代码示例。假设我有一个压缩的公钥作为缓冲区publicKeyBuffer[:]
然后我可以解组它以获得 x 坐标和 y 坐标
pk := new(ecdsa.PublicKey)
pk.Curve = elliptic.P256()
pk.X, pk.Y = elliptic.UnmarshalCompressed(elliptic.P256(), publicKeyBuffer[:])
我们可以检查签名元组 {r,s} 是否有效
valid := ecdsa.Verify(pk, hash[:], r, s)
如果签名无效,即valid == false尝试将值更改pk.Y为对应的值。您可以通过两种简单的方式做到这一点。
第一种方法,您将公钥缓冲区的第一个字节从 更改为0x02或0x03反之亦然。您可以通过XOR使用(仅 1)执行第一个字节来实现此目的0x01。
valid = ecdsa.Verify(pk, hash[:], r, s)
/* The 1st way */
if !valid {
publicKeyBuffer[0] ^= 1 // change the y-coordinate by switching 0x02 to 0x03 (or vice versa) of public key buffer
pk.X, pk.Y = elliptic.UnmarshalCompressed(elliptic.P256(), publicKeyBuffer[:])
valid = ecdsa.Verify(pk, hash[:], r, s)
}
fmt.Println("signature verified:", valid)
在第二种方式中,我们将 y 坐标更改为其 (py) 值,即,将其更改为 (-y)
valid = ecdsa.Verify(pk, hash[:], r, s)
/* The 2nd way */
if !valid {
pk.Y = new(big.Int).Sub(pk.Curve.Params().P, pk.Y) // replace y-coordinate pk.Y with (-y), Note. (-y) is equal (p - y)
valid = ecdsa.Verify(pk, hash[:], r, s)
}
fmt.Println("signature verified:", valid)
如果签名仍然无效,那么这与公钥 Unmarshal 无关。
PS 我不知道为什么 GO ECDSA 语言不允许公钥的其他 y 坐标值
- 1 回答
- 0 关注
- 304 浏览
添加回答
举报