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

feat(admissions): implement parsing of admissions extension #11903

Merged
merged 18 commits into from
Nov 11, 2024

Conversation

hoefling
Copy link
Contributor

@hoefling hoefling commented Nov 6, 2024

This is the sixth PR for #11875 which adds parsing of the Admissions extension object. As with #11892, I am least confident in the parsing part in certificate.rs.

Please also advise on the names and descriptions of the certificates added to test vectors. Is there maybe a file naming policy I should stick to?

The certificates and their origins are also described in the #11875 comment.

@hoefling hoefling force-pushed the feature/extension/admissions/parse branch from 0b9ef86 to 330b8cc Compare November 6, 2024 01:27
@hoefling
Copy link
Contributor Author

hoefling commented Nov 6, 2024

Please also advise on this failure - could it be the introduced changes from this PR that caused it? Looks like a random failure thoughm, I also can't reproduce it locally in an ubuntu container. Log for future reference:

____________________ TestRSA.test_generate_rsa_keys[3-2048] ____________________
[gw1] linux -- Python 3.10.12 /__w/cryptography/cryptography/.nox/tests/bin/python3

self = <tests.hazmat.primitives.test_rsa.TestRSA object at 0x7f6f39949d80>
backend = <OpenSSLBackend(version: OpenSSL 3.0.2 15 Mar 2022, FIPS: False, Legacy: True)>
public_exponent = 3, key_size = 2048

    @pytest.mark.parametrize(
        ("public_exponent", "key_size"),
        itertools.product(
            (3, 65537),
            (1024, 1536, 2048),
        ),
    )
    def test_generate_rsa_keys(self, backend, public_exponent, key_size):
        if backend._fips_enabled:
            if key_size < backend._fips_rsa_min_key_size:
                pytest.skip(f"Key size not FIPS compliant: {key_size}")
            if public_exponent < backend._fips_rsa_min_public_exponent:
                pytest.skip(f"Exponent not FIPS compliant: {public_exponent}")
>       skey = rsa.generate_private_key(public_exponent, key_size, backend)

tests/hazmat/primitives/test_rsa.py:184: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

public_exponent = 3, key_size = 2048
backend = <OpenSSLBackend(version: OpenSSL 3.0.2 15 Mar 2022, FIPS: False, Legacy: True)>

    def generate_private_key(
        public_exponent: int,
        key_size: int,
        backend: typing.Any = None,
    ) -> RSAPrivateKey:
        _verify_rsa_parameters(public_exponent, key_size)
>       return rust_openssl.rsa.generate_private_key(public_exponent, key_size)
E       cryptography.exceptions.InternalError: Unknown OpenSSL error. This error is commonly encountered
E                           when another library is not cleaning up the OpenSSL error
E                           stack. If you are using cryptography with another library
E                           that uses OpenSSL try disabling it before reporting a bug.
E                           Otherwise please file an issue at
E                           https://github.com/pyca/cryptography/issues with
E                           information on how to reproduce this. (OpenSSL error)

.nox/tests/lib/python3.10/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py: InternalError

@alex
Copy link
Member

alex commented Nov 6, 2024 via email

@hoefling hoefling force-pushed the feature/extension/admissions/parse branch from 330b8cc to 7893e1e Compare November 6, 2024 20:20
@hoefling hoefling force-pushed the feature/extension/admissions/parse branch 2 times, most recently from 7e27924 to 0e01647 Compare November 7, 2024 00:02
@hoefling
Copy link
Contributor Author

hoefling commented Nov 7, 2024

@alex both #11892 and this one should be ready now, I cherry-picked the necessary changes from the bugfix for profession OIDs, the added diff is 526e083 for #11892 and 56bec7f for this one.

@hoefling hoefling force-pushed the feature/extension/admissions/parse branch from 0e01647 to ea70242 Compare November 7, 2024 09:09
@@ -546,6 +546,15 @@ Custom X.509 Vectors
This is an invalid certificate per :rfc:`5280` 4.2.1.12.
* ``malformed-san.pem`` - A certificate with a malformed SAN.
* ``malformed-ian.pem`` - A certificate with a malformed IAN.
* ``admissions_extension_cert_e256.pem`` - A certificate with the ``Admissions``
Copy link
Member

