op-geth/crypto/secp256k1/secp256.go

289 lines
7.4 KiB
Go
Raw Normal View History

2015-07-07 00:54:22 +00:00
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
2015-07-07 00:54:22 +00:00
//
// The go-ethereum library is free software: you can redistribute it and/or modify
2015-07-07 00:54:22 +00:00
// 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,
2015-07-07 00:54:22 +00:00
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2015-07-07 00:54:22 +00:00
// 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/>.
2015-07-07 00:54:22 +00:00
2015-01-21 23:35:00 +00:00
package secp256k1
2015-04-07 10:40:31 +00:00
// TODO: set USE_SCALAR_4X64 depending on platform?
2015-01-21 23:35:00 +00:00
/*
#cgo CFLAGS: -I./libsecp256k1
#cgo darwin CFLAGS: -I/usr/local/include -I/opt/pkg/include
2015-07-28 22:16:16 +00:00
#cgo freebsd CFLAGS: -I/usr/local/include
#cgo linux,arm CFLAGS: -I/usr/local/arm/include
2015-04-07 13:20:24 +00:00
#cgo LDFLAGS: -lgmp
#cgo darwin LDFLAGS: -L/usr/local/lib -L/opt/pkg/lib
2015-07-28 22:16:16 +00:00
#cgo freebsd LDFLAGS: -L/usr/local/lib
#cgo linux,arm LDFLAGS: -L/usr/local/arm/lib
2015-01-21 23:35:00 +00:00
#define USE_NUM_GMP
2015-04-07 10:40:31 +00:00
#define USE_FIELD_10X26
2015-01-21 23:35:00 +00:00
#define USE_FIELD_INV_BUILTIN
2015-04-07 10:40:31 +00:00
#define USE_SCALAR_8X32
#define USE_SCALAR_INV_BUILTIN
2015-04-07 16:09:58 +00:00
#define NDEBUG
#include "./libsecp256k1/src/secp256k1.c"
#include "./libsecp256k1/src/modules/recovery/main_impl.h"
2015-01-21 23:35:00 +00:00
*/
import "C"
import (
"bytes"
"errors"
"unsafe"
"github.com/ethereum/go-ethereum/crypto/randentropy"
2015-01-21 23:35:00 +00:00
)
//#define USE_FIELD_5X64
/*
TODO:
2015-01-21 23:35:00 +00:00
> store private keys in buffer and shuffle (deters persistance on swap disc)
> byte permutation (changing)
2015-01-21 23:35:00 +00:00
> xor with chaning random block (to deter scanning memory for 0x63) (stream cipher?)
> on disk: store keys in wallets
2015-01-21 23:35:00 +00:00
*/
// holds ptr to secp256k1_context_struct (see secp256k1/include/secp256k1.h)
var context *C.secp256k1_context
2015-01-21 23:35:00 +00:00
func init() {
// around 20 ms on a modern CPU.
context = C.secp256k1_context_create(3) // SECP256K1_START_SIGN | SECP256K1_START_VERIFY
2015-01-21 23:35:00 +00:00
}
func GenerateKeyPair() ([]byte, []byte) {
var seckey []byte = randentropy.GetEntropyCSPRNG(32)
2015-01-21 23:35:00 +00:00
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]))
2015-04-07 10:40:31 +00:00
ret := C.secp256k1_ec_pubkey_create(
context,
pubkey64_ptr,
seckey_ptr,
)
2015-01-21 23:35:00 +00:00
if ret != C.int(1) {
return GenerateKeyPair() // invalid secret, try again
2015-01-21 23:35:00 +00:00
}
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
2015-01-21 23:35:00 +00:00
}
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]))
2015-01-21 23:35:00 +00:00
var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
2015-04-07 10:40:31 +00:00
ret := C.secp256k1_ec_pubkey_create(
context,
pubkey_ptr,
seckey_ptr,
)
2015-01-21 23:35:00 +00:00
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) {
msg_ptr := (*C.uchar)(unsafe.Pointer(&msg[0]))
seckey_ptr := (*C.uchar)(unsafe.Pointer(&seckey[0]))
2015-01-21 23:35:00 +00:00
sig := make([]byte, 65)
sig_ptr := (*C.secp256k1_ecdsa_recoverable_signature)(unsafe.Pointer(&sig[0]))
2015-01-21 23:35:00 +00:00
nonce := randentropy.GetEntropyCSPRNG(32)
ndata_ptr := unsafe.Pointer(&nonce[0])
2015-04-07 10:40:31 +00:00
noncefp_ptr := &(*C.secp256k1_nonce_function_default)
2015-01-21 23:35:00 +00:00
if C.secp256k1_ec_seckey_verify(context, seckey_ptr) != C.int(1) {
2015-01-21 23:35:00 +00:00
return nil, errors.New("Invalid secret key")
}
ret := C.secp256k1_ecdsa_sign_recoverable(
context,
2015-01-21 23:35:00 +00:00
sig_ptr,
msg_ptr,
2015-01-21 23:35:00 +00:00
seckey_ptr,
2015-04-07 10:40:31 +00:00
noncefp_ptr,
ndata_ptr,
)
2015-01-21 23:35:00 +00:00
if ret == C.int(0) {
return Sign(msg, seckey) //invalid secret, try again
2015-01-21 23:35:00 +00:00
}
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
2015-01-21 23:35:00 +00:00
}
func VerifySeckeyValidity(seckey []byte) error {
if len(seckey) != 32 {
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(context, seckey_ptr)
2015-01-21 23:35:00 +00:00
if int(ret) != 1 {
return errors.New("invalid seckey")
}
return nil
}
func VerifySignatureValidity(sig []byte) bool {
//64+1
if len(sig) != 65 {
return false
}
//malleability check, highest bit must be 1
if (sig[32] & 0x80) == 0x80 {
return false
}
//recovery id check
if sig[64] >= 4 {
return false
}
return true
}
//for compressed signatures, does not need pubkey
func VerifySignature(msg []byte, sig []byte, pubkey1 []byte) error {
if msg == nil || sig == nil || pubkey1 == nil {
return errors.New("inputs must be non-nil")
}
if len(sig) != 65 {
return errors.New("invalid signature length")
}
if len(pubkey1) != 65 {
return errors.New("Invalid public key length")
}
//to enforce malleability, highest bit of S must be 0
//S starts at 32nd byte
if (sig[32] & 0x80) == 0x80 { //highest bit must be 1
return errors.New("Signature not malleable")
}
if sig[64] >= 4 {
return errors.New("Recover byte invalid")
}
// if pubkey recovered, signature valid
pubkey2, err := RecoverPubkey(msg, sig)
if err != nil {
return err
}
if len(pubkey2) != 65 {
return errors.New("Invalid recovered public key length")
}
if !bytes.Equal(pubkey1, pubkey2) {
return errors.New("Public key does not match recovered public key")
}
return nil
}
// recovers a public key from the signature
2015-01-21 23:35:00 +00:00
func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
if len(sig) != 65 {
return nil, errors.New("Invalid signature length")
}
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)
2015-01-21 23:35:00 +00:00
if ret == C.int(0) {
return nil, errors.New("Failed to parse signature")
}
2015-01-21 23:35:00 +00:00
ret = C.secp256k1_ecdsa_recover(
context,
2015-04-07 10:40:31 +00:00
pubkey_ptr,
recoverable_sig_ptr,
msg_ptr,
2015-01-21 23:35:00 +00:00
)
if ret == C.int(0) {
return nil, errors.New("Failed to recover public key")
} else {
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
2015-01-21 23:35:00 +00:00
}
}