-
Notifications
You must be signed in to change notification settings - Fork 17.8k
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
x/crypto/cryptobyte: support reading implicitly-tagged values #64811
Comments
Thanks for the report. |
@FiloSottile @rolandshoemaker @golang/security per owners |
I don't think we want to add transparent support for IMPLICIT values, if you expect to parse an EXPLICIT field, but get an IMPLICIT field, that seems like it should be an error (as it is now). We've mostly avoided adding methods for all of the IMPLICIT variations of the parsing methods, since we've not really run into them all that much as of yet. The one exception to this so far is AddASN1Int64WithTag/ReadASN1Int64WithTag. We could add ReadASN1BooleanWithTag/ReadOptionalASN1BooleanWithTag (...or something with a better name), but I feel like going down this path risks the combinatorial explosion of adding IMPLICIT versions of everything we currently have. I cannot form the exact API in my mind right now, but it seems like there is perhaps a nicer composable style API we could implement here that would allow us to just provide all of the pieces and allow the user to build the thing they want (without having to re-implement optional semantics etc). |
Absolutely agreed, parsing explicit vs implicit should be segregated somehow so you don't accidentally get the wrong thing.
Thinking out loud: What if the asn1 subpackage's It could become a struct, rather than just a bare uint8. From there the simple solution would be to have it carry an explicit vs implicit boolean, and methods in cryptobyte could change their behavior depending on that. But perhaps an even more exciting solution would be something like
and then the cryptobyte methods would make sure that the whole tag (both layers of it, in the explicit case) matches, and would use .Peek() to ensure that the next length byte is exactly 2 less than the length byte skipped over by the |
Yeah Tag would probably make the most sense, although we've somewhat backed ourselves into a corner by making it just a typed uint8, so there isn't really an obvious place to hide the relevant information. |
Maybe the cryptobyte methods are updated to take a Tag interface, the uint8 Tag type is given a dummy method to implement that interface, and the Of course, a downside of this approach is that it means that default (i.e. current) tags would be assumed to be Implicit, which is a change from current behavior :/ |
Coming back to this, the API design of this package is clearly not ideal, and isn't particularly conducive to making these kinds of changes cleanly. There are a number of significant changes I'd like to make, but I think those need to be put off until we have the resources to do a v2 of the package. Until we can completely overhaul the package, I think tolerating the combinatorial explosion in the number of methods is mostly acceptable. It's not exactly pretty, but this is an explicit parser, and having a ton of methods that just do what they say is probably fine. In terms of a concrete proposal, essentially for each of the existing |
This proposal has been added to the active column of the proposals project |
For just the Boolean type, the new APIs would be:
Note There is an open question of whether we want to implement these APIs for the rest of the ASN1 types (BitString, Bytes, Element, Enum, GeneralizedTime, Integer, ObjectIdentifier, and UTCTime). Unless there is a current need for them (@aarongable have you run into this same problem for the other types?), I think it's reasonable to leave them out, and just implement the Boolean API for now, and then revisit this again for other types when they are needed. |
This API makes sense to me. I don't think there's a strong argument for implementing the same for all types -- for example, cryptobyte currently only has "ReadOptionalASN1Foo" methods for Boolean, Integer, and OctetString, so there's prior art for not providing every possible permutation of a method. That said, RFC 5280 does have examples of some of those types:
|
Based on the discussion above, this proposal seems like a likely accept. The new API is in #64811 (comment). |
No change in consensus, so accepted. 🎉 The new API is in #64811 (comment). |
@rolandshoemaker I have an implementation here if it helps. |
Go version
go version go1.21.5 linux/amd64
What operating system and processor architecture are you using (
go env
)?What did you do?
Attempted to use
ReadOptionalASN1Boolean
to read theonlyContainsCACerts
andonlyContainsUserCerts
fields of a CRL'sIssuingDistributionPoint
extension.You can see my attempt, failure, and resulting investigation here. I also have a Go playground which minimally reproduces the situation:
What did you expect to see?
I expected ReadOptionalASN1Boolean to succeed when reading the onlyContainsCACerts field. That field is an optional boolean, so ReadOptionalASN1Boolean should successfully read it.
What did you see instead?
ReadOptionalASN1Boolean fails when attempting to read the onlyContainsCACerts field. This is because ReadOptionalASN1Boolean only works for explicitly-tagged fields. It's expecting to get a five-byte sequence, as in my second example above: a context-specific tag and length, wrapping a normal boolean tag, length, and value.
But the IssuingDistributionPoint extension is defined (in both X.509 and RFC 5280) in an ASN.1 module that sets DEFINITIONS IMPLICIT TAGS, so these fields are implicitly-tagged, not explicitly-tagged. They're only correctly encoded as a three bytes: a context-specific tag and length followed immediately by the single-byte boolean itself.
There are lots of IMPLICIT fields in x509, so it's somewhat concerning that the cryptobyte parser only has facilities for parsing EXPLICIT fields.
Note: Perhaps I should have expected the behavior I saw, because the documentation of the methods in question does say (emphasis added) "ReadOptionalASN1Boolean attempts to read an optional ASN.1 BOOLEAN explicitly tagged with tag...". But I think this single word is easy to miss, especially when it's being used as a term of art rather than as plain english. And regardless of the behavior of the existing methods, I still think it would be valuable to have methods which can parse implicitly-tagged fields.
The text was updated successfully, but these errors were encountered: