diff options
author | Mike Crute <mcrute@gmail.com> | 2009-04-21 21:44:29 -0400 |
---|---|---|
committer | Mike Crute <mcrute@gmail.com> | 2009-04-21 21:44:29 -0400 |
commit | e179fec1f865ede349873a436d5eb2ed4e98da9b (patch) | |
tree | 5f9cca6b9fc2d51f06cbf2e6df45d68e11c38bf9 | |
download | pyapache-e179fec1f865ede349873a436d5eb2ed4e98da9b.tar.bz2 pyapache-e179fec1f865ede349873a436d5eb2ed4e98da9b.tar.xz pyapache-e179fec1f865ede349873a436d5eb2ed4e98da9b.zip |
Initial import from subversion.
-rw-r--r-- | __init__.py | 0 | ||||
-rw-r--r-- | htpasswd.py | 35 | ||||
-rwxr-xr-x | md5.py | 134 | ||||
-rw-r--r-- | password.py | 31 |
4 files changed, 200 insertions, 0 deletions
diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/__init__.py | |||
diff --git a/htpasswd.py b/htpasswd.py new file mode 100644 index 0000000..3641c34 --- /dev/null +++ b/htpasswd.py | |||
@@ -0,0 +1,35 @@ | |||
1 | #!/usr/bin/python | ||
2 | """ | ||
3 | Apache htaccess File Library | ||
4 | by Mike Crute on July 12, 2008 | ||
5 | for SoftGroup Interactive, Inc. | ||
6 | Released under the terms of the BSD license. | ||
7 | |||
8 | A collection of classes and functions to manipulate apache htaccess files. | ||
9 | """ | ||
10 | |||
11 | __all__ = [ "generate_user" ] | ||
12 | |||
13 | def hash_password(passwd, ctype="crypt"): | ||
14 | """Create an Apache-style password hash. | ||
15 | This is basically just a simplfified interface to apachelib.password | ||
16 | for use in generating htaccess files. Valid ctypes are crypt, sha and | ||
17 | md5. | ||
18 | """ | ||
19 | if ctype is "crypt": | ||
20 | from apachelib.password import crypt_password | ||
21 | return crypt_password(passwd) | ||
22 | elif ctype is "sha": | ||
23 | from apachelib.password import sha_password | ||
24 | return sha_password(passwd) | ||
25 | elif ctype is "md5": | ||
26 | from apachelib.password import md5_password | ||
27 | return md5_password(passwd) | ||
28 | |||
29 | # We should never get here | ||
30 | raise ValueError("%s is not a valid value for ctype." % ctype) | ||
31 | |||
32 | def generate_user(username, passwd, ctype="crypt"): | ||
33 | """Generate a single htaccess line. | ||
34 | """ | ||
35 | return "%s:%s" % (username, hash_password(passwd, ctype)) | ||
@@ -0,0 +1,134 @@ | |||
1 | #!/usr/bin/python | ||
2 | """ | ||
3 | Apache Portable Runtime (APR) MD5 Calculation | ||
4 | by Mike Crute on July 12, 2008 | ||
5 | for SoftGroup Interactive, Inc. | ||
6 | Released under the terms of the BSD license. | ||
7 | |||
8 | This is basically a feature-complete implementation of the MD5 | ||
9 | algorithm used by the APR, Apache and the htpasswd tool. APR | ||
10 | takes a different approach to generating MD5 sums which is a | ||
11 | little bit wierd. | ||
12 | |||
13 | This is a pythonic adaption of the C code in the APR library. | ||
14 | If there are any questions please refer directly to the C code. | ||
15 | You'll find it in apache subversion under | ||
16 | /ap/apr-util/crypto/apr_md5.c | ||
17 | """ | ||
18 | |||
19 | import string | ||
20 | from hashlib import md5 | ||
21 | from random import random | ||
22 | from math import floor | ||
23 | |||
24 | __all__ = [ "generate_md5", "generate_salt", "generate_short_salt" ] | ||
25 | |||
26 | # Defined as such for brevity | ||
27 | md5digest = lambda x: md5(x).digest() | ||
28 | |||
29 | def ap_to64(input, count=4): | ||
30 | """Weird-ass implementation of base64 conversion used by the | ||
31 | APR library. | ||
32 | """ | ||
33 | chars = "./0123456789%s%s" % (string.uppercase, string.lowercase) | ||
34 | output = "" | ||
35 | input = int(input) # Need ints to do binary math | ||
36 | |||
37 | for i in range(0, count): | ||
38 | output += chars[input & 0x3f] # Take 6 bits right | ||
39 | input >>= 6 # shift by 6 bits | ||
40 | |||
41 | return output | ||
42 | |||
43 | def generate_short_salt(): | ||
44 | """Generate a short, 2-character salt. | ||
45 | This is suitable for use in the htpasswd crypt routine. | ||
46 | """ | ||
47 | return generate_salt()[0:2] | ||
48 | |||
49 | def generate_salt(): | ||
50 | """Mine a little salt for your passwords. | ||
51 | Returns 8 random characters in base64. It is 4 + 4 because the original | ||
52 | implmentation was in C and they wanted it it fit nicely in an integer. | ||
53 | """ | ||
54 | salt = ap_to64(floor(random() * 16777215)) | ||
55 | salt += ap_to64(floor(random() * 16777215)) | ||
56 | return salt | ||
57 | |||
58 | def generate_md5(passwd, salt=None): | ||
59 | """Generate an APRfied MD5 hash. | ||
60 | This was adapted directly from the C code in the APR library. | ||
61 | It was made more pythonic where possible but please reference | ||
62 | the APR code for a better understanding of what's going on. | ||
63 | """ | ||
64 | # I know not what this means or how it may change in the future | ||
65 | # (well OK, its the APR version that generated the hash) and | ||
66 | # I don't think it is really "checked" by Apache but I'm too | ||
67 | # lazy to confirm this. | ||
68 | MAGIC_TOKEN = "$apr1$" | ||
69 | |||
70 | # Mainly just used for testing but why not leave it? | ||
71 | salt = salt if salt else generate_salt() | ||
72 | |||
73 | # Start with our password in the clear, a little magic and | ||
74 | # a pinch of salt | ||
75 | message = "%s%s%s" % (passwd, MAGIC_TOKEN, salt) | ||
76 | |||
77 | # Then just as many characters of the MD5(pw, salt, pw) | ||
78 | retval = md5digest(passwd + salt + passwd) | ||
79 | passlen = len(passwd) | ||
80 | while passlen > 0: | ||
81 | end = 16 if passlen > 16 else passlen | ||
82 | message += retval[0:end] | ||
83 | passlen -= 16 | ||
84 | |||
85 | # Then something really wierd | ||
86 | passlen = len(passwd) | ||
87 | while passlen != 0: | ||
88 | if passlen & 1: | ||
89 | message += chr(0) | ||
90 | else: | ||
91 | message += passwd[0] | ||
92 | passlen >>= 1 | ||
93 | |||
94 | retval = md5digest(message) | ||
95 | |||
96 | for i in range(0, 1000): | ||
97 | if i & 1: | ||
98 | message = passwd | ||
99 | else: | ||
100 | message = retval[0:16] | ||
101 | |||
102 | if i % 3: | ||
103 | message += salt | ||
104 | |||
105 | if i % 7: | ||
106 | message += passwd | ||
107 | |||
108 | if i & 1: | ||
109 | message += retval[0:16] | ||
110 | else: | ||
111 | message += passwd | ||
112 | |||
113 | retval = md5digest(message) | ||
114 | |||
115 | # Now make the output string | ||
116 | 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])) | ||
118 | output += ap_to64((ord(retval[2]) << 16) | (ord(retval[8]) << 8) | ord(retval[14])) | ||
119 | output += ap_to64((ord(retval[3]) << 16) | (ord(retval[9]) << 8) | ord(retval[15])) | ||
120 | output += ap_to64((ord(retval[4]) << 16) | (ord(retval[10]) << 8) | ord(retval[5])) | ||
121 | output += ap_to64(ord(retval[11]), 2) | ||
122 | |||
123 | return "%s%s$%s" % (MAGIC_TOKEN, salt, output) | ||
124 | |||
125 | def test_driver(): | ||
126 | """Run this to verify that the library is functioning properly. | ||
127 | This one test case should be enough to verify the functionality. | ||
128 | """ | ||
129 | output = generate_md5("test", "yTbof...") | ||
130 | assert output == "$apr1$yTbof...$r3r2AZWwYNbWRfNmLfrEh1" | ||
131 | print "Passed the test!" | ||
132 | |||
133 | if __name__ == "__main__": | ||
134 | test_driver() | ||
diff --git a/password.py b/password.py new file mode 100644 index 0000000..3570cf4 --- /dev/null +++ b/password.py | |||
@@ -0,0 +1,31 @@ | |||
1 | #!/usr/bin/python | ||
2 | """ | ||
3 | Apache Password Hash Generation Functions | ||
4 | by Mike Crute on July 12, 2008 | ||
5 | for SoftGroup Interactive, Inc. | ||
6 | Released under the terms of the BSD license. | ||
7 | |||
8 | A collection of functions used to generate Apache-style password hashes. | ||
9 | Algorithm information was collected for various sources around the web | ||
10 | and from analysis of the APR C code. | ||
11 | """ | ||
12 | |||
13 | def crypt_password(passwd): | ||
14 | """Generate Apache-style CRYPT password hash. | ||
15 | """ | ||
16 | from crypt import crypt | ||
17 | from apachelib.md5 import generate_short_salt | ||
18 | return crypt(passwd, generate_short_salt()) | ||
19 | |||
20 | def sha_password(passwd): | ||
21 | """Generate Apache-style SHA1 password hash. | ||
22 | """ | ||
23 | from hashlib import sha1 | ||
24 | from base64 import b64encode | ||
25 | return "{SHA}%s" % b64encode(sha1(passwd).digest()) | ||
26 | |||
27 | def md5_password(passwd): | ||
28 | """Generate Apache-style MD5 password hash. | ||
29 | """ | ||
30 | from apachelib.md5 import generate_md5 | ||
31 | return generate_md5(passwd) | ||