#!/usr/bin/python """ (C) 2016 Jack Lloyd (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity Botan is released under the Simplified BSD License (see license.txt) """ import sys import datetime import re from collections import defaultdict def format_oid(oid): #return '"' + oid + '"' return "{" + oid.replace('.', ',') + '}' def format_map(m, for_oid = False): s = '' for k in sorted(m.keys()): v = m[k] if len(s) > 0: s += ' ' if for_oid: s += '{ "%s", OID(%s) },\n' % (k,format_oid(v)) else: s += '{ "%s", "%s" },\n' % (k,v) s = s[:-2] # chomp last two chars return s def format_as_map(oid2str, str2oid): return """/* * OID maps * * This file was automatically generated by %s on %s * * All manual edits to this file will be lost. Edit the script * then regenerate this source file. * * Botan is released under the Simplified BSD License (see license.txt) */ #include #include namespace Botan { std::unordered_map OIDS::load_oid2str_map() { return std::unordered_map{ %s }; } std::unordered_map OIDS::load_str2oid_map() { return std::unordered_map{ %s }; } } """ % (sys.argv[0], datetime.date.today().strftime("%Y-%m-%d"), format_map(oid2str), format_map(str2oid, True)) def format_if(m, nm,t=False): s = '' for k in sorted(m.keys()): v = m[k] if t: s += ' if(%s == "%s") return OID(%s);\n' % (nm,k, format_oid(v)) else: s += ' if(%s == "%s") return "%s";\n' % (nm,k, v) s = s[:-1] return s def format_as_ifs(oid2str, str2oid): return """/* * OID maps * * This file was automatically generated by %s on %s * * All manual edits to this file will be lost. Edit the script * then regenerate this source file. * * Botan is released under the Simplified BSD License (see license.txt) */ #include namespace Botan { namespace OIDS { std::string lookup(const OID& oid) { const std::string oid_str = oid.to_string(); %s return std::string(); } OID lookup(const std::string& name) { %s return OID(); } } } """ % (sys.argv[0], datetime.date.today().strftime("%Y-%m-%d"), format_if(oid2str,"oid_str"), format_if(str2oid, "name", True)) def format_dn_ub_map(dn_ub, oid2str): s = '' for k in sorted(dn_ub.keys()): v = dn_ub[k] s += ' { Botan::OID({%s}), %s }, // %s\n' % (k.replace('.',','),v,oid2str[k]) # delete last ',' and \n idx = s.rfind(',') if idx != -1: s = s[:idx] + s[idx+1:-1] return s def format_dn_ub_as_map(dn_ub, oid2str): return """/* * DN_UB maps: Upper bounds on the length of DN strings * * This file was automatically generated by %s on %s * * All manual edits to this file will be lost. Edit the script * then regenerate this source file. * * Botan is released under the Simplified BSD License (see license.txt) */ #include #include #include namespace { /** * Upper bounds for the length of distinguished name fields as given in RFC 5280, Appendix A. * Only OIDS recognized by botan are considered, so far. * Maps OID string representations instead of human readable strings in order * to avoid an additional lookup. */ static const std::map DN_UB = { %s }; } namespace Botan { //static size_t X509_DN::lookup_ub(const OID& oid) { auto ub_entry = DN_UB.find(oid); if(ub_entry != DN_UB.end()) { return ub_entry->second; } else { return 0; } } } """ % (sys.argv[0], datetime.date.today().strftime("%Y-%m-%d"), format_dn_ub_map(dn_ub,oid2str)) def format_set_map(m): s = '' for k in sorted(m.keys()): v = m[k] if len(s) > 0: s += ' ' s += '{ "%s", {' % k for pad in v: s += '"%s", ' % pad if len(v) is not 0: s = s[:-2] s += '} },\n' s = s[:-1] return s def format_pads_as_map(sig_dict): return """/* * Sets of allowed padding schemes for public key types * * This file was automatically generated by %s on %s * * All manual edits to this file will be lost. Edit the script * then regenerate this source file. * * Botan is released under the Simplified BSD License (see license.txt) */ #include #include #include #include #include namespace Botan { namespace { const std::unordered_map> allowed_signature_paddings = { %s }; } const std::vector get_sig_paddings(const std::string algo) { auto i = allowed_signature_paddings.find(algo); if(i != allowed_signature_paddings.end()) return i->second; return {}; } bool sig_algo_and_pad_ok(const std::string algo, std::string padding) { const std::vector pads = get_sig_paddings(algo); return std::find(pads.begin(), pads.end(), padding) != pads.end(); } } """ % (sys.argv[0], datetime.date.today().strftime("%Y-%m-%d"), format_set_map(sig_dict)) def main(args = None): """ Print header files (oids.cpp, dn_ub.cpp) depending on the first argument and on srs/build-data/oids.txt Choose 'oids' to print oids.cpp, needs to be written to src/lib/asn1/oids.cpp Choose 'dn_ub' to print dn_ub.cpp, needs to be written to src/lib/x509/X509_dn_ub.cpp Choose 'pads' to print padding.cpp, needs to be written to src/lib/pk_pad/padding.cpp """ if args is None: args = sys.argv if len(args) < 2: raise Exception("Use either 'oids', 'dn_ub', 'pads' as first argument") oid_lines = open('./src/build-data/oids.txt').readlines() oid_re = re.compile("^([0-9][0-9.]+) = ([A-Za-z0-9_\./\(\), -]+)(?: = )?([0-9]+)?$") hdr_re = re.compile("^\[([a-z0-9_]+)\]$") pad_re = re.compile("^([A-Za-z0-9_\., -]+)/([A-Za-z0-9_-]+)[A-Za-z0-9_\.\(\), -]*$") oid2str = {} str2oid = {} dn_ub = {} sig2pads = defaultdict(set) cur_hdr = None for line in oid_lines: line = line.strip() if len(line) == 0: continue if line[0] == '#': continue match = hdr_re.match(line) if match is not None: cur_hdr = match.group(1) continue match = oid_re.match(line) if match is None: raise Exception(line) oid = match.group(1) nam = match.group(2) if oid in str2oid: print("Duplicated OID", oid, name, oid2str[oid]) sys.exit() # hard error else: oid2str[oid] = nam # parse upper bounds for DNs if cur_hdr == "dn": if match.lastindex < 3: raise Exception("Could not find an upper bound for DN " + match.group(1)) dn_ub[oid] = match.group(3) # parse signature paddings elif cur_hdr == "signature": pad_match = pad_re.search(nam) if pad_match is not None: sig2pads[pad_match.group(1)].add(pad_match.group(2)) if nam in str2oid: #str2oid[nam] = oid pass else: str2oid[nam] = oid if args[1] == "oids": print(format_as_map(oid2str, str2oid)) elif args[1] == "dn_ub": print(format_dn_ub_as_map(dn_ub,oid2str)) elif args[1] == "pads": print(format_pads_as_map(sig2pads)) else: print("Unknown command: try oids, dn_ub, or pads") return 0 if __name__ == '__main__': sys.exit(main())