summaryrefslogtreecommitdiffstats
path: root/Modules/_hashopenssl.c
diff options
context:
space:
mode:
Diffstat (limited to 'Modules/_hashopenssl.c')
-rw-r--r--Modules/_hashopenssl.c410
1 files changed, 401 insertions, 9 deletions
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
index 936b515..292e92a 100644
--- a/Modules/_hashopenssl.c
+++ b/Modules/_hashopenssl.c
@@ -35,6 +35,32 @@
/* OpenSSL < 1.1.0 */
#define EVP_MD_CTX_new EVP_MD_CTX_create
#define EVP_MD_CTX_free EVP_MD_CTX_destroy
+
+HMAC_CTX *
+HMAC_CTX_new(void)
+{
+ HMAC_CTX *ctx = OPENSSL_malloc(sizeof(HMAC_CTX));
+ if (ctx != NULL) {
+ memset(ctx, 0, sizeof(HMAC_CTX));
+ HMAC_CTX_init(ctx);
+ }
+ return ctx;
+}
+
+void
+HMAC_CTX_free(HMAC_CTX *ctx)
+{
+ if (ctx != NULL) {
+ HMAC_CTX_cleanup(ctx);
+ OPENSSL_free(ctx);
+ }
+}
+
+const EVP_MD *
+HMAC_CTX_get_md(const HMAC_CTX *ctx)
+{
+ return ctx->md;
+}
#endif
#define MUNCH_SIZE INT_MAX
@@ -55,6 +81,7 @@ static PyModuleDef _hashlibmodule;
typedef struct {
PyTypeObject *EVPtype;
+ PyTypeObject *HMACtype;
#ifdef PY_OPENSSL_HAS_SHAKE
PyTypeObject *EVPXOFtype;
#endif
@@ -77,14 +104,20 @@ typedef struct {
PyThread_type_lock lock; /* OpenSSL context lock */
} EVPobject;
+typedef struct {
+ PyObject_HEAD
+ HMAC_CTX *ctx; /* OpenSSL hmac context */
+ PyThread_type_lock lock; /* HMAC context lock */
+} HMACobject;
#include "clinic/_hashopenssl.c.h"
/*[clinic input]
module _hashlib
class _hashlib.HASH "EVPobject *" "((_hashlibstate *)PyModule_GetState(module))->EVPtype"
class _hashlib.HASHXOF "EVPobject *" "((_hashlibstate *)PyModule_GetState(module))->EVPXOFtype"
+class _hashlib.HMAC "HMACobject *" "((_hashlibstate *)PyModule_GetState(module))->HMACtype"
[clinic start generated code]*/
-/*[clinic end generated code: output=da39a3ee5e6b4b0d input=813acc7b2d8f322c]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=7df1bcf6f75cb8ef]*/
/* LCOV_EXCL_START */
@@ -1091,7 +1124,7 @@ pbkdf2_hmac_impl(PyObject *module, const char *hash_name,
int retval;
const EVP_MD *digest;
- digest = EVP_get_digestbyname(hash_name);
+ digest = py_digest_by_name(hash_name);
if (digest == NULL) {
PyErr_SetString(PyExc_ValueError, "unsupported hash type");
goto end;
@@ -1293,7 +1326,7 @@ _hashlib_scrypt_impl(PyObject *module, Py_buffer *password, Py_buffer *salt,
*/
/*[clinic input]
-_hashlib.hmac_digest
+_hashlib.hmac_digest as _hashlib_hmac_singleshot
key: Py_buffer
msg: Py_buffer
@@ -1303,16 +1336,16 @@ Single-shot HMAC.
[clinic start generated code]*/
static PyObject *
-_hashlib_hmac_digest_impl(PyObject *module, Py_buffer *key, Py_buffer *msg,
- const char *digest)
-/*[clinic end generated code: output=75630e684cdd8762 input=562d2f4249511bd3]*/
+_hashlib_hmac_singleshot_impl(PyObject *module, Py_buffer *key,
+ Py_buffer *msg, const char *digest)
+/*[clinic end generated code: output=15658ede5ab98185 input=019dffc571909a46]*/
{
unsigned char md[EVP_MAX_MD_SIZE] = {0};
unsigned int md_len = 0;
unsigned char *result;
const EVP_MD *evp;
- evp = EVP_get_digestbyname(digest);
+ evp = py_digest_by_name(digest);
if (evp == NULL) {
PyErr_SetString(PyExc_ValueError, "unsupported hash type");
return NULL;
@@ -1344,6 +1377,354 @@ _hashlib_hmac_digest_impl(PyObject *module, Py_buffer *key, Py_buffer *msg,
return PyBytes_FromStringAndSize((const char*)md, md_len);
}
+/* OpenSSL-based HMAC implementation
+ */
+
+static int _hmac_update(HMACobject*, PyObject*);
+
+/*[clinic input]
+_hashlib.hmac_new
+
+ key: Py_buffer
+ msg as msg_obj: object(c_default="NULL") = b''
+ digestmod: str(c_default="NULL") = None
+
+Return a new hmac object.
+[clinic start generated code]*/
+
+static PyObject *
+_hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj,
+ const char *digestmod)
+/*[clinic end generated code: output=9a35673be0cbea1b input=a0878868eb190134]*/
+{
+ PyTypeObject *type = get_hashlib_state(module)->HMACtype;
+ const EVP_MD *digest;
+ HMAC_CTX *ctx = NULL;
+ HMACobject *self = NULL;
+ int r;
+
+ if ((digestmod == NULL) || !strlen(digestmod)) {
+ PyErr_SetString(
+ PyExc_TypeError, "Missing required parameter 'digestmod'.");
+ return NULL;
+ }
+
+ digest = py_digest_by_name(digestmod);
+ if (!digest) {
+ PyErr_SetString(PyExc_ValueError, "unknown hash function");
+ return NULL;
+ }
+
+ ctx = HMAC_CTX_new();
+ if (ctx == NULL) {
+ _setException(PyExc_ValueError);
+ goto error;
+ }
+
+ r = HMAC_Init_ex(
+ ctx,
+ (const char*)key->buf,
+ key->len,
+ digest,
+ NULL /*impl*/);
+ if (r == 0) {
+ _setException(PyExc_ValueError);
+ goto error;
+ }
+
+ self = (HMACobject *)PyObject_New(HMACobject, type);
+ if (self == NULL) {
+ goto error;
+ }
+
+ self->ctx = ctx;
+ self->lock = NULL;
+
+ if ((msg_obj != NULL) && (msg_obj != Py_None)) {
+ if (!_hmac_update(self, msg_obj))
+ goto error;
+ }
+
+ return (PyObject*)self;
+
+error:
+ if (ctx) HMAC_CTX_free(ctx);
+ if (self) PyObject_Del(self);
+ return NULL;
+}
+
+/* helper functions */
+static int
+locked_HMAC_CTX_copy(HMAC_CTX *new_ctx_p, HMACobject *self)
+{
+ int result;
+ ENTER_HASHLIB(self);
+ result = HMAC_CTX_copy(new_ctx_p, self->ctx);
+ LEAVE_HASHLIB(self);
+ return result;
+}
+
+static unsigned int
+_hmac_digest_size(HMACobject *self)
+{
+ unsigned int digest_size = EVP_MD_size(HMAC_CTX_get_md(self->ctx));
+ assert(digest_size <= EVP_MAX_MD_SIZE);
+ return digest_size;
+}
+
+static int
+_hmac_update(HMACobject *self, PyObject *obj)
+{
+ int r;
+ Py_buffer view = {0};
+
+ GET_BUFFER_VIEW_OR_ERROR(obj, &view, return 0);
+
+ if (self->lock == NULL && view.len >= HASHLIB_GIL_MINSIZE) {
+ self->lock = PyThread_allocate_lock();
+ /* fail? lock = NULL and we fail over to non-threaded code. */
+ }
+
+ if (self->lock != NULL) {
+ ENTER_HASHLIB(self);
+ r = HMAC_Update(self->ctx, (const unsigned char*)view.buf, view.len);
+ LEAVE_HASHLIB(self);
+ } else {
+ r = HMAC_Update(self->ctx, (const unsigned char*)view.buf, view.len);
+ }
+
+ PyBuffer_Release(&view);
+
+ if (r == 0) {
+ _setException(PyExc_ValueError);
+ return 0;
+ }
+ return 1;
+}
+
+/*[clinic input]
+_hashlib.HMAC.copy
+
+Return a copy ("clone") of the HMAC object.
+[clinic start generated code]*/
+
+static PyObject *
+_hashlib_HMAC_copy_impl(HMACobject *self)
+/*[clinic end generated code: output=29aa28b452833127 input=e2fa6a05db61a4d6]*/
+{
+ HMACobject *retval;
+
+ HMAC_CTX *ctx = HMAC_CTX_new();
+ if (ctx == NULL) {
+ return _setException(PyExc_ValueError);
+ }
+ if (!locked_HMAC_CTX_copy(ctx, self)) {
+ HMAC_CTX_free(ctx);
+ return _setException(PyExc_ValueError);
+ }
+
+ retval = (HMACobject *)PyObject_New(HMACobject, Py_TYPE(self));
+ if (retval == NULL) {
+ HMAC_CTX_free(ctx);
+ return NULL;
+ }
+ retval->ctx = ctx;
+ retval->lock = NULL;
+
+ return (PyObject *)retval;
+}
+
+static void
+_hmac_dealloc(HMACobject *self)
+{
+ PyTypeObject *tp = Py_TYPE(self);
+ if (self->lock != NULL) {
+ PyThread_free_lock(self->lock);
+ }
+ HMAC_CTX_free(self->ctx);
+ PyObject_Del(self);
+ Py_DECREF(tp);
+}
+
+static PyObject *
+_hmac_repr(HMACobject *self)
+{
+ PyObject *digest_name = py_digest_name(HMAC_CTX_get_md(self->ctx));
+ if (digest_name == NULL) {
+ return NULL;
+ }
+ PyObject *repr = PyUnicode_FromFormat(
+ "<%U HMAC object @ %p>", digest_name, self
+ );
+ Py_DECREF(digest_name);
+ return repr;
+}
+
+/*[clinic input]
+_hashlib.HMAC.update
+ msg: object
+
+Update the HMAC object with msg.
+[clinic start generated code]*/
+
+static PyObject *
+_hashlib_HMAC_update_impl(HMACobject *self, PyObject *msg)
+/*[clinic end generated code: output=f31f0ace8c625b00 input=1829173bb3cfd4e6]*/
+{
+ if (!_hmac_update(self, msg)) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static int
+_hmac_digest(HMACobject *self, unsigned char *buf, unsigned int len)
+{
+ HMAC_CTX *temp_ctx = HMAC_CTX_new();
+ if (temp_ctx == NULL) {
+ PyErr_NoMemory();
+ return 0;
+ }
+ if (!locked_HMAC_CTX_copy(temp_ctx, self)) {
+ _setException(PyExc_ValueError);
+ return 0;
+ }
+ int r = HMAC_Final(temp_ctx, buf, &len);
+ HMAC_CTX_free(temp_ctx);
+ if (r == 0) {
+ _setException(PyExc_ValueError);
+ return 0;
+ }
+ return 1;
+}
+
+/*[clinic input]
+_hashlib.HMAC.digest
+Return the digest of the bytes passed to the update() method so far.
+[clinic start generated code]*/
+
+static PyObject *
+_hashlib_HMAC_digest_impl(HMACobject *self)
+/*[clinic end generated code: output=1b1424355af7a41e input=bff07f74da318fb4]*/
+{
+ unsigned char digest[EVP_MAX_MD_SIZE];
+ unsigned int digest_size = _hmac_digest_size(self);
+ if (digest_size == 0) {
+ return _setException(PyExc_ValueError);
+ }
+ int r = _hmac_digest(self, digest, digest_size);
+ if (r == 0) {
+ return NULL;
+ }
+ return PyBytes_FromStringAndSize((const char *)digest, digest_size);
+}
+
+/*[clinic input]
+_hashlib.HMAC.hexdigest
+
+Return hexadecimal digest of the bytes passed to the update() method so far.
+
+This may be used to exchange the value safely in email or other non-binary
+environments.
+[clinic start generated code]*/
+
+static PyObject *
+_hashlib_HMAC_hexdigest_impl(HMACobject *self)
+/*[clinic end generated code: output=80d825be1eaae6a7 input=5abc42702874ddcf]*/
+{
+ unsigned char digest[EVP_MAX_MD_SIZE];
+ unsigned int digest_size = _hmac_digest_size(self);
+ if (digest_size == 0) {
+ return _setException(PyExc_ValueError);
+ }
+ int r = _hmac_digest(self, digest, digest_size);
+ if (r == 0) {
+ return NULL;
+ }
+ return _Py_strhex((const char *)digest, digest_size);
+}
+
+static PyObject *
+_hashlib_hmac_get_digest_size(HMACobject *self, void *closure)
+{
+ unsigned int digest_size = _hmac_digest_size(self);
+ if (digest_size == 0) {
+ return _setException(PyExc_ValueError);
+ }
+ return PyLong_FromLong(digest_size);
+}
+
+static PyObject *
+_hashlib_hmac_get_block_size(HMACobject *self, void *closure)
+{
+ const EVP_MD *md = HMAC_CTX_get_md(self->ctx);
+ if (md == NULL) {
+ return _setException(PyExc_ValueError);
+ }
+ return PyLong_FromLong(EVP_MD_block_size(md));
+}
+
+static PyObject *
+_hashlib_hmac_get_name(HMACobject *self, void *closure)
+{
+ PyObject *digest_name = py_digest_name(HMAC_CTX_get_md(self->ctx));
+ if (digest_name == NULL) {
+ return NULL;
+ }
+ PyObject *name = PyUnicode_FromFormat("hmac-%U", digest_name);
+ Py_DECREF(digest_name);
+ return name;
+}
+
+static PyMethodDef HMAC_methods[] = {
+ _HASHLIB_HMAC_UPDATE_METHODDEF
+ _HASHLIB_HMAC_DIGEST_METHODDEF
+ _HASHLIB_HMAC_HEXDIGEST_METHODDEF
+ _HASHLIB_HMAC_COPY_METHODDEF
+ {NULL, NULL} /* sentinel */
+};
+
+static PyGetSetDef HMAC_getset[] = {
+ {"digest_size", (getter)_hashlib_hmac_get_digest_size, NULL, NULL, NULL},
+ {"block_size", (getter)_hashlib_hmac_get_block_size, NULL, NULL, NULL},
+ {"name", (getter)_hashlib_hmac_get_name, NULL, NULL, NULL},
+ {NULL} /* Sentinel */
+};
+
+
+PyDoc_STRVAR(hmactype_doc,
+"The object used to calculate HMAC of a message.\n\
+\n\
+Methods:\n\
+\n\
+update() -- updates the current digest with an additional string\n\
+digest() -- return the current digest value\n\
+hexdigest() -- return the current digest as a string of hexadecimal digits\n\
+copy() -- return a copy of the current hash object\n\
+\n\
+Attributes:\n\
+\n\
+name -- the name, including the hash algorithm used by this object\n\
+digest_size -- number of bytes in digest() output\n");
+
+static PyType_Slot HMACtype_slots[] = {
+ {Py_tp_doc, (char *)hmactype_doc},
+ {Py_tp_repr, (reprfunc)_hmac_repr},
+ {Py_tp_dealloc,(destructor)_hmac_dealloc},
+ {Py_tp_methods, HMAC_methods},
+ {Py_tp_getset, HMAC_getset},
+ {Py_tp_new, _disabled_new},
+ {0, NULL}
+};
+
+PyType_Spec HMACtype_spec = {
+ "_hashlib.HMAC", /* name */
+ sizeof(HMACobject), /* basicsize */
+ .flags = Py_TPFLAGS_DEFAULT,
+ .slots = HMACtype_slots,
+};
+
+
/* State for our callback function so that it can accumulate a result. */
typedef struct _internal_name_mapper_state {
PyObject *set;
@@ -1448,7 +1829,8 @@ static struct PyMethodDef EVP_functions[] = {
PBKDF2_HMAC_METHODDEF
_HASHLIB_SCRYPT_METHODDEF
_HASHLIB_GET_FIPS_MODE_METHODDEF
- _HASHLIB_HMAC_DIGEST_METHODDEF
+ _HASHLIB_HMAC_SINGLESHOT_METHODDEF
+ _HASHLIB_HMAC_NEW_METHODDEF
_HASHLIB_OPENSSL_MD5_METHODDEF
_HASHLIB_OPENSSL_SHA1_METHODDEF
_HASHLIB_OPENSSL_SHA224_METHODDEF
@@ -1472,6 +1854,7 @@ hashlib_traverse(PyObject *m, visitproc visit, void *arg)
{
_hashlibstate *state = get_hashlib_state(m);
Py_VISIT(state->EVPtype);
+ Py_VISIT(state->HMACtype);
#ifdef PY_OPENSSL_HAS_SHAKE
Py_VISIT(state->EVPXOFtype);
#endif
@@ -1483,6 +1866,7 @@ hashlib_clear(PyObject *m)
{
_hashlibstate *state = get_hashlib_state(m);
Py_CLEAR(state->EVPtype);
+ Py_CLEAR(state->HMACtype);
#ifdef PY_OPENSSL_HAS_SHAKE
Py_CLEAR(state->EVPXOFtype);
#endif
@@ -1541,10 +1925,18 @@ PyInit__hashlib(void)
return NULL;
}
state->EVPtype = EVPtype;
-
Py_INCREF((PyObject *)state->EVPtype);
PyModule_AddObject(m, "HASH", (PyObject *)state->EVPtype);
+ PyTypeObject *HMACtype = (PyTypeObject *)PyType_FromSpec(&HMACtype_spec);
+ if (HMACtype == NULL) {
+ Py_DECREF(m);
+ return NULL;
+ }
+ state->HMACtype = HMACtype;
+ Py_INCREF((PyObject *)state->HMACtype);
+ PyModule_AddObject(m, "HMAC", (PyObject *)state->HMACtype);
+
#ifdef PY_OPENSSL_HAS_SHAKE
bases = PyTuple_Pack(1, (PyObject *)EVPtype);
if (bases == NULL) {