init
This commit is contained in:
5
.env
Normal file
5
.env
Normal file
@@ -0,0 +1,5 @@
|
||||
NET_SUBNET=172.30.0.0/24
|
||||
SERVER_IP=172.30.0.10
|
||||
CLIENT_IP=172.30.0.11
|
||||
SERVER_PORT=3030
|
||||
HMAC_KEY=FBEDF55EB2D01072E2AE0280FEA75F730473A32A57C0902A23CB55AC5DC0214EE6E34D735AA562ADF54CC55E73D126723D3CA5A65EEFB491C317A024DCA54813
|
||||
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# latex garbages
|
||||
*.aux
|
||||
*.fdb_latexmk
|
||||
*.fls
|
||||
*.log
|
||||
*.synctex.gz
|
||||
*.out
|
||||
|
||||
# vscode garbage
|
||||
.vscode
|
||||
BIN
CSE433S_HW3_net_hash_fcn-2.pdf
Normal file
BIN
CSE433S_HW3_net_hash_fcn-2.pdf
Normal file
Binary file not shown.
BIN
bin/client
Executable file
BIN
bin/client
Executable file
Binary file not shown.
BIN
bin/server
Executable file
BIN
bin/server
Executable file
Binary file not shown.
1
client-log.sh
Normal file
1
client-log.sh
Normal file
@@ -0,0 +1 @@
|
||||
docker logs hw3-client
|
||||
170
client.cpp
Normal file
170
client.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
#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()
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <cstdlib> // for atoi()
|
||||
#include <vector>
|
||||
|
||||
#include "helper.h"
|
||||
|
||||
// block cipher encryption function
|
||||
int block_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_cbc(), 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 ciphertext_len + 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: ";
|
||||
|
||||
// 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);
|
||||
printf("Message sent to server: %s, length: %d \n", byte_to_hex((unsigned char *)buffer, strlen(buffer)), (int)strlen(buffer));
|
||||
|
||||
ssize_t sent = send(client_socket, buffer, strlen(buffer), 0);
|
||||
if (sent <= 0)
|
||||
{
|
||||
perror("No message sent to server");
|
||||
break;
|
||||
}
|
||||
// send hmac of the message
|
||||
unsigned char hmac[20];
|
||||
memset(hmac, 0, sizeof(hmac));
|
||||
cal_hmac(hmac, buffer);
|
||||
printf("HMAC sent to server: %s\n", byte_to_hex(hmac, 20));
|
||||
|
||||
ssize_t sent_hmac = send(client_socket, hmac, strlen((char *)hmac), 0);
|
||||
if (sent_hmac <= 0) {
|
||||
perror("No HMAC 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;
|
||||
}
|
||||
30
compile.sh
Executable file
30
compile.sh
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
mkdir -p bin
|
||||
|
||||
# add libssl-dev if needed
|
||||
|
||||
# apt-get update && apt-get install -y --no-install-recommends libssl-dev
|
||||
|
||||
# Adjust flags as you like
|
||||
CXXFLAGS="-std=c++17 -O2 -Wall -Wextra -pedantic"
|
||||
OPENSSL_LIBS="-lssl -lcrypto"
|
||||
|
||||
g++ ${CXXFLAGS} server.cpp helper.cpp -o bin/server ${OPENSSL_LIBS}
|
||||
g++ ${CXXFLAGS} client.cpp helper.cpp -o bin/client ${OPENSSL_LIBS}
|
||||
|
||||
echo "Built:"
|
||||
file bin/server bin/client || true
|
||||
|
||||
chmod a+x bin/server bin/client
|
||||
|
||||
# stop the containers and free up network resources
|
||||
docker compose down --remove-orphans || true
|
||||
docker network rm net-hw3 >/dev/null 2>&1 || true
|
||||
|
||||
# start the containers
|
||||
docker compose up -d server
|
||||
docker compose run --rm -it client
|
||||
|
||||
echo "Done."
|
||||
38
docker-compose.yml
Normal file
38
docker-compose.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
services:
|
||||
server:
|
||||
image: debian:bookworm-slim
|
||||
container_name: hw3-server
|
||||
working_dir: /app
|
||||
env_file:
|
||||
- .env
|
||||
networks:
|
||||
net-hw3:
|
||||
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: hw3-client
|
||||
working_dir: /app
|
||||
env_file:
|
||||
- .env
|
||||
networks:
|
||||
net-hw3:
|
||||
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-hw3:
|
||||
name: net-hw3
|
||||
driver: bridge
|
||||
ipam:
|
||||
config:
|
||||
- subnet: ${NET_SUBNET}
|
||||
101
helper.cpp
Normal file
101
helper.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
// load environment variables from .env file
|
||||
#include <fstream>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
void load_env(char const* 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;
|
||||
}
|
||||
|
||||
char * byte_to_hex(unsigned char *bytes, size_t len)
|
||||
{
|
||||
char *hex = new char[len * 2 + 1];
|
||||
for (size_t i = 0; i < len; i++)
|
||||
{
|
||||
sprintf(hex + i * 2, "%02X", bytes[i]);
|
||||
}
|
||||
hex[len * 2] = '\0';
|
||||
return hex;
|
||||
}
|
||||
|
||||
void cal_hmac(unsigned char *mac, char *message)
|
||||
{
|
||||
|
||||
/* The secret key for hashing */
|
||||
const char *key_str = getenv("HMAC_KEY");
|
||||
if (key_str == NULL)
|
||||
{
|
||||
fprintf(stderr, "HMAC_KEY not set in environment\n");
|
||||
return;
|
||||
}
|
||||
unsigned char key_char[64];
|
||||
int key_len = hex_to_bytes_upper(key_str, key_char, sizeof(key_char));
|
||||
if (key_len != 64) {
|
||||
fprintf(stderr, "Invalid HMAC_KEY format\n");
|
||||
return;
|
||||
}
|
||||
const char *key = (const char *)key_char;
|
||||
// printf("HMAC key: %s\n", byte_to_hex((unsigned char *)key, key_len));
|
||||
|
||||
/* Change the length accordingly with your chosen hash engine.
|
||||
* Be careful of the length of string with the chosen hash engine. For
|
||||
example, SHA1 needed 20 characters. */
|
||||
unsigned int len = 20;
|
||||
/* Create and initialize the context */
|
||||
HMAC_CTX *ctx = HMAC_CTX_new();
|
||||
/* Initialize the HMAC operation. */
|
||||
HMAC_Init_ex(ctx, key, key_len, EVP_sha1(), NULL);
|
||||
/* Provide the message to HMAC, and start HMAC authentication. */
|
||||
HMAC_Update(ctx, (unsigned char *)message, strlen(message));
|
||||
/* HMAC_Final() writes the hashed values to md, which must have enough
|
||||
space for the hash function output. */
|
||||
HMAC_Final(ctx, mac, &len);
|
||||
/* Releases any associated resources and finally frees context variable
|
||||
*/
|
||||
HMAC_CTX_free(ctx);
|
||||
return;
|
||||
}
|
||||
9
helper.h
Normal file
9
helper.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef HELPER_H
|
||||
#define HELPER_H
|
||||
|
||||
void load_env(char const* path);
|
||||
int hex_to_bytes_upper(const char* hex, unsigned char* out, size_t out_size);
|
||||
char* byte_to_hex(unsigned char* bytes, size_t len);
|
||||
void cal_hmac(unsigned char* mac, char* message);
|
||||
|
||||
#endif
|
||||
BIN
latex/H3_CSE4303_ZheyuanWu.pdf
Normal file
BIN
latex/H3_CSE4303_ZheyuanWu.pdf
Normal file
Binary file not shown.
669
latex/H3_CSE4303_ZheyuanWu.tex
Normal file
669
latex/H3_CSE4303_ZheyuanWu.tex
Normal file
@@ -0,0 +1,669 @@
|
||||
\documentclass[11pt]{article}
|
||||
\usepackage{amsmath, amsfonts, amsthm}
|
||||
\usepackage{amssymb}
|
||||
\usepackage{fancyhdr,parskip}
|
||||
\usepackage{fullpage}
|
||||
\usepackage{mathrsfs}
|
||||
\usepackage{mathtools}
|
||||
|
||||
\usepackage{float}
|
||||
|
||||
% code listing
|
||||
\usepackage{listings}
|
||||
\usepackage{xcolor}
|
||||
\lstset{%
|
||||
language=c++,
|
||||
breaklines=true,
|
||||
commentstyle=\color{green}, % comment style
|
||||
escapeinside={\%*}{*)}, % if you want to add LaTeX within your code
|
||||
keywordstyle=\color{blue}, % keyword style
|
||||
stringstyle=\color{purple}, % string literal style
|
||||
}
|
||||
|
||||
%%
|
||||
%% Stuff above here is packages that will be used to compile your document.
|
||||
%% If you've used unusual LaTeX features, you may have to install extra packages by adding them to this list.
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
|
||||
\setlength{\headheight}{15.2pt}
|
||||
\setlength{\headsep}{20pt}
|
||||
\pagestyle{fancyplain}
|
||||
|
||||
%%
|
||||
%% Stuff above here is layout and formatting. If you've never used LaTeX before, you probably don't need to change any of it.
|
||||
%% Later, you can learn how it all works and adjust it to your liking, or write your own formatting code.
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%
|
||||
% These commands create theorem-like environments.
|
||||
\newtheorem{theorem}{Theorem}
|
||||
\newtheorem{lemma}[theorem]{Lemma}
|
||||
\newtheorem{corollary}[theorem]{Corollary}
|
||||
\newtheorem{prop}[theorem]{Proposition}
|
||||
\newtheorem{defn}[theorem]{Definition}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% This section contains some useful macros that will save you time typing.
|
||||
%%
|
||||
|
||||
% Using \displaystyle (or \ds) in a block of math has a number of effects, but most notably, it makes your fractions come out bigger.
|
||||
\newcommand{\ds}{\displaystyle}
|
||||
|
||||
% These lines are for displaying integrals; typing \dx will make the dx at the end of the integral look better.
|
||||
\newcommand{\is}{\hspace{2pt}}
|
||||
\newcommand{\dx}{\is dx}
|
||||
|
||||
% These commands produce the fancy Z (for the integers) and other letters conveniently.
|
||||
\newcommand{\Z}{\mathbb{Z}}
|
||||
\newcommand{\Q}{\mathbb{Q}}
|
||||
\newcommand{\R}{\mathbb{R}}
|
||||
\newcommand{\C}{\mathbb{C}}
|
||||
\newcommand{\F}{\mathbb{F}}
|
||||
\newcommand{\T}{\mathcal{T}}
|
||||
\newcommand{\B}{\mathcal{B}}
|
||||
|
||||
% for fancy empty set char
|
||||
\renewcommand{\emptyset}{\varnothing}
|
||||
|
||||
% customized commands for future assignements
|
||||
\newcommand{\imply}{\Rightarrow}
|
||||
\def\P{\mathscr{P}}
|
||||
\def\L{\mathscr{L}}
|
||||
\def\M{\mathscr{M}}
|
||||
\DeclarePairedDelimiterX{\inp}[2]{\langle}{\rangle}{#1, #2}
|
||||
|
||||
% url embedding
|
||||
\usepackage{hyperref}
|
||||
|
||||
\hypersetup{colorlinks=true,linkcolor=blue,urlcolor=blue}
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% This is the header. It will appear on every page, and it's a good place to put your name, the assignment title, and stuff like that.
|
||||
%% I usually leave the center header blank to avoid clutter.
|
||||
%%
|
||||
|
||||
\fancyhead[L]{\textbf{CSE4303 Homework 2}}
|
||||
\fancyhead[C]{509191}
|
||||
\fancyhead[R]{Zheyuan Wu}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
|
||||
\begin{document}
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% Actual math starts here!
|
||||
|
||||
|
||||
% 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/CSE4303H3}.}
|
||||
|
||||
\begin{enumerate}
|
||||
\item[1.] Description of experimental setup
|
||||
|
||||
We use the same setup as in the previous assignment.
|
||||
|
||||
Here is the docker compose and environment file used for this assignment.
|
||||
|
||||
We use \texttt{sha-1} as the hash function for HMAC, since it is simple and fast compared to other hash functions, and to test the code effectively, we remove the block cipher and stream cipher.
|
||||
|
||||
We moved the helper functions like reading environment variables and calculating HMAC to a separate file \texttt{helper.cpp} and \texttt{helper.h} to make the code cleaner.
|
||||
|
||||
\begin{lstlisting}[language=bash]
|
||||
services:
|
||||
server:
|
||||
image: debian:bookworm-slim
|
||||
container_name: hw3-server
|
||||
working_dir: /app
|
||||
env_file:
|
||||
- .env
|
||||
networks:
|
||||
net-hw3:
|
||||
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: hw3-client
|
||||
working_dir: /app
|
||||
env_file:
|
||||
- .env
|
||||
networks:
|
||||
net-hw3:
|
||||
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-hw3:
|
||||
name: net-hw3
|
||||
driver: bridge
|
||||
ipam:
|
||||
config:
|
||||
- subnet: ${NET_SUBNET}
|
||||
\end{lstlisting}
|
||||
|
||||
Here is the environment file used for this assignment.
|
||||
|
||||
\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
|
||||
HMAC_KEY=FBEDF55EB2D01072E2AE0280FEA75F730473A32A57C0902A23CB55AC5DC0214EE6E34D735AA562ADF54CC55E73D126723D3CA5A65EEFB491C317A024DCA54813
|
||||
\end{lstlisting}
|
||||
|
||||
Here is the \texttt{helper.cpp} we used for this assignment.
|
||||
|
||||
\begin{lstlisting}[language=c++]
|
||||
|
||||
// load environment variables from .env file
|
||||
#include <fstream>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
void load_env(char const* 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;
|
||||
}
|
||||
|
||||
char * byte_to_hex(unsigned char *bytes, size_t len)
|
||||
{
|
||||
char *hex = new char[len * 2 + 1];
|
||||
for (size_t i = 0; i < len; i++)
|
||||
{
|
||||
sprintf(hex + i * 2, "%02X", bytes[i]);
|
||||
}
|
||||
hex[len * 2] = '\0';
|
||||
return hex;
|
||||
}
|
||||
|
||||
void cal_hmac(unsigned char *mac, char *message)
|
||||
{
|
||||
|
||||
/* The secret key for hashing */
|
||||
const char *key_str = getenv("HMAC_KEY");
|
||||
if (key_str == NULL)
|
||||
{
|
||||
fprintf(stderr, "HMAC_KEY not set in environment\n");
|
||||
return;
|
||||
}
|
||||
unsigned char key_char[64];
|
||||
int key_len = hex_to_bytes_upper(key_str, key_char, sizeof(key_char));
|
||||
if (key_len != 64) {
|
||||
fprintf(stderr, "Invalid HMAC_KEY format\n");
|
||||
return;
|
||||
}
|
||||
const char *key = (const char *)key_char;
|
||||
// printf("HMAC key: %s\n", byte_to_hex((unsigned char *)key, key_len));
|
||||
|
||||
/* Change the length accordingly with your chosen hash engine.
|
||||
* Be careful of the length of string with the chosen hash engine. For
|
||||
example, SHA1 needed 20 characters. */
|
||||
unsigned int len = 20;
|
||||
/* Create and initialize the context */
|
||||
HMAC_CTX *ctx = HMAC_CTX_new();
|
||||
/* Initialize the HMAC operation. */
|
||||
HMAC_Init_ex(ctx, key, key_len, EVP_sha1(), NULL);
|
||||
/* Provide the message to HMAC, and start HMAC authentication. */
|
||||
HMAC_Update(ctx, (unsigned char *)message, strlen(message));
|
||||
/* HMAC_Final() writes the hashed values to md, which must have enough
|
||||
space for the hash function output. */
|
||||
HMAC_Final(ctx, mac, &len);
|
||||
/* Releases any associated resources and finally frees context variable
|
||||
*/
|
||||
HMAC_CTX_free(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
\end{lstlisting}
|
||||
|
||||
\newpage
|
||||
\item[2.] Server code
|
||||
|
||||
\begin{enumerate}
|
||||
\item Screenshot or well-formatted copy of code.
|
||||
|
||||
Here is the server code used for this assignment.
|
||||
|
||||
\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()
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <cstdlib> // for atoi()
|
||||
#include <vector>
|
||||
|
||||
// load environment variables from .env file, and additional helper functions
|
||||
#include "helper.h"
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("Server starting...\n");
|
||||
load_env(".env");
|
||||
|
||||
// Declare variables
|
||||
const char *server_ip = getenv("SERVER_IP");
|
||||
const int server_port = atoi(getenv("SERVER_PORT"));
|
||||
char client_message[2048];
|
||||
char server_message[2048];
|
||||
const char *custom_message_success = "Server: Hello from server, message authenticated!\n";
|
||||
const char *custom_message_failed = "Server: Hello from server, message failed authentication!\n";
|
||||
|
||||
// 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 = 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));
|
||||
|
||||
// store message history, for HMAC calculation
|
||||
// msg on odd is msg, and on even is corresponding HMAC
|
||||
vector<string> message_history;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
if (strcmp((char *)client_message, "\\exit\n") == 0)
|
||||
break;
|
||||
|
||||
printf("Msg from client: %s \n", byte_to_hex((unsigned char *)client_message, n));
|
||||
// store message history for HMAC calculation
|
||||
message_history.push_back(string((char *)client_message, n));
|
||||
|
||||
// only respond after receiving both msg and HMAC
|
||||
if (message_history.size() % 2 == 1)
|
||||
continue;
|
||||
|
||||
// check HMAC
|
||||
const string expected_hmac_str = message_history.back();
|
||||
unsigned char expected_hmac[20];
|
||||
memcpy(expected_hmac, expected_hmac_str.c_str(), 20);
|
||||
const string plaintext_str = message_history[message_history.size() - 2];
|
||||
char plaintext_cstr[2048];
|
||||
strcpy(plaintext_cstr, plaintext_str.c_str());
|
||||
printf("Plaintext: %s, string length: %d\n", byte_to_hex((unsigned char *)plaintext_cstr, strlen(plaintext_cstr)), strlen(plaintext_cstr));
|
||||
|
||||
unsigned char calculated_hmac[20];
|
||||
memset(calculated_hmac, 0, sizeof(calculated_hmac));
|
||||
cal_hmac(calculated_hmac, plaintext_cstr);
|
||||
|
||||
if (memcmp(expected_hmac, calculated_hmac, 20) != 0)
|
||||
{
|
||||
fprintf(stderr, "HMAC mismatch\n, expected: %s, calculated: %s\n", byte_to_hex(expected_hmac, 20), byte_to_hex(calculated_hmac, 20));
|
||||
memcpy(server_message, custom_message_failed, strlen(custom_message_failed));
|
||||
;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(server_message, custom_message_success, strlen(custom_message_success));
|
||||
;
|
||||
}
|
||||
// Respond to client
|
||||
// prepare server 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.
|
||||
|
||||
We let the server listen for incoming connection, and once the client sends both message and HMAC, the server will calculate the HMAC of the message and compare it with the received HMAC, if they match, the server will respond with a success message, otherwise, it will respond with a failure message.
|
||||
|
||||
\item Screenshots showing your server program during/after execution.
|
||||
|
||||
Here is the screenshot from the server side receiving the message from the client.
|
||||
\begin{figure}[H]
|
||||
\centering
|
||||
\includegraphics[width=0.9\textwidth]{./images/server.png}
|
||||
\end{figure}
|
||||
\end{enumerate}
|
||||
\newpage
|
||||
|
||||
\item[3.] Client code
|
||||
|
||||
\begin{enumerate}
|
||||
|
||||
\item Screenshot or well-formatted copy of code
|
||||
|
||||
|
||||
Here is the client code used for this assignment.
|
||||
|
||||
\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()
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <cstdlib> // for atoi()
|
||||
#include <vector>
|
||||
|
||||
#include "helper.h"
|
||||
|
||||
// block cipher encryption function
|
||||
int block_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_cbc(), 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 ciphertext_len + 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: ";
|
||||
|
||||
// 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);
|
||||
printf("Message sent to server: %s, length: %d \n", byte_to_hex((unsigned char *)buffer, strlen(buffer)), (int)strlen(buffer));
|
||||
|
||||
ssize_t sent = send(client_socket, buffer, strlen(buffer), 0);
|
||||
if (sent <= 0)
|
||||
{
|
||||
perror("No message sent to server");
|
||||
break;
|
||||
}
|
||||
// send hmac of the message
|
||||
unsigned char hmac[20];
|
||||
memset(hmac, 0, sizeof(hmac));
|
||||
cal_hmac(hmac, buffer);
|
||||
printf("HMAC sent to server: %s\n", byte_to_hex(hmac, 20));
|
||||
|
||||
ssize_t sent_hmac = send(client_socket, hmac, strlen((char *)hmac), 0);
|
||||
if (sent_hmac <= 0) {
|
||||
perror("No HMAC 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.
|
||||
|
||||
The same as basic protocol in the assignment 0, we send two message for each user input, one is the plaintext message, and the other is the HMAC of the message.
|
||||
|
||||
Then we wait for server response, if the HMAC is correct, the server will respond with a success message, otherwise, it will respond with a failure message.
|
||||
|
||||
\item Screenshots showing your client program during/after execution
|
||||
|
||||
Here is the screenshot from the client side sending message to the server and server response with plain text.
|
||||
\begin{figure}[H]
|
||||
\centering
|
||||
\includegraphics[width=0.9\textwidth]{./images/client.png}
|
||||
\end{figure}
|
||||
|
||||
\end{enumerate}
|
||||
\newpage
|
||||
|
||||
|
||||
\newpage
|
||||
|
||||
\end{enumerate}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% Actual math ends here. Don't put any content below the \end{document} line.
|
||||
%%
|
||||
|
||||
\end{document}
|
||||
BIN
latex/images/client.png
Normal file
BIN
latex/images/client.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 215 KiB |
BIN
latex/images/server.png
Normal file
BIN
latex/images/server.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 168 KiB |
1
message.txt
Normal file
1
message.txt
Normal file
@@ -0,0 +1 @@
|
||||
My password is 0214BA9480AE8D21842B80FF5287418FD0465113070FDFF8263F0F1FCD6CF030
|
||||
1
server-log.sh
Normal file
1
server-log.sh
Normal file
@@ -0,0 +1 @@
|
||||
docker logs hw3-server --tail=50
|
||||
159
server.cpp
Normal file
159
server.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
#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()
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <cstdlib> // for atoi()
|
||||
#include <vector>
|
||||
|
||||
// load environment variables from .env file, and additional helper functions
|
||||
#include "helper.h"
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("Server starting...\n");
|
||||
load_env(".env");
|
||||
|
||||
// Declare variables
|
||||
const char *server_ip = getenv("SERVER_IP");
|
||||
const int server_port = atoi(getenv("SERVER_PORT"));
|
||||
char client_message[2048];
|
||||
char server_message[2048];
|
||||
const char *custom_message_success = "Server: Hello from server, message authenticated!\n";
|
||||
const char *custom_message_failed = "Server: Hello from server, message failed authentication!\n";
|
||||
|
||||
// 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 = 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));
|
||||
|
||||
// store message history, for HMAC calculation
|
||||
// msg on odd is msg, and on even is corresponding HMAC
|
||||
vector<string> message_history;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
if (strcmp((char *)client_message, "\\exit\n") == 0)
|
||||
break;
|
||||
|
||||
printf("Msg from client: %s \n", byte_to_hex((unsigned char *)client_message, n));
|
||||
// store message history for HMAC calculation
|
||||
message_history.push_back(string((char *)client_message, n));
|
||||
|
||||
// only respond after receiving both msg and HMAC
|
||||
if (message_history.size() % 2 == 1)
|
||||
continue;
|
||||
|
||||
// check HMAC
|
||||
const string expected_hmac_str = message_history.back();
|
||||
unsigned char expected_hmac[20];
|
||||
memcpy(expected_hmac, expected_hmac_str.c_str(), 20);
|
||||
const string plaintext_str = message_history[message_history.size() - 2];
|
||||
char plaintext_cstr[2048];
|
||||
strcpy(plaintext_cstr, plaintext_str.c_str());
|
||||
printf("Plaintext: %s, string length: %d\n", byte_to_hex((unsigned char *)plaintext_cstr, strlen(plaintext_cstr)), strlen(plaintext_cstr));
|
||||
|
||||
unsigned char calculated_hmac[20];
|
||||
memset(calculated_hmac, 0, sizeof(calculated_hmac));
|
||||
cal_hmac(calculated_hmac, plaintext_cstr);
|
||||
|
||||
if (memcmp(expected_hmac, calculated_hmac, 20) != 0)
|
||||
{
|
||||
fprintf(stderr, "HMAC mismatch\n, expected: %s, calculated: %s\n", byte_to_hex(expected_hmac, 20), byte_to_hex(calculated_hmac, 20));
|
||||
memcpy(server_message, custom_message_failed, strlen(custom_message_failed));
|
||||
;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(server_message, custom_message_success, strlen(custom_message_success));
|
||||
;
|
||||
}
|
||||
// Respond to client
|
||||
// prepare server 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;
|
||||
}
|
||||
Reference in New Issue
Block a user