linzer 0.7.2 → 0.7.3

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: 760341bd66c313c36150f7cb31d741c84252ffe726ecd5bc838928b5f656832f
4
- data.tar.gz: 6df1c1af21e5fbadc3eb242dc84973251ddc8dd6849606a2bd213ea57ffd9d89
3
+ metadata.gz: 13262f7f33737c2ad3aec4007e612001dc9d72c97f9a0636681f0ae695d98315
4
+ data.tar.gz: c9663a699455f8cab8cc2214c0af3da835a4e46542f6029dcddece40ad5b9a35
5
5
  SHA512:
6
- metadata.gz: 899a41a945b59b368110befe463d300445d605483ef64020e1e35c12e71e0945f3b6aeb1774155cbc4919246f88fc0b77175fe16d2f4270e4bc57f861983f145
7
- data.tar.gz: 8af02940869d0e451a8c45738f943cb32bdc515347d325121cafbb9c49e6e1e601d897749e2a5f9453f7c1f6a98acff59e778a5489ed70a60476f0086ab4bace
6
+ metadata.gz: f4e7386cf5bb74bd20d2c74bd3fd8054c068ccc1b8704a088296ead0f61c5029cccd51744d974ef1123166a30068790590cafe6c4e3f27c55c67e0637a0073bb
7
+ data.tar.gz: 798032dd1a2c345f51fd23281bd9028a1ff31495f41e8d4c241d653d1857233e1c9c3bb327539c2296a399d0b5df4bbf02b9954e252ba486a022c80f79066785
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.7.3] - 2025-06-01
4
+
5
+ - Fix broken retrieval of header names from Rack responses.
6
+ Previously, this caused signatures attached to Rack response instances
7
+ to use incorrect header names, making them unverifiable.
8
+
9
+ - Add Linzer.signature_base method.
10
+
11
+ - Add initial support for JWS algorithms. See Linzer::JWS module for more details.
12
+ In this initial preview, only EdDSA algorithm (Ed25519) is supported).
13
+
14
+ - Add a simple integration test to verify signatures on HTTP responses.
15
+
3
16
  ## [0.7.2] - 2025-05-21
4
17
 
5
18
  - Add a few integration tests against CloudFlare test server.
data/README.md CHANGED
@@ -356,6 +356,10 @@ in subsequent releases.
356
356
 
