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