[tor-commits] [obfsproxy/master] * Renamed the OpenSSH obfuscation variant plugin to "obfs2" and moved
nickm at torproject.org
nickm at torproject.org
Wed Apr 27 00:17:45 UTC 2011
commit bff0015cb6aef8cde564c2b3d51f3a9019b25533
Author: George Kadianakis <desnacked at 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)
More information about the tor-commits
mailing list