summary refs log tree commit diff
path: root/crypto
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2021-11-22 15:22:28 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2021-11-22 15:22:28 +0700
commit2ee089e648a2aefe6d84244855b5c923b1496881 (patch)
tree89866bac74edd42edba77b26e0bb0b0dfa6aa337 /crypto
parentPerf. opts. (diff)
downloadBouncyCastle.NET-ed25519-2ee089e648a2aefe6d84244855b5c923b1496881.tar.xz
Add BitString parsers
Diffstat (limited to 'crypto')
-rw-r--r--crypto/BouncyCastle.Android.csproj4
-rw-r--r--crypto/BouncyCastle.csproj4
-rw-r--r--crypto/BouncyCastle.iOS.csproj4
-rw-r--r--crypto/crypto.csproj20
-rw-r--r--crypto/src/asn1/ASN1StreamParser.cs16
-rw-r--r--crypto/src/asn1/Asn1InputStream.cs12
-rw-r--r--crypto/src/asn1/BerBitStringParser.cs56
-rw-r--r--crypto/src/asn1/ConstructedBitStream.cs134
-rw-r--r--crypto/src/asn1/DLBitString.cs55
-rw-r--r--crypto/src/asn1/DLBitStringParser.cs65
-rw-r--r--crypto/src/asn1/DLSequence.cs11
-rw-r--r--crypto/src/asn1/DerBitString.cs2
-rw-r--r--crypto/src/asn1/util/Asn1Dump.cs105
-rw-r--r--crypto/test/src/asn1/test/BitStringTest.cs31
14 files changed, 442 insertions, 77 deletions
diff --git a/crypto/BouncyCastle.Android.csproj b/crypto/BouncyCastle.Android.csproj
index 198ec093e..875c99cfe 100644
--- a/crypto/BouncyCastle.Android.csproj
+++ b/crypto/BouncyCastle.Android.csproj
@@ -95,12 +95,14 @@
     <Compile Include="src\asn1\BERTaggedObjectParser.cs" />
     <Compile Include="src\asn1\BerApplicationSpecific.cs" />
     <Compile Include="src\asn1\BerApplicationSpecificParser.cs" />
+    <Compile Include="src\asn1\BerBitStringParser.cs" />
     <Compile Include="src\asn1\BerNull.cs" />
     <Compile Include="src\asn1\BerOctetString.cs" />
     <Compile Include="src\asn1\BerOutputStream.cs" />
     <Compile Include="src\asn1\BerSequence.cs" />
     <Compile Include="src\asn1\BerSet.cs" />
     <Compile Include="src\asn1\BerTaggedObject.cs" />
+    <Compile Include="src\asn1\ConstructedBitStream.cs" />
     <Compile Include="src\asn1\ConstructedDLEncoding.cs" />
     <Compile Include="src\asn1\ConstructedILEncoding.cs" />
     <Compile Include="src\asn1\ConstructedLazyDLEncoding.cs" />
@@ -141,6 +143,8 @@
     <Compile Include="src\asn1\DerVideotexString.cs" />
     <Compile Include="src\asn1\DerVisibleString.cs" />
     <Compile Include="src\asn1\DLApplicationSpecific.cs" />
+    <Compile Include="src\asn1\DLBitString.cs" />
+    <Compile Include="src\asn1\DLBitStringParser.cs" />
     <Compile Include="src\asn1\DLSequence.cs" />
     <Compile Include="src\asn1\DLSet.cs" />
     <Compile Include="src\asn1\DLTaggedObject.cs" />
diff --git a/crypto/BouncyCastle.csproj b/crypto/BouncyCastle.csproj
index 849dd3202..56be079db 100644
--- a/crypto/BouncyCastle.csproj
+++ b/crypto/BouncyCastle.csproj
@@ -89,12 +89,14 @@
     <Compile Include="src\asn1\BERTaggedObjectParser.cs" />
     <Compile Include="src\asn1\BerApplicationSpecific.cs" />
     <Compile Include="src\asn1\BerApplicationSpecificParser.cs" />
