Загрузить файлы в «venv/Lib/site-packages/passlib/handlers»
This commit is contained in:
269
venv/Lib/site-packages/passlib/handlers/misc.py
Normal file
269
venv/Lib/site-packages/passlib/handlers/misc.py
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
"""passlib.handlers.misc - misc generic handlers
|
||||||
|
"""
|
||||||
|
#=============================================================================
|
||||||
|
# imports
|
||||||
|
#=============================================================================
|
||||||
|
# core
|
||||||
|
import sys
|
||||||
|
import logging; log = logging.getLogger(__name__)
|
||||||
|
from warnings import warn
|
||||||
|
# site
|
||||||
|
# pkg
|
||||||
|
from passlib.utils import to_native_str, str_consteq
|
||||||
|
from passlib.utils.compat import unicode, u, unicode_or_bytes_types
|
||||||
|
import passlib.utils.handlers as uh
|
||||||
|
# local
|
||||||
|
__all__ = [
|
||||||
|
"unix_disabled",
|
||||||
|
"unix_fallback",
|
||||||
|
"plaintext",
|
||||||
|
]
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# handler
|
||||||
|
#=============================================================================
|
||||||
|
class unix_fallback(uh.ifc.DisabledHash, uh.StaticHandler):
|
||||||
|
"""This class provides the fallback behavior for unix shadow files, and follows the :ref:`password-hash-api`.
|
||||||
|
|
||||||
|
This class does not implement a hash, but instead provides fallback
|
||||||
|
behavior as found in /etc/shadow on most unix variants.
|
||||||
|
If used, should be the last scheme in the context.
|
||||||
|
|
||||||
|
* this class will positively identify all hash strings.
|
||||||
|
* for security, passwords will always hash to ``!``.
|
||||||
|
* it rejects all passwords if the hash is NOT an empty string (``!`` or ``*`` are frequently used).
|
||||||
|
* by default it rejects all passwords if the hash is an empty string,
|
||||||
|
but if ``enable_wildcard=True`` is passed to verify(),
|
||||||
|
all passwords will be allowed through if the hash is an empty string.
|
||||||
|
|
||||||
|
.. deprecated:: 1.6
|
||||||
|
This has been deprecated due to its "wildcard" feature,
|
||||||
|
and will be removed in Passlib 1.8. Use :class:`unix_disabled` instead.
|
||||||
|
"""
|
||||||
|
name = "unix_fallback"
|
||||||
|
context_kwds = ("enable_wildcard",)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def identify(cls, hash):
|
||||||
|
if isinstance(hash, unicode_or_bytes_types):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
raise uh.exc.ExpectedStringError(hash, "hash")
|
||||||
|
|
||||||
|
def __init__(self, enable_wildcard=False, **kwds):
|
||||||
|
warn("'unix_fallback' is deprecated, "
|
||||||
|
"and will be removed in Passlib 1.8; "
|
||||||
|
"please use 'unix_disabled' instead.",
|
||||||
|
DeprecationWarning)
|
||||||
|
super(unix_fallback, self).__init__(**kwds)
|
||||||
|
self.enable_wildcard = enable_wildcard
|
||||||
|
|
||||||
|
def _calc_checksum(self, secret):
|
||||||
|
if self.checksum:
|
||||||
|
# NOTE: hash will generally be "!", but we want to preserve
|
||||||
|
# it in case it's something else, like "*".
|
||||||
|
return self.checksum
|
||||||
|
else:
|
||||||
|
return u("!")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def verify(cls, secret, hash, enable_wildcard=False):
|
||||||
|
uh.validate_secret(secret)
|
||||||
|
if not isinstance(hash, unicode_or_bytes_types):
|
||||||
|
raise uh.exc.ExpectedStringError(hash, "hash")
|
||||||
|
elif hash:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return enable_wildcard
|
||||||
|
|
||||||
|
_MARKER_CHARS = u("*!")
|
||||||
|
_MARKER_BYTES = b"*!"
|
||||||
|
|
||||||
|
class unix_disabled(uh.ifc.DisabledHash, uh.MinimalHandler):
|
||||||
|
"""This class provides disabled password behavior for unix shadow files,
|
||||||
|
and follows the :ref:`password-hash-api`.
|
||||||
|
|
||||||
|
This class does not implement a hash, but instead matches the "disabled account"
|
||||||
|
strings found in ``/etc/shadow`` on most Unix variants. "encrypting" a password
|
||||||
|
will simply return the disabled account marker. It will reject all passwords,
|
||||||
|
no matter the hash string. The :meth:`~passlib.ifc.PasswordHash.hash`
|
||||||
|
method supports one optional keyword:
|
||||||
|
|
||||||
|
:type marker: str
|
||||||
|
:param marker:
|
||||||
|
Optional marker string which overrides the platform default
|
||||||
|
used to indicate a disabled account.
|
||||||
|
|
||||||
|
If not specified, this will default to ``"*"`` on BSD systems,
|
||||||
|
and use the Linux default ``"!"`` for all other platforms.
|
||||||
|
(:attr:`!unix_disabled.default_marker` will contain the default value)
|
||||||
|
|
||||||
|
.. versionadded:: 1.6
|
||||||
|
This class was added as a replacement for the now-deprecated
|
||||||
|
:class:`unix_fallback` class, which had some undesirable features.
|
||||||
|
"""
|
||||||
|
name = "unix_disabled"
|
||||||
|
setting_kwds = ("marker",)
|
||||||
|
context_kwds = ()
|
||||||
|
|
||||||
|
_disable_prefixes = tuple(str(_MARKER_CHARS))
|
||||||
|
|
||||||
|
# TODO: rename attr to 'marker'...
|
||||||
|
if 'bsd' in sys.platform: # pragma: no cover -- runtime detection
|
||||||
|
default_marker = u("*")
|
||||||
|
else:
|
||||||
|
# use the linux default for other systems
|
||||||
|
# (glibc also supports adding old hash after the marker
|
||||||
|
# so it can be restored later).
|
||||||
|
default_marker = u("!")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def using(cls, marker=None, **kwds):
|
||||||
|
subcls = super(unix_disabled, cls).using(**kwds)
|
||||||
|
if marker is not None:
|
||||||
|
if not cls.identify(marker):
|
||||||
|
raise ValueError("invalid marker: %r" % marker)
|
||||||
|
subcls.default_marker = marker
|
||||||
|
return subcls
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def identify(cls, hash):
|
||||||
|
# NOTE: technically, anything in the /etc/shadow password field
|
||||||
|
# which isn't valid crypt() output counts as "disabled".
|
||||||
|
# but that's rather ambiguous, and it's hard to predict what
|
||||||
|
# valid output is for unknown crypt() implementations.
|
||||||
|
# so to be on the safe side, we only match things *known*
|
||||||
|
# to be disabled field indicators, and will add others
|
||||||
|
# as they are found. things beginning w/ "$" should *never* match.
|
||||||
|
#
|
||||||
|
# things currently matched:
|
||||||
|
# * linux uses "!"
|
||||||
|
# * bsd uses "*"
|
||||||
|
# * linux may use "!" + hash to disable but preserve original hash
|
||||||
|
# * linux counts empty string as "any password";
|
||||||
|
# this code recognizes it, but treats it the same as "!"
|
||||||
|
if isinstance(hash, unicode):
|
||||||
|
start = _MARKER_CHARS
|
||||||
|
elif isinstance(hash, bytes):
|
||||||
|
start = _MARKER_BYTES
|
||||||
|
else:
|
||||||
|
raise uh.exc.ExpectedStringError(hash, "hash")
|
||||||
|
return not hash or hash[0] in start
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def verify(cls, secret, hash):
|
||||||
|
uh.validate_secret(secret)
|
||||||
|
if not cls.identify(hash): # handles typecheck
|
||||||
|
raise uh.exc.InvalidHashError(cls)
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def hash(cls, secret, **kwds):
|
||||||
|
if kwds:
|
||||||
|
uh.warn_hash_settings_deprecation(cls, kwds)
|
||||||
|
return cls.using(**kwds).hash(secret)
|
||||||
|
uh.validate_secret(secret)
|
||||||
|
marker = cls.default_marker
|
||||||
|
assert marker and cls.identify(marker)
|
||||||
|
return to_native_str(marker, param="marker")
|
||||||
|
|
||||||
|
@uh.deprecated_method(deprecated="1.7", removed="2.0")
|
||||||
|
@classmethod
|
||||||
|
def genhash(cls, secret, config, marker=None):
|
||||||
|
if not cls.identify(config):
|
||||||
|
raise uh.exc.InvalidHashError(cls)
|
||||||
|
elif config:
|
||||||
|
# preserve the existing str,since it might contain a disabled password hash ("!" + hash)
|
||||||
|
uh.validate_secret(secret)
|
||||||
|
return to_native_str(config, param="config")
|
||||||
|
else:
|
||||||
|
if marker is not None:
|
||||||
|
cls = cls.using(marker=marker)
|
||||||
|
return cls.hash(secret)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def disable(cls, hash=None):
|
||||||
|
out = cls.hash("")
|
||||||
|
if hash is not None:
|
||||||
|
hash = to_native_str(hash, param="hash")
|
||||||
|
if cls.identify(hash):
|
||||||
|
# extract original hash, so that we normalize marker
|
||||||
|
hash = cls.enable(hash)
|
||||||
|
if hash:
|
||||||
|
out += hash
|
||||||
|
return out
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def enable(cls, hash):
|
||||||
|
hash = to_native_str(hash, param="hash")
|
||||||
|
for prefix in cls._disable_prefixes:
|
||||||
|
if hash.startswith(prefix):
|
||||||
|
orig = hash[len(prefix):]
|
||||||
|
if orig:
|
||||||
|
return orig
|
||||||
|
else:
|
||||||
|
raise ValueError("cannot restore original hash")
|
||||||
|
raise uh.exc.InvalidHashError(cls)
|
||||||
|
|
||||||
|
class plaintext(uh.MinimalHandler):
|
||||||
|
"""This class stores passwords in plaintext, and follows the :ref:`password-hash-api`.
|
||||||
|
|
||||||
|
The :meth:`~passlib.ifc.PasswordHash.hash`, :meth:`~passlib.ifc.PasswordHash.genhash`, and :meth:`~passlib.ifc.PasswordHash.verify` methods all require the
|
||||||
|
following additional contextual keyword:
|
||||||
|
|
||||||
|
:type encoding: str
|
||||||
|
:param encoding:
|
||||||
|
This controls the character encoding to use (defaults to ``utf-8``).
|
||||||
|
|
||||||
|
This encoding will be used to encode :class:`!unicode` passwords
|
||||||
|
under Python 2, and decode :class:`!bytes` hashes under Python 3.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.6
|
||||||
|
The ``encoding`` keyword was added.
|
||||||
|
"""
|
||||||
|
# NOTE: this is subclassed by ldap_plaintext
|
||||||
|
|
||||||
|
name = "plaintext"
|
||||||
|
setting_kwds = ()
|
||||||
|
context_kwds = ("encoding",)
|
||||||
|
default_encoding = "utf-8"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def identify(cls, hash):
|
||||||
|
if isinstance(hash, unicode_or_bytes_types):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
raise uh.exc.ExpectedStringError(hash, "hash")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def hash(cls, secret, encoding=None):
|
||||||
|
uh.validate_secret(secret)
|
||||||
|
if not encoding:
|
||||||
|
encoding = cls.default_encoding
|
||||||
|
return to_native_str(secret, encoding, "secret")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def verify(cls, secret, hash, encoding=None):
|
||||||
|
if not encoding:
|
||||||
|
encoding = cls.default_encoding
|
||||||
|
hash = to_native_str(hash, encoding, "hash")
|
||||||
|
if not cls.identify(hash):
|
||||||
|
raise uh.exc.InvalidHashError(cls)
|
||||||
|
return str_consteq(cls.hash(secret, encoding), hash)
|
||||||
|
|
||||||
|
@uh.deprecated_method(deprecated="1.7", removed="2.0")
|
||||||
|
@classmethod
|
||||||
|
def genconfig(cls):
|
||||||
|
return cls.hash("")
|
||||||
|
|
||||||
|
@uh.deprecated_method(deprecated="1.7", removed="2.0")
|
||||||
|
@classmethod
|
||||||
|
def genhash(cls, secret, config, encoding=None):
|
||||||
|
# NOTE: 'config' is ignored, as this hash has no salting / etc
|
||||||
|
if not cls.identify(config):
|
||||||
|
raise uh.exc.InvalidHashError(cls)
|
||||||
|
return cls.hash(secret, encoding=encoding)
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# eof
|
||||||
|
#=============================================================================
|
||||||
244
venv/Lib/site-packages/passlib/handlers/mssql.py
Normal file
244
venv/Lib/site-packages/passlib/handlers/mssql.py
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
"""passlib.handlers.mssql - MS-SQL Password Hash
|
||||||
|
|
||||||
|
Notes
|
||||||
|
=====
|
||||||
|
MS-SQL has used a number of hash algs over the years,
|
||||||
|
most of which were exposed through the undocumented
|
||||||
|
'pwdencrypt' and 'pwdcompare' sql functions.
|
||||||
|
|
||||||
|
Known formats
|
||||||
|
-------------
|
||||||
|
6.5
|
||||||
|
snefru hash, ascii encoded password
|
||||||
|
no examples found
|
||||||
|
|
||||||
|
7.0
|
||||||
|
snefru hash, unicode (what encoding?)
|
||||||
|
saw ref that these blobs were 16 bytes in size
|
||||||
|
no examples found
|
||||||
|
|
||||||
|
2000
|
||||||
|
byte string using displayed as 0x hex, using 0x0100 prefix.
|
||||||
|
contains hashes of password and upper-case password.
|
||||||
|
|
||||||
|
2007
|
||||||
|
same as 2000, but without the upper-case hash.
|
||||||
|
|
||||||
|
refs
|
||||||
|
----------
|
||||||
|
https://blogs.msdn.com/b/lcris/archive/2007/04/30/sql-server-2005-about-login-password-hashes.aspx?Redirected=true
|
||||||
|
http://us.generation-nt.com/securing-passwords-hash-help-35429432.html
|
||||||
|
http://forum.md5decrypter.co.uk/topic230-mysql-and-mssql-get-password-hashes.aspx
|
||||||
|
http://www.theregister.co.uk/2002/07/08/cracking_ms_sql_server_passwords/
|
||||||
|
"""
|
||||||
|
#=============================================================================
|
||||||
|
# imports
|
||||||
|
#=============================================================================
|
||||||
|
# core
|
||||||
|
from binascii import hexlify, unhexlify
|
||||||
|
from hashlib import sha1
|
||||||
|
import re
|
||||||
|
import logging; log = logging.getLogger(__name__)
|
||||||
|
from warnings import warn
|
||||||
|
# site
|
||||||
|
# pkg
|
||||||
|
from passlib.utils import consteq
|
||||||
|
from passlib.utils.compat import bascii_to_str, unicode, u
|
||||||
|
import passlib.utils.handlers as uh
|
||||||
|
# local
|
||||||
|
__all__ = [
|
||||||
|
"mssql2000",
|
||||||
|
"mssql2005",
|
||||||
|
]
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# mssql 2000
|
||||||
|
#=============================================================================
|
||||||
|
def _raw_mssql(secret, salt):
|
||||||
|
assert isinstance(secret, unicode)
|
||||||
|
assert isinstance(salt, bytes)
|
||||||
|
return sha1(secret.encode("utf-16-le") + salt).digest()
|
||||||
|
|
||||||
|
BIDENT = b"0x0100"
|
||||||
|
##BIDENT2 = b("\x01\x00")
|
||||||
|
UIDENT = u("0x0100")
|
||||||
|
|
||||||
|
def _ident_mssql(hash, csize, bsize):
|
||||||
|
"""common identify for mssql 2000/2005"""
|
||||||
|
if isinstance(hash, unicode):
|
||||||
|
if len(hash) == csize and hash.startswith(UIDENT):
|
||||||
|
return True
|
||||||
|
elif isinstance(hash, bytes):
|
||||||
|
if len(hash) == csize and hash.startswith(BIDENT):
|
||||||
|
return True
|
||||||
|
##elif len(hash) == bsize and hash.startswith(BIDENT2): # raw bytes
|
||||||
|
## return True
|
||||||
|
else:
|
||||||
|
raise uh.exc.ExpectedStringError(hash, "hash")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _parse_mssql(hash, csize, bsize, handler):
|
||||||
|
"""common parser for mssql 2000/2005; returns 4 byte salt + checksum"""
|
||||||
|
if isinstance(hash, unicode):
|
||||||
|
if len(hash) == csize and hash.startswith(UIDENT):
|
||||||
|
try:
|
||||||
|
return unhexlify(hash[6:].encode("utf-8"))
|
||||||
|
except TypeError: # throw when bad char found
|
||||||
|
pass
|
||||||
|
elif isinstance(hash, bytes):
|
||||||
|
# assumes ascii-compat encoding
|
||||||
|
assert isinstance(hash, bytes)
|
||||||
|
if len(hash) == csize and hash.startswith(BIDENT):
|
||||||
|
try:
|
||||||
|
return unhexlify(hash[6:])
|
||||||
|
except TypeError: # throw when bad char found
|
||||||
|
pass
|
||||||
|
##elif len(hash) == bsize and hash.startswith(BIDENT2): # raw bytes
|
||||||
|
## return hash[2:]
|
||||||
|
else:
|
||||||
|
raise uh.exc.ExpectedStringError(hash, "hash")
|
||||||
|
raise uh.exc.InvalidHashError(handler)
|
||||||
|
|
||||||
|
class mssql2000(uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
|
||||||
|
"""This class implements the password hash used by MS-SQL 2000, and follows the :ref:`password-hash-api`.
|
||||||
|
|
||||||
|
It supports a fixed-length salt.
|
||||||
|
|
||||||
|
The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords:
|
||||||
|
|
||||||
|
:type salt: bytes
|
||||||
|
:param salt:
|
||||||
|
Optional salt string.
|
||||||
|
If not specified, one will be autogenerated (this is recommended).
|
||||||
|
If specified, it must be 4 bytes in length.
|
||||||
|
|
||||||
|
:type relaxed: bool
|
||||||
|
:param relaxed:
|
||||||
|
By default, providing an invalid value for one of the other
|
||||||
|
keywords will result in a :exc:`ValueError`. If ``relaxed=True``,
|
||||||
|
and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning`
|
||||||
|
will be issued instead. Correctable errors include
|
||||||
|
``salt`` strings that are too long.
|
||||||
|
"""
|
||||||
|
#===================================================================
|
||||||
|
# algorithm information
|
||||||
|
#===================================================================
|
||||||
|
name = "mssql2000"
|
||||||
|
setting_kwds = ("salt",)
|
||||||
|
checksum_size = 40
|
||||||
|
min_salt_size = max_salt_size = 4
|
||||||
|
|
||||||
|
#===================================================================
|
||||||
|
# formatting
|
||||||
|
#===================================================================
|
||||||
|
|
||||||
|
# 0100 - 2 byte identifier
|
||||||
|
# 4 byte salt
|
||||||
|
# 20 byte checksum
|
||||||
|
# 20 byte checksum
|
||||||
|
# = 46 bytes
|
||||||
|
# encoded '0x' + 92 chars = 94
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def identify(cls, hash):
|
||||||
|
return _ident_mssql(hash, 94, 46)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_string(cls, hash):
|
||||||
|
data = _parse_mssql(hash, 94, 46, cls)
|
||||||
|
return cls(salt=data[:4], checksum=data[4:])
|
||||||
|
|
||||||
|
def to_string(self):
|
||||||
|
raw = self.salt + self.checksum
|
||||||
|
# raw bytes format - BIDENT2 + raw
|
||||||
|
return "0x0100" + bascii_to_str(hexlify(raw).upper())
|
||||||
|
|
||||||
|
def _calc_checksum(self, secret):
|
||||||
|
if isinstance(secret, bytes):
|
||||||
|
secret = secret.decode("utf-8")
|
||||||
|
salt = self.salt
|
||||||
|
return _raw_mssql(secret, salt) + _raw_mssql(secret.upper(), salt)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def verify(cls, secret, hash):
|
||||||
|
# NOTE: we only compare against the upper-case hash
|
||||||
|
# XXX: add 'full' just to verify both checksums?
|
||||||
|
uh.validate_secret(secret)
|
||||||
|
self = cls.from_string(hash)
|
||||||
|
chk = self.checksum
|
||||||
|
if chk is None:
|
||||||
|
raise uh.exc.MissingDigestError(cls)
|
||||||
|
if isinstance(secret, bytes):
|
||||||
|
secret = secret.decode("utf-8")
|
||||||
|
result = _raw_mssql(secret.upper(), self.salt)
|
||||||
|
return consteq(result, chk[20:])
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# handler
|
||||||
|
#=============================================================================
|
||||||
|
class mssql2005(uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
|
||||||
|
"""This class implements the password hash used by MS-SQL 2005, and follows the :ref:`password-hash-api`.
|
||||||
|
|
||||||
|
It supports a fixed-length salt.
|
||||||
|
|
||||||
|
The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords:
|
||||||
|
|
||||||
|
:type salt: bytes
|
||||||
|
:param salt:
|
||||||
|
Optional salt string.
|
||||||
|
If not specified, one will be autogenerated (this is recommended).
|
||||||
|
If specified, it must be 4 bytes in length.
|
||||||
|
|
||||||
|
:type relaxed: bool
|
||||||
|
:param relaxed:
|
||||||
|
By default, providing an invalid value for one of the other
|
||||||
|
keywords will result in a :exc:`ValueError`. If ``relaxed=True``,
|
||||||
|
and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning`
|
||||||
|
will be issued instead. Correctable errors include
|
||||||
|
``salt`` strings that are too long.
|
||||||
|
"""
|
||||||
|
#===================================================================
|
||||||
|
# algorithm information
|
||||||
|
#===================================================================
|
||||||
|
name = "mssql2005"
|
||||||
|
setting_kwds = ("salt",)
|
||||||
|
|
||||||
|
checksum_size = 20
|
||||||
|
min_salt_size = max_salt_size = 4
|
||||||
|
|
||||||
|
#===================================================================
|
||||||
|
# formatting
|
||||||
|
#===================================================================
|
||||||
|
|
||||||
|
# 0x0100 - 2 byte identifier
|
||||||
|
# 4 byte salt
|
||||||
|
# 20 byte checksum
|
||||||
|
# = 26 bytes
|
||||||
|
# encoded '0x' + 52 chars = 54
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def identify(cls, hash):
|
||||||
|
return _ident_mssql(hash, 54, 26)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_string(cls, hash):
|
||||||
|
data = _parse_mssql(hash, 54, 26, cls)
|
||||||
|
return cls(salt=data[:4], checksum=data[4:])
|
||||||
|
|
||||||
|
def to_string(self):
|
||||||
|
raw = self.salt + self.checksum
|
||||||
|
# raw bytes format - BIDENT2 + raw
|
||||||
|
return "0x0100" + bascii_to_str(hexlify(raw)).upper()
|
||||||
|
|
||||||
|
def _calc_checksum(self, secret):
|
||||||
|
if isinstance(secret, bytes):
|
||||||
|
secret = secret.decode("utf-8")
|
||||||
|
return _raw_mssql(secret, self.salt)
|
||||||
|
|
||||||
|
#===================================================================
|
||||||
|
# eoc
|
||||||
|
#===================================================================
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# eof
|
||||||
|
#=============================================================================
|
||||||
128
venv/Lib/site-packages/passlib/handlers/mysql.py
Normal file
128
venv/Lib/site-packages/passlib/handlers/mysql.py
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
"""passlib.handlers.mysql
|
||||||
|
|
||||||
|
MySQL 3.2.3 / OLD_PASSWORD()
|
||||||
|
|
||||||
|
This implements Mysql's OLD_PASSWORD algorithm, introduced in version 3.2.3, deprecated in version 4.1.
|
||||||
|
|
||||||
|
See :mod:`passlib.handlers.mysql_41` for the new algorithm was put in place in version 4.1
|
||||||
|
|
||||||
|
This algorithm is known to be very insecure, and should only be used to verify existing password hashes.
|
||||||
|
|
||||||
|
http://djangosnippets.org/snippets/1508/
|
||||||
|
|
||||||
|
MySQL 4.1.1 / NEW PASSWORD
|
||||||
|
This implements Mysql new PASSWORD algorithm, introduced in version 4.1.
|
||||||
|
|
||||||
|
This function is unsalted, and therefore not very secure against rainbow attacks.
|
||||||
|
It should only be used when dealing with mysql passwords,
|
||||||
|
for all other purposes, you should use a salted hash function.
|
||||||
|
|
||||||
|
Description taken from http://dev.mysql.com/doc/refman/6.0/en/password-hashing.html
|
||||||
|
"""
|
||||||
|
#=============================================================================
|
||||||
|
# imports
|
||||||
|
#=============================================================================
|
||||||
|
# core
|
||||||
|
from hashlib import sha1
|
||||||
|
import re
|
||||||
|
import logging; log = logging.getLogger(__name__)
|
||||||
|
from warnings import warn
|
||||||
|
# site
|
||||||
|
# pkg
|
||||||
|
from passlib.utils import to_native_str
|
||||||
|
from passlib.utils.compat import bascii_to_str, unicode, u, \
|
||||||
|
byte_elem_value, str_to_uascii
|
||||||
|
import passlib.utils.handlers as uh
|
||||||
|
# local
|
||||||
|
__all__ = [
|
||||||
|
'mysql323',
|
||||||
|
'mysq41',
|
||||||
|
]
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# backend
|
||||||
|
#=============================================================================
|
||||||
|
class mysql323(uh.StaticHandler):
|
||||||
|
"""This class implements the MySQL 3.2.3 password hash, and follows the :ref:`password-hash-api`.
|
||||||
|
|
||||||
|
It has no salt and a single fixed round.
|
||||||
|
|
||||||
|
The :meth:`~passlib.ifc.PasswordHash.hash` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods accept no optional keywords.
|
||||||
|
"""
|
||||||
|
#===================================================================
|
||||||
|
# class attrs
|
||||||
|
#===================================================================
|
||||||
|
name = "mysql323"
|
||||||
|
checksum_size = 16
|
||||||
|
checksum_chars = uh.HEX_CHARS
|
||||||
|
|
||||||
|
#===================================================================
|
||||||
|
# methods
|
||||||
|
#===================================================================
|
||||||
|
@classmethod
|
||||||
|
def _norm_hash(cls, hash):
|
||||||
|
return hash.lower()
|
||||||
|
|
||||||
|
def _calc_checksum(self, secret):
|
||||||
|
# FIXME: no idea if mysql has a policy about handling unicode passwords
|
||||||
|
if isinstance(secret, unicode):
|
||||||
|
secret = secret.encode("utf-8")
|
||||||
|
|
||||||
|
MASK_32 = 0xffffffff
|
||||||
|
MASK_31 = 0x7fffffff
|
||||||
|
WHITE = b' \t'
|
||||||
|
|
||||||
|
nr1 = 0x50305735
|
||||||
|
nr2 = 0x12345671
|
||||||
|
add = 7
|
||||||
|
for c in secret:
|
||||||
|
if c in WHITE:
|
||||||
|
continue
|
||||||
|
tmp = byte_elem_value(c)
|
||||||
|
nr1 ^= ((((nr1 & 63)+add)*tmp) + (nr1 << 8)) & MASK_32
|
||||||
|
nr2 = (nr2+((nr2 << 8) ^ nr1)) & MASK_32
|
||||||
|
add = (add+tmp) & MASK_32
|
||||||
|
return u("%08x%08x") % (nr1 & MASK_31, nr2 & MASK_31)
|
||||||
|
|
||||||
|
#===================================================================
|
||||||
|
# eoc
|
||||||
|
#===================================================================
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# handler
|
||||||
|
#=============================================================================
|
||||||
|
class mysql41(uh.StaticHandler):
|
||||||
|
"""This class implements the MySQL 4.1 password hash, and follows the :ref:`password-hash-api`.
|
||||||
|
|
||||||
|
It has no salt and a single fixed round.
|
||||||
|
|
||||||
|
The :meth:`~passlib.ifc.PasswordHash.hash` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods accept no optional keywords.
|
||||||
|
"""
|
||||||
|
#===================================================================
|
||||||
|
# class attrs
|
||||||
|
#===================================================================
|
||||||
|
name = "mysql41"
|
||||||
|
_hash_prefix = u("*")
|
||||||
|
checksum_chars = uh.HEX_CHARS
|
||||||
|
checksum_size = 40
|
||||||
|
|
||||||
|
#===================================================================
|
||||||
|
# methods
|
||||||
|
#===================================================================
|
||||||
|
@classmethod
|
||||||
|
def _norm_hash(cls, hash):
|
||||||
|
return hash.upper()
|
||||||
|
|
||||||
|
def _calc_checksum(self, secret):
|
||||||
|
# FIXME: no idea if mysql has a policy about handling unicode passwords
|
||||||
|
if isinstance(secret, unicode):
|
||||||
|
secret = secret.encode("utf-8")
|
||||||
|
return str_to_uascii(sha1(sha1(secret).digest()).hexdigest()).upper()
|
||||||
|
|
||||||
|
#===================================================================
|
||||||
|
# eoc
|
||||||
|
#===================================================================
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# eof
|
||||||
|
#=============================================================================
|
||||||
172
venv/Lib/site-packages/passlib/handlers/oracle.py
Normal file
172
venv/Lib/site-packages/passlib/handlers/oracle.py
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
"""passlib.handlers.oracle - Oracle DB Password Hashes"""
|
||||||
|
#=============================================================================
|
||||||
|
# imports
|
||||||
|
#=============================================================================
|
||||||
|
# core
|
||||||
|
from binascii import hexlify, unhexlify
|
||||||
|
from hashlib import sha1
|
||||||
|
import re
|
||||||
|
import logging; log = logging.getLogger(__name__)
|
||||||
|
# site
|
||||||
|
# pkg
|
||||||
|
from passlib.utils import to_unicode, xor_bytes
|
||||||
|
from passlib.utils.compat import irange, u, \
|
||||||
|
uascii_to_str, unicode, str_to_uascii
|
||||||
|
from passlib.crypto.des import des_encrypt_block
|
||||||
|
import passlib.utils.handlers as uh
|
||||||
|
# local
|
||||||
|
__all__ = [
|
||||||
|
"oracle10g",
|
||||||
|
"oracle11g"
|
||||||
|
]
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# oracle10
|
||||||
|
#=============================================================================
|
||||||
|
def des_cbc_encrypt(key, value, iv=b'\x00' * 8, pad=b'\x00'):
|
||||||
|
"""performs des-cbc encryption, returns only last block.
|
||||||
|
|
||||||
|
this performs a specific DES-CBC encryption implementation
|
||||||
|
as needed by the Oracle10 hash. it probably won't be useful for
|
||||||
|
other purposes as-is.
|
||||||
|
|
||||||
|
input value is null-padded to multiple of 8 bytes.
|
||||||
|
|
||||||
|
:arg key: des key as bytes
|
||||||
|
:arg value: value to encrypt, as bytes.
|
||||||
|
:param iv: optional IV
|
||||||
|
:param pad: optional pad byte
|
||||||
|
|
||||||
|
:returns: last block of DES-CBC encryption of all ``value``'s byte blocks.
|
||||||
|
"""
|
||||||
|
value += pad * (-len(value) % 8) # null pad to multiple of 8
|
||||||
|
hash = iv # start things off
|
||||||
|
for offset in irange(0,len(value),8):
|
||||||
|
chunk = xor_bytes(hash, value[offset:offset+8])
|
||||||
|
hash = des_encrypt_block(key, chunk)
|
||||||
|
return hash
|
||||||
|
|
||||||
|
# magic string used as initial des key by oracle10
|
||||||
|
ORACLE10_MAGIC = b"\x01\x23\x45\x67\x89\xAB\xCD\xEF"
|
||||||
|
|
||||||
|
class oracle10(uh.HasUserContext, uh.StaticHandler):
|
||||||
|
"""This class implements the password hash used by Oracle up to version 10g, and follows the :ref:`password-hash-api`.
|
||||||
|
|
||||||
|
It does a single round of hashing, and relies on the username as the salt.
|
||||||
|
|
||||||
|
The :meth:`~passlib.ifc.PasswordHash.hash`, :meth:`~passlib.ifc.PasswordHash.genhash`, and :meth:`~passlib.ifc.PasswordHash.verify` methods all require the
|
||||||
|
following additional contextual keywords:
|
||||||
|
|
||||||
|
:type user: str
|
||||||
|
:param user: name of oracle user account this password is associated with.
|
||||||
|
"""
|
||||||
|
#===================================================================
|
||||||
|
# algorithm information
|
||||||
|
#===================================================================
|
||||||
|
name = "oracle10"
|
||||||
|
checksum_chars = uh.HEX_CHARS
|
||||||
|
checksum_size = 16
|
||||||
|
|
||||||
|
#===================================================================
|
||||||
|
# methods
|
||||||
|
#===================================================================
|
||||||
|
@classmethod
|
||||||
|
def _norm_hash(cls, hash):
|
||||||
|
return hash.upper()
|
||||||
|
|
||||||
|
def _calc_checksum(self, secret):
|
||||||
|
# FIXME: not sure how oracle handles unicode.
|
||||||
|
# online docs about 10g hash indicate it puts ascii chars
|
||||||
|
# in a 2-byte encoding w/ the high byte set to null.
|
||||||
|
# they don't say how it handles other chars, or what encoding.
|
||||||
|
#
|
||||||
|
# so for now, encoding secret & user to utf-16-be,
|
||||||
|
# since that fits, and if secret/user is bytes,
|
||||||
|
# we assume utf-8, and decode first.
|
||||||
|
#
|
||||||
|
# this whole mess really needs someone w/ an oracle system,
|
||||||
|
# and some answers :)
|
||||||
|
if isinstance(secret, bytes):
|
||||||
|
secret = secret.decode("utf-8")
|
||||||
|
user = to_unicode(self.user, "utf-8", param="user")
|
||||||
|
input = (user+secret).upper().encode("utf-16-be")
|
||||||
|
hash = des_cbc_encrypt(ORACLE10_MAGIC, input)
|
||||||
|
hash = des_cbc_encrypt(hash, input)
|
||||||
|
return hexlify(hash).decode("ascii").upper()
|
||||||
|
|
||||||
|
#===================================================================
|
||||||
|
# eoc
|
||||||
|
#===================================================================
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# oracle11
|
||||||
|
#=============================================================================
|
||||||
|
class oracle11(uh.HasSalt, uh.GenericHandler):
|
||||||
|
"""This class implements the Oracle11g password hash, and follows the :ref:`password-hash-api`.
|
||||||
|
|
||||||
|
It supports a fixed-length salt.
|
||||||
|
|
||||||
|
The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords:
|
||||||
|
|
||||||
|
:type salt: str
|
||||||
|
:param salt:
|
||||||
|
Optional salt string.
|
||||||
|
If not specified, one will be autogenerated (this is recommended).
|
||||||
|
If specified, it must be 20 hexadecimal characters.
|
||||||
|
|
||||||
|
:type relaxed: bool
|
||||||
|
:param relaxed:
|
||||||
|
By default, providing an invalid value for one of the other
|
||||||
|
keywords will result in a :exc:`ValueError`. If ``relaxed=True``,
|
||||||
|
and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning`
|
||||||
|
will be issued instead. Correctable errors include
|
||||||
|
``salt`` strings that are too long.
|
||||||
|
|
||||||
|
.. versionadded:: 1.6
|
||||||
|
"""
|
||||||
|
#===================================================================
|
||||||
|
# class attrs
|
||||||
|
#===================================================================
|
||||||
|
#--GenericHandler--
|
||||||
|
name = "oracle11"
|
||||||
|
setting_kwds = ("salt",)
|
||||||
|
checksum_size = 40
|
||||||
|
checksum_chars = uh.UPPER_HEX_CHARS
|
||||||
|
|
||||||
|
#--HasSalt--
|
||||||
|
min_salt_size = max_salt_size = 20
|
||||||
|
salt_chars = uh.UPPER_HEX_CHARS
|
||||||
|
|
||||||
|
|
||||||
|
#===================================================================
|
||||||
|
# methods
|
||||||
|
#===================================================================
|
||||||
|
_hash_regex = re.compile(u("^S:(?P<chk>[0-9a-f]{40})(?P<salt>[0-9a-f]{20})$"), re.I)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_string(cls, hash):
|
||||||
|
hash = to_unicode(hash, "ascii", "hash")
|
||||||
|
m = cls._hash_regex.match(hash)
|
||||||
|
if not m:
|
||||||
|
raise uh.exc.InvalidHashError(cls)
|
||||||
|
salt, chk = m.group("salt", "chk")
|
||||||
|
return cls(salt=salt, checksum=chk.upper())
|
||||||
|
|
||||||
|
def to_string(self):
|
||||||
|
chk = self.checksum
|
||||||
|
hash = u("S:%s%s") % (chk.upper(), self.salt.upper())
|
||||||
|
return uascii_to_str(hash)
|
||||||
|
|
||||||
|
def _calc_checksum(self, secret):
|
||||||
|
if isinstance(secret, unicode):
|
||||||
|
secret = secret.encode("utf-8")
|
||||||
|
chk = sha1(secret + unhexlify(self.salt.encode("ascii"))).hexdigest()
|
||||||
|
return str_to_uascii(chk).upper()
|
||||||
|
|
||||||
|
#===================================================================
|
||||||
|
# eoc
|
||||||
|
#===================================================================
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# eof
|
||||||
|
#=============================================================================
|
||||||
475
venv/Lib/site-packages/passlib/handlers/pbkdf2.py
Normal file
475
venv/Lib/site-packages/passlib/handlers/pbkdf2.py
Normal file
@@ -0,0 +1,475 @@
|
|||||||
|
"""passlib.handlers.pbkdf - PBKDF2 based hashes"""
|
||||||
|
#=============================================================================
|
||||||
|
# imports
|
||||||
|
#=============================================================================
|
||||||
|
# core
|
||||||
|
from binascii import hexlify, unhexlify
|
||||||
|
from base64 import b64encode, b64decode
|
||||||
|
import logging; log = logging.getLogger(__name__)
|
||||||
|
# site
|
||||||
|
# pkg
|
||||||
|
from passlib.utils import to_unicode
|
||||||
|
from passlib.utils.binary import ab64_decode, ab64_encode
|
||||||
|
from passlib.utils.compat import str_to_bascii, u, uascii_to_str, unicode
|
||||||
|
from passlib.crypto.digest import pbkdf2_hmac
|
||||||
|
import passlib.utils.handlers as uh
|
||||||
|
# local
|
||||||
|
__all__ = [
|
||||||
|
"pbkdf2_sha1",
|
||||||
|
"pbkdf2_sha256",
|
||||||
|
"pbkdf2_sha512",
|
||||||
|
"cta_pbkdf2_sha1",
|
||||||
|
"dlitz_pbkdf2_sha1",
|
||||||
|
"grub_pbkdf2_sha512",
|
||||||
|
]
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
#
|
||||||
|
#=============================================================================
|
||||||
|
class Pbkdf2DigestHandler(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
|
||||||
|
"""base class for various pbkdf2_{digest} algorithms"""
|
||||||
|
#===================================================================
|
||||||
|
# class attrs
|
||||||
|
#===================================================================
|
||||||
|
|
||||||
|
#--GenericHandler--
|
||||||
|
setting_kwds = ("salt", "salt_size", "rounds")
|
||||||
|
checksum_chars = uh.HASH64_CHARS
|
||||||
|
|
||||||
|
#--HasSalt--
|
||||||
|
default_salt_size = 16
|
||||||
|
max_salt_size = 1024
|
||||||
|
|
||||||
|
#--HasRounds--
|
||||||
|
default_rounds = None # set by subclass
|
||||||
|
min_rounds = 1
|
||||||
|
max_rounds = 0xffffffff # setting at 32-bit limit for now
|
||||||
|
rounds_cost = "linear"
|
||||||
|
|
||||||
|
#--this class--
|
||||||
|
_digest = None # name of subclass-specified hash
|
||||||
|
|
||||||
|
# NOTE: max_salt_size and max_rounds are arbitrarily chosen to provide sanity check.
|
||||||
|
# the underlying pbkdf2 specifies no bounds for either.
|
||||||
|
|
||||||
|
# NOTE: defaults chosen to be at least as large as pbkdf2 rfc recommends...
|
||||||
|
# >8 bytes of entropy in salt, >1000 rounds
|
||||||
|
# increased due to time since rfc established
|
||||||
|
|
||||||
|
#===================================================================
|
||||||
|
# methods
|
||||||
|
#===================================================================
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_string(cls, hash):
|
||||||
|
rounds, salt, chk = uh.parse_mc3(hash, cls.ident, handler=cls)
|
||||||
|
salt = ab64_decode(salt.encode("ascii"))
|
||||||
|
if chk:
|
||||||
|
chk = ab64_decode(chk.encode("ascii"))
|
||||||
|
return cls(rounds=rounds, salt=salt, checksum=chk)
|
||||||
|
|
||||||
|
def to_string(self):
|
||||||
|
salt = ab64_encode(self.salt).decode("ascii")
|
||||||
|
chk = ab64_encode(self.checksum).decode("ascii")
|
||||||
|
return uh.render_mc3(self.ident, self.rounds, salt, chk)
|
||||||
|
|
||||||
|
def _calc_checksum(self, secret):
|
||||||
|
# NOTE: pbkdf2_hmac() will encode secret & salt using UTF8
|
||||||
|
return pbkdf2_hmac(self._digest, secret, self.salt, self.rounds, self.checksum_size)
|
||||||
|
|
||||||
|
def create_pbkdf2_hash(hash_name, digest_size, rounds=12000, ident=None, module=__name__):
|
||||||
|
"""create new Pbkdf2DigestHandler subclass for a specific hash"""
|
||||||
|
name = 'pbkdf2_' + hash_name
|
||||||
|
if ident is None:
|
||||||
|
ident = u("$pbkdf2-%s$") % (hash_name,)
|
||||||
|
base = Pbkdf2DigestHandler
|
||||||
|
return type(name, (base,), dict(
|
||||||
|
__module__=module, # so ABCMeta won't clobber it.
|
||||||
|
name=name,
|
||||||
|
ident=ident,
|
||||||
|
_digest = hash_name,
|
||||||
|
default_rounds=rounds,
|
||||||
|
checksum_size=digest_size,
|
||||||
|
encoded_checksum_size=(digest_size*4+2)//3,
|
||||||
|
__doc__="""This class implements a generic ``PBKDF2-HMAC-%(digest)s``-based password hash, and follows the :ref:`password-hash-api`.
|
||||||
|
|
||||||
|
It supports a variable-length salt, and a variable number of rounds.
|
||||||
|
|
||||||
|
The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords:
|
||||||
|
|
||||||
|
:type salt: bytes
|
||||||
|
:param salt:
|
||||||
|
Optional salt bytes.
|
||||||
|
If specified, the length must be between 0-1024 bytes.
|
||||||
|
If not specified, a %(dsc)d byte salt will be autogenerated (this is recommended).
|
||||||
|
|
||||||
|
:type salt_size: int
|
||||||
|
:param salt_size:
|
||||||
|
Optional number of bytes to use when autogenerating new salts.
|
||||||
|
Defaults to %(dsc)d bytes, but can be any value between 0 and 1024.
|
||||||
|
|
||||||
|
:type rounds: int
|
||||||
|
:param rounds:
|
||||||
|
Optional number of rounds to use.
|
||||||
|
Defaults to %(dr)d, but must be within ``range(1,1<<32)``.
|
||||||
|
|
||||||
|
:type relaxed: bool
|
||||||
|
:param relaxed:
|
||||||
|
By default, providing an invalid value for one of the other
|
||||||
|
keywords will result in a :exc:`ValueError`. If ``relaxed=True``,
|
||||||
|
and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning`
|
||||||
|
will be issued instead. Correctable errors include ``rounds``
|
||||||
|
that are too small or too large, and ``salt`` strings that are too long.
|
||||||
|
|
||||||
|
.. versionadded:: 1.6
|
||||||
|
""" % dict(digest=hash_name.upper(), dsc=base.default_salt_size, dr=rounds)
|
||||||
|
))
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
# derived handlers
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
pbkdf2_sha1 = create_pbkdf2_hash("sha1", 20, 131000, ident=u("$pbkdf2$"))
|
||||||
|
pbkdf2_sha256 = create_pbkdf2_hash("sha256", 32, 29000)
|
||||||
|
pbkdf2_sha512 = create_pbkdf2_hash("sha512", 64, 25000)
|
||||||
|
|
||||||
|
ldap_pbkdf2_sha1 = uh.PrefixWrapper("ldap_pbkdf2_sha1", pbkdf2_sha1, "{PBKDF2}", "$pbkdf2$", ident=True)
|
||||||
|
ldap_pbkdf2_sha256 = uh.PrefixWrapper("ldap_pbkdf2_sha256", pbkdf2_sha256, "{PBKDF2-SHA256}", "$pbkdf2-sha256$", ident=True)
|
||||||
|
ldap_pbkdf2_sha512 = uh.PrefixWrapper("ldap_pbkdf2_sha512", pbkdf2_sha512, "{PBKDF2-SHA512}", "$pbkdf2-sha512$", ident=True)
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# cryptacular's pbkdf2 hash
|
||||||
|
#=============================================================================
|
||||||
|
|
||||||
|
# bytes used by cta hash for base64 values 63 & 64
|
||||||
|
CTA_ALTCHARS = b"-_"
|
||||||
|
|
||||||
|
class cta_pbkdf2_sha1(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
|
||||||
|
"""This class implements Cryptacular's PBKDF2-based crypt algorithm, and follows the :ref:`password-hash-api`.
|
||||||
|
|
||||||
|
It supports a variable-length salt, and a variable number of rounds.
|
||||||
|
|
||||||
|
The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords:
|
||||||
|
|
||||||
|
:type salt: bytes
|
||||||
|
:param salt:
|
||||||
|
Optional salt bytes.
|
||||||
|
If specified, it may be any length.
|
||||||
|
If not specified, a one will be autogenerated (this is recommended).
|
||||||
|
|
||||||
|
:type salt_size: int
|
||||||
|
:param salt_size:
|
||||||
|
Optional number of bytes to use when autogenerating new salts.
|
||||||
|
Defaults to 16 bytes, but can be any value between 0 and 1024.
|
||||||
|
|
||||||
|
:type rounds: int
|
||||||
|
:param rounds:
|
||||||
|
Optional number of rounds to use.
|
||||||
|
Defaults to 60000, must be within ``range(1,1<<32)``.
|
||||||
|
|
||||||
|
:type relaxed: bool
|
||||||
|
:param relaxed:
|
||||||
|
By default, providing an invalid value for one of the other
|
||||||
|
keywords will result in a :exc:`ValueError`. If ``relaxed=True``,
|
||||||
|
and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning`
|
||||||
|
will be issued instead. Correctable errors include ``rounds``
|
||||||
|
that are too small or too large, and ``salt`` strings that are too long.
|
||||||
|
|
||||||
|
.. versionadded:: 1.6
|
||||||
|
"""
|
||||||
|
|
||||||
|
#===================================================================
|
||||||
|
# class attrs
|
||||||
|
#===================================================================
|
||||||
|
#--GenericHandler--
|
||||||
|
name = "cta_pbkdf2_sha1"
|
||||||
|
setting_kwds = ("salt", "salt_size", "rounds")
|
||||||
|
ident = u("$p5k2$")
|
||||||
|
checksum_size = 20
|
||||||
|
|
||||||
|
# NOTE: max_salt_size and max_rounds are arbitrarily chosen to provide a
|
||||||
|
# sanity check. underlying algorithm (and reference implementation)
|
||||||
|
# allows effectively unbounded values for both of these parameters.
|
||||||
|
|
||||||
|
#--HasSalt--
|
||||||
|
default_salt_size = 16
|
||||||
|
max_salt_size = 1024
|
||||||
|
|
||||||
|
#--HasRounds--
|
||||||
|
default_rounds = pbkdf2_sha1.default_rounds
|
||||||
|
min_rounds = 1
|
||||||
|
max_rounds = 0xffffffff # setting at 32-bit limit for now
|
||||||
|
rounds_cost = "linear"
|
||||||
|
|
||||||
|
#===================================================================
|
||||||
|
# formatting
|
||||||
|
#===================================================================
|
||||||
|
|
||||||
|
# hash $p5k2$1000$ZxK4ZBJCfQg=$jJZVscWtO--p1-xIZl6jhO2LKR0=
|
||||||
|
# ident $p5k2$
|
||||||
|
# rounds 1000
|
||||||
|
# salt ZxK4ZBJCfQg=
|
||||||
|
# chk jJZVscWtO--p1-xIZl6jhO2LKR0=
|
||||||
|
# NOTE: rounds in hex
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_string(cls, hash):
|
||||||
|
# NOTE: passlib deviation - forbidding zero-padded rounds
|
||||||
|
rounds, salt, chk = uh.parse_mc3(hash, cls.ident, rounds_base=16, handler=cls)
|
||||||
|
salt = b64decode(salt.encode("ascii"), CTA_ALTCHARS)
|
||||||
|
if chk:
|
||||||
|
chk = b64decode(chk.encode("ascii"), CTA_ALTCHARS)
|
||||||
|
return cls(rounds=rounds, salt=salt, checksum=chk)
|
||||||
|
|
||||||
|
def to_string(self):
|
||||||
|
salt = b64encode(self.salt, CTA_ALTCHARS).decode("ascii")
|
||||||
|
chk = b64encode(self.checksum, CTA_ALTCHARS).decode("ascii")
|
||||||
|
return uh.render_mc3(self.ident, self.rounds, salt, chk, rounds_base=16)
|
||||||
|
|
||||||
|
#===================================================================
|
||||||
|
# backend
|
||||||
|
#===================================================================
|
||||||
|
def _calc_checksum(self, secret):
|
||||||
|
# NOTE: pbkdf2_hmac() will encode secret & salt using utf-8
|
||||||
|
return pbkdf2_hmac("sha1", secret, self.salt, self.rounds, 20)
|
||||||
|
|
||||||
|
#===================================================================
|
||||||
|
# eoc
|
||||||
|
#===================================================================
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# dlitz's pbkdf2 hash
|
||||||
|
#=============================================================================
|
||||||
|
class dlitz_pbkdf2_sha1(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
|
||||||
|
"""This class implements Dwayne Litzenberger's PBKDF2-based crypt algorithm, and follows the :ref:`password-hash-api`.
|
||||||
|
|
||||||
|
It supports a variable-length salt, and a variable number of rounds.
|
||||||
|
|
||||||
|
The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords:
|
||||||
|
|
||||||
|
:type salt: str
|
||||||
|
:param salt:
|
||||||
|
Optional salt string.
|
||||||
|
If specified, it may be any length, but must use the characters in the regexp range ``[./0-9A-Za-z]``.
|
||||||
|
If not specified, a 16 character salt will be autogenerated (this is recommended).
|
||||||
|
|
||||||
|
:type salt_size: int
|
||||||
|
:param salt_size:
|
||||||
|
Optional number of bytes to use when autogenerating new salts.
|
||||||
|
Defaults to 16 bytes, but can be any value between 0 and 1024.
|
||||||
|
|
||||||
|
:type rounds: int
|
||||||
|
:param rounds:
|
||||||
|
Optional number of rounds to use.
|
||||||
|
Defaults to 60000, must be within ``range(1,1<<32)``.
|
||||||
|
|
||||||
|
:type relaxed: bool
|
||||||
|
:param relaxed:
|
||||||
|
By default, providing an invalid value for one of the other
|
||||||
|
keywords will result in a :exc:`ValueError`. If ``relaxed=True``,
|
||||||
|
and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning`
|
||||||
|
will be issued instead. Correctable errors include ``rounds``
|
||||||
|
that are too small or too large, and ``salt`` strings that are too long.
|
||||||
|
|
||||||
|
.. versionadded:: 1.6
|
||||||
|
"""
|
||||||
|
|
||||||
|
#===================================================================
|
||||||
|
# class attrs
|
||||||
|
#===================================================================
|
||||||
|
#--GenericHandler--
|
||||||
|
name = "dlitz_pbkdf2_sha1"
|
||||||
|
setting_kwds = ("salt", "salt_size", "rounds")
|
||||||
|
ident = u("$p5k2$")
|
||||||
|
_stub_checksum = u("0" * 48 + "=")
|
||||||
|
|
||||||
|
# NOTE: max_salt_size and max_rounds are arbitrarily chosen to provide a
|
||||||
|
# sanity check. underlying algorithm (and reference implementation)
|
||||||
|
# allows effectively unbounded values for both of these parameters.
|
||||||
|
|
||||||
|
#--HasSalt--
|
||||||
|
default_salt_size = 16
|
||||||
|
max_salt_size = 1024
|
||||||
|
salt_chars = uh.HASH64_CHARS
|
||||||
|
|
||||||
|
#--HasRounds--
|
||||||
|
# NOTE: for security, the default here is set to match pbkdf2_sha1,
|
||||||
|
# even though this hash's extra block makes it twice as slow.
|
||||||
|
default_rounds = pbkdf2_sha1.default_rounds
|
||||||
|
min_rounds = 1
|
||||||
|
max_rounds = 0xffffffff # setting at 32-bit limit for now
|
||||||
|
rounds_cost = "linear"
|
||||||
|
|
||||||
|
#===================================================================
|
||||||
|
# formatting
|
||||||
|
#===================================================================
|
||||||
|
|
||||||
|
# hash $p5k2$c$u9HvcT4d$Sd1gwSVCLZYAuqZ25piRnbBEoAesaa/g
|
||||||
|
# ident $p5k2$
|
||||||
|
# rounds c
|
||||||
|
# salt u9HvcT4d
|
||||||
|
# chk Sd1gwSVCLZYAuqZ25piRnbBEoAesaa/g
|
||||||
|
# rounds in lowercase hex, no zero padding
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_string(cls, hash):
|
||||||
|
rounds, salt, chk = uh.parse_mc3(hash, cls.ident, rounds_base=16,
|
||||||
|
default_rounds=400, handler=cls)
|
||||||
|
return cls(rounds=rounds, salt=salt, checksum=chk)
|
||||||
|
|
||||||
|
def to_string(self):
|
||||||
|
rounds = self.rounds
|
||||||
|
if rounds == 400:
|
||||||
|
rounds = None # omit rounds measurement if == 400
|
||||||
|
return uh.render_mc3(self.ident, rounds, self.salt, self.checksum, rounds_base=16)
|
||||||
|
|
||||||
|
def _get_config(self):
|
||||||
|
rounds = self.rounds
|
||||||
|
if rounds == 400:
|
||||||
|
rounds = None # omit rounds measurement if == 400
|
||||||
|
return uh.render_mc3(self.ident, rounds, self.salt, None, rounds_base=16)
|
||||||
|
|
||||||
|
#===================================================================
|
||||||
|
# backend
|
||||||
|
#===================================================================
|
||||||
|
def _calc_checksum(self, secret):
|
||||||
|
# NOTE: pbkdf2_hmac() will encode secret & salt using utf-8
|
||||||
|
salt = self._get_config()
|
||||||
|
result = pbkdf2_hmac("sha1", secret, salt, self.rounds, 24)
|
||||||
|
return ab64_encode(result).decode("ascii")
|
||||||
|
|
||||||
|
#===================================================================
|
||||||
|
# eoc
|
||||||
|
#===================================================================
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# crowd
|
||||||
|
#=============================================================================
|
||||||
|
class atlassian_pbkdf2_sha1(uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
|
||||||
|
"""This class implements the PBKDF2 hash used by Atlassian.
|
||||||
|
|
||||||
|
It supports a fixed-length salt, and a fixed number of rounds.
|
||||||
|
|
||||||
|
The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords:
|
||||||
|
|
||||||
|
:type salt: bytes
|
||||||
|
:param salt:
|
||||||
|
Optional salt bytes.
|
||||||
|
If specified, the length must be exactly 16 bytes.
|
||||||
|
If not specified, a salt will be autogenerated (this is recommended).
|
||||||
|
|
||||||
|
:type relaxed: bool
|
||||||
|
:param relaxed:
|
||||||
|
By default, providing an invalid value for one of the other
|
||||||
|
keywords will result in a :exc:`ValueError`. If ``relaxed=True``,
|
||||||
|
and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning`
|
||||||
|
will be issued instead. Correctable errors include
|
||||||
|
``salt`` strings that are too long.
|
||||||
|
|
||||||
|
.. versionadded:: 1.6
|
||||||
|
"""
|
||||||
|
#--GenericHandler--
|
||||||
|
name = "atlassian_pbkdf2_sha1"
|
||||||
|
setting_kwds =("salt",)
|
||||||
|
ident = u("{PKCS5S2}")
|
||||||
|
checksum_size = 32
|
||||||
|
|
||||||
|
#--HasRawSalt--
|
||||||
|
min_salt_size = max_salt_size = 16
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_string(cls, hash):
|
||||||
|
hash = to_unicode(hash, "ascii", "hash")
|
||||||
|
ident = cls.ident
|
||||||
|
if not hash.startswith(ident):
|
||||||
|
raise uh.exc.InvalidHashError(cls)
|
||||||
|
data = b64decode(hash[len(ident):].encode("ascii"))
|
||||||
|
salt, chk = data[:16], data[16:]
|
||||||
|
return cls(salt=salt, checksum=chk)
|
||||||
|
|
||||||
|
def to_string(self):
|
||||||
|
data = self.salt + self.checksum
|
||||||
|
hash = self.ident + b64encode(data).decode("ascii")
|
||||||
|
return uascii_to_str(hash)
|
||||||
|
|
||||||
|
def _calc_checksum(self, secret):
|
||||||
|
# TODO: find out what crowd's policy is re: unicode
|
||||||
|
# crowd seems to use a fixed number of rounds.
|
||||||
|
# NOTE: pbkdf2_hmac() will encode secret & salt using utf-8
|
||||||
|
return pbkdf2_hmac("sha1", secret, self.salt, 10000, 32)
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# grub
|
||||||
|
#=============================================================================
|
||||||
|
class grub_pbkdf2_sha512(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
|
||||||
|
"""This class implements Grub's pbkdf2-hmac-sha512 hash, and follows the :ref:`password-hash-api`.
|
||||||
|
|
||||||
|
It supports a variable-length salt, and a variable number of rounds.
|
||||||
|
|
||||||
|
The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords:
|
||||||
|
|
||||||
|
:type salt: bytes
|
||||||
|
:param salt:
|
||||||
|
Optional salt bytes.
|
||||||
|
If specified, the length must be between 0-1024 bytes.
|
||||||
|
If not specified, a 64 byte salt will be autogenerated (this is recommended).
|
||||||
|
|
||||||
|
:type salt_size: int
|
||||||
|
:param salt_size:
|
||||||
|
Optional number of bytes to use when autogenerating new salts.
|
||||||
|
Defaults to 64 bytes, but can be any value between 0 and 1024.
|
||||||
|
|
||||||
|
:type rounds: int
|
||||||
|
:param rounds:
|
||||||
|
Optional number of rounds to use.
|
||||||
|
Defaults to 19000, but must be within ``range(1,1<<32)``.
|
||||||
|
|
||||||
|
:type relaxed: bool
|
||||||
|
:param relaxed:
|
||||||
|
By default, providing an invalid value for one of the other
|
||||||
|
keywords will result in a :exc:`ValueError`. If ``relaxed=True``,
|
||||||
|
and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning`
|
||||||
|
will be issued instead. Correctable errors include ``rounds``
|
||||||
|
that are too small or too large, and ``salt`` strings that are too long.
|
||||||
|
|
||||||
|
.. versionadded:: 1.6
|
||||||
|
"""
|
||||||
|
name = "grub_pbkdf2_sha512"
|
||||||
|
setting_kwds = ("salt", "salt_size", "rounds")
|
||||||
|
|
||||||
|
ident = u("grub.pbkdf2.sha512.")
|
||||||
|
checksum_size = 64
|
||||||
|
|
||||||
|
# NOTE: max_salt_size and max_rounds are arbitrarily chosen to provide a
|
||||||
|
# sanity check. the underlying pbkdf2 specifies no bounds for either,
|
||||||
|
# and it's not clear what grub specifies.
|
||||||
|
|
||||||
|
default_salt_size = 64
|
||||||
|
max_salt_size = 1024
|
||||||
|
|
||||||
|
default_rounds = pbkdf2_sha512.default_rounds
|
||||||
|
min_rounds = 1
|
||||||
|
max_rounds = 0xffffffff # setting at 32-bit limit for now
|
||||||
|
rounds_cost = "linear"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_string(cls, hash):
|
||||||
|
rounds, salt, chk = uh.parse_mc3(hash, cls.ident, sep=u("."),
|
||||||
|
handler=cls)
|
||||||
|
salt = unhexlify(salt.encode("ascii"))
|
||||||
|
if chk:
|
||||||
|
chk = unhexlify(chk.encode("ascii"))
|
||||||
|
return cls(rounds=rounds, salt=salt, checksum=chk)
|
||||||
|
|
||||||
|
def to_string(self):
|
||||||
|
salt = hexlify(self.salt).decode("ascii").upper()
|
||||||
|
chk = hexlify(self.checksum).decode("ascii").upper()
|
||||||
|
return uh.render_mc3(self.ident, self.rounds, salt, chk, sep=u("."))
|
||||||
|
|
||||||
|
def _calc_checksum(self, secret):
|
||||||
|
# TODO: find out what grub's policy is re: unicode
|
||||||
|
# NOTE: pbkdf2_hmac() will encode secret & salt using utf-8
|
||||||
|
return pbkdf2_hmac("sha512", secret, self.salt, self.rounds, 64)
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# eof
|
||||||
|
#=============================================================================
|
||||||
Reference in New Issue
Block a user