/// <summary>
        /// Returns the name of the response header (WWW-Authenticate or Server-Authorization) and the corresponding
        /// value, respectively for an unauthorized and a successful request.
        /// </summary>
        public async Task <Tuple <string, string> > CreateServerAuthorizationAsync(IResponseMessage response)
        {
            if (response.StatusCode == HttpStatusCode.Unauthorized)
            {
                string challenge   = String.Format(" {0}", request.ChallengeParameter ?? String.Empty);
                string headerValue = HawkConstants.Scheme + challenge.TrimEnd(' ');

                return(new Tuple <string, string>(HawkConstants.WwwAuthenticateHeaderName, headerValue));
            }
            else
            {
                // No Server-Authorization header for the following:
                // (1) There is no result or failed authentication.
                // (2) The credential is a bewit.
                // (3) The server is configured to not send the header.
                bool createHeader = this.result != null &&
                                    this.result.IsAuthentic &&
                                    (!this.IsBewitRequest) &&
                                    options.EnableServerAuthorization;

                if (createHeader)
                {
                    if (options.NormalizationCallback != null)
                    {
                        this.result.Artifacts.ApplicationSpecificData = options.NormalizationCallback(response);
                    }

                    // Sign the response
                    Tuple <string, string> hostAndPort = options.DetermineHostDetailsCallback(request);
                    var normalizedRequest = new NormalizedRequest(request, this.result.Artifacts, hostAndPort.Item1, hostAndPort.Item2)
                    {
                        IsServerAuthorization = true
                    };
                    var crypto = new Cryptographer(normalizedRequest, this.result.Artifacts, this.result.Credential);

                    // Response body is needed only if payload hash must be included in the response MAC.
                    string body = null;
                    if (options.ResponsePayloadHashabilityCallback != null &&
                        options.ResponsePayloadHashabilityCallback(this.request))
                    {
                        body = await response.ReadBodyAsStringAsync();
                    }

                    crypto.Sign(body, response.ContentType);

                    string authorization = this.result.Artifacts.ToServerAuthorizationHeaderParameter();

                    if (!String.IsNullOrWhiteSpace(authorization))
                    {
                        return(new Tuple <string, string>(HawkConstants.ServerAuthorizationHeaderName,
                                                          String.Format("{0} {1}", HawkConstants.Scheme, authorization)));
                    }
                }
            }

            return(null);
        }
        /// <summary>
        /// Returns an AuthenticationResult object corresponding to the result of authentication done
        /// using the client supplied artifacts in the HTTP authorization header in hawk scheme.
        /// </summary>
        /// <param name="now">Current UNIX time in milliseconds.</param>
        /// <param name="request">Request object.</param>
        /// <param name="options">Hawk authentication options</param>
        /// <returns></returns>
        internal static async Task<AuthenticationResult> AuthenticateAsync(ulong now, IRequestMessage request, Options options)
        {
            ArtifactsContainer artifacts = null;
            Credential credential = null;

            if (request.HasValidHawkScheme())
            {
                if (ArtifactsContainer.TryParse(request.Authorization.Parameter, out artifacts))
                {
                    if (artifacts != null && artifacts.AreClientArtifactsValid)
                    {
                        string lastUsedBy = options.DetermineNonceReplayCallback(artifacts.Nonce);

                        if (String.IsNullOrEmpty(lastUsedBy)) // Not an old nonce, and hence not a replay.
                        {
                            credential = options.CredentialsCallback(artifacts.Id);
                            if (credential != null && credential.IsValid)
                            {
                                HawkEventSource.Log.Debug(
                                    String.Format("Algorithm={0} Key={1} ID={2}",
                                                        credential.Algorithm.ToString(),
                                                        Convert.ToBase64String(credential.Key),
                                                        credential.Id));

                                Tuple<string, string> hostAndPort = options.DetermineHostDetailsCallback(request);
                                var normalizedRequest = new NormalizedRequest(request, artifacts, hostAndPort.Item1, hostAndPort.Item2);
                                var crypto = new Cryptographer(normalizedRequest, artifacts, credential);

                                // Request body is needed only when payload hash is present in the request
                                string body = null;
                                if (artifacts.PayloadHash != null && artifacts.PayloadHash.Length > 0)
                                {
                                    body = await request.ReadBodyAsStringAsync();
                                }

                                if (crypto.IsSignatureValid(body, request.ContentType)) // MAC and hash checks
                                {
                                    if (IsTimestampFresh(now, artifacts, options))
                                    {
                                        // If you get this far, you are authentic. Welcome and thanks for flying Hawk!

                                        // Before returning the result, store nonce to detect replays.
                                        options.StoreNonceCallback(artifacts.Nonce, credential.Id, options.ClockSkewSeconds);

                                        return new AuthenticationResult()
                                        {
                                            IsAuthentic = true,
                                            Artifacts = artifacts,
                                            Credential = credential,
                                            ApplicationSpecificData = artifacts.ApplicationSpecificData
                                        };
                                    }
                                    else
                                    {
                                        // Authentic but for the timestamp freshness.
                                        // Give a chance to the client to correct the clocks skew.
                                        var timestamp = new NormalizedTimestamp(DateTime.UtcNow, credential, options.LocalTimeOffsetMillis);
                                        request.ChallengeParameter = timestamp.ToWwwAuthenticateHeaderParameter();
                                    }
                                }
                            }
                        }
                        else
                        {
                            HawkEventSource.Log.NonceReplay(artifacts.Nonce, lastUsedBy);
                        }
                    }
                }
            }

            return new AuthenticationResult() { IsAuthentic = false };
        }
        /// <summary>
        /// Returns an AuthenticationResult object corresponding to the result of authentication done
        /// using the client supplied artifacts in the HTTP authorization header in hawk scheme.
        /// </summary>
        /// <param name="now">Current UNIX time in milliseconds.</param>
        /// <param name="request">Request object.</param>
        /// <param name="options">Hawk authentication options</param>
        /// <returns></returns>
        internal static async Task <AuthenticationResult> AuthenticateAsync(ulong now, IRequestMessage request, Options options)
        {
            ArtifactsContainer artifacts  = null;
            Credential         credential = null;

            if (request.HasValidHawkScheme())
            {
                if (ArtifactsContainer.TryParse(request.Authorization.Parameter, out artifacts))
                {
                    if (artifacts != null && artifacts.AreClientArtifactsValid)
                    {
                        string lastUsedBy = options.DetermineNonceReplayCallback(artifacts.Nonce);

                        if (String.IsNullOrEmpty(lastUsedBy)) // Not an old nonce, and hence not a replay.
                        {
                            credential = options.CredentialsCallback(artifacts.Id);
                            if (credential != null && credential.IsValid)
                            {
                                HawkEventSource.Log.Debug(
                                    String.Format("Algorithm={0} Key={1} ID={2}",
                                                  credential.Algorithm.ToString(),
                                                  Convert.ToBase64String(credential.Key),
                                                  credential.Id));

                                Tuple <string, string> hostAndPort = options.DetermineHostDetailsCallback(request);
                                var normalizedRequest = new NormalizedRequest(request, artifacts, hostAndPort.Item1, hostAndPort.Item2);
                                var crypto            = new Cryptographer(normalizedRequest, artifacts, credential);

                                // Request body is needed only when payload hash is present in the request
                                string body = null;
                                if (artifacts.PayloadHash != null && artifacts.PayloadHash.Length > 0)
                                {
                                    body = await request.ReadBodyAsStringAsync();
                                }

                                if (crypto.IsSignatureValid(body, request.ContentType)) // MAC and hash checks
                                {
                                    if (IsTimestampFresh(now, artifacts, options))
                                    {
                                        // If you get this far, you are authentic. Welcome and thanks for flying Hawk!

                                        // Before returning the result, store nonce to detect replays.
                                        options.StoreNonceCallback(artifacts.Nonce, credential.Id, options.ClockSkewSeconds);

                                        return(new AuthenticationResult()
                                        {
                                            IsAuthentic = true,
                                            Artifacts = artifacts,
                                            Credential = credential,
                                            ApplicationSpecificData = artifacts.ApplicationSpecificData
                                        });
                                    }
                                    else
                                    {
                                        // Authentic but for the timestamp freshness.
                                        // Give a chance to the client to correct the clocks skew.
                                        var timestamp = new NormalizedTimestamp(DateTime.UtcNow, credential, options.LocalTimeOffsetMillis);
                                        request.ChallengeParameter = timestamp.ToWwwAuthenticateHeaderParameter();
                                    }
                                }
                            }
                        }
                        else
                        {
                            HawkEventSource.Log.NonceReplay(artifacts.Nonce, lastUsedBy);
                        }
                    }
                }
            }

            return(new AuthenticationResult()
            {
                IsAuthentic = false
            });
        }