-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Path validation: builder/verifier API skeletons #9405
Changes from 70 commits
36fef28
e4e300f
35a4e28
bb76afe
3a67770
6cabf0d
8157ce4
5a69a28
539a7b2
f6933ca
91ef5c4
fbe4132
2b46371
1f5ee07
c96f4b8
5493338
d900a6e
0789d73
7852430
a5154e1
08df667
d2d54c4
2c1017b
fafafcd
35c3d9f
9b62075
438e59d
318393c
cf3ad95
27f7628
00af5ca
7d1b0c1
9ec4154
92804f1
6b45a77
e956dd6
26cee8b
aa45a4f
7f3cef1
69538d8
1f7dc3a
d355128
a4e7d34
083ad34
f217a36
cd3887a
fb078fc
a48d6af
9920ef1
1aa0253
640d735
96269ce
8aefa1c
00d960e
03fb4b7
9ffbc9d
ad90a81
3610191
05fb380
d0842ce
924c749
7cad491
92259c7
a5a88e6
5e18f8e
de2c7b0
a1a7c57
2e7bd70
35f5e6f
3c036bc
fbb2e6a
0f91265
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,14 +3,17 @@ | |
// for complete details. | ||
|
||
use cryptography_x509::certificate::Certificate; | ||
use cryptography_x509_validation::ops::CryptoOps; | ||
use cryptography_x509_validation::{ | ||
ops::CryptoOps, | ||
policy::{Policy, Subject}, | ||
types::{DNSName, IPAddress}, | ||
}; | ||
|
||
use crate::error::{CryptographyError, CryptographyResult}; | ||
use crate::types; | ||
use crate::x509::certificate::Certificate as PyCertificate; | ||
use crate::x509::common::{datetime_now, datetime_to_py, py_to_datetime}; | ||
use crate::x509::sign; | ||
use crate::{ | ||
error::{CryptographyError, CryptographyResult}, | ||
types, | ||
x509::certificate::Certificate as PyCertificate, | ||
}; | ||
|
||
pub(crate) struct PyCryptoOps {} | ||
|
||
|
@@ -43,6 +46,129 @@ impl CryptoOps for PyCryptoOps { | |
} | ||
} | ||
|
||
struct PyCryptoPolicy<'a>(Policy<'a, PyCryptoOps>); | ||
|
||
/// This enum exists solely to provide heterogeneously typed ownership for `OwnedPolicy`. | ||
enum SubjectOwner { | ||
DNSName(String), | ||
IPAddress(pyo3::Py<pyo3::types::PyBytes>), | ||
} | ||
|
||
self_cell::self_cell!( | ||
struct OwnedPolicy { | ||
owner: SubjectOwner, | ||
|
||
#[covariant] | ||
dependent: PyCryptoPolicy, | ||
} | ||
); | ||
|
||
#[pyo3::pyclass( | ||
name = "ServerVerifier", | ||
module = "cryptography.hazmat.bindings._rust.x509" | ||
)] | ||
struct PyServerVerifier { | ||
// TODO: Switch this to `Py<PyString>` once Pyo3's `to_str()` preserves a | ||
// lifetime relationship between an a `PyString` and its borrowed `&str` | ||
// reference in all limited API builds. PyO3 can't currently do that in | ||
// older limited API builds because it needs `PyUnicode_AsUTF8AndSize` to do | ||
// so, which was only stabilized with 3.10. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pretty sure this goes in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, bad copy. Thanks. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
py_subject: pyo3::Py<pyo3::PyAny>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
policy: OwnedPolicy, | ||
} | ||
|
||
impl PyServerVerifier { | ||
fn as_policy(&self) -> &Policy<'_, PyCryptoOps> { | ||
&self.policy.borrow_dependent().0 | ||
} | ||
} | ||
|
||
#[pyo3::pymethods] | ||
impl PyServerVerifier { | ||
#[getter] | ||
fn subject<'p>(&'p self, py: pyo3::Python<'p>) -> pyo3::PyResult<Option<&'p pyo3::PyAny>> { | ||
Ok(Some(self.py_subject.as_ref(py))) | ||
} | ||
|
||
#[getter] | ||
fn validation_time<'p>(&self, py: pyo3::Python<'p>) -> pyo3::PyResult<&'p pyo3::PyAny> { | ||
datetime_to_py(py, &self.as_policy().validation_time) | ||
} | ||
} | ||
|
||
fn build_subject_owner( | ||
py: pyo3::Python<'_>, | ||
subject: &pyo3::Py<pyo3::PyAny>, | ||
) -> pyo3::PyResult<SubjectOwner> { | ||
let subject = subject.as_ref(py); | ||
|
||
if subject.is_instance(types::DNS_NAME.get(py)?)? { | ||
let value = subject | ||
.getattr(pyo3::intern!(py, "value"))? | ||
.downcast::<pyo3::types::PyString>()?; | ||
|
||
Ok(SubjectOwner::DNSName(value.to_str()?.to_owned())) | ||
} else if subject.is_instance(types::IP_ADDRESS.get(py)?)? { | ||
let value = subject | ||
.getattr(pyo3::intern!(py, "_packed"))? | ||
.call0()? | ||
.downcast::<pyo3::types::PyBytes>()?; | ||
|
||
Ok(SubjectOwner::IPAddress(value.into())) | ||
} else { | ||
Err(pyo3::exceptions::PyTypeError::new_err( | ||
"unsupported subject type", | ||
)) | ||
} | ||
} | ||
|
||
fn build_subject<'a>( | ||
py: pyo3::Python<'_>, | ||
subject: &'a SubjectOwner, | ||
) -> pyo3::PyResult<Option<Subject<'a>>> { | ||
match subject { | ||
SubjectOwner::DNSName(dns_name) => { | ||
let dns_name = DNSName::new(dns_name) | ||
.ok_or_else(|| pyo3::exceptions::PyValueError::new_err("invalid domain name"))?; | ||
|
||
Ok(Some(Subject::DNS(dns_name))) | ||
} | ||
SubjectOwner::IPAddress(ip_addr) => { | ||
let ip_addr = IPAddress::from_bytes(ip_addr.as_bytes(py)) | ||
.ok_or_else(|| pyo3::exceptions::PyValueError::new_err("invalid IP address"))?; | ||
|
||
Ok(Some(Subject::IP(ip_addr))) | ||
} | ||
} | ||
} | ||
|
||
#[pyo3::prelude::pyfunction] | ||
fn create_server_verifier( | ||
py: pyo3::Python<'_>, | ||
subject: pyo3::Py<pyo3::PyAny>, | ||
time: Option<&pyo3::PyAny>, | ||
) -> pyo3::PyResult<PyServerVerifier> { | ||
let time = match time { | ||
Some(time) => py_to_datetime(py, time)?, | ||
None => datetime_now(py)?, | ||
woodruffw marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}; | ||
|
||
let subject_owner = build_subject_owner(py, &subject)?; | ||
let policy = OwnedPolicy::try_new(subject_owner, |subject_owner| { | ||
let subject = build_subject(py, subject_owner)?; | ||
Ok::<PyCryptoPolicy<'_>, pyo3::PyErr>(PyCryptoPolicy(Policy::new( | ||
PyCryptoOps {}, | ||
subject, | ||
time, | ||
))) | ||
})?; | ||
|
||
Ok(PyServerVerifier { | ||
py_subject: subject, | ||
policy, | ||
}) | ||
} | ||
|
||
#[pyo3::pyclass( | ||
frozen, | ||
name = "Store", | ||
|
@@ -64,7 +190,9 @@ impl PyStore { | |
} | ||
|
||
pub(crate) fn add_to_module(module: &pyo3::prelude::PyModule) -> pyo3::PyResult<()> { | ||
module.add_class::<PyServerVerifier>()?; | ||
module.add_class::<PyStore>()?; | ||
module.add_function(pyo3::wrap_pyfunction!(create_server_verifier, module)?)?; | ||
|
||
Ok(()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NB: I moved this down and renamed it to make it consistent with
DNS_NAME
+ co-locate it with the other exported name types (DNS_NAME
andRFC822_NAME
).