add some more things

This commit is contained in:
Cyberes 2024-06-27 22:35:28 -06:00
parent 2fd486a6e4
commit 28cb7ecc43
6 changed files with 133 additions and 23 deletions

View File

@ -2,4 +2,5 @@
_A proxy to encrypt the Traccar Freematics protocol._
Inspired by previous work:
https://github.com/rfhigler/Freematics/commit/25cf781ca9fecc3e3082348ce9d28e4d69ff7764

View File

@ -17,8 +17,9 @@ func Decrypt(key, ciphertextMsg []byte) ([]byte, error) {
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:]
// Split the message apart.
// The order is nonce, ciphertext, and tag. The last two aren't used.
nonce, _, _ := ciphertextMsg[:nonceSize], ciphertextMsg[nonceSize:len(ciphertextMsg)-tagSize], ciphertextMsg[len(ciphertextMsg)-tagSize:]
return aead.Open(nil, nonce, append(ciphertext, tag...), nil)
return aead.Open(nil, nonce, ciphertextMsg[nonceSize:], nil)
}

View File

@ -0,0 +1,24 @@
package encryption
import (
"crypto/rand"
"golang.org/x/crypto/chacha20poly1305"
"io"
)
func Encrypt(key, plaintextMsg []byte) ([]byte, error) {
aead, err := chacha20poly1305.New(key)
if err != nil {
return nil, err
}
// Generate a new nonce for this encryption.
nonce := make([]byte, aead.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}
// Encrypt the message and append the nonce and the ciphertext.
ciphertext := aead.Seal(nonce, nonce, plaintextMsg, nil)
return ciphertext, nil
}

View File

@ -73,13 +73,53 @@ func main() {
}
defer conn.Close()
// Address to forward the decrypted messages
logger.Infof("Listening on 0.0.0.0:%s\n", port)
for {
buf := make([]byte, 1500) // 1500 is the standard internet MTU.
n, addr, err := conn.ReadFromUDP(buf)
if err != nil {
logger.Fatalf(formatLogMsg(addr.IP.String(), dest.Address, dest.Port, fmt.Sprintf("Error reading from UDP: %s", err)))
}
// Handle the message.
go func(addr *net.UDPAddr, buf []byte, n int) {
// Do the decryption.
var plaintext []byte
if len(buf[:n]) > 0 {
plaintext, err = encryption.Decrypt(key, buf[:n]) // Use only the part of the buffer that has data.
if err != nil {
rawHex := hex.EncodeToString(buf[:n])
logger.Warnf(formatLogMsg(addr.IP.String(), dest.Address, dest.Port, fmt.Sprintf(`Error decrypting message: %s. Length: %d, Raw: "%s"`, err, len(rawHex), rawHex)))
// Forward the raw message to the backend without bothering with decryption.
plaintext = buf[:n]
}
} else {
plaintext = buf[:n]
}
forwardAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", dest.Address, dest.Port))
if err != nil {
logger.Fatalln("Error resolving forward address:", err)
return
}
// Create a new UDP address for listening to the backend server's response.
listenAddr, err := net.ResolveUDPAddr("udp", ":0") // Let the OS pick a free port.
if err != nil {
logger.Fatalln("Error resolving listen address:", err)
return
}
// Create a new UDP listener for the backend server's response.
listenConn, err := net.ListenUDP("udp", listenAddr)
if err != nil {
logger.Fatalln("Error listening for backend response:", err)
return
}
defer listenConn.Close()
// Dial the backend server without binding a local address.
forwardConn, err := net.DialUDP("udp", nil, forwardAddr)
if err != nil {
logger.Fatalln("Error dialing to forward address:", err)
@ -87,29 +127,37 @@ func main() {
}
defer forwardConn.Close()
logger.Infof("Listening on 0.0.0.0:%s\n", port)
for {
buf := make([]byte, 1500) // 1500 is the standard internet MTU
n, addr, err := conn.ReadFromUDP(buf)
if err != nil {
logger.Fatalf(formatLogMsg(addr.IP.String(), dest.Address, dest.Port, fmt.Sprintf("Error reading from UDP: %s", err)))
continue
}
// Forward the decrypted message
go func(addr *net.UDPAddr, buf []byte, n int) {
plaintext, err := encryption.Decrypt(key, buf[:n]) // Use only the part of the buffer that has data
if err != nil {
logger.Errorf(formatLogMsg(addr.IP.String(), dest.Address, dest.Port, fmt.Sprintf("Error decrypting message: %s", err)))
return
}
// Forward the plaintext to the backend.
_, err = forwardConn.Write(plaintext)
if err != nil {
logger.Errorf(formatLogMsg(addr.IP.String(), dest.Address, dest.Port, fmt.Sprintf("Error forwarding message: %s", err)))
return
}
// Read the response from the backend.
backendResponse := make([]byte, 1500)
n, err = forwardConn.Read(backendResponse)
if err != nil {
logger.Errorf(formatLogMsg(addr.IP.String(), dest.Address, dest.Port, fmt.Sprintf("Error reading response from backend server: %s", err)))
return
}
fmt.Println(string(backendResponse[:]))
// Encrypt the backend's response.
encryptedBackendResponse, err := encryption.Encrypt(key, backendResponse[:n])
if err != nil {
logger.Errorf(formatLogMsg(addr.IP.String(), dest.Address, dest.Port, fmt.Sprintf("Error encrypting response: %s", err)))
return
}
// Forward the encrypted backend response to the client.
_, err = conn.WriteToUDP(encryptedBackendResponse, addr)
if err != nil {
logger.Errorf(formatLogMsg(addr.IP.String(), dest.Address, dest.Port, fmt.Sprintf("Error forwarding response to client: %s", err)))
return
}
logger.Infof(formatLogMsg(addr.IP.String(), dest.Address, dest.Port, string(plaintext)))
}(addr, buf, n)
}

View File

@ -13,7 +13,7 @@ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
message = ('Hello, Server! ' + str(time.time())).encode()
# The key and nonce
key = bytes.fromhex('example123')
key = bytes.fromhex('d38a3b96a26d0b1139bd30c174884f5dbc8eaaf492493725633ecebfa4ab19e9')
# Encrypt the message
cipher = ChaCha20_Poly1305.new(key=key)

36
test/test_raw.py Normal file
View File

@ -0,0 +1,36 @@
import socket
import binascii
from Crypto.Cipher import ChaCha20_Poly1305
# Send an initalization message and decrypt the response.
# Use this key: d38a3b96a26d0b1139bd30c174884f5dbc8eaaf492493725633ecebfa4ab19e9
# The server's address and port
server_address = ('localhost', 5171)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# Convert hex string to bytes
ciphertext = binascii.unhexlify('40a7a1ef284e36e65cdb87abdb9aaea7ba4df5ae527b7311ba79a7d7f73729268d5b136c0c701fe366d775315f33e9ef893214fbf26a6ec281c8eadf46663b9d90')
# Send the encrypted message to the server
sock.sendto(ciphertext, server_address)
# Receive the response from the server
response, server = sock.recvfrom(4096)
# Decrypt that response
key = bytes.fromhex('d38a3b96a26d0b1139bd30c174884f5dbc8eaaf492493725633ecebfa4ab19e9')
# ChaCha20_Poly1305 nonce size is 12 bytes and tag size is 16 bytes
nonce = response[:12]
ciphertext_and_tag = response[12:]
ciphertext = ciphertext_and_tag[:-16]
tag = ciphertext_and_tag[-16:]
cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce)
plaintext = cipher.decrypt_and_verify(ciphertext, tag)
print("Decrypted message: ", plaintext)
sock.close()