crypto, crypto/secp256k1: add CompressPubkey

This commit is contained in:
Felix Lange 2017-12-07 13:26:59 +01:00
parent 516c83d593
commit f0199b83df
5 changed files with 66 additions and 16 deletions

View file

@ -74,26 +74,32 @@ static int secp256k1_ext_ecdsa_verify(
return secp256k1_ecdsa_verify(ctx, &sig, msgdata, &pubkey); return secp256k1_ecdsa_verify(ctx, &sig, msgdata, &pubkey);
} }
// secp256k1_ext_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 // Returns: 1: conversion successful
// 0: public key is invalid // 0: conversion unsuccessful
// Args: ctx: pointer to a context object (cannot be NULL) // Args: ctx: pointer to a context object (cannot be NULL)
// Out: pubkey_out: the serialized 65-byte public key (cannot be NULL) // Out: out: output buffer that will contain the reencoded key (cannot be NULL)
// In: pubkeydata: pointer to 33 bytes of compressed public key data (cannot be NULL) // In: outlen: length of out (33 for compressed keys, 65 for uncompressed keys)
static int secp256k1_ext_decompress_pubkey( // pubkeydata: the input public key (cannot be NULL)
// pubkeylen: length of pubkeydata
static int secp256k1_ext_reencode_pubkey(
const secp256k1_context* ctx, const secp256k1_context* ctx,
unsigned char *pubkey_out, unsigned char *out,
const unsigned char *pubkeydata size_t outlen,
const unsigned char *pubkeydata,
size_t pubkeylen
) { ) {
secp256k1_pubkey pubkey; secp256k1_pubkey pubkey;
if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeydata, 33)) { if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeydata, pubkeylen)) {
return 0; return 0;
} }
size_t outputlen = 65; unsigned int flag = (outlen == 33) ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED;
return secp256k1_ec_pubkey_serialize(ctx, pubkey_out, &outputlen, &pubkey, SECP256K1_EC_UNCOMPRESSED); return secp256k1_ec_pubkey_serialize(ctx, out, &outlen, &pubkey, flag);
} }i
// secp256k1_ext_scalar_mul multiplies a point by a scalar in constant time. // secp256k1_ext_scalar_mul multiplies a point by a scalar in constant time.
// //

View file

@ -139,15 +139,35 @@ func DecompressPubkey(pubkey []byte) (x, y *big.Int) {
if len(pubkey) != 33 { if len(pubkey) != 33 {
return nil, nil return nil, nil
} }
out := make([]byte, 65) var (
outdata := (*C.uchar)(unsafe.Pointer(&out[0])) pubkeydata = (*C.uchar)(unsafe.Pointer(&pubkey[0]))
pubkeydata := (*C.uchar)(unsafe.Pointer(&pubkey[0])) pubkeylen = C.size_t(len(pubkey))
if C.secp256k1_ext_decompress_pubkey(context, outdata, pubkeydata) == 0 { 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 nil, nil
} }
return new(big.Int).SetBytes(out[1:33]), new(big.Int).SetBytes(out[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 { func checkSignature(sig []byte) error {
if len(sig) != 65 { if len(sig) != 65 {
return ErrInvalidSignatureLen return ErrInvalidSignatureLen

View file

@ -76,6 +76,11 @@ func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
return &ecdsa.PublicKey{X: x, Y: y, Curve: S256()}, nil 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. // S256 returns an instance of the secp256k1 curve.
func S256() elliptic.Curve { func S256() elliptic.Curve {
return secp256k1.S256() return secp256k1.S256()

View file

@ -102,6 +102,11 @@ func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
return key.ToECDSA(), nil 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. // S256 returns an instance of the secp256k1 curve.
func S256() elliptic.Curve { func S256() elliptic.Curve {
return btcec.S256() return btcec.S256()

View file

@ -18,10 +18,12 @@ package crypto
import ( import (
"bytes" "bytes"
"crypto/ecdsa"
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
) )
var ( var (
@ -86,6 +88,18 @@ 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 BenchmarkEcrecoverSignature(b *testing.B) { func BenchmarkEcrecoverSignature(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
if _, err := Ecrecover(testmsg, testsig); err != nil { if _, err := Ecrecover(testmsg, testsig); err != nil {