Lockfile /var/lib/dkim-rotate/instance/lock
/var/lib/dkim-rotate/instance/stateUpdated with rename(2).
Text file, line based, with the these keywords. All of the following entries must appear, in this order:
sel_offset selectorSelector of first key entry. (As a number.)
sel_limit numberValue that selectors are modulo. Indicates the maximum selector that we are currently using, or might use.
Normally this is equal to the max_selector config key.
last_serial numberLast DNS serial number used. Each attempt to publish needs to increment this.
status statusIndicates that subsequent key entries have this status. status is one of -1 +0 +N +X, and, again, one of each status must appear, once, in order. There must not be more than one key in status -1 or status +0.
key selector time_t keyname dkim dns dataDefines a key. Repeated zero or more times after or in between status.
selector must be the letter corresponding to (key number in list + sel_offset) % sel_limit).
time_t is in decimal. It may also be one or more of the two special values DNS or MTA, separated by commas. These means that the config update event referred to has not yet been completed.
After all of these has been completed, the value should be replaced with the time of the successful update. Eg, DNS means “DNS zonefile update is outstanding”; the new DNS zone should be generated and the nameserver reloaded, and then DNS replaced with the time of that update.
keyname is derived from the public key value.
dkim dns data (starting at the first nonwhitespace) is the combined content of the TXT strings tbat ought to be published in the DNS to advertise this key.
README.txt
/var/lib/dkim-rotate/INSTANCE/pub/HH/KEYNAME.pem
Here HH is the first two characters of keyname.
keyname is derived from the public key value, as follows:
p= base64stringEg, base64 -d <contents-of-p-field | md5sum.
Note that this is not sufficient to use as a key identifier for verification purposes, but it is nicely uniformly distributed (so can’t be pre-guessed) and determinable from the public key (so that if someone could verify a DKIM signature they can know where to try to find the leaked key).
The directory priv/ must be readable by the MTA, but not otherwise accessible. With Exim on Debian that probably means it ought to be group-owned by Debian-exim.
The directories pub/HH should all executable but unreadable by the webserver. All of these directories should be created before any work is done. This prevents enumeration of the keys.
The individual private key files in pub/ will all have the fixed mtime with time_t 1000000000. This prevents a putative verifier from determining when a key first existed.
pub/README.txt explains the situation and is installed and maintained automatically.
Preparation/cleanup:
pub/ directories exist.Check if last +X can be revealed.
dns_lag elapsed.R;
pub/+X.Check if last +N can be deadvertised.
email_lag elapsed.+X.+N.Check if actual selector limit can be adjusted towards intended. Requirements:
+X, so that seeing sel_limit decrease is suffiient to know one can change/remove the “upstream” DNS CNAMEs.)This is fiddly. Instead we use the following conditions:
sel_offset is zero.Possibly advance -1 key to become newly-in-use +0.
dns_lag (for -1) has elapsed.+0 becomes +N-1 becomes +0Possibly create -1 key.
-1 key.+X is OK) (not used to publish another key).max_selector (the intended sel_limit). Again, this condition is fiddely: instead, we check that the proposed new number of keys doesn’t exceed max_selector.SPDX-License-Identifier: GPL-3.0-or-later