// 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 ssh

import (
	
	
	
	
	
	

	
)

// mlkem768WithCurve25519sha256 implements the hybrid ML-KEM768 with
// curve25519-sha256 key exchange method, as described by
// draft-kampanakis-curdle-ssh-pq-ke-05 section 2.3.3.
type mlkem768WithCurve25519sha256 struct{}

func ( *mlkem768WithCurve25519sha256) ( packetConn,  io.Reader,  *handshakeMagics) (*kexResult, error) {
	var  curve25519KeyPair
	if  := .generate();  != nil {
		return nil, 
	}

	 := make([]byte, mlkem.SeedSize)
	if ,  := io.ReadFull(, );  != nil {
		return nil, 
	}

	,  := mlkem.NewDecapsulationKey768()
	if  != nil {
		return nil, 
	}

	 := append(.EncapsulationKey().Bytes(), .pub[:]...)
	if  := .writePacket(Marshal(&kexECDHInitMsg{}));  != nil {
		return nil, 
	}

	,  := .readPacket()
	if  != nil {
		return nil, 
	}

	var  kexECDHReplyMsg
	if  = Unmarshal(, &);  != nil {
		return nil, 
	}

	if len(.EphemeralPubKey) != mlkem.CiphertextSize768+32 {
		return nil, errors.New("ssh: peer's mlkem768x25519 public value has wrong length")
	}

	// Perform KEM decapsulate operation to obtain shared key from ML-KEM.
	,  := .Decapsulate(.EphemeralPubKey[:mlkem.CiphertextSize768])
	if  != nil {
		return nil, 
	}

	// Complete Curve25519 ECDH to obtain its shared key.
	,  := curve25519.X25519(.priv[:], .EphemeralPubKey[mlkem.CiphertextSize768:])
	if  != nil {
		return nil, fmt.Errorf("ssh: peer's mlkem768x25519 public value is not valid: %w", )
	}
	// Compute actual shared key.
	 := sha256.New()
	.Write()
	.Write()
	 := .Sum(nil)

	.Reset()
	.write()
	writeString(, .HostKey)
	writeString(, )
	writeString(, .EphemeralPubKey)

	 := make([]byte, stringLength(len()))
	marshalString(, )
	.Write()

	return &kexResult{
		H:         .Sum(nil),
		K:         ,
		HostKey:   .HostKey,
		Signature: .Signature,
		Hash:      crypto.SHA256,
	}, nil
}

func ( *mlkem768WithCurve25519sha256) ( packetConn,  io.Reader,  *handshakeMagics,  AlgorithmSigner,  string) (*kexResult, error) {
	,  := .readPacket()
	if  != nil {
		return nil, 
	}

	var  kexECDHInitMsg
	if  = Unmarshal(, &);  != nil {
		return nil, 
	}

	if len(.ClientPubKey) != mlkem.EncapsulationKeySize768+32 {
		return nil, errors.New("ssh: peer's ML-KEM768/curve25519 public value has wrong length")
	}

	,  := mlkem.NewEncapsulationKey768(.ClientPubKey[:mlkem.EncapsulationKeySize768])
	if  != nil {
		return nil, fmt.Errorf("ssh: peer's ML-KEM768 encapsulation key is not valid: %w", )
	}
	// Perform KEM encapsulate operation to obtain ciphertext and shared key.
	,  := .Encapsulate()

	// Perform server side of Curve25519 ECDH to obtain server public value and
	// shared key.
	var  curve25519KeyPair
	if  := .generate();  != nil {
		return nil, 
	}
	,  := curve25519.X25519(.priv[:], .ClientPubKey[mlkem.EncapsulationKeySize768:])
	if  != nil {
		return nil, fmt.Errorf("ssh: peer's ML-KEM768/curve25519 public value is not valid: %w", )
	}
	 := append(, .pub[:]...)

	// Compute actual shared key.
	 := sha256.New()
	.Write()
	.Write()
	 := .Sum(nil)

	 := .PublicKey().Marshal()

	.Reset()
	.write()
	writeString(, )
	writeString(, .ClientPubKey)
	writeString(, )

	 := make([]byte, stringLength(len()))
	marshalString(, )
	.Write()

	 := .Sum(nil)

	,  := signAndMarshal(, , , )
	if  != nil {
		return nil, 
	}

	 := kexECDHReplyMsg{
		EphemeralPubKey: ,
		HostKey:         ,
		Signature:       ,
	}
	if  := .writePacket(Marshal(&));  != nil {
		return nil, 
	}
	return &kexResult{
		H:         ,
		K:         ,
		HostKey:   ,
		Signature: ,
		Hash:      crypto.SHA256,
	}, nil
}