[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