
Implementation details
======================

Cryptographic library
---------------------

- The wheel hasn't been reinvented.

- The crypto constructions come from [NaCl](http://nacl.cr.yp.to/) and
  the proxy leverages [libsodium](https://github.com/jedisct1/libsodium).

- Why NaCl? Unbloated, blazing fast, and less error-prone that other libraries.
  See http://cr.yp.to/highspeed/coolnacl-20111201.pdf

- crypto_box() for authenticating/encrypting queries and replies,
  crypto_sign() for signing certificates, randombytes_*() for random numbers.
  See the [libsodium documentation](http://www.libsodium.org/doc/) for details.

Event-notification library
--------------------------

- Uses libevent2. Unbound's boilerplate is also excellent, but it hasn't been
  packaged as a standalone library yet.

- Because it is totally awesome for writing portable software.

- Bundled with dnscrypt, for now, because it's a modified version (so
  that evdns can cope with TXT records) and because some distributions
  are still shipping dead old versions.

Certificates
------------

The following information has to be provided to the proxy:

- The provider name
- The provider public key
- The resolver IP address

These information can be automatically retrieved from the global list
(the `dnscrypt-proxy.csv` file) based on the name provided using the
`-R` (`--resolver-name`) command-line option.

At startup and every 60 minute, the proxy directly connects to the specified
resolver IP address and issues a TXT query for the provider name. The
first component of the provider name indicates the latest protocol version,
or the version range, supported by the client. Right now, this should
be 2. Always.

One or more TXT records are returned, each containing a signed certificate.
The provider public key is only used to verify a certificate, never for
authenticating/encrypting queries.

Certificates have the following header:
- 4 byte magic header DNSC
- 2 byte major version
- 2 byte minor version

Followed by signed content (the signature adds 512 bits to the payload):
- server 256-bit public key
- 8 byte magic header to use for queries using this specific key
- 4 byte serial number
- 4 + 4 byte validity period (two timestamps)

This is the current structure of the second version of the protocol.
Don't assume anything about its length, it is very likely to change
after a version bump.

The proxy drops invalid certificates for the current date, and picks the one
with the highest serial number.

More than one certificate may be returned when keys rollovers or
function changes are performed.

Resolvers are never signing certificates themselves, they are just providing
pre-signed certificates.

Queries
-------

Queries and replies are inspired from djb's dnscurve protocol:
http://www.dnscurve.org/

Legacy mode:
- The proxy generates a new, in-memory key pair at startup, and reuses
it for all queries.

Ephemeral key pair mode:
- The proxy always generates a new, in-memory session key at startup.
- This session key and the client nonce are used to derive an ephemeral key
pair for each query.

Random padding is added to queries and replies, using a 64 byte block size.

Encrypted queries are prefixed with the following header structure:

- 8 byte magic header (provided by the chosen certificate)
- A 256 bit client public key
- A 96 bit client nonce (64 bit monotically increasing timestamp +
  32 random bits)
- A 128 bit Poly1305 MAC

Replies are prefixed with the following header structure:

- 8 byte static magic header r6fnvWJ8
- A 192 bit nonce: the 96 bit client-supplied nonce + a 96 bit server
nonce extension.
- A 128 bit Poly1305 MAC.

The proxy immediately discards replies to queries made more than 10
second ago and replies that don't match the client-supplied nonce.

Miscellaneous
-------------

If you need extra monitoring/profiling, the proxy provides a bunch of
dtrace probes on OSX, as the dnscrypt-proxy provider.
See src/dnscrypt-proxy/probes_dnscrypt_proxy.d

The proxy doesn't cache replies. Neither does it perform any DNSSEC
validation yet.

This is better handled by a separate process or by linking libunbound.

The proxy does alter queries, though, in order to:

- immediately reply with a "reply truncated" message to a UDP query when
the --tcp-port switch has been turned on.

- add an empty OPT section in order to advertise a payload size unless
an EDNS section was already present or unless --payload-size with a
< 512 bytes size has been specified.

OSX Yosemite, OpenBSD/amd64 and Dragonfly BSD/amd64 are the primary
development platforms, but the code has been designed to be as
portable as possible, and patches to support other operating systems
and architectures are more than welcome.
