Skip to content

Commit 826dfe8

Browse files
committed
Improve specs and blind spots
1 parent 9e6c4d8 commit 826dfe8

18 files changed

+177
-40
lines changed

Diff for: lib/jwt/decode.rb

+2-5
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,8 @@ def allowed_and_valid_algorithms
7777
:algorithms].freeze
7878

7979
def given_algorithms
80-
ALGORITHM_KEYS.each do |alg_key|
81-
alg = @options[alg_key]
82-
return Array(alg) if alg
83-
end
84-
[]
80+
alg_key = ALGORITHM_KEYS.find { |key| @options[key] }
81+
Array(@options[alg_key])
8582
end
8683

8784
def allowed_algorithms

Diff for: lib/jwt/jwa/ecdsa.rb

-4
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,6 @@ def verify(data:, signature:, verification_key:)
5656
register_algorithm(new(v[:algorithm], v[:digest]))
5757
end
5858

59-
def self.from_algorithm(algorithm)
60-
new(algorithm, algorithm.downcase.gsub('es', 'sha'))
61-
end
62-
6359
def self.curve_by_name(name)
6460
NAMED_CURVES.fetch(name) do
6561
raise UnsupportedEcdsaCurve, "The ECDSA curve '#{name}' is not supported"

Diff for: lib/jwt/jwa/hmac.rb

-4
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ module JWA
66
class Hmac
77
include JWT::JWA::SigningAlgorithm
88

9-
def self.from_algorithm(algorithm)
10-
new(algorithm, OpenSSL::Digest.new(algorithm.downcase.gsub('hs', 'sha')))
11-
end
12-
139
def initialize(alg, digest)
1410
@alg = alg
1511
@digest = digest

Diff for: lib/jwt/jwk/ec.rb

-4
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,6 @@ def encode_octets(octets)
124124
::JWT::Base64.url_encode(octets)
125125
end
126126

127-
def encode_open_ssl_bn(key_part)
128-
::JWT::Base64.url_encode(key_part.to_s(BINARY))
129-
end
130-
131127
def parse_ec_key(key)
132128
crv, x_octets, y_octets = keypair_components(key)
133129
octets = key.private_key&.to_bn&.to_s(BINARY)

Diff for: lib/jwt/jwk/rsa.rb

+3
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ def create_rsa_key_using_sets(rsa_parameters)
165165
end
166166
end
167167

168+
# :nocov:
169+
# Before openssl 2.0, we need to use the accessors to set the key
168170
def create_rsa_key_using_accessors(rsa_parameters) # rubocop:disable Metrics/AbcSize
169171
validate_rsa_parameters!(rsa_parameters)
170172

@@ -179,6 +181,7 @@ def create_rsa_key_using_accessors(rsa_parameters) # rubocop:disable Metrics/Abc
179181
rsa_key.iqmp = rsa_parameters[:qi] if rsa_parameters[:qi]
180182
end
181183
end
184+
# :nocov:
182185

183186
def validate_rsa_parameters!(rsa_parameters)
184187
return unless rsa_parameters.key?(:d)

Diff for: spec/jwt/claims/verifier_spec.rb

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe JWT::Claims::Verifier do
4+
describe '.verify!' do
5+
context 'when all claims are given' do
6+
let(:options) do
7+
[
8+
:exp,
9+
:nbf,
10+
{ iss: 'issuer' },
11+
:iat,
12+
:jti,
13+
{ aud: 'aud' },
14+
:sub,
15+
:crit,
16+
{ required: [] },
17+
:numeric
18+
]
19+
end
20+
21+
it 'verifies all claims' do
22+
token = SpecSupport::Token.new(payload: { 'iss' => 'issuer', 'jti' => 1, 'aud' => 'aud' }, header: { 'crit' => [] })
23+
expect(described_class.verify!(token, *options)).to eq(nil)
24+
end
25+
end
26+
end
27+
end

Diff for: spec/jwt/jwa/none_spec.rb

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe JWT::JWA::None do
4+
subject { described_class.new }
5+
6+
describe '#sign' do
7+
it 'returns an empty string' do
8+
expect(subject.sign('data', 'key')).to eq('')
9+
end
10+
end
11+
12+
describe '#verify' do
13+
it 'returns true' do
14+
expect(subject.verify('data', 'signature', 'key')).to be true
15+
end
16+
end
17+
end

