python-aprmd5
is a Python extension written in C that wraps the MD5 routines of
the Apache Portable Runtime (APR) Utility Library (libaprutil
) and exposes them
to the Python interpreter as the module aprmd5
. The main purpose of writing
python-aprmd5
in the first place has been to expose the function
apr_md5_encode()
, which generates salted crypt-style hashes using a version of
the MD5 hash algorithm that was modified especially for the APR project.
The resulting hashes always start with the prefix characters $apr1$
in order
to distinguish them from the result of various other crypt()
variants, which
use other prefixes. For instance, for input "foo" and salt "mYJd83wW",
apr_md5_encode()
produces the following hash:
$apr1$mYJd83wW$IO.6aK3G0d4mHxcImhPX50
Hashes like this are typically generated by the command line utility htpasswd
,
which is part of the Apache HTTP server project. The hashes encrypt user
passwords that are used by the Apache HTTP server for basic user authentication.
For completeness sake, python-aprmd5
exposes not only apr_md5_encode()
but most
of the other functions of libaprutil
's MD5 routines as well. Where those
functions are concerned with the original, unmodified MD5 algorithm, this is
mostly a duplication of effort as that algorithm can be easily obtained through
the Python Standard Library module hashlib
. In fact I advise against using
python-aprmd5
if you are interested in the original MD5 algorithm only.
This is python-aprmd5
0.2.1.
Changes in this release:
- patch for setup.py by Juan A. Diaz to auto-detect Mac OS X and Debian
- the project has moved to GitHub
- no functional changes
For more details see the ChangeLog
document.
As of version 0.2, I consider python-aprmd5
to be feature complete. Unless a
change in libaprutil
breaks python-aprmd5
, it is therefore rather unlikely that
there will ever be a version 0.3. Maintenance releases 0.2.x will occur only if
bugs are detected, or if I can be motivated to fix the crappy build process.
python-aprmd5
is licensed under the GNU General Public License (GPLv3). You
should have received a copy of the license along with the python-aprmd5
module
distribution (see the file COPYING
inside the distribution). If not, see
http://www.gnu.org/licenses/.
The source code for python-aprmd5
can be downloaded from GitHub. The repository includes project files to hack python-aprmd5
in Eclipse using PyDev.
Obviously, python-aprmd5
depends on the presence of libaprutil
. At compile time,
both the headers and the library file must be present. At runtime, the library
file is sufficient.
No formal tests for compatibility with certain versions of libaprutil
have been
performed. The earliest version that I have casually used on my system at home
(a Mac OS X box) is libaprutil
0.9. Another version that I have used for casual
testing on a Debian virtual box is libaprutil
1.3.5. As the interface of the
MD5 routines has not changed for a long time, and even across major versions of
libaprutil
, it is reasonable to expect that python-aprmd5
will work with pretty
much all versions of libaprutil
.
See the INSTALL
document for details on how to build python-aprmd5
against
different versions of libaprutil
.
python-aprmd5
implements both the 2.x and the 3.x versions of Python's C-API,
so in theory it should work with all versions of Python 2.x and 3.x. In
practice, no formal tests have been performed to verify this statement. The
earliest version of Python that I have used to build python-aprmd5
with has been
2.5.1, and the latest version has been 3.1.1 (both on Mac OS X 10.5).
On some platforms, password_validate()
incorrectly returns True
if it is fed
with an empty hash, regardless of the password that is being validated. This is
probably a bug in the system's crypt()
function, exposed by the way how
libaprutil
calls that function. I have found this behaviour on Debian lenny, the
issue is reported at the Debian bugtracker.
Example 1: Usage of apr_md5_encode()
.
from aprmd5 import md5_encode
password = "foo"
salt = "mYJd83wW"
# result will be "$apr1$mYJd83wW$IO.6aK3G0d4mHxcImhPX50"
result = md5_encode(password, salt)
Example 2: Usage of password_validate()
.
from aprmd5 import password_validate
password = "foo"
hash = "$apr1$mYJd83wW$IO.6aK3G0d4mHxcImhPX50"
# result will be True
result = password_validate(password, hash)
Example 3: Usage of regular MD5 algorithm. The example uses the b""
notation
from Python 3.
from aprmd5 import md5
m1 = md5(b"foo")
m2 = md5()
m2.update(b"foo")
# result1 and result2 will both be "acbd18db4cc2f85cedef654fccc4a4d8"
result1 = m1.hexdigest()
result2 = m2.hexdigest()