Closed Bug 143334 Opened 22 years ago Closed 21 years ago

Support GeneralizedTime option in certificate validity field

Categories

(NSS :: Libraries, enhancement, P2)

enhancement

Tracking

(Not tracked)

RESOLVED FIXED

People

(Reporter: thayes0993, Assigned: julien.pierre)

References

Details

(Whiteboard: [cert])

Attachments

(1 file, 7 obsolete files)

The validity value in X.509 certificates now supports a CHOICE of UTCTime or
GeneralizedTime for the notBefore and notAfter fields.  GeneralizedTime MUST be
used for dates beginning in 2050.

While most certificates will not contain dates beyond 2049, in the next few
years certificate authority (CA) certificates (especially roots) may be issued
with expiration dates that far in the future.

When processing certificates, NSS should accept the GeneralizedTime format for
all dates.  When generating certificates NSS should choise the GeneralizedTime
option for dates beginning in 2050.

See X.509 and IETF RFC 2459 for details.
Severity: normal → enhancement
Priority: -- → P1
Whiteboard: [cert]
Status: NEW → ASSIGNED
Target Milestone: --- → 3.6
Moved to Future.  Will consider this for NSS 3.7.
Target Milestone: 3.6 → Future
Target Milestone: Future → 3.7
Target Milestone: 3.7 → Future
We really ought to outlaw the target milestone of "Future".  
This bug needs to be fixed NOW.  We're starting to see more and more certs
with GeneralizedTime dates and we really ought to handle them.  (IMO :-)
Priority: P1 → P2
Target Milestone: Future → 3.9
Let's implement GeneralizedTime support in NSS 3.9.
Julien, want to take a stab at this?

I worked on this a little bit during NSS 3.5
development.  That was when Terry and I discovered
some problems with our ASN.1 encoder and decoder's
handling of CHOICE.  The bugs have been fixed on
the trunk (see bug 161580).  The support of
GeneralizedTime requires correct handling of CHOICE.
Assignee: wtc → jpierre
Status: ASSIGNED → NEW
Wan-Teh,

Does this change apply only to certificates or also to other objects, eg. CRLs ?

Regardless, it sounds like something I could work on. It would be useful to have
a list of functions that need to support this. I am assuming the CERT_*Validity
functions and the CERTValidity structure are the primary ones.
I suspect that this changes applies to CRLs as well,
but you should consult RFC 3280 to be sure.

You can start with the structure that represents
a decoded Validity, and look for the functions that
operate on that structure.
Thanks for looking at this problem, but until it is fixed could you please
change the error message.

I am writing a server application which creates it's own certs and I don't want
to think about the number of days I spent going through my code to try to figure
out why I got this message when I tried to connect :

"Error establishing an encrypted connection. Error code -8183"

Eventually I gave up and switched to running mozilla with gdb to track down the
problem was with the date being generalized not utc.

Please display a more meaningful message, or at least print some debug log line
to say what has happened.

Anyway, thanks again for looking at it.
Greg.
Greg,  This bug report is against the NSS libraries, which are used by the
mozilla browser and by numerous other software products.  The NSS libraries
provide NO UI (and NO error dialogs) for the browser.  
Complaints about the browser's error dialogs would be the subject of a bug
against PSM, not a bug against the NSS security libraries.  
I invite you to search bugzilla for PSM bugs about error messages and vote
for them, or better yet, contribute code to fix them.
Actually, RFC3280 says the following :

   CAs conforming to this profile MUST always encode certificate
   validity dates through the year 2049 as UTCTime; certificate validity
   dates in 2050 or later MUST be encoded as GeneralizedTime.

Therefore the problem only exists for dates after 2049. The RFC does not allow
dates before 2050 to be encoded as GeneralizedTime.
The problem is only relevant if a certificate expires after 2050 .
It's true that RFC3280 says that the GeneralizedTime format must be used only 
for dates beginning in 2050.  This is to allow compatibility with existing 
implementations (like NSS) that do not yet support the format.

However, there is no reason for an implementation that supports 
GeneralizedTime to reject a certificate (or other object) that contains dates 
before 2050 that are encoded using that format.

Dates generated by NSS should use the RFC guideline to ensure interoperability 
with other RFC3280-conformant implementations.
Terry,

I agree that when we support GeneralizedTime in NSS, we should have no
restriction on dates.
 
