mirror of
https://github.com/luxfi/crypto.git
synced 2026-07-27 01:54:50 +00:00
- Consolidated all cryptographic primitives into ONE implementation each - SECP256K1: Decred (pure Go) + libsecp256k1 (CGO optimized) - Verkle/IPA: Single unified implementation replacing external deps - Added VOPRF, HPKE, and KangarooTwelve from Cloudflare CIRCL - Performance: 2-6x improvement with CGO enabled - All packages (geth, node, evm, coreth) now use luxfi/crypto - Removed github.com/ethereum/go-verkle dependency - Removed github.com/crate-crypto/go-ipa dependency - Added comprehensive precompiles for Verkle operations - Full test coverage for CGO=0 and CGO=1 builds
145 lines
3.9 KiB
Go
145 lines
3.9 KiB
Go
// Copyright 2015 Jeffrey Wilcke, Felix Lange, Gustav Simonsson. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be found in
|
|
// the LICENSE file.
|
|
|
|
//go:build !cgo
|
|
// +build !cgo
|
|
|
|
// Package secp256k1 wraps the bitcoin secp256k1 C library.
|
|
package secp256k1
|
|
|
|
import (
|
|
"errors"
|
|
"math/big"
|
|
|
|
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
|
decred_ecdsa "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
|
|
)
|
|
|
|
var (
|
|
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")
|
|
ErrInvalidPubkey = errors.New("invalid public key")
|
|
ErrSignFailed = errors.New("signing failed")
|
|
ErrRecoverFailed = errors.New("recovery failed")
|
|
)
|
|
|
|
// 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
|
|
}
|
|
if len(seckey) != 32 {
|
|
return nil, ErrInvalidKey
|
|
}
|
|
|
|
// Create a decred private key
|
|
var priv secp256k1.PrivateKey
|
|
if overflow := priv.Key.SetByteSlice(seckey); overflow || priv.Key.IsZero() {
|
|
return nil, ErrInvalidKey
|
|
}
|
|
defer priv.Zero()
|
|
|
|
// Sign the message
|
|
sig := decred_ecdsa.SignCompact(&priv, msg, false) // ref uncompressed pubkey
|
|
|
|
// Convert to Ethereum signature format with 'recovery id' v at the end.
|
|
v := sig[0] - 27
|
|
copy(sig, sig[1:])
|
|
sig[64] = v
|
|
|
|
return sig, nil
|
|
}
|
|
|
|
// RecoverPubkey returns the public key of the signer.
|
|
// msg must be the 32-byte hash of the message to be signed.
|
|
// sig must be a 65-byte compact ECDSA signature containing the
|
|
// recovery id as the last element.
|
|
func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
|
|
if len(msg) != 32 {
|
|
return nil, ErrInvalidMsgLen
|
|
}
|
|
if err := checkSignature(sig); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Convert to secp256k1 input format with 'recovery id' v at the beginning.
|
|
btcsig := make([]byte, 65)
|
|
btcsig[0] = sig[64] + 27
|
|
copy(btcsig[1:], sig)
|
|
|
|
pub, _, err := decred_ecdsa.RecoverCompact(btcsig, msg)
|
|
if err != nil {
|
|
return nil, ErrRecoverFailed
|
|
}
|
|
|
|
return pub.SerializeUncompressed(), nil
|
|
}
|
|
|
|
// VerifySignature checks that the given pubkey created signature over message.
|
|
// The signature should be in [R || S] format.
|
|
func VerifySignature(pubkey, msg, signature []byte) bool {
|
|
if len(msg) != 32 || len(signature) != 64 || len(pubkey) == 0 {
|
|
return false
|
|
}
|
|
|
|
var r, s secp256k1.ModNScalar
|
|
if r.SetByteSlice(signature[:32]) {
|
|
return false // overflow
|
|
}
|
|
if s.SetByteSlice(signature[32:]) {
|
|
return false
|
|
}
|
|
sig := decred_ecdsa.NewSignature(&r, &s)
|
|
|
|
key, err := secp256k1.ParsePubKey(pubkey)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
// Reject malleable signatures. libsecp256k1 does this check but decred doesn't.
|
|
if s.IsOverHalfOrder() {
|
|
return false
|
|
}
|
|
|
|
return sig.Verify(msg, key)
|
|
}
|
|
|
|
// DecompressPubkey parses a public key in the 33-byte compressed format.
|
|
// It returns non-nil coordinates if the public key is valid.
|
|
func DecompressPubkey(pubkey []byte) (x, y *big.Int) {
|
|
if len(pubkey) != 33 {
|
|
return nil, nil
|
|
}
|
|
key, err := secp256k1.ParsePubKey(pubkey)
|
|
if err != nil {
|
|
return nil, nil
|
|
}
|
|
return key.X(), key.Y()
|
|
}
|
|
|
|
// CompressPubkey encodes a public key to 33-byte compressed format.
|
|
func CompressPubkey(x, y *big.Int) []byte {
|
|
var xVal, yVal secp256k1.FieldVal
|
|
xVal.SetByteSlice(x.Bytes())
|
|
yVal.SetByteSlice(y.Bytes())
|
|
return secp256k1.NewPublicKey(&xVal, &yVal).SerializeCompressed()
|
|
}
|
|
|
|
func checkSignature(sig []byte) error {
|
|
if len(sig) != 65 {
|
|
return ErrInvalidSignatureLen
|
|
}
|
|
if sig[64] >= 4 {
|
|
return ErrInvalidRecoveryID
|
|
}
|
|
return nil
|
|
}
|