On Sat, Oct 21, 2023 at 5:41 AM Ilari Liusvaara <ilariliusva...@welho.com>
wrote:

> On Fri, Oct 20, 2023 at 04:07:21PM -0400, David Benjamin wrote:
> > On Thu, Oct 19, 2023 at 3:17 PM Ilari Liusvaara <
> ilariliusva...@welho.com>
> > wrote:
> > > - The multiple certificates from one ACME order really scares me. It
> > >   seems to me that can lead to all sorts of trouble.
> > >
> >
> > Certainly open to different mechanisms, though could you elaborate on
> > the trouble? We started with this one because it's actually just fixing a
> > mechanism ACME *already has*. RFC 8555, 7.4.2 has this bit:
> >
> >    The server MAY provide one or more link relation header fields
> >    [RFC8288] with relation "alternate".  Each such field SHOULD express
> >    an alternative certificate chain starting with the same end-entity
> >    certificate.  This can be used to express paths to various trust
> >    anchors.  Clients can fetch these alternates and use their own
> >    heuristics to decide which is optimal.
>
> Note the part "the same end-entity certificate.". The way I interpretted
> the draft, returning different end-entity certificates would be allowed.
>
>
> > Whether anyone has ever used this, I don't know. The "and use their own
> > heuristics to decide which is optimal" bit is quite absurd. :-) Relative
> to
> > that, all we've done is:
>
> Yes, that gets sometimes used.
>
>
> > But I suspect we'll want to define one where you make multiple orders
> too.
> > That would probably work better for, e.g., Merkle Tree certs where the
> two
> > issuances complete at very different times. But then, conversely, when
> the
> > two paths actually share an end-entity certificate, I imagine a single
> > order would be better so the CA knows it only needs to generate one
> > signature. And then when they don't share an end-entity certificate but
> are
> > similar enough in lifetime and issuance time, either seems fine, so we
> > figured this was a reasonable starting point.
>
> Well, I do not think it is feasible to use the normal ACME issuance
> mechanism for Merkle Tree certificates. The issuance is just too slow.
>
> And things like ACME ARI (which is required for actually handling
> revocations) inherently assume each order can only result one
> certificate.
>

The note about sharing an EE cert is just a SHOULD, not a MUST. RFC 8555
doesn't say why, but our interpretation was, like you note, this was mostly
a concern for things like accounting for renewals and revocations. We tried
to firm that up a bit by saying this makes sense when you're willing to
issue and renew all the variants together. For something like ARI, I was
imagining the ACME client would just check all of them (we're already
assuming the ACME client has been updated) and, if it needs to renew any of
them, it goes ahead and renews all of them. Slightly wasteful if renewal
was triggered by one of them getting revoked, rather than them all expiring
together. But I expect that's not common enough to be worth optimizing for.

Do you think multiple orders would be better? A multi-order flow is
probably more complex than fits in this document (this ACME change is
pretty small), so we didn't start with it. Plus this initial version seemed
natural to us based on what ACME had already defined. But we're much more
interested in making this kind of multi-certificate deployment model
possible than any of the particular details. Happy to adjust things based
on what turns out to work best. (One nuisance with a multi-order flow is
that the CA will have a harder time linking the requests together, which
opens a can of worms around whether they do separate validations or not.)



> > > - If there can be only one certificate, one could send all the chains
> > >   in one go via fist sending the certificate, then issuer chains each
> > >   ended by entry describing the trust anchor.
> > >
> >
> > I'm not quite sure if I've parsed this right, but are you thinking of one
> > file that somehow describes all alternatives together? That's plausible
> > too. Like I said, we mostly did this one because ACME already did it, so
> we
> > inferred that was The ACME Way. :-)
>
> Yes, one file describing all alternatives together. I think that is
> easier to work with than the existing alternatives mechanism (I don't
> think most clients even support that).
>

I think having to do a round of updating existing clients is fine here.
We'd already need to teach them to implement this thing. But if we think
putting them in one bundle is more convenient, that's fine by me too. I
don't actually care.