+    <Compile Include="src\asn1\BerBitStringParser.cs" />
     <Compile Include="src\asn1\BerNull.cs" />
     <Compile Include="src\asn1\BerOctetString.cs" />
     <Compile Include="src\asn1\BerOutputStream.cs" />
     <Compile Include="src\asn1\BerSequence.cs" />
     <Compile Include="src\asn1\BerSet.cs" />
     <Compile Include="src\asn1\BerTaggedObject.cs" />
+    <Compile Include="src\asn1\ConstructedBitStream.cs" />
     <Compile Include="src\asn1\ConstructedDLEncoding.cs" />
     <Compile Include="src\asn1\ConstructedILEncoding.cs" />
     <Compile Include="src\asn1\ConstructedLazyDLEncoding.cs" />
@@ -135,6 +137,8 @@
     <Compile Include="src\asn1\DerVideotexString.cs" />
     <Compile Include="src\asn1\DerVisibleString.cs" />
     <Compile Include="src\asn1\DLApplicationSpecific.cs" />
+    <Compile Include="src\asn1\DLBitString.cs" />
+    <Compile Include="src\asn1\DLBitStringParser.cs" />
     <Compile Include="src\asn1\DLSequence.cs" />
     <Compile Include="src\asn1\DLSet.cs" />
     <Compile Include="src\asn1\DLTaggedObject.cs" />
diff --git a/crypto/BouncyCastle.iOS.csproj b/crypto/BouncyCastle.iOS.csproj
index 456a15b19..4490f0f07 100644
--- a/crypto/BouncyCastle.iOS.csproj
+++ b/crypto/BouncyCastle.iOS.csproj
@@ -90,12 +90,14 @@
     <Compile Include="src\asn1\BERTaggedObjectParser.cs" />
     <Compile Include="src\asn1\BerApplicationSpecific.cs" />
     <Compile Include="src\asn1\BerApplicationSpecificParser.cs" />
+    <Compile Include="src\asn1\BerBitStringParser.cs" />
     <Compile Include="src\asn1\BerNull.cs" />
     <Compile Include="src\asn1\BerOctetString.cs" />
     <Compile Include="src\asn1\BerOutputStream.cs" />
     <Compile Include="src\asn1\BerSequence.cs" />
     <Compile Include="src\asn1\BerSet.cs" />
     <Compile Include="src\asn1\BerTaggedObject.cs" />
+    <Compile Include="src\asn1\ConstructedBitStream.cs" />
     <Compile Include="src\asn1\ConstructedDLEncoding.cs" />
     <Compile Include="src\asn1\ConstructedILEncoding.cs" />
     <Compile Include="src\asn1\ConstructedLazyDLEncoding.cs" />
@@ -136,6 +138,8 @@
     <Compile Include="src\asn1\DerVideotexString.cs" />
     <Compile Include="src\asn1\DerVisibleString.cs" />
     <Compile Include="src\asn1\DLApplicationSpecific.cs" />
