module Roda::RodaPlugins::Sessions::RequestMethods
def _deserialize_session(data)
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