summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2021-10-15 17:10:01 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2021-10-15 17:10:01 +0700
commitf84f98dcb1fff8ffdfa8a3b47708c6eb8f15c5cf (patch)
tree38160e84b7de77e8327fafddca26c1571c2d780d
parentMerge checks (diff)
downloadBouncyCastle.NET-ed25519-f84f98dcb1fff8ffdfa8a3b47708c6eb8f15c5cf.tar.xz
Use primitive encoding for short octet strings
-rw-r--r--crypto/src/asn1/BerOctetString.cs135
-rw-r--r--crypto/src/asn1/DerOctetString.cs7
-rw-r--r--crypto/test/src/asn1/test/CMSTest.cs46
-rw-r--r--crypto/test/src/asn1/test/PKCS12Test.cs23
4 files changed, 135 insertions, 76 deletions
diff --git a/crypto/src/asn1/BerOctetString.cs b/crypto/src/asn1/BerOctetString.cs
index b092d8fb2..4855e31d1 100644
--- a/crypto/src/asn1/BerOctetString.cs
+++ b/crypto/src/asn1/BerOctetString.cs
@@ -9,7 +9,7 @@ namespace Org.BouncyCastle.Asn1
     public class BerOctetString
         : DerOctetString, IEnumerable
     {
-        private static readonly int DefaultChunkSize = 1000;
+        private static readonly int DefaultSegmentLimit = 1000;
 
         public static BerOctetString FromSequence(Asn1Sequence seq)
         {
@@ -17,7 +17,7 @@ namespace Org.BouncyCastle.Asn1
             Asn1OctetString[] v = new Asn1OctetString[count];
             for (int i = 0; i < count; ++i)
             {
-                v[i] = Asn1OctetString.GetInstance(seq[i]);
+                v[i] = GetInstance(seq[i]);
             }
             return new BerOctetString(v);
         }
@@ -62,13 +62,13 @@ namespace Org.BouncyCastle.Asn1
             Asn1OctetString[] v = new Asn1OctetString[count];
             for (int i = 0; i < count; ++i)
             {
-                v[i] = Asn1OctetString.GetInstance(list[i]);
+                v[i] = GetInstance(list[i]);
             }
             return v;
         }
 
-        private readonly int chunkSize;
-        private readonly Asn1OctetString[] octs;
+        private readonly int segmentLimit;
+        private readonly Asn1OctetString[] elements;
 
         [Obsolete("Will be removed")]
         public BerOctetString(IEnumerable e)
@@ -77,30 +77,30 @@ namespace Org.BouncyCastle.Asn1
         }
 
         public BerOctetString(byte[] str)
-			: this(str, DefaultChunkSize)
+			: this(str, DefaultSegmentLimit)
 		{
 		}
 
-        public BerOctetString(Asn1OctetString[] octs)
-            : this(octs, DefaultChunkSize)
+        public BerOctetString(Asn1OctetString[] elements)
+            : this(elements, DefaultSegmentLimit)
         {
         }
 
-        public BerOctetString(byte[] str, int chunkSize)
-            : this(str, null, chunkSize)
+        public BerOctetString(byte[] str, int segmentLimit)
+            : this(str, null, segmentLimit)
         {
         }
 
-        public BerOctetString(Asn1OctetString[] octs, int chunkSize)
-            : this(FlattenOctetStrings(octs), octs, chunkSize)
+        public BerOctetString(Asn1OctetString[] elements, int segmentLimit)
+            : this(FlattenOctetStrings(elements), elements, segmentLimit)
         {
         }
 
-        private BerOctetString(byte[] str, Asn1OctetString[] octs, int chunkSize)
-            : base(str)
+        private BerOctetString(byte[] octets, Asn1OctetString[] elements, int segmentLimit)
+            : base(octets)
         {
-            this.octs = octs;
-            this.chunkSize = chunkSize;
+            this.elements = elements;
+            this.segmentLimit = segmentLimit;
         }
 
         /**
@@ -108,10 +108,10 @@ namespace Org.BouncyCastle.Asn1
          */
 		public IEnumerator GetEnumerator()
 		{
-			if (octs == null)
-                return new ChunkEnumerator(str, chunkSize);
+			if (elements == null)
+                return new ChunkEnumerator(str, segmentLimit);
 
-			return octs.GetEnumerator();
+			return elements.GetEnumerator();
 		}
 
 		[Obsolete("Use GetEnumerator() instead")]
