crypto/secp256k1: adapt to new libsecp256k1 API

- Use defined constants instead of hard-coding their integer value.
- Allocate secp256k1 structs on the C stack instead of converting []byte
- Remove dead code
This commit is contained in:
Felix Lange 2017-01-10 16:53:36 +01:00
parent e0fc30dbe4
commit 6a2c12a2dc
7 changed files with 171 additions and 502 deletions

View file

@ -72,14 +72,6 @@ func BenchmarkSha3(b *testing.B) {
fmt.Println(amount, ":", time.Since(start))
}
func Test0Key(t *testing.T) {
key := common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")
_, err := secp256k1.GeneratePubKey(key)
if err == nil {
t.Errorf("expected error due to zero privkey")
}
}
func TestSign(t *testing.T) {
key, _ := HexToECDSA(testPrivHex)
addr := common.HexToAddress(testAddrHex)

View file

@ -33,7 +33,6 @@ package secp256k1
import (
"crypto/elliptic"
"io"
"math/big"
"sync"
"unsafe"
@ -257,31 +256,6 @@ func (BitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
return BitCurve.ScalarMult(BitCurve.Gx, BitCurve.Gy, k)
}
var mask = []byte{0xff, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f}
//TODO: double check if it is okay
// GenerateKey returns a public/private key pair. The private key is generated
// using the given reader, which must return random data.
func (BitCurve *BitCurve) GenerateKey(rand io.Reader) (priv []byte, x, y *big.Int, err error) {
byteLen := (BitCurve.BitSize + 7) >> 3
priv = make([]byte, byteLen)
for x == nil {
_, err = io.ReadFull(rand, priv)
if err != nil {
return
}
// We have to mask off any excess bits in the case that the size of the
// underlying field is not a whole number of bytes.
priv[0] &= mask[BitCurve.BitSize%8]
// This is because, in tests, rand will return all zeros and we don't
// want to get the point at infinity and loop forever.
priv[1] ^= 0x42
x, y = BitCurve.ScalarBaseMult(priv)
}
return
}
// Marshal converts a point into the form specified in section 4.3.6 of ANSI
// X9.62.
func (BitCurve *BitCurve) Marshal(x, y *big.Int) []byte {

87
crypto/secp256k1/ext.h Normal file
View file

@ -0,0 +1,87 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// secp256k1_context_create_sign_verify creates a context for signing and signature verification.
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.
//
// 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(
const secp256k1_context* ctx,
unsigned char *pubkey_out,
const unsigned char *sigdata,
const unsigned char *msgdata
) {
secp256k1_ecdsa_recoverable_signature sig;
secp256k1_pubkey pubkey;
if (!secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &sig, sigdata, (int)sigdata[64])) {
return 0;
}
if (!secp256k1_ecdsa_recover(ctx, &pubkey, &sig, msgdata)) {
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
// 0: scalar was invalid (zero or overflow)
// Args: ctx: pointer to a context object (cannot be NULL)
// Out: point: the multiplied point (usually secret)
// 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 ret = 0;
int overflow = 0;
secp256k1_fe feX, feY;
secp256k1_gej res;
secp256k1_ge ge;
secp256k1_scalar s;
ARG_CHECK(point != NULL);
ARG_CHECK(scalar != NULL);
(void)ctx;
secp256k1_fe_set_b32(&feX, point);
secp256k1_fe_set_b32(&feY, point+32);
secp256k1_ge_set_xy(&ge, &feX, &feY);
secp256k1_scalar_set_b32(&s, scalar, &overflow);
if (overflow || secp256k1_scalar_is_zero(&s)) {
ret = 0;
} else {
secp256k1_ecmult_const(&res, &ge, &s);
secp256k1_ge_set_gej(&ge, &res);
/* Note: can't use secp256k1_pubkey_save here because it is not constant time. */
secp256k1_fe_normalize(&ge.x);
secp256k1_fe_normalize(&ge.y);
secp256k1_fe_get_b32(point, &ge.x);
secp256k1_fe_get_b32(point+32, &ge.y);
ret = 1;
}
secp256k1_scalar_clear(&s);
return ret;
}

View file

@ -1,208 +0,0 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package secp256k1
/*
<HaltingState> sipa, int secp256k1_ecdsa_pubkey_create(unsigned char *pubkey, int *pubkeylen, const unsigned char *seckey, int compressed);
<HaltingState> is that how i generate private/public keys?
<sipa> HaltingState: you pass in a random 32-byte string as seckey
<sipa> HaltingState: if it is valid, the corresponding pubkey is put in pubkey
<sipa> and true is returned
<sipa> otherwise, false is returned
<sipa> around 1 in 2^128 32-byte strings are invalid, so the odds of even ever seeing one is extremely rare
<sipa> private keys are mathematically numbers
<sipa> each has a corresponding point on the curve as public key
<sipa> a private key is just a number
<sipa> a public key is a point with x/y coordinates
<sipa> almost every 256-bit number is a valid private key (one with a point on the curve corresponding to it)
<sipa> HaltingState: ok?
<sipa> more than half of random points are not on the curve
<sipa> and actually, it is less than the square root, not less than half, sorry :)
!!!
<sipa> a private key is a NUMBER
<sipa> a public key is a POINT
<gmaxwell> half the x,y values in the field are not on the curve, a private key is an integer.
<sipa> HaltingState: yes, n,q = private keys; N,Q = corresponding public keys (N=n*G, Q=q*G); then it follows that n*Q = n*q*G = q*n*G = q*N
<sipa> that's the reason ECDH works
<sipa> multiplication is associative and commutativ
*/
/*
<HaltingState> sipa, ok; i am doing compact signatures and I want to know; can someone change the signature to get another valid signature for same message without the private key
<HaltingState> because i know they can do that for the normal 72 byte signatures that openssl was putting out
<sipa> HaltingState: if you don't enforce non-malleability, yes
<sipa> HaltingState: if you force the highest bit of t
<sipa> it _creates_ signatures that already satisfy that condition
<sipa> but it will accept ones that don't
<sipa> maybe i should change that, and be strict
<HaltingState> yes; i want some way to know signature is valid but fails malleability
<sipa> well if the highest bit of S is 1, you can take its complement
<sipa> and end up with a valid signature
<sipa> that is canonical
*/
/*
<HaltingState> sipa, I am signing messages and highest bit of the compact signature is 1!!!
<HaltingState> if (b & 0x80) == 0x80 {
<HaltingState> log.Panic("b= %v b2= %v \n", b, b&0x80)
<HaltingState> }
<sipa> what bit?
* Pengoo has quit (Ping timeout: 272 seconds)
<HaltingState> the highest bit of the first byte of signature
<sipa> it's the highest bit of S
<sipa> so the 32nd byte
<HaltingState> wtf
*/
/*
For instance, nonces are used in HTTP digest access authentication to calculate an MD5 digest
of the password. The nonces are different each time the 401 authentication challenge
response code is presented, thus making replay attacks virtually impossible.
can verify client/server match without sending password over network
*/
/*
<hanihani> one thing I dont get about armory for instance,
is how the hot-wallet can generate new addresses without
knowing the master key
*/
/*
<HaltingState> i am yelling at the telehash people for using secp256r1
instead of secp256k1; they thing r1 is "more secure" despite fact that
there is no implementation that works and wrapping it is now taking
up massive time, lol
<gmaxwell> ...
<gmaxwell> You know that the *r curves are selected via an undisclosed
secret process, right?
<gmaxwell> HaltingState: telehash is offtopic for this channel.
*/
/*
For instance, nonces are used in HTTP digest access authentication to calculate an MD5 digest
of the password. The nonces are different each time the 401 authentication challenge
response code is presented, thus making replay attacks virtually impossible.
can verify client/server match without sending password over network
*/
/*
void secp256k1_start(void);
void secp256k1_stop(void);
* Verify an ECDSA signature.
* Returns: 1: correct signature
* 0: incorrect signature
* -1: invalid public key
* -2: invalid signature
*
int secp256k1_ecdsa_verify(const unsigned char *msg, int msglen,
const unsigned char *sig, int siglen,
const unsigned char *pubkey, int pubkeylen);
http://www.nilsschneider.net/2013/01/28/recovering-bitcoin-private-keys.html
Why did this work? ECDSA requires a random number for each signature. If this random
number is ever used twice with the same private key it can be recovered.
This transaction was generated by a hardware bitcoin wallet using a pseudo-random number
generator that was returning the same random number every time.
Nonce is 32 bytes?
* Create an ECDSA signature.
* Returns: 1: signature created
* 0: nonce invalid, try another one
* In: msg: the message being signed
* msglen: the length of the message being signed
* seckey: pointer to a 32-byte secret key (assumed to be valid)
* nonce: pointer to a 32-byte nonce (generated with a cryptographic PRNG)
* Out: sig: pointer to a 72-byte array where the signature will be placed.
* siglen: pointer to an int, which will be updated to the signature length (<=72).
*
int secp256k1_ecdsa_sign(const unsigned char *msg, int msglen,
unsigned char *sig, int *siglen,
const unsigned char *seckey,
const unsigned char *nonce);
* Create a compact ECDSA signature (64 byte + recovery id).
* Returns: 1: signature created
* 0: nonce invalid, try another one
* In: msg: the message being signed
* msglen: the length of the message being signed
* seckey: pointer to a 32-byte secret key (assumed to be valid)
* nonce: pointer to a 32-byte nonce (generated with a cryptographic PRNG)
* Out: sig: pointer to a 64-byte array where the signature will be placed.
* recid: pointer to an int, which will be updated to contain the recovery id.
*
int secp256k1_ecdsa_sign_compact(const unsigned char *msg, int msglen,
unsigned char *sig64,
const unsigned char *seckey,
const unsigned char *nonce,
int *recid);
* Recover an ECDSA public key from a compact signature.
* Returns: 1: public key successfully recovered (which guarantees a correct signature).
* 0: otherwise.
* In: msg: the message assumed to be signed
* msglen: the length of the message
* compressed: whether to recover a compressed or uncompressed pubkey
* recid: the recovery id (as returned by ecdsa_sign_compact)
* Out: pubkey: pointer to a 33 or 65 byte array to put the pubkey.
* pubkeylen: pointer to an int that will contain the pubkey length.
*
recovery id is between 0 and 3
int secp256k1_ecdsa_recover_compact(const unsigned char *msg, int msglen,
const unsigned char *sig64,
unsigned char *pubkey, int *pubkeylen,
int compressed, int recid);
* Verify an ECDSA secret key.
* Returns: 1: secret key is valid
* 0: secret key is invalid
* In: seckey: pointer to a 32-byte secret key
*
int secp256k1_ecdsa_seckey_verify(const unsigned char *seckey);
** Just validate a public key.
* Returns: 1: valid public key
* 0: invalid public key
*
int secp256k1_ecdsa_pubkey_verify(const unsigned char *pubkey, int pubkeylen);
** Compute the public key for a secret key.
* In: compressed: whether the computed public key should be compressed
* seckey: pointer to a 32-byte private key.
* Out: pubkey: pointer to a 33-byte (if compressed) or 65-byte (if uncompressed)
* area to store the public key.
* pubkeylen: pointer to int that will be updated to contains the pubkey's
* length.
* Returns: 1: secret was valid, public key stores
* 0: secret was invalid, try again.
*
int secp256k1_ecdsa_pubkey_create(unsigned char *pubkey, int *pubkeylen, const unsigned char *seckey, int compressed);
*/

View file

@ -1,56 +0,0 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
/** Multiply point by scalar in constant time.
* Returns: 1: multiplication was successful
* 0: scalar was invalid (zero or overflow)
* Args: ctx: pointer to a context object (cannot be NULL)
* Out: point: the multiplied point (usually secret)
* In: point: pointer to a 64-byte bytepublic 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 ret = 0;
int overflow = 0;
secp256k1_fe feX, feY;
secp256k1_gej res;
secp256k1_ge ge;
secp256k1_scalar s;
ARG_CHECK(point != NULL);
ARG_CHECK(scalar != NULL);
(void)ctx;
secp256k1_fe_set_b32(&feX, point);
secp256k1_fe_set_b32(&feY, point+32);
secp256k1_ge_set_xy(&ge, &feX, &feY);
secp256k1_scalar_set_b32(&s, scalar, &overflow);
if (overflow || secp256k1_scalar_is_zero(&s)) {
ret = 0;
} else {
secp256k1_ecmult_const(&res, &ge, &s);
secp256k1_ge_set_gej(&ge, &res);
/* Note: can't use secp256k1_pubkey_save here because it is not constant time. */
secp256k1_fe_normalize(&ge.x);
secp256k1_fe_normalize(&ge.y);
secp256k1_fe_get_b32(point, &ge.x);
secp256k1_fe_get_b32(point+32, &ge.y);
ret = 1;
}
secp256k1_scalar_clear(&s);
return ret;
}

