Calculating DS record from DNSKEY with Python 3

While working on DNSSEC for PCextreme’s Aurora DNS I had to convert a DNSKEY to a DS-record which could be set in the parent zone for proper delegation.

The foundation for Aurora DNS is PowerDNS together with Python 3.

The API for Aurora DNS has to return the DS-records so that a end-user can use these in the parent zone. I had the DNSKEY, but I didn’t have the DS-record so I had to calculate it using Python 3.

I eventually ended up with this Python code which you can find on my Github Gists page.

"""
Generate a DNSSEC DS record based on the incoming DNSKEY record

The DNSKEY can be found using for example 'dig':

$ dig DNSKEY secure.widodh.nl

The output can then be parsed with the following code to generate a DS record
for in the parent DNS zone

Author: Wido den Hollander 

Many thanks to this blogpost: https://www.v13.gr/blog/?p=239
"""

import struct
import base64
import hashlib


DOMAIN = 'secure.widodh.nl'
DNSKEY = '257 3 8 AwEAAckZ+lfb0j6aHBW5AanV5A0V0IfF99vAKFZd6+fJfEChpZtjnItWDnJLPa3/LAFec/tUhLZ4jgmzaoEuX3EQQgI1V4kp9SYf8HMlFPP014eO+AnjkYFGLE2uqHPx/Tu7/pO3EyKwTXi5fMadROKuo/mfat5AEIhGjteGGO93DhnOa6kcqj5RHYJBh5OZ/GoZfbeYHK6Muur1T16hHiI12rYGoqJ6ZW5+njYprG6qwp6TZXxJyE7wF1JdD+Zhbjhf0Md4zMEysP22wBLghBaX6eDIBh/7jU7dw1Ob+I42YWk+X4NSiU3sRYPaq1R13JEK4zVqQtL++UVtgRPEbfj5RQ8='


def _calc_keyid(flags, protocol, algorithm, dnskey):
    st = struct.pack('!HBB', int(flags), int(protocol), int(algorithm))
    st += base64.b64decode(dnskey)

    cnt = 0
    for idx in range(len(st)):
        s = struct.unpack('B', st[idx:idx+1])[0]
        if (idx % 2) == 0:
            cnt += s << 8
        else:
            cnt += s

    return ((cnt & 0xFFFF) + (cnt >> 16)) & 0xFFFF


def _calc_ds(domain, flags, protocol, algorithm, dnskey):
    if domain.endswith('.') is False:
        domain += '.'

    signature = bytes()
    for i in domain.split('.'):
        signature += struct.pack('B', len(i)) + i.encode()

    signature += struct.pack('!HBB', int(flags), int(protocol), int(algorithm))
    signature += base64.b64decode(dnskey)

    return {
        'sha1':    hashlib.sha1(signature).hexdigest().upper(),
        'sha256':  hashlib.sha256(signature).hexdigest().upper(),
    }


def dnskey_to_ds(domain, dnskey):
    dnskeylist = dnskey.split(' ', 3)

    flags = dnskeylist[0]
    protocol = dnskeylist[1]
    algorithm = dnskeylist[2]
    key = dnskeylist[3].replace(' ', '')

    keyid = _calc_keyid(flags, protocol, algorithm, key)
    ds = _calc_ds(domain, flags, protocol, algorithm, key)

    ret = list()
    ret.append(str(keyid) + ' ' + str(algorithm) + ' ' + str(1) + ' '
               + ds['sha1'].lower())
    ret.append(str(keyid) + ' ' + str(algorithm) + ' ' + str(2) + ' '
               + ds['sha256'].lower())
    return ret


print(dnskey_to_ds(DOMAIN, DNSKEY))

Last day before we head back

Aurora Borealis

One of the things we can for was the Aurora Borealis, also called the ‘Northern Lights’.

For two nights we had clear skies and saw a beautiful display. You can find enough pictures on the internet, so I won’t post all of them!

aurora-borealias-1

aurora-borealias-2

aurora-borealias-3

Route home

The route home from the Lofoten Islands is going to be 3.000km. Our guess is that it will take us 4 to 5 days. Not due to the charging, but because you simply can not drive very fast through Norway and Sweden.

Ferry from the Lofoten

We will be taking the ferry from Moskenes to Bodø. The Hurtigruten from Stamsun to Bodø was not an option this time as it leaves late at night.

Route Norway Back 2016

This part of the trip probably won’t be very special. I hope that Fiskevägen will be as nice as it was last year. Besides from that we are not expecting any highlights anymore.

IPv6 Router Advertisements under FreeBSD with rtadvd

Aurora Compute

At PCextreme B.V. we started using FreeBSD machines as routers for our Aurora Compute cloud platform.

Using the Intel Xeon E5-v3 processor and the SR-IOV technique of Intel’s 10Gbit Network Cards we can achieve high throughput and low latency through these routers. They actually perform better than most other routers!

By deploying multiple, smaller routers we create smaller failure domains in our network.

IPv6 Router Advertisements

On our Aurora Compute platform we support IPv6 and do this using SLAAC.

This is done by Routers sending out Router Advertisements (RAs) which is done by a daemon running on the router. Under Linux this is done by radvd and under FreeBSD by rtadvd.

rtadvd

The configuration syntax of rtadvd is odd in my opinion. I thought it was worth it to write a small blogpost and share the configuration we are using on of the routers.

The configuration below sends out RAs on multiple VLAN interfaces and also sends out the DNS servers in these advertisements. The templates we use on Aurora Compute pick up these nameservers from the RAs and add them to /etc/resolv.conf.

/etc/rtadvd.conf

vlan704:\
    :addrs#1:addr="2001:db8:100::"\
    :prefixlen#64\
    :tc=default\
    :rdnss="2001:db8:53::1,2001:db8::53::2":

vlan705:\
    :addrs#1:addr="2001:db8:101::"\
    :prefixlen#64\
    :tc=default\
    :rdnss="2001:db8:53::1,2001:db8::53::2":

vlan706:\
    :addrs#1:addr="2001:db8:102::"\
    :prefixlen#64\
    :tc=default\
    :rdnss="2001:db8:53::1,2001:db8::53::2":

You also have to enable rtadvd in your /etc/rc.conf:

/etc/rc.conf

# RADVD
rtadvd_enable="YES"
rtadvd_interfaces="vlan704 vlan705 vlan706"