diff --git a/Lib/hmac.py b/Lib/hmac.py index 9d3fae8b1b1597..e0c040bcd5fe3d 100644 --- a/Lib/hmac.py +++ b/Lib/hmac.py @@ -171,6 +171,7 @@ def copy(self): # Call __new__ directly to avoid the expensive __init__. other = self.__class__.__new__(self.__class__) other.digest_size = self.digest_size + other.block_size = self.block_size if self._hmac: other._hmac = self._hmac.copy() other._inner = other._outer = None diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py index 7634deeb1d8eb9..17888a9f286c8f 100644 --- a/Lib/test/test_hmac.py +++ b/Lib/test/test_hmac.py @@ -1076,6 +1076,15 @@ def test_properties(self): self.assertEqual(h.digest_size, self.digest_size) self.assertEqual(h.block_size, self.block_size) + def test_copy(self): + # Test a generic copy() and the attributes it exposes. + # See https://github.com/python/cpython/issues/142451. + h1 = self.hmac_new(b"my secret key", digestmod=self.digestname) + h2 = h1.copy() + self.assertEqual(h1.name, h2.name) + self.assertEqual(h1.digest_size, h2.digest_size) + self.assertEqual(h1.block_size, h2.block_size) + def test_repr(self): # HMAC object representation may differ across implementations raise NotImplementedError diff --git a/Misc/NEWS.d/next/Library/2025-12-10-11-02-53.gh-issue-142451.eCLvhG.rst b/Misc/NEWS.d/next/Library/2025-12-10-11-02-53.gh-issue-142451.eCLvhG.rst new file mode 100644 index 00000000000000..3726d4178e26e4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-10-11-02-53.gh-issue-142451.eCLvhG.rst @@ -0,0 +1,2 @@ +:mod:`hmac`: correctly copy :class:`~hmac.HMAC` attributes for objects +copied through :meth:`HMAC.copy() `. Patch by Bénédikt Tran. diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c index 19089b009f7911..77832a768e0cbc 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c @@ -606,9 +606,14 @@ get_asn1_utf8name_by_nid(int nid) { const char *name = OBJ_nid2ln(nid); if (name == NULL) { - // In OpenSSL 3.0 and later, OBJ_nid*() are thread-safe and may raise. - assert(ERR_peek_last_error() != 0); - if (ERR_GET_REASON(ERR_peek_last_error()) != OBJ_R_UNKNOWN_NID) { + /* In OpenSSL 3.0 and later, OBJ_nid*() are thread-safe and may raise. + * However, not all versions of OpenSSL set a last error, so we simply + * ignore the last error if none exists. + * + * See https://github.com/python/cpython/issues/142451. + */ + unsigned long errcode = ERR_peek_last_error(); + if (errcode && ERR_GET_REASON(errcode) != OBJ_R_UNKNOWN_NID) { goto error; } // fallback to short name and unconditionally propagate errors @@ -2255,6 +2260,9 @@ _hashlib_HMAC_copy_impl(HMACobject *self) return NULL; } retval->ctx = ctx; +#ifdef Py_HAS_OPENSSL3_SUPPORT + retval->evp_md_nid = self->evp_md_nid; +#endif HASHLIB_INIT_MUTEX(retval); return (PyObject *)retval; }