From 0477e476e80f1c959d637a76612df0682b3060be Mon Sep 17 00:00:00 2001 From: Logan007 Date: Thu, 28 May 2020 03:17:21 +0545 Subject: [PATCH] added Speck --- Makefile.in | 4 +- edge.c | 6 ++ edge_utils.c | 5 ++ n2n.h | 1 + n2n_transforms.h | 1 + speck.c | 149 ++++++++++++++++++++++++++++++++ speck.h | 9 ++ tools/benchmark.c | 4 + transform_speck.c | 212 ++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 389 insertions(+), 2 deletions(-) create mode 100644 speck.c create mode 100644 speck.h create mode 100644 transform_speck.c diff --git a/Makefile.in b/Makefile.in index d428259..bd4b23d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -48,9 +48,9 @@ MAN7DIR=$(MANDIR)/man7 MAN8DIR=$(MANDIR)/man8 N2N_LIB=libn2n.a -N2N_OBJS=n2n.o wire.o minilzo.o twofish.o \ +N2N_OBJS=n2n.o wire.o minilzo.o twofish.o speck.o \ edge_utils.o sn_utils.o \ - transform_null.o transform_tf.o transform_aes.o transform_cc20.o \ + transform_null.o transform_tf.o transform_aes.o transform_cc20.o transform_speck.o \ tuntap_freebsd.o tuntap_netbsd.o tuntap_linux.o \ tuntap_osx.o LIBS_EDGE+=$(LIBS_EDGE_OPT) diff --git a/edge.c b/edge.c index 9e100f7..ac28a20 100644 --- a/edge.c +++ b/edge.c @@ -179,6 +179,7 @@ static void help() { #endif #ifdef HAVE_OPENSSL_1_1 printf("-A4 | Use ChaCha20 for payload encryption. Requires a key.\n"); + printf("-A5 | Use Speck for payload encryption. Requires a key.\n"); #endif printf("-E | Accept multicast MAC addresses (default=drop).\n"); printf("-S | Do not connect P2P. Always use the supernode.\n"); @@ -324,6 +325,11 @@ static int setOption(int optkey, char *optargument, n2n_priv_config_t *ec, n2n_e break; } #endif + case 5: + { + conf->transop_id = N2N_TRANSFORM_ID_SPECK; + break; + } default: { conf->transop_id = N2N_TRANSFORM_ID_INVAL; diff --git a/edge_utils.c b/edge_utils.c index 454c30e..29e6bc7 100644 --- a/edge_utils.c +++ b/edge_utils.c @@ -139,6 +139,7 @@ const char* transop_str(enum n2n_transform tr) { case N2N_TRANSFORM_ID_TWOFISH: return("twofish"); case N2N_TRANSFORM_ID_AESCBC: return("AES-CBC"); case N2N_TRANSFORM_ID_CHACHA20:return("ChaCha20"); + case N2N_TRANSFORM_ID_SPECK :return("Speck"); default: return("invalid"); }; } @@ -247,6 +248,10 @@ n2n_edge_t* edge_init(const tuntap_dev *dev, const n2n_edge_conf_t *conf, int *r rc = n2n_transop_cc20_init(&eee->conf, &eee->transop); break; #endif + case N2N_TRANSFORM_ID_SPECK: + rc = n2n_transop_speck_init(&eee->conf, &eee->transop); + break; + default: rc = n2n_transop_null_init(&eee->conf, &eee->transop); } diff --git a/n2n.h b/n2n.h index c41098b..4b48274 100644 --- a/n2n.h +++ b/n2n.h @@ -296,6 +296,7 @@ int n2n_transop_aes_cbc_init(const n2n_edge_conf_t *conf, n2n_trans_op_t *ttt); #ifdef HAVE_OPENSSL_1_1 int n2n_transop_cc20_init(const n2n_edge_conf_t *conf, n2n_trans_op_t *ttt); #endif +int n2n_transop_speck_init(const n2n_edge_conf_t *conf, n2n_trans_op_t *ttt); /* Log */ void setTraceLevel(int level); diff --git a/n2n_transforms.h b/n2n_transforms.h index f203257..1734ec1 100644 --- a/n2n_transforms.h +++ b/n2n_transforms.h @@ -31,6 +31,7 @@ typedef enum n2n_transform { N2N_TRANSFORM_ID_TWOFISH = 2, N2N_TRANSFORM_ID_AESCBC = 3, N2N_TRANSFORM_ID_CHACHA20 = 4, + N2N_TRANSFORM_ID_SPECK = 5, } n2n_transform_t; struct n2n_trans_op; diff --git a/speck.c b/speck.c new file mode 100644 index 0000000..abdb59e --- /dev/null +++ b/speck.c @@ -0,0 +1,149 @@ +// cipher SPECK -- 128 bit block size -- 256 bit key size +// taken from (and modified: removed pure crypto-stream generation and seperated key expansion) +// https://github.com/nsacyber/simon-speck-supercop/blob/master/crypto_stream/speck128256ctr/ref/stream.c + +#include +#include +#include + +// #define u64 unsigned long long +#define u64 uint64_t + +#define ROR64(x,r) (((x)>>(r))|((x)<<(64-(r)))) +#define ROL64(x,r) (((x)<<(r))|((x)>>(64-(r)))) +#define R(x,y,k) (x=ROR64(x,8), x+=y, x^=k, y=ROL64(y,3), y^=x) +#define RI(x,y,k) (y^=x, y=ROR64(y,3), x^=k, x-=y, x=ROL64(x,8)) + + +static int speck_encrypt(u64 *u, u64 *v, u64 key[]) { + + u64 i, x = *u, y = *v; + + for (i = 0; i < 34; i++) + R (x, y, key[i]); + + *u = x; *v = y; + + return 0; +} + + +// not neccessary for CTR mode +/* static int speck_decrypt(u64 *u, u64 *v, u64 key[]) { + + int i; + u64 x=*u,y=*v; + for (i = 33; i >= 0 ;i--) + RI (x, y, key[i]); + + *u = x; *v = y; + + return 0; +} */ + + +int speck_ctr (unsigned char *out, const unsigned char *in, + unsigned long long inlen, + const unsigned char *n, + u64 rk[]) { + + u64 i, nonce[2], x, y, t; + unsigned char *block = malloc (16); + + if (!inlen) { + free (block); + return 0; + } +// !!! htole64 !!! + nonce[0] = htole64 ( ((u64*)n)[0] ); + nonce[1] = htole64 ( ((u64*)n)[1] ); + + t=0; + while(inlen >= 16) { + x = nonce[1]; y = nonce[0]; nonce[0]++; + speck_encrypt (&x, &y, rk); +// !!! htole64 !!! + ((u64 *)out)[1+t] = htole64 (x ^ ((u64 *)in)[1+t]); + ((u64 *)out)[0+t] = htole64 (y ^ ((u64 *)in)[0+t]); + t += 2; + inlen -= 16; + } + if (inlen > 0) { + x = nonce[1]; y = nonce[0]; + speck_encrypt (&x, &y, rk); +// !!! htole64 !!! + ((u64 *)block)[1] = htole64 (x); ((u64 *)block)[0] = htole64 (y); + for (i=0; i < inlen; i++) + out[i + 8*t] = block[i] ^ in[i + 8*t]; + } + + free (block); + + return 0; +} + + +int speck_expand_key (const unsigned char *k, u64 rk[]) { + + u64 K[4]; + u64 i; + + for (i=0; i < 4; i++) +// !!! htole64 !!! + K[i] = htole64 ( ((u64 *)k)[i] ); + + + u64 D = K[3], C = K[2], B = K[1], A = K[0]; + + for (i = 0; i < 33; i += 3) { + rk[i ] = A; R (B, A, i ); + rk[i+1] = A; R (C, A, i + 1); + rk[i+2] = A; R (D, A, i + 2); + } + rk[33] = A; + + return 1; +} + + +int speck_test () { + + uint8_t key[32] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F }; + + + uint8_t iv[16] = { 0x70, 0x6f, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x20, + 0x49, 0x6e, 0x20, 0x74, 0x68, 0x6f, 0x73, 0x65 }; + + uint8_t pt[16] = { 0x00 }; + + // expected outcome (according to pp. 35 & 36 of Implementation Guide) + uint8_t ct[16] = { 0x43, 0x8f, 0x18, 0x9c, 0x8d, 0xb4, 0xee, 0x4e, + 0x3e, 0xf5, 0xc0, 0x05, 0x04, 0x01, 0x09, 0x41 }; + + u64 round_keys[34]; + speck_expand_key (key, round_keys); + + speck_ctr (pt, pt, 16, iv, round_keys); + +fprintf (stderr, "rk00: %016lx\n", round_keys[0]); +fprintf (stderr, "rk33: %016lx\n", round_keys[33]); +fprintf (stderr, "out : %016lx\n", *(uint64_t*)pt); +fprintf (stderr, "mem : " ); for (int i=0; i < 16; i++) fprintf (stderr, "%02x ", pt[i]); fprintf (stderr, "\n"); + + int ret = 1; + for (int i=0; i < 16; i++) + if (pt[i] != ct[i]) ret = 0; + + return (ret); +} + +/* +int main (int argc, char* argv[]) { + + fprintf (stdout, "SPECK SELF TEST RESULT: %u\n", speck_test (0,NULL)); +} +*/ + diff --git a/speck.h b/speck.h new file mode 100644 index 0000000..836e06d --- /dev/null +++ b/speck.h @@ -0,0 +1,9 @@ + +#define u64 uint64_t + +int speck_ctr (unsigned char *out, const unsigned char *in, + unsigned long long inlen, + const unsigned char *n, + u64 rk[]); + +int speck_expand_key (const unsigned char *k, u64 rk[]); diff --git a/tools/benchmark.c b/tools/benchmark.c index 9197035..b2491f4 100644 --- a/tools/benchmark.c +++ b/tools/benchmark.c @@ -100,6 +100,7 @@ int main(int argc, char * argv[]) { #ifdef HAVE_OPENSSL_1_1 n2n_trans_op_t transop_cc20; #endif + n2n_trans_op_t transop_speck; n2n_edge_conf_t conf; parseArgs(argc, argv); @@ -118,6 +119,7 @@ int main(int argc, char * argv[]) { #ifdef HAVE_OPENSSL_1_1 n2n_transop_cc20_init(&conf, &transop_cc20); #endif + n2n_transop_speck_init(&conf, &transop_speck); /* Run the tests */ run_transop_benchmark("transop_null", &transop_null, &conf, pktbuf); @@ -128,6 +130,7 @@ int main(int argc, char * argv[]) { #ifdef N2N_HAVE_AES run_transop_benchmark("transop_cc20", &transop_cc20, &conf, pktbuf); #endif + run_transop_benchmark("transop_speck", &transop_speck, &conf, pktbuf); /* Cleanup */ transop_null.deinit(&transop_null); @@ -138,6 +141,7 @@ int main(int argc, char * argv[]) { #ifdef HAVE_OPENSSL_1_1 transop_cc20.deinit(&transop_cc20); #endif + transop_speck.deinit(&transop_speck); return 0; } diff --git a/transform_speck.c b/transform_speck.c new file mode 100644 index 0000000..bc299ac --- /dev/null +++ b/transform_speck.c @@ -0,0 +1,212 @@ +/** + * (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 "n2n.h" +#include "n2n_transforms.h" +#include "speck.h" + +#define N2N_SPECK_TRANSFORM_VERSION 1 /* version of the transform encoding */ +#define N2N_SPECK_IVEC_SIZE 16 + +#define SPECK_KEY_BYTES (256/8) + +/* Speck plaintext preamble */ +#define TRANSOP_SPECK_VER_SIZE 1 /* Support minor variants in encoding in one module. */ +#define TRANSOP_SPECK_PREAMBLE_SIZE (TRANSOP_SPECK_VER_SIZE + N2N_SPECK_IVEC_SIZE) + +typedef unsigned char n2n_speck_ivec_t[N2N_SPECK_IVEC_SIZE]; + +typedef struct transop_speck { + uint64_t rk[34]; /* the round keys for payload encryption & decryption */ +} transop_speck_t; + +/* ****************************************************** */ + +static int transop_deinit_speck(n2n_trans_op_t *arg) { + transop_speck_t *priv = (transop_speck_t *)arg->priv; + + if(priv) + free(priv); + + return 0; +} + +/* ****************************************************** */ + +static void set_speck_iv(transop_speck_t *priv, n2n_speck_ivec_t ivec) { + // keep in mind the following condition: N2N_SPECK_IVEC_SIZE % sizeof(rand_value) == 0 ! + uint32_t rand_value; + for (uint8_t i = 0; i < N2N_SPECK_IVEC_SIZE; i += sizeof(rand_value)) { + rand_value = rand(); // CONCERN: rand() is not considered cryptographicly secure, REPLACE later + memcpy(ivec + i, &rand_value, sizeof(rand_value)); + } +} + +/* ****************************************************** */ + +/** The Speck packet format consists of: + * + * - a 8-bit speck encoding version in clear text + * - a 128-bit random IV + * - encrypted payload. + * + * [V|IIII|DDDDDDDDDDDDDDDDDDDDD] + * |<---- encrypted ---->| + */ +static int transop_encode_speck(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) { + int len=-1; + transop_speck_t * priv = (transop_speck_t *)arg->priv; + + if(in_len <= N2N_PKT_BUF_SIZE) { + if((in_len + TRANSOP_SPECK_PREAMBLE_SIZE) <= out_len) { + size_t idx=0; + n2n_speck_ivec_t enc_ivec = {0}; + + traceEvent(TRACE_DEBUG, "encode_speck %lu bytes", in_len); + + /* Encode the Speck format version. */ + encode_uint8(outbuf, &idx, N2N_SPECK_TRANSFORM_VERSION); + + /* Generate and encode the IV. */ + set_speck_iv(priv, enc_ivec); + encode_buf(outbuf, &idx, &enc_ivec, N2N_SPECK_IVEC_SIZE); + traceEvent(TRACE_DEBUG, "encode_speck iv=%016llx:%016llx", + htobe64(*(uint64_t*)&enc_ivec[0]), + htobe64(*(uint64_t*)&enc_ivec[8]) ); + + /* Encrypt the payload 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; + + speck_ctr (outbuf + TRANSOP_SPECK_PREAMBLE_SIZE, inbuf, in_len, enc_ivec, priv->rk); + traceEvent(TRACE_DEBUG, "encode_speck: encrypted %u bytes.\n", in_len); + + len += TRANSOP_SPECK_PREAMBLE_SIZE; /* size of data carried in UDP. */ + } else + traceEvent(TRACE_ERROR, "encode_speck outbuf too small."); + } else + traceEvent(TRACE_ERROR, "encode_speck inbuf too big to encrypt."); + + return len; +} + +/* ****************************************************** */ + +/* See transop_encode_speck for packet format */ +static int transop_decode_speck(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) { + int len=0; + transop_speck_t * priv = (transop_speck_t *)arg->priv; + + if(((in_len - TRANSOP_SPECK_PREAMBLE_SIZE) <= N2N_PKT_BUF_SIZE) /* Cipher text fits in buffer */ + && (in_len >= TRANSOP_SPECK_PREAMBLE_SIZE) /* Has at least version, iv */ + ) + { + size_t rem=in_len; + size_t idx=0; + uint8_t speck_enc_ver=0; + n2n_speck_ivec_t dec_ivec = {0}; + + /* Get the encoding version to make sure it is supported */ + decode_uint8(&speck_enc_ver, inbuf, &rem, &idx ); + + if(N2N_SPECK_TRANSFORM_VERSION == speck_enc_ver) { + traceEvent(TRACE_DEBUG, "decode_speck %lu bytes", in_len); + len = (in_len - TRANSOP_SPECK_PREAMBLE_SIZE); + + /* Get the IV */ + decode_buf((uint8_t *)&dec_ivec, N2N_SPECK_IVEC_SIZE, inbuf, &rem, &idx); + traceEvent(TRACE_DEBUG, "decode_speck iv=%016llx:%016llx", + htobe64(*(uint64_t*)&dec_ivec[0]), + htobe64(*(uint64_t*)&dec_ivec[8]) ); + + speck_ctr (outbuf, inbuf + TRANSOP_SPECK_PREAMBLE_SIZE, len, dec_ivec, priv->rk); + traceEvent(TRACE_DEBUG, "decode_speck: decrypted %u bytes.\n", len); + + } else + traceEvent(TRACE_ERROR, "decode_speck unsupported Speck version %u.", speck_enc_ver); + } else + traceEvent(TRACE_ERROR, "decode_speck inbuf wrong size (%ul) to decrypt.", in_len); + + return len; +} + +/* ****************************************************** */ + +static int setup_speck_key(transop_speck_t *priv, const uint8_t *key, ssize_t key_size) { + + uint8_t key_mat_buf[32] = { 0x00 }; + + /* Clear out any old possibly longer key matter. */ + memset(&(priv->rk), 0, sizeof(priv->rk) ); + + /* TODO: The input key always gets hashed to make a more unpredictable and more complete use of the key space */ + // REVISIT: Hash the key to keymat (formerly used: SHA) + // SHA256(key, key_size, key_mat_buf) + // memcpy (priv->key, key_mat_buf, SHA256_DIGEST_LENGTH); + // ADD: Pearson Hashing + // FOR NOW: USE KEY ITSELF + memcpy (key_mat_buf, key, ((key_size>32)?32:key_size) ); + + speck_expand_key (key_mat_buf, priv->rk); + traceEvent(TRACE_DEBUG, "Speck key setup completed\n"); + + return(0); +} + +/* ****************************************************** */ + +static void transop_tick_speck(n2n_trans_op_t * arg, time_t now) { ; } + +/* ****************************************************** */ + +/* Speck initialization function */ +int n2n_transop_speck_init(const n2n_edge_conf_t *conf, n2n_trans_op_t *ttt) { + transop_speck_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_SPECK; + + ttt->tick = transop_tick_speck; + ttt->deinit = transop_deinit_speck; + ttt->fwd = transop_encode_speck; + ttt->rev = transop_decode_speck; + + priv = (transop_speck_t*) calloc(1, sizeof(transop_speck_t)); + if(!priv) { + traceEvent(TRACE_ERROR, "cannot allocate transop_speck_t memory"); + return(-1); + } + ttt->priv = priv; + + /* Setup the cipher and key */ + return(setup_speck_key(priv, encrypt_key, encrypt_key_len)); +} +