// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ed25519

import (
	
	
	
	
	
	
	
)

// See https://blog.mozilla.org/warner/2011/11/29/ed25519-keys/ for the
// components of the keys and the moving parts of the algorithm.

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 ( *PrivateKey) () []byte {
	 := make([]byte, 0, privateKeySize)
	 = append(, .seed[:]...)
	 = append(, .pub[:]...)
	return 
}

func ( *PrivateKey) () []byte {
	 := .seed
	return [:]
}

func ( *PrivateKey) () []byte {
	 := .pub
	return [:]
}

type PublicKey struct {
	a      edwards25519.Point
	aBytes [32]byte
}

func ( *PublicKey) () []byte {
	 := .aBytes
	return [:]
}

// GenerateKey generates a new Ed25519 private key pair.
func () (*PrivateKey, error) {
	 := &PrivateKey{}
	return generateKey()
}

func generateKey( *PrivateKey) (*PrivateKey, error) {
	fips140.RecordApproved()
	drbg.Read(.seed[:])
	precomputePrivateKey()
	if  := fipsPCT();  != nil {
		// This clearly can't happen, but FIPS 140-3 requires that we check.
		panic()
	}
	return , nil
}

func ( []byte) (*PrivateKey, error) {
	 := &PrivateKey{}
	return newPrivateKeyFromSeed(, )
}

func newPrivateKeyFromSeed( *PrivateKey,  []byte) (*PrivateKey, error) {
	fips140.RecordApproved()
	if  := len();  != seedSize {
		return nil, errors.New("ed25519: bad seed length: " + strconv.Itoa())
	}
	copy(.seed[:], )
	precomputePrivateKey()
	if  := fipsPCT();  != nil {
		// This clearly can't happen, but FIPS 140-3 requires that we check.
		panic()
	}
	return , nil
}

func precomputePrivateKey( *PrivateKey) {
	 := sha512.New()
	.Write(.seed[:])
	 := .Sum(make([]byte, 0, sha512Size))

	,  := .s.SetBytesWithClamping([:32])
	if  != nil {
		panic("ed25519: internal error: setting scalar failed")
	}
	 := (&edwards25519.Point{}).ScalarBaseMult()
	copy(.pub[:], .Bytes())

	copy(.prefix[:], [32:])
}

func ( []byte) (*PrivateKey, error) {
	 := &PrivateKey{}
	return newPrivateKey(, )
}

func newPrivateKey( *PrivateKey,  []byte) (*PrivateKey, error) {
	fips140.RecordApproved()
	if  := len();  != privateKeySize {
		return nil, errors.New("ed25519: bad private key length: " + strconv.Itoa())
	}

	copy(.seed[:], [:32])

	 := sha512.New()
	.Write(.seed[:])
	 := .Sum(make([]byte, 0, sha512Size))

	if ,  := .s.SetBytesWithClamping([:32]);  != nil {
		panic("ed25519: internal error: setting scalar failed")
	}
	// Note that we are not decompressing the public key point here,
	// because it takes > 20% of the time of a signature generation.
	// Signing doesn't use it as a point anyway.
	copy(.pub[:], [32:])

	copy(.prefix[:], [32:])

	if  := fipsPCT();  != nil {
		// This can happen if the application messed with the private key
		// encoding, and the public key doesn't match the seed anymore.
		return nil, 
	}

	return , nil
}

func ( []byte) (*PublicKey, error) {
	 := &PublicKey{}
	return newPublicKey(, )
}

func newPublicKey( *PublicKey,  []byte) (*PublicKey, error) {
	if  := len();  != publicKeySize {
		return nil, errors.New("ed25519: bad public key length: " + strconv.Itoa())
	}
	// SetBytes checks that the point is on the curve.
	if ,  := .a.SetBytes();  != nil {
		return nil, errors.New("ed25519: bad public key")
	}
	copy(.aBytes[:], )
	return , nil
}

// Domain separation prefixes used to disambiguate Ed25519/Ed25519ph/Ed25519ctx.
// See RFC 8032, Section 2 and Section 5.1.
const (
	// domPrefixPure is empty for pure Ed25519.
	domPrefixPure = ""
	// domPrefixPh is dom2(phflag=1) for Ed25519ph. It must be followed by the
	// uint8-length prefixed context.
	domPrefixPh = "SigEd25519 no Ed25519 collisions\x01"
	// domPrefixCtx is dom2(phflag=0) for Ed25519ctx. It must be followed by the
	// uint8-length prefixed context.
	domPrefixCtx = "SigEd25519 no Ed25519 collisions\x00"
)

func ( *PrivateKey,  []byte) []byte {
	// Outline the function body so that the returned signature can be
	// stack-allocated.
	 := make([]byte, signatureSize)
	return sign(, , )
}

func sign( []byte,  *PrivateKey,  []byte) []byte {
	fipsSelfTest()
	fips140.RecordApproved()
	return signWithDom(, , , domPrefixPure, "")
}

