Table of Contents
-----------------
1. Introduction
2. Release notes
3. Project status
4. License and source code
5. Dependencies
6. libaprutil versions
7. Python versions
8. Limitations and known bugs
9. Examples


Introduction
------------
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.


Release notes
-------------
This is python-aprmd5 0.2.

Changes in this release:
- [feature] replace wrappers related to original MD5 by md5 object [fixes #70]
- [bugfix] fixed a few incompatibilities with Python 2.6 and earlier

For more details see the ChangeLog document.


Project status
--------------
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.


License and source code
-----------------------
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 its website
http://www.herzbube.ch/python-aprmd5/. The source files are packaged into a tar
ball using the Distutils Python module. Alternatively you may also get the
source code (including project files to hack python-aprmd5 in Eclipse) from this
git respository: http://herzbube.ch/git/python-aprmd5/


Dependencies
------------
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.


libaprutil versions
-------------------
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 versions
---------------
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).


Limitations and known bugs
--------------------------
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 here: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=550360.


Examples
--------
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()
