示例#1
0
        /// <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);
        }
示例#2
0
        /// <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();
            }
        }
        /// <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");
            }
        }