github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/crypto/ed25519/ed25519.go (about) 1 package ed25519 2 3 import ( 4 "bytes" 5 "crypto/rand" 6 "crypto/sha256" 7 "crypto/subtle" 8 "errors" 9 "fmt" 10 "io" 11 12 "github.com/oasisprotocol/curve25519-voi/primitives/ed25519" 13 "github.com/oasisprotocol/curve25519-voi/primitives/ed25519/extra/cache" 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 20 //------------------------------------- 21 22 var ( 23 _ crypto.PrivKey = PrivKey{} 24 25 // curve25519-voi's Ed25519 implementation supports configurable 26 // verification behavior, and tendermint uses the ZIP-215 verification 27 // semantics. 28 verifyOptions = &ed25519.Options{ 29 Verify: ed25519.VerifyOptionsZIP_215, 30 } 31 32 cachingVerifier = cache.NewVerifier(cache.NewLRUCache(cacheSize)) 33 ) 34 35 const ( 36 PrivKeyName = "tendermint/PrivKeyEd25519" 37 PubKeyName = "tendermint/PubKeyEd25519" 38 // PubKeySize is is the size, in bytes, of public keys as used in this package. 39 PubKeySize = 32 40 // PrivateKeySize is the size, in bytes, of private keys as used in this package. 41 PrivateKeySize = 64 42 // Size of an Edwards25519 signature. Namely the size of a compressed 43 // Edwards25519 point, and a field element. Both of which are 32 bytes. 44 SignatureSize = 64 45 // SeedSize is the size, in bytes, of private key seeds. These are the 46 // private key representations used by RFC 8032. 47 SeedSize = 32 48 49 KeyType = "ed25519" 50 51 // cacheSize is the number of public keys that will be cached in 52 // an expanded format for repeated signature verification. 53 // 54 // TODO/perf: Either this should exclude single verification, or be 55 // tuned to `> validatorSize + maxTxnsPerBlock` to avoid cache 56 // thrashing. 57 cacheSize = 4096 58 ) 59 60 func init() { 61 tmjson.RegisterType(PubKey{}, PubKeyName) 62 tmjson.RegisterType(PrivKey{}, PrivKeyName) 63 64 jsontypes.MustRegister(PubKey{}) 65 jsontypes.MustRegister(PrivKey{}) 66 } 67 68 // PrivKey implements crypto.PrivKey. 69 type PrivKey []byte 70 71 // TypeTag satisfies the jsontypes.Tagged interface. 72 func (PrivKey) TypeTag() string { return PrivKeyName } 73 74 // Bytes returns the privkey byte format. 75 func (privKey PrivKey) Bytes() []byte { 76 return []byte(privKey) 77 } 78 79 // Sign produces a signature on the provided message. 80 // This assumes the privkey is wellformed in the golang format. 81 // The first 32 bytes should be random, 82 // corresponding to the normal ed25519 private key. 83 // The latter 32 bytes should be the compressed public key. 84 // If these conditions aren't met, Sign will panic or produce an 85 // incorrect signature. 86 func (privKey PrivKey) Sign(msg []byte) ([]byte, error) { 87 signatureBytes := ed25519.Sign(ed25519.PrivateKey(privKey), msg) 88 return signatureBytes, nil 89 } 90 91 // PubKey gets the corresponding public key from the private key. 92 // 93 // Panics if the private key is not initialized. 94 func (privKey PrivKey) PubKey() crypto.PubKey { 95 // If the latter 32 bytes of the privkey are all zero, privkey is not 96 // initialized. 97 initialized := false 98 for _, v := range privKey[32:] { 99 if v != 0 { 100 initialized = true 101 break 102 } 103 } 104 105 if !initialized { 106 panic("Expected ed25519 PrivKey to include concatenated pubkey bytes") 107 } 108 109 pubkeyBytes := make([]byte, PubKeySize) 110 copy(pubkeyBytes, privKey[32:]) 111 return PubKey(pubkeyBytes) 112 } 113 114 // Equals - you probably don't need to use this. 115 // Runs in constant time based on length of the keys. 116 func (privKey PrivKey) Equals(other crypto.PrivKey) bool { 117 if otherEd, ok := other.(PrivKey); ok { 118 return subtle.ConstantTimeCompare(privKey[:], otherEd[:]) == 1 119 } 120 121 return false 122 } 123 124 func (privKey PrivKey) Type() string { 125 return KeyType 126 } 127 128 // GenPrivKey generates a new ed25519 private key. 129 // It uses OS randomness in conjunction with the current global random seed 130 // in tendermint/libs/common to generate the private key. 131 func GenPrivKey() PrivKey { 132 return genPrivKey(rand.Reader) 133 } 134 135 // genPrivKey generates a new ed25519 private key using the provided reader. 136 func genPrivKey(rand io.Reader) PrivKey { 137 _, priv, err := ed25519.GenerateKey(rand) 138 if err != nil { 139 panic(err) 140 } 141 142 return PrivKey(priv) 143 } 144 145 // GenPrivKeyFromSecret hashes the secret with SHA2, and uses 146 // that 32 byte output to create the private key. 147 // NOTE: secret should be the output of a KDF like bcrypt, 148 // if it's derived from user input. 149 func GenPrivKeyFromSecret(secret []byte) PrivKey { 150 seed := sha256.Sum256(secret) 151 return PrivKey(ed25519.NewKeyFromSeed(seed[:])) 152 } 153 154 //------------------------------------- 155 156 var _ crypto.PubKey = PubKey{} 157 158 // PubKeyEd25519 implements crypto.PubKey for the Ed25519 signature scheme. 159 type PubKey []byte 160 161 // TypeTag satisfies the jsontypes.Tagged interface. 162 func (PubKey) TypeTag() string { return PubKeyName } 163 164 // Address is the SHA256-20 of the raw pubkey bytes. 165 func (pubKey PubKey) Address() crypto.Address { 166 if len(pubKey) != PubKeySize { 167 panic("pubkey is incorrect size") 168 } 169 return crypto.AddressHash(pubKey) 170 } 171 172 // Bytes returns the PubKey byte format. 173 func (pubKey PubKey) Bytes() []byte { 174 return []byte(pubKey) 175 } 176 177 func (pubKey PubKey) VerifySignature(msg []byte, sig []byte) bool { 178 // make sure we use the same algorithm to sign 179 if len(sig) != SignatureSize { 180 return false 181 } 182 183 return cachingVerifier.VerifyWithOptions(ed25519.PublicKey(pubKey), msg, sig, verifyOptions) 184 } 185 186 func (pubKey PubKey) String() string { 187 return fmt.Sprintf("PubKeyEd25519{%X}", []byte(pubKey)) 188 } 189 190 func (pubKey PubKey) Type() string { 191 return KeyType 192 } 193 194 func (pubKey PubKey) Equals(other crypto.PubKey) bool { 195 if otherEd, ok := other.(PubKey); ok { 196 return bytes.Equal(pubKey[:], otherEd[:]) 197 } 198 199 return false 200 } 201 202 var _ crypto.BatchVerifier = &BatchVerifier{} 203 204 // BatchVerifier implements batch verification for ed25519. 205 type BatchVerifier struct { 206 *ed25519.BatchVerifier 207 } 208 209 func NewBatchVerifier() crypto.BatchVerifier { 210 return &BatchVerifier{ed25519.NewBatchVerifier()} 211 } 212 213 func (b *BatchVerifier) Add(key crypto.PubKey, msg, signature []byte) error { 214 pkEd, ok := key.(PubKey) 215 if !ok { 216 return fmt.Errorf("pubkey is not Ed25519") 217 } 218 219 pkBytes := pkEd.Bytes() 220 221 if l := len(pkBytes); l != PubKeySize { 222 return fmt.Errorf("pubkey size is incorrect; expected: %d, got %d", PubKeySize, l) 223 } 224 225 // check that the signature is the correct length 226 if len(signature) != SignatureSize { 227 return errors.New("invalid signature") 228 } 229 230 cachingVerifier.AddWithOptions(b.BatchVerifier, ed25519.PublicKey(pkBytes), msg, signature, verifyOptions) 231 232 return nil 233 } 234 235 func (b *BatchVerifier) Verify() (bool, []bool) { 236 return b.BatchVerifier.Verify(rand.Reader) 237 }