Skip to content

Commit a5bc8ea

Browse files
committed
feat: support raw ECDSA (non DER) signatures
1 parent 72799c9 commit a5bc8ea

File tree

3 files changed

+52
-1
lines changed

3 files changed

+52
-1
lines changed

lib/openssl/signature_algorithm/base.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,14 @@ def hash_function
2222
end
2323

2424
def verify(signature, verification_data)
25-
verify_key.verify(hash_function, signature, verification_data) ||
25+
formatted_signature =
26+
if respond_to?(:formatted_signature, true)
27+
formatted_signature(signature)
28+
else
29+
signature
30+
end
31+
32+
verify_key.verify(hash_function, formatted_signature, verification_data) ||
2633
raise(OpenSSL::SignatureAlgorithm::Error, "Signature verification failed")
2734
end
2835
end

lib/openssl/signature_algorithm/ecdsa.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
module OpenSSL
77
module SignatureAlgorithm
88
class ECDSA < Base
9+
BYTE_LENGTH = 8
10+
911
class SigningKey < OpenSSL::PKey::EC
1012
def initialize(*args)
1113
super(*args).generate_key
@@ -39,6 +41,29 @@ def curve_name
3941
CURVE_BY_DIGEST_LENGTH[digest_length] ||
4042
raise(OpenSSL::SignatureAlgorithm::Error, "Unsupported digest length #{digest_length}")
4143
end
44+
45+
private
46+
47+
# Borrowed from jwt rubygem.
48+
# https://github.com/jwt/ruby-jwt/blob/7a6a3f1dbaff806993156d1dff9c217bb2523ff8/lib/jwt/security_utils.rb#L34-L39
49+
#
50+
# Hopefully this will be provided by openssl rubygem in the future.
51+
def formatted_signature(signature)
52+
n = (verify_key_length.to_f / BYTE_LENGTH).ceil
53+
54+
if signature.size == n * 2
55+
r = signature[0..(n - 1)]
56+
s = signature[n..-1]
57+
58+
OpenSSL::ASN1::Sequence.new([r, s].map { |int| OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(int, 2)) }).to_der
59+
else
60+
signature
61+
end
62+
end
63+
64+
def verify_key_length
65+
verify_key.group.degree
66+
end
4267
end
4368
end
4469
end

spec/openssl/signature_algorithm/ecdsa_spec.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,23 @@
5353
algorithm.verify_key = verify_key
5454
algorithm.verify(signature, to_be_signed)
5555
end
56+
57+
it "works for raw (non DER) signature" do
58+
to_be_signed = "to-be-signed"
59+
60+
# Signer
61+
algorithm = OpenSSL::SignatureAlgorithm::ECDSA.new("256")
62+
signing_key = algorithm.generate_signing_key
63+
signature = algorithm.sign(to_be_signed)
64+
65+
raw_signature = OpenSSL::ASN1.decode(signature).value.map { |v| v.value.to_s(2) }.join
66+
67+
# Signer sends verify key to Verifier
68+
verify_key = signing_key.verify_key
69+
70+
# Verifier
71+
algorithm = OpenSSL::SignatureAlgorithm::ECDSA.new("256")
72+
algorithm.verify_key = verify_key
73+
algorithm.verify(raw_signature, to_be_signed)
74+
end
5675
end

0 commit comments

Comments
 (0)