remove old password hashing code + support lib, replace it with dummy code
authorThomas Waldmann <tw AT waldmann-edv DOT de>
Sat, 19 Jan 2013 04:23:42 +0100
changeset 1912aff38f7e35b7
parent 1911 a1c0c90d5c52
child 1913 dbcadc76561a
remove old password hashing code + support lib, replace it with dummy code
MoinMoin/_tests/test_user.py
MoinMoin/util/_tests/test_crypto.py
MoinMoin/util/_tests/test_md5crypt.py
MoinMoin/util/crypto.py
MoinMoin/util/md5crypt.py
docs/admin/configure.rst
     1.1 --- a/MoinMoin/_tests/test_user.py	Thu Jan 17 12:17:13 2013 +0100
     1.2 +++ b/MoinMoin/_tests/test_user.py	Sat Jan 19 04:23:42 2013 +0100
     1.3 @@ -78,105 +78,19 @@
     1.4          theUser = user.User(name=name, password=password)
     1.5          assert theUser.valid
     1.6  
     1.7 -    def test_auth_with_ssha_stored_password(self):
     1.8 +    def test_login(self):
     1.9          """
    1.10 -        Create user with {SSHA} password and check that user can login.
    1.11 +        Create user with some password and check that user can login.
    1.12          """
    1.13          # Create test user
    1.14          name = u'Test User'
    1.15 -        # pass = 12345
    1.16 -        # salt = salt
    1.17 -        password = '{SSHA}x4YEGdfI4i0qROaY3NTHCmwSJY5zYWx0'
    1.18 -        self.createUser(name, password, True)
    1.19 +        password = '12345'
    1.20 +        salt = 'salt'
    1.21 +        pw_hash = ''
    1.22 +        self.createUser(name, pw_hash, True)
    1.23  
    1.24          # Try to "login"
    1.25 -        theuser = user.User(name=name, password='12345')
    1.26 -        assert theuser.valid
    1.27 -
    1.28 -    def test_auth_with_apr1_stored_password(self):
    1.29 -        """
    1.30 -        Create user with {APR1} password and check that user can login.
    1.31 -        """
    1.32 -        # Create test user
    1.33 -        name = u'Test User'
    1.34 -        # generated with "htpasswd -nbm blaze 12345"
    1.35 -        password = '{APR1}$apr1$NG3VoiU5$PSpHT6tV0ZMKkSZ71E3qg.' # 12345
    1.36 -        self.createUser(name, password, True)
    1.37 -
    1.38 -        # Try to "login"
    1.39 -        theuser = user.User(name=name, password='12345')
    1.40 -        assert theuser.valid
    1.41 -
    1.42 -    def test_auth_with_md5_stored_password(self):
    1.43 -        """
    1.44 -        Create user with {MD5} password and check that user can login.
    1.45 -        """
    1.46 -        # Create test user
    1.47 -        name = u'Test User'
    1.48 -        password = '{MD5}$1$salt$etVYf53ma13QCiRbQOuRk/' # 12345
    1.49 -        self.createUser(name, password, True)
    1.50 -
    1.51 -        # Try to "login"
    1.52 -        theuser = user.User(name=name, password='12345')
    1.53 -        assert theuser.valid
    1.54 -
    1.55 -    def test_auth_with_des_stored_password(self):
    1.56 -        """
    1.57 -        Create user with {DES} password and check that user can login.
    1.58 -        """
    1.59 -        # Create test user
    1.60 -        name = u'Test User'
    1.61 -        # generated with "htpasswd -nbd blaze 12345"
    1.62 -        password = '{DES}gArsfn7O5Yqfo' # 12345
    1.63 -        self.createUser(name, password, True)
    1.64 -
    1.65 -        try:
    1.66 -            import crypt
    1.67 -            # Try to "login"
    1.68 -            theuser = user.User(name=name, password='12345')
    1.69 -            assert theuser.valid
    1.70 -        except ImportError:
    1.71 -            pytest.skip("Platform does not provide crypt module!")
    1.72 -
    1.73 -    def test_auth_with_ssha256_stored_password(self):
    1.74 -        """
    1.75 -        Create user with {SSHA256} password and check that user can login.
    1.76 -        """
    1.77 -        # Create test user
    1.78 -        name = u'Test User'
    1.79 -        # generated with online sha256 tool
    1.80 -        # pass: 12345
    1.81 -        # salt: salt
    1.82 -        # base64 encoded
    1.83 -        password = '{SSHA256}r4ONZUfEyn9MUkcyDQkQ5MBNpdIerM24MasxFpuQBaFzYWx0'
    1.84 -
    1.85 -        self.createUser(name, password, True)
    1.86 -
    1.87 -        # Try to "login"
    1.88 -        theuser = user.User(name=name, password='12345')
    1.89 -        assert theuser.valid
    1.90 -
    1.91 -    def test_regression_user_password_started_with_sha(self):
    1.92 -        # This is regression test for bug in function 'user.create_user'.
    1.93 -        #
    1.94 -        # This function does not encode passwords which start with '{SHA}'
    1.95 -        # It treats them as already encoded SHA hashes.
    1.96 -        #
    1.97 -        # If user during registration specifies password starting with '{SHA}'
    1.98 -        # this password will not get encoded and user object will get saved with empty enc_password
    1.99 -        # field.
   1.100 -        #
   1.101 -        # Such situation leads to "KeyError: 'enc_password'" during
   1.102 -        # user authentication.
   1.103 -
   1.104 -        # Any Password begins with the {SHA} symbols led to
   1.105 -        # "KeyError: 'enc_password'" error during user authentication.
   1.106 -        user_name = u'moin'
   1.107 -        user_password = u'{SHA}LKM56'
   1.108 -        user.create_user(user_name, user_password, u'moin@moinmo.in', u'')
   1.109 -
   1.110 -        # Try to "login"
   1.111 -        theuser = user.User(name=user_name, password=user_password)
   1.112 +        theuser = user.User(name=name, password=password)
   1.113          assert theuser.valid
   1.114  
   1.115      def testSubscriptionSubscribedPage(self):
   1.116 @@ -202,72 +116,6 @@
   1.117          theUser.subscribe(pagename)
   1.118          assert not theUser.is_subscribed_to([testPagename]) # list(!) of pages to check
   1.119  
   1.120 -    def test_upgrade_password_from_ssha_to_ssha256(self):
   1.121 -        """
   1.122 -        Create user with {SSHA} password and check that logging in
   1.123 -        upgrades to {SSHA256}.
   1.124 -        """
   1.125 -        name = u'/no such user/'
   1.126 -        # pass = 'MoinMoin', salt = '12345'
   1.127 -        password = '{SSHA}xkDIIx1I7A4gC98Vt/+UelIkTDYxMjM0NQ=='
   1.128 -        self.createUser(name, password, True)
   1.129 -
   1.130 -        theuser = user.User(name=name, password='MoinMoin')
   1.131 -        assert theuser.enc_password[:9] == '{SSHA256}'
   1.132 -
   1.133 -    def test_upgrade_password_from_sha_to_ssha256(self):
   1.134 -        """
   1.135 -        Create user with {SHA} password and check that logging in
   1.136 -        upgrades to {SSHA256}.
   1.137 -        """
   1.138 -        name = u'/no such user/'
   1.139 -        password = '{SHA}jLIjfQZ5yojbZGTqxg2pY0VROWQ=' # 12345
   1.140 -        self.createUser(name, password, True)
   1.141 -
   1.142 -        theuser = user.User(name=name, password='12345')
   1.143 -        assert theuser.enc_password[:9] == '{SSHA256}'
   1.144 -
   1.145 -    def test_upgrade_password_from_apr1_to_ssha256(self):
   1.146 -        """
   1.147 -        Create user with {APR1} password and check that logging in
   1.148 -        upgrades to {SSHA256}.
   1.149 -        """
   1.150 -        # Create test user
   1.151 -        name = u'Test User'
   1.152 -        # generated with "htpasswd -nbm blaze 12345"
   1.153 -        password = '{APR1}$apr1$NG3VoiU5$PSpHT6tV0ZMKkSZ71E3qg.' # 12345
   1.154 -        self.createUser(name, password, True)
   1.155 -
   1.156 -        theuser = user.User(name=name, password='12345')
   1.157 -        assert theuser.enc_password[:9] == '{SSHA256}'
   1.158 -
   1.159 -    def test_upgrade_password_from_md5_to_ssha256(self):
   1.160 -        """
   1.161 -        Create user with {MD5} password and check that logging in
   1.162 -        upgrades to {SSHA}.
   1.163 -        """
   1.164 -        # Create test user
   1.165 -        name = u'Test User'
   1.166 -        password = '{MD5}$1$salt$etVYf53ma13QCiRbQOuRk/' # 12345
   1.167 -        self.createUser(name, password, True)
   1.168 -
   1.169 -        theuser = user.User(name=name, password='12345')
   1.170 -        assert theuser.enc_password[:9] == '{SSHA256}'
   1.171 -
   1.172 -    def test_upgrade_password_from_des_to_ssha256(self):
   1.173 -        """
   1.174 -        Create user with {DES} password and check that logging in
   1.175 -        upgrades to {SSHA}.
   1.176 -        """
   1.177 -        # Create test user
   1.178 -        name = u'Test User'
   1.179 -        # generated with "htpasswd -nbd blaze 12345"
   1.180 -        password = '{DES}gArsfn7O5Yqfo' # 12345
   1.181 -        self.createUser(name, password, True)
   1.182 -
   1.183 -        theuser = user.User(name=name, password='12345')
   1.184 -        assert theuser.enc_password[:9] == '{SSHA256}'
   1.185 -
   1.186      # Bookmarks -------------------------------------------------------
   1.187  
   1.188      def test_bookmark(self):
     2.1 --- a/MoinMoin/util/_tests/test_crypto.py	Thu Jan 17 12:17:13 2013 +0100
     2.2 +++ b/MoinMoin/util/_tests/test_crypto.py	Sat Jan 19 04:23:42 2013 +0100
     2.3 @@ -29,49 +29,6 @@
     2.4          assert result == expected, ('Expected length "%(expected)s" but got "%(result)s"') % locals()
     2.5  
     2.6  
     2.7 -class TestEncodePassword(object):
     2.8 -    """crypto: encode passwords tests"""
     2.9 -
    2.10 -    def testAscii(self):
    2.11 -        """user: encode ascii password"""
    2.12 -        # u'MoinMoin' and 'MoinMoin' should be encoded to same result
    2.13 -        expected = "{SSHA256}n0JB8FCTQCpQeg0bmdgvTGwPKvxm8fVNjSRD+JGNs50xMjM0NQ=="
    2.14 -
    2.15 -        result = crypto.crypt_password("MoinMoin", salt='12345')
    2.16 -        assert result == expected
    2.17 -        result = crypto.crypt_password(u"MoinMoin", salt='12345')
    2.18 -        assert result == expected
    2.19 -
    2.20 -    def testUnicode(self):
    2.21 -        """ user: encode unicode password """
    2.22 -        result = crypto.crypt_password(u'סיסמה סודית בהחלט', salt='12345') # Hebrew
    2.23 -        expected = "{SSHA256}pdYvYv+4Vph259sv/HAm7zpZTv4sBKX9ITOX/m00HMsxMjM0NQ=="
    2.24 -        assert result == expected
    2.25 -
    2.26 -    def testupgradepassword(self):
    2.27 -        """ return new password hash with better hash """
    2.28 -        result = crypto.upgrade_password(u'MoinMoin', "junk_hash")
    2.29 -        assert result.startswith('{SSHA256}')
    2.30 -
    2.31 -    def testvalidpassword(self):
    2.32 -        """ validate user password """
    2.33 -        hash_val = crypto.crypt_password(u"MoinMoin", salt='12345')
    2.34 -        result = crypto.valid_password(u'MoinMoin', hash_val)
    2.35 -        assert result
    2.36 -        with pytest.raises(ValueError):
    2.37 -            invalid_result = crypto.valid_password("MoinMoin", '{junk_value}')
    2.38 -
    2.39 -    def testvalidpassword2(self):
    2.40 -        """ validate user password """
    2.41 -        hash_val = crypto.crypt_password(u"MoinMoin")
    2.42 -        result = crypto.valid_password('MoinMoin', hash_val)
    2.43 -        assert result
    2.44 -        result = crypto.valid_password('WrongPassword', hash_val)
    2.45 -        assert not result
    2.46 -        with pytest.raises(ValueError):
    2.47 -            invalid_result = crypto.valid_password("MoinMoin", '{junk_value}')
    2.48 -
    2.49 -
    2.50  class TestToken(object):
    2.51      """ tests for the generated tokens """
    2.52  
     3.1 --- a/MoinMoin/util/_tests/test_md5crypt.py	Thu Jan 17 12:17:13 2013 +0100
     3.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.3 @@ -1,28 +0,0 @@
     3.4 -# -*- coding: utf-8 -*-
     3.5 -# Copyright: 2011 Prashant Kumar <contactprashantat AT gmail DOT com>
     3.6 -# License: GNU GPL v2 (or any later version), see LICENSE.txt for details.
     3.7 -
     3.8 -"""
     3.9 -MoinMoin - MoinMoin.util.md5crypt Tests
    3.10 -"""
    3.11 -
    3.12 -
    3.13 -import pytest
    3.14 -from MoinMoin.util import md5crypt
    3.15 -
    3.16 -def test_unix_md5_crypt():
    3.17 -    # when magic != None
    3.18 -    result = md5crypt.unix_md5_crypt('test_pass', 'Moin_test', '$test_magic$')
    3.19 -    expected = '$test_magic$Moin_tes$JRfmeHgnmCVhVYW.bTtiY1'
    3.20 -    assert result == expected
    3.21 -
    3.22 -    # when magic == None
    3.23 -    result = md5crypt.unix_md5_crypt('test_pass', 'Moin_test', None)
    3.24 -    expected = '$1$Moin_tes$hArc67BzmDWtyWWKO5uxQ1'
    3.25 -    assert result == expected
    3.26 -
    3.27 -def test_apache_md5_crypt():
    3.28 -    # Here magic == '$apr1$'
    3.29 -    result = md5crypt.apache_md5_crypt('test_pass', 'Moin_test')
    3.30 -    expected = '$apr1$Moin_tes$4/5zV8nADrNv3BJcY1rZX1'
    3.31 -    assert result == expected
     4.1 --- a/MoinMoin/util/crypto.py	Thu Jan 17 12:17:13 2013 +0100
     4.2 +++ b/MoinMoin/util/crypto.py	Sat Jan 19 04:23:42 2013 +0100
     4.3 @@ -13,8 +13,6 @@
     4.4  
     4.5  - generate strong, salted cryptographic password hashes for safe pw storage
     4.6  - verify cleartext password against any supported crypto (see METHODS)
     4.7 -- support old (weak) password crypto so one can import existing password
     4.8 -  databases
     4.9  - supports password hash upgrades to stronger methods if the cleartext
    4.10    password is available (usually at login time)
    4.11  - generate password recovery tokens
    4.12 @@ -26,27 +24,11 @@
    4.13  
    4.14  from __future__ import absolute_import, division
    4.15  
    4.16 -import base64
    4.17  import hashlib
    4.18  import hmac
    4.19  import random
    4.20  import time
    4.21  
    4.22 -# Note: have the (strong) method that crypt_password() uses at index 0:
    4.23 -METHODS = ['{SSHA256}', '{SSHA}', '{SHA}', ]
    4.24 -
    4.25 -try:
    4.26 -    from . import md5crypt
    4.27 -    METHODS.extend(['{APR1}', '{MD5}', ])
    4.28 -except ImportError:
    4.29 -    pass
    4.30 -
    4.31 -try:
    4.32 -    import crypt
    4.33 -    METHODS.extend(['{DES}', ])
    4.34 -except ImportError:
    4.35 -    pass
    4.36 -
    4.37  from uuid import uuid4
    4.38  
    4.39  make_uuid = lambda: unicode(uuid4().hex)
    4.40 @@ -79,15 +61,9 @@
    4.41      :param password: cleartext password [unicode]
    4.42      :param salt: salt for the password [str] or None to generate a random salt
    4.43      :rtype: str
    4.44 -    :returns: the SSHA256 password hash
    4.45 +    :returns: the password hash
    4.46      """
    4.47 -    password = password.encode('utf-8')
    4.48 -    if salt is None:
    4.49 -        salt = random_string(32)
    4.50 -    assert isinstance(salt, str)
    4.51 -    h = hashlib.new('sha256', password)
    4.52 -    h.update(salt)
    4.53 -    return '{SSHA256}' + base64.encodestring(h.digest() + salt).rstrip()
    4.54 +    return 'foobar' # TODO
    4.55  
    4.56  
    4.57  def upgrade_password(password, pw_hash):
    4.58 @@ -99,10 +75,7 @@
    4.59      :rtype: str
    4.60      :returns: new password hash (or None, if unchanged)
    4.61      """
    4.62 -    if not pw_hash.startswith('{SSHA256}'):
    4.63 -        # pw_hash using some old hash method, upgrade to better method
    4.64 -        return crypt_password(password)
    4.65 -
    4.66 +    # TODO
    4.67  
    4.68  def valid_password(password, pw_hash):
    4.69      """
    4.70 @@ -113,47 +86,7 @@
    4.71      :rtype: bool
    4.72      :returns: password is valid
    4.73      """
    4.74 -    # encode password
    4.75 -    pw_utf8 = password.encode('utf-8')
    4.76 -
    4.77 -    for method in METHODS:
    4.78 -        if pw_hash.startswith(method):
    4.79 -            d = pw_hash[len(method):]
    4.80 -            if method == '{SSHA256}':
    4.81 -                ph = base64.decodestring(d)
    4.82 -                # ph is of the form "<hash><salt>"
    4.83 -                salt = ph[32:]
    4.84 -                h = hashlib.new('sha256', pw_utf8)
    4.85 -                h.update(salt)
    4.86 -                enc = base64.encodestring(h.digest() + salt).rstrip()
    4.87 -            elif method == '{SSHA}':
    4.88 -                ph = base64.decodestring(d)
    4.89 -                # ph is of the form "<hash><salt>"
    4.90 -                salt = ph[20:]
    4.91 -                h = hashlib.new('sha1', pw_utf8)
    4.92 -                h.update(salt)
    4.93 -                enc = base64.encodestring(h.digest() + salt).rstrip()
    4.94 -            elif method == '{SHA}':
    4.95 -                h = hashlib.new('sha1', pw_utf8)
    4.96 -                enc = base64.encodestring(h.digest()).rstrip()
    4.97 -            elif method == '{APR1}':
    4.98 -                # d is of the form "$apr1$<salt>$<hash>"
    4.99 -                salt = d.split('$')[2]
   4.100 -                enc = md5crypt.apache_md5_crypt(pw_utf8, salt.encode('ascii'))
   4.101 -            elif method == '{MD5}':
   4.102 -                # d is of the form "$1$<salt>$<hash>"
   4.103 -                salt = d.split('$')[2]
   4.104 -                enc = md5crypt.unix_md5_crypt(pw_utf8, salt.encode('ascii'))
   4.105 -            elif method == '{DES}':
   4.106 -                # d is 2 characters salt + 11 characters hash
   4.107 -                salt = d[:2]
   4.108 -                enc = crypt.crypt(pw_utf8, salt.encode('ascii'))
   4.109 -            else:
   4.110 -                raise ValueError("missing password hash method {0} handler".format(method))
   4.111 -            return pw_hash == method + enc
   4.112 -    else:
   4.113 -        idx = pw_hash.index('}') + 1
   4.114 -        raise ValueError("unsupported password hash method {0!r}".format(pw_hash[:idx]))
   4.115 +    return True # TODO
   4.116  
   4.117  
   4.118  # password recovery token
     5.1 --- a/MoinMoin/util/md5crypt.py	Thu Jan 17 12:17:13 2013 +0100
     5.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.3 @@ -1,172 +0,0 @@
     5.4 -#########################################################
     5.5 -# md5crypt.py
     5.6 -#
     5.7 -# 0423.2000 by michal wallace http://www.sabren.com/
     5.8 -# based on perl's Crypt::PasswdMD5 by Luis Munoz (lem@cantv.net)
     5.9 -# based on /usr/src/libcrypt/crypt.c from FreeBSD 2.2.5-RELEASE
    5.10 -#
    5.11 -# MANY THANKS TO
    5.12 -#
    5.13 -#  Carey Evans - http://home.clear.net.nz/pages/c.evans/
    5.14 -#  Dennis Marti - http://users.starpower.net/marti1/
    5.15 -#
    5.16 -#  For the patches that got this thing working!
    5.17 -#
    5.18 -#########################################################
    5.19 -"""md5crypt.py - Provides interoperable MD5-based crypt() function
    5.20 -
    5.21 -SYNOPSIS
    5.22 -
    5.23 -        import md5crypt.py
    5.24 -
    5.25 -        cryptedpassword = md5crypt.md5crypt(password, salt);
    5.26 -
    5.27 -DESCRIPTION
    5.28 -
    5.29 -unix_md5_crypt() provides a crypt()-compatible interface to the
    5.30 -rather new MD5-based crypt() function found in modern operating systems.
    5.31 -It's based on the implementation found on FreeBSD 2.2.[56]-RELEASE and
    5.32 -contains the following license in it:
    5.33 -
    5.34 - "THE BEER-WARE LICENSE" (Revision 42):
    5.35 - <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
    5.36 - can do whatever you want with this stuff. If we meet some day, and you think
    5.37 - this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
    5.38 -
    5.39 -apache_md5_crypt() provides a function compatible with Apache's
    5.40 -.htpasswd files. This was contributed by Bryan Hart <bryan@eai.com>.
    5.41 -
    5.42 -"""
    5.43 -
    5.44 -MAGIC = '$1$'                   # Magic string
    5.45 -ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
    5.46 -
    5.47 -try:
    5.48 -    import hashlib
    5.49 -    hash_md5 = hashlib.md5
    5.50 -except ImportError:
    5.51 -    # maybe we have python < 2.5 (no hashlib)
    5.52 -    import md5
    5.53 -    hash_md5 = md5.new
    5.54 -
    5.55 -
    5.56 -def to64(v, n):
    5.57 -    ret = ''
    5.58 -    while (n - 1 >= 0):
    5.59 -        n = n - 1
    5.60 -        ret = ret + ITOA64[v & 0x3f]
    5.61 -        v = v >> 6
    5.62 -    return ret
    5.63 -
    5.64 -
    5.65 -def apache_md5_crypt(pw, salt):
    5.66 -    # change the Magic string to match the one used by Apache
    5.67 -    return unix_md5_crypt(pw, salt, '$apr1$')
    5.68 -
    5.69 -
    5.70 -def unix_md5_crypt(pw, salt, magic=None):
    5.71 -
    5.72 -    if magic is None:
    5.73 -        magic = MAGIC
    5.74 -
    5.75 -    # Take care of the magic string if present
    5.76 -    if salt[:len(magic)] == magic:
    5.77 -        salt = salt[len(magic):]
    5.78 -
    5.79 -
    5.80 -    # salt can have up to 8 characters:
    5.81 -    import string
    5.82 -    salt = string.split(salt, '$', 1)[0]
    5.83 -    salt = salt[:8]
    5.84 -
    5.85 -    ctx = pw + magic + salt
    5.86 -
    5.87 -    md5 = hash_md5()
    5.88 -    md5.update(pw + salt + pw)
    5.89 -    final = md5.digest()
    5.90 -
    5.91 -    for pl in range(len(pw), 0, -16):
    5.92 -        if pl > 16:
    5.93 -            ctx = ctx + final[:16]
    5.94 -        else:
    5.95 -            ctx = ctx + final[:pl]
    5.96 -
    5.97 -
    5.98 -    # Now the 'weird' xform (??)
    5.99 -
   5.100 -    i = len(pw)
   5.101 -    while i:
   5.102 -        if i & 1:
   5.103 -            ctx = ctx + chr(0)  #if ($i & 1) { $ctx->add(pack("C", 0)); }
   5.104 -        else:
   5.105 -            ctx = ctx + pw[0]
   5.106 -        i = i >> 1
   5.107 -
   5.108 -    md5 = hash_md5()
   5.109 -    md5.update(ctx)
   5.110 -    final = md5.digest()
   5.111 -
   5.112 -    # The following is supposed to make
   5.113 -    # things run slower.
   5.114 -
   5.115 -    # my question: WTF???
   5.116 -
   5.117 -    for i in range(1000):
   5.118 -        ctx1 = ''
   5.119 -        if i & 1:
   5.120 -            ctx1 = ctx1 + pw
   5.121 -        else:
   5.122 -            ctx1 = ctx1 + final[:16]
   5.123 -
   5.124 -        if i % 3:
   5.125 -            ctx1 = ctx1 + salt
   5.126 -
   5.127 -        if i % 7:
   5.128 -            ctx1 = ctx1 + pw
   5.129 -
   5.130 -        if i & 1:
   5.131 -            ctx1 = ctx1 + final[:16]
   5.132 -        else:
   5.133 -            ctx1 = ctx1 + pw
   5.134 -
   5.135 -
   5.136 -        md5 = hash_md5()
   5.137 -        md5.update(ctx1)
   5.138 -        final = md5.digest()
   5.139 -
   5.140 -
   5.141 -    # Final xform
   5.142 -
   5.143 -    passwd = ''
   5.144 -
   5.145 -    passwd = passwd + to64((int(ord(final[0])) << 16)
   5.146 -                           |(int(ord(final[6])) << 8)
   5.147 -                           |(int(ord(final[12]))), 4)
   5.148 -
   5.149 -    passwd = passwd + to64((int(ord(final[1])) << 16)
   5.150 -                           |(int(ord(final[7])) << 8)
   5.151 -                           |(int(ord(final[13]))), 4)
   5.152 -
   5.153 -    passwd = passwd + to64((int(ord(final[2])) << 16)
   5.154 -                           |(int(ord(final[8])) << 8)
   5.155 -                           |(int(ord(final[14]))), 4)
   5.156 -
   5.157 -    passwd = passwd + to64((int(ord(final[3])) << 16)
   5.158 -                           |(int(ord(final[9])) << 8)
   5.159 -                           |(int(ord(final[15]))), 4)
   5.160 -
   5.161 -    passwd = passwd + to64((int(ord(final[4])) << 16)
   5.162 -                           |(int(ord(final[10])) << 8)
   5.163 -                           |(int(ord(final[5]))), 4)
   5.164 -
   5.165 -    passwd = passwd + to64((int(ord(final[11]))), 2)
   5.166 -
   5.167 -
   5.168 -    return magic + salt + '$' + passwd
   5.169 -
   5.170 -
   5.171 -## assign a wrapper function:
   5.172 -md5crypt = unix_md5_crypt
   5.173 -
   5.174 -if __name__ == "__main__":
   5.175 -    print unix_md5_crypt("cat", "hat")
     6.1 --- a/docs/admin/configure.rst	Thu Jan 17 12:17:13 2013 +0100
     6.2 +++ b/docs/admin/configure.rst	Sat Jan 19 04:23:42 2013 +0100
     6.3 @@ -630,8 +630,7 @@
     6.4  
     6.5  Password storage
     6.6  ----------------
     6.7 -Moin never stores passwords in clear text, but always as cryptographic hash
     6.8 -with a random salt. Currently ssha256 is the default.
     6.9 +Moin never stores passwords in clear text.
    6.10  
    6.11  
    6.12  Authorization