http_signature 1.0.0 → 1.1.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 +4 -4
- data/.github/workflows/push_gem.yml +25 -0
- data/AGENTS.md +8 -0
- data/Gemfile.lock +1 -1
- data/README.md +55 -32
- data/http_signature.gemspec +1 -1
- data/lib/http_signature/rack.rb +1 -3
- data/lib/http_signature/rails.rb +3 -5
- data/lib/http_signature/version.rb +1 -1
- data/lib/http_signature.rb +74 -30
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4d7340510585b31400802574581fc40aa006889f7f43e67e6ac4d5c926ddc3ce
|
|
4
|
+
data.tar.gz: 3364453874b93eb6b37a2ef13208dbb2792b4b50975c872459fea1a1d70a68d7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ba102a57a504be38d46ff962442d273dcee1866e5e49127927f638774dc30a66b92c063edb62670ba09b42b14bb2257772992183b606ef2abc7c1d1340677717
|
|
7
|
+
data.tar.gz: b2ee1d08d85958e663760b999c45a8865e2db4f43920fbf9a77bae78479a5192e939d0cfdab26a6d47dd936c1bb17311b809ac6b8278f92d1c8ced43de957d9d
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
on:
|
|
2
|
+
push:
|
|
3
|
+
tags:
|
|
4
|
+
- 'v*.*.*'
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
push:
|
|
8
|
+
name: Push gem to RubyGems.org
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
|
13
|
+
contents: write # IMPORTANT: this permission is required for `rake release` to push the release tag
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v5
|
|
17
|
+
with:
|
|
18
|
+
persist-credentials: false
|
|
19
|
+
- name: Set up Ruby
|
|
20
|
+
uses: ruby/setup-ruby@v1
|
|
21
|
+
with:
|
|
22
|
+
bundler-cache: true
|
|
23
|
+
ruby-version: '3.4'
|
|
24
|
+
|
|
25
|
+
- uses: rubygems/release-gem@v1
|
data/AGENTS.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# HTTP signature gem
|
|
2
|
+
|
|
3
|
+
This is a Ruby gem implementing the [HTTP Message Signatures RFC 9421 standard](https://www.rfc-editor.org/rfc/rfc9421.txt). Always adhere to the standard
|
|
4
|
+
|
|
5
|
+
## Tests
|
|
6
|
+
- Run all tests: `bundle exec rake test`
|
|
7
|
+
- Run single test file: `bundle exec rake test TEST=test/http_signature_test.rb`
|
|
8
|
+
- Run single test: `bundle exec rake test TEST=test/http_signature_test.rb TESTOPTS="--name=/test_rsa_pss_sha512/"`
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -2,18 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
Create and validate HTTP Message Signatures per [RFC 9421](https://www.rfc-editor.org/rfc/rfc9421) using the `Signature-Input` and `Signature` headers.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
TL;DR: You specify what should be signed in `Signature-Input` with [components](https://www.rfc-editor.org/rfc/rfc9421#name-derived-components) and lowercase headers. And then the signature is in the `Signature` header
|
|
6
|
+
|
|
7
|
+
Example:
|
|
6
8
|
|
|
7
|
-
__NOTE__: RFC 9421 signs components via two headers:
|
|
8
9
|
```
|
|
9
|
-
Signature-Input: sig1=("@method" "@
|
|
10
|
-
Signature: sig1=:
|
|
10
|
+
Signature-Input: sig1=("@method" "@target-uri" "date");created=1767816111;keyid="Test";alg="hmac-sha256"
|
|
11
|
+
Signature: sig1=:7a1ajkE2rOu+gnW3WLZ4ZEcgCm3TfExmypM/giIgdM0=:
|
|
11
12
|
```
|
|
12
13
|
|
|
13
14
|
## Installation
|
|
14
15
|
|
|
15
16
|
```shell
|
|
16
|
-
|
|
17
|
+
bundle add http_signature
|
|
17
18
|
```
|
|
18
19
|
|
|
19
20
|
## Usage
|
|
@@ -22,21 +23,40 @@ gem install http_signature
|
|
|
22
23
|
|
|
23
24
|
`HTTPSignature.create` returns both `Signature-Input` and `Signature` headers that you can include in your request.
|
|
24
25
|
|
|
25
|
-
|
|
26
26
|
```ruby
|
|
27
|
-
headers = {
|
|
27
|
+
headers = { "date" => "Tue, 20 Apr 2021 02:07:55 GMT" }
|
|
28
28
|
|
|
29
29
|
sig_headers = HTTPSignature.create(
|
|
30
|
-
url:
|
|
30
|
+
url: "https://example.com/foo?pet=dog",
|
|
31
31
|
method: :get,
|
|
32
|
+
key_id: "Test",
|
|
33
|
+
key: "secret",
|
|
32
34
|
headers: headers,
|
|
33
|
-
|
|
34
|
-
key: 'secret',
|
|
35
|
-
covered_components: %w[@method @target-uri date],
|
|
35
|
+
components: %w[@method @target-uri date]
|
|
36
36
|
)
|
|
37
37
|
|
|
38
|
-
request[
|
|
39
|
-
request[
|
|
38
|
+
request["Signature-Input"] = sig_headers["Signature-Input"]
|
|
39
|
+
request["Signature"] = sig_headers["Signature"]
|
|
40
|
+
```
|
|
41
|
+
#### All options
|
|
42
|
+
|
|
43
|
+
```ruby
|
|
44
|
+
HTTPSignature.create(
|
|
45
|
+
url: "https://example.com/foo?pet=dog",
|
|
46
|
+
method: :get,
|
|
47
|
+
key_id: "Test",
|
|
48
|
+
key: "secret",
|
|
49
|
+
# Optional arguments
|
|
50
|
+
headers: headers, # Default: {}
|
|
51
|
+
body: "Hello world", # Default: ""
|
|
52
|
+
components: %w[@method @target-uri date], # Default: %w[@method @target-uri content-digest content-type]
|
|
53
|
+
created: Time.now.to_i, # Default: Time.now.to_i
|
|
54
|
+
expires: Time.now.to_i + 600, # Default: nil
|
|
55
|
+
nonce: "1", # Default: nil
|
|
56
|
+
label: "sig1", # Default: "sig1",
|
|
57
|
+
query_string_params: {pet2: "cat"} # Default: {}, you can pass query string params both here and in the `url` param
|
|
58
|
+
algorithm: "hmac-sha512" # Default: "hmac-sha256"
|
|
59
|
+
)
|
|
40
60
|
```
|
|
41
61
|
|
|
42
62
|
|
|
@@ -51,6 +71,9 @@ HTTPSignature.valid?(
|
|
|
51
71
|
headers: headers,
|
|
52
72
|
key: "secret"
|
|
53
73
|
)
|
|
74
|
+
|
|
75
|
+
# Returns true when all is good.
|
|
76
|
+
# Raises `SignatureError` for invalid signatures
|
|
54
77
|
```
|
|
55
78
|
|
|
56
79
|
## Outgoing request examples
|
|
@@ -58,10 +81,10 @@ HTTPSignature.valid?(
|
|
|
58
81
|
### NET::HTTP
|
|
59
82
|
|
|
60
83
|
```ruby
|
|
61
|
-
require
|
|
62
|
-
require
|
|
84
|
+
require "net/http"
|
|
85
|
+
require "http_signature"
|
|
63
86
|
|
|
64
|
-
uri = URI(
|
|
87
|
+
uri = URI("http://example.com/hello")
|
|
65
88
|
|
|
66
89
|
Net::HTTP.start(uri.host, uri.port) do |http|
|
|
67
90
|
request = Net::HTTP::Get.new(uri)
|
|
@@ -70,14 +93,14 @@ Net::HTTP.start(uri.host, uri.port) do |http|
|
|
|
70
93
|
url: request.uri,
|
|
71
94
|
method: request.method,
|
|
72
95
|
headers: request.each_header.map { |k, v| [k, v] }.to_h,
|
|
73
|
-
key:
|
|
74
|
-
key_id:
|
|
75
|
-
algorithm:
|
|
76
|
-
body: request.body
|
|
96
|
+
key: "MYSECRETKEY",
|
|
97
|
+
key_id: "KEY_1",
|
|
98
|
+
algorithm: "hmac-sha256",
|
|
99
|
+
body: request.body || ""
|
|
77
100
|
)
|
|
78
101
|
|
|
79
|
-
request[
|
|
80
|
-
request[
|
|
102
|
+
request["Signature-Input"] = sig_headers["Signature-Input"]
|
|
103
|
+
request["Signature"] = sig_headers["Signature"]
|
|
81
104
|
|
|
82
105
|
response = http.request(request) # Net::HTTPResponse
|
|
83
106
|
end
|
|
@@ -88,18 +111,18 @@ end
|
|
|
88
111
|
As a faraday middleware
|
|
89
112
|
|
|
90
113
|
```ruby
|
|
91
|
-
require
|
|
114
|
+
require "http_signature/faraday"
|
|
92
115
|
|
|
93
|
-
HTTPSignature::Faraday.key =
|
|
94
|
-
HTTPSignature::Faraday.key_id =
|
|
116
|
+
HTTPSignature::Faraday.key = "secret"
|
|
117
|
+
HTTPSignature::Faraday.key_id = "key-1"
|
|
95
118
|
|
|
96
|
-
Faraday.new(
|
|
119
|
+
Faraday.new("http://example.com") do |faraday|
|
|
97
120
|
faraday.use(HTTPSignature::Faraday)
|
|
98
121
|
faraday.adapter(Faraday.default_adapter)
|
|
99
122
|
end
|
|
100
123
|
|
|
101
124
|
# Now this request will contain the `Signature-Input` and `Signature` headers
|
|
102
|
-
response = conn.get(
|
|
125
|
+
response = conn.get("/")
|
|
103
126
|
|
|
104
127
|
# Request looking like:
|
|
105
128
|
# Signature-Input: sig1=("@method" "@authority" "@target-uri" "date");created=...
|
|
@@ -114,14 +137,14 @@ Rack middlewares sits in between your app and the HTTP request and validate the
|
|
|
114
137
|
Here is how it could be used with sinatra:
|
|
115
138
|
|
|
116
139
|
```ruby
|
|
117
|
-
require
|
|
140
|
+
require "http_signature/rack"
|
|
118
141
|
|
|
119
142
|
HTTPSignature.configure do |config|
|
|
120
143
|
config.keys = [
|
|
121
|
-
{id:
|
|
144
|
+
{id: "key-1", value: "MySecureKey"}
|
|
122
145
|
]
|
|
123
146
|
end
|
|
124
|
-
HTTPSignature::Rack.exclude_paths = [
|
|
147
|
+
HTTPSignature::Rack.exclude_paths = ["/", "/hello/*"]
|
|
125
148
|
|
|
126
149
|
use HTTPSignature::Rack
|
|
127
150
|
run MyApp
|
|
@@ -133,7 +156,7 @@ Opt-in per controller/action using a before_action. It responds with `401 Unauth
|
|
|
133
156
|
```ruby
|
|
134
157
|
# app/controllers/api/base_controller.rb
|
|
135
158
|
|
|
136
|
-
require
|
|
159
|
+
require "http_signature/rails"
|
|
137
160
|
|
|
138
161
|
class Api::BaseController < ApplicationController
|
|
139
162
|
include HTTPSignature::Rails::Controller
|
|
@@ -148,7 +171,7 @@ Set the keys in an initializer
|
|
|
148
171
|
|
|
149
172
|
HTTPSignature.configure do |config|
|
|
150
173
|
config.keys = [
|
|
151
|
-
{id:
|
|
174
|
+
{id: "key-1", value: "MySecureKey"}
|
|
152
175
|
]
|
|
153
176
|
end
|
|
154
177
|
```
|
data/http_signature.gemspec
CHANGED
|
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
|
|
|
18
18
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
19
19
|
spec.require_paths = ["lib"]
|
|
20
20
|
|
|
21
|
-
spec.required_ruby_version = ">= 3.
|
|
21
|
+
spec.required_ruby_version = ">= 3.1.0"
|
|
22
22
|
|
|
23
23
|
spec.add_development_dependency "bundler"
|
|
24
24
|
spec.add_development_dependency "rake"
|
data/lib/http_signature/rack.rb
CHANGED
|
@@ -33,7 +33,7 @@ class HTTPSignature::Rack
|
|
|
33
33
|
else
|
|
34
34
|
""
|
|
35
35
|
end
|
|
36
|
-
|
|
36
|
+
HTTPSignature.valid?(
|
|
37
37
|
url: request.url,
|
|
38
38
|
method: request.request_method,
|
|
39
39
|
headers: request_headers,
|
|
@@ -44,8 +44,6 @@ class HTTPSignature::Rack
|
|
|
44
44
|
return [401, {}, ["Invalid signature"]]
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
-
return [401, {}, ["Invalid signature"]] unless valid_signature
|
|
48
|
-
|
|
49
47
|
@app.call(env)
|
|
50
48
|
end
|
|
51
49
|
|
data/lib/http_signature/rails.rb
CHANGED
|
@@ -20,7 +20,7 @@ module HTTPSignature
|
|
|
20
20
|
|
|
21
21
|
request_body = read_request_body
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
HTTPSignature.valid?(
|
|
24
24
|
url: request.url,
|
|
25
25
|
method: request.request_method,
|
|
26
26
|
headers: request_headers,
|
|
@@ -28,10 +28,8 @@ module HTTPSignature
|
|
|
28
28
|
key_resolver: ->(key_id) { HTTPSignature.key(key_id) }
|
|
29
29
|
)
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
render status: :unauthorized, plain: "Invalid signature"
|
|
34
|
-
rescue HTTPSignature::SignatureError, ArgumentError
|
|
31
|
+
nil
|
|
32
|
+
rescue HTTPSignature::SignatureError
|
|
35
33
|
render status: :unauthorized, plain: "Invalid signature"
|
|
36
34
|
end
|
|
37
35
|
|
data/lib/http_signature.rb
CHANGED
|
@@ -11,11 +11,13 @@ module HTTPSignature
|
|
|
11
11
|
Config = Struct.new(:keys)
|
|
12
12
|
DEFAULT_LABEL = "sig1"
|
|
13
13
|
DEFAULT_ALGORITHM = "hmac-sha256"
|
|
14
|
-
DEFAULT_COMPONENTS = %w[@method @
|
|
14
|
+
DEFAULT_COMPONENTS = %w[@method @target-uri].freeze
|
|
15
|
+
DEFAULT_HEADERS = %w[content-digest content-type].freeze
|
|
15
16
|
|
|
16
17
|
class SignatureError < StandardError; end
|
|
17
18
|
class MissingComponent < SignatureError; end
|
|
18
19
|
class UnsupportedAlgorithm < SignatureError; end
|
|
20
|
+
class ExpiredError < SignatureError; end
|
|
19
21
|
|
|
20
22
|
Algorithm = Struct.new(:type, :digest_name, :curve)
|
|
21
23
|
ALGORITHMS = {
|
|
@@ -55,36 +57,52 @@ module HTTPSignature
|
|
|
55
57
|
headers: {},
|
|
56
58
|
body: "",
|
|
57
59
|
algorithm: DEFAULT_ALGORITHM,
|
|
58
|
-
|
|
60
|
+
components: nil,
|
|
59
61
|
created: Time.now.to_i,
|
|
62
|
+
expires: nil,
|
|
60
63
|
nonce: nil,
|
|
61
64
|
label: DEFAULT_LABEL,
|
|
62
65
|
query_string_params: {}
|
|
63
66
|
)
|
|
67
|
+
unless created.is_a?(Integer)
|
|
68
|
+
raise ArgumentError, "created must be a Unix timestamp integer"
|
|
69
|
+
end
|
|
70
|
+
if expires && !expires.is_a?(Integer)
|
|
71
|
+
raise ArgumentError, "expires must be a Unix timestamp integer"
|
|
72
|
+
end
|
|
73
|
+
if expires && created > expires
|
|
74
|
+
raise ArgumentError, "expires (#{expires}) must be greater than created (#{created})"
|
|
75
|
+
end
|
|
64
76
|
algorithm_entry = algorithm_entry_for(algorithm)
|
|
65
77
|
normalized_headers = normalize_headers(headers)
|
|
66
78
|
uri = apply_query_params(URI(url), query_string_params)
|
|
67
79
|
|
|
68
|
-
|
|
80
|
+
components ||=
|
|
81
|
+
default_components(normalized_headers, body:)
|
|
69
82
|
|
|
70
|
-
|
|
71
|
-
|
|
83
|
+
normalized_headers =
|
|
84
|
+
if components.include?("content-digest")
|
|
85
|
+
ensure_content_digest(normalized_headers, body)
|
|
86
|
+
else
|
|
87
|
+
normalized_headers
|
|
88
|
+
end
|
|
72
89
|
|
|
73
90
|
canonical_components = build_components(
|
|
74
|
-
uri
|
|
75
|
-
method
|
|
91
|
+
uri:,
|
|
92
|
+
method:,
|
|
76
93
|
headers: normalized_headers,
|
|
77
|
-
|
|
94
|
+
components:
|
|
78
95
|
)
|
|
79
96
|
|
|
80
97
|
signature_input_header, base_string = build_signature_input(
|
|
81
|
-
label
|
|
82
|
-
components
|
|
83
|
-
created
|
|
84
|
-
|
|
98
|
+
label:,
|
|
99
|
+
components:,
|
|
100
|
+
created:,
|
|
101
|
+
expires:,
|
|
102
|
+
key_id:,
|
|
85
103
|
alg: algorithm,
|
|
86
|
-
nonce
|
|
87
|
-
canonical_components:
|
|
104
|
+
nonce:,
|
|
105
|
+
canonical_components:
|
|
88
106
|
)
|
|
89
107
|
|
|
90
108
|
signature_bytes = sign(base_string, key: key, algorithm: algorithm_entry)
|
|
@@ -98,7 +116,7 @@ module HTTPSignature
|
|
|
98
116
|
|
|
99
117
|
# Verify RFC 9421 Signature headers
|
|
100
118
|
#
|
|
101
|
-
# @return [Boolean]
|
|
119
|
+
# @return [Boolean] true when signature verification succeeds
|
|
102
120
|
def self.valid?(
|
|
103
121
|
url:,
|
|
104
122
|
method:,
|
|
@@ -120,30 +138,42 @@ module HTTPSignature
|
|
|
120
138
|
|
|
121
139
|
algorithm_entry = algorithm_entry_for(parsed_input[:params][:alg] || DEFAULT_ALGORITHM)
|
|
122
140
|
key_id = parsed_input[:params][:keyid]
|
|
141
|
+
created = parsed_input[:params][:created].to_i
|
|
142
|
+
expires = parsed_input[:params][:expires]&.to_i
|
|
143
|
+
now = Time.now.to_i
|
|
144
|
+
if expires && (created > expires || now > expires)
|
|
145
|
+
raise ExpiredError, "Signature expired at #{expires}"
|
|
146
|
+
end
|
|
123
147
|
resolved_key = key || key_resolver&.call(key_id) || key_from_store(key_id)
|
|
124
148
|
raise SignatureError, "Key is required for verification" unless resolved_key
|
|
125
149
|
|
|
126
150
|
uri = apply_query_params(URI(url), query_string_params)
|
|
127
|
-
|
|
151
|
+
if parsed_input[:components].include?("content-digest")
|
|
152
|
+
normalized_headers = ensure_content_digest(normalized_headers, body)
|
|
153
|
+
end
|
|
128
154
|
|
|
129
155
|
canonical_components = build_components(
|
|
130
|
-
uri
|
|
131
|
-
method
|
|
156
|
+
uri:,
|
|
157
|
+
method:,
|
|
132
158
|
headers: normalized_headers,
|
|
133
|
-
|
|
159
|
+
components: parsed_input[:components]
|
|
134
160
|
)
|
|
135
161
|
|
|
136
162
|
_, base_string = build_signature_input(
|
|
137
|
-
label
|
|
163
|
+
label:,
|
|
138
164
|
components: parsed_input[:components],
|
|
139
|
-
created
|
|
140
|
-
|
|
165
|
+
created:,
|
|
166
|
+
expires:,
|
|
167
|
+
key_id:,
|
|
141
168
|
alg: parsed_input[:params][:alg],
|
|
142
169
|
nonce: parsed_input[:params][:nonce],
|
|
143
|
-
canonical_components:
|
|
170
|
+
canonical_components:
|
|
144
171
|
)
|
|
145
172
|
|
|
146
|
-
verify_signature(base_string, parsed_signature, resolved_key, algorithm_entry)
|
|
173
|
+
verified = verify_signature(base_string, parsed_signature, resolved_key, algorithm_entry)
|
|
174
|
+
raise SignatureError, "Invalid signature" unless verified
|
|
175
|
+
|
|
176
|
+
true
|
|
147
177
|
end
|
|
148
178
|
|
|
149
179
|
# -- Private-ish helpers --
|
|
@@ -162,10 +192,19 @@ module HTTPSignature
|
|
|
162
192
|
new_uri
|
|
163
193
|
end
|
|
164
194
|
|
|
165
|
-
def self.default_components(headers)
|
|
195
|
+
def self.default_components(headers, body: nil)
|
|
166
196
|
components = DEFAULT_COMPONENTS.dup
|
|
167
|
-
|
|
168
|
-
|
|
197
|
+
DEFAULT_HEADERS.each do |header|
|
|
198
|
+
include_header =
|
|
199
|
+
if header == "content-digest"
|
|
200
|
+
!body.to_s.empty? || headers[header]
|
|
201
|
+
else
|
|
202
|
+
headers[header]
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
components << header if include_header
|
|
206
|
+
end
|
|
207
|
+
|
|
169
208
|
components
|
|
170
209
|
end
|
|
171
210
|
|
|
@@ -177,8 +216,8 @@ module HTTPSignature
|
|
|
177
216
|
headers.merge("content-digest" => "sha-256=:#{Base64.strict_encode64(digest)}:")
|
|
178
217
|
end
|
|
179
218
|
|
|
180
|
-
def self.build_components(uri:, method:, headers:,
|
|
181
|
-
|
|
219
|
+
def self.build_components(uri:, method:, headers:, components:)
|
|
220
|
+
components.map do |component|
|
|
182
221
|
if component.start_with?("@")
|
|
183
222
|
[component, derived_component(component, uri, method)]
|
|
184
223
|
else
|
|
@@ -219,13 +258,16 @@ module HTTPSignature
|
|
|
219
258
|
label:,
|
|
220
259
|
components:,
|
|
221
260
|
created:,
|
|
261
|
+
expires:,
|
|
222
262
|
key_id:,
|
|
223
263
|
alg:,
|
|
224
264
|
nonce:,
|
|
225
265
|
canonical_components:
|
|
226
266
|
)
|
|
227
267
|
component_tokens = components.map { |c| %("#{escape_structured_string(c)}") }.join(" ")
|
|
228
|
-
params = ["created=#{created}"
|
|
268
|
+
params = ["created=#{created}"]
|
|
269
|
+
params << "expires=#{expires}" unless expires.nil?
|
|
270
|
+
params << %(keyid="#{escape_structured_string(key_id)}")
|
|
229
271
|
params << %(alg="#{escape_structured_string(alg)}") if alg
|
|
230
272
|
params << %(nonce="#{escape_structured_string(nonce)}") if nonce
|
|
231
273
|
|
|
@@ -332,6 +374,8 @@ module HTTPSignature
|
|
|
332
374
|
|
|
333
375
|
encoded = match[1]
|
|
334
376
|
Base64.strict_decode64(encoded)
|
|
377
|
+
rescue ArgumentError
|
|
378
|
+
raise SignatureError, "Invalid signature format"
|
|
335
379
|
end
|
|
336
380
|
|
|
337
381
|
def self.split_header(header)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: http_signature
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Joel Larsson
|
|
@@ -143,9 +143,11 @@ extensions: []
|
|
|
143
143
|
extra_rdoc_files: []
|
|
144
144
|
files:
|
|
145
145
|
- ".github/workflows/ci.yml"
|
|
146
|
+
- ".github/workflows/push_gem.yml"
|
|
146
147
|
- ".github/workflows/standardrb.yml"
|
|
147
148
|
- ".gitignore"
|
|
148
149
|
- ".ruby-version"
|
|
150
|
+
- AGENTS.md
|
|
149
151
|
- Gemfile
|
|
150
152
|
- Gemfile.lock
|
|
151
153
|
- README.md
|
|
@@ -168,7 +170,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
168
170
|
requirements:
|
|
169
171
|
- - ">="
|
|
170
172
|
- !ruby/object:Gem::Version
|
|
171
|
-
version: 3.
|
|
173
|
+
version: 3.1.0
|
|
172
174
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
173
175
|
requirements:
|
|
174
176
|
- - ">="
|