Skip to content

Commit 972a7b5

Browse files
woodruffwalex
andauthored
verification: add test_verify_tz_aware (#10229)
* verification: add test_verify_tz_aware Signed-off-by: William Woodruff <[email protected]> * py_to_datetime handles tzinfo, add test Signed-off-by: William Woodruff <[email protected]> * Update src/rust/src/x509/common.rs Co-authored-by: Alex Gaynor <[email protected]> * x509/common: coverage for the coverage god Signed-off-by: William Woodruff <[email protected]> --------- Signed-off-by: William Woodruff <[email protected]> Co-authored-by: Alex Gaynor <[email protected]>
1 parent 41daf2d commit 972a7b5

File tree

2 files changed

+50
-6
lines changed

2 files changed

+50
-6
lines changed

src/rust/src/x509/common.rs

+15-6
Original file line numberDiff line numberDiff line change
@@ -503,13 +503,22 @@ pub(crate) fn py_to_datetime(
503503
py: pyo3::Python<'_>,
504504
val: &pyo3::PyAny,
505505
) -> pyo3::PyResult<asn1::DateTime> {
506+
// We treat naive datetimes as UTC times, while aware datetimes get
507+
// normalized to UTC before conversion.
508+
let val_utc = if val.getattr(pyo3::intern!(py, "tzinfo"))?.is_none() {
509+
val
510+
} else {
511+
let utc = types::DATETIME_TIMEZONE_UTC.get(py)?;
512+
val.call_method1(pyo3::intern!(py, "astimezone"), (utc,))?
513+
};
514+
506515
Ok(asn1::DateTime::new(
507-
val.getattr(pyo3::intern!(py, "year"))?.extract()?,
508-
val.getattr(pyo3::intern!(py, "month"))?.extract()?,
509-
val.getattr(pyo3::intern!(py, "day"))?.extract()?,
510-
val.getattr(pyo3::intern!(py, "hour"))?.extract()?,
511-
val.getattr(pyo3::intern!(py, "minute"))?.extract()?,
512-
val.getattr(pyo3::intern!(py, "second"))?.extract()?,
516+
val_utc.getattr(pyo3::intern!(py, "year"))?.extract()?,
517+
val_utc.getattr(pyo3::intern!(py, "month"))?.extract()?,
518+
val_utc.getattr(pyo3::intern!(py, "day"))?.extract()?,
519+
val_utc.getattr(pyo3::intern!(py, "hour"))?.extract()?,
520+
val_utc.getattr(pyo3::intern!(py, "minute"))?.extract()?,
521+
val_utc.getattr(pyo3::intern!(py, "second"))?.extract()?,
513522
)
514523
.unwrap())
515524
}

tests/x509/verification/test_verification.py

+35
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,38 @@ def test_store_rejects_empty_list(self):
103103
def test_store_rejects_non_certificates(self):
104104
with pytest.raises(TypeError):
105105
Store(["not a cert"]) # type: ignore[list-item]
106+
107+
108+
class TestServerVerifier:
109+
@pytest.mark.parametrize(
110+
("validation_time", "valid"),
111+
[
112+
# 03:15:02 UTC+2, or 1 second before expiry in UTC
113+
("2018-11-16T03:15:02+02:00", True),
114+
# 00:15:04 UTC-1, or 1 second after expiry in UTC
115+
("2018-11-16T00:15:04-01:00", False),
116+
],
117+
)
118+
def test_verify_tz_aware(self, validation_time, valid):
119+
# expires 2018-11-16 01:15:03 UTC
120+
leaf = _load_cert(
121+
os.path.join("x509", "cryptography.io.pem"),
122+
x509.load_pem_x509_certificate,
123+
)
124+
125+
store = Store([leaf])
126+
127+
builder = PolicyBuilder().store(store)
128+
builder = builder.time(
129+
datetime.datetime.fromisoformat(validation_time)
130+
)
131+
verifier = builder.build_server_verifier(DNSName("cryptography.io"))
132+
133+
if valid:
134+
assert verifier.verify(leaf, []) == [leaf]
135+
else:
136+
with pytest.raises(
137+
x509.verification.VerificationError,
138+
match="cert is not valid at validation time",
139+
):
140+
verifier.verify(leaf, [])

0 commit comments

Comments
 (0)