Skip to content

Instantly share code, notes, and snippets.

@andrewxhill
Last active November 8, 2023 06:14
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andrewxhill/107e343e676351b39db65910aa3d78d7 to your computer and use it in GitHub Desktop.
Save andrewxhill/107e343e676351b39db65910aa3d78d7 to your computer and use it in GitHub Desktop.
Chain of Trust - but verify...

Provide a domain and a TXT content flag you want to discover and then verify all the way to ICANN.

On boom.fyi, we have a TXT record with data-cert=hello_world

domain = "boom.fyi"

Collect data for our target TXT record flag (including all sigs etc). Then collect data for the entire chain back to root.

chain_data = collect_record(domain, 'data-cert=')
chain_data = collect_chain_data(domain, chain_data)

This all is stored off in chain_data.json

Now, verify.

  • Check that the TXT record was signed correct
  • Check that the key that signed it came from the parent zoon
  • Check that that signature was correct
  • And check that it's key came from the parent zoon
  • Uncover the turtles
  • All the way down to the root
verify_chain(domain, data)

Finally, it makes sure the last step was signed by keys that came from the root we already know. Stored in root_rrset.json Since no exceptions were thrown, we'll finally show what the value of our flag was set to:

show_verified_message(domain, data)
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 97,
"id": "d4d8da14-d9d6-41a2-a1a3-e5851dc99a32",
"metadata": {},
"outputs": [],
"source": [
"import dns.message\n",
"import dns.query\n",
"import dns.rdatatype\n",
"import dns.dnssec\n",
"import dns.name\n",
"import dns.rdataclass\n",
"import dns.resolver\n",
"import dns.rdata\n",
"import dns.rrset\n",
"import json"
]
},
{
"cell_type": "code",
"execution_count": 98,
"id": "9d5afcb2-5631-455a-8bb3-eb9d0d4a7952",
"metadata": {},
"outputs": [],
"source": [
"def collect_record(domain, flag, data=None):\n",
" if data == None: \n",
" data = {}\n",
" # Query for the TXT record\n",
" txt_request = dns.message.make_query(domain, dns.rdatatype.TXT)\n",
" txt_request.want_dnssec(True)\n",
" txt_response = dns.query.udp(txt_request, '1.1.1.1')\n",
" txt_rrset = txt_response.find_rrset(txt_response.answer, dns.name.from_text(domain), dns.rdataclass.IN, dns.rdatatype.TXT)\n",
" data['txt_data'] = [str(rdata) for rdata in txt_rrset if flag in str(rdata)]\n",
" data['txt_params'] = {\n",
" 'name': str(txt_rrset.name),\n",
" 'ttl': txt_rrset.ttl,\n",
" 'rdclass': dns.rdataclass.to_text(txt_rrset.rdclass),\n",
" 'rdtype': dns.rdatatype.to_text(txt_rrset.rdtype),\n",
" 'rdata': [str(rdata) for rdata in txt_rrset],\n",
" }\n",
"\n",
" txt_rrsig_rrset = txt_response.find_rrset(txt_response.answer, dns.name.from_text(domain), dns.rdataclass.IN, dns.rdatatype.RRSIG, dns.rdatatype.TXT)\n",
" data['txt_rrsig_data'] = [str(rdata) for rdata in txt_rrsig_rrset]\n",
" data['txt_rrsig_params'] = {\n",
" 'name': str(txt_rrsig_rrset.name),\n",
" 'ttl': txt_rrsig_rrset.ttl,\n",
" 'rdclass': dns.rdataclass.to_text(txt_rrsig_rrset.rdclass),\n",
" 'rdtype': dns.rdatatype.to_text(txt_rrsig_rrset.rdtype),\n",
" 'rdata': [str(rdata) for rdata in txt_rrsig_rrset],\n",
" }\n",
"\n",
" return data\n",
"\n",
"def collect_domain_data(domain, data):\n",
" # Query for the DNSKEY record\n",
" dnskey_request = dns.message.make_query(domain, dns.rdatatype.DNSKEY)\n",
" dnskey_request.want_dnssec(True)\n",
" dnskey_response = dns.query.udp(dnskey_request, '1.1.1.1')\n",
" dnskey_rrset = dnskey_response.find_rrset(dnskey_response.answer, dns.name.from_text(domain), dns.rdataclass.IN, dns.rdatatype.DNSKEY)\n",
" data['dnskey_data'] = [str(rdata) for rdata in dnskey_rrset]\n",
" data['dnskey_params'] = {\n",
" 'name': str(dnskey_rrset.name),\n",
" 'ttl': dnskey_rrset.ttl,\n",
" 'rdclass': dns.rdataclass.to_text(dnskey_rrset.rdclass),\n",
" 'rdtype': dns.rdatatype.to_text(dnskey_rrset.rdtype),\n",
" 'rdata': [str(rdata) for rdata in dnskey_rrset],\n",
" }\n",
" \n",
" ds_request = dns.message.make_query(domain, dns.rdatatype.DS)\n",
" ds_request.want_dnssec(True)\n",
" ds_response = dns.query.udp(ds_request, '1.1.1.1')\n",
" ds_rrset = ds_response.find_rrset(ds_response.answer, dns.name.from_text(domain), dns.rdataclass.IN, dns.rdatatype.DS)\n",
" data['ds_data'] = [str(rdata) for rdata in ds_rrset]\n",
" data['ds_params'] = {\n",
" 'name': str(ds_rrset.name),\n",
" 'ttl': ds_rrset.ttl,\n",
" 'rdclass': dns.rdataclass.to_text(ds_rrset.rdclass),\n",
" 'rdtype': dns.rdatatype.to_text(ds_rrset.rdtype),\n",
" 'rdata': [str(rdata) for rdata in ds_rrset],\n",
" 'digest_type': [rdata.digest_type for rdata in ds_rrset]\n",
" }\n",
" \n",
" # # Query for the RRSIG of the DS record\n",
" ds_rrsig_rrset = ds_response.find_rrset(ds_response.answer, dns.name.from_text(domain), dns.rdataclass.IN, dns.rdatatype.RRSIG, dns.rdatatype.DS)\n",
" data['ds_rrsig_data'] = [str(rdata) for rdata in ds_rrsig_rrset]\n",
" data['ds_rrsig_params'] = {\n",
" 'name': str(ds_rrsig_rrset.name),\n",
" 'ttl': ds_rrsig_rrset.ttl,\n",
" 'rdclass': dns.rdataclass.to_text(ds_rrsig_rrset.rdclass),\n",
" 'rdtype': dns.rdatatype.to_text(ds_rrsig_rrset.rdtype),\n",
" 'rdata': [str(rdata) for rdata in ds_rrsig_rrset],\n",
" }\n",
" \n",
" return data\n",
"\n",
"def collect_chain_data(domain, data=None):\n",
" if data is None:\n",
" data = {}\n",
"\n",
" domain_data = collect_domain_data(domain, {})\n",
" data[domain] = domain_data\n",
"\n",
" parent_domain = '.'.join(domain.split('.')[1:])\n",
" if parent_domain: # If it's not the root\n",
" data = collect_chain_data(parent_domain, data)\n",
"\n",
" return data"
]
},
{
"cell_type": "code",
"execution_count": 99,
"id": "3be530fd-4478-449d-a8b3-0baa0577558d",
"metadata": {},
"outputs": [],
"source": [
"def getRecord(data):\n",
" name = dns.name.from_text(data['name'])\n",
" ttl = data['ttl']\n",
" rdclass = dns.rdataclass.from_text(data['rdclass'])\n",
" rdtype = dns.rdatatype.from_text(data['rdtype'])\n",
" text_rdatas = data['rdata']\n",
" return dns.rrset.from_text_list(name, ttl, rdclass, rdtype, text_rdatas)\n",
"\n",
"def verify_txt_signature(txt_data, txt_rrsig_data, dnskey_data):\n",
" # Create RRsets from data\n",
" txt_rrset = getRecord(txt_data)\n",
" txt_rrsig_rrset = getRecord(txt_rrsig_data)\n",
" dnskey_rrset = getRecord(dnskey_data)\n",
"\n",
" # Create a dictionary for the DNSKEY RRset for validation\n",
" dnskey_dict = {dnskey_rrset.name: dnskey_rrset}\n",
"\n",
" # Validate the signature\n",
" try:\n",
" dns.dnssec.validate(txt_rrset, txt_rrsig_rrset, dnskey_dict)\n",
" print(f'TXT record is valid')\n",
" except dns.dnssec.ValidationFailure as e:\n",
" print(f'Failed to validate TXT record')\n",
" print(e)\n",
" raise Exception(f'Failed to validate TXT record')"
]
},
{
"cell_type": "code",
"execution_count": 100,
"id": "1ceb20e5-6e60-48ff-b2b3-e1b011de59da",
"metadata": {},
"outputs": [],
"source": [
"def verify_dnskey_with_ds(ds_data, dnskey_data):\n",
" # Create RRsets from data\n",
" ds_rrset = getRecord(ds_data)\n",
" dnskey_rrset = getRecord(dnskey_data)\n",
"\n",
" # Extract Key Tag from DS RRset\n",
" ds_key_tag = ds_rrset[0].key_tag\n",
"\n",
" # Iterate through DNSKEY RRset to find matching key\n",
" for rdata in dnskey_rrset:\n",
" key_tag = dns.dnssec.key_id(rdata)\n",
" if key_tag == ds_key_tag:\n",
" # Matching key found\n",
" print(f'DNSKEY for fyi is valid')\n",
" return\n",
" raise Exception(f'Failed to validate DNSKEY')"
]
},
{
"cell_type": "code",
"execution_count": 101,
"id": "105962dd-17f2-4bb4-8f5d-ce9c93f1f5aa",
"metadata": {},
"outputs": [],
"source": [
"def verify_ds_signature(ds_data, ds_rrsig_data, parent_dnskey_data):\n",
" # Create RRsets from data\n",
" ds_rrset = getRecord(ds_data)\n",
" ds_rrsig_rrset = getRecord(ds_rrsig_data)\n",
" parent_dnskey_rrset = getRecord(parent_dnskey_data)\n",
"\n",
" # Verify DS signature\n",
" try:\n",
" dns.dnssec.validate(ds_rrset, ds_rrsig_rrset, {dns.name.from_text(parent_dnskey_data['name']): parent_dnskey_rrset})\n",
" print(f'DS signature for fyi is valid')\n",
" except dns.dnssec.ValidationFailure as e:\n",
" print(f'Failed to validate DS signature')\n",
" print(e)\n",
" raise Exception('Invalid DS')\n"
]
},
{
"cell_type": "code",
"execution_count": 102,
"id": "cdeb0ce4-056b-4bc0-8a3d-470b8913f741",
"metadata": {},
"outputs": [],
"source": [
"def show_verified_message(domain, data):\n",
" msg = str(data[\"txt_data\"][-1])\n",
" value = msg.split('=')[1].strip('\"')\n",
" print(f'Successfully verified a new cert at {domain}: {value}')"
]
},
{
"cell_type": "code",
"execution_count": 103,
"id": "41044668-e44b-4b2c-969f-bff3aabaad89",
"metadata": {},
"outputs": [],
"source": [
"\n",
"def verify_chain(current_domain, data):\n",
" # Step a: Walk up the records\n",
"\n",
" # Step b: Verify TXT record was signed with the DNSKEY\n",
" verify_txt_signature(\n",
" data['txt_params'],\n",
" data['txt_rrsig_params'],\n",
" data[current_domain]['dnskey_params']\n",
" )\n",
"\n",
" while current_domain:\n",
" # Step c: Verify that each DNSKEY came from the provided DS record\n",
" verify_dnskey_with_ds(\n",
" data[current_domain]['ds_params'],\n",
" data[current_domain]['dnskey_params']\n",
" )\n",
" \n",
" parent_domain = '.'.join(current_domain.split('.')[1:])\n",
" \n",
" if parent_domain not in data:\n",
" # Special case, we already know the root, so everything MUST end up here or it's bogus\n",
" with open('root_rrset.json', 'r') as file:\n",
" root_data = json.load(file)\n",
" \n",
" verify_ds_signature(\n",
" data[current_domain]['ds_params'],\n",
" data[current_domain]['ds_rrsig_params'],\n",
" root_data,\n",
" )\n",
" \n",
" # Reached the root\n",
" return\n",
"\n",
" # Step d: Verify that the DS record was signed by the parent DNSKEY\n",
" print(current_domain, parent_domain)\n",
" verify_ds_signature(\n",
" data[current_domain]['ds_params'],\n",
" data[current_domain]['ds_rrsig_params'],\n",
" data[parent_domain]['dnskey_params'],\n",
" )\n",
" print('done')\n",
"\n",
" current_domain = parent_domain\n",
"\n",
" raise Exception('Never reached root')\n"
]
},
{
"cell_type": "code",
"execution_count": 104,
"id": "735e04ee-7dbf-41d1-8c20-bb2bd6f29662",
"metadata": {},
"outputs": [],
"source": [
"domain = \"boom.fyi\"\n",
"# Collect data for the entire chain\n",
"chain_data = collect_record(domain, 'data-cert=')\n",
"chain_data = collect_chain_data(domain, chain_data)\n",
"\n",
"# Save to a JSON file\n",
"with open('chain_data.json', 'w') as f:\n",
" json.dump(chain_data, f, indent=4)"
]
},
{
"cell_type": "code",
"execution_count": 105,
"id": "fbdddd27-dcfc-47e6-91f3-0e42d79ee0a2",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"TXT record is valid\n",
"DNSKEY for fyi is valid\n",
"boom.fyi fyi\n",
"DS signature for fyi is valid\n",
"done\n",
"DNSKEY for fyi is valid\n",
"DS signature for fyi is valid\n",
"Successfully verified a new cert at boom.fyi: hello_world\n"
]
}
],
"source": [
"# Load the data from the JSON file\n",
"with open('chain_data.json', 'r') as file:\n",
" data = json.load(file)\n",
"\n",
"# Run the verification\n",
"verify_chain(domain, data)\n",
"# No exceptions thrown, so now\n",
"show_verified_message(domain, data)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c5686aa7-1063-4e7a-80a1-6e8674cc7cb3",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.4"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
{
"txt_data": [
"\"data-cert=hello_world\""
],
"txt_params": {
"name": "boom.fyi.",
"ttl": 300,
"rdclass": "IN",
"rdtype": "TXT",
"rdata": [
"\"data-cert=hello_world\""
]
},
"txt_rrsig_data": [
"TXT 13 2 300 20231104043233 20231102023233 34505 boom.fyi. TyfWfONX/ehwmC5NI+nmQ93cFHd1d0UN O9AL4fA4nSe1BF+WSlG6360/bKkoWMdG URLmVXVM64JdsPLF3F0CSQ=="
],
"txt_rrsig_params": {
"name": "boom.fyi.",
"ttl": 300,
"rdclass": "IN",
"rdtype": "RRSIG",
"rdata": [
"TXT 13 2 300 20231104043233 20231102023233 34505 boom.fyi. TyfWfONX/ehwmC5NI+nmQ93cFHd1d0UN O9AL4fA4nSe1BF+WSlG6360/bKkoWMdG URLmVXVM64JdsPLF3F0CSQ=="
]
},
"boom.fyi": {
"dnskey_data": [
"256 3 13 oJMRESz5E4gYzS/q6XDrvU1qMPYIjCWz JaOau8XNEZeqCYKD5ar0IRd8KqXXFJkq mVfRvMGPmM1x8fGAa2XhSA==",
"257 3 13 mdsswUyr3DPW132mOi8V9xESWE8jTo0d xCjjnopKl+GqJxpVXckHAeF+KkxLbxIL fDLUT0rAK9iUzy1L53eKGQ=="
],
"dnskey_params": {
"name": "boom.fyi.",
"ttl": 619,
"rdclass": "IN",
"rdtype": "DNSKEY",
"rdata": [
"256 3 13 oJMRESz5E4gYzS/q6XDrvU1qMPYIjCWz JaOau8XNEZeqCYKD5ar0IRd8KqXXFJkq mVfRvMGPmM1x8fGAa2XhSA==",
"257 3 13 mdsswUyr3DPW132mOi8V9xESWE8jTo0d xCjjnopKl+GqJxpVXckHAeF+KkxLbxIL fDLUT0rAK9iUzy1L53eKGQ=="
]
},
"ds_data": [
"2371 13 2 7c2bad06c3db075fb95f8cbc750d2f9ce96c088c0ca2b66eaf16d655b200fbdb"
],
"ds_params": {
"name": "boom.fyi.",
"ttl": 3600,
"rdclass": "IN",
"rdtype": "DS",
"rdata": [
"2371 13 2 7c2bad06c3db075fb95f8cbc750d2f9ce96c088c0ca2b66eaf16d655b200fbdb"
],
"digest_type": [
2
]
},
"ds_rrsig_data": [
"DS 8 2 3600 20231122154425 20231101144425 9855 fyi. pu+b6A8msuqQSUvRbEBeZHT9KAwf96bC EkTcXehzXTvWdvjGgZq+w7Mxp7BbA3uU ZXaxoRHJU2WwGUUDIK3RxLDjrKoD1iWX sRwGmwXz31kvZFQXJphxIhUKk+3/Ny84 503KYK8+eBEXeDKTxKN/8c7hA+ZhChsF 7gGiqxErxX4="
],
"ds_rrsig_params": {
"name": "boom.fyi.",
"ttl": 3600,
"rdclass": "IN",
"rdtype": "RRSIG",
"rdata": [
"DS 8 2 3600 20231122154425 20231101144425 9855 fyi. pu+b6A8msuqQSUvRbEBeZHT9KAwf96bC EkTcXehzXTvWdvjGgZq+w7Mxp7BbA3uU ZXaxoRHJU2WwGUUDIK3RxLDjrKoD1iWX sRwGmwXz31kvZFQXJphxIhUKk+3/Ny84 503KYK8+eBEXeDKTxKN/8c7hA+ZhChsF 7gGiqxErxX4="
]
}
},
"fyi": {
"dnskey_data": [
"256 3 8 AwEAAafOh6Ngub3lbBILGuzioMIQBBkB rQTvmBeFlYUNDQguL318ZwYoeIbZrxVo XVvcggw5bckHEFSSz2a0cAN063Sw11fl HAgbaUFtVOFBPmQ8Jxvvp0Y9BcepFA3k cfyLqN2mFFMcsrbxLgzK/dEC40G1SPtN yZKM8pKH1z28yHTH",
"256 3 8 AwEAAcTC0rO4M40wiDySfgS0AcQsJ72f haS0osWXnZOMw9yBOLDSJmffvGpQPyeA 9s/fFpE5HVp8tCOG3Wr+BO+3DPV4kCZo JWqOSuYXIIVJKxdIIBG/3sq2zZf8NzHr QHFmK7jsLfHKzYSCcHxXpq2EzKIY6bxZ Hdbqhb6rzYFTr4M3",
"257 3 8 AwEAAbTyOzbsbV/JFqJ9dDMOWletULAY 2enp0InxEpMMNE0MzO9x9TDhDHrEDagr AEZfY5yPobXlzfIJdYo1CPz4kNYPwE/t HGMfgErbrjIerMC77UmgiF8pP1Lrx7Mb J3T2ImXdwxnkgyA4RN3KENJgiP2pHQAr fsG5d7ASPB/8GVBO5Ad6yf74buqXlnGv oRzOYkLccG/AveUtuL3gdHaUcnhSLHXk lqKP/jbjToU1QYBvzJLDwGLEYxHUhDTj GvsiMSxFPjkLn/PYItWOEY6fUEGevHyv xPdiIdx4x+ZmoRy/jzOCjZ+ZKii4aZ51 444KgUJ6dn7M2psf0x7XLR9r5xs="
],
"dnskey_params": {
"name": "fyi.",
"ttl": 3059,
"rdclass": "IN",
"rdtype": "DNSKEY",
"rdata": [
"256 3 8 AwEAAafOh6Ngub3lbBILGuzioMIQBBkB rQTvmBeFlYUNDQguL318ZwYoeIbZrxVo XVvcggw5bckHEFSSz2a0cAN063Sw11fl HAgbaUFtVOFBPmQ8Jxvvp0Y9BcepFA3k cfyLqN2mFFMcsrbxLgzK/dEC40G1SPtN yZKM8pKH1z28yHTH",
"256 3 8 AwEAAcTC0rO4M40wiDySfgS0AcQsJ72f haS0osWXnZOMw9yBOLDSJmffvGpQPyeA 9s/fFpE5HVp8tCOG3Wr+BO+3DPV4kCZo JWqOSuYXIIVJKxdIIBG/3sq2zZf8NzHr QHFmK7jsLfHKzYSCcHxXpq2EzKIY6bxZ Hdbqhb6rzYFTr4M3",
"257 3 8 AwEAAbTyOzbsbV/JFqJ9dDMOWletULAY 2enp0InxEpMMNE0MzO9x9TDhDHrEDagr AEZfY5yPobXlzfIJdYo1CPz4kNYPwE/t HGMfgErbrjIerMC77UmgiF8pP1Lrx7Mb J3T2ImXdwxnkgyA4RN3KENJgiP2pHQAr fsG5d7ASPB/8GVBO5Ad6yf74buqXlnGv oRzOYkLccG/AveUtuL3gdHaUcnhSLHXk lqKP/jbjToU1QYBvzJLDwGLEYxHUhDTj GvsiMSxFPjkLn/PYItWOEY6fUEGevHyv xPdiIdx4x+ZmoRy/jzOCjZ+ZKii4aZ51 444KgUJ6dn7M2psf0x7XLR9r5xs="
]
},
"ds_data": [
"24340 8 2 853f208b5d528007d5b57bb498524364da3a2c43ad48444aae41d3afdb5b5aba"
],
"ds_params": {
"name": "fyi.",
"ttl": 86400,
"rdclass": "IN",
"rdtype": "DS",
"rdata": [
"24340 8 2 853f208b5d528007d5b57bb498524364da3a2c43ad48444aae41d3afdb5b5aba"
],
"digest_type": [
2
]
},
"ds_rrsig_data": [
"DS 8 1 86400 20231115200000 20231102190000 46780 . OECcmbAMpI4qw6yiiDemrDfuCw5ZAsj0 MRv5Dd8Y/DeGyTYgNtu5NhK8AfBC59OJ WnFGrskuNIsyNKmbGKtcs9BD/3P1fjPs obDSO5rDiV/XQ1RYqXeI5CzI2TrxBlR8 nuZlozj/9lMuDgve5M8pMSh7nlVK6Qe3 Fd1EbWDeG5YtmOO+2mCamtrRjzopFWdG r1tYvz3XNqvuOFe9ofHvSH8dvPBP9vIg 9JDcTfl5SsjJNAnzanQBQHr1l54NCuxF pwRtYQCHMnMVwia/Dw6pALqH2vamPP7G JTYSVxHVwnG6V8M38fp9XsrNgOkUzBKB F5JAHiF5f2MX/Z0H2rY0VA=="
],
"ds_rrsig_params": {
"name": "fyi.",
"ttl": 86400,
"rdclass": "IN",
"rdtype": "RRSIG",
"rdata": [
"DS 8 1 86400 20231115200000 20231102190000 46780 . OECcmbAMpI4qw6yiiDemrDfuCw5ZAsj0 MRv5Dd8Y/DeGyTYgNtu5NhK8AfBC59OJ WnFGrskuNIsyNKmbGKtcs9BD/3P1fjPs obDSO5rDiV/XQ1RYqXeI5CzI2TrxBlR8 nuZlozj/9lMuDgve5M8pMSh7nlVK6Qe3 Fd1EbWDeG5YtmOO+2mCamtrRjzopFWdG r1tYvz3XNqvuOFe9ofHvSH8dvPBP9vIg 9JDcTfl5SsjJNAnzanQBQHr1l54NCuxF pwRtYQCHMnMVwia/Dw6pALqH2vamPP7G JTYSVxHVwnG6V8M38fp9XsrNgOkUzBKB F5JAHiF5f2MX/Z0H2rY0VA=="
]
}
}
}
{
"TrustAnchor": {
"id": "380DC50D-484E-40D0-A3AE-68F2B18F61C7",
"source": "http://data.iana.org/root-anchors/root-anchors.xml",
"Zone": ".",
"KeyDigests": [
{
"id": "Kjqmt7v",
"validFrom": "2010-07-15T00:00:00+00:00",
"validUntil": "2019-01-11T00:00:00+00:00",
"KeyTag": 19036,
"Algorithm": 8,
"DigestType": 2,
"Digest": "49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5"
},
{
"id": "Klajeyz",
"validFrom": "2017-02-02T00:00:00+00:00",
"KeyTag": 20326,
"Algorithm": 8,
"DigestType": 2,
"Digest": "E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D"
}
]
}
}
{
"name": ".",
"ttl": 3239,
"rdclass": "IN",
"rdtype": "DNSKEY",
"rdata": [
"256 3 8 AwEAAddS95RV5uUtkUCN7vyvpb0kDZgm tXwN5Sj/d08+X7ND2sgWBabKnFhftrOs Sx9DUhKR3gpMPIxac84Nou8Wzkiu2A/s TzP1F6KpCL8epgemdlZVd1ATHEjpB0KH IQmDjSEO/frGgi8ijQ2vDF3AMSrUwH7q ntL1E5ufPHGKRM+agGghcAYfJHJN1dw7 Ki3Fo22RDB3VZBxU9yJ3vl/T4hngeL7z K84vgl62tlJJw1rK5S/3U4p/bZarjtMF OHDfh0DEj1ywtRpkpPnge03gmINoa2tz +Kff67kbQb0NhHJYzPRpViaMEWZI9pgG H9ZyuFdNrNRx68XSiO7sya7/i+c=",
"257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexT BAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq 7HrxRixHlFlExOLAJr5emLvN7SWXgnLh 4+B5xQlNVz8Og8kvArMtNROxVQuCaSnI DdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLr jyBxWezF0jLHwVN8efS3rCj/EWgvIWgb 9tarpVUDK/b58Da+sqqls3eNbuv7pr+e oZG+SrDK6nWeL3c6H5Apxz7LjVc1uTId sIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6 +cn8HFRm+2hM8AnXGXws9555KrUB5qih ylGa8subX2Nn6UwNR1AkUTV74bU="
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment