Subject: [pkix] Why is the crlNumber an OCTET STRING?

From: Peter Gutmann <[email-masked]>
Date: Tue, 20 Apr 2021 21:20:36 +0000

This came up recently in an implementation, the crlNumber, defined as "a
monotonically increasing sequence number for a given CRL scope and CRL
issuer", is defined in RFC 3280 (but not the original 2459) as "CRL numbers
can be expected to contain long integers.  CRL verifiers MUST be able to
handle CRLNumber values up to 20 octets", i.e. a SHA-1 hash disguised as an
INTEGER.

So if it's a monotonically increasing sequence number, why is it also a SHA-1
hash?  How can a CA issue several billion CRLs/delta CRLs to overflow an
actual integer?

Peter.

---

From: Russ Housley <[email-masked]>
Date: Tue, 20 Apr 2021 17:41:40 -0400

RFC 3280, Section 5.2.3 says:

   The CRL number is a non-critical CRL extension which conveys a
   monotonically increasing sequence number for a given CRL scope and
   CRL issuer.  This extension allows users to easily determine when a
   particular CRL supersedes another CRL.  CRL numbers also support the
   identification of complementary complete CRLs and delta CRLs.  CRL
   issuers conforming to this profile MUST include this extension in all
   CRLs.

   If a CRL issuer generates delta CRLs in addition to complete CRLs for
   a given scope, the complete CRLs and delta CRLs MUST share one
   numbering sequence.  If a delta CRL and a complete CRL that cover the
   same scope are issued at the same time, they MUST have the same CRL
   number and provide the same revocation information.  That is, the
   combination of the delta CRL and an acceptable complete CRL MUST
   provide the same revocation information as the simultaneously issued
   complete CRL.

   If a CRL issuer generates two CRLs (two complete CRLs, two delta
   CRLs, or a complete CRL and a delta CRL) for the same scope at
   different times, the two CRLs MUST NOT have the same CRL number.
   That is, if the this update field (section 5.1.2.4) in the two CRLs
   are not identical, the CRL numbers MUST be different.

   Given the requirements above, CRL numbers can be expected to contain
   long integers.  CRL verifiers MUST be able to handle CRLNumber values
   up to 20 octets.  Conformant CRL issuers MUST NOT use CRLNumber
   values longer than 20 octets.

   id-ce-cRLNumber OBJECT IDENTIFIER ::= { id-ce 20 }

   CRLNumber ::= INTEGER (0..MAX)

I see nothing about an OCTET STRING ...

The Appendix B says:

   As noted in section 5.2.3, CRL numbers can be expected to contain
   long integers.  CRL validators MUST be able to handle cRLNumber
   values up to 20 octets in length.  Conformant CRL issuers MUST NOT
   use cRLNumber values longer than 20 octets.

This does bot say anything about OCTET STRING either.

Russ

> On Apr 20, 2021, at 5:20 PM, Peter Gutmann <[email-masked]> wrote:
> 
> _______________________________________________
> pkix mailing list
> [email-masked]
> https://www.ietf.org/mailman/listinfo/pkix

---

From: Peter Gutmann <[email-masked]>
Date: Tue, 20 Apr 2021 21:58:14 +0000

>I see nothing about an OCTET STRING ...

If it's 20 bytes it's an OCTET STRING dressed up as an INTEGER, not a real
INTEGER.  In particular, if it's something where you'd need to issue 18
quintillion, 446 quadrillion, 744 trillion, 73 billion, 709 million, 551
thousand and 615 CRLs to exceed the capacity of an actual integer value
(assuming 64-bit) then there's something else going on, which is what I was
trying to find out.  It's not a "monotonically increasing sequence number" any
more because it's not possible to issue that many CRLs, so what is it?

Peter.

---

From: Russ Housley <[email-masked]>
Date: Tue, 20 Apr 2021 18:04:32 -0400

> On Apr 20, 2021, at 5:58 PM, Peter Gutmann <[email-masked]> wrote:
> 

