Skip to content

Commit 2338eaf

Browse files
santiagorodriguez96grzuy
authored andcommitted
feat: add support for Ed25519
This commit attempts to close #2. Used the gem RubyCrypto/ed25519 as we still require ruby/openssl to expose the required OpenSSL APIs in Ruby, expected to be released as 3.0 – See ruby/openssl#329. Once OpenSSL Ruby v3.0 is released, we could change the implementation in a way that users that use this gem alongside that version of OpenSSL will use the API provided by OpenSSL, but users with an older version of OpenSSL will still be provided with Ed25519 support with the use of RubyCrypto/ed25519
1 parent 658c39a commit 2338eaf

File tree

10 files changed

+121
-1
lines changed

10 files changed

+121
-1
lines changed

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ gemspec
77

88
gem "appraisal", "~> 2.2"
99
gem "byebug", "~> 11.0"
10+
gem "ed25519", "~> 1.2"
1011
gem "rake", "~> 13.0"
1112
gem "rspec", "~> 3.0"
1213
gem "rubocop", "~> 0.80.1"

Gemfile.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ GEM
1414
ast (2.4.0)
1515
byebug (11.1.1)
1616
diff-lcs (1.3)
17+
ed25519 (1.2.4)
1718
jaro_winkler (1.5.4)
1819
openssl (2.2.0)
1920
parallel (1.19.1)
@@ -53,6 +54,7 @@ PLATFORMS
5354
DEPENDENCIES
5455
appraisal (~> 2.2)
5556
byebug (~> 11.0)
57+
ed25519 (~> 1.2)
5658
openssl-signature_algorithm!
5759
rake (~> 13.0)
5860
rspec (~> 3.0)

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,26 @@ algorithm.verify_key = verify_key
5252
algorithm.verify(signature, to_be_signed)
5353
```
5454

55+
### EdDSA
56+
57+
```ruby
58+
to_be_signed = "to-be-signed"
59+
60+
# Signer
61+
algorithm = OpenSSL::SignatureAlgorithm::EdDSA.new
62+
signing_key = algorithm.generate_signing_key
63+
signature = algorithm.sign(to_be_signed)
64+
65+
# Signer sends verify key to Verifier
66+
verify_key_string = signing_key.verify_key.serialize
67+
68+
# Verifier
69+
verify_key = OpenSSL::SignatureAlgorithm::EdDSA::VerifyKey.deserialize(verify_key_string)
70+
algorithm = OpenSSL::SignatureAlgorithm::EdDSA.new
71+
algorithm.verify_key = verify_key
72+
algorithm.verify(signature, to_be_signed)
73+
```
74+
5575
### RSA-PSS
5676

5777
```ruby

gemfiles/openssl_2_0.gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ gem "byebug", "~> 11.0"
77
gem "rake", "~> 13.0"
88
gem "rspec", "~> 3.0"
99
gem "rubocop", "~> 0.80.1"
10+
gem "ed25519", "~> 1.2"
1011
gem "openssl", "~> 2.0.0"
1112

1213
gemspec path: "../"

gemfiles/openssl_2_1.gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ gem "byebug", "~> 11.0"
77
gem "rake", "~> 13.0"
88
gem "rspec", "~> 3.0"
99
gem "rubocop", "~> 0.80.1"
10+
gem "ed25519", "~> 1.2"
1011
gem "openssl", "~> 2.1.0"
1112

1213
gemspec path: "../"

gemfiles/openssl_2_2.gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ gem "byebug", "~> 11.0"
77
gem "rake", "~> 13.0"
88
gem "rspec", "~> 3.0"
99
gem "rubocop", "~> 0.80.1"
10+
gem "ed25519", "~> 1.2"
1011
gem "openssl", "~> 2.2.0"
1112

1213
gemspec path: "../"

