linzer 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6b025e976012c79dc5153ebd4ca98f23df08a55bf0706c6d8427e0374b3cd7f5
4
- data.tar.gz: b864415aad8cc6c9cbee217a64531378b92edbb62fb58b67a036e5997fec4a30
3
+ metadata.gz: 22298d26596b660ac67a7f039ed0d05cc41715a5413c4583a4703ce452e6548c
4
+ data.tar.gz: 735d31e3752eea02207baa7e093bd18a114281f4c493acf98cd7451d73e03fff
5
5
  SHA512:
6
- metadata.gz: 9d2eea3036d11c6b7f8969d0f30b71fa3715d62820acde93c7d512689aa7c91d281436f8b2ea91e47b906f62167a4611298d2fad64bcf241b39b18f4405ed7a9
7
- data.tar.gz: 6f85ffe146b8a52647969575dc80011f5fefdf299bdf082cd4f3b4de0fc605f60f8beaaa1cc4cf04843cf2b3a5f7d6441c7f6b3989d50ed559cca398bbc3d7b6
6
+ metadata.gz: 81428b963ffaa3f39e86ed28e52927923998aaeeb07f773a852bb01abe9272f812c8b0a593813293908845f57799c849163da00d4ae4ca2ef62d36687055ce81
7
+ data.tar.gz: 3f91ef995bd53bda69832e774ce383cf55a8ef903ffe615e8dbb1a586cf437b85a3d98f8082f39311a6757a3bd4f2657ac4b5d73c3270b4a4534ea571b9e8427
data/.standard.yml CHANGED
@@ -1,3 +1,11 @@
1
1
  # For available configuration options, see:
2
2
  # https://github.com/testdouble/standard
3
3
  ruby_version: 2.6
4
+
5
+ ignore:
6
+ - 'lib/**/*':
7
+ - Layout/ExtraSpacing
8
+ - Layout/HashAlignment
9
+ - 'spec/**/*':
10
+ - Layout/ExtraSpacing
11
+ - Layout/HashAlignment
data/CHANGELOG.md CHANGED
@@ -1,6 +1,16 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.0] - 2024-02-28
4
+
5
+ - Add support for the following algorithms: Ed25519, HMAC-SHA256 and
6
+ ECDSA (P-256 and P-384 curves).
7
+
8
+ ## [0.2.0] - 2024-02-23
9
+
10
+ - Add signature signing functionality. RSASSA-PSS using SHA-512 is still the only
11
+ supported algorithm.
12
+
3
13
  ## [0.1.0] - 2024-02-18
4
14
 
5
15
  - Initial release
6
- - It barely passes unit tests to verify signatures with RSASSA-PSS Using SHA-512.
16
+ - It barely passes unit tests to verify signatures with RSASSA-PSS using SHA-512.
data/README.md CHANGED
@@ -14,11 +14,56 @@ Or just `gem install linzer`.
14
14
 
15
15
  ## Usage
16
16
 
17
- TODO: Write usage instructions here
17
+ ### To sign a HTTP message:
18
18
 