@@ -120,82 +120,119 @@ namespace Org.BouncyCastle.Asn1
 			return GetEnumerator();
 		}
 
+        private bool IsConstructed
+        {
+            get { return null != elements || str.Length > segmentLimit; }
+        }
+
         internal override int EncodedLength(bool withID)
         {
             throw Platform.CreateNotImplementedException("BerOctetString.EncodedLength");
+
+            // TODO This depends on knowing it's not DER
+            //if (!IsConstructed)
+            //    return EncodedLength(withID, str.Length);
+
+            //int totalLength = withID ? 4 : 3;
+
+            //if (null != elements)
+            //{
+            //    for (int i = 0; i < elements.Length; ++i)
+            //    {
+            //        totalLength += elements[i].EncodedLength(true);
+            //    }
+            //}
+            //else
+            //{
+            //    int fullSegments = str.Length / segmentLimit;
+            //    totalLength += fullSegments * EncodedLength(true, segmentLimit);
+
+            //    int lastSegmentLength = str.Length - (fullSegments * segmentLimit);
+            //    if (lastSegmentLength > 0)
+            //    {
+            //        totalLength += EncodedLength(true, lastSegmentLength);
+            //    }
+            //}
+
+            //return totalLength;
         }
 
         internal override void Encode(Asn1OutputStream asn1Out, bool withID)
         {
-            if (asn1Out.IsBer)
+            if (!asn1Out.IsBer || !IsConstructed)
             {
-                if (withID)
-                {
-                    asn1Out.WriteByte(Asn1Tags.Constructed | Asn1Tags.OctetString);
-                }
+                base.Encode(asn1Out, withID);
+                return;
+            }
 
-                asn1Out.WriteByte(0x80);
+            asn1Out.WriteIdentifier(withID, Asn1Tags.Constructed | Asn1Tags.OctetString);
+            asn1Out.WriteByte(0x80);
 
-                foreach (Asn1OctetString oct in this)
-                {
-                    oct.Encode(asn1Out, true);
-                }
-
-				asn1Out.WriteByte(0x00);
-                asn1Out.WriteByte(0x00);
+            if (null != elements)
+            {
+                asn1Out.WritePrimitives(elements);
             }
             else
             {
-                base.Encode(asn1Out, withID);
+                int pos = 0;
+                while (pos < str.Length)
+                {
+                    int segmentLength = System.Math.Min(str.Length - pos, segmentLimit);
+                    Encode(asn1Out, true, str, pos, segmentLength);
+                    pos += segmentLength;
+                }
             }
+
+            asn1Out.WriteByte(0x00);
+            asn1Out.WriteByte(0x00);
         }
 
         private class ChunkEnumerator
             : IEnumerator
         {
             private readonly byte[] octets;
-            private readonly int chunkSize;
+            private readonly int segmentLimit;
 
-            private DerOctetString currentChunk = null;
-            private int nextChunkPos = 0;
+            private DerOctetString currentSegment = null;
+            private int nextSegmentPos = 0;
 
-            internal ChunkEnumerator(byte[] octets, int chunkSize)
+            internal ChunkEnumerator(byte[] octets, int segmentLimit)
             {
                 this.octets = octets;
-                this.chunkSize = chunkSize;
+                this.segmentLimit = segmentLimit;
             }
 
             public object Current
             {
                 get
                 {
-                    if (null == currentChunk)
+                    if (null == currentSegment)
                         throw new InvalidOperationException();
 
-                    return currentChunk;
+                    return currentSegment;
                 }
             }
 
             public bool MoveNext()
             {
-                if (nextChunkPos >= octets.Length)
+                if (nextSegmentPos >= octets.Length)
                 {
-                    this.currentChunk = null;
+                    this.currentSegment = null;
                     return false;
                 }
 
-                int length = System.Math.Min(octets.Length - nextChunkPos, chunkSize);
-                byte[] chunk = new byte[length];
-                Array.Copy(octets, nextChunkPos, chunk, 0, length);
-                this.currentChunk = new DerOctetString(chunk);
-                this.nextChunkPos += length;
+                int length = System.Math.Min(octets.Length - nextSegmentPos, segmentLimit);
+                byte[] segment = new byte[length];
+                Array.Copy(octets, nextSegmentPos, segment, 0, length);
+                this.currentSegment = new DerOctetString(segment);
+                this.nextSegmentPos += length;
                 return true;
             }
 
             public void Reset()
             {
-                this.currentChunk = null;
-                this.nextChunkPos = 0;
+                this.currentSegment = null;
+                this.nextSegmentPos = 0;
             }
         }
     }
