Coded By Taylor Christian Newsome https://www.linkedin.com/in/clumsy/
Today I will release a secure peer-to-peer (P2P) chat application named CryptoClient. This application, coded in C, incorporates robust encryption mechanisms to ensure that communication remains confidential and secure. Today, we delve into how this application works, its core features, and the security protocols it employs.
Core Functionality
CryptoClient is designed as a console-based chat system that allows two users to communicate over a network. It uses the standard internet protocols UDP/IP for sending and receiving messages. The application is built using several complex functions that handle encryption, communication, and file transfer capabilities.
Key Features and Implementation
Encryption Techniques:
AES Encryption: The app uses AES (Advanced Encryption Standard) for encrypting messages. AES is a symmetric key encryption algorithm known for its speed and security. The implementation pads messages to ensure they fit the block size and uses CBC (Cipher Block Chaining) mode for enhanced security.
XOR Encryption: Before applying AES, the data is first encrypted using a simple XOR algorithm with a user-provided key. This dual-layer encryption adds an extra level of security.
Message and File Transfer:
Users can send and receive messages or files. Special commands like “/send” for sending files and “/leave” to end the chat session are implemented for ease of use.
Files are encrypted before transmission, and any attempts to send files named with potentially dangerous paths (e.g., starting with “.” or “/”) are blocked to prevent directory traversal attacks.
Dynamic Interaction:
The system uses sockets for network communication. One instance of the application will bind to a socket to receive messages (CryptoClient_recvMSG), while another sends messages (CryptoClient_sendMSG).
The client setup involves entering the IP address and port number of the other user, along with encryption keys for both XOR and AES.
Security Aspects
The application’s security is bolstered by its use of AES and XOR encryption. AES is implemented with OpenSSL, a robust and widely used library. Additionally, user interaction does not display encryption keys, reducing the risk of shoulder surfing.
Random initialization vectors (IVs) for AES and reversible transformations for the XOR key (depending on the mode) ensure that each message’s encryption pattern remains unique, thereby mitigating some common cryptographic attacks like pattern analysis.
Usage Scenario
Upon launching CryptoClient, users are prompted to enter the necessary communication details and encryption keys. The system supports concurrent send/receive operations using Unix fork(), which splits the process into a parent and child. The child process handles incoming messages, and the parent manages outgoing messages.
This architecture allows users to continuously send and receive encrypted data without interruptions, mimicking real-time conversation.
Conclusion
CryptoClient represents a robust approach to secure communications in P2P environments. By combining standard encryption algorithms with a straightforward user interface, it offers a secure way for users to chat and share files over the network. As privacy concerns continue to grow, tools like CryptoClient provide essential capabilities for secure, private communication.
This detailed examination of CryptoClient shows how traditional and custom encryption techniques can be integrated into practical applications that prioritize user security and data privacy.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <openssl/aes.h>
#include <openssl/rand.h>
#define MAX_BUF_SIZE 1073741824
#define AES_BLOCK_SIZE 16
typedef struct CryptoClient {
char IP[50];
int PORT;
char EncryptKeyXOR[100];
char EncryptKeyAES[100];
// Functions
char* pad(char *s);
char* unpad(char *s);
char* encryptAES(char *raw);
char* decryptAES(char *enc);
char* XOR(char *securekey, char *data, int mode);
char* encryptMSG(char *data);
char* decryptMSG(char *data);
void sendMSG();
void sendFILE(char *file_);
char* randStr(int length);
void recvMSG();
} CryptoClient;
char* CryptoClient_pad(CryptoClient *client, char *s) {
int BS = AES_BLOCK_SIZE;
int padLength = BS - strlen(s) % BS;
char *padding = (char *)malloc(padLength + 1);
memset(padding, (char)padLength, padLength);
padding[padLength] = '\0';
strcat(s, padding);
return s;
}
char* CryptoClient_unpad(CryptoClient *client, char *s) {
int length = strlen(s);
int padValue = (int)s[length - 1];
s[length - padValue] = '\0';
return s;
}
char* CryptoClient_encryptAES(CryptoClient *client, char *raw) {
raw = CryptoClient_pad(client, raw);
unsigned char iv[AES_BLOCK_SIZE];
RAND_bytes(iv, AES_BLOCK_SIZE);
AES_KEY aesKey;
AES_set_encrypt_key(client->EncryptKeyAES, AES_BLOCK_SIZE * 8, &aesKey);
int rawLength = strlen(raw);
int encryptedLength = ((rawLength + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
char *encrypted = (char *)malloc(encryptedLength + 1);
memset(encrypted, '\0', encryptedLength + 1);
AES_cbc_encrypt((unsigned char *)raw, (unsigned char *)encrypted, rawLength, &aesKey, iv, AES_ENCRYPT);
char *result = (char *)malloc((AES_BLOCK_SIZE + encryptedLength) * sizeof(char));
memcpy(result, iv, AES_BLOCK_SIZE);
memcpy(result + AES_BLOCK_SIZE, encrypted, encryptedLength);
return result;
}
char* CryptoClient_decryptAES(CryptoClient *client, char *enc) {
unsigned char iv[AES_BLOCK_SIZE];
memcpy(iv, enc, AES_BLOCK_SIZE);
AES_KEY aesKey;
AES_set_decrypt_key(client->EncryptKeyAES, AES_BLOCK_SIZE * 8, &aesKey);
int encryptedLength = strlen(enc + AES_BLOCK_SIZE);
char *decrypted = (char *)malloc(encryptedLength + 1);
memset(decrypted, '\0', encryptedLength + 1);
AES_cbc_encrypt((unsigned char *)(enc + AES_BLOCK_SIZE), (unsigned char *)decrypted, encryptedLength, &aesKey, iv, AES_DECRYPT);
return CryptoClient_unpad(client, decrypted);
}
char* CryptoClient_XOR(CryptoClient *client, char *securekey, char *data, int mode) {
int keyLength = strlen(securekey);
if (mode == 1) {
int i, j;
char temp;
for (i = 0, j = keyLength - 1; i < j; ++i, --j) {
temp = securekey[i];
securekey[i] = securekey[j];
securekey[j] = temp;
}
}
int dataLength = strlen(data);
char *result = (char *)malloc(dataLength + 1);
strcpy(result, data);
int i, j;
for (i = 0; i < keyLength; ++i) {
for (j = 0; j < dataLength; ++j) {
result[j] = result[j] ^ securekey[i];
}
}
return result;
}
char* CryptoClient_encryptMSG(CryptoClient *client, char *data) {
char *encryptedXOR = CryptoClient_XOR(client, client->EncryptKeyXOR, data, 0);
char *encryptedAES = CryptoClient_encryptAES(client, encryptedXOR);
return encryptedAES;
}
char* CryptoClient_decryptMSG(CryptoClient *client, char *data) {
char *decryptedAES = CryptoClient_decryptAES(client, data);
char *decryptedXOR = CryptoClient_XOR(client, client->EncryptKeyXOR, decryptedAES, 1);
return decryptedXOR;
}
void CryptoClient_sendMSG(CryptoClient *client) {
int clientsock;
struct sockaddr_in serverAddr;
if ((clientsock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
memset(&serverAddr, '\0', sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(client->PORT);
if (inet_pton(AF_INET, client->IP, &serverAddr.sin_addr) <= 0) {
perror("Invalid address/ Address not supported");
exit(EXIT_FAILURE);
}
printf("\nYou are now talking to '%s'\n", client->IP);
char message[MAX_BUF_SIZE];
while (1) {
printf("> ");
fgets(message, MAX_BUF_SIZE, stdin);
message[strcspn(message, "\n")] = '\0';
if (strncmp(message, "/send", 5) == 0) {
CryptoClient_sendFILE(client, message + 6);
continue;
}
if (strcmp(message, "/leave") == 0) {
char *encrypted = CryptoClient_encryptMSG(client, "\x03");
sendto(clientsock, encrypted, strlen(encrypted), 0, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
close(clientsock);
exit(0);
}
if (strncmp(message, "/msg", 4) == 0) {
strcpy(client->IP, message + 5);
printf("[CLIENT] You are now talking to '%s'\n", client->IP);
continue;
}
char *encrypted = CryptoClient_encryptMSG(client, "\x01");
strcat(encrypted, message);
sendto(clientsock, encrypted, strlen(encrypted), 0, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
}
}
void CryptoClient_sendFILE(CryptoClient *client, char *file_) {
if (file_[0] == '.' || file_[0] == '/') {
printf("[CLIENT] For security and safety reasons, filenames starting with '.' or '/' will not be sent. Aborting.\n");
} else {
int clientsock;
struct sockaddr_in serverAddr;
if ((clientsock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
memset(&serverAddr, '\0', sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(client->PORT);
if (inet_pton(AF_INET, client->IP, &serverAddr.sin_addr) <= 0) {
perror("Invalid address/ Address not supported");
exit(EXIT_FAILURE);
}
char data[MAX_BUF_SIZE];
strcpy(data, "\x02");
strcat(data, file_);
strcat(data, "\xFF");
FILE *file = fopen(file_, "rb");
if (file == NULL) {
printf("[CLIENT] Error opening file.\n");
exit(EXIT_FAILURE);
}
fseek(file, 0, SEEK_END);
long fileSize = ftell(file);
fseek(file, 0, SEEK_SET);
fread(data + strlen(data), 1, fileSize, file);
fclose(file);
char *encrypted = CryptoClient_encryptMSG(client, data);
sendto(clientsock, encrypted, strlen(encrypted), 0, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
close(clientsock);
printf("[CLIENT] File Sent!\n");
}
}
char* CryptoClient_randStr(CryptoClient *client, int length) {
char *result = (char *)malloc((length + 1) * sizeof(char));
int i;
for (i = 0; i < length; ++i) {
result[i] = 'a' + (rand() % 26);
}
result[length] = '\0';
return result;
}
void CryptoClient_recvMSG(CryptoClient *client) {
int serversock;
struct sockaddr_in serverAddr, clientAddr;
socklen_t clientAddrLen = sizeof(clientAddr);
if ((serversock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
memset(&serverAddr, '\0', sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(client->PORT);
if (bind(serversock, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) {
perror("Binding failed");
exit(EXIT_FAILURE);
}
char data[MAX_BUF_SIZE];
while (1) {
int bytesReceived = recvfrom(serversock, data, MAX_BUF_SIZE, 0, (struct sockaddr *)&clientAddr, &clientAddrLen);
data[bytesReceived] = '\0';
char *decrypted = CryptoClient_decryptMSG(client, data);
if (decrypted[0] == '\x02') {
char filename[MAX_BUF_SIZE];
int i = 1;
while (decrypted[i] != '\xFF') {
filename[i - 1] = decrypted[i];
i++;
}
filename[i - 1] = '\0';
i++;
if (filename[0] == '.' || filename[0] == '/') {
printf("[!!!ALERT!!!] %s has attempted to overwrite your %s\n", inet_ntoa(clientAddr.sin_addr), filename);
} else {
printf("[CLIENT] %s has sent %s\n", inet_ntoa(clientAddr.sin_addr), filename);
printf("[CLIENT] Downloading...\n");
FILE *file = fopen(filename, "wb");
fwrite(&decrypted[i], 1, bytesReceived - i, file);
fclose(file);
printf("[CLIENT] Saved.\n");
}
} else if (decrypted[0] == '\x01') {
printf("[%s] > | %s\n", inet_ntoa(clientAddr.sin_addr), decrypted + 1);
} else if (decrypted[0] == '\x03') {
printf("[CLIENT] %s has left.\n", inet_ntoa(clientAddr.sin_addr));
exit(0);
}
}
}
int main() {
CryptoClient client;
printf("Welcome to CryptoChat, a secure P2P chat client coded by Taylor Christian Newsome\n");
printf("If you don't know what you're doing, Google mudkipz\n");
printf("Please enter the IP address you wish to chat with: ");
fgets(client.IP, sizeof(client.IP), stdin);
client.IP[strcspn(client.IP, "\n")] = '\0';
printf("Enter the port for communication: ");
scanf("%d", &client.PORT);
getchar(); // Consume the newline character
printf("\nNow enter the keys for the different encryption methods, make sure they are different.\n");
printf("Please note they will not be printed for your security.\n\n");
printf("Enter desired key for XOR encryption: ");
fgets(client.EncryptKeyXOR, sizeof(client.EncryptKeyXOR), stdin);
client.EncryptKeyXOR[strcspn(client.EncryptKeyXOR, "\n")] = '\0';
printf("Enter a secure passphrase for AES: ");
fgets(client.EncryptKeyAES, sizeof(client.EncryptKeyAES), stdin);
client.EncryptKeyAES[strcspn(client.EncryptKeyAES, "\n")] = '\0';
printf("\nPress enter when both clients are ready.\n");
getchar();
pid_t pid = fork();
if (pid == -1) {
perror("Fork failed");
exit(EXIT_FAILURE);
}
if (pid == 0) {
CryptoClient_recvMSG(&client);
} else {
CryptoClient_sendMSG(&client);
}
return 0;
}