Class: Audioproxy::Signer

Inherits:
Object
  • Object
show all
Defined in:
lib/audioproxy/signer.rb

Overview

Signature building — the one piece that must stay liftable into a standalone gem. It therefore depends on stdlib and base64 only: no ActiveSupport, no Rails, and nothing else in this gem. Everything it needs arrives through the constructor, so extracting it is a git mv plus a gemspec, not a rewrite.

Byte contract, mirroring the proxy's reference signer:

base64url(HMAC-SHA256(key, salt ‖ rest_of_path))   — unpadded

where key and salt are the decoded binary values and rest_of_path is the exact byte sequence after the signature segment, leading "/" included.

Constant Summary collapse

DIGEST =
"SHA256".freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(key:, salt:) ⇒ Signer

Returns a new instance of Signer.



21
22
23
24
# File 'lib/audioproxy/signer.rb', line 21

def initialize(key:, salt:)
  @key = key
  @salt = salt
end

Instance Attribute Details

#keyObject (readonly)

Returns the value of attribute key.



19
20
21
# File 'lib/audioproxy/signer.rb', line 19

def key
  @key
end

#saltObject (readonly)

Returns the value of attribute salt.



19
20
21
# File 'lib/audioproxy/signer.rb', line 19

def salt
  @salt
end

Instance Method Details

#sign(rest_of_path) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/audioproxy/signer.rb', line 26

def sign(rest_of_path)
  unless rest_of_path.start_with?("/")
    raise ArgumentError, "rest_of_path must begin with '/' to be verifiable at the proxy, got #{rest_of_path.inspect}"
  end

  # .b so a non-ASCII path cannot raise Encoding::CompatibilityError against
  # the binary salt.
  digest = OpenSSL::HMAC.digest(DIGEST, key, salt + rest_of_path.b)

  # Unpadded: the proxy accepts both spellings, but emitting exactly one
  # keeps URLs and CDN cache keys stable.
  Base64.urlsafe_encode64(digest, padding: false)
end