While playing around with the Ethereum Name Service (ENS) and the DNSSEC oracle, I wanted to explore the DNS records of juniperspring.xyz related to DNSSEC. An introductory understanding of DNSSEC, elliptic curve cryptography, and ECDSA is recommended for this post; recommended reading is provided as references12.

Running dig juniperspring.xyz dnskey returns two records:

;; ANSWER SECTION:
juniperspring.xyz.  3316    IN  DNSKEY  256 3 13 SGjhjkfPUdkl+lMIVSh0m/VBULv8dzacEjLi8F9ykoCTDxFYPMQQpYv+ mOPkEMdbFuoS11uZn7gijI4d/BMMjw==
juniperspring.xyz.  3316    IN  DNSKEY  257 3 13 T428PVvB2uqJg1NaUXEoh+9lt1jbwx1Dqqu1had5cp7R48NEiGcTZlg8 +wdzDtnQrJosM+2G8fCrxnKJxYNJoQ==

Each DNSKEY record has the “Zone Key” flag set, yielding 256. The DNSKEY record containing the KSK public key additionally has the “Secure Entry Point” flag set, yielding 257. Thus the above entries (from top to bottom) represent the ZSK public key and KSK public key, respectively.

The number 13 in this context represents the signature algorithm, specifically ECDSA Curve P-256 with SHA-256 (ECDSAP256SHA256)3. Curve P-256 is also known as secp256r1; P-256 is the name given by NIST. In ECDSA, the public key represents a (two-dimensional) point; the private key represents a one-dimensional scalar.

The public key $$Q_A$$ is generated by multiplication of the scalar $$d_A$$ by the known base point $$G$$. In other words, $$Q_A=d_A\cdot G$$, where the operator $$\cdot$$ represents multiplication over the elliptic field.

ECDSA Public Key Compression

The textual representation of ECDSA public keys depends partially on whether it is a compressed or uncompressed representation. Multiplication is performed over a finite field; the size of the public key is dependent on the size of the field.

The field size of NIST P-256 is 256 bits, or 32 bytes. An uncompressed ECDSA public key consists of an x and y‑coordinate, and since each coordinate is a field element (i.e., for NIST P-256, is at most 32 bytes in length), an ECDSA public key for Curve P-256 requires 64 bytes, not including a prefix. When the (well-known) prefix 0x04 is prepended, the total length is 65 bytes.

The prefix is used to quickly differentiate between uncompressed (0x04) and compressed (0x02, 0x03) forms. The compressed form takes advantage of the property that the y‑coordinate of an ECDSA public key can be unambiguously derived given the curve equation, x‑coordinate, and an odd-even flag (prefix). Solving the curve equation for y given x yields two possible values for y. One solution is always even, one solution is always odd. By prefixing the compressed form with a well-known byte (0x02 for even, 0x03 for odd), the public key can be unambiguously specified with a total of $$32+1=33$$ bytes.

ECDSA Key Format for DNSKEY

When used in DNSKEY records, ECDSA public keys are given in uncompressed form. References to some of the relevant RFCs are given below. Practically, this means that the DNSSEC public keys for juniperspring.xyz should each be $32*2=64$ bytes long when no prefix is used. These keys are always exactly 64 bytes long, probably because the RFCs strictly specify uncompressed form (i.e., a prefix would be redundant) and because small DNS records are desirable.

The RFC on DNSSEC records (RFC40344) describes the DNSKEY Public Key Field briefly:

The Public Key Field holds the public key material. The format depends on the algorithm of the key being stored and is described in separate documents.

ECDSA key formats for DNSSEC are specified in RFC66055; it says:

ECDSA public keys consist of a single value, called “Q” in FIPS 186-3. In DNSSEC keys, Q is a simple bit string that represents the uncompressed form of a curve point, “x | y”. … For P-256, each integer MUST be encoded as 32 octets

Looking at the base64 strings from the two DNSKEY records, we have:

SGjhjkfPUdkl+lMIVSh0m/VBULv8dzacEjLi8F9ykoCTDxFYPMQQpYv+ mOPkEMdbFuoS11uZn7gijI4d/BMMjw==
T428PVvB2uqJg1NaUXEoh+9lt1jbwx1Dqqu1had5cp7R48NEiGcTZlg8 +wdzDtnQrJosM+2G8fCrxnKJxYNJoQ==

I convert these strings to hex with JavaScript:

let zsk = 'SGjhjkfPUdkl+lMIVSh0m/VBULv8dzacEjLi8F9ykoCTDxFYPMQQpYv+ mOPkEMdbFuoS11uZn7gijI4d/BMMjw=='
let ksk = 'T428PVvB2uqJg1NaUXEoh+9lt1jbwx1Dqqu1had5cp7R48NEiGcTZlg8 +wdzDtnQrJosM+2G8fCrxnKJxYNJoQ=='
let b64ToHex = (b64) => [...atob(b64)].map(c=> c.charCodeAt(0).toString(16).padStart(2,0)).join('')
b64ToHex(zsk) // '4868e18e47cf51d925fa53085528749bf54150bbfc77369c1232e2f05f729280930f11583cc410a58bfe98e3e410c75b16ea12d75b999fb8228c8e1dfc130c8f'
b64ToHex(ksk) // '4f8dbc3d5bc1daea8983535a51712887ef65b758dbc31d43aaabb585a779729ed1e3c34488671366583cfb07730ed9d0ac9a2c33ed86f1f0abc67289c58349a1'

Each of the resulting hex strings is 128 chars (64 bytes) long and does not contain a 0x04 prefix byte, consistent with the format of uncompressed ECDSA P-256 public keys.

DNSSEC and Namecheap

At the time of writing, the juniperspring.xyz domain is provided by Namecheap. The Namecheap web interface provides a toggle for enabling DNSSEC for the domain (DNSSEC is disabled by default). DNSSEC in the Namecheap management interface Empirically, Namecheap uses ECDSAP256SHA256 as the signature algorithm by default, generates the requisite key pairs, and (I assume) stores the private keys on Namecheap’s servers. You can check the resulting DNSSEC records with an online tool provided by Verisign Labs6.

On the Ethereum DNSSEC Oracle

Having a working knowledge of DNS and DNSSEC enables a better understanding of the Ethereum Name Service (ENS)7 and the Ethereum DNS Oracle8. Submitting a proof to the DNSSEC oracle is significantly more expensive (multiple times the cost of purchasing juniperspring.eth for four years) than purchasing a .eth domain.

Purchasing the juniperspring.eth domain name was relatively inexpensive at the time of writing, relative to the expense of submitting a proof to the DNSSEC oracle. Understanding DNSSEC signatures and validation helps explain the high gas cost of the latter.

The ENS docs9 explain:

Submitting proof to DNSSEC oracle takes up a lot of gas because it is heavy computation work. It will take up even more gas if you submit the first domain under the specific TLD. This is because it submits proof of both your domain and its parent domain(eg: matoken.live, as well as .live).

Understanding DNS and DNSSEC elucidates ENS; understanding ENS and the DNSSEC oracle elucidates DNS and DNSSEC.