Module: Linzer::Helper

Included in:
Linzer
Defined in:
lib/linzer/helper.rb

Overview

Note:

These methods are mixed into the Linzer module and can be called directly as Linzer.sign! and Linzer.verify!.

Convenience methods for signing and verifying HTTP messages.

These methods provide a simpler interface for common use cases, handling message wrapping and signature attachment automatically.

Instance Method Summary collapse

Instance Method Details

#sign!(request_or_response, **args) ⇒ Object

Signs an HTTP request or response and attaches the signature.

This is a convenience method that wraps the message, creates a signature, and attaches it to the original HTTP message in one step.

Examples:

Sign a Net::HTTP request

request = Net::HTTP::Post.new(uri)
request["content-type"] = "application/json"
request["date"] = Time.now.httpdate

Linzer.sign!(request,
  key: private_key,
  components: %w[@method @path content-type date]
)
# request now has "signature" and "signature-input" headers

Sign with additional parameters

Linzer.sign!(request,
  key: private_key,
  components: %w[@method @path],
  label: "my-sig",
  params: { nonce: SecureRandom.hex(16), tag: "my-app" }
)

Parameters:

  • request_or_response (Net::HTTPRequest, Net::HTTPResponse, Rack::Request, Rack::Response, HTTP::Request)

    The HTTP message to sign

  • args (Hash)

    Keyword arguments

Options Hash (**args):

  • :key (Linzer::Key)

    The private key to sign with (required)

  • :components (Array<String>)

    The components to include in the signature (required). Example: ‘%w[@method @path content-type]`

  • :label (String)

    Optional signature label (defaults to “sig1”)

  • :params (Hash)

    Additional signature parameters (created, nonce, etc.)

Returns:

  • (Object)

    The original HTTP message with signature headers attached

Raises:

  • (SigningError)

    If signing fails

  • (KeyError)

    If required arguments are missing



49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/linzer/helper.rb', line 49

def sign!(request_or_response, **args)
  message = Message.new(request_or_response)
  options = {}

  label = args[:label]
  options[:label] = label if label
  options.merge!(args.fetch(:params, {}))

  key = args.fetch(:key)
  signature = Linzer::Signer.sign(key, message, args.fetch(:components), options)
  message.attach!(signature)
end

#verify!(request_or_response, key: nil, no_older_than: 900) {|keyid| ... } ⇒ true

Verifies a signed HTTP request or response.

Extracts the signature from the message headers, rebuilds the signature base, and verifies the cryptographic signature.

Examples:

Verify with a known key

Linzer.verify!(request, key: public_key)

Verify with key lookup

Linzer.verify!(request) do |keyid|
  PublicKey.find_by(identifier: keyid).to_linzer_key
end

Verify with custom age limit (5 minutes)

Linzer.verify!(request, key: public_key, no_older_than: 300)

Verify without age checking

Linzer.verify!(request, key: public_key, no_older_than: nil)

Parameters:

  • request_or_response (Net::HTTPRequest, Net::HTTPResponse, Rack::Request, Rack::Response, HTTP::Request, HTTP::Response)

    The signed HTTP message

  • key (Linzer::Key, nil) (defaults to: nil)

    The public key to verify with. If nil, a block must be provided to look up the key.

  • no_older_than (Integer) (defaults to: 900)

    Maximum signature age in seconds. Defaults to 900 (15 minutes). Set to nil to disable age checking.

Yields:

  • (keyid)

    Block to look up the verification key by keyid. Only called if key is nil.

Yield Parameters:

  • keyid (String)

    The key identifier from the signature

Yield Returns:

  • (Linzer::Key)

    The public key to use for verification

Returns:

  • (true)

    Returns true if verification succeeds

Raises:

  • (VerifyError)

    If verification fails

  • (Error)

    If no key is provided and no keyid is in the signature



97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/linzer/helper.rb', line 97

def verify!(request_or_response, key: nil, no_older_than: 900)
  message = Message.new(request_or_response)
  signature_headers = {}
  %w[signature-input signature].each do |name|
    value = message.header(name)
    signature_headers[name] = value if value
  end
  signature = Signature.build(signature_headers)
  keyid = signature.parameters["keyid"]
  raise Linzer::Error, "key not found" if !key && !keyid
  verify_key = block_given? ? (yield keyid) : key
  Linzer.verify(verify_key, message, signature, no_older_than: no_older_than)
end