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

refactor: Split SafeBag serialization into its own function for PKCS#12 structures. #12452

Merged
merged 5 commits into from
Feb 14, 2025

Conversation

crbednarz
Copy link
Contributor

This PR covers the refactoring required for #12393, which mostly involves splitting the safebag serialization out of serialize_key_and_certificates.

Summary

The PKCS#12 format contains an AuthenticatedSafe which holds a collection of ContentInfo objects. Each of these ContentInfo objects may or maynot be encrypted, and will (typically) hold one or more SafeBags. These SafeBags are where the actual content of the PKCS#12 file is held, and in the context of this library will either be a certificate (CertBag) or private key (KeyBag or ShroudedKeyBag).

As it stands, serialize_key_and_certificates (in pkcs12.rs) performs the majority of roles in the serialization process.
If encryption is enabled, the process looks like this:

  1. serialize_key_and_certificates takes in collections of keys and certificates.
  2. Each certificate is placed in a CertBag, and the bags are placed in an encrypted ContentInfo.
  3. Each key is placed in a ShroudedKeyBag (encrypted), and the bags are placed in an unencrypted ContentInfo (to avoid re-encryption)
  4. All ContentInfo are added to the primary PKU structure and serialized.

If encryption is not enabled, the process is similar:

  1. serialize_key_and_certificates takes in collections of keys and certificates.
  2. Each certificate is placed in a CertBag, and the bags are placed in an unencrypted ContentInfo.
  3. Each key is placed in a KeyBag (unencrypted), and the bags are placed in an unencrypted ContentInfo
  4. All ContentInfo are added to the primary PKU structure and serialized.

The goal of this PR is to let serialize_key_and_certificates focus on just the SafeBag aspect of the process. Once it gathers all of its SafeBags, it will hand them off to a new function (serialize_bags) which will determine the appropriate structuring and serialization process. By making this change, new functions may be added which create more specialized collections of SafeBags, without duplicating the logic of serialize_key_and_certificates more than necessary.

Implementation

As noted above, the main change here is a new serialize_bags method. While the overall logic is very similar to what it was before the refactoring, I did want to touch on how this method actually functions, given that it accepts a single slice of bags.

Previously, SafeBags were separated by keys and certificates. This was useful as, with encryption enabled, the keys would be placed into ShroudedKeyBags which were not to be re-encrypted. However, this separation was occurring regardless of whether encryption was enabled. With the new, more generic serialize_bags, I found it made more sense to instead accept a single list of bags and only split apart ShroudedKeyBags if encryption was enabled. This way the function arguments stay simple and caller wouldn't need to worry about how the bags were being handled- simply that they needed to be provided.

Under the refactoring, the process looks like this:

  1. serialize_key_and_certificates takes in collections of keys and certificates.
  2. Each certificate is placed in a CertBag.
  3. Each key is placed in a KeyBag (unencrypted) or ShroudedKeyBag (encrypted)
  4. All SafeBags are handed to serialize_bags for final serialization.
    5a. If encryption is disabled, serialize_bags groups all SafeBags together and serializes them under a single ContentInfo.
    5b. If encryption is enabled, serialize_bags groups ShroudedKeyBags into their own unencrypted ContentInfo, and the remaining bags into an encrypted ContentInfo.
  5. All ContentInfo are added to the primary PKU structure and serialized.

@@ -297,29 +297,31 @@ fn cert_to_bag<'a>(
})
}

struct KeySerializationEncryption<'a> {
password: String,
Copy link
Member

Choose a reason for hiding this comment

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

What's the motivation to switch from PyBackedBytes to a String?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My thought here was that decode_encryption_algorithm's goal was to pull details about the requested encryption from Python into a more Rust friendly format. That way by the time we get to internal calls like serialize_bags we're no longer working with unprocessed Python input (with the exception of mac_alogrithm which would have involved greater refactoring).

The other advantage is that since we use the password for both serialize_key_and_certificates (encrypting ShroudedKeyBag) and serialize_bags (encrypting the ContentInfo), we can rely on the same KeySerializationEncryption object without pulling the string out of Python twice or passing an extra argument.

Copy link
Member

Choose a reason for hiding this comment

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

PyBackedBytes has teh advantage that it avoids an allocation+copy, so it'd be preferrable to stick to it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, I was under the impression the extra allocation was occurring regardless. But looking closer, I think you're right. I've corrected it.

Comment on lines 416 to 419
cryptography_x509::pkcs12::BagValue::ShroudedKeyBag(_) => &mut shrouded_safebags,
_ => &mut plain_safebags,
}
.push(safebag);
Copy link
Member

Choose a reason for hiding this comment

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

This would be easier to read as => shrouded_safebags.push(safebag) (and the same for the other path)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Completely agree. I've updated it. Not sure why I did it this way to begin with.

@alex alex enabled auto-merge (squash) February 14, 2025 00:17
@alex alex merged commit e254621 into pyca:main Feb 14, 2025
64 checks passed
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