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))
|
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) {
|
func TestSign(t *testing.T) {
|
||||||
key, _ := HexToECDSA(testPrivHex)
|
key, _ := HexToECDSA(testPrivHex)
|
||||||
addr := common.HexToAddress(testAddrHex)
|
addr := common.HexToAddress(testAddrHex)
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ package secp256k1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"io"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
@ -257,31 +256,6 @@ func (BitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
|
||||||
return BitCurve.ScalarMult(BitCurve.Gx, BitCurve.Gy, k)
|
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
|
// Marshal converts a point into the form specified in section 4.3.6 of ANSI
|
||||||
// X9.62.
|
// X9.62.
|
||||||
func (BitCurve *BitCurve) Marshal(x, y *big.Int) []byte {
|
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
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Package secp256k1 wraps the bitcoin secp256k1 C library.
|
||||||
package secp256k1
|
package secp256k1
|
||||||
|
|
||||||
// TODO: set USE_SCALAR_4X64 depending on platform?
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#cgo CFLAGS: -I./libsecp256k1
|
#cgo CFLAGS: -I./libsecp256k1
|
||||||
#cgo CFLAGS: -I./libsecp256k1/src/
|
#cgo CFLAGS: -I./libsecp256k1/src/
|
||||||
|
|
@ -29,7 +28,7 @@ package secp256k1
|
||||||
#define NDEBUG
|
#define NDEBUG
|
||||||
#include "./libsecp256k1/src/secp256k1.c"
|
#include "./libsecp256k1/src/secp256k1.c"
|
||||||
#include "./libsecp256k1/src/modules/recovery/main_impl.h"
|
#include "./libsecp256k1/src/modules/recovery/main_impl.h"
|
||||||
#include "pubkey_scalar_mul.h"
|
#include "ext.h"
|
||||||
|
|
||||||
typedef void (*callbackFunc) (const char* msg, void* data);
|
typedef void (*callbackFunc) (const char* msg, void* data);
|
||||||
extern void secp256k1GoPanicIllegal(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"
|
"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 (
|
var (
|
||||||
context *C.secp256k1_context
|
context *C.secp256k1_context
|
||||||
N *big.Int
|
N *big.Int
|
||||||
|
|
@ -67,127 +56,53 @@ func init() {
|
||||||
HalfN, _ = new(big.Int).SetString("7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", 16)
|
HalfN, _ = new(big.Int).SetString("7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", 16)
|
||||||
|
|
||||||
// around 20 ms on a modern CPU.
|
// 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_illegal_callback(context, C.callbackFunc(C.secp256k1GoPanicIllegal), nil)
|
||||||
C.secp256k1_context_set_error_callback(context, C.callbackFunc(C.secp256k1GoPanicError), nil)
|
C.secp256k1_context_set_error_callback(context, C.callbackFunc(C.secp256k1GoPanicError), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
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")
|
ErrInvalidSignatureLen = errors.New("invalid signature length")
|
||||||
ErrInvalidRecoveryID = errors.New("invalid signature recovery id")
|
ErrInvalidRecoveryID = errors.New("invalid signature recovery id")
|
||||||
|
ErrInvalidKey = errors.New("invalid private key")
|
||||||
|
ErrSignFailed = errors.New("signing failed")
|
||||||
|
ErrRecoverFailed = errors.New("recovery failed")
|
||||||
)
|
)
|
||||||
|
|
||||||
func GenerateKeyPair() ([]byte, []byte) {
|
// Sign creates a recoverable ECDSA signature.
|
||||||
var seckey []byte = randentropy.GetEntropyCSPRNG(32)
|
// The produced signature is in the 65-byte [R || S || V] format where V is 0 or 1.
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
func Sign(msg []byte, seckey []byte) ([]byte, error) {
|
func Sign(msg []byte, seckey []byte) ([]byte, error) {
|
||||||
msg_ptr := (*C.uchar)(unsafe.Pointer(&msg[0]))
|
if len(msg) != 32 {
|
||||||
seckey_ptr := (*C.uchar)(unsafe.Pointer(&seckey[0]))
|
return nil, ErrInvalidMsgLen
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
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]))
|
seckeydata := (*C.uchar)(unsafe.Pointer(&seckey[0]))
|
||||||
ret := C.secp256k1_ec_seckey_verify(context, seckey_ptr)
|
if C.secp256k1_ec_seckey_verify(context, seckeydata) != 1 {
|
||||||
if int(ret) != 1 {
|
return nil, ErrInvalidKey
|
||||||
return errors.New("invalid seckey")
|
|
||||||
}
|
}
|
||||||
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.
|
// RecoverPubkey returns the the public key of the signer.
|
||||||
|
|
@ -202,49 +117,15 @@ func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
msg_ptr := (*C.uchar)(unsafe.Pointer(&msg[0]))
|
var (
|
||||||
sig_ptr := (*C.uchar)(unsafe.Pointer(&sig[0]))
|
pubkey = make([]byte, 65)
|
||||||
pubkey := make([]byte, 64)
|
sigdata = (*C.uchar)(unsafe.Pointer(&sig[0]))
|
||||||
/*
|
msgdata = (*C.uchar)(unsafe.Pointer(&msg[0]))
|
||||||
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,
|
|
||||||
)
|
)
|
||||||
if ret == C.int(0) {
|
if C.secp256k1_ecdsa_recover_pubkey(context, (*C.uchar)(unsafe.Pointer(&pubkey[0])), sigdata, msgdata) == 0 {
|
||||||
return nil, errors.New("Failed to recover public key")
|
return nil, ErrRecoverFailed
|
||||||
}
|
}
|
||||||
|
return pubkey, nil
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkSignature(sig []byte) error {
|
func checkSignature(sig []byte) error {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,9 @@ package secp256k1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -26,15 +29,41 @@ import (
|
||||||
|
|
||||||
const TestCount = 1000
|
const TestCount = 1000
|
||||||
|
|
||||||
func TestPrivkeyGenerate(t *testing.T) {
|
func generateKeyPair() (pubkey, privkey []byte) {
|
||||||
_, seckey := GenerateKeyPair()
|
key, err := ecdsa.GenerateKey(S256(), rand.Reader)
|
||||||
if err := VerifySeckeyValidity(seckey); err != nil {
|
if err != nil {
|
||||||
t.Errorf("seckey not valid: %s", err)
|
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) {
|
func TestSignatureValidity(t *testing.T) {
|
||||||
pubkey, seckey := GenerateKeyPair()
|
pubkey, seckey := generateKeyPair()
|
||||||
msg := randentropy.GetEntropyCSPRNG(32)
|
msg := randentropy.GetEntropyCSPRNG(32)
|
||||||
sig, err := Sign(msg, seckey)
|
sig, err := Sign(msg, seckey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -57,7 +86,7 @@ func TestSignatureValidity(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidRecoveryID(t *testing.T) {
|
func TestInvalidRecoveryID(t *testing.T) {
|
||||||
_, seckey := GenerateKeyPair()
|
_, seckey := generateKeyPair()
|
||||||
msg := randentropy.GetEntropyCSPRNG(32)
|
msg := randentropy.GetEntropyCSPRNG(32)
|
||||||
sig, _ := Sign(msg, seckey)
|
sig, _ := Sign(msg, seckey)
|
||||||
sig[64] = 99
|
sig[64] = 99
|
||||||
|
|
@ -68,7 +97,7 @@ func TestInvalidRecoveryID(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSignAndRecover(t *testing.T) {
|
func TestSignAndRecover(t *testing.T) {
|
||||||
pubkey1, seckey := GenerateKeyPair()
|
pubkey1, seckey := generateKeyPair()
|
||||||
msg := randentropy.GetEntropyCSPRNG(32)
|
msg := randentropy.GetEntropyCSPRNG(32)
|
||||||
sig, err := Sign(msg, seckey)
|
sig, err := Sign(msg, seckey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -84,7 +113,7 @@ func TestSignAndRecover(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRandomMessagesWithSameKey(t *testing.T) {
|
func TestRandomMessagesWithSameKey(t *testing.T) {
|
||||||
pubkey, seckey := GenerateKeyPair()
|
pubkey, seckey := generateKeyPair()
|
||||||
keys := func() ([]byte, []byte) {
|
keys := func() ([]byte, []byte) {
|
||||||
return pubkey, seckey
|
return pubkey, seckey
|
||||||
}
|
}
|
||||||
|
|
@ -93,7 +122,7 @@ func TestRandomMessagesWithSameKey(t *testing.T) {
|
||||||
|
|
||||||
func TestRandomMessagesWithRandomKeys(t *testing.T) {
|
func TestRandomMessagesWithRandomKeys(t *testing.T) {
|
||||||
keys := func() ([]byte, []byte) {
|
keys := func() ([]byte, []byte) {
|
||||||
pubkey, seckey := GenerateKeyPair()
|
pubkey, seckey := generateKeyPair()
|
||||||
return pubkey, seckey
|
return pubkey, seckey
|
||||||
}
|
}
|
||||||
signAndRecoverWithRandomMessages(t, keys)
|
signAndRecoverWithRandomMessages(t, keys)
|
||||||
|
|
@ -129,7 +158,7 @@ func signAndRecoverWithRandomMessages(t *testing.T, keys func() ([]byte, []byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRecoveryOfRandomSignature(t *testing.T) {
|
func TestRecoveryOfRandomSignature(t *testing.T) {
|
||||||
pubkey1, _ := GenerateKeyPair()
|
pubkey1, _ := generateKeyPair()
|
||||||
msg := randentropy.GetEntropyCSPRNG(32)
|
msg := randentropy.GetEntropyCSPRNG(32)
|
||||||
|
|
||||||
for i := 0; i < TestCount; i++ {
|
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) {
|
func TestRandomMessagesAgainstValidSig(t *testing.T) {
|
||||||
pubkey1, seckey := GenerateKeyPair()
|
pubkey1, seckey := generateKeyPair()
|
||||||
msg := randentropy.GetEntropyCSPRNG(32)
|
msg := randentropy.GetEntropyCSPRNG(32)
|
||||||
sig, _ := Sign(msg, seckey)
|
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
|
// Useful when the underlying libsecp256k1 API changes to quickly
|
||||||
// check only recover function without use of signature function
|
// check only recover function without use of signature function
|
||||||
func TestRecoverSanity(t *testing.T) {
|
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) {
|
func BenchmarkSign(b *testing.B) {
|
||||||
_, seckey := GenerateKeyPair()
|
_, seckey := generateKeyPair()
|
||||||
msg := randentropy.GetEntropyCSPRNG(32)
|
msg := randentropy.GetEntropyCSPRNG(32)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
|
|
@ -213,7 +212,7 @@ func BenchmarkSign(b *testing.B) {
|
||||||
|
|
||||||
func BenchmarkRecover(b *testing.B) {
|
func BenchmarkRecover(b *testing.B) {
|
||||||
msg := randentropy.GetEntropyCSPRNG(32)
|
msg := randentropy.GetEntropyCSPRNG(32)
|
||||||
_, seckey := GenerateKeyPair()
|
_, seckey := generateKeyPair()
|
||||||
sig, _ := Sign(msg, seckey)
|
sig, _ := Sign(msg, seckey)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue