sandal 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +0 -1
- data/CHANGELOG.md +14 -0
- data/README.md +1 -1
- data/lib/sandal.rb +77 -76
- data/lib/sandal/claims.rb +13 -13
- data/lib/sandal/enc.rb +15 -49
- data/lib/sandal/enc/acbc_hs.rb +97 -52
- data/lib/sandal/enc/agcm.rb +64 -26
- data/lib/sandal/enc/alg.rb +2 -3
- data/lib/sandal/enc/alg/direct.rb +27 -25
- data/lib/sandal/enc/alg/rsa.rb +82 -0
- data/lib/sandal/sig.rb +12 -12
- data/lib/sandal/sig/es.rb +43 -25
- data/lib/sandal/sig/hs.rb +21 -8
- data/lib/sandal/sig/rs.rb +34 -23
- data/lib/sandal/util.rb +7 -7
- data/lib/sandal/version.rb +1 -1
- data/spec/helper.rb +1 -0
- data/spec/sample_keys.rb +28 -0
- data/spec/sandal/claims_spec.rb +4 -4
- data/spec/sandal/enc/a128cbc_hs256_spec.rb +15 -39
- data/spec/sandal/enc/a128gcm_spec.rb +13 -6
- data/spec/sandal/enc/a256cbc_hs512_spec.rb +13 -4
- data/spec/sandal/enc/a256gcm_spec.rb +15 -37
- data/spec/sandal/enc/alg/direct_spec.rb +27 -33
- data/spec/sandal/enc/alg/rsa_spec.rb +100 -0
- data/spec/sandal/enc/shared_examples.rb +93 -21
- data/spec/sandal/sig/es_spec.rb +145 -188
- data/spec/sandal/sig/hs_spec.rb +73 -18
- data/spec/sandal/sig/rs_spec.rb +81 -78
- metadata +7 -6
- data/lib/sandal/enc/alg/rsa1_5.rb +0 -47
- data/lib/sandal/enc/alg/rsa_oaep.rb +0 -48
- data/spec/sandal/enc/alg/rsa1_5_spec.rb +0 -40
data/spec/sandal/claims_spec.rb
CHANGED
@@ -130,14 +130,14 @@ describe Sandal::Claims do
|
|
130
130
|
claims.validate_exp(120)
|
131
131
|
end
|
132
132
|
|
133
|
-
it 'raises
|
133
|
+
it 'raises an ExpiredTokenError when the expiry time is in the past with no clock skew' do
|
134
134
|
claims = { 'exp' => (Time.now - 300).to_i }.extend(Sandal::Claims)
|
135
|
-
expect { claims.validate_exp }.to raise_error Sandal::
|
135
|
+
expect { claims.validate_exp }.to raise_error Sandal::ExpiredTokenError
|
136
136
|
end
|
137
137
|
|
138
|
-
it 'raises
|
138
|
+
it 'raises an ExpiredTokenError when the expiry time is in the past and outside the max clock skew' do
|
139
139
|
claims = { 'exp' => (Time.now - 300).to_i }.extend(Sandal::Claims)
|
140
|
-
expect { claims.validate_exp(120) }.to raise_error Sandal::
|
140
|
+
expect { claims.validate_exp(120) }.to raise_error Sandal::ExpiredTokenError
|
141
141
|
end
|
142
142
|
|
143
143
|
end
|
@@ -1,48 +1,24 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require_relative
|
4
|
-
|
5
|
-
# TODO: These tests are really for the Sandal module rather than just the algorithm -- move them!
|
1
|
+
require "helper"
|
2
|
+
require "openssl"
|
3
|
+
require_relative "shared_examples"
|
6
4
|
|
7
5
|
describe Sandal::Enc::A128CBC_HS256 do
|
8
|
-
include_examples
|
9
|
-
|
10
|
-
# these tests don't run with jruby as it errors when you try and set rsa.d parameter directly
|
11
|
-
context 'using the example RSA key from JWE section A.2', :jruby_incompatible do
|
12
|
-
|
13
|
-
before :all do
|
14
|
-
@rsa = OpenSSL::PKey::RSA.new(2048)
|
15
|
-
@rsa.n = make_bn([177, 119, 33, 13, 164, 30, 108, 121, 207, 136, 107, 242, 12, 224, 19, 226, 198, 134, 17, 71, 173, 75, 42, 61, 48, 162, 206, 161, 97, 108, 185, 234, 226, 219, 118, 206, 118, 5, 169, 224, 60, 181, 90, 85, 51, 123, 6, 224, 4, 122, 29, 230, 151, 12, 244, 127, 121, 25, 4, 85, 220, 144, 215, 110, 130, 17, 68, 228, 129, 138, 7, 130, 231, 40, 212, 214, 17, 179, 28, 124, 151, 178, 207, 20, 14, 154, 222, 113, 176, 24, 198, 73, 211, 113, 9, 33, 178, 80, 13, 25, 21, 25, 153, 212, 206, 67, 154, 147, 70, 194, 192, 183, 160, 83, 98, 236, 175, 85, 23, 97, 75, 199, 177, 73, 145, 50, 253, 206, 32, 179, 254, 236, 190, 82, 73, 67, 129, 253, 252, 220, 108, 136, 138, 11, 192, 1, 36, 239, 228, 55, 81, 113, 17, 25, 140, 63, 239, 146, 3, 172, 96, 60, 227, 233, 64, 255, 224, 173, 225, 228, 229, 92, 112, 72, 99, 97, 26, 87, 187, 123, 46, 50, 90, 202, 117, 73, 10, 153, 47, 224, 178, 163, 77, 48, 46, 154, 33, 148, 34, 228, 33, 172, 216, 89, 46, 225, 127, 68, 146, 234, 30, 147, 54, 146, 5, 133, 45, 78, 254, 85, 55, 75, 213, 86, 194, 218, 215, 163, 189, 194, 54, 6, 83, 36, 18, 153, 53, 7, 48, 89, 35, 66, 144, 7, 65, 154, 13, 97, 75, 55, 230, 132, 3, 13, 239, 71])
|
16
|
-
@rsa.e = make_bn([1, 0, 1])
|
17
|
-
@rsa.d = make_bn([84, 80, 150, 58, 165, 235, 242, 123, 217, 55, 38, 154, 36, 181, 221, 156, 211, 215, 100, 164, 90, 88, 40, 228, 83, 148, 54, 122, 4, 16, 165, 48, 76, 194, 26, 107, 51, 53, 179, 165, 31, 18, 198, 173, 78, 61, 56, 97, 252, 158, 140, 80, 63, 25, 223, 156, 36, 203, 214, 252, 120, 67, 180, 167, 3, 82, 243, 25, 97, 214, 83, 133, 69, 16, 104, 54, 160, 200, 41, 83, 164, 187, 70, 153, 111, 234, 242, 158, 175, 28, 198, 48, 211, 45, 148, 58, 23, 62, 227, 74, 52, 117, 42, 90, 41, 249, 130, 154, 80, 119, 61, 26, 193, 40, 125, 10, 152, 174, 227, 225, 205, 32, 62, 66, 6, 163, 100, 99, 219, 19, 253, 25, 105, 80, 201, 29, 252, 157, 237, 69, 1, 80, 171, 167, 20, 196, 156, 109, 249, 88, 0, 3, 152, 38, 165, 72, 87, 6, 152, 71, 156, 214, 16, 71, 30, 82, 51, 103, 76, 218, 63, 9, 84, 163, 249, 91, 215, 44, 238, 85, 101, 240, 148, 1, 82, 224, 91, 135, 105, 127, 84, 171, 181, 152, 210, 183, 126, 24, 46, 196, 90, 173, 38, 245, 219, 186, 222, 27, 240, 212, 194, 15, 66, 135, 226, 178, 190, 52, 245, 74, 65, 224, 81, 100, 85, 25, 204, 165, 203, 187, 175, 84, 100, 82, 15, 11, 23, 202, 151, 107, 54, 41, 207, 3, 136, 229, 134, 131, 93, 139, 50, 182, 204, 93, 130, 89])
|
18
|
-
end
|
6
|
+
include_examples "algorithm compatibility", Sandal::Enc::A128CBC_HS256
|
7
|
+
include_examples "invalid tokens", Sandal::Enc::A128CBC_HS256
|
19
8
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
Sandal::Enc::A128CBC_HS256.new(alg)
|
25
|
-
end
|
26
|
-
payload.should == 'No matter where you go, there you are.'
|
9
|
+
context "#name" do
|
10
|
+
it "is 'A128CBC-HS256'" do
|
11
|
+
enc = Sandal::Enc::A128CBC_HS256.new(Sandal::Enc::Alg::Direct.new("a cmk"))
|
12
|
+
enc.name.should == "A128CBC-HS256"
|
27
13
|
end
|
28
|
-
|
29
|
-
it 'raises a token error when the integrity value is changed' do
|
30
|
-
token = 'eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDK0hTMjU2In0.ZmnlqWgjXyqwjr7cXHys8F79anIUI6J2UWdAyRQEcGBU-KPHsePM910_RoTDGu1IW40Dn0dvcdVEjpJcPPNIbzWcMxDi131Ejeg-b8ViW5YX5oRdYdiR4gMSDDB3mbkInMNUFT-PK5CuZRnHB2rUK5fhPuF6XFqLLZCG5Q_rJm6Evex-XLcNQAJNa1-6CIU12Wj3mPExxw9vbnsQDU7B4BfmhdyiflLA7Ae5ZGoVRl3A__yLPXxRjHFhpOeDp_adx8NyejF5cz9yDKULugNsDMdlHeJQOMGVLYaSZt3KP6aWNSqFA1PHDg-10ceuTEtq_vPE4-Gtev4N4K4Eudlj4Q.AxY8DCtDaGlsbGljb3RoZQ.Rxsjg6PIExcmGSF7LnSEkDqWIKfAw1wZz2XpabV5PwQsolKwEauWYZNE9Q1hZJEZ.7V5ZDko0v_mf2PAc4JMiUg'
|
31
|
-
expect { Sandal.decode_token(token) do |header|
|
32
|
-
alg = Sandal::Enc::Alg::RSA1_5.new(@rsa)
|
33
|
-
Sandal::Enc::A128CBC_HS256.new(alg)
|
34
|
-
end }.to raise_error Sandal::TokenError, 'Invalid integrity value.'
|
35
|
-
end
|
36
|
-
|
37
14
|
end
|
38
15
|
|
39
|
-
|
40
|
-
token
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end }.to raise_error Sandal::TokenError, 'Cannot decrypt content master key.'
|
16
|
+
context "#decrypt" do
|
17
|
+
it "can decrypt the example token from JWE draft-11 appendix 2", :jruby_incompatible do
|
18
|
+
token="eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A.AxY8DCtDaGlsbGljb3RoZQ.KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY.9hH0vgRfYgPnAHOd8stkvw"
|
19
|
+
enc = Sandal::Enc::A128CBC_HS256.new(Sandal::Enc::Alg::RSA1_5.new(SampleKeys.jwe_draft11_appendix2_rsa))
|
20
|
+
enc.decrypt(token).should == "Live long and prosper."
|
21
|
+
end
|
46
22
|
end
|
47
23
|
|
48
24
|
end
|
@@ -1,13 +1,20 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require_relative
|
4
|
-
|
5
|
-
# TODO: These tests are really for the Sandal module rather than just the algorithm -- move them!
|
1
|
+
require "helper"
|
2
|
+
require "openssl"
|
3
|
+
require_relative "shared_examples"
|
6
4
|
|
7
5
|
if defined? Sandal::Enc::A128GCM
|
8
6
|
|
9
7
|
describe Sandal::Enc::A128GCM do
|
10
|
-
include_examples
|
8
|
+
include_examples "algorithm compatibility", Sandal::Enc::A128GCM
|
9
|
+
include_examples "invalid tokens", Sandal::Enc::A128GCM
|
10
|
+
|
11
|
+
context "#name" do
|
12
|
+
it "is 'A128GCM'" do
|
13
|
+
enc = Sandal::Enc::A128GCM.new(Sandal::Enc::Alg::Direct.new("a cmk"))
|
14
|
+
enc.name.should == "A128GCM"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
11
18
|
end
|
12
19
|
|
13
20
|
end
|
@@ -1,10 +1,19 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require_relative
|
1
|
+
require "helper"
|
2
|
+
require "openssl"
|
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::A256CBC_HS512 do
|
8
|
-
include_examples
|
8
|
+
include_examples "algorithm compatibility", Sandal::Enc::A256CBC_HS512
|
9
|
+
include_examples "invalid tokens", Sandal::Enc::A256CBC_HS512
|
10
|
+
|
11
|
+
context "#name" do
|
12
|
+
it "is 'A256CBC-HS512'" do
|
13
|
+
enc = Sandal::Enc::A256CBC_HS512.new(Sandal::Enc::Alg::Direct.new("a cmk"))
|
14
|
+
enc.name.should == "A256CBC-HS512"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
9
18
|
end
|
10
19
|
|
@@ -1,50 +1,28 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require_relative
|
4
|
-
|
5
|
-
# TODO: These tests are really for the Sandal module rather than just the algorithm -- move them!
|
1
|
+
require "helper"
|
2
|
+
require "openssl"
|
3
|
+
require_relative "shared_examples"
|
6
4
|
|
7
5
|
if defined? Sandal::Enc::A256GCM
|
8
6
|
|
9
7
|
describe Sandal::Enc::A256GCM do
|
10
|
-
include_examples
|
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
|
8
|
+
include_examples "algorithm compatibility", Sandal::Enc::A256GCM
|
9
|
+
include_examples "invalid tokens", Sandal::Enc::A256GCM
|
21
10
|
|
22
|
-
|
23
|
-
token = 'eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.M2XxpbORKezKSzzQL_95-GjiudRBTqn_omS8z9xgoRb7L0Jw5UsEbxmtyHn2T71mrZLkjg4Mp8gbhYoltPkEOHvAopz25-vZ8C2e1cOaAo5WPcbSIuFcB4DjBOM3t0UAO6JHkWLuAEYoe58lcxIQneyKdaYSLbV9cKqoUoFQpvKWYRHZbfszIyfsa18rmgTjzrtLDTPnc09DSJE24aQ8w3i8RXEDthW9T1J6LsTH_vwHdwUgkI-tC2PNeGrnM-dNSfzF3Y7-lwcGy0FsdXkPXytvDV7y4pZeeUiQ-0VdibIN2AjjfW60nfrPuOjepMFG6BBBbR37pHcyzext9epOAQ.48V1_ALb6US04U3b._e21tGGhac_peEFkLXr2dMPUZiUkrw.7V5ZDko0v_mf2PAc4JMiUg'
|
24
|
-
payload = Sandal.decode_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
|
11
|
+
context "#name" do
|
30
12
|
|
31
|
-
it
|
32
|
-
|
33
|
-
|
34
|
-
alg = Sandal::Enc::Alg::RSA_OAEP.new(@rsa)
|
35
|
-
Sandal::Enc::A256GCM.new(alg)
|
36
|
-
end }.to raise_error Sandal::TokenError, 'Invalid token.'
|
13
|
+
it "is 'A256GCM'" do
|
14
|
+
enc = Sandal::Enc::A256GCM.new(Sandal::Enc::Alg::Direct.new("a cmk"))
|
15
|
+
enc.name.should == "A256GCM"
|
37
16
|
end
|
38
17
|
|
39
18
|
end
|
40
19
|
|
41
|
-
|
42
|
-
token
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end }.to raise_error Sandal::TokenError, 'Cannot decrypt content master key.'
|
20
|
+
context "#decrypt" do
|
21
|
+
it "can decrypt the example token from JWE draft-11 appendix 1", :jruby_incompatible do
|
22
|
+
token = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg.48V1_ALb6US04U3b.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ"
|
23
|
+
enc = Sandal::Enc::A256GCM.new(Sandal::Enc::Alg::RSA_OAEP.new(SampleKeys.jwe_draft11_appendix1_rsa))
|
24
|
+
enc.decrypt(token).should == "The true sign of intelligence is not knowledge but imagination."
|
25
|
+
end
|
48
26
|
end
|
49
27
|
|
50
28
|
end
|
@@ -1,53 +1,47 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "helper"
|
2
|
+
require "openssl"
|
3
3
|
|
4
4
|
describe Sandal::Enc::Alg::Direct do
|
5
5
|
|
6
|
-
context
|
7
|
-
|
8
|
-
|
9
|
-
alg
|
10
|
-
alg.name.should == 'dir'
|
6
|
+
context "#name" do
|
7
|
+
it "is 'dir'" do
|
8
|
+
alg = Sandal::Enc::Alg::Direct.new("some key")
|
9
|
+
alg.name.should == "dir"
|
11
10
|
end
|
12
|
-
|
13
11
|
end
|
14
12
|
|
15
|
-
context
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
alg
|
20
|
-
alg.cmk.should == cmk
|
13
|
+
context "#preshared_key" do
|
14
|
+
it "returns the pre-shared key" do
|
15
|
+
key = "the pre-shared key"
|
16
|
+
alg = Sandal::Enc::Alg::Direct.new(key)
|
17
|
+
alg.preshared_key.should == key
|
21
18
|
end
|
22
|
-
|
23
19
|
end
|
24
20
|
|
25
|
-
context
|
26
|
-
|
27
|
-
|
28
|
-
alg
|
29
|
-
alg.encrypt_cmk('any value').should == ''
|
21
|
+
context "#encrypt_key" do
|
22
|
+
it "returns an empty string" do
|
23
|
+
alg = Sandal::Enc::Alg::Direct.new("the real key")
|
24
|
+
alg.encrypt_key("any value").should == ""
|
30
25
|
end
|
31
|
-
|
32
26
|
end
|
33
27
|
|
34
|
-
context
|
28
|
+
context "#decrypt_key" do
|
35
29
|
|
36
|
-
it
|
37
|
-
|
38
|
-
alg = Sandal::Enc::Alg::Direct.new(
|
39
|
-
alg.
|
30
|
+
it "returns the pre-shared content key when the value to decrypt is nil" do
|
31
|
+
key = "a pre-shared key"
|
32
|
+
alg = Sandal::Enc::Alg::Direct.new(key)
|
33
|
+
alg.decrypt_key(nil).should == key
|
40
34
|
end
|
41
35
|
|
42
|
-
it
|
43
|
-
|
44
|
-
alg = Sandal::Enc::Alg::Direct.new(
|
45
|
-
alg.
|
36
|
+
it "returns the pre-shared content key when the value to decrypt is empty" do
|
37
|
+
key = "my pre-shared key"
|
38
|
+
alg = Sandal::Enc::Alg::Direct.new(key)
|
39
|
+
alg.decrypt_key("").should == key
|
46
40
|
end
|
47
41
|
|
48
|
-
it
|
49
|
-
alg = Sandal::Enc::Alg::Direct.new(
|
50
|
-
expect { alg.
|
42
|
+
it "raises an InvalidTokenError if the value to decrypt is not nil or empty" do
|
43
|
+
alg = Sandal::Enc::Alg::Direct.new("the pre-shared key")
|
44
|
+
expect { alg.decrypt_key("a value") }.to raise_error Sandal::InvalidTokenError
|
51
45
|
end
|
52
46
|
|
53
47
|
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require "helper"
|
2
|
+
require "openssl"
|
3
|
+
|
4
|
+
include Sandal::Util
|
5
|
+
|
6
|
+
shared_examples "encryption and decryption" do |enc_class|
|
7
|
+
|
8
|
+
it "can encrypt and decrypt a content master key" do
|
9
|
+
key = OpenSSL::PKey::RSA.new(2048)
|
10
|
+
encrypter = enc_class.new(key.public_key)
|
11
|
+
decrypter = enc_class.new(key)
|
12
|
+
key = "an encryption key"
|
13
|
+
decrypter.decrypt_key(encrypter.encrypt_key(key)).should == key
|
14
|
+
end
|
15
|
+
|
16
|
+
it "can use DER-encoded keys to encrypt and decrypt a content master key" do
|
17
|
+
key = OpenSSL::PKey::RSA.new(2048)
|
18
|
+
encrypter = enc_class.new(key.public_key.to_der)
|
19
|
+
decrypter = enc_class.new(key.to_der)
|
20
|
+
key = "an encryption key"
|
21
|
+
decrypter.decrypt_key(encrypter.encrypt_key(key)).should == key
|
22
|
+
end
|
23
|
+
|
24
|
+
it "can use PEM-encoded keys to encrypt and decrypt a content master key" do
|
25
|
+
key = OpenSSL::PKey::RSA.new(2048)
|
26
|
+
encrypter = enc_class.new(key.public_key.to_pem)
|
27
|
+
decrypter = enc_class.new(key.to_pem)
|
28
|
+
key = "an encryption key"
|
29
|
+
decrypter.decrypt_key(encrypter.encrypt_key(key)).should == key
|
30
|
+
end
|
31
|
+
|
32
|
+
context "#decrypt_key" do
|
33
|
+
|
34
|
+
it "raises an InvalidTokenError when the wrong key is used for decryption" do
|
35
|
+
encrypter = enc_class.new(OpenSSL::PKey::RSA.new(2048).public_key)
|
36
|
+
decrypter = enc_class.new(OpenSSL::PKey::RSA.new(2048))
|
37
|
+
encrypted_key = encrypter.encrypt_key("an encryption key")
|
38
|
+
expect { decrypter.decrypt_key(encrypted_key) }.to raise_error Sandal::InvalidTokenError
|
39
|
+
end
|
40
|
+
|
41
|
+
it "raises an InvalidTokenError when the key to decrypt is nil" do
|
42
|
+
decrypter = enc_class.new(OpenSSL::PKey::RSA.new(2048))
|
43
|
+
expect { decrypter.decrypt_key(nil) }.to raise_error Sandal::InvalidTokenError
|
44
|
+
end
|
45
|
+
|
46
|
+
it "raises an InvalidTokenError when the key to decrypt is empty" do
|
47
|
+
decrypter = enc_class.new(OpenSSL::PKey::RSA.new(2048))
|
48
|
+
expect { decrypter.decrypt_key("") }.to raise_error Sandal::InvalidTokenError
|
49
|
+
end
|
50
|
+
|
51
|
+
it "raises an InvalidTokenError when the key to decrypt is invalid" do
|
52
|
+
decrypter = enc_class.new(OpenSSL::PKey::RSA.new(2048))
|
53
|
+
expect { decrypter.decrypt_key("not a real encrypted key") }.to raise_error Sandal::InvalidTokenError
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
describe Sandal::Enc::Alg::RSA1_5 do
|
61
|
+
include_examples "encryption and decryption", Sandal::Enc::Alg::RSA1_5
|
62
|
+
|
63
|
+
context "#name" do
|
64
|
+
it "is 'RSA1_5'" do
|
65
|
+
alg = Sandal::Enc::Alg::RSA1_5.new(OpenSSL::PKey::RSA.new(2048))
|
66
|
+
alg.name.should == "RSA1_5"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "#decrypt_key" do
|
71
|
+
it "can decrypt the encypted content master key from JWE draft-11 appendix 2", :jruby_incompatible do
|
72
|
+
key = [4, 211, 31, 197, 84, 157, 252, 254, 11, 100, 157, 250, 63, 170, 106, 206, 107, 124, 212, 45, 111, 107, 9, 219, 200, 177, 0, 240, 143, 156, 44, 207].pack("C*")
|
73
|
+
encrypted_key = [80, 104, 72, 58, 11, 130, 236, 139, 132, 189, 255, 205, 61, 86, 151, 176, 99, 40, 44, 233, 176, 189, 205, 70, 202, 169, 72, 40, 226, 181, 156, 223, 120, 156, 115, 232, 150, 209, 145, 133, 104, 112, 237, 156, 116, 250, 65, 102, 212, 210, 103, 240, 177, 61, 93, 40, 71, 231, 223, 226, 240, 157, 15, 31, 150, 89, 200, 215, 198, 203, 108, 70, 117, 66, 212, 238, 193, 205, 23, 161, 169, 218, 243, 203, 128, 214, 127, 253, 215, 139, 43, 17, 135, 103, 179, 220, 28, 2, 212, 206, 131, 158, 128, 66, 62, 240, 78, 186, 141, 125, 132, 227, 60, 137, 43, 31, 152, 199, 54, 72, 34, 212, 115, 11, 152, 101, 70, 42, 219, 233, 142, 66, 151, 250, 126, 146, 141, 216, 190, 73, 50, 177, 146, 5, 52, 247, 28, 197, 21, 59, 170, 247, 181, 89, 131, 241, 169, 182, 246, 99, 15, 36, 102, 166, 182, 172, 197, 136, 230, 120, 60, 58, 219, 243, 149, 94, 222, 150, 154, 194, 110, 227, 225, 112, 39, 89, 233, 112, 207, 211, 241, 124, 174, 69, 221, 179, 107, 196, 225, 127, 167, 112, 226, 12, 242, 16, 24, 28, 120, 182, 244, 213, 244, 153, 194, 162, 69, 160, 244, 248, 63, 165, 141, 4, 207, 249, 193, 79, 131, 0, 169, 233, 127, 167, 101, 151, 125, 56, 112, 111, 248, 29, 232, 90, 29, 147, 110, 169, 146, 114, 165, 204, 71, 136, 41, 252].pack("C*")
|
74
|
+
alg = Sandal::Enc::Alg::RSA1_5.new(SampleKeys.jwe_draft11_appendix2_rsa)
|
75
|
+
alg.decrypt_key(encrypted_key).should == key
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
describe Sandal::Enc::Alg::RSA_OAEP do
|
82
|
+
include_examples "encryption and decryption", Sandal::Enc::Alg::RSA_OAEP
|
83
|
+
|
84
|
+
context "#name" do
|
85
|
+
it "is 'RSA-OAEP'" do
|
86
|
+
alg = Sandal::Enc::Alg::RSA_OAEP.new(OpenSSL::PKey::RSA.new(2048))
|
87
|
+
alg.name.should == "RSA-OAEP"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "#decrypt_key" do
|
92
|
+
it "can decrypt the encypted content master key from JWE draft-11 appendix 1", :jruby_incompatible do
|
93
|
+
key = [177, 161, 244, 128, 84, 143, 225, 115, 63, 180, 3, 255, 107, 154, 212, 246, 138, 7, 110, 91, 112, 46, 34, 105, 47, 130, 203, 46, 122, 234, 64, 252].pack("C*")
|
94
|
+
encrypted_key = [56, 163, 154, 192, 58, 53, 222, 4, 105, 218, 136, 218, 29, 94, 203, 22, 150, 92, 129, 94, 211, 232, 53, 89, 41, 60, 138, 56, 196, 216, 82, 98, 168, 76, 37, 73, 70, 7, 36, 8, 191, 100, 136, 196, 244, 220, 145, 158, 138, 155, 4, 117, 141, 230, 199, 247, 173, 45, 182, 214, 74, 177, 107, 211, 153, 11, 205, 196, 171, 226, 162, 128, 171, 182, 13, 237, 239, 99, 193, 4, 91, 219, 121, 223, 107, 167, 61, 119, 228, 173, 156, 137, 134, 200, 80, 219, 74, 253, 56, 185, 91, 177, 34, 158, 89, 154, 205, 96, 55, 18, 138, 43, 96, 218, 215, 128, 124, 75, 138, 243, 85, 25, 109, 117, 140, 26, 155, 249, 67, 167, 149, 231, 100, 6, 41, 65, 214, 251, 232, 87, 72, 40, 182, 149, 154, 168, 31, 193, 126, 215, 89, 28, 111, 219, 125, 182, 139, 235, 195, 197, 23, 234, 55, 58, 63, 180, 68, 202, 206, 149, 75, 205, 248, 176, 67, 39, 178, 60, 98, 193, 32, 238, 122, 96, 158, 222, 57, 183, 111, 210, 55, 188, 215, 206, 180, 166, 150, 166, 106, 250, 55, 229, 72, 40, 69, 214, 216, 104, 23, 40, 135, 212, 28, 127, 41, 80, 175, 174, 168, 115, 171, 197, 89, 116, 92, 103, 246, 83, 216, 182, 176, 84, 37, 147, 35, 45, 219, 172, 99, 226, 233, 73, 37, 124, 42, 72, 49, 242, 35, 127, 184, 134, 117, 114, 135, 206].pack("C*")
|
95
|
+
alg = Sandal::Enc::Alg::RSA_OAEP.new(SampleKeys.jwe_draft11_appendix1_rsa)
|
96
|
+
alg.decrypt_key(encrypted_key).should == key
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
@@ -1,37 +1,109 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "helper"
|
2
|
+
require "securerandom"
|
3
3
|
|
4
|
-
shared_examples
|
4
|
+
shared_examples "algorithm compatibility" do |enc_class|
|
5
5
|
|
6
|
-
it
|
7
|
-
payload =
|
8
|
-
|
9
|
-
|
10
|
-
token =
|
11
|
-
output =
|
6
|
+
it "can encrypt and decrypt tokens with the dir algorithm" do
|
7
|
+
payload = "Some text to encrypt"
|
8
|
+
content_encryption_key = SecureRandom.random_bytes(enc_class::KEY_SIZE / 8)
|
9
|
+
enc = enc_class.new(Sandal::Enc::Alg::Direct.new(content_encryption_key))
|
10
|
+
token = enc.encrypt("", payload)
|
11
|
+
output = enc.decrypt(token)
|
12
12
|
output.should == payload
|
13
13
|
end
|
14
14
|
|
15
|
-
it
|
16
|
-
payload =
|
15
|
+
it "can encrypt and decrypt tokens with the RSA1_5 algorithm" do
|
16
|
+
payload = "Some other text to encrypt"
|
17
17
|
rsa = OpenSSL::PKey::RSA.new(2048)
|
18
18
|
encrypter = enc_class.new(Sandal::Enc::Alg::RSA1_5.new(rsa.public_key))
|
19
|
-
token =
|
20
|
-
|
21
|
-
|
22
|
-
end
|
19
|
+
token = encrypter.encrypt("", payload)
|
20
|
+
decrypter = enc_class.new(Sandal::Enc::Alg::RSA1_5.new(rsa))
|
21
|
+
output = decrypter.decrypt(token)
|
23
22
|
output.should == payload
|
24
23
|
end
|
25
24
|
|
26
|
-
it
|
27
|
-
payload =
|
25
|
+
it "can encrypt and decrypt tokens with the RSA-OAEP algorithm" do
|
26
|
+
payload = "Some more text to encrypt"
|
28
27
|
rsa = OpenSSL::PKey::RSA.new(2048)
|
29
28
|
encrypter = enc_class.new(Sandal::Enc::Alg::RSA_OAEP.new(rsa.public_key))
|
30
|
-
token =
|
31
|
-
|
32
|
-
|
33
|
-
end
|
29
|
+
token = encrypter.encrypt("", payload)
|
30
|
+
decrypter = enc_class.new(Sandal::Enc::Alg::RSA_OAEP.new(rsa))
|
31
|
+
output = decrypter.decrypt(token)
|
34
32
|
output.should == payload
|
35
33
|
end
|
36
34
|
|
35
|
+
end
|
36
|
+
|
37
|
+
shared_examples "invalid tokens" do |enc_class|
|
38
|
+
|
39
|
+
context "#decrypt" do
|
40
|
+
|
41
|
+
def test_decrypt_mangled_token(enc_class)
|
42
|
+
content_encryption_key = SecureRandom.random_bytes(enc_class::KEY_SIZE / 8)
|
43
|
+
enc = enc_class.new(Sandal::Enc::Alg::Direct.new(content_encryption_key))
|
44
|
+
token = enc.encrypt("", "any old payload")
|
45
|
+
token_parts = token.split(".")
|
46
|
+
yield token_parts
|
47
|
+
token = token_parts.join(".")
|
48
|
+
expect { enc.decrypt(token) }.to raise_error Sandal::InvalidTokenError
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_decrypt_token_with_missing_part(enc_class, part_index)
|
52
|
+
test_decrypt_mangled_token(enc_class) do |token_parts|
|
53
|
+
token_parts.delete_at(part_index)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it "raises an InvalidTokenError when the header is missing" do
|
58
|
+
test_decrypt_token_with_missing_part(enc_class, 0)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "raises an InvalidTokenError when the encrypted key is missing" do
|
62
|
+
test_decrypt_token_with_missing_part(enc_class, 1)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "raises an InvalidTokenError when the IV is missing" do
|
66
|
+
test_decrypt_token_with_missing_part(enc_class, 2)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "raises an InvalidTokenError when the encrypted data is missing" do
|
70
|
+
test_decrypt_token_with_missing_part(enc_class, 3)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "raises an InvalidTokenError when the integrity value is missing" do
|
74
|
+
test_decrypt_token_with_missing_part(enc_class, 4)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "raises an InvalidTokenError when the header value is invalid" do
|
78
|
+
test_decrypt_mangled_token(enc_class) do |token_parts|
|
79
|
+
token_parts[0] = token_parts[4]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
it "raises an InvalidTokenError when the encrypted key is invalid" do
|
84
|
+
test_decrypt_mangled_token(enc_class) do |token_parts|
|
85
|
+
token_parts[1] = token_parts[4]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
it "raises an InvalidTokenError when the IV is invalid" do
|
90
|
+
test_decrypt_mangled_token(enc_class) do |token_parts|
|
91
|
+
token_parts[2] = token_parts[4]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it "raises an InvalidTokenError when the encrypted data is invalid" do
|
96
|
+
test_decrypt_mangled_token(enc_class) do |token_parts|
|
97
|
+
token_parts[3] = token_parts[4]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
it "raises an InvalidTokenError when the integrity value is invalid" do
|
102
|
+
test_decrypt_mangled_token(enc_class) do |token_parts|
|
103
|
+
token_parts[4] = token_parts[0]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
37
109
|
end
|