Subject: [TLS] OCSP Stapling confusion

From: Daniel Kahn Gillmor <[email-masked]>
Date: Sun, 09 Dec 2018 20:11:48 -0500

I was trying to sort out concrete, specific advice for OCSP stapling
that provides security benefits for the server (and not just performance
and privacy benefits).  Either i'm easily confused, or it's a mess.  I
hope it's the former, please unconfuse me!

Given the IAB's statement from nearly two years ago about the value in
OCSP stapling [0], it's a little distressing to me how difficult it
seems to be to get it right in today's landscape.

  [0]   https://mailarchive.ietf.org/arch/msg/ietf-announce/j0JIIGhD-xvbaWxtGm2-pFK0JQ8

In the bullet points below, i'll refer to the status requester as the
"client", and the party returning the status as the "server".  I'm aware
that the roles could be reversed if the server is requesting client
certs, but i'm going to punt on that wrinkle for now, since the
straightforward/common case is confusing enough.

 * the status_request TLS extension doesn't provide a mechanism for
   stapling OCSP for intermediate certs.

 * the status_request_v2 extension does provide a mechanism for stapling
   intermediate OCSP, but it is deprecated by TLS 1.3 and TLS servers
   responding with a TLS 1.3 handshake MUST NOT send
   status_request_v2 in the ServerHello.

 * the location of the stapled response has changed between TLS 1.2 and
   TLS 1.3 -- in 1.2 and below, the OCSP response data is carried in a
   CertificateStatus message.  In 1.3, it's in an Extension object
   associated with a Certificate, which allows certificate stapling for
   intermediate certs.  this is a good thing!  but a TLS client capable
   of both 1.2 and 1.3 that wants to be rigorous about stapling in both
   protocol versions has a much more complicated set of logic to worry
   about.

 * the certificate_status extension has its own internal extension
   point:

        enum { ocsp(1), (255) } CertificateStatusType;

   This goes against the "have one joint, and keep it well-oiled" design
   pattern we've learned over the years, because now you have the
   extension itself (certificate_status) and its own config selector.

   This results in another 8-bit registry to maintain: [1]
   
   [1] https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#certificate-status

   I think we should have omitted this additional joint, and just
   defined the certificate_status extension to be ocsp_status, without
   this field.  If someone wants to introduce a new status type, they
   can introduce a distinct TLS extension later.

 * I see no requirement that the CertificateStatusType in the
   CertificateStatus record actually matches the CertificateStatusType
   in the status_request extension sent by the client.

 * In TLS 1.2 and earlier, there is no obligation for a server which
   replies with the certificate_status extension to actually publish a
   CertificateStatus message.

 * Furthermore, if a status_request arrives at a TLS 1.2 or earlier
   server with a CertificateStatusType other than ocsp(1), or with
   non-empty ResponderID or Extensions members, it's conceivable that
   the server couldn't accurately create a corresponding OCSP response
   (either because it does not know about, or cannot contact any of the
   requested OCSP responders, or is for whatever reason unable to apply
   the requested OCSP extension(s).

 * the TLS Feature Request x.509v3 extension only indicates which
   extensions are supported in a generic, per-extension way.  So while a
   CA might be able to assert "this server will always reply with a
   status_request extension for this certificate", it doesn't appear to
   guarantee that (in 1.2) the server will actually send a
   CertificateStatus message at all, even if a status_request extension
   is sent, let alone whether the contents of that CertificateStatus
   message will be related to the client's status_request.  And in 1.3
   it doesn't guarantee that status_type will be ocsp(1) for the
   status_request extension that is sent.

 * Without the TLS Feature Request x.509v3 extension in the server's
   cert, there's no way to ensure that a compromised server can't just
   block the client from ever learning any OCSP details (e.g. by
   controlling the client's network).

So i think this is a big swirling mishmash of not-quite-compatible and
not-quite-complete specs, especially as we think about TLS clients and
servers that want to be interoperable with both TLS 1.2 and TLS 1.3.

In trying to figure out reasonable, useful guidance, and to make the TLS
Feature Request X.509v3 extension somehow useful, i'm tempted to try to
write up a clarifying/simplifying specification that actually cleans up
some of the semantics of status_request by cleaning up and enforcing the
currently-expected semantics.

In particular, i would want to propose something like:

 * regardless of TLS version, clients SHOULD NOT send status_request_v2,
   and servers SHOULD NOT return it even if the client supplies it.  If
   you want stapling for your intermediate certs, you need to have
   negotiated TLS 1.3.  (i think this is in line with the deprecation
   announced in 8446)

 * ask IANA to lock down the CertificateStatusType registry, or maybe
   remove it altogether.  status_type would then become just a legacy
   fixed value of 0x01 ("ocsp")

 * clients SHOULD always send ResponderID of length 0 and rely on the
   server to select an OCSP responder based on the AIA X.509v3
   extension's "OCSP" and "CA Issuers" fields in the server's
   certificate's X.509v3 extensions.

 * i'm not sure what to do about the Extensions field in status_request.
   For simplicty's sake, i'd like to say it should always be empty.  how
   much would we lose if we lost the ability to force in
   id-pkix-ocsp-nonce.

 * I'd like to redefine the semantics of what the TLS Feature Extension
   means for X.509 if it includes the status_request integer (5).  In
   TLS 1.2 and earlier, it means that if the client sends a
   status_request extension, a server MUST always send a
   CertificateStatus message after its Certificate message, and that
   CertificateStatus message MUST have status_type set to ocsp(1).  for
   TLS v1.3, it means that each CertificateEntry that contains this cert
   should also have a status_request extension, and that the extension's
   contents should match the expectations for v1.2 CertificateStatus
   messages described above (e.g., status_type=ocsp(1), no ResponderID).

additional conclusions for CAs
------------------------------

 * CAs must not issue certificates with a TLS Feature extension that
   indicates status_request_v2 for any server that intends to use the
   certificate with TLS 1.3, since TLS 1.3 servers are prohibited from
   responding with status_request_v2 extensions anywhere.

what do folks think of the above simplifications and subtle shifts in
semantics of already-defined code?  Is it worth trying to collect all
this into a single document with clear guidance to implementers, and a
promise that we will not extend status_request to non-ocsp mechanisms,
so that the X.509 TLS Feature Extension can be meaningfully applied for
this purpose?

Or does such a document already exist?  should we be encouraging
stapling as much as possible for security, in addition to performance
and privacy enhancements?

   --dkg

---

From: "Salz, Rich" <[email-masked]>
Date: Mon, 10 Dec 2018 02:24:29 +0000

Nobody does this.  There's a handful of reasons, but the end result is: nobody does this.

Yes, there are many things that could be cleared out with a BCP doc.  I would be interested in helping with that.

---

From: Daniel Kahn Gillmor <[email-masked]>
Date: Mon, 10 Dec 2018 07:16:31 -0500

> Nobody does this.  There's a handful of reasons, but the end result is: nobody does this.

I'd be interested in hearing the reasons enumerated.  It seems to me
like being able to promptly revoke an intermediate certificate is a
useful bit of mechanism.  is it just because we hope the major browsers
are clever and responsive enough that they'll push out a CRLset (or
equivalent) when they hear of an intermediate that is found to be
violating the BRs?

> Yes, there are many things that could be cleared out with a BCP doc.
> I would be interested in helping with that.

I agree that a BCP would be pretty helpful in clarifiying the state of
play, since it's currently scattered across a few docs over several
years.

But I think i'm proposing something that is not quite just a "BCP",
since it might involve changing the semantics and extensibility of
status_request (limiting it to OCSP responses (from the authorities
found in the AIA?))  so that "TLS Feature" can actually provide
meaningful guarantees of OCSP stapling for X.509v3 certificates.

Because right now, with all the extra (currently unused) flexibility in
status_request, it's hard to see how a client or server can meaningfully
rely on "TLS Feature" as a "must-staple" extension.

           --dkg

---

From: Ilari Liusvaara <[email-masked]>
Date: Mon, 10 Dec 2018 20:40:17 +0200

> equivalent) when they hear of an intermediate that is found to be

