summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2012-06-27 02:28:49 +0200
committerMatthias Schiffer <mschiffer@universe-factory.net>2012-06-27 02:28:49 +0200
commit5e451533dd5d1198f55561529ba5924e5f951123 (patch)
tree9eacc19aadb5d1a5ef52417d68cc5d0943cd1830
parent0ec1eb3d4c4823389659a4284b1f4ddeecf047e8 (diff)
downloadfastd-5e451533dd5d1198f55561529ba5924e5f951123.tar
fastd-5e451533dd5d1198f55561529ba5924e5f951123.zip
Primitive aes128-gcm implementation
-rw-r--r--CMakeLists.txt1
-rw-r--r--config.h.in1
-rw-r--r--src/CMakeLists.txt4
-rw-r--r--src/config.c7
-rw-r--r--src/method_aes128_gcm.c399
5 files changed, 412 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c50ec05..5c1c41e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -17,6 +17,7 @@ find_package(NaCl REQUIRED)
set(WITH_METHOD_XSALSA20_POLY1305 TRUE CACHE BOOL "Include xsalsa20-poly1305 method")
+set(WITH_METHOD_AES128_GCM TRUE CACHE BOOL "Include aes128-gcm method")
set(MAX_CONFIG_DEPTH 10 CACHE STRING "Maximum config include depth")
diff --git a/config.h.in b/config.h.in
index c9db59c..6b9595d 100644
--- a/config.h.in
+++ b/config.h.in
@@ -29,6 +29,7 @@
#define _FASTD_CONFIG_H_
#cmakedefine WITH_METHOD_XSALSA20_POLY1305
+#cmakedefine WITH_METHOD_AES128_GCM
#define MAX_CONFIG_DEPTH @MAX_CONFIG_DEPTH_NUM@
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ea1e8c9..29e3ba5 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -4,6 +4,10 @@ if(WITH_METHOD_XSALSA20_POLY1305)
list(APPEND METHODS method_xsalsa20_poly1305.c)
endif(WITH_METHOD_XSALSA20_POLY1305)
+if(WITH_METHOD_AES128_GCM)
+ list(APPEND METHODS method_aes128_gcm.c)
+endif(WITH_METHOD_AES128_GCM)
+
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${FASTD_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${UECC_INCLUDE_DIR} ${NACL_INCLUDE_DIR})
FLEX_TARGET(fastd_config_lex config.l ${CMAKE_CURRENT_BINARY_DIR}/config.ll.c)
diff --git a/src/config.c b/src/config.c
index 92c7a45..e69abcc 100644
--- a/src/config.c
+++ b/src/config.c
@@ -48,6 +48,9 @@ extern const fastd_method fastd_method_null;
#ifdef WITH_METHOD_XSALSA20_POLY1305
extern const fastd_method fastd_method_xsalsa20_poly1305;
#endif
+#ifdef WITH_METHOD_AES128_GCM
+extern const fastd_method fastd_method_aes128_gcm;
+#endif
static void default_config(fastd_config *conf) {
@@ -140,6 +143,10 @@ bool fastd_config_method(fastd_context *ctx, fastd_config *conf, const char *nam
else if (!strcmp(name, "xsalsa20-poly1305"))
conf->method = &fastd_method_xsalsa20_poly1305;
#endif
+#ifdef WITH_METHOD_AES128_GCM
+ else if (!strcmp(name, "aes128-gcm"))
+ conf->method = &fastd_method_aes128_gcm;
+#endif
else
return false;
diff --git a/src/method_aes128_gcm.c b/src/method_aes128_gcm.c
new file mode 100644
index 0000000..139276c
--- /dev/null
+++ b/src/method_aes128_gcm.c
@@ -0,0 +1,399 @@
+/*
+ Copyright (c) 2012, Matthias Schiffer <mschiffer@universe-factory.net>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include "fastd.h"
+#include <crypto_stream_aes128ctr.h>
+
+
+#define NONCEBYTES 7
+#define BLOCKBYTES 16
+
+
+struct _fastd_method_session_state {
+ struct timespec valid_till;
+ struct timespec refresh_after;
+
+ uint8_t d[crypto_stream_aes128ctr_BEFORENMBYTES];
+ uint8_t H[BLOCKBYTES];
+
+ uint8_t send_nonce[NONCEBYTES];
+ uint8_t receive_nonce[NONCEBYTES];
+
+ struct timespec receive_last;
+ uint64_t receive_reorder_seen;
+};
+
+
+static inline void increment_nonce(uint8_t nonce[NONCEBYTES]) {
+ nonce[0] += 2;
+
+ if (nonce[0] == 0 || nonce[0] == 1) {
+ int i;
+ for (i = 1; i < NONCEBYTES; i++) {
+ nonce[i]++;
+ if (nonce[i] != 0)
+ break;
+ }
+ }
+}
+
+static inline bool is_nonce_valid(const uint8_t nonce[NONCEBYTES], const uint8_t old_nonce[NONCEBYTES], int64_t *age) {
+ if ((nonce[0] & 1) != (old_nonce[0] & 1))
+ return false;
+
+ int i;
+ *age = 0;
+
+ for (i = NONCEBYTES-1; i >= 0; i--) {
+ *age *= 256;
+ *age += old_nonce[i]-nonce[i];
+ }
+
+ *age /= 2;
+ return true;
+}
+
+static size_t method_max_packet_size(fastd_context *ctx) {
+ return (fastd_max_packet_size(ctx) + NONCEBYTES + BLOCKBYTES);
+}
+
+
+static size_t method_min_encrypt_head_space(fastd_context *ctx) {
+ return 0;
+}
+
+static size_t method_min_decrypt_head_space(fastd_context *ctx) {
+ return 0;
+}
+
+static fastd_method_session_state* method_session_init(fastd_context *ctx, uint8_t *secret, size_t length, bool initiator) {
+ int i;
+
+ if (length < crypto_stream_aes128ctr_KEYBYTES)
+ exit_bug(ctx, "aes128-gcm: tried to init with short secret");
+
+ fastd_method_session_state *session = malloc(sizeof(fastd_method_session_state));
+
+ session->valid_till = ctx->now;
+ session->valid_till.tv_sec += ctx->conf->key_valid;
+
+ session->refresh_after = ctx->now;
+ session->refresh_after.tv_sec += ctx->conf->key_refresh;
+
+ crypto_stream_aes128ctr_beforenm(session->d, secret);
+
+ uint8_t zerononce[crypto_stream_aes128ctr_NONCEBYTES];
+ memset(zerononce, 0, crypto_stream_aes128ctr_NONCEBYTES);
+ crypto_stream_aes128ctr_afternm(session->H, BLOCKBYTES, zerononce, session->d);
+
+ session->send_nonce[0] = initiator ? 3 : 2;
+ session->receive_nonce[0] = initiator ? 0 : 1;
+
+ for (i = 1; i < NONCEBYTES; i++) {
+ session->send_nonce[i] = 0;
+ session->receive_nonce[i] = 0;
+ }
+
+ return session;
+}
+
+static bool method_session_is_valid(fastd_context *ctx, fastd_method_session_state *session) {
+ return (session && timespec_after(&session->valid_till, &ctx->now));
+}
+
+static bool method_session_is_initiator(fastd_context *ctx, fastd_method_session_state *session) {
+ return (session->send_nonce[0] & 1);
+}
+
+static bool method_session_want_refresh(fastd_context *ctx, fastd_method_session_state *session) {
+ return timespec_after(&ctx->now, &session->refresh_after);
+}
+
+static void method_session_free(fastd_context *ctx, fastd_method_session_state *session) {
+ if(session) {
+ memset(session, 0, sizeof(fastd_method_session_state));
+ free(session);
+ }
+}
+
+static void sel(uint8_t *out, const uint8_t *r, const uint8_t *s, unsigned int l, unsigned int b) {
+ unsigned int j;
+ unsigned int t;
+ uint8_t bminus1;
+
+ bminus1 = b - 1;
+ for (j = 0; j < l; ++j) {
+ t = bminus1 & (r[j] ^ s[j]);
+ out[j] = s[j] ^ t;
+ }
+}
+
+static inline void xor(uint8_t *x, const uint8_t *a, const uint8_t *b, unsigned int l) {
+ int i;
+ for (i = 0; i < l; i++)
+ x[i] = a[i] ^ b[i];
+}
+
+static inline void shl1(uint8_t *x, unsigned int l) {
+ int i;
+ uint8_t c = 0;
+
+ for (i = 0; i < l; i++) {
+ uint8_t c2 = x[i] >> 7;
+ x[i] = (x[i] << 1) | c;
+ c = c2;
+ }
+}
+
+static inline void shr1(uint8_t *x, unsigned int l) {
+ int i;
+ uint8_t c = 0;
+
+ for (i = l-1; i >= 0; i--) {
+ uint8_t c2 = x[i] & 1;
+ x[i] = (x[i] >> 1) | (c << 7);
+ c = c2;
+ }
+}
+
+static const uint8_t p[BLOCKBYTES] = {0x87};
+
+static void mul(uint8_t out[BLOCKBYTES], const uint8_t a[BLOCKBYTES], const uint8_t b[BLOCKBYTES]) {
+ uint8_t out2[2*BLOCKBYTES];
+ uint8_t zero[2*BLOCKBYTES];
+ uint8_t a2[2*BLOCKBYTES];
+ uint8_t b2[BLOCKBYTES];
+
+ memset(out2, 0, 2*BLOCKBYTES);
+ memset(zero, 0, 2*BLOCKBYTES);
+
+ memcpy(a2, a, BLOCKBYTES);
+ memset(a2+BLOCKBYTES, 0, BLOCKBYTES);
+
+ memcpy(b2, b, BLOCKBYTES);
+
+ int i;
+ for (i = 0; i < 8*BLOCKBYTES; i++) {
+ uint8_t tmp[2*BLOCKBYTES];
+
+ sel(tmp, zero, a2, 2*BLOCKBYTES, b2[0]&1);
+ xor(out2, out2, tmp, 2*BLOCKBYTES);
+
+ shl1(a2, 2*BLOCKBYTES);
+ shr1(b2, BLOCKBYTES);
+ }
+
+ uint8_t out3[BLOCKBYTES+1];
+ memcpy(out3, out2, BLOCKBYTES);
+ out3[BLOCKBYTES] = 0;
+
+ uint8_t p2[BLOCKBYTES+1];
+ memcpy(p2, p, BLOCKBYTES);
+ p2[BLOCKBYTES] = 0;
+
+ for (i = 0; i < 8*BLOCKBYTES; i++) {
+ uint8_t tmp[BLOCKBYTES+1];
+
+ sel(tmp, zero, p2, BLOCKBYTES+1, out2[BLOCKBYTES]&1);
+ xor(out3, out3, tmp, BLOCKBYTES+1);
+
+ shl1(p2, BLOCKBYTES+1);
+ shr1(out2+BLOCKBYTES, BLOCKBYTES);
+ }
+
+ memcpy(out, out3, BLOCKBYTES);
+ memcpy(p2, p, BLOCKBYTES);
+
+ for (i = 0; i < 8; i++) {
+ uint8_t tmp[2];
+
+ sel(tmp, zero, p2, 2, out3[BLOCKBYTES]&1);
+ xor(out, out, tmp, 2);
+
+ shl1(p2, BLOCKBYTES);
+ out3[BLOCKBYTES] >>= 1;
+ }
+}
+
+#define BLOCKPTR(buf, i) (((uint8_t*)(buf))+i*BLOCKBYTES)
+
+static bool method_encrypt(fastd_context *ctx, fastd_peer *peer, fastd_method_session_state *session, fastd_buffer *out, fastd_buffer in) {
+ *out = fastd_buffer_alloc(in.len, NONCEBYTES+BLOCKBYTES, 0);
+ uint8_t *sig = ((uint8_t*)out->data) - BLOCKBYTES;
+
+ memset(sig, 0, BLOCKBYTES);
+
+ uint8_t nonce[crypto_stream_aes128ctr_NONCEBYTES];
+ memcpy(nonce, session->send_nonce, NONCEBYTES);
+ memset(nonce+NONCEBYTES, 0, crypto_stream_aes128ctr_NONCEBYTES-NONCEBYTES-1);
+ nonce[crypto_stream_aes128ctr_NONCEBYTES-1] = 1;
+
+ uint8_t stream[in.len+BLOCKBYTES];
+ crypto_stream_aes128ctr_afternm(stream, in.len+BLOCKBYTES, nonce, session->d);
+
+ int blocks = (in.len+BLOCKBYTES-1)/BLOCKBYTES;
+
+ int i;
+ for (i = 0; i < blocks; i++) {
+ int len = BLOCKBYTES;
+ if (i == blocks-1)
+ len = in.len - i*BLOCKBYTES;
+
+ xor(BLOCKPTR(out->data, i), BLOCKPTR(in.data, i), BLOCKPTR(stream, i+1), len);
+
+ xor(sig, sig, BLOCKPTR(out->data, i), len);
+ mul(sig, sig, session->H);
+ }
+
+ sig[BLOCKBYTES-8] ^= (in.len >> 53) & 0xff;
+ sig[BLOCKBYTES-7] ^= (in.len >> 45) & 0xff;
+ sig[BLOCKBYTES-6] ^= (in.len >> 37) & 0xff;
+ sig[BLOCKBYTES-5] ^= (in.len >> 29) & 0xff;
+ sig[BLOCKBYTES-4] ^= (in.len >> 21) & 0xff;
+ sig[BLOCKBYTES-3] ^= (in.len >> 13) & 0xff;
+ sig[BLOCKBYTES-2] ^= (in.len >> 5) & 0xff;
+ sig[BLOCKBYTES-1] ^= (in.len << 3) & 0xff;
+ mul(sig, sig, session->H);
+
+ xor(sig, sig, stream, BLOCKBYTES);
+
+ fastd_buffer_free(in);
+
+ fastd_buffer_pull_head(out, NONCEBYTES+BLOCKBYTES);
+ memcpy(out->data, session->send_nonce, NONCEBYTES);
+ increment_nonce(session->send_nonce);
+
+ return true;
+}
+
+static bool method_decrypt(fastd_context *ctx, fastd_peer *peer, fastd_method_session_state *session, fastd_buffer *out, fastd_buffer in) {
+ if (in.len < NONCEBYTES+BLOCKBYTES)
+ return false;
+
+ if (!method_session_is_valid(ctx, session))
+ return false;
+
+ uint8_t nonce[crypto_stream_aes128ctr_NONCEBYTES];
+ memcpy(nonce, in.data, NONCEBYTES);
+ memset(nonce+NONCEBYTES, 0, crypto_stream_aes128ctr_NONCEBYTES-NONCEBYTES-1);
+ nonce[crypto_stream_aes128ctr_NONCEBYTES-1] = 1;
+
+ int64_t age;
+ if (!is_nonce_valid(nonce, session->receive_nonce, &age))
+ return false;
+
+ if (age >= 0) {
+ if (timespec_diff(&ctx->now, &session->receive_last) > ctx->conf->reorder_time*1000)
+ return false;
+
+ if (age > ctx->conf->reorder_count)
+ return false;
+ }
+
+ fastd_buffer_push_head(&in, NONCEBYTES+BLOCKBYTES);
+
+ *out = fastd_buffer_alloc(in.len, 0, 0);
+
+ uint8_t sig[BLOCKBYTES];
+ memset(sig, 0, BLOCKBYTES);
+
+ uint8_t stream[in.len+BLOCKBYTES];
+ crypto_stream_aes128ctr_afternm(stream, in.len+BLOCKBYTES, nonce, session->d);
+
+ int blocks = (in.len+BLOCKBYTES-1)/BLOCKBYTES;
+
+ int i;
+ for (i = 0; i < blocks; i++) {
+ int len = BLOCKBYTES;
+ if (i == blocks-1)
+ len = in.len - i*BLOCKBYTES;
+
+ xor(BLOCKPTR(out->data, i), BLOCKPTR(in.data, i), BLOCKPTR(stream, i+1), len);
+
+ xor(sig, sig, BLOCKPTR(in.data, i), len);
+ mul(sig, sig, session->H);
+ }
+
+ sig[BLOCKBYTES-8] ^= (in.len >> 53) & 0xff;
+ sig[BLOCKBYTES-7] ^= (in.len >> 45) & 0xff;
+ sig[BLOCKBYTES-6] ^= (in.len >> 37) & 0xff;
+ sig[BLOCKBYTES-5] ^= (in.len >> 29) & 0xff;
+ sig[BLOCKBYTES-4] ^= (in.len >> 21) & 0xff;
+ sig[BLOCKBYTES-3] ^= (in.len >> 13) & 0xff;
+ sig[BLOCKBYTES-2] ^= (in.len >> 5) & 0xff;
+ sig[BLOCKBYTES-1] ^= (in.len << 3) & 0xff;
+ mul(sig, sig, session->H);
+
+ xor(sig, sig, stream, BLOCKBYTES);
+
+ fastd_buffer_pull_head(&in, BLOCKBYTES);
+
+ if (memcmp(sig, in.data, BLOCKBYTES) != 0) {
+ fastd_buffer_free(*out);
+
+ /* restore input buffer */
+ fastd_buffer_pull_head(&in, NONCEBYTES);
+
+ return false;
+ }
+
+ fastd_buffer_free(in);
+
+ if (age < 0) {
+ session->receive_reorder_seen >>= age;
+ session->receive_reorder_seen |= (1 >> (age+1));
+ memcpy(session->receive_nonce, nonce, NONCEBYTES);
+ session->receive_last = ctx->now;
+ }
+ else if (age == 0 || session->receive_reorder_seen & (1 << (age-1))) {
+ pr_debug(ctx, "dropping duplicate packet from %P (age %u)", peer, (unsigned)age);
+ fastd_buffer_free(*out);
+ *out = fastd_buffer_alloc(0, 0, 0);
+ }
+ else {
+ pr_debug(ctx, "accepting reordered packet from %P (age %u)", peer, (unsigned)age);
+ session->receive_reorder_seen |= (1 << (age-1));
+ }
+
+ return true;
+}
+
+const fastd_method fastd_method_aes128_gcm = {
+ .name = "aes128-gcm",
+
+ .max_packet_size = method_max_packet_size,
+ .min_encrypt_head_space = method_min_encrypt_head_space,
+ .min_decrypt_head_space = method_min_decrypt_head_space,
+
+ .session_init = method_session_init,
+ .session_is_valid = method_session_is_valid,
+ .session_is_initiator = method_session_is_initiator,
+ .session_want_refresh = method_session_want_refresh,
+ .session_free = method_session_free,
+
+ .encrypt = method_encrypt,
+ .decrypt = method_decrypt,
+};