Skip to content

Commit 308b376

Browse files
committed
optimize calls to PyMem_Malloc in SHAKE digest computation
1 parent 2dbada1 commit 308b376

File tree

4 files changed

+55
-37
lines changed

4 files changed

+55
-37
lines changed

Lib/test/test_hashlib.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,11 @@ def hash_constructors(self):
207207
constructors = self.constructors_to_test.values()
208208
return itertools.chain.from_iterable(constructors)
209209

210+
@property
211+
def shake_constructors(self):
212+
for shake_name in self.shakes:
213+
yield from self.constructors_to_test.get(shake_name, ())
214+
210215
@property
211216
def is_fips_mode(self):
212217
return get_fips_mode()
@@ -376,6 +381,13 @@ def test_hexdigest(self):
376381
self.assertIsInstance(h.digest(), bytes)
377382
self.assertEqual(hexstr(h.digest()), h.hexdigest())
378383

384+
def test_shakes_digest_zero_length(self):
385+
for constructor in self.shake_constructors:
386+
with self.subTest(constructor=constructor):
387+
h = constructor(b'abcdef', usedforsecurity=False)
388+
self.assertEqual(h.digest(0), b'')
389+
self.assertEqual(h.hexdigest(0), '')
390+
379391
def test_digest_length_overflow(self):
380392
# See issue #34922
381393
large_sizes = (2**29, 2**32-10, 2**32+10, 2**61, 2**64-10, 2**64+10)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:mod:`hashlib`: ensure that exceptions raised by SHAKE objects when a digest
2+
length exceeding ``2 ** 29`` is given are consistent across implementations.
3+
Patch by Bénédikt Tran.

Modules/_hashopenssl.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -938,8 +938,13 @@ _hashlib_HASHXOF_digest_impl(HASHobject *self, Py_ssize_t length)
938938
/*[clinic end generated code: output=dcb09335dd2fe908 input=3eb034ce03c55b21]*/
939939
{
940940
EVP_MD_CTX *temp_ctx;
941-
PyObject *retval = PyBytes_FromStringAndSize(NULL, length);
941+
PyObject *retval;
942+
943+
if (length == 0) {
944+
return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES);
945+
}
942946

947+
retval = PyBytes_FromStringAndSize(NULL, length);
943948
if (retval == NULL) {
944949
return NULL;
945950
}
@@ -986,6 +991,10 @@ _hashlib_HASHXOF_hexdigest_impl(HASHobject *self, Py_ssize_t length)
986991
EVP_MD_CTX *temp_ctx;
987992
PyObject *retval;
988993

994+
if (length == 0) {
995+
return Py_GetConstant(Py_CONSTANT_EMPTY_STR);
996+
}
997+
989998
digest = (unsigned char*)PyMem_Malloc(length);
990999
if (digest == NULL) {
9911000
PyErr_NoMemory();

Modules/sha3module.c

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -472,40 +472,6 @@ SHA3_TYPE_SPEC(sha3_384_spec, "sha3_384", sha3_384_slots);
472472
SHA3_TYPE_SLOTS(sha3_512_slots, sha3_512__doc__, SHA3_methods, SHA3_getseters);
473473
SHA3_TYPE_SPEC(sha3_512_spec, "sha3_512", sha3_512_slots);
474474

475-
static PyObject *
476-
_SHAKE_digest(PyObject *op, unsigned long digestlen, int hex)
477-
{
478-
unsigned char *digest = NULL;
479-
PyObject *result = NULL;
480-
SHA3object *self = _SHA3object_CAST(op);
481-
482-
if (digestlen >= (1 << 29)) {
483-
PyErr_SetString(PyExc_ValueError, "length is too large");
484-
return NULL;
485-
}
486-
digest = (unsigned char*)PyMem_Malloc(digestlen);
487-
if (digest == NULL) {
488-
return PyErr_NoMemory();
489-
}
490-
491-
/* Get the raw (binary) digest value. The HACL functions errors out if:
492-
* - the algorithm is not shake -- not the case here
493-
* - the output length is zero -- we follow the existing behavior and return
494-
* an empty digest, without raising an error */
495-
if (digestlen > 0) {
496-
(void)Hacl_Hash_SHA3_squeeze(self->hash_state, digest, digestlen);
497-
}
498-
if (hex) {
499-
result = _Py_strhex((const char *)digest, digestlen);
500-
}
501-
else {
502-
result = PyBytes_FromStringAndSize((const char *)digest, digestlen);
503-
}
504-
PyMem_Free(digest);
505-
return result;
506-
}
507-
508-
509475
/*[clinic input]
510476
_sha3.shake_128.digest
511477
@@ -518,7 +484,21 @@ static PyObject *
518484
_sha3_shake_128_digest_impl(SHA3object *self, unsigned long length)
519485
/*[clinic end generated code: output=2313605e2f87bb8f input=93d6d6ff32904f18]*/
520486
{
521-
return _SHAKE_digest((PyObject *)self, length, 0);
487+
/*
488+
* Hacl_Hash_SHA3_squeeze() fails if the algorithm is not SHAKE,
489+
* or if the length is 0. In the latter case, we follow OpenSSL's
490+
* behavior and return an empty digest, without raising an error.
491+
*/
492+
if (length == 0) {
493+
return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES);
494+
}
495+
496+
PyObject *digest = PyBytes_FromStringAndSize(NULL, length);
497+
uint8_t *buffer = (uint8_t *)PyBytes_AS_STRING(digest);
498+
ENTER_HASHLIB(self);
499+
(void)Hacl_Hash_SHA3_squeeze(self->hash_state, buffer, length);
500+
LEAVE_HASHLIB(self);
501+
return digest;
522502
}
523503

524504

@@ -534,7 +514,21 @@ static PyObject *
534514
_sha3_shake_128_hexdigest_impl(SHA3object *self, unsigned long length)
535515
/*[clinic end generated code: output=bf8e2f1e490944a8 input=562d74e7060b56ab]*/
536516
{
537-
return _SHAKE_digest((PyObject *)self, length, 1);
517+
/* See _sha3_shake_128_digest_impl() for the fast path rationale. */
518+
if (length == 0) {
519+
return Py_GetConstant(Py_CONSTANT_EMPTY_STR);
520+
}
521+
522+
uint8_t *buffer = PyMem_Malloc(length);
523+
if (buffer == NULL) {
524+
return PyErr_NoMemory();
525+
}
526+
ENTER_HASHLIB(self);
527+
(void)Hacl_Hash_SHA3_squeeze(self->hash_state, buffer, length);
528+
LEAVE_HASHLIB(self);
529+
PyObject *digest = _Py_strhex((const char *)buffer, length);
530+
PyMem_Free(buffer);
531+
return digest;
538532
}
539533

540534
static PyObject *

0 commit comments

Comments
 (0)