commit bff0015cb6aef8cde564c2b3d51f3a9019b25533 Author: George Kadianakis desnacked@gmail.com Date: Thu Mar 17 16:09:19 2011 +0100
* Renamed the OpenSSH obfuscation variant plugin to "obfs2" and moved it to the right place. * Renamed module.{c,h} to protocol.{c,h}. * Moved the protocol state inside protocol_t. --- Makefile.am | 14 +- src/crypt_protocol.c | 359 --------------------------------------------- src/crypt_protocol.h | 88 ----------- src/main.c | 2 +- src/module.c | 31 ---- src/module.h | 29 ---- src/network.c | 24 ++-- src/network.h | 5 +- src/plugins/obfs2.c | 359 +++++++++++++++++++++++++++++++++++++++++++++ src/plugins/obfs2.h | 88 +++++++++++ src/protocol.c | 31 ++++ src/protocol.h | 32 ++++ src/test/unittest_socks.c | 2 +- 13 files changed, 533 insertions(+), 531 deletions(-)
diff --git a/Makefile.am b/Makefile.am index 430fd13..9376df3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,12 +9,11 @@ noinst_PROGRAMS = unittests
libobfsproxy_a_SOURCES = \ src/crypt.c \ - src/crypt_protocol.c \ - src/module.c \ src/network.c \ + src/protocol.c \ src/socks.c \ - src/util.c - + src/util.c \ + src/plugins/obfs2.c
obfsproxy_SOURCES = \ src/main.c @@ -29,15 +28,14 @@ unittests_SOURCES = \ unittests_LDADD = @libevent_LIBS@ @openssl_LIBS@ libobfsproxy.a
noinst_HEADERS = \ - src/crypt_protocol.h \ src/crypt.h \ - src/module.h \ src/network.h \ + src/protocol.h \ src/socks.h \ src/util.h \ src/test/tinytest.h \ - src/test/tinytest_macros.h - + src/test/tinytest_macros.h \ + src/plugins/obfs2.h
EXTRA_DIST = doc/protocol-spec.txt src/sha256.c
diff --git a/autogen.sh b/autogen.sh old mode 100644 new mode 100755 diff --git a/src/crypt_protocol.c b/src/crypt_protocol.c deleted file mode 100644 index ce81a33..0000000 --- a/src/crypt_protocol.c +++ /dev/null @@ -1,359 +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 "crypt.h" -#include "crypt_protocol.h" -#include "util.h" -#include "module.h" - -void * -new_brl(struct protocol_t *proto_struct) { - proto_struct->destroy = (void *)brl_state_free; - proto_struct->init = (void *)brl_state_new; - proto_struct->handshake = (void *)brl_send_initial_message; - proto_struct->send = (void *)brl_send; - proto_struct->recv = (void *)brl_recv; - - return NULL; -} - -/** 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(brl_state_t *state, const char *keytype) -{ - 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(brl_state_t *state, const uchar *seed, - const char *keytype) -{ - 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; -} - -/** - 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. - */ -brl_state_t * -brl_state_new(int *initiator) -{ - brl_state_t *state = calloc(1, sizeof(brl_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 -brl_state_set_shared_secret(brl_state_t *state, - const char *secret, size_t secretlen) -{ - 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. - */ -int -brl_send_initial_message(brl_state_t *state, struct evbuffer *buf) -{ - 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. - */ -int -brl_send(brl_state_t *state, - struct evbuffer *source, struct evbuffer *dest) -{ - 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(brl_state_t *state) -{ - 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" */ -int -brl_recv(brl_state_t *state, struct evbuffer *source, - struct evbuffer *dest) -{ - 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); -} - -void -brl_state_free(brl_state_t *s) -{ - if (s->send_crypto) - crypt_free(s->send_crypto); - if (s->send_padding_crypto) - crypt_free(s->send_padding_crypto); - if (s->recv_crypto) - crypt_free(s->recv_crypto); - if (s->recv_padding_crypto) - crypt_free(s->recv_padding_crypto); - if (s->pending_data_to_send) - evbuffer_free(s->pending_data_to_send); - memset(s, 0x0a, sizeof(brl_state_t)); - free(s); -} diff --git a/src/crypt_protocol.h b/src/crypt_protocol.h deleted file mode 100644 index e6e76d2..0000000 --- a/src/crypt_protocol.h +++ /dev/null @@ -1,88 +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 CRYPT_PROTOCOL_H -#define CRYPT_PROTOCOL_H - -#include <sys/types.h> - -typedef struct brl_state_t brl_state_t; -struct evbuffer; -struct protocol_t; - -#define SHARED_SECRET_LENGTH 16 - -brl_state_t *brl_state_new(int *initiator); -void brl_state_set_shared_secret(brl_state_t *state, - const char *secret, size_t secretlen); -void brl_state_free(brl_state_t *state); -int brl_send_initial_message(brl_state_t *state, struct evbuffer *buf); -int brl_send(brl_state_t *state, - struct evbuffer *source, struct evbuffer *dest); -int brl_recv(brl_state_t *state, struct evbuffer *source, - struct evbuffer *dest); - -void *new_brl(struct protocol_t *proto_struct); - - -#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 brl_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/main.c b/src/main.c index 94e8442..de5cd61 100644 --- a/src/main.c +++ b/src/main.c @@ -14,7 +14,7 @@ #include "crypt.h" #include "network.h" #include "util.h" -#include "module.h" +#include "protocol.h"
#ifndef __GNUC__ #define __attribute__(x) diff --git a/src/module.c b/src/module.c deleted file mode 100644 index 51f91ac..0000000 --- a/src/module.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "stdlib.h" -#include "stdio.h" - -#include "module.h" -#include "crypt_protocol.h" -#include "crypt.h" -#include "network.h" - -/** - This function returns a protocol_t structure based on the mode - of obfsproxy -*/ -struct protocol_t * -set_up_module(int protocol) { - struct protocol_t *proto = calloc(1, sizeof(struct protocol_t)); - - if (protocol == BRL_PROTOCOL) { - proto->new = &new_brl; - proto->new(proto); - printf("Protocol constructed\n"); - - if (initialize_crypto() < 0) { - fprintf(stderr, "Can't initialize crypto; failing\n"); - return NULL; - } - } - /* elif { other protocols } */ - - return proto; -} - diff --git a/src/module.h b/src/module.h deleted file mode 100644 index 8302acf..0000000 --- a/src/module.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef MODULE_H -#define MODULE_H - -/* ASN I'm gonna be calling crypt_protocol.c BRL_RPOTOCOL for now. Yes. */ -#define BRL_PROTOCOL 1 - -struct protocol_t *set_up_module(int protocol); - -/* ASN */ -struct protocol_t { - /* Constructor: creates the protocol; sets up functions etc. */ - void *(*new)(struct protocol_t *self); - /* Destructor */ - void (*destroy)(void *arg); - - /* does nessesary initiation steps; like build a proto state etc. */ - void *(*init)(void *arg); - - /* does handshake. Supposedly all modules have a handshake. */ - void *(*handshake)(void *state, void *buf); - /* send data function */ - int (*send)(void *state, void *source, - void *dest); - /* receive data function */ - int (*recv)(void *state, void *source, - void *dest); -}; - -#endif diff --git a/src/network.c b/src/network.c index 882d352..b6dadd7 100644 --- a/src/network.c +++ b/src/network.c @@ -6,11 +6,10 @@ */
#define NETWORK_PRIVATE -#include "crypt_protocol.h" #include "network.h" #include "util.h" #include "socks.h" -#include "module.h" +#include "protocol.h"
#include <assert.h> #include <stdlib.h> @@ -24,6 +23,8 @@ #include <errno.h> #include <event2/util.h>
+#include "plugins/obfs2.h" + struct listener_t { struct evconnlistener *listener; struct sockaddr_storage target_address; @@ -62,9 +63,9 @@ listener_new(struct event_base *base, assert(mode == LSN_SIMPLE_CLIENT || mode == LSN_SIMPLE_SERVER || mode == LSN_SOCKS_CLIENT);
- struct protocol_t *proto = set_up_module(protocol); + struct protocol_t *proto = set_up_protocol(protocol); if (!proto) { - printf("This is just terrible. We can't even set up a module!Seppuku time!\n"); + printf("This is just terrible. We can't even set up a protocol! Seppuku time!\n"); exit(-1); }
@@ -124,9 +125,9 @@ simple_listener_cb(struct evconnlistener *evcl, /* ASN Is this actually modular. Will all protocols need to init here? I don't think so. I don't know. */ int is_initiator = (conn->mode != LSN_SIMPLE_SERVER) ? 1 : 0; - conn->proto_state = lsn->proto->init(&is_initiator); + conn->proto->state = lsn->proto->init(&is_initiator);
- if (!conn->proto_state) + if (!conn->proto->state) goto err;
if (conn->mode == LSN_SOCKS_CLIENT) { @@ -176,8 +177,9 @@ simple_listener_cb(struct evconnlistener *evcl, /* Queue output right now. */ struct bufferevent *encrypted = conn->mode == LSN_SIMPLE_SERVER ? conn->input : conn->output; + /* ASN Send handshake */ - if (lsn->proto->handshake(conn->proto_state, + if (lsn->proto->handshake(conn->proto->state, bufferevent_get_output(encrypted))<0) goto err;
@@ -202,8 +204,8 @@ simple_listener_cb(struct evconnlistener *evcl, static void conn_free(conn_t *conn) { - if (conn->proto_state) - conn->proto->destroy((void *)conn->proto_state); + if (conn->proto->state) + conn->proto->destroy((void *)conn->proto->state); if (conn->socks_state) socks_state_free(conn->socks_state); if (conn->input) @@ -278,7 +280,7 @@ plaintext_read_cb(struct bufferevent *bev, void *arg) other = (bev == conn->input) ? conn->output : conn->input;
dbg(("Got data on plaintext side\n")); - if (conn->proto->send(conn->proto_state, + if (conn->proto->send(conn->proto->state, bufferevent_get_input(bev), bufferevent_get_output(other)) < 0) conn_free(conn); @@ -292,7 +294,7 @@ obfsucated_read_cb(struct bufferevent *bev, void *arg) other = (bev == conn->input) ? conn->output : conn->input;
dbg(("Got data on encrypted side\n")); - if (conn->proto->recv(conn->proto_state, + if (conn->proto->recv(conn->proto->state, bufferevent_get_input(bev), bufferevent_get_output(other)) < 0) conn_free(conn); diff --git a/src/network.h b/src/network.h index 048f80c..619e45f 100644 --- a/src/network.h +++ b/src/network.h @@ -8,13 +8,14 @@ #ifndef NETWORK_H #define NETWORK_H
+#include <stdlib.h> + typedef struct listener_t *listener;
struct sockaddr; struct event_base; struct socks_state_t;
- #define LSN_SIMPLE_CLIENT 1 #define LSN_SIMPLE_SERVER 2 #define LSN_SOCKS_CLIENT 3 @@ -33,8 +34,6 @@ void listener_free(listener_t *listener); #ifdef NETWORK_PRIVATE typedef struct conn_t { struct socks_state_t *socks_state; - void *proto_state; /* ASN Is this correct? - Supposedly, it represents a generic proto state. */ struct protocol_t *proto; /* ASN Do we like this here? We probably don't. But it's so convenient!! So convenient! */ int mode; diff --git a/src/plugins/obfs2.c b/src/plugins/obfs2.c new file mode 100644 index 0000000..90a8528 --- /dev/null +++ b/src/plugins/obfs2.c @@ -0,0 +1,359 @@ +/* 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 "../crypt.h" +#include "obfs2.h" +#include "../util.h" +#include "../protocol.h" + +void * +obfs2_new(struct protocol_t *proto_struct) { + proto_struct->destroy = (void *)obfs2_state_free; + proto_struct->init = (void *)obfs2_state_new; + proto_struct->handshake = (void *)obfs2_send_initial_message; + proto_struct->send = (void *)obfs2_send; + proto_struct->recv = (void *)obfs2_recv; + + return NULL; +} + +/** 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(obfs2_state_t *state, const char *keytype) +{ + 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(obfs2_state_t *state, const uchar *seed, + const char *keytype) +{ + 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; +} + +/** + 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. + */ +obfs2_state_t * +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(obfs2_state_t *state, + const char *secret, size_t secretlen) +{ + 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. + */ +int +obfs2_send_initial_message(obfs2_state_t *state, struct evbuffer *buf) +{ + 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. + */ +int +obfs2_send(obfs2_state_t *state, + struct evbuffer *source, struct evbuffer *dest) +{ + 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(obfs2_state_t *state) +{ + 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" */ +int +obfs2_recv(obfs2_state_t *state, struct evbuffer *source, + struct evbuffer *dest) +{ + 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); +} + +void +obfs2_state_free(obfs2_state_t *s) +{ + if (s->send_crypto) + crypt_free(s->send_crypto); + if (s->send_padding_crypto) + crypt_free(s->send_padding_crypto); + if (s->recv_crypto) + crypt_free(s->recv_crypto); + if (s->recv_padding_crypto) + crypt_free(s->recv_padding_crypto); + if (s->pending_data_to_send) + evbuffer_free(s->pending_data_to_send); + memset(s, 0x0a, sizeof(obfs2_state_t)); + free(s); +} diff --git a/src/plugins/obfs2.h b/src/plugins/obfs2.h new file mode 100644 index 0000000..dd0c842 --- /dev/null +++ b/src/plugins/obfs2.h @@ -0,0 +1,88 @@ +/* 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 + +obfs2_state_t *obfs2_state_new(int *initiator); +void obfs2_state_set_shared_secret(obfs2_state_t *state, + const char *secret, size_t secretlen); +void obfs2_state_free(obfs2_state_t *state); +int obfs2_send_initial_message(obfs2_state_t *state, struct evbuffer *buf); +int obfs2_send(obfs2_state_t *state, + struct evbuffer *source, struct evbuffer *dest); +int obfs2_recv(obfs2_state_t *state, struct evbuffer *source, + struct evbuffer *dest); + +void *obfs2_new(struct protocol_t *proto_struct); + + +#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/protocol.c b/src/protocol.c new file mode 100644 index 0000000..c186857 --- /dev/null +++ b/src/protocol.c @@ -0,0 +1,31 @@ +#include "stdlib.h" +#include "stdio.h" + +#include "protocol.h" +#include "crypt.h" +#include "network.h" + +#include "plugins/obfs2.h" + +/** + This function returns a protocol_t structure based on the mode + of obfsproxy +*/ +struct protocol_t * +set_up_protocol(int protocol) { + struct protocol_t *proto = calloc(1, sizeof(struct protocol_t)); + + if (protocol == BRL_PROTOCOL) { + proto->new = &obfs2_new; + proto->new(proto); + printf("Protocol constructed\n"); + + if (initialize_crypto() < 0) { + fprintf(stderr, "Can't initialize crypto; failing\n"); + return NULL; + } + } + /* elif { other protocols } */ + + return proto; +} diff --git a/src/protocol.h b/src/protocol.h new file mode 100644 index 0000000..159f3e3 --- /dev/null +++ b/src/protocol.h @@ -0,0 +1,32 @@ +#ifndef PROTOCOL_H +#define PROTOCOL_H + +/* ASN I'm gonna be calling crypt_protocol.c BRL_RPOTOCOL for now. Yes. */ +#define BRL_PROTOCOL 1 + +struct protocol_t *set_up_protocol(int protocol); + +/* ASN */ +struct protocol_t { + /* Constructor: creates the protocol; sets up functions etc. */ + void *(*new)(struct protocol_t *self); + /* Destructor */ + void (*destroy)(void *arg); + + /* does nessesary initiation steps; like build a proto state etc. */ + void *(*init)(void *arg); + + /* does handshake. Supposedly all protocols have a handshake. */ + void *(*handshake)(void *state, void *buf); + /* send data function */ + int (*send)(void *state, void *source, + void *dest); + /* receive data function */ + int (*recv)(void *state, void *source, + void *dest); + + /* ASN do we need a proto_get_state()? */ + void *state; +}; + +#endif diff --git a/src/test/unittest_socks.c b/src/test/unittest_socks.c index 2b4e38a..ec24e5f 100644 --- a/src/test/unittest_socks.c +++ b/src/test/unittest_socks.c @@ -11,7 +11,7 @@ #include "../socks.h" #include "../crypt.h" #include "../util.h" -#include "../crypt_protocol.h" +#include "../plugins/obfs2.h"
static void test_socks_send_negotiation(void *data)
tor-commits@lists.torproject.org