瀏覽代碼

crypto, crypto/secp256k1: add CompressPubkey (#15626)

This adds the inverse to DecompressPubkey and improves a few minor
details in crypto/secp256k1.
Felix Lange 8 年之前
父節點
當前提交
c6069a627c
共有 6 個文件被更改,包括 118 次插入52 次删除
  1. 19 27
      crypto/secp256k1/curve.go
  2. 23 17
      crypto/secp256k1/ext.h
  3. 28 8
      crypto/secp256k1/secp256.go
  4. 5 0
      crypto/signature_cgo.go
  5. 5 0
      crypto/signature_nocgo.go
  6. 38 0
      crypto/signature_test.go

+ 19 - 27
crypto/secp256k1/curve.go

@@ -34,7 +34,6 @@ package secp256k1
 import (
 	"crypto/elliptic"
 	"math/big"
-	"sync"
 	"unsafe"
 
 	"github.com/ethereum/go-ethereum/common/math"
@@ -42,7 +41,7 @@ import (
 
 /*
 #include "libsecp256k1/include/secp256k1.h"
-extern int secp256k1_pubkey_scalar_mul(const secp256k1_context* ctx, const unsigned char *point, const unsigned char *scalar);
+extern int secp256k1_ext_scalar_mul(const secp256k1_context* ctx, const unsigned char *point, const unsigned char *scalar);
 */
 import "C"
 
@@ -236,7 +235,7 @@ func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int,
 	math.ReadBits(By, point[32:])
 	pointPtr := (*C.uchar)(unsafe.Pointer(&point[0]))
 	scalarPtr := (*C.uchar)(unsafe.Pointer(&scalar[0]))
-	res := C.secp256k1_pubkey_scalar_mul(context, pointPtr, scalarPtr)
+	res := C.secp256k1_ext_scalar_mul(context, pointPtr, scalarPtr)
 
 	// Unpack the result and clear temporaries.
 	x := new(big.Int).SetBytes(point[:32])
@@ -263,14 +262,10 @@ func (BitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
 // X9.62.
 func (BitCurve *BitCurve) Marshal(x, y *big.Int) []byte {
 	byteLen := (BitCurve.BitSize + 7) >> 3
-
 	ret := make([]byte, 1+2*byteLen)
-	ret[0] = 4 // uncompressed point
-
-	xBytes := x.Bytes()
-	copy(ret[1+byteLen-len(xBytes):], xBytes)
-	yBytes := y.Bytes()
-	copy(ret[1+2*byteLen-len(yBytes):], yBytes)
+	ret[0] = 4 // uncompressed point flag
+	math.ReadBits(x, ret[1:1+byteLen])
+	math.ReadBits(y, ret[1+byteLen:])
 	return ret
 }
 
@@ -289,24 +284,21 @@ func (BitCurve *BitCurve) Unmarshal(data []byte) (x, y *big.Int) {
 	return
 }
 
-var (
-	initonce sync.Once
-	theCurve *BitCurve
-)
+var theCurve = new(BitCurve)
+
+func init() {
+	// See SEC 2 section 2.7.1
+	// curve parameters taken from:
+	// http://www.secg.org/collateral/sec2_final.pdf
+	theCurve.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16)
+	theCurve.N, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16)
+	theCurve.B, _ = new(big.Int).SetString("0000000000000000000000000000000000000000000000000000000000000007", 16)
+	theCurve.Gx, _ = new(big.Int).SetString("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16)
+	theCurve.Gy, _ = new(big.Int).SetString("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16)
+	theCurve.BitSize = 256
+}
 
-// S256 returns a BitCurve which implements secp256k1 (see SEC 2 section 2.7.1)
+// S256 returns a BitCurve which implements secp256k1.
 func S256() *BitCurve {
-	initonce.Do(func() {
-		// See SEC 2 section 2.7.1
-		// curve parameters taken from:
-		// http://www.secg.org/collateral/sec2_final.pdf
-		theCurve = new(BitCurve)
-		theCurve.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16)
-		theCurve.N, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16)
-		theCurve.B, _ = new(big.Int).SetString("0000000000000000000000000000000000000000000000000000000000000007", 16)
-		theCurve.Gx, _ = new(big.Int).SetString("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16)
-		theCurve.Gy, _ = new(big.Int).SetString("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16)
-		theCurve.BitSize = 256
-	})
 	return theCurve
 }

+ 23 - 17
crypto/secp256k1/ext.h

@@ -19,7 +19,7 @@ static secp256k1_context* secp256k1_context_create_sign_verify() {
 	return secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
 }
 
-// secp256k1_ecdsa_recover_pubkey recovers the public key of an encoded compact signature.
+// secp256k1_ext_ecdsa_recover recovers the public key of an encoded compact signature.
 //
 // Returns: 1: recovery was successful
 //          0: recovery was not successful
@@ -27,7 +27,7 @@ static secp256k1_context* secp256k1_context_create_sign_verify() {
 //  Out:    pubkey_out: the serialized 65-byte public key of the signer (cannot be NULL)
 //  In:     sigdata:    pointer to a 65-byte signature with the recovery id at the end (cannot be NULL)
 //          msgdata:    pointer to a 32-byte message (cannot be NULL)
-static int secp256k1_ecdsa_recover_pubkey(
+static int secp256k1_ext_ecdsa_recover(
 	const secp256k1_context* ctx,
 	unsigned char *pubkey_out,
 	const unsigned char *sigdata,
@@ -46,7 +46,7 @@ static int secp256k1_ecdsa_recover_pubkey(
 	return secp256k1_ec_pubkey_serialize(ctx, pubkey_out, &outputlen, &pubkey, SECP256K1_EC_UNCOMPRESSED);
 }
 
-// secp256k1_ecdsa_verify_enc verifies an encoded compact signature.
+// secp256k1_ext_ecdsa_verify verifies an encoded compact signature.
 //
 // Returns: 1: signature is valid
 //          0: signature is invalid
@@ -55,7 +55,7 @@ static int secp256k1_ecdsa_recover_pubkey(
 //          msgdata:    pointer to a 32-byte message (cannot be NULL)
 //          pubkeydata: pointer to public key data (cannot be NULL)
 //          pubkeylen:  length of pubkeydata
-static int secp256k1_ecdsa_verify_enc(
+static int secp256k1_ext_ecdsa_verify(
 	const secp256k1_context* ctx,
 	const unsigned char *sigdata,
 	const unsigned char *msgdata,
@@ -74,28 +74,34 @@ static int secp256k1_ecdsa_verify_enc(
 	return secp256k1_ecdsa_verify(ctx, &sig, msgdata, &pubkey);
 }
 
-// secp256k1_decompress_pubkey decompresses a public key.
+// secp256k1_ext_reencode_pubkey decodes then encodes a public key. It can be used to
+// convert between public key formats. The input/output formats are chosen depending on the
+// length of the input/output buffers.
 //
-// Returns: 1: public key is valid
-//          0: public key is invalid
+// Returns: 1: conversion successful
+//          0: conversion unsuccessful
 // Args:    ctx:        pointer to a context object (cannot be NULL)
-//  Out:    pubkey_out: the serialized 65-byte public key (cannot be NULL)
-//  In:     pubkeydata: pointer to 33 bytes of compressed public key data (cannot be NULL)
-static int secp256k1_decompress_pubkey(
+//  Out:    out:        output buffer that will contain the reencoded key (cannot be NULL)
+//  In:     outlen:     length of out (33 for compressed keys, 65 for uncompressed keys)
+//          pubkeydata: the input public key (cannot be NULL)
+//          pubkeylen:  length of pubkeydata
+static int secp256k1_ext_reencode_pubkey(
 	const secp256k1_context* ctx,
-	unsigned char *pubkey_out,
-	const unsigned char *pubkeydata
+	unsigned char *out,
+	size_t outlen,
+	const unsigned char *pubkeydata,
+	size_t pubkeylen
 ) {
 	secp256k1_pubkey pubkey;
 
-	if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeydata, 33)) {
+	if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeydata, pubkeylen)) {
 		return 0;
 	}
-	size_t outputlen = 65;
-	return secp256k1_ec_pubkey_serialize(ctx, pubkey_out, &outputlen, &pubkey, SECP256K1_EC_UNCOMPRESSED);
+	unsigned int flag = (outlen == 33) ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED;
+	return secp256k1_ec_pubkey_serialize(ctx, out, &outlen, &pubkey, flag);
 }
 
-// secp256k1_pubkey_scalar_mul multiplies a point by a scalar in constant time.
+// secp256k1_ext_scalar_mul multiplies a point by a scalar in constant time.
 //
 // Returns: 1: multiplication was successful
 //          0: scalar was invalid (zero or overflow)
@@ -104,7 +110,7 @@ static int secp256k1_decompress_pubkey(
 //  In:     point:    pointer to a 64-byte public point,
 //                    encoded as two 256bit big-endian numbers.
 //          scalar:   a 32-byte scalar with which to multiply the point
-int secp256k1_pubkey_scalar_mul(const secp256k1_context* ctx, unsigned char *point, const unsigned char *scalar) {
+int secp256k1_ext_scalar_mul(const secp256k1_context* ctx, unsigned char *point, const unsigned char *scalar) {
 	int ret = 0;
 	int overflow = 0;
 	secp256k1_fe feX, feY;

+ 28 - 8
crypto/secp256k1/secp256.go

@@ -115,7 +115,7 @@ func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
 		sigdata = (*C.uchar)(unsafe.Pointer(&sig[0]))
 		msgdata = (*C.uchar)(unsafe.Pointer(&msg[0]))
 	)
-	if C.secp256k1_ecdsa_recover_pubkey(context, (*C.uchar)(unsafe.Pointer(&pubkey[0])), sigdata, msgdata) == 0 {
+	if C.secp256k1_ext_ecdsa_recover(context, (*C.uchar)(unsafe.Pointer(&pubkey[0])), sigdata, msgdata) == 0 {
 		return nil, ErrRecoverFailed
 	}
 	return pubkey, nil
@@ -130,22 +130,42 @@ func VerifySignature(pubkey, msg, signature []byte) bool {
 	sigdata := (*C.uchar)(unsafe.Pointer(&signature[0]))
 	msgdata := (*C.uchar)(unsafe.Pointer(&msg[0]))
 	keydata := (*C.uchar)(unsafe.Pointer(&pubkey[0]))
-	return C.secp256k1_ecdsa_verify_enc(context, sigdata, msgdata, keydata, C.size_t(len(pubkey))) != 0
+	return C.secp256k1_ext_ecdsa_verify(context, sigdata, msgdata, keydata, C.size_t(len(pubkey))) != 0
 }
 
 // DecompressPubkey parses a public key in the 33-byte compressed format.
 // It returns non-nil coordinates if the public key is valid.
-func DecompressPubkey(pubkey []byte) (X, Y *big.Int) {
+func DecompressPubkey(pubkey []byte) (x, y *big.Int) {
 	if len(pubkey) != 33 {
 		return nil, nil
 	}
-	buf := make([]byte, 65)
-	bufdata := (*C.uchar)(unsafe.Pointer(&buf[0]))
-	pubkeydata := (*C.uchar)(unsafe.Pointer(&pubkey[0]))
-	if C.secp256k1_decompress_pubkey(context, bufdata, pubkeydata) == 0 {
+	var (
+		pubkeydata = (*C.uchar)(unsafe.Pointer(&pubkey[0]))
+		pubkeylen  = C.size_t(len(pubkey))
+		out        = make([]byte, 65)
+		outdata    = (*C.uchar)(unsafe.Pointer(&out[0]))
+		outlen     = C.size_t(len(out))
+	)
+	if C.secp256k1_ext_reencode_pubkey(context, outdata, outlen, pubkeydata, pubkeylen) == 0 {
 		return nil, nil
 	}
-	return new(big.Int).SetBytes(buf[1:33]), new(big.Int).SetBytes(buf[33:])
+	return new(big.Int).SetBytes(out[1:33]), new(big.Int).SetBytes(out[33:])
+}
+
+// CompressPubkey encodes a public key to 33-byte compressed format.
+func CompressPubkey(x, y *big.Int) []byte {
+	var (
+		pubkey     = S256().Marshal(x, y)
+		pubkeydata = (*C.uchar)(unsafe.Pointer(&pubkey[0]))
+		pubkeylen  = C.size_t(len(pubkey))
+		out        = make([]byte, 33)
+		outdata    = (*C.uchar)(unsafe.Pointer(&out[0]))
+		outlen     = C.size_t(len(out))
+	)
+	if C.secp256k1_ext_reencode_pubkey(context, outdata, outlen, pubkeydata, pubkeylen) == 0 {
+		panic("libsecp256k1 error")
+	}
+	return out
 }
 
 func checkSignature(sig []byte) error {

+ 5 - 0
crypto/signature_cgo.go

@@ -76,6 +76,11 @@ func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
 	return &ecdsa.PublicKey{X: x, Y: y, Curve: S256()}, nil
 }
 
+// CompressPubkey encodes a public key to the 33-byte compressed format.
+func CompressPubkey(pubkey *ecdsa.PublicKey) []byte {
+	return secp256k1.CompressPubkey(pubkey.X, pubkey.Y)
+}
+
 // S256 returns an instance of the secp256k1 curve.
 func S256() elliptic.Curve {
 	return secp256k1.S256()

+ 5 - 0
crypto/signature_nocgo.go

@@ -102,6 +102,11 @@ func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
 	return key.ToECDSA(), nil
 }
 
+// CompressPubkey encodes a public key to the 33-byte compressed format.
+func CompressPubkey(pubkey *ecdsa.PublicKey) []byte {
+	return (*btcec.PublicKey)(pubkey).SerializeCompressed()
+}
+
 // S256 returns an instance of the secp256k1 curve.
 func S256() elliptic.Curve {
 	return btcec.S256()

+ 38 - 0
crypto/signature_test.go

@@ -18,10 +18,13 @@ package crypto
 
 import (
 	"bytes"
+	"crypto/ecdsa"
+	"reflect"
 	"testing"
 
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/common/hexutil"
+	"github.com/ethereum/go-ethereum/common/math"
 )
 
 var (
@@ -65,6 +68,11 @@ func TestVerifySignature(t *testing.T) {
 	if VerifySignature(testpubkey, testmsg, sig[:len(sig)-2]) {
 		t.Errorf("signature valid even though it's incomplete")
 	}
+	wrongkey := common.CopyBytes(testpubkey)
+	wrongkey[10]++
+	if VerifySignature(wrongkey, testmsg, sig) {
+		t.Errorf("signature valid with with wrong public key")
+	}
 }
 
 func TestDecompressPubkey(t *testing.T) {
@@ -86,6 +94,36 @@ func TestDecompressPubkey(t *testing.T) {
 	}
 }
 
+func TestCompressPubkey(t *testing.T) {
+	key := &ecdsa.PublicKey{
+		Curve: S256(),
+		X:     math.MustParseBig256("0xe32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a"),
+		Y:     math.MustParseBig256("0x0a2b2667f7e725ceea70c673093bf67663e0312623c8e091b13cf2c0f11ef652"),
+	}
+	compressed := CompressPubkey(key)
+	if !bytes.Equal(compressed, testpubkeyc) {
+		t.Errorf("wrong public key result: got %x, want %x", compressed, testpubkeyc)
+	}
+}
+
+func TestPubkeyRandom(t *testing.T) {
+	const runs = 200
+
+	for i := 0; i < runs; i++ {
+		key, err := GenerateKey()
+		if err != nil {
+			t.Fatalf("iteration %d: %v", i, err)
+		}
+		pubkey2, err := DecompressPubkey(CompressPubkey(&key.PublicKey))
+		if err != nil {
+			t.Fatalf("iteration %d: %v", i, err)
+		}
+		if !reflect.DeepEqual(key.PublicKey, *pubkey2) {
+			t.Fatalf("iteration %d: keys not equal", i)
+		}
+	}
+}
+
 func BenchmarkEcrecoverSignature(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		if _, err := Ecrecover(testmsg, testsig); err != nil {