[tor-commits] [obfsproxy/master] * Revived obfs2 unit tests.

nickm at torproject.org nickm at torproject.org
Wed Apr 27 00:17:45 UTC 2011


commit aa399c851ad5e4741edde13d05d2f92c59d20335
Author: George Kadianakis <desnacked at gmail.com>
Date:   Mon Apr 11 02:34:15 2011 +0200

    * Revived obfs2 unit tests.
    * Renamed unittest_protocol.c to unittest_obfs2.c
    * Renamed src/plugins/ to src/protocols/
---
 Makefile.am                  |   14 +-
 src/plugins/dummy.c          |   63 ------
 src/plugins/dummy.h          |   10 -
 src/plugins/obfs2.c          |  405 -----------------------------------
 src/plugins/obfs2.h          |   81 -------
 src/plugins/obfs2_crypt.c    |  206 ------------------
 src/plugins/obfs2_crypt.h    |   62 ------
 src/protocols/dummy.c        |   63 ++++++
 src/protocols/dummy.h        |   10 +
 src/protocols/obfs2.c        |  405 +++++++++++++++++++++++++++++++++++
 src/protocols/obfs2.h        |   81 +++++++
 src/protocols/obfs2_crypt.c  |  206 ++++++++++++++++++
 src/protocols/obfs2_crypt.h  |   62 ++++++
 src/test/unittest_obfs2.c    |  476 ++++++++++++++++++++++++++++++++++++++++++
 src/test/unittest_protocol.c |  469 -----------------------------------------
 15 files changed, 1310 insertions(+), 1303 deletions(-)

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





More information about the tor-commits mailing list