Files
Hanzo Dev e4205c2176 feat: unified crypto package with single implementations
- 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
2025-08-16 02:24:36 -05:00

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
}