357
357
  linzer is built in [Continuous Integration](https://github.com/nomadium/linzer/actions/workflows/main.yml) on Ruby 3.0+.
358
358
 
359
+ ## Security
360
+
361
+ This gem is provided “as is” without any warranties. It has not been audited for security vulnerabilities. Users are advised to review the code and assess its suitability for their use case, particularly in production environments.
362
+
359
363
  ## Development
360
364
 
361
365
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -6,4 +6,4 @@ gem "sinatra", "~> 4.0"
6
6
  gem "rackup", "~> 2.1"
7
7
  gem "webrick", "~> 1.9"
8
8
  gem "rack-contrib", "~> 2.4"
9
- gem "linzer", "~> 0.7.0.beta2"
9
+ gem "linzer", "~> 0.7.2"
data/lib/linzer/common.rb CHANGED
@@ -13,6 +13,9 @@ module Linzer
13
13
  signature_base << "\"@signature-params\": #{signature_params}"
14
14
  signature_base
15
15
  end
16
+ module_function :signature_base
17
+
18
+ private
16
19
 
17
20
  def validate_components(message, components)
18
21
  if components.include?("@signature-params")
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Linzer
4
+ module Helper
5
+ def sign!(request_or_response, **args)
6
+ message = Message.new(request_or_response)
7
+ options = {}
8
+
9
+ label = args[:label]
10
+ options[:label] = label if label
11
+ options.merge!(args.fetch(:params, {}))
12
+
13
+ key = args.fetch(:key)
14
+ signature = Linzer::Signer.sign(key, message, args.fetch(:components), options)
15
+ message.attach!(signature)
16
+ end
17
+
18
+ def verify!(request_or_response, key: nil, no_older_than: 900)
19
+ message = Message.new(request_or_response)
20
+ signature_headers = {}
21
+ %w[signature-input signature].each do |name|
22
+ value = message.header(name)
23
+ signature_headers[name] = value if value
24
+ end
25
+ signature = Signature.build(signature_headers)
26
+ keyid = signature.parameters["keyid"]
27
+ raise Linzer::Error, "key not found" if !key && !keyid
28
+ verify_key = block_given? ? (yield keyid) : key
29
+ Linzer.verify(verify_key, message, signature, no_older_than: no_older_than)
30
+ end
31
+ end
32
+ end
data/lib/linzer/jws.rb ADDED
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jwt"
4
+ require "jwt/eddsa"
5
+ require "ed25519"
6
+
7
+ module Linzer
8
+ module JWS
9
+ def jwk_import(key, params = {})
10
+ material = JWT::JWK.import(key)
11
+ Linzer::JWS::Key.new(material, params)
12
+ end
13
+ module_function :jwk_import
14
+
15
+ def generate_key(algorithm:)
16
+ case String(algorithm)
17
+ when "EdDSA"
18
+ ed25519_keypair = ::Ed25519::SigningKey.generate
19
+ material = JWT::JWK.new(ed25519_keypair)
20
+ Linzer::JWS::Key.new(material)
21
+ else
22
+ err_msg = "Algorithm '#{algorithm}' is unsupported or not implemented yet."
23
+ raise Linzer::Error, err_msg
24
+ end
25
+ end
26
+ module_function :generate_key
27
+
28
+ class Key < Linzer::Key
29
+ def sign(data)
30
+ raise Linzer::SigningError, "Private key is missing!" if !material.private?
31
+ algo = resolve_algorithm
32
+ algo.sign(data: data, signing_key: signing_key)
33
+ end
34
+
35
+ def verify(signature, data)
36
+ algo = resolve_algorithm
37
+ algo.verify(data: data, signature: signature, verification_key: verify_key)
38
+ end
39
+
40
+ private
41
+
42
+ def resolve_algorithm
43
+ case
44
+ when material.verify_key.is_a?(::Ed25519::VerifyKey)
45
+ JWT::JWA.resolve("EdDSA")
46
+ else
47
+ raise Linzer::Error, "Unknown/unsupported algorithm"
48
+ end
49
+ end
50
+
51
+ def verify_key
52
+ material.verify_key
53
+ end
54
+
55
+ def signing_key
56
+ material.signing_key
57
+ end
58
+ end
59
+ end
60
+ end
@@ -85,6 +85,14 @@ module Linzer
85
85
  key = OpenSSL::PKey::EC.new(material)
86
86
  Linzer::ECDSA::Key.new(key, id: key_id, digest: "SHA384")
87
87
  end
88
+
89
+ def generate_jws_key(algorithm:)
90
+ Linzer::JWS.generate_key(algorithm: algorithm)
91
+ end
92
+
93
+ def jwk_import(key, params = {})
94
+ Linzer::JWS.jwk_import(key, params)
95
+ end
88
96
  end
89
97
  end
90
98
  end
@@ -28,8 +28,11 @@ module Linzer
28
28
  raise ArgumentError.new, "Blank header name." if name.empty?
29
29
  name.to_str
30
30
  rescue => ex
31
+ # :nocov:
32
+ # XXX: this block of code seems to be unreachable
31
33
  err_msg = "Invalid header name: '#{name}'"
32
- raise Linzer::Error.new, err_msg, cause: ex
34
+ raise Linzer::Error, err_msg, cause: ex
35
+ # :nocov:
33
36
  end
34
37
 
35
38
  def rack_header_name(field_name)
@@ -44,19 +47,6 @@ module Linzer
44
47
  end
45
48
  end
46
49
 
47
- def rack_request_headers(rack_request)
48
- rack_request
49
- .each_header
50
- .to_h
51
- .select do |k, _|
52
- k.start_with?("HTTP_") || %w[CONTENT_TYPE CONTENT_LENGTH].include?(k)
53
- end
54
- .transform_keys { |k| k.downcase.tr("_", "-") }
55
- .transform_keys do |k|
56
- %w[content-type content-length].include?(k) ? k : k.gsub(/^http-/, "")
57
- end
58
- end
59
-
60
50
  def derived(name)
61
51
  method = DERIVED_COMPONENT[name.value]
62
52
 
@@ -76,7 +66,7 @@ module Linzer
76
66
  else
77
67
  rack_header_name = rack_header_name(name.value.to_s)
78
68
  value = @operation.env[rack_header_name] if request?
79
- value = @operation.get_header(rack_header_name) if response?
69
+ value = @operation.get_header(name.value.to_s) if response?
80
70
  end
81
71
  value.dup&.strip
82
72
  end
@@ -17,12 +17,12 @@ module Linzer
17
17
  end
18
18
 
19
19
  def header(name)
20
- @operation.get_header(rack_header_name(name))
20
+ @operation.get_header(name)
21
21
  end
22
22
 
23
23
  def attach!(signature)
24
24
  signature.to_h.each do |h, v|
25
- @operation.set_header(rack_header_name(h), v)
25
+ @operation.set_header(h, v)
26
26
  end
27
27
  @operation
28
28
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Linzer
4
- VERSION = "0.7.2"
4
+ VERSION = "0.7.3"
5
5
  end
data/lib/linzer.rb CHANGED
@@ -9,6 +9,7 @@ require "net/http"
9
9
 
10
10
  require_relative "linzer/version"
11
11
  require_relative "linzer/common"
12
+ require_relative "linzer/helper"
12
13
  require_relative "linzer/options"
13
14
  require_relative "linzer/message"
14
15
  require_relative "linzer/message/adapter"
@@ -35,6 +36,7 @@ module Linzer
35
36
 
36
37
  class << self
37
38
  include Key::Helper
39
+ include Helper
38
40
 
39
41
  def verify(pubkey, message, signature, no_older_than: nil)
40
42
  Linzer::Verifier.verify(pubkey, message, signature, no_older_than: no_older_than)
@@ -44,31 +46,8 @@ module Linzer
44
46
  Linzer::Signer.sign(key, message, components, options)
45
47
  end
46
48
 
47
- def sign!(request_or_response, **args)
48
- message = Message.new(request_or_response)
49
- options = {}
50
-
51
- label = args[:label]
52
- options[:label] = label if label
53
- options.merge!(args.fetch(:params, {}))
54
-
55
- key = args.fetch(:key)
56
- signature = Linzer::Signer.sign(key, message, args.fetch(:components), options)
57
- message.attach!(signature)
58
- end
59
-
60
- def verify!(request_or_response, key: nil, no_older_than: 900)
61
- message = Message.new(request_or_response)
62
- signature_headers = {}
63
- %w[signature-input signature].each do |name|
64
- value = message.header(name)
65
- signature_headers[name] = value if value
66
- end
67
- signature = Signature.build(signature_headers)
68
- keyid = signature.parameters["keyid"]
69
- raise Linzer::Error, "key not found" if !key && !keyid
70
- verify_key = block_given? ? (yield keyid) : key
71
- Linzer.verify(verify_key, message, signature, no_older_than: no_older_than)
49
+ def signature_base(message, components, parameters)
50
+ Linzer::Common.signature_base(message, components, parameters)
72
51
  end
73
52
  end
74
53
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: linzer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.2
4
+ version: 0.7.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miguel Landaeta
@@ -192,10 +192,12 @@ files:
192
192
  - lib/linzer/common.rb
193
193
  - lib/linzer/ecdsa.rb
194
194
  - lib/linzer/ed25519.rb
195
+ - lib/linzer/helper.rb
195
196
  - lib/linzer/hmac.rb
196
197
  - lib/linzer/http.rb
197
198
  - lib/linzer/http/bootstrap.rb
198
199
  - lib/linzer/http/signature_feature.rb
200
+ - lib/linzer/jws.rb
199
201
  - lib/linzer/key.rb
200
202
  - lib/linzer/key/helper.rb
201
203
  - lib/linzer/message.rb