/// <summary> /// Reads the next value as a SET-OF with the specified tag /// and returns the result as an <see cref="AsnReader"/> positioned at the first /// value in the set-of (or with <see cref="HasData"/> == <c>false</c>). /// </summary> /// <param name="expectedTag">The tag to check for before reading.</param> /// <param name="skipSortOrderValidation"> /// <c>true</c> to always accept the data in the order it is presented, /// <c>false</c> to verify that the data is sorted correctly when the /// encoding rules say sorting was required (CER and DER). /// </param> /// <returns> /// an <see cref="AsnReader"/> positioned at the first /// value in the set-of (or with <see cref="HasData"/> == <c>false</c>). /// </returns> /// <remarks> /// the nested content is not evaluated by this method (aside from sort order, when /// required), and may contain data which is not valid under the current encoding rules. /// </remarks> /// <exception cref="CryptographicException"> /// the next value does not have the correct tag --OR-- /// the length encoding is not valid under the current encoding rules --OR-- /// the contents are not valid under the current encoding rules /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagClass"/> is /// <see cref="TagClass.Universal"/>, but /// <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for /// the method /// </exception> public AsnReader ReadSetOf(Asn1Tag expectedTag, bool skipSortOrderValidation = false) { Asn1Tag tag = ReadTagAndLength(out int?length, out int headerLength); CheckExpectedTag(tag, expectedTag, UniversalTagNumber.SetOf); // T-REC-X.690-201508 sec 8.12.1 if (!tag.IsConstructed) { throw new CryptographicException(SR.Resource("Cryptography_Der_Invalid_Encoding")); } int suffix = 0; if (length == null) { length = SeekEndOfContents(_data.Slice(headerLength)); suffix = EndOfContentsEncodedLength; } ReadOnlyMemory <byte> contents = Slice(_data, headerLength, length.Value); if (!skipSortOrderValidation) { // T-REC-X.690-201508 sec 11.6 // BER data is not required to be sorted. if (RuleSet == AsnEncodingRules.DER || RuleSet == AsnEncodingRules.CER) { AsnReader reader = new AsnReader(contents, RuleSet); ReadOnlyMemory <byte> current = ReadOnlyMemory <byte> .Empty; SetOfValueComparer comparer = SetOfValueComparer.Instance; while (reader.HasData) { ReadOnlyMemory <byte> previous = current; current = reader.ReadEncodedValue(); if (comparer.Compare(current, previous) < 0) { throw new CryptographicException(SR.Resource("Cryptography_Der_Invalid_Encoding")); } } } } _data = _data.Slice(headerLength + contents.Length + suffix); return(new AsnReader(contents, RuleSet)); }
/// <summary> /// Reads a Set-Of value from <paramref name="source"/> with a specified tag /// under the specified encoding rules. /// </summary> /// <param name="source">The buffer containing encoded data.</param> /// <param name="ruleSet">The encoding constraints to use when interpreting the data.</param> /// <param name="contentOffset"> /// When this method returns, the offset of the content payload relative to the start of /// <paramref name="source"/>. /// This parameter is treated as uninitialized. /// </param> /// <param name="contentLength"> /// When this method returns, the number of bytes in the content payload (which may be 0). /// This parameter is treated as uninitialized. /// </param> /// <param name="bytesConsumed"> /// When this method returns, the total number of bytes for the encoded value. /// This parameter is treated as uninitialized. /// </param> /// <param name="skipSortOrderValidation"> /// <see langword="true"/> to always accept the data in the order it is presented, /// <see langword="false"/> to verify that the data is sorted correctly when the /// encoding rules say sorting was required (CER and DER). /// </param> /// <param name="expectedTag"> /// The tag to check for before reading, or <see langword="null"/> for the default tag (Universal 17). /// </param> /// <remarks> /// The nested content is not evaluated by this method, except for minimal processing to /// determine the location of an end-of-contents marker or verification of the content /// sort order. /// Therefore, the contents may contain data which is not valid under the current encoding rules. /// </remarks> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="ruleSet"/> is not defined. /// </exception> /// <exception cref="AsnContentException"> /// the next value does not have the correct tag. /// /// -or- /// /// the length encoding is not valid under the current encoding rules. /// /// -or- /// /// the contents are not valid under the current encoding rules. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagClass"/> is /// <see cref="TagClass.Universal"/>, but /// <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for /// the method. /// </exception> public static void ReadSetOf( ReadOnlySpan <byte> source, AsnEncodingRules ruleSet, out int contentOffset, out int contentLength, out int bytesConsumed, bool skipSortOrderValidation = false, Asn1Tag?expectedTag = null) { Asn1Tag tag = ReadTagAndLength(source, ruleSet, out int?length, out int headerLength); CheckExpectedTag(tag, expectedTag ?? Asn1Tag.SetOf, UniversalTagNumber.SetOf); // T-REC-X.690-201508 sec 8.12.1 if (!tag.IsConstructed) { throw new AsnContentException( SR.Format( SR.ContentException_ConstructedEncodingRequired, UniversalTagNumber.SetOf)); } int suffix; ReadOnlySpan <byte> contents; if (length.HasValue) { suffix = 0; contents = Slice(source, headerLength, length.Value); } else { int actualLength = SeekEndOfContents(source.Slice(headerLength), ruleSet); contents = Slice(source, headerLength, actualLength); suffix = EndOfContentsEncodedLength; } if (!skipSortOrderValidation) { // T-REC-X.690-201508 sec 11.6 // BER data is not required to be sorted. if (ruleSet == AsnEncodingRules.DER || ruleSet == AsnEncodingRules.CER) { ReadOnlySpan <byte> remaining = contents; ReadOnlySpan <byte> previous = default; while (!remaining.IsEmpty) { ReadEncodedValue(remaining, ruleSet, out _, out _, out int consumed); ReadOnlySpan <byte> current = remaining.Slice(0, consumed); remaining = remaining.Slice(consumed); if (SetOfValueComparer.Compare(current, previous) < 0) { throw new AsnContentException(SR.ContentException_SetOfNotSorted); } previous = current; } } } contentOffset = headerLength; contentLength = contents.Length; bytesConsumed = headerLength + contents.Length + suffix; }