lib/roda/plugins/sessions.rb
def _deserialize_session(data)
          opts = roda_class.opts[:sessions]

          begin
            data = Base64.urlsafe_decode64(data)
          rescue ArgumentError
            return _session_serialization_error("Unable to decode session: invalid base64")
          end

          case version = data.getbyte(0)
          when 1
            per_cookie_secret = true
            # minimum length (1+32+16+12+32) (version+random_data+cipher_iv+minimum session+hmac)
            # 1 : version
            # 32 : random_data (if per_cookie_cipher_secret)
            # 16 : cipher_iv
            # 12 : minimum_session
            #      2 : bitmap for gzip + padding info
            #      4 : creation time
            #      4 : update time
            #      2 : data
            # 32 : HMAC-SHA-256
            min_data_length = 93
          when 0
            per_cookie_secret = false
            # minimum length (1+16+12+32) (version+cipher_iv+minimum session+hmac)
            min_data_length = 61
          when nil
            return _session_serialization_error("Unable to decode session: no data")
          else
            return _session_serialization_error("Unable to decode session: version marker unsupported")
          end

          length = data.bytesize
          if data.length < min_data_length
            return _session_serialization_error("Unable to decode session: data too short")
          end

          encrypted_data = data.slice!(0, length-32)
          unless Rack::Utils.secure_compare(data, OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, opts[:hmac_secret], encrypted_data+opts[:key]))
            if opts[:old_hmac_secret] && Rack::Utils.secure_compare(data, OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, opts[:old_hmac_secret], encrypted_data+opts[:key]))
              use_old_cipher_secret = true
            else
              return _session_serialization_error("Not decoding session: HMAC invalid")
            end
          end

          # Remove version
          encrypted_data.slice!(0)

          cipher_secret = opts[use_old_cipher_secret ? :old_cipher_secret : :cipher_secret]
          if per_cookie_secret
            cipher_secret = OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, cipher_secret, encrypted_data.slice!(0, 32))
          end
          cipher_iv = encrypted_data.slice!(0, 16)

          cipher = OpenSSL::Cipher.new("aes-256-ctr")

          # Not rescuing cipher errors.  If there is an error in the decryption, that's
          # either a bug in the plugin that needs to be fixed, or an attacker is already
          # able to forge a valid HMAC, in which case the error should be raised to
          # alert the application owner about the problem.
          cipher.decrypt
          cipher.key = cipher_secret
          cipher.iv = cipher_iv
          data = cipher.update(encrypted_data) << cipher.final

          bitmap, created_at, updated_at = data.unpack('vVV')
          padding_bytes = bitmap & PADDING_MASK
          now = Time.now.to_i
          if (max = opts[:max_seconds]) && now > created_at + max
            return _session_serialization_error("Not returning session: maximum session time expired")
          end
          if (max = opts[:max_idle_seconds]) && now > updated_at + max
            return _session_serialization_error("Not returning session: maximum session idle time expired")
          end

          data = data.slice(10+padding_bytes, data.bytesize)

          if bitmap & DEFLATE_BIT > 0
            data = Zlib::Inflate.inflate(data)
          end

          env = @env
          env[SESSION_CREATED_AT] = created_at
          env[SESSION_UPDATED_AT] = updated_at
          env[SESSION_SERIALIZED] = data
          env[SESSION_VERSION_NUM] = version

          opts[:parser].call(data)
        end
# Interpret given cookie data as a Rack::Session::Cookie
Interpret given cookie data as a Rack::Session::Cookie
Interpret given cookie data as a Rack :: Session :: Cookie
def _deserialize_session(data)
          opts = roda_class.opts[:sessions]
          begin
            data = Base64.urlsafe_decode64(data)
          rescue ArgumentError
            return _session_serialization_error("Unable to decode session: invalid base64")
          end
          case version = data.getbyte(0)
          when 1
            per_cookie_secret = true
            min_data_length = 93
          when 0
            per_cookie_secret = false
            min_data_length = 61
          when nil
            return _session_serialization_error("Unable to decode session: no data")
          else
            return _session_serialization_error("Unable to decode session: version marker unsupported")
          end
          length = data.bytesize
          if data.length < min_data_length
            return _session_serialization_error("Unable to decode session: data too short")
          end
          encrypted_data = data.slice!(0, length-32)
          unless Rack::Utils.secure_compare(data, OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, opts[:hmac_secret], encrypted_data+opts[:key]))
            if opts[:old_hmac_secret] && Rack::Utils.secure_compare(data, OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, opts[:old_hmac_secret], encrypted_data+opts[:key]))
              use_old_cipher_secret = true
            else
              return _session_serialization_error("Not decoding session: HMAC invalid")
            end
          end
          encrypted_data.slice!(0)
          cipher_secret = opts[use_old_cipher_secret ? :old_cipher_secret : :cipher_secret]
          if per_cookie_secret
            cipher_secret = OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, cipher_secret, encrypted_data.slice!(0, 32))
          end
          cipher_iv = encrypted_data.slice!(0, 16)
          cipher = OpenSSL::Cipher.new("aes-256-ctr")
          cipher.decrypt
          cipher.key = cipher_secret
          cipher.iv = cipher_iv
          data = cipher.update(encrypted_data) << cipher.final
          bitmap, created_at, updated_at = data.unpack('vVV')
          padding_bytes = bitmap & PADDING_MASK
          now = Time.now.to_i
          if (max = opts[:max_seconds]) && now > created_at + max
            return _session_serialization_error("Not returning session: maximum session time expired")
          end
          if (max = opts[:max_idle_seconds]) && now > updated_at + max
            return _session_serialization_error("Not returning session: maximum session idle time expired")
          end
          data = data.slice(10+padding_bytes, data.bytesize)
          if bitmap & DEFLATE_BIT > 0
            data = Zlib::Inflate.inflate(data)
          end
          env = @env
          env[SESSION_CREATED_AT] = created_at
          env[SESSION_UPDATED_AT] = updated_at
          env[SESSION_SERIALIZED] = data
          env[SESSION_VERSION_NUM] = version
          opts[:parser].call(data)
        end
