1 |
329216fe
|
Stanislav Král
|
from functools import cmp_to_key
|
2 |
|
|
|
3 |
|
|
# basic constraints
|
4 |
|
|
from typing import Dict
|
5 |
|
|
|
6 |
|
|
BASIC_CONSTRAINTS_KEY = "basicConstraints"
|
7 |
|
|
|
8 |
|
|
CRITICAL = "critical"
|
9 |
|
|
CA = "CA:TRUE"
|
10 |
|
|
|
11 |
|
|
# key usages
|
12 |
|
|
KEY_USAGES_KEY = "keyUsage"
|
13 |
|
|
|
14 |
|
|
DIGITAL_SIGNATURE = "digitalSignature"
|
15 |
|
|
NON_REPUDIATION = "nonRepudiation"
|
16 |
|
|
KEY_ENCIPHERMENT = "keyEncipherment"
|
17 |
|
|
DATA_ENCIPHERMENT = "dataEncipherment"
|
18 |
|
|
KEY_AGREEMENT = "keyAgreement"
|
19 |
|
|
KEY_CERT_SIGN = "keyCertSign"
|
20 |
|
|
CRL_SIGN = "cRLSign"
|
21 |
|
|
ENCIPHER_ONLY = "encipherOnly"
|
22 |
|
|
DECIPHER_ONLY = "decipherOnly"
|
23 |
|
|
|
24 |
|
|
# extended key usages
|
25 |
|
|
EXTENDED_KEY_USAGE_KEY = "extendedKeyUsage"
|
26 |
|
|
|
27 |
|
|
SERVER_AUTH = "serverAuth"
|
28 |
|
|
CLIENT_AUTH = "clientAuth"
|
29 |
|
|
CODE_SIGNING = "codeSigning"
|
30 |
|
|
EMAIL_PROTECTION = "emailProtection"
|
31 |
|
|
TIME_STAMPING = "timeStamping"
|
32 |
|
|
OCSP_SIGNING = "OCSPSigning"
|
33 |
|
|
|
34 |
|
|
|
35 |
|
|
class ExtensionFieldFlags:
|
36 |
|
|
def __init__(self, key_usages_flags, extended_key_usages_flags, basic_constraints_flags):
|
37 |
|
|
self.key_usages_flags = key_usages_flags
|
38 |
|
|
self.extended_key_usages_flags = extended_key_usages_flags
|
39 |
|
|
self.basic_constraints_flags = basic_constraints_flags
|
40 |
|
|
|
41 |
|
|
|
42 |
|
|
def __compare_with_critical_prioritized(item1, item2):
|
43 |
1bd093c5
|
Stanislav Král
|
# compare in such way, that the CRITICAL str is always considered to be "smaller" (put before) than any other string
|
44 |
329216fe
|
Stanislav Král
|
if item1 == CRITICAL:
|
45 |
|
|
# first item is CRITICAL, return -1
|
46 |
|
|
return -1
|
47 |
|
|
elif item2 == CRITICAL:
|
48 |
|
|
# second item is CRITICAL, return 2
|
49 |
|
|
return 1
|
50 |
|
|
elif item1 == item2:
|
51 |
|
|
# items are same, return 0
|
52 |
|
|
return 0
|
53 |
|
|
else:
|
54 |
|
|
# none of the items is CRITICAL and they are not equal, compare via < str overloaded operator
|
55 |
|
|
return -1 if item1 < item2 else 1
|
56 |
|
|
|
57 |
|
|
|
58 |
|
|
def usages_to_extension_lines(usages, required_extension_flags: Dict[int, ExtensionFieldFlags]):
|
59 |
|
|
"""
|
60 |
|
|
Converts usages dictionary to a configuration lines to be put in the extensions section
|
61 |
|
|
|
62 |
|
|
:param usages: a dictionary containing usages to be converted into ext. configuration lines
|
63 |
|
|
:param required_extension_flags: an object containing an information about which flags are required to be present
|
64 |
|
|
in the extension configuration lines to be generated
|
65 |
|
|
:return: a list of configuration lines
|
66 |
|
|
"""
|
67 |
|
|
# initialize sets that will represent values of all required fields (keyUsages, extendedKeyUsage, basicConstraints)
|
68 |
|
|
key_usages = set()
|
69 |
|
|
extended_key_usages = set()
|
70 |
|
|
basic_constraints = set()
|
71 |
|
|
|
72 |
|
|
# iterate over given usages
|
73 |
|
|
for usage, value in usages.items():
|
74 |
|
|
# check whether the usage is set to true
|
75 |
|
|
if value:
|
76 |
|
|
# load required extension fields
|
77 |
|
|
key_usage = required_extension_flags[usage]
|
78 |
|
|
|
79 |
|
|
# append the required keyUsage flags
|
80 |
|
|
key_usages = key_usages.union(key_usage.key_usages_flags)
|
81 |
|
|
|
82 |
|
|
# append the required extendedKeyUsage flags
|
83 |
|
|
extended_key_usages = extended_key_usages.union(key_usage.extended_key_usages_flags)
|
84 |
|
|
|
85 |
|
|
# append the required basicConstraints flags
|
86 |
|
|
basic_constraints = basic_constraints.union(key_usage.basic_constraints_flags)
|
87 |
|
|
|
88 |
|
|
lines = []
|
89 |
|
|
|
90 |
|
|
# sort flags so their order is consistent and therefore testable
|
91 |
|
|
# after sorting the flags generate an extension line joining the flags with "," character
|
92 |
|
|
if len(basic_constraints) > 0:
|
93 |
|
|
# "basicConstraints" (maybe) require the "critical" flag to be at the head of the list if present
|
94 |
|
|
lines.append(
|
95 |
|
|
f"""{BASIC_CONSTRAINTS_KEY}={",".join(sorted(basic_constraints, key=cmp_to_key(__compare_with_critical_prioritized)))}""")
|
96 |
|
|
|
97 |
|
|
if len(key_usages) > 0:
|
98 |
|
|
# "keyUsages" (maybe) require the "critical" flag to be at the head of the list if present
|
99 |
|
|
lines.append(
|
100 |
|
|
f"""{KEY_USAGES_KEY}={",".join(sorted(key_usages, key=cmp_to_key(__compare_with_critical_prioritized)))}""")
|
101 |
|
|
|
102 |
|
|
if len(extended_key_usages) > 0:
|
103 |
|
|
lines.append(f"""{EXTENDED_KEY_USAGE_KEY}={",".join(sorted(extended_key_usages))}""")
|
104 |
|
|
|
105 |
|
|
return lines
|