1
|
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
|
# compare in such way, that the CRITICAL str is always considered to be "smaller" (put before) than any other string
|
44
|
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
|