Skip to content

Commit

Permalink
Do not require EdDSA algo if rbnacl not available.
Browse files Browse the repository at this point in the history
Unit tests for some algos
  • Loading branch information
anakinj committed Sep 15, 2024
1 parent 71019d7 commit 33e636f
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

- Refactor claim validators into their own classes [#605](https://github.com/jwt/ruby-jwt/pull/605) ([@anakinj](https://github.com/anakinj), [@MatteoPierro](https://github.com/MatteoPierro))
- Allow extending available algorithms [#607](https://github.com/jwt/ruby-jwt/pull/607) ([@anakinj](https://github.com/anakinj))
- Do not include the EdDSA algorithm if rbnacl not available [#613](https://github.com/jwt/ruby-jwt/pull/613) ([@anakinj](https://github.com/anakinj))
- Your contribution here

## [v2.8.2](https://github.com/jwt/ruby-jwt/tree/v2.8.2) (2024-06-18)
Expand Down
6 changes: 4 additions & 2 deletions lib/jwt/jwa.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@
end

require_relative 'jwa/signing_algorithm'

require_relative 'jwa/ecdsa'
require_relative 'jwa/eddsa'
require_relative 'jwa/hmac'
require_relative 'jwa/none'
require_relative 'jwa/ps'
require_relative 'jwa/rsa'
require_relative 'jwa/unsupported'
require_relative 'jwa/wrapper'

if JWT.rbnacl?
require_relative 'jwa/eddsa'
end

if JWT.rbnacl_6_or_greater?
require_relative 'jwa/hmac_rbnacl'
elsif JWT.rbnacl?
Expand Down
1 change: 0 additions & 1 deletion lib/jwt/jwa/hmac.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ def initialize(alg, digest)

def sign(data:, signing_key:)
signing_key ||= ''

raise_verify_error!('HMAC key expected to be a String') unless signing_key.is_a?(String)

OpenSSL::HMAC.digest(digest.new, signing_key, data)
Expand Down
22 changes: 22 additions & 0 deletions spec/jwt/jwa/hmac_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@

RSpec.describe JWT::JWA::Hmac do
let(:instance) { described_class.new('HS256', OpenSSL::Digest::SHA256) }
let(:valid_signature) { [60, 56, 87, 72, 185, 194, 150, 13, 18, 148, 76, 245, 94, 91, 201, 64, 111, 91, 167, 156, 43, 148, 41, 113, 168, 156, 137, 12, 11, 31, 58, 97].pack('C*') }
let(:hmac_secret) { 'secret_key' }

describe '#sign' do
subject { instance.sign(data: 'test', signing_key: hmac_secret) }

context 'when signing with a key' do
it { is_expected.to eq(valid_signature) }
end

# Address OpenSSL 3.0 errors with empty hmac_secret - https://github.com/jwt/ruby-jwt/issues/526
context 'when nil hmac_secret is passed' do
let(:hmac_secret) { nil }
Expand Down Expand Up @@ -103,4 +109,20 @@
end
end
end

describe '#verify' do
subject { instance.verify(data: 'test', signature: signature, verification_key: hmac_secret) }

context 'when signature is valid' do
let(:signature) { valid_signature }

it { is_expected.to be(true) }
end

context 'when signature is invalid' do
let(:signature) { [60, 56, 87, 72, 185, 194].pack('C*') }

it { is_expected.to be(false) }
end
end
end
72 changes: 72 additions & 0 deletions spec/jwt/jwa/ps_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# frozen_string_literal: true

RSpec.describe JWT::JWA::Ps do
let(:rsa_key) { OpenSSL::PKey::RSA.generate(2048) }
let(:data) { 'test data' }
let(:ps256_instance) { described_class.new('PS256') }
let(:ps384_instance) { described_class.new('PS384') }
let(:ps512_instance) { described_class.new('PS512') }

describe '#initialize' do
it 'initializes with the correct algorithm and digest' do
expect(ps256_instance.instance_variable_get(:@alg)).to eq('PS256')
expect(ps256_instance.send(:digest_algorithm)).to eq('sha256')

expect(ps384_instance.instance_variable_get(:@alg)).to eq('PS384')
expect(ps384_instance.send(:digest_algorithm)).to eq('sha384')

expect(ps512_instance.instance_variable_get(:@alg)).to eq('PS512')
expect(ps512_instance.send(:digest_algorithm)).to eq('sha512')
end
end

describe '#sign' do
context 'with a valid RSA key' do
it 'signs the data with PS256' do
expect(ps256_instance.sign(data: data, signing_key: rsa_key)).not_to be_nil
end

it 'signs the data with PS384' do
expect(ps384_instance.sign(data: data, signing_key: rsa_key)).not_to be_nil
end

it 'signs the data with PS512' do
expect(ps512_instance.sign(data: data, signing_key: rsa_key)).not_to be_nil
end
end

context 'with an invalid key' do
it 'raises an error' do
expect do
ps256_instance.sign(data: data, signing_key: 'invalid_key')
end.to raise_error(JWT::EncodeError, /The given key is a String. It has to be an OpenSSL::PKey::RSA instance./)
end
end
end

describe '#verify' do
let(:ps256_signature) { ps256_instance.sign(data: data, signing_key: rsa_key) }
let(:ps384_signature) { ps384_instance.sign(data: data, signing_key: rsa_key) }
let(:ps512_signature) { ps512_instance.sign(data: data, signing_key: rsa_key) }

context 'with a valid RSA key' do
it 'verifies the signature with PS256' do
expect(ps256_instance.verify(data: data, signature: ps256_signature, verification_key: rsa_key)).to be(true)
end

it 'verifies the signature with PS384' do
expect(ps384_instance.verify(data: data, signature: ps384_signature, verification_key: rsa_key)).to be(true)
end

it 'verifies the signature with PS512' do
expect(ps512_instance.verify(data: data, signature: ps512_signature, verification_key: rsa_key)).to be(true)
end
end

context 'with an invalid signature' do
it 'raises a verification error' do
expect(ps256_instance.verify(data: data, signature: 'invalid_signature', verification_key: rsa_key)).to be(false)
end
end
end
end
53 changes: 53 additions & 0 deletions spec/jwt/jwa/rsa_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# frozen_string_literal: true

RSpec.describe JWT::JWA::Rsa do
let(:rsa_key) { OpenSSL::PKey::RSA.generate(2048) }
let(:data) { 'test data' }
let(:rsa_instance) { described_class.new('RS256') }

describe '#initialize' do
it 'initializes with the correct algorithm and digest' do
expect(rsa_instance.instance_variable_get(:@alg)).to eq('RS256')
expect(rsa_instance.send(:digest).name).to eq('SHA256')
end
end

describe '#sign' do
context 'with a valid RSA key' do
it 'signs the data' do
signature = rsa_instance.sign(data: data, signing_key: rsa_key)
expect(signature).not_to be_nil
end
end

context 'with an invalid key' do
it 'raises an error' do
expect do
rsa_instance.sign(data: data, signing_key: 'invalid_key')
end.to raise_error(JWT::EncodeError, /The given key is a String. It has to be an OpenSSL::PKey::RSA instance/)
end
end
end

describe '#verify' do
let(:signature) { rsa_instance.sign(data: data, signing_key: rsa_key) }

context 'with a valid RSA key' do
it 'returns true' do
expect(rsa_instance.verify(data: data, signature: signature, verification_key: rsa_key)).to be(true)
end
end

context 'with an invalid signature' do
it 'returns false' do
expect(rsa_instance.verify(data: data, signature: 'invalid_signature', verification_key: rsa_key)).to be(false)
end
end

context 'with an invalid key' do
it 'returns false' do
expect(rsa_instance.verify(data: data, signature: 'invalid_signature', verification_key: OpenSSL::PKey::RSA.generate(2048))).to be(false)
end
end
end
end

0 comments on commit 33e636f

Please sign in to comment.