|
| 1 | +## From Certipy https://github.com/ly4k/Certipy/blob/main/certipy/lib/constants.py |
| 2 | +## https://github.com/ly4k/Certipy/blob/main/certipy/lib/structs.py |
| 3 | +## https://github.com/ly4k/Certipy/blob/main/certipy/lib/formatting.py |
| 4 | + |
| 5 | +import enum |
| 6 | +import struct |
| 7 | + |
| 8 | +def filetime_to_span(filetime: str) -> int: |
| 9 | + (span,) = struct.unpack("<q", filetime) |
| 10 | + |
| 11 | + span *= -0.0000001 |
| 12 | + |
| 13 | + return int(span) |
| 14 | + |
| 15 | +def span_to_str(span: int) -> str: |
| 16 | + if (span % 31536000 == 0) and (span // 31536000) >= 1: |
| 17 | + if (span / 31536000) == 1: |
| 18 | + return "1 year" |
| 19 | + return "%i years" % (span // 31536000) |
| 20 | + elif (span % 2592000 == 0) and (span // 2592000) >= 1: |
| 21 | + if (span // 2592000) == 1: |
| 22 | + return "1 month" |
| 23 | + else: |
| 24 | + return "%i months" % (span // 2592000) |
| 25 | + elif (span % 604800 == 0) and (span // 604800) >= 1: |
| 26 | + if (span / 604800) == 1: |
| 27 | + return "1 week" |
| 28 | + else: |
| 29 | + return "%i weeks" % (span // 604800) |
| 30 | + |
| 31 | + elif (span % 86400 == 0) and (span // 86400) >= 1: |
| 32 | + if (span // 86400) == 1: |
| 33 | + return "1 day" |
| 34 | + else: |
| 35 | + return "%i days" % (span // 86400) |
| 36 | + elif (span % 3600 == 0) and (span / 3600) >= 1: |
| 37 | + if (span // 3600) == 1: |
| 38 | + return "1 hour" |
| 39 | + else: |
| 40 | + return "%i hours" % (span // 3600) |
| 41 | + else: |
| 42 | + return "" |
| 43 | + |
| 44 | +def to_pascal_case(snake_str: str) -> str: |
| 45 | + components = snake_str.split("_") |
| 46 | + return "".join(x.title() for x in components) |
| 47 | + |
| 48 | +def _high_bit(value): |
| 49 | + """returns index of highest bit, or -1 if value is zero or negative""" |
| 50 | + return value.bit_length() - 1 |
| 51 | + |
| 52 | +def _decompose(flag, value): |
| 53 | + """Extract all members from the value.""" |
| 54 | + # _decompose is only called if the value is not named |
| 55 | + not_covered = value |
| 56 | + negative = value < 0 |
| 57 | + members = [] |
| 58 | + for member in flag: |
| 59 | + member_value = member.value |
| 60 | + if member_value and member_value & value == member_value: |
| 61 | + members.append(member) |
| 62 | + not_covered &= ~member_value |
| 63 | + if not negative: |
| 64 | + tmp = not_covered |
| 65 | + while tmp: |
| 66 | + flag_value = 2 ** _high_bit(tmp) |
| 67 | + if flag_value in flag._value2member_map_: |
| 68 | + members.append(flag._value2member_map_[flag_value]) |
| 69 | + not_covered &= ~flag_value |
| 70 | + tmp &= ~flag_value |
| 71 | + if not members and value in flag._value2member_map_: |
| 72 | + members.append(flag._value2member_map_[value]) |
| 73 | + members.sort(key=lambda m: m._value_, reverse=True) |
| 74 | + if len(members) > 1 and members[0].value == value: |
| 75 | + # we have the breakdown, don't need the value member itself |
| 76 | + members.pop(0) |
| 77 | + return members, not_covered |
| 78 | + |
| 79 | +class PkiCertificateAuthorityFlags(enum.Enum): |
| 80 | + NO_TEMPLATE_SUPPORT = 1 |
| 81 | + SUPPORTS_NT_AUTHENTICATION = 2 |
| 82 | + CA_SUPPORTS_MANUAL_AUTHENTICATION = 4 |
| 83 | + CA_SERVERTYPE_ADVANCED = 8 |
| 84 | + |
| 85 | +OID_TO_STR_MAP = { |
| 86 | + "1.3.6.1.4.1.311.76.6.1": "Windows Update", |
| 87 | + "1.3.6.1.4.1.311.10.3.11": "Key Recovery", |
| 88 | + "1.3.6.1.4.1.311.10.3.25": "Windows Third Party Application Component", |
| 89 | + "1.3.6.1.4.1.311.21.6": "Key Recovery Agent", |
| 90 | + "1.3.6.1.4.1.311.10.3.6": "Windows System Component Verification", |
| 91 | + "1.3.6.1.4.1.311.61.4.1": "Early Launch Antimalware Drive", |
| 92 | + "1.3.6.1.4.1.311.10.3.23": "Windows TCB Component", |
| 93 | + "1.3.6.1.4.1.311.61.1.1": "Kernel Mode Code Signing", |
| 94 | + "1.3.6.1.4.1.311.10.3.26": "Windows Software Extension Verification", |
| 95 | + "2.23.133.8.3": "Attestation Identity Key Certificate", |
| 96 | + "1.3.6.1.4.1.311.76.3.1": "Windows Store", |
| 97 | + "1.3.6.1.4.1.311.10.6.1": "Key Pack Licenses", |
| 98 | + "1.3.6.1.4.1.311.20.2.2": "Smart Card Logon", |
| 99 | + "1.3.6.1.5.2.3.5": "KDC Authentication", |
| 100 | + "1.3.6.1.5.5.7.3.7": "IP security use", |
| 101 | + "1.3.6.1.4.1.311.10.3.8": "Embedded Windows System Component Verification", |
| 102 | + "1.3.6.1.4.1.311.10.3.20": "Windows Kits Component", |
| 103 | + "1.3.6.1.5.5.7.3.6": "IP security tunnel termination", |
| 104 | + "1.3.6.1.4.1.311.10.3.5": "Windows Hardware Driver Verification", |
| 105 | + "1.3.6.1.5.5.8.2.2": "IP security IKE intermediate", |
| 106 | + "1.3.6.1.4.1.311.10.3.39": "Windows Hardware Driver Extended Verification", |
| 107 | + "1.3.6.1.4.1.311.10.6.2": "License Server Verification", |
| 108 | + "1.3.6.1.4.1.311.10.3.5.1": "Windows Hardware Driver Attested Verification", |
| 109 | + "1.3.6.1.4.1.311.76.5.1": "Dynamic Code Generato", |
| 110 | + "1.3.6.1.5.5.7.3.8": "Time Stamping", |
| 111 | + "1.3.6.1.4.1.311.10.3.4.1": "File Recovery", |
| 112 | + "1.3.6.1.4.1.311.2.6.1": "SpcRelaxedPEMarkerCheck", |
| 113 | + "2.23.133.8.1": "Endorsement Key Certificate", |
| 114 | + "1.3.6.1.4.1.311.2.6.2": "SpcEncryptedDigestRetryCount", |
| 115 | + "1.3.6.1.4.1.311.10.3.4": "Encrypting File System", |
| 116 | + "1.3.6.1.5.5.7.3.1": "Server Authentication", |
| 117 | + "1.3.6.1.4.1.311.61.5.1": "HAL Extension", |
| 118 | + "1.3.6.1.5.5.7.3.4": "Secure Email", |
| 119 | + "1.3.6.1.5.5.7.3.5": "IP security end system", |
| 120 | + "1.3.6.1.4.1.311.10.3.9": "Root List Signe", |
| 121 | + "1.3.6.1.4.1.311.10.3.30": "Disallowed List", |
| 122 | + "1.3.6.1.4.1.311.10.3.19": "Revoked List Signe", |
| 123 | + "1.3.6.1.4.1.311.10.3.21": "Windows RT Verification", |
| 124 | + "1.3.6.1.4.1.311.10.3.10": "Qualified Subordination", |
| 125 | + "1.3.6.1.4.1.311.10.3.12": "Document Signing", |
| 126 | + "1.3.6.1.4.1.311.10.3.24": "Protected Process Verification", |
| 127 | + "1.3.6.1.4.1.311.80.1": "Document Encryption", |
| 128 | + "1.3.6.1.4.1.311.10.3.22": "Protected Process Light Verification", |
| 129 | + "1.3.6.1.4.1.311.21.19": "Directory Service Email Replication", |
| 130 | + "1.3.6.1.4.1.311.21.5": "Private Key Archival", |
| 131 | + "1.3.6.1.4.1.311.10.5.1": "Digital Rights", |
| 132 | + "1.3.6.1.4.1.311.10.3.27": "Preview Build Signing", |
| 133 | + "1.3.6.1.4.1.311.20.2.1": "Certificate Request Agent", |
| 134 | + "2.23.133.8.2": "Platform Certificate", |
| 135 | + "1.3.6.1.4.1.311.20.1": "CTL Usage", |
| 136 | + "1.3.6.1.5.5.7.3.9": "OCSP Signing", |
| 137 | + "1.3.6.1.5.5.7.3.3": "Code Signing", |
| 138 | + "1.3.6.1.4.1.311.10.3.1": "Microsoft Trust List Signing", |
| 139 | + "1.3.6.1.4.1.311.10.3.2": "Microsoft Time Stamping", |
| 140 | + "1.3.6.1.4.1.311.76.8.1": "Microsoft Publishe", |
| 141 | + "1.3.6.1.5.5.7.3.2": "Client Authentication", |
| 142 | + "1.3.6.1.5.2.3.4": "PKINIT Client Authentication", |
| 143 | + "1.3.6.1.4.1.311.10.3.13": "Lifetime Signing", |
| 144 | + "2.5.29.37.0": "Any Purpose", |
| 145 | + "1.3.6.1.4.1.311.64.1.1": "Server Trust", |
| 146 | + "1.3.6.1.4.1.311.10.3.7": "OEM Windows System Component Verification", |
| 147 | +} |
| 148 | + |
| 149 | +class IntFlag(enum.IntFlag): |
| 150 | + def to_list(self): |
| 151 | + cls = self.__class__ |
| 152 | + members, _ = _decompose(cls, self._value_) |
| 153 | + return members |
| 154 | + |
| 155 | + def to_str_list(self): |
| 156 | + return list(map(lambda x: str(x), self.to_list())) |
| 157 | + |
| 158 | + |
| 159 | + def __str__(self): |
| 160 | + cls = self.__class__ |
| 161 | + if self._name_ is not None: |
| 162 | + return "%s" % (to_pascal_case(self._name_)) |
| 163 | + members, _ = _decompose(cls, self._value_) |
| 164 | + if len(members) == 1 and members[0]._name_ is None: |
| 165 | + return "%r" % (members[0]._value_) |
| 166 | + else: |
| 167 | + return "%s" % ( |
| 168 | + ", ".join( |
| 169 | + [to_pascal_case(str(m._name_ or m._value_)) for m in members] |
| 170 | + ), |
| 171 | + ) |
| 172 | + |
| 173 | + def __repr__(self): |
| 174 | + return str(self) |
| 175 | + |
| 176 | +class MS_PKI_CERTIFICATE_NAME_FLAG(IntFlag): |
| 177 | + NONE = 0 |
| 178 | + ENROLLEE_SUPPLIES_SUBJECT = 1 |
| 179 | + ADD_EMAIL = 0x00000002 |
| 180 | + ADD_OBJ_GUID = 0x00000004 |
| 181 | + OLD_CERT_SUPPLIES_SUBJECT_AND_ALT_NAME = 0x00000008 |
| 182 | + ADD_DIRECTORY_PATH = 0x00000100 |
| 183 | + ENROLLEE_SUPPLIES_SUBJECT_ALT_NAME = 0x00010000 |
| 184 | + SUBJECT_ALT_REQUIRE_DOMAIN_DNS = 0x00400000 |
| 185 | + SUBJECT_ALT_REQUIRE_SPN = 0x00800000 |
| 186 | + SUBJECT_ALT_REQUIRE_DIRECTORY_GUID = 0x01000000 |
| 187 | + SUBJECT_ALT_REQUIRE_UPN = 0x02000000 |
| 188 | + SUBJECT_ALT_REQUIRE_EMAIL = 0x04000000 |
| 189 | + SUBJECT_ALT_REQUIRE_DNS = 0x08000000 |
| 190 | + SUBJECT_REQUIRE_DNS_AS_CN = 0x10000000 |
| 191 | + SUBJECT_REQUIRE_EMAIL = 0x20000000 |
| 192 | + SUBJECT_REQUIRE_COMMON_NAME = 0x40000000 |
| 193 | + SUBJECT_REQUIRE_DIRECTORY_PATH = 0x80000000 |
| 194 | + |
| 195 | + def __str__(self): |
| 196 | + return self.name |
| 197 | + |
| 198 | +class MS_PKI_PRIVATE_KEY_FLAG(IntFlag): |
| 199 | + REQUIRE_PRIVATE_KEY_ARCHIVAL = 0x00000001 |
| 200 | + EXPORTABLE_KEY = 0x00000010 |
| 201 | + STRONG_KEY_PROTECTION_REQUIRED = 0x00000020 |
| 202 | + REQUIRE_ALTERNATE_SIGNATURE_ALGORITHM = 0x00000040 |
| 203 | + REQUIRE_SAME_KEY_RENEWAL = 0x00000080 |
| 204 | + USE_LEGACY_PROVIDER = 0x00000100 |
| 205 | + ATTEST_NONE = 0x00000000 |
| 206 | + ATTEST_REQUIRED = 0x00002000 |
| 207 | + ATTEST_PREFERRED = 0x00001000 |
| 208 | + ATTESTATION_WITHOUT_POLICY = 0x00004000 |
| 209 | + EK_TRUST_ON_USE = 0x00000200 |
| 210 | + EK_VALIDATE_CERT = 0x00000400 |
| 211 | + EK_VALIDATE_KEY = 0x00000800 |
| 212 | + HELLO_LOGON_KEY = 0x00200000 |
| 213 | + |
| 214 | + def __str__(self): |
| 215 | + cls = self.__class__ |
| 216 | + if self._name_ is not None: |
| 217 | + return "%s" % (self._name_) |
| 218 | + members, _ = _decompose(cls, self._value_) |
| 219 | + if len(members) == 1 and members[0]._name_ is None: |
| 220 | + return "%r" % (members[0]._value_) |
| 221 | + else: |
| 222 | + return "%s" % ( |
| 223 | + ", ".join( |
| 224 | + [str(m._name_ or m._value_) for m in members] |
| 225 | + ), |
| 226 | + ) |
| 227 | + |
| 228 | +class MS_PKI_ENROLLMENT_FLAG(IntFlag): |
| 229 | + NONE = 0x00000000 |
| 230 | + INCLUDE_SYMMETRIC_ALGORITHMS = 0x00000001 |
| 231 | + PEND_ALL_REQUESTS = 0x00000002 |
| 232 | + PUBLISH_TO_KRA_CONTAINER = 0x00000004 |
| 233 | + PUBLISH_TO_DS = 0x00000008 |
| 234 | + AUTO_ENROLLMENT_CHECK_USER_DS_CERTIFICATE = 0x00000010 |
| 235 | + AUTO_ENROLLMENT = 0x00000020 |
| 236 | + CT_FLAG_DOMAIN_AUTHENTICATION_NOT_REQUIRED = 0x80 |
| 237 | + PREVIOUS_APPROVAL_VALIDATE_REENROLLMENT = 0x00000040 |
| 238 | + USER_INTERACTION_REQUIRED = 0x00000100 |
| 239 | + ADD_TEMPLATE_NAME = 0x200 |
| 240 | + REMOVE_INVALID_CERTIFICATE_FROM_PERSONAL_STORE = 0x00000400 |
| 241 | + ALLOW_ENROLL_ON_BEHALF_OF = 0x00000800 |
| 242 | + ADD_OCSP_NOCHECK = 0x00001000 |
| 243 | + ENABLE_KEY_REUSE_ON_NT_TOKEN_KEYSET_STORAGE_FULL = 0x00002000 |
| 244 | + NOREVOCATIONINFOINISSUEDCERTS = 0x00004000 |
| 245 | + INCLUDE_BASIC_CONSTRAINTS_FOR_EE_CERTS = 0x00008000 |
| 246 | + ALLOW_PREVIOUS_APPROVAL_KEYBASEDRENEWAL_VALIDATE_REENROLLMENT = 0x00010000 |
| 247 | + ISSUANCE_POLICIES_FROM_REQUEST = 0x00020000 |
| 248 | + SKIP_AUTO_RENEWAL = 0x00040000 |
| 249 | + NO_SECURITY_EXTENSION = 0x00080000 |
| 250 | + |
| 251 | + def __str__(self): |
| 252 | + cls = self.__class__ |
| 253 | + if self._name_ is not None: |
| 254 | + return "%s" % (self._name_) |
| 255 | + members, _ = _decompose(cls, self._value_) |
| 256 | + if len(members) == 1 and members[0]._name_ is None: |
| 257 | + return "%r" % (members[0]._value_) |
| 258 | + else: |
| 259 | + return "%s" % ( |
| 260 | + ", ".join( |
| 261 | + [str(m._name_ or m._value_) for m in members] |
| 262 | + ), |
| 263 | + ) |
0 commit comments