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
@@ -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
|