linzer 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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