module OpenToken
Constants
- CLOCK_SKEW_TOLERANCE
- VERSION
Attributes
debug[RW]
password[RW]
renew_until_lifetime[RW]
token_lifetime[RW]
Public Class Methods
debug?()
click to toggle source
# File lib/opentoken.rb, line 18 def debug? !!debug end
decode(opentoken = nil)
click to toggle source
# File lib/opentoken.rb, line 59 def decode(opentoken = nil) verify opentoken.present?, 'Unable to parse empty token' data = urlsafe_decode64(opentoken) inspect_binary_string 'DATA', data verify_header data verify_version data #cipher suite identifier cipher_suite = char_value_of data[4] cipher = OpenToken::Cipher.for_suite cipher_suite #SHA-1 HMAC payload_hmac = data[5..24] inspect_binary_string "PAYLOAD HMAC [5..24]", payload_hmac #Initialization Vector (iv) iv_length = char_value_of data[25] iv_end = char_value_of [26, 26 + iv_length - 1].max iv = data[26..iv_end] inspect_binary_string "IV [26..#{iv_end}]", iv verify iv_length == cipher.iv_length, "Cipher expects iv length of #{cipher.iv_length} and was: #{iv_length}" #key (not currently used) key_length = char_value_of data[iv_end + 1] key_end = iv_end + 1 verify key_length == 0, "Token key embedding is not currently supported. Key length is: #{key_length}" #payload payload_length = data[(key_end + 1)..(key_end + 2)].unpack('n').first payload_offset = key_end + 3 encrypted_payload = data[payload_offset..(data.length - 1)] verify encrypted_payload.length == payload_length, "Payload length is #{encrypted_payload.length} and was expected to be #{payload_length}" inspect_binary_string "ENCRYPTED PAYLOAD [#{payload_offset}..#{data.length - 1}]", encrypted_payload key = cipher.generate_key inspect_binary_string 'KEY', key compressed_payload = cipher.decrypt_payload encrypted_payload, key, iv inspect_binary_string 'COMPRESSED PAYLOAD', compressed_payload unparsed_payload = unzip_payload compressed_payload puts 'EXPANDED PAYLOAD', unparsed_payload if debug? #validate payload hmac mac = [] mac << "0x01".hex.chr mac << cipher_suite.chr mac << iv mac << key if key_length > 0 #key embedding is not currently supported mac << unparsed_payload hash = OpenSSL::HMAC.digest(OpenToken::PasswordKeyGenerator::SHA1_DIGEST, key, mac.join) if (hash <=> payload_hmac) != 0 verify payload_hmac == hash, "HMAC for payload was #{hash} and expected to be #{payload_hmac}" end unescaped_payload = CGI::unescapeHTML(unparsed_payload) puts 'UNESCAPED PAYLOAD', unescaped_payload if debug? token = OpenToken::KeyValueSerializer.deserialize force_encoding(unescaped_payload, 'UTF-8') puts token.inspect if debug? token.validate! token end
encode(attributes, cipher)
click to toggle source
# File lib/opentoken.rb, line 26 def encode(attributes, cipher) attributes['not-before'] = Time.now.utc.iso8601.to_s attributes['not-on-or-after'] = Time.at(Time.now.to_i + token_lifetime).utc.iso8601.to_s attributes['renew-until'] = Time.at(Time.now.to_i + renew_until_lifetime).utc.iso8601.to_s serialized = OpenToken::KeyValueSerializer.serialize(attributes) compressed = zip_payload serialized key = cipher.generate_key iv = cipher.generate_iv encrypted = cipher.encrypt_payload compressed, key, iv mac = [] mac << "0x01".hex.chr # OTK version mac << cipher.suite.chr mac << iv mac << force_encoding(serialized, 'BINARY') hash = OpenSSL::HMAC.digest(OpenToken::PasswordKeyGenerator::SHA1_DIGEST, key, mac.join) token_string = "" token_string = "OTK" + 1.chr + cipher.suite.chr token_string += hash token_string += cipher.iv_length.chr token_string += iv token_string += 0.chr # key info length token_string += ((encrypted.length >> 8) &0xFF ).chr token_string += (encrypted.length & 0xFF).chr token_string += encrypted inspect_binary_string "Unencoded", token_string encoded = urlsafe_encode64 token_string inspect_binary_string "Encoded", encoded encoded end
Private Class Methods
char_value_of(character)
click to toggle source
# File lib/opentoken.rb, line 124 def char_value_of(character) if RUBY_VERSION < "1.9" return character else return character.chr.ord end end
force_encoding(string, encoding)
click to toggle source
# File lib/opentoken.rb, line 174 def force_encoding(string, encoding) string.respond_to?(:force_encoding) ? string.force_encoding(encoding) : string end
inspect_binary_string(header, string)
click to toggle source
# File lib/opentoken.rb, line 165 def inspect_binary_string(header, string) return unless debug? puts "#{header}:" index = 0 string.each_byte do |b| puts "#{index}: #{b} => #{b.chr}" index += 1 end end
unzip_payload(compressed_payload)
click to toggle source
decompress the payload see stackoverflow.com/questions/1361892/how-to-decompress-gzip-data-in-ruby
# File lib/opentoken.rb, line 154 def unzip_payload(compressed_payload) unparsed_payload = begin Zlib::Inflate.inflate(compressed_payload) rescue Zlib::BufError Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(compressed_payload[2, compressed_payload.size]) end end
urlsafe_decode64(token)
click to toggle source
ruby 1.9 has Base64.urlsafe_decode64 which can be used instead of gsubbing ‘_’ and ‘-’
# File lib/opentoken.rb, line 140 def urlsafe_decode64(token) string = token.gsub('*', '=').gsub('_', '/').gsub('-', '+') data = Base64.decode64(string) end
urlsafe_encode64(token)
click to toggle source
# File lib/opentoken.rb, line 144 def urlsafe_encode64(token) string = Base64.encode64(token); string = string.gsub('=', '*').gsub('/', '_').gsub('+', '-').gsub(10.chr, '').gsub(11.chr, '') string end
verify(assertion, message = 'Invalid Token')
click to toggle source
# File lib/opentoken.rb, line 149 def verify(assertion, message = 'Invalid Token') raise OpenToken::TokenInvalidError.new(message) unless assertion end
verify_header(data)
click to toggle source
# File lib/opentoken.rb, line 131 def verify_header(data) header = data[0..2] verify header == 'OTK', "Invalid token header: #{header}" end
verify_version(data)
click to toggle source
# File lib/opentoken.rb, line 135 def verify_version(data) version = char_value_of data[3] verify version == 1, "Unsupported token version: '#{version}'" end
zip_payload(uncompressed)
click to toggle source
# File lib/opentoken.rb, line 161 def zip_payload(uncompressed) compressed = Zlib::Deflate.deflate(uncompressed, 9) compressed end