An interpreted, interactive, object-oriented programming language
CentOS Sources
2017-08-01 71084d584ff953f5463757ec6536406320560b4d
commit | author | age
f63228 1
CS 2 # HG changeset patch
3 # User Benjamin Peterson <benjamin@python.org>
4 # Date 1399849904 25200
5 # Node ID b40f1a00b13460cc089450028280c4e52dd24a64
6 # Parent  951775c68b1b7782750c213b0fce1f61d46b2f51
7 backport hmac.compare_digest to partially implement PEP 466 (closes #21306)
8
9 Backport from Alex Gaynor.
10
11 diff --git a/Doc/library/hmac.rst b/Doc/library/hmac.rst
12 --- a/Doc/library/hmac.rst
13 +++ b/Doc/library/hmac.rst
14 @@ -38,6 +38,13 @@ An HMAC object has the following methods
15     This string will be the same length as the *digest_size* of the digest given to
16     the constructor.  It may contain non-ASCII characters, including NUL bytes.
17  
18 +   .. warning::
19 +
20 +      When comparing the output of :meth:`digest` to an externally-supplied
21 +      digest during a verification routine, it is recommended to use the
22 +      :func:`compare_digest` function instead of the ``==`` operator
23 +      to reduce the vulnerability to timing attacks.
24 +
25  
26  .. method:: HMAC.hexdigest()
27  
28 @@ -45,6 +52,13 @@ An HMAC object has the following methods
29     containing only hexadecimal digits.  This may be used to exchange the value
30     safely in email or other non-binary environments.
31  
32 +   .. warning::
33 +
34 +      When comparing the output of :meth:`hexdigest` to an externally-supplied
35 +      digest during a verification routine, it is recommended to use the
36 +      :func:`compare_digest` function instead of the ``==`` operator
37 +      to reduce the vulnerability to timing attacks.
38 +
39  
40  .. method:: HMAC.copy()
41  
42 @@ -52,6 +66,25 @@ An HMAC object has the following methods
43     compute the digests of strings that share a common initial substring.
44  
45  
46 +This module also provides the following helper function:
47 +
48 +.. function:: compare_digest(a, b)
49 +
50 +   Return ``a == b``.  This function uses an approach designed to prevent
51 +   timing analysis by avoiding content-based short circuiting behaviour,
52 +   making it appropriate for cryptography.  *a* and *b* must both be of the
53 +   same type: either :class:`unicode` or a :term:`bytes-like object`.
54 +
55 +   .. note::
56 +
57 +      If *a* and *b* are of different lengths, or if an error occurs,
58 +      a timing attack could theoretically reveal information about the
59 +      types and lengths of *a* and *b*--but not their values.
60 +
61 +
62 +   .. versionadded:: 2.7.7
63 +
64 +
65  .. seealso::
66  
67     Module :mod:`hashlib`
68 diff --git a/Lib/hmac.py b/Lib/hmac.py
69 --- a/Lib/hmac.py
70 +++ b/Lib/hmac.py
71 @@ -5,6 +5,9 @@ Implements the HMAC algorithm as describ
72  
73  import warnings as _warnings
74  
75 +from operator import _compare_digest as compare_digest
76 +
77 +
78  trans_5C = "".join ([chr (x ^ 0x5C) for x in xrange(256)])
79  trans_36 = "".join ([chr (x ^ 0x36) for x in xrange(256)])
80  
81 diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py
82 --- a/Lib/test/test_hmac.py
83 +++ b/Lib/test/test_hmac.py
84 @@ -302,12 +302,122 @@ class CopyTestCase(unittest.TestCase):
85          self.assertTrue(h1.hexdigest() == h2.hexdigest(),
86              "Hexdigest of copy doesn't match original hexdigest.")
87  
88 +
89 +class CompareDigestTestCase(unittest.TestCase):
90 +
91 +    def test_compare_digest(self):
92 +        # Testing input type exception handling
93 +        a, b = 100, 200
94 +        self.assertRaises(TypeError, hmac.compare_digest, a, b)
95 +        a, b = 100, b"foobar"
96 +        self.assertRaises(TypeError, hmac.compare_digest, a, b)
97 +        a, b = b"foobar", 200
98 +        self.assertRaises(TypeError, hmac.compare_digest, a, b)
99 +        a, b = u"foobar", b"foobar"
100 +        self.assertRaises(TypeError, hmac.compare_digest, a, b)
101 +        a, b = b"foobar", u"foobar"
102 +        self.assertRaises(TypeError, hmac.compare_digest, a, b)
103 +
104 +        # Testing bytes of different lengths
105 +        a, b = b"foobar", b"foo"
106 +        self.assertFalse(hmac.compare_digest(a, b))
107 +        a, b = b"\xde\xad\xbe\xef", b"\xde\xad"
108 +        self.assertFalse(hmac.compare_digest(a, b))
109 +
110 +        # Testing bytes of same lengths, different values
111 +        a, b = b"foobar", b"foobaz"
112 +        self.assertFalse(hmac.compare_digest(a, b))
113 +        a, b = b"\xde\xad\xbe\xef", b"\xab\xad\x1d\xea"
114 +        self.assertFalse(hmac.compare_digest(a, b))
115 +
116 +        # Testing bytes of same lengths, same values
117 +        a, b = b"foobar", b"foobar"
118 +        self.assertTrue(hmac.compare_digest(a, b))
119 +        a, b = b"\xde\xad\xbe\xef", b"\xde\xad\xbe\xef"
120 +        self.assertTrue(hmac.compare_digest(a, b))
121 +
122 +        # Testing bytearrays of same lengths, same values
123 +        a, b = bytearray(b"foobar"), bytearray(b"foobar")
124 +        self.assertTrue(hmac.compare_digest(a, b))
125 +
126 +        # Testing bytearrays of diffeent lengths
127 +        a, b = bytearray(b"foobar"), bytearray(b"foo")
128 +        self.assertFalse(hmac.compare_digest(a, b))
129 +
130 +        # Testing bytearrays of same lengths, different values
131 +        a, b = bytearray(b"foobar"), bytearray(b"foobaz")
132 +        self.assertFalse(hmac.compare_digest(a, b))
133 +
134 +        # Testing byte and bytearray of same lengths, same values
135 +        a, b = bytearray(b"foobar"), b"foobar"
136 +        self.assertTrue(hmac.compare_digest(a, b))
137 +        self.assertTrue(hmac.compare_digest(b, a))
138 +
139 +        # Testing byte bytearray of diffeent lengths
140 +        a, b = bytearray(b"foobar"), b"foo"
141 +        self.assertFalse(hmac.compare_digest(a, b))
142 +        self.assertFalse(hmac.compare_digest(b, a))
143 +
144 +        # Testing byte and bytearray of same lengths, different values
145 +        a, b = bytearray(b"foobar"), b"foobaz"
146 +        self.assertFalse(hmac.compare_digest(a, b))
147 +        self.assertFalse(hmac.compare_digest(b, a))
148 +
149 +        # Testing str of same lengths
150 +        a, b = "foobar", "foobar"
151 +        self.assertTrue(hmac.compare_digest(a, b))
152 +
153 +        # Testing str of diffeent lengths
154 +        a, b = "foo", "foobar"
155 +        self.assertFalse(hmac.compare_digest(a, b))
156 +
157 +        # Testing bytes of same lengths, different values
158 +        a, b = "foobar", "foobaz"
159 +        self.assertFalse(hmac.compare_digest(a, b))
160 +
161 +        # Testing error cases
162 +        a, b = u"foobar", b"foobar"
163 +        self.assertRaises(TypeError, hmac.compare_digest, a, b)
164 +        a, b = b"foobar", u"foobar"
165 +        self.assertRaises(TypeError, hmac.compare_digest, a, b)
166 +        a, b = b"foobar", 1
167 +        self.assertRaises(TypeError, hmac.compare_digest, a, b)
168 +        a, b = 100, 200
169 +        self.assertRaises(TypeError, hmac.compare_digest, a, b)
170 +        a, b = "fooä", "fooä"
171 +        self.assertTrue(hmac.compare_digest(a, b))
172 +
173 +        # subclasses are supported by ignore __eq__
174 +        class mystr(str):
175 +            def __eq__(self, other):
176 +                return False
177 +
178 +        a, b = mystr("foobar"), mystr("foobar")
179 +        self.assertTrue(hmac.compare_digest(a, b))
180 +        a, b = mystr("foobar"), "foobar"
181 +        self.assertTrue(hmac.compare_digest(a, b))
182 +        a, b = mystr("foobar"), mystr("foobaz")
183 +        self.assertFalse(hmac.compare_digest(a, b))
184 +
185 +        class mybytes(bytes):
186 +            def __eq__(self, other):
187 +                return False
188 +
189 +        a, b = mybytes(b"foobar"), mybytes(b"foobar")
190 +        self.assertTrue(hmac.compare_digest(a, b))
191 +        a, b = mybytes(b"foobar"), b"foobar"
192 +        self.assertTrue(hmac.compare_digest(a, b))
193 +        a, b = mybytes(b"foobar"), mybytes(b"foobaz")
194 +        self.assertFalse(hmac.compare_digest(a, b))
195 +
196 +
197  def test_main():
198      test_support.run_unittest(
199          TestVectorsTestCase,
200          ConstructorTestCase,
201          SanityTestCase,
202 -        CopyTestCase
203 +        CopyTestCase,
204 +        CompareDigestTestCase,
205      )
206  
207  if __name__ == "__main__":
208 diff --git a/Modules/operator.c b/Modules/operator.c
209 --- a/Modules/operator.c
210 +++ b/Modules/operator.c
211 @@ -235,6 +235,132 @@ op_delslice(PyObject *s, PyObject *a)
212  #define spam2o(OP,ALTOP,DOC) {#OP, op_##OP, METH_O, PyDoc_STR(DOC)}, \
213                             {#ALTOP, op_##OP, METH_O, PyDoc_STR(DOC)},
214  
215 +
216 +
217 +/* compare_digest **********************************************************/
218 +
219 +/*
220 + * timing safe compare
221 + *
222 + * Returns 1 of the strings are equal.
223 + * In case of len(a) != len(b) the function tries to keep the timing
224 + * dependent on the length of b. CPU cache locally may still alter timing
225 + * a bit.
226 + */
227 +static int
228 +_tscmp(const unsigned char *a, const unsigned char *b,
229 +        Py_ssize_t len_a, Py_ssize_t len_b)
230 +{
231 +    /* The volatile type declarations make sure that the compiler has no
232 +     * chance to optimize and fold the code in any way that may change
233 +     * the timing.
234 +     */
235 +    volatile Py_ssize_t length;
236 +    volatile const unsigned char *left;
237 +    volatile const unsigned char *right;
238 +    Py_ssize_t i;
239 +    unsigned char result;
240 +
241 +    /* loop count depends on length of b */
242 +    length = len_b;
243 +    left = NULL;
244 +    right = b;
245 +
246 +    /* don't use else here to keep the amount of CPU instructions constant,
247 +     * volatile forces re-evaluation
248 +     *  */
249 +    if (len_a == length) {
250 +        left = *((volatile const unsigned char**)&a);
251 +        result = 0;
252 +    }
253 +    if (len_a != length) {
254 +        left = b;
255 +        result = 1;
256 +    }
257 +
258 +    for (i=0; i < length; i++) {
259 +        result |= *left++ ^ *right++;
260 +    }
261 +
262 +    return (result == 0);
263 +}
264 +
265 +PyDoc_STRVAR(compare_digest__doc__,
266 +"compare_digest(a, b) -> bool\n"
267 +"\n"
268 +"Return 'a == b'.  This function uses an approach designed to prevent\n"
269 +"timing analysis, making it appropriate for cryptography.\n"
270 +"a and b must both be of the same type: either str (ASCII only),\n"
271 +"or any type that supports the buffer protocol (e.g. bytes).\n"
272 +"\n"
273 +"Note: If a and b are of different lengths, or if an error occurs,\n"
274 +"a timing attack could theoretically reveal information about the\n"
275 +"types and lengths of a and b--but not their values.\n");
276 +
277 +static PyObject*
278 +compare_digest(PyObject *self, PyObject *args)
279 +{
280 +    PyObject *a, *b;
281 +    int rc;
282 +
283 +    if (!PyArg_ParseTuple(args, "OO:compare_digest", &a, &b)) {
284 +        return NULL;
285 +    }
286 +
287 +    /* Unicode string */
288 +    if (PyUnicode_Check(a) && PyUnicode_Check(b)) {
289 +        rc = _tscmp(PyUnicode_AS_DATA(a),
290 +                    PyUnicode_AS_DATA(b),
291 +                    PyUnicode_GET_DATA_SIZE(a),
292 +                    PyUnicode_GET_DATA_SIZE(b));
293 +    }
294 +    /* fallback to buffer interface for bytes, bytesarray and other */
295 +    else {
296 +        Py_buffer view_a;
297 +        Py_buffer view_b;
298 +
299 +        if ((PyObject_CheckBuffer(a) == 0) & (PyObject_CheckBuffer(b) == 0)) {
300 +            PyErr_Format(PyExc_TypeError,
301 +                         "unsupported operand types(s) or combination of types: "
302 +                         "'%.100s' and '%.100s'",
303 +                         Py_TYPE(a)->tp_name, Py_TYPE(b)->tp_name);
304 +            return NULL;
305 +        }
306 +
307 +        if (PyObject_GetBuffer(a, &view_a, PyBUF_SIMPLE) == -1) {
308 +            return NULL;
309 +        }
310 +        if (view_a.ndim > 1) {
311 +            PyErr_SetString(PyExc_BufferError,
312 +                            "Buffer must be single dimension");
313 +            PyBuffer_Release(&view_a);
314 +            return NULL;
315 +        }
316 +
317 +        if (PyObject_GetBuffer(b, &view_b, PyBUF_SIMPLE) == -1) {
318 +            PyBuffer_Release(&view_a);
319 +            return NULL;
320 +        }
321 +        if (view_b.ndim > 1) {
322 +            PyErr_SetString(PyExc_BufferError,
323 +                            "Buffer must be single dimension");
324 +            PyBuffer_Release(&view_a);
325 +            PyBuffer_Release(&view_b);
326 +            return NULL;
327 +        }
328 +
329 +        rc = _tscmp((const unsigned char*)view_a.buf,
330 +                    (const unsigned char*)view_b.buf,
331 +                    view_a.len,
332 +                    view_b.len);
333 +
334 +        PyBuffer_Release(&view_a);
335 +        PyBuffer_Release(&view_b);
336 +    }
337 +
338 +    return PyBool_FromLong(rc);
339 +}
340 +
341  static struct PyMethodDef operator_methods[] = {
342  
343  spam1o(isCallable,
344 @@ -318,6 +444,8 @@ spam2(ne,__ne__, "ne(a, b) -- Same as a!
345  spam2(gt,__gt__, "gt(a, b) -- Same as a>b.")
346  spam2(ge,__ge__, "ge(a, b) -- Same as a>=b.")
347  
348 +    {"_compare_digest", (PyCFunction)compare_digest, METH_VARARGS,
349 +     compare_digest__doc__},
350      {NULL,              NULL}           /* sentinel */
351  
352  };
353