github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/crypto/secp256k1/secp256k1.go (about) 1 package secp256k1 2 3 import ( 4 "bytes" 5 "crypto/sha256" 6 "crypto/subtle" 7 "io" 8 "math/big" 9 10 secp256k1 "github.com/btcsuite/btcd/btcec/v2" 11 "golang.org/x/crypto/ripemd160" 12 13 "github.com/gnolang/gno/tm2/pkg/amino" 14 "github.com/gnolang/gno/tm2/pkg/crypto" 15 ) 16 17 //------------------------------------- 18 19 var _ crypto.PrivKey = PrivKeySecp256k1{} 20 21 // PrivKeySecp256k1 implements PrivKey. 22 type PrivKeySecp256k1 [32]byte 23 24 // Bytes marshalls the private key using amino encoding. 25 func (privKey PrivKeySecp256k1) Bytes() []byte { 26 return amino.MustMarshalAny(privKey) 27 } 28 29 // PubKey performs the point-scalar multiplication from the privKey on the 30 // generator point to get the pubkey. 31 func (privKey PrivKeySecp256k1) PubKey() crypto.PubKey { 32 _, pubkeyObject := secp256k1.PrivKeyFromBytes(privKey[:]) 33 var pubkeyBytes PubKeySecp256k1 34 copy(pubkeyBytes[:], pubkeyObject.SerializeCompressed()) 35 return pubkeyBytes 36 } 37 38 // Equals - you probably don't need to use this. 39 // Runs in constant time based on length of the keys. 40 func (privKey PrivKeySecp256k1) Equals(other crypto.PrivKey) bool { 41 if otherSecp, ok := other.(PrivKeySecp256k1); ok { 42 return subtle.ConstantTimeCompare(privKey[:], otherSecp[:]) == 1 43 } 44 return false 45 } 46 47 // GenPrivKey generates a new ECDSA private key on curve secp256k1 private key. 48 // It uses OS randomness to generate the private key. 49 func GenPrivKey() PrivKeySecp256k1 { 50 return genPrivKey(crypto.CReader()) 51 } 52 53 // genPrivKey generates a new secp256k1 private key using the provided reader. 54 func genPrivKey(rand io.Reader) PrivKeySecp256k1 { 55 var privKeyBytes [32]byte 56 d := new(big.Int) 57 for { 58 privKeyBytes = [32]byte{} 59 _, err := io.ReadFull(rand, privKeyBytes[:]) 60 if err != nil { 61 panic(err) 62 } 63 64 d.SetBytes(privKeyBytes[:]) 65 // break if we found a valid point (i.e. > 0 and < N == curverOrder) 66 isValidFieldElement := 0 < d.Sign() && d.Cmp(secp256k1.S256().N) < 0 67 if isValidFieldElement { 68 break 69 } 70 } 71 72 return PrivKeySecp256k1(privKeyBytes) 73 } 74 75 var one = new(big.Int).SetInt64(1) 76 77 // GenPrivKeySecp256k1 hashes the secret with SHA2, and uses 78 // that 32 byte output to create the private key. 79 // 80 // It makes sure the private key is a valid field element by setting: 81 // 82 // c = sha256(secret) 83 // k = (c mod (n − 1)) + 1, where n = curve order. 84 // 85 // NOTE: secret should be the output of a KDF like bcrypt, 86 // if it's derived from user input. 87 func GenPrivKeySecp256k1(secret []byte) PrivKeySecp256k1 { 88 secHash := sha256.Sum256(secret) 89 // to guarantee that we have a valid field element, we use the approach of: 90 // "Suite B Implementer’s Guide to FIPS 186-3", A.2.1 91 // https://apps.nsa.gov/iaarchive/library/ia-guidance/ia-solutions-for-classified/algorithm-guidance/suite-b-implementers-guide-to-fips-186-3-ecdsa.cfm 92 // see also https://github.com/golang/go/blob/0380c9ad38843d523d9c9804fe300cb7edd7cd3c/src/crypto/ecdsa/ecdsa.go#L89-L101 93 fe := new(big.Int).SetBytes(secHash[:]) 94 n := new(big.Int).Sub(secp256k1.S256().N, one) 95 fe.Mod(fe, n) 96 fe.Add(fe, one) 97 98 feB := fe.Bytes() 99 var privKey32 [32]byte 100 // copy feB over to fixed 32 byte privKey32 and pad (if necessary) 101 copy(privKey32[32-len(feB):32], feB) 102 103 return PrivKeySecp256k1(privKey32) 104 } 105 106 //------------------------------------- 107 108 var _ crypto.PubKey = PubKeySecp256k1{} 109 110 // PubKeySecp256k1Size is comprised of 32 bytes for one field element 111 // (the x-coordinate), plus one byte for the parity of the y-coordinate. 112 const PubKeySecp256k1Size = 33 113 114 // PubKeySecp256k1 implements crypto.PubKey. 115 // It is the compressed form of the pubkey. The first byte depends is a 0x02 byte 116 // if the y-coordinate is the lexicographically largest of the two associated with 117 // the x-coordinate. Otherwise the first byte is a 0x03. 118 // This prefix is followed with the x-coordinate. 119 type PubKeySecp256k1 [PubKeySecp256k1Size]byte 120 121 // Address returns a Bitcoin style addresses: RIPEMD160(SHA256(pubkey)) 122 func (pubKey PubKeySecp256k1) Address() crypto.Address { 123 hasherSHA256 := sha256.New() 124 hasherSHA256.Write(pubKey[:]) // does not error 125 sha := hasherSHA256.Sum(nil) 126 127 hasherRIPEMD160 := ripemd160.New() 128 hasherRIPEMD160.Write(sha) // does not error 129 return crypto.AddressFromBytes(hasherRIPEMD160.Sum(nil)) 130 } 131 132 // Bytes returns the pubkey marshalled with amino encoding. 133 func (pubKey PubKeySecp256k1) Bytes() []byte { 134 return amino.MustMarshalAny(pubKey) 135 } 136 137 func (pubKey PubKeySecp256k1) String() string { 138 return crypto.PubKeyToBech32(pubKey) 139 } 140 141 func (pubKey PubKeySecp256k1) Equals(other crypto.PubKey) bool { 142 if otherSecp, ok := other.(PubKeySecp256k1); ok { 143 return bytes.Equal(pubKey[:], otherSecp[:]) 144 } 145 return false 146 }