mirror of
https://gitlab.com/MoonTestUse1/AdministrationItDepartmens.git
synced 2025-08-14 00:25:46 +02:00
Initial commit
This commit is contained in:
1220
venv/Lib/site-packages/passlib/utils/__init__.py
Normal file
1220
venv/Lib/site-packages/passlib/utils/__init__.py
Normal file
File diff suppressed because it is too large
Load Diff
884
venv/Lib/site-packages/passlib/utils/binary.py
Normal file
884
venv/Lib/site-packages/passlib/utils/binary.py
Normal file
@@ -0,0 +1,884 @@
|
||||
"""
|
||||
passlib.utils.binary - binary data encoding/decoding/manipulation
|
||||
"""
|
||||
#=============================================================================
|
||||
# imports
|
||||
#=============================================================================
|
||||
# core
|
||||
from __future__ import absolute_import, division, print_function
|
||||
from base64 import (
|
||||
b64encode,
|
||||
b64decode,
|
||||
b32decode as _b32decode,
|
||||
b32encode as _b32encode,
|
||||
)
|
||||
from binascii import b2a_base64, a2b_base64, Error as _BinAsciiError
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
# site
|
||||
# pkg
|
||||
from passlib import exc
|
||||
from passlib.utils.compat import (
|
||||
PY3, bascii_to_str,
|
||||
irange, imap, iter_byte_chars, join_byte_values, join_byte_elems,
|
||||
nextgetter, suppress_cause,
|
||||
u, unicode, unicode_or_bytes_types,
|
||||
)
|
||||
from passlib.utils.decor import memoized_property
|
||||
# from passlib.utils import BASE64_CHARS, HASH64_CHARS
|
||||
# local
|
||||
__all__ = [
|
||||
# constants
|
||||
"BASE64_CHARS", "PADDED_BASE64_CHARS",
|
||||
"AB64_CHARS",
|
||||
"HASH64_CHARS",
|
||||
"BCRYPT_CHARS",
|
||||
"HEX_CHARS", "LOWER_HEX_CHARS", "UPPER_HEX_CHARS",
|
||||
|
||||
"ALL_BYTE_VALUES",
|
||||
|
||||
# misc
|
||||
"compile_byte_translation",
|
||||
|
||||
# base64
|
||||
'ab64_encode', 'ab64_decode',
|
||||
'b64s_encode', 'b64s_decode',
|
||||
|
||||
# base32
|
||||
"b32encode", "b32decode",
|
||||
|
||||
# custom encodings
|
||||
'Base64Engine',
|
||||
'LazyBase64Engine',
|
||||
'h64',
|
||||
'h64big',
|
||||
'bcrypt64',
|
||||
]
|
||||
|
||||
#=============================================================================
|
||||
# constant strings
|
||||
#=============================================================================
|
||||
|
||||
#-------------------------------------------------------------
|
||||
# common salt_chars & checksum_chars values
|
||||
#-------------------------------------------------------------
|
||||
|
||||
#: standard base64 charmap
|
||||
BASE64_CHARS = u("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
|
||||
|
||||
#: alt base64 charmap -- "." instead of "+"
|
||||
AB64_CHARS = u("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./")
|
||||
|
||||
#: charmap used by HASH64 encoding.
|
||||
HASH64_CHARS = u("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
|
||||
|
||||
#: charmap used by BCrypt
|
||||
BCRYPT_CHARS = u("./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
|
||||
|
||||
#: std base64 chars + padding char
|
||||
PADDED_BASE64_CHARS = BASE64_CHARS + u("=")
|
||||
|
||||
#: all hex chars
|
||||
HEX_CHARS = u("0123456789abcdefABCDEF")
|
||||
|
||||
#: upper case hex chars
|
||||
UPPER_HEX_CHARS = u("0123456789ABCDEF")
|
||||
|
||||
#: lower case hex chars
|
||||
LOWER_HEX_CHARS = u("0123456789abcdef")
|
||||
|
||||
#-------------------------------------------------------------
|
||||
# byte strings
|
||||
#-------------------------------------------------------------
|
||||
|
||||
#: special byte string containing all possible byte values
|
||||
#: NOTE: for efficiency, this is treated as singleton by some of the code
|
||||
ALL_BYTE_VALUES = join_byte_values(irange(256))
|
||||
|
||||
#: some string constants we reuse
|
||||
B_EMPTY = b''
|
||||
B_NULL = b'\x00'
|
||||
B_EQUAL = b'='
|
||||
|
||||
#=============================================================================
|
||||
# byte translation
|
||||
#=============================================================================
|
||||
|
||||
#: base list used to compile byte translations
|
||||
_TRANSLATE_SOURCE = list(iter_byte_chars(ALL_BYTE_VALUES))
|
||||
|
||||
def compile_byte_translation(mapping, source=None):
|
||||
"""
|
||||
return a 256-byte string for translating bytes using specified mapping.
|
||||
bytes not specified by mapping will be left alone.
|
||||
|
||||
:param mapping:
|
||||
dict mapping input byte (str or int) -> output byte (str or int).
|
||||
|
||||
:param source:
|
||||
optional existing byte translation string to use as base.
|
||||
(must be 255-length byte string). defaults to identity mapping.
|
||||
|
||||
:returns:
|
||||
255-length byte string for passing to bytes().translate.
|
||||
"""
|
||||
if source is None:
|
||||
target = _TRANSLATE_SOURCE[:]
|
||||
else:
|
||||
assert isinstance(source, bytes) and len(source) == 255
|
||||
target = list(iter_byte_chars(source))
|
||||
for k, v in mapping.items():
|
||||
if isinstance(k, unicode_or_bytes_types):
|
||||
k = ord(k)
|
||||
assert isinstance(k, int) and 0 <= k < 256
|
||||
if isinstance(v, unicode):
|
||||
v = v.encode("ascii")
|
||||
assert isinstance(v, bytes) and len(v) == 1
|
||||
target[k] = v
|
||||
return B_EMPTY.join(target)
|
||||
|
||||
#=============================================================================
|
||||
# unpadding / stripped base64 encoding
|
||||
#=============================================================================
|
||||
def b64s_encode(data):
|
||||
"""
|
||||
encode using shortened base64 format which omits padding & whitespace.
|
||||
uses default ``+/`` altchars.
|
||||
"""
|
||||
return b2a_base64(data).rstrip(_BASE64_STRIP)
|
||||
|
||||
def b64s_decode(data):
|
||||
"""
|
||||
decode from shortened base64 format which omits padding & whitespace.
|
||||
uses default ``+/`` altchars.
|
||||
"""
|
||||
if isinstance(data, unicode):
|
||||
# needs bytes for replace() call, but want to accept ascii-unicode ala a2b_base64()
|
||||
try:
|
||||
data = data.encode("ascii")
|
||||
except UnicodeEncodeError:
|
||||
raise suppress_cause(ValueError("string argument should contain only ASCII characters"))
|
||||
off = len(data) & 3
|
||||
if off == 0:
|
||||
pass
|
||||
elif off == 2:
|
||||
data += _BASE64_PAD2
|
||||
elif off == 3:
|
||||
data += _BASE64_PAD1
|
||||
else: # off == 1
|
||||
raise ValueError("invalid base64 input")
|
||||
try:
|
||||
return a2b_base64(data)
|
||||
except _BinAsciiError as err:
|
||||
raise suppress_cause(TypeError(err))
|
||||
|
||||
#=============================================================================
|
||||
# adapted-base64 encoding
|
||||
#=============================================================================
|
||||
_BASE64_STRIP = b"=\n"
|
||||
_BASE64_PAD1 = b"="
|
||||
_BASE64_PAD2 = b"=="
|
||||
|
||||
# XXX: Passlib 1.8/1.9 -- deprecate everything that's using ab64_encode(),
|
||||
# have it start outputing b64s_encode() instead? can use a64_decode() to retain backwards compat.
|
||||
|
||||
def ab64_encode(data):
|
||||
"""
|
||||
encode using shortened base64 format which omits padding & whitespace.
|
||||
uses custom ``./`` altchars.
|
||||
|
||||
it is primarily used by Passlib's custom pbkdf2 hashes.
|
||||
"""
|
||||
return b64s_encode(data).replace(b"+", b".")
|
||||
|
||||
def ab64_decode(data):
|
||||
"""
|
||||
decode from shortened base64 format which omits padding & whitespace.
|
||||
uses custom ``./`` altchars, but supports decoding normal ``+/`` altchars as well.
|
||||
|
||||
it is primarily used by Passlib's custom pbkdf2 hashes.
|
||||
"""
|
||||
if isinstance(data, unicode):
|
||||
# needs bytes for replace() call, but want to accept ascii-unicode ala a2b_base64()
|
||||
try:
|
||||
data = data.encode("ascii")
|
||||
except UnicodeEncodeError:
|
||||
raise suppress_cause(ValueError("string argument should contain only ASCII characters"))
|
||||
return b64s_decode(data.replace(b".", b"+"))
|
||||
|
||||
#=============================================================================
|
||||
# base32 codec
|
||||
#=============================================================================
|
||||
|
||||
def b32encode(source):
|
||||
"""
|
||||
wrapper around :func:`base64.b32encode` which strips padding,
|
||||
and returns a native string.
|
||||
"""
|
||||
# NOTE: using upper case by default here, since 'I & L' are less
|
||||
# visually ambiguous than 'i & l'
|
||||
return bascii_to_str(_b32encode(source).rstrip(B_EQUAL))
|
||||
|
||||
#: byte translation map to replace common mistyped base32 chars.
|
||||
#: XXX: could correct '1' -> 'I', but could be a mistyped lower-case 'l', so leaving it alone.
|
||||
_b32_translate = compile_byte_translation({"8": "B", "0": "O"})
|
||||
|
||||
#: helper to add padding
|
||||
_b32_decode_pad = B_EQUAL * 8
|
||||
|
||||
def b32decode(source):
|
||||
"""
|
||||
wrapper around :func:`base64.b32decode`
|
||||
which handles common mistyped chars.
|
||||
padding optional, ignored if present.
|
||||
"""
|
||||
# encode & correct for typos
|
||||
if isinstance(source, unicode):
|
||||
source = source.encode("ascii")
|
||||
source = source.translate(_b32_translate)
|
||||
|
||||
# pad things so final string is multiple of 8
|
||||
remainder = len(source) & 0x7
|
||||
if remainder:
|
||||
source += _b32_decode_pad[:-remainder]
|
||||
|
||||
# XXX: py27 stdlib's version of this has some inefficiencies,
|
||||
# could look into using optimized version.
|
||||
return _b32decode(source, True)
|
||||
|
||||
#=============================================================================
|
||||
# base64-variant encoding
|
||||
#=============================================================================
|
||||
|
||||
class Base64Engine(object):
|
||||
"""Provides routines for encoding/decoding base64 data using
|
||||
arbitrary character mappings, selectable endianness, etc.
|
||||
|
||||
:arg charmap:
|
||||
A string of 64 unique characters,
|
||||
which will be used to encode successive 6-bit chunks of data.
|
||||
A character's position within the string should correspond
|
||||
to its 6-bit value.
|
||||
|
||||
:param big:
|
||||
Whether the encoding should be big-endian (default False).
|
||||
|
||||
.. note::
|
||||
This class does not currently handle base64's padding characters
|
||||
in any way what so ever.
|
||||
|
||||
Raw Bytes <-> Encoded Bytes
|
||||
===========================
|
||||
The following methods convert between raw bytes,
|
||||
and strings encoded using the engine's specific base64 variant:
|
||||
|
||||
.. automethod:: encode_bytes
|
||||
.. automethod:: decode_bytes
|
||||
.. automethod:: encode_transposed_bytes
|
||||
.. automethod:: decode_transposed_bytes
|
||||
|
||||
..
|
||||
.. automethod:: check_repair_unused
|
||||
.. automethod:: repair_unused
|
||||
|
||||
Integers <-> Encoded Bytes
|
||||
==========================
|
||||
The following methods allow encoding and decoding
|
||||
unsigned integers to and from the engine's specific base64 variant.
|
||||
Endianess is determined by the engine's ``big`` constructor keyword.
|
||||
|
||||
.. automethod:: encode_int6
|
||||
.. automethod:: decode_int6
|
||||
|
||||
.. automethod:: encode_int12
|
||||
.. automethod:: decode_int12
|
||||
|
||||
.. automethod:: encode_int24
|
||||
.. automethod:: decode_int24
|
||||
|
||||
.. automethod:: encode_int64
|
||||
.. automethod:: decode_int64
|
||||
|
||||
Informational Attributes
|
||||
========================
|
||||
.. attribute:: charmap
|
||||
|
||||
unicode string containing list of characters used in encoding;
|
||||
position in string matches 6bit value of character.
|
||||
|
||||
.. attribute:: bytemap
|
||||
|
||||
bytes version of :attr:`charmap`
|
||||
|
||||
.. attribute:: big
|
||||
|
||||
boolean flag indicating this using big-endian encoding.
|
||||
"""
|
||||
|
||||
#===================================================================
|
||||
# instance attrs
|
||||
#===================================================================
|
||||
# public config
|
||||
bytemap = None # charmap as bytes
|
||||
big = None # little or big endian
|
||||
|
||||
# filled in by init based on charmap.
|
||||
# (byte elem: single byte under py2, 8bit int under py3)
|
||||
_encode64 = None # maps 6bit value -> byte elem
|
||||
_decode64 = None # maps byte elem -> 6bit value
|
||||
|
||||
# helpers filled in by init based on endianness
|
||||
_encode_bytes = None # throws IndexError if bad value (shouldn't happen)
|
||||
_decode_bytes = None # throws KeyError if bad char.
|
||||
|
||||
#===================================================================
|
||||
# init
|
||||
#===================================================================
|
||||
def __init__(self, charmap, big=False):
|
||||
# validate charmap, generate encode64/decode64 helper functions.
|
||||
if isinstance(charmap, unicode):
|
||||
charmap = charmap.encode("latin-1")
|
||||
elif not isinstance(charmap, bytes):
|
||||
raise exc.ExpectedStringError(charmap, "charmap")
|
||||
if len(charmap) != 64:
|
||||
raise ValueError("charmap must be 64 characters in length")
|
||||
if len(set(charmap)) != 64:
|
||||
raise ValueError("charmap must not contain duplicate characters")
|
||||
self.bytemap = charmap
|
||||
self._encode64 = charmap.__getitem__
|
||||
lookup = dict((value, idx) for idx, value in enumerate(charmap))
|
||||
self._decode64 = lookup.__getitem__
|
||||
|
||||
# validate big, set appropriate helper functions.
|
||||
self.big = big
|
||||
if big:
|
||||
self._encode_bytes = self._encode_bytes_big
|
||||
self._decode_bytes = self._decode_bytes_big
|
||||
else:
|
||||
self._encode_bytes = self._encode_bytes_little
|
||||
self._decode_bytes = self._decode_bytes_little
|
||||
|
||||
# TODO: support padding character
|
||||
##if padding is not None:
|
||||
## if isinstance(padding, unicode):
|
||||
## padding = padding.encode("latin-1")
|
||||
## elif not isinstance(padding, bytes):
|
||||
## raise TypeError("padding char must be unicode or bytes")
|
||||
## if len(padding) != 1:
|
||||
## raise ValueError("padding must be single character")
|
||||
##self.padding = padding
|
||||
|
||||
@property
|
||||
def charmap(self):
|
||||
"""charmap as unicode"""
|
||||
return self.bytemap.decode("latin-1")
|
||||
|
||||
#===================================================================
|
||||
# encoding byte strings
|
||||
#===================================================================
|
||||
def encode_bytes(self, source):
|
||||
"""encode bytes to base64 string.
|
||||
|
||||
:arg source: byte string to encode.
|
||||
:returns: byte string containing encoded data.
|
||||
"""
|
||||
if not isinstance(source, bytes):
|
||||
raise TypeError("source must be bytes, not %s" % (type(source),))
|
||||
chunks, tail = divmod(len(source), 3)
|
||||
if PY3:
|
||||
next_value = nextgetter(iter(source))
|
||||
else:
|
||||
next_value = nextgetter(ord(elem) for elem in source)
|
||||
gen = self._encode_bytes(next_value, chunks, tail)
|
||||
out = join_byte_elems(imap(self._encode64, gen))
|
||||
##if tail:
|
||||
## padding = self.padding
|
||||
## if padding:
|
||||
## out += padding * (3-tail)
|
||||
return out
|
||||
|
||||
def _encode_bytes_little(self, next_value, chunks, tail):
|
||||
"""helper used by encode_bytes() to handle little-endian encoding"""
|
||||
#
|
||||
# output bit layout:
|
||||
#
|
||||
# first byte: v1 543210
|
||||
#
|
||||
# second byte: v1 ....76
|
||||
# +v2 3210..
|
||||
#
|
||||
# third byte: v2 ..7654
|
||||
# +v3 10....
|
||||
#
|
||||
# fourth byte: v3 765432
|
||||
#
|
||||
idx = 0
|
||||
while idx < chunks:
|
||||
v1 = next_value()
|
||||
v2 = next_value()
|
||||
v3 = next_value()
|
||||
yield v1 & 0x3f
|
||||
yield ((v2 & 0x0f)<<2)|(v1>>6)
|
||||
yield ((v3 & 0x03)<<4)|(v2>>4)
|
||||
yield v3>>2
|
||||
idx += 1
|
||||
if tail:
|
||||
v1 = next_value()
|
||||
if tail == 1:
|
||||
# note: 4 msb of last byte are padding
|
||||
yield v1 & 0x3f
|
||||
yield v1>>6
|
||||
else:
|
||||
assert tail == 2
|
||||
# note: 2 msb of last byte are padding
|
||||
v2 = next_value()
|
||||
yield v1 & 0x3f
|
||||
yield ((v2 & 0x0f)<<2)|(v1>>6)
|
||||
yield v2>>4
|
||||
|
||||
def _encode_bytes_big(self, next_value, chunks, tail):
|
||||
"""helper used by encode_bytes() to handle big-endian encoding"""
|
||||
#
|
||||
# output bit layout:
|
||||
#
|
||||
# first byte: v1 765432
|
||||
#
|
||||
# second byte: v1 10....
|
||||
# +v2 ..7654
|
||||
#
|
||||
# third byte: v2 3210..
|
||||
# +v3 ....76
|
||||
#
|
||||
# fourth byte: v3 543210
|
||||
#
|
||||
idx = 0
|
||||
while idx < chunks:
|
||||
v1 = next_value()
|
||||
v2 = next_value()
|
||||
v3 = next_value()
|
||||
yield v1>>2
|
||||
yield ((v1&0x03)<<4)|(v2>>4)
|
||||
yield ((v2&0x0f)<<2)|(v3>>6)
|
||||
yield v3 & 0x3f
|
||||
idx += 1
|
||||
if tail:
|
||||
v1 = next_value()
|
||||
if tail == 1:
|
||||
# note: 4 lsb of last byte are padding
|
||||
yield v1>>2
|
||||
yield (v1&0x03)<<4
|
||||
else:
|
||||
assert tail == 2
|
||||
# note: 2 lsb of last byte are padding
|
||||
v2 = next_value()
|
||||
yield v1>>2
|
||||
yield ((v1&0x03)<<4)|(v2>>4)
|
||||
yield ((v2&0x0f)<<2)
|
||||
|
||||
#===================================================================
|
||||
# decoding byte strings
|
||||
#===================================================================
|
||||
|
||||
def decode_bytes(self, source):
|
||||
"""decode bytes from base64 string.
|
||||
|
||||
:arg source: byte string to decode.
|
||||
:returns: byte string containing decoded data.
|
||||
"""
|
||||
if not isinstance(source, bytes):
|
||||
raise TypeError("source must be bytes, not %s" % (type(source),))
|
||||
##padding = self.padding
|
||||
##if padding:
|
||||
## # TODO: add padding size check?
|
||||
## source = source.rstrip(padding)
|
||||
chunks, tail = divmod(len(source), 4)
|
||||
if tail == 1:
|
||||
# only 6 bits left, can't encode a whole byte!
|
||||
raise ValueError("input string length cannot be == 1 mod 4")
|
||||
next_value = nextgetter(imap(self._decode64, source))
|
||||
try:
|
||||
return join_byte_values(self._decode_bytes(next_value, chunks, tail))
|
||||
except KeyError as err:
|
||||
raise ValueError("invalid character: %r" % (err.args[0],))
|
||||
|
||||
def _decode_bytes_little(self, next_value, chunks, tail):
|
||||
"""helper used by decode_bytes() to handle little-endian encoding"""
|
||||
#
|
||||
# input bit layout:
|
||||
#
|
||||
# first byte: v1 ..543210
|
||||
# +v2 10......
|
||||
#
|
||||
# second byte: v2 ....5432
|
||||
# +v3 3210....
|
||||
#
|
||||
# third byte: v3 ......54
|
||||
# +v4 543210..
|
||||
#
|
||||
idx = 0
|
||||
while idx < chunks:
|
||||
v1 = next_value()
|
||||
v2 = next_value()
|
||||
v3 = next_value()
|
||||
v4 = next_value()
|
||||
yield v1 | ((v2 & 0x3) << 6)
|
||||
yield (v2>>2) | ((v3 & 0xF) << 4)
|
||||
yield (v3>>4) | (v4<<2)
|
||||
idx += 1
|
||||
if tail:
|
||||
# tail is 2 or 3
|
||||
v1 = next_value()
|
||||
v2 = next_value()
|
||||
yield v1 | ((v2 & 0x3) << 6)
|
||||
# NOTE: if tail == 2, 4 msb of v2 are ignored (should be 0)
|
||||
if tail == 3:
|
||||
# NOTE: 2 msb of v3 are ignored (should be 0)
|
||||
v3 = next_value()
|
||||
yield (v2>>2) | ((v3 & 0xF) << 4)
|
||||
|
||||
def _decode_bytes_big(self, next_value, chunks, tail):
|
||||
"""helper used by decode_bytes() to handle big-endian encoding"""
|
||||
#
|
||||
# input bit layout:
|
||||
#
|
||||
# first byte: v1 543210..
|
||||
# +v2 ......54
|
||||
#
|
||||
# second byte: v2 3210....
|
||||
# +v3 ....5432
|
||||
#
|
||||
# third byte: v3 10......
|
||||
# +v4 ..543210
|
||||
#
|
||||
idx = 0
|
||||
while idx < chunks:
|
||||
v1 = next_value()
|
||||
v2 = next_value()
|
||||
v3 = next_value()
|
||||
v4 = next_value()
|
||||
yield (v1<<2) | (v2>>4)
|
||||
yield ((v2&0xF)<<4) | (v3>>2)
|
||||
yield ((v3&0x3)<<6) | v4
|
||||
idx += 1
|
||||
if tail:
|
||||
# tail is 2 or 3
|
||||
v1 = next_value()
|
||||
v2 = next_value()
|
||||
yield (v1<<2) | (v2>>4)
|
||||
# NOTE: if tail == 2, 4 lsb of v2 are ignored (should be 0)
|
||||
if tail == 3:
|
||||
# NOTE: 2 lsb of v3 are ignored (should be 0)
|
||||
v3 = next_value()
|
||||
yield ((v2&0xF)<<4) | (v3>>2)
|
||||
|
||||
#===================================================================
|
||||
# encode/decode helpers
|
||||
#===================================================================
|
||||
|
||||
# padmap2/3 - dict mapping last char of string ->
|
||||
# equivalent char with no padding bits set.
|
||||
|
||||
def __make_padset(self, bits):
|
||||
"""helper to generate set of valid last chars & bytes"""
|
||||
pset = set(c for i,c in enumerate(self.bytemap) if not i & bits)
|
||||
pset.update(c for i,c in enumerate(self.charmap) if not i & bits)
|
||||
return frozenset(pset)
|
||||
|
||||
@memoized_property
|
||||
def _padinfo2(self):
|
||||
"""mask to clear padding bits, and valid last bytes (for strings 2 % 4)"""
|
||||
# 4 bits of last char unused (lsb for big, msb for little)
|
||||
bits = 15 if self.big else (15<<2)
|
||||
return ~bits, self.__make_padset(bits)
|
||||
|
||||
@memoized_property
|
||||
def _padinfo3(self):
|
||||
"""mask to clear padding bits, and valid last bytes (for strings 3 % 4)"""
|
||||
# 2 bits of last char unused (lsb for big, msb for little)
|
||||
bits = 3 if self.big else (3<<4)
|
||||
return ~bits, self.__make_padset(bits)
|
||||
|
||||
def check_repair_unused(self, source):
|
||||
"""helper to detect & clear invalid unused bits in last character.
|
||||
|
||||
:arg source:
|
||||
encoded data (as ascii bytes or unicode).
|
||||
|
||||
:returns:
|
||||
`(True, result)` if the string was repaired,
|
||||
`(False, source)` if the string was ok as-is.
|
||||
"""
|
||||
# figure out how many padding bits there are in last char.
|
||||
tail = len(source) & 3
|
||||
if tail == 2:
|
||||
mask, padset = self._padinfo2
|
||||
elif tail == 3:
|
||||
mask, padset = self._padinfo3
|
||||
elif not tail:
|
||||
return False, source
|
||||
else:
|
||||
raise ValueError("source length must != 1 mod 4")
|
||||
|
||||
# check if last char is ok (padset contains bytes & unicode versions)
|
||||
last = source[-1]
|
||||
if last in padset:
|
||||
return False, source
|
||||
|
||||
# we have dirty bits - repair the string by decoding last char,
|
||||
# clearing the padding bits via <mask>, and encoding new char.
|
||||
if isinstance(source, unicode):
|
||||
cm = self.charmap
|
||||
last = cm[cm.index(last) & mask]
|
||||
assert last in padset, "failed to generate valid padding char"
|
||||
else:
|
||||
# NOTE: this assumes ascii-compat encoding, and that
|
||||
# all chars used by encoding are 7-bit ascii.
|
||||
last = self._encode64(self._decode64(last) & mask)
|
||||
assert last in padset, "failed to generate valid padding char"
|
||||
if PY3:
|
||||
last = bytes([last])
|
||||
return True, source[:-1] + last
|
||||
|
||||
def repair_unused(self, source):
|
||||
return self.check_repair_unused(source)[1]
|
||||
|
||||
##def transcode(self, source, other):
|
||||
## return ''.join(
|
||||
## other.charmap[self.charmap.index(char)]
|
||||
## for char in source
|
||||
## )
|
||||
|
||||
##def random_encoded_bytes(self, size, random=None, unicode=False):
|
||||
## "return random encoded string of given size"
|
||||
## data = getrandstr(random or rng,
|
||||
## self.charmap if unicode else self.bytemap, size)
|
||||
## return self.repair_unused(data)
|
||||
|
||||
#===================================================================
|
||||
# transposed encoding/decoding
|
||||
#===================================================================
|
||||
def encode_transposed_bytes(self, source, offsets):
|
||||
"""encode byte string, first transposing source using offset list"""
|
||||
if not isinstance(source, bytes):
|
||||
raise TypeError("source must be bytes, not %s" % (type(source),))
|
||||
tmp = join_byte_elems(source[off] for off in offsets)
|
||||
return self.encode_bytes(tmp)
|
||||
|
||||
def decode_transposed_bytes(self, source, offsets):
|
||||
"""decode byte string, then reverse transposition described by offset list"""
|
||||
# NOTE: if transposition does not use all bytes of source,
|
||||
# the original can't be recovered... and join_byte_elems() will throw
|
||||
# an error because 1+ values in <buf> will be None.
|
||||
tmp = self.decode_bytes(source)
|
||||
buf = [None] * len(offsets)
|
||||
for off, char in zip(offsets, tmp):
|
||||
buf[off] = char
|
||||
return join_byte_elems(buf)
|
||||
|
||||
#===================================================================
|
||||
# integer decoding helpers - mainly used by des_crypt family
|
||||
#===================================================================
|
||||
def _decode_int(self, source, bits):
|
||||
"""decode base64 string -> integer
|
||||
|
||||
:arg source: base64 string to decode.
|
||||
:arg bits: number of bits in resulting integer.
|
||||
|
||||
:raises ValueError:
|
||||
* if the string contains invalid base64 characters.
|
||||
* if the string is not long enough - it must be at least
|
||||
``int(ceil(bits/6))`` in length.
|
||||
|
||||
:returns:
|
||||
a integer in the range ``0 <= n < 2**bits``
|
||||
"""
|
||||
if not isinstance(source, bytes):
|
||||
raise TypeError("source must be bytes, not %s" % (type(source),))
|
||||
big = self.big
|
||||
pad = -bits % 6
|
||||
chars = (bits+pad)/6
|
||||
if len(source) != chars:
|
||||
raise ValueError("source must be %d chars" % (chars,))
|
||||
decode = self._decode64
|
||||
out = 0
|
||||
try:
|
||||
for c in source if big else reversed(source):
|
||||
out = (out<<6) + decode(c)
|
||||
except KeyError:
|
||||
raise ValueError("invalid character in string: %r" % (c,))
|
||||
if pad:
|
||||
# strip padding bits
|
||||
if big:
|
||||
out >>= pad
|
||||
else:
|
||||
out &= (1<<bits)-1
|
||||
return out
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# optimized versions for common integer sizes
|
||||
#---------------------------------------------------------------
|
||||
|
||||
def decode_int6(self, source):
|
||||
"""decode single character -> 6 bit integer"""
|
||||
if not isinstance(source, bytes):
|
||||
raise TypeError("source must be bytes, not %s" % (type(source),))
|
||||
if len(source) != 1:
|
||||
raise ValueError("source must be exactly 1 byte")
|
||||
if PY3:
|
||||
# convert to 8bit int before doing lookup
|
||||
source = source[0]
|
||||
try:
|
||||
return self._decode64(source)
|
||||
except KeyError:
|
||||
raise ValueError("invalid character")
|
||||
|
||||
def decode_int12(self, source):
|
||||
"""decodes 2 char string -> 12-bit integer"""
|
||||
if not isinstance(source, bytes):
|
||||
raise TypeError("source must be bytes, not %s" % (type(source),))
|
||||
if len(source) != 2:
|
||||
raise ValueError("source must be exactly 2 bytes")
|
||||
decode = self._decode64
|
||||
try:
|
||||
if self.big:
|
||||
return decode(source[1]) + (decode(source[0])<<6)
|
||||
else:
|
||||
return decode(source[0]) + (decode(source[1])<<6)
|
||||
except KeyError:
|
||||
raise ValueError("invalid character")
|
||||
|
||||
def decode_int24(self, source):
|
||||
"""decodes 4 char string -> 24-bit integer"""
|
||||
if not isinstance(source, bytes):
|
||||
raise TypeError("source must be bytes, not %s" % (type(source),))
|
||||
if len(source) != 4:
|
||||
raise ValueError("source must be exactly 4 bytes")
|
||||
decode = self._decode64
|
||||
try:
|
||||
if self.big:
|
||||
return decode(source[3]) + (decode(source[2])<<6)+ \
|
||||
(decode(source[1])<<12) + (decode(source[0])<<18)
|
||||
else:
|
||||
return decode(source[0]) + (decode(source[1])<<6)+ \
|
||||
(decode(source[2])<<12) + (decode(source[3])<<18)
|
||||
except KeyError:
|
||||
raise ValueError("invalid character")
|
||||
|
||||
def decode_int30(self, source):
|
||||
"""decode 5 char string -> 30 bit integer"""
|
||||
return self._decode_int(source, 30)
|
||||
|
||||
def decode_int64(self, source):
|
||||
"""decode 11 char base64 string -> 64-bit integer
|
||||
|
||||
this format is used primarily by des-crypt & variants to encode
|
||||
the DES output value used as a checksum.
|
||||
"""
|
||||
return self._decode_int(source, 64)
|
||||
|
||||
#===================================================================
|
||||
# integer encoding helpers - mainly used by des_crypt family
|
||||
#===================================================================
|
||||
def _encode_int(self, value, bits):
|
||||
"""encode integer into base64 format
|
||||
|
||||
:arg value: non-negative integer to encode
|
||||
:arg bits: number of bits to encode
|
||||
|
||||
:returns:
|
||||
a string of length ``int(ceil(bits/6.0))``.
|
||||
"""
|
||||
assert value >= 0, "caller did not sanitize input"
|
||||
pad = -bits % 6
|
||||
bits += pad
|
||||
if self.big:
|
||||
itr = irange(bits-6, -6, -6)
|
||||
# shift to add lsb padding.
|
||||
value <<= pad
|
||||
else:
|
||||
itr = irange(0, bits, 6)
|
||||
# padding is msb, so no change needed.
|
||||
return join_byte_elems(imap(self._encode64,
|
||||
((value>>off) & 0x3f for off in itr)))
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# optimized versions for common integer sizes
|
||||
#---------------------------------------------------------------
|
||||
|
||||
def encode_int6(self, value):
|
||||
"""encodes 6-bit integer -> single hash64 character"""
|
||||
if value < 0 or value > 63:
|
||||
raise ValueError("value out of range")
|
||||
if PY3:
|
||||
return self.bytemap[value:value+1]
|
||||
else:
|
||||
return self._encode64(value)
|
||||
|
||||
def encode_int12(self, value):
|
||||
"""encodes 12-bit integer -> 2 char string"""
|
||||
if value < 0 or value > 0xFFF:
|
||||
raise ValueError("value out of range")
|
||||
raw = [value & 0x3f, (value>>6) & 0x3f]
|
||||
if self.big:
|
||||
raw = reversed(raw)
|
||||
return join_byte_elems(imap(self._encode64, raw))
|
||||
|
||||
def encode_int24(self, value):
|
||||
"""encodes 24-bit integer -> 4 char string"""
|
||||
if value < 0 or value > 0xFFFFFF:
|
||||
raise ValueError("value out of range")
|
||||
raw = [value & 0x3f, (value>>6) & 0x3f,
|
||||
(value>>12) & 0x3f, (value>>18) & 0x3f]
|
||||
if self.big:
|
||||
raw = reversed(raw)
|
||||
return join_byte_elems(imap(self._encode64, raw))
|
||||
|
||||
def encode_int30(self, value):
|
||||
"""decode 5 char string -> 30 bit integer"""
|
||||
if value < 0 or value > 0x3fffffff:
|
||||
raise ValueError("value out of range")
|
||||
return self._encode_int(value, 30)
|
||||
|
||||
def encode_int64(self, value):
|
||||
"""encode 64-bit integer -> 11 char hash64 string
|
||||
|
||||
this format is used primarily by des-crypt & variants to encode
|
||||
the DES output value used as a checksum.
|
||||
"""
|
||||
if value < 0 or value > 0xffffffffffffffff:
|
||||
raise ValueError("value out of range")
|
||||
return self._encode_int(value, 64)
|
||||
|
||||
#===================================================================
|
||||
# eof
|
||||
#===================================================================
|
||||
|
||||
class LazyBase64Engine(Base64Engine):
|
||||
"""Base64Engine which delays initialization until it's accessed"""
|
||||
_lazy_opts = None
|
||||
|
||||
def __init__(self, *args, **kwds):
|
||||
self._lazy_opts = (args, kwds)
|
||||
|
||||
def _lazy_init(self):
|
||||
args, kwds = self._lazy_opts
|
||||
super(LazyBase64Engine, self).__init__(*args, **kwds)
|
||||
del self._lazy_opts
|
||||
self.__class__ = Base64Engine
|
||||
|
||||
def __getattribute__(self, attr):
|
||||
if not attr.startswith("_"):
|
||||
self._lazy_init()
|
||||
return object.__getattribute__(self, attr)
|
||||
|
||||
#-------------------------------------------------------------
|
||||
# common variants
|
||||
#-------------------------------------------------------------
|
||||
|
||||
h64 = LazyBase64Engine(HASH64_CHARS)
|
||||
h64big = LazyBase64Engine(HASH64_CHARS, big=True)
|
||||
bcrypt64 = LazyBase64Engine(BCRYPT_CHARS, big=True)
|
||||
|
||||
#=============================================================================
|
||||
# eof
|
||||
#=============================================================================
|
474
venv/Lib/site-packages/passlib/utils/compat/__init__.py
Normal file
474
venv/Lib/site-packages/passlib/utils/compat/__init__.py
Normal file
@@ -0,0 +1,474 @@
|
||||
"""passlib.utils.compat - python 2/3 compatibility helpers"""
|
||||
#=============================================================================
|
||||
# figure out what we're running
|
||||
#=============================================================================
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
# python version
|
||||
#------------------------------------------------------------------------
|
||||
import sys
|
||||
PY2 = sys.version_info < (3,0)
|
||||
PY3 = sys.version_info >= (3,0)
|
||||
|
||||
# make sure it's not an unsupported version, even if we somehow got this far
|
||||
if sys.version_info < (2,6) or (3,0) <= sys.version_info < (3,2):
|
||||
raise RuntimeError("Passlib requires Python 2.6, 2.7, or >= 3.2 (as of passlib 1.7)")
|
||||
|
||||
PY26 = sys.version_info < (2,7)
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
# python implementation
|
||||
#------------------------------------------------------------------------
|
||||
JYTHON = sys.platform.startswith('java')
|
||||
|
||||
PYPY = hasattr(sys, "pypy_version_info")
|
||||
|
||||
if PYPY and sys.pypy_version_info < (2,0):
|
||||
raise RuntimeError("passlib requires pypy >= 2.0 (as of passlib 1.7)")
|
||||
|
||||
# e.g. '2.7.7\n[Pyston 0.5.1]'
|
||||
# NOTE: deprecated support 2019-11
|
||||
PYSTON = "Pyston" in sys.version
|
||||
|
||||
#=============================================================================
|
||||
# common imports
|
||||
#=============================================================================
|
||||
import logging; log = logging.getLogger(__name__)
|
||||
if PY3:
|
||||
import builtins
|
||||
else:
|
||||
import __builtin__ as builtins
|
||||
|
||||
def add_doc(obj, doc):
|
||||
"""add docstring to an object"""
|
||||
obj.__doc__ = doc
|
||||
|
||||
#=============================================================================
|
||||
# the default exported vars
|
||||
#=============================================================================
|
||||
__all__ = [
|
||||
# python versions
|
||||
'PY2', 'PY3', 'PY26',
|
||||
|
||||
# io
|
||||
'BytesIO', 'StringIO', 'NativeStringIO', 'SafeConfigParser',
|
||||
'print_',
|
||||
|
||||
# type detection
|
||||
## 'is_mapping',
|
||||
'int_types',
|
||||
'num_types',
|
||||
'unicode_or_bytes_types',
|
||||
'native_string_types',
|
||||
|
||||
# unicode/bytes types & helpers
|
||||
'u',
|
||||
'unicode',
|
||||
'uascii_to_str', 'bascii_to_str',
|
||||
'str_to_uascii', 'str_to_bascii',
|
||||
'join_unicode', 'join_bytes',
|
||||
'join_byte_values', 'join_byte_elems',
|
||||
'byte_elem_value',
|
||||
'iter_byte_values',
|
||||
|
||||
# iteration helpers
|
||||
'irange', #'lrange',
|
||||
'imap', 'lmap',
|
||||
'iteritems', 'itervalues',
|
||||
'next',
|
||||
|
||||
# collections
|
||||
'OrderedDict',
|
||||
|
||||
# context helpers
|
||||
'nullcontext',
|
||||
|
||||
# introspection
|
||||
'get_method_function', 'add_doc',
|
||||
]
|
||||
|
||||
# begin accumulating mapping of lazy-loaded attrs,
|
||||
# 'merged' into module at bottom
|
||||
_lazy_attrs = dict()
|
||||
|
||||
#=============================================================================
|
||||
# unicode & bytes types
|
||||
#=============================================================================
|
||||
if PY3:
|
||||
unicode = str
|
||||
|
||||
# TODO: once we drop python 3.2 support, can use u'' again!
|
||||
def u(s):
|
||||
assert isinstance(s, str)
|
||||
return s
|
||||
|
||||
unicode_or_bytes_types = (str, bytes)
|
||||
native_string_types = (unicode,)
|
||||
|
||||
else:
|
||||
unicode = builtins.unicode
|
||||
|
||||
def u(s):
|
||||
assert isinstance(s, str)
|
||||
return s.decode("unicode_escape")
|
||||
|
||||
unicode_or_bytes_types = (basestring,)
|
||||
native_string_types = (basestring,)
|
||||
|
||||
# shorter preferred aliases
|
||||
unicode_or_bytes = unicode_or_bytes_types
|
||||
unicode_or_str = native_string_types
|
||||
|
||||
# unicode -- unicode type, regardless of python version
|
||||
# bytes -- bytes type, regardless of python version
|
||||
# unicode_or_bytes_types -- types that text can occur in, whether encoded or not
|
||||
# native_string_types -- types that native python strings (dict keys etc) can occur in.
|
||||
|
||||
#=============================================================================
|
||||
# unicode & bytes helpers
|
||||
#=============================================================================
|
||||
# function to join list of unicode strings
|
||||
join_unicode = u('').join
|
||||
|
||||
# function to join list of byte strings
|
||||
join_bytes = b''.join
|
||||
|
||||
if PY3:
|
||||
def uascii_to_str(s):
|
||||
assert isinstance(s, unicode)
|
||||
return s
|
||||
|
||||
def bascii_to_str(s):
|
||||
assert isinstance(s, bytes)
|
||||
return s.decode("ascii")
|
||||
|
||||
def str_to_uascii(s):
|
||||
assert isinstance(s, str)
|
||||
return s
|
||||
|
||||
def str_to_bascii(s):
|
||||
assert isinstance(s, str)
|
||||
return s.encode("ascii")
|
||||
|
||||
join_byte_values = join_byte_elems = bytes
|
||||
|
||||
def byte_elem_value(elem):
|
||||
assert isinstance(elem, int)
|
||||
return elem
|
||||
|
||||
def iter_byte_values(s):
|
||||
assert isinstance(s, bytes)
|
||||
return s
|
||||
|
||||
def iter_byte_chars(s):
|
||||
assert isinstance(s, bytes)
|
||||
# FIXME: there has to be a better way to do this
|
||||
return (bytes([c]) for c in s)
|
||||
|
||||
else:
|
||||
def uascii_to_str(s):
|
||||
assert isinstance(s, unicode)
|
||||
return s.encode("ascii")
|
||||
|
||||
def bascii_to_str(s):
|
||||
assert isinstance(s, bytes)
|
||||
return s
|
||||
|
||||
def str_to_uascii(s):
|
||||
assert isinstance(s, str)
|
||||
return s.decode("ascii")
|
||||
|
||||
def str_to_bascii(s):
|
||||
assert isinstance(s, str)
|
||||
return s
|
||||
|
||||
def join_byte_values(values):
|
||||
return join_bytes(chr(v) for v in values)
|
||||
|
||||
join_byte_elems = join_bytes
|
||||
|
||||
byte_elem_value = ord
|
||||
|
||||
def iter_byte_values(s):
|
||||
assert isinstance(s, bytes)
|
||||
return (ord(c) for c in s)
|
||||
|
||||
def iter_byte_chars(s):
|
||||
assert isinstance(s, bytes)
|
||||
return s
|
||||
|
||||
add_doc(uascii_to_str, "helper to convert ascii unicode -> native str")
|
||||
add_doc(bascii_to_str, "helper to convert ascii bytes -> native str")
|
||||
add_doc(str_to_uascii, "helper to convert ascii native str -> unicode")
|
||||
add_doc(str_to_bascii, "helper to convert ascii native str -> bytes")
|
||||
|
||||
# join_byte_values -- function to convert list of ordinal integers to byte string.
|
||||
|
||||
# join_byte_elems -- function to convert list of byte elements to byte string;
|
||||
# i.e. what's returned by ``b('a')[0]``...
|
||||
# this is b('a') under PY2, but 97 under PY3.
|
||||
|
||||
# byte_elem_value -- function to convert byte element to integer -- a noop under PY3
|
||||
|
||||
add_doc(iter_byte_values, "iterate over byte string as sequence of ints 0-255")
|
||||
add_doc(iter_byte_chars, "iterate over byte string as sequence of 1-byte strings")
|
||||
|
||||
#=============================================================================
|
||||
# numeric
|
||||
#=============================================================================
|
||||
if PY3:
|
||||
int_types = (int,)
|
||||
num_types = (int, float)
|
||||
else:
|
||||
int_types = (int, long)
|
||||
num_types = (int, long, float)
|
||||
|
||||
#=============================================================================
|
||||
# iteration helpers
|
||||
#
|
||||
# irange - range iterable / view (xrange under py2, range under py3)
|
||||
# lrange - range list (range under py2, list(range()) under py3)
|
||||
#
|
||||
# imap - map to iterator
|
||||
# lmap - map to list
|
||||
#=============================================================================
|
||||
if PY3:
|
||||
irange = range
|
||||
##def lrange(*a,**k):
|
||||
## return list(range(*a,**k))
|
||||
|
||||
def lmap(*a, **k):
|
||||
return list(map(*a,**k))
|
||||
imap = map
|
||||
|
||||
def iteritems(d):
|
||||
return d.items()
|
||||
def itervalues(d):
|
||||
return d.values()
|
||||
|
||||
def nextgetter(obj):
|
||||
return obj.__next__
|
||||
|
||||
izip = zip
|
||||
|
||||
else:
|
||||
irange = xrange
|
||||
##lrange = range
|
||||
|
||||
lmap = map
|
||||
from itertools import imap, izip
|
||||
|
||||
def iteritems(d):
|
||||
return d.iteritems()
|
||||
def itervalues(d):
|
||||
return d.itervalues()
|
||||
|
||||
def nextgetter(obj):
|
||||
return obj.next
|
||||
|
||||
add_doc(nextgetter, "return function that yields successive values from iterable")
|
||||
|
||||
#=============================================================================
|
||||
# typing
|
||||
#=============================================================================
|
||||
##def is_mapping(obj):
|
||||
## # non-exhaustive check, enough to distinguish from lists, etc
|
||||
## return hasattr(obj, "items")
|
||||
|
||||
#=============================================================================
|
||||
# introspection
|
||||
#=============================================================================
|
||||
if PY3:
|
||||
method_function_attr = "__func__"
|
||||
else:
|
||||
method_function_attr = "im_func"
|
||||
|
||||
def get_method_function(func):
|
||||
"""given (potential) method, return underlying function"""
|
||||
return getattr(func, method_function_attr, func)
|
||||
|
||||
def get_unbound_method_function(func):
|
||||
"""given unbound method, return underlying function"""
|
||||
return func if PY3 else func.__func__
|
||||
|
||||
def error_from(exc, # *,
|
||||
cause=None):
|
||||
"""
|
||||
backward compat hack to suppress exception cause in python3.3+
|
||||
|
||||
one python < 3.3 support is dropped, can replace all uses with "raise exc from None"
|
||||
"""
|
||||
exc.__cause__ = cause
|
||||
exc.__suppress_context__ = True
|
||||
return exc
|
||||
|
||||
# legacy alias
|
||||
suppress_cause = error_from
|
||||
|
||||
#=============================================================================
|
||||
# input/output
|
||||
#=============================================================================
|
||||
if PY3:
|
||||
_lazy_attrs = dict(
|
||||
BytesIO="io.BytesIO",
|
||||
UnicodeIO="io.StringIO",
|
||||
NativeStringIO="io.StringIO",
|
||||
SafeConfigParser="configparser.ConfigParser",
|
||||
)
|
||||
|
||||
print_ = getattr(builtins, "print")
|
||||
|
||||
else:
|
||||
_lazy_attrs = dict(
|
||||
BytesIO="cStringIO.StringIO",
|
||||
UnicodeIO="StringIO.StringIO",
|
||||
NativeStringIO="cStringIO.StringIO",
|
||||
SafeConfigParser="ConfigParser.SafeConfigParser",
|
||||
)
|
||||
|
||||
def print_(*args, **kwds):
|
||||
"""The new-style print function."""
|
||||
# extract kwd args
|
||||
fp = kwds.pop("file", sys.stdout)
|
||||
sep = kwds.pop("sep", None)
|
||||
end = kwds.pop("end", None)
|
||||
if kwds:
|
||||
raise TypeError("invalid keyword arguments")
|
||||
|
||||
# short-circuit if no target
|
||||
if fp is None:
|
||||
return
|
||||
|
||||
# use unicode or bytes ?
|
||||
want_unicode = isinstance(sep, unicode) or isinstance(end, unicode) or \
|
||||
any(isinstance(arg, unicode) for arg in args)
|
||||
|
||||
# pick default end sequence
|
||||
if end is None:
|
||||
end = u("\n") if want_unicode else "\n"
|
||||
elif not isinstance(end, unicode_or_bytes_types):
|
||||
raise TypeError("end must be None or a string")
|
||||
|
||||
# pick default separator
|
||||
if sep is None:
|
||||
sep = u(" ") if want_unicode else " "
|
||||
elif not isinstance(sep, unicode_or_bytes_types):
|
||||
raise TypeError("sep must be None or a string")
|
||||
|
||||
# write to buffer
|
||||
first = True
|
||||
write = fp.write
|
||||
for arg in args:
|
||||
if first:
|
||||
first = False
|
||||
else:
|
||||
write(sep)
|
||||
if not isinstance(arg, basestring):
|
||||
arg = str(arg)
|
||||
write(arg)
|
||||
write(end)
|
||||
|
||||
#=============================================================================
|
||||
# collections
|
||||
#=============================================================================
|
||||
if PY26:
|
||||
_lazy_attrs['OrderedDict'] = 'passlib.utils.compat._ordered_dict.OrderedDict'
|
||||
else:
|
||||
_lazy_attrs['OrderedDict'] = 'collections.OrderedDict'
|
||||
|
||||
#=============================================================================
|
||||
# context managers
|
||||
#=============================================================================
|
||||
|
||||
try:
|
||||
# new in py37
|
||||
from contextlib import nullcontext
|
||||
except ImportError:
|
||||
|
||||
class nullcontext(object):
|
||||
"""
|
||||
Context manager that does no additional processing.
|
||||
"""
|
||||
def __init__(self, enter_result=None):
|
||||
self.enter_result = enter_result
|
||||
|
||||
def __enter__(self):
|
||||
return self.enter_result
|
||||
|
||||
def __exit__(self, *exc_info):
|
||||
pass
|
||||
|
||||
#=============================================================================
|
||||
# lazy overlay module
|
||||
#=============================================================================
|
||||
from types import ModuleType
|
||||
|
||||
def _import_object(source):
|
||||
"""helper to import object from module; accept format `path.to.object`"""
|
||||
modname, modattr = source.rsplit(".",1)
|
||||
mod = __import__(modname, fromlist=[modattr], level=0)
|
||||
return getattr(mod, modattr)
|
||||
|
||||
class _LazyOverlayModule(ModuleType):
|
||||
"""proxy module which overlays original module,
|
||||
and lazily imports specified attributes.
|
||||
|
||||
this is mainly used to prevent importing of resources
|
||||
that are only needed by certain password hashes,
|
||||
yet allow them to be imported from a single location.
|
||||
|
||||
used by :mod:`passlib.utils`, :mod:`passlib.crypto`,
|
||||
and :mod:`passlib.utils.compat`.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def replace_module(cls, name, attrmap):
|
||||
orig = sys.modules[name]
|
||||
self = cls(name, attrmap, orig)
|
||||
sys.modules[name] = self
|
||||
return self
|
||||
|
||||
def __init__(self, name, attrmap, proxy=None):
|
||||
ModuleType.__init__(self, name)
|
||||
self.__attrmap = attrmap
|
||||
self.__proxy = proxy
|
||||
self.__log = logging.getLogger(name)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
proxy = self.__proxy
|
||||
if proxy and hasattr(proxy, attr):
|
||||
return getattr(proxy, attr)
|
||||
attrmap = self.__attrmap
|
||||
if attr in attrmap:
|
||||
source = attrmap[attr]
|
||||
if callable(source):
|
||||
value = source()
|
||||
else:
|
||||
value = _import_object(source)
|
||||
setattr(self, attr, value)
|
||||
self.__log.debug("loaded lazy attr %r: %r", attr, value)
|
||||
return value
|
||||
raise AttributeError("'module' object has no attribute '%s'" % (attr,))
|
||||
|
||||
def __repr__(self):
|
||||
proxy = self.__proxy
|
||||
if proxy:
|
||||
return repr(proxy)
|
||||
else:
|
||||
return ModuleType.__repr__(self)
|
||||
|
||||
def __dir__(self):
|
||||
attrs = set(dir(self.__class__))
|
||||
attrs.update(self.__dict__)
|
||||
attrs.update(self.__attrmap)
|
||||
proxy = self.__proxy
|
||||
if proxy is not None:
|
||||
attrs.update(dir(proxy))
|
||||
return list(attrs)
|
||||
|
||||
# replace this module with overlay that will lazily import attributes.
|
||||
_LazyOverlayModule.replace_module(__name__, _lazy_attrs)
|
||||
|
||||
#=============================================================================
|
||||
# eof
|
||||
#=============================================================================
|
242
venv/Lib/site-packages/passlib/utils/compat/_ordered_dict.py
Normal file
242
venv/Lib/site-packages/passlib/utils/compat/_ordered_dict.py
Normal file
@@ -0,0 +1,242 @@
|
||||
"""passlib.utils.compat._ordered_dict -- backport of collections.OrderedDict for py26
|
||||
|
||||
taken from stdlib-suggested recipe at http://code.activestate.com/recipes/576693/
|
||||
|
||||
this should be imported from passlib.utils.compat.OrderedDict, not here.
|
||||
"""
|
||||
|
||||
try:
|
||||
from thread import get_ident as _get_ident
|
||||
except ImportError:
|
||||
from dummy_thread import get_ident as _get_ident
|
||||
|
||||
class OrderedDict(dict):
|
||||
"""Dictionary that remembers insertion order"""
|
||||
# An inherited dict maps keys to values.
|
||||
# The inherited dict provides __getitem__, __len__, __contains__, and get.
|
||||
# The remaining methods are order-aware.
|
||||
# Big-O running times for all methods are the same as for regular dictionaries.
|
||||
|
||||
# The internal self.__map dictionary maps keys to links in a doubly linked list.
|
||||
# The circular doubly linked list starts and ends with a sentinel element.
|
||||
# The sentinel element never gets deleted (this simplifies the algorithm).
|
||||
# Each link is stored as a list of length three: [PREV, NEXT, KEY].
|
||||
|
||||
def __init__(self, *args, **kwds):
|
||||
'''Initialize an ordered dictionary. Signature is the same as for
|
||||
regular dictionaries, but keyword arguments are not recommended
|
||||
because their insertion order is arbitrary.
|
||||
|
||||
'''
|
||||
if len(args) > 1:
|
||||
raise TypeError('expected at most 1 arguments, got %d' % len(args))
|
||||
try:
|
||||
self.__root
|
||||
except AttributeError:
|
||||
self.__root = root = [] # sentinel node
|
||||
root[:] = [root, root, None]
|
||||
self.__map = {}
|
||||
self.__update(*args, **kwds)
|
||||
|
||||
def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
|
||||
'od.__setitem__(i, y) <==> od[i]=y'
|
||||
# Setting a new item creates a new link which goes at the end of the linked
|
||||
# list, and the inherited dictionary is updated with the new key/value pair.
|
||||
if key not in self:
|
||||
root = self.__root
|
||||
last = root[0]
|
||||
last[1] = root[0] = self.__map[key] = [last, root, key]
|
||||
dict_setitem(self, key, value)
|
||||
|
||||
def __delitem__(self, key, dict_delitem=dict.__delitem__):
|
||||
'od.__delitem__(y) <==> del od[y]'
|
||||
# Deleting an existing item uses self.__map to find the link which is
|
||||
# then removed by updating the links in the predecessor and successor nodes.
|
||||
dict_delitem(self, key)
|
||||
link_prev, link_next, key = self.__map.pop(key)
|
||||
link_prev[1] = link_next
|
||||
link_next[0] = link_prev
|
||||
|
||||
def __iter__(self):
|
||||
'od.__iter__() <==> iter(od)'
|
||||
root = self.__root
|
||||
curr = root[1]
|
||||
while curr is not root:
|
||||
yield curr[2]
|
||||
curr = curr[1]
|
||||
|
||||
def __reversed__(self):
|
||||
'od.__reversed__() <==> reversed(od)'
|
||||
root = self.__root
|
||||
curr = root[0]
|
||||
while curr is not root:
|
||||
yield curr[2]
|
||||
curr = curr[0]
|
||||
|
||||
def clear(self):
|
||||
'od.clear() -> None. Remove all items from od.'
|
||||
try:
|
||||
for node in self.__map.itervalues():
|
||||
del node[:]
|
||||
root = self.__root
|
||||
root[:] = [root, root, None]
|
||||
self.__map.clear()
|
||||
except AttributeError:
|
||||
pass
|
||||
dict.clear(self)
|
||||
|
||||
def popitem(self, last=True):
|
||||
'''od.popitem() -> (k, v), return and remove a (key, value) pair.
|
||||
Pairs are returned in LIFO order if last is true or FIFO order if false.
|
||||
|
||||
'''
|
||||
if not self:
|
||||
raise KeyError('dictionary is empty')
|
||||
root = self.__root
|
||||
if last:
|
||||
link = root[0]
|
||||
link_prev = link[0]
|
||||
link_prev[1] = root
|
||||
root[0] = link_prev
|
||||
else:
|
||||
link = root[1]
|
||||
link_next = link[1]
|
||||
root[1] = link_next
|
||||
link_next[0] = root
|
||||
key = link[2]
|
||||
del self.__map[key]
|
||||
value = dict.pop(self, key)
|
||||
return key, value
|
||||
|
||||
# -- the following methods do not depend on the internal structure --
|
||||
|
||||
def keys(self):
|
||||
'od.keys() -> list of keys in od'
|
||||
return list(self)
|
||||
|
||||
def values(self):
|
||||
'od.values() -> list of values in od'
|
||||
return [self[key] for key in self]
|
||||
|
||||
def items(self):
|
||||
'od.items() -> list of (key, value) pairs in od'
|
||||
return [(key, self[key]) for key in self]
|
||||
|
||||
def iterkeys(self):
|
||||
'od.iterkeys() -> an iterator over the keys in od'
|
||||
return iter(self)
|
||||
|
||||
def itervalues(self):
|
||||
'od.itervalues -> an iterator over the values in od'
|
||||
for k in self:
|
||||
yield self[k]
|
||||
|
||||
def iteritems(self):
|
||||
'od.iteritems -> an iterator over the (key, value) items in od'
|
||||
for k in self:
|
||||
yield (k, self[k])
|
||||
|
||||
def update(*args, **kwds):
|
||||
'''od.update(E, **F) -> None. Update od from dict/iterable E and F.
|
||||
|
||||
If E is a dict instance, does: for k in E: od[k] = E[k]
|
||||
If E has a .keys() method, does: for k in E.keys(): od[k] = E[k]
|
||||
Or if E is an iterable of items, does: for k, v in E: od[k] = v
|
||||
In either case, this is followed by: for k, v in F.items(): od[k] = v
|
||||
|
||||
'''
|
||||
if len(args) > 2:
|
||||
raise TypeError('update() takes at most 2 positional '
|
||||
'arguments (%d given)' % (len(args),))
|
||||
elif not args:
|
||||
raise TypeError('update() takes at least 1 argument (0 given)')
|
||||
self = args[0]
|
||||
# Make progressively weaker assumptions about "other"
|
||||
other = ()
|
||||
if len(args) == 2:
|
||||
other = args[1]
|
||||
if isinstance(other, dict):
|
||||
for key in other:
|
||||
self[key] = other[key]
|
||||
elif hasattr(other, 'keys'):
|
||||
for key in other.keys():
|
||||
self[key] = other[key]
|
||||
else:
|
||||
for key, value in other:
|
||||
self[key] = value
|
||||
for key, value in kwds.items():
|
||||
self[key] = value
|
||||
|
||||
__update = update # let subclasses override update without breaking __init__
|
||||
|
||||
__marker = object()
|
||||
|
||||
def pop(self, key, default=__marker):
|
||||
'''od.pop(k[,d]) -> v, remove specified key and return the corresponding value.
|
||||
If key is not found, d is returned if given, otherwise KeyError is raised.
|
||||
|
||||
'''
|
||||
if key in self:
|
||||
result = self[key]
|
||||
del self[key]
|
||||
return result
|
||||
if default is self.__marker:
|
||||
raise KeyError(key)
|
||||
return default
|
||||
|
||||
def setdefault(self, key, default=None):
|
||||
'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'
|
||||
if key in self:
|
||||
return self[key]
|
||||
self[key] = default
|
||||
return default
|
||||
|
||||
def __repr__(self, _repr_running={}):
|
||||
'od.__repr__() <==> repr(od)'
|
||||
call_key = id(self), _get_ident()
|
||||
if call_key in _repr_running:
|
||||
return '...'
|
||||
_repr_running[call_key] = 1
|
||||
try:
|
||||
if not self:
|
||||
return '%s()' % (self.__class__.__name__,)
|
||||
return '%s(%r)' % (self.__class__.__name__, self.items())
|
||||
finally:
|
||||
del _repr_running[call_key]
|
||||
|
||||
def __reduce__(self):
|
||||
'Return state information for pickling'
|
||||
items = [[k, self[k]] for k in self]
|
||||
inst_dict = vars(self).copy()
|
||||
for k in vars(OrderedDict()):
|
||||
inst_dict.pop(k, None)
|
||||
if inst_dict:
|
||||
return (self.__class__, (items,), inst_dict)
|
||||
return self.__class__, (items,)
|
||||
|
||||
def copy(self):
|
||||
'od.copy() -> a shallow copy of od'
|
||||
return self.__class__(self)
|
||||
|
||||
@classmethod
|
||||
def fromkeys(cls, iterable, value=None):
|
||||
'''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S
|
||||
and values equal to v (which defaults to None).
|
||||
|
||||
'''
|
||||
d = cls()
|
||||
for key in iterable:
|
||||
d[key] = value
|
||||
return d
|
||||
|
||||
def __eq__(self, other):
|
||||
'''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive
|
||||
while comparison to a regular mapping is order-insensitive.
|
||||
|
||||
'''
|
||||
if isinstance(other, OrderedDict):
|
||||
return len(self)==len(other) and self.items() == other.items()
|
||||
return dict.__eq__(self, other)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
233
venv/Lib/site-packages/passlib/utils/decor.py
Normal file
233
venv/Lib/site-packages/passlib/utils/decor.py
Normal file
@@ -0,0 +1,233 @@
|
||||
"""
|
||||
passlib.utils.decor -- helper decorators & properties
|
||||
"""
|
||||
#=============================================================================
|
||||
# imports
|
||||
#=============================================================================
|
||||
# core
|
||||
from __future__ import absolute_import, division, print_function
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
from functools import wraps, update_wrapper
|
||||
import types
|
||||
from warnings import warn
|
||||
# site
|
||||
# pkg
|
||||
from passlib.utils.compat import PY3
|
||||
# local
|
||||
__all__ = [
|
||||
"classproperty",
|
||||
"hybrid_method",
|
||||
|
||||
"memoize_single_value",
|
||||
"memoized_property",
|
||||
|
||||
"deprecated_function",
|
||||
"deprecated_method",
|
||||
]
|
||||
|
||||
#=============================================================================
|
||||
# class-level decorators
|
||||
#=============================================================================
|
||||
class classproperty(object):
|
||||
"""Function decorator which acts like a combination of classmethod+property (limited to read-only properties)"""
|
||||
|
||||
def __init__(self, func):
|
||||
self.im_func = func
|
||||
|
||||
def __get__(self, obj, cls):
|
||||
return self.im_func(cls)
|
||||
|
||||
@property
|
||||
def __func__(self):
|
||||
"""py3 compatible alias"""
|
||||
return self.im_func
|
||||
|
||||
class hybrid_method(object):
|
||||
"""
|
||||
decorator which invokes function with class if called as class method,
|
||||
and with object if called at instance level.
|
||||
"""
|
||||
|
||||
def __init__(self, func):
|
||||
self.func = func
|
||||
update_wrapper(self, func)
|
||||
|
||||
def __get__(self, obj, cls):
|
||||
if obj is None:
|
||||
obj = cls
|
||||
if PY3:
|
||||
return types.MethodType(self.func, obj)
|
||||
else:
|
||||
return types.MethodType(self.func, obj, cls)
|
||||
|
||||
#=============================================================================
|
||||
# memoization
|
||||
#=============================================================================
|
||||
|
||||
def memoize_single_value(func):
|
||||
"""
|
||||
decorator for function which takes no args,
|
||||
and memoizes result. exposes a ``.clear_cache`` method
|
||||
to clear the cached value.
|
||||
"""
|
||||
cache = {}
|
||||
|
||||
@wraps(func)
|
||||
def wrapper():
|
||||
try:
|
||||
return cache[True]
|
||||
except KeyError:
|
||||
pass
|
||||
value = cache[True] = func()
|
||||
return value
|
||||
|
||||
def clear_cache():
|
||||
cache.pop(True, None)
|
||||
wrapper.clear_cache = clear_cache
|
||||
|
||||
return wrapper
|
||||
|
||||
class memoized_property(object):
|
||||
"""
|
||||
decorator which invokes method once, then replaces attr with result
|
||||
"""
|
||||
def __init__(self, func):
|
||||
self.__func__ = func
|
||||
self.__name__ = func.__name__
|
||||
self.__doc__ = func.__doc__
|
||||
|
||||
def __get__(self, obj, cls):
|
||||
if obj is None:
|
||||
return self
|
||||
value = self.__func__(obj)
|
||||
setattr(obj, self.__name__, value)
|
||||
return value
|
||||
|
||||
if not PY3:
|
||||
|
||||
@property
|
||||
def im_func(self):
|
||||
"""py2 alias"""
|
||||
return self.__func__
|
||||
|
||||
def clear_cache(self, obj):
|
||||
"""
|
||||
class-level helper to clear stored value (if any).
|
||||
|
||||
usage: :samp:`type(self).{attr}.clear_cache(self)`
|
||||
"""
|
||||
obj.__dict__.pop(self.__name__, None)
|
||||
|
||||
def peek_cache(self, obj, default=None):
|
||||
"""
|
||||
class-level helper to peek at stored value
|
||||
|
||||
usage: :samp:`value = type(self).{attr}.clear_cache(self)`
|
||||
"""
|
||||
return obj.__dict__.get(self.__name__, default)
|
||||
|
||||
# works but not used
|
||||
##class memoized_class_property(object):
|
||||
## """function decorator which calls function as classmethod,
|
||||
## and replaces itself with result for current and all future invocations.
|
||||
## """
|
||||
## def __init__(self, func):
|
||||
## self.im_func = func
|
||||
##
|
||||
## def __get__(self, obj, cls):
|
||||
## func = self.im_func
|
||||
## value = func(cls)
|
||||
## setattr(cls, func.__name__, value)
|
||||
## return value
|
||||
##
|
||||
## @property
|
||||
## def __func__(self):
|
||||
## "py3 compatible alias"
|
||||
|
||||
#=============================================================================
|
||||
# deprecation
|
||||
#=============================================================================
|
||||
def deprecated_function(msg=None, deprecated=None, removed=None, updoc=True,
|
||||
replacement=None, _is_method=False,
|
||||
func_module=None):
|
||||
"""decorator to deprecate a function.
|
||||
|
||||
:arg msg: optional msg, default chosen if omitted
|
||||
:kwd deprecated: version when function was first deprecated
|
||||
:kwd removed: version when function will be removed
|
||||
:kwd replacement: alternate name / instructions for replacing this function.
|
||||
:kwd updoc: add notice to docstring (default ``True``)
|
||||
"""
|
||||
if msg is None:
|
||||
if _is_method:
|
||||
msg = "the method %(mod)s.%(klass)s.%(name)s() is deprecated"
|
||||
else:
|
||||
msg = "the function %(mod)s.%(name)s() is deprecated"
|
||||
if deprecated:
|
||||
msg += " as of Passlib %(deprecated)s"
|
||||
if removed:
|
||||
msg += ", and will be removed in Passlib %(removed)s"
|
||||
if replacement:
|
||||
msg += ", use %s instead" % replacement
|
||||
msg += "."
|
||||
def build(func):
|
||||
is_classmethod = _is_method and isinstance(func, classmethod)
|
||||
if is_classmethod:
|
||||
# NOTE: PY26 doesn't support "classmethod().__func__" directly...
|
||||
func = func.__get__(None, type).__func__
|
||||
opts = dict(
|
||||
mod=func_module or func.__module__,
|
||||
name=func.__name__,
|
||||
deprecated=deprecated,
|
||||
removed=removed,
|
||||
)
|
||||
if _is_method:
|
||||
def wrapper(*args, **kwds):
|
||||
tmp = opts.copy()
|
||||
klass = args[0] if is_classmethod else args[0].__class__
|
||||
tmp.update(klass=klass.__name__, mod=klass.__module__)
|
||||
warn(msg % tmp, DeprecationWarning, stacklevel=2)
|
||||
return func(*args, **kwds)
|
||||
else:
|
||||
text = msg % opts
|
||||
def wrapper(*args, **kwds):
|
||||
warn(text, DeprecationWarning, stacklevel=2)
|
||||
return func(*args, **kwds)
|
||||
update_wrapper(wrapper, func)
|
||||
if updoc and (deprecated or removed) and \
|
||||
wrapper.__doc__ and ".. deprecated::" not in wrapper.__doc__:
|
||||
txt = deprecated or ''
|
||||
if removed or replacement:
|
||||
txt += "\n "
|
||||
if removed:
|
||||
txt += "and will be removed in version %s" % (removed,)
|
||||
if replacement:
|
||||
if removed:
|
||||
txt += ", "
|
||||
txt += "use %s instead" % replacement
|
||||
txt += "."
|
||||
if not wrapper.__doc__.strip(" ").endswith("\n"):
|
||||
wrapper.__doc__ += "\n"
|
||||
wrapper.__doc__ += "\n.. deprecated:: %s\n" % (txt,)
|
||||
if is_classmethod:
|
||||
wrapper = classmethod(wrapper)
|
||||
return wrapper
|
||||
return build
|
||||
|
||||
def deprecated_method(msg=None, deprecated=None, removed=None, updoc=True,
|
||||
replacement=None):
|
||||
"""decorator to deprecate a method.
|
||||
|
||||
:arg msg: optional msg, default chosen if omitted
|
||||
:kwd deprecated: version when method was first deprecated
|
||||
:kwd removed: version when method will be removed
|
||||
:kwd replacement: alternate name / instructions for replacing this method.
|
||||
:kwd updoc: add notice to docstring (default ``True``)
|
||||
"""
|
||||
return deprecated_function(msg, deprecated, removed, updoc, replacement,
|
||||
_is_method=True)
|
||||
|
||||
#=============================================================================
|
||||
# eof
|
||||
#=============================================================================
|
46
venv/Lib/site-packages/passlib/utils/des.py
Normal file
46
venv/Lib/site-packages/passlib/utils/des.py
Normal file
@@ -0,0 +1,46 @@
|
||||
"""
|
||||
passlib.utils.des - DEPRECATED LOCATION, WILL BE REMOVED IN 2.0
|
||||
|
||||
This has been moved to :mod:`passlib.crypto.des`.
|
||||
"""
|
||||
#=============================================================================
|
||||
# import from new location
|
||||
#=============================================================================
|
||||
from warnings import warn
|
||||
warn("the 'passlib.utils.des' module has been relocated to 'passlib.crypto.des' "
|
||||
"as of passlib 1.7, and the old location will be removed in passlib 2.0",
|
||||
DeprecationWarning)
|
||||
|
||||
#=============================================================================
|
||||
# relocated functions
|
||||
#=============================================================================
|
||||
from passlib.utils.decor import deprecated_function
|
||||
from passlib.crypto.des import expand_des_key, des_encrypt_block, des_encrypt_int_block
|
||||
|
||||
expand_des_key = deprecated_function(deprecated="1.7", removed="1.8",
|
||||
replacement="passlib.crypto.des.expand_des_key")(expand_des_key)
|
||||
|
||||
des_encrypt_block = deprecated_function(deprecated="1.7", removed="1.8",
|
||||
replacement="passlib.crypto.des.des_encrypt_block")(des_encrypt_block)
|
||||
|
||||
des_encrypt_int_block = deprecated_function(deprecated="1.7", removed="1.8",
|
||||
replacement="passlib.crypto.des.des_encrypt_int_block")(des_encrypt_int_block)
|
||||
|
||||
#=============================================================================
|
||||
# deprecated functions -- not carried over to passlib.crypto.des
|
||||
#=============================================================================
|
||||
import struct
|
||||
_unpack_uint64 = struct.Struct(">Q").unpack
|
||||
|
||||
@deprecated_function(deprecated="1.6", removed="1.8",
|
||||
replacement="passlib.crypto.des.des_encrypt_int_block()")
|
||||
def mdes_encrypt_int_block(key, input, salt=0, rounds=1): # pragma: no cover -- deprecated & unused
|
||||
if isinstance(key, bytes):
|
||||
if len(key) == 7:
|
||||
key = expand_des_key(key)
|
||||
key = _unpack_uint64(key)[0]
|
||||
return des_encrypt_int_block(key, input, salt, rounds)
|
||||
|
||||
#=============================================================================
|
||||
# eof
|
||||
#=============================================================================
|
2711
venv/Lib/site-packages/passlib/utils/handlers.py
Normal file
2711
venv/Lib/site-packages/passlib/utils/handlers.py
Normal file
File diff suppressed because it is too large
Load Diff
29
venv/Lib/site-packages/passlib/utils/md4.py
Normal file
29
venv/Lib/site-packages/passlib/utils/md4.py
Normal file
@@ -0,0 +1,29 @@
|
||||
"""
|
||||
passlib.utils.md4 - DEPRECATED MODULE, WILL BE REMOVED IN 2.0
|
||||
|
||||
MD4 should now be looked up through ``passlib.crypto.digest.lookup_hash("md4").const``,
|
||||
which provides unified handling stdlib implementation (if present).
|
||||
"""
|
||||
#=============================================================================
|
||||
# issue deprecation warning for module
|
||||
#=============================================================================
|
||||
from warnings import warn
|
||||
warn("the module 'passlib.utils.md4' is deprecated as of Passlib 1.7, "
|
||||
"and will be removed in Passlib 2.0, please use "
|
||||
"'lookup_hash(\"md4\").const()' from 'passlib.crypto' instead",
|
||||
DeprecationWarning)
|
||||
|
||||
#=============================================================================
|
||||
# backwards compat exports
|
||||
#=============================================================================
|
||||
__all__ = ["md4"]
|
||||
|
||||
# this should use hashlib version if available,
|
||||
# and fall back to builtin version.
|
||||
from passlib.crypto.digest import lookup_hash
|
||||
md4 = lookup_hash("md4").const
|
||||
del lookup_hash
|
||||
|
||||
#=============================================================================
|
||||
# eof
|
||||
#=============================================================================
|
193
venv/Lib/site-packages/passlib/utils/pbkdf2.py
Normal file
193
venv/Lib/site-packages/passlib/utils/pbkdf2.py
Normal file
@@ -0,0 +1,193 @@
|
||||
"""passlib.pbkdf2 - PBKDF2 support
|
||||
|
||||
this module is getting increasingly poorly named.
|
||||
maybe rename to "kdf" since it's getting more key derivation functions added.
|
||||
"""
|
||||
#=============================================================================
|
||||
# imports
|
||||
#=============================================================================
|
||||
from __future__ import division
|
||||
# core
|
||||
import logging; log = logging.getLogger(__name__)
|
||||
# site
|
||||
# pkg
|
||||
from passlib.exc import ExpectedTypeError
|
||||
from passlib.utils.decor import deprecated_function
|
||||
from passlib.utils.compat import native_string_types
|
||||
from passlib.crypto.digest import norm_hash_name, lookup_hash, pbkdf1 as _pbkdf1, pbkdf2_hmac, compile_hmac
|
||||
# local
|
||||
__all__ = [
|
||||
# hash utils
|
||||
"norm_hash_name",
|
||||
|
||||
# prf utils
|
||||
"get_prf",
|
||||
|
||||
# kdfs
|
||||
"pbkdf1",
|
||||
"pbkdf2",
|
||||
]
|
||||
|
||||
#=============================================================================
|
||||
# issue deprecation warning for module
|
||||
#=============================================================================
|
||||
from warnings import warn
|
||||
|
||||
warn("the module 'passlib.utils.pbkdf2' is deprecated as of Passlib 1.7, "
|
||||
"and will be removed in Passlib 2.0, please use 'passlib.crypto' instead",
|
||||
DeprecationWarning)
|
||||
|
||||
#=============================================================================
|
||||
# hash helpers
|
||||
#=============================================================================
|
||||
|
||||
norm_hash_name = deprecated_function(deprecated="1.7", removed="1.8", func_module=__name__,
|
||||
replacement="passlib.crypto.digest.norm_hash_name")(norm_hash_name)
|
||||
|
||||
#=============================================================================
|
||||
# prf lookup
|
||||
#=============================================================================
|
||||
|
||||
#: cache mapping prf name/func -> (func, digest_size)
|
||||
_prf_cache = {}
|
||||
|
||||
#: list of accepted prefixes
|
||||
_HMAC_PREFIXES = ("hmac_", "hmac-")
|
||||
|
||||
def get_prf(name):
|
||||
"""Lookup pseudo-random family (PRF) by name.
|
||||
|
||||
:arg name:
|
||||
This must be the name of a recognized prf.
|
||||
Currently this only recognizes names with the format
|
||||
:samp:`hmac-{digest}`, where :samp:`{digest}`
|
||||
is the name of a hash function such as
|
||||
``md5``, ``sha256``, etc.
|
||||
|
||||
todo: restore text about callables.
|
||||
|
||||
:raises ValueError: if the name is not known
|
||||
:raises TypeError: if the name is not a callable or string
|
||||
|
||||
:returns:
|
||||
a tuple of :samp:`({prf_func}, {digest_size})`, where:
|
||||
|
||||
* :samp:`{prf_func}` is a function implementing
|
||||
the specified PRF, and has the signature
|
||||
``prf_func(secret, message) -> digest``.
|
||||
|
||||
* :samp:`{digest_size}` is an integer indicating
|
||||
the number of bytes the function returns.
|
||||
|
||||
Usage example::
|
||||
|
||||
>>> from passlib.utils.pbkdf2 import get_prf
|
||||
>>> hmac_sha256, dsize = get_prf("hmac-sha256")
|
||||
>>> hmac_sha256
|
||||
<function hmac_sha256 at 0x1e37c80>
|
||||
>>> dsize
|
||||
32
|
||||
>>> digest = hmac_sha256('password', 'message')
|
||||
|
||||
.. deprecated:: 1.7
|
||||
|
||||
This function is deprecated, and will be removed in Passlib 2.0.
|
||||
This only related replacement is :func:`passlib.crypto.digest.compile_hmac`.
|
||||
"""
|
||||
global _prf_cache
|
||||
if name in _prf_cache:
|
||||
return _prf_cache[name]
|
||||
if isinstance(name, native_string_types):
|
||||
if not name.startswith(_HMAC_PREFIXES):
|
||||
raise ValueError("unknown prf algorithm: %r" % (name,))
|
||||
digest = lookup_hash(name[5:]).name
|
||||
def hmac(key, msg):
|
||||
return compile_hmac(digest, key)(msg)
|
||||
record = (hmac, hmac.digest_info.digest_size)
|
||||
elif callable(name):
|
||||
# assume it's a callable, use it directly
|
||||
digest_size = len(name(b'x', b'y'))
|
||||
record = (name, digest_size)
|
||||
else:
|
||||
raise ExpectedTypeError(name, "str or callable", "prf name")
|
||||
_prf_cache[name] = record
|
||||
return record
|
||||
|
||||
#=============================================================================
|
||||
# pbkdf1 support
|
||||
#=============================================================================
|
||||
def pbkdf1(secret, salt, rounds, keylen=None, hash="sha1"):
|
||||
"""pkcs#5 password-based key derivation v1.5
|
||||
|
||||
:arg secret: passphrase to use to generate key
|
||||
:arg salt: salt string to use when generating key
|
||||
:param rounds: number of rounds to use to generate key
|
||||
:arg keylen: number of bytes to generate (if ``None``, uses digest's native size)
|
||||
:param hash:
|
||||
hash function to use. must be name of a hash recognized by hashlib.
|
||||
|
||||
:returns:
|
||||
raw bytes of generated key
|
||||
|
||||
.. note::
|
||||
|
||||
This algorithm has been deprecated, new code should use PBKDF2.
|
||||
Among other limitations, ``keylen`` cannot be larger
|
||||
than the digest size of the specified hash.
|
||||
|
||||
.. deprecated:: 1.7
|
||||
|
||||
This has been relocated to :func:`passlib.crypto.digest.pbkdf1`,
|
||||
and this version will be removed in Passlib 2.0.
|
||||
*Note the call signature has changed.*
|
||||
"""
|
||||
return _pbkdf1(hash, secret, salt, rounds, keylen)
|
||||
|
||||
#=============================================================================
|
||||
# pbkdf2
|
||||
#=============================================================================
|
||||
def pbkdf2(secret, salt, rounds, keylen=None, prf="hmac-sha1"):
|
||||
"""pkcs#5 password-based key derivation v2.0
|
||||
|
||||
:arg secret:
|
||||
passphrase to use to generate key
|
||||
|
||||
:arg salt:
|
||||
salt string to use when generating key
|
||||
|
||||
:param rounds:
|
||||
number of rounds to use to generate key
|
||||
|
||||
:arg keylen:
|
||||
number of bytes to generate.
|
||||
if set to ``None``, will use digest size of selected prf.
|
||||
|
||||
:param prf:
|
||||
psuedo-random family to use for key strengthening.
|
||||
this must be a string starting with ``"hmac-"``, followed by the name of a known digest.
|
||||
this defaults to ``"hmac-sha1"`` (the only prf explicitly listed in
|
||||
the PBKDF2 specification)
|
||||
|
||||
.. rst-class:: warning
|
||||
|
||||
.. versionchanged 1.7:
|
||||
|
||||
This argument no longer supports arbitrary PRF callables --
|
||||
These were rarely / never used, and created too many unwanted codepaths.
|
||||
|
||||
:returns:
|
||||
raw bytes of generated key
|
||||
|
||||
.. deprecated:: 1.7
|
||||
|
||||
This has been deprecated in favor of :func:`passlib.crypto.digest.pbkdf2_hmac`,
|
||||
and will be removed in Passlib 2.0. *Note the call signature has changed.*
|
||||
"""
|
||||
if callable(prf) or (isinstance(prf, native_string_types) and not prf.startswith(_HMAC_PREFIXES)):
|
||||
raise NotImplementedError("non-HMAC prfs are not supported as of Passlib 1.7")
|
||||
digest = prf[5:]
|
||||
return pbkdf2_hmac(digest, secret, salt, rounds, keylen)
|
||||
|
||||
#=============================================================================
|
||||
# eof
|
||||
#=============================================================================
|
Reference in New Issue
Block a user