Choose a reason for hiding this comment

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

Can you do a separate PR with these? We like to review them separately from the implementation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@alex done! But unfortunately I had to extend the test and add an additional certificate in order to cover all corner cases - I hope this is fine.

Comment on lines 739 to 740
Some(data) => oid_to_py_oid(py, data)?.to_object(py),
None => py.None(),
Copy link
Member

Choose a reason for hiding this comment

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

Instead of doing to_object() on the some path, I'd recommend doing a bind(py) on the None path. It's a moderately more flexible pattern.

Same for the py_url path.

py_registration_number,
py_add_profession_info,
))?
.to_object(py);
Copy link
Member

Choose a reason for hiding this comment

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

to_object shouldn't be needed here.

fn parse_profession_infos<'a>(
py: pyo3::Python<'_>,
profession_infos: &asn1::SequenceOf<'a, ProfessionInfo<'a>>,
) -> CryptographyResult<pyo3::PyObject> {
Copy link
Member

Choose a reason for hiding this comment

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

This should return a Bound<'p, PyAny>, in general our habbit of returning PyObject all over hte place was a mistake we don't want to extend.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@alex done - I also went through other functions I added (parse_naming_authority and parse_admissions) and adjusted them as well; this also applies to your other suggestions. Please verify that I did what you indeed had in mind!

…overing the case of naming authority with no data

Signed-off-by: oleg.hoefling <[email protected]>
@hoefling hoefling force-pushed the feature/extension/admissions/parse branch from ea70242 to 4ef7e87 Compare November 9, 2024 13:15
…o complete rust coverage

Signed-off-by: Oleg Hoefling <[email protected]>
@hoefling hoefling force-pushed the feature/extension/admissions/parse branch from 1d8b0ee to 20f74b9 Compare November 9, 2024 13:34
Comment on lines 549 to 551
* ``admissions_extension_cert_synthetic.pem`` - A certificate containing
the ``Admissions`` extension with synthetic data,
signed by ``x509/custom/ca/rsa_ca.pem`` CA.
Copy link
Member

Choose a reason for hiding this comment

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

"synthetic data" is a bit unclear, can you describe the structure of this cert, what makes it interesting/what is it for testing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@alex I agree it wasn't the best naming, amended to have optional_data_not_provided suffix and a better description. Do you think it makes sense to also document the certificate generation, similar to this comment of mine or is it too much?

) -> CryptographyResult<pyo3::Bound<'p, pyo3::PyAny>> {
let py_id = match &authority.id {
Some(data) => oid_to_py_oid(py, data)?,
None => py.None().bind(py).clone().into_any(),
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
None => py.None().bind(py).clone().into_any(),
None => py.None().into_bound(py),

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@alex how did I miss this in pyo3's documentation 🙈

Comment on lines +549 to +555
* ``admissions_extension_optional_data_not_provided.pem`` -
A certificate containing the ``Admissions`` extension with multiple admissions,
signed by ``x509/custom/ca/rsa_ca.pem`` CA. The admissions in this certificate
are prepared using synthetic data to verify the possible corner cases are handled
by the parser correctly (an admission missing naming authority or admission
authority, a profession info missing naming authority or profession OIDs
or the registration number etc).
Copy link
Member

Choose a reason for hiding this comment

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

This is fine (and won't block merging), but FYI for the future if you contribute again (which I hope you do!), our usual approach would be: "The admissions in this certificate have every field filled in with all the available options."

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@alex I see - but I did also cover the case of all available fields fille out, too! The extension in the certificate contains five admissions in total, first one has all fields filled out, the remaining four test the corner cases. Either I still didn't a good job at describing the certificate then, will amend this. Or I didn't do the testing part properly - but wasn't sure how to test all the corner cases with missing fields, so I put every corner case in two certificates 🙈

@alex alex merged commit fef1270 into pyca:main Nov 11, 2024
60 checks passed
@hoefling hoefling deleted the feature/extension/admissions/parse branch November 11, 2024 07:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants