[dns-operations] calculating DNSSEC keytags in awk

Joe Abley jabley at hopcount.ca
Fri Dec 16 21:21:37 UTC 2011


Hi all,

I realise I'm probably the only person in the world who would ever want to do such a thing, but in case I'm wrong about that here's a function in standard awk (no GNUisms) to calculate a keytag from DNSKEY RDATA passed in canonical form, i.e. with the public key modulus base64-encoded, as you might see as output from dig.

It seems to generate the right keytags for the zones I have been playing with. Note that the function walks unashamedly over the arrays word[] and rdata[] and doesn't bother with local-scoping any other variables, because what fun would that be?


Joe

#!/usr/bin/awk -f

# Derive keytag from DNSKEY canonical presentation format data.
#
# e.g.
#
#  tag = keytag(256, 3, 8, \
#          "AwEAAdNW7YIhcTdqXrzgZjJJ35VjAFT1ArvnhAzXDm7AuGxSQqmGBRmj" \
#          "JvBv0xS4gahB9mj6ekF0dVKoeZgLmNAjo8hj2JI7K281YTo2R5k3mKSc" \ 
#          "4hOCP55hR22r5hIsPJoT19pv/VdZQfyTzZ96frQ16qRa9+/GSjzjtFfQ" \
#          "v16FwE7R");
#
# Comments, complaints and howls of derisive laughter to jabley at hopcount.ca.

function keytag(flags, proto, alg, pubkey) {
  BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

  # length of RDATA in octets, count as we go
  rdlength = 0;

  # DNSKEY fixed-field RDATA, RFC4034 section 2
  rdata[rdlength++] = int(flags / 256);
  rdata[rdlength++] = flags % 256;
  rdata[rdlength++] = proto;
  rdata[rdlength++] = alg;

  # DNSKEY public key modulus, decode base64 and store
  while (pubkey) {
    # extract the next four base64 6-bit words into word[]
    # padding characters will be stored as zeros
    for (i = 1; i < 5; i++) {
      w = index(BASE64, substr(pubkey, i, 1));
      word[i] = (w ? w - 1 : 0);
    }

    # derive three octets from those four base64 6-bit words
    rdata[rdlength++] = (word[1] * 4 + int(word[2] / 16));
    rdata[rdlength++] = (word[2] * 16 + int(word[3] / 4)) % 256;
    rdata[rdlength++] = (word[3] * 64 + word[4]) % 256;

    # decrease incoming string by 4
    pubkey = substr(pubkey, 5);
  }

  # calculate the keytag and return it
  if (alg == 1) {
    # algorithm 1 is special, see RFC 4034 appendix B.1
    return 256*rdata[rdlength - 3] + rdata[rdlength - 2];
  } else {
    # for all other algorithms, follow RFC 4034 appendix B
    ac = 0;

    for (i = 0; i < rdlength; i++)
      ac += (i % 2 ? rdata[i] : 256 * rdata[i]);
    ac += int(ac / 65536) % 65536;
    return ac % 65536;
  }
}




More information about the dns-operations mailing list