Repository: go
Issue: x/crypto/acme/autocert: add Manager.StapleOCSP
Labels: Proposal,Proposal-Accepted,FeatureRequest,Proposal-Crypto

OSCP stapling is considered a good practice to reduce load on OCSP servers and also to eliminate a round-trip to the CA when a user-agent verifies the certificate. `autocert.Manager` does not currently staple OCSP responses and it should. I propose that `autocert.Manager` be updated to staple the OCSP response when the certificate is generated and then treat OCSP expiration as a case of renewal in the `domainRenewal` logic.

Stapling would likely need to happen in two different places:

 * [`autocert.Manager.cert`](https://github.com/golang/crypto/blob/20e1d8d225ab8b19a0e7dc00fa5095216b8d6553/acme/autocert/autocert.go#L433) which is the cache fetch path for [`autocert.Manager.GetCertificate`](https://github.com/golang/crypto/blob/20e1d8d225ab8b19a0e7dc00fa5095216b8d6553/acme/autocert/autocert.go#L242). The code should load the certificate, check the expiration of the OCSP staple and in the absence of a staple or if the staple is expired it should attempt to fetch an OCSP response and staple it. On this path there should be a rather short deadline (perhaps 10 seconds) that, upon expiration, returns the certificate without an OCSP staple to prevent OCSP outages from unduly impacting users. The background renewal logic will eventually fix missing staples.
 * [`autocert.Manager.authorizeCert`](https://github.com/golang/crypto/blob/20e1d8d225ab8b19a0e7dc00fa5095216b8d6553/acme/autocert/autocert.go#L647) which is in the cache-miss path for `autocert.Manager.GetCertificate`. This code should continue to fetch a certificate from the CA and upon success also make an OCSP request and staple that response to the certificate. This is also used by [`autocert.domainRenewal.do`](https://github.com/golang/crypto/blob/20e1d8d225ab8b19a0e7dc00fa5095216b8d6553/acme/autocert/renewal.go#L91) which would then inherit the OCSP stapling logic.

WIthin the domain renewal logic the follow updates would be needed:

 * [`autocert.domainRenewal.renew`](https://github.com/golang/crypto/blob/20e1d8d225ab8b19a0e7dc00fa5095216b8d6553/acme/autocert/renewal.go#L55) performs the renewal of a certificate in the background of an `autocert.Manager`. This would need to instead check the both the OCSP staple expiration and the certificate expiration and renew whichever one was about to expire.
 * [`autocert.domainRenewal.next`](https://github.com/golang/crypto/blob/20e1d8d225ab8b19a0e7dc00fa5095216b8d6553/acme/autocert/renewal.go#L130) performs the schedule calculation for the renewal loop that runs as a goroutine for each certificate. This would need to be updated to consider upcoming OCSP staple expiration as a reason to trigger a renewal run.

It would also be possible to do OCSP stapling as a separate set of goroutines within `autocert.Manager` but that seems unnecessary given that the existing renewal logic seems easy to adapt to also handling this use-case.

Additionally, if this proposal is accepted I will add support for fetching OCSP responses and stapling them to a certificate to x/crypto/ocsp so that the functionality can be shared outside of autocert.

If there is consensus that this feature would be accepted and the approach is valid then I will implement this and create a CL.