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
- HEADER =
Pattern that matches strings that are probably a V3 session cookie.
/^WzM/- 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
-
#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.
Methods inherited from Abstract
#[], #[]=, #delete, #dirty?, #each_pair, #has_key?, #initialize, #inspect, #invalidate!, #keys, #new_record?, #renew!, #supports_key?, #to_h, #valid?, #values
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
60 61 62 63 64 |
# File 'lib/global_session/session/v3.rb', line 60 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.
102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/global_session/session/v3.rb', line 102 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.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/global_session/session/v3.rb', line 76 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
#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
169 170 171 |
# File 'lib/global_session/session/v3.rb', line 169 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.
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 156 157 158 159 160 |
# File 'lib/global_session/session/v3.rb', line 122 def to_s if @cookie && !dirty? #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_timestamps || @dirty_secure) #use cached signature unless we've changed secure state = @authority else = @directory. hash['a'] = signed_hash = RightSupport::Crypto::SignedHash.new( hash, @directory.private_key, envelope: true, encoding: GlobalSession::Encoding::JSON) @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 |