core/secp256k1: update libsecp256k1 Go wrapper and tests

This commit is contained in:
Gustav Simonsson 2015-09-28 11:19:23 +02:00
parent 1d20b0247c
commit f32fa075f1
3 changed files with 265 additions and 258 deletions

View File

@ -198,7 +198,9 @@ func Sign(hash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash)) return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash))
} }
sig, err = secp256k1.Sign(hash, common.LeftPadBytes(prv.D.Bytes(), prv.Params().BitSize/8)) seckey := common.LeftPadBytes(prv.D.Bytes(), prv.Params().BitSize/8)
defer zeroBytes(seckey)
sig, err = secp256k1.Sign(hash, seckey)
return return
} }
@ -337,3 +339,9 @@ func PubkeyToAddress(p ecdsa.PublicKey) common.Address {
pubBytes := FromECDSAPub(&p) pubBytes := FromECDSAPub(&p)
return common.BytesToAddress(Sha3(pubBytes[1:])[12:]) return common.BytesToAddress(Sha3(pubBytes[1:])[12:])
} }
func zeroBytes(bytes []byte) {
for i := range bytes {
bytes[i] = 0
}
}

View File

@ -19,7 +19,7 @@ package secp256k1
// TODO: set USE_SCALAR_4X64 depending on platform? // TODO: set USE_SCALAR_4X64 depending on platform?
/* /*
#cgo CFLAGS: -I./secp256k1 #cgo CFLAGS: -I./libsecp256k1
#cgo darwin CFLAGS: -I/usr/local/include #cgo darwin CFLAGS: -I/usr/local/include
#cgo freebsd CFLAGS: -I/usr/local/include #cgo freebsd CFLAGS: -I/usr/local/include
#cgo linux,arm CFLAGS: -I/usr/local/arm/include #cgo linux,arm CFLAGS: -I/usr/local/arm/include
@ -33,7 +33,8 @@ package secp256k1
#define USE_SCALAR_8X32 #define USE_SCALAR_8X32
#define USE_SCALAR_INV_BUILTIN #define USE_SCALAR_INV_BUILTIN
#define NDEBUG #define NDEBUG
#include "./secp256k1/src/secp256k1.c" #include "./libsecp256k1/src/secp256k1.c"
#include "./libsecp256k1/src/modules/recovery/main_impl.h"
*/ */
import "C" import "C"
@ -48,48 +49,51 @@ import (
//#define USE_FIELD_5X64 //#define USE_FIELD_5X64
/* /*
Todo: TODO:
> Centralize key management in module
> add pubkey/private key struct
> Dont let keys leave module; address keys as ints
> store private keys in buffer and shuffle (deters persistance on swap disc) > store private keys in buffer and shuffle (deters persistance on swap disc)
> Byte permutation (changing) > byte permutation (changing)
> xor with chaning random block (to deter scanning memory for 0x63) (stream cipher?) > xor with chaning random block (to deter scanning memory for 0x63) (stream cipher?)
> on disk: store keys in wallets
On Disk
> Store keys in wallets
> use slow key derivation function for wallet encryption key (2 seconds)
*/ */
func init() { // holds ptr to secp256k1_context_struct (see secp256k1/include/secp256k1.h)
//takes 10ms to 100ms var context *C.secp256k1_context
C.secp256k1_start(3) // SECP256K1_START_SIGN | SECP256K1_START_VERIFY
}
func Stop() { func init() {
C.secp256k1_stop() // around 20 ms on a modern CPU.
context = C.secp256k1_context_create(3) // SECP256K1_START_SIGN | SECP256K1_START_VERIFY
} }
func GenerateKeyPair() ([]byte, []byte) { func GenerateKeyPair() ([]byte, []byte) {
var seckey []byte = randentropy.GetEntropyCSPRNG(32)
pubkey_len := C.int(65)
const seckey_len = 32
var pubkey []byte = make([]byte, pubkey_len)
var seckey []byte = randentropy.GetEntropyCSPRNG(seckey_len)
var pubkey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&pubkey[0]))
var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0])) 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( ret := C.secp256k1_ec_pubkey_create(
pubkey_ptr, &pubkey_len, context,
seckey_ptr, 0) pubkey64_ptr,
seckey_ptr,
)
if ret != C.int(1) { if ret != C.int(1) {
return GenerateKeyPair() //invalid secret, try again return GenerateKeyPair() // invalid secret, try again
} }
return pubkey, seckey
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) { func GeneratePubKey(seckey []byte) ([]byte, error) {
@ -97,17 +101,16 @@ func GeneratePubKey(seckey []byte) ([]byte, error) {
return nil, err return nil, err
} }
pubkey_len := C.int(65) var pubkey []byte = make([]byte, 64)
const seckey_len = 32 var pubkey_ptr *C.secp256k1_pubkey = (*C.secp256k1_pubkey)(unsafe.Pointer(&pubkey[0]))
var pubkey []byte = make([]byte, pubkey_len)
var pubkey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&pubkey[0]))
var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0])) var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
ret := C.secp256k1_ec_pubkey_create( ret := C.secp256k1_ec_pubkey_create(
pubkey_ptr, &pubkey_len, context,
seckey_ptr, 0) pubkey_ptr,
seckey_ptr,
)
if ret != C.int(1) { if ret != C.int(1) {
return nil, errors.New("Unable to generate pubkey from seckey") return nil, errors.New("Unable to generate pubkey from seckey")
@ -117,38 +120,48 @@ func GeneratePubKey(seckey []byte) ([]byte, error) {
} }
func Sign(msg []byte, seckey []byte) ([]byte, error) { 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) nonce := randentropy.GetEntropyCSPRNG(32)
ndata_ptr := unsafe.Pointer(&nonce[0])
var sig []byte = make([]byte, 65) noncefp_ptr := &(*C.secp256k1_nonce_function_default)
var recid C.int
var msg_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&msg[0])) if C.secp256k1_ec_seckey_verify(context, seckey_ptr) != C.int(1) {
var sig_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&sig[0]))
var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
var noncefp_ptr = &(*C.secp256k1_nonce_function_default)
var ndata_ptr = unsafe.Pointer(&nonce[0])
if C.secp256k1_ec_seckey_verify(seckey_ptr) != C.int(1) {
return nil, errors.New("Invalid secret key") return nil, errors.New("Invalid secret key")
} }
ret := C.secp256k1_ecdsa_sign_compact( ret := C.secp256k1_ecdsa_sign_recoverable(
msg_ptr, context,
sig_ptr, sig_ptr,
msg_ptr,
seckey_ptr, seckey_ptr,
noncefp_ptr, noncefp_ptr,
ndata_ptr, ndata_ptr,
&recid) )
sig[64] = byte(int(recid)) if ret == C.int(0) {
return Sign(msg, seckey) //invalid secret, try again
if ret != C.int(1) {
// nonce invalid, retry
return Sign(msg, seckey)
} }
return sig, nil 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
} }
@ -157,26 +170,13 @@ func VerifySeckeyValidity(seckey []byte) error {
return errors.New("priv key is not 32 bytes") return errors.New("priv key is not 32 bytes")
} }
var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0])) var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
ret := C.secp256k1_ec_seckey_verify(seckey_ptr) ret := C.secp256k1_ec_seckey_verify(context, seckey_ptr)
if int(ret) != 1 { if int(ret) != 1 {
return errors.New("invalid seckey") return errors.New("invalid seckey")
} }
return nil return nil
} }
func VerifyPubkeyValidity(pubkey []byte) error {
if len(pubkey) != 65 {
return errors.New("pub key is not 65 bytes")
}
var pubkey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&pubkey[0]))
ret := C.secp256k1_ec_pubkey_verify(pubkey_ptr, 65)
if int(ret) != 1 {
return errors.New("invalid pubkey")
}
return nil
}
func VerifySignatureValidity(sig []byte) bool { func VerifySignatureValidity(sig []byte) bool {
//64+1 //64+1
if len(sig) != 65 { if len(sig) != 65 {
@ -231,36 +231,58 @@ func VerifySignature(msg []byte, sig []byte, pubkey1 []byte) error {
return nil return nil
} }
//recovers the public key from the signature // recovers a public key from the signature
//recovery of pubkey means correct signature
func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) { func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
if len(sig) != 65 { if len(sig) != 65 {
return nil, errors.New("Invalid signature length") return nil, errors.New("Invalid signature length")
} }
var pubkey []byte = make([]byte, 65) msg_ptr := (*C.uchar)(unsafe.Pointer(&msg[0]))
sig_ptr := (*C.uchar)(unsafe.Pointer(&sig[0]))
var msg_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&msg[0])) pubkey := make([]byte, 64)
var sig_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&sig[0])) /*
var pubkey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&pubkey[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)
var pubkeylen C.int pubkey_ptr := (*C.secp256k1_pubkey)(unsafe.Pointer(&pubkey[0]))
recoverable_sig_ptr := (*C.secp256k1_ecdsa_recoverable_signature)(unsafe.Pointer(&bytes65[0]))
ret := C.secp256k1_ecdsa_recover_compact( recid := C.int(sig[64])
msg_ptr, ret := C.secp256k1_ecdsa_recoverable_signature_parse_compact(
context,
recoverable_sig_ptr,
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, pubkey_ptr,
&pubkeylen, recoverable_sig_ptr,
C.int(0), msg_ptr,
C.int(sig[64]),
) )
if ret == C.int(0) { if ret == C.int(0) {
return nil, errors.New("Failed to recover public key") return nil, errors.New("Failed to recover public key")
} else if pubkeylen != C.int(65) {
return nil, errors.New("Impossible Error: Invalid recovered public key length")
} else { } else {
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
} }
return nil, errors.New("Impossible Error: func RecoverPubkey has reached an unreachable state")
} }

View File

@ -18,169 +18,130 @@ package secp256k1
import ( import (
"bytes" "bytes"
"fmt" "encoding/hex"
"log"
"testing" "testing"
"github.com/ethereum/go-ethereum/crypto/randentropy" "github.com/ethereum/go-ethereum/crypto/randentropy"
) )
const TESTS = 10000 // how many tests const TestCount = 10000
const SigSize = 65 //64+1
func Test_Secp256_00(t *testing.T) { func TestPrivkeyGenerate(t *testing.T) {
_, seckey := GenerateKeyPair()
var nonce []byte = randentropy.GetEntropyCSPRNG(32) //going to get bitcoins stolen!
if len(nonce) != 32 {
t.Fatal()
}
}
//tests for Malleability
//highest bit of S must be 0; 32nd byte
func CompactSigTest(sig []byte) {
var b int = int(sig[32])
if b < 0 {
log.Panic()
}
if ((b >> 7) == 1) != ((b & 0x80) == 0x80) {
log.Panic("b= %v b2= %v \n", b, b>>7)
}
if (b & 0x80) == 0x80 {
log.Panic("b= %v b2= %v \n", b, b&0x80)
}
}
//test pubkey/private generation
func Test_Secp256_01(t *testing.T) {
pubkey, seckey := GenerateKeyPair()
if err := VerifySeckeyValidity(seckey); err != nil { if err := VerifySeckeyValidity(seckey); err != nil {
t.Fatal() t.Errorf("seckey not valid: %s", err)
}
if err := VerifyPubkeyValidity(pubkey); err != nil {
t.Fatal()
} }
} }
//test size of messages func TestSignatureValidity(t *testing.T) {
func Test_Secp256_02s(t *testing.T) {
pubkey, seckey := GenerateKeyPair() pubkey, seckey := GenerateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32) msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey) sig, err := Sign(msg, seckey)
CompactSigTest(sig) if err != nil {
if sig == nil { t.Errorf("signature error: %s", err)
t.Fatal("Signature nil")
} }
compactSigCheck(t, sig)
if len(pubkey) != 65 { if len(pubkey) != 65 {
t.Fail() t.Errorf("pubkey length mismatch: want: 65 have: %d", len(pubkey))
} }
if len(seckey) != 32 { if len(seckey) != 32 {
t.Fail() t.Errorf("seckey length mismatch: want: 32 have: %d", len(seckey))
} }
if len(sig) != 64+1 { if len(sig) != 65 {
t.Fail() t.Errorf("sig length mismatch: want: 65 have: %d", len(sig))
}
recid := int(sig[64])
if recid > 4 || recid < 0 {
t.Errorf("sig recid mismatch: want: within 0 to 4 have: %d", int(sig[64]))
} }
if int(sig[64]) > 4 {
t.Fail()
} //should be 0 to 4
} }
//test signing message func TestSignAndRecover(t *testing.T) {
func Test_Secp256_02(t *testing.T) {
pubkey1, seckey := GenerateKeyPair() pubkey1, seckey := GenerateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32) msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey) sig, err := Sign(msg, seckey)
if sig == nil {
t.Fatal("Signature nil")
}
pubkey2, _ := RecoverPubkey(msg, sig)
if pubkey2 == nil {
t.Fatal("Recovered pubkey invalid")
}
if bytes.Equal(pubkey1, pubkey2) == false {
t.Fatal("Recovered pubkey does not match")
}
err := VerifySignature(msg, sig, pubkey1)
if err != nil { if err != nil {
t.Fatal("Signature invalid") t.Errorf("signature error: %s", err)
}
pubkey2, err := RecoverPubkey(msg, sig)
if err != nil {
t.Errorf("recover error: %s", err)
}
if !bytes.Equal(pubkey1, pubkey2) {
t.Errorf("pubkey mismatch: want: %x have: %x", pubkey1, pubkey2)
}
err = VerifySignature(msg, sig, pubkey1)
if err != nil {
t.Errorf("signature verification error: %s", err)
} }
} }
//test pubkey recovery func TestRandomMessagesWithSameKey(t *testing.T) {
func Test_Secp256_02a(t *testing.T) { pubkey, seckey := GenerateKeyPair()
pubkey1, seckey1 := GenerateKeyPair() keys := func() ([]byte, []byte) {
msg := randentropy.GetEntropyCSPRNG(32) // Sign function zeroes the privkey so we need a new one in each call
sig, _ := Sign(msg, seckey1) newkey := make([]byte, len(seckey))
copy(newkey, seckey)
if sig == nil { return pubkey, newkey
t.Fatal("Signature nil")
}
err := VerifySignature(msg, sig, pubkey1)
if err != nil {
t.Fatal("Signature invalid")
}
pubkey2, _ := RecoverPubkey(msg, sig)
if len(pubkey1) != len(pubkey2) {
t.Fatal()
}
for i, _ := range pubkey1 {
if pubkey1[i] != pubkey2[i] {
t.Fatal()
}
}
if bytes.Equal(pubkey1, pubkey2) == false {
t.Fatal()
} }
signAndRecoverWithRandomMessages(t, keys)
} }
//test random messages for the same pub/private key func TestRandomMessagesWithRandomKeys(t *testing.T) {
func Test_Secp256_03(t *testing.T) { keys := func() ([]byte, []byte) {
_, seckey := GenerateKeyPair() pubkey, seckey := GenerateKeyPair()
for i := 0; i < TESTS; i++ { return pubkey, seckey
msg := randentropy.GetEntropyCSPRNG(32) }
sig, _ := Sign(msg, seckey) signAndRecoverWithRandomMessages(t, keys)
CompactSigTest(sig) }
func signAndRecoverWithRandomMessages(t *testing.T, keys func() ([]byte, []byte)) {
for i := 0; i < TestCount; i++ {
pubkey1, seckey := keys()
msg := randentropy.GetEntropyCSPRNG(32)
sig, err := Sign(msg, seckey)
if err != nil {
t.Fatalf("signature error: %s", err)
}
if sig == nil {
t.Fatal("signature is nil")
}
compactSigCheck(t, sig)
// TODO: why do we flip around the recovery id?
sig[len(sig)-1] %= 4 sig[len(sig)-1] %= 4
pubkey2, _ := RecoverPubkey(msg, sig)
pubkey2, err := RecoverPubkey(msg, sig)
if err != nil {
t.Fatalf("recover error: %s", err)
}
if pubkey2 == nil { if pubkey2 == nil {
t.Fail() t.Error("pubkey is nil")
}
if !bytes.Equal(pubkey1, pubkey2) {
t.Fatalf("pubkey mismatch: want: %x have: %x", pubkey1, pubkey2)
} }
} }
} }
//test random messages for different pub/private keys func TestRecoveryOfRandomSignature(t *testing.T) {
func Test_Secp256_04(t *testing.T) {
for i := 0; i < TESTS; i++ {
pubkey1, seckey := GenerateKeyPair() pubkey1, seckey := GenerateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32) msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey) sig, err := Sign(msg, seckey)
CompactSigTest(sig) if err != nil {
t.Errorf("signature error: %s", err)
}
if sig[len(sig)-1] >= 4 { for i := 0; i < TestCount; i++ {
t.Fail() sig = randSig()
}
pubkey2, _ := RecoverPubkey(msg, sig) pubkey2, _ := RecoverPubkey(msg, sig)
if pubkey2 == nil { // recovery can sometimes work, but if so should always give wrong pubkey
t.Fail() if bytes.Equal(pubkey1, pubkey2) {
} t.Fatalf("iteration: %d: pubkey mismatch: do NOT want %x: ", i, pubkey2)
if bytes.Equal(pubkey1, pubkey2) == false {
t.Fail()
} }
} }
} }
//test random signatures against fixed messages; should fail
//crashes:
// -SIPA look at this
func randSig() []byte { func randSig() []byte {
sig := randentropy.GetEntropyCSPRNG(65) sig := randentropy.GetEntropyCSPRNG(65)
sig[32] &= 0x70 sig[32] &= 0x70
@ -188,67 +149,83 @@ func randSig() []byte {
return sig return sig
} }
func Test_Secp256_06a_alt0(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)
if sig == nil { for i := 0; i < TestCount; i++ {
t.Fail()
}
if len(sig) != 65 {
t.Fail()
}
for i := 0; i < TESTS; i++ {
sig = randSig()
pubkey2, _ := RecoverPubkey(msg, sig)
if bytes.Equal(pubkey1, pubkey2) == true {
t.Fail()
}
if pubkey2 != nil && VerifySignature(msg, sig, pubkey2) != nil {
t.Fail()
}
if VerifySignature(msg, sig, pubkey1) == nil {
t.Fail()
}
}
}
//test random messages against valid signature: should fail
func Test_Secp256_06b(t *testing.T) {
pubkey1, seckey := GenerateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey)
fail_count := 0
for i := 0; i < TESTS; i++ {
msg = randentropy.GetEntropyCSPRNG(32) msg = randentropy.GetEntropyCSPRNG(32)
pubkey2, _ := RecoverPubkey(msg, sig) pubkey2, _ := RecoverPubkey(msg, sig)
if bytes.Equal(pubkey1, pubkey2) == true { // recovery can sometimes work, but if so should always give wrong pubkey
t.Fail() if bytes.Equal(pubkey1, pubkey2) {
t.Fatalf("iteration: %d: pubkey mismatch: do NOT want %x: ", i, pubkey2)
} }
if pubkey2 != nil && VerifySignature(msg, sig, pubkey2) != nil {
t.Fail()
}
if VerifySignature(msg, sig, pubkey1) == nil {
t.Fail()
}
}
if fail_count != 0 {
fmt.Printf("ERROR: Accepted signature for %v of %v random messages\n", fail_count, TESTS)
} }
} }
func TestInvalidKey(t *testing.T) { func TestZeroPrivkey(t *testing.T) {
p1 := make([]byte, 32) zeroedBytes := make([]byte, 32)
err := VerifySeckeyValidity(p1) err := VerifySeckeyValidity(zeroedBytes)
if err == nil { if err == nil {
t.Errorf("pvk %x varify sec key should have returned error", p1) 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) {
msg, _ := hex.DecodeString("ce0677bb30baa8cf067c88db9811f4333d131bf8bcf12fe7065d211dce971008")
sig, _ := hex.DecodeString("90f27b8b488db00b00606796d2987f6a5f59ae62ea05effe84fef5b8b0e549984a691139ad57a3f0b906637673aa2f63d1f55cb1a69199d4009eea23ceaddc9301")
pubkey1, _ := hex.DecodeString("04e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a0a2b2667f7e725ceea70c673093bf67663e0312623c8e091b13cf2c0f11ef652")
pubkey2, err := RecoverPubkey(msg, sig)
if err != nil {
t.Fatalf("recover error: %s", err)
}
if !bytes.Equal(pubkey1, pubkey2) {
t.Errorf("pubkey mismatch: want: %x have: %x", pubkey1, pubkey2)
}
}
// 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)
}
}
// godep go test -v -run=XXX -bench=BenchmarkSignRandomInputEachRound
// add -benchtime=10s to benchmark longer for more accurate average
func BenchmarkSignRandomInputEachRound(b *testing.B) {
for i := 0; i < b.N; i++ {
b.StopTimer()
_, seckey := GenerateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32)
b.StartTimer()
if _, err := Sign(msg, seckey); err != nil {
b.Fatal(err)
}
}
}
//godep go test -v -run=XXX -bench=BenchmarkRecoverRandomInputEachRound
func BenchmarkRecoverRandomInputEachRound(b *testing.B) {
for i := 0; i < b.N; i++ {
b.StopTimer()
_, seckey := GenerateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey)
b.StartTimer()
if _, err := RecoverPubkey(msg, sig); err != nil {
b.Fatal(err)
}
} }
} }