import hashlib MAGIC = '$1$' ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" def to64(v, n): ret = '' while n > 0: ret += ITOA64[v & 0x3f] v >>= 6 n -= 1 return ret def apache_md5_crypt(pw, salt): return unix_md5_crypt(pw, salt, '$apr1$') def unix_md5_crypt(pw, salt, magic=None): if magic is None: magic = MAGIC if salt.startswith(magic): salt = salt[len(magic):] salt = salt.split('$', 1)[0][:8] pw_bytes = pw.encode('utf-8') salt_bytes = salt.encode('utf-8') magic_bytes = magic.encode('utf-8') ctx = pw_bytes + magic_bytes + salt_bytes final = hashlib.md5(pw_bytes + salt_bytes + pw_bytes).digest() for pl in range(len(pw_bytes), 0, -16): ctx += final[:min(16, pl)] i = len(pw_bytes) while i: if i & 1: ctx += b'\x00' else: ctx += pw_bytes[:1] i >>= 1 final = hashlib.md5(ctx).digest() for i in range(1000): ctx1 = b'' if i & 1: ctx1 += pw_bytes else: ctx1 += final if i % 3: ctx1 += salt_bytes if i % 7: ctx1 += pw_bytes if i & 1: ctx1 += final else: ctx1 += pw_bytes final = hashlib.md5(ctx1).digest() passwd = '' passwd += to64((final[0] << 16) | (final[6] << 8) | final[12], 4) passwd += to64((final[1] << 16) | (final[7] << 8) | final[13], 4) passwd += to64((final[2] << 16) | (final[8] << 8) | final[14], 4) passwd += to64((final[3] << 16) | (final[9] << 8) | final[15], 4) passwd += to64((final[4] << 16) | (final[10] << 8) | final[5], 4) passwd += to64(final[11], 2) return magic + salt + '$' + passwd md5crypt = unix_md5_crypt if __name__ == "__main__": print(unix_md5_crypt("cat", "hat"))