+    <Compile Include="src\asn1\DLBitString.cs" />
+    <Compile Include="src\asn1\DLBitStringParser.cs" />
     <Compile Include="src\asn1\DLSequence.cs" />
     <Compile Include="src\asn1\DLSet.cs" />
     <Compile Include="src\asn1\DLTaggedObject.cs" />
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index a588a961a..d3666bfcf 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -294,6 +294,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\asn1\BerBitStringParser.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\asn1\BERGenerator.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -364,6 +369,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\asn1\ConstructedBitStream.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\asn1\ConstructedDLEncoding.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -564,6 +574,16 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\asn1\DLBitString.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\asn1\DLBitStringParser.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\asn1\DLSequence.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
diff --git a/crypto/src/asn1/ASN1StreamParser.cs b/crypto/src/asn1/ASN1StreamParser.cs
index 6128c9b78..3281310c0 100644
--- a/crypto/src/asn1/ASN1StreamParser.cs
+++ b/crypto/src/asn1/ASN1StreamParser.cs
@@ -130,9 +130,9 @@ namespace Org.BouncyCastle.Asn1
         {
             switch (univTagNo)
             {
-            // TODO[asn1] DLConstructedBitStringParser
-            //case Asn1Tags.BitString:
-            //    return new BerBitStringParser(this);
+            case Asn1Tags.BitString:
+                // TODO[asn1] DLConstructedBitStringParser
+                return new BerBitStringParser(this);
             case Asn1Tags.External:
                 return new DerExternalParser(this);
             case Asn1Tags.OctetString:
@@ -151,9 +151,8 @@ namespace Org.BouncyCastle.Asn1
         {
             switch (univTagNo)
             {
-            // TODO[asn1] BerBitStringParser
-            //case Asn1Tags.BitString:
-            //    return new BerBitStringParser(this);
+            case Asn1Tags.BitString:
+                return new BerBitStringParser(this);
             case Asn1Tags.External:
                 // TODO[asn1] BERExternalParser
                 return new DerExternalParser(this);
@@ -178,9 +177,8 @@ namespace Org.BouncyCastle.Asn1
             // Some primitive encodings can be handled by parsers too...
             switch (univTagNo)
             {
-            // TODO[asn1] DLBitStringParser
-            //case Asn1Tags.BitString:
-            //    return new DLBitStringParser(defIn);
+            case Asn1Tags.BitString:
+                return new DLBitStringParser(defIn);
             case Asn1Tags.External:
                 throw new Asn1Exception("externals must use constructed encoding (see X.690 8.18)");
             case Asn1Tags.OctetString:
diff --git a/crypto/src/asn1/Asn1InputStream.cs b/crypto/src/asn1/Asn1InputStream.cs
index 04de3112e..aa1bb0571 100644
--- a/crypto/src/asn1/Asn1InputStream.cs
+++ b/crypto/src/asn1/Asn1InputStream.cs
@@ -193,9 +193,8 @@ namespace Org.BouncyCastle.Asn1
 
             switch (tagNo)
             {
-            // TODO[asn1] BerBitStringParser
-            //case Asn1Tags.BitString:
-            //    return BerBitStringParser.Parse(sp);
+            case Asn1Tags.BitString:
+                return BerBitStringParser.Parse(sp);
             case Asn1Tags.OctetString:
                 return BerOctetStringParser.Parse(sp);
             case Asn1Tags.Sequence:
@@ -224,8 +223,7 @@ namespace Org.BouncyCastle.Asn1
                 bitStrings[i] = bitString;
             }
 
-            // TODO Probably ought to be DLBitString
-            return new BerBitString(bitStrings);
+            return new DLBitString(BerBitString.FlattenBitStrings(bitStrings), false);
         }
 
         internal virtual Asn1OctetString BuildConstructedOctetString(Asn1EncodableVector contentsElements)
@@ -242,8 +240,8 @@ namespace Org.BouncyCastle.Asn1
                 octetStrings[i] = octetString;
             }
 
-            // TODO Probably ought to be DerOctetString (no DLOctetString available)
-            return new BerOctetString(octetStrings);
+            // Note: No DLOctetString available
+            return new DerOctetString(BerOctetString.FlattenOctetStrings(octetStrings));
         }
 
         internal virtual int Limit
diff --git a/crypto/src/asn1/BerBitStringParser.cs b/crypto/src/asn1/BerBitStringParser.cs
new file mode 100644
index 000000000..656538ba1
--- /dev/null
+++ b/crypto/src/asn1/BerBitStringParser.cs
@@ -0,0 +1,56 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Asn1
+{
+    /// <summary>A parser for indefinite-length BIT STRINGs.</summary>
+    internal class BerBitStringParser
+        : Asn1BitStringParser
+    {
+        private readonly Asn1StreamParser m_parser;
+
+        private ConstructedBitStream m_bitStream;
+
+        internal BerBitStringParser(Asn1StreamParser parser)
+        {
+            m_parser = parser;
+        }
+
+        public Stream GetOctetStream()
+        {
+            return m_bitStream = new ConstructedBitStream(m_parser, true);
+        }
+
+        public Stream GetBitStream()
+        {
+            return m_bitStream = new ConstructedBitStream(m_parser, false);
+        }
+
+        public int PadBits
+        {
+            get { return m_bitStream.PadBits; }
+        }
+
+        public Asn1Object ToAsn1Object()
+        {
+            try
+            {
+                return Parse(m_parser);
+            }
+            catch (IOException e)
+            {
+                throw new Asn1ParsingException("IOException converting stream to byte array: " + e.Message, e);
+            }
+        }
+
+        internal static BerBitString Parse(Asn1StreamParser sp)
+        {
+            ConstructedBitStream bitStream = new ConstructedBitStream(sp, false);
+            byte[] data = Streams.ReadAll(bitStream);
+            int padBits = bitStream.PadBits;
+            return new BerBitString(data, padBits);
+        }
+    }
+}
diff --git a/crypto/src/asn1/ConstructedBitStream.cs b/crypto/src/asn1/ConstructedBitStream.cs
new file mode 100644
index 000000000..7c9e7c9e4
--- /dev/null
+++ b/crypto/src/asn1/ConstructedBitStream.cs
@@ -0,0 +1,134 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Asn1
+{
+    internal class ConstructedBitStream
+        : BaseInputStream
+    {
+        private readonly Asn1StreamParser m_parser;
+        private readonly bool m_octetAligned;
+
+        private bool m_first = true;
+        private int m_padBits = 0;
+
+        private Asn1BitStringParser m_currentParser;
+        private Stream m_currentStream;
+
+        internal ConstructedBitStream(Asn1StreamParser parser, bool octetAligned)
+        {
+            m_parser = parser;
+            m_octetAligned = octetAligned;
+        }
+
+        internal int PadBits
+        {
+            get { return m_padBits; }
+        }
+
+        public override int Read(byte[] buf, int off, int len)
+        {
+            if (len < 1)
+                return 0;
+
+            if (m_currentStream == null)
+            {
+                if (!m_first)
+                    return 0;
+
+                m_currentParser = GetNextParser();
+                if (m_currentParser == null)
+                    return 0;
+
+                m_first = false;
+                m_currentStream = m_currentParser.GetBitStream();
+            }
+
+            int totalRead = 0;
+
+            for (;;)
+            {
+                int numRead = m_currentStream.Read(buf, off + totalRead, len - totalRead);
+
+                if (numRead > 0)
+                {
+                    totalRead += numRead;
+
+                    if (totalRead == len)
+                        return totalRead;
+                }
+                else
+                {
+                    m_padBits = m_currentParser.PadBits;
+                    m_currentParser = GetNextParser();
+                    if (m_currentParser == null)
+                    {
+                        m_currentStream = null;
+                        return totalRead;
+                    }
+
+                    m_currentStream = m_currentParser.GetBitStream();
+                }
+            }
+        }
+
+        public override int ReadByte()
+        {
+            if (m_currentStream == null)
+            {
+                if (!m_first)
+                    return -1;
+
+                m_currentParser = GetNextParser();
+                if (m_currentParser == null)
+                    return -1;
+
+                m_first = false;
+                m_currentStream = m_currentParser.GetBitStream();
+            }
+
+            for (;;)
+            {
+                int b = m_currentStream.ReadByte();
+
+                if (b >= 0)
+                    return b;
+
+                m_padBits = m_currentParser.PadBits;
+                m_currentParser = GetNextParser();
+                if (m_currentParser == null)
+                {
+                    m_currentStream = null;
+                    return -1;
+                }
+
+                m_currentStream = m_currentParser.GetBitStream();
+            }
+        }
+
+        private Asn1BitStringParser GetNextParser()
+        {
+            IAsn1Convertible asn1Obj = m_parser.ReadObject();
+            if (asn1Obj == null)
+            {
+                if (m_octetAligned && m_padBits != 0)
+                    throw new IOException("expected octet-aligned bitstring, but found padBits: " + m_padBits);
+
+                return null;
+            }
+
+            if (asn1Obj is Asn1BitStringParser)
+            {
+                if (m_padBits != 0)
+                    throw new IOException("only the last nested bitstring can have padding");
+
+                return (Asn1BitStringParser)asn1Obj;
+            }
+
+            throw new IOException("unknown object encountered: " + Platform.GetTypeName(asn1Obj));
+        }
+    }
+}
diff --git a/crypto/src/asn1/DLBitString.cs b/crypto/src/asn1/DLBitString.cs
new file mode 100644
index 000000000..5d8f3ac5e
--- /dev/null
+++ b/crypto/src/asn1/DLBitString.cs
@@ -0,0 +1,55 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1
+{
+    /// <summary>A Definite length BIT STRING</summary>
+    public class DLBitString
+        : DerBitString
+    {
+        public DLBitString(byte data, int padBits)
+            : base(data, padBits)
+        {
+        }
+
+        public DLBitString(byte[] data)
+            : this(data, 0)
+        {
+        }
+
+        public DLBitString(byte[] data, int padBits)
+            : base(data, padBits)
+        {
+        }
+
+        public DLBitString(int namedBits)
+            : base(namedBits)
+        {
+        }
+
+        public DLBitString(Asn1Encodable obj)
+            : this(obj.GetDerEncoded(), 0)
+        {
+        }
+
+        internal DLBitString(byte[] contents, bool check)
+            : base(contents, check)
+        {
+        }
+
+        internal override IAsn1Encoding GetEncoding(int encoding)
+        {
+            if (Asn1OutputStream.EncodingDer == encoding)
+                return base.GetEncoding(encoding);
+
+            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.BitString, contents);
+        }
+
+        internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
+        {
+            if (Asn1OutputStream.EncodingDer == encoding)
+                return base.GetEncodingImplicit(encoding, tagClass, tagNo);
+
+            return new PrimitiveEncoding(tagClass, tagNo, contents);
+        }
+    }
+}
diff --git a/crypto/src/asn1/DLBitStringParser.cs b/crypto/src/asn1/DLBitStringParser.cs
new file mode 100644
index 000000000..643361e64
--- /dev/null
+++ b/crypto/src/asn1/DLBitStringParser.cs
@@ -0,0 +1,65 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Asn1
+{
+    /// <summary>Parser for a DL encoded BIT STRING.</summary>
+    internal class DLBitStringParser
+        : Asn1BitStringParser
+    {
+        private readonly DefiniteLengthInputStream m_stream;
+        private int m_padBits = 0;
+
+        internal DLBitStringParser(DefiniteLengthInputStream stream)
+        {
+            m_stream = stream;
+        }
+
+        public Stream GetBitStream()
+        {
+            return GetBitStream(false);
+        }
+
+        public Stream GetOctetStream()
+        {
+            return GetBitStream(true);
+        }
+
+        public int PadBits
+        {
+            get { return m_padBits; }
+        }
+
+        public Asn1Object ToAsn1Object()
+        {
+            try
+            {
+                return DerBitString.CreatePrimitive(m_stream.ToArray());
+            }
+            catch (IOException e)
+            {
+                throw new Asn1ParsingException("IOException converting stream to byte array: " + e.Message, e);
+            }
+        }
+
+        private Stream GetBitStream(bool octetAligned)
+        {
+            int length = m_stream.Remaining;
+            if (length < 1)
+                throw new InvalidOperationException("content octets cannot be empty");
+
+            m_padBits = m_stream.ReadByte();
+            if (m_padBits > 0)
+            {
+                if (length < 2)
+                    throw new InvalidOperationException("zero length data with non-zero pad bits");
+                if (m_padBits > 7)
+                    throw new InvalidOperationException("pad bits cannot be greater than 7 or less than 0");
+                if (octetAligned)
+                    throw new IOException("expected octet-aligned bitstring, but found padBits: " + m_padBits);
+            }
+
+            return m_stream;
+        }
+    }
+}
diff --git a/crypto/src/asn1/DLSequence.cs b/crypto/src/asn1/DLSequence.cs
index 8cf8d5bcb..b3c21ece5 100644
--- a/crypto/src/asn1/DLSequence.cs
+++ b/crypto/src/asn1/DLSequence.cs
@@ -64,6 +64,17 @@ namespace Org.BouncyCastle.Asn1
                 Asn1OutputStream.GetContentsEncodings(encoding, elements));
         }
 
