Pages

Friday, 17 August 2012

DNSSEC key tag (keyid) and DS signature calculation in python

This one took me a considerable amount of hours to figure out so here it is.

While trying to automate DNS zone generation I had to calculate some of the values programmatically. Two of the auto-generated values had to do with DNSSEC entries: The key tag (or keyid) and the DS record's signatures.

The required details on how these are calculated are found in the following places:

For the calculations you need to provide the following:

  • For the key tag: flags, protocol, algorithm, public key

  • For the DS signatures: owner (the domain name), flags, protocol, algorithm, public key


Code


I used python for this but the approach is the same for other languages since the algorithms are the same.

So here it is:

[code lang="python"]
import struct
import hashlib
import base64

def calc_keyid(flags, protocol, algorithm, st):
"""
@param owner The corresponding domain
@param flags The flags of the entry (256 or 257)
@param protocol Should always be 3
@param algorithm Should always be 5
@param st The public key as listed in the DNSKEY record.
Spaces are removed.
@return The key tag
"""
# Remove spaces and create the wire format
st0=st.replace(' ', '')
st2=struct.pack('!HBB', int(flags), int(protocol), int(algorithm))
st2+=base64.b64decode(st0)

# Calculate the tag
cnt=0
for idx in xrange(len(st2)):
s=struct.unpack('B', st2[idx])[0]
if (idx % 2) == 0:
cnt+=s<<8
else:
cnt+=s

ret=((cnt & 0xFFFF) + (cnt>>16)) & 0xFFFF

return(ret)

def calc_ds(owner, flags, protocol, algorithm, st):
"""
@param flags Usually it is 257 or something that indicates a KSK.
It can be 256 though.
@param protocol Should always be 3
@param algorithm Should always be 5
@param st The public key as listed in the DNSKEY record.
Spaces are removed.
@return A dictionary of hashes where the key is the hashing algorithm.
"""

# Remove spaces and create the wire format
st0=st.replace(' ', '')
st2=struct.pack('!HBB', int(flags), int(protocol), int(algorithm))
st2+=base64.b64decode(st0)

# Ensure a trailing dot
if owner[-1]=='.':
owner2=owner
else:
owner2=owner+'.'

# Create the name wire format
owner3=''
for i in owner2.split('.'):
owner3+=struct.pack('B', len(i))+i

# Calculate the hashes
st3=owner3+st2
ret={
'sha1': hashlib.sha1(st3).hexdigest().upper(),
'sha256': hashlib.sha256(st3).hexdigest().upper(),
}

return(ret)
[/code]

Data


The following were created by bind's dnssec tools:

[code light="true"]
$ cat Ktest.hell.gr.+005+33630.key
; This is a zone-signing key, keyid 33630, for test.hell.gr.
; Created: 20101007114826 (Thu Oct  7 14:48:26 2010)
; Publish: 20101007114826 (Thu Oct  7 14:48:26 2010)
; Activate: 20101007114826 (Thu Oct  7 14:48:26 2010)
test.hell.gr. IN DNSKEY 256 3 5 AwEAAb+lTDjZCfq7D5N9cNd1ug30wLrbCXB9mVJJQGlQQHpiHHlMaLGG
sV2/j5+eojHp+WQUzNpOzrULF6msbEvUuV2gSEnpbueRV4twO8muGE+x
eUuseSoHh/aTpA8Z9SPubb01mduqqaUEN5Juz2Q4hF0dSUSJYlJPKhp6
NrOgoeyj

$ cat dsset-test.hell.gr.
test.hell.gr.        IN DS 33630 5 1 A2AD2648B353365631EBC9C70EDA1E0C04563FCC
test.hell.gr.        IN DS 33630 5 2 4177EAEC09A37178357871EBE3FB361CABB2861F12A1D51DDE18CBA2 439BB5C1
[/code]

Result


[code lang="python" light="true"]
>>> domain='test.hell.gr'
>>> flags=256
>>> protocol=3
>>> algorithm=5
>>> key='AwEAAb+...goeyj' # Truncated
>>> calc_keyid(flags, protocol, algorithm, key)
33630
>>> r=calc_ds(domain, flags, protocol, algorithm, key)
>>> r['sha1']
'A2AD2648B353365631EBC9C70EDA1E0C04563FCC'
>>> r['sha256']
'4177EAEC09A37178357871EBE3FB361CABB2861F12A1D51DDE18CBA2439BB5C1'
[/code]

Legal


You can use the above under the MIT license. If it doesn't fit your needs let me know. My intention is to make this usable by anyone for any kind of use with no obligation.

3 comments:

  1. i am new, please help ! :)

    i want to add cert record in DNS for direct project, while adding cert record its asking there for following information and i don't know how to get it.

    Please help how can i get following information.
    i just have certificate file.

    Key Tag.
    Format.
    Algorithm.


    Please response..

    ReplyDelete
  2. Also could you please guide how to run above script and get required information (key tag, algorithm)? i have little bit knowledge of Linux... Please guide i will do.

    ReplyDelete
  3. When i run bcc script then its throw following error:

    Traceback (most recent call last):
    File "/usr/bin/bcc.py", line 61, in
    bind_cert_convert(sys.argv[1], sys.argv[2])
    File "/usr/bin/bcc.py", line 40, in bind_cert_convert
    decoded_clean_pk = clean_pk.decode('base64', strict)
    NameError: global name 'strict' is not defined

    ReplyDelete