summary refs log tree commit diff
path: root/crypto/src/asn1/BERBitString.cs
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/asn1/BERBitString.cs')
-rw-r--r--crypto/src/asn1/BERBitString.cs179
1 files changed, 168 insertions, 11 deletions
diff --git a/crypto/src/asn1/BERBitString.cs b/crypto/src/asn1/BERBitString.cs
index a738a75e6..1756ee9c0 100644
--- a/crypto/src/asn1/BERBitString.cs
+++ b/crypto/src/asn1/BERBitString.cs
@@ -1,4 +1,5 @@
 using System;
+using System.Diagnostics;
 
 using Org.BouncyCastle.Utilities;
 
@@ -7,36 +8,192 @@ namespace Org.BouncyCastle.Asn1
     public class BerBitString
         : DerBitString
     {
-        public BerBitString(byte[] data, int padBits)
+        private const int DefaultSegmentLimit = 1000;
+
+        internal static byte[] FlattenBitStrings(DerBitString[] bitStrings)
+        {
+            int count = bitStrings.Length;
+            switch (count)
+            {
+            case 0:
+                // No bits
+                return new byte[]{ 0 };
+            case 1:
+                return bitStrings[0].contents;
+            default:
+            {
+                int last = count - 1, totalLength = 0;
+                for (int i = 0; i < last; ++i)
+                {
+                    byte[] elementContents = bitStrings[i].contents;
+                    if (elementContents[0] != 0)
+                        throw new ArgumentException("only the last nested bitstring can have padding", "bitStrings");
+
+                    totalLength += elementContents.Length - 1;
+                }
+
+                // Last one can have padding
+                byte[] lastElementContents = bitStrings[last].contents;
+                byte padBits = lastElementContents[0];
+                totalLength += lastElementContents.Length;
+
+                byte[] contents = new byte[totalLength];
+                contents[0] = padBits;
+
+                int pos = 1;
+                for (int i = 0; i < count; ++i)
+                {
+                    byte[] elementContents = bitStrings[i].contents;
+                    int length = elementContents.Length - 1;
+                    Array.Copy(elementContents, 1, contents, pos, length);
+                    pos += length;
+                }
+
+                Debug.Assert(pos == totalLength);
+                return contents;
+            }
+            }
+        }
+
+        private readonly int segmentLimit;
+        private readonly DerBitString[] elements;
+
+        public BerBitString(byte data, int padBits)
             : base(data, padBits)
-		{
-		}
+        {
+            this.elements = null;
+            this.segmentLimit = DefaultSegmentLimit;
+        }
+
+        public BerBitString(byte[] data)
+            : this(data, 0)
+        {
+        }
 
-		public BerBitString(byte[] data)
-            : base(data)
+        public BerBitString(byte[] data, int padBits)
+            : this(data, padBits, DefaultSegmentLimit)
 		{
-		}
+        }
+
+        public BerBitString(byte[] data, int padBits, int segmentLimit)
+            : base(data, padBits)
+        {
+            this.elements = null;
+            this.segmentLimit = segmentLimit;
+        }
 
         public BerBitString(int namedBits)
             : base(namedBits)
         {
+            this.elements = null;
+            this.segmentLimit = DefaultSegmentLimit;
         }
 
         public BerBitString(Asn1Encodable obj)
-            : base(obj)
+            : this(obj.GetDerEncoded(), 0)
 		{
-		}
+        }
+
+        public BerBitString(DerBitString[] elements)
+            : this(elements, DefaultSegmentLimit)
+        {
+        }
+
+        public BerBitString(DerBitString[] elements, int segmentLimit)
+            : base(FlattenBitStrings(elements), false)
+        {
+            this.elements = elements;
+            this.segmentLimit = segmentLimit;
+        }
+
+        internal BerBitString(byte[] contents, bool check)
+            : base(contents, check)
+        {
+            this.elements = null;
+            this.segmentLimit = DefaultSegmentLimit;
+        }
+
+        private bool IsConstructed
+        {
+            get { return null != elements || contents.Length > segmentLimit; }
+        }
+
+        internal override int EncodedLength(bool withID)
+        {
+            throw Platform.CreateNotImplementedException("BerBitString.EncodedLength");
+
+            // TODO This depends on knowing it's not DER
+            //if (!IsConstructed)
+            //    return EncodedLength(withID, contents.Length);
+
+            //int totalLength = withID ? 4 : 3;
+
+            //if (null != elements)
+            //{
+            //    for (int i = 0; i < elements.Length; ++i)
+            //    {
+            //        totalLength += elements[i].EncodedLength(true);
+            //    }
+            //}
+            //else if (contents.Length < 2)
+            //{
+            //    // No bits
+            //}
+            //else
+            //{
+            //    int extraSegments = (contents.Length - 2) / (segmentLimit - 1);
+            //    totalLength += extraSegments * EncodedLength(true, segmentLimit);
+
+            //    int lastSegmentLength = contents.Length - (extraSegments * (segmentLimit - 1));
+            //    totalLength += EncodedLength(true, lastSegmentLength);
+            //}
+
+            //return totalLength;
+        }
 
         internal override void Encode(Asn1OutputStream asn1Out, bool withID)
         {
-            if (asn1Out.IsBer)
+            if (!asn1Out.IsBer)
+            {
+                base.Encode(asn1Out, withID);
+                return;
+            }
+
+            if (!IsConstructed)
             {
-                asn1Out.WriteEncodingDL(withID, Asn1Tags.BitString, (byte)mPadBits, mData, 0, mData.Length);
+                Encode(asn1Out, withID, contents, 0, contents.Length);
+                return;
+            }
+
+            asn1Out.WriteIdentifier(withID, Asn1Tags.Constructed | Asn1Tags.BitString);
+            asn1Out.WriteByte(0x80);
+
+            if (null != elements)
+            {
+                asn1Out.WritePrimitives(elements);
+            }
+            else if (contents.Length < 2)
+            {
+                // No bits
             }
             else
             {
-                base.Encode(asn1Out, withID);
+                byte pad = contents[0];
+                int length = contents.Length;
+                int remaining = length - 1;
+                int segmentLength = segmentLimit - 1;
+
+                while (remaining > segmentLength)
+                {
+                    Encode(asn1Out, true, (byte)0, contents, length - remaining, segmentLength);
+                    remaining -= segmentLength;
+                }
+
+                Encode(asn1Out, true, pad, contents, length - remaining, remaining);
             }
+
+            asn1Out.WriteByte(0x00);
+            asn1Out.WriteByte(0x00);
         }
     }
 }