sandal 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -1
- data/README.md +10 -25
- data/lib/sandal.rb +79 -40
- data/lib/sandal/claims.rb +30 -18
- data/lib/sandal/enc/acbc_hs.rb +36 -26
- data/lib/sandal/enc/agcm.rb +17 -11
- data/lib/sandal/enc/alg/direct.rb +5 -3
- data/lib/sandal/enc/alg/rsa1_5.rb +7 -4
- data/lib/sandal/enc/alg/rsa_oaep.rb +7 -4
- data/lib/sandal/sig.rb +4 -1
- data/lib/sandal/sig/es.rb +27 -18
- data/lib/sandal/sig/hs.rb +4 -2
- data/lib/sandal/sig/rs.rb +30 -12
- data/lib/sandal/util.rb +39 -20
- data/lib/sandal/version.rb +1 -1
- data/sandal.gemspec +1 -0
- data/spec/helper.rb +13 -1
- data/spec/sandal/claims_spec.rb +213 -0
- data/spec/sandal/enc/a128cbc_hs256_spec.rb +6 -42
- data/spec/sandal/enc/a128gcm_spec.rb +2 -15
- data/spec/sandal/enc/a256cbc_hs512_spec.rb +10 -0
- data/spec/sandal/enc/a256gcm_spec.rb +52 -0
- data/spec/sandal/enc/alg/direct_spec.rb +55 -0
- data/spec/sandal/enc/shared_examples.rb +37 -0
- data/spec/sandal/sig/es_spec.rb +86 -6
- data/spec/sandal/sig/rs_spec.rb +71 -0
- data/spec/sandal/util_spec.rb +62 -22
- data/spec/sandal_spec.rb +18 -6
- metadata +26 -2
data/sandal.gemspec
CHANGED
@@ -23,6 +23,7 @@ Gem::Specification.new do |s|
|
|
23
23
|
s.add_development_dependency 'bundler', '>= 1.3'
|
24
24
|
s.add_development_dependency 'rake', '>= 10.0'
|
25
25
|
s.add_development_dependency 'rspec', '>= 2.13'
|
26
|
+
s.add_development_dependency 'simplecov', '>= 0.7'
|
26
27
|
s.add_development_dependency 'coveralls', '>= 0.6'
|
27
28
|
s.add_development_dependency 'yard', '>= 0.8'
|
28
29
|
s.add_development_dependency 'redcarpet', '>= 2.2' unless RUBY_PLATFORM == 'java' # for yard
|
data/spec/helper.rb
CHANGED
@@ -1,9 +1,21 @@
|
|
1
1
|
require 'coveralls'
|
2
|
-
|
2
|
+
require 'simplecov'
|
3
|
+
|
4
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
5
|
+
SimpleCov::Formatter::HTMLFormatter,
|
6
|
+
Coveralls::SimpleCov::Formatter]
|
7
|
+
SimpleCov.start do
|
8
|
+
add_filter 'spec/'
|
9
|
+
add_group 'Encryption', 'sandal/enc'
|
10
|
+
add_group 'Signatures', 'sandal/sig'
|
11
|
+
end
|
12
|
+
|
3
13
|
|
4
14
|
require 'rspec'
|
15
|
+
|
5
16
|
RSpec.configure do |c|
|
6
17
|
c.treat_symbols_as_metadata_keys_with_true_values = true
|
18
|
+
c.filter_run_excluding :timing_dependent # these are unreliable so don't run unless specified explicitly
|
7
19
|
c.filter_run_excluding :jruby_incompatible if RUBY_PLATFORM == 'java'
|
8
20
|
end
|
9
21
|
|
@@ -0,0 +1,213 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Sandal::Claims do
|
4
|
+
|
5
|
+
context '#validate_claims' do
|
6
|
+
|
7
|
+
it 'calls #validate_aud when valid audiences are provided' do
|
8
|
+
claims = { 'aud' => 'example.org' }.extend(Sandal::Claims)
|
9
|
+
valid_aud = %w(example.org)
|
10
|
+
claims.should_receive(:validate_aud).with(valid_aud)
|
11
|
+
claims.validate_claims(valid_aud: valid_aud)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'calls #validate_exp by default' do
|
15
|
+
claims = {}.extend(Sandal::Claims)
|
16
|
+
claims.should_receive(:validate_exp)
|
17
|
+
claims.validate_claims
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'does not call #validate_exp when the :ignore_exp option is set' do
|
21
|
+
claims = {}.extend(Sandal::Claims)
|
22
|
+
claims.should_not_receive(:validate_exp)
|
23
|
+
claims.validate_claims(ignore_exp: true)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'calls #validate_iss when valid issuers are provided' do
|
27
|
+
claims = { 'iss' => 'example.org' }.extend(Sandal::Claims)
|
28
|
+
valid_iss = %w(example.org)
|
29
|
+
claims.should_receive(:validate_iss).with(valid_iss)
|
30
|
+
claims.validate_claims(valid_iss: valid_iss)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'calls #validate_nbf by default' do
|
34
|
+
claims = {}.extend(Sandal::Claims)
|
35
|
+
claims.should_receive(:validate_nbf)
|
36
|
+
claims.validate_claims
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'does not call #validate_nbf when the :ignore_nbf option is set' do
|
40
|
+
claims = {}.extend(Sandal::Claims)
|
41
|
+
claims.should_not_receive(:validate_nbf)
|
42
|
+
claims.validate_claims(ignore_nbf: true)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
context '#validate_aud' do
|
48
|
+
|
49
|
+
it 'succeeds when the audience claim is missing and no valid audiences are given' do
|
50
|
+
claims = {}.extend(Sandal::Claims)
|
51
|
+
claims.validate_aud([])
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'succeeds when the audience string is empty and no valid audiences are given' do
|
55
|
+
claims = { 'aud' => '' }.extend(Sandal::Claims)
|
56
|
+
claims.validate_aud([])
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'succeeds when the audience string is the same as the valid audience' do
|
60
|
+
claims = { 'aud' => 'example.org' }.extend(Sandal::Claims)
|
61
|
+
claims.validate_aud(%w(example.org))
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'succeeds when the audience string is the same as a valid audience' do
|
65
|
+
claims = { 'aud' => 'example.org' }.extend(Sandal::Claims)
|
66
|
+
claims.validate_aud(%w(example.org example.com))
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'succeeds when the audience array is empty and no valid audiences are given' do
|
70
|
+
claims = { 'aud' => [] }.extend(Sandal::Claims)
|
71
|
+
claims.validate_aud([])
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'succeeds when the audience array is the same as the valid audience' do
|
75
|
+
claims = { 'aud' => %w(example.org) }.extend(Sandal::Claims)
|
76
|
+
claims.validate_aud(%w(example.org))
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'succeeds when the audience array contains a valid audience' do
|
80
|
+
claims = { 'aud' => %w(example.org example.com) }.extend(Sandal::Claims)
|
81
|
+
claims.validate_aud(%w(example.org))
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'succeeds when the audience array contains multiple valid audiences' do
|
85
|
+
claims = { 'aud' => %w(example.org example.com example.net) }.extend(Sandal::Claims)
|
86
|
+
claims.validate_aud(%w(example.com example.org))
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'raises a ClaimError when the audience claim is missing and a valid audience is given' do
|
90
|
+
claims = {}.extend(Sandal::Claims)
|
91
|
+
expect { claims.validate_aud(%w(example.org)) }.to raise_error Sandal::ClaimError
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'raises a ClaimError when the audience string is empty and a valid audience is given' do
|
95
|
+
claims = { 'aud' => '' }.extend(Sandal::Claims)
|
96
|
+
expect { claims.validate_aud(%w(example.org)) }.to raise_error Sandal::ClaimError
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'raises a ClaimError when the audience string does not contain a valid audience' do
|
100
|
+
claims = { 'aud' => 'example.com' }.extend(Sandal::Claims)
|
101
|
+
expect { claims.validate_aud(%w(example.org example.net)) }.to raise_error Sandal::ClaimError
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'raises a ClaimError when the audience array is empty and a valid audience is given' do
|
105
|
+
claims = { 'aud' => [] }.extend(Sandal::Claims)
|
106
|
+
expect { claims.validate_aud(%w(example.org)) }.to raise_error Sandal::ClaimError
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'raises a ClaimError when the audience array does not contain a valid audience' do
|
110
|
+
claims = { 'aud' => %w(example.com example.net) }.extend(Sandal::Claims)
|
111
|
+
expect { claims.validate_aud(%w(example.org)) }.to raise_error Sandal::ClaimError
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
context '#validate_exp' do
|
117
|
+
|
118
|
+
it 'succeeds when the expires claim is missing' do
|
119
|
+
claims = {}.extend(Sandal::Claims)
|
120
|
+
claims.validate_exp
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'succeeds when the expiry time is in the future' do
|
124
|
+
claims = { 'exp' => (Time.now + 300).to_i }.extend(Sandal::Claims)
|
125
|
+
claims.validate_exp
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'succeeds when the expiry time is in the past but within the max clock skew' do
|
129
|
+
claims = { 'exp' => (Time.now - 60).to_i }.extend(Sandal::Claims)
|
130
|
+
claims.validate_exp(120)
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'raises a ClaimError when the expiry time is in the past with no clock skew' do
|
134
|
+
claims = { 'exp' => (Time.now - 300).to_i }.extend(Sandal::Claims)
|
135
|
+
expect { claims.validate_exp }.to raise_error Sandal::ClaimError
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'raises a ClaimError when the expiry time is in the past and outside the max clock skew' do
|
139
|
+
claims = { 'exp' => (Time.now - 300).to_i }.extend(Sandal::Claims)
|
140
|
+
expect { claims.validate_exp(120) }.to raise_error Sandal::ClaimError
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
context '#validate_iss' do
|
146
|
+
|
147
|
+
it 'succeeds when the issuer claim is missing and no valid issuers are given' do
|
148
|
+
claims = {}.extend(Sandal::Claims)
|
149
|
+
claims.validate_iss([])
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'succeeds when the issuer string is empty and no valid issuers are given' do
|
153
|
+
claims = { 'iss' => '' }.extend(Sandal::Claims)
|
154
|
+
claims.validate_iss([])
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'succeeds when the issuer string is the same as the valid issuer' do
|
158
|
+
claims = { 'iss' => 'example.org' }.extend(Sandal::Claims)
|
159
|
+
claims.validate_iss(%w(example.org))
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'succeeds when the issuer string is the same as a valid issuer' do
|
163
|
+
claims = { 'iss' => 'example.org' }.extend(Sandal::Claims)
|
164
|
+
claims.validate_iss(%w(example.org example.com))
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'raises a ClaimError when the issuer claim is missing and a valid issuer is given' do
|
168
|
+
claims = {}.extend(Sandal::Claims)
|
169
|
+
expect { claims.validate_iss(%w(example.org)) }.to raise_error Sandal::ClaimError
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'raises a ClaimError when the issuer string is empty and a valid issuer is given' do
|
173
|
+
claims = { 'iss' => '' }.extend(Sandal::Claims)
|
174
|
+
expect { claims.validate_iss(%w(example.org)) }.to raise_error Sandal::ClaimError
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'raises a ClaimError when the issuer string is not a valid issuer' do
|
178
|
+
claims = { 'iss' => 'example.com' }.extend(Sandal::Claims)
|
179
|
+
expect { claims.validate_iss(%w(example.org example.net)) }.to raise_error Sandal::ClaimError
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
context '#validate_nbf' do
|
185
|
+
|
186
|
+
it 'succeeds when the not-before claim is missing' do
|
187
|
+
claims = {}.extend(Sandal::Claims)
|
188
|
+
claims.validate_nbf
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'succeeds when the not-before time is in the past' do
|
192
|
+
claims = { 'nbf' => (Time.now - 300).to_i }.extend(Sandal::Claims)
|
193
|
+
claims.validate_nbf
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'succeeds when the not-before time is in the future but within the max clock skew' do
|
197
|
+
claims = { 'nbf' => (Time.now + 60).to_i }.extend(Sandal::Claims)
|
198
|
+
claims.validate_nbf(120)
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'raises a ClaimError when the not-before time is in the future with no clock skew' do
|
202
|
+
claims = { 'nbf' => (Time.now + 300).to_i }.extend(Sandal::Claims)
|
203
|
+
expect { claims.validate_nbf }.to raise_error Sandal::ClaimError
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'raises a ClaimError when the not-before time is in the future and outside the max clock skew' do
|
207
|
+
claims = { 'nbf' => (Time.now + 300).to_i }.extend(Sandal::Claims)
|
208
|
+
expect { claims.validate_nbf(120) }.to raise_error Sandal::ClaimError
|
209
|
+
end
|
210
|
+
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
require 'helper'
|
2
2
|
require 'openssl'
|
3
|
-
|
3
|
+
require_relative 'shared_examples'
|
4
4
|
|
5
5
|
# TODO: These tests are really for the Sandal module rather than just the algorithm -- move them!
|
6
6
|
|
7
7
|
describe Sandal::Enc::A128CBC_HS256 do
|
8
|
+
include_examples 'algorithm compatibility', Sandal::Enc::A128CBC_HS256
|
8
9
|
|
9
10
|
# these tests don't run with jruby as it errors when you try and set rsa.d parameter directly
|
10
11
|
context 'using the example RSA key from JWE section A.2', :jruby_incompatible do
|
@@ -20,7 +21,7 @@ describe Sandal::Enc::A128CBC_HS256 do
|
|
20
21
|
token = 'eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDK0hTMjU2In0.ZmnlqWgjXyqwjr7cXHys8F79anIUI6J2UWdAyRQEcGBU-KPHsePM910_RoTDGu1IW40Dn0dvcdVEjpJcPPNIbzWcMxDi131Ejeg-b8ViW5YX5oRdYdiR4gMSDDB3mbkInMNUFT-PK5CuZRnHB2rUK5fhPuF6XFqLLZCG5Q_rJm6Evex-XLcNQAJNa1-6CIU12Wj3mPExxw9vbnsQDU7B4BfmhdyiflLA7Ae5ZGoVRl3A__yLPXxRjHFhpOeDp_adx8NyejF5cz9yDKULugNsDMdlHeJQOMGVLYaSZt3KP6aWNSqFA1PHDg-10ceuTEtq_vPE4-Gtev4N4K4Eudlj4Q.AxY8DCtDaGlsbGljb3RoZQ.Rxsjg6PIExcmGSF7LnSEkDqWIKfAw1wZz2XpabV5PwQsolKwEauWYZNE9Q1hZJEZ.8LXqMd0JLGsxMaB5uoNaMpg7uUW_p40RlaZHCwMIyzk'
|
21
22
|
payload = Sandal.decrypt_token(token) do |header|
|
22
23
|
alg = Sandal::Enc::Alg::RSA1_5.new(@rsa)
|
23
|
-
|
24
|
+
Sandal::Enc::A128CBC_HS256.new(alg)
|
24
25
|
end
|
25
26
|
payload.should == 'No matter where you go, there you are.'
|
26
27
|
end
|
@@ -29,7 +30,7 @@ describe Sandal::Enc::A128CBC_HS256 do
|
|
29
30
|
token = 'eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDK0hTMjU2In0.ZmnlqWgjXyqwjr7cXHys8F79anIUI6J2UWdAyRQEcGBU-KPHsePM910_RoTDGu1IW40Dn0dvcdVEjpJcPPNIbzWcMxDi131Ejeg-b8ViW5YX5oRdYdiR4gMSDDB3mbkInMNUFT-PK5CuZRnHB2rUK5fhPuF6XFqLLZCG5Q_rJm6Evex-XLcNQAJNa1-6CIU12Wj3mPExxw9vbnsQDU7B4BfmhdyiflLA7Ae5ZGoVRl3A__yLPXxRjHFhpOeDp_adx8NyejF5cz9yDKULugNsDMdlHeJQOMGVLYaSZt3KP6aWNSqFA1PHDg-10ceuTEtq_vPE4-Gtev4N4K4Eudlj4Q.AxY8DCtDaGlsbGljb3RoZQ.Rxsjg6PIExcmGSF7LnSEkDqWIKfAw1wZz2XpabV5PwQsolKwEauWYZNE9Q1hZJEZ.7V5ZDko0v_mf2PAc4JMiUg'
|
30
31
|
expect { Sandal.decrypt_token(token) do |header|
|
31
32
|
alg = Sandal::Enc::Alg::RSA1_5.new(@rsa)
|
32
|
-
|
33
|
+
Sandal::Enc::A128CBC_HS256.new(alg)
|
33
34
|
end }.to raise_error Sandal::TokenError, 'Invalid integrity value.'
|
34
35
|
end
|
35
36
|
|
@@ -40,45 +41,8 @@ describe Sandal::Enc::A128CBC_HS256 do
|
|
40
41
|
expect { Sandal.decrypt_token(token) do |header|
|
41
42
|
rsa = OpenSSL::PKey::RSA.new(2048)
|
42
43
|
alg = Sandal::Enc::Alg::RSA1_5.new(rsa)
|
43
|
-
|
44
|
-
end }.to raise_error Sandal::TokenError, '
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'can encrypt and decrypt tokens with the "dir" algorithm' do
|
48
|
-
payload = 'Some text to encrypt'
|
49
|
-
content_master_key = SecureRandom.random_bytes(16)
|
50
|
-
|
51
|
-
encrypter = Sandal::Enc::A128CBC_HS256.new(Sandal::Enc::Alg::Direct.new(content_master_key))
|
52
|
-
token = Sandal.encrypt_token(payload, encrypter)
|
53
|
-
|
54
|
-
output = Sandal.decrypt_token(token) { encrypter }
|
55
|
-
output.should == payload
|
56
|
-
end
|
57
|
-
|
58
|
-
it 'can encrypt and decrypt tokens with the RSA1_5 algorithm' do
|
59
|
-
payload = 'Some other text to encrypt'
|
60
|
-
rsa = OpenSSL::PKey::RSA.new(2048)
|
61
|
-
|
62
|
-
encrypter = Sandal::Enc::A128CBC_HS256.new(Sandal::Enc::Alg::RSA1_5.new(rsa.public_key))
|
63
|
-
token = Sandal.encrypt_token(payload, encrypter)
|
64
|
-
|
65
|
-
output = Sandal.decrypt_token(token) do
|
66
|
-
Sandal::Enc::A128CBC_HS256.new(Sandal::Enc::Alg::RSA1_5.new(rsa))
|
67
|
-
end
|
68
|
-
output.should == payload
|
69
|
-
end
|
70
|
-
|
71
|
-
it 'can encrypt and decrypt tokens with the RSA-OAEP algorithm' do
|
72
|
-
payload = 'Some more text to encrypt'
|
73
|
-
rsa = OpenSSL::PKey::RSA.new(2048)
|
74
|
-
|
75
|
-
encrypter = Sandal::Enc::A128CBC_HS256.new(Sandal::Enc::Alg::RSA_OAEP.new(rsa.public_key))
|
76
|
-
token = Sandal.encrypt_token(payload, encrypter)
|
77
|
-
|
78
|
-
output = Sandal.decrypt_token(token) do
|
79
|
-
Sandal::Enc::A128CBC_HS256.new(Sandal::Enc::Alg::RSA_OAEP.new(rsa))
|
80
|
-
end
|
81
|
-
output.should == payload
|
44
|
+
Sandal::Enc::A128CBC_HS256.new(alg)
|
45
|
+
end }.to raise_error Sandal::TokenError, 'Cannot decrypt content master key.'
|
82
46
|
end
|
83
47
|
|
84
48
|
end
|
@@ -1,26 +1,13 @@
|
|
1
1
|
require 'helper'
|
2
2
|
require 'openssl'
|
3
|
-
|
3
|
+
require_relative 'shared_examples'
|
4
4
|
|
5
5
|
# TODO: These tests are really for the Sandal module rather than just the algorithm -- move them!
|
6
6
|
|
7
7
|
if defined? Sandal::Enc::A128GCM
|
8
8
|
|
9
9
|
describe Sandal::Enc::A128GCM do
|
10
|
-
|
11
|
-
it 'can encrypt and decrypt tokens with the RSA-OAEP algorithm' do
|
12
|
-
payload = 'Some more text to encrypt'
|
13
|
-
rsa = OpenSSL::PKey::RSA.new(2048)
|
14
|
-
|
15
|
-
encrypter = Sandal::Enc::A128GCM.new(Sandal::Enc::Alg::RSA_OAEP.new(rsa.public_key))
|
16
|
-
token = Sandal.encrypt_token(payload, encrypter)
|
17
|
-
|
18
|
-
output = Sandal.decrypt_token(token) do
|
19
|
-
Sandal::Enc::A128GCM.new(Sandal::Enc::Alg::RSA_OAEP.new(rsa))
|
20
|
-
end
|
21
|
-
output.should == payload
|
22
|
-
end
|
23
|
-
|
10
|
+
include_examples 'algorithm compatibility', Sandal::Enc::A128GCM
|
24
11
|
end
|
25
12
|
|
26
13
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'openssl'
|
3
|
+
require_relative 'shared_examples'
|
4
|
+
|
5
|
+
# TODO: These tests are really for the Sandal module rather than just the algorithm -- move them!
|
6
|
+
|
7
|
+
describe Sandal::Enc::A256CBC_HS512 do
|
8
|
+
include_examples 'algorithm compatibility', Sandal::Enc::A256CBC_HS512
|
9
|
+
end
|
10
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'openssl'
|
3
|
+
require_relative 'shared_examples'
|
4
|
+
|
5
|
+
# TODO: These tests are really for the Sandal module rather than just the algorithm -- move them!
|
6
|
+
|
7
|
+
if defined? Sandal::Enc::A256GCM
|
8
|
+
|
9
|
+
describe Sandal::Enc::A256GCM do
|
10
|
+
include_examples 'algorithm compatibility', Sandal::Enc::A256GCM
|
11
|
+
|
12
|
+
# these tests don't run with jruby as it errors when you try and set rsa.d parameter directly
|
13
|
+
context 'using the example RSA key from JWE section A.1', :jruby_incompatible do
|
14
|
+
|
15
|
+
before :all do
|
16
|
+
@rsa = OpenSSL::PKey::RSA.new(2048)
|
17
|
+
@rsa.n = make_bn([161, 168, 84, 34, 133, 176, 208, 173, 46, 176, 163, 110, 57, 30, 135, 227, 9, 31, 226, 128, 84, 92, 116, 241, 70, 248, 27, 227, 193, 62, 5, 91, 241, 145, 224, 205, 141, 176, 184, 133, 239, 43, 81, 103, 9, 161, 153, 157, 179, 104, 123, 51, 189, 34, 152, 69, 97, 69, 78, 93, 140, 131, 87, 182, 169, 101, 92, 142, 3, 22, 167, 8, 212, 56, 35, 79, 210, 222, 192, 208, 252, 49, 109, 138, 173, 253, 210, 166, 201, 63, 102, 74, 5, 158, 41, 90, 144, 108, 160, 79, 10, 89, 222, 231, 172, 31, 227, 197, 0, 19, 72, 81, 138, 78, 136, 221, 121, 118, 196, 17, 146, 10, 244, 188, 72, 113, 55, 221, 162, 217, 171, 27, 57, 233, 210, 101, 236, 154, 199, 56, 138, 239, 101, 48, 198, 186, 202, 160, 76, 111, 234, 71, 57, 183, 5, 211, 171, 136, 126, 64, 40, 75, 58, 89, 244, 254, 107, 84, 103, 7, 236, 69, 163, 18, 180, 251, 58, 153, 46, 151, 174, 12, 103, 197, 181, 161, 162, 55, 250, 235, 123, 110, 17, 11, 158, 24, 47, 133, 8, 199, 235, 107, 126, 130, 246, 73, 195, 20, 108, 202, 176, 214, 187, 45, 146, 182, 118, 54, 32, 200, 61, 201, 71, 243, 1, 255, 131, 84, 37, 111, 211, 168, 228, 45, 192, 118, 27, 197, 235, 232, 36, 10, 230, 248, 190, 82, 182, 140, 35, 204, 108, 190, 253, 186, 186, 27])
|
18
|
+
@rsa.e = make_bn([1, 0, 1])
|
19
|
+
@rsa.d = make_bn([144, 183, 109, 34, 62, 134, 108, 57, 44, 252, 10, 66, 73, 54, 16, 181, 233, 92, 54, 219, 101, 42, 35, 178, 63, 51, 43, 92, 119, 136, 251, 41, 53, 23, 191, 164, 164, 60, 88, 227, 229, 152, 228, 213, 149, 228, 169, 237, 104, 71, 151, 75, 88, 252, 216, 77, 251, 231, 28, 97, 88, 193, 215, 202, 248, 216, 121, 195, 211, 245, 250, 112, 71, 243, 61, 129, 95, 39, 244, 122, 225, 217, 169, 211, 165, 48, 253, 220, 59, 122, 219, 42, 86, 223, 32, 236, 39, 48, 103, 78, 122, 216, 187, 88, 176, 89, 24, 1, 42, 177, 24, 99, 142, 170, 1, 146, 43, 3, 108, 64, 194, 121, 182, 95, 187, 134, 71, 88, 96, 134, 74, 131, 167, 69, 106, 143, 121, 27, 72, 44, 245, 95, 39, 194, 179, 175, 203, 122, 16, 112, 183, 17, 200, 202, 31, 17, 138, 156, 184, 210, 157, 184, 154, 131, 128, 110, 12, 85, 195, 122, 241, 79, 251, 229, 183, 117, 21, 123, 133, 142, 220, 153, 9, 59, 57, 105, 81, 255, 138, 77, 82, 54, 62, 216, 38, 249, 208, 17, 197, 49, 45, 19, 232, 157, 251, 131, 137, 175, 72, 126, 43, 229, 69, 179, 117, 82, 157, 213, 83, 35, 57, 210, 197, 252, 171, 143, 194, 11, 47, 163, 6, 253, 75, 252, 96, 11, 187, 84, 130, 210, 7, 121, 78, 91, 79, 57, 251, 138, 132, 220, 60, 224, 173, 56, 224, 201])
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'can decrypt the example token' do
|
23
|
+
token = 'eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.M2XxpbORKezKSzzQL_95-GjiudRBTqn_omS8z9xgoRb7L0Jw5UsEbxmtyHn2T71mrZLkjg4Mp8gbhYoltPkEOHvAopz25-vZ8C2e1cOaAo5WPcbSIuFcB4DjBOM3t0UAO6JHkWLuAEYoe58lcxIQneyKdaYSLbV9cKqoUoFQpvKWYRHZbfszIyfsa18rmgTjzrtLDTPnc09DSJE24aQ8w3i8RXEDthW9T1J6LsTH_vwHdwUgkI-tC2PNeGrnM-dNSfzF3Y7-lwcGy0FsdXkPXytvDV7y4pZeeUiQ-0VdibIN2AjjfW60nfrPuOjepMFG6BBBbR37pHcyzext9epOAQ.48V1_ALb6US04U3b._e21tGGhac_peEFkLXr2dMPUZiUkrw.7V5ZDko0v_mf2PAc4JMiUg'
|
24
|
+
payload = Sandal.decrypt_token(token) do |header|
|
25
|
+
alg = Sandal::Enc::Alg::RSA_OAEP.new(@rsa)
|
26
|
+
Sandal::Enc::A256GCM.new(alg)
|
27
|
+
end
|
28
|
+
payload.should == 'Live long and prosper.'
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'raises a token error when the integrity value is changed' do
|
32
|
+
token = 'eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.M2XxpbORKezKSzzQL_95-GjiudRBTqn_omS8z9xgoRb7L0Jw5UsEbxmtyHn2T71mrZLkjg4Mp8gbhYoltPkEOHvAopz25-vZ8C2e1cOaAo5WPcbSIuFcB4DjBOM3t0UAO6JHkWLuAEYoe58lcxIQneyKdaYSLbV9cKqoUoFQpvKWYRHZbfszIyfsa18rmgTjzrtLDTPnc09DSJE24aQ8w3i8RXEDthW9T1J6LsTH_vwHdwUgkI-tC2PNeGrnM-dNSfzF3Y7-lwcGy0FsdXkPXytvDV7y4pZeeUiQ-0VdibIN2AjjfW60nfrPuOjepMFG6BBBbR37pHcyzext9epOAQ.48V1_ALb6US04U3b._e21tGGhac_peEFkLXr2dMPUZiUkrw.8LXqMd0JLGsxMaB5uoNaMpg7uUW_p40RlaZHCwMIyzk'
|
33
|
+
expect { Sandal.decrypt_token(token) do |header|
|
34
|
+
alg = Sandal::Enc::Alg::RSA_OAEP.new(@rsa)
|
35
|
+
Sandal::Enc::A256GCM.new(alg)
|
36
|
+
end }.to raise_error Sandal::TokenError, 'Invalid token.'
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'raises a token error when the RSA keys JWE section A.1 are changed' do
|
42
|
+
token = 'eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.M2XxpbORKezKSzzQL_95-GjiudRBTqn_omS8z9xgoRb7L0Jw5UsEbxmtyHn2T71mrZLkjg4Mp8gbhYoltPkEOHvAopz25-vZ8C2e1cOaAo5WPcbSIuFcB4DjBOM3t0UAO6JHkWLuAEYoe58lcxIQneyKdaYSLbV9cKqoUoFQpvKWYRHZbfszIyfsa18rmgTjzrtLDTPnc09DSJE24aQ8w3i8RXEDthW9T1J6LsTH_vwHdwUgkI-tC2PNeGrnM-dNSfzF3Y7-lwcGy0FsdXkPXytvDV7y4pZeeUiQ-0VdibIN2AjjfW60nfrPuOjepMFG6BBBbR37pHcyzext9epOAQ.48V1_ALb6US04U3b._e21tGGhac_peEFkLXr2dMPUZiUkrw.8LXqMd0JLGsxMaB5uoNaMpg7uUW_p40RlaZHCwMIyzk'
|
43
|
+
expect { Sandal.decrypt_token(token) do |header|
|
44
|
+
rsa = OpenSSL::PKey::RSA.new(2048)
|
45
|
+
alg = Sandal::Enc::Alg::RSA_OAEP.new(rsa)
|
46
|
+
Sandal::Enc::A256GCM.new(alg)
|
47
|
+
end }.to raise_error Sandal::TokenError, 'Cannot decrypt content master key.'
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'openssl'
|
3
|
+
|
4
|
+
describe Sandal::Enc::Alg::Direct do
|
5
|
+
|
6
|
+
context '#name' do
|
7
|
+
|
8
|
+
it 'is "dir"' do
|
9
|
+
alg = Sandal::Enc::Alg::Direct.new('some cmk')
|
10
|
+
alg.name.should == 'dir'
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
context '#cmk' do
|
16
|
+
|
17
|
+
it 'returns the real CMK' do
|
18
|
+
cmk = 'the real cmk'
|
19
|
+
alg = Sandal::Enc::Alg::Direct.new(cmk)
|
20
|
+
alg.cmk.should == cmk
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
context '#encrypt_cmk' do
|
26
|
+
|
27
|
+
it 'returns an empty string' do
|
28
|
+
alg = Sandal::Enc::Alg::Direct.new('the real cmk')
|
29
|
+
alg.encrypt_cmk('any value').should == ''
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
context '#decrypt_cmk' do
|
35
|
+
|
36
|
+
it 'returns the real CMK when the value to decrypt is nil' do
|
37
|
+
cmk = 'the real cmk'
|
38
|
+
alg = Sandal::Enc::Alg::Direct.new(cmk)
|
39
|
+
alg.decrypt_cmk(nil).should == cmk
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'returns the real CMK when the value to decrypt is empty' do
|
43
|
+
cmk = 'the real cmk'
|
44
|
+
alg = Sandal::Enc::Alg::Direct.new(cmk)
|
45
|
+
alg.decrypt_cmk('').should == cmk
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'raises a TokenError if the value to decrypt is not nil or empty' do
|
49
|
+
alg = Sandal::Enc::Alg::Direct.new('the real cmk')
|
50
|
+
expect { alg.decrypt_cmk('a value') }.to raise_error Sandal::TokenError
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|