github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/crypto/secp256k1/secp256k1.go (about)

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