summaryrefslogtreecommitdiff
path: root/passlib/ifc.py
blob: 79911fb87c5140053edf8190c401c4cb6fe93a27 (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
181
182
183
184
185
186
187
188
189
190
191
192
193
"""passlib.ifc - abstract interfaces used by Passlib"""
#=============================================================================
# imports
#=============================================================================
# core
import logging; log = logging.getLogger(__name__)
import sys
# site
# pkg
# local
__all__ = [
    "PasswordHash",
]

#=====================================================
# 2.5-3.2 compatibility helpers
#=====================================================
if sys.version_info >= (2,6):
    from abc import ABCMeta, abstractmethod, abstractproperty
else:
    # create stub for python 2.5
    ABCMeta = type
    def abstractmethod(func):
        return func
#    def abstractproperty():
#        return None

def create_with_metaclass(meta):
    "class decorator that re-creates class using metaclass"
    # have to do things this way since abc not present in py25,
    # and py2/py3 have different ways of doing metaclasses.
    def builder(cls):
        if meta is type(cls):
            return cls
        return meta(cls.__name__, cls.__bases__, cls.__dict__.copy())
    return builder

#=====================================================
# PasswordHash interface
#=====================================================
class PasswordHash(object):
    """This class describes an abstract interface which all password hashes
    in Passlib adhere to. Under Python 2.6 and up, this is an actual
    Abstract Base Class built using the :mod:`!abc` module.

    See the Passlib docs for full documentation.
    """
    #=====================================================
    # class attributes
    #=====================================================

    #----------------------------------
    # general information
    #----------------------------------
    ##name
    ##setting_kwds
    ##context_kwds

    #----------------------------------
    # salt information -- if 'salt' in setting_kwds
    #----------------------------------
    ##min_salt_size
    ##max_salt_size
    ##default_salt_size
    ##salt_chars
    ##default_salt_chars

    #----------------------------------
    # rounds information -- if 'rounds' in setting_kwds
    #----------------------------------
    ##min_rounds
    ##max_rounds
    ##default_rounds
    ##rounds_cost

    #----------------------------------
    # encoding info -- if 'encoding' in context_kwds
    #----------------------------------
    ##default_encoding

    #=====================================================
    # primary methods
    #=====================================================
    @classmethod
    @abstractmethod
    def encrypt(cls, secret, **setting_and_context_kwds): # pragma: no cover -- abstract method
        "encrypt secret, returning resulting hash"
        raise NotImplementedError("must be implemented by subclass")

    @classmethod
    @abstractmethod
    def verify(cls, secret, hash, **context_kwds): # pragma: no cover -- abstract method
        "verify secret against hash, returns True/False"
        raise NotImplementedError("must be implemented by subclass")

    #=====================================================
    # additional methods
    #=====================================================
    @classmethod
    @abstractmethod
    def identify(cls, hash): # pragma: no cover -- abstract method
        "check if hash belongs to this scheme, returns True/False"
        raise NotImplementedError("must be implemented by subclass")

    @classmethod
    @abstractmethod
    def genconfig(cls, **setting_kwds): # pragma: no cover -- abstract method
        "compile settings into a configuration string for genhash()"
        raise NotImplementedError("must be implemented by subclass")

    @classmethod
    @abstractmethod
    def genhash(cls, secret, config, **context_kwds): # pragma: no cover -- abstract method
        "generated hash for secret, using settings from config/hash string"
        raise NotImplementedError("must be implemented by subclass")

    #=====================================================
    # undocumented methods / attributes
    #=====================================================
    # the following entry points are used internally by passlib,
    # and aren't documented as part of the exposed interface.
    # they are subject to change between releases,
    # but are documented here so there's a list of them *somewhere*.

    #----------------------------------
    # checksum information - defined for many hashes
    #----------------------------------
    ## checksum_chars
    ## checksum_size

    #----------------------------------
    # CryptContext flags
    #----------------------------------

    # hack for bsdi_crypt: if True, causes CryptContext to only generate
    # odd rounds values. assumed False if not defined.
    ## _avoid_even_rounds = False

    ##@classmethod
    ##def _bind_needs_update(cls, **setting_kwds):
    ##    """return helper to detect hashes that need updating.
    ##
    ##    if this method is defined, the CryptContext constructor
    ##    will invoke it with the settings specified for the context.
    ##    this method should return either ``None``, or a callable
    ##    with the signature ``needs_update(hash,secret)->bool``.
    ##
    ##    this ``needs_update`` function should return True if the hash
    ##    should be re-encrypted, whether due to internal
    ##    issues or the specified settings.
    ##
    ##    CryptContext will automatically take care of deprecating
    ##    hashes with insufficient rounds for classes which define fromstring()
    ##    and a rounds attribute - though the requirements for this last
    ##    part may change at some point.
    ##    """

    #----------------------------------
    # experimental methods
    #----------------------------------

    ##@classmethod
    ##def normhash(cls, hash):
    ##    """helper to clean up non-canonic instances of hash.
    ##    currently only provided by bcrypt() to fix an historical passlib issue.
    ##    """

    # experimental helper to parse hash into components.
    ##@classmethod
    ##def parsehash(cls, hash, checksum=True, sanitize=False):
    ##    """helper to parse hash into components, returns dict"""

    # experiment helper to estimate bitsize of different hashes,
    # implement for GenericHandler, but may be currently be off for some hashes.
    # want to expand this into a way to programmatically compare
    # "strengths" of different hashes and hash algorithms.
    # still needs to have some factor for estimate relative cost per round,
    # ala in the style of the scrypt whitepaper.
    ##@classmethod
    ##def bitsize(cls, **kwds):
    ##    """returns dict mapping component -> bits contributed.
    ##    components currently include checksum, salt, rounds.
    ##    """

    #=====================================================
    # eoc
    #=====================================================

PasswordHash = create_with_metaclass(ABCMeta)(PasswordHash)

#=============================================================================
# eof
#=============================================================================