This commit is contained in:
Zheyuan Wu
2026-02-11 20:13:53 -06:00
parent 6cc8ce61b3
commit 5a45bd312d
14 changed files with 614 additions and 35 deletions

4
.env
View File

@@ -2,5 +2,5 @@ NET_SUBNET=172.30.0.0/24
SERVER_IP=172.30.0.10
CLIENT_IP=172.30.0.11
SERVER_PORT=3030
INITIAL_VECTOR=807DEDFDE74B0C59
SECRET_KEY=EA514659DC556DEECFFA5FD061363642
INITIAL_VECTOR=EA514659DC556DEECFFA5FD061363642
SECRET_KEY=0214BA9480AE8D21842B80FF5287418FD0465113070FDFF8263F0F1FCD6CF030

Binary file not shown.

Binary file not shown.

View File

@@ -1 +0,0 @@
docker exec -it hw1-client sh

View File

@@ -34,6 +34,25 @@ void load_env(const std::string& path) {
}
}
// hex to byte helper function
int hex_to_bytes_upper(const char* hex, unsigned char* out, size_t out_size)
{
size_t i = 0;
while (hex[0] && hex[1]) {
if (i >= out_size) return -1;
unsigned char h = hex[0];
unsigned char l = hex[1];
int hi = (h <= '9') ? (h - '0') : (h - 'A' + 10);
int lo = (l <= '9') ? (l - '0') : (l - 'A' + 10);
// minimal sanity check
if (hi < 0 || hi > 15 || lo < 0 || lo > 15)
return -1;
out[i++] = (unsigned char)((hi << 4) | lo);
hex += 2;
}
return (int)i;
}
// steam cipher encryption function
int stream_encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key, unsigned char *iv, unsigned char *ciphertext) {
/* Declare cipher context */
@@ -43,6 +62,9 @@ int stream_encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *k
/* Create and initialise the context */
ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
ERR_print_errors_fp(stderr);
}
/* Initialise the encryption operation. */
// choice for aes_256 ref: https://stackoverflow.com/questions/1220751/how-to-choose-an-aes-encryption-mode-cbc-ecb-ctr-ocb-cfb
@@ -63,7 +85,7 @@ int stream_encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *k
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
return ciphertext_len;
return len + ciphertext_len;
}
int main(void){
@@ -80,7 +102,16 @@ int main(void){
// encryption parameters
const char *inital_vector = std::getenv("INITIAL_VECTOR");
const char *secret_key = std::getenv("SECRET_KEY");
unsigned char key_bytes[32], iv_bytes[16];
int key_len = hex_to_bytes_upper(secret_key, key_bytes, sizeof(key_bytes));
int iv_len = hex_to_bytes_upper(inital_vector, iv_bytes, sizeof(iv_bytes));
if (key_len != 32 || iv_len != 16) {
fprintf(stderr, "Invalid key/IV size for AES-256\n");
return 1;
}
// Create socket:
int client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket == -1) {
@@ -121,14 +152,23 @@ int main(void){
char buffer[2048];
std::snprintf(buffer, sizeof(buffer), "%s%s", custom_message, client_message);
// encrypt message
char ciphertext[2048];
int ciphertext_len = stream_encrypt((unsigned char*)buffer, sizeof(buffer), (unsigned char*)secret_key, (unsigned char*)inital_vector, (unsigned char*)ciphertext);
unsigned char ciphertext[2048];
send(client_socket, ciphertext, ciphertext_len, 0);
int plaintext_len = (int)strlen(buffer);
int ciphertext_len = stream_encrypt((unsigned char*)buffer, plaintext_len, key_bytes, iv_bytes, ciphertext);
if (ciphertext_len <= 0) {
printf("encrypt failed or produced empty ciphertext_len=%d\n", ciphertext_len);
break;
}
ssize_t sent = send(client_socket, ciphertext, (size_t)ciphertext_len, 0);
if (sent <= 0) { perror("No message sent to server"); break; }
// Receive the server's response:
recv(client_socket, server_message, sizeof(server_message), 0);
// add terminator for string
ssize_t recvd = recv(client_socket, server_message, sizeof(server_message) - 1, 0);
if (recvd <= 0) { perror("No message received from server"); break; }
server_message[recvd] = '\0';
printf("Server's response: %s\n", server_message);

View File

@@ -19,8 +19,9 @@ file bin/server bin/client || true
chmod a+x bin/server bin/client
# clean up existing containers and networks
docker compose down
# stop the containers and free up network resources
docker compose down --remove-orphans || true
docker network rm net-hw1 >/dev/null 2>&1 || true
# start the containers
docker compose up -d server

Binary file not shown.

View File

@@ -103,6 +103,8 @@
% Use an enumerated list to write up problems. First we begin a list.
\textit{Source code and detailed setup scripts are available at \url{https://git.trance-0.com/Trance-0/CSE4303H1}.}
\begin{enumerate}
\item[1.] Description of experimental setup
@@ -110,14 +112,288 @@ We use the same docker image and settings with environment files as last time.
For encryption method, I checked some wikis on the internet and found aes seems to be the best practically safe algorithm I can found.
For the detailed method and distinctions for different variations for aes, I checked the post on \href{stackoverflow.com/questions/1220751/how-to-choose-an-aes-encryption-mode-cbc-ecb-ctr-ocb-cfb}{stackoverflow} for steam cipher, seems that \texttt{NVP\_aes\_256\_ctr()} is the optimal choice for us.
For the detailed method and distinctions for different variations for aes, I checked the post on \href{stackoverflow.com/questions/1220751/how-to-choose-an-aes-encryption-mode-cbc-ecb-ctr-ocb-cfb}{stackoverflow} for steam cipher, seems that \texttt{EVP\_aes\_256\_ctr()} is the optimal choice for us.
For docker network, I used the same network as last time with the following compose file:
\begin{lstlisting}[language=bash]
services:
server:
image: debian:bookworm-slim
container_name: hw1-server
working_dir: /app
env_file:
- .env
networks:
net-hw1:
ipv4_address: ${SERVER_IP}
volumes:
- ./bin/server:/usr/local/bin/server:ro
entrypoint: ["bash", "-lc", "apt-get update && apt-get install -y --no-install-recommends libstdc++6 iproute2 libssl-dev; exec stdbuf -oL -eL /usr/local/bin/server"]
client:
image: debian:bookworm-slim
container_name: hw1-client
working_dir: /app
env_file:
- .env
networks:
net-hw1:
ipv4_address: ${CLIENT_IP}
depends_on:
- server
stdin_open: true
tty: true
volumes:
- ./bin/client:/usr/local/bin/client:ro
entrypoint: ["bash", "-lc", "apt-get update && apt-get install -y --no-install-recommends libstdc++6 iproute2 libssl-dev; exec stdbuf -oL -eL /usr/local/bin/client"]
networks:
net-hw1:
name: net-hw1
driver: bridge
ipam:
config:
- subnet: ${NET_SUBNET}
\end{lstlisting}
The environment file is as follows:
\begin{lstlisting}[language=bash]
NET_SUBNET=172.30.0.0/24
SERVER_IP=172.30.0.10
CLIENT_IP=172.30.0.11
SERVER_PORT=3030
INITIAL_VECTOR=EA514659DC556DEECFFA5FD061363642
SECRET_KEY=0214BA9480AE8D21842B80FF5287418FD0465113070FDFF8263F0F1FCD6CF030
\end{lstlisting}
\newpage
\item[2.] Server code
\begin{enumerate}
\item Screenshot or well-formatted copy of code
\begin{lstlisting}[language=c++]
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
// open ssl
#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <errno.h> // for perror()
#include <unistd.h> // for close()
// load environment variables from .env file
#include <fstream>
#include <cstdlib>
void load_env(const std::string& path) {
std::ifstream f(path);
std::string line;
while (std::getline(f, line)) {
if (line.empty() || line[0] == '#') continue;
auto pos = line.find('=');
if (pos == std::string::npos) continue;
std::string key = line.substr(0, pos);
std::string val = line.substr(pos + 1);
#ifdef _WIN32
_putenv_s(key.c_str(), val.c_str());
#else
setenv(key.c_str(), val.c_str(), 1);
#endif
}
}
// hex to byte helper function
int hex_to_bytes_upper(const char* hex, unsigned char* out, size_t out_size)
{
size_t i = 0;
while (hex[0] && hex[1]) {
if (i >= out_size) return -1;
unsigned char h = hex[0];
unsigned char l = hex[1];
int hi = (h <= '9') ? (h - '0') : (h - 'A' + 10);
int lo = (l <= '9') ? (l - '0') : (l - 'A' + 10);
// minimal sanity check
if (hi < 0 || hi > 15 || lo < 0 || lo > 15)
return -1;
out[i++] = (unsigned char)((hi << 4) | lo);
hex += 2;
}
return (int)i;
}
// Define the decryption function
int stream_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, unsigned char *iv, unsigned char *plaintext) {
/* Declare cipher context */
EVP_CIPHER_CTX *ctx;
int len, plaintext_len;
/* Create and initialise the context */
ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
ERR_print_errors_fp(stderr);
}
/* Initialise the decryption operation. */
if (EVP_DecryptInit_ex(ctx, EVP_aes_256_ctr(), NULL, key, iv) != 1){
ERR_print_errors_fp(stderr);
}
/* Provide the message to be decrypted, and obtain the plaintext output. EVP_DecryptUpdate can be called multiple times if necessary */
if (EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len) != 1) {
ERR_print_errors_fp(stderr);
}
/* Finalize the decryption. Further plaintext bytes may be written at this stage. */
if (EVP_DecryptFinal_ex(ctx, plaintext + len, &plaintext_len) != 1) {
ERR_print_errors_fp(stderr);
}
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
return len + plaintext_len;
}
int main(void){
printf("Server starting...\n");
load_env(".env");
// Declare variables
const char *server_ip = std::getenv("SERVER_IP");
const int server_port = std::atoi(std::getenv("SERVER_PORT"));
char client_message[2048];
char server_message[2048];
const char * custom_message="Server: Hello from server, message recieved!\n";
// decryption parameters
const char *inital_vector = std::getenv("INITIAL_VECTOR");
const char *secret_key = std::getenv("SECRET_KEY");
unsigned char key_bytes[32], iv_bytes[16];
int key_len = hex_to_bytes_upper(secret_key, key_bytes, sizeof(key_bytes));
int iv_len = hex_to_bytes_upper(inital_vector, iv_bytes, sizeof(iv_bytes));
if (key_len != 32 || iv_len != 16) {
fprintf(stderr, "Invalid key/IV size for AES-256\n");
return 1;
}
// debug
printf("Server starting at IP: %s, Port: %d\n", server_ip, server_port);
// Create socket
const int server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1) {
perror("Failed to create socket");
return 1;
}
// Bind to the set port and IP
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(server_port);
inet_pton(AF_INET, server_ip, &server_addr.sin_addr);
if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("Failed to bind socket");
close(server_socket);
return 1;
}
printf("Done with binding with IP: %s, Port: %d\n", server_ip, server_port);
// Listen for clients:
const char *client_ip = std::getenv("CLIENT_IP");
if (listen(server_socket, 1) == -1) {
perror("Failed to listen on socket");
close(server_socket);
return 1;
}
printf("Listening for incoming connections...\n");
// Accept an incoming connection
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);
int client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_addr_len);
if (client_socket == -1) {
perror("Failed to accept connection");
close(server_socket);
return 1;
}
printf("Client connected at IP: %s \n", client_ip);
// clean exising buffer
memset(client_message, 0, sizeof(client_message));
// Receive client's message
while (1) {
// clean exising buffer
memset(client_message, 0, sizeof(client_message));
ssize_t n = recv(client_socket, client_message, sizeof(client_message), 0);
if (n == 0) break;
if (n < 0) { perror("recv"); break; }
// decrypt client message
unsigned char plaintext[2048];
int plaintext_len = stream_decrypt((unsigned char*)client_message, (int)n, key_bytes, iv_bytes, plaintext);
if (plaintext_len < 0) {
fprintf(stderr, "decrypt failed\n");
break;
}
if (plaintext_len >= (int)sizeof(plaintext)) plaintext_len = (int)sizeof(plaintext) - 1;
plaintext[plaintext_len] = '\0';
if (strcmp((char*)plaintext, "\\exit\n") == 0) break;
printf("Msg from client: %s", plaintext);
// Respond to client
// prepare server message
memcpy(server_message, custom_message, strlen(custom_message));
size_t reply_len = strlen(server_message);
if (send(client_socket, server_message, reply_len, 0) == -1) {
perror("Send failed");
break;
}
printf("Response sent to client: %s\n", server_message);
}
// Close the socket
close(client_socket);
close(server_socket);
return 0;
}
\end{lstlisting}
\item Quick overview of what the code does in your own words.
Compared with previous assignment, we added a new function to decrypt the message from client, and modify the code and allow the server to process multiple client request.
In addition to environment loading function, we build another hex to byes function to convert hex string to bytes used for initial vector and secret key.
In \texttt{stream\_decrypt} function, first we initialize the context for aes, then we decrypt the message from client, and finally we return the decrypted message. For each step, we capture possible errors and output the error message using \texttt{ERR\_print\_errors\_fp} function.
After decrypting the message from client, we print the message on the server side and told the client we received the encrypted message.
Here is the screenshot for the server output:
\begin{figure}[H]
\centering
\includegraphics[width=1\textwidth]{./images/server.png}
\caption{Server output}
\end{figure}
\end{enumerate}
\newpage
@@ -126,13 +402,244 @@ For the detailed method and distinctions for different variations for aes, I che
\begin{enumerate}
\item Screenshot or well-formatted copy of code
\begin{lstlisting}[language=c++]
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
// open ssl
#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <errno.h> // error handling
#include <unistd.h> // for close()
// load environment variables from .env file
#include <fstream>
#include <cstdlib>
void load_env(const std::string& path) {
std::ifstream f(path);
std::string line;
while (std::getline(f, line)) {
if (line.empty() || line[0] == '#') continue;
auto pos = line.find('=');
if (pos == std::string::npos) continue;
std::string key = line.substr(0, pos);
std::string val = line.substr(pos + 1);
#ifdef _WIN32
_putenv_s(key.c_str(), val.c_str());
#else
setenv(key.c_str(), val.c_str(), 1);
#endif
}
}
// hex to byte helper function
int hex_to_bytes_upper(const char* hex, unsigned char* out, size_t out_size)
{
size_t i = 0;
while (hex[0] && hex[1]) {
if (i >= out_size) return -1;
unsigned char h = hex[0];
unsigned char l = hex[1];
int hi = (h <= '9') ? (h - '0') : (h - 'A' + 10);
int lo = (l <= '9') ? (l - '0') : (l - 'A' + 10);
// minimal sanity check
if (hi < 0 || hi > 15 || lo < 0 || lo > 15)
return -1;
out[i++] = (unsigned char)((hi << 4) | lo);
hex += 2;
}
return (int)i;
}
// steam cipher encryption function
int stream_encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key, unsigned char *iv, unsigned char *ciphertext) {
/* Declare cipher context */
EVP_CIPHER_CTX *ctx;
int len, ciphertext_len;
/* Create and initialise the context */
ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
ERR_print_errors_fp(stderr);
}
/* Initialise the encryption operation. */
// choice for aes_256 ref: https://stackoverflow.com/questions/1220751/how-to-choose-an-aes-encryption-mode-cbc-ecb-ctr-ocb-cfb
if (EVP_EncryptInit_ex(ctx, EVP_aes_256_ctr(), NULL, key, iv) != 1){
ERR_print_errors_fp(stderr);
}
/* Provide the message to be encrypted, and obtain the encrypted output. EVP_EncryptUpdate can be called multiple times if necessary */
if (EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len) != 1) {
ERR_print_errors_fp(stderr);
}
/* Finalize the encryption. Further cipher text bytes may be written at this stage. */
if (EVP_EncryptFinal_ex(ctx, ciphertext + len, &ciphertext_len) != 1) {
ERR_print_errors_fp(stderr);
}
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
return len + ciphertext_len;
}
int main(void){
load_env(".env");
// Declare variables
const char *server_ip = std::getenv("SERVER_IP");
const int server_port = std::atoi(std::getenv("SERVER_PORT"));
printf("Connecting to server %s:%d\n", server_ip, server_port);
char client_message[1024];
char server_message[1024];
const char * custom_message="Zheyuan Wu: ";
// encryption parameters
const char *inital_vector = std::getenv("INITIAL_VECTOR");
const char *secret_key = std::getenv("SECRET_KEY");
unsigned char key_bytes[32], iv_bytes[16];
int key_len = hex_to_bytes_upper(secret_key, key_bytes, sizeof(key_bytes));
int iv_len = hex_to_bytes_upper(inital_vector, iv_bytes, sizeof(iv_bytes));
if (key_len != 32 || iv_len != 16) {
fprintf(stderr, "Invalid key/IV size for AES-256\n");
return 1;
}
// Create socket:
int client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket == -1) {
perror("Failed to create socket");
return 1;
}else{
printf("Socket created successfully\n");
}
// Send connection request to server, be sure to set por tand IP the same as server-side
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(server_port);
inet_pton(AF_INET, server_ip, &server_addr.sin_addr);
if (connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("Failed to connect to server");
close(client_socket);
return 1;
}else{
printf("Connected to server successfully\n");
}
// Get input from the user:
printf("Enter message sent to the server (type \\quit to exit): ");
// clean the buffer
memset(client_message, 0, sizeof(client_message));
if (fgets(client_message, sizeof(client_message), stdin) == NULL) {
// EOF or error reading from stdin, exit the loop
perror("Error reading from stdin");
return 1;
}
while (strcmp(client_message, "\\quit\n") != 0) {
// Send the message to server:
// add my name in the front
char buffer[2048];
std::snprintf(buffer, sizeof(buffer), "%s%s", custom_message, client_message);
unsigned char ciphertext[2048];
int plaintext_len = (int)strlen(buffer);
int ciphertext_len = stream_encrypt((unsigned char*)buffer, plaintext_len, key_bytes, iv_bytes, ciphertext);
if (ciphertext_len <= 0) {
printf("encrypt failed or produced empty ciphertext_len=%d\n", ciphertext_len);
break;
}
ssize_t sent = send(client_socket, ciphertext, (size_t)ciphertext_len, 0);
if (sent <= 0) { perror("No message sent to server"); break; }
// Receive the server's response:
// add terminator for string
ssize_t recvd = recv(client_socket, server_message, sizeof(server_message) - 1, 0);
if (recvd <= 0) { perror("No message received from server"); break; }
server_message[recvd] = '\0';
printf("Server's response: %s\n", server_message);
printf("Enter message sent to the server (type \\quit to exit): ");
// clean the buffer
memset(client_message, 0, sizeof(client_message));
memset(server_message, 0, sizeof(server_message));
if (fgets(client_message, sizeof(client_message), stdin) == NULL) {
// EOF or error reading from stdin, exit the loop
perror("Error reading from stdin");
break;
}
}
// Close the socket
close(client_socket);
return 0;
}
\end{lstlisting}
\item Quick overview of what the code does in your own words.
Same as the server code, we add hex to byte converting function to decrypt the key and initial vector.
The \texttt{stream\_encrypt} function initialize the context, the encryption operation, and create cipher text from plain text. After that, we send the cipher text to the server and receive the server's response. Finally, we free up the resources, returning the cipher text length to user.
In main function, we add additional encryption for the client message and send it to the server. We add our name to the front as before.
Here is the screenshot for the client output:
\begin{figure}[H]
\centering
\includegraphics[width=1\textwidth]{./images/client.png}
\caption{Client output}
\label{fig:client}
\end{figure}
\end{enumerate}
\newpage
\item[4.] Extra credits
We use wireshark to capture the packets between the server and client, and we found the following packets:
\begin{figure}[H]
\centering
\includegraphics[width=1\textwidth]{./images/wireshark_client.png}
\caption{Wireshark capture for client}
\label{fig:wireshark_client}
\end{figure}
This packets seems random but immediately after that, server respond with plain text indicating that the decryption succeeded.
The server response is expected as follows shows:
\begin{figure}[H]
\centering
\includegraphics[width=1\textwidth]{./images/wireshark_server.png}
\caption{Wireshark capture for server}
\label{fig:wireshark_server}
\end{figure}
\newpage
\end{enumerate}

