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();
|