        obfs2 (The Twobfuscator)

0. Protocol overview

   This is a protocol obfuscation layer for TCP protocols.  Its purpose
   is to keep a third party from telling what protocol is in use based
   on message contents.  It is based on brl's ssh obfuscation protocol.

   It does not provide authentication or data integrity.  It does not
   hide data lengths.  It is more suitable for providing a layer of
   obfuscation for an existing authenticated protocol, like SSH or TLS.

   The protocol has two phases: in the first phase, the parties
   establish keys.  In the second, the parties exchange superenciphered
   traffic.

1. Primitives, notation, and constants.

    H(x) is SHA256 of x.
    H^n(x) is H(x) called iteratively n times.

    E(K,s) is the AES-CTR-128 encryption of s using K as key.

    x | y is the concatenation of x and y.
    UINT32(n) is the 4 byte value of n in big-endian (network) order.
    SR(n) is n bytes of strong random data.
    WR(n) is n bytes of weaker random data.
    "xyz" is the ASCII characters 'x', 'y', and 'z', not NUL-terminated.
    s[:n] is the first n bytes of s.
    s[n:] is the last n bytes of s.

    MAGIC_VALUE      is  0x2BF5CA7E
    SEED_LENGTH      is  16
    MAX_PADDING      is  8192
    HASH_ITERATIONS  is  100000

    KEYLEN is the length of the key used by E(K,s) -- that is, 16.
    IVLEN is the length of the IV used by E(K,s) -- that is, 16.

    HASHLEN is the length of the output of H() -- that is, 32.

    MAC(s, x) = H(s | x | s)

    A "byte" is an 8-bit octet.

    We require that HASHLEN >= KEYLEN + IVLEN

2. Key establishment phase.

   The party who opens the connection is the 'initiator'; the one who
   accepts it is the 'responder'.  Each begins by generating a seed
   and a padding key as follows.  The initiator generates:

    INIT_SEED = SR(SEED_LENGTH)
    INIT_PAD_KEY = MAC("Initiator obfuscation padding", INIT_SEED)[:KEYLEN]

   And the responder generates:

    RESP_SEED = SR(SEED_LENGTH)
    RESP_PAD_KEY = MAC("Responder obfuscation padding", INIT_SEED)[:KEYLEN]

   Each then generates a random number PADLEN in range from 0 through
   MAX_PADDING (inclusive).

   The initiator then sends:

    INIT_SEED | E(INIT_PAD_KEY, UINT32(MAGIC_VALUE) | UINT32(PADLEN) | WR(PADLEN))

   and the responder sends:

    RESP_SEED | E(RESP_PAD_KEY, UINT32(MAGIC_VALUE) | UINT32(PADLEN) | WR(PADLEN))

   Upon receiving the SEED from the other party, each party derives
   the other party's padding key value as above, and decrypts the next
   8 bytes of the key establishment message.  If the MAGIC_VALUE does
   not match, or the PADLEN value is greater than MAX_PADDING, the
   party receiving it should close the connection immediately.
   Otherwise, it should read the remaining PADLEN bytes of padding data
   and discard them.

   Additional keys are then derived as:

     INIT_SECRET = MAC("Initiator obfuscated data", INIT_SEED|RESP_SEED)
     RESP_SECRET = MAC("Responder obfuscated data", INIT_SEED|RESP_SEED)
     INIT_KEY = INIT_SECRET[:KEYLEN]
     INIT_IV = INIT_SECRET[KEYLEN:]
     RESP_KEY = RESP_SECRET[:KEYLEN]
     RESP_IV = RESP_SECRET[KEYLEN:]

   The INIT_KEY value keys a stream cipher used to encrypt values from
   initiator to responder thereafter.  The stream cipher's IV is
   INIT_IV.  The RESP_KEY value keys a stream cipher used to encrypt
   values from responder to initiator thereafter.  That stream cipher's
   IV is RESP_IV.

3. Shared-secret extension

   Optionally, if the client and server share a secret value SECRET,
   they can replace the MAC function with:

      MAC(s,x) = H^n(s | x | H(SECRET) | s)

   where n = HASH_ITERATIONS.
