Skip to content
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

Reinstate indefinite length and [UNIVERSAL 0] support in crypto/asn1 #2306

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

samuel40791765
Copy link
Contributor

Issues:

Addresses CryptoAlg-3037

Description of changes:

This is the first step of trying to reinstate support for BER in our OpenSSL ASN1 macro parsers. Proper support for BER consists of two parts, "indefinite length BER" and "implicitly tagged constructed BER strings". This is being done to properly support BER in our PKCS7 parsers, as we've made the transition to switch from CBB -> ASN1 macros for PKCS7 for now: 621bceb. We were using CBS_asn1_ber_to_der to translate BER -> DER for d2i_PKCS7, but the function has a caveat with implicitly-tagged constructed strings that causes encoding translations to be incomplete. We've decided to restore BER support in the macro parsers to resolve the PKCS7 compatibility issue and provide better OpenSSL compatibility for possible applications using OpenSSL's ASN.1 functions with BER encoding.

This commit is a revert of the two prior commits below. All "new logic" in crypto/asn1/tasn_dec.c was taken from these two commits.

The only smaller new changes are the following:

  1. I've only translated the comment code styling and added a few more comments clarifying some of the historic ASN1 shenanigans.
  2. There's also a change to the call to asn1_get_object_maybe_indefinite to call ASN1_get_object. This aligns with the original code in OpenSSL. I think we can clean up some of the changes done in cfe86a3, but I'd prefer to do so in a separate commit.

Testing:

  • I've reused the BER tests in our CBB parsers here so we're confident that the functionalities 1:1. This commit is reinstating indefinite length so I've only borrowed those corresponding tests for now.
  • I've generated a few new ASN1 test files with indefinite length as well. Indefinite length is more common among container types like SET and SEQUENCE, so we only pass those tests for now. Other indefinite length scenarios involve constructed form, but we don't have support for that just yet.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license.

@samuel40791765 samuel40791765 requested a review from a team as a code owner April 1, 2025 03:15
@codecov-commenter
Copy link

codecov-commenter commented Apr 1, 2025

Codecov Report

Attention: Patch coverage is 83.84615% with 21 lines in your changes missing coverage. Please review.

Project coverage is 78.76%. Comparing base (dc5fe84) to head (88ee67c).
Report is 12 commits behind head on main.

Files with missing lines Patch % Lines
crypto/asn1/tasn_dec.c 80.00% 17 Missing ⚠️
crypto/test/test_util.h 60.00% 3 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2306      +/-   ##
==========================================
+ Coverage   78.73%   78.76%   +0.02%     
==========================================
  Files         616      619       +3     
  Lines      107584   107861     +277     
  Branches    15286    15319      +33     
==========================================
+ Hits        84709    84959     +250     
- Misses      22220    22249      +29     
+ Partials      655      653       -2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@samuel40791765 samuel40791765 requested a review from justsmth April 1, 2025 21:04
Comment on lines +2378 to +2379
const std::vector<uint8_t> zero_tag_sequence = {0x30, 0x02, 0x00, 0x00};
const std::vector<uint8_t> zero_tag_set_any = {0x31, 0x02, 0x00, 0x00};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OpenSSL uses a variant of this that we might want to test as well:

  const std::vector<uint8_t> zero_tag_bad_sequence = {0x31, 0x02, 0x02, 0x00};

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh nice, I hadn't thought of looking into OpenSSL's tests. I'll scour for more if there's any.

Comment on lines 947 to 948
if (inf) {
expected_eoc++;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • OpenSSL performs an extra check here. (OpenSSL also declares expected_eoc as a uint32_t.):
    if (expected_eoc == UINT32_MAX) {
        ASN1err(ASN1_F_ASN1_FIND_END, ERR_R_NESTED_ASN1_ERROR);
        return 0;
    }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I was mainly focused on reverting the commit, but new changes around the code in OpenSSL definitely need to be taken note of as well. I'll look around to see if there's more.

Copy link
Contributor

@WillChilds-Klein WillChilds-Klein left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, much cleaner revert than I was expecting! Changes are looking good, just a few requests to bump up coverage.

if (expected) {
EXPECT_TRUE(obj);
} else {
EXPECT_FALSE(obj);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we also check that the error queue is populated here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! The errors will be different for each specific circumstance, but we can check that the error type is of ASN1 here.

// If indefinite length constructed find the real end.
if (inf) {
if (!asn1_find_end(&p, plen, inf)) {
goto err;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is uncovered. can we add a test case that drops the 00 00 trailer?

if (expected_eoc == 0) {
break;
}
len -= 2;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is uncovered. is there a nested-indefinite test SEQUENCE we can construct that will exercise this?

OPENSSL_PUT_ERROR(ASN1, ASN1_R_NESTED_ASN1_ERROR);
return 0;
}
if (inf) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the true case here is uncovered. can we add a test case to exercise it?

Copy link
Contributor Author

@samuel40791765 samuel40791765 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a few requests to bump up coverage.

Added a couple more test cases which should add coverage against the places we were looking at!

if (expected) {
EXPECT_TRUE(obj);
} else {
EXPECT_FALSE(obj);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! The errors will be different for each specific circumstance, but we can check that the error type is of ASN1 here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants