Skip to main content

Modules/_sha512module.c

This module provides the _sha512 built-in extension, which exposes SHA-512 and SHA-384 digest objects to Python. Since CPython 3.12 the low-level arithmetic is delegated to HACL*, a formally verified C library, replacing the hand-rolled implementation that lived here before.

Map​

SymbolKindNotes
SHA512objectstructholds sha512_state_s and an is_sha384 flag
sha512typePyTypeObjectdigest_size=64, block_size=128
sha384typePyTypeObjectdigest_size=48, block_size=128
SHA512_newfunctionconstructs either type; dispatches on is_sha384
SHA512_copymethodreturns a fresh object sharing the accumulated state
SHA512_digestmethodfinalises a copy, returns bytes of length 64 or 48
SHA512_hexdigestmethodsame but hex-encoded
SHA512_updatemethodfeeds more data into the HACL* state
_sha512_modulePyModuleDefregisters both types and the module-level constructors

Reading​

Object layout and the is_sha384 flag​

typedef struct {
PyObject_HEAD
int is_sha384;
Hacl_Hash_SHA2_state_t_512 *state;
} SHA512object;

A single C struct covers both algorithms. The is_sha384 flag is checked at finalisation time to decide how many bytes to read out of the HACL* state. This keeps the code paths for update and copy identical between the two variants.

Constructing a new hash object​

static SHA512object *
newSHA512object(PyTypeObject *type)
{
SHA512object *sha;
sha = (SHA512object *)PyObject_GC_New(SHA512object, type);
if (sha == NULL)
return NULL;
sha->state = Hacl_Hash_SHA2_malloc_512();
if (sha->state == NULL) {
PyObject_GC_Del(sha);
return NULL;
}
PyObject_GC_Track(sha);
return sha;
}

Hacl_Hash_SHA2_malloc_512 allocates and zeroes the HACL* internal state. The object is tracked by the GC so the finaliser can call Hacl_Hash_SHA2_free_512 even if a cycle prevents normal reference counting from firing.

Finalisation and digest extraction​

static PyObject *
SHA512_digest(SHA512object *self, PyObject *args)
{
uint8_t digest[SHA512_DIGESTSIZE];
Hacl_Hash_SHA2_state_t_512 *state_copy;

state_copy = Hacl_Hash_SHA2_copy_512(self->state);
if (self->is_sha384)
Hacl_Hash_SHA2_finish_384(state_copy, digest);
else
Hacl_Hash_SHA2_finish_512(state_copy, digest);
Hacl_Hash_SHA2_free_512(state_copy);
return PyBytes_FromStringAndSize((char *)digest,
self->is_sha384 ? SHA384_DIGESTSIZE
: SHA512_DIGESTSIZE);
}

The state is copied before finalisation so that update can continue to be called on the original object after digest() returns. CPython's hashlib contract requires this.

gopy mirror​

Not yet ported. The natural home would be module/_sha512/ following the flat layout convention. The HACL* dependency would be replaced by crypto/sha512 from the Go standard library, which is also a pure-Go verified implementation. The is_sha384 flag maps cleanly to a boolean field on a Go struct, and the two PyTypeObject entries map to two objects.Type singletons registered at module init.

CPython 3.14 changes​

  • The HACL* state type was renamed from Hacl_Hash_SHA2_state_t_512 to a typedef alias introduced in _hacl/include/krml/types.h; the module itself is unchanged.
  • A usedforsecurity keyword argument was added to the module-level constructors to align with hashlib.sha512(). The flag is passed through but has no effect on the HACL* path.