func ServerKeyExchange(privKey *rsa.PrivateKey, conn net.Conn) (ks *keySet, err error) {
	group, _ := dhkx.GetGroup(dhGroupID)
	priv, _ := group.GeneratePrivateKey(nil)
	mypub := priv.Bytes()
	mypub = leftPaddingZero(mypub, dhPubkeyLen)
	salt := make([]byte, pssSaltLen)
	n, err := io.ReadFull(rand.Reader, salt)
	if err != nil || n != len(salt) {
		err = ErrZeroEntropy
		return
	}
	sha := sha256.New()
	hashed := make([]byte, sha.Size())
	sha.Write([]byte{currentProtocolVersion})
	sha.Write(mypub)
	hashed = sha.Sum(hashed[:0])
	sig, err := pss.SignPSS(rand.Reader, privKey, crypto.SHA256, hashed, salt)
	if err != nil {
		return
	}
	siglen := (privKey.N.BitLen() + 7) / 8
	keyExPkt := make([]byte, dhPubkeyLen+siglen+nonceLen+1)
	keyExPkt[0] = currentProtocolVersion
	copy(keyExPkt[1:], mypub)
	copy(keyExPkt[dhPubkeyLen+1:], sig)
	nonce := keyExPkt[dhPubkeyLen+siglen+1:]
	n, err = io.ReadFull(rand.Reader, nonce)
	if err != nil || n != len(nonce) {
		err = ErrZeroEntropy
		return
	}
	err = writen(conn, keyExPkt)
	if err != nil {
		return
	}
	keyExPkt = keyExPkt[:1+dhPubkeyLen+authKeyLen]
	n, err = io.ReadFull(conn, keyExPkt)
	if err != nil {
		return
	}
	if n != len(keyExPkt) {
		err = ErrBadKeyExchangePacket
		return
	}
	version := keyExPkt[0]
	if version > currentProtocolVersion {
		err = ErrImcompatibleProtocol
		return
	}
	clientpub := dhkx.NewPublicKey(keyExPkt[1 : dhPubkeyLen+1])
	K, err := group.ComputeKey(clientpub, priv)
	if err != nil {
		return
	}
	ks, err = generateKeys(K.Bytes(), nonce)
	if err != nil {
		return
	}
	err = ks.checkClientHMAC(keyExPkt[:dhPubkeyLen+1], keyExPkt[dhPubkeyLen+1:])
	if err != nil {
		return
	}
	return
}
The authentication here is quite similar with, if not same as, tarsnap's auth algorithm.

First, server generate a Diffie-Hellman public key, dhpub1, sign it with
server's private key using RSASSA-PSS signing algorithm.
Send dhpub1, its signature and a nonce to client.
An nonce is just a sequence of random bytes.

Server -- dhpub1 + sign(dhpub1) + nonce --> Client

Then client generate its own Diffie-Hellman key. It now can calculate
a key, K, using its own Diffie-Hellman key and server's DH public key.
(According to DH key exchange algorithm)

Now, we can use K to derive any key we need on server and client side.
master key, mkey = MGF1(nonce || K, 48)
package proto
import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"github.com/monnand/dhkx"
	pss "github.com/monnand/rsa"
	"io"
	"net"
)
const currentProtocolVersion byte = 1