Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ Changelog
:class:`~cryptography.hazmat.primitives.ciphers.aead.AESSIV`, and
:class:`~cryptography.hazmat.primitives.ciphers.aead.ChaCha20Poly1305` to
allow encrypting directly into a pre-allocated buffer.
* Builtin hash classes and instances of classes in
:mod:`~cryptography.hazmat.primitives.asymmetric.padding` can now be compared with `==`

.. _v46-0-3:

Expand Down
2 changes: 1 addition & 1 deletion docs/hazmat/primitives/asymmetric/cloudhsm.rst
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ if you only need a subset of functionality.
... Maps the cryptography padding and algorithm to the corresponding KMS signing algorithm.
... This is specific to your implementation.
... """
... if isinstance(padding, PKCS1v15) and isinstance(algorithm, hashes.SHA256):
... if padding == PKCS1v15() and algorithm == hashes.SHA256():
... return b"RSA_PKCS1_V1_5_SHA_256"
... else:
... raise NotImplementedError()
Expand Down
10 changes: 5 additions & 5 deletions docs/x509/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ Loading Certificate Revocation Lists
>>> from cryptography import x509
>>> from cryptography.hazmat.primitives import hashes
>>> crl = x509.load_pem_x509_crl(pem_crl_data)
>>> isinstance(crl.signature_hash_algorithm, hashes.SHA256)
>>> crl.signature_hash_algorithm == hashes.SHA256()
True

.. function:: load_der_x509_crl(data)
Expand Down Expand Up @@ -287,7 +287,7 @@ Loading Certificate Signing Requests
>>> from cryptography import x509
>>> from cryptography.hazmat.primitives import hashes
>>> csr = x509.load_pem_x509_csr(pem_req_data)
>>> isinstance(csr.signature_hash_algorithm, hashes.SHA256)
>>> csr.signature_hash_algorithm == hashes.SHA256()
True

.. function:: load_der_x509_csr(data)
Expand Down Expand Up @@ -477,7 +477,7 @@ X.509 Certificate Object
.. doctest::

>>> from cryptography.hazmat.primitives import hashes
>>> isinstance(cert.signature_hash_algorithm, hashes.SHA256)
>>> cert.signature_hash_algorithm == hashes.SHA256()
True

.. attribute:: signature_algorithm_oid
Expand Down Expand Up @@ -716,7 +716,7 @@ X.509 CRL (Certificate Revocation List) Object
.. doctest::

>>> from cryptography.hazmat.primitives import hashes
>>> isinstance(crl.signature_hash_algorithm, hashes.SHA256)
>>> crl.signature_hash_algorithm == hashes.SHA256()
True

.. attribute:: signature_algorithm_oid
Expand Down Expand Up @@ -1119,7 +1119,7 @@ X.509 CSR (Certificate Signing Request) Object
.. doctest::

>>> from cryptography.hazmat.primitives import hashes
>>> isinstance(csr.signature_hash_algorithm, hashes.SHA256)
>>> csr.signature_hash_algorithm == hashes.SHA256()
True

.. attribute:: signature_algorithm_oid
Expand Down
33 changes: 33 additions & 0 deletions src/cryptography/hazmat/primitives/asymmetric/padding.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from __future__ import annotations

import abc
import typing

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives._asymmetric import (
Expand All @@ -16,6 +17,9 @@
class PKCS1v15(AsymmetricPadding):
name = "EMSA-PKCS1-v1_5"

def __eq__(self, other: typing.Any) -> bool:
return isinstance(other, PKCS1v15)


class _MaxLength:
"Sentinel value for `MAX_LENGTH`."
Expand Down Expand Up @@ -56,6 +60,17 @@ def __init__(

self._salt_length = salt_length

def __eq__(self, other: typing.Any) -> bool:
if not isinstance(other, PSS):
return False

if isinstance(self._salt_length, int):
eq_salt_length = self._salt_length == other._salt_length
else:
eq_salt_length = self._salt_length is other._salt_length

return eq_salt_length and self._mgf == other._mgf

@property
def mgf(self) -> MGF:
return self._mgf
Expand All @@ -77,6 +92,14 @@ def __init__(
self._algorithm = algorithm
self._label = label

def __eq__(self, other: typing.Any) -> bool:
return (
isinstance(other, OAEP)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue here.

Copy link
Contributor Author

@mathiasertl mathiasertl Nov 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually not correct in this case:

>>> h = hashes.SHA256()
>>> mgf = MGF1(h)
>>> OAEP(mgf, h, None) == h
False

and self._mgf == other._mgf
and self._algorithm == other._algorithm
and self._label == other._label
)

@property
def algorithm(self) -> hashes.HashAlgorithm:
return self._algorithm
Expand All @@ -89,6 +112,13 @@ def mgf(self) -> MGF:
class MGF(metaclass=abc.ABCMeta):
_algorithm: hashes.HashAlgorithm

@abc.abstractmethod
def __eq__(self, other: typing.Any) -> bool:
"""
Implement equality checking.
"""
...


class MGF1(MGF):
def __init__(self, algorithm: hashes.HashAlgorithm):
Expand All @@ -97,6 +127,9 @@ def __init__(self, algorithm: hashes.HashAlgorithm):

self._algorithm = algorithm

def __eq__(self, other: typing.Any) -> bool:
return isinstance(other, MGF1) and self._algorithm == other._algorithm


def calculate_max_pss_salt_length(
key: rsa.RSAPrivateKey | rsa.RSAPublicKey,
Expand Down
64 changes: 64 additions & 0 deletions src/cryptography/hazmat/primitives/hashes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from __future__ import annotations

import abc
import typing

from cryptography.hazmat.bindings._rust import openssl as rust_openssl
from cryptography.utils import Buffer
Expand Down Expand Up @@ -103,66 +104,99 @@ class SHA1(HashAlgorithm):
digest_size = 20
block_size = 64

def __eq__(self, other: typing.Any) -> bool:
return isinstance(other, SHA1)


class SHA512_224(HashAlgorithm): # noqa: N801
name = "sha512-224"
digest_size = 28
block_size = 128

def __eq__(self, other: typing.Any) -> bool:
return isinstance(other, SHA512_224)


class SHA512_256(HashAlgorithm): # noqa: N801
name = "sha512-256"
digest_size = 32
block_size = 128

def __eq__(self, other: typing.Any) -> bool:
return isinstance(other, SHA512_256)


class SHA224(HashAlgorithm):
name = "sha224"
digest_size = 28
block_size = 64

def __eq__(self, other: typing.Any) -> bool:
return isinstance(other, SHA224)


class SHA256(HashAlgorithm):
name = "sha256"
digest_size = 32
block_size = 64

def __eq__(self, other: typing.Any) -> bool:
return isinstance(other, SHA256)


class SHA384(HashAlgorithm):
name = "sha384"
digest_size = 48
block_size = 128

def __eq__(self, other: typing.Any) -> bool:
return isinstance(other, SHA384)


class SHA512(HashAlgorithm):
name = "sha512"
digest_size = 64
block_size = 128

def __eq__(self, other: typing.Any) -> bool:
return isinstance(other, SHA512)


class SHA3_224(HashAlgorithm): # noqa: N801
name = "sha3-224"
digest_size = 28
block_size = None

def __eq__(self, other: typing.Any) -> bool:
return isinstance(other, SHA3_224)


class SHA3_256(HashAlgorithm): # noqa: N801
name = "sha3-256"
digest_size = 32
block_size = None

def __eq__(self, other: typing.Any) -> bool:
return isinstance(other, SHA3_256)


class SHA3_384(HashAlgorithm): # noqa: N801
name = "sha3-384"
digest_size = 48
block_size = None

def __eq__(self, other: typing.Any) -> bool:
return isinstance(other, SHA3_384)


class SHA3_512(HashAlgorithm): # noqa: N801
name = "sha3-512"
digest_size = 64
block_size = None

def __eq__(self, other: typing.Any) -> bool:
return isinstance(other, SHA3_512)


class SHAKE128(HashAlgorithm, ExtendableOutputFunction):
name = "shake128"
Expand All @@ -177,6 +211,12 @@ def __init__(self, digest_size: int):

self._digest_size = digest_size

def __eq__(self, other: typing.Any) -> bool:
return (
isinstance(other, SHAKE128)
and self._digest_size == other._digest_size
)

@property
def digest_size(self) -> int:
return self._digest_size
Expand All @@ -195,6 +235,12 @@ def __init__(self, digest_size: int):

self._digest_size = digest_size

def __eq__(self, other: typing.Any) -> bool:
return (
isinstance(other, SHAKE256)
and self._digest_size == other._digest_size
)

@property
def digest_size(self) -> int:
return self._digest_size
Expand All @@ -205,6 +251,9 @@ class MD5(HashAlgorithm):
digest_size = 16
block_size = 64

def __eq__(self, other: typing.Any) -> bool:
return isinstance(other, MD5)


class BLAKE2b(HashAlgorithm):
name = "blake2b"
Expand All @@ -218,6 +267,12 @@ def __init__(self, digest_size: int):

self._digest_size = digest_size

def __eq__(self, other: typing.Any) -> bool:
return (
isinstance(other, BLAKE2b)
and self._digest_size == other._digest_size
)

@property
def digest_size(self) -> int:
return self._digest_size
Expand All @@ -235,6 +290,12 @@ def __init__(self, digest_size: int):

self._digest_size = digest_size

def __eq__(self, other: typing.Any) -> bool:
return (
isinstance(other, BLAKE2s)
and self._digest_size == other._digest_size
)

@property
def digest_size(self) -> int:
return self._digest_size
Expand All @@ -244,3 +305,6 @@ class SM3(HashAlgorithm):
name = "sm3"
digest_size = 32
block_size = 64

def __eq__(self, other: typing.Any) -> bool:
return isinstance(other, SM3)
7 changes: 7 additions & 0 deletions tests/doubles.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

import typing

from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
Expand Down Expand Up @@ -40,6 +41,12 @@ class DummyHashAlgorithm(hashes.HashAlgorithm):
def __init__(self, digest_size: int = 32) -> None:
self._digest_size = digest_size

def __eq__(self, other: typing.Any) -> bool:
return (
isinstance(other, DummyHashAlgorithm)
and self._digest_size == other._digest_size
)

@property
def digest_size(self) -> int:
return self._digest_size
Expand Down
9 changes: 9 additions & 0 deletions tests/hazmat/backends/test_openssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@


import itertools
import typing

import pytest

Expand Down Expand Up @@ -32,6 +33,9 @@ class DummyMGF(padding.MGF):
_salt_length = 0
_algorithm = hashes.SHA1()

def __eq__(self, other: typing.Any) -> bool:
return isinstance(other, DummyMGF)


class TestOpenSSL:
def test_backend_exists(self):
Expand Down Expand Up @@ -194,6 +198,11 @@ def test_rsa_padding_unsupported_mgf(self):
is False
)

def test_dummy_mgf_eq(self):
"""This test just exists to fix code coverage for the dummy class."""
assert DummyMGF() == DummyMGF()
assert DummyMGF() != padding.MGF1(hashes.SHA256())

def test_unsupported_mgf1_hash_algorithm_md5_decrypt(self, rsa_key_2048):
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING):
rsa_key_2048.decrypt(
Expand Down
Loading