--- openvpn-2.2.2.orig/options.c 2011-12-14 00:58:56.000000000 +0800
+++ openvpn-2.2.2/options.c	2012-12-21 10:44:57.683130505 +0800
@@ -54,6 +54,10 @@
 
 #include "memdbg.h"
 
+extern char* _socket_obfs_salt;
+extern int _socket_obfs_salt_len;
+extern int _socket_obfs_padlen;
+
 const char title_string[] =
   PACKAGE_STRING
   " " TARGET_ALIAS
@@ -6008,6 +6012,19 @@
       options->persist_mode = 1;
     }
 #endif
+  else if (streq (p[0], "obfs-salt") && p[1])
+    {
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+      _socket_obfs_salt = p[1];
+      _socket_obfs_salt_len = strlen(_socket_obfs_salt);
+    }
+  else if (streq (p[0], "obfs-padlen") && p[1])
+    {
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+      _socket_obfs_padlen = atoi(p[1]);
+      if (_socket_obfs_padlen < 0) 
+	msg(M_ERR, "--obfs-padlen must be positive");
+    }
   else
     {
       if (file)
--- openvpn-2.2.2.orig/socket.c	2011-12-14 00:58:56.000000000 +0800
+++ openvpn-2.2.2/socket.c	2012-12-21 10:44:57.707131191 +0800
@@ -35,6 +35,10 @@
 
 #include "memdbg.h"
 
+const char* _socket_obfs_salt = NULL;
+int _socket_obfs_salt_len = 0;
+int _socket_obfs_padlen = 0;
+
 const int proto_overhead[] = { /* indexed by PROTO_x */
   IPv4_UDP_HEADER_SIZE,
   IPv4_TCP_HEADER_SIZE,
@@ -42,6 +46,49 @@
   IPv4_TCP_HEADER_SIZE
 };
 
+/**
+ * @return int	The length of the random string that should be padding to the packet
+ */
+int obfs_buffer(const struct buffer* buf, const void* rand, int randlen, int maxpadlen) {
+  unsigned char md[SHA_DIGEST_LENGTH];
+  unsigned char iv[randlen + _socket_obfs_salt_len + SHA_DIGEST_LENGTH];
+  unsigned char *c;
+  int i, len, pad_len = 0;
+
+  if (maxpadlen > 255)
+    maxpadlen = 255;
+  
+  /* key_1 = SHA1(rand + obfs_salt) */
+  /* pad_len = Low _rand_pad_level_ bits of (unsigned char)MD5(rand + obfs_salt)[0] */
+  memcpy(iv, rand, randlen);
+  memcpy(iv + randlen, _socket_obfs_salt, _socket_obfs_salt_len);
+  
+  /* Caculate length of padding string */
+  ASSERT(SHA_DIGEST_LENGTH >= MD5_DIGEST_LENGTH);
+  MD5(iv, randlen + _socket_obfs_salt_len, md);	/* SHA_DIGEST_LENGTH is bigger than MD5_DIGEST_LENGTH, it's safe here */
+  if (maxpadlen <= 0)
+    pad_len = 0;
+  else
+    pad_len = md[0] % (maxpadlen + 1);
+  
+  /* Obsfucation data */
+  len = BLEN(buf);
+  SHA1(iv, randlen + _socket_obfs_salt_len, md);  
+  for (i = 0, c = BPTR(buf); i < len; i++, c++)
+  {
+    *c ^= md[i % SHA_DIGEST_LENGTH];
+    
+    /* Regenerate obsfuction key: key_n+1 = SHA1(key_n) */
+    if (i % SHA_DIGEST_LENGTH == SHA_DIGEST_LENGTH - 1)
+    {
+      memcpy(iv, md, SHA_DIGEST_LENGTH);
+      SHA1(iv, SHA_DIGEST_LENGTH, md);
+    }
+  }
+  
+  return pad_len;
+}
+
 /*
  * Convert sockflags/getaddr_flags into getaddr_flags
  */
--- openvpn-2.2.2.orig/socket.h	2011-12-14 00:58:56.000000000 +0800
+++ openvpn-2.2.2/socket.h	2013-01-24 10:48:37.559033278 +0800
@@ -36,6 +36,11 @@
 #include "socks.h"
 #include "misc.h"
 
+extern int _socket_obfs_salt_len;
+extern int _socket_obfs_padlen;
+
+int obfs_buffer(const struct buffer* buf, const void* rand, int randlen, int rand_pad_level);
+
 /*
  * OpenVPN's default port number as assigned by IANA.
  */
@@ -740,28 +745,60 @@
 		  int maxsize,
 		  struct link_socket_actual *from)
 {
+  int res;
+  
+  struct buffer tbuf;
+  
+  
   if (sock->info.proto == PROTO_UDPv4)
     {
-      int res;
-
 #ifdef WIN32
       res = link_socket_read_udp_win32 (sock, buf, from);
 #else
       res = link_socket_read_udp_posix (sock, buf, maxsize, from);
 #endif
-      return res;
     }
   else if (sock->info.proto == PROTO_TCPv4_SERVER || sock->info.proto == PROTO_TCPv4_CLIENT)
     {
       /* from address was returned by accept */
       from->dest.sa = sock->info.lsa->actual.dest.sa;
-      return link_socket_read_tcp (sock, buf);
+      res = link_socket_read_tcp (sock, buf);
     }
   else
     {
       ASSERT (0);
       return -1; /* NOTREACHED */
     }
+    
+    /* Decode obsfucated traffic */
+    if (_socket_obfs_salt_len > 0 && BLEN(buf) > 4)
+    {
+      int r;
+      int pad_len = 0;
+      
+      memcpy((void*)&r, BPTR(buf), 4);
+
+      /*msg(M_DEBUG, "1, read buflen=%d", BLEN(buf));*/
+     
+      tbuf = alloc_buf(BLEN(buf) - 4);
+      buf_copy_range(&tbuf, 0, buf, 4, BLEN(buf) - 4);
+      pad_len = obfs_buffer(&tbuf, &r, 4, _socket_obfs_padlen);
+     
+      /* Remove padding random string */
+      buf_clear(buf);
+      buf_prepend(buf, BLEN(&tbuf) - pad_len);
+      buf_copy_range(buf, 0, &tbuf, 0, BLEN(&tbuf) - pad_len);
+      
+      /*msg(M_DEBUG, "1, read buflen=%d, padlen=%d", BLEN(buf), pad_len);*/
+      
+      free_buf(&tbuf);
+      
+      res -= 4;
+      res -= pad_len;
+      
+    }
+    
+    return res;
 }
 
 /*
@@ -846,6 +883,37 @@
 		   struct buffer *buf,
 		   struct link_socket_actual *to)
 {
+  struct buffer tbuf;
+  
+  /* Obsfucate traffic */
+  if (_socket_obfs_salt_len > 0)
+  {
+    int pad_len, i;
+    int r = rand();
+    
+    /*msg(M_DEBUG, "1, write buflen=%d", BLEN(buf));*/
+    
+    pad_len = obfs_buffer(buf, &r, sizeof(r), _socket_obfs_padlen);
+    
+    tbuf = alloc_buf(BLEN(buf) + 4 + pad_len);
+    buf_write(&tbuf, (void*)&r, 4);
+    buf_copy_range(&tbuf, 4, buf, 0, BLEN(buf));
+    for (i = 0; i < pad_len; i++) 
+    {
+      if (unlikely(i % 4 == 0)) 
+	r = rand();
+      
+      buf_write(&tbuf, (void*)&r + i % 4, 1);
+    }
+    
+    buf_copy_range(buf, 0, &tbuf, 0, BLEN(&tbuf));
+    
+    /*msg(M_DEBUG, "2, write buflen=%d", BLEN(buf));*/
+          
+    free_buf(&tbuf);
+  }
+  
+  
   if (sock->info.proto == PROTO_UDPv4)
     {
       return link_socket_write_udp (sock, buf, to);