Exploring that direction a bit, won't that make issues around ARI and
different issuance rates worse? Bundling them means the system is further
inclined to treat them as one unit, but a lot of these problems come from
wanting to be able to treat each path separately, be it different renewal
times, different issuance times, potentially even different private keys
associated, etc. I also worry it'll get a bit thorny as it passes through
the layers. E.g. if the TLS serving software expects one PEM file to be one
certificate chain, but the TLS library secretly turns it into multiple
alternative chains, the rest of the logic will break. (E.g. imagine if the
serving software knows to set up OCSP stapling or otherwise associate data
with the cert it's serving.)


> > > - I am not sure this is useful for the client->server direction.
> > >
> >
> > Eh, it costs ~nothing to define it in both directions, just a global
> > s/client/relying party/ and s/server/subscriber/ across the document.
> :-) I
> > figure we may as well define it in both directions, and if some client
> > certificate deployments find it useful, cool. On the Chrome side, if the
> > operating systems could give us something like this, with pre-made paths
> > and unambiguous rules for when to send each, I would be overjoyed. We
> spend
> > quite a lot of time helping people debug misconfigurations and quirks
> > around client certificate selection.
>
> How useful it is is different from how difficult it is to specify. :-)
>

Certainly. I just think it's a waste not to make certificate-related flows
work in both directions, after we made them neatly symmetric in TLS 1.3. If
it's not as useful in the other direction, that's fine. Folks can always
choose to only deploy or even implement it in one direction. *shrug*


> > > What I think is a simpler version that might work:
> > >
> > >
> > > Information from root program to CA:
> > >
> > > - Root program name.
> > > - For each trust anchor:
> > >   * Trust anchor certificate.
> > >   * First version TA appeared in.
> > >   * Expiry time
> > >   * List of indices.
> > >
> > > Indices can be reused after all TAs using those have expired.
> > >
> > >
> > > Information from CA to TLS server for each TA:
> > >
> > > - For each root program:
> > >   * Root program name
> > >   * The first version TA appeared in.
> > >   * List of indices.
> > >
> > > CA MUST NOT include entries that expire before the certificate.
> > >
> > >
> > > Information from TLS client to TLS server:
> > >
> > > - Root program name.
> > > - Root program version.
> > > - List of revoked indices.
> > >
> > > The revoked indices specifies TAs that have been recently removed
> > > before expiry (there could still be unexpired certificates out
> > > there).
> > >
> > >
> > > Chain is usable if it includes an entry where:
> > >
> > > a) Root program name matches, AND
> > > b) Root program version is at least the first version, AND
> > > c) Intersection of indices and revoked indices is empty.
> > >
> > > If TLS server has multiple configured certificates, it should skip ones
> > > that have no usable chains. If no certificate has usable chain, it
> > > should act like the extension was not sent.
> > >
> >
> > If I'm parsing this right, I think the main issue here is this presumes a
> > removed CA will stop issuing certificates:
> >
> > Suppose some CA is in v2 and then was removed in v3. We need to ensure
> > certificates issued by that CA don't match a v3 client, so the server
> will
> > send a different one. Immediately after removal, there are plenty of
> > existing certs that predate v3's definition, so the relying party needs
> to
> > ship in exclusion. However, we would like the exclusion to eventually
> fall
> > off, or every historical removal will be sent in every ClientHello ever.
>
> I think the mechansism is essentially the same as in the draft, just
> with different wording. After sufficiently long time after distrust
> (max cert lifetime + few days) the revocations can be dropped.
>
>
> > If the CA ceases operation, the exclusions can be dropped then. But the
> CA
> > may have good reasons to keep issuing. Consider root rotation. Unupdated
> > clients won't trust the new root, and servers may still need to work with
> > those clients for a long period of time. The CA may quite reasonably wish
> > to continue issuing from that root until that is no longer the case. This
> > may even happen during a distrust: it could be that one population of
> > clients no longer trusts the CA, while another population of clients
> > (perhaps some unupdatable devices somewhere) *only* trusts that CA.
> Servers
> > that need to serve both populations could then deploy a different
> > certificate for each, in which case the removed CA might continue
> issuing.
> > It only takes *one* population of relying parties for it to be useful to
> > keep issuing from that CA. And as long as *any* server has certificates
> > from that CA installed, we need to account for them in cert selection
> > somehow.
>
> Oh yeah, that causes problems.
>
> Maybe simplest attempt at fix would be to add revoked-in-version to
> trust anchor information for revoked trust anchors.
>
> Then TLS server can check v_first <= version < v_revoke.
>
> Then after transition period:
>
> - Old client: root_program, v_old, no revoke.
>   * Old chain: Passes all checks.
>   * New chain: v_first > version, so fails version check.
> - New client: root_program, v_new, no revoke.
>   * Old chain: version >= v_revoke, so fails version check.
>   * New chain: Passes all checks.
>
>
> > The way to square this is to have both a lower bound *and* an optional
> > upper bound. If the CA knows it in v1, v2, but not v3 onwards, it can
> tell
> > the server this. Once you add the upper bound, I *think* your sketch is
> > basically our design (though I may have missed something). The main
> > difference is rather than store a range in manifest and inclusion list,
> > we've just listed each version individually. If you've got a
> > latest_version_at_issuance entry, it means the range has no upper bound.
>
> My sketch also has expiry, which immediately expires the root.
>

