summaryrefslogtreecommitdiff
path: root/passlib/handlers/sun_md5_crypt.py
diff options
context:
space:
mode:
Diffstat (limited to 'passlib/handlers/sun_md5_crypt.py')
-rw-r--r--passlib/handlers/sun_md5_crypt.py169
1 files changed, 88 insertions, 81 deletions
diff --git a/passlib/handlers/sun_md5_crypt.py b/passlib/handlers/sun_md5_crypt.py
index 8296e5e..a99289c 100644
--- a/passlib/handlers/sun_md5_crypt.py
+++ b/passlib/handlers/sun_md5_crypt.py
@@ -7,30 +7,29 @@
See documentation for details.
"""
-#=========================================================
-#imports
-#=========================================================
-#core
+#=============================================================================
+# imports
+#=============================================================================
+# core
from hashlib import md5
import re
import logging; log = logging.getLogger(__name__)
from warnings import warn
-#site
-#libs
+# site
+# pkg
from passlib.utils import h64, to_unicode
from passlib.utils.compat import b, bytes, byte_elem_value, irange, u, \
uascii_to_str, unicode, str_to_bascii
import passlib.utils.handlers as uh
-#pkg
-#local
+# local
__all__ = [
"sun_md5_crypt",
]
-#=========================================================
-#backend
-#=========================================================
-#constant data used by alg - Hamlet act 3 scene 1 + null char
+#=============================================================================
+# backend
+#=============================================================================
+# constant data used by alg - Hamlet act 3 scene 1 + null char
# exact bytes as in http://www.ibiblio.org/pub/docs/books/gutenberg/etext98/2ws2610.txt
# from Project Gutenberg.
@@ -72,13 +71,13 @@ MAGIC_HAMLET = b(
"Be all my sins remember'd.\n\x00" #<- apparently null at end of C string is included (test vector won't pass otherwise)
)
-#NOTE: these sequences are pre-calculated iteration ranges used by X & Y loops w/in rounds function below
+# NOTE: these sequences are pre-calculated iteration ranges used by X & Y loops w/in rounds function below
xr = irange(7)
_XY_ROUNDS = [
- tuple((i,i,i+3) for i in xr), #xrounds 0
- tuple((i,i+1,i+4) for i in xr), #xrounds 1
- tuple((i,i+8,(i+11)&15) for i in xr), #yrounds 0
- tuple((i,(i+9)&15, (i+12)&15) for i in xr), #yrounds 1
+ tuple((i,i,i+3) for i in xr), # xrounds 0
+ tuple((i,i+1,i+4) for i in xr), # xrounds 1
+ tuple((i,i+8,(i+11)&15) for i in xr), # yrounds 0
+ tuple((i,(i+9)&15, (i+12)&15) for i in xr), # yrounds 1
]
del xr
@@ -88,37 +87,45 @@ def raw_sun_md5_crypt(secret, rounds, salt):
assert isinstance(secret, bytes)
assert isinstance(salt, bytes)
- #validate rounds
+ # validate rounds
if rounds <= 0:
rounds = 0
real_rounds = 4096 + rounds
- #NOTE: spec seems to imply max 'rounds' is 2**32-1
+ # NOTE: spec seems to imply max 'rounds' is 2**32-1
- #generate initial digest to start off round 0.
- #NOTE: algorithm 'salt' includes full config string w/ trailing "$"
+ # generate initial digest to start off round 0.
+ # NOTE: algorithm 'salt' includes full config string w/ trailing "$"
result = md5(secret + salt).digest()
assert len(result) == 16
- #NOTE: many things have been inlined to speed up the loop as much as possible,
- # so that this only barely resembles the algorithm as described in the docs.
- # * all accesses to a given bit have been inlined using the formula
- # rbitval(bit) = (rval((bit>>3) & 15) >> (bit & 7)) & 1
- # * the calculation of coinflip value R has been inlined
- # * the conditional division of coinflip value V has been inlined as a shift right of 0 or 1.
- # * the i, i+3, etc iterations are precalculated in lists.
- # * the round-based conditional division of x & y is now performed
- # by choosing an appropriate precalculated list, so only the 7 used bits
- # are actually calculated
+ # NOTE: many things in this function have been inlined (to speed up the loop
+ # as much as possible), to the point that this code barely resembles
+ # the algorithm as described in the docs. in particular:
+ #
+ # * all accesses to a given bit have been inlined using the formula
+ # rbitval(bit) = (rval((bit>>3) & 15) >> (bit & 7)) & 1
+ #
+ # * the calculation of coinflip value R has been inlined
+ #
+ # * the conditional division of coinflip value V has been inlined as
+ # a shift right of 0 or 1.
+ #
+ # * the i, i+3, etc iterations are precalculated in lists.
+ #
+ # * the round-based conditional division of x & y is now performed
+ # by choosing an appropriate precalculated list, so that it only
+ # calculates the 7 bits which will actually be used.
+ #
X_ROUNDS_0, X_ROUNDS_1, Y_ROUNDS_0, Y_ROUNDS_1 = _XY_ROUNDS
- #NOTE: % appears to be *slightly* slower than &, so we prefer & if possible
+ # NOTE: % appears to be *slightly* slower than &, so we prefer & if possible
round = 0
while round < real_rounds:
- #convert last result byte string to list of byte-ints for easy access
+ # convert last result byte string to list of byte-ints for easy access
rval = [ byte_elem_value(c) for c in result ].__getitem__
- #build up X bit by bit
+ # build up X bit by bit
x = 0
xrounds = X_ROUNDS_1 if (rval((round>>3) & 15)>>(round & 7)) & 1 else X_ROUNDS_0
for i, ia, ib in xrounds:
@@ -127,7 +134,7 @@ def raw_sun_md5_crypt(secret, rounds, salt):
v = rval((a >> (b % 5)) & 15) >> ((b>>(a&7)) & 1)
x |= ((rval((v>>3)&15)>>(v&7))&1) << i
- #build up Y bit by bit
+ # build up Y bit by bit
y = 0
yrounds = Y_ROUNDS_1 if (rval(((round+64)>>3) & 15)>>(round & 7)) & 1 else Y_ROUNDS_0
for i, ia, ib in yrounds:
@@ -136,10 +143,10 @@ def raw_sun_md5_crypt(secret, rounds, salt):
v = rval((a >> (b % 5)) & 15) >> ((b>>(a&7)) & 1)
y |= ((rval((v>>3)&15)>>(v&7))&1) << i
- #extract x'th and y'th bit, xoring them together to yeild "coin flip"
+ # extract x'th and y'th bit, xoring them together to yeild "coin flip"
coin = ((rval(x>>3) >> (x&7)) ^ (rval(y>>3) >> (y&7))) & 1
- #construct hash for this round
+ # construct hash for this round
h = md5(result)
if coin:
h.update(MAGIC_HAMLET)
@@ -148,10 +155,10 @@ def raw_sun_md5_crypt(secret, rounds, salt):
round += 1
- #encode output
+ # encode output
return h64.encode_transposed_bytes(result, _chk_offsets)
-#NOTE: same offsets as md5_crypt
+# NOTE: same offsets as md5_crypt
_chk_offsets = (
12,6,0,
13,7,1,
@@ -161,9 +168,9 @@ _chk_offsets = (
11,
)
-#=========================================================
-#handler
-#=========================================================
+#=============================================================================
+# handler
+#=============================================================================
class sun_md5_crypt(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
"""This class implements the Sun-MD5-Crypt password hash, and follows the :ref:`password-hash-api`.
@@ -206,47 +213,47 @@ class sun_md5_crypt(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
.. versionadded:: 1.6
"""
- #=========================================================
- #class attrs
- #=========================================================
+ #===================================================================
+ # class attrs
+ #===================================================================
name = "sun_md5_crypt"
setting_kwds = ("salt", "rounds", "bare_salt", "salt_size")
checksum_chars = uh.HASH64_CHARS
checksum_size = 22
- #NOTE: docs say max password length is 255.
- #release 9u2
+ # NOTE: docs say max password length is 255.
+ # release 9u2
- #NOTE: not sure if original crypt has a salt size limit,
+ # NOTE: not sure if original crypt has a salt size limit,
# all instances that have been seen use 8 chars.
default_salt_size = 8
min_salt_size = 0
max_salt_size = None
salt_chars = uh.HASH64_CHARS
- default_rounds = 5000 #current passlib default
+ default_rounds = 5000 # current passlib default
min_rounds = 0
max_rounds = 4294963199 ##2**32-1-4096
- #XXX: ^ not sure what it does if past this bound... does 32 int roll over?
+ # XXX: ^ not sure what it does if past this bound... does 32 int roll over?
rounds_cost = "linear"
ident_values = (u("$md5$"), u("$md5,"))
- #=========================================================
- #instance attrs
- #=========================================================
- bare_salt = False #flag to indicate legacy hashes that lack "$$" suffix
+ #===================================================================
+ # instance attrs
+ #===================================================================
+ bare_salt = False # flag to indicate legacy hashes that lack "$$" suffix
- #=========================================================
- #constructor
- #=========================================================
+ #===================================================================
+ # constructor
+ #===================================================================
def __init__(self, bare_salt=False, **kwds):
self.bare_salt = bare_salt
super(sun_md5_crypt, self).__init__(**kwds)
- #=========================================================
- #internal helpers
- #=========================================================
+ #===================================================================
+ # internal helpers
+ #===================================================================
@classmethod
def identify(cls, hash):
hash = uh.to_unicode_for_identify(hash)
@@ -257,9 +264,9 @@ class sun_md5_crypt(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
hash = to_unicode(hash, "ascii", "hash")
#
- #detect if hash specifies rounds value.
- #if so, parse and validate it.
- #by end, set 'rounds' to int value, and 'tail' containing salt+chk
+ # detect if hash specifies rounds value.
+ # if so, parse and validate it.
+ # by end, set 'rounds' to int value, and 'tail' containing salt+chk
#
if hash.startswith(u("$md5$")):
rounds = 0
@@ -276,7 +283,7 @@ class sun_md5_crypt(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
if rstr != unicode(rounds):
raise uh.exc.ZeroPaddedRoundsError(cls)
if rounds == 0:
- #NOTE: not sure if this is forbidden by spec or not;
+ # NOTE: not sure if this is forbidden by spec or not;
# but allowing it would complicate things,
# and it should never occur anyways.
raise uh.exc.MalformedHashError(cls, "explicit zero rounds")
@@ -285,9 +292,9 @@ class sun_md5_crypt(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
raise uh.exc.InvalidHashError(cls)
#
- #salt/checksum separation is kinda weird,
- #to deal cleanly with some backward-compatible workarounds
- #implemented by original implementation.
+ # salt/checksum separation is kinda weird,
+ # to deal cleanly with some backward-compatible workarounds
+ # implemented by original implementation.
#
chk_idx = hash.rfind(u("$"), salt_idx)
if chk_idx == -1:
@@ -333,25 +340,25 @@ class sun_md5_crypt(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
hash = u("%s$%s") % (hash, chk)
return uascii_to_str(hash)
- #=========================================================
- #primary interface
- #=========================================================
- #TODO: if we're on solaris, check for native crypt() support.
- # this will require extra testing, to make sure native crypt
- # actually behaves correctly.
- # especially, when using ''-config, make sure to append '$x' to string.
+ #===================================================================
+ # primary interface
+ #===================================================================
+ # TODO: if we're on solaris, check for native crypt() support.
+ # this will require extra testing, to make sure native crypt
+ # actually behaves correctly. of particular importance:
+ # when using ""-config, make sure to append "$x" to string.
def _calc_checksum(self, secret):
- #NOTE: no reference for how sun_md5_crypt handles unicode
+ # NOTE: no reference for how sun_md5_crypt handles unicode
if isinstance(secret, unicode):
secret = secret.encode("utf-8")
config = str_to_bascii(self.to_string(withchk=False))
return raw_sun_md5_crypt(secret, self.rounds, config).decode("ascii")
- #=========================================================
- #eoc
- #=========================================================
+ #===================================================================
+ # eoc
+ #===================================================================
-#=========================================================
-#eof
-#=========================================================
+#=============================================================================
+# eof
+#=============================================================================