git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/crypto/ed25519.go (about) 1 package crypto 2 3 import ( 4 "crypto" 5 "crypto/ed25519" 6 "crypto/sha512" 7 "errors" 8 "io" 9 "math/big" 10 ) 11 12 const ( 13 // Ed25519PublicKeySize is the size, in bytes, of public keys as used in this package. 14 Ed25519PublicKeySize = ed25519.PublicKeySize 15 16 // Ed25519PrivateKeySize is the size, in bytes, of private keys as used in this package. 17 Ed25519PrivateKeySize = ed25519.PrivateKeySize 18 19 // Ed25519SignatureSize is the size, in bytes, of signatures generated and verified by this package. 20 Ed25519SignatureSize = ed25519.SignatureSize 21 22 // Ed25519SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032. 23 Ed25519SeedSize = ed25519.SeedSize 24 25 // Ed25519SignerOpts must be used for `PrivateKey.Sign` 26 Ed25519SignerOpts = crypto.Hash(0) 27 ) 28 29 // Ed25519PublicKey is the type of Ed25519 public keys. 30 type Ed25519PublicKey ed25519.PublicKey 31 32 // Verify reports whether sig is a valid signature of message by publicKey. 33 // returns true if signature is valid. false otherwise. 34 func (publicKey Ed25519PublicKey) Verify(message, signature []byte) (bool, error) { 35 if len(publicKey) != Ed25519PublicKeySize { 36 return false, errors.New("crypto: Invalid Ed25519 public key size") 37 } 38 39 return ed25519.Verify(ed25519.PublicKey(publicKey), message, signature), nil 40 } 41 42 var curve25519P, _ = new(big.Int).SetString("57896044618658097711785492504343953926634992332820282019728792003956564819949", 10) 43 44 // ToCurve25519PublicKey returns the corresponding Curve25519 public key. 45 // 46 // See here for more details: https://blog.filippo.io/using-ed25519-keys-for-encryption 47 func (publicKey Ed25519PublicKey) ToCurve25519PublicKey() Curve25519PublicKey { 48 // taken from https://github.com/FiloSottile/age/blob/master/internal/agessh/agessh.go#L179 49 50 // ed25519.PublicKey is a little endian representation of the y-coordinate, 51 // with the most significant bit set based on the sign of the x-coordinate. 52 bigEndianY := make([]byte, Ed25519PublicKeySize) 53 for i, b := range publicKey { 54 bigEndianY[Ed25519PublicKeySize-i-1] = b 55 } 56 bigEndianY[0] &= 0b0111_1111 57 58 // The Montgomery u-coordinate is derived through the bilinear map 59 // 60 // u = (1 + y) / (1 - y) 61 // 62 // See https://blog.filippo.io/using-ed25519-keys-for-encryption. 63 y := new(big.Int).SetBytes(bigEndianY) 64 denom := big.NewInt(1) 65 denom.ModInverse(denom.Sub(denom, y), curve25519P) // 1 / (1 - y) 66 u := y.Mul(y.Add(y, big.NewInt(1)), denom) 67 u.Mod(u, curve25519P) 68 69 out := make([]byte, Curve25519PublicKeySize) 70 uBytes := u.Bytes() 71 for i, b := range uBytes { 72 out[len(uBytes)-i-1] = b 73 } 74 75 return out 76 } 77 78 func (publicKey Ed25519PublicKey) Bytes() []byte { 79 return publicKey[:] 80 } 81 82 func NewEd25519PublicKeyFromBytes(key []byte) (publicKey Ed25519PublicKey, err error) { 83 if len(key) != Ed25519PublicKeySize { 84 err = errors.New("crypto: Invalid Ed25519 public key size") 85 return 86 } 87 publicKey = Ed25519PublicKey(key) 88 return 89 } 90 91 // Ed25519PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer. 92 type Ed25519PrivateKey ed25519.PrivateKey 93 94 // Sign signs the given message with priv. 95 // Ed25519 performs two passes over messages to be signed and therefore cannot 96 // handle pre-hashed messages. Thus opts.HashFunc() must return zero to 97 // indicate the message hasn't been hashed. This can be achieved by passing 98 // crypto.Ed25519SignerOpts as the value for opts. 99 func (privateKey Ed25519PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) { 100 if len(privateKey) != Ed25519PrivateKeySize { 101 return nil, errors.New("crpyto: Invalid Ed25519 private key size") 102 } 103 104 return ed25519.Sign(ed25519.PrivateKey(privateKey), message), nil 105 } 106 107 // ToCurve25519PrivateKey returns a corresponding Curve25519 private key. 108 // 109 // See here for more details: https://blog.filippo.io/using-ed25519-keys-for-encryption 110 func (privateKey Ed25519PrivateKey) ToCurve25519PrivateKey() Curve25519PrivateKey { 111 // taken from https://github.com/FiloSottile/age/blob/292c3aaeea0695dbba356dfe18a70f10efb17d75/internal/agessh/agessh.go#L294 112 h := sha512.New() 113 h.Write(privateKey.Seed()) 114 out := h.Sum(nil) 115 return out[:Curve25519PrivateKeySize] 116 } 117 118 // Public returns the Ed25519PublicKey corresponding to priv. 119 func (privateKey Ed25519PrivateKey) Public() Ed25519PublicKey { 120 return Ed25519PublicKey(ed25519.PrivateKey(privateKey).Public().(ed25519.PublicKey)) 121 } 122 123 // Seed returns the private key seed corresponding to priv. It is provided for interoperability 124 // with RFC 8032. RFC 8032's private keys correspond to seeds in this package. 125 func (privateKey Ed25519PrivateKey) Seed() []byte { 126 return ed25519.PrivateKey(privateKey).Seed() 127 } 128 129 func (privateKey Ed25519PrivateKey) Bytes() []byte { 130 return privateKey[:] 131 } 132 133 func NewEd25519PrivateKeyFromBytes(key []byte) (privateKey Ed25519PrivateKey, err error) { 134 if len(key) != Ed25519PrivateKeySize { 135 err = errors.New("crypto: Invalid Ed25519 private key size") 136 return 137 } 138 privateKey = Ed25519PrivateKey(key) 139 return 140 } 141 142 // GenerateEd25519KeyPair generates a public/private Ed25519 key pair 143 func GenerateEd25519KeyPair() (Ed25519PublicKey, Ed25519PrivateKey, error) { 144 public, private, err := ed25519.GenerateKey(nil) 145 return Ed25519PublicKey(public), Ed25519PrivateKey(private), err 146 } 147 148 // NewEd25519PrivateKeyFromSeed calculates a private key from a seed. It will panic if 149 // len(seed) is not SeedSize. This function is provided for interoperability 150 // with RFC 8032. RFC 8032's private keys correspond to seeds in this 151 // package. 152 func NewEd25519PrivateKeyFromSeed(seed []byte) (Ed25519PrivateKey, error) { 153 if len(seed) != Ed25519SeedSize { 154 return nil, errors.New("crypto: Invalid Ed25519 seed size") 155 } 156 157 private := ed25519.NewKeyFromSeed(seed) 158 return Ed25519PrivateKey(private), nil 159 }