// Copyright 2026 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.

//go:build !fips140v1.0

package mldsa

import (
	
	
	
	
)

// PrivateKey is an in-memory ML-DSA private key. It implements [crypto.Signer]
// and the informal extended [crypto.PrivateKey] interface.
//
// A PrivateKey is safe for concurrent use.
type PrivateKey struct {
	k mldsa.PrivateKey
}

var errInvalidParameters = errors.New("mldsa: invalid parameters")

// GenerateKey generates a new random ML-DSA private key.
func ( Parameters) (*PrivateKey, error) {
	switch  {
	case MLDSA44():
		return &PrivateKey{k: *mldsa.GenerateKey44()}, nil
	case MLDSA65():
		return &PrivateKey{k: *mldsa.GenerateKey65()}, nil
	case MLDSA87():
		return &PrivateKey{k: *mldsa.GenerateKey87()}, nil
	default:
		return nil, errInvalidParameters
	}
}

// NewPrivateKey decodes an ML-DSA private key from the given seed.
//
// The seed must be exactly [PrivateKeySize] bytes long.
func ( Parameters,  []byte) (*PrivateKey, error) {
	var  error
	var  *mldsa.PrivateKey
	switch  {
	case MLDSA44():
		,  = mldsa.NewPrivateKey44()
	case MLDSA65():
		,  = mldsa.NewPrivateKey65()
	case MLDSA87():
		,  = mldsa.NewPrivateKey87()
	default:
		return nil, errInvalidParameters
	}
	if  != nil {
		return nil, 
	}
	return &PrivateKey{k: *}, nil
}

// Public returns the corresponding [PublicKey] for this private key.
//
// It implements the [crypto.Signer] interface.
func ( *PrivateKey) () crypto.PublicKey {
	return .PublicKey()
}

// Equal reports whether sk and x are the same key (i.e. they are derived from
// the same seed).
//
// If x is not a *PrivateKey, Equal returns false.
func ( *PrivateKey) ( crypto.PrivateKey) bool {
	,  := .(*PrivateKey)
	if ! ||  == nil {
		return false
	}
	return .k.Equal(&.k)
}

// PublicKey returns the corresponding [PublicKey] for this private key.
func ( *PrivateKey) () *PublicKey {
	// Making a copy severs the pointer relationship between the private and
	// public keys, so that keeping the public key around doesn't keep the
	// private key alive. This costs a copy and an allocation.
	return &PublicKey{p: *.k.PublicKey()}
}

// Bytes returns the private key seed.
func ( *PrivateKey) () []byte {
	return .k.Bytes()
}

var errInvalidSignerOpts = errors.New("mldsa: invalid SignerOpts")

// Sign returns a signature of the given message using this private key.
//
// If opts is nil or opts.HashFunc returns zero, the message is signed directly.
// If opts.HashFunc returns [crypto.MLDSAMu], the provided message must be a
// [pre-hashed μ message representative]. opts can be of type *[Options] if a
// context string is desired along with a directly-signed message. The io.Reader
// argument is ignored.
//
// [pre-hashed μ message representative]: https://www.rfc-editor.org/rfc/rfc9881.html#externalmu
func ( *PrivateKey) ( io.Reader,  []byte,  crypto.SignerOpts) ( []byte,  error) {
	if .k == (mldsa.PrivateKey{}) {
		return nil, errors.New("mldsa: zero private key")
	}
	if  == nil {
		 = &Options{}
	}
	switch .HashFunc() {
	case 0:
		var  string
		if ,  := .(*Options);  &&  != nil {
			 = .Context
		}
		return mldsa.Sign(&.k, , )
	case crypto.MLDSAMu:
		return mldsa.SignExternalMu(&.k, )
	default:
		return nil, errInvalidSignerOpts
	}
}

// SignDeterministic works like [PrivateKey.Sign], but the signature is
// deterministic.
func ( *PrivateKey) ( []byte,  crypto.SignerOpts) ( []byte,  error) {
	if .k == (mldsa.PrivateKey{}) {
		return nil, errors.New("mldsa: zero private key")
	}
	if  == nil {
		 = &Options{}
	}
	switch .HashFunc() {
	case 0:
		var  string
		if ,  := .(*Options);  &&  != nil {
			 = .Context
		}
		return mldsa.SignDeterministic(&.k, , )
	case crypto.MLDSAMu:
		return mldsa.SignExternalMuDeterministic(&.k, )
	default:
		return nil, errInvalidSignerOpts
	}
}

// PublicKey is an ML-DSA public key. It implements the informal extended
// [crypto.PublicKey] interface.
//
// A PublicKey is safe for concurrent use.
type PublicKey struct {
	p mldsa.PublicKey
}

// NewPublicKey creates a new ML-DSA public key from the given encoding.
func ( Parameters,  []byte) (*PublicKey, error) {
	return newPublicKey(&PublicKey{}, , )
}

func ( *PublicKey,  Parameters,  []byte) (*PublicKey, error) {
	var  error
	var  *mldsa.PublicKey
	switch  {
	case MLDSA44():
		,  = mldsa.NewPublicKey44()
	case MLDSA65():
		,  = mldsa.NewPublicKey65()
	case MLDSA87():
		,  = mldsa.NewPublicKey87()
	default:
		return nil, errInvalidParameters
	}
	if  != nil {
		return nil, 
	}
	.p = *
	return , nil
}

// Bytes returns the public key encoding.
func ( *PublicKey) () []byte {
	return .p.Bytes()
}

// Equal reports whether pk and x are the same key (i.e. they have the same
// encoding).
//
// If x is not a *PublicKey, Equal returns false.
func ( *PublicKey) ( crypto.PublicKey) bool {
	,  := .(*PublicKey)
	if ! ||  == nil {
		return false
	}
	return .p.Equal(&.p)
}

// Parameters returns the parameters associated with this public key.
func ( *PublicKey) () Parameters {
	switch .p.Parameters() {
	case "ML-DSA-44":
		return MLDSA44()
	case "ML-DSA-65":
		return MLDSA65()
	case "ML-DSA-87":
		return MLDSA87()
	default:
		panic("mldsa: invalid parameters in public key")
	}
}

// Verify reports whether signature is a valid signature of message by pk.
// If opts is nil, it's equivalent to the zero value of Options.
func ( *PublicKey,  []byte,  []byte,  *Options) error {
	if  == nil {
		return errors.New("mldsa: nil public key")
	}
	if .p == (mldsa.PublicKey{}) {
		return errors.New("mldsa: zero public key")
	}
	if  == nil {
		 = &Options{}
	}
	return mldsa.Verify(&.p, , , .Context)
}