dkim-rotate internals

STATE

Lockfile /var/lib/dkim-rotate/instance/lock

primary statefile /var/lib/dkim-rotate/instance/state

Updated with rename(2).

Text file, line based, with the these keywords. All of the following entries must appear, in this order:

sel_offset selector

Selector of first key entry. (As a number.)

sel_limit number

Value 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 number

Last DNS serial number used. Each attempt to publish needs to increment this.

status status

Indicates 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 data

Defines 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.

Outputs for MTA and nameserver

Private key - private storage and public archive

README.txt
/var/lib/dkim-rotate/INSTANCE/pub/HH/KEYNAME.pem

Here HH is the first two characters of keyname.

keyname

keyname is derived from the public key value, as follows:

  1. base64 decode the p= base64string
  2. run the result through MD5.
  3. represent the result in hex

Eg, 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).

Permissions, visibility, and metadata

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.

ALGORITHM

  1. Preparation/cleanup:
  2. Check if last +X can be revealed.

  3. Check if last +N can be deadvertised.

  4. Check if actual selector limit can be adjusted towards intended. Requirements:

    This is fiddly. Instead we use the following conditions:

  5. Possibly advance -1 key to become newly-in-use +0.

  1. Possibly create -1 key.

COPYRIGHT AND AUTHORSHIP

Copyright 2022 Ian Jackson and contributors to dkim-rotate.
There is NO WARRANTY.
SPDX-License-Identifier: GPL-3.0-or-later