Initial commit
This commit is contained in:
commit
297f55bc60
9 changed files with 801 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
zsm
|
||||
zsmc
|
||||
*.o
|
||||
*.tar.gz
|
52
Makefile
Normal file
52
Makefile
Normal file
|
@ -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
|
22
README.md
Normal file
22
README.md
Normal file
|
@ -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.
|
8
include/notification.h
Normal file
8
include/notification.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#ifndef NOTIFICATION_H
|
||||
#define NOTIFICATION_H
|
||||
|
||||
#include <libnotify/notify.h>
|
||||
|
||||
void send_notification(const char *content);
|
||||
|
||||
#endif
|
69
include/packet.h
Normal file
69
include/packet.h
Normal file
|
@ -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
|
8
lib/notification.c
Normal file
8
lib/notification.c
Normal file
|
@ -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));
|
||||
}
|
273
lib/packet.c
Normal file
273
lib/packet.c
Normal file
|
@ -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);
|
||||
}
|
147
src/client/client.c
Normal file
147
src/client/client.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
218
src/server/server.c
Normal file
218
src/server/server.c
Normal file
|
@ -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;
|
||||
}
|
Loading…
Reference in a new issue