Skip to content

Instantly share code, notes, and snippets.

@mzpqnxow
Last active September 12, 2021 00:31
Show Gist options
  • Save mzpqnxow/b863ee8ea4b30c96c10f7ee4d101e887 to your computer and use it in GitHub Desktop.
Save mzpqnxow/b863ee8ea4b30c96c10f7ee4d101e887 to your computer and use it in GitHub Desktop.
Generate a mapping for IANA, OpenSSL and the int16 representation of SSL/TLS cipher-suites
#!/usr/bin/env python3
#
# Parse https://testssl.sh/openssl-iana.mapping.html or a copy/paste of
# the content from a browser, drop it in a line-based JSON file
#
# This lets you quickly translate from IANA<->OpenSSL<->int16 which is
# useful for unusual people :>
#
# Requirements: lxml, pandas>=1.2
# If Pandas is too old, you will get:
# TypeError: applymap() got an unexpected keyword argument 'na_action'
# Install Pandas >= 1.2
#
# - (C) 2021, [email protected], BSD 3-Clause License
#
import pandas as pd
from typing import Optional, Iterable
SSL_MAPPING_URL = 'https://testssl.sh/openssl-iana.mapping.html'
# You can copy+paste the table from a browser into a file
# and save it locally if you want, it loads fine. Otherwise
# leave this None
# SSL_MAPPING_CSV = 'copy-pasted-from-testssl-sh.csv'
SSL_MAPPING_CSV = None
DEFAULT_COLUMNS = ['suite_id', 'openssl_name', 'kex', 'symmetric', 'keysize', 'iana_name']
OUTFILE = 'cipher_map.ndjson'
def postprocess(df: pd.DataFrame):
df = df.merge(
df['keysize'].str.extract(r'(\d+),?(.*)', expand=True),
left_index=True,
right_index=True).rename(columns={0: 'keysize_bits', 1: 'export'})
df.drop(columns=['keysize'], inplace=True)
df['suite_id'] = df['suite_id'].str.strip('[]')
df[['suite_id', 'keysize_bits']] = df[['suite_id', 'keysize_bits']].applymap(
lambda s: int(s, base=0x10), na_action='ignore')
df['export'] = df['export'].str.strip().map(bool, na_action='ignore')
return df
def from_url(url: str, flavor: str = 'lxml',
columns: Optional[Iterable[str]] = None) -> pd.DataFrame:
if columns is None:
columns = DEFAULT_COLUMNS
df = pd.read_html(SSL_MAPPING_URL, flavor='lxml').pop()
return df.rename(columns=dict(zip(df.columns, columns)))
def from_csv(infile: str, names: Iterable[str] = None) -> pd.DataFrame:
if names is None:
names = DEFAULT_COLUMNS
return pd.read_csv(infile, delimiter='\t', names=names)
def main():
df = from_csv(SSL_MAPPING_CSV) if SSL_MAPPING_CSV is not None else from_url(SSL_MAPPING_URL)
df = postprocess(df)
df.to_json(OUTFILE, orient='records', lines=True)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment