commit 297f55bc60c2371050bf0cdf6d43615c60540704
Author: night0721 <[email protected]>
Date: Tue, 30 Apr 2024 17:17:17 +0100
Initial commit
Diffstat:
9 files changed, 801 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,4 @@
+zsm
+zsmc
+*.o
+*.tar.gz
diff --git a/Makefile b/Makefile
@@ -0,0 +1,52 @@
+.POSIX:
+.SUFFIXES:
+
+CC = cc
+VERSION = 1.0
+SERVER = zsm
+CLIENT = zsmc
+MANPAGE = $(TARGET).1
+PREFIX ?= /usr/local
+BINDIR = $(PREFIX)/bin
+MANDIR = $(PREFIX)/share/man/man1
+
+# Flags
+LDFLAGS = $(shell pkg-config --libs libsodium libnotify ncurses)
+CFLAGS = -O3 -mtune=native -march=native -pipe -g -std=c99 -Wno-pointer-sign -Wpedantic -Wall -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 $(shell pkg-config --cflags libsodium libnotify ncurses) -lpthread
+
+SERVERSRC = src/server/*.c
+CLIENTSRC = src/client/*.c
+LIBSRC = lib/*.c
+INCLUDE = -Iinclude/
+
+$(SERVER): $(SERVERSRC) $(LIBSRC)
+ $(CC) $(SERVERSRC) $(LIBSRC) $(INCLUDE) -o $@ $(CFLAGS) $(LDFLAGS)
+
+$(CLIENT): $(CLIENTSRC) $(LIBSRC)
+ $(CC) $(CLIENTSRC) $(LIBSRC) $(INCLUDE) -o $@ $(CFLAGS) $(LDFLAGS)
+
+dist:
+ mkdir -p $(TARGET)-$(VERSION)
+ cp -R README.md $(MANPAGE) $(TARGET) $(TARGET)-$(VERSION)
+ tar -cf $(TARGET)-$(VERSION).tar $(TARGET)-$(VERSION)
+ gzip $(TARGET)-$(VERSION).tar
+ rm -rf $(TARGET)-$(VERSION)
+
+install: $(TARGET)
+ mkdir -p $(DESTDIR)$(BINDIR)
+ mkdir -p $(DESTDIR)$(MANDIR)
+ cp -p $(TARGET) $(DESTDIR)$(BINDIR)/$(TARGET)
+ chmod 755 $(DESTDIR)$(BINDIR)/$(TARGET)
+ cp -p $(MANPAGE) $(DESTDIR)$(MANDIR)/$(MANPAGE)
+ chmod 644 $(DESTDIR)$(MANDIR)/$(MANPAGE)
+
+uninstall:
+ $(RM) $(DESTDIR)$(BINDIR)/$(TARGET)
+ $(RM) $(DESTDIR)$(MANDIR)/$(MANPAGE)
+
+clean:
+ $(RM) $(TARGET)
+
+all: $(TARGET)
+
+.PHONY: all dist install uninstall clean
diff --git a/README.md b/README.md
@@ -0,0 +1,22 @@
+# zsm
+
+Zen Secure Messaging(zsm) is a secure messaging protocol specifically for Linux(Unix-based) systems.
+
+## Dependencies
+- sqlite
+- libsodium
+- libnotify
+
+## Building
+
+> You will need to run these with elevated privilages.
+> You will need `*-dev` packages to build both server and client.
+
+```sh
+git clone https://github.com/night0721/zsm
+make
+make install
+```
+
+## License
+This project is licensed under the GNU Public License v3.0. See [LICENSE](https://github.com/night0721/zsm/blob/master/LICENSE) for more information.
diff --git a/include/notification.h b/include/notification.h
@@ -0,0 +1,8 @@
+#ifndef NOTIFICATION_H
+#define NOTIFICATION_H
+
+#include <libnotify/notify.h>
+
+void send_notification(const char *content);
+
+#endif
diff --git a/include/packet.h b/include/packet.h
@@ -0,0 +1,69 @@
+#ifndef PACKET_H
+#define PACKET_H
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sodium.h>
+
+#define DEBUG 1
+#define DOMAIN "127.0.0.1"
+#define PORT 20247
+#define MAX_CONNECTION 5
+#define MAX_MESSAGE_LENGTH 8192
+#define ERROR_LENGTH 26
+
+#define ZSM_TYP_KEY 0x1
+#define ZSM_TYP_SEND_MESSAGE 0x2
+#define ZSM_TYP_UPDATE_MESSAGE 0x3
+#define ZSM_TYP_DELETE_MESSAGE 0x4
+#define ZSM_TYP_PRESENCE 0x5
+#define ZSM_TYP_TYPING 0x6
+#define ZSM_TYP_ERROR 0x7 /* Error message */
+#define ZSM_TYP_B 0x8
+
+#define ZSM_STA_SUCCESS 0x1
+#define ZSM_STA_INVALID_TYPE 0x2
+#define ZSM_STA_INVALID_LENGTH 0x3
+#define ZSM_STA_TOO_LONG 0x4
+#define ZSM_STA_READING_SOCKET 0x5
+#define ZSM_STA_WRITING_SOCKET 0x6
+#define ZSM_STA_UNKNOWN_USER 0x7
+#define ZSM_STA_MEMORY_ALLOCATION 0x8
+#define ZSM_STA_WRONG_KEY_LENGTH 0x9
+
+#define PUBLIC_KEY_SIZE crypto_kx_PUBLICKEYBYTES
+#define PRIVATE_KEY_SIZE crypto_kx_SECRETKEYBYTES
+#define SHARED_KEY_SIZE crypto_kx_SESSIONKEYBYTES
+#define NONCE_SIZE crypto_aead_xchacha20poly1305_ietf_NPUBBYTES
+#define ADDITIONAL_SIZE crypto_aead_xchacha20poly1305_ietf_ABYTES
+
+typedef struct message {
+ uint8_t option;
+ uint8_t type;
+ unsigned long long length;
+ unsigned char *data;
+} message;
+
+/* Utilities functions */
+void error(int fatal, const char *fmt, ...);
+void *memalloc(size_t size);
+void *estrdup(void *str);
+unsigned char *get_public_key(int sockfd);
+int send_public_key(int sockfd, unsigned char *pk);
+void print_packet(message *msg);
+int recv_packet(message *msg, int fd);
+message *create_error_packet(int code);
+message *create_packet(uint8_t option, uint8_t type, uint32_t length, char *data);
+int send_packet(message *msg, int fd);
+void free_packet(message *msg);
+
+#endif
diff --git a/lib/notification.c b/lib/notification.c
@@ -0,0 +1,8 @@
+#include "notification.h"
+
+void send_notification(const char *content)
+{
+ NotifyNotification *noti = notify_notification_new("Client", content, "dialog-information");
+ notify_notification_show(noti, NULL);
+ g_object_unref(G_OBJECT(noti));
+}
diff --git a/lib/packet.c b/lib/packet.c
@@ -0,0 +1,273 @@
+#include "packet.h"
+
+/*
+ * msg is the error message to print to stderr
+ * will include error message from function if errno isn't 0
+ * end program is fatal is 1
+ */
+void error(int fatal, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+
+ /* to preserve errno */
+ int errsv = errno;
+
+ /* Determine the length of the formatted error message */
+ va_list args_copy;
+ va_copy(args_copy, args);
+ size_t error_len = vsnprintf(NULL, 0, fmt, args_copy);
+ va_end(args_copy);
+
+ /* 7 for [zsm], space and null */
+ char errorstr[error_len + 1];
+ vsnprintf(errorstr, error_len + 1, fmt, args);
+ fprintf(stderr, "[zsm] ");
+
+ if (errsv != 0) {
+ perror(errorstr);
+ errno = 0;
+ } else {
+ fprintf(stderr, "%s\n", errorstr);
+ }
+
+ va_end(args);
+ if (fatal) exit(1);
+}
+
+void *memalloc(size_t size)
+{
+ void *ptr = malloc(size);
+ if (!ptr) {
+ error(0, "Error allocating memory");
+ return NULL;
+ }
+ return ptr;
+}
+
+void *estrdup(void *str)
+{
+ void *modstr = strdup(str);
+ if (modstr == NULL) {
+ error(0, "Error allocating memory");
+ return NULL;
+ }
+ return modstr;
+}
+
+uint8_t *get_public_key(int sockfd)
+{
+ message keyex_msg;
+ if (recv_packet(&keyex_msg, sockfd) != ZSM_STA_SUCCESS) {
+ /* We can't do anything if key exchange already failed */
+ close(sockfd);
+ return NULL;
+ } else {
+ int status = 0;
+ /* Check to see if the content is actually a key */
+ if (keyex_msg.type != ZSM_TYP_KEY) {
+ status = ZSM_STA_INVALID_TYPE;
+ }
+ if (keyex_msg.length != PUBLIC_KEY_SIZE) {
+ status = ZSM_STA_WRONG_KEY_LENGTH;
+ }
+ if (status != 0) {
+ free(keyex_msg.data);
+ message *error_msg = create_error_packet(status);
+ send_packet(error_msg, sockfd);
+ free_packet(error_msg);
+ close(sockfd);
+ return NULL;
+ }
+ }
+ /* Obtain public key from packet */
+ uint8_t *pk = memalloc(PUBLIC_KEY_SIZE * sizeof(char));
+ memcpy(pk, keyex_msg.data, PUBLIC_KEY_SIZE);
+ if (pk == NULL) {
+ free(keyex_msg.data);
+ /* Fatal, we couldn't complete key exchange */
+ close(sockfd);
+ return NULL;
+ }
+ free(keyex_msg.data);
+ return pk;
+}
+
+int send_public_key(int sockfd, uint8_t *pk)
+{
+ /* send_packet requires heap allocated buffer */
+ uint8_t *pk_dup = memalloc(PUBLIC_KEY_SIZE * sizeof(char));
+ memcpy(pk_dup, pk, PUBLIC_KEY_SIZE);
+ if (pk_dup == NULL) {
+ close(sockfd);
+ return -1;
+ }
+
+ /* Sending our public key to client */
+ /* option???? */
+ message *keyex = create_packet(1, ZSM_TYP_KEY, PUBLIC_KEY_SIZE, pk_dup);
+ send_packet(keyex, sockfd);
+ free_packet(keyex);
+ return 0;
+}
+
+void print_packet(message *msg)
+{
+ printf("Option: %d\n", msg->option);
+ printf("Type: %d\n", msg->type);
+ printf("Length: %lld\n", msg->length);
+ printf("Data: %s\n\n", msg->data);
+}
+
+/*
+ * Requires manually free message data
+ */
+int recv_packet(message *msg, int fd)
+{
+ int status = ZSM_STA_SUCCESS;
+
+ /* Read the message components */
+ if (recv(fd, &msg->option, sizeof(msg->option), 0) < 0 ||
+ recv(fd, &msg->type, sizeof(msg->type), 0) < 0 ||
+ recv(fd, &msg->length, sizeof(msg->length), 0) < 0) {
+ status = ZSM_STA_READING_SOCKET;
+ error(0, "Error reading from socket");
+ }
+ #if DEBUG == 1
+ printf("==========PACKET RECEIVED==========\n");
+ #endif
+ #if DEBUG == 1
+ printf("Option: %d\n", msg->option);
+ #endif
+
+ if (msg->type > 0xFF || msg->type < 0x0) {
+ status = ZSM_STA_INVALID_TYPE;
+ error(0, "Invalid message type");
+ goto failure;
+ }
+ #if DEBUG == 1
+ printf("Type: %d\n", msg->type);
+ #endif
+
+ /* Convert message length from network byte order to host byte order */
+ if (msg->length > MAX_MESSAGE_LENGTH) {
+ status = ZSM_STA_TOO_LONG;
+ error(0, "Message too long: %lld", msg->length);
+ goto failure;
+ }
+ #if DEBUG == 1
+ printf("Length: %lld\n", msg->length);
+ #endif
+
+ // Allocate memory for message data
+ msg->data = memalloc((msg->length + 1) * sizeof(char));
+ if (msg->data == NULL) {
+ status = ZSM_STA_MEMORY_ALLOCATION;
+ goto failure;
+ }
+
+ /* Read message data from the socket */
+ size_t bytes_read = 0;
+ if ((bytes_read = recv(fd, msg->data, msg->length, 0)) < 0) {
+ status = ZSM_STA_READING_SOCKET;
+ error(0, "Error reading from socket");
+ free(msg->data);
+ goto failure;
+ }
+ if (bytes_read != msg->length) {
+ status = ZSM_STA_INVALID_LENGTH;
+ error(0, "Invalid message length: bytes_read=%ld != msg->length=%lld", bytes_read, msg->length);
+ free(msg->data);
+ goto failure;
+ }
+ msg->data[msg->length] = '\0';
+
+ #if DEBUG == 1
+ printf("Data: %s\n\n", msg->data);
+ #endif
+
+ return status;
+failure:;
+ message *error_msg = create_error_packet(status);
+ if (send_packet(error_msg, fd) != ZSM_STA_SUCCESS) {
+ /* Resend it? */
+ error(0, "Failed to send error packet to peer. Error status => %d", status);
+ }
+ free_packet(error_msg);
+ return status;
+}
+
+message *create_error_packet(int code)
+{
+ char *err = memalloc(ERROR_LENGTH * sizeof(char));
+ switch (code) {
+ case ZSM_STA_INVALID_TYPE:
+ strcpy(err, "Invalid message type ");
+ break;
+ case ZSM_STA_INVALID_LENGTH:
+ strcpy(err, "Invalid message length ");
+ break;
+ case ZSM_STA_TOO_LONG:
+ strcpy(err, "Message too long ");
+ break;
+ case ZSM_STA_READING_SOCKET:
+ strcpy(err, "Error reading from socket");
+ break;
+ case ZSM_STA_WRITING_SOCKET:
+ strcpy(err, "Error writing to socket ");
+ break;
+ case ZSM_STA_UNKNOWN_USER:
+ strcpy(err, "Unknwon user ");
+ break;
+ case ZSM_STA_WRONG_KEY_LENGTH:
+ strcpy(err, "Wrong public key length ");
+ break;
+ }
+ return create_packet(1, ZSM_TYP_ERROR, ERROR_LENGTH, err);
+}
+
+/*
+ * Requires heap allocated msg data
+ */
+message *create_packet(uint8_t option, uint8_t type, uint32_t length, char *data)
+{
+ message *msg = memalloc(sizeof(message));
+ msg->option = option;
+ msg->type = type;
+ msg->length = length;
+ msg->data = data;
+ return msg;
+}
+
+/*
+ * Requires heap allocated msg data
+ */
+int send_packet(message *msg, int fd)
+{
+ int status = ZSM_STA_SUCCESS;
+ uint32_t length = msg->length;
+ // Send the message back to the client
+ if (send(fd, &msg->option, sizeof(msg->option), 0) <= 0 ||
+ send(fd, &msg->type, sizeof(msg->type), 0) <= 0 ||
+ send(fd, &msg->length, sizeof(msg->length), 0) <= 0 ||
+ send(fd, msg->data, length, 0) <= 0) {
+ status = ZSM_STA_WRITING_SOCKET;
+ error(0, "Error writing to socket");
+ //free(msg->data);
+ close(fd); // Close the socket and continue accepting connections
+ }
+ #if DEBUG == 1
+ printf("==========PACKET SENT==========\n");
+ print_packet(msg);
+ #endif
+ return status;
+}
+
+void free_packet(message *msg)
+{
+ if (msg->type != 0x10) {
+ /* temp solution, dont use stack allocated msg to send to client */
+ free(msg->data);
+ }
+ free(msg);
+}
diff --git a/src/client/client.c b/src/client/client.c
@@ -0,0 +1,147 @@
+#include "packet.h"
+#include <pthread.h>
+
+uint8_t shared_key[SHARED_KEY_SIZE];
+int sockfd;
+
+/*
+ * Connect to socket server
+ */
+int socket_init()
+{
+ int sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sockfd < 0) {
+ error(1, "Error on opening socket");
+ }
+
+ struct hostent *server = gethostbyname(DOMAIN);
+ if (server == NULL) {
+ error(1, "No such host %s", DOMAIN);
+ }
+
+ struct sockaddr_in sv_addr;
+ memset(&sv_addr, 0, sizeof(sv_addr));
+ sv_addr.sin_family = AF_INET;
+ sv_addr.sin_port = htons(PORT);
+ memcpy(&sv_addr.sin_addr.s_addr, server->h_addr, server->h_length);
+
+/* free(server); */
+ if (connect(sockfd, (struct sockaddr *) &sv_addr, sizeof(sv_addr)) < 0) {
+ error(1, "Error on connect");
+ close(sockfd);
+ return 0;
+ }
+ printf("Connected to server at %s\n", DOMAIN);
+ return sockfd;
+}
+
+/*
+ * Performs key exchange with server
+ */
+int key_exchange(int sockfd)
+{
+ /* Generate the client's key pair */
+ uint8_t cl_pk[PUBLIC_KEY_SIZE], cl_sk[PRIVATE_KEY_SIZE];
+ crypto_kx_keypair(cl_pk, cl_sk);
+
+ /* Send our public key */
+ if (send_public_key(sockfd, cl_pk) < 0) {
+ return -1;
+ }
+
+ /* Get public key from server */
+ uint8_t *pk;
+ if ((pk = get_public_key(sockfd)) == NULL) {
+ return -1;
+ }
+
+ /* Compute a shared key using the server's public key and our secret key */
+ if (crypto_kx_client_session_keys(NULL, shared_key, cl_pk, cl_sk, pk) != 0) {
+ error(1, "Server public key is not acceptable");
+ free(pk);
+ close(sockfd);
+ return -1;
+ }
+ free(pk);
+ return 0;
+}
+
+void *sender()
+{
+ while (1) {
+ printf("Enter message to send to server: ");
+ fflush(stdout);
+ char line[1024];
+ line[0] = '\0';
+ size_t length = strlen(line);
+ while (length <= 1) {
+ fgets(line, sizeof(line), stdin);
+ length = strlen(line);
+ }
+ length -= 1;
+ line[length] = '\0';
+
+ uint8_t nonce[NONCE_SIZE];
+ uint8_t encrypted[length + ADDITIONAL_SIZE];
+ unsigned long long encrypted_len;
+
+ randombytes_buf(nonce, sizeof(nonce));
+ crypto_aead_xchacha20poly1305_ietf_encrypt(encrypted, &encrypted_len,
+ line, length,
+ NULL, 0, NULL, nonce, shared_key);
+ size_t payload_t = NONCE_SIZE + encrypted_len;
+ uint8_t encryptedwithnonce[payload_t];
+ memcpy(encryptedwithnonce, nonce, NONCE_SIZE);
+ memcpy(encryptedwithnonce + NONCE_SIZE, encrypted, encrypted_len);
+
+ message *msg = create_packet(1, 0x10, payload_t, encryptedwithnonce);
+ if (send_packet(msg, sockfd) != ZSM_STA_SUCCESS) {
+ close(sockfd);
+ }
+ free_packet(msg);
+ }
+ close(sockfd);
+
+}
+
+void *receiver()
+{
+ while (1) {
+ message servermsg;
+ if (recv_packet(&servermsg, sockfd) != ZSM_STA_SUCCESS) {
+ close(sockfd);
+ return 0;
+ }
+ free(servermsg.data);
+ }
+ return NULL;
+}
+
+int main()
+{
+ if (sodium_init() < 0) {
+ error(1, "Error initializing libsodium");
+ }
+ sockfd = socket_init();
+ if (key_exchange(sockfd) < 0) {
+ /* Fatal */
+ error(1, "Error performing key exchange with server");
+ }
+ pthread_t recv_worker, send_worker;
+ if (pthread_create(&recv_worker, NULL, sender, NULL) != 0) {
+ fprintf(stderr, "Error creating incoming thread\n");
+ return 1;
+ }
+
+ if (pthread_create(&send_worker, NULL, receiver, NULL) != 0) {
+ fprintf(stderr, "Error creating outgoing thread\n");
+ return 1;
+ }
+
+ // Join threads
+ pthread_join(recv_worker, NULL);
+ pthread_join(send_worker, NULL);
+
+ return 0;
+}
+
diff --git a/src/server/server.c b/src/server/server.c
@@ -0,0 +1,218 @@
+#include "packet.h"
+#include "notification.h"
+#include <pthread.h>
+
+socklen_t clilen;
+struct sockaddr_in cli_address;
+uint8_t shared_key[SHARED_KEY_SIZE];
+int clientfd;
+
+/*
+ * Initialise socket server
+ */
+int socket_init()
+{
+ int serverfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (serverfd < 0) {
+ error(1, "Error on opening socket");
+ }
+
+ /* Reuse addr(for debug) */
+ int optval = 1;
+ if (setsockopt(serverfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) {
+
+ error(1, "Error at setting SO_REUSEADDR");
+ }
+
+ struct sockaddr_in sv_addr;
+ memset(&sv_addr, 0, sizeof(sv_addr));
+ sv_addr.sin_family = AF_INET;
+ sv_addr.sin_addr.s_addr = INADDR_ANY;
+ sv_addr.sin_port = htons(PORT);
+
+ if (bind(serverfd, (struct sockaddr *) &sv_addr, sizeof(sv_addr)) < 0) {
+ error(1, "Error on bind");
+ }
+
+ if (listen(serverfd, MAX_CONNECTION) < 0) {
+ error(1, "Error on listen");
+ }
+
+ printf("Listening on port %d\n", PORT);
+ clilen = sizeof(cli_address);
+ return serverfd;
+}
+
+/*
+ * Performs key exchange with client
+ */
+int key_exchange(int clientfd)
+{
+ /* Generate the server's key pair */
+ uint8_t sv_pk[PUBLIC_KEY_SIZE], sv_sk[PRIVATE_KEY_SIZE];
+ crypto_kx_keypair(sv_pk, sv_sk);
+
+ /* Get public key from client */
+ uint8_t *pk;
+ if ((pk = get_public_key(clientfd)) == NULL) {
+ return -1;
+ }
+
+ /* Send our public key */
+ if (send_public_key(clientfd, sv_pk) < 0) {
+ free(pk);
+ return -1;
+ }
+
+ /* Compute a shared key using the client's public key and our secret key. */
+ if (crypto_kx_server_session_keys(NULL, shared_key, sv_pk, sv_sk, pk) != 0) {
+ error(0, "Client public key is not acceptable");
+ free(pk);
+ close(clientfd);
+ return -1;
+ }
+
+ free(pk);
+ return 0;
+}
+
+void signal_handler(int signal)
+{
+ switch (signal) {
+ case SIGPIPE:
+ error(0, "SIGPIPE received");
+ break;
+ case SIGABRT:
+ case SIGINT:
+ case SIGTERM:
+ notify_uninit();
+ error(1, "Shutdown signal received");
+ break;
+ }
+}
+
+void *receiver()
+{
+ int serverfd = socket_init();
+ clientfd = accept(serverfd, (struct sockaddr *) &cli_address, &clilen);
+ if (clientfd < 0) {
+ error(0, "Error on accepting client");
+ /* Continue accpeting connections */
+/* continue; */
+ }
+
+ if (key_exchange(clientfd) < 0) {
+ error(0, "Error performing key exchange with client");
+/* continue; */
+ }
+ while (1) {
+ message msg;
+ memset(&msg, 0, sizeof(msg));
+ if (recv_packet(&msg, clientfd) != ZSM_STA_SUCCESS) {
+ close(clientfd);
+ break;
+/* continue; */
+ }
+
+ size_t encrypted_len = msg.length - NONCE_SIZE;
+ size_t msg_len = encrypted_len - ADDITIONAL_SIZE;
+ uint8_t nonce[NONCE_SIZE];
+ uint8_t encrypted[encrypted_len];
+ uint8_t decrypted[msg_len + 1];
+ unsigned long long decrypted_len;
+ memcpy(nonce, msg.data, NONCE_SIZE);
+ memcpy(encrypted, msg.data + NONCE_SIZE, encrypted_len);
+
+ free(msg.data);
+ if (crypto_aead_xchacha20poly1305_ietf_decrypt(decrypted, &decrypted_len,
+ NULL,
+ encrypted, encrypted_len,
+ NULL, 0,
+ nonce, shared_key) != 0) {
+ error(0, "Cannot decrypt message");
+ } else {
+ /* Decrypted message */
+ decrypted[msg_len] = '\0';
+ printf("Decrypted: %s\n", decrypted);
+ send_notification(decrypted);
+ msg.data = malloc(14);
+ strcpy(msg.data, "Received data");
+ msg.length = 14;
+ send_packet(&msg, clientfd);
+ free(msg.data);
+ }
+ }
+ close(clientfd);
+ close(serverfd);
+ return NULL;
+}
+
+void *sender()
+{
+ while (1) {
+ printf("Enter message to send to client: ");
+ fflush(stdout);
+ char line[1024];
+ line[0] = '\0';
+ size_t length = strlen(line);
+ while (length <= 1) {
+ fgets(line, sizeof(line), stdin);
+ length = strlen(line);
+ }
+ length -= 1;
+ line[length] = '\0';
+
+ uint8_t nonce[NONCE_SIZE];
+ uint8_t encrypted[length + ADDITIONAL_SIZE];
+ unsigned long long encrypted_len;
+
+ randombytes_buf(nonce, sizeof(nonce));
+ crypto_aead_xchacha20poly1305_ietf_encrypt(encrypted, &encrypted_len,
+ line, length,
+ NULL, 0, NULL, nonce, shared_key);
+ size_t payload_t = NONCE_SIZE + encrypted_len;
+ uint8_t encryptedwithnonce[payload_t];
+ memcpy(encryptedwithnonce, nonce, NONCE_SIZE);
+ memcpy(encryptedwithnonce + NONCE_SIZE, encrypted, encrypted_len);
+
+ message *msg = create_packet(1, 0x10, payload_t, encryptedwithnonce);
+ if (send_packet(msg, clientfd) != ZSM_STA_SUCCESS) {
+ close(clientfd);
+ }
+ free_packet(msg);
+ }
+ close(clientfd);
+ return NULL;
+}
+
+int main()
+{
+ if (sodium_init() < 0) {
+ error(1, "Error initializing libsodium");
+ }
+ /* Init libnotify with app name */
+ if (notify_init("zsm") < 0) {
+ error(1, "Error initializing libnotify");
+ }
+ signal(SIGPIPE, signal_handler);
+ signal(SIGABRT, signal_handler);
+ signal(SIGINT, signal_handler);
+ signal(SIGTERM, signal_handler);
+
+ pthread_t recv_worker, send_worker;
+ if (pthread_create(&recv_worker, NULL, sender, NULL) != 0) {
+ fprintf(stderr, "Error creating incoming thread\n");
+ return 1;
+ }
+
+ if (pthread_create(&send_worker, NULL, receiver, NULL) != 0) {
+ fprintf(stderr, "Error creating outgoing thread\n");
+ return 1;
+ }
+
+ // Join threads
+ pthread_join(recv_worker, NULL);
+ pthread_join(send_worker, NULL);
+
+ return 0;
+}