opentoken 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -15,7 +15,7 @@ attributes = OpenToken.decode 'opentoken-hashed-string'
15
15
 
16
16
  # encrypt opentoken from hash of attributes
17
17
  attributes = { 'subject' => 'foo', 'bar' => 'bak' }
18
- token = OpenToken.encode attributes, OpenToken::CIPHER_AES_128_CBC
18
+ token = OpenToken.encode attributes, OpenToken::Cipher::AES_128_CBC
19
19
  ```
20
20
 
21
21
  ## Contributing
@@ -8,36 +8,11 @@ require 'time'
8
8
  require File.join(File.dirname(__FILE__), 'opentoken', 'token')
9
9
  require File.join(File.dirname(__FILE__), 'opentoken', 'key_value_serializer')
10
10
  require File.join(File.dirname(__FILE__), 'opentoken', 'password_key_generator')
11
+ require File.join(File.dirname(__FILE__), 'opentoken', 'cipher')
11
12
 
12
13
  module OpenToken
13
14
  class TokenInvalidError < StandardError; end
14
15
 
15
- CIPHER_NULL = 0
16
- CIPHER_AES_256_CBC = 1
17
- CIPHER_AES_128_CBC = 2
18
- CIPHER_3DES_168_CBC = 3
19
-
20
- CIPHERS = {
21
- CIPHER_NULL => {
22
- :iv_length => 0
23
- },
24
- CIPHER_AES_256_CBC => {
25
- :algorithm => 'aes-256-cbc',
26
- :iv_length => 32,
27
- :key_length => 256
28
- },
29
- CIPHER_AES_128_CBC => {
30
- :algorithm => 'aes-128-cbc',
31
- :iv_length => 16,
32
- :key_length => 128
33
- },
34
- CIPHER_3DES_168_CBC => {
35
- :algorithm => 'des-cbc',
36
- :iv_length => 8,
37
- :key_length => 168
38
- }
39
- }
40
-
41
16
  class << self
42
17
  attr_accessor :debug
43
18
  def debug?
@@ -48,39 +23,29 @@ module OpenToken
48
23
  attr_accessor :token_lifetime
49
24
  attr_accessor :renew_until_lifetime
50
25
 
51
- def encode(attributes, cipher_suite)
26
+ def encode(attributes, cipher)
52
27
  attributes['not-before'] = Time.now.utc.iso8601.to_s
53
28
  attributes['not-on-or-after'] = Time.at(Time.now.to_i + token_lifetime).utc.iso8601.to_s
54
29
  attributes['renew-until'] = Time.at(Time.now.to_i + renew_until_lifetime).utc.iso8601.to_s
55
30
 
56
- cipher = CIPHERS[cipher_suite]
57
- verify !cipher.nil?, "Unknown cipher suite: #{cipher_suite}"
58
- key = OpenToken::PasswordKeyGenerator.generate(password, cipher)
59
- c = OpenSSL::Cipher::Cipher::new(cipher[:algorithm])
60
- c.encrypt
61
- c.key = key
62
- c.iv = iv = c.random_iv
63
31
  serialized = OpenToken::KeyValueSerializer.serialize(attributes)
64
32
  compressed = zip_payload serialized
65
- ivlen = cipher[:iv_length]
66
- if ((compressed.length % ivlen) == 0)
67
- padlen = ivlen
68
- else
69
- padlen = ivlen - (compressed.length % ivlen)
70
- end
71
- compressed += padlen.chr * padlen
72
- encrypted = c.update(compressed)
33
+
34
+ key = cipher.generate_key
35
+ iv = cipher.generate_iv
36
+ encrypted = cipher.encrypt_payload compressed, key, iv
37
+
73
38
  mac = []
74
39
  mac << "0x01".hex.chr # OTK version
75
- mac << cipher_suite.chr
40
+ mac << cipher.suite.chr
76
41
  mac << iv
77
42
  mac << serialized
78
43
  hash = OpenSSL::HMAC.digest(OpenToken::PasswordKeyGenerator::SHA1_DIGEST, key, mac.join)
79
44
 
80
45
  token_string = ""
81
- token_string = "OTK" + 1.chr + cipher_suite.chr
46
+ token_string = "OTK" + 1.chr + cipher.suite.chr
82
47
  token_string += hash
83
- token_string += ivlen.chr
48
+ token_string += cipher.iv_length.chr
84
49
  token_string += iv
85
50
  token_string += 0.chr # key info length
86
51
  token_string += ((encrypted.length >> 8) &0xFF ).chr
@@ -101,8 +66,7 @@ module OpenToken
101
66
 
102
67
  #cipher suite identifier
103
68
  cipher_suite = char_value_of data[4]
104
- cipher = CIPHERS[cipher_suite]
105
- verify !cipher.nil?, "Unknown cipher suite: #{cipher_suite}"
69
+ cipher = OpenToken::Cipher.for_suite cipher_suite
106
70
 
107
71
  #SHA-1 HMAC
108
72
  payload_hmac = data[5..24]
@@ -113,7 +77,7 @@ module OpenToken
113
77
  iv_end = char_value_of [26, 26 + iv_length - 1].max
114
78
  iv = data[26..iv_end]
115
79
  inspect_binary_string "IV [26..#{iv_end}]", iv
116
- verify iv_length == cipher[:iv_length], "Cipher expects iv length of #{cipher[:iv_length]} and was: #{iv_length}"
80
+ verify iv_length == cipher.iv_length, "Cipher expects iv length of #{cipher.iv_length} and was: #{iv_length}"
117
81
 
118
82
  #key (not currently used)
119
83
  key_length = char_value_of data[iv_end + 1]
@@ -127,10 +91,10 @@ module OpenToken
127
91
  verify encrypted_payload.length == payload_length, "Payload length is #{encrypted_payload.length} and was expected to be #{payload_length}"
128
92
  inspect_binary_string "ENCRYPTED PAYLOAD [#{payload_offset}..#{data.length - 1}]", encrypted_payload
129
93
 
130
- key = OpenToken::PasswordKeyGenerator.generate(password, cipher)
94
+ key = cipher.generate_key
131
95
  inspect_binary_string 'KEY', key
132
96
 
133
- compressed_payload = decrypt_payload(encrypted_payload, cipher, key, iv)
97
+ compressed_payload = cipher.decrypt_payload encrypted_payload, key, iv
134
98
  inspect_binary_string 'COMPRESSED PAYLOAD', compressed_payload
135
99
 
136
100
  unparsed_payload = unzip_payload compressed_payload
@@ -185,17 +149,6 @@ module OpenToken
185
149
  def verify(assertion, message = 'Invalid Token')
186
150
  raise OpenToken::TokenInvalidError.new(message) unless assertion
187
151
  end
188
- #see http://snippets.dzone.com/posts/show/4975
189
- #see http://jdwyah.blogspot.com/2009/12/decrypting-ruby-aes-encryption.html
190
- #see http://snippets.dzone.com/posts/show/576
191
- def decrypt_payload(encrypted_payload, cipher, key, iv)
192
- return encrypted_payload unless cipher[:algorithm]
193
- crypt = OpenSSL::Cipher::Cipher.new(cipher[:algorithm])
194
- crypt.decrypt
195
- crypt.key = key
196
- crypt.iv = iv
197
- crypt.update(encrypted_payload) + crypt.final
198
- end
199
152
  #decompress the payload
200
153
  #see http://stackoverflow.com/questions/1361892/how-to-decompress-gzip-data-in-ruby
201
154
  def unzip_payload(compressed_payload)
@@ -0,0 +1,69 @@
1
+ require 'openssl'
2
+
3
+ module OpenToken
4
+ class Cipher
5
+ class InvalidCipherError < StandardError; end
6
+
7
+ attr_reader :algorithm
8
+ attr_reader :iv_length
9
+ attr_reader :key_length
10
+ attr_reader :suite
11
+
12
+ def initialize(attrs = {})
13
+ @suite = attrs[:suite]
14
+ @iv_length = attrs[:iv_length]
15
+ @key_length = attrs[:key_length]
16
+ @algorithm = attrs[:algorithm]
17
+ end
18
+ def self.for_suite(cipher_suite)
19
+ cipher = REGISTERED_CIPHERS.detect {|c| c.suite == cipher_suite }
20
+ raise InvalidCipherError.new("Unknown cipher suite: #{cipher_suite}") unless cipher
21
+ cipher
22
+ end
23
+
24
+ def generate_key
25
+ OpenToken::PasswordKeyGenerator.generate OpenToken.password, self
26
+ end
27
+ def generate_iv
28
+ OpenSSL::Random.random_bytes(iv_length)
29
+ end
30
+
31
+ #see http://snippets.dzone.com/posts/show/4975
32
+ #see http://jdwyah.blogspot.com/2009/12/decrypting-ruby-aes-encryption.html
33
+ #see http://snippets.dzone.com/posts/show/576
34
+ def decrypt_payload(encrypted_payload, key, iv)
35
+ return encrypted_payload unless algorithm
36
+ c = crypt :decrypt, key, iv
37
+ c.update(encrypted_payload) + c.final
38
+ end
39
+ def encrypt_payload(payload, key, iv)
40
+ c = crypt :encrypt, key, iv
41
+ padding = if payload.length % iv_length == 0
42
+ iv_length
43
+ else
44
+ iv_length - (payload.length % iv_length)
45
+ end
46
+ c.update(payload + (padding.chr * padding))
47
+ end
48
+
49
+ private
50
+ def crypt(operation, key, iv)
51
+ crypt = OpenSSL::Cipher::Cipher.new(algorithm)
52
+ crypt.send operation
53
+ crypt.key = key
54
+ crypt.iv = iv
55
+ crypt
56
+ end
57
+
58
+ NULL = Cipher.new(:suite => 0, :iv_length => 0)
59
+ AES_256_CBC = Cipher.new(:suite => 1, :iv_length => 32, :key_length => 256, :algorithm => 'aes-256-cbc')
60
+ AES_128_CBC = Cipher.new(:suite => 2, :iv_length => 16, :key_length => 128, :algorithm => 'aes-128-cbc')
61
+ DES3_168_CBC = Cipher.new(:suite => 3, :iv_length => 8, :key_length => 168, :algorithm => 'des-cbc')
62
+
63
+ REGISTERED_CIPHERS = []
64
+ REGISTERED_CIPHERS << NULL
65
+ REGISTERED_CIPHERS << AES_256_CBC
66
+ REGISTERED_CIPHERS << AES_128_CBC
67
+ REGISTERED_CIPHERS << DES3_168_CBC
68
+ end
69
+ end
@@ -3,9 +3,9 @@ module OpenToken
3
3
  SHA1_DIGEST = OpenSSL::Digest::Digest.new('sha1')
4
4
 
5
5
  class << self
6
- def generate(password, cipher_suite)
6
+ def generate(password, cipher)
7
7
  salt = 0.chr * 8
8
- generate_impl(password, cipher_suite, salt, 1000)
8
+ generate_impl(password, cipher, salt, 1000)
9
9
  end
10
10
 
11
11
  private
@@ -35,9 +35,9 @@ module OpenToken
35
35
  end
36
36
 
37
37
  def generate_impl(password, cipher, salt, iterations)
38
- return unless cipher[:algorithm]
38
+ return unless cipher.algorithm
39
39
 
40
- key_size = cipher[:key_length] / 8
40
+ key_size = cipher.key_length / 8
41
41
  numblocks = key_size / 20
42
42
  numblocks += 1 if (key_size % 20) > 0
43
43
 
@@ -1,3 +1,3 @@
1
1
  module OpenToken
2
- VERSION = '1.1.0'
2
+ VERSION = '1.2.0'
3
3
  end
@@ -1,6 +1,8 @@
1
1
  require 'helper'
2
2
 
3
3
  class TestOpentoken < Test::Unit::TestCase
4
+ # OpenToken.debug = true
5
+
4
6
  #"renew-until"=>"2010-03-05T07:19:15Z"
5
7
  #"not-before"=>"2010-03-04T19:19:15Z"
6
8
  #"not-on-or-after"=>"2010-03-04T19:24:15Z"
@@ -75,7 +77,7 @@ class TestOpentoken < Test::Unit::TestCase
75
77
  context "with aes-128-cbc and subject attribute" do
76
78
  setup do
77
79
  @attributesIn = { "subject" => "john", "email" => "john@example.com"}
78
- @token = OpenToken.encode @attributesIn, OpenToken::CIPHER_AES_128_CBC
80
+ @token = OpenToken.encode @attributesIn, OpenToken::Cipher::AES_128_CBC
79
81
  end
80
82
  should "be decodable" do
81
83
  @attributesOut = OpenToken.decode @token
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opentoken
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 31
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
- - 1
8
+ - 2
9
9
  - 0
10
- version: 1.1.0
10
+ version: 1.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Ryan Sonnek
@@ -95,6 +95,7 @@ files:
95
95
  - README.md
96
96
  - Rakefile
97
97
  - lib/opentoken.rb
98
+ - lib/opentoken/cipher.rb
98
99
  - lib/opentoken/key_value_serializer.rb
99
100
  - lib/opentoken/password_key_generator.rb
100
101
  - lib/opentoken/token.rb