##// END OF EJS Templates
sha1dc: use buffer protocol when parsing arguments...
Gregory Szorc -
r44546:dc9b5348 default
parent child Browse files
Show More
@@ -1,198 +1,212 b''
1 1 #define PY_SSIZE_T_CLEAN
2 2 #include <Python.h>
3 3
4 4 #include "lib/sha1.h"
5 5
6 6 #if PY_MAJOR_VERSION >= 3
7 7 #define IS_PY3K
8 8 #endif
9 9
10 10 /* helper to switch things like string literal depending on Python version */
11 11 #ifdef IS_PY3K
12 12 #define PY23(py2, py3) py3
13 13 #else
14 14 #define PY23(py2, py3) py2
15 15 #endif
16 16
17 17 static char sha1dc_doc[] = "Efficient detection of SHA1 collision constructs.";
18 18
19 19 /* clang-format off */
20 20 typedef struct {
21 21 PyObject_HEAD
22 22 SHA1_CTX ctx;
23 23 } pysha1ctx;
24 24 /* clang-format on */
25 25
26 26 static int pysha1ctx_init(pysha1ctx *self, PyObject *args)
27 27 {
28 const char *data = NULL;
29 Py_ssize_t len;
28 Py_buffer data;
29 data.obj = NULL;
30 30
31 31 SHA1DCInit(&(self->ctx));
32 32 /* We don't want "safe" sha1s, wherein sha1dc can give you a
33 33 different hash for something that's trying to give you a
34 34 collision. We just want to detect collisions.
35 35 */
36 36 SHA1DCSetSafeHash(&(self->ctx), 0);
37 if (!PyArg_ParseTuple(args, PY23("|s#", "|y#"), &data, &len)) {
37 if (!PyArg_ParseTuple(args, PY23("|s*", "|y*"), &data)) {
38 38 return -1;
39 39 }
40 if (data) {
41 SHA1DCUpdate(&(self->ctx), data, len);
40 if (data.obj) {
41 if (!PyBuffer_IsContiguous(&data, 'C') || data.ndim > 1) {
42 PyErr_SetString(PyExc_BufferError,
43 "buffer must be contiguous and single dimension");
44 PyBuffer_Release(&data);
45 return -1;
46 }
47
48 SHA1DCUpdate(&(self->ctx), data.buf, data.len);
49 PyBuffer_Release(&data);
42 50 }
43 51 return 0;
44 52 }
45 53
46 54 static void pysha1ctx_dealloc(pysha1ctx *self)
47 55 {
48 56 PyObject_Del(self);
49 57 }
50 58
51 59 static PyObject *pysha1ctx_update(pysha1ctx *self, PyObject *args)
52 60 {
53 const char *data;
54 Py_ssize_t len;
55 if (!PyArg_ParseTuple(args, PY23("s#", "y#"), &data, &len)) {
61 Py_buffer data;
62 if (!PyArg_ParseTuple(args, PY23("s*", "y*"), &data)) {
56 63 return NULL;
57 64 }
58 SHA1DCUpdate(&(self->ctx), data, len);
65 if (!PyBuffer_IsContiguous(&data, 'C') || data.ndim > 1) {
66 PyErr_SetString(PyExc_BufferError,
67 "buffer must be contiguous and single dimension");
68 PyBuffer_Release(&data);
69 return NULL;
70 }
71 SHA1DCUpdate(&(self->ctx), data.buf, data.len);
72 PyBuffer_Release(&data);
59 73 Py_RETURN_NONE;
60 74 }
61 75
62 76 /* it is intentional that this take a ctx by value, as that clones the
63 77 context so we can keep using .update() without poisoning the state
64 78 with padding.
65 79 */
66 80 static int finalize(SHA1_CTX ctx, unsigned char *hash_out)
67 81 {
68 82 if (SHA1DCFinal(hash_out, &ctx)) {
69 83 PyErr_SetString(PyExc_OverflowError,
70 84 "sha1 collision attack detected");
71 85 return 0;
72 86 }
73 87 return 1;
74 88 }
75 89
76 90 static PyObject *pysha1ctx_digest(pysha1ctx *self)
77 91 {
78 92 unsigned char hash[20];
79 93 if (!finalize(self->ctx, hash)) {
80 94 return NULL;
81 95 }
82 96 return PyBytes_FromStringAndSize((char *)hash, 20);
83 97 }
84 98
85 99 static PyObject *pysha1ctx_hexdigest(pysha1ctx *self)
86 100 {
87 101 static const char hexdigit[] = "0123456789abcdef";
88 102 unsigned char hash[20];
89 103 char hexhash[40];
90 104 int i;
91 105 if (!finalize(self->ctx, hash)) {
92 106 return NULL;
93 107 }
94 108 for (i = 0; i < 20; ++i) {
95 109 hexhash[i * 2] = hexdigit[hash[i] >> 4];
96 110 hexhash[i * 2 + 1] = hexdigit[hash[i] & 15];
97 111 }
98 112 return PY23(PyString_FromStringAndSize, PyUnicode_FromStringAndSize)(hexhash, 40);
99 113 }
100 114
101 115 static PyTypeObject sha1ctxType;
102 116
103 117 static PyObject *pysha1ctx_copy(pysha1ctx *self)
104 118 {
105 119 pysha1ctx *clone = (pysha1ctx *)PyObject_New(pysha1ctx, &sha1ctxType);
106 120 if (!clone) {
107 121 return NULL;
108 122 }
109 123 clone->ctx = self->ctx;
110 124 return (PyObject *)clone;
111 125 }
112 126
113 127 static PyMethodDef pysha1ctx_methods[] = {
114 128 {"update", (PyCFunction)pysha1ctx_update, METH_VARARGS,
115 129 "Update this hash object's state with the provided bytes."},
116 130 {"digest", (PyCFunction)pysha1ctx_digest, METH_NOARGS,
117 131 "Return the digest value as a string of binary data."},
118 132 {"hexdigest", (PyCFunction)pysha1ctx_hexdigest, METH_NOARGS,
119 133 "Return the digest value as a string of hexadecimal digits."},
120 134 {"copy", (PyCFunction)pysha1ctx_copy, METH_NOARGS,
121 135 "Return a copy of the hash object."},
122 136 {NULL},
123 137 };
124 138
125 139 /* clang-format off */
126 140 static PyTypeObject sha1ctxType = {
127 141 PyVarObject_HEAD_INIT(NULL, 0) /* header */
128 142 "sha1dc.sha1", /* tp_name */
129 143 sizeof(pysha1ctx), /* tp_basicsize */
130 144 0, /* tp_itemsize */
131 145 (destructor)pysha1ctx_dealloc, /* tp_dealloc */
132 146 0, /* tp_print */
133 147 0, /* tp_getattr */
134 148 0, /* tp_setattr */
135 149 0, /* tp_compare */
136 150 0, /* tp_repr */
137 151 0, /* tp_as_number */
138 152 0, /* tp_as_sequence */
139 153 0, /* tp_as_mapping */
140 154 0, /* tp_hash */
141 155 0, /* tp_call */
142 156 0, /* tp_str */
143 157 0, /* tp_getattro */
144 158 0, /* tp_setattro */
145 159 0, /* tp_as_buffer */
146 160 Py_TPFLAGS_DEFAULT, /* tp_flags */
147 161 "sha1 implementation that looks for collisions", /* tp_doc */
148 162 0, /* tp_traverse */
149 163 0, /* tp_clear */
150 164 0, /* tp_richcompare */
151 165 0, /* tp_weaklistoffset */
152 166 0, /* tp_iter */
153 167 0, /* tp_iternext */
154 168 pysha1ctx_methods, /* tp_methods */
155 169 0, /* tp_members */
156 170 0, /* tp_getset */
157 171 0, /* tp_base */
158 172 0, /* tp_dict */
159 173 0, /* tp_descr_get */
160 174 0, /* tp_descr_set */
161 175 0, /* tp_dictoffset */
162 176 (initproc)pysha1ctx_init, /* tp_init */
163 177 0, /* tp_alloc */
164 178 };
165 179 /* clang-format on */
166 180
167 181 static PyMethodDef methods[] = {
168 182 {NULL, NULL},
169 183 };
170 184
171 185 static void module_init(PyObject *mod)
172 186 {
173 187 sha1ctxType.tp_new = PyType_GenericNew;
174 188 if (PyType_Ready(&sha1ctxType) < 0) {
175 189 return;
176 190 }
177 191 Py_INCREF(&sha1ctxType);
178 192
179 193 PyModule_AddObject(mod, "sha1", (PyObject *)&sha1ctxType);
180 194 }
181 195
182 196 #ifdef IS_PY3K
183 197 static struct PyModuleDef sha1dc_module = {PyModuleDef_HEAD_INIT, "sha1dc",
184 198 sha1dc_doc, -1, methods};
185 199
186 200 PyMODINIT_FUNC PyInit_sha1dc(void)
187 201 {
188 202 PyObject *mod = PyModule_Create(&sha1dc_module);
189 203 module_init(mod);
190 204 return mod;
191 205 }
192 206 #else
193 207 PyMODINIT_FUNC initsha1dc(void)
194 208 {
195 209 PyObject *mod = Py_InitModule3("sha1dc", methods, sha1dc_doc);
196 210 module_init(mod);
197 211 }
198 212 #endif
@@ -1,60 +1,80 b''
1 1 # Tests to ensure that sha1dc.sha1 is exactly a drop-in for
2 2 # hashlib.sha1 for our needs.
3 3 from __future__ import absolute_import
4 4
5 5 import hashlib
6 6 import unittest
7 7
8 8 import silenttestrunner
9 9
10 10 try:
11 11 from mercurial.thirdparty import sha1dc
12 12 except ImportError:
13 13 sha1dc = None
14 14
15 15
16 16 class hashertestsbase(object):
17 17 def test_basic_hash(self):
18 18 h = self.hasher()
19 19 h.update(b'foo')
20 20 self.assertEqual(
21 21 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33', h.hexdigest()
22 22 )
23 23 h.update(b'bar')
24 24 self.assertEqual(
25 25 '8843d7f92416211de9ebb963ff4ce28125932878', h.hexdigest()
26 26 )
27 27
28 28 def test_copy_hasher(self):
29 29 h = self.hasher()
30 30 h.update(b'foo')
31 31 h2 = h.copy()
32 32 h.update(b'baz')
33 33 h2.update(b'bar')
34 34 self.assertEqual(
35 35 '21eb6533733a5e4763acacd1d45a60c2e0e404e1', h.hexdigest()
36 36 )
37 37 self.assertEqual(
38 38 '8843d7f92416211de9ebb963ff4ce28125932878', h2.hexdigest()
39 39 )
40 40
41 41 def test_init_hasher(self):
42 42 h = self.hasher(b'initial string')
43 43 self.assertEqual(
44 44 b'\xc9y|n\x1f3S\xa4:\xbaJ\xca,\xc1\x1a\x9e\xb8\xd8\xdd\x86',
45 45 h.digest(),
46 46 )
47 47
48 def test_bytes_like_types(self):
49 h = self.hasher()
50 h.update(bytearray(b'foo'))
51 h.update(memoryview(b'baz'))
52 self.assertEqual(
53 '21eb6533733a5e4763acacd1d45a60c2e0e404e1', h.hexdigest()
54 )
55
56 h = self.hasher(bytearray(b'foo'))
57 h.update(b'baz')
58 self.assertEqual(
59 '21eb6533733a5e4763acacd1d45a60c2e0e404e1', h.hexdigest()
60 )
61
62 h = self.hasher(memoryview(b'foo'))
63 h.update(b'baz')
64 self.assertEqual(
65 '21eb6533733a5e4763acacd1d45a60c2e0e404e1', h.hexdigest()
66 )
67
48 68
49 69 class hashlibtests(unittest.TestCase, hashertestsbase):
50 70 hasher = hashlib.sha1
51 71
52 72
53 73 if sha1dc:
54 74
55 75 class sha1dctests(unittest.TestCase, hashertestsbase):
56 76 hasher = sha1dc.sha1
57 77
58 78
59 79 if __name__ == '__main__':
60 80 silenttestrunner.main(__name__)
General Comments 0
You need to be logged in to leave comments. Login now