package ed25519
import (
"bytes"
"crypto"
"crypto/ed25519/internal/edwards25519"
cryptorand "crypto/rand"
"crypto/sha512"
"errors"
"io"
"strconv"
)
const (
PublicKeySize = 32
PrivateKeySize = 64
SignatureSize = 64
SeedSize = 32
)
type PublicKey []byte
func (pub PublicKey ) Equal (x crypto .PublicKey ) bool {
xx , ok := x .(PublicKey )
if !ok {
return false
}
return bytes .Equal (pub , xx )
}
type PrivateKey []byte
func (priv PrivateKey ) Public () crypto .PublicKey {
publicKey := make ([]byte , PublicKeySize )
copy (publicKey , priv [32 :])
return PublicKey (publicKey )
}
func (priv PrivateKey ) Equal (x crypto .PrivateKey ) bool {
xx , ok := x .(PrivateKey )
if !ok {
return false
}
return bytes .Equal (priv , xx )
}
func (priv PrivateKey ) Seed () []byte {
seed := make ([]byte , SeedSize )
copy (seed , priv [:32 ])
return seed
}
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 GenerateKey (rand io .Reader ) (PublicKey , PrivateKey , error ) {
if rand == nil {
rand = cryptorand .Reader
}
seed := make ([]byte , SeedSize )
if _ , err := io .ReadFull (rand , seed ); err != nil {
return nil , nil , err
}
privateKey := NewKeyFromSeed (seed )
publicKey := make ([]byte , PublicKeySize )
copy (publicKey , privateKey [32 :])
return publicKey , privateKey , nil
}
func NewKeyFromSeed (seed []byte ) PrivateKey {
privateKey := make ([]byte , PrivateKeySize )
newKeyFromSeed (privateKey , seed )
return privateKey
}
func newKeyFromSeed (privateKey , seed []byte ) {
if l := len (seed ); l != SeedSize {
panic ("ed25519: bad seed length: " + strconv .Itoa (l ))
}
h := sha512 .Sum512 (seed )
s := edwards25519 .NewScalar ().SetBytesWithClamping (h [:32 ])
A := (&edwards25519 .Point {}).ScalarBaseMult (s )
publicKey := A .Bytes ()
copy (privateKey , seed )
copy (privateKey [32 :], publicKey )
}
func Sign (privateKey PrivateKey , message []byte ) []byte {
signature := make ([]byte , SignatureSize )
sign (signature , privateKey , message )
return signature
}
func sign (signature , privateKey , message []byte ) {
if l := len (privateKey ); l != PrivateKeySize {
panic ("ed25519: bad private key length: " + strconv .Itoa (l ))
}
seed , publicKey := privateKey [:SeedSize ], privateKey [SeedSize :]
h := sha512 .Sum512 (seed )
s := edwards25519 .NewScalar ().SetBytesWithClamping (h [:32 ])
prefix := h [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 ())
}
func Verify (publicKey PublicKey , message , sig []byte ) bool {
if l := len (publicKey ); l != PublicKeySize {
panic ("ed25519: bad public key length: " + strconv .Itoa (l ))
}
if len (sig ) != SignatureSize || sig [63 ]&224 != 0 {
return false
}
A , err := (&edwards25519 .Point {}).SetBytes (publicKey )
if err != nil {
return false
}
kh := sha512 .New ()
kh .Write (sig [:32 ])
kh .Write (publicKey )
kh .Write (message )
hramDigest := make ([]byte , 0 , sha512 .Size )
hramDigest = kh .Sum (hramDigest )
k := edwards25519 .NewScalar ().SetUniformBytes (hramDigest )
S , err := edwards25519 .NewScalar ().SetCanonicalBytes (sig [32 :])
if err != nil {
return false
}
minusA := (&edwards25519 .Point {}).Negate (A )
R := (&edwards25519 .Point {}).VarTimeDoubleScalarBaseMult (k , minusA , S )
return bytes .Equal (sig [:32 ], R .Bytes ())
}