mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 14:46:42 +00:00
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:
parent
e0fc30dbe4
commit
6a2c12a2dc
7 changed files with 171 additions and 502 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
87
crypto/secp256k1/ext.h
Normal 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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
*/
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue