Пример #1
0
        /// <summary>
        /// Returns the string representation of the bewit, which is a base64 URL encoded string of format
        /// id\exp\mac\ext, where id is the user identifier, exp is the UNIX time until which bewit is
        /// valid, mac is the HMAC of the bewit to protect integrity, and ext is the application specific data.
        /// </summary>
        public string ToBewitString()
        {
            if (request.Method != HttpMethod.Get) // Not supporting HEAD
                throw new InvalidOperationException("Bewit not allowed for methods other than GET");

            ulong now = utcNow.ToUnixTime() + Convert.ToUInt64(this.localOffset);

            var artifacts = new ArtifactsContainer()
            {
                Id = credential.Id,
                Timestamp = now + (ulong)lifeSeconds,
                Nonce = String.Empty,
                ApplicationSpecificData = this.applicationSpecificData ?? String.Empty
            };

            var normalizedRequest = new NormalizedRequest(request, artifacts) { IsBewit = true };
            var crypto = new Cryptographer(normalizedRequest, artifacts, credential);

            // Sign the request
            crypto.Sign(); // Bewit is for GET and GET must have no request body

            // bewit: id\exp\mac\ext
            string bewit = String.Format(@"{0}\{1}\{2}\{3}",
                                credential.Id,
                                artifacts.Timestamp,
                                artifacts.Mac.ToBase64String(),
                                artifacts.ApplicationSpecificData);

            return bewit.ToBytesFromUtf8().ToBase64UrlString();
        }
        /// <summary>
        /// Attempts to convert the passed in header parameter (string) into the CLR equivalent, which is an
        /// instance of ArtifactsContainer. The return value indicates whether the conversion succeeded.
        /// </summary>
        internal static bool TryParse(string headerParameter, out ArtifactsContainer container)
        {
            HawkEventSource.Log.Debug("Hawk Authorization: " + headerParameter);

            ArtifactsContainer result = new ArtifactsContainer();

            var keysToBeProcessed = new HashSet<string>() { ID, TS, NONCE, EXT, MAC, HASH, TSM };

            var replacedString = Regex.Replace(headerParameter, PARAMETER_MATCH_PATTERN, (Match match) =>
            {
                string key = match.Groups[1].Value.Trim();
                string value = match.Groups[2].Value.Trim();

                bool isValidValue = Regex.Match(value, VALUE_MATCH_PATTERN).Success;
                bool isValidKey = keysToBeProcessed.Any(k => k == key); // Key is neither duplicate nor bad

                if (isValidValue && isValidKey)
                {
                    switch (key)
                    {
                        case ID: result.Id = value; break;

                        case TS:
                            {
                                ulong timestamp;
                                if (UInt64.TryParse(value, out timestamp))
                                {
                                    result.Timestamp = timestamp;
                                    break;
                                }
                                else
                                    return value;
                            }

                        case NONCE: result.Nonce = value; break;
                        case EXT: result.ApplicationSpecificData = value; break;
                        case MAC: result.Mac = value.ToBytesFromBase64(); break;
                        case HASH: result.PayloadHash = value.ToBytesFromBase64(); break;
                        case TSM: result.TimestampMac = value.ToBytesFromBase64(); break;
                    }

                    keysToBeProcessed.Remove(key); // Processed

                    return String.Empty;
                }
                else
                    return value;
            });

            if (replacedString == String.Empty) // No more, no less -> valid parameter data
            {
                container = result;
                return true;
            }

            HawkEventSource.Log.UnparsedArtifact(replacedString);

            container = null;
            return false;
        }
 internal Cryptographer(NormalizedRequest request, ArtifactsContainer artifacts, Credential credential)
 {
     this.normalizedRequest = request;
     this.artifacts = artifacts;
     this.credential = credential;
     this.hasher = new Hasher(credential.Algorithm);
 }
Пример #4
0
 internal Cryptographer(NormalizedRequest request, ArtifactsContainer artifacts, Credential credential)
 {
     this.normalizedRequest = request;
     this.artifacts         = artifacts;
     this.credential        = credential;
     this.hasher            = new Hasher(credential.Algorithm);
 }
Пример #5
0
        /// <summary>
        /// Returns the string representation of the bewit, which is a base64 URL encoded string of format
        /// id\exp\mac\ext, where id is the user identifier, exp is the UNIX time until which bewit is
        /// valid, mac is the HMAC of the bewit to protect integrity, and ext is the application specific data.
        /// </summary>
        public string ToBewitString()
        {
            if (request.Method != HttpMethod.Get) // Not supporting HEAD
            {
                throw new InvalidOperationException("Bewit not allowed for methods other than GET");
            }

            ulong now = utcNow.ToUnixTime() + Convert.ToUInt64(this.localOffset);

            var artifacts = new ArtifactsContainer()
            {
                Id        = credential.Id,
                Timestamp = now + (ulong)lifeSeconds,
                Nonce     = String.Empty,
                ApplicationSpecificData = this.applicationSpecificData ?? String.Empty
            };

            var normalizedRequest = new NormalizedRequest(request, artifacts)
            {
                IsBewit = true
            };
            var crypto = new Cryptographer(normalizedRequest, artifacts, credential);

            // Sign the request
            crypto.Sign(); // Bewit is for GET and GET must have no request body

            // bewit: id\exp\mac\ext
            string bewit = String.Format(@"{0}\{1}\{2}\{3}",
                                         credential.Id,
                                         artifacts.Timestamp,
                                         artifacts.Mac.ToBase64String(),
                                         artifacts.ApplicationSpecificData);

            return(bewit.ToBytesFromUtf8().ToBase64UrlString());
        }
        internal NormalizedRequest(IRequestMessage request, ArtifactsContainer artifacts, string hostName = null, string port = null)
        {
            this.artifacts = artifacts;

            this.hostName = (hostName ?? request.Uri.Host).ToLower();
            this.port = port ?? request.Uri.Port.ToString();
            this.method = request.Method.Method.ToUpper();
            this.path = WebUtility.UrlDecode(request.Uri.AbsolutePath) + request.Uri.Query;
        }
        internal NormalizedRequest(IRequestMessage request, ArtifactsContainer artifacts, string hostName = null, string port = null)
        {
            this.artifacts = artifacts;

            this.hostName = (hostName ?? request.Uri.Host).ToLower();
            this.port     = port ?? request.Uri.Port.ToString();
            this.method   = request.Method.Method.ToUpper();
            this.path     = WebUtility.UrlDecode(request.Uri.AbsolutePath) + request.Uri.Query;
        }
Пример #8
0
        /// <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
            });
        }
Пример #9
0
        internal NormalizedRequest(IRequestMessage request,
                                   ArtifactsContainer artifacts,
                                   HostNameSource?hostNameSource = null)
        {
            this.artifacts = artifacts;

            // Case 1: For bewit, host and port are always from the request URI.
            if (IsBewit)
            {
                this.hostName = request.Uri.Host;
                this.port     = request.Uri.Port.ToString();
            }
            else
            {
                if (hostNameSource.HasValue) // Case 2: NOT bewit and user has specified the host name source
                {
                    switch (hostNameSource.Value)
                    {
                    case HostNameSource.XForwardedForHeader:
                        this.hostName = this.GetHostName(request.ForwardedFor, out this.port); break;

                    case HostNameSource.HostHeader:
                        this.hostName = this.GetHostName(request.Host, out this.port); break;

                    case HostNameSource.RequestUri:
                        this.hostName = request.Uri.Host; break;
                    }
                }

                if (String.IsNullOrWhiteSpace(this.hostName))
                {
                    // Case 3: NOT bewit and user has specified the host name source but unable to determine host name.
                    // Case 4: NOT bewit and user has NOT specified the host name source.
                    // For both cases, try X-Forwarded-For header first, then Host header, and finally request URI.

                    this.hostName = this.GetHostName(request.ForwardedFor, out this.port) ??
                                    this.GetHostName(request.Host, out this.port) ??
                                    request.Uri.Host;
                }
            }

            if (String.IsNullOrWhiteSpace(this.port))
            {
                this.port = request.Uri.Port.ToString();
            }

            this.method = request.Method.Method.ToUpper();
            this.path   = request.Uri.PathAndQuery;
        }
        /// <summary>
        /// Returns true if the timestamp sent in by the client is fresh subject to the 
        /// maximum allowed skew and the adjustment offset.
        /// </summary>
        private static bool IsTimestampFresh(ulong now, ArtifactsContainer artifacts, Options options)
        {
            now = now + Convert.ToUInt64(options.LocalTimeOffsetMillis);

            ulong shelfLife = (Convert.ToUInt64(options.ClockSkewSeconds) * 1000);
            var age = Math.Abs((artifacts.Timestamp * 1000.0) - now);

            bool isFresh = (age <= shelfLife);

            if (!isFresh)
                Tracing.Information(
                            String.Format("Stale Timestamp: Age {0} is more than shelf life of {1}", age, shelfLife));

            return isFresh;
        }
        /// <summary>
        /// Returns true if the timestamp sent in by the client is fresh subject to the
        /// maximum allowed skew and the adjustment offset.
        /// </summary>
        private static bool IsTimestampFresh(ulong now, ArtifactsContainer artifacts, Options options)
        {
            now = now + Convert.ToUInt64(options.LocalTimeOffsetMillis);

            ulong shelfLife = (Convert.ToUInt64(options.ClockSkewSeconds) * 1000);
            var   age       = Math.Abs((artifacts.Timestamp * 1000.0) - now);

            bool isFresh = (age <= shelfLife);

            if (!isFresh)
            {
                HawkEventSource.Log.StaleTimestamp(age.ToString(), shelfLife.ToString());
            }

            return(isFresh);
        }