I do not read it that way at all.  It is saying that relying parties need to the able to handle INTEGER values that are up to 20 octets in size.  Of course RSA keys use INTEGER values that are much bigger than that.  And, the text explains there are various ways that a CRL issuer can assign numbers for different scopes that can lead to larger values.

Russ

---

From: "Paul Hoffman" <[email-masked]>
Date: Tue, 20 Apr 2021 15:13:59 -0700

Not only is it not an OCTET STRING, you chose to use RFC 3280 instead of 
RFC 5280. :-(

Having said that, the question is germane. I cannot see how:
    The CRL number is a non-critical CRL extension that conveys a
    monotonically increasing sequence number for a given CRL scope and
    CRL issuer.
and
    Given the requirements above, CRL numbers can be expected to contain
    long integers.  CRL verifiers MUST be able to handle CRLNumber 
values
    up to 20 octets.
can both be true. I think that "long integers" is correct for some value 
of "long", but "20 octets" is just silly. I don't mind saying that CRL 
verifiers must be able to handle silly values, but would prefer to not 
have said that.

--Paul Hoffman

---

From: "Paul Hoffman" <[email-masked]>
Date: Tue, 20 Apr 2021 15:16:08 -0700

On 20 Apr 2021, at 15:04, Russ Housley wrote:

> I do not read it that way at all.  It is saying that relying parties 
> need to the able to handle INTEGER values that are up to 20 octets in 
> size.  Of course RSA keys use INTEGER values that are much bigger than 
> that.  And, the text explains there are various ways that a CRL issuer 
> can assign numbers for different scopes that can lead to larger 
> values.

I do not see how the text says that, and am concerned that you do. Are 
you saying that an CRL issuer can pick an initial number for the scope 
that is large? If so, where in 5280 (or somewhere else) does it say 
that?

--Paul Hoffman

---

From: Peter Gutmann <[email-masked]>
Date: Tue, 20 Apr 2021 22:28:47 +0000

Russ Housley <[email-masked]> writes:

>the text explains there are various ways that a CRL issuer can assign numbers
>for different scopes that can lead to larger values.

It doesn't really explain it, all it says is:

So CRL #1 has crlNumber 17, CRL #2 has crlNumber 18.  That's monotonically
increasing, and fits into a standard integer.

Paul Hoffman <[email-masked]> writes:

>you chose to use RFC 3280 instead of RFC 5280. :-(

I used 3280 because that's where the requirement for 20-byte "integers" was
introduced, so I was wondering what caused it.  My guess, for lack of any
obvious reason, was that it was someone's hack/implementation bug that was
written into the spec, because I can't see any other reason for the
OCTET_STRING-as-INTEGER use.  You certainly can't monotonically increase a
counter to the point where it'd be necessary.

Peter.

---

From: "Manger, James" <[email-masked]>
Date: Wed, 21 Apr 2021 02:42:14 +0000

Presumably CRLNumber has the “20 octet” language merely for consistency with CertificateSerialNumber. They sound so similar: numbering CRLs vs numbering certs.
Peter is right that it doesn’t actually make much sense as you can’t use hashes for CRL numbers given they need to be ordered.
Limiting CRL numbers to, say, [0, 2^63] could have been friendlier to devs. But it’s too late for that.

It is almost conceivable that a CA could hash the details of a CRL’s scope, then replace the least-significant, say, 64 bits with nano-seconds-since-1970 ; - )

--
James Manger

From: pkix <[email-masked]> on behalf of Peter Gutmann <[email-masked]>
Date: Wednesday, 21 April 2021 at 8:29 am
To: Russ Housley <[email-masked]>
Cc: IETF PKIX <[email-masked]>
Subject: Re: [pkix] Why is the crlNumber an OCTET STRING?
[External Email] This email was sent from outside the organisation – be cautious, particularly with links and attachments.

Peter.

---

From: Peter Gutmann <[email-masked]>
Date: Wed, 21 Apr 2021 02:50:27 +0000

Manger, James <[email-masked]> writes:

>Presumably CRLNumber has the “20 octet” language merely for consistency with
>CertificateSerialNumber. They sound so similar: numbering CRLs vs numbering
>certs.

Ah, that would make sense.

Just for fun, I thought about what it would take to blow past the limits of a
generic integer value, say 64 bits.  It's not just a case of running a counter
up to whatever number it is that a twenty-byte value is called, you need to
sign and publish a CRL for each one.  Let's say, rather optimistically, that
you can do a hundred a second (since it's not just raw sigs but actually
assembling and publishing a CRL), so you're doing ~10M a day.  That's about 5
billion years of issuing CRLs as fast as you can to exceed what an integer
value can hold.

Looked at another way, if you've got some way you can run something that
requires 2^160 operations you'll be doing other things, probably related to
BTC, with it instead of mucking around with PKI.

Peter.

---

From: "Manger, James" <[email-masked]>
Date: Wed, 21 Apr 2021 02:55:52 +0000

“monotonically increasing” doesn’t mean increase by 1. It means increase in 1 direction. So nano-seconds-since-1970 is valid, even if you only sign a CRL once a day – and doesn’t require state.

From: Peter Gutmann <[email-masked]>
Date: Wednesday, 21 April 2021 at 12:50 pm
To: Manger, James <[email-masked]>, Russ Housley <[email-masked]>
Cc: IETF PKIX <[email-masked]>
Subject: Re: [pkix] Why is the crlNumber an OCTET STRING?
[External Email] This email was sent from outside the organisation – be cautious, particularly with links and attachments.

Peter.

---

From: Niklas Matthies <[email-masked]>
Date: Wed, 21 Apr 2021 16:23:05 +0200

On Tue 2021-04-20 at 21:58h, Peter Gutmann wrote on pkix:
>If it's 20 bytes it's an OCTET STRING dressed up as an INTEGER, not a 
>real INTEGER.

One thing that isn't entirely clear is whether the 20-octets limit 
implies a maximum integer value of 2^159 - 1 or of 2^160 - 1. Assuming 
the two's complement integer representation of DER, I would assume the 
former. For serial numbers, this is almost made explicit by the first 
paragraph of Appendix B ("almost" because it is not made explicit that 
the 20-octets limit refers to the DER encoding of the value), but 
there is no corresponding paragraph for CRL numbers, so one has to 
wonder whether there is some intended difference between the two.

The sign bit having to be zero would also imply that you can't use 
just any SHA-1 hash value, besides the monotonicity requirement.

Niklas

---

From: Peter Gutmann <[email-masked]>
Date: Wed, 21 Apr 2021 15:20:56 +0000

>CertificateSerialNumber. They sound so similar: numbering CRLs vs numbering

Just noticed that the RFC text kinda confirms this:

   As noted in Section 4.1.2.2, serial numbers can be expected to
   contain long integers.  Certificate users MUST be able to handle
   serialNumber values up to 20 octets in length.  Conforming CAs MUST
   NOT use serialNumber values longer than 20 octets.

   As noted in Section 5.2.3, CRL numbers can be expected to contain
   long integers.  CRL validators MUST be able to handle cRLNumber
   values up to 20 octets in length.  Conforming CRL issuers MUST NOT
   use cRLNumber values longer than 20 octets.

So it's a cut&paste of the text for certificate serial numbers, for which
there's a legitimate reason, the German tank problem, to not use actual serial
numbers.

>It is almost conceivable that a CA could hash the details of a CRL’s scope,
>then replace the least-significant, say, 64 bits with nano-seconds-since-1970

Or ISO 8601 dates stuffed into the "serial number" for the CRL.

That does actually point out another issue though:

   The CRL number is a non-critical CRL extension that conveys a
   monotonically increasing sequence number for a given CRL scope and
   CRL issuer.  This extension allows users to easily determine when a
   particular CRL supersedes another CRL.

Isn't that what the dates in the CRL are for?  The only argument I can see for
using crlNumber is if you're brave enough to risk using delta CRLs.

Peter.

---

From: Russ Housley <[email-masked]>
Date: Wed, 21 Apr 2021 11:33:40 -0400

> using crlNumber is if you're brave enough to risk using delta CRLs.

The CRL number is helpful in any situation where the CRL issuer produces CRLs with different scopes.

Also, CRLs can be issued before the expected nextUpdate.

Russ

---

From: Stephen Farrell <[email-masked]>
Date: Wed, 21 Apr 2021 16:38:52 +0100

Hiya,

> Just noticed that the RFC text kinda confirms this:

That sounds reasonable - simplest explanation being most
likely and all that:-)

