Skip to content

Commit

Permalink
core/secp256k1: update libsecp256k1 Go wrapper and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Gustav Simonsson committed Oct 9, 2015
1 parent 1d20b02 commit f32fa07
Show file tree
Hide file tree
Showing 3 changed files with 256 additions and 249 deletions.
10 changes: 9 additions & 1 deletion crypto/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down Expand Up @@ -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
}
}
196 changes: 109 additions & 87 deletions crypto/secp256k1/secp256.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"

Expand All @@ -48,66 +49,68 @@ 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) {
if err := VerifySeckeyValidity(seckey); err != nil {
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")
Expand All @@ -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

}

Expand All @@ -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 {
Expand Down Expand Up @@ -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")
}
Loading

0 comments on commit f32fa07

Please sign in to comment.