Пример #12
0
        /// <summary>
        /// Returns true if the timestamp sent in by the client is fresh subject to the
        /// maximum allowed skew and the adjustment offset.
        /// </summary>
        private static bool IsTimestampFresh(ulong now, ArtifactsContainer artifacts, Options options)
        {
            now = now + Convert.ToUInt64(options.LocalTimeOffsetMillis);

            ulong shelfLife = (Convert.ToUInt64(options.ClockSkewSeconds) * 1000);
            var   age       = Math.Abs((artifacts.Timestamp * 1000.0) - now);

            bool isFresh = (age <= shelfLife);

            if (!isFresh)
            {
                Tracing.Information(
                    String.Format("Stale Timestamp: Age {0} is more than shelf life of {1}", age, shelfLife));
            }

            return(isFresh);
        }
        internal NormalizedRequest(IRequestMessage request,
                                        ArtifactsContainer artifacts,
                                            HostNameSource? hostNameSource = null)
        {
            this.artifacts = artifacts;

            // Case 1: For bewit, host and port are always from the request URI.
            if (IsBewit)
            {
                this.hostName = request.Uri.Host;
                this.port = request.Uri.Port.ToString();
            }
            else
            {
                if (hostNameSource.HasValue) // Case 2: NOT bewit and user has specified the host name source
                {
                    switch (hostNameSource.Value)
                    {
                        case HostNameSource.XForwardedForHeader:
                            this.hostName = this.GetHostName(request.ForwardedFor, out this.port); break;
                        case HostNameSource.HostHeader:
                            this.hostName = this.GetHostName(request.Host, out this.port); break;
                        case HostNameSource.RequestUri:
                                this.hostName = request.Uri.Host; break;
                    }
                }

                if (String.IsNullOrWhiteSpace(this.hostName))
                {
                    // Case 3: NOT bewit and user has specified the host name source but unable to determine host name.
                    // Case 4: NOT bewit and user has NOT specified the host name source.
                    // For both cases, try X-Forwarded-For header first, then Host header, and finally request URI.

                    this.hostName = this.GetHostName(request.ForwardedFor, out this.port) ??
                                        this.GetHostName(request.Host, out this.port) ??
                                            request.Uri.Host;
                }
            }

            if (String.IsNullOrWhiteSpace(this.port))
                this.port = request.Uri.Port.ToString();

            this.method = request.Method.Method.ToUpper();
            this.path = request.Uri.PathAndQuery;
        }
        internal NormalizedRequest(IRequestMessage request, ArtifactsContainer artifacts)
        {
            this.artifacts = artifacts;

            // Determine host and port - take the host name from X-Forwarded-For header, if present, or from
            // the Host header, if present, or from the HttpRequestMessage object. For bewit, it is always from URI.
            string firstPreference = IsBewit? null : request.ForwardedFor;
            string secondPreference = IsBewit ? null : request.Host;

            this.hostName = this.GetHostName(firstPreference, out this.port) ??
                                this.GetHostName(secondPreference, out this.port) ??
                                    request.Uri.Host;

            if (String.IsNullOrWhiteSpace(this.port))
                this.port = request.Uri.Port.ToString();

            this.method = request.Method.Method.ToUpper();
            this.path = request.Uri.PathAndQuery;
        }