Ah, this is "expiry" in the sense of actually removing the root from v2?
I.e. distinct from what our draft (somewhat confusingly) calls expiry. (Our
"expiry" relates to this cross-version issue.)

Yeah, we don't have that right now. Though I think most cases don't need
explicit expiry information. Since this is used for negotiation, not trust,
it doesn't actually matter if v2 contains some roots that have expired, as
long as both RP and CA agree on when this happens. I.e. *for negotiation
purposes*, it makes no difference whether an expired, no longer issuing
root is in the v2 set or not. Of course, it does matter for security
purposes, so the RP definitely needs to no longer accept signatures from
that key. And if the RP and CA don't agree on the root's expiration time,
such that the CA continues to issue from the root, then that doesn't work
and we may need explicit expiry in the system. (Though I might suggest that
RP and CA disagreeing is kinda odd. We're talking here about cases where a
root would *automatically* get removed from v2, e.g. directly trusting a
short-lived intermediate. In that case, the CA should know this ahead of
time and, if they don't have a root that's of the desired lifetime, just
spin up a new one. The great thing about this deployment model is that
spinning up new roots for new purposes can be basically free.)

Anyway, happy to add something like that if folks think it's useful. The
main cost is a little complexity: one more moving part in the system, and
having to explain that (example, v2) is not actually a fixed set and slowly
shrinks over time. But I think it'd work just fine.


> > We listed them out because that makes versions a bit more independent,
> > which allows the root program to adjust its label allocations over time.
> > For example, maybe the root program later decides it'd be useful to mark
> > CAs that use some algorithm. Or perhaps some keys changed hands and we'd
> > like to reflect that in the labels. Now, there's a little subtlety here
> > because label changes take some time to be reliable. But after max_age +
> > max_lifetime, all certificates will have the new information available.
> > (Before then, the trust expression creation process will just tell you
> that
> > you need to account for both old and new entries, so you may need to add
> a
> > few more labels.) Also, when we went to describe the root program
> > operation, it was much more straightforward to just talk about versions
> as
> > independent, with minimal cross-talk between them.
>
> It is possible to change label sets of old versions, the label changes
> just take some time to propagate (the longest revoke holddown among
> trust anchors covered). :-)
>

Oh, were you thinking that label changes propagate back to old versions,
and that way you just update the CA -> subscriber record for new issuances?
I was thinking they wouldn't, because then we have to reason about what
kinds of retroactive changes are compatible with older RPs. If we don't
retroactively change things, the CA -> subscriber message needs to support
saying things like "from v1 to v10 you had these labels, from v11 to v20
you had these other labels". And if we're going down that road already, we
figured, eh, this is a premature optimization, we can just list all the
versions one by one and not worry about ranges. (But I'm also not opposed
to doing ranges. If people prefer that, sure, let's do that.)

One of our assumptions here is that arbitrarily out-of-date RPs may exist
indefinitely. Some RPs don't auto-update. Even among those that do (e.g.
browsers), some clients may not have talked to their update server. And
open source code ends up all over the place, so I don't want to discount
the possibility that some server may care about serving, say, arbitrarily
outdated Chromium derivatives. That means we shouldn't incompatibly change
the meaning of past trust expressions that refer to past trust store
versions. Easiest way to do that, I think, is to just keep the meaning of
"(example, v2)" fixed.


> > Of course, labels probably won't change between versions much, so a range
> > scheme would make the list more compact. We didn't go with that just
> > because the exploded one was less complicated. We did some estimates and
> it
> > didn't look like the compression was actually needed, so we omitted it.
> > (These are not sent in TLS connections, just in the root program -> CA ->
> > subscriber flow. We don't want them to be *humongous*, but we don't need
> to
> > squeeze them that tightly.) But if folks prefer ranges, that's easy
> enough
> > to add.
>
> One thing I noticed is that the information is sorted, presumably for
> binary search. However, straightforward binary search will not work
> because of the variable length entries (and there are no sync markers
> either).
>

Oh yeah, I'm assuming you'd parse this into some seekable in-memory
structure. The sort order isn't *that* important because you could just as
easily sort them after parsing. Just an easy way to save some effort on the
receiver, and make validation straightforward. I suppose we could make an
actually seekable structure on the wire, but it wouldn't be very TLS-y, and
seems far, far more fuss than is worth it. :-)

David
_______________________________________________
TLS mailing list
TLS@ietf.org
https://www.ietf.org/mailman/listinfo/tls

Reply via email to