#!/usr/bin/python3
#
# Copyright (C) 2019 Joe Testa <[email protected]>
#
# This tool will parse the list of TLS ciphersuites from IANA
# (https://www.iana.org/assignments/tls-parameters/tls-parameters.xml) into a C-struct
# for use in missing_ciphersuites.h.
#
import csv, sys
from datetime import date
# We must be given a path to a CSV file with the ciphersuites. It can be obtained from
# <https://www.iana.org/assignments/tls-parameters/tls-parameters.xml>.
if len(sys.argv) != 2:
print("\nUsage: %s tls_ciphers.csv\n\nHint: copy the TLS table in CSV format from <https://www.iana.org/assignments/tls-parameters/tls-parameters.xml>.\n" % sys.argv[0])
exit(0)
csv_file = sys.argv[1]
print("/* Auto-generated by %s on %s. */" % (sys.argv[0], date.today().strftime("%B %d, %Y")))
print("struct missing_ciphersuite missing_ciphersuites[] = {")
with open(csv_file, 'r') as f:
reader = csv.reader(f)
for row in reader:
id = row[0]
cipher_name = row[1]
# Skip the header.
if '0x' not in id:
continue
# Skip reserved or unassigned ranges. Also skip SCSV ciphers.
if ('Reserved' in cipher_name) or ('Unassigned' in cipher_name) or ('TLS_FALLBACK_SCSV' in cipher_name) or ('TLS_EMPTY_RENEGOTIATION_INFO_SCSV' in cipher_name):
continue
# Convert '0xC0,0x87' to '0xC087'
parsed_id = id[0:4] + id[7:9]
if len(parsed_id) != 6:
print("Error: parsed ID is not length 6: %s" % parsed_id)
exit -1
# Make an educated guess of the cipher's bit strength based on its name.
bits = -1
if 'AES_128' in cipher_name:
bits = 128
elif 'AES_256' in cipher_name:
bits = 256
elif 'CHACHA20' in cipher_name:
bits = 256
elif 'CAMELLIA_128' in cipher_name:
bits = 128
elif 'CAMELLIA_256' in cipher_name:
bits = 256
elif 'ARIA_128' in cipher_name:
bits = 128
elif 'ARIA_256' in cipher_name:
bits = 256
elif '3DES' in cipher_name:
bits = 112
elif 'DES40' in cipher_name:
bits = 40
elif '_DES_' in cipher_name:
bits = 56
elif 'RC4_128' in cipher_name:
bits = 128
elif 'RC4_40' in cipher_name:
bits = 40
elif 'IDEA' in cipher_name:
bits = 128
elif '_RC2_' in cipher_name:
bits = 40
elif 'GOSTR341112_256' in cipher_name:
bits = 256
elif '_SM4_' in cipher_name: # See http://www.gmbz.org.cn/upload/2018-04-04/1522788048733065051.pdf
bits = 128
print(' {%s, "%s", %d, VALL, 0},' % (parsed_id, cipher_name, bits))
# These ciphers are reserved for private use. Kind of like the 10.0.0.0/8 IPv4
# addresses.
print("\n /* The ciphers below are reserved for private use (see RFC8446). */")
for i in range(0, 256):
low_byte = hex(i)[2:].upper()
if len(low_byte) == 1:
low_byte = '0' + low_byte
parsed_id = '0xFF' + low_byte
cipher_name = 'PRIVATE_CIPHER_%d' % i
bits = -1
print(' {%s, "%s", %d, VALL, 0},' % (parsed_id, cipher_name, bits))
print("};")
exit 0