summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2022-08-11 12:41:05 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2022-08-11 12:41:05 +0700
commit2447cea7296abf3b77207eeeb39dc75ae2a73a4e (patch)
tree2e01b1b243d0b9a506934765632685931ebf40aa
parentAdd note for future Arm implementation (diff)
downloadBouncyCastle.NET-ed25519-2447cea7296abf3b77207eeeb39dc75ae2a73a4e.tar.xz
GeneralizedTime improvements
-rw-r--r--crypto/src/asn1/DerGeneralizedTime.cs94
-rw-r--r--crypto/test/src/asn1/test/GeneralizedTimeTest.cs87
2 files changed, 158 insertions, 23 deletions
diff --git a/crypto/src/asn1/DerGeneralizedTime.cs b/crypto/src/asn1/DerGeneralizedTime.cs
index 16774cb02..22dc04912 100644
--- a/crypto/src/asn1/DerGeneralizedTime.cs
+++ b/crypto/src/asn1/DerGeneralizedTime.cs
@@ -139,32 +139,28 @@ namespace Org.BouncyCastle.Asn1
             // standardise the format.
             //
             if (time[time.Length - 1] == 'Z')
-            {
                 return time.Substring(0, time.Length - 1) + "GMT+00:00";
+
+            int signPos = time.Length - 5;
+            char sign = time[signPos];
+            if (sign == '-' || sign == '+')
+            {
+                return time.Substring(0, signPos)
+                    + "GMT"
+                    + time.Substring(signPos, 3)
+                    + ":"
+                    + time.Substring(signPos + 3);
             }
             else
             {
-                int signPos = time.Length - 5;
-                char sign = time[signPos];
+                signPos = time.Length - 3;
+                sign = time[signPos];
                 if (sign == '-' || sign == '+')
                 {
                     return time.Substring(0, signPos)
                         + "GMT"
-                        + time.Substring(signPos, 3)
-                        + ":"
-                        + time.Substring(signPos + 3);
-                }
-                else
-                {
-                    signPos = time.Length - 3;
-                    sign = time[signPos];
-                    if (sign == '-' || sign == '+')
-                    {
-                        return time.Substring(0, signPos)
-                            + "GMT"
-                            + time.Substring(signPos)
-                            + ":00";
-                    }
+                        + time.Substring(signPos)
+                        + ":00";
                 }
             }
 
@@ -212,10 +208,18 @@ namespace Org.BouncyCastle.Asn1
                     int fCount = d.Length - d.IndexOf('.') - 2;
                     formatStr = @"yyyyMMddHHmmss." + FString(fCount) + @"\Z";
                 }
-                else
+                else if (HasSeconds)
                 {
                     formatStr = @"yyyyMMddHHmmss\Z";
                 }
+                else if (HasMinutes)
+                {
+                    formatStr = @"yyyyMMddHHmm\Z";
+                }
+                else
+                {
+                    formatStr = @"yyyyMMddHH\Z";
+                }
             }
             else if (time.IndexOf('-') > 0 || time.IndexOf('+') > 0)
             {
@@ -239,10 +243,18 @@ namespace Org.BouncyCastle.Asn1
                     int fCount = d.Length - 1 - d.IndexOf('.');
                     formatStr = @"yyyyMMddHHmmss." + FString(fCount);
                 }
-                else
+                else if (HasSeconds)
                 {
                     formatStr = @"yyyyMMddHHmmss";
                 }
+                else if (HasMinutes)
+                {
+                    formatStr = @"yyyyMMddHHmm";
+                }
+                else
+                {
+                    formatStr = @"yyyyMMddHH";
+                }
 
                 // TODO?
 //				dateF.setTimeZone(new SimpleTimeZone(0, TimeZone.getDefault().getID()));
@@ -291,19 +303,55 @@ namespace Org.BouncyCastle.Asn1
             get { return time.IndexOf('.') == 14; }
         }
 
-        private byte[] GetOctets()
+        private bool HasSeconds =>  IsDigit(12) && IsDigit(13);
+
+        private bool HasMinutes => IsDigit(10) && IsDigit(11);
+
+        private bool IsDigit(int pos)
+        {
+            return time.Length > pos && char.IsDigit(time[pos]);
+        }
+
+        private byte[] GetOctets(int encoding)
         {
+            if (Asn1OutputStream.EncodingDer == encoding && time[time.Length - 1] == 'Z')
+            {
+                if (!HasMinutes)
+                    return Strings.ToAsciiByteArray(time.Insert(time.Length - 1, "0000"));
+                if (!HasSeconds)
+                    return Strings.ToAsciiByteArray(time.Insert(time.Length - 1, "00"));
+
+                if (HasFractionalSeconds)
+                {
+                    int ind = time.Length - 2;
+                    while (ind > 0 && time[ind] == '0')
+                    {
+                        --ind;
+                    }
+
+                    if (time[ind] != '.')
+                    {
+                        ++ind;
+                    }
+
+                    if (ind != time.Length - 1)
+                    {
+                        return Strings.ToAsciiByteArray(time.Remove(ind, time.Length - 1 - ind));
+                    }
+                }
+            }
+
             return Strings.ToAsciiByteArray(time);
         }
 
         internal override IAsn1Encoding GetEncoding(int encoding)
         {
-            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.GeneralizedTime, GetOctets());
+            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.GeneralizedTime, GetOctets(encoding));
         }
 
         internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
         {
-            return new PrimitiveEncoding(tagClass, tagNo, GetOctets());
+            return new PrimitiveEncoding(tagClass, tagNo, GetOctets(encoding));
         }
 
         protected override bool Asn1Equals(Asn1Object asn1Object)
