Files
kisstron/secp256k1/secp256k1_test.go
T

131 lines
4.1 KiB
Go
Raw Normal View History

2026-09-19 18:26:07 +03:00
package secp256k1
import (
"encoding/hex"
"math/big"
"testing"
)
func mustDecodeHex(t *testing.T, s string) []byte {
t.Helper()
b, err := hex.DecodeString(s)
if err != nil {
t.Fatalf("decode hex: %v", err)
}
return b
}
// TestBasePointOnCurve confirms the generator satisfies y^2 = x^3 + 7.
func TestBasePointOnCurve(t *testing.T) {
if !IsOnCurve(&Point{X: Gx, Y: Gy}) {
t.Fatalf("generator is not on the curve")
}
}
// TestPointDoublingMatchesScalarMult verifies group consistency: 2*G computed
// via point doubling equals k*G for k=2.
func TestPointDoublingMatchesScalarMult(t *testing.T) {
g := &Point{X: Gx, Y: Gy}
viaDouble := Add(g, g)
viaScalar := ScalarBaseMult(big.NewInt(2))
if viaDouble.X.Cmp(viaScalar.X) != 0 || viaDouble.Y.Cmp(viaScalar.Y) != 0 {
t.Fatalf("2G mismatch: double=%v scalar=%v", viaDouble, viaScalar)
}
if !IsOnCurve(viaScalar) {
t.Fatalf("2G not on curve")
}
}
// TestScalarMultDistributes checks (a+b)G == aG + bG.
func TestScalarMultDistributes(t *testing.T) {
a := big.NewInt(12345)
b := big.NewInt(67890)
sum := new(big.Int).Add(a, b)
left := ScalarBaseMult(sum)
right := Add(ScalarBaseMult(a), ScalarBaseMult(b))
if left.X.Cmp(right.X) != 0 || left.Y.Cmp(right.Y) != 0 {
t.Fatalf("(a+b)G != aG + bG")
}
}
// TestPublicKeyKnownValues locks the uncompressed public key for the t3 key.
func TestPublicKeyKnownValues(t *testing.T) {
priv := mustDecodeHex(t, "a96135266ba58bb5b3a450b8fe7a9d59ea3e6cb0cdb2300e8138d2d2d48e61a8")
point, err := PublicKey(priv)
if err != nil {
t.Fatalf("PublicKey: %v", err)
}
raw := make([]byte, 64)
point.X.FillBytes(raw[0:32])
point.Y.FillBytes(raw[32:64])
want := "076e3db946fcb53d575c4e742a9f8ff8c4b57ded53776b2a80d13022e1647adaa1641f3e2210f5f6a3bdd73d073ac029ba79d576093d8a92dd148e448a8e63cb"
if got := hex.EncodeToString(raw); got != want {
t.Fatalf("pubkey = %s, want %s", got, want)
}
if !IsOnCurve(point) {
t.Fatalf("derived public key not on curve")
}
}
// TestSignRecoverRoundTrip signs and recovers for several keys and hashes.
func TestSignRecoverRoundTrip(t *testing.T) {
privs := []string{
"a96135266ba58bb5b3a450b8fe7a9d59ea3e6cb0cdb2300e8138d2d2d48e61a8",
"3252a4cb9b3865c94dbb7d6f50107ca704c1c2b9e3d1eb81753e221c7db8fa25",
"993638df18a38ef2be9f8c915eb9063647d015edc8fb70e3bcb32469bee39c21",
}
hashes := []string{
"e93945efde601eed484553338e58218f4e81c51261dfda72db2d874f75378fe8",
"0000000000000000000000000000000000000000000000000000000000000001",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
}
for _, p := range privs {
priv := mustDecodeHex(t, p)
wantPoint, _ := PublicKey(priv)
for _, h := range hashes {
hash := mustDecodeHex(t, h)
sig, err := Sign(priv, hash)
if err != nil {
t.Fatalf("Sign: %v", err)
}
// low-s enforcement
if sig.S.Cmp(halfN) > 0 {
t.Fatalf("signature s is not low-s")
}
recovered, err := RecoverPublicKey(hash, sig)
if err != nil {
t.Fatalf("RecoverPublicKey: %v", err)
}
if recovered.X.Cmp(wantPoint.X) != 0 || recovered.Y.Cmp(wantPoint.Y) != 0 {
t.Fatalf("recovered key differs for priv=%s hash=%s", p, h)
}
}
}
}
// TestSignIsDeterministic confirms RFC6979-style reproducibility.
func TestSignIsDeterministic(t *testing.T) {
priv := mustDecodeHex(t, "a96135266ba58bb5b3a450b8fe7a9d59ea3e6cb0cdb2300e8138d2d2d48e61a8")
hash := mustDecodeHex(t, "e93945efde601eed484553338e58218f4e81c51261dfda72db2d874f75378fe8")
a, err := Sign(priv, hash)
if err != nil {
t.Fatalf("Sign: %v", err)
}
b, err := Sign(priv, hash)
if err != nil {
t.Fatalf("Sign: %v", err)
}
if hex.EncodeToString(a.Bytes()) != hex.EncodeToString(b.Bytes()) {
t.Fatalf("signatures differ for identical inputs")
}
}
func TestPublicKeyRejectsInvalid(t *testing.T) {
if _, err := PublicKey(make([]byte, 31)); err == nil {
t.Fatalf("expected error for short key")
}
if _, err := PublicKey(make([]byte, 32)); err == nil {
t.Fatalf("expected error for zero key")
}
}