diff --git a/doc/Crypto.md b/doc/Crypto.md index c1d2da7..a66cb29 100644 --- a/doc/Crypto.md +++ b/doc/Crypto.md @@ -17,7 +17,7 @@ The following chart might help to make a quick comparison and decide what cipher | Cipher | Mode | Block Size | Key Size | IV length |Speed | Built-In | Origin | | :---: | :---:| :---: | :---: | :---: |:---: | :---: | --- | -|Twofish | CTS | 128 bits | 256 bit | 32 bit | - | Y | Bruce Schneier | +|Twofish | CTS | 128 bits | 256 bit | 128 bit | -..O | Y | Bruce Schneier | |AES | CBC | 128 bits | 128, 192, 256 bit| 128 bit | O..+ | N | Joan Daemen, Vincent Rijmen, NSA-approved | |ChaCha20| CTR | Stream | 256 bit | 128 bit | +..++| N | Daniel J. Bernstein | |SPECK | CTR | Stream | 256 bit | 128 bit | ++ | Y | NSA | @@ -28,13 +28,11 @@ Note that AES and ChaCha20 are available only if n2n is compiled with openSSL su ### Twofish -This implementation prepends a 32 bit random value to the plain text. In the `src/transform_tf.c` file, it is called `nonce`. In CBC mode, this basically has the same effect as a respectively shorter IV. +This implementation prepends a 128 bit random value to the plain text. Its size is adjustable by changing the `TF_PREAMBLE_SIZE` definition found in `src/transform_tf.c`. It defaults to TF_BLOCK_SIZE (== 16). As CTS uses underlying CBC mode, this basically has the same effect as a respectively shorter IV. Twofish requires no padding as it employs a CBC/CTS scheme which can send out plaintext-length ciphertexts. The scheme however has a small flaw in handling messages shorter than one block, only low-level programmer might encounter this. -Twofish is the slowest of the ciphers present. - -_We might try to find a faster implementation._ +On Intel CPUs, Twofish usually is the slowest of the ciphers present. However, on Raspberry Pi 3B+, Twofish was observed to be faster than AES-CTS. Your mileage may vary. Cipher speed's can be compared running the `tools/n2n-benchmark` tool. ### AES @@ -42,8 +40,6 @@ AES also prepends a random value to the plaintext. Its size is adjustable by cha AES relies on openSSL's `evp_*` interface which also offers hardware acceleration where available (SSE, AES-NI, …). It however is slower than the following stream ciphers because the CBC mode cannot compete with the optimized stream ciphers. -_Perhaps, AES-CTR being a stream cipher could have competed with the stream ciphers._ - ### ChaCha20 ChaCha20 was the first stream cipher supported by n2n. @@ -52,13 +48,13 @@ It also relies on openSSL's `evp_*` interface. It does not use the Poly1305 mess The random full 128-bit IV is transmitted in plain. -ChaCha20 usually performs faster than AES-CBC. +ChaCha20 usually performs faster than AES-CTS. ### SPECK SPECK is recommended by the NSA for offical use in case AES implementation is not feasible due to system constraints (performance, size, …). The block cipher is used in CTR mode making it a stream cipher. The random full 128-bit IV is transmitted in plain. -On Intel CPUs, SPECK performs even faster than openSSL's ChaCha20 as it takes advantage of SSE4 or AVX2 if available (compile using `-march=native`). On Raspberry's ARM CPU, it is second place behind ChaCha20 and before AES-CBC. +On Intel CPUs, SPECK performs even faster than openSSL's ChaCha20 as it takes advantage of SSE4 or AVX2 if available (compile using `-march=native`). On Raspberry's ARM CPU, it is second place behind ChaCha20 and before Twofish. ### Random Numbers diff --git a/include/tf.h b/include/tf.h index f4ea3cc..33eeb6d 100644 --- a/include/tf.h +++ b/include/tf.h @@ -53,6 +53,7 @@ THE SOFTWARE. #include #include #include +#include "portable_endian.h" #define TF_BLOCK_SIZE 16 @@ -78,5 +79,7 @@ int tf_cbc_decrypt (unsigned char *out, const unsigned char *in, size_t in_len, int tf_init (const unsigned char *key, size_t key_size, tf_context_t **ctx); +int tf_deinit (tf_context_t *ctx); + #endif // TF_H diff --git a/src/tf.c b/src/tf.c index b846088..76281fb 100644 --- a/src/tf.c +++ b/src/tf.c @@ -48,7 +48,7 @@ THE SOFTWARE. #include "tf.h" -#include "portable_endian.h" + const uint8_t RS[4][8] = { { 0x01, 0xA4, 0x55, 0x87, 0x5A, 0x58, 0xDB, 0x9E, }, { 0xA4, 0x56, 0x82, 0xF3, 0x1E, 0xC6, 0x68, 0xE5, }, @@ -503,3 +503,11 @@ int tf_init (const unsigned char *key, size_t key_size, tf_context_t **ctx) { return 0; } + + +int tf_deinit (tf_context_t *ctx) { + + if (ctx) free (ctx); + + return 0; +} diff --git a/src/transform_tf.c b/src/transform_tf.c index c9316b8..05d035d 100644 --- a/src/transform_tf.c +++ b/src/transform_tf.c @@ -40,7 +40,7 @@ typedef struct transop_tf { static int transop_deinit_tf(n2n_trans_op_t *arg) { transop_tf_t *priv = (transop_tf_t *)arg->priv; - if(priv->ctx) free(priv->ctx); + if(priv->ctx) tf_deinit(priv->ctx); if(priv) free(priv); @@ -81,12 +81,8 @@ static int transop_encode_tf(n2n_trans_op_t * arg, traceEvent(TRACE_DEBUG, "transop_encode_tf %lu bytes plaintext", in_len); // full block sized random value (128 bit) - // !!! replace with 2 calls to encode_uint64(...) as as available - // !!! which is still under consideration in pull request 'revAes' - encode_uint32(assembly, &idx, n2n_rand()); - encode_uint32(assembly, &idx, n2n_rand()); - encode_uint32(assembly, &idx, n2n_rand()); - encode_uint32(assembly, &idx, n2n_rand()); + encode_uint64(assembly, &idx, n2n_rand()); + encode_uint64(assembly, &idx, n2n_rand()); // adjust for maybe differently chosen TF_PREAMBLE_SIZE idx = TF_PREAMBLE_SIZE; @@ -159,7 +155,7 @@ static int transop_decode_tf(n2n_trans_op_t * arg, tf_cbc_decrypt(assembly, assembly, in_len + TF_BLOCK_SIZE - rest, tf_null_iv, priv->ctx); // check for expected zero padding and give a warning otherwise - if (memcmp(assembly + in_len, tf_null_iv, TF_BLOCK_SIZE - rest)) { + if(memcmp(assembly + in_len, tf_null_iv, TF_BLOCK_SIZE - rest)) { traceEvent(TRACE_WARNING, "transop_decode_tf payload decryption failed with unexpected cipher text stealing padding"); return -1; } @@ -191,7 +187,7 @@ static int setup_tf_key(transop_tf_t *priv, const uint8_t *password, ssize_t pas key_size = 32; // 256 bit // setup the key and have corresponding context created - if (tf_init (key, key_size * 8, &(priv->ctx))) { + if(tf_init(key, key_size * 8, &(priv->ctx))) { traceEvent(TRACE_ERROR, "setup_tf_key %u-bit key setup unsuccessful", key_size * 8); return -1; @@ -210,6 +206,7 @@ static void transop_tick_tf(n2n_trans_op_t * arg, time_t now) { ; } // Twofish initialization function int n2n_transop_tf_init(const n2n_edge_conf_t *conf, n2n_trans_op_t *ttt) { + transop_tf_t *priv; const u_char *encrypt_key = (const u_char *)conf->encrypt_key; size_t encrypt_key_len = strlen(conf->encrypt_key); @@ -225,10 +222,10 @@ int n2n_transop_tf_init(const n2n_edge_conf_t *conf, n2n_trans_op_t *ttt) { priv = (transop_tf_t*) calloc(1, sizeof(transop_tf_t)); if(!priv) { traceEvent(TRACE_ERROR, "n2n_transop_tf_cbc_init cannot allocate transop_tf_t memory"); - return(-1); + return -1; } ttt->priv = priv; // setup the cipher and key - return(setup_tf_key(priv, encrypt_key, encrypt_key_len)); + return setup_tf_key(priv, encrypt_key, encrypt_key_len); }