sandal 0.5.1 → 0.5.2
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/.travis.yml +8 -3
- data/CHANGELOG.md +17 -0
- data/Gemfile +12 -2
- data/README.md +3 -1
- data/lib/sandal.rb +7 -7
- data/lib/sandal/claims.rb +1 -1
- data/lib/sandal/json.rb +43 -0
- data/lib/sandal/version.rb +1 -1
- data/sandal.gemspec +0 -1
- data/spec/sandal/claims_spec.rb +6 -6
- data/spec/sandal/enc/a128cbc_hs256_spec.rb +2 -2
- data/spec/sandal/enc/a128gcm_spec.rb +1 -1
- data/spec/sandal/enc/a256cbc_hs512_spec.rb +1 -1
- data/spec/sandal/enc/a256gcm_spec.rb +2 -2
- data/spec/sandal/enc/alg/direct_spec.rb +5 -5
- data/spec/sandal/enc/alg/rsa_spec.rb +9 -9
- data/spec/sandal/enc/shared_examples.rb +3 -3
- data/spec/sandal/sig/es_spec.rb +25 -25
- data/spec/sandal/sig/hs_spec.rb +8 -8
- data/spec/sandal/sig/rs_spec.rb +10 -10
- data/spec/sandal/util_spec.rb +12 -12
- data/spec/sandal_spec.rb +27 -28
- metadata +4 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9335b993fe3eba12af0ce2a5f5b548495cb236e
|
4
|
+
data.tar.gz: acc295000fad37701881ec296247358ea5e1527c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4946603d929b6f6c063a458fe63d2f8847ecf3b97ef0c8e9fab3ea57119cd2bdee51c070dea1e693778e9a53b90c58a43a6c012cd4d24a59510e5593d0fe40ed
|
7
|
+
data.tar.gz: 62f892126c09d7dd8845be99120230a4858a51d501efef370a8afb2f35569ab1167a252bb655f985c8cf85fccfbc0f4577e3550af67bd852f3631f8770d4d3f5
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
## 0.5.2 (03 January 2014)
|
2
|
+
|
3
|
+
Improvements:
|
4
|
+
|
5
|
+
- RSpec assertions now use 3.x syntax (thanks to [@petergoldstein](https://github.com/petergoldstein))
|
6
|
+
- MultiJson is now an optional dependency (thanks [@petergoldstein](https://github.com/petergoldstein))
|
7
|
+
|
8
|
+
## 0.5.1 (11 June 2013)
|
9
|
+
|
10
|
+
Bug fixes:
|
11
|
+
|
12
|
+
- Fixed some loading errors when using the library with Sinatra.
|
13
|
+
|
14
|
+
Breaking changes:
|
15
|
+
|
16
|
+
- Methods on the `Sandal::Util` module are now static rather than being designed to be mixed in.
|
17
|
+
|
1
18
|
## 0.5.0 (07 June 2013)
|
2
19
|
|
3
20
|
Features:
|
data/Gemfile
CHANGED
@@ -1,2 +1,12 @@
|
|
1
|
-
source
|
2
|
-
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
platforms :rbx do
|
4
|
+
gem "rubysl", "~> 2.0"
|
5
|
+
gem "rubysl-bundler", "~> 2.0"
|
6
|
+
gem "rubysl-rake", "~> 2.0"
|
7
|
+
gem "rubysl-securerandom", "~> 2.0"
|
8
|
+
gem "rubinius-coverage", "~> 2.0"
|
9
|
+
gem "json"
|
10
|
+
end
|
11
|
+
|
12
|
+
gemspec
|
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# Sandal [](https://travis-ci.org/gregbeech/sandal) [](https://coveralls.io/r/gregbeech/sandal) [](https://codeclimate.com/github/gregbeech/sandal) [](https://gemnasium.com/gregbeech/sandal)
|
2
2
|
|
3
|
-
A Ruby library for creating and reading [JSON Web Tokens (JWT) draft-
|
3
|
+
A Ruby library for creating and reading [JSON Web Tokens (JWT) draft-13](http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-13), supporting [JSON Web Signatures (JWS) draft-18](http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-18) and [JSON Web Encryption (JWE) draft-18](http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption-18). See the [CHANGELOG](CHANGELOG.md) for version history.
|
4
|
+
|
5
|
+
_Note: Updates have not been forthcoming of late as (a) I've been very busy leading a team to get a major new project off the ground, and (b) there have been no incompatible updates to the specs, just terminology changes. I do have plans for the library such as refactoring into encoder/decoder classes and adding JSON serialisation and JWK support; if you want to help out it would be most welcome._
|
4
6
|
|
5
7
|
## Installation
|
6
8
|
|
data/lib/sandal.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
require "multi_json"
|
2
1
|
require "openssl"
|
3
2
|
require "zlib"
|
4
3
|
require "sandal/version"
|
5
4
|
require "sandal/claims"
|
6
5
|
require "sandal/enc"
|
6
|
+
require "sandal/json"
|
7
7
|
require "sandal/sig"
|
8
8
|
require "sandal/util"
|
9
9
|
|
@@ -107,9 +107,9 @@ module Sandal
|
|
107
107
|
header = {}
|
108
108
|
header["alg"] = signer.name
|
109
109
|
header = header_fields.merge(header) if header_fields
|
110
|
-
header =
|
110
|
+
header = Sandal::Json.dump(header)
|
111
111
|
|
112
|
-
payload =
|
112
|
+
payload = Sandal::Json.dump(payload) unless payload.is_a?(String)
|
113
113
|
|
114
114
|
sec_input = [header, payload].map { |p| Sandal::Util.jwt_base64_encode(p) }.join(".")
|
115
115
|
signature = signer.sign(sec_input)
|
@@ -135,7 +135,7 @@ module Sandal
|
|
135
135
|
payload = Zlib::Deflate.deflate(payload, Zlib::BEST_COMPRESSION)
|
136
136
|
end
|
137
137
|
|
138
|
-
encrypter.encrypt(
|
138
|
+
encrypter.encrypt(Sandal::Json.dump(header), payload)
|
139
139
|
end
|
140
140
|
|
141
141
|
# Decodes and validates a signed and/or encrypted JSON Web Token, recursing into any nested tokens, and returns the
|
@@ -177,7 +177,7 @@ module Sandal
|
|
177
177
|
end
|
178
178
|
end
|
179
179
|
|
180
|
-
if header["cty"]
|
180
|
+
if header.has_key?("cty") && header["cty"] =~ /\AJWT\Z/i
|
181
181
|
if depth > 0
|
182
182
|
if block_given?
|
183
183
|
decode_token(payload, depth - 1, &Proc.new)
|
@@ -206,7 +206,7 @@ module Sandal
|
|
206
206
|
# Decodes the parts of a token.
|
207
207
|
def self.decode_token_parts(parts)
|
208
208
|
parts = parts.map { |part| Sandal::Util.jwt_base64_decode(part) }
|
209
|
-
parts[0] =
|
209
|
+
parts[0] = Sandal::Json.load(parts[0])
|
210
210
|
parts
|
211
211
|
rescue
|
212
212
|
raise TokenError, "Invalid token encoding."
|
@@ -214,7 +214,7 @@ module Sandal
|
|
214
214
|
|
215
215
|
# Parses the content of a token and validates the claims if is JSON claims.
|
216
216
|
def self.parse_and_validate(payload, options)
|
217
|
-
claims =
|
217
|
+
claims = Sandal::Json.load(payload) rescue nil
|
218
218
|
if claims
|
219
219
|
claims.extend(Sandal::Claims).validate_claims(options)
|
220
220
|
else
|
data/lib/sandal/claims.rb
CHANGED
data/lib/sandal/json.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
module Sandal
|
2
|
+
# Contains JSON encode and decode functionality.
|
3
|
+
module Json
|
4
|
+
if !defined?(MultiJson)
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
# Decode a JSON string into Ruby. This version delegates to the included JSON engine.
|
8
|
+
#
|
9
|
+
# @param encoded [String] The JSON string representation of the object.
|
10
|
+
# @return The decoded Ruby object.
|
11
|
+
def self.load(encoded)
|
12
|
+
JSON.parse(encoded)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Encodes a Ruby object as JSON. This version delegates to the included JSON engine.
|
16
|
+
#
|
17
|
+
# @param raw The Ruby object to be encoded
|
18
|
+
# @return [String] The JSON string representation of the object.
|
19
|
+
def self.dump(raw)
|
20
|
+
JSON.generate(raw)
|
21
|
+
end
|
22
|
+
|
23
|
+
else
|
24
|
+
require 'multi_json'
|
25
|
+
|
26
|
+
# Decode a JSON string into Ruby. This version delegates to MultiJson.
|
27
|
+
#
|
28
|
+
# @param encoded [String] The JSON string representation of the object.
|
29
|
+
# @return The decoded Ruby object.
|
30
|
+
def self.load(encoded)
|
31
|
+
MultiJson.load(encoded)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Encodes a Ruby object as JSON. This version delegates to MultiJson.
|
35
|
+
#
|
36
|
+
# @param raw The Ruby object to be encoded
|
37
|
+
# @return [String] The JSON string representation of the object.
|
38
|
+
def self.dump(raw)
|
39
|
+
MultiJson.dump(raw)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/sandal/version.rb
CHANGED
data/sandal.gemspec
CHANGED
@@ -17,7 +17,6 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.require_paths = ["lib"]
|
18
18
|
s.extra_rdoc_files = ["README.md", "LICENSE.md", "CHANGELOG.md"]
|
19
19
|
|
20
|
-
s.add_runtime_dependency "multi_json", "~> 1.7"
|
21
20
|
s.add_runtime_dependency "jruby-openssl", "~> 0.7", ">= 0.7.3" if RUBY_PLATFORM == "java"
|
22
21
|
|
23
22
|
s.add_development_dependency "bundler", ">= 1.3"
|
data/spec/sandal/claims_spec.rb
CHANGED
@@ -7,38 +7,38 @@ describe Sandal::Claims do
|
|
7
7
|
it 'calls #validate_aud when valid audiences are provided' do
|
8
8
|
claims = { 'aud' => 'example.org' }.extend(Sandal::Claims)
|
9
9
|
valid_aud = %w(example.org)
|
10
|
-
claims.
|
10
|
+
expect(claims).to receive(:validate_aud).with(valid_aud)
|
11
11
|
claims.validate_claims(valid_aud: valid_aud)
|
12
12
|
end
|
13
13
|
|
14
14
|
it 'calls #validate_exp by default' do
|
15
15
|
claims = {}.extend(Sandal::Claims)
|
16
|
-
claims.
|
16
|
+
expect(claims).to receive(:validate_exp)
|
17
17
|
claims.validate_claims
|
18
18
|
end
|
19
19
|
|
20
20
|
it 'does not call #validate_exp when the :ignore_exp option is set' do
|
21
21
|
claims = {}.extend(Sandal::Claims)
|
22
|
-
claims.
|
22
|
+
expect(claims).not_to receive(:validate_exp)
|
23
23
|
claims.validate_claims(ignore_exp: true)
|
24
24
|
end
|
25
25
|
|
26
26
|
it 'calls #validate_iss when valid issuers are provided' do
|
27
27
|
claims = { 'iss' => 'example.org' }.extend(Sandal::Claims)
|
28
28
|
valid_iss = %w(example.org)
|
29
|
-
claims.
|
29
|
+
expect(claims).to receive(:validate_iss).with(valid_iss)
|
30
30
|
claims.validate_claims(valid_iss: valid_iss)
|
31
31
|
end
|
32
32
|
|
33
33
|
it 'calls #validate_nbf by default' do
|
34
34
|
claims = {}.extend(Sandal::Claims)
|
35
|
-
claims.
|
35
|
+
expect(claims).to receive(:validate_nbf)
|
36
36
|
claims.validate_claims
|
37
37
|
end
|
38
38
|
|
39
39
|
it 'does not call #validate_nbf when the :ignore_nbf option is set' do
|
40
40
|
claims = {}.extend(Sandal::Claims)
|
41
|
-
claims.
|
41
|
+
expect(claims).not_to receive(:validate_nbf)
|
42
42
|
claims.validate_claims(ignore_nbf: true)
|
43
43
|
end
|
44
44
|
|
@@ -9,7 +9,7 @@ describe Sandal::Enc::A128CBC_HS256 do
|
|
9
9
|
context "#name" do
|
10
10
|
it "is 'A128CBC-HS256'" do
|
11
11
|
enc = Sandal::Enc::A128CBC_HS256.new(Sandal::Enc::Alg::Direct.new("a cmk"))
|
12
|
-
enc.name.
|
12
|
+
expect(enc.name).to eq("A128CBC-HS256")
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
@@ -17,7 +17,7 @@ describe Sandal::Enc::A128CBC_HS256 do
|
|
17
17
|
it "can decrypt the example token from JWE draft-11 appendix 2", :jruby_incompatible do
|
18
18
|
token="eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A.AxY8DCtDaGlsbGljb3RoZQ.KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY.9hH0vgRfYgPnAHOd8stkvw"
|
19
19
|
enc = Sandal::Enc::A128CBC_HS256.new(Sandal::Enc::Alg::RSA1_5.new(SampleKeys.jwe_draft11_appendix2_rsa))
|
20
|
-
enc.decrypt(token).
|
20
|
+
expect(enc.decrypt(token)).to eq("Live long and prosper.")
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
@@ -11,7 +11,7 @@ describe Sandal::Enc::A256CBC_HS512 do
|
|
11
11
|
context "#name" do
|
12
12
|
it "is 'A256CBC-HS512'" do
|
13
13
|
enc = Sandal::Enc::A256CBC_HS512.new(Sandal::Enc::Alg::Direct.new("a cmk"))
|
14
|
-
enc.name.
|
14
|
+
expect(enc.name).to eq("A256CBC-HS512")
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
@@ -12,7 +12,7 @@ describe Sandal::Enc::A256GCM do
|
|
12
12
|
|
13
13
|
it "is 'A256GCM'" do
|
14
14
|
enc = Sandal::Enc::A256GCM.new(Sandal::Enc::Alg::Direct.new("a cmk"))
|
15
|
-
enc.name.
|
15
|
+
expect(enc.name).to eq("A256GCM")
|
16
16
|
end
|
17
17
|
|
18
18
|
end
|
@@ -21,7 +21,7 @@ describe Sandal::Enc::A256GCM do
|
|
21
21
|
it "can decrypt the example token from JWE draft-11 appendix 1", :jruby_incompatible do
|
22
22
|
token = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg.48V1_ALb6US04U3b.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ"
|
23
23
|
enc = Sandal::Enc::A256GCM.new(Sandal::Enc::Alg::RSA_OAEP.new(SampleKeys.jwe_draft11_appendix1_rsa))
|
24
|
-
enc.decrypt(token).
|
24
|
+
expect(enc.decrypt(token)).to eq("The true sign of intelligence is not knowledge but imagination.")
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -6,7 +6,7 @@ describe Sandal::Enc::Alg::Direct do
|
|
6
6
|
context "#name" do
|
7
7
|
it "is 'dir'" do
|
8
8
|
alg = Sandal::Enc::Alg::Direct.new("some key")
|
9
|
-
alg.name.
|
9
|
+
expect(alg.name).to eq("dir")
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
@@ -14,14 +14,14 @@ describe Sandal::Enc::Alg::Direct do
|
|
14
14
|
it "returns the pre-shared key" do
|
15
15
|
key = "the pre-shared key"
|
16
16
|
alg = Sandal::Enc::Alg::Direct.new(key)
|
17
|
-
alg.preshared_key.
|
17
|
+
expect(alg.preshared_key).to eq(key)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
21
|
context "#encrypt_key" do
|
22
22
|
it "returns an empty string" do
|
23
23
|
alg = Sandal::Enc::Alg::Direct.new("the real key")
|
24
|
-
alg.encrypt_key("any value").
|
24
|
+
expect(alg.encrypt_key("any value")).to eq("")
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -30,13 +30,13 @@ describe Sandal::Enc::Alg::Direct do
|
|
30
30
|
it "returns the pre-shared content key when the value to decrypt is nil" do
|
31
31
|
key = "a pre-shared key"
|
32
32
|
alg = Sandal::Enc::Alg::Direct.new(key)
|
33
|
-
alg.decrypt_key(nil).
|
33
|
+
expect(alg.decrypt_key(nil)).to eq(key)
|
34
34
|
end
|
35
35
|
|
36
36
|
it "returns the pre-shared content key when the value to decrypt is empty" do
|
37
37
|
key = "my pre-shared key"
|
38
38
|
alg = Sandal::Enc::Alg::Direct.new(key)
|
39
|
-
alg.decrypt_key("").
|
39
|
+
expect(alg.decrypt_key("")).to eq(key)
|
40
40
|
end
|
41
41
|
|
42
42
|
it "raises an InvalidTokenError if the value to decrypt is not nil or empty" do
|
@@ -8,7 +8,7 @@ shared_examples "encryption and decryption" do |enc_class|
|
|
8
8
|
encrypter = enc_class.new(key.public_key)
|
9
9
|
decrypter = enc_class.new(key)
|
10
10
|
key = "an encryption key"
|
11
|
-
decrypter.decrypt_key(encrypter.encrypt_key(key)).
|
11
|
+
expect(decrypter.decrypt_key(encrypter.encrypt_key(key))).to eq(key)
|
12
12
|
end
|
13
13
|
|
14
14
|
it "can use DER-encoded keys to encrypt and decrypt a content master key" do
|
@@ -16,7 +16,7 @@ shared_examples "encryption and decryption" do |enc_class|
|
|
16
16
|
encrypter = enc_class.new(key.public_key.to_der)
|
17
17
|
decrypter = enc_class.new(key.to_der)
|
18
18
|
key = "an encryption key"
|
19
|
-
decrypter.decrypt_key(encrypter.encrypt_key(key)).
|
19
|
+
expect(decrypter.decrypt_key(encrypter.encrypt_key(key))).to eq(key)
|
20
20
|
end
|
21
21
|
|
22
22
|
it "can use PEM-encoded keys to encrypt and decrypt a content master key" do
|
@@ -24,7 +24,7 @@ shared_examples "encryption and decryption" do |enc_class|
|
|
24
24
|
encrypter = enc_class.new(key.public_key.to_pem)
|
25
25
|
decrypter = enc_class.new(key.to_pem)
|
26
26
|
key = "an encryption key"
|
27
|
-
decrypter.decrypt_key(encrypter.encrypt_key(key)).
|
27
|
+
expect(decrypter.decrypt_key(encrypter.encrypt_key(key))).to eq(key)
|
28
28
|
end
|
29
29
|
|
30
30
|
context "#decrypt_key" do
|
@@ -61,7 +61,7 @@ describe Sandal::Enc::Alg::RSA1_5 do
|
|
61
61
|
context "#name" do
|
62
62
|
it "is 'RSA1_5'" do
|
63
63
|
alg = Sandal::Enc::Alg::RSA1_5.new(OpenSSL::PKey::RSA.new(2048))
|
64
|
-
alg.name.
|
64
|
+
expect(alg.name).to eq("RSA1_5")
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
@@ -70,19 +70,19 @@ describe Sandal::Enc::Alg::RSA1_5 do
|
|
70
70
|
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*")
|
71
71
|
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*")
|
72
72
|
alg = Sandal::Enc::Alg::RSA1_5.new(SampleKeys.jwe_draft11_appendix2_rsa)
|
73
|
-
alg.decrypt_key(encrypted_key).
|
73
|
+
expect(alg.decrypt_key(encrypted_key)).to eq(key)
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
77
77
|
end
|
78
78
|
|
79
|
-
describe Sandal::Enc::Alg::RSA_OAEP do
|
80
|
-
include_examples "encryption and decryption", Sandal::Enc::Alg::RSA_OAEP
|
79
|
+
describe Sandal::Enc::Alg::RSA_OAEP do
|
80
|
+
include_examples "encryption and decryption", Sandal::Enc::Alg::RSA_OAEP
|
81
81
|
|
82
82
|
context "#name" do
|
83
83
|
it "is 'RSA-OAEP'" do
|
84
84
|
alg = Sandal::Enc::Alg::RSA_OAEP.new(OpenSSL::PKey::RSA.new(2048))
|
85
|
-
alg.name.
|
85
|
+
expect(alg.name).to eq("RSA-OAEP")
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
@@ -91,7 +91,7 @@ describe Sandal::Enc::Alg::RSA_OAEP do
|
|
91
91
|
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*")
|
92
92
|
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*")
|
93
93
|
alg = Sandal::Enc::Alg::RSA_OAEP.new(SampleKeys.jwe_draft11_appendix1_rsa)
|
94
|
-
alg.decrypt_key(encrypted_key).
|
94
|
+
expect(alg.decrypt_key(encrypted_key)).to eq(key)
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
@@ -9,7 +9,7 @@ shared_examples "algorithm compatibility" do |enc_class|
|
|
9
9
|
enc = enc_class.new(Sandal::Enc::Alg::Direct.new(content_encryption_key))
|
10
10
|
token = enc.encrypt("", payload)
|
11
11
|
output = enc.decrypt(token)
|
12
|
-
output.
|
12
|
+
expect(output).to eq(payload)
|
13
13
|
end
|
14
14
|
|
15
15
|
it "can encrypt and decrypt tokens with the RSA1_5 algorithm" do
|
@@ -19,7 +19,7 @@ shared_examples "algorithm compatibility" do |enc_class|
|
|
19
19
|
token = encrypter.encrypt("", payload)
|
20
20
|
decrypter = enc_class.new(Sandal::Enc::Alg::RSA1_5.new(rsa))
|
21
21
|
output = decrypter.decrypt(token)
|
22
|
-
output.
|
22
|
+
expect(output).to eq(payload)
|
23
23
|
end
|
24
24
|
|
25
25
|
it "can encrypt and decrypt tokens with the RSA-OAEP algorithm" do
|
@@ -29,7 +29,7 @@ shared_examples "algorithm compatibility" do |enc_class|
|
|
29
29
|
token = encrypter.encrypt("", payload)
|
30
30
|
decrypter = enc_class.new(Sandal::Enc::Alg::RSA_OAEP.new(rsa))
|
31
31
|
output = decrypter.decrypt(token)
|
32
|
-
output.
|
32
|
+
expect(output).to eq(payload)
|
33
33
|
end
|
34
34
|
|
35
35
|
end
|
data/spec/sandal/sig/es_spec.rb
CHANGED
@@ -16,44 +16,44 @@ shared_examples "signing and validation" do |enc_class|
|
|
16
16
|
|
17
17
|
it "can sign data and validate signatures" do
|
18
18
|
data = "some data to sign"
|
19
|
-
group = OpenSSL::PKey::EC::Group.new(enc_class::CURVE_NAME)
|
19
|
+
group = OpenSSL::PKey::EC::Group.new(enc_class::CURVE_NAME)
|
20
20
|
private_key = OpenSSL::PKey::EC.new(group).generate_key
|
21
21
|
signer = enc_class.new(private_key)
|
22
22
|
signature = signer.sign(data)
|
23
23
|
public_key = OpenSSL::PKey::EC.new(group)
|
24
24
|
public_key.public_key = private_key.public_key
|
25
25
|
validator = enc_class.new(public_key)
|
26
|
-
validator.valid?(signature, data).
|
26
|
+
expect(validator.valid?(signature, data)).to eq(true)
|
27
27
|
end
|
28
28
|
|
29
29
|
it "can use DER-encoded keys to sign data and validate signatures" do
|
30
30
|
data = "some data to sign"
|
31
|
-
group = OpenSSL::PKey::EC::Group.new(enc_class::CURVE_NAME)
|
31
|
+
group = OpenSSL::PKey::EC::Group.new(enc_class::CURVE_NAME)
|
32
32
|
private_key = OpenSSL::PKey::EC.new(group).generate_key
|
33
33
|
signer = enc_class.new(private_key.to_der)
|
34
34
|
signature = signer.sign(data)
|
35
35
|
public_key = OpenSSL::PKey::EC.new(group)
|
36
36
|
public_key.public_key = private_key.public_key
|
37
37
|
validator = enc_class.new(public_key.to_der)
|
38
|
-
validator.valid?(signature, data).
|
38
|
+
expect(validator.valid?(signature, data)).to eq(true)
|
39
39
|
end
|
40
40
|
|
41
41
|
it "can use PEM-encoded keys to sign data and validate signatures" do
|
42
42
|
data = "some data to sign"
|
43
|
-
group = OpenSSL::PKey::EC::Group.new(enc_class::CURVE_NAME)
|
43
|
+
group = OpenSSL::PKey::EC::Group.new(enc_class::CURVE_NAME)
|
44
44
|
private_key = OpenSSL::PKey::EC.new(group).generate_key
|
45
45
|
signer = enc_class.new(private_key.to_pem)
|
46
46
|
signature = signer.sign(data)
|
47
47
|
public_key = OpenSSL::PKey::EC.new(group)
|
48
48
|
public_key.public_key = private_key.public_key
|
49
49
|
validator = enc_class.new(public_key.to_pem)
|
50
|
-
validator.valid?(signature, data).
|
50
|
+
expect(validator.valid?(signature, data)).to eq(true)
|
51
51
|
end
|
52
52
|
|
53
53
|
context "#initialize" do
|
54
54
|
|
55
55
|
it "raises an argument error if the key has the wrong curve" do
|
56
|
-
group = OpenSSL::PKey::EC::Group.new("secp224k1")
|
56
|
+
group = OpenSSL::PKey::EC::Group.new("secp224k1")
|
57
57
|
private_key = OpenSSL::PKey::EC.new(group).generate_key
|
58
58
|
expect { enc_class.new(private_key) }.to raise_error ArgumentError
|
59
59
|
end
|
@@ -62,39 +62,39 @@ shared_examples "signing and validation" do |enc_class|
|
|
62
62
|
|
63
63
|
context "#valid?" do
|
64
64
|
|
65
|
-
it "fails to validate the signature when the key is changed" do
|
65
|
+
it "fails to validate the signature when the key is changed" do
|
66
66
|
data = "some data to sign"
|
67
|
-
group = OpenSSL::PKey::EC::Group.new(enc_class::CURVE_NAME)
|
67
|
+
group = OpenSSL::PKey::EC::Group.new(enc_class::CURVE_NAME)
|
68
68
|
private_key = OpenSSL::PKey::EC.new(group).generate_key
|
69
69
|
signer = enc_class.new(private_key)
|
70
70
|
signature = signer.sign(data)
|
71
71
|
public_key = OpenSSL::PKey::EC.new(group).generate_key
|
72
72
|
validator = enc_class.new(public_key)
|
73
|
-
validator.valid?(signature, data).
|
73
|
+
expect(validator.valid?(signature, data)).to eq(false)
|
74
74
|
end
|
75
75
|
|
76
|
-
it "fails to validate the signature when the signature is changed" do
|
76
|
+
it "fails to validate the signature when the signature is changed" do
|
77
77
|
data = "some data to sign"
|
78
|
-
group = OpenSSL::PKey::EC::Group.new(enc_class::CURVE_NAME)
|
78
|
+
group = OpenSSL::PKey::EC::Group.new(enc_class::CURVE_NAME)
|
79
79
|
private_key = OpenSSL::PKey::EC.new(group).generate_key
|
80
80
|
signer = enc_class.new(private_key)
|
81
81
|
signature = signer.sign(data)
|
82
82
|
public_key = OpenSSL::PKey::EC.new(group)
|
83
83
|
public_key.public_key = private_key.public_key
|
84
84
|
validator = enc_class.new(public_key)
|
85
|
-
validator.valid?(signature + "x", data).
|
85
|
+
expect(validator.valid?(signature + "x", data)).to eq(false)
|
86
86
|
end
|
87
87
|
|
88
|
-
it "fails to validate the signature when the data is changed" do
|
88
|
+
it "fails to validate the signature when the data is changed" do
|
89
89
|
data = "some data to sign"
|
90
|
-
group = OpenSSL::PKey::EC::Group.new(enc_class::CURVE_NAME)
|
90
|
+
group = OpenSSL::PKey::EC::Group.new(enc_class::CURVE_NAME)
|
91
91
|
private_key = OpenSSL::PKey::EC.new(group).generate_key
|
92
92
|
signer = enc_class.new(private_key)
|
93
93
|
signature = signer.sign(data)
|
94
94
|
public_key = OpenSSL::PKey::EC.new(group)
|
95
95
|
public_key.public_key = private_key.public_key
|
96
96
|
validator = enc_class.new(public_key)
|
97
|
-
validator.valid?(signature, data + "x").
|
97
|
+
expect(validator.valid?(signature, data + "x")).to eq(false)
|
98
98
|
end
|
99
99
|
|
100
100
|
end
|
@@ -110,7 +110,7 @@ describe Sandal::Sig::ES do
|
|
110
110
|
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])
|
111
111
|
signature = Sandal::Sig::ES.encode_jws_signature(r, s, 256)
|
112
112
|
base64_signature = Sandal::Util.jwt_base64_encode(signature)
|
113
|
-
base64_signature.
|
113
|
+
expect(base64_signature).to eq("DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q")
|
114
114
|
end
|
115
115
|
|
116
116
|
it "can encode the signature in JWS draft-11 appendix 4" do
|
@@ -118,7 +118,7 @@ describe Sandal::Sig::ES do
|
|
118
118
|
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])
|
119
119
|
signature = Sandal::Sig::ES.encode_jws_signature(r, s, 521)
|
120
120
|
base64_signature = Sandal::Util.jwt_base64_encode(signature)
|
121
|
-
base64_signature.
|
121
|
+
expect(base64_signature).to eq("AdwMgeerwtHoh-l192l60hp9wAHZFVJbLfD_UxMi70cwnZOYaRI1bKPWROc-mZZqwqT2SI-KGDKB34XO0aw_7XdtAG8GaSwFKdCAPZgoXD2YBJZCPEX3xKpRwcdOO8KpEHwJjyqOgzDO7iKvU8vcnwNrmxYbSW9ERBXukOXolLzeO_Jn")
|
122
122
|
end
|
123
123
|
|
124
124
|
end
|
@@ -131,7 +131,7 @@ describe Sandal::Sig::ES256 do
|
|
131
131
|
context "#name" do
|
132
132
|
it "is 'ES256'" do
|
133
133
|
enc = Sandal::Sig::ES256.new(OpenSSL::PKey::EC.new("prime256v1").generate_key)
|
134
|
-
enc.name.
|
134
|
+
expect(enc.name).to eq("ES256")
|
135
135
|
end
|
136
136
|
end
|
137
137
|
|
@@ -144,11 +144,11 @@ describe Sandal::Sig::ES256 do
|
|
144
144
|
data = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
|
145
145
|
signature = Sandal::Util.jwt_base64_decode("DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q")
|
146
146
|
|
147
|
-
group = OpenSSL::PKey::EC::Group.new("prime256v1")
|
147
|
+
group = OpenSSL::PKey::EC::Group.new("prime256v1")
|
148
148
|
public_key = OpenSSL::PKey::EC.new(group)
|
149
149
|
public_key.public_key = make_point(group, x, y)
|
150
150
|
validator = Sandal::Sig::ES256.new(public_key)
|
151
|
-
validator.valid?(signature, data).
|
151
|
+
expect(validator.valid?(signature, data)).to eq(true)
|
152
152
|
end
|
153
153
|
|
154
154
|
end
|
@@ -161,7 +161,7 @@ describe Sandal::Sig::ES384 do
|
|
161
161
|
context "#name" do
|
162
162
|
it "is 'ES384'" do
|
163
163
|
enc = Sandal::Sig::ES384.new(OpenSSL::PKey::EC.new("secp384r1").generate_key)
|
164
|
-
enc.name.
|
164
|
+
expect(enc.name).to eq("ES384")
|
165
165
|
end
|
166
166
|
end
|
167
167
|
|
@@ -173,7 +173,7 @@ describe Sandal::Sig::ES512 do
|
|
173
173
|
context "#name" do
|
174
174
|
it "is 'ES512'" do
|
175
175
|
enc = Sandal::Sig::ES512.new(OpenSSL::PKey::EC.new("secp521r1").generate_key)
|
176
|
-
enc.name.
|
176
|
+
expect(enc.name).to eq("ES512")
|
177
177
|
end
|
178
178
|
end
|
179
179
|
|
@@ -186,11 +186,11 @@ describe Sandal::Sig::ES512 do
|
|
186
186
|
data = "eyJhbGciOiJFUzUxMiJ9.UGF5bG9hZA"
|
187
187
|
signature = Sandal::Util.jwt_base64_decode("AdwMgeerwtHoh-l192l60hp9wAHZFVJbLfD_UxMi70cwnZOYaRI1bKPWROc-mZZqwqT2SI-KGDKB34XO0aw_7XdtAG8GaSwFKdCAPZgoXD2YBJZCPEX3xKpRwcdOO8KpEHwJjyqOgzDO7iKvU8vcnwNrmxYbSW9ERBXukOXolLzeO_Jn")
|
188
188
|
|
189
|
-
group = OpenSSL::PKey::EC::Group.new("secp521r1")
|
189
|
+
group = OpenSSL::PKey::EC::Group.new("secp521r1")
|
190
190
|
public_key = OpenSSL::PKey::EC.new(group)
|
191
191
|
public_key.public_key = make_point(group, x, y)
|
192
192
|
validator = Sandal::Sig::ES512.new(public_key)
|
193
|
-
validator.valid?(signature, data).
|
193
|
+
expect(validator.valid?(signature, data)).to eq(true)
|
194
194
|
end
|
195
195
|
|
196
196
|
end
|
data/spec/sandal/sig/hs_spec.rb
CHANGED
@@ -8,7 +8,7 @@ shared_examples "signing and validation" do |enc_class|
|
|
8
8
|
key = "A secret key"
|
9
9
|
signer = enc_class.new(key)
|
10
10
|
signature = signer.sign(data)
|
11
|
-
signer.valid?(signature, data).
|
11
|
+
expect(signer.valid?(signature, data)).to eq(true)
|
12
12
|
end
|
13
13
|
|
14
14
|
context "#valid?" do
|
@@ -19,7 +19,7 @@ shared_examples "signing and validation" do |enc_class|
|
|
19
19
|
signer = enc_class.new(key)
|
20
20
|
signature = signer.sign(data)
|
21
21
|
verifier = enc_class.new(key + "x")
|
22
|
-
verifier.valid?(signature, data).
|
22
|
+
expect(verifier.valid?(signature, data)).to eq(false)
|
23
23
|
end
|
24
24
|
|
25
25
|
it "fails to validate the signature when the signature is changed" do
|
@@ -27,7 +27,7 @@ shared_examples "signing and validation" do |enc_class|
|
|
27
27
|
key = "Another secret key"
|
28
28
|
signer = enc_class.new(key)
|
29
29
|
signature = signer.sign(data)
|
30
|
-
signer.valid?(signature + "x", data).
|
30
|
+
expect(signer.valid?(signature + "x", data)).to eq(false)
|
31
31
|
end
|
32
32
|
|
33
33
|
it "fails to validate the signature when the data is changed" do
|
@@ -35,7 +35,7 @@ shared_examples "signing and validation" do |enc_class|
|
|
35
35
|
key = "Another secret key"
|
36
36
|
signer = enc_class.new(key)
|
37
37
|
signature = signer.sign(data)
|
38
|
-
signer.valid?(signature, data + "x").
|
38
|
+
expect(signer.valid?(signature, data + "x")).to eq(false)
|
39
39
|
end
|
40
40
|
|
41
41
|
end
|
@@ -48,7 +48,7 @@ describe Sandal::Sig::HS256 do
|
|
48
48
|
context "#name" do
|
49
49
|
it "is 'HS256'" do
|
50
50
|
enc = Sandal::Sig::HS256.new("any old key")
|
51
|
-
enc.name.
|
51
|
+
expect(enc.name).to eq("HS256")
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
@@ -57,7 +57,7 @@ describe Sandal::Sig::HS256 do
|
|
57
57
|
key = [3, 35, 53, 75, 43, 15, 165, 188, 131, 126, 6, 101, 119, 123, 166, 143, 90, 179, 40, 230, 240, 84, 201, 40, 169, 15, 132, 178, 210, 80, 46, 191, 211, 251, 90, 146, 210, 6, 71, 239, 150, 138, 180, 195, 119, 98, 61, 34, 61, 46, 33, 114, 5, 46, 79, 8, 192, 205, 154, 245, 103, 208, 128, 163].pack("C*")
|
58
58
|
signer = Sandal::Sig::HS256.new(key)
|
59
59
|
signature = [116, 24, 223, 180, 151, 153, 224, 37, 79, 250, 96, 125, 216, 173, 187, 186, 22, 212, 37, 77, 105, 214, 191, 240, 91, 88, 5, 88, 83, 132, 141, 121].pack("C*")
|
60
|
-
signer.valid?(signature, data).
|
60
|
+
expect(signer.valid?(signature, data)).to eq(true)
|
61
61
|
end
|
62
62
|
|
63
63
|
end
|
@@ -68,7 +68,7 @@ describe Sandal::Sig::HS384 do
|
|
68
68
|
context "#name" do
|
69
69
|
it "is 'HS384'" do
|
70
70
|
enc = Sandal::Sig::HS384.new("any old key")
|
71
|
-
enc.name.
|
71
|
+
expect(enc.name).to eq("HS384")
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
@@ -80,7 +80,7 @@ describe Sandal::Sig::HS512 do
|
|
80
80
|
context "#name" do
|
81
81
|
it "is 'HS512'" do
|
82
82
|
enc = Sandal::Sig::HS512.new("any old key")
|
83
|
-
enc.name.
|
83
|
+
expect(enc.name).to eq("HS512")
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
data/spec/sandal/sig/rs_spec.rb
CHANGED
@@ -9,7 +9,7 @@ shared_examples "signing and validation" do |enc_class|
|
|
9
9
|
signer = enc_class.new(private_key)
|
10
10
|
signature = signer.sign(data)
|
11
11
|
validator = enc_class.new(private_key.public_key)
|
12
|
-
validator.valid?(signature, data).
|
12
|
+
expect(validator.valid?(signature, data)).to eq(true)
|
13
13
|
end
|
14
14
|
|
15
15
|
it "can use DER-encoded keys to sign data and validate signatures" do
|
@@ -18,7 +18,7 @@ shared_examples "signing and validation" do |enc_class|
|
|
18
18
|
signer = enc_class.new(private_key.to_der)
|
19
19
|
signature = signer.sign(data)
|
20
20
|
validator = enc_class.new(private_key.public_key.to_der)
|
21
|
-
validator.valid?(signature, data).
|
21
|
+
expect(validator.valid?(signature, data)).to eq(true)
|
22
22
|
end
|
23
23
|
|
24
24
|
it "can use PEM-encoded keys to sign data and validate signatures" do
|
@@ -27,7 +27,7 @@ shared_examples "signing and validation" do |enc_class|
|
|
27
27
|
signer = enc_class.new(private_key.to_pem)
|
28
28
|
signature = signer.sign(data)
|
29
29
|
validator = enc_class.new(private_key.public_key.to_pem)
|
30
|
-
validator.valid?(signature, data).
|
30
|
+
expect(validator.valid?(signature, data)).to eq(true)
|
31
31
|
end
|
32
32
|
|
33
33
|
context "#valid?" do
|
@@ -37,7 +37,7 @@ shared_examples "signing and validation" do |enc_class|
|
|
37
37
|
signer = enc_class.new(OpenSSL::PKey::RSA.generate(2048))
|
38
38
|
signature = signer.sign(data)
|
39
39
|
validator = enc_class.new(OpenSSL::PKey::RSA.generate(2048).public_key)
|
40
|
-
validator.valid?(signature, data).
|
40
|
+
expect(validator.valid?(signature, data)).to eq(false)
|
41
41
|
end
|
42
42
|
|
43
43
|
it "fails to validate the signature when the signature is changed" do
|
@@ -46,7 +46,7 @@ shared_examples "signing and validation" do |enc_class|
|
|
46
46
|
signer = enc_class.new(private_key)
|
47
47
|
signature = signer.sign(data)
|
48
48
|
validator = enc_class.new(private_key.public_key)
|
49
|
-
validator.valid?(signature + "x", data).
|
49
|
+
expect(validator.valid?(signature + "x", data)).to eq(false)
|
50
50
|
end
|
51
51
|
|
52
52
|
it "fails to validate the signature when the data is changed" do
|
@@ -55,7 +55,7 @@ shared_examples "signing and validation" do |enc_class|
|
|
55
55
|
signer = enc_class.new(private_key)
|
56
56
|
signature = signer.sign(data)
|
57
57
|
validator = enc_class.new(private_key.public_key)
|
58
|
-
validator.valid?(signature, data + "x").
|
58
|
+
expect(validator.valid?(signature, data + "x")).to eq(false)
|
59
59
|
end
|
60
60
|
|
61
61
|
end
|
@@ -72,13 +72,13 @@ describe Sandal::Sig::RS256 do
|
|
72
72
|
signer = Sandal::Sig::RS384.new(private_key)
|
73
73
|
signature = signer.sign(data)
|
74
74
|
validator = Sandal::Sig::RS384.new(private_key.public_key)
|
75
|
-
validator.valid?(signature, data).
|
75
|
+
expect(validator.valid?(signature, data)).to eq(true)
|
76
76
|
end
|
77
77
|
|
78
78
|
context "#name" do
|
79
79
|
it "is 'RS256'" do
|
80
80
|
enc = Sandal::Sig::RS256.new(OpenSSL::PKey::RSA.generate(2048))
|
81
|
-
enc.name.
|
81
|
+
expect(enc.name).to eq("RS256")
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
@@ -90,7 +90,7 @@ describe Sandal::Sig::RS384 do
|
|
90
90
|
context "#name" do
|
91
91
|
it "is 'RS384'" do
|
92
92
|
enc = Sandal::Sig::RS384.new(OpenSSL::PKey::RSA.generate(2048))
|
93
|
-
enc.name.
|
93
|
+
expect(enc.name).to eq("RS384")
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
@@ -102,7 +102,7 @@ describe Sandal::Sig::RS512 do
|
|
102
102
|
context "#name" do
|
103
103
|
it "is 'RS512'" do
|
104
104
|
enc = Sandal::Sig::RS512.new(OpenSSL::PKey::RSA.generate(2048))
|
105
|
-
enc.name.
|
105
|
+
expect(enc.name).to eq("RS512")
|
106
106
|
end
|
107
107
|
end
|
108
108
|
|
data/spec/sandal/util_spec.rb
CHANGED
@@ -9,7 +9,7 @@ describe Sandal::Util do
|
|
9
9
|
it 'decodes base64 as per JWT example 6.1' do
|
10
10
|
encoded = 'eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ'
|
11
11
|
val = Sandal::Util.jwt_base64_decode(encoded)
|
12
|
-
val.
|
12
|
+
expect(val).to eq(%!{"iss":"joe",\r\n "exp":1300819380,\r\n "http://example.com/is_root":true}!)
|
13
13
|
end
|
14
14
|
|
15
15
|
it 'raises an ArgumentError if base64 strings contain padding' do
|
@@ -27,7 +27,7 @@ describe Sandal::Util do
|
|
27
27
|
it 'encodes base64 as per JWT example 6.1' do
|
28
28
|
src = %!{"iss":"joe",\r\n "exp":1300819380,\r\n "http://example.com/is_root":true}!
|
29
29
|
encoded = Sandal::Util.jwt_base64_encode(src)
|
30
|
-
encoded.
|
30
|
+
expect(encoded).to eq('eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ')
|
31
31
|
end
|
32
32
|
|
33
33
|
end
|
@@ -35,31 +35,31 @@ describe Sandal::Util do
|
|
35
35
|
context '#jwt_strings_equal?' do
|
36
36
|
|
37
37
|
it 'compares nil strings as equal' do
|
38
|
-
Sandal::Util.jwt_strings_equal?(nil, nil).
|
38
|
+
expect(Sandal::Util.jwt_strings_equal?(nil, nil)).to eq(true)
|
39
39
|
end
|
40
40
|
|
41
41
|
it 'compares empty strings as equal' do
|
42
|
-
Sandal::Util.jwt_strings_equal?('', '').
|
42
|
+
expect(Sandal::Util.jwt_strings_equal?('', '')).to eq(true)
|
43
43
|
end
|
44
44
|
|
45
45
|
it 'compares nil strings as unequal to empty strings' do
|
46
|
-
Sandal::Util.jwt_strings_equal?(nil, '').
|
47
|
-
Sandal::Util.jwt_strings_equal?('', nil).
|
46
|
+
expect(Sandal::Util.jwt_strings_equal?(nil, '')).to eq(false)
|
47
|
+
expect(Sandal::Util.jwt_strings_equal?('', nil)).to eq(false)
|
48
48
|
end
|
49
49
|
|
50
50
|
it 'compares equal strings as equal' do
|
51
|
-
Sandal::Util.jwt_strings_equal?('hello', 'hello').
|
52
|
-
Sandal::Util.jwt_strings_equal?('a longer string', 'a longer string').
|
51
|
+
expect(Sandal::Util.jwt_strings_equal?('hello', 'hello')).to eq(true)
|
52
|
+
expect(Sandal::Util.jwt_strings_equal?('a longer string', 'a longer string')).to eq(true)
|
53
53
|
end
|
54
54
|
|
55
55
|
it 'compares unequal strings as unequal' do
|
56
|
-
Sandal::Util.jwt_strings_equal?('hello', 'world').
|
57
|
-
Sandal::Util.jwt_strings_equal?('a longer string', 'a different longer string').
|
56
|
+
expect(Sandal::Util.jwt_strings_equal?('hello', 'world')).to eq(false)
|
57
|
+
expect(Sandal::Util.jwt_strings_equal?('a longer string', 'a different longer string')).to eq(false)
|
58
58
|
end
|
59
59
|
|
60
60
|
it 'compares strings without short-circuiting', :timing_dependent do
|
61
61
|
measure_equals = -> a, b do
|
62
|
-
Benchmark.realtime { 100.times { Sandal::Util.jwt_strings_equal?(a, b) } }
|
62
|
+
Benchmark.realtime { 100.times { Sandal::Util.jwt_strings_equal?(a, b) } }
|
63
63
|
end
|
64
64
|
ref = 'a' * 10000
|
65
65
|
cmp1 = ('a' * 9999) + 'b'
|
@@ -67,7 +67,7 @@ describe Sandal::Util do
|
|
67
67
|
t1 = measure_equals.(ref, cmp1)
|
68
68
|
t2 = measure_equals.(ref, cmp2)
|
69
69
|
range = (t1 - t1/20.0)..(t1 + t1/20.0)
|
70
|
-
range.
|
70
|
+
expect(range).to be === t2
|
71
71
|
end
|
72
72
|
|
73
73
|
end
|
data/spec/sandal_spec.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'helper'
|
2
2
|
require 'openssl'
|
3
|
-
require 'multi_json'
|
4
3
|
|
5
4
|
describe Sandal do
|
6
5
|
|
@@ -11,23 +10,23 @@ describe Sandal do
|
|
11
10
|
private_key = OpenSSL::PKey::RSA.new(2048)
|
12
11
|
encrypter = Sandal::Enc::A128CBC_HS256.new(Sandal::Enc::Alg::RSA1_5.new(private_key.public_key))
|
13
12
|
token = Sandal.encrypt_token(payload, encrypter, { 'zip' => 'DEF' })
|
14
|
-
decoded_payload = Sandal.decode_token(token) do |header|
|
13
|
+
decoded_payload = Sandal.decode_token(token) do |header|
|
15
14
|
Sandal::Enc::A128CBC_HS256.new(Sandal::Enc::Alg::RSA1_5.new(private_key))
|
16
15
|
end
|
17
|
-
decoded_payload.
|
16
|
+
expect(decoded_payload).to eq(payload)
|
18
17
|
end
|
19
18
|
|
20
19
|
it 'raises an ArgumentError if the zip parameter is present and nil' do
|
21
20
|
encrypter = Sandal::Enc::A128CBC_HS256.new(Sandal::Enc::Alg::RSA1_5.new(OpenSSL::PKey::RSA.new(2048)))
|
22
|
-
expect {
|
23
|
-
Sandal.encrypt_token('any payload', encrypter, { 'zip' => nil })
|
21
|
+
expect {
|
22
|
+
Sandal.encrypt_token('any payload', encrypter, { 'zip' => nil })
|
24
23
|
}.to raise_error ArgumentError, 'Invalid zip algorithm.'
|
25
24
|
end
|
26
25
|
|
27
26
|
it 'raises an ArgumentError if the zip parameter is present and not "DEF"' do
|
28
27
|
encrypter = Sandal::Enc::A128CBC_HS256.new(Sandal::Enc::Alg::RSA1_5.new(OpenSSL::PKey::RSA.new(2048)))
|
29
|
-
expect {
|
30
|
-
Sandal.encrypt_token('any payload', encrypter, { 'zip' => 'INVALID' })
|
28
|
+
expect {
|
29
|
+
Sandal.encrypt_token('any payload', encrypter, { 'zip' => 'INVALID' })
|
31
30
|
}.to raise_error ArgumentError, 'Invalid zip algorithm.'
|
32
31
|
end
|
33
32
|
|
@@ -35,7 +34,7 @@ describe Sandal do
|
|
35
34
|
|
36
35
|
it 'raises a token error when the token format is invalid' do
|
37
36
|
expect { Sandal.decode_token('not a valid token') }.to raise_error Sandal::TokenError
|
38
|
-
end
|
37
|
+
end
|
39
38
|
|
40
39
|
it 'raises a token error when the token encoding is invalid' do
|
41
40
|
expect { Sandal.decode_token('an.invalid.token') }.to raise_error Sandal::TokenError
|
@@ -45,24 +44,24 @@ describe Sandal do
|
|
45
44
|
payload = 'Hello, World'
|
46
45
|
token = Sandal.encode_token(payload, nil)
|
47
46
|
decoded_payload = Sandal.decode_token(token)
|
48
|
-
decoded_payload.
|
47
|
+
expect(decoded_payload).to eq(payload)
|
49
48
|
end
|
50
49
|
|
51
50
|
it 'encodes and decodes tokens with "none" signature' do
|
52
51
|
payload = 'Hello, World'
|
53
52
|
token = Sandal.encode_token(payload, Sandal::Sig::None.instance)
|
54
53
|
decoded_payload = Sandal.decode_token(token)
|
55
|
-
decoded_payload.
|
54
|
+
expect(decoded_payload).to eq(payload)
|
56
55
|
end
|
57
56
|
|
58
57
|
it 'decodes non-JSON payloads to a String' do
|
59
58
|
token = Sandal.encode_token('not valid json', nil)
|
60
|
-
Sandal.decode_token(token).
|
59
|
+
expect(Sandal.decode_token(token)).to be_kind_of String
|
61
60
|
end
|
62
61
|
|
63
62
|
it 'decodes JSON payloads to a Hash' do
|
64
63
|
token = Sandal.encode_token({ 'valid' => 'json' }, nil)
|
65
|
-
Sandal.decode_token(token).
|
64
|
+
expect(Sandal.decode_token(token)).to be_kind_of Hash
|
66
65
|
end
|
67
66
|
|
68
67
|
it 'raises a claim error when the expiry date is far in the past' do
|
@@ -77,7 +76,7 @@ describe Sandal do
|
|
77
76
|
|
78
77
|
it 'does not raise an error when the expiry date is in the past but validation is disabled' do
|
79
78
|
token = Sandal.encode_token({ 'exp' => (Time.now - 600).to_i }, nil)
|
80
|
-
Sandal.decode_token(token) do |header, options|
|
79
|
+
Sandal.decode_token(token) do |header, options|
|
81
80
|
options[:ignore_exp] = true
|
82
81
|
nil
|
83
82
|
end
|
@@ -108,7 +107,7 @@ describe Sandal do
|
|
108
107
|
|
109
108
|
it 'does not raise an error when the not-before date is in the future but validation is disabled' do
|
110
109
|
token = Sandal.encode_token({ 'nbf' => (Time.now + 600).to_i }, nil)
|
111
|
-
Sandal.decode_token(token) do |header, options|
|
110
|
+
Sandal.decode_token(token) do |header, options|
|
112
111
|
options[:ignore_nbf] = true
|
113
112
|
nil
|
114
113
|
end
|
@@ -129,15 +128,15 @@ describe Sandal do
|
|
129
128
|
|
130
129
|
it 'raises a claim error when the issuer is not valid' do
|
131
130
|
token = Sandal.encode_token({ 'iss' => 'example.org' }, nil)
|
132
|
-
expect { Sandal.decode_token(token) do |header, options|
|
133
|
-
options[:valid_iss] = ['example.net']
|
131
|
+
expect { Sandal.decode_token(token) do |header, options|
|
132
|
+
options[:valid_iss] = ['example.net']
|
134
133
|
nil
|
135
134
|
end }.to raise_error Sandal::ClaimError
|
136
135
|
end
|
137
136
|
|
138
137
|
it 'does not raise an error when the issuer is valid' do
|
139
138
|
token = Sandal.encode_token({ 'iss' => 'example.org' }, nil)
|
140
|
-
Sandal.decode_token(token) do |header, options|
|
139
|
+
Sandal.decode_token(token) do |header, options|
|
141
140
|
options[:valid_iss] = ['example.org', 'example.com']
|
142
141
|
nil
|
143
142
|
end
|
@@ -145,32 +144,32 @@ describe Sandal do
|
|
145
144
|
|
146
145
|
it 'raises a claim error when the audience string is not valid' do
|
147
146
|
token = Sandal.encode_token({ 'aud' => 'example.com' }, nil)
|
148
|
-
expect { Sandal.decode_token(token) do |header, options|
|
149
|
-
options[:valid_aud] = ['example.net']
|
147
|
+
expect { Sandal.decode_token(token) do |header, options|
|
148
|
+
options[:valid_aud] = ['example.net']
|
150
149
|
nil
|
151
150
|
end }.to raise_error Sandal::ClaimError
|
152
151
|
end
|
153
152
|
|
154
153
|
it 'raises a claim error when the audience array is not valid' do
|
155
154
|
token = Sandal.encode_token({ 'aud' => ['example.org', 'example.com'] }, nil)
|
156
|
-
expect { Sandal.decode_token(token) do |header, options|
|
157
|
-
options[:valid_aud] = ['example.net']
|
155
|
+
expect { Sandal.decode_token(token) do |header, options|
|
156
|
+
options[:valid_aud] = ['example.net']
|
158
157
|
nil
|
159
158
|
end }.to raise_error Sandal::ClaimError
|
160
159
|
end
|
161
160
|
|
162
161
|
it 'does not raise an error when the audience string is valid' do
|
163
162
|
token = Sandal.encode_token({ 'aud' => 'example.net' }, nil)
|
164
|
-
Sandal.decode_token(token) do |header, options|
|
165
|
-
options[:valid_aud] = ['example.net']
|
163
|
+
Sandal.decode_token(token) do |header, options|
|
164
|
+
options[:valid_aud] = ['example.net']
|
166
165
|
nil
|
167
166
|
end
|
168
167
|
end
|
169
168
|
|
170
169
|
it 'does not raise an error when the audience array is valid' do
|
171
170
|
token = Sandal.encode_token({ 'aud' => ['example.com', 'example.net'] }, nil)
|
172
|
-
Sandal.decode_token(token) do |header, options|
|
173
|
-
options[:valid_aud] = ['example.net']
|
171
|
+
Sandal.decode_token(token) do |header, options|
|
172
|
+
options[:valid_aud] = ['example.net']
|
174
173
|
nil
|
175
174
|
end
|
176
175
|
end
|
@@ -180,7 +179,7 @@ describe Sandal do
|
|
180
179
|
private_key = OpenSSL::PKey::RSA.generate(2048)
|
181
180
|
token = Sandal.encode_token(payload, Sandal::Sig::RS256.new(private_key))
|
182
181
|
decoded_payload = Sandal.decode_token(token) { |header| Sandal::Sig::RS256.new(private_key.public_key) }
|
183
|
-
decoded_payload.
|
182
|
+
expect(decoded_payload).to eq(payload)
|
184
183
|
end
|
185
184
|
|
186
185
|
it 'encodes and decodes tokens with RS384 signatures' do
|
@@ -188,7 +187,7 @@ describe Sandal do
|
|
188
187
|
private_key = OpenSSL::PKey::RSA.generate(2048)
|
189
188
|
token = Sandal.encode_token(payload, Sandal::Sig::RS384.new(private_key))
|
190
189
|
decoded_payload = Sandal.decode_token(token) { |header| Sandal::Sig::RS384.new(private_key.public_key) }
|
191
|
-
decoded_payload.
|
190
|
+
expect(decoded_payload).to eq(payload)
|
192
191
|
end
|
193
192
|
|
194
193
|
it 'encodes and decodes tokens with RS512 signatures' do
|
@@ -196,7 +195,7 @@ describe Sandal do
|
|
196
195
|
private_key = OpenSSL::PKey::RSA.generate(2048)
|
197
196
|
token = Sandal.encode_token(payload, Sandal::Sig::RS512.new(private_key))
|
198
197
|
decoded_payload = Sandal.decode_token(token) { |header| Sandal::Sig::RS512.new(private_key.public_key) }
|
199
|
-
decoded_payload.
|
198
|
+
expect(decoded_payload).to eq(payload)
|
200
199
|
end
|
201
200
|
|
202
201
|
end
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sandal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Greg Beech
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-02-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: multi_json
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ~>
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '1.7'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ~>
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '1.7'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: bundler
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -150,6 +136,7 @@ files:
|
|
150
136
|
- lib/sandal/enc/alg.rb
|
151
137
|
- lib/sandal/enc/alg/direct.rb
|
152
138
|
- lib/sandal/enc/alg/rsa.rb
|
139
|
+
- lib/sandal/json.rb
|
153
140
|
- lib/sandal/sig.rb
|
154
141
|
- lib/sandal/sig/es.rb
|
155
142
|
- lib/sandal/sig/hs.rb
|
@@ -193,7 +180,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
193
180
|
requirements:
|
194
181
|
- openssl 1.0.1c for EC signature methods
|
195
182
|
rubyforge_project:
|
196
|
-
rubygems_version: 2.0
|
183
|
+
rubygems_version: 2.2.0
|
197
184
|
signing_key:
|
198
185
|
specification_version: 4
|
199
186
|
summary: A JSON Web Token (JWT) library.
|