+        internal override DerBitString ToAsn1BitString()
+        {
+            return new DLBitString(BerBitString.FlattenBitStrings(GetConstructedBitStrings()), false);
+        }
+
+        // TODO[asn1] DLExternal
+        //internal override DerExternal ToAsn1External()
+        //{
+        //    return new DLExternal(this);
+        //}
+
         internal override Asn1Set ToAsn1Set()
         {
             return new DLSet(false, elements);
diff --git a/crypto/src/asn1/DerBitString.cs b/crypto/src/asn1/DerBitString.cs
index d3836b740..2b1ff3354 100644
--- a/crypto/src/asn1/DerBitString.cs
+++ b/crypto/src/asn1/DerBitString.cs
@@ -359,7 +359,7 @@ namespace Org.BouncyCastle.Asn1
 
                 byte finalOctet = contents[length - 1];
                 if (finalOctet != (byte)(finalOctet & (0xFF << padBits)))
-                    return new BerBitString(contents, false);
+                    return new DLBitString(contents, false);
             }
 
             return new DerBitString(contents, false);
diff --git a/crypto/src/asn1/util/Asn1Dump.cs b/crypto/src/asn1/util/Asn1Dump.cs
index f573d3663..0ba5c394f 100644
--- a/crypto/src/asn1/util/Asn1Dump.cs
+++ b/crypto/src/asn1/util/Asn1Dump.cs
@@ -132,64 +132,88 @@ namespace Org.BouncyCastle.Asn1.Utilities
             {
                 buf.Append(indent + "Integer(" + ((DerInteger)obj).Value + ")" + NewLine);
             }
