Skip to content

Unbundle params from output types #328

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

Merged
merged 9 commits into from
Apr 23, 2025
Merged

Unbundle params from output types #328

merged 9 commits into from
Apr 23, 2025

Conversation

djc
Copy link
Member

@djc djc commented Apr 13, 2025

This is an alternative direction to #326. From here, I think we can add a nice Issuer builder API.

@djc djc requested review from cpu and est31 April 13, 2025 13:09
@est31
Copy link
Member

est31 commented Apr 13, 2025

I like this one better than #326, but not sure if it has some negative consequences if the params are not available for Certificate.

@djc
Copy link
Member Author

djc commented Apr 13, 2025

I like this one better than #326, but not sure if it has some negative consequences if the params are not available for Certificate.

The caller could always bundle up the params and the Certificate if they need both, right?

@djc
Copy link
Member Author

djc commented Apr 13, 2025

@est31 sorry, I just pushed a bunch more stuff -- will stop here and let you take another look.

@djc djc force-pushed the issuer branch 3 times, most recently from 74e48d2 to 9e88850 Compare April 13, 2025 19:56
@blheatwole
Copy link

I set my project to use rcgen = { version = "0.13", features = ["aws_lc_rs"], git = "https://github.com/rustls/rcgen", branch="issuer"} which put me at 9e88850.

After a few updates I was able to successfully sign using a mock Key Vault. I'm working on confirming with the real Azure Key Vault.

@cpu
Copy link
Member

cpu commented Apr 16, 2025

I'll give this a review by EOW.

Copy link
Member

@cpu cpu left a comment

Choose a reason for hiding this comment

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

Thanks, this is a nice improvement.

One of the two CI failures will be fixed with a rebase to pick up the version bump in main. The other is a real doctest issue:

---- rcgen\src\crl.rs - crl::CertificateRevocationList (line 23) stdout ----
error[E0407]: method `public_key` is not a member of trait `SigningKey`
 --> rcgen\src\crl.rs:31:3
  |
9 |   fn public_key(&self) -> &[u8] { &self.public_key }
  |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a member of trait `SigningKey`

error[E0407]: method `algorithm` is not a member of trait `SigningKey`
  --> rcgen\src\crl.rs:33:3
   |
11 |   fn algorithm(&self) -> &'static SignatureAlgorithm { &PKCS_ED25519 }
   |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a member of trait `SigningKey`

It seems like the doctest in question is more complicated than it needs to be in order to work around builds without crypto enabled.

WDYT about simplifying that example to avoid all the key pair trait stuff and just configuring the docs build to have the required feature? IMO the example is better when it focuses on the CRL bits and avoids a dummy remote key pair.

@djc djc force-pushed the issuer branch 4 times, most recently from cc9ce2b to 8ea0913 Compare April 22, 2025 10:29
@est31
Copy link
Member

est31 commented Apr 22, 2025

IMO the example is better when it focuses on the CRL bits and avoids a dummy remote key pair.

do we have examples with dummy remote key pairs? We should at least have some example.

@djc
Copy link
Member Author

djc commented Apr 22, 2025

IMO the example is better when it focuses on the CRL bits and avoids a dummy remote key pair.

do we have examples with dummy remote key pairs? We should at least have some example.

I kept the existing example which demonstrates both, I think?

@djc djc force-pushed the issuer branch 2 times, most recently from 6395fec to 2887998 Compare April 22, 2025 11:10
@est31
Copy link
Member

est31 commented Apr 22, 2025

I kept the existing example which demonstrates both, I think?

That's fine, @cpu was suggesting to remove that usage from the crl example.

In any case, wondering about the reasons behind the inverting of the KeyPair/SigningKey hierarchy: why do we do this?

I chose the old hierarchy because felt to me that it occurs less often that you want to use a RemoteKeyPair than if you want to use one with the whole key pair available. So I felt that users shouldn't be punished by extra complexity that a trait introduces (you need to find types compatible with that trait).

I'm not saying we shouldn't do it, but want to hear arguments :).

@est31
Copy link
Member

est31 commented Apr 22, 2025

From here, I think we can add a nice Issuer builder API.

@djc what do you envision for that API? just that I know which direction we are walking in.

@djc
Copy link
Member Author

djc commented Apr 22, 2025

I chose the old hierarchy because felt to me that it occurs less often that you want to use a RemoteKeyPair than if you want to use one with the whole key pair available. So I felt that users shouldn't be punished by extra complexity that a trait introduces (you need to find types compatible with that trait).

I'm not saying we shouldn't do it, but want to hear arguments :).

