summaryrefslogtreecommitdiff
path: root/passlib/utils/pbkdf2.py
blob: d9bd0832368d9ba2ad9b977cc7d55ff2e62d6ee7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
"""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
#=============================================================================
# core
import logging; log = logging.getLogger(__name__)
# site
# pkg
from passlib.exc import ExpectedTypeError
from passlib.crypto.digest import lookup_hash, pbkdf1 as _pbkdf1, pbkdf2_hmac, compile_hmac
# local
__all__ = [
    # 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)

#=============================================================================
# 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, str):
        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, str) 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
#=============================================================================