Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 19 additions & 27 deletions crypto/secp256k1/curve.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,14 @@ package secp256k1
import (
"crypto/elliptic"
"math/big"
"sync"
"unsafe"

"github.com/ethereum/go-ethereum/common/math"
)

/*
#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"

Expand Down Expand Up @@ -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])
Expand All @@ -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
}

Expand All @@ -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
}
40 changes: 23 additions & 17 deletions crypto/secp256k1/ext.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ 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
// Args: ctx: pointer to a context object (cannot be NULL)
// 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,
Expand All @@ -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
Expand All @@ -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,
Expand All @@ -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)
Expand All @@ -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;
Expand Down
36 changes: 28 additions & 8 deletions crypto/secp256k1/secp256.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down
5 changes: 5 additions & 0 deletions crypto/signature_cgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
5 changes: 5 additions & 0 deletions crypto/signature_nocgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
38 changes: 38 additions & 0 deletions crypto/signature_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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) {
Expand All @@ -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 {
Expand Down