package  tls 
 
import  ( 
	"crypto"  
	"crypto/ecdh"  
	"crypto/md5"  
	"crypto/rsa"  
	"crypto/sha1"  
	"crypto/x509"  
	"errors"  
	"fmt"  
	"io"  
	"slices"  
) 
 
 
 
type  keyAgreement interface  { 
	 
 
	 
 
 
	generateServerKeyExchange(*Config , *Certificate , *clientHelloMsg , *serverHelloMsg ) (*serverKeyExchangeMsg , error ) 
	processClientKeyExchange(*Config , *Certificate , *clientKeyExchangeMsg , uint16 ) ([]byte , error ) 
 
	 
 
	 
 
	processServerKeyExchange(*Config , *clientHelloMsg , *serverHelloMsg , *x509 .Certificate , *serverKeyExchangeMsg ) error  
	generateClientKeyExchange(*Config , *clientHelloMsg , *x509 .Certificate ) ([]byte , *clientKeyExchangeMsg , error ) 
} 
 
var  errClientKeyExchange = errors .New ("tls: invalid ClientKeyExchange message" ) 
var  errServerKeyExchange = errors .New ("tls: invalid ServerKeyExchange message" ) 
 
 
 
type  rsaKeyAgreement struct {} 
 
func  (ka  rsaKeyAgreement ) generateServerKeyExchange (config  *Config , cert  *Certificate , clientHello  *clientHelloMsg , hello  *serverHelloMsg ) (*serverKeyExchangeMsg , error ) { 
	return  nil , nil  
} 
 
func  (ka  rsaKeyAgreement ) processClientKeyExchange (config  *Config , cert  *Certificate , ckx  *clientKeyExchangeMsg , version  uint16 ) ([]byte , error ) { 
	if  len (ckx .ciphertext ) < 2  { 
		return  nil , errClientKeyExchange  
	} 
	ciphertextLen  := int (ckx .ciphertext [0 ])<<8  | int (ckx .ciphertext [1 ]) 
	if  ciphertextLen  != len (ckx .ciphertext )-2  { 
		return  nil , errClientKeyExchange  
	} 
	ciphertext  := ckx .ciphertext [2 :] 
 
	priv , ok  := cert .PrivateKey .(crypto .Decrypter ) 
	if  !ok  { 
		return  nil , errors .New ("tls: certificate private key does not implement crypto.Decrypter" ) 
	} 
	 
	preMasterSecret , err  := priv .Decrypt (config .rand (), ciphertext , &rsa .PKCS1v15DecryptOptions {SessionKeyLen : 48 }) 
	if  err  != nil  { 
		return  nil , err  
	} 
	 
 
 
 
 
 
	return  preMasterSecret , nil  
} 
 
func  (ka  rsaKeyAgreement ) processServerKeyExchange (config  *Config , clientHello  *clientHelloMsg , serverHello  *serverHelloMsg , cert  *x509 .Certificate , skx  *serverKeyExchangeMsg ) error  { 
	return  errors .New ("tls: unexpected ServerKeyExchange" ) 
} 
 
func  (ka  rsaKeyAgreement ) generateClientKeyExchange (config  *Config , clientHello  *clientHelloMsg , cert  *x509 .Certificate ) ([]byte , *clientKeyExchangeMsg , error ) { 
	preMasterSecret  := make ([]byte , 48 ) 
	preMasterSecret [0 ] = byte (clientHello .vers  >> 8 ) 
	preMasterSecret [1 ] = byte (clientHello .vers ) 
	_ , err  := io .ReadFull (config .rand (), preMasterSecret [2 :]) 
	if  err  != nil  { 
		return  nil , nil , err  
	} 
 
	rsaKey , ok  := cert .PublicKey .(*rsa .PublicKey ) 
	if  !ok  { 
		return  nil , nil , errors .New ("tls: server certificate contains incorrect key type for selected ciphersuite" ) 
	} 
	encrypted , err  := rsa .EncryptPKCS1v15 (config .rand (), rsaKey , preMasterSecret ) 
	if  err  != nil  { 
		return  nil , nil , err  
	} 
	ckx  := new (clientKeyExchangeMsg ) 
	ckx .ciphertext  = make ([]byte , len (encrypted )+2 ) 
	ckx .ciphertext [0 ] = byte (len (encrypted ) >> 8 ) 
	ckx .ciphertext [1 ] = byte (len (encrypted )) 
	copy (ckx .ciphertext [2 :], encrypted ) 
	return  preMasterSecret , ckx , nil  
} 
 
 
func  sha1Hash(slices  [][]byte ) []byte  { 
	hsha1  := sha1 .New () 
	for  _ , slice  := range  slices  { 
		hsha1 .Write (slice ) 
	} 
	return  hsha1 .Sum (nil ) 
} 
 
 
 
