linzer 0.7.9.beta1 → 0.7.9.beta3

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: 1e68e3de6c89b69da86e383a47e365ab275597440ac71ffb6ce261cd10ad9f51
4
- data.tar.gz: d036d8264a8bba7a7d964432e187725da146f228ee81335d3db5c3659d1578ee
3
+ metadata.gz: d39f9d5f600453197afb744fc030afc2325f40d580540f9fbbca2ba81e0275e2
4
+ data.tar.gz: 0ab05a81ade047eb116e0527c8647bce22008a04684b6dae5bdd37a9441b0fa8
5
5
  SHA512:
6
- metadata.gz: b635cf2b46a0235a7e4131719b225df2a8c03b689c175baf4d9d234dcb1e1ad9f2b4ad1b52da19296444e2883064dbf11e69509ebce96695e3ee51fe7aa31f45
7
- data.tar.gz: 729797295ce7a27128046cd6ae9714d0a74e6ba64f3db74e9f4fe48106548fd911c60a2f9442e387a5a04e82c54644294587f5b61e2d3aaa21d6a09aa5e1681e
6
+ metadata.gz: 52adbd0af86067b700fd60e2666675134bf58a8ed611b60087ecd3c74da64b50f83232271e4a8a4c2af6162558fd880598fb15af6cd201f9621ff4cd976bbdc5
7
+ data.tar.gz: b351287249fb050bb06bc08a5312b24bf9876dc555fd7c2ee69edbab15e0b43bec5d0cbc3ab852c1766c2eede159a1bd8c6c2ba963558e308b2ec7e73ad469d5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.7.9.beta3] - 2026-04-27
4
+
5
+ - Enforce `expires` signature parameter validation in Verifier.
6
+
7
+ - Add Faraday middleware and adapters for HTTP message signatures.
8
+
9
+ ## [0.7.9.beta2] - 2026-04-19
10
+
11
+ - Add support for http gem 6.x while maintaining compatibility with 5.x.
12
+ Handles API differences introduced in 6.0.
13
+ - Improve README clarity and align it with current behavior and production usage.
14
+
3
15
  ## [0.7.9.beta1] - 2026-03-03
4
16
 
5
17
  (Beta release to test gem release automation; no functional changes)
data/README.md CHANGED
@@ -9,7 +9,12 @@
9
9
  [rubydoc-badge]: https://img.shields.io/badge/docs-RubyDoc.info-blue
10
10
  [rubydoc-link]: https://www.rubydoc.info/gems/linzer
11
11
 
