linzer 0.6.2 → 0.6.4
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 +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +9 -0
- data/lib/linzer/signature.rb +20 -6
- data/lib/linzer/verifier.rb +6 -3
- data/lib/linzer/version.rb +1 -1
- data/lib/linzer.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bff7f24f79244ceffa58e5b005b45b2384b34f2d23eda4c0a613060c4f708fa4
|
4
|
+
data.tar.gz: bfee805d74cf0e1fc3d3588ec6eac8a1de5b96a16021198d016f0be9fb48e371
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d62f773a0d20b09bb97987180f251a70b4db5c29eb4ecf2112e486c9169e418085823c813fa68e4063347d421328f171604c3f82bdf1b6ffae0385bfc41fd8ce
|
7
|
+
data.tar.gz: 0e53c41f9485a8028f11f67e368ca0ec60a82f8e795973d258fb96a57cfdcfe085308f4cd2eb0ac55fe8b5ee39c1a4f8d56d3d9d09fa57ee720eda17831e66ae
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.6.4] - 2025-04-04
|
4
|
+
|
5
|
+
- Allow validating the `created` parameter to mitigate the
|
6
|
+
risk of replay attacks.
|
7
|
+
Pull request [#8](https://github.com/nomadium/linzer/pull/8)
|
8
|
+
by [oneiros](https://github.com/oneiros).
|
9
|
+
|
10
|
+
## [0.6.3] - 2025-03-29
|
11
|
+
|
12
|
+
- Parse signature structured fields values as ASCII string.
|
13
|
+
|
3
14
|
## [0.6.2] - 2024-12-10
|
4
15
|
|
5
16
|
- Remove dependency on ed25519 gem. Pull request
|
data/README.md
CHANGED
@@ -117,6 +117,15 @@ Linzer.verify(pubkey, message, signature)
|
|
117
117
|
# => true
|
118
118
|
```
|
119
119
|
|
120
|
+
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:
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
Linzer.verify(pubkey, message, signature, no_older_than: 500)
|
124
|
+
```
|
125
|
+
|
126
|
+
`no_older_than` expects a number of seconds, but you can pass anything that to responds to `#to_i`, including an `ActiveSupport::Duration`.
|
127
|
+
`::verify` will raise if the `created` parameter of the signature is older than the given number of seconds.
|
128
|
+
|
120
129
|
### What if an invalid signature if verified?
|
121
130
|
|
122
131
|
```ruby
|
data/lib/linzer/signature.rb
CHANGED
@@ -14,6 +14,18 @@ module Linzer
|
|
14
14
|
alias_method :components, :metadata
|
15
15
|
alias_method :bytes, :value
|
16
16
|
|
17
|
+
def created
|
18
|
+
Integer(parameters["created"])
|
19
|
+
rescue
|
20
|
+
return nil if parameters["created"].nil?
|
21
|
+
raise Error.new "Signature has a non-integer `created` parameter"
|
22
|
+
end
|
23
|
+
|
24
|
+
def older_than?(seconds)
|
25
|
+
raise Error.new "Signature is missing the `created` parameter" if created.nil?
|
26
|
+
(Time.now.to_i - created) > seconds
|
27
|
+
end
|
28
|
+
|
17
29
|
def to_h
|
18
30
|
{
|
19
31
|
"signature" => Starry.serialize({label => value}),
|
@@ -31,11 +43,11 @@ module Linzer
|
|
31
43
|
headers.transform_keys!(&:downcase)
|
32
44
|
validate headers
|
33
45
|
|
34
|
-
input =
|
46
|
+
input = parse_structured_field(headers, "signature-input")
|
35
47
|
reject_multiple_signatures if input.size > 1 && options[:label].nil?
|
36
48
|
label = options[:label] || input.keys.first
|
37
49
|
|
38
|
-
signature =
|
50
|
+
signature = parse_structured_field(headers, "signature")
|
39
51
|
fail_with_signature_not_found label unless signature.key?(label)
|
40
52
|
|
41
53
|
raw_signature =
|
@@ -44,8 +56,7 @@ module Linzer
|
|
44
56
|
|
45
57
|
fail_due_invalid_components unless input[label].value.respond_to?(:each)
|
46
58
|
|
47
|
-
|
48
|
-
components = input[label].value.map { |c| c.value.encode(ascii) }
|
59
|
+
components = input[label].value.map(&:value)
|
49
60
|
parameters = input[label].parameters
|
50
61
|
|
51
62
|
new(components, raw_signature, label, parameters)
|
@@ -75,8 +86,11 @@ module Linzer
|
|
75
86
|
raise Error.new "Unexpected value for covered components."
|
76
87
|
end
|
77
88
|
|
78
|
-
def
|
79
|
-
|
89
|
+
def parse_structured_field(hsh, field_name)
|
90
|
+
# Serialized Structured Field values for HTTP are ASCII strings.
|
91
|
+
# See: RFC 8941 (https://datatracker.ietf.org/doc/html/rfc8941)
|
92
|
+
value = hsh[field_name].encode(Encoding::US_ASCII)
|
93
|
+
Message.parse_structured_dictionary(value, field_name)
|
80
94
|
end
|
81
95
|
end
|
82
96
|
end
|
data/lib/linzer/verifier.rb
CHANGED
@@ -5,8 +5,8 @@ module Linzer
|
|
5
5
|
class << self
|
6
6
|
include Common
|
7
7
|
|
8
|
-
def verify(key, message, signature)
|
9
|
-
validate message, key, signature
|
8
|
+
def verify(key, message, signature, no_older_than: nil)
|
9
|
+
validate message, key, signature, no_older_than: no_older_than
|
10
10
|
|
11
11
|
parameters = signature.parameters
|
12
12
|
components = signature.components
|
@@ -18,7 +18,7 @@ module Linzer
|
|
18
18
|
|
19
19
|
private
|
20
20
|
|
21
|
-
def validate(message, key, signature)
|
21
|
+
def validate(message, key, signature, no_older_than: nil)
|
22
22
|
raise Error.new "Message to verify cannot be null" if message.nil?
|
23
23
|
raise Error.new "Key to verify signature cannot be null" if key.nil?
|
24
24
|
raise Error.new "Signature to verify cannot be null" if signature.nil?
|
@@ -31,6 +31,9 @@ module Linzer
|
|
31
31
|
raise Error.new "Components cannot be null" if signature.components.nil?
|
32
32
|
|
33
33
|
validate_components message, signature.components
|
34
|
+
|
35
|
+
return unless no_older_than
|
36
|
+
raise Error.new "Signature created more than #{no_older_than} seconds ago" if signature.older_than?(no_older_than.to_i)
|
34
37
|
end
|
35
38
|
|
36
39
|
def verify_or_fail(key, signature, data)
|
data/lib/linzer/version.rb
CHANGED
data/lib/linzer.rb
CHANGED
@@ -32,8 +32,8 @@ module Linzer
|
|
32
32
|
Linzer::Request.build(verb, uri, params, headers)
|
33
33
|
end
|
34
34
|
|
35
|
-
def verify(pubkey, message, signature)
|
36
|
-
Linzer::Verifier.verify(pubkey, message, signature)
|
35
|
+
def verify(pubkey, message, signature, no_older_than: nil)
|
36
|
+
Linzer::Verifier.verify(pubkey, message, signature, no_older_than: no_older_than)
|
37
37
|
end
|
38
38
|
|
39
39
|
def sign(key, message, components, options = {})
|
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.6.
|
4
|
+
version: 0.6.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Miguel Landaeta
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-04-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: openssl
|