/// <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). }