Commit f32fa075 authored by Gustav Simonsson's avatar Gustav Simonsson

core/secp256k1: update libsecp256k1 Go wrapper and tests

parent 1d20b024
......@@ -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))
}
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
}
......@@ -337,3 +339,9 @@ func PubkeyToAddress(p ecdsa.PublicKey) common.Address {
pubBytes := FromECDSAPub(&p)
return common.BytesToAddress(Sha3(pubBytes[1:])[12:])
}
func zeroBytes(bytes []byte) {
for i := range bytes {
bytes[i] = 0
}
}
......@@ -19,7 +19,7 @@ package secp256k1
// TODO: set USE_SCALAR_4X64 depending on platform?
/*
#cgo CFLAGS: -I./secp256k1
#cgo CFLAGS: -I./libsecp256k1
#cgo darwin CFLAGS: -I/usr/local/include
#cgo freebsd CFLAGS: -I/usr/local/include
#cgo linux,arm CFLAGS: -I/usr/local/arm/include
......@@ -33,7 +33,8 @@ package secp256k1
#define USE_SCALAR_8X32
#define USE_SCALAR_INV_BUILTIN
#define NDEBUG
#include "./secp256k1/src/secp256k1.c"
#include "./libsecp256k1/src/secp256k1.c"
#include "./libsecp256k1/src/modules/recovery/main_impl.h"
*/
import "C"
......@@ -48,48 +49,51 @@ import (
//#define USE_FIELD_5X64
/*
Todo:
> Centralize key management in module
> add pubkey/private key struct
> Dont let keys leave module; address keys as ints
TODO:
> 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?)
On Disk
> Store keys in wallets
> use slow key derivation function for wallet encryption key (2 seconds)
> on disk: store keys in wallets
*/
func init() {
//takes 10ms to 100ms
C.secp256k1_start(3) // SECP256K1_START_SIGN | SECP256K1_START_VERIFY
}
// holds ptr to secp256k1_context_struct (see secp256k1/include/secp256k1.h)
var context *C.secp256k1_context
func Stop() {
C.secp256k1_stop()
func init() {
// around 20 ms on a modern CPU.
context = C.secp256k1_context_create(3) // SECP256K1_START_SIGN | SECP256K1_START_VERIFY
}
func GenerateKeyPair() ([]byte, []byte) {
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 []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(
pubkey_ptr, &pubkey_len,
seckey_ptr, 0)
context,
pubkey64_ptr,
seckey_ptr,
)
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) {
......@@ -97,17 +101,16 @@ func GeneratePubKey(seckey []byte) ([]byte, error) {
return nil, err
}
pubkey_len := C.int(65)
const seckey_len = 32
var pubkey []byte = make([]byte, 64)
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]))
ret := C.secp256k1_ec_pubkey_create(
pubkey_ptr, &pubkey_len,
seckey_ptr, 0)
context,
pubkey_ptr,
seckey_ptr,
)
if ret != C.int(1) {
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) {
nonce := randentropy.GetEntropyCSPRNG(32)
msg_ptr := (*C.uchar)(unsafe.Pointer(&msg[0]))
seckey_ptr := (*C.uchar)(unsafe.Pointer(&seckey[0]))
var sig []byte = make([]byte, 65)
var recid C.int
sig := make([]byte, 65)
sig_ptr := (*C.secp256k1_ecdsa_recoverable_signature)(unsafe.Pointer(&sig[0]))
var msg_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&msg[0]))
var sig_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&sig[0]))
var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
nonce := randentropy.GetEntropyCSPRNG(32)
ndata_ptr := unsafe.Pointer(&nonce[0])
var noncefp_ptr = &(*C.secp256k1_nonce_function_default)
var ndata_ptr = unsafe.Pointer(&nonce[0])
noncefp_ptr := &(*C.secp256k1_nonce_function_default)
if C.secp256k1_ec_seckey_verify(seckey_ptr) != C.int(1) {
if C.secp256k1_ec_seckey_verify(context, seckey_ptr) != C.int(1) {
return nil, errors.New("Invalid secret key")
}
ret := C.secp256k1_ecdsa_sign_compact(
msg_ptr,
ret := C.secp256k1_ecdsa_sign_recoverable(
context,
sig_ptr,
msg_ptr,
seckey_ptr,
noncefp_ptr,
ndata_ptr,
&recid)
sig[64] = byte(int(recid))
)
if ret != C.int(1) {
// nonce invalid, retry
return Sign(msg, seckey)
if ret == C.int(0) {
return Sign(msg, seckey) //invalid secret, try again
}
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")
}
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 {
return errors.New("invalid seckey")
}
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 {
//64+1
if len(sig) != 65 {
......@@ -231,36 +231,58 @@ func VerifySignature(msg []byte, sig []byte, pubkey1 []byte) error {
return nil
}
//recovers the public key from the signature
//recovery of pubkey means correct signature
// recovers a public key from the signature
func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
if len(sig) != 65 {
return nil, errors.New("Invalid signature length")
}
var pubkey []byte = make([]byte, 65)
var msg_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&msg[0]))
var sig_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&sig[0]))
var pubkey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&pubkey[0]))
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)
var pubkeylen C.int
if ret == C.int(0) {
return nil, errors.New("Failed to parse signature")
}
ret := C.secp256k1_ecdsa_recover_compact(
msg_ptr,
sig_ptr,
ret = C.secp256k1_ecdsa_recover(
context,
pubkey_ptr,
&pubkeylen,
C.int(0),
C.int(sig[64]),
recoverable_sig_ptr,
msg_ptr,
)
if ret == C.int(0) {
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 {
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")
}
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment