mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 01:13:45 +00:00
crypto: add DecompressPubkey, VerifySignature
This commit is contained in:
parent
0a3a97022e
commit
8de2b8a47b
5 changed files with 195 additions and 8 deletions
|
|
@ -46,6 +46,55 @@ static int secp256k1_ecdsa_recover_pubkey(
|
||||||
return secp256k1_ec_pubkey_serialize(ctx, pubkey_out, &outputlen, &pubkey, SECP256K1_EC_UNCOMPRESSED);
|
return secp256k1_ec_pubkey_serialize(ctx, pubkey_out, &outputlen, &pubkey, SECP256K1_EC_UNCOMPRESSED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// secp256k1_ecdsa_verify_enc verifies an encoded compact signature.
|
||||||
|
//
|
||||||
|
// Returns: 1: signature is valid was successful
|
||||||
|
// 0: signature is invalid
|
||||||
|
// Args: ctx: pointer to a context object (cannot be NULL)
|
||||||
|
// In: sigdata: pointer to a 64-byte signature (cannot be NULL)
|
||||||
|
// 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(
|
||||||
|
const secp256k1_context* ctx,
|
||||||
|
const unsigned char *sigdata,
|
||||||
|
const unsigned char *msgdata,
|
||||||
|
const unsigned char *pubkeydata,
|
||||||
|
size_t pubkeylen
|
||||||
|
) {
|
||||||
|
secp256k1_ecdsa_signature sig;
|
||||||
|
secp256k1_pubkey pubkey;
|
||||||
|
|
||||||
|
if (!secp256k1_ecdsa_signature_parse_compact(ctx, &sig, sigdata)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeydata, pubkeylen)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return secp256k1_ecdsa_verify(ctx, &sig, msgdata, &pubkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// secp256k1_decompress_pubkey decompresses a public key.
|
||||||
|
//
|
||||||
|
// Returns: 1: public key is valid
|
||||||
|
// 0: public key is invalid
|
||||||
|
// 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(
|
||||||
|
const secp256k1_context* ctx,
|
||||||
|
unsigned char *pubkey_out,
|
||||||
|
const unsigned char *pubkeydata
|
||||||
|
) {
|
||||||
|
secp256k1_pubkey pubkey;
|
||||||
|
|
||||||
|
if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeydata, 33)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size_t outputlen = 65;
|
||||||
|
return secp256k1_ec_pubkey_serialize(ctx, pubkey_out, &outputlen, &pubkey, SECP256K1_EC_UNCOMPRESSED);
|
||||||
|
}
|
||||||
|
|
||||||
// secp256k1_pubkey_scalar_mul multiplies a point by a scalar in constant time.
|
// secp256k1_pubkey_scalar_mul multiplies a point by a scalar in constant time.
|
||||||
//
|
//
|
||||||
// Returns: 1: multiplication was successful
|
// Returns: 1: multiplication was successful
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"math/big"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -55,6 +56,7 @@ var (
|
||||||
ErrInvalidSignatureLen = errors.New("invalid signature length")
|
ErrInvalidSignatureLen = errors.New("invalid signature length")
|
||||||
ErrInvalidRecoveryID = errors.New("invalid signature recovery id")
|
ErrInvalidRecoveryID = errors.New("invalid signature recovery id")
|
||||||
ErrInvalidKey = errors.New("invalid private key")
|
ErrInvalidKey = errors.New("invalid private key")
|
||||||
|
ErrInvalidPubkey = errors.New("invalid public key")
|
||||||
ErrSignFailed = errors.New("signing failed")
|
ErrSignFailed = errors.New("signing failed")
|
||||||
ErrRecoverFailed = errors.New("recovery failed")
|
ErrRecoverFailed = errors.New("recovery failed")
|
||||||
)
|
)
|
||||||
|
|
@ -119,6 +121,33 @@ func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
|
||||||
return pubkey, nil
|
return pubkey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VerifySignature checks that the given pubkey created signature over message.
|
||||||
|
// The signature should be in [R || S] format.
|
||||||
|
func VerifySignature(pubkey, msg, signature []byte) bool {
|
||||||
|
if len(msg) != 32 || len(signature) != 64 || len(pubkey) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
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 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return new(big.Int).SetBytes(buf[1:33]), new(big.Int).SetBytes(buf[33:])
|
||||||
|
}
|
||||||
|
|
||||||
func checkSignature(sig []byte) error {
|
func checkSignature(sig []byte) error {
|
||||||
if len(sig) != 65 {
|
if len(sig) != 65 {
|
||||||
return ErrInvalidSignatureLen
|
return ErrInvalidSignatureLen
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,12 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Ecrecover returns the public key that created the given signature.
|
||||||
func Ecrecover(hash, sig []byte) ([]byte, error) {
|
func Ecrecover(hash, sig []byte) ([]byte, error) {
|
||||||
return secp256k1.RecoverPubkey(hash, sig)
|
return secp256k1.RecoverPubkey(hash, sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SigToPub returns the public key that created the given signature.
|
||||||
func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
|
func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
|
||||||
s, err := Ecrecover(hash, sig)
|
s, err := Ecrecover(hash, sig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -58,6 +60,22 @@ func Sign(hash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
|
||||||
return secp256k1.Sign(hash, seckey)
|
return secp256k1.Sign(hash, seckey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VerifySignature checks that the given public key created signature over hash.
|
||||||
|
// The public key should be in compressed (33 bytes) or uncompressed (64 bytes) format.
|
||||||
|
// The signature should have the 64 byte [R || S] format.
|
||||||
|
func VerifySignature(pubkey, hash, signature []byte) bool {
|
||||||
|
return secp256k1.VerifySignature(pubkey, hash, signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecompressPubkey parses a public key in the 33-byte compressed format.
|
||||||
|
func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
|
||||||
|
x, y := secp256k1.DecompressPubkey(pubkey)
|
||||||
|
if x == nil {
|
||||||
|
return nil, fmt.Errorf("invalid public key")
|
||||||
|
}
|
||||||
|
return &ecdsa.PublicKey{X: x, Y: y, Curve: S256()}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// 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()
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,14 @@ package crypto
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcec"
|
"github.com/btcsuite/btcd/btcec"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Ecrecover returns the public key that created the given signature.
|
||||||
func Ecrecover(hash, sig []byte) ([]byte, error) {
|
func Ecrecover(hash, sig []byte) ([]byte, error) {
|
||||||
pub, err := SigToPub(hash, sig)
|
pub, err := SigToPub(hash, sig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -35,6 +38,7 @@ func Ecrecover(hash, sig []byte) ([]byte, error) {
|
||||||
return bytes, err
|
return bytes, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SigToPub returns the public key that created the given signature.
|
||||||
func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
|
func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
|
||||||
// Convert to btcec input format with 'recovery id' v at the beginning.
|
// Convert to btcec input format with 'recovery id' v at the beginning.
|
||||||
btcsig := make([]byte, 65)
|
btcsig := make([]byte, 65)
|
||||||
|
|
@ -71,6 +75,33 @@ func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) {
|
||||||
return sig, nil
|
return sig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VerifySignature checks that the given public key created signature over hash.
|
||||||
|
// The public key should be in compressed (33 bytes) or uncompressed (65 bytes) format.
|
||||||
|
// The signature should have the 64 byte [R || S] format.
|
||||||
|
func VerifySignature(pubkey, hash, signature []byte) bool {
|
||||||
|
if len(signature) != 64 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
sig := &btcec.Signature{R: new(big.Int).SetBytes(signature[:32]), S: new(big.Int).SetBytes(signature[32:])}
|
||||||
|
key, err := btcec.ParsePubKey(pubkey, btcec.S256())
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return sig.Verify(hash, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecompressPubkey parses a public key in the 33-byte compressed format.
|
||||||
|
func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
|
||||||
|
if len(pubkey) != 33 {
|
||||||
|
return nil, errors.New("invalid compressed public key length")
|
||||||
|
}
|
||||||
|
key, err := btcec.ParsePubKey(pubkey, btcec.S256())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return key.ToECDSA(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// 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()
|
||||||
|
|
|
||||||
|
|
@ -18,19 +18,79 @@ package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRecoverSanity(t *testing.T) {
|
var (
|
||||||
msg, _ := hex.DecodeString("ce0677bb30baa8cf067c88db9811f4333d131bf8bcf12fe7065d211dce971008")
|
testmsg = hexutil.MustDecode("0xce0677bb30baa8cf067c88db9811f4333d131bf8bcf12fe7065d211dce971008")
|
||||||
sig, _ := hex.DecodeString("90f27b8b488db00b00606796d2987f6a5f59ae62ea05effe84fef5b8b0e549984a691139ad57a3f0b906637673aa2f63d1f55cb1a69199d4009eea23ceaddc9301")
|
testsig = hexutil.MustDecode("0x90f27b8b488db00b00606796d2987f6a5f59ae62ea05effe84fef5b8b0e549984a691139ad57a3f0b906637673aa2f63d1f55cb1a69199d4009eea23ceaddc9301")
|
||||||
pubkey1, _ := hex.DecodeString("04e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a0a2b2667f7e725ceea70c673093bf67663e0312623c8e091b13cf2c0f11ef652")
|
testpubkey = hexutil.MustDecode("0x04e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a0a2b2667f7e725ceea70c673093bf67663e0312623c8e091b13cf2c0f11ef652")
|
||||||
pubkey2, err := Ecrecover(msg, sig)
|
testpubkeyc = hexutil.MustDecode("0x02e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a")
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEcrecover(t *testing.T) {
|
||||||
|
pubkey, err := Ecrecover(testmsg, testsig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("recover error: %s", err)
|
t.Fatalf("recover error: %s", err)
|
||||||
}
|
}
|
||||||
if !bytes.Equal(pubkey1, pubkey2) {
|
if !bytes.Equal(pubkey, testpubkey) {
|
||||||
t.Errorf("pubkey mismatch: want: %x have: %x", pubkey1, pubkey2)
|
t.Errorf("pubkey mismatch: want: %x have: %x", testpubkey, pubkey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVerifySignature(t *testing.T) {
|
||||||
|
sig := testsig[:len(testsig)-1] // remove recovery id
|
||||||
|
if !VerifySignature(testpubkey, testmsg, sig) {
|
||||||
|
t.Errorf("can't verify signature with uncompressed key")
|
||||||
|
}
|
||||||
|
if !VerifySignature(testpubkeyc, testmsg, sig) {
|
||||||
|
t.Errorf("can't verify signature with compressed key")
|
||||||
|
}
|
||||||
|
|
||||||
|
if VerifySignature(nil, testmsg, sig) {
|
||||||
|
t.Errorf("signature valid with no key")
|
||||||
|
}
|
||||||
|
if VerifySignature(testpubkey, nil, sig) {
|
||||||
|
t.Errorf("signature valid with no message")
|
||||||
|
}
|
||||||
|
if VerifySignature(testpubkey, testmsg, nil) {
|
||||||
|
t.Errorf("nil signature valid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecompressPubkey(t *testing.T) {
|
||||||
|
key, err := DecompressPubkey(testpubkeyc)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if uncompressed := FromECDSAPub(key); !bytes.Equal(uncompressed, testpubkey) {
|
||||||
|
t.Errorf("wrong public key result: got %x, want %x", uncompressed, testpubkey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEcrecoverSignature(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if _, err := Ecrecover(testmsg, testsig); err != nil {
|
||||||
|
b.Fatal("ecrecover error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkVerifySignature(b *testing.B) {
|
||||||
|
sig := testsig[:len(testsig)-1] // remove recovery id
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if !VerifySignature(testpubkey, testmsg, sig) {
|
||||||
|
b.Fatal("verify error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDecompressPubkey(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if _, err := DecompressPubkey(testpubkeyc); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue