/// <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 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 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="callback">The callback function that returns a Credential object corresponding to the identifier passed in.</param> /// <returns></returns> internal static async Task <AuthenticationResult> AuthenticateAsync(ulong now, HttpRequestMessage request, Func <string, Credential> callback) { ArtifactsContainer artifacts = null; Credential credential = null; if (request.HasValidHawkScheme()) { if (ArtifactsContainer.TryParse(request.Headers.Authorization.Parameter, out artifacts)) { if (artifacts != null && artifacts.AreClientArtifactsValid) { credential = callback(artifacts.Id); if (credential != null && credential.IsValid) { var normalizedRequest = new NormalizedRequest(request, artifacts); var crypto = new Cryptographer(normalizedRequest, artifacts, credential); if (await crypto.IsSignatureValidAsync(request.Content)) // MAC and hash checks { if (IsTimestampFresh(now, artifacts)) { // 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); request.PutChallengeParameter(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="callback">The callback function that returns a Credential object corresponding to the identifier passed in.</param> /// <returns></returns> internal static async Task<AuthenticationResult> AuthenticateAsync(ulong now, HttpRequestMessage request, Func<string, Credential> callback) { ArtifactsContainer artifacts = null; Credential credential = null; if (request.HasValidHawkScheme()) { if (ArtifactsContainer.TryParse(request.Headers.Authorization.Parameter, out artifacts)) { if (artifacts != null && artifacts.AreClientArtifactsValid) { credential = callback(artifacts.Id); if (credential != null && credential.IsValid) { var normalizedRequest = new NormalizedRequest(request, artifacts); var crypto = new Cryptographer(normalizedRequest, artifacts, credential); if (await crypto.IsSignatureValidAsync(request.Content)) // MAC and hash checks { if (IsTimestampFresh(now, artifacts)) { // 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); request.PutChallengeParameter(timestamp.ToWwwAuthenticateHeaderParameter()); } } } } } } return new AuthenticationResult() { IsAuthentic = false }; }
public async Task TimestampInSubsequentRequestMustBeAdjustedBasedOnTimestampInWwwAuthenticateHeaderOfPrevious() { using (var invoker = new HttpMessageInvoker(server)) { using (var request = new HttpRequestMessage(HttpMethod.Get, URI)) { var client = new HawkClient(() => ServerFactory.DefaultCredential); await client.CreateClientAuthorizationAsync(request); using (var response = await invoker.SendAsync(request, CancellationToken.None)) { // Simulate server clock running 5 minutes slower than client and // produce the parameter with ts and tsm var timestamp = new NormalizedTimestamp(DateTime.UtcNow.AddMinutes(-5), ServerFactory.DefaultCredential); string timestampHeader = timestamp.ToWwwAuthenticateHeaderParameter(); // Add that to the WWW-Authenticate before authenticating with the // client, to simulate clock skew response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue("hawk", timestampHeader)); // Client must have now calculated an offset of -300 seconds approx Assert.IsTrue(await client.AuthenticateAsync(response)); Assert.IsTrue(HawkClient.CompensatorySeconds <= -299 && HawkClient.CompensatorySeconds >= -301); // Create a fresh request and see if this offset is applied to the timestamp using (var subsequentRequest = new HttpRequestMessage(HttpMethod.Get, URI)) { await client.CreateClientAuthorizationAsync(subsequentRequest); string header = subsequentRequest.Headers.Authorization.Parameter; ArtifactsContainer artifacts = null; Assert.IsTrue(ArtifactsContainer.TryParse(header, out artifacts)); var timestampInSubsequentRequest = artifacts.Timestamp; var now = DateTime.UtcNow.ToUnixTime(); // Since server clock is slow, the timestamp going out must be offset by the same 5 minutes // or 300 seconds. Give leeway of a second while asserting. ulong difference = now - timestampInSubsequentRequest; Assert.IsTrue(difference >= 299 && difference <= 301); } } } } }
/// <summary> /// Returns true, if there is a WWW-Authenticate header containing ts and tsm but mac /// computed for ts does not match tsm, indicating possible tampering. Otherwise, returns false. /// This method also sets the compensation field so that the timestamp in the subsequent requests /// are adjusted to reduce the clock skew. /// </summary> private bool IsTimestampResponseTampered(ArtifactsContainer artifacts, IResponseMessage response) { var wwwHeader = response.WwwAuthenticate; if (wwwHeader != null) { string parameter = wwwHeader.Parameter; ArtifactsContainer timestampArtifacts; if (!String.IsNullOrWhiteSpace(parameter) && ArtifactsContainer.TryParse(parameter, out timestampArtifacts)) { var ts = new NormalizedTimestamp(timestampArtifacts.Timestamp, options.CredentialsCallback(), options.LocalTimeOffsetMillis); if (!ts.IsValid(timestampArtifacts.TimestampMac)) return true; lock (myPrecious) HawkClient.CompensatorySeconds = (int)(timestampArtifacts.Timestamp - DateTime.UtcNow.ToUnixTime()); Tracing.Information("HawkClient.CompensatorySeconds set to " + HawkClient.CompensatorySeconds); } } return false; }