Пример #15
0
        internal NormalizedRequest(IRequestMessage request, ArtifactsContainer artifacts)
        {
            this.artifacts = artifacts;

            // Determine host and port - take the host name from X-Forwarded-For header, if present, or from
            // the Host header, if present, or from the HttpRequestMessage object. For bewit, it is always from URI.
            string firstPreference  = IsBewit? null : request.ForwardedFor;
            string secondPreference = IsBewit ? null : request.Host;

            this.hostName = this.GetHostName(firstPreference, out this.port) ??
                            this.GetHostName(secondPreference, out this.port) ??
                            request.Uri.Host;

            if (String.IsNullOrWhiteSpace(this.port))
            {
                this.port = request.Uri.Port.ToString();
            }

            this.method = request.Method.Method.ToUpper();
            this.path   = request.Uri.PathAndQuery;
        }
        /// <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;
        }
        /// <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>
        /// Creates the HTTP Authorization header in hawk scheme.
        /// </summary>
        internal async Task CreateClientAuthorizationInternalAsync(IRequestMessage request, DateTime utcNow)
        {
            var credential = options.CredentialsCallback();
            this.artifacts = new ArtifactsContainer()
            {
                Id = credential.Id,
                Timestamp = utcNow.AddSeconds(HawkClient.CompensatorySeconds).ToUnixTime(),
                Nonce = NonceGenerator.Generate()
            };

            if (options.NormalizationCallback != null)
                this.artifacts.ApplicationSpecificData = options.NormalizationCallback(request);

            var normalizedRequest = new NormalizedRequest(request, this.artifacts);
            this.crypto = new Cryptographer(normalizedRequest, this.artifacts, credential);

            // Sign the request
            bool includePayloadHash = options.RequestPayloadHashabilityCallback != null &&
                                            options.RequestPayloadHashabilityCallback(request);

            string payload = includePayloadHash ? await request.ReadBodyAsStringAsync() : null;
            crypto.Sign(payload, request.ContentType);

            request.Authorization = new AuthenticationHeaderValue(HawkConstants.Scheme,
                                                this.artifacts.ToAuthorizationHeaderParameter());
        }
        /// <summary>
        /// Returns true if the timestamp sent in by the client is fresh subject to the 
        /// maximum allowed skew and the adjustment offset.
        /// </summary>
        private static bool IsTimestampFresh(ulong now, ArtifactsContainer artifacts, Options options)
        {
            now = now + Convert.ToUInt64(options.LocalTimeOffsetMillis);

            ulong shelfLife = (Convert.ToUInt64(options.ClockSkewSeconds) * 1000);
            var age = Math.Abs((artifacts.Timestamp * 1000.0) - now);

            bool isFresh = (age <= shelfLife);

            if (!isFresh)
                HawkEventSource.Log.StaleTimestamp(age.ToString(), shelfLife.ToString());

            return isFresh;
        }
Пример #20
0
        /// <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
            });
        }
Пример #22
0
        /// <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 };
        }
Пример #23
0
        /// <summary>
        /// Attempts to convert the passed in header parameter (string) into the CLR equivalent, which is an
        /// instance of ArtifactsContainer. The return value indicates whether the conversion succeeded.
        /// </summary>
        internal static bool TryParse(string headerParameter, out ArtifactsContainer container)
        {
            ArtifactsContainer result = new ArtifactsContainer();

            var keysToBeProcessed = new HashSet <string>()
            {
                ID, TS, NONCE, EXT, MAC, HASH, TSM
            };

            var replacedString = Regex.Replace(headerParameter, PARAMETER_MATCH_PATTERN, (Match match) =>
            {
                string key   = match.Groups[1].Value.Trim();
                string value = match.Groups[2].Value.Trim();

                bool isValidValue = Regex.Match(value, VALUE_MATCH_PATTERN).Success;
                bool isValidKey   = keysToBeProcessed.Any(k => k == key); // Key is neither duplicate nor bad

                if (isValidValue && isValidKey)
                {
                    switch (key)
                    {
                    case ID: result.Id = value; break;

                    case TS:
                        {
                            ulong timestamp;
                            if (UInt64.TryParse(value, out timestamp))
                            {
                                result.Timestamp = timestamp;
                                break;
                            }
                            else
                            {
                                return(value);
                            }
                        }

                    case NONCE: result.Nonce = value; break;

                    case EXT: result.ApplicationSpecificData = value; break;

                    case MAC: result.Mac = value.ToBytesFromBase64(); break;

                    case HASH: result.PayloadHash = value.ToBytesFromBase64(); break;

                    case TSM: result.TimestampMac = value.ToBytesFromBase64(); break;
                    }

                    keysToBeProcessed.Remove(key); // Processed

                    return(String.Empty);
                }
                else
                {
                    return(value);
                }
            });

            if (replacedString == String.Empty) // No more, no less -> valid parameter data
            {
                container = result;
                return(true);
            }

            Tracing.Error("Unable to parse the artifacts.");

            container = null;
            return(false);
        }