Initial checkin of new API implementation
[plcapi.git] / PLC / md5crypt.py
1 #########################################################
2 # md5crypt.py
3 #
4 # 0423.2000 by michal wallace http://www.sabren.com/
5 # based on perl's Crypt::PasswdMD5 by Luis Munoz (lem@cantv.net)
6 # based on /usr/src/libcrypt/crypt.c from FreeBSD 2.2.5-RELEASE
7 #
8 # MANY THANKS TO
9 #
10 #  Carey Evans - http://home.clear.net.nz/pages/c.evans/
11 #  Dennis Marti - http://users.starpower.net/marti1/
12 #
13 #  For the patches that got this thing working!
14 #
15 #########################################################
16 """md5crypt.py - Provides interoperable MD5-based crypt() function
17
18 SYNOPSIS
19
20         import md5crypt.py
21
22         cryptedpassword = md5crypt.md5crypt(password, salt);
23
24 DESCRIPTION
25
26 unix_md5_crypt() provides a crypt()-compatible interface to the
27 rather new MD5-based crypt() function found in modern operating systems.
28 It's based on the implementation found on FreeBSD 2.2.[56]-RELEASE and
29 contains the following license in it:
30
31  "THE BEER-WARE LICENSE" (Revision 42):
32  <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
33  can do whatever you want with this stuff. If we meet some day, and you think
34  this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
35
36 apache_md5_crypt() provides a function compatible with Apache's
37 .htpasswd files. This was contributed by Bryan Hart <bryan@eai.com>.
38
39 """
40
41 MAGIC = '$1$'                   # Magic string
42 ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
43
44 import md5
45
46 def to64 (v, n):
47     ret = ''
48     while (n - 1 >= 0):
49         n = n - 1
50         ret = ret + ITOA64[v & 0x3f]
51         v = v >> 6
52     return ret
53
54
55 def apache_md5_crypt (pw, salt):
56     # change the Magic string to match the one used by Apache
57     return unix_md5_crypt(pw, salt, '$apr1$')
58
59
60 def unix_md5_crypt(pw, salt, magic=None):
61     
62     if magic==None:
63         magic = MAGIC
64
65     # Take care of the magic string if present
66     if salt[:len(magic)] == magic:
67         salt = salt[len(magic):]
68         
69
70     # salt can have up to 8 characters:
71     import string
72     salt = string.split(salt, '$', 1)[0]
73     salt = salt[:8]
74
75     ctx = pw + magic + salt
76
77     final = md5.md5(pw + salt + pw).digest()
78
79     for pl in range(len(pw),0,-16):
80         if pl > 16:
81             ctx = ctx + final[:16]
82         else:
83             ctx = ctx + final[:pl]
84
85
86     # Now the 'weird' xform (??)
87
88     i = len(pw)
89     while i:
90         if i & 1:
91             ctx = ctx + chr(0)  #if ($i & 1) { $ctx->add(pack("C", 0)); }
92         else:
93             ctx = ctx + pw[0]
94         i = i >> 1
95
96     final = md5.md5(ctx).digest()
97     
98     # The following is supposed to make
99     # things run slower. 
100
101     # my question: WTF???
102
103     for i in range(1000):
104         ctx1 = ''
105         if i & 1:
106             ctx1 = ctx1 + pw
107         else:
108             ctx1 = ctx1 + final[:16]
109
110         if i % 3:
111             ctx1 = ctx1 + salt
112
113         if i % 7:
114             ctx1 = ctx1 + pw
115
116         if i & 1:
117             ctx1 = ctx1 + final[:16]
118         else:
119             ctx1 = ctx1 + pw
120             
121             
122         final = md5.md5(ctx1).digest()
123
124
125     # Final xform
126                                 
127     passwd = ''
128
129     passwd = passwd + to64((int(ord(final[0])) << 16)
130                            |(int(ord(final[6])) << 8)
131                            |(int(ord(final[12]))),4)
132
133     passwd = passwd + to64((int(ord(final[1])) << 16)
134                            |(int(ord(final[7])) << 8)
135                            |(int(ord(final[13]))), 4)
136
137     passwd = passwd + to64((int(ord(final[2])) << 16)
138                            |(int(ord(final[8])) << 8)
139                            |(int(ord(final[14]))), 4)
140
141     passwd = passwd + to64((int(ord(final[3])) << 16)
142                            |(int(ord(final[9])) << 8)
143                            |(int(ord(final[15]))), 4)
144
145     passwd = passwd + to64((int(ord(final[4])) << 16)
146                            |(int(ord(final[10])) << 8)
147                            |(int(ord(final[5]))), 4)
148
149     passwd = passwd + to64((int(ord(final[11]))), 2)
150
151
152     return magic + salt + '$' + passwd
153
154
155 ## assign a wrapper function:
156 md5crypt = unix_md5_crypt
157
158 if __name__ == "__main__":
159     print unix_md5_crypt("cat", "hat")