summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Crute <mcrute@gmail.com>2009-04-21 21:44:29 -0400
committerMike Crute <mcrute@gmail.com>2009-04-21 21:44:29 -0400
commite179fec1f865ede349873a436d5eb2ed4e98da9b (patch)
tree5f9cca6b9fc2d51f06cbf2e6df45d68e11c38bf9
downloadpyapache-e179fec1f865ede349873a436d5eb2ed4e98da9b.tar.bz2
pyapache-e179fec1f865ede349873a436d5eb2ed4e98da9b.tar.xz
pyapache-e179fec1f865ede349873a436d5eb2ed4e98da9b.zip
Initial import from subversion.
-rw-r--r--__init__.py0
-rw-r--r--htpasswd.py35
-rwxr-xr-xmd5.py134
-rw-r--r--password.py31
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"""
3Apache htaccess File Library
4by Mike Crute on July 12, 2008
5for SoftGroup Interactive, Inc.
6Released under the terms of the BSD license.
7
8A collection of classes and functions to manipulate apache htaccess files.
9"""
10
11__all__ = [ "generate_user" ]
12
13def 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
32def generate_user(username, passwd, ctype="crypt"):
33 """Generate a single htaccess line.
34 """
35 return "%s:%s" % (username, hash_password(passwd, ctype))
diff --git a/md5.py b/md5.py
new file mode 100755
index 0000000..917612e
--- /dev/null
+++ b/md5.py
@@ -0,0 +1,134 @@
1#!/usr/bin/python
2"""
3Apache Portable Runtime (APR) MD5 Calculation
4by Mike Crute on July 12, 2008
5for SoftGroup Interactive, Inc.
6Released under the terms of the BSD license.
7
8This is basically a feature-complete implementation of the MD5
9algorithm used by the APR, Apache and the htpasswd tool. APR
10takes a different approach to generating MD5 sums which is a
11little bit wierd.
12
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.
15You'll find it in apache subversion under
16/ap/apr-util/crypto/apr_md5.c
17"""
18
19import string
20from hashlib import md5
21from random import random
22from math import floor
23
24__all__ = [ "generate_md5", "generate_salt", "generate_short_salt" ]
25
26# Defined as such for brevity
27md5digest = lambda x: md5(x).digest()
28
29def 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
43def 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
49def 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
58def 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
125def 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
133if __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"""
3Apache Password Hash Generation Functions
4by Mike Crute on July 12, 2008
5for SoftGroup Interactive, Inc.
6Released under the terms of the BSD license.
7
8A collection of functions used to generate Apache-style password hashes.
9Algorithm information was collected for various sources around the web
10and from analysis of the APR C code.
11"""
12
13def 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
20def 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
27def md5_password(passwd):
28 """Generate Apache-style MD5 password hash.
29 """
30 from apachelib.md5 import generate_md5
31 return generate_md5(passwd)