package ssh
import (
"crypto"
"crypto/mlkem"
"crypto/sha256"
"errors"
"fmt"
"io"
"golang.org/x/crypto/curve25519"
)
type mlkem768WithCurve25519sha256 struct {}
func (kex *mlkem768WithCurve25519sha256 ) Client (c packetConn , rand io .Reader , magics *handshakeMagics ) (*kexResult , error ) {
var c25519kp curve25519KeyPair
if err := c25519kp .generate (rand ); err != nil {
return nil , err
}
seed := make ([]byte , mlkem .SeedSize )
if _ , err := io .ReadFull (rand , seed ); err != nil {
return nil , err
}
mlkemDk , err := mlkem .NewDecapsulationKey768 (seed )
if err != nil {
return nil , err
}
hybridKey := append (mlkemDk .EncapsulationKey ().Bytes (), c25519kp .pub [:]...)
if err := c .writePacket (Marshal (&kexECDHInitMsg {hybridKey })); err != nil {
return nil , err
}
packet , err := c .readPacket ()
if err != nil {
return nil , err
}
var reply kexECDHReplyMsg
if err = Unmarshal (packet , &reply ); err != nil {
return nil , err
}
if len (reply .EphemeralPubKey ) != mlkem .CiphertextSize768 +32 {
return nil , errors .New ("ssh: peer's mlkem768x25519 public value has wrong length" )
}
mlkem768Secret , err := mlkemDk .Decapsulate (reply .EphemeralPubKey [:mlkem .CiphertextSize768 ])
if err != nil {
return nil , err
}
c25519Secret , err := curve25519 .X25519 (c25519kp .priv [:], reply .EphemeralPubKey [mlkem .CiphertextSize768 :])
if err != nil {
return nil , fmt .Errorf ("ssh: peer's mlkem768x25519 public value is not valid: %w" , err )
}
h := sha256 .New ()
h .Write (mlkem768Secret )
h .Write (c25519Secret )
secret := h .Sum (nil )
h .Reset ()
magics .write (h )
writeString (h , reply .HostKey )
writeString (h , hybridKey )
writeString (h , reply .EphemeralPubKey )
K := make ([]byte , stringLength (len (secret )))
marshalString (K , secret )
h .Write (K )
return &kexResult {
H : h .Sum (nil ),
K : K ,
HostKey : reply .HostKey ,
Signature : reply .Signature ,
Hash : crypto .SHA256 ,
}, nil
}
func (kex *mlkem768WithCurve25519sha256 ) Server (c packetConn , rand io .Reader , magics *handshakeMagics , priv AlgorithmSigner , algo string ) (*kexResult , error ) {
packet , err := c .readPacket ()
if err != nil {
return nil , err
}
var kexInit kexECDHInitMsg
if err = Unmarshal (packet , &kexInit ); err != nil {
return nil , err
}
if len (kexInit .ClientPubKey ) != mlkem .EncapsulationKeySize768 +32 {
return nil , errors .New ("ssh: peer's ML-KEM768/curve25519 public value has wrong length" )
}
encapsulationKey , err := mlkem .NewEncapsulationKey768 (kexInit .ClientPubKey [:mlkem .EncapsulationKeySize768 ])
if err != nil {
return nil , fmt .Errorf ("ssh: peer's ML-KEM768 encapsulation key is not valid: %w" , err )
}
mlkem768Secret , mlkem768Ciphertext := encapsulationKey .Encapsulate ()
var c25519kp curve25519KeyPair
if err := c25519kp .generate (rand ); err != nil {
return nil , err
}
c25519Secret , err := curve25519 .X25519 (c25519kp .priv [:], kexInit .ClientPubKey [mlkem .EncapsulationKeySize768 :])
if err != nil {
return nil , fmt .Errorf ("ssh: peer's ML-KEM768/curve25519 public value is not valid: %w" , err )
}
hybridKey := append (mlkem768Ciphertext , c25519kp .pub [:]...)
h := sha256 .New ()
h .Write (mlkem768Secret )
h .Write (c25519Secret )
secret := h .Sum (nil )
hostKeyBytes := priv .PublicKey ().Marshal ()
h .Reset ()
magics .write (h )
writeString (h , hostKeyBytes )
writeString (h , kexInit .ClientPubKey )
writeString (h , hybridKey )
K := make ([]byte , stringLength (len (secret )))
marshalString (K , secret )
h .Write (K )
H := h .Sum (nil )
sig , err := signAndMarshal (priv , rand , H , algo )
if err != nil {
return nil , err
}
reply := kexECDHReplyMsg {
EphemeralPubKey : hybridKey ,
HostKey : hostKeyBytes ,
Signature : sig ,
}
if err := c .writePacket (Marshal (&reply )); err != nil {
return nil , err
}
return &kexResult {
H : H ,
K : K ,
HostKey : hostKeyBytes ,
Signature : sig ,
Hash : crypto .SHA256 ,
}, nil
}
The pages are generated with Golds v0.8.2 . (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 .