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  }