sandal 0.5.1 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![Build Status](https://travis-ci.org/gregbeech/sandal.png?branch=master)](https://travis-ci.org/gregbeech/sandal) [![Coverage Status](https://coveralls.io/repos/gregbeech/sandal/badge.png?branch=master)](https://coveralls.io/r/gregbeech/sandal) [![Code Climate](https://codeclimate.com/github/gregbeech/sandal.png)](https://codeclimate.com/github/gregbeech/sandal) [![Dependency Status](https://gemnasium.com/gregbeech/sandal.png)](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.
|