crypto: add DecompressPubkey, VerifySignature (#15615)

We need those operations for p2p/enr.

Also upgrade github.com/btcsuite/btcd/btcec to the latest version
and improve BenchmarkSha3. The benchmark printed extra output 
that confused tools like benchstat and ignored N.
This commit is contained in:
Felix Lange
2017-12-06 16:07:08 +01:00
committed by GitHub
parent 6e613cf3de
commit e85b68ef53
14 changed files with 326 additions and 229 deletions

View File

@ -46,6 +46,55 @@ 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.
//
// Returns: 1: signature is valid
// 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.
//
// Returns: 1: multiplication was successful

View File

@ -38,6 +38,7 @@ import "C"
import (
"errors"
"math/big"
"unsafe"
)
@ -55,6 +56,7 @@ var (
ErrInvalidSignatureLen = errors.New("invalid signature length")
ErrInvalidRecoveryID = errors.New("invalid signature recovery id")
ErrInvalidKey = errors.New("invalid private key")
ErrInvalidPubkey = errors.New("invalid public key")
ErrSignFailed = errors.New("signing failed")
ErrRecoverFailed = errors.New("recovery failed")
)
@ -119,6 +121,33 @@ func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
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 {
if len(sig) != 65 {
return ErrInvalidSignatureLen