One is that in baseline requirements, the maximum intermediate OCSP
lifetime is 12 months(!) (compare this with the maximum end-entity
OCSP lifetime, which is 7 days), which makes them pretty useless for
quickly notifying of intermediate compromises when stapled.

And yes, some CAs actually use those 12 month OCSP responses.

As noted, all the browsers handle intermediate compromise via their
own blacklisting measures (oneCRL/CRLset/Disallowed.stl/etc...)

> rely on "TLS Feature" as a "must-staple" extension.

The TLS feature extension looks to be woefully underspecified. Even
in the one usecase it was meant to, which was this.

The interpretation I took when writing some releated code was that 5
in any (including CA) TLS feature extension means OCSP stapling EE
certificate is mandatory (or the EE certficate is shortlived). Just
answering the extension 5 is not enough.

-Ilari

---

From: Martin Thomson <[email-masked]>
Date: Tue, 11 Dec 2018 11:50:34 +1100

> equivalent) when they hear of an intermediate that is found to be

Yeah, from the browser side of things, we tend to rush out updates
when an intermediate is revoked.

I don't think that there is any reason we would prevent stapling in
TLS 1.3, other than the extensions being hard to plumb through.  NSS
throws them away and doesn't have an API surface for exposing the
information.  It's fairly tricky getting all this stuff where it is
needed.

---

