package ed25519
import (
"bytes"
"crypto/internal/fips140"
"crypto/internal/fips140/drbg"
"crypto/internal/fips140/edwards25519"
"crypto/internal/fips140/sha512"
"errors"
"strconv"
)
const (
seedSize = 32
publicKeySize = 32
privateKeySize = seedSize + publicKeySize
signatureSize = 64
sha512Size = 64
)
type PrivateKey struct {
seed [seedSize ]byte
pub [publicKeySize ]byte
s edwards25519 .Scalar
prefix [sha512Size / 2 ]byte
}
func (priv *PrivateKey ) Bytes () []byte {
k := make ([]byte , 0 , privateKeySize )
k = append (k , priv .seed [:]...)
k = append (k , priv .pub [:]...)
return k
}
func (priv *PrivateKey ) Seed () []byte {
seed := priv .seed
return seed [:]
}
func (priv *PrivateKey ) PublicKey () []byte {
pub := priv .pub
return pub [:]
}
type PublicKey struct {
a edwards25519 .Point
aBytes [32 ]byte
}
func (pub *PublicKey ) Bytes () []byte {
a := pub .aBytes
return a [:]
}
func GenerateKey () (*PrivateKey , error ) {
priv := &PrivateKey {}
return generateKey (priv )
}
func generateKey(priv *PrivateKey ) (*PrivateKey , error ) {
fips140 .RecordApproved ()
drbg .Read (priv .seed [:])
precomputePrivateKey (priv )
if err := fipsPCT (priv ); err != nil {
panic (err )
}
return priv , nil
}
func NewPrivateKeyFromSeed (seed []byte ) (*PrivateKey , error ) {
priv := &PrivateKey {}
return newPrivateKeyFromSeed (priv , seed )
}
func newPrivateKeyFromSeed(priv *PrivateKey , seed []byte ) (*PrivateKey , error ) {
fips140 .RecordApproved ()
if l := len (seed ); l != seedSize {
return nil , errors .New ("ed25519: bad seed length: " + strconv .Itoa (l ))
}
copy (priv .seed [:], seed )
precomputePrivateKey (priv )
if err := fipsPCT (priv ); err != nil {
panic (err )
}
return priv , nil
}
func precomputePrivateKey(priv *PrivateKey ) {
hs := sha512 .New ()
hs .Write (priv .seed [:])
h := hs .Sum (make ([]byte , 0 , sha512Size ))
s , err := priv .s .SetBytesWithClamping (h [:32 ])
if err != nil {
panic ("ed25519: internal error: setting scalar failed" )
}
A := (&edwards25519 .Point {}).ScalarBaseMult (s )
copy (priv .pub [:], A .Bytes ())
copy (priv .prefix [:], h [32 :])
}
func NewPrivateKey (priv []byte ) (*PrivateKey , error ) {
p := &PrivateKey {}
return newPrivateKey (p , priv )
}
func newPrivateKey(priv *PrivateKey , privBytes []byte ) (*PrivateKey , error ) {
fips140 .RecordApproved ()
if l := len (privBytes ); l != privateKeySize {
return nil , errors .New ("ed25519: bad private key length: " + strconv .Itoa (l ))
}
copy (priv .seed [:], privBytes [:32 ])
hs := sha512 .New ()
hs .Write (priv .seed [:])
h := hs .Sum (make ([]byte , 0 , sha512Size ))
if _ , err := priv .s .SetBytesWithClamping (h [:32 ]); err != nil {
panic ("ed25519: internal error: setting scalar failed" )
}
copy (priv .pub [:], privBytes [32 :])
copy (priv .prefix [:], h [32 :])
if err := fipsPCT (priv ); err != nil {
return nil , err
}
return priv , nil
}
func NewPublicKey (pub []byte ) (*PublicKey , error ) {
p := &PublicKey {}
return newPublicKey (p , pub )
}
func newPublicKey(pub *PublicKey , pubBytes []byte ) (*PublicKey , error ) {
if l := len (pubBytes ); l != publicKeySize {
return nil , errors .New ("ed25519: bad public key length: " + strconv .Itoa (l ))
}
if _ , err := pub .a .SetBytes (pubBytes ); err != nil {
return nil , errors .New ("ed25519: bad public key" )
}
copy (pub .aBytes [:], pubBytes )
return pub , nil
}
const (
domPrefixPure = ""
domPrefixPh = "SigEd25519 no Ed25519 collisions\x01"
domPrefixCtx = "SigEd25519 no Ed25519 collisions\x00"
)
func Sign (priv *PrivateKey , message []byte ) []byte {
signature := make ([]byte , signatureSize )
return sign (signature , priv , message )
}
func sign(signature []byte , priv *PrivateKey , message []byte ) []byte {
fipsSelfTest ()
fips140 .RecordApproved ()
return signWithDom (signature , priv , message , domPrefixPure , "" )
}
func SignPH (priv *PrivateKey , message []byte , context string ) ([]byte , error ) {
signature := make ([]byte , signatureSize )
return signPH (signature , priv , message , context )
}
func signPH(signature []byte , priv *PrivateKey , message []byte , context string ) ([]byte , error ) {
fipsSelfTest ()
fips140 .RecordApproved ()
if l := len (message ); l != sha512Size {
return nil , errors .New ("ed25519: bad Ed25519ph message hash length: " + strconv .Itoa (l ))
}
if l := len (context ); l > 255 {
return nil , errors .New ("ed25519: bad Ed25519ph context length: " + strconv .Itoa (l ))
}
return signWithDom (signature , priv , message , domPrefixPh , context ), nil
}
func SignCtx (priv *PrivateKey , message []byte , context string ) ([]byte , error ) {
signature := make ([]byte , signatureSize )
return signCtx (signature , priv , message , context )
}
func signCtx(signature []byte , priv *PrivateKey , message []byte , context string ) ([]byte , error ) {
fipsSelfTest ()
fips140 .RecordNonApproved ()
if l := len (context ); l > 255 {
return nil , errors .New ("ed25519: bad Ed25519ctx context length: " + strconv .Itoa (l ))
}
return signWithDom (signature , priv , message , domPrefixCtx , context ), nil
}
func signWithDom(signature []byte , priv *PrivateKey , message []byte , domPrefix , context string ) []byte {
mh := sha512 .New ()
if domPrefix != domPrefixPure {
mh .Write ([]byte (domPrefix ))
mh .Write ([]byte {byte (len (context ))})
mh .Write ([]byte (context ))
}
mh .Write (priv .prefix [:])
mh .Write (message )
messageDigest := make ([]byte , 0 , sha512Size )
messageDigest = mh .Sum (messageDigest )
r , err := edwards25519 .NewScalar ().SetUniformBytes (messageDigest )
if err != nil {
panic ("ed25519: internal error: setting scalar failed" )
}
R := (&edwards25519 .Point {}).ScalarBaseMult (r )
kh := sha512 .New ()
if domPrefix != domPrefixPure {
kh .Write ([]byte (domPrefix ))
kh .Write ([]byte {byte (len (context ))})
kh .Write ([]byte (context ))
}
kh .Write (R .Bytes ())
kh .Write (priv .pub [:])
kh .Write (message )
hramDigest := make ([]byte , 0 , sha512Size )
hramDigest = kh .Sum (hramDigest )
k , err := edwards25519 .NewScalar ().SetUniformBytes (hramDigest )
if err != nil {
panic ("ed25519: internal error: setting scalar failed" )
}
S := edwards25519 .NewScalar ().MultiplyAdd (k , &priv .s , r )
copy (signature [:32 ], R .Bytes ())
copy (signature [32 :], S .Bytes ())
return signature
}
func Verify (pub *PublicKey , message , sig []byte ) error {
return verify (pub , message , sig )
}
func verify(pub *PublicKey , message , sig []byte ) error {
fipsSelfTest ()
fips140 .RecordApproved ()
return verifyWithDom (pub , message , sig , domPrefixPure , "" )
}
func VerifyPH (pub *PublicKey , message []byte , sig []byte , context string ) error {
fipsSelfTest ()
fips140 .RecordApproved ()
if l := len (message ); l != sha512Size {
return errors .New ("ed25519: bad Ed25519ph message hash length: " + strconv .Itoa (l ))
}
if l := len (context ); l > 255 {
return errors .New ("ed25519: bad Ed25519ph context length: " + strconv .Itoa (l ))
}
return verifyWithDom (pub , message , sig , domPrefixPh , context )
}
func VerifyCtx (pub *PublicKey , message []byte , sig []byte , context string ) error {
fipsSelfTest ()
fips140 .RecordNonApproved ()
if l := len (context ); l > 255 {
return errors .New ("ed25519: bad Ed25519ctx context length: " + strconv .Itoa (l ))
}
return verifyWithDom (pub , message , sig , domPrefixCtx , context )
}
func verifyWithDom(pub *PublicKey , message , sig []byte , domPrefix , context string ) error {
if l := len (sig ); l != signatureSize {
return errors .New ("ed25519: bad signature length: " + strconv .Itoa (l ))
}
if sig [63 ]&224 != 0 {
return errors .New ("ed25519: invalid signature" )
}
kh := sha512 .New ()
if domPrefix != domPrefixPure {
kh .Write ([]byte (domPrefix ))
kh .Write ([]byte {byte (len (context ))})
kh .Write ([]byte (context ))
}
kh .Write (sig [:32 ])
kh .Write (pub .aBytes [:])
kh .Write (message )
hramDigest := make ([]byte , 0 , sha512Size )
hramDigest = kh .Sum (hramDigest )
k , err := edwards25519 .NewScalar ().SetUniformBytes (hramDigest )
if err != nil {
panic ("ed25519: internal error: setting scalar failed" )
}
S , err := edwards25519 .NewScalar ().SetCanonicalBytes (sig [32 :])
if err != nil {
return errors .New ("ed25519: invalid signature" )
}
minusA := (&edwards25519 .Point {}).Negate (&pub .a )
R := (&edwards25519 .Point {}).VarTimeDoubleScalarBaseMult (k , minusA , S )
if !bytes .Equal (sig [:32 ], R .Bytes ()) {
return errors .New ("ed25519: invalid signature" )
}
return nil
}
The pages are generated with Golds v0.7.3-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .