diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go
index f42605d32b..d4d3098494 100644
--- a/crypto/crypto_test.go
+++ b/crypto/crypto_test.go
@@ -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)
diff --git a/crypto/secp256k1/curve.go b/crypto/secp256k1/curve.go
index 6e44a6771f..ccc5f5ce27 100644
--- a/crypto/secp256k1/curve.go
+++ b/crypto/secp256k1/curve.go
@@ -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 {
diff --git a/crypto/secp256k1/ext.h b/crypto/secp256k1/ext.h
new file mode 100644
index 0000000000..ee759fde69
--- /dev/null
+++ b/crypto/secp256k1/ext.h
@@ -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 .
+
+// 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;
+}
diff --git a/crypto/secp256k1/notes.go b/crypto/secp256k1/notes.go
deleted file mode 100644
index 49fcf8e2d5..0000000000
--- a/crypto/secp256k1/notes.go
+++ /dev/null
@@ -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 .
-
-package secp256k1
-
-/*
- sipa, int secp256k1_ecdsa_pubkey_create(unsigned char *pubkey, int *pubkeylen, const unsigned char *seckey, int compressed);
- is that how i generate private/public keys?
- HaltingState: you pass in a random 32-byte string as seckey
- HaltingState: if it is valid, the corresponding pubkey is put in pubkey
- and true is returned
- otherwise, false is returned
- around 1 in 2^128 32-byte strings are invalid, so the odds of even ever seeing one is extremely rare
-
- private keys are mathematically numbers
- each has a corresponding point on the curve as public key
- a private key is just a number
- a public key is a point with x/y coordinates
- almost every 256-bit number is a valid private key (one with a point on the curve corresponding to it)
- HaltingState: ok?
-
- more than half of random points are not on the curve
- and actually, it is less than the square root, not less than half, sorry :)
-!!!
- a private key is a NUMBER
- a public key is a POINT
- half the x,y values in the field are not on the curve, a private key is an integer.
-
- 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
- that's the reason ECDH works
- multiplication is associative and commutativ
-*/
-
-/*
- 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
- because i know they can do that for the normal 72 byte signatures that openssl was putting out
- HaltingState: if you don't enforce non-malleability, yes
- HaltingState: if you force the highest bit of t
-
- it _creates_ signatures that already satisfy that condition
- but it will accept ones that don't
- maybe i should change that, and be strict
- yes; i want some way to know signature is valid but fails malleability
- well if the highest bit of S is 1, you can take its complement
- and end up with a valid signature
- that is canonical
-*/
-
-/*
-
- sipa, I am signing messages and highest bit of the compact signature is 1!!!
- if (b & 0x80) == 0x80 {
- log.Panic("b= %v b2= %v \n", b, b&0x80)
- }
- what bit?
-* Pengoo has quit (Ping timeout: 272 seconds)
- the highest bit of the first byte of signature
- it's the highest bit of S
- so the 32nd byte
- 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
-*/
-
-/*
- one thing I dont get about armory for instance,
-is how the hot-wallet can generate new addresses without
-knowing the master key
-*/
-
-/*
- 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
- ...
-
- You know that the *r curves are selected via an undisclosed
-secret process, right?
- 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);
-*/
diff --git a/crypto/secp256k1/pubkey_scalar_mul.h b/crypto/secp256k1/pubkey_scalar_mul.h
deleted file mode 100644
index 0511545ec0..0000000000
--- a/crypto/secp256k1/pubkey_scalar_mul.h
+++ /dev/null
@@ -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 .
-
-/** 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;
-}
-
diff --git a/crypto/secp256k1/secp256.go b/crypto/secp256k1/secp256.go
index 2c5f614504..ffa186108d 100644
--- a/crypto/secp256k1/secp256.go
+++ b/crypto/secp256k1/secp256.go
@@ -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 .
+// 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 {
diff --git a/crypto/secp256k1/secp256_test.go b/crypto/secp256k1/secp256_test.go
index a35e04e742..ec28b8e39e 100644
--- a/crypto/secp256k1/secp256_test.go
+++ b/crypto/secp256k1/secp256_test.go
@@ -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()