-
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 67 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 |
---|---|---|
|
@@ -201,7 +201,6 @@ pub static UNRECOGNIZED_EXTENSION: LazyPyImport = | |
LazyPyImport::new("cryptography.x509", &["UnrecognizedExtension"]); | ||
pub static EXTENSION: LazyPyImport = LazyPyImport::new("cryptography.x509", &["Extension"]); | ||
pub static EXTENSIONS: LazyPyImport = LazyPyImport::new("cryptography.x509", &["Extensions"]); | ||
pub static IPADDRESS: LazyPyImport = LazyPyImport::new("cryptography.x509", &["IPAddress"]); | ||
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. NB: I moved this down and renamed it to make it consistent with |
||
pub static NAME: LazyPyImport = LazyPyImport::new("cryptography.x509", &["Name"]); | ||
pub static RELATIVE_DISTINGUISHED_NAME: LazyPyImport = | ||
LazyPyImport::new("cryptography.x509", &["RelativeDistinguishedName"]); | ||
|
@@ -243,6 +242,7 @@ pub static DIRECTORY_NAME: LazyPyImport = | |
pub static UNIFORM_RESOURCE_IDENTIFIER: LazyPyImport = | ||
LazyPyImport::new("cryptography.x509", &["UniformResourceIdentifier"]); | ||
pub static DNS_NAME: LazyPyImport = LazyPyImport::new("cryptography.x509", &["DNSName"]); | ||
pub static IP_ADDRESS: LazyPyImport = LazyPyImport::new("cryptography.x509", &["IPAddress"]); | ||
pub static RFC822_NAME: LazyPyImport = LazyPyImport::new("cryptography.x509", &["RFC822Name"]); | ||
pub static OTHER_NAME: LazyPyImport = LazyPyImport::new("cryptography.x509", &["OtherName"]); | ||
pub static CERTIFICATE_VERSION_V1: LazyPyImport = | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,14 +3,18 @@ | |
// 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 pyo3::IntoPy; | ||
|
||
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 +47,125 @@ impl CryptoOps for PyCryptoOps { | |
} | ||
} | ||
|
||
struct FixedPolicy<'a>(Policy<'a, PyCryptoOps>); | ||
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. Is there a better name than 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. Hmm, maybe 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. How about 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. |
||
|
||
/// This enum exists solely to provide heterogeneously typed ownership for `OwnedPolicy`. | ||
enum SubjectOwner { | ||
// 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. | ||
Comment on lines
+53
to
+57
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. Doesn't need to block this, but would be good to send a PR that implements the method for Python >= 3.10. |
||
DNSName((pyo3::Py<pyo3::PyAny>, String)), | ||
IPAddress((pyo3::Py<pyo3::PyAny>, pyo3::Py<pyo3::types::PyBytes>)), | ||
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. I think it'd be marginally cleaner if there was a seperate 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. |
||
} | ||
|
||
self_cell::self_cell!( | ||
struct OwnedPolicy { | ||
owner: SubjectOwner, | ||
|
||
#[covariant] | ||
dependent: FixedPolicy, | ||
} | ||
); | ||
|
||
#[pyo3::pyclass( | ||
name = "ServerVerifier", | ||
module = "cryptography.hazmat.bindings._rust.x509" | ||
)] | ||
struct PyServerVerifier(OwnedPolicy); | ||
|
||
impl PyServerVerifier { | ||
fn as_policy(&self) -> &Policy<'_, PyCryptoOps> { | ||
&self.0.borrow_dependent().0 | ||
} | ||
} | ||
|
||
#[pyo3::pymethods] | ||
impl PyServerVerifier { | ||
#[getter] | ||
fn subject<'p>(&'p self, py: pyo3::Python<'p>) -> pyo3::PyResult<Option<&'p pyo3::PyAny>> { | ||
match self.0.borrow_owner() { | ||
SubjectOwner::DNSName((subject, _)) => Ok(Some(subject.as_ref(py))), | ||
SubjectOwner::IPAddress((subject, _)) => Ok(Some(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(( | ||
subject.into_py(py), | ||
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((subject.into_py(py), 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::<FixedPolicy<'_>, pyo3::PyErr>(FixedPolicy(Policy::new(PyCryptoOps {}, subject, time))) | ||
alex marked this conversation as resolved.
Show resolved
Hide resolved
|
||
})?; | ||
|
||
Ok(PyServerVerifier(policy)) | ||
} | ||
|
||
#[pyo3::pyclass( | ||
frozen, | ||
name = "Store", | ||
|
@@ -64,7 +187,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.
The
if not specified
should go in the policy builder docs, and should state that it's now at the time you call buildThere 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.
2e7bd70