expiration time in ISO-8801 format

NIIBE Yutaka gniibe at fsij.org
Tue Sep 12 07:59:46 CEST 2017


Hello,

I'm trying to fix this issue:

    https://dev.gnupg.org/T3278

When a user specify expiration date and time by ISO-8801, GnuPG
currently interprets as UTC, which is not as same as ISO-8801
interpretation.

In ISO-8801, time is interpreted as local time if there is no "Z" or
timezone.

I think that GnuPG should be fixed, following the interpretation of
ISO-8801.  Perhaps, it is better to modify in multiple steps.

If so, it's like these steps:

(1) Firstly, modify GnuPG to accept format with timezone ("Z", and
    possibly +hh:mm, +hhmm, +hh or -hh:mm, -hhmm, and -hh).  Time with
    no timezone will be interpreted as local time, as before.

(2) Secondly (after some releases?), modify GnuPG to output its data
    always with "Z".

(3) Thirdly (after some more releases?), modify GnuPG to interpret
    time with no timezone as UTC.


Existing scripts and programs will be needed to change if it assume
local time.  And, since gpg-agent stores privete key with protected-at
attribute by ISO timestamp, modification affects passphrase expiration
time.


While looking around the code, the deepest one is in libksba.  Here is
to show fixing libksba, (1) partially, (2), (3) at once.

-- 

diff --git a/src/certreq.c b/src/certreq.c
index 46fca44..575ca9d 100644
--- a/src/certreq.c
+++ b/src/certreq.c
@@ -747,7 +747,7 @@ build_cri (ksba_certreq_t cr)
         *tp++ = 15;
         if (cr->x509.not_before[0])
           {
-            if (_ksba_cmp_time (cr->x509.not_before, "20500101T000000") >= 0)
+            if (_ksba_cmp_time (cr->x509.not_before, "20500101T000000Z") >= 0)
               {
                 memcpy (tp, cr->x509.not_before, 8);
                 tp += 8;
@@ -777,7 +777,7 @@ build_cri (ksba_certreq_t cr)
         *tp++ = 15;
         if (cr->x509.not_after[0])
           {
-            if (_ksba_cmp_time (cr->x509.not_after, "20500101T000000") >= 0)
+            if (_ksba_cmp_time (cr->x509.not_after, "20500101T000000Z") >= 0)
               {
                 memcpy (tp, cr->x509.not_after, 8);
                 tp += 8;
diff --git a/src/der-encoder.c b/src/der-encoder.c
index 9592123..71eeb4e 100644
--- a/src/der-encoder.c
+++ b/src/der-encoder.c
@@ -261,11 +261,11 @@ _ksba_der_store_time (AsnNode node, const ksba_isotime_t atime)
     return err;
 
   memcpy (buf, atime, 8);
-  memcpy (buf+8, atime+9, 6);
+  memcpy (buf+8, atime+9, 6);   /* Also accept format with no 'Z'.  */
   strcpy (buf+14, "Z");
 
   /* We need to use generalized time beginning with the year 2050. */
-  need_gen = (_ksba_cmp_time (atime, "20500101T000000") >= 0);
+  need_gen = (_ksba_cmp_time (atime, "20500101T000000Z") >= 0);
 
   if (node->type == TYPE_ANY)
     node->type = need_gen? TYPE_GENERALIZED_TIME : TYPE_UTC_TIME;
diff --git a/src/ksba.h b/src/ksba.h
index 955dc06..cb5d441 100644
--- a/src/ksba.h
+++ b/src/ksba.h
@@ -150,9 +150,8 @@ typedef enum
 ksba_key_usage_t;
 typedef ksba_key_usage_t KsbaKeyUsage _KSBA_DEPRECATED;
 
-/* ISO format, e.g. "19610711T172059", assumed to be UTC. */
-typedef char ksba_isotime_t[16];
-
+/* ISO format, e.g. "19610711T172059Z" in UTC. */
+typedef char ksba_isotime_t[17];
 
 /* X.509 certificates are represented by this object.
    ksba_cert_new() creates such an object */
diff --git a/src/time.c b/src/time.c
index d793476..cb4767b 100644
--- a/src/time.c
+++ b/src/time.c
@@ -91,13 +91,14 @@ _ksba_asntime_to_iso (const char *buffer, size_t length, int is_utctime,
     {
       memcpy (timebuf+9, s, 6);
     }
-  timebuf[15] = 0;
+  timebuf[15] = 'Z';
+  timebuf[16] = 0;
 
   return 0;
 }
 
 
-/* Return 0 if ATIME has the proper format (e.g. "19660205T131415"). */
+/* Return 0 if ATIME has the proper format (e.g. "19660205T131415Z"). */
 gpg_error_t
 _ksba_assert_time_format (const ksba_isotime_t atime)
 {
@@ -111,12 +112,15 @@ _ksba_assert_time_format (const ksba_isotime_t atime)
     if (!digitp (s))
       return gpg_error (GPG_ERR_BUG);
   if (*s != 'T')
-      return gpg_error (GPG_ERR_BUG);
+    return gpg_error (GPG_ERR_BUG);
   for (s++, i=9; i < 15; i++, s++)
     if (!digitp (s))
       return gpg_error (GPG_ERR_BUG);
-  if (*s)
-      return gpg_error (GPG_ERR_BUG);
+  if (*s == 0)
+    /* For backward compatibility, accept format with no 'Z' at the end.  */
+    return 0;
+  else if (*s++ != 'Z' || *s)
+    return gpg_error (GPG_ERR_BUG);
   return 0;
 }
 
@@ -127,14 +131,17 @@ void
 _ksba_copy_time (ksba_isotime_t d, const ksba_isotime_t s)
 {
   if (!*s)
-    memset (d, 0, 16);
+    memset (d, 0, 17);
   else if ( _ksba_assert_time_format (s) )
     {
       fprintf (stderr, "BUG: invalid isotime buffer\n");
       abort ();
     }
   else
-    strcpy (d, s);
+    {
+      strncpy (d, s, 16);
+      d[16] = 'Z';
+    }
 }
 
 
@@ -144,7 +151,7 @@ _ksba_copy_time (ksba_isotime_t d, const ksba_isotime_t s)
 int
 _ksba_cmp_time (const ksba_isotime_t a, const ksba_isotime_t b)
 {
-  return strcmp (a, b);
+  return strncmp (a, b, 16);
 }
 
 /* Fill the TIMEBUF with the current time (UTC of course). */
@@ -160,7 +167,7 @@ _ksba_current_time (ksba_isotime_t timebuf)
 #else
   tp = gmtime ( &epoch );
 #endif
-  sprintf (timebuf,"%04d%02d%02dT%02d%02d%02d",
+  sprintf (timebuf, "%04d%02d%02dT%02d%02d%02dZ",
            1900 + tp->tm_year, tp->tm_mon+1, tp->tm_mday,
            tp->tm_hour, tp->tm_min, tp->tm_sec);
 }
diff --git a/tests/t-common.h b/tests/t-common.h
index cf82f3e..bd6db69 100644
--- a/tests/t-common.h
+++ b/tests/t-common.h
@@ -225,5 +225,5 @@ print_time (ksba_isotime_t t)
   if (!t || !*t)
     fputs ("none", stdout);
   else
-    printf ("%.4s-%.2s-%.2s %.2s:%.2s:%s", t, t+4, t+6, t+9, t+11, t+13);
+    printf ("%.4s-%.2s-%.2s %.2s:%.2s:%.2s", t, t+4, t+6, t+9, t+11, t+13);
 }

================



More information about the Gnupg-devel mailing list