TCP forwardData copied plaintext after the handshake. Frame each chunk as uint32 length plus AES-GCM ciphertext in both client and server, both directions.
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user