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, key:, components: nil, label: nil, params: {}, profile: nil) ⇒ 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

  • key (Linzer::Key)

    The private key to sign with (required)

  • components (Array<String>) (defaults to: nil)

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

  • label (String, nil) (defaults to: nil)

    Optional signature label (defaults to “sig1”)

  • params (Hash) (defaults to: {})

    Additional signature parameters (created, nonce, etc.)

  • profile (Symbol, Linzer::Signature::Profile::Base, nil) (defaults to: nil)

    Optional signing profile

Returns:

  • (Object)

    The original HTTP message with signature headers attached

Raises:

  • (SigningError)

    If signing fails

  • (KeyError)

    If required arguments are missing



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/linzer/helper.rb', line 59

def sign!(request_or_response, key:, components: nil, label: nil, params: {}, profile: nil)
  ctx = Signature::Context.new(
    message:    Message.new(request_or_response),
    key:        key,
    label:      label,
    components: Array(components),
    params:     Hash(params)
  )

  resolved_profile = Signature::Profile.resolve(profile)
  resolved_profile&.apply(ctx)

  signature = Linzer::Signer.sign(
    ctx.key,
    ctx.message,
    ctx.components,
    ctx.params
  )

  ctx.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



116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/linzer/helper.rb', line 116

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