From 49b25443fe86125904704cf425472d9daa8b2e11 Mon Sep 17 00:00:00 2001 From: Unbewohnte Date: Thu, 4 Nov 2021 23:05:16 +0300 Subject: [PATCH] Encryption --- src/addr/{outbound.go => local.go} | 2 +- src/encryption/decrypt.go | 2 +- src/encryption/encrypt.go | 2 +- src/node/node.go | 159 ++++++++++++++++++----------- src/node/packets.go | 30 +++++- src/node/transfer.go | 31 +++++- src/protocol/headers.go | 4 +- src/protocol/packet.go | 33 +++--- 8 files changed, 181 insertions(+), 82 deletions(-) rename src/addr/{outbound.go => local.go} (88%) diff --git a/src/addr/outbound.go b/src/addr/local.go similarity index 88% rename from src/addr/outbound.go rename to src/addr/local.go index a69ee25..8c3509d 100644 --- a/src/addr/outbound.go +++ b/src/addr/local.go @@ -5,7 +5,7 @@ import ( ) // Get local IP address; from https://stackoverflow.com/a/37382208 -func GetLocalIP() (string, error) { +func GetLocal() (string, error) { conn, err := net.Dial("udp", "8.8.8.8:80") if err != nil { return "", err diff --git a/src/encryption/decrypt.go b/src/encryption/decrypt.go index 5a8b648..504339d 100644 --- a/src/encryption/decrypt.go +++ b/src/encryption/decrypt.go @@ -7,7 +7,7 @@ import ( ) // Decrypts encrypted aes data with given key. -// https://www.melvinvivas.com/how-to-encrypt-and-decrypt-data-using-aes/ - very grateful to the author, THANK YOU. +// From https://www.melvinvivas.com/how-to-encrypt-and-decrypt-data-using-aes/ func Decrypt(key, dataToDecrypt []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { diff --git a/src/encryption/encrypt.go b/src/encryption/encrypt.go index 339f379..323aa12 100644 --- a/src/encryption/encrypt.go +++ b/src/encryption/encrypt.go @@ -7,7 +7,7 @@ import ( ) // Encrypts given data using aes encryption. -// https://www.melvinvivas.com/how-to-encrypt-and-decrypt-data-using-aes/ - very grateful to the author, THANK YOU. +// From https://www.melvinvivas.com/how-to-encrypt-and-decrypt-data-using-aes/ func Encrypt(key, dataToEncrypt []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { diff --git a/src/node/node.go b/src/node/node.go index 524c8c6..5b39493 100644 --- a/src/node/node.go +++ b/src/node/node.go @@ -14,6 +14,7 @@ import ( "github.com/Unbewohnte/ftu/addr" "github.com/Unbewohnte/ftu/checksum" + "github.com/Unbewohnte/ftu/encryption" "github.com/Unbewohnte/ftu/fsys" "github.com/Unbewohnte/ftu/protocol" ) @@ -42,6 +43,7 @@ type TransferInfo struct { // Sender and receiver in one type ! type Node struct { PacketPipe chan *protocol.Packet + ErrorPipe chan error Mutex *sync.Mutex IsSending bool Net *Net @@ -55,6 +57,7 @@ func NewNode(options *NodeOptions) (*Node, error) { node := Node{ PacketPipe: make(chan *protocol.Packet, 100), + ErrorPipe: make(chan error, 100), Mutex: mutex, IsSending: options.IsSending, Net: &Net{ @@ -147,7 +150,7 @@ func (node *Node) Start() { case true: // SENDER - localIP, err := addr.GetLocalIP() + localIP, err := addr.GetLocal() if err != nil { panic(err) } @@ -164,82 +167,96 @@ func (node *Node) Start() { panic(err) } + // generate and send encryption key + encrKey := encryption.Generate32AESkey() + node.Net.EncryptionKey = encrKey + fmt.Printf("Generated encryption key: %s\n", encrKey) + + err = sendEncryptionKey(node.Net.Conn, encrKey) + if err != nil { + panic(err) + } + // listen for incoming packets go receivePackets(node.Net.Conn, node.PacketPipe) // send fileoffer - go sendFilePacket(node.Net.Conn, file) + go sendFilePacket(node.Net.Conn, file, node.Net.EncryptionKey) // mainloop for { - node.Mutex.Lock() - stopped := node.State.Stopped - node.Mutex.Unlock() - - if stopped { - node.Mutex.Lock() + if node.State.Stopped { node.disconnect() - node.Mutex.Unlock() break } - incomingPacket := <-node.PacketPipe + incomingPacket, ok := <-node.PacketPipe + if !ok { + node.State.Stopped = true + } + + if node.Net.EncryptionKey != nil { + err = incomingPacket.DecryptBody(node.Net.EncryptionKey) + if err != nil { + panic(err) + } + } switch incomingPacket.Header { case protocol.HeaderReady: - node.Mutex.Lock() node.TransferInfo.Ready = true - node.Mutex.Unlock() case protocol.HeaderAccept: - node.Mutex.Lock() node.State.AllowedToTransfer = true - node.Mutex.Unlock() - go fmt.Printf("Transfer allowed. Sending...\n") + + fmt.Printf("Transfer allowed. Sending...\n") case protocol.HeaderDisconnecting: - node.Mutex.Lock() node.State.Stopped = true - node.Mutex.Unlock() - go fmt.Printf("%s disconnected\n", node.Net.Conn.RemoteAddr()) + + fmt.Printf("%s disconnected\n", node.Net.Conn.RemoteAddr()) case protocol.HeaderReject: - node.Mutex.Lock() node.State.Stopped = true - node.Mutex.Unlock() - go fmt.Printf("Transfer rejected. Disconnecting...") + + fmt.Printf("Transfer rejected. Disconnecting...") } - if node.State.AllowedToTransfer { - err = sendPiece(file, node.Net.Conn) + if node.State.AllowedToTransfer || node.TransferInfo.Ready { + err = sendPiece(file, node.Net.Conn, node.Net.EncryptionKey) if err != nil { if err == ErrorSentAll { // the file has been sent fully fileIDBuff := new(bytes.Buffer) err = binary.Write(fileIDBuff, binary.BigEndian, file.ID) if err != nil { - node.Mutex.Lock() - node.State.Stopped = true - node.Mutex.Unlock() + panic(err) } - protocol.SendPacket(node.Net.Conn, protocol.Packet{ + endFilePacket := protocol.Packet{ Header: protocol.HeaderEndfile, Body: fileIDBuff.Bytes(), - }) + } + + if node.Net.EncryptionKey != nil { + err = endFilePacket.EncryptBody(node.Net.EncryptionKey) + if err != nil { + panic(err) + } + } + + protocol.SendPacket(node.Net.Conn, endFilePacket) - node.Mutex.Lock() node.State.Stopped = true - node.Mutex.Unlock() } else { - node.Mutex.Lock() node.State.Stopped = true - node.Mutex.Unlock() fmt.Printf("An error occured when sending a piece of \"%s\": %s\n", file.Name, err) panic(err) } } + + node.TransferInfo.Ready = false } } @@ -257,14 +274,8 @@ func (node *Node) Start() { // mainloop for { - node.Mutex.Lock() - stopped := node.State.Stopped - node.Mutex.Unlock() - - if stopped { - node.Mutex.Lock() + if node.State.Stopped { node.disconnect() - node.Mutex.Unlock() break } @@ -272,8 +283,15 @@ func (node *Node) Start() { if !ok { break } + if node.Net.EncryptionKey != nil { + err = incomingPacket.DecryptBody(node.Net.EncryptionKey) + if err != nil { + panic(err) + } + } switch incomingPacket.Header { + case protocol.HeaderFile: go func() { file, err := decodeFilePacket(incomingPacket) @@ -311,11 +329,27 @@ func (node *Node) Start() { file.Path = fullFilePath file.Open() - node.Mutex.Lock() node.TransferInfo.AcceptedFiles = append(node.TransferInfo.AcceptedFiles, file) - node.Mutex.Unlock() - // notify the node that we`re ready to transportation + // send aceptance packet + acceptancePacket := protocol.Packet{ + Header: protocol.HeaderAccept, + Body: responsePacketFileIDBuffer.Bytes(), + } + if node.Net.EncryptionKey != nil { + err = acceptancePacket.EncryptBody(node.Net.EncryptionKey) + if err != nil { + panic(err) + } + } + + err = protocol.SendPacket(node.Net.Conn, acceptancePacket) + if err != nil { + panic(err) + } + + // notify the node that we`re ready to transportation. No need + // for encryption because the body is nil err = protocol.SendPacket(node.Net.Conn, protocol.Packet{ Header: protocol.HeaderReady, }) @@ -323,25 +357,26 @@ func (node *Node) Start() { panic(err) } - // send aceptance packet - protocol.SendPacket(node.Net.Conn, protocol.Packet{ - Header: protocol.HeaderAccept, - Body: responsePacketFileIDBuffer.Bytes(), - }) - } else { // no - err = protocol.SendPacket(node.Net.Conn, protocol.Packet{ + rejectionPacket := protocol.Packet{ Header: protocol.HeaderReject, Body: responsePacketFileIDBuffer.Bytes(), - }) + } + + if node.Net.EncryptionKey != nil { + err = rejectionPacket.EncryptBody(node.Net.EncryptionKey) + if err != nil { + panic(err) + } + } + + err = protocol.SendPacket(node.Net.Conn, rejectionPacket) if err != nil { panic(err) } - node.Mutex.Lock() node.State.Stopped = true - node.Mutex.Unlock() } }() @@ -354,7 +389,6 @@ func (node *Node) Start() { panic(err) } - node.Mutex.Lock() for _, acceptedFile := range node.TransferInfo.AcceptedFiles { if acceptedFile.ID == fileID { // accepted @@ -368,7 +402,6 @@ func (node *Node) Start() { } } } - node.Mutex.Unlock() err = protocol.SendPacket(node.Net.Conn, protocol.Packet{ Header: protocol.HeaderReady, @@ -385,7 +418,6 @@ func (node *Node) Start() { panic(err) } - node.Mutex.Lock() for index, acceptedFile := range node.TransferInfo.AcceptedFiles { if acceptedFile.ID == fileID { // accepted @@ -414,14 +446,23 @@ func (node *Node) Start() { } node.State.Stopped = true - node.Mutex.Unlock() + + case protocol.HeaderEncryptionKey: + // retrieve the key + packetReader := bytes.NewReader(incomingPacket.Body) + + var keySize uint64 + binary.Read(packetReader, binary.BigEndian, &keySize) + + encrKey := make([]byte, keySize) + packetReader.Read(encrKey) + + node.Net.EncryptionKey = encrKey case protocol.HeaderDisconnecting: - node.Mutex.Lock() node.State.Stopped = true - node.Mutex.Unlock() - go fmt.Printf("%s disconnected\n", node.Net.Conn.RemoteAddr()) + fmt.Printf("%s disconnected\n", node.Net.Conn.RemoteAddr()) } } diff --git a/src/node/packets.go b/src/node/packets.go index f5094d2..1f00675 100644 --- a/src/node/packets.go +++ b/src/node/packets.go @@ -10,7 +10,32 @@ import ( "github.com/Unbewohnte/ftu/protocol" ) -// Reads packets from connection in an endless loop, sends them to the channel +// sends an encryption key to the other side +func sendEncryptionKey(connection net.Conn, encrKey []byte) error { + encrKeyPacketBuffer := new(bytes.Buffer) + + encrKeyLength := uint64(len(encrKey)) + + err := binary.Write(encrKeyPacketBuffer, binary.BigEndian, &encrKeyLength) + if err != nil { + return err + } + + encrKeyPacketBuffer.Write(encrKey) + + err = protocol.SendPacket(connection, protocol.Packet{ + Header: protocol.HeaderEncryptionKey, + Body: encrKeyPacketBuffer.Bytes(), + }) + if err != nil { + return err + } + + return nil +} + +// Reads packets from connection in an endless loop, sends them to the channel, if encrKey is not nil - +// decrypts packet`s body beforehand func receivePackets(connection net.Conn, packetPipe chan *protocol.Packet) error { for { if connection == nil { @@ -33,7 +58,8 @@ func receivePackets(connection net.Conn, packetPipe chan *protocol.Packet) error } } -// decodes packet with the header FILE into the fsys.File struct +// decodes packet with the header FILE into the fsys.File struct. If encrKey is not nil - +// filepacket`s body will be decrypted beforehand func decodeFilePacket(filePacket *protocol.Packet) (*fsys.File, error) { // FILE~(idInBinary)(filenameLengthInBinary)(filename)(filesize)(checksumLengthInBinary)checksum diff --git a/src/node/transfer.go b/src/node/transfer.go index 4c718c5..8afdef3 100644 --- a/src/node/transfer.go +++ b/src/node/transfer.go @@ -12,8 +12,8 @@ import ( "github.com/Unbewohnte/ftu/protocol" ) -// sends a notification about the file -func sendFilePacket(connection net.Conn, file *fsys.File) error { +// sends a notification about the file to the other side. If encrKey is not nil - encrypts packet`s body +func sendFilePacket(connection net.Conn, file *fsys.File, encrKey []byte) error { if connection == nil { return ErrorNotConnected } @@ -54,6 +54,15 @@ func sendFilePacket(connection net.Conn, file *fsys.File) error { filePacket.Body = fPacketBodyBuff.Bytes() + if encrKey != nil { + // if the key is given - encrypt ready-to-go packet + err = filePacket.EncryptBody(encrKey) + if err != nil { + return err + } + } + + // and send it err = protocol.SendPacket(connection, filePacket) if err != nil { return err @@ -72,8 +81,9 @@ func sendDirectoryPacket(connection net.Conn, dir *fsys.Directory) error { } // sends a piece of file to the connection; The next calls will send -// another piece util the file has been fully sent -func sendPiece(file *fsys.File, connection net.Conn) error { +// another piece util the file has been fully sent. If encrKey is not nil - encrypts each packet with +// this key +func sendPiece(file *fsys.File, connection net.Conn, encrKey []byte) error { if file.Handler == nil { fHandler, err := os.Open(file.Path) if err != nil { @@ -106,9 +116,15 @@ func sendPiece(file *fsys.File, connection net.Conn) error { // fill the remaining space of packet with the contents of a file canSendBytes := uint64(protocol.MAXPACKETSIZE) - fileBytesPacket.Size() - uint64(packetBodyBuff.Len()) + if encrKey != nil { + // account for padding + canSendBytes -= 32 + } + if (file.Size - file.SentBytes) < canSendBytes { canSendBytes = (file.Size - file.SentBytes) } + fileBytes := make([]byte, canSendBytes) read, err := file.Handler.ReadAt(fileBytes, int64(file.SentBytes)) @@ -120,6 +136,13 @@ func sendPiece(file *fsys.File, connection net.Conn) error { fileBytesPacket.Body = packetBodyBuff.Bytes() + if encrKey != nil { + err = fileBytesPacket.EncryptBody(encrKey) + if err != nil { + return err + } + } + // send it to the other side err = protocol.SendPacket(connection, fileBytesPacket) if err != nil { diff --git a/src/protocol/headers.go b/src/protocol/headers.go index ae1dcc4..d8d3e68 100644 --- a/src/protocol/headers.go +++ b/src/protocol/headers.go @@ -9,8 +9,8 @@ type Header string // ENCRKEY. // The FIRST header to be sent. Sent immediately after the connection has been established -// by sender. Body contains randomly generated by sender aes encryption key. -// ie: ENCRKEY~SUPER_SECURE_ENCRYPTION_KEY_|||| +// by sender. Body contains a size of a key and the key itself. +// ie: ENCRKEY~(size)(SUPER SECURE ENCRYPTION KEY) const HeaderEncryptionKey Header = "ENCRKEY" // REJECT. diff --git a/src/protocol/packet.go b/src/protocol/packet.go index 2806808..76f01dd 100644 --- a/src/protocol/packet.go +++ b/src/protocol/packet.go @@ -59,7 +59,7 @@ func BytesToPacket(packetbytes []byte) (*Packet, error) { }, nil } -var ErrorExceededMaxPacketsize error = fmt.Errorf("too big packet") +var ErrorExceededMaxPacketsize error = fmt.Errorf("the packet is too big") // Converts given packet struct into ready-to-transfer bytes, constructed by following the protocol func (packet *Packet) ToBytes() ([]byte, error) { @@ -86,14 +86,15 @@ func (packet *Packet) ToBytes() ([]byte, error) { return packetBuffer.Bytes(), nil } -// Sends given packet to connection, following all the protocol`s rules. +// Sends given packet to connection. // ALL packets MUST be sent by this method func SendPacket(connection net.Conn, packet Packet) error { packetBytes, err := packet.ToBytes() if err != nil { return err } - // fmt.Printf("DEBUG: sending packet %+v\n", packet) + + // fmt.Printf("[SEND] packet %+s; len: %d\n", packetBytes[:30], len(packetBytes)) // write the result (ie: (packetsize)(header)~(bodybytes)) connection.Write(packetBytes) @@ -113,6 +114,22 @@ func (packet *Packet) EncryptBody(key []byte) error { return nil } +// Decrypts packet`s BODY with AES decryption +func (packet *Packet) DecryptBody(key []byte) error { + if len(packet.Body) == 0 { + return nil + } + + decryptedBody, err := encryption.Decrypt(key, packet.Body) + if err != nil { + return err + } + + packet.Body = decryptedBody + + return nil +} + // Reads a packet from given connection, returns its bytes. // ASSUMING THAT THE PACKETS ARE SENT BY `SendPacket` function !!!! func ReadFromConn(connection net.Conn) ([]byte, error) { @@ -143,15 +160,7 @@ func ReadFromConn(connection net.Conn) ([]byte, error) { packetBuffer.Write(buff[:read]) } - // read the rest of the packet - // packet := make([]byte, packetSize) - // read, err := connection.Read(packet) - // if err != nil { - // return nil, err - // } - - // fmt.Printf("DEBUG: read from connection: %s; length: %d\n", packetBuffer.Bytes()[:40], packetBuffer.Len()) - // fmt.Printf("DEBUG: read from connection: %s; length: %d\n", packet, len(packet)) + // fmt.Printf("[RECV] read from connection: %s; length: %d\n", packetBuffer.Bytes()[:30], packetBuffer.Len()) return packetBuffer.Bytes(), nil }