// Package ed25519 implements Tor/BitTorrent-like ed25519 keys. // // See the following stack overflow post for details on why // golang.org/x/crypto/ed25519 can't be used: // https://stackoverflow.com/questions/44810708/ed25519-public-result-is-different package ed25519 import ( "crypto" "crypto/rand" "crypto/sha512" "errors" "io" "git.openprivacy.ca/openprivacy/bine/torutil/ed25519/internal/edwards25519" "golang.org/x/crypto/ed25519" ) const ( // PublicKeySize is the size, in bytes, of public keys as used in this package. PublicKeySize = 32 // PrivateKeySize is the size, in bytes, of private keys as used in this package. PrivateKeySize = 64 // SignatureSize is the size, in bytes, of signatures generated and verified by this package. SignatureSize = 64 ) // PrivateKey is a 64-byte Ed25519 private key. Unlike // golang.org/x/crypto/ed25519, this is just the digest and does not contain // the public key within it. Instead call PublicKey() or better, call KeyPair() // which stores the precomputed public key. type PrivateKey []byte // PublicKey is a 32-byte Ed25519 public key. type PublicKey []byte // FromCryptoPrivateKey converts a Go private key to the one in this package. func FromCryptoPrivateKey(key ed25519.PrivateKey) KeyPair { digest := sha512.Sum512(key[:32]) digest[0] &= 248 digest[31] &= 127 digest[31] |= 64 return &precomputedKeyPair{PrivateKeyBytes: digest[:], PublicKeyBytes: PublicKey(key[32:])} } // FromCryptoPublicKey converts a Go public key to the one in this package. func FromCryptoPublicKey(key ed25519.PublicKey) PublicKey { return PublicKey(key) } // KeyPair returns a new key pair with the public key precomputed. func (p PrivateKey) KeyPair() KeyPair { return &precomputedKeyPair{PrivateKeyBytes: p, PublicKeyBytes: p.PublicKey()} } // PrivateKey simply returns itself. Implements KeyPair.PrivateKey. func (p PrivateKey) PrivateKey() PrivateKey { return p } // Public simply delegates to PublicKey() to satisfy crypto.Signer. This method // does a bit more work than the traditional Go ed25519's private key's Public() // method so developers are encouraged to reuse the result or use KeyPair() // which stores this value. func (p PrivateKey) Public() crypto.PublicKey { return p.PublicKey() } // PublicKey generates a public key for this private key. This method does a bit // more work than the traditional Go ed25519's private key's Public() method so // developers are encouraged to reuse the result or use KeyPair() which stores // this value. Implements KeyPair.PublicKey. func (p PrivateKey) PublicKey() PublicKey { var A edwards25519.ExtendedGroupElement var hBytes [32]byte copy(hBytes[:], p[:]) edwards25519.GeScalarMultBase(&A, &hBytes) var publicKeyBytes [32]byte A.ToBytes(&publicKeyBytes) return publicKeyBytes[:] } // Sign signs the given message with priv. Ed25519 performs two passes over // messages to be signed and therefore cannot handle pre-hashed messages. Thus // opts.HashFunc() must return zero to indicate the message hasn't been hashed. // This can be achieved by passing crypto.Hash(0) as the value for opts. func (p PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) ([]byte, error) { if opts.HashFunc() != crypto.Hash(0) { return nil, errors.New("ed25519: cannot sign hashed message") } return Sign(p, message), nil } // Verify simply calls PublicKey().Verify(). Callers are encouraged to instead // store a precomputed KeyPair (via KeyPair() or GenerateKey()) and call Verify // on that. func (p PrivateKey) Verify(message []byte, sig []byte) bool { return p.PublicKey().Verify(message, sig) } // Verify simply calls the package-level function Verify(). func (p PublicKey) Verify(message []byte, sig []byte) bool { return Verify(p, message, sig) } // KeyPair is an interface for types with both keys. While PrivateKey does // implement this, it generates the PublicKey on demand. For better performance, // use the result of GenerateKey directly or call PrivateKey.KeyPair(). type KeyPair interface { crypto.Signer PrivateKey() PrivateKey PublicKey() PublicKey Verify(message []byte, sig []byte) bool } type precomputedKeyPair struct { PrivateKeyBytes PrivateKey PublicKeyBytes PublicKey } func (p *precomputedKeyPair) PrivateKey() PrivateKey { return p.PrivateKeyBytes } func (p *precomputedKeyPair) PublicKey() PublicKey { return p.PublicKeyBytes } func (p *precomputedKeyPair) Public() crypto.PublicKey { return p.PublicKey() } func (p *precomputedKeyPair) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) ([]byte, error) { if opts.HashFunc() != crypto.Hash(0) { return nil, errors.New("ed25519: cannot sign hashed message") } return Sign(p, message), nil } func (p *precomputedKeyPair) Verify(message []byte, sig []byte) bool { return p.PublicKeyBytes.Verify(message, sig) } // GenerateKey generates a public/private key pair using entropy from rand. // If rand is nil, crypto/rand.Reader will be used. func GenerateKey(rnd io.Reader) (KeyPair, error) { if rnd == nil { rnd = rand.Reader } rndByts := make([]byte, 32) if _, err := io.ReadFull(rnd, rndByts); err != nil { return nil, err } digest := sha512.Sum512(rndByts) digest[0] &= 248 digest[31] &= 127 digest[31] |= 64 return PrivateKey(digest[:]).KeyPair(), nil } // Sign signs the message with the given key pair. func Sign(keyPair KeyPair, message []byte) []byte { // Ref: https://stackoverflow.com/questions/44810708/ed25519-public-result-is-different var privateKeyA [32]byte copy(privateKeyA[:], keyPair.PrivateKey()) // we need this in an array later var messageDigest, hramDigest [64]byte h := sha512.New() h.Write(keyPair.PrivateKey()[32:]) h.Write(message) h.Sum(messageDigest[:0]) var messageDigestReduced [32]byte edwards25519.ScReduce(&messageDigestReduced, &messageDigest) var R edwards25519.ExtendedGroupElement edwards25519.GeScalarMultBase(&R, &messageDigestReduced) var encodedR [32]byte R.ToBytes(&encodedR) h.Reset() h.Write(encodedR[:]) h.Write(keyPair.PublicKey()) h.Write(message) h.Sum(hramDigest[:0]) var hramDigestReduced [32]byte edwards25519.ScReduce(&hramDigestReduced, &hramDigest) var s [32]byte edwards25519.ScMulAdd(&s, &hramDigestReduced, &privateKeyA, &messageDigestReduced) signature := make([]byte, 64) copy(signature[:], encodedR[:]) copy(signature[32:], s[:]) return signature } // Verify verifies a signed message. func Verify(p PublicKey, message []byte, sig []byte) bool { return ed25519.Verify(ed25519.PublicKey(p), message, sig) }