New upstream release.
Kali Janitor
2 years ago
0 | 0 | Metadata-Version: 1.2 |
1 | 1 | Name: msldap |
2 | Version: 0.3.30 | |
2 | Version: 0.3.38 | |
3 | 3 | Summary: Python library to play with MS LDAP |
4 | 4 | Home-page: https://github.com/skelsec/msldap |
5 | 5 | Author: Tamas Jos |
0 | [![Documentation Status](https://readthedocs.org/projects/msldap/badge/?version=latest)](https://msldap.readthedocs.io/en/latest/?badge=latest) | |
0 | ![Supported Python versions](https://img.shields.io/badge/python-3.6+-blue.svg) [![Documentation Status](https://readthedocs.org/projects/msldap/badge/?version=latest)](https://msldap.readthedocs.io/en/latest/?badge=latest) [![Twitter](https://img.shields.io/twitter/follow/skelsec?label=skelsec&style=social)](https://twitter.com/intent/follow?screen_name=skelsec) | |
1 | 1 | |
2 | # msldap client | |
3 | ![Documentation Status](https://user-images.githubusercontent.com/19204702/81515211-3761e880-9333-11ea-837f-bcbe2a67ee48.gif ) | |
2 | :triangular_flag_on_post: This is the public repository of msldap, for latest version and updates please consider supporting us through https://porchetta.industries/ | |
4 | 3 | |
5 | 4 | # msldap |
6 | 5 | LDAP library for MS AD |
6 | ![Documentation Status](https://user-images.githubusercontent.com/19204702/81515211-3761e880-9333-11ea-837f-bcbe2a67ee48.gif ) | |
7 | ||
8 | ## :triangular_flag_on_post: Sponsors | |
9 | ||
10 | If you want to sponsors this project and have the latest updates on this project, latest issues fixed, latest features, please support us on https://porchetta.industries/ | |
11 | ||
12 | ## Official Discord Channel | |
13 | ||
14 | Come hang out on Discord! | |
15 | ||
16 | [![Porchetta Industries](https://discordapp.com/api/guilds/736724457258745996/widget.png?style=banner3)](https://discord.gg/ycGXUxy) | |
17 | ||
7 | 18 | |
8 | 19 | # Documentation |
9 | 20 | [Awesome documentation here!](https://msldap.readthedocs.io/en/latest/) |
59 | 70 | - kerberos-aes (dc option param must be used) |
60 | 71 | - kerberos-keytab (dc option param must be used) |
61 | 72 | - kerberos-ccache (dc option param must be used) |
73 | - kerberos-pfx (dc option param must be used) | |
74 | - kerberos-pem (dc option param must be used) | |
75 | - kerberos-certstore (dc option param must be used, windows only) | |
62 | 76 | - sspi-ntlm (windows only!) |
63 | 77 | - sspi-kerberos (windows only!) |
64 | 78 | - anonymous |
95 | 109 | ``` |
96 | 110 | |
97 | 111 | # Kudos |
98 | ||
112 | Certificate services functionality was based on [certi](https://github.com/zer1t0/certi) created by @zer1t0 |
0 | msldap (0.3.38-0kali1) UNRELEASED; urgency=low | |
1 | ||
2 | * New upstream release. | |
3 | ||
4 | -- Kali Janitor <[email protected]> Wed, 30 Mar 2022 00:15:12 -0000 | |
5 | ||
0 | 6 | msldap (0.3.30-0kali1) kali-dev; urgency=medium |
1 | 7 | |
2 | 8 | [ Kali Janitor ] |
5 | 5 | |
6 | 6 | from minikerberos.protocol.constants import EncryptionType |
7 | 7 | from minikerberos.protocol import encryption |
8 | from minikerberos.crypto.hashing import md5, hmac_md5 | |
9 | from minikerberos.crypto.RC4 import RC4 | |
8 | from unicrypto.hashlib import md5 | |
9 | from unicrypto import hmac # import md5, hmac_md5 | |
10 | from unicrypto.symmetric import RC4 | |
10 | 11 | |
11 | 12 | #TODO: RC4 support! |
12 | 13 | |
204 | 205 | else: |
205 | 206 | mic.SND_SEQ = sequenceNumber.to_bytes(4, 'big', signed = False) + b'\xff'*4 |
206 | 207 | |
207 | Ksign_ctx = hmac_md5(self.session_key.contents) | |
208 | Ksign_ctx = hmac.new(self.session_key.contents, digestmod='md5') | |
208 | 209 | Ksign_ctx.update(b'signaturekey\0') |
209 | 210 | Ksign = Ksign_ctx.digest() |
210 | 211 | |
211 | 212 | id = 15 |
212 | 213 | temp = md5( id.to_bytes(4, 'little', signed = False) + mic.to_bytes()[:8] ).digest() |
213 | chksum_ctx = hmac_md5(Ksign) | |
214 | chksum_ctx = hmac.new(Ksign, digestmod='md5') | |
214 | 215 | chksum_ctx.update(temp) |
215 | 216 | mic.SGN_CKSUM = chksum_ctx.digest()[:8] |
216 | 217 | |
217 | 218 | id = 0 |
218 | temp = hmac_md5(self.session_key.contents) | |
219 | temp = hmac.new(self.session_key.contents, digestmod='md5') | |
219 | 220 | temp.update(id.to_bytes(4, 'little', signed = False)) |
220 | 221 | |
221 | Kseq_ctx = hmac_md5(temp.digest()) | |
222 | Kseq_ctx = hmac.new(temp.digest(), digestmod='md5') | |
222 | 223 | Kseq_ctx.update(mic.SGN_CKSUM) |
223 | 224 | Kseq = Kseq_ctx.digest() |
224 | 225 | |
263 | 264 | # #testing purposes only, pls remove |
264 | 265 | |
265 | 266 | |
266 | temp = hmac_md5(self.session_key.contents) | |
267 | temp = hmac.new(self.session_key.contents, digestmod='md5') | |
267 | 268 | temp.update(b'signaturekey\0') |
268 | 269 | Ksign = temp.digest() |
269 | 270 | |
275 | 276 | klocal += bytes([b ^ 0xf0]) |
276 | 277 | |
277 | 278 | id = 0 |
278 | temp = hmac_md5(klocal) | |
279 | temp = hmac.new(klocal, digestmod='md5') | |
279 | 280 | temp.update(id.to_bytes(4, 'little', signed = False)) |
280 | temp = hmac_md5(temp.digest()) | |
281 | temp = hmac.new(temp.digest(), digestmod='md5') | |
281 | 282 | temp.update(seq_num.to_bytes(4, 'big', signed = False)) |
282 | 283 | Kcrypt = temp.digest() |
283 | 284 | |
284 | temp = hmac_md5(Ksign) | |
285 | temp = hmac.new(Ksign, digestmod='md5') | |
285 | 286 | temp.update(Sgn_Cksum) |
286 | 287 | token.SGN_CKSUM = temp.digest()[:8] |
287 | 288 | |
288 | 289 | id = 0 |
289 | temp = hmac_md5(self.session_key.contents) | |
290 | temp = hmac.new(self.session_key.contents, digestmod='md5') | |
290 | 291 | temp.update(id.to_bytes(4, 'little', signed = False)) |
291 | temp = hmac_md5(temp.digest()) | |
292 | temp = hmac.new(temp.digest(), digestmod='md5') | |
292 | 293 | temp.update(token.SGN_CKSUM) |
293 | 294 | Kseq = temp.digest() |
294 | 295 | |
308 | 309 | wrap = GSSWRAP_RC4.from_bytes(hdr) |
309 | 310 | |
310 | 311 | id = 0 |
311 | temp = hmac_md5(self.session_key.contents) | |
312 | temp = hmac.new(self.session_key.contents, digestmod='md5') | |
312 | 313 | temp.update(id.to_bytes(4, 'little', signed = False)) |
313 | temp = hmac_md5(temp.digest()) | |
314 | temp = hmac.new(temp.digest(), digestmod='md5') | |
314 | 315 | temp.update(wrap.SGN_CKSUM) |
315 | 316 | Kseq = temp.digest() |
316 | 317 | |
317 | 318 | snd_seq = RC4(Kseq).encrypt(wrap.SND_SEQ) |
318 | 319 | |
319 | 320 | id = 0 |
320 | temp = hmac_md5(klocal) | |
321 | temp = hmac.new(klocal, digestmod='md5') | |
321 | 322 | temp.update(id.to_bytes(4, 'little', signed = False)) |
322 | temp = hmac_md5(temp.digest()) | |
323 | temp = hmac.new(temp.digest(), digestmod='md5') | |
323 | 324 | temp.update(snd_seq[:4]) |
324 | 325 | Kcrypt = temp.digest() |
325 | 326 | |
330 | 331 | id = 13 |
331 | 332 | Sgn_Cksum_calc = md5(id.to_bytes(4, 'little', signed = False) + wrap.to_bytes()[:8] + dec_cofounder + dec_data).digest() |
332 | 333 | |
333 | temp = hmac_md5(Ksign) | |
334 | temp = hmac.new(Ksign, digestmod='md5') | |
334 | 335 | temp.update(Sgn_Cksum_calc) |
335 | 336 | Sgn_Cksum_calc = temp.digest()[:8] |
336 | 337 |
14 | 14 | from msldap.authentication.kerberos.gssapi import get_gssapi, GSSWrapToken, KRB5_MECH_INDEP_TOKEN |
15 | 15 | from minikerberos.protocol.asn1_structs import AP_REQ, AP_REP, TGS_REP |
16 | 16 | from minikerberos.protocol.encryption import Enctype, Key, _enctype_table |
17 | from pyodidewsnet.sspiproxyws import SSPIProxyWS | |
17 | from wsnet.operator.sspiproxy import WSNETSSPIProxy | |
18 | 18 | |
19 | 19 | |
20 | 20 | # mutual auth not supported |
51 | 51 | self.settings = settings |
52 | 52 | self.mode = 'CLIENT' |
53 | 53 | url = '%s://%s:%s' % (self.settings.proto, self.settings.host, self.settings.port) |
54 | self.sspi = SSPIProxyWS(url, self.settings.agent_id) | |
54 | self.sspi = WSNETSSPIProxy(url, self.settings.agent_id) | |
55 | 55 | self.client = None |
56 | 56 | self.target = None |
57 | 57 | self.gssapi = None |
14 | 14 | from msldap.authentication.kerberos.gssapi import get_gssapi, GSSWrapToken, KRB5_MECH_INDEP_TOKEN |
15 | 15 | from minikerberos.protocol.asn1_structs import AP_REQ, AP_REP, TGS_REP |
16 | 16 | from minikerberos.protocol.encryption import Enctype, Key, _enctype_table |
17 | from pyodidewsnet.clientauth import WSNETAuth | |
17 | from wsnet.pyodide.clientauth import WSNETAuth | |
18 | 18 | |
19 | 19 | |
20 | 20 | # mutual auth not supported |
0 | import io | |
1 | import os | |
2 | import hmac | |
3 | 0 | import datetime |
4 | 1 | |
5 | from msldap.crypto.symmetric import DES | |
6 | from msldap.crypto.hashing import * | |
2 | from unicrypto.symmetric import DES | |
3 | from unicrypto import hmac | |
4 | from unicrypto.hashlib import md5, md4 | |
7 | 5 | from msldap.authentication.ntlm.structures.challenge_response import * |
8 | ||
6 | from msldap.authentication.ntlm.structures.negotiate_flags import NegotiateFlags | |
9 | 7 | |
10 | 8 | class NTLMCredentials: |
11 | 9 | @staticmethod |
158 | 156 | else: |
159 | 157 | raise Exception('Unknown cred type!') |
160 | 158 | |
161 | hm = hmac_md5(lm_hash) | |
159 | hm = hmac.new(lm_hash, digestmod='md5') | |
162 | 160 | hm.update(bytes.fromhex(self.ServerChallenge)) |
163 | 161 | hm.update(bytes.fromhex(self.ChallengeFromClinet)) |
164 | 162 | |
185 | 183 | else: |
186 | 184 | nt_hash = bytes.fromhex(self.credentials.nt_hash) |
187 | 185 | |
188 | hm = hmac_md5(self.SessionBaseKey) | |
186 | hm = hmac.new(self.SessionBaseKey, digestmod='md5') | |
189 | 187 | hm.update(self.ServerChallenge) |
190 | 188 | hm.update(self.LMResponse.to_bytes()[:8]) |
191 | 189 | |
414 | 412 | cc = NTLMv2ClientChallenge.construct(timestamp, client_challenge, server_details) |
415 | 413 | temp = cc.to_bytes() |
416 | 414 | |
417 | hm = hmac_md5(nt_hash_v2) | |
415 | hm = hmac.new(nt_hash_v2, digestmod='md5') | |
418 | 416 | hm.update(server_challenge) |
419 | 417 | hm.update(temp) |
420 | 418 | |
425 | 423 | ntlm_creds.NTResponse.ChallengeFromClinet = cc |
426 | 424 | |
427 | 425 | |
428 | hm = hmac_md5(nt_hash_v2) | |
426 | hm = hmac.new(nt_hash_v2, digestmod='md5') | |
429 | 427 | hm.update(server_challenge) |
430 | 428 | hm.update(client_challenge) |
431 | 429 | |
434 | 432 | ntlm_creds.LMResponse.ChallengeFromClinet = client_challenge |
435 | 433 | |
436 | 434 | |
437 | hm = hmac_md5(nt_hash_v2) | |
435 | hm = hmac.new(nt_hash_v2, digestmod='md5') | |
438 | 436 | hm.update(NTProofStr) |
439 | 437 | ntlm_creds.SessionBaseKey = hm.digest() |
440 | 438 | |
476 | 474 | # print(self.ServerChallenge) |
477 | 475 | # print(self.ChallengeFromClinet) |
478 | 476 | |
479 | hm = hmac_md5(nt_hash) | |
477 | hm = hmac.new(nt_hash, digestmod='md5') | |
480 | 478 | hm.update(bytes.fromhex(self.ServerChallenge)) |
481 | 479 | hm.update(bytes.fromhex(self.ChallengeFromClinet)) |
482 | 480 | |
511 | 509 | |
512 | 510 | def NTOWFv2(Passwd, User, UserDom, PasswdHash = None): |
513 | 511 | if PasswdHash is not None: |
514 | fp = hmac_md5(PasswdHash) | |
512 | fp = hmac.new(PasswdHash, digestmod='md5') | |
515 | 513 | else: |
516 | fp = hmac_md5(NTOWFv1(Passwd)) | |
514 | fp = hmac.new(NTOWFv1(Passwd), digestmod='md5') | |
517 | 515 | fp.update((User.upper() + UserDom).encode('utf-16le')) |
518 | 516 | return fp.digest() |
519 | 517 |
0 | 0 | import os |
1 | 1 | import struct |
2 | import hmac | |
3 | import copy | |
4 | import hashlib | |
5 | ||
6 | #from aiosmb.commons.connection.credential import SMBNTLMCredential | |
7 | #from aiosmb.commons.serverinfo import NTLMServerInfo | |
2 | ||
8 | 3 | from msldap.authentication.ntlm.templates.server import NTLMServerTemplates |
9 | 4 | from msldap.authentication.ntlm.templates.client import NTLMClientTemplates |
10 | 5 | from msldap.authentication.ntlm.structures.negotiate_flags import NegotiateFlags |
15 | 10 | from msldap.authentication.ntlm.messages.challenge import NTLMChallenge |
16 | 11 | from msldap.authentication.ntlm.messages.authenticate import NTLMAuthenticate |
17 | 12 | from msldap.authentication.ntlm.creds_calc import * |
18 | from msldap.crypto.symmetric import RC4 | |
13 | from unicrypto.symmetric import RC4 | |
14 | from unicrypto import hmac | |
15 | from unicrypto import hashlib | |
19 | 16 | |
20 | 17 | |
21 | 18 | class NTLMHandlerSettings: |
163 | 160 | msg = NTLMSSP_MESSAGE_SIGNATURE() |
164 | 161 | if NegotiateFlags.NEGOTIATE_KEY_EXCH in self.ntlmChallenge.NegotiateFlags: |
165 | 162 | tt = struct.pack('<i', seqNum) + message |
166 | t = hmac_md5(signingKey) | |
163 | t = hmac.new(signingKey, digestmod='md5') | |
167 | 164 | t.update(tt) |
168 | 165 | |
169 | 166 | msg.Checksum = handle(t.digest()[:8]) |
170 | 167 | msg.SeqNum = seqNum |
171 | 168 | seqNum += 1 |
172 | 169 | else: |
173 | t = hmac_md5(signingKey) | |
170 | t = hmac.new(signingKey, digestmod='md5') | |
174 | 171 | t.update(struct.pack('<i',seqNum)+message) |
175 | 172 | msg.Checksum = t.digest()[:8] |
176 | 173 | msg.SeqNum = seqNum |
0 | 0 | from msldap import logger |
1 | 1 | from msldap.authentication.ntlm.native import NTLMAUTHHandler, NTLMHandlerSettings |
2 | from pyodidewsnet.sspiproxyws import SSPIProxyWS | |
2 | from wsnet.operator.sspiproxy import WSNETSSPIProxy | |
3 | 3 | import enum |
4 | 4 | |
5 | 5 | class ISC_REQ(enum.IntFlag): |
32 | 32 | self.settings = settings |
33 | 33 | self.mode = None #'CLIENT' |
34 | 34 | url = '%s://%s:%s' % (self.settings.proto, self.settings.host, self.settings.port) |
35 | self.sspi = SSPIProxyWS(url, self.settings.agent_id) | |
35 | self.sspi = WSNETSSPIProxy(url, self.settings.agent_id) | |
36 | 36 | self.operator = None |
37 | 37 | self.client = None |
38 | 38 | self.target = None |
0 | 0 | from msldap import logger |
1 | 1 | from msldap.authentication.ntlm.native import NTLMAUTHHandler, NTLMHandlerSettings |
2 | from pyodidewsnet.clientauth import WSNETAuth | |
2 | from wsnet.pyodide.clientauth import WSNETAuth | |
3 | 3 | import enum |
4 | 4 | |
5 | 5 | class ISC_REQ(enum.IntFlag): |
3 | 3 | # Tamas Jos (@skelsec) |
4 | 4 | # |
5 | 5 | |
6 | from codecs import lookup | |
6 | 7 | import copy |
7 | 8 | import asyncio |
8 | 9 | |
15 | 16 | from msldap.connection import MSLDAPClientConnection |
16 | 17 | from msldap.protocol.messages import Control |
17 | 18 | from msldap.ldap_objects import * |
19 | from msldap.commons.utils import KNOWN_SIDS | |
18 | 20 | |
19 | 21 | from winacl.dtyp.security_descriptor import SECURITY_DESCRIPTOR |
20 | 22 | from winacl.dtyp.ace import ACCESS_ALLOWED_OBJECT_ACE, ADS_ACCESS_MASK |
37 | 39 | :rtype: dict |
38 | 40 | |
39 | 41 | """ |
40 | def __init__(self, target, creds): | |
42 | def __init__(self, target, creds, connection = None, keepalive = False): | |
41 | 43 | self.creds = creds |
42 | 44 | self.target = target |
43 | ||
44 | self.ldap_query_page_size = self.target.ldap_query_page_size | |
45 | self.keepalive = keepalive | |
46 | self.ldap_query_page_size = 1000 | |
47 | if self.target is not None: | |
48 | self.ldap_query_page_size = self.target.ldap_query_page_size | |
49 | ||
50 | self.ldap_query_ratelimit = 0 | |
51 | if self.target is not None: | |
52 | self.ldap_query_ratelimit = self.target.ldap_query_ratelimit | |
53 | ||
45 | 54 | self._tree = None |
46 | 55 | self._ldapinfo = None |
47 | self._con = None | |
56 | self._con = connection | |
57 | self.__keepalive_task = None | |
58 | self.keepalive_period = 10 | |
59 | self.disconnected_evt = None | |
60 | self._sid_cache = {} #SID -> (domain, user) | |
61 | self._domainsid_cache = {} # SID -> domain | |
48 | 62 | |
49 | 63 | async def __aenter__(self): |
50 | 64 | return self |
52 | 66 | async def __aexit__(self, exc_type, exc, traceback): |
53 | 67 | await asyncio.wait_for(self.disconnect(), timeout = 1) |
54 | 68 | |
69 | async def __keepalive(self): | |
70 | try: | |
71 | while not self.disconnected_evt.is_set(): | |
72 | if self._con is not None: | |
73 | ldap_filter = r'(distinguishedName=%s)' % self._tree | |
74 | async for entry, err in self.pagedsearch(ldap_filter, MSADInfo_ATTRS): | |
75 | if err is not None: | |
76 | return None, err | |
77 | await asyncio.sleep(self.keepalive_period) | |
78 | ||
79 | ||
80 | except asyncio.CancelledError: | |
81 | return | |
82 | ||
83 | except Exception as e: | |
84 | print('Keepalive exception: %s' % e) | |
85 | await self.disconnect() | |
86 | ||
55 | 87 | async def disconnect(self): |
56 | 88 | try: |
89 | if self.__keepalive_task is not None: | |
90 | self.__keepalive_task.cancel() | |
57 | 91 | if self._con is not None: |
58 | 92 | await self._con.disconnect() |
93 | ||
94 | self.disconnected_evt.set() | |
59 | 95 | |
60 | 96 | except Exception as e: |
61 | 97 | return False, e |
62 | 98 | |
63 | 99 | async def connect(self): |
64 | 100 | try: |
65 | self._con = MSLDAPClientConnection(self.target, self.creds) | |
66 | _, err = await self._con.connect() | |
67 | if err is not None: | |
68 | raise err | |
69 | res, err = await self._con.bind() | |
70 | if err is not None: | |
71 | return False, err | |
101 | self.disconnected_evt = asyncio.Event() | |
102 | if self._con is None: | |
103 | self._con = MSLDAPClientConnection(self.target, self.creds) | |
104 | _, err = await self._con.connect() | |
105 | if err is not None: | |
106 | raise err | |
107 | res, err = await self._con.bind() | |
108 | if err is not None: | |
109 | return False, err | |
72 | 110 | res, err = await self._con.get_serverinfo() |
73 | 111 | if err is not None: |
74 | 112 | raise err |
75 | 113 | self._serverinfo = res |
76 | 114 | self._tree = res['defaultNamingContext'] |
77 | 115 | self._ldapinfo, err = await self.get_ad_info() |
116 | self._domainsid_cache[self._ldapinfo.objectSid] = self._ldapinfo.name | |
117 | if self.keepalive is True: | |
118 | self.__keepalive_task = asyncio.create_task(self.__keepalive()) | |
78 | 119 | if err is not None: |
79 | 120 | raise err |
80 | 121 | return True, None |
84 | 125 | def get_server_info(self): |
85 | 126 | return self._serverinfo |
86 | 127 | |
87 | async def pagedsearch(self, query, attributes, controls = None): | |
128 | async def pagedsearch(self, query, attributes, controls = None, tree = None): | |
88 | 129 | """ |
89 | 130 | Performs a paged search on the AD, using the filter and attributes as a normal query does. |
90 | 131 | !The LDAP connection MUST be active before invoking this function! |
111 | 152 | print('Theconnection is in stopped state!') |
112 | 153 | return |
113 | 154 | |
114 | if self._tree is None: | |
155 | if tree is None: | |
156 | tree = self._tree | |
157 | if tree is None: | |
115 | 158 | raise Exception('BIND first!') |
116 | 159 | t = [] |
117 | 160 | for x in attributes: |
130 | 173 | controls = t |
131 | 174 | |
132 | 175 | async for entry, err in self._con.pagedsearch( |
133 | self._tree, | |
176 | tree, | |
134 | 177 | query, |
135 | 178 | attributes = attributes, |
136 | 179 | size_limit = self.ldap_query_page_size, |
137 | 180 | controls = controls, |
138 | rate_limit=self.target.ldap_query_ratelimit | |
181 | rate_limit=self.ldap_query_ratelimit | |
139 | 182 | ): |
140 | 183 | |
141 | 184 | if err is not None: |
169 | 212 | size_limit = self.ldap_query_page_size, |
170 | 213 | search_scope=LEVEL, |
171 | 214 | controls = None, |
172 | rate_limit=self.target.ldap_query_ratelimit | |
215 | rate_limit=self.ldap_query_ratelimit | |
173 | 216 | ): |
174 | 217 | if err is not None: |
175 | 218 | raise err |
292 | 335 | size_limit = self.ldap_query_page_size, |
293 | 336 | search_scope=BASE, |
294 | 337 | controls = None, |
295 | rate_limit=self.target.ldap_query_ratelimit | |
338 | rate_limit=self.ldap_query_ratelimit | |
296 | 339 | ): |
297 | 340 | if err is not None: |
298 | 341 | yield None, err |
644 | 687 | return None, err |
645 | 688 | |
646 | 689 | return entry['attributes']['objectSid'], None |
647 | ||
690 | ||
691 | async def get_tokengroups_user(self, samaccountname): | |
692 | ldap_filter = r'(sAMAccountName=%s)' % escape_filter_chars(samaccountname) | |
693 | user_dn = None | |
694 | async for entry, err in self.pagedsearch(ldap_filter, ['distinguishedName']): | |
695 | if err is not None: | |
696 | return None, err | |
697 | ||
698 | user_dn = entry['attributes']['distinguishedName'] | |
699 | ||
700 | if user_dn is None: | |
701 | return None, Exception('User not found! %s' % samaccountname) | |
702 | ||
703 | tokengroup = [] | |
704 | async for sids, err in self.get_tokengroups(user_dn): | |
705 | if err is not None: | |
706 | return None, err | |
707 | tokengroup.append(sids) | |
708 | ||
709 | return tokengroup, None | |
710 | ||
648 | 711 | async def get_tokengroups(self, dn): |
649 | 712 | """ |
650 | 713 | Yields SIDs of groups that the given DN is a member of. |
662 | 725 | attributes = attributes, |
663 | 726 | size_limit = self.ldap_query_page_size, |
664 | 727 | search_scope=BASE, |
665 | rate_limit=self.target.ldap_query_ratelimit | |
728 | rate_limit=self.ldap_query_ratelimit | |
666 | 729 | ): |
667 | 730 | if err is not None: |
668 | 731 | yield None, err |
699 | 762 | attributes = [b'tokenGroups'], |
700 | 763 | size_limit = self.ldap_query_page_size, |
701 | 764 | search_scope=BASE, |
702 | rate_limit=self.target.ldap_query_ratelimit | |
765 | rate_limit=self.ldap_query_ratelimit | |
703 | 766 | ): |
704 | 767 | |
705 | 768 | #print(entry2) |
1147 | 1210 | return True, None |
1148 | 1211 | except Exception as e: |
1149 | 1212 | return False, e |
1213 | ||
1214 | async def list_root_cas(self): | |
1215 | try: | |
1216 | ldap_filter = "(objectClass=certificationAuthority)" | |
1217 | tree = "CN=Certification Authorities,CN=Public Key Services,CN=Services,CN=Configuration,%s" % self._ldapinfo.distinguishedName | |
1218 | async for entry, err in self.pagedsearch(ldap_filter, attributes = MSADCA_ATTRS, tree = tree): | |
1219 | if err is not None: | |
1220 | yield None, err | |
1221 | return | |
1222 | yield MSADCA.from_ldap(entry, 'ROOTCA'), None | |
1223 | ||
1224 | except Exception as e: | |
1225 | yield None, e | |
1226 | return | |
1227 | ||
1228 | async def list_ntcas(self): | |
1229 | try: | |
1230 | ldap_filter = "(objectClass=certificationAuthority)" | |
1231 | tree = "CN=NTAuthCertificates,CN=Public Key Services,CN=Services,CN=Configuration,%s" % self._ldapinfo.distinguishedName | |
1232 | async for entry, err in self.pagedsearch(ldap_filter, attributes = MSADCA_ATTRS, tree = tree): | |
1233 | if err is not None: | |
1234 | yield None, err | |
1235 | return | |
1236 | yield MSADCA.from_ldap(entry, 'NTCA'), None | |
1237 | ||
1238 | except Exception as e: | |
1239 | yield None, e | |
1240 | return | |
1241 | ||
1242 | async def list_aiacas(self): | |
1243 | try: | |
1244 | ldap_filter = "(objectClass=certificationAuthority)" | |
1245 | tree = "CN=AIA,CN=Public Key Services,CN=Services,CN=Configuration,%s" % self._ldapinfo.distinguishedName | |
1246 | async for entry, err in self.pagedsearch(ldap_filter, attributes = MSADCA_ATTRS, tree = tree): | |
1247 | if err is not None: | |
1248 | yield None, err | |
1249 | return | |
1250 | yield MSADCA.from_ldap(entry, 'AIACA'), None | |
1251 | ||
1252 | except Exception as e: | |
1253 | yield None, e | |
1254 | return | |
1255 | ||
1256 | async def list_enrollment_services(self): | |
1257 | try: | |
1258 | ldap_filter = "(objectCategory=pKIEnrollmentService)" | |
1259 | tree = "CN=Configuration,%s" % self._ldapinfo.distinguishedName | |
1260 | ||
1261 | async for entry, err in self.pagedsearch(ldap_filter, attributes = MSADEnrollmentService_ATTRS, tree = tree): | |
1262 | if err is not None: | |
1263 | yield None, err | |
1264 | return | |
1265 | yield MSADEnrollmentService.from_ldap(entry), None | |
1266 | ||
1267 | except Exception as e: | |
1268 | yield None, e | |
1269 | return | |
1270 | ||
1271 | async def list_certificate_templates(self, name = None): | |
1272 | try: | |
1273 | req_flags = SDFlagsRequestValue({'Flags' : SDFlagsRequest.DACL_SECURITY_INFORMATION|SDFlagsRequest.GROUP_SECURITY_INFORMATION|SDFlagsRequest.OWNER_SECURITY_INFORMATION}) | |
1274 | controls = [('1.2.840.113556.1.4.801', True, req_flags.dump())] | |
1275 | ||
1276 | ldap_filter = "(objectCategory=pKICertificateTemplate)" | |
1277 | if name is not None: | |
1278 | ldap_filter = "(&(objectCategory=pKICertificateTemplate)(name=%s))" % name | |
1279 | tree = "CN=Configuration,%s" % self._ldapinfo.distinguishedName | |
1280 | ||
1281 | async for entry, err in self.pagedsearch(ldap_filter, attributes = MSADCertificateTemplate_ATTRS, controls=controls, tree = tree): | |
1282 | if err is not None: | |
1283 | yield None, err | |
1284 | return | |
1285 | yield MSADCertificateTemplate.from_ldap(entry), None | |
1286 | ||
1287 | except Exception as e: | |
1288 | yield None, e | |
1289 | return | |
1290 | ||
1291 | async def resolv_sd(self, sd): | |
1292 | "Resolves all SIDs found in security descriptor, returns lookup table" | |
1293 | try: | |
1294 | if isinstance(sd, bytes): | |
1295 | sd = SECURITY_DESCRIPTOR.from_bytes(sd) | |
1296 | ||
1297 | lookup_table = {} | |
1298 | sids = {} | |
1299 | sids[str(sd.Owner)] = 1 | |
1300 | sids[str(sd.Group)] = 1 | |
1301 | if sd.Dacl is not None: | |
1302 | for ace in sd.Dacl.aces: | |
1303 | sids[str(ace.Sid)] = 1 | |
1304 | if sd.Sacl is not None: | |
1305 | for ace in sd.Sacl.aces: | |
1306 | sids[str(ace.Sid)] = 1 | |
1307 | ||
1308 | for sid in sids: | |
1309 | domain, username, err = await self.resolv_sid(sid) | |
1310 | if err is not None: | |
1311 | raise err | |
1312 | lookup_table[sid] = (domain, username) | |
1313 | ||
1314 | return lookup_table, None | |
1315 | ||
1316 | except Exception as e: | |
1317 | return None, e | |
1318 | ||
1319 | async def resolv_sid(self, sid, use_cache = True): | |
1320 | """Performs a SID lookup for object and returns the domain name and the samaccountname""" | |
1321 | try: | |
1322 | sid = str(sid).upper() | |
1323 | if sid in KNOWN_SIDS: | |
1324 | return "BUILTIN", KNOWN_SIDS[sid], None | |
1325 | domain = None | |
1326 | username = None | |
1327 | domainsid = sid.rsplit('-',1)[0] | |
1328 | if domainsid not in self._domainsid_cache: | |
1329 | logger.debug('Domain SID "%s" was not found! ' % domainsid) | |
1330 | return '???', '???', None | |
1331 | domain = self._domainsid_cache[domainsid] | |
1332 | if sid in self._sid_cache: | |
1333 | username = self._sid_cache[sid] | |
1334 | ||
1335 | else: | |
1336 | ldap_filter = r'(objectSid=%s)' % sid | |
1337 | async for entry, err in self.pagedsearch(ldap_filter, attributes = ['sAMAccountName']): | |
1338 | if err is not None: | |
1339 | return None, None, err | |
1340 | username = entry['attributes'].get('sAMAccountName') | |
1341 | ||
1342 | if username is None: | |
1343 | return domain, '???', None | |
1344 | #raise Exception('User not found! %s' % sid) | |
1345 | ||
1346 | if use_cache is True: | |
1347 | self._sid_cache[sid] = username | |
1348 | return domain, username, None | |
1349 | except Exception as e: | |
1350 | return None, None, e | |
1351 | ||
1352 | async def whoami(self): | |
1353 | return await self._con.whoami() | |
1354 | ||
1355 | async def whoamifull(self): | |
1356 | """Full whoami""" | |
1357 | #TODO: it can be the case that the server returns the SID of the user | |
1358 | # implement that path! | |
1359 | result = {} | |
1360 | try: | |
1361 | res, err = await self.whoami() | |
1362 | if err is not None: | |
1363 | raise err | |
1364 | result['raw'] = res | |
1365 | if res.startswith('u:') is True: | |
1366 | domain, samaccountname = res[2:].split('\\', 1) | |
1367 | result['domain'] = domain | |
1368 | result['samaccountname'] = samaccountname | |
1369 | user, err = await self.get_user(samaccountname) | |
1370 | if err is not None: | |
1371 | raise err | |
1372 | result['sid'] = str(user.objectSid) | |
1373 | result['groups'] = {} | |
1374 | async for group_sid, err in self.get_tokengroups(user.distinguishedName): | |
1375 | if err is not None: | |
1376 | raise err | |
1377 | result['groups'][group_sid] = ('NA','NA') | |
1378 | domain, username, err = await self.resolv_sid(group_sid) | |
1379 | if err is not None: | |
1380 | raise err | |
1381 | result['groups'][group_sid] = (domain, username) | |
1382 | ||
1383 | return result, None | |
1384 | except: | |
1385 | return result, None | |
1150 | 1386 | |
1151 | 1387 | #async def get_permissions_for_dn(self, dn): |
1152 | 1388 | # """ |
225 | 225 | LDAPAuthProtocol.KERBEROS_PASSWORD, |
226 | 226 | LDAPAuthProtocol.KERBEROS_CCACHE, |
227 | 227 | LDAPAuthProtocol.KERBEROS_KEYTAB, |
228 | LDAPAuthProtocol.KERBEROS_KIRBI]: | |
228 | LDAPAuthProtocol.KERBEROS_KIRBI, | |
229 | LDAPAuthProtocol.KERBEROS_PFX, | |
230 | LDAPAuthProtocol.KERBEROS_PEM, | |
231 | LDAPAuthProtocol.KERBEROS_CERTSTORE]: | |
229 | 232 | |
230 | 233 | if self.target is None: |
231 | 234 | raise Exception('Target must be specified with Kerberos!') |
243 | 246 | kc = KerberosCredential.from_ccache_file(self.creds.password, self.creds.username, self.creds.domain) |
244 | 247 | elif self.creds.auth_method == LDAPAuthProtocol.KERBEROS_KEYTAB: |
245 | 248 | kc = KerberosCredential.from_kirbi(self.creds.password, self.creds.username, self.creds.domain) |
249 | elif self.creds.auth_method == LDAPAuthProtocol.KERBEROS_PFX: | |
250 | kc = KerberosCredential.from_pfx_file(self.creds.username, self.creds.password, username = self.creds.altname, domain = self.creds.altdomain) | |
251 | self.creds.username = kc.username | |
252 | self.creds.domain = kc.domain | |
253 | self.target.domain = kc.domain | |
254 | elif self.creds.auth_method == LDAPAuthProtocol.KERBEROS_PEM: | |
255 | kc = KerberosCredential.from_pem_file(self.creds.username, self.creds.password, username = self.creds.altname, domain = self.creds.altdomain) | |
256 | self.creds.username = kc.username | |
257 | self.creds.domain = kc.domain | |
258 | self.target.domain = kc.domain | |
259 | elif self.creds.auth_method == LDAPAuthProtocol.KERBEROS_CERTSTORE: | |
260 | # username is the CN of the certificate | |
261 | # secret is the name of the certstore, default: MY | |
262 | certstore = self.creds.secret | |
263 | if self.creds.secret is None: | |
264 | certstore = 'MY' | |
265 | kc = KerberosCredential.from_windows_certstore(self.creds.username, certstore, username = self.creds.altname, domain = self.creds.altdomain) | |
266 | self.creds.username = kc.username | |
267 | self.creds.domain = kc.domain | |
268 | self.target.domain = kc.domain | |
269 | ||
246 | 270 | else: |
247 | 271 | kc = KerberosCredential() |
248 | 272 | kc.username = self.creds.username |
268 | 292 | kcred.enctypes = [23] |
269 | 293 | |
270 | 294 | elif self.creds.auth_method == LDAPAuthProtocol.KERBEROS_CCACHE: |
271 | kc.ccache = self.creds.password | |
272 | 295 | kcred.enctypes = [23,17,18] # TODO: fix this |
273 | 296 | elif self.creds.auth_method == LDAPAuthProtocol.KERBEROS_KEYTAB: |
274 | 297 | kc.keytab = self.creds.password |
275 | 298 | kcred.enctypes = [23,17,18] # TODO: fix this |
276 | 299 | elif self.creds.auth_method == LDAPAuthProtocol.KERBEROS_KIRBI: |
277 | 300 | kcred.enctypes = [23,17,18] # TODO: fix this |
301 | elif self.creds.auth_method in [LDAPAuthProtocol.KERBEROS_PFX, LDAPAuthProtocol.KERBEROS_CERTSTORE, LDAPAuthProtocol.KERBEROS_PEM]: | |
302 | kcred.enctypes = [17,18] | |
278 | 303 | else: |
279 | 304 | raise Exception('No suitable secret type found to set up kerberos!') |
280 | 305 |
34 | 34 | KERBEROS_PASSWORD = 'KERBEROS_PASSWORD' |
35 | 35 | KERBEROS_CCACHE = 'KERBEROS_CCACHE' |
36 | 36 | KERBEROS_KEYTAB = 'KERBEROS_KEYTAB' |
37 | KERBEROS_KIRBI = 'KERBEROS_KIRBI' | |
37 | KERBEROS_KIRBI = 'KERBEROS_KIRBI' | |
38 | KERBEROS_PFX = 'KERBEROS_PFX' | |
39 | KERBEROS_PEM = 'KERBEROS_PEM' | |
40 | KERBEROS_CERTSTORE = 'KERBEROS_CERTSTORE' | |
38 | 41 | MULTIPLEXOR_KERBEROS = 'MULTIPLEXOR_KERBEROS' |
39 | 42 | MULTIPLEXOR_NTLM = 'MULTIPLEXOR_NTLM' |
40 | 43 | MULTIPLEXOR_SSL_KERBEROS = 'MULTIPLEXOR_SSL_KERBEROS' |
56 | 59 | LDAPAuthProtocol.KERBEROS_CCACHE , |
57 | 60 | LDAPAuthProtocol.KERBEROS_KEYTAB , |
58 | 61 | LDAPAuthProtocol.KERBEROS_KIRBI , |
62 | LDAPAuthProtocol.KERBEROS_PFX , | |
63 | LDAPAuthProtocol.KERBEROS_PEM , | |
64 | LDAPAuthProtocol.KERBEROS_CERTSTORE , | |
59 | 65 | LDAPAuthProtocol.SSPI_NTLM , |
60 | 66 | LDAPAuthProtocol.SSPI_KERBEROS, |
61 | 67 | LDAPAuthProtocol.MULTIPLEXOR_KERBEROS, |
76 | 82 | LDAPAuthProtocol.KERBEROS_CCACHE , |
77 | 83 | LDAPAuthProtocol.KERBEROS_KEYTAB , |
78 | 84 | LDAPAuthProtocol.KERBEROS_KIRBI , |
85 | LDAPAuthProtocol.KERBEROS_PFX , | |
86 | LDAPAuthProtocol.KERBEROS_PEM , | |
87 | LDAPAuthProtocol.KERBEROS_CERTSTORE , | |
79 | 88 | ] |
80 | 89 | |
81 | 90 | class MSLDAPCredential: |
97 | 106 | :param encrypt: Use protocol-level encryption. Doesnt work on LDAPS |
98 | 107 | :type encrypt: bool |
99 | 108 | """ |
100 | def __init__(self, domain=None, username= None, password = None, auth_method = None, settings = None, etypes = None, encrypt = False): | |
109 | def __init__(self, domain=None, username= None, password = None, auth_method = None, settings = None, etypes = None, encrypt = False, altname = None, altdomain = None): | |
101 | 110 | self.auth_method = auth_method |
102 | 111 | self.domain = domain |
103 | 112 | self.username = username |
107 | 116 | self.settings = settings |
108 | 117 | self.etypes = etypes |
109 | 118 | self.encrypt = encrypt |
119 | self.altname = altname | |
120 | self.altdomain = altdomain | |
110 | 121 | |
111 | 122 | def get_msuser(self): |
112 | 123 | if not self.domain: |
7 | 7 | import enum |
8 | 8 | |
9 | 9 | import platform |
10 | try: | |
11 | import ssl | |
12 | except: | |
13 | if platform.system() == 'Emscripten': | |
14 | pass | |
10 | import ssl | |
11 | ||
15 | 12 | |
16 | 13 | class LDAPProtocol(enum.Enum): |
17 | 14 | TCP = 'TCP' |
39 | 36 | :type ldap_query_page_size: int |
40 | 37 | :param ldap_query_ratelimit: rate limit of paged queries. This will cause a sleep (in seconds) between fetching of each page of the query |
41 | 38 | :type ldap_query_ratelimit: float |
39 | :param dc_ip: Ip address of the kerberos server (if kerberos is used) | |
40 | :type dc_ip: str | |
42 | 41 | """ |
43 | def __init__(self, host, port = 389, proto = LDAPProtocol.TCP, tree = None, proxy = None, timeout = 10, ldap_query_page_size = 1000, ldap_query_ratelimit = 0): | |
42 | def __init__(self, host, port = 389, proto = LDAPProtocol.TCP, tree = None, proxy = None, timeout = 10, ldap_query_page_size = 1000, ldap_query_ratelimit = 0, dc_ip:str = None): | |
44 | 43 | self.proto = proto |
45 | 44 | self.host = host |
46 | 45 | self.tree = tree |
47 | 46 | self.port = port |
48 | 47 | self.proxy = proxy |
49 | 48 | self.timeout = timeout |
50 | self.dc_ip = None | |
49 | self.dc_ip = dc_ip | |
51 | 50 | self.serverip = None |
52 | 51 | self.domain = None |
53 | 52 | self.sslctx = None |
9 | 9 | import getpass |
10 | 10 | import base64 |
11 | 11 | import enum |
12 | import copy | |
12 | 13 | from urllib.parse import urlparse, parse_qs |
13 | 14 | |
14 | 15 | from msldap.commons.credential import MSLDAPCredential, LDAPAuthProtocol, MSLDAP_KERBEROS_PROTOCOLS |
92 | 93 | ldap://TEST\\victim:[email protected]/DC=test,DC=corp/?timeout=99&proxytype=socks5&proxyhost=127.0.0.1&proxyport=1080&proxytimeout=44 |
93 | 94 | """ |
94 | 95 | |
95 | def __init__(self, url): | |
96 | def __init__(self, url, credential:MSLDAPCredential = None, target:MSLDAPTarget = None ): | |
97 | self.credential = credential | |
98 | self.target = target | |
96 | 99 | self.url = url |
97 | 100 | self.ldap_scheme = None |
98 | 101 | self.auth_scheme = None |
103 | 106 | self.encrypt = False |
104 | 107 | self.auth_settings = {} |
105 | 108 | self.etypes = None |
109 | self.altname = None | |
110 | self.altdomain = None | |
106 | 111 | |
107 | 112 | self.ldap_proto = None |
108 | 113 | self.ldap_host = None |
117 | 122 | |
118 | 123 | self.__pwpreprocess = None |
119 | 124 | |
120 | self.parse() | |
125 | if url is not None: | |
126 | self.parse() | |
121 | 127 | |
122 | 128 | |
123 | 129 | def get_credential(self): |
127 | 133 | :return: Credential object |
128 | 134 | :rtype: :class:`MSLDAPCredential` |
129 | 135 | """ |
136 | if self.credential is not None: | |
137 | return copy.deepcopy(self.credential) | |
130 | 138 | t = MSLDAPCredential( |
131 | 139 | domain=self.domain, |
132 | 140 | username=self.username, |
133 | 141 | password = self.password, |
134 | 142 | auth_method=self.auth_scheme, |
135 | settings = self.auth_settings | |
143 | settings = self.auth_settings, | |
144 | altname=self.altname, | |
145 | altdomain=self.altdomain | |
136 | 146 | ) |
137 | 147 | t.encrypt = self.encrypt |
138 | 148 | t.etypes = self.etypes |
146 | 156 | :return: Target object |
147 | 157 | :rtype: :class:`MSLDAPTarget` |
148 | 158 | """ |
159 | if self.target is not None: | |
160 | return copy.deepcopy(self.target) | |
161 | ||
149 | 162 | target = MSLDAPTarget( |
150 | 163 | self.ldap_host, |
151 | 164 | port = self.ldap_port, |
160 | 173 | target.proxy = self.proxy |
161 | 174 | target.serverip = self.serverip |
162 | 175 | return target |
176 | ||
177 | def __str__(self): | |
178 | t = '==== MSLDAPURLDecoder ====\r\n' | |
179 | for k in self.__dict__: | |
180 | val = self.__dict__[k] | |
181 | if isinstance(val, enum.IntFlag): | |
182 | val = val | |
183 | elif isinstance(val, enum.Enum): | |
184 | val = val.name | |
185 | ||
186 | t += '%s: %s\r\n' % (k, str(val)) | |
187 | ||
188 | return t | |
189 | ||
163 | 190 | |
164 | 191 | def get_client(self): |
165 | 192 | """ |
342 | 369 | self.target_ratelimit = float(query[k][0]) |
343 | 370 | elif k == 'pagesize': |
344 | 371 | self.target_pagesize = int(query[k][0]) |
372 | elif k == 'altname': | |
373 | self.altname = query[k][0] | |
374 | elif k == 'altdomain': | |
375 | self.altdomain = query[k][0] | |
345 | 376 | #elif k.startswith('same'): |
346 | 377 | # self.auth_settings[k[len('same'):]] = query[k] |
347 | 378 |
14 | 14 | def datetime2timestamp(dt): |
15 | 15 | delta = dt - datetime.datetime(1601, 1, 1) |
16 | 16 | ns = int((delta / datetime.timedelta(microseconds=1)) * 10) |
17 | return ns.to_bytes(8, 'little', signed = False)⏎ | |
17 | return ns.to_bytes(8, 'little', signed = False) | |
18 | ||
19 | def wrap(s, w): | |
20 | return [s[i:i + w] for i in range(0, len(s), w)] | |
21 | ||
22 | def print_cert(cert, offset=0): | |
23 | cert = cert['tbs_certificate'] | |
24 | blanks = " " * offset | |
25 | msg = [ | |
26 | "Cert Subject: %s" % cert['subject']['common_name'], | |
27 | "Cert Serial: %s" % cert['serial_number'], | |
28 | "Cert Start: %s" % cert['validity']['not_before'], | |
29 | "Cert End: %s" % cert['validity']['not_after'], | |
30 | "Cert Issuer: %s" % cert['issuer']['common_name'], | |
31 | ] | |
32 | return "{}{}".format(blanks, "\n{}".format(blanks).join(msg)) | |
33 | ||
34 | KNOWN_SIDS = { | |
35 | "S-1-0": "Null Authority", | |
36 | "S-1-0-0": "Nobody", | |
37 | "S-1-1": "World Authority", | |
38 | "S-1-1-0": "Everyone", | |
39 | "S-1-2": "Local Authority", | |
40 | "S-1-2-0": "Local", | |
41 | "S-1-3": "Creator Authority", | |
42 | "S-1-3-0": "Creator Owner", | |
43 | "S-1-3-1": "Creator Group", | |
44 | "S-1-3-4": "Owner Rights", | |
45 | "S-1-4": "Non-unique Authority", | |
46 | "S-1-5": "NT Authority", | |
47 | "S-1-5-1": "Dialup", | |
48 | "S-1-5-2": "Network", | |
49 | "S-1-5-3": "Batch", | |
50 | "S-1-5-4": "Interactive", | |
51 | "S-1-5-5-X-Y": "Logon Session", | |
52 | "S-1-5-6": "Service", | |
53 | "S-1-5-7": "Anonymous", | |
54 | "S-1-5-9": "Enterprise Domain Controllers", | |
55 | "S-1-5-10": "Principal Self", | |
56 | "S-1-5-11": "Authenticated Users", | |
57 | "S-1-5-12": "Restricted Code", | |
58 | "S-1-5-13": "Terminal Server Users", | |
59 | "S-1-5-14": "Remote Interactive Logon", | |
60 | "S-1-5-17": "IUSR", | |
61 | "S-1-5-18": "Local System", | |
62 | "S-1-5-19": "NT Authority Local Service", | |
63 | "S-1-5-20": "NT Authority Network Service", | |
64 | "S-1-5-32-544": "Administrators", | |
65 | "S-1-5-32-545": "Users", | |
66 | "S-1-5-32-546": "Guests", | |
67 | "S-1-5-32-547": "Power Users", | |
68 | "S-1-5-32-548": "Account Operators", | |
69 | "S-1-5-32-549": "Server Operators", | |
70 | "S-1-5-32-550": "Print Operators", | |
71 | "S-1-5-32-551": "Backup Operators", | |
72 | "S-1-5-32-552": "Replicators", | |
73 | "S-1-5-32-582": "Storage Replica Administrators", | |
74 | "S-1-5-64-10": "NTLM Authentication", | |
75 | "S-1-5-64-14": "SChannel Authentication", | |
76 | "S-1-5-64-21": "Digest Authentication", | |
77 | "S-1-5-80": "NT Service", | |
78 | } |
6 | 6 | protocolOp, AuthenticationChoice, SaslCredentials, \ |
7 | 7 | SearchRequest, AttributeDescription, Filter, Filters, \ |
8 | 8 | Controls, Control, SearchControlValue, AddRequest, \ |
9 | ModifyRequest, DelRequest | |
9 | ModifyRequest, DelRequest, ExtendedRequest, ExtendedResponse | |
10 | 10 | |
11 | 11 | from msldap.protocol.utils import calcualte_length |
12 | 12 | from msldap.protocol.typeconversion import convert_result, convert_attributes, encode_attributes, encode_changes |
22 | 22 | from minikerberos.gssapi.channelbindings import ChannelBindingsStruct |
23 | 23 | |
24 | 24 | class MSLDAPClientConnection: |
25 | def __init__(self, target, creds): | |
26 | if target is None: | |
27 | raise Exception('Target cant be none!') | |
25 | def __init__(self, target, creds, auth=None): | |
28 | 26 | self.target = target |
29 | 27 | self.creds = creds |
30 | self.auth = AuthenticatorBuilder(self.creds, self.target).build() | |
28 | if auth is not None: | |
29 | self.auth = auth | |
30 | else: | |
31 | self.auth = AuthenticatorBuilder(self.creds, self.target).build() | |
31 | 32 | self.connected = False |
32 | 33 | self.bind_ok = False |
33 | 34 | self.__sign_messages = False |
54 | 55 | try: |
55 | 56 | while True: |
56 | 57 | message_data, err = await self.network.in_queue.get() |
58 | if message_data is None and err is None: | |
59 | return | |
60 | ||
57 | 61 | if err is not None: |
58 | 62 | logger.debug('Client terminating bc __handle_incoming got an error!') |
59 | 63 | raise err |
206 | 210 | |
207 | 211 | logger.debug('Disconnecting!') |
208 | 212 | self.bind_ok = False |
213 | await self.network.in_queue.put((None,None)) | |
214 | await self.network.out_queue.put(None) | |
215 | await asyncio.sleep(0) | |
216 | ||
217 | if self.network is not None: | |
218 | await self.network.terminate() | |
219 | ||
209 | 220 | if self.handle_incoming_task is not None: |
210 | 221 | self.handle_incoming_task.cancel() |
211 | if self.network is not None: | |
212 | await self.network.terminate() | |
213 | 222 | |
214 | 223 | |
215 | 224 | def __bind_success(self): |
419 | 428 | else: |
420 | 429 | raise Exception('Not implemented authentication method: %s' % self.creds.auth_method.name) |
421 | 430 | except Exception as e: |
431 | await self.disconnect() | |
422 | 432 | return False, e |
423 | 433 | |
424 | 434 | async def add(self, entry, attributes): |
704 | 714 | except Exception as e: |
705 | 715 | yield (None, e) |
706 | 716 | |
717 | async def whoami(self): | |
718 | if self.status != MSLDAPClientStatus.RUNNING: | |
719 | return None, Exception('Connection not running! Probably encountered an error') | |
720 | ||
721 | ext = { | |
722 | 'requestName': b'1.3.6.1.4.1.4203.1.11.3', | |
723 | } | |
724 | br = { 'extendedReq' : ExtendedRequest(ext)} | |
725 | msg = { 'protocolOp' : protocolOp(br)} | |
726 | ||
727 | msg_id = await self.send_message(msg) | |
728 | res = await self.recv_message(msg_id) | |
729 | res = res[0] | |
730 | if isinstance(res, Exception): | |
731 | return None, res | |
732 | if res.native['protocolOp']['resultCode'] != 'success': | |
733 | return False, LDAPBindException( | |
734 | res['protocolOp']['resultCode'], | |
735 | res['protocolOp']['diagnosticMessage'] | |
736 | ) | |
737 | return res.native['protocolOp']['responseValue'].decode(), None | |
738 | ||
739 | ||
707 | 740 | |
708 | 741 | async def get_serverinfo(self): |
709 | 742 | if self.status != MSLDAPClientStatus.RUNNING: |
0 | """ | |
1 | The idea here is to offer compatibility with 3rd party libraries by extending wrappers for ech encryption mode | |
2 | This is needed because the pure python implementation for encryption and hashing algorithms are quite slow | |
3 | ||
4 | currently it's not the perfect wrapper, needs to be extended | |
5 | """ | |
6 | ||
7 | from msldap.crypto.BASE import symmetricBASE, cipherMODE | |
8 | from msldap.crypto.pure.AES import AESModeOfOperationECB, AESModeOfOperationCBC, AESModeOfOperationCTR | |
9 | try: | |
10 | from Crypto.Cipher import AES as _pyCryptoAES | |
11 | except: | |
12 | pass | |
13 | ||
14 | try: | |
15 | from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes | |
16 | from cryptography.hazmat.primitives import padding | |
17 | from cryptography.hazmat.backends import default_backend | |
18 | except: | |
19 | pass | |
20 | ||
21 | class pureAES(symmetricBASE): | |
22 | def __init__(self, key, mode = cipherMODE.ECB, IV = None, pad = None, padMode = None): | |
23 | self.key = key | |
24 | self.mode = mode | |
25 | self.IV = IV | |
26 | self.pad = pad | |
27 | self.padMode = padMode | |
28 | ||
29 | symmetricBASE.__init__(self) | |
30 | ||
31 | def setup_cipher(self): | |
32 | if self.mode == cipherMODE.ECB: | |
33 | self._cipher = AESModeOfOperationECB(self.key) | |
34 | elif self.mode == cipherMODE.CBC: | |
35 | self._cipher = AESModeOfOperationCBC(self.key, iv = self.IV) | |
36 | elif self.mode == cipherMODE.CTR: | |
37 | self._cipher = AESModeOfOperationCTR(self.key, iv = self.IV) | |
38 | else: | |
39 | raise Exception('Unknown cipher mode!') | |
40 | ||
41 | def encrypt(self, data): | |
42 | return self._cipher.encrypt(data) | |
43 | def decrypt(self, data): | |
44 | return self._cipher.decrypt(data) | |
45 | ||
46 | class pyCryptoAES(symmetricBASE): | |
47 | def __init__(self, key, mode = cipherMODE.ECB, IV = None, pad = None, padMode = None): | |
48 | self.key = key | |
49 | self.mode = mode | |
50 | self.IV = IV | |
51 | self.pad = pad | |
52 | self.padMode = padMode | |
53 | ||
54 | symmetricBASE.__init__(self) | |
55 | ||
56 | def setup_cipher(self): | |
57 | if self.mode == cipherMODE.ECB: | |
58 | self._cipher = _pyCryptoAES.new(self.key, _pyCryptoAES.MODE_ECB) | |
59 | ||
60 | elif self.mode == cipherMODE.CBC: | |
61 | self._cipher = _pyCryptoAES.new(self.key, _pyCryptoAES.MODE_CBC, self.IV) | |
62 | elif self.mode == cipherMODE.CTR: | |
63 | self._cipher = _pyCryptoAES.new(self.key, _pyCryptoAES.MODE_CTR, self.IV) | |
64 | else: | |
65 | raise Exception('Unknown cipher mode!') | |
66 | ||
67 | def encrypt(self, data): | |
68 | return self._cipher.encrypt(data) | |
69 | def decrypt(self, data): | |
70 | return self._cipher.decrypt(data) | |
71 | ||
72 | class cryptographyAES(symmetricBASE): | |
73 | def __init__(self, key, mode = cipherMODE.ECB, IV = None, pad = None, padMode = None): | |
74 | self.IV = IV | |
75 | #the python cryptography module sets the IV in the operational mode!!! | |
76 | if mode == cipherMODE.ECB: | |
77 | self.IV = modes.ECB() | |
78 | elif mode == cipherMODE.CBC: | |
79 | self.IV = modes.CBC(IV) | |
80 | elif mode == cipherMODE.CBC: | |
81 | self.IV = modes.CTR(IV) | |
82 | else: | |
83 | raise Exception('Unknown cipher mode!') | |
84 | ||
85 | self.key = key | |
86 | ||
87 | """ TODO padding | |
88 | if self.padMode is not None: | |
89 | """ | |
90 | ||
91 | self.encryptor = None | |
92 | self.decryptor = None | |
93 | symmetricBASE.__init__(self) | |
94 | ||
95 | def setup_cipher(self): | |
96 | algorithm = algorithms.AES(self.key) | |
97 | self._cipher = Cipher(algorithm, mode=self.IV, backend=default_backend()) | |
98 | self.encryptor = self._cipher.encryptor() | |
99 | self.decryptor = self._cipher.decryptor() | |
100 | ||
101 | def encrypt(self, data): | |
102 | return self.encryptor.update(data) | |
103 | ||
104 | ||
105 | def decrypt(self, data): | |
106 | return self.decryptor.update(data) |
0 | from abc import ABC, abstractmethod | |
1 | import enum | |
2 | ||
3 | class cipherMODE(enum.Enum): | |
4 | ECB = enum.auto() | |
5 | CBC = enum.auto() | |
6 | CTR = enum.auto() | |
7 | ||
8 | class symmetricBASE(): | |
9 | def __init__(self): | |
10 | self._cipher = None | |
11 | self.setup_cipher() | |
12 | ||
13 | @abstractmethod | |
14 | def setup_cipher(self): | |
15 | #create the hash object here | |
16 | pass | |
17 | ||
18 | @abstractmethod | |
19 | def encrypt(self, data): | |
20 | pass | |
21 | ||
22 | @abstractmethod | |
23 | def decrypt(self): | |
24 | pass | |
25 | ||
26 | class hashBASE(): | |
27 | def __init__(self, data): | |
28 | self._hash = None | |
29 | self.setup_hash() | |
30 | ||
31 | if data is not None: | |
32 | self._hash.update(data) | |
33 | ||
34 | @abstractmethod | |
35 | def setup_hash(self): | |
36 | #create the hash object here | |
37 | pass | |
38 | ||
39 | @abstractmethod | |
40 | def update(self, data): | |
41 | pass | |
42 | ||
43 | @abstractmethod | |
44 | def digest(self): | |
45 | pass | |
46 | ||
47 | @abstractmethod | |
48 | def hexdigest(self): | |
49 | pass | |
50 | ||
51 | class hmacBASE(): | |
52 | def __init__(self, key): | |
53 | self._key = key | |
54 | self._hash = None | |
55 | self.setup_hash() | |
56 | ||
57 | @abstractmethod | |
58 | def setup_hash(self): | |
59 | #create the hash object here | |
60 | pass | |
61 | ||
62 | @abstractmethod | |
63 | def update(self, data): | |
64 | pass | |
65 | ||
66 | @abstractmethod | |
67 | def digest(self): | |
68 | pass | |
69 | ||
70 | @abstractmethod | |
71 | def hexdigest(self): | |
72 | pass⏎ |
0 | """ | |
1 | The idea here is to offer compatibility with 3rd party libraries by extending wrappers for ech encryption mode | |
2 | This is needed because the pure python implementation for encryption and hashing algorithms are quite slow | |
3 | ||
4 | currently it's not the perfect wrapper, needs to be extended | |
5 | """ | |
6 | ||
7 | from msldap.crypto.BASE import symmetricBASE, cipherMODE | |
8 | import msldap.crypto.pure.DES.DES as _pyDES | |
9 | try: | |
10 | from Crypto.Cipher import DES as _pyCryptoDES | |
11 | except: | |
12 | pass | |
13 | ||
14 | try: | |
15 | from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes | |
16 | from cryptography.hazmat.backends import default_backend | |
17 | except: | |
18 | pass | |
19 | ||
20 | ||
21 | # from impacket | |
22 | def expand_DES_key(key): | |
23 | # Expand the key from a 7-byte password key into a 8-byte DES key | |
24 | key = key[:7] | |
25 | key += b'\x00'*(7-len(key)) | |
26 | s = (((key[0] >> 1) & 0x7f) << 1).to_bytes(1, byteorder = 'big') | |
27 | s += (((key[0] & 0x01) << 6 | ((key[1] >> 2) & 0x3f)) << 1).to_bytes(1, byteorder = 'big') | |
28 | s += (((key[1] & 0x03) << 5 | ((key[2] >> 3) & 0x1f)) << 1).to_bytes(1, byteorder = 'big') | |
29 | s += (((key[2] & 0x07) << 4 | ((key[3] >> 4) & 0x0f)) << 1).to_bytes(1, byteorder = 'big') | |
30 | s += (((key[3] & 0x0f) << 3 | ((key[4] >> 5) & 0x07)) << 1).to_bytes(1, byteorder = 'big') | |
31 | s += (((key[4] & 0x1f) << 2 | ((key[5] >> 6) & 0x03)) << 1).to_bytes(1, byteorder = 'big') | |
32 | s += (((key[5] & 0x3f) << 1 | ((key[6] >> 7) & 0x01)) << 1).to_bytes(1, byteorder = 'big') | |
33 | s += ( (key[6] & 0x7f) << 1).to_bytes(1, byteorder = 'big') | |
34 | return s | |
35 | # | |
36 | ||
37 | class pureDES(symmetricBASE): | |
38 | def __init__(self, key, mode = cipherMODE.ECB, IV = None): | |
39 | self.key = key | |
40 | if len(key) == 7: | |
41 | self.key = expand_DES_key(key) | |
42 | ||
43 | self.mode = mode | |
44 | self.IV = IV | |
45 | symmetricBASE.__init__(self) | |
46 | ||
47 | def setup_cipher(self): | |
48 | if self.mode == cipherMODE.ECB: | |
49 | mode = _pyDES.ECB | |
50 | elif self.mode == cipherMODE.CBC: | |
51 | mode = _pyDES.CBC | |
52 | else: | |
53 | raise Exception('Unknown cipher mode!') | |
54 | ||
55 | self._cipher = _pyDES.des(self.key, mode, self.IV) | |
56 | ||
57 | def encrypt(self, data): | |
58 | return self._cipher.encrypt(data) | |
59 | def decrypt(self, data): | |
60 | return self._cipher.decrypt(data) | |
61 | ||
62 | ||
63 | ||
64 | class pyCryptoDES(symmetricBASE): | |
65 | def __init__(self, key, mode = cipherMODE.ECB, IV = None): | |
66 | self.key = key | |
67 | if len(key) == 7: | |
68 | self.key = __expand_DES_key(key) | |
69 | ||
70 | self.mode = mode | |
71 | self.IV = IV | |
72 | symmetricBASE.__init__(self) | |
73 | ||
74 | def setup_cipher(self): | |
75 | if self.mode == cipherMODE.ECB: | |
76 | self._cipher = _pyCryptoDES.new(self.key) | |
77 | elif self.mode == cipherMODE.CBC: | |
78 | self._cipher = _pyCryptoDES.new(self.key, _pyCryptoDES.MODE_CBC, self.IV) | |
79 | else: | |
80 | raise Exception('Unknown cipher mode!') | |
81 | ||
82 | ||
83 | ||
84 | def encrypt(self, data): | |
85 | return self._cipher.encrypt(data) | |
86 | def decrypt(self, data): | |
87 | return self._cipher.decrypt(data) | |
88 |
0 | #!/usr/bin/env python3 | |
1 | # -*- coding: utf-8 -*- | |
2 | # | |
3 | # Copyright © 2019 James Seo <[email protected]> (github.com/kangtastic). | |
4 | # | |
5 | # This file is released under the WTFPL, version 2 (wtfpl.net). | |
6 | # | |
7 | # md4.py: An implementation of the MD4 hash algorithm in pure Python 3. | |
8 | # | |
9 | # Description: Zounds! Yet another rendition of pseudocode from RFC1320! | |
10 | # Bonus points for the algorithm literally being from 1992. | |
11 | # | |
12 | # Usage: Why would anybody use this? This is self-rolled crypto, and | |
13 | # self-rolled *obsolete* crypto at that. DO NOT USE if you need | |
14 | # something "performant" or "secure". :P | |
15 | # | |
16 | # Anyway, from the command line: | |
17 | # | |
18 | # $ ./md4.py [messages] | |
19 | # | |
20 | # where [messages] are some strings to be hashed. | |
21 | # | |
22 | # In Python, use similarly to hashlib (not that it even has MD4): | |
23 | # | |
24 | # from .md4 import MD4 | |
25 | # | |
26 | # digest = MD4("BEES").hexdigest() | |
27 | # | |
28 | # print(digest) # "501af1ef4b68495b5b7e37b15b4cda68" | |
29 | # | |
30 | # | |
31 | # Sample console output: | |
32 | # | |
33 | # Testing the MD4 class. | |
34 | # | |
35 | # Message: b'' | |
36 | # Expected: 31d6cfe0d16ae931b73c59d7e0c089c0 | |
37 | # Actual: 31d6cfe0d16ae931b73c59d7e0c089c0 | |
38 | # | |
39 | # Message: b'The quick brown fox jumps over the lazy dog' | |
40 | # Expected: 1bee69a46ba811185c194762abaeae90 | |
41 | # Actual: 1bee69a46ba811185c194762abaeae90 | |
42 | # | |
43 | # Message: b'BEES' | |
44 | # Expected: 501af1ef4b68495b5b7e37b15b4cda68 | |
45 | # Actual: 501af1ef4b68495b5b7e37b15b4cda68 | |
46 | # | |
47 | import struct | |
48 | ||
49 | ||
50 | class MD4: | |
51 | """An implementation of the MD4 hash algorithm.""" | |
52 | ||
53 | width = 32 | |
54 | mask = 0xFFFFFFFF | |
55 | ||
56 | # Unlike, say, SHA-1, MD4 uses little-endian. Fascinating! | |
57 | h = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476] | |
58 | ||
59 | def __init__(self, msg=None): | |
60 | """:param ByteString msg: The message to be hashed.""" | |
61 | if msg is None: | |
62 | msg = b"" | |
63 | ||
64 | self.msg = msg | |
65 | ||
66 | # Pre-processing: Total length is a multiple of 512 bits. | |
67 | ml = len(msg) * 8 | |
68 | msg += b"\x80" | |
69 | msg += b"\x00" * (-(len(msg) + 8) % 64) | |
70 | msg += struct.pack("<Q", ml) | |
71 | ||
72 | # Process the message in successive 512-bit chunks. | |
73 | self._process([msg[i : i + 64] for i in range(0, len(msg), 64)]) | |
74 | ||
75 | def __repr__(self): | |
76 | if self.msg: | |
77 | return f"{self.__class__.__name__}({self.msg:s})" | |
78 | return f"{self.__class__.__name__}()" | |
79 | ||
80 | def __str__(self): | |
81 | return self.hexdigest() | |
82 | ||
83 | def __eq__(self, other): | |
84 | return self.h == other.h | |
85 | ||
86 | def bytes(self): | |
87 | """:return: The final hash value as a `bytes` object.""" | |
88 | return struct.pack("<4L", *self.h) | |
89 | ||
90 | def hexbytes(self): | |
91 | """:return: The final hash value as hexbytes.""" | |
92 | return self.hexdigest().encode | |
93 | ||
94 | def hexdigest(self): | |
95 | """:return: The final hash value as a hexstring.""" | |
96 | return "".join(f"{value:02x}" for value in self.bytes()) | |
97 | ||
98 | def digest(self): | |
99 | return self.bytes() | |
100 | ||
101 | def _process(self, chunks): | |
102 | for chunk in chunks: | |
103 | X, h = list(struct.unpack("<16I", chunk)), self.h.copy() | |
104 | ||
105 | # Round 1. | |
106 | Xi = [3, 7, 11, 19] | |
107 | for n in range(16): | |
108 | i, j, k, l = map(lambda x: x % 4, range(-n, -n + 4)) | |
109 | K, S = n, Xi[n % 4] | |
110 | hn = h[i] + MD4.F(h[j], h[k], h[l]) + X[K] | |
111 | h[i] = MD4.lrot(hn & MD4.mask, S) | |
112 | ||
113 | # Round 2. | |
114 | Xi = [3, 5, 9, 13] | |
115 | for n in range(16): | |
116 | i, j, k, l = map(lambda x: x % 4, range(-n, -n + 4)) | |
117 | K, S = n % 4 * 4 + n // 4, Xi[n % 4] | |
118 | hn = h[i] + MD4.G(h[j], h[k], h[l]) + X[K] + 0x5A827999 | |
119 | h[i] = MD4.lrot(hn & MD4.mask, S) | |
120 | ||
121 | # Round 3. | |
122 | Xi = [3, 9, 11, 15] | |
123 | Ki = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15] | |
124 | for n in range(16): | |
125 | i, j, k, l = map(lambda x: x % 4, range(-n, -n + 4)) | |
126 | K, S = Ki[n], Xi[n % 4] | |
127 | hn = h[i] + MD4.H(h[j], h[k], h[l]) + X[K] + 0x6ED9EBA1 | |
128 | h[i] = MD4.lrot(hn & MD4.mask, S) | |
129 | ||
130 | self.h = [((v + n) & MD4.mask) for v, n in zip(self.h, h)] | |
131 | ||
132 | @staticmethod | |
133 | def F(x, y, z): | |
134 | return (x & y) | (~x & z) | |
135 | ||
136 | @staticmethod | |
137 | def G(x, y, z): | |
138 | return (x & y) | (x & z) | (y & z) | |
139 | ||
140 | @staticmethod | |
141 | def H(x, y, z): | |
142 | return x ^ y ^ z | |
143 | ||
144 | @staticmethod | |
145 | def lrot(value, n): | |
146 | lbits, rbits = (value << n) & MD4.mask, value >> (MD4.width - n) | |
147 | return lbits | rbits | |
148 | ||
149 | ||
150 | def main(): | |
151 | # Import is intentionally delayed. | |
152 | import sys | |
153 | ||
154 | if len(sys.argv) > 1: | |
155 | messages = [msg.encode() for msg in sys.argv[1:]] | |
156 | for message in messages: | |
157 | print(MD4(message).hexdigest()) | |
158 | else: | |
159 | messages = [b"", b"The quick brown fox jumps over the lazy dog", b"BEES"] | |
160 | known_hashes = [ | |
161 | "31d6cfe0d16ae931b73c59d7e0c089c0", | |
162 | "1bee69a46ba811185c194762abaeae90", | |
163 | "501af1ef4b68495b5b7e37b15b4cda68", | |
164 | ] | |
165 | ||
166 | print("Testing the MD4 class.") | |
167 | print() | |
168 | ||
169 | for message, expected in zip(messages, known_hashes): | |
170 | print("Message: ", message) | |
171 | print("Expected:", expected) | |
172 | print("Actual: ", MD4(message).hexdigest()) | |
173 | print() | |
174 | ||
175 | ||
176 | if __name__ == "__main__": | |
177 | try: | |
178 | main() | |
179 | except KeyboardInterrupt: | |
180 | pass |
0 | """ | |
1 | The idea here is to offer compatibility with 3rd party libraries by extending wrappers for ech encryption mode | |
2 | This is needed because the pure python implementation for encryption and hashing algorithms are quite slow | |
3 | ||
4 | currently it's not the perfect wrapper, needs to be extended | |
5 | """ | |
6 | ||
7 | from msldap.crypto.BASE import symmetricBASE, cipherMODE | |
8 | from msldap.crypto.pure.RC4.RC4 import RC4 as _pureRC4 | |
9 | try: | |
10 | from Crypto.Cipher import ARC4 as _pyCryptoRC4 | |
11 | except Exception as e: | |
12 | #print(e) | |
13 | pass | |
14 | ||
15 | try: | |
16 | from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes | |
17 | from cryptography.hazmat.backends import default_backend | |
18 | except: | |
19 | pass | |
20 | ||
21 | class pureRC4(symmetricBASE): | |
22 | def __init__(self, key): | |
23 | if not isinstance(key, bytes): | |
24 | raise Exception('Key needs to be bytes!') | |
25 | self.key = key | |
26 | symmetricBASE.__init__(self) | |
27 | ||
28 | def setup_cipher(self): | |
29 | self._cipher = _pureRC4(self.key) | |
30 | ||
31 | def encrypt(self, data): | |
32 | return self._cipher.encrypt(data) | |
33 | def decrypt(self, data): | |
34 | return self._cipher.decrypt(data) | |
35 | ||
36 | class pyCryptoRC4(symmetricBASE): | |
37 | def __init__(self, key): | |
38 | self.key = key | |
39 | symmetricBASE.__init__(self) | |
40 | ||
41 | def setup_cipher(self): | |
42 | self._cipher = _pyCryptoRC4.new(self.key) | |
43 | ||
44 | def encrypt(self, data): | |
45 | return self._cipher.encrypt(data) | |
46 | def decrypt(self, data): | |
47 | return self._cipher.decrypt(data) | |
48 | ||
49 | class cryptographyRC4(symmetricBASE): | |
50 | def __init__(self, key): | |
51 | if not isinstance(key, bytes): | |
52 | raise Exception('Key needs to be bytes!') | |
53 | self.key = key | |
54 | self.encryptor = None | |
55 | self.decryptor = None | |
56 | symmetricBASE.__init__(self) | |
57 | ||
58 | def setup_cipher(self): | |
59 | algorithm = algorithms.ARC4(self.key) | |
60 | self._cipher = Cipher(algorithm, mode=None, backend=default_backend()) | |
61 | self.encryptor = self._cipher.encryptor() | |
62 | self.decryptor = self._cipher.decryptor() | |
63 | ||
64 | def encrypt(self, data): | |
65 | return self.encryptor.update(data) | |
66 | def decrypt(self, data): | |
67 | return self.decryptor.update(data)⏎ |
0 | """ | |
1 | The idea here is to offer compatibility with 3rd party libraries by extending wrappers for ech encryption mode | |
2 | This is needed because the pure python implementation for encryption and hashing algorithms are quite slow | |
3 | ||
4 | currently it's not the perfect wrapper, needs to be extended | |
5 | """ | |
6 | ||
7 | from msldap.crypto.BASE import symmetricBASE, cipherMODE, padMode | |
8 | import msldap.crypto.pure.DES.DES as _pyDES | |
9 | try: | |
10 | from Crypto.Cipher import DES3 as _pyCryptoDES3 | |
11 | except: | |
12 | pass | |
13 | ||
14 | try: | |
15 | from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes | |
16 | from cryptography.hazmat.primitives import padding | |
17 | from cryptography.hazmat.backends import default_backend | |
18 | except: | |
19 | pass | |
20 | ||
21 | class pureTDES(symmetricBASE): | |
22 | def __init__(self, key, mode = cipherMODE.ECB, IV = None, pad = None, padMode = None): | |
23 | symmetricBASE.__init__(self) | |
24 | if not isinstance(key, bytes): | |
25 | raise Exception('Key needs to be bytes!') | |
26 | ||
27 | self.mode = mode | |
28 | self.IV = IV | |
29 | self.pad = pad | |
30 | self.padMode = padMode | |
31 | ||
32 | def setup_cipher(self): | |
33 | if self.mode == cipherMODE.ECB: | |
34 | mode = _pyDes.ECB | |
35 | self._cipher = _pyDES.triple_des(self.key, mode) | |
36 | elif self.mode == cipherMODE.CBC: | |
37 | mode = _pyDES.CBC | |
38 | if padMode is None: | |
39 | self._cipher = _pyDES.triple_des(self.key, mode, self.IV, self.pad, self.padmode) | |
40 | else: | |
41 | self._cipher = _pyDES.triple_des(self.key, mode, self.IV) | |
42 | else: | |
43 | raise Exception('Unknown cipher mode!') | |
44 | ||
45 | def encrypt(self, data): | |
46 | return self._cipher.encrypt(data) | |
47 | def decrypt(self, data): | |
48 | return self._cipher.decrypt(data) | |
49 | ||
50 | class pyCryptoTDES(symmetricBASE): | |
51 | def __init__(self, key, mode = cipherMODE.ECB, IV = None, pad = None, padMode = None): | |
52 | self.key = key | |
53 | self.mode = mode | |
54 | self.IV = IV | |
55 | self.pad = pad | |
56 | self.padMode = padMode | |
57 | ||
58 | symmetricBASE.__init__(self) | |
59 | ||
60 | def setup_cipher(self): | |
61 | if self.mode == cipherMODE.ECB: | |
62 | self._cipher = _pyCryptoDES3.new(self.key, _pyCryptoDES3.MODE_ECB) | |
63 | ||
64 | elif self.mode == cipherMODE.CBC: | |
65 | self._cipher = _pyCryptoDES3.new(self.key, _pyCryptoDES3.MODE_CBC, self.IV) | |
66 | else: | |
67 | raise Exception('Unknown cipher mode!') | |
68 | ||
69 | def encrypt(self, data): | |
70 | return self._cipher.encrypt(data) | |
71 | def decrypt(self, data): | |
72 | return self._cipher.decrypt(data) | |
73 | ||
74 | class cryptographyTDES(symmetricBASE): | |
75 | def __init__(self, key, mode = cipherMODE.ECB, IV = None, pad = None, padMode = None): | |
76 | if not isinstance(key, bytes): | |
77 | raise Exception('Key needs to be bytes!') | |
78 | self.IV = IV | |
79 | if mode == cipherMode.ECB: | |
80 | self.IV = modes.ECB() | |
81 | elif mode == cipherMODE.CBC: | |
82 | self.IV = modes.CBC(IV) | |
83 | else: | |
84 | raise Exception('Unknown cipher mode!') | |
85 | self.key = key | |
86 | ||
87 | """ TODO padding | |
88 | if self.padMode is not None: | |
89 | """ | |
90 | ||
91 | self.encryptor = None | |
92 | self.decryptor = None | |
93 | symmetricBASE.__init__(self) | |
94 | ||
95 | def setup_cipher(self): | |
96 | algorithm = algorithms.TripleDES(self.key) | |
97 | self._cipher = Cipher(algorithm, mode=self.IV, backend=default_backend()) | |
98 | self.encryptor = self._cipher.encryptor() | |
99 | self.decryptor = self._cipher.decryptor() | |
100 | ||
101 | def encrypt(self, data): | |
102 | return self.encryptor.update(data) | |
103 | ||
104 | ||
105 | def decrypt(self, data): | |
106 | return self.decryptor.update(data) | |
107 |
0 | import hashlib | |
1 | import hmac | |
2 | ||
3 | from msldap.crypto.BASE import hashBASE, hmacBASE | |
4 | from msldap.crypto.MD4 import MD4 | |
5 | ||
6 | class md5(hashBASE): | |
7 | def __init__(self, data = None): | |
8 | hashBASE.__init__(self, data) | |
9 | def setup_hash(self): | |
10 | self._hash = hashlib.new('md5') | |
11 | def update(self, data): | |
12 | return self._hash.update(data) | |
13 | def digest(self): | |
14 | return self._hash.digest() | |
15 | def hexdigest(self): | |
16 | return self._hash.hexdigest() | |
17 | ||
18 | #class md4(hashBASE): | |
19 | # def __init__(self, data = None): | |
20 | # hashBASE.__init__(self, data) | |
21 | # def setup_hash(self): | |
22 | # self._hash = hashlib.new('md4') | |
23 | # def update(self, data): | |
24 | # return self._hash.update(data) | |
25 | # def digest(self): | |
26 | # return self._hash.digest() | |
27 | # def hexdigest(self): | |
28 | # return self._hash.hexdigest() | |
29 | ||
30 | md4 = MD4 | |
31 | ||
32 | class hmac_md5(hmacBASE): | |
33 | def __init__(self, key): | |
34 | hmacBASE.__init__(self, key) | |
35 | def setup_hash(self): | |
36 | self._hmac = hmac.new(self._key, digestmod = hashlib.md5) | |
37 | def update(self, data): | |
38 | return self._hmac.update(data) | |
39 | def digest(self): | |
40 | return self._hmac.digest() | |
41 | def hexdigest(self): | |
42 | return self._hmac.hexdigest() | |
43 | ||
44 | class sha256(): | |
45 | def __init__(self, data = None): | |
46 | hashBASE.__init__(self, data) | |
47 | def setup_hash(self): | |
48 | self._hash = hashlib.new('sha256') | |
49 | def update(self, data): | |
50 | return self._hash.update(data) | |
51 | def digest(self): | |
52 | return self._hash.digest() | |
53 | def hexdigest(self): | |
54 | return self._hash.hexdigest() ⏎ |
0 | ||
1 | #https://github.com/ricmoo/pyaes/blob/master/pyaes/aes.py | |
2 | # The MIT License (MIT) | |
3 | # | |
4 | # Copyright (c) 2014 Richard Moore | |
5 | # | |
6 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | # of this software and associated documentation files (the "Software"), to deal | |
8 | # in the Software without restriction, including without limitation the rights | |
9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | # copies of the Software, and to permit persons to whom the Software is | |
11 | # furnished to do so, subject to the following conditions: | |
12 | # | |
13 | # The above copyright notice and this permission notice shall be included in | |
14 | # all copies or substantial portions of the Software. | |
15 | # | |
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | # THE SOFTWARE. | |
23 | ||
24 | # This is a pure-Python implementation of the AES algorithm and AES common | |
25 | # modes of operation. | |
26 | ||
27 | # See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard | |
28 | ||
29 | # Honestly, the best description of the modes of operations are the wonderful | |
30 | # diagrams on Wikipedia. They explain in moments what my words could never | |
31 | # achieve. Hence the inline documentation here is sparer than I'd prefer. | |
32 | # See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation | |
33 | ||
34 | # Also useful, PyCrypto, a crypto library implemented in C with Python bindings: | |
35 | # https://www.dlitz.net/software/pycrypto/ | |
36 | ||
37 | ||
38 | # Supported key sizes: | |
39 | # 128-bit | |
40 | # 192-bit | |
41 | # 256-bit | |
42 | ||
43 | ||
44 | # Supported modes of operation: | |
45 | # ECB - Electronic Codebook | |
46 | # CBC - Cipher-Block Chaining | |
47 | # CFB - Cipher Feedback | |
48 | # OFB - Output Feedback | |
49 | # CTR - Counter | |
50 | ||
51 | ||
52 | # See the README.md for API details and general information. | |
53 | ||
54 | ||
55 | import copy | |
56 | import struct | |
57 | ||
58 | __all__ = ["AES", "AESModeOfOperationCTR", "AESModeOfOperationCBC", "AESModeOfOperationCFB", | |
59 | "AESModeOfOperationECB", "AESModeOfOperationOFB", "AESModesOfOperation", "Counter"] | |
60 | ||
61 | ||
62 | def _compact_word(word): | |
63 | return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3] | |
64 | ||
65 | def _string_to_bytes(text): | |
66 | return list(ord(c) for c in text) | |
67 | ||
68 | def _bytes_to_string(binary): | |
69 | return "".join(chr(b) for b in binary) | |
70 | ||
71 | def _concat_list(a, b): | |
72 | return a + b | |
73 | ||
74 | ||
75 | # Python 3 compatibility | |
76 | try: | |
77 | xrange | |
78 | except Exception: | |
79 | xrange = range | |
80 | ||
81 | # Python 3 supports bytes, which is already an array of integers | |
82 | def _string_to_bytes(text): | |
83 | if isinstance(text, bytes): | |
84 | return text | |
85 | return [ord(c) for c in text] | |
86 | ||
87 | # In Python 3, we return bytes | |
88 | def _bytes_to_string(binary): | |
89 | return bytes(binary) | |
90 | ||
91 | # Python 3 cannot concatenate a list onto a bytes, so we bytes-ify it first | |
92 | def _concat_list(a, b): | |
93 | return a + bytes(b) | |
94 | ||
95 | ||
96 | # Based *largely* on the Rijndael implementation | |
97 | # See: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf | |
98 | class AES(object): | |
99 | '''Encapsulates the AES block cipher. | |
100 | ||
101 | You generally should not need this. Use the AESModeOfOperation classes | |
102 | below instead.''' | |
103 | ||
104 | # Number of rounds by keysize | |
105 | number_of_rounds = {16: 10, 24: 12, 32: 14} | |
106 | ||
107 | # Round constant words | |
108 | rcon = [ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ] | |
109 | ||
110 | # S-box and Inverse S-box (S is for Substitution) | |
111 | S = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ] | |
112 | Si =[ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ] | |
113 | ||
114 | # Transformations for encryption | |
115 | T1 = [ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a ] | |
116 | T2 = [ 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616 ] | |
117 | T3 = [ 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16 ] | |
118 | T4 = [ 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c ] | |
119 | ||
120 | # Transformations for decryption | |
121 | T5 = [ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742 ] | |
122 | T6 = [ 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857 ] | |
123 | T7 = [ 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8 ] | |
124 | T8 = [ 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0 ] | |
125 | ||
126 | # Transformations for decryption key expansion | |
127 | U1 = [ 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3 ] | |
128 | U2 = [ 0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697 ] | |
129 | U3 = [ 0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46 ] | |
130 | U4 = [ 0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d ] | |
131 | ||
132 | def __init__(self, key): | |
133 | ||
134 | if len(key) not in (16, 24, 32): | |
135 | raise ValueError('Invalid key size') | |
136 | ||
137 | rounds = self.number_of_rounds[len(key)] | |
138 | ||
139 | # Encryption round keys | |
140 | self._Ke = [[0] * 4 for i in xrange(rounds + 1)] | |
141 | ||
142 | # Decryption round keys | |
143 | self._Kd = [[0] * 4 for i in xrange(rounds + 1)] | |
144 | ||
145 | round_key_count = (rounds + 1) * 4 | |
146 | KC = len(key) // 4 | |
147 | ||
148 | # Convert the key into ints | |
149 | tk = [ struct.unpack('>i', key[i:i + 4])[0] for i in xrange(0, len(key), 4) ] | |
150 | ||
151 | # Copy values into round key arrays | |
152 | for i in xrange(0, KC): | |
153 | self._Ke[i // 4][i % 4] = tk[i] | |
154 | self._Kd[rounds - (i // 4)][i % 4] = tk[i] | |
155 | ||
156 | # Key expansion (fips-197 section 5.2) | |
157 | rconpointer = 0 | |
158 | t = KC | |
159 | while t < round_key_count: | |
160 | ||
161 | tt = tk[KC - 1] | |
162 | tk[0] ^= ((self.S[(tt >> 16) & 0xFF] << 24) ^ | |
163 | (self.S[(tt >> 8) & 0xFF] << 16) ^ | |
164 | (self.S[ tt & 0xFF] << 8) ^ | |
165 | self.S[(tt >> 24) & 0xFF] ^ | |
166 | (self.rcon[rconpointer] << 24)) | |
167 | rconpointer += 1 | |
168 | ||
169 | if KC != 8: | |
170 | for i in xrange(1, KC): | |
171 | tk[i] ^= tk[i - 1] | |
172 | ||
173 | # Key expansion for 256-bit keys is "slightly different" (fips-197) | |
174 | else: | |
175 | for i in xrange(1, KC // 2): | |
176 | tk[i] ^= tk[i - 1] | |
177 | tt = tk[KC // 2 - 1] | |
178 | ||
179 | tk[KC // 2] ^= (self.S[ tt & 0xFF] ^ | |
180 | (self.S[(tt >> 8) & 0xFF] << 8) ^ | |
181 | (self.S[(tt >> 16) & 0xFF] << 16) ^ | |
182 | (self.S[(tt >> 24) & 0xFF] << 24)) | |
183 | ||
184 | for i in xrange(KC // 2 + 1, KC): | |
185 | tk[i] ^= tk[i - 1] | |
186 | ||
187 | # Copy values into round key arrays | |
188 | j = 0 | |
189 | while j < KC and t < round_key_count: | |
190 | self._Ke[t // 4][t % 4] = tk[j] | |
191 | self._Kd[rounds - (t // 4)][t % 4] = tk[j] | |
192 | j += 1 | |
193 | t += 1 | |
194 | ||
195 | # Inverse-Cipher-ify the decryption round key (fips-197 section 5.3) | |
196 | for r in xrange(1, rounds): | |
197 | for j in xrange(0, 4): | |
198 | tt = self._Kd[r][j] | |
199 | self._Kd[r][j] = (self.U1[(tt >> 24) & 0xFF] ^ | |
200 | self.U2[(tt >> 16) & 0xFF] ^ | |
201 | self.U3[(tt >> 8) & 0xFF] ^ | |
202 | self.U4[ tt & 0xFF]) | |
203 | ||
204 | def encrypt(self, plaintext): | |
205 | 'Encrypt a block of plain text using the AES block cipher.' | |
206 | ||
207 | if len(plaintext) != 16: | |
208 | raise ValueError('wrong block length') | |
209 | ||
210 | rounds = len(self._Ke) - 1 | |
211 | (s1, s2, s3) = [1, 2, 3] | |
212 | a = [0, 0, 0, 0] | |
213 | ||
214 | # Convert plaintext to (ints ^ key) | |
215 | t = [(_compact_word(plaintext[4 * i:4 * i + 4]) ^ self._Ke[0][i]) for i in xrange(0, 4)] | |
216 | ||
217 | # Apply round transforms | |
218 | for r in xrange(1, rounds): | |
219 | for i in xrange(0, 4): | |
220 | a[i] = (self.T1[(t[ i ] >> 24) & 0xFF] ^ | |
221 | self.T2[(t[(i + s1) % 4] >> 16) & 0xFF] ^ | |
222 | self.T3[(t[(i + s2) % 4] >> 8) & 0xFF] ^ | |
223 | self.T4[ t[(i + s3) % 4] & 0xFF] ^ | |
224 | self._Ke[r][i]) | |
225 | t = copy.copy(a) | |
226 | ||
227 | # The last round is special | |
228 | result = [ ] | |
229 | for i in xrange(0, 4): | |
230 | tt = self._Ke[rounds][i] | |
231 | result.append((self.S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) | |
232 | result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) | |
233 | result.append((self.S[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) | |
234 | result.append((self.S[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) | |
235 | ||
236 | return result | |
237 | ||
238 | def decrypt(self, ciphertext): | |
239 | 'Decrypt a block of cipher text using the AES block cipher.' | |
240 | ||
241 | if len(ciphertext) != 16: | |
242 | raise ValueError('wrong block length') | |
243 | ||
244 | rounds = len(self._Kd) - 1 | |
245 | (s1, s2, s3) = [3, 2, 1] | |
246 | a = [0, 0, 0, 0] | |
247 | ||
248 | # Convert ciphertext to (ints ^ key) | |
249 | t = [(_compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in xrange(0, 4)] | |
250 | ||
251 | # Apply round transforms | |
252 | for r in xrange(1, rounds): | |
253 | for i in xrange(0, 4): | |
254 | a[i] = (self.T5[(t[ i ] >> 24) & 0xFF] ^ | |
255 | self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^ | |
256 | self.T7[(t[(i + s2) % 4] >> 8) & 0xFF] ^ | |
257 | self.T8[ t[(i + s3) % 4] & 0xFF] ^ | |
258 | self._Kd[r][i]) | |
259 | t = copy.copy(a) | |
260 | ||
261 | # The last round is special | |
262 | result = [ ] | |
263 | for i in xrange(0, 4): | |
264 | tt = self._Kd[rounds][i] | |
265 | result.append((self.Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) | |
266 | result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) | |
267 | result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) | |
268 | result.append((self.Si[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) | |
269 | ||
270 | return result | |
271 | ||
272 | ||
273 | class Counter(object): | |
274 | '''A counter object for the Counter (CTR) mode of operation. | |
275 | ||
276 | To create a custom counter, you can usually just override the | |
277 | increment method.''' | |
278 | ||
279 | def __init__(self, initial_value = 1): | |
280 | ||
281 | # Convert the value into an array of bytes long | |
282 | self._counter = [ ((initial_value >> i) % 256) for i in xrange(128 - 8, -1, -8) ] | |
283 | ||
284 | value = property(lambda s: s._counter) | |
285 | ||
286 | def increment(self): | |
287 | '''Increment the counter (overflow rolls back to 0).''' | |
288 | ||
289 | for i in xrange(len(self._counter) - 1, -1, -1): | |
290 | self._counter[i] += 1 | |
291 | ||
292 | if self._counter[i] < 256: break | |
293 | ||
294 | # Carry the one | |
295 | self._counter[i] = 0 | |
296 | ||
297 | # Overflow | |
298 | else: | |
299 | self._counter = [ 0 ] * len(self._counter) | |
300 | ||
301 | ||
302 | class AESBlockModeOfOperation(object): | |
303 | '''Super-class for AES modes of operation that require blocks.''' | |
304 | def __init__(self, key): | |
305 | self._aes = AES(key) | |
306 | ||
307 | def decrypt(self, ciphertext): | |
308 | raise Exception('not implemented') | |
309 | ||
310 | def encrypt(self, plaintext): | |
311 | raise Exception('not implemented') | |
312 | ||
313 | ||
314 | class AESStreamModeOfOperation(AESBlockModeOfOperation): | |
315 | '''Super-class for AES modes of operation that are stream-ciphers.''' | |
316 | ||
317 | class AESSegmentModeOfOperation(AESStreamModeOfOperation): | |
318 | '''Super-class for AES modes of operation that segment data.''' | |
319 | ||
320 | segment_bytes = 16 | |
321 | ||
322 | ||
323 | ||
324 | class AESModeOfOperationECB(AESBlockModeOfOperation): | |
325 | '''AES Electronic Codebook Mode of Operation. | |
326 | ||
327 | o Block-cipher, so data must be padded to 16 byte boundaries | |
328 | ||
329 | Security Notes: | |
330 | o This mode is not recommended | |
331 | o Any two identical blocks produce identical encrypted values, | |
332 | exposing data patterns. (See the image of Tux on wikipedia) | |
333 | ||
334 | Also see: | |
335 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29 | |
336 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.1''' | |
337 | ||
338 | ||
339 | name = "Electronic Codebook (ECB)" | |
340 | ||
341 | def encrypt(self, plaintext): | |
342 | if len(plaintext) != 16: | |
343 | raise ValueError('plaintext block must be 16 bytes') | |
344 | ||
345 | plaintext = _string_to_bytes(plaintext) | |
346 | return _bytes_to_string(self._aes.encrypt(plaintext)) | |
347 | ||
348 | def decrypt(self, ciphertext): | |
349 | if len(ciphertext) != 16: | |
350 | raise ValueError('ciphertext block must be 16 bytes') | |
351 | ||
352 | ciphertext = _string_to_bytes(ciphertext) | |
353 | return _bytes_to_string(self._aes.decrypt(ciphertext)) | |
354 | ||
355 | ||
356 | ||
357 | class AESModeOfOperationCBC(AESBlockModeOfOperation): | |
358 | '''AES Cipher-Block Chaining Mode of Operation. | |
359 | ||
360 | o The Initialization Vector (IV) | |
361 | o Block-cipher, so data must be padded to 16 byte boundaries | |
362 | o An incorrect initialization vector will only cause the first | |
363 | block to be corrupt; all other blocks will be intact | |
364 | o A corrupt bit in the cipher text will cause a block to be | |
365 | corrupted, and the next block to be inverted, but all other | |
366 | blocks will be intact. | |
367 | ||
368 | Security Notes: | |
369 | o This method (and CTR) ARE recommended. | |
370 | ||
371 | Also see: | |
372 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29 | |
373 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.2''' | |
374 | ||
375 | ||
376 | name = "Cipher-Block Chaining (CBC)" | |
377 | ||
378 | def __init__(self, key, iv = None): | |
379 | if iv is None: | |
380 | self._last_cipherblock = [ 0 ] * 16 | |
381 | elif len(iv) != 16: | |
382 | raise ValueError('initialization vector must be 16 bytes') | |
383 | else: | |
384 | self._last_cipherblock = _string_to_bytes(iv) | |
385 | ||
386 | AESBlockModeOfOperation.__init__(self, key) | |
387 | ||
388 | def encrypt(self, plaintext): | |
389 | if len(plaintext) != 16: | |
390 | raise ValueError('plaintext block must be 16 bytes') | |
391 | ||
392 | plaintext = _string_to_bytes(plaintext) | |
393 | precipherblock = [ (p ^ l) for (p, l) in zip(plaintext, self._last_cipherblock) ] | |
394 | self._last_cipherblock = self._aes.encrypt(precipherblock) | |
395 | ||
396 | return _bytes_to_string(self._last_cipherblock) | |
397 | ||
398 | def decrypt(self, ciphertext): | |
399 | if len(ciphertext) != 16: | |
400 | raise ValueError('ciphertext block must be 16 bytes') | |
401 | ||
402 | cipherblock = _string_to_bytes(ciphertext) | |
403 | plaintext = [ (p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock) ] | |
404 | self._last_cipherblock = cipherblock | |
405 | ||
406 | return _bytes_to_string(plaintext) | |
407 | ||
408 | ||
409 | ||
410 | class AESModeOfOperationCFB(AESSegmentModeOfOperation): | |
411 | '''AES Cipher Feedback Mode of Operation. | |
412 | ||
413 | o A stream-cipher, so input does not need to be padded to blocks, | |
414 | but does need to be padded to segment_size | |
415 | ||
416 | Also see: | |
417 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_.28CFB.29 | |
418 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.3''' | |
419 | ||
420 | ||
421 | name = "Cipher Feedback (CFB)" | |
422 | ||
423 | def __init__(self, key, iv, segment_size = 1): | |
424 | if segment_size == 0: segment_size = 1 | |
425 | ||
426 | if iv is None: | |
427 | self._shift_register = [ 0 ] * 16 | |
428 | elif len(iv) != 16: | |
429 | raise ValueError('initialization vector must be 16 bytes') | |
430 | else: | |
431 | self._shift_register = _string_to_bytes(iv) | |
432 | ||
433 | self._segment_bytes = segment_size | |
434 | ||
435 | AESBlockModeOfOperation.__init__(self, key) | |
436 | ||
437 | segment_bytes = property(lambda s: s._segment_bytes) | |
438 | ||
439 | def encrypt(self, plaintext): | |
440 | if len(plaintext) % self._segment_bytes != 0: | |
441 | raise ValueError('plaintext block must be a multiple of segment_size') | |
442 | ||
443 | plaintext = _string_to_bytes(plaintext) | |
444 | ||
445 | # Break block into segments | |
446 | encrypted = [ ] | |
447 | for i in xrange(0, len(plaintext), self._segment_bytes): | |
448 | plaintext_segment = plaintext[i: i + self._segment_bytes] | |
449 | xor_segment = self._aes.encrypt(self._shift_register)[:len(plaintext_segment)] | |
450 | cipher_segment = [ (p ^ x) for (p, x) in zip(plaintext_segment, xor_segment) ] | |
451 | ||
452 | # Shift the top bits out and the ciphertext in | |
453 | self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment) | |
454 | ||
455 | encrypted.extend(cipher_segment) | |
456 | ||
457 | return _bytes_to_string(encrypted) | |
458 | ||
459 | def decrypt(self, ciphertext): | |
460 | if len(ciphertext) % self._segment_bytes != 0: | |
461 | raise ValueError('ciphertext block must be a multiple of segment_size') | |
462 | ||
463 | ciphertext = _string_to_bytes(ciphertext) | |
464 | ||
465 | # Break block into segments | |
466 | decrypted = [ ] | |
467 | for i in xrange(0, len(ciphertext), self._segment_bytes): | |
468 | cipher_segment = ciphertext[i: i + self._segment_bytes] | |
469 | xor_segment = self._aes.encrypt(self._shift_register)[:len(cipher_segment)] | |
470 | plaintext_segment = [ (p ^ x) for (p, x) in zip(cipher_segment, xor_segment) ] | |
471 | ||
472 | # Shift the top bits out and the ciphertext in | |
473 | self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment) | |
474 | ||
475 | decrypted.extend(plaintext_segment) | |
476 | ||
477 | return _bytes_to_string(decrypted) | |
478 | ||
479 | ||
480 | ||
481 | class AESModeOfOperationOFB(AESStreamModeOfOperation): | |
482 | '''AES Output Feedback Mode of Operation. | |
483 | ||
484 | o A stream-cipher, so input does not need to be padded to blocks, | |
485 | allowing arbitrary length data. | |
486 | o A bit twiddled in the cipher text, twiddles the same bit in the | |
487 | same bit in the plain text, which can be useful for error | |
488 | correction techniques. | |
489 | ||
490 | Also see: | |
491 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_.28OFB.29 | |
492 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.4''' | |
493 | ||
494 | ||
495 | name = "Output Feedback (OFB)" | |
496 | ||
497 | def __init__(self, key, iv = None): | |
498 | if iv is None: | |
499 | self._last_precipherblock = [ 0 ] * 16 | |
500 | elif len(iv) != 16: | |
501 | raise ValueError('initialization vector must be 16 bytes') | |
502 | else: | |
503 | self._last_precipherblock = _string_to_bytes(iv) | |
504 | ||
505 | self._remaining_block = [ ] | |
506 | ||
507 | AESBlockModeOfOperation.__init__(self, key) | |
508 | ||
509 | def encrypt(self, plaintext): | |
510 | encrypted = [ ] | |
511 | for p in _string_to_bytes(plaintext): | |
512 | if len(self._remaining_block) == 0: | |
513 | self._remaining_block = self._aes.encrypt(self._last_precipherblock) | |
514 | self._last_precipherblock = [ ] | |
515 | precipherbyte = self._remaining_block.pop(0) | |
516 | self._last_precipherblock.append(precipherbyte) | |
517 | cipherbyte = p ^ precipherbyte | |
518 | encrypted.append(cipherbyte) | |
519 | ||
520 | return _bytes_to_string(encrypted) | |
521 | ||
522 | def decrypt(self, ciphertext): | |
523 | # AES-OFB is symetric | |
524 | return self.encrypt(ciphertext) | |
525 | ||
526 | ||
527 | ||
528 | class AESModeOfOperationCTR(AESStreamModeOfOperation): | |
529 | '''AES Counter Mode of Operation. | |
530 | ||
531 | o A stream-cipher, so input does not need to be padded to blocks, | |
532 | allowing arbitrary length data. | |
533 | o The counter must be the same size as the key size (ie. len(key)) | |
534 | o Each block independant of the other, so a corrupt byte will not | |
535 | damage future blocks. | |
536 | o Each block has a uniue counter value associated with it, which | |
537 | contributes to the encrypted value, so no data patterns are | |
538 | leaked. | |
539 | o Also known as: Counter Mode (CM), Integer Counter Mode (ICM) and | |
540 | Segmented Integer Counter (SIC | |
541 | ||
542 | Security Notes: | |
543 | o This method (and CBC) ARE recommended. | |
544 | o Each message block is associated with a counter value which must be | |
545 | unique for ALL messages with the same key. Otherwise security may be | |
546 | compromised. | |
547 | ||
548 | Also see: | |
549 | ||
550 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29 | |
551 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.5 | |
552 | and Appendix B for managing the initial counter''' | |
553 | ||
554 | ||
555 | name = "Counter (CTR)" | |
556 | ||
557 | def __init__(self, key, counter = None): | |
558 | AESBlockModeOfOperation.__init__(self, key) | |
559 | ||
560 | if counter is None: | |
561 | counter = Counter() | |
562 | ||
563 | self._counter = counter | |
564 | self._remaining_counter = [ ] | |
565 | ||
566 | def encrypt(self, plaintext): | |
567 | while len(self._remaining_counter) < len(plaintext): | |
568 | self._remaining_counter += self._aes.encrypt(self._counter.value) | |
569 | self._counter.increment() | |
570 | ||
571 | plaintext = _string_to_bytes(plaintext) | |
572 | ||
573 | encrypted = [ (p ^ c) for (p, c) in zip(plaintext, self._remaining_counter) ] | |
574 | self._remaining_counter = self._remaining_counter[len(encrypted):] | |
575 | ||
576 | return _bytes_to_string(encrypted) | |
577 | ||
578 | def decrypt(self, crypttext): | |
579 | # AES-CTR is symetric | |
580 | return self.encrypt(crypttext) | |
581 | ||
582 | ||
583 | # Simple lookup table for each mode | |
584 | AESModesOfOperation = dict( | |
585 | ctr = AESModeOfOperationCTR, | |
586 | cbc = AESModeOfOperationCBC, | |
587 | cfb = AESModeOfOperationCFB, | |
588 | ecb = AESModeOfOperationECB, | |
589 | ofb = AESModeOfOperationOFB, | |
590 | ) |
0 | #https://raw.githubusercontent.com/ricmoo/pyaes/master/pyaes/__init__.py | |
1 | # The MIT License (MIT) | |
2 | # | |
3 | # Copyright (c) 2014 Richard Moore | |
4 | # | |
5 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |
6 | # of this software and associated documentation files (the "Software"), to deal | |
7 | # in the Software without restriction, including without limitation the rights | |
8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
9 | # copies of the Software, and to permit persons to whom the Software is | |
10 | # furnished to do so, subject to the following conditions: | |
11 | # | |
12 | # The above copyright notice and this permission notice shall be included in | |
13 | # all copies or substantial portions of the Software. | |
14 | # | |
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
21 | # THE SOFTWARE. | |
22 | ||
23 | # This is a pure-Python implementation of the AES algorithm and AES common | |
24 | # modes of operation. | |
25 | ||
26 | # See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard | |
27 | # See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation | |
28 | ||
29 | ||
30 | # Supported key sizes: | |
31 | # 128-bit | |
32 | # 192-bit | |
33 | # 256-bit | |
34 | ||
35 | ||
36 | # Supported modes of operation: | |
37 | # ECB - Electronic Codebook | |
38 | # CBC - Cipher-Block Chaining | |
39 | # CFB - Cipher Feedback | |
40 | # OFB - Output Feedback | |
41 | # CTR - Counter | |
42 | ||
43 | # See the README.md for API details and general information. | |
44 | ||
45 | # Also useful, PyCrypto, a crypto library implemented in C with Python bindings: | |
46 | # https://www.dlitz.net/software/pycrypto/ | |
47 | ||
48 | ||
49 | VERSION = [1, 3, 0] | |
50 | ||
51 | from .AES import AES, AESModeOfOperationCTR, AESModeOfOperationCBC, AESModeOfOperationCFB, AESModeOfOperationECB, AESModeOfOperationOFB, AESModesOfOperation, Counter | |
52 | from .blockfeeder import decrypt_stream, Decrypter, encrypt_stream, Encrypter | |
53 | from .blockfeeder import PADDING_NONE, PADDING_DEFAULT |
0 | ||
1 | #https://github.com/ricmoo/pyaes/blob/master/pyaes/blockfeeder.py | |
2 | # The MIT License (MIT) | |
3 | # | |
4 | # Copyright (c) 2014 Richard Moore | |
5 | # | |
6 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | # of this software and associated documentation files (the "Software"), to deal | |
8 | # in the Software without restriction, including without limitation the rights | |
9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | # copies of the Software, and to permit persons to whom the Software is | |
11 | # furnished to do so, subject to the following conditions: | |
12 | # | |
13 | # The above copyright notice and this permission notice shall be included in | |
14 | # all copies or substantial portions of the Software. | |
15 | # | |
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | # THE SOFTWARE. | |
23 | ||
24 | ||
25 | from .AES import AESBlockModeOfOperation, AESSegmentModeOfOperation, AESStreamModeOfOperation | |
26 | from .util import append_PKCS7_padding, strip_PKCS7_padding, to_bufferable | |
27 | ||
28 | ||
29 | # First we inject three functions to each of the modes of operations | |
30 | # | |
31 | # _can_consume(size) | |
32 | # - Given a size, determine how many bytes could be consumed in | |
33 | # a single call to either the decrypt or encrypt method | |
34 | # | |
35 | # _final_encrypt(data, padding = PADDING_DEFAULT) | |
36 | # - call and return encrypt on this (last) chunk of data, | |
37 | # padding as necessary; this will always be at least 16 | |
38 | # bytes unless the total incoming input was less than 16 | |
39 | # bytes | |
40 | # | |
41 | # _final_decrypt(data, padding = PADDING_DEFAULT) | |
42 | # - same as _final_encrypt except for decrypt, for | |
43 | # stripping off padding | |
44 | # | |
45 | ||
46 | PADDING_NONE = 'none' | |
47 | PADDING_DEFAULT = 'default' | |
48 | ||
49 | # @TODO: Ciphertext stealing and explicit PKCS#7 | |
50 | # PADDING_CIPHERTEXT_STEALING | |
51 | # PADDING_PKCS7 | |
52 | ||
53 | # ECB and CBC are block-only ciphers | |
54 | ||
55 | def _block_can_consume(self, size): | |
56 | if size >= 16: return 16 | |
57 | return 0 | |
58 | ||
59 | # After padding, we may have more than one block | |
60 | def _block_final_encrypt(self, data, padding = PADDING_DEFAULT): | |
61 | if padding == PADDING_DEFAULT: | |
62 | data = append_PKCS7_padding(data) | |
63 | ||
64 | elif padding == PADDING_NONE: | |
65 | if len(data) != 16: | |
66 | raise Exception('invalid data length for final block') | |
67 | else: | |
68 | raise Exception('invalid padding option') | |
69 | ||
70 | if len(data) == 32: | |
71 | return self.encrypt(data[:16]) + self.encrypt(data[16:]) | |
72 | ||
73 | return self.encrypt(data) | |
74 | ||
75 | ||
76 | def _block_final_decrypt(self, data, padding = PADDING_DEFAULT): | |
77 | if padding == PADDING_DEFAULT: | |
78 | return strip_PKCS7_padding(self.decrypt(data)) | |
79 | ||
80 | if padding == PADDING_NONE: | |
81 | if len(data) != 16: | |
82 | raise Exception('invalid data length for final block') | |
83 | return self.decrypt(data) | |
84 | ||
85 | raise Exception('invalid padding option') | |
86 | ||
87 | AESBlockModeOfOperation._can_consume = _block_can_consume | |
88 | AESBlockModeOfOperation._final_encrypt = _block_final_encrypt | |
89 | AESBlockModeOfOperation._final_decrypt = _block_final_decrypt | |
90 | ||
91 | ||
92 | ||
93 | # CFB is a segment cipher | |
94 | ||
95 | def _segment_can_consume(self, size): | |
96 | return self.segment_bytes * int(size // self.segment_bytes) | |
97 | ||
98 | # CFB can handle a non-segment-sized block at the end using the remaining cipherblock | |
99 | def _segment_final_encrypt(self, data, padding = PADDING_DEFAULT): | |
100 | if padding != PADDING_DEFAULT: | |
101 | raise Exception('invalid padding option') | |
102 | ||
103 | faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes))) | |
104 | padded = data + to_bufferable(faux_padding) | |
105 | return self.encrypt(padded)[:len(data)] | |
106 | ||
107 | # CFB can handle a non-segment-sized block at the end using the remaining cipherblock | |
108 | def _segment_final_decrypt(self, data, padding = PADDING_DEFAULT): | |
109 | if padding != PADDING_DEFAULT: | |
110 | raise Exception('invalid padding option') | |
111 | ||
112 | faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes))) | |
113 | padded = data + to_bufferable(faux_padding) | |
114 | return self.decrypt(padded)[:len(data)] | |
115 | ||
116 | AESSegmentModeOfOperation._can_consume = _segment_can_consume | |
117 | AESSegmentModeOfOperation._final_encrypt = _segment_final_encrypt | |
118 | AESSegmentModeOfOperation._final_decrypt = _segment_final_decrypt | |
119 | ||
120 | ||
121 | ||
122 | # OFB and CTR are stream ciphers | |
123 | ||
124 | def _stream_can_consume(self, size): | |
125 | return size | |
126 | ||
127 | def _stream_final_encrypt(self, data, padding = PADDING_DEFAULT): | |
128 | if padding not in [PADDING_NONE, PADDING_DEFAULT]: | |
129 | raise Exception('invalid padding option') | |
130 | ||
131 | return self.encrypt(data) | |
132 | ||
133 | def _stream_final_decrypt(self, data, padding = PADDING_DEFAULT): | |
134 | if padding not in [PADDING_NONE, PADDING_DEFAULT]: | |
135 | raise Exception('invalid padding option') | |
136 | ||
137 | return self.decrypt(data) | |
138 | ||
139 | AESStreamModeOfOperation._can_consume = _stream_can_consume | |
140 | AESStreamModeOfOperation._final_encrypt = _stream_final_encrypt | |
141 | AESStreamModeOfOperation._final_decrypt = _stream_final_decrypt | |
142 | ||
143 | ||
144 | ||
145 | class BlockFeeder(object): | |
146 | '''The super-class for objects to handle chunking a stream of bytes | |
147 | into the appropriate block size for the underlying mode of operation | |
148 | and applying (or stripping) padding, as necessary.''' | |
149 | ||
150 | def __init__(self, mode, feed, final, padding = PADDING_DEFAULT): | |
151 | self._mode = mode | |
152 | self._feed = feed | |
153 | self._final = final | |
154 | self._buffer = to_bufferable("") | |
155 | self._padding = padding | |
156 | ||
157 | def feed(self, data = None): | |
158 | '''Provide bytes to encrypt (or decrypt), returning any bytes | |
159 | possible from this or any previous calls to feed. | |
160 | ||
161 | Call with None or an empty string to flush the mode of | |
162 | operation and return any final bytes; no further calls to | |
163 | feed may be made.''' | |
164 | ||
165 | if self._buffer is None: | |
166 | raise ValueError('already finished feeder') | |
167 | ||
168 | # Finalize; process the spare bytes we were keeping | |
169 | if data is None: | |
170 | result = self._final(self._buffer, self._padding) | |
171 | self._buffer = None | |
172 | return result | |
173 | ||
174 | self._buffer += to_bufferable(data) | |
175 | ||
176 | # We keep 16 bytes around so we can determine padding | |
177 | result = to_bufferable('') | |
178 | while len(self._buffer) > 16: | |
179 | can_consume = self._mode._can_consume(len(self._buffer) - 16) | |
180 | if can_consume == 0: break | |
181 | result += self._feed(self._buffer[:can_consume]) | |
182 | self._buffer = self._buffer[can_consume:] | |
183 | ||
184 | return result | |
185 | ||
186 | ||
187 | class Encrypter(BlockFeeder): | |
188 | 'Accepts bytes of plaintext and returns encrypted ciphertext.' | |
189 | ||
190 | def __init__(self, mode, padding = PADDING_DEFAULT): | |
191 | BlockFeeder.__init__(self, mode, mode.encrypt, mode._final_encrypt, padding) | |
192 | ||
193 | ||
194 | class Decrypter(BlockFeeder): | |
195 | 'Accepts bytes of ciphertext and returns decrypted plaintext.' | |
196 | ||
197 | def __init__(self, mode, padding = PADDING_DEFAULT): | |
198 | BlockFeeder.__init__(self, mode, mode.decrypt, mode._final_decrypt, padding) | |
199 | ||
200 | ||
201 | # 8kb blocks | |
202 | BLOCK_SIZE = (1 << 13) | |
203 | ||
204 | def _feed_stream(feeder, in_stream, out_stream, block_size = BLOCK_SIZE): | |
205 | 'Uses feeder to read and convert from in_stream and write to out_stream.' | |
206 | ||
207 | while True: | |
208 | chunk = in_stream.read(block_size) | |
209 | if not chunk: | |
210 | break | |
211 | converted = feeder.feed(chunk) | |
212 | out_stream.write(converted) | |
213 | converted = feeder.feed() | |
214 | out_stream.write(converted) | |
215 | ||
216 | ||
217 | def encrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT): | |
218 | 'Encrypts a stream of bytes from in_stream to out_stream using mode.' | |
219 | ||
220 | encrypter = Encrypter(mode, padding = padding) | |
221 | _feed_stream(encrypter, in_stream, out_stream, block_size) | |
222 | ||
223 | ||
224 | def decrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT): | |
225 | 'Decrypts a stream of bytes from in_stream to out_stream using mode.' | |
226 | ||
227 | decrypter = Decrypter(mode, padding = padding) | |
228 | _feed_stream(decrypter, in_stream, out_stream, block_size) |
0 | ||
1 | #https://github.com/ricmoo/pyaes/blob/master/pyaes/util.py | |
2 | # The MIT License (MIT) | |
3 | # | |
4 | # Copyright (c) 2014 Richard Moore | |
5 | # | |
6 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | # of this software and associated documentation files (the "Software"), to deal | |
8 | # in the Software without restriction, including without limitation the rights | |
9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | # copies of the Software, and to permit persons to whom the Software is | |
11 | # furnished to do so, subject to the following conditions: | |
12 | # | |
13 | # The above copyright notice and this permission notice shall be included in | |
14 | # all copies or substantial portions of the Software. | |
15 | # | |
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | # THE SOFTWARE. | |
23 | ||
24 | # Why to_bufferable? | |
25 | # Python 3 is very different from Python 2.x when it comes to strings of text | |
26 | # and strings of bytes; in Python 3, strings of bytes do not exist, instead to | |
27 | # represent arbitrary binary data, we must use the "bytes" object. This method | |
28 | # ensures the object behaves as we need it to. | |
29 | ||
30 | def to_bufferable(binary): | |
31 | return binary | |
32 | ||
33 | def _get_byte(c): | |
34 | return ord(c) | |
35 | ||
36 | try: | |
37 | xrange | |
38 | except: | |
39 | ||
40 | def to_bufferable(binary): | |
41 | if isinstance(binary, bytes): | |
42 | return binary | |
43 | return bytes(ord(b) for b in binary) | |
44 | ||
45 | def _get_byte(c): | |
46 | return c | |
47 | ||
48 | def append_PKCS7_padding(data): | |
49 | pad = 16 - (len(data) % 16) | |
50 | return data + to_bufferable(chr(pad) * pad) | |
51 | ||
52 | def strip_PKCS7_padding(data): | |
53 | if len(data) % 16 != 0: | |
54 | raise ValueError("invalid length") | |
55 | ||
56 | pad = _get_byte(data[-1]) | |
57 | ||
58 | if pad > 16: | |
59 | raise ValueError("invalid padding byte") | |
60 | ||
61 | return data[:-pad] |
0 | ############################################################################# | |
1 | # Documentation # | |
2 | ############################################################################# | |
3 | ||
4 | # Author: Todd Whiteman | |
5 | # Date: 28th April, 2010 | |
6 | # Version: 2.0.1 | |
7 | # License: MIT | |
8 | # Homepage: http://twhiteman.netfirms.com/des.html | |
9 | # | |
10 | # This is a pure python implementation of the DES encryption algorithm. | |
11 | # It's pure python to avoid portability issues, since most DES | |
12 | # implementations are programmed in C (for performance reasons). | |
13 | # | |
14 | # Triple DES class is also implemented, utilizing the DES base. Triple DES | |
15 | # is either DES-EDE3 with a 24 byte key, or DES-EDE2 with a 16 byte key. | |
16 | # | |
17 | # See the README.txt that should come with this python module for the | |
18 | # implementation methods used. | |
19 | # | |
20 | # Thanks to: | |
21 | # * David Broadwell for ideas, comments and suggestions. | |
22 | # * Mario Wolff for pointing out and debugging some triple des CBC errors. | |
23 | # * Santiago Palladino for providing the PKCS5 padding technique. | |
24 | # * Shaya for correcting the PAD_PKCS5 triple des CBC errors. | |
25 | # | |
26 | """A pure python implementation of the DES and TRIPLE DES encryption algorithms. | |
27 | ||
28 | Class initialization | |
29 | -------------------- | |
30 | pyDes.des(key, [mode], [IV], [pad], [padmode]) | |
31 | pyDes.triple_des(key, [mode], [IV], [pad], [padmode]) | |
32 | ||
33 | key -> Bytes containing the encryption key. 8 bytes for DES, 16 or 24 bytes | |
34 | for Triple DES | |
35 | mode -> Optional argument for encryption type, can be either | |
36 | pyDes.ECB (Electronic Code Book) or pyDes.CBC (Cypher Block Chaining) | |
37 | IV -> Optional Initial Value bytes, must be supplied if using CBC mode. | |
38 | Length must be 8 bytes. | |
39 | pad -> Optional argument, set the pad character (PAD_NORMAL) to use during | |
40 | all encrypt/decrypt operations done with this instance. | |
41 | padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5) | |
42 | to use during all encrypt/decrypt operations done with this instance. | |
43 | ||
44 | I recommend to use PAD_PKCS5 padding, as then you never need to worry about any | |
45 | padding issues, as the padding can be removed unambiguously upon decrypting | |
46 | data that was encrypted using PAD_PKCS5 padmode. | |
47 | ||
48 | Common methods | |
49 | -------------- | |
50 | encrypt(data, [pad], [padmode]) | |
51 | decrypt(data, [pad], [padmode]) | |
52 | ||
53 | data -> Bytes to be encrypted/decrypted | |
54 | pad -> Optional argument. Only when using padmode of PAD_NORMAL. For | |
55 | encryption, adds this characters to the end of the data block when | |
56 | data is not a multiple of 8 bytes. For decryption, will remove the | |
57 | trailing characters that match this pad character from the last 8 | |
58 | bytes of the unencrypted data block. | |
59 | padmode -> Optional argument, set the padding mode, must be one of PAD_NORMAL | |
60 | or PAD_PKCS5). Defaults to PAD_NORMAL. | |
61 | ||
62 | ||
63 | Example | |
64 | ------- | |
65 | from pyDes import * | |
66 | ||
67 | data = "Please encrypt my data" | |
68 | k = des("DESCRYPT", CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5) | |
69 | # For Python3, you'll need to use bytes, i.e.: | |
70 | # data = b"Please encrypt my data" | |
71 | # k = des(b"DESCRYPT", CBC, b"\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5) | |
72 | d = k.encrypt(data) | |
73 | print "Encrypted: %r" % d | |
74 | print "Decrypted: %r" % k.decrypt(d) | |
75 | assert k.decrypt(d, padmode=PAD_PKCS5) == data | |
76 | ||
77 | ||
78 | See the module source (pyDes.py) for more examples of use. | |
79 | You can also run the pyDes.py file without and arguments to see a simple test. | |
80 | ||
81 | Note: This code was not written for high-end systems needing a fast | |
82 | implementation, but rather a handy portable solution with small usage. | |
83 | ||
84 | """ | |
85 | ||
86 | import sys | |
87 | ||
88 | # _pythonMajorVersion is used to handle Python2 and Python3 differences. | |
89 | _pythonMajorVersion = sys.version_info[0] | |
90 | ||
91 | # Modes of crypting / cyphering | |
92 | ECB = 0 | |
93 | CBC = 1 | |
94 | ||
95 | # Modes of padding | |
96 | PAD_NORMAL = 1 | |
97 | PAD_PKCS5 = 2 | |
98 | ||
99 | # PAD_PKCS5: is a method that will unambiguously remove all padding | |
100 | # characters after decryption, when originally encrypted with | |
101 | # this padding mode. | |
102 | # For a good description of the PKCS5 padding technique, see: | |
103 | # http://www.faqs.org/rfcs/rfc1423.html | |
104 | ||
105 | # The base class shared by des and triple des. | |
106 | class _baseDes(object): | |
107 | def __init__(self, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): | |
108 | if IV: | |
109 | IV = self._guardAgainstUnicode(IV) | |
110 | if pad: | |
111 | pad = self._guardAgainstUnicode(pad) | |
112 | self.block_size = 8 | |
113 | # Sanity checking of arguments. | |
114 | if pad and padmode == PAD_PKCS5: | |
115 | raise ValueError("Cannot use a pad character with PAD_PKCS5") | |
116 | if IV and len(IV) != self.block_size: | |
117 | raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes") | |
118 | ||
119 | # Set the passed in variables | |
120 | self._mode = mode | |
121 | self._iv = IV | |
122 | self._padding = pad | |
123 | self._padmode = padmode | |
124 | ||
125 | def getKey(self): | |
126 | """getKey() -> bytes""" | |
127 | return self.__key | |
128 | ||
129 | def setKey(self, key): | |
130 | """Will set the crypting key for this object.""" | |
131 | key = self._guardAgainstUnicode(key) | |
132 | self.__key = key | |
133 | ||
134 | def getMode(self): | |
135 | """getMode() -> pyDes.ECB or pyDes.CBC""" | |
136 | return self._mode | |
137 | ||
138 | def setMode(self, mode): | |
139 | """Sets the type of crypting mode, pyDes.ECB or pyDes.CBC""" | |
140 | self._mode = mode | |
141 | ||
142 | def getPadding(self): | |
143 | """getPadding() -> bytes of length 1. Padding character.""" | |
144 | return self._padding | |
145 | ||
146 | def setPadding(self, pad): | |
147 | """setPadding() -> bytes of length 1. Padding character.""" | |
148 | if pad is not None: | |
149 | pad = self._guardAgainstUnicode(pad) | |
150 | self._padding = pad | |
151 | ||
152 | def getPadMode(self): | |
153 | """getPadMode() -> pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" | |
154 | return self._padmode | |
155 | ||
156 | def setPadMode(self, mode): | |
157 | """Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" | |
158 | self._padmode = mode | |
159 | ||
160 | def getIV(self): | |
161 | """getIV() -> bytes""" | |
162 | return self._iv | |
163 | ||
164 | def setIV(self, IV): | |
165 | """Will set the Initial Value, used in conjunction with CBC mode""" | |
166 | if not IV or len(IV) != self.block_size: | |
167 | raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes") | |
168 | IV = self._guardAgainstUnicode(IV) | |
169 | self._iv = IV | |
170 | ||
171 | def _padData(self, data, pad, padmode): | |
172 | # Pad data depending on the mode | |
173 | if padmode is None: | |
174 | # Get the default padding mode. | |
175 | padmode = self.getPadMode() | |
176 | if pad and padmode == PAD_PKCS5: | |
177 | raise ValueError("Cannot use a pad character with PAD_PKCS5") | |
178 | ||
179 | if padmode == PAD_NORMAL: | |
180 | if len(data) % self.block_size == 0: | |
181 | # No padding required. | |
182 | return data | |
183 | ||
184 | if not pad: | |
185 | # Get the default padding. | |
186 | pad = self.getPadding() | |
187 | if not pad: | |
188 | raise ValueError("Data must be a multiple of " + str(self.block_size) + " bytes in length. Use padmode=PAD_PKCS5 or set the pad character.") | |
189 | data += (self.block_size - (len(data) % self.block_size)) * pad | |
190 | ||
191 | elif padmode == PAD_PKCS5: | |
192 | pad_len = 8 - (len(data) % self.block_size) | |
193 | if _pythonMajorVersion < 3: | |
194 | data += pad_len * chr(pad_len) | |
195 | else: | |
196 | data += bytes([pad_len] * pad_len) | |
197 | ||
198 | return data | |
199 | ||
200 | def _unpadData(self, data, pad, padmode): | |
201 | # Unpad data depending on the mode. | |
202 | if not data: | |
203 | return data | |
204 | if pad and padmode == PAD_PKCS5: | |
205 | raise ValueError("Cannot use a pad character with PAD_PKCS5") | |
206 | if padmode is None: | |
207 | # Get the default padding mode. | |
208 | padmode = self.getPadMode() | |
209 | ||
210 | if padmode == PAD_NORMAL: | |
211 | if not pad: | |
212 | # Get the default padding. | |
213 | pad = self.getPadding() | |
214 | if pad: | |
215 | data = data[:-self.block_size] + \ | |
216 | data[-self.block_size:].rstrip(pad) | |
217 | ||
218 | elif padmode == PAD_PKCS5: | |
219 | if _pythonMajorVersion < 3: | |
220 | pad_len = ord(data[-1]) | |
221 | else: | |
222 | pad_len = data[-1] | |
223 | data = data[:-pad_len] | |
224 | ||
225 | return data | |
226 | ||
227 | def _guardAgainstUnicode(self, data): | |
228 | # Only accept byte strings or ascii unicode values, otherwise | |
229 | # there is no way to correctly decode the data into bytes. | |
230 | if _pythonMajorVersion < 3: | |
231 | if isinstance(data, unicode): | |
232 | raise ValueError("pyDes can only work with bytes, not Unicode strings.") | |
233 | else: | |
234 | if isinstance(data, str): | |
235 | # Only accept ascii unicode values. | |
236 | try: | |
237 | return data.encode('ascii') | |
238 | except UnicodeEncodeError: | |
239 | pass | |
240 | raise ValueError("pyDes can only work with encoded strings, not Unicode.") | |
241 | return data | |
242 | ||
243 | ############################################################################# | |
244 | # DES # | |
245 | ############################################################################# | |
246 | class des(_baseDes): | |
247 | """DES encryption/decrytpion class | |
248 | ||
249 | Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes. | |
250 | ||
251 | pyDes.des(key,[mode], [IV]) | |
252 | ||
253 | key -> Bytes containing the encryption key, must be exactly 8 bytes | |
254 | mode -> Optional argument for encryption type, can be either pyDes.ECB | |
255 | (Electronic Code Book), pyDes.CBC (Cypher Block Chaining) | |
256 | IV -> Optional Initial Value bytes, must be supplied if using CBC mode. | |
257 | Must be 8 bytes in length. | |
258 | pad -> Optional argument, set the pad character (PAD_NORMAL) to use | |
259 | during all encrypt/decrypt operations done with this instance. | |
260 | padmode -> Optional argument, set the padding mode (PAD_NORMAL or | |
261 | PAD_PKCS5) to use during all encrypt/decrypt operations done | |
262 | with this instance. | |
263 | """ | |
264 | ||
265 | ||
266 | # Permutation and translation tables for DES | |
267 | __pc1 = [56, 48, 40, 32, 24, 16, 8, | |
268 | 0, 57, 49, 41, 33, 25, 17, | |
269 | 9, 1, 58, 50, 42, 34, 26, | |
270 | 18, 10, 2, 59, 51, 43, 35, | |
271 | 62, 54, 46, 38, 30, 22, 14, | |
272 | 6, 61, 53, 45, 37, 29, 21, | |
273 | 13, 5, 60, 52, 44, 36, 28, | |
274 | 20, 12, 4, 27, 19, 11, 3 | |
275 | ] | |
276 | ||
277 | # number left rotations of pc1 | |
278 | __left_rotations = [ | |
279 | 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 | |
280 | ] | |
281 | ||
282 | # permuted choice key (table 2) | |
283 | __pc2 = [ | |
284 | 13, 16, 10, 23, 0, 4, | |
285 | 2, 27, 14, 5, 20, 9, | |
286 | 22, 18, 11, 3, 25, 7, | |
287 | 15, 6, 26, 19, 12, 1, | |
288 | 40, 51, 30, 36, 46, 54, | |
289 | 29, 39, 50, 44, 32, 47, | |
290 | 43, 48, 38, 55, 33, 52, | |
291 | 45, 41, 49, 35, 28, 31 | |
292 | ] | |
293 | ||
294 | # initial permutation IP | |
295 | __ip = [57, 49, 41, 33, 25, 17, 9, 1, | |
296 | 59, 51, 43, 35, 27, 19, 11, 3, | |
297 | 61, 53, 45, 37, 29, 21, 13, 5, | |
298 | 63, 55, 47, 39, 31, 23, 15, 7, | |
299 | 56, 48, 40, 32, 24, 16, 8, 0, | |
300 | 58, 50, 42, 34, 26, 18, 10, 2, | |
301 | 60, 52, 44, 36, 28, 20, 12, 4, | |
302 | 62, 54, 46, 38, 30, 22, 14, 6 | |
303 | ] | |
304 | ||
305 | # Expansion table for turning 32 bit blocks into 48 bits | |
306 | __expansion_table = [ | |
307 | 31, 0, 1, 2, 3, 4, | |
308 | 3, 4, 5, 6, 7, 8, | |
309 | 7, 8, 9, 10, 11, 12, | |
310 | 11, 12, 13, 14, 15, 16, | |
311 | 15, 16, 17, 18, 19, 20, | |
312 | 19, 20, 21, 22, 23, 24, | |
313 | 23, 24, 25, 26, 27, 28, | |
314 | 27, 28, 29, 30, 31, 0 | |
315 | ] | |
316 | ||
317 | # The (in)famous S-boxes | |
318 | __sbox = [ | |
319 | # S1 | |
320 | [14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, | |
321 | 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, | |
322 | 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, | |
323 | 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13], | |
324 | ||
325 | # S2 | |
326 | [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, | |
327 | 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, | |
328 | 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, | |
329 | 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9], | |
330 | ||
331 | # S3 | |
332 | [10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, | |
333 | 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, | |
334 | 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, | |
335 | 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12], | |
336 | ||
337 | # S4 | |
338 | [7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, | |
339 | 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, | |
340 | 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, | |
341 | 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14], | |
342 | ||
343 | # S5 | |
344 | [2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, | |
345 | 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, | |
346 | 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, | |
347 | 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3], | |
348 | ||
349 | # S6 | |
350 | [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, | |
351 | 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, | |
352 | 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, | |
353 | 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13], | |
354 | ||
355 | # S7 | |
356 | [4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, | |
357 | 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, | |
358 | 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, | |
359 | 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12], | |
360 | ||
361 | # S8 | |
362 | [13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, | |
363 | 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, | |
364 | 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, | |
365 | 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11], | |
366 | ] | |
367 | ||
368 | ||
369 | # 32-bit permutation function P used on the output of the S-boxes | |
370 | __p = [ | |
371 | 15, 6, 19, 20, 28, 11, | |
372 | 27, 16, 0, 14, 22, 25, | |
373 | 4, 17, 30, 9, 1, 7, | |
374 | 23,13, 31, 26, 2, 8, | |
375 | 18, 12, 29, 5, 21, 10, | |
376 | 3, 24 | |
377 | ] | |
378 | ||
379 | # final permutation IP^-1 | |
380 | __fp = [ | |
381 | 39, 7, 47, 15, 55, 23, 63, 31, | |
382 | 38, 6, 46, 14, 54, 22, 62, 30, | |
383 | 37, 5, 45, 13, 53, 21, 61, 29, | |
384 | 36, 4, 44, 12, 52, 20, 60, 28, | |
385 | 35, 3, 43, 11, 51, 19, 59, 27, | |
386 | 34, 2, 42, 10, 50, 18, 58, 26, | |
387 | 33, 1, 41, 9, 49, 17, 57, 25, | |
388 | 32, 0, 40, 8, 48, 16, 56, 24 | |
389 | ] | |
390 | ||
391 | # Type of crypting being done | |
392 | ENCRYPT = 0x00 | |
393 | DECRYPT = 0x01 | |
394 | ||
395 | # Initialisation | |
396 | def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): | |
397 | # Sanity checking of arguments. | |
398 | if len(key) != 8: | |
399 | raise ValueError("Invalid DES key size. Key must be exactly 8 bytes long.") | |
400 | _baseDes.__init__(self, mode, IV, pad, padmode) | |
401 | self.key_size = 8 | |
402 | ||
403 | self.L = [] | |
404 | self.R = [] | |
405 | self.Kn = [ [0] * 48 ] * 16 # 16 48-bit keys (K1 - K16) | |
406 | self.final = [] | |
407 | ||
408 | self.setKey(key) | |
409 | ||
410 | def setKey(self, key): | |
411 | """Will set the crypting key for this object. Must be 8 bytes.""" | |
412 | _baseDes.setKey(self, key) | |
413 | self.__create_sub_keys() | |
414 | ||
415 | def __String_to_BitList(self, data): | |
416 | """Turn the string data, into a list of bits (1, 0)'s""" | |
417 | if _pythonMajorVersion < 3: | |
418 | # Turn the strings into integers. Python 3 uses a bytes | |
419 | # class, which already has this behaviour. | |
420 | data = [ord(c) for c in data] | |
421 | l = len(data) * 8 | |
422 | result = [0] * l | |
423 | pos = 0 | |
424 | for ch in data: | |
425 | i = 7 | |
426 | while i >= 0: | |
427 | if ch & (1 << i) != 0: | |
428 | result[pos] = 1 | |
429 | else: | |
430 | result[pos] = 0 | |
431 | pos += 1 | |
432 | i -= 1 | |
433 | ||
434 | return result | |
435 | ||
436 | def __BitList_to_String(self, data): | |
437 | """Turn the list of bits -> data, into a string""" | |
438 | result = [] | |
439 | pos = 0 | |
440 | c = 0 | |
441 | while pos < len(data): | |
442 | c += data[pos] << (7 - (pos % 8)) | |
443 | if (pos % 8) == 7: | |
444 | result.append(c) | |
445 | c = 0 | |
446 | pos += 1 | |
447 | ||
448 | if _pythonMajorVersion < 3: | |
449 | return ''.join([ chr(c) for c in result ]) | |
450 | else: | |
451 | return bytes(result) | |
452 | ||
453 | def __permutate(self, table, block): | |
454 | """Permutate this block with the specified table""" | |
455 | return list(map(lambda x: block[x], table)) | |
456 | ||
457 | # Transform the secret key, so that it is ready for data processing | |
458 | # Create the 16 subkeys, K[1] - K[16] | |
459 | def __create_sub_keys(self): | |
460 | """Create the 16 subkeys K[1] to K[16] from the given key""" | |
461 | key = self.__permutate(des.__pc1, self.__String_to_BitList(self.getKey())) | |
462 | i = 0 | |
463 | # Split into Left and Right sections | |
464 | self.L = key[:28] | |
465 | self.R = key[28:] | |
466 | while i < 16: | |
467 | j = 0 | |
468 | # Perform circular left shifts | |
469 | while j < des.__left_rotations[i]: | |
470 | self.L.append(self.L[0]) | |
471 | del self.L[0] | |
472 | ||
473 | self.R.append(self.R[0]) | |
474 | del self.R[0] | |
475 | ||
476 | j += 1 | |
477 | ||
478 | # Create one of the 16 subkeys through pc2 permutation | |
479 | self.Kn[i] = self.__permutate(des.__pc2, self.L + self.R) | |
480 | ||
481 | i += 1 | |
482 | ||
483 | # Main part of the encryption algorithm, the number cruncher :) | |
484 | def __des_crypt(self, block, crypt_type): | |
485 | """Crypt the block of data through DES bit-manipulation""" | |
486 | block = self.__permutate(des.__ip, block) | |
487 | self.L = block[:32] | |
488 | self.R = block[32:] | |
489 | ||
490 | # Encryption starts from Kn[1] through to Kn[16] | |
491 | if crypt_type == des.ENCRYPT: | |
492 | iteration = 0 | |
493 | iteration_adjustment = 1 | |
494 | # Decryption starts from Kn[16] down to Kn[1] | |
495 | else: | |
496 | iteration = 15 | |
497 | iteration_adjustment = -1 | |
498 | ||
499 | i = 0 | |
500 | while i < 16: | |
501 | # Make a copy of R[i-1], this will later become L[i] | |
502 | tempR = self.R[:] | |
503 | ||
504 | # Permutate R[i - 1] to start creating R[i] | |
505 | self.R = self.__permutate(des.__expansion_table, self.R) | |
506 | ||
507 | # Exclusive or R[i - 1] with K[i], create B[1] to B[8] whilst here | |
508 | self.R = list(map(lambda x, y: x ^ y, self.R, self.Kn[iteration])) | |
509 | B = [self.R[:6], self.R[6:12], self.R[12:18], self.R[18:24], self.R[24:30], self.R[30:36], self.R[36:42], self.R[42:]] | |
510 | # Optimization: Replaced below commented code with above | |
511 | #j = 0 | |
512 | #B = [] | |
513 | #while j < len(self.R): | |
514 | # self.R[j] = self.R[j] ^ self.Kn[iteration][j] | |
515 | # j += 1 | |
516 | # if j % 6 == 0: | |
517 | # B.append(self.R[j-6:j]) | |
518 | ||
519 | # Permutate B[1] to B[8] using the S-Boxes | |
520 | j = 0 | |
521 | Bn = [0] * 32 | |
522 | pos = 0 | |
523 | while j < 8: | |
524 | # Work out the offsets | |
525 | m = (B[j][0] << 1) + B[j][5] | |
526 | n = (B[j][1] << 3) + (B[j][2] << 2) + (B[j][3] << 1) + B[j][4] | |
527 | ||
528 | # Find the permutation value | |
529 | v = des.__sbox[j][(m << 4) + n] | |
530 | ||
531 | # Turn value into bits, add it to result: Bn | |
532 | Bn[pos] = (v & 8) >> 3 | |
533 | Bn[pos + 1] = (v & 4) >> 2 | |
534 | Bn[pos + 2] = (v & 2) >> 1 | |
535 | Bn[pos + 3] = v & 1 | |
536 | ||
537 | pos += 4 | |
538 | j += 1 | |
539 | ||
540 | # Permutate the concatination of B[1] to B[8] (Bn) | |
541 | self.R = self.__permutate(des.__p, Bn) | |
542 | ||
543 | # Xor with L[i - 1] | |
544 | self.R = list(map(lambda x, y: x ^ y, self.R, self.L)) | |
545 | # Optimization: This now replaces the below commented code | |
546 | #j = 0 | |
547 | #while j < len(self.R): | |
548 | # self.R[j] = self.R[j] ^ self.L[j] | |
549 | # j += 1 | |
550 | ||
551 | # L[i] becomes R[i - 1] | |
552 | self.L = tempR | |
553 | ||
554 | i += 1 | |
555 | iteration += iteration_adjustment | |
556 | ||
557 | # Final permutation of R[16]L[16] | |
558 | self.final = self.__permutate(des.__fp, self.R + self.L) | |
559 | return self.final | |
560 | ||
561 | ||
562 | # Data to be encrypted/decrypted | |
563 | def crypt(self, data, crypt_type): | |
564 | """Crypt the data in blocks, running it through des_crypt()""" | |
565 | ||
566 | # Error check the data | |
567 | if not data: | |
568 | return '' | |
569 | if len(data) % self.block_size != 0: | |
570 | if crypt_type == des.DECRYPT: # Decryption must work on 8 byte blocks | |
571 | raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n.") | |
572 | if not self.getPadding(): | |
573 | raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n. Try setting the optional padding character") | |
574 | else: | |
575 | data += (self.block_size - (len(data) % self.block_size)) * self.getPadding() | |
576 | # print "Len of data: %f" % (len(data) / self.block_size) | |
577 | ||
578 | if self.getMode() == CBC: | |
579 | if self.getIV(): | |
580 | iv = self.__String_to_BitList(self.getIV()) | |
581 | else: | |
582 | raise ValueError("For CBC mode, you must supply the Initial Value (IV) for ciphering") | |
583 | ||
584 | # Split the data into blocks, crypting each one seperately | |
585 | i = 0 | |
586 | dict = {} | |
587 | result = [] | |
588 | #cached = 0 | |
589 | #lines = 0 | |
590 | while i < len(data): | |
591 | # Test code for caching encryption results | |
592 | #lines += 1 | |
593 | #if dict.has_key(data[i:i+8]): | |
594 | #print "Cached result for: %s" % data[i:i+8] | |
595 | # cached += 1 | |
596 | # result.append(dict[data[i:i+8]]) | |
597 | # i += 8 | |
598 | # continue | |
599 | ||
600 | block = self.__String_to_BitList(data[i:i+8]) | |
601 | ||
602 | # Xor with IV if using CBC mode | |
603 | if self.getMode() == CBC: | |
604 | if crypt_type == des.ENCRYPT: | |
605 | block = list(map(lambda x, y: x ^ y, block, iv)) | |
606 | #j = 0 | |
607 | #while j < len(block): | |
608 | # block[j] = block[j] ^ iv[j] | |
609 | # j += 1 | |
610 | ||
611 | processed_block = self.__des_crypt(block, crypt_type) | |
612 | ||
613 | if crypt_type == des.DECRYPT: | |
614 | processed_block = list(map(lambda x, y: x ^ y, processed_block, iv)) | |
615 | #j = 0 | |
616 | #while j < len(processed_block): | |
617 | # processed_block[j] = processed_block[j] ^ iv[j] | |
618 | # j += 1 | |
619 | iv = block | |
620 | else: | |
621 | iv = processed_block | |
622 | else: | |
623 | processed_block = self.__des_crypt(block, crypt_type) | |
624 | ||
625 | ||
626 | # Add the resulting crypted block to our list | |
627 | #d = self.__BitList_to_String(processed_block) | |
628 | #result.append(d) | |
629 | result.append(self.__BitList_to_String(processed_block)) | |
630 | #dict[data[i:i+8]] = d | |
631 | i += 8 | |
632 | ||
633 | # print "Lines: %d, cached: %d" % (lines, cached) | |
634 | ||
635 | # Return the full crypted string | |
636 | if _pythonMajorVersion < 3: | |
637 | return ''.join(result) | |
638 | else: | |
639 | return bytes.fromhex('').join(result) | |
640 | ||
641 | def encrypt(self, data, pad=None, padmode=None): | |
642 | """encrypt(data, [pad], [padmode]) -> bytes | |
643 | ||
644 | data : Bytes to be encrypted | |
645 | pad : Optional argument for encryption padding. Must only be one byte | |
646 | padmode : Optional argument for overriding the padding mode. | |
647 | ||
648 | The data must be a multiple of 8 bytes and will be encrypted | |
649 | with the already specified key. Data does not have to be a | |
650 | multiple of 8 bytes if the padding character is supplied, or | |
651 | the padmode is set to PAD_PKCS5, as bytes will then added to | |
652 | ensure the be padded data is a multiple of 8 bytes. | |
653 | """ | |
654 | data = self._guardAgainstUnicode(data) | |
655 | if pad is not None: | |
656 | pad = self._guardAgainstUnicode(pad) | |
657 | data = self._padData(data, pad, padmode) | |
658 | return self.crypt(data, des.ENCRYPT) | |
659 | ||
660 | def decrypt(self, data, pad=None, padmode=None): | |
661 | """decrypt(data, [pad], [padmode]) -> bytes | |
662 | ||
663 | data : Bytes to be decrypted | |
664 | pad : Optional argument for decryption padding. Must only be one byte | |
665 | padmode : Optional argument for overriding the padding mode. | |
666 | ||
667 | The data must be a multiple of 8 bytes and will be decrypted | |
668 | with the already specified key. In PAD_NORMAL mode, if the | |
669 | optional padding character is supplied, then the un-encrypted | |
670 | data will have the padding characters removed from the end of | |
671 | the bytes. This pad removal only occurs on the last 8 bytes of | |
672 | the data (last data block). In PAD_PKCS5 mode, the special | |
673 | padding end markers will be removed from the data after decrypting. | |
674 | """ | |
675 | data = self._guardAgainstUnicode(data) | |
676 | if pad is not None: | |
677 | pad = self._guardAgainstUnicode(pad) | |
678 | data = self.crypt(data, des.DECRYPT) | |
679 | return self._unpadData(data, pad, padmode) | |
680 | ||
681 | ||
682 | ||
683 | ############################################################################# | |
684 | # Triple DES # | |
685 | ############################################################################# | |
686 | class triple_des(_baseDes): | |
687 | """Triple DES encryption/decrytpion class | |
688 | ||
689 | This algorithm uses the DES-EDE3 (when a 24 byte key is supplied) or | |
690 | the DES-EDE2 (when a 16 byte key is supplied) encryption methods. | |
691 | Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes. | |
692 | ||
693 | pyDes.des(key, [mode], [IV]) | |
694 | ||
695 | key -> Bytes containing the encryption key, must be either 16 or | |
696 | 24 bytes long | |
697 | mode -> Optional argument for encryption type, can be either pyDes.ECB | |
698 | (Electronic Code Book), pyDes.CBC (Cypher Block Chaining) | |
699 | IV -> Optional Initial Value bytes, must be supplied if using CBC mode. | |
700 | Must be 8 bytes in length. | |
701 | pad -> Optional argument, set the pad character (PAD_NORMAL) to use | |
702 | during all encrypt/decrypt operations done with this instance. | |
703 | padmode -> Optional argument, set the padding mode (PAD_NORMAL or | |
704 | PAD_PKCS5) to use during all encrypt/decrypt operations done | |
705 | with this instance. | |
706 | """ | |
707 | def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): | |
708 | _baseDes.__init__(self, mode, IV, pad, padmode) | |
709 | self.setKey(key) | |
710 | ||
711 | def setKey(self, key): | |
712 | """Will set the crypting key for this object. Either 16 or 24 bytes long.""" | |
713 | self.key_size = 24 # Use DES-EDE3 mode | |
714 | if len(key) != self.key_size: | |
715 | if len(key) == 16: # Use DES-EDE2 mode | |
716 | self.key_size = 16 | |
717 | else: | |
718 | raise ValueError("Invalid triple DES key size. Key must be either 16 or 24 bytes long") | |
719 | if self.getMode() == CBC: | |
720 | if not self.getIV(): | |
721 | # Use the first 8 bytes of the key | |
722 | self._iv = key[:self.block_size] | |
723 | if len(self.getIV()) != self.block_size: | |
724 | raise ValueError("Invalid IV, must be 8 bytes in length") | |
725 | self.__key1 = des(key[:8], self._mode, self._iv, | |
726 | self._padding, self._padmode) | |
727 | self.__key2 = des(key[8:16], self._mode, self._iv, | |
728 | self._padding, self._padmode) | |
729 | if self.key_size == 16: | |
730 | self.__key3 = self.__key1 | |
731 | else: | |
732 | self.__key3 = des(key[16:], self._mode, self._iv, | |
733 | self._padding, self._padmode) | |
734 | _baseDes.setKey(self, key) | |
735 | ||
736 | # Override setter methods to work on all 3 keys. | |
737 | ||
738 | def setMode(self, mode): | |
739 | """Sets the type of crypting mode, pyDes.ECB or pyDes.CBC""" | |
740 | _baseDes.setMode(self, mode) | |
741 | for key in (self.__key1, self.__key2, self.__key3): | |
742 | key.setMode(mode) | |
743 | ||
744 | def setPadding(self, pad): | |
745 | """setPadding() -> bytes of length 1. Padding character.""" | |
746 | _baseDes.setPadding(self, pad) | |
747 | for key in (self.__key1, self.__key2, self.__key3): | |
748 | key.setPadding(pad) | |
749 | ||
750 | def setPadMode(self, mode): | |
751 | """Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" | |
752 | _baseDes.setPadMode(self, mode) | |
753 | for key in (self.__key1, self.__key2, self.__key3): | |
754 | key.setPadMode(mode) | |
755 | ||
756 | def setIV(self, IV): | |
757 | """Will set the Initial Value, used in conjunction with CBC mode""" | |
758 | _baseDes.setIV(self, IV) | |
759 | for key in (self.__key1, self.__key2, self.__key3): | |
760 | key.setIV(IV) | |
761 | ||
762 | def encrypt(self, data, pad=None, padmode=None): | |
763 | """encrypt(data, [pad], [padmode]) -> bytes | |
764 | ||
765 | data : bytes to be encrypted | |
766 | pad : Optional argument for encryption padding. Must only be one byte | |
767 | padmode : Optional argument for overriding the padding mode. | |
768 | ||
769 | The data must be a multiple of 8 bytes and will be encrypted | |
770 | with the already specified key. Data does not have to be a | |
771 | multiple of 8 bytes if the padding character is supplied, or | |
772 | the padmode is set to PAD_PKCS5, as bytes will then added to | |
773 | ensure the be padded data is a multiple of 8 bytes. | |
774 | """ | |
775 | ENCRYPT = des.ENCRYPT | |
776 | DECRYPT = des.DECRYPT | |
777 | data = self._guardAgainstUnicode(data) | |
778 | if pad is not None: | |
779 | pad = self._guardAgainstUnicode(pad) | |
780 | # Pad the data accordingly. | |
781 | data = self._padData(data, pad, padmode) | |
782 | if self.getMode() == CBC: | |
783 | self.__key1.setIV(self.getIV()) | |
784 | self.__key2.setIV(self.getIV()) | |
785 | self.__key3.setIV(self.getIV()) | |
786 | i = 0 | |
787 | result = [] | |
788 | while i < len(data): | |
789 | block = self.__key1.crypt(data[i:i+8], ENCRYPT) | |
790 | block = self.__key2.crypt(block, DECRYPT) | |
791 | block = self.__key3.crypt(block, ENCRYPT) | |
792 | self.__key1.setIV(block) | |
793 | self.__key2.setIV(block) | |
794 | self.__key3.setIV(block) | |
795 | result.append(block) | |
796 | i += 8 | |
797 | if _pythonMajorVersion < 3: | |
798 | return ''.join(result) | |
799 | else: | |
800 | return bytes.fromhex('').join(result) | |
801 | else: | |
802 | data = self.__key1.crypt(data, ENCRYPT) | |
803 | data = self.__key2.crypt(data, DECRYPT) | |
804 | return self.__key3.crypt(data, ENCRYPT) | |
805 | ||
806 | def decrypt(self, data, pad=None, padmode=None): | |
807 | """decrypt(data, [pad], [padmode]) -> bytes | |
808 | ||
809 | data : bytes to be encrypted | |
810 | pad : Optional argument for decryption padding. Must only be one byte | |
811 | padmode : Optional argument for overriding the padding mode. | |
812 | ||
813 | The data must be a multiple of 8 bytes and will be decrypted | |
814 | with the already specified key. In PAD_NORMAL mode, if the | |
815 | optional padding character is supplied, then the un-encrypted | |
816 | data will have the padding characters removed from the end of | |
817 | the bytes. This pad removal only occurs on the last 8 bytes of | |
818 | the data (last data block). In PAD_PKCS5 mode, the special | |
819 | padding end markers will be removed from the data after | |
820 | decrypting, no pad character is required for PAD_PKCS5. | |
821 | """ | |
822 | ENCRYPT = des.ENCRYPT | |
823 | DECRYPT = des.DECRYPT | |
824 | data = self._guardAgainstUnicode(data) | |
825 | if pad is not None: | |
826 | pad = self._guardAgainstUnicode(pad) | |
827 | if self.getMode() == CBC: | |
828 | self.__key1.setIV(self.getIV()) | |
829 | self.__key2.setIV(self.getIV()) | |
830 | self.__key3.setIV(self.getIV()) | |
831 | i = 0 | |
832 | result = [] | |
833 | while i < len(data): | |
834 | iv = data[i:i+8] | |
835 | block = self.__key3.crypt(iv, DECRYPT) | |
836 | block = self.__key2.crypt(block, ENCRYPT) | |
837 | block = self.__key1.crypt(block, DECRYPT) | |
838 | self.__key1.setIV(iv) | |
839 | self.__key2.setIV(iv) | |
840 | self.__key3.setIV(iv) | |
841 | result.append(block) | |
842 | i += 8 | |
843 | if _pythonMajorVersion < 3: | |
844 | data = ''.join(result) | |
845 | else: | |
846 | data = bytes.fromhex('').join(result) | |
847 | else: | |
848 | data = self.__key3.crypt(data, DECRYPT) | |
849 | data = self.__key2.crypt(data, ENCRYPT) | |
850 | data = self.__key1.crypt(data, DECRYPT) | |
851 | return self._unpadData(data, pad, padmode) |
0 | #!/usr/bin/env python | |
1 | ||
2 | ||
3 | """ | |
4 | https://raw.githubusercontent.com/bozhu/RC4-Python/master/rc4.py | |
5 | ||
6 | Copyright (C) 2012 Bo Zhu http://about.bozhu.me | |
7 | ||
8 | Permission is hereby granted, free of charge, to any person obtaining a | |
9 | copy of this software and associated documentation files (the "Software"), | |
10 | to deal in the Software without restriction, including without limitation | |
11 | the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
12 | and/or sell copies of the Software, and to permit persons to whom the | |
13 | Software is furnished to do so, subject to the following conditions: | |
14 | ||
15 | The above copyright notice and this permission notice shall be included in | |
16 | all copies or substantial portions of the Software. | |
17 | ||
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
21 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
24 | DEALINGS IN THE SOFTWARE. | |
25 | """ | |
26 | class RC4(): | |
27 | def __init__(self, key): | |
28 | self.key = key | |
29 | self.S = self.KSA() | |
30 | self.keystream = self.PRGA() | |
31 | ||
32 | def KSA(self): | |
33 | keylength = len(self.key) | |
34 | ||
35 | S = list(range(256)) | |
36 | ||
37 | j = 0 | |
38 | for i in range(256): | |
39 | j = (j + S[i] + self.key[i % keylength]) % 256 | |
40 | S[i], S[j] = S[j], S[i] # swap | |
41 | ||
42 | return S | |
43 | ||
44 | def PRGA(self): | |
45 | i = 0 | |
46 | j = 0 | |
47 | while True: | |
48 | i = (i + 1) % 256 | |
49 | j = (j + self.S[i]) % 256 | |
50 | self.S[i], self.S[j] = self.S[j], self.S[i] # swap | |
51 | ||
52 | K = self.S[(self.S[i] + self.S[j]) % 256] | |
53 | yield K | |
54 | ||
55 | ||
56 | def encrypt(self, data): | |
57 | res = b'' | |
58 | for b in data: | |
59 | res += (b ^ next(self.keystream)).to_bytes(1, byteorder = 'big', signed = False) | |
60 | return res | |
61 | ||
62 | def decrypt(self, data): | |
63 | return self.encrypt(data)⏎ |
0 | import importlib | |
1 | import importlib.util | |
2 | ||
3 | # | |
4 | ##key = name of the cipher, value=list of module names, in order of preference | |
5 | #preftable = { | |
6 | # 'DES' : ['pyCrypto','pure'], | |
7 | # 'TDES': ['pyCrypto','pure'], | |
8 | # 'AES' : ['cryptography','pyCrypto','pure'], | |
9 | # 'RC4' : ['cryptography','pyCrypto','pure'], | |
10 | # | |
11 | #} | |
12 | # | |
13 | #available_modules = ['pure'] | |
14 | # | |
15 | #if importlib.util.find_spec("cryptography") is not None: | |
16 | # #print('Found cryptography package!') | |
17 | # available_modules.append("cryptography") | |
18 | # | |
19 | #elif importlib.util.find_spec("pyCrypto") is not None: | |
20 | # #print('Found cryptography package!') | |
21 | # available_modules.append("pyCrypto") | |
22 | # | |
23 | # | |
24 | ##https://stackoverflow.com/questions/8790003/dynamically-import-a-method-in-a-file-from-a-string | |
25 | #def import_from(module, name): | |
26 | # module = __import__(module, fromlist=[name]) | |
27 | # return getattr(module, name) | |
28 | # | |
29 | # | |
30 | #def getPreferredCipher(cipherName): | |
31 | # if cipherName not in preftable: | |
32 | # raise Exception('Cipher %s doesnt have any preferences set!' % cipherName) | |
33 | # possible_prefmodule = list(set(preftable[cipherName]).intersection(set(available_modules))) | |
34 | # selected_module = None | |
35 | # for moduleName in preftable[cipherName]: | |
36 | # if moduleName in possible_prefmodule: | |
37 | # selected_module = moduleName | |
38 | # | |
39 | # if selected_module is None: | |
40 | # raise Exception('Could not find any modules to load cipher %s' % cipherName) | |
41 | # | |
42 | # | |
43 | # #print('Preferred module selected for cipher %s is %s' % (cipherName, selected_module)) | |
44 | # moduleName = 'aiosmb.crypto.%s' % cipherName | |
45 | # objectName = selected_module + cipherName | |
46 | # return import_from(moduleName , objectName) | |
47 | # | |
48 | #def getSpecificCipher(cipherName, moduleBaseName): | |
49 | # moduleName = 'aiosmb.crypto.%s' % cipherName | |
50 | # objectName = '%s%s' % (moduleBaseName, cipherName) | |
51 | # return import_from(moduleName , objectName) | |
52 | ||
53 | ||
54 | #import ciphers | |
55 | # TODO: fix the dynamic imports, currently only supporting pure-python ciphers for two reasons: | |
56 | # 1. dynamic import messes up some scripts like pyinstaller/nuitka/py2exe | |
57 | # 2. additional effort needed to support more crypto libs anyhow | |
58 | ||
59 | from msldap.crypto.AES import pureAES | |
60 | from msldap.crypto.RC4 import pureRC4 | |
61 | from msldap.crypto.DES import pureDES | |
62 | ||
63 | DES = pureDES #getPreferredCipher('DES') | |
64 | AES = pureAES #getPreferredCipher('AES') | |
65 | RC4 = pureRC4 #getPreferredCipher('RC4') | |
66 | #TDES = getPreferredCipher('TDES') |
4 | 4 | # |
5 | 5 | |
6 | 6 | import asyncio |
7 | from os import terminal_size | |
7 | 8 | import traceback |
8 | 9 | import logging |
9 | 10 | import csv |
10 | 11 | import shlex |
11 | 12 | import datetime |
12 | 13 | import copy |
14 | import typing | |
13 | 15 | |
14 | 16 | from msldap.external.aiocmd.aiocmd import aiocmd |
15 | 17 | from msldap.external.asciitree.asciitree import LeftAligned |
22 | 24 | from msldap.ldap_objects import MSADUser, MSADMachine, MSADUser_TSV_ATTRS |
23 | 25 | |
24 | 26 | from winacl.dtyp.security_descriptor import SECURITY_DESCRIPTOR |
25 | from winacl.dtyp.ace import ACCESS_ALLOWED_OBJECT_ACE, ADS_ACCESS_MASK | |
27 | from winacl.dtyp.ace import ACCESS_ALLOWED_OBJECT_ACE, ADS_ACCESS_MASK, AceFlags, ACE_OBJECT_PRESENCE | |
26 | 28 | from winacl.dtyp.sid import SID |
27 | 29 | from winacl.dtyp.guid import GUID |
30 | ||
31 | from msldap.ldap_objects.adcertificatetemplate import MSADCertificateTemplate, EX_RIGHT_CERTIFICATE_ENROLLMENT, CertificateNameFlag | |
32 | from msldap.wintypes.asn1.sdflagsrequest import SDFlagsRequest | |
28 | 33 | |
29 | 34 | |
30 | 35 | class MSLDAPClientConsole(aiocmd.PromptToolkitCmd): |
211 | 216 | traceback.print_exc() |
212 | 217 | return False |
213 | 218 | |
214 | async def do_user(self, samaccountname): | |
219 | async def do_user(self, samaccountname, to_print=True): | |
215 | 220 | """Feteches a user object based on the sAMAccountName of the user""" |
216 | 221 | try: |
217 | 222 | await self.do_ldapinfo(False) |
219 | 224 | user, err = await self.connection.get_user(samaccountname) |
220 | 225 | if err is not None: |
221 | 226 | raise err |
222 | if user is None: | |
223 | print('User not found!') | |
224 | else: | |
225 | print(user) | |
226 | ||
227 | return True | |
227 | ||
228 | if to_print is True: | |
229 | if user is None: | |
230 | print('User not found!') | |
231 | else: | |
232 | print(user) | |
233 | ||
234 | return user | |
228 | 235 | except: |
229 | 236 | traceback.print_exc() |
230 | 237 | return False |
604 | 611 | except: |
605 | 612 | traceback.print_exc() |
606 | 613 | return False |
607 | ||
614 | ||
615 | async def do_rootcas(self, to_print = True): | |
616 | """Lists Root CA certificates""" | |
617 | try: | |
618 | cas = [] | |
619 | async for ca, err in self.connection.list_root_cas(): | |
620 | if err is not None: | |
621 | raise err | |
622 | cas.append(ca) | |
623 | if to_print is True: | |
624 | print(ca) | |
625 | return cas | |
626 | except: | |
627 | traceback.print_exc() | |
628 | return False | |
629 | ||
630 | async def do_ntcas(self, to_print = True): | |
631 | """Lists NT CA certificates""" | |
632 | try: | |
633 | cas = [] | |
634 | async for ca, err in self.connection.list_ntcas(): | |
635 | if err is not None: | |
636 | raise err | |
637 | cas.append(ca) | |
638 | if to_print is True: | |
639 | print(ca) | |
640 | return cas | |
641 | except: | |
642 | traceback.print_exc() | |
643 | return False | |
644 | ||
645 | async def do_aiacas(self, to_print = True): | |
646 | """Lists AIA CA certificates""" | |
647 | try: | |
648 | cas = [] | |
649 | async for ca, err in self.connection.list_aiacas(): | |
650 | if err is not None: | |
651 | raise err | |
652 | cas.append(ca) | |
653 | if to_print is True: | |
654 | print(ca) | |
655 | return cas | |
656 | except: | |
657 | traceback.print_exc() | |
658 | return False | |
659 | ||
660 | async def do_enrollmentservices(self, to_print=True): | |
661 | """Lists AIA CA certificates""" | |
662 | try: | |
663 | services = [] | |
664 | async for srv, err in self.connection.list_enrollment_services(): | |
665 | if err is not None: | |
666 | raise err | |
667 | services.append(srv) | |
668 | if to_print is True: | |
669 | print(srv) | |
670 | return services | |
671 | except: | |
672 | traceback.print_exc() | |
673 | return False | |
674 | ||
675 | async def do_addcerttemplatenameflagaltname(self, certtemplatename, flags = None): | |
676 | """Modifyies the msPKI-Certificate-Name-Flag value of the specified certificate template and enables ENROLLEE_SUPPLIES_SUBJECT_ALT_NAME bit. If 'flags' is present then it will assign that value.""" | |
677 | try: | |
678 | template = None | |
679 | async for template, err in self.connection.list_certificate_templates(certtemplatename): | |
680 | if err is not None: | |
681 | raise err | |
682 | break | |
683 | ||
684 | if template is None: | |
685 | raise Exception("Template could not be found!") | |
686 | ||
687 | template = typing.cast(MSADCertificateTemplate, template) | |
688 | if flags is not None: | |
689 | flags = int(flags) | |
690 | else: | |
691 | flags = int(CertificateNameFlag(template.Certificate_Name_Flag) | CertificateNameFlag.ENROLLEE_SUPPLIES_SUBJECT_ALT_NAME) | |
692 | ||
693 | changes = { | |
694 | 'msPKI-Certificate-Name-Flag' : [('replace', [flags])] | |
695 | } | |
696 | ||
697 | _, err = await self.connection.modify(template.distinguishedName, changes) | |
698 | if err is not None: | |
699 | raise err | |
700 | ||
701 | print('Modify OK!') | |
702 | return True | |
703 | ||
704 | ||
705 | except: | |
706 | traceback.print_exc() | |
707 | return False | |
708 | ||
709 | async def do_addenrollmentright(self, certtemplatename, user_dn): | |
710 | """Grants enrollment rights to a user (by DN) for the specified certificate template.""" | |
711 | try: | |
712 | user_sid, err = await self.connection.get_objectsid_for_dn(user_dn) | |
713 | if err is not None: | |
714 | raise err | |
715 | ||
716 | template = None | |
717 | async for template, err in self.connection.list_certificate_templates(certtemplatename): | |
718 | if err is not None: | |
719 | raise err | |
720 | break | |
721 | ||
722 | if template is None: | |
723 | raise Exception("Template could not be found!") | |
724 | template = typing.cast(MSADCertificateTemplate, template) | |
725 | new_sd = copy.deepcopy(template.nTSecurityDescriptor) | |
726 | ace = ACCESS_ALLOWED_OBJECT_ACE() | |
727 | ace.Sid = SID.from_string(user_sid) | |
728 | ace.ObjectType = GUID.from_string(EX_RIGHT_CERTIFICATE_ENROLLMENT) | |
729 | ace.AceFlags = AceFlags(0) | |
730 | ace.Mask = ADS_ACCESS_MASK.READ_PROP | ADS_ACCESS_MASK.WRITE_PROP | ADS_ACCESS_MASK.CONTROL_ACCESS | |
731 | ace.Flags = ACE_OBJECT_PRESENCE.ACE_OBJECT_TYPE_PRESENT | |
732 | new_sd.Dacl.aces.append(ace) | |
733 | _, err = await self.connection.set_objectacl_by_dn(template.distinguishedName, new_sd.to_bytes(), flags=SDFlagsRequest.DACL_SECURITY_INFORMATION) | |
734 | if err is not None: | |
735 | raise err | |
736 | print('SD set sucessfully') | |
737 | return True | |
738 | except: | |
739 | traceback.print_exc() | |
740 | return False | |
741 | ||
742 | async def do_certtemplates(self, name = None, to_print = True): | |
743 | """Lists certificate templates""" | |
744 | try: | |
745 | services = await self.do_enrollmentservices(to_print=False) | |
746 | templates = [] | |
747 | async for template, err in self.connection.list_certificate_templates(name): | |
748 | if err is not None: | |
749 | raise err | |
750 | ||
751 | lt = None | |
752 | if template.nTSecurityDescriptor is not None: | |
753 | lt, err = await self.connection.resolv_sd(template.nTSecurityDescriptor) | |
754 | if err is not None: | |
755 | raise err | |
756 | template.sid_lookup_table = lt | |
757 | for srv in services: | |
758 | if template.name in srv.certificateTemplates: | |
759 | template.enroll_services.append('%s\\%s' % (srv.dNSHostName, srv.name)) | |
760 | ||
761 | templates.append(template) | |
762 | if to_print is True: | |
763 | print(template.prettyprint()) | |
764 | ||
765 | return templates | |
766 | except: | |
767 | traceback.print_exc() | |
768 | return False | |
769 | ||
770 | async def do_sidresolv(self, sid, to_print = True): | |
771 | """Returns the domain and username for SID""" | |
772 | try: | |
773 | domain, username, err = await self.connection.resolv_sid(sid) | |
774 | if err is not None: | |
775 | raise err | |
776 | res = '%s\\%s' % (domain, username) | |
777 | if to_print is True: | |
778 | print(res) | |
779 | return res | |
780 | except: | |
781 | traceback.print_exc() | |
782 | return False | |
783 | ||
784 | async def do_certify(self, cmd = None, username = None): | |
785 | """ADCA security test""" | |
786 | try: | |
787 | es = await self.do_enrollmentservices(to_print=False) | |
788 | if es is False: | |
789 | raise Exception('Listing enrollment Services error! %s' % es) | |
790 | if es is None: | |
791 | raise Exception('No Enrollment Services present, stopping!') | |
792 | ||
793 | templates = await self.do_certtemplates(to_print=False) | |
794 | if templates is False: | |
795 | raise Exception('Listing templates error! %s' % es) | |
796 | ||
797 | if templates is None: | |
798 | raise Exception('No templates exists!') | |
799 | ||
800 | for enrollment in es: | |
801 | print(enrollment) | |
802 | ||
803 | if cmd is not None: | |
804 | if cmd.lower().startswith('vuln') is True: | |
805 | tokengroups = None | |
806 | if username is not None: | |
807 | tokengroups, err = await self.connection.get_tokengroups_user(username) | |
808 | if err is not None: | |
809 | raise err | |
810 | ||
811 | for template in templates: | |
812 | isvuln, reason = template.is_vulnerable(tokengroups) | |
813 | if isvuln is True: | |
814 | print(reason) | |
815 | print(template) | |
816 | else: | |
817 | for template in templates: | |
818 | print(template) | |
819 | ||
820 | return True | |
821 | except: | |
822 | traceback.print_exc() | |
823 | return False | |
824 | ||
825 | async def do_whoamiraw(self): | |
826 | """Simple whoami""" | |
827 | try: | |
828 | res, err = await self.connection.whoami() | |
829 | if err is not None: | |
830 | raise err | |
831 | print(res) | |
832 | except: | |
833 | traceback.print_exc() | |
834 | return False | |
835 | ||
836 | async def do_whoami(self): | |
837 | """Full whoami""" | |
838 | try: | |
839 | res, err = await self.connection.whoamifull() | |
840 | if err is not None: | |
841 | raise err | |
842 | ||
843 | for x in res: | |
844 | if isinstance(res[x], str) is True: | |
845 | print('%s: %s' % (x, res[x])) | |
846 | elif isinstance(res[x], dict) is True: | |
847 | for k in res[x]: | |
848 | print('Group: %s (%s)' % (k,'\\'.join(res[x][k]))) | |
849 | return True | |
850 | except: | |
851 | traceback.print_exc() | |
852 | return False, None | |
853 | ||
608 | 854 | async def do_test(self): |
609 | 855 | """testing, dontuse""" |
610 | 856 | try: |
13 | 13 | from msldap.ldap_objects.adgpo import MSADGPO, MSADGPO_ATTRS |
14 | 14 | from msldap.ldap_objects.adtrust import MSADDomainTrust, MSADDomainTrust_ATTRS |
15 | 15 | from msldap.ldap_objects.adschemaentry import MSADSCHEMAENTRY_ATTRS, MSADSchemaEntry |
16 | from msldap.ldap_objects.adca import MSADCA, MSADCA_ATTRS | |
17 | from msldap.ldap_objects.adenrollmentservice import MSADEnrollmentService_ATTRS, MSADEnrollmentService | |
18 | from msldap.ldap_objects.adcertificatetemplate import MSADCertificateTemplate, MSADCertificateTemplate_ATTRS | |
16 | 19 | |
17 | 20 | __all__ = [ |
18 | 21 | 'MSADUser', |
36 | 39 | 'MSADOU_ATTRS', |
37 | 40 | 'MSADSCHEMAENTRY_ATTRS', |
38 | 41 | 'MSADSchemaEntry', |
42 | 'MSADCA', | |
43 | 'MSADCA_ATTRS', | |
44 | 'MSADEnrollmentService_ATTRS', | |
45 | 'MSADEnrollmentService', | |
46 | 'MSADCertificateTemplate', | |
47 | 'MSADCertificateTemplate_ATTRS', | |
48 | ||
39 | 49 | ]⏎ |
0 | ||
1 | from asn1crypto.x509 import Certificate | |
2 | ||
3 | MSADCA_ATTRS = ['cACertificate', 'cn', 'sn', 'distinguishedName', 'whenChanged', 'whenCreated', 'name'] | |
4 | ||
5 | class MSADCA: | |
6 | def __init__(self): | |
7 | self.location = None | |
8 | self.sn = None #str | |
9 | self.cn = None #str | |
10 | self.distinguishedName = None #dn | |
11 | self.whenChanged = None | |
12 | self.whenCreated = None | |
13 | self.cACertificate = None | |
14 | self.name = None | |
15 | ||
16 | @staticmethod | |
17 | def from_ldap(entry, location): | |
18 | adi = MSADCA() | |
19 | adi.location = location | |
20 | adi.sn = entry['attributes'].get('sn') | |
21 | adi.cn = entry['attributes'].get('cn') | |
22 | adi.distinguishedName = entry['attributes'].get('distinguishedName') | |
23 | adi.whenChanged = entry['attributes'].get('whenChanged') | |
24 | adi.whenCreated = entry['attributes'].get('whenCreated') | |
25 | adi.cACertificate = entry['attributes'].get('cACertificate') | |
26 | if adi.cACertificate is not None: | |
27 | adi.cACertificate = Certificate.load(adi.cACertificate) | |
28 | adi.name = entry['attributes'].get('name') | |
29 | ||
30 | return adi | |
31 | ||
32 | def __str__(self): | |
33 | t = '== MSADCA ==\r\n' | |
34 | for k in self.__dict__: | |
35 | t += '%s: %s\r\n' % (k, self.__dict__[k]) | |
36 | ||
37 | return t |
0 | import enum | |
1 | from winacl.dtyp.security_descriptor import SECURITY_DESCRIPTOR | |
2 | from winacl.dtyp.ace import ACEType, ACE_OBJECT_PRESENCE, ACCESS_MASK, ADS_ACCESS_MASK | |
3 | ||
4 | ||
5 | EX_RIGHT_CERTIFICATE_ENROLLMENT = "0e10c968-78fb-11d2-90d4-00c04f79dc55" | |
6 | EX_RIGHT_CERTIFICATE_AUTOENROLLMENT = "a05b8cc2-17bc-4802-a710-e7c15ab866a2" | |
7 | ||
8 | ||
9 | class CertificateNameFlag(enum.IntFlag): | |
10 | ENROLLEE_SUPPLIES_SUBJECT = 0x00000001 | |
11 | ENROLLEE_SUPPLIES_SUBJECT_ALT_NAME = 0x00010000 | |
12 | SUBJECT_ALT_REQUIRE_DOMAIN_DNS = 0x00400000 | |
13 | SUBJECT_ALT_REQUIRE_SPN = 0x00800000 | |
14 | SUBJECT_ALT_REQUIRE_DIRECTORY_GUID = 0x01000000 | |
15 | SUBJECT_ALT_REQUIRE_UPN = 0x02000000 | |
16 | SUBJECT_ALT_REQUIRE_EMAIL = 0x04000000 | |
17 | SUBJECT_ALT_REQUIRE_DNS = 0x08000000 | |
18 | SUBJECT_REQUIRE_DNS_AS_CN = 0x10000000 | |
19 | SUBJECT_REQUIRE_EMAIL = 0x20000000 | |
20 | SUBJECT_REQUIRE_COMMON_NAME = 0x40000000 | |
21 | SUBJECT_REQUIRE_DIRECTORY_PATH = 0x80000000 | |
22 | OLD_CERT_SUPPLIES_SUBJECT_AND_ALT_NAME = 0x00000008 | |
23 | ||
24 | class EnrollmentFlag(enum.IntFlag): | |
25 | INCLUDE_SYMMETRIC_ALGORITHMS = 0x00000001 | |
26 | PEND_ALL_REQUESTS = 0x00000002 | |
27 | PUBLISH_TO_KRA_CONTAINER = 0x00000004 | |
28 | PUBLISH_TO_DS = 0x00000008 | |
29 | AUTO_ENROLLMENT_CHECK_USER_DS_CERTIFICATE = 0x00000010 | |
30 | AUTO_ENROLLMENT = 0x00000020 | |
31 | PREVIOUS_APPROVAL_VALIDATE_REENROLLMENT = 0x00000040 | |
32 | USER_INTERACTION_REQUIRED = 0x00000100 | |
33 | REMOVE_INVALID_CERTIFICATE_FROM_PERSONAL_STORE = 0x00000400 | |
34 | ALLOW_ENROLL_ON_BEHALF_OF = 0x00000800 | |
35 | ADD_OCSP_NOCHECK = 0x00001000 | |
36 | ENABLE_KEY_REUSE_ON_NT_TOKEN_KEYSET_STORAGE_FULL = 0x00002000 | |
37 | NOREVOCATIONINFOINISSUEDCERTS = 0x00004000 | |
38 | INCLUDE_BASIC_CONSTRAINTS_FOR_EE_CERTS = 0x00008000 | |
39 | ALLOW_PREVIOUS_APPROVAL_KEYBASEDRENEWAL_VALIDATE_REENROLLMENT = 0x00010000 | |
40 | ISSUANCE_POLICIES_FROM_REQUEST = 0x00020000 | |
41 | SKIP_AUTO_RENEWAL = 0x00040000 | |
42 | ||
43 | class PrivateKeyFlag(enum.IntFlag): | |
44 | REQUIRE_PRIVATE_KEY_ARCHIVAL = 0x00000001 | |
45 | EXPORTABLE_KEY = 0x00000010 | |
46 | STRONG_KEY_PROTECTION_REQUIRED = 0x00000020 | |
47 | REQUIRE_ALTERNATE_SIGNATURE_ALGORITHM = 0x00000040 | |
48 | REQUIRE_SAME_KEY_RENEWAL = 0x00000080 | |
49 | USE_LEGACY_PROVIDER = 0x00000100 | |
50 | ATTEST_REQUIRED = 0x000002000 | |
51 | ATTEST_PREFERRED = 0x000001000 | |
52 | HELLO_LOGON_KEY = 0x00200000 | |
53 | ||
54 | EKU_CLIENT_AUTHENTICATION_OID = "1.3.6.1.5.5.7.3.2" | |
55 | EKU_PKINIT_CLIENT_AUTHENTICATION_OID = "1.3.6.1.5.2.3.4" | |
56 | EKU_SMART_CARD_LOGON_OID = "1.3.6.1.4.1.311.20.2.2" | |
57 | EKU_ANY_PURPOSE_OID = "2.5.29.37.0" | |
58 | EKU_CERTIFICATE_REQUEST_AGENT_OID = "1.3.6.1.4.1.311.20.2.1" | |
59 | ||
60 | EKUS_NAMES = { | |
61 | "1.3.6.1.4.1.311.2.6.1": "SpcRelaxedPEMarkerCheck", | |
62 | "1.3.6.1.4.1.311.2.6.2": "SpcEncryptedDigestRetryCount", | |
63 | "1.3.6.1.4.1.311.10.3.6": "Windows System Component Verification", | |
64 | "1.3.6.1.4.1.311.10.3.22": "Protected Process Light Verification", | |
65 | "1.3.6.1.4.1.311.10.3.27": "Preview Build Signing", | |
66 | "1.3.6.1.4.1.311.10.3.1": "Microsoft Trust List Signing", | |
67 | "1.3.6.1.4.1.311.10.3.2": "Microsoft Time Stamping", | |
68 | "1.3.6.1.4.1.311.10.3.7": "OEM Windows System Component Verification", | |
69 | "1.3.6.1.4.1.311.10.3.13": "Lifetime Signing", | |
70 | "1.3.6.1.4.1.311.10.3.11": "Key Recovery", | |
71 | "1.3.6.1.4.1.311.10.3.23": "Windows TCB Component", | |
72 | "1.3.6.1.4.1.311.10.3.25": "Windows Third Party Application Component", | |
73 | "1.3.6.1.4.1.311.10.3.26": "Windows Software Extension Verification", | |
74 | "1.3.6.1.4.1.311.10.3.8": "Embedded Windows System Component Verification", | |
75 | "1.3.6.1.4.1.311.10.3.20": "Windows Kits Component", | |
76 | "1.3.6.1.4.1.311.10.3.5": "Windows Hardware Driver Verification", | |
77 | "1.3.6.1.4.1.311.10.3.39": "Windows Hardware Driver Extended Verification", | |
78 | "1.3.6.1.4.1.311.10.3.5.1": "Windows Hardware Driver Attested Verification", | |
79 | "1.3.6.1.4.1.311.10.3.4.1": "File Recovery", | |
80 | "1.3.6.1.4.1.311.10.3.30": "Disallowed List", | |
81 | "1.3.6.1.4.1.311.10.3.19": "Revoked List Signer", | |
82 | "1.3.6.1.4.1.311.10.3.21": "Windows RT Verification", | |
83 | "1.3.6.1.4.1.311.10.3.10": "Qualified Subordination", | |
84 | "1.3.6.1.4.1.311.10.3.12": "Document Signing", | |
85 | "1.3.6.1.4.1.311.10.3.24": "Protected Process Verification", | |
86 | "1.3.6.1.4.1.311.10.3.4": "Encrypting File System", | |
87 | "1.3.6.1.4.1.311.10.3.9": "Root List Signer", | |
88 | "1.3.6.1.4.1.311.10.5.1": "Digital Rights", | |
89 | "1.3.6.1.4.1.311.10.6.2": "License Server Verification", | |
90 | "1.3.6.1.4.1.311.10.6.1": "Key Pack Licenses", | |
91 | EKU_SMART_CARD_LOGON_OID: "Smart Card Logon", | |
92 | EKU_CERTIFICATE_REQUEST_AGENT_OID: "Certificate Request Agent", | |
93 | "1.3.6.1.4.1.311.20.1": "CTL Usage", | |
94 | "1.3.6.1.4.1.311.21.6": "Key Recovery Agent", | |
95 | "1.3.6.1.4.1.311.21.19": "Directory Service Email Replication", | |
96 | "1.3.6.1.4.1.311.21.5": "Private Key Archival", | |
97 | "1.3.6.1.4.1.311.61.1.1": "Kernel Mode Code Signing", | |
98 | "1.3.6.1.4.1.311.61.4.1": "Early Launch Antimalware Driver", | |
99 | "1.3.6.1.4.1.311.61.5.1": "HAL Extension", | |
100 | "1.3.6.1.4.1.311.64.1.1": "Domain Name System (DNS) Server Trust", | |
101 | "1.3.6.1.4.1.311.76.6.1": "Windows Update", | |
102 | "1.3.6.1.4.1.311.76.3.1": "Windows Store", | |
103 | "1.3.6.1.4.1.311.76.5.1": "Dynamic Code Generator", | |
104 | "1.3.6.1.4.1.311.76.8.1": "Microsoft Publisher", | |
105 | "1.3.6.1.4.1.311.80.1": "Document Encryption", | |
106 | EKU_PKINIT_CLIENT_AUTHENTICATION_OID: "PKINIT Client Authentication", | |
107 | "1.3.6.1.5.2.3.5": "KDC Authentication", | |
108 | "1.3.6.1.5.5.7.3.7": "IP security user", | |
109 | EKU_CLIENT_AUTHENTICATION_OID: "Client Authentication", | |
110 | "1.3.6.1.5.5.7.3.9": "OCSP Signing", | |
111 | "1.3.6.1.5.5.7.3.3": "Code Signing", | |
112 | "1.3.6.1.5.5.7.3.4": "Secure Email", | |
113 | "1.3.6.1.5.5.7.3.5": "IP security end system", | |
114 | "1.3.6.1.5.5.7.3.6": "IP security tunnel termination", | |
115 | "1.3.6.1.5.5.8.2.2": "IP security IKE intermediate", | |
116 | "1.3.6.1.5.5.7.3.8": "Time Stamping", | |
117 | "1.3.6.1.5.5.7.3.1": "Server Authentication", | |
118 | EKU_ANY_PURPOSE_OID: "Any Purpose", | |
119 | "2.23.133.8.1": "Endorsement Key Certificate", | |
120 | "2.23.133.8.2": "Platform Certificate", | |
121 | "2.23.133.8.3": "Attestation Identity Key Certificate", | |
122 | } | |
123 | ||
124 | ENROLLMENT_FLAGS_NAMES = { | |
125 | EnrollmentFlag.INCLUDE_SYMMETRIC_ALGORITHMS: "INCLUDE_SYMMETRIC_ALGORITHMS", | |
126 | EnrollmentFlag.PEND_ALL_REQUESTS: "PEND_ALL_REQUESTS", | |
127 | EnrollmentFlag.PUBLISH_TO_KRA_CONTAINER: "PUBLISH_TO_KRA_CONTAINER", | |
128 | EnrollmentFlag.PUBLISH_TO_DS: "PUBLISH_TO_DS", | |
129 | EnrollmentFlag.AUTO_ENROLLMENT_CHECK_USER_DS_CERTIFICATE: "AUTO_ENROLLMENT_CHECK_USER_DS_CERTIFICATE", | |
130 | EnrollmentFlag.AUTO_ENROLLMENT: "AUTO_ENROLLMENT", | |
131 | EnrollmentFlag.PREVIOUS_APPROVAL_VALIDATE_REENROLLMENT: "PREVIOUS_APPROVAL_VALIDATE_REENROLLMENT", | |
132 | EnrollmentFlag.USER_INTERACTION_REQUIRED: "USER_INTERACTION_REQUIRED", | |
133 | EnrollmentFlag.REMOVE_INVALID_CERTIFICATE_FROM_PERSONAL_STORE: "REMOVE_INVALID_CERTIFICATE_FROM_PERSONAL_STORE", | |
134 | EnrollmentFlag.ALLOW_ENROLL_ON_BEHALF_OF: "ALLOW_ENROLL_ON_BEHALF_OF", | |
135 | EnrollmentFlag.ADD_OCSP_NOCHECK: "ADD_OCSP_NOCHECK", | |
136 | EnrollmentFlag.ENABLE_KEY_REUSE_ON_NT_TOKEN_KEYSET_STORAGE_FULL: "ENABLE_KEY_REUSE_ON_NT_TOKEN_KEYSET_STORAGE_FULL", | |
137 | EnrollmentFlag.NOREVOCATIONINFOINISSUEDCERTS: "NOREVOCATIONINFOINISSUEDCERTS", | |
138 | EnrollmentFlag.INCLUDE_BASIC_CONSTRAINTS_FOR_EE_CERTS: "INCLUDE_BASIC_CONSTRAINTS_FOR_EE_CERTS", | |
139 | EnrollmentFlag.ALLOW_PREVIOUS_APPROVAL_KEYBASEDRENEWAL_VALIDATE_REENROLLMENT: "ALLOW_PREVIOUS_APPROVAL_KEYBASEDRENEWAL_VALIDATE_REENROLLMENT", | |
140 | EnrollmentFlag.ISSUANCE_POLICIES_FROM_REQUEST: "ISSUANCE_POLICIES_FROM_REQUEST", | |
141 | EnrollmentFlag.SKIP_AUTO_RENEWAL: "SKIP_AUTO_RENEWAL", | |
142 | } | |
143 | ||
144 | MSADCertificateTemplate_ATTRS = [ | |
145 | 'cn', 'sn', 'distinguishedName', 'name', 'msPKI-RA-Application-Policies', 'msPKI-Certificate-Application-Policy', | |
146 | 'msPKI-Template-Schema-Version', 'msPKI-Certificate-Name-Flag', 'msPKI-Enrollment-Flag', 'msPKI-RA-Signature', | |
147 | 'msPKI-Private-Key-Flag', 'pKIExtendedKeyUsage', 'nTSecurityDescriptor' | |
148 | ] | |
149 | ||
150 | class MSADCertificateTemplate: | |
151 | def __init__(self): | |
152 | self.sn = None #str | |
153 | self.cn = None #str | |
154 | self.distinguishedName = None #dn | |
155 | self.name = None | |
156 | self.RA_Application_Policies = None | |
157 | self.Certificate_Application_Policy = None | |
158 | self.Template_Schema_Version = None | |
159 | self.Certificate_Name_Flag = None | |
160 | self.Enrollment_Flag = None | |
161 | self.RA_Signature = None | |
162 | self.Private_Key_Flag = None | |
163 | self.pKIExtendedKeyUsage = None | |
164 | self.nTSecurityDescriptor:SECURITY_DESCRIPTOR = None | |
165 | ||
166 | self.vulns = [] | |
167 | self.enroll_sids = set() | |
168 | self.autoenroll_sids = set() | |
169 | self.write_owner_sids = set() | |
170 | self.write_dacl_sids = set() | |
171 | self.write_property_sids = set() | |
172 | self.fullcontrol_sids = set() | |
173 | self.allextendedrights_sids = set() | |
174 | self.sid_lookup_table = None | |
175 | self.enroll_services = [] | |
176 | ||
177 | @staticmethod | |
178 | def from_ldap(entry): | |
179 | adi = MSADCertificateTemplate() | |
180 | adi.sn = entry['attributes'].get('sn') | |
181 | adi.cn = entry['attributes'].get('cn') | |
182 | adi.distinguishedName = entry['attributes'].get('distinguishedName') | |
183 | adi.name = entry['attributes'].get('name') | |
184 | adi.RA_Application_Policies = entry['attributes'].get('msPKI-RA-Application-Policies') | |
185 | adi.Certificate_Application_Policy = entry['attributes'].get('msPKI-Certificate-Application-Policy') | |
186 | adi.Template_Schema_Version = entry['attributes'].get('msPKI-Template-Schema-Version') | |
187 | adi.Certificate_Name_Flag = entry['attributes'].get('msPKI-Certificate-Name-Flag') | |
188 | adi.Enrollment_Flag = entry['attributes'].get('msPKI-Enrollment-Flag') | |
189 | adi.RA_Signature = entry['attributes'].get('msPKI-RA-Signature') | |
190 | adi.Private_Key_Flag = entry['attributes'].get('msPKI-Private-Key-Flag') | |
191 | adi.pKIExtendedKeyUsage = entry['attributes'].get('pKIExtendedKeyUsage', []) | |
192 | adi.nTSecurityDescriptor = entry['attributes'].get('nTSecurityDescriptor') | |
193 | if adi.nTSecurityDescriptor is not None: | |
194 | adi.nTSecurityDescriptor = SECURITY_DESCRIPTOR.from_bytes(adi.nTSecurityDescriptor) | |
195 | ||
196 | adi.calc_aces() | |
197 | return adi | |
198 | ||
199 | def allows_authentication(self): | |
200 | return self.can_be_used_for_any_purpose() or len(set([EKU_CLIENT_AUTHENTICATION_OID, EKU_SMART_CARD_LOGON_OID, EKU_PKINIT_CLIENT_AUTHENTICATION_OID]).intersection(set(self.pKIExtendedKeyUsage))) > 0 | |
201 | ||
202 | def can_be_used_for_any_purpose(self): | |
203 | return len(self.pKIExtendedKeyUsage) == 0 or EKU_ANY_PURPOSE_OID in self.pKIExtendedKeyUsage | |
204 | ||
205 | def requires_manager_approval(self): | |
206 | return EnrollmentFlag.PEND_ALL_REQUESTS in EnrollmentFlag(self.Enrollment_Flag) | |
207 | ||
208 | def requires_authorized_signatures(self): | |
209 | return self.RA_Signature != None and self.RA_Signature > 0 | |
210 | ||
211 | def allows_to_specify_san(self): | |
212 | return CertificateNameFlag(self.Certificate_Name_Flag) & CertificateNameFlag.ENROLLEE_SUPPLIES_SUBJECT > 0 | |
213 | ||
214 | def allows_to_request_agent_certificate(self): | |
215 | return EKU_CERTIFICATE_REQUEST_AGENT_OID in self.pKIExtendedKeyUsage | |
216 | ||
217 | def allows_to_use_agent_certificate(self): | |
218 | return self.Template_Schema_Version == 1 \ | |
219 | or ( | |
220 | self.Template_Schema_Version > 1 \ | |
221 | and self.RA_Signature == 1 \ | |
222 | and EKU_CERTIFICATE_REQUEST_AGENT_OID in self.RA_Application_Policies | |
223 | ) | |
224 | ||
225 | def is_vulnerable(self, tokengroups = None): | |
226 | def isLowPrivSid(sid): | |
227 | sid = str(sid) | |
228 | if sid in ['S-1-1-0', 'S-1-5-11']: | |
229 | return True | |
230 | if sid.startswith('S-1-5-21-') is True and sid.rsplit('-',1)[1] in ['513','515','545']: | |
231 | return True | |
232 | return False | |
233 | ||
234 | if tokengroups is None: | |
235 | if isLowPrivSid(str(self.nTSecurityDescriptor.Owner)) is True: | |
236 | return True, 'Owner is low priv user' | |
237 | ||
238 | else: | |
239 | if str(self.nTSecurityDescriptor.Owner) in tokengroups: | |
240 | return True, 'Owner can be controlled by current user' | |
241 | ||
242 | lowprivcanenroll = False | |
243 | if tokengroups is None: | |
244 | if any(isLowPrivSid(str(sid)) for sid in self.fullcontrol_sids) is True: | |
245 | return True, 'Lowpriv SID has full control' | |
246 | ||
247 | if any(isLowPrivSid(str(sid)) for sid in self.write_dacl_sids) is True: | |
248 | return True, 'Lowpriv SID can write DACLs' | |
249 | ||
250 | if any(isLowPrivSid(str(sid)) for sid in self.write_owner_sids) is True: | |
251 | return True, 'Lowpriv SID can change Owner' | |
252 | ||
253 | if any(isLowPrivSid(str(sid)) for sid in self.write_property_sids) is True: | |
254 | return True, 'Lowpriv SID can write property' | |
255 | ||
256 | if any(isLowPrivSid(str(sid)) for sid in self.enroll_sids) is True: | |
257 | lowprivcanenroll = True | |
258 | ||
259 | if any(isLowPrivSid(str(sid)) for sid in self.allextendedrights_sids) is True: | |
260 | lowprivcanenroll = True | |
261 | ||
262 | else: | |
263 | if len(self.enroll_sids.intersection(set(tokengroups))) > 0 or len(self.allextendedrights_sids.intersection(set(tokengroups))) > 0: | |
264 | lowprivcanenroll = True | |
265 | ||
266 | if len(self.write_dacl_sids.intersection(set(tokengroups))) > 0: | |
267 | return True, 'Current user can write DACLs' | |
268 | ||
269 | if len(self.write_owner_sids.intersection(set(tokengroups))) > 0: | |
270 | return True, 'Current user can change Owner' | |
271 | ||
272 | if len(self.write_property_sids.intersection(set(tokengroups))) > 0: | |
273 | return True, 'Current user can write property' | |
274 | ||
275 | if len(self.fullcontrol_sids.intersection(set(tokengroups))) > 0: | |
276 | return True, 'Current user has full control' | |
277 | ||
278 | if self.requires_manager_approval() is True: | |
279 | return False, 'Needs manager approval' | |
280 | ||
281 | if self.requires_authorized_signatures() is True: | |
282 | return False, 'Needs authorized signature' | |
283 | ||
284 | if self.allows_authentication() and lowprivcanenroll and self.allows_to_specify_san(): | |
285 | return True, 'Enrollee supplies subject' | |
286 | ||
287 | if lowprivcanenroll and self.allows_to_use_agent_certificate() and self.allows_to_request_agent_certificate(): | |
288 | return True, 'Certificate request agent' | |
289 | ||
290 | return False, 'No match found' | |
291 | ||
292 | def calc_aces(self): | |
293 | if self.nTSecurityDescriptor is None: | |
294 | return | |
295 | for ace in self.nTSecurityDescriptor.Dacl.aces: | |
296 | if ace.AceType != ACEType.ACCESS_ALLOWED_OBJECT_ACE_TYPE and ace.AceType != ACEType.ACCESS_ALLOWED_ACE_TYPE: | |
297 | continue | |
298 | ||
299 | if ace.AceType == ACEType.ACCESS_ALLOWED_OBJECT_ACE_TYPE: | |
300 | if str(ace.ObjectType) == EX_RIGHT_CERTIFICATE_ENROLLMENT: | |
301 | self.enroll_sids.add(str(ace.Sid)) | |
302 | elif str(ace.ObjectType) == EX_RIGHT_CERTIFICATE_AUTOENROLLMENT: | |
303 | self.autoenroll_sids.add(str(ace.Sid)) | |
304 | elif ADS_ACCESS_MASK.CONTROL_ACCESS in ADS_ACCESS_MASK(ace.Mask) and ace.ObjectType is None: | |
305 | self.allextendedrights_sids.add(str(ace.Sid)) | |
306 | continue | |
307 | ||
308 | if ADS_ACCESS_MASK.GENERIC_ALL in ADS_ACCESS_MASK(ace.Mask): | |
309 | self.fullcontrol_sids.add(str(ace.Sid)) | |
310 | ||
311 | if ADS_ACCESS_MASK.WRITE_DACL in ADS_ACCESS_MASK(ace.Mask): | |
312 | self.write_dacl_sids.add(str(ace.Sid)) | |
313 | ||
314 | if ADS_ACCESS_MASK.WRITE_OWNER in ADS_ACCESS_MASK(ace.Mask): | |
315 | self.write_owner_sids.add(str(ace.Sid)) | |
316 | ||
317 | if ADS_ACCESS_MASK.WRITE_PROP in ADS_ACCESS_MASK(ace.Mask): | |
318 | self.write_property_sids.add(str(ace.Sid)) | |
319 | ||
320 | if ADS_ACCESS_MASK.CONTROL_ACCESS in ADS_ACCESS_MASK(ace.Mask): | |
321 | self.allextendedrights_sids.add(str(ace.Sid)) | |
322 | ||
323 | @property | |
324 | def is_enabled(self): | |
325 | return len(self.enroll_services) > 0 | |
326 | ||
327 | def __str__(self): | |
328 | t = '== MSADCertificateTemplate ==\r\n' | |
329 | for k in self.__dict__: | |
330 | t += '%s: %s\r\n' % (k, self.__dict__[k]) | |
331 | ||
332 | return t | |
333 | ||
334 | def prettyprint(self): | |
335 | def print_sids(buffer, sidlist, offset = 6): | |
336 | for sid in sidlist: | |
337 | if self.sid_lookup_table is not None and sid in self.sid_lookup_table: | |
338 | buffer += '%s%s\\%s [%s]\r\n' % (offset*' ', self.sid_lookup_table[sid][0], self.sid_lookup_table[sid][1], sid) | |
339 | else: | |
340 | buffer += '%s%s\r\n' % (offset*' ', sid) | |
341 | return buffer | |
342 | ||
343 | t = '== MSADCertificateTemplate ==\r\n' | |
344 | t += "Name: %s\r\n" % self.name | |
345 | t += 'distinguishedName: %s\r\n' % self.distinguishedName | |
346 | t += "Schema Version: %s\r\n" % self.Template_Schema_Version | |
347 | ||
348 | if self.enroll_services: | |
349 | t += "Enroll Services: %s\r\n" % ", ".join(self.enroll_services) | |
350 | ||
351 | if len(self.vulns) > 0: | |
352 | t += "Vulnerabilities: %s\r\n" % ", ".join(self.vulns) | |
353 | ||
354 | if self.Certificate_Name_Flag is not None: | |
355 | t += "msPKI-Certificate-Name-Flag: %s\r\n" % str(CertificateNameFlag(self.Certificate_Name_Flag)).split('.',1)[1].replace('|',', ') | |
356 | if self.Enrollment_Flag is not None: | |
357 | t += "msPKI-Enrollment-Flag: %s\r\n" % str(EnrollmentFlag(self.Enrollment_Flag)).split('.',1)[1].replace('|',', ') | |
358 | if self.RA_Signature is not None: | |
359 | t += "msPKI-RA-Signature: %s\r\n" % self.RA_Signature | |
360 | if self.pKIExtendedKeyUsage is not None: | |
361 | t += "pKIExtendedKeyUsage: %s\r\n" % ", ".join([EKUS_NAMES.get(oid, oid) for oid in self.pKIExtendedKeyUsage]) | |
362 | if self.Certificate_Application_Policy is not None: | |
363 | t += "msPKI-Certificate-Application-Policy: %s\r\n" % ", ".join([EKUS_NAMES.get(oid, oid) for oid in self.Certificate_Application_Policy]) | |
364 | if self.RA_Application_Policies is not None: | |
365 | t += "msPKI-RA-Application-Policy: %s\r\n" % ", ".join([EKUS_NAMES.get(oid, oid) for oid in self.RA_Application_Policies]) | |
366 | ||
367 | t += "Permissions\r\n" | |
368 | t += " Enrollment Permissions\r\n" | |
369 | if len(self.enroll_sids) > 0: | |
370 | t += " Enrollment Rights\r\n" | |
371 | t = print_sids(t, self.enroll_sids) | |
372 | ||
373 | if len(self.autoenroll_sids) > 0: | |
374 | t += " AutoEnrollment Rights\r\n" | |
375 | t = print_sids(t, self.autoenroll_sids) | |
376 | ||
377 | if len(self.allextendedrights_sids) > 0: | |
378 | t += " All Extended Rights\r\n" | |
379 | t = print_sids(t, self.allextendedrights_sids) | |
380 | ||
381 | t+= " Object Control Permissions\r\n" | |
382 | t+= ' Owner\r\n' | |
383 | sid = str(self.nTSecurityDescriptor.Owner) | |
384 | if self.sid_lookup_table is not None and sid in self.sid_lookup_table: | |
385 | t += ' %s\\%s [%s]\r\n' % (self.sid_lookup_table[sid][0], self.sid_lookup_table[sid][1], sid) | |
386 | else: | |
387 | t += " %s\r\n" % sid | |
388 | ||
389 | if len(self.fullcontrol_sids) > 0: | |
390 | t+= " Full Control\r\n" | |
391 | t = print_sids(t, self.fullcontrol_sids) | |
392 | ||
393 | t += " Write Owner\r\n" | |
394 | t = print_sids(t, self.write_owner_sids) | |
395 | ||
396 | t += " Write DACL\r\n" | |
397 | t = print_sids(t, self.write_dacl_sids) | |
398 | ||
399 | t += " Write Property\r\n" | |
400 | t = print_sids(t, self.write_property_sids) | |
401 | ||
402 | t += " SDDL\r\n" | |
403 | t += self.nTSecurityDescriptor.to_sddl() | |
404 | t += '\r\n' | |
405 | return t | |
406 | ||
407 | def __str__(self): | |
408 | return self.prettyprint() |
0 | ||
1 | ||
2 | from msldap.commons.utils import print_cert | |
3 | from asn1crypto.x509 import Certificate | |
4 | ||
5 | MSADEnrollmentService_ATTRS = ['cACertificate', 'msPKI-Enrollment-Servers', 'dNSHostName', 'cn', 'sn', 'distinguishedName', 'whenChanged', 'whenCreated', 'name', 'displayName', 'cACertificateDN', 'certificateTemplates'] | |
6 | ||
7 | class MSADEnrollmentService: | |
8 | def __init__(self): | |
9 | self.sn = None #str | |
10 | self.cn = None #str | |
11 | self.distinguishedName = None #dn | |
12 | self.name = None | |
13 | self.displayName = None | |
14 | self.cACertificate = None | |
15 | self.cACertificateDN = None | |
16 | self.dNSHostName = None | |
17 | self.certificateTemplates = [] | |
18 | self.enrollmentServers = [] | |
19 | ||
20 | @staticmethod | |
21 | def from_ldap(entry): | |
22 | adi = MSADEnrollmentService() | |
23 | adi.sn = entry['attributes'].get('sn') | |
24 | adi.cn = entry['attributes'].get('cn') | |
25 | adi.distinguishedName = entry['attributes'].get('distinguishedName') | |
26 | adi.cACertificate = entry['attributes'].get('cACertificate') | |
27 | if adi.cACertificate is not None: | |
28 | adi.cACertificate = Certificate.load(adi.cACertificate) | |
29 | adi.name = entry['attributes'].get('name') | |
30 | adi.displayName = entry['attributes'].get('displayName') | |
31 | adi.dNSHostName = entry['attributes'].get('dNSHostName') | |
32 | adi.cACertificateDN = entry['attributes'].get('cACertificateDN') | |
33 | adi.certificateTemplates = entry['attributes'].get('certificateTemplates') | |
34 | for serverdef in entry['attributes'].get('msPKI-Enrollment-Servers', []): | |
35 | adi.enrollmentServers.append(serverdef.split('\n')[3]) | |
36 | return adi | |
37 | ||
38 | def __str__(self): | |
39 | t = '== MSADEnrollmentService ==\r\n' | |
40 | t += "Name: %s\r\n" % self.name | |
41 | t += "DNS name: %s\r\n" % self.dNSHostName | |
42 | t += "Templates: %s\r\n" % ', '.join(self.certificateTemplates) | |
43 | if len(self.enrollmentServers) > 0: | |
44 | t += "Web services: %s\r\n" % ", ".join(self.enrollmentServers) | |
45 | t += "Certificate: \r\n%s\r\n" % print_cert(self.cACertificate.native, 2) | |
46 | ||
47 | return t⏎ |
1 | 1 | import enum |
2 | 2 | import asyncio |
3 | 3 | import ipaddress |
4 | import traceback | |
4 | 5 | |
5 | 6 | from msldap import logger |
6 | 7 | |
7 | 8 | from asysocks.client import SOCKSClient |
8 | 9 | from asysocks.common.comms import SocksQueueComms |
9 | 10 | from msldap.protocol.utils import calcualte_length |
11 | from msldap.commons.target import LDAPProtocol | |
10 | 12 | |
11 | 13 | |
12 | 14 | class SocksProxyConnection: |
43 | 45 | await self.disconnect() |
44 | 46 | |
45 | 47 | def get_peer_certificate(self): |
46 | raise Exception('Not yet implemented! SSL implementation on socks is missing!') | |
47 | return self.writer.get_extra_info('socket').getpeercert(True) | |
48 | return self.client.get_peercert() | |
48 | 49 | |
49 | 50 | def get_one_message(self,data): |
50 | 51 | if len(data) < 6: |
101 | 102 | |
102 | 103 | """ |
103 | 104 | try: |
105 | wrap_ssl = False | |
106 | if self.target.proto == LDAPProtocol.SSL: | |
107 | wrap_ssl = True | |
104 | 108 | self.out_queue = asyncio.Queue() |
105 | 109 | self.in_queue = asyncio.Queue() |
106 | 110 | |
107 | 111 | self.proxy_in_queue = asyncio.Queue() |
108 | comms = SocksQueueComms(self.out_queue, self.proxy_in_queue) | |
112 | comms = SocksQueueComms(self.out_queue, self.proxy_in_queue, wrap_ssl=wrap_ssl) | |
109 | 113 | |
110 | 114 | self.target.proxy.target[-1].endpoint_ip = self.target.host if self.target.serverip is None else self.target.serverip |
111 | 115 | self.target.proxy.target[-1].endpoint_port = int(self.target.port) |
112 | 116 | self.target.proxy.target[-1].endpoint_timeout = None #TODO: maybe implement endpoint timeout? |
113 | 117 | self.target.proxy.target[-1].timeout = self.target.timeout |
114 | 118 | self.client = SOCKSClient(comms, self.target.proxy.target) |
115 | self.proxy_task = asyncio.create_task(self.client.run()) | |
119 | proxy_coro = await self.client.run(True) | |
120 | self.proxy_task = asyncio.create_task(proxy_coro) | |
121 | await self.client.proxy_running_evt.wait() | |
116 | 122 | self.handle_in_task = asyncio.create_task(self.handle_in_q()) |
117 | 123 | return True, None |
118 | 124 | except Exception as e: |
125 | traceback.print_exc() | |
119 | 126 | return False, e |
120 | 127 |
13 | 13 | from msldap import logger |
14 | 14 | from msldap.protocol.utils import calcualte_length |
15 | 15 | |
16 | from pyodidewsnet.client import WSNetworkTCP | |
16 | from wsnet.pyodide.client import WSNetworkTCP | |
17 | 17 | |
18 | 18 | |
19 | 19 |
20 | 20 | elif value == '*': |
21 | 21 | return Filter({ |
22 | 22 | 'present' : AttributeDescription(attr.encode()) |
23 | }) | |
24 | ||
25 | elif value.startswith('*') and value.endswith('*'): | |
26 | return Filter({ | |
27 | 'substrings' : SubstringFilter({ | |
28 | 'type' : attr.encode(), | |
29 | 'substrings' : Substrings([ | |
30 | Substring({ | |
31 | 'any' : value[1:-1].encode() | |
32 | }) | |
33 | ]) | |
34 | }) | |
23 | 35 | }) |
24 | 36 | |
25 | 37 | elif value.startswith('*') is True: |
92 | 92 | def list_bytes_one_enc(x): |
93 | 93 | return x |
94 | 94 | |
95 | def bytes2timedelta(x): | |
96 | return int2timedelta([int.from_bytes(x[0], byteorder='little', signed=True)]) | |
97 | ||
95 | 98 | def int2timedelta(x): |
96 | 99 | x = int(x[0]) |
97 | 100 | if x == '-9223372036854775808': |
165 | 168 | t.append(ts2dt((a, None))) |
166 | 169 | return t |
167 | 170 | |
171 | def list_ts2dt_one(x): | |
172 | return ts2dt(x[0]) | |
168 | 173 | |
169 | 174 | LDAP_ATTRIBUTE_TYPES = { |
170 | 175 | 'supportedCapabilities' : list_str, |
274 | 279 | 'unicodePwd' : list_str_one, |
275 | 280 | 'ms-Mcs-AdmPwd' : list_str_one, |
276 | 281 | 'msDS-AllowedToActOnBehalfOfOtherIdentity' : list_bytes_one, |
282 | 'cACertificate': list_bytes_one, | |
283 | 'certificateTemplates': list_str, | |
284 | 'cACertificateDN': list_str_one, | |
285 | 'msPKI-Enrollment-Servers': list_str, # it's actually a 5-part multi string split by '\n' | |
286 | 'revision' : list_int_one, | |
287 | 'pKIKeyUsage' : list_bytes_one, | |
288 | 'pKIDefaultKeySpec' : list_str_one, | |
289 | 'pKIMaxIssuingDepth' : list_int_one, | |
290 | 'pKICriticalExtensions' : list_str_one, | |
291 | 'pKIExpirationPeriod' : bytes2timedelta, | |
292 | 'pKIOverlapPeriod' : bytes2timedelta, | |
293 | 'pKIExtendedKeyUsage' : list_str, | |
294 | 'msPKI-RA-Signature' : list_int_one, | |
295 | 'msPKI-Enrollment-Flag' : list_int_one, | |
296 | 'msPKI-Private-Key-Flag' : list_int_one, | |
297 | 'msPKI-Certificate-Name-Flag' : list_int_one, | |
298 | 'msPKI-Minimal-Key-Size' : list_int_one, | |
299 | 'msPKI-Template-Schema-Version': list_int_one, | |
300 | 'msPKI-Template-Minor-Revision': list_int_one, | |
301 | 'msPKI-Cert-Template-OID' : list_str_one, | |
302 | 'msPKI-Certificate-Application-Policy' : list_str, | |
303 | 'msPKI-RA-Application-Policies' : list_str, #I'm guessing here | |
277 | 304 | } |
278 | 305 | |
279 | 306 | LDAP_ATTRIBUTE_TYPES_ENC = { |
294 | 321 | 'member' : list_str_enc, |
295 | 322 | 'msDS-AllowedToActOnBehalfOfOtherIdentity' : list_bytes_one_enc, |
296 | 323 | 'nTSecurityDescriptor' : list_bytes_one_enc, |
324 | 'msPKI-Certificate-Name-Flag' : list_int_one_enc | |
297 | 325 | } |
298 | 326 | |
299 | 327 | def encode_attributes(x): |
0 | 0 | Metadata-Version: 1.2 |
1 | 1 | Name: msldap |
2 | Version: 0.3.30 | |
2 | Version: 0.3.38 | |
3 | 3 | Summary: Python library to play with MS LDAP |
4 | 4 | Home-page: https://github.com/skelsec/msldap |
5 | 5 | Author: Tamas Jos |
56 | 56 | msldap/commons/target.py |
57 | 57 | msldap/commons/url.py |
58 | 58 | msldap/commons/utils.py |
59 | msldap/crypto/AES.py | |
60 | msldap/crypto/BASE.py | |
61 | msldap/crypto/DES.py | |
62 | msldap/crypto/MD4.py | |
63 | msldap/crypto/RC4.py | |
64 | msldap/crypto/TDES.py | |
65 | msldap/crypto/__init__.py | |
66 | msldap/crypto/hashing.py | |
67 | msldap/crypto/symmetric.py | |
68 | msldap/crypto/pure/__init__.py | |
69 | msldap/crypto/pure/AES/AES.py | |
70 | msldap/crypto/pure/AES/__init__.py | |
71 | msldap/crypto/pure/AES/blockfeeder.py | |
72 | msldap/crypto/pure/AES/util.py | |
73 | msldap/crypto/pure/DES/DES.py | |
74 | msldap/crypto/pure/DES/__init__.py | |
75 | msldap/crypto/pure/RC4/RC4.py | |
76 | msldap/crypto/pure/RC4/__init__.py | |
77 | 59 | msldap/examples/__init__.py |
78 | 60 | msldap/examples/msldapclient.py |
79 | 61 | msldap/examples/msldapcompdnslist.py |
90 | 72 | msldap/external/asciitree/asciitree/traversal.py |
91 | 73 | msldap/external/asciitree/asciitree/util.py |
92 | 74 | msldap/ldap_objects/__init__.py |
75 | msldap/ldap_objects/adca.py | |
76 | msldap/ldap_objects/adcertificatetemplate.py | |
93 | 77 | msldap/ldap_objects/adcomp.py |
78 | msldap/ldap_objects/adenrollmentservice.py | |
94 | 79 | msldap/ldap_objects/adgpo.py |
95 | 80 | msldap/ldap_objects/adgroup.py |
96 | 81 | msldap/ldap_objects/adinfo.py |
0 | asn1crypto | |
1 | asysocks>=0.1.1 | |
2 | minikerberos>=0.2.14 | |
0 | asn1crypto>=1.3.0 | |
1 | asysocks>=0.1.7 | |
2 | minikerberos>=0.2.20 | |
3 | 3 | prompt-toolkit>=3.0.2 |
4 | 4 | tqdm |
5 | winacl>=0.1.1 | |
5 | unicrypto>=0.0.5 | |
6 | wcwidth | |
7 | winacl>=0.1.2 | |
6 | 8 | |
7 | 9 | [:platform_system == "Windows"] |
8 | 10 | winsspi>=0.0.9 |
46 | 46 | "Operating System :: OS Independent", |
47 | 47 | ), |
48 | 48 | install_requires=[ |
49 | 'asn1crypto', | |
49 | 'unicrypto>=0.0.5', | |
50 | 'asn1crypto>=1.3.0', | |
51 | 'asysocks>=0.1.7', | |
52 | 'minikerberos>=0.2.20', | |
50 | 53 | 'winsspi>=0.0.9;platform_system=="Windows"', |
51 | 'minikerberos>=0.2.14', | |
52 | 'asysocks>=0.1.1', | |
53 | 'winacl>=0.1.1', | |
54 | 'winacl>=0.1.2', | |
54 | 55 | 'prompt-toolkit>=3.0.2', |
55 | 56 | 'tqdm', |
57 | 'wcwidth', | |
56 | 58 | ], |
57 | 59 | entry_points={ |
58 | 60 | 'console_scripts': [ |