1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
|
// 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"
"github.com/cretz/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)
}
|