Skip to content

Commit a0f8200

Browse files
authored
feat: Replace rbnacl with ed25519 (#9)
* Replace rbnacl with ed25519 * Test with a public JWK
1 parent cd98dcf commit a0f8200

File tree

7 files changed

+39
-32
lines changed

7 files changed

+39
-32
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
- name: Set up Ruby
1818
uses: ruby/setup-ruby@v1
1919
with:
20-
ruby-version: "3.3"
20+
ruby-version: ruby
2121
bundler-cache: true
2222
- name: Run RuboCop
2323
run: bundle exec rubocop
@@ -38,11 +38,6 @@
3838
steps:
3939
- uses: actions/checkout@v4
4040

41-
- name: Install libsodium
42-
run: |
43-
sudo apt-get update -q
44-
sudo apt-get install libsodium-dev -y
45-
4641
- name: Set up Ruby
4742
uses: ruby/setup-ruby@v1
4843
with:

jwt-eddsa.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ Gem::Specification.new do |spec|
3333
spec.require_paths = ["lib"]
3434

3535
spec.add_dependency "base64"
36+
spec.add_dependency "ed25519"
3637
spec.add_dependency "jwt", "> 2.8.2"
37-
spec.add_dependency "rbnacl", "~> 6.0"
3838

3939
spec.metadata["rubygems_mfa_required"] = "true"
4040
end

lib/jwt/eddsa.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# frozen_string_literal: true
22

33
require "jwt"
4+
require "ed25519"
45

56
require_relative "eddsa/version"
67
require_relative "eddsa/jwk/okp"

lib/jwt/eddsa/algo.rb

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,20 @@ module Algo
1010

1111
class << self
1212
def sign(_algorithm, msg, key)
13-
unless key.is_a?(RbNaCl::Signatures::Ed25519::SigningKey)
14-
raise_sign_error!("Key given is a #{key.class} but needs to be a " \
15-
"RbNaCl::Signatures::Ed25519::SigningKey")
13+
unless key.is_a?(Ed25519::SigningKey)
14+
raise_sign_error!("Key given is a #{key.class} but needs to be a Ed25519::SigningKey")
1615
end
1716

1817
key.sign(msg)
1918
end
2019

2120
def verify(_algorithm, public_key, signing_input, signature)
22-
unless public_key.is_a?(RbNaCl::Signatures::Ed25519::VerifyKey)
23-
raise_verify_error!("Key given is a #{public_key.class} but needs to be a " \
24-
"RbNaCl::Signatures::Ed25519::VerifyKey")
21+
unless public_key.is_a?(Ed25519::VerifyKey)
22+
raise_verify_error!("Key given is a #{public_key.class} but needs to be a Ed25519::VerifyKey")
2523
end
2624

2725
public_key.verify(signature, signing_input)
28-
rescue RbNaCl::CryptoError
26+
rescue Ed25519::VerifyError
2927
false
3028
end
3129
end

lib/jwt/eddsa/jwk/okp.rb

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ module JWK
66
# https://datatracker.ietf.org/doc/html/rfc8037
77
class OKP < ::JWT::JWK::KeyBase
88
KTY = "OKP"
9-
KTYS = [KTY, JWT::EdDSA::JWK::OKP, RbNaCl::Signatures::Ed25519::SigningKey,
10-
RbNaCl::Signatures::Ed25519::VerifyKey].freeze
9+
KTYS = [KTY, JWT::EdDSA::JWK::OKP, Ed25519::SigningKey, Ed25519::VerifyKey].freeze
1110
OKP_PUBLIC_KEY_ELEMENTS = %i[kty n x].freeze
1211
OKP_PRIVATE_KEY_ELEMENTS = %i[d].freeze
1312

@@ -63,20 +62,20 @@ def extract_key_params(key) # rubocop:disable Metrics/MethodLength
6362
case key
6463
when JWT::JWK::KeyBase
6564
key.export(include_private: true)
66-
when RbNaCl::Signatures::Ed25519::SigningKey
65+
when Ed25519::SigningKey
6766
@signing_key = key
6867
@verify_key = key.verify_key
6968
parse_okp_key_params(@verify_key, @signing_key)
70-
when RbNaCl::Signatures::Ed25519::VerifyKey
69+
when Ed25519::VerifyKey
7170
@signing_key = nil
7271
@verify_key = key
7372
parse_okp_key_params(@verify_key)
7473
when Hash
7574
key.transform_keys(&:to_sym)
7675
else
7776
raise ArgumentError,
78-
"key must be of type RbNaCl::Signatures::Ed25519::SigningKey, " \
79-
"RbNaCl::Signatures::Ed25519::VerifyKey " \
77+
"key must be of type Ed25519::SigningKey, " \
78+
"Ed25519::VerifyKey " \
8079
"or Hash with key parameters"
8180
end
8281
end
@@ -101,13 +100,13 @@ def parse_okp_key_params(verify_key, signing_key = nil)
101100
end
102101

103102
def verify_key_from_parameters
104-
RbNaCl::Signatures::Ed25519::VerifyKey.new(::Base64.urlsafe_decode64(self[:x]))
103+
Ed25519::VerifyKey.new(::Base64.urlsafe_decode64(self[:x]))
105104
end
106105

107106
def signing_key_from_parameters
108107
return nil unless self[:d]
109108

110-
RbNaCl::Signatures::Ed25519::SigningKey.new(::Base64.urlsafe_decode64(self[:d]))
109+
Ed25519::SigningKey.new(::Base64.urlsafe_decode64(self[:d]))
111110
end
112111

113112
class << self

spec/integration_spec.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
require "securerandom"
44

55
RSpec.describe "Usage via ruby-jwt" do
6-
let(:private_key) { RbNaCl::Signatures::Ed25519::SigningKey.new("b" * 32) }
6+
let(:private_key) { Ed25519::SigningKey.new("b" * 32) }
77
let(:public_key) { private_key.verify_key }
88

99
let(:payload) { { "pay" => "load" } }
@@ -18,7 +18,7 @@
1818
end
1919

2020
context "when decoding key is wrong" do
21-
let(:public_key) { RbNaCl::Signatures::Ed25519::SigningKey.new("a" * 32).verify_key }
21+
let(:public_key) { Ed25519::SigningKey.new("a" * 32).verify_key }
2222
it "raises decoding error" do
2323
token = JWT.encode(payload, private_key, "EdDSA")
2424

@@ -48,7 +48,7 @@
4848
end
4949

5050
describe "OKP JWK usage" do
51-
let(:jwk) { JWT::JWK.new(RbNaCl::Signatures::Ed25519::SigningKey.new(SecureRandom.hex)) }
51+
let(:jwk) { JWT::JWK.new(Ed25519::SigningKey.new(SecureRandom.hex)) }
5252
let(:public_jwks) { { keys: [jwk.export, { kid: "not_the_correct_one", kty: "oct", k: "secret" }] } }
5353
let(:signed_token) { JWT.encode(token_payload, jwk.signing_key, "EdDSA", token_headers) }
5454
let(:token_payload) { { "data" => "something" } }

spec/jwt/jwk/okp_spec.rb

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
require "securerandom"
44

55
RSpec.describe JWT::EdDSA::JWK::OKP do
6-
let(:private_key) { RbNaCl::Signatures::Ed25519::SigningKey.new(SecureRandom.hex) }
6+
let(:private_key) { Ed25519::SigningKey.new(SecureRandom.hex) }
77
let(:public_key) { private_key.verify_key }
88
let(:key) { nil }
99

@@ -37,13 +37,27 @@
3737
end
3838
it { is_expected.to be_a(described_class) }
3939
end
40+
41+
context "when a random key found from the Internet is given" do
42+
let(:key) do
43+
{
44+
"kty" => "OKP",
45+
"kid" => "-1909572257",
46+
"alg" => "EdDSA",
47+
"crv" => "Ed25519",
48+
"x" => "XWxGtApfcqmKI7p0OKnF5JSEWMVoLsytFXLEP7xZ_l8"
49+
}
50+
end
51+
52+
it { is_expected.to be_a(described_class) }
53+
end
4054
end
4155

4256
describe "#verify_key" do
4357
let(:key) { private_key }
4458
subject { instance.verify_key }
4559
it "is the verify key" do
46-
expect(subject).to be_a(RbNaCl::Signatures::Ed25519::VerifyKey)
60+
expect(subject).to be_a(Ed25519::VerifyKey)
4761
end
4862
end
4963

@@ -91,7 +105,7 @@
91105
let(:import_data) { described_class.new(public_key).export }
92106
it "creates a new instance of the class" do
93107
expect(subject.private?).to eq(false)
94-
expect(subject.verify_key).to be_a(RbNaCl::Signatures::Ed25519::VerifyKey)
108+
expect(subject.verify_key).to be_a(Ed25519::VerifyKey)
95109
expect(subject.signing_key).to eq(nil)
96110
expect(subject.verify_key.to_bytes).to eq(public_key.to_bytes)
97111
expect(subject.kid).to eq(import_data[:kid])
@@ -102,8 +116,8 @@
102116
let(:import_data) { described_class.new(private_key).export(include_private: true) }
103117
it "creates a new instance of the class" do
104118
expect(subject.private?).to eq(true)
105-
expect(subject.verify_key).to be_a(RbNaCl::Signatures::Ed25519::VerifyKey)
106-
expect(subject.signing_key).to be_a(RbNaCl::Signatures::Ed25519::SigningKey)
119+
expect(subject.verify_key).to be_a(Ed25519::VerifyKey)
120+
expect(subject.signing_key).to be_a(Ed25519::SigningKey)
107121
expect(subject.verify_key.to_bytes).to eq(public_key.to_bytes)
108122
expect(subject.kid).to eq(import_data[:kid])
109123
end
@@ -113,8 +127,8 @@
113127
let(:import_data) { described_class.new(private_key) }
114128
it "creates a new instance of the class" do
115129
expect(subject.private?).to eq(true)
116-
expect(subject.verify_key).to be_a(RbNaCl::Signatures::Ed25519::VerifyKey)
117-
expect(subject.signing_key).to be_a(RbNaCl::Signatures::Ed25519::SigningKey)
130+
expect(subject.verify_key).to be_a(Ed25519::VerifyKey)
131+
expect(subject.signing_key).to be_a(Ed25519::SigningKey)
118132
expect(subject.verify_key.to_bytes).to eq(public_key.to_bytes)
119133
expect(subject.kid).to eq(import_data[:kid])
120134
end

0 commit comments

Comments
 (0)