transform_tf.c (n2n-2.8) | : | transform_tf.c (n2n-3.0) | ||
---|---|---|---|---|
/** | /** | |||
* (C) 2007-20 - ntop.org and contributors | * (C) 2007-21 - ntop.org and contributors | |||
* | * | |||
* This program is free software; you can redistribute it and/or modify | * 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 | * it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 3 of the License, or | * the Free Software Foundation; either version 3 of the License, or | |||
* (at your option) any later version. | * (at your option) any later version. | |||
* | * | |||
* This program is distributed in the hope that it will be useful, | * This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | * GNU General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | |||
* along with this program; if not see see <http://www.gnu.org/licenses/> | * along with this program; if not see see <http://www.gnu.org/licenses/> | |||
* | * | |||
*/ | */ | |||
#include "n2n.h" | #include "n2n.h" | |||
#define N2N_TWOFISH_NUM_SA 32 /* space for SAa */ | // size of random value prepended to plaintext defaults to TF_BLOCK_SIZE; | |||
// gradually abandoning security, lower values could be chosen; | ||||
#define N2N_TWOFISH_TRANSFORM_VERSION 1 /* version of the transform encoding | // however, minimum transmission size with cipher text stealing scheme is one | |||
*/ | // block; as network packets should be longer anyway, only low level programmer | |||
// might encounter an issue with lower values here | ||||
#define TF_PREAMBLE_SIZE (TF_BLOCK_SIZE) | ||||
// cbc mode is being used with random value prepended to plaintext | ||||
// instead of iv so, actual iv is tf_null_iv | ||||
const uint8_t tf_null_iv[TF_IV_SIZE] = { 0 }; | ||||
typedef struct transop_tf { | typedef struct transop_tf { | |||
TWOFISH* enc_tf; /* tx state */ | tf_context_t *ctx; | |||
TWOFISH* dec_tf; /* rx state */ | ||||
} transop_tf_t; | } transop_tf_t; | |||
static int transop_deinit_twofish( n2n_trans_op_t * arg ) { | static int transop_deinit_tf (n2n_trans_op_t *arg) { | |||
transop_tf_t *priv = (transop_tf_t *)arg->priv; | ||||
if(priv) { | transop_tf_t *priv = (transop_tf_t *)arg->priv; | |||
TwoFishDestroy(priv->enc_tf); /* deallocate TWOFISH */ | ||||
TwoFishDestroy(priv->dec_tf); /* deallocate TWOFISH */ | ||||
free(priv); | ||||
} | ||||
return 0; | if(priv->ctx) | |||
} | tf_deinit(priv->ctx); | |||
#define TRANSOP_TF_VER_SIZE 1 /* Support minor variants in encoding in | if(priv) | |||
one module. */ | free(priv); | |||
#define TRANSOP_TF_NONCE_SIZE 4 | ||||
#define TRANSOP_TF_SA_SIZE 4 | ||||
/** The twofish packet format consists of: | return 0; | |||
* | } | |||
* - a 8-bit twofish encoding version in clear text | ||||
* - a 32-bit SA number in clear text | // the Twofish packet format consists of | |||
* - ciphertext encrypted from a 32-bit nonce followed by the payload. | // | |||
* | // - a random TF_PREAMBLE_SIZE-sized value prepended to plaintext | |||
* [V|SSSS|nnnnDDDDDDDDDDDDDDDDDDDDD] | // encrypted together with the... | |||
* |<------ encrypted ------>| | // - ... payload data | |||
*/ | // | |||
static int transop_encode_twofish( n2n_trans_op_t * arg, | // [VV|DDDDDDDDDDDDDDDDDDDDD] | |||
uint8_t * outbuf, | // | <---- encrypted ----> | | |||
size_t out_len, | // | |||
const uint8_t * inbuf, | static int transop_encode_tf (n2n_trans_op_t *arg, | |||
size_t in_len, | uint8_t *outbuf, | |||
const uint8_t * peer_mac) | size_t out_len, | |||
{ | const uint8_t *inbuf, | |||
int len=-1; | size_t in_len, | |||
transop_tf_t * priv = (transop_tf_t *)arg->priv; | const uint8_t *peer_mac) { | |||
uint8_t assembly[N2N_PKT_BUF_SIZE]; | ||||
uint32_t * pnonce; | transop_tf_t *priv = (transop_tf_t *)arg->priv; | |||
if ( (in_len + TRANSOP_TF_NONCE_SIZE) <= N2N_PKT_BUF_SIZE ) | // the assembly buffer is a source for encrypting data | |||
{ | // the whole contents of assembly are encrypted | |||
if ( (in_len + TRANSOP_TF_NONCE_SIZE + TRANSOP_TF_SA_SIZE + TRANSOP_TF_VER | uint8_t assembly[N2N_PKT_BUF_SIZE]; | |||
_SIZE) <= out_len ) | size_t idx = 0; | |||
{ | int padded_len; | |||
size_t idx=0; | uint8_t padding; | |||
uint32_t sa_id=0; // Not used | uint8_t buf[TF_BLOCK_SIZE]; | |||
traceEvent(TRACE_DEBUG, "encode_twofish %lu", in_len); | if(in_len <= N2N_PKT_BUF_SIZE) { | |||
if((in_len + TF_PREAMBLE_SIZE + TF_BLOCK_SIZE) <= out_len) { | ||||
/* Encode the twofish format version. */ | traceEvent(TRACE_DEBUG, "transop_encode_tf %lu bytes plaintext", in_ | |||
encode_uint8( outbuf, &idx, N2N_TWOFISH_TRANSFORM_VERSION ); | len); | |||
/* Encode the security association (SA) number */ | // full block sized random value (128 bit) | |||
encode_uint32( outbuf, &idx, sa_id ); | encode_uint64(assembly, &idx, n2n_rand()); | |||
encode_uint64(assembly, &idx, n2n_rand()); | ||||
/* The assembly buffer is a source for encrypting data. The nonce is | ||||
* written in first followed by the packet payload. The whole | // adjust for maybe differently chosen TF_PREAMBLE_SIZE | |||
* contents of assembly are encrypted. */ | idx = TF_PREAMBLE_SIZE; | |||
pnonce = (uint32_t *)assembly; | ||||
*pnonce = n2n_rand(); | // the plaintext data | |||
memcpy( assembly + TRANSOP_TF_NONCE_SIZE, inbuf, in_len ); | encode_buf(assembly, &idx, inbuf, in_len); | |||
/* Encrypt the assembly contents and write the ciphertext after the SA. | // round up to next whole TF block size | |||
*/ | padded_len = (((idx - 1) / TF_BLOCK_SIZE) + 1) * TF_BLOCK_SIZE; | |||
len = TwoFishEncryptRaw( assembly, /* source */ | padding = (padded_len-idx); | |||
outbuf + TRANSOP_TF_VER_SIZE + TRANSOP_TF_SA_S | ||||
IZE, | // pad the following bytes with zero, fixed length (TF_BLOCK_SIZE) s | |||
in_len + TRANSOP_TF_NONCE_SIZE, /* enc size */ | eems to compile | |||
priv->enc_tf); | // to slightly faster code than run-time dependant 'padding' | |||
if ( len > 0 ) | memset(assembly + idx, 0, TF_BLOCK_SIZE); | |||
{ | tf_cbc_encrypt(outbuf, assembly, padded_len, tf_null_iv, priv->ctx); | |||
len += TRANSOP_TF_VER_SIZE + TRANSOP_TF_SA_SIZE; /* size of data ca | ||||
rried in UDP. */ | if(padding) { | |||
} | // exchange last two cipher blocks | |||
else | memcpy(buf, outbuf + padded_len - TF_BLOCK_SIZE, TF_BLOCK_SIZE); | |||
{ | memcpy(outbuf + padded_len - TF_BLOCK_SIZE, outbuf + padded_len | |||
traceEvent( TRACE_ERROR, "encode_twofish encryption failed." ); | - 2 * TF_BLOCK_SIZE, TF_BLOCK_SIZE); | |||
memcpy(outbuf + padded_len - 2 * TF_BLOCK_SIZE, buf, TF_BLOCK_SI | ||||
ZE); | ||||
} | } | |||
} else | ||||
traceEvent(TRACE_ERROR, "transop_encode_tf outbuf too small"); | ||||
} else | ||||
traceEvent(TRACE_ERROR, "transop_encode_tf inbuf too big to encrypt"); | ||||
return idx; | ||||
} | ||||
// see transop_encode_tf for packet format | ||||
static int transop_decode_tf (n2n_trans_op_t *arg, | ||||
uint8_t *outbuf, | ||||
size_t out_len, | ||||
const uint8_t *inbuf, | ||||
size_t in_len, | ||||
const uint8_t *peer_mac) { | ||||
transop_tf_t *priv = (transop_tf_t *)arg->priv; | ||||
uint8_t assembly[N2N_PKT_BUF_SIZE]; | ||||
uint8_t rest; | ||||
size_t penultimate_block; | ||||
uint8_t buf[TF_BLOCK_SIZE]; | ||||
int len = -1; | ||||
if(((in_len - TF_PREAMBLE_SIZE) <= N2N_PKT_BUF_SIZE) /* cipher text fits in | ||||
assembly */ | ||||
&& (in_len >= TF_PREAMBLE_SIZE) /* has at least random | ||||
number */ | ||||
&& (in_len >= TF_BLOCK_SIZE)) { /* minimum size require | ||||
ment for cipher text stealing */ | ||||
traceEvent(TRACE_DEBUG, "transop_decode_tf %lu bytes ciphertext", in_len | ||||
); | ||||
rest = in_len % TF_BLOCK_SIZE; | ||||
if(rest) { /* cipher text stealing */ | ||||
penultimate_block = ((in_len / TF_BLOCK_SIZE) - 1) * TF_BLOCK_SIZE; | ||||
// everything normal up to penultimate block | ||||
memcpy(assembly, inbuf, penultimate_block); | ||||
// prepare new penultimate block in buf | ||||
tf_ecb_decrypt(buf, inbuf + penultimate_block, priv->ctx); | ||||
memcpy(buf, inbuf + in_len - rest, rest); | ||||
// former penultimate block becomes new ultimate block | ||||
memcpy(assembly + penultimate_block + TF_BLOCK_SIZE, inbuf + penulti | ||||
mate_block, TF_BLOCK_SIZE); | ||||
// write new penultimate block from buf | ||||
memcpy(assembly + penultimate_block, buf, TF_BLOCK_SIZE); | ||||
// regular cbc decryption of the re-arranged ciphertext | ||||
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)) { | ||||
traceEvent(TRACE_WARNING, "transop_decode_tf payload decryption | ||||
failed with unexpected cipher text stealing padding"); | ||||
return -1; | ||||
} | ||||
} else { | ||||
// regular cbc decryption on multiple block-sized payload | ||||
tf_cbc_decrypt(assembly, inbuf, in_len, tf_null_iv, priv->ctx); | ||||
} | } | |||
else | len = in_len - TF_PREAMBLE_SIZE; | |||
{ | memcpy(outbuf, assembly + TF_PREAMBLE_SIZE, len); | |||
traceEvent( TRACE_ERROR, "encode_twofish outbuf too small." ); | } else | |||
} | traceEvent(TRACE_ERROR, "transop_decode_tf inbuf wrong size (%ul) to dec | |||
} | rypt", in_len); | |||
else | ||||
{ | return len; | |||
traceEvent( TRACE_ERROR, "encode_twofish inbuf too big to encrypt." ); | } | |||
static int setup_tf_key (transop_tf_t *priv, const uint8_t *password, ssize_t pa | ||||
ssword_len) { | ||||
unsigned char key[32]; /* tf key length, equals hash length */ | ||||
size_t key_size; | ||||
// the input password always gets hashed to make a more unpredictable use of | ||||
the key space | ||||
// just think of usually reset MSB of ASCII coded password bytes | ||||
pearson_hash_256(key, password, password_len); | ||||
key_size = 32; /* 256 bit */ | ||||
// setup the key and have corresponding context created | ||||
if(tf_init(key, key_size * 8, &(priv->ctx))) { | ||||
traceEvent(TRACE_ERROR, "setup_tf_key %u-bit key setup unsuccessful", ke | ||||
y_size * 8); | ||||
return -1; | ||||
} | } | |||
return len; | traceEvent(TRACE_DEBUG, "setup_tf_key %u-bit key setup completed", key_size | |||
* 8); | ||||
return 0; | ||||
} | } | |||
/** The twofish packet format consists of: | static void transop_tick_tf (n2n_trans_op_t *arg, time_t now) { | |||
* | ||||
* - a 8-bit twofish encoding version in clear text | // no tick action | |||
* - a 32-bit SA number in clear text | } | |||
* - ciphertext encrypted from a 32-bit nonce followed by the payload. | ||||
* | // Twofish initialization function | |||
* [V|SSSS|nnnnDDDDDDDDDDDDDDDDDDDDD] | int n2n_transop_tf_init (const n2n_edge_conf_t *conf, n2n_trans_op_t *ttt) { | |||
* |<------ encrypted ------>| | ||||
*/ | transop_tf_t *priv; | |||
static int transop_decode_twofish( n2n_trans_op_t * arg, | const u_char *encrypt_key = (const u_char *)conf->encrypt_key; | |||
uint8_t * outbuf, | size_t encrypt_key_len = strlen(conf->encrypt_key); | |||
size_t out_len, | ||||
const uint8_t * inbuf, | memset(ttt, 0, sizeof(*ttt)); | |||
size_t in_len, | ttt->transform_id = N2N_TRANSFORM_ID_TWOFISH; | |||
const uint8_t * peer_mac) | ||||
{ | ttt->tick = transop_tick_tf; | |||
int len=0; | ttt->deinit = transop_deinit_tf; | |||
transop_tf_t * priv = (transop_tf_t *)arg->priv; | ttt->fwd = transop_encode_tf; | |||
uint8_t assembly[N2N_PKT_BUF_SIZE]; | ttt->rev = transop_decode_tf; | |||
if ( ( (in_len - (TRANSOP_TF_VER_SIZE + TRANSOP_TF_SA_SIZE)) <= N2N_PKT_BUF_SI | priv = (transop_tf_t*)calloc(1, sizeof(transop_tf_t)); | |||
ZE ) /* Cipher text fits in assembly */ | if(!priv) { | |||
&& (in_len >= (TRANSOP_TF_VER_SIZE + TRANSOP_TF_SA_SIZE + TRANSOP_TF_NONC | traceEvent(TRACE_ERROR, "n2n_transop_tf_cbc_init cannot allocate transop | |||
E_SIZE) ) /* Has at least version, SA and nonce */ | _tf_t memory"); | |||
) { | return -1; | |||
size_t rem=in_len; | } | |||
size_t idx=0; | ttt->priv = priv; | |||
uint8_t tf_enc_ver=0; | ||||
uint32_t sa_rx=0; // Not used | ||||
/* Get the encoding version to make sure it is supported */ | ||||
decode_uint8( &tf_enc_ver, inbuf, &rem, &idx ); | ||||
if ( N2N_TWOFISH_TRANSFORM_VERSION == tf_enc_ver ) { | ||||
/* Get the SA number and make sure we are decrypting with the right one | ||||
. */ | ||||
decode_uint32( &sa_rx, inbuf, &rem, &idx ); | ||||
traceEvent(TRACE_DEBUG, "decode_twofish %lu", in_len); | ||||
len = TwoFishDecryptRaw( (void *)(inbuf + TRANSOP_TF_VER_SIZE + TRANSOP | ||||
_TF_SA_SIZE), | ||||
assembly, /* destination */ | ||||
(in_len - (TRANSOP_TF_VER_SIZE + TRANSOP_TF_ | ||||
SA_SIZE)), | ||||
priv->dec_tf); | ||||
if(len > 0) { | ||||
/* Step over 4-byte random nonce value */ | ||||
len -= TRANSOP_TF_NONCE_SIZE; /* size of ethernet packet */ | ||||
memcpy( outbuf, | ||||
assembly + TRANSOP_TF_NONCE_SIZE, | ||||
len ); | ||||
} else | ||||
traceEvent(TRACE_ERROR, "decode_twofish decryption failed"); | ||||
} else | ||||
traceEvent( TRACE_ERROR, "decode_twofish unsupported twofish version %u." | ||||
, tf_enc_ver ); | ||||
} else | ||||
traceEvent( TRACE_ERROR, "decode_twofish inbuf wrong size (%ul) to decrypt." | ||||
, in_len ); | ||||
return len; | ||||
} | ||||
static void transop_tick_twofish( n2n_trans_op_t * arg, time_t now ) {} | ||||
/* Twofish initialization function */ | ||||
int n2n_transop_twofish_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); | ||||
memset(ttt, 0, sizeof(*ttt)); | ||||
ttt->transform_id = N2N_TRANSFORM_ID_TWOFISH; | ||||
ttt->tick = transop_tick_twofish; | ||||
ttt->deinit = transop_deinit_twofish; | ||||
ttt->fwd = transop_encode_twofish; | ||||
ttt->rev = transop_decode_twofish; | ||||
priv = (transop_tf_t*) calloc(1, sizeof(transop_tf_t)); | ||||
if(!priv) { | ||||
traceEvent(TRACE_ERROR, "cannot allocate transop_tf_t memory"); | ||||
return(-1); | ||||
} | ||||
ttt->priv = priv; | ||||
/* This is a preshared key setup. Both Tx and Rx are using the same security a | ||||
ssociation. */ | ||||
priv->enc_tf = TwoFishInit(encrypt_key, encrypt_key_len); | ||||
priv->dec_tf = TwoFishInit(encrypt_key, encrypt_key_len); | ||||
if((!priv->enc_tf) || (!priv->dec_tf)) { | ||||
if(priv->enc_tf) TwoFishDestroy(priv->enc_tf); | ||||
if(priv->dec_tf) TwoFishDestroy(priv->dec_tf); | ||||
free(priv); | ||||
traceEvent(TRACE_ERROR, "TwoFishInit failed"); | ||||
return(-2); | ||||
} | ||||
return(0); | // setup the cipher and key | |||
return setup_tf_key(priv, encrypt_key, encrypt_key_len); | ||||
} | } | |||
End of changes. 14 change blocks. | ||||
190 lines changed or deleted | 203 lines changed or added |