func  md5SHA1Hash(slices  [][]byte ) []byte  { 
	md5sha1  := make ([]byte , md5 .Size +sha1 .Size ) 
	hmd5  := md5 .New () 
	for  _ , slice  := range  slices  { 
		hmd5 .Write (slice ) 
	} 
	copy (md5sha1 , hmd5 .Sum (nil )) 
	copy (md5sha1 [md5 .Size :], sha1Hash (slices )) 
	return  md5sha1  
} 
 
 
 
 
 
func  hashForServerKeyExchange(sigType  uint8 , hashFunc  crypto .Hash , version  uint16 , slices  ...[]byte ) []byte  { 
	if  sigType  == signatureEd25519  { 
		var  signed  []byte  
		for  _ , slice  := range  slices  { 
			signed  = append (signed , slice ...) 
		} 
		return  signed  
	} 
	if  version  >= VersionTLS12  { 
		h  := hashFunc .New () 
		for  _ , slice  := range  slices  { 
			h .Write (slice ) 
		} 
		digest  := h .Sum (nil ) 
		return  digest  
	} 
	if  sigType  == signatureECDSA  { 
		return  sha1Hash (slices ) 
	} 
	return  md5SHA1Hash (slices ) 
} 
 
 
 
 
 
type  ecdheKeyAgreement struct  { 
	version uint16  
	isRSA   bool  
	key     *ecdh .PrivateKey  
 
	 
 
	ckx             *clientKeyExchangeMsg  
	preMasterSecret []byte  
 
	 
 
	curveID            CurveID  
	signatureAlgorithm SignatureScheme  
} 
 
