diff options
Diffstat (limited to 'crypto/secp256k1/secp256.go')
-rw-r--r-- | crypto/secp256k1/secp256.go | 201 |
1 files changed, 43 insertions, 158 deletions
diff --git a/crypto/secp256k1/secp256.go b/crypto/secp256k1/secp256.go index 2c5f61450..070e0d902 100644 --- a/crypto/secp256k1/secp256.go +++ b/crypto/secp256k1/secp256.go @@ -14,10 +14,9 @@ // 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/>. +// Package secp256k1 wraps the bitcoin secp256k1 C library. package secp256k1 -// TODO: set USE_SCALAR_4X64 depending on platform? - /* #cgo CFLAGS: -I./libsecp256k1 #cgo CFLAGS: -I./libsecp256k1/src/ @@ -29,7 +28,7 @@ package secp256k1 #define NDEBUG #include "./libsecp256k1/src/secp256k1.c" #include "./libsecp256k1/src/modules/recovery/main_impl.h" -#include "pubkey_scalar_mul.h" +#include "ext.h" typedef void (*callbackFunc) (const char* msg, void* data); extern void secp256k1GoPanicIllegal(const char* msg, void* data); @@ -45,16 +44,6 @@ import ( "github.com/ethereum/go-ethereum/crypto/randentropy" ) -//#define USE_FIELD_5X64 - -/* - TODO: - > store private keys in buffer and shuffle (deters persistence on swap disc) - > byte permutation (changing) - > xor with chaning random block (to deter scanning memory for 0x63) (stream cipher?) -*/ - -// holds ptr to secp256k1_context_struct (see secp256k1/include/secp256k1.h) var ( context *C.secp256k1_context N *big.Int @@ -67,127 +56,57 @@ func init() { HalfN, _ = new(big.Int).SetString("7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", 16) // around 20 ms on a modern CPU. - context = C.secp256k1_context_create(3) // SECP256K1_START_SIGN | SECP256K1_START_VERIFY + context = C.secp256k1_context_create_sign_verify() C.secp256k1_context_set_illegal_callback(context, C.callbackFunc(C.secp256k1GoPanicIllegal), nil) C.secp256k1_context_set_error_callback(context, C.callbackFunc(C.secp256k1GoPanicError), nil) } var ( - ErrInvalidMsgLen = errors.New("invalid message length for signature recovery") + ErrInvalidMsgLen = errors.New("invalid message length, need 32 bytes") ErrInvalidSignatureLen = errors.New("invalid signature length") ErrInvalidRecoveryID = errors.New("invalid signature recovery id") + ErrInvalidKey = errors.New("invalid private key") + ErrSignFailed = errors.New("signing failed") + ErrRecoverFailed = errors.New("recovery failed") ) -func GenerateKeyPair() ([]byte, []byte) { - 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( - context, - pubkey64_ptr, - seckey_ptr, - ) - - if ret != C.int(1) { - return GenerateKeyPair() // invalid secret, try again - } - - 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 +// Sign creates a recoverable ECDSA signature. +// The produced signature is in the 65-byte [R || S || V] format where V is 0 or 1. +// +// The caller is responsible for ensuring that msg cannot be chosen +// directly by an attacker. It is usually preferable to use a cryptographic +// hash function on any input before handing it to this function. +func Sign(msg []byte, seckey []byte) ([]byte, error) { + if len(msg) != 32 { + return nil, ErrInvalidMsgLen } - - var pubkey []byte = make([]byte, 64) - var pubkey_ptr *C.secp256k1_pubkey = (*C.secp256k1_pubkey)(unsafe.Pointer(&pubkey[0])) - - var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0])) - - ret := C.secp256k1_ec_pubkey_create( - context, - pubkey_ptr, - seckey_ptr, - ) - - if ret != C.int(1) { - return nil, errors.New("Unable to generate pubkey from seckey") + if len(seckey) != 32 { + return nil, ErrInvalidKey } - - 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])) - - sig := make([]byte, 65) - sig_ptr := (*C.secp256k1_ecdsa_recoverable_signature)(unsafe.Pointer(&sig[0])) - - nonce := randentropy.GetEntropyCSPRNG(32) - ndata_ptr := unsafe.Pointer(&nonce[0]) - - noncefp_ptr := &(*C.secp256k1_nonce_function_default) - - if C.secp256k1_ec_seckey_verify(context, seckey_ptr) != C.int(1) { - return nil, errors.New("Invalid secret key") + seckeydata := (*C.uchar)(unsafe.Pointer(&seckey[0])) + if C.secp256k1_ec_seckey_verify(context, seckeydata) != 1 { + return nil, ErrInvalidKey } - ret := C.secp256k1_ecdsa_sign_recoverable( - context, - sig_ptr, - msg_ptr, - seckey_ptr, - noncefp_ptr, - ndata_ptr, + var ( + msgdata = (*C.uchar)(unsafe.Pointer(&msg[0])) + nonce = randentropy.GetEntropyCSPRNG(32) + noncefunc = &(*C.secp256k1_nonce_function_default) + noncefuncData = unsafe.Pointer(&nonce[0]) + sigstruct C.secp256k1_ecdsa_recoverable_signature ) - - if ret == C.int(0) { - return Sign(msg, seckey) //invalid secret, try again + if C.secp256k1_ecdsa_sign_recoverable(context, &sigstruct, msgdata, seckeydata, noncefunc, noncefuncData) == 0 { + return nil, ErrSignFailed } - 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 + var ( + sig = make([]byte, 65) + sigdata = (*C.uchar)(unsafe.Pointer(&sig[0])) + recid C.int ) - - sig_serialized[64] = byte(int(recid)) // add back recid to get 65 bytes sig - - return sig_serialized, nil - -} - -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) - if int(ret) != 1 { - return errors.New("invalid seckey") - } - return nil + C.secp256k1_ecdsa_recoverable_signature_serialize_compact(context, sigdata, &recid, &sigstruct) + sig[64] = byte(recid) // add back recid to get 65 bytes sig + return sig, nil } // RecoverPubkey returns the the public key of the signer. @@ -202,49 +121,15 @@ func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) { return nil, err } - 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) - if ret == C.int(0) { - return nil, errors.New("Failed to parse signature") - } - - ret = C.secp256k1_ecdsa_recover( - context, - pubkey_ptr, - recoverable_sig_ptr, - msg_ptr, + var ( + pubkey = make([]byte, 65) + sigdata = (*C.uchar)(unsafe.Pointer(&sig[0])) + msgdata = (*C.uchar)(unsafe.Pointer(&msg[0])) ) - if ret == C.int(0) { - return nil, errors.New("Failed to recover public key") + if C.secp256k1_ecdsa_recover_pubkey(context, (*C.uchar)(unsafe.Pointer(&pubkey[0])), sigdata, msgdata) == 0 { + return nil, ErrRecoverFailed } - - 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 pubkey, nil } func checkSignature(sig []byte) error { |