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