sandal 0.2.0 → 0.3.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.
- 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
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
shared_examples 'algorithm compatibility' do |enc_class|
|
5
|
+
|
6
|
+
it 'can encrypt and decrypt tokens with the "dir" algorithm' do
|
7
|
+
payload = 'Some text to encrypt'
|
8
|
+
content_master_key = SecureRandom.random_bytes(32)
|
9
|
+
encrypter = enc_class.new(Sandal::Enc::Alg::Direct.new(content_master_key))
|
10
|
+
token = Sandal.encrypt_token(payload, encrypter)
|
11
|
+
output = Sandal.decrypt_token(token) { encrypter }
|
12
|
+
output.should == payload
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'can encrypt and decrypt tokens with the RSA1_5 algorithm' do
|
16
|
+
payload = 'Some other text to encrypt'
|
17
|
+
rsa = OpenSSL::PKey::RSA.new(2048)
|
18
|
+
encrypter = enc_class.new(Sandal::Enc::Alg::RSA1_5.new(rsa.public_key))
|
19
|
+
token = Sandal.encrypt_token(payload, encrypter)
|
20
|
+
output = Sandal.decrypt_token(token) do
|
21
|
+
enc_class.new(Sandal::Enc::Alg::RSA1_5.new(rsa))
|
22
|
+
end
|
23
|
+
output.should == payload
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'can encrypt and decrypt tokens with the RSA-OAEP algorithm' do
|
27
|
+
payload = 'Some more text to encrypt'
|
28
|
+
rsa = OpenSSL::PKey::RSA.new(2048)
|
29
|
+
encrypter = enc_class.new(Sandal::Enc::Alg::RSA_OAEP.new(rsa.public_key))
|
30
|
+
token = Sandal.encrypt_token(payload, encrypter)
|
31
|
+
output = Sandal.decrypt_token(token) do
|
32
|
+
enc_class.new(Sandal::Enc::Alg::RSA_OAEP.new(rsa))
|
33
|
+
end
|
34
|
+
output.should == payload
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
data/spec/sandal/sig/es_spec.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'helper'
|
2
2
|
require 'openssl'
|
3
3
|
|
4
|
+
include Sandal::Util
|
5
|
+
|
4
6
|
# EC isn't implemented in jruby-openssl at the moment
|
5
7
|
if defined? Sandal::Sig::ES
|
6
8
|
|
@@ -26,7 +28,7 @@ describe Sandal::Sig::ES do
|
|
26
28
|
r = make_bn([14, 209, 33, 83, 121, 99, 108, 72, 60, 47, 127, 21, 88, 7, 212, 2, 163, 178, 40, 3, 58, 249, 124, 126, 23, 129, 154, 195, 22, 158, 166, 101] )
|
27
29
|
s = make_bn([197, 10, 7, 211, 140, 60, 112, 229, 216, 241, 45, 175, 8, 74, 84, 128, 166, 101, 144, 197, 242, 147, 80, 154, 143, 63, 127, 138, 131, 163, 84, 213])
|
28
30
|
signature = Sandal::Sig::ES.encode_jws_signature(r, s, 256)
|
29
|
-
base64_signature =
|
31
|
+
base64_signature = jwt_base64_encode(signature)
|
30
32
|
base64_signature.should == 'DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q'
|
31
33
|
end
|
32
34
|
|
@@ -34,7 +36,7 @@ describe Sandal::Sig::ES do
|
|
34
36
|
r = make_bn([1, 220, 12, 129, 231, 171, 194, 209, 232, 135, 233, 117, 247, 105, 122, 210, 26, 125, 192, 1, 217, 21, 82, 91, 45, 240, 255, 83, 19, 34, 239, 71, 48, 157, 147, 152, 105, 18, 53, 108, 163, 214, 68, 231, 62, 153, 150, 106, 194, 164, 246, 72, 143, 138, 24, 50, 129, 223, 133, 206, 209, 172, 63, 237, 119, 109] )
|
35
37
|
s = make_bn([0, 111, 6, 105, 44, 5, 41, 208, 128, 61, 152, 40, 92, 61, 152, 4, 150, 66, 60, 69, 247, 196, 170, 81, 193, 199, 78, 59, 194, 169, 16, 124, 9, 143, 42, 142, 131, 48, 206, 238, 34, 175, 83, 203, 220, 159, 3, 107, 155, 22, 27, 73, 111, 68, 68, 21, 238, 144, 229, 232, 148, 188, 222, 59, 242, 103] )
|
36
38
|
signature = Sandal::Sig::ES.encode_jws_signature(r, s, 521)
|
37
|
-
base64_signature =
|
39
|
+
base64_signature = jwt_base64_encode(signature)
|
38
40
|
base64_signature.should == 'AdwMgeerwtHoh-l192l60hp9wAHZFVJbLfD_UxMi70cwnZOYaRI1bKPWROc-mZZqwqT2SI-KGDKB34XO0aw_7XdtAG8GaSwFKdCAPZgoXD2YBJZCPEX3xKpRwcdOO8KpEHwJjyqOgzDO7iKvU8vcnwNrmxYbSW9ERBXukOXolLzeO_Jn'
|
39
41
|
end
|
40
42
|
|
@@ -54,12 +56,36 @@ describe Sandal::Sig::ES256 do
|
|
54
56
|
validator.valid?(signature, data).should == true
|
55
57
|
end
|
56
58
|
|
59
|
+
it 'can use string keys to sign data and verify signatures' do
|
60
|
+
private_key = <<KEY_END
|
61
|
+
-----BEGIN EC PARAMETERS-----
|
62
|
+
BggqhkjOPQMBBw==
|
63
|
+
-----END EC PARAMETERS-----
|
64
|
+
-----BEGIN EC PRIVATE KEY-----
|
65
|
+
MHcCAQEEII1Ar4w2EVK6wNL84EpVTVY7XXXVmVqyvjZ4EW9kBGhSoAoGCCqGSM49
|
66
|
+
AwEHoUQDQgAEVnYRY+AEiU+UNdYzl+KtuWvdAfKBoAmEekv4icfZQCbLew/eXIlv
|
67
|
+
32E8+j0bFYwYi3XjxCJXRE3S2iWPEEygcA==
|
68
|
+
-----END EC PRIVATE KEY-----
|
69
|
+
KEY_END
|
70
|
+
public_key = <<KEY_END
|
71
|
+
-----BEGIN PUBLIC KEY-----
|
72
|
+
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEVnYRY+AEiU+UNdYzl+KtuWvdAfKB
|
73
|
+
oAmEekv4icfZQCbLew/eXIlv32E8+j0bFYwYi3XjxCJXRE3S2iWPEEygcA==
|
74
|
+
-----END PUBLIC KEY-----
|
75
|
+
KEY_END
|
76
|
+
data = 'Hello ES256'
|
77
|
+
signer = Sandal::Sig::ES256.new(private_key)
|
78
|
+
signature = signer.sign(data)
|
79
|
+
validator = Sandal::Sig::ES256.new(public_key)
|
80
|
+
validator.valid?(signature, data).should == true
|
81
|
+
end
|
82
|
+
|
57
83
|
it 'can verify the signature in JWS section A3.1' do
|
58
84
|
x = make_bn([127, 205, 206, 39, 112, 246, 196, 93, 65, 131, 203, 238, 111, 219, 75, 123, 88, 7, 51, 53, 123, 233, 239, 19, 186, 207, 110, 60, 123, 209, 84, 69])
|
59
85
|
y = make_bn([199, 241, 68, 205, 27, 189, 155, 126, 135, 44, 223, 237, 185, 238, 185, 244, 179, 105, 93, 110, 169, 11, 36, 173, 138, 70, 35, 40, 133, 136, 229, 173])
|
60
86
|
d = make_bn([142, 155, 16, 158, 113, 144, 152, 191, 152, 4, 135, 223, 31, 93, 119, 233, 203, 41, 96, 110, 190, 210, 38, 59, 95, 87, 194, 19, 223, 132, 244, 178])
|
61
87
|
data = 'eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ'
|
62
|
-
signature =
|
88
|
+
signature = jwt_base64_decode('DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q')
|
63
89
|
|
64
90
|
group = OpenSSL::PKey::EC::Group.new('prime256v1')
|
65
91
|
public_key = OpenSSL::PKey::EC.new(group)
|
@@ -73,7 +99,7 @@ describe Sandal::Sig::ES256 do
|
|
73
99
|
y = make_bn([199, 241, 68, 205, 27, 189, 155, 126, 135, 44, 223, 237, 185, 238, 185, 244, 179, 105, 93, 110, 169, 11, 36, 173, 138, 70, 35, 40, 133, 136, 229, 173])
|
74
100
|
d = make_bn([142, 155, 16, 158, 113, 144, 152, 191, 152, 4, 135, 223, 31, 93, 119, 233, 203, 41, 96, 110, 190, 210, 38, 59, 95, 87, 194, 19, 223, 132, 244, 178])
|
75
101
|
data = 'not the data that was signed'
|
76
|
-
signature =
|
102
|
+
signature = jwt_base64_decode('DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q')
|
77
103
|
|
78
104
|
group = OpenSSL::PKey::EC::Group.new('prime256v1')
|
79
105
|
public_key = OpenSSL::PKey::EC.new(group)
|
@@ -104,6 +130,32 @@ describe Sandal::Sig::ES384 do
|
|
104
130
|
validator.valid?(signature, data).should == true
|
105
131
|
end
|
106
132
|
|
133
|
+
it 'can use string keys to sign data and verify signatures' do
|
134
|
+
private_key = <<KEY_END
|
135
|
+
-----BEGIN EC PARAMETERS-----
|
136
|
+
BgUrgQQAIg==
|
137
|
+
-----END EC PARAMETERS-----
|
138
|
+
-----BEGIN EC PRIVATE KEY-----
|
139
|
+
MIGkAgEBBDDHWNCqR7V8EQS1aeCWXJ6arxaj31tvfBozSVDhbgzvFsFM9tbgbhTb
|
140
|
+
1PGWXJEP91SgBwYFK4EEACKhZANiAASSjX9LH/BrmGp6WoHN/gBYN3Su/nIAApwM
|
141
|
+
iuFPbUFcWamxo8hUUTxLpdwvrrEHIVV2urXaVc0KHdSo93bVEHMOvLYjpXFXu+8f
|
142
|
+
6Fu17ofcECDNIaI9A+uydWY3E/cJUDM=
|
143
|
+
-----END EC PRIVATE KEY-----
|
144
|
+
KEY_END
|
145
|
+
public_key = <<KEY_END
|
146
|
+
-----BEGIN PUBLIC KEY-----
|
147
|
+
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEko1/Sx/wa5hqelqBzf4AWDd0rv5yAAKc
|
148
|
+
DIrhT21BXFmpsaPIVFE8S6XcL66xByFVdrq12lXNCh3UqPd21RBzDry2I6VxV7vv
|
149
|
+
H+hbte6H3BAgzSGiPQPrsnVmNxP3CVAz
|
150
|
+
-----END PUBLIC KEY-----
|
151
|
+
KEY_END
|
152
|
+
data = 'Hello ES384'
|
153
|
+
signer = Sandal::Sig::ES384.new(private_key)
|
154
|
+
signature = signer.sign(data)
|
155
|
+
validator = Sandal::Sig::ES384.new(public_key)
|
156
|
+
validator.valid?(signature, data).should == true
|
157
|
+
end
|
158
|
+
|
107
159
|
it 'raises an argument error if the key has the wrong curve' do
|
108
160
|
group = OpenSSL::PKey::EC::Group.new('secp521r1')
|
109
161
|
private_key = OpenSSL::PKey::EC.new(group).generate_key
|
@@ -126,12 +178,40 @@ describe Sandal::Sig::ES512 do
|
|
126
178
|
validator.valid?(signature, data).should == true
|
127
179
|
end
|
128
180
|
|
181
|
+
it 'can use string keys to sign data and verify signatures' do
|
182
|
+
private_key = <<KEY_END
|
183
|
+
-----BEGIN EC PARAMETERS-----
|
184
|
+
BgUrgQQAIw==
|
185
|
+
-----END EC PARAMETERS-----
|
186
|
+
-----BEGIN EC PRIVATE KEY-----
|
187
|
+
MIHcAgEBBEIBQokOnEjac/cnqtuEPrS+ekzObqwN4wcsh4MgW1M9D/lC+cfHcgso
|
188
|
+
QhdmC1fZEYV9G3eiVYRO818XBrgzX8sOqQGgBwYFK4EEACOhgYkDgYYABADWDbxx
|
189
|
+
FZHDy5nzP+tL1AcDgbVtZbin6jOz3E0EPzjDJS8267XROdSLxh/FdM54HaZ5ak2D
|
190
|
+
q0VThSOquJdkiy6jyAEOlXLeznDrV9ZP9ddFFFA8OMM2aImU+HdGq6rlWrAs7qMU
|
191
|
+
tu6lP9k3+WHD7Z1+YkCafox+lpraE4NrnlkVqO2RVg==
|
192
|
+
-----END EC PRIVATE KEY-----
|
193
|
+
KEY_END
|
194
|
+
public_key = <<KEY_END
|
195
|
+
-----BEGIN PUBLIC KEY-----
|
196
|
+
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQA1g28cRWRw8uZ8z/rS9QHA4G1bWW4
|
197
|
+
p+ozs9xNBD84wyUvNuu10TnUi8YfxXTOeB2meWpNg6tFU4UjqriXZIsuo8gBDpVy
|
198
|
+
3s5w61fWT/XXRRRQPDjDNmiJlPh3Rquq5VqwLO6jFLbupT/ZN/lhw+2dfmJAmn6M
|
199
|
+
fpaa2hODa55ZFajtkVY=
|
200
|
+
-----END PUBLIC KEY-----
|
201
|
+
KEY_END
|
202
|
+
data = 'Hello ES512'
|
203
|
+
signer = Sandal::Sig::ES512.new(private_key)
|
204
|
+
signature = signer.sign(data)
|
205
|
+
validator = Sandal::Sig::ES512.new(public_key)
|
206
|
+
validator.valid?(signature, data).should == true
|
207
|
+
end
|
208
|
+
|
129
209
|
it 'can verify the signature in JWS section A4.1' do
|
130
210
|
x = make_bn([1, 233, 41, 5, 15, 18, 79, 198, 188, 85, 199, 213, 57, 51, 101, 223, 157, 239, 74, 176, 194, 44, 178, 87, 152, 249, 52, 235, 4, 227, 198, 186, 227, 112, 26, 87, 167, 145, 14, 157, 129, 191, 54, 49, 89, 232, 235, 203, 21, 93, 99, 73, 244, 189, 182, 204, 248, 169, 76, 92, 89, 199, 170, 193, 1, 164])
|
131
211
|
y = make_bn([0, 52, 166, 68, 14, 55, 103, 80, 210, 55, 31, 209, 189, 194, 200, 243, 183, 29, 47, 78, 229, 234, 52, 50, 200, 21, 204, 163, 21, 96, 254, 93, 147, 135, 236, 119, 75, 85, 131, 134, 48, 229, 203, 191, 90, 140, 190, 10, 145, 221, 0, 100, 198, 153, 154, 31, 110, 110, 103, 250, 221, 237, 228, 200, 200, 246])
|
132
212
|
d = make_bn([1, 142, 105, 111, 176, 52, 80, 88, 129, 221, 17, 11, 72, 62, 184, 125, 50, 206, 73, 95, 227, 107, 55, 69, 237, 242, 216, 202, 228, 240, 242, 83, 159, 70, 21, 160, 233, 142, 171, 82, 179, 192, 197, 234, 196, 206, 7, 81, 133, 168, 231, 187, 71, 222, 172, 29, 29, 231, 123, 204, 246, 97, 53, 230, 61, 130] )
|
133
213
|
data = 'eyJhbGciOiJFUzUxMiJ9.UGF5bG9hZA'
|
134
|
-
signature =
|
214
|
+
signature = jwt_base64_decode('AdwMgeerwtHoh-l192l60hp9wAHZFVJbLfD_UxMi70cwnZOYaRI1bKPWROc-mZZqwqT2SI-KGDKB34XO0aw_7XdtAG8GaSwFKdCAPZgoXD2YBJZCPEX3xKpRwcdOO8KpEHwJjyqOgzDO7iKvU8vcnwNrmxYbSW9ERBXukOXolLzeO_Jn')
|
135
215
|
|
136
216
|
group = OpenSSL::PKey::EC::Group.new('secp521r1')
|
137
217
|
public_key = OpenSSL::PKey::EC.new(group)
|
@@ -145,7 +225,7 @@ describe Sandal::Sig::ES512 do
|
|
145
225
|
y = make_bn([0, 52, 166, 68, 14, 55, 103, 80, 210, 55, 31, 209, 189, 194, 200, 243, 183, 29, 47, 78, 229, 234, 52, 50, 200, 21, 204, 163, 21, 96, 254, 93, 147, 135, 236, 119, 75, 85, 131, 134, 48, 229, 203, 191, 90, 140, 190, 10, 145, 221, 0, 100, 198, 153, 154, 31, 110, 110, 103, 250, 221, 237, 228, 200, 200, 246])
|
146
226
|
d = make_bn([1, 142, 105, 111, 176, 52, 80, 88, 129, 221, 17, 11, 72, 62, 184, 125, 50, 206, 73, 95, 227, 107, 55, 69, 237, 242, 216, 202, 228, 240, 242, 83, 159, 70, 21, 160, 233, 142, 171, 82, 179, 192, 197, 234, 196, 206, 7, 81, 133, 168, 231, 187, 71, 222, 172, 29, 29, 231, 123, 204, 246, 97, 53, 230, 61, 130] )
|
147
227
|
data = 'not the data that was signed'
|
148
|
-
signature =
|
228
|
+
signature = jwt_base64_decode('AdwMgeerwtHoh-l192l60hp9wAHZFVJbLfD_UxMi70cwnZOYaRI1bKPWROc-mZZqwqT2SI-KGDKB34XO0aw_7XdtAG8GaSwFKdCAPZgoXD2YBJZCPEX3xKpRwcdOO8KpEHwJjyqOgzDO7iKvU8vcnwNrmxYbSW9ERBXukOXolLzeO_Jn')
|
149
229
|
|
150
230
|
group = OpenSSL::PKey::EC::Group.new('secp521r1')
|
151
231
|
public_key = OpenSSL::PKey::EC.new(group)
|
data/spec/sandal/sig/rs_spec.rb
CHANGED
@@ -1,7 +1,49 @@
|
|
1
1
|
require 'helper'
|
2
2
|
require 'openssl'
|
3
3
|
|
4
|
+
rsa_private_key = <<KEY_END
|
5
|
+
-----BEGIN RSA PRIVATE KEY-----
|
6
|
+
MIIEowIBAAKCAQEA4lt7zb5RlxwLVvw2mOKW06AGrBW3kfUVIkV6lImwqRps6jpZ
|
7
|
+
UNBOUkLjqIipXBkKeG6TbL46z4Rw2oEcUTTpOgm/9XEiJP/7nfkK/Sr6cChVLDr5
|
8
|
+
sohKnxkADrltdNwUUF0gPlK0REa2wiEvpd00D46Sfxfa5kpe/oYajCyRtesmGyrD
|
9
|
+
iD4BKJIaHTal4613l1k8HWhzza4qztbufZ4BMPfHkjyjOBWLsYSU0axI86b5WnxJ
|
10
|
+
KZUyghxeL51jYqV5eSeMBC3rr+HHuwdF3ulhvDo0jUxGjFJBG/6ZUheVNAGrAvD8
|
11
|
+
5RV3tp8ukcc02t2l0Z97PWDcZHpiiul+DvvmeQIDAQABAoIBADy56lbiDiWKAojN
|
12
|
+
lSAi+e/AaMnV8a+YnpjZJu+emORlEH8uNDP4DmsHQug98aGhnit9DtQHnON7VoNo
|
13
|
+
S96FYWSOpQ8F0PE4M5rH62jMFO/uAhuhnseExPA11swcdv745AJDWZkeuvnuNq2S
|
14
|
+
FaRb2dGqoCa0kadioGWMOKcOdfDlqcBApTI5IWy67wLJwF7+qTS+BT7BVAreQnQf
|
15
|
+
2qlYXSPWBxpL8iGobBGXQlsWTdiYDalrfyV0mvJaXxwHml3PMxyVrJyIvbc0HgMn
|
16
|
+
YqrBgnWrCz7FIU+8OXd4XFGqD09QpHn7SkdLvgXNlSy5fi3SeLN+ClyP1XvrFQYk
|
17
|
+
KhfCbwECgYEA/CpsxJEZzwCtvBeHlhNvEV5H2O0HI7Wb+pN5J3QyhMjdOc8KZozx
|
18
|
+
8D3hj6+I2NJM/Uj0V27LH0R92H29fKLjXUjtRwHtrV33PXWQAMzSS8gHOdeQe8iP
|
19
|
+
GgdAVDdDJsCR3W5oXEQGj7q8QgLAVdV0X9jZ6BG2MGIbdMi6SUE7DlECgYEA5cyY
|
20
|
+
/diePvEcXsEX8AgraOGwH+E4w+d21uJPjh4UpBhiJdrqEdZ2bjKtpl6czKmqu4tx
|
21
|
+
R7WNHqRd8LyUpdGHNvQU+kg1Uc1y1hy8HR1x1lZQYMBi4qkB1P6G08RHEL/oilxO
|
22
|
+
F1EIxYpwHbW/ZVXzGAyIr4Z9xGMLE5j9jVTsg6kCgYB4JdaxSdmcMdyVtDhcH2Ja
|
23
|
+
Siu9hiJSt2NcXwvo6opvji0qMCXqetmD+FgS2DZB6OHaBPq29gk+GqpDjpXMXugq
|
24
|
+
OGcl4BtY8V6uH+e/GdhRVztqKfWjpQnaAv55oeMTAcn+UW7UF21w6i5s3Va7Dvtl
|
25
|
+
97LLyjSelQA0ArgP007KIQKBgQDDzDAPGiK7PnUNxzi+LDfQhXurrhrP0MhRD0L5
|
26
|
+
tGeh6aS23G/UAwelnUiYGMVBHM98PLOohehX03S3Sfbd0kmDaTT2i8/ig0r1ZEZk
|
27
|
+
CFKWbbTOux2GQrps4PHAPdzPSLS6LyvachEnP22H4vPRRAp80zEjXVSLoFgvuotP
|
28
|
+
gKyFAQKBgBuMvB9XVILcn8IcZ3ax9B8agU4jeBLScoBV25GvSq7hUaFaNC4WMHzf
|
29
|
+
8av7nDTzlZlLLDMB8rvpz66gMWIWGeU5JWYJaiLMM/JeS9UJOo/6Wn10MvtNSBXH
|
30
|
+
30+kWAHpOSjtxL7tzmMrb46krFS/0iYDFKiLtIPNiacjxlEzBTZL
|
31
|
+
-----END RSA PRIVATE KEY-----
|
32
|
+
KEY_END
|
33
|
+
rsa_public_key = <<KEY_END
|
34
|
+
-----BEGIN PUBLIC KEY-----
|
35
|
+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4lt7zb5RlxwLVvw2mOKW
|
36
|
+
06AGrBW3kfUVIkV6lImwqRps6jpZUNBOUkLjqIipXBkKeG6TbL46z4Rw2oEcUTTp
|
37
|
+
Ogm/9XEiJP/7nfkK/Sr6cChVLDr5sohKnxkADrltdNwUUF0gPlK0REa2wiEvpd00
|
38
|
+
D46Sfxfa5kpe/oYajCyRtesmGyrDiD4BKJIaHTal4613l1k8HWhzza4qztbufZ4B
|
39
|
+
MPfHkjyjOBWLsYSU0axI86b5WnxJKZUyghxeL51jYqV5eSeMBC3rr+HHuwdF3ulh
|
40
|
+
vDo0jUxGjFJBG/6ZUheVNAGrAvD85RV3tp8ukcc02t2l0Z97PWDcZHpiiul+Dvvm
|
41
|
+
eQIDAQAB
|
42
|
+
-----END PUBLIC KEY-----
|
43
|
+
KEY_END
|
44
|
+
|
4
45
|
describe Sandal::Sig::RS256 do
|
46
|
+
|
5
47
|
it 'can sign data and verify signatures' do
|
6
48
|
data = 'Hello RS256'
|
7
49
|
private_key = OpenSSL::PKey::RSA.generate(2048)
|
@@ -10,9 +52,19 @@ describe Sandal::Sig::RS256 do
|
|
10
52
|
validator = Sandal::Sig::RS256.new(private_key.public_key)
|
11
53
|
validator.valid?(signature, data).should == true
|
12
54
|
end
|
55
|
+
|
56
|
+
it 'can use string keys to sign data and verify signatures' do
|
57
|
+
data = 'Hello RS256'
|
58
|
+
signer = Sandal::Sig::RS256.new(rsa_private_key)
|
59
|
+
signature = signer.sign(data)
|
60
|
+
validator = Sandal::Sig::RS256.new(rsa_public_key)
|
61
|
+
validator.valid?(signature, data).should == true
|
62
|
+
end
|
63
|
+
|
13
64
|
end
|
14
65
|
|
15
66
|
describe Sandal::Sig::RS384 do
|
67
|
+
|
16
68
|
it 'can sign data and verify signatures' do
|
17
69
|
data = 'Hello RS384'
|
18
70
|
private_key = OpenSSL::PKey::RSA.generate(2048)
|
@@ -21,9 +73,19 @@ describe Sandal::Sig::RS384 do
|
|
21
73
|
validator = Sandal::Sig::RS384.new(private_key.public_key)
|
22
74
|
validator.valid?(signature, data).should == true
|
23
75
|
end
|
76
|
+
|
77
|
+
it 'can use string keys to sign data and verify signatures' do
|
78
|
+
data = 'Hello RS384'
|
79
|
+
signer = Sandal::Sig::RS384.new(rsa_private_key)
|
80
|
+
signature = signer.sign(data)
|
81
|
+
validator = Sandal::Sig::RS384.new(rsa_public_key)
|
82
|
+
validator.valid?(signature, data).should == true
|
83
|
+
end
|
84
|
+
|
24
85
|
end
|
25
86
|
|
26
87
|
describe Sandal::Sig::RS512 do
|
88
|
+
|
27
89
|
it 'can sign data and verify signatures' do
|
28
90
|
data = 'Hello RS512'
|
29
91
|
private_key = OpenSSL::PKey::RSA.generate(2048)
|
@@ -32,4 +94,13 @@ describe Sandal::Sig::RS512 do
|
|
32
94
|
validator = Sandal::Sig::RS512.new(private_key.public_key)
|
33
95
|
validator.valid?(signature, data).should == true
|
34
96
|
end
|
97
|
+
|
98
|
+
it 'can use string keys to sign data and verify signatures' do
|
99
|
+
data = 'Hello RS512'
|
100
|
+
signer = Sandal::Sig::RS512.new(rsa_private_key)
|
101
|
+
signature = signer.sign(data)
|
102
|
+
validator = Sandal::Sig::RS512.new(rsa_public_key)
|
103
|
+
validator.valid?(signature, data).should == true
|
104
|
+
end
|
105
|
+
|
35
106
|
end
|
data/spec/sandal/util_spec.rb
CHANGED
@@ -1,37 +1,77 @@
|
|
1
1
|
require 'helper'
|
2
2
|
require 'openssl'
|
3
|
+
require 'benchmark'
|
4
|
+
|
5
|
+
include Sandal::Util
|
3
6
|
|
4
7
|
describe Sandal::Util do
|
5
8
|
|
6
|
-
|
7
|
-
src = "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n \"http://example.com/is_root\":true}"
|
8
|
-
encoded = Sandal::Util.base64_encode(src)
|
9
|
-
encoded.should == 'eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ'
|
10
|
-
val = Sandal::Util.base64_decode(encoded)
|
11
|
-
val.should == src
|
12
|
-
end
|
9
|
+
context '#jwt_base64_decode' do
|
13
10
|
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
it 'decodes base64 as per JWT example 6.1' do
|
12
|
+
encoded = 'eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ'
|
13
|
+
val = jwt_base64_decode(encoded)
|
14
|
+
val.should == %!{"iss":"joe",\r\n "exp":1300819380,\r\n "http://example.com/is_root":true}!
|
15
|
+
end
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
it 'raises an ArgumentError if base64 strings contain padding' do
|
18
|
+
expect { jwt_base64_decode('eyJpc3MiOiJq=') }.to raise_error ArgumentError
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'raises an ArgumentError if base64 strings are invalid' do
|
22
|
+
expect { jwt_base64_decode('not valid base64') }.to raise_error ArgumentError
|
23
|
+
end
|
21
24
|
|
22
|
-
it 'compares nil strings as unequal to empty strings' do
|
23
|
-
Sandal::Util.secure_equals(nil, '').should == false
|
24
|
-
Sandal::Util.secure_equals('', nil).should == false
|
25
25
|
end
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
context '#jwt_base64_encode' do
|
28
|
+
|
29
|
+
it 'encodes base64 as per JWT example 6.1' do
|
30
|
+
src = %!{"iss":"joe",\r\n "exp":1300819380,\r\n "http://example.com/is_root":true}!
|
31
|
+
encoded = jwt_base64_encode(src)
|
32
|
+
encoded.should == 'eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ'
|
33
|
+
end
|
34
|
+
|
30
35
|
end
|
31
36
|
|
32
|
-
|
33
|
-
|
34
|
-
|
37
|
+
context '#jwt_strings_equal?' do
|
38
|
+
|
39
|
+
it 'compares nil strings as equal' do
|
40
|
+
jwt_strings_equal?(nil, nil).should == true
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'compares empty strings as equal' do
|
44
|
+
jwt_strings_equal?('', '').should == true
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'compares nil strings as unequal to empty strings' do
|
48
|
+
jwt_strings_equal?(nil, '').should == false
|
49
|
+
jwt_strings_equal?('', nil).should == false
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'compares equal strings as equal' do
|
53
|
+
jwt_strings_equal?('hello', 'hello').should == true
|
54
|
+
jwt_strings_equal?('a longer string', 'a longer string').should == true
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'compares unequal strings as unequal' do
|
58
|
+
jwt_strings_equal?('hello', 'world').should == false
|
59
|
+
jwt_strings_equal?('a longer string', 'a different longer string').should == false
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'compares strings without short-circuiting', :timing_dependent do
|
63
|
+
measure_equals = -> a, b do
|
64
|
+
Benchmark.realtime { 100.times { jwt_strings_equal?(a, b) } }
|
65
|
+
end
|
66
|
+
ref = 'a' * 10000
|
67
|
+
cmp1 = ('a' * 9999) + 'b'
|
68
|
+
cmp2 = 'a' + ('b' * 9999)
|
69
|
+
t1 = measure_equals.(ref, cmp1)
|
70
|
+
t2 = measure_equals.(ref, cmp2)
|
71
|
+
range = (t1 - t1/20.0)..(t1 + t1/20.0)
|
72
|
+
range.should === t2
|
73
|
+
end
|
74
|
+
|
35
75
|
end
|
36
76
|
|
37
77
|
end
|
data/spec/sandal_spec.rb
CHANGED
@@ -45,14 +45,20 @@ describe Sandal do
|
|
45
45
|
expect { Sandal.decode_token(token) }.to raise_error Sandal::ClaimError
|
46
46
|
end
|
47
47
|
|
48
|
-
it 'does not raise an error when the expiry date is
|
48
|
+
it 'does not raise an error when the expiry date is in the past but validation is disabled' do
|
49
49
|
token = Sandal.encode_token({ 'exp' => (Time.now - 600).to_i }, nil)
|
50
|
-
Sandal.decode_token(token)
|
50
|
+
Sandal.decode_token(token) do |header, options|
|
51
|
+
options[:ignore_exp] = true
|
52
|
+
nil
|
53
|
+
end
|
51
54
|
end
|
52
55
|
|
53
56
|
it 'does not raise an error when the expiry date is in the past but within the clock skew' do
|
54
57
|
token = Sandal.encode_token({ 'exp' => (Time.now - 60).to_i }, nil)
|
55
|
-
Sandal.decode_token(token)
|
58
|
+
Sandal.decode_token(token) do |header, options|
|
59
|
+
options[:max_clock_skew] = 300
|
60
|
+
nil
|
61
|
+
end
|
56
62
|
end
|
57
63
|
|
58
64
|
it 'does not raise an error when the expiry date is valid' do
|
@@ -70,14 +76,20 @@ describe Sandal do
|
|
70
76
|
expect { Sandal.decode_token(token) }.to raise_error Sandal::ClaimError
|
71
77
|
end
|
72
78
|
|
73
|
-
it 'does not raise an error when the not-before date is
|
79
|
+
it 'does not raise an error when the not-before date is in the future but validation is disabled' do
|
74
80
|
token = Sandal.encode_token({ 'nbf' => (Time.now + 600).to_i }, nil)
|
75
|
-
Sandal.decode_token(token)
|
81
|
+
Sandal.decode_token(token) do |header, options|
|
82
|
+
options[:ignore_nbf] = true
|
83
|
+
nil
|
84
|
+
end
|
76
85
|
end
|
77
86
|
|
78
87
|
it 'does not raise an error when the not-before date is in the future but within the clock skew' do
|
79
88
|
token = Sandal.encode_token({ 'nbf' => (Time.now + 60).to_i }, nil)
|
80
|
-
Sandal.decode_token(token)
|
89
|
+
Sandal.decode_token(token) do |header, options|
|
90
|
+
options[:max_clock_skew] = 300
|
91
|
+
nil
|
92
|
+
end
|
81
93
|
end
|
82
94
|
|
83
95
|
it 'does not raise an error when the not-before is valid' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sandal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Greg Beech
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-04-
|
11
|
+
date: 2013-04-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: multi_json
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - '>='
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '2.13'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.7'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.7'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: coveralls
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -144,8 +158,13 @@ files:
|
|
144
158
|
- lib/sandal/version.rb
|
145
159
|
- sandal.gemspec
|
146
160
|
- spec/helper.rb
|
161
|
+
- spec/sandal/claims_spec.rb
|
147
162
|
- spec/sandal/enc/a128cbc_hs256_spec.rb
|
148
163
|
- spec/sandal/enc/a128gcm_spec.rb
|
164
|
+
- spec/sandal/enc/a256cbc_hs512_spec.rb
|
165
|
+
- spec/sandal/enc/a256gcm_spec.rb
|
166
|
+
- spec/sandal/enc/alg/direct_spec.rb
|
167
|
+
- spec/sandal/enc/shared_examples.rb
|
149
168
|
- spec/sandal/sig/es_spec.rb
|
150
169
|
- spec/sandal/sig/hs_spec.rb
|
151
170
|
- spec/sandal/sig/rs_spec.rb
|
@@ -178,8 +197,13 @@ specification_version: 4
|
|
178
197
|
summary: A JSON Web Token (JWT) library.
|
179
198
|
test_files:
|
180
199
|
- spec/helper.rb
|
200
|
+
- spec/sandal/claims_spec.rb
|
181
201
|
- spec/sandal/enc/a128cbc_hs256_spec.rb
|
182
202
|
- spec/sandal/enc/a128gcm_spec.rb
|
203
|
+
- spec/sandal/enc/a256cbc_hs512_spec.rb
|
204
|
+
- spec/sandal/enc/a256gcm_spec.rb
|
205
|
+
- spec/sandal/enc/alg/direct_spec.rb
|
206
|
+
- spec/sandal/enc/shared_examples.rb
|
183
207
|
- spec/sandal/sig/es_spec.rb
|
184
208
|
- spec/sandal/sig/hs_spec.rb
|
185
209
|
- spec/sandal/sig/rs_spec.rb
|