/// <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) { credential = options.CredentialsCallback(artifacts.Id); if (credential != null && credential.IsValid) { var normalizedRequest = new NormalizedRequest(request, artifacts, options.HostNameSource); 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! 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(); } } } } } } 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) { credential = options.CredentialsCallback(artifacts.Id); if (credential != null && credential.IsValid) { var normalizedRequest = new NormalizedRequest(request, artifacts); 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! 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(); } } } } } } return new AuthenticationResult() { IsAuthentic = false }; }
/// <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). }
/// <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 bewit query string parameter. /// </summary> /// <param name="bewit">Value of the query string parameter with the name of 'bewit'.</param> /// <param name="now">Date and time in UTC to be used as the base for computing bewit life.</param> /// <param name="request">Request object.</param> /// <param name="options">Hawk authentication options</param> internal static AuthenticationResult Authenticate(string bewit, ulong now, IRequestMessage request, Options options) { if (!String.IsNullOrWhiteSpace(bewit)) { if (request.Method == HttpMethod.Get) { if (options != null && options.CredentialsCallback != null) { var parts = bewit.ToUtf8StringFromBase64Url().Split('\\'); if (parts.Length == 4) { ulong timestamp = 0; if (UInt64.TryParse(parts[1], out timestamp) && timestamp * 1000 > now) { string id = parts[0]; string mac = parts[2]; string ext = parts[3]; if (!String.IsNullOrWhiteSpace(id) && !String.IsNullOrWhiteSpace(mac)) { RemoveBewitFromUri(request); Credential credential = options.CredentialsCallback(id); if (credential != null && credential.IsValid) { var artifacts = new ArtifactsContainer() { Id = id, Nonce = String.Empty, Timestamp = timestamp, Mac = mac.ToBytesFromBase64(), ApplicationSpecificData = ext ?? String.Empty }; var normalizedRequest = new NormalizedRequest(request, artifacts) { IsBewit = true }; var crypto = new Cryptographer(normalizedRequest, artifacts, credential); if (crypto.IsSignatureValid()) // Bewit is for GET and GET must have no request body { return new AuthenticationResult() { IsAuthentic = true, Credential = credential, Artifacts = artifacts, ApplicationSpecificData = ext }; } } } } } } } } return new AuthenticationResult() { IsAuthentic = false }; }
/// <summary> /// Returns an AuthenticationResult object corresponding to the result of authentication done /// using the client supplied artifacts in the bewit query string parameter. /// </summary> /// <param name="bewit">Value of the query string parameter with the name of 'bewit'.</param> /// <param name="now">Date and time in UTC to be used as the base for computing bewit life.</param> /// <param name="request">Request object.</param> /// <param name="options">Hawk authentication options</param> internal static AuthenticationResult Authenticate(string bewit, ulong now, IRequestMessage request, Options options) { if (!String.IsNullOrWhiteSpace(bewit)) { if (request.Method == HttpMethod.Get) { if (options != null && options.CredentialsCallback != null) { var parts = bewit.ToUtf8StringFromBase64Url().Split('\\'); if (parts.Length == 4) { ulong timestamp = 0; if (UInt64.TryParse(parts[1], out timestamp) && timestamp * 1000 > now) { string id = parts[0]; string mac = parts[2]; string ext = parts[3]; if (!String.IsNullOrWhiteSpace(id) && !String.IsNullOrWhiteSpace(mac)) { RemoveBewitFromUri(request); Credential credential = options.CredentialsCallback(id); if (credential != null && credential.IsValid) { var artifacts = new ArtifactsContainer() { Id = id, Nonce = String.Empty, Timestamp = timestamp, Mac = mac.ToBytesFromBase64(), ApplicationSpecificData = ext ?? String.Empty }; var normalizedRequest = new NormalizedRequest(request, artifacts) { IsBewit = true }; var crypto = new Cryptographer(normalizedRequest, artifacts, credential); if (crypto.IsSignatureValid()) // Bewit is for GET and GET must have no request body { return(new AuthenticationResult() { IsAuthentic = true, Credential = credential, Artifacts = artifacts, ApplicationSpecificData = ext }); } } } } } } } } 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 }); }