func ( *PrivateKey,  []byte,  string) ([]byte, error) {
	// Outline the function body so that the returned signature can be
	// stack-allocated.
	 := make([]byte, signatureSize)
	return signPH(, , , )
}

func signPH( []byte,  *PrivateKey,  []byte,  string) ([]byte, error) {
	fipsSelfTest()
	fips140.RecordApproved()
	if  := len();  != sha512Size {
		return nil, errors.New("ed25519: bad Ed25519ph message hash length: " + strconv.Itoa())
	}
	if  := len();  > 255 {
		return nil, errors.New("ed25519: bad Ed25519ph context length: " + strconv.Itoa())
	}
	return signWithDom(, , , domPrefixPh, ), nil
}

func ( *PrivateKey,  []byte,  string) ([]byte, error) {
	// Outline the function body so that the returned signature can be
	// stack-allocated.
	 := make([]byte, signatureSize)
	return signCtx(, , , )
}

func signCtx( []byte,  *PrivateKey,  []byte,  string) ([]byte, error) {
	fipsSelfTest()
	// FIPS 186-5 specifies Ed25519 and Ed25519ph (with context), but not Ed25519ctx.
	fips140.RecordNonApproved()
	// Note that per RFC 8032, Section 5.1, the context SHOULD NOT be empty.
	if  := len();  > 255 {
		return nil, errors.New("ed25519: bad Ed25519ctx context length: " + strconv.Itoa())
	}
	return signWithDom(, , , domPrefixCtx, ), nil
}

func signWithDom( []byte,  *PrivateKey,  []byte, ,  string) []byte {
	 := sha512.New()
	if  != domPrefixPure {
		.Write([]byte())
		.Write([]byte{byte(len())})
		.Write([]byte())
	}
	.Write(.prefix[:])
	.Write()
	 := make([]byte, 0, sha512Size)
	 = .Sum()
	,  := edwards25519.NewScalar().SetUniformBytes()
	if  != nil {
		panic("ed25519: internal error: setting scalar failed")
	}

	 := (&edwards25519.Point{}).ScalarBaseMult()

	 := sha512.New()
	if  != domPrefixPure {
		.Write([]byte())
		.Write([]byte{byte(len())})
		.Write([]byte())
	}
	.Write(.Bytes())
	.Write(.pub[:])
	.Write()
	 := make([]byte, 0, sha512Size)
	 = .Sum()
	,  := edwards25519.NewScalar().SetUniformBytes()
	if  != nil {
		panic("ed25519: internal error: setting scalar failed")
	}

	 := edwards25519.NewScalar().MultiplyAdd(, &.s, )

	copy([:32], .Bytes())
	copy([32:], .Bytes())

	return 
}

func ( *PublicKey, ,  []byte) error {
	return verify(, , )
}

func verify( *PublicKey, ,  []byte) error {
	fipsSelfTest()
	fips140.RecordApproved()
	return verifyWithDom(, , , domPrefixPure, "")
}

func ( *PublicKey,  []byte,  []byte,  string) error {
	fipsSelfTest()
	fips140.RecordApproved()
	if  := len();  != sha512Size {
		return errors.New("ed25519: bad Ed25519ph message hash length: " + strconv.Itoa())
	}
	if  := len();  > 255 {
		return errors.New("ed25519: bad Ed25519ph context length: " + strconv.Itoa())
	}
	return verifyWithDom(, , , domPrefixPh, )
}

func ( *PublicKey,  []byte,  []byte,  string) error {
	fipsSelfTest()
	// FIPS 186-5 specifies Ed25519 and Ed25519ph (with context), but not Ed25519ctx.
	fips140.RecordNonApproved()
	if  := len();  > 255 {
		return errors.New("ed25519: bad Ed25519ctx context length: " + strconv.Itoa())
	}
	return verifyWithDom(, , , domPrefixCtx, )
}

func verifyWithDom( *PublicKey, ,  []byte, ,  string) error {
	if  := len();  != signatureSize {
		return errors.New("ed25519: bad signature length: " + strconv.Itoa())
	}

	if [63]&224 != 0 {
		return errors.New("ed25519: invalid signature")
	}

	 := sha512.New()
	if  != domPrefixPure {
		.Write([]byte())
		.Write([]byte{byte(len())})
		.Write([]byte())
	}
	.Write([:32])
	.Write(.aBytes[:])
	.Write()
	 := make([]byte, 0, sha512Size)
	 = .Sum()
	,  := edwards25519.NewScalar().SetUniformBytes()
	if  != nil {
		panic("ed25519: internal error: setting scalar failed")
	}

	,  := edwards25519.NewScalar().SetCanonicalBytes([32:])
	if  != nil {
		return errors.New("ed25519: invalid signature")
	}

	// [S]B = R + [k]A --> [k](-A) + [S]B = R
	 := (&edwards25519.Point{}).Negate(&.a)
	 := (&edwards25519.Point{}).VarTimeDoubleScalarBaseMult(, , )

	if !bytes.Equal([:32], .Bytes()) {
		return errors.New("ed25519: invalid signature")
	}
	return nil
}