Fix time offset calculation.
authorTodd Short <tshort@akamai.com>
Thu, 16 Feb 2017 21:08:02 +0000 (16:08 -0500)
committerRichard Levitte <levitte@openssl.org>
Tue, 2 May 2017 08:38:54 +0000 (10:38 +0200)
ASN1_GENERALIZEDTIME and ASN1_UTCTIME may be specified using offsets,
even though that's not supported within certificates.

To convert the offset time back to GMT, the offsets are supposed to be
subtracted, not added. e.g. 1759-0500 == 2359+0100 == 2259Z.

Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Richard Levitte <levitte@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/2654)

crypto/asn1/a_gentm.c
crypto/asn1/a_utctm.c
test/build.info
test/recipes/90-test_time_offset.t [new file with mode: 0644]
test/time_offset_test.c [new file with mode: 0644]

index c02c8d9acc953b19d93ef5ce21c3e1564465444b..ff1b6954756698e4175ca707c955ba3ac62213ad 100644 (file)
@@ -101,7 +101,7 @@ int asn1_generalizedtime_to_tm(struct tm *tm, const ASN1_GENERALIZEDTIME *d)
     if (a[o] == 'Z')
         o++;
     else if ((a[o] == '+') || (a[o] == '-')) {
-        int offsign = a[o] == '-' ? -1 : 1, offset = 0;
+        int offsign = a[o] == '-' ? 1 : -1, offset = 0;
         o++;
         if (o + 4 > l)
             goto err;
index 7916e300ef0509a523b7712d2d2382ca5c4c21b0..9797aa8a1eb4b781bf1b99a891a1f53676165040 100644 (file)
@@ -75,7 +75,7 @@ int asn1_utctime_to_tm(struct tm *tm, const ASN1_UTCTIME *d)
     if (a[o] == 'Z')
         o++;
     else if ((a[o] == '+') || (a[o] == '-')) {
-        int offsign = a[o] == '-' ? -1 : 1, offset = 0;
+        int offsign = a[o] == '-' ? 1 : -1, offset = 0;
         o++;
         if (o + 4 > l)
             goto err;
index 2b1ced8b822501a5438f6c9779439c1beaf4e6de..00ff4670f1f49fa425de62045b0d5a037384a561 100644 (file)
@@ -42,7 +42,8 @@ INCLUDE_MAIN___test_libtestutil_OLB = /INCLUDE=MAIN
           ssl_test_ctx_test ssl_test x509aux cipherlist_test asynciotest \
           bioprinttest sslapitest dtlstest sslcorrupttest bio_enc_test \
           pkey_meth_test uitest cipherbytes_test asn1_encode_test \
-          x509_time_test x509_dup_cert_test recordlentest
+          x509_time_test x509_dup_cert_test recordlentest \
+          time_offset_test
 
   SOURCE[aborttest]=aborttest.c
   INCLUDE[aborttest]=../include
@@ -345,6 +346,10 @@ INCLUDE_MAIN___test_libtestutil_OLB = /INCLUDE=MAIN
   INCLUDE[asn1_encode_test]=../include
   DEPEND[asn1_encode_test]=../libcrypto libtestutil.a
 
+  SOURCE[time_offset_test]=time_offset_test.c
+  INCLUDE[time_offset_test]=.. ../include
+  DEPEND[time_offset_test]=../libcrypto libtestutil.a
+
   # Internal test programs.  These are essentially a collection of internal
   # test routines.  Some of them need to reach internal symbols that aren't
   # available through the shared library (at least on Linux, Solaris, Windows
diff --git a/test/recipes/90-test_time_offset.t b/test/recipes/90-test_time_offset.t
new file mode 100644 (file)
index 0000000..a032d9b
--- /dev/null
@@ -0,0 +1,12 @@
+#! /usr/bin/env perl
+# Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+use OpenSSL::Test::Simple;
+
+simple_test("test_time_offset", "time_offset_test");
diff --git a/test/time_offset_test.c b/test/time_offset_test.c
new file mode 100644 (file)
index 0000000..7b4bec9
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* time_t/offset (+/-XXXX) tests for ASN1 and X509 */
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include <openssl/asn1.h>
+#include <openssl/x509.h>
+#include "testutil.h"
+#include "e_os.h"
+
+typedef struct {
+    const char *data;
+    int time_result;
+    int type;
+} TESTDATA;
+
+
+/**********************************************************************
+ *
+ * Test driver
+ *
+ ***/
+
+static TESTDATA tests[] = {
+    { "20001201000000Z",      0, V_ASN1_GENERALIZEDTIME },
+    { "20001201010000+0100",  0, V_ASN1_GENERALIZEDTIME },
+    { "20001201050000+0500",  0, V_ASN1_GENERALIZEDTIME },
+    { "20001130230000-0100",  0, V_ASN1_GENERALIZEDTIME },
+    { "20001130190000-0500",  0, V_ASN1_GENERALIZEDTIME },
+    { "20001130190001-0500",  1, V_ASN1_GENERALIZEDTIME }, /* +1 second */
+    { "20001130185959-0500", -1, V_ASN1_GENERALIZEDTIME }, /* -1 second */
+    { "001201000000Z",        0, V_ASN1_UTCTIME },
+    { "001201010000+0100",    0, V_ASN1_UTCTIME },
+    { "001201050000+0500",    0, V_ASN1_UTCTIME },
+    { "001130230000-0100",    0, V_ASN1_UTCTIME },
+    { "001130190000-0500",    0, V_ASN1_UTCTIME },
+    { "001201000000-0000",    0, V_ASN1_UTCTIME },
+    { "001201000001-0000",    1, V_ASN1_UTCTIME }, /* +1 second */
+    { "001130235959-0000",   -1, V_ASN1_UTCTIME }, /* -1 second */
+    { "20001201000000+0000",  0, V_ASN1_GENERALIZEDTIME },
+    { "20001201000000+0100", -1, V_ASN1_GENERALIZEDTIME },
+    { "001201000000+0100",   -1, V_ASN1_UTCTIME },
+    { "20001201000000-0100",  1, V_ASN1_GENERALIZEDTIME },
+    { "001201000000-0100",    1, V_ASN1_UTCTIME },
+    { "20001201123400+1234",  0, V_ASN1_GENERALIZEDTIME },
+    { "20001130112600-1234",  0, V_ASN1_GENERALIZEDTIME },
+};
+
+static time_t the_time = 975628800;
+static ASN1_TIME the_asn1_time = {
+    15,
+    V_ASN1_GENERALIZEDTIME,
+    (unsigned char*)"20001201000000Z",
+    0
+};
+
+static int test_offset(int idx)
+{
+    ASN1_TIME at;
+    const TESTDATA *testdata = &tests[idx];
+    int ret = -2;
+    int day, sec;
+
+    at.data = (unsigned char*)testdata->data;
+    at.length = strlen(testdata->data);
+    at.type = testdata->type;
+
+    if (!TEST_true(ASN1_TIME_diff(&day, &sec, &the_asn1_time, &at))) {
+        TEST_info("ASN1_TIME_diff() failed for %s\n", at.data);
+        return 0;
+    }
+    if (day > 0)
+        ret = 1;
+    else if (day < 0)
+        ret = -1;
+    else if (sec > 0)
+        ret = 1;
+    else if (sec < 0)
+        ret = -1;
+    else
+        ret = 0;
+
+    if (!TEST_int_eq(testdata->time_result, ret)) {
+        TEST_info("ASN1_TIME_diff() test failed for %s day=%d sec=%d\n", at.data, day, sec);
+        return 0;
+    }
+
+    if (at.type == V_ASN1_UTCTIME)
+        ret = ASN1_UTCTIME_cmp_time_t(&at, the_time);
+    else
+        return 1; /* no other cmp_time_t() functions available, yet */
+
+    if (!TEST_int_eq(testdata->time_result, ret)) {
+        TEST_info("ASN1_UTCTIME_cmp_time_t() test failed for %s\n", at.data);
+        return 0;
+    }
+
+    return 1;
+}
+
+void register_tests()
+{
+    ADD_ALL_TESTS(test_offset, OSSL_NELEM(tests));
+}