Files
Ananta-ps/pkg/protocol/handshake.go
T

86 lines
2.1 KiB
Go

package protocol
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/binary"
"errors"
"io"
"net"
"ananta-ps/pkg/crypto/chacha8"
)
// HandleHandshake processes client C2S handshake and performs S2C key exchange.
// It returns read and write ciphers initialized with negotiated ChaCha8 keys.
func HandleHandshake(conn net.Conn) (*chacha8.UxChaCha8, *chacha8.UxChaCha8, error) {
// Read frame 0 (mode 0x01 plaintext)
frame, err := ReadFrame(conn, nil)
if err != nil {
return nil, nil, err
}
if len(frame.Payload) < 20 {
return nil, nil, errors.New("client handshake too short")
}
aesKey := frame.Payload[4:20] // 16-byte key
clientWriteKey := make([]byte, 32)
clientReadKey := make([]byte, 32)
streamNonce := make([]byte, 12)
aesNonce := make([]byte, 12)
if _, err := io.ReadFull(rand.Reader, clientWriteKey); err != nil {
return nil, nil, err
}
if _, err := io.ReadFull(rand.Reader, clientReadKey); err != nil {
return nil, nil, err
}
if _, err := io.ReadFull(rand.Reader, streamNonce); err != nil {
return nil, nil, err
}
if _, err := io.ReadFull(rand.Reader, aesNonce); err != nil {
return nil, nil, err
}
chaChaHead := make([]byte, 76)
copy(chaChaHead[0:32], clientWriteKey)
copy(chaChaHead[32:64], clientReadKey)
copy(chaChaHead[64:76], streamNonce)
block, err := aes.NewCipher(aesKey)
if err != nil {
return nil, nil, err
}
aesGcm, err := cipher.NewGCM(block)
if err != nil {
return nil, nil, err
}
encrypted := aesGcm.Seal(nil, aesNonce, chaChaHead, nil) // 76 + 16 = 92 bytes
payload := make([]byte, 368)
binary.LittleEndian.PutUint32(payload[0:4], 1)
binary.LittleEndian.PutUint32(payload[4:8], 10)
copy(payload[8:20], aesNonce)
copy(payload[20:96], encrypted[0:76])
copy(payload[96:112], encrypted[76:92])
// Send handshake reply in plaintext with mode 0x01
if err := WriteFrame(conn, 0x01, payload, nil); err != nil {
return nil, nil, err
}
readCryptor, err := chacha8.New(clientWriteKey, streamNonce)
if err != nil {
return nil, nil, err
}
writeCryptor, err := chacha8.New(clientReadKey, streamNonce)
if err != nil {
return nil, nil, err
}
return readCryptor, writeCryptor, nil
}