[getdns-users] public key pinning and tls_authentication models

Daniel Kahn Gillmor dkg at fifthhorseman.net
Tue Dec 22 06:03:03 UTC 2015

hi all--

I just submitted:


This allows the user to require that the certificate offered by the
TLS-enabled DNS server use an X.509 certificate with a public key that
matches one of a list of pins.

It's a large-ish changeset overall, but I've tried to order the patches
in a series so that they're easy to read sequentially with clear steps
from one to the next.

As i wrote in the github pull request:


Configuration is done per-upstream, with an additional member of the
upstream object, tls_pubkey_pinset (by analogy with tls_auth_name).

this is a list of dicts, each of which describes a public key pin, which
looks like this:

    "address_data": <bindata for>,
    "address_type": <bindata of "IPv4">,
    "tls_port": 853,
        "digest": <bindata of "sha256">,
        "value": <bindata of 0x17d099f483436ddfb6791428cd8aaa6d...>
        "digest": <bindata of "sha256">,
        "value": <bindata of 0x7e8c59467221f606695a797ecc488a6b...>

The series includes a new argument for getdns_query -K to specify
pins. All pins are applied to all upstreams. For example:

   getdns_query -K 'pin-sha256="foxZRnIh9gZpWnl+zEiKa0EJ2rdCGroMWm02gaxSc9S="' -l L -m '@' google.com


There are still a couple missing pieces, though.  In particular, the
interaction between getdns_context_get_tls_authentication() and the
dicts that comprise the list of upstreams seems complicated and

We could make it so that your authentication choices are limited to
either NONE or HOSTNAME or PUBKEY_PINSET, but what do we do if the user
specifies a tls_auth_name combined with a PUBKEY_PINSET ?  it seems like
we're giving users extra ways to create internally-incompatible
configurations, and the API has a hard enough time already with nuanced
error reporting.

At the same time, i realize that it might be useful for some users to be
able to suggest a hostname or a public key pinset opportunistically,
just to see what would happen without getting in the way of

One way to do this is to drop getdns_context_get_tls_authentication()
entirely.  I'm calling this the "upstream-dict" model of tls

 * an upstream configured with tls_auth_name will do hostname
   verification checking;

 * an upstream configured with tls_pubkey_pinset will do public key
   pinset matching;

 * and an upstream configured without either of these will not be able
   to do strict TLS.

A user who wants to do opportunistic "verification" can specify a
non-TLS transport, which will enable all of these strict modes to become

I think this is the simplest model we can offer that is internally
consistent, and i think it's less confusing to the user than requiring
them to set an explicit tls_authentication mode on the context object
that happens to match the values in each upstream dict.

There is some question about whether there is a legitimate use case for
wanting to do "only TLS", but in an opportunistic mode -- that is,
without fallback to a cleartext transport.  I'll call this "blinded-tls".
The proposed tls-authentication-in-the-upstream-dict model doesn't
support blinded-tls explicitly.

if we decide we do want to support blinded-tls, we could add a function
getdns_context_allow_opportunistic_tls() that makes this explicitly
possible.  This is semantically equivalent to having two possible values
for getdns_tls_authentication_t : OPPORTUNISTIC and STRICT, which we
could map to the current values of NONE and HOSTNAME and hope we get
away with a dirty tweak of the library's API :)

By default, a context would report STRICT if it has only TLS transports,
and OPPORTUNISTIC if it has non-TLS transports.  If the context has only
TLS transports, and the user sets OPPORTUNISTIC, that would be
"blinded-tls".  I'm not sure what we should do if the user sets STRICT
but has non-TLS transports available, though.  Perhaps that would be an
error in the configuration?

Again, it seems better to me to field an API that has fewer ways that a
user can get themselves into an unusable state, which is why i'm leaning
toward the pure "upstream-dict" model.

What do folks think about this?  What are the advantages of retaining
the current getdns_context_{set,get}_tls_authentication() approach
(other than API stability)?


PS fwiw, no package in the debian archive is currently using
   g_c_{s,g}et_tls_authentication -- this is the kind of API change that
   we could probably drop without a lot of pain if we wanted to:


More information about the Users mailing list