dns-api/ed25519/key.go
DataHoarder e258da67dd
All checks were successful
continuous-integration/drone/push Build is passing
Added DNS zones, DNSSEC signing
2022-06-14 20:20:13 +02:00

122 lines
3.8 KiB
Go

package ed25519
import (
"bytes"
"crypto"
goEd25519 "crypto/ed25519"
"crypto/sha512"
"errors"
"filippo.io/edwards25519"
"io"
"strconv"
)
type PrivateKey []byte
type PublicKey goEd25519.PublicKey
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 = PrivateKeyFormSize + PublicKeySize
PrivateKeyFormSize = 64
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
SignatureSize = 64
)
// Sign signs the message with privateKey and returns a signature. It will
// panic if len(privateKey) is not PrivateKeySize.
func Sign(privateKey PrivateKey, message []byte) []byte {
// Outline the function body so that the returned signature can be
// stack-allocated.
signature := make([]byte, SignatureSize)
sign(signature, privateKey, message)
return signature
}
// 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 (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
if opts.HashFunc() != crypto.Hash(0) {
return nil, errors.New("ed25519: cannot sign hashed message")
}
return Sign(priv, message), nil
}
func sign(signature, privateKey, message []byte) {
if l := len(privateKey); l != PrivateKeySize {
panic("ed25519: bad private key length: " + strconv.Itoa(l))
}
privateKey, publicKey := privateKey[:PrivateKeyFormSize], privateKey[PrivateKeyFormSize:]
s, _ := edwards25519.NewScalar().SetBytesWithClamping(privateKey[:32])
prefix := privateKey[32:]
mh := sha512.New()
mh.Write(prefix)
mh.Write(message)
messageDigest := make([]byte, 0, sha512.Size)
messageDigest = mh.Sum(messageDigest)
r, _ := edwards25519.NewScalar().SetUniformBytes(messageDigest)
R := (&edwards25519.Point{}).ScalarBaseMult(r)
kh := sha512.New()
kh.Write(R.Bytes())
kh.Write(publicKey)
kh.Write(message)
hramDigest := make([]byte, 0, sha512.Size)
hramDigest = kh.Sum(hramDigest)
k, _ := edwards25519.NewScalar().SetUniformBytes(hramDigest)
S := edwards25519.NewScalar().MultiplyAdd(k, s, r)
copy(signature[:32], R.Bytes())
copy(signature[32:], S.Bytes())
}
// Verify reports whether sig is a valid signature of message by publicKey. It
// will panic if len(publicKey) is not PublicKeySize.
func Verify(publicKey PublicKey, message, sig []byte) bool {
return goEd25519.Verify(goEd25519.PublicKey(publicKey), message, sig)
}
// NewKeyFromSeed calculates a private key from a seed. It will panic if
// len(seed) is not SeedSize. This function is provided for interoperability
// with RFC 8032. RFC 8032's private keys correspond to seeds in this
// package.
func NewKeyFromSeed(seed []byte) PrivateKey {
return NewKeyFromStandard(goEd25519.NewKeyFromSeed(seed))
}
// GenerateKey generates a public/private key pair using entropy from rand.
// If rand is nil, crypto/rand.Reader will be used.
func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
publicKey, privateKey, err := goEd25519.GenerateKey(rand)
if err != nil {
return nil, nil, err
}
return PublicKey(publicKey), NewKeyFromStandard(privateKey), nil
}
// Public returns the PublicKey corresponding to priv.
func (priv PrivateKey) Public() crypto.PublicKey {
publicKey := make([]byte, PublicKeySize)
copy(publicKey, priv[PrivateKeyFormSize:])
return PublicKey(publicKey)
}
// Equal reports whether priv and x have the same value.
func (priv PrivateKey) Equal(x crypto.PrivateKey) bool {
xx, ok := x.(PrivateKey)
if !ok {
return false
}
return bytes.Equal(priv, xx)
}