diff --git a/crypto/src/asn1/DerOctetString.cs b/crypto/src/asn1/DerOctetString.cs
index bdabb8221..bcd4e7333 100644
--- a/crypto/src/asn1/DerOctetString.cs
+++ b/crypto/src/asn1/DerOctetString.cs
@@ -36,5 +36,10 @@ namespace Org.BouncyCastle.Asn1
 		{
 			asn1Out.WriteEncodingDL(withID, Asn1Tags.OctetString, buf, off, len);
 		}
-	}
+
+        internal static int EncodedLength(bool withID, int contentsLength)
+        {
+            return Asn1OutputStream.GetLengthOfEncodingDL(withID, contentsLength);
+        }
+    }
 }
diff --git a/crypto/test/src/asn1/test/CMSTest.cs b/crypto/test/src/asn1/test/CMSTest.cs
index 1afb363af..9930830f1 100644
--- a/crypto/test/src/asn1/test/CMSTest.cs
+++ b/crypto/test/src/asn1/test/CMSTest.cs
@@ -18,7 +18,7 @@ namespace Org.BouncyCastle.Asn1.Tests
 		//
 		// compressed data object
 		//
-		private static readonly byte[] compData = Base64.Decode(
+		private static readonly byte[] OrigCompData = Base64.Decode(
 			"MIAGCyqGSIb3DQEJEAEJoIAwgAIBADANBgsqhkiG9w0BCRADCDCABgkqhkiG9w0BBwGggCSABIIC"
 			+ "Hnic7ZRdb9owFIbvK/k/5PqVYPFXGK12YYyboVFASSp1vQtZGiLRACZE49/XHoUW7S/0tXP8Efux"
 			+ "fU5ivWnasml72XFb3gb5druui7ytN803M570nii7C5r8tfwR281hy/p/KSM3+jzH5s3+pbQ90xSb"
@@ -72,7 +72,7 @@ namespace Org.BouncyCastle.Asn1.Tests
 		//
 		// signed data
 		//
-		private static readonly byte[] signedData = Base64.Decode(
+		private static readonly byte[] OrigSignedData = Base64.Decode(
 			  "MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAaCA"
 			+ "JIAEDEhlbGxvIFdvcmxkIQAAAAAAAKCCBGIwggINMIIBdqADAgECAgEBMA0GCSqG"
 			+ "SIb3DQEBBAUAMCUxFjAUBgNVBAoTDUJvdW5jeSBDYXN0bGUxCzAJBgNVBAYTAkFV"
@@ -114,14 +114,10 @@ namespace Org.BouncyCastle.Asn1.Tests
 		{
 			try
 			{
-				ContentInfo info = ContentInfo.GetInstance(
-					Asn1Object.FromByteArray(compData));
-				CompressedData data = CompressedData.GetInstance(info.Content);
-
-				data = new CompressedData(data.CompressionAlgorithmIdentifier, data.EncapContentInfo);
-				info = new ContentInfo(CmsObjectIdentifiers.CompressedData, data);
+                byte[] compData1 = ImplCompressionTest(OrigCompData);
+                byte[] compData2 = ImplCompressionTest(compData1);
 
-				if (!Arrays.AreEqual(info.GetEncoded(), compData))
+				if (!Arrays.AreEqual(compData1, compData2))
 				{
 					return new SimpleTestResult(false, Name + ": CMS compression failed to re-encode");
 				}
@@ -134,7 +130,16 @@ namespace Org.BouncyCastle.Asn1.Tests
 			}
 		}
 
-		private ITestResult EnvelopedTest()
+        private byte[] ImplCompressionTest(byte[] compData)
+        {
+            ContentInfo info = ContentInfo.GetInstance(Asn1Object.FromByteArray(compData));
+            CompressedData data = CompressedData.GetInstance(info.Content);
+            data = new CompressedData(data.CompressionAlgorithmIdentifier, data.EncapContentInfo);
+            info = new ContentInfo(CmsObjectIdentifiers.CompressedData, data);
+            return info.GetEncoded();
+        }
+
+        private ITestResult EnvelopedTest()
 		{
 			try
 			{
@@ -243,14 +248,10 @@ namespace Org.BouncyCastle.Asn1.Tests
 		{
 			try
 			{
-				ContentInfo info = ContentInfo.GetInstance(
-					Asn1Object.FromByteArray(signedData));
-				SignedData sData = SignedData.GetInstance(info.Content);
-
-				sData = new SignedData(sData.DigestAlgorithms, sData.EncapContentInfo, sData.Certificates, sData.CRLs, sData.SignerInfos);
-				info = new ContentInfo(CmsObjectIdentifiers.SignedData, sData);
+                byte[] signedData1 = ImplSignedTest(OrigSignedData);
+                byte[] signedData2 = ImplSignedTest(signedData1);
 
-				if (!Arrays.AreEqual(info.GetEncoded(), signedData))
+				if (!Arrays.AreEqual(signedData1, signedData2))
 				{
 					return new SimpleTestResult(false, Name + ": CMS signed failed to re-encode");
 				}
@@ -263,7 +264,16 @@ namespace Org.BouncyCastle.Asn1.Tests
 			}
 		}
 
-		public ITestResult Perform()
+        private byte[] ImplSignedTest(byte[] signedData)
+        {
+            ContentInfo info = ContentInfo.GetInstance(Asn1Object.FromByteArray(signedData));
+            SignedData sData = SignedData.GetInstance(info.Content);
+            sData = new SignedData(sData.DigestAlgorithms, sData.EncapContentInfo, sData.Certificates, sData.CRLs, sData.SignerInfos);
+            info = new ContentInfo(CmsObjectIdentifiers.SignedData, sData);
+            return info.GetEncoded();
+        }
+
+        public ITestResult Perform()
 		{
 			ITestResult res = CompressionTest();
 
diff --git a/crypto/test/src/asn1/test/PKCS12Test.cs b/crypto/test/src/asn1/test/PKCS12Test.cs
index 5e4748728..eae61de46 100644
--- a/crypto/test/src/asn1/test/PKCS12Test.cs
+++ b/crypto/test/src/asn1/test/PKCS12Test.cs
@@ -113,6 +113,20 @@ namespace Org.BouncyCastle.Asn1.Tests
 			+ "AgFkAAA=");
 
 		public override void PerformTest()
+        {
+            byte[] pfxEncoding1 = ImplTest(pkcs12);
+            byte[] pfxEncoding2 = ImplTest(pfxEncoding1);
+
+            //
+            // comparison test
+            //
+            if (!Arrays.AreEqual(pfxEncoding1, pfxEncoding2))
+            {
+                Fail("failed comparison test");
+            }
+        }
+
+        private byte[] ImplTest(byte[] pkcs12)
 		{
 			Pfx                 bag = Pfx.GetInstance(pkcs12);
 			ContentInfo         info = bag.AuthSafe;
@@ -178,14 +192,7 @@ namespace Org.BouncyCastle.Asn1.Tests
 
 			bag = new Pfx(info, mData);
 
-			//
-			// comparison test
-			//
-            byte[] pfxEncoding = bag.GetEncoded();
-			if (!Arrays.AreEqual(pfxEncoding, pkcs12))
-			{
-				Fail("Failed comparison test");
-			}
+            return bag.GetEncoded();
 		}
 
 		public override string Name