> there's a legitimate reason, the German tank problem, to not use actual serial

IIRC there was also a request from a CA in .au around then
that used company registration numbers as (part of?) the
cert serial number and those were long structured numbers or
something. I vaguely recall being asked to get text in that
alerted people that serials could be longer than some
implementations expected at that time.

I've no recollection of how the CRL number text ended up the
same.

Cheers,
S.

---

From: Russ Housley <[email-masked]>
Date: Wed, 21 Apr 2021 11:42:48 -0400

> I've no recollection of how the CRL number text ended up the

I recall someone saying that we needed to support values larger than 8 octets, but that we needed to provide some guidance on a maximum.

Russ

---

From: Peter Gutmann <[email-masked]>
Date: Wed, 21 Apr 2021 15:47:53 +0000

>The CRL number is helpful in any situation where the CRL issuer produces CRLs

How would the crlNumber help there?  And in particular, since thisUpdate is a
monotonically increasing sequence number, why is there a need for a second
parallel monotonically increasing sequence number?  It looks like an easy way
to implement crlNumber is:

  crlNumber := thisUpdate;

Which, in effect, is what the 8601-based implementation that's causing the
problem is doing, it's literally just copying the value of thisUpdate into
crlNumber.

Peter.

---

From: Russ Housley <[email-masked]>
Date: Wed, 21 Apr 2021 11:55:54 -0400