lib/openssl/signature_algorithm.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 "openssl/signature_algorithm/ecdsa"
4+
require "openssl/signature_algorithm/eddsa"
45
require "openssl/signature_algorithm/error"
56
require "openssl/signature_algorithm/rsapss"
67
require "openssl/signature_algorithm/rsapkcs1"
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# frozen_string_literal: true
2+
3+
begin
4+
gem "ed25519", ">= 1.0.0"
5+
require "ed25519"
6+
rescue LoadError
7+
warn "OpenSSL::SignatureAlgorithm::EdDSA requires the ed25519 gem, version 1.0 or higher. "\
8+
"Please add it to your Gemfile: `gem \"ed25519\", \"~> 1.0\"`"
9+
raise
10+
end
11+
12+
require "openssl/signature_algorithm/base"
13+
14+
module OpenSSL
15+
module SignatureAlgorithm
16+
class EdDSA < Base
17+
class SigningKey < ::Ed25519::SigningKey
18+
def verify_key
19+
VerifyKey.new(keypair[32, 32])
20+
end
21+
end
22+
23+
class VerifyKey < ::Ed25519::VerifyKey
24+
def self.deserialize(key_bytes)
25+
new(key_bytes)
26+
end
27+
28+
def serialize
29+
to_bytes
30+
end
31+
end
32+
33+
def generate_signing_key
34+
@signing_key = SigningKey.generate
35+
end
36+
37+
def sign(data)
38+
signing_key.sign(data)
39+
end
40+
41+
def verify(signature, verification_data)
42+
verify_key.verify(signature, verification_data)
43+
rescue ::Ed25519::VerifyError
44+
raise(OpenSSL::SignatureAlgorithm::SignatureVerificationError, "Signature verification failed")
45+
end
46+
end
47+
end
48+
end

openssl-signature_algorithm.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
99
spec.email = ["gonzalo@cedarcode.com"]
1010
spec.license = "Apache-2.0"
1111

12-
spec.summary = "ECDSA, RSA-PSS and RSA-PKCS#1 algorithms for ruby"
12+
spec.summary = "ECDSA, EdDSA, RSA-PSS and RSA-PKCS#1 algorithms for ruby"
1313
spec.description = spec.summary
1414

1515
spec.homepage = "https://github.com/cedarcode/openssl-signature_algorithm"
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# frozen_string_literal: true
2+
3+
require "openssl/signature_algorithm/eddsa"
4+
5+
RSpec.describe "OpenSSL::SignatureAlgorithm::EdDSA" do
6+
let(:to_be_signed) { "to-be-signed" }
7+
let(:signature) do
8+
signing_key
9+
signer_algorithm.sign(to_be_signed)
10+
end
11+
let(:signer_algorithm) { OpenSSL::SignatureAlgorithm::EdDSA.new }
12+
let(:signing_key) { signer_algorithm.generate_signing_key }
13+
let(:verifier_algorithm) { OpenSSL::SignatureAlgorithm::EdDSA.new }
14+
15+
context "when everything is in place" do
16+
it "works" do
17+
# Signer sends verify key to Verifier
18+
verify_key_string = signing_key.verify_key.serialize
19+
20+
# Verifier
21+
verifier_algorithm.verify_key = OpenSSL::SignatureAlgorithm::EdDSA::VerifyKey.deserialize(verify_key_string)
22+
expect(verifier_algorithm.verify(signature, to_be_signed)).to be_truthy
23+
end
24+
end
25+
26+
context "when signature is invalid" do
27+
let(:signature) do
28+
signing_key
29+
signature = signer_algorithm.sign(to_be_signed)
30+
signature[63] = 'X' # Change the last byte to make it incorrect
31+
32+
signature
33+
end
34+
35+
it "raises an error" do
36+
# Signer sends verify key to Verifier
37+
verify_key_string = signing_key.verify_key.serialize
38+
39+
# Verifier
40+
verifier_algorithm.verify_key = OpenSSL::SignatureAlgorithm::EdDSA::VerifyKey.deserialize(verify_key_string)
41+
expect { verifier_algorithm.verify(signature, to_be_signed) }
42+
.to raise_error(OpenSSL::SignatureAlgorithm::SignatureVerificationError)
43+
end
44+
end
45+
end

0 commit comments

Comments
 (0)