86 lines
2.1 KiB
Go
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
|
|
}
|