func  (ka  *ecdheKeyAgreement ) generateServerKeyExchange (config  *Config , cert  *Certificate , clientHello  *clientHelloMsg , hello  *serverHelloMsg ) (*serverKeyExchangeMsg , error ) { 
	for  _ , c  := range  clientHello .supportedCurves  { 
		if  config .supportsCurve (ka .version , c ) { 
			ka .curveID  = c  
			break  
		} 
	} 
 
	if  ka .curveID  == 0  { 
		return  nil , errors .New ("tls: no supported elliptic curves offered" ) 
	} 
	if  _ , ok  := curveForCurveID (ka .curveID ); !ok  { 
		return  nil , errors .New ("tls: CurvePreferences includes unsupported curve" ) 
	} 
 
	key , err  := generateECDHEKey (config .rand (), ka .curveID ) 
	if  err  != nil  { 
		return  nil , err  
	} 
	ka .key  = key  
 
	 
	ecdhePublic  := key .PublicKey ().Bytes () 
	serverECDHEParams  := make ([]byte , 1 +2 +1 +len (ecdhePublic )) 
	serverECDHEParams [0 ] = 3   
	serverECDHEParams [1 ] = byte (ka .curveID  >> 8 ) 
	serverECDHEParams [2 ] = byte (ka .curveID ) 
	serverECDHEParams [3 ] = byte (len (ecdhePublic )) 
	copy (serverECDHEParams [4 :], ecdhePublic ) 
 
	priv , ok  := cert .PrivateKey .(crypto .Signer ) 
	if  !ok  { 
		return  nil , fmt .Errorf ("tls: certificate private key of type %T does not implement crypto.Signer" , cert .PrivateKey ) 
	} 
 
	var  sigType  uint8  
	var  sigHash  crypto .Hash  
	if  ka .version  >= VersionTLS12  { 
		ka .signatureAlgorithm , err  = selectSignatureScheme (ka .version , cert , clientHello .supportedSignatureAlgorithms ) 
		if  err  != nil  { 
			return  nil , err  
		} 
		sigType , sigHash , err  = typeAndHashFromSignatureScheme (ka .signatureAlgorithm ) 
		if  err  != nil  { 
			return  nil , err  
		} 
		if  sigHash  == crypto .SHA1  { 
			tlssha1 .Value ()  
			tlssha1 .IncNonDefault () 
		} 
	} else  { 
		sigType , sigHash , err  = legacyTypeAndHashFromPublicKey (priv .Public ()) 
		if  err  != nil  { 
			return  nil , err  
		} 
	} 
	if  (sigType  == signaturePKCS1v15  || sigType  == signatureRSAPSS ) != ka .isRSA  { 
		return  nil , errors .New ("tls: certificate cannot be used with the selected cipher suite" ) 
	} 
 
	signed  := hashForServerKeyExchange (sigType , sigHash , ka .version , clientHello .random , hello .random , serverECDHEParams ) 
 
	signOpts  := crypto .SignerOpts (sigHash ) 
	if  sigType  == signatureRSAPSS  { 
		signOpts  = &rsa .PSSOptions {SaltLength : rsa .PSSSaltLengthEqualsHash , Hash : sigHash } 
	} 
	sig , err  := priv .Sign (config .rand (), signed , signOpts ) 
	if  err  != nil  { 
		return  nil , errors .New ("tls: failed to sign ECDHE parameters: "  + err .Error()) 
	} 
 
	skx  := new (serverKeyExchangeMsg ) 
	sigAndHashLen  := 0  
	if  ka .version  >= VersionTLS12  { 
		sigAndHashLen  = 2  
	} 
	skx .key  = make ([]byte , len (serverECDHEParams )+sigAndHashLen +2 +len (sig )) 
	copy (skx .key , serverECDHEParams ) 
	k  := skx .key [len (serverECDHEParams ):] 
	if  ka .version  >= VersionTLS12  { 
		k [0 ] = byte (ka .signatureAlgorithm  >> 8 ) 
		k [1 ] = byte (ka .signatureAlgorithm ) 
		k  = k [2 :] 
	} 
	k [0 ] = byte (len (sig ) >> 8 ) 
	k [1 ] = byte (len (sig )) 
	copy (k [2 :], sig ) 
 
	return  skx , nil  
} 
 
func  (ka  *ecdheKeyAgreement ) processClientKeyExchange (config  *Config , cert  *Certificate , ckx  *clientKeyExchangeMsg , version  uint16 ) ([]byte , error ) { 
	if  len (ckx .ciphertext ) == 0  || int (ckx .ciphertext [0 ]) != len (ckx .ciphertext )-1  { 
		return  nil , errClientKeyExchange  
	} 
 
	peerKey , err  := ka .key .Curve ().NewPublicKey (ckx .ciphertext [1 :]) 
	if  err  != nil  { 
		return  nil , errClientKeyExchange  
	} 
	preMasterSecret , err  := ka .key .ECDH (peerKey ) 
	if  err  != nil  { 
		return  nil , errClientKeyExchange  
	} 
 
	return  preMasterSecret , nil  
} 
 
