So, I think it’s fairly elegant and straightforward to put a key into DNS like so:

http://www.hospital-link.org IN TXT “v=key1 ha=sha1 h=f1d2d2f924e986ac86fdf7b36c94bcdf32beec15”

On connecting to http://www.hospital-link.org over TLS, a certificate will be delivered. Traditionally, this certificate would be interrogated to make sure it was signed by a trusted certificate authority. In the new model:

If DNSSEC says the above TXT record chains to the (one) DNS root, and

The subtype of the TXT record is key1, and

The SHA-1 hash of the delivered certificate is precisely f1d2d2f924e986ac86fdf7b36c94bcdf32beec15, and

TLS is happy with the public key in the certificate

…then the connection is considered authenticated, whether or not a CA ultimately signed the certificate. That’s it. Move on with life. Cheap.

Can it really be this easy? I’ll be honest, some people aren’t the biggest fans of this design. Implementers like it. Ops guys like it. I like it.

Some old school DNS people do not like it. They don’t want to see everything shoved under a single record type (TXT or otherwise), and they don’t like the idea of a seemingly unstructured textual field containing anything important. Heck, they even wrote an Informational RFC on the subject. The core arguments in this doc:

No semantics to prevent collision with other use

Space considerations in the DNS message

It’s really not a great RFC, but these aren’t meaningless concerns. However, the heart of engineering is understanding not just why systems succeed, but why they fail. Lets talk about why, despite the above, I’m convinced public keys in DNS should be encoded as TXT records.

The primary reason is that the story of TXT is the story of real world systems trying non-TXT, and, well, running away screaming. In the last ten years, four major projects have all gone TXT:

SPF (Sender Policy Framework) DKIM (DomainKeys) GPG’s PKA (PGP Key Retrieval) FreeSWAN’s IPSec (X-IPSec-Server)

They have company. From a sample of DNS traces, looking at the count of unique types per unique name:

0.40 143453512 A 0.31 110097927 NS 0.13 48033288 CNAME 0.07 23989120 MX 0.04 12841932 RRSIG 0.02 8314220 PTR 0.02 7665020 TXT 0.01 2575267 NSEC 0.00 1557321 NSEC3 0.00 563675 SRV 0.00 276820 AAAA 0.00 53855 NULL 0.00 44411 SPF 0.00 24308 DS 0.00 3190 DNSKEY 0.00 1498 RP 0.00 1216 HINFO 0.00 1118 DLV 0.00 687 DNAME 0.00 557 LOC 0.00 545 514 0.00 491 NAPTR

Excluding DNSSEC, which is its own universe, and AAAA, which is a trivial blob of bits (a 128 bit IPv6 address), TXT is about three orders of magnitude more popular than all RR types invented in the last ten years combined. Data encoded in TXT is almost as popular as Reverse DNS!

And it’s not like projects didn’t have the opportunity to use custom RRTYPEs.

Consider: For half of these packages (GPG and FreeSWAN), RFC’s existed to store their data already, but (and this is important) either the developers found the RFCs too difficult to implement, or users found the code too tricky to deploy. In FreeS/WAN’s case, this was so true that:

As of FreeS/WAN 2.01, OE uses DNS TXT resource records (RRs) only (rather than TXT with KEY). This change causes a “flag day”.

Wow. That is fairly tremendous. Flag days — the complete shift of a system from a broken design to a hopefully better one — are rare to the point of being almost unheard of in networking design. They are enormously painful, and only executed when the developer believes their product is absolutely doomed to failure otherwise.

There’s a lot of talk about how custom RR’s aren’t that bad. Talk is cheap. Evidence that a design choice forced a flag day is rare, precious, and foreboding.

The reality — the real precedent, far larger than the world of DNS or even security — is that for operational and developmental reasons, developers have abandoned binary formats. XML, and even its lighter cousin JSON, are not the smallest possible way to encode a message the world has ever known. It is possible to save more space.

