summaryrefslogtreecommitdiff
path: root/md5.py
diff options
context:
space:
mode:
Diffstat (limited to 'md5.py')
-rwxr-xr-xmd5.py25
1 files changed, 15 insertions, 10 deletions
diff --git a/md5.py b/md5.py
index 917612e..0098c56 100755
--- a/md5.py
+++ b/md5.py
@@ -12,7 +12,7 @@ little bit wierd.
12 12
13This is a pythonic adaption of the C code in the APR library. 13This is a pythonic adaption of the C code in the APR library.
14If there are any questions please refer directly to the C code. 14If there are any questions please refer directly to the C code.
15You'll find it in apache subversion under 15You'll find it in apache subversion under
16/ap/apr-util/crypto/apr_md5.c 16/ap/apr-util/crypto/apr_md5.c
17""" 17"""
18 18
@@ -21,11 +21,12 @@ from hashlib import md5
21from random import random 21from random import random
22from math import floor 22from math import floor
23 23
24__all__ = [ "generate_md5", "generate_salt", "generate_short_salt" ] 24__all__ = ["generate_md5", "generate_salt", "generate_short_salt"]
25 25
26# Defined as such for brevity 26# Defined as such for brevity
27md5digest = lambda x: md5(x).digest() 27md5digest = lambda x: md5(x).digest()
28 28
29
29def ap_to64(input, count=4): 30def ap_to64(input, count=4):
30 """Weird-ass implementation of base64 conversion used by the 31 """Weird-ass implementation of base64 conversion used by the
31 APR library. 32 APR library.
@@ -33,19 +34,21 @@ def ap_to64(input, count=4):
33 chars = "./0123456789%s%s" % (string.uppercase, string.lowercase) 34 chars = "./0123456789%s%s" % (string.uppercase, string.lowercase)
34 output = "" 35 output = ""
35 input = int(input) # Need ints to do binary math 36 input = int(input) # Need ints to do binary math
36 37
37 for i in range(0, count): 38 for i in range(0, count):
38 output += chars[input & 0x3f] # Take 6 bits right 39 output += chars[input & 0x3f] # Take 6 bits right
39 input >>= 6 # shift by 6 bits 40 input >>= 6 # shift by 6 bits
40 41
41 return output 42 return output
42 43
44
43def generate_short_salt(): 45def generate_short_salt():
44 """Generate a short, 2-character salt. 46 """Generate a short, 2-character salt.
45 This is suitable for use in the htpasswd crypt routine. 47 This is suitable for use in the htpasswd crypt routine.
46 """ 48 """
47 return generate_salt()[0:2] 49 return generate_salt()[0:2]
48 50
51
49def generate_salt(): 52def generate_salt():
50 """Mine a little salt for your passwords. 53 """Mine a little salt for your passwords.
51 Returns 8 random characters in base64. It is 4 + 4 because the original 54 Returns 8 random characters in base64. It is 4 + 4 because the original
@@ -55,6 +58,7 @@ def generate_salt():
55 salt += ap_to64(floor(random() * 16777215)) 58 salt += ap_to64(floor(random() * 16777215))
56 return salt 59 return salt
57 60
61
58def generate_md5(passwd, salt=None): 62def generate_md5(passwd, salt=None):
59 """Generate an APRfied MD5 hash. 63 """Generate an APRfied MD5 hash.
60 This was adapted directly from the C code in the APR library. 64 This was adapted directly from the C code in the APR library.
@@ -66,10 +70,10 @@ def generate_md5(passwd, salt=None):
66 # I don't think it is really "checked" by Apache but I'm too 70 # I don't think it is really "checked" by Apache but I'm too
67 # lazy to confirm this. 71 # lazy to confirm this.
68 MAGIC_TOKEN = "$apr1$" 72 MAGIC_TOKEN = "$apr1$"
69 73
70 # Mainly just used for testing but why not leave it? 74 # Mainly just used for testing but why not leave it?
71 salt = salt if salt else generate_salt() 75 salt = salt if salt else generate_salt()
72 76
73 # Start with our password in the clear, a little magic and 77 # Start with our password in the clear, a little magic and
74 # a pinch of salt 78 # a pinch of salt
75 message = "%s%s%s" % (passwd, MAGIC_TOKEN, salt) 79 message = "%s%s%s" % (passwd, MAGIC_TOKEN, salt)
@@ -89,21 +93,21 @@ def generate_md5(passwd, salt=None):
89 message += chr(0) 93 message += chr(0)
90 else: 94 else:
91 message += passwd[0] 95 message += passwd[0]
92 passlen >>= 1 96 passlen >>= 1
93 97
94 retval = md5digest(message) 98 retval = md5digest(message)
95 99
96 for i in range(0, 1000): 100 for i in range(0, 1000):
97 if i & 1: 101 if i & 1:
98 message = passwd 102 message = passwd
99 else: 103 else:
100 message = retval[0:16] 104 message = retval[0:16]
101 105
102 if i % 3: 106 if i % 3:
103 message += salt 107 message += salt
104 108
105 if i % 7: 109 if i % 7:
106 message += passwd 110 message += passwd
107 111
108 if i & 1: 112 if i & 1:
109 message += retval[0:16] 113 message += retval[0:16]
@@ -111,7 +115,7 @@ def generate_md5(passwd, salt=None):
111 message += passwd 115 message += passwd
112 116
113 retval = md5digest(message) 117 retval = md5digest(message)
114 118
115 # Now make the output string 119 # Now make the output string
116 output = ap_to64((ord(retval[0]) << 16) | (ord(retval[6]) << 8) | ord(retval[12])) 120 output = ap_to64((ord(retval[0]) << 16) | (ord(retval[6]) << 8) | ord(retval[12]))
117 output += ap_to64((ord(retval[1]) << 16) | (ord(retval[7]) << 8) | ord(retval[13])) 121 output += ap_to64((ord(retval[1]) << 16) | (ord(retval[7]) << 8) | ord(retval[13]))
@@ -122,6 +126,7 @@ def generate_md5(passwd, salt=None):
122 126
123 return "%s%s$%s" % (MAGIC_TOKEN, salt, output) 127 return "%s%s$%s" % (MAGIC_TOKEN, salt, output)
124 128
129
125def test_driver(): 130def test_driver():
126 """Run this to verify that the library is functioning properly. 131 """Run this to verify that the library is functioning properly.
127 This one test case should be enough to verify the functionality. 132 This one test case should be enough to verify the functionality.