I only made this quote from RFC3280 to shed some light on the urgency of making
this change to NSS. People have been using certs with GeneralizedTime validity
format which didn't decode in mozilla, and that was one reason that we wanted to
implement this support.

I'm guessing that the problematic certs we have encountered so far were either :

1) not following the RFC3280 guidelines, ie. they had dates before 2049 in
GeneralizedTime format.

2) dummy self-signed rootswith 100 year validity date, generated with OpenSSL by
some wannabe CA administrator in their bedroom

Perhaps I am wrong and there are already valid reasons in 2003 to issue certs
expiring so long in the future, but even the trusted roots aren't going as far
into the future yet. Actually, the longest expiring root I can find is the AOL
TW root, and it expires in 2037, so perhaps this problem was part of the reason
for limiting the expiration date .

Regardless, this will get fixed in 3.9 .
More findings about GeneralizedTime :

RFC3280 also specifies the use of GeneralizedTime for CRLs thisUpdate and
nextUpdate fields, in the same way it does for certificates' notBefore and
notAfter fields . It is also legal for the revocation date in CRLs.

To implement this for certificates, I need to modify the CERTValidityStr
structure. Unfortunately, that is included in the CERTCertificate as a struct
member. I would break binary compatibility by doing this change.
To preserve binary compatibility, I will need to add a new validity structure at
the end of CERTCertificate. The old one structure would have to stay as a
placeholder.

Another way would be to do away with the entire CERT_ValidityTemplate, add the
new fields at the end of the CERTCertificate structure, and do the decoding in
the CERTCertificate's main template instead of subtemplate. I am leaning towards
that option, because the whole "validity structure" doesn't actually exist, it
is just a representation of the 2 fields in the certificate.
I think you should be able to keep binary compatibility by placing the choice
discriminant in the type member of the secitem for each of the time values.
Do we have any certs encoded with GeneralizedTime for testing purposes ?
I have written some code that allows decoding of CERTCertificate by NSS of
either the UTC or GeneralizedTime. I used the SECItem type field for the choice
result, as suggested by Nelson.

However, by quickly reviewing the various CERTValidity* manipulation functions
in util/sectime.c, I find that many of these functions appear to be broken.

For instance :
- CERT_CreateValidity calls DER_TimeToUTCTime, which allocates memory on the
main heap, rather than in the CERTValidity's arena . 

- CERT_DestroyValidity does not attempt to free the memory in notBefore.data and
notAfter.data ; it only destroys the CERTValidity's arena, thus leaking memory .

- CERT_CopyValidity first tries to destroy the destination validity structure
before doing the copy and assigning fields into it ! In the only actual usage of
this function, in CERT_CreateCertificate, the target validity structure does not
have an arena, so it cannot get freed, and this bug does not actually manifest
itself.

In addition to fixing the above bugs, I have identified the following remaining
tasks :

- fix CERT_CreateValidity so that it encodes the GeneralizedTime type for dates
past 2049 .

- fix the softoken cert validity decoding. Softoken uses its own "hand" decoder
called nsslowcert . Currently it only supports UTC time.
Looks like the attached patch breaks the NSS tests . I still got some work to do.

Also, I need to add the support for GeneralizedTime in CRLs.
I finally found out why this patch was breaking the tests. The same template
gets used for decoding and encoding. The encoder was failing on the CHOICE,
because I hadn't modified the cert encoding function to assign the type field of
the SECItem to match one of the IDs in the template. Therefore, it didn't know
whether to use the  component type of UTC or Generalized when encoding.

I have made a patch that passes the NSS tests.
It works for both encoding and decoding.

The only thing missing still is the nsslowcert support in softoken, and the CRL
support (which should probably be a separate bug from this one, since this is
about certificates).

I was able to run the tests in year 2050, by setting my machine to that date.
Presumably the CA certs are being created with the GeneralizedTime encoding in
this case, though I had no way to verify it. Apparently, the fact that
nsslowcert doesn't understand this format is not a problem. The only problem I
saw in the test is that gmake rolls over to 1969 when I set my computer's date
to 2050, and thinks my NSS built in 2003 is "in the future".
Attachment #130946 - Attachment is obsolete: true
Actually, with my patch, there were still three failures when setting the
computer to year 2050 :

Signing a set of files (signtool -Z) 	Failed
Listing signed files in jar (signtool -v) 	Failed
Show who signed jar (signtool -w)

