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 }