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