From: Ryan Sleevi <[email-masked]>
Date: Mon, 10 Dec 2018 21:43:05 -0500

> equivalent) when they hear of an intermediate that is found to be

A few more reasons - speaking broadly to the "revoke intermediates" problem:

* The traditional revocation model (of CRLs and OCSP) is not as robust as
browser-based methods.
  - OCSP & CRLs both do the (Issuer+Serial number) dance, while alternative
revocation methods can use use the (Subject+SPKI), or even just the (SPKI),
as a blacklisting mechanism.
  - In the case of stapling responses for Intermediates, it's not
sufficient to 'simply' staple the response for the given intermediate - it
would have to consider all possible intermediates that chain to trust
anchors (both public and locally-configured).

* It's inefficient for the stated goal, under an adversarial model. This
applies to both leafs and intermediates.
  - While TLS 1.3 has the potential to make this better [1], the
intermediate response is generally going to be highly common between
multiple connections. There's no way (AFAIK), under V2 or 1.3, to indicate
that you already have a suitable response regarding a particular
intermediate. Past proposals [2] ran into a whole host of sharp edges of
complexity here; if memory serves, that complexity is very similar, in
terms of security/privacy/performance tradeoffs, as some of the cache
digest proposals [3] - bloom filters abound :)
  - If a client doesn't support it (and, indeed, only Microsoft provides
the relevant APIs in a safe way [4]), it's wasted bytes. Unlike
status_request_v2, AFAIK, there's no way for a 1.3 client to signal it's
uninterested in those intermediates; whether because it doesn't support
them or because it has alternative means.

* It's slow, under an adversarial model.
  - For leaves, we're talking 7 days of reusable "GOOD" response from an
adversary - the maximally permitted time under current root programs, less
than the 10 days the CABF BRs allow. For intermediates, as Ilari mentioned,
this becomes a substantially greater timescale. And this is without
touching on some of the ambiguity CAs rely on - the difference between
marking in their (internal only) database that a certificate is revoked
versus making that information publicly available (aka distributed to their
CDN). I'm told lag times may go up to an additional 7 days there.
  - For intermediates, this is "12 months". Some efforts to profile this
down [5] resulted in non-trivial pushback, given that many
non-actively-issuing certificates (offline roots/intermediates) require
ceremonies for the use of the key material - such as delegated responders
(which then opens up a whole new can of worms regarding compromises of
responder certificates - given id-pkix-ocsp-nocheck in the responders)

And this ignores some of the other "technical but not fundamental" problem,
such as few servers actually perform any form of path building or
validation on the certificates they will serve, and largely leave that to
the server administrator to configure. The lack of robust OCSP stapling
infrastructure for leaves - for which Microsoft IIS or Caddy may be the
only COTS server software robust enough to handle it - is dwarfed by the
complexity of also considering path building, possible paths, and finding
the 'ideal' intermediate and response to serve to avoid the known
challenges, let alone the challenges for some of the issues above.

To your specific question about the semantics for TLS 1.3, I don't think
that proposed semantic shift is good or desirable. I think it will both
increase inefficiency and fail to achieve the desired security properties,
as a consequence of the reasons above. Happy to expand on that, if it does
not seem like the conclusion logically follows the enumeration of problems.
That said, I also acknowledge that there aren't plans, at this time, to
support for "TLS Feature" extension, due to the policy and availability
implications, which are not technical and a consequence of the Web PKI
specifically.

[1] https://tools.ietf.org/html/draft-ietf-tls-certificate-compression
[2]
https://github.com/hannestschofenig/tschofenig-ids/tree/master/tls-cached-info
Hannes could speak to better threads capturing these discussions, no doubt
[3] https://tools.ietf.org/html/draft-ietf-httpbis-cache-digest
[4]
https://docs.microsoft.com/en-us/windows/desktop/api/wincrypt/nf-wincrypt-certsetcertificatecontextproperty
with CERT_OCSP_RESPONSE_PROP_ID allows per-certificate handle setting; NSS
uses https://dxr.mozilla.org/nss/source/nss/lib/certhigh/ocsp.c#5073 ,
which is per CERTCertificate handle, except those can get shared between
connections, resulting in clobbering of the OCSP cache by 'adversarial'
peers. Of course, Firefox ignores this with
https://dxr.mozilla.org/mozilla-central/source/security/certverifier/NSSCertDBTrustDomain.h?q=%2Bfunction%3A"mozilla%3A%3Apsm%3A%3ANSSCertDBTrustDomain%3A%3AGetOCSPStaplingStatus%28%29+const"&redirect_type=single#162
. For Apple,
https://developer.apple.com/documentation/security/1400880-sectrustsetocspresponse?language=objc
exists but doesn't provide any (documented) way of binding the
response<->certificate pairs.
[5] https://github.com/sleevi/cabforum-docs/pull/2/files