From 890a3f527bdd1ea135b5cb3cb26558dce2a475f3 Mon Sep 17 00:00:00 2001 From: Cyberes Date: Thu, 27 Jun 2024 17:16:47 -0600 Subject: [PATCH] mvp --- .gitignore | 4 +- README.md | 4 +- config.sample.yml | 6 ++ generate-key.sh | 4 ++ server/encryption/decrypt.go | 24 ++++++++ server/go.mod | 10 +++ server/go.sum | 8 +++ server/server.go | 116 +++++++++++++++++++++++++++++++++++ test/README.md | 3 + test/test.py | 28 +++++++++ 10 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 config.sample.yml create mode 100755 generate-key.sh create mode 100644 server/encryption/decrypt.go create mode 100644 server/go.mod create mode 100644 server/go.sum create mode 100644 server/server.go create mode 100644 test/README.md create mode 100644 test/test.py diff --git a/.gitignore b/.gitignore index 5d381cc..694044e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +.idea +config.yml + # ---> Python # Byte-compiled / optimized / DLL files __pycache__/ @@ -159,4 +162,3 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ - diff --git a/README.md b/README.md index 1560b8a..90c768b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ # freematics-traccar-encrypted -A proxy to encrypt the Traccar Freematics protocol. \ No newline at end of file +_A proxy to encrypt the Traccar Freematics protocol._ + +https://github.com/rfhigler/Freematics/commit/25cf781ca9fecc3e3082348ce9d28e4d69ff7764 diff --git a/config.sample.yml b/config.sample.yml new file mode 100644 index 0000000..b63f8b3 --- /dev/null +++ b/config.sample.yml @@ -0,0 +1,6 @@ +chacha_key: example123 + +destinations: + 5170: + address: 192.168.1.200 + port: 8080 \ No newline at end of file diff --git a/generate-key.sh b/generate-key.sh new file mode 100755 index 0000000..7f2df4f --- /dev/null +++ b/generate-key.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +echo "Key: $(openssl rand -hex 32)" + diff --git a/server/encryption/decrypt.go b/server/encryption/decrypt.go new file mode 100644 index 0000000..97bc7a3 --- /dev/null +++ b/server/encryption/decrypt.go @@ -0,0 +1,24 @@ +package encryption + +import ( + "errors" + "golang.org/x/crypto/chacha20poly1305" +) + +func Decrypt(key, ciphertextMsg []byte) ([]byte, error) { + aead, err := chacha20poly1305.New(key) + if err != nil { + return nil, err + } + + nonceSize := aead.NonceSize() + tagSize := aead.Overhead() + if len(ciphertextMsg) < nonceSize+tagSize { + return nil, errors.New("ciphertext too short") + } + + // Split nonce and ciphertext. + nonce, ciphertext, tag := ciphertextMsg[:nonceSize], ciphertextMsg[nonceSize:len(ciphertextMsg)-tagSize], ciphertextMsg[len(ciphertextMsg)-tagSize:] + + return aead.Open(nil, nonce, append(ciphertext, tag...), nil) +} diff --git a/server/go.mod b/server/go.mod new file mode 100644 index 0000000..aea9f59 --- /dev/null +++ b/server/go.mod @@ -0,0 +1,10 @@ +module server + +go 1.22.2 + +require ( + golang.org/x/crypto v0.24.0 + gopkg.in/yaml.v2 v2.4.0 +) + +require golang.org/x/sys v0.21.0 // indirect diff --git a/server/go.sum b/server/go.sum new file mode 100644 index 0000000..1d8f59f --- /dev/null +++ b/server/go.sum @@ -0,0 +1,8 @@ +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/server/server.go b/server/server.go new file mode 100644 index 0000000..3a7350b --- /dev/null +++ b/server/server.go @@ -0,0 +1,116 @@ +package main + +import ( + "encoding/hex" + "flag" + "fmt" + "gopkg.in/yaml.v2" + "net" + "os" + "server/encryption" +) + +type Config struct { + Destinations map[string]Destination `yaml:"destinations"` + ChachaKey string `yaml:"chacha_key"` +} + +type Destination struct { + Address string `yaml:"address"` + Port int `yaml:"port"` +} + +func main() { + configFile := flag.String("config", "", "Path to the configuration file") + flag.Parse() + + if *configFile == "" { + fmt.Println("Please provide a configuration file") + os.Exit(1) + } + + data, err := os.ReadFile(*configFile) + if err != nil { + fmt.Println("Error reading the configuration file:", err) + os.Exit(1) + } + + var config Config + err = yaml.Unmarshal(data, &config) + if err != nil { + fmt.Println("Error parsing the configuration file:", err) + os.Exit(1) + } + + // Validate chacha key + if len(config.ChachaKey) != 64 { + fmt.Println("Invalid chacha_key. Should be 64 characters long") + os.Exit(1) + } + + // Validate destinations + for port, dest := range config.Destinations { + if dest.Address == "" || dest.Port == 0 { + fmt.Printf("Invalid destination for port %s\n", port) + os.Exit(1) + } + } + + key, _ := hex.DecodeString(config.ChachaKey) + + for port, dest := range config.Destinations { + go func(port string, dest Destination) { + addr, err := net.ResolveUDPAddr("udp", ":"+port) + if err != nil { + fmt.Println("Error resolving address:", err) + return + } + + conn, err := net.ListenUDP("udp", addr) + if err != nil { + fmt.Println("Error listening on UDP:", err) + return + } + defer conn.Close() + + // Address to forward the decrypted messages + forwardAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", dest.Address, dest.Port)) + if err != nil { + fmt.Println("Error resolving forward address:", err) + return + } + + forwardConn, err := net.DialUDP("udp", nil, forwardAddr) + if err != nil { + fmt.Println("Error dialing to forward address:", err) + return + } + defer forwardConn.Close() + + for { + buf := make([]byte, 1500) // 1500 is the standard internet MTU + n, addr, err := conn.ReadFromUDP(buf) + if err != nil { + fmt.Println("Error reading from UDP:", err) + return + } + + plaintext, err := encryption.Decrypt(key, buf[:n]) // Use only the part of the buffer that has data + if err != nil { + fmt.Println("Error decrypting message:", err) + return + } + + fmt.Printf("%s -- %s\n", addr.IP, string(plaintext)) + + // Forward the decrypted message + _, err = forwardConn.Write(plaintext) + if err != nil { + fmt.Println("Error forwarding message:", err) + return + } + } + }(port, dest) + } + select {} +} diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000..2a7457e --- /dev/null +++ b/test/README.md @@ -0,0 +1,3 @@ +```shell +pip install pycryptodome +``` \ No newline at end of file diff --git a/test/test.py b/test/test.py new file mode 100644 index 0000000..f586102 --- /dev/null +++ b/test/test.py @@ -0,0 +1,28 @@ +import socket +import time + +from Crypto.Cipher import ChaCha20_Poly1305 + +# The server's address and port +server_address = ('localhost', 5170) + +# Create a UDP socket +sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + +# The message to be sent +message = ('Hello, Server! ' + str(time.time())).encode() + +# The key and nonce +key = bytes.fromhex('example123') + +# Encrypt the message +cipher = ChaCha20_Poly1305.new(key=key) +ciphertext, tag = cipher.encrypt_and_digest(message) + +# Send the encrypted message to the server +sock.sendto(cipher.nonce + ciphertext + tag, server_address) +sock.close() + +cipher = ChaCha20_Poly1305.new(key=key, nonce=cipher.nonce) +plaintext = cipher.decrypt_and_verify(ciphertext, tag) +print(plaintext)