108 lines
2.5 KiB
Go
108 lines
2.5 KiB
Go
package mpc
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"fmt"
|
|
"math/big"
|
|
|
|
"golang.org/x/crypto/sha3"
|
|
)
|
|
|
|
func VerifyWithPubKey(pubKey []byte, data []byte, sig []byte) (bool, error) {
|
|
if len(sig) != 64 {
|
|
return false, fmt.Errorf("invalid signature length: expected 64, got %d", len(sig))
|
|
}
|
|
|
|
pk, err := parsePublicKey(pubKey)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
r := new(big.Int).SetBytes(sig[:32])
|
|
s := new(big.Int).SetBytes(sig[32:])
|
|
|
|
hash := sha3.New256()
|
|
hash.Write(data)
|
|
digest := hash.Sum(nil)
|
|
|
|
return ecdsa.Verify(pk, digest, r, s), nil
|
|
}
|
|
|
|
func parsePublicKey(pubKey []byte) (*ecdsa.PublicKey, error) {
|
|
curve := elliptic.P256()
|
|
// Use secp256k1 parameters manually since Go stdlib doesn't include it
|
|
curve = secp256k1Curve()
|
|
|
|
switch len(pubKey) {
|
|
case 65: // uncompressed: 0x04 || x || y
|
|
if pubKey[0] != 0x04 {
|
|
return nil, fmt.Errorf("invalid uncompressed pubkey prefix: %x", pubKey[0])
|
|
}
|
|
x := new(big.Int).SetBytes(pubKey[1:33])
|
|
y := new(big.Int).SetBytes(pubKey[33:65])
|
|
return &ecdsa.PublicKey{Curve: curve, X: x, Y: y}, nil
|
|
|
|
case 33: // compressed: 0x02/0x03 || x
|
|
x, y := decompressPoint(curve, pubKey)
|
|
if x == nil {
|
|
return nil, fmt.Errorf("failed to decompress pubkey")
|
|
}
|
|
return &ecdsa.PublicKey{Curve: curve, X: x, Y: y}, nil
|
|
|
|
default:
|
|
return nil, fmt.Errorf("invalid pubkey length: %d", len(pubKey))
|
|
}
|
|
}
|
|
|
|
func secp256k1Curve() elliptic.Curve {
|
|
return &secp256k1Params
|
|
}
|
|
|
|
var secp256k1Params = elliptic.CurveParams{
|
|
Name: "secp256k1",
|
|
BitSize: 256,
|
|
P: fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"),
|
|
N: fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"),
|
|
B: big.NewInt(7),
|
|
Gx: fromHex("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"),
|
|
Gy: fromHex("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8"),
|
|
}
|
|
|
|
func fromHex(s string) *big.Int {
|
|
i, _ := new(big.Int).SetString(s, 16)
|
|
return i
|
|
}
|
|
|
|
func decompressPoint(curve elliptic.Curve, compressed []byte) (*big.Int, *big.Int) {
|
|
if len(compressed) != 33 {
|
|
return nil, nil
|
|
}
|
|
|
|
prefix := compressed[0]
|
|
if prefix != 0x02 && prefix != 0x03 {
|
|
return nil, nil
|
|
}
|
|
|
|
x := new(big.Int).SetBytes(compressed[1:])
|
|
p := curve.Params().P
|
|
|
|
// y² = x³ + 7 (for secp256k1)
|
|
x3 := new(big.Int).Mul(x, x)
|
|
x3.Mul(x3, x)
|
|
x3.Add(x3, big.NewInt(7))
|
|
x3.Mod(x3, p)
|
|
|
|
y := new(big.Int).ModSqrt(x3, p)
|
|
if y == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
// Check parity
|
|
if (y.Bit(0) == 1) != (prefix == 0x03) {
|
|
y.Sub(p, y)
|
|
}
|
|
|
|
return x, y
|
|
}
|