This repository has been archived on 2025-10-15. You can view files and clone it, but cannot push or open issues or pull requests.
Files
Uftp/internal/common/packets.go

254 lines
5.7 KiB
Go

package common
import (
"crypto/rand"
"encoding/binary"
"errors"
"fmt"
"golang.org/x/crypto/chacha20poly1305"
)
const PacketSize = 504
const HeaderSize int = 1 + 4
const SecureHeaderSize int = 1 + 24 + 8 + 4
const MaxDataSize = PacketSize - HeaderSize - SecureHeaderSize - 16 // AEAD Overhead
type SessionID [8]byte
type SecurePacket struct {
IsRsa byte // 0 = false everything else is true
Nonce [24]byte
Sid SessionID
DataLength uint32
EncryptedData []byte
}
type Packet struct {
Flag HeaderFlag
Sync uint32
Data []byte
// NOT IN BYTES THAT ARE SENT
Sid SessionID
DataLength uint32
}
func NewSymetricSecurePacket(key [32]byte, pck *Packet) *SecurePacket {
sid := pck.Sid
data := pck.ToBytes()
aead, err := chacha20poly1305.NewX(key[:])
if err != nil {
panic(err)
}
nonce := make([]byte, 24)
if _, err = rand.Read(nonce); err != nil {
panic(err)
}
encrypted := make([]byte, len(data)+aead.Overhead())
encrypted = aead.Seal(nil, nonce, data, nil)
return &SecurePacket{
IsRsa: 0,
Nonce: [24]byte(nonce),
Sid: sid,
DataLength: uint32(len(encrypted)),
EncryptedData: encrypted,
}
}
func SecurePacketFromBytes(bytes []byte) SecurePacket {
isRsa := bytes[0]
nonce := bytes[1:25]
sid := SessionID(bytes[25:33])
length := binary.LittleEndian.Uint32(bytes[33:37])
enc := bytes[37 : SecureHeaderSize+int(length)]
return SecurePacket{
IsRsa: isRsa,
Nonce: [24]byte(nonce),
Sid: sid,
DataLength: length,
EncryptedData: enc,
}
}
func (secPck *SecurePacket) ToBytes() []byte {
encSize := int(secPck.DataLength)
arr := make([]byte, SecureHeaderSize+encSize)
arr[0] = secPck.IsRsa
copy(arr[1:25], secPck.Nonce[:])
copy(arr[25:33], secPck.Sid[:])
binary.LittleEndian.PutUint32(arr[33:37], secPck.DataLength)
copy(arr[37:SecureHeaderSize+encSize], secPck.EncryptedData)
return arr
}
func (secPck *SecurePacket) ExtractPacket(key [32]byte) (Packet, error) {
aead, err := chacha20poly1305.NewX(key[:])
if err != nil {
panic(err)
}
data, err := aead.Open(nil, secPck.Nonce[:], secPck.EncryptedData, nil)
if err != nil {
return Packet{}, err
}
// fmt.Println(data)
packet := PacketFromBytes(data, secPck.DataLength-uint32(HeaderSize)-uint32(aead.Overhead()), secPck.Sid)
return packet, nil
}
func NewRsaPacket(sid SessionID, key [32]byte) *SecurePacket {
return &SecurePacket{
IsRsa: 1,
Nonce: [24]byte(make([]byte, 24)),
Sid: sid,
EncryptedData: key[:],
DataLength: 32,
}
}
func (secPck *SecurePacket) ExtractKey( /*RSA HERE LATER*/ ) []byte {
return secPck.EncryptedData[:32]
}
func PacketFromBytes(bytes []byte, dataLength uint32, sid SessionID) Packet {
flag := HeaderFlag(bytes[0])
sync := binary.LittleEndian.Uint32(bytes[1:5])
pck := Packet{
Sid: sid,
Flag: flag,
Sync: sync,
DataLength: dataLength,
Data: bytes[HeaderSize : HeaderSize+int(dataLength)],
}
return pck
}
func NewAck(pckToAck *Packet) *Packet {
data := make([]byte, 4)
binary.LittleEndian.PutUint32(data, pckToAck.Sync)
return &Packet{
Sid: pckToAck.Sid,
Flag: Ack,
Sync: pckToAck.Sync + 1,
DataLength: uint32(4),
Data: data,
}
}
func NewRequest(path string) *Packet {
data := []byte(path)
buf := make([]byte, 32)
_, err := rand.Read(buf)
if err != nil {
panic(err)
}
return &Packet{
Sid: SessionID(buf),
Flag: Request,
Sync: 0,
DataLength: uint32(len(data)),
Data: data,
}
}
func (pck *Packet) GetUint32Payload() (uint32, error) {
flag := pck.Flag
if flag != PTE && flag != Ack && flag != End && flag != Resend {
return 0, errors.New(fmt.Sprintf("Can not get Sync from Packet Type with flag: %v", flag))
}
return binary.LittleEndian.Uint32(pck.Data), nil
}
func (pck *Packet) GetFilePath() (string, error) {
if pck.Flag != Request {
return "", errors.New("Can not get FilePath from Packet that is not Request")
}
return string(pck.Data), nil
}
func NewResendFile(resendPck *Packet, data []byte) *Packet {
sync, _ := resendPck.GetUint32Payload()
return &Packet{
Sid: resendPck.Sid,
Flag: File,
Sync: sync,
DataLength: uint32(len(data)),
Data: data,
}
}
func NewFile(lastPck *Packet, data []byte) *Packet {
return &Packet{
Sid: lastPck.Sid,
Flag: File,
Sync: lastPck.Sync + 1,
DataLength: uint32(len(data)),
Data: data,
}
}
func NewEnd(lastFilePck *Packet) *Packet {
data := make([]byte, 4)
binary.LittleEndian.PutUint32(data, lastFilePck.Sync)
return &Packet{
Sid: lastFilePck.Sid,
Flag: End,
Sync: lastFilePck.Sync + 1,
DataLength: uint32(4),
Data: data,
}
}
func NewResend(sync uint32, lastPck *Packet) *Packet {
data := make([]byte, 4)
binary.LittleEndian.PutUint32(data, sync)
return &Packet{
Sid: lastPck.Sid,
Flag: Resend,
Sync: lastPck.Sync + 1,
DataLength: uint32(4),
Data: data,
}
}
func NewPte(fileSize uint32, lastPck *Packet) *Packet {
data := make([]byte, 4)
binary.LittleEndian.PutUint32(data, fileSize)
return &Packet{
Sid: lastPck.Sid,
Flag: PTE,
Sync: lastPck.Sync + 1,
DataLength: uint32(4),
Data: data,
}
}
func (pck *Packet) ToBytes() []byte {
arr := make([]byte, HeaderSize+int(pck.DataLength))
arr[0] = byte(pck.Flag)
binary.LittleEndian.PutUint32(arr[1:5], pck.Sync)
copy(arr[HeaderSize:HeaderSize+int(pck.DataLength)], pck.Data)
return arr
}
type HeaderFlag uint8
const (
Request HeaderFlag = iota
PTE HeaderFlag = iota
Ack HeaderFlag = iota
File HeaderFlag = iota
End HeaderFlag = iota
Resend HeaderFlag = iota
)