diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2021-11-22 15:22:28 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2021-11-22 15:22:28 +0700 |
commit | 2ee089e648a2aefe6d84244855b5c923b1496881 (patch) | |
tree | 89866bac74edd42edba77b26e0bb0b0dfa6aa337 /crypto/src/asn1 | |
parent | Perf. opts. (diff) | |
download | BouncyCastle.NET-ed25519-2ee089e648a2aefe6d84244855b5c923b1496881.tar.xz |
Add BitString parsers
Diffstat (limited to 'crypto/src/asn1')
-rw-r--r-- | crypto/src/asn1/ASN1StreamParser.cs | 16 | ||||
-rw-r--r-- | crypto/src/asn1/Asn1InputStream.cs | 12 | ||||
-rw-r--r-- | crypto/src/asn1/BerBitStringParser.cs | 56 | ||||
-rw-r--r-- | crypto/src/asn1/ConstructedBitStream.cs | 134 | ||||
-rw-r--r-- | crypto/src/asn1/DLBitString.cs | 55 | ||||
-rw-r--r-- | crypto/src/asn1/DLBitStringParser.cs | 65 | ||||
-rw-r--r-- | crypto/src/asn1/DLSequence.cs | 11 | ||||
-rw-r--r-- | crypto/src/asn1/DerBitString.cs | 2 | ||||
-rw-r--r-- | crypto/src/asn1/util/Asn1Dump.cs | 105 |
9 files changed, 396 insertions, 60 deletions
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(); |