-            else if (obj is BerOctetString)
+            else if (obj is Asn1OctetString)
             {
-                byte[] octets = ((Asn1OctetString)obj).GetOctets();
-                string extra = verbose ? DumpBinaryDataAsString(indent, octets) : "";
-                buf.Append(indent + "BER Octet String" + "[" + octets.Length + "] " + extra + NewLine);
-            }
-            else if (obj is DerOctetString)
-            {
-                byte[] octets = ((Asn1OctetString)obj).GetOctets();
-                string extra = verbose ? DumpBinaryDataAsString(indent, octets) : "";
-                buf.Append(indent + "DER Octet String" + "[" + octets.Length + "] " + extra + NewLine);
+                Asn1OctetString oct = (Asn1OctetString)obj;
+                byte[] octets = oct.GetOctets();
+
+                if (obj is BerOctetString)
+                {
+                    buf.Append(indent + "BER Octet String[" + octets.Length + "]" + NewLine);
+                }
+                else
+                {
+                    buf.Append(indent + "DER Octet String[" + octets.Length + "]" + NewLine);
+                }
+
+                if (verbose)
+                {
+                    buf.Append(DumpBinaryDataAsString(indent, octets));
+                }
             }
             else if (obj is DerBitString)
             {
-                DerBitString bt = (DerBitString)obj; 
-                byte[] bytes = bt.GetBytes();
-                string extra = verbose ? DumpBinaryDataAsString(indent, bytes) : "";
-                buf.Append(indent + "DER Bit String" + "[" + bytes.Length + ", " + bt.PadBits + "] " + extra + NewLine);
+                DerBitString bitString = (DerBitString)obj;
+                byte[] bytes = bitString.GetBytes();
+                int padBits = bitString.PadBits;
+
+                if (bitString is BerBitString)
+                {
+                    buf.Append(indent + "BER Bit String[" + bytes.Length + ", " + padBits + "]" + NewLine);
+                }
+                else if (bitString is DLBitString)
+                {
+                    buf.Append(indent + "DL Bit String[" + bytes.Length + ", " + padBits + "]" + NewLine);
+                }
+                else
+                {
+                    buf.Append(indent + "DER Bit String[" + bytes.Length + ", " + padBits + "]" + NewLine);
+                }
+
+                if (verbose)
+                {
+                    buf.Append(DumpBinaryDataAsString(indent, bytes));
+                }
             }
             else if (obj is DerIA5String)
             {
-                buf.Append(indent + "IA5String(" + ((DerIA5String)obj).GetString() + ") " + NewLine);
+                buf.Append(indent + "IA5String(" + ((DerIA5String)obj).GetString() + ")" + NewLine);
             }
             else if (obj is DerUtf8String)
             {
-                buf.Append(indent + "UTF8String(" + ((DerUtf8String)obj).GetString() + ") " + NewLine);
+                buf.Append(indent + "UTF8String(" + ((DerUtf8String)obj).GetString() + ")" + NewLine);
             }
             else if (obj is DerPrintableString)
             {
-                buf.Append(indent + "PrintableString(" + ((DerPrintableString)obj).GetString() + ") " + NewLine);
+                buf.Append(indent + "PrintableString(" + ((DerPrintableString)obj).GetString() + ")" + NewLine);
             }
             else if (obj is DerVisibleString)
             {
-                buf.Append(indent + "VisibleString(" + ((DerVisibleString)obj).GetString() + ") " + NewLine);
+                buf.Append(indent + "VisibleString(" + ((DerVisibleString)obj).GetString() + ")" + NewLine);
             }
             else if (obj is DerBmpString)
             {
-                buf.Append(indent + "BMPString(" + ((DerBmpString)obj).GetString() + ") " + NewLine);
+                buf.Append(indent + "BMPString(" + ((DerBmpString)obj).GetString() + ")" + NewLine);
             }
             else if (obj is DerT61String)
             {
-                buf.Append(indent + "T61String(" + ((DerT61String)obj).GetString() + ") " + NewLine);
+                buf.Append(indent + "T61String(" + ((DerT61String)obj).GetString() + ")" + NewLine);
             }
             else if (obj is DerGraphicString)
             {
-                buf.Append(indent + "GraphicString(" + ((DerGraphicString)obj).GetString() + ") " + NewLine);
+                buf.Append(indent + "GraphicString(" + ((DerGraphicString)obj).GetString() + ")" + NewLine);
             }
             else if (obj is DerVideotexString)
             {
-                buf.Append(indent + "VideotexString(" + ((DerVideotexString)obj).GetString() + ") " + NewLine);
+                buf.Append(indent + "VideotexString(" + ((DerVideotexString)obj).GetString() + ")" + NewLine);
             }
             else if (obj is DerUtcTime)
             {
-                buf.Append(indent + "UTCTime(" + ((DerUtcTime)obj).TimeString + ") " + NewLine);
+                buf.Append(indent + "UTCTime(" + ((DerUtcTime)obj).TimeString + ")" + NewLine);
             }
             else if (obj is DerGeneralizedTime)
             {
-                buf.Append(indent + "GeneralizedTime(" + ((DerGeneralizedTime)obj).GetTime() + ") " + NewLine);
+                buf.Append(indent + "GeneralizedTime(" + ((DerGeneralizedTime)obj).GetTime() + ")" + NewLine);
             }
             else if (obj is DerEnumerated)
             {
@@ -250,32 +274,27 @@ namespace Org.BouncyCastle.Asn1.Utilities
 
         private static string DumpBinaryDataAsString(string indent, byte[] bytes)
         {
+            if (bytes.Length < 1)
+                return "";
+
             indent += Tab;
 
-            StringBuilder buf = new StringBuilder(NewLine);
+            StringBuilder buf = new StringBuilder();
 
             for (int i = 0; i < bytes.Length; i += SampleSize)
             {
-                if (bytes.Length - i > SampleSize)
-                {
-                    buf.Append(indent);
-                    buf.Append(Hex.ToHexString(bytes, i, SampleSize));
-                    buf.Append(Tab);
-                    buf.Append(CalculateAscString(bytes, i, SampleSize));
-                    buf.Append(NewLine);
-                }
-                else
+                int remaining = bytes.Length - i;
+                int chunk = System.Math.Min(remaining, SampleSize);
+
+                buf.Append(indent);
+                buf.Append(Hex.ToHexString(bytes, i, chunk));
+                for (int j = chunk; j < SampleSize; ++j)
                 {
-                    buf.Append(indent);
-                    buf.Append(Hex.ToHexString(bytes, i, bytes.Length - i));
-                    for (int j = bytes.Length - i; j != SampleSize; j++)
-                    {
-                        buf.Append("  ");
-                    }
-                    buf.Append(Tab);
-                    buf.Append(CalculateAscString(bytes, i, bytes.Length - i));
-                    buf.Append(NewLine);
+                    buf.Append("  ");
                 }
+                buf.Append(Tab);
+                buf.Append(CalculateAscString(bytes, i, chunk));
+                buf.Append(NewLine);
             }
 
             return buf.ToString();
diff --git a/crypto/test/src/asn1/test/BitStringTest.cs b/crypto/test/src/asn1/test/BitStringTest.cs
index 35b7811bc..af893fe9f 100644
--- a/crypto/test/src/asn1/test/BitStringTest.cs
+++ b/crypto/test/src/asn1/test/BitStringTest.cs
@@ -83,29 +83,26 @@ namespace Org.BouncyCastle.Asn1.Tests
         {
             if (Arrays.AreEqual(derData, Asn1Object.FromByteArray(dlData).GetEncoded()))
             {
-                //Fail("failed DL check");
-                Fail("failed BER check");
+                Fail("failed DL check");
             }
-            IAsn1String dl = BerBitString.GetInstance(dlData);
+            DerBitString dl = DerBitString.GetInstance(dlData);
 
-            //IsTrue("DL test failed", dl is DLBitString);
-            IsTrue("BER test failed", dl is BerBitString);
+            IsTrue("DL test failed", dl is DLBitString);
             if (!Arrays.AreEqual(derData, Asn1Object.FromByteArray(dlData).GetDerEncoded()))
             {
                 Fail("failed DER check");
             }
-            // TODO This test isn't applicable until we get the DL variants
-            //try
-            //{
-            //    DerBitString.GetInstance(dlData);
-            //    Fail("no exception");
-            //}
-            //catch (ArgumentException e)
-            //{
-            //    // ignore
-            //}
-            IAsn1String der = DerBitString.GetInstance(derData);
-            IsTrue("DER test failed", der is DerBitString);
+            try
+            {
+                // GetInstance should work for "an object that can be converted into [a DerBitString]".
+                DerBitString.GetInstance(dlData);
+            }
+            catch (ArgumentException)
+            {
+                Fail("failed DL encoding conversion");
+            }
+            DerBitString der = DerBitString.GetInstance(derData);
+            IsTrue("DER test failed", typeof(DerBitString) == der.GetType());
         }
 
         public override void PerformTest()