#!/usr/bin/env python3 from sys import argv from os.path import realpath, dirname from bundlewrap.repo import Repository from base64 import b64decode, urlsafe_b64encode from cryptography.utils import int_to_bytes from cryptography.hazmat.primitives import serialization as crypto_serialization from struct import pack, unpack from hashlib import sha1, sha256 def long_to_base64(n): return urlsafe_b64encode(int_to_bytes(n, None)).decode() domain = argv[1] repo = Repository(dirname(dirname(realpath(__file__)))) #repo = Repository('.') flags = 256 protocol = 3 algorithm = 8 algorithm_name = 'RSASHA256' # Private Key priv = repo.libs.rsa.generate_deterministic_rsa_private_key( b64decode(str(repo.vault.random_bytes_as_base64_for('dnssec' + domain))) ) # https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa/#cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateNumbers # https://crypto.stackexchange.com/a/21104 public_exponent = priv.private_numbers().public_numbers.e modulo = priv.private_numbers().public_numbers.n private_exponent = priv.private_numbers().d prime1 = priv.private_numbers().p prime2 = priv.private_numbers().q exponent1 = priv.private_numbers().dmp1 exponent2 = priv.private_numbers().dmq1 coefficient = priv.private_numbers().iqmp print(f""" Private-key-format: v1.3 Algorithm: {algorithm} ({algorithm_name}) Modulus: {long_to_base64(modulo)} PublicExponent: {long_to_base64(public_exponent)} PrivateExponent: {long_to_base64(private_exponent)} Prime1: {long_to_base64(prime1)} Prime2: {long_to_base64(prime2)} Exponent1: {long_to_base64(exponent1)} Exponent2: {long_to_base64(exponent2)} Coefficient: {long_to_base64(coefficient)} Created: 20230428110109 Publish: 20230428110109 Activate: 20230428110109 """) # DNSKEY pub = priv.public_key() dnskey = ''.join(pub.public_bytes( crypto_serialization.Encoding.PEM, crypto_serialization.PublicFormat.SubjectPublicKeyInfo ).decode().split('\n')[1:-2]) value = f"{flags} {protocol} {algorithm} {dnskey}" print(f""" {domain}. IN DNSKEY {value} """) # DS # https://gist.github.com/wido/4c6288b2f5ba6d16fce37dca3fc2cb4a#file-dnskey_to_dsrecord-py-L40 def _calc_ds(domain, flags, protocol, algorithm, dnskey): if domain.endswith('.') is False: domain += '.' signature = bytes() for i in domain.split('.'): signature += pack('B', len(i)) + i.encode() signature += pack('!HBB', int(flags), int(protocol), int(algorithm)) signature += b64decode(dnskey) return { 'sha1': sha1(signature).hexdigest().upper(), 'sha256': sha256(signature).hexdigest().upper(), } def _calc_keyid(flags, protocol, algorithm, dnskey): st = pack('!HBB', int(flags), int(protocol), int(algorithm)) st += b64decode(dnskey) cnt = 0 for idx in range(len(st)): s = unpack('B', st[idx:idx+1])[0] if (idx % 2) == 0: cnt += s << 8 else: cnt += s return ((cnt & 0xFFFF) + (cnt >> 16)) & 0xFFFF keyid = _calc_keyid(flags, protocol, algorithm, dnskey) ds = _calc_ds(domain, flags, protocol, algorithm, dnskey) print(f"{domain}. IN DS {str(keyid)} {str(algorithm)} 1 {ds['sha1'].lower()}") print(f"{domain}. IN DS {str(keyid)} {str(algorithm)} 2 {ds['sha256'].lower()}")