Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- Support only stricter base64 decoding (RFC 4648) [#658](https://github.com/jwt/ruby-jwt/pull/658) ([@anakinj](https://github.com/anakinj))
- Custom algorithms are required to include `JWT::JWA::SigningAlgorithm` [#660](https://github.com/jwt/ruby-jwt/pull/660) ([@anakinj](https://github.com/anakinj))
- Require RSA keys to be at least 2048 bits [#661](https://github.com/jwt/ruby-jwt/pull/661) ([@anakinj](https://github.com/anakinj))
- Base64 encode and decode the k value for HMAC JWKs [#662](https://github.com/jwt/ruby-jwt/pull/662) ([@anakinj](https://github.com/anakinj))

Take a look at the [upgrade guide](UPGRADING.md) for more details.

Expand Down
6 changes: 5 additions & 1 deletion UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,8 @@ Claim verification has been [split into separate classes](https://github.com/jwt

The internal algorithms were [restructured](https://github.com/jwt/ruby-jwt/pull/607) to support extensions from separate libraries. The changes led to a few deprecations and new requirements:
- The `sign` and `verify` static methods on all the algorithms (`::JWT::JWA`) will be removed.
- Custom algorithms are expected to include the `JWT::JWA::SigningAlgorithm` module.
- Custom algorithms are expected to include the `JWT::JWA::SigningAlgorithm` module.

## Base64 the `k´ value for HMAC JWKs

The gem was missing the Base64 encoding and decoding when representing and parsing a HMAC key as a JWK. This issue is now addressed. The added encoding will break compatibility with JWKs produced by older versions of the gem.
4 changes: 2 additions & 2 deletions lib/jwt/jwk/hmac.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,15 @@ def []=(key, value)
private

def secret
self[:k]
@secret ||= ::JWT::Base64.url_decode(self[:k])
end

def extract_key_params(key)
case key
when JWT::JWK::HMAC
key.export(include_private: true)
when String # Accept String key as input
{ kty: KTY, k: key }
{ kty: KTY, k: ::JWT::Base64.url_encode(key) }
when Hash
key.transform_keys(&:to_sym)
else
Expand Down
9 changes: 9 additions & 0 deletions spec/jwt/jwk/hmac_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,15 @@
end
end
end

context 'when example from RFC' do
let(:params) { { kty: 'oct', k: 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow' } }

it 'decodes the k' do
expected_key = "\x03#5K+\x0F\xA5\xBC\x83~\x06ew{\xA6\x8FZ\xB3(\xE6\xF0T\xC9(\xA9\x0F\x84\xB2\xD2P.\xBF\xD3\xFBZ\x92\xD2\x06G\xEF\x96\x8A\xB4\xC3wb=\"=.!r\x05.O\b\xC0\xCD\x9A\xF5g\xD0\x80\xA3".dup.force_encoding('ASCII-8BIT')
expect(subject.verify_key).to eq(expected_key)
end
end
end

describe '#[]=' do
Expand Down
22 changes: 11 additions & 11 deletions spec/jwt/jwk/set_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
end

it 'from a JWKS hash with symbol keys' do
jwks = { keys: [{ kty: 'oct', k: 'testkey' }] }
jwk = JWT::JWK.new({ kty: 'oct', k: 'testkey' })
jwks = { keys: [{ kty: 'oct', k: Base64.strict_encode64('testkey') }] }
jwk = JWT::JWK.new({ kty: 'oct', k: Base64.strict_encode64('testkey') })
expect(described_class.new(jwks).keys).to eql([jwk])
end

it 'from a JWKS hash with string keys' do
jwks = { 'keys' => [{ 'kty' => 'oct', 'k' => 'testkey' }] }
jwk = JWT::JWK.new({ kty: 'oct', k: 'testkey' })
jwks = { 'keys' => [{ 'kty' => 'oct', 'k' => Base64.strict_encode64('testkey') }] }
jwk = JWT::JWK.new({ kty: 'oct', k: Base64.strict_encode64('testkey') })
expect(described_class.new(jwks).keys).to eql([jwk])
end

Expand All @@ -30,7 +30,7 @@
end

it 'from an existing JWT::JWK::Set' do
jwk = JWT::JWK.new({ kty: 'oct', k: 'testkey' })
jwk = JWT::JWK.new({ kty: 'oct', k: Base64.strict_encode64('testkey') })
jwks = described_class.new(jwk)
expect(described_class.new(jwks)).to eql(jwks)
end
Expand All @@ -43,7 +43,7 @@

describe '.export' do
it 'exports the JWKS to Hash' do
jwk = JWT::JWK.new({ kty: 'oct', k: 'testkey' })
jwk = JWT::JWK.new({ kty: 'oct', k: Base64.strict_encode64('testkey') })
jwks = described_class.new(jwk)
exported = jwks.export
expect(exported[:keys].size).to eql(1)
Expand All @@ -53,15 +53,15 @@

describe '.eql?' do
it 'correctly classifies equal sets' do
jwk = JWT::JWK.new({ kty: 'oct', k: 'testkey' })
jwk = JWT::JWK.new({ kty: 'oct', k: Base64.strict_encode64('testkey') })
jwks1 = described_class.new(jwk)
jwks2 = described_class.new(jwk)
expect(jwks1).to eql(jwks2)
end

it 'correctly classifies different sets' do
jwk1 = JWT::JWK.new({ kty: 'oct', k: 'testkey' })
jwk2 = JWT::JWK.new({ kty: 'oct', k: 'testkex' })
jwk1 = JWT::JWK.new({ kty: 'oct', k: Base64.strict_encode64('testkey') })
jwk2 = JWT::JWK.new({ kty: 'oct', k: Base64.strict_encode64('testkex') })
jwks1 = described_class.new(jwk1)
jwks2 = described_class.new(jwk2)
expect(jwks1).not_to eql(jwks2)
Expand All @@ -72,8 +72,8 @@
# but Array#uniq! doesn't recognize this, despite the documentation saying otherwise
describe '.uniq!' do
it 'filters out equal keys' do
jwk = JWT::JWK.new({ kty: 'oct', k: 'testkey' })
jwk2 = JWT::JWK.new({ kty: 'oct', k: 'testkey' })
jwk = JWT::JWK.new({ kty: 'oct', k: Base64.strict_encode64('testkey') })
jwk2 = JWT::JWK.new({ kty: 'oct', k: Base64.strict_encode64('testkey') })
jwks = described_class.new([jwk, jwk2])
jwks.uniq!
expect(jwks.keys.size).to eql(1)
Expand Down
Loading