Class: Linzer::Signature
- Inherits:
-
Object
- Object
- Linzer::Signature
- Defined in:
- lib/linzer/signature.rb
Overview
Represents an HTTP message signature as defined in RFC 9421.
A Signature encapsulates:
-
The raw signature bytes
-
The covered components (fields included in the signature)
-
The signature parameters (created, keyid, etc.)
-
The signature label (for identifying multiple signatures)
Signatures are immutable once created. Use Signature.build to create instances from HTTP headers, or receive them from Linzer::Signer.sign.
Instance Attribute Summary collapse
-
#label ⇒ Object
readonly
Returns the value of attribute label.
-
#metadata ⇒ Object
(also: #serialized_components)
readonly
Returns the value of attribute metadata.
-
#parameters ⇒ Object
readonly
Returns the value of attribute parameters.
-
#value ⇒ Object
(also: #bytes)
readonly
Returns the value of attribute value.
Class Method Summary collapse
-
.build(headers, options = {}) ⇒ Signature
Builds a Signature from HTTP headers.
Instance Method Summary collapse
-
#components ⇒ Array<String>
Returns the deserialized component identifiers.
-
#created ⇒ Integer?
Returns the signature creation timestamp.
-
#expired? ⇒ Boolean
Checks if the signature has expired based on the ‘expires` parameter.
-
#initialize(metadata, value, label, parameters = {}) ⇒ Signature
constructor
private
Use Signature.build to create Signature instances.
-
#older_than?(seconds) ⇒ Boolean
Checks if the signature is older than a given number of seconds.
-
#to_h ⇒ Hash{String => String}
Converts the signature to HTTP header format.
Constructor Details
#initialize(metadata, value, label, parameters = {}) ⇒ Signature
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Use build to create Signature instances.
30 31 32 33 34 35 36 |
# File 'lib/linzer/signature.rb', line 30 def initialize(, value, label, parameters = {}) @metadata = .clone.freeze @value = value.clone.freeze @parameters = parameters.clone.freeze @label = label.clone.freeze freeze end |
Instance Attribute Details
#label ⇒ Object (readonly)
Returns the value of attribute label.
54 55 56 |
# File 'lib/linzer/signature.rb', line 54 def label @label end |
#metadata ⇒ Object (readonly) Also known as: serialized_components
Returns the value of attribute metadata.
41 42 43 |
# File 'lib/linzer/signature.rb', line 41 def @metadata end |
#parameters ⇒ Object (readonly)
Returns the value of attribute parameters.
50 51 52 |
# File 'lib/linzer/signature.rb', line 50 def parameters @parameters end |
#value ⇒ Object (readonly) Also known as: bytes
Returns the value of attribute value.
45 46 47 |
# File 'lib/linzer/signature.rb', line 45 def value @value end |
Class Method Details
.build(headers, options = {}) ⇒ Signature
Builds a Signature from HTTP headers.
Parses the ‘signature` and `signature-input` headers according to RFC 9421 and RFC 8941 (Structured Field Values).
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/linzer/signature.rb', line 178 def build(headers, = {}) basic_validate headers headers.transform_keys!(&:downcase) validate headers input = parse_structured_field(headers, "signature-input") reject_multiple_signatures if input.size > 1 && [:label].nil? label = [:label] || input.keys.first signature = parse_structured_field(headers, "signature") fail_with_signature_not_found label unless signature.key?(label) raw_signature = signature[label].value .force_encoding(Encoding::ASCII_8BIT) fail_due_invalid_components unless input[label].value.respond_to?(:each) components = input[label].value.map { |c| Starry.serialize_item(c) } parameters = input[label].parameters new(components, raw_signature, label, parameters) end |
Instance Method Details
#components ⇒ Array<String>
Returns the deserialized component identifiers.
Unlike #serialized_components, this returns the components in a more human-readable form.
73 74 75 |
# File 'lib/linzer/signature.rb', line 73 def components FieldId.deserialize_components(serialized_components) end |
#created ⇒ Integer?
Returns the signature creation timestamp.
82 83 84 85 86 87 |
# File 'lib/linzer/signature.rb', line 82 def created Integer(parameters["created"]) rescue return nil if parameters["created"].nil? raise Error.new "Signature has a non-integer `created` parameter" end |
#expired? ⇒ Boolean
Checks if the signature has expired based on the ‘expires` parameter.
If the ‘expires` parameter is not present, the signature is considered not expired (returns false). If the parameter is present but not a valid integer, an error is raised.
118 119 120 121 122 123 |
# File 'lib/linzer/signature.rb', line 118 def expired? return false if !parameters.key?("expires") Time.now.to_i >= Integer(parameters["expires"]) rescue ArgumentError, TypeError raise Error.new "Signature has a non-integer `expires` parameter" end |
#older_than?(seconds) ⇒ Boolean
Checks if the signature is older than a given number of seconds.
This is useful for implementing replay attack protection by rejecting signatures that are too old.
100 101 102 103 |
# File 'lib/linzer/signature.rb', line 100 def older_than?(seconds) raise Error.new "Signature is missing the `created` parameter" if created.nil? (Time.now.to_i - created) > seconds end |
#to_h ⇒ Hash{String => String}
Converts the signature to HTTP header format.
Returns a hash suitable for setting as HTTP headers on a request or response. The hash contains ‘signature` and `signature-input` keys.
134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/linzer/signature.rb', line 134 def to_h { "signature" => Starry.serialize({label => value}), "signature-input" => Starry.serialize({ label => Starry::InnerList.new( serialized_components.map { |c| Starry.parse_item(c) }, parameters ) }) } end |