package sr25519 import ( "encoding/json" "fmt" "io" "github.com/oasisprotocol/curve25519-voi/primitives/sr25519" "github.com/tendermint/tendermint/crypto" ) var ( _ crypto.PrivKey = PrivKey{} signingCtx = sr25519.NewSigningContext([]byte{}) ) const ( // PrivKeySize is the size of a sr25519 signature in bytes. PrivKeySize = sr25519.MiniSecretKeySize KeyType = "sr25519" ) // PrivKey implements crypto.PrivKey. type PrivKey struct { msk sr25519.MiniSecretKey kp *sr25519.KeyPair } // TypeTag satisfies the jsontypes.Tagged interface. func (PrivKey) TypeTag() string { return PrivKeyName } // Bytes returns the byte-encoded PrivKey. func (privKey PrivKey) Bytes() []byte { if privKey.kp == nil { return nil } return privKey.msk[:] } // Sign produces a signature on the provided message. func (privKey PrivKey) Sign(msg []byte) ([]byte, error) { if privKey.kp == nil { return nil, fmt.Errorf("sr25519: uninitialized private key") } st := signingCtx.NewTranscriptBytes(msg) sig, err := privKey.kp.Sign(crypto.CReader(), st) if err != nil { return nil, fmt.Errorf("sr25519: failed to sign message: %w", err) } sigBytes, err := sig.MarshalBinary() if err != nil { return nil, fmt.Errorf("sr25519: failed to serialize signature: %w", err) } return sigBytes, nil } // PubKey gets the corresponding public key from the private key. // // Panics if the private key is not initialized. func (privKey PrivKey) PubKey() crypto.PubKey { if privKey.kp == nil { panic("sr25519: uninitialized private key") } b, err := privKey.kp.PublicKey().MarshalBinary() if err != nil { panic("sr25519: failed to serialize public key: " + err.Error()) } return PubKey(b) } // Equals - you probably don't need to use this. // Runs in constant time based on length of the keys. func (privKey PrivKey) Equals(other crypto.PrivKey) bool { if otherSr, ok := other.(PrivKey); ok { return privKey.msk.Equal(&otherSr.msk) } return false } func (privKey PrivKey) Type() string { return KeyType } func (privKey PrivKey) MarshalJSON() ([]byte, error) { var b []byte // Handle uninitialized private keys gracefully. if privKey.kp != nil { b = privKey.Bytes() } return json.Marshal(b) } func (privKey *PrivKey) UnmarshalJSON(data []byte) error { for i := range privKey.msk { privKey.msk[i] = 0 } privKey.kp = nil var b []byte if err := json.Unmarshal(data, &b); err != nil { return fmt.Errorf("sr25519: failed to deserialize JSON: %w", err) } if len(b) == 0 { return nil } msk, err := sr25519.NewMiniSecretKeyFromBytes(b) if err != nil { return err } sk := msk.ExpandEd25519() privKey.msk = *msk privKey.kp = sk.KeyPair() return nil } // GenPrivKey generates a new sr25519 private key. // It uses OS randomness in conjunction with the current global random seed // in tendermint/libs/common to generate the private key. func GenPrivKey() PrivKey { return genPrivKey(crypto.CReader()) } func genPrivKey(rng io.Reader) PrivKey { msk, err := sr25519.GenerateMiniSecretKey(rng) if err != nil { panic("sr25519: failed to generate MiniSecretKey: " + err.Error()) } sk := msk.ExpandEd25519() return PrivKey{ msk: *msk, kp: sk.KeyPair(), } } // GenPrivKeyFromSecret hashes the secret with SHA2, and uses // that 32 byte output to create the private key. // NOTE: secret should be the output of a KDF like bcrypt, // if it's derived from user input. func GenPrivKeyFromSecret(secret []byte) PrivKey { seed := crypto.Sha256(secret) // Not Ripemd160 because we want 32 bytes. var privKey PrivKey if err := privKey.msk.UnmarshalBinary(seed); err != nil { panic("sr25519: failed to deserialize MiniSecretKey: " + err.Error()) } sk := privKey.msk.ExpandEd25519() privKey.kp = sk.KeyPair() return privKey }