github.com/badrootd/celestia-core@v0.0.0-20240305091328-aa4207a4b25d/crypto/secp256k1/secp256k1.go (about)

     1  package secp256k1
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/sha256"
     6  	"crypto/subtle"
     7  	"fmt"
     8  	"io"
     9  	"math/big"
    10  
    11  	secp256k1 "github.com/btcsuite/btcd/btcec/v2"
    12  	"github.com/btcsuite/btcd/btcec/v2/ecdsa"
    13  	"golang.org/x/crypto/ripemd160" //nolint: staticcheck // necessary for Bitcoin address format
    14  
    15  	"github.com/badrootd/celestia-core/crypto"
    16  	cmtjson "github.com/badrootd/celestia-core/libs/json"
    17  )
    18  
    19  // -------------------------------------
    20  const (
    21  	PrivKeyName = "tendermint/PrivKeySecp256k1"
    22  	PubKeyName  = "tendermint/PubKeySecp256k1"
    23  
    24  	KeyType     = "secp256k1"
    25  	PrivKeySize = 32
    26  )
    27  
    28  func init() {
    29  	cmtjson.RegisterType(PubKey{}, PubKeyName)
    30  	cmtjson.RegisterType(PrivKey{}, PrivKeyName)
    31  }
    32  
    33  var _ crypto.PrivKey = PrivKey{}
    34  
    35  // PrivKey implements PrivKey.
    36  type PrivKey []byte
    37  
    38  // Bytes marshalls the private key using amino encoding.
    39  func (privKey PrivKey) Bytes() []byte {
    40  	return []byte(privKey)
    41  }
    42  
    43  // PubKey performs the point-scalar multiplication from the privKey on the
    44  // generator point to get the pubkey.
    45  func (privKey PrivKey) PubKey() crypto.PubKey {
    46  	_, pubkeyObject := secp256k1.PrivKeyFromBytes(privKey)
    47  
    48  	pk := pubkeyObject.SerializeCompressed()
    49  
    50  	return PubKey(pk)
    51  }
    52  
    53  // Equals - you probably don't need to use this.
    54  // Runs in constant time based on length of the keys.
    55  func (privKey PrivKey) Equals(other crypto.PrivKey) bool {
    56  	if otherSecp, ok := other.(PrivKey); ok {
    57  		return subtle.ConstantTimeCompare(privKey[:], otherSecp[:]) == 1
    58  	}
    59  	return false
    60  }
    61  
    62  func (privKey PrivKey) Type() string {
    63  	return KeyType
    64  }
    65  
    66  // GenPrivKey generates a new ECDSA private key on curve secp256k1 private key.
    67  // It uses OS randomness to generate the private key.
    68  func GenPrivKey() PrivKey {
    69  	return genPrivKey(crypto.CReader())
    70  }
    71  
    72  // genPrivKey generates a new secp256k1 private key using the provided reader.
    73  func genPrivKey(rand io.Reader) PrivKey {
    74  	var privKeyBytes [PrivKeySize]byte
    75  	d := new(big.Int)
    76  
    77  	for {
    78  		privKeyBytes = [PrivKeySize]byte{}
    79  		_, err := io.ReadFull(rand, privKeyBytes[:])
    80  		if err != nil {
    81  			panic(err)
    82  		}
    83  
    84  		d.SetBytes(privKeyBytes[:])
    85  		// break if we found a valid point (i.e. > 0 and < N == curverOrder)
    86  		isValidFieldElement := 0 < d.Sign() && d.Cmp(secp256k1.S256().N) < 0
    87  		if isValidFieldElement {
    88  			break
    89  		}
    90  	}
    91  
    92  	return PrivKey(privKeyBytes[:])
    93  }
    94  
    95  var one = new(big.Int).SetInt64(1)
    96  
    97  // GenPrivKeySecp256k1 hashes the secret with SHA2, and uses
    98  // that 32 byte output to create the private key.
    99  //
   100  // It makes sure the private key is a valid field element by setting:
   101  //
   102  // c = sha256(secret)
   103  // k = (c mod (n − 1)) + 1, where n = curve order.
   104  //
   105  // NOTE: secret should be the output of a KDF like bcrypt,
   106  // if it's derived from user input.
   107  func GenPrivKeySecp256k1(secret []byte) PrivKey {
   108  	secHash := sha256.Sum256(secret)
   109  	// to guarantee that we have a valid field element, we use the approach of:
   110  	// "Suite B Implementer’s Guide to FIPS 186-3", A.2.1
   111  	// https://apps.nsa.gov/iaarchive/library/ia-guidance/ia-solutions-for-classified/algorithm-guidance/suite-b-implementers-guide-to-fips-186-3-ecdsa.cfm
   112  	// see also https://github.com/golang/go/blob/0380c9ad38843d523d9c9804fe300cb7edd7cd3c/src/crypto/ecdsa/ecdsa.go#L89-L101
   113  	fe := new(big.Int).SetBytes(secHash[:])
   114  	n := new(big.Int).Sub(secp256k1.S256().N, one)
   115  	fe.Mod(fe, n)
   116  	fe.Add(fe, one)
   117  
   118  	feB := fe.Bytes()
   119  	privKey32 := make([]byte, PrivKeySize)
   120  	// copy feB over to fixed 32 byte privKey32 and pad (if necessary)
   121  	copy(privKey32[32-len(feB):32], feB)
   122  
   123  	return PrivKey(privKey32)
   124  }
   125  
   126  // Sign creates an ECDSA signature on curve Secp256k1, using SHA256 on the msg.
   127  // The returned signature will be of the form R || S (in lower-S form).
   128  func (privKey PrivKey) Sign(msg []byte) ([]byte, error) {
   129  	priv, _ := secp256k1.PrivKeyFromBytes(privKey)
   130  
   131  	sig, err := ecdsa.SignCompact(priv, crypto.Sha256(msg), false)
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  
   136  	// remove the first byte which is compactSigRecoveryCode
   137  	return sig[1:], nil
   138  }
   139  
   140  //-------------------------------------
   141  
   142  var _ crypto.PubKey = PubKey{}
   143  
   144  // PubKeySize is comprised of 32 bytes for one field element
   145  // (the x-coordinate), plus one byte for the parity of the y-coordinate.
   146  const PubKeySize = 33
   147  
   148  // PubKey implements crypto.PubKey.
   149  // It is the compressed form of the pubkey. The first byte depends is a 0x02 byte
   150  // if the y-coordinate is the lexicographically largest of the two associated with
   151  // the x-coordinate. Otherwise the first byte is a 0x03.
   152  // This prefix is followed with the x-coordinate.
   153  type PubKey []byte
   154  
   155  // Address returns a Bitcoin style addresses: RIPEMD160(SHA256(pubkey))
   156  func (pubKey PubKey) Address() crypto.Address {
   157  	if len(pubKey) != PubKeySize {
   158  		panic("length of pubkey is incorrect")
   159  	}
   160  	hasherSHA256 := sha256.New()
   161  	_, _ = hasherSHA256.Write(pubKey) // does not error
   162  	sha := hasherSHA256.Sum(nil)
   163  
   164  	hasherRIPEMD160 := ripemd160.New()
   165  	_, _ = hasherRIPEMD160.Write(sha) // does not error
   166  
   167  	return crypto.Address(hasherRIPEMD160.Sum(nil))
   168  }
   169  
   170  // Bytes returns the pubkey marshaled with amino encoding.
   171  func (pubKey PubKey) Bytes() []byte {
   172  	return []byte(pubKey)
   173  }
   174  
   175  func (pubKey PubKey) String() string {
   176  	return fmt.Sprintf("PubKeySecp256k1{%X}", []byte(pubKey))
   177  }
   178  
   179  func (pubKey PubKey) Equals(other crypto.PubKey) bool {
   180  	if otherSecp, ok := other.(PubKey); ok {
   181  		return bytes.Equal(pubKey[:], otherSecp[:])
   182  	}
   183  	return false
   184  }
   185  
   186  func (pubKey PubKey) Type() string {
   187  	return KeyType
   188  }
   189  
   190  // VerifySignature verifies a signature of the form R || S.
   191  // It rejects signatures which are not in lower-S form.
   192  func (pubKey PubKey) VerifySignature(msg []byte, sigStr []byte) bool {
   193  	if len(sigStr) != 64 {
   194  		return false
   195  	}
   196  
   197  	pub, err := secp256k1.ParsePubKey(pubKey)
   198  	if err != nil {
   199  		return false
   200  	}
   201  
   202  	// parse the signature:
   203  	signature := signatureFromBytes(sigStr)
   204  	// Reject malleable signatures. libsecp256k1 does this check but btcec doesn't.
   205  	// see: https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/signature_nocgo.go#L90-L93
   206  	// Serialize() would negate S value if it is over half order.
   207  	// Hence, if the signature is different after Serialize() if should be rejected.
   208  	var modifiedSignature, parseErr = ecdsa.ParseDERSignature(signature.Serialize())
   209  	if parseErr != nil {
   210  		return false
   211  	}
   212  	if !signature.IsEqual(modifiedSignature) {
   213  		return false
   214  	}
   215  
   216  	return signature.Verify(crypto.Sha256(msg), pub)
   217  }
   218  
   219  // Read Signature struct from R || S. Caller needs to ensure
   220  // that len(sigStr) == 64.
   221  func signatureFromBytes(sigStr []byte) *ecdsa.Signature {
   222  	var r secp256k1.ModNScalar
   223  	r.SetByteSlice(sigStr[:32])
   224  	var s secp256k1.ModNScalar
   225  	s.SetByteSlice(sigStr[32:64])
   226  	return ecdsa.NewSignature(&r, &s)
   227  }