BIN
latex/images/client.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

BIN
latex/images/server.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 KiB

View File

@@ -1 +1 @@
My password is EA514659DC556DEECFFA5FD061363642
My password is 0214BA9480AE8D21842B80FF5287418FD0465113070FDFF8263F0F1FCD6CF030

View File

@@ -34,15 +34,36 @@ void load_env(const std::string& path) {
}
}
// hex to byte helper function
int hex_to_bytes_upper(const char* hex, unsigned char* out, size_t out_size)
{
size_t i = 0;
while (hex[0] && hex[1]) {
if (i >= out_size) return -1;
unsigned char h = hex[0];
unsigned char l = hex[1];
int hi = (h <= '9') ? (h - '0') : (h - 'A' + 10);
int lo = (l <= '9') ? (l - '0') : (l - 'A' + 10);
// minimal sanity check
if (hi < 0 || hi > 15 || lo < 0 || lo > 15)
return -1;
out[i++] = (unsigned char)((hi << 4) | lo);
hex += 2;
}
return (int)i;
}
// Define the decryption function
int stream_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, unsigned char *iv, unsigned char *plaintext) {
/* Declare cipher context */
EVP_CIPHER_CTX *ctx;
int len, plaintext_len;
/* Create and initialise the context */
ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
ERR_print_errors_fp(stderr);
}
/* Initialise the decryption operation. */
if (EVP_DecryptInit_ex(ctx, EVP_aes_256_ctr(), NULL, key, iv) != 1){
@@ -62,7 +83,7 @@ int stream_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
return plaintext_len;
return len + plaintext_len;
}
int main(void){
@@ -74,11 +95,20 @@ int main(void){
const int server_port = std::atoi(std::getenv("SERVER_PORT"));
char client_message[2048];
char server_message[2048];
const char * custom_message="Server: Hello from server, ";
const char * custom_message="Server: Hello from server, message recieved!\n";
// decryption parameters
const char *inital_vector = std::getenv("INITIAL_VECTOR");
const char *secret_key = std::getenv("SECRET_KEY");
unsigned char key_bytes[32], iv_bytes[16];
int key_len = hex_to_bytes_upper(secret_key, key_bytes, sizeof(key_bytes));
int iv_len = hex_to_bytes_upper(inital_vector, iv_bytes, sizeof(iv_bytes));
if (key_len != 32 || iv_len != 16) {
fprintf(stderr, "Invalid key/IV size for AES-256\n");
return 1;
}
// debug
printf("Server starting at IP: %s, Port: %d\n", server_ip, server_port);
@@ -127,38 +157,40 @@ int main(void){
memset(client_message, 0, sizeof(client_message));
// Receive client's message
while (strcmp(client_message, "\\exit\n") != 0) {
while (1) {
// clean exising buffer
memset(client_message, 0, sizeof(client_message));
recv(client_socket, client_message, sizeof(client_message), 0);
ssize_t n = recv(client_socket, client_message, sizeof(client_message), 0);
if (n == 0) break;
if (n < 0) { perror("recv"); break; }
// decrypt client message
char plaintext[2048];
int plaintext_len = stream_decrypt((unsigned char*)client_message, strlen(client_message), (unsigned char*)secret_key, (unsigned char*)inital_vector, (unsigned char*)plaintext);
unsigned char plaintext[2048];
int plaintext_len = stream_decrypt((unsigned char*)client_message, (int)n, key_bytes, iv_bytes, plaintext);
if (plaintext_len < 0) {
fprintf(stderr, "decrypt failed\n");
break;
}
printf("Msg from client: %.*s", plaintext_len, plaintext);
if (plaintext_len >= (int)sizeof(plaintext)) plaintext_len = (int)sizeof(plaintext) - 1;
plaintext[plaintext_len] = '\0';
// assign client message to plaintext
memcpy(client_message, plaintext, plaintext_len);
if (strcmp((char*)plaintext, "\\exit\n") == 0) break;
printf("Msg from client: %s", plaintext);
// Respond to client
// prepare server message
memset(server_message, 0, sizeof(server_message));
;
memcpy(server_message, custom_message, strlen(custom_message));
// add my name in the back as response.
std::snprintf(server_message, sizeof(server_message), "%s%s", custom_message, strtok(client_message, ":"));
if (send(client_socket, server_message, sizeof(server_message), 0) == -1) {
perror("Failed to send message");
close(client_socket);
close(server_socket);
return 1;
size_t reply_len = strlen(server_message);
if (send(client_socket, server_message, reply_len, 0) == -1) {
perror("Send failed");
break;
}
printf("Response sent to client: %s\n", server_message);
printf("Response sent to client: %s\n", server_message);
}
// Close the socket
close(client_socket);