public DigestResponse(DigestChallenge challenge, string protocol, string hostName, string authzid, string userName, string password, string cnonce) { UserName = userName; if (challenge.Realms != null && challenge.Realms.Length > 0) { Realm = challenge.Realms[0]; } else { Realm = string.Empty; } Nonce = challenge.Nonce; CNonce = cnonce; Nc = 1; // FIXME: make sure this is supported Qop = "auth"; DigestUri = string.Format("{0}/{1}", protocol, hostName); if (!string.IsNullOrEmpty(challenge.Charset)) { Charset = challenge.Charset; } Algorithm = challenge.Algorithm; AuthZid = authzid; Cipher = null; Response = ComputeHash(password, true); }
/// <summary> /// Parse the server's challenge token and return the next challenge response. /// </summary> /// <remarks> /// Parses the server's challenge token and returns the next challenge response. /// </remarks> /// <returns>The next challenge response.</returns> /// <param name="token">The server's challenge token.</param> /// <param name="startIndex">The index into the token specifying where the server's challenge begins.</param> /// <param name="length">The length of the server's challenge.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.NotSupportedException"> /// The SASL mechanism does not support SASL-IR. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="SaslException"> /// An error has occurred while parsing the server's challenge token. /// </exception> protected override byte[] Challenge(byte[] token, int startIndex, int length, CancellationToken cancellationToken) { if (token == null) { throw new NotSupportedException("DIGEST-MD5 does not support SASL-IR."); } if (IsAuthenticated) { return(null); } switch (state) { case LoginState.Auth: if (token.Length > 2048) { throw new SaslException(MechanismName, SaslErrorCode.ChallengeTooLong, "Server challenge too long."); } challenge = DigestChallenge.Parse(Encoding.UTF8.GetString(token, startIndex, length)); encoding = challenge.Charset != null ? Encoding.UTF8 : TextEncodings.Latin1; cnonce = cnonce ?? GenerateEntropy(15); response = new DigestResponse(challenge, encoding, Uri.Scheme, Uri.DnsSafeHost, AuthorizationId, Credentials.UserName, Credentials.Password, cnonce); state = LoginState.Final; return(response.Encode(encoding)); case LoginState.Final: if (token.Length == 0) { throw new SaslException(MechanismName, SaslErrorCode.MissingChallenge, "Server response did not contain any authentication data."); } var text = encoding.GetString(token, startIndex, length); string key, value; if (!DigestChallenge.TryParseKeyValuePair(text, out key, out value)) { throw new SaslException(MechanismName, SaslErrorCode.IncompleteChallenge, "Server response contained incomplete authentication data."); } if (!key.Equals("rspauth", StringComparison.OrdinalIgnoreCase)) { throw new SaslException(MechanismName, SaslErrorCode.InvalidChallenge, "Server response contained invalid data."); } var expected = response.ComputeHash(encoding, Credentials.Password, false); if (value != expected) { throw new SaslException(MechanismName, SaslErrorCode.IncorrectHash, "Server response did not contain the expected hash."); } IsAuthenticated = true; break; } return(null); }
/// <summary> /// Resets the state of the SASL mechanism. /// </summary> public override void Reset() { state = LoginState.Auth; challenge = null; response = null; base.Reset(); }
/// <summary> /// Parses the server's challenge token and returns the next challenge response. /// </summary> /// <returns>The next challenge response.</returns> /// <param name="token">The server's challenge token.</param> /// <param name="startIndex">The index into the token specifying where the server's challenge begins.</param> /// <param name="length">The length of the server's challenge.</param> /// <exception cref="SaslException"> /// An error has occurred while parsing the server's challenge token. /// </exception> protected override byte[] Challenge(byte[] token, int startIndex, int length) { if (IsAuthenticated) { throw new InvalidOperationException(); } if (token == null) { return(null); } var cred = Credentials.GetCredential(Uri, MechanismName); switch (state) { case LoginState.Auth: if (token.Length > 2048) { throw new SaslException(MechanismName, SaslErrorCode.ChallengeTooLong, "Server challenge too long."); } challenge = DigestChallenge.Parse(Encoding.UTF8.GetString(token)); response = new DigestResponse(challenge, Uri.Scheme, Uri.DnsSafeHost, cred.UserName, cred.Password); state = LoginState.Final; return(response.Encode()); case LoginState.Final: if (token.Length == 0) { throw new SaslException(MechanismName, SaslErrorCode.MissingChallenge, "Server response did not contain any authentication data."); } var text = Encoding.UTF8.GetString(token); string key, value; int index = 0; if (!DigestChallenge.TryParseKeyValuePair(text, ref index, out key, out value)) { throw new SaslException(MechanismName, SaslErrorCode.IncompleteChallenge, "Server response contained incomplete authentication data."); } var expected = response.ComputeHash(cred.Password, false); if (value != expected) { throw new SaslException(MechanismName, SaslErrorCode.IncorrectHash, "Server response did not contain the expected hash."); } IsAuthenticated = true; return(new byte[0]); default: throw new ArgumentOutOfRangeException(); } }
public DigestResponse(DigestChallenge challenge, string protocol, string hostName, string userName, string password) { var cnonce = new byte[15]; new Random().NextBytes(cnonce); UserName = userName; if (challenge.Realms.Length > 0) { Realm = challenge.Realms[0]; } else { Realm = string.Empty; } Nonce = challenge.Nonce; CNonce = Convert.ToBase64String(cnonce); Nc = 1; // FIXME: make sure this is supported Qop = "auth"; DigestUri = string.Format("{0}://{1}", protocol, hostName); if (!string.IsNullOrEmpty(challenge.Charset)) { Charset = challenge.Charset; } Algorithm = challenge.Algorithm; AuthZid = null; Cipher = null; Response = ComputeHash(password, true); }
public DigestResponse (DigestChallenge challenge, string protocol, string hostName, string userName, string password, string cnonce) { UserName = userName; if (challenge.Realms != null && challenge.Realms.Length > 0) Realm = challenge.Realms[0]; else Realm = string.Empty; Nonce = challenge.Nonce; CNonce = cnonce; Nc = 1; // FIXME: make sure this is supported Qop = "auth"; DigestUri = string.Format ("{0}/{1}", protocol, hostName); if (!string.IsNullOrEmpty (challenge.Charset)) Charset = challenge.Charset; Algorithm = challenge.Algorithm; AuthZid = null; Cipher = null; Response = ComputeHash (password, true); }
public static DigestChallenge Parse (string token) { var challenge = new DigestChallenge (); int index = 0; while (index < token.Length) { string key, value; if (!TryParseKeyValuePair (token, ref index, out key, out value)) throw new SaslException ("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format ("Invalid SASL challenge from the server: {0}", token)); switch (key.ToLowerInvariant ()) { case "realm": challenge.Realms = value.Split (new [] { ',' }, StringSplitOptions.RemoveEmptyEntries); break; case "nonce": challenge.Nonce = value; break; case "qop": foreach (var qop in value.Split (new [] { ',' }, StringSplitOptions.RemoveEmptyEntries)) challenge.Qop.Add (qop.Trim ()); break; case "stale": challenge.Stale = value.ToLowerInvariant () == "true"; break; case "maxbuf": challenge.MaxBuf = int.Parse (value); break; case "charset": challenge.Charset = value; break; case "algorithm": challenge.Algorithm = value; break; case "cipher": foreach (var cipher in value.Split (new [] { ',' }, StringSplitOptions.RemoveEmptyEntries)) challenge.Ciphers.Add (cipher.Trim ()); break; } SkipWhiteSpace (token, ref index); if (index < token.Length && token[index] == ',') index++; } return challenge; }
/// <summary> /// Resets the state of the SASL mechanism. /// </summary> /// <remarks> /// Resets the state of the SASL mechanism. /// </remarks> public override void Reset () { state = LoginState.Auth; challenge = null; response = null; cnonce = null; base.Reset (); }
/// <summary> /// Parses the server's challenge token and returns the next challenge response. /// </summary> /// <remarks> /// Parses the server's challenge token and returns the next challenge response. /// </remarks> /// <returns>The next challenge response.</returns> /// <param name="token">The server's challenge token.</param> /// <param name="startIndex">The index into the token specifying where the server's challenge begins.</param> /// <param name="length">The length of the server's challenge.</param> /// <exception cref="System.InvalidOperationException"> /// The SASL mechanism is already authenticated. /// </exception> /// <exception cref="System.NotSupportedException"> /// THe SASL mechanism does not support SASL-IR. /// </exception> /// <exception cref="SaslException"> /// An error has occurred while parsing the server's challenge token. /// </exception> protected override byte[] Challenge (byte[] token, int startIndex, int length) { if (IsAuthenticated) throw new InvalidOperationException (); if (token == null) throw new NotSupportedException ("DIGEST-MD5 does not support SASL-IR."); var cred = Credentials.GetCredential (Uri, MechanismName); switch (state) { case LoginState.Auth: if (token.Length > 2048) throw new SaslException (MechanismName, SaslErrorCode.ChallengeTooLong, "Server challenge too long."); challenge = DigestChallenge.Parse (Encoding.UTF8.GetString (token, startIndex, length)); if (string.IsNullOrEmpty (cnonce)) { var entropy = new byte[15]; using (var rng = RandomNumberGenerator.Create ()) rng.GetBytes (entropy); cnonce = Convert.ToBase64String (entropy); } response = new DigestResponse (challenge, Uri.Scheme, Uri.DnsSafeHost, cred.UserName, cred.Password, cnonce); state = LoginState.Final; return response.Encode (); case LoginState.Final: if (token.Length == 0) throw new SaslException (MechanismName, SaslErrorCode.MissingChallenge, "Server response did not contain any authentication data."); var text = Encoding.UTF8.GetString (token, startIndex, length); string key, value; int index = 0; if (!DigestChallenge.TryParseKeyValuePair (text, ref index, out key, out value)) throw new SaslException (MechanismName, SaslErrorCode.IncompleteChallenge, "Server response contained incomplete authentication data."); var expected = response.ComputeHash (cred.Password, false); if (value != expected) throw new SaslException (MechanismName, SaslErrorCode.IncorrectHash, "Server response did not contain the expected hash."); IsAuthenticated = true; return new byte[0]; default: throw new IndexOutOfRangeException ("state"); } }
public static DigestChallenge Parse(string token) { var challenge = new DigestChallenge(); int index = 0; while (index < token.Length) { string key, value; if (!TryParseKeyValuePair(token, ref index, out key, out value)) { throw new SaslException("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format("Invalid SASL challenge from the server: {0}", token)); } switch (key.ToLowerInvariant()) { case "realm": challenge.Realms = value.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries); break; case "nonce": challenge.Nonce = value; break; case "qop": foreach (var qop in value.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { challenge.Qop.Add(qop.Trim()); } break; case "stale": challenge.Stale = value.ToLowerInvariant() == "true"; break; case "maxbuf": challenge.MaxBuf = int.Parse(value); break; case "charset": challenge.Charset = value; break; case "algorithm": challenge.Algorithm = value; break; case "cipher": foreach (var cipher in value.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { challenge.Ciphers.Add(cipher.Trim()); } break; } SkipWhiteSpace(token, ref index); if (index < token.Length && token[index] == ',') { index++; } } return(challenge); }
/// <summary> /// Parses the server's challenge token and returns the next challenge response. /// </summary> /// <remarks> /// Parses the server's challenge token and returns the next challenge response. /// </remarks> /// <returns>The next challenge response.</returns> /// <param name="token">The server's challenge token.</param> /// <param name="startIndex">The index into the token specifying where the server's challenge begins.</param> /// <param name="length">The length of the server's challenge.</param> /// <exception cref="System.InvalidOperationException"> /// The SASL mechanism is already authenticated. /// </exception> /// <exception cref="System.NotSupportedException"> /// THe SASL mechanism does not support SASL-IR. /// </exception> /// <exception cref="SaslException"> /// An error has occurred while parsing the server's challenge token. /// </exception> protected override byte[] Challenge(byte[] token, int startIndex, int length) { if (IsAuthenticated) { throw new InvalidOperationException(); } if (token == null) { throw new NotSupportedException("DIGEST-MD5 does not support SASL-IR."); } switch (state) { case LoginState.Auth: if (token.Length > 2048) { throw new SaslException(MechanismName, SaslErrorCode.ChallengeTooLong, "Server challenge too long."); } challenge = DigestChallenge.Parse(Encoding.UTF8.GetString(token, startIndex, length)); if (string.IsNullOrEmpty(cnonce)) { var entropy = new byte[15]; using (var rng = RandomNumberGenerator.Create()) rng.GetBytes(entropy); cnonce = Convert.ToBase64String(entropy); } response = new DigestResponse(challenge, Uri.Scheme, Uri.DnsSafeHost, AuthorizationId, Credentials.UserName, Credentials.Password, cnonce); state = LoginState.Final; return(response.Encode()); case LoginState.Final: if (token.Length == 0) { throw new SaslException(MechanismName, SaslErrorCode.MissingChallenge, "Server response did not contain any authentication data."); } var text = Encoding.UTF8.GetString(token, startIndex, length); string key, value; int index = 0; if (!DigestChallenge.TryParseKeyValuePair(text, ref index, out key, out value)) { throw new SaslException(MechanismName, SaslErrorCode.IncompleteChallenge, "Server response contained incomplete authentication data."); } var expected = response.ComputeHash(Credentials.Password, false); if (value != expected) { throw new SaslException(MechanismName, SaslErrorCode.IncorrectHash, "Server response did not contain the expected hash."); } IsAuthenticated = true; return(new byte[0]); default: throw new IndexOutOfRangeException("state"); } }
public DigestResponse(DigestChallenge challenge, string protocol, string hostName, string userName, string password) { var cnonce = new byte[15]; new Random ().NextBytes (cnonce); UserName = userName; if (challenge.Realms.Length > 0) Realm = challenge.Realms[0]; else Realm = string.Empty; Nonce = challenge.Nonce; CNonce = Convert.ToBase64String (cnonce); Nc = 1; // FIXME: make sure this is supported Qop = "auth"; DigestUri = string.Format ("{0}://{1}", protocol, hostName); if (!string.IsNullOrEmpty (challenge.Charset)) Charset = challenge.Charset; Algorithm = challenge.Algorithm; AuthZid = null; Cipher = null; Response = ComputeHash (password, true); }
/// <summary> /// Parses the server's challenge token and returns the next challenge response. /// </summary> /// <returns>The next challenge response.</returns> /// <param name="token">The server's challenge token.</param> /// <param name="startIndex">The index into the token specifying where the server's challenge begins.</param> /// <param name="length">The length of the server's challenge.</param> /// <exception cref="SaslException"> /// An error has occurred while parsing the server's challenge token. /// </exception> protected override byte[] Challenge(byte[] token, int startIndex, int length) { if (IsAuthenticated) throw new InvalidOperationException (); if (token == null) return null; var cred = Credentials.GetCredential (Uri, MechanismName); switch (state) { case LoginState.Auth: if (token.Length > 2048) throw new SaslException (MechanismName, SaslErrorCode.ChallengeTooLong, "Server challenge too long."); challenge = DigestChallenge.Parse (Encoding.UTF8.GetString (token)); response = new DigestResponse (challenge, Uri.Scheme, Uri.DnsSafeHost, cred.UserName, cred.Password); state = LoginState.Final; return response.Encode (); case LoginState.Final: if (token.Length == 0) throw new SaslException (MechanismName, SaslErrorCode.MissingChallenge, "Server response did not contain any authentication data."); var text = Encoding.UTF8.GetString (token); string key, value; int index = 0; if (!DigestChallenge.TryParseKeyValuePair (text, ref index, out key, out value)) throw new SaslException (MechanismName, SaslErrorCode.IncompleteChallenge, "Server response contained incomplete authentication data."); var expected = response.ComputeHash (cred.Password, false); if (value != expected) throw new SaslException (MechanismName, SaslErrorCode.IncorrectHash, "Server response did not contain the expected hash."); IsAuthenticated = true; return new byte[0]; default: throw new ArgumentOutOfRangeException (); } }
public static DigestChallenge Parse(string token) { var challenge = new DigestChallenge(); int index = 0; int maxbuf; SkipWhiteSpace(token, ref index); while (index < token.Length) { string key, value; if (!TryParseKeyValuePair(token, ref index, out key, out value)) { throw new SaslException("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format("Invalid SASL challenge from the server: {0}", token)); } switch (key.ToLowerInvariant()) { case "realm": challenge.Realms = value.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries); break; case "nonce": if (challenge.Nonce != null) { throw new SaslException("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format("Invalid SASL challenge from the server: {0}", token)); } challenge.Nonce = value; break; case "qop": foreach (var qop in value.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { challenge.Qop.Add(qop.Trim()); } break; case "stale": if (challenge.Stale.HasValue) { throw new SaslException("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format("Invalid SASL challenge from the server: {0}", token)); } challenge.Stale = value.ToLowerInvariant() == "true"; break; case "maxbuf": if (challenge.MaxBuf.HasValue || !int.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out maxbuf)) { throw new SaslException("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format("Invalid SASL challenge from the server: {0}", token)); } challenge.MaxBuf = maxbuf; break; case "charset": if (challenge.Charset != null || !value.Equals("utf-8", StringComparison.OrdinalIgnoreCase)) { throw new SaslException("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format("Invalid SASL challenge from the server: {0}", token)); } challenge.Charset = "utf-8"; break; case "algorithm": if (challenge.Algorithm != null) { throw new SaslException("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format("Invalid SASL challenge from the server: {0}", token)); } challenge.Algorithm = value; break; case "cipher": if (challenge.Ciphers.Count > 0) { throw new SaslException("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format("Invalid SASL challenge from the server: {0}", token)); } foreach (var cipher in value.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { challenge.Ciphers.Add(cipher.Trim()); } break; } SkipWhiteSpace(token, ref index); if (index < token.Length && token[index] == ',') { index++; SkipWhiteSpace(token, ref index); } } return(challenge); }