Module: OpenToken

Defined in:
lib/opentoken-newrelic-rails23.rb,
lib/opentoken-newrelic-rails23/token.rb,
lib/opentoken-newrelic-rails23/cipher.rb,
lib/opentoken-newrelic-rails23/version.rb,
lib/opentoken-newrelic-rails23/key_value_serializer.rb,
lib/opentoken-newrelic-rails23/password_key_generator.rb

Defined Under Namespace

Classes: Cipher, KeyValueSerializer, PasswordKeyGenerator, Token, TokenExpiredError, TokenInvalidError

Constant Summary collapse

VERSION =
'1.2.2'

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.debugObject

Returns the value of attribute debug.



17
18
19
# File 'lib/opentoken-newrelic-rails23.rb', line 17

def debug
  @debug
end

.passwordObject

Returns the value of attribute password.



22
23
24
# File 'lib/opentoken-newrelic-rails23.rb', line 22

def password
  @password
end

.renew_until_lifetimeObject

Returns the value of attribute renew_until_lifetime.



24
25
26
# File 'lib/opentoken-newrelic-rails23.rb', line 24

def renew_until_lifetime
  @renew_until_lifetime
end

.token_lifetimeObject

Returns the value of attribute token_lifetime.



23
24
25
# File 'lib/opentoken-newrelic-rails23.rb', line 23

def token_lifetime
  @token_lifetime
end

Class Method Details

.debug?Boolean

Returns:

  • (Boolean)


18
19
20
# File 'lib/opentoken-newrelic-rails23.rb', line 18

def debug?
  !!debug
end

.decode(opentoken = nil) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/opentoken-newrelic-rails23.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) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/opentoken-newrelic-rails23.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