Skip to main content

Modules/_hashopenssl.c (part 5)

Source:

cpython 3.14 @ ab2d84fe1023/Modules/_hashopenssl.c

This annotation covers HMAC and the algorithm registry. See modules_hashlib4_detail for hashlib.md5/sha256, the EVPobject, and digest/hexdigest.

Map

LinesSymbolRole
1-80hashlib.newConstruct a hash object by name
81-180Algorithm registryMap string names to OpenSSL EVP_MD pointers
181-280HMAChmac.new using HMAC_CTX
281-380BLAKE2hashlib.blake2b / hashlib.blake2s
381-500hashlib.pbkdf2_hmacPKCS#5 key derivation

Reading

hashlib.new

// CPython: Modules/_hashopenssl.c:880 _hashlib_new_impl
static PyObject *
_hashlib_new_impl(PyObject *module, const char *name, PyObject *data_obj,
int usedforsecurity)
{
const EVP_MD *digest = py_digest_by_name(module, name, usedforsecurity);
if (digest == NULL) return NULL;
PyObject *ret = EVPnew(module, digest);
if (data_obj != NULL && data_obj != Py_None) {
Py_buffer view;
PyObject_GetBuffer(data_obj, &view, PyBUF_SIMPLE);
EVP_DigestUpdate(((EVPobject *)ret)->ctx, view.buf, view.len);
PyBuffer_Release(&view);
}
return ret;
}

hashlib.new('sha3_256', b'data') looks up 'sha3_256' in OpenSSL's registry. usedforsecurity=False allows MD5/SHA1 on FIPS systems (for checksums, not cryptographic use). The initial data is hashed immediately after construction.

Algorithm registry

// CPython: Modules/_hashopenssl.c:320 py_digest_by_name
static const EVP_MD *
py_digest_by_name(PyObject *module, const char *name, int usedforsecurity)
{
/* Normalize name: 'SHA256' -> 'sha256', 'sha2-256' -> 'sha256' */
char normalized[64];
_hashlib_norm_name(name, normalized);
const EVP_MD *digest = EVP_get_digestbyname(normalized);
if (digest == NULL) {
/* Try alias table: 'sha256' -> 'SHA2-256' */
const char *alias = _Py_hashlib_get_alias(normalized);
if (alias) digest = EVP_get_digestbyname(alias);
}
if (digest == NULL) {
PyErr_Format(PyExc_ValueError, "unsupported hash type %s", name);
}
return digest;
}

Name normalization handles case and separator variants (SHA-256, sha256, SHA_256 all work). The alias table maps Python names to OpenSSL names where they differ.

HMAC

// CPython: Modules/_hashopenssl.c:1060 _hashlib_hmac_new_impl
static PyObject *
_hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj,
const char *digestmod)
{
const EVP_MD *digest = py_digest_by_name(module, digestmod, 1);
HMAC_CTX *ctx = HMAC_CTX_new();
HMAC_Init_ex(ctx, key->buf, (int)key->len, digest, NULL);
if (msg_obj != NULL && msg_obj != Py_None) {
Py_buffer msg;
PyObject_GetBuffer(msg_obj, &msg, PyBUF_SIMPLE);
HMAC_Update(ctx, msg.buf, msg.len);
PyBuffer_Release(&msg);
}
return HMACobject_new(module, ctx);
}

hmac.new(key, msg, digestmod='sha256') creates an HMAC_CTX backed by OpenSSL. The HMAC_CTX is copied on copy() to allow independent digests from the same key/state.

hashlib.pbkdf2_hmac

// CPython: Modules/_hashopenssl.c:1360 _hashlib_pbkdf2_hmac_impl
static PyObject *
_hashlib_pbkdf2_hmac_impl(PyObject *module, const char *hash_name,
Py_buffer *password, Py_buffer *salt,
long iterations, PyObject *dklen_obj)
{
const EVP_MD *digest = py_digest_by_name(module, hash_name, 1);
Py_ssize_t dklen = digest_len;
PyObject *key = PyBytes_FromStringAndSize(NULL, dklen);
Py_BEGIN_ALLOW_THREADS
int r = PKCS5_PBKDF2_HMAC(
password->buf, (int)password->len,
salt->buf, (int)salt->len,
iterations, digest, (int)dklen,
(unsigned char *)PyBytes_AS_STRING(key));
Py_END_ALLOW_THREADS
if (!r) { Py_DECREF(key); return _setException(PyExc_ValueError, ...); }
return key;
}

hashlib.pbkdf2_hmac('sha256', password, salt, 100000) releases the GIL during the CPU-intensive key derivation. The output length defaults to the digest size but can be overridden with dklen.

gopy notes

hashlib.new is module/hashlib.New in module/hashlib/module.go. The algorithm registry uses Go's crypto standard library; sha256, sha512, md5, sha1 are natively available. HMAC is crypto/hmac. pbkdf2_hmac uses golang.org/x/crypto/pbkdf2. BLAKE2 uses golang.org/x/crypto/blake2b and blake2s.