diff --git a/crypto/test/src/asn1/test/GeneralizedTimeTest.cs b/crypto/test/src/asn1/test/GeneralizedTimeTest.cs
index 4df2666b3..d9d84462a 100644
--- a/crypto/test/src/asn1/test/GeneralizedTimeTest.cs
+++ b/crypto/test/src/asn1/test/GeneralizedTimeTest.cs
@@ -2,6 +2,8 @@ using System;
 
 using NUnit.Framework;
 
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
 using Org.BouncyCastle.Utilities.Test;
 
 namespace Org.BouncyCastle.Asn1.Tests
@@ -113,6 +115,43 @@ namespace Org.BouncyCastle.Asn1.Tests
             "20020122022220.000Z"
         };
 
+        private static readonly string[] derMzOutput =
+        {
+            "20020122122220Z",
+            "20020122122220Z",
+            "20020122222220Z",
+            "20020122122220Z",
+            "20020122122220.1Z",
+            "20020122122220.1Z",
+            "20020122222220.1Z",
+            "20020122122220.1Z",
+            "20020122122220.01Z",
+            "20020122122220.01Z",
+            "20020122222220.01Z",
+            "20020122122220.01Z",
+            "20020122122220.001Z",
+            "20020122122220.001Z",
+            "20020122222220.001Z",
+            "20020122122220.001Z",
+            "20020122122220Z",
+            "20020122122220Z",
+            "20020122222220Z",
+            "20020122122220Z",
+            "20020122022220Z"
+        };
+
+        private static readonly string[] truncOutput =
+        {
+            "200201221222Z",
+            "2002012212Z"
+        };
+
+        private static readonly string[] derTruncOutput =
+        {
+            "20020122122200Z",
+            "20020122120000Z"
+        };
+
         public override string Name
         {
             get { return "GeneralizedTime"; }
@@ -164,6 +203,54 @@ namespace Org.BouncyCastle.Asn1.Tests
                 }
             }
 
+            // TODO
+            //for (int i = 0; i != mzOutput.Length; i++)
+            //{
+            //    DerGeneralizedTime t = new DerGeneralizedTime(mzOutput[i]);
+
+            //    if (!AreEqual(t.GetEncoded(), new DerGeneralizedTime(derMzOutput[i]).GetEncoded()))
+            //    {
+            //        Fail("der encoding wrong");
+            //    }
+            //}
+
+            // TODO
+            //for (int i = 0; i != truncOutput.Length; i++)
+            //{
+            //    DerGeneralizedTime t = new DerGeneralizedTime(truncOutput[i]);
+
+            //    if (!AreEqual(t.GetEncoded(), new DerGeneralizedTime(derTruncOutput[i]).GetEncoded()))
+            //    {
+            //        Fail("trunc der encoding wrong");
+            //    }
+            //}
+
+            {
+                // check BER encoding is still "as given"
+                DerGeneralizedTime t = new DerGeneralizedTime("202208091215Z");
+
+                //IsTrue(Arrays.AreEqual(Hex.Decode("180d3230323230383039313231355a"), t.GetEncoded(Asn1Encodable.DL)));
+                IsTrue(Arrays.AreEqual(Hex.Decode("180d3230323230383039313231355a"), t.GetEncoded(Asn1Encodable.Ber)));
+                IsTrue(Arrays.AreEqual(Hex.Decode("180f32303232303830393132313530305a"), t.GetEncoded(Asn1Encodable.Der)));
+            }
+
+            // TODO
+            //{
+            //    // check an actual GMT string comes back untampered
+            //    DerGeneralizedTime time = new DerGeneralizedTime("20190704031318GMT+00:00");
+
+            //    IsTrue("20190704031318GMT+00:00".Equals(time.GetTime()));
+
+            //    try
+            //    {
+            //        DerGeneralizedTime.GetInstance(new byte[0]);
+            //    }
+            //    catch (ArgumentException e)
+            //    {
+            //        IsTrue(e.Message.Equals("GeneralizedTime string too short"));
+            //    }
+            //}
+
             /*
              * [BMA-87]
              */