> problem is doing, it's literally just copying the value of thisUpdate into

This would work if and only if the CRL issuer is dealing with one scope.

For example, if a CRL issuer has partitioned the certificate population into multiple distribution points, all of these CRLs might be updated at the same time, but they each need different CRL numbers.  This kind of partitioning is used to make sure that none of the CRLs becomes overly large, even if the entire certificate population that it covers is revoked.

Russ

---

From: Stefan Santesson <[email-masked]>
Date: Wed, 21 Apr 2021 21:38:20 +0200

Isn't this one of all these PKI things that is a great intellectual
debate to fill out your time, but lacks any kind of real implications?

I have done quite some PKI validation implementations, but I have never
found any reason yet to check the CRL number for any reason what so ever.

When I do CRL checking, I download the current CRL, check that it is
current and still valid, and has the intended scope.

No more, and no less. CRL number is not part of that process.

So basically, I find this interesting intellectually, but in what
practical context does this matter?

/Stefan

---

From: Russ Housley <[email-masked]>
Date: Wed, 21 Apr 2021 16:04:02 -0400

> practical context does this matter?

If you cache CRLs to avoid fetching every time that you perform validation, then the CRL number is one way to determine whether the just fetched one is more "current" than the one in the cache.  Assuming the same scope, date works for this too as Peter pointed out.

Russ

---

From: Stefan Santesson <[email-masked]>
Date: Wed, 21 Apr 2021 22:18:58 +0200

Yes, I understand the logic. I just say that as implementer I never
found the reason to use this in practice.

In practice I have a policy that test if my current cached CRL is valid
to use. If yes, then I use it. if no, then I discard it and attempts to
get a new CRL.

The new CRL is treated the same way before being used.

Perhaps my implementations are to simplistic, but I never found a
compelling reason to check and compare CRL number.

/Stefan

---

From: Niklas Matthies <[email-masked]>
Date: Wed, 21 Apr 2021 22:51:31 +0200

