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 ))
}
digest := sha512 .Sum512 (seed )
digest [0 ] &= 248
digest [31 ] &= 127
digest [31 ] |= 64
var A edwards25519 .ExtendedGroupElement
var hBytes [32 ]byte
copy (hBytes [:], digest [:])
edwards25519 .GeScalarMultBase (&A , &hBytes )
var publicKeyBytes [32 ]byte
A .ToBytes (&publicKeyBytes )
copy (privateKey , seed )
copy (privateKey [32 :], publicKeyBytes [:])
}
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 ))
}
h := sha512 .New ()
h .Write (privateKey [:32 ])
var digest1 , messageDigest , hramDigest [64 ]byte
var expandedSecretKey [32 ]byte
h .Sum (digest1 [:0 ])
copy (expandedSecretKey [:], digest1 [:])
expandedSecretKey [0 ] &= 248
expandedSecretKey [31 ] &= 63
expandedSecretKey [31 ] |= 64
h .Reset ()
h .Write (digest1 [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 (privateKey [32 :])
h .Write (message )
h .Sum (hramDigest [:0 ])
var hramDigestReduced [32 ]byte
edwards25519 .ScReduce (&hramDigestReduced , &hramDigest )
var s [32 ]byte
edwards25519 .ScMulAdd (&s , &hramDigestReduced , &expandedSecretKey , &messageDigestReduced )
copy (signature [:], encodedR [:])
copy (signature [32 :], s [:])
}
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
}
var A edwards25519 .ExtendedGroupElement
var publicKeyBytes [32 ]byte
copy (publicKeyBytes [:], publicKey )
if !A .FromBytes (&publicKeyBytes ) {
return false
}
edwards25519 .FeNeg (&A .X , &A .X )
edwards25519 .FeNeg (&A .T , &A .T )
h := sha512 .New ()
h .Write (sig [:32 ])
h .Write (publicKey [:])
h .Write (message )
var digest [64 ]byte
h .Sum (digest [:0 ])
var hReduced [32 ]byte
edwards25519 .ScReduce (&hReduced , &digest )
var R edwards25519 .ProjectiveGroupElement
var s [32 ]byte
copy (s [:], sig [32 :])
if !edwards25519 .ScMinimal (&s ) {
return false
}
edwards25519 .GeDoubleScalarMultVartime (&R , &hReduced , &A , &s )
var checkR [32 ]byte
R .ToBytes (&checkR )
return bytes .Equal (sig [:32 ], checkR [:])
}