-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Basic support for multiple SINGLERESP messages in one OCSP response, take 2 #6410
Conversation
I think we do need to figure out the problems you were seeing with the other approach because an iterable is the API we want here. If you have a gist of the code you were attempting to get working @alex and I can take a look. |
I'll try to get something as clean as possible pushed in the next day or two. |
Sorry for the delay - I haven't had much time to devote to this. There were enough changes on main I ended up just starting again from origin/main. In my perfect world, I would move move all the getter methods on OCSPResponse that refer just grab fields from SingleResponse (e.g. serial_number, certificate_status) onto SingleResponse, replace the OCSPResponse methods with pass-throughs for the "only one SINGLERESP" case, and then return a list of SingleRespones for OCSPResponse.responses. If I try to make SingleResponse a I can implement the IntoPy trait for that type, but have no idea what type to wrap it as. Any suggestions? Right now I'm just returning a placeholder since I can't figure out how to make SingleResponse Python-accessible. |
I suspect the right thing to look at is CRLs, which model basically this
pattern of one structure containing a bunch of sub-structures from the ASN.1
…On Mon, Oct 25, 2021 at 6:02 PM turettn ***@***.***> wrote:
Sorry for the delay - I haven't had much time to devote to this.
There were enough changes on main I ended up just starting again from
origin/main.
In my perfect world, I would move move all the getter methods on
OCSPResponse that refer just grab fields from SingleResponse (e.g.
serial_number, certificate_status) onto SingleResponse, replace the
OCSPResponse methods with pass-throughs for the "only one SINGLERESP" case,
and then return a list of SingleRespones for OCSPResponse.responses.
If I try to make SingleResponse a pyclass, I get #[pyclass] cannot have
generic paramters.
I can implement the IntoPy trait for that type, but have no idea what type
to wrap it as. Any suggestions? Right now I'm just returning a placeholder
since I can't figure out how to make SingleResponse Python-accessible.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#6410 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAAAGBBC7HFDL436RQT2VDLUIXHX5ANCNFSM5FZRGKUA>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
--
All that is necessary for evil to succeed is for good people to do nothing.
|
@alex Thanks for the pointer - it got me going down the right path. I think this is the iterator API you wanted. Everything passes CI, coverage looks good, etc... The only thing I can't figure out right now is getting the docs to build. It complains that:
except I can't figure out why it thinks that:
If you have any suggestions about the docs warning, or have any other thoughts about the code, please let me know. |
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.
It's getting close! Thanks for continuing to work on this.
#[pyo3::prelude::pyclass] | ||
struct OCSPResponseIterator { | ||
contents: OwnedRawOCSPResponse, | ||
cur_pos: usize, |
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.
Rather than indices, can we use the iterator, as seen in CRLs? That'll avoid the usize::MAX
stuff as well as the O(n^2)
behavior.
src/cryptography/x509/ocsp.py
Outdated
@@ -163,7 +163,73 @@ def extensions(self) -> x509.Extensions: | |||
""" | |||
|
|||
|
|||
class OCSPResponseIterator(metaclass=abc.ABCMeta): |
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.
This type isn't actually an iterator, this seems to be a single response and then response_iter
returns a typing.Iterator[ThisType]
?
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.
It's an iterator, in that it implements __next__
and __iter__
. The root of all my pain is that my understanding of PyO3 is that I have to return a pyclass object, which can't contain any lifetimes. This means I can't just return SingleResponse<'_>
, or turn it into a pyclass. To solve this problem, I have the iterator object with the index on it. Each time you call __next__
, it increments the index, then when you call the property methods (e.g. serial_number
), it burrows into contents, finds the appropriate SingleResponse
, extracts the requested field in a way Python can handle, and returns it.
If I understand what you're suggesting, you want next do something like:
let next_resp = slf
.contents
.borrow_basic_response()
.as_ref()
.unwrap()
.tbs_response_data
.responses
.unwrap_read()
.next()
.unwrap();
Then use next_resp
to create a new pyclass object. I can get close to this, since the __next__
method has slf.py()
to get the pyo3::Python
object, but when I try to advance...tbs_response_data.responses
it gives me an error about being unable to borrow the result of unwrap_read()
as mutable.
If you have any advice on how to get tbs_response_data.responses
into a state where I can next()
through it, that would be great.
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 ABC doesn't contain __next__
, that's what's confusing me. This seems to be an ABC for SingleResponse
.
src/cryptography/x509/ocsp.py
Outdated
@@ -163,7 +163,73 @@ def extensions(self) -> x509.Extensions: | |||
""" | |||
|
|||
|
|||
class OCSPResponseIterator(metaclass=abc.ABCMeta): |
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 ABC doesn't contain __next__
, that's what's confusing me. This seems to be an ABC for SingleResponse
.
@alex Regarding the right answer seems to involve |
Why is the design to have an iterator where |
The mutating iterator was the way I found that avoided a pyclass object with lifetimes, while also avoided a truly ridiculous amount of cloning. The last change I made clearly broke CI badly - I assume this is something to do with the metaclass vs just subclassing. I'm on the road for a couple days and will fix it once I'm back. |
You may want to take a look at crl.rs, which has an iterator in Rust that
yields that from an ASN.1 structure. It should be the exact same pattern as
is required here.
…On Sat, Dec 11, 2021 at 11:43 PM turettn ***@***.***> wrote:
The mutating iterator was the way I found that avoided a pyclass object
with lifetimes, while also avoided a truly ridiculous amount of cloning.
The last change I made clearly broke CI badly - I assume this is something
to do with the metaclass vs just subclassing. I'm on the road for a couple
days and will fix it once I'm back.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#6410 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAAAGBFWPBEYOVSB5SLF353UQQSAPANCNFSM5FZRGKUA>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
--
All that is necessary for evil to succeed is for good people to do nothing.
|
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.
Very very close! Thanks for your work on this, and sorry I've been so slow to review.
Oh, please add an entry to the CHANGELOG for this. |
Instead of throwing an exception when encountering a OCSP response with multiple SINGLERESPs, throw the exception when attempting to pull a single structure if multiple are present. Add a response_iter property to the OCSP Response object, which allows for iteration through all the SINGLERESPs, and properties to be individually accessed for each.
@alex No problem - it's that time of year. I made the requested changes. Let me know if it needs anything else. |
LGTM! Ping'd @reaperhulk to see if he wants to take a look before I merge. Thanks for all your work on this! |
Second attempt at #6320, this time in Rust.
#5124 identified an issue where OCSP responses with multiple certificate blocks were rejected violently. #5316 cleaned this up with an intelligent error message.
Motivation-wise, there's at least one commonly used OCSP server that pre-generates OCSP responses in batches of 20, then when it receives a OCSP request it simply finds the correct response file and fires it back. These servers seem to be mostly used in environments with extremely large client certificate roll-outs (e.g. the NIPRNet, where every user has a physical token with a client certificate). On these networks, servers are constantly checking client certificate validity due to the high rate of revocations (e.g. cards are lost\stolen, eligibility for access changes, etc...). The original issue referenced above provided the ocsp-army.deps.mil-resp.der test vector, which appears to have come from one such network.
This patch does something very similar to what we had discussed in #6320. We had discussed trying to expose the individual single-response objects via an iterable to Python, but went down the rabbit-hole of lifetimes, and discovered that I clearly don't understand them as well as I should. Additionally, this (and most of things I could try that actually compiled) broke backward-compatibility with the existing API, which I was trying really hard to avoid.
I am by no means a Rust expert - if you can suggest a way to clean any of this up, I'm happy to submit follow up revisions.
Thanks for taking a look!