/// <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 true if the server response HMAC cannot be validated, indicating possible tampering.
        /// </summary>
        private async Task <bool> IsResponseTamperedAsync(ArtifactsContainer artifacts, Cryptographer crypto,
                                                          IResponseMessage response)
        {
            if (response.Headers.ContainsKey(HawkConstants.ServerAuthorizationHeaderName))
            {
                string header = response.Headers[HawkConstants.ServerAuthorizationHeaderName].FirstOrDefault();

                if (!String.IsNullOrWhiteSpace(header) &&
                    header.Substring(0, HawkConstants.Scheme.Length).ToLower() == HawkConstants.Scheme)
                {
                    ArtifactsContainer serverAuthorizationArtifacts;
                    if (ArtifactsContainer.TryParse(header.Substring(HawkConstants.Scheme.Length + " ".Length),
                                                    out serverAuthorizationArtifacts))
                    {
                        // To validate response, ext, hash, and mac in the request artifacts must be
                        // replaced with the ones from the server.
                        artifacts.ApplicationSpecificData = serverAuthorizationArtifacts.ApplicationSpecificData;
                        artifacts.PayloadHash             = serverAuthorizationArtifacts.PayloadHash;
                        artifacts.Mac = serverAuthorizationArtifacts.Mac;

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

                        bool isValid = crypto.IsSignatureValid(body, response.ContentType, isServerAuthorization: true);

                        if (isValid)
                        {
                            string appSpecificData = serverAuthorizationArtifacts.ApplicationSpecificData;

                            isValid = options.VerificationCallback == null ||
                                      options.VerificationCallback(response, appSpecificData);
                        }

                        return(!isValid);
                    }
                }
            }

            return(true); // Missing header means possible tampered response (to err on the side of caution).
        }
        /// <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
                    var normalizedRequest = new NormalizedRequest(request, this.result.Artifacts);
                    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 true if the server response HMAC cannot be validated, indicating possible tampering.
        /// </summary>
        private async Task<bool> IsResponseTamperedAsync(ArtifactsContainer artifacts, Cryptographer crypto,
                                                        IResponseMessage response)
        {
            if (response.Headers.ContainsKey(HawkConstants.ServerAuthorizationHeaderName))
            {
                string header = response.Headers[HawkConstants.ServerAuthorizationHeaderName].FirstOrDefault();

                if (!String.IsNullOrWhiteSpace(header) &&
                                    header.Substring(0, HawkConstants.Scheme.Length).ToLower() == HawkConstants.Scheme)
                {
                    ArtifactsContainer serverAuthorizationArtifacts;
                    if (ArtifactsContainer.TryParse(header.Substring(HawkConstants.Scheme.Length + " ".Length),
                                                                                    out serverAuthorizationArtifacts))
                    {
                        // To validate response, ext, hash, and mac in the request artifacts must be
                        // replaced with the ones from the server.
                        artifacts.ApplicationSpecificData = serverAuthorizationArtifacts.ApplicationSpecificData;
                        artifacts.PayloadHash = serverAuthorizationArtifacts.PayloadHash;
                        artifacts.Mac = serverAuthorizationArtifacts.Mac;

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

                        bool isValid = crypto.IsSignatureValid(body, response.ContentType);

                        if (isValid)
                        {
                            string appSpecificData = serverAuthorizationArtifacts.ApplicationSpecificData;

                            isValid = options.VerificationCallback == null ||
                                                    options.VerificationCallback(response, appSpecificData);
                        }

                        return !isValid;
                    }
                }
            }

            return true; // Missing header means possible tampered response (to err on the side of caution).
        }