Skip to content
Merged
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
5 changes: 5 additions & 0 deletions src/cryptography/hazmat/bindings/_rust/declarative_asn1.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -81,30 +81,35 @@ class PrintableString:
def __new__(cls, inner: str) -> PrintableString: ...
def __repr__(self) -> str: ...
def __eq__(self, other: object) -> bool: ...
def __hash__(self) -> int: ...
def as_str(self) -> str: ...

class IA5String:
def __new__(cls, inner: str) -> IA5String: ...
def __repr__(self) -> str: ...
def __eq__(self, other: object) -> bool: ...
def __hash__(self) -> int: ...
def as_str(self) -> str: ...

class UTCTime:
def __new__(cls, inner: datetime.datetime) -> UTCTime: ...
def __repr__(self) -> str: ...
def __eq__(self, other: object) -> bool: ...
def __hash__(self) -> int: ...
def as_datetime(self) -> datetime.datetime: ...

class GeneralizedTime:
def __new__(cls, inner: datetime.datetime) -> GeneralizedTime: ...
def __repr__(self) -> str: ...
def __eq__(self, other: object) -> bool: ...
def __hash__(self) -> int: ...
def as_datetime(self) -> datetime.datetime: ...

class BitString:
def __new__(cls, data: bytes, padding_bits: int) -> BitString: ...
def __repr__(self) -> str: ...
def __eq__(self, other: object) -> bool: ...
def __hash__(self) -> int: ...
def as_bytes(self) -> bytes: ...
def padding_bits(self) -> int: ...

Expand Down
22 changes: 22 additions & 0 deletions src/rust/src/declarative_asn1/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,10 @@ impl PrintableString {
(**self.inner.bind(py)).eq(other.inner.bind(py))
}

fn __hash__(&self, py: pyo3::Python<'_>) -> pyo3::PyResult<isize> {
(**self.inner.bind(py)).hash()
}

pub fn __repr__<'py>(
&self,
py: pyo3::Python<'py>,
Expand Down Expand Up @@ -301,6 +305,10 @@ impl IA5String {
(**self.inner.bind(py)).eq(other.inner.bind(py))
}

fn __hash__(&self, py: pyo3::Python<'_>) -> pyo3::PyResult<isize> {
(**self.inner.bind(py)).hash()
}

pub fn __repr__<'py>(
&self,
py: pyo3::Python<'py>,
Expand Down Expand Up @@ -359,6 +367,10 @@ impl UtcTime {
(**self.inner.bind(py)).eq(other.inner.bind(py))
}

fn __hash__(&self, py: pyo3::Python<'_>) -> pyo3::PyResult<isize> {
(**self.inner.bind(py)).hash()
}

pub fn __repr__<'py>(
&self,
py: pyo3::Python<'py>,
Expand Down Expand Up @@ -404,6 +416,10 @@ impl GeneralizedTime {
(**self.inner.bind(py)).eq(other.inner.bind(py))
}

fn __hash__(&self, py: pyo3::Python<'_>) -> pyo3::PyResult<isize> {
(**self.inner.bind(py)).hash()
}

pub fn __repr__<'py>(
&self,
py: pyo3::Python<'py>,
Expand Down Expand Up @@ -453,6 +469,12 @@ impl BitString {
&& self.padding_bits == other.padding_bits)
}

fn __hash__(&self, py: pyo3::Python<'_>) -> pyo3::PyResult<isize> {
(self.data.bind(py), self.padding_bits)
.into_pyobject(py)?
.hash()
}

pub fn __repr__<'py>(
&self,
py: pyo3::Python<'py>,
Expand Down
49 changes: 49 additions & 0 deletions tests/hazmat/asn1/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ def test_invalid_printable_string(self) -> None:
with pytest.raises(ValueError, match="invalid PrintableString: café"):
asn1.PrintableString("café")

def test_hash_printable_string(self) -> None:
assert hash(asn1.PrintableString("MyString")) == hash(
asn1.PrintableString("MyString")
)
assert hash(asn1.PrintableString("MyString")) != hash(
asn1.PrintableString("OtherString")
)

def test_repr_ia5_string(self) -> None:
my_string = "MyString"
assert repr(asn1.IA5String(my_string)) == f"IA5String({my_string!r})"
Expand All @@ -41,6 +49,14 @@ def test_invalid_ia5_string(self) -> None:
with pytest.raises(ValueError, match="invalid IA5String: café"):
asn1.IA5String("café")

def test_hash_ia5_string(self) -> None:
assert hash(asn1.IA5String("MyString")) == hash(
asn1.IA5String("MyString")
)
assert hash(asn1.IA5String("MyString")) != hash(
asn1.IA5String("OtherString")
)

def test_utc_time_as_datetime(self) -> None:
dt = datetime.datetime(
2000, 1, 1, 10, 10, 10, tzinfo=datetime.timezone.utc
Expand All @@ -53,6 +69,16 @@ def test_repr_utc_time(self) -> None:
)
assert repr(asn1.UTCTime(dt)) == f"UTCTime({dt!r})"

def test_hash_utc_time(self) -> None:
dt = datetime.datetime(
2000, 1, 1, 10, 10, 10, tzinfo=datetime.timezone.utc
)
other_dt = datetime.datetime(
2001, 1, 1, 10, 10, 10, tzinfo=datetime.timezone.utc
)
assert hash(asn1.UTCTime(dt)) == hash(asn1.UTCTime(dt))
assert hash(asn1.UTCTime(dt)) != hash(asn1.UTCTime(other_dt))

def test_invalid_utc_time(self) -> None:
with pytest.raises(
ValueError,
Expand Down Expand Up @@ -107,6 +133,18 @@ def test_repr_generalized_time(self) -> None:
)
assert repr(asn1.GeneralizedTime(dt)) == f"GeneralizedTime({dt!r})"

def test_hash_generalized_time(self) -> None:
dt = datetime.datetime(
2000, 1, 1, 10, 10, 10, 300000, tzinfo=datetime.timezone.utc
)
other_dt = datetime.datetime(
2001, 1, 1, 10, 10, 10, 300000, tzinfo=datetime.timezone.utc
)
assert hash(asn1.GeneralizedTime(dt)) == hash(asn1.GeneralizedTime(dt))
assert hash(asn1.GeneralizedTime(dt)) != hash(
asn1.GeneralizedTime(other_dt)
)

def test_invalid_generalized_time(self) -> None:
with pytest.raises(
ValueError,
Expand All @@ -129,6 +167,17 @@ def test_repr_bitstring(self) -> None:
== f"BitString(data={data!r}, padding_bits=2)"
)

def test_hash_bitstring(self) -> None:
assert hash(asn1.BitString(b"\x01\x02\x30", 2)) == hash(
asn1.BitString(b"\x01\x02\x30", 2)
)
assert hash(asn1.BitString(b"\x01\x02\x30", 2)) != hash(
asn1.BitString(b"\x01\x02\x40", 2)
)
assert hash(asn1.BitString(b"\x01\x02\x30", 2)) != hash(
asn1.BitString(b"\x01\x02\x30", 3)
)

def test_invalid_bitstring(self) -> None:
with pytest.raises(
ValueError,
Expand Down
Loading