<PackageReference Include="System.Formats.Asn1" Version="10.0.0-preview.7.25380.108" />

AsnDecoder

public static class 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(); } } }