Class: GlobalSession::Session::V3
- Defined in:
- lib/global_session/session/v3.rb
Overview
Global session V3 uses JSON serialization, no compression, and a detached signature that is excluded from the JSON structure for efficiency reasons.
The binary structure of a V3 session looks like this:
<utf8_json><0x00><binary_signature>
Its JSON structure is an Array with the following format:
[<version_integer>,
<uuid_string>,
<signing_authority_string>,
<creation_timestamp_integer>,
<expiration_timestamp_integer>,
{<signed_data_hash>},
{<unsigned_data_hash>}]
The design goal of V3 is to ensure broad compatibility across various programming languages and cryptographic libraries, and to create a serialization format that can be reused for future versions. To this end, it sacrifices space efficiency by switching back to JSON encoding (instead of msgpack), and uses the undocumented OpenSSL::PKey#sign and #verify operations which rely on the PKCS7-compliant OpenSSL EVP API.
Constant Summary collapse
- STRING_ENCODING =
!!(RUBY_VERSION !~ /1.8/)
Instance Attribute Summary
Attributes inherited from Abstract
#authority, #created_at, #directory, #expired_at, #id, #insecure, #signed
Class Method Summary collapse
-
.decode_cookie(cookie) ⇒ Object
Utility method to decode a cookie; good for console debugging.
-
.join_body(json, signature) ⇒ String
Join a UTF-8 JSON document and an ASCII-8bit binary string.
-
.split_body(input) ⇒ Array
Split an ASCII-8bit input string into two constituent parts: a UTF-8 JSON document and an ASCII-8bit binary string.
Instance Method Summary collapse
-
#[](key) ⇒ Object
Lookup a value by its key.
-
#[]=(key, value) ⇒ Object
Set a value in the global session hash.
-
#each_pair(&block) ⇒ Object
Iterate over each key/value pair.
-
#keys ⇒ Object
Return the keys that are currently present in the global session.
-
#renew!(expired_at = nil) ⇒ Object
Renews this global session, changing its expiry timestamp into the future.
-
#signature_digest ⇒ Object
Return the SHA1 hash of the most recently-computed RSA signature of this session.
-
#to_s ⇒ String
Serialize the session to a form suitable for use with HTTP cookies.
-
#values ⇒ Object
Return the values that are currently present in the global session.
Methods inherited from Abstract
#has_key?, #initialize, #inspect, #invalidate!, #new_record?, #supports_key?, #to_hash, #valid?
Constructor Details
This class inherits a constructor from GlobalSession::Session::Abstract
Class Method Details
.decode_cookie(cookie) ⇒ Object
Utility method to decode a cookie; good for console debugging. This performs no validation or security check of any sort.
Parameters
- cookie(String)
-
well-formed global session cookie
54 55 56 57 58 |
# File 'lib/global_session/session/v3.rb', line 54 def self.() bin = GlobalSession::Encoding::Base64Cookie.load() json, sig = split_body(bin) return GlobalSession::Encoding::JSON.load(json), sig end |
.join_body(json, signature) ⇒ String
Join a UTF-8 JSON document and an ASCII-8bit binary string.
This is an implementation helper for GlobalSession serialization and not useful for the public at large. It’s left public as an aid for those who want to hack sessions.
96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/global_session/session/v3.rb', line 96 def self.join_body(json, signature) result = "" if STRING_ENCODING result.force_encoding(Encoding::ASCII_8BIT) json.force_encoding(Encoding::ASCII_8BIT) signature.force_encoding(Encoding::ASCII_8BIT) end result << json result << "\x00" result << signature result end |
.split_body(input) ⇒ Array
Split an ASCII-8bit input string into two constituent parts: a UTF-8 JSON document and an ASCII-8bit binary string. A null (0x00) separator character is presumed to separate the two parts of the input string.
This is an implementation helper for GlobalSession serialization and not useful for the public at large. It’s left public as an aid for those who want to hack sessions.
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/global_session/session/v3.rb', line 70 def self.split_body(input) input.force_encoding(Encoding::ASCII_8BIT) if STRING_ENCODING null_at = input.index("\x00") if null_at json = input[0...null_at] sig = input[null_at+1..-1] if STRING_ENCODING json.force_encoding(Encoding::UTF_8) sig.force_encoding(Encoding::ASCII_8BIT) end return json, sig else raise ArgumentError, "Malformed input string does not contain 0x00 byte" end end |
Instance Method Details
#[](key) ⇒ Object
Lookup a value by its key.
Parameters
- key(String)
-
the key
Return
- value(Object)
-
The value associated with
key, or nil ifkeyis not present
192 193 194 195 |
# File 'lib/global_session/session/v3.rb', line 192 def [](key) key = key.to_s #take care of symbol-style keys @signed[key] || @insecure[key] end |
#[]=(key, value) ⇒ Object
Set a value in the global session hash. If the supplied key is denoted as secure by the global session schema, causes a new signature to be computed when the session is next serialized.
Parameters
- key(String)
-
The key to set
- value(Object)
-
The value to set
Return
- value(Object)
-
Always returns the value that was set
Raise
- InvalidSession
-
if the session has been invalidated (and therefore can’t be written to)
- ArgumentError
-
if the configuration doesn’t define the specified key as part of the global session
- NoAuthority
-
if the specified key is secure and the local node is not an authority
- UnserializableType
-
if the specified value can’t be serialized as JSON
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/global_session/session/v3.rb', line 213 def []=(key, value) key = key.to_s #take care of symbol-style keys raise GlobalSession::InvalidSession unless valid? if @schema_signed.include?(key) @signed[key] = value @dirty_secure = true elsif @schema_insecure.include?(key) @insecure[key] = value @dirty_insecure = true else raise ArgumentError, "Attribute '#{key}' is not specified in global session configuration" end return value end |
#each_pair(&block) ⇒ Object
Iterate over each key/value pair
Block
An iterator which will be called with each key/value pair
Return
Returns the value of the last expression evaluated by the block
180 181 182 183 |
# File 'lib/global_session/session/v3.rb', line 180 def each_pair(&block) # :yields: |key, value| @signed.each_pair(&block) @insecure.each_pair(&block) end |
#keys ⇒ Object
Return the keys that are currently present in the global session.
Return
- keys(Array)
-
List of keys contained in the global session
161 162 163 |
# File 'lib/global_session/session/v3.rb', line 161 def keys @signed.keys + @insecure.keys end |
#renew!(expired_at = nil) ⇒ Object
Renews this global session, changing its expiry timestamp into the future. Causes a new signature will be computed when the session is next serialized.
Return
- true
-
Always returns true
236 237 238 239 |
# File 'lib/global_session/session/v3.rb', line 236 def renew!(expired_at=nil) super(expired_at) @dirty_secure = true end |
#signature_digest ⇒ Object
Return the SHA1 hash of the most recently-computed RSA signature of this session. This isn’t really intended for the end user; it exists so the Web framework integration code can optimize request speed by caching the most recently verified signature in the local session and avoid re-verifying it on every request.
Return
- digest(String)
-
SHA1 hex-digest of most-recently-computed signature
248 249 250 |
# File 'lib/global_session/session/v3.rb', line 248 def signature_digest @signature ? digest(@signature) : nil end |
#to_s ⇒ String
Serialize the session to a form suitable for use with HTTP cookies. If any secure attributes have changed since the session was instantiated, compute a fresh RSA signature.
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/global_session/session/v3.rb', line 117 def to_s if @cookie && !@dirty_insecure && !@dirty_secure #use cached cookie if nothing has changed return @cookie end unless serializable?(@signed) && serializable?(@insecure) raise GlobalSession::UnserializableType, "Attributes hash contains non-String keys, cannot be cleanly marshalled" end hash = {'v' => 3, 'id' => @id, 'a' => @authority, 'tc' => @created_at.to_i, 'te' => @expired_at.to_i, 'ds' => @signed} if @signature && !@dirty_secure #use cached signature unless we've changed secure state = @authority else = @directory. hash['a'] = signed_hash = RightSupport::Crypto::SignedHash.new( hash, :envelope=>true, :encoding=>GlobalSession::Encoding::JSON, :private_key=>@directory.private_key) @signature = signed_hash.sign(@expired_at) end hash['dx'] = @insecure hash['a'] = array = attribute_hash_to_array(hash) json = GlobalSession::Encoding::JSON.dump(array) bin = self.class.join_body(json, @signature) return GlobalSession::Encoding::Base64Cookie.dump(bin) end |
#values ⇒ Object
Return the values that are currently present in the global session.
Return
- values(Array)
-
List of values contained in the global session
169 170 171 |
# File 'lib/global_session/session/v3.rb', line 169 def values @signed.values + @insecure.values end |