View file

@ -14,10 +14,9 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package secp256k1 wraps the bitcoin secp256k1 C library.
package secp256k1
// TODO: set USE_SCALAR_4X64 depending on platform?
/*
#cgo CFLAGS: -I./libsecp256k1
#cgo CFLAGS: -I./libsecp256k1/src/
@ -29,7 +28,7 @@ package secp256k1
#define NDEBUG
#include "./libsecp256k1/src/secp256k1.c"
#include "./libsecp256k1/src/modules/recovery/main_impl.h"
#include "pubkey_scalar_mul.h"
#include "ext.h"
typedef void (*callbackFunc) (const char* msg, void* data);
extern void secp256k1GoPanicIllegal(const char* msg, void* data);
@ -45,16 +44,6 @@ import (
"github.com/ethereum/go-ethereum/crypto/randentropy"
)
//#define USE_FIELD_5X64
/*
TODO:
> store private keys in buffer and shuffle (deters persistence on swap disc)
> byte permutation (changing)
> xor with chaning random block (to deter scanning memory for 0x63) (stream cipher?)
*/
// holds ptr to secp256k1_context_struct (see secp256k1/include/secp256k1.h)
var (
context *C.secp256k1_context
N *big.Int
@ -67,127 +56,53 @@ func init() {
HalfN, _ = new(big.Int).SetString("7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", 16)
// around 20 ms on a modern CPU.
context = C.secp256k1_context_create(3) // SECP256K1_START_SIGN | SECP256K1_START_VERIFY
context = C.secp256k1_context_create_sign_verify()
C.secp256k1_context_set_illegal_callback(context, C.callbackFunc(C.secp256k1GoPanicIllegal), nil)
C.secp256k1_context_set_error_callback(context, C.callbackFunc(C.secp256k1GoPanicError), nil)
}
var (
ErrInvalidMsgLen = errors.New("invalid message length for signature recovery")
ErrInvalidMsgLen = errors.New("invalid message length, need 32 bytes")
ErrInvalidSignatureLen = errors.New("invalid signature length")
ErrInvalidRecoveryID = errors.New("invalid signature recovery id")
ErrInvalidKey = errors.New("invalid private key")
ErrSignFailed = errors.New("signing failed")
ErrRecoverFailed = errors.New("recovery failed")
)
func GenerateKeyPair() ([]byte, []byte) {
var seckey []byte = randentropy.GetEntropyCSPRNG(32)
var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
var pubkey64 []byte = make([]byte, 64) // secp256k1_pubkey
var pubkey65 []byte = make([]byte, 65) // 65 byte uncompressed pubkey
pubkey64_ptr := (*C.secp256k1_pubkey)(unsafe.Pointer(&pubkey64[0]))
pubkey65_ptr := (*C.uchar)(unsafe.Pointer(&pubkey65[0]))
ret := C.secp256k1_ec_pubkey_create(
context,
pubkey64_ptr,
seckey_ptr,
)
if ret != C.int(1) {
return GenerateKeyPair() // invalid secret, try again
}
var output_len C.size_t
C.secp256k1_ec_pubkey_serialize( // always returns 1
context,
pubkey65_ptr,
&output_len,
pubkey64_ptr,
0, // SECP256K1_EC_COMPRESSED
)
return pubkey65, seckey
}
func GeneratePubKey(seckey []byte) ([]byte, error) {
if err := VerifySeckeyValidity(seckey); err != nil {
return nil, err
}
var pubkey []byte = make([]byte, 64)
var pubkey_ptr *C.secp256k1_pubkey = (*C.secp256k1_pubkey)(unsafe.Pointer(&pubkey[0]))
var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
ret := C.secp256k1_ec_pubkey_create(
context,
pubkey_ptr,
seckey_ptr,
)
if ret != C.int(1) {
return nil, errors.New("Unable to generate pubkey from seckey")
}
return pubkey, nil
}
// Sign creates a recoverable ECDSA signature.
// The produced signature is in the 65-byte [R || S || V] format where V is 0 or 1.
func Sign(msg []byte, seckey []byte) ([]byte, error) {
msg_ptr := (*C.uchar)(unsafe.Pointer(&msg[0]))
seckey_ptr := (*C.uchar)(unsafe.Pointer(&seckey[0]))
sig := make([]byte, 65)
sig_ptr := (*C.secp256k1_ecdsa_recoverable_signature)(unsafe.Pointer(&sig[0]))
nonce := randentropy.GetEntropyCSPRNG(32)
ndata_ptr := unsafe.Pointer(&nonce[0])
noncefp_ptr := &(*C.secp256k1_nonce_function_default)
if C.secp256k1_ec_seckey_verify(context, seckey_ptr) != C.int(1) {
return nil, errors.New("Invalid secret key")
if len(msg) != 32 {
return nil, ErrInvalidMsgLen
}
ret := C.secp256k1_ecdsa_sign_recoverable(
context,
sig_ptr,
msg_ptr,
seckey_ptr,
noncefp_ptr,
ndata_ptr,
)
if ret == C.int(0) {
return Sign(msg, seckey) //invalid secret, try again
}
sig_serialized := make([]byte, 65)
sig_serialized_ptr := (*C.uchar)(unsafe.Pointer(&sig_serialized[0]))
var recid C.int
C.secp256k1_ecdsa_recoverable_signature_serialize_compact(
context,
sig_serialized_ptr, // 64 byte compact signature
&recid,
sig_ptr, // 65 byte "recoverable" signature
)
sig_serialized[64] = byte(int(recid)) // add back recid to get 65 bytes sig
return sig_serialized, nil
}
func VerifySeckeyValidity(seckey []byte) error {
if len(seckey) != 32 {
return errors.New("priv key is not 32 bytes")
return nil, ErrInvalidKey
}
var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
ret := C.secp256k1_ec_seckey_verify(context, seckey_ptr)
if int(ret) != 1 {
return errors.New("invalid seckey")
seckeydata := (*C.uchar)(unsafe.Pointer(&seckey[0]))
if C.secp256k1_ec_seckey_verify(context, seckeydata) != 1 {
return nil, ErrInvalidKey
}
return nil
var (
msgdata = (*C.uchar)(unsafe.Pointer(&msg[0]))
nonce = randentropy.GetEntropyCSPRNG(32)
noncefunc = &(*C.secp256k1_nonce_function_default)
noncefuncData = unsafe.Pointer(&nonce[0])
sigstruct C.secp256k1_ecdsa_recoverable_signature
)
if C.secp256k1_ecdsa_sign_recoverable(context, &sigstruct, msgdata, seckeydata, noncefunc, noncefuncData) == 0 {
return nil, ErrSignFailed
}
var (
sig = make([]byte, 65)
sigdata = (*C.uchar)(unsafe.Pointer(&sig[0]))
recid C.int
)
C.secp256k1_ecdsa_recoverable_signature_serialize_compact(context, sigdata, &recid, &sigstruct)
sig[64] = byte(recid) // add back recid to get 65 bytes sig
return sig, nil
}
// RecoverPubkey returns the the public key of the signer.
@ -202,49 +117,15 @@ func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
return nil, err
}
msg_ptr := (*C.uchar)(unsafe.Pointer(&msg[0]))
sig_ptr := (*C.uchar)(unsafe.Pointer(&sig[0]))
pubkey := make([]byte, 64)
/*
this slice is used for both the recoverable signature and the
resulting serialized pubkey (both types in libsecp256k1 are 65
bytes). this saves one allocation of 65 bytes, which is nice as
pubkey recovery is one bottleneck during load in Ethereum
*/
bytes65 := make([]byte, 65)
pubkey_ptr := (*C.secp256k1_pubkey)(unsafe.Pointer(&pubkey[0]))
recoverable_sig_ptr := (*C.secp256k1_ecdsa_recoverable_signature)(unsafe.Pointer(&bytes65[0]))
recid := C.int(sig[64])
ret := C.secp256k1_ecdsa_recoverable_signature_parse_compact(
context,
recoverable_sig_ptr,
sig_ptr,
recid)
if ret == C.int(0) {
return nil, errors.New("Failed to parse signature")
}
ret = C.secp256k1_ecdsa_recover(
context,
pubkey_ptr,
recoverable_sig_ptr,
msg_ptr,
var (
pubkey = make([]byte, 65)
sigdata = (*C.uchar)(unsafe.Pointer(&sig[0]))
msgdata = (*C.uchar)(unsafe.Pointer(&msg[0]))
)
if ret == C.int(0) {
return nil, errors.New("Failed to recover public key")
if C.secp256k1_ecdsa_recover_pubkey(context, (*C.uchar)(unsafe.Pointer(&pubkey[0])), sigdata, msgdata) == 0 {
return nil, ErrRecoverFailed
}
serialized_pubkey_ptr := (*C.uchar)(unsafe.Pointer(&bytes65[0]))
var output_len C.size_t
C.secp256k1_ec_pubkey_serialize( // always returns 1
context,
serialized_pubkey_ptr,
&output_len,
pubkey_ptr,
0, // SECP256K1_EC_COMPRESSED
)
return bytes65, nil
return pubkey, nil
}
func checkSignature(sig []byte) error {

View file

@ -18,6 +18,9 @@ package secp256k1
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"encoding/hex"
"testing"
@ -26,15 +29,41 @@ import (
const TestCount = 1000
func TestPrivkeyGenerate(t *testing.T) {
_, seckey := GenerateKeyPair()
if err := VerifySeckeyValidity(seckey); err != nil {
t.Errorf("seckey not valid: %s", err)
func generateKeyPair() (pubkey, privkey []byte) {
key, err := ecdsa.GenerateKey(S256(), rand.Reader)
if err != nil {
panic(err)
}
pubkey = elliptic.Marshal(S256(), key.X, key.Y)
privkey = make([]byte, 32)
readBits(privkey, key.D)
return pubkey, privkey
}
func randSig() []byte {
sig := randentropy.GetEntropyCSPRNG(65)
sig[32] &= 0x70
sig[64] %= 4
return sig
}
// tests for malleability
// highest bit of signature ECDSA s value must be 0, in the 33th byte
func compactSigCheck(t *testing.T, sig []byte) {
var b int = int(sig[32])
if b < 0 {
t.Errorf("highest bit is negative: %d", b)
}
if ((b >> 7) == 1) != ((b & 0x80) == 0x80) {
t.Errorf("highest bit: %d bit >> 7: %d", b, b>>7)
}
if (b & 0x80) == 0x80 {
t.Errorf("highest bit: %d bit & 0x80: %d", b, b&0x80)
}
}
func TestSignatureValidity(t *testing.T) {
pubkey, seckey := GenerateKeyPair()
pubkey, seckey := generateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32)
sig, err := Sign(msg, seckey)
if err != nil {
@ -57,7 +86,7 @@ func TestSignatureValidity(t *testing.T) {
}
func TestInvalidRecoveryID(t *testing.T) {
_, seckey := GenerateKeyPair()
_, seckey := generateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey)
sig[64] = 99
@ -68,7 +97,7 @@ func TestInvalidRecoveryID(t *testing.T) {
}
func TestSignAndRecover(t *testing.T) {
pubkey1, seckey := GenerateKeyPair()
pubkey1, seckey := generateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32)
sig, err := Sign(msg, seckey)
if err != nil {
@ -84,7 +113,7 @@ func TestSignAndRecover(t *testing.T) {
}
func TestRandomMessagesWithSameKey(t *testing.T) {
pubkey, seckey := GenerateKeyPair()
pubkey, seckey := generateKeyPair()
keys := func() ([]byte, []byte) {
return pubkey, seckey
}
@ -93,7 +122,7 @@ func TestRandomMessagesWithSameKey(t *testing.T) {
func TestRandomMessagesWithRandomKeys(t *testing.T) {
keys := func() ([]byte, []byte) {
pubkey, seckey := GenerateKeyPair()
pubkey, seckey := generateKeyPair()
return pubkey, seckey
}
signAndRecoverWithRandomMessages(t, keys)
@ -129,7 +158,7 @@ func signAndRecoverWithRandomMessages(t *testing.T, keys func() ([]byte, []byte)
}
func TestRecoveryOfRandomSignature(t *testing.T) {
pubkey1, _ := GenerateKeyPair()
pubkey1, _ := generateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32)
for i := 0; i < TestCount; i++ {
@ -141,15 +170,8 @@ func TestRecoveryOfRandomSignature(t *testing.T) {
}
}
func randSig() []byte {
sig := randentropy.GetEntropyCSPRNG(65)
sig[32] &= 0x70
sig[64] %= 4
return sig
}
func TestRandomMessagesAgainstValidSig(t *testing.T) {
pubkey1, seckey := GenerateKeyPair()
pubkey1, seckey := generateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey)
@ -163,14 +185,6 @@ func TestRandomMessagesAgainstValidSig(t *testing.T) {
}
}
func TestZeroPrivkey(t *testing.T) {
zeroedBytes := make([]byte, 32)
err := VerifySeckeyValidity(zeroedBytes)
if err == nil {
t.Errorf("zeroed bytes should have returned error")
}
}
// Useful when the underlying libsecp256k1 API changes to quickly
// check only recover function without use of signature function
func TestRecoverSanity(t *testing.T) {
@ -186,23 +200,8 @@ func TestRecoverSanity(t *testing.T) {
}
}
// tests for malleability
// highest bit of signature ECDSA s value must be 0, in the 33th byte
func compactSigCheck(t *testing.T, sig []byte) {
var b int = int(sig[32])
if b < 0 {
t.Errorf("highest bit is negative: %d", b)
}
if ((b >> 7) == 1) != ((b & 0x80) == 0x80) {
t.Errorf("highest bit: %d bit >> 7: %d", b, b>>7)
}
if (b & 0x80) == 0x80 {
t.Errorf("highest bit: %d bit & 0x80: %d", b, b&0x80)
}
}
func BenchmarkSign(b *testing.B) {
_, seckey := GenerateKeyPair()
_, seckey := generateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32)
b.ResetTimer()
@ -213,7 +212,7 @@ func BenchmarkSign(b *testing.B) {
func BenchmarkRecover(b *testing.B) {
msg := randentropy.GetEntropyCSPRNG(32)
_, seckey := GenerateKeyPair()
_, seckey := generateKeyPair()
sig, _ := Sign(msg, seckey)
b.ResetTimer()