http_message_signatures 0.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 +7 -0
- data/CHANGELOG.md +14 -0
- data/LICENSE.txt +661 -0
- data/README.md +288 -0
- data/lib/http_message_signatures/cavage/signature_header.rb +112 -0
- data/lib/http_message_signatures/cavage/signer.rb +77 -0
- data/lib/http_message_signatures/cavage/signing_string.rb +56 -0
- data/lib/http_message_signatures/cavage/verifier.rb +127 -0
- data/lib/http_message_signatures/errors.rb +16 -0
- data/lib/http_message_signatures/http_signer.rb +108 -0
- data/lib/http_message_signatures/keys.rb +58 -0
- data/lib/http_message_signatures/rack_middleware.rb +141 -0
- data/lib/http_message_signatures/rfc9421/algorithms.rb +155 -0
- data/lib/http_message_signatures/rfc9421/component_identifier.rb +61 -0
- data/lib/http_message_signatures/rfc9421/component_resolver.rb +148 -0
- data/lib/http_message_signatures/rfc9421/signature_base.rb +61 -0
- data/lib/http_message_signatures/rfc9421/signature_params.rb +117 -0
- data/lib/http_message_signatures/rfc9421/signer.rb +104 -0
- data/lib/http_message_signatures/rfc9421/verifier.rb +115 -0
- data/lib/http_message_signatures/version.rb +3 -0
- data/lib/http_message_signatures.rb +34 -0
- metadata +108 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module HTTPMessageSignatures
|
|
2
|
+
module RFC9421
|
|
3
|
+
# Constructs the signature base string per RFC 9421 Section 2.5.
|
|
4
|
+
#
|
|
5
|
+
# The signature base is an ASCII string containing the canonicalized HTTP
|
|
6
|
+
# message components covered by the signature, ending with the serialized
|
|
7
|
+
# @signature-params line.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# base = SignatureBase.create(
|
|
11
|
+
# signature_params: params,
|
|
12
|
+
# method: "POST",
|
|
13
|
+
# url: "https://example.com/foo",
|
|
14
|
+
# headers: { "content-type" => "application/json" }
|
|
15
|
+
# )
|
|
16
|
+
class SignatureBase
|
|
17
|
+
# Build the signature base string.
|
|
18
|
+
#
|
|
19
|
+
# @param signature_params [SignatureParams] The signature parameters
|
|
20
|
+
# @param headers [Hash{String => String}] HTTP headers
|
|
21
|
+
# @param method [String, nil] HTTP method
|
|
22
|
+
# @param status [Integer, nil] HTTP response status code
|
|
23
|
+
# @param url [String, URI, nil] Full request URL
|
|
24
|
+
# @return [String] The signature base string
|
|
25
|
+
# @raise [ParseError] If any component cannot be resolved
|
|
26
|
+
def self.create signature_params:, headers: {}, method: nil, status: nil, url: nil
|
|
27
|
+
resolver = ComponentResolver.new headers: headers, method: method, status: status, url: url
|
|
28
|
+
|
|
29
|
+
seen = {}
|
|
30
|
+
lines = []
|
|
31
|
+
|
|
32
|
+
signature_params.components.each do |component|
|
|
33
|
+
identifier = serialize_identifier component
|
|
34
|
+
|
|
35
|
+
raise ParseError, "Duplicate component identifier: #{identifier}" if seen[identifier]
|
|
36
|
+
|
|
37
|
+
seen[identifier] = true
|
|
38
|
+
|
|
39
|
+
value = resolver.resolve component
|
|
40
|
+
lines << "#{identifier}: #{value}"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
lines << "\"@signature-params\": #{signature_params}"
|
|
44
|
+
lines.join "\n"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class << self
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def serialize_identifier component
|
|
51
|
+
case component
|
|
52
|
+
when ComponentIdentifier
|
|
53
|
+
component.to_s
|
|
54
|
+
else
|
|
55
|
+
"\"#{component}\""
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
require 'starry'
|
|
2
|
+
|
|
3
|
+
module HTTPMessageSignatures
|
|
4
|
+
module RFC9421
|
|
5
|
+
# Represents RFC 9421 signature parameters: the covered components
|
|
6
|
+
# and metadata (created, expires, nonce, alg, keyid, tag).
|
|
7
|
+
#
|
|
8
|
+
# The serialized form is a Structured Fields Inner List with parameters,
|
|
9
|
+
# as defined in RFC 9421 Section 2.3.
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# params = SignatureParams.new(
|
|
13
|
+
# components: ["@method", "@authority", "@path"],
|
|
14
|
+
# created: 1618884473,
|
|
15
|
+
# keyid: "test-key-rsa-pss"
|
|
16
|
+
# )
|
|
17
|
+
# params.to_s
|
|
18
|
+
# # => '("@method" "@authority" "@path");created=1618884473;keyid="test-key-rsa-pss"'
|
|
19
|
+
class SignatureParams
|
|
20
|
+
METADATA_KEYS = %i[created expires nonce alg keyid tag].freeze
|
|
21
|
+
|
|
22
|
+
attr_reader :components
|
|
23
|
+
attr_accessor :created, :expires, :nonce, :alg, :keyid, :tag
|
|
24
|
+
|
|
25
|
+
def initialize components:, **metadata
|
|
26
|
+
@components = components
|
|
27
|
+
METADATA_KEYS.each { |key| instance_variable_set :"@#{key}", metadata[key] }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Parse a Signature-Input member value (an Inner List with parameters).
|
|
31
|
+
#
|
|
32
|
+
# @param input [String] e.g. '("@method" "@authority");created=1618884473;keyid="test-key"'
|
|
33
|
+
# @return [SignatureParams]
|
|
34
|
+
# @raise [ParseError]
|
|
35
|
+
def self.parse input
|
|
36
|
+
list = Starry.parse_list input
|
|
37
|
+
inner_list = list.first
|
|
38
|
+
|
|
39
|
+
raise ParseError, "Expected inner list, got #{inner_list.class}" unless inner_list.is_a? Starry::InnerList
|
|
40
|
+
|
|
41
|
+
from_inner_list inner_list
|
|
42
|
+
rescue Starry::ParseError => e
|
|
43
|
+
raise ParseError, e.message
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Parse a full Signature-Input header (Dictionary of labeled inner lists).
|
|
47
|
+
#
|
|
48
|
+
# @param input [String] e.g. 'sig1=("@method");created=123, sig2=("@authority");created=456'
|
|
49
|
+
# @return [Hash{String => SignatureParams}]
|
|
50
|
+
# @raise [ParseError]
|
|
51
|
+
def self.parse_dictionary input
|
|
52
|
+
dict = Starry.parse_dictionary input
|
|
53
|
+
dict.transform_values { |inner_list| from_inner_list inner_list }
|
|
54
|
+
rescue Starry::ParseError => e
|
|
55
|
+
raise ParseError, e.message
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Serialize to the Signature-Input / @signature-params format.
|
|
59
|
+
#
|
|
60
|
+
# @return [String]
|
|
61
|
+
def to_s
|
|
62
|
+
to_inner_list.to_s
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Convert to a Starry::InnerList for use in dictionary serialization.
|
|
66
|
+
#
|
|
67
|
+
# @return [Starry::InnerList]
|
|
68
|
+
def to_inner_list
|
|
69
|
+
items = @components.map do |c|
|
|
70
|
+
case c
|
|
71
|
+
when ComponentIdentifier
|
|
72
|
+
Starry::Item.new c.name, c.params
|
|
73
|
+
else
|
|
74
|
+
c.to_s
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
params = {}
|
|
79
|
+
METADATA_KEYS.each do |key|
|
|
80
|
+
value = instance_variable_get :"@#{key}"
|
|
81
|
+
params[key.to_s] = value if value
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
Starry::InnerList.new items, params
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
class << self
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
def from_inner_list inner_list
|
|
91
|
+
raise ParseError, "Expected inner list, got #{inner_list.class}" unless inner_list.is_a? Starry::InnerList
|
|
92
|
+
|
|
93
|
+
components = inner_list.map { |item| item_to_component item }
|
|
94
|
+
metadata = extract_metadata inner_list.parameters
|
|
95
|
+
|
|
96
|
+
new(components: components, **metadata)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def item_to_component item
|
|
100
|
+
return item.to_s unless item.is_a? Starry::Item
|
|
101
|
+
return item.value.to_s if item.parameters.empty?
|
|
102
|
+
|
|
103
|
+
ComponentIdentifier.new item.value.to_s, item.parameters
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def extract_metadata parameters
|
|
107
|
+
metadata = {}
|
|
108
|
+
parameters.each do |key, value|
|
|
109
|
+
sym = key.to_sym
|
|
110
|
+
metadata[sym] = value if METADATA_KEYS.include? sym
|
|
111
|
+
end
|
|
112
|
+
metadata
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
require 'base64'
|
|
2
|
+
|
|
3
|
+
module HTTPMessageSignatures
|
|
4
|
+
module RFC9421
|
|
5
|
+
# Signs HTTP messages per RFC 9421.
|
|
6
|
+
#
|
|
7
|
+
# @example
|
|
8
|
+
# signer = Signer.new(
|
|
9
|
+
# key: private_key,
|
|
10
|
+
# key_id: "my-key",
|
|
11
|
+
# algorithm: "ed25519",
|
|
12
|
+
# covered_components: %w[@method @authority @path content-type]
|
|
13
|
+
# )
|
|
14
|
+
# headers = signer.sign(
|
|
15
|
+
# method: "POST",
|
|
16
|
+
# url: "https://example.com/inbox",
|
|
17
|
+
# headers: { "Content-Type" => "application/json" }
|
|
18
|
+
# )
|
|
19
|
+
class Signer
|
|
20
|
+
# @param algorithm [String] Algorithm name (e.g. "ed25519", "rsa-pss-sha512")
|
|
21
|
+
# @param covered_components [Array<String, ComponentIdentifier>] Components to sign
|
|
22
|
+
# @param key [OpenSSL::PKey, String] Private key or shared secret
|
|
23
|
+
# @param key_id [String] Key identifier for the keyid parameter
|
|
24
|
+
# @param expires_in [Integer, nil] Seconds until expiration
|
|
25
|
+
# @param include_alg [Boolean] Include alg parameter (default: true)
|
|
26
|
+
# @param label [String] Signature label (default: "sig1")
|
|
27
|
+
# @param nonce [String, nil] Random nonce value
|
|
28
|
+
# @param tag [String, nil] Application-specific tag
|
|
29
|
+
def initialize algorithm:, covered_components:, key:, key_id:,
|
|
30
|
+
expires_in: nil, include_alg: true, label: 'sig1', nonce: nil, tag: nil
|
|
31
|
+
@algorithm = Algorithms.resolve algorithm
|
|
32
|
+
@alg_name = algorithm
|
|
33
|
+
@components = covered_components
|
|
34
|
+
@key = key
|
|
35
|
+
@key_id = key_id
|
|
36
|
+
|
|
37
|
+
@expires_in = expires_in
|
|
38
|
+
@include_alg = include_alg
|
|
39
|
+
@label = label
|
|
40
|
+
@nonce = nonce
|
|
41
|
+
@tag = tag
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Sign an HTTP request.
|
|
45
|
+
#
|
|
46
|
+
# @return [Hash{String => String}] Headers with Signature-Input and Signature added
|
|
47
|
+
def sign method:, url:, headers: {}
|
|
48
|
+
sign_message headers: headers, method: method, url: url
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Sign an HTTP response.
|
|
52
|
+
#
|
|
53
|
+
# @return [Hash{String => String}] Headers with Signature-Input and Signature added
|
|
54
|
+
def sign_response status:, headers: {}
|
|
55
|
+
sign_message headers: headers, status: status
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def sign_message headers: {}, method: nil, status: nil, url: nil
|
|
61
|
+
params = build_params
|
|
62
|
+
base = build_base params, headers: headers, method: method, status: status, url: url
|
|
63
|
+
|
|
64
|
+
signature_bytes = @algorithm.sign @key, base
|
|
65
|
+
attach_headers headers, params, signature_bytes
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def build_params
|
|
69
|
+
metadata = { created: Time.now.to_i, keyid: @key_id }
|
|
70
|
+
metadata[:alg] = @alg_name if @include_alg
|
|
71
|
+
metadata[:expires] = metadata[:created] + @expires_in if @expires_in
|
|
72
|
+
metadata[:nonce] = @nonce if @nonce
|
|
73
|
+
metadata[:tag] = @tag if @tag
|
|
74
|
+
|
|
75
|
+
SignatureParams.new components: @components, **metadata
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def build_base params, headers: {}, method: nil, status: nil, url: nil
|
|
79
|
+
SignatureBase.create(
|
|
80
|
+
signature_params: params,
|
|
81
|
+
headers: headers,
|
|
82
|
+
method: method,
|
|
83
|
+
status: status,
|
|
84
|
+
url: url
|
|
85
|
+
)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def attach_headers headers, params, signature_bytes
|
|
89
|
+
encoded = Base64.strict_encode64 signature_bytes
|
|
90
|
+
new_input = "#{@label}=#{params}"
|
|
91
|
+
new_sig = "#{@label}=:#{encoded}:"
|
|
92
|
+
|
|
93
|
+
result = headers.dup
|
|
94
|
+
result['Signature-Input'] = append_dictionary_member result['Signature-Input'], new_input
|
|
95
|
+
result['Signature'] = append_dictionary_member result['Signature'], new_sig
|
|
96
|
+
result
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def append_dictionary_member existing, new_member
|
|
100
|
+
existing ? "#{existing}, #{new_member}" : new_member
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
require 'base64'
|
|
2
|
+
require 'starry'
|
|
3
|
+
|
|
4
|
+
module HTTPMessageSignatures
|
|
5
|
+
module RFC9421
|
|
6
|
+
# Verifies HTTP message signatures per RFC 9421.
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
# verifier = Verifier.new
|
|
10
|
+
# result = verifier.verify(
|
|
11
|
+
# method: "POST",
|
|
12
|
+
# url: "https://example.com/inbox",
|
|
13
|
+
# headers: headers
|
|
14
|
+
# ) { |key_id, algorithm| fetch_key(key_id) }
|
|
15
|
+
class Verifier
|
|
16
|
+
# Verification result with structured data.
|
|
17
|
+
Result = Struct.new :verified, :label, :key_id, :algorithm, :components, keyword_init: true
|
|
18
|
+
|
|
19
|
+
# Verify a signed HTTP request.
|
|
20
|
+
#
|
|
21
|
+
# @yield [key_id, algorithm] Block to resolve the public key or shared secret
|
|
22
|
+
# @return [Result]
|
|
23
|
+
# @raise [ParseError] If headers are missing or malformed
|
|
24
|
+
def verify headers:, method:, url:, label: nil, &key_resolver
|
|
25
|
+
verify_message headers: headers, method: method, url: url, label: label, &key_resolver
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Verify a signed HTTP response.
|
|
29
|
+
#
|
|
30
|
+
# @yield [key_id, algorithm] Block to resolve the public key
|
|
31
|
+
# @return [Result]
|
|
32
|
+
def verify_response headers:, status:, label: nil, &key_resolver
|
|
33
|
+
verify_message headers: headers, status: status, label: label, &key_resolver
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# List all signature labels present in the headers.
|
|
37
|
+
#
|
|
38
|
+
# @param headers [Hash{String => String}] HTTP headers
|
|
39
|
+
# @return [Array<String>] Signature labels
|
|
40
|
+
def labels headers
|
|
41
|
+
normalized = headers.transform_keys(&:downcase)
|
|
42
|
+
sig_input_raw = normalized['signature-input']
|
|
43
|
+
return [] unless sig_input_raw
|
|
44
|
+
|
|
45
|
+
SignatureParams.parse_dictionary(sig_input_raw).keys
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Verify all signatures on a request.
|
|
49
|
+
#
|
|
50
|
+
# @yield [key_id, algorithm] Block to resolve keys
|
|
51
|
+
# @return [Array<Result>]
|
|
52
|
+
def verify_all headers:, method:, url:, &key_resolver
|
|
53
|
+
labels(headers).map do |label|
|
|
54
|
+
verify headers: headers, method: method, url: url, label: label, &key_resolver
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def verify_message headers: {}, label: nil, method: nil, status: nil, url: nil
|
|
61
|
+
params, signature_bytes, label = parse_signature_headers headers, label
|
|
62
|
+
|
|
63
|
+
key = yield params.keyid, params.alg
|
|
64
|
+
alg = Algorithms.resolve params.alg
|
|
65
|
+
|
|
66
|
+
base = SignatureBase.create(
|
|
67
|
+
signature_params: params,
|
|
68
|
+
headers: headers,
|
|
69
|
+
method: method,
|
|
70
|
+
status: status,
|
|
71
|
+
url: url
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
verified = alg.verify key, signature_bytes, base
|
|
75
|
+
|
|
76
|
+
Result.new verified: verified, label: label, key_id: params.keyid,
|
|
77
|
+
algorithm: params.alg, components: params.components
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def parse_signature_headers headers, label
|
|
81
|
+
normalized = headers.transform_keys(&:downcase)
|
|
82
|
+
|
|
83
|
+
sig_input_raw = normalized['signature-input']
|
|
84
|
+
sig_raw = normalized['signature']
|
|
85
|
+
raise ParseError, 'Missing Signature-Input header' unless sig_input_raw
|
|
86
|
+
raise ParseError, 'Missing Signature header' unless sig_raw
|
|
87
|
+
|
|
88
|
+
sig_input_dict = SignatureParams.parse_dictionary sig_input_raw
|
|
89
|
+
sig_dict = Starry.parse_dictionary sig_raw
|
|
90
|
+
|
|
91
|
+
label ||= sig_input_dict.keys.first
|
|
92
|
+
raise ParseError, "No signature found with label: #{label}" unless sig_input_dict.key? label
|
|
93
|
+
|
|
94
|
+
params = sig_input_dict[label]
|
|
95
|
+
signature_bytes = extract_signature_bytes sig_dict, sig_raw, label
|
|
96
|
+
|
|
97
|
+
[params, signature_bytes, label]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def extract_signature_bytes sig_dict, sig_raw, label
|
|
101
|
+
sig_entry = sig_dict[label]
|
|
102
|
+
raise ParseError, "No signature value for label: #{label}" unless sig_entry
|
|
103
|
+
|
|
104
|
+
sig_value = sig_entry.is_a?(Starry::Item) ? sig_entry.value : sig_entry
|
|
105
|
+
return sig_value if sig_value.is_a?(String) && sig_value.encoding == Encoding::ASCII_8BIT
|
|
106
|
+
|
|
107
|
+
# Fallback: manually extract base64 from the raw header
|
|
108
|
+
pattern = %r{#{Regexp.escape(label)}=:([A-Za-z0-9+/=]+):}
|
|
109
|
+
raise ParseError, "Cannot extract signature bytes for label: #{label}" unless sig_raw =~ pattern
|
|
110
|
+
|
|
111
|
+
Base64.strict_decode64 ::Regexp.last_match(1)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'starry'
|
|
2
|
+
require 'strscan'
|
|
3
|
+
|
|
4
|
+
require_relative 'http_message_signatures/version'
|
|
5
|
+
require_relative 'http_message_signatures/errors'
|
|
6
|
+
require_relative 'http_message_signatures/keys'
|
|
7
|
+
require_relative 'http_message_signatures/cavage/signature_header'
|
|
8
|
+
require_relative 'http_message_signatures/cavage/signing_string'
|
|
9
|
+
require_relative 'http_message_signatures/cavage/signer'
|
|
10
|
+
require_relative 'http_message_signatures/cavage/verifier'
|
|
11
|
+
require_relative 'http_message_signatures/rfc9421/component_identifier'
|
|
12
|
+
require_relative 'http_message_signatures/rfc9421/component_resolver'
|
|
13
|
+
require_relative 'http_message_signatures/rfc9421/signature_params'
|
|
14
|
+
require_relative 'http_message_signatures/rfc9421/signature_base'
|
|
15
|
+
require_relative 'http_message_signatures/rfc9421/algorithms'
|
|
16
|
+
require_relative 'http_message_signatures/rfc9421/signer'
|
|
17
|
+
require_relative 'http_message_signatures/rfc9421/verifier'
|
|
18
|
+
require_relative 'http_message_signatures/rack_middleware'
|
|
19
|
+
require_relative 'http_message_signatures/http_signer'
|
|
20
|
+
|
|
21
|
+
# HTTP Message Signatures for the Fediverse
|
|
22
|
+
#
|
|
23
|
+
# Sign and verify HTTP messages using draft-cavage (legacy Fediverse standard)
|
|
24
|
+
# and RFC 9421 (the final HTTP Message Signatures standard).
|
|
25
|
+
#
|
|
26
|
+
# @example Signing a request (draft-cavage)
|
|
27
|
+
# signer = HTTPMessageSignatures::CavageSigner.new(key_id: 'https://example.com/actor#main-key', private_key: key)
|
|
28
|
+
# headers = signer.sign(method: 'POST', url: 'https://remote.example/inbox', headers: headers, body: body)
|
|
29
|
+
#
|
|
30
|
+
# @example Verifying a request (draft-cavage)
|
|
31
|
+
# verifier = HTTPMessageSignatures::CavageVerifier.new
|
|
32
|
+
# result = verifier.verify(method: 'POST', url: '/inbox', headers: headers, body: body) { |key_id| fetch_key(key_id) }
|
|
33
|
+
module HTTPMessageSignatures
|
|
34
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: http_message_signatures
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Shane Becker
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: base64
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.3'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.3'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rack
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '2.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '2.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: starry
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0.2'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0.2'
|
|
54
|
+
description: |
|
|
55
|
+
Sign and verify HTTP messages using draft-cavage (legacy Fediverse standard)
|
|
56
|
+
and RFC 9421 (the final HTTP Message Signatures standard).
|
|
57
|
+
Supports RSA-SHA256, RSA-PSS, HMAC-SHA256, ECDSA, and Ed25519.
|
|
58
|
+
email:
|
|
59
|
+
- veganstraightedge@gmail.com
|
|
60
|
+
executables: []
|
|
61
|
+
extensions: []
|
|
62
|
+
extra_rdoc_files: []
|
|
63
|
+
files:
|
|
64
|
+
- CHANGELOG.md
|
|
65
|
+
- LICENSE.txt
|
|
66
|
+
- README.md
|
|
67
|
+
- lib/http_message_signatures.rb
|
|
68
|
+
- lib/http_message_signatures/cavage/signature_header.rb
|
|
69
|
+
- lib/http_message_signatures/cavage/signer.rb
|
|
70
|
+
- lib/http_message_signatures/cavage/signing_string.rb
|
|
71
|
+
- lib/http_message_signatures/cavage/verifier.rb
|
|
72
|
+
- lib/http_message_signatures/errors.rb
|
|
73
|
+
- lib/http_message_signatures/http_signer.rb
|
|
74
|
+
- lib/http_message_signatures/keys.rb
|
|
75
|
+
- lib/http_message_signatures/rack_middleware.rb
|
|
76
|
+
- lib/http_message_signatures/rfc9421/algorithms.rb
|
|
77
|
+
- lib/http_message_signatures/rfc9421/component_identifier.rb
|
|
78
|
+
- lib/http_message_signatures/rfc9421/component_resolver.rb
|
|
79
|
+
- lib/http_message_signatures/rfc9421/signature_base.rb
|
|
80
|
+
- lib/http_message_signatures/rfc9421/signature_params.rb
|
|
81
|
+
- lib/http_message_signatures/rfc9421/signer.rb
|
|
82
|
+
- lib/http_message_signatures/rfc9421/verifier.rb
|
|
83
|
+
- lib/http_message_signatures/version.rb
|
|
84
|
+
homepage: https://github.com/xoengineering/http_message_signatures
|
|
85
|
+
licenses:
|
|
86
|
+
- AGPL-3.0
|
|
87
|
+
metadata:
|
|
88
|
+
source_code_uri: https://github.com/xoengineering/http_message_signatures
|
|
89
|
+
changelog_uri: https://github.com/xoengineering/http_message_signatures/blob/main/CHANGELOG.md
|
|
90
|
+
rubygems_mfa_required: 'true'
|
|
91
|
+
rdoc_options: []
|
|
92
|
+
require_paths:
|
|
93
|
+
- lib
|
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
95
|
+
requirements:
|
|
96
|
+
- - ">="
|
|
97
|
+
- !ruby/object:Gem::Version
|
|
98
|
+
version: 4.0.0
|
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
requirements: []
|
|
105
|
+
rubygems_version: 4.0.3
|
|
106
|
+
specification_version: 4
|
|
107
|
+
summary: HTTP Message Signatures for the Fediverse
|
|
108
|
+
test_files: []
|