However, if I run the tip of NSS (no patch) in year 2050, I get far more
failures : every SSL test fails, except the negative ones. Half the S/MIME tests
do as well. 2 cert verification tests fail, including one of the FIPS tests. The
three test tool failures mentioned above also occur .
All those failures except the last 3 tool failures are fixed by my patch in 2050.
Interesting test results.

We need a way to test your new code without setting the
system clock to 2050 for two reasons.  1. We can't change
the system clock during our normal QA testing.  2. Setting
the system clock to 2050 will test more than the
support of GeneralizedTime.  The test failures may
be caused by the 32-bit time_t overflow rather than
the lack of support for GeneralizedTime.
As you probably know, certutil's commands to create a cert take two optional
arguments:
   -w warp-months      (default 0)
   -v validity-months  (default 0)

The notBefore date is "now" + "warp-months" months.
The notAfter  date is the notBefore date + ("validity-months" + 3) months.

So, by using a value of, say, 600 (12*50) validity months in the QA scripts,
it should be possible to ensure that we generate certs with both UTCTime and
GeneralizedTime.
Attachment #131114 - Flags: superreview?(wchang0222)
Attachment #131114 - Flags: review?(relyea)
Attachment #131154 - Flags: superreview?(wchang0222)
Attachment #131154 - Flags: review?(relyea)
Bob, Wan-Teh,

The patches to add support for GeneralizedTime on certificates are complete.
Please review them. I will open a separate bug for the CRL support.
Comment on attachment 131114 [details] [diff] [review]
updated patch to fix both encoder and decoder

This patch looks good.	I just wanted to suggest some changes.

The 'type' field of a SECItem structure has the enumeration
type SECItemType, so you should add new enumeration constants
(siGeneralizedTime and siUTCTime) to SECItemType rather than
using SEC_ASN1_GENERALIZED_TIME and SEC_ASN1_UTC_TIME.

>+int DecodeValidityTime(PRTime* output, SECItem* input)

This function should return SECStatus.

