Browse Source

Merge pull request #395 from Logan007/amndmntTf

twofish transform code clean-up amendment
pull/398/head
Luca Deri 4 years ago
committed by GitHub
parent
commit
3f2401c5ff
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      doc/Crypto.md
  2. 3
      include/tf.h
  3. 10
      src/tf.c
  4. 19
      src/transform_tf.c

14
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

3
include/tf.h

@ -53,6 +53,7 @@ THE SOFTWARE.
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#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

10
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;
}

19
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);
}

Loading…
Cancel
Save