Modified crypto to support multiple peers (multiple sender / multiple receiver) per ncrypt context

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
This commit is contained in:
Toni Uhlig
2025-01-08 21:00:37 +01:00
parent 7d94632811
commit a47bc9caa3
5 changed files with 455 additions and 381 deletions

View File

@@ -14,9 +14,13 @@ struct
struct cmdarg listen_address;
struct cmdarg local_private_key_file;
struct cmdarg remote_public_key_file;
struct cmdarg parse_json_lines;
struct cmdarg quiet;
} options = {.listen_address = CMDARG_STR("127.0.0.1:7443"),
.local_private_key_file = CMDARG_STR(NULL),
.remote_public_key_file = CMDARG_STR(NULL)};
.remote_public_key_file = CMDARG_STR(NULL),
.parse_json_lines = CMDARG_BOOL(0),
.quiet = CMDARG_BOOL(0)};
struct confopt config_map[] = {CONFOPT(NULL, &options.listen_address),
CONFOPT(NULL, &options.local_private_key_file),
@@ -26,7 +30,6 @@ static void print_usage(char const * const arg0)
{
static char const usage[] =
"Usage: %s "
"\t \t"
"[-l] [-L listen-address] [-k private-key-file] [-K public-key-file]\n"
"\t \t"
"[-h]\n\n"
@@ -35,6 +38,8 @@ static void print_usage(char const * const arg0)
"\t \t(encrypted) UDP packets sent by nDPId\n"
"\t-k\tThe path to the local private X25519 key file (PEM format)\n"
"\t-K\tThe path to the remote public X25519 key file (PEM format)\n"
"\t-p\tParse decrypted JSON lines\n"
"\t-q\tQuiet mode, print errors only\n"
"\t-h\tthis\n";
fprintf(stderr, usage, arg0);
@@ -44,7 +49,7 @@ static int parse_options(int argc, char ** argv)
{
int opt;
while ((opt = getopt(argc, argv, "lL:k:K:h")) != -1)
while ((opt = getopt(argc, argv, "lL:k:K:pqh")) != -1)
{
switch (opt)
{
@@ -60,6 +65,12 @@ static int parse_options(int argc, char ** argv)
case 'K':
set_cmdarg_string(&options.remote_public_key_file, optarg);
break;
case 'p':
set_cmdarg_boolean(&options.parse_json_lines, 1);
break;
case 'q':
set_cmdarg_boolean(&options.quiet, 1);
break;
case 'h':
default:
@@ -99,17 +110,44 @@ int udp_server(struct ncrypt * const nc)
return 1;
}
struct ncrypt_buffer read_buf = {};
size_t msgs_recvd = 0;
char read_buffer[NCRYPT_BUFFER_SIZE];
struct nDPIsrvd_json_buffer json_buf;
struct nDPIsrvd_jsmn json_ctx;
for (;;)
{
int bytes_read = ncrypt_decrypt_recv(nc, sock_fd, &read_buf);
if (bytes_read <= 0)
nDPIsrvd_json_buffer_reset(&json_buf);
size_t read_buffer_size = sizeof(read_buffer);
int ret = ncrypt_dgram_recv(nc, sock_fd, read_buffer, read_buffer_size);
if (ret < 0)
{
logger(1, "Crypto error: %d", bytes_read);
logger(1, "Crypto error: %d", ret);
break;
}
msgs_recvd++;
printf("read %d bytes: %.*s", bytes_read, (int)read_buf.data_used, read_buf.plaintext.data);
if (GET_CMDARG_BOOL(options.quiet) == 0)
{
printf("received: %.*s\n", (int)read_buffer_size, read_buffer);
if ((msgs_recvd % 25) == 0)
{
printf("*** Messages received: %zu ***\n", msgs_recvd);
}
}
if (GET_CMDARG_BOOL(options.parse_json_lines) != 0)
{
json_buf.buf.ptr.raw = (uint8_t *)read_buffer;
json_buf.buf.used = json_buf.buf.max = read_buffer_size;
enum nDPIsrvd_parse_return ret = nDPIsrvd_parse_line(&json_buf, &json_ctx);
if (ret != PARSE_OK)
{
logger(1, "JSON parsing failed with: %d", ret);
break;
}
json_ctx.tokens_found = 0;
}
}
return 0;
@@ -174,12 +212,6 @@ int main(int argc, char ** argv)
logger_early(1, "Crypto initialization failed: %d", ret);
return 1;
}
ret = ncrypt_init_decrypt(&nc);
if (ret != 0)
{
logger_early(1, "Crypto decrypt initialization failed: %d", ret);
return 1;
}
}
return udp_server(&nc);

View File

@@ -141,10 +141,10 @@ struct distributor_return_value
};
#define TC_INIT(initial, wanted) \
{ \
.mutex = PTHREAD_MUTEX_INITIALIZER, .condition = PTHREAD_COND_INITIALIZER, .value = initial, \
.wanted_value = wanted \
}
{.mutex = PTHREAD_MUTEX_INITIALIZER, \
.condition = PTHREAD_COND_INITIALIZER, \
.value = initial, \
.wanted_value = wanted}
struct thread_condition
{
pthread_mutex_t mutex;
@@ -1647,11 +1647,19 @@ static int ncrypt_selftest()
int ret = 0;
struct ncrypt nc_peer1 = {};
struct ncrypt nc_peer2 = {};
struct aes aes_peer1 = {};
struct aes aes_peer2 = {};
unsigned char peer1_priv_key[NCRYPT_X25519_KEYLEN];
unsigned char peer2_priv_key[NCRYPT_X25519_KEYLEN];
unsigned char peer1_pub_key[NCRYPT_X25519_KEYLEN];
unsigned char peer2_pub_key[NCRYPT_X25519_KEYLEN];
unsigned char iv[NCRYPT_AES_IVLEN];
char const plaintext[] = "Secret Message!";
unsigned char encrypted[NCRYPT_BUFFER_SIZE];
unsigned char tag[NCRYPT_TAG_SIZE];
char decrypted[NCRYPT_BUFFER_SIZE];
memset(iv, 0x41, sizeof(iv));
if (ncrypt_keygen(peer1_priv_key, peer1_pub_key) != 0)
{
@@ -1669,176 +1677,74 @@ static int ncrypt_selftest()
{
ret++;
}
if (ncrypt_init_encrypt(&nc_peer1) != 0)
if (ncrypt_init_encrypt(&nc_peer1, &aes_peer1) != 0)
{
ret++;
}
memcpy(&nc_peer2.iv, &nc_peer1.iv, sizeof(nc_peer1.iv));
if (ncrypt_init_decrypt(&nc_peer2) != 0)
if (ncrypt_init_decrypt(&nc_peer2, &aes_peer2) != 0)
{
ret++;
}
if (memcmp(nc_peer1.shared_secret, nc_peer2.shared_secret, NCRYPT_X25519_KEYLEN) != 0)
int enc_bytes = ncrypt_encrypt(&aes_peer1, plaintext, sizeof(plaintext), iv, encrypted, tag);
int dec_bytes = ncrypt_decrypt(&aes_peer2, encrypted, enc_bytes, iv, tag, decrypted);
if (enc_bytes < 0 || dec_bytes < 0)
{
ret++;
}
if (memcmp(nc_peer1.iv, nc_peer2.iv, NCRYPT_AES_IVLEN) != 0)
if (memcmp(plaintext, decrypted, sizeof(plaintext)) != 0)
{
ret++;
}
memcpy(iv, nc_peer1.iv, NCRYPT_AES_IVLEN);
unsigned char plaintext[] = "Secret Message";
unsigned char encrypted1[NCRYPT_BUFFER_SIZE];
unsigned char tag1[NCRYPT_TAG_SIZE];
unsigned char encrypted2[NCRYPT_BUFFER_SIZE];
unsigned char tag2[NCRYPT_TAG_SIZE];
unsigned char tmp1[NETWORK_BUFFER_MAX_SIZE];
unsigned char tmp2[NETWORK_BUFFER_MAX_SIZE];
ncrypt_free_aes(&aes_peer2);
ncrypt_free_aes(&aes_peer1);
memset(decrypted, '\0', sizeof(decrypted));
memset(encrypted1, 0xFF, sizeof(encrypted1));
memset(encrypted2, 0xFF, sizeof(encrypted2));
memset(tag1, 0xFF, sizeof(tag1));
memset(tag2, 0xFF, sizeof(tag2));
int enc_bytes;
int dec_bytes;
enc_bytes = ncrypt_encrypt(&nc_peer1, plaintext, sizeof(plaintext), encrypted1, tag1);
dec_bytes = ncrypt_decrypt(&nc_peer2, encrypted1, enc_bytes, tag1, tmp1);
if (enc_bytes != dec_bytes)
struct nDPIsrvd_address listen_address;
if (nDPIsrvd_setup_address(&listen_address, "127.0.0.1:17443") != 0)
{
ret++;
}
if (memcmp(plaintext, tmp1, dec_bytes) != 0)
if (ncrypt_add_peer(&nc_peer1, &listen_address) != 0)
{
ret++;
}
memset(tmp1, 0xFF, sizeof(tmp1));
enc_bytes = ncrypt_encrypt(&nc_peer1, plaintext, sizeof(plaintext), encrypted2, tag2);
dec_bytes = ncrypt_decrypt(&nc_peer2, encrypted2, enc_bytes, tag2, tmp1);
if (enc_bytes != dec_bytes)
{
ret++;
}
if (memcmp(plaintext, tmp1, dec_bytes) != 0)
if (ncrypt_init_encrypt2(&nc_peer1, &listen_address) != 0)
{
ret++;
}
if (memcmp(tag1, tag2, NCRYPT_TAG_SIZE) == 0)
int udp_sockfd_listen = socket(listen_address.raw.sa_family, SOCK_DGRAM, 0);
int udp_sockfd_connect = socket(listen_address.raw.sa_family, SOCK_DGRAM, 0);
if (udp_sockfd_listen < 0 || udp_sockfd_connect < 0)
{
ret++;
}
if (memcmp(encrypted1, encrypted2, enc_bytes) == 0)
if (bind(udp_sockfd_listen, &listen_address.raw, listen_address.size) != 0)
{
ret++;
}
if (connect(udp_sockfd_connect, &listen_address.raw, listen_address.size) < 0)
{
ret++;
}
if (memcmp(iv, nc_peer1.iv, NCRYPT_AES_IVLEN) == 0)
if (ncrypt_dgram_send(&nc_peer1, udp_sockfd_connect, plaintext, sizeof(plaintext)) != 0)
{
ret++;
}
if (ncrypt_dgram_recv(&nc_peer2, udp_sockfd_listen, decrypted, sizeof(decrypted)) != 0)
{
ret++;
}
memset(encrypted1, 0xFF, sizeof(encrypted1));
memset(tag1, 0xFF, sizeof(tag1));
memset(tmp1, 0x41, sizeof(tmp1));
enc_bytes = ncrypt_encrypt(&nc_peer1, tmp1, sizeof(tmp1), encrypted1, tag1);
dec_bytes = ncrypt_decrypt(&nc_peer2, encrypted1, enc_bytes, tag1, tmp2);
if (enc_bytes != sizeof(tmp1) || dec_bytes != sizeof(tmp2))
{
ret++;
}
if (memcmp(tmp1, tmp2, sizeof(tmp1)) != 0)
if (memcmp(plaintext, decrypted, sizeof(plaintext)) != 0)
{
ret++;
}
enc_bytes = ncrypt_encrypt(&nc_peer1, tmp1, sizeof(tmp1), encrypted2, tag2);
dec_bytes = ncrypt_decrypt(&nc_peer2, encrypted2, enc_bytes, tag2, tmp2);
if (enc_bytes != sizeof(tmp1) || dec_bytes != sizeof(tmp2))
{
ret++;
}
if (memcmp(tmp1, tmp2, sizeof(tmp1)) != 0)
{
ret++;
}
if (enc_bytes != dec_bytes)
{
ret++;
}
if (memcmp(tmp2, encrypted1, dec_bytes) == 0)
{
ret++;
}
if (memcmp(tag1, tag2, NCRYPT_TAG_SIZE) == 0)
{
ret++;
}
if (memcmp(encrypted1, encrypted2, enc_bytes) == 0)
{
ret++;
}
if (memcmp(iv, nc_peer1.iv, NCRYPT_AES_IVLEN) == 0)
{
ret++;
}
if (memcmp(nc_peer1.iv, nc_peer2.iv, NCRYPT_AES_IVLEN) != 0)
{
ret++;
}
int pipefds[2] = {-1, -1};
if (pipe2(pipefds, O_DIRECT) != 0)
{
ret++;
}
struct ncrypt_buffer encrypted_buf = {};
struct ncrypt_buffer decrypted_buf = {};
memcpy(encrypted_buf.plaintext.data, plaintext, sizeof(plaintext));
encrypted_buf.data_used = sizeof(plaintext);
int sent;
sent = ncrypt_encrypt_send(&nc_peer1, pipefds[1], &encrypted_buf);
if (sent < 0)
{
ret++;
}
int received = ncrypt_decrypt_recv(&nc_peer2, pipefds[0], &decrypted_buf);
if (received < 0)
{
ret++;
}
if (received != sent)
{
ret++;
}
sent = ncrypt_encrypt_send(&nc_peer1, pipefds[1], &encrypted_buf);
int sent2 = ncrypt_encrypt_send(&nc_peer1, pipefds[1], &encrypted_buf);
if (sent < 0 || sent != sent2)
{
ret++;
}
received = ncrypt_decrypt_recv(&nc_peer2, pipefds[0], &decrypted_buf);
int received2 = ncrypt_decrypt_recv(&nc_peer2, pipefds[0], &decrypted_buf);
if (received < 0 || received != received2)
{
ret++;
}
close(pipefds[0]);
close(pipefds[1]);
close(udp_sockfd_listen);
close(udp_sockfd_connect);
ncrypt_free(&nc_peer2);
ncrypt_free(&nc_peer1);

21
nDPId.c
View File

@@ -1618,7 +1618,14 @@ static struct nDPId_workflow * init_workflow(char const * const file_or_device)
free_workflow(&workflow);
return NULL;
}
rv = ncrypt_init_encrypt(&workflow->crypto);
rv = ncrypt_add_peer(&workflow->crypto, &nDPId_options.parsed_collector_address);
if (rv != 0)
{
logger(1, "Could not add peer: %d", rv);
free_workflow(&workflow);
return NULL;
}
rv = ncrypt_init_encrypt2(&workflow->crypto, &nDPId_options.parsed_collector_address);
if (rv != 0)
{
logger_early(1, "Could not init encryption mode: %d", rv);
@@ -2691,18 +2698,18 @@ static void send_to_collector(struct nDPId_reader_thread * const reader_thread,
IS_CMDARG_SET(nDPId_options.remote_public_key_file) != 0)
{
int rv;
struct ncrypt_buffer buf = {.data_used = s_ret};
memcpy(buf.plaintext.data, newline_json_msg, s_ret);
rv = ncrypt_encrypt_send(&workflow->crypto, reader_thread->collector_sockfd, &buf);
if (rv - (NCRYPT_AES_IVLEN + NCRYPT_TAG_SIZE) != s_ret)
errno = 0;
rv = ncrypt_dgram_send(&workflow->crypto, reader_thread->collector_sockfd, newline_json_msg, (size_t)s_ret);
if (rv != 0)
{
logger(1,
"[%8llu, %zu] Crypto: encrypt and send returned %d, but expected %d",
"[%8llu, %zu] Crypto: encrypt and send returned %d (buffer size %d): %s",
workflow->packets_captured,
reader_thread->array_index,
rv,
s_ret + (NCRYPT_AES_IVLEN + NCRYPT_TAG_SIZE));
s_ret,
strerror(errno));
}
return;
}

477
ncrypt.c
View File

@@ -6,12 +6,14 @@
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define OPENSSL_DUMP(ptr, siz) \
do \
{ \
fprintf(stderr, "Raw output (%s, %zu):\n", #ptr, siz); \
BIO_dump_indent_fp(stderr, ptr, siz, 2); \
fputc('\n', stderr); \
} while (0);
@@ -31,6 +33,73 @@ union iv
unsigned char buffer[NCRYPT_AES_IVLEN];
};
union packet
{
unsigned char raw[NCRYPT_PACKET_BUFFER_SIZE];
struct
{
unsigned char iv[NCRYPT_AES_IVLEN];
unsigned char tag[NCRYPT_TAG_SIZE];
unsigned char data[NCRYPT_BUFFER_SIZE];
} __attribute__((__packed__));
} __attribute__((__packed__));
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
_Static_assert(sizeof(((union iv *)0)->buffer) == sizeof(((union iv *)0)->numeric),
"IV buffer must be of the same size as the numerics");
#endif
static inline nDPIsrvd_hashkey peer_build_hashkey(struct nDPIsrvd_address const * const peer_address)
{
uint32_t hash = nDPIsrvd_HASHKEY_SEED;
socklen_t slen = peer_address->size;
while (slen-- > 0)
{
hash = ((hash << 5) + hash) + ((uint8_t *)&peer_address->raw)[slen];
}
return hash;
}
int ncrypt_add_peer(struct ncrypt * const nc, struct nDPIsrvd_address const * const peer_address)
{
nDPIsrvd_hashkey peer_key = peer_build_hashkey(peer_address);
if (peer_key == nDPIsrvd_HASHKEY_SEED)
{
return -1;
}
struct peer * peer = (struct peer *)calloc(1, sizeof(*peer));
if (peer == NULL)
{
return -2;
}
peer->hash_key = peer_key;
peer->address = *peer_address;
HASH_ADD_INT(nc->peers, hash_key, peer);
return 0;
}
struct peer * ncrypt_get_peer(struct ncrypt * const nc, struct nDPIsrvd_address const * const peer_address)
{
nDPIsrvd_hashkey peer_key = peer_build_hashkey(peer_address);
if (peer_key == nDPIsrvd_HASHKEY_SEED)
{
return NULL;
}
struct peer * peer;
HASH_FIND_INT(nc->peers, &peer_key, peer);
if (peer == NULL)
{
return NULL;
}
return peer;
}
int ncrypt_keygen(unsigned char priv_key[NCRYPT_X25519_KEYLEN], unsigned char pub_key[NCRYPT_X25519_KEYLEN])
{
EVP_PKEY * const pkey = EVP_PKEY_Q_keygen(NULL, NULL, "X25519");
@@ -109,7 +178,7 @@ int ncrypt_load_pubkey(char const * const public_key_file, unsigned char pub_key
return 0;
}
static int init_iv(struct ncrypt * const nc)
static int init_iv(struct peer * const peer)
{
FILE * rnd_fp;
@@ -120,7 +189,7 @@ static int init_iv(struct ncrypt * const nc)
return -1;
}
if (fread(&nc->iv[0], sizeof(nc->iv[0]), sizeof(nc->iv) / sizeof(nc->iv[0]), rnd_fp) != NCRYPT_AES_IVLEN)
if (fread(&peer->iv[0], sizeof(peer->iv[0]), sizeof(peer->iv) / sizeof(peer->iv[0]), rnd_fp) != NCRYPT_AES_IVLEN)
{
fclose(rnd_fp);
return -2;
@@ -131,9 +200,9 @@ static int init_iv(struct ncrypt * const nc)
return 0;
}
static void next_iv(struct ncrypt * const nc)
static void next_iv(struct peer * const peer)
{
union iv * const iv = (union iv *)&nc->iv[0];
union iv * const iv = (union iv *)&peer->iv[0];
uint64_t lower = be64toh(iv->numeric.lower);
lower++;
@@ -151,9 +220,19 @@ int ncrypt_init(struct ncrypt * const nc,
unsigned char local_priv_key[NCRYPT_X25519_KEYLEN],
unsigned char remote_pub_key[NCRYPT_X25519_KEYLEN])
{
EVP_PKEY_CTX * key_ctx;
int rv = 0;
EVP_PKEY_CTX * key_ctx = NULL;
size_t pub_key_datalen = 0;
size_t secret_len = 0;
struct
{
EVP_PKEY * priv_key;
unsigned char pub_key[NCRYPT_X25519_KEYLEN];
} local = {.priv_key = NULL, .pub_key = {}};
struct
{
EVP_PKEY * pub_key;
} remote = {.pub_key = NULL};
if (nc->libctx != NULL)
{
@@ -165,112 +244,143 @@ int ncrypt_init(struct ncrypt * const nc,
return -2;
}
nc->local.priv_key =
local.priv_key =
EVP_PKEY_new_raw_private_key_ex(nc->libctx, "X25519", nc->propq, local_priv_key, NCRYPT_X25519_KEYLEN);
if (nc->local.priv_key == NULL)
if (local.priv_key == NULL)
{
return -3;
}
if (EVP_PKEY_get_octet_string_param(nc->local.priv_key,
OSSL_PKEY_PARAM_PUB_KEY,
nc->local.pub_key,
sizeof(nc->local.pub_key),
&pub_key_datalen) == 0)
if (EVP_PKEY_get_octet_string_param(
local.priv_key, OSSL_PKEY_PARAM_PUB_KEY, local.pub_key, sizeof(local.pub_key), &pub_key_datalen) == 0)
{
return -4;
rv = -4;
goto error;
}
if (pub_key_datalen != NCRYPT_X25519_KEYLEN)
{
return -5;
rv = -5;
goto error;
}
nc->remote.pub_key =
remote.pub_key =
EVP_PKEY_new_raw_public_key_ex(nc->libctx, "X25519", nc->propq, remote_pub_key, NCRYPT_X25519_KEYLEN);
if (nc->remote.pub_key == NULL)
if (remote.pub_key == NULL)
{
return -6;
rv = -6;
goto error;
}
key_ctx = EVP_PKEY_CTX_new_from_pkey(nc->libctx, nc->local.priv_key, nc->propq);
key_ctx = EVP_PKEY_CTX_new_from_pkey(nc->libctx, local.priv_key, nc->propq);
if (key_ctx == NULL)
{
return -7;
rv = -7;
goto error;
}
if (EVP_PKEY_derive_init(key_ctx) == 0)
{
EVP_PKEY_CTX_free(key_ctx);
return -8;
rv = -8;
goto error;
}
if (EVP_PKEY_derive_set_peer(key_ctx, nc->remote.pub_key) == 0)
if (EVP_PKEY_derive_set_peer(key_ctx, remote.pub_key) == 0)
{
EVP_PKEY_CTX_free(key_ctx);
return -9;
rv = -9;
goto error;
}
if (EVP_PKEY_derive(key_ctx, NULL, &secret_len) == 0)
{
EVP_PKEY_CTX_free(key_ctx);
return -10;
rv = -10;
goto error;
}
if (secret_len != NCRYPT_X25519_KEYLEN)
{
EVP_PKEY_CTX_free(key_ctx);
return -11;
rv = -11;
goto error;
}
nc->shared_secret = OPENSSL_malloc(secret_len);
if (nc->shared_secret == NULL)
{
EVP_PKEY_CTX_free(key_ctx);
return -12;
}
if (EVP_PKEY_derive(key_ctx, nc->shared_secret, &secret_len) == 0)
{
EVP_PKEY_CTX_free(key_ctx);
OPENSSL_clear_free(nc->shared_secret, secret_len);
nc->shared_secret = NULL;
return -13;
rv = -12;
OPENSSL_cleanse(nc->shared_secret, NCRYPT_X25519_KEYLEN);
goto error;
}
nc->iv_mismatches = 0;
OPENSSL_cleanse(local_priv_key, NCRYPT_X25519_KEYLEN);
OPENSSL_cleanse(remote_pub_key, NCRYPT_X25519_KEYLEN);
error:
EVP_PKEY_CTX_free(key_ctx);
return 0;
EVP_PKEY_free(local.priv_key);
EVP_PKEY_free(remote.pub_key);
return rv;
}
int ncrypt_init_encrypt(struct ncrypt * const nc)
int ncrypt_init_encrypt(struct ncrypt * const nc, struct aes * const aes)
{
if (nc->aesctx == NULL)
aes->ctx = EVP_CIPHER_CTX_new();
if (aes->ctx == NULL)
{
nc->aesctx = EVP_CIPHER_CTX_new();
if (nc->aesctx == NULL)
{
return -1;
}
if (EVP_EncryptInit_ex(nc->aesctx, EVP_aes_256_gcm(), NULL, NULL, NULL) == 0)
{
return -2;
}
if (EVP_CIPHER_CTX_ctrl(nc->aesctx, EVP_CTRL_GCM_SET_IVLEN, NCRYPT_AES_IVLEN, NULL) == 0)
{
return -3;
}
return -3;
}
if (init_iv(nc) != 0)
if (EVP_EncryptInit_ex(aes->ctx, EVP_aes_256_gcm(), NULL, NULL, NULL) == 0)
{
return -4;
}
if (EVP_EncryptInit_ex(nc->aesctx, NULL, NULL, nc->shared_secret, nc->iv) == 0)
if (EVP_CIPHER_CTX_ctrl(aes->ctx, EVP_CTRL_GCM_SET_IVLEN, NCRYPT_AES_IVLEN, NULL) == 0)
{
return -5;
}
if (EVP_EncryptInit_ex(aes->ctx, NULL, NULL, nc->shared_secret, NULL) == 0)
{
return -6;
}
return 0;
}
int ncrypt_init_encrypt2(struct ncrypt * const nc, struct nDPIsrvd_address * const peer_address)
{
struct peer * const peer = ncrypt_get_peer(nc, peer_address);
if (peer == NULL)
{
return -1;
}
if (init_iv(peer) != 0)
{
return -2;
}
return ncrypt_init_encrypt(nc, &peer->aes);
}
int ncrypt_init_decrypt(struct ncrypt * const nc, struct aes * const aes)
{
aes->ctx = EVP_CIPHER_CTX_new();
if (aes->ctx == NULL)
{
return -2;
}
if (EVP_DecryptInit_ex(aes->ctx, EVP_aes_256_gcm(), NULL, NULL, NULL) == 0)
{
return -3;
}
if (EVP_CIPHER_CTX_ctrl(aes->ctx, EVP_CTRL_GCM_SET_IVLEN, NCRYPT_AES_IVLEN, NULL) == 0)
{
return -4;
}
if (EVP_DecryptInit_ex(aes->ctx, NULL, NULL, nc->shared_secret, NULL) == 0)
{
return -5;
}
@@ -278,93 +388,81 @@ int ncrypt_init_encrypt(struct ncrypt * const nc)
return 0;
}
int ncrypt_init_decrypt(struct ncrypt * const nc)
int ncrypt_init_decrypt2(struct ncrypt * const nc, struct nDPIsrvd_address * const peer_address)
{
if (nc->aesctx == NULL)
struct peer * const peer = ncrypt_get_peer(nc, peer_address);
if (peer == NULL)
{
nc->aesctx = EVP_CIPHER_CTX_new();
if (nc->aesctx == NULL)
{
return -1;
}
if (EVP_DecryptInit_ex(nc->aesctx, EVP_aes_256_gcm(), NULL, NULL, NULL) == 0)
{
return -2;
}
if (EVP_CIPHER_CTX_ctrl(nc->aesctx, EVP_CTRL_GCM_SET_IVLEN, NCRYPT_AES_IVLEN, NULL) == 0)
{
return -3;
}
return -1;
}
if (EVP_DecryptInit_ex(nc->aesctx, NULL, NULL, nc->shared_secret, nc->iv) == 0)
return ncrypt_init_decrypt(nc, &peer->aes);
}
void ncrypt_free_aes(struct aes * const aes)
{
EVP_CIPHER_CTX_free(aes->ctx);
aes->ctx = NULL;
}
static void cleanup_peers(struct ncrypt * const nc)
{
struct peer * current_peer;
struct peer * ctmp;
if (nc->peers == NULL)
{
return -4;
return;
}
return 0;
HASH_ITER(hh, nc->peers, current_peer, ctmp)
{
ncrypt_free_aes(&current_peer->aes);
HASH_DEL(nc->peers, current_peer);
free(current_peer);
}
}
void ncrypt_free(struct ncrypt * const nc)
{
if (nc->aesctx != NULL)
{
EVP_CIPHER_CTX_free(nc->aesctx);
nc->aesctx = NULL;
}
if (nc->shared_secret != NULL)
{
OPENSSL_clear_free(nc->shared_secret, NCRYPT_X25519_KEYLEN);
nc->shared_secret = NULL;
}
if (nc->local.priv_key != NULL)
{
EVP_PKEY_free(nc->local.priv_key);
nc->local.priv_key = NULL;
}
if (nc->remote.pub_key != NULL)
{
EVP_PKEY_free(nc->remote.pub_key);
nc->remote.pub_key = NULL;
}
OPENSSL_cleanse(nc->shared_secret, NCRYPT_X25519_KEYLEN);
if (nc->libctx != NULL)
{
OSSL_LIB_CTX_free(nc->libctx);
nc->libctx = NULL;
}
cleanup_peers(nc);
}
static int encrypt(struct ncrypt * const nc,
unsigned char const * const plaintext,
size_t used,
static int encrypt(struct aes * const aes,
char const * const plaintext,
size_t plaintext_size,
unsigned char const iv[NCRYPT_AES_IVLEN],
unsigned char encrypted[NCRYPT_BUFFER_SIZE],
unsigned char tag[NCRYPT_TAG_SIZE])
{
int encrypted_used;
int remaining;
if (EVP_EncryptInit_ex(nc->aesctx, NULL, NULL, NULL, nc->iv) == 0)
if (EVP_EncryptInit_ex(aes->ctx, NULL, NULL, NULL, iv) == 0)
{
return -2;
}
if (EVP_EncryptUpdate(nc->aesctx, encrypted, &encrypted_used, plaintext, used) == 0)
if (EVP_EncryptUpdate(aes->ctx, encrypted, &encrypted_used, (const unsigned char *)plaintext, plaintext_size) == 0)
{
return -3;
}
if (EVP_EncryptFinal_ex(nc->aesctx, encrypted + encrypted_used, &remaining) == 0)
if (EVP_EncryptFinal_ex(aes->ctx, encrypted + encrypted_used, &remaining) == 0)
{
return -4;
}
if (EVP_CIPHER_CTX_ctrl(nc->aesctx, EVP_CTRL_GCM_GET_TAG, NCRYPT_TAG_SIZE, tag) == 0)
if (EVP_CIPHER_CTX_ctrl(aes->ctx, EVP_CTRL_GCM_GET_TAG, NCRYPT_TAG_SIZE, tag) == 0)
{
return -5;
}
@@ -372,47 +470,47 @@ static int encrypt(struct ncrypt * const nc,
return encrypted_used + remaining;
}
int ncrypt_encrypt(struct ncrypt * const nc,
unsigned char const * const plaintext,
size_t used,
int ncrypt_encrypt(struct aes * const aes,
char const * const plaintext,
size_t plaintext_size,
unsigned char const iv[NCRYPT_AES_IVLEN],
unsigned char encrypted[NCRYPT_BUFFER_SIZE],
unsigned char tag[NCRYPT_TAG_SIZE])
{
if (used > NCRYPT_BUFFER_SIZE)
if (plaintext_size > NCRYPT_BUFFER_SIZE)
{
return -1;
}
next_iv(nc);
return encrypt(nc, plaintext, used, encrypted, tag);
return encrypt(aes, plaintext, plaintext_size, iv, encrypted, tag);
}
int decrypt(struct ncrypt * const nc,
unsigned char const * const encrypted,
size_t used,
unsigned char tag[NCRYPT_TAG_SIZE],
unsigned char plaintext[NCRYPT_BUFFER_SIZE])
static int decrypt(struct aes * const aes,
unsigned char const * const encrypted,
size_t encrypt_size,
unsigned char const iv[NCRYPT_AES_IVLEN],
unsigned char tag[NCRYPT_TAG_SIZE],
char plaintext[NCRYPT_BUFFER_SIZE])
{
int decrypted_used;
int remaining;
if (EVP_DecryptInit_ex(nc->aesctx, NULL, NULL, NULL, nc->iv) == 0)
if (EVP_DecryptInit_ex(aes->ctx, NULL, NULL, NULL, iv) == 0)
{
return -2;
}
if (EVP_DecryptUpdate(nc->aesctx, plaintext, &decrypted_used, encrypted, used) == 0)
if (EVP_DecryptUpdate(aes->ctx, (unsigned char *)plaintext, &decrypted_used, encrypted, encrypt_size) == 0)
{
return -3;
}
if (EVP_CIPHER_CTX_ctrl(nc->aesctx, EVP_CTRL_GCM_SET_TAG, NCRYPT_TAG_SIZE, tag) == 0)
if (EVP_CIPHER_CTX_ctrl(aes->ctx, EVP_CTRL_GCM_SET_TAG, NCRYPT_TAG_SIZE, tag) == 0)
{
return -4;
}
if (EVP_DecryptFinal_ex(nc->aesctx, plaintext + decrypted_used, &remaining) == 0)
if (EVP_DecryptFinal_ex(aes->ctx, (unsigned char *)plaintext + decrypted_used, &remaining) == 0)
{
return -5;
}
@@ -420,78 +518,117 @@ int decrypt(struct ncrypt * const nc,
return decrypted_used + remaining;
}
int ncrypt_decrypt(struct ncrypt * const nc,
int ncrypt_decrypt(struct aes * const aes,
unsigned char const * const encrypted,
size_t used,
size_t encrypt_size,
unsigned char const iv[NCRYPT_AES_IVLEN],
unsigned char tag[NCRYPT_TAG_SIZE],
unsigned char plaintext[NCRYPT_BUFFER_SIZE])
char plaintext[NCRYPT_BUFFER_SIZE])
{
if (used > NCRYPT_BUFFER_SIZE)
if (encrypt_size > NCRYPT_BUFFER_SIZE)
{
return -1;
}
next_iv(nc);
return decrypt(nc, encrypted, used, tag, plaintext);
return decrypt(aes, encrypted, encrypt_size, iv, tag, plaintext);
}
int ncrypt_encrypt_send(struct ncrypt * const nc, int fd, struct ncrypt_buffer * const buf)
int ncrypt_dgram_send(struct ncrypt * const nc, int fd, char const * const plaintext, size_t plaintext_size)
{
int encrypted_used = encrypt(nc, buf->plaintext.data, buf->data_used, buf->encrypted.data, buf->encrypted.tag);
if (encrypted_used < 0)
if (plaintext_size > NCRYPT_BUFFER_SIZE)
{
return -1;
}
memcpy(buf->encrypted.iv, nc->iv, NCRYPT_AES_IVLEN);
ssize_t bytes_written = write(fd, buf->encrypted.raw, NCRYPT_AES_IVLEN + NCRYPT_TAG_SIZE + encrypted_used);
next_iv(nc);
int retval = 0;
struct peer * current_peer;
struct peer * tmp_peer;
union packet encrypted;
HASH_ITER(hh, nc->peers, current_peer, tmp_peer)
{
int encrypted_used =
encrypt(&current_peer->aes, plaintext, plaintext_size, current_peer->iv, encrypted.data, encrypted.tag);
if (encrypted_used < 0 || encrypted_used > (int)NCRYPT_BUFFER_SIZE)
{
current_peer->crypto_errors++;
retval++;
continue;
}
if (bytes_written < 0)
{
return -2;
}
if (bytes_written != NCRYPT_AES_IVLEN + NCRYPT_TAG_SIZE + encrypted_used)
{
nc->partial_writes++;
buf->write_offset += bytes_written;
memcpy(encrypted.iv, current_peer->iv, NCRYPT_AES_IVLEN);
ssize_t bytes_written = sendto(fd,
encrypted.raw,
NCRYPT_PACKET_OVERHEAD + encrypted_used,
0,
&current_peer->address.raw,
current_peer->address.size);
next_iv(current_peer);
if (bytes_written < 0)
{
current_peer->send_errors++;
retval++;
continue;
}
if (bytes_written != NCRYPT_PACKET_OVERHEAD + encrypted_used)
{
current_peer->partial_writes++;
retval++;
continue;
}
}
return (int)bytes_written;
return retval;
}
int ncrypt_decrypt_recv(struct ncrypt * const nc, int fd, struct ncrypt_buffer * const buf)
int ncrypt_dgram_recv(struct ncrypt * const nc, int fd, char * const plaintext, size_t plaintext_size)
{
ssize_t bytes_read = read(fd, buf->encrypted.raw, sizeof(buf->encrypted.raw));
if (plaintext_size > NCRYPT_BUFFER_SIZE)
{
return -1;
}
struct nDPIsrvd_address remote = {.size = sizeof(remote.raw)};
union packet encrypted;
ssize_t bytes_read = recvfrom(fd, encrypted.raw, sizeof(encrypted.raw), 0, &remote.raw, &remote.size);
if (bytes_read < 0)
{
return -1;
}
if (bytes_read < NCRYPT_AES_IVLEN + NCRYPT_TAG_SIZE + 1)
{
return -2;
}
if (memcmp(nc->iv, buf->encrypted.iv, NCRYPT_AES_IVLEN) != 0)
{
nc->iv_mismatches++;
}
memcpy(nc->iv, buf->encrypted.iv, NCRYPT_AES_IVLEN);
int decrypted_used = decrypt(nc,
buf->encrypted.data,
bytes_read - NCRYPT_AES_IVLEN - NCRYPT_TAG_SIZE,
buf->encrypted.tag,
buf->plaintext.data);
next_iv(nc);
if (decrypted_used < 0)
if (bytes_read < NCRYPT_PACKET_MIN_SIZE)
{
return -3;
}
if (plaintext_size < (size_t)bytes_read - NCRYPT_PACKET_OVERHEAD)
{
return -4;
}
buf->data_used = decrypted_used;
struct peer * peer = ncrypt_get_peer(nc, &remote);
if (peer == NULL)
{
if (ncrypt_add_peer(nc, &remote) != 0)
{
return -5;
}
peer = ncrypt_get_peer(nc, &remote);
ncrypt_init_decrypt(nc, &peer->aes);
}
return (int)bytes_read;
if (memcmp(peer->iv, encrypted.iv, NCRYPT_AES_IVLEN) != 0)
{
peer->iv_mismatches++;
memcpy(peer->iv, encrypted.iv, NCRYPT_AES_IVLEN);
}
int decrypted_used =
decrypt(&peer->aes, encrypted.data, bytes_read - NCRYPT_PACKET_OVERHEAD, peer->iv, encrypted.tag, plaintext);
next_iv(peer);
if (decrypted_used < 0)
{
return -6;
}
return 0;
}

102
ncrypt.h
View File

@@ -4,63 +4,43 @@
#include <stdlib.h>
#include "config.h"
#include "nDPIsrvd.h"
#define NCRYPT_X25519_KEYLEN 32
#define NCRYPT_AES_IVLEN 12
#define NCRYPT_TAG_SIZE 16
#define NCRYPT_BUFFER_SIZE NETWORK_BUFFER_MAX_SIZE
#define NCRYPT_PACKET_BUFFER_SIZE NCRYPT_AES_IVLEN + NCRYPT_TAG_SIZE + NCRYPT_BUFFER_SIZE
#define NCRYPT_PACKET_OVERHEAD (NCRYPT_AES_IVLEN + NCRYPT_TAG_SIZE)
#define NCRYPT_PACKET_BUFFER_SIZE (NCRYPT_PACKET_OVERHEAD + NCRYPT_BUFFER_SIZE)
#define NCRYPT_PACKET_MIN_SIZE (NCRYPT_PACKET_OVERHEAD + NETWORK_BUFFER_LENGTH_DIGITS + 1)
struct aes
{
void * ctx;
};
struct peer
{
nDPIsrvd_hashkey hash_key;
struct nDPIsrvd_address address;
unsigned char iv[NCRYPT_AES_IVLEN];
size_t crypto_errors;
size_t iv_mismatches;
size_t send_errors;
size_t recv_errors;
size_t partial_writes;
struct aes aes;
UT_hash_handle hh;
};
struct ncrypt
{
void * libctx;
void * aesctx;
unsigned char * shared_secret;
const char * propq;
struct
{
void * priv_key;
unsigned char pub_key[NCRYPT_X25519_KEYLEN];
} local;
struct
{
void * pub_key;
} remote;
unsigned char iv[NCRYPT_AES_IVLEN];
size_t iv_mismatches;
size_t partial_writes;
unsigned char shared_secret[NCRYPT_X25519_KEYLEN];
struct peer * peers;
};
struct ncrypt_buffer
{
struct
{
unsigned char data[NCRYPT_BUFFER_SIZE];
} plaintext;
struct
{
union
{
unsigned char raw[NCRYPT_PACKET_BUFFER_SIZE];
struct
{
unsigned char iv[NCRYPT_AES_IVLEN];
unsigned char tag[NCRYPT_TAG_SIZE];
unsigned char data[NCRYPT_BUFFER_SIZE];
} __attribute__((__packed__));
};
} encrypted;
size_t data_used; // size of plaintext and encrypted is equal for AES-GCM
size_t write_offset; // partial write; offset to next bytes of data
};
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
_Static_assert(sizeof(((struct ncrypt_buffer *)0)->encrypted) == sizeof(((struct ncrypt_buffer *)0)->encrypted.raw),
"Raw buffer and iv/tag/data sizes differ");
#endif
int ncrypt_keygen(unsigned char priv_key[NCRYPT_X25519_KEYLEN], unsigned char pub_key[NCRYPT_X25519_KEYLEN]);
int ncrypt_load_privkey(char const * const private_key_file, unsigned char priv_key[NCRYPT_X25519_KEYLEN]);
@@ -71,26 +51,38 @@ int ncrypt_init(struct ncrypt * const nc,
unsigned char local_priv_key[NCRYPT_X25519_KEYLEN],
unsigned char remote_pub_key[NCRYPT_X25519_KEYLEN]);
int ncrypt_init_encrypt(struct ncrypt * const nc);
int ncrypt_init_encrypt(struct ncrypt * const nc, struct aes * const aes);
int ncrypt_init_decrypt(struct ncrypt * const nc);
int ncrypt_init_encrypt2(struct ncrypt * const nc, struct nDPIsrvd_address * const peer_address);
int ncrypt_init_decrypt(struct ncrypt * const nc, struct aes * const aes);
int ncrypt_init_decrypt2(struct ncrypt * const nc, struct nDPIsrvd_address * const peer_address);
void ncrypt_free_aes(struct aes * const aes);
void ncrypt_free(struct ncrypt * const nc);
int ncrypt_encrypt(struct ncrypt * const nc,
unsigned char const * const plaintext,
size_t used,
int ncrypt_add_peer(struct ncrypt * const nc, struct nDPIsrvd_address const * const peer_address);
struct peer * ncrypt_get_peer(struct ncrypt * const nc, struct nDPIsrvd_address const * const peer_address);
int ncrypt_encrypt(struct aes * const aes,
char const * const plaintext,
size_t plaintext_size,
unsigned char const iv[NCRYPT_AES_IVLEN],
unsigned char encrypted[NCRYPT_BUFFER_SIZE],
unsigned char tag[NCRYPT_TAG_SIZE]);
int ncrypt_decrypt(struct ncrypt * const nc,
int ncrypt_decrypt(struct aes * const aes,
unsigned char const * const encrypted,
size_t used,
size_t encrypted_size,
unsigned char const iv[NCRYPT_AES_IVLEN],
unsigned char tag[NCRYPT_TAG_SIZE],
unsigned char plaintext[NCRYPT_BUFFER_SIZE]);
char plaintext[NCRYPT_BUFFER_SIZE]);
int ncrypt_encrypt_send(struct ncrypt * const nc, int fd, struct ncrypt_buffer * const buf);
int ncrypt_dgram_send(struct ncrypt * const nc, int fd, char const * const plaintext, size_t plaintext_size);
int ncrypt_decrypt_recv(struct ncrypt * const nc, int fd, struct ncrypt_buffer * const buf);
int ncrypt_dgram_recv(struct ncrypt * const nc, int fd, char * const plaintext, size_t plaintext_size);
#endif