12
- Linzer is a Ruby library for [HTTP Message Signatures (RFC 9421)](https://www.rfc-editor.org/rfc/rfc9421.html).
12
+ Linzer is a Ruby library for [HTTP Message Signatures (RFC 9421)](https://www.rfc-editor.org/rfc/rfc9421.html),
13
+ allowing you to sign and verify HTTP requests and responses with
14
+ standard-compliant cryptographic signatures.
15
+
16
+ Useful for APIs, webhooks, and services that need to verify request
17
+ authenticity or prevent tampering.
13
18
 
14
19
  ## Install
15
20
 
@@ -23,48 +28,92 @@ Or just `gem install linzer`.
23
28
 
24
29
  ## Usage
25
30
 
26
- ### TL;DR: I just want to protect my application!!
31
+ ### Quick start
32
+
33
+ - To sign/verify HTTP requests and responses, see the
34
+ [Signing Requests](#signing-http-requests-and-responses) and
35
+ [Verifying HTTP signatures](#verifying-http-signatures) or
36
+ [Verifying responses (client-side)](#verifying-responses-client-side)
37
+ sections.
38
+
39
+ - For a more hands-off approach to enforcing request authentication
40
+ with HTTP signatures in Rack applications (such as Rails),
41
+ see the examples in the next section.
42
+
43
+ ### Rack middleware
27
44
 
28
- Add the following middleware to your Rack application and configure it
29
- as needed, e.g.:
45
+ Add the middleware to your Rack application:
30
46
 
31
47
  ```ruby
32
48
  # config.ru
33
- use Rack::Auth::Signature, except: "/login",
34
- default_key: {material: Base64.strict_decode64(ENV["MYAPP_KEY"]), alg: "hmac-sha256"}
35
- # or: default_key: {material: IO.read("app/config/pubkey.pem"), "ed25519"}
49
+ use Rack::Auth::Signature,
50
+ except: "/login",
51
+ default_key: {
52
+ material: Base64.strict_decode64(ENV["MYAPP_KEY"]),
53
+ alg: "hmac-sha256"
54
+ }
55
+ # or using a public/private key pair:
56
+ # default_key: { material: IO.read("app/config/pubkey.pem"), alg: "ed25519" }
36
57
  ```
37
58
 
38
- or on more complex scenarios:
59
+ In this example, the middleware requires a valid HTTP Message Signature
60
+ for all endpoints except /login.
61
+
62
+ #### Using a configuration file
63
+
64
+ For more complex setups, you can load configuration from a file, e.g.:
39
65
 
40
66
  ```ruby
41
67
  # config.ru
42
- use Rack::Auth::Signature, except: "/login",
68
+ use Rack::Auth::Signature,
69
+ except: "/login",
43
70
  config_path: "app/configuration/http-signatures.yml"
44
71
  ```
45
72
 
46
- or with a typical Rails application:
73
+ #### Rails
74
+
75
+ In a Rails application, add the middleware in your configuration:
47
76
 
48
77
  ```ruby
49
78
  # config/application.rb
50
- config.middleware.use Rack::Auth::Signature, except: "/login",
79
+ config.middleware.use Rack::Auth::Signature,
80
+ except: "/login",
51
81
  config_path: "http-signatures.yml"
52
82
  ```
53
83
 
54
- And that's it, all routes in the example app (except `/login`) above will
55
- require a valid signature created with the respective private key held by a
56
- client. For more details on what configuration options are available, take a
57
- look at
58
- [examples/sinatra/http-signatures.yml](https://github.com/nomadium/linzer/tree/master/examples/sinatra/http-signatures.yml) to get started and/or
59
- [lib/rack/auth/signature.rb](https://github.com/nomadium/linzer/tree/master/lib/rack/auth/signature.rb) for full implementation details.
84
+ #### What this does?
85
+
86
+ Once enabled, all protected routes will require a valid signature
87
+ generated by a client using the corresponding private key. Requests
88
+ without a valid signature will be rejected.
89
+
90
+ #### Next steps
91
+
92
+ - See how to [sign HTTP messages](#signing-http-requests-and-responses)
93
+ or [verify HTTP requests and responses](#verifying-responses-client-side).
94
+
95
+ - See a full configuration example:
96
+ [examples/sinatra/http-signatures.yml](https://github.com/nomadium/linzer/tree/master/examples/sinatra/http-signatures.yml)
97
+
98
+ - Browse the Rack middleware implementation for all options:
99
+ [lib/rack/auth/signature.rb](https://github.com/nomadium/linzer/tree/master/lib/rack/auth/signature.rb)
100
+
101
+ - For more specific scenarios and use cases, continue below.
102
+
103
+ ### Signing HTTP requests and responses
60
104
 
61
- To learn about more specific scenarios or use cases, keep reading on below.
105
+ Linzer signs HTTP requests by adding the required `Signature` and
106
+ `Signature-Input` headers based on selected request components (e.g.
107
+ method, path, headers, etc).
62
108
 
63
- ### To sign a HTTP request:
109
+ Choose your client:
64
110
 
65
- There are several options:
111
+ - Use the [http gem](https://github.com/httprb/http) → recommended (simplest)
112
+ - Use Faraday and the provided middleware → also recommended (very simple)
113
+ - Use `Net::HTTP` → lower-level control
114
+ - Use `Linzer::HTTP` → quick experiments / debugging
66
115
 
67
- #### If you are using http gem:
116
+ #### Using [http gem](https://github.com/httprb/http)
68
117
 
69
118
  ```ruby
70
119
  # first require http signatures feature class ready to be used with http gem:
@@ -85,7 +134,36 @@ response.body.to_s
85
134
  => "protected content..."
86
135
  ```
87
136
 
88
- #### If you are using plain old Net::HTTP:
137
+ #### Using Faraday
138
+
139
+ Linzer ships Faraday middleware for signing outbound requests and verifying
140
+ signed responses.
141
+
142
+ To sign requests:
143
+
144
+ ```ruby
145
+ require "linzer/faraday"
146
+
147
+ api_url = "https://example.com/api/service"
148
+ components = %w[@target-uri @authority date cache-control]
149
+ signature_params = {alg: "rsa-pss-sha512", keyid: "test-key-rsa-pss",
150
+ expires: Time.now.to_i + 300}
151
+
152
+ conn = Faraday.new(url: api_url) do |builder|
153
+ builder.headers["Cache-control"] = "no-cache"
154
+ builder.headers["Date"] = Time.now.httpdate
155
+ builder.request :http_signature, key: signing_key,
156
+ components: components,
157
+ params: signature_params
158
+ end
159
+ response = conn.post("/task")
160
+ ```
161
+
162
+ This signs the request automatically before dispatch. In this example,
163
+ `Date` and `Cache-Control` are included in the signature to protect
164
+ freshness-related metadata from modification.
165
+
166
+ #### Using `Net::HTTP` (manual control)
89
167
 
90
168
  ```ruby
91
169
  key = Linzer.generate_ed25519_key
@@ -111,7 +189,7 @@ request["signature-input"]
111
189
  # => "sig1=(\"@method\" \"@request-target\" \"date\" ..."}
112
190
  ```
113
191
 
114
- Then you can submit the signed request with Net::HTTP client:
192
+ Then send the request:
115
193
 
116
194
  ```ruby
117
195
  require "net/http"
@@ -152,10 +230,7 @@ response = http.request(request)
152
230
  # => #<Net::HTTPOK 200 OK readbody=true>
153
231
  ```
154
232
 
155
- #### Or you can also use the simple HTTP client bundled with this library:
156
-
157
- (This client is probably not suitable for production use but could be useful
158
- enough to get started. It's build on top of Net::HTTP.)
233
+ #### Using the built-in client
159
234
 
160
235
  ```ruby
161
236
  key = Linzer.generate_rsa_pss_sha512_key(4096)
@@ -172,13 +247,62 @@ response =
172
247
  => #<Net::HTTPOK 200 OK readbody=true>
173
248
  ```
174
249
 
175
- ### To verify an incoming request on the server side:
250
+ (This client is intended for testing and exploration.
251
+ For production use, prefer a full-featured HTTP client).
176
252
 
177
- The middleware `Rack::Auth::Signature` can be used for this scenario
178
- [as shown above](#tldr-i-just-want-to-protect-my-application).
253
+ #### Signing HTTP responses (server-side)
179
254
 
180
- Or directly in the application controller (or routes), the incoming request can
181
- be verified with the following approach:
255
+ You can sign responses using the same API as for requests, e.g.:
256
+
257
+ ```ruby
258
+ put "/baz" do
259
+ ...
260
+ response
261
+ # => #<Sinatra::Response:0x0000000109ac40b8 ...
262
+ response.headers["x-custom-app-header"] = "..."
263
+ Linzer.sign!(response,
264
+ key: my_key,
265
+ components: %w[@status content-type content-digest x-custom-app-header],
266
+ label: "sig1",
267
+ params: {
268
+ created: Time.now.to_i
269
+ }
270
+ )
271
+ response["signature"]
272
+ # => "sig1=:2TPCzD4l48bg6LMcVXdV9u..."
273
+ response["signature-input"]
274
+ # => "sig1=(\"@status\" \"content-type\" \"content-digest\"..."
275
+ ...
276
+ end
277
+ ```
278
+
279
+ ### Verifying HTTP signatures
280
+
281
+ Linzer verifies incoming requests (or responses) by checking:
282
+
283
+ - the signature is valid for the given key
284
+ - the signed components match the actual request
285
+ - any signature parameters (e.g. created, expires) are valid
286
+
287
+ If verification fails, an exception is raised explaining the reason.
288
+
289
+ #### Recommended: Rack middleware
290
+
291
+ The easiest way to verify incoming requests is via middleware:
292
+
293
+ ```ruby
294
+ use Rack::Auth::Signature, except: "/login"
295
+ ```
296
+
297
+ This automatically:
298
+
299
+ - verifies all incoming requests
300
+ - rejects invalid or unsigned requests
301
+ - integrates cleanly with Rack-based frameworks (Rails, Sinatra, etc.)
302
+
303
+ #### Manual verification (controller / route level)
304
+
305
+ If you need more control, you can verify incoming requests manually:
182
306
 
183
307
  ```ruby
184
308
  post "/foo" do
@@ -190,18 +314,31 @@ post "/foo" do
190
314
  # "PATH_INFO" => "/api",
191
315
  # ...
192
316
 
193
- result = Linzer.verify!(request, key: some_client_key)
317
+ result = Linzer.verify!(request, key: some_client_key) rescue false
194
318
  # => true
195
319
  ...
320
+ # proceed with trusted request
321
+ end
322
+ ```
323
+
324
+ If the signature is missing or invalid, verify! will raise an exception.
325
+
326
+ ```ruby
327
+ head "/bar" do
328
+ begin
329
+ Linzer.verify!(request, key: key)
330
+ rescue Linzer::VerifyError => e
331
+ halt 401, e.message
332
+ end
196
333
  end
197
334
  ```
198
335
 
199
- If the signature is missing or invalid, the verification method will raise an
200
- exception with a message clarifying why the request signature failed verification.
336
+ #### Dynamic key lookup
337
+
338
+ In many cases, the verification key depends on the `keyid` parameter
339
+ provided in the signature.
201
340
 
202
- Also, for additional flexibility on the server side, the method above can take
203
- a block with the `keyid` parameter extracted from the signature (if any) as argument.
204
- This can be useful to retrieve key data from databases/caches on the server side, e.g.:
341
+ You can supply a block to resolve keys dynamically:
205
342
 
206
343
  ```ruby
207
344
  get "/bar" do
@@ -211,14 +348,22 @@ get "/bar" do
211
348
  end
212
349
  # => true
213
350
  ...
351
+ # request is now verified
214
352
  end
215
353
  ```
216
354
 
217
- ### To verify a received response on the client side:
355
+ This is useful when:
356
+
357
+ - you have multiple clients
358
+ - keys are stored in a database or external service
359
+ - keys rotate over time
360
+
361
+ #### Verifying responses (client-side)
218
362
 
219
- It's similar to verifying requests, the same method is used, see example below:
363
+ As expected, signed responses are verified using the same API shown previously:
220
364
 
221
365
  ```ruby
366
+ ...
222
367
  response
223
368
  # => #<Net::HTTPOK 200 OK readbody=true>
224
369
  response.body
@@ -228,43 +373,60 @@ result = Linzer.verify!(response, key: pubkey, no_older_than: 600)
228
373
  # => true
229
374
  ```
230
375
 
231
- ### To sign an outgoing response on the server side:
232
-
233
- Again, the same principle used to sign outgoing requests, the same method is used,
234
- see example below:
376
+ Or if you are using Faraday, response verification can be handled by
377
+ middleware as well:
235
378
 
236
379
  ```ruby
237
- put "/baz" do
238
- ...
239
- response
240
- # => #<Sinatra::Response:0x0000000109ac40b8 ...
241
- response.headers["x-custom-app-header"] = "..."
242
- Linzer.sign!(response,
243
- key: my_key,
244
- components: %w[@status content-type content-digest x-custom-app-header],
245
- label: "sig1",
246
- params: {
247
- created: Time.now.to_i
248
- }
249
- )
250
- response["signature"]
251
- # => "sig1=:2TPCzD4l48bg6LMcVXdV9u..."
252
- response["signature-input"]
253
- # => "sig1=(\"@status\" \"content-type\" \"content-digest\"..."
254
- ...
380
+ require "linzer/faraday"
381
+
382
+ ...
383
+
384
+ conn = Faraday.new(url: api_url) do |builder|
385
+ builder.response :http_signature, key: verify_key
255
386
  end
387
+ response = conn.post("/task")
388
+
389
+ response.env[:http_signature_verified]
390
+ # => true
391
+
392
+ response.env[:http_signature]
393
+ # => #<Linzer::Signature ...>
256
394
  ```
257
395
 
258
- ### What do you do if you want to sign/verify requests and responses with your preferred HTTP ruby library/framework (not using Rack or `Net::HTTP`, for example)?
396
+ After verification, the middleware stores:
397
+
398
+ - `env[:http_signature_verified]` — whether verification succeeded
399
+
400
+ - `env[:http_signature]` — the parsed `Linzer::Signature` object (when valid)
401
+
402
+ Verification failures can optionally raise instead of returning status;
403
+ see middleware options below.
404
+
405
+ ### Using a custom HTTP library
406
+
407
+ If you are using another HTTP library, you may not need a custom Linzer
408
+ adapter at all. Because Linzer integrates with Faraday middleware, you can
409
+ often use Faraday together with the appropriate Faraday adapter for your
410
+ HTTP client of choice.
259
411
 
260
- You can provide an adapter class and then register it with this library.
261
- For guidance on how to implement such adapters, you can consult an
262
- [example adapter for http gem response](https://github.com/nomadium/linzer/blob/master/lib/linzer/message/adapter/http_gem/response.rb)
263
- included with this gem or the ones
264
- [provided out of the box](https://github.com/nomadium/linzer/blob/master/lib/linzer/message/adapter).
412
+ If you need tighter integration or are not using Faraday, you can also
413
+ implement a native Linzer adapter directly.
265
414
 
266
- For how to register a custom adapter and how to verify signatures in a response,
267
- see this example:
415
+ In most cases, implementing an adapter just means mapping your library's
416
+ request/response objects to the small interface Linzer expects, then
417
+ registering it.
418
+
419
+ To do this:
420
+
421
+ - implement a simple adapter for your request/response objects
422
+ - register it with `Linzer::Message`
423
+
424
+ You can use the existing adapters as references:
425
+
426
+ - [HTTP gem response adapter](https://github.com/nomadium/linzer/blob/master/lib/linzer/message/adapter/http_gem/response.rb)
427
+ - [Built-in adapters](https://github.com/nomadium/linzer/blob/master/lib/linzer/message/adapter)
428
+
429
+ Example of how to register an adapter before using a custom HTTP library:
268
430
 
269
431
  ```ruby
270
432
  Linzer::Message.register_adapter(HTTP::Response, Linzer::Message::Adapter::HTTPGem::Response)
@@ -278,89 +440,96 @@ response["signature-input"]
278
440
  result = Linzer.verify!(response, key: my_key)
279
441
  # => true
280
442
  ```
281
- ---
282
443
 
283
- Furthermore, on some low-level scenarios where a user wants or needs additional
284
- control on how the signing and verification routines are performed, Linzer allows
285
- to manipulate instances of internal HTTP messages (requests & responses, see
286
- `Linzer::Message` class and available adapters), signature objects
287
- (`Linzer::Signature`) and how to register additional message adapters for any
288
- HTTP ruby library not supported out of the box by this gem.
444
+ ### Advanced verification
289
445
 
290
- See below for a few examples of these scenarios.
446
+ For low-level control over signing and verification, Linzer
447
+ exposes internal message and signature objects. This allows
448
+ you to work directly with `Linzer::Message` and `Linzer::Signature`,
449
+ or integrate custom HTTP adapters if needed.
291
450
 
292
- #### To verify a valid signature:
451
+ #### Verifying a signature manually
293
452
 
294
453
  ```ruby
295
454
  test_ed25519_key_pub = key.material.public_to_pem
296
- # => "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAK1ZrC4JqC356pRsUiLVJdFZ3dAjo909VfWs1li33MCQ=\n-----END PUBLIC KEY-----\n"
455
+ # => "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAK1ZrC4JqC356pRs..."
297
456
 
298
457
  pubkey = Linzer.new_ed25519_public_key(test_ed25519_key_pub, "some-key-ed25519")
299
458
  # => #<Linzer::Ed25519::Key:0x00000fe19b9384b0
300
459
 
301
460
  message = Linzer::Message.new(request)
302
461
 
303
- signature = Linzer::Signature.build(message.headers)
462
+ signature = Linzer::Signature.build(request.headers)
304
463
 
305
464
  Linzer.verify(pubkey, message, signature)
306
465
  # => true
307
466
  ```
308
467
 
309
- To mitigate the risk of "replay attacks" (i.e. an attacker capturing a message with a valid signature and re-sending it at a later point) applications may want to validate the `created` parameter of the signature. Linzer can do this automatically when given the optional `no_older_than` keyword argument:
310
-
311
- ```ruby
312
- Linzer.verify(pubkey, message, signature, no_older_than: 500)
313
- ```
468
+ #### Preventing replay attacks
314
469
 
315
- `no_older_than` expects a number of seconds, but you can pass anything that to responds to `#to_i`, including an `ActiveSupport::Duration`.
316
- `::verify` will raise if the `created` parameter of the signature is older than the given number of seconds.
470
+ To reduce the risk of replay attacks (e.g. reusing a captured
471
+ valid request), you can validate the `created` timestamp in the signature.
317
472
 
318
- #### What if an invalid signature if verified?
473
+ Linzer supports this via the `no_older_than` option:
319
474
 
320
475
  ```ruby
321
- result = Linzer.verify(pubkey, message, signature)
322
- lib/linzer/verifier.rb:38:in `verify_or_fail': Failed to verify message: Invalid signature. (Linzer::Error)
476
+ Linzer.verify(pubkey, message, signature, no_older_than: 500)
323
477
  ```
324
478
 
325
- #### HTTP responses are also supported
479
+ `no_older_than` expects a number of seconds, but you can pass
480
+ anything that to responds to `#to_i`, including an `ActiveSupport::Duration`.
326
481
 
327
- HTTP responses can also be signed and verified in the same way as requests.
482
+ If the signature is older than the allowed window, verification
483
+ fails with an error.
328
484
 
329
- ```ruby
330
- headers = {
331
- "date" => "Sat, 30 Mar 2024 21:40:13 GMT",
332
- "x-response-custom" => "bar"
333
- }
485
+ ## Supported algorithms
334
486
 
335
- response = Linzer.new_response("request body", 200, headers)
336
- # or just use the response object exposed by your HTTP framework
487
+ Linzer currently supports the following signature algorithms:
337
488
 
338
- message = Linzer::Message.new(response)
339
- fields = %w[@status date x-response-custom]
489
+ - RSASSA-PSS (SHA-512)
490
+ - RSASSA-PKCS1-v1_5 (SHA-256)
491
+ - HMAC-SHA256
492
+ - Ed25519
493
+ - ECDSA (P-256 and P-384 curves).
340
494
 
341
- signature = Linzer.sign(key, message, fields)
495
+ Of the JSON Web Signature (JWS) algorithms mentioned in RFC 9421,
496
+ only Ed25519 is currently supported. Support for additional
497
+ algorithms is planned and should be straightforward to add.
342
498
 
343
- pp signature.to_h
344
- # => {"signature"=>
345
- # "sig1=:tCldwXqbISktyABrmbhszo...",
346
- # "signature-input"=>"sig1=(\"@status\" \"date\" ..."}
499
+ The goal is to support as much of the RFC as possible before the 1.0 release.
347
500
 
348
- ```
349
-
350
- For now, to consult additional details just take a look at source code and/or the unit tests.
501
+ ## Documentation
351
502
 
352
- Please note that is still early days and extensive testing is still ongoing. For now the following algorithms are supported: RSASSA-PSS using SHA-512, RSASSA-PKCS1-v1_5 using SHA-256, HMAC-SHA256, Ed25519 and ECDSA (P-256 and P-384 curves). JSON Web Signature (JWS) algorithms mentioned in the RFC are not supported yet.
503
+ The codebase is well-documented, and the Ruby API documentation is
504
+ available on [Rubydoc](https://www.rubydoc.info/gems/linzer).
353
505
 
354
- I'll be expanding the library to cover more functionality specified in the RFC
355
- in subsequent releases.
506
+ For deeper details or edge cases, the source code and unit tests are also a good reference.
356
507
 
357
508
  ## Ruby version compatibility
358
509
 
359
510
  linzer is built in [Continuous Integration](https://github.com/nomadium/linzer/actions/workflows/main.yml) on Ruby 3.0+.
360
511
 
512
+ > [!NOTE]
513
+ >
514
+ > Ruby 3.0 is supported and tested in CI, but RSA-based signature algorithms
515
+ > (RSA-PSS and RSA PKCS#1 v1.5) may not work correctly due to its older
516
+ > OpenSSL bindings. If you need RSA algorithms, use Ruby 3.1 or later.
517
+ > Ruby 3.0 has been EOL since March 2024 — users are advised to upgrade
518
+ > to a supported Ruby release.
519
+
361
520
  ## Security
362
521
 
363
- 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.
522
+ This gem is provided “as is” without any warranties. It has not
523
+ been independently audited for security vulnerabilities. Users
524
+ are advised to review the code and assess its suitability for their
525
+ use case, particularly in production environments.
526
+
527
+ Despite this, Linzer is already used in production by other projects
528
+ with security-sensitive requirements, including
529
+ [Mastodon](https://github.com/mastodon/mastodon)
530
+ ([since version 4.5.0](https://docs.joinmastodon.org/spec/security/#http-message-signatures)).
531
+ This does not constitute a security guarantee or endorsement,
532
+ but it may be useful context when evaluating adoption.
364
533
 
365
534
  ## Development
366
535
 
data/flake.lock CHANGED
@@ -2,7 +2,9 @@
2
2
  "nodes": {
3
3
  "bundix": {
4
4
  "inputs": {
5
- "nixpkgs": "nixpkgs"
5
+ "nixpkgs": [
6
+ "nixpkgs"
7
+ ]
6
8
  },
7
9
  "locked": {
8
10
  "lastModified": 1762235257,
@@ -20,25 +22,11 @@
20
22
  },
21
23
  "nixpkgs": {
22
24
  "locked": {
23
- "lastModified": 1761880412,
24
- "narHash": "sha256-QoJjGd4NstnyOG4mm4KXF+weBzA2AH/7gn1Pmpfcb0A=",
25
+ "lastModified": 1775710090,
26
+ "narHash": "sha256-ar3rofg+awPB8QXDaFJhJ2jJhu+KqN/PRCXeyuXR76E=",
25
27
  "owner": "NixOS",
26
28
  "repo": "nixpkgs",
27
- "rev": "a7fc11be66bdfb5cdde611ee5ce381c183da8386",
28
- "type": "github"
29
- },
30
- "original": {
31
- "id": "nixpkgs",
32
- "type": "indirect"
33
- }
34
- },
35
- "nixpkgs_2": {
36
- "locked": {
37
- "lastModified": 1770115704,
38
- "narHash": "sha256-KHFT9UWOF2yRPlAnSXQJh6uVcgNcWlFqqiAZ7OVlHNc=",
39
- "owner": "NixOS",
40
- "repo": "nixpkgs",
41
- "rev": "e6eae2ee2110f3d31110d5c222cd395303343b08",
29
+ "rev": "4c1018dae018162ec878d42fec712642d214fdfa",
42
30
  "type": "github"
43
31
  },
44
32
  "original": {
@@ -48,31 +36,19 @@
48
36
  "type": "github"
49
37
  }
50
38
  },
51
- "nixpkgs_3": {
52
- "locked": {
53
- "lastModified": 1678875422,
54
- "narHash": "sha256-T3o6NcQPwXjxJMn2shz86Chch4ljXgZn746c2caGxd8=",
55
- "owner": "NixOS",
56
- "repo": "nixpkgs",
57
- "rev": "126f49a01de5b7e35a43fd43f891ecf6d3a51459",
58
- "type": "github"
59
- },
60
- "original": {
61
- "id": "nixpkgs",
62
- "type": "indirect"
63
- }
64
- },
65
39
  "root": {
66
40
  "inputs": {
67
41
  "bundix": "bundix",
68
- "nixpkgs": "nixpkgs_2",
42
+ "nixpkgs": "nixpkgs",
69
43
  "ruby-nix": "ruby-nix",
70
44
  "systems": "systems"
71
45
  }
72
46
  },
73
47
  "ruby-nix": {
74
48
  "inputs": {
75
- "nixpkgs": "nixpkgs_3"
49
+ "nixpkgs": [
50
+ "nixpkgs"
51
+ ]
76
52
  },
77
53
  "locked": {
78
54
  "lastModified": 1755059052,