Skip to content

Dark Decoys Tagging and Signalling

Sergey Frolov edited this page May 21, 2020 · 1 revision

Moved from the old wiki, last edit was on Feb 7, 2019.

Motivation

In Dark Decoys we don't need to decrypt the original flow, and station engineers agree that if we have a chance not to import OpenSSL, then we should not import OpenSSL. However, we used to have variable-length protobuf in a header of first HTTP request, which we don't want to decrypt anymore.

We do however need multiple cryptographic keys now. And we do want a variable-length sized protobuf in there for extensibility.

New tag format

High-level decryption algorithm:

  1. extract shared_secret from Representative and generate encryption keys
  2. decrypt Fixed-Size Payload, which will contain size of Variable-Size Payload
  3. decrypt Variable-Size Payload
[     VSP    ][VSP GMAC][Representative][ FSP  ][ FSP GMAC ][ Suffix ]
 \ variable* /  \ 16* /   \ 32 bytes* /  \ 6* /  \   16*  /  \  4   /

* before base64-style encoding. Actual size on the wire will increase by 33%.

Representative

Same old stuff. Grab eligator-transformed representative of client's public key, combine it with station's private key, get 32 byte-long shared_secret.

Located at 92 bytes offset from the end. Calculations(from the end):

  • [16 bytes] GMAC for the whole TLS record
  • [4 bytes] suffix
  • [16 bytes] GMAC from FSP encryption
  • [6 bytes] FSP
  • [32 bytes] representative

(32 + 6 + 16) / 3 * 4 + 4 + 16 = 92

Key derivation

Use HKDF to expand and extract with following parameters

  • PRK = SHA256
  • SECRET = shared_secret, derived from representative
  • SALT = []byte{"tapdancetapdancetapdancetapdance"}
  • INFO = nil

This gives us a Key Derivation Function, that is able to generate a stream, which we then use to derive multiple secure keys of any length:

  1. FSP_key = first 16 bytes from HKDF
  2. FSP_iv = next 12 bytes
  3. VSP_key = next 16 bytes
  4. VSP_iv = next 12 bytes
  5. new_master_secret = next 48 bytes
  6. DarkDecoy_seed = next 16 bytes

FSP - fixed-size payload

Encrypted with AES-128-GCM, using FSP_key and FSP_iv.

  • size of encrypted VSP [2 bytes, BigEndian uint16] - MUST be divisible by 3
  • flags [1 byte]
  • Unassigned [3 bytes]

VSP - variable-size payload

Empty right now. Will probably contain ClientToStation protobuf

Suffix

4 bytes long.
Allows us to append \r\n\r\n at the end of the request to complete it. For incomplete requests, may be filled with anything in ASCII range.