commit aa399c851ad5e4741edde13d05d2f92c59d20335 Author: George Kadianakis desnacked@gmail.com Date: Mon Apr 11 02:34:15 2011 +0200
* Revived obfs2 unit tests. * Renamed unittest_protocol.c to unittest_obfs2.c * Renamed src/plugins/ to src/protocols/ --- Makefile.am | 14 +- src/plugins/dummy.c | 63 ------ src/plugins/dummy.h | 10 - src/plugins/obfs2.c | 405 ----------------------------------- src/plugins/obfs2.h | 81 ------- src/plugins/obfs2_crypt.c | 206 ------------------ src/plugins/obfs2_crypt.h | 62 ------ src/protocols/dummy.c | 63 ++++++ src/protocols/dummy.h | 10 + src/protocols/obfs2.c | 405 +++++++++++++++++++++++++++++++++++ src/protocols/obfs2.h | 81 +++++++ src/protocols/obfs2_crypt.c | 206 ++++++++++++++++++ src/protocols/obfs2_crypt.h | 62 ++++++ src/test/unittest_obfs2.c | 476 ++++++++++++++++++++++++++++++++++++++++++ src/test/unittest_protocol.c | 469 ----------------------------------------- 15 files changed, 1310 insertions(+), 1303 deletions(-)
diff --git a/Makefile.am b/Makefile.am index 32d5c05..bd3a2b2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,9 +12,9 @@ libobfsproxy_a_SOURCES = \ src/protocol.c \ src/socks.c \ src/util.c \ - src/plugins/obfs2.c \ - src/plugins/obfs2_crypt.c \ - src/plugins/dummy.c + src/protocols/obfs2.c \ + src/protocols/obfs2_crypt.c \ + src/protocols/dummy.c
obfsproxy_SOURCES = \ src/main.c @@ -23,7 +23,7 @@ obfsproxy_LDADD = @libevent_LIBS@ @openssl_LIBS@ libobfsproxy.a unittests_SOURCES = \ src/test/tinytest.c \ src/test/unittest.c \ - src/test/unittest_protocol.c \ + src/test/unittest_obfs2.c \ src/test/unittest_crypt.c \ src/test/unittest_socks.c unittests_LDADD = @libevent_LIBS@ @openssl_LIBS@ libobfsproxy.a @@ -35,9 +35,9 @@ noinst_HEADERS = \ src/util.h \ src/test/tinytest.h \ src/test/tinytest_macros.h \ - src/plugins/obfs2.h \ - src/plugins/obfs2_crypt.h \ - src/plugins/dummy.h + src/protocols/obfs2.h \ + src/protocols/obfs2_crypt.h \ + src/protocols/dummy.h
EXTRA_DIST = doc/protocol-spec.txt src/sha256.c
diff --git a/src/plugins/dummy.c b/src/plugins/dummy.c deleted file mode 100644 index 8fe722e..0000000 --- a/src/plugins/dummy.c +++ /dev/null @@ -1,63 +0,0 @@ -#include <assert.h> -#include <string.h> -#include <stdlib.h> -#include <stdio.h> - -#include <unistd.h> - -#include <openssl/rand.h> -#include <event2/buffer.h> - -#include "dummy.h" -#include "../util.h" -#include "../protocol.h" - - -static int dummy_send(void *nothing, - struct evbuffer *source, struct evbuffer *dest); -static int dummy_recv(void *nothing, struct evbuffer *source, - struct evbuffer *dest); - -static protocol_vtable *vtable=NULL; - -int -dummy_init(void) { - vtable = calloc(1, sizeof(protocol_vtable)); - if (!vtable) - return -1; - - vtable->destroy = NULL; - vtable->create = dummy_new; - vtable->handshake = NULL; - vtable->send = dummy_send; - vtable->recv = dummy_recv; - - return 1; -} - -void * -dummy_new(struct protocol_t *proto_struct, int whatever) { - (void)whatever; - - proto_struct->vtable = vtable; - - /* Dodging state check. - This is terrible I know.*/ - return (void *)666U; -} - -static int -dummy_send(void *nothing, - struct evbuffer *source, struct evbuffer *dest) { - (void)nothing; - - return evbuffer_add_buffer(dest,source); -} - -static int -dummy_recv(void *nothing, - struct evbuffer *source, struct evbuffer *dest) { - (void)nothing; - - return evbuffer_add_buffer(dest,source); -} diff --git a/src/plugins/dummy.h b/src/plugins/dummy.h deleted file mode 100644 index 241366d..0000000 --- a/src/plugins/dummy.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef DUMMY_H -#define DUMMY_H - -struct protocol_t; -struct evbuffer; - -int dummy_init(void); -void *dummy_new(struct protocol_t *proto_struct, int whatever); - -#endif diff --git a/src/plugins/obfs2.c b/src/plugins/obfs2.c deleted file mode 100644 index cac7bb2..0000000 --- a/src/plugins/obfs2.c +++ /dev/null @@ -1,405 +0,0 @@ -/* Copyright 2011 Nick Mathewson - - You may do anything with this work that copyright law would normally - restrict, so long as you retain the above notice(s) and this license - in all redistributed copies and derived works. There is no warranty. -*/ - -#include <assert.h> -#include <string.h> -#include <stdlib.h> -#include <stdio.h> - -#include <openssl/rand.h> -#include <event2/buffer.h> - -#define CRYPT_PROTOCOL_PRIVATE - -#include "obfs2_crypt.h" -#include "obfs2.h" -#include "../util.h" -#include "../protocol.h" - -static void obfs2_state_free(void *state); -static int obfs2_send_initial_message(void *state, struct evbuffer *buf); -static int obfs2_send(void *state, - struct evbuffer *source, struct evbuffer *dest); -static int obfs2_recv(void *state, struct evbuffer *source, - struct evbuffer *dest); -static void *obfs2_state_new(int initiator); - -static protocol_vtable *vtable=NULL; - -/* Sets the function table for the obfs2 protocol and - calls initialize_crypto(). - Returns 0 on success, -1 on fail. -*/ -int -obfs2_init(void) { - vtable = calloc(1, sizeof(protocol_vtable)); - if (!vtable) - return -1; - - vtable->destroy = obfs2_state_free; - vtable->create = obfs2_new; - vtable->handshake = obfs2_send_initial_message; - vtable->send = obfs2_send; - vtable->recv = obfs2_recv; - - if (initialize_crypto() < 0) { - fprintf(stderr, "Can't initialize crypto; failing\n"); - return -1; - } - - return 1; -} - -/** Return true iff the OBFUSCATE_SEED_LENGTH-byte seed in 'seed' is nonzero */ -static int -seed_nonzero(const uchar *seed) -{ - return memcmp(seed, OBFUSCATE_ZERO_SEED, OBFUSCATE_SEED_LENGTH) != 0; -} - -/** - Derive and return key of type 'keytype' from the seeds currently set in - 'state'. Returns NULL on failure. - */ -static crypt_t * -derive_key(void *s, const char *keytype) -{ - obfs2_state_t *state = s; - - crypt_t *cryptstate; - uchar buf[32]; - digest_t *c = digest_new(); - digest_update(c, (uchar*)keytype, strlen(keytype)); - if (seed_nonzero(state->initiator_seed)) - digest_update(c, state->initiator_seed, OBFUSCATE_SEED_LENGTH); - if (seed_nonzero(state->responder_seed)) - digest_update(c, state->responder_seed, OBFUSCATE_SEED_LENGTH); - if (seed_nonzero(state->secret_seed)) - digest_update(c, state->secret_seed, SHARED_SECRET_LENGTH); - digest_update(c, (uchar*)keytype, strlen(keytype)); - digest_getdigest(c, buf, sizeof(buf)); - cryptstate = crypt_new(buf, 16); - crypt_set_iv(cryptstate, buf+16, 16); - memset(buf, 0, sizeof(buf)); - digest_free(c); - return cryptstate; -} - -static crypt_t * -derive_padding_key(void *s, const uchar *seed, - const char *keytype) -{ - obfs2_state_t *state = s; - - crypt_t *cryptstate; - uchar buf[32]; - digest_t *c = digest_new(); - digest_update(c, (uchar*)keytype, strlen(keytype)); - if (seed_nonzero(seed)) - digest_update(c, seed, OBFUSCATE_SEED_LENGTH); - if (seed_nonzero(state->secret_seed)) - digest_update(c, state->secret_seed, OBFUSCATE_SEED_LENGTH); - digest_update(c, (uchar*)keytype, strlen(keytype)); - digest_getdigest(c, buf, sizeof(buf)); - cryptstate = crypt_new(buf, 16); - crypt_set_iv(cryptstate, buf+16, 16); - memset(buf, 0, 16); - digest_free(c); - return cryptstate; -} - -void * -obfs2_new(struct protocol_t *proto_struct, int initiator) { - assert(vtable); - proto_struct->vtable = vtable; - - return obfs2_state_new(initiator); -} - -/** - Return a new object to handle protocol state. If 'initiator' is true, - we're the handshake initiator. Otherwise, we're the responder. Return - NULL on failure. - */ -static void * -obfs2_state_new(int initiator) -{ - obfs2_state_t *state = calloc(1, sizeof(obfs2_state_t)); - uchar *seed; - const char *send_pad_type; - - if (!state) - return NULL; - state->state = ST_WAIT_FOR_KEY; - state->we_are_initiator = initiator; - if (initiator) { - send_pad_type = INITIATOR_PAD_TYPE; - seed = state->initiator_seed; - } else { - send_pad_type = RESPONDER_PAD_TYPE; - seed = state->responder_seed; - } - - /* Generate our seed */ - if (random_bytes(seed, OBFUSCATE_SEED_LENGTH) < 0) { - free(state); - return NULL; - } - - /* Derive the key for what we're sending */ - state->send_padding_crypto = derive_padding_key(state, seed, send_pad_type); - if (state->send_padding_crypto == NULL) { - free(state); - return NULL; - } - - return state; -} - -/** Set the shared secret to be used with this protocol state. */ -void -obfs2_state_set_shared_secret(void *s, - const char *secret, size_t secretlen) -{ - obfs2_state_t *state = s; - - if (secretlen > SHARED_SECRET_LENGTH) - secretlen = SHARED_SECRET_LENGTH; - memcpy(state->secret_seed, secret, secretlen); -} - -/** - Write the initial protocol setup and padding message for 'state' to - the evbuffer 'buf'. Return 0 on success, -1 on failure. - */ -static int -obfs2_send_initial_message(void *s, struct evbuffer *buf) -{ - obfs2_state_t *state = s; - - uint32_t magic = htonl(OBFUSCATE_MAGIC_VALUE), plength, send_plength; - uchar msg[OBFUSCATE_MAX_PADDING + OBFUSCATE_SEED_LENGTH + 8]; - const uchar *seed; - - /* We're going to send: - SEED | E_PAD_KEY( UINT32(MAGIC_VALUE) | UINT32(PADLEN) | WR(PADLEN) ) - */ - - assert(sizeof(magic) == 4); - - /* generate padlen */ - if (random_bytes((uchar*)&plength, 4) < 0) - return -1; - plength %= OBFUSCATE_MAX_PADDING; - send_plength = htonl(plength); - - if (state->we_are_initiator) - seed = state->initiator_seed; - else - seed = state->responder_seed; - - /* Marshal the message, but with no parts encrypted */ - memcpy(msg, seed, OBFUSCATE_SEED_LENGTH); - memcpy(msg+OBFUSCATE_SEED_LENGTH, &magic, 4); - memcpy(msg+OBFUSCATE_SEED_LENGTH+4, &send_plength, 4); - if (random_bytes(msg+OBFUSCATE_SEED_LENGTH+8, plength) < 0) - return -1; - - /* Encrypt it */ - stream_crypt(state->send_padding_crypto, - msg+OBFUSCATE_SEED_LENGTH, 8+plength); - - /* Put it on the buffer */ - evbuffer_add(buf, msg, OBFUSCATE_SEED_LENGTH+8+plength); - return 0; -} - -/** - Helper: encrypt every byte from 'source' using the key in 'crypto', - and write those bytes onto 'dest'. Return 0 on success, -1 on failure. - */ -static int -crypt_and_transmit(crypt_t *crypto, - struct evbuffer *source, struct evbuffer *dest) -{ - uchar data[1024]; - while (1) { - int n = evbuffer_remove(source, data, 1024); - if (n <= 0) - return 0; - stream_crypt(crypto, data, n); - // printf("Message is: %s", data); - evbuffer_add(dest, data, n); - dbg(("Processed %d bytes.", n)); - } -} - -/** - Called when data arrives from the user side and we want to send the - obfuscated version. Copies and obfuscates data from 'source' into 'dest' - using the state in 'state'. Returns 0 on success, -1 on failure. - */ -static int -obfs2_send(void *s, - struct evbuffer *source, struct evbuffer *dest) -{ - obfs2_state_t *state = s; - - if (state->send_crypto) { - /* Our crypto is set up; just relay the bytes */ - return crypt_and_transmit(state->send_crypto, source, dest); - } else { - /* Our crypto isn't set up yet, we'll have to queue the data */ - if (evbuffer_get_length(source)) { - if (! state->pending_data_to_send) { - state->pending_data_to_send = evbuffer_new(); - } - evbuffer_add_buffer(state->pending_data_to_send, source); - } - return 0; - } -} - -/** - Helper: called after reciving our partner's setup message. Initializes all - keys. Returns 0 on success, -1 on failure. - */ -static int -init_crypto(void *s) -{ - obfs2_state_t *state = s; - - const char *send_keytype; - const char *recv_keytype; - const char *recv_pad_keytype; - const uchar *recv_seed; - - if (state->we_are_initiator) { - send_keytype = INITIATOR_SEND_TYPE; - recv_keytype = RESPONDER_SEND_TYPE; - recv_pad_keytype = RESPONDER_PAD_TYPE; - recv_seed = state->responder_seed; - } else { - send_keytype = RESPONDER_SEND_TYPE; - recv_keytype = INITIATOR_SEND_TYPE; - recv_pad_keytype = INITIATOR_PAD_TYPE; - recv_seed = state->initiator_seed; - } - - /* Derive all of the keys that depend on our partner's seed */ - state->send_crypto = derive_key(state, send_keytype); - state->recv_crypto = derive_key(state, recv_keytype); - state->recv_padding_crypto = - derive_padding_key(state, recv_seed, recv_pad_keytype); - - if (state->send_crypto && state->recv_crypto && state->recv_padding_crypto) - return 0; - else - return -1; -} - -/* Called when we receive data in an evbuffer 'source': deobfuscates that data - * and writes it to 'dest'. - * - * Returns x for "don't call again till you have x bytes". 0 for "all ok". -1 - * for "fail, close" */ -static int -obfs2_recv(void *s, struct evbuffer *source, - struct evbuffer *dest) -{ - obfs2_state_t *state = s; - - if (state->state == ST_WAIT_FOR_KEY) { - /* We're waiting for the first OBFUSCATE_SEED_LENGTH+8 bytes to show up - * so we can learn the partner's seed and padding length */ - uchar buf[OBFUSCATE_SEED_LENGTH+8], *other_seed; - uint32_t magic, plength; - if (evbuffer_get_length(source) < OBFUSCATE_SEED_LENGTH+8) { - /* data not here yet */ - return OBFUSCATE_SEED_LENGTH+8; - } - evbuffer_remove(source, buf, OBFUSCATE_SEED_LENGTH+8); - - if (state->we_are_initiator) - other_seed = state->responder_seed; - else - other_seed = state->initiator_seed; - - memcpy(other_seed, buf, OBFUSCATE_SEED_LENGTH); - - /* Now we can set up all the keys from the seed */ - if (init_crypto(state) < 0) - return -1; - - /* Decrypt the next 8 bytes */ - stream_crypt(state->recv_padding_crypto, buf+OBFUSCATE_SEED_LENGTH, 8); - /* Check the magic number and extract the padding length */ - memcpy(&magic, buf+OBFUSCATE_SEED_LENGTH, 4); - memcpy(&plength, buf+OBFUSCATE_SEED_LENGTH+4, 4); - magic = ntohl(magic); - plength = ntohl(plength); - if (magic != OBFUSCATE_MAGIC_VALUE) - return -1; - if (plength > OBFUSCATE_MAX_PADDING) - return -1; - - /* Send any data that we've been waiting to send */ - if (state->pending_data_to_send) { - crypt_and_transmit(state->send_crypto, state->pending_data_to_send, dest); - evbuffer_free(state->pending_data_to_send); - state->pending_data_to_send = NULL; - } - - /* Now we're waiting for plength bytes of padding */ - state->padding_left_to_read = plength; - state->state = ST_WAIT_FOR_PADDING; - - /* Fall through here: if there is padding data waiting on the buffer, pull - it off immediately. */ - dbg(("Received key, expecting %d bytes of padding\n", plength)); - } - - /* If we're still looking for padding, start pulling off bytes and - discarding them. */ - while (state->padding_left_to_read) { - int n = state->padding_left_to_read; - size_t sourcelen = evbuffer_get_length(source); - if (!sourcelen) - return n; - if ((size_t) n > evbuffer_get_length(source)) - n = evbuffer_get_length(source); - evbuffer_drain(source, n); - state->padding_left_to_read -= n; - dbg(("Received %d bytes of padding; %d left to read\n", n, - state->padding_left_to_read)); - } - - /* Okay; now we're definitely open. Process whatever data we have. */ - state->state = ST_OPEN; - - dbg(("Processing %d bytes data onto destination buffer\n", - (int) evbuffer_get_length(source))); - return crypt_and_transmit(state->recv_crypto, source, dest); -} - -static void -obfs2_state_free(void *s) -{ - obfs2_state_t *state = s; - if (state->send_crypto) - crypt_free(state->send_crypto); - if (state->send_padding_crypto) - crypt_free(state->send_padding_crypto); - if (state->recv_crypto) - crypt_free(state->recv_crypto); - if (state->recv_padding_crypto) - crypt_free(state->recv_padding_crypto); - if (state->pending_data_to_send) - evbuffer_free(state->pending_data_to_send); - memset(state, 0x0a, sizeof(obfs2_state_t)); - free(state); -} diff --git a/src/plugins/obfs2.h b/src/plugins/obfs2.h deleted file mode 100644 index 3124bbc..0000000 --- a/src/plugins/obfs2.h +++ /dev/null @@ -1,81 +0,0 @@ -/* Copyright 2011 Nick Mathewson - - You may do anything with this work that copyright law would normally - restrict, so long as you retain the above notice(s) and this license - in all redistributed copies and derived works. There is no warranty. -*/ - -#ifndef OBFS2_H -#define OBFS2_H - -#include <sys/types.h> - -typedef struct obfs2_state_t obfs2_state_t; -struct evbuffer; -struct protocol_t; - -#define SHARED_SECRET_LENGTH 16 - -void obfs2_state_set_shared_secret(void *state, - const char *secret, size_t secretlen); -int obfs2_init(void); -void *obfs2_new(struct protocol_t *proto_struct, int initiator); - - -#ifdef CRYPT_PROTOCOL_PRIVATE -/* ========== - These definitions are not part of the crypt_protocol interface. - They're exposed here so that the unit tests can use them. - ========== -*/ -/* from brl's obfuscated-ssh standard. */ -//#define OBFUSCATE_MAGIC_VALUE 0x0BF5CA7E - -/* our own, since we break brl's spec */ -#define OBFUSCATE_MAGIC_VALUE 0x2BF5CA7E -#define OBFUSCATE_SEED_LENGTH 16 -#define OBFUSCATE_MAX_PADDING 8192 -#define OBFUSCATE_ZERO_SEED "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - -#define INITIATOR_PAD_TYPE "Initiator obfuscation padding" -#define RESPONDER_PAD_TYPE "Responder obfuscation padding" -#define INITIATOR_SEND_TYPE "Initiator obfuscated data" -#define RESPONDER_SEND_TYPE "Responder obfuscated data" - -struct obfs2_state_t { - /** Current protocol state. We start out waiting for key information. Then - we have a key and wait for padding to arrive. Finally, we are sending - and receiving bytes on the connection. - */ - enum { - ST_WAIT_FOR_KEY, - ST_WAIT_FOR_PADDING, - ST_OPEN, - } state; - /** Random seed we generated for this stream */ - uchar initiator_seed[OBFUSCATE_SEED_LENGTH]; - /** Random seed the other side generated for this stream */ - uchar responder_seed[OBFUSCATE_SEED_LENGTH]; - /** Shared secret seed value. */ - uchar secret_seed[SHARED_SECRET_LENGTH]; - /** True iff we opened this connection */ - int we_are_initiator; - - /** key used to encrypt outgoing data */ - crypt_t *send_crypto; - /** key used to encrypt outgoing padding */ - crypt_t *send_padding_crypto; - /** key used to decrypt incoming data */ - crypt_t *recv_crypto; - /** key used to decrypt incoming padding */ - crypt_t *recv_padding_crypto; - - /** Buffer full of data we'll send once the handshake is done. */ - struct evbuffer *pending_data_to_send; - - /** Number of padding bytes to read before we get to real data */ - int padding_left_to_read; -}; -#endif - -#endif diff --git a/src/plugins/obfs2_crypt.c b/src/plugins/obfs2_crypt.c deleted file mode 100644 index 0121c93..0000000 --- a/src/plugins/obfs2_crypt.c +++ /dev/null @@ -1,206 +0,0 @@ -/* Copyright 2011 Nick Mathewson - - You may do anything with this work that copyright law would normally - restrict, so long as you retain the above notice(s) and this license - in all redistributed copies and derived works. There is no warranty. -*/ - -#include "config.h" - -#include <assert.h> -#include <string.h> -#include <stdlib.h> -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif -#ifdef HAVE_FCNTL_H -#include <fcntl.h> -#endif -#ifdef HAVE_STDINT_H -#include <stdint.h> -#endif - -#include <openssl/opensslv.h> -#include <openssl/aes.h> -#include <openssl/rand.h> -#include <openssl/err.h> - -#define CRYPT_PRIVATE -#include "obfs2_crypt.h" - -#if OPENSSL_VERSION_NUMBER >= 0x0090800f -#define USE_OPENSSL_RANDPOLL 1 -#define USE_OPENSSL_SHA256 1 -#include <openssl/sha.h> -#else -#define STMT_BEGIN do { -#define STMT_END } while (0) -static void -set_uint32(void *ptr, uint32_t val) -{ - memcpy(ptr, &val, 4); -} -static uint32_t -get_uint32(const void *ptr) -{ - uint32_t val; - memcpy(&val, ptr, 4); - return val; -} -#define LTC_ARGCHK(x) assert((x)) -#include "sha256.c" -#endif - -int -initialize_crypto(void) -{ - ERR_load_crypto_strings(); - -#ifdef USE_OPENSSL_RANDPOLL - return RAND_poll() == 1 ? 0 : -1; -#else - /* XXX Or maybe fall back to the arc4random implementation in libevent2? */ - { - char buf[32]; - int fd, n; - fd = open("/dev/urandom", O_RDONLY); - if (fd == -1) { - perror("open"); - return -1; - } - n = read(fd, buf, sizeof(buf)); - if (n != sizeof(buf)) { - close(fd); - return -1; - } - RAND_seed(buf, sizeof(buf)); - close(fd); - return 0; - } -#endif -} - -void -cleanup_crypto(void) -{ - ERR_free_strings(); -} - -/* ===== - Digests - ===== */ - -#ifdef USE_OPENSSL_SHA256 -struct digest_t { - SHA256_CTX ctx; -}; -digest_t * -digest_new(void) -{ - digest_t *d = malloc(sizeof(digest_t)); - SHA256_Init(&d->ctx); - return d; -} -void -digest_update(digest_t *d, const uchar *buf, size_t len) -{ - SHA256_Update(&d->ctx, buf, len); -} -size_t -digest_getdigest(digest_t *d, uchar *buf, size_t len) -{ - uchar tmp[32]; - int n = 32; - SHA256_Final(tmp, &d->ctx); - if (len < 32) - n = len; - memcpy(buf, tmp, n); - memset(tmp, 0, sizeof(tmp)); - return n; -} -#else -struct digest_t { - sha256_state ctx; -}; -digest_t * -digest_new(void) -{ - digest_t *d = malloc(sizeof(digest_t)); - sha256_init(&d->ctx); - return d; -} -void -digest_update(digest_t *d, const uchar *buf, size_t len) -{ - sha256_process(&d->ctx, buf, len); -} -size_t -digest_getdigest(digest_t *d, uchar *buf, size_t len) -{ - uchar tmp[32]; - int n = 32; - sha256_done(&d->ctx, tmp); - if (len < 32) - n = len; - memcpy(buf, tmp, n); - memset(tmp, 0, sizeof(tmp)); - return n; -} -#endif - -void -digest_free(digest_t *d) -{ - memset(d, 0, sizeof(digest_t)); - free(d); -} - -/* ===== - Stream crypto - ===== */ - -crypt_t * -crypt_new(const uchar *key, size_t keylen) -{ - crypt_t *k; - if (keylen < AES_BLOCK_SIZE) - return NULL; - - k = calloc(1, sizeof(crypt_t)); - if (k == NULL) - return NULL; - - AES_set_encrypt_key(key, 128, &k->key); - - return k; -} -void -crypt_set_iv(crypt_t *key, const uchar *iv, size_t ivlen) -{ - assert(ivlen == sizeof(key->ivec)); - memcpy(key->ivec, iv, ivlen); -} -void -stream_crypt(crypt_t *key, uchar *buf, size_t len) -{ - AES_ctr128_encrypt(buf, buf, /* XXX make sure this is okay to do. */ - len, - &key->key, key->ivec, key->ecount_buf, - &key->pos); -} -void -crypt_free(crypt_t *key) -{ - memset(key, 0, sizeof(key)); - free(key); -} - -/* ===== - PRNG - ===== */ - -int -random_bytes(uchar *buf, size_t buflen) -{ - return RAND_bytes(buf, buflen) == 1 ? 0 : -1; -} diff --git a/src/plugins/obfs2_crypt.h b/src/plugins/obfs2_crypt.h deleted file mode 100644 index c9841d8..0000000 --- a/src/plugins/obfs2_crypt.h +++ /dev/null @@ -1,62 +0,0 @@ -/* Copyright 2011 Nick Mathewson - - You may do anything with this work that copyright law would normally - restrict, so long as you retain the above notice(s) and this license - in all redistributed copies and derived works. There is no warranty. -*/ - -#ifndef OBFS2_CRYPT_H -#define OBFS2_CRYPT_H - -#include <sys/types.h> - -/* Stream cipher state */ -typedef struct crypt_t crypt_t; -/* Digest state */ -typedef struct digest_t digest_t; - -typedef unsigned char uchar; - -/** Initialize global crypto state. Returrn 0 on success, -1 on failure */ -int initialize_crypto(void); -/** Clean up global crypto state */ -void cleanup_crypto(void); - -/** Return a newly allocated digest state, or NULL on failure. */ -digest_t *digest_new(void); -/** Add n bytes from b to the digest state. */ -void digest_update(digest_t *, const uchar *b, size_t n); -/** Get a digest from the digest state. Put it in up the first n bytes of the -buffer b. Return the number of bytes actually written.*/ -size_t digest_getdigest(digest_t *, uchar *b, size_t n); -/** Clear and free a digest state */ -void digest_free(digest_t *); - -/** Return a new stream cipher state taking key and IV from the data provided. - * The data length must be exactly 32 */ -crypt_t *crypt_new(const uchar *, size_t); -void crypt_set_iv(crypt_t *key, const uchar *iv, size_t ivlen); - -/** Encrypt n bytes of data in the buffer b, in place. */ -void stream_crypt(crypt_t *, uchar *b, size_t n); -/** Clear and free a stream cipher state. */ -void crypt_free(crypt_t *); - -/** Set b to contain n random bytes. */ -int random_bytes(uchar *b, size_t n); - -#ifdef CRYPT_PRIVATE -/* ========== - These definitions are not part of the crypt interface. - They're exposed here so that the unit tests can use them. - ========== -*/ -struct crypt_t { - AES_KEY key; - uchar ivec[AES_BLOCK_SIZE]; - uchar ecount_buf[AES_BLOCK_SIZE]; - unsigned int pos; -}; -#endif - -#endif diff --git a/src/protocols/dummy.c b/src/protocols/dummy.c new file mode 100644 index 0000000..8fe722e --- /dev/null +++ b/src/protocols/dummy.c @@ -0,0 +1,63 @@ +#include <assert.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include <unistd.h> + +#include <openssl/rand.h> +#include <event2/buffer.h> + +#include "dummy.h" +#include "../util.h" +#include "../protocol.h" + + +static int dummy_send(void *nothing, + struct evbuffer *source, struct evbuffer *dest); +static int dummy_recv(void *nothing, struct evbuffer *source, + struct evbuffer *dest); + +static protocol_vtable *vtable=NULL; + +int +dummy_init(void) { + vtable = calloc(1, sizeof(protocol_vtable)); + if (!vtable) + return -1; + + vtable->destroy = NULL; + vtable->create = dummy_new; + vtable->handshake = NULL; + vtable->send = dummy_send; + vtable->recv = dummy_recv; + + return 1; +} + +void * +dummy_new(struct protocol_t *proto_struct, int whatever) { + (void)whatever; + + proto_struct->vtable = vtable; + + /* Dodging state check. + This is terrible I know.*/ + return (void *)666U; +} + +static int +dummy_send(void *nothing, + struct evbuffer *source, struct evbuffer *dest) { + (void)nothing; + + return evbuffer_add_buffer(dest,source); +} + +static int +dummy_recv(void *nothing, + struct evbuffer *source, struct evbuffer *dest) { + (void)nothing; + + return evbuffer_add_buffer(dest,source); +} diff --git a/src/protocols/dummy.h b/src/protocols/dummy.h new file mode 100644 index 0000000..241366d --- /dev/null +++ b/src/protocols/dummy.h @@ -0,0 +1,10 @@ +#ifndef DUMMY_H +#define DUMMY_H + +struct protocol_t; +struct evbuffer; + +int dummy_init(void); +void *dummy_new(struct protocol_t *proto_struct, int whatever); + +#endif diff --git a/src/protocols/obfs2.c b/src/protocols/obfs2.c new file mode 100644 index 0000000..cac7bb2 --- /dev/null +++ b/src/protocols/obfs2.c @@ -0,0 +1,405 @@ +/* Copyright 2011 Nick Mathewson + + You may do anything with this work that copyright law would normally + restrict, so long as you retain the above notice(s) and this license + in all redistributed copies and derived works. There is no warranty. +*/ + +#include <assert.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include <openssl/rand.h> +#include <event2/buffer.h> + +#define CRYPT_PROTOCOL_PRIVATE + +#include "obfs2_crypt.h" +#include "obfs2.h" +#include "../util.h" +#include "../protocol.h" + +static void obfs2_state_free(void *state); +static int obfs2_send_initial_message(void *state, struct evbuffer *buf); +static int obfs2_send(void *state, + struct evbuffer *source, struct evbuffer *dest); +static int obfs2_recv(void *state, struct evbuffer *source, + struct evbuffer *dest); +static void *obfs2_state_new(int initiator); + +static protocol_vtable *vtable=NULL; + +/* Sets the function table for the obfs2 protocol and + calls initialize_crypto(). + Returns 0 on success, -1 on fail. +*/ +int +obfs2_init(void) { + vtable = calloc(1, sizeof(protocol_vtable)); + if (!vtable) + return -1; + + vtable->destroy = obfs2_state_free; + vtable->create = obfs2_new; + vtable->handshake = obfs2_send_initial_message; + vtable->send = obfs2_send; + vtable->recv = obfs2_recv; + + if (initialize_crypto() < 0) { + fprintf(stderr, "Can't initialize crypto; failing\n"); + return -1; + } + + return 1; +} + +/** Return true iff the OBFUSCATE_SEED_LENGTH-byte seed in 'seed' is nonzero */ +static int +seed_nonzero(const uchar *seed) +{ + return memcmp(seed, OBFUSCATE_ZERO_SEED, OBFUSCATE_SEED_LENGTH) != 0; +} + +/** + Derive and return key of type 'keytype' from the seeds currently set in + 'state'. Returns NULL on failure. + */ +static crypt_t * +derive_key(void *s, const char *keytype) +{ + obfs2_state_t *state = s; + + crypt_t *cryptstate; + uchar buf[32]; + digest_t *c = digest_new(); + digest_update(c, (uchar*)keytype, strlen(keytype)); + if (seed_nonzero(state->initiator_seed)) + digest_update(c, state->initiator_seed, OBFUSCATE_SEED_LENGTH); + if (seed_nonzero(state->responder_seed)) + digest_update(c, state->responder_seed, OBFUSCATE_SEED_LENGTH); + if (seed_nonzero(state->secret_seed)) + digest_update(c, state->secret_seed, SHARED_SECRET_LENGTH); + digest_update(c, (uchar*)keytype, strlen(keytype)); + digest_getdigest(c, buf, sizeof(buf)); + cryptstate = crypt_new(buf, 16); + crypt_set_iv(cryptstate, buf+16, 16); + memset(buf, 0, sizeof(buf)); + digest_free(c); + return cryptstate; +} + +static crypt_t * +derive_padding_key(void *s, const uchar *seed, + const char *keytype) +{ + obfs2_state_t *state = s; + + crypt_t *cryptstate; + uchar buf[32]; + digest_t *c = digest_new(); + digest_update(c, (uchar*)keytype, strlen(keytype)); + if (seed_nonzero(seed)) + digest_update(c, seed, OBFUSCATE_SEED_LENGTH); + if (seed_nonzero(state->secret_seed)) + digest_update(c, state->secret_seed, OBFUSCATE_SEED_LENGTH); + digest_update(c, (uchar*)keytype, strlen(keytype)); + digest_getdigest(c, buf, sizeof(buf)); + cryptstate = crypt_new(buf, 16); + crypt_set_iv(cryptstate, buf+16, 16); + memset(buf, 0, 16); + digest_free(c); + return cryptstate; +} + +void * +obfs2_new(struct protocol_t *proto_struct, int initiator) { + assert(vtable); + proto_struct->vtable = vtable; + + return obfs2_state_new(initiator); +} + +/** + Return a new object to handle protocol state. If 'initiator' is true, + we're the handshake initiator. Otherwise, we're the responder. Return + NULL on failure. + */ +static void * +obfs2_state_new(int initiator) +{ + obfs2_state_t *state = calloc(1, sizeof(obfs2_state_t)); + uchar *seed; + const char *send_pad_type; + + if (!state) + return NULL; + state->state = ST_WAIT_FOR_KEY; + state->we_are_initiator = initiator; + if (initiator) { + send_pad_type = INITIATOR_PAD_TYPE; + seed = state->initiator_seed; + } else { + send_pad_type = RESPONDER_PAD_TYPE; + seed = state->responder_seed; + } + + /* Generate our seed */ + if (random_bytes(seed, OBFUSCATE_SEED_LENGTH) < 0) { + free(state); + return NULL; + } + + /* Derive the key for what we're sending */ + state->send_padding_crypto = derive_padding_key(state, seed, send_pad_type); + if (state->send_padding_crypto == NULL) { + free(state); + return NULL; + } + + return state; +} + +/** Set the shared secret to be used with this protocol state. */ +void +obfs2_state_set_shared_secret(void *s, + const char *secret, size_t secretlen) +{ + obfs2_state_t *state = s; + + if (secretlen > SHARED_SECRET_LENGTH) + secretlen = SHARED_SECRET_LENGTH; + memcpy(state->secret_seed, secret, secretlen); +} + +/** + Write the initial protocol setup and padding message for 'state' to + the evbuffer 'buf'. Return 0 on success, -1 on failure. + */ +static int +obfs2_send_initial_message(void *s, struct evbuffer *buf) +{ + obfs2_state_t *state = s; + + uint32_t magic = htonl(OBFUSCATE_MAGIC_VALUE), plength, send_plength; + uchar msg[OBFUSCATE_MAX_PADDING + OBFUSCATE_SEED_LENGTH + 8]; + const uchar *seed; + + /* We're going to send: + SEED | E_PAD_KEY( UINT32(MAGIC_VALUE) | UINT32(PADLEN) | WR(PADLEN) ) + */ + + assert(sizeof(magic) == 4); + + /* generate padlen */ + if (random_bytes((uchar*)&plength, 4) < 0) + return -1; + plength %= OBFUSCATE_MAX_PADDING; + send_plength = htonl(plength); + + if (state->we_are_initiator) + seed = state->initiator_seed; + else + seed = state->responder_seed; + + /* Marshal the message, but with no parts encrypted */ + memcpy(msg, seed, OBFUSCATE_SEED_LENGTH); + memcpy(msg+OBFUSCATE_SEED_LENGTH, &magic, 4); + memcpy(msg+OBFUSCATE_SEED_LENGTH+4, &send_plength, 4); + if (random_bytes(msg+OBFUSCATE_SEED_LENGTH+8, plength) < 0) + return -1; + + /* Encrypt it */ + stream_crypt(state->send_padding_crypto, + msg+OBFUSCATE_SEED_LENGTH, 8+plength); + + /* Put it on the buffer */ + evbuffer_add(buf, msg, OBFUSCATE_SEED_LENGTH+8+plength); + return 0; +} + +/** + Helper: encrypt every byte from 'source' using the key in 'crypto', + and write those bytes onto 'dest'. Return 0 on success, -1 on failure. + */ +static int +crypt_and_transmit(crypt_t *crypto, + struct evbuffer *source, struct evbuffer *dest) +{ + uchar data[1024]; + while (1) { + int n = evbuffer_remove(source, data, 1024); + if (n <= 0) + return 0; + stream_crypt(crypto, data, n); + // printf("Message is: %s", data); + evbuffer_add(dest, data, n); + dbg(("Processed %d bytes.", n)); + } +} + +/** + Called when data arrives from the user side and we want to send the + obfuscated version. Copies and obfuscates data from 'source' into 'dest' + using the state in 'state'. Returns 0 on success, -1 on failure. + */ +static int +obfs2_send(void *s, + struct evbuffer *source, struct evbuffer *dest) +{ + obfs2_state_t *state = s; + + if (state->send_crypto) { + /* Our crypto is set up; just relay the bytes */ + return crypt_and_transmit(state->send_crypto, source, dest); + } else { + /* Our crypto isn't set up yet, we'll have to queue the data */ + if (evbuffer_get_length(source)) { + if (! state->pending_data_to_send) { + state->pending_data_to_send = evbuffer_new(); + } + evbuffer_add_buffer(state->pending_data_to_send, source); + } + return 0; + } +} + +/** + Helper: called after reciving our partner's setup message. Initializes all + keys. Returns 0 on success, -1 on failure. + */ +static int +init_crypto(void *s) +{ + obfs2_state_t *state = s; + + const char *send_keytype; + const char *recv_keytype; + const char *recv_pad_keytype; + const uchar *recv_seed; + + if (state->we_are_initiator) { + send_keytype = INITIATOR_SEND_TYPE; + recv_keytype = RESPONDER_SEND_TYPE; + recv_pad_keytype = RESPONDER_PAD_TYPE; + recv_seed = state->responder_seed; + } else { + send_keytype = RESPONDER_SEND_TYPE; + recv_keytype = INITIATOR_SEND_TYPE; + recv_pad_keytype = INITIATOR_PAD_TYPE; + recv_seed = state->initiator_seed; + } + + /* Derive all of the keys that depend on our partner's seed */ + state->send_crypto = derive_key(state, send_keytype); + state->recv_crypto = derive_key(state, recv_keytype); + state->recv_padding_crypto = + derive_padding_key(state, recv_seed, recv_pad_keytype); + + if (state->send_crypto && state->recv_crypto && state->recv_padding_crypto) + return 0; + else + return -1; +} + +/* Called when we receive data in an evbuffer 'source': deobfuscates that data + * and writes it to 'dest'. + * + * Returns x for "don't call again till you have x bytes". 0 for "all ok". -1 + * for "fail, close" */ +static int +obfs2_recv(void *s, struct evbuffer *source, + struct evbuffer *dest) +{ + obfs2_state_t *state = s; + + if (state->state == ST_WAIT_FOR_KEY) { + /* We're waiting for the first OBFUSCATE_SEED_LENGTH+8 bytes to show up + * so we can learn the partner's seed and padding length */ + uchar buf[OBFUSCATE_SEED_LENGTH+8], *other_seed; + uint32_t magic, plength; + if (evbuffer_get_length(source) < OBFUSCATE_SEED_LENGTH+8) { + /* data not here yet */ + return OBFUSCATE_SEED_LENGTH+8; + } + evbuffer_remove(source, buf, OBFUSCATE_SEED_LENGTH+8); + + if (state->we_are_initiator) + other_seed = state->responder_seed; + else + other_seed = state->initiator_seed; + + memcpy(other_seed, buf, OBFUSCATE_SEED_LENGTH); + + /* Now we can set up all the keys from the seed */ + if (init_crypto(state) < 0) + return -1; + + /* Decrypt the next 8 bytes */ + stream_crypt(state->recv_padding_crypto, buf+OBFUSCATE_SEED_LENGTH, 8); + /* Check the magic number and extract the padding length */ + memcpy(&magic, buf+OBFUSCATE_SEED_LENGTH, 4); + memcpy(&plength, buf+OBFUSCATE_SEED_LENGTH+4, 4); + magic = ntohl(magic); + plength = ntohl(plength); + if (magic != OBFUSCATE_MAGIC_VALUE) + return -1; + if (plength > OBFUSCATE_MAX_PADDING) + return -1; + + /* Send any data that we've been waiting to send */ + if (state->pending_data_to_send) { + crypt_and_transmit(state->send_crypto, state->pending_data_to_send, dest); + evbuffer_free(state->pending_data_to_send); + state->pending_data_to_send = NULL; + } + + /* Now we're waiting for plength bytes of padding */ + state->padding_left_to_read = plength; + state->state = ST_WAIT_FOR_PADDING; + + /* Fall through here: if there is padding data waiting on the buffer, pull + it off immediately. */ + dbg(("Received key, expecting %d bytes of padding\n", plength)); + } + + /* If we're still looking for padding, start pulling off bytes and + discarding them. */ + while (state->padding_left_to_read) { + int n = state->padding_left_to_read; + size_t sourcelen = evbuffer_get_length(source); + if (!sourcelen) + return n; + if ((size_t) n > evbuffer_get_length(source)) + n = evbuffer_get_length(source); + evbuffer_drain(source, n); + state->padding_left_to_read -= n; + dbg(("Received %d bytes of padding; %d left to read\n", n, + state->padding_left_to_read)); + } + + /* Okay; now we're definitely open. Process whatever data we have. */ + state->state = ST_OPEN; + + dbg(("Processing %d bytes data onto destination buffer\n", + (int) evbuffer_get_length(source))); + return crypt_and_transmit(state->recv_crypto, source, dest); +} + +static void +obfs2_state_free(void *s) +{ + obfs2_state_t *state = s; + if (state->send_crypto) + crypt_free(state->send_crypto); + if (state->send_padding_crypto) + crypt_free(state->send_padding_crypto); + if (state->recv_crypto) + crypt_free(state->recv_crypto); + if (state->recv_padding_crypto) + crypt_free(state->recv_padding_crypto); + if (state->pending_data_to_send) + evbuffer_free(state->pending_data_to_send); + memset(state, 0x0a, sizeof(obfs2_state_t)); + free(state); +} diff --git a/src/protocols/obfs2.h b/src/protocols/obfs2.h new file mode 100644 index 0000000..3124bbc --- /dev/null +++ b/src/protocols/obfs2.h @@ -0,0 +1,81 @@ +/* Copyright 2011 Nick Mathewson + + You may do anything with this work that copyright law would normally + restrict, so long as you retain the above notice(s) and this license + in all redistributed copies and derived works. There is no warranty. +*/ + +#ifndef OBFS2_H +#define OBFS2_H + +#include <sys/types.h> + +typedef struct obfs2_state_t obfs2_state_t; +struct evbuffer; +struct protocol_t; + +#define SHARED_SECRET_LENGTH 16 + +void obfs2_state_set_shared_secret(void *state, + const char *secret, size_t secretlen); +int obfs2_init(void); +void *obfs2_new(struct protocol_t *proto_struct, int initiator); + + +#ifdef CRYPT_PROTOCOL_PRIVATE +/* ========== + These definitions are not part of the crypt_protocol interface. + They're exposed here so that the unit tests can use them. + ========== +*/ +/* from brl's obfuscated-ssh standard. */ +//#define OBFUSCATE_MAGIC_VALUE 0x0BF5CA7E + +/* our own, since we break brl's spec */ +#define OBFUSCATE_MAGIC_VALUE 0x2BF5CA7E +#define OBFUSCATE_SEED_LENGTH 16 +#define OBFUSCATE_MAX_PADDING 8192 +#define OBFUSCATE_ZERO_SEED "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + +#define INITIATOR_PAD_TYPE "Initiator obfuscation padding" +#define RESPONDER_PAD_TYPE "Responder obfuscation padding" +#define INITIATOR_SEND_TYPE "Initiator obfuscated data" +#define RESPONDER_SEND_TYPE "Responder obfuscated data" + +struct obfs2_state_t { + /** Current protocol state. We start out waiting for key information. Then + we have a key and wait for padding to arrive. Finally, we are sending + and receiving bytes on the connection. + */ + enum { + ST_WAIT_FOR_KEY, + ST_WAIT_FOR_PADDING, + ST_OPEN, + } state; + /** Random seed we generated for this stream */ + uchar initiator_seed[OBFUSCATE_SEED_LENGTH]; + /** Random seed the other side generated for this stream */ + uchar responder_seed[OBFUSCATE_SEED_LENGTH]; + /** Shared secret seed value. */ + uchar secret_seed[SHARED_SECRET_LENGTH]; + /** True iff we opened this connection */ + int we_are_initiator; + + /** key used to encrypt outgoing data */ + crypt_t *send_crypto; + /** key used to encrypt outgoing padding */ + crypt_t *send_padding_crypto; + /** key used to decrypt incoming data */ + crypt_t *recv_crypto; + /** key used to decrypt incoming padding */ + crypt_t *recv_padding_crypto; + + /** Buffer full of data we'll send once the handshake is done. */ + struct evbuffer *pending_data_to_send; + + /** Number of padding bytes to read before we get to real data */ + int padding_left_to_read; +}; +#endif + +#endif diff --git a/src/protocols/obfs2_crypt.c b/src/protocols/obfs2_crypt.c new file mode 100644 index 0000000..0121c93 --- /dev/null +++ b/src/protocols/obfs2_crypt.c @@ -0,0 +1,206 @@ +/* Copyright 2011 Nick Mathewson + + You may do anything with this work that copyright law would normally + restrict, so long as you retain the above notice(s) and this license + in all redistributed copies and derived works. There is no warranty. +*/ + +#include "config.h" + +#include <assert.h> +#include <string.h> +#include <stdlib.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#include <openssl/opensslv.h> +#include <openssl/aes.h> +#include <openssl/rand.h> +#include <openssl/err.h> + +#define CRYPT_PRIVATE +#include "obfs2_crypt.h" + +#if OPENSSL_VERSION_NUMBER >= 0x0090800f +#define USE_OPENSSL_RANDPOLL 1 +#define USE_OPENSSL_SHA256 1 +#include <openssl/sha.h> +#else +#define STMT_BEGIN do { +#define STMT_END } while (0) +static void +set_uint32(void *ptr, uint32_t val) +{ + memcpy(ptr, &val, 4); +} +static uint32_t +get_uint32(const void *ptr) +{ + uint32_t val; + memcpy(&val, ptr, 4); + return val; +} +#define LTC_ARGCHK(x) assert((x)) +#include "sha256.c" +#endif + +int +initialize_crypto(void) +{ + ERR_load_crypto_strings(); + +#ifdef USE_OPENSSL_RANDPOLL + return RAND_poll() == 1 ? 0 : -1; +#else + /* XXX Or maybe fall back to the arc4random implementation in libevent2? */ + { + char buf[32]; + int fd, n; + fd = open("/dev/urandom", O_RDONLY); + if (fd == -1) { + perror("open"); + return -1; + } + n = read(fd, buf, sizeof(buf)); + if (n != sizeof(buf)) { + close(fd); + return -1; + } + RAND_seed(buf, sizeof(buf)); + close(fd); + return 0; + } +#endif +} + +void +cleanup_crypto(void) +{ + ERR_free_strings(); +} + +/* ===== + Digests + ===== */ + +#ifdef USE_OPENSSL_SHA256 +struct digest_t { + SHA256_CTX ctx; +}; +digest_t * +digest_new(void) +{ + digest_t *d = malloc(sizeof(digest_t)); + SHA256_Init(&d->ctx); + return d; +} +void +digest_update(digest_t *d, const uchar *buf, size_t len) +{ + SHA256_Update(&d->ctx, buf, len); +} +size_t +digest_getdigest(digest_t *d, uchar *buf, size_t len) +{ + uchar tmp[32]; + int n = 32; + SHA256_Final(tmp, &d->ctx); + if (len < 32) + n = len; + memcpy(buf, tmp, n); + memset(tmp, 0, sizeof(tmp)); + return n; +} +#else +struct digest_t { + sha256_state ctx; +}; +digest_t * +digest_new(void) +{ + digest_t *d = malloc(sizeof(digest_t)); + sha256_init(&d->ctx); + return d; +} +void +digest_update(digest_t *d, const uchar *buf, size_t len) +{ + sha256_process(&d->ctx, buf, len); +} +size_t +digest_getdigest(digest_t *d, uchar *buf, size_t len) +{ + uchar tmp[32]; + int n = 32; + sha256_done(&d->ctx, tmp); + if (len < 32) + n = len; + memcpy(buf, tmp, n); + memset(tmp, 0, sizeof(tmp)); + return n; +} +#endif + +void +digest_free(digest_t *d) +{ + memset(d, 0, sizeof(digest_t)); + free(d); +} + +/* ===== + Stream crypto + ===== */ + +crypt_t * +crypt_new(const uchar *key, size_t keylen) +{ + crypt_t *k; + if (keylen < AES_BLOCK_SIZE) + return NULL; + + k = calloc(1, sizeof(crypt_t)); + if (k == NULL) + return NULL; + + AES_set_encrypt_key(key, 128, &k->key); + + return k; +} +void +crypt_set_iv(crypt_t *key, const uchar *iv, size_t ivlen) +{ + assert(ivlen == sizeof(key->ivec)); + memcpy(key->ivec, iv, ivlen); +} +void +stream_crypt(crypt_t *key, uchar *buf, size_t len) +{ + AES_ctr128_encrypt(buf, buf, /* XXX make sure this is okay to do. */ + len, + &key->key, key->ivec, key->ecount_buf, + &key->pos); +} +void +crypt_free(crypt_t *key) +{ + memset(key, 0, sizeof(key)); + free(key); +} + +/* ===== + PRNG + ===== */ + +int +random_bytes(uchar *buf, size_t buflen) +{ + return RAND_bytes(buf, buflen) == 1 ? 0 : -1; +} diff --git a/src/protocols/obfs2_crypt.h b/src/protocols/obfs2_crypt.h new file mode 100644 index 0000000..c9841d8 --- /dev/null +++ b/src/protocols/obfs2_crypt.h @@ -0,0 +1,62 @@ +/* Copyright 2011 Nick Mathewson + + You may do anything with this work that copyright law would normally + restrict, so long as you retain the above notice(s) and this license + in all redistributed copies and derived works. There is no warranty. +*/ + +#ifndef OBFS2_CRYPT_H +#define OBFS2_CRYPT_H + +#include <sys/types.h> + +/* Stream cipher state */ +typedef struct crypt_t crypt_t; +/* Digest state */ +typedef struct digest_t digest_t; + +typedef unsigned char uchar; + +/** Initialize global crypto state. Returrn 0 on success, -1 on failure */ +int initialize_crypto(void); +/** Clean up global crypto state */ +void cleanup_crypto(void); + +/** Return a newly allocated digest state, or NULL on failure. */ +digest_t *digest_new(void); +/** Add n bytes from b to the digest state. */ +void digest_update(digest_t *, const uchar *b, size_t n); +/** Get a digest from the digest state. Put it in up the first n bytes of the +buffer b. Return the number of bytes actually written.*/ +size_t digest_getdigest(digest_t *, uchar *b, size_t n); +/** Clear and free a digest state */ +void digest_free(digest_t *); + +/** Return a new stream cipher state taking key and IV from the data provided. + * The data length must be exactly 32 */ +crypt_t *crypt_new(const uchar *, size_t); +void crypt_set_iv(crypt_t *key, const uchar *iv, size_t ivlen); + +/** Encrypt n bytes of data in the buffer b, in place. */ +void stream_crypt(crypt_t *, uchar *b, size_t n); +/** Clear and free a stream cipher state. */ +void crypt_free(crypt_t *); + +/** Set b to contain n random bytes. */ +int random_bytes(uchar *b, size_t n); + +#ifdef CRYPT_PRIVATE +/* ========== + These definitions are not part of the crypt interface. + They're exposed here so that the unit tests can use them. + ========== +*/ +struct crypt_t { + AES_KEY key; + uchar ivec[AES_BLOCK_SIZE]; + uchar ecount_buf[AES_BLOCK_SIZE]; + unsigned int pos; +}; +#endif + +#endif diff --git a/src/test/unittest_obfs2.c b/src/test/unittest_obfs2.c new file mode 100644 index 0000000..cdac4c9 --- /dev/null +++ b/src/test/unittest_obfs2.c @@ -0,0 +1,476 @@ +/* Copyright 2011 Nick Mathewson + + You may do anything with this work that copyright law would normally + restrict, so long as you retain the above notice(s) and this license + in all redistributed copies and derived works. There is no warranty. +*/ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "tinytest.h" +#include "tinytest_macros.h" + +#include <event2/buffer.h> +#include <openssl/aes.h> + + +#define CRYPT_PROTOCOL_PRIVATE +#define CRYPT_PRIVATE +#include "../plugins/obfs2_crypt.h" +#include "../util.h" +#include "../protocol.h" +#include "../plugins/obfs2.h" + +/* Make sure we can successfully set up a protocol state */ +static void +test_proto_setup(void *data) +{ + struct protocol_t *client_proto = NULL; + struct protocol_t *server_proto = NULL; + + tt_assert(set_up_protocol(OBFS2_PROTOCOL) >= 0); + + client_proto = proto_new(OBFS2_PROTOCOL,1); + server_proto = proto_new(OBFS2_PROTOCOL,1); + + tt_assert(client_proto); + tt_assert(server_proto); + tt_assert(client_proto->state); + tt_assert(server_proto->state); + + end: + if (client_proto->state) + proto_destroy(client_proto); + if (server_proto->state) + proto_destroy(server_proto); + +} + +static void +test_proto_handshake(void *data) +{ + struct evbuffer *output_buffer = NULL; + struct evbuffer *dummy_buffer = NULL; + output_buffer = evbuffer_new(); + dummy_buffer = evbuffer_new(); + + struct protocol_t *client_proto = NULL; + struct protocol_t *server_proto = NULL; + + tt_assert(set_up_protocol(OBFS2_PROTOCOL) >= 0); + + client_proto = proto_new(OBFS2_PROTOCOL,1); + server_proto = proto_new(OBFS2_PROTOCOL,0); + + tt_assert(client_proto); + tt_assert(server_proto); + tt_assert(client_proto->state); + tt_assert(server_proto->state); + + obfs2_state_t *client_state = client_proto->state; + obfs2_state_t *server_state = server_proto->state; + + /* We create a client handshake message and pass it to output_buffer */ + tt_int_op(0, <=, proto_handshake(client_proto, output_buffer)<0); + + /* We simulate the server receiving and processing the client's handshake message, + by using proto_recv() on the output_buffer */ + tt_int_op(0, <=, proto_recv(server_proto, output_buffer, dummy_buffer) <0); + + /* Now, we create the server's handshake and pass it to output_buffer */ + tt_int_op(0, <=, proto_handshake(server_proto, output_buffer)<0); + + /* We simulate the client receiving and processing the server's handshake */ + tt_int_op(0, <=, proto_recv(client_proto, output_buffer, dummy_buffer) <0); + + /* The handshake is now complete. We should have: + client's send_crypto == server's recv_crypto + server's send_crypto == client's recv_crypto . */ + tt_int_op(0, ==, memcmp(client_state->send_crypto, + server_state->recv_crypto, + sizeof(crypt_t))); + + tt_int_op(0, ==, memcmp(client_state->recv_crypto, + server_state->send_crypto, + sizeof(crypt_t))); + + end: + if (client_proto->state) + proto_destroy(client_proto); + if (server_proto->state) + proto_destroy(server_proto); + + if (output_buffer) + evbuffer_free(output_buffer); + if (dummy_buffer) + evbuffer_free(dummy_buffer); +} + +static void +test_proto_transfer(void *data) +{ + struct evbuffer *output_buffer = NULL; + struct evbuffer *dummy_buffer = NULL; + output_buffer = evbuffer_new(); + dummy_buffer = evbuffer_new(); + + struct protocol_t *client_proto = NULL; + struct protocol_t *server_proto = NULL; + + tt_assert(set_up_protocol(OBFS2_PROTOCOL) >= 0); + + client_proto = proto_new(OBFS2_PROTOCOL,1); + server_proto = proto_new(OBFS2_PROTOCOL,0); + + tt_assert(client_proto); + tt_assert(server_proto); + tt_assert(client_proto->state); + tt_assert(server_proto->state); + + int n; + struct evbuffer_iovec v[2]; + + /* Handshake */ + tt_int_op(0, <=, proto_handshake(client_proto, output_buffer)<0); + tt_int_op(0, <=, proto_recv(server_proto, output_buffer, dummy_buffer) <0); + tt_int_op(0, <=, proto_handshake(server_proto, output_buffer)<0); + tt_int_op(0, <=, proto_recv(client_proto, output_buffer, dummy_buffer) <0); + /* End of Handshake */ + + /* Now let's pass some data around. */ + char *msg1 = "this is a 54-byte message passed from client to server"; + char *msg2 = "this is a 55-byte message passed from server to client!"; + + /* client -> server */ + evbuffer_add(dummy_buffer, msg1, 54); + proto_send(client_proto, dummy_buffer, output_buffer); + + tt_int_op(0, <=, proto_recv(server_proto, output_buffer, dummy_buffer)); + + n = evbuffer_peek(dummy_buffer, -1, NULL, &v[0], 2); + + /* Let's check if it matches. */ + tt_int_op(0, ==, strncmp(msg1, v[0].iov_base, 54)); + + /* emptying dummy_buffer before next test */ + size_t buffer_len = evbuffer_get_length(dummy_buffer); + tt_int_op(0, ==, evbuffer_drain(dummy_buffer, buffer_len)); + + /* client <- server */ + evbuffer_add(dummy_buffer, msg2, 55); + tt_int_op(0, <=, proto_send(server_proto, dummy_buffer, output_buffer)); + + tt_int_op(0, <=, proto_recv(client_proto, output_buffer, dummy_buffer)); + + n = evbuffer_peek(dummy_buffer, -1, NULL, &v[1], 2); + tt_int_op(0, ==, strncmp(msg2, v[1].iov_base, 55)); + + end: + if (client_proto->state) + proto_destroy(client_proto); + if (server_proto->state) + proto_destroy(server_proto); + + if (output_buffer) + evbuffer_free(output_buffer); + if (dummy_buffer) + evbuffer_free(dummy_buffer); +} + +/* We are going to split client's handshake into: + msgclient_1 = [OBFUSCATE_SEED_LENGTH + 8 + <one fourth of padding>] + and msgclient_2 = [<rest of padding>]. + + We are then going to split server's handshake into: + msgserver_1 = [OBFUSCATE_SEED_LENGTH + 8] + and msgserver_2 = [<all padding>]. + + Afterwards we will verify that they both got the correct keys. + That's right, this unit test is loco . */ +static void +test_proto_splitted_handshake(void *data) +{ + obfs2_state_t *client_state = NULL; + obfs2_state_t *server_state = NULL; + + struct evbuffer *output_buffer = NULL; + struct evbuffer *dummy_buffer = NULL; + output_buffer = evbuffer_new(); + dummy_buffer = evbuffer_new(); + + struct protocol_t *client_proto = NULL; + struct protocol_t *server_proto = NULL; + + tt_assert(set_up_protocol(OBFS2_PROTOCOL) >= 0); + + client_proto = proto_new(OBFS2_PROTOCOL,1); + server_proto = proto_new(OBFS2_PROTOCOL,0); + + tt_assert(client_proto); + tt_assert(server_proto); + tt_assert(client_proto->state); + tt_assert(server_proto->state); + + client_state = client_proto->state; + server_state = server_proto->state; + + uint32_t magic = htonl(OBFUSCATE_MAGIC_VALUE); + uint32_t plength1, plength1_msg1, plength1_msg2, send_plength1; + const uchar *seed1; + + /* generate padlen */ + tt_int_op(0, <=, random_bytes((uchar*)&plength1, 4)); + + plength1 %= OBFUSCATE_MAX_PADDING; + + plength1_msg1 = plength1 / 4; + plength1_msg2 = plength1 - plength1_msg1; + + send_plength1 = htonl(plength1); + + uchar msgclient_1[OBFUSCATE_MAX_PADDING + OBFUSCATE_SEED_LENGTH + 8]; + uchar msgclient_2[OBFUSCATE_MAX_PADDING]; + + seed1 = client_state->initiator_seed; + + memcpy(msgclient_1, seed1, OBFUSCATE_SEED_LENGTH); + memcpy(msgclient_1+OBFUSCATE_SEED_LENGTH, &magic, 4); + memcpy(msgclient_1+OBFUSCATE_SEED_LENGTH+4, &send_plength1, 4); + tt_int_op(0, <=, random_bytes(msgclient_1+OBFUSCATE_SEED_LENGTH+8, plength1_msg1)); + + stream_crypt(client_state->send_padding_crypto, msgclient_1+OBFUSCATE_SEED_LENGTH, 8+plength1_msg1); + + /* Client sends handshake part 1 */ + evbuffer_add(output_buffer, msgclient_1, OBFUSCATE_SEED_LENGTH+8+plength1_msg1); + + /* Server receives handshake part 1 */ + tt_int_op(0, <=, proto_recv(server_proto, output_buffer, dummy_buffer)); + + tt_assert(server_state->state == ST_WAIT_FOR_PADDING); + + /* Preparing client's handshake part 2 */ + tt_int_op(0, <=, random_bytes(msgclient_2, plength1_msg2)); + stream_crypt(client_state->send_padding_crypto, msgclient_2, plength1_msg2); + + /* Client sends handshake part 2 */ + evbuffer_add(output_buffer, msgclient_2, plength1_msg2); + + /* Server receives handshake part 2 */ + tt_int_op(0, <=, proto_recv(server_proto, output_buffer, dummy_buffer)); + + tt_assert(server_state->state == ST_OPEN); + + /* Since everything went right, let's do a server to client handshake now! */ + uint32_t plength2, send_plength2; + const uchar *seed2; + + /* generate padlen */ + tt_int_op(0, <=, random_bytes((uchar*)&plength2, 4)); + + plength2 %= OBFUSCATE_MAX_PADDING; + send_plength2 = htonl(plength2); + + uchar msgserver_1[OBFUSCATE_SEED_LENGTH + 8]; + uchar msgserver_2[OBFUSCATE_MAX_PADDING]; + + seed2 = server_state->responder_seed; + + memcpy(msgserver_1, seed2, OBFUSCATE_SEED_LENGTH); + memcpy(msgserver_1+OBFUSCATE_SEED_LENGTH, &magic, 4); + memcpy(msgserver_1+OBFUSCATE_SEED_LENGTH+4, &send_plength2, 4); + + stream_crypt(server_state->send_padding_crypto, msgserver_1+OBFUSCATE_SEED_LENGTH, 8); + + /* Server sends handshake part 1 */ + evbuffer_add(output_buffer, msgserver_1, OBFUSCATE_SEED_LENGTH+8); + + /* Client receives handshake part 1 */ + tt_int_op(0, <=, proto_recv(client_proto, output_buffer, dummy_buffer)); + + tt_assert(client_state->state == ST_WAIT_FOR_PADDING); + + /* Preparing client's handshake part 2 */ + tt_int_op(0, <=, random_bytes(msgserver_2, plength2)); + stream_crypt(server_state->send_padding_crypto, msgserver_2, plength2); + + /* Server sends handshake part 2 */ + evbuffer_add(output_buffer, msgserver_2, plength2); + + /* Client receives handshake part 2 */ + tt_int_op(0, <=, proto_recv(client_proto, output_buffer, dummy_buffer)); + + tt_assert(client_state->state == ST_OPEN); + + /* The handshake is finally complete. We should have: */ + /* client's send_crypto == server's recv_crypto */ + /* server's send_crypto == client's recv_crypto . */ + tt_int_op(0, ==, memcmp(client_state->send_crypto, + server_state->recv_crypto, + sizeof(crypt_t))); + + tt_int_op(0, ==, memcmp(client_state->recv_crypto, + server_state->send_crypto, + sizeof(crypt_t))); + + end: + if (client_state) + proto_destroy(client_proto); + if (server_state) + proto_destroy(server_proto); + + if (output_buffer) + evbuffer_free(output_buffer); + if (dummy_buffer) + evbuffer_free(dummy_buffer); +} + +/* + Erroneous handshake test: + Wrong magic value. +*/ +static void +test_proto_wrong_handshake_magic(void *data) +{ + obfs2_state_t *client_state = NULL; + obfs2_state_t *server_state = NULL; + + struct evbuffer *output_buffer = NULL; + struct evbuffer *dummy_buffer = NULL; + output_buffer = evbuffer_new(); + dummy_buffer = evbuffer_new(); + + struct protocol_t *client_proto = NULL; + struct protocol_t *server_proto = NULL; + + tt_assert(set_up_protocol(OBFS2_PROTOCOL) >= 0); + + client_proto = proto_new(OBFS2_PROTOCOL,1); + server_proto = proto_new(OBFS2_PROTOCOL,0); + + tt_assert(client_proto); + tt_assert(server_proto); + tt_assert(client_proto->state); + tt_assert(server_proto->state); + + client_state = client_proto->state; + server_state = server_proto->state; + + uint32_t wrong_magic = 0xD15EA5E; + + uint32_t plength, send_plength; + const uchar *seed; + uchar msg[OBFUSCATE_MAX_PADDING + OBFUSCATE_SEED_LENGTH + 8]; + + tt_int_op(0, >=, random_bytes((uchar*)&plength, 4)); + plength %= OBFUSCATE_MAX_PADDING; + send_plength = htonl(plength); + + seed = client_state->initiator_seed; + memcpy(msg, seed, OBFUSCATE_SEED_LENGTH); + memcpy(msg+OBFUSCATE_SEED_LENGTH, &wrong_magic, 4); + memcpy(msg+OBFUSCATE_SEED_LENGTH+4, &send_plength, 4); + tt_int_op(0, >=, random_bytes(msg+OBFUSCATE_SEED_LENGTH+8, plength)); + + stream_crypt(client_state->send_padding_crypto, + msg+OBFUSCATE_SEED_LENGTH, 8+plength); + + evbuffer_add(output_buffer, msg, OBFUSCATE_SEED_LENGTH+8+plength); + + tt_int_op(-1, ==, proto_recv(server_proto, output_buffer, dummy_buffer)); + + tt_assert(server_state->state == ST_WAIT_FOR_KEY); + + end: + if (client_state) + proto_destroy(client_proto); + if (server_state) + proto_destroy(server_proto); + + if (output_buffer) + evbuffer_free(output_buffer); + if (dummy_buffer) + evbuffer_free(dummy_buffer); +} + +/* Erroneous handshake test: + plength field larger than OBFUSCATE_MAX_PADDING +*/ +static void +test_proto_wrong_handshake_plength(void *data) +{ + obfs2_state_t *client_state = NULL; + obfs2_state_t *server_state = NULL; + + struct evbuffer *output_buffer = NULL; + struct evbuffer *dummy_buffer = NULL; + output_buffer = evbuffer_new(); + dummy_buffer = evbuffer_new(); + + struct protocol_t *client_proto = NULL; + struct protocol_t *server_proto = NULL; + + tt_assert(set_up_protocol(OBFS2_PROTOCOL) >= 0); + + client_proto = proto_new(OBFS2_PROTOCOL,1); + server_proto = proto_new(OBFS2_PROTOCOL,0); + + tt_assert(client_proto); + tt_assert(server_proto); + tt_assert(client_proto->state); + tt_assert(server_proto->state); + + client_state = client_proto->state; + server_state = server_proto->state; + + uchar msg[OBFUSCATE_MAX_PADDING + OBFUSCATE_SEED_LENGTH + 8 + 1]; + uint32_t magic = htonl(OBFUSCATE_MAGIC_VALUE); + uint32_t plength, send_plength; + const uchar *seed; + seed = client_state->initiator_seed; + + plength = OBFUSCATE_MAX_PADDING + 1U; + send_plength = htonl(plength); + + memcpy(msg, seed, OBFUSCATE_SEED_LENGTH); + memcpy(msg+OBFUSCATE_SEED_LENGTH, &magic, 4); + memcpy(msg+OBFUSCATE_SEED_LENGTH+4, &send_plength, 4); + tt_int_op(0, >=, random_bytes(msg+OBFUSCATE_SEED_LENGTH+8, plength)); + + stream_crypt(client_state->send_padding_crypto, + msg+OBFUSCATE_SEED_LENGTH, 8+plength); + + evbuffer_add(output_buffer, msg, OBFUSCATE_SEED_LENGTH+8+plength); + + tt_int_op(-1, ==, proto_recv(server_proto, output_buffer, dummy_buffer)); + + tt_assert(server_state->state == ST_WAIT_FOR_KEY); + + end: + if (client_state) + proto_destroy(client_proto); + if (server_state) + proto_destroy(server_proto); + + if (output_buffer) + evbuffer_free(output_buffer); + if (dummy_buffer) + evbuffer_free(dummy_buffer); +} + + +#define T(name, flags) \ + { #name, test_proto_##name, (flags), NULL, NULL } + +struct testcase_t protocol_tests[] = { + T(setup, 0), + T(handshake, 0), + T(transfer, 0), + T(splitted_handshake, 0), + T(wrong_handshake_magic, 0), +#if 0 + T(wrong_handshake_padding, 0), +#endif + T(wrong_handshake_plength, 0), + END_OF_TESTCASES +}; diff --git a/src/test/unittest_protocol.c b/src/test/unittest_protocol.c deleted file mode 100644 index 1864a3a..0000000 --- a/src/test/unittest_protocol.c +++ /dev/null @@ -1,469 +0,0 @@ -/* Copyright 2011 Nick Mathewson - - You may do anything with this work that copyright law would normally - restrict, so long as you retain the above notice(s) and this license - in all redistributed copies and derived works. There is no warranty. -*/ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "tinytest.h" -#include "tinytest_macros.h" - -#include <event2/buffer.h> -#include <openssl/aes.h> - - -#define CRYPT_PROTOCOL_PRIVATE -#define CRYPT_PRIVATE -#include "../plugins/obfs2_crypt.h" -#include "../util.h" -#include "../protocol.h" -#include "../plugins/obfs2.h" - -/* Make sure we can successfully set up a protocol state */ -static void -test_proto_setup(void *data) -{ - struct protocol_t *client_proto = set_up_protocol(OBFS2_PROTOCOL); - struct protocol_t *server_proto = set_up_protocol(OBFS2_PROTOCOL); - - int initiator = 1; - int no_initiator = 0; - client_proto->state = proto_init(client_proto, &initiator); - server_proto->state = proto_init(server_proto, &no_initiator); - - tt_assert(client_proto); - tt_assert(server_proto); - tt_assert(client_proto->state); - tt_assert(server_proto->state); - - end: - if (client_proto->state) - proto_destroy(client_proto); - if (server_proto->state) - proto_destroy(server_proto); - -} - -static void -test_proto_handshake(void *data) -{ - struct evbuffer *output_buffer = NULL; - struct evbuffer *dummy_buffer = NULL; - output_buffer = evbuffer_new(); - dummy_buffer = evbuffer_new(); - - struct protocol_t *client_proto = set_up_protocol(OBFS2_PROTOCOL); - struct protocol_t *server_proto = set_up_protocol(OBFS2_PROTOCOL); - - int initiator = 1; - int no_initiator = 0; - client_proto->state = proto_init(client_proto, &initiator); - server_proto->state = proto_init(server_proto, &no_initiator); - tt_assert(client_proto); - tt_assert(server_proto); - tt_assert(client_proto->state); - tt_assert(server_proto->state); - - obfs2_state_t *client_state = client_proto->state; - obfs2_state_t *server_state = server_proto->state; - - /* We create a client handshake message and pass it to output_buffer */ - tt_int_op(0, <=, proto_handshake(client_proto, output_buffer)<0); - - /* We simulate the server receiving and processing the client's handshake message, - by using proto_recv() on the output_buffer */ - tt_int_op(0, <=, proto_recv(server_proto, output_buffer, dummy_buffer) <0); - - /* Now, we create the server's handshake and pass it to output_buffer */ - tt_int_op(0, <=, proto_handshake(server_proto, output_buffer)<0); - - /* We simulate the client receiving and processing the server's handshake */ - tt_int_op(0, <=, proto_recv(client_proto, output_buffer, dummy_buffer) <0); - - /* The handshake is now complete. We should have: - client's send_crypto == server's recv_crypto - server's send_crypto == client's recv_crypto . */ - tt_int_op(0, ==, memcmp(client_state->send_crypto, - server_state->recv_crypto, - sizeof(crypt_t))); - - tt_int_op(0, ==, memcmp(client_state->recv_crypto, - server_state->send_crypto, - sizeof(crypt_t))); - - end: - if (client_proto->state) - proto_destroy(client_proto); - if (server_proto->state) - proto_destroy(server_proto); - - if (output_buffer) - evbuffer_free(output_buffer); - if (dummy_buffer) - evbuffer_free(dummy_buffer); -} - -static void -test_proto_transfer(void *data) -{ - struct evbuffer *output_buffer = NULL; - struct evbuffer *dummy_buffer = NULL; - output_buffer = evbuffer_new(); - dummy_buffer = evbuffer_new(); - - struct protocol_t *client_proto = set_up_protocol(OBFS2_PROTOCOL); - struct protocol_t *server_proto = set_up_protocol(OBFS2_PROTOCOL); - - int initiator = 1; - int no_initiator = 0; - client_proto->state = proto_init(client_proto, &initiator); - server_proto->state = proto_init(server_proto, &no_initiator); - tt_assert(client_proto); - tt_assert(server_proto); - tt_assert(client_proto->state); - tt_assert(server_proto->state); - - int n; - struct evbuffer_iovec v[2]; - - /* Handshake */ - tt_int_op(0, <=, proto_handshake(client_proto, output_buffer)<0); - tt_int_op(0, <=, proto_recv(server_proto, output_buffer, dummy_buffer) <0); - tt_int_op(0, <=, proto_handshake(server_proto, output_buffer)<0); - tt_int_op(0, <=, proto_recv(client_proto, output_buffer, dummy_buffer) <0); - /* End of Handshake */ - - /* Now let's pass some data around. */ - char *msg1 = "this is a 54-byte message passed from client to server"; - char *msg2 = "this is a 55-byte message passed from server to client!"; - - /* client -> server */ - evbuffer_add(dummy_buffer, msg1, 54); - proto_send(client_proto, dummy_buffer, output_buffer); - - tt_int_op(0, <=, proto_recv(server_proto, output_buffer, dummy_buffer)); - - n = evbuffer_peek(dummy_buffer, -1, NULL, &v[0], 2); - - /* Let's check if it matches. */ - tt_int_op(0, ==, strncmp(msg1, v[0].iov_base, 54)); - - /* emptying dummy_buffer before next test */ - size_t buffer_len = evbuffer_get_length(dummy_buffer); - tt_int_op(0, ==, evbuffer_drain(dummy_buffer, buffer_len)); - - /* client <- server */ - evbuffer_add(dummy_buffer, msg2, 55); - tt_int_op(0, <=, proto_send(server_proto, dummy_buffer, output_buffer)); - - tt_int_op(0, <=, proto_recv(client_proto, output_buffer, dummy_buffer)); - - n = evbuffer_peek(dummy_buffer, -1, NULL, &v[1], 2); - tt_int_op(0, ==, strncmp(msg2, v[1].iov_base, 55)); - - end: - if (client_proto->state) - proto_destroy(client_proto); - if (server_proto->state) - proto_destroy(server_proto); - - if (output_buffer) - evbuffer_free(output_buffer); - if (dummy_buffer) - evbuffer_free(dummy_buffer); -} - -/* We are going to split client's handshake into: - msgclient_1 = [OBFUSCATE_SEED_LENGTH + 8 + <one fourth of padding>] - and msgclient_2 = [<rest of padding>]. - - We are then going to split server's handshake into: - msgserver_1 = [OBFUSCATE_SEED_LENGTH + 8] - and msgserver_2 = [<all padding>]. - - Afterwards we will verify that they both got the correct keys. - That's right, this unit test is loco . */ -static void -test_proto_splitted_handshake(void *data) -{ - obfs2_state_t *client_state = NULL; - obfs2_state_t *server_state = NULL; - - struct evbuffer *output_buffer = NULL; - struct evbuffer *dummy_buffer = NULL; - output_buffer = evbuffer_new(); - dummy_buffer = evbuffer_new(); - - struct protocol_t *client_proto = set_up_protocol(OBFS2_PROTOCOL); - struct protocol_t *server_proto = set_up_protocol(OBFS2_PROTOCOL); - - int initiator = 1; - int no_initiator = 0; - client_proto->state = proto_init(client_proto, &initiator); - server_proto->state = proto_init(server_proto, &no_initiator); - tt_assert(client_proto); - tt_assert(server_proto); - tt_assert(client_proto->state); - tt_assert(server_proto->state); - - client_state = client_proto->state; - server_state = server_proto->state; - - uint32_t magic = htonl(OBFUSCATE_MAGIC_VALUE); - uint32_t plength1, plength1_msg1, plength1_msg2, send_plength1; - const uchar *seed1; - - /* generate padlen */ - tt_int_op(0, <=, random_bytes((uchar*)&plength1, 4)); - - plength1 %= OBFUSCATE_MAX_PADDING; - - plength1_msg1 = plength1 / 4; - plength1_msg2 = plength1 - plength1_msg1; - - send_plength1 = htonl(plength1); - - uchar msgclient_1[OBFUSCATE_MAX_PADDING + OBFUSCATE_SEED_LENGTH + 8]; - uchar msgclient_2[OBFUSCATE_MAX_PADDING]; - - seed1 = client_state->initiator_seed; - - memcpy(msgclient_1, seed1, OBFUSCATE_SEED_LENGTH); - memcpy(msgclient_1+OBFUSCATE_SEED_LENGTH, &magic, 4); - memcpy(msgclient_1+OBFUSCATE_SEED_LENGTH+4, &send_plength1, 4); - tt_int_op(0, <=, random_bytes(msgclient_1+OBFUSCATE_SEED_LENGTH+8, plength1_msg1)); - - stream_crypt(client_state->send_padding_crypto, msgclient_1+OBFUSCATE_SEED_LENGTH, 8+plength1_msg1); - - /* Client sends handshake part 1 */ - evbuffer_add(output_buffer, msgclient_1, OBFUSCATE_SEED_LENGTH+8+plength1_msg1); - - /* Server receives handshake part 1 */ - tt_int_op(0, <=, proto_recv(server_proto, output_buffer, dummy_buffer)); - - tt_assert(server_state->state == ST_WAIT_FOR_PADDING); - - /* Preparing client's handshake part 2 */ - tt_int_op(0, <=, random_bytes(msgclient_2, plength1_msg2)); - stream_crypt(client_state->send_padding_crypto, msgclient_2, plength1_msg2); - - /* Client sends handshake part 2 */ - evbuffer_add(output_buffer, msgclient_2, plength1_msg2); - - /* Server receives handshake part 2 */ - tt_int_op(0, <=, proto_recv(server_proto, output_buffer, dummy_buffer)); - - tt_assert(server_state->state == ST_OPEN); - - /* Since everything went right, let's do a server to client handshake now! */ - uint32_t plength2, send_plength2; - const uchar *seed2; - - /* generate padlen */ - tt_int_op(0, <=, random_bytes((uchar*)&plength2, 4)); - - plength2 %= OBFUSCATE_MAX_PADDING; - send_plength2 = htonl(plength2); - - uchar msgserver_1[OBFUSCATE_SEED_LENGTH + 8]; - uchar msgserver_2[OBFUSCATE_MAX_PADDING]; - - seed2 = server_state->responder_seed; - - memcpy(msgserver_1, seed2, OBFUSCATE_SEED_LENGTH); - memcpy(msgserver_1+OBFUSCATE_SEED_LENGTH, &magic, 4); - memcpy(msgserver_1+OBFUSCATE_SEED_LENGTH+4, &send_plength2, 4); - - stream_crypt(server_state->send_padding_crypto, msgserver_1+OBFUSCATE_SEED_LENGTH, 8); - - /* Server sends handshake part 1 */ - evbuffer_add(output_buffer, msgserver_1, OBFUSCATE_SEED_LENGTH+8); - - /* Client receives handshake part 1 */ - tt_int_op(0, <=, proto_recv(client_proto, output_buffer, dummy_buffer)); - - tt_assert(client_state->state == ST_WAIT_FOR_PADDING); - - /* Preparing client's handshake part 2 */ - tt_int_op(0, <=, random_bytes(msgserver_2, plength2)); - stream_crypt(server_state->send_padding_crypto, msgserver_2, plength2); - - /* Server sends handshake part 2 */ - evbuffer_add(output_buffer, msgserver_2, plength2); - - /* Client receives handshake part 2 */ - tt_int_op(0, <=, proto_recv(client_proto, output_buffer, dummy_buffer)); - - tt_assert(client_state->state == ST_OPEN); - - /* The handshake is finally complete. We should have: */ - /* client's send_crypto == server's recv_crypto */ - /* server's send_crypto == client's recv_crypto . */ - tt_int_op(0, ==, memcmp(client_state->send_crypto, - server_state->recv_crypto, - sizeof(crypt_t))); - - tt_int_op(0, ==, memcmp(client_state->recv_crypto, - server_state->send_crypto, - sizeof(crypt_t))); - - end: - if (client_state) - proto_destroy(client_proto); - if (server_state) - proto_destroy(server_proto); - - if (output_buffer) - evbuffer_free(output_buffer); - if (dummy_buffer) - evbuffer_free(dummy_buffer); -} - -/* - Erroneous handshake test: - Wrong magic value. -*/ -static void -test_proto_wrong_handshake_magic(void *data) -{ - obfs2_state_t *client_state = NULL; - obfs2_state_t *server_state = NULL; - - struct evbuffer *output_buffer = NULL; - struct evbuffer *dummy_buffer = NULL; - output_buffer = evbuffer_new(); - dummy_buffer = evbuffer_new(); - - struct protocol_t *client_proto = set_up_protocol(OBFS2_PROTOCOL); - struct protocol_t *server_proto = set_up_protocol(OBFS2_PROTOCOL); - - int initiator = 1; - int no_initiator = 0; - client_proto->state = proto_init(client_proto, &initiator); - server_proto->state = proto_init(server_proto, &no_initiator); - tt_assert(client_proto); - tt_assert(server_proto); - tt_assert(client_proto->state); - tt_assert(server_proto->state); - - client_state = client_proto->state; - server_state = server_proto->state; - - uint32_t wrong_magic = 0xD15EA5E; - - uint32_t plength, send_plength; - const uchar *seed; - uchar msg[OBFUSCATE_MAX_PADDING + OBFUSCATE_SEED_LENGTH + 8]; - - tt_int_op(0, >=, random_bytes((uchar*)&plength, 4)); - plength %= OBFUSCATE_MAX_PADDING; - send_plength = htonl(plength); - - seed = client_state->initiator_seed; - memcpy(msg, seed, OBFUSCATE_SEED_LENGTH); - memcpy(msg+OBFUSCATE_SEED_LENGTH, &wrong_magic, 4); - memcpy(msg+OBFUSCATE_SEED_LENGTH+4, &send_plength, 4); - tt_int_op(0, >=, random_bytes(msg+OBFUSCATE_SEED_LENGTH+8, plength)); - - stream_crypt(client_state->send_padding_crypto, - msg+OBFUSCATE_SEED_LENGTH, 8+plength); - - evbuffer_add(output_buffer, msg, OBFUSCATE_SEED_LENGTH+8+plength); - - tt_int_op(-1, ==, proto_recv(server_proto, output_buffer, dummy_buffer)); - - tt_assert(server_state->state == ST_WAIT_FOR_KEY); - - end: - if (client_state) - proto_destroy(client_proto); - if (server_state) - proto_destroy(server_proto); - - if (output_buffer) - evbuffer_free(output_buffer); - if (dummy_buffer) - evbuffer_free(dummy_buffer); -} - -/* Erroneous handshake test: - plength field larger than OBFUSCATE_MAX_PADDING -*/ -static void -test_proto_wrong_handshake_plength(void *data) -{ - obfs2_state_t *client_state = NULL; - obfs2_state_t *server_state = NULL; - struct evbuffer *output_buffer = NULL; - struct evbuffer *dummy_buffer = NULL; - output_buffer = evbuffer_new(); - dummy_buffer = evbuffer_new(); - - struct protocol_t *client_proto = set_up_protocol(OBFS2_PROTOCOL); - struct protocol_t *server_proto = set_up_protocol(OBFS2_PROTOCOL); - int initiator = 1; - int no_initiator = 0; - client_proto->state = proto_init(client_proto, &initiator); - server_proto->state = proto_init(server_proto, &no_initiator); - tt_assert(client_proto); - tt_assert(server_proto); - tt_assert(client_proto->state); - tt_assert(server_proto->state); - - client_state = client_proto->state; - server_state = server_proto->state; - - uchar msg[OBFUSCATE_MAX_PADDING + OBFUSCATE_SEED_LENGTH + 8 + 1]; - uint32_t magic = htonl(OBFUSCATE_MAGIC_VALUE); - uint32_t plength, send_plength; - const uchar *seed; - seed = client_state->initiator_seed; - - plength = OBFUSCATE_MAX_PADDING + 1U; - send_plength = htonl(plength); - - memcpy(msg, seed, OBFUSCATE_SEED_LENGTH); - memcpy(msg+OBFUSCATE_SEED_LENGTH, &magic, 4); - memcpy(msg+OBFUSCATE_SEED_LENGTH+4, &send_plength, 4); - tt_int_op(0, >=, random_bytes(msg+OBFUSCATE_SEED_LENGTH+8, plength)); - - stream_crypt(client_state->send_padding_crypto, - msg+OBFUSCATE_SEED_LENGTH, 8+plength); - - evbuffer_add(output_buffer, msg, OBFUSCATE_SEED_LENGTH+8+plength); - - tt_int_op(-1, ==, proto_recv(server_proto, output_buffer, dummy_buffer)); - - tt_assert(server_state->state == ST_WAIT_FOR_KEY); - - end: - if (client_state) - proto_destroy(client_proto); - if (server_state) - proto_destroy(server_proto); - - if (output_buffer) - evbuffer_free(output_buffer); - if (dummy_buffer) - evbuffer_free(dummy_buffer); -} - - -#define T(name, flags) \ - { #name, test_proto_##name, (flags), NULL, NULL } - -struct testcase_t protocol_tests[] = { - T(setup, 0), - T(handshake, 0), - T(transfer, 0), - T(splitted_handshake, 0), - T(wrong_handshake_magic, 0), -#if 0 - T(wrong_handshake_padding, 0), -#endif - T(wrong_handshake_plength, 0), - END_OF_TESTCASES -};
tor-commits@lists.torproject.org