Basically, I think that it simplies the code. A bunch of public API unnecessarily took a &KeyPair directly which actually just needs the signing (and public key) capabilities. By making KeyPair implement SigningKey I feel that the simplicity of the external API is mostly intact (most usages should keep working), while function signatures can directly express their requirements for &impl PublicKeyData and &impl SigningKey.

From here, I think we can add a nice Issuer builder API.

@djc what do you envision for that API? just that I know which direction we are walking in.

I'm envisioning something like Issuer::from_cert_pem(&pem)?.with_key(&ca_key) or Issuer::builder().with_distinguished_name(DnType::OrganizationName, "foo").with_usage(KeyUsagePurpose::DigitalSignature).with_key(KeyPair::generate()?). I'd like to work towards a direction where CertificateParams, CertificateSigningRequestParams and CertificateRevocationListParams (or derivatives of these) implement a ToDer trait such that we can have an Issuer::sign(subject: &impl ToDer) (I have a private branch that moves in this direction). This would drop the CertificateParams::from_ca_cert*() methods.

@cpu
Copy link
Member

cpu commented Apr 22, 2025

IMO the example is better when it focuses on the CRL bits and avoids a dummy remote key pair.

do we have examples with dummy remote key pairs? We should at least have some example.

I kept the existing example which demonstrates both, I think?

That's fine, @cpu was suggesting to remove that usage from the crl example.

Yeah exactly, I think it's best to try to keep it one concept per example. If we don't have a separate example showing implementing the trait then I'd be 👍 to adding that with the bits removed from the CRL example.

I can take a crack at this as a follow-up if folks agree on the direction. No need to block this PR since it's a pre-existing example.

@djc djc mentioned this pull request Apr 22, 2025
@djc
Copy link
Member Author

djc commented Apr 22, 2025

@est31 I'll await your acknowledgement of my last comment before merging, let me know. 👍

@est31
Copy link
Member

est31 commented Apr 23, 2025

By making KeyPair implement SigningKey I feel that the simplicity of the external API is mostly intact (most usages should keep working)

It still works but if you try to write new code that uses the library, each trait is another point of friction: you need to look up implementations of that trait and which one is good for you. It's better to be economic with traits, and the old hierarchy ensured that only people who really needed the trait needed to learn about it (most people don't). Still, I think the change is good overall as it gives a little bit more flexibility (esp wrt stuff like Box vs Rc vs Arc, although you can always just put everything into Box<Arc<_>>).

I'd like to work towards a direction where CertificateParams, CertificateSigningRequestParams and CertificateRevocationListParams (or derivatives of these) implement a ToDer trait such that we can have an Issuer::sign(subject: &impl ToDer) (I have a private branch that moves in this direction).

The name would probably need to be adjusted, i.e. to CanSignDer, as simple strings of course can be converted to Der too but we don't want to expose that, we should only allow signing of stuff that is specified in the RFCs.

As I've explained above, I'm not a fan of even more traits in the API surface :). We should make sure that the main recommended API usage for signing doesn't involve traits, but maybe goes via inherent functions on the params types (at least most of them, we can be economic here).

Copy link
Member

@est31 est31 left a comment

Choose a reason for hiding this comment

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

fine with merging this one

@djc djc added this pull request to the merge queue Apr 23, 2025
Merged via the queue into main with commit 83a4398 Apr 23, 2025
28 checks passed
@djc djc deleted the issuer branch April 23, 2025 14:15
@djc
Copy link
Member Author

djc commented Apr 23, 2025

By making KeyPair implement SigningKey I feel that the simplicity of the external API is mostly intact (most usages should keep working)

It still works but if you try to write new code that uses the library, each trait is another point of friction: you need to look up implementations of that trait and which one is good for you. It's better to be economic with traits, and the old hierarchy ensured that only people who really needed the trait needed to learn about it (most people don't). Still, I think the change is good overall as it gives a little bit more flexibility (esp wrt stuff like Box vs Rc vs Arc, although you can always just put everything into Box<Arc<_>>).

In this case, because rcgen itself provides a single implementation of the trait, that seems like a clear way forward for users who want to stick with the simple path.

I'd like to work towards a direction where CertificateParams, CertificateSigningRequestParams and CertificateRevocationListParams (or derivatives of these) implement a ToDer trait such that we can have an Issuer::sign(subject: &impl ToDer) (I have a private branch that moves in this direction).

The name would probably need to be adjusted, i.e. to CanSignDer, as simple strings of course can be converted to Der too but we don't want to expose that, we should only allow signing of stuff that is specified in the RFCs.

As I've explained above, I'm not a fan of even more traits in the API surface :). We should make sure that the main recommended API usage for signing doesn't involve traits, but maybe goes via inherent functions on the params types (at least most of them, we can be economic here).

Sure, let's discuss in future PRs how to shape this API.

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