json-jwt 0.5.0 → 0.5.1

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.

Potentially problematic release.


This version of json-jwt might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7fc22bebad1c3924998f637b07f91089ba1d365f
4
- data.tar.gz: 8f969eba9420ffccaeb0053096e039c02fbcfb89
3
+ metadata.gz: 86380eefda874cbe7e7939e70fdfcffe379395e4
4
+ data.tar.gz: d2f3fc1ba0a60787927dac2d0b21d8a151488218
5
5
  SHA512:
6
- metadata.gz: a32887bed2783fb91c98776c70e6a471a6640f59b336b56d4a5576019ab79cdbdbb25bcf149bf26d4710006f03a0699c4dae0137ab61d3895ded29dbd38cdfa2
7
- data.tar.gz: 44ee98be92413ef444c90d93529566802a0725a182e55517d5ecea8e8b7b66b5d3386e3dccf7d3b67e728c038ca336f42a2bcf3837ffd2c5c20a2f73b5f0bcfc
6
+ metadata.gz: 2257bab98f399a6b5da81de323eead584846a68064024655bd58cc06c29305be08ee173b32816fb2d848b47f633c9382d6d1929bf83b2171c159f07a04ad4854
7
+ data.tar.gz: f4721c5653fd40b4895b9086ca4291e78317a135c9ec22005d9fda471cb9c07a06f278fc9d787b295345ce88f9fa09e569b1430003795e95e394d0bb3a9b33e6
data/.gitignore CHANGED
@@ -17,6 +17,7 @@ tmtags
17
17
  coverage*
18
18
  rdoc
19
19
  pkg
20
+ Gemfile.lock
20
21
 
21
22
  ## PROJECT::SPECIFIC
22
23
  .class
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.0
1
+ 0.5.1
data/lib/json/jwe.rb CHANGED
@@ -7,16 +7,22 @@ module JSON
7
7
  class DecryptionFailed < JWT::VerificationFailed; end
8
8
  class UnexpectedAlgorithm < JWT::UnexpectedAlgorithm; end
9
9
 
10
- attr_accessor :public_key_or_secret, :plain_text, :master_key, :encrypted_master_key, :encryption_key, :integrity_key, :integrity_value, :iv, :cipher_text
10
+ attr_accessor(
11
+ :public_key_or_secret, :private_key_or_secret, :mode,
12
+ :input, :plain_text, :cipher_text, :integrity_value, :iv,
13
+ :master_key, :encrypted_master_key, :encryption_key, :integrity_key
14
+ )
11
15
 
12
16
  register_header_keys :enc, :epk, :zip, :jku, :jwk, :x5u, :x5t, :x5c, :kid, :typ, :cty, :apu, :apv, :epu, :epv
13
17
  alias_method :encryption_method, :enc
14
18
 
15
- def initialize(jwt_or_plain_text)
16
- self.plain_text = jwt_or_plain_text.to_s
19
+ def initialize(input)
20
+ self.input = input.to_s
17
21
  end
18
22
 
19
23
  def encrypt!(public_key_or_secret)
24
+ self.mode = :encyption
25
+ self.plain_text = input
20
26
  self.public_key_or_secret = public_key_or_secret
21
27
  cipher.encrypt
22
28
  generate_cipher_keys!
@@ -24,24 +30,37 @@ module JSON
24
30
  self
25
31
  end
26
32
 
27
- def decrypt!
28
- raise NotImplementedError.new('JWE decryption not supported yet')
33
+ def decrypt!(private_key_or_secret)
34
+ self.mode = :decryption
35
+ self.private_key_or_secret = private_key_or_secret
36
+ decode_segments!
37
+ cipher.decrypt
38
+ restore_cipher_keys!
39
+ self.plain_text = cipher.update(cipher_text) + cipher.final
40
+ verify_cbc_integirity_value! if cbc?
41
+ self
29
42
  end
30
43
 
31
44
  def to_s
32
- [
33
- header.to_json,
34
- encrypted_master_key,
35
- iv,
36
- cipher_text,
37
- integrity_value
38
- ].collect do |segment|
39
- UrlSafeBase64.encode64 segment.to_s
40
- end.join('.')
45
+ if mode == :encyption
46
+ [
47
+ header.to_json,
48
+ encrypted_master_key,
49
+ iv,
50
+ cipher_text,
51
+ integrity_value
52
+ ].collect do |segment|
53
+ UrlSafeBase64.encode64 segment.to_s
54
+ end.join('.')
55
+ else
56
+ plain_text
57
+ end
41
58
  end
42
59
 
43
60
  private
44
61
 
62
+ # common
63
+
45
64
  def gcm_supported?
46
65
  RUBY_VERSION >= '2.0.0' && OpenSSL::OPENSSL_VERSION >= 'OpenSSL 1.0.1c'
47
66
  end
@@ -96,6 +115,44 @@ module JSON
96
115
  OpenSSL::Digest::Digest.new "SHA#{sha_size}"
97
116
  end
98
117
 
118
+ def derive_cbc_encryption_and_integirity_keys!
119
+ encryption_key_size = sha_size / 2
120
+ integrity_key_size = sha_size
121
+ encryption_segments = [
122
+ 1,
123
+ master_key,
124
+ encryption_key_size,
125
+ encryption_method,
126
+ epu || 0,
127
+ epv || 0,
128
+ 'Encryption'
129
+ ]
130
+ integrity_segments = [
131
+ 1,
132
+ master_key,
133
+ integrity_key_size,
134
+ encryption_method,
135
+ epu || 0,
136
+ epv || 0,
137
+ 'Integrity'
138
+ ]
139
+ encryption_hash_input, integrity_hash_input = [encryption_segments, integrity_segments].collect do |segments|
140
+ segments.collect do |segment|
141
+ case segment
142
+ when Integer
143
+ BinData::Int32be.new(segment).to_binary_s
144
+ else
145
+ segment.to_s
146
+ end
147
+ end.join
148
+ end
149
+ self.encryption_key = sha_digest.digest(encryption_hash_input)[0, encryption_key_size / 8]
150
+ self.integrity_key = sha_digest.digest integrity_hash_input
151
+ self
152
+ end
153
+
154
+ # encyption
155
+
99
156
  def encrypted_master_key
100
157
  @encrypted_master_key ||= case algorithm.to_s
101
158
  when :RSA1_5.to_s
@@ -126,8 +183,8 @@ module JSON
126
183
  when cbc?
127
184
  generate_cbc_keys!
128
185
  end
129
- @cipher.key = encryption_key
130
- self.iv = @cipher.random_iv
186
+ cipher.key = encryption_key
187
+ self.iv = cipher.random_iv
131
188
  if gcm?
132
189
  cipher.auth_data = [header.to_json, encrypted_master_key, iv].collect do |segment|
133
190
  UrlSafeBase64.encode64 segment.to_s
@@ -140,7 +197,7 @@ module JSON
140
197
  self.master_key ||= if dir?
141
198
  public_key_or_secret
142
199
  else
143
- @cipher.random_key
200
+ cipher.random_key
144
201
  end
145
202
  self.encryption_key = master_key
146
203
  self.integrity_key = :wont_be_used
@@ -148,50 +205,19 @@ module JSON
148
205
  end
149
206
 
150
207
  def generate_cbc_keys!
151
- encryption_key_size = sha_size / 2
152
- integrity_key_size = master_key_size = sha_size
153
208
  self.master_key ||= if dir?
154
209
  public_key_or_secret
155
210
  else
156
- SecureRandom.random_bytes master_key_size / 8
211
+ SecureRandom.random_bytes sha_size / 8
157
212
  end
158
- encryption_segments = [
159
- 1,
160
- master_key,
161
- encryption_key_size,
162
- encryption_method,
163
- epu || 0,
164
- epv || 0,
165
- 'Encryption'
166
- ]
167
- integrity_segments = [
168
- 1,
169
- master_key,
170
- integrity_key_size,
171
- encryption_method,
172
- epu || 0,
173
- epv || 0,
174
- 'Integrity'
175
- ]
176
- encryption_hash_input, integrity_hash_input = [encryption_segments, integrity_segments].collect do |segments|
177
- segments.collect do |segment|
178
- case segment
179
- when Integer
180
- BinData::Int32be.new(segment).to_binary_s
181
- else
182
- segment.to_s
183
- end
184
- end.join
185
- end
186
- self.encryption_key = sha_digest.digest(encryption_hash_input)[0, encryption_key_size / 8]
187
- self.integrity_key = sha_digest.digest integrity_hash_input
188
- self
213
+ derive_cbc_encryption_and_integirity_keys!
189
214
  end
190
215
 
191
216
  def integrity_value
