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) { ...@@ -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 { ...@@ -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
}
}
...@@ -19,7 +19,7 @@ package secp256k1 ...@@ -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 ...@@ -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 ( ...@@ -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) { ...@@ -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) { ...@@ -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) {
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) sig := make([]byte, 65)
var recid C.int sig_ptr := (*C.secp256k1_ecdsa_recoverable_signature)(unsafe.Pointer(&sig[0]))
var msg_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&msg[0])) nonce := randentropy.GetEntropyCSPRNG(32)
var sig_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&sig[0])) ndata_ptr := unsafe.Pointer(&nonce[0])
var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
var noncefp_ptr = &(*C.secp256k1_nonce_function_default) 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) { if C.secp256k1_ec_seckey_verify(context, 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(1) { if ret == C.int(0) {
// nonce invalid, retry return Sign(msg, seckey) //invalid secret, try again
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 { ...@@ -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 { ...@@ -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]))
var sig_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&sig[0])) pubkey := make([]byte, 64)
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)
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( ret = C.secp256k1_ecdsa_recover(
msg_ptr, context,
sig_ptr,
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")
} }
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