In case of revocations, a new CRL is generally issued before
NextUpdate. Therefore it makes sense to regularly refresh the cache
entry regardless of the NextUpdate value, in order to limit (based on
local policy) the delay of noticing a new revocation. And then you
could conceivably compare the CRL number and abort the refresh if it's
still the same, as a performance optimization.

However, I would agree that comparing ThisUpdate makes more sense,
also because it's one of the first fields of a CRL and thus more
quickly accessible.

Niklas

---

From: Stefan Santesson <[email-masked]>
Date: Wed, 21 Apr 2021 23:07:58 +0200

Niklas,

Thanks for refreshing my memory. Yes, that is actually the process I
follow. Including comparison of ThisUpdate.

I just clearly recalled that I never saw the need to compare CRLNumber.

But I guess that is not what this thread attempted to solve. Sorry for
the side-tracking.

/Stefan

On 2021-04-21 22:51, Niklas Matthies wrote:
>>> If you cache CRLs to avoid fetching every time that you perform
>>> validation, then the CRL number is one way to determine whether the
>>> just fetched one is more "current" than the one in the cache. 
>>> Assuming the same scope, date works for this too as Peter pointed out.
>>>
>>> Russ
>>

---

From: Jeffrey Walton <[email-masked]>
Date: Wed, 21 Apr 2021 23:28:43 -0400

> compelling reason to check and compare CRL number.

It would make sense to use the existing CRL if the CRL has not changed
if it is expensive to download a CRL. The use case would include a
large PKI, like a federal PKI with a CRL that is hundreds of MB, and a
mobile client over 3G on a restrictive data plan.

Jeff

---

From: Peter Gutmann <[email-masked]>
Date: Thu, 22 Apr 2021 11:21:24 +0000

Stefan Santesson <[email-masked]> writes:

>I have done quite some PKI validation implementations, but I have never found
>any reason yet to check the CRL number for any reason what so ever.

Same here, could never figure out what the purpose of it was.  Russ' answer
about partitioned CRLs makes sense, but I'd never even considered that
because, like delta CRLs, I've never encountered anyone brave enough to want
to see what clients do in response to seeing one.

>When I do CRL checking, I download the current CRL, check that it is current
>and still valid, and has the intended scope.
>

Yup.

>So basically, I find this interesting intellectually, but in what practical
>context does this matter?

I've got a client who asked about it, and my response of "I have no idea what
purpose these things serve" was possibly a bit underwhelming :-).

Peter.

---

From: Ernst G Giessmann <[email-masked]>
Date: Thu, 22 Apr 2021 18:58:02 +0200

Stefan,
nevertheless it could happen, that you got two different CRLs, e.g. from
two different CRL distribution points, both valid (current time inside
thisUpdate and NextUpdate). How you decide which is the "current"?
Comparing the corresponding thisUpdate information? And if they are the
same due to lousy accuracy?
If you always implemented it this way, then your implementations are
wrong. The correct check is comparing the CRL numbers.
Sorry.
/Ernst.

---

From: Peter Gutmann <[email-masked]>
Date: Thu, 22 Apr 2021 23:01:22 +0000

Ernst G Giessmann <[email-masked]> writes:

>nevertheless it could happen, that you got two different CRLs, e.g. from two
>different CRL distribution points, both valid (current time inside thisUpdate
>and NextUpdate). How you decide which is the "current"? Comparing the
>corresponding thisUpdate information? And if they are the same due to lousy
>accuracy?

If you have to bend over backwards to manufacture an artificial case where
something is needed then it's probably not actually needed... 

Peter.

---

From: "Dars, Mihran [VendorPass]" <[email-masked]>
Date: Fri, 30 Apr 2021 17:02:20 +0000

Tend to agree with Ernst, if a CRL is copied to an isolated site, then number seems a better option to determine the latest one.
/Mihran

-----Original Message-----
From: pkix <[email-masked]> On Behalf Of Ernst G Giessmann
Sent: Thursday, April 22, 2021 09:58
To: [email-masked]
Subject: [**EXTERNAL**] Re: [pkix] Why is the crlNumber an OCTET STRING?

Stefan,