192
- @integrity_value ||= if gcm?
217
+ @integrity_value ||= case
218
+ when gcm?
193
219
  cipher.auth_tag
194
- else
220
+ when cbc?
195
221
  secured_input = [
196
222
  header.to_json,
197
223
  encrypted_master_key,
@@ -202,7 +228,59 @@ module JSON
202
228
  end.join('.')
203
229
  OpenSSL::HMAC.digest sha_digest, integrity_key, secured_input
204
230
  end
205
- @integrity_value
231
+ end
232
+
233
+ # decryption
234
+
235
+ def decode_segments!
236
+ _header_json_, self.encrypted_master_key, self.iv, self.cipher_text, self.integrity_value = input.split('.').collect do |segment|
237
+ UrlSafeBase64.decode64 segment
238
+ end
239
+ self
240
+ end
241
+
242
+ def decrypt_master_key
243
+ case algorithm.to_s
244
+ when :RSA1_5.to_s
245
+ private_key_or_secret.private_decrypt encrypted_master_key
246
+ when :'RSA-OAEP'.to_s
247
+ private_key_or_secret.private_decrypt encrypted_master_key, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING
248
+ when :A128KW .to_s
249
+ raise NotImplementedError.new('A128KW not supported yet')
250
+ when :A256KW.to_s
251
+ raise NotImplementedError.new('A256KW not supported yet')
252
+ when :dir.to_s
253
+ private_key_or_secret
254
+ when :'ECDH-ES'.to_s
255
+ raise NotImplementedError.new('ECDH-ES not supported yet')
256
+ when :'ECDH-ES+A128KW'.to_s
257
+ raise NotImplementedError.new('ECDH-ES+A128KW not supported yet')
258
+ when :'ECDH-ES+A256KW'.to_s
259
+ raise NotImplementedError.new('ECDH-ES+A256KW not supported yet')
260
+ else
261
+ raise UnexpectedAlgorithm.new('Unknown Encryption Algorithm')
262
+ end
263
+ end
264
+
265
+ def restore_cipher_keys!
266
+ self.master_key = decrypt_master_key
267
+ case
268
+ when gcm?
269
+ self.encryption_key = master_key
270
+ self.integrity_key = :wont_be_used
271
+ when cbc?
272
+ derive_cbc_encryption_and_integirity_keys!
273
+ end
274
+ cipher.key = encryption_key
275
+ cipher.iv = iv # NOTE: 'iv' has to be set after 'key' for GCM
276
+ if gcm?
277
+ cipher.auth_tag = integrity_value
278
+ cipher.auth_data = input.split('.')[0, 3].join('.')
279
+ end
280
+ end
281
+
282
+ def verify_cbc_integirity_value!
283
+ # raise UnexpectedAlgorithm.new('TODO')
206
284
  end
207
285
  end
208
286
  end
@@ -154,4 +154,106 @@ describe JSON::JWE do
154
154
  end
155
155
  end
156
156
  end
157
+
158
+ describe 'decrypt!' do
159
+ let(:plain_text) { 'Hello World' }
160
+ let(:input) do
161
+ _jwe_ = JSON::JWE.new plain_text
162
+ _jwe_.alg, _jwe_.enc = alg, enc
163
+ _jwe_.encrypt! key
164
+ _jwe_.to_s
165
+ end
166
+ let(:jwe) do
167
+ _jwe_ = JSON::JWE.new input
168
+ _jwe_.alg, _jwe_.enc = alg, enc
169
+ _jwe_
170
+ end
171
+
172
+ shared_examples_for :decryptable do
173
+ it do
174
+ jwe.decrypt! key
175
+ jwe.to_s.should == plain_text
176
+ end
177
+ end
178
+
179
+ shared_examples_for :gcm_decryption_unsupported do
180
+ it do
181
+ expect do
182
+ jwe.decrypt! key
183
+ end.to raise_error JSON::JWE::UnexpectedAlgorithm
184
+ end
185
+ end
186
+
187
+ context 'when alg=RSA1_5' do
188
+ let(:alg) { :RSA1_5 }
189
+ let(:key) { private_key }
190
+
191
+ context 'when enc=A128GCM' do
192
+ let(:enc) { :A128GCM }
193
+ if gcm_supported?
194
+ it_behaves_like :decryptable
195
+ else
196
+ it_behaves_like :gcm_decryption_unsupported
197
+ end
198
+ end
199
+
200
+ context 'when enc=A256GCM' do
201
+ let(:enc) { :A256GCM }
202
+ if gcm_supported?
203
+ it_behaves_like :decryptable
204
+ else
205
+ it_behaves_like :gcm_decryption_unsupported
206
+ end
207
+ end
208
+
209
+ context 'when enc=A128CBC+HS256' do
210
+ let(:enc) { :'A128CBC+HS256' }
211
+ it_behaves_like :decryptable
212
+ end
213
+
214
+ context 'when enc=A256CBC+HS512' do
215
+ let(:enc) { :'A256CBC+HS512' }
216
+ it_behaves_like :decryptable
217
+ end
218
+ end
219
+
220
+ context 'when alg=RSA-OAEP' do
221
+ let(:alg) { :'RSA-OAEP' }
222
+ let(:key) { private_key }
223
+
224
+ context 'when enc=A128GCM' do
225
+ let(:enc) { :A128GCM }
226
+ if gcm_supported?
227
+ it_behaves_like :decryptable
228
+ else
229
+ it_behaves_like :gcm_decryption_unsupported
230
+ end
231
+ end
232
+
233
+ context 'when enc=A256GCM' do
234
+ let(:enc) { :A256GCM }
235
+ if gcm_supported?
236
+ it_behaves_like :decryptable
237
+ else
238
+ it_behaves_like :gcm_decryption_unsupported
239
+ end
240
+ end
241
+
242
+ context 'when enc=A128CBC+HS256' do
243
+ let(:enc) { :'A128CBC+HS256' }
244
+ it_behaves_like :decryptable
245
+ end
246
+
247
+ context 'when enc=A256CBC+HS512' do
248
+ let(:enc) { :'A256CBC+HS512' }
249
+ it_behaves_like :decryptable
250
+ end
251
+ end
252
+
253
+ context 'when alg=dir' do
254
+ let(:alg) { :dir }
255
+ let(:key) { 'todo' }
256
+ it :TODO
257
+ end
258
+ end
157
259
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json-jwt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - nov matake
@@ -135,7 +135,6 @@ files:
135
135
  - .rspec
136
136
  - .travis.yml
137
137
  - Gemfile
138
- - Gemfile.lock
139
138
  - LICENSE
140
139
  - README.rdoc
141
140
  - Rakefile
@@ -182,7 +181,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
182
181
  version: '0'
183
182
  requirements: []
184
183
  rubyforge_project:
185
- rubygems_version: 2.0.0
184
+ rubygems_version: 2.0.3
186
185
  signing_key:
187
186
  specification_version: 4
188
187
  summary: JSON Web Token and its family (JSON Web Signature, JSON Web Encryption and
@@ -205,4 +204,3 @@ test_files:
205
204
  - spec/json/jws_spec.rb
206
205
  - spec/json/jwt_spec.rb
207
206
  - spec/spec_helper.rb
208
- has_rdoc:
data/Gemfile.lock DELETED
@@ -1,44 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- json-jwt (0.4.3)
5
- activesupport (>= 2.3)
6
- i18n
7
- multi_json (>= 1.3)
8
- url_safe_base64
9
-
10
- GEM
11
- remote: http://rubygems.org/
12
- specs:
13
- activesupport (3.2.12)
14
- i18n (~> 0.6)
15
- multi_json (~> 1.0)
16
- configatron (2.10.0)
17
- yamler (>= 0.1.0)
18
- cover_me (1.2.0)
19
- configatron
20
- hashie
21
- diff-lcs (1.2.1)
22
- hashie (2.0.2)
23
- i18n (0.6.4)
24
- multi_json (1.6.1)
25
- rake (10.0.3)
26
- rspec (2.13.0)
27
- rspec-core (~> 2.13.0)
28
- rspec-expectations (~> 2.13.0)
29
- rspec-mocks (~> 2.13.0)
30
- rspec-core (2.13.0)
31
- rspec-expectations (2.13.0)
32
- diff-lcs (>= 1.1.3, < 2.0)
33
- rspec-mocks (2.13.0)
34
- url_safe_base64 (0.2.1)
35
- yamler (0.1.0)
36
-
37
- PLATFORMS
38
- ruby
39
-
40
- DEPENDENCIES
41
- cover_me (>= 1.2.0)
42
- json-jwt!
43
- rake (>= 0.8)
44
- rspec (>= 2)