Use ASN1 UTC tags for dates before 2000

Signed-off-by: Werner Lewis <werner.lewis@arm.com>
diff --git a/ChangeLog.d/fix-time-format-pre-2000.txt b/ChangeLog.d/fix-time-format-pre-2000.txt
new file mode 100644
index 0000000..414201e
--- /dev/null
+++ b/ChangeLog.d/fix-time-format-pre-2000.txt
@@ -0,0 +1,3 @@
+Bugfix
+   * Encode X.509 dates before 1/1/2000 as UTCTime rather than
+     GeneralizedTime. Fixes #5465.
diff --git a/library/x509write_crt.c b/library/x509write_crt.c
index 184c90c..0c5e991 100644
--- a/library/x509write_crt.c
+++ b/library/x509write_crt.c
@@ -299,7 +299,7 @@
     /*
      * write MBEDTLS_ASN1_UTC_TIME if year < 2050 (2 bytes shorter)
      */
-    if( t[0] == '2' && t[1] == '0' && t[2] < '5' )
+    if( t[0] < '2' || ( t[0] == '2' && t[1] == '0' && t[2] < '5' ) )
     {
         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_raw_buffer( p, start,
                                              (const unsigned char *) t + 2,
diff --git a/tests/suites/test_suite_x509write.data b/tests/suites/test_suite_x509write.data
index e0f80be..87f28a7 100644
--- a/tests/suites/test_suite_x509write.data
+++ b/tests/suites/test_suite_x509write.data
@@ -58,6 +58,22 @@
 depends_on:MBEDTLS_SHA1_C:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15:MBEDTLS_DES_C:MBEDTLS_CIPHER_MODE_CBC:MBEDTLS_MD5_C
 x509_crt_check:"data_files/server1.key":"":"C=NL,O=PolarSSL,CN=PolarSSL Server 1":"data_files/test-ca.key":"PolarSSLTest":"C=NL,O=PolarSSL,CN=PolarSSL Test CA":"1":"20190210144406":"20290210144406":MBEDTLS_MD_SHA1:0:0:0:0:1:-1:"data_files/server1.crt":0:0
 
+Certificate write check Server1 SHA1, not before 1970
+depends_on:MBEDTLS_SHA1_C:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15:MBEDTLS_DES_C:MBEDTLS_CIPHER_MODE_CBC:MBEDTLS_MD5_C
+x509_crt_check:"data_files/server1.key":"":"C=NL,O=PolarSSL,CN=PolarSSL Server 1":"data_files/test-ca.key":"PolarSSLTest":"C=NL,O=PolarSSL,CN=PolarSSL Test CA":"1":"19700210144406":"20290210144406":MBEDTLS_MD_SHA1:0:0:0:0:1:-1:"":0:0
+
+Certificate write check Server1 SHA1, not after 2050
+depends_on:MBEDTLS_SHA1_C:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15:MBEDTLS_DES_C:MBEDTLS_CIPHER_MODE_CBC:MBEDTLS_MD5_C
+x509_crt_check:"data_files/server1.key":"":"C=NL,O=PolarSSL,CN=PolarSSL Server 1":"data_files/test-ca.key":"PolarSSLTest":"C=NL,O=PolarSSL,CN=PolarSSL Test CA":"1":"20190210144406":"20500210144406":MBEDTLS_MD_SHA1:0:0:0:0:1:-1:"":0:0
+
+Certificate write check Server1 SHA1, not before 1970, not after 2050
+depends_on:MBEDTLS_SHA1_C:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15:MBEDTLS_DES_C:MBEDTLS_CIPHER_MODE_CBC:MBEDTLS_MD5_C
+x509_crt_check:"data_files/server1.key":"":"C=NL,O=PolarSSL,CN=PolarSSL Server 1":"data_files/test-ca.key":"PolarSSLTest":"C=NL,O=PolarSSL,CN=PolarSSL Test CA":"1":"19700210144406":"20500210144406":MBEDTLS_MD_SHA1:0:0:0:0:1:-1:"":0:0
+
+Certificate write check Server1 SHA1, not before 2050, not after 2059
+depends_on:MBEDTLS_SHA1_C:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15:MBEDTLS_DES_C:MBEDTLS_CIPHER_MODE_CBC:MBEDTLS_MD5_C
+x509_crt_check:"data_files/server1.key":"":"C=NL,O=PolarSSL,CN=PolarSSL Server 1":"data_files/test-ca.key":"PolarSSLTest":"C=NL,O=PolarSSL,CN=PolarSSL Test CA":"1":"20500210144406":"20590210144406":MBEDTLS_MD_SHA1:0:0:0:0:1:-1:"":0:0
+
 Certificate write check Server1 SHA1, key_usage
 depends_on:MBEDTLS_SHA1_C:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15:MBEDTLS_DES_C:MBEDTLS_CIPHER_MODE_CBC:MBEDTLS_MD5_C
 x509_crt_check:"data_files/server1.key":"":"C=NL,O=PolarSSL,CN=PolarSSL Server 1":"data_files/test-ca.key":"PolarSSLTest":"C=NL,O=PolarSSL,CN=PolarSSL Test CA":"1":"20190210144406":"20290210144406":MBEDTLS_MD_SHA1:MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_NON_REPUDIATION | MBEDTLS_X509_KU_KEY_ENCIPHERMENT:1:0:0:1:-1:"data_files/server1.key_usage.crt":0:0
diff --git a/tests/suites/test_suite_x509write.function b/tests/suites/test_suite_x509write.function
index ce475e6..6a9cfeb 100644
--- a/tests/suites/test_suite_x509write.function
+++ b/tests/suites/test_suite_x509write.function
@@ -208,8 +208,10 @@
     mbedtls_x509write_cert crt;
     unsigned char buf[4096];
     unsigned char check_buf[5000];
+    unsigned char *p, *end;
+    unsigned char tag, sz;
     mbedtls_mpi serial;
-    int ret;
+    int ret, before_tag, after_tag;
     size_t olen = 0, pem_len = 0, buf_index = 0;
     int der_len = -1;
     FILE *f;
@@ -287,14 +289,16 @@
         TEST_ASSERT( buf[buf_index] == 0 );
     }
 
-    f = fopen( cert_check_file, "r" );
-    TEST_ASSERT( f != NULL );
-    olen = fread( check_buf, 1, sizeof( check_buf ), f );
-    fclose( f );
-    TEST_ASSERT( olen < sizeof( check_buf ) );
-
-    TEST_ASSERT( olen >= pem_len - 1 );
-    TEST_ASSERT( memcmp( buf, check_buf, pem_len - 1 ) == 0 );
+    if( *cert_check_file != '\0' )
+    {
+        f = fopen( cert_check_file, "r" );
+        TEST_ASSERT( f != NULL );
+        olen = fread( check_buf, 1, sizeof( check_buf ), f );
+        fclose( f );
+        TEST_ASSERT( olen < sizeof( check_buf ) );
+        TEST_ASSERT( olen >= pem_len - 1 );
+        TEST_ASSERT( memcmp( buf, check_buf, pem_len - 1 ) == 0 );
+    }
 
     der_len = mbedtls_x509write_crt_der( &crt, buf, sizeof( buf ),
                                          mbedtls_test_rnd_pseudo_rand,
@@ -304,6 +308,54 @@
     if( der_len == 0 )
         goto exit;
 
+    // Not testing against file, check date format
+    if( *cert_check_file == '\0' )
+    {
+        // UTC tag if before 2050, 2 digits less for year
+        if( not_before[0] == '2' && ( not_before[1] > '0' || not_before[2] > '4' ) )
+        {
+            before_tag = MBEDTLS_ASN1_GENERALIZED_TIME;
+        }
+        else
+        {
+            before_tag = MBEDTLS_ASN1_UTC_TIME;
+            not_before += 2;
+        }
+        if( not_after[0] == '2' && ( not_after[1] > '0' || not_after[2] > '4' ) )
+        {
+            after_tag = MBEDTLS_ASN1_GENERALIZED_TIME;
+        }
+        else
+        {
+            after_tag = MBEDTLS_ASN1_UTC_TIME;
+            not_after += 2;
+        }
+        end = buf + sizeof( buf );
+        for( p = end - der_len ; p < end ; )
+        {
+            tag = *p++;
+            sz = *p++;
+            if( tag == MBEDTLS_ASN1_UTC_TIME || tag == MBEDTLS_ASN1_GENERALIZED_TIME )
+            {
+                // Check correct tag and time written
+                TEST_ASSERT( before_tag == tag );
+                TEST_ASSERT( memcmp( p, not_before, sz - 1 ) == 0 );
+                p += sz;
+                tag = *p++;
+                sz = *p++;
+                TEST_ASSERT( after_tag == tag );
+                TEST_ASSERT( memcmp( p, not_after, sz - 1 ) == 0 );
+                break;
+            }
+            // Increment if long form ASN1 length
+            if( sz & 0x80 )
+                p += sz & 0x0F;
+            if( tag != ( MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) )
+                p += sz;
+        }
+        TEST_ASSERT( p < end );
+    }
+
     ret = mbedtls_x509write_crt_der( &crt, buf, (size_t)( der_len - 1 ),
                                      mbedtls_test_rnd_pseudo_rand, &rnd_info );
     TEST_ASSERT( ret == MBEDTLS_ERR_ASN1_BUF_TOO_SMALL );