mirror of
https://github.com/utoni/nDPId.git
synced 2026-04-28 06:59:35 +00:00
Switch to OpenSSL for all crypto stuff
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
This commit is contained in:
parent
b8d3cf9e8f
commit
6d3dc99fad
7 changed files with 294 additions and 1091 deletions
|
|
@ -397,7 +397,7 @@ endif()
|
|||
if(ENABLE_CRYPTO)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
set(OSSL_DEFS "-DENABLE_CRYPTO=1")
|
||||
set(OSSL_LIBRARY "${OPENSSL_CRYPTO_LIBRARY}")
|
||||
set(OSSL_LIBRARY "${OPENSSL_SSL_LIBRARY}" "${OPENSSL_CRYPTO_LIBRARY}")
|
||||
endif()
|
||||
|
||||
if(STATIC_LIBNDPI_INSTALLDIR OR BUILD_NDPI OR NDPI_NO_PKGCONFIG)
|
||||
|
|
|
|||
122
nDPId-test.c
122
nDPId-test.c
|
|
@ -10,9 +10,6 @@ extern void nDPIsrvd_memprof_log_free(size_t free_size);
|
|||
// #define VERBOSE_MEMORY_PROFILING 1
|
||||
#define NO_MAIN 1
|
||||
#include "utils.c"
|
||||
#ifdef ENABLE_CRYPTO
|
||||
#include "ncrypt.c"
|
||||
#endif
|
||||
#include "nio.c"
|
||||
#include "nDPIsrvd.c"
|
||||
#include "nDPId.c"
|
||||
|
|
@ -1542,7 +1539,7 @@ static int nio_selftest()
|
|||
goto error;
|
||||
}
|
||||
|
||||
int pipefds[2];
|
||||
int pipefds[2] = {-1, -1};
|
||||
int rv = pipe(pipefds);
|
||||
if (rv < 0)
|
||||
{
|
||||
|
|
@ -1635,122 +1632,16 @@ static int nio_selftest()
|
|||
}
|
||||
|
||||
nio_free(&io);
|
||||
close(pipefds[0]);
|
||||
close(pipefds[1]);
|
||||
return 0;
|
||||
error:
|
||||
nio_free(&io);
|
||||
close(pipefds[0]);
|
||||
close(pipefds[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_CRYPTO
|
||||
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)
|
||||
{
|
||||
ret++;
|
||||
}
|
||||
if (ncrypt_keygen(peer2_priv_key, peer2_pub_key) != 0)
|
||||
{
|
||||
ret++;
|
||||
}
|
||||
if (ncrypt_init(&nc_peer1, peer1_priv_key, peer2_pub_key) != 0)
|
||||
{
|
||||
ret++;
|
||||
}
|
||||
if (ncrypt_init(&nc_peer2, peer2_priv_key, peer1_pub_key) != 0)
|
||||
{
|
||||
ret++;
|
||||
}
|
||||
if (ncrypt_init_encrypt(&nc_peer1, &aes_peer1) != 0)
|
||||
{
|
||||
ret++;
|
||||
}
|
||||
if (ncrypt_init_decrypt(&nc_peer2, &aes_peer2) != 0)
|
||||
{
|
||||
ret++;
|
||||
}
|
||||
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(plaintext, decrypted, sizeof(plaintext)) != 0)
|
||||
{
|
||||
ret++;
|
||||
}
|
||||
|
||||
ncrypt_free_aes(&aes_peer2);
|
||||
ncrypt_free_aes(&aes_peer1);
|
||||
memset(decrypted, '\0', sizeof(decrypted));
|
||||
|
||||
struct nDPIsrvd_address listen_address;
|
||||
if (nDPIsrvd_setup_address(&listen_address, "127.0.0.1:17443") != 0)
|
||||
{
|
||||
ret++;
|
||||
}
|
||||
if (ncrypt_add_peer(&nc_peer1, &listen_address) != 0)
|
||||
{
|
||||
ret++;
|
||||
}
|
||||
if (ncrypt_init_encrypt2(&nc_peer1, &listen_address) != 0)
|
||||
{
|
||||
ret++;
|
||||
}
|
||||
|
||||
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 (bind(udp_sockfd_listen, &listen_address.raw, listen_address.size) != 0)
|
||||
{
|
||||
ret++;
|
||||
} else
|
||||
if (connect(udp_sockfd_connect, &listen_address.raw, listen_address.size) < 0)
|
||||
{
|
||||
ret++;
|
||||
} else
|
||||
if (ncrypt_dgram_send(&nc_peer1, udp_sockfd_connect, plaintext, sizeof(plaintext)) != 0)
|
||||
{
|
||||
ret++;
|
||||
} else
|
||||
if (ncrypt_dgram_recv(&nc_peer2, udp_sockfd_listen, decrypted, sizeof(decrypted)) != 0)
|
||||
{
|
||||
ret++;
|
||||
}
|
||||
|
||||
if (memcmp(plaintext, decrypted, sizeof(plaintext)) != 0)
|
||||
{
|
||||
ret++;
|
||||
}
|
||||
|
||||
close(udp_sockfd_listen);
|
||||
close(udp_sockfd_connect);
|
||||
ncrypt_free(&nc_peer2);
|
||||
ncrypt_free(&nc_peer1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define THREADS_RETURNED_ERROR() \
|
||||
(nDPId_return.thread_return_value.val != 0 || nDPIsrvd_return.val != 0 || \
|
||||
distributor_return.thread_return_value.val != 0)
|
||||
|
|
@ -1784,9 +1675,6 @@ int main(int argc, char ** argv)
|
|||
|
||||
retval += base64_selftest();
|
||||
retval += nio_selftest();
|
||||
#ifdef ENABLE_CRYPTO
|
||||
retval += ncrypt_selftest();
|
||||
#endif
|
||||
|
||||
logger(1, "Selftest returned: %d%s", retval, (retval == 0 ? " (OK)" : ""));
|
||||
return retval;
|
||||
|
|
|
|||
153
nDPId.c
153
nDPId.c
|
|
@ -311,7 +311,7 @@ struct nDPId_workflow
|
|||
uint64_t current_compression_diff;
|
||||
#endif
|
||||
#ifdef ENABLE_CRYPTO
|
||||
struct ncrypt crypto;
|
||||
struct ncrypt_entity ncrypt_entity;
|
||||
#endif
|
||||
|
||||
uint64_t last_scan_time;
|
||||
|
|
@ -472,6 +472,9 @@ static MT_VALUE(zlib_compression_diff, uint64_t) = MT_INIT(0);
|
|||
static MT_VALUE(zlib_compression_bytes, uint64_t) = MT_INIT(0);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_CRYPTO
|
||||
static struct ncrypt_ctx ncrypt_ctx;
|
||||
#endif
|
||||
static struct
|
||||
{
|
||||
/* options which are resolved automatically */
|
||||
|
|
@ -508,8 +511,9 @@ static struct
|
|||
struct cmdarg use_pfring;
|
||||
#endif
|
||||
#ifdef ENABLE_CRYPTO
|
||||
struct cmdarg local_private_key_file;
|
||||
struct cmdarg remote_public_key_file;
|
||||
struct cmdarg client_crt_pem_file;
|
||||
struct cmdarg client_key_pem_file;
|
||||
struct cmdarg server_ca_pem_file;
|
||||
#endif
|
||||
/* subopts */
|
||||
struct cmdarg max_flows_per_thread;
|
||||
|
|
@ -562,8 +566,9 @@ static struct
|
|||
.use_pfring = CMDARG_BOOL(0),
|
||||
#endif
|
||||
#ifdef ENABLE_CRYPTO
|
||||
.local_private_key_file = CMDARG_STR(NULL),
|
||||
.remote_public_key_file = CMDARG_STR(NULL),
|
||||
.client_crt_pem_file = CMDARG_STR(NULL),
|
||||
.client_key_pem_file = CMDARG_STR(NULL),
|
||||
.server_ca_pem_file = CMDARG_STR(NULL),
|
||||
#endif
|
||||
.max_flows_per_thread = CMDARG_ULL(nDPId_MAX_FLOWS_PER_THREAD / 2),
|
||||
.max_idle_flows_per_thread = CMDARG_ULL(nDPId_MAX_IDLE_FLOWS_PER_THREAD / 2),
|
||||
|
|
@ -1573,68 +1578,6 @@ static struct nDPId_workflow * init_workflow(char const * const file_or_device)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_CRYPTO
|
||||
if (IS_CMDARG_SET(nDPId_options.local_private_key_file) != 0 &&
|
||||
IS_CMDARG_SET(nDPId_options.remote_public_key_file) != 0)
|
||||
{
|
||||
unsigned char local_priv_key[NCRYPT_X25519_KEYLEN];
|
||||
unsigned char remote_pub_key[NCRYPT_X25519_KEYLEN];
|
||||
int rv;
|
||||
|
||||
rv = chmod_chown(GET_CMDARG_STR(nDPId_options.local_private_key_file), S_IRUSR | S_IWUSR, "root", "root");
|
||||
if (rv != 0)
|
||||
{
|
||||
logger_early(1,
|
||||
"Could not chmod/chown private key file `%s' to 0600/root: %s",
|
||||
GET_CMDARG_STR(nDPId_options.local_private_key_file),
|
||||
strerror(rv));
|
||||
free_workflow(&workflow);
|
||||
return NULL;
|
||||
}
|
||||
rv = ncrypt_load_privkey(GET_CMDARG_STR(nDPId_options.local_private_key_file), local_priv_key);
|
||||
if (rv != 0)
|
||||
{
|
||||
logger_early(1,
|
||||
"Could not load (local) private key file `%s': %d",
|
||||
GET_CMDARG_STR(nDPId_options.local_private_key_file),
|
||||
rv);
|
||||
free_workflow(&workflow);
|
||||
return NULL;
|
||||
}
|
||||
rv = ncrypt_load_pubkey(GET_CMDARG_STR(nDPId_options.remote_public_key_file), remote_pub_key);
|
||||
if (rv != 0)
|
||||
{
|
||||
logger_early(1,
|
||||
"Could not load (remote) public key file `%s': %d",
|
||||
GET_CMDARG_STR(nDPId_options.remote_public_key_file),
|
||||
rv);
|
||||
free_workflow(&workflow);
|
||||
return NULL;
|
||||
}
|
||||
rv = ncrypt_init(&workflow->crypto, local_priv_key, remote_pub_key);
|
||||
if (rv != 0)
|
||||
{
|
||||
logger_early(1, "Could not init crypto system: %d", rv);
|
||||
free_workflow(&workflow);
|
||||
return NULL;
|
||||
}
|
||||
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);
|
||||
free_workflow(&workflow);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return workflow;
|
||||
}
|
||||
|
||||
|
|
@ -1763,13 +1706,6 @@ static void free_workflow(struct nDPId_workflow ** const workflow)
|
|||
npfring_close(&w->npf);
|
||||
}
|
||||
#endif
|
||||
#ifdef ENABLE_CRYPTO
|
||||
if (IS_CMDARG_SET(nDPId_options.local_private_key_file) != 0 &&
|
||||
IS_CMDARG_SET(nDPId_options.remote_public_key_file) != 0)
|
||||
{
|
||||
ncrypt_free(&w->crypto);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (w->pcap_handle != NULL)
|
||||
{
|
||||
|
|
@ -2693,28 +2629,6 @@ static void send_to_collector(struct nDPId_reader_thread * const reader_thread,
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_CRYPTO
|
||||
if (IS_CMDARG_SET(nDPId_options.local_private_key_file) != 0 &&
|
||||
IS_CMDARG_SET(nDPId_options.remote_public_key_file) != 0)
|
||||
{
|
||||
int rv;
|
||||
|
||||
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 (buffer size %d): %s",
|
||||
workflow->packets_captured,
|
||||
reader_thread->array_index,
|
||||
rv,
|
||||
s_ret,
|
||||
strerror(errno));
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
errno = 0;
|
||||
ssize_t written;
|
||||
if (reader_thread->collector_sock_last_errno == 0 &&
|
||||
|
|
@ -5559,9 +5473,11 @@ static void print_usage(char const * const arg0)
|
|||
"\t-c\tPath to a UNIX socket (nDPIsrvd Collector) or a custom UDP endpoint.\n"
|
||||
"\t \tDefault: `%s'\n"
|
||||
#ifdef ENABLE_CRYPTO
|
||||
"\t-k\tPath to the local private key file (PEM format)\n"
|
||||
"\t-k\tPath to the client certificate file (PEM format)\n"
|
||||
"\t \tDefault: disabled\n"
|
||||
"\t-K\tPath to the remote public key file (PEM format)\n"
|
||||
"\t-K\tPath to the client key file (PEM format)\n"
|
||||
"\t \tDefault: disabled\n"
|
||||
"\t-F\tPath to the server CA file (PEM format)\n"
|
||||
"\t \tDefault: disabled\n"
|
||||
#endif
|
||||
#ifdef ENABLE_EPOLL
|
||||
|
|
@ -5682,7 +5598,7 @@ static int nDPId_parse_options(int argc, char ** argv)
|
|||
{
|
||||
int opt;
|
||||
|
||||
while ((opt = getopt(argc, argv, "f:i:rIEB:tlL:c:k:K:edp:u:g:R:P:C:J:S:a:U:Azo:vh")) != -1)
|
||||
while ((opt = getopt(argc, argv, "f:i:rIEB:tlL:c:k:K:F:edp:u:g:R:P:C:J:S:a:U:Azo:vh")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
|
|
@ -5726,7 +5642,7 @@ static int nDPId_parse_options(int argc, char ** argv)
|
|||
break;
|
||||
case 'k':
|
||||
#ifdef ENABLE_CRYPTO
|
||||
set_cmdarg_string(&nDPId_options.local_private_key_file, optarg);
|
||||
set_cmdarg_string(&nDPId_options.client_crt_pem_file, optarg);
|
||||
break;
|
||||
#else
|
||||
logger(1, "%s", "nDPId was built w/o OpenSSL/Crypto support");
|
||||
|
|
@ -5734,7 +5650,15 @@ static int nDPId_parse_options(int argc, char ** argv)
|
|||
#endif
|
||||
case 'K':
|
||||
#ifdef ENABLE_CRYPTO
|
||||
set_cmdarg_string(&nDPId_options.remote_public_key_file, optarg);
|
||||
set_cmdarg_string(&nDPId_options.client_key_pem_file, optarg);
|
||||
break;
|
||||
#else
|
||||
logger(1, "%s", "nDPId was built w/o OpenSSL/Crypto support");
|
||||
return 1;
|
||||
#endif
|
||||
case 'F':
|
||||
#ifdef ENABLE_CRYPTO
|
||||
set_cmdarg_string(&nDPId_options.server_ca_pem_file, optarg);
|
||||
break;
|
||||
#else
|
||||
logger(1, "%s", "nDPId was built w/o OpenSSL/Crypto support");
|
||||
|
|
@ -6099,12 +6023,14 @@ static int validate_options(void)
|
|||
logger_early(1, "%s", "Higher values of max-packets-per-flow-to-send may cause superfluous network usage.");
|
||||
}
|
||||
#ifdef ENABLE_CRYPTO
|
||||
if ((IS_CMDARG_SET(nDPId_options.local_private_key_file) != 0 &&
|
||||
IS_CMDARG_SET(nDPId_options.remote_public_key_file) == 0) ||
|
||||
(IS_CMDARG_SET(nDPId_options.local_private_key_file) == 0 &&
|
||||
IS_CMDARG_SET(nDPId_options.remote_public_key_file) != 0))
|
||||
if ((IS_CMDARG_SET(nDPId_options.client_crt_pem_file) != 0 &&
|
||||
IS_CMDARG_SET(nDPId_options.client_key_pem_file) == 0) ||
|
||||
(IS_CMDARG_SET(nDPId_options.client_crt_pem_file) == 0 &&
|
||||
IS_CMDARG_SET(nDPId_options.client_key_pem_file) != 0) ||
|
||||
(IS_CMDARG_SET(nDPId_options.client_crt_pem_file) != 0 &&
|
||||
IS_CMDARG_SET(nDPId_options.server_ca_pem_file) == 0))
|
||||
{
|
||||
logger_early(1, "%s", "Encryption requires a local private key file and a remote public key file to be set.");
|
||||
logger_early(1, "%s", "Encryption requires a client certificate, key and a server CA file to be set. See `-k', `-K' and `-F'.");
|
||||
retval = 1;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -6201,6 +6127,10 @@ int main(int argc, char ** argv)
|
|||
set_ndpi_flow_free(NULL);
|
||||
|
||||
init_logging("nDPId");
|
||||
#ifdef ENABLE_CRYPTO
|
||||
ncrypt_init();
|
||||
ncrypt_ctx_init(&ncrypt_ctx);
|
||||
#endif
|
||||
|
||||
if (nDPId_parse_options(argc, argv) != 0)
|
||||
{
|
||||
|
|
@ -6238,6 +6168,17 @@ int main(int argc, char ** argv)
|
|||
return 1;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_CRYPTO
|
||||
if (IS_CMDARG_SET(nDPId_options.server_ca_pem_file) != 0 &&
|
||||
ncrypt_init_client(&ncrypt_ctx, GET_CMDARG_STR(nDPId_options.server_ca_pem_file),
|
||||
GET_CMDARG_STR(nDPId_options.client_key_pem_file),
|
||||
GET_CMDARG_STR(nDPId_options.client_crt_pem_file)) != NCRYPT_SUCCESS)
|
||||
{
|
||||
logger_early(1, "%s", "Could not initialize crypto.");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
log_app_info();
|
||||
|
||||
nDPId_print_deps_version(stdout);
|
||||
|
|
|
|||
910
ncrypt.c
910
ncrypt.c
|
|
@ -4,798 +4,160 @@
|
|||
#include <openssl/conf.h>
|
||||
#include <openssl/core_names.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/kdf.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <openssl/ssl.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);
|
||||
#define OPENSSL_ERROR(retval) \
|
||||
do \
|
||||
{ \
|
||||
fprintf(stderr, "OpenSSL Error: %s\n", ERR_error_string(ERR_get_error(), NULL)); \
|
||||
} while (0);
|
||||
#define PACKET_TYPE_KEYEX 0x00u
|
||||
#define PACKET_TYPE_JSON 0xFFu
|
||||
#define PACKET_JSON_OVERHEAD (NCRYPT_AAD_SIZE + NCRYPT_AES_IVLEN + NCRYPT_TAG_SIZE)
|
||||
#define PACKET_BUFFER_SIZE (PACKET_JSON_OVERHEAD + NCRYPT_BUFFER_SIZE)
|
||||
#define NCRYPT_PACKED __attribute__((__packed__))
|
||||
|
||||
static unsigned char hkdf_salt[] = {0xf2, 0xad, 0xc9, 0xca, 0x6e, 0xb3, 0xd9, 0xcd, 0x3b, 0x34, 0xf3, 0x8d, 0x75,
|
||||
0x91, 0x84, 0xbe, 0x7b, 0x1a, 0x5f, 0x80, 0x5f, 0x20, 0x86, 0x97, 0x37, 0xec,
|
||||
0x72, 0x25, 0x2a, 0x4c, 0x9d, 0x0e, 0x10, 0x8e, 0xaf, 0xf0, 0x43, 0x04, 0xb4,
|
||||
0x9e, 0xe5, 0x46, 0x41, 0xb0, 0xb1, 0xc3, 0x7c, 0x5a, 0x35, 0x2b, 0x75, 0xa9,
|
||||
0x36, 0xfc, 0x5e, 0x6c, 0xed, 0x32, 0x00, 0xd1, 0xf0, 0xb1, 0xc3, 0x0d};
|
||||
|
||||
union iv
|
||||
int ncrypt_init(void)
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint32_t upper;
|
||||
uint64_t lower;
|
||||
} NCRYPT_PACKED numeric;
|
||||
unsigned char buffer[NCRYPT_AES_IVLEN];
|
||||
};
|
||||
SSL_load_error_strings();
|
||||
OpenSSL_add_all_algorithms();
|
||||
|
||||
union packet
|
||||
return NCRYPT_SUCCESS;
|
||||
}
|
||||
|
||||
static int ncrypt_init_ctx(struct ncrypt_ctx * const ctx, SSL_METHOD const * const meth)
|
||||
{
|
||||
unsigned char raw[PACKET_BUFFER_SIZE];
|
||||
struct
|
||||
if (meth == NULL)
|
||||
{
|
||||
union
|
||||
return NCRYPT_NULL_PTR;
|
||||
}
|
||||
if (ctx->ssl_ctx != NULL)
|
||||
{
|
||||
return NCRYPT_ALREADY_INITIALIZED;
|
||||
}
|
||||
|
||||
ctx->ssl_ctx = SSL_CTX_new(meth);
|
||||
if (ctx->ssl_ctx == NULL)
|
||||
{
|
||||
return NCRYPT_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
SSL_CTX_set_min_proto_version(ctx->ssl_ctx, TLS1_3_VERSION);
|
||||
SSL_CTX_set_max_proto_version(ctx->ssl_ctx, TLS1_3_VERSION);
|
||||
SSL_CTX_set_ciphersuites(ctx->ssl_ctx, "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256");
|
||||
|
||||
return NCRYPT_SUCCESS;
|
||||
}
|
||||
|
||||
static int ncrypt_load_pems(struct ncrypt_ctx * const ctx,
|
||||
char const * const ca_path,
|
||||
char const * const privkey_pem_path,
|
||||
char const * const pubkey_pem_path)
|
||||
{
|
||||
if (SSL_CTX_use_certificate_file(ctx->ssl_ctx, pubkey_pem_path, SSL_FILETYPE_PEM) <= 0 ||
|
||||
SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, privkey_pem_path, SSL_FILETYPE_PEM) <= 0 ||
|
||||
SSL_CTX_load_verify_locations(ctx->ssl_ctx, ca_path, NULL) <= 0)
|
||||
{
|
||||
return NCRYPT_PEM_LOAD_FAILED;
|
||||
}
|
||||
|
||||
SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
||||
SSL_CTX_set_verify_depth(ctx->ssl_ctx, 4);
|
||||
return NCRYPT_SUCCESS;
|
||||
}
|
||||
|
||||
int ncrypt_init_client(struct ncrypt_ctx * const ctx,
|
||||
char const * const ca_path,
|
||||
char const * const privkey_pem_path,
|
||||
char const * const pubkey_pem_path)
|
||||
{
|
||||
if (ca_path == NULL || privkey_pem_path == NULL || pubkey_pem_path == NULL)
|
||||
{
|
||||
return NCRYPT_NULL_PTR;
|
||||
}
|
||||
|
||||
int rv = ncrypt_init_ctx(ctx, TLS_client_method());
|
||||
|
||||
if (rv != NCRYPT_SUCCESS)
|
||||
{
|
||||
return rv;
|
||||
}
|
||||
|
||||
return ncrypt_load_pems(ctx, ca_path, privkey_pem_path, pubkey_pem_path);
|
||||
}
|
||||
|
||||
int ncrypt_init_server(struct ncrypt_ctx * const ctx,
|
||||
char const * const ca_path,
|
||||
char const * const privkey_pem_path,
|
||||
char const * const pubkey_pem_path)
|
||||
{
|
||||
if (ca_path == NULL || privkey_pem_path == NULL || pubkey_pem_path == NULL)
|
||||
{
|
||||
return NCRYPT_NULL_PTR;
|
||||
}
|
||||
|
||||
int rv = ncrypt_init_ctx(ctx, TLS_server_method());
|
||||
|
||||
if (rv != NCRYPT_SUCCESS)
|
||||
{
|
||||
return rv;
|
||||
}
|
||||
|
||||
return ncrypt_load_pems(ctx, ca_path, privkey_pem_path, pubkey_pem_path);
|
||||
}
|
||||
|
||||
int ncrypt_on_connect(struct ncrypt_ctx * const ctx, int connect_fd, struct ncrypt_entity * const ent)
|
||||
{
|
||||
if (ent->ssl == NULL)
|
||||
{
|
||||
ent->ssl = SSL_new(ctx->ssl_ctx);
|
||||
if (ent->ssl == NULL)
|
||||
{
|
||||
unsigned char raw[NCRYPT_AAD_SIZE];
|
||||
struct
|
||||
{
|
||||
unsigned char type;
|
||||
uint16_t size;
|
||||
} NCRYPT_PACKED;
|
||||
} NCRYPT_PACKED aad;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned char pubkey[NCRYPT_X25519_KEYLEN];
|
||||
unsigned char signature[32];
|
||||
} keyex;
|
||||
struct
|
||||
{
|
||||
unsigned char iv[NCRYPT_AES_IVLEN];
|
||||
unsigned char tag[NCRYPT_TAG_SIZE];
|
||||
unsigned char data[NCRYPT_BUFFER_SIZE];
|
||||
} json;
|
||||
};
|
||||
} NCRYPT_PACKED;
|
||||
} NCRYPT_PACKED;
|
||||
|
||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
||||
_Static_assert(sizeof(((union packet *)0)->aad.raw) ==
|
||||
sizeof(((union packet *)0)->aad.type) + sizeof(((union packet *)0)->aad.size),
|
||||
"NCrypt packet AAD is not equal the expected size");
|
||||
_Static_assert(sizeof(((union packet *)0)->raw) ==
|
||||
sizeof(((union packet *)0)->aad) + sizeof(((union packet *)0)->json.iv) +
|
||||
sizeof(((union packet *)0)->json.tag) + sizeof(((union packet *)0)->json.data),
|
||||
"NCrypt packet is not equal the expected size");
|
||||
_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");
|
||||
size_t klen = NCRYPT_X25519_KEYLEN;
|
||||
|
||||
if (EVP_PKEY_get_raw_private_key(pkey, priv_key, &klen) == 0 || klen != NCRYPT_X25519_KEYLEN)
|
||||
{
|
||||
EVP_PKEY_free(pkey);
|
||||
return -1;
|
||||
}
|
||||
if (EVP_PKEY_get_raw_public_key(pkey, pub_key, &klen) == 0 || klen != NCRYPT_X25519_KEYLEN)
|
||||
{
|
||||
return -2;
|
||||
EVP_PKEY_free(pkey);
|
||||
}
|
||||
|
||||
EVP_PKEY_free(pkey);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ncrypt_load_privkey(char const * const private_key_file, unsigned char priv_key[NCRYPT_X25519_KEYLEN])
|
||||
{
|
||||
FILE * const pkfp = fopen(private_key_file, "r+b");
|
||||
EVP_PKEY * pkey = NULL;
|
||||
size_t klen = NCRYPT_X25519_KEYLEN;
|
||||
|
||||
if (pkfp == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
pkey = PEM_read_PrivateKey(pkfp, NULL, NULL, NULL);
|
||||
if (pkey == NULL)
|
||||
{
|
||||
fclose(pkfp);
|
||||
return -2;
|
||||
}
|
||||
fclose(pkfp);
|
||||
|
||||
if (EVP_PKEY_get_raw_private_key(pkey, priv_key, &klen) == 0 || klen != NCRYPT_X25519_KEYLEN)
|
||||
{
|
||||
EVP_PKEY_free(pkey);
|
||||
return -3;
|
||||
}
|
||||
|
||||
EVP_PKEY_free(pkey);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ncrypt_load_pubkey(char const * const public_key_file, unsigned char pub_key[NCRYPT_X25519_KEYLEN])
|
||||
{
|
||||
FILE * const pkfp = fopen(public_key_file, "r+b");
|
||||
EVP_PKEY * pkey = NULL;
|
||||
size_t klen = NCRYPT_X25519_KEYLEN;
|
||||
|
||||
if (pkfp == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
pkey = PEM_read_PUBKEY(pkfp, NULL, NULL, NULL);
|
||||
if (pkey == NULL)
|
||||
{
|
||||
fclose(pkfp);
|
||||
return -2;
|
||||
}
|
||||
fclose(pkfp);
|
||||
|
||||
if (EVP_PKEY_get_raw_public_key(pkey, pub_key, &klen) == 0 || klen != NCRYPT_X25519_KEYLEN)
|
||||
{
|
||||
EVP_PKEY_free(pkey);
|
||||
return -3;
|
||||
}
|
||||
|
||||
EVP_PKEY_free(pkey);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init_iv(struct peer * const peer)
|
||||
{
|
||||
FILE * rnd_fp;
|
||||
|
||||
rnd_fp = fopen("/dev/random", "r+b");
|
||||
|
||||
if (rnd_fp == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
fclose(rnd_fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void next_iv(struct peer * const peer)
|
||||
{
|
||||
union iv * const iv = (union iv *)&peer->iv[0];
|
||||
|
||||
uint64_t lower = be64toh(iv->numeric.lower);
|
||||
lower++;
|
||||
iv->numeric.lower = htobe64(lower);
|
||||
|
||||
if (iv->numeric.lower == 0)
|
||||
{
|
||||
uint32_t upper = be32toh(iv->numeric.upper);
|
||||
upper++;
|
||||
iv->numeric.upper = htobe32(upper);
|
||||
}
|
||||
}
|
||||
|
||||
static int hkdf_x25519(unsigned char shared_secret[NCRYPT_X25519_KEYLEN],
|
||||
char * label,
|
||||
unsigned char out[NCRYPT_X25519_KEYLEN])
|
||||
{
|
||||
EVP_KDF * kdf;
|
||||
EVP_KDF_CTX * kctx;
|
||||
OSSL_PARAM params[5], *p = params;
|
||||
|
||||
kdf = EVP_KDF_fetch(NULL, "HKDF", NULL);
|
||||
kctx = EVP_KDF_CTX_new(kdf);
|
||||
EVP_KDF_free(kdf);
|
||||
|
||||
*p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, SN_sha256, strlen(SN_sha256));
|
||||
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, shared_secret, NCRYPT_X25519_KEYLEN);
|
||||
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, label, strlen(label));
|
||||
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, hkdf_salt, sizeof(hkdf_salt));
|
||||
*p = OSSL_PARAM_construct_end();
|
||||
if (EVP_KDF_derive(kctx, out, NCRYPT_X25519_KEYLEN, params) <= 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
EVP_KDF_CTX_free(kctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ncrypt_init(struct ncrypt * const nc,
|
||||
unsigned char local_priv_key[NCRYPT_X25519_KEYLEN],
|
||||
unsigned char remote_pub_key[NCRYPT_X25519_KEYLEN])
|
||||
{
|
||||
int rv = 0;
|
||||
EVP_PKEY_CTX * key_ctx = NULL;
|
||||
size_t pub_key_datalen = 0;
|
||||
size_t secret_len = 0;
|
||||
struct
|
||||
{
|
||||
unsigned char pub_key[NCRYPT_X25519_KEYLEN];
|
||||
unsigned char uni_dist_shared_key[NCRYPT_X25519_KEYLEN];
|
||||
} local = {.pub_key = {}, .uni_dist_shared_key = {}};
|
||||
struct
|
||||
{
|
||||
EVP_PKEY * pub_key;
|
||||
} remote = {.pub_key = NULL};
|
||||
|
||||
if (nc->libctx != NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
nc->libctx = OSSL_LIB_CTX_new();
|
||||
if (nc->libctx == NULL)
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
|
||||
nc->private_key =
|
||||
EVP_PKEY_new_raw_private_key_ex(nc->libctx, "X25519", nc->propq, local_priv_key, NCRYPT_X25519_KEYLEN);
|
||||
if (nc->private_key == NULL)
|
||||
{
|
||||
return -3;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_get_octet_string_param(
|
||||
nc->private_key, OSSL_PKEY_PARAM_PUB_KEY, local.pub_key, sizeof(local.pub_key), &pub_key_datalen) == 0)
|
||||
{
|
||||
rv = -4;
|
||||
goto error;
|
||||
}
|
||||
if (pub_key_datalen != NCRYPT_X25519_KEYLEN)
|
||||
{
|
||||
rv = -5;
|
||||
goto error;
|
||||
}
|
||||
|
||||
remote.pub_key =
|
||||
EVP_PKEY_new_raw_public_key_ex(nc->libctx, "X25519", nc->propq, remote_pub_key, NCRYPT_X25519_KEYLEN);
|
||||
if (remote.pub_key == NULL)
|
||||
{
|
||||
rv = -6;
|
||||
goto error;
|
||||
}
|
||||
|
||||
key_ctx = EVP_PKEY_CTX_new_from_pkey(nc->libctx, nc->private_key, nc->propq);
|
||||
if (key_ctx == NULL)
|
||||
{
|
||||
rv = -7;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_derive_init(key_ctx) == 0)
|
||||
{
|
||||
rv = -8;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_derive_set_peer(key_ctx, remote.pub_key) == 0)
|
||||
{
|
||||
rv = -9;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_derive(key_ctx, NULL, &secret_len) == 0)
|
||||
{
|
||||
rv = -10;
|
||||
goto error;
|
||||
}
|
||||
if (secret_len != NCRYPT_X25519_KEYLEN)
|
||||
{
|
||||
rv = -11;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_derive(key_ctx, nc->shared_secret, &secret_len) == 0)
|
||||
{
|
||||
rv = -12;
|
||||
OPENSSL_cleanse(nc->shared_secret, NCRYPT_X25519_KEYLEN);
|
||||
goto error;
|
||||
}
|
||||
if (hkdf_x25519(nc->shared_secret, "ncrypt initial keygen", local.uni_dist_shared_key) != 0)
|
||||
{
|
||||
rv = -13;
|
||||
OPENSSL_cleanse(nc->shared_secret, NCRYPT_X25519_KEYLEN);
|
||||
goto error;
|
||||
}
|
||||
|
||||
memcpy(nc->shared_secret, local.uni_dist_shared_key, NCRYPT_X25519_KEYLEN);
|
||||
OPENSSL_cleanse(local_priv_key, NCRYPT_X25519_KEYLEN);
|
||||
OPENSSL_cleanse(remote_pub_key, NCRYPT_X25519_KEYLEN);
|
||||
|
||||
error:
|
||||
EVP_PKEY_CTX_free(key_ctx);
|
||||
EVP_PKEY_free(remote.pub_key);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int ncrypt_init_encrypt(struct ncrypt * const nc, struct aes * const aes)
|
||||
{
|
||||
aes->ctx = EVP_CIPHER_CTX_new();
|
||||
if (aes->ctx == NULL)
|
||||
{
|
||||
return -3;
|
||||
}
|
||||
|
||||
if (EVP_EncryptInit_ex(aes->ctx, EVP_aes_256_gcm(), NULL, NULL, NULL) == 0)
|
||||
{
|
||||
return -4;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ncrypt_init_decrypt2(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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
HASH_ITER(hh, nc->peers, current_peer, ctmp)
|
||||
{
|
||||
ncrypt_free_aes(¤t_peer->aes);
|
||||
HASH_DEL(nc->peers, current_peer);
|
||||
free(current_peer);
|
||||
}
|
||||
}
|
||||
|
||||
void ncrypt_free(struct ncrypt * const nc)
|
||||
{
|
||||
EVP_PKEY_free(nc->private_key);
|
||||
nc->private_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 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],
|
||||
unsigned char const aad[NCRYPT_AAD_SIZE])
|
||||
{
|
||||
int encrypted_used;
|
||||
int remaining;
|
||||
|
||||
if (EVP_EncryptInit_ex(aes->ctx, NULL, NULL, NULL, iv) == 0)
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (EVP_EncryptUpdate(aes->ctx, NULL, &encrypted_used, aad, NCRYPT_AAD_SIZE) == 0)
|
||||
{
|
||||
return -3;
|
||||
}
|
||||
|
||||
if (EVP_EncryptUpdate(aes->ctx, encrypted, &encrypted_used, (const unsigned char *)plaintext, plaintext_size) == 0)
|
||||
{
|
||||
return -4;
|
||||
}
|
||||
|
||||
if (EVP_EncryptFinal_ex(aes->ctx, encrypted + encrypted_used, &remaining) == 0)
|
||||
{
|
||||
return -5;
|
||||
}
|
||||
|
||||
if (EVP_CIPHER_CTX_ctrl(aes->ctx, EVP_CTRL_GCM_GET_TAG, NCRYPT_TAG_SIZE, tag) == 0)
|
||||
{
|
||||
return -6;
|
||||
}
|
||||
|
||||
return encrypted_used + remaining;
|
||||
}
|
||||
|
||||
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])
|
||||
{
|
||||
unsigned char const aad[NCRYPT_AAD_SIZE] = {0x00, 0x00, 0x00}; // garbage
|
||||
|
||||
if (plaintext_size > NCRYPT_BUFFER_SIZE)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return encrypt(aes, plaintext, plaintext_size, iv, encrypted, tag, aad);
|
||||
}
|
||||
|
||||
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],
|
||||
unsigned char const aad[NCRYPT_AAD_SIZE])
|
||||
{
|
||||
int decrypted_used;
|
||||
int remaining;
|
||||
|
||||
if (EVP_DecryptInit_ex(aes->ctx, NULL, NULL, NULL, iv) == 0)
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (EVP_DecryptUpdate(aes->ctx, NULL, &decrypted_used, aad, NCRYPT_AAD_SIZE) == 0)
|
||||
{
|
||||
return -3;
|
||||
}
|
||||
|
||||
if (EVP_DecryptUpdate(aes->ctx, (unsigned char *)plaintext, &decrypted_used, encrypted, encrypt_size) == 0)
|
||||
{
|
||||
return -4;
|
||||
}
|
||||
|
||||
if (EVP_CIPHER_CTX_ctrl(aes->ctx, EVP_CTRL_GCM_SET_TAG, NCRYPT_TAG_SIZE, tag) == 0)
|
||||
{
|
||||
return -5;
|
||||
}
|
||||
|
||||
if (EVP_DecryptFinal_ex(aes->ctx, (unsigned char *)plaintext + decrypted_used, &remaining) == 0)
|
||||
{
|
||||
return -6;
|
||||
}
|
||||
|
||||
return decrypted_used + remaining;
|
||||
}
|
||||
|
||||
int ncrypt_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])
|
||||
{
|
||||
unsigned char const aad[NCRYPT_AAD_SIZE] = {0x00, 0x00, 0x00}; // garbage
|
||||
|
||||
if (encrypt_size > NCRYPT_BUFFER_SIZE)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return decrypt(aes, encrypted, encrypt_size, iv, tag, plaintext, aad);
|
||||
}
|
||||
|
||||
static size_t keyex_packet()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t json_packet(struct peer * const current_peer,
|
||||
union packet * const pkt,
|
||||
char const * const plaintext,
|
||||
size_t plaintext_size)
|
||||
{
|
||||
pkt->aad.type = PACKET_TYPE_JSON;
|
||||
pkt->aad.size = htons(NCRYPT_AES_IVLEN + NCRYPT_TAG_SIZE + plaintext_size);
|
||||
|
||||
int encrypted_used = encrypt(
|
||||
¤t_peer->aes, plaintext, plaintext_size, current_peer->iv, pkt->json.data, pkt->json.tag, pkt->aad.raw);
|
||||
if (encrypted_used < 0 || encrypted_used > (int)NCRYPT_BUFFER_SIZE)
|
||||
{
|
||||
current_peer->crypto_errors++;
|
||||
return 0;
|
||||
}
|
||||
encrypted_used += PACKET_JSON_OVERHEAD;
|
||||
|
||||
current_peer->cryptions++;
|
||||
memcpy(pkt->json.iv, current_peer->iv, NCRYPT_AES_IVLEN);
|
||||
next_iv(current_peer);
|
||||
|
||||
return encrypted_used;
|
||||
}
|
||||
|
||||
int ncrypt_dgram_send(struct ncrypt * const nc, int fd, char const * const plaintext, size_t plaintext_size)
|
||||
{
|
||||
if (plaintext_size > NCRYPT_BUFFER_SIZE)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int retval = 0;
|
||||
struct peer * current_peer;
|
||||
struct peer * tmp_peer;
|
||||
union packet encrypted;
|
||||
HASH_ITER(hh, nc->peers, current_peer, tmp_peer)
|
||||
{
|
||||
ssize_t used;
|
||||
|
||||
if (current_peer->ephemeral.current_private_key != NULL)
|
||||
{
|
||||
used = keyex_packet();
|
||||
if (used == 0)
|
||||
{
|
||||
retval++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
used = json_packet(current_peer, &encrypted, plaintext, plaintext_size);
|
||||
if (used == 0)
|
||||
{
|
||||
retval++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ssize_t bytes_written =
|
||||
sendto(fd, encrypted.raw, used, 0, ¤t_peer->address.raw, current_peer->address.size);
|
||||
if (bytes_written < 0)
|
||||
{
|
||||
current_peer->send_errors++;
|
||||
retval++;
|
||||
continue;
|
||||
}
|
||||
if (bytes_written != used)
|
||||
{
|
||||
current_peer->partial_writes++;
|
||||
retval++;
|
||||
continue;
|
||||
return NCRYPT_NOT_INITIALIZED;
|
||||
}
|
||||
SSL_set_fd(ent->ssl, connect_fd);
|
||||
SSL_set_connect_state(ent->ssl);
|
||||
}
|
||||
|
||||
return retval;
|
||||
int rv = SSL_do_handshake(ent->ssl);
|
||||
if (rv != 1)
|
||||
{
|
||||
int err = SSL_get_error(ent->ssl, rv);
|
||||
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
|
||||
return NCRYPT_SUCCESS;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
return NCRYPT_SUCCESS;
|
||||
}
|
||||
|
||||
static size_t check_packet_size(union packet const * const pkt, size_t bytes_read)
|
||||
int ncrypt_on_accept(struct ncrypt_ctx * const ctx, int accept_fd, struct ncrypt_entity * const ent)
|
||||
{
|
||||
size_t payload_size = 0;
|
||||
|
||||
if (bytes_read < NCRYPT_AAD_SIZE)
|
||||
if (ent->ssl == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
payload_size = ntohs(pkt->aad.size);
|
||||
switch (pkt->aad.type)
|
||||
{
|
||||
case PACKET_TYPE_KEYEX:
|
||||
if (payload_size != sizeof(pkt->keyex))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case PACKET_TYPE_JSON:
|
||||
if (payload_size < NCRYPT_AES_IVLEN + NCRYPT_TAG_SIZE + 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
if (bytes_read != NCRYPT_AAD_SIZE + payload_size)
|
||||
{
|
||||
return 0;
|
||||
ent->ssl = SSL_new(ctx->ssl_ctx);
|
||||
if (ent->ssl == NULL)
|
||||
{
|
||||
return NCRYPT_NOT_INITIALIZED;
|
||||
}
|
||||
SSL_set_fd(ent->ssl, accept_fd);
|
||||
SSL_set_accept_state(ent->ssl);
|
||||
}
|
||||
|
||||
return payload_size;
|
||||
int rv = SSL_accept(ent->ssl);
|
||||
if (rv != 1)
|
||||
{
|
||||
int err = SSL_get_error(ent->ssl, rv);
|
||||
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
|
||||
return NCRYPT_SUCCESS;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
return NCRYPT_SUCCESS;
|
||||
}
|
||||
|
||||
int ncrypt_dgram_recv(struct ncrypt * const nc, int fd, char * const plaintext, size_t plaintext_size)
|
||||
void ncrypt_free_entity(struct ncrypt_entity * const ent)
|
||||
{
|
||||
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 -2;
|
||||
}
|
||||
size_t payload_size = check_packet_size(&encrypted, bytes_read);
|
||||
if (payload_size == 0)
|
||||
{
|
||||
return -3;
|
||||
}
|
||||
if (plaintext_size < payload_size)
|
||||
{
|
||||
return -4;
|
||||
}
|
||||
|
||||
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);
|
||||
if (peer == NULL)
|
||||
{
|
||||
return -6;
|
||||
}
|
||||
ncrypt_init_decrypt(nc, &peer->aes);
|
||||
}
|
||||
|
||||
if (memcmp(peer->iv, encrypted.json.iv, NCRYPT_AES_IVLEN) != 0)
|
||||
{
|
||||
peer->iv_mismatches++;
|
||||
memcpy(peer->iv, encrypted.json.iv, NCRYPT_AES_IVLEN);
|
||||
}
|
||||
int decrypted_used = decrypt(&peer->aes,
|
||||
encrypted.json.data,
|
||||
payload_size - NCRYPT_AES_IVLEN - NCRYPT_TAG_SIZE,
|
||||
peer->iv,
|
||||
encrypted.json.tag,
|
||||
plaintext,
|
||||
encrypted.aad.raw);
|
||||
next_iv(peer);
|
||||
|
||||
if (decrypted_used < 0)
|
||||
{
|
||||
return -7;
|
||||
}
|
||||
peer->cryptions++;
|
||||
|
||||
return 0;
|
||||
SSL_free(ent->ssl);
|
||||
ent->ssl = NULL;
|
||||
}
|
||||
|
||||
void ncrypt_free_ctx(struct ncrypt_ctx * const ctx)
|
||||
{
|
||||
SSL_CTX_free(ctx->ssl_ctx);
|
||||
ctx->ssl_ctx = NULL;
|
||||
EVP_cleanup();
|
||||
}
|
||||
|
|
|
|||
107
ncrypt.h
107
ncrypt.h
|
|
@ -1,93 +1,56 @@
|
|||
#ifndef NCRYPT_H
|
||||
#define NCRYPT_H 1
|
||||
|
||||
#include <stdlib.h>
|
||||
#define ncrypt_ctx_init(x) \
|
||||
do \
|
||||
{ \
|
||||
(x)->ssl_ctx = NULL; \
|
||||
} while (0);
|
||||
#define ncrypt_entity_init(x) \
|
||||
do \
|
||||
{ \
|
||||
(x)->ssl = NULL; \
|
||||
(x)->handshake_done = 0; \
|
||||
} while (0);
|
||||
|
||||
#include "config.h"
|
||||
#include "nDPIsrvd.h"
|
||||
|
||||
#define NCRYPT_X25519_KEYLEN 32
|
||||
#define NCRYPT_AES_IVLEN 12
|
||||
#define NCRYPT_TAG_SIZE 16
|
||||
#define NCRYPT_AAD_SIZE 3 // packet type + packet size
|
||||
#define NCRYPT_BUFFER_SIZE NETWORK_BUFFER_MAX_SIZE
|
||||
|
||||
struct aes
|
||||
enum
|
||||
{
|
||||
void * ctx;
|
||||
NCRYPT_SUCCESS = 0,
|
||||
NCRYPT_NOT_INITIALIZED = -1,
|
||||
NCRYPT_ALREADY_INITIALIZED = -2,
|
||||
NCRYPT_NULL_PTR = -3,
|
||||
NCRYPT_PEM_LOAD_FAILED = -4,
|
||||
};
|
||||
|
||||
struct peer
|
||||
struct ncrypt_ctx
|
||||
{
|
||||
nDPIsrvd_hashkey hash_key;
|
||||
struct nDPIsrvd_address address;
|
||||
unsigned char iv[NCRYPT_AES_IVLEN];
|
||||
struct
|
||||
{
|
||||
void * last_private_key;
|
||||
void * current_private_key;
|
||||
} ephemeral;
|
||||
size_t key_rotations;
|
||||
size_t cryptions;
|
||||
size_t crypto_errors;
|
||||
size_t iv_mismatches;
|
||||
size_t send_errors;
|
||||
size_t partial_writes;
|
||||
struct aes aes;
|
||||
UT_hash_handle hh;
|
||||
void * ssl_ctx;
|
||||
};
|
||||
|
||||
struct ncrypt
|
||||
struct ncrypt_entity
|
||||
{
|
||||
void * libctx;
|
||||
const char * propq;
|
||||
void * private_key;
|
||||
unsigned char shared_secret[NCRYPT_X25519_KEYLEN];
|
||||
struct peer * peers;
|
||||
void * ssl;
|
||||
int handshake_done;
|
||||
};
|
||||
|
||||
int ncrypt_keygen(unsigned char priv_key[NCRYPT_X25519_KEYLEN], unsigned char pub_key[NCRYPT_X25519_KEYLEN]);
|
||||
int ncrypt_init(void);
|
||||
|
||||
int ncrypt_load_privkey(char const * const private_key_file, unsigned char priv_key[NCRYPT_X25519_KEYLEN]);
|
||||
int ncrypt_init_client(struct ncrypt_ctx * const ctx,
|
||||
char const * const ca_path,
|
||||
char const * const privkey_pem_path,
|
||||
char const * const pubkey_pem_path);
|
||||
|
||||
int ncrypt_load_pubkey(char const * const public_key_file, unsigned char pub_key[NCRYPT_X25519_KEYLEN]);
|
||||
int ncrypt_init_server(struct ncrypt_ctx * const ctx,
|
||||
char const * const ca_path,
|
||||
char const * const privkey_pem_path,
|
||||
char const * const pubkey_pem_path);
|
||||
|
||||
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_on_connect(struct ncrypt_ctx * const ctx, int connect_fd, struct ncrypt_entity * const ent);
|
||||
|
||||
int ncrypt_init_encrypt(struct ncrypt * const nc, struct aes * const aes);
|
||||
int ncrypt_on_accept(struct ncrypt_ctx * const ctx, int accept_fd, struct ncrypt_entity * const ent);
|
||||
|
||||
int ncrypt_init_encrypt2(struct ncrypt * const nc, struct nDPIsrvd_address * const peer_address);
|
||||
void ncrypt_free_entity(struct ncrypt_entity * const ent);
|
||||
|
||||
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_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 aes * const aes,
|
||||
unsigned char const * const encrypted,
|
||||
size_t encrypted_size,
|
||||
unsigned char const iv[NCRYPT_AES_IVLEN],
|
||||
unsigned char tag[NCRYPT_TAG_SIZE],
|
||||
char plaintext[NCRYPT_BUFFER_SIZE]);
|
||||
|
||||
int ncrypt_dgram_send(struct ncrypt * const nc, int fd, char const * const plaintext, size_t plaintext_size);
|
||||
|
||||
int ncrypt_dgram_recv(struct ncrypt * const nc, int fd, char * const plaintext, size_t plaintext_size);
|
||||
void ncrypt_free_ctx(struct ncrypt_ctx * const ctx);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
70
scripts/gen-cacerts.sh
Executable file
70
scripts/gen-cacerts.sh
Executable file
|
|
@ -0,0 +1,70 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
printf 'usage: %s [out-dir] [client-cname] [server-cname]\n' "${0}"
|
||||
|
||||
if [ -z "${1}" ]; then
|
||||
OUT_DIR="$(dirname ${0})/pki"
|
||||
else
|
||||
OUT_DIR="${1}"
|
||||
fi
|
||||
|
||||
if [ -z "${2}" ]; then
|
||||
CLIENT_CN="unknown"
|
||||
else
|
||||
CLIENT_CN="${2}"
|
||||
fi
|
||||
|
||||
if [ -z "${3}" ]; then
|
||||
SERVER_CN="unknown"
|
||||
else
|
||||
SERVER_CN="${3}"
|
||||
fi
|
||||
|
||||
printf 'PKI Directory: %s\n' "${OUT_DIR}"
|
||||
printf 'Client CName.: %s\n' "${CLIENT_CN}"
|
||||
printf 'Server CName.: %s\n' "${SERVER_CN}"
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
OLDPWD="$(pwd)"
|
||||
mkdir -p "${OUT_DIR}"
|
||||
cd "${OUT_DIR}"
|
||||
|
||||
if [ ! -r ./ca.key -o ! -r ./ca.crt ]; then
|
||||
printf '%s\n' '[*] Create CA...'
|
||||
openssl genrsa -out ./ca.key 4096
|
||||
openssl req -x509 -new -nodes -key ./ca.key -sha256 -days 3650 -out ./ca.crt -subj "/CN=nDPId Root CA"
|
||||
fi
|
||||
|
||||
if [ ! -r ./server_${SERVER_CN}.key -o ! -r ./server_${SERVER_CN}.crt ]; then
|
||||
printf '[*] Create Server Cert: %s\n' "${SERVER_CN}"
|
||||
openssl genrsa -out ./server_${SERVER_CN}.key 2048
|
||||
openssl req -new -key ./server_${SERVER_CN}.key -out ./server_${SERVER_CN}.csr -subj "/CN=${SERVER_CN}"
|
||||
openssl x509 -req -in ./server_${SERVER_CN}.csr -CA ./ca.crt -CAkey ./ca.key -CAcreateserial \
|
||||
-out ./server_${SERVER_CN}.crt -days 825 -sha256
|
||||
fi
|
||||
|
||||
if [ ! -r ./client_${CLIENT_CN}.key -o ! -r ./client_${CLIENT_CN}.crt ]; then
|
||||
printf '[*] Create Client Cert: %s\n' "${CLIENT_CN}"
|
||||
openssl genrsa -out ./client_${CLIENT_CN}.key 2048
|
||||
openssl req -new -key ./client_${CLIENT_CN}.key -out ./client_${CLIENT_CN}.csr -subj "/CN=${CLIENT_CN}"
|
||||
openssl x509 -req -in ./client_${CLIENT_CN}.csr -CA ./ca.crt -CAkey ./ca.key -CAcreateserial \
|
||||
-out ./client_${CLIENT_CN}.crt -days 825 -sha256
|
||||
fi
|
||||
|
||||
printf '%s\n' '[*] Done'
|
||||
|
||||
cd "${OLDPWD}"
|
||||
|
||||
set +x
|
||||
|
||||
printf '%s\n' 'To test the certs you may use OpenSSL and start a client/server with:'
|
||||
printf 'openssl s_server -accept %s -cert %s -key %s -CAfile %s -Verify 1 -verify_return_error -tls1_3\n' \
|
||||
"7777" \
|
||||
"${OUT_DIR}/server_${SERVER_CN}.crt" "${OUT_DIR}/server_${SERVER_CN}.key" \
|
||||
"${OUT_DIR}/ca.crt"
|
||||
printf 'openssl s_client -connect 127.0.0.1:%s -cert %s -key %s -CAfile %s -verify_return_error -tls1_3\n' \
|
||||
"7777" \
|
||||
"${OUT_DIR}/client_${CLIENT_CN}.crt" "${OUT_DIR}/client_${CLIENT_CN}.key" \
|
||||
"${OUT_DIR}/ca.crt"
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
printf 'usage: %s [out-pem-private-key-file] [out-pem-public-key-file]\n' "${0}"
|
||||
|
||||
if [ -z "${1}" ]; then
|
||||
PRIV_KEY="./nDPId-x25519-priv.pem"
|
||||
else
|
||||
PRIV_KEY="${1}"
|
||||
fi
|
||||
|
||||
if [ -z "${2}" ]; then
|
||||
PUB_KEY="./nDPId-x25519-pub.pem"
|
||||
else
|
||||
PUB_KEY="${2}"
|
||||
fi
|
||||
|
||||
printf 'Private Key: %s\n' "${PRIV_KEY}"
|
||||
printf 'Public Key.: %s\n' "${PUB_KEY}"
|
||||
|
||||
openssl genpkey -algorithm x25519 -out "${PRIV_KEY}"
|
||||
openssl pkey -in "${PRIV_KEY}" -outform PEM -pubout -out "${PUB_KEY}"
|
||||
Loading…
Add table
Add a link
Reference in a new issue