/// <summary> /// Locates the contents range for the encoded value at the beginning of the /// <paramref name="source"/> buffer using 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> /// <returns> /// The tag identifying the content. /// </returns> /// <remarks> /// <para> /// This method performs very little validation on the contents. /// If the encoded value uses a definite length, the contents are not inspected at all. /// If the encoded value uses an indefinite length, the contents are only inspected /// as necessary to determine the location of the relevant end-of-contents marker. /// </para> /// <para> /// When the encoded value uses an indefinite length, the <paramref name="bytesConsumed"/> /// value will be larger than the sum of <paramref name="contentOffset"/> and /// <paramref name="contentLength"/> to account for the end-of-contents marker. /// </para> /// </remarks> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="ruleSet"/> is not defined. /// </exception> /// <exception cref="AsnContentException"> /// <paramref name="source"/> does not represent a value encoded under the specified /// encoding rules. /// </exception> public static Asn1Tag ReadEncodedValue( ReadOnlySpan <byte> source, AsnEncodingRules ruleSet, out int contentOffset, out int contentLength, out int bytesConsumed) { CheckEncodingRules(ruleSet); Asn1Tag tag = Asn1Tag.Decode(source, out int tagLength); int? encodedLength = ReadLength(source.Slice(tagLength), ruleSet, out int lengthLength); int headerLength = tagLength + lengthLength; LengthValidity validity = ValidateLength( source.Slice(headerLength), ruleSet, tag, encodedLength, out int len, out int consumed); if (validity == LengthValidity.Valid) { contentOffset = headerLength; contentLength = len; bytesConsumed = headerLength + consumed; return(tag); } throw GetValidityException(validity); }
private static AsnContentException GetValidityException(LengthValidity validity) { Debug.Assert(validity != LengthValidity.Valid); switch (validity) { case LengthValidity.CerRequiresIndefinite: return(new AsnContentException(SR.ContentException_CerRequiresIndefiniteLength)); case LengthValidity.LengthExceedsInput: return(new AsnContentException(SR.ContentException_LengthExceedsPayload)); case LengthValidity.PrimitiveEncodingRequiresDefinite: return(new AsnContentException()); default: Debug.Fail($"No handler for validity {validity}."); goto case LengthValidity.PrimitiveEncodingRequiresDefinite; } }
/// <summary> /// Attempts locate the contents range for the encoded value at the beginning of the /// <paramref name="source"/> buffer using 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="tag"> /// When this method returns, the tag identifying the content. /// This parameter is treated as uninitialized. /// </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> /// <returns> /// <see langword="true" /> if <paramref name="source"/> represents a valid structural /// encoding for the specified encoding rules; otherwise, <see langword="false"/>. /// </returns> /// <remarks> /// <para> /// This method performs very little validation on the contents. /// If the encoded value uses a definite length, the contents are not inspected at all. /// If the encoded value uses an indefinite length, the contents are only inspected /// as necessary to determine the location of the relevant end-of-contents marker. /// </para> /// <para> /// When the encoded value uses an indefinite length, the <paramref name="bytesConsumed"/> /// value will be larger than the sum of <paramref name="contentOffset"/> and /// <paramref name="contentLength"/> to account for the end-of-contents marker. /// </para> /// </remarks> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="ruleSet"/> is not defined. /// </exception> 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 localTag, out int tagLength) && TryReadLength(source.Slice(tagLength), ruleSet, out int?encodedLength, out int lengthLength)) { int headerLength = tagLength + lengthLength; LengthValidity validity = ValidateLength( source.Slice(headerLength), ruleSet, localTag, encodedLength, out int len, out int consumed); if (validity == LengthValidity.Valid) { tag = localTag; contentOffset = headerLength; contentLength = len; bytesConsumed = headerLength + consumed; return(true); } } tag = default; contentOffset = contentLength = bytesConsumed = 0; return(false); }