opentoken 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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