Securely Connecting To Local Devices: Cryptographically Generated Domain Names
Have you ever had a device, such as a printer, with a web interface? Have you ever tried to connect to that web interface via HTTPS? If you have, you’ll almost certainly have gotten an “untrusted certificate” warning, pointing out that you are vulnerable to a monster-in-the-middle (MITM) attack. Unless you are willing to set up and trust your own certificate authority and upload a custom certificate, it’s almost impossible to avoid this. I strongly suspect almost all people don’t know how to fix the problem or aren’t willing to put in the work to mitigate the risk. If you are interested in a solution to this problem, read on!
What’s The Problem?
The problem is that the only way to get a certificate that a web browser
will trust is to prove possession of a domain name. However, the device
does not own any globally-valid domain names! It owns a .local
domain
name, but .local
domains are not globally valid, so a publicly-trusted
certification authority cannot issue certificates for them. One can
work around this by using a private certification authority, but this is
infeasible for non-technical users. The result is that most local
communication is either unencrypted or uses self-signed unpinned
certificates, neither of which are secure. Furthermore, this provides
an incentive for device manufacturers to use cloud-based solutions,
which support HTTPS without any hurdles.
HTTPS For Every Device
This problem can be solved by embedding the fingerprint of a public key in the domain name, producing a cryptographically-generated domain name, or CGDN. Only the owner of the corresponding private key can issue a certificate for such a domain or sign DNSSEC RRSIGs for them. Such domain names are no longer human-memorable, but phones nowadays generally have a camera, which allows for the CGDN to be included in a QR code. QR codes are public, but critically the optical signal cannot be practically tampered with except by physical alteration of the hardware.
The domain name is generated by hashing a public key. The private half of this key can be used for two purposes:
- Signing X.509 certificates and CRLs.
- Producing RRSIGs for DNSSEC records, acting as a Key Signing Key (KSK).
It can, but usually should not be, used for these purposes too:
- Acting as an SSHv2 host key.
- Acting directly as a TLS1.3 secret key.
- Making SSH signatures according to the specification published by OpenSSH.
It is cryptographically sound to use the same key for all five purposes because all five require that the data to be signed have a distinct prefix. The prefix is:
- “SSH-2.0-“ (the start of the client identification string) for SSHv2.
- “SSHSIG” for OpenSSH signatures.
- ” “ (ASCII-encoded and repeated multiple times) for TLSv1.3.
- 0x00 0x30 (48, the type of a DNSKEY record, encoded as a 16-bit big-endian integer) for an RRSIG of one or more DNSKEY records.
- 0x10 (the type byte of an ASN.1 SEQUENCE) for X.509.
The private key MUST NOT be used for any purpose not listed here unless an extension to the specification explicitly permits such use. Forbidden purposes include, but are not limited to:
- Signing handshakes for TLS1.2 or below, or for any version of SSL. TLSv1.2 allows the client to request that data with an arbitrary prefix be signed, and older TLS versions are obsolete and insecure anyway.
- Signing DNS records of types other than DNSSEC. These use a different Type Covered field and so might result in prefix collisions.
- RSA decryption, due to the ROBOT attack.
- OpenPGP signing, which allows the attacker to provide the entire prefix of the data to be signed.
An implementation that uses the key for any purpose not listed in this specification MUST be assumed to have a security vulnerability unless there is a sound cryptographic reason to believe that no exploit will ever be possible, even in the presence of future extensions to this protocol. If it is necessary to sign additional data, the SSH signature format with a specific namespace can be used.
The Format
A cryptographically generated domain can use one of the following forms:
<hash of public key>.local
SOMETHING.<hash of public key>.local
.local
is reserved for multicast DNS, which is suitable for home networks.
The device itself would advertise the CGDN. CGDNs should also be supported
on the public Internet, with one of the following formats for the domain name:
<hash of public key>.keyhash.arpa
SOMETHING.<hash of public key>.keyhash.arpa
.keyhash.arpa
is a placeholder; the real domain name chosen might be different.
For CGDNs to be reachable over the public Internet, the owner of the corresponding
public key must be able to set DNS records for them. I have
not yet figured out what the mechanism for this should be.
Computing The Public Key Hash
Computing the public key hash is trickier than it sounds, because there are multiple distinct formats in which the public key will be received. Therefore, a canonical format must be chosen. This version of this document chooses the encoding used by SSH as canonical and requires all other formats to be converted to it.
SSH uses a simple and extensible encoding scheme, whereas X.509 requires complicated ASN.1 DER encoding. DNSSEC also uses a simple encoding scheme, but DNSSEC is extremely severely impacted by large signature and public key sizes, so its future in a post-quantum world is less clear.
The hash includes only the algorithm field and the public key. It is encoded into a domain name the same way that Tor v3 onion service domain names are encoded, except that the 32-byte public key is a SHA256 hash. Security of this scheme depends on second-preimage resistance (not collision resistance!) of the hash function, so SHA256 will be secure for a very, very long time.
Open Questions
-
How should registration work? The intent is that anyone will be able to register a CGDN by proving possession of the corresponding secret key. Is DNS TSIG the best way to implement this?
-
What is the best way to get this adopted? Writing up an idea is easy, but getting it implemented by major browsers is much harder. Is this something that should be hard-coded in their TLS libraries?
-
Who should run the public registration service for non-
.local
CGDNs? At the very least, one or more such services need to exist so that domain names can be resolved to IP addresses. -
Should there be a public relay service that avoids devices needing to accept incoming connections themselves? This would make life easier for those behind NAT, but someone needs to run the relay service, and there are privacy and abuse concerns.
-
Is there experience from Tor onion services that is relevant? Anonymity is explicitly not a goal of CGDNs, mostly because Tor already serves that role, but also for performance reasons.
-
Due to their limited computing power, embedded devices are very vulnerable to denial of service attacks. Is there a good solution to this? Since CGDNs implicitly use certificate pinning, any DDoS-prevention solution that does not require access to plaintext traffic can be used without risking confidentiality or integrity, except against traffic analysis attacks.