Sorry, X.509. We’ve abandoned ASN.1, even if bit for bit it’s more space efficient. We abandoned it for a reason. The programming community is simply done with the sort of bit-fiddly constructs that were popular in the 90’s, and that most DNS RR’s are constructed from, even if they are easy to parse in C.

Binary parsers (outside of media formats) are dead. Frankly, as the number one source of exploitable crashes in most software, I’m not going to miss them. Heck, we’re not even using TCP ports anymore — we have port 80, and we subtype the heck out of it at the URL layer (much as TXT is subtyped with v=key1).

It doesn’t help that, most of the time, DNS knows what fields it’s going to have to store, and identifies them positionally. Contrast this with the coming reality of storing keys in DNS, where over time, we’re absolutely going to be adding more flags and bits. That binary format will not be pretty.

Now, I have no interest in putting XML or even JSON into DNS (though the latter wouldn’t be the worst thing ever). But what we’ve seen is reasonable consensus regarding what should go into TXT records:

SPF: “v=spf1 a -all”

DKIM: “v=DKIM1;p=MIGfMA0G … cQ2QIDAQAB”

GPG: “v=pka1;fpr=[#1];uri=[#2]”

FreeS/WAN: “X-IPsec-Server(10)=192.1.1.5 AQMM…3s1Q==”

Identical? No. But straight key value pairs, and only one protocol that doesn’t begin with a magic cookie? That’s doable. That’s easy.

That’s cheap to deploy. And cheap to deploy matters.

Now, here’s an interesting question:

“Why not use a custom RRTYPE, but fill it with key value pairs, or even JSON? Why overload TXT?”

It’s a reasonable question. The biggest source of content expansion comes from the fact that now irrelevant records need to be sent, if there are multiple applications sitting on TXT. Why not have multiple TXTs?

Perhaps, in the future, we could move in this direction. Nowhere does it say that the same record can’t show up with multiple types. (SPF tried this, without much success though.) But, today, a big part of what makes non-TXT expensive to deploy is that name servers traditionally needed to be rebuilt to support a new record type. After all, the server wasn’t just hosting records, it was compiling them into a binary format.

(Those who are IT administrators, but not DNS administrators, are all facepalming).

Eventually, RFC 3267 was built to get around this, allowing zone files to contain blobs like this:

sshfp TYPE44 \# 22 01 01 c691e90714a1629d167de8e5ee0021f12a7eaa1e

To be gentle, zone files filled with hex and length fields, requiring record compilers to update, aren’t exactly operation’s idea of a good, debuggable time. This is expensive.

Of course, a lot of people aren’t directly editing zone files anymore. They’re operating through web interfaces. Through these interfaces, either you’re putting in a resource type the server knows of, you’re putting text into a TXT record, or…you’re putting in nothing.

It’s possible to demand that web interfaces around the Internet be updated to support your new record type. But there is no more expensive ask than that which demands a UI shift. A hundred backends can be altered for each frontend that needs to be poked at. The backends support TXT. They don’t support anything else, and that fact isn’t going to change anytime soon.

And then there are the firewalls — the same firewalls which, like it or not, have made HTTP on 80/tcp the Universal Tunneling Protocol. Quoting the above RFC 5507 (which observes the following, and then just seems to shrug):

…Firewalls have dropped queries or responses with Resource Record Types that are unknown to the firewall. This is, for example, one of the reasons the ENUM standard reuses the NAPTR Resource Record, a decision that today might have gone to creating a new Resource Record Type instead.

There’s a reason all those packages abandoned non-TXT. Whatever downsides there are to using TXT, and I do admit there are some, those who have chosen an architecturally pure custom RRTYPE have failed, while those who’ve “temporarily” went TXT have prospered.

Next up, lets talk about the challenges of putting bootstrap content into the DNS itself.

"v=pka1;fpr=[#1];uri=[#2]"