Skip to content

Handshake returns cleaned peer certificates #5311

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 12 commits into from
Sep 7, 2019

Conversation

yschimke
Copy link
Collaborator

@yschimke yschimke commented Jul 29, 2019

#5138

Pass through clean certificates with only a single pass of cert cleaning.

@yschimke
Copy link
Collaborator Author

@swankjesse Any advice for testing here? We test RealConnection through CallTest, and the JVM code around KeyManager will likely send the clean certificates.

@swankjesse
Copy link
Collaborator

This situation is frustrating.

Cleaning the certificates could be non-trivial, so this has the potential of breaking or slowing code that works otherwise.

I’d like to land 4.1 without this, and then put this in 4.2 early with lots of bake time.

@swankjesse
Copy link
Collaborator

That said, the change itself looks great!

As for testing . . . wanna test with a fake cleaner?

@yschimke yschimke added this to the 4.2 milestone Jul 29, 2019
@yschimke
Copy link
Collaborator Author

That said, the change itself looks great!

As for testing . . . wanna test with a fake cleaner?

That route isn't great. It makes new internal APIs for setting the cleaner in OkHttpClient.Builder. Cleaner currently comes from the Platform, or via the TrustManager if you customise that. But they are on a public API, so they tend to show up for Java clients.

image

@yschimke
Copy link
Collaborator Author

@swankjesse I'm tempted to move this into sslSocketSession.handshake()

But also wondering whether we should retain the uncleaned certificates? Handshake could have both, potentially interesting when trying to understand why a decision was made. e.g. if certificates are served that have multiple root CAs for different clients. Not sure if that happens in the wild.

@swankjesse
Copy link
Collaborator

We can hide things from Java clients with a - prefix on the @JvmName. Does that help? (We should probably do this universally!)

@yschimke yschimke marked this pull request as ready for review August 26, 2019 23:11
@yschimke yschimke requested a review from swankjesse August 27, 2019 12:46
@swankjesse
Copy link
Collaborator

Can we always use the cleaner from the pinner? What’s the benefit of wiring it through the connection? If that one is null we shouldn’t bother.

I’m still unsatisfied with the performance cost here. It has a non-zero cost and a nearly-zero benefit.

@yschimke
Copy link
Collaborator Author

yschimke commented Sep 4, 2019

Can we always use the cleaner from the pinner? What’s the benefit of wiring it through the connection? If that one is null we shouldn’t bother.

I’m still unsatisfied with the performance cost here. It has a non-zero cost and a nearly-zero benefit.

OK, so cost is cleaning certificates when we aren't currently doing certificate pinning.

The benefit is that callers can rely on the handshake certificates being those that we trust and validate. If we choose to expose the handshake certificates (public API), I'd argue that we must provide the validated ones.

I'll take a look if we can defer cleaning until these are requested in the handshake.

@yschimke
Copy link
Collaborator Author

yschimke commented Sep 4, 2019

Now deferring cleaning until we either

  1. need to check certificate pinning (no cost)
  2. caller looks at the peer certificates (this incurs additional cost)

But only done at most once.

@yschimke yschimke changed the title Pass through clean certificates in Handshake Handshake returns cleaned peer certificates Sep 4, 2019
@yschimke
Copy link
Collaborator Author

yschimke commented Sep 4, 2019

n.b. I removed the test because the change otherwise becomes awkward with OkHttpClient, since that constructor generally always fetches a new cleaner from Platform :(

Copy link
Collaborator

@swankjesse swankjesse left a comment

Choose a reason for hiding this comment

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

I love the structure of this. You get clean certificates if you use ’em, but you don’t pay until use. Very nice.

}
}

internal fun check(hostname: String, cleanedPeerCertificatesFn: () -> List<X509Certificate>) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think passing a lambda here is too surprising; it might be better to make this take cleanedCertificates, and then make the one callsite guard the call with like hasPins() check

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's not a simple check without cost if there is pinning in place. e.g. a client configures pinning on their API host, but not on the CDN (unknown hosts).

In this case, findMatchingPins(hostname) is the check we need to do externally rather than hasPins.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's also internal API, for a class with public API.

Copy link
Collaborator

Choose a reason for hiding this comment

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

SGTM

}
}

internal fun check(hostname: String, cleanedPeerCertificatesFn: () -> List<X509Certificate>) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

SGTM

@get:JvmName(
"localCertificates") val localCertificates: List<Certificate>,

// Delayed provider of peerCertificates, to allow lazy cleaning.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nice.

@swankjesse swankjesse merged commit ba2c676 into square:master Sep 7, 2019
@swankjesse
Copy link
Collaborator

I think this is a very nice improvement.

@yschimke yschimke deleted the pass_through_cleancerts branch September 9, 2019 11:03
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.

None yet

2 participants