summaryrefslogtreecommitdiff
path: root/passlib
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2011-01-25 01:17:40 +0000
committerEli Collins <elic@assurancetechnologies.com>2011-01-25 01:17:40 +0000
commit2ba159bb201d3da7fe5fcd533e1a6531a3489062 (patch)
tree6f2a3e51150d9c729f3695d36688a7f1637da89d /passlib
parent5e9e909043cab4133443c574ef4318bb229e87ec (diff)
downloadpasslib-2ba159bb201d3da7fe5fcd533e1a6531a3489062.tar.gz
work on lanman hash
Diffstat (limited to 'passlib')
-rw-r--r--passlib/lanman.py44
-rw-r--r--passlib/tests/test_des_crypt.py52
-rw-r--r--passlib/utils/__init__.py23
-rw-r--r--passlib/utils/_slow_des_crypt.py49
-rw-r--r--passlib/utils/md4.py1
5 files changed, 146 insertions, 23 deletions
diff --git a/passlib/lanman.py b/passlib/lanman.py
index 7611cfa..ead8f07 100644
--- a/passlib/lanman.py
+++ b/passlib/lanman.py
@@ -3,33 +3,35 @@
lanman
macintosh
-D47F3AF827A48F7DFA4F2C1F12D68CD6 08460EB13C5CA0C4CA9516712F7FED95
+D47F 3AF8 27A4 8F7D FA4F 2C1F 12D6 8CD6
-ntlm
-
-"""
+08460EB13C5CA0C4CA9516712F7FED95
-from passlib.utils._slow_des_crypt import des_encrypt_rounds
+ntlm
-secret = "macintosh"
-s = secret.upper()[:14] + "\x00" * (14-len(secret))
-sa, sb = s[:7], s[7:]
+lanman
-cc = 0
-for c in reversed("KGS!@#$%"):
- cc <<= 8
- cc |= ord(c)
+C234 13A8 A1E7 665f
+AAD3 B435 B514 04EE
+welcome
+"""
+from binascii import hexlify
+from bps.numeric import int_to_base
+from passlib.utils._slow_des_crypt import des_encrypt_block
-ka = 0
-for c in reversed(sa):
- ka <<= 8
- ka |= (ord(c)<<1)
+LM_MAGIC = "KGS!@#$%"
-ct = des_encrypt_rounds(cc, 0, 25, ka)
+def lmhash(secret):
+ #XXX: encoding should be oem ascii
+ ns = secret.upper()[:14] + "\x00" * (14-len(secret))
+ return hexlify(des_encrypt_block(expand_des_key(ns[:7]), LM_MAGIC) + des_encrypt_block(expand_des_key(ns[7:]), LM_MAGIC))
-out = ''
-for i in xrange(8):
- out += '%02x' % ((ct>>(8*(7-i))) & 0xFF)
+for secret, hash in [
+ #hashes from http://msdn.microsoft.com/en-us/library/cc245828(v=prot.10).aspx
+ ("OLDPASSWORD", "c9b81d939d6fd80cd408e6b105741864"),
+ ("NEWPASSWORD", '09eeab5aa415d6e4d408e6b105741864'),
+ ("welcome", "c23413a8a1e7665faad3b435b51404ee"),
+ ]:
-print out
+ print secret, lmhash(secret), hash == lmhash(secret)
diff --git a/passlib/tests/test_des_crypt.py b/passlib/tests/test_des_crypt.py
index ea8898a..6553a77 100644
--- a/passlib/tests/test_des_crypt.py
+++ b/passlib/tests/test_des_crypt.py
@@ -131,6 +131,58 @@ if enable_test("backends"):
def get_crypt(self):
return mod.crypt
+
+class DesTest(TestCase):
+
+ #test vectors taken from http://www.skepticfiles.org/faq/testdes.htm
+
+ #(key, plaintext, ciphertext) all as 64 bit
+ test_des_vectors = [
+ (int(line[4:21],16), int(line[21:38],16), int(line[38:],16))
+ for line in
+ """ 0000000000000000 0000000000000000 8CA64DE9C1B123A7
+ FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF 7359B2163E4EDC58
+ 3000000000000000 1000000000000001 958E6E627A05557B
+ 1111111111111111 1111111111111111 F40379AB9E0EC533
+ 0123456789ABCDEF 1111111111111111 17668DFC7292532D
+ 1111111111111111 0123456789ABCDEF 8A5AE1F81AB8F2DD
+ 0000000000000000 0000000000000000 8CA64DE9C1B123A7
+ FEDCBA9876543210 0123456789ABCDEF ED39D950FA74BCC4
+ 7CA110454A1A6E57 01A1D6D039776742 690F5B0D9A26939B
+ 0131D9619DC1376E 5CD54CA83DEF57DA 7A389D10354BD271
+ 07A1133E4A0B2686 0248D43806F67172 868EBB51CAB4599A
+ 3849674C2602319E 51454B582DDF440A 7178876E01F19B2A
+ 04B915BA43FEB5B6 42FD443059577FA2 AF37FB421F8C4095
+ 0113B970FD34F2CE 059B5E0851CF143A 86A560F10EC6D85B
+ 0170F175468FB5E6 0756D8E0774761D2 0CD3DA020021DC09
+ 43297FAD38E373FE 762514B829BF486A EA676B2CB7DB2B7A
+ 07A7137045DA2A16 3BDD119049372802 DFD64A815CAF1A0F
+ 04689104C2FD3B2F 26955F6835AF609A 5C513C9C4886C088
+ 37D06BB516CB7546 164D5E404F275232 0A2AEEAE3FF4AB77
+ 1F08260D1AC2465E 6B056E18759F5CCA EF1BF03E5DFA575A
+ 584023641ABA6176 004BD6EF09176062 88BF0DB6D70DEE56
+ 025816164629B007 480D39006EE762F2 A1F9915541020B56
+ 49793EBC79B3258F 437540C8698F3CFA 6FBF1CAFCFFD0556
+ 4FB05E1515AB73A7 072D43A077075292 2F22E49BAB7CA1AC
+ 49E95D6D4CA229BF 02FE55778117F12A 5A6B612CC26CCE4A
+ 018310DC409B26D6 1D9D5C5018F728C2 5F4C038ED12B2E41
+ 1C587F1C13924FEF 305532286D6F295A 63FAC0D034D9F793
+ 0101010101010101 0123456789ABCDEF 617B3A0CE8F07100
+ 1F1F1F1F0E0E0E0E 0123456789ABCDEF DB958605F8C8C606
+ E0FEE0FEF1FEF1FE 0123456789ABCDEF EDBFD1C66C29CCC7
+ 0000000000000000 FFFFFFFFFFFFFFFF 355550B2150E2451
+ FFFFFFFFFFFFFFFF 0000000000000000 CAAAAF4DEAF1DBAE
+ 0123456789ABCDEF 0000000000000000 D5D44FF720683D0D
+ FEDCBA9876543210 FFFFFFFFFFFFFFFF 2A2BB008DF97C2F2
+ """.split("\n") if line.strip()
+ ]
+
+ def test_des_encrypt_int_block(self):
+ from passlib.utils._slow_des_crypt import des_encrypt_int_block
+ for k,p,c in self.test_des_vectors:
+ result = des_encrypt_int_block(k,p)
+ self.assertEqual(result, c, "key=%r p=%r:" % (k,p))
+
#=========================================================
#EOF
#=========================================================
diff --git a/passlib/utils/__init__.py b/passlib/utils/__init__.py
index dd17d1d..9b8a472 100644
--- a/passlib/utils/__init__.py
+++ b/passlib/utils/__init__.py
@@ -73,8 +73,29 @@ Undef = object() #singleton used as default kwd value in some functions
#numeric helpers
#=================================================================================
-#TODO: rename 'bytes' kwd for py30 compat purposes
+##def int_to_bytes(value, count=None):
+## """encode a integer into a string of bytes"""
+##
+##def bytes_to_int(value, order="big"):
+## """decode a byte string into an integer representation of it's binary value.
+##
+## :arg value: the string to decode.
+## :param order: the byte ordering; "big" (the default), "little", or "native"
+##
+## :returns: the decoded positive integer.
+## """
+## if not value:
+## return 0
+## if order == "native":
+## order = sys.byteorder
+## if order == "little":
+## value = reversed(value)
+## out = 0
+## for v in value:
+## out = (out<<8) | ord(v)
+## return out
+#TODO: rename 'bytes' kwd for py30 compat purposes
def list_to_bytes(value, bytes=None, order="big"):
"""Returns a multi-character string corresponding to a list of byte values.
diff --git a/passlib/utils/_slow_des_crypt.py b/passlib/utils/_slow_des_crypt.py
index cc68327..9ebf518 100644
--- a/passlib/utils/_slow_des_crypt.py
+++ b/passlib/utils/_slow_des_crypt.py
@@ -620,8 +620,55 @@ def permute(c, p):
c >>= 4
return out
+def bytes_to_int(value):
+ out = 0
+ for v in value:
+ out = (out<<8) | ord(v)
+ return out
+
+def int_to_bytes(value, count):
+ return ''.join(
+ chr((value>>s) & 0xff)
+ for s in xrange(8*count-8,-8,-8)
+ )
+
+def des_encrypt_block(key, input):
+ "do traditional encryption of a single DES block"
+ assert len(input) == 8
+ assert len(key) == 8
+ input = bytes_to_int(input)
+ key = bytes_to_int(key)
+ out = des_encrypt_rounds(input, 0, 1, key)
+ return int_to_bytes(out, 8)
+
+def expand_des_key(source):
+ "convert 7 byte des key to 8 byte des key (by adding parity bit every 7 bits)"
+ #NOTE: could probably do this much more cleverly and efficiently,
+ # but no need really given it's use
+ assert len(source) == 7
+
+ def iter_bits(source):
+ for c in source:
+ v = ord(c)
+ for i in xrange(7,-1,-1):
+ yield (v>>i) & 1
+
+ out = 0
+ p = 1
+ for i, b in enumerate(iter_bits(source)):
+ out = (out<<1) + b
+ p ^= b
+ if i % 7 == 6:
+ out = (out<<1) + p
+ p = 1
+
+ return ''.join(
+ chr((out>>s) & 0xFF)
+ for s in xrange(8*7,-8,-8)
+ )
+
def des_encrypt_rounds(input, salt, rounds, key):
- """Returns modified DES for single block of input"""
+ """returns modified DES for single block of input, used by des-crypt algorithm"""
global SPE, PCXROT, IE3264, CF6464
#bounds check
diff --git a/passlib/utils/md4.py b/passlib/utils/md4.py
index fe88f6a..2eb2c95 100644
--- a/passlib/utils/md4.py
+++ b/passlib/utils/md4.py
@@ -27,6 +27,7 @@ class md4(object):
"md4 hash algorithm"
#FIXME: make this follow hash object PEP better.
#FIXME: this isn't threadsafe
+ #XXX: should we monkeypatch ourselves into hashlib for general use? probably wouldn't be nice.
digest_size = digestsize = 16