Page MenuHome GnuPG

explicit key expiration date/time is interpreted as UTC, is echoed back using local time, confusion ensues
Open, WishlistPublic

Description

I remember this working the last time I tried, but I wouldn't venture a guess when it stopped working.

Example:

./gpg2 --full-gen-key
gpg (GnuPG) 2.1.20; Copyright (C) 2017 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
Your selection?
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048)
Requested keysize is 2048 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 20191231T235959
Key expires at Tue Dec 31 15:59:59 2019 PST
Is this correct? (y/N)

The correct output should be "Key expires at Tue Dec 31 23:59:59 2019 PST"

This feature is not documented at all in the user guides but I've been using it for years. It's documented in the source code, but not very well. It looks to me that the first character after the "T" is being ignored. This format (no punctuation and a "T" separating the date and time) is the only format that can be used to specify a time. To specify a date with no time, YYYY-MM-DD can be used.

Details

Version
2.1.20

Event Timeline

mick updated the task description. (Show Details)
mick updated the task description. (Show Details)
mick updated the task description. (Show Details)
justus triaged this task as Wishlist priority.Jul 14 2017, 3:04 PM
justus added a subscriber: justus.

Hi :)

this discrepancy is easily explained. You are entering a date that is interpreted as UTC, and it is echoing it back using your local time zone. PST is UTC−8:00, matching the output.

The documentation should be improved.

justus renamed this task from Specifying explicit key expiration date/time does not result in correct date/time to explicit key expiration date/time is interpreted as UTC, is echoed back using local time, confusion ensues.Jul 14 2017, 3:08 PM

Hi Justin

Forgive the nit, but the comments in this section of code strongly suggest that ISO 8601 date/time format is used. With ISO 8601, the UTC timezone can be specified by appending a "Z" to the date/time string. Example:


2017-07-14T14:32:05Z

...or a +/-HH:MM string can be appended to reflect the offset from UTC:


2017-07-14T14:32:05-07:00

...but gnupg doesn't grok these valid ISO 8601 strings.

I understand how this could be seen as a documentation shortcoming, but I'm still thinking that ISO 8601 format should be supported. That would eliminate ambiguity and confusion.

Thanks

fwiw, i agree that GnuPG should interpret these as ISO-8601 strings. At the very least:

  • With no suffix, they should be interpreted as local time.
  • With a Z suffix, they should be interpreted as UTC.

If you want to delay implementation of, or ignore explicit +/-HH:MM suffixes for now, the above two fixes would be an improvement.

While I confirmed that GnuPG interprets YYYY-MM-DDThh:mm:ss in UTC (which should be interpret as local time according to ISO-8601), I don't know how we can fix this.
If I change the interpretation of GnuPG (possibly supporting the format with Z suffix and timezone), it may break existing script which assumes UTC.

It is pretty much confusing. When a user specify in YYYY-MM-DD format with no hh:mm:ss, it is interpreted as local time (noon of that day).
When a user adding Thh:mm:ss, it is UTC.

I think any existing script that assumes UTC should add an explicit Z suffix. (that is, i don't think the breakage is a big deal, and anyone writing scripts that needs this kind of precision will be more likely be thankful that we have a sensible, normalized interface).

[copied from gnupg-devel@]

The undocumented way to enter a timestamp instead of an interval was
introduced a long time ago and the time format reflects the internal
format already used by gpgsm. It was never intended as a way for humans
to enter a timestamp but for scripts using the machine interface
(i.e. using --command-fd/--with-colons). Thus, similiar to the fixed
UTF-8 encoding in this interface, all times are also expressed in UTC.
For example in GPGME we do this

#ifdef HAVE_TIMEGM
   return timegm (&buf);
#else
    {  /* Ugly HACK for systems without timegm.  */
      time_t tim;

      putenv ("TZ=UTC");
      tim = mktime (&buf);
#endif

which shows that the --with-colons interface is expected to return UTC.
IT is obvious that entering a date also requires UTC in the machine
interface - on the command line it can be different of course.

(3) Thirdly (after some more releases?), modify GnuPG to interpret

time with no timezone as UTC.

This is already the case.

Now, why do we use the compact 8601 format for timestamps? Around 2003
I wrote gpgsm and figured that there are already X.509 certificates with
expiration times after 2038-01-19. Thus on the common 32 bit systems it
was impossible to express them as time_t. I had a meeting with Uli
Drepper in Karlsruhe to convince him to change time_t in glibc to 64 bit
similar to how this was done with LFS. That was not successful because
he rejected the idea to express wall time with time_t (“time_t was never
been intended to express wall time”). We then tried to implement a
second time interface using a state of the art suggestion on how to
express times in a computer. However that failed because the time
interface too much interweaves with other libc services and we could not
come up with a solid and portable solution.

The way out was to do use something similar to what X.509 does: the
ASN.1 GeneralizedTime type. Thus the internal time format in gpgsm is
"YYYYMMDDTHHMMSS". For convenience and better alignment a 16 byte
buffer is used so that it is always a C string:

/* A type to hold the ISO time.  Note that this is the same as
   the KSBA type ksba_isotime_t. */
typedef char gnupg_isotime_t[16];

This is meanwhile also partly used in gpg. The advantage of this format
is that it can easily be sorted and compared, much similar to an
integer. And it has the real thing: No year 2038 problem in the
internal processing (displaying in human readable format is a different
story, though).

Carrying a timezone indicator would be bad because then the
gnupg_isotime_t can't be used as a time_t replacement.

The bottom line is that there should be no change in the representation
of the time. In particular not in libksba.

What could be added are more smart parsers in the human interface.
Accepting UTC (i.e. a Z or +0000 suffix in the machine interface would
also be okay as long as any other timezone would be rejected because the
machine interface requires UTC. Double timezone conversion of time
values is one of the most annoying bugs and thus (similar to
standardizing on UTF-8), there shall be only UTC in the machine
interface and internal representation.

I'm fine with (and i totally understand) wanting nothing but UTC in the machine interface and internal representations.

there are some (odd, rare) non-OpenPGP use cases for wanting to record time zone data in machine-readable form, but i don't see any need to include them in GnuPG.

However, for the human-accessible interfaces, we need to acknowledge that humans use local time, and that sometimes humans want to specify a specific external timezone. and we *definitely* shouldn't confuse humans by accepting their input in local time and showing it to them in UTC, or the other way around.

One of the difficulties here is that parts of the GnuPG interface is used for both humans and machines. Can we meet all of these goals?

OK, I changed my own purpose. I don't touch internal representations.

Now, I focus on human interface of gpg frontend.

As an initial change, I change the code for gpg, accepting the 'Z' suffix.