public void VerifyToken(RedwoodRequestContext context, string token)
        {
            if (context == null) throw new ArgumentNullException("context");
            if (string.IsNullOrWhiteSpace(token)) throw new SecurityException("CSRF protection token is missing.");

            // Get application key helper
            var keyHelper = new ApplicationKeyHelper(context.Configuration.Security);

            // Get token
            var userIdentity = ProtectionHelpers.GetUserIdentity(context);
            var requestIdentity = ProtectionHelpers.GetRequestIdentity(context);
            byte[] tokenSid;
            try
            {
                var tokenData = Convert.FromBase64String(token);
                tokenSid = keyHelper.UnprotectData(tokenData, KDF_LABEL_TOKEN, userIdentity, requestIdentity);
            }
            catch (Exception ex)
            {
                // Incorrect Base64 formatting of crypto protection error
                throw new SecurityException("CSRF protection token is invalid.", ex);
            }

            // Get SID from cookie and compare with token one
            var cookieSid = this.GetOrCreateSessionId(context);
            if (!cookieSid.SequenceEqual(tokenSid)) throw new SecurityException("CSRF protection token is invalid.");
        }
        private byte[] GetOrCreateSessionId(RedwoodRequestContext context)
        {
            if (context == null) throw new ArgumentNullException("context");
            if (string.IsNullOrWhiteSpace(context.Configuration.Security.SessionIdCookieName)) throw new FormatException("Configured SessionIdCookieName is missing or empty.");

            // Get cookie manager
            var mgr = new ChunkingCookieManager(); // TODO: Make this configurable

            // Get application key helper
            var keyHelper = new ApplicationKeyHelper(context.Configuration.Security);

            // Get cookie value
            var sidCookieValue = mgr.GetRequestCookie(context.OwinContext, context.Configuration.Security.SessionIdCookieName);

            if (string.IsNullOrWhiteSpace(sidCookieValue))
            {
                // No SID - generate and protect new one
                var rng = new System.Security.Cryptography.RNGCryptoServiceProvider();
                var sid = new byte[SID_LENGTH];
                rng.GetBytes(sid);
                var protectedSid = keyHelper.ProtectData(sid, KDF_LABEL_SID);

                // Save to cookie
                sidCookieValue = Convert.ToBase64String(protectedSid);
                mgr.AppendResponseCookie(
                    context.OwinContext,
                    context.Configuration.Security.SessionIdCookieName, // Configured cookie name
                    sidCookieValue,                                     // Base64-encoded SID value
                    new Microsoft.Owin.CookieOptions
                    {
                        HttpOnly = true,                                // Don't allow client script access
                        Secure = context.OwinContext.Request.IsSecure   // If request goes trough HTTPS, mark as secure only
                    });

                // Return newly generated SID
                return sid;
            }
            else
            {
                // Try to read from cookie
                try
                {
                    var protectedSid = Convert.FromBase64String(sidCookieValue);
                    var sid = keyHelper.UnprotectData(protectedSid, KDF_LABEL_SID);
                    return sid;
                }
                catch (Exception ex)
                {
                    // Incorrect Base64 formatting of crypto protection error
                    throw new SecurityException("Value of the SessionID cookie is corrupted or has been tampered with.", ex);
                }
            }
        }