2d46d313d1
CI / check-and-test (pull_request) Successful in 11s
TCP forwardData copied plaintext after the handshake. Frame each chunk as uint32 length plus AES-GCM ciphertext in both client and server, both directions.
69 lines
1.8 KiB
Go
69 lines
1.8 KiB
Go
package encryption
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
// MaxFrameSize is the maximum AES-GCM ciphertext length accepted on a framed
|
|
// TCP tunnel (uint32 length prefix + ciphertext).
|
|
const MaxFrameSize = 1024 * 1024 // 1 MiB
|
|
|
|
// WriteEncryptedFrame encrypts plaintext with AES-GCM (EncryptData) and writes
|
|
// a uint32 big-endian length prefix followed by the ciphertext.
|
|
func WriteEncryptedFrame(w io.Writer, plaintext, key []byte) error {
|
|
ciphertext, err := EncryptData(plaintext, key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(ciphertext) == 0 {
|
|
return fmt.Errorf("encrypted data cannot be empty")
|
|
}
|
|
if len(ciphertext) > MaxFrameSize {
|
|
return fmt.Errorf("frame too large: %d bytes (max %d)", len(ciphertext), MaxFrameSize)
|
|
}
|
|
|
|
var length [4]byte
|
|
binary.BigEndian.PutUint32(length[:], uint32(len(ciphertext)))
|
|
if err := writeFull(w, length[:]); err != nil {
|
|
return err
|
|
}
|
|
return writeFull(w, ciphertext)
|
|
}
|
|
|
|
// ReadEncryptedFrame reads a uint32 big-endian length, caps it, reads the
|
|
// ciphertext, and decrypts it with AES-GCM (DecryptData).
|
|
func ReadEncryptedFrame(r io.Reader, key []byte) ([]byte, error) {
|
|
var length uint32
|
|
if err := binary.Read(r, binary.BigEndian, &length); err != nil {
|
|
return nil, err
|
|
}
|
|
if length == 0 {
|
|
return nil, fmt.Errorf("frame length cannot be zero")
|
|
}
|
|
if length > MaxFrameSize {
|
|
return nil, fmt.Errorf("frame too large: %d bytes (max %d)", length, MaxFrameSize)
|
|
}
|
|
|
|
ciphertext := make([]byte, length)
|
|
if _, err := io.ReadFull(r, ciphertext); err != nil {
|
|
return nil, fmt.Errorf("failed to read frame data: %v", err)
|
|
}
|
|
return DecryptData(ciphertext, key)
|
|
}
|
|
|
|
func writeFull(w io.Writer, data []byte) error {
|
|
for len(data) > 0 {
|
|
n, err := w.Write(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if n == 0 {
|
|
return io.ErrShortWrite
|
|
}
|
|
data = data[n:]
|
|
}
|
|
return nil
|
|
}
|