summaryrefslogtreecommitdiff
path: root/passlib
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2011-01-31 23:34:37 +0000
committerEli Collins <elic@assurancetechnologies.com>2011-01-31 23:34:37 +0000
commit0eafd59fcee89cf4769add43c057a11e62c49a3a (patch)
tree19b476c6317f76ce39140900d1d671440aef34ae /passlib
parent8903ccd8ecf8b26cd5e80466eecfdd8df140482b (diff)
downloadpasslib-0eafd59fcee89cf4769add43c057a11e62c49a3a.tar.gz
updated docs
Diffstat (limited to 'passlib')
-rw-r--r--passlib/base.py10
-rw-r--r--passlib/hash/apr_md5_crypt.py13
-rw-r--r--passlib/hash/postgres_md5.py24
-rw-r--r--passlib/utils/__init__.py64
-rw-r--r--passlib/utils/des.py14
-rw-r--r--passlib/utils/h64.py61
-rw-r--r--passlib/utils/handlers.py8
-rw-r--r--passlib/utils/md4.py23
8 files changed, 82 insertions, 135 deletions
diff --git a/passlib/base.py b/passlib/base.py
index 09e5f89..53d48d4 100644
--- a/passlib/base.py
+++ b/passlib/base.py
@@ -21,10 +21,11 @@ import hashlib
import logging; log = logging.getLogger(__name__)
import time
import os
+from warnings import warn
#site
#libs
import passlib.hash as _hmod
-from passlib.utils import abstract_class_method, Undef, is_crypt_handler, splitcomma
+from passlib.utils import abstractclassmethod, Undef, is_crypt_handler, splitcomma
#pkg
#local
__all__ = [
@@ -85,8 +86,11 @@ def get_crypt_handler(name, default=Undef):
"resolve crypt algorithm name"
global _hmod
- ###normalize name
- ##name = name.replace("-","_").lower()
+ #normalize name
+ alt = name.replace("-","_").lower()
+ if alt != name:
+ warn("handler names be lower-case, and use underscores instead of hyphens: %r => %r" % (name, alt))
+ name = alt
#check if handler loaded
handler = getattr(_hmod, name, None)
diff --git a/passlib/hash/apr_md5_crypt.py b/passlib/hash/apr_md5_crypt.py
index 3c1932c..5eb45fa 100644
--- a/passlib/hash/apr_md5_crypt.py
+++ b/passlib/hash/apr_md5_crypt.py
@@ -1,15 +1,4 @@
-"""passlib.hash.apr_md5_crypt - Apache variant of md5-crypt algorithm
-
-This format is primarily used by Apache in htpasswd files.
-
-.. note::
- This format would be identical to md5-crypt,
- except for two things: it uses ``$apr1$`` as it's prefix
- when encoded, and inserts that constant into the hash calculation
- where md5-crypt would insert ``$1$``.
- Thus, the formats aren't compatible, nor the checksums they contain.
- Other than that, they have identical levels of security.
-"""
+"""passlib.hash.apr_md5_crypt - Apache variant of md5-crypt algorithm"""
#=========================================================
#imports
#=========================================================
diff --git a/passlib/hash/postgres_md5.py b/passlib/hash/postgres_md5.py
index 0efefe6..faa27e5 100644
--- a/passlib/hash/postgres_md5.py
+++ b/passlib/hash/postgres_md5.py
@@ -1,26 +1,4 @@
-"""passlib.hash.postgres_md5 - MD5-based algorithm used by Postgres for pg_shadow table
-
-This implements the md5-based hash algorithm used by Postgres to store
-passwords in the pg_shadow table.
-
-This algorithm shouldn't be used for any purpose besides Postgres interaction,
-it's a weak unsalted algorithm which could be attacked with a rainbow table
-built against common user names.
-
-.. warning::
- This algorithm is slightly different from most of the others,
- in that both encrypt() and verify() require you pass in
- the name of the user account via the required 'user' keyword,
- since postgres uses this in place of a salt :(
-
-Usage Example::
-
- >>> from passlib.hash import postgres_md5 as pm
- >>> pm.encrypt("mypass", user="postgres")
- 'md55fba2ea04fd36069d2574ea71c8efe9d'
- >>> pm.verify("mypass", 'md55fba2ea04fd36069d2574ea71c8efe9d', user="postgres")
- True
-"""
+"""passlib.hash.postgres_md5 - MD5-based algorithm used by Postgres for pg_shadow table"""
#=========================================================
#imports
#=========================================================
diff --git a/passlib/utils/__init__.py b/passlib/utils/__init__.py
index 37b25b2..0a88df9 100644
--- a/passlib/utils/__init__.py
+++ b/passlib/utils/__init__.py
@@ -20,7 +20,7 @@ __all__ = [
#decorators
"classproperty",
"abstractmethod",
- "abstract_class_property",
+ "abstractclassmethod",
#byte manipulation
"bytes_to_list",
@@ -36,7 +36,7 @@ __all__ = [
#decorators
#=================================================================================
class classproperty(object):
- """Decorator which acts like a combination of classmethod+property (limited to read-only)"""
+ """Function decorator which acts like a combination of classmethod+property (limited to read-only properties)"""
def __init__(self, func):
self.im_func = func
@@ -57,9 +57,9 @@ def abstractmethod(func):
update_wrapper(wrapper, func)
return wrapper
-def abstract_class_method(func):
+def abstractclassmethod(func):
"""Class Method decorator which indicates this is a placeholder method which
- should be overridden by subclass.
+ should be overridden by subclass, and must be a classmethod.
If called directly, this method will raise an :exc:`NotImplementedError`.
"""
@@ -76,7 +76,7 @@ Undef = object() #singleton used as default kwd value in some functions
#protocol helpers
#==========================================================
def is_crypt_handler(obj):
- "check if obj follows CryptHandler api"
+ "check if object follows the :ref:`crypt handler api <crypt-handler-api>`"
return all(hasattr(obj, name) for name in (
"name",
"setting_kwds", "context_kwds",
@@ -96,7 +96,7 @@ def is_crypt_handler(obj):
#string helpers
#=================================================================================
def splitcomma(source):
- "split comma separated list into elements, stripping whitespace"
+ "split comma separated string into list elements, stripping whitespace and empty elements"
return [
elem.strip()
for elem in source.split(",")
@@ -143,14 +143,14 @@ def splitcomma(source):
## return out
def bytes_to_int(value):
- "decode bytes as single big-endian integer"
+ "decode string of bytes as single big-endian integer"
out = 0
for v in value:
out = (out<<8) | ord(v)
return out
def int_to_bytes(value, count):
- "encode integer into single big-endian byte string"
+ "encodes integer into single big-endian byte string"
assert value < (1<<(8*count)), "value too large for %d bytes: %d" % (count, value)
return ''.join(
chr((value>>s) & 0xff)
@@ -161,8 +161,8 @@ def int_to_bytes(value, count):
def list_to_bytes(value, bytes=None, order="big"):
"""Returns a multi-character string corresponding to a list of byte values.
- This is similar to :func:`int_to_bytes`, except that this a list of integers
- instead of a single encoded integer.
+ This is similar to :func:`int_to_bytes`, except that this returns a list
+ of integers, where each integer corresponds to a single byte of the input.
:arg value:
The list of integers to encode.
@@ -183,7 +183,7 @@ def list_to_bytes(value, bytes=None, order="big"):
Usage Example::
- >>> from passlib.util import list_to_bytes, bytes_to_list
+ >>> from passlib.utils import list_to_bytes, bytes_to_list
>>> list_to_bytes([4, 210], 4)
'\\x00\\x00\\x04\\xd2'
@@ -253,7 +253,7 @@ def bytes_to_list(value, order="big"):
_join = "".join
def xor_bytes(left, right):
- "bitwise-xor two byte-strings together"
+ "perform bitwise-xor of two byte-strings"
return _join(chr(ord(l) ^ ord(r)) for l, r in zip(left, right))
#=================================================================================
@@ -308,10 +308,10 @@ rng = random.Random(genseed())
#-----------------------------------------------------------------------
def getrandbytes(rng, count):
- """return string of *count* number of random bytes, using specified rng"""
+ """return byte-string containing *count* number of randomly generated bytes, using specified rng"""
#NOTE: would be nice if this was present in stdlib Random class
- ###just in case rng provides this (eg our SystemRandom subclass above)...
+ ###just in case rng provides this...
##meth = getattr(rng, "getrandbytes", None)
##if meth:
## return meth(count)
@@ -324,23 +324,23 @@ def getrandbytes(rng, count):
value //= 0xff
return buf.getvalue()
-def getrandstr(rng, alphabet, count):
- """return string of *size* number of chars, whose elements are drawn from specified alphabet"""
+def getrandstr(rng, charset, count):
+ """return character string containg *count* number of chars, whose elements are drawn from specified charset, using specified rng"""
#check alphabet & count
if count < 0:
raise ValueError, "count must be >= 0"
- letters = len(alphabet)
+ letters = len(charset)
if letters == 0:
raise ValueError, "alphabet must not be empty"
if letters == 1:
- return alphabet * count
+ return charset * count
#get random value, and write out to buffer
#XXX: break into chunks for large number of letters?
value = rng.randrange(0, letters**count)
buf = StringIO()
for i in xrange(count):
- buf.write(alphabet[value % letters])
+ buf.write(charset[value % letters])
value //= letters
assert value == 0
return buf.getvalue()
@@ -374,6 +374,7 @@ def norm_rounds(rounds, default_rounds, min_rounds, max_rounds, name="this crypt
return rounds
def gen_salt(count, charset=h64.CHARS):
+ "generate salt string of *count* chars using specified *charset*"
global rng
return getrandstr(rng, charset, count)
@@ -389,11 +390,12 @@ def norm_salt(salt, min_chars, max_chars=None, charset=h64.CHARS, gen_charset=No
:arg salt: user-provided salt
:arg min_chars: minimum number of chars in salt
:arg max_chars: maximum number of chars in salt (if omitted, same as min_chars)
- :param charset: character set that salt MUST be subset of
+ :param charset: character set that salt MUST be subset of (defaults to :)
:param gen_charset: optional character set to restrict to when generating new salts (defaults to charset)
:param name: optional name of handler, for inserting into error messages
:raises ValueError:
+
* if salt contains chars that aren't in salt_charset.
* if salt contains less than min_salt_chars characters.
@@ -422,27 +424,5 @@ def norm_salt(salt, min_chars, max_chars=None, charset=h64.CHARS, gen_charset=No
return salt
#=================================================================================
-#errors
-#=================================================================================
-
-###NOTE: not all handlers will raise these errors,
-### the only thing currently guaranteed is that they
-### will
-##
-##class HandlerError(ValueError):
-## "helper class for various errors used by some CryptHandlers"
-## message = None
-##
-## def __init__(self, msg=None):
-## ValueError.__init__(self, msg or self.message)
-##
-##class NoChecksumError(HandlerError):
-## "helper raised by CryptHandler.verify() when config string passed in instead of hash"
-## #helper for common message raised by handlers
-##
-## message = "hash lacks checksum (did you pass a config string into verify?)"
-
-
-#=================================================================================
#eof
#=================================================================================
diff --git a/passlib/utils/des.py b/passlib/utils/des.py
index 0851bb3..aea62d7 100644
--- a/passlib/utils/des.py
+++ b/passlib/utils/des.py
@@ -1,17 +1,3 @@
-"""passlib.utils.des -- DES encryption routines
-
-This module contains routines for encrypting blocks of data using the DES algorithm.
-
-They do not support multi-block operation or decryption,
-since they are designed for use in password hash algorithms
-such as ``lmhash`` and ``des-crypt``.
-
-.. function:: expand_des_key
-.. function:: des_encrypt_block
-.. function:: mdes_encrypt_int_block
-
-"""
-
"""
History
=======
diff --git a/passlib/utils/h64.py b/passlib/utils/h64.py
index 9e91cb7..4d472c2 100644
--- a/passlib/utils/h64.py
+++ b/passlib/utils/h64.py
@@ -1,22 +1,4 @@
-"""passlib.utils.h64 - hash64 encoding helpers
-
-many of the password hash algorithms in passlib
-use a encoding scheme very similar to, but not compatible with,
-the standard base64 encoding scheme. the main differences are that
-it uses ``.`` instead of ``+``, and assigns the
-characters *completely* different numeric values.
-
-this encoding system appears to have originated with des-crypt hash,
-but is used by md5-crypt, sha-256-crypt, and others.
-within passlib, this encoding is referred as ``hash64`` encoding,
-and this module contains various utilities functions for encoding
-and decoding strings in that format.
-
-.. note::
- It may *look* like bcrypt uses this scheme,
- when in fact bcrypt uses the standard base64 encoding scheme,
- but with ``+`` replaced with ``.``.
-"""
+"""passlib.utils.h64 - hash64 encoding helpers"""
#=================================================================================
#imports
#=================================================================================
@@ -34,10 +16,9 @@ __all__ = [
"encode_2_offsets",
"encode_1_offset",
- "decode_int12",
+ "decode_int12", "encode_int12"
"decode_int24", "encode_int24",
- "encode_int64",
-
+ "decode_int64", "encode_int64",
]
#=================================================================================
@@ -93,17 +74,17 @@ def encode_1_offset(buffer, o1):
# int <-> b64 string, used by des_crypt, ext_des_crypt
#=================================================================================
-##def decode_int(value):
-## "decode hash-64 format used by crypt into integer"
-## #FORMAT: little-endian, each char contributes 6 bits,
-## # char value = index in H64_CHARS string
-## try:
-## out = 0
-## for c in reversed(value):
-## out = (out<<6) + b64_decode_6bit(c)
-## return out
-## except KeyError:
-## raise ValueError, "invalid character in string"
+def decode_int(value):
+ "decode hash-64 format used by crypt into integer"
+ #FORMAT: little-endian, each char contributes 6 bits,
+ # char value = index in H64_CHARS string
+ try:
+ out = 0
+ for c in reversed(value):
+ out = (out<<6) + b64_decode_6bit(c)
+ return out
+ except KeyError:
+ raise ValueError, "invalid character in string"
def decode_int12(value):
"decode 2 chars of hash-64 format used by crypt, returning 12-bit integer"
@@ -112,8 +93,12 @@ def decode_int12(value):
except KeyError:
raise ValueError, "invalid character"
+def encode_int12(value):
+ "encode 2 chars of hash-64 format from a 12-bit integer"
+ return encode_6bit(value & 0x3f) + encode_6bit((value>>6) & 0x3f)
+
def decode_int24(value):
- "decode 4 chars of hash-64 format used by crypt, returning 24-bit integer"
+ "decode 4 chars of hash-64 format, returning 24-bit integer"
try:
return decode_6bit(value[0]) +\
(decode_6bit(value[1])<<6)+\
@@ -123,7 +108,7 @@ def decode_int24(value):
raise ValueError, "invalid character"
def encode_int24(value):
- "decode 2 chars of hash-64 format used by crypt, returning 12-bit integer"
+ "encode 4 chars of hash-64 format from a 24-bit integer"
return encode_6bit(value & 0x3f) + \
encode_6bit((value>>6) & 0x3f) + \
encode_6bit((value>>12) & 0x3f) + \
@@ -131,8 +116,12 @@ def encode_int24(value):
_RR9_1 = range(9,-1,-1)
+def decode_int64(value):
+ "decode 64-bit integer from 11 chars of hash-64 format"
+ return decode_int(value)
+
def encode_int64(value):
- "encode 64-bit integer to hash-64 format used by crypt, returning 11 chars"
+ "encode 64-bit integer to hash-64 format, returning 11 chars"
out = [None] * 10 + [ encode_6bit((value<<2)&0x3f) ]
value >>= 4
for i in _RR9_1:
diff --git a/passlib/utils/handlers.py b/passlib/utils/handlers.py
index 6c09375..8e9fe1f 100644
--- a/passlib/utils/handlers.py
+++ b/passlib/utils/handlers.py
@@ -12,7 +12,7 @@ import time
import os
#site
#libs
-from passlib.utils import abstract_class_method, classproperty, h64, \
+from passlib.utils import abstractclassmethod, classproperty, h64, \
getrandstr, rng, Undef, is_crypt_handler
#pkg
#local
@@ -67,7 +67,7 @@ class CryptHandler(object):
#primary interface - primary methods implemented by each handler
#=========================================================
- @abstract_class_method
+ @abstractclassmethod
def genhash(cls, secret, config, **context):
"""encrypt secret to hash
@@ -355,7 +355,7 @@ class CryptHandler(object):
## #backend parsing routines - used by helpers below
## #=========================================================
##
-## @abstract_class_method
+## @abstractclassmethod
## def parse(cls, hash):
## """parse hash or config into dictionary.
##
@@ -388,7 +388,7 @@ class CryptHandler(object):
## verify() method can work properly.
## """
##
-## @abstract_class_method
+## @abstractclassmethod
## def render(cls, checksum=None, **settings):
## """render hash from checksum & settings (as returned by :meth:`parse`).
##
diff --git a/passlib/utils/md4.py b/passlib/utils/md4.py
index 550eff8..f01ecb0 100644
--- a/passlib/utils/md4.py
+++ b/passlib/utils/md4.py
@@ -24,7 +24,28 @@ def new(content=None):
return md4(content)
class md4(object):
- "md4 hash algorithm"
+ """pep-247 compatible implementation of MD4 hash algorithm
+
+ .. attribute:: digest_size
+
+ size of md4 digest in bytes (16 bytes)
+
+ .. method:: update
+
+ update digest by appending additional content
+
+ .. method:: copy
+
+ create clone of digest object, including current state
+
+ .. method:: digest
+
+ return bytes representing md4 digest of current content
+
+ .. method:: hexdigest
+
+ return hexdecimal version of digest
+ """
#FIXME: make this follow hash object PEP better.
#FIXME: this isn't threadsafe
#XXX: should we monkeypatch ourselves into hashlib for general use? probably wouldn't be nice.