Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:mcepl:branches:devel:languages:python:Factory
python36
openssl-300-hashlib-usedforsecurity.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File openssl-300-hashlib-usedforsecurity.patch of Package python36
From b6b17e3c6a80f0bba74639f49d368e4f86182d71 Mon Sep 17 00:00:00 2001 From: Christian Heimes <christian@python.org> Date: Thu, 13 Jan 2022 12:20:45 +0200 Subject: [PATCH] OpenSSL 3.0.0: Fix hashlib's usedforsecurity Fix hashlib usedforsecurity option to work correctly with OpenSSL 3.0.0 in FIPS mode. Co-authored-by: Christian Heimes <christian@python.org> Fixes: bpo-40479 From-PR: gh#python/cpython!30455 Patch: openssl-300-hashlib-usedforsecurity.patch Released-in: 3.9.10 --- Doc/library/hashlib.rst | 4 +- Lib/test/test_hashlib.py | 53 +- Lib/test/test_imaplib.py | 3 + Lib/test/test_poplib.py | 2 + Lib/test/test_smtplib.py | 40 + Lib/test/test_tools/test_md5sum.py | 1 + Lib/test/test_urllib2_localnet.py | 2 +- .../2022-01-07-15-20-19.bpo-40479.EKfr3F.rst | 2 + Modules/_hashopenssl.c | 1544 ++++++++++++++--- 9 files changed, 1429 insertions(+), 222 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-01-07-15-20-19.bpo-40479.EKfr3F.rst diff --git a/Doc/library/hashlib.rst b/Doc/library/hashlib.rst index d78f9f48d1b..322afdd06f5 100644 --- a/Doc/library/hashlib.rst +++ b/Doc/library/hashlib.rst @@ -109,10 +109,10 @@ More condensed: Using :func:`new` with an algorithm provided by OpenSSL: - >>> h = hashlib.new('sha512_256') + >>> h = hashlib.new('sha256') >>> h.update(b"Nobody inspects the spammish repetition") >>> h.hexdigest() - '19197dc4d03829df858011c6c87600f994a858103bbc19005f20987aa19a97e2' + '031edd7d41651593c5fe5c006fa5752b37fddff7bc4e843aa6af0c950f4b9406' Hashlib provides the following constant attributes: diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py index 08f0af37489..023f6397b47 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -13,6 +13,7 @@ import importlib import itertools import os import sys +import sysconfig try: import threading except ImportError: @@ -26,8 +27,35 @@ from http.client import HTTPException # Were we compiled --with-pydebug or with #define Py_DEBUG? COMPILED_WITH_PYDEBUG = hasattr(sys, 'gettotalrefcount') -c_hashlib = import_fresh_module('hashlib', fresh=['_hashlib']) -py_hashlib = import_fresh_module('hashlib', blocked=['_hashlib']) +# default builtin hash module +default_builtin_hashes = {'md5', 'sha1', 'sha256', 'sha512', 'sha3', 'blake2'} +# --with-builtin-hashlib-hashes override +builtin_hashes = sysconfig.get_config_var("PY_BUILTIN_HASHLIB_HASHES") +if builtin_hashes is None: + builtin_hashes = default_builtin_hashes +else: + builtin_hashes = { + m.strip() for m in builtin_hashes.strip('"').lower().split(",") + } + +# hashlib with and without OpenSSL backend for PBKDF2 +# only import builtin_hashlib when all builtin hashes are available. +# Otherwise import prints noise on stderr +openssl_hashlib = import_fresh_module('hashlib', fresh=['_hashlib']) +if builtin_hashes == default_builtin_hashes: + builtin_hashlib = import_fresh_module('hashlib', blocked=['_hashlib']) +else: + builtin_hashlib = None + +try: + from _hashlib import HASH, HASHXOF, openssl_md_meth_names, get_fips_mode +except ImportError: + HASH = None + HASHXOF = None + openssl_md_meth_names = frozenset() + + def get_fips_mode(): + return 0 try: import _blake2 @@ -165,15 +193,9 @@ class HashLibTestCase(unittest.TestCase): constructors = self.constructors_to_test.values() return itertools.chain.from_iterable(constructors) - @support.refcount_test - @unittest.skipIf(c_hashlib is None, 'Require _hashlib module') - def test_refleaks_in_hash___init__(self): - gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount') - sha1_hash = c_hashlib.new('sha1') - refs_before = gettotalrefcount() - for i in range(100): - sha1_hash.__init__('sha1') - self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10) + @property + def is_fips_mode(self): + return get_fips_mode() def test_hash_array(self): a = array.array("b", range(10)) @@ -927,13 +949,7 @@ class KDFTests(unittest.TestCase): self.assertEqual(out, expected, (digest_name, password, salt, rounds)) - self.assertRaises(TypeError, pbkdf2, b'sha1', b'pass', b'salt', 1) - self.assertRaises(TypeError, pbkdf2, 'sha1', 'pass', 'salt', 1) - self.assertRaises(ValueError, pbkdf2, 'sha1', b'pass', b'salt', 0) - self.assertRaises(ValueError, pbkdf2, 'sha1', b'pass', b'salt', -1) - self.assertRaises(ValueError, pbkdf2, 'sha1', b'pass', b'salt', 1, 0) - self.assertRaises(ValueError, pbkdf2, 'sha1', b'pass', b'salt', 1, -1) - with self.assertRaisesRegex(ValueError, 'unsupported hash type'): + with self.assertRaisesRegex(ValueError, '.*unsupported.*'): pbkdf2('unknown', b'pass', b'salt', 1) out = pbkdf2(hash_name='sha1', password=b'password', salt=b'salt', iterations=1, dklen=None) @@ -950,6 +966,7 @@ class KDFTests(unittest.TestCase): @unittest.skipUnless(hasattr(c_hashlib, 'scrypt'), ' test requires OpenSSL > 1.1') + @unittest.skipIf(get_fips_mode(), reason="scrypt is blocked in FIPS mode") def test_scrypt(self): for password, salt, n, r, p, expected in self.scrypt_test_vectors: result = hashlib.scrypt(password, salt=salt, n=n, r=r, p=p) diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py index 0593a3756b0..b5b5223e17e 100644 --- a/Lib/test/test_imaplib.py +++ b/Lib/test/test_imaplib.py @@ -369,6 +369,7 @@ class NewIMAPTestsMixin(): self.assertEqual(code, 'OK') self.assertEqual(server.response, b'ZmFrZQ==\r\n') # b64 encoded 'fake' + @hashlib_helper.requires_hashdigest('md5', openssl=True) def test_login_cram_md5_bytes(self): class AuthHandler(SimpleIMAPHandler): capabilities = 'LOGINDISABLED AUTH=CRAM-MD5' @@ -386,6 +387,7 @@ class NewIMAPTestsMixin(): ret, _ = client.login_cram_md5("tim", b"tanstaaftanstaaf") self.assertEqual(ret, "OK") + @hashlib_helper.requires_hashdigest('md5', openssl=True) def test_login_cram_md5_plain_text(self): class AuthHandler(SimpleIMAPHandler): capabilities = 'LOGINDISABLED AUTH=CRAM-MD5' @@ -797,6 +799,7 @@ class ThreadedNetworkedTests(unittest.TestCase): b'ZmFrZQ==\r\n') # b64 encoded 'fake' @reap_threads + @hashlib_helper.requires_hashdigest('md5', openssl=True) def test_login_cram_md5(self): class AuthHandler(SimpleIMAPHandler): diff --git a/Lib/test/test_poplib.py b/Lib/test/test_poplib.py index 234c855545c..f30ed2e494f 100644 --- a/Lib/test/test_poplib.py +++ b/Lib/test/test_poplib.py @@ -304,9 +304,11 @@ class TestPOP3Class(TestCase): def test_rpop(self): self.assertOK(self.client.rpop('foo')) + @hashlib_helper.requires_hashdigest('md5', openssl=True) def test_apop_normal(self): self.assertOK(self.client.apop('foo', 'dummypassword')) + @hashlib_helper.requires_hashdigest('md5', openssl=True) def test_apop_REDOS(self): # Replace welcome with very long evil welcome. # NB The upper bound on welcome length is currently 2048. diff --git a/Lib/test/test_smtplib.py b/Lib/test/test_smtplib.py index 121bbcc9dbd..3c6627461d4 100644 --- a/Lib/test/test_smtplib.py +++ b/Lib/test/test_smtplib.py @@ -1023,6 +1023,45 @@ class SMTPSimTests(unittest.TestCase): self.assertEqual(resp, (235, b'Authentication Succeeded')) smtp.close() + def testAUTH_LOGIN_initial_response_ok(self): + self.serv.add_feature("AUTH LOGIN") + with smtplib.SMTP(HOST, self.port, local_hostname='localhost', + timeout=support.LOOPBACK_TIMEOUT) as smtp: + smtp.user, smtp.password = sim_auth + smtp.ehlo("test_auth_login") + resp = smtp.auth("LOGIN", smtp.auth_login, initial_response_ok=True) + self.assertEqual(resp, (235, b'Authentication Succeeded')) + + def testAUTH_LOGIN_initial_response_notok(self): + self.serv.add_feature("AUTH LOGIN") + with smtplib.SMTP(HOST, self.port, local_hostname='localhost', + timeout=support.LOOPBACK_TIMEOUT) as smtp: + smtp.user, smtp.password = sim_auth + smtp.ehlo("test_auth_login") + resp = smtp.auth("LOGIN", smtp.auth_login, initial_response_ok=False) + self.assertEqual(resp, (235, b'Authentication Succeeded')) + + def testAUTH_BUGGY(self): + self.serv.add_feature("AUTH BUGGY") + + def auth_buggy(challenge=None): + self.assertEqual(b"BuGgYbUgGy", challenge) + return "\0" + + smtp = smtplib.SMTP( + HOST, self.port, local_hostname='localhost', + timeout=support.LOOPBACK_TIMEOUT + ) + try: + smtp.user, smtp.password = sim_auth + smtp.ehlo("test_auth_buggy") + expect = r"^Server AUTH mechanism infinite loop.*" + with self.assertRaisesRegex(smtplib.SMTPException, expect) as cm: + smtp.auth("BUGGY", auth_buggy, initial_response_ok=False) + finally: + smtp.close() + + @hashlib_helper.requires_hashdigest('md5', openssl=True) def testAUTH_CRAM_MD5(self): self.serv.add_feature("AUTH CRAM-MD5") smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=15) @@ -1030,6 +1069,7 @@ class SMTPSimTests(unittest.TestCase): self.assertEqual(resp, (235, b'Authentication Succeeded')) smtp.close() + @hashlib_helper.requires_hashdigest('md5', openssl=True) def testAUTH_multiple(self): # Test that multiple authentication methods are tried. self.serv.add_feature("AUTH BOGUS PLAIN LOGIN CRAM-MD5") diff --git a/Lib/test/test_tools/test_md5sum.py b/Lib/test/test_tools/test_md5sum.py index fb565b73778..ce8656b772f 100644 --- a/Lib/test/test_tools/test_md5sum.py +++ b/Lib/test/test_tools/test_md5sum.py @@ -9,6 +9,7 @@ from test.test_tools import scriptsdir, skip_if_missing skip_if_missing() +@hashlib_helper.requires_hashdigest('md5', openssl=True) class MD5SumTests(unittest.TestCase): @classmethod def setUpClass(cls): diff --git a/Lib/test/test_urllib2_localnet.py b/Lib/test/test_urllib2_localnet.py index ef0091c4930..33eb5538368 100644 --- a/Lib/test/test_urllib2_localnet.py +++ b/Lib/test/test_urllib2_localnet.py @@ -317,7 +317,7 @@ class BasicAuthTests(unittest.TestCase): self.assertRaises(urllib.error.HTTPError, urllib.request.urlopen, self.server_url) -@unittest.skipUnless(threading, "Threading required for this test.") +@hashlib_helper.requires_hashdigest("md5", openssl=True) class ProxyAuthTests(unittest.TestCase): URL = "http://localhost" diff --git a/Misc/NEWS.d/next/Library/2022-01-07-15-20-19.bpo-40479.EKfr3F.rst b/Misc/NEWS.d/next/Library/2022-01-07-15-20-19.bpo-40479.EKfr3F.rst new file mode 100644 index 00000000000..af72923bbd7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-01-07-15-20-19.bpo-40479.EKfr3F.rst @@ -0,0 +1,2 @@ +Fix :mod:`hashlib` *usedforsecurity* option to work correctly with OpenSSL +3.0.0 in FIPS mode. diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c index 957e80625c3..bf2ac8da083 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c @@ -11,10 +11,21 @@ * */ +/* Don't warn about deprecated functions, */ +#ifndef OPENSSL_API_COMPAT + // 0x10101000L == 1.1.1, 30000 == 3.0.0 + #define OPENSSL_API_COMPAT 0x10101000L +#endif +#define OPENSSL_NO_DEPRECATED 1 + +#ifndef Py_BUILD_CORE_BUILTIN +# define Py_BUILD_CORE_MODULE 1 +#endif + #define PY_SSIZE_T_CLEAN #include "Python.h" -#include "structmember.h" +#include "hashtable.h" #include "hashlib.h" #include "pystrhex.h" @@ -48,6 +59,200 @@ module _hashlib #define HAS_FAST_PKCS5_PBKDF2_HMAC 1 #endif +#define MUNCH_SIZE INT_MAX + +#ifdef NID_sha3_224 +#define PY_OPENSSL_HAS_SHA3 1 +#endif + +#if defined(EVP_MD_FLAG_XOF) && defined(NID_shake128) +#define PY_OPENSSL_HAS_SHAKE 1 +#endif + +#if defined(NID_blake2b512) && !defined(OPENSSL_NO_BLAKE2) +#define PY_OPENSSL_HAS_BLAKE2 1 +#endif + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#define PY_EVP_MD EVP_MD +#define PY_EVP_MD_fetch(algorithm, properties) EVP_MD_fetch(NULL, algorithm, properties) +#define PY_EVP_MD_up_ref(md) EVP_MD_up_ref(md) +#define PY_EVP_MD_free(md) EVP_MD_free(md) +#else +#define PY_EVP_MD const EVP_MD +#define PY_EVP_MD_fetch(algorithm, properties) EVP_get_digestbyname(algorithm) +#define PY_EVP_MD_up_ref(md) do {} while(0) +#define PY_EVP_MD_free(md) do {} while(0) +#endif + +/* hash alias map and fast lookup + * + * Map between Python's preferred names and OpenSSL internal names. Maintain + * cache of fetched EVP MD objects. The EVP_get_digestbyname() and + * EVP_MD_fetch() API calls have a performance impact. + * + * The py_hashentry_t items are stored in a _Py_hashtable_t with py_name and + * py_alias as keys. + */ + +enum Py_hash_type { + Py_ht_evp, // usedforsecurity=True / default + Py_ht_evp_nosecurity, // usedforsecurity=False + Py_ht_mac, // HMAC + Py_ht_pbkdf2, // PKBDF2 +}; + +typedef struct { + const char *py_name; + const char *py_alias; + const char *ossl_name; + int ossl_nid; + int refcnt; + PY_EVP_MD *evp; + PY_EVP_MD *evp_nosecurity; +} py_hashentry_t; + +#define Py_hash_md5 "md5" +#define Py_hash_sha1 "sha1" +#define Py_hash_sha224 "sha224" +#define Py_hash_sha256 "sha256" +#define Py_hash_sha384 "sha384" +#define Py_hash_sha512 "sha512" +#define Py_hash_sha512_224 "sha512_224" +#define Py_hash_sha512_256 "sha512_256" +#define Py_hash_sha3_224 "sha3_224" +#define Py_hash_sha3_256 "sha3_256" +#define Py_hash_sha3_384 "sha3_384" +#define Py_hash_sha3_512 "sha3_512" +#define Py_hash_shake_128 "shake_128" +#define Py_hash_shake_256 "shake_256" +#define Py_hash_blake2s "blake2s" +#define Py_hash_blake2b "blake2b" + +#define PY_HASH_ENTRY(py_name, py_alias, ossl_name, ossl_nid) \ + {py_name, py_alias, ossl_name, ossl_nid, 0, NULL, NULL} + +static const py_hashentry_t py_hashes[] = { + /* md5 */ + PY_HASH_ENTRY(Py_hash_md5, "MD5", SN_md5, NID_md5), + /* sha1 */ + PY_HASH_ENTRY(Py_hash_sha1, "SHA1", SN_sha1, NID_sha1), + /* sha2 family */ + PY_HASH_ENTRY(Py_hash_sha224, "SHA224", SN_sha224, NID_sha224), + PY_HASH_ENTRY(Py_hash_sha256, "SHA256", SN_sha256, NID_sha256), + PY_HASH_ENTRY(Py_hash_sha384, "SHA384", SN_sha384, NID_sha384), + PY_HASH_ENTRY(Py_hash_sha512, "SHA512", SN_sha512, NID_sha512), + /* truncated sha2 */ +#ifdef NID_sha512_224 + PY_HASH_ENTRY(Py_hash_sha512_224, "SHA512_224", SN_sha512_224, NID_sha512_224), + PY_HASH_ENTRY(Py_hash_sha512_256, "SHA512_256", SN_sha512_256, NID_sha512_256), +#endif + /* sha3 */ +#ifdef PY_OPENSSL_HAS_SHA3 + PY_HASH_ENTRY(Py_hash_sha3_224, NULL, SN_sha3_224, NID_sha3_224), + PY_HASH_ENTRY(Py_hash_sha3_256, NULL, SN_sha3_256, NID_sha3_256), + PY_HASH_ENTRY(Py_hash_sha3_384, NULL, SN_sha3_384, NID_sha3_384), + PY_HASH_ENTRY(Py_hash_sha3_512, NULL, SN_sha3_512, NID_sha3_512), +#endif + /* sha3 shake */ +#ifdef PY_OPENSSL_HAS_SHAKE + PY_HASH_ENTRY(Py_hash_shake_128, NULL, SN_shake128, NID_shake128), + PY_HASH_ENTRY(Py_hash_shake_256, NULL, SN_shake256, NID_shake256), +#endif + /* blake2 digest */ +#ifdef PY_OPENSSL_HAS_BLAKE2 + PY_HASH_ENTRY(Py_hash_blake2s, "blake2s256", SN_blake2s256, NID_blake2s256), + PY_HASH_ENTRY(Py_hash_blake2b, "blake2b512", SN_blake2b512, NID_blake2b512), +#endif + PY_HASH_ENTRY(NULL, NULL, NULL, 0), +}; + +static Py_uhash_t +py_hashentry_t_hash_name(const void *key) { + return _Py_HashBytes(key, strlen((const char *)key)); +} + +static int +py_hashentry_t_compare_name(const void *key1, const void *key2) { + return strcmp((const char *)key1, (const char *)key2) == 0; +} + +static void +py_hashentry_t_destroy_value(void *entry) { + py_hashentry_t *h = (py_hashentry_t *)entry; + if (--(h->refcnt) == 0) { + if (h->evp != NULL) { + PY_EVP_MD_free(h->evp); + h->evp = NULL; + } + if (h->evp_nosecurity != NULL) { + PY_EVP_MD_free(h->evp_nosecurity); + h->evp_nosecurity = NULL; + } + PyMem_Free(entry); + } +} + +static _Py_hashtable_t * +py_hashentry_table_new(void) { + _Py_hashtable_t *ht = _Py_hashtable_new_full( + py_hashentry_t_hash_name, + py_hashentry_t_compare_name, + NULL, + py_hashentry_t_destroy_value, + NULL + ); + if (ht == NULL) { + return NULL; + } + + for (const py_hashentry_t *h = py_hashes; h->py_name != NULL; h++) { + py_hashentry_t *entry = (py_hashentry_t *)PyMem_Malloc(sizeof(py_hashentry_t)); + if (entry == NULL) { + goto error; + } + memcpy(entry, h, sizeof(py_hashentry_t)); + + if (_Py_hashtable_set(ht, (const void*)entry->py_name, (void*)entry) < 0) { + PyMem_Free(entry); + goto error; + } + entry->refcnt = 1; + + if (h->py_alias != NULL) { + if (_Py_hashtable_set(ht, (const void*)entry->py_alias, (void*)entry) < 0) { + PyMem_Free(entry); + goto error; + } + entry->refcnt++; + } + } + + return ht; + error: + _Py_hashtable_destroy(ht); + return NULL; +} + +/* Module state */ +static PyModuleDef _hashlibmodule; + +typedef struct { + PyTypeObject *EVPtype; + PyTypeObject *HMACtype; +#ifdef PY_OPENSSL_HAS_SHAKE + PyTypeObject *EVPXOFtype; +#endif + _Py_hashtable_t *hashtable; +} _hashlibstate; + +static inline _hashlibstate* +get_hashlib_state(PyObject *module) +{ + void *state = PyModule_GetState(module); + assert(state != NULL); + return (_hashlibstate *)state; +} typedef struct { PyObject_HEAD @@ -76,16 +281,27 @@ DEFINE_CONSTS_FOR_NEW(sha512) /* LCOV_EXCL_START */ static PyObject * -_setException(PyObject *exc) +_setException(PyObject *exc, const char* altmsg, ...) { - unsigned long errcode; + unsigned long errcode = ERR_peek_last_error(); const char *lib, *func, *reason; + va_list vargs; - errcode = ERR_peek_last_error(); +#ifdef HAVE_STDARG_PROTOTYPES + va_start(vargs, altmsg); +#else + va_start(vargs); +#endif if (!errcode) { - PyErr_SetString(exc, "unknown reasons"); + if (altmsg == NULL) { + PyErr_SetString(exc, "no reason supplied"); + } else { + PyErr_FormatV(exc, altmsg, vargs); + } + va_end(vargs); return NULL; } + va_end(vargs); ERR_clear_error(); lib = ERR_lib_error_string(errcode); @@ -105,6 +321,92 @@ _setException(PyObject *exc) } /* LCOV_EXCL_STOP */ +/* {Py_tp_new, NULL} doesn't block __new__ */ +static PyObject * +_disabled_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyErr_Format(PyExc_TypeError, + "cannot create '%.100s' instances", _PyType_Name(type)); + return NULL; +} + +static PyObject* +py_digest_name(const EVP_MD *md) +{ + int nid = EVP_MD_nid(md); + const char *name = NULL; + const py_hashentry_t *h; + + for (h = py_hashes; h->py_name != NULL; h++) { + if (h->ossl_nid == nid) { + name = h->py_name; + break; + } + } + if (name == NULL) { + /* Ignore aliased names and only use long, lowercase name. The aliases + * pollute the list and OpenSSL appears to have its own definition of + * alias as the resulting list still contains duplicate and alternate + * names for several algorithms. + */ + name = OBJ_nid2ln(nid); + if (name == NULL) + name = OBJ_nid2sn(nid); + } + + return PyUnicode_FromString(name); +} + +/* Get EVP_MD by HID and purpose */ +static PY_EVP_MD* +py_digest_by_name(PyObject *module, const char *name, enum Py_hash_type py_ht) +{ + PY_EVP_MD *digest = NULL; + _hashlibstate *state = get_hashlib_state(module); + py_hashentry_t *entry = (py_hashentry_t *)_Py_hashtable_get( + state->hashtable, (const void*)name + ); + + if (entry != NULL) { + switch (py_ht) { + case Py_ht_evp: + case Py_ht_mac: + case Py_ht_pbkdf2: + if (entry->evp == NULL) { + entry->evp = PY_EVP_MD_fetch(entry->ossl_name, NULL); + } + digest = entry->evp; + break; + case Py_ht_evp_nosecurity: + if (entry->evp_nosecurity == NULL) { + entry->evp_nosecurity = PY_EVP_MD_fetch(entry->ossl_name, "-fips"); + } + digest = entry->evp_nosecurity; + break; + } + if (digest != NULL) { + PY_EVP_MD_up_ref(digest); + } + } else { + // Fall back for looking up an unindexed OpenSSL specific name. + switch (py_ht) { + case Py_ht_evp: + case Py_ht_mac: + case Py_ht_pbkdf2: + digest = PY_EVP_MD_fetch(name, NULL); + break; + case Py_ht_evp_nosecurity: + digest = PY_EVP_MD_fetch(name, "-fips"); + break; + } + } + if (digest == NULL) { + _setException(PyExc_ValueError, "unsupported hash type %s", name); + return NULL; + } + return digest; +} + static EVPobject * newEVPobject(PyObject *name) { @@ -140,8 +442,8 @@ EVP_hash(EVPobject *self, const void *vp, Py_ssize_t len) else process = Py_SAFE_DOWNCAST(len, Py_ssize_t, unsigned int); if (!EVP_DigestUpdate(self->ctx, (const void*)cp, process)) { - _setException(PyExc_ValueError); - break; + _setException(PyExc_ValueError, NULL); + return -1; } len -= process; cp += process; @@ -186,7 +488,8 @@ EVP_copy(EVPobject *self, PyObject *unused) return NULL; if (!locked_EVP_MD_CTX_copy(newobj->ctx, self)) { - return _setException(PyExc_ValueError); + Py_DECREF(newobj); + return _setException(PyExc_ValueError, NULL); } return (PyObject *)newobj; } @@ -209,11 +512,11 @@ EVP_digest(EVPobject *self, PyObject *unused) } if (!locked_EVP_MD_CTX_copy(temp_ctx, self)) { - return _setException(PyExc_ValueError); + return _setException(PyExc_ValueError, NULL); } digest_size = EVP_MD_CTX_size(temp_ctx); if (!EVP_DigestFinal(temp_ctx, digest, NULL)) { - _setException(PyExc_ValueError); + _setException(PyExc_ValueError, NULL); return NULL; } @@ -240,11 +543,11 @@ EVP_hexdigest(EVPobject *self, PyObject *unused) /* Get the raw (binary) digest value */ if (!locked_EVP_MD_CTX_copy(temp_ctx, self)) { - return _setException(PyExc_ValueError); + return _setException(PyExc_ValueError, NULL); } digest_size = EVP_MD_CTX_size(temp_ctx); if (!EVP_DigestFinal(temp_ctx, digest, NULL)) { - _setException(PyExc_ValueError); + _setException(PyExc_ValueError, NULL); return NULL; } @@ -418,216 +721,262 @@ static PyTypeObject EVPtype = { "_hashlib.HASH", /*tp_name*/ sizeof(EVPobject), /*tp_basicsize*/ 0, /*tp_itemsize*/ - /* methods */ - (destructor)EVP_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_reserved*/ - (reprfunc)EVP_repr, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - hashtype_doc, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - EVP_methods, /* tp_methods */ - EVP_members, /* tp_members */ - EVP_getseters, /* tp_getset */ -#if 1 - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ -#endif -#if HASH_OBJ_CONSTRUCTOR - (initproc)EVP_tp_init, /* tp_init */ -#endif + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + EVPtype_slots }; +#ifdef PY_OPENSSL_HAS_SHAKE + +/*[clinic input] +_hashlib.HASHXOF.digest as EVPXOF_digest + + length: Py_ssize_t + +Return the digest value as a bytes object. +[clinic start generated code]*/ + static PyObject * -EVPnew(PyObject *name_obj, - const EVP_MD *digest, const EVP_MD_CTX *initial_ctx, - const unsigned char *cp, Py_ssize_t len) +EVPXOF_digest_impl(EVPobject *self, Py_ssize_t length) +/*[clinic end generated code: output=ef9320c23280efad input=816a6537cea3d1db]*/ { - EVPobject *self; + EVP_MD_CTX *temp_ctx; + PyObject *retval = PyBytes_FromStringAndSize(NULL, length); - if (!digest && !initial_ctx) { - PyErr_SetString(PyExc_ValueError, "unsupported hash type"); + if (retval == NULL) { return NULL; } - if ((self = newEVPobject(name_obj)) == NULL) + temp_ctx = EVP_MD_CTX_new(); + if (temp_ctx == NULL) { + Py_DECREF(retval); + PyErr_NoMemory(); return NULL; - - if (initial_ctx) { - EVP_MD_CTX_copy(self->ctx, initial_ctx); - } else { - if (!EVP_DigestInit(self->ctx, digest)) { - _setException(PyExc_ValueError); - Py_DECREF(self); - return NULL; - } } - if (cp && len) { - if (len >= HASHLIB_GIL_MINSIZE) { - Py_BEGIN_ALLOW_THREADS - EVP_hash(self, cp, len); - Py_END_ALLOW_THREADS - } else { - EVP_hash(self, cp, len); - } + if (!locked_EVP_MD_CTX_copy(temp_ctx, self)) { + Py_DECREF(retval); + EVP_MD_CTX_free(temp_ctx); + return _setException(PyExc_ValueError, NULL); + } + if (!EVP_DigestFinalXOF(temp_ctx, + (unsigned char*)PyBytes_AS_STRING(retval), + length)) { + Py_DECREF(retval); + EVP_MD_CTX_free(temp_ctx); + _setException(PyExc_ValueError, NULL); + return NULL; } - return (PyObject *)self; + EVP_MD_CTX_free(temp_ctx); + return retval; } +/*[clinic input] +_hashlib.HASHXOF.hexdigest as EVPXOF_hexdigest -/* The module-level function: new() */ + length: Py_ssize_t -PyDoc_STRVAR(EVP_new__doc__, -"Return a new hash object using the named algorithm.\n\ -An optional string argument may be provided and will be\n\ -automatically hashed.\n\ -\n\ -The MD5 and SHA1 algorithms are always supported.\n"); +Return the digest value as a string of hexadecimal digits. +[clinic start generated code]*/ static PyObject * -EVP_new(PyObject *self, PyObject *args, PyObject *kwdict) +EVPXOF_hexdigest_impl(EVPobject *self, Py_ssize_t length) +/*[clinic end generated code: output=eb3e6ee7788bf5b2 input=5f9d6a8f269e34df]*/ { - static char *kwlist[] = {"name", "string", NULL}; - PyObject *name_obj = NULL; - PyObject *data_obj = NULL; - Py_buffer view = { 0 }; - PyObject *ret_obj; - char *name; - const EVP_MD *digest; + unsigned char *digest; + EVP_MD_CTX *temp_ctx; + PyObject *retval; - if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O|O:new", kwlist, - &name_obj, &data_obj)) { + digest = (unsigned char*)PyMem_Malloc(length); + if (digest == NULL) { + PyErr_NoMemory(); return NULL; } - if (!PyArg_Parse(name_obj, "s", &name)) { - PyErr_SetString(PyExc_TypeError, "name must be a string"); + temp_ctx = EVP_MD_CTX_new(); + if (temp_ctx == NULL) { + PyMem_Free(digest); + PyErr_NoMemory(); return NULL; } - if (data_obj) - GET_BUFFER_VIEW_OR_ERROUT(data_obj, &view); + /* Get the raw (binary) digest value */ + if (!locked_EVP_MD_CTX_copy(temp_ctx, self)) { + PyMem_Free(digest); + EVP_MD_CTX_free(temp_ctx); + return _setException(PyExc_ValueError, NULL); + } + if (!EVP_DigestFinalXOF(temp_ctx, digest, length)) { + PyMem_Free(digest); + EVP_MD_CTX_free(temp_ctx); + _setException(PyExc_ValueError, NULL); + return NULL; + } + + EVP_MD_CTX_free(temp_ctx); + + retval = _Py_strhex((const char *)digest, length); + PyMem_Free(digest); + return retval; +} - digest = EVP_get_digestbyname(name); +static PyMethodDef EVPXOF_methods[] = { + EVPXOF_DIGEST_METHODDEF + EVPXOF_HEXDIGEST_METHODDEF + {NULL, NULL} /* sentinel */ +}; - ret_obj = EVPnew(name_obj, digest, NULL, (unsigned char*)view.buf, view.len); - if (data_obj) - PyBuffer_Release(&view); - return ret_obj; +static PyObject * +EVPXOF_get_digest_size(EVPobject *self, void *closure) +{ + return PyLong_FromLong(0); } +static PyGetSetDef EVPXOF_getseters[] = { + {"digest_size", + (getter)EVPXOF_get_digest_size, NULL, + NULL, + NULL}, + {NULL} /* Sentinel */ +}; +PyDoc_STRVAR(hashxoftype_doc, +"HASHXOF(name, string=b\'\')\n" +"--\n" +"\n" +"A hash is an object used to calculate a checksum of a string of information.\n" +"\n" +"Methods:\n" +"\n" +"update() -- updates the current digest with an additional string\n" +"digest(length) -- return the current digest value\n" +"hexdigest(length) -- 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 hash algorithm being used by this object\n" +"digest_size -- number of bytes in this hashes output"); + +static PyType_Slot EVPXOFtype_slots[] = { + {Py_tp_doc, (char *)hashxoftype_doc}, + {Py_tp_methods, EVPXOF_methods}, + {Py_tp_getset, EVPXOF_getseters}, + {Py_tp_new, _disabled_new}, + {0, 0}, +}; -#if (OPENSSL_VERSION_NUMBER >= 0x10000000 && !defined(OPENSSL_NO_HMAC) \ - && !defined(OPENSSL_NO_SHA)) +static PyType_Spec EVPXOFtype_spec = { + "_hashlib.HASHXOF", /*tp_name*/ + sizeof(EVPobject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + EVPXOFtype_slots +}; -#define PY_PBKDF2_HMAC 1 -#if !HAS_FAST_PKCS5_PBKDF2_HMAC -/* Improved implementation of PKCS5_PBKDF2_HMAC() - * - * PKCS5_PBKDF2_HMAC_fast() hashes the password exactly one time instead of - * `iter` times. Today (2013) the iteration count is typically 100,000 or - * more. The improved algorithm is not subject to a Denial-of-Service - * vulnerability with overly large passwords. - * - * Also OpenSSL < 1.0 don't provide PKCS5_PBKDF2_HMAC(), only - * PKCS5_PBKDF2_SHA1. - */ -static int -PKCS5_PBKDF2_HMAC_fast(const char *pass, int passlen, - const unsigned char *salt, int saltlen, - int iter, const EVP_MD *digest, - int keylen, unsigned char *out) +#endif +#if HASH_OBJ_CONSTRUCTOR + (initproc)EVP_tp_init, /* tp_init */ +#endif +}; + +static PyObject* +py_evp_fromname(PyObject *module, const char *digestname, PyObject *data_obj, + int usedforsecurity) { - unsigned char digtmp[EVP_MAX_MD_SIZE], *p, itmp[4]; - int cplen, j, k, tkeylen, mdlen; - unsigned long i = 1; - HMAC_CTX hctx_tpl, hctx; + Py_buffer view = { 0 }; + PY_EVP_MD *digest = NULL; + PyTypeObject *type; + EVPobject *self = NULL; - mdlen = EVP_MD_size(digest); - if (mdlen < 0) - return 0; + if (data_obj != NULL) { + GET_BUFFER_VIEW_OR_ERROUT(data_obj, &view); + } - HMAC_CTX_init(&hctx_tpl); - HMAC_CTX_init(&hctx); - p = out; - tkeylen = keylen; - if (!HMAC_Init_ex(&hctx_tpl, pass, passlen, digest, NULL)) { - HMAC_CTX_cleanup(&hctx_tpl); - return 0; + digest = py_digest_by_name( + module, digestname, usedforsecurity ? Py_ht_evp : Py_ht_evp_nosecurity + ); + if (digest == NULL) { + goto exit; } - while (tkeylen) { - if (tkeylen > mdlen) - cplen = mdlen; - else - cplen = tkeylen; - /* We are unlikely to ever use more than 256 blocks (5120 bits!) - * but just in case... - */ - itmp[0] = (unsigned char)((i >> 24) & 0xff); - itmp[1] = (unsigned char)((i >> 16) & 0xff); - itmp[2] = (unsigned char)((i >> 8) & 0xff); - itmp[3] = (unsigned char)(i & 0xff); - if (!HMAC_CTX_copy(&hctx, &hctx_tpl)) { - HMAC_CTX_cleanup(&hctx_tpl); - return 0; - } - if (!HMAC_Update(&hctx, salt, saltlen) - || !HMAC_Update(&hctx, itmp, 4) - || !HMAC_Final(&hctx, digtmp, NULL)) { - HMAC_CTX_cleanup(&hctx_tpl); - HMAC_CTX_cleanup(&hctx); - return 0; + +#ifdef PY_OPENSSL_HAS_SHAKE + if ((EVP_MD_flags(digest) & EVP_MD_FLAG_XOF) == EVP_MD_FLAG_XOF) { + type = get_hashlib_state(module)->EVPXOFtype; + } else +#endif + { + type = get_hashlib_state(module)->EVPtype; + } + + self = newEVPobject(type); + if (self == NULL) { + goto exit; + } + +#if defined(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW) && OPENSSL_VERSION_NUMBER < 0x30000000L + // In OpenSSL 1.1.1 the non FIPS allowed flag is context specific while + // in 3.0.0 it is a different EVP_MD provider. + if (!usedforsecurity) { + EVP_MD_CTX_set_flags(self->ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); + } +#endif + + int result = EVP_DigestInit_ex(self->ctx, digest, NULL); + if (!result) { + _setException(PyExc_ValueError, NULL); + Py_CLEAR(self); + goto exit; + } + + if (view.buf && view.len) { + if (view.len >= HASHLIB_GIL_MINSIZE) { + Py_BEGIN_ALLOW_THREADS + result = EVP_hash(self, view.buf, view.len); + Py_END_ALLOW_THREADS + } else { + result = EVP_hash(self, view.buf, view.len); } - HMAC_CTX_cleanup(&hctx); - memcpy(p, digtmp, cplen); - for (j = 1; j < iter; j++) { - if (!HMAC_CTX_copy(&hctx, &hctx_tpl)) { - HMAC_CTX_cleanup(&hctx_tpl); - return 0; - } - if (!HMAC_Update(&hctx, digtmp, mdlen) - || !HMAC_Final(&hctx, digtmp, NULL)) { - HMAC_CTX_cleanup(&hctx_tpl); - HMAC_CTX_cleanup(&hctx); - return 0; - } - HMAC_CTX_cleanup(&hctx); - for (k = 0; k < cplen; k++) { - p[k] ^= digtmp[k]; - } + if (result == -1) { + Py_CLEAR(self); + goto exit; } - tkeylen-= cplen; - i++; - p+= cplen; } - HMAC_CTX_cleanup(&hctx_tpl); - return 1; + + exit: + if (data_obj != NULL) { + PyBuffer_Release(&view); + } + if (digest != NULL) { + PY_EVP_MD_free(digest); + } + + return (PyObject *)self; +} + + +/* The module-level function: new() */ + +PyDoc_STRVAR(EVP_new__doc__, +"Return a new hash object using the named algorithm.\n\ +An optional string argument may be provided and will be\n\ +automatically hashed.\n\ +\n\ +The MD5 and SHA1 algorithms are always supported.\n"); + +static PyObject * +EVP_new(PyObject *self, PyObject *args, PyObject *kwdict) +{ + char *name; + if (!PyArg_Parse(name_obj, "s", &name)) { + PyErr_SetString(PyExc_TypeError, "name must be a string"); + return NULL; + } + return py_evp_fromname(module, name, data_obj, usedforsecurity); } -#endif + PyDoc_STRVAR(pbkdf2_hmac__doc__, @@ -639,22 +988,253 @@ pseudorandom function."); static PyObject * pbkdf2_hmac(PyObject *self, PyObject *args, PyObject *kwdict) { - static char *kwlist[] = {"hash_name", "password", "salt", "iterations", - "dklen", NULL}; - PyObject *key_obj = NULL, *dklen_obj = Py_None; - char *name, *key; - Py_buffer password, salt; - long iterations, dklen; - int retval; - const EVP_MD *digest; + return py_evp_fromname(module, Py_hash_md5, data_obj, usedforsecurity); +} - if (!PyArg_ParseTupleAndKeywords(args, kwdict, "sy*y*l|O:pbkdf2_hmac", - kwlist, &name, &password, &salt, - &iterations, &dklen_obj)) { - return NULL; - } - digest = EVP_get_digestbyname(name); +/*[clinic input] +_hashlib.openssl_sha1 + + string as data_obj: object(py_default="b''") = NULL + * + usedforsecurity: bool = True + +Returns a sha1 hash object; optionally initialized with a string + +[clinic start generated code]*/ + +static PyObject * +_hashlib_openssl_sha1_impl(PyObject *module, PyObject *data_obj, + int usedforsecurity) +/*[clinic end generated code: output=6813024cf690670d input=948f2f4b6deabc10]*/ +{ + return py_evp_fromname(module, Py_hash_sha1, data_obj, usedforsecurity); +} + + +/*[clinic input] +_hashlib.openssl_sha224 + + string as data_obj: object(py_default="b''") = NULL + * + usedforsecurity: bool = True + +Returns a sha224 hash object; optionally initialized with a string + +[clinic start generated code]*/ + +static PyObject * +_hashlib_openssl_sha224_impl(PyObject *module, PyObject *data_obj, + int usedforsecurity) +/*[clinic end generated code: output=a2dfe7cc4eb14ebb input=f9272821fadca505]*/ +{ + return py_evp_fromname(module, Py_hash_sha224, data_obj, usedforsecurity); +} + + +/*[clinic input] +_hashlib.openssl_sha256 + + string as data_obj: object(py_default="b''") = NULL + * + usedforsecurity: bool = True + +Returns a sha256 hash object; optionally initialized with a string + +[clinic start generated code]*/ + +static PyObject * +_hashlib_openssl_sha256_impl(PyObject *module, PyObject *data_obj, + int usedforsecurity) +/*[clinic end generated code: output=1f874a34870f0a68 input=549fad9d2930d4c5]*/ +{ + return py_evp_fromname(module, Py_hash_sha256, data_obj, usedforsecurity); +} + + +/*[clinic input] +_hashlib.openssl_sha384 + + string as data_obj: object(py_default="b''") = NULL + * + usedforsecurity: bool = True + +Returns a sha384 hash object; optionally initialized with a string + +[clinic start generated code]*/ + +static PyObject * +_hashlib_openssl_sha384_impl(PyObject *module, PyObject *data_obj, + int usedforsecurity) +/*[clinic end generated code: output=58529eff9ca457b2 input=48601a6e3bf14ad7]*/ +{ + return py_evp_fromname(module, Py_hash_sha384, data_obj, usedforsecurity); +} + + +/*[clinic input] +_hashlib.openssl_sha512 + + string as data_obj: object(py_default="b''") = NULL + * + usedforsecurity: bool = True + +Returns a sha512 hash object; optionally initialized with a string + +[clinic start generated code]*/ + +static PyObject * +_hashlib_openssl_sha512_impl(PyObject *module, PyObject *data_obj, + int usedforsecurity) +/*[clinic end generated code: output=2c744c9e4a40d5f6 input=c5c46a2a817aa98f]*/ +{ + return py_evp_fromname(module, Py_hash_sha512, data_obj, usedforsecurity); +} + + +#ifdef PY_OPENSSL_HAS_SHA3 + +/*[clinic input] +_hashlib.openssl_sha3_224 + + string as data_obj: object(py_default="b''") = NULL + * + usedforsecurity: bool = True + +Returns a sha3-224 hash object; optionally initialized with a string + +[clinic start generated code]*/ + +static PyObject * +_hashlib_openssl_sha3_224_impl(PyObject *module, PyObject *data_obj, + int usedforsecurity) +/*[clinic end generated code: output=144641c1d144b974 input=e3a01b2888916157]*/ +{ + return py_evp_fromname(module, Py_hash_sha3_224, data_obj, usedforsecurity); +} + +/*[clinic input] +_hashlib.openssl_sha3_256 + + string as data_obj: object(py_default="b''") = NULL + * + usedforsecurity: bool = True + +Returns a sha3-256 hash object; optionally initialized with a string + +[clinic start generated code]*/ + +static PyObject * +_hashlib_openssl_sha3_256_impl(PyObject *module, PyObject *data_obj, + int usedforsecurity) +/*[clinic end generated code: output=c61f1ab772d06668 input=e2908126c1b6deed]*/ +{ + return py_evp_fromname(module, Py_hash_sha3_256, data_obj , usedforsecurity); +} + +/*[clinic input] +_hashlib.openssl_sha3_384 + + string as data_obj: object(py_default="b''") = NULL + * + usedforsecurity: bool = True + +Returns a sha3-384 hash object; optionally initialized with a string + +[clinic start generated code]*/ + +static PyObject * +_hashlib_openssl_sha3_384_impl(PyObject *module, PyObject *data_obj, + int usedforsecurity) +/*[clinic end generated code: output=f68e4846858cf0ee input=ec0edf5c792f8252]*/ +{ + return py_evp_fromname(module, Py_hash_sha3_384, data_obj , usedforsecurity); +} + +/*[clinic input] +_hashlib.openssl_sha3_512 + + string as data_obj: object(py_default="b''") = NULL + * + usedforsecurity: bool = True + +Returns a sha3-512 hash object; optionally initialized with a string + +[clinic start generated code]*/ + +static PyObject * +_hashlib_openssl_sha3_512_impl(PyObject *module, PyObject *data_obj, + int usedforsecurity) +/*[clinic end generated code: output=2eede478c159354a input=64e2cc0c094d56f4]*/ +{ + return py_evp_fromname(module, Py_hash_sha3_512, data_obj , usedforsecurity); +} +#endif /* PY_OPENSSL_HAS_SHA3 */ + +#ifdef PY_OPENSSL_HAS_SHAKE +/*[clinic input] +_hashlib.openssl_shake_128 + + string as data_obj: object(py_default="b''") = NULL + * + usedforsecurity: bool = True + +Returns a shake-128 variable hash object; optionally initialized with a string + +[clinic start generated code]*/ + +static PyObject * +_hashlib_openssl_shake_128_impl(PyObject *module, PyObject *data_obj, + int usedforsecurity) +/*[clinic end generated code: output=bc49cdd8ada1fa97 input=6c9d67440eb33ec8]*/ +{ + return py_evp_fromname(module, Py_hash_shake_128, data_obj , usedforsecurity); +} + +/*[clinic input] +_hashlib.openssl_shake_256 + + string as data_obj: object(py_default="b''") = NULL + * + usedforsecurity: bool = True + +Returns a shake-256 variable hash object; optionally initialized with a string + +[clinic start generated code]*/ + +static PyObject * +_hashlib_openssl_shake_256_impl(PyObject *module, PyObject *data_obj, + int usedforsecurity) +/*[clinic end generated code: output=358d213be8852df7 input=479cbe9fefd4a9f8]*/ +{ + return py_evp_fromname(module, Py_hash_shake_256, data_obj , usedforsecurity); +} +#endif /* PY_OPENSSL_HAS_SHAKE */ + +/*[clinic input] +_hashlib.pbkdf2_hmac as pbkdf2_hmac + + hash_name: str + password: Py_buffer + salt: Py_buffer + iterations: long + dklen as dklen_obj: object = None + +Password based key derivation function 2 (PKCS #5 v2.0) with HMAC as pseudorandom function. +[clinic start generated code]*/ + +static PyObject * +pbkdf2_hmac_impl(PyObject *module, const char *hash_name, + Py_buffer *password, Py_buffer *salt, long iterations, + PyObject *dklen_obj) +/*[clinic end generated code: output=144b76005416599b input=ed3ab0d2d28b5d5c]*/ +{ + PyObject *key_obj = NULL; + char *key; + long dklen; + int retval; + + PY_EVP_MD *digest = py_digest_by_name(module, hash_name, Py_ht_pbkdf2); if (digest == NULL) { PyErr_SetString(PyExc_ValueError, "unsupported hash type"); goto end; @@ -725,13 +1305,14 @@ pbkdf2_hmac(PyObject *self, PyObject *args, PyObject *kwdict) if (!retval) { Py_CLEAR(key_obj); - _setException(PyExc_ValueError); + _setException(PyExc_ValueError, NULL); goto end; } end: - PyBuffer_Release(&password); - PyBuffer_Release(&salt); + if (digest != NULL) { + PY_EVP_MD_free(digest); + } return key_obj; } @@ -833,9 +1414,7 @@ _hashlib_scrypt_impl(PyObject *module, Py_buffer *password, Py_buffer *salt, /* let OpenSSL validate the rest */ retval = EVP_PBE_scrypt(NULL, 0, NULL, 0, n, r, p, maxmem, NULL, 0); if (!retval) { - /* sorry, can't do much better */ - PyErr_SetString(PyExc_ValueError, - "Invalid paramemter combination for n, r, p, maxmem."); + _setException(PyExc_ValueError, "Invalid parameter combination for n, r, p, maxmem."); return NULL; } @@ -856,13 +1435,429 @@ _hashlib_scrypt_impl(PyObject *module, Py_buffer *password, Py_buffer *salt, if (!retval) { Py_CLEAR(key_obj); - _setException(PyExc_ValueError); + _setException(PyExc_ValueError, NULL); return NULL; } return key_obj; } #endif +/* Fast HMAC for hmac.digest() + */ + +/*[clinic input] +_hashlib.hmac_digest as _hashlib_hmac_singleshot + + key: Py_buffer + msg: Py_buffer + digest: str + +Single-shot HMAC. +[clinic start generated code]*/ + +static PyObject * +_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; + PY_EVP_MD *evp; + + evp = py_digest_by_name(module, digest, Py_ht_mac); + if (evp == NULL) { + return NULL; + } + if (key->len > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "key is too long."); + return NULL; + } + if (msg->len > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "msg is too long."); + return NULL; + } + + evp = py_digest_by_name(module, digest, Py_ht_mac); + if (evp == NULL) { + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + result = HMAC( + evp, + (const void*)key->buf, (int)key->len, + (const unsigned char*)msg->buf, (int)msg->len, + md, &md_len + ); + Py_END_ALLOW_THREADS + PY_EVP_MD_free(evp); + + if (result == NULL) { + _setException(PyExc_ValueError, NULL); + return NULL; + } + 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; + PY_EVP_MD *digest; + HMAC_CTX *ctx = NULL; + HMACobject *self = NULL; + int r; + + if (key->len > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "key is too long."); + return NULL; + } + + if ((digestmod == NULL) || !strlen(digestmod)) { + PyErr_SetString( + PyExc_TypeError, "Missing required parameter 'digestmod'."); + return NULL; + } + + digest = py_digest_by_name(module, digestmod, Py_ht_mac); + if (!digest) { + return NULL; + } + + ctx = HMAC_CTX_new(); + if (ctx == NULL) { + _setException(PyExc_ValueError, NULL); + goto error; + } + + r = HMAC_Init_ex( + ctx, + (const char*)key->buf, + (int)key->len, + digest, + NULL /*impl*/); + PY_EVP_MD_free(digest); + if (r == 0) { + _setException(PyExc_ValueError, NULL); + 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) { + Py_BEGIN_ALLOW_THREADS + PyThread_acquire_lock(self->lock, 1); + r = HMAC_Update(self->ctx, (const unsigned char*)view.buf, view.len); + PyThread_release_lock(self->lock); + Py_END_ALLOW_THREADS + } else { + r = HMAC_Update(self->ctx, (const unsigned char*)view.buf, view.len); + } + + PyBuffer_Release(&view); + + if (r == 0) { + _setException(PyExc_ValueError, NULL); + 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, NULL); + } + if (!locked_HMAC_CTX_copy(ctx, self)) { + HMAC_CTX_free(ctx); + return _setException(PyExc_ValueError, NULL); + } + + 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, NULL); + return 0; + } + int r = HMAC_Final(temp_ctx, buf, &len); + HMAC_CTX_free(temp_ctx); + if (r == 0) { + _setException(PyExc_ValueError, NULL); + 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, NULL); + } + 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, NULL); + } + 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, NULL); + } + 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, NULL); + } + 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; @@ -997,7 +1992,7 @@ _hashlib_get_fips_mode_impl(PyObject *module) // But 0 is also a valid result value. unsigned long errcode = ERR_peek_last_error(); if (errcode) { - _setException(PyExc_ValueError); + _setException(PyExc_ValueError, NULL); return -1; } } @@ -1049,6 +2044,137 @@ static struct PyMethodDef EVP_functions[] = { /* Initialize this module. */ +static int +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 + return 0; +} + +static int +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 + if (state->hashtable != NULL) { + _Py_hashtable_destroy(state->hashtable); + state->hashtable = NULL; + } + return 0; +} + +static void +hashlib_free(void *m) +{ + hashlib_clear((PyObject *)m); +} + +/* Py_mod_exec functions */ +static int +hashlib_openssl_legacy_init(PyObject *module) +{ +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) + /* Load all digest algorithms and initialize cpuid */ + OPENSSL_add_all_algorithms_noconf(); + ERR_load_crypto_strings(); +#endif + return 0; +} + +static int +hashlib_init_hashtable(PyObject *module) +{ + _hashlibstate *state = get_hashlib_state(module); + + state->hashtable = py_hashentry_table_new(); + if (state->hashtable == NULL) { + PyErr_NoMemory(); + return -1; + } + return 0; +} + +static int +hashlib_init_evptype(PyObject *module) +{ + _hashlibstate *state = get_hashlib_state(module); + + state->EVPtype = (PyTypeObject *)PyType_FromSpec(&EVPtype_spec); + if (state->EVPtype == NULL) { + return -1; + } + if (PyModule_AddType(module, state->EVPtype) < 0) { + return -1; + } + return 0; +} + +static int +hashlib_init_evpxoftype(PyObject *module) +{ +#ifdef PY_OPENSSL_HAS_SHAKE + _hashlibstate *state = get_hashlib_state(module); + PyObject *bases; + + if (state->EVPtype == NULL) { + return -1; + } + + bases = PyTuple_Pack(1, state->EVPtype); + if (bases == NULL) { + return -1; + } + + state->EVPXOFtype = (PyTypeObject *)PyType_FromSpecWithBases( + &EVPXOFtype_spec, bases + ); + Py_DECREF(bases); + if (state->EVPXOFtype == NULL) { + return -1; + } + if (PyModule_AddType(module, state->EVPXOFtype) < 0) { + return -1; + } +#endif + return 0; +} + +static int +hashlib_init_hmactype(PyObject *module) +{ + _hashlibstate *state = get_hashlib_state(module); + + state->HMACtype = (PyTypeObject *)PyType_FromSpec(&HMACtype_spec); + if (state->HMACtype == NULL) { + return -1; + } + if (PyModule_AddType(module, state->HMACtype) < 0) { + return -1; + } + return 0; +} + +#if 0 +static PyModuleDef_Slot hashlib_slots[] = { + /* OpenSSL 1.0.2 and LibreSSL */ + {Py_mod_exec, hashlib_openssl_legacy_init}, + {Py_mod_exec, hashlib_init_hashtable}, + {Py_mod_exec, hashlib_init_evptype}, + {Py_mod_exec, hashlib_init_evpxoftype}, + {Py_mod_exec, hashlib_init_hmactype}, + {Py_mod_exec, hashlib_md_meth_names}, + {0, NULL} +}; +#endif static struct PyModuleDef _hashlibmodule = { PyModuleDef_HEAD_INIT, @@ -1091,7 +2217,23 @@ PyInit__hashlib(void) Py_DECREF(m); return NULL; } - if (PyModule_AddObject(m, "openssl_md_meth_names", openssl_md_meth_names)) { + if (hashlib_init_hashtable(m) < 0) { + Py_DECREF(m); + return NULL; + } + if (hashlib_init_evptype(m) < 0) { + Py_DECREF(m); + return NULL; + } + if (hashlib_init_evpxoftype(m) < 0) { + Py_DECREF(m); + return NULL; + } + if (hashlib_init_hmactype(m) < 0) { + Py_DECREF(m); + return NULL; + } + if (hashlib_md_meth_names(m) == -1) { Py_DECREF(m); return NULL; } -- 2.46.1
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor