cubicweb/md5crypt.py
changeset 11057 0b59724cb3f2
parent 10775 4b3c1069bd4e
child 12567 26744ad37953
equal deleted inserted replaced
11052:058bb3dc685f 11057:0b59724cb3f2
       
     1 # md5crypt.py
       
     2 #
       
     3 # 0423.2000 by michal wallace http://www.sabren.com/
       
     4 # based on perl's Crypt::PasswdMD5 by Luis Munoz (lem@cantv.net)
       
     5 # based on /usr/src/libcrypt/crypt.c from FreeBSD 2.2.5-RELEASE
       
     6 #
       
     7 # MANY THANKS TO
       
     8 #
       
     9 #  Carey Evans - http://home.clear.net.nz/pages/c.evans/
       
    10 #  Dennis Marti - http://users.starpower.net/marti1/
       
    11 #
       
    12 #  For the patches that got this thing working!
       
    13 #
       
    14 # modification by logilab:
       
    15 # * remove usage of the string module
       
    16 # * don't include the magic string in the output string
       
    17 #   for true crypt.crypt compatibility
       
    18 # * use hashlib module instead of md5
       
    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 = b'$1$'                        # Magic string
       
    42 ITOA64 = b"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
       
    43 
       
    44 from hashlib import md5 # pylint: disable=E0611
       
    45 
       
    46 from six import text_type, indexbytes
       
    47 from six.moves import range
       
    48 
       
    49 
       
    50 def to64 (v, n):
       
    51     ret = bytearray()
       
    52     while (n - 1 >= 0):
       
    53         n = n - 1
       
    54         ret.append(ITOA64[v & 0x3f])
       
    55         v = v >> 6
       
    56     return ret
       
    57 
       
    58 def crypt(pw, salt):
       
    59     if isinstance(pw, text_type):
       
    60         pw = pw.encode('utf-8')
       
    61     if isinstance(salt, text_type):
       
    62         salt = salt.encode('ascii')
       
    63     # Take care of the magic string if present
       
    64     if salt.startswith(MAGIC):
       
    65         salt = salt[len(MAGIC):]
       
    66     # salt can have up to 8 characters:
       
    67     salt = salt.split(b'$', 1)[0]
       
    68     salt = salt[:8]
       
    69     ctx = pw + MAGIC + salt
       
    70     final = md5(pw + salt + pw).digest()
       
    71     for pl in range(len(pw), 0, -16):
       
    72         if pl > 16:
       
    73             ctx = ctx + final[:16]
       
    74         else:
       
    75             ctx = ctx + final[:pl]
       
    76     # Now the 'weird' xform (??)
       
    77     i = len(pw)
       
    78     while i:
       
    79         if i & 1:
       
    80             ctx = ctx + b'\0'  #if ($i & 1) { $ctx->add(pack("C", 0)); }
       
    81         else:
       
    82             ctx = ctx + pw[0]
       
    83         i = i >> 1
       
    84     final = md5(ctx).digest()
       
    85     # The following is supposed to make
       
    86     # things run slower.
       
    87     # my question: WTF???
       
    88     for i in range(1000):
       
    89         ctx1 = b''
       
    90         if i & 1:
       
    91             ctx1 = ctx1 + pw
       
    92         else:
       
    93             ctx1 = ctx1 + final[:16]
       
    94         if i % 3:
       
    95             ctx1 = ctx1 + salt
       
    96         if i % 7:
       
    97             ctx1 = ctx1 + pw
       
    98         if i & 1:
       
    99             ctx1 = ctx1 + final[:16]
       
   100         else:
       
   101             ctx1 = ctx1 + pw
       
   102         final = md5(ctx1).digest()
       
   103     # Final xform
       
   104     passwd = b''
       
   105     passwd += to64((indexbytes(final, 0) << 16)
       
   106                    |(indexbytes(final, 6) << 8)
       
   107                    |(indexbytes(final, 12)),4)
       
   108     passwd += to64((indexbytes(final, 1) << 16)
       
   109                    |(indexbytes(final, 7) << 8)
       
   110                    |(indexbytes(final, 13)), 4)
       
   111     passwd += to64((indexbytes(final, 2) << 16)
       
   112                    |(indexbytes(final, 8) << 8)
       
   113                    |(indexbytes(final, 14)), 4)
       
   114     passwd += to64((indexbytes(final, 3) << 16)
       
   115                    |(indexbytes(final, 9) << 8)
       
   116                    |(indexbytes(final, 15)), 4)
       
   117     passwd += to64((indexbytes(final, 4) << 16)
       
   118                    |(indexbytes(final, 10) << 8)
       
   119                    |(indexbytes(final, 5)), 4)
       
   120     passwd += to64((indexbytes(final, 11)), 2)
       
   121     return passwd