19
- For now just take a look at the unit tests.
19
+ ```ruby
20
+ irb(main):001:0> key = Linzer.generate_ed25519_key
21
+ # => #<Linzer::Ed25519::Key:0x00000fe13e9bd208
22
+
23
+ message = Linzer::Message.new(headers: {"date" => "Fri, 23 Feb 2024 17:57:23 GMT", "x-custom-header" => "foo"})
24
+ # => #<Linzer::Message:0x0000000111b592a0 @headers={"date"=>"Fri, 23 Feb 2024 17:57:23 GMT", ...
25
+
26
+ fields = %w[date x-custom-header]
27
+ signature = Linzer.sign(key, message, fields)
28
+ # => #<Linzer::Signature:0x0000000111f77ad0 ...
29
+
30
+ puts signature.to_h
31
+ {"signature"=>
32
+ "sig1=:8rLY3nFtezwwsK+sqZEMe7wzbNHojZJGEnvp3suKichgwH...",
33
+ "signature-input"=>"sig1=(\"date\" \"x-custom-header\");created=1709075013;keyid=\"test-key-ed25519\""}
34
+ ```
35
+
36
+ ### To verify a valid signature:
37
+
38
+ ```ruby
39
+ pubkey = Linzer.new_ed25519_public_key(test_ed25519_key_pub, "some-key-ed25519")
40
+ # => #<Linzer::Ed25519::Key:0x00000fe19b9384b0
41
+
42
+ headers = {"signature-input" => "...", signature => "...", "date" => "Fri, 23 Feb 2024 13:18:15 GMT", "x-custom-header" => "bar"})
43
+
44
+ message = Linzer::Message.new(headers)
45
+ # => #<Linzer::Message:0x0000000111b592a0 @headers={"date"=>"Fri, 23 Feb 2024 13:18:15 GMT", ...
46
+
47
+ signature = Linzer::Signature.build(headers)
48
+ # => #<Linzer::Signature:0x0000000112396008 ...
49
+
50
+ Linzer.verify(pubkey, message, signature)
51
+ # => true
52
+ ```
53
+
54
+ ### What if an invalid signature if verified?
55
+
56
+ ```ruby
57
+ result = Linzer.verify(pubkey, message, signature)
58
+ lib/linzer/verifier.rb:34:in `verify_or_fail': Failed to verify message: Invalid signature. (Linzer::Error)
59
+ ```
60
+
61
+ For now, to consult additional details, just take a look at source code and/or the unit tests.
62
+
63
+ Please note that is still early days and extensive testing is still ongoing. For now only the following algorithms are supported: RSASSA-PSS using SHA-512, HMAC-SHA256, Ed25519 and ECDSA P-256 curve. ECDSA P-384 curve was also added but not tested yet.
20
64
 
21
- It's still early days, so only signatures RSASSA-PSS Using SHA-512 like ones described in the RFC are supported. I'll be expanding the library to cover more functionality specified in the RFC in subsequent releases.
65
+ I'll be expanding the library to cover more functionality specified in the RFC
66
+ in subsequent releases.
22
67
 
23
68
 
24
69
  ## Development
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Linzer
4
+ module Common
5
+ def signature_base(message, components, parameters)
6
+ signature_base = components.each_with_object(+"") do |component, base|
7
+ base << "\"#{component}\": #{message[component]}\n"
8
+ end
9
+
10
+ signature_params =
11
+ Starry.serialize([Starry::InnerList.new(components, parameters)])
12
+
13
+ signature_base << "\"@signature-params\": #{signature_params}"
14
+ signature_base
15
+ end
16
+
17
+ def validate_components(message, components)
18
+ if components.include?("@signature-params")
19
+ raise Error.new "Invalid component in signature input"
20
+ end
21
+ msg = "Cannot verify signature. Missing component in message: %s"
22
+ components.each { |c| raise Error.new msg % "\"#{c}\"" unless message.field?(c) }
23
+ msg = "Invalid signature. Duplicated component in signature input."
24
+ raise Error.new msg if components.size != components.uniq.size
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Linzer
4
+ module ECDSA
5
+ class Key < Linzer::Key
6
+ def validate
7
+ super
8
+ validate_digest
9
+ end
10
+
11
+ def sign(data)
12
+ material.sign(@params[:digest], data)
13
+ end
14
+
15
+ def verify(signature, data)
16
+ material.verify(@params[:digest], signature, data)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ed25519"
4
+
5
+ module Linzer
6
+ module Ed25519
7
+ class Key < Linzer::Key
8
+ def sign(data)
9
+ material.sign(data)
10
+ end
11
+
12
+ def verify(signature, data)
13
+ verify_key = material.is_a?(::Ed25519::SigningKey) ? material.verify_key : material
14
+ verify_key.verify(signature, data)
15
+ rescue ::Ed25519::VerifyError
16
+ false
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Linzer
4
+ module HMAC
5
+ class Key < Linzer::Key
6
+ def validate
7
+ super
8
+ validate_digest
9
+ end
10
+
11
+ def sign(data)
12
+ OpenSSL::HMAC.digest(@params[:digest], material, data)
13
+ end
14
+
15
+ def verify(signature, data)
16
+ signature == sign(data)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Linzer
4
+ class Key
5
+ module Helper
6
+ def generate_rsa_pss_sha512_key(size, key_id = nil)
7
+ material = OpenSSL::PKey::RSA.generate(size)
8
+ Linzer::RSA::Key.new(material, id: key_id, digest: "SHA512")
9
+ end
10
+
11
+ def new_rsa_pss_sha512_key(material, key_id = nil)
12
+ key = OpenSSL::PKey.read(material)
13
+ Linzer::RSA::Key.new(key, id: key_id, digest: "SHA512")
14
+ end
15
+
16
+ def new_rsa_pss_sha512_public_key(material, key_id = nil)
17
+ key = OpenSSL::PKey::RSA.new(material)
18
+ Linzer::RSA::Key.new(key, id: key_id, digest: "SHA512")
19
+ end
20
+
21
+ # XXX: to-do
22
+ # Linzer::RSA::Key
23
+ # def new_rsa_v1_5_sha256_key
24
+ # def generate_rsa_v1_5_sha256_key
25
+
26
+ def generate_hmac_sha256_key(key_id = nil)
27
+ material = OpenSSL::Random.random_bytes(64)
28
+ Linzer::HMAC::Key.new(material, id: key_id, digest: "SHA256")
29
+ end
30
+
31
+ def new_hmac_sha256_key(material, key_id = nil)
32
+ Linzer::HMAC::Key.new(material, id: key_id, digest: "SHA256")
33
+ end
34
+
35
+ def generate_ed25519_key(key_id = nil)
36
+ material = ::Ed25519::SigningKey.generate
37
+ Linzer::Ed25519::Key.new(material, id: key_id)
38
+ end
39
+
40
+ def new_ed25519_key(material, key_id = nil)
41
+ key = ::Ed25519::SigningKey.new(material)
42
+ Linzer::Ed25519::Key.new(key, id: key_id)
43
+ end
44
+
45
+ def new_ed25519_public_key(material, key_id = nil)
46
+ key = ::Ed25519::VerifyKey.new(material)
47
+ Linzer::Ed25519::Key.new(key, id: key_id)
48
+ end
49
+
50
+ # https://www.rfc-editor.org/rfc/rfc4492.html#appendix-A
51
+ # Table 6: Equivalent curves defined by SECG, ANSI, and NIST
52
+ # secp256r1 | prime256v1 | NIST P-256
53
+ def generate_ecdsa_p256_sha256_key(key_id = nil)
54
+ material = OpenSSL::PKey::EC.generate("prime256v1")
55
+ Linzer::ECDSA::Key.new(material, id: key_id, digest: "SHA256")
56
+ end
57
+
58
+ def new_ecdsa_p256_sha256_key(material, key_id = nil)
59
+ key = OpenSSL::PKey::EC.new(material)
60
+ Linzer::ECDSA::Key.new(key, id: key_id, digest: "SHA256")
61
+ end
62
+
63
+ # https://www.rfc-editor.org/rfc/rfc4492.html#appendix-A
64
+ # Table 6: Equivalent curves defined by SECG, ANSI, and NIST
65
+ # secp384r1 | | NIST P-384
66
+ def generate_ecdsa_p384_sha256_key(key_id = nil)
67
+ material = OpenSSL::PKey::EC.generate("secp384r1")
68
+ Linzer::ECDSA::Key.new(material, id: key_id, digest: "SHA384")
69
+ end
70
+
71
+ def new_ecdsa_p384_sha384_key(material, key_id = nil)
72
+ key = OpenSSL::PKey::EC.new(material)
73
+ Linzer::ECDSA::Key.new(key, id: key_id, digest: "SHA384")
74
+ end
75
+ end
76
+ end
77
+ end
data/lib/linzer/key.rb ADDED
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Linzer
4
+ class Key
5
+ def initialize(material, params = {})
6
+ @material = material
7
+ @params = Hash(params).clone.freeze
8
+ validate
9
+ freeze
10
+ end
11
+
12
+ attr_reader :material
13
+
14
+ def key_id
15
+ @params[:id]
16
+ end
17
+
18
+ def sign(*args)
19
+ abstract_error = "Cannot sign data, \"#{self.class}\" is an abstract class."
20
+ raise Error.new abstract_error
21
+ end
22
+
23
+ def verify(*args)
24
+ abstract_error = "Cannot verify signature, \"#{self.class}\" is an abstract class."
25
+ raise Error.new abstract_error
26
+ end
27
+
28
+ private
29
+
30
+ def validate
31
+ !material.nil? or raise Error.new "Invalid key. No key material provided."
32
+ end
33
+
34
+ def validate_digest
35
+ no_digest = !@params.key?(:digest) || @params[:digest].nil? || String(@params[:digest]).empty?
36
+ no_digest_error = "Invalid key definition, no digest algorithm was selected."
37
+ raise Linzer::Error.new no_digest_error if no_digest
38
+ end
39
+ end
40
+ end
@@ -3,8 +3,9 @@
3
3
  module Linzer
4
4
  class Message
5
5
  def initialize(request_data)
6
- @headers = Hash(request_data[:headers])
7
- @http = Hash(request_data[:http])
6
+ @headers = Hash(request_data[:headers].clone).freeze
7
+ @http = Hash(request_data[:http].clone).freeze
8
+ freeze
8
9
  end
9
10
 
10
11
  def empty?
@@ -15,16 +16,29 @@ module Linzer
15
16
  @headers.key?(header)
16
17
  end
17
18
 
19
+ def field?(f)
20
+ !!self[f]
21
+ end
22
+
18
23
  def [](field_name)
19
24
  return @headers[field_name] if !field_name.start_with?("@")
20
25
 
21
26
  case field_name
22
- when "@method" then @http["method"]
27
+ when "@method" then @http["method"]
23
28
  when "@authority" then @http["host"]
24
- when "@path" then @http["path"]
29
+ when "@path" then @http["path"]
30
+ when "@status" then @http["status"]
25
31
  else # XXX: improve this and add support for all fields in the RFC
26
32
  raise Error.new "Unknown/unsupported field: \"#{field_name}\""
27
33
  end
28
34
  end
35
+
36
+ class << self
37
+ def parse_structured_dictionary(str, field_name = nil)
38
+ Starry.parse_dictionary(str)
39
+ rescue Starry::ParseError => _
40
+ raise Error.new "Cannot parse \"#{field_name}\" field. Bailing out!"
41
+ end
42
+ end
29
43
  end
30
44
  end
data/lib/linzer/rsa.rb ADDED
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Linzer
4
+ module RSA
5
+ class Key < Linzer::Key
6
+ def validate
7
+ super
8
+ validate_digest
9
+ end
10
+
11
+ def sign(data)
12
+ # XXX: should check if the key is usable for signing
13
+ @material.sign(@params[:digest], data)
14
+ end
15
+
16
+ def verify(signature, data)
17
+ # XXX: should check if the key is usable for verifying
18
+ return true if @material.verify_pss(
19
+ @params[:digest],
20
+ signature,
21
+ data,
22
+ salt_length: @params[:salt_length] || :auto,
23
+ mgf1_hash: @params[:digest]
24
+ )
25
+ false
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Linzer
4
+ class Signature
5
+ def initialize(metadata, value, label, parameters = {})
6
+ @metadata = metadata.clone.freeze
7
+ @value = value.clone.freeze
8
+ @parameters = parameters.clone.freeze
9
+ @label = label.clone.freeze
10
+ freeze
11
+ end
12
+
13
+ attr_reader :metadata, :value, :parameters, :label
14
+ alias_method :components, :metadata
15
+ alias_method :bytes, :value
16
+
17
+ def to_h
18
+ {
19
+ "signature" => Starry.serialize({label => value}),
20
+ "signature-input" =>
21
+ Starry.serialize({label =>
22
+ Starry::InnerList.new(components, parameters)})
23
+ }
24
+ end
25
+
26
+ class << self
27
+ private :new
28
+
29
+ def build(headers, options = {})
30
+ validate headers
31
+
32
+ input = parse_field(headers, "signature-input")
33
+ reject_multiple_signatures if input.size > 1 && options[:label].nil?
34
+ label = options[:label] || input.keys.first
35
+
36
+ signature = parse_field(headers, "signature")
37
+ fail_with_signature_not_found label unless signature.key?(label)
38
+
39
+ raw_signature = signature[label].value
40
+
41
+ fail_due_invalid_components unless input[label].value.respond_to?(:each)
42
+
43
+ components = input[label].value.map(&:value)
44
+ parameters = input[label].parameters
45
+
46
+ new(components, raw_signature, label, parameters)
47
+ end
48
+
49
+ private
50
+
51
+ def validate(headers)
52
+ raise Error.new "Cannot build signature: Request headers cannot be null" if headers.nil?
53
+ raise Error.new "Cannot build signature: No request headers found" if headers.empty?
54
+ raise Error.new "Cannot build signature: No \"signature-input\" header found" unless headers.key?("signature-input")
55
+ raise Error.new "Cannot build signature: No \"signature\" header found" unless headers.key?("signature")
56
+ end
57
+
58
+ def reject_multiple_signatures
59
+ raise Error.new "Multiple signatures found but none was selected."
60
+ end
61
+
62
+ def fail_with_signature_not_found(label)
63
+ raise Error.new "Signature label not found: \"#{label}\""
64
+ end
65
+
66
+ def fail_due_invalid_components
67
+ raise Error.new "Unexpected value for covered components."
68
+ end
69
+
70
+ def parse_field(hsh, field_name)
71
+ Message.parse_structured_dictionary(hsh[field_name], field_name)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Linzer
4
+ module Signer
5
+ DEFAULT_LABEL = "sig1"
6
+
7
+ class << self
8
+ include Common
9
+
10
+ def sign(key, message, components, options = {})
11
+ validate key, message, components
12
+
13
+ parameters = populate_parameters(key, options)
14
+ signature_base = signature_base(message, components, parameters)
15
+
16
+ signature = key.sign(signature_base)
17
+ label = options[:label] || DEFAULT_LABEL
18
+
19
+ Linzer::Signature.build(serialize(signature, components, parameters, label))
20
+ end
21
+
22
+ def default_label
23
+ DEFAULT_LABEL
24
+ end
25
+
26
+ private
27
+
28
+ def validate(key, message, components)
29
+ msg = "Message cannot be signed with null %s"
30
+ raise Error.new msg % "value" if message.nil?
31
+ raise Error.new msg % "key" if key.nil?
32
+ raise Error.new msg % "component" if components.nil?
33
+
34
+ validate_components message, components
35
+ end
36
+
37
+ def populate_parameters(key, options)
38
+ parameters = {}
39
+
40
+ parameters[:created] = options[:created] || Time.now.getutc.to_i
41
+
42
+ key_id = options[:keyid] || (key.key_id if key.respond_to?(:key_id))
43
+ parameters[:keyid] = key_id unless key_id.nil?
44
+
45
+ (options.keys - %i[created keyid label]).each { |k| parameters[k] = options[k] }
46
+
47
+ parameters
48
+ end
49
+
50
+ def serialize(signature, components, parameters, label)
51
+ {
52
+ "signature" => Starry.serialize({label => signature}),
53
+ "signature-input" =>
54
+ Starry.serialize({label =>
55
+ Starry::InnerList.new(components, parameters)})
56
+ }
57
+ end
58
+ end
59
+ end
60
+ end
@@ -1,101 +1,42 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Linzer
4
- class Verifier
5
- def initialize(pubkeys = nil)
6
- @pubkeys = Hash(pubkeys)
7
- end
8
-
9
- attr_reader :pubkeys
4
+ module Verifier
5
+ class << self
6
+ include Common
10
7
 
11
- # XXX: probably all this validation can be moved to the Message class
12
- def verify(message)
13
- validate message
8
+ def verify(key, message, signature)
9
+ validate message, key, signature
14
10
 
15
- signature_input = parse_field(message, "signature-input")
16
- signature = parse_field(message, "signature")
11
+ parameters = signature.parameters
12
+ components = signature.components
17
13
 
18
- # XXX: this is a self-imposed limitation, fix later
19
- reject_multiple(signature)
14
+ signature_base = signature_base(message, components, parameters)
20
15
 
21
- choosen_signature = signature.keys[0]
22
- if !signature_input.key?(choosen_signature)
23
- raise Error.new "Signature \"#{choosen_signature}\" is not found."
16
+ verify_or_fail key, signature.value, signature_base
24
17
  end
25
18
 
26
- covered_components = signature_input[choosen_signature].to_a
27
- signature_parameters = signature_input[choosen_signature].parameters
19
+ private
28
20
 
29
- signature_value = signature[choosen_signature].value
30
- # XXX to-do: have a mechanism to inspect components and parameters
21
+ def validate(message, key, signature)
22
+ raise Error.new "Message to verify cannot be null" if message.nil?
23
+ raise Error.new "Key to verify signature cannot be null" if key.nil?
24
+ raise Error.new "Signature to verify cannot be null" if signature.nil?
31
25
 
32
- check_key_presence signature_parameters
33
- check_components message, covered_components
26
+ if !signature.respond_to?(:value) || !signature.respond_to?(:components)
27
+ raise Error.new "Signature is invalid"
28
+ end
34
29
 
35
- signature_base = build_signature_base(message, signature_input)
30
+ raise Error.new "Signature raw value to cannot be null" if signature.value.nil?
31
+ raise Error.new "Components cannot be null" if signature.components.nil?
36
32
 
37
- # XXX to-do: get rid of this hard-coded SHA512 values, support more algs
38
- key = pubkeys[signature_parameters["keyid"]]
39
- if !key.verify_pss("SHA512", signature_value, signature_base, salt_length: :auto, mgf1_hash: "SHA512")
40
- raise Error.new "Failed to verify message: Invalid signature."
33
+ validate_components message, signature.components
41
34
  end
42
35
 
43
- true
44
- end
45
-
46
- private
47
-
48
- def validate(message)
49
- raise Error.new "Message to verify cannot be null" if message.nil?
50
- raise Error.new "Message to verify cannot be empty" if message.empty?
51
- raise Error.new "Message signature cannot be incomplete" unless message.header?("signature-input")
52
- raise Error.new "Message has no signature to verify" unless message.header?("signature")
53
- end
54
-
55
- def parse_field(message, field_name)
56
- Starry.parse_dictionary(message[field_name])
57
- rescue Starry::ParseError => _
58
- raise Error.new "Cannot parse \"#{field_name}\" field. Bailing out!"
59
- end
60
-
61
- def reject_multiple(hsh)
62
- msg = "Messages with more than 1 signatures are not supported"
63
- raise Error.new msg if hsh.keys.size > 1
64
- end
65
-
66
- def check_key_presence(parameters)
67
- msg = "Cannot verify signature. Key not found"
68
-
69
- key_id = parameters["keyid"]
70
- raise Error.new msg if key_id.nil?
71
- msg += ": \"#{key_id}\"" if !key_id.empty?
72
-
73
- raise Error.new msg unless pubkeys.key?(key_id)
74
- end
75
-
76
- def check_components(message, components)
77
- msg = "Cannot verify signature. Missing component in message: "
78
- components
79
- .map(&:value)
80
- .reject { |component| message[component] }
81
- .shift
82
- .tap do |component|
83
- if component
84
- msg += "\"#{component}\""
85
- raise Error.new msg
86
- end
87
- end
88
- end
89
-
90
- def build_signature_base(message, signature_input)
91
- signature_base = +""
92
- signature_params = ""
93
- signature_input.each do |k, l|
94
- signature_params = l.to_s
95
- l.value.each { |c| signature_base << "\"#{c.value}\": #{message[c.value]}\n" }
36
+ def verify_or_fail(key, signature, data)
37
+ return true if key.verify(signature, data)
38
+ raise Error.new "Failed to verify message: Invalid signature."
96
39
  end
97
- signature_base << "\"@signature-params\": #{signature_params}"
98
- signature_base
99
40
  end
100
41
  end
101
42
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Linzer
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/linzer.rb CHANGED
@@ -4,16 +4,30 @@ require "starry"
4
4
  require "openssl"
5
5
 
6
6
  require_relative "linzer/version"
7
+ require_relative "linzer/common"
7
8
  require_relative "linzer/message"
9
+ require_relative "linzer/signature"
10
+ require_relative "linzer/key"
11
+ require_relative "linzer/rsa"
12
+ require_relative "linzer/hmac"
13
+ require_relative "linzer/ed25519"
14
+ require_relative "linzer/ecdsa"
15
+ require_relative "linzer/key/helper"
16
+ require_relative "linzer/signer"
8
17
  require_relative "linzer/verifier"
9
18
 
10
19
  module Linzer
11
20
  class Error < StandardError; end
12
21
 
13
22
  class << self
14
- def verify(pubkeys, message)
15
- Linzer::Verifier.new(pubkeys)
16
- .verify(message)
23
+ include Key::Helper
24
+
25
+ def verify(pubkey, message, signature)
26
+ Linzer::Verifier.verify(pubkey, message, signature)
27
+ end
28
+
29
+ def sign(key, message, components, options = {})
30
+ Linzer::Signer.sign(key, message, components, options)
17
31
  end
18
32
  end
19
33
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: linzer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miguel Landaeta
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-02-18 00:00:00.000000000 Z
11
+ date: 2024-02-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ed25519
@@ -59,7 +59,16 @@ files:
59
59
  - README.md
60
60
  - Rakefile
61
61
  - lib/linzer.rb
62
+ - lib/linzer/common.rb
63
+ - lib/linzer/ecdsa.rb
64
+ - lib/linzer/ed25519.rb
65
+ - lib/linzer/hmac.rb
66
+ - lib/linzer/key.rb
67
+ - lib/linzer/key/helper.rb
62
68
  - lib/linzer/message.rb
69
+ - lib/linzer/rsa.rb
70
+ - lib/linzer/signature.rb
71
+ - lib/linzer/signer.rb
63
72
  - lib/linzer/verifier.rb
64
73
  - lib/linzer/version.rb
65
74
  - linzer.gemspec