265 lines
6.6 KiB
C#
265 lines
6.6 KiB
C#
using System;
|
|
using System.IO;
|
|
using Org.BouncyCastle.Utilities.IO;
|
|
|
|
namespace Org.BouncyCastle.Asn1;
|
|
|
|
public class Asn1InputStream : FilterStream
|
|
{
|
|
private readonly int limit;
|
|
|
|
private readonly byte[][] tmpBuffers;
|
|
|
|
internal static int FindLimit(Stream input)
|
|
{
|
|
if (input is LimitedInputStream)
|
|
{
|
|
return ((LimitedInputStream)input).GetRemaining();
|
|
}
|
|
if (input is MemoryStream)
|
|
{
|
|
MemoryStream memoryStream = (MemoryStream)input;
|
|
return (int)(memoryStream.Length - memoryStream.Position);
|
|
}
|
|
return int.MaxValue;
|
|
}
|
|
|
|
public Asn1InputStream(Stream inputStream)
|
|
: this(inputStream, FindLimit(inputStream))
|
|
{
|
|
}
|
|
|
|
public Asn1InputStream(Stream inputStream, int limit)
|
|
: base(inputStream)
|
|
{
|
|
this.limit = limit;
|
|
tmpBuffers = new byte[16][];
|
|
}
|
|
|
|
public Asn1InputStream(byte[] input)
|
|
: this(new MemoryStream(input, writable: false), input.Length)
|
|
{
|
|
}
|
|
|
|
private Asn1Object BuildObject(int tag, int tagNo, int length)
|
|
{
|
|
bool flag = (tag & 0x20) != 0;
|
|
DefiniteLengthInputStream definiteLengthInputStream = new DefiniteLengthInputStream(s, length);
|
|
if ((tag & 0x40) != 0)
|
|
{
|
|
return new DerApplicationSpecific(flag, tagNo, definiteLengthInputStream.ToArray());
|
|
}
|
|
if ((tag & 0x80) != 0)
|
|
{
|
|
return new Asn1StreamParser(definiteLengthInputStream).ReadTaggedObject(flag, tagNo);
|
|
}
|
|
if (flag)
|
|
{
|
|
return tagNo switch
|
|
{
|
|
4 => new BerOctetString(BuildDerEncodableVector(definiteLengthInputStream)),
|
|
16 => CreateDerSequence(definiteLengthInputStream),
|
|
17 => CreateDerSet(definiteLengthInputStream),
|
|
8 => new DerExternal(BuildDerEncodableVector(definiteLengthInputStream)),
|
|
_ => throw new IOException("unknown tag " + tagNo + " encountered"),
|
|
};
|
|
}
|
|
return CreatePrimitiveDerObject(tagNo, definiteLengthInputStream, tmpBuffers);
|
|
}
|
|
|
|
internal Asn1EncodableVector BuildEncodableVector()
|
|
{
|
|
Asn1EncodableVector asn1EncodableVector = new Asn1EncodableVector();
|
|
Asn1Object asn1Object;
|
|
while ((asn1Object = ReadObject()) != null)
|
|
{
|
|
asn1EncodableVector.Add(asn1Object);
|
|
}
|
|
return asn1EncodableVector;
|
|
}
|
|
|
|
internal virtual Asn1EncodableVector BuildDerEncodableVector(DefiniteLengthInputStream dIn)
|
|
{
|
|
return new Asn1InputStream(dIn).BuildEncodableVector();
|
|
}
|
|
|
|
internal virtual DerSequence CreateDerSequence(DefiniteLengthInputStream dIn)
|
|
{
|
|
return DerSequence.FromVector(BuildDerEncodableVector(dIn));
|
|
}
|
|
|
|
internal virtual DerSet CreateDerSet(DefiniteLengthInputStream dIn)
|
|
{
|
|
return DerSet.FromVector(BuildDerEncodableVector(dIn), needsSorting: false);
|
|
}
|
|
|
|
public Asn1Object ReadObject()
|
|
{
|
|
int num = ReadByte();
|
|
if (num <= 0)
|
|
{
|
|
if (num == 0)
|
|
{
|
|
throw new IOException("unexpected end-of-contents marker");
|
|
}
|
|
return null;
|
|
}
|
|
int num2 = ReadTagNumber(s, num);
|
|
bool flag = (num & 0x20) != 0;
|
|
int num3 = ReadLength(s, limit);
|
|
if (num3 < 0)
|
|
{
|
|
if (!flag)
|
|
{
|
|
throw new IOException("indefinite length primitive encoding encountered");
|
|
}
|
|
IndefiniteLengthInputStream inStream = new IndefiniteLengthInputStream(s, limit);
|
|
Asn1StreamParser parser = new Asn1StreamParser(inStream, limit);
|
|
if ((num & 0x40) != 0)
|
|
{
|
|
return new BerApplicationSpecificParser(num2, parser).ToAsn1Object();
|
|
}
|
|
if ((num & 0x80) != 0)
|
|
{
|
|
return new BerTaggedObjectParser(constructed: true, num2, parser).ToAsn1Object();
|
|
}
|
|
return num2 switch
|
|
{
|
|
4 => new BerOctetStringParser(parser).ToAsn1Object(),
|
|
16 => new BerSequenceParser(parser).ToAsn1Object(),
|
|
17 => new BerSetParser(parser).ToAsn1Object(),
|
|
8 => new DerExternalParser(parser).ToAsn1Object(),
|
|
_ => throw new IOException("unknown BER object encountered"),
|
|
};
|
|
}
|
|
try
|
|
{
|
|
return BuildObject(num, num2, num3);
|
|
}
|
|
catch (ArgumentException exception)
|
|
{
|
|
throw new Asn1Exception("corrupted stream detected", exception);
|
|
}
|
|
}
|
|
|
|
internal static int ReadTagNumber(Stream s, int tag)
|
|
{
|
|
int num = tag & 0x1F;
|
|
if (num == 31)
|
|
{
|
|
num = 0;
|
|
int num2 = s.ReadByte();
|
|
if ((num2 & 0x7F) == 0)
|
|
{
|
|
throw new IOException("Corrupted stream - invalid high tag number found");
|
|
}
|
|
while (num2 >= 0 && (num2 & 0x80) != 0)
|
|
{
|
|
num |= num2 & 0x7F;
|
|
num <<= 7;
|
|
num2 = s.ReadByte();
|
|
}
|
|
if (num2 < 0)
|
|
{
|
|
throw new EndOfStreamException("EOF found inside tag value.");
|
|
}
|
|
num |= num2 & 0x7F;
|
|
}
|
|
return num;
|
|
}
|
|
|
|
internal static int ReadLength(Stream s, int limit)
|
|
{
|
|
int num = s.ReadByte();
|
|
if (num < 0)
|
|
{
|
|
throw new EndOfStreamException("EOF found when length expected");
|
|
}
|
|
if (num == 128)
|
|
{
|
|
return -1;
|
|
}
|
|
if (num > 127)
|
|
{
|
|
int num2 = num & 0x7F;
|
|
if (num2 > 4)
|
|
{
|
|
throw new IOException("DER length more than 4 bytes: " + num2);
|
|
}
|
|
num = 0;
|
|
for (int i = 0; i < num2; i++)
|
|
{
|
|
int num3 = s.ReadByte();
|
|
if (num3 < 0)
|
|
{
|
|
throw new EndOfStreamException("EOF found reading length");
|
|
}
|
|
num = (num << 8) + num3;
|
|
}
|
|
if (num < 0)
|
|
{
|
|
throw new IOException("Corrupted stream - negative length found");
|
|
}
|
|
if (num >= limit)
|
|
{
|
|
throw new IOException("Corrupted stream - out of bounds length found");
|
|
}
|
|
}
|
|
return num;
|
|
}
|
|
|
|
internal static byte[] GetBuffer(DefiniteLengthInputStream defIn, byte[][] tmpBuffers)
|
|
{
|
|
int remaining = defIn.GetRemaining();
|
|
if (remaining >= tmpBuffers.Length)
|
|
{
|
|
return defIn.ToArray();
|
|
}
|
|
byte[] array = tmpBuffers[remaining];
|
|
if (array == null)
|
|
{
|
|
array = (tmpBuffers[remaining] = new byte[remaining]);
|
|
}
|
|
defIn.ReadAllIntoByteArray(array);
|
|
return array;
|
|
}
|
|
|
|
internal static Asn1Object CreatePrimitiveDerObject(int tagNo, DefiniteLengthInputStream defIn, byte[][] tmpBuffers)
|
|
{
|
|
switch (tagNo)
|
|
{
|
|
case 1:
|
|
return DerBoolean.FromOctetString(GetBuffer(defIn, tmpBuffers));
|
|
case 10:
|
|
return DerEnumerated.FromOctetString(GetBuffer(defIn, tmpBuffers));
|
|
case 6:
|
|
return DerObjectIdentifier.FromOctetString(GetBuffer(defIn, tmpBuffers));
|
|
default:
|
|
{
|
|
byte[] array = defIn.ToArray();
|
|
return tagNo switch
|
|
{
|
|
3 => DerBitString.FromAsn1Octets(array),
|
|
30 => new DerBmpString(array),
|
|
24 => new DerGeneralizedTime(array),
|
|
27 => new DerGeneralString(array),
|
|
25 => new DerGraphicString(array),
|
|
22 => new DerIA5String(array),
|
|
2 => new DerInteger(array),
|
|
5 => DerNull.Instance,
|
|
18 => new DerNumericString(array),
|
|
4 => new DerOctetString(array),
|
|
19 => new DerPrintableString(array),
|
|
20 => new DerT61String(array),
|
|
28 => new DerUniversalString(array),
|
|
23 => new DerUtcTime(array),
|
|
12 => new DerUtf8String(array),
|
|
21 => new DerVideotexString(array),
|
|
26 => new DerVisibleString(array),
|
|
_ => throw new IOException("unknown tag " + tagNo + " encountered"),
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|