-
Notifications
You must be signed in to change notification settings - Fork 726
Support for Raw Public Keys (RFC 7250) #2062
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
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2062 +/- ##
==========================================
+ Coverage 94.67% 94.72% +0.05%
==========================================
Files 102 102
Lines 23471 23753 +282
==========================================
+ Hits 22220 22499 +279
- Misses 1251 1254 +3 ☔ View full report in Codecov by Sentry. |
There is no reason for openssl to send Would be good to automate the running of rpk_example.rs against openssl in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's some comments/suggestions from a rough first pass.
Beyond the mechanics of the implementation I'm also interested in how this interacts with revocation. This is one of the aspects of raw public keys that I think deserves explicit attention in our documentation. As I understand it there isn't any mechanism for revocation checking when you've forgone certificates in favour of raw public keys. E.g. neither CRLs, nor OCSP will cover these entities.
Does that match your understanding? Should a client or server configuration that's configured with both CRLs and raw public keys fail in an obvious manner?
I'm curious what other reviewers think of this aspect. From my perspective it seems unfortunate/subpar to be doing this cast since we know the SPKI DER isn't certificate DER. It feels like we want a more type-safe way of representing the union of the two but I'm not sure what kind of flexibility we have to rework this without breaking changes. |
Speaking strictly at the level of public API changes, I would really like to avoid the 99.9999% of users -- who have no interest in or use for RPK -- from having to think about this. eg, |
Agreed. In Quinn we call this
Hmm, not sure -- I might prefer a parallel function if that means we can avoid sticking not-certificate DER in a |
RFC7250 has:
This means the encoding of a single x509 certificate with value 0xaa is:
and the encoding of a single raw public key is:
I think that means it is necessary to introduce an additional Do you need this feature for TLS1.2? |
I was thinking similar here. I'm fully in agreement that we want to avoid making the subject of raw public keys front-and-center for the average user, but it doesn't seem too egregious to have a parallel function like this. |
I guess that was just one example. To completely avoid putting an RPK into a
(I'm not sure that's a complete list.) |
In the RFC 7250 §6 a few comments are made regarding security considerations. There is no mention of either CRL or OCSP. Just different methods to authenticate raw public keys out-of-band. The assumption we made following @ctz's comment is that when someone decides to use RPK's they have pretty specific a-priori knowledge about the server and how it will be authenticated (and vice versa). I'm not sure if it should happen that a server is using both CRLs and raw public keys (unless using the What do you think? |
Thank you @ctz!! That clarifies things a lot. Very painful indeed. |
TLS1.3 has a |
Thanks for the review work! I've been lurking here for a while, as I'm helping a bit behind the scenes. It looks like there are a few decisions still to be made, and I wanted to provide my 2 cents. Could we avoid supporting TLS 1.2?It turns out that supporting TLS 1.2 adds some undesirable complexity. @cpu pointed out the branching it introduces, and @ctz that we'd probably need a new Where should the example code live?@djc voiced some concerns on letting the example code live in this repository, to which @cpu responded with his opinion. There's no consensus yet, but the issue is small enough that I guess it can be easily decided once @ctz tells us what he thinks.
|
Thanks for the feedback and all the work so far. Except for the comments on
If there's anything I'm forgetting please let me know! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's another round of feedback. Thanks!
In the RFC 7250 §6 a few comments are made regarding security considerations. There is no mention of either CRL or OCSP. Just different methods to authenticate raw public keys out-of-band.
Indeed :-( I think that's an oversight. I'm not sure it merits an errata but it feels like a missing piece to me.
The assumption we made following @ctz's #423 (comment) is that when someone decides to use RPK's they have pretty specific a-priori knowledge about the server and how it will be authenticated (and vice versa).
I agree with the assumption Ctz & you folks are making. I'm mainly advocating that this patchset should try to explicitly document the need for that a-priori knowledge in very strong & obvious language.
I'm not sure if it should happen that a server is using both CRLs and raw public keys (unless using the Acceptor api), in that case it might be a good idea to indeed fail.
You're right, I think the main concern I had (a client/server configuration with both CRLs and RPK configured) isn't possible based on this impl 👍 I think the Acceptor
API shouldn't introduce any additional risk.
I guess the issue boils down to choosing between (1) an inconsistency in the CertificateDer type, which sometimes contains an SPKI DER; and (2) consistent types at the cost of more complex internals. Personally, I feel inclined towards 1
I think I'm coming around to (1) as well short any other ideas that can be implemented within semver without complicating the API for the normal use-case.
I think we have consensus on the open questions now (?) - is it accurate to say we're waiting on another round of updates to finish some of the TODO's from this comment and then we're in the final stretch for code-review with an eye towards merging? |
@cpu I think so too! Thanks for this round of feedback. Most of it is already processed, and beginning next week I'll finish it and push everything. |
We've incorporated all the feedback and finished the todo's. Thanks for all the great help and feedback so far. Hopefully we can get this merged soon ;) Two remarks about the OpenSSL tests and the example:
The 'example' is not really in line with the rest of the examples right now, since it is living in The OpenSSL tests fail at the moment, most likely because the runner has OpenSSL 3.0 instead of 3.2. Is it better to add a new workflow for OpenSSL tests with OpenSSL 3.2 installed on the runner or is it okay to modify the existing workflow and run all tests against OpenSSL 3.2? |
Seems reasonable to me 👍
I think testing against one version is likely sufficient unless someone has strong opinions. 3.2 also ships certificate compression and it might be nice to have interop testing for that some day. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for persevering with this -- I feel it is pretty close.
I will catch up with the progress in this branch ~tomorrow/friday. Thank you! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for all the work on this!
Benchmark resultsInstruction countsSignificant differencesClick to expand
Other differencesClick to expand
Wall-timeSignificant differencesThere are no significant wall-time differences Other differencesClick to expand
Additional informationCheckout details:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice work, thank you for sticking with this.
I had a fair number of comments but they're all pretty insubstantial and should be easy fixes. I'm happy to +1 after that.
Now might be a good point to update the PR description as well if you're up for it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modulo the outstanding comments, this LGTM
Co-authored-by: Adolfo Ochagavía <github@adolfo.ochagavia.nl>
Co-authored-by: Adolfo Ochagavía <github@adolfo.ochagavia.nl>
Co-authored-by: Adolfo Ochagavía <github@adolfo.ochagavia.nl>
Co-authored-by: Adolfo Ochagavía <github@adolfo.ochagavia.nl>
- Added `requires_raw_public_keys()` to `ServerCertVerifier` and `ClientCertVerifier` traits, and `only_raw_public_keys()` to `ServerResolveCert` and `ClientResolveCert` traits, with default implementations returning `false`. - Introduced `AlwaysResolvesServerRawPublicKeys` and `AlwaysResolvesClientRawPublicKeys` for raw public key resolution. - Implemented `webpki::verify::verify_tls13_signature_with_spki()` using `webpki::RawPublicKeyEntity` for signature verification. - Added new error values to `PeerIncompatible`: - `IncorrectCertificateTypeExtension` - `UnsolicitedCertificateTypeExtension` - Expanded `handshake::ServerExtension` and `handshake::ClientExtension` to include `CertificateType` support. - Modified `server_conn::ClientHello` to include certificate type extensions for `Acceptor` API. - Made `tls13::key_schedule::derive_traffic_key()` and `tls13::key_schedule::derive_traffic_iv()` public for testing. Considerations: - To maintain backward compatibility, we use `CertificateDer` for Raw Public Keys instead of `SubjectPublicKeyInfoDer` in `CommonState::peer_certificates()` and the `cert` field of `CertifiedKey`. - We assume a-priori knowledge of the peer's authentication method when using Raw Public Keys, deviating slightly from RFC 7250. We do not offer both `RawPublicKey` and `X509`, though the RFC allows it. Co-authored-by: Adolfo Ochagavía <github@adolfo.ochagavia.nl>
Thanks for all the thorough feedback and consistent input! I just processed the last outstanding comments (sorry that it took a bit longer), and I think it's ready to merge! |
Co-authored-by: Adolfo Ochagavía <github@adolfo.ochagavia.nl>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work, thanks again :-)
@cpu @holodorum Hmm... This seems broken. After the latest rustls release, my peers stopped being able to handshake with rustls. I believe this is because this implementation is not RFC 7250 compliant. rustls server configs that have an X.509 cert but no raw public key will now terminate handshakes with a TLS alert, if those servers see a ClientHello advertising both X.509 and RPK as a supported client certificate type. The correct behavior per RFC 7250 Section 4.2 is to either omit the server_certificate_type extension, or to respond with client_certificate_type X.509. I tried to hack around this problem on the rustls server side but stumbled across an API design issue. In pseudocode, I would expect the API to look more like so:
Do you mind if I open GitHub issues and provide test cases for the aforementioned two issues? Or is the above intended behavior? Many thanks in advance. |
A separate issue sounds great. It will be easier to discuss there. |
This PR implements RFC 7250 (Raw Public Key support) for TLS 1.3. The relevant discussion can be found in this issue, with a detailed comment by @ctz explaining the necessary steps.
Main API changes:
ServerCertVerifier
andClientCertVerifier
traits now include arequires_raw_public_keys()
function, while theServerResolveCert
andClientResolveCert
traits now have aonly_raw_public_keys()
function. Both functions have default implementations that returnfalse
.ServerResolveCert
andClientResolveCert
that always resolve to a specific raw public key (AlwaysResolvesServerRawPublicKeys
,AlwaysResolvesClientRawPublicKeys
).webpki::verify::verify_tls13_signature_with_spki()
for signature verification. These functions usewebpki::RawPublicKeyEntity
, which was added torustls-webpki
in this PR.PeerIncompatible
:IncorrectCertificateTypeExtension
, raised when the local side expects Raw Public Keys but the peer sends an incorrect certificate type extension.UnsolicitedCertificateTypeExtension
, raised when the peer sends a certificate type extension, but the local side does not expect Raw Public Keys.handshake::ServerExtension
andhandshake::ClientExtension
:ClientExtension::ServerCertTypes
andClientExtension::ClientCertTypes
are used to communicate theCertificateType
s supported by the client.ServerExtension::ServerCertType
andServerExtension::ClientCertType
are used to communicate theCertificateType
chosen by the server.ServerExtension::ServerCertType
andServerExtension::ClientCertType
, are added toserver_conn::ClientHello
for use in theAcceptor
API.ServerHello
message to test our implementation. For this,tls13::key_schedule::derive_traffic_key()
andtls13::key_schedule::derive_traffic_iv()
were made public.Interoperability testing and example implementation:
raw_key_openssl_interop.rs
. This implementation is tested against an OpenSSL client and server to ensure interoperability with our implementation.Considerations:
CertificateDer
type for a Raw Public Key instead of the more conciseSubjectPublicKeyInfoDer
. This approach is used in:CommonState::peer_certificates()
cert
field of theCertifiedKey
struct.RawPublicKey
alongsideX509
, though the RFC permits it.Contributors:
This PR was created in collaboration with @aochagavia.