Diff for: spec/jwt/jwa/ps_spec.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# frozen_string_literal: true
22

33
RSpec.describe JWT::JWA::Ps do
4-
let(:rsa_key) { OpenSSL::PKey::RSA.generate(2048) }
4+
let(:rsa_key) { test_pkey('rsa-2048-private.pem') }
55
let(:data) { 'test data' }
66
let(:ps256_instance) { described_class.new('PS256') }
77
let(:ps384_instance) { described_class.new('PS384') }
@@ -44,7 +44,7 @@
4444
end
4545

4646
context 'with a key length less than 2048 bits' do
47-
let(:rsa_key) { OpenSSL::PKey::RSA.generate(1024) }
47+
let(:rsa_key) { OpenSSL::PKey::RSA.generate(2047) }
4848

4949
it 'raises an error' do
5050
expect do

Diff for: spec/jwt/jwa/rsa_spec.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# frozen_string_literal: true
22

33
RSpec.describe JWT::JWA::Rsa do
4-
let(:rsa_key) { OpenSSL::PKey::RSA.generate(2048) }
4+
let(:rsa_key) { test_pkey('rsa-2048-private.pem') }
55
let(:data) { 'test data' }
66
let(:rsa_instance) { described_class.new('RS256') }
77

@@ -21,7 +21,7 @@
2121
end
2222

2323
context 'with a key length less than 2048 bits' do
24-
let(:rsa_key) { OpenSSL::PKey::RSA.generate(1024) }
24+
let(:rsa_key) { OpenSSL::PKey::RSA.generate(2047) }
2525

2626
it 'raises an error' do
2727
expect do

Diff for: spec/jwt/jwa/unsupported_spec.rb

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe JWT::JWA::Unsupported do
4+
describe '.sign' do
5+
it 'raises an error for unsupported signing method' do
6+
expect { described_class.sign('data', 'key') }.to raise_error(JWT::EncodeError, 'Unsupported signing method')
7+
end
8+
end
9+
10+
describe '.verify' do
11+
it 'raises an error for unsupported algorithm' do
12+
expect { described_class.verify('data', 'signature', 'key') }.to raise_error(JWT::VerificationError, 'Algorithm not supported')
13+
end
14+
end
15+
end

Diff for: spec/jwt/jwk/decode_with_jwk_spec.rb

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
RSpec.describe JWT do
44
describe '.decode for JWK usecase' do
5-
let(:keypair) { OpenSSL::PKey::RSA.new(2048) }
5+
let(:keypair) { test_pkey('rsa-2048-private.pem') }
66
let(:jwk) { JWT::JWK.new(keypair) }
77
let(:public_jwks) { { keys: [jwk.export, { kid: 'not_the_correct_one', kty: 'oct', k: 'secret' }] } }
88
let(:token_payload) { { 'data' => 'something' } }
@@ -102,9 +102,9 @@
102102

103103
context 'mixing algorithms using kid header' do
104104
let(:hmac_jwk) { JWT::JWK.new('secret') }
105-
let(:rsa_jwk) { JWT::JWK.new(OpenSSL::PKey::RSA.new(2048)) }
106-
let(:ec_jwk_secp384r1) { JWT::JWK.new(OpenSSL::PKey::EC.generate('secp384r1')) }
107-
let(:ec_jwk_secp521r1) { JWT::JWK.new(OpenSSL::PKey::EC.generate('secp521r1')) }
105+
let(:rsa_jwk) { JWT::JWK.new(test_pkey('rsa-2048-private.pem')) }
106+
let(:ec_jwk_secp384r1) { JWT::JWK.new(test_pkey('ec384-private.pem')) }
107+
let(:ec_jwk_secp521r1) { JWT::JWK.new(test_pkey('ec384-private.pem')) }
108108
let(:jwks) { { keys: [hmac_jwk.export(include_private: true), rsa_jwk.export, ec_jwk_secp384r1.export, ec_jwk_secp521r1.export] } }
109109

110110
context 'when RSA key is pointed to as HMAC secret' do

Diff for: spec/jwt/jwk/ec_spec.rb

+24-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# frozen_string_literal: true
22

33
RSpec.describe JWT::JWK::EC do
4-
let(:ec_key) { OpenSSL::PKey::EC.generate('secp384r1') }
4+
let(:ec_key) { test_pkey('ec384-private.pem') }
55

66
describe '.new' do
77
subject { described_class.new(keypair) }
@@ -21,16 +21,38 @@
2121
expect(subject.private?).to eq false
2222
end
2323
end
24+
25+
context 'when a number is given' do
26+
let(:keypair) { 1234 }
27+
it 'raises an argument error' do
28+
expect { subject }.to raise_error(ArgumentError, 'key must be of type OpenSSL::PKey::EC or Hash with key parameters')
29+
end
30+
end
31+
32+
context 'when EC with unsupported curve is given' do
33+
let(:keypair) { OpenSSL::PKey::EC.generate('prime239v2') }
34+
it 'raises an error' do
35+
expect { subject }.to raise_error(JWT::JWKError, "Unsupported curve 'prime239v2'")
36+
end
37+
end
2438
end
2539

2640
describe '#keypair' do
2741
subject(:jwk) { described_class.new(ec_key) }
2842

29-
it 'warns to stderr' do
43+
it 'returns the key' do
3044
expect(jwk.keypair).to eq(ec_key)
3145
end
3246
end
3347

48+
describe '#public_key' do
49+
subject(:jwk) { described_class.new(ec_key) }
50+
51+
it 'returns the key' do
52+
expect(jwk.public_key).to eq(ec_key)
53+
end
54+
end
55+
3456
describe '#export' do
3557
let(:kid) { nil }
3658
subject { described_class.new(keypair, kid).export }

Diff for: spec/jwt/jwk/hmac_spec.rb

+7
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212
expect(jwk.private?).to eq true
1313
end
1414
end
15+
16+
context 'when key is a number' do
17+
let(:key) { 123 }
18+
it 'raises an ArgumentError' do
19+
expect { jwk }.to raise_error(ArgumentError, 'key must be of type String or Hash with key parameters')
20+
end
21+
end
1522
end
1623

1724
describe '#keypair' do

Diff for: spec/jwt/jwk/rsa_spec.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@
228228

229229
describe '.create_rsa_key_using_accessors' do
230230
before do
231-
skip 'OpenSSL if RSA#set_key is available there is no accessors anymore' if OpenSSL::PKey::RSA.new.respond_to?(:set_key)
231+
skip 'OpenSSL if RSA#d= is not available there is no accessors anymore' unless OpenSSL::PKey::RSA.new.respond_to?(:d=)
232232
end
233233

234234
subject(:rsa) { described_class.create_rsa_key_using_accessors(rsa_parameters) }

Diff for: spec/jwt/jwk/set_spec.rb

+10-10
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@
8383
describe '.select!' do
8484
it 'filters the keyset' do
8585
jwks = described_class.new([])
86-
jwks << JWT::JWK.new(OpenSSL::PKey::RSA.new(2048))
87-
jwks << JWT::JWK.new(OpenSSL::PKey::EC.generate('secp384r1'))
86+
jwks << JWT::JWK.new(test_pkey('rsa-2048-private.pem'))
87+
jwks << JWT::JWK.new(test_pkey('ec384-private.pem'))
8888
jwks.select! { |k| k[:kty] == 'RSA' }
8989
expect(jwks.size).to eql(1)
9090
expect(jwks.keys[0][:kty]).to eql('RSA')
@@ -94,8 +94,8 @@
9494
describe '.reject!' do
9595
it 'filters the keyset' do
9696
jwks = described_class.new([])
97-
jwks << JWT::JWK.new(OpenSSL::PKey::RSA.new(2048))
98-
jwks << JWT::JWK.new(OpenSSL::PKey::EC.generate('secp384r1'))
97+
jwks << JWT::JWK.new(test_pkey('rsa-2048-private.pem'))
98+
jwks << JWT::JWK.new(test_pkey('ec384-private.pem'))
9999
jwks.reject! { |k| k[:kty] == 'RSA' }
100100
expect(jwks.size).to eql(1)
101101
expect(jwks.keys[0][:kty]).to eql('EC')
@@ -105,8 +105,8 @@
105105
describe '.merge' do
106106
context 'merges two JWKSs' do
107107
it 'when called via .union' do
108-
jwks1 = described_class.new(JWT::JWK.new(OpenSSL::PKey::RSA.new(2048)))
109-
jwks2 = described_class.new(JWT::JWK.new(OpenSSL::PKey::EC.generate('secp384r1')))
108+
jwks1 = described_class.new(JWT::JWK.new(test_pkey('rsa-2048-private.pem')))
109+
jwks2 = described_class.new(JWT::JWK.new(test_pkey('ec384-private.pem')))
110110
jwks3 = jwks1.union(jwks2)
111111
expect(jwks1.size).to eql(1)
112112
expect(jwks2.size).to eql(1)
@@ -116,8 +116,8 @@
116116
end
117117

118118
it 'when called via "|" operator' do
119-
jwks1 = described_class.new(JWT::JWK.new(OpenSSL::PKey::RSA.new(2048)))
120-
jwks2 = described_class.new(JWT::JWK.new(OpenSSL::PKey::EC.generate('secp384r1')))
119+
jwks1 = described_class.new(JWT::JWK.new(test_pkey('rsa-2048-private.pem')))
120+
jwks2 = described_class.new(JWT::JWK.new(test_pkey('ec384-private.pem')))
121121
jwks3 = jwks1 | jwks2
122122
expect(jwks1.size).to eql(1)
123123
expect(jwks2.size).to eql(1)
@@ -127,8 +127,8 @@
127127
end
128128

129129
it 'when called directly' do
130-
jwks1 = described_class.new(JWT::JWK.new(OpenSSL::PKey::RSA.new(2048)))
131-
jwks2 = described_class.new(JWT::JWK.new(OpenSSL::PKey::EC.generate('secp384r1')))
130+
jwks1 = described_class.new(JWT::JWK.new(test_pkey('rsa-2048-private.pem')))
131+
jwks2 = described_class.new(JWT::JWK.new(test_pkey('ec384-private.pem')))
132132
jwks3 = jwks1.merge(jwks2)
133133
expect(jwks1.size).to eql(2)
134134
expect(jwks2.size).to eql(1)

Diff for: spec/jwt/jwk_spec.rb

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# frozen_string_literal: true
22

33
RSpec.describe JWT::JWK do
4-
let(:rsa_key) { OpenSSL::PKey::RSA.new(2048) }
5-
let(:ec_key) { OpenSSL::PKey::EC.generate('secp384r1') }
4+
let(:rsa_key) { test_pkey('rsa-2048-private.pem') }
5+
let(:ec_key) { test_pkey('ec256k-private.pem') }
66

77
describe '.import' do
88
let(:keypair) { rsa_key.public_key }
@@ -16,6 +16,13 @@
1616
expect(subject.export).to eq(exported_key)
1717
end
1818

19+
context 'when number is given' do
20+
let(:params) { 1234 }
21+
it 'raises an error' do
22+
expect { subject }.to raise_error(JWT::JWKError, 'Cannot create JWK from a Integer')
23+
end
24+
end
25+
1926
context 'parsed from JSON' do
2027
let(:params) { exported_key }
2128
it 'creates a ::JWT::JWK::RSA instance from JSON parsed JWK' do

Diff for: spec/jwt/jwt_spec.rb

+10
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,16 @@
429429
end.to raise_error JWT::IncorrectAlgorithm
430430
end
431431

432+
it 'raises error when keyfinder does not find anything' do
433+
token = JWT.encode(payload, 'secret', 'HS256')
434+
435+
expect do
436+
JWT.decode(token, nil, true, algorithm: 'HS256') do
437+
nil
438+
end
439+
end.to raise_error JWT::DecodeError, 'No verification key available'
440+
end
441+
432442
it 'should raise JWT::IncorrectAlgorithm when algorithms array does not contain algorithm' do
433443
token = JWT.encode payload, data[:secret], 'HS512'
434444

0 commit comments

Comments
 (0)