func  (ka  *ecdheKeyAgreement ) processServerKeyExchange (config  *Config , clientHello  *clientHelloMsg , serverHello  *serverHelloMsg , cert  *x509 .Certificate , skx  *serverKeyExchangeMsg ) error  { 
	if  len (skx .key ) < 4  { 
		return  errServerKeyExchange  
	} 
	if  skx .key [0 ] != 3  {  
		return  errors .New ("tls: server selected unsupported curve" ) 
	} 
	ka .curveID  = CurveID (skx .key [1 ])<<8  | CurveID (skx .key [2 ]) 
 
	publicLen  := int (skx .key [3 ]) 
	if  publicLen +4  > len (skx .key ) { 
		return  errServerKeyExchange  
	} 
	serverECDHEParams  := skx .key [:4 +publicLen ] 
	publicKey  := serverECDHEParams [4 :] 
 
	sig  := skx .key [4 +publicLen :] 
	if  len (sig ) < 2  { 
		return  errServerKeyExchange  
	} 
 
	if  !slices .Contains (clientHello .supportedCurves , ka .curveID ) { 
		return  errors .New ("tls: server selected unoffered curve" ) 
	} 
 
	if  _ , ok  := curveForCurveID (ka .curveID ); !ok  { 
		return  errors .New ("tls: server selected unsupported curve" ) 
	} 
 
	key , err  := generateECDHEKey (config .rand (), ka .curveID ) 
	if  err  != nil  { 
		return  err  
	} 
	ka .key  = key  
 
	peerKey , err  := key .Curve ().NewPublicKey (publicKey ) 
	if  err  != nil  { 
		return  errServerKeyExchange  
	} 
	ka .preMasterSecret , err  = key .ECDH (peerKey ) 
	if  err  != nil  { 
		return  errServerKeyExchange  
	} 
 
	ourPublicKey  := key .PublicKey ().Bytes () 
	ka .ckx  = new (clientKeyExchangeMsg ) 
	ka .ckx .ciphertext  = make ([]byte , 1 +len (ourPublicKey )) 
	ka .ckx .ciphertext [0 ] = byte (len (ourPublicKey )) 
	copy (ka .ckx .ciphertext [1 :], ourPublicKey ) 
 
	var  sigType  uint8  
	var  sigHash  crypto .Hash  
	if  ka .version  >= VersionTLS12  { 
		ka .signatureAlgorithm  = SignatureScheme (sig [0 ])<<8  | SignatureScheme (sig [1 ]) 
		sig  = sig [2 :] 
		if  len (sig ) < 2  { 
			return  errServerKeyExchange  
		} 
 
		if  !isSupportedSignatureAlgorithm (ka .signatureAlgorithm , clientHello .supportedSignatureAlgorithms ) { 
			return  errors .New ("tls: certificate used with invalid signature algorithm" ) 
		} 
		sigType , sigHash , err  = typeAndHashFromSignatureScheme (ka .signatureAlgorithm ) 
		if  err  != nil  { 
			return  err  
		} 
		if  sigHash  == crypto .SHA1  { 
			tlssha1 .Value ()  
			tlssha1 .IncNonDefault () 
		} 
	} else  { 
		sigType , sigHash , err  = legacyTypeAndHashFromPublicKey (cert .PublicKey ) 
		if  err  != nil  { 
			return  err  
		} 
	} 
	if  (sigType  == signaturePKCS1v15  || sigType  == signatureRSAPSS ) != ka .isRSA  { 
		return  errServerKeyExchange  
	} 
 
	sigLen  := int (sig [0 ])<<8  | int (sig [1 ]) 
	if  sigLen +2  != len (sig ) { 
		return  errServerKeyExchange  
	} 
	sig  = sig [2 :] 
 
	signed  := hashForServerKeyExchange (sigType , sigHash , ka .version , clientHello .random , serverHello .random , serverECDHEParams ) 
	if  err  := verifyHandshakeSignature (sigType , cert .PublicKey , sigHash , signed , sig ); err  != nil  { 
		return  errors .New ("tls: invalid signature by the server certificate: "  + err .Error()) 
	} 
	return  nil  
} 
 
func  (ka  *ecdheKeyAgreement ) generateClientKeyExchange (config  *Config , clientHello  *clientHelloMsg , cert  *x509 .Certificate ) ([]byte , *clientKeyExchangeMsg , error ) { 
	if  ka .ckx  == nil  { 
		return  nil , nil , errors .New ("tls: missing ServerKeyExchange message" ) 
	} 
 
	return  ka .preMasterSecret , ka .ckx , nil  
} 
  
The pages are generated with Golds   v0.7.9-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 .