AsnDecoder
Provides stateless methods for decoding BER-encoded, CER-encoded, and DER-encoded ASN.1 data.
using System.Buffers.Binary;
using System.Buffers.Text;
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
namespace System.Formats.Asn1
{
public static class AsnDecoder
{
private enum LengthDecodeStatus
{
NeedMoreData,
DerIndefinite,
ReservedValue,
LengthTooBig,
LaxEncodingProhibited,
Success
}
private enum LengthValidity
{
CerRequiresIndefinite,
PrimitiveEncodingRequiresDefinite,
LengthExceedsInput,
Valid
}
private delegate void BitStringCopyAction (ReadOnlySpan<byte> value, byte normalizedLastByte, Span<byte> destination);
public static bool TryReadEncodedValue(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out Asn1Tag tag, out int contentOffset, out int contentLength, out int bytesConsumed)
{
CheckEncodingRules(ruleSet);
if (Asn1Tag.TryDecode(source, out Asn1Tag tag2, out int bytesConsumed2) && TryReadLength(source.Slice(bytesConsumed2), ruleSet, out int? length, out int bytesRead)) {
int num = bytesConsumed2 + bytesRead;
if (ValidateLength(source.Slice(num), ruleSet, tag2, length, out int actualLength, out int bytesConsumed3) == LengthValidity.Valid) {
tag = tag2;
contentOffset = num;
contentLength = actualLength;
bytesConsumed = num + bytesConsumed3;
return true;
}
}
tag = default(Asn1Tag);
contentOffset = (contentLength = (bytesConsumed = 0));
return false;
}
public static Asn1Tag ReadEncodedValue(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int contentOffset, out int contentLength, out int bytesConsumed)
{
CheckEncodingRules(ruleSet);
int bytesConsumed2;
Asn1Tag asn1Tag = Asn1Tag.Decode(source, out bytesConsumed2);
int bytesConsumed3;
int? encodedLength = ReadLength(source.Slice(bytesConsumed2), ruleSet, out bytesConsumed3);
int num = bytesConsumed2 + bytesConsumed3;
int actualLength;
int bytesConsumed4;
LengthValidity lengthValidity = ValidateLength(source.Slice(num), ruleSet, asn1Tag, encodedLength, out actualLength, out bytesConsumed4);
if (lengthValidity == LengthValidity.Valid) {
contentOffset = num;
contentLength = actualLength;
bytesConsumed = num + bytesConsumed4;
return asn1Tag;
}
throw GetValidityException(lengthValidity);
}
private static ReadOnlySpan<byte> GetPrimitiveContentSpan(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, Asn1Tag expectedTag, UniversalTagNumber tagNumber, out int bytesConsumed)
{
CheckEncodingRules(ruleSet);
int bytesConsumed2;
Asn1Tag tag = Asn1Tag.Decode(source, out bytesConsumed2);
int bytesConsumed3;
int? nullable = ReadLength(source.Slice(bytesConsumed2), ruleSet, out bytesConsumed3);
int num = bytesConsumed2 + bytesConsumed3;
CheckExpectedTag(tag, expectedTag, tagNumber);
if (tag.IsConstructed)
throw new AsnContentException(System.SR.Format(System.SR.ContentException_PrimitiveEncodingRequired, tagNumber));
if (!nullable.HasValue)
throw new AsnContentException();
ReadOnlySpan<byte> result = Slice(source, num, nullable.Value);
bytesConsumed = num + result.Length;
return result;
}
public static int? DecodeLength(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int bytesConsumed)
{
CheckEncodingRules(ruleSet);
int bytesConsumed2;
int? result = ReadLength(source, ruleSet, out bytesConsumed2);
bytesConsumed = bytesConsumed2;
return result;
}
public static bool TryDecodeLength(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int? decodedLength, out int bytesConsumed)
{
CheckEncodingRules(ruleSet);
int? length;
int bytesRead;
bool result = TryReadLength(source, ruleSet, out length, out bytesRead);
bytesConsumed = bytesRead;
decodedLength = length;
return result;
}
private static bool TryReadLength(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int? length, out int bytesRead)
{
return DecodeLength(source, ruleSet, out length, out bytesRead) == LengthDecodeStatus.Success;
}
private static int? ReadLength(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int bytesConsumed)
{
int? length;
switch (DecodeLength(source, ruleSet, out length, out bytesConsumed)) {
case LengthDecodeStatus.Success:
return length;
case LengthDecodeStatus.LengthTooBig:
throw new AsnContentException(System.SR.ContentException_LengthTooBig);
case LengthDecodeStatus.DerIndefinite:
case LengthDecodeStatus.LaxEncodingProhibited:
throw new AsnContentException(System.SR.ContentException_LengthRuleSetConstraint);
default:
throw new AsnContentException();
}
}
private static LengthDecodeStatus DecodeLength(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int? length, out int bytesRead)
{
length = null;
bytesRead = 0;
if (source.IsEmpty)
return LengthDecodeStatus.NeedMoreData;
byte b = source[bytesRead];
bytesRead++;
if (b == 128) {
if (ruleSet == AsnEncodingRules.DER) {
bytesRead = 0;
return LengthDecodeStatus.DerIndefinite;
}
return LengthDecodeStatus.Success;
}
if (b < 128) {
length = b;
return LengthDecodeStatus.Success;
}
if (b == 255) {
bytesRead = 0;
return LengthDecodeStatus.ReservedValue;
}
byte b2 = (byte)(b & -129);
if (b2 + 1 > source.Length) {
bytesRead = 0;
return LengthDecodeStatus.NeedMoreData;
}
bool flag = ruleSet == AsnEncodingRules.DER || ruleSet == AsnEncodingRules.CER;
if (flag && b2 > 4) {
bytesRead = 0;
return LengthDecodeStatus.LengthTooBig;
}
uint num = 0;
for (int i = 0; i < b2; i++) {
byte b3 = source[bytesRead];
bytesRead++;
if (num == 0) {
if (flag && b3 == 0) {
bytesRead = 0;
return LengthDecodeStatus.LaxEncodingProhibited;
}
if (!flag && b3 != 0 && b2 - i > 4) {
bytesRead = 0;
return LengthDecodeStatus.LengthTooBig;
}
}
num <<= 8;
num |= b3;
}
if (num > 2147483647) {
bytesRead = 0;
return LengthDecodeStatus.LengthTooBig;
}
if (flag && num < 128) {
bytesRead = 0;
return LengthDecodeStatus.LaxEncodingProhibited;
}
length = (int)num;
return LengthDecodeStatus.Success;
}
private static Asn1Tag ReadTagAndLength(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int? contentsLength, out int bytesRead)
{
int bytesConsumed;
Asn1Tag result = Asn1Tag.Decode(source, out bytesConsumed);
int bytesConsumed2;
int? nullable = ReadLength(source.Slice(bytesConsumed), ruleSet, out bytesConsumed2);
int num = bytesConsumed + bytesConsumed2;
if (result.IsConstructed) {
if (ruleSet == AsnEncodingRules.CER && nullable.HasValue)
throw GetValidityException(LengthValidity.CerRequiresIndefinite);
} else if (!nullable.HasValue) {
throw GetValidityException(LengthValidity.PrimitiveEncodingRequiresDefinite);
}
bytesRead = num;
contentsLength = nullable;
return result;
}
private static void ValidateEndOfContents(Asn1Tag tag, int? length, int headerLength)
{
if (!tag.IsConstructed) {
int? nullable = length;
int num = 0;
if (((nullable.GetValueOrDefault() == num) & nullable.HasValue) && headerLength == 2)
return;
}
throw new AsnContentException();
}
private static LengthValidity ValidateLength(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, Asn1Tag localTag, int? encodedLength, out int actualLength, out int bytesConsumed)
{
if (localTag.IsConstructed) {
if (ruleSet == AsnEncodingRules.CER && encodedLength.HasValue) {
actualLength = (bytesConsumed = 0);
return LengthValidity.CerRequiresIndefinite;
}
} else if (!encodedLength.HasValue) {
actualLength = (bytesConsumed = 0);
return LengthValidity.PrimitiveEncodingRequiresDefinite;
}
if (encodedLength.HasValue) {
int value = encodedLength.Value;
if (value > source.Length) {
actualLength = (bytesConsumed = 0);
return LengthValidity.LengthExceedsInput;
}
actualLength = value;
bytesConsumed = value;
return LengthValidity.Valid;
}
actualLength = SeekEndOfContents(source, ruleSet);
bytesConsumed = actualLength + 2;
return LengthValidity.Valid;
}
private static AsnContentException GetValidityException(LengthValidity validity)
{
switch (validity) {
case LengthValidity.CerRequiresIndefinite:
return new AsnContentException(System.SR.ContentException_CerRequiresIndefiniteLength);
case LengthValidity.LengthExceedsInput:
return new AsnContentException(System.SR.ContentException_LengthExceedsPayload);
default:
return new AsnContentException();
}
}
private static int GetPrimitiveIntegerSize(Type primitiveType)
{
if (primitiveType == typeof(byte) || primitiveType == typeof(sbyte))
return 1;
if (primitiveType == typeof(short) || primitiveType == typeof(ushort))
return 2;
if (primitiveType == typeof(int) || primitiveType == typeof(uint))
return 4;
if (primitiveType == typeof(long) || primitiveType == typeof(ulong))
return 8;
return 0;
}
private static int SeekEndOfContents(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet)
{
ReadOnlySpan<byte> source2 = source;
int num = 0;
int num2 = 1;
while (!source2.IsEmpty) {
int? contentsLength;
int bytesRead;
Asn1Tag asn1Tag = ReadTagAndLength(source2, ruleSet, out contentsLength, out bytesRead);
if (asn1Tag == Asn1Tag.EndOfContents) {
ValidateEndOfContents(asn1Tag, contentsLength, bytesRead);
num2--;
if (num2 == 0)
return num;
}
if (!contentsLength.HasValue) {
num2++;
source2 = source2.Slice(bytesRead);
num += bytesRead;
} else {
ReadOnlySpan<byte> readOnlySpan = Slice(source2, 0, bytesRead + contentsLength.Value);
source2 = source2.Slice(readOnlySpan.Length);
num += readOnlySpan.Length;
}
}
throw new AsnContentException();
}
private static int ParseNonNegativeIntAndSlice(ref ReadOnlySpan<byte> data, int bytesToRead)
{
int result = ParseNonNegativeInt(Slice(data, 0, bytesToRead));
data = data.Slice(bytesToRead);
return result;
}
private static int ParseNonNegativeInt(ReadOnlySpan<byte> data)
{
int bytesConsumed;
if (Utf8Parser.TryParse(data, out uint value, out bytesConsumed, ' ') && value <= 2147483647 && bytesConsumed == data.Length)
return (int)value;
throw new AsnContentException();
}
private static ReadOnlySpan<byte> SliceAtMost(ReadOnlySpan<byte> source, int longestPermitted)
{
int length = Math.Min(longestPermitted, source.Length);
return source.Slice(0, length);
}
private static ReadOnlySpan<byte> Slice(ReadOnlySpan<byte> source, int offset, int length)
{
if (length < 0 || source.Length - offset < length)
throw new AsnContentException(System.SR.ContentException_LengthExceedsPayload);
return source.Slice(offset, length);
}
private static ReadOnlySpan<byte> Slice(ReadOnlySpan<byte> source, int offset, int? length)
{
if (!length.HasValue)
return source.Slice(offset);
int value = length.Value;
if (value < 0 || source.Length - offset < value)
throw new AsnContentException(System.SR.ContentException_LengthExceedsPayload);
return source.Slice(offset, value);
}
internal static ReadOnlyMemory<byte> Slice(ReadOnlyMemory<byte> bigger, ReadOnlySpan<byte> smaller)
{
if (smaller.IsEmpty)
return default(ReadOnlyMemory<byte>);
if (bigger.Span.Overlaps(smaller, out int elementOffset))
return bigger.Slice(elementOffset, smaller.Length);
throw new AsnContentException();
}
internal static void CheckEncodingRules(AsnEncodingRules ruleSet)
{
if (ruleSet != 0 && ruleSet != AsnEncodingRules.CER && ruleSet != AsnEncodingRules.DER)
throw new ArgumentOutOfRangeException("ruleSet");
}
private static void CheckExpectedTag(Asn1Tag tag, Asn1Tag expectedTag, UniversalTagNumber tagNumber)
{
if (expectedTag.TagClass == TagClass.Universal && expectedTag.TagValue != (int)tagNumber)
throw new ArgumentException(System.SR.Argument_UniversalValueIsFixed, "expectedTag");
if (expectedTag.TagClass != tag.TagClass || expectedTag.TagValue != tag.TagValue)
throw new AsnContentException(System.SR.Format(System.SR.ContentException_WrongTag, tag.TagClass, tag.TagValue, expectedTag.TagClass, expectedTag.TagValue));
}
public static bool TryReadPrimitiveBitString(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int unusedBitCount, out ReadOnlySpan<byte> value, out int bytesConsumed, Asn1Tag? expectedTag = default(Asn1Tag?))
{
if (TryReadPrimitiveBitStringCore(source, ruleSet, expectedTag ?? Asn1Tag.PrimitiveBitString, out int? _, out int _, out int unusedBitCount2, out ReadOnlySpan<byte> value2, out int bytesConsumed2, out byte normalizedLastByte) && (value2.Length == 0 || normalizedLastByte == value2[value2.Length - 1])) {
unusedBitCount = unusedBitCount2;
value = value2;
bytesConsumed = bytesConsumed2;
return true;
}
unusedBitCount = 0;
value = default(ReadOnlySpan<byte>);
bytesConsumed = 0;
return false;
}
public static bool TryReadBitString(ReadOnlySpan<byte> source, Span<byte> destination, AsnEncodingRules ruleSet, out int unusedBitCount, out int bytesConsumed, out int bytesWritten, Asn1Tag? expectedTag = default(Asn1Tag?))
{
if (source.Overlaps(destination))
throw new ArgumentException(System.SR.Argument_SourceOverlapsDestination, "destination");
if (TryReadPrimitiveBitStringCore(source, ruleSet, expectedTag ?? Asn1Tag.PrimitiveBitString, out int? contentsLength, out int headerLength, out int unusedBitCount2, out ReadOnlySpan<byte> value, out int bytesConsumed2, out byte normalizedLastByte)) {
if (value.Length > destination.Length) {
bytesConsumed = 0;
bytesWritten = 0;
unusedBitCount = 0;
return false;
}
CopyBitStringValue(value, normalizedLastByte, destination);
bytesWritten = value.Length;
bytesConsumed = bytesConsumed2;
unusedBitCount = unusedBitCount2;
return true;
}
if (TryCopyConstructedBitStringValue(Slice(source, headerLength, contentsLength), ruleSet, destination, !contentsLength.HasValue, out unusedBitCount2, out int bytesRead, out int bytesWritten2)) {
unusedBitCount = unusedBitCount2;
bytesConsumed = headerLength + bytesRead;
bytesWritten = bytesWritten2;
return true;
}
bytesWritten = (bytesConsumed = (unusedBitCount = 0));
return false;
}
[return: Nullable(1)]
public static byte[] ReadBitString(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int unusedBitCount, out int bytesConsumed, Asn1Tag? expectedTag = default(Asn1Tag?))
{
if (TryReadPrimitiveBitStringCore(source, ruleSet, expectedTag ?? Asn1Tag.PrimitiveBitString, out int? contentsLength, out int headerLength, out int unusedBitCount2, out ReadOnlySpan<byte> value, out int bytesConsumed2, out byte normalizedLastByte)) {
byte[] array = value.ToArray();
if (value.Length > 0)
array[array.Length - 1] = normalizedLastByte;
unusedBitCount = unusedBitCount2;
bytesConsumed = bytesConsumed2;
return array;
}
byte[] array2 = CryptoPool.Rent(contentsLength ?? SeekEndOfContents(source.Slice(headerLength), ruleSet));
if (TryCopyConstructedBitStringValue(Slice(source, headerLength, contentsLength), ruleSet, array2, !contentsLength.HasValue, out unusedBitCount2, out int bytesRead, out int bytesWritten)) {
byte[] result = array2.AsSpan(0, bytesWritten).ToArray();
CryptoPool.Return(array2, bytesWritten);
unusedBitCount = unusedBitCount2;
bytesConsumed = headerLength + bytesRead;
return result;
}
throw new AsnContentException();
}
private static void ParsePrimitiveBitStringContents(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int unusedBitCount, out ReadOnlySpan<byte> value, out byte normalizedLastByte)
{
if (ruleSet == AsnEncodingRules.CER && source.Length > 1000)
throw new AsnContentException(System.SR.ContentException_InvalidUnderCer_TryBerOrDer);
if (source.Length == 0)
throw new AsnContentException();
unusedBitCount = source[0];
if (unusedBitCount > 7)
throw new AsnContentException();
if (source.Length == 1) {
if (unusedBitCount > 0)
throw new AsnContentException();
value = ReadOnlySpan<byte>.Empty;
normalizedLastByte = 0;
} else {
int num = -1 << unusedBitCount;
byte b = source[source.Length - 1];
byte b2 = (byte)(b & num);
if (b2 != b && (ruleSet == AsnEncodingRules.DER || ruleSet == AsnEncodingRules.CER))
throw new AsnContentException(System.SR.ContentException_InvalidUnderCerOrDer_TryBer);
normalizedLastByte = b2;
value = source.Slice(1);
}
}
private static void CopyBitStringValue(ReadOnlySpan<byte> value, byte normalizedLastByte, Span<byte> destination)
{
if (value.Length != 0) {
value.CopyTo(destination);
destination[value.Length - 1] = normalizedLastByte;
}
}
private static int CountConstructedBitString(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, bool isIndefinite)
{
Span<byte> empty = Span<byte>.Empty;
int lastUnusedBitCount;
int bytesRead;
return ProcessConstructedBitString(source, ruleSet, empty, null, isIndefinite, out lastUnusedBitCount, out bytesRead);
}
private static void CopyConstructedBitString(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, Span<byte> destination, bool isIndefinite, out int unusedBitCount, out int bytesRead, out int bytesWritten)
{
bytesWritten = ProcessConstructedBitString(source, ruleSet, destination, CopyBitStringValue, isIndefinite, out unusedBitCount, out bytesRead);
}
private static int ProcessConstructedBitString(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, Span<byte> destination, BitStringCopyAction copyAction, bool isIndefinite, out int lastUnusedBitCount, out int bytesRead)
{
lastUnusedBitCount = 0;
bytesRead = 0;
int num = 1000;
ReadOnlySpan<byte> readOnlySpan = source;
Stack<(int, int, bool, int)> stack = null;
int num2 = 0;
Asn1Tag asn1Tag = Asn1Tag.ConstructedBitString;
Span<byte> destination2 = destination;
while (true) {
if (!readOnlySpan.IsEmpty) {
asn1Tag = ReadTagAndLength(readOnlySpan, ruleSet, out int? contentsLength, out int bytesRead2);
if (asn1Tag == Asn1Tag.PrimitiveBitString) {
if (lastUnusedBitCount != 0)
throw new AsnContentException();
if (ruleSet == AsnEncodingRules.CER && num != 1000)
throw new AsnContentException(System.SR.ContentException_InvalidUnderCer_TryBerOrDer);
ReadOnlySpan<byte> source2 = Slice(readOnlySpan, bytesRead2, contentsLength.Value);
ParsePrimitiveBitStringContents(source2, ruleSet, out lastUnusedBitCount, out ReadOnlySpan<byte> value, out byte normalizedLastByte);
int num3 = bytesRead2 + source2.Length;
readOnlySpan = readOnlySpan.Slice(num3);
bytesRead += num3;
num2 += value.Length;
num = source2.Length;
if (copyAction != null) {
copyAction(value, normalizedLastByte, destination2);
destination2 = destination2.Slice(value.Length);
}
continue;
}
if (!((asn1Tag == Asn1Tag.EndOfContents) & isIndefinite)) {
if (!(asn1Tag == Asn1Tag.ConstructedBitString))
throw new AsnContentException();
if (ruleSet == AsnEncodingRules.CER)
throw new AsnContentException(System.SR.ContentException_InvalidUnderCerOrDer_TryBer);
if (stack == null)
stack = new Stack<(int, int, bool, int)>();
if (!source.Overlaps(readOnlySpan, out int elementOffset))
throw new AsnContentException();
stack.Push((elementOffset, readOnlySpan.Length, isIndefinite, bytesRead));
readOnlySpan = Slice(readOnlySpan, bytesRead2, contentsLength);
bytesRead = bytesRead2;
isIndefinite = !contentsLength.HasValue;
continue;
}
ValidateEndOfContents(asn1Tag, contentsLength, bytesRead2);
bytesRead += bytesRead2;
if (stack != null && stack.Count > 0) {
(int, int, bool, int) valueTuple = stack.Pop();
int item = valueTuple.Item1;
int item2 = valueTuple.Item2;
bool item3 = valueTuple.Item3;
int item4 = valueTuple.Item4;
readOnlySpan = source.Slice(item, item2).Slice(bytesRead);
bytesRead += item4;
isIndefinite = item3;
continue;
}
}
if (isIndefinite && asn1Tag != Asn1Tag.EndOfContents)
throw new AsnContentException();
if (stack == null || stack.Count <= 0)
break;
(int, int, bool, int) valueTuple2 = stack.Pop();
int item5 = valueTuple2.Item1;
int item6 = valueTuple2.Item2;
bool item7 = valueTuple2.Item3;
int item8 = valueTuple2.Item4;
readOnlySpan = source.Slice(item5, item6).Slice(bytesRead);
isIndefinite = item7;
bytesRead += item8;
}
return num2;
}
private static bool TryCopyConstructedBitStringValue(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, Span<byte> dest, bool isIndefinite, out int unusedBitCount, out int bytesRead, out int bytesWritten)
{
int num = CountConstructedBitString(source, ruleSet, isIndefinite);
if (ruleSet == AsnEncodingRules.CER && num < 1000)
throw new AsnContentException(System.SR.ContentException_InvalidUnderCerOrDer_TryBer);
if (dest.Length < num) {
unusedBitCount = 0;
bytesRead = 0;
bytesWritten = 0;
return false;
}
CopyConstructedBitString(source, ruleSet, dest, isIndefinite, out unusedBitCount, out bytesRead, out bytesWritten);
return true;
}
private static bool TryReadPrimitiveBitStringCore(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, Asn1Tag expectedTag, out int? contentsLength, out int headerLength, out int unusedBitCount, out ReadOnlySpan<byte> value, out int bytesConsumed, out byte normalizedLastByte)
{
Asn1Tag tag = ReadTagAndLength(source, ruleSet, out contentsLength, out headerLength);
CheckExpectedTag(tag, expectedTag, UniversalTagNumber.BitString);
ReadOnlySpan<byte> source2 = Slice(source, headerLength, contentsLength);
if (tag.IsConstructed) {
if (ruleSet == AsnEncodingRules.DER)
throw new AsnContentException(System.SR.ContentException_InvalidUnderDer_TryBerOrCer);
unusedBitCount = 0;
value = default(ReadOnlySpan<byte>);
normalizedLastByte = 0;
bytesConsumed = 0;
return false;
}
ParsePrimitiveBitStringContents(source2, ruleSet, out unusedBitCount, out value, out normalizedLastByte);
bytesConsumed = headerLength + source2.Length;
return true;
}
public static bool ReadBoolean(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int bytesConsumed, Asn1Tag? expectedTag = default(Asn1Tag?))
{
int bytesConsumed2;
ReadOnlySpan<byte> primitiveContentSpan = GetPrimitiveContentSpan(source, ruleSet, expectedTag ?? Asn1Tag.Boolean, UniversalTagNumber.Boolean, out bytesConsumed2);
if (primitiveContentSpan.Length != 1)
throw new AsnContentException();
switch (primitiveContentSpan[0]) {
case 0:
bytesConsumed = bytesConsumed2;
return false;
default:
if (ruleSet == AsnEncodingRules.DER || ruleSet == AsnEncodingRules.CER)
throw new AsnContentException(System.SR.ContentException_InvalidUnderCerOrDer_TryBer);
break;
case byte.MaxValue:
break;
}
bytesConsumed = bytesConsumed2;
return true;
}
public static ReadOnlySpan<byte> ReadEnumeratedBytes(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int bytesConsumed, Asn1Tag? expectedTag = default(Asn1Tag?))
{
return GetIntegerContents(source, ruleSet, expectedTag ?? Asn1Tag.Enumerated, UniversalTagNumber.Enumerated, out bytesConsumed);
}
[return: Nullable(1)]
public static TEnum ReadEnumeratedValue<TEnum>(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int bytesConsumed, Asn1Tag? expectedTag = default(Asn1Tag?)) where TEnum : Enum
{
Type typeFromHandle = typeof(TEnum);
return (TEnum)Enum.ToObject(typeFromHandle, ReadEnumeratedValue(source, ruleSet, typeFromHandle, out bytesConsumed, expectedTag));
}
[NullableContext(1)]
public static Enum ReadEnumeratedValue([Nullable(0)] ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, Type enumType, out int bytesConsumed, Asn1Tag? expectedTag = default(Asn1Tag?))
{
if (enumType == (Type)null)
throw new ArgumentNullException("enumType");
Asn1Tag expectedTag2 = expectedTag ?? Asn1Tag.Enumerated;
Type enumUnderlyingType = enumType.GetEnumUnderlyingType();
if (enumType.IsDefined(typeof(FlagsAttribute), false))
throw new ArgumentException(System.SR.Argument_EnumeratedValueRequiresNonFlagsEnum, "enumType");
int primitiveIntegerSize = GetPrimitiveIntegerSize(enumUnderlyingType);
if (enumUnderlyingType == typeof(int) || enumUnderlyingType == typeof(long) || enumUnderlyingType == typeof(short) || enumUnderlyingType == typeof(sbyte)) {
if (!TryReadSignedInteger(source, ruleSet, primitiveIntegerSize, expectedTag2, UniversalTagNumber.Enumerated, out long value, out int bytesConsumed2))
throw new AsnContentException(System.SR.ContentException_EnumeratedValueTooBig);
bytesConsumed = bytesConsumed2;
return (Enum)Enum.ToObject(enumType, value);
}
if (enumUnderlyingType == typeof(uint) || enumUnderlyingType == typeof(ulong) || enumUnderlyingType == typeof(ushort) || enumUnderlyingType == typeof(byte)) {
if (!TryReadUnsignedInteger(source, ruleSet, primitiveIntegerSize, expectedTag2, UniversalTagNumber.Enumerated, out ulong value2, out int bytesConsumed3))
throw new AsnContentException(System.SR.ContentException_EnumeratedValueTooBig);
bytesConsumed = bytesConsumed3;
return (Enum)Enum.ToObject(enumType, value2);
}
throw new AsnContentException(System.SR.Format(System.SR.Argument_EnumeratedValueBackingTypeNotSupported, enumUnderlyingType.FullName));
}
public unsafe static DateTimeOffset ReadGeneralizedTime(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int bytesConsumed, Asn1Tag? expectedTag = default(Asn1Tag?))
{
byte[] rented = null;
Span<byte> tmpSpace = new Span<byte>(stackalloc byte[64], 64);
int bytesConsumed2;
ReadOnlySpan<byte> octetStringContents = GetOctetStringContents(source, ruleSet, expectedTag ?? Asn1Tag.GeneralizedTime, UniversalTagNumber.GeneralizedTime, out bytesConsumed2, ref rented, tmpSpace);
DateTimeOffset result = ParseGeneralizedTime(ruleSet, octetStringContents);
if (rented != null)
CryptoPool.Return(rented, octetStringContents.Length);
bytesConsumed = bytesConsumed2;
return result;
}
private static DateTimeOffset ParseGeneralizedTime(AsnEncodingRules ruleSet, ReadOnlySpan<byte> contentOctets)
{
bool flag = ruleSet == AsnEncodingRules.DER || ruleSet == AsnEncodingRules.CER;
if (flag && contentOctets.Length < 15)
throw new AsnContentException(System.SR.ContentException_InvalidUnderCerOrDer_TryBer);
if (contentOctets.Length < 10)
throw new AsnContentException();
ReadOnlySpan<byte> data = contentOctets;
int year = ParseNonNegativeIntAndSlice(ref data, 4);
int month = ParseNonNegativeIntAndSlice(ref data, 2);
int day = ParseNonNegativeIntAndSlice(ref data, 2);
int hour = ParseNonNegativeIntAndSlice(ref data, 2);
int? nullable = null;
int? nullable2 = null;
ulong value = 0;
ulong num = 1;
byte b = byte.MaxValue;
TimeSpan? nullable3 = null;
bool flag2 = false;
byte b2 = 0;
while (b2 == 0 && data.Length != 0) {
byte? nullable4 = <ParseGeneralizedTime>g__GetNextState|43_0(data[0]);
if (!nullable4.HasValue) {
if (!nullable.HasValue)
nullable = ParseNonNegativeIntAndSlice(ref data, 2);
else {
if (nullable2.HasValue)
throw new AsnContentException();
nullable2 = ParseNonNegativeIntAndSlice(ref data, 2);
}
} else
b2 = nullable4.Value;
}
if (b2 == 1) {
switch (data[0]) {
case 44:
if (flag)
throw new AsnContentException(System.SR.ContentException_InvalidUnderCerOrDer_TryBer);
break;
default:
throw new AsnContentException();
case 46:
break;
}
data = data.Slice(1);
if (data.IsEmpty)
throw new AsnContentException();
if (!Utf8Parser.TryParse(SliceAtMost(data, 12), out value, out int bytesConsumed, ' ') || bytesConsumed == 0)
throw new AsnContentException();
b = (byte)(value % 10);
for (int i = 0; i < bytesConsumed; i++) {
num *= 10;
}
data = data.Slice(bytesConsumed);
uint value2;
while (Utf8Parser.TryParse(SliceAtMost(data, 9), out value2, out bytesConsumed, ' ')) {
data = data.Slice(bytesConsumed);
b = (byte)(value2 % 10);
}
if (data.Length != 0) {
byte? nullable5 = <ParseGeneralizedTime>g__GetNextState|43_0(data[0]);
if (!nullable5.HasValue)
throw new AsnContentException();
b2 = nullable5.Value;
}
}
if (b2 == 2) {
byte b3 = data[0];
data = data.Slice(1);
bool flag3;
int hours;
int num2;
TimeSpan timeSpan;
switch (b3) {
case 90:
nullable3 = TimeSpan.Zero;
flag2 = true;
break;
case 43:
flag3 = false;
goto IL_0233;
case 45:
flag3 = true;
goto IL_0233;
default:
{
throw new AsnContentException();
}
IL_0233:
if (data.IsEmpty)
throw new AsnContentException();
hours = ParseNonNegativeIntAndSlice(ref data, 2);
num2 = 0;
if (data.Length != 0)
num2 = ParseNonNegativeIntAndSlice(ref data, 2);
if (num2 > 59)
throw new AsnContentException();
timeSpan = new TimeSpan(hours, num2, 0);
if (flag3)
timeSpan = -timeSpan;
nullable3 = timeSpan;
break;
}
}
if (data.IsEmpty) {
if (flag) {
if (!flag2 || !nullable2.HasValue)
throw new AsnContentException(System.SR.ContentException_InvalidUnderCerOrDer_TryBer);
if (b == 0)
throw new AsnContentException(System.SR.ContentException_InvalidUnderCerOrDer_TryBer);
}
double num3 = (double)value / (double)num;
TimeSpan timeSpan2 = TimeSpan.Zero;
if (!nullable.HasValue) {
nullable = 0;
nullable2 = 0;
if (value != 0)
timeSpan2 = new TimeSpan((long)(num3 * 36000000000));
} else if (!nullable2.HasValue) {
nullable2 = 0;
if (value != 0)
timeSpan2 = new TimeSpan((long)(num3 * 600000000));
} else if (value != 0) {
timeSpan2 = new TimeSpan((long)(num3 * 10000000));
}
try {
DateTimeOffset dateTimeOffset = nullable3.HasValue ? new DateTimeOffset(year, month, day, hour, nullable.Value, nullable2.Value, nullable3.Value) : new DateTimeOffset(new DateTime(year, month, day, hour, nullable.Value, nullable2.Value));
return dateTimeOffset + timeSpan2;
} catch (Exception inner) {
throw new AsnContentException(System.SR.ContentException_DefaultMessage, inner);
}
}
throw new AsnContentException();
}
public static ReadOnlySpan<byte> ReadIntegerBytes(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int bytesConsumed, Asn1Tag? expectedTag = default(Asn1Tag?))
{
return GetIntegerContents(source, ruleSet, expectedTag ?? Asn1Tag.Integer, UniversalTagNumber.Integer, out bytesConsumed);
}
public static BigInteger ReadInteger(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int bytesConsumed, Asn1Tag? expectedTag = default(Asn1Tag?))
{
int bytesConsumed2;
ReadOnlySpan<byte> value = ReadIntegerBytes(source, ruleSet, out bytesConsumed2, expectedTag);
BigInteger result = new BigInteger(value, false, true);
bytesConsumed = bytesConsumed2;
return result;
}
public static bool TryReadInt32(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int value, out int bytesConsumed, Asn1Tag? expectedTag = default(Asn1Tag?))
{
if (TryReadSignedInteger(source, ruleSet, 4, expectedTag ?? Asn1Tag.Integer, UniversalTagNumber.Integer, out long value2, out bytesConsumed)) {
value = (int)value2;
return true;
}
value = 0;
return false;
}
[CLSCompliant(false)]
public static bool TryReadUInt32(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out uint value, out int bytesConsumed, Asn1Tag? expectedTag = default(Asn1Tag?))
{
if (TryReadUnsignedInteger(source, ruleSet, 4, expectedTag ?? Asn1Tag.Integer, UniversalTagNumber.Integer, out ulong value2, out bytesConsumed)) {
value = (uint)value2;
return true;
}
value = 0;
return false;
}
public static bool TryReadInt64(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out long value, out int bytesConsumed, Asn1Tag? expectedTag = default(Asn1Tag?))
{
return TryReadSignedInteger(source, ruleSet, 8, expectedTag ?? Asn1Tag.Integer, UniversalTagNumber.Integer, out value, out bytesConsumed);
}
[CLSCompliant(false)]
public static bool TryReadUInt64(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out ulong value, out int bytesConsumed, Asn1Tag? expectedTag = default(Asn1Tag?))
{
return TryReadUnsignedInteger(source, ruleSet, 8, expectedTag ?? Asn1Tag.Integer, UniversalTagNumber.Integer, out value, out bytesConsumed);
}
private static ReadOnlySpan<byte> GetIntegerContents(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, Asn1Tag expectedTag, UniversalTagNumber tagNumber, out int bytesConsumed)
{
int bytesConsumed2;
ReadOnlySpan<byte> primitiveContentSpan = GetPrimitiveContentSpan(source, ruleSet, expectedTag, tagNumber, out bytesConsumed2);
if (primitiveContentSpan.IsEmpty)
throw new AsnContentException();
if (BinaryPrimitives.TryReadUInt16BigEndian(primitiveContentSpan, out ushort value)) {
ushort num = (ushort)(value & 65408);
if (num == 0 || num == 65408)
throw new AsnContentException();
}
bytesConsumed = bytesConsumed2;
return primitiveContentSpan;
}
private static bool TryReadSignedInteger(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, int sizeLimit, Asn1Tag expectedTag, UniversalTagNumber tagNumber, out long value, out int bytesConsumed)
{
int bytesConsumed2;
ReadOnlySpan<byte> integerContents = GetIntegerContents(source, ruleSet, expectedTag, tagNumber, out bytesConsumed2);
if (integerContents.Length > sizeLimit) {
value = 0;
bytesConsumed = 0;
return false;
}
long num = ((integerContents[0] & 128) != 0) ? (-1) : 0;
for (int i = 0; i < integerContents.Length; i++) {
num <<= 8;
num |= integerContents[i];
}
bytesConsumed = bytesConsumed2;
value = num;
return true;
}
private static bool TryReadUnsignedInteger(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, int sizeLimit, Asn1Tag expectedTag, UniversalTagNumber tagNumber, out ulong value, out int bytesConsumed)
{
int bytesConsumed2;
ReadOnlySpan<byte> readOnlySpan = GetIntegerContents(source, ruleSet, expectedTag, tagNumber, out bytesConsumed2);
if ((readOnlySpan[0] & 128) != 0) {
bytesConsumed = 0;
value = 0;
return false;
}
if (readOnlySpan.Length > 1 && readOnlySpan[0] == 0)
readOnlySpan = readOnlySpan.Slice(1);
if (readOnlySpan.Length > sizeLimit) {
bytesConsumed = 0;
value = 0;
return false;
}
ulong num = 0;
for (int i = 0; i < readOnlySpan.Length; i++) {
num <<= 8;
num |= readOnlySpan[i];
}
bytesConsumed = bytesConsumed2;
value = num;
return true;
}
[return: Nullable(1)]
public static TFlagsEnum ReadNamedBitListValue<TFlagsEnum>(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int bytesConsumed, Asn1Tag? expectedTag = default(Asn1Tag?)) where TFlagsEnum : Enum
{
Type typeFromHandle = typeof(TFlagsEnum);
int bytesConsumed2;
TFlagsEnum result = (TFlagsEnum)Enum.ToObject(typeFromHandle, ReadNamedBitListValue(source, ruleSet, typeFromHandle, out bytesConsumed2, expectedTag));
bytesConsumed = bytesConsumed2;
return result;
}
[NullableContext(1)]
public unsafe static Enum ReadNamedBitListValue([Nullable(0)] ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, Type flagsEnumType, out int bytesConsumed, Asn1Tag? expectedTag = default(Asn1Tag?))
{
if (flagsEnumType == (Type)null)
throw new ArgumentNullException("flagsEnumType");
Type enumUnderlyingType = flagsEnumType.GetEnumUnderlyingType();
if (!flagsEnumType.IsDefined(typeof(FlagsAttribute), false))
throw new ArgumentException(System.SR.Argument_NamedBitListRequiresFlagsEnum, "flagsEnumType");
Span<byte> destination = new Span<byte>(stackalloc byte[8], 8);
int primitiveIntegerSize = GetPrimitiveIntegerSize(enumUnderlyingType);
destination = destination.Slice(0, primitiveIntegerSize);
if (!TryReadBitString(source, destination, ruleSet, out int unusedBitCount, out int bytesConsumed2, out int bytesWritten, expectedTag))
throw new AsnContentException(System.SR.Format(System.SR.ContentException_NamedBitListValueTooBig, flagsEnumType.Name));
if (bytesWritten == 0) {
Enum result = (Enum)Enum.ToObject(flagsEnumType, 0);
bytesConsumed = bytesConsumed2;
return result;
}
ReadOnlySpan<byte> valueSpan = destination.Slice(0, bytesWritten);
if (ruleSet == AsnEncodingRules.DER || ruleSet == AsnEncodingRules.CER) {
byte num = valueSpan[bytesWritten - 1];
byte b = (byte)(1 << unusedBitCount);
if ((num & b) == 0)
throw new AsnContentException(System.SR.ContentException_InvalidUnderCerOrDer_TryBer);
}
Enum result2 = (Enum)Enum.ToObject(flagsEnumType, InterpretNamedBitListReversed(valueSpan));
bytesConsumed = bytesConsumed2;
return result2;
}
[return: Nullable(1)]
public static BitArray ReadNamedBitList(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int bytesConsumed, Asn1Tag? expectedTag = default(Asn1Tag?))
{
int contentOffset;
int contentLength;
int bytesConsumed2;
Asn1Tag tag = ReadEncodedValue(source, ruleSet, out contentOffset, out contentLength, out bytesConsumed2);
if (expectedTag.HasValue)
CheckExpectedTag(tag, expectedTag.Value, UniversalTagNumber.BitString);
byte[] array = CryptoPool.Rent(contentLength);
if (!TryReadBitString(source, array, ruleSet, out int unusedBitCount, out int bytesConsumed3, out int bytesWritten, expectedTag))
throw new InvalidOperationException();
int length = checked(bytesWritten * 8 - unusedBitCount);
ReverseBitsPerByte(array.AsSpan(0, bytesWritten));
BitArray bitArray = new BitArray(array);
CryptoPool.Return(array, bytesWritten);
bitArray.Length = length;
bytesConsumed = bytesConsumed3;
return bitArray;
}
private static long InterpretNamedBitListReversed(ReadOnlySpan<byte> valueSpan)
{
long num = 0;
long num2 = 1;
foreach (byte b in valueSpan) {
for (int num3 = 7; num3 >= 0; num3--) {
int num4 = 1 << num3;
if ((b & num4) != 0)
num |= num2;
num2 <<= 1;
}
}
return num;
}
internal static void ReverseBitsPerByte(Span<byte> value)
{
for (int i = 0; i < value.Length; i++) {
value[i] = (byte)((ulong)((value[i] * 8623620610) & 1136090292240) % 1023);
}
}
public static void ReadNull(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int bytesConsumed, Asn1Tag? expectedTag = default(Asn1Tag?))
{
if (GetPrimitiveContentSpan(source, ruleSet, expectedTag ?? Asn1Tag.Null, UniversalTagNumber.Null, out int bytesConsumed2).Length != 0)
throw new AsnContentException();
bytesConsumed = bytesConsumed2;
}
public static bool TryReadOctetString(ReadOnlySpan<byte> source, Span<byte> destination, AsnEncodingRules ruleSet, out int bytesConsumed, out int bytesWritten, Asn1Tag? expectedTag = default(Asn1Tag?))
{
if (source.Overlaps(destination))
throw new ArgumentException(System.SR.Argument_SourceOverlapsDestination, "destination");
if (TryReadPrimitiveOctetStringCore(source, ruleSet, expectedTag ?? Asn1Tag.PrimitiveOctetString, UniversalTagNumber.OctetString, out int? contentLength, out int headerLength, out ReadOnlySpan<byte> contents, out int bytesConsumed2)) {
if (contents.Length > destination.Length) {
bytesWritten = 0;
bytesConsumed = 0;
return false;
}
contents.CopyTo(destination);
bytesWritten = contents.Length;
bytesConsumed = bytesConsumed2;
return true;
}
int bytesRead;
bool num = TryCopyConstructedOctetStringContents(Slice(source, headerLength, contentLength), ruleSet, destination, !contentLength.HasValue, out bytesRead, out bytesWritten);
if (num) {
bytesConsumed = headerLength + bytesRead;
return num;
}
bytesConsumed = 0;
return num;
}
[return: Nullable(1)]
public static byte[] ReadOctetString(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int bytesConsumed, Asn1Tag? expectedTag = default(Asn1Tag?))
{
byte[] rented = null;
int bytesConsumed2;
ReadOnlySpan<byte> octetStringContents = GetOctetStringContents(source, ruleSet, expectedTag ?? Asn1Tag.PrimitiveOctetString, UniversalTagNumber.OctetString, out bytesConsumed2, ref rented, default(Span<byte>));
byte[] result = octetStringContents.ToArray();
if (rented != null)
CryptoPool.Return(rented, octetStringContents.Length);
bytesConsumed = bytesConsumed2;
return result;
}
private static bool TryReadPrimitiveOctetStringCore(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, Asn1Tag expectedTag, UniversalTagNumber universalTagNumber, out int? contentLength, out int headerLength, out ReadOnlySpan<byte> contents, out int bytesConsumed)
{
Asn1Tag tag = ReadTagAndLength(source, ruleSet, out contentLength, out headerLength);
CheckExpectedTag(tag, expectedTag, universalTagNumber);
ReadOnlySpan<byte> readOnlySpan = Slice(source, headerLength, contentLength);
if (tag.IsConstructed) {
if (ruleSet == AsnEncodingRules.DER)
throw new AsnContentException(System.SR.ContentException_InvalidUnderDer_TryBerOrCer);
contents = default(ReadOnlySpan<byte>);
bytesConsumed = 0;
return false;
}
if (ruleSet == AsnEncodingRules.CER && readOnlySpan.Length > 1000)
throw new AsnContentException(System.SR.ContentException_InvalidUnderCer_TryBerOrDer);
contents = readOnlySpan;
bytesConsumed = headerLength + readOnlySpan.Length;
return true;
}
public static bool TryReadPrimitiveOctetString(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out ReadOnlySpan<byte> value, out int bytesConsumed, Asn1Tag? expectedTag = default(Asn1Tag?))
{
int? contentLength;
int headerLength;
return TryReadPrimitiveOctetStringCore(source, ruleSet, expectedTag ?? Asn1Tag.PrimitiveOctetString, UniversalTagNumber.OctetString, out contentLength, out headerLength, out value, out bytesConsumed);
}
private static int CountConstructedOctetString(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, bool isIndefinite)
{
int bytesRead;
int num = CopyConstructedOctetString(source, ruleSet, Span<byte>.Empty, false, isIndefinite, out bytesRead);
if (ruleSet == AsnEncodingRules.CER && num <= 1000)
throw new AsnContentException(System.SR.ContentException_InvalidUnderCerOrDer_TryBer);
return num;
}
private static void CopyConstructedOctetString(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, Span<byte> destination, bool isIndefinite, out int bytesRead, out int bytesWritten)
{
bytesWritten = CopyConstructedOctetString(source, ruleSet, destination, true, isIndefinite, out bytesRead);
}
private static int CopyConstructedOctetString(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, Span<byte> destination, bool write, bool isIndefinite, out int bytesRead)
{
bytesRead = 0;
int num = 1000;
ReadOnlySpan<byte> readOnlySpan = source;
Stack<(int, int, bool, int)> stack = null;
int num2 = 0;
Asn1Tag asn1Tag = Asn1Tag.ConstructedBitString;
Span<byte> destination2 = destination;
while (true) {
if (!readOnlySpan.IsEmpty) {
asn1Tag = ReadTagAndLength(readOnlySpan, ruleSet, out int? contentsLength, out int bytesRead2);
if (asn1Tag == Asn1Tag.PrimitiveOctetString) {
if (ruleSet == AsnEncodingRules.CER && num != 1000)
throw new AsnContentException(System.SR.ContentException_InvalidUnderCerOrDer_TryBer);
ReadOnlySpan<byte> readOnlySpan2 = Slice(readOnlySpan, bytesRead2, contentsLength.Value);
int num3 = bytesRead2 + readOnlySpan2.Length;
readOnlySpan = readOnlySpan.Slice(num3);
bytesRead += num3;
num2 += readOnlySpan2.Length;
num = readOnlySpan2.Length;
if (ruleSet == AsnEncodingRules.CER && num > 1000)
throw new AsnContentException(System.SR.ContentException_InvalidUnderCerOrDer_TryBer);
if (write) {
readOnlySpan2.CopyTo(destination2);
destination2 = destination2.Slice(readOnlySpan2.Length);
}
continue;
}
if (!((asn1Tag == Asn1Tag.EndOfContents) & isIndefinite)) {
if (!(asn1Tag == Asn1Tag.ConstructedOctetString))
throw new AsnContentException();
if (ruleSet == AsnEncodingRules.CER)
throw new AsnContentException(System.SR.ContentException_InvalidUnderCerOrDer_TryBer);
if (stack == null)
stack = new Stack<(int, int, bool, int)>();
if (!source.Overlaps(readOnlySpan, out int elementOffset))
throw new AsnContentException();
stack.Push((elementOffset, readOnlySpan.Length, isIndefinite, bytesRead));
readOnlySpan = Slice(readOnlySpan, bytesRead2, contentsLength);
bytesRead = bytesRead2;
isIndefinite = !contentsLength.HasValue;
continue;
}
ValidateEndOfContents(asn1Tag, contentsLength, bytesRead2);
bytesRead += bytesRead2;
if (stack != null && stack.Count > 0) {
(int, int, bool, int) valueTuple = stack.Pop();
int item = valueTuple.Item1;
int item2 = valueTuple.Item2;
bool item3 = valueTuple.Item3;
int item4 = valueTuple.Item4;
readOnlySpan = source.Slice(item, item2).Slice(bytesRead);
bytesRead += item4;
isIndefinite = item3;
continue;
}
}
if (isIndefinite && asn1Tag != Asn1Tag.EndOfContents)
throw new AsnContentException();
if (stack == null || stack.Count <= 0)
break;
(int, int, bool, int) valueTuple2 = stack.Pop();
int item5 = valueTuple2.Item1;
int item6 = valueTuple2.Item2;
bool item7 = valueTuple2.Item3;
int item8 = valueTuple2.Item4;
readOnlySpan = source.Slice(item5, item6).Slice(bytesRead);
isIndefinite = item7;
bytesRead += item8;
}
return num2;
}
private static bool TryCopyConstructedOctetStringContents(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, Span<byte> dest, bool isIndefinite, out int bytesRead, out int bytesWritten)
{
bytesRead = 0;
int num = CountConstructedOctetString(source, ruleSet, isIndefinite);
if (dest.Length < num) {
bytesWritten = 0;
return false;
}
CopyConstructedOctetString(source, ruleSet, dest, isIndefinite, out bytesRead, out bytesWritten);
return true;
}
private static ReadOnlySpan<byte> GetOctetStringContents(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, Asn1Tag expectedTag, UniversalTagNumber universalTagNumber, out int bytesConsumed, ref byte[] rented, Span<byte> tmpSpace = default(Span<byte>))
{
if (TryReadPrimitiveOctetStringCore(source, ruleSet, expectedTag, universalTagNumber, out int? contentLength, out int headerLength, out ReadOnlySpan<byte> contents, out bytesConsumed))
return contents;
contents = source.Slice(headerLength);
int num = contentLength ?? SeekEndOfContents(contents, ruleSet);
if (tmpSpace.Length > 0 && num > tmpSpace.Length) {
bool isIndefinite = !contentLength.HasValue;
num = CountConstructedOctetString(contents, ruleSet, isIndefinite);
}
if (num > tmpSpace.Length) {
rented = CryptoPool.Rent(num);
tmpSpace = rented;
}
if (TryCopyConstructedOctetStringContents(Slice(source, headerLength, contentLength), ruleSet, tmpSpace, !contentLength.HasValue, out int bytesRead, out int bytesWritten)) {
bytesConsumed = headerLength + bytesRead;
return tmpSpace.Slice(0, bytesWritten);
}
throw new AsnContentException();
}
[return: Nullable(1)]
public static string ReadObjectIdentifier(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int bytesConsumed, Asn1Tag? expectedTag = default(Asn1Tag?))
{
int bytesConsumed2;
ReadOnlySpan<byte> primitiveContentSpan = GetPrimitiveContentSpan(source, ruleSet, expectedTag ?? Asn1Tag.ObjectIdentifier, UniversalTagNumber.ObjectIdentifier, out bytesConsumed2);
string value = WellKnownOids.GetValue(primitiveContentSpan);
if (value != null) {
bytesConsumed = bytesConsumed2;
return value;
}
string result = ReadObjectIdentifier(primitiveContentSpan);
bytesConsumed = bytesConsumed2;
return result;
}
private unsafe static void ReadSubIdentifier(ReadOnlySpan<byte> source, out int bytesRead, out long? smallValue, out BigInteger? largeValue)
{
if (source[0] == 128)
throw new AsnContentException();
byte b = source[0];
int num = (b >= 136) ? ((b >= 160) ? ((b < 192) ? (-1) : 0) : ((b < 144) ? (-3) : (-2))) : ((b >= 130) ? ((b < 132) ? (-5) : (-4)) : ((b >= 129) ? (-6) : 0));
int num2 = num;
int num3 = -1;
for (int i = 0; i < source.Length; i++) {
num2 += 7;
if (num2 > 128)
throw new AsnContentException(System.SR.ContentException_OidTooBig);
if ((source[i] & 128) == 0) {
num3 = i;
break;
}
}
if (num3 < 0)
throw new AsnContentException();
bytesRead = num3 + 1;
long num4 = 0;
if (bytesRead <= 9) {
for (int i = 0; i < bytesRead; i++) {
byte b2 = source[i];
num4 <<= 7;
num4 |= (byte)(b2 & 127);
}
largeValue = null;
smallValue = num4;
} else {
int minimumLength = (bytesRead / 8 + 1) * 7;
byte[] array = CryptoPool.Rent(minimumLength);
Array.Clear(array, 0, array.Length);
Span<byte> destination = array;
Span<byte> destination2 = new Span<byte>(stackalloc byte[8], 8);
int num5 = bytesRead;
int i = bytesRead - 8;
while (num5 > 0) {
byte b3 = source[i];
num4 <<= 7;
num4 |= (byte)(b3 & 127);
i++;
if (i >= num5) {
BinaryPrimitives.WriteInt64LittleEndian(destination2, num4);
destination2.Slice(0, 7).CopyTo(destination);
destination = destination.Slice(7);
num4 = 0;
num5 -= 8;
i = Math.Max(0, num5 - 8);
}
}
int clearSize = array.Length - destination.Length;
largeValue = new BigInteger(array);
smallValue = null;
CryptoPool.Return(array, clearSize);
}
}
private static string ReadObjectIdentifier(ReadOnlySpan<byte> contents)
{
if (contents.Length < 1)
throw new AsnContentException();
StringBuilder stringBuilder = new StringBuilder((byte)contents.Length * 4);
ReadSubIdentifier(contents, out int bytesRead, out long? smallValue, out BigInteger? largeValue);
if (smallValue.HasValue) {
long num = smallValue.Value;
byte value;
if (num < 40)
value = 0;
else if (num < 80) {
value = 1;
num -= 40;
} else {
value = 2;
num -= 80;
}
stringBuilder.Append(value);
stringBuilder.Append('.');
stringBuilder.Append(num);
} else {
BigInteger left = largeValue.Value;
byte value = 2;
left -= (BigInteger)80;
stringBuilder.Append(value);
stringBuilder.Append('.');
stringBuilder.Append(left.ToString());
}
contents = contents.Slice(bytesRead);
int num2 = 62;
while (!contents.IsEmpty) {
if (num2 <= 0)
throw new AsnContentException(System.SR.ContentException_OidTooBig);
num2--;
ReadSubIdentifier(contents, out bytesRead, out smallValue, out largeValue);
stringBuilder.Append('.');
if (smallValue.HasValue)
stringBuilder.Append(smallValue.Value);
else
stringBuilder.Append(largeValue.Value.ToString());
contents = contents.Slice(bytesRead);
}
return stringBuilder.ToString();
}
public static void ReadSequence(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int contentOffset, out int contentLength, out int bytesConsumed, Asn1Tag? expectedTag = default(Asn1Tag?))
{
int? contentsLength;
int bytesRead;
Asn1Tag tag = ReadTagAndLength(source, ruleSet, out contentsLength, out bytesRead);
CheckExpectedTag(tag, expectedTag ?? Asn1Tag.Sequence, UniversalTagNumber.Sequence);
if (!tag.IsConstructed)
throw new AsnContentException(System.SR.Format(System.SR.ContentException_ConstructedEncodingRequired, UniversalTagNumber.Sequence));
if (contentsLength.HasValue) {
if (contentsLength.Value > source.Length - bytesRead)
throw GetValidityException(LengthValidity.LengthExceedsInput);
contentLength = contentsLength.Value;
contentOffset = bytesRead;
bytesConsumed = contentLength + bytesRead;
} else {
int num = contentLength = SeekEndOfContents(source.Slice(bytesRead), ruleSet);
contentOffset = bytesRead;
bytesConsumed = num + bytesRead + 2;
}
}
public static void ReadSetOf(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int contentOffset, out int contentLength, out int bytesConsumed, bool skipSortOrderValidation = false, Asn1Tag? expectedTag = default(Asn1Tag?))
{
int? contentsLength;
int bytesRead;
Asn1Tag tag = ReadTagAndLength(source, ruleSet, out contentsLength, out bytesRead);
CheckExpectedTag(tag, expectedTag ?? Asn1Tag.SetOf, UniversalTagNumber.Set);
if (!tag.IsConstructed)
throw new AsnContentException(System.SR.Format(System.SR.ContentException_ConstructedEncodingRequired, UniversalTagNumber.Set));
int num;
ReadOnlySpan<byte> readOnlySpan;
if (contentsLength.HasValue) {
num = 0;
readOnlySpan = Slice(source, bytesRead, contentsLength.Value);
} else {
int length = SeekEndOfContents(source.Slice(bytesRead), ruleSet);
readOnlySpan = Slice(source, bytesRead, length);
num = 2;
}
if (!skipSortOrderValidation && (ruleSet == AsnEncodingRules.DER || ruleSet == AsnEncodingRules.CER)) {
ReadOnlySpan<byte> source2 = readOnlySpan;
ReadOnlySpan<byte> y = default(ReadOnlySpan<byte>);
while (!source2.IsEmpty) {
ReadEncodedValue(source2, ruleSet, out int _, out int _, out int bytesConsumed2);
ReadOnlySpan<byte> readOnlySpan2 = source2.Slice(0, bytesConsumed2);
source2 = source2.Slice(bytesConsumed2);
if (SetOfValueComparer.Compare(readOnlySpan2, y) < 0)
throw new AsnContentException(System.SR.ContentException_SetOfNotSorted);
y = readOnlySpan2;
}
}
contentOffset = bytesRead;
contentLength = readOnlySpan.Length;
bytesConsumed = bytesRead + readOnlySpan.Length + num;
}
public static bool TryReadPrimitiveCharacterStringBytes(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, Asn1Tag expectedTag, out ReadOnlySpan<byte> value, out int bytesConsumed)
{
UniversalTagNumber universalTagNumber = UniversalTagNumber.IA5String;
if (expectedTag.TagClass == TagClass.Universal) {
universalTagNumber = (UniversalTagNumber)expectedTag.TagValue;
if (!IsCharacterStringEncodingType(universalTagNumber))
throw new ArgumentException(System.SR.Argument_Tag_NotCharacterString, "expectedTag");
}
int? contentLength;
int headerLength;
return TryReadPrimitiveOctetStringCore(source, ruleSet, expectedTag, universalTagNumber, out contentLength, out headerLength, out value, out bytesConsumed);
}
public static bool TryReadCharacterStringBytes(ReadOnlySpan<byte> source, Span<byte> destination, AsnEncodingRules ruleSet, Asn1Tag expectedTag, out int bytesConsumed, out int bytesWritten)
{
if (source.Overlaps(destination))
throw new ArgumentException(System.SR.Argument_SourceOverlapsDestination, "destination");
UniversalTagNumber universalTagNumber = UniversalTagNumber.IA5String;
if (expectedTag.TagClass == TagClass.Universal) {
universalTagNumber = (UniversalTagNumber)expectedTag.TagValue;
if (!IsCharacterStringEncodingType(universalTagNumber))
throw new ArgumentException(System.SR.Argument_Tag_NotCharacterString, "expectedTag");
}
return TryReadCharacterStringBytesCore(source, ruleSet, expectedTag, universalTagNumber, destination, out bytesConsumed, out bytesWritten);
}
public static bool TryReadCharacterString(ReadOnlySpan<byte> source, Span<char> destination, AsnEncodingRules ruleSet, UniversalTagNumber encodingType, out int bytesConsumed, out int charsWritten, Asn1Tag? expectedTag = default(Asn1Tag?))
{
Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType);
return TryReadCharacterStringCore(source, ruleSet, expectedTag ?? new Asn1Tag(encodingType, false), encodingType, encoding, destination, out bytesConsumed, out charsWritten);
}
[return: Nullable(1)]
public static string ReadCharacterString(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, UniversalTagNumber encodingType, out int bytesConsumed, Asn1Tag? expectedTag = default(Asn1Tag?))
{
Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType);
return ReadCharacterStringCore(source, ruleSet, expectedTag ?? new Asn1Tag(encodingType, false), encodingType, encoding, out bytesConsumed);
}
private static bool TryReadCharacterStringBytesCore(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, Asn1Tag expectedTag, UniversalTagNumber universalTagNumber, Span<byte> destination, out int bytesConsumed, out int bytesWritten)
{
if (TryReadPrimitiveOctetStringCore(source, ruleSet, expectedTag, universalTagNumber, out int? contentLength, out int headerLength, out ReadOnlySpan<byte> contents, out int bytesConsumed2)) {
if (contents.Length > destination.Length) {
bytesWritten = 0;
bytesConsumed = 0;
return false;
}
contents.CopyTo(destination);
bytesWritten = contents.Length;
bytesConsumed = bytesConsumed2;
return true;
}
int bytesRead;
bool num = TryCopyConstructedOctetStringContents(Slice(source, headerLength, contentLength), ruleSet, destination, !contentLength.HasValue, out bytesRead, out bytesWritten);
if (num) {
bytesConsumed = headerLength + bytesRead;
return num;
}
bytesConsumed = 0;
return num;
}
private static bool TryReadCharacterStringCore(ReadOnlySpan<byte> source, Span<char> destination, Encoding encoding, out int charsWritten)
{
try {
return encoding.TryGetChars(source, destination, out charsWritten);
} catch (DecoderFallbackException inner) {
throw new AsnContentException(System.SR.ContentException_DefaultMessage, inner);
}
}
private unsafe static string ReadCharacterStringCore(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, Asn1Tag expectedTag, UniversalTagNumber universalTagNumber, Encoding encoding, out int bytesConsumed)
{
byte[] rented = null;
int bytesConsumed2;
ReadOnlySpan<byte> octetStringContents = GetOctetStringContents(source, ruleSet, expectedTag, universalTagNumber, out bytesConsumed2, ref rented, default(Span<byte>));
string result;
if (octetStringContents.Length != 0) {
fixed (byte* bytes = &MemoryMarshal.GetReference(octetStringContents)) {
try {
result = encoding.GetString(bytes, octetStringContents.Length);
} catch (DecoderFallbackException inner) {
throw new AsnContentException(System.SR.ContentException_DefaultMessage, inner);
}
}
} else
result = string.Empty;
if (rented != null)
CryptoPool.Return(rented, octetStringContents.Length);
bytesConsumed = bytesConsumed2;
return result;
}
private static bool TryReadCharacterStringCore(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, Asn1Tag expectedTag, UniversalTagNumber universalTagNumber, Encoding encoding, Span<char> destination, out int bytesConsumed, out int charsWritten)
{
byte[] rented = null;
int bytesConsumed2;
ReadOnlySpan<byte> octetStringContents = GetOctetStringContents(source, ruleSet, expectedTag, universalTagNumber, out bytesConsumed2, ref rented, default(Span<byte>));
bool num = TryReadCharacterStringCore(octetStringContents, destination, encoding, out charsWritten);
if (rented != null)
CryptoPool.Return(rented, octetStringContents.Length);
if (num) {
bytesConsumed = bytesConsumed2;
return num;
}
bytesConsumed = 0;
return num;
}
private static bool IsCharacterStringEncodingType(UniversalTagNumber encodingType)
{
switch (encodingType) {
case UniversalTagNumber.UTF8String:
case UniversalTagNumber.NumericString:
case UniversalTagNumber.PrintableString:
case UniversalTagNumber.TeletexString:
case UniversalTagNumber.VideotexString:
case UniversalTagNumber.IA5String:
case UniversalTagNumber.GraphicString:
case UniversalTagNumber.VisibleString:
case UniversalTagNumber.GeneralString:
case UniversalTagNumber.UniversalString:
case UniversalTagNumber.BMPString:
return true;
default:
return false;
}
}
public unsafe static DateTimeOffset ReadUtcTime(ReadOnlySpan<byte> source, AsnEncodingRules ruleSet, out int bytesConsumed, int twoDigitYearMax = 2049, Asn1Tag? expectedTag = default(Asn1Tag?))
{
if (twoDigitYearMax < 1 || twoDigitYearMax > 9999)
throw new ArgumentOutOfRangeException("twoDigitYearMax");
Span<byte> tmpSpace = new Span<byte>(stackalloc byte[17], 17);
byte[] rented = null;
int bytesConsumed2;
ReadOnlySpan<byte> octetStringContents = GetOctetStringContents(source, ruleSet, expectedTag ?? Asn1Tag.UtcTime, UniversalTagNumber.UtcTime, out bytesConsumed2, ref rented, tmpSpace);
DateTimeOffset result = ParseUtcTime(octetStringContents, ruleSet, twoDigitYearMax);
if (rented != null)
CryptoPool.Return(rented, octetStringContents.Length);
bytesConsumed = bytesConsumed2;
return result;
}
private static DateTimeOffset ParseUtcTime(ReadOnlySpan<byte> contentOctets, AsnEncodingRules ruleSet, int twoDigitYearMax)
{
if ((ruleSet == AsnEncodingRules.DER || ruleSet == AsnEncodingRules.CER) && contentOctets.Length != 13)
throw new AsnContentException(System.SR.ContentException_InvalidUnderCerOrDer_TryBer);
if (contentOctets.Length < 11 || contentOctets.Length > 17 || (contentOctets.Length & 1) != 1)
throw new AsnContentException();
ReadOnlySpan<byte> data = contentOctets;
int num = ParseNonNegativeIntAndSlice(ref data, 2);
int month = ParseNonNegativeIntAndSlice(ref data, 2);
int day = ParseNonNegativeIntAndSlice(ref data, 2);
int hour = ParseNonNegativeIntAndSlice(ref data, 2);
int minute = ParseNonNegativeIntAndSlice(ref data, 2);
int second = 0;
int hours = 0;
int num2 = 0;
bool flag = false;
if (contentOctets.Length == 17 || contentOctets.Length == 13)
second = ParseNonNegativeIntAndSlice(ref data, 2);
if (contentOctets.Length == 11 || contentOctets.Length == 13) {
if (data[0] != 90)
throw new AsnContentException();
} else {
if (data[0] == 45)
flag = true;
else if (data[0] != 43) {
throw new AsnContentException();
}
data = data.Slice(1);
hours = ParseNonNegativeIntAndSlice(ref data, 2);
num2 = ParseNonNegativeIntAndSlice(ref data, 2);
}
if (num2 <= 59) {
TimeSpan timeSpan = new TimeSpan(hours, num2, 0);
if (flag)
timeSpan = -timeSpan;
int num3 = twoDigitYearMax / 100;
if (num > twoDigitYearMax % 100)
num3--;
int year = num3 * 100 + num;
try {
return new DateTimeOffset(year, month, day, hour, minute, second, timeSpan);
} catch (Exception inner) {
throw new AsnContentException(System.SR.ContentException_DefaultMessage, inner);
}
}
throw new AsnContentException();
}
}
}