Codebase list msldap / e4f1ede4-5072-44de-88c0-2e434c6cb618/main
New upstream snapshot. Kali Janitor 1 year, 6 months ago
87 changed file(s) with 1620 addition(s) and 9148 deletion(s). Raw diff Collapse all Expand all
0 Metadata-Version: 1.2
0 Metadata-Version: 2.1
11 Name: msldap
2 Version: 0.3.30
2 Version: 0.4.6
33 Summary: Python library to play with MS LDAP
44 Home-page: https://github.com/skelsec/msldap
55 Author: Tamas Jos
66 Author-email: [email protected]
7 License: UNKNOWN
8 Description: Python library to play with MS LDAP
9 Platform: UNKNOWN
107 Classifier: Programming Language :: Python :: 3.7
118 Classifier: Programming Language :: Python :: 3.8
129 Classifier: License :: OSI Approved :: MIT License
1310 Classifier: Operating System :: OS Independent
1411 Requires-Python: >=3.7
12 License-File: LICENSE
13
14 Python library to play with MS LDAP
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)
11
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/
43
54 # msldap
65 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
718
819 # Documentation
920 [Awesome documentation here!](https://msldap.readthedocs.io/en/latest/)
2839 `pip install msldap`
2940
3041 # Prerequisites
31 - `winsspi` module. For windows only. This supports SSPI based authentication.
3242 - `asn1crypto` module. Some LDAP queries incorporate ASN1 strucutres to be sent on top of the ASN1 transport XD
3343 - `asysocks` module. To support socks proxying.
3444 - `aiocmd` For the interactive client
5969 - kerberos-aes (dc option param must be used)
6070 - kerberos-keytab (dc option param must be used)
6171 - kerberos-ccache (dc option param must be used)
72 - kerberos-pfx (dc option param must be used)
73 - kerberos-pem (dc option param must be used)
74 - kerberos-certstore (dc option param must be used, windows only)
6275 - sspi-ntlm (windows only!)
6376 - sspi-kerberos (windows only!)
6477 - anonymous
95108 ```
96109
97110 # Kudos
98
111 Certificate services functionality was based on [certi](https://github.com/zer1t0/certi) created by @zer1t0
0 msldap (0.4.1+git20221006.1.4818c97-0kali1) UNRELEASED; urgency=low
1
2 * New upstream snapshot.
3
4 -- Kali Janitor <[email protected]> Wed, 02 Nov 2022 22:16:28 -0000
5
06 msldap (0.3.30-0kali1) kali-dev; urgency=medium
17
28 [ Kali Janitor ]
55
66 import logging
77 import csv
8 from msldap.connection import MSLDAPConnection
9 from msldap.commons.url import MSLDAPURLDecoder
8 from msldap.connection import MSLDAPClientConnection
9 from msldap.commons.factory import LDAPConnectionFactory
1010 from msldap.ldap_objects import MSADUser, MSADMachine
1111
1212
3838 else:
3939 logging.basicConfig(level=logging.DEBUG)
4040
41 url_dec = MSLDAPURLDecoder(args.connection)
41 url_dec = LDAPConnectionFactory.from_url(args.connection)
4242 creds = url_dec.get_credential()
4343 target = url_dec.get_target()
4444 print(str(creds))
4545 print(str(target))
46 connection = MSLDAPConnection(creds, target)
46 connection = MSLDAPClientConnection(creds, target)
4747
4848 if args.command == 'dsa':
4949 print(connection.get_server_info())
00
1 __version__ = "0.3.30"
1 __version__ = "0.4.6"
22 __banner__ = \
33 """
44 # msldap %s
+0
-0
msldap/authentication/__init__.py less more
(Empty file)
+0
-0
msldap/authentication/kerberos/__init__.py less more
(Empty file)
+0
-543
msldap/authentication/kerberos/gssapi.py less more
0 import enum
1 import io
2 import os
3
4 from asn1crypto.core import ObjectIdentifier
5
6 from minikerberos.protocol.constants import EncryptionType
7 from minikerberos.protocol import encryption
8 from minikerberos.crypto.hashing import md5, hmac_md5
9 from minikerberos.crypto.RC4 import RC4
10
11 #TODO: RC4 support!
12
13 # https://tools.ietf.org/html/draft-raeburn-krb-rijndael-krb-05
14 # https://tools.ietf.org/html/rfc2478
15 # https://tools.ietf.org/html/draft-ietf-krb-wg-gssapi-cfx-02
16 # https://tools.ietf.org/html/rfc4757
17 # https://www.rfc-editor.org/errata/rfc4757
18
19 GSS_WRAP_HEADER = b'\x60\x2b\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02'
20 GSS_WRAP_HEADER_OID = b'\x60\x2b\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02'
21
22 class KRB5_MECH_INDEP_TOKEN:
23 # https://tools.ietf.org/html/rfc2743#page-81
24 # Mechanism-Independent Token Format
25
26 def __init__(self, data, oid, remlen = None):
27 self.oid = oid
28 self.data = data
29
30 #dont set this
31 self.length = remlen
32
33 @staticmethod
34 def from_bytes(data):
35 return KRB5_MECH_INDEP_TOKEN.from_buffer(io.BytesIO(data))
36
37 @staticmethod
38 def from_buffer(buff):
39
40 start = buff.read(1)
41 if start != b'\x60':
42 raise Exception('Incorrect token data!')
43 remaining_length = KRB5_MECH_INDEP_TOKEN.decode_length_buffer(buff)
44 token_data = buff.read(remaining_length)
45
46 buff = io.BytesIO(token_data)
47 pos = buff.tell()
48 buff.read(1)
49 oid_length = KRB5_MECH_INDEP_TOKEN.decode_length_buffer(buff)
50 buff.seek(pos)
51 token_oid = ObjectIdentifier.load(buff.read(oid_length+2))
52
53 return KRB5_MECH_INDEP_TOKEN(buff.read(), str(token_oid), remlen = remaining_length)
54
55 @staticmethod
56 def decode_length_buffer(buff):
57 lf = buff.read(1)[0]
58 if lf <= 127:
59 length = lf
60 else:
61 bcount = lf - 128
62 length = int.from_bytes(buff.read(bcount), byteorder = 'big', signed = False)
63 return length
64
65 @staticmethod
66 def encode_length(length):
67 if length <= 127:
68 return length.to_bytes(1, byteorder = 'big', signed = False)
69 else:
70 lb = length.to_bytes((length.bit_length() + 7) // 8, 'big')
71 return (128+len(lb)).to_bytes(1, byteorder = 'big', signed = False) + lb
72
73
74 def to_bytes(self):
75 t = ObjectIdentifier(self.oid).dump() + self.data
76 t = b'\x60' + KRB5_MECH_INDEP_TOKEN.encode_length(len(t)) + t
77 return t[:-len(self.data)] , self.data
78
79
80 class GSSAPIFlags(enum.IntFlag):
81 GSS_C_DCE_STYLE = 0x1000
82 GSS_C_DELEG_FLAG = 1
83 GSS_C_MUTUAL_FLAG = 2
84 GSS_C_REPLAY_FLAG = 4
85 GSS_C_SEQUENCE_FLAG = 8
86 GSS_C_CONF_FLAG = 0x10
87 GSS_C_INTEG_FLAG = 0x20
88
89 class KG_USAGE(enum.Enum):
90 ACCEPTOR_SEAL = 22
91 ACCEPTOR_SIGN = 23
92 INITIATOR_SEAL = 24
93 INITIATOR_SIGN = 25
94
95 class FlagsField(enum.IntFlag):
96 SentByAcceptor = 0
97 Sealed = 2
98 AcceptorSubkey = 4
99
100 # https://tools.ietf.org/html/rfc4757 (7.2)
101 class GSSMIC_RC4:
102 def __init__(self):
103 self.TOK_ID = b'\x01\x01'
104 self.SGN_ALG = b'\x11\x00' #HMAC
105 self.Filler = b'\xff'*4
106 self.SND_SEQ = None
107 self.SGN_CKSUM = None
108
109 @staticmethod
110 def from_bytes(data):
111 return GSSMIC_RC4.from_buffer(io.BytesIO(data))
112
113 @staticmethod
114 def from_buffer(buff):
115 mic = GSSMIC_RC4()
116 mic.TOK_ID = buff.read(2)
117 mic.SGN_ALG = buff.read(2)
118 mic.Filler = buff.read(4)
119 mic.SND_SEQ = buff.read(8)
120 mic.SGN_CKSUM = buff.read(8)
121
122 return mic
123
124 def to_bytes(self):
125 t = self.TOK_ID
126 t += self.SGN_ALG
127 t += self.Filler
128 t += self.SND_SEQ
129 if self.SGN_CKSUM is not None:
130 t += self.SGN_CKSUM
131
132 return t
133
134 class GSSWRAP_RC4:
135 def __init__(self):
136 self.TOK_ID = b'\x02\x01'
137 self.SGN_ALG = b'\x11\x00' #HMAC
138 self.SEAL_ALG = None
139 self.Filler = b'\xFF' * 2
140 self.SND_SEQ = None
141 self.SGN_CKSUM = None
142 self.Confounder = None
143
144 def __str__(self):
145 t = 'GSSWRAP_RC4\r\n'
146 t += 'TOK_ID : %s\r\n' % self.TOK_ID.hex()
147 t += 'SGN_ALG : %s\r\n' % self.SGN_ALG.hex()
148 t += 'SEAL_ALG : %s\r\n' % self.SEAL_ALG.hex()
149 t += 'Filler : %s\r\n' % self.Filler.hex()
150 t += 'SND_SEQ : %s\r\n' % self.SND_SEQ.hex()
151 t += 'SGN_CKSUM : %s\r\n' % self.SGN_CKSUM.hex()
152 t += 'Confounder : %s\r\n' % self.Confounder.hex()
153 return t
154
155 @staticmethod
156 def from_bytes(data):
157 return GSSWRAP_RC4.from_buffer(io.BytesIO(data))
158
159 @staticmethod
160 def from_buffer(buff):
161 wrap = GSSWRAP_RC4()
162 wrap.TOK_ID = buff.read(2)
163 wrap.SGN_ALG = buff.read(2)
164 wrap.SEAL_ALG = buff.read(2)
165 wrap.Filler = buff.read(2)
166 wrap.SND_SEQ = buff.read(8)
167 wrap.SGN_CKSUM = buff.read(8)
168 wrap.Confounder = buff.read(8)
169
170 return wrap
171
172 def to_bytes(self):
173 t = self.TOK_ID
174 t += self.SGN_ALG
175 t += self.SEAL_ALG
176 t += self.Filler
177 t += self.SND_SEQ
178
179 if self.SGN_CKSUM:
180 t += self.SGN_CKSUM
181 if self.Confounder:
182 t += self.Confounder
183
184
185 return t
186
187 class GSSAPI_RC4:
188 def __init__(self, session_key):
189 self.session_key = session_key
190
191 def GSS_GetMIC(self, data, sequenceNumber, direction = 'init'):
192 raise Exception('Not tested! Sure it needs some changes')
193 GSS_GETMIC_HEADER = b'\x60\x23\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02'
194
195 # Let's pad the data
196 pad = (4 - (len(data) % 4)) & 0x3
197 padStr = bytes([pad]) * pad
198 data += padStr
199
200 mic = GSSMIC_RC4()
201
202 if direction == 'init':
203 mic.SND_SEQ = sequenceNumber.to_bytes(4, 'big', signed = False) + b'\x00'*4
204 else:
205 mic.SND_SEQ = sequenceNumber.to_bytes(4, 'big', signed = False) + b'\xff'*4
206
207 Ksign_ctx = hmac_md5(self.session_key.contents)
208 Ksign_ctx.update(b'signaturekey\0')
209 Ksign = Ksign_ctx.digest()
210
211 id = 15
212 temp = md5( id.to_bytes(4, 'little', signed = False) + mic.to_bytes()[:8] ).digest()
213 chksum_ctx = hmac_md5(Ksign)
214 chksum_ctx.update(temp)
215 mic.SGN_CKSUM = chksum_ctx.digest()[:8]
216
217 id = 0
218 temp = hmac_md5(self.session_key.contents)
219 temp.update(id.to_bytes(4, 'little', signed = False))
220
221 Kseq_ctx = hmac_md5(temp.digest())
222 Kseq_ctx.update(mic.SGN_CKSUM)
223 Kseq = Kseq_ctx.digest()
224
225 mic.SGN_CKSUM = RC4(Kseq).encrypt(mic.SND_SEQ)
226
227 return GSS_GETMIC_HEADER + mic.to_bytes()
228
229
230 def GSS_Wrap(self, data, seq_num, direction = 'init', encrypt=True, cofounder = None):
231 #direction = 'a'
232 #seq_num = 0
233 #print('[GSS_Wrap] data: %s' % data)
234 #print('[GSS_Wrap] seq_num: %s' % seq_num.to_bytes(4, 'big', signed = False).hex())
235 #print('[GSS_Wrap] direction: %s' % direction)
236 #print('[GSS_Wrap] encrypt: %s' % encrypt)
237 #
238 #print('[GSS_Wrap] auth_data: %s' % auth_data)
239
240 #pad = 0
241 if encrypt is True:
242 data += b'\x01'
243 #pad = (8 - (len(data) % 8)) & 0x7
244 #padStr = bytes([pad]) * pad
245 #data += padStr
246 #
247 ##data += b'\x08' * 8
248 #print('[GSS_Wrap] pad: %s' % pad)
249 #print('[GSS_Wrap] data padded: %s' % data)
250
251
252 token = GSSWRAP_RC4()
253 token.SEAL_ALG = b'\x10\x00' # RC4
254
255 if direction == 'init':
256 token.SND_SEQ = seq_num.to_bytes(4, 'big', signed = False) + b'\x00'*4
257 else:
258 token.SND_SEQ = seq_num.to_bytes(4, 'big', signed = False) + b'\xff'*4
259
260 token.Confounder = os.urandom(8)
261 #if cofounder is not None:
262 # token.Confounder = cofounder
263 # #testing purposes only, pls remove
264
265
266 temp = hmac_md5(self.session_key.contents)
267 temp.update(b'signaturekey\0')
268 Ksign = temp.digest()
269
270 id = 13
271 Sgn_Cksum = md5(id.to_bytes(4, 'little', signed = False) + token.to_bytes()[:8] + token.Confounder + data).digest()
272
273 klocal = b''
274 for b in self.session_key.contents:
275 klocal += bytes([b ^ 0xf0])
276
277 id = 0
278 temp = hmac_md5(klocal)
279 temp.update(id.to_bytes(4, 'little', signed = False))
280 temp = hmac_md5(temp.digest())
281 temp.update(seq_num.to_bytes(4, 'big', signed = False))
282 Kcrypt = temp.digest()
283
284 temp = hmac_md5(Ksign)
285 temp.update(Sgn_Cksum)
286 token.SGN_CKSUM = temp.digest()[:8]
287
288 id = 0
289 temp = hmac_md5(self.session_key.contents)
290 temp.update(id.to_bytes(4, 'little', signed = False))
291 temp = hmac_md5(temp.digest())
292 temp.update(token.SGN_CKSUM)
293 Kseq = temp.digest()
294
295 token.SND_SEQ = RC4(Kseq).encrypt(token.SND_SEQ)
296
297
298 #if auth_data is not None:
299 if encrypt is False:
300 #print('Unwrap sessionkey: %s' % self.session_key.contents.hex())
301 #print('Unwrap data : %s' % data.hex())
302
303 sspi_wrap = KRB5_MECH_INDEP_TOKEN.from_bytes(data)
304
305 hdr = sspi_wrap.data[:32]
306 data = sspi_wrap.data[32:]
307
308 wrap = GSSWRAP_RC4.from_bytes(hdr)
309
310 id = 0
311 temp = hmac_md5(self.session_key.contents)
312 temp.update(id.to_bytes(4, 'little', signed = False))
313 temp = hmac_md5(temp.digest())
314 temp.update(wrap.SGN_CKSUM)
315 Kseq = temp.digest()
316
317 snd_seq = RC4(Kseq).encrypt(wrap.SND_SEQ)
318
319 id = 0
320 temp = hmac_md5(klocal)
321 temp.update(id.to_bytes(4, 'little', signed = False))
322 temp = hmac_md5(temp.digest())
323 temp.update(snd_seq[:4])
324 Kcrypt = temp.digest()
325
326 rc4 = RC4(Kcrypt)
327 dec_cofounder = rc4.decrypt(wrap.Confounder)
328 dec_data = rc4.decrypt(data)
329
330 id = 13
331 Sgn_Cksum_calc = md5(id.to_bytes(4, 'little', signed = False) + wrap.to_bytes()[:8] + dec_cofounder + dec_data).digest()
332
333 temp = hmac_md5(Ksign)
334 temp.update(Sgn_Cksum_calc)
335 Sgn_Cksum_calc = temp.digest()[:8]
336
337 if wrap.SGN_CKSUM != Sgn_Cksum_calc[:8]:
338 return None, Exception('Integrity verification failed')
339
340 pad = 1
341 return dec_data[:-pad], None
342
343 elif encrypt is True:
344 rc4 = RC4(Kcrypt)
345 token.Confounder = rc4.encrypt(token.Confounder)
346 cipherText = rc4.encrypt(data)
347 finalData, cipherText = KRB5_MECH_INDEP_TOKEN( token.to_bytes() + cipherText, '1.2.840.113554.1.2.2' ).to_bytes()
348
349
350 #print('cipherText %s' % cipherText.hex())
351 #print('finalData %s' % finalData.hex())
352 #print('sessionkey %s' % self.session_key.contents.hex())
353 return cipherText, finalData
354
355
356 def GSS_Unwrap(self, data, seq_num, direction='init'):
357 #print('GSS_Unwrap data : %s' % data)
358 dec_data, err = self.GSS_Wrap(data, seq_num, direction=direction, encrypt = False)
359 #print('GSS_Unwrap decrypted data : %s' % dec_data)
360 return dec_data, err
361
362 # 4.2.6.1. MIC Tokens
363 class GSSMIC:
364 def __init__(self):
365 self.TOK_ID = b'\x04\x04'
366 self.Flags = None
367 self.Filler = b'\xFF' * 5
368 self.SND_SEQ = None
369 self.SGN_CKSUM = None
370
371 @staticmethod
372 def from_bytes(data):
373 return GSSMIC.from_buffer(io.BytesIO(data))
374
375 @staticmethod
376 def from_buffer(buff):
377 m = GSSMIC()
378 m.TOK_ID = buff.read(2)
379 m.Flags = FlagsField(int.from_bytes(buff.read(1), 'big', signed = False))
380 m.Filler = buff.read(5)
381 m.SND_SEQ = int.from_bytes(buff.read(8), 'big', signed = False)
382 m.SGN_CKSUM = buff.read() #should know the size based on the algo!
383 return m
384
385 def to_bytes(self):
386 t = self.TOK_ID
387 t += self.Flags.to_bytes(1, 'big', signed = False)
388 t += self.Filler
389 t += self.SND_SEQ.to_bytes(8, 'big', signed = False)
390 if self.SGN_CKSUM is not None:
391 t += self.SGN_CKSUM
392
393 return t
394
395 # 4.2.6.2. Wrap Tokens
396 class GSSWrapToken:
397 def __init__(self):
398 self.TOK_ID = b'\x05\x04'
399 self.Flags = None
400 self.Filler = b'\xFF'
401 self.EC = None
402 self.RRC = None
403 self.SND_SEQ = None
404 self.Data = None
405
406 @staticmethod
407 def from_bytes(data):
408 return GSSWrapToken.from_buffer(io.BytesIO(data))
409
410 @staticmethod
411 def from_buffer(buff):
412 m = GSSWrapToken()
413 m.TOK_ID = buff.read(2)
414 m.Flags = FlagsField(int.from_bytes(buff.read(1), 'big', signed = False))
415 m.Filler = buff.read(1)
416 m.EC = int.from_bytes(buff.read(2), 'big', signed = False)
417 m.RRC = int.from_bytes(buff.read(2), 'big', signed = False)
418 m.SND_SEQ = int.from_bytes(buff.read(8), 'big', signed = False)
419 return m
420
421 def to_bytes(self):
422 t = self.TOK_ID
423 t += self.Flags.to_bytes(1, 'big', signed = False)
424 t += self.Filler
425 t += self.EC.to_bytes(2, 'big', signed = False)
426 t += self.RRC.to_bytes(2, 'big', signed = False)
427 t += self.SND_SEQ.to_bytes(8, 'big', signed = False)
428 if self.Data is not None:
429 t += self.Data
430
431 return t
432
433 class GSSAPI_AES:
434 def __init__(self, session_key, cipher_type, checksum_profile):
435 self.session_key = session_key
436 self.checksum_profile = checksum_profile
437 self.cipher_type = cipher_type
438 self.cipher = None
439
440 def rotate(self, data, numBytes):
441 numBytes %= len(data)
442 left = len(data) - numBytes
443 result = data[left:] + data[:left]
444 return result
445
446 def unrotate(self, data, numBytes):
447 numBytes %= len(data)
448 result = data[numBytes:] + data[:numBytes]
449 return result
450
451 def GSS_GetMIC(self, data, seq_num):
452 pad = (4 - (len(data) % 4)) & 0x3
453 padStr = bytes([pad]) * pad
454 data += padStr
455
456 m = GSSMIC()
457 m.Flags = FlagsField.AcceptorSubkey
458 m.SND_SEQ = seq_num
459 checksum_profile = self.checksum_profile()
460 m.checksum = checksum_profile.checksum(self.session_key, KG_USAGE.INITIATOR_SIGN.value, data + m.to_bytes()[:16])
461
462 return m.to_bytes()
463
464 def GSS_Wrap(self, data, seq_num, use_padding = False):
465 #print('[GSS_Wrap] seq_num: %s' % seq_num.to_bytes(4, 'big', signed = False).hex())
466 cipher = self.cipher_type()
467 pad = 0
468 if use_padding is True:
469 pad = ((cipher.blocksize - len(data)) % cipher.blocksize) #(cipher.blocksize - (len(data) % cipher.blocksize)) & 15
470 padStr = b'\xFF' * pad
471 data += padStr
472
473 t = GSSWrapToken()
474 t.Flags = FlagsField.AcceptorSubkey | FlagsField.Sealed
475 t.EC = pad
476 t.RRC = 0
477 t.SND_SEQ = seq_num
478
479 #print('Wrap data: %s' % (data + t.to_bytes()))
480 cipher_text = cipher.encrypt(self.session_key, KG_USAGE.INITIATOR_SEAL.value, data + t.to_bytes(), None)
481 t.RRC = 28 #[RFC4121] section 4.2.5
482 cipher_text = self.rotate(cipher_text, t.RRC + t.EC)
483
484 ret1 = cipher_text
485 ret2 = t.to_bytes()
486
487 return ret1, ret2
488
489 def GSS_Unwrap(self, data, seq_num, direction='init', auth_data = None, use_padding = False):
490 #print('')
491 #print('Unwrap data %s' % data[16:])
492 #print('Unwrap hdr %s' % data[:16])
493
494 cipher = self.cipher_type()
495 original_hdr = GSSWrapToken.from_bytes(data[:16])
496 rotated = data[16:]
497
498 cipher_text = self.unrotate(rotated, original_hdr.RRC + original_hdr.EC)
499 plain_text = cipher.decrypt(self.session_key, KG_USAGE.ACCEPTOR_SEAL.value, cipher_text)
500 new_hdr = GSSWrapToken.from_bytes(plain_text[-16:])
501
502 #signature checking
503 new_hdr.RRC = 28
504 if data[:16] != new_hdr.to_bytes():
505 return None, Exception('GSS_Unwrap signature mismatch!')
506
507
508 #print('Unwrap checksum: %s' % plain_text[-(original_hdr.EC + 16):])
509 #print('Unwrap orig chk: %s' % original_hdr.to_bytes())
510 #print('Unwrap result 1: %s' % plain_text)
511 #print('Unwrap result : %s' % plain_text[:-(original_hdr.EC + 16)])
512 return plain_text[:-(original_hdr.EC + 16)], None
513
514 def get_gssapi(session_key):
515 if session_key.enctype == encryption.Enctype.AES256:
516 return GSSAPI_AES(session_key, encryption._AES256CTS, encryption._SHA1AES256)
517 if session_key.enctype == encryption.Enctype.AES128:
518 return GSSAPI_AES(session_key, encryption._AES128CTS, encryption._SHA1AES128)
519 elif session_key.enctype == encryption.Enctype.RC4:
520 return GSSAPI_RC4(session_key)
521 else:
522 raise Exception('Unsupported etype %s' % session_key.enctype)
523
524
525 def test():
526 data = b'\xAF' * 1024
527 session_key = encryption.Key( encryption.Enctype.AES256 , bytes.fromhex('3e242e91996aadd513ecb1bc2369e44183e08e08c51550fa4b681e77f75ed8e1'))
528 sequenceNumber = 0
529 gssapi = get_gssapi(session_key)
530
531 r1, r2 = gssapi.GSS_Wrap(data, sequenceNumber)
532 print(len(r2))
533 sent = r2 + r1
534 print(r1)
535 ret1, ret2 = gssapi.GSS_Unwrap(sent, sequenceNumber)
536
537 print(r1.hex())
538 print(ret1.hex())
539
540
541 if __name__ == '__main__':
542 test()
+0
-155
msldap/authentication/kerberos/multiplexor.py less more
0
1 ##
2 ##
3 ## Interface to allow remote kerberos authentication via Multiplexor
4 ##
5 ##
6 ##
7 ##
8 ##
9 ## TODO: RPC auth type is not implemented or tested!!!!
10
11 from msldap.authentication.spnego.asn1_structs import KRB5Token
12 from msldap.authentication.kerberos.gssapi import get_gssapi, GSSWrapToken, KRB5_MECH_INDEP_TOKEN
13 from minikerberos.protocol.asn1_structs import AP_REQ, AP_REP, TGS_REP
14 from minikerberos.protocol.encryption import Enctype, Key, _enctype_table
15
16 from multiplexor.operator.external.sspi import KerberosSSPIClient
17 from multiplexor.operator import MultiplexorOperator
18 import enum
19
20 # mutual auth not supported
21 # encryption is always on
22 # we dont get the output flags back (lack of time to do the multiplexor protocol... TODO
23
24 class ISC_REQ(enum.IntFlag):
25 DELEGATE = 1
26 MUTUAL_AUTH = 2
27 REPLAY_DETECT = 4
28 SEQUENCE_DETECT = 8
29 CONFIDENTIALITY = 16
30 USE_SESSION_KEY = 32
31 PROMPT_FOR_CREDS = 64
32 USE_SUPPLIED_CREDS = 128
33 ALLOCATE_MEMORY = 256
34 USE_DCE_STYLE = 512
35 DATAGRAM = 1024
36 CONNECTION = 2048
37 CALL_LEVEL = 4096
38 FRAGMENT_SUPPLIED = 8192
39 EXTENDED_ERROR = 16384
40 STREAM = 32768
41 INTEGRITY = 65536
42 IDENTIFY = 131072
43 NULL_SESSION = 262144
44 MANUAL_CRED_VALIDATION = 524288
45 RESERVED1 = 1048576
46 FRAGMENT_TO_FIT = 2097152
47 HTTP = 0x10000000
48
49 class MSLDAPKerberosMultiplexor:
50 def __init__(self, settings):
51 self.iterations = 0
52 self.settings = settings
53 self.mode = 'CLIENT'
54 self.ksspi = None
55 self.client = None
56 self.target = None
57 self.gssapi = None
58 self.etype = None
59 self.session_key = None
60 self.seq_number = 0
61 self.flags = ISC_REQ.CONNECTION
62
63 self.setup()
64
65 def setup(self):
66 if self.settings.encrypt is True:
67 self.flags = \
68 ISC_REQ.CONFIDENTIALITY |\
69 ISC_REQ.INTEGRITY |\
70 ISC_REQ.REPLAY_DETECT |\
71 ISC_REQ.SEQUENCE_DETECT
72
73 def get_seq_number(self):
74 """
75 Fetches the starting sequence number. This is either zero or can be found in the authenticator field of the
76 AP_REQ structure. As windows uses a random seq number AND a subkey as well, we can't obtain it by decrypting the
77 AP_REQ structure. Insead under the hood we perform an encryption operation via EncryptMessage API which will
78 yield the start sequence number
79 """
80 return self.seq_number
81
82 async def encrypt(self, data, message_no):
83 return self.gssapi.GSS_Wrap(data, message_no)
84
85 async def decrypt(self, data, message_no, direction='init', auth_data=None):
86 return self.gssapi.GSS_Unwrap(data, message_no, direction=direction, auth_data=auth_data)
87
88 def signing_needed(self):
89 """
90 Checks if integrity protection was negotiated
91 """
92 return ISC_REQ.INTEGRITY in self.flags
93
94 def encryption_needed(self):
95 """
96 Checks if confidentiality flag was negotiated
97 """
98 return ISC_REQ.CONFIDENTIALITY in self.flags
99
100 def get_session_key(self):
101 return self.session_key
102
103 async def authenticate(self, authData = None, flags = None, seq_number = 0, cb_data=None):
104 #authdata is only for api compatibility reasons
105 if self.ksspi is None:
106 await self.start_remote_kerberos()
107 try:
108 apreq, res = await self.ksspi.authenticate(self.settings.target.to_target_string(), flags=str(self.flags.value))
109 #print('MULTIPLEXOR KERBEROS SSPI, APREQ: %s ERROR: %s' % (apreq, res))
110 if res is not None:
111 return None, None, res
112
113 # here it seems like we get the full token not just the apreq data...
114 # so we need to discard the layers
115
116 self.session_key, err = await self.ksspi.get_session_key()
117 if err is not None:
118 return None, None, err
119
120 unwrap = KRB5_MECH_INDEP_TOKEN.from_bytes(apreq)
121 aprep = AP_REQ.load(unwrap.data[2:]).native
122 subkey = Key(aprep['ticket']['enc-part']['etype'], self.session_key)
123 self.gssapi = get_gssapi(subkey)
124
125 if aprep['ticket']['enc-part']['etype'] != 23:
126 raw_seq_data, err = await self.ksspi.get_seq_number()
127 if err is not None:
128 return None, None, err
129 self.seq_number = GSSWrapToken.from_bytes(raw_seq_data[16:]).SND_SEQ
130
131 return unwrap.data[2:], False, res
132 except Exception as e:
133 return None, None, e
134
135 async def start_remote_kerberos(self):
136 try:
137 #print(self.settings.get_url())
138 #print(self.settings.agent_id)
139 self.operator = MultiplexorOperator(self.settings.get_url())
140 await self.operator.connect()
141 #creating virtual sspi server
142 server_info = await self.operator.start_sspi(self.settings.agent_id)
143 #print(server_info)
144
145 sspi_url = 'ws://%s:%s' % (server_info['listen_ip'], server_info['listen_port'])
146
147 #print(sspi_url)
148 self.ksspi = KerberosSSPIClient(sspi_url)
149 await self.ksspi.connect()
150 except Exception as e:
151 import traceback
152 traceback.print_exc()
153 return None
154
+0
-220
msldap/authentication/kerberos/native.py less more
0 #
1 #
2 # This is just a simple interface to the minikerberos library to support SPNEGO
3 #
4 #
5 # - Links -
6 # 1. See minikerberos library
7
8 import datetime
9
10 import os
11 from minikerberos.common import *
12
13
14 from minikerberos.protocol.asn1_structs import AP_REP, EncAPRepPart, EncryptedData, AP_REQ, Ticket
15 from msldap.authentication.kerberos.gssapi import get_gssapi, KRB5_MECH_INDEP_TOKEN
16 from msldap.commons.proxy import MSLDAPProxyType
17 from minikerberos.protocol.structures import ChecksumFlags
18 from minikerberos.protocol.encryption import Enctype, Key, _enctype_table
19 from minikerberos.protocol.constants import MESSAGE_TYPE
20 from minikerberos.aioclient import AIOKerberosClient
21 from minikerberos.network.aioclientsockssocket import AIOKerberosClientSocksSocket
22 from msldap import logger
23
24 # SMBKerberosCredential
25
26 MSLDAP_SOCKS_PROXY_TYPES = [
27 MSLDAPProxyType.SOCKS4,
28 MSLDAPProxyType.SOCKS4_SSL,
29 MSLDAPProxyType.SOCKS5,
30 MSLDAPProxyType.SOCKS5_SSL,
31 MSLDAPProxyType.WSNET,
32 ]
33
34 class MSLDAPKerberos:
35 def __init__(self, settings):
36 self.settings = settings
37 self.signing_preferred = None
38 self.encryption_preferred = None
39 self.ccred = None
40 self.target = None
41 self.spn = None
42 self.kc = None
43 self.flags = None
44 self.preferred_etypes = [23,17,18]
45
46 self.session_key = None
47 self.gssapi = None
48 self.iterations = 0
49 self.etype = None
50 self.seq_number = 0
51 self.expected_server_seq_number = None
52 self.from_ccache = False
53
54 self.setup()
55
56 def get_seq_number(self):
57 """
58 Returns the initial sequence number. It is 0 by default, but can be adjusted during authentication,
59 by passing the 'seq_number' parameter in the 'authenticate' function
60 """
61 return self.seq_number
62
63 def signing_needed(self):
64 """
65 Checks if integrity protection was negotiated
66 """
67 return ChecksumFlags.GSS_C_INTEG_FLAG in self.flags
68
69 def encryption_needed(self):
70 """
71 Checks if confidentiality flag was negotiated
72 """
73 return ChecksumFlags.GSS_C_CONF_FLAG in self.flags
74
75 async def sign(self, data, message_no, direction = 'init'):
76 """
77 Signs a message.
78 """
79 return self.gssapi.GSS_GetMIC(data, message_no, direction = direction)
80
81 async def encrypt(self, data, message_no):
82 """
83 Encrypts a message.
84 """
85
86 return self.gssapi.GSS_Wrap(data, message_no)
87
88 async def decrypt(self, data, message_no, direction='init'):
89 """
90 Decrypts message. Also performs integrity checking.
91 """
92
93 return self.gssapi.GSS_Unwrap(data, message_no, direction=direction)
94
95 def setup(self):
96 self.ccred = self.settings.ccred
97 self.spn = self.settings.spn
98 self.target = self.settings.target
99 if self.settings.enctypes is not None:
100 self.preferred_etypes = self.settings.enctypes
101
102 self.flags = ChecksumFlags.GSS_C_MUTUAL_FLAG
103 if self.settings.encrypt is True:
104 self.flags = \
105 ChecksumFlags.GSS_C_CONF_FLAG |\
106 ChecksumFlags.GSS_C_INTEG_FLAG |\
107 ChecksumFlags.GSS_C_REPLAY_FLAG |\
108 ChecksumFlags.GSS_C_SEQUENCE_FLAG
109
110
111 def get_session_key(self):
112 return self.session_key.contents, None
113
114
115 async def setup_kc(self):
116 try:
117 # sockst/wsnet proxying is handled by the minikerberos&asysocks modules
118 if self.target.proxy is None or self.target.proxy.type in MSLDAP_SOCKS_PROXY_TYPES:
119 self.kc = AIOKerberosClient(self.ccred, self.target)
120
121 elif self.target.proxy.type in [MSLDAPProxyType.MULTIPLEXOR, MSLDAPProxyType.MULTIPLEXOR_SSL]:
122 from msldap.network.multiplexor import MultiplexorProxyConnection
123 mpc = MultiplexorProxyConnection(self.target)
124 socks_proxy = await mpc.connect(is_kerberos = True)
125
126 self.kc = AIOKerberosClient(self.ccred, socks_proxy)
127
128 else:
129 raise Exception('Unknown proxy type %s' % self.target.proxy.type)
130
131 return None, None
132 except Exception as e:
133 return None, e
134
135 async def authenticate(self, authData, flags = None, seq_number = 0, cb_data = None):
136 """
137 This function is called (multiple times depending on the flags) to perform authentication.
138 """
139 try:
140 if self.kc is None:
141 _, err = await self.setup_kc()
142 if err is not None:
143 return None, None, err
144
145 if self.iterations == 0:
146 self.seq_number = 0
147 self.iterations += 1
148
149 try:
150 #check TGS first, maybe ccache already has what we need
151 for target in self.ccred.ccache.list_targets():
152 # just printing this to debug...
153 logger.debug('CCACHE SPN record: %s' % target)
154 tgs, encpart, self.session_key = await self.kc.get_TGS(self.spn)
155
156 self.from_ccache = True
157 except:
158 tgt = await self.kc.get_TGT(override_etype = self.preferred_etypes)
159 tgs, encpart, self.session_key = await self.kc.get_TGS(self.spn)#, override_etype = self.preferred_etypes)
160
161 #self.expected_server_seq_number = encpart.get('nonce', seq_number)
162
163 ap_opts = []
164 if ChecksumFlags.GSS_C_MUTUAL_FLAG in self.flags or ChecksumFlags.GSS_C_DCE_STYLE in self.flags:
165 if ChecksumFlags.GSS_C_MUTUAL_FLAG in self.flags:
166 ap_opts.append('mutual-required')
167 if self.from_ccache is False:
168 apreq = self.kc.construct_apreq(tgs, encpart, self.session_key, flags = self.flags, seq_number = self.seq_number, ap_opts=ap_opts, cb_data = cb_data)
169 else:
170 apreq = self.kc.construct_apreq_from_ticket(Ticket(tgs['ticket']).dump(), self.session_key, tgs['crealm'], tgs['cname']['name-string'][0], flags = self.flags, seq_number = self.seq_number, ap_opts = ap_opts, cb_data = cb_data)
171 return apreq, True, None
172
173 else:
174 #no mutual or dce auth will take one step only
175 if self.from_ccache is False:
176 apreq = self.kc.construct_apreq(tgs, encpart, self.session_key, flags = self.flags, seq_number = self.seq_number, ap_opts=[], cb_data = cb_data)
177 else:
178 apreq = self.kc.construct_apreq_from_ticket(Ticket(tgs['ticket']).dump(), self.session_key, tgs['crealm'], tgs['cname']['name-string'][0], flags = self.flags, seq_number = self.seq_number, ap_opts = ap_opts, cb_data = cb_data)
179
180
181 self.gssapi = get_gssapi(self.session_key)
182 return apreq, False, None
183
184 else:
185 self.iterations += 1
186 if ChecksumFlags.GSS_C_DCE_STYLE in self.flags:
187 # adata = authData[16:]
188 # if ChecksumFlags.GSS_C_DCE_STYLE in self.flags:
189 # adata = authData
190 raise Exception('DCE auth Not implemented!')
191
192 # at this point we are dealing with mutual authentication
193 # This means that the server sent back an AP-rep wrapped in a token
194 # The APREP contains a new session key we'd need to update and a seq-number
195 # that is expected the server will use for future communication.
196 # For mutual auth we dont need to reply anything after this step,
197 # but for DCE auth a reply is expected. TODO
198
199 # converting the token to aprep
200 token = KRB5_MECH_INDEP_TOKEN.from_bytes(authData)
201 if token.data[:2] != b'\x02\x00':
202 raise Exception('Unexpected token type! %s' % token.data[:2].hex() )
203 aprep = AP_REP.load(token.data[2:]).native
204
205 # decrypting aprep
206 cipher = _enctype_table[int(aprep['enc-part']['etype'])]()
207 cipher_text = aprep['enc-part']['cipher']
208 temp = cipher.decrypt(self.session_key, 12, cipher_text)
209 enc_part = EncAPRepPart.load(temp).native
210
211 #updating session key, gssapi
212 self.session_key = Key(int(enc_part['subkey']['keytype']), enc_part['subkey']['keyvalue'])
213 #self.seq_number = enc_part.get('seq-number', 0)
214 self.gssapi = get_gssapi(self.session_key)
215
216 return b'', False, None
217
218 except Exception as e:
219 return None, None, e
+0
-148
msldap/authentication/kerberos/sspi.py less more
0 #
1 # This is just a simple interface to the winsspi library to support Kerberos
2 # Will only work on windows, ovbiously
3 #
4 #
5 #
6
7 from msldap.authentication.spnego.asn1_structs import KRB5Token
8 from winsspi.sspi import KerberosMSLDAPSSPI
9 from winsspi.common.function_defs import ISC_REQ, GetSequenceNumberFromEncryptdataKerberos
10 from msldap.authentication.kerberos.gssapi import get_gssapi, GSSWrapToken
11 from minikerberos.protocol.asn1_structs import AP_REQ, AP_REP
12 from minikerberos.protocol.encryption import Enctype, Key, _enctype_table
13
14 class MSLDAPKerberosSSPI:
15 def __init__(self, settings):
16 self.iterations = 0
17 self.settings = settings
18 self.username = settings.username
19 self.password = settings.password
20 self.domain = settings.domain
21 self.actual_ctx_flags = None #this will be popilated by the output of get_ticket_for_spn
22 self.flags = ISC_REQ.CONNECTION
23 if settings.encrypt is True:
24 self.flags = ISC_REQ.CONFIDENTIALITY| ISC_REQ.INTEGRITY | ISC_REQ.CONNECTION #| ISC_REQ.MUTUAL_AUTH #| ISC_REQ.USE_DCE_STYLE
25 self.ksspi = None
26 self.spn = settings.spn
27 self.gssapi = None
28 self.etype = None
29 self.session_key = None
30 self.seq_number = None
31
32 def get_seq_number(self):
33 """
34 Fetches the starting sequence number. This is either zero or can be found in the authenticator field of the
35 AP_REQ structure. As windows uses a random seq number AND a subkey as well, we can't obtain it by decrypting the
36 AP_REQ structure. Insead under the hood we perform an encryption operation via EncryptMessage API which will
37 yield the start sequence number
38 """
39 if self.seq_number is not None:
40 return self.seq_number
41 if ISC_REQ.CONFIDENTIALITY in self.actual_ctx_flags:
42 self.seq_number = GetSequenceNumberFromEncryptdataKerberos(self.ksspi.context)
43 if self.seq_number is None:
44 self.seq_number = 0
45
46 return self.seq_number
47
48 def signing_needed(self):
49 """
50 Checks if integrity protection was enabled
51 """
52 return ISC_REQ.INTEGRITY in self.actual_ctx_flags
53
54 def encryption_needed(self):
55 """
56 Checks if confidentiality was enabled
57 """
58 return ISC_REQ.CONFIDENTIALITY in self.actual_ctx_flags
59
60 async def sign(self, data, message_no, direction = 'init'):
61 """
62 Signs a message.
63 """
64 return self.gssapi.GSS_GetMIC(data, message_no, direction = direction)
65
66 async def encrypt(self, data, message_no):
67 """
68 Encrypts a message.
69 """
70 return self.gssapi.GSS_Wrap(data, message_no)
71
72 async def decrypt(self, data, message_no, direction='init'):
73 """
74 Decrypts message. Also performs integrity checking.
75 """
76 return self.gssapi.GSS_Unwrap(data, message_no, direction=direction)
77
78 def get_session_key(self):
79 """
80 Fetches the session key. Under the hood this uses QueryContextAttributes API call.
81 This will fail if the authentication is not yet finished!
82 """
83 err = None
84 if self.session_key is None:
85 self.session_key, err = self.ksspi.get_session_key()
86 return self.session_key, err
87
88 async def authenticate(self, authData = None, flags = None, seq_number = 0, cb_data = None):
89 """
90 This function is called (multiple times depending on the flags) to perform authentication.
91 """
92 try:
93 if self.iterations == 0:
94 self.ksspi = KerberosMSLDAPSSPI(domain = self.domain, username=self.username, password=self.password)
95 token, self.actual_ctx_flags = self.ksspi.get_ticket_for_spn(self.spn, ctx_flags = self.flags)
96 self.iterations += 1
97
98
99 if ISC_REQ.MUTUAL_AUTH in self.actual_ctx_flags or ISC_REQ.USE_DCE_STYLE in self.actual_ctx_flags:
100 #in these cases continuation is needed
101 return token, True, None
102
103 else:
104 #no mutual or dce auth will take one step only
105 _, err = self.get_session_key()
106 if err is not None:
107 return None, None, err
108 apreq = AP_REQ.load(token).native
109 subkey = Key(apreq['ticket']['enc-part']['etype'], self.session_key)
110 self.gssapi = get_gssapi(subkey)
111 self.get_seq_number()
112
113 return token, False, None
114
115
116 else:
117 adata = authData[16:]
118 if ISC_REQ.USE_DCE_STYLE in self.actual_ctx_flags:
119 adata = authData
120 token, self.actual_ctx_flags = self.ksspi.get_ticket_for_spn(self.spn, ctx_flags = self.actual_ctx_flags, token_data = adata)
121
122
123
124 if ISC_REQ.USE_DCE_STYLE in self.actual_ctx_flags:
125 #Using DCE style 3-legged auth
126 aprep = AP_REP.load(token).native
127 else:
128 aprep = AP_REP.load(adata).native
129 subkey = Key(aprep['enc-part']['etype'], self.get_session_key())
130
131 _, err = self.get_session_key()
132 if err is not None:
133 return None, None, err
134
135 _, err = self.get_seq_number()
136 if err is not None:
137 return None, None, err
138
139 subkey = Key(token['enc-part']['etype'], self.session_key)
140 self.gssapi = get_gssapi(subkey)
141
142 self.iterations += 1
143 return token, False, None
144
145 except Exception as e:
146 return None, None, e
147
+0
-126
msldap/authentication/kerberos/sspiproxyws.py less more
0
1 ##
2 ##
3 ## Interface to allow remote kerberos authentication via Multiplexor
4 ##
5 ##
6 ##
7 ##
8 ##
9 ## TODO: RPC auth type is not implemented or tested!!!!
10
11 import enum
12
13 from msldap.authentication.spnego.asn1_structs import KRB5Token
14 from msldap.authentication.kerberos.gssapi import get_gssapi, GSSWrapToken, KRB5_MECH_INDEP_TOKEN
15 from minikerberos.protocol.asn1_structs import AP_REQ, AP_REP, TGS_REP
16 from minikerberos.protocol.encryption import Enctype, Key, _enctype_table
17 from pyodidewsnet.sspiproxyws import SSPIProxyWS
18
19
20 # mutual auth not supported
21 # encryption is always on
22
23 class ISC_REQ(enum.IntFlag):
24 DELEGATE = 1
25 MUTUAL_AUTH = 2
26 REPLAY_DETECT = 4
27 SEQUENCE_DETECT = 8
28 CONFIDENTIALITY = 16
29 USE_SESSION_KEY = 32
30 PROMPT_FOR_CREDS = 64
31 USE_SUPPLIED_CREDS = 128
32 ALLOCATE_MEMORY = 256
33 USE_DCE_STYLE = 512
34 DATAGRAM = 1024
35 CONNECTION = 2048
36 CALL_LEVEL = 4096
37 FRAGMENT_SUPPLIED = 8192
38 EXTENDED_ERROR = 16384
39 STREAM = 32768
40 INTEGRITY = 65536
41 IDENTIFY = 131072
42 NULL_SESSION = 262144
43 MANUAL_CRED_VALIDATION = 524288
44 RESERVED1 = 1048576
45 FRAGMENT_TO_FIT = 2097152
46 HTTP = 0x10000000
47
48 class MSLDAPSSPIProxyKerberosAuth:
49 def __init__(self, settings):
50 self.iterations = 0
51 self.settings = settings
52 self.mode = 'CLIENT'
53 url = '%s://%s:%s' % (self.settings.proto, self.settings.host, self.settings.port)
54 self.sspi = SSPIProxyWS(url, self.settings.agent_id)
55 self.client = None
56 self.target = None
57 self.gssapi = None
58 self.etype = None
59 self.session_key = None
60 self.seq_number = 0
61 self.flags = ISC_REQ.CONNECTION
62
63 self.setup()
64
65 def setup(self):
66 if self.settings.encrypt is True:
67 self.flags = \
68 ISC_REQ.CONFIDENTIALITY |\
69 ISC_REQ.INTEGRITY |\
70 ISC_REQ.REPLAY_DETECT |\
71 ISC_REQ.SEQUENCE_DETECT
72
73 def get_seq_number(self):
74 return self.seq_number
75
76 async def encrypt(self, data, message_no):
77 return self.gssapi.GSS_Wrap(data, message_no)
78
79 async def decrypt(self, data, message_no, direction='init', auth_data=None):
80 return self.gssapi.GSS_Unwrap(data, message_no, direction=direction, auth_data=auth_data)
81
82 def signing_needed(self):
83 """
84 Checks if integrity protection was negotiated
85 """
86 return ISC_REQ.INTEGRITY in self.flags
87
88 def encryption_needed(self):
89 """
90 Checks if confidentiality flag was negotiated
91 """
92 return ISC_REQ.CONFIDENTIALITY in self.flags
93
94 def get_session_key(self):
95 return self.session_key
96
97 async def authenticate(self, authData = None, flags = None, seq_number = 0, cb_data=None):
98 try:
99 status, ctxattr, apreq, err = await self.sspi.authenticate('KERBEROS', '', self.settings.target.to_target_string(), 3, self.flags.value, authdata = b'')
100 if err is not None:
101 raise err
102
103 self.flags = ISC_REQ(ctxattr)
104
105 self.session_key, err = await self.sspi.get_sessionkey()
106 if err is not None:
107 return None, None, err
108
109 unwrap = KRB5_MECH_INDEP_TOKEN.from_bytes(apreq)
110 aprep = AP_REQ.load(unwrap.data[2:]).native
111 subkey = Key(aprep['ticket']['enc-part']['etype'], self.session_key)
112 self.gssapi = get_gssapi(subkey)
113
114 if aprep['ticket']['enc-part']['etype'] != 23:
115 if ISC_REQ.CONFIDENTIALITY in self.flags:
116 raw_seq_data, err = await self.sspi.get_sequenceno()
117 if err is not None:
118 return None, None, err
119 self.seq_number = GSSWrapToken.from_bytes(raw_seq_data[16:]).SND_SEQ
120
121 return unwrap.data[2:], False, None
122 except Exception as e:
123 return None, None, e
124
125
+0
-125
msldap/authentication/kerberos/wsnet.py less more
0
1 ##
2 ##
3 ## Interface to allow remote kerberos authentication via Multiplexor
4 ##
5 ##
6 ##
7 ##
8 ##
9 ## TODO: RPC auth type is not implemented or tested!!!!
10
11 import enum
12
13 from msldap.authentication.spnego.asn1_structs import KRB5Token
14 from msldap.authentication.kerberos.gssapi import get_gssapi, GSSWrapToken, KRB5_MECH_INDEP_TOKEN
15 from minikerberos.protocol.asn1_structs import AP_REQ, AP_REP, TGS_REP
16 from minikerberos.protocol.encryption import Enctype, Key, _enctype_table
17 from pyodidewsnet.clientauth import WSNETAuth
18
19
20 # mutual auth not supported
21 # encryption is always on
22
23 class ISC_REQ(enum.IntFlag):
24 DELEGATE = 1
25 MUTUAL_AUTH = 2
26 REPLAY_DETECT = 4
27 SEQUENCE_DETECT = 8
28 CONFIDENTIALITY = 16
29 USE_SESSION_KEY = 32
30 PROMPT_FOR_CREDS = 64
31 USE_SUPPLIED_CREDS = 128
32 ALLOCATE_MEMORY = 256
33 USE_DCE_STYLE = 512
34 DATAGRAM = 1024
35 CONNECTION = 2048
36 CALL_LEVEL = 4096
37 FRAGMENT_SUPPLIED = 8192
38 EXTENDED_ERROR = 16384
39 STREAM = 32768
40 INTEGRITY = 65536
41 IDENTIFY = 131072
42 NULL_SESSION = 262144
43 MANUAL_CRED_VALIDATION = 524288
44 RESERVED1 = 1048576
45 FRAGMENT_TO_FIT = 2097152
46 HTTP = 0x10000000
47
48 class MSLDAPWSNetKerberosAuth:
49 def __init__(self, settings):
50 self.iterations = 0
51 self.settings = settings
52 self.mode = 'CLIENT'
53 self.sspi = WSNETAuth()
54 self.client = None
55 self.target = None
56 self.gssapi = None
57 self.etype = None
58 self.session_key = None
59 self.seq_number = 0
60 self.flags = ISC_REQ.CONNECTION
61
62 self.setup()
63
64 def setup(self):
65 if self.settings.encrypt is True:
66 self.flags = \
67 ISC_REQ.CONFIDENTIALITY |\
68 ISC_REQ.INTEGRITY |\
69 ISC_REQ.REPLAY_DETECT |\
70 ISC_REQ.SEQUENCE_DETECT
71
72 def get_seq_number(self):
73 return self.seq_number
74
75 async def encrypt(self, data, message_no):
76 return self.gssapi.GSS_Wrap(data, message_no)
77
78 async def decrypt(self, data, message_no, direction='init', auth_data=None):
79 return self.gssapi.GSS_Unwrap(data, message_no, direction=direction, auth_data=auth_data)
80
81 def signing_needed(self):
82 """
83 Checks if integrity protection was negotiated
84 """
85 return ISC_REQ.INTEGRITY in self.flags
86
87 def encryption_needed(self):
88 """
89 Checks if confidentiality flag was negotiated
90 """
91 return ISC_REQ.CONFIDENTIALITY in self.flags
92
93 def get_session_key(self):
94 return self.session_key
95
96 async def authenticate(self, authData = None, flags = None, seq_number = 0, cb_data=None):
97 try:
98 status, ctxattr, apreq, err = await self.sspi.authenticate('KERBEROS', '', self.settings.target.to_target_string(), 3, self.flags.value, authdata = b'')
99 if err is not None:
100 raise err
101
102 self.flags = ISC_REQ(ctxattr)
103
104 self.session_key, err = await self.sspi.get_sessionkey()
105 if err is not None:
106 return None, None, err
107
108 unwrap = KRB5_MECH_INDEP_TOKEN.from_bytes(apreq)
109 aprep = AP_REQ.load(unwrap.data[2:]).native
110 subkey = Key(aprep['ticket']['enc-part']['etype'], self.session_key)
111 self.gssapi = get_gssapi(subkey)
112
113 if aprep['ticket']['enc-part']['etype'] != 23:
114 if ISC_REQ.CONFIDENTIALITY in self.flags:
115 raw_seq_data, err = await self.sspi.get_sequenceno()
116 if err is not None:
117 return None, None, err
118 self.seq_number = GSSWrapToken.from_bytes(raw_seq_data[16:]).SND_SEQ
119
120 return unwrap.data[2:], False, None
121 except Exception as e:
122 return None, None, e
123
124
+0
-0
msldap/authentication/ntlm/__init__.py less more
(Empty file)
+0
-540
msldap/authentication/ntlm/creds_calc.py less more
0 import io
1 import os
2 import hmac
3 import datetime
4
5 from msldap.crypto.symmetric import DES
6 from msldap.crypto.hashing import *
7 from msldap.authentication.ntlm.structures.challenge_response import *
8
9
10 class NTLMCredentials:
11 @staticmethod
12 def construct(ntlmNegotiate, ntlmChallenge, ntlmAuthenticate):
13 # now the guessing-game begins
14
15 if isinstance(ntlmAuthenticate.NTChallenge, NTLMv2Response):
16 #if ntlmAuthenticate._use_NTLMv2:
17 # this is a netNTLMv2 then, otherwise auth would have failed on protocol level
18 creds = netntlmv2()
19 creds.username = ntlmAuthenticate.UserName
20 creds.domain = ntlmAuthenticate.DomainName
21 creds.ServerChallenge = ntlmChallenge.ServerChallenge
22 creds.ClientResponse = ntlmAuthenticate.NTChallenge.Response
23 creds.ChallengeFromClinet = ntlmAuthenticate.NTChallenge.ChallengeFromClinet_hex
24
25 creds2 = netlmv2()
26 creds2.username = ntlmAuthenticate.UserName
27 creds2.domain = ntlmAuthenticate.DomainName
28 creds2.ServerChallenge = ntlmChallenge.ServerChallenge
29 creds2.ClientResponse = ntlmAuthenticate.LMChallenge.Response
30 creds2.ChallengeFromClinet = ntlmAuthenticate.LMChallenge.ChallengeFromClinet
31 return [creds, creds2]
32
33 else:
34 if ntlmAuthenticate.NegotiateFlags & NegotiateFlags.NEGOTIATE_EXTENDED_SESSIONSECURITY:
35 # extended security is used, this means that the LMresponse actually contains client challenge data
36 # and the LM and NT respondses need to be combined to form the cred data
37 creds = netntlm_ess()
38 creds.username = ntlmAuthenticate.UserName
39 creds.domain = ntlmAuthenticate.DomainName
40 creds.ServerChallenge = ntlmChallenge.ServerChallenge
41 creds.ClientResponse = ntlmAuthenticate.NTChallenge.Response
42 creds.ChallengeFromClinet = ntlmAuthenticate.LMChallenge.Response
43
44 return [creds]
45
46 else:
47 creds = netntlm()
48 creds.username = ntlmAuthenticate.UserName
49 creds.domain = ntlmAuthenticate.DomainName
50 creds.ServerChallenge = ntlmChallenge.ServerChallenge
51 creds.ClientResponse = ntlmAuthenticate.NTChallenge.Response
52
53 if ntlmAuthenticate.NTChallenge.Response == ntlmAuthenticate.LMChallenge.Response:
54 # the the two responses are the same, then the client did not send encrypted LM hashes, only NT
55 return [creds]
56
57
58 # CAME FOR COPPER, FOUND GOLD!!!!!
59 # HOW OUTDATED IS YOUR CLIENT ANYHOW???
60 creds2 = netlm()
61 creds2.username = ntlmAuthenticate.UserName
62 creds2.domain = ntlmAuthenticate.DomainName
63 creds2.ServerChallenge = ntlmChallenge.ServerChallenge
64 creds2.ClientResponse = ntlmAuthenticate.LMChallenge.Response
65 return [creds2, creds]
66
67 class netlm:
68 # not supported by hashcat?
69 def __init__(self):
70 # this part comes from the NTLMAuthenticate class
71 self.username = None
72 self.domain = None
73 # this comes from the NTLMChallenge class
74 self.ServerChallenge = None
75
76 # this is from the LMv1Response class (that is a member of NTLMAuthenticate class)
77 self.ClientResponse = None
78
79 def to_credential(self):
80 cred = Credential('netLM',
81 username = self.username,
82 fullhash = '%s:$NETLM$%s$%s' % (self.username, self.ServerChallenge, self.ClientResponse)
83 )
84 return cred
85
86 def verify(self, creds, credtype='plain'):
87 """
88 Verifies the authentication data against the user credentials
89 Be careful! If the credtype is 'hash' then LM hash is expected!
90 :param creds: dictionary containing the domain, user, hash/password
91 :param credtype: can be 'plain' or 'hash' this indicates what type of credential lookup to perform
92 :return: bool
93 """
94
95 # print('Creds: %s' % creds)
96 if creds is None:
97 return True
98
99 if self.domain not in creds:
100 return False
101 if self.username not in creds[self.domain]:
102 return False
103
104 if credtype == 'plain':
105 lm_hash = LMOWFv1(creds[self.domain][self.username])
106 elif credtype == 'hash':
107 lm_hash = bytes.fromhex(creds[self.domain][self.username])
108 else:
109 raise Exception('Unknown cred type!')
110
111 calc_response = DESL(lm_hash, self.ServerChallenge)
112
113 return self.ClientResponse == calc_response.hex()
114
115
116 class netlmv2:
117 # not supported by hashcat?
118 def __init__(self):
119 # this part comes from the NTLMAuthenticate class
120 self.username = None
121 self.domain = None
122 # this comes from the NTLMChallenge class
123 self.ServerChallenge = None
124
125 # this is from the LMv2Response class (that is a member of NTLMAuthenticate class)
126 self.ClientResponse = None
127 self.ChallengeFromClinet = None
128
129 def to_credential(self):
130 cred = Credential(
131 'netLMv2',
132 username = self.username,
133 fullhash = '$NETLMv2$%s$%s$%s$%s' % (self.username, self.ServerChallenge, self.ClientResponse, self.ChallengeFromClinet)
134 )
135 return cred
136
137 def verify(self, creds, credtype='plain'):
138 """
139 Verifies the authentication data against the user credentials
140 :param creds: dictionary containing the domain, user, hash/password
141 :param credtype: can be 'plain' or 'hash' this indicates what type of credential lookup to perform
142 :return: bool
143 """
144
145 # print('Creds: %s' % creds)
146 if creds is None:
147 return True
148
149 if self.domain not in creds:
150 return False
151 if self.username not in creds[self.domain]:
152 return False
153
154 if credtype == 'plain':
155 lm_hash = LMOWFv2(creds[self.domain][self.username], self.username, self.domain)
156 elif credtype == 'hash':
157 lm_hash = LMOWFv2(None, self.username, self.domain, bytes.fromhex(creds[self.domain][self.username]))
158 else:
159 raise Exception('Unknown cred type!')
160
161 hm = hmac_md5(lm_hash)
162 hm.update(bytes.fromhex(self.ServerChallenge))
163 hm.update(bytes.fromhex(self.ChallengeFromClinet))
164
165 return self.ClientResponse == hm.hexdigest()
166
167
168 class netntlm_ess:
169 def __init__(self):
170 # this part comes from the NTLMAuthenticate class
171 self.credentials = None
172 # this comes from the NTLMChallenge class
173 self.ServerChallenge = None
174
175 self.LMResponse = None
176 self.NTResponse = None
177
178 self.SessionBaseKey = None
179
180
181 # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/d86303b5-b29e-4fb9-b119-77579c761370
182 def calc_key_exchange_key(self):
183 if not self.credentials.nt_hash:
184 nt_hash = NTOWFv1(self.credentials.password)
185 else:
186 nt_hash = bytes.fromhex(self.credentials.nt_hash)
187
188 hm = hmac_md5(self.SessionBaseKey)
189 hm.update(self.ServerChallenge)
190 hm.update(self.LMResponse.to_bytes()[:8])
191
192 return hm.digest()
193
194 @staticmethod
195 def construct(server_challenge, client_challenge, credentials):
196 ntlm_creds = netntlm_ess()
197 ntlm_creds.credentials = credentials
198 ntlm_creds.ServerChallenge = server_challenge
199
200 if credentials.password:
201 nt_hash = NTOWFv1(credentials.password)
202 lm_hash = LMOWFv1(credentials.password)
203 else:
204 nt_hash = bytes.fromhex(credentials.nt_hash)
205 lm_hash = bytes.fromhex(credentials.lm_hash) if credentials.lm_hash else None
206
207
208 ntlm_creds.LMResponse = LMResponse()
209 ntlm_creds.LMResponse.Response = client_challenge + b'\x00' * 16
210
211 temp_1 = md5(server_challenge + client_challenge[:8]).digest()
212 data = DESL(nt_hash, temp_1[:8])
213
214 ntlm_creds.NTResponse = NTLMv1Response()
215 ntlm_creds.NTResponse.Response = data
216
217 ntlm_creds.SessionBaseKey = md4(nt_hash).digest()
218
219 return ntlm_creds
220
221 def to_credential(self):
222 cred = Credential(
223 'netNTLMv1-ESS',
224 username = self.username,
225 fullhash = '%s::%s:%s:%s:%s' % (self.credentials.username, self.credentials.domain, ntlm_creds.LMResponse.Response, ntlm_creds.NTResponse.Response, self.ServerChallenge)
226 )
227 return cred
228 # u4-netntlm::kNS:338d08f8e26de93300000000000000000000000000000000:9526fb8c23a90751cdd619b6cea564742e1e4bf33006ba41:cb8086049ec4736c
229
230 def calc_session_base_key(self, creds, credtype = 'plain'):
231 if credtype == 'plain':
232 nt_hash = NTOWFv1(creds[self.domain][self.username])
233 elif credtype == 'hash':
234 nt_hash = bytes.fromhex(creds[self.domain][self.username])
235 else:
236 raise Exception('Unknown cred type!')
237
238 session_base_key = md4(nt_hash).digest()
239 return session_base_key
240
241 def verify(self, creds, credtype='plain'):
242 """
243 Verifies the authentication data against the user credentials
244 :param creds: dictionary containing the domain, user, hash/password
245 :param credtype: can be 'plain' or 'hash' this indicates what type of credential lookup to perform
246 :return: bool
247 """
248 if creds is None:
249 return True
250 if self.domain not in creds:
251 return False
252 if self.username not in creds[self.domain]:
253 return False
254
255 if credtype == 'plain':
256 nt_hash = NTOWFv1(creds[self.domain][self.username])
257 elif credtype == 'hash':
258 nt_hash = bytes.fromhex(creds[self.domain][self.username])
259 else:
260 raise Exception('Unknown cred type!')
261
262 # print('Server chall: %s' % self.ServerChallenge)
263 # print('Client chall: %s' % self.ChallengeFromClinet)
264
265 temp_1 = md5(bytes.fromhex(self.ServerChallenge) + bytes.fromhex(self.ChallengeFromClinet)[:8]).digest()
266 calc_response = DESL(nt_hash, temp_1[:8])
267 # print('calc_response: %s' % calc_response.hex())
268 # print('ClientResponse: %s' % self.ClientResponse)
269
270 return calc_response == bytes.fromhex(self.ClientResponse)
271
272
273 class netntlm:
274 # not supported by hashcat?
275 def __init__(self):
276 # this part comes from the NTLMAuthenticate class
277 self.credentials = None
278 # this comes from the NTLMChallenge class
279 self.ServerChallenge = None
280
281 self.LMResponse = None
282 self.NTResponse = None
283
284
285 self.SessionBaseKey = None
286
287 def calc_key_exchange_key(self, with_lm = False, non_nt_session_key = False):
288
289 if self.credentials.password:
290 lm_hash = LMOWFv1(self.credentials.password)
291 else:
292 lm_hash = self.credentials.lm_hash
293
294 if with_lm:
295 temp1 = DES(lm_hash[:7]).encrypt(self.LMResponse.to_bytes()[:8])
296 temp2 = DES(lm_hash[7:8] + b'\xBD\xBD\xBD\xBD\xBD\xBD').encrypt(self.LMResponse.to_bytes()[:8])
297 kex = temp1 + temp2
298
299 else:
300 if non_nt_session_key:
301 kex = lm_hash[:8] + b'\x00' * 8
302 else:
303 kex = self.SessionBaseKey
304
305 return kex
306
307 @staticmethod
308 def construct(server_challenge, credentials):
309 ntlm_creds = netntlm()
310 ntlm_creds.credentials = credentials
311 ntlm_creds.ServerChallenge = server_challenge
312
313 if credentials.password:
314 nt_hash = NTOWFv1(credentials.password)
315 lm_hash = LMOWFv1(credentials.password)
316 else:
317 nt_hash = bytes.fromhex(credentials.nt_hash)
318 lm_hash = bytes.fromhex(credentials.lm_hash) if credentials.lm_hash else None
319
320 ntlm_creds.NTResponse = NTLMv1Response()
321 ntlm_creds.NTResponse.Response = DESL(nt_hash, server_challenge)
322
323 if lm_hash:
324 ntlm_creds.LMResponse = LMResponse()
325 ntlm_creds.LMResponse.Response = DESL(lm_hash, server_challenge)
326 else:
327 ntlm_creds.LMResponse = ntresponse
328
329 ntlm_creds.SessionBaseKey = md4(nt_hash).digest()
330
331 return ntlm_creds
332
333 def to_credential(self):
334 cred = Credential('netNTLMv1',
335 username = self.username,
336 fullhash = '%s:$NETNTLM$%s$%s' % (self.username, self.ServerChallenge, self.NTResponse.Response)
337 )
338 return cred
339 #username:$NETNTLM$1122334455667788$B2B2220790F40C88BCFF347C652F67A7C4A70D3BEBD70233
340
341 def calc_session_base_key(self, creds, credtype = 'plain'):
342 if credtype == 'plain':
343 nt_hash = NTOWFv1(creds[self.domain][self.username])
344 elif credtype == 'hash':
345 nt_hash = bytes.fromhex(creds[self.domain][self.username])
346 else:
347 raise Exception('Unknown cred type!')
348
349 session_base_key = md4(nt_hash).digest()
350 return session_base_key
351
352 def verify(self, creds, credtype='plain'):
353 """
354 Verifies the authentication data against the user credentials
355 :param creds: dictionary containing the domain, user, hash/password
356 :param credtype: can be 'plain' or 'hash' this indicates what type of credential lookup to perform
357 :return: bool
358 """
359 if creds is None:
360 return True
361 if self.domain not in creds:
362 return False
363 if self.username not in creds[self.domain]:
364 return False
365
366 if credtype == 'plain':
367 nt_hash = NTOWFv1(creds[self.domain][self.username])
368 elif credtype == 'hash':
369 nt_hash = bytes.fromhex(creds[self.domain][self.username])
370 else:
371 raise Exception('Unknown cred type!')
372
373 return DESL(nt_hash, self.ServerChallenge) == bytes.fromhex(self.ClientResponse)
374
375
376 class netntlmv2:
377 def __init__(self):
378 self.credentials = None
379
380 # this comes from the NTLMChallenge class
381 self.ServerChallenge = None
382
383 # this is from the NTLMv2Response class (that is a member of NTLMAuthenticate class)
384 #self.ClientResponse = None
385 #self.ChallengeFromClinet = None
386
387 self.LMResponse = None
388 self.NTResponse = None
389
390
391 self.SessionBaseKey = None
392
393 # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/d86303b5-b29e-4fb9-b119-77579c761370
394 def calc_key_exchange_key(self):
395 return self.SessionBaseKey
396
397 @staticmethod
398 def construct(server_challenge, client_challenge, server_details, credentials, timestamp = None):
399 ntlm_creds = netntlmv2()
400 ntlm_creds.credentials = credentials
401 ntlm_creds.ServerChallenge = server_challenge
402
403 if not credentials.nt_hash and not credentials.password:
404 raise Exception('Password or NT hash must be supplied!')
405
406 if credentials.password:
407 nt_hash_v2 = NTOWFv2(credentials.password, credentials.username, credentials.domain)
408 else:
409 nt_hash_v2 = NTOWFv2(None, credentials.username, credentials.domain, bytes.fromhex(credentials.nt_hash))
410
411 if not timestamp:
412 timestamp = datetime.datetime.utcnow()
413
414 cc = NTLMv2ClientChallenge.construct(timestamp, client_challenge, server_details)
415 temp = cc.to_bytes()
416
417 hm = hmac_md5(nt_hash_v2)
418 hm.update(server_challenge)
419 hm.update(temp)
420
421 NTProofStr = hm.digest()
422
423 ntlm_creds.NTResponse = NTLMv2Response()
424 ntlm_creds.NTResponse.Response = NTProofStr
425 ntlm_creds.NTResponse.ChallengeFromClinet = cc
426
427
428 hm = hmac_md5(nt_hash_v2)
429 hm.update(server_challenge)
430 hm.update(client_challenge)
431
432 ntlm_creds.LMResponse = LMv2Response()
433 ntlm_creds.LMResponse.Response = hm.digest()
434 ntlm_creds.LMResponse.ChallengeFromClinet = client_challenge
435
436
437 hm = hmac_md5(nt_hash_v2)
438 hm.update(NTProofStr)
439 ntlm_creds.SessionBaseKey = hm.digest()
440
441 return ntlm_creds
442
443 def to_credential(self):
444 cred = Credential(
445 'netNTLMv2',
446 username = self.username,
447 domain = self.domain,
448 fullhash = '%s::%s:%s:%s:%s' % (self.credentials.username, self.credentials.domain, self.ServerChallenge, self.NTResponse.Response, self.NTResponse.ChallengeFromClinet)
449 )
450 return cred
451
452 def verify(self, creds, credtype = 'plain'):
453 """
454 Verifies the authentication data against the user credentials
455 :param creds: dictionary containing the domain, user, hash/password
456 :param credtype: can be 'plain' or 'hash' this indicates what type of credential lookup to perform
457 :return: bool
458 """
459
460 # print('Creds: %s' % creds)
461 if creds is None:
462 return True
463
464 if self.domain not in creds:
465 return False
466 if self.username not in creds[self.domain]:
467 return False
468
469 if credtype == 'plain':
470 nt_hash = NTOWFv2(creds[self.domain][self.username], self.username, self.domain)
471 elif credtype == 'hash':
472 nt_hash = NTOWFv2(None, self.username, self.domain, bytes.fromhex(creds[self.domain][self.username]))
473 else:
474 raise Exception('Unknown cred type!')
475
476 # print(self.ServerChallenge)
477 # print(self.ChallengeFromClinet)
478
479 hm = hmac_md5(nt_hash)
480 hm.update(bytes.fromhex(self.ServerChallenge))
481 hm.update(bytes.fromhex(self.ChallengeFromClinet))
482
483 # print('M_nthash: %s' % nthash.hex())
484 # print('M_temp: %s' % self.ChallengeFromClinet)
485 # print('M_nthash: %s' % nthash.hex())
486 # print('M_server_chall: %s' % self.ServerChallenge)
487 # print('M_ntproof_string: %s' % self.ClientResponse)
488 # print('M_ntproof_string_calc: %s' % hm.hexdigest())
489
490 return self.ClientResponse == hm.hexdigest()
491
492
493 def LMOWFv1(password):
494 LM_SECRET = b'KGS!@#$%'
495 t1 = password[:14].ljust(14, '\x00').upper()
496 d = DES(t1[:7].encode('ascii'))
497 r1 = d.encrypt(LM_SECRET)
498 d = DES(t1[7:].encode('ascii'))
499 r2 = d.encrypt(LM_SECRET)
500
501 return r1+r2
502
503
504 def NTOWFv1(password):
505 return md4(password.encode('utf-16le')).digest()
506
507
508 def LMOWFv2(Passwd, User, UserDom, PasswdHash = None):
509 return NTOWFv2(Passwd, User, UserDom, PasswdHash)
510
511
512 def NTOWFv2(Passwd, User, UserDom, PasswdHash = None):
513 if PasswdHash is not None:
514 fp = hmac_md5(PasswdHash)
515 else:
516 fp = hmac_md5(NTOWFv1(Passwd))
517 fp.update((User.upper() + UserDom).encode('utf-16le'))
518 return fp.digest()
519
520
521 def DESL(K, D):
522 """
523 Indicates the encryption of an 8-byte data item D with the 16-byte key K
524 using the Data Encryption Standard Long (DESL) algorithm.
525 The result is 24 bytes in length.
526 :param K:
527 :param D:
528 :return:
529 """
530 if len(K) != 16:
531 raise Exception("K MUST be 16 bytes long")
532 if len(D) != 8:
533 raise Exception("D MUST be 8 bytes long")
534
535 res = b''
536 res += DES(K[:7]).encrypt(D)
537 res += DES(K[7:14]).encrypt(D)
538 res += DES(K[14:] + b'\x00'*5).encrypt(D)
539 return res
+0
-0
msldap/authentication/ntlm/messages/__init__.py less more
(Empty file)
+0
-231
msldap/authentication/ntlm/messages/authenticate.py less more
0 import io
1
2 from msldap.authentication.ntlm.structures.fields import Fields
3 from msldap.authentication.ntlm.structures.negotiate_flags import NegotiateFlags
4 from msldap.authentication.ntlm.structures.version import Version
5 from msldap.authentication.ntlm.structures.challenge_response import *
6
7 # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/033d32cc-88f9-4483-9bf2-b273055038ce
8 class NTLMAuthenticate:
9 def __init__(self, _use_NTLMv2 = True):
10 self.Signature = b'NTLMSSP\x00'
11 self.MessageType = 3
12 self.LmChallengeResponseFields = None
13 self.NtChallengeResponseFields = None
14 self.DomainNameFields = None
15 self.UserNameFields = None
16 self.WorkstationFields = None
17 self.EncryptedRandomSessionKeyFields = None
18 self.NegotiateFlags = None
19 self.Version = None
20 self.MIC = None
21 self.Payload = None
22
23 # high level
24 self.LMChallenge = None
25 self.NTChallenge = None
26 self.DomainName = None
27 self.UserName = None
28 self.Workstation = None
29 self.EncryptedRandomSession = None
30
31 # this is a global variable that needs to be indicated
32 self._use_NTLMv2 = _use_NTLMv2
33
34 @staticmethod
35 def construct(flags, domainname= None, workstationname= None, username= None, encrypted_session= None, lm_response= None, nt_response= None, version = None, mic = b'\x00'*16):
36 auth = NTLMAuthenticate()
37 auth.Payload = b''
38
39 payload_pos = 8+4+8+8+8+8+8+8+4
40 if flags & NegotiateFlags.NEGOTIATE_VERSION:
41 if not version:
42 raise Exception('NEGOTIATE_VERSION set but no Version supplied!')
43
44 auth.Version = version
45
46 payload_pos += 8
47
48 if mic is not None:
49 auth.MIC = mic
50 payload_pos += 16
51
52 if lm_response:
53 data = lm_response.to_bytes()
54 auth.Payload += data
55 auth.LmChallengeResponseFields = Fields(len(data), payload_pos)
56 payload_pos += len(data)
57 auth.LMChallenge = lm_response
58 else:
59 auth.LmChallengeResponseFields = Fields(0,0)
60
61 if nt_response:
62 data = nt_response.to_bytes()
63 auth.Payload += data
64 auth.NtChallengeResponseFields = Fields(len(data), payload_pos)
65 payload_pos += len(data)
66 auth.NTChallenge = nt_response
67 else:
68 auth.NtChallengeResponseFields = Fields(0,0)
69
70
71 if domainname:
72 data = domainname.encode('utf-16le')
73 auth.Payload += data
74 auth.DomainNameFields = Fields(len(data), payload_pos)
75 payload_pos += len(data)
76 auth.DomainName = domainname
77 else:
78 auth.DomainNameFields = Fields(0,0)
79
80 if username:
81 data = username.encode('utf-16le')
82 auth.Payload += data
83 auth.UserNameFields = Fields(len(data), payload_pos)
84 payload_pos += len(data)
85 auth.UserName = username
86 else:
87 auth.UserNameFields = Fields(0,0)
88
89 if workstationname:
90 data = workstationname.encode('utf-16le')
91 auth.Payload += data
92 auth.WorkstationFields = Fields(len(data), payload_pos)
93 payload_pos += len(data)
94 auth.Workstation = workstationname
95 else:
96 auth.WorkstationFields = Fields(0,0)
97
98 if encrypted_session:
99 data = encrypted_session
100 auth.Payload += data
101 auth.EncryptedRandomSessionKeyFields = Fields(len(data), payload_pos)
102 payload_pos += len(data)
103 auth.EncryptedRandomSession = encrypted_session
104 else:
105 auth.EncryptedRandomSessionKeyFields = Fields(0,0)
106
107 auth.NegotiateFlags = flags
108 return auth
109
110 def to_bytes(self):
111 t = b''
112 t += self.Signature
113 t += self.MessageType.to_bytes(4, byteorder = 'little', signed = False)
114
115 t += self.LmChallengeResponseFields.to_bytes()
116 t += self.NtChallengeResponseFields.to_bytes()
117 t += self.DomainNameFields.to_bytes()
118 t += self.UserNameFields.to_bytes()
119 t += self.WorkstationFields.to_bytes()
120 t += self.EncryptedRandomSessionKeyFields.to_bytes()
121 t += self.NegotiateFlags.to_bytes(4, byteorder = 'little', signed = False)
122 if self.Version:
123 t += self.Version.to_bytes()
124 if self.MIC is not None:
125 t += self.MIC
126 t += self.Payload
127 return t
128
129
130 @staticmethod
131 def from_bytes(bbuff,_use_NTLMv2 = True):
132 return NTLMAuthenticate.from_buffer(io.BytesIO(bbuff), _use_NTLMv2 = _use_NTLMv2)
133
134 @staticmethod
135 def from_buffer(buff, _use_NTLMv2 = True):
136 auth = NTLMAuthenticate(_use_NTLMv2)
137 auth.Signature = buff.read(8)
138 auth.MessageType = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
139 auth.LmChallengeResponseFields = Fields.from_buffer(buff)
140 auth.NtChallengeResponseFields = Fields.from_buffer(buff)
141 auth.DomainNameFields = Fields.from_buffer(buff)
142 auth.UserNameFields = Fields.from_buffer(buff)
143 auth.WorkstationFields = Fields.from_buffer(buff)
144 auth.EncryptedRandomSessionKeyFields = Fields.from_buffer(buff)
145 auth.NegotiateFlags = NegotiateFlags(int.from_bytes(buff.read(4), byteorder = 'little', signed = False))
146 if auth.NegotiateFlags & NegotiateFlags.NEGOTIATE_VERSION:
147 auth.Version = Version.from_buffer(buff)
148
149 # TODO: I'm not sure about this condition!!! Need to test this!
150 if auth.NegotiateFlags & NegotiateFlags.NEGOTIATE_ALWAYS_SIGN:
151 auth.MIC = buff.read(16)
152
153 currPos = buff.tell()
154 auth.Payload = buff.read()
155
156 if auth._use_NTLMv2 and auth.NtChallengeResponseFields.length > 24:
157 buff.seek(auth.LmChallengeResponseFields.offset, io.SEEK_SET)
158 auth.LMChallenge = LMv2Response.from_buffer(buff)
159
160
161 buff.seek(auth.NtChallengeResponseFields.offset, io.SEEK_SET)
162 auth.NTChallenge = NTLMv2Response.from_buffer(buff)
163
164 else:
165 buff.seek(auth.LmChallengeResponseFields.offset, io.SEEK_SET)
166 auth.LMChallenge = LMResponse.from_buffer(buff)
167
168 buff.seek(auth.NtChallengeResponseFields.offset, io.SEEK_SET)
169 auth.NTChallenge = NTLMv1Response.from_buffer(buff)
170
171 buff.seek(auth.DomainNameFields.offset,io.SEEK_SET)
172 auth.DomainName = buff.read(auth.DomainNameFields.length).decode('utf-16le')
173
174 buff.seek(auth.UserNameFields.offset,io.SEEK_SET)
175 auth.UserName = buff.read(auth.UserNameFields.length).decode('utf-16le')
176
177 buff.seek(auth.WorkstationFields.offset,io.SEEK_SET)
178 auth.Workstation = buff.read(auth.WorkstationFields.length).decode('utf-16le')
179
180 buff.seek(auth.EncryptedRandomSessionKeyFields.offset,io.SEEK_SET)
181 auth.EncryptedRandomSession = buff.read(auth.EncryptedRandomSessionKeyFields.length)
182
183 buff.seek(currPos, io.SEEK_SET)
184
185 return auth
186
187 def __repr__(self):
188 t = '== NTLMAuthenticate ==\r\n'
189 t += 'Signature : %s\r\n' % repr(self.Signature)
190 t += 'MessageType : %s\r\n' % repr(self.MessageType)
191 t += 'NegotiateFlags: %s\r\n' % repr(self.NegotiateFlags)
192 t += 'Version : %s\r\n' % repr(self.Version)
193 t += 'MIC : %s\r\n' % repr(self.MIC.hex() if self.MIC else 'None')
194 t += 'LMChallenge : %s\r\n' % repr(self.LMChallenge)
195 t += 'NTChallenge : %s\r\n' % repr(self.NTChallenge)
196 t += 'DomainName : %s\r\n' % repr(self.DomainName)
197 t += 'UserName : %s\r\n' % repr(self.UserName)
198 t += 'Workstation : %s\r\n' % repr(self.Workstation)
199 t += 'EncryptedRandomSession: %s\r\n' % repr(self.EncryptedRandomSession.hex())
200 return t
201
202 def test():
203 test_reconstrut()
204 test_construct()
205 test_2()
206
207 def test_2():
208 data = bytes.fromhex('4e 54 4c 4d 53 53 50 00 03 00 00 00 18 00 18 006c 00 00 00 54 00 54 00 84 00 00 00 0c 00 0c 0048 00 00 00 08 00 08 00 54 00 00 00 10 00 10 005c 00 00 00 10 00 10 00 d8 00 00 00 35 82 88 e205 01 28 0a 00 00 00 0f 44 00 6f 00 6d 00 61 0069 00 6e 00 55 00 73 00 65 00 72 00 43 00 4f 004d 00 50 00 55 00 54 00 45 00 52 00 86 c3 50 97ac 9c ec 10 25 54 76 4a 57 cc cc 19 aa aa aa aaaa aa aa aa 68 cd 0a b8 51 e5 1c 96 aa bc 92 7beb ef 6a 1c 01 01 00 00 00 00 00 00 00 00 00 0000 00 00 00 aa aa aa aa aa aa aa aa 00 00 00 0002 00 0c 00 44 00 6f 00 6d 00 61 00 69 00 6e 0001 00 0c 00 53 00 65 00 72 00 76 00 65 00 72 0000 00 00 00 00 00 00 00 c5 da d2 54 4f c9 79 9094 ce 1c e9 0b c9 d0 3e')
209 challenge = NTLMAuthenticate.from_bytes(data)
210 print(repr(challenge))
211
212 def test_reconstrut(data = None):
213 print('=== reconstruct===')
214 if not data:
215 auth_test_data = bytes.fromhex('4e544c4d5353500003000000180018007c000000180118019400000008000800580000000c000c0060000000100010006c00000010001000ac010000158288e20a00d73a0000000f0d98eb57e9c52820709c99b98ca321a15400450053005400760069006300740069006d00570049004e0031003000580036003400000000000000000000000000000000000000000000000000fade3940b9381c53c91ddcdd0d44000b0101000000000000aec600bfc5fdd4011bfa20699d7628730000000002000800540045005300540001001200570049004e003200300031003900410044000400120074006500730074002e0063006f007200700003002600570049004e003200300031003900410044002e0074006500730074002e0063006f007200700007000800aec600bfc5fdd40106000400020000000800300030000000000000000000000000200000527d27f234de743760966384d36f61ae2aa4fc2a380699f8caa600011b486d890a0010000000000000000000000000000000000009001e0063006900660073002f00310030002e00310030002e00310030002e003200000000000000000000000000fd67edfb41c09465a91fd733deb0b55b')
216 else:
217 auth_test_data = data
218 challenge = NTLMAuthenticate.from_bytes(auth_test_data)
219 print(repr(challenge))
220 auth_test_data_verify = challenge.to_bytes()
221 print('====== reconstructed ====')
222 print(hexdump(auth_test_data_verify))
223 print('====== original ====')
224 print(hexdump(auth_test_data))
225 assert auth_test_data == auth_test_data_verify
226
227
228 def test_construct():
229 pass
230
+0
-162
msldap/authentication/ntlm/messages/challenge.py less more
0 import os
1 import io
2 import base64
3
4 from msldap.authentication.ntlm.structures.fields import Fields
5 from msldap.authentication.ntlm.structures.negotiate_flags import NegotiateFlags
6 from msldap.authentication.ntlm.structures.version import Version
7 from msldap.authentication.ntlm.structures.avpair import AVPairs
8
9 from msldap.authentication.ntlm.templates.server import NTLMServerTemplates
10
11 # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/801a4681-8809-4be9-ab0d-61dcfe762786
12 class NTLMChallenge:
13 def __init__(self):
14 self.Signature = b'NTLMSSP\x00'
15 self.MessageType = 2
16 self.TargetNameFields = None
17 self.NegotiateFlags = None
18 self.ServerChallenge = None
19 self.Reserved = b'\x00'*8
20 self.TargetInfoFields = None
21 self.Version = None
22 self.Payload = None
23
24 self.TargetName = None
25 self.TargetInfo = None
26
27
28 @staticmethod
29 def from_bytes(bbuff):
30 return NTLMChallenge.from_buffer(io.BytesIO(bbuff))
31
32 @staticmethod
33 def from_buffer(buff):
34 t = NTLMChallenge()
35 t.Signature = buff.read(8)
36 t.MessageType = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
37 t.TargetNameFields = Fields.from_buffer(buff)
38 t.NegotiateFlags = NegotiateFlags(int.from_bytes(buff.read(4), byteorder = 'little', signed = False))
39 t.ServerChallenge = buff.read(8)
40 t.Reserved = buff.read(8)
41 t.TargetInfoFields = Fields.from_buffer(buff)
42
43 if t.NegotiateFlags & NegotiateFlags.NEGOTIATE_VERSION:
44 t.Version = Version.from_buffer(buff)
45
46 currPos = buff.tell()
47 t.Payload = buff.read()
48
49 if t.TargetNameFields.length != 0:
50 buff.seek(t.TargetNameFields.offset, io.SEEK_SET)
51 raw_data = buff.read(t.TargetNameFields.length)
52 try:
53 t.TargetName = raw_data.decode('utf-16le')
54 except UnicodeDecodeError:
55 # yet another cool bug.
56 t.TargetName = raw_data.decode('utf-8')
57
58 if t.TargetInfoFields.length != 0:
59 buff.seek(t.TargetInfoFields.offset, io.SEEK_SET)
60 raw_data = buff.read(t.TargetInfoFields.length)
61 t.TargetInfo = AVPairs.from_bytes(raw_data)
62
63
64
65 return t
66
67 @staticmethod
68 def construct_from_template(templateName, challenge = os.urandom(8), ess = True):
69 version = NTLMServerTemplates[templateName]['version']
70 challenge = challenge
71 targetName = NTLMServerTemplates[templateName]['targetname']
72 targetInfo = NTLMServerTemplates[templateName]['targetinfo']
73 targetInfo = NTLMServerTemplates[templateName]['targetinfo']
74 flags = NTLMServerTemplates[templateName]['flags']
75 if ess:
76 flags |= NegotiateFlags.NEGOTIATE_EXTENDED_SESSIONSECURITY
77 else:
78 flags &= ~NegotiateFlags.NEGOTIATE_EXTENDED_SESSIONSECURITY
79
80 return NTLMChallenge.construct(challenge=challenge, targetName = targetName, targetInfo = targetInfo, version = version, flags= flags)
81
82
83 # TODO: needs some clearning up (like re-calculating flags when needed)
84 @staticmethod
85 def construct(challenge = os.urandom(8), targetName = None, targetInfo = None, version = None, flags = None):
86 pos = 48
87 if version:
88 pos += 8
89 t = NTLMChallenge()
90 t.NegotiateFlags = flags
91 t.Version = version
92 t.ServerChallenge = challenge
93 t.TargetName = targetName
94 t.TargetInfo = targetInfo
95
96 t.TargetNameFields = Fields(len(t.TargetName.encode('utf-16le')),pos)
97 t.TargetInfoFields = Fields(len(t.TargetInfo.to_bytes()), pos + len(t.TargetName.encode('utf-16le')))
98
99 t.Payload = t.TargetName.encode('utf-16le')
100 t.Payload += t.TargetInfo.to_bytes()
101
102 return t
103
104 def to_bytes(self):
105 tn = self.TargetName.encode('utf-16le')
106 ti = self.TargetInfo.to_bytes()
107
108 buff = self.Signature
109 buff += self.MessageType.to_bytes(4, byteorder = 'little', signed = False)
110 buff += self.TargetNameFields.to_bytes()
111 buff += self.NegotiateFlags.to_bytes(4, byteorder = 'little', signed = False)
112 buff += self.ServerChallenge
113 buff += self.Reserved
114 buff += self.TargetInfoFields.to_bytes()
115 if self.Version:
116 buff += self.Version.to_bytes()
117 buff += self.Payload
118
119 return buff
120
121 def __repr__(self):
122 t = '== NTLMChallenge ==\r\n'
123 t += 'Signature : %s\r\n' % repr(self.Signature)
124 t += 'MessageType : %s\r\n' % repr(self.MessageType)
125 t += 'ServerChallenge: %s\r\n' % repr(self.ServerChallenge)
126 t += 'TargetName : %s\r\n' % repr(self.TargetName)
127 t += 'TargetInfo : %s\r\n' % repr(self.TargetInfo)
128 return t
129
130 def toBase64(self):
131 return base64.b64encode(self.to_bytes()).decode('ascii')
132
133
134 def test():
135 test_reconstrut()
136 test_construct()
137 test_template()
138
139 def test_reconstrut(data = None):
140 print('=== reconstruct===')
141 if not data:
142 challenge_test_data = bytes.fromhex('4e544c4d53535000020000000800080038000000158289e2a7314a557bdb11bf000000000000000072007200400000000a0063450000000f540045005300540002000800540045005300540001001200570049004e003200300031003900410044000400120074006500730074002e0063006f007200700003002600570049004e003200300031003900410044002e0074006500730074002e0063006f007200700007000800aec600bfc5fdd40100000000')
143 else:
144 challenge_test_data = data
145 challenge = NTLMChallenge.from_bytes(challenge_test_data)
146 print(repr(challenge))
147 challenge_test_data_verify = challenge.to_bytes()
148 print('====== reconstructed ====')
149 print(hexdump(challenge_test_data_verify))
150 print('====== original ====')
151 print(hexdump(challenge_test_data))
152 assert challenge_test_data == challenge_test_data_verify
153
154 def test_template():
155
156 challenge = NTLMChallenge.construct_from_template('Windows2003')
157 test_reconstrut(challenge.to_bytes())
158
159 def test_construct():
160 pass
161
+0
-169
msldap/authentication/ntlm/messages/negotiate.py less more
0 import io
1
2
3 from msldap.authentication.ntlm.structures.fields import Fields
4 from msldap.authentication.ntlm.structures.negotiate_flags import NegotiateFlags
5 from msldap.authentication.ntlm.structures.version import Version
6
7 # https://msdn.microsoft.com/en-us/library/cc236641.aspx
8 class NTLMNegotiate:
9 def __init__(self):
10 self.Signature = b'NTLMSSP\x00'
11 self.MessageType = 1
12 self.NegotiateFlags = None
13 self.DomainNameFields = None
14 self.WorkstationFields = None
15 self.Version = None
16 self.Payload = None
17
18 ####High-level variables
19 self.Domain = None
20 self.Workstation = None
21
22 @staticmethod
23 def from_bytes(bbuff):
24 return NTLMNegotiate.from_buffer(io.BytesIO(bbuff))
25
26 @staticmethod
27 def from_buffer(buff):
28 t = NTLMNegotiate()
29 t.Signature = buff.read(8)
30 t.MessageType = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
31 t.NegotiateFlags = NegotiateFlags(int.from_bytes(buff.read(4), byteorder = 'little', signed = False))
32 t.DomainNameFields = Fields.from_buffer(buff)
33 t.WorkstationFields = Fields.from_buffer(buff)
34
35 if t.NegotiateFlags & NegotiateFlags.NEGOTIATE_VERSION:
36 t.Version = Version.from_buffer(buff)
37
38
39 currPos = buff.tell()
40 t.Payload = buff.read()
41
42 #currPos = buff.tell()
43
44 if t.DomainNameFields.length != 0:
45 buff.seek(t.DomainNameFields.offset, io.SEEK_SET)
46 raw_data = buff.read(t.WorkstationFields.length)
47 #print(raw_data)
48 #print(t.DomainNameFields.length)
49 try:
50 t.Domain = raw_data.decode('utf-16le')
51 except UnicodeDecodeError:
52 # yet another cool bug.
53 t.Domain = raw_data.decode('utf-8')
54
55 if t.WorkstationFields.length != 0:
56 buff.seek(t.WorkstationFields.offset, io.SEEK_SET)
57 raw_data = buff.read(t.WorkstationFields.length)
58 try:
59 t.Workstation = raw_data.decode('utf-16le')
60 except UnicodeDecodeError:
61 # yet another cool bug.
62 t.Workstation = raw_data.decode('utf-8')
63
64 #buff.seek(currPos, io.SEEK_SET)
65
66 return t
67
68 @staticmethod
69 def construct(flags, domainname = None, workstationname = None, version = None):
70 nego = NTLMNegotiate()
71 nego.NegotiateFlags = flags
72 nego.Payload = b''
73
74 payload_pos = 32
75 if flags & NegotiateFlags.NEGOTIATE_VERSION:
76 if not version:
77 raise Exception('NEGOTIATE_VERSION set but no Version supplied!')
78 payload_pos += 8
79 nego.Version = version
80
81
82
83 if nego.NegotiateFlags & NegotiateFlags.NEGOTIATE_OEM_DOMAIN_SUPPLIED and domainname:
84 data = domainname.encode('utf-16le')
85 nego.Payload += data
86 nego.DomainNameFields = Fields(len(data), payload_pos)
87 payload_pos += len(data)
88 nego.Domain = data
89
90 else:
91 nego.DomainNameFields = Fields(0,0)
92
93 if nego.NegotiateFlags & NegotiateFlags.NEGOTIATE_OEM_WORKSTATION_SUPPLIED and workstationname:
94 data = workstationname.encode('utf-16le')
95 nego.Payload += data
96 nego.WorkstationFields = Fields(len(data), payload_pos)
97 payload_pos += len(data)
98 nego.Workstation = data
99
100 else:
101 nego.WorkstationFields = Fields(0,0)
102
103 return nego
104
105 def to_bytes(self):
106 t = b''
107 t += self.Signature
108 t += self.MessageType.to_bytes(4, byteorder = 'little', signed = False)
109 t += self.NegotiateFlags.to_bytes(4, byteorder = 'little', signed = False)
110 t += self.DomainNameFields.to_bytes()
111 t += self.WorkstationFields.to_bytes()
112 if self.Version:
113 t += self.Version.to_bytes()
114 t += self.Payload
115 return t
116
117 def __repr__(self):
118 t = '== NTLMNegotiate ==\r\n'
119 t += 'Signature : %s\r\n' % repr(self.Signature)
120 t += 'MessageType: %s\r\n' % repr(self.MessageType)
121 t += 'NegotiateFlags: %s\r\n' % repr(self.NegotiateFlags)
122 t += 'Version : %s\r\n' % repr(self.Version)
123 t += 'Domain : %s\r\n' % repr(self.Domain)
124 t += 'Workstation: %s\r\n' % repr(self.Workstation)
125
126 return t
127
128 def test():
129 test_reconstrut()
130 test_construct()
131
132 def test_reconstrut(data = None):
133 print('=== reconstruct===')
134 if not data:
135 nego_test_data = bytes.fromhex('4e544c4d5353500001000000978208e2000000000000000000000000000000000a00d73a0000000f')
136 else:
137 nego_test_data = data
138 nego = NTLMNegotiate.from_bytes(nego_test_data)
139 print(repr(nego))
140 nego_test_data_verify = nego.to_bytes()
141 assert nego_test_data == nego_test_data_verify
142
143 def test_construct():
144 flags = NegotiateFlags.NEGOTIATE_56|NegotiateFlags.NEGOTIATE_KEY_EXCH|NegotiateFlags.NEGOTIATE_128|\
145 NegotiateFlags.NEGOTIATE_VERSION|\
146 NegotiateFlags.NEGOTIATE_EXTENDED_SESSIONSECURITY|\
147 NegotiateFlags.NEGOTIATE_ALWAYS_SIGN|NegotiateFlags.NEGOTIATE_NTLM|NegotiateFlags.NEGOTIATE_LM_KEY|\
148 NegotiateFlags.NEGOTIATE_SIGN|NegotiateFlags.REQUEST_TARGET|NegotiateFlags.NTLM_NEGOTIATE_OEM|NegotiateFlags.NEGOTIATE_UNICODE|\
149 NegotiateFlags.NEGOTIATE_OEM_WORKSTATION_SUPPLIED|NegotiateFlags.NEGOTIATE_OEM_DOMAIN_SUPPLIED
150 nego = NTLMNegotiate.construct(flags, domainname = "alma.com", workstationname = "testjoe", version = Version.construct())
151 nego.to_bytes()
152 print(repr(nego))
153
154 test_reconstrut(nego.to_bytes())
155
156 flags = NegotiateFlags.NEGOTIATE_56|NegotiateFlags.NEGOTIATE_KEY_EXCH|NegotiateFlags.NEGOTIATE_128|\
157 NegotiateFlags.NEGOTIATE_EXTENDED_SESSIONSECURITY|\
158 NegotiateFlags.NEGOTIATE_ALWAYS_SIGN|NegotiateFlags.NEGOTIATE_NTLM|NegotiateFlags.NEGOTIATE_LM_KEY|\
159 NegotiateFlags.NEGOTIATE_SIGN|NegotiateFlags.REQUEST_TARGET|NegotiateFlags.NTLM_NEGOTIATE_OEM|NegotiateFlags.NEGOTIATE_UNICODE|\
160 NegotiateFlags.NEGOTIATE_OEM_WORKSTATION_SUPPLIED|NegotiateFlags.NEGOTIATE_OEM_DOMAIN_SUPPLIED
161 nego = NTLMNegotiate.construct(flags, domainname = "alma.com", workstationname = "testjoe2")
162 print(nego.to_bytes())
163 print(repr(nego))
164
165 test_reconstrut(nego.to_bytes())
166
167 if __name__ == '__main__':
168 test()
+0
-154
msldap/authentication/ntlm/multiplexor.py less more
0 #
1 #
2 # Interface to support remote authentication via multiplexor
3 #
4 # Connects to the multiplexor server, and starts an SSPI server locally for the specific agentid
5 # SSPI server will be used to perform NTLM authentication remotely,
6 # while constructing a local NTLM authentication object
7 # After the auth finishes, it also grabs the sessionkey.
8 # The NTLM object can be used in future operations (encrypt/decrypt/sign) locally
9 # without the need of future remote calls
10 #
11
12 from msldap import logger
13 from msldap.authentication.ntlm.native import NTLMAUTHHandler, NTLMHandlerSettings
14 from multiplexor.operator.external.sspi import SSPINTLMClient
15 from multiplexor.operator import MultiplexorOperator
16 import enum
17
18 class ISC_REQ(enum.IntFlag):
19 DELEGATE = 1
20 MUTUAL_AUTH = 2
21 REPLAY_DETECT = 4
22 SEQUENCE_DETECT = 8
23 CONFIDENTIALITY = 16
24 USE_SESSION_KEY = 32
25 PROMPT_FOR_CREDS = 64
26 USE_SUPPLIED_CREDS = 128
27 ALLOCATE_MEMORY = 256
28 USE_DCE_STYLE = 512
29 DATAGRAM = 1024
30 CONNECTION = 2048
31 CALL_LEVEL = 4096
32 FRAGMENT_SUPPLIED = 8192
33 EXTENDED_ERROR = 16384
34 STREAM = 32768
35 INTEGRITY = 65536
36 IDENTIFY = 131072
37 NULL_SESSION = 262144
38 MANUAL_CRED_VALIDATION = 524288
39 RESERVED1 = 1048576
40 FRAGMENT_TO_FIT = 2097152
41 HTTP = 0x10000000
42
43 #
44 #
45 # Interface to support remote authentication via multiplexor
46 #
47 # Connects to the multiplexor server, and starts an SSPI server locally for the specific agentid
48 # SSPI server will be used to perform NTLM authentication remotely,
49 # while constructing a local NTLM authentication object
50 # After the auth finishes, it also grabs the sessionkey.
51 # The NTLM object can be used in future operations (encrypt/decrypt/sign) locally
52 # without the need of future remote calls
53 #
54
55 class MSLDAPNTLMMultiplexor:
56 def __init__(self, settings):
57 self.settings = settings
58 self.mode = None #'CLIENT'
59 self.sspi = None
60 self.operator = None
61 self.client = None
62 self.target = None
63 self.seq_number = 0
64
65 self.session_key = None
66 self.ntlm_ctx = NTLMAUTHHandler(NTLMHandlerSettings(None, 'MANUAL'))
67
68 def setup(self):
69 return
70
71 @property
72 def ntlmChallenge(self):
73 return self.ntlm_ctx.ntlmChallenge
74
75 def get_sealkey(self, mode = 'Client'):
76 return self.ntlm_ctx.get_sealkey(mode = mode)
77
78 def get_signkey(self, mode = 'Client'):
79 return self.ntlm_ctx.get_signkey(mode = mode)
80
81 def get_session_key(self):
82 return self.session_key
83
84 def is_extended_security(self):
85 return self.ntlm_ctx.is_extended_security()
86
87 def get_seq_number(self):
88 return self.seq_number
89
90 def signing_needed(self):
91 return self.ntlm_ctx.signing_needed()
92
93 def encryption_needed(self):
94 return self.ntlm_ctx.encryption_needed()
95
96 async def encrypt(self, data, message_no):
97 return await self.ntlm_ctx.encrypt(data, message_no)
98
99 async def decrypt(self, data, sequence_no, direction='init', auth_data=None):
100 return await self.ntlm_ctx.decrypt(data, sequence_no, direction=direction, auth_data=auth_data)
101
102 async def sign(self, data, message_no, direction=None, reset_cipher = False):
103 return await self.ntlm_ctx.sign(data, message_no, direction=None, reset_cipher = reset_cipher)
104
105 async def authenticate(self, authData = None, flags = None, seq_number = 0, cb_data=None):
106 is_rpc = False
107 if self.sspi is None:
108 res, err = await self.start_remote_sspi()
109 if err is not None:
110 return None, None, err
111
112 if is_rpc is True and flags is None:
113 flags = ISC_REQ.REPLAY_DETECT | ISC_REQ.CONFIDENTIALITY| ISC_REQ.USE_SESSION_KEY| ISC_REQ.INTEGRITY| ISC_REQ.SEQUENCE_DETECT| ISC_REQ.CONNECTION
114 flags = int(flags)
115
116 if authData is None:
117 data, res = await self.sspi.authenticate(flags = flags)
118 if res is None:
119 self.ntlm_ctx.load_negotiate(data)
120 return data, res, None
121 else:
122 self.ntlm_ctx.load_challenge( authData)
123 data, res = await self.sspi.challenge(authData, flags = flags)
124 if res is None:
125 self.ntlm_ctx.load_authenticate( data)
126 self.session_key, res = await self.sspi.get_session_key()
127 if res is None:
128 self.ntlm_ctx.load_sessionkey(self.get_session_key())
129
130 return data, res, None
131
132
133 async def start_remote_sspi(self):
134 try:
135 #print(self.settings.get_url())
136 self.operator = MultiplexorOperator(self.settings.get_url(), logging_sink=logger)
137 await self.operator.connect()
138 #creating virtual sspi server
139 server_info = await self.operator.start_sspi(self.settings.agent_id)
140 #print(server_info)
141
142 sspi_url = 'ws://%s:%s' % (server_info['listen_ip'], server_info['listen_port'])
143
144 #print(sspi_url)
145 self.sspi = SSPINTLMClient(sspi_url)
146 await self.sspi.connect()
147 return True, None
148 except Exception as e:
149 import traceback
150 traceback.print_exc()
151 return None, e
152
153
+0
-450
msldap/authentication/ntlm/native.py less more
0 import os
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
8 from msldap.authentication.ntlm.templates.server import NTLMServerTemplates
9 from msldap.authentication.ntlm.templates.client import NTLMClientTemplates
10 from msldap.authentication.ntlm.structures.negotiate_flags import NegotiateFlags
11 from msldap.authentication.ntlm.structures.version import Version
12 from msldap.authentication.ntlm.structures.ntlmssp_message_signature import NTLMSSP_MESSAGE_SIGNATURE
13 from msldap.authentication.ntlm.structures.ntlmssp_message_signature_noext import NTLMSSP_MESSAGE_SIGNATURE_NOEXT
14 from msldap.authentication.ntlm.messages.negotiate import NTLMNegotiate
15 from msldap.authentication.ntlm.messages.challenge import NTLMChallenge
16 from msldap.authentication.ntlm.messages.authenticate import NTLMAuthenticate
17 from msldap.authentication.ntlm.creds_calc import *
18 from msldap.crypto.symmetric import RC4
19
20
21 class NTLMHandlerSettings:
22 def __init__(self, credential, mode = 'CLIENT', template_name = 'Windows10_15063', custom_template = None):
23 self.credential = credential
24 self.mode = mode
25 self.template_name = template_name
26 self.custom_template = custom_template #for custom templates, must be dict
27
28 self.encrypt = False
29
30 self.template = None
31 self.ntlm_downgrade = False
32
33 self.construct_message_template()
34
35 def construct_message_template(self):
36 if self.mode.upper() == 'MANUAL':
37 return
38
39 if not self.template_name:
40 if not self.custom_template:
41 raise Exception('No NTLM tamplate specified!')
42
43 self.template = self.custom_template
44
45 self.encrypt = self.credential.encrypt
46
47 if self.encrypt is True:
48 self.template_name = 'Windows10_15063_channel'
49
50 if self.mode.upper() == 'SERVER':
51 if self.template_name in NTLMServerTemplates:
52 self.template = NTLMServerTemplates[self.template_name]
53 else:
54 raise Exception('No NTLM server template found with name %s' % self.template_name)
55
56 else:
57 if self.template_name in NTLMClientTemplates:
58 self.template = NTLMClientTemplates[self.template_name]
59 if 'ntlm_downgrade' in self.template:
60 self.ntlm_downgrade = self.template['ntlm_downgrade']
61 else:
62 raise Exception('No NTLM server template found with name %s' % self.template_name)
63
64
65 class NTLMAUTHHandler:
66 def __init__(self, settings):
67 self.settings = settings #NTLMHandlerSettings
68
69 self.mode = None
70 self.flags = None
71 self.challenge = None
72
73 self.ntlmNegotiate = None #ntlm Negotiate message from client
74 self.ntlmChallenge = None #ntlm Challenge message to client
75 self.ntlmAuthenticate = None #ntlm Authenticate message from client
76
77 self.ntlmNegotiate_raw = None #message as bytes, as it's recieved/sent
78 self.ntlmChallenge_raw = None #message as bytes, as it's recieved/sent
79 self.ntlmAuthenticate_raw = None #message as bytes, as it's recieved/sent
80
81
82 self.EncryptedRandomSessionKey = None
83 self.RandomSessionKey = None
84 self.SessionBaseKey = None
85 self.KeyExchangeKey = None
86
87 self.SignKey_client = None
88 self.SealKey_client = None
89 self.SignKey_server = None
90 self.SealKey_server = None
91
92 self.crypthandle_client = None
93 self.crypthandle_server = None
94 #self.signhandle_server = None doesnt exists, only crypthandle
95 #self.signhandle_client = None doesnt exists, only crypthandle
96
97 self.seq_number = 0
98 self.iteration_cnt = 0
99 self.ntlm_credentials = None
100 self.timestamp = None #used in unittest only!
101 self.extra_info = None
102 self.setup()
103
104 def setup(self):
105 self.mode = self.settings.mode
106 if self.mode.upper() == 'MANUAL':
107 #for passign the messages automatically with the sessionbasekey, the using this class for sign and seal
108 return
109
110 if 'challenge' not in self.settings.template:
111 self.challenge = os.urandom(8)
112 else:
113 self.challenge = self.settings.template['challenge']
114 self.flags = self.settings.template['flags']
115 if 'session_key' in self.settings.template:
116 self.RandomSessionKey = self.settings.template['session_key']
117
118 self.timestamp = self.settings.template.get('timestamp') #used in unittest only!
119
120 def load_negotiate(self, data):
121 self.ntlmNegotiate = NTLMNegotiate.from_bytes(data)
122
123 def load_challenge(self, data):
124 self.ntlmChallenge = NTLMChallenge.from_bytes(data)
125
126 def load_authenticate(self, data):
127 self.ntlmAuthenticate = NTLMAuthenticate.from_bytes(data)
128
129 def load_sessionkey(self, data):
130 self.RandomSessionKey = data
131 self.setup_crypto()
132
133 def get_seq_number(self):
134 return self.seq_number
135
136 def set_sign(self, tf = True):
137 if tf == True:
138 self.flags |= NegotiateFlags.NEGOTIATE_SIGN
139 else:
140 self.flags &= ~NegotiateFlags.NEGOTIATE_SIGN
141
142 def set_seal(self, tf = True):
143 if tf == True:
144 self.flags |= NegotiateFlags.NEGOTIATE_SEAL
145 else:
146 self.flags &= ~NegotiateFlags.NEGOTIATE_SEAL
147
148 def set_version(self, tf = True):
149 if tf == True:
150 self.flags |= NegotiateFlags.NEGOTIATE_VERSION
151 else:
152 self.flags &= ~NegotiateFlags.NEGOTIATE_VERSION
153
154 def is_extended_security(self):
155 return NegotiateFlags.NEGOTIATE_EXTENDED_SESSIONSECURITY in self.ntlmChallenge.NegotiateFlags
156
157 #def get_extra_info(self):
158 # self.extra_info = NTLMServerInfo.from_challenge(self.ntlmChallenge)
159 # return self.extra_info
160
161 def MAC(self, handle, signingKey, seqNum, message):
162 if self.is_extended_security() == True:
163 msg = NTLMSSP_MESSAGE_SIGNATURE()
164 if NegotiateFlags.NEGOTIATE_KEY_EXCH in self.ntlmChallenge.NegotiateFlags:
165 tt = struct.pack('<i', seqNum) + message
166 t = hmac_md5(signingKey)
167 t.update(tt)
168
169 msg.Checksum = handle(t.digest()[:8])
170 msg.SeqNum = seqNum
171 seqNum += 1
172 else:
173 t = hmac_md5(signingKey)
174 t.update(struct.pack('<i',seqNum)+message)
175 msg.Checksum = t.digest()[:8]
176 msg.SeqNum = seqNum
177 seqNum += 1
178
179 else:
180 raise Exception('Not implemented!')
181 #t = struct.pack('<I',binascii.crc32(message)& 0xFFFFFFFF)
182 #randompad = 0
183 #msg = NTLMSSP_MESSAGE_SIGNATURE_NOEXT()
184 #msg.RandomPad = handle(struct.pack('<I',randompad))
185 #msg.Checksum = struct.unpack('<I',handle(messageSignature['Checksum']))[0]
186
187 return msg.to_bytes()
188
189 async def encrypt(self, data, sequence_no):
190 """
191 This function is to support SSPI encryption.
192 """
193 return self.SEAL(
194 #self.SignKey_client,
195 self.SignKey_client,
196 self.SealKey_client,
197 data,
198 data,
199 sequence_no,
200 self.crypthandle_client.encrypt
201 )
202
203 async def decrypt(self, data, sequence_no, direction='init', auth_data=None):
204 """
205 This function is to support SSPI decryption.
206 """
207 edata = data[16:]
208 srv_sig = NTLMSSP_MESSAGE_SIGNATURE.from_bytes(data[:16])
209 sealedMessage = self.crypthandle_server.encrypt(edata)
210 signature = self.MAC(self.crypthandle_server.encrypt, self.SignKey_server, srv_sig.SeqNum, sealedMessage)
211 #print('seqno %s' % sequence_no)
212 #print('Srv sig: %s' % data[:16])
213 #print('Calc sig: %s' % signature)
214
215 return sealedMessage, None
216
217 async def sign(self, data, message_no, direction=None, reset_cipher = False):
218 """
219 Singing outgoing messages. The reset_cipher parameter is needed for calculating mechListMIC.
220 """
221 #print('sign data : %s' % data)
222 #print('sign message_no : %s' % message_no)
223 #print('sign direction : %s' % direction)
224 signature = self.MAC(self.crypthandle_client.encrypt, self.SignKey_client, message_no, data)
225 if reset_cipher is True:
226 self.crypthandle_client = RC4(self.SealKey_client)
227 self.crypthandle_server = RC4(self.SealKey_server)
228 self.seq_number += 1
229 return signature
230
231 async def verify(self, data, signature):
232 """
233 Verifying incoming server message
234 """
235 signature_struct = NTLMSSP_MESSAGE_SIGNATURE.from_bytes(signature)
236 calc_sig = self.MAC(self.crypthandle_server.encrypt, self.SignKey_server, signature_struct.SeqNum, data)
237 #print('server signature : %s' % signature)
238 #print('calculates signature: %s' % calc_sig)
239 return signature == calc_sig
240
241 def SEAL(self, signingKey, sealingKey, messageToSign, messageToEncrypt, seqNum, cipher_encrypt):
242 """
243 This is the official SEAL function.
244 """
245 sealedMessage = cipher_encrypt(messageToEncrypt)
246 signature = self.MAC(cipher_encrypt, signingKey, seqNum, messageToSign)
247 return sealedMessage, signature
248
249 def SIGN(self, signingKey, message, seqNum, cipher_encrypt):
250 """
251 This is the official SIGN function.
252 """
253 return self.MAC(cipher_encrypt, signingKey, seqNum, message)
254
255 def signing_needed(self):
256 return (
257 NegotiateFlags.NEGOTIATE_SIGN in self.ntlmChallenge.NegotiateFlags or \
258 NegotiateFlags.NEGOTIATE_SEAL in self.ntlmChallenge.NegotiateFlags
259 )
260
261 def encryption_needed(self):
262 return NegotiateFlags.NEGOTIATE_SEAL in self.ntlmChallenge.NegotiateFlags
263
264 def calc_sealkey(self, mode = 'Client'):
265 if NegotiateFlags.NEGOTIATE_EXTENDED_SESSIONSECURITY in self.ntlmChallenge.NegotiateFlags:
266 if NegotiateFlags.NEGOTIATE_128 in self.ntlmChallenge.NegotiateFlags:
267 sealkey = self.RandomSessionKey
268 elif NegotiateFlags.NEGOTIATE_56 in self.ntlmChallenge.NegotiateFlags:
269 sealkey = self.RandomSessionKey[:7]
270 else:
271 sealkey = self.RandomSessionKey[:5]
272
273 if mode == 'Client':
274 md5 = hashlib.new('md5')
275 md5.update(sealkey + b'session key to client-to-server sealing key magic constant\x00')
276 sealkey = md5.digest()
277 else:
278 md5 = hashlib.new('md5')
279 md5.update(sealkey + b'session key to server-to-client sealing key magic constant\x00')
280 sealkey = md5.digest()
281
282 elif NegotiateFlags.NEGOTIATE_56 in self.ntlmChallenge.NegotiateFlags:
283 sealkey = self.RandomSessionKey[:7] + b'\xa0'
284 else:
285 sealkey = self.RandomSessionKey[:5] + b'\xe5\x38\xb0'
286
287 if mode == 'Client':
288 self.SealKey_client = sealkey
289 if sealkey is not None:
290 self.crypthandle_client = RC4(self.SealKey_client)
291 else:
292 self.SealKey_server = sealkey
293 if sealkey is not None:
294 self.crypthandle_server = RC4(self.SealKey_server)
295
296 return sealkey
297
298 def calc_signkey(self, mode = 'Client'):
299 if NegotiateFlags.NEGOTIATE_EXTENDED_SESSIONSECURITY in self.ntlmChallenge.NegotiateFlags:
300 if mode == 'Client':
301 md5 = hashlib.new('md5')
302 md5.update(self.RandomSessionKey + b"session key to client-to-server signing key magic constant\x00")
303 signkey = md5.digest()
304 else:
305 md5 = hashlib.new('md5')
306 md5.update(self.RandomSessionKey + b"session key to server-to-client signing key magic constant\x00")
307 signkey = md5.digest()
308 else:
309 signkey = None
310
311 if mode == 'Client':
312 self.SignKey_client = signkey
313
314 else:
315 self.SignKey_server = signkey
316
317 return signkey
318
319 def get_session_key(self):
320 return self.RandomSessionKey
321
322 def get_sealkey(self, mode = 'Client'):
323 if mode == 'Client':
324 return self.SealKey_client
325 else:
326 return self.SealKey_server
327
328 def get_signkey(self, mode = 'Client'):
329 if mode == 'Client':
330 return self.SignKey_client
331 else:
332 return self.SignKey_server
333
334 def setup_crypto(self):
335 if not self.RandomSessionKey:
336 self.RandomSessionKey = os.urandom(16)
337
338 if self.mode.upper() != 'MANUAL':
339 #this check is here to provide the option to load the messages + the sessionbasekey manually
340 #then you will be able to use the sign and seal functions provided by this class
341 self.SessionBaseKey = self.ntlm_credentials.SessionBaseKey
342
343 rc4 = RC4(self.KeyExchangeKey)
344 self.EncryptedRandomSessionKey = rc4.encrypt(self.RandomSessionKey)
345
346 self.calc_sealkey('Client')
347 self.calc_sealkey('Server')
348 self.calc_signkey('Client')
349 self.calc_signkey('Server')
350
351 async def authenticate(self, authData, flags = None, seq_number = 0, cb_data = None):
352 if self.mode.upper() == 'CLIENT':
353 if self.iteration_cnt == 0:
354 if authData is not None:
355 raise Exception('First call as client MUST be with empty data!')
356
357 self.iteration_cnt += 1
358 #negotiate message was already calulcated in setup
359 self.ntlmNegotiate = NTLMNegotiate.construct(self.flags, domainname = self.settings.template['domain_name'], workstationname = self.settings.template['workstation_name'], version = self.settings.template.get('version'))
360 self.ntlmNegotiate_raw = self.ntlmNegotiate.to_bytes()
361 return self.ntlmNegotiate_raw, True, None
362
363 else:
364 #server challenge incoming
365 self.ntlmChallenge_raw = authData
366 self.ntlmChallenge = NTLMChallenge.from_bytes(authData)
367
368 ##################self.flags = self.ntlmChallenge.NegotiateFlags
369
370 #we need to calculate the response based on the credential and the settings flags
371 if self.settings.ntlm_downgrade == True:
372 #NTLMv1 authentication
373 # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/464551a8-9fc4-428e-b3d3-bc5bfb2e73a5
374
375 #check if we authenticate as guest
376 if self.settings.credential.is_guest == True:
377 lmresp = LMResponse()
378 lmresp.Response = b'\x00'
379 self.ntlmAuthenticate = NTLMAuthenticate.construct(self.flags, lm_response= lmresp)
380 return self.ntlmAuthenticate.to_bytes(), False, None
381
382 if self.flags & NegotiateFlags.NEGOTIATE_EXTENDED_SESSIONSECURITY:
383 #Extended auth!
384 self.ntlm_credentials = netntlm_ess.construct(self.ntlmChallenge.ServerChallenge, self.challenge, self.settings.credential)
385
386 self.KeyExchangeKey = self.ntlm_credentials.calc_key_exchange_key()
387 self.setup_crypto()
388
389 self.ntlmAuthenticate = NTLMAuthenticate.construct(self.flags, lm_response= self.ntlm_credentials.LMResponse, nt_response = self.ntlm_credentials.NTResponse, version = self.ntlmNegotiate.Version, encrypted_session = self.EncryptedRandomSessionKey)
390 else:
391 self.ntlm_credentials = netntlm.construct(self.ntlmChallenge.ServerChallenge, self.settings.credential)
392
393 self.KeyExchangeKey = self.ntlm_credentials.calc_key_exchange_key(with_lm = self.flags & NegotiateFlags.NEGOTIATE_LM_KEY, non_nt_session_key = self.flags & NegotiateFlags.REQUEST_NON_NT_SESSION_KEY)
394 self.setup_crypto()
395 self.ntlmAuthenticate = NTLMAuthenticate.construct(self.flags, lm_response= self.ntlm_credentials.LMResponse, nt_response = self.ntlm_credentials.NTResponse, version = self.ntlmNegotiate.Version, encrypted_session = self.EncryptedRandomSessionKey)
396
397
398
399 else:
400 #NTLMv2
401 # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/5e550938-91d4-459f-b67d-75d70009e3f3
402 if self.settings.credential.is_guest == True:
403 lmresp = LMResponse()
404 lmresp.Response = b'\x00'
405 self.ntlmAuthenticate = NTLMAuthenticate.construct(self.flags, lm_response= lmresp)
406 return self.ntlmAuthenticate.to_bytes(), False, None
407
408 else:
409 #comment this out for testing!
410 ti = self.ntlmChallenge.TargetInfo
411 ti[AVPAIRType.MsvAvTargetName] = 'ldaps/%s' % ti[AVPAIRType.MsvAvDnsComputerName]
412 if cb_data is not None:
413 md5_ctx = hashlib.new('md5')
414 md5_ctx.update(cb_data)
415 ti[AVPAIRType.MsvChannelBindings] = md5_ctx.digest()
416 ###
417
418 self.ntlm_credentials = netntlmv2.construct(self.ntlmChallenge.ServerChallenge, self.challenge, ti, self.settings.credential, timestamp = self.timestamp)
419 self.KeyExchangeKey = self.ntlm_credentials.calc_key_exchange_key()
420 self.setup_crypto()
421
422 #TODO: if "ti" / targetinfo in the challenge message has "MsvAvFlags" type and the bit for MIC is set (0x00000002) we need to send a MIC. probably...
423 mic = None
424
425 self.ntlmAuthenticate = NTLMAuthenticate.construct(self.flags, domainname= self.settings.credential.domain, workstationname= self.settings.credential.workstation, username= self.settings.credential.username, lm_response= self.ntlm_credentials.LMResponse, nt_response= self.ntlm_credentials.NTResponse, version = self.ntlmNegotiate.Version, encrypted_session = self.EncryptedRandomSessionKey, mic = mic)
426
427
428 self.ntlmAuthenticate_raw = self.ntlmAuthenticate.to_bytes()
429 return self.ntlmAuthenticate_raw, False, None
430
431 elif self.mode.upper() == 'RELAY':
432 if self.iteration_cnt == 0:
433 self.ntlmNegotiate_raw = authData
434 self.ntlmNegotiate = NTLMNegotiate.from_bytes(authData)
435 self.iteration_cnt += 1
436
437 elif self.iteration_cnt == 1:
438 self.ntlmChallenge_raw = authData
439 self.ntlmChallenge = NTLMChallenge.from_bytes(authData)
440 self.iteration_cnt += 1
441
442 elif self.iteration_cnt == 2:
443 self.ntlmChallenge_raw = authData
444 self.ntlmChallenge = NTLMChallenge.from_bytes(authData)
445 self.iteration_cnt += 1
446
447 else:
448 raise Exception('Too many iterations for relay mode!')
449
+0
-99
msldap/authentication/ntlm/sspi.py less more
0 #
1 #
2 # This is just a simple interface to the winsspi library to support NTLM
3 #
4 from winsspi.sspi import NTLMMSLDAPSSPI
5 from winsspi.common.function_defs import ISC_REQ
6 from msldap.authentication.ntlm.native import NTLMAUTHHandler, NTLMHandlerSettings
7
8 class MSLDAPNTLMSSPI:
9 def __init__(self, settings):
10 self.settings = settings
11 self.mode = 'CLIENT'
12 self.username = settings.username
13 self.password = settings.password
14 self.domain = settings.domain
15 self.actual_ctx_flags = None
16 self.flags = ISC_REQ.CONNECTION
17 if settings.encrypt is True:
18 #self.flags = ISC_REQ.REPLAY_DETECT | ISC_REQ.CONFIDENTIALITY| ISC_REQ.USE_SESSION_KEY| ISC_REQ.INTEGRITY| ISC_REQ.SEQUENCE_DETECT| ISC_REQ.CONNECTION
19 self.flags = ISC_REQ.CONNECTION | ISC_REQ.CONFIDENTIALITY
20 self.sspi = NTLMMSLDAPSSPI()
21
22 self.seq_number = 0
23 self.session_key = None
24 self.ntlm_ctx = NTLMAUTHHandler(NTLMHandlerSettings(None, 'MANUAL'))
25
26 @property
27 def ntlmChallenge(self):
28 return self.ntlm_ctx.ntlmChallenge
29
30 def get_seq_number(self):
31 return self.ntlm_ctx.get_seq_number()
32
33 def signing_needed(self):
34 return self.ntlm_ctx.signing_needed()
35
36 def encryption_needed(self):
37 return self.ntlm_ctx.encryption_needed()
38
39 def get_sealkey(self, mode = 'Client'):
40 return self.ntlm_ctx.get_sealkey(mode = mode)
41
42 def get_signkey(self, mode = 'Client'):
43 return self.ntlm_ctx.get_signkey(mode = mode)
44
45 #def wrap(self, data, sequence_no):
46 # self.ntlm_ctx.wrap()
47
48 def unwrap(self, data):
49 return self.ntlm_ctx.unwrap(data)
50
51 def SEAL(self, signingKey, sealingKey, messageToSign, messageToEncrypt, seqNum, cipher_encrypt):
52 return self.ntlm_ctx.SEAL(signingKey, sealingKey, messageToSign, messageToEncrypt, seqNum, cipher_encrypt)
53
54 def SIGN(self, signingKey, message, seqNum, cipher_encrypt):
55 return self.ntlm_ctx.SIGN(signingKey, message, seqNum, cipher_encrypt)
56
57 def sign(self, data, message_no = 0, direction = 'init', reset_cipher = False):
58 return self.ntlm_ctx.sign(data, message_no = message_no, reset_cipher = reset_cipher)
59
60 def verify(self, data, signature):
61 return self.ntlm_ctx.verify(data, signature)
62
63 def get_session_key(self):
64 if not self.session_key:
65 self.session_key = self.sspi.get_session_key()
66
67 return self.session_key
68
69 #def get_extra_info(self):
70 # return self.ntlm_ctx.get_extra_info()
71
72 def is_extended_security(self):
73 return self.ntlm_ctx.is_extended_security()
74
75 def encrypt(self, data, message_no):
76 return self.ntlm_ctx.encrypt(data, message_no)
77
78 def decrypt(self, data, message_no, direction='init', auth_data=None):
79 return self.ntlm_ctx.decrypt(data, message_no, direction=direction, auth_data=auth_data)
80
81 async def authenticate(self, authData = None, flags = None, seq_number = 0, cb_data = None):
82 if authData is None:
83 try:
84 data, res = self.sspi.negotiate(ctx_flags = self.flags)
85 self.actual_ctx_flags = self.sspi.ctx_outflags
86 self.ntlm_ctx.load_negotiate(data)
87 return data, res, None
88 except Exception as e:
89 return None, None, e
90 else:
91 self.ntlm_ctx.load_challenge(authData)
92 data, res = self.sspi.authenticate(authData, ctx_flags = self.flags)
93 self.ntlm_ctx.load_authenticate( data)
94 self.ntlm_ctx.load_sessionkey(self.get_session_key())
95
96 return data, res, None
97
98
+0
-117
msldap/authentication/ntlm/sspiproxy.py less more
0 from msldap import logger
1 from msldap.authentication.ntlm.native import NTLMAUTHHandler, NTLMHandlerSettings
2 from pyodidewsnet.sspiproxyws import SSPIProxyWS
3 import enum
4
5 class ISC_REQ(enum.IntFlag):
6 DELEGATE = 1
7 MUTUAL_AUTH = 2
8 REPLAY_DETECT = 4
9 SEQUENCE_DETECT = 8
10 CONFIDENTIALITY = 16
11 USE_SESSION_KEY = 32
12 PROMPT_FOR_CREDS = 64
13 USE_SUPPLIED_CREDS = 128
14 ALLOCATE_MEMORY = 256
15 USE_DCE_STYLE = 512
16 DATAGRAM = 1024
17 CONNECTION = 2048
18 CALL_LEVEL = 4096
19 FRAGMENT_SUPPLIED = 8192
20 EXTENDED_ERROR = 16384
21 STREAM = 32768
22 INTEGRITY = 65536
23 IDENTIFY = 131072
24 NULL_SESSION = 262144
25 MANUAL_CRED_VALIDATION = 524288
26 RESERVED1 = 1048576
27 FRAGMENT_TO_FIT = 2097152
28 HTTP = 0x10000000
29
30 class MSLDAPSSPIProxyNTLMAuth:
31 def __init__(self, settings):
32 self.settings = settings
33 self.mode = None #'CLIENT'
34 url = '%s://%s:%s' % (self.settings.proto, self.settings.host, self.settings.port)
35 self.sspi = SSPIProxyWS(url, self.settings.agent_id)
36 self.operator = None
37 self.client = None
38 self.target = None
39 self.seq_number = 0
40
41 self.session_key = None
42 self.ntlm_ctx = NTLMAUTHHandler(NTLMHandlerSettings(None, 'MANUAL'))
43
44 def setup(self):
45 return
46
47 @property
48 def ntlmChallenge(self):
49 return self.ntlm_ctx.ntlmChallenge
50
51 def get_sealkey(self, mode = 'Client'):
52 return self.ntlm_ctx.get_sealkey(mode = mode)
53
54 def get_signkey(self, mode = 'Client'):
55 return self.ntlm_ctx.get_signkey(mode = mode)
56
57 def signing_needed(self):
58 return self.ntlm_ctx.signing_needed()
59
60 def encryption_needed(self):
61 return self.ntlm_ctx.encryption_needed()
62
63 async def encrypt(self, data, message_no):
64 return await self.ntlm_ctx.encrypt(data, message_no)
65
66 async def decrypt(self, data, sequence_no, direction='init', auth_data=None):
67 return await self.ntlm_ctx.decrypt(data, sequence_no, direction=direction, auth_data=auth_data)
68
69 async def sign(self, data, message_no, direction=None, reset_cipher = False):
70 return await self.ntlm_ctx.sign(data, message_no, direction=None, reset_cipher = reset_cipher)
71
72
73 def SEAL(self, signingKey, sealingKey, messageToSign, messageToEncrypt, seqNum, cipher_encrypt):
74 return self.ntlm_ctx.SEAL(signingKey, sealingKey, messageToSign, messageToEncrypt, seqNum, cipher_encrypt)
75
76 def SIGN(self, signingKey, message, seqNum, cipher_encrypt):
77 return self.ntlm_ctx.SIGN(signingKey, message, seqNum, cipher_encrypt)
78
79 def get_session_key(self):
80 return self.session_key
81
82 def get_seq_number(self):
83 return self.seq_number
84
85 def is_extended_security(self):
86 return self.ntlm_ctx.is_extended_security()
87
88 async def authenticate(self, authData = b'', flags = None, seq_number = 0, cb_data = None):
89 try:
90 if flags is None:
91 flags = ISC_REQ.CONNECTION
92
93 if authData is None:
94 status, ctxattr, data, err = await self.sspi.authenticate('NTLM', '', '', 3, flags.value, authdata = b'')
95 if err is not None:
96 raise err
97 self.ntlm_ctx.load_negotiate(data)
98 return data, True, None
99 else:
100 self.ntlm_ctx.load_challenge(authData)
101 status, ctxattr, data, err = await self.sspi.authenticate('NTLM', '', '', 3, flags.value, authdata = authData)
102 if err is not None:
103 raise err
104 if err is None:
105 self.ntlm_ctx.load_authenticate(data)
106 self.session_key, err = await self.sspi.get_sessionkey()
107 if err is not None:
108 raise err
109 self.ntlm_ctx.load_sessionkey(self.get_session_key())
110
111 await self.sspi.disconnect()
112 return data, False, None
113 except Exception as e:
114 return None, None, e
115
116
+0
-0
msldap/authentication/ntlm/structures/__init__.py less more
(Empty file)
+0
-96
msldap/authentication/ntlm/structures/avpair.py less more
0 import enum
1 import io
2 import collections
3
4 class MsvAvFlags(enum.IntFlag):
5 CONSTRAINED_AUTH = 0x00000001
6 MIC_PRESENT = 0x00000002
7 SPN_UNTRUSTED = 0x00000004
8
9 class AVPAIRType(enum.Enum):
10 MsvAvEOL = 0x0000 #Indicates that this is the last AV_PAIR in the list. AvLen MUST be 0. This type of information MUST be present in the AV pair list.
11 MsvAvNbComputerName = 0x0001 #The server's NetBIOS computer name. The name MUST be in Unicode, and is not null-terminated. This type of information MUST be present in the AV_pair list.
12 MsvAvNbDomainName = 0x0002 #The server's NetBIOS domain name. The name MUST be in Unicode, and is not null-terminated. This type of information MUST be present in the AV_pair list.
13 MsvAvDnsComputerName = 0x0003 #The fully qualified domain name (FQDN) of the computer. The name MUST be in Unicode, and is not null-terminated.
14 MsvAvDnsDomainName = 0x0004 #The FQDN of the domain. The name MUST be in Unicode, and is not null-terminated.
15 MsvAvDnsTreeName = 0x0005 #The FQDN of the forest. The name MUST be in Unicode, and is not null-terminated.<13>
16 MsvAvFlags = 0x0006 #A 32-bit value indicating server or client configuration.
17 MsvAvTimestamp = 0x0007 #A FILETIME structure ([MS-DTYP] section 2.3.3) in little-endian byte order that contains the server local time. This structure is always sent in the CHALLENGE_MESSAGE.<16>
18 MsvAvSingleHost = 0x0008 #A Single_Host_Data (section 2.2.2.2) structure. The Value field contains a platform-specific blob, as well as a MachineID created at computer startup to identify the calling machine.<17>
19 MsvAvTargetName = 0x0009 #The SPN of the target server. The name MUST be in Unicode and is not null-terminated.<18>
20 MsvChannelBindings = 0x000A #A channel bindings hash. The Value field contains an MD5 hash ([RFC4121] section 4.1.1.2) of a gss_channel_bindings_struct ([RFC2744] section 3.11). An all-zero value of the hash is used to indicate absence of channel bindings.<19>
21
22
23 # ???? https://msdn.microsoft.com/en-us/library/windows/desktop/aa374793(v=vs.85).aspx
24 # https://msdn.microsoft.com/en-us/library/cc236646.aspx
25 class AVPairs(collections.UserDict):
26 """
27 AVPairs is a dictionary-like object that stores the "AVPair list" in a key -value format where key is an AVPAIRType object and value is the corresponding object defined by the MSDN documentation. Usually it's string but can be other object as well
28 """
29 def __init__(self, data = None):
30 collections.UserDict.__init__(self, data)
31
32 @staticmethod
33 def from_bytes(bbuff):
34 return AVPairs.from_buffer(io.BytesIO(bbuff))
35
36 @staticmethod
37 def from_buffer(buff):
38 avp = AVPairs()
39 while True:
40 avId = AVPAIRType(int.from_bytes(buff.read(2), byteorder = 'little', signed = False))
41 AvLen = int.from_bytes(buff.read(2), byteorder = 'little', signed = False)
42 if avId == AVPAIRType.MsvAvEOL:
43 break
44
45 elif avId in [AVPAIRType.MsvAvNbComputerName,
46 AVPAIRType.MsvAvNbDomainName,
47 AVPAIRType.MsvAvDnsComputerName,
48 AVPAIRType.MsvAvDnsDomainName,
49 AVPAIRType.MsvAvDnsTreeName,
50 AVPAIRType.MsvAvTargetName,
51 ]:
52 avp[avId] = buff.read(AvLen).decode('utf-16le')
53
54 elif avId == AVPAIRType.MsvAvFlags:
55 avp[avId] = MsvAvFlags(int.from_bytes(buff.read(4), byteorder = 'little', signed = False))
56
57 # TODO IMPLEMENT PARSING OFR OTHER TYPES!!!!
58 else:
59 avp[avId] = buff.read(AvLen)
60
61 return avp
62
63 def to_bytes(self):
64 t = b''
65 for av in self.data:
66 t += AVPair(data = self.data[av], type = av).to_bytes()
67
68 t += AVPair(data = '', type = AVPAIRType.MsvAvEOL).to_bytes()
69 return t
70
71
72 class AVPair:
73 def __init__(self, data = None, type = None):
74 self.type = type
75 self.data = data
76
77 def to_bytes(self):
78 t = self.type.value.to_bytes(2, byteorder = 'little', signed = False)
79 raw_data = None
80 if self.type in [AVPAIRType.MsvAvNbComputerName,
81 AVPAIRType.MsvAvNbDomainName,
82 AVPAIRType.MsvAvDnsComputerName,
83 AVPAIRType.MsvAvDnsDomainName,
84 AVPAIRType.MsvAvDnsTreeName,
85 AVPAIRType.MsvAvTargetName,
86 AVPAIRType.MsvAvEOL
87 ]:
88 raw_data = self.data.encode('utf-16le')
89 else:
90 raw_data = self.data
91 t += len(raw_data).to_bytes(2, byteorder = 'little', signed = False)
92 t += raw_data
93 return t
94
95
+0
-204
msldap/authentication/ntlm/structures/challenge_response.py less more
0 import io
1 import datetime
2
3 from msldap.commons.utils import *
4 from msldap.authentication.ntlm.structures.avpair import AVPairs, AVPAIRType
5
6 # https://msdn.microsoft.com/en-us/library/cc236648.aspx
7 class LMResponse:
8 def __init__(self):
9 self.Response = None
10
11 def to_bytes(self):
12 return self.Response
13
14 @staticmethod
15 def from_bytes(bbuff):
16 return LMResponse.from_buffer(io.BytesIO(bbuff))
17
18 @staticmethod
19 def from_buffer(buff):
20 t = LMResponse()
21 t.Response = buff.read(24)
22 return t
23
24 def __repr__(self):
25 t = '== LMResponse ==\r\n'
26 t += 'Response: %s\r\n' % repr(self.Response.hex())
27 return t
28
29
30 # https://msdn.microsoft.com/en-us/library/cc236649.aspx
31 class LMv2Response:
32 def __init__(self):
33 self.Response = None
34 self.ChallengeFromClinet = None
35
36
37 def to_bytes(self):
38 return self.Response + self.ChallengeFromClinet
39
40 @staticmethod
41 def from_bytes(bbuff):
42 return LMv2Response.from_buffer(io.BytesIO(bbuff))
43
44 @staticmethod
45 def from_buffer(buff):
46 t = LMv2Response()
47 t.Response = buff.read(16).hex()
48 t.ChallengeFromClinet = buff.read(8).hex()
49 return t
50
51 def __repr__(self):
52 t = '== LMv2Response ==\r\n'
53 t += 'Response: %s\r\n' % repr(self.Response)
54 t += 'ChallengeFromClinet: %s\r\n' % repr(self.ChallengeFromClinet)
55 return t
56
57
58 # https://msdn.microsoft.com/en-us/library/cc236651.aspx
59 class NTLMv1Response:
60 def __init__(self):
61 self.Response = None
62
63 def to_bytes(self):
64 return self.Response
65
66 @staticmethod
67 def from_bytes(bbuff):
68 return NTLMv1Response.from_buffer(io.BytesIO(bbuff))
69
70 @staticmethod
71 def from_buffer(buff):
72 t = NTLMv1Response()
73 t.Response = buff.read(24).hex()
74 return t
75
76 def __repr__(self):
77 t = '== NTLMv1Response ==\r\n'
78 t += 'Response: %s\r\n' % repr(self.Response)
79 return t
80
81
82 # https://msdn.microsoft.com/en-us/library/cc236653.aspx
83 class NTLMv2Response:
84 def __init__(self):
85 self.Response = None
86 self.ChallengeFromClinet = None
87
88 def to_bytes(self):
89 return self.Response + self.ChallengeFromClinet.to_bytes()
90
91 @staticmethod
92 def from_bytes(bbuff):
93 return NTLMv2Response.from_buffer(io.BytesIO(bbuff))
94
95 @staticmethod
96 def from_buffer(buff):
97 t = NTLMv2Response()
98 t.Response = buff.read(16).hex()
99 pos = buff.tell()
100 t.ChallengeFromClinet = NTLMv2ClientChallenge.from_buffer(buff)
101
102 return t
103
104 def __repr__(self):
105 t = '== NTLMv2Response ==\r\n'
106 t += 'Response : %s\r\n' % repr(self.Response)
107 t += 'ChallengeFromClinet: %s\r\n' % repr(self.ChallengeFromClinet)
108 return t
109
110
111 class NTLMv2ClientChallenge:
112 def __init__(self):
113 self.RespType = 1
114 self.HiRespType = 1
115 self.Reserved1 = 0
116 self.TimeStamp = None #bytes! because of conversion error :(
117 self.Reserved2 = 0
118 self.ChallengeFromClient = None
119 self.Reserved3 = 0
120 self.Details = None #named AVPairs in the documentation
121
122 self.timestamp_dt = None
123 self.raw_data = b''
124
125 @staticmethod
126 def construct(timestamp, client_challenge, details):
127 """
128 timestamp: datetime.datetime
129 client_challenge: 8 bytes
130 details: AVPairs object
131 """
132 cc = NTLMv2ClientChallenge()
133 cc.TimeStamp = datetime2timestamp(timestamp)
134 cc.ChallengeFromClient = client_challenge
135 cc.Details = details
136 cc.timestamp_dt = timestamp
137 return cc
138
139 def to_bytes(self):
140 t = self.RespType.to_bytes(1 , byteorder = 'little', signed = False)
141 t += self.HiRespType.to_bytes(1 , byteorder = 'little', signed = False)
142 t += self.Reserved1.to_bytes(6, byteorder = 'little', signed = False)
143 t += self.TimeStamp
144 t += self.ChallengeFromClient
145 t += self.Reserved2.to_bytes(4, byteorder = 'little', signed = False)
146 t += self.Details.to_bytes()
147 t += self.Reserved3.to_bytes(4, byteorder = 'little', signed = False)
148
149 return t
150
151 @staticmethod
152 def from_bytes(bbuff):
153 return NTLMv2ClientChallenge.from_buffer(io.BytesIO(bbuff))
154
155 @staticmethod
156 def from_buffer(buff):
157 cc = NTLMv2ClientChallenge()
158 cc.RespType = int.from_bytes(buff.read(1), byteorder = 'little', signed = False)
159 cc.HiRespType = int.from_bytes(buff.read(1), byteorder = 'little', signed = False)
160 cc.Reserved1 = int.from_bytes(buff.read(6), byteorder = 'little', signed = False)
161 cc.TimeStamp = buff.read(8)
162 cc.ChallengeFromClient = buff.read(8)
163 cc.Reserved2 = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
164 cc.Details = AVPairs.from_buffer(buff) #referred to as ServerName in the documentation
165 cc.Reserved3 = int.from_bytes(buff.read(4), byteorder='little', signed=False)
166
167 cc.timestamp_dt = timestamp2datetime(cc.TimeStamp)
168
169 return cc
170
171 def __repr__(self):
172 t = '== NTLMv2ClientChallenge ==\r\n'
173 t += 'RespType : %s\r\n' % repr(self.RespType)
174 t += 'TimeStamp : %s\r\n' % repr(self.timestamp_dt)
175 t += 'ChallengeFromClient: %s\r\n' % repr(self.ChallengeFromClient.hex())
176 t += 'Details : %s\r\n' % repr(self.Details)
177 return t
178
179 def test():
180 test_data = bytes.fromhex('0101000000000000aec600bfc5fdd4011bfa20699d7628730000000002000800540045005300540001001200570049004e003200300031003900410044000400120074006500730074002e0063006f007200700003002600570049004e003200300031003900410044002e0074006500730074002e0063006f007200700007000800aec600bfc5fdd40106000400020000000800300030000000000000000000000000200000527d27f234de743760966384d36f61ae2aa4fc2a380699f8caa600011b486d890a0010000000000000000000000000000000000009001e0063006900660073002f00310030002e00310030002e00310030002e0032000000000000000000')
181
182 cc = NTLMv2ClientChallenge.from_bytes(test_data)
183 print(repr(cc))
184
185 cc2 = NTLMv2ClientChallenge.from_bytes(cc.to_bytes())
186 print(repr(cc2))
187 print('=== Original ===')
188 print(hexdump(test_data))
189 print('=== CC ===')
190 print(hexdump(cc.to_bytes()))
191
192 ### assertions here fail because of the timestamp re-conversion loosing info (float-int conversion)
193 #assert cc.to_bytes() == test_data
194 #assert cc2.to_bytes() == test_data
195
196 details = AVPairs({AVPAIRType.MsvAvNbDomainName: 'TEST', AVPAIRType.MsvAvNbComputerName: 'WIN2019AD', AVPAIRType.MsvAvDnsDomainName: 'test.corp', AVPAIRType.MsvAvDnsComputerName: 'WIN2019AD.test.corp', AVPAIRType.MsvAvTimestamp: b'\xae\xc6\x00\xbf\xc5\xfd\xd4\x01', AVPAIRType.MsvAvFlags: b'\x02\x00\x00\x00', AVPAIRType.MsvAvSingleHost: b"0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00R}'\xf24\xdet7`\x96c\x84\xd3oa\xae*\xa4\xfc*8\x06\x99\xf8\xca\xa6\x00\x01\x1bHm\x89", AVPAIRType.MsvChannelBindings: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', AVPAIRType.MsvAvTargetName: 'cifs/10.10.10.2'})
197 timestamp = datetime.datetime(2019,1,1)
198 client_challenge = os.urandom(8)
199
200 cc3 = NTLMv2ClientChallenge.construct(timestamp, client_challenge, details)
201 print(repr(cc3))
202 cc4 = NTLMv2ClientChallenge.from_bytes(cc3.to_bytes())
203
+0
-23
msldap/authentication/ntlm/structures/fields.py less more
0
1 class Fields:
2 def __init__(self, length, offset, maxLength = None):
3 self.length = length
4 self.maxLength = length if maxLength is None else maxLength
5 self.offset = offset
6
7 @staticmethod
8 def from_bytes(bbuff):
9 return Fields.from_buffer(io.BytesIO(bbuff))
10
11 @staticmethod
12 def from_buffer( buff):
13 length = int.from_bytes(buff.read(2), byteorder = 'little', signed = False)
14 maxLength = int.from_bytes(buff.read(2), byteorder = 'little', signed = False)
15 offset = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
16
17 return Fields(length, offset, maxLength = maxLength)
18
19 def to_bytes(self):
20 return self.length.to_bytes(2, byteorder = 'little', signed = False) + \
21 self.maxLength.to_bytes(2, byteorder = 'little', signed = False) + \
22 self.offset.to_bytes(4, byteorder = 'little', signed = False)
+0
-72
msldap/authentication/ntlm/structures/negotiate_flags.py less more
0 import enum
1
2 # https://msdn.microsoft.com/en-us/library/cc236650.aspx
3 class NegotiateFlags(enum.IntFlag):
4 NEGOTIATE_56 = 0x80000000
5 NEGOTIATE_KEY_EXCH = 0x40000000
6 NEGOTIATE_128 = 0x20000000
7 r1 = 0x10000000
8 r2 = 0x8000000
9 r3 = 0x4000000
10 NEGOTIATE_VERSION = 0x2000000
11 r4 = 0x1000000
12 NEGOTIATE_TARGET_INFO = 0x800000
13 REQUEST_NON_NT_SESSION_KEY = 0x400000
14 r5 = 0x200000
15 NEGOTIATE_IDENTIFY = 0x100000
16 NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x80000
17 r6 = 0x40000
18 TARGET_TYPE_SERVER = 0x20000
19 TARGET_TYPE_DOMAIN = 0x10000
20 NEGOTIATE_ALWAYS_SIGN = 0x8000
21 r7 = 0x4000
22 NEGOTIATE_OEM_WORKSTATION_SUPPLIED = 0x2000
23 NEGOTIATE_OEM_DOMAIN_SUPPLIED = 0x1000
24 J = 0x800
25 r8 = 0x400
26 NEGOTIATE_NTLM = 0x200
27 r9 = 0x100
28 NEGOTIATE_LM_KEY = 0x80
29 NEGOTIATE_DATAGRAM = 0x40
30 NEGOTIATE_SEAL = 0x20
31 NEGOTIATE_SIGN = 0x10
32 r10 = 0x8
33 REQUEST_TARGET = 0x4
34 NTLM_NEGOTIATE_OEM = 0x2
35 NEGOTIATE_UNICODE = 0x1
36
37 NegotiateFlagExp = {
38 NegotiateFlags.NEGOTIATE_56 : 'requests 56-bit encryption. If the client sends NTLMSSP_NEGOTIATE_SEAL or NTLMSSP_NEGOTIATE_SIGN with NTLMSSP_NEGOTIATE_56 to the server in the NEGOTIATE_MESSAGE, the server MUST return NTLMSSP_NEGOTIATE_56 to the client in the CHALLENGE_MESSAGE. Otherwise it is ignored. If both NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128 are requested and supported by the client and server, NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128 will both be returned to the client. Clients and servers that set NTLMSSP_NEGOTIATE_SEAL SHOULD set NTLMSSP_NEGOTIATE_56 if it is supported. An alternate name for this field is NTLMSSP_NEGOTIATE_56.',
39 NegotiateFlags.NEGOTIATE_KEY_EXCH : 'requests an explicit key exchange. This capability SHOULD be used because it improves security for message integrity or confidentiality. See sections 3.2.5.1.2, 3.2.5.2.1, and 3.2.5.2.2 for details. An alternate name for this field is NTLMSSP_NEGOTIATE_KEY_EXCH.',
40 NegotiateFlags.NEGOTIATE_128 : 'requests 128-bit session key negotiation. An alternate name for this field is NTLMSSP_NEGOTIATE_128. If the client sends NTLMSSP_NEGOTIATE_128 to the server in the NEGOTIATE_MESSAGE, the server MUST return NTLMSSP_NEGOTIATE_128 to the client in the CHALLENGE_MESSAGE only if the client sets NTLMSSP_NEGOTIATE_SEAL or NTLMSSP_NEGOTIATE_SIGN. Otherwise it is ignored. If both NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128 are requested and supported by the client and server, NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128 will both be returned to the client. Clients and servers that set NTLMSSP_NEGOTIATE_SEAL SHOULD set NTLMSSP_NEGOTIATE_128 if it is supported. An alternate name for this field is NTLMSSP_NEGOTIATE_128.<23>',
41 NegotiateFlags.r1 : 'This bit is unused and MUST be zero.',
42 NegotiateFlags.r2 : 'This bit is unused and MUST be zero.',
43 NegotiateFlags.r3 : 'This bit is unused and MUST be zero.',
44 NegotiateFlags.NEGOTIATE_VERSION : 'requests the protocol version number. The data corresponding to this flag is provided in the Version field of the NEGOTIATE_MESSAGE, the CHALLENGE_MESSAGE, and the AUTHENTICATE_MESSAGE.<24> An alternate name for this field is NTLMSSP_NEGOTIATE_VERSION.',
45 NegotiateFlags.r4 : 'This bit is unused and MUST be zero.',
46 NegotiateFlags.NEGOTIATE_TARGET_INFO : 'indicates that the TargetInfo fields in the CHALLENGE_MESSAGE (section 2.2.1.2) are populated. An alternate name for this field is NTLMSSP_NEGOTIATE_TARGET_INFO.',
47 NegotiateFlags.REQUEST_NON_NT_SESSION_KEY : ' requests the usage of the LMOWF. An alternate name for this field is NTLMSSP_REQUEST_NON_NT_SESSION_KEY.',
48 NegotiateFlags.r5 : 'This bit is unused and MUST be zero.',
49 NegotiateFlags.NEGOTIATE_IDENTIFY : 'requests an identify level token. An alternate name for this field is NTLMSSP_NEGOTIATE_IDENTIFY.',
50 NegotiateFlags.NEGOTIATE_EXTENDED_SESSIONSECURITY : 'requests usage of the NTLM v2 session security. NTLM v2 session security is a misnomer because it is not NTLM v2. It is NTLM v1 using the extended session security that is also in NTLM v2. NTLMSSP_NEGOTIATE_LM_KEY and NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY are mutually exclusive. If both NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY and NTLMSSP_NEGOTIATE_LM_KEY are requested, NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY alone MUST be returned to the client. NTLM v2 authentication session key generation MUST be supported by both the client and the DC in order to be used, and extended session security signing and sealing requires support from the client and the server in order to be used.<25> An alternate name for this field is NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.',
51 NegotiateFlags.r6 : 'This bit is unused and MUST be zero.',
52 NegotiateFlags.TARGET_TYPE_SERVER : 'TargetName MUST be a server name. The data corresponding to this flag is provided by the server in the TargetName field of the CHALLENGE_MESSAGE. If this bit is set, then NTLMSSP_TARGET_TYPE_DOMAIN MUST NOT be set. This flag MUST be ignored in the NEGOTIATE_MESSAGE and the AUTHENTICATE_MESSAGE. An alternate name for this field is NTLMSSP_TARGET_TYPE_SERVER.',
53 NegotiateFlags.TARGET_TYPE_DOMAIN : 'TargetName MUST be a domain name. The data corresponding to this flag is provided by the server in the TargetName field of the CHALLENGE_MESSAGE. then NTLMSSP_TARGET_TYPE_SERVER MUST NOT be set. This flag MUST be ignored in the NEGOTIATE_MESSAGE and the AUTHENTICATE_MESSAGE. An alternate name for this field is NTLMSSP_TARGET_TYPE_DOMAIN.',
54 NegotiateFlags.NEGOTIATE_ALWAYS_SIGN : ' requests the presence of a signature block on all messages. NTLMSSP_NEGOTIATE_ALWAYS_SIGN MUST be set in the NEGOTIATE_MESSAGE to the server and the CHALLENGE_MESSAGE to the client. NTLMSSP_NEGOTIATE_ALWAYS_SIGN is overridden by NTLMSSP_NEGOTIATE_SIGN and NTLMSSP_NEGOTIATE_SEAL, if they are supported. An alternate name for this field is NTLMSSP_NEGOTIATE_ALWAYS_SIGN.',
55 NegotiateFlags.r7 : 'This bit is unused and MUST be zero.',
56 NegotiateFlags.NEGOTIATE_OEM_WORKSTATION_SUPPLIED : 'This flag indicates whether the Workstation field is present. If this flag is not set, the Workstation field MUST be ignored. If this flag is set, the length of the Workstation field specifies whether the workstation name is nonempty or not.<26> An alternate name for this field is NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED.',
57 NegotiateFlags.NEGOTIATE_OEM_DOMAIN_SUPPLIED : 'the domain name is provided (section 2.2.1.1).<27> An alternate name for this field is NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED.',
58 NegotiateFlags.J : 'the connection SHOULD be anonymous.<28>',
59 NegotiateFlags.r8 : 'This bit is unused and MUST be zero.',
60 NegotiateFlags.NEGOTIATE_NTLM : 'requests usage of the NTLM v1 session security protocol. NTLMSSP_NEGOTIATE_NTLM MUST be set in the NEGOTIATE_MESSAGE to the server and the CHALLENGE_MESSAGE to the client. An alternate name for this field is NTLMSSP_NEGOTIATE_NTLM.',
61 NegotiateFlags.r9 : 'This bit is unused and MUST be zero.',
62 NegotiateFlags.NEGOTIATE_LM_KEY : 'requests LAN Manager (LM) session key computation. NTLMSSP_NEGOTIATE_LM_KEY and NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY are mutually exclusive. If both NTLMSSP_NEGOTIATE_LM_KEY and NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY are requested, NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY alone MUST be returned to the client. NTLM v2 authentication session key generation MUST be supported by both the client and the DC in order to be used, and extended session security signing and sealing requires support from the client and the server to be used. An alternate name for this field is NTLMSSP_NEGOTIATE_LM_KEY.',
63 NegotiateFlags.NEGOTIATE_DATAGRAM : 'requests connectionless authentication. If NTLMSSP_NEGOTIATE_DATAGRAM is set, then NTLMSSP_NEGOTIATE_KEY_EXCH MUST always be set in the AUTHENTICATE_MESSAGE to the server and the CHALLENGE_MESSAGE to the client. An alternate name for this field is NTLMSSP_NEGOTIATE_DATAGRAM',
64 NegotiateFlags.NEGOTIATE_SEAL : 'requests session key negotiation for message confidentiality. If the client sends NTLMSSP_NEGOTIATE_SEAL to the server in the NEGOTIATE_MESSAGE, the server MUST return NTLMSSP_NEGOTIATE_SEAL to the client in the CHALLENGE_MESSAGE. Clients and servers that set NTLMSSP_NEGOTIATE_SEAL SHOULD always set NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128, if they are supported. An alternate name for this field is NTLMSSP_NEGOTIATE_SEAL.',
65 NegotiateFlags.NEGOTIATE_SIGN : 'requests session key negotiation for message signatures. If the client sends NTLMSSP_NEGOTIATE_SIGN to the server in the NEGOTIATE_MESSAGE, the server MUST return NTLMSSP_NEGOTIATE_SIGN to the client in the CHALLENGE_MESSAGE. An alternate name for this field is NTLMSSP_NEGOTIATE_SIGN.',
66 NegotiateFlags.r10 : 'This bit is unused and MUST be zero.',
67 NegotiateFlags.REQUEST_TARGET : 'TargetName field of the CHALLENGE_MESSAGE (section 2.2.1.2) MUST be supplied. An alternate name for this field is NTLMSSP_REQUEST_TARGET.',
68 NegotiateFlags.NTLM_NEGOTIATE_OEM : 'requests OEM character set encoding. An alternate name for this field is NTLM_NEGOTIATE_OEM. See bit A for details.',
69 NegotiateFlags.NEGOTIATE_UNICODE : 'requests Unicode character set encoding. An alternate name for this field is NTLMSSP_NEGOTIATE_UNICODE.',
70
71 }
+0
-33
msldap/authentication/ntlm/structures/ntlmssp_message_signature.py less more
0 import io
1
2 class NTLMSSP_MESSAGE_SIGNATURE:
3 def __init__(self):
4 self.Version = 1
5 self.Checksum = None
6 self.SeqNum = None
7
8 def to_bytes(self):
9 t = self.Version.to_bytes(4, byteorder = 'little', signed = False)
10 t += self.Checksum
11 t += self.SeqNum.to_bytes(4, byteorder = 'little', signed = False)
12 return t
13
14 @staticmethod
15 def from_bytes(bbuff):
16 return NTLMSSP_MESSAGE_SIGNATURE.from_buffer(io.BytesIO(bbuff))
17
18 @staticmethod
19 def from_buffer(buff):
20 v = NTLMSSP_MESSAGE_SIGNATURE()
21 v.Version = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
22 v.Checksum = buff.read(8)
23 v.SeqNum = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
24
25 return v
26
27 def __repr__(self):
28 t = '== NTLMSSP_MESSAGE_SIGNATURE ==\r\n'
29 t += 'Version : %s\r\n' % self.Version
30 t += 'Checksum : %s\r\n' % self.Checksum
31 t += 'SeqNum : %s\r\n' % self.SeqNum
32 return t
+0
-37
msldap/authentication/ntlm/structures/ntlmssp_message_signature_noext.py less more
0 import io
1
2 class NTLMSSP_MESSAGE_SIGNATURE_NOEXT:
3 def __init__(self):
4 self.Version = 1
5 self.RandomPad = None
6 self.Checksum = None
7 self.SeqNum = None
8
9 def to_bytes(self):
10 t = self.Version.to_bytes(4, byteorder = 'little', signed = False)
11 t += self.RandomPad
12 t += self.Checksum
13 t += self.SeqNum.to_bytes(4, byteorder = 'little', signed = False)
14 return t
15
16 @staticmethod
17 def from_bytes(bbuff):
18 return NTLMSSP_MESSAGE_SIGNATURE_NOEXT.from_buffer(io.BytesIO(bbuff))
19
20 @staticmethod
21 def from_buffer(buff):
22 v = NTLMSSP_MESSAGE_SIGNATURE_NOEXT()
23 v.Version = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
24 v.RandomPad = buff.read(4)
25 v.Checksum = buff.read(4)
26 v.SeqNum = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
27
28 return v
29
30 def __repr__(self):
31 t = '== NTLMSSP_MESSAGE_SIGNATURE_NOEXT ==\r\n'
32 t += 'Version : %s\r\n' % self.Version
33 t += 'RandomPad : %s\r\n' % self.RandomPad
34 t += 'Checksum : %s\r\n' % self.Checksum
35 t += 'SeqNum : %s\r\n' % self.SeqNum
36 return t
+0
-89
msldap/authentication/ntlm/structures/version.py less more
0 import enum
1 import io
2
3 class NTLMRevisionCurrent(enum.Enum):
4 NTLMSSP_REVISION_W2K3 = 0x0F
5
6
7 # https://msdn.microsoft.com/en-us/library/cc236722.aspx#Appendix_A_33
8 class WindowsMajorVersion(enum.Enum):
9 WINDOWS_MAJOR_VERSION_5 = 0x05
10 WINDOWS_MAJOR_VERSION_6 = 0x06
11 WINDOWS_MAJOR_VERSION_10 = 0x0A
12
13
14 # https://msdn.microsoft.com/en-us/library/cc236722.aspx#Appendix_A_33
15 class WindowsMinorVersion(enum.Enum):
16 WINDOWS_MINOR_VERSION_0 = 0x00
17 WINDOWS_MINOR_VERSION_1 = 0x01
18 WINDOWS_MINOR_VERSION_2 = 0x02
19 WINDOWS_MINOR_VERSION_3 = 0x03
20
21 # https://msdn.microsoft.com/en-us/library/cc236722.aspx#Appendix_A_33
22 WindowsProduct = {
23 (WindowsMajorVersion.WINDOWS_MAJOR_VERSION_5, WindowsMinorVersion.WINDOWS_MINOR_VERSION_1) : 'Windows XP operating system Service Pack 2 (SP2)',
24 (WindowsMajorVersion.WINDOWS_MAJOR_VERSION_5, WindowsMinorVersion.WINDOWS_MINOR_VERSION_2) : 'Windows Server 2003',
25 (WindowsMajorVersion.WINDOWS_MAJOR_VERSION_6, WindowsMinorVersion.WINDOWS_MINOR_VERSION_0) : 'Windows Vista or Windows Server 2008',
26 (WindowsMajorVersion.WINDOWS_MAJOR_VERSION_6, WindowsMinorVersion.WINDOWS_MINOR_VERSION_1) : 'Windows 7 or Windows Server 2008 R2',
27 (WindowsMajorVersion.WINDOWS_MAJOR_VERSION_6, WindowsMinorVersion.WINDOWS_MINOR_VERSION_2) : 'Windows 8 or Windows Server 2012 operating system',
28 (WindowsMajorVersion.WINDOWS_MAJOR_VERSION_6, WindowsMinorVersion.WINDOWS_MINOR_VERSION_3) : 'Windows 8.1 or Windows Server 2012 R2',
29 (WindowsMajorVersion.WINDOWS_MAJOR_VERSION_10,WindowsMinorVersion.WINDOWS_MINOR_VERSION_0) : 'Windows 10 or Windows Server 2016',
30 }
31
32 # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/b1a6ceb2-f8ad-462b-b5af-f18527c48175
33 class Version:
34 def __init__(self):
35 self.ProductMajorVersion = None
36 self.ProductMinorVersion = None
37 self.ProductBuild = None
38 self.Reserved = 0
39 self.NTLMRevisionCurrent = None
40
41 # higher level
42 self.WindowsProduct = None
43
44 @staticmethod
45 def construct(major = WindowsMajorVersion.WINDOWS_MAJOR_VERSION_10, minor = WindowsMinorVersion.WINDOWS_MINOR_VERSION_0, build = 1555 ):
46 v = Version()
47 v.ProductMajorVersion = major
48 v.ProductMinorVersion = minor
49 v.ProductBuild = build
50 v.NTLMRevisionCurrent = NTLMRevisionCurrent.NTLMSSP_REVISION_W2K3
51
52 return v
53
54 def to_bytes(self):
55 t = self.ProductMajorVersion.value.to_bytes(1, byteorder = 'little', signed = False)
56 t += self.ProductMinorVersion.value.to_bytes(1, byteorder = 'little', signed = False)
57 t += self.ProductBuild.to_bytes(2, byteorder = 'little', signed = False)
58 t += self.Reserved.to_bytes(3, byteorder = 'little', signed = False)
59 t += self.NTLMRevisionCurrent.value.to_bytes(1, byteorder = 'little', signed = False)
60 return t
61
62 @staticmethod
63 def from_bytes(bbuff):
64 return Version.from_buffer(io.BytesIO(bbuff))
65
66 @staticmethod
67 def from_buffer(buff):
68 v = Version()
69 v.ProductMajorVersion = WindowsMajorVersion(int.from_bytes(buff.read(1), byteorder = 'little', signed = False))
70 v.ProductMinorVersion = WindowsMinorVersion(int.from_bytes(buff.read(1), byteorder = 'little', signed = False))
71 v.ProductBuild = int.from_bytes(buff.read(2), byteorder = 'little', signed = False)
72 v.Reserved = int.from_bytes(buff.read(3), byteorder = 'little', signed = False)
73 v.NTLMRevisionCurrent = NTLMRevisionCurrent(int.from_bytes(buff.read(1), byteorder = 'little', signed = False))
74
75 try:
76 v.WindowsProduct = WindowsProduct[(v.ProductMajorVersion, v.ProductMinorVersion)]
77 except:
78 pass
79
80 return v
81
82 def __repr__(self):
83 t = '== NTLMVersion ==\r\n'
84 t += 'ProductMajorVersion : %s\r\n' % repr(self.ProductMajorVersion.name)
85 t += 'ProductMinorVersion : %s\r\n' % repr(self.ProductMinorVersion.name)
86 t += 'ProductBuild : %s\r\n' % repr(self.ProductBuild)
87 t += 'WindowsProduct : %s\r\n' % repr(self.WindowsProduct)
88 return t
+0
-0
msldap/authentication/ntlm/templates/__init__.py less more
(Empty file)
+0
-89
msldap/authentication/ntlm/templates/client.py less more
0 from msldap.authentication.ntlm.structures.fields import Fields
1 from msldap.authentication.ntlm.structures.negotiate_flags import NegotiateFlags
2 from msldap.authentication.ntlm.structures.version import Version, WindowsMajorVersion, WindowsMinorVersion
3
4 # LDAP doesnt seem to support sign-only. either no seal nor sign nor always_sign OR include seal.
5 NTLMClientTemplates = {
6 "Windows10_15063" : {
7 'flags' : NegotiateFlags.NEGOTIATE_KEY_EXCH|
8 NegotiateFlags.NEGOTIATE_128|
9 NegotiateFlags.NEGOTIATE_VERSION|
10 NegotiateFlags.NEGOTIATE_EXTENDED_SESSIONSECURITY|
11 NegotiateFlags.NEGOTIATE_NTLM|
12 NegotiateFlags.REQUEST_TARGET|
13 NegotiateFlags.NEGOTIATE_UNICODE,
14 'version' : Version.construct(WindowsMajorVersion.WINDOWS_MAJOR_VERSION_10, minor = WindowsMinorVersion.WINDOWS_MINOR_VERSION_0, build = 15063 ),
15 'domain_name' : None,
16 'workstation_name' : None,
17 'ntlm_downgrade' : False,
18 },
19 "Windows10_15063_channel" : {
20 'flags' : NegotiateFlags.NEGOTIATE_KEY_EXCH|
21 NegotiateFlags.NEGOTIATE_128|
22 NegotiateFlags.NEGOTIATE_VERSION|
23 NegotiateFlags.NEGOTIATE_EXTENDED_SESSIONSECURITY|
24 NegotiateFlags.NEGOTIATE_ALWAYS_SIGN|
25 NegotiateFlags.NEGOTIATE_NTLM|
26 NegotiateFlags.NEGOTIATE_SIGN|
27 NegotiateFlags.NEGOTIATE_SEAL|
28 NegotiateFlags.REQUEST_TARGET|
29 NegotiateFlags.NEGOTIATE_UNICODE,
30 'version' : Version.construct(WindowsMajorVersion.WINDOWS_MAJOR_VERSION_10, minor = WindowsMinorVersion.WINDOWS_MINOR_VERSION_0, build = 15063 ),
31 'domain_name' : None,
32 'workstation_name' : None,
33 'ntlm_downgrade' : False,
34 },
35 "Windows10_15063_old" : {
36 'flags' : NegotiateFlags.NEGOTIATE_56|
37 NegotiateFlags.NEGOTIATE_KEY_EXCH|
38 NegotiateFlags.NEGOTIATE_128|
39 NegotiateFlags.NEGOTIATE_VERSION|
40 NegotiateFlags.NEGOTIATE_EXTENDED_SESSIONSECURITY|
41 NegotiateFlags.NEGOTIATE_ALWAYS_SIGN|
42 NegotiateFlags.NEGOTIATE_NTLM|
43 NegotiateFlags.NEGOTIATE_LM_KEY|
44 NegotiateFlags.NEGOTIATE_SIGN|
45 NegotiateFlags.REQUEST_TARGET|
46 NegotiateFlags.NTLM_NEGOTIATE_OEM|
47 NegotiateFlags.NEGOTIATE_UNICODE,
48 'version' : Version.construct(WindowsMajorVersion.WINDOWS_MAJOR_VERSION_10, minor = WindowsMinorVersion.WINDOWS_MINOR_VERSION_0, build = 15063 ),
49 'domain_name' : None,
50 'workstation_name' : None,
51 'ntlm_downgrade' : False,
52 },
53 "Windows10_15063_knowkey" : {
54 'flags' : NegotiateFlags.NEGOTIATE_56|
55 NegotiateFlags.NEGOTIATE_KEY_EXCH|
56 NegotiateFlags.NEGOTIATE_128|
57 NegotiateFlags.NEGOTIATE_VERSION|
58 NegotiateFlags.NEGOTIATE_EXTENDED_SESSIONSECURITY|
59 NegotiateFlags.NEGOTIATE_ALWAYS_SIGN|
60 NegotiateFlags.NEGOTIATE_NTLM|
61 NegotiateFlags.NEGOTIATE_LM_KEY|
62 NegotiateFlags.NEGOTIATE_SIGN|
63 NegotiateFlags.REQUEST_TARGET|
64 NegotiateFlags.NTLM_NEGOTIATE_OEM|
65 NegotiateFlags.NEGOTIATE_UNICODE,
66 'version' : Version.construct(WindowsMajorVersion.WINDOWS_MAJOR_VERSION_10, minor = WindowsMinorVersion.WINDOWS_MINOR_VERSION_0, build = 15063 ),
67 'domain_name' : None,
68 'workstation_name' : None,
69 'ntlm_downgrade' : False,
70 'session_key' : b'A'*16,
71 },
72 "Windows10_15063_nosign" : {
73 'flags' : NegotiateFlags.NEGOTIATE_56|
74 NegotiateFlags.NEGOTIATE_KEY_EXCH|
75 NegotiateFlags.NEGOTIATE_128|
76 NegotiateFlags.NEGOTIATE_VERSION|
77 NegotiateFlags.NEGOTIATE_EXTENDED_SESSIONSECURITY|
78 NegotiateFlags.NEGOTIATE_NTLM|
79 NegotiateFlags.NEGOTIATE_LM_KEY|
80 NegotiateFlags.REQUEST_TARGET|
81 NegotiateFlags.NTLM_NEGOTIATE_OEM|
82 NegotiateFlags.NEGOTIATE_UNICODE,
83 'version' : Version.construct(WindowsMajorVersion.WINDOWS_MAJOR_VERSION_10, minor = WindowsMinorVersion.WINDOWS_MINOR_VERSION_0, build = 15063 ),
84 'domain_name' : None,
85 'workstation_name' : None,
86 'ntlm_downgrade' : False,
87 },
88 }
+0
-23
msldap/authentication/ntlm/templates/server.py less more
0 from msldap.authentication.ntlm.structures.fields import Fields
1 from msldap.authentication.ntlm.structures.negotiate_flags import NegotiateFlags
2 from msldap.authentication.ntlm.structures.version import Version
3 from msldap.authentication.ntlm.structures.avpair import AVPairs, AVPAIRType
4
5 NTLMServerTemplates = {
6 "Windows2003" : {
7 'flags' : NegotiateFlags.NEGOTIATE_56|NegotiateFlags.NEGOTIATE_128|
8 NegotiateFlags.NEGOTIATE_VERSION|NegotiateFlags.NEGOTIATE_TARGET_INFO|
9 NegotiateFlags.NEGOTIATE_EXTENDED_SESSIONSECURITY|
10 NegotiateFlags.TARGET_TYPE_DOMAIN|NegotiateFlags.NEGOTIATE_NTLM|
11 NegotiateFlags.REQUEST_TARGET|NegotiateFlags.NEGOTIATE_UNICODE ,
12 'version' : Version.from_bytes(b"\x05\x02\xce\x0e\x00\x00\x00\x0f"),
13 'targetinfo' : AVPairs({ AVPAIRType.MsvAvNbDomainName : 'SMB',
14 AVPAIRType.MsvAvNbComputerName : 'SMB-TOOLKIT',
15 AVPAIRType.MsvAvDnsDomainName : 'smb.local',
16 AVPAIRType.MsvAvDnsComputerName : 'server2003.smb.local',
17 AVPAIRType.MsvAvDnsTreeName : 'smb.local',
18 }),
19
20 'targetname' : 'SMB',
21 },
22 }
+0
-116
msldap/authentication/ntlm/wsnet.py less more
0 from msldap import logger
1 from msldap.authentication.ntlm.native import NTLMAUTHHandler, NTLMHandlerSettings
2 from pyodidewsnet.clientauth import WSNETAuth
3 import enum
4
5 class ISC_REQ(enum.IntFlag):
6 DELEGATE = 1
7 MUTUAL_AUTH = 2
8 REPLAY_DETECT = 4
9 SEQUENCE_DETECT = 8
10 CONFIDENTIALITY = 16
11 USE_SESSION_KEY = 32
12 PROMPT_FOR_CREDS = 64
13 USE_SUPPLIED_CREDS = 128
14 ALLOCATE_MEMORY = 256
15 USE_DCE_STYLE = 512
16 DATAGRAM = 1024
17 CONNECTION = 2048
18 CALL_LEVEL = 4096
19 FRAGMENT_SUPPLIED = 8192
20 EXTENDED_ERROR = 16384
21 STREAM = 32768
22 INTEGRITY = 65536
23 IDENTIFY = 131072
24 NULL_SESSION = 262144
25 MANUAL_CRED_VALIDATION = 524288
26 RESERVED1 = 1048576
27 FRAGMENT_TO_FIT = 2097152
28 HTTP = 0x10000000
29
30 class MSLDAPWSNetNTLMAuth:
31 def __init__(self, settings):
32 self.settings = settings
33 self.mode = None #'CLIENT'
34 self.sspi = WSNETAuth()
35 self.operator = None
36 self.client = None
37 self.target = None
38 self.seq_number = 0
39
40 self.session_key = None
41 self.ntlm_ctx = NTLMAUTHHandler(NTLMHandlerSettings(None, 'MANUAL'))
42
43 def setup(self):
44 return
45
46 @property
47 def ntlmChallenge(self):
48 return self.ntlm_ctx.ntlmChallenge
49
50 def get_sealkey(self, mode = 'Client'):
51 return self.ntlm_ctx.get_sealkey(mode = mode)
52
53 def get_signkey(self, mode = 'Client'):
54 return self.ntlm_ctx.get_signkey(mode = mode)
55
56 def signing_needed(self):
57 return self.ntlm_ctx.signing_needed()
58
59 def encryption_needed(self):
60 return self.ntlm_ctx.encryption_needed()
61
62 async def encrypt(self, data, message_no):
63 return await self.ntlm_ctx.encrypt(data, message_no)
64
65 async def decrypt(self, data, sequence_no, direction='init', auth_data=None):
66 return await self.ntlm_ctx.decrypt(data, sequence_no, direction=direction, auth_data=auth_data)
67
68 async def sign(self, data, message_no, direction=None, reset_cipher = False):
69 return await self.ntlm_ctx.sign(data, message_no, direction=None, reset_cipher = reset_cipher)
70
71
72 def SEAL(self, signingKey, sealingKey, messageToSign, messageToEncrypt, seqNum, cipher_encrypt):
73 return self.ntlm_ctx.SEAL(signingKey, sealingKey, messageToSign, messageToEncrypt, seqNum, cipher_encrypt)
74
75 def SIGN(self, signingKey, message, seqNum, cipher_encrypt):
76 return self.ntlm_ctx.SIGN(signingKey, message, seqNum, cipher_encrypt)
77
78 def get_session_key(self):
79 return self.session_key
80
81 def get_seq_number(self):
82 return self.seq_number
83
84 def is_extended_security(self):
85 return self.ntlm_ctx.is_extended_security()
86
87 async def authenticate(self, authData = b'', flags = None, seq_number = 0, cb_data = None):
88 try:
89 if flags is None:
90 flags = ISC_REQ.CONNECTION
91
92 if authData is None:
93 status, ctxattr, data, err = await self.sspi.authenticate('NTLM', '', '', 3, flags.value, authdata = b'')
94 if err is not None:
95 raise err
96 self.ntlm_ctx.load_negotiate(data)
97 return data, True, None
98 else:
99 self.ntlm_ctx.load_challenge(authData)
100 status, ctxattr, data, err = await self.sspi.authenticate('NTLM', '', '', 3, flags.value, authdata = authData)
101 if err is not None:
102 raise err
103 if err is None:
104 self.ntlm_ctx.load_authenticate(data)
105 self.session_key, err = await self.sspi.get_sessionkey()
106 if err is not None:
107 raise err
108 self.ntlm_ctx.load_sessionkey(self.get_session_key())
109
110 await self.sspi.disconnect()
111 return data, False, None
112 except Exception as e:
113 return None, None, e
114
115
+0
-0
msldap/authentication/spnego/__init__.py less more
(Empty file)
+0
-168
msldap/authentication/spnego/asn1_structs.py less more
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6 # https://www.rfc-editor.org/rfc/rfc4178.txt
7
8 from asn1crypto.core import ObjectIdentifier, Sequence, SequenceOf, Enumerated, GeneralString, OctetString, BitString, Choice, Any, Boolean
9 import enum
10 import os
11 import io
12
13 TAG = 'explicit'
14
15 # class
16 UNIVERSAL = 0
17 APPLICATION = 1
18 CONTEXT = 2
19
20
21 class MechType(ObjectIdentifier):
22 _map = {
23 '1.3.6.1.4.1.311.2.2.10': 'NTLMSSP - Microsoft NTLM Security Support Provider',
24 '1.2.840.48018.1.2.2' : 'MS KRB5 - Microsoft Kerberos 5',
25 '1.2.840.113554.1.2.2' : 'KRB5 - Kerberos 5',
26 '1.2.840.113554.1.2.2.3': 'KRB5 - Kerberos 5 - User to User',
27 '1.3.6.1.4.1.311.2.2.30': 'NEGOEX - SPNEGO Extended Negotiation Security Mechanism',
28 }
29
30 class MechTypes(SequenceOf):
31 _child_spec = MechType
32
33 class ContextFlags(BitString):
34 _map = {
35 0: 'delegFlag',
36 1: 'mutualFlag',
37 2: 'replayFlag',
38 3: 'sequenceFlag',
39 4: 'anonFlag',
40 5: 'confFlag',
41 6: 'integFlag',
42 }
43
44 class NegState(Enumerated):
45 _map = {
46 0: 'accept-completed',
47 1: 'accept-incomplete',
48 2: 'reject',
49 3: 'request-mic',
50 }
51
52 class NegHints(Sequence):
53 _fields = [
54 ('hintName', GeneralString, {'explicit': 0, 'optional': True}),
55 ('hintAddress', OctetString, {'explicit': 1, 'optional': True}),
56 ]
57
58 # https://www.rfc-editor.org/rfc/rfc4178.txt 4.2.1
59 # EXTENDED IN: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-spng/8e71cf53-e867-4b79-b5b5-38c92be3d472
60 class NegTokenInit2(Sequence):
61 #explicit = (APPLICATION, 0)
62
63 _fields = [
64 ('mechTypes', MechTypes, {'tag_type': TAG, 'tag': 0}),
65 ('reqFlags', ContextFlags, {'tag_type': TAG, 'tag': 1, 'optional': True}),
66 ('mechToken', OctetString, {'tag_type': TAG, 'tag': 2, 'optional': True}),
67 ('negHints', NegHints, {'tag_type': TAG, 'tag': 3, 'optional': True}),
68 ('mechListMIC', OctetString, {'tag_type': TAG, 'tag': 4, 'optional': True}),
69 ]
70
71 # https://www.rfc-editor.org/rfc/rfc4178.txt 4.2.2
72
73 class NegTokenResp(Sequence):
74 #explicit = (APPLICATION, 1)
75
76 _fields = [
77 ('negState', NegState, {'tag_type': TAG, 'tag': 0, 'optional': True}),
78 ('supportedMech', MechType, {'tag_type': TAG, 'tag': 1, 'optional': True}),
79 ('responseToken', OctetString, {'tag_type': TAG, 'tag': 2, 'optional': True}),
80 ('mechListMIC', OctetString, {'tag_type': TAG, 'tag': 3, 'optional': True}),
81 ]
82
83
84 class NegotiationToken(Choice):
85 _alternatives = [
86 ('negTokenInit', NegTokenInit2, {'explicit': (CONTEXT, 0) } ),
87 ('negTokenResp', NegTokenResp, {'explicit': (CONTEXT, 1) } ),
88 ]
89
90
91 class GSS_SPNEGO(Sequence):
92 class_ = 2
93 tag = 0
94
95 _fields = [
96 ('NegotiationToken', NegotiationToken),
97 ]
98
99 ### I have 0 idea where this is tandardized :(
100 class GSSType(ObjectIdentifier):
101 _map = {
102 #'': 'SNMPv2-SMI::enterprises.311.2.2.30',
103 '1.3.6.1.5.5.2': 'SPNEGO',
104 }
105
106 class GSSAPI(Sequence):
107 class_ = 1
108 tag = 0
109
110 _fields = [
111 ('type', GSSType, {'optional': False}),
112 ('value', Any, {'optional': False}),
113 ]
114
115 _oid_pair = ('type', 'value')
116 _oid_specs = {
117 'SPNEGO': NegotiationToken,
118 }
119
120 # https://tools.ietf.org/html/rfc2743#page-81
121 # You may think this is ASN1. But it truth, it's not.
122 # Below is a fucking disgrace of a protocol design.
123 class KRB5Token:
124 def __init__(self, data = None, tok_id = b'\x01\x00'):
125 self.tok_id = tok_id
126 self.data = data
127
128
129 @staticmethod
130 def from_bytes(data):
131 return KRB5Token.from_buffer(io.BytesIO(data))
132
133 @staticmethod
134 def from_buffer(buff):
135 t = KRB5Token()
136 buff.read(1)
137 length = -1
138 x = int.from_bytes(buff.read(1), 'big', signed = False)
139 input(x)
140 if x <= 127:
141 length = x
142 else:
143 x &= ~0x80
144 input(x)
145 length = int.from_bytes(buff.read(x), 'big', signed = False)
146 input('length: %s' % length)
147 oid_asn1 = buff.read(11)
148 t.tok_id = int.from_bytes(buff.read(2), 'big', signed = False)
149 t.data = buff.read(length-13)
150 input(t.tok_id )
151 return t
152
153 def length_encode(self, x):
154 if x <= 127:
155 return x.to_bytes(1, 'big', signed = False)
156 else:
157 lb = x.to_bytes((x.bit_length() + 7) // 8, 'big')
158 t = (0x80 | len(lb)).to_bytes(1, 'big', signed = False)
159 return t+lb
160
161 def to_bytes(self):
162 t = b'\x60' #
163 t += self.length_encode(11 + 2 + len(self.data))
164 t += bytes.fromhex('06092a864886f712010202') #OID length + OID for kerberos
165 t += self.tok_id
166 t += self.data
167 return t
+0
-258
msldap/authentication/spnego/native.py less more
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6 import copy
7 from msldap.authentication.spnego.asn1_structs import *
8 from asn1crypto.core import OctetString
9
10 # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-spng/d4f2b41c-5f9e-4e11-98d0-ade76467095d
11
12
13 # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-spng/94ccc4f8-d224-495f-8d31-4f58d1af598e
14 ## SPNEGO has been assigned the following object identifier (OID): so.org.dod.internet.security.mechanism.snego (1.3.6.1.5.5.2)
15
16 class SPNEGO:
17 def __init__(self, mode = 'CLIENT'):
18 self.mode = mode
19 self.authentication_contexts = {}
20 self.original_authentication_contexts = {}
21 self.selected_authentication_context = None
22 self.selected_mechtype = None
23 self.iteration_ctr = 0
24
25 def get_copy(self):
26 spnego = SPNEGO()
27 for ctx_name in self.list_original_conexts():
28 spnego.add_auth_context(ctx_name, self.get_original_context(ctx_name))
29 return spnego
30
31 def list_original_conexts(self):
32 """
33 Returns a list of authentication context names available to the SPNEGO authentication.
34 """
35 return list(self.original_authentication_contexts.keys())
36
37 def get_original_context(self, ctx_name):
38 """
39 Returns a copy of the original (not used) authentication context sp[ecified by name.
40 You may use this ctx to perform future authentication, as it has the user credentials
41 """
42 return copy.deepcopy(self.original_authentication_contexts[ctx_name])
43
44 def signing_needed(self):
45 return self.selected_authentication_context.signing_needed()
46
47 def encryption_needed(self):
48 return self.selected_authentication_context.encryption_needed()
49
50 def get_seq_number(self):
51 return self.selected_authentication_context.get_seq_number()
52
53 async def unsign(self, data):
54 #TODO: IMPLEMENT THIS
55 return data
56
57 async def verify(self, data, signature):
58 return await self.selected_authentication_context.verify(data, signature)
59
60 async def sign(self, data, message_no, direction='init', reset_cipher = False):
61 return await self.selected_authentication_context.sign(data, message_no, direction=direction, reset_cipher = reset_cipher)
62
63 async def encrypt(self, data, message_no):
64 return await self.selected_authentication_context.encrypt(data, message_no)
65
66 async def decrypt(self, data, message_no, direction='init', auth_data=None):
67 return await self.selected_authentication_context.decrypt(data, message_no, direction=direction)
68
69 def add_auth_context(self, name, ctx):
70 """
71 Add an authentication context to the given authentication context name.
72 Valid names are:
73 'NTLMSSP - Microsoft NTLM Security Support Provider'
74 'MS KRB5 - Microsoft Kerberos 5'
75 'KRB5 - Kerberos 5'
76 'KRB5 - Kerberos 5 - User to User'
77 'NEGOEX - SPNEGO Extended Negotiation Security Mechanism'
78
79 Context MUST be already set up!
80 """
81 self.authentication_contexts[name] = ctx
82 self.original_authentication_contexts[name] = copy.deepcopy(ctx)
83
84 def select_common_athentication_type(self, mech_types):
85 for auth_type_name in self.authentication_contexts:
86 if auth_type_name in mech_types:
87 return auth_type_name, self.authentication_contexts[auth_type_name]
88
89 return None, None
90
91 async def process_ctx_authenticate(self, token_data, include_negstate = False, flags = None, seq_number = 0, cb_data = None):
92 result, to_continue, err = await self.selected_authentication_context.authenticate(token_data, flags = flags, seq_number = seq_number, cb_data = cb_data)
93 if err is not None:
94 return None, None, err
95 if not result:
96 return None, False, None
97 response = {}
98 if include_negstate == True:
99 if to_continue == True:
100 response['negState'] = NegState('accept-incomplete')
101 else:
102 response['negState'] = NegState('accept-completed')
103
104 response['responseToken'] = result
105 return response, to_continue, None
106
107 def get_extra_info(self):
108 if hasattr(self.selected_authentication_context, 'get_extra_info'):
109 return self.selected_authentication_context.get_extra_info()
110 return None
111
112 def get_session_key(self):
113 return self.selected_authentication_context.get_session_key()
114
115 def get_mechtypes_list(self):
116 neghint = {'hintName':'not_defined_in_RFC4178@please_ignore'}
117 tokinit = {
118 'mechTypes': [MechType(mt) for mt in self.authentication_contexts],
119 'negHints': NegHints(neghint),
120 }
121
122 negtoken = NegotiationToken({'negTokenInit':NegTokenInit2(tokinit)})
123 #spnego = GSS_SPNEGO({'NegotiationToken':negtoken})
124 return GSSAPI({'type': GSSType('1.3.6.1.5.5.2'), 'value':negtoken}).dump()
125
126 async def authenticate(self, token, flags = None, seq_number = 0, cb_data = None):
127 """
128 This function is called (multiple times) during negotiation phase of a protocol to determine hich auth mechanism to be used
129 Token is a byte array that is an ASN1 NegotiationToken structure.
130 """
131 if self.selected_mechtype is None:
132 if token is None:
133 #first call to auth, we need to create NegTokenInit2
134 #we must list all available auth types, if only one is present then generate initial auth data with it
135
136 selected_name = None
137 mechtypes = []
138 for mechname in self.authentication_contexts:
139 selected_name = mechname #only used if there is one!
140 mechtypes.append(MechType(mechname))
141
142 response = {}
143 response['mechTypes'] = MechTypes(mechtypes)
144
145 self.negtypes_store = MechTypes(mechtypes).dump()
146
147
148 if len(mechtypes) == 1:
149 self.selected_authentication_context = self.authentication_contexts[selected_name]
150 self.selected_mechtype = selected_name
151 result, to_continue, err = await self.selected_authentication_context.authenticate(None, cb_data=cb_data)
152 if err is not None:
153 return None, None, err
154
155 if not result:
156 return None, False, None
157 if str(response['mechTypes'][0]) == '1.2.840.48018.1.2.2':
158 response['mechToken'] = KRB5Token(result).to_bytes()
159
160 #response['mechToken'] = bytes.fromhex('2a864886f712010202') + #???????
161 else:
162 response['mechToken'] = result
163 #raise Exception('NTLM as RPC GSSAPI not implemented!')
164
165 ### First message and ONLY the first message goes out with additional wrapping
166
167 negtoken = NegotiationToken({'negTokenInit':NegTokenInit2(response)})
168
169
170 #spnego = GSS_SPNEGO({'NegotiationToken':negtoken})
171 return GSSAPI({'type': GSSType('1.3.6.1.5.5.2'), 'value':negtoken}).dump(), True, None
172
173
174 else:
175 #we have already send the NegTokenInit2, but it contained multiple auth types,
176 #at this point server is replying which auth type to use
177 neg_token_raw = NegotiationToken.load(token)
178
179 neg_token = neg_token_raw.native
180
181 if not isinstance(neg_token_raw, NegTokenResp):
182 raise Exception('Server send init???')
183
184
185 self.selected_authentication_context = self.authentication_contexts[neg_token.mechTypes[0]]
186 self.selected_mechtype = neg_token['supportedMech']
187
188
189 response, to_continue, err = await self.process_ctx_authenticate(neg_token['responseToken'], flags = flags, seq_number = seq_number, cb_data = cb_data)
190 if err is not None:
191 return None, None, err
192 return NegTokenResp(response).dump(), to_continue, None
193
194 else:
195 #everything is netotiated, but authentication needs more setps
196 neg_token_raw = NegotiationToken.load(token)
197 neg_token = neg_token_raw.native
198 if neg_token['negState'] == 'accept-completed':
199 return None, True, None
200 if neg_token['responseToken'] is None:
201 # https://tools.ietf.org/html/rfc4178#section-5
202 # mechlistmic exchange happening at the end of the authentication
203 return None, True, None
204 #raise Exception('Should not be here....')
205 #print('server mechListMIC: %s' % neg_token['mechListMIC'])
206 #res = await self.verify(self.negtypes_store, neg_token['mechListMIC'])
207 #print('res %s' % res)
208 #print(self.negtypes_store)
209 #print(self.negtypes_store.hex())
210 #ret = await self.sign(self.negtypes_store, 0)
211 #print(ret)
212 #print(ret.hex())
213 #res = {
214 # 'mechListMIC' : ret,
215 # 'negState': NegState('accept-completed')
216 #}
217 #return NegotiationToken({'negTokenResp':NegTokenResp(res)}).dump(), True, None
218
219 else:
220 response, to_continue, err = await self.process_ctx_authenticate(neg_token['responseToken'], flags = flags, seq_number = seq_number, cb_data = cb_data)
221 if err is not None:
222 return None, None, err
223 if not response:
224 return None, False, None
225
226 if self.selected_mechtype.startswith('NTLM'):
227 response['mechListMIC'] = await self.sign(self.negtypes_store, 0, reset_cipher = True)
228 #self.selected_authentication_context.
229 #print(response)
230 res = NegotiationToken({'negTokenResp':NegTokenResp(response)}).dump()
231
232 return res, to_continue, None
233
234 def test():
235 test_data = bytes.fromhex('a03e303ca00e300c060a2b06010401823702020aa22a04284e544c4d5353500001000000978208e2000000000000000000000000000000000a00d73a0000000f')
236 neg_token = NegotiationToken.load(test_data)
237 print(neg_token.native)
238
239
240 test_data_2 = bytes.fromhex('a181ce3081cba0030a0101a10c060a2b06010401823702020aa281b50481b24e544c4d53535000020000000800080038000000158289e2a7314a557bdb11bf000000000000000072007200400000000a0063450000000f540045005300540002000800540045005300540001001200570049004e003200300031003900410044000400120074006500730074002e0063006f007200700003002600570049004e003200300031003900410044002e0074006500730074002e0063006f007200700007000800aec600bfc5fdd40100000000')
241 neg_token = NegotiationToken.load(test_data_2)
242 print(neg_token.native)
243
244 test_data_3 = bytes.fromhex('a11b3019a0030a0100a3120410010000006b65125a00bb9ab400000000')
245 neg_token = NegotiationToken.load(test_data_3)
246 print(neg_token.native)
247
248 mt = MechType('NTLMSSP - Microsoft NTLM Security Support Provider')
249 print(mt)
250
251 print(MechType.map('1.3.6.1.4.1.311.2.2.10'))
252 print(MechType.unmap('1.3.6.1.4.1.311.2.2.10'))
253
254 #spnego_test = SPNEGO()
255 #spnego_test.authenticate(test_data_2)
256 if __name__ == '__main__':
257 test()
+0
-63
msldap/authentication/spnego/sspi.py less more
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5 # This is a failed attempt to simplify the SSPI integration.
6 # Sadly this doesnt work, as windows doesnt give the session key for some reason?
7 #
8
9 from winsspi.sspi import NegotiateSSPI, SSPIResult
10
11 class SPNEGO_SSPI:
12 def __init__(self, settings):
13 self.mode = 'CLIENT'
14 self.settings = settings
15 self.sspi = None
16 self.username = None
17 self.password = None
18 self.target = None
19
20 self.setup()
21
22 def setup(self):
23 if 'mode' in self.settings:
24 self.mode = self.settings['mode']
25
26 if 'username' in self.settings:
27 self.username = self.settings['username']
28 if 'password' in self.settings:
29 self.password = self.settings['password']
30
31 if 'target' in self.settings:
32 self.username = self.settings['target']
33
34 self.sspi = NegotiateSSPI()
35 self.sspi.authGSSClientInit(self.target, client_name = self.username)
36
37 def get_session_key(self):
38 return self.sspi.get_session_key()
39
40 async def encrypt(self, data, message_no):
41 return await self.sspi.encrypt(data, message_no)
42
43 async def decrypt(self, data, message_no):
44 return await self.sspi.decrypt(data, message_no)
45
46 async def authenticate(self, token, flags = None, seq_number = 0):
47 try:
48 if self.mode.upper() == 'CLIENT':
49 res, data = self.sspi.authGSSClientStep(token)
50 if res == SSPIResult.OK:
51 return data[0][1], True
52 elif res == SSPIResult.CONTINUE:
53 return data[0][1], False
54 else:
55 raise Exception('SSPI errors')
56
57 else:
58 raise Exception('SERVER is not supported now')
59 except Exception as e:
60 import traceback
61 traceback.print_exc()
62
1515 from msldap.connection import MSLDAPClientConnection
1616 from msldap.protocol.messages import Control
1717 from msldap.ldap_objects import *
18 from msldap.commons.utils import KNOWN_SIDS
1819
1920 from winacl.dtyp.security_descriptor import SECURITY_DESCRIPTOR
2021 from winacl.dtyp.ace import ACCESS_ALLOWED_OBJECT_ACE, ADS_ACCESS_MASK
3738 :rtype: dict
3839
3940 """
40 def __init__(self, target, creds):
41 def __init__(self, target, creds, connection = None, keepalive = False):
4142 self.creds = creds
4243 self.target = target
43
44 self.ldap_query_page_size = self.target.ldap_query_page_size
44 self.keepalive = keepalive
45 self.ldap_query_page_size = 1000
46 if self.target is not None:
47 self.ldap_query_page_size = self.target.ldap_query_page_size
48
49 self.ldap_query_ratelimit = 0
50 if self.target is not None:
51 self.ldap_query_ratelimit = self.target.ldap_query_ratelimit
52
4553 self._tree = None
4654 self._ldapinfo = None
47 self._con = None
55 self._con = connection
56 self.__keepalive_task = None
57 self.keepalive_period = 10
58 self.disconnected_evt = None
59 self._sid_cache = {} #SID -> (domain, user)
60 self._domainsid_cache = {} # SID -> domain
4861
4962 async def __aenter__(self):
5063 return self
5265 async def __aexit__(self, exc_type, exc, traceback):
5366 await asyncio.wait_for(self.disconnect(), timeout = 1)
5467
68 async def __keepalive(self):
69 try:
70 while not self.disconnected_evt.is_set():
71 if self._con is not None:
72 ldap_filter = r'(distinguishedName=%s)' % self._tree
73 async for entry, err in self.pagedsearch(ldap_filter, MSADInfo_ATTRS):
74 if err is not None:
75 return None, err
76 await asyncio.sleep(self.keepalive_period)
77
78
79 except asyncio.CancelledError:
80 return
81
82 except Exception as e:
83 print('Keepalive exception: %s' % e)
84 await self.disconnect()
85
5586 async def disconnect(self):
5687 try:
88 if self.__keepalive_task is not None:
89 self.__keepalive_task.cancel()
5790 if self._con is not None:
5891 await self._con.disconnect()
92
93 self.disconnected_evt.set()
5994
6095 except Exception as e:
6196 return False, e
6297
6398 async def connect(self):
6499 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
100 self.disconnected_evt = asyncio.Event()
101 if self._con is None:
102 self._con = MSLDAPClientConnection(self.target, self.creds)
103 _, err = await self._con.connect()
104 if err is not None:
105 raise err
106 res, err = await self._con.bind()
107 if err is not None:
108 return False, err
72109 res, err = await self._con.get_serverinfo()
73110 if err is not None:
74111 raise err
75112 self._serverinfo = res
76113 self._tree = res['defaultNamingContext']
77114 self._ldapinfo, err = await self.get_ad_info()
78 if err is not None:
79 raise err
115 if self._con.is_anon is False:
116 if err is not None:
117 raise err
118 self._domainsid_cache[self._ldapinfo.objectSid] = self._ldapinfo.name
119
120 if self.keepalive is True:
121 self.__keepalive_task = asyncio.create_task(self.__keepalive())
80122 return True, None
81123 except Exception as e:
82124 return False, e
84126 def get_server_info(self):
85127 return self._serverinfo
86128
87 async def pagedsearch(self, query, attributes, controls = None):
129 async def pagedsearch(self, query, attributes, controls = None, tree = None):
88130 """
89131 Performs a paged search on the AD, using the filter and attributes as a normal query does.
90132 !The LDAP connection MUST be active before invoking this function!
111153 print('Theconnection is in stopped state!')
112154 return
113155
114 if self._tree is None:
156 if tree is None:
157 tree = self._tree
158 if tree is None:
115159 raise Exception('BIND first!')
116160 t = []
117161 for x in attributes:
130174 controls = t
131175
132176 async for entry, err in self._con.pagedsearch(
133 self._tree,
177 tree,
134178 query,
135179 attributes = attributes,
136180 size_limit = self.ldap_query_page_size,
137181 controls = controls,
138 rate_limit=self.target.ldap_query_ratelimit
182 rate_limit=self.ldap_query_ratelimit
139183 ):
140184
141185 if err is not None:
169213 size_limit = self.ldap_query_page_size,
170214 search_scope=LEVEL,
171215 controls = None,
172 rate_limit=self.target.ldap_query_ratelimit
216 rate_limit=self.ldap_query_ratelimit
173217 ):
174218 if err is not None:
175219 raise err
184228 tree[entry['attributes']['distinguishedName']] = subtree
185229 return {root_dn : tree}
186230
187 async def get_all_users(self):
231 async def get_all_users(self, attrs = MSADUser_ATTRS):
188232 """
189233 Fetches all user objects available in the LDAP tree and yields them as MSADUser object.
190234
194238 """
195239 logger.debug('Polling AD for all user objects')
196240 ldap_filter = r'(sAMAccountType=805306368)'
197 async for entry, err in self.pagedsearch(ldap_filter, MSADUser_ATTRS):
241 async for entry, err in self.pagedsearch(ldap_filter, attrs):
198242 if err is not None:
199243 yield None, err
200244 return
221265 yield MSADMachine.from_ldap(entry, self._ldapinfo), None
222266 logger.debug('Finished polling for entries!')
223267
224 async def get_all_gpos(self):
268 async def get_all_gpos(self, attrs = MSADGPO_ATTRS):
225269 """
226270 Fetches all GPOs available in the LDAP tree and yields them as MSADGPO object.
227271
231275 """
232276
233277 ldap_filter = r'(objectCategory=groupPolicyContainer)'
234 async for entry, err in self.pagedsearch(ldap_filter, MSADGPO_ATTRS):
278 async for entry, err in self.pagedsearch(ldap_filter, attrs):
235279 if err is not None:
236280 yield None, err
237281 return
292336 size_limit = self.ldap_query_page_size,
293337 search_scope=BASE,
294338 controls = None,
295 rate_limit=self.target.ldap_query_ratelimit
339 rate_limit=self.ldap_query_ratelimit
296340 ):
297341 if err is not None:
298342 yield None, err
525569 }
526570 return await self._con.modify(object_dn, changes, controls = controls)
527571
528 async def get_all_groups(self):
572 async def get_all_groups(self, attrs = MSADGroup_ATTRS):
529573 """
530574 Yields all Groups present in the LDAP tree.
531575
533577 :rtype: Iterator[(:class:`MSADGroup`, :class:`Exception`)]
534578 """
535579 ldap_filter = r'(objectClass=group)'
536 async for entry, err in self.pagedsearch(ldap_filter, MSADGroup_ATTRS):
580 async for entry, err in self.pagedsearch(ldap_filter, attrs):
537581 if err is not None:
538582 yield None, err
539583 return
568612 if err is not None:
569613 return None, err
570614 return MSADGroup.from_ldap(entry), None
615
616 return None, Exception('Search returned no results!')
571617
572618 async def get_user_by_dn(self, user_dn):
573619 """
597643 :rtype: Iterator[(:class:`MSADUser`, :class:`Exception`)]
598644 """
599645
600 group, err = self.get_group_by_dn(dn)
646 group, err = await self.get_group_by_dn(dn)
601647 if err is not None:
602648 yield None, err
603649 return
604650 for member in group.member:
605 async for result in self.get_object_by_dn(member):
651 async for result, err in self.get_object_by_dn(member):
606652 if isinstance(result, MSADGroup) and recursive:
607653 async for user, err in self.get_group_members(result.distinguishedName, recursive = True):
608654 yield user, err
609655 else:
610656 yield result, err
657
611658
612659 async def get_dn_for_objectsid(self, objectsid):
613660 """
626673 return None, err
627674
628675 return entry['attributes']['distinguishedName'], None
676
677 return None, Exception('Search returned no results!')
629678
630679 async def get_objectsid_for_dn(self, dn):
631680 """
644693 return None, err
645694
646695 return entry['attributes']['objectSid'], None
647
696
697 return None, Exception('Search returned no results!')
698
699 async def get_tokengroups_user(self, samaccountname):
700 ldap_filter = r'(sAMAccountName=%s)' % escape_filter_chars(samaccountname)
701 user_dn = None
702 async for entry, err in self.pagedsearch(ldap_filter, ['distinguishedName']):
703 if err is not None:
704 return None, err
705
706 user_dn = entry['attributes']['distinguishedName']
707
708 if user_dn is None:
709 return None, Exception('User not found! %s' % samaccountname)
710
711 tokengroup = []
712 async for sids, err in self.get_tokengroups(user_dn):
713 if err is not None:
714 return None, err
715 tokengroup.append(sids)
716
717 return tokengroup, None
718
648719 async def get_tokengroups(self, dn):
649720 """
650721 Yields SIDs of groups that the given DN is a member of.
662733 attributes = attributes,
663734 size_limit = self.ldap_query_page_size,
664735 search_scope=BASE,
665 rate_limit=self.target.ldap_query_ratelimit
736 rate_limit=self.ldap_query_ratelimit
666737 ):
667738 if err is not None:
668739 yield None, err
699770 attributes = [b'tokenGroups'],
700771 size_limit = self.ldap_query_page_size,
701772 search_scope=BASE,
702 rate_limit=self.target.ldap_query_ratelimit
773 rate_limit=self.ldap_query_ratelimit
703774 ):
704775
705776 #print(entry2)
864935 'servicePrincipalName': [('add', [spn])]
865936 }
866937 return await self._con.modify(user_dn, changes)
938
939 async def del_user_spn(self, user_dn, spn):
940 """
941 Adds an SPN record to the user object.
942
943 :param user_dn: The user's DN
944 :type user_dn: str
945 :param spn: The SPN to be added. It must follow the SPN string format specifications.
946 :type spn: str
947 :return: A tuple of (True, None) on success or (False, Exception) on error.
948 :rtype: (:class:`bool`, :class:`Exception`)
949
950 """
951 changes = {
952 'servicePrincipalName': [('delete', [spn])]
953 }
954 return await self._con.modify(user_dn, changes)
867955
868956 async def add_additional_hostname(self, user_dn, hostname):
869957 """
11471235 return True, None
11481236 except Exception as e:
11491237 return False, e
1238
1239 async def list_root_cas(self):
1240 try:
1241 ldap_filter = "(objectClass=certificationAuthority)"
1242 tree = "CN=Certification Authorities,CN=Public Key Services,CN=Services,CN=Configuration,%s" % self._ldapinfo.distinguishedName
1243 async for entry, err in self.pagedsearch(ldap_filter, attributes = MSADCA_ATTRS, tree = tree):
1244 if err is not None:
1245 yield None, err
1246 return
1247 yield MSADCA.from_ldap(entry, 'ROOTCA'), None
1248
1249 except Exception as e:
1250 yield None, e
1251 return
1252
1253 async def list_ntcas(self):
1254 try:
1255 ldap_filter = "(objectClass=certificationAuthority)"
1256 tree = "CN=NTAuthCertificates,CN=Public Key Services,CN=Services,CN=Configuration,%s" % self._ldapinfo.distinguishedName
1257 async for entry, err in self.pagedsearch(ldap_filter, attributes = MSADCA_ATTRS, tree = tree):
1258 if err is not None:
1259 yield None, err
1260 return
1261 yield MSADCA.from_ldap(entry, 'NTCA'), None
1262
1263 except Exception as e:
1264 yield None, e
1265 return
1266
1267 async def list_aiacas(self):
1268 try:
1269 ldap_filter = "(objectClass=certificationAuthority)"
1270 tree = "CN=AIA,CN=Public Key Services,CN=Services,CN=Configuration,%s" % self._ldapinfo.distinguishedName
1271 async for entry, err in self.pagedsearch(ldap_filter, attributes = MSADCA_ATTRS, tree = tree):
1272 if err is not None:
1273 yield None, err
1274 return
1275 yield MSADCA.from_ldap(entry, 'AIACA'), None
1276
1277 except Exception as e:
1278 yield None, e
1279 return
1280
1281 async def list_enrollment_services(self):
1282 try:
1283 ldap_filter = "(objectCategory=pKIEnrollmentService)"
1284 tree = "CN=Configuration,%s" % self._ldapinfo.distinguishedName
1285
1286 async for entry, err in self.pagedsearch(ldap_filter, attributes = MSADEnrollmentService_ATTRS, tree = tree):
1287 if err is not None:
1288 yield None, err
1289 return
1290 yield MSADEnrollmentService.from_ldap(entry), None
1291
1292 except Exception as e:
1293 yield None, e
1294 return
1295
1296 async def list_certificate_templates(self, name = None):
1297 try:
1298 req_flags = SDFlagsRequestValue({'Flags' : SDFlagsRequest.DACL_SECURITY_INFORMATION|SDFlagsRequest.GROUP_SECURITY_INFORMATION|SDFlagsRequest.OWNER_SECURITY_INFORMATION})
1299 controls = [('1.2.840.113556.1.4.801', True, req_flags.dump())]
1300
1301 ldap_filter = "(objectCategory=pKICertificateTemplate)"
1302 if name is not None:
1303 ldap_filter = "(&(objectCategory=pKICertificateTemplate)(name=%s))" % name
1304 tree = "CN=Configuration,%s" % self._ldapinfo.distinguishedName
1305
1306 async for entry, err in self.pagedsearch(ldap_filter, attributes = MSADCertificateTemplate_ATTRS, controls=controls, tree = tree):
1307 if err is not None:
1308 yield None, err
1309 return
1310 yield MSADCertificateTemplate.from_ldap(entry), None
1311
1312 except Exception as e:
1313 yield None, e
1314 return
1315
1316 async def resolv_sd(self, sd):
1317 "Resolves all SIDs found in security descriptor, returns lookup table"
1318 try:
1319 if isinstance(sd, bytes):
1320 sd = SECURITY_DESCRIPTOR.from_bytes(sd)
1321
1322 lookup_table = {}
1323 sids = {}
1324 sids[str(sd.Owner)] = 1
1325 sids[str(sd.Group)] = 1
1326 if sd.Dacl is not None:
1327 for ace in sd.Dacl.aces:
1328 sids[str(ace.Sid)] = 1
1329 if sd.Sacl is not None:
1330 for ace in sd.Sacl.aces:
1331 sids[str(ace.Sid)] = 1
1332
1333 for sid in sids:
1334 domain, username, err = await self.resolv_sid(sid)
1335 if err is not None:
1336 raise err
1337 lookup_table[sid] = (domain, username)
1338
1339 return lookup_table, None
1340
1341 except Exception as e:
1342 return None, e
1343
1344 async def resolv_sid(self, sid, use_cache = True):
1345 """Performs a SID lookup for object and returns the domain name and the samaccountname"""
1346 try:
1347 sid = str(sid).upper()
1348 if sid in KNOWN_SIDS:
1349 return "BUILTIN", KNOWN_SIDS[sid], None
1350 domain = None
1351 username = None
1352 domainsid = sid.rsplit('-',1)[0]
1353 if domainsid not in self._domainsid_cache:
1354 logger.debug('Domain SID "%s" was not found! ' % domainsid)
1355 return '???', '???', None
1356 domain = self._domainsid_cache[domainsid]
1357 if sid in self._sid_cache:
1358 username = self._sid_cache[sid]
1359
1360 else:
1361 ldap_filter = r'(objectSid=%s)' % sid
1362 async for entry, err in self.pagedsearch(ldap_filter, attributes = ['sAMAccountName']):
1363 if err is not None:
1364 return None, None, err
1365 username = entry['attributes'].get('sAMAccountName')
1366
1367 if username is None:
1368 return domain, '???', None
1369 #raise Exception('User not found! %s' % sid)
1370
1371 if use_cache is True:
1372 self._sid_cache[sid] = username
1373 return domain, username, None
1374 except Exception as e:
1375 return None, None, e
1376
1377 async def whoami(self):
1378 return await self._con.whoami()
1379
1380 async def whoamifull(self):
1381 """Full whoami"""
1382 #TODO: it can be the case that the server returns the SID of the user
1383 # implement that path!
1384 result = {}
1385 try:
1386 res, err = await self.whoami()
1387 if err is not None:
1388 raise err
1389 result['raw'] = res
1390 if res.startswith('u:') is True:
1391 domain, samaccountname = res[2:].split('\\', 1)
1392 result['domain'] = domain
1393 result['samaccountname'] = samaccountname
1394 user, err = await self.get_user(samaccountname)
1395 if err is not None:
1396 raise err
1397 result['sid'] = str(user.objectSid)
1398 result['groups'] = {}
1399 async for group_sid, err in self.get_tokengroups(user.distinguishedName):
1400 if err is not None:
1401 raise err
1402 result['groups'][group_sid] = ('NA','NA')
1403 domain, username, err = await self.resolv_sid(group_sid)
1404 if err is not None:
1405 raise err
1406 result['groups'][group_sid] = (domain, username)
1407
1408 return result, None
1409 except:
1410 return result, None
11501411
11511412 #async def get_permissions_for_dn(self, dn):
11521413 # """
0 import enum
1 import platform
0 from asyauth.common.credentials.spnego import SPNEGOCredential
1 from asyauth.common.constants import asyauthProtocol
2 from asyauth.common.credentials import UniCredential
23
3 import copy
4 from msldap.commons.credential import MSLDAPCredential, LDAPAuthProtocol
5 from msldap.authentication.spnego.native import SPNEGO
6 from msldap.authentication.ntlm.native import NTLMAUTHHandler, NTLMHandlerSettings
7 from msldap.authentication.kerberos.native import MSLDAPKerberos
8 from msldap.commons.proxy import MSLDAPProxyType
9 from minikerberos.common.target import KerberosTarget
10 from minikerberos.common.proxy import KerberosProxy
11 from minikerberos.common.creds import KerberosCredential
12 from minikerberos.common.spn import KerberosSPN
4 def get_auth_context(credential:UniCredential):
5 if credential.protocol in [asyauthProtocol.NTLM, asyauthProtocol.KERBEROS]:
6 spnego = SPNEGOCredential([credential])
7 return spnego.build_context()
8
9 elif credential.protocol == asyauthProtocol.SICILY:
10 return credential.build_context()
1311
14 from minikerberos.network.selector import KerberosClientSocketSelector
12 elif credential.protocol in [asyauthProtocol.SIMPLE, asyauthProtocol.PLAIN]:
13 return credential
1514
16
17 if platform.system().upper() == 'WINDOWS':
18 from msldap.authentication.kerberos.sspi import MSLDAPKerberosSSPI
19 from msldap.authentication.ntlm.sspi import MSLDAPNTLMSSPI
20
21 class MSLDAPNTLMCredential:
22 def __init__(self):
23 self.username = None
24 self.domain = ''
25 self.password = None
26 self.workstation = None
27 self.is_guest = False
28 self.nt_hash = None
29 self.lm_hash = None
30 self.encrypt = False
31
32 class MSLDAPSIMPLECredential:
33 def __init__(self):
34 self.username = None
35 self.domain = None
36 self.password = None
37
38 class MSLDAPPLAINCredential:
39 def __init__(self):
40 self.username = None
41 self.domain = None
42 self.password = None
43
44 class MSLDAPKerberosCredential:
45 def __init__(self):
46 self.connection = None #KerberosCredential
47 self.target = None #KerberosTarget
48 self.ksoc = None #KerberosSocketAIO
49 self.ccred = None
50 self.encrypt = False
51 self.enctypes = None #[23,17,18]
52
53 class MSLDAPKerberosSSPICredential:
54 def __init__(self):
55 self.domain = None
56 self.password = None
57 self.username = None
58 self.encrypt = False
59
60 class MSLDAPNTLMSSPICredential:
61 def __init__(self):
62 self.username = None
63 self.password = None
64 self.domain = None
65 self.encrypt = False
66
67 class MSLDAPWSNETCredential:
68 def __init__(self):
69 self.type = 'NTLM'
70 self.username = '<CURRENT>'
71 self.domain = '<CURRENT>'
72 self.password = '<CURRENT>'
73 self.target = None
74 self.is_guest = False
75 self.agent_id = None
76 self.encrypt = False
77
78 class MSLDAPSSPIProxyCredential:
79 def __init__(self):
80 self.type = 'NTLM'
81 self.username = '<CURRENT>'
82 self.domain = '<CURRENT>'
83 self.password = '<CURRENT>'
84 self.target = None
85 self.is_guest = False
86 self.agent_id = None
87 self.encrypt = False
88 self.host = '127.0.0.1'
89 self.port = 9999
90 self.proto = 'ws'
91
92
93
94 class MSLDAPMultiplexorCredential:
95 def __init__(self):
96 self.type = 'NTLM'
97 self.username = '<CURRENT>'
98 self.domain = '<CURRENT>'
99 self.password = '<CURRENT>'
100 self.target = None
101 self.is_guest = False
102 self.is_ssl = False
103 self.mp_host = '127.0.0.1'
104 self.mp_port = 9999
105 self.mp_username = None
106 self.mp_domain = None
107 self.mp_password = None
108 self.agent_id = None
109 self.encrypt = False
110
111 def get_url(self):
112 url_temp = 'ws://%s:%s'
113 if self.is_ssl is True:
114 url_temp = 'wss://%s:%s'
115 url = url_temp % (self.mp_host, self.mp_port)
116 return url
117
118 def parse_settings(self, settings):
119 req = ['agentid']
120 for r in req:
121 if r not in settings:
122 raise Exception('%s parameter missing' % r)
123 self.mp_host = settings.get('host', ['127.0.0.1'])[0]
124 self.mp_port = settings.get('port', ['9999'])[0]
125 if self.mp_port is None:
126 self.mp_port = '9999'
127 if 'user' in settings:
128 self.mp_username = settings.get('user')[0]
129 if 'domain' in settings:
130 self.mp_domain = settings.get('domain')[0]
131 if 'password' in settings:
132 self.mp_password = settings.get('password')[0]
133 self.agent_id = settings['agentid'][0]
134
135
136
137 """
138 class LDAPAuthProtocol(enum.Enum):
139 PLAIN = 'PLAIN' #actually SASL-PLAIN
140
141 MULTIPLEXOR = 'MULTIPLEXOR'
142 MULTIPLEXOR_SSL = 'MULTIPLEXOR_SSL'
143 SSPI_NTLM = 'SSPI_NTLM' #actually SASL-GSSAPI-SPNEGO-NTLM but with integrated SSPI
144 SSPI_KERBEROS = 'SSPI_KERBEROS' #actually SASL-GSSAPI-SPNEGO-KERBEROS but with integrated SSPI
145 """
146
147 class AuthenticatorBuilder:
148 def __init__(self, creds, target = None):
149 self.creds = creds
150 self.target = target
151
152 def build(self):
153 if self.creds.auth_method == LDAPAuthProtocol.SICILY:
154 ntlmcred = MSLDAPNTLMCredential()
155 ntlmcred.username = self.creds.username
156 ntlmcred.domain = self.creds.domain if self.creds.domain is not None else ''
157 ntlmcred.workstation = None
158 ntlmcred.is_guest = False
159 ntlmcred.encrypt = self.creds.encrypt
160
161
162 if self.creds.password is None:
163 raise Exception('NTLM authentication requres password/NT hash!')
164
165
166 if len(self.creds.password) == 32:
167 try:
168 bytes.fromhex(self.creds.password)
169 except:
170 ntlmcred.password = self.creds.password
171 else:
172 ntlmcred.nt_hash = self.creds.password
173
174 else:
175 ntlmcred.password = self.creds.password
176
177 settings = NTLMHandlerSettings(ntlmcred)
178 return NTLMAUTHHandler(settings)
179
180 elif self.creds.auth_method == LDAPAuthProtocol.SIMPLE:
181 cred = MSLDAPPLAINCredential()
182 cred.username = self.creds.username
183 cred.domain = self.creds.domain
184 cred.password = self.creds.password
185 return cred
186
187 elif self.creds.auth_method == LDAPAuthProtocol.PLAIN:
188 cred = MSLDAPSIMPLECredential()
189 cred.username = self.creds.username
190 cred.domain = self.creds.domain
191 cred.password = self.creds.password
192 return cred
193
194 elif self.creds.auth_method in [LDAPAuthProtocol.NTLM_PASSWORD, LDAPAuthProtocol.NTLM_NT]:
195 ntlmcred = MSLDAPNTLMCredential()
196 ntlmcred.username = self.creds.username
197 ntlmcred.domain = self.creds.domain if self.creds.domain is not None else ''
198 ntlmcred.workstation = None
199 ntlmcred.is_guest = False
200 ntlmcred.encrypt = self.creds.encrypt
201
202 if self.creds.password is None:
203 raise Exception('NTLM authentication requres password!')
204
205 if self.creds.auth_method == LDAPAuthProtocol.NTLM_PASSWORD:
206 ntlmcred.password = self.creds.password
207 elif self.creds.auth_method == LDAPAuthProtocol.NTLM_NT:
208 ntlmcred.nt_hash = self.creds.password
209 else:
210 raise Exception('Unknown NTLM auth method!')
211
212 settings = NTLMHandlerSettings(ntlmcred)
213 handler = NTLMAUTHHandler(settings)
214
215 ##setting up SPNEGO
216 spneg = SPNEGO()
217 spneg.add_auth_context('NTLMSSP - Microsoft NTLM Security Support Provider', handler)
218
219 return spneg
220
221 elif self.creds.auth_method in [
222 LDAPAuthProtocol.KERBEROS_RC4,
223 LDAPAuthProtocol.KERBEROS_NT,
224 LDAPAuthProtocol.KERBEROS_AES,
225 LDAPAuthProtocol.KERBEROS_PASSWORD,
226 LDAPAuthProtocol.KERBEROS_CCACHE,
227 LDAPAuthProtocol.KERBEROS_KEYTAB,
228 LDAPAuthProtocol.KERBEROS_KIRBI]:
229
230 if self.target is None:
231 raise Exception('Target must be specified with Kerberos!')
232
233 if self.target.host is None:
234 raise Exception('target must have a domain name or hostname for kerberos!')
235
236 if self.target.dc_ip is None:
237 raise Exception('target must have a dc_ip for kerberos!')
238
239 kcred = MSLDAPKerberosCredential()
240 if self.creds.auth_method == LDAPAuthProtocol.KERBEROS_KIRBI:
241 kc = KerberosCredential.from_kirbi(self.creds.password, self.creds.username, self.creds.domain)
242 elif self.creds.auth_method == LDAPAuthProtocol.KERBEROS_CCACHE:
243 kc = KerberosCredential.from_ccache_file(self.creds.password, self.creds.username, self.creds.domain)
244 elif self.creds.auth_method == LDAPAuthProtocol.KERBEROS_KEYTAB:
245 kc = KerberosCredential.from_kirbi(self.creds.password, self.creds.username, self.creds.domain)
246 else:
247 kc = KerberosCredential()
248 kc.username = self.creds.username
249 kc.domain = self.creds.domain
250 kcred.enctypes = []
251 if self.creds.auth_method == LDAPAuthProtocol.KERBEROS_PASSWORD:
252 kc.password = self.creds.password
253 kcred.enctypes = [23,17,18]
254 elif self.creds.auth_method == LDAPAuthProtocol.KERBEROS_NT:
255 kc.nt_hash = self.creds.password
256 kcred.enctypes = [23]
257
258 elif self.creds.auth_method == LDAPAuthProtocol.KERBEROS_AES:
259 if len(self.creds.password) == 32:
260 kc.kerberos_key_aes_128 = self.creds.password
261 kcred.enctypes = [17]
262 elif len(self.creds.password) == 64:
263 kc.kerberos_key_aes_256 = self.creds.password
264 kcred.enctypes = [18]
265
266 elif self.creds.auth_method == LDAPAuthProtocol.KERBEROS_RC4:
267 kc.kerberos_key_rc4 = self.creds.password
268 kcred.enctypes = [23]
269
270 elif self.creds.auth_method == LDAPAuthProtocol.KERBEROS_CCACHE:
271 kc.ccache = self.creds.password
272 kcred.enctypes = [23,17,18] # TODO: fix this
273 elif self.creds.auth_method == LDAPAuthProtocol.KERBEROS_KEYTAB:
274 kc.keytab = self.creds.password
275 kcred.enctypes = [23,17,18] # TODO: fix this
276 elif self.creds.auth_method == LDAPAuthProtocol.KERBEROS_KIRBI:
277 kcred.enctypes = [23,17,18] # TODO: fix this
278 else:
279 raise Exception('No suitable secret type found to set up kerberos!')
280
281 if self.creds.etypes is not None:
282 kcred.enctypes = list(set(self.creds.etypes).intersection(set(kcred.enctypes)))
283
284 kcred.ccred = kc
285 kcred.spn = KerberosSPN.from_target_string(self.target.to_target_string())
286 kcred.target = KerberosTarget(self.target.dc_ip)
287 kcred.encrypt = self.creds.encrypt
288
289 if self.target.proxy is not None:
290 kcred.target.proxy = KerberosProxy()
291 kcred.target.proxy.type = self.target.proxy.type
292 kcred.target.proxy.target = copy.deepcopy(self.target.proxy.target)
293 kcred.target.proxy.target[-1].endpoint_ip = self.target.dc_ip
294 kcred.target.proxy.target[-1].endpoint_port = 88
295
296 handler = MSLDAPKerberos(kcred)
297
298 #setting up SPNEGO
299 spneg = SPNEGO()
300 spneg.add_auth_context('MS KRB5 - Microsoft Kerberos 5', handler)
301 return spneg
302
303 elif self.creds.auth_method == LDAPAuthProtocol.SSPI_KERBEROS:
304 if self.target is None:
305 raise Exception('Target must be specified with Kerberos SSPI!')
306
307 kerbcred = MSLDAPKerberosSSPICredential()
308 kerbcred.username = self.creds.domain if self.creds.domain is not None else '<CURRENT>'
309 kerbcred.username = self.creds.username if self.creds.username is not None else '<CURRENT>'
310 kerbcred.password = self.creds.password if self.creds.password is not None else '<CURRENT>'
311 kerbcred.spn = self.target.to_target_string()
312 kerbcred.encrypt = self.creds.encrypt
313
314 handler = MSLDAPKerberosSSPI(kerbcred)
315 #setting up SPNEGO
316 spneg = SPNEGO()
317 spneg.add_auth_context('MS KRB5 - Microsoft Kerberos 5', handler)
318 return spneg
319
320 elif self.creds.auth_method == LDAPAuthProtocol.SSPI_NTLM:
321 ntlmcred = MSLDAPNTLMSSPICredential()
322 ntlmcred.username = self.creds.domain if self.creds.domain is not None else '<CURRENT>'
323 ntlmcred.username = self.creds.username if self.creds.username is not None else '<CURRENT>'
324 ntlmcred.password = self.creds.password if self.creds.password is not None else '<CURRENT>'
325 ntlmcred.encrypt = self.creds.encrypt
326
327 handler = MSLDAPNTLMSSPI(ntlmcred)
328 #setting up SPNEGO
329 spneg = SPNEGO()
330 spneg.add_auth_context('NTLMSSP - Microsoft NTLM Security Support Provider', handler)
331 return spneg
332
333 elif self.creds.auth_method.value.startswith('MULTIPLEXOR'):
334 if self.creds.auth_method in [LDAPAuthProtocol.MULTIPLEXOR_SSL_NTLM, LDAPAuthProtocol.MULTIPLEXOR_NTLM]:
335 from msldap.authentication.ntlm.multiplexor import MSLDAPNTLMMultiplexor
336 ntlmcred = MSLDAPMultiplexorCredential()
337 ntlmcred.type = 'NTLM'
338 if self.creds.username is not None:
339 ntlmcred.username = '<CURRENT>'
340 if self.creds.domain is not None:
341 ntlmcred.domain = '<CURRENT>'
342 if self.creds.password is not None:
343 ntlmcred.password = '<CURRENT>'
344 ntlmcred.is_guest = False
345 ntlmcred.is_ssl = True if self.creds.auth_method == LDAPAuthProtocol.MULTIPLEXOR_SSL_NTLM else False
346 ntlmcred.parse_settings(self.creds.settings)
347 ntlmcred.encrypt = self.creds.encrypt
348
349 handler = MSLDAPNTLMMultiplexor(ntlmcred)
350 #setting up SPNEGO
351 spneg = SPNEGO()
352 spneg.add_auth_context('NTLMSSP - Microsoft NTLM Security Support Provider', handler)
353 return spneg
354
355 elif self.creds.auth_method in [LDAPAuthProtocol.MULTIPLEXOR_SSL_KERBEROS, LDAPAuthProtocol.MULTIPLEXOR_KERBEROS]:
356 from msldap.authentication.kerberos.multiplexor import MSLDAPKerberosMultiplexor
357
358 ntlmcred = MSLDAPMultiplexorCredential()
359 ntlmcred.type = 'KERBEROS'
360 ntlmcred.target = self.target
361 if self.creds.username is not None:
362 ntlmcred.username = '<CURRENT>'
363 if self.creds.domain is not None:
364 ntlmcred.domain = '<CURRENT>'
365 if self.creds.password is not None:
366 ntlmcred.password = '<CURRENT>'
367 ntlmcred.is_guest = False
368 ntlmcred.is_ssl = True if self.creds.auth_method == LDAPAuthProtocol.MULTIPLEXOR_SSL_NTLM else False
369 ntlmcred.parse_settings(self.creds.settings)
370 ntlmcred.encrypt = self.creds.encrypt
371
372 handler = MSLDAPKerberosMultiplexor(ntlmcred)
373 #setting up SPNEGO
374 spneg = SPNEGO()
375 spneg.add_auth_context('MS KRB5 - Microsoft Kerberos 5', handler)
376 return spneg
377
378 elif self.creds.auth_method.value.startswith('SSPIPROXY'):
379 if self.creds.auth_method == LDAPAuthProtocol.SSPIPROXY_NTLM:
380 from msldap.authentication.ntlm.sspiproxy import MSLDAPSSPIProxyNTLMAuth
381 ntlmcred = MSLDAPSSPIProxyCredential()
382 ntlmcred.type = 'NTLM'
383 if self.creds.username is not None:
384 ntlmcred.username = '<CURRENT>'
385 if self.creds.domain is not None:
386 ntlmcred.domain = '<CURRENT>'
387 if self.creds.password is not None:
388 ntlmcred.password = '<CURRENT>'
389 ntlmcred.is_guest = False
390 ntlmcred.encrypt = self.creds.encrypt
391 ntlmcred.host = self.creds.settings['host'][0]
392 ntlmcred.port = int(self.creds.settings['port'][0])
393 ntlmcred.proto = 'ws'
394 if 'proto' in self.creds.settings:
395 ntlmcred.proto = self.creds.settings['proto'][0]
396 if 'agentid' in self.creds.settings:
397 ntlmcred.agent_id = bytes.fromhex(self.creds.settings['agentid'][0])
398
399 handler = MSLDAPSSPIProxyNTLMAuth(ntlmcred)
400 #setting up SPNEGO
401 spneg = SPNEGO()
402 spneg.add_auth_context('NTLMSSP - Microsoft NTLM Security Support Provider', handler)
403 return spneg
404
405 elif self.creds.auth_method == LDAPAuthProtocol.SSPIPROXY_KERBEROS:
406 from msldap.authentication.kerberos.sspiproxyws import MSLDAPSSPIProxyKerberosAuth
407
408 ntlmcred = MSLDAPSSPIProxyCredential()
409 ntlmcred.type = 'KERBEROS'
410 ntlmcred.target = self.target
411 if self.creds.username is not None:
412 ntlmcred.username = '<CURRENT>'
413 if self.creds.domain is not None:
414 ntlmcred.domain = '<CURRENT>'
415 if self.creds.password is not None:
416 ntlmcred.password = '<CURRENT>'
417 ntlmcred.is_guest = False
418 ntlmcred.encrypt = self.creds.encrypt
419 ntlmcred.host = self.creds.settings['host'][0]
420 ntlmcred.port = self.creds.settings['port'][0]
421 ntlmcred.proto = 'ws'
422 if 'proto' in self.creds.settings:
423 ntlmcred.proto = self.creds.settings['proto'][0]
424 if 'agentid' in self.creds.settings:
425 ntlmcred.agent_id = bytes.fromhex(self.creds.settings['agentid'][0])
426
427 handler = MSLDAPSSPIProxyKerberosAuth(ntlmcred)
428 #setting up SPNEGO
429 spneg = SPNEGO()
430 spneg.add_auth_context('MS KRB5 - Microsoft Kerberos 5', handler)
431 return spneg
432
433 elif self.creds.auth_method.value.startswith('WSNET'):
434 if self.creds.auth_method in [LDAPAuthProtocol.WSNET_NTLM]:
435 from msldap.authentication.ntlm.wsnet import MSLDAPWSNetNTLMAuth
436
437 ntlmcred = MSLDAPWSNETCredential()
438 ntlmcred.type = 'NTLM'
439 if self.creds.username is not None:
440 ntlmcred.username = '<CURRENT>'
441 if self.creds.domain is not None:
442 ntlmcred.domain = '<CURRENT>'
443 if self.creds.password is not None:
444 ntlmcred.password = '<CURRENT>'
445 ntlmcred.is_guest = False
446
447 handler = MSLDAPWSNetNTLMAuth(ntlmcred)
448 spneg = SPNEGO()
449 spneg.add_auth_context('NTLMSSP - Microsoft NTLM Security Support Provider', handler)
450 return spneg
451
452
453 elif self.creds.auth_method in [LDAPAuthProtocol.WSNET_KERBEROS]:
454 from msldap.authentication.kerberos.wsnet import MSLDAPWSNetKerberosAuth
455
456 ntlmcred = MSLDAPWSNETCredential()
457 ntlmcred.type = 'KERBEROS'
458 ntlmcred.target = self.target
459 if self.creds.username is not None:
460 ntlmcred.username = '<CURRENT>'
461 if self.creds.domain is not None:
462 ntlmcred.domain = '<CURRENT>'
463 if self.creds.password is not None:
464 ntlmcred.password = '<CURRENT>'
465 ntlmcred.is_guest = False
466
467 handler = MSLDAPWSNetKerberosAuth(ntlmcred)
468 #setting up SPNEGO
469 spneg = SPNEGO()
470 spneg.add_auth_context('MS KRB5 - Microsoft Kerberos 5', handler)
471 return spneg
15 else:
16 raise Exception('Unsupported authentication protocol "%s"' % credential.protocol)
17
+0
-124
msldap/commons/credential.py less more
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6 import enum
7
8 # https://tools.ietf.org/html/rfc4513
9 # simple auth:
10 # - anonymous
11 # - user without password
12 # - username + password
13 #
14 # SASL:
15 # - plain
16 # - gssapi
17 # -SSPI
18 # - NTLM
19 # - KERBEROS
20 # Sicily:
21 # - NTLM
22 # Multiplexor
23 #
24
25 class LDAPAuthProtocol(enum.Enum):
26 SIMPLE = 'SIMPLE' #SIMPLE can be with no creds - anonymous bind
27 PLAIN = 'PLAIN' #actually SASL-PLAIN
28 SICILY = 'SICILY' #NTLM (old proprietary from MS)
29 NTLM_PASSWORD = 'NTLM_PASSWORD' #actually SASL-GSSAPI-SPNEGO-NTLM
30 NTLM_NT = 'NTLM_NT' #actually SASL-GSSAPI-SPNEGO-NTLM
31 KERBEROS_RC4 = 'KERBEROS_RC4'
32 KERBEROS_NT = 'KERBEROS_NT'
33 KERBEROS_AES = 'KERBEROS_AES'
34 KERBEROS_PASSWORD = 'KERBEROS_PASSWORD'
35 KERBEROS_CCACHE = 'KERBEROS_CCACHE'
36 KERBEROS_KEYTAB = 'KERBEROS_KEYTAB'
37 KERBEROS_KIRBI = 'KERBEROS_KIRBI'
38 MULTIPLEXOR_KERBEROS = 'MULTIPLEXOR_KERBEROS'
39 MULTIPLEXOR_NTLM = 'MULTIPLEXOR_NTLM'
40 MULTIPLEXOR_SSL_KERBEROS = 'MULTIPLEXOR_SSL_KERBEROS'
41 MULTIPLEXOR_SSL_NTLM = 'MULTIPLEXOR_SSL_NTLM'
42 SSPI_NTLM = 'SSPI_NTLM' #actually SASL-GSSAPI-SPNEGO-NTLM but with integrated SSPI
43 SSPI_KERBEROS = 'SSPI_KERBEROS' #actually SASL-GSSAPI-SPNEGO-KERBEROS but with integrated SSPI
44 WSNET_NTLM = 'WSNET_NTLM'
45 WSNET_KERBEROS = 'WSNET_KERBEROS'
46 SSPIPROXY_NTLM = 'SSPIPROXY_NTLM'
47 SSPIPROXY_KERBEROS = 'SSPIPROXY_KERBEROS'
48
49 MSLDAP_GSS_METHODS = [
50 LDAPAuthProtocol.NTLM_PASSWORD ,
51 LDAPAuthProtocol.NTLM_NT ,
52 LDAPAuthProtocol.KERBEROS_RC4 ,
53 LDAPAuthProtocol.KERBEROS_NT ,
54 LDAPAuthProtocol.KERBEROS_AES ,
55 LDAPAuthProtocol.KERBEROS_PASSWORD ,
56 LDAPAuthProtocol.KERBEROS_CCACHE ,
57 LDAPAuthProtocol.KERBEROS_KEYTAB ,
58 LDAPAuthProtocol.KERBEROS_KIRBI ,
59 LDAPAuthProtocol.SSPI_NTLM ,
60 LDAPAuthProtocol.SSPI_KERBEROS,
61 LDAPAuthProtocol.MULTIPLEXOR_KERBEROS,
62 LDAPAuthProtocol.MULTIPLEXOR_NTLM,
63 LDAPAuthProtocol.MULTIPLEXOR_SSL_KERBEROS,
64 LDAPAuthProtocol.MULTIPLEXOR_SSL_NTLM,
65 LDAPAuthProtocol.WSNET_NTLM,
66 LDAPAuthProtocol.WSNET_KERBEROS,
67 LDAPAuthProtocol.SSPIPROXY_NTLM,
68 LDAPAuthProtocol.SSPIPROXY_KERBEROS,
69 ]
70
71 MSLDAP_KERBEROS_PROTOCOLS = [
72 LDAPAuthProtocol.KERBEROS_RC4 ,
73 LDAPAuthProtocol.KERBEROS_NT ,
74 LDAPAuthProtocol.KERBEROS_AES ,
75 LDAPAuthProtocol.KERBEROS_PASSWORD ,
76 LDAPAuthProtocol.KERBEROS_CCACHE ,
77 LDAPAuthProtocol.KERBEROS_KEYTAB ,
78 LDAPAuthProtocol.KERBEROS_KIRBI ,
79 ]
80
81 class MSLDAPCredential:
82 """
83 Describes the user's credentials to be used for authentication during the bind operation.
84
85 :param domain: Domain of the user
86 :type domain: str
87 :param username: Username of the user
88 :type username: str
89 :param password: The authentication secret. The actual contents depend on the `auth_method`
90 :type password: str
91 :param auth_method: The ahtentication method to be performed during bind operation
92 :type auth_method: :class:`LDAPAuthProtocol`
93 :param settings: Additional settings
94 :type settings: dict
95 :param etypes: Supported encryption types for Kerberos authentication.
96 :type etypes: List[:class:`int`]
97 :param encrypt: Use protocol-level encryption. Doesnt work on LDAPS
98 :type encrypt: bool
99 """
100 def __init__(self, domain=None, username= None, password = None, auth_method = None, settings = None, etypes = None, encrypt = False):
101 self.auth_method = auth_method
102 self.domain = domain
103 self.username = username
104 self.password = password
105 self.signing_preferred = False
106 self.encryption_preferred = False
107 self.settings = settings
108 self.etypes = etypes
109 self.encrypt = encrypt
110
111 def get_msuser(self):
112 if not self.domain:
113 return self.username
114
115 return '%s\\%s' % (self.domain,self.username)
116
117 def __str__(self):
118 t = '==== MSLDAPCredential ====\r\n'
119 for k in self.__dict__:
120 t += '%s: %s\r\n' % (k, self.__dict__[k])
121
122 return t
123
0
1 #!/usr/bin/env python3
2 #
3 # Author:
4 # Tamas Jos (@skelsec)
5 #
6 import enum
7 import copy
8
9 from msldap.commons.target import MSLDAPTarget
10 from msldap.client import MSLDAPClient
11 from msldap.connection import MSLDAPClientConnection
12 from asyauth.common.credentials import UniCredential
13
14 class LDAPConnectionFactory:
15 """
16 The URL describes both the connection target and the credentials. This class creates all necessary objects to set up the client.
17
18 :param url:
19 :type url: str
20 """
21
22 help_epilog = """
23 MSLDAP URL Format: <protocol>+<auth>://<username>:<password>@<ip_or_host>:<port>/<tree>/?<param>=<value>
24 <protocol> sets the ldap protocol following values supported:
25 - ldap
26 - ldaps
27 <auth> can be omitted if plaintext authentication is to be performed (in that case it default to ntlm-password), otherwise:
28 - ntlm-password
29 - ntlm-nt
30 - kerberos-password (dc option param must be used)
31 - kerberos-rc4 / kerberos-nt (dc option param must be used)
32 - kerberos-aes (dc option param must be used)
33 - kerberos-keytab (dc option param must be used)
34 - kerberos-ccache (dc option param must be used)
35 - sspi-ntlm (windows only!)
36 - sspi-kerberos (windows only!)
37 - anonymous
38 - plain
39 - simple
40 - sicily (same format as ntlm-nt but using the SICILY authentication)
41 <tree>:
42 OPTIONAL. Specifies the root tree of all queries
43 <param> can be:
44 - timeout : connction timeout in seconds
45 - proxytype: currently only socks5 proxy is supported
46 - proxyhost: Ip or hostname of the proxy server
47 - proxyport: port of the proxy server
48 - proxytimeout: timeout in secodns for the proxy connection
49 - dc: the IP address of the domain controller, MUST be used for kerberos authentication
50 - encrypt: enable encryption. Only for NTLM. DOESNT WORK WITH LDAPS
51 - etype: Supported encryption types for Kerberos authentication. Multiple can be specified.
52 - rate: LDAP paged search query rate limit. Will sleep for seconds between each new page. Default: 0 (no limit)
53 - pagesize: LDAP paged search query size per page. Max: 1000. Default: 1000
54
55 Examples:
56 ldap://10.10.10.2 (anonymous bind)
57 ldaps://test.corp (anonymous bind)
58 ldap+sspi-ntlm://test.corp
59 ldap+sspi-kerberos://test.corp
60 ldap://TEST\\victim:<password>@10.10.10.2 (defaults to SASL GSSAPI NTLM)
61 ldap+simple://TEST\\victim:<password>@10.10.10.2 (SASL SIMPLE auth)
62 ldap+plain://TEST\\victim:<password>@10.10.10.2 (SASL SIMPLE auth)
63 ldap+ntlm-password://TEST\\victim:<password>@10.10.10.2
64 ldap+ntlm-nt://TEST\\victim:<nthash>@10.10.10.2
65 ldap+kerberos-password://TEST\\victim:<password>@10.10.10.2
66 ldap+kerberos-rc4://TEST\\victim:<rc4key>@10.10.10.2
67 ldap+kerberos-aes://TEST\\victim:<aes>@10.10.10.2
68 ldap://TEST\\victim:[email protected]/DC=test,DC=corp/
69 ldap://TEST\\victim:[email protected]/DC=test,DC=corp/?timeout=99&proxytype=socks5&proxyhost=127.0.0.1&proxyport=1080&proxytimeout=44
70 """
71
72 def __init__(self, credential:UniCredential = None, target:MSLDAPTarget = None ):
73 self.credential = credential
74 self.target = target
75
76 @staticmethod
77 def from_url(connection_url):
78 target = MSLDAPTarget.from_url(connection_url)
79 credential = UniCredential.from_url(connection_url)
80 return LDAPConnectionFactory(credential, target)
81
82 def get_credential(self) -> UniCredential:
83 """
84 Creates a credential object
85
86 :return: Credential object
87 :rtype: :class:`UniCredential`
88 """
89 return copy.deepcopy(self.credential)
90
91 def get_target(self) -> MSLDAPTarget:
92 """
93 Creates a target object
94
95 :return: Target object
96 :rtype: :class:`MSLDAPTarget`
97 """
98 return copy.deepcopy(self.target)
99
100 def get_client(self) -> MSLDAPClient:
101 """
102 Creates a client that can be used to interface with the server
103
104 :return: LDAP client
105 :rtype: :class:`MSLDAPClient`
106 """
107 cred = self.get_credential()
108 target = self.get_target()
109 return MSLDAPClient(target, cred)
110
111
112 def get_connection(self) -> MSLDAPClientConnection:
113 """
114 Creates a connection that can be used to interface with the server
115
116 :return: LDAP connection
117 :rtype: :class:`MSLDAPClientConnection`
118 """
119 cred = self.get_credential()
120 target = self.get_target()
121 return MSLDAPClientConnection(target, cred)
122
123 def __str__(self):
124 t = '==== LDAPConnectionFactory ====\r\n'
125 for k in self.__dict__:
126 val = self.__dict__[k]
127 if isinstance(val, enum.IntFlag):
128 val = val
129 elif isinstance(val, enum.Enum):
130 val = val.name
131
132 t += '%s: %s\r\n' % (k, str(val))
133
134 return t
135
136 if __name__ == '__main__':
137 url_tests = [
138 'ldap://10.10.10.2',
139 'ldap://10.10.10.2:9999',
140 'ldap://test:[email protected]',
141 'ldap://domain\\[email protected]',
142 'ldap://domain\\test:[email protected]:9999',
143 'ldap://domain\\test:[email protected]:9999',
144 'ldaps+sspi-ntlm://10.10.10.2',
145 'ldaps+sspi-kerberos://10.10.10.2',
146 'ldaps+ntlm-password://domain\\test:[email protected]:9999',
147 'ldaps+ntlm-nt://domain\\test:[email protected]:9999',
148 'ldaps+kerberos-password://domain\\test:[email protected]:9999',
149 'ldaps://10.10.10.2:9999',
150 'ldaps://test:[email protected]',
151 'ldaps://domain\\[email protected]',
152 'ldaps://domain\\test:[email protected]:9999',
153 'ldaps://DOMAIN\\test:[email protected]:9999/?proxytype=socks5&proxyserver=127.0.0.1',
154 'ldaps://DOMAIN\\test:[email protected]:9999/?proxytype=socks5&proxyserver=127.0.0.1&proxyuser=admin&proxypass=alma',
155 'ldaps://DOMAIN\\test:[email protected]:9999/?proxytype=multiplexor&proxyserver=127.0.0.1&proxyport=9999&proxyuser=admin&proxypass=alma',
156 'ldaps://10.10.10.2',
157 'ldaps://10.10.10.2:6666',
158 ]
159 for url in url_tests:
160 print('===========================================================================')
161 print(url)
162 try:
163 dec = LDAPConnectionFactory.from_url(url)
164 creds = dec.get_credential()
165 target = dec.get_target()
166 except Exception as e:
167 import traceback
168 traceback.print_exc()
169 print('ERROR! Reason: %s' % e)
170 input()
171 else:
172 print(str(creds))
173 print(str(target))
174 input()
+0
-145
msldap/commons/proxy.py less more
0
1 #!/usr/bin/env python3
2 #
3 # Author:
4 # Tamas Jos (@skelsec)
5 #
6
7 import enum
8 from urllib.parse import urlparse, parse_qs
9
10 from asysocks.common.clienturl import SocksClientURL
11
12 class MSLDAPProxyType(enum.Enum):
13 SOCKS4 = 'SOCKS4'
14 SOCKS4_SSL = 'SOCKS4_SSL'
15 SOCKS5 = 'SOCKS5'
16 SOCKS5_SSL = 'SOCKS5_SSL'
17 MULTIPLEXOR = 'MULTIPLEXOR'
18 MULTIPLEXOR_SSL = 'MULTIPLEXOR_SSL'
19 WSNET = 'WSNET'
20 WSNETWS = 'WSNETWS'
21 WSNETWSS = 'WSNETWSS'
22
23 class MSLDAPProxy:
24 """
25 Describes the proxy to be used when connecting to the server. Used as a parameter to the `MSLDAPTarget` object
26
27 :param type: Specifies the proxy type
28 :type type: :class:`MSLDAPProxyType`
29 :param target:
30 :type target:
31 :param auth: Specifies the proxy authentication if any
32 :type auth:
33 """
34 def __init__(self, type = None, target = None, auth = None):
35 self.type = type
36 self.target = target
37 self.auth = auth
38
39
40 @staticmethod
41 def from_params(url_str):
42 """
43 Creates a proxy object from the parameters found in an LDAP URL string
44
45 :param type: url_str
46 :type type: str
47 :return: The proxy object
48 :rtype: :class:`MSLDAPProxy`
49 """
50 proxy = MSLDAPProxy()
51 url = urlparse(url_str)
52 if url.query is None:
53 return None
54
55 query = parse_qs(url.query)
56 if 'proxytype' not in query and 'sametype' not in query:
57 return None
58
59 proxy.type = MSLDAPProxyType(query['proxytype'][0].upper())
60 if proxy.type in [MSLDAPProxyType.WSNET, MSLDAPProxyType.WSNETWS, MSLDAPProxyType.WSNETWSS,MSLDAPProxyType.SOCKS4, MSLDAPProxyType.SOCKS4_SSL, MSLDAPProxyType.SOCKS5, MSLDAPProxyType.SOCKS5_SSL]:
61 proxy.target = SocksClientURL.from_params(url_str)
62 else:
63 proxy.target = MSLDAPMultiplexorProxy.from_params(url_str)
64
65 return proxy
66
67 def __str__(self):
68 t = '==== MSLDAPProxy ====\r\n'
69 for k in self.__dict__:
70 t += '%s: %s\r\n' % (k, self.__dict__[k])
71
72 return t
73
74
75 class MSLDAPMultiplexorProxy:
76 def __init__(self):
77 self.ip = None
78 self.port = None
79 self.timeout = 10
80 self.type = MSLDAPProxyType.MULTIPLEXOR
81 self.username = None
82 self.password = None
83 self.domain = None
84 self.agent_id = None
85 self.virtual_socks_port = None
86 self.virtual_socks_ip = None
87
88 def sanity_check(self):
89 if self.ip is None:
90 raise Exception('MULTIPLEXOR server IP is missing!')
91 if self.port is None:
92 raise Exception('MULTIPLEXOR server port is missing!')
93 if self.agent_id is None:
94 raise Exception('MULTIPLEXOR proxy requires agentid to be set!')
95
96 def get_server_url(self):
97 con_str = 'ws://%s:%s' % (self.ip, self.port)
98 if self.type == MSLDAPProxyType.MULTIPLEXOR_SSL:
99 con_str = 'wss://%s:%s' % (self.ip, self.port)
100 return con_str
101
102 @staticmethod
103 def from_params(url_str):
104 res = MSLDAPMultiplexorProxy()
105 url = urlparse(url_str)
106 res.endpoint_ip = url.hostname
107 if url.port:
108 res.endpoint_port = int(url.port)
109 if url.query is not None:
110 query = parse_qs(url.query)
111
112 for k in query:
113 if k.startswith('proxy'):
114 if k[5:] in multiplexorproxyurl_param2var:
115
116 data = query[k][0]
117 for c in multiplexorproxyurl_param2var[k[5:]][1]:
118 data = c(data)
119
120 setattr(
121 res,
122 multiplexorproxyurl_param2var[k[5:]][0],
123 data
124 )
125 res.sanity_check()
126
127 return res
128
129 def stru(x):
130 return str(x).upper()
131
132 multiplexorproxyurl_param2var = {
133 'type' : ('version', [stru, MSLDAPProxyType]),
134 'host' : ('ip', [str]),
135 'port' : ('port', [int]),
136 'timeout': ('timeout', [int]),
137 'user' : ('username', [str]),
138 'pass' : ('password', [str]),
139 #'authtype' : ('authtype', [SOCKS5Method]),
140 'agentid' : ('agent_id', [str]),
141 'domain' : ('domain', [str])
142
143 }
144
44 # Tamas Jos (@skelsec)
55 #
66
7 import enum
7 from os import stat
8 from sqlite3 import connect
9 from asysocks.unicomm.common.target import UniTarget, UniProto
10 from urllib.parse import urlparse, parse_qs
11 from asysocks.unicomm.utils.paramprocessor import str_one, int_one, bool_one
812
9 import platform
10 try:
11 import ssl
12 except:
13 if platform.system() == 'Emscripten':
14 pass
15
16 class LDAPProtocol(enum.Enum):
17 TCP = 'TCP'
18 UDP = 'UDP'
19 SSL = 'SSL'
13 msldaptarget_url_params = {
14 'pagesize' : int_one,
15 'rate' : int_one,
16 }
2017
2118
22 class MSLDAPTarget:
19 class MSLDAPTarget(UniTarget):
2320 """
2421 Describes the connection to the server.
2522
2825 :param port: port of the LDAP service running on the server
2926 :type port: int
3027 :param proto: Connection protocol to be used
31 :type proto: :class:`LDAPProtocol`
28 :type proto: :class:`UniProto`
3229 :param tree: The tree to connect to
3330 :type tree: str
34 :param proxy: specifies what kind of proxy to be used
35 :type proxy: :class:`MSLDAPProxy`
31 :param proxies: specifies what kind of proxy to be used
32 :type proxies: :class:`List[UniProxyTarget]`
3633 :param timeout: connection timeout in seconds
3734 :type timeout: int
3835 :param ldap_query_page_size: Maximum number of elements to fetch in each paged_query call.
3936 :type ldap_query_page_size: int
4037 :param ldap_query_ratelimit: rate limit of paged queries. This will cause a sleep (in seconds) between fetching of each page of the query
4138 :type ldap_query_ratelimit: float
39 :param dc_ip: Ip address of the kerberos server (if kerberos is used)
40 :type dc_ip: str
4241 """
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):
44 self.proto = proto
45 self.host = host
42 def __init__(self, ip, port = 389, protocol = UniProto.CLIENT_TCP, tree = None, proxies = None, timeout = 10, ldap_query_page_size = 1000, ldap_query_ratelimit = 0, dns:str=None, dc_ip:str = None, domain:str = None, hostname:str = None):
43 UniTarget.__init__(self, ip, port, protocol, timeout, hostname = hostname, proxies = proxies, domain = domain, dc_ip = dc_ip, dns=dns)
4644 self.tree = tree
47 self.port = port
48 self.proxy = proxy
49 self.timeout = timeout
50 self.dc_ip = None
51 self.serverip = None
52 self.domain = None
53 self.sslctx = None
5445 self.ldap_query_page_size = ldap_query_page_size
5546 self.ldap_query_ratelimit = ldap_query_ratelimit
56
57 def get_ssl_context(self):
58 if self.proto == LDAPProtocol.SSL:
59 if self.sslctx is None:
60 # TODO ssl verification :)
61 self.sslctx = ssl._create_unverified_context()
62 #self.sslctx.verify = False
63 return self.sslctx
64 return None
65
47
6648 def to_target_string(self):
67 return 'ldap/%s@%s' % (self.host,self.domain) #ldap/WIN2019AD.test.corp @ TEST.CORP
49 return 'ldap/%s@%s' % (self.get_hostname_or_ip(), self.domain) #ldap/WIN2019AD.test.corp @ TEST.CORP
6850
6951 def get_host(self):
70 return '%s://%s:%s' % (self.proto, self.host, self.port)
52 if self.protocol == UniProto.CLIENT_SSL_TCP:
53 proto = 'ldaps'
54 elif self.protocol == UniProto.CLIENT_TCP:
55 proto = 'ldap'
56 return '%s://%s:%s' % (proto, self.get_hostname_or_ip(), self.port)
7157
7258 def is_ssl(self):
73 return self.proto == LDAPProtocol.SSL
59 return self.protocol == UniProto.CLIENT_SSL_TCP
60
61 @staticmethod
62 def from_url(connection_url):
63 url_e = urlparse(connection_url)
64 schemes = []
65 for item in url_e.scheme.upper().split('+'):
66 schemes.append(item.replace('-','_'))
67 if schemes[0] == 'LDAP':
68 protocol = UniProto.CLIENT_TCP
69 port = 389
70 elif schemes[0] == 'LDAPS':
71 protocol = UniProto.CLIENT_SSL_TCP
72 port = 636
73 elif schemes[0] == 'LDAP_SSL':
74 protocol = UniProto.CLIENT_SSL_TCP
75 port = 636
76 elif schemes[0] == 'LDAP_TCP':
77 protocol = UniProto.CLIENT_TCP
78 port= 389
79 elif schemes[0] == 'LDAP_UDP':
80 raise NotImplementedError()
81 protocol = UniProto.CLIENT_UDP
82 port = 389
83 else:
84 raise Exception('Unknown protocol! %s' % schemes[0])
85
86 if url_e.port:
87 port = url_e.port
88 if port is None:
89 raise Exception('Port must be provided!')
90
91 path = None
92 if url_e.path not in ['/', '', None]:
93 path = url_e.path
94
95 unitarget, extraparams = UniTarget.from_url(connection_url, protocol, port, msldaptarget_url_params)
96 pagesize = extraparams['pagesize'] if extraparams['pagesize'] is not None else 1000
97 rate = extraparams['rate'] if extraparams['rate'] is not None else 0
98
99 target = MSLDAPTarget(
100 unitarget.ip,
101 port = unitarget.port,
102 protocol = unitarget.protocol,
103 tree = path,
104 proxies = unitarget.proxies,
105 timeout = unitarget.timeout,
106 ldap_query_page_size = pagesize,
107 ldap_query_ratelimit = rate,
108 dns = unitarget.dns,
109 dc_ip = unitarget.dc_ip,
110 domain = unitarget.domain,
111 hostname = unitarget.hostname
112 )
113 return target
114
74115
75116 def __str__(self):
76117 t = '==== MSLDAPTarget ====\r\n'
+0
-416
msldap/commons/url.py less more
0
1 #!/usr/bin/env python3
2 #
3 # Author:
4 # Tamas Jos (@skelsec)
5 #
6
7 import platform
8 import hashlib
9 import getpass
10 import base64
11 import enum
12 from urllib.parse import urlparse, parse_qs
13
14 from msldap.commons.credential import MSLDAPCredential, LDAPAuthProtocol, MSLDAP_KERBEROS_PROTOCOLS
15 from msldap.commons.target import MSLDAPTarget, LDAPProtocol
16 from msldap.commons.proxy import MSLDAPProxy, MSLDAPProxyType
17 from msldap.client import MSLDAPClient
18 from msldap.connection import MSLDAPClientConnection
19
20 class PLAINTEXTSCHEME(enum.Enum):
21 """
22 Additional conveinence functions.
23 """
24 SIMPLE_PROMPT = 'SIMPLE_PROMPT'
25 SIMPLE_HEX = 'SIMPLE_HEX'
26 SIMPLE_B64 = 'SIMPLE_B64'
27 PLAIN_PROMPT = 'PLAIN_PROMPT'
28 PLAIN_HEX = 'PLAIN_HEX'
29 PLAIN_B64 = 'PLAIN_B64'
30 SICILY_PROMPT = 'SICILY_PROMPT'
31 SICILY_HEX = 'SICILY_HEX'
32 SICILY_B64 = 'SICILY_B64'
33 NTLM_PROMPT = 'NTLM_PROMPT'
34 NTLM_HEX = 'NTLM_HEX'
35 NTLM_B64 = 'NTLM_B64'
36
37 class MSLDAPURLDecoder:
38 """
39 The URL describes both the connection target and the credentials. This class creates all necessary objects to set up the client.
40
41 :param url:
42 :type url: str
43 """
44
45 help_epilog = """
46 MSLDAP URL Format: <protocol>+<auth>://<username>:<password>@<ip_or_host>:<port>/<tree>/?<param>=<value>
47 <protocol> sets the ldap protocol following values supported:
48 - ldap
49 - ldaps
50 <auth> can be omitted if plaintext authentication is to be performed (in that case it default to ntlm-password), otherwise:
51 - ntlm-password
52 - ntlm-nt
53 - kerberos-password (dc option param must be used)
54 - kerberos-rc4 / kerberos-nt (dc option param must be used)
55 - kerberos-aes (dc option param must be used)
56 - kerberos-keytab (dc option param must be used)
57 - kerberos-ccache (dc option param must be used)
58 - sspi-ntlm (windows only!)
59 - sspi-kerberos (windows only!)
60 - anonymous
61 - plain
62 - simple
63 - sicily (same format as ntlm-nt but using the SICILY authentication)
64 <tree>:
65 OPTIONAL. Specifies the root tree of all queries
66 <param> can be:
67 - timeout : connction timeout in seconds
68 - proxytype: currently only socks5 proxy is supported
69 - proxyhost: Ip or hostname of the proxy server
70 - proxyport: port of the proxy server
71 - proxytimeout: timeout in secodns for the proxy connection
72 - dc: the IP address of the domain controller, MUST be used for kerberos authentication
73 - encrypt: enable encryption. Only for NTLM. DOESNT WORK WITH LDAPS
74 - etype: Supported encryption types for Kerberos authentication. Multiple can be specified.
75 - rate: LDAP paged search query rate limit. Will sleep for seconds between each new page. Default: 0 (no limit)
76 - pagesize: LDAP paged search query size per page. Max: 1000. Default: 1000
77
78 Examples:
79 ldap://10.10.10.2 (anonymous bind)
80 ldaps://test.corp (anonymous bind)
81 ldap+sspi-ntlm://test.corp
82 ldap+sspi-kerberos://test.corp
83 ldap://TEST\\victim:<password>@10.10.10.2 (defaults to SASL GSSAPI NTLM)
84 ldap+simple://TEST\\victim:<password>@10.10.10.2 (SASL SIMPLE auth)
85 ldap+plain://TEST\\victim:<password>@10.10.10.2 (SASL SIMPLE auth)
86 ldap+ntlm-password://TEST\\victim:<password>@10.10.10.2
87 ldap+ntlm-nt://TEST\\victim:<nthash>@10.10.10.2
88 ldap+kerberos-password://TEST\\victim:<password>@10.10.10.2
89 ldap+kerberos-rc4://TEST\\victim:<rc4key>@10.10.10.2
90 ldap+kerberos-aes://TEST\\victim:<aes>@10.10.10.2
91 ldap://TEST\\victim:[email protected]/DC=test,DC=corp/
92 ldap://TEST\\victim:[email protected]/DC=test,DC=corp/?timeout=99&proxytype=socks5&proxyhost=127.0.0.1&proxyport=1080&proxytimeout=44
93 """
94
95 def __init__(self, url):
96 self.url = url
97 self.ldap_scheme = None
98 self.auth_scheme = None
99
100 self.domain = None
101 self.username = None
102 self.password = None
103 self.encrypt = False
104 self.auth_settings = {}
105 self.etypes = None
106
107 self.ldap_proto = None
108 self.ldap_host = None
109 self.ldap_port = 389
110 self.ldap_tree = None
111 self.target_timeout = 5
112 self.target_pagesize = 1000
113 self.target_ratelimit = 0
114 self.dc_ip = None
115 self.serverip = None
116 self.proxy = None
117
118 self.__pwpreprocess = None
119
120 self.parse()
121
122
123 def get_credential(self):
124 """
125 Creates a credential object
126
127 :return: Credential object
128 :rtype: :class:`MSLDAPCredential`
129 """
130 t = MSLDAPCredential(
131 domain=self.domain,
132 username=self.username,
133 password = self.password,
134 auth_method=self.auth_scheme,
135 settings = self.auth_settings
136 )
137 t.encrypt = self.encrypt
138 t.etypes = self.etypes
139
140 return t
141
142 def get_target(self):
143 """
144 Creates a target object
145
146 :return: Target object
147 :rtype: :class:`MSLDAPTarget`
148 """
149 target = MSLDAPTarget(
150 self.ldap_host,
151 port = self.ldap_port,
152 proto = self.ldap_scheme,
153 tree=self.ldap_tree,
154 timeout = self.target_timeout,
155 ldap_query_page_size = self.target_pagesize,
156 ldap_query_ratelimit = self.target_ratelimit
157 )
158 target.domain = self.domain
159 target.dc_ip = self.dc_ip
160 target.proxy = self.proxy
161 target.serverip = self.serverip
162 return target
163
164 def get_client(self):
165 """
166 Creates a client that can be used to interface with the server
167
168 :return: LDAP client
169 :rtype: :class:`MSLDAPClient`
170 """
171 cred = self.get_credential()
172 target = self.get_target()
173 return MSLDAPClient(target, cred)
174
175 def get_connection(self):
176 """
177 Creates a connection that can be used to interface with the server
178
179 :return: LDAP connection
180 :rtype: :class:`MSLDAPClientConnection`
181 """
182 cred = self.get_credential()
183 target = self.get_target()
184 return MSLDAPClientConnection(target, cred)
185
186 def scheme_decoder(self, scheme):
187 schemes = []
188 for item in scheme.upper().split('+'):
189 schemes.append(item.replace('-','_'))
190
191 if schemes[0] == 'LDAP':
192 self.ldap_scheme = LDAPProtocol.TCP
193 self.ldap_port = 389
194 elif schemes[0] == 'LDAPS':
195 self.ldap_scheme = LDAPProtocol.SSL
196 self.ldap_port = 636
197 elif schemes[0] == 'LDAP_SSL':
198 self.ldap_scheme = LDAPProtocol.SSL
199 self.ldap_port = 636
200 elif schemes[0] == 'LDAP_TCP':
201 self.ldap_scheme = LDAPProtocol.TCP
202 self.ldap_port = 389
203 elif schemes[0] == 'LDAP_UDP':
204 self.ldap_scheme = LDAPProtocol.UDP
205 self.ldap_port = 389
206 else:
207 raise Exception('Unknown protocol! %s' % schemes[0])
208
209 if len(schemes) == 1:
210 return
211
212 try:
213 x = PLAINTEXTSCHEME(schemes[1])
214 if x == PLAINTEXTSCHEME.SIMPLE_PROMPT:
215 self.auth_scheme = LDAPAuthProtocol.SIMPLE
216 self.__pwpreprocess = 'PROMPT'
217
218 if x == PLAINTEXTSCHEME.SIMPLE_HEX:
219 self.auth_scheme = LDAPAuthProtocol.SIMPLE
220 self.__pwpreprocess = 'HEX'
221
222 if x == PLAINTEXTSCHEME.SIMPLE_B64:
223 self.auth_scheme = LDAPAuthProtocol.SIMPLE
224 self.__pwpreprocess = 'B64'
225
226 if x == PLAINTEXTSCHEME.PLAIN_PROMPT:
227 self.auth_scheme = LDAPAuthProtocol.PLAIN
228 self.__pwpreprocess = 'PROMPT'
229
230 if x == PLAINTEXTSCHEME.PLAIN_HEX:
231 self.auth_scheme = LDAPAuthProtocol.PLAIN
232 self.__pwpreprocess = 'HEX'
233
234 if x == PLAINTEXTSCHEME.PLAIN_B64:
235 self.auth_scheme = LDAPAuthProtocol.PLAIN
236 self.__pwpreprocess = 'B64'
237
238 if x == PLAINTEXTSCHEME.SICILY_PROMPT:
239 self.auth_scheme = LDAPAuthProtocol.SICILY
240 self.__pwpreprocess = 'PROMPT'
241
242 if x == PLAINTEXTSCHEME.SICILY_HEX:
243 self.auth_scheme = LDAPAuthProtocol.SICILY
244 self.__pwpreprocess = 'HEX'
245
246 if x == PLAINTEXTSCHEME.SICILY_B64:
247 self.auth_scheme = LDAPAuthProtocol.SICILY
248 self.__pwpreprocess = 'B64'
249
250 if x == PLAINTEXTSCHEME.NTLM_PROMPT:
251 self.auth_scheme = LDAPAuthProtocol.NTLM_PASSWORD
252 self.__pwpreprocess = 'PROMPT'
253
254 if x == PLAINTEXTSCHEME.NTLM_HEX:
255 self.auth_scheme = LDAPAuthProtocol.NTLM_PASSWORD
256 self.__pwpreprocess = 'HEX'
257
258 if x == PLAINTEXTSCHEME.NTLM_B64:
259 self.auth_scheme = LDAPAuthProtocol.NTLM_PASSWORD
260 self.__pwpreprocess = 'B64'
261 except:
262 try:
263 self.auth_scheme = LDAPAuthProtocol(schemes[1])
264 except:
265 raise Exception('Uknown scheme!')
266
267 return
268
269 def parse(self):
270 url_e = urlparse(self.url)
271 self.scheme_decoder(url_e.scheme)
272
273 self.password = url_e.password
274 if self.__pwpreprocess is not None:
275 if self.__pwpreprocess == 'PROMPT':
276 self.password = getpass.getpass()
277
278 elif self.__pwpreprocess == 'HEX':
279 self.password = bytes.fromhex(self.password).decode()
280
281 elif self.__pwpreprocess == 'B64':
282 self.password = base64.b64decode(self.password).decode()
283
284 else:
285 raise Exception('Unknown password preprocess directive %s' % self.__pwpreprocess)
286
287
288 if url_e.username is not None:
289 if url_e.username.find('\\') != -1:
290 self.domain , self.username = url_e.username.split('\\')
291 else:
292 self.domain = None
293 self.username = url_e.username
294
295 #defaulting schemes...
296 if self.auth_scheme is None:
297 if self.username is not None and self.domain is not None and self.password is not None:
298 #tricky parsing to make user feel confortable...
299 if len(self.password) == 32:
300 try:
301 bytes.fromhex(self.password)
302 self.auth_scheme = LDAPAuthProtocol.NTLM_NT
303 except:
304 self.auth_scheme = LDAPAuthProtocol.NTLM_PASSWORD
305 else:
306 self.auth_scheme = LDAPAuthProtocol.NTLM_PASSWORD
307 else:
308 self.auth_scheme = LDAPAuthProtocol.SIMPLE
309
310 self.ldap_host = url_e.hostname
311 if url_e.port is not None:
312 self.ldap_port = int(url_e.port)
313
314 if url_e.path is not None:
315 tree = url_e.path.replace('/','')
316 if tree != '':
317 self.ldap_tree = tree
318
319 proxy_present = False
320 if url_e.query is not None:
321 query = parse_qs(url_e.query)
322 if 'etype' in query:
323 self.etypes = []
324 for k in query:
325 if k.startswith('proxy') is True:
326 proxy_present = True
327 if k == 'dc':
328 self.dc_ip = query[k][0]
329 elif k == 'timeout':
330 self.timeout = int(query[k][0])
331 elif k == 'serverip':
332 self.serverip = query[k][0]
333 elif k == 'dns':
334 self.dns = query[k] #multiple dns can be set, so not trimming here
335 elif k == 'encrypt':
336 self.encrypt = bool(int(query[k][0]))
337 elif k == 'etype':
338 self.etypes = [int(x) for x in query[k]]
339 elif k.startswith('auth'):
340 self.auth_settings[k[len('auth'):]] = query[k]
341 elif k == 'rate':
342 self.target_ratelimit = float(query[k][0])
343 elif k == 'pagesize':
344 self.target_pagesize = int(query[k][0])
345 #elif k.startswith('same'):
346 # self.auth_settings[k[len('same'):]] = query[k]
347
348 if proxy_present is True:
349 self.proxy = MSLDAPProxy.from_params(self.url)
350
351 if self.auth_scheme in [LDAPAuthProtocol.SSPI_NTLM, LDAPAuthProtocol.SSPI_KERBEROS]:
352 if platform.system().upper() != 'WINDOWS':
353 raise Exception('SSPI auth only works on Windows!')
354 if self.username is None:
355 self.username = '<CURRENT>'
356 if self.password is None:
357 self.password = '<CURRENT>'
358 if self.domain is None:
359 self.domain = '<CURRENT>'
360
361 if self.auth_scheme in MSLDAP_KERBEROS_PROTOCOLS and self.dc_ip is None:
362 raise Exception('The "dc" parameter MUST be used for kerberos authentication types!')
363
364
365 # if self.proxy_scheme in [LDAPProxyType.MULTIPLEXOR, LDAPProxyType.MULTIPLEXOR_SSL]:
366 # if self.proxy_port is None:
367 # self.proxy_port = 9999
368 #
369 # if self.proxy_scheme in [LDAPProxyType.MULTIPLEXOR, LDAPProxyType.MULTIPLEXOR_SSL]:
370 # if 'agentid' not in self.proxy_settings:
371 # raise Exception('multiplexor proxy reuires agentid to be set! Set it via proxyagentid parameter!')
372 #
373
374
375
376
377 if __name__ == '__main__':
378 url_tests = [
379 'ldap://10.10.10.2',
380 'ldap://10.10.10.2:9999',
381 'ldap://test:[email protected]',
382 'ldap://domain\\[email protected]',
383 'ldap://domain\\test:[email protected]:9999',
384 'ldap://domain\\test:[email protected]:9999',
385 'ldaps+sspi-ntlm://10.10.10.2',
386 'ldaps+sspi-kerberos://10.10.10.2',
387 'ldaps+ntlm-password://domain\\test:[email protected]:9999',
388 'ldaps+ntlm-nt://domain\\test:[email protected]:9999',
389 'ldaps+kerberos-password://domain\\test:[email protected]:9999',
390 'ldaps://10.10.10.2:9999',
391 'ldaps://test:[email protected]',
392 'ldaps://domain\\[email protected]',
393 'ldaps://domain\\test:[email protected]:9999',
394 'ldaps://DOMAIN\\test:[email protected]:9999/?proxytype=socks5&proxyserver=127.0.0.1',
395 'ldaps://DOMAIN\\test:[email protected]:9999/?proxytype=socks5&proxyserver=127.0.0.1&proxyuser=admin&proxypass=alma',
396 'ldaps://DOMAIN\\test:[email protected]:9999/?proxytype=multiplexor&proxyserver=127.0.0.1&proxyport=9999&proxyuser=admin&proxypass=alma',
397 'ldaps://10.10.10.2',
398 'ldaps://10.10.10.2:6666',
399 ]
400 for url in url_tests:
401 print('===========================================================================')
402 print(url)
403 try:
404 dec = MSLDAPURLDecoder(url)
405 creds = dec.get_credential()
406 target = dec.get_target()
407 except Exception as e:
408 import traceback
409 traceback.print_exc()
410 print('ERROR! Reason: %s' % e)
411 input()
412 else:
413 print(str(creds))
414 print(str(target))
415 input()
00
11 import datetime
22
3 def timestamp2datetime(dt):
3 def timestamp2datetime(dt) -> datetime.datetime:
44 """
55 Converting Windows timestamps to datetime.datetime format
66 :param dt: Windows timestamp as array of bytes
1111 return datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds=us)
1212
1313
14 def datetime2timestamp(dt):
14 def datetime2timestamp(dt) -> int:
1515 delta = dt - datetime.datetime(1601, 1, 1)
1616 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) -> str:
20 return [s[i:i + w] for i in range(0, len(s), w)]
21
22 def print_cert(cert, offset=0) -> str:
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 }
22
33 from msldap import logger
44 from msldap.commons.common import MSLDAPClientStatus
5 from .commons.target import MSLDAPTarget
56 from msldap.protocol.messages import LDAPMessage, BindRequest, \
67 protocolOp, AuthenticationChoice, SaslCredentials, \
78 SearchRequest, AttributeDescription, Filter, Filters, \
89 Controls, Control, SearchControlValue, AddRequest, \
9 ModifyRequest, DelRequest
10 ModifyRequest, DelRequest, ExtendedRequest, ExtendedResponse
1011
1112 from msldap.protocol.utils import calcualte_length
1213 from msldap.protocol.typeconversion import convert_result, convert_attributes, encode_attributes, encode_changes
1314 from msldap.protocol.query import escape_filter_chars, query_syntax_converter
14 from msldap.commons.authbuilder import AuthenticatorBuilder
15 from msldap.commons.credential import MSLDAP_GSS_METHODS
16 from msldap.network.selector import MSLDAPNetworkSelector
17 from msldap.commons.credential import LDAPAuthProtocol
18 from msldap.commons.target import LDAPProtocol
19 from msldap.commons.exceptions import LDAPServerException, LDAPBindException, LDAPAddException, LDAPModifyException, LDAPDeleteException
20 from asn1crypto.x509 import Certificate
15 from msldap.commons.authbuilder import get_auth_context
16 from msldap.network.packetizer import LDAPPacketizer
17 from asysocks.unicomm.common.target import UniProto
18 from msldap.commons.exceptions import LDAPBindException, LDAPAddException, LDAPModifyException, LDAPDeleteException
2119 from hashlib import sha256
22 from minikerberos.gssapi.channelbindings import ChannelBindingsStruct
20 from asysocks.unicomm.client import UniClient
21 from asyauth.common.constants import asyauthProtocol
22 from asyauth.common.credentials import UniCredential
23 from asyauth.common.winapi.constants import ISC_REQ
2324
2425 class MSLDAPClientConnection:
25 def __init__(self, target, creds):
26 if target is None:
27 raise Exception('Target cant be none!')
26 def __init__(self, target:MSLDAPTarget, credential:UniCredential, auth=None):
2827 self.target = target
29 self.creds = creds
30 self.auth = AuthenticatorBuilder(self.creds, self.target).build()
28 self.credential = credential
29 if auth is not None:
30 self.auth = auth
31 else:
32 self.auth = get_auth_context(self.credential)
33
3134 self.connected = False
3235 self.bind_ok = False
36 self.is_anon = False
3337 self.__sign_messages = False
3438 self.__encrypt_messages = False
3539 self.network = None
5256
5357 async def __handle_incoming(self):
5458 try:
55 while True:
56 message_data, err = await self.network.in_queue.get()
57 if err is not None:
58 logger.debug('Client terminating bc __handle_incoming got an error!')
59 raise err
60
59 async for message_data in self.network.read():
6160 #print('Incoming message data: %s' % message_data)
6261 if self.bind_ok is True:
6362 if self.__encrypt_messages is True:
145144 self.encryption_sequence_counter += 1
146145
147146 self.message_table_notify[curr_msg_id] = asyncio.Event()
148 await self.network.out_queue.put(message_data)
147 await self.network.write(message_data)
149148
150149 return curr_msg_id
151150
175174 """
176175 try:
177176 logger.debug('Connecting!')
178 self.network = await MSLDAPNetworkSelector.select(self.target)
179 res, err = await self.network.run()
180 if res is False:
181 return False, err
177 packetizer = LDAPPacketizer()
178 client = UniClient(self.target, packetizer)
179 self.network = await client.connect()
182180
183181 # now processing channel binding options
184 if self.target.proto == LDAPProtocol.SSL:
182 if self.target.protocol == UniProto.CLIENT_SSL_TCP:
185183 certdata = self.network.get_peer_certificate()
186 #cert = Certificate.load(certdata).native
187 #print(cert)
188 cb_struct = ChannelBindingsStruct()
189 cb_struct.application_data = b'tls-server-end-point:' + sha256(certdata).digest()
190
191 self.cb_data = cb_struct.to_bytes()
184 self.cb_data = b'tls-server-end-point:' + sha256(certdata).digest()
192185
193186 self.handle_incoming_task = asyncio.create_task(self.__handle_incoming())
194187 logger.debug('Connection succsessful!')
206199
207200 logger.debug('Disconnecting!')
208201 self.bind_ok = False
202 if self.network is not None:
203 await self.network.close()
204 await asyncio.sleep(0)
205
209206 if self.handle_incoming_task is not None:
210207 self.handle_incoming_task.cancel()
211 if self.network is not None:
212 await self.network.terminate()
213208
214209
215210 def __bind_success(self):
219214 """
220215 logger.debug('BIND Success!')
221216 self.bind_ok = True
222 if self.creds.auth_method in MSLDAP_GSS_METHODS or self.creds.auth_method == LDAPAuthProtocol.SICILY:
217 if self.credential.protocol in [asyauthProtocol.NTLM, asyauthProtocol.KERBEROS, asyauthProtocol.SICILY]:
223218 self.__sign_messages = self.auth.signing_needed()
224219 self.__encrypt_messages = self.auth.encryption_needed()
225220 if self.__encrypt_messages or self.__sign_messages:
226 self.network.is_plain_msg = False
221 self.network.packetizer.is_plain_msg = False
227222
228223 async def bind(self):
229224 """
235230 """
236231 logger.debug('BIND in progress...')
237232 try:
238 if self.creds.auth_method == LDAPAuthProtocol.SICILY:
239
240 data, to_continue, err = await self.auth.authenticate(None)
233 if self.credential.protocol == asyauthProtocol.SICILY:
234 flags = ISC_REQ.CONNECTION|ISC_REQ.CONFIDENTIALITY|ISC_REQ.INTEGRITY
235 if self.target.protocol == UniProto.CLIENT_SSL_TCP:
236 flags = ISC_REQ.CONNECTION
237 data, to_continue, err = await self.auth.authenticate(None, spn=self.target.to_target_string(), flags=flags, cb_data = self.cb_data)
241238 if err is not None:
242239 return None, err
243240
291288 res['protocolOp']['diagnosticMessage']
292289 )
293290
294 data, to_continue, err = await self.auth.authenticate(res['protocolOp']['matchedDN'])
291 data, to_continue, err = await self.auth.authenticate(res['protocolOp']['matchedDN'], spn=self.target.to_target_string(), cb_data = self.cb_data)
295292 if err is not None:
296293 return None, err
297294
324321 self.__bind_success()
325322 return True, None
326323
327 elif self.creds.auth_method == LDAPAuthProtocol.SIMPLE:
324 elif self.credential.protocol == asyauthProtocol.SIMPLE:
328325 pw = b''
329 if self.auth.password != None:
330 pw = self.auth.password.encode()
326 if self.auth.secret != None:
327 pw = self.auth.secret.encode()
331328
332329 user = b''
333330 if self.auth.username != None:
334331 user = self.auth.username.encode()
335
332 if user == b'':
333 self.is_anon = True
334
336335 auth = {
337336 'simple' : pw
338337 }
362361 res['protocolOp']['diagnosticMessage']
363362 )
364363
365 elif self.creds.auth_method in MSLDAP_GSS_METHODS:
364 elif self.credential.protocol in [asyauthProtocol.NTLM, asyauthProtocol.KERBEROS]:
366365 challenge = None
367366 while True:
368367 try:
369 data, to_continue, err = await self.auth.authenticate(challenge, cb_data = self.cb_data)
368 flags = ISC_REQ.CONNECTION|ISC_REQ.CONFIDENTIALITY|ISC_REQ.INTEGRITY
369 if self.target.protocol == UniProto.CLIENT_SSL_TCP:
370 flags = ISC_REQ.CONNECTION
371
372 data, to_continue, err = await self.auth.authenticate(challenge, cb_data = self.cb_data, spn=self.target.to_target_string(), flags=flags)
370373 if err is not None:
371374 raise err
372375 except Exception as e:
397400 res = res.native
398401 if res['protocolOp']['resultCode'] == 'success':
399402 if 'serverSaslCreds' in res['protocolOp']:
400 data, _, err = await self.auth.authenticate(res['protocolOp']['serverSaslCreds'], cb_data = self.cb_data)
403 data, _, err = await self.auth.authenticate(res['protocolOp']['serverSaslCreds'], cb_data = self.cb_data, spn=self.target.to_target_string(), flags=flags)
401404 if err is not None:
402405 return False, err
403406
404 self.encryption_sequence_counter = self.auth.get_seq_number()
407 if self.auth.encryption_needed() is True or self.auth.signing_needed() is True:
408 self.encryption_sequence_counter = self.auth.get_seq_number()
405409 self.__bind_success()
406410
407411 return True, None
417421 )
418422
419423 else:
420 raise Exception('Not implemented authentication method: %s' % self.creds.auth_method.name)
424 raise Exception('Not implemented authentication method: %s' % self.credential.protocol.name)
421425 except Exception as e:
426 await self.disconnect()
422427 return False, e
423428
424429 async def add(self, entry, attributes):
704709 except Exception as e:
705710 yield (None, e)
706711
712 async def whoami(self):
713 if self.status != MSLDAPClientStatus.RUNNING:
714 return None, Exception('Connection not running! Probably encountered an error')
715
716 ext = {
717 'requestName': b'1.3.6.1.4.1.4203.1.11.3',
718 }
719 br = { 'extendedReq' : ExtendedRequest(ext)}
720 msg = { 'protocolOp' : protocolOp(br)}
721
722 msg_id = await self.send_message(msg)
723 res = await self.recv_message(msg_id)
724 res = res[0]
725 if isinstance(res, Exception):
726 return None, res
727 if res.native['protocolOp']['resultCode'] != 'success':
728 return False, LDAPBindException(
729 res['protocolOp']['resultCode'],
730 res['protocolOp']['diagnosticMessage']
731 )
732 return res.native['protocolOp']['responseValue'].decode(), None
733
734
707735
708736 async def get_serverinfo(self):
709737 if self.status != MSLDAPClientStatus.RUNNING:
751779 #print('res')
752780 #print(res)
753781 return convert_attributes(res.native['protocolOp']['attributes']), None
754
755
756 async def amain():
757 import traceback
758 from msldap.commons.url import MSLDAPURLDecoder
759
760 base = 'DC=TEST,DC=CORP'
761
762 #ip = 'WIN2019AD'
763 #domain = 'TEST'
764 #username = 'victim'
765 #password = 'Passw0rd!1'
766 ##auth_method = LDAPAuthProtocol.SICILY
767 #auth_method = LDAPAuthProtocol.SIMPLE
768
769 #cred = MSLDAPCredential(domain, username, password , auth_method)
770 #target = MSLDAPTarget(ip)
771 #target.dc_ip = '10.10.10.2'
772 #target.domain = 'TEST'
773
774 url = 'ldaps+ntlm-password://test\\Administrator:QLFbT8zkiFGlJuf0B3Qq@WIN2019AD/?dc=10.10.10.2'
775
776 dec = MSLDAPURLDecoder(url)
777 cred = dec.get_credential()
778 target = dec.get_target()
779
780 print(cred)
781 print(target)
782
783 input()
784
785 client = MSLDAPClientConnection(target, cred)
786 await client.connect()
787 res, err = await client.bind()
788 if err is not None:
789 raise err
790
791 user = "CN=ldaptest_2,CN=Users,DC=test,DC=corp"
792 #attributes = {'objectClass': ['inetOrgPerson', 'posixGroup', 'top'], 'sn': 'user_sn', 'gidNumber': 0}
793 #res, err = await client.add(user, attributes)
794 #if err is not None:
795 # print(err)
796
797 #changes = {
798 # 'unicodePwd': [('replace', ['"TESTPassw0rd!1"'])],
799 # #'lockoutTime': [('replace', [0])]
800 #}
801
802 #res, err = await client.modify(user, changes)
803 #if err is not None:
804 # print('ERR! %s' % err)
805 #else:
806 # print('OK!')
807
808 res, err = await client.delete(user)
809 if err is not None:
810 print('ERR! %s' % err)
811
812 await client.disconnect()
813
814
815
816 if __name__ == '__main__':
817 from msldap import logger
818 from msldap.commons.credential import MSLDAPCredential, LDAPAuthProtocol
819 from msldap.commons.target import MSLDAPTarget
820 from msldap.protocol.query import query_syntax_converter
821
822 logger.setLevel(2)
823
824
825 asyncio.run(amain())
826
827
828
829
830
831
832
+0
-107
msldap/crypto/AES.py less more
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
-73
msldap/crypto/BASE.py less more
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
-89
msldap/crypto/DES.py less more
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
-181
msldap/crypto/MD4.py less more
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
-68
msldap/crypto/RC4.py less more
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
-108
msldap/crypto/TDES.py less more
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
-0
msldap/crypto/__init__.py less more
(Empty file)
+0
-55
msldap/crypto/hashing.py less more
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
-591
msldap/crypto/pure/AES/AES.py less more
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
-54
msldap/crypto/pure/AES/__init__.py less more
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
-229
msldap/crypto/pure/AES/blockfeeder.py less more
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
-62
msldap/crypto/pure/AES/util.py less more
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
-852
msldap/crypto/pure/DES/DES.py less more
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
-0
msldap/crypto/pure/DES/__init__.py less more
(Empty file)
+0
-64
msldap/crypto/pure/RC4/RC4.py less more
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
-0
msldap/crypto/pure/RC4/__init__.py less more
(Empty file)
+0
-0
msldap/crypto/pure/__init__.py less more
(Empty file)
+0
-67
msldap/crypto/symmetric.py less more
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')
44 #
55
66 import asyncio
7 from os import terminal_size
78 import traceback
89 import logging
910 import csv
1011 import shlex
1112 import datetime
1213 import copy
14 import typing
1315
1416 from msldap.external.aiocmd.aiocmd import aiocmd
1517 from msldap.external.asciitree.asciitree import LeftAligned
1820 from msldap import logger
1921 from asysocks import logger as sockslogger
2022 from msldap.client import MSLDAPClient
21 from msldap.commons.url import MSLDAPURLDecoder
23 from msldap.commons.factory import LDAPConnectionFactory
2224 from msldap.ldap_objects import MSADUser, MSADMachine, MSADUser_TSV_ATTRS
2325
2426 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
2628 from winacl.dtyp.sid import SID
2729 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
2833
2934
3035 class MSLDAPClientConsole(aiocmd.PromptToolkitCmd):
3237 aiocmd.PromptToolkitCmd.__init__(self, ignore_sigint=False) #Setting this to false, since True doesnt work on windows...
3338 self.conn_url = None
3439 if url is not None:
35 self.conn_url = MSLDAPURLDecoder(url)
40 self.conn_url = LDAPConnectionFactory.from_url(url)
3641 self.connection = None
3742 self.adinfo = None
3843 self.ldapinfo = None
4449 if self.conn_url is None and url is None:
4550 print('Not url was set, cant do logon')
4651 if url is not None:
47 self.conn_url = MSLDAPURLDecoder(url)
52 self.conn_url = LDAPConnectionFactory.from_url(url)
4853
4954 logger.debug(self.conn_url.get_credential())
5055 logger.debug(self.conn_url.get_target())
5459 _, err = await self.connection.connect()
5560 if err is not None:
5661 raise err
62 print('BIND OK!')
5763
5864 return True
5965 except:
211217 traceback.print_exc()
212218 return False
213219
214 async def do_user(self, samaccountname):
220 async def do_user(self, samaccountname, to_print=True):
215221 """Feteches a user object based on the sAMAccountName of the user"""
216222 try:
217223 await self.do_ldapinfo(False)
219225 user, err = await self.connection.get_user(samaccountname)
220226 if err is not None:
221227 raise err
222 if user is None:
223 print('User not found!')
224 else:
225 print(user)
226
227 return True
228
229 if to_print is True:
230 if user is None:
231 print('User not found!')
232 else:
233 print(user)
234
235 return user
228236 except:
229237 traceback.print_exc()
230238 return False
569577 traceback.print_exc()
570578 return False
571579
580 async def do_delspn(self, user_dn, spn):
581 """Removes an SPN entry to the users account"""
582 try:
583 _, err = await self.connection.del_user_spn(user_dn, spn)
584 if err is not None:
585 raise err
586 print('SPN removed!')
587 return True
588 except:
589 traceback.print_exc()
590 return False
591
572592 async def do_addhostname(self, user_dn, hostname):
573593 """Adds additional hostname to computer account"""
574594 try:
604624 except:
605625 traceback.print_exc()
606626 return False
607
627
628 async def do_rootcas(self, to_print = True):
629 """Lists Root CA certificates"""
630 try:
631 cas = []
632 async for ca, err in self.connection.list_root_cas():
633 if err is not None:
634 raise err
635 cas.append(ca)
636 if to_print is True:
637 print(ca)
638 return cas
639 except:
640 traceback.print_exc()
641 return False
642
643 async def do_ntcas(self, to_print = True):
644 """Lists NT CA certificates"""
645 try:
646 cas = []
647 async for ca, err in self.connection.list_ntcas():
648 if err is not None:
649 raise err
650 cas.append(ca)
651 if to_print is True:
652 print(ca)
653 return cas
654 except:
655 traceback.print_exc()
656 return False
657
658 async def do_aiacas(self, to_print = True):
659 """Lists AIA CA certificates"""
660 try:
661 cas = []
662 async for ca, err in self.connection.list_aiacas():
663 if err is not None:
664 raise err
665 cas.append(ca)
666 if to_print is True:
667 print(ca)
668 return cas
669 except:
670 traceback.print_exc()
671 return False
672
673 async def do_enrollmentservices(self, to_print=True):
674 """Lists AIA CA certificates"""
675 try:
676 services = []
677 async for srv, err in self.connection.list_enrollment_services():
678 if err is not None:
679 raise err
680 services.append(srv)
681 if to_print is True:
682 print(srv)
683 return services
684 except:
685 traceback.print_exc()
686 return False
687
688 async def do_addcerttemplatenameflagaltname(self, certtemplatename, flags = None):
689 """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."""
690 try:
691 template = None
692 async for template, err in self.connection.list_certificate_templates(certtemplatename):
693 if err is not None:
694 raise err
695 break
696
697 if template is None:
698 raise Exception("Template could not be found!")
699
700 template = typing.cast(MSADCertificateTemplate, template)
701 if flags is not None:
702 flags = int(flags)
703 else:
704 flags = int(CertificateNameFlag(template.Certificate_Name_Flag) | CertificateNameFlag.ENROLLEE_SUPPLIES_SUBJECT_ALT_NAME)
705
706 changes = {
707 'msPKI-Certificate-Name-Flag' : [('replace', [flags])]
708 }
709
710 _, err = await self.connection.modify(template.distinguishedName, changes)
711 if err is not None:
712 raise err
713
714 print('Modify OK!')
715 return True
716
717
718 except:
719 traceback.print_exc()
720 return False
721
722 async def do_addenrollmentright(self, certtemplatename, user_dn):
723 """Grants enrollment rights to a user (by DN) for the specified certificate template."""
724 try:
725 user_sid, err = await self.connection.get_objectsid_for_dn(user_dn)
726 if err is not None:
727 raise err
728
729 template = None
730 async for template, err in self.connection.list_certificate_templates(certtemplatename):
731 if err is not None:
732 raise err
733 break
734
735 if template is None:
736 raise Exception("Template could not be found!")
737 template = typing.cast(MSADCertificateTemplate, template)
738 new_sd = copy.deepcopy(template.nTSecurityDescriptor)
739 ace = ACCESS_ALLOWED_OBJECT_ACE()
740 ace.Sid = SID.from_string(user_sid)
741 ace.ObjectType = GUID.from_string(EX_RIGHT_CERTIFICATE_ENROLLMENT)
742 ace.AceFlags = AceFlags(0)
743 ace.Mask = ADS_ACCESS_MASK.READ_PROP | ADS_ACCESS_MASK.WRITE_PROP | ADS_ACCESS_MASK.CONTROL_ACCESS
744 ace.Flags = ACE_OBJECT_PRESENCE.ACE_OBJECT_TYPE_PRESENT
745 new_sd.Dacl.aces.append(ace)
746 _, err = await self.connection.set_objectacl_by_dn(template.distinguishedName, new_sd.to_bytes(), flags=SDFlagsRequest.DACL_SECURITY_INFORMATION)
747 if err is not None:
748 raise err
749 print('SD set sucessfully')
750 return True
751 except:
752 traceback.print_exc()
753 return False
754
755 async def do_certtemplates(self, name = None, to_print = True):
756 """Lists certificate templates"""
757 try:
758 services = await self.do_enrollmentservices(to_print=False)
759 templates = []
760 async for template, err in self.connection.list_certificate_templates(name):
761 if err is not None:
762 raise err
763
764 lt = None
765 if template.nTSecurityDescriptor is not None:
766 lt, err = await self.connection.resolv_sd(template.nTSecurityDescriptor)
767 if err is not None:
768 raise err
769 template.sid_lookup_table = lt
770 for srv in services:
771 if template.name in srv.certificateTemplates:
772 template.enroll_services.append('%s\\%s' % (srv.dNSHostName, srv.name))
773
774 templates.append(template)
775 if to_print is True:
776 print(template.prettyprint())
777
778 return templates
779 except:
780 traceback.print_exc()
781 return False
782
783 async def do_sidresolv(self, sid, to_print = True):
784 """Returns the domain and username for SID"""
785 try:
786 domain, username, err = await self.connection.resolv_sid(sid)
787 if err is not None:
788 raise err
789 res = '%s\\%s' % (domain, username)
790 if to_print is True:
791 print(res)
792 return res
793 except:
794 traceback.print_exc()
795 return False
796
797 async def do_certify(self, cmd = None, username = None):
798 """ADCA security test"""
799 try:
800 es = await self.do_enrollmentservices(to_print=False)
801 if es is False:
802 raise Exception('Listing enrollment Services error! %s' % es)
803 if es is None:
804 raise Exception('No Enrollment Services present, stopping!')
805
806 templates = await self.do_certtemplates(to_print=False)
807 if templates is False:
808 raise Exception('Listing templates error! %s' % es)
809
810 if templates is None:
811 raise Exception('No templates exists!')
812
813 for enrollment in es:
814 print(enrollment)
815
816 if cmd is not None:
817 if cmd.lower().startswith('vuln') is True:
818 tokengroups = None
819 if username is not None:
820 tokengroups, err = await self.connection.get_tokengroups_user(username)
821 if err is not None:
822 raise err
823
824 for template in templates:
825 isvuln, reason = template.is_vulnerable(tokengroups)
826 if isvuln is True:
827 print(reason)
828 print(template)
829 else:
830 for template in templates:
831 print(template)
832
833 return True
834 except:
835 traceback.print_exc()
836 return False
837
838 async def do_whoamiraw(self):
839 """Simple whoami"""
840 try:
841 res, err = await self.connection.whoami()
842 if err is not None:
843 raise err
844 print(res)
845 except:
846 traceback.print_exc()
847 return False
848
849 async def do_whoami(self):
850 """Full whoami"""
851 try:
852 res, err = await self.connection.whoamifull()
853 if err is not None:
854 raise err
855
856 for x in res:
857 if isinstance(res[x], str) is True:
858 print('%s: %s' % (x, res[x]))
859 elif isinstance(res[x], dict) is True:
860 for k in res[x]:
861 print('Group: %s (%s)' % (k,'\\'.join(res[x][k])))
862 return True
863 except:
864 traceback.print_exc()
865 return False, None
866
608867 async def do_test(self):
609868 """testing, dontuse"""
610869 try:
66 import asyncio
77 import traceback
88 import logging
9 import csv
10 import shlex
11 import datetime
12
13 from msldap.external.aiocmd.aiocmd import aiocmd
14 from msldap.external.asciitree.asciitree import LeftAligned
15 from tqdm import tqdm
169
1710 from msldap import logger
1811 from asysocks import logger as sockslogger
19 from msldap.client import MSLDAPClient
20 from msldap.commons.url import MSLDAPURLDecoder
21 from msldap.ldap_objects import MSADUser, MSADMachine, MSADUser_TSV_ATTRS
22
23 from winacl.dtyp.security_descriptor import SECURITY_DESCRIPTOR
24
12 from msldap.commons.factory import LDAPConnectionFactory
2513
2614 class MSLDAPCompDomainList:
2715 def __init__(self, ldap_url):
6856 _, err = await self.do_adinfo(False)
6957 if err is not None:
7058 raise err
71 #machine_filename = '%s_computers_%s.txt' % (self.domain_name, datetime.datetime.now().strftime("%Y%m%d-%H%M%S"))
7259
7360 async for machine, err in self.connection.get_all_machines(attrs=['sAMAccountName', 'dNSHostName']):
7461 if err is not None:
10188 logger.setLevel(logging.DEBUG)
10289 logging.basicConfig(level=logging.DEBUG)
10390
104 ldap_url = MSLDAPURLDecoder(args.url)
91 ldap_url = LDAPConnectionFactory.from_url(args.url)
10592 compdomlist = MSLDAPCompDomainList(ldap_url)
10693
10794
1313 from msldap.ldap_objects.adgpo import MSADGPO, MSADGPO_ATTRS
1414 from msldap.ldap_objects.adtrust import MSADDomainTrust, MSADDomainTrust_ATTRS
1515 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
1619
1720 __all__ = [
1821 'MSADUser',
3639 'MSADOU_ATTRS',
3740 'MSADSCHEMAENTRY_ATTRS',
3841 'MSADSchemaEntry',
42 'MSADCA',
43 'MSADCA_ATTRS',
44 'MSADEnrollmentService_ATTRS',
45 'MSADEnrollmentService',
46 'MSADCertificateTemplate',
47 'MSADCertificateTemplate_ATTRS',
48
3949 ]
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
+0
-81
msldap/network/multiplexor.py less more
0 import enum
1 import asyncio
2 import ipaddress
3 import copy
4
5 from asysocks.common.clienturl import SocksClientURL
6 from asysocks.common.constants import SocksServerVersion, SocksProtocol, SOCKS5Method
7 from asysocks.common.target import SocksTarget
8
9 from msldap import logger
10 from msldap.network.socks import SocksProxyConnection
11 from msldap.commons.proxy import MSLDAPProxy, MSLDAPProxyType
12 from minikerberos.common.target import KerberosTarget
13 from minikerberos.common.proxy import KerberosProxy
14
15
16
17 class MultiplexorProxyConnection:
18 """
19 """
20 def __init__(self, target):
21 self.target = target
22
23 async def connect(self, is_kerberos = False):
24 """
25
26 """
27 #hiding the import, so you'll only need to install multiplexor only when actually using it
28 from multiplexor.operator import MultiplexorOperator
29
30 con_str = self.target.proxy.target.get_server_url()
31 #creating operator and connecting to multiplexor server
32 self.operator = MultiplexorOperator(con_str, logging_sink = logger)
33 await self.operator.connect()
34 #creating socks5 proxy
35 server_info = await self.operator.start_socks5(self.target.proxy.target.agent_id)
36 await self.operator.terminate()
37 #print(server_info)
38 if is_kerberos is False:
39
40 #copying the original target, then feeding it to socks5proxy object. it will hold the actual socks5 proxy server address we created before
41 tp = MSLDAPProxy()
42 tp.target = SocksTarget()
43 tp.target.version = SocksServerVersion.SOCKS5
44 tp.target.server_ip = server_info['listen_ip']
45 tp.target.server_port = server_info['listen_port']
46 tp.target.is_bind = False
47 tp.target.proto = SocksProtocol.TCP
48 tp.target.timeout = self.target.timeout
49 tp.target.buffer_size = 4096
50
51 tp.target.endpoint_ip = self.target.host
52 tp.target.endpoint_port = self.target.port
53 tp.target.endpoint_timeout = None # TODO: maybe implement endpoint timeout in the msldap target?
54 tp.type = MSLDAPProxyType.SOCKS5
55
56 newtarget = copy.deepcopy(self.target)
57 newtarget.proxy = tp
58
59
60
61 return SocksProxyConnection(target = newtarget)
62
63 else:
64 kt = copy.deepcopy(self.target)
65 kt.proxy = KerberosProxy()
66 kt.proxy.target = SocksTarget()
67 kt.proxy.target.version = SocksServerVersion.SOCKS5
68 kt.proxy.target.server_ip = server_info['listen_ip']
69 kt.proxy.target.server_port = server_info['listen_port']
70 kt.proxy.target.is_bind = False
71 kt.proxy.target.proto = SocksProtocol.TCP
72 kt.proxy.target.timeout = 10
73 kt.proxy.target.buffer_size = 4096
74
75 kt.proxy.target.endpoint_ip = self.target.ip
76 kt.proxy.target.endpoint_port = self.target.port
77 #kt.proxy.creds = copy.deepcopy(self.target.proxy.auth)
78
79 return kt
80
0 import asyncio
1
2 from msldap import logger
3 from msldap.protocol.utils import calcualte_length
4 from asysocks.unicomm.common.packetizers import Packetizer
5
6 class LDAPPacketizer(Packetizer):
7 def __init__(self):
8 Packetizer.__init__(self, 65535)
9 self.in_buffer = b''
10 self.is_plain_msg = True
11
12 def process_buffer(self):
13 preread = 6
14 remaining_length = -1
15 while True:
16 if len(self.in_buffer) < preread:
17 break
18 lb = self.in_buffer[:preread]
19 if self.is_plain_msg is True:
20 remaining_length = calcualte_length(lb) - preread
21 else:
22 remaining_length = int.from_bytes(lb[:4], byteorder = 'big', signed = False)
23 remaining_length = (remaining_length + 4) - preread
24 if len(self.in_buffer) >= remaining_length+preread:
25 data = self.in_buffer[:remaining_length+preread]
26 self.in_buffer = self.in_buffer[remaining_length+preread:]
27 yield data
28 continue
29 break
30
31
32 async def data_out(self, data):
33 yield data
34
35 async def data_in(self, data):
36 if data is None:
37 yield data
38 self.in_buffer += data
39 for packet in self.process_buffer():
40 yield packet
+0
-0
msldap/network/proxy/__init__.py less more
(Empty file)
+0
-103
msldap/network/proxy/handler.py less more
0 import copy
1 import asyncio
2
3 from msldap.commons.proxy import MSLDAPProxyType
4
5
6 class Proxyhandler:
7 def __init__(self, target):
8 self.target = target
9
10 def select(self):
11 if self.target.proxy is None:
12 return self.target
13
14 if self.target.proxy.proxy_type in [MSLDAPProxyType.SOCKS5, MSLDAPProxyType.SOCKS5_SSL]:
15 import socket
16 try:
17 from socks5line.socks5line import Socks5LineProxyServer,SOCKS5Line
18 except ImportError:
19 raise Exception('Failed to import socks5line proxy emulator! Install it then retry!')
20 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
21 s.bind(('127.0.0.1', 0))
22 new_port = s.getsockname()[1]
23 proxy = Socks5LineProxyServer()
24 proxy.ip = self.target.proxy.ip
25 proxy.port = self.target.proxy.port
26 proxy.timeout = self.target.proxy.timeout
27 proxy.username = self.target.proxy.username
28 proxy.password = self.target.proxy.secret
29
30 sl = SOCKS5Line(proxy, self.target.host, self.target.port)
31 sl.run_newthread(s)
32
33 newtarget = copy.deepcopy(self.target)
34 newtarget.proxy = None
35 newtarget.host = '127.0.0.1'
36 newtarget.port = new_port
37
38 return newtarget
39
40 elif self.target.proxy.proxy_type in [LDAPProxyType.MULTIPLEXOR, LDAPProxyType.MULTIPLEXOR_SSL]:
41 import socket
42 try:
43 from socks5line.socks5line import Socks5LineProxyServer,SOCKS5Line
44 except ImportError:
45 raise Exception('Failed to import socks5line proxy emulator! Install it then retry!')
46 try:
47 from multiplexor.operator import MultiplexorOperator
48 except ImportError:
49 raise Exception('Failed to import multiplexor! Install it then retry!')
50
51 async def create_proxy(connection_string, agent_id):
52 try:
53 #creating operator and connecting to multiplexor server
54 self.operator = MultiplexorOperator(con_str, reconnect_tries = 1)
55 await self.operator.connect()
56 #creating socks5 proxy
57 server_info = await self.operator.start_socks5(agent_id)
58 asyncio.create_task(self.operator.terminate())
59 return server_info
60 except Exception as e:
61 asyncio.create_task(self.operator.terminate())
62 return e
63
64 #creating connection string
65 if self.target.proxy.proxy_type == LDAPProxyType.MULTIPLEXOR:
66 con_str = 'ws://%s:%s' % (self.target.proxy.ip, self.target.proxy.port)
67 else:
68 con_str = 'wss://%s:%s' % (self.target.proxy.ip, self.target.proxy.port)
69
70
71 #because of URL stuff, this logic needs to be in place
72 #if self.target.proxy.domain is None:
73 # agent_id = self.target.proxy.username
74 #else:
75 # agent_id = self.target.proxy.domain
76 print('proxy_connecting')
77 server_info = asyncio.run(create_proxy(con_str, self.target.proxy.settings['agentid'][0]))
78 print('socks5 server info %s' % server_info)
79 if isinstance(server_info, Exception):
80 raise Exception('Failed to create socks proxy Reason: %s '% server_info)
81 #copying the original target, then feeding it to socks5proxy object. it will hold the actual socks5 proxy server address we created before
82
83 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
84 s.bind(('127.0.0.1', 0))
85 new_port = s.getsockname()[1]
86 proxy = Socks5LineProxyServer()
87 proxy.ip = server_info['listen_ip']
88 proxy.port = server_info['listen_port']
89 proxy.timeout = self.target.proxy.timeout
90 proxy.username = self.target.proxy.username
91 proxy.password = self.target.proxy.secret
92
93 sl = SOCKS5Line(proxy, self.target.host, self.target.port)
94 sl.run_newthread(s)
95
96 print('socks5 socks5line ready')
97 newtarget = copy.deepcopy(self.target)
98 newtarget.proxy = None
99 newtarget.host = '127.0.0.1'
100 newtarget.port = new_port
101
102 return newtarget
+0
-32
msldap/network/selector.py less more
0
1 from msldap import logger
2 from msldap.network.tcp import MSLDAPTCPNetwork
3 from msldap.network.socks import SocksProxyConnection
4 from msldap.network.multiplexor import MultiplexorProxyConnection
5 from msldap.commons.proxy import MSLDAPProxyType
6
7 MSLDAP_SOCKS_PROXY_TYPES = [
8 MSLDAPProxyType.SOCKS4,
9 MSLDAPProxyType.SOCKS4_SSL,
10 MSLDAPProxyType.SOCKS5,
11 MSLDAPProxyType.SOCKS5_SSL,
12 MSLDAPProxyType.WSNET,
13 MSLDAPProxyType.WSNETWS,
14 MSLDAPProxyType.WSNETWSS,
15 ]
16
17 class MSLDAPNetworkSelector:
18 def __init__(self):
19 pass
20
21 @staticmethod
22 async def select(target):
23 if target.proxy is not None:
24 if target.proxy.type in MSLDAP_SOCKS_PROXY_TYPES:
25 return SocksProxyConnection(target)
26 else:
27 mpc = MultiplexorProxyConnection(target)
28 socks_proxy = await mpc.connect()
29 return socks_proxy
30
31 return MSLDAPTCPNetwork(target)
+0
-121
msldap/network/socks.py less more
0
1 import enum
2 import asyncio
3 import ipaddress
4
5 from msldap import logger
6
7 from asysocks.client import SOCKSClient
8 from asysocks.common.comms import SocksQueueComms
9 from msldap.protocol.utils import calcualte_length
10
11
12 class SocksProxyConnection:
13 """
14 Generic asynchronous TCP socket class, nothing SMB related.
15 Creates the connection and channels incoming/outgoing bytes via asynchonous queues.
16 """
17 def __init__(self, target):
18 self.target = target
19
20 self.client = None
21 self.proxy_task = None
22 self.handle_in_task = None
23
24 self.out_queue = None#asyncio.Queue()
25 self.in_queue = None#asyncio.Queue()
26
27 self.proxy_in_queue = None#asyncio.Queue()
28 self.is_plain_msg = True
29
30 async def disconnect(self):
31 """
32 Disconnects from the socket.
33 Stops the reader and writer streams.
34 """
35 if self.client is not None:
36 await self.client.terminate()
37 if self.proxy_task is not None:
38 self.proxy_task.cancel()
39 if self.handle_in_q is not None:
40 self.handle_in_task.cancel()
41
42 async def terminate(self):
43 await self.disconnect()
44
45 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
49 def get_one_message(self,data):
50 if len(data) < 6:
51 return None
52
53 if self.is_plain_msg is True:
54 dl = calcualte_length(data[:6])
55 else:
56 dl = int.from_bytes(data[:4], byteorder = 'big', signed = False)
57 dl = dl + 4
58
59
60 #print(dl)
61 if len(data) >= dl:
62 return data[:dl]
63
64 async def handle_in_q(self):
65 try:
66 data = b''
67 while True:
68 while True:
69 msg_data = self.get_one_message(data)
70 if msg_data is None:
71 break
72
73 await self.in_queue.put((msg_data, None))
74 data = data[len(msg_data):]
75
76 temp, err = await self.proxy_in_queue.get()
77 #print(temp)
78 if err is not None:
79 raise err
80
81 if temp == b'' or temp is None:
82 logger.debug('Server finished!')
83 return
84
85 data += temp
86 continue
87
88 except asyncio.CancelledError:
89 return
90 except Exception as e:
91 logger.exception('handle_in_q')
92 await self.in_queue.put((None, e))
93
94 finally:
95 self.proxy_task.cancel()
96
97
98
99 async def run(self):
100 """
101
102 """
103 try:
104 self.out_queue = asyncio.Queue()
105 self.in_queue = asyncio.Queue()
106
107 self.proxy_in_queue = asyncio.Queue()
108 comms = SocksQueueComms(self.out_queue, self.proxy_in_queue)
109
110 self.target.proxy.target[-1].endpoint_ip = self.target.host if self.target.serverip is None else self.target.serverip
111 self.target.proxy.target[-1].endpoint_port = int(self.target.port)
112 self.target.proxy.target[-1].endpoint_timeout = None #TODO: maybe implement endpoint timeout?
113 self.target.proxy.target[-1].timeout = self.target.timeout
114 self.client = SOCKSClient(comms, self.target.proxy.target)
115 self.proxy_task = asyncio.create_task(self.client.run())
116 self.handle_in_task = asyncio.create_task(self.handle_in_q())
117 return True, None
118 except Exception as e:
119 return False, e
120
+0
-99
msldap/network/tcp.py less more
0
1 import asyncio
2
3 from msldap import logger
4 from msldap.protocol.utils import calcualte_length
5
6 class MSLDAPTCPNetwork:
7 def __init__(self, target):
8 self.target = target
9 self.timeout = None
10 self.in_queue = None
11 self.out_queue = None
12 self.reader = None
13 self.writer = None
14
15 self.handle_in_task = None
16 self.handle_out_task = None
17
18 self.is_plain_msg = True
19
20 async def terminate(self):
21 self.handle_in_task.cancel()
22 self.handle_out_task.cancel()
23
24 def get_peer_certificate(self):
25 return self.writer.get_extra_info('ssl_object').getpeercert(True)
26
27 async def handle_in_q(self):
28 try:
29 while True:
30
31 preread = 6
32 lb = await asyncio.wait_for(self.reader.readexactly(preread), self.timeout)
33 if lb is None:
34 logger.debug('Server timed out!')
35 return
36 if lb == b'':
37 logger.debug('Server finished!')
38 return
39
40 if self.is_plain_msg is True:
41 remaining_length = calcualte_length(lb) - preread
42 else:
43 remaining_length = int.from_bytes(lb[:4], byteorder = 'big', signed = False)
44 remaining_length = (remaining_length + 4) - preread
45 #print('Reading %s' % remaining_length)
46
47 remaining_data = await asyncio.wait_for(self.reader.readexactly(remaining_length), self.timeout)
48
49 await self.in_queue.put((lb+remaining_data, None))
50
51
52 #except asyncio.CancelledError:
53 # return
54 except Exception as e:
55 #logger.exception('handle_in_q')
56 await self.in_queue.put((None, e))
57
58 finally:
59 self.handle_out_task.cancel()
60
61 async def handle_out_q(self):
62 try:
63 while True:
64 data = await self.out_queue.get()
65 if data is None:
66 logger.debug('Client finished!')
67 return
68
69 self.writer.write(data)
70 await self.writer.drain()
71 except asyncio.CancelledError:
72 return
73 except:
74 logger.exception('handle_out_q')
75
76 finally:
77 self.writer.close()
78 self.handle_in_task.cancel()
79
80
81 async def run(self):
82 try:
83 self.in_queue = asyncio.Queue()
84 self.out_queue = asyncio.Queue()
85 self.reader, self.writer = await asyncio.wait_for(
86 asyncio.open_connection(
87 self.target.serverip if self.target.serverip is not None else self.target.host,
88 self.target.port,
89 ssl=self.target.get_ssl_context()
90 ),
91 timeout = self.target.timeout
92 )
93
94 self.handle_in_task = asyncio.create_task(self.handle_in_q())
95 self.handle_out_task = asyncio.create_task(self.handle_out_q())
96 return True, None
97 except Exception as e:
98 return False, e
+0
-125
msldap/network/wsnet.py less more
0
1 #
2 #
3 #
4 #
5 #
6 #
7
8
9 import enum
10 import asyncio
11 import ipaddress
12
13 from msldap import logger
14 from msldap.protocol.utils import calcualte_length
15
16 from pyodidewsnet.client import WSNetworkTCP
17
18
19
20 class WSNetProxyConnection:
21 """
22 Generic asynchronous TCP socket class, nothing SMB related.
23 Creates the connection and channels incoming/outgoing bytes via asynchonous queues.
24 """
25 def __init__(self, target):
26 self.target = target
27
28 self.client = None
29 self.handle_in_task = None
30
31 self.out_queue = None#asyncio.Queue()
32 self.in_queue = None#asyncio.Queue()
33
34 self.proxy_in_queue = None#asyncio.Queue()
35 self.is_plain_msg = True
36
37 async def disconnect(self):
38 """
39 Disconnects from the socket.
40 Stops the reader and writer streams.
41 """
42 if self.client is not None:
43 await self.client.terminate()
44 if self.handle_in_q is not None:
45 self.handle_in_task.cancel()
46
47 async def terminate(self):
48 await self.disconnect()
49
50 def get_peer_certificate(self):
51 raise Exception('Not yet implemented! SSL implementation on socks is missing!')
52 return self.writer.get_extra_info('socket').getpeercert(True)
53
54 def get_one_message(self,data):
55 if len(data) < 6:
56 return None
57
58 if self.is_plain_msg is True:
59 dl = calcualte_length(data[:6])
60 else:
61 dl = int.from_bytes(data[:4], byteorder = 'big', signed = False)
62 dl = dl + 4
63
64
65 #print(dl)
66 if len(data) >= dl:
67 return data[:dl]
68
69 async def handle_in_q(self):
70 try:
71 data = b''
72 while True:
73 while True:
74 msg_data = self.get_one_message(data)
75 if msg_data is None:
76 break
77
78 await self.in_queue.put((msg_data, None))
79 data = data[len(msg_data):]
80
81 temp, err = await self.proxy_in_queue.get()
82 #print(temp)
83 if err is not None:
84 raise err
85
86 if temp == b'' or temp is None:
87 logger.debug('Server finished!')
88 return
89
90 data += temp
91 continue
92
93 except asyncio.CancelledError:
94 return
95 except Exception as e:
96 logger.exception('handle_in_q')
97 await self.in_queue.put((None, e))
98
99 finally:
100 await self.client.terminate()
101
102
103
104 async def run(self):
105 """
106
107 """
108 try:
109 self.out_queue = asyncio.Queue()
110 self.in_queue = asyncio.Queue()
111 self.proxy_in_queue = asyncio.Queue()
112
113 self.client = WSNetworkTCP(self.target.host, int(self.target.port), self.proxy_in_queue, self.out_queue)
114 _, err = await self.client.run()
115 if err is not None:
116 raise err
117
118 self.handle_in_task = asyncio.create_task(self.handle_in_q())
119
120 return True, None
121
122 except Exception as e:
123 return False, e
124
2020 elif value == '*':
2121 return Filter({
2222 '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 })
2335 })
2436
2537 elif value.startswith('*') is True:
9292 def list_bytes_one_enc(x):
9393 return x
9494
95 def bytes2timedelta(x):
96 return int2timedelta([int.from_bytes(x[0], byteorder='little', signed=True)])
97
9598 def int2timedelta(x):
9699 x = int(x[0])
97100 if x == '-9223372036854775808':
165168 t.append(ts2dt((a, None)))
166169 return t
167170
171 def list_ts2dt_one(x):
172 return ts2dt(x[0])
168173
169174 LDAP_ATTRIBUTE_TYPES = {
170175 'supportedCapabilities' : list_str,
274279 'unicodePwd' : list_str_one,
275280 'ms-Mcs-AdmPwd' : list_str_one,
276281 '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
277304 }
278305
279306 LDAP_ATTRIBUTE_TYPES_ENC = {
294321 'member' : list_str_enc,
295322 'msDS-AllowedToActOnBehalfOfOtherIdentity' : list_bytes_one_enc,
296323 'nTSecurityDescriptor' : list_bytes_one_enc,
324 'msPKI-Certificate-Name-Flag' : list_int_one_enc
297325 }
298326
299327 def encode_attributes(x):
0 Metadata-Version: 1.2
0 Metadata-Version: 2.1
11 Name: msldap
2 Version: 0.3.30
2 Version: 0.4.6
33 Summary: Python library to play with MS LDAP
44 Home-page: https://github.com/skelsec/msldap
55 Author: Tamas Jos
66 Author-email: [email protected]
7 License: UNKNOWN
8 Description: Python library to play with MS LDAP
9 Platform: UNKNOWN
107 Classifier: Programming Language :: Python :: 3.7
118 Classifier: Programming Language :: Python :: 3.8
129 Classifier: License :: OSI Approved :: MIT License
1310 Classifier: Operating System :: OS Independent
1411 Requires-Python: >=3.7
12 License-File: LICENSE
13
14 Python library to play with MS LDAP
1313 msldap.egg-info/not-zip-safe
1414 msldap.egg-info/requires.txt
1515 msldap.egg-info/top_level.txt
16 msldap/authentication/__init__.py
17 msldap/authentication/kerberos/__init__.py
18 msldap/authentication/kerberos/gssapi.py
19 msldap/authentication/kerberos/multiplexor.py
20 msldap/authentication/kerberos/native.py
21 msldap/authentication/kerberos/sspi.py
22 msldap/authentication/kerberos/sspiproxyws.py
23 msldap/authentication/kerberos/wsnet.py
24 msldap/authentication/ntlm/__init__.py
25 msldap/authentication/ntlm/creds_calc.py
26 msldap/authentication/ntlm/multiplexor.py
27 msldap/authentication/ntlm/native.py
28 msldap/authentication/ntlm/sspi.py
29 msldap/authentication/ntlm/sspiproxy.py
30 msldap/authentication/ntlm/wsnet.py
31 msldap/authentication/ntlm/messages/__init__.py
32 msldap/authentication/ntlm/messages/authenticate.py
33 msldap/authentication/ntlm/messages/challenge.py
34 msldap/authentication/ntlm/messages/negotiate.py
35 msldap/authentication/ntlm/structures/__init__.py
36 msldap/authentication/ntlm/structures/avpair.py
37 msldap/authentication/ntlm/structures/challenge_response.py
38 msldap/authentication/ntlm/structures/fields.py
39 msldap/authentication/ntlm/structures/negotiate_flags.py
40 msldap/authentication/ntlm/structures/ntlmssp_message_signature.py
41 msldap/authentication/ntlm/structures/ntlmssp_message_signature_noext.py
42 msldap/authentication/ntlm/structures/version.py
43 msldap/authentication/ntlm/templates/__init__.py
44 msldap/authentication/ntlm/templates/client.py
45 msldap/authentication/ntlm/templates/server.py
46 msldap/authentication/spnego/__init__.py
47 msldap/authentication/spnego/asn1_structs.py
48 msldap/authentication/spnego/native.py
49 msldap/authentication/spnego/sspi.py
5016 msldap/commons/__init__.py
5117 msldap/commons/authbuilder.py
5218 msldap/commons/common.py
53 msldap/commons/credential.py
5419 msldap/commons/exceptions.py
55 msldap/commons/proxy.py
20 msldap/commons/factory.py
5621 msldap/commons/target.py
57 msldap/commons/url.py
5822 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
7723 msldap/examples/__init__.py
7824 msldap/examples/msldapclient.py
7925 msldap/examples/msldapcompdnslist.py
9036 msldap/external/asciitree/asciitree/traversal.py
9137 msldap/external/asciitree/asciitree/util.py
9238 msldap/ldap_objects/__init__.py
39 msldap/ldap_objects/adca.py
40 msldap/ldap_objects/adcertificatetemplate.py
9341 msldap/ldap_objects/adcomp.py
42 msldap/ldap_objects/adenrollmentservice.py
9443 msldap/ldap_objects/adgpo.py
9544 msldap/ldap_objects/adgroup.py
9645 msldap/ldap_objects/adinfo.py
10150 msldap/ldap_objects/aduser.py
10251 msldap/ldap_objects/common.py
10352 msldap/network/__init__.py
104 msldap/network/multiplexor.py
105 msldap/network/selector.py
106 msldap/network/socks.py
107 msldap/network/tcp.py
108 msldap/network/wsnet.py
109 msldap/network/proxy/__init__.py
110 msldap/network/proxy/handler.py
53 msldap/network/packetizer.py
11154 msldap/protocol/__init__.py
11255 msldap/protocol/constants.py
11356 msldap/protocol/messages.py
00 [console_scripts]
11 msldap = msldap.examples.msldapclient:main
22 msldapcompdns = msldap.examples.msldapcompdnslist:main
3
0 asn1crypto
1 asysocks>=0.1.1
2 minikerberos>=0.2.14
0 asn1crypto>=1.3.0
1 asyauth>=0.0.5
2 asysocks>=0.2.1
3 minikerberos>=0.3.1
34 prompt-toolkit>=3.0.2
45 tqdm
5 winacl>=0.1.1
6
7 [:platform_system == "Windows"]
8 winsspi>=0.0.9
6 unicrypto>=0.0.9
7 wcwidth
8 winacl>=0.1.4
4646 "Operating System :: OS Independent",
4747 ),
4848 install_requires=[
49 'asn1crypto',
50 'winsspi>=0.0.9;platform_system=="Windows"',
51 'minikerberos>=0.2.14',
52 'asysocks>=0.1.1',
53 'winacl>=0.1.1',
49 'unicrypto>=0.0.9',
50 'asyauth>=0.0.5',
51 'asysocks>=0.2.1',
52 'asn1crypto>=1.3.0',
53 'minikerberos>=0.3.1',
54 'winacl>=0.1.4',
5455 'prompt-toolkit>=3.0.2',
5556 'tqdm',
57 'wcwidth',
5658 ],
5759 entry_points={
5860 'console_scripts': [
6062 'msldapcompdns = msldap.examples.msldapcompdnslist:main',
6163 ],
6264 }
63 )
65 )