/// <summary> /// Tries to parse the given input buffer into a new <see cref="AuthenticationResults"/> instance. /// </summary> /// <remarks> /// Parses an Authentication-Results header value from the supplied buffer starting at the given index /// and spanning across the specified number of bytes. /// </remarks> /// <returns><c>true</c> if the authentication results were successfully parsed; otherwise, <c>false</c>.</returns> /// <param name="buffer">The input buffer.</param> /// <param name="startIndex">The starting index of the input buffer.</param> /// <param name="length">The number of bytes in the input buffer to parse.</param> /// <param name="authres">The parsed authentication results.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="buffer"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentOutOfRangeException"> /// <paramref name="startIndex"/> and <paramref name="length"/> do not specify /// a valid range in the byte array. /// </exception> public static bool TryParse(byte[] buffer, int startIndex, int length, out AuthenticationResults authres) { ParseUtils.ValidateArguments(buffer, startIndex, length); int index = startIndex; return(TryParse(buffer, ref index, startIndex + length, false, out authres)); }
/// <summary> /// Tries to parse the given input buffer into a new <see cref="AuthenticationResults"/> instance. /// </summary> /// <remarks> /// Parses an Authentication-Results header value from the supplied buffer. /// </remarks> /// <returns><c>true</c> if the authentication results were successfully parsed; otherwise, <c>false</c>.</returns> /// <param name="buffer">The input buffer.</param> /// <param name="authres">The parsed authentication results.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="buffer"/> is <c>null</c>. /// </exception> public static bool TryParse(byte[] buffer, out AuthenticationResults authres) { if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } int index = 0; return(TryParse(buffer, ref index, buffer.Length, false, out authres)); }
internal static ArcSignatureValidationResult GetArcHeaderSets(MimeMessage message, bool throwOnError, out ArcHeaderSet[] sets, out int count) { ArcHeaderSet set; sets = new ArcHeaderSet[50]; count = 0; for (int i = 0; i < message.Headers.Count; i++) { Dictionary <string, string> parameters = null; var header = message.Headers[i]; int instance; string value; switch (header.Id) { case HeaderId.ArcAuthenticationResults: if (!AuthenticationResults.TryParse(header.RawValue, out AuthenticationResults authres)) { if (throwOnError) { throw new FormatException("Invalid ARC-Authentication-Results header."); } return(ArcSignatureValidationResult.Fail); } if (!authres.Instance.HasValue) { if (throwOnError) { throw new FormatException("Missing instance tag in ARC-Authentication-Results header."); } return(ArcSignatureValidationResult.Fail); } instance = authres.Instance.Value; if (instance < 1 || instance > 50) { if (throwOnError) { throw new FormatException(string.Format("Invalid instance tag in ARC-Authentication-Results header: i={0}", instance)); } return(ArcSignatureValidationResult.Fail); } break; case HeaderId.ArcMessageSignature: case HeaderId.ArcSeal: try { parameters = ParseParameterTags(header.Id, header.Value); } catch { if (throwOnError) { throw; } return(ArcSignatureValidationResult.Fail); } if (!parameters.TryGetValue("i", out value)) { if (throwOnError) { throw new FormatException(string.Format("Missing instance tag in {0} header.", header.Id.ToHeaderName())); } return(ArcSignatureValidationResult.Fail); } if (!int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out instance) || instance < 1 || instance > 50) { if (throwOnError) { throw new FormatException(string.Format("Invalid instance tag in {0} header: i={1}", header.Id.ToHeaderName(), value)); } return(ArcSignatureValidationResult.Fail); } break; default: instance = 0; break; } if (instance == 0) { continue; } set = sets[instance - 1]; if (set == null) { sets[instance - 1] = set = new ArcHeaderSet(); } if (!set.Add(header, parameters)) { return(ArcSignatureValidationResult.Fail); } if (instance > count) { count = instance; } } if (count == 0) { // there are no ARC sets return(ArcSignatureValidationResult.None); } // verify that all ARC sets are complete for (int i = 0; i < count; i++) { set = sets[i]; if (set == null) { if (throwOnError) { throw new FormatException(string.Format("Missing ARC headers for i={0}", i + 1)); } return(ArcSignatureValidationResult.Fail); } if (set.ArcAuthenticationResult == null) { if (throwOnError) { throw new FormatException(string.Format("Missing ARC-Authentication-Results header for i={0}", i + 1)); } return(ArcSignatureValidationResult.Fail); } if (set.ArcMessageSignature == null) { if (throwOnError) { throw new FormatException(string.Format("Missing ARC-Message-Signature header for i={0}", i + 1)); } return(ArcSignatureValidationResult.Fail); } if (set.ArcSeal == null) { if (throwOnError) { throw new FormatException(string.Format("Missing ARC-Seal header for i={0}", i + 1)); } return(ArcSignatureValidationResult.Fail); } if (!set.ArcSealParameters.TryGetValue("cv", out string cv)) { if (throwOnError) { throw new FormatException(string.Format("Missing chain validation tag in ARC-Seal header for i={0}.", i + 1)); } return(ArcSignatureValidationResult.Fail); } // The "cv" value for all ARC-Seal header fields MUST NOT be // "fail". For ARC Sets with instance values > 1, the values // MUST be "pass". For the ARC Set with instance value = 1, the // value MUST be "none". if (!cv.Equals(i == 0 ? "none" : "pass", StringComparison.Ordinal)) { return(ArcSignatureValidationResult.Fail); } } return(ArcSignatureValidationResult.Pass); }
static bool TryParse(byte[] text, ref int index, int endIndex, bool throwOnError, out AuthenticationResults authres) { int? instance = null; string srvid = null; string value; bool quoted; authres = null; if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } do { int start = index; if (index >= endIndex || !SkipValue(text, ref index, endIndex, out quoted)) { if (throwOnError) { throw new ParseException(string.Format("Incomplete authserv-id token at offset {0}", start), start, index); } return(false); } value = Encoding.UTF8.GetString(text, start, index - start); if (quoted) { // this can only be the authserv-id token srvid = MimeUtils.Unquote(value); } else { // this could either be the authserv-id or it could be "i=#" (ARC instance) if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (index < endIndex && text[index] == (byte)'=') { // probably i=# if (instance.HasValue) { if (throwOnError) { throw new ParseException(string.Format("Invalid token at offset {0}", start), start, index); } return(false); } if (value != "i") { if (throwOnError) { throw new ParseException(string.Format("Invalid instance token at offset {0}", start), start, index); } return(false); } // skip over '=' index++; if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } start = index; if (!ParseUtils.TryParseInt32(text, ref index, endIndex, out int i)) { if (throwOnError) { throw new ParseException(string.Format("Invalid instance value at offset {0}", start), start, index); } return(false); } instance = i; if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (index >= endIndex) { if (throwOnError) { throw new ParseException(string.Format("Missing semi-colon after instance value at offset {0}", start), start, index); } return(false); } if (text[index] != ';') { if (throwOnError) { throw new ParseException(string.Format("Unexpected token after instance value at offset {0}", index), index, index); } return(false); } // skip over ';' index++; if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } } else { srvid = value; } } } while (srvid == null); authres = new AuthenticationResults(srvid) { Instance = instance }; if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (index >= endIndex) { return(true); } if (text[index] != (byte)';') { // might be the authres-version token int start = index; if (!ParseUtils.TryParseInt32(text, ref index, endIndex, out int version)) { if (throwOnError) { throw new ParseException(string.Format("Invalid authres-version at offset {0}", start), start, index); } return(false); } authres.Version = version; if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (index >= endIndex) { return(true); } if (text[index] != (byte)';') { if (throwOnError) { throw new ParseException(string.Format("Unknown token at offset {0}", index), index, index); } return(false); } } // skip the ';' index++; while (index < endIndex) { if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (index >= endIndex) { break; } int methodIndex = index; // skip the method name if (!SkipKeyword(text, ref index, endIndex)) { if (throwOnError) { throw new ParseException(string.Format("Invalid method token at offset {0}", methodIndex), methodIndex, index); } return(false); } var method = Encoding.ASCII.GetString(text, methodIndex, index - methodIndex); if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (index >= endIndex) { if (method != "none") { if (throwOnError) { throw new ParseException(string.Format("Incomplete methodspec token at offset {0}", methodIndex), methodIndex, index); } return(false); } if (authres.Results.Count > 0) { if (throwOnError) { throw new ParseException(string.Format("Invalid no-result token at offset {0}", methodIndex), methodIndex, index); } return(false); } break; } var resinfo = new AuthenticationMethodResult(method); authres.Results.Add(resinfo); int tokenIndex; if (text[index] == (byte)'/') { // optional method-version token index++; if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } tokenIndex = index; if (!ParseUtils.TryParseInt32(text, ref index, endIndex, out int version)) { if (throwOnError) { throw new ParseException(string.Format("Invalid method-version token at offset {0}", tokenIndex), tokenIndex, index); } return(false); } resinfo.Version = version; if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (index >= endIndex) { if (throwOnError) { throw new ParseException(string.Format("Incomplete methodspec token at offset {0}", methodIndex), methodIndex, index); } return(false); } } if (text[index] != (byte)'=') { if (throwOnError) { throw new ParseException(string.Format("Invalid methodspec token at offset {0}", methodIndex), methodIndex, index); } return(false); } // skip over '=' index++; if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (index >= endIndex) { if (throwOnError) { throw new ParseException(string.Format("Incomplete methodspec token at offset {0}", methodIndex), methodIndex, index); } return(false); } tokenIndex = index; if (!SkipKeyword(text, ref index, endIndex)) { if (throwOnError) { throw new ParseException(string.Format("Invalid result token at offset {0}", tokenIndex), tokenIndex, index); } return(false); } resinfo.Result = Encoding.ASCII.GetString(text, tokenIndex, index - tokenIndex); ParseUtils.SkipWhiteSpace(text, ref index, endIndex); if (index < endIndex && text[index] == '(') { int commentIndex = index; if (!ParseUtils.SkipComment(text, ref index, endIndex)) { if (throwOnError) { throw new ParseException(string.Format("Incomplete comment token at offset {0}", commentIndex), commentIndex, index); } return(false); } commentIndex++; resinfo.ResultComment = Header.Unfold(Encoding.UTF8.GetString(text, commentIndex, (index - 1) - commentIndex)); if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } } if (index >= endIndex) { break; } if (text[index] == (byte)';') { index++; continue; } // optional reasonspec or propspec tokenIndex = index; if (!SkipKeyword(text, ref index, endIndex)) { if (throwOnError) { throw new ParseException(string.Format("Invalid reasonspec or propspec token at offset {0}", tokenIndex), tokenIndex, index); } return(false); } value = Encoding.ASCII.GetString(text, tokenIndex, index - tokenIndex); if (value == "reason") { if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (index >= endIndex) { if (throwOnError) { throw new ParseException(string.Format("Incomplete reasonspec token at offset {0}", tokenIndex), tokenIndex, index); } return(false); } if (text[index] != (byte)'=') { if (throwOnError) { throw new ParseException(string.Format("Invalid reasonspec token at offset {0}", tokenIndex), tokenIndex, index); } return(false); } index++; if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } int reasonIndex = index; if (index >= endIndex || !SkipValue(text, ref index, endIndex, out quoted)) { if (throwOnError) { throw new ParseException(string.Format("Invalid reasonspec value token at offset {0}", reasonIndex), reasonIndex, index); } return(false); } resinfo.Reason = Encoding.UTF8.GetString(text, reasonIndex, index - reasonIndex); if (quoted) { resinfo.Reason = MimeUtils.Unquote(resinfo.Reason); } if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (index >= endIndex) { break; } if (text[index] == (byte)';') { index++; continue; } // optional propspec tokenIndex = index; if (!SkipKeyword(text, ref index, endIndex)) { if (throwOnError) { throw new ParseException(string.Format("Invalid propspec token at offset {0}", tokenIndex), tokenIndex, index); } return(false); } value = Encoding.ASCII.GetString(text, tokenIndex, index - tokenIndex); } do { // value is a propspec ptype token var ptype = value; if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (index >= endIndex) { if (throwOnError) { throw new ParseException(string.Format("Incomplete propspec token at offset {0}", tokenIndex), tokenIndex, index); } return(false); } if (text[index] != (byte)'.') { if (throwOnError) { throw new ParseException(string.Format("Invalid propspec token at offset {0}", tokenIndex), tokenIndex, index); } return(false); } index++; if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (index >= endIndex) { if (throwOnError) { throw new ParseException(string.Format("Incomplete propspec token at offset {0}", tokenIndex), tokenIndex, index); } return(false); } int propertyIndex = index; if (!SkipKeyword(text, ref index, endIndex)) { if (throwOnError) { throw new ParseException(string.Format("Invalid property token at offset {0}", propertyIndex), propertyIndex, index); } return(false); } var property = Encoding.ASCII.GetString(text, propertyIndex, index - propertyIndex); if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (index >= endIndex) { if (throwOnError) { throw new ParseException(string.Format("Incomplete propspec token at offset {0}", tokenIndex), tokenIndex, index); } return(false); } if (text[index] != (byte)'=') { if (throwOnError) { throw new ParseException(string.Format("Invalid propspec token at offset {0}", tokenIndex), tokenIndex, index); } return(false); } index++; if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (index >= endIndex) { if (throwOnError) { throw new ParseException(string.Format("Incomplete propspec token at offset {0}", tokenIndex), tokenIndex, index); } return(false); } int valueIndex = index; while (index < endIndex && text[index] != ';' && !text[index].IsWhitespace()) { index++; } if (index == valueIndex) { if (throwOnError) { throw new ParseException(string.Format("Incomplete propspec token at offset {0}", tokenIndex), tokenIndex, index); } return(false); } value = Encoding.UTF8.GetString(text, valueIndex, index - valueIndex); var propspec = new AuthenticationMethodProperty(ptype, property, value); resinfo.Properties.Add(propspec); if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (index >= endIndex || text[index] == (byte)';') { break; } tokenIndex = index; if (!SkipKeyword(text, ref index, endIndex)) { if (throwOnError) { throw new ParseException(string.Format("Invalid propspec token at offset {0}", tokenIndex), tokenIndex, index); } return(false); } value = Encoding.ASCII.GetString(text, tokenIndex, index - tokenIndex); } while (true); // skip over ';' index++; } return(true); }