def _deserialize_session ( data ) opts = roda_class . opts [ :sessions ] begin data = Base64 . urlsafe_decode64 ( data ) rescue ArgumentError return _session_serialization_error ( "Unable to decode session: invalid base64" ) end case version = data . getbyte ( 0 ) when 1 per_cookie_secret = true min_data_length = 93 when 0 per_cookie_secret = false min_data_length = 61 when nil return _session_serialization_error ( "Unable to decode session: no data" ) else return _session_serialization_error ( "Unable to decode session: version marker unsupported" ) end length = data . bytesize if data . length < min_data_length return _session_serialization_error ( "Unable to decode session: data too short" ) end encrypted_data = data . slice! ( 0 , length - 32 ) unless Rack :: Utils . secure_compare ( data , OpenSSL :: HMAC . digest ( OpenSSL :: Digest :: SHA256 . new , opts [ :hmac_secret ] , encrypted_data + opts [ :key ] ) ) if opts [ :old_hmac_secret ] && Rack :: Utils . secure_compare ( data , OpenSSL :: HMAC . digest ( OpenSSL :: Digest :: SHA256 . new , opts [ :old_hmac_secret ] , encrypted_data + opts [ :key ] ) ) use_old_cipher_secret = true else return _session_serialization_error ( "Not decoding session: HMAC invalid" ) end end encrypted_data . slice! ( 0 ) cipher_secret = opts [ use_old_cipher_secret ? :old_cipher_secret : :cipher_secret ] if per_cookie_secret cipher_secret = OpenSSL :: HMAC . digest ( OpenSSL :: Digest :: SHA256 . new , cipher_secret , encrypted_data . slice! ( 0 , 32 ) ) end cipher_iv = encrypted_data . slice! ( 0 , 16 ) cipher = OpenSSL :: Cipher . new ( "aes-256-ctr" ) cipher . decrypt cipher . key = cipher_secret cipher . iv = cipher_iv data = cipher . update ( encrypted_data ) << cipher . final bitmap , created_at , updated_at = data . unpack ( 'vVV' ) padding_bytes = bitmap & PADDING_MASK now = Time . now . to_i if ( max = opts [ :max_seconds ] ) && now > created_at + max return _session_serialization_error ( "Not returning session: maximum session time expired" ) end if ( max = opts [ :max_idle_seconds ] ) && now > updated_at + max return _session_serialization_error ( "Not returning session: maximum session idle time expired" ) end data = data . slice ( 10 + padding_bytes , data . bytesize ) if bitmap & DEFLATE_BIT > 0 data = Zlib :: Inflate . inflate ( data ) end env = @env env [ SESSION_CREATED_AT ] = created_at env [ SESSION_UPDATED_AT ] = updated_at env [ SESSION_SERIALIZED ] = data env [ SESSION_VERSION_NUM ] = version opts [ :parser ] . call ( data ) end
Interpret given cookie data as a Rack::Session::Cookie
Interpret given cookie data as a Rack :: Session :: Cookie
# minimum length (1+32+16+12+32) (version+random_data+cipher_iv+minimum session+hmac) # 1 : version # 32 : random_data (if per_cookie_cipher_secret) # 16 : cipher_iv # 12 : minimum_session #      2 : bitmap for gzip + padding info #      4 : creation time #      4 : update time #      2 : data # 32 : HMAC-SHA-256 # minimum length (1+16+12+32) (version+cipher_iv+minimum session+hmac) # Remove version # Not rescuing cipher errors.  If there is an error in the decryption, that's # either a bug in the plugin that needs to be fixed, or an attacker is already # able to forge a valid HMAC, in which case the error should be raised to # alert the application owner about the problem.
data