From 2841700f2d3f731fe2f75fada21c20683212a3d7 Mon Sep 17 00:00:00 2001 From: Jephte CLAIN Date: Fri, 4 Jul 2014 12:52:56 +0400 Subject: [PATCH] nouveau module password --- pyulib/src/ulib/all/__init__.py | 1 + pyulib/src/ulib/base/password.py | 141 +++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 pyulib/src/ulib/base/password.py diff --git a/pyulib/src/ulib/all/__init__.py b/pyulib/src/ulib/all/__init__.py index e58d681..f6d9252 100644 --- a/pyulib/src/ulib/all/__init__.py +++ b/pyulib/src/ulib/all/__init__.py @@ -22,6 +22,7 @@ except ImportError: 'getopt', 'optparse', 'args', 'control', 'functions', 'json', 'web', 'flock', + 'password', ) MODULES = list(MODULES) def __has_module_and_remove(m, remove=True): diff --git a/pyulib/src/ulib/base/password.py b/pyulib/src/ulib/base/password.py new file mode 100644 index 0000000..876d6e8 --- /dev/null +++ b/pyulib/src/ulib/base/password.py @@ -0,0 +1,141 @@ +# -*- coding: utf-8 -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 +import i_need_py25 + +"""Des fonctions pour gérer les formes cryptées des mots de passe + +Les scheme supportés sont {CRYPT}, {SSHA}, {SMD5}, {SHA}, {MD5} +""" + +__all__ = () + +import base64, random, hashlib + +try: from crypt import crypt +except: crypt = None + +from uio import Utf8IO +_utf8 = Utf8IO() + +def is_crypt_scheme(pw): + if pw is None: return False + return pw[:7].lower() == '{crypt}' or pw.lower() == 'crypt' + +def is_smd5_scheme(pw): + if pw is None: return False + return pw[:6].lower() == '{smd5}' or pw.lower() == 'smd5' + +def is_ssha_scheme(pw): + if pw is None: return False + return pw[:6].lower() == '{ssha}' or pw.lower() == 'ssha' + +def is_md5_scheme(pw): + if pw is None: return False + return pw[:5].lower() == '{md5}' or pw.lower() == 'md5' + +def is_sha_scheme(pw): + if pw is None: return False + return pw[:5].lower() == '{sha}' or pw.lower() == 'sha' + +def get_scheme(pw): + if pw is None: return None + if is_crypt_scheme(pw): return '{CRYPT}' + elif is_smd5_scheme(pw): return '{SMD5}' + elif is_ssha_scheme(pw): return '{SSHA}' + elif is_md5_scheme(pw): return '{MD5}' + elif is_sha_scheme(pw): return '{SHA}' + else: return '{CRYPT}' + +def get_crypt_salt(pw): + if pw is None: return None + if pw[:7].lower() == '{crypt}': pw = pw[7:] + return pw[:2] + +def get_smd5_salt(pw): + if pw is None: return None + if pw[:6].lower() == '{smd5}': pw = pw[6:] + return base64.decodestring(pw)[16:] + +def get_ssha_salt(pw): + if pw is None: return None + if pw[:6].lower() == '{ssha}': pw = pw[6:] + return base64.decodestring(pw)[20:] + +def get_salt(pw): + if pw is None: return None + if pw[:7].lower() == '{crypt}': return get_crypt_salt(pw) + if pw[:6].lower() == '{smd5}': return get_smd5_salt(pw) + if pw[:6].lower() == '{ssha}': return get_ssha_salt(pw) + # pas de salt + if pw[:5].lower() == '{md5}': return '' + if pw[:5].lower() == '{sha}': return '' + # par défaut, considérer le salt comme pour crypt + return get_crypt_salt(pw) + +CRYPT_SALT_CHARS = "./abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" +def gen_crypt_salt(): + return random.choice(CRYPT_SALT_CHARS) + random.choice(CRYPT_SALT_CHARS) + +def gen_binary_salt(length=20): + salt = [] + for i in range(length): + salt.append(chr(random.randint(0, 255))) + return ''.join(salt) + +def gen_salt(pw=None, length=20): + if pw is not None: + if pw[:7].lower() == '{crypt}' or pw.lower() == 'crypt': return gen_crypt_salt() + if pw[:6].lower() == '{smd5}' or pw.lower() == 'smd5': return gen_binary_salt(length) + if pw[:6].lower() == '{ssha}' or pw.lower() == 'ssha': return gen_binary_salt(length) + # pas de salt pour MD5 et SHA + if pw[:5].lower() == '{md5}' or pw.lower() == 'md5': return '' + if pw[:5].lower() == '{sha}' or pw.lower() == 'sha': return '' + # par défaut, générer un salt pour crypt + return gen_crypt_salt() + +def gen_crypt_hash(pw, salt=None): + if salt is None: salt = gen_crypt_salt() + if crypt is None: raise ValueError("crypt is unvailable") + return crypt(pw, salt) + +def gen_binary_hash(pw, scheme, salt=None): + if is_md5_scheme(scheme): + m = hashlib.md5() + elif is_sha_scheme(scheme): + m = hashlib.sha1() + elif is_smd5_scheme(scheme): + m = hashlib.md5() + if salt is None: salt = gen_binary_salt() + elif is_ssha_scheme(scheme): + m = hashlib.sha1() + if salt is None: salt = gen_binary_salt() + else: raise ValueError("unsupported scheme: %s" % scheme) + + pw = _utf8.s(pw) + m.update(pw) + if salt is not None: m.update(salt) + + hash = m.digest() + if salt is not None: hash += salt + + return base64.b64encode(hash) + +def gen_hash(pw, scheme=None, ref=None, normalized=True): + if ref is not None: + scheme = get_scheme(ref) + salt = get_salt(ref) + if is_crypt_scheme(scheme): + hash = gen_crypt_hash(pw, salt) + else: + hash = gen_binary_hash(pw, scheme, salt) + elif scheme is not None: + scheme = get_scheme(scheme) + if is_crypt_scheme(scheme): + hash = gen_crypt_hash(pw) + else: + hash = gen_binary_hash(pw, scheme) + else: + # Par défaut, hasher en crypt + scheme = '{CRYPT}' + hash = gen_crypt_hash(pw) + if normalized: hash = scheme + hash + return hash