diff --git a/include/cc20.h b/include/cc20.h new file mode 100644 index 0000000..1bc52eb --- /dev/null +++ b/include/cc20.h @@ -0,0 +1,57 @@ +/** + * (C) 2007-20 - ntop.org and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not see see + * + */ + + +#ifndef CC20_H +#define CC20_H + +#include "n2n.h" // HAVE_OPENSSL_1_1, traceEvent ... + + +#ifdef HAVE_OPENSSL_1_1 + + +#include +#include +#include + +#define CC20_IV_SIZE 16 +#define CC20_KEY_BYTES (256/8) + + +typedef struct cc20_context_t { + EVP_CIPHER_CTX *ctx; /* openssl's reusable evp_* en/de-cryption context */ + const EVP_CIPHER *cipher; /* cipher to use: e.g. EVP_chacha20() */ + uint8_t key[CC20_KEY_BYTES]; /* the pure key data for payload encryption & decryption */ +} cc20_context_t; + + +int cc20_crypt (unsigned char *out, const unsigned char *in, size_t in_len, + const unsigned char *iv, cc20_context_t *ctx); + + +int cc20_init (const unsigned char *key, cc20_context_t **ctx); + + +int cc20_deinit (cc20_context_t *ctx); + + +#endif // HAVE_OPENSSL_1_1 + + +#endif // CC20_H diff --git a/include/n2n.h b/include/n2n.h index dc08248..74b8798 100644 --- a/include/n2n.h +++ b/include/n2n.h @@ -160,6 +160,7 @@ typedef struct ether_hdr ether_hdr_t; #include "pearson.h" #include "portable_endian.h" #include "aes.h" +#include "cc20.h" #include "speck.h" #include "n2n_regex.h" diff --git a/src/cc20.c b/src/cc20.c new file mode 100644 index 0000000..87bb5e1 --- /dev/null +++ b/src/cc20.c @@ -0,0 +1,110 @@ +/** + * (C) 2007-20 - ntop.org and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not see see + * + */ + + +#include "cc20.h" + + +#ifdef HAVE_OPENSSL_1_1 + + +/* ****************************************************** */ + +/* get any erorr message out of openssl + taken from https://en.wikibooks.org/wiki/OpenSSL/Error_handling */ +static char *openssl_err_as_string (void) { + BIO *bio = BIO_new (BIO_s_mem ()); + ERR_print_errors (bio); + char *buf = NULL; + size_t len = BIO_get_mem_data (bio, &buf); + char *ret = (char *) calloc (1, 1 + len); + + if(ret) + memcpy (ret, buf, len); + + BIO_free (bio); + return ret; +} + +/* ****************************************************** */ + +// encryption == decryption +int cc20_crypt (unsigned char *out, const unsigned char *in, size_t in_len, + const unsigned char *iv, cc20_context_t *ctx) { + + int evp_len; + int evp_ciphertext_len; + + if(1 == EVP_EncryptInit_ex(ctx->ctx, ctx->cipher, NULL, ctx->key, iv)) { + if(1 == EVP_CIPHER_CTX_set_padding(ctx->ctx, 0)) { + if(1 == EVP_EncryptUpdate(ctx->ctx, out, &evp_len, in, in_len)) { + evp_ciphertext_len = evp_len; + if(1 == EVP_EncryptFinal_ex(ctx->ctx, out + evp_len, &evp_len)) { + evp_ciphertext_len += evp_len; + if(evp_ciphertext_len != in_len) + traceEvent(TRACE_ERROR, "cc20_crypt openssl encryption: encrypted %u bytes where %u were expected", + evp_ciphertext_len, in_len); + } else + traceEvent(TRACE_ERROR, "cc20_crypt openssl final encryption: %s", + openssl_err_as_string()); + } else + traceEvent(TRACE_ERROR, "cc20_encrypt openssl encrpytion: %s", + openssl_err_as_string()); + } else + traceEvent(TRACE_ERROR, "cc20_encrypt openssl padding setup: %s", + openssl_err_as_string()); + } else + traceEvent(TRACE_ERROR, "cc20_encrypt openssl init: %s", + openssl_err_as_string()); + + EVP_CIPHER_CTX_reset(ctx->ctx); + + return 0; +} + + +int cc20_init (const unsigned char *key, cc20_context_t **ctx) { + + // allocate context... + *ctx = (cc20_context_t*) calloc(1, sizeof(cc20_context_t)); + if (!(*ctx)) + return -1; + + if(!((*ctx)->ctx = EVP_CIPHER_CTX_new())) { + traceEvent(TRACE_ERROR, "cc20_init openssl's evp_* encryption context creation failed: %s", + openssl_err_as_string()); + return -1; + } + + (*ctx)->cipher = EVP_chacha20(); + + memcpy((*ctx)->key, key, CC20_KEY_BYTES); + + return 0; +} + + +int cc20_deinit (cc20_context_t *ctx) { + + if (ctx->ctx) EVP_CIPHER_CTX_free(ctx->ctx); + + return 0; +} + + +#endif // HAVE_OPENSSL_1_1 diff --git a/src/transform_cc20.c b/src/transform_cc20.c index 0665c3b..d3c42f3 100644 --- a/src/transform_cc20.c +++ b/src/transform_cc20.c @@ -20,33 +20,21 @@ #ifdef HAVE_OPENSSL_1_1 -#include -#include -#include - -#define N2N_CC20_IVEC_SIZE 16 - -#define CC20_KEY_BYTES (256/8) - /* ChaCha20 plaintext preamble */ -#define TRANSOP_CC20_PREAMBLE_SIZE (N2N_CC20_IVEC_SIZE) - -typedef unsigned char n2n_cc20_ivec_t[N2N_CC20_IVEC_SIZE]; +#define CC20_PREAMBLE_SIZE (CC20_IV_SIZE) typedef struct transop_cc20 { - EVP_CIPHER_CTX *enc_ctx; /* openssl's reusable evp_* encryption context */ - EVP_CIPHER_CTX *dec_ctx; /* openssl's reusable evp_* decryption context */ - const EVP_CIPHER *cipher; /* cipher to use: EVP_chacha20() */ - uint8_t key[32]; /* the pure key data for payload encryption & decryption */ + cc20_context_t *ctx; } transop_cc20_t; /* ****************************************************** */ static int transop_deinit_cc20(n2n_trans_op_t *arg) { + transop_cc20_t *priv = (transop_cc20_t *)arg->priv; - EVP_CIPHER_CTX_free(priv->enc_ctx); - EVP_CIPHER_CTX_free(priv->dec_ctx); + if(priv->ctx) + cc20_deinit(priv->ctx); if(priv) free(priv); @@ -56,35 +44,6 @@ static int transop_deinit_cc20(n2n_trans_op_t *arg) { /* ****************************************************** */ -/* get any erorr message out of openssl - taken from https://en.wikibooks.org/wiki/OpenSSL/Error_handling */ -static char *openssl_err_as_string (void) { - BIO *bio = BIO_new (BIO_s_mem ()); - ERR_print_errors (bio); - char *buf = NULL; - size_t len = BIO_get_mem_data (bio, &buf); - char *ret = (char *) calloc (1, 1 + len); - - if(ret) - memcpy (ret, buf, len); - - BIO_free (bio); - return ret; -} - -/* ****************************************************** */ - -static void set_cc20_iv(transop_cc20_t *priv, n2n_cc20_ivec_t ivec) { - // keep in mind the following condition: N2N_CC20_IVEC_SIZE % sizeof(rand_value) == 0 ! - uint64_t rand_value; - for (uint8_t i = 0; i < N2N_CC20_IVEC_SIZE; i += sizeof(rand_value)) { - rand_value = n2n_rand(); - memcpy(ivec + i, &rand_value, sizeof(rand_value)); - } -} - -/* ****************************************************** */ - /** The ChaCha20 packet format consists of: * * - a 128-bit random IV @@ -99,56 +58,30 @@ static int transop_encode_cc20(n2n_trans_op_t * arg, const uint8_t * inbuf, size_t in_len, const uint8_t * peer_mac) { + int len=-1; transop_cc20_t * priv = (transop_cc20_t *)arg->priv; uint8_t assembly[N2N_PKT_BUF_SIZE] = {0}; if(in_len <= N2N_PKT_BUF_SIZE) { - if((in_len + TRANSOP_CC20_PREAMBLE_SIZE) <= out_len) { + if((in_len + CC20_PREAMBLE_SIZE) <= out_len) { + size_t idx=0; - n2n_cc20_ivec_t enc_ivec = {0}; traceEvent(TRACE_DEBUG, "encode_cc20 %lu bytes", in_len); - /* Generate and encode the IV. */ - set_cc20_iv(priv, enc_ivec); - encode_buf(outbuf, &idx, &enc_ivec, N2N_CC20_IVEC_SIZE); + // full IV sized random value (128 bit) + encode_uint64(outbuf, &idx, n2n_rand()); + encode_uint64(outbuf, &idx, n2n_rand()); - /* Encrypt the assembly contents and write the ciphertext after the iv. */ - /* len is set to the length of the cipher plain text to be encrpyted - which is (in this case) identical to original packet lentgh */ len = in_len; + cc20_crypt(outbuf + CC20_PREAMBLE_SIZE, + inbuf, + in_len, + outbuf, // IV + priv->ctx); - /* The assembly buffer is a source for encrypting data. - * The whole contents of assembly are encrypted. */ - memcpy(assembly, inbuf, in_len); - - EVP_CIPHER_CTX *ctx = priv->enc_ctx; - int evp_len; - int evp_ciphertext_len; - - if(1 == EVP_EncryptInit_ex(ctx, priv->cipher, NULL, priv->key, enc_ivec)) { - if(1 == EVP_CIPHER_CTX_set_padding(ctx, 0)) { - if(1 == EVP_EncryptUpdate(ctx, outbuf + TRANSOP_CC20_PREAMBLE_SIZE, &evp_len, assembly, len)) { - evp_ciphertext_len = evp_len; - if(1 == EVP_EncryptFinal_ex(ctx, outbuf + TRANSOP_CC20_PREAMBLE_SIZE + evp_len, &evp_len)) { - evp_ciphertext_len += evp_len; - - if(evp_ciphertext_len != len) - traceEvent(TRACE_ERROR, "encode_cc20 openssl encryption: encrypted %u bytes where %u were expected.\n", - evp_ciphertext_len, len); - } else - traceEvent(TRACE_ERROR, "encode_cc20 openssl final encryption: %s\n", openssl_err_as_string()); - } else - traceEvent(TRACE_ERROR, "encode_cc20 openssl encrpytion: %s\n", openssl_err_as_string()); - } else - traceEvent(TRACE_ERROR, "encode_cc20 openssl padding setup: %s\n", openssl_err_as_string()); - } else - traceEvent(TRACE_ERROR, "encode_cc20 openssl init: %s\n", openssl_err_as_string()); - - EVP_CIPHER_CTX_reset(ctx); - - len += TRANSOP_CC20_PREAMBLE_SIZE; /* size of data carried in UDP. */ + len += CC20_PREAMBLE_SIZE; /* size of data carried in UDP. */ } else traceEvent(TRACE_ERROR, "encode_cc20 outbuf too small."); } else @@ -166,72 +99,51 @@ static int transop_decode_cc20(n2n_trans_op_t * arg, const uint8_t * inbuf, size_t in_len, const uint8_t * peer_mac) { + int len=0; transop_cc20_t * priv = (transop_cc20_t *)arg->priv; uint8_t assembly[N2N_PKT_BUF_SIZE]; - if(((in_len - TRANSOP_CC20_PREAMBLE_SIZE) <= N2N_PKT_BUF_SIZE) /* Cipher text fits in assembly */ - && (in_len >= TRANSOP_CC20_PREAMBLE_SIZE) /* Has at least iv */ + if(((in_len - CC20_PREAMBLE_SIZE) <= N2N_PKT_BUF_SIZE) /* Cipher text fits in assembly */ + && (in_len >= CC20_PREAMBLE_SIZE) /* Has at least iv */ ) { size_t rem=in_len; size_t idx=0; - n2n_cc20_ivec_t dec_ivec = {0}; - - traceEvent(TRACE_DEBUG, "decode_cc20 %lu bytes", in_len); - len = (in_len - TRANSOP_CC20_PREAMBLE_SIZE); - - /* Get the IV */ - decode_buf((uint8_t *)&dec_ivec, N2N_CC20_IVEC_SIZE, inbuf, &rem, &idx); - - EVP_CIPHER_CTX *ctx = priv->dec_ctx; - int evp_len; - int evp_plaintext_len; - - if(1 == EVP_DecryptInit_ex(ctx, priv->cipher, NULL, priv->key, dec_ivec)) { - if(1 == EVP_CIPHER_CTX_set_padding(ctx, 0)) { - if(1 == EVP_DecryptUpdate(ctx, assembly, &evp_len, inbuf + TRANSOP_CC20_PREAMBLE_SIZE, len)) { - evp_plaintext_len = evp_len; - if(1 == EVP_DecryptFinal_ex(ctx, assembly + evp_len, &evp_len)) { - evp_plaintext_len += evp_len; - - if(evp_plaintext_len != len) - traceEvent(TRACE_ERROR, "decode_cc20 openssl decryption: decrypted %u bytes where %u were expected.\n", - evp_plaintext_len, len); - } else - traceEvent(TRACE_ERROR, "decode_cc20 openssl final decryption: %s\n", openssl_err_as_string()); - } else - traceEvent(TRACE_ERROR, "decode_cc20 openssl decrpytion: %s\n", openssl_err_as_string()); - } else - traceEvent(TRACE_ERROR, "decode_cc20 openssl padding setup: %s\n", openssl_err_as_string()); - } else - traceEvent(TRACE_ERROR, "decode_cc20 openssl init: %s\n", openssl_err_as_string()); - - EVP_CIPHER_CTX_reset(ctx); - - memcpy(outbuf, assembly, len); + + traceEvent(TRACE_DEBUG, "decode_cc20 %lu bytes", in_len); + + len = (in_len - CC20_PREAMBLE_SIZE); + + cc20_crypt(outbuf, + inbuf + CC20_PREAMBLE_SIZE, + in_len, + inbuf, // IV + priv->ctx); + } else - traceEvent(TRACE_ERROR, "decode_cc20 inbuf wrong size (%ul) to decrypt.", in_len); + traceEvent(TRACE_ERROR, "decode_cc20 inbuf wrong size (%ul) to decrypt.", in_len); return len; } /* ****************************************************** */ -static int setup_cc20_key(transop_cc20_t *priv, const uint8_t *key, ssize_t key_size) { - uint8_t key_mat_buf[SHA256_DIGEST_LENGTH]; +static int setup_cc20_key(transop_cc20_t *priv, const uint8_t *password, ssize_t password_len) { - priv->cipher = EVP_chacha20(); + uint8_t key_mat[CC20_KEY_BYTES]; - /* Clear out any old possibly longer key matter. */ - memset(&(priv->key), 0, sizeof(priv->key) ); /* The input key always gets hashed to make a more unpredictable and more complete use of the key space */ - SHA256(key, key_size, key_mat_buf); - memcpy (priv->key, key_mat_buf, SHA256_DIGEST_LENGTH); + pearson_hash_256(key_mat, password, password_len); + + if(cc20_init(key_mat, &(priv->ctx))) { + traceEvent(TRACE_ERROR, "setup_cc20_key setup unsuccessful"); + return -1; + } - traceEvent(TRACE_DEBUG, "ChaCha20 key setup completed\n"); + traceEvent(TRACE_DEBUG, "setup_cc20_key completed"); - return(0); + return 0; } /* ****************************************************** */ @@ -242,6 +154,7 @@ static void transop_tick_cc20(n2n_trans_op_t * arg, time_t now) { ; } /* ChaCha20 initialization function */ int n2n_transop_cc20_init(const n2n_edge_conf_t *conf, n2n_trans_op_t *ttt) { + transop_cc20_t *priv; const u_char *encrypt_key = (const u_char *)conf->encrypt_key; size_t encrypt_key_len = strlen(conf->encrypt_key); @@ -261,19 +174,8 @@ int n2n_transop_cc20_init(const n2n_edge_conf_t *conf, n2n_trans_op_t *ttt) { } ttt->priv = priv; - /* Setup openssl's reusable evp_* contexts for encryption and decryption*/ - if(!(priv->enc_ctx = EVP_CIPHER_CTX_new())) { - traceEvent(TRACE_ERROR, "openssl's evp_* encryption context creation: %s\n", openssl_err_as_string()); - return(-1); - } - - if(!(priv->dec_ctx = EVP_CIPHER_CTX_new())) { - traceEvent(TRACE_ERROR, "openssl's evp_* decryption context creation: %s\n", openssl_err_as_string()); - return(-1); - } - /* Setup the cipher and key */ - return(setup_cc20_key(priv, encrypt_key, encrypt_key_len)); + return setup_cc20_key(priv, encrypt_key, encrypt_key_len); } #endif /* HAVE_OPENSSL_1_1 */