md5crypt.py
changeset 0 b97547f5f1fa
child 1132 96752791c2b6
equal deleted inserted replaced
-1:000000000000 0:b97547f5f1fa
       
     1 #########################################################
       
     2 # md5crypt.py
       
     3 #
       
     4 # 0423.2000 by michal wallace http://www.sabren.com/
       
     5 # based on perl's Crypt::PasswdMD5 by Luis Munoz (lem@cantv.net)
       
     6 # based on /usr/src/libcrypt/crypt.c from FreeBSD 2.2.5-RELEASE
       
     7 #
       
     8 # MANY THANKS TO
       
     9 #
       
    10 #  Carey Evans - http://home.clear.net.nz/pages/c.evans/
       
    11 #  Dennis Marti - http://users.starpower.net/marti1/
       
    12 #
       
    13 #  For the patches that got this thing working!
       
    14 #
       
    15 # modification by logilab:
       
    16 # * remove usage of the string module
       
    17 # * don't include the magic string in the output string
       
    18 #   for true crypt.crypt compatibility
       
    19 #########################################################
       
    20 """md5crypt.py - Provides interoperable MD5-based crypt() function
       
    21 
       
    22 SYNOPSIS
       
    23 
       
    24         import md5crypt.py
       
    25 
       
    26         cryptedpassword = md5crypt.md5crypt(password, salt);
       
    27 
       
    28 DESCRIPTION
       
    29 
       
    30 unix_md5_crypt() provides a crypt()-compatible interface to the
       
    31 rather new MD5-based crypt() function found in modern operating systems.
       
    32 It's based on the implementation found on FreeBSD 2.2.[56]-RELEASE and
       
    33 contains the following license in it:
       
    34 
       
    35  "THE BEER-WARE LICENSE" (Revision 42):
       
    36  <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
       
    37  can do whatever you want with this stuff. If we meet some day, and you think
       
    38  this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
       
    39 """
       
    40 
       
    41 MAGIC = '$1$'                        # Magic string
       
    42 ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
       
    43 
       
    44 import md5
       
    45 
       
    46 def to64 (v, n):
       
    47     ret = ''
       
    48     while (n - 1 >= 0):
       
    49         n = n - 1
       
    50         ret = ret + ITOA64[v & 0x3f]
       
    51         v = v >> 6
       
    52     return ret
       
    53 
       
    54 
       
    55 def crypt(pw, salt, magic=None):
       
    56     if magic==None:
       
    57         magic = MAGIC
       
    58     # Take care of the magic string if present
       
    59     if salt[:len(magic)] == magic:
       
    60         salt = salt[len(magic):]
       
    61     # salt can have up to 8 characters:
       
    62     salt = salt.split('$', 1)[0]
       
    63     salt = salt[:8]
       
    64     ctx = pw + magic + salt
       
    65     final = md5.md5(pw + salt + pw).digest()
       
    66     for pl in range(len(pw),0,-16):
       
    67         if pl > 16:
       
    68             ctx = ctx + final[:16]
       
    69         else:
       
    70             ctx = ctx + final[:pl]
       
    71     # Now the 'weird' xform (??)
       
    72     i = len(pw)
       
    73     while i:
       
    74         if i & 1:
       
    75             ctx = ctx + chr(0)  #if ($i & 1) { $ctx->add(pack("C", 0)); }
       
    76         else:
       
    77             ctx = ctx + pw[0]
       
    78         i = i >> 1
       
    79     final = md5.md5(ctx).digest()
       
    80     # The following is supposed to make
       
    81     # things run slower. 
       
    82     # my question: WTF???
       
    83     for i in range(1000):
       
    84         ctx1 = ''
       
    85         if i & 1:
       
    86             ctx1 = ctx1 + pw
       
    87         else:
       
    88             ctx1 = ctx1 + final[:16]
       
    89         if i % 3:
       
    90             ctx1 = ctx1 + salt
       
    91         if i % 7:
       
    92             ctx1 = ctx1 + pw
       
    93         if i & 1:
       
    94             ctx1 = ctx1 + final[:16]
       
    95         else:
       
    96             ctx1 = ctx1 + pw
       
    97         final = md5.md5(ctx1).digest()
       
    98     # Final xform
       
    99     passwd = ''
       
   100     passwd = passwd + to64((int(ord(final[0])) << 16)
       
   101                            |(int(ord(final[6])) << 8)
       
   102                            |(int(ord(final[12]))),4)
       
   103     passwd = passwd + to64((int(ord(final[1])) << 16)
       
   104                            |(int(ord(final[7])) << 8)
       
   105                            |(int(ord(final[13]))), 4)
       
   106     passwd = passwd + to64((int(ord(final[2])) << 16)
       
   107                            |(int(ord(final[8])) << 8)
       
   108                            |(int(ord(final[14]))), 4)
       
   109     passwd = passwd + to64((int(ord(final[3])) << 16)
       
   110                            |(int(ord(final[9])) << 8)
       
   111                            |(int(ord(final[15]))), 4)
       
   112     passwd = passwd + to64((int(ord(final[4])) << 16)
       
   113                            |(int(ord(final[10])) << 8)
       
   114                            |(int(ord(final[5]))), 4)
       
   115     passwd = passwd + to64((int(ord(final[11]))), 2)
       
   116     return salt + '$' + passwd