> SECStatus
> CERT_GetCertTimes(CERTCertificate *c, PRTime *notBefore, PRTime *notAfter)
> {
>     int rv;
>+
>+    if (!c || !notBefore || !notAfter)
>+        return SECFailure;

We should set an error code before returning SECFailure.
(BTW, 'rv' should have the SECStatus type.)

CERT_TimeChoice should be renamed CERT_TimeChoiceTemplate.

>+SECStatus EncodeValidityTime(SECItem* output, PRTime input, PRArenaPool* arena)
>+{
...
>+    if (printableTime.tm_year>=2050) {
>+        rv = DER_TimeToGeneralizedTimeArena(output, input, arena);
>+        output->type = SEC_ASN1_GENERALIZED_TIME;
>+        return rv;
>+    } else {
>+        rv = DER_TimeToUTCTimeArena(output, input, arena);
>+        output->type = SEC_ASN1_UTC_TIME;
>+        return rv;
>+    }

Should we set output->type in the DER_TimeToXXXTimeArena functions?
Attachment #131114 - Flags: superreview?(wchang0222)
Attachment #131114 - Flags: superreview-
Attachment #131114 - Flags: review?(relyea)
Attachment #131154 - Flags: superreview?(wchang0222) → superreview+
Comment on attachment 131114 [details] [diff] [review]
updated patch to fix both encoder and decoder

Some questions about the first patch.

>+int DecodeValidityTime(PRTime* output, SECItem* input)
>+{
>+    switch (input->type) {
>+        case SEC_ASN1_GENERALIZED_TIME:
>+            return DER_GeneralizedTimeToTime(output, input);
>+
>+        case SEC_ASN1_UTC_TIME:
>+        default:
>+            return DER_UTCTimeToTime(output, input);
>+    }
>+}

If input->type is neither GenerailzedTime nor UTCTime, we treat
it as UTCTime.	Why?  Why doesn't this function fail in that case?

I suspect it's because some existing code doesn't set input->type.
Can we determine the type of input by its length?

>+extern SECStatus DER_TimeToUTCTimeArena(SECItem *dst, int64 gmttime,
>+                                        PRArenaPool* arenaOpt);
>+

>+extern SECStatus DER_TimeToGeneralizedTimeArena(SECItem *dst, int64 gmttime,
>+                                                PRArenaPool* arenaOpt);

It is more consistent with existing functions to have the PRArenaPool*
as the first argument.

Declare DecodeValidityTime and EncodeValidityTime as static.
Comment on attachment 131154 [details] [diff] [review]
changes for the softoken lowcert layer

Nelson pointed out that there are some ambiguous cases where a UTCtime value
cannot be distinguished from a GeneralizedTime . This is because seconds are 
optional.
The following value for example :
20040101010101Z
Can mean either january 1st 2004 in GeneralizedTime, or April 1st 2020 in
UTCTime ...

So, we must distinguish them by ASN.1 component tag.

The hand decoder built into the softokn does not check any component tag value,
unfortunately.
Therefore, this patch must be reworked.
Attached patch updated patch (obsolete) — Splinter Review
Attachment #131114 - Attachment is obsolete: true
Attachment #131154 - Attachment is obsolete: true
Comment on attachment 131367 [details] [diff] [review]
updated patch

All the NSS tests pass with these patches at the current date.
The changes include:
- adding and using siUtcTime and siGeneralizedTime to SECItemType

- using SECStatus instead of int for return types

- moving some functions areound to util

- renaming of the time choice template to CERT_TimeChoiceTemplate

- addition of CERT_InlineTimeChoiceTemplate (needed for some of my other
forthcoming patches)

- setting of SECItem.type in the encoder functions

- asserting and failing if the SECItem.type isn't set, since we can't safely
guess the type with the value at decoding time

- reordering arena arguments

- in softoken, add an argument to return the ASN.1 tag so that the correct
decoding of the time can be done

- remove "UTC" from comments in several places where PR_Now() is called, since
this is somewhat ambiguous given the DER UTCtime issues (I was searching on
that for flaws)

- probably more ...
Attachment #131367 - Flags: superreview?(wchang0222)
Attachment #131367 - Flags: review?(nelsonb)
I forgot to mention this patch also includes the fix for CRLs. I didn't touch
the KRLs.

Wan-Teh, Nelson, I know this is a very long patch, but I would appreciate a review.
Attached patch incorporate Nelson's feedback (obsolete) — Splinter Review
- fix bug in lowcert.c where notBefore was used where notAfter was meant
- use dst->len in lowcert.c rather than repeating constants for memory
allocation
- use PRTime instead of int64 for new code in sectime and dertime
- optimize the year 2050 by adding a constant PRTime to compare against,
instead of calling PR_ExplodeTime
Attachment #131367 - Attachment is obsolete: true
Attachment #131637 - Flags: superreview?(MisterSSL)
Attachment #131637 - Flags: review?(wchang0222)
Note: the stanpcertdb.c fix shouldn't go in because it changes the DB format by
allowing a GeneralizedTime to be saved for the S/MIME profile time. See bug
219527 for that issue.
Blocks: 219524
The change to stanpcertdb would have saved either a GeneralizedTime or UTCTime
S/MIME profile time, and we can't do that without breaking the database format
for backwards compatibility.
Attachment #131637 - Attachment is obsolete: true
Attachment #131643 - Flags: superreview?(MisterSSL)
Attachment #131643 - Flags: review?(wchang0222)
Attached patch more fixes (obsolete) — Splinter Review
Hopefully this is the last patch needed here .
The changes from the last patch are :
- revert from sizeof(SECItemType) to sizeof(SECItem) in the
CERT_TimeChoiceTemplate
- remove CERT_InlineTimeChoiceTemplate

Thanks to Nelson, I also included the changes for the test scripts to extend
the validity date of certs to 600 months, past year 2050.
Attachment #131643 - Attachment is obsolete: true
Comment on attachment 131654 [details] [diff] [review]
more fixes

Note that all that the NSS tests now pass, both at current date and after 2050,
so we should be set as far as this feature goes, at least for everything
currently tested by the test suite.
Attachment #131654 - Flags: superreview?(MisterSSL)
Attachment #131654 - Flags: review?(wchang0222)
Blocks: 218793
Comment on attachment 131654 [details] [diff] [review]
more fixes

This patch is very good.  Most of my comments are style or minor
issues.  The two biggies are:

1. In lib/nss/nss.def, the new functions need to be added to a
new NSS_3.9 section rather than the NSS_3.8 section.

2. If I understand it correctly, the changes to tests/cert/cert.sh
means we will only be testing GeneralizedTime and won't be testing
UTCTime in some cases.	We want to test both.  This may mean
repeating some of the tests for GeneralizedTime testing.

I will give you the rest of my comments offline.
Attachment #131654 - Flags: review?(wchang0222) → review-
In response to comment #35, issue 2 :
The certs created by certutil will have a notBefore encoded in UTCTime and a
notAfter encoded in GeneralizedTime, so we will indeed be testing both encodings.
If we want to test both fields with the different encodings, we will have to
reset the system time after 2050. Setting notBefore as GeneralizedTime implies
that the notBefore is after 2050, and I believe some of the tests (SSL) can only
run at the current system date, so they would fail.
Some of the certs generated in the QA tests are generated by signtool. 
signtool always produces certs with a hard-coded 3 month validity period.  
So, presently, the certs produced by signtool will NOT test GeneralizedTime.
But I think GeneralizedTime will get adequate testing in other cases.

Apart from the nss.def issue, I think this patch is ready to checkin.  
We don't need to go to great length (such as setting the
system clock to Year 2050) to test a notBefore encoded
in GeneralizedTime, but we need to test a notAfter encoded
in UTCTime.
One way to test certs with both notAfter in UTCTime and GeneralizedTime without
adding to the test suite would be to just have one of the certs (the CA) in the
existing cert.sh script use -v 60, and another one (the end-entity) use -v 600 . 

Changes are :
- fixing nss.def to use the 3.9 section
- renaming siUtcTime to siUTCTime .
- add a missing PORT_SetError in DER_TimeChoiceDayToAscii if SECItem type isn't
set

Checking in lib/certdb/certdb.c;
/cvsroot/mozilla/security/nss/lib/certdb/certdb.c,v  <--  certdb.c
new revision: 1.55; previous revision: 1.54
done
Checking in lib/certdb/certt.h;
/cvsroot/mozilla/security/nss/lib/certdb/certt.h,v  <--  certt.h
new revision: 1.24; previous revision: 1.23
done
Checking in lib/certdb/crl.c;
/cvsroot/mozilla/security/nss/lib/certdb/crl.c,v  <--  crl.c
new revision: 1.37; previous revision: 1.36
done
Checking in lib/certhigh/certhtml.c;
/cvsroot/mozilla/security/nss/lib/certhigh/certhtml.c,v  <--  certhtml.c
new revision: 1.4; previous revision: 1.3
done
Checking in lib/certhigh/certvfy.c;
/cvsroot/mozilla/security/nss/lib/certhigh/certvfy.c,v	<--  certvfy.c
new revision: 1.35; previous revision: 1.34
done
Checking in lib/nss/nss.def;
/cvsroot/mozilla/security/nss/lib/nss/nss.def,v  <--  nss.def
new revision: 1.111; previous revision: 1.110
done
Checking in lib/softoken/lowcert.c;
/cvsroot/mozilla/security/nss/lib/softoken/lowcert.c,v	<--  lowcert.c
new revision: 1.15; previous revision: 1.14
done
Checking in lib/util/dertime.c;
/cvsroot/mozilla/security/nss/lib/util/dertime.c,v  <--  dertime.c
new revision: 1.3; previous revision: 1.2
done
Checking in lib/util/seccomon.h;
/cvsroot/mozilla/security/nss/lib/util/seccomon.h,v  <--  seccomon.h
new revision: 1.4; previous revision: 1.3
done
Checking in lib/util/secder.h;
/cvsroot/mozilla/security/nss/lib/util/secder.h,v  <--	secder.h
new revision: 1.3; previous revision: 1.2
done
Checking in lib/util/sectime.c;
/cvsroot/mozilla/security/nss/lib/util/sectime.c,v  <--  sectime.c
new revision: 1.2; previous revision: 1.1
done
Checking in tests/cert/cert.sh;
/cvsroot/mozilla/security/nss/tests/cert/cert.sh,v  <--  cert.sh
new revision: 1.25; previous revision: 1.24
done
Attachment #131654 - Attachment is obsolete: true
Resolved.
Status: NEW → RESOLVED
Closed: 21 years ago
Resolution: --- → FIXED
Blocks: 219082
Attachment #131154 - Flags: review?(rrelyea0264) → review+
Attachment #131637 - Flags: superreview?(MisterSSL)
Attachment #131637 - Flags: review?(wchang0222)
Attachment #131367 - Flags: superreview?(wchang0222)
Attachment #131367 - Flags: review?(MisterSSL)
Attachment #131643 - Flags: superreview?(MisterSSL)
Attachment #131643 - Flags: review?(wchang0222)
Attachment #131654 - Flags: superreview?(MisterSSL)
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: