Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Algorithm unit tests #613

Merged
merged 1 commit into from
Sep 15, 2024
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 @@ -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
Loading