module Faria::Launchpad::Packet

Constants

VERSION

Public Class Methods

add_api_url(packet, url) click to toggle source
# File lib/faria/launchpad/packet.rb, line 85
def self.add_api_url(packet, url)
  packet["api_url"] = Addressable::URI.parse(url).normalize.to_s
  packet
end
add_expires(packet, expires_in) click to toggle source
# File lib/faria/launchpad/packet.rb, line 95
def self.add_expires(packet, expires_in)
  packet[:exp] = Time.now.utc.to_i + expires_in
  packet
end
add_issued_at(packet) click to toggle source
# File lib/faria/launchpad/packet.rb, line 90
def self.add_issued_at(packet)
  packet[:iat] = Time.now.utc.to_i
  packet
end
add_issuer(packet, issuer) click to toggle source
# File lib/faria/launchpad/packet.rb, line 80
def self.add_issuer(packet, issuer)
  packet[:iss] = issuer
  packet
end
add_source(packet, source) click to toggle source
# File lib/faria/launchpad/packet.rb, line 75
def self.add_source(packet, source)
  packet[:faria_source] = source
  packet
end
decrypt(raw_data, options = {}, local_key:, remote_key: ) click to toggle source

for cases where you known in advance the remote key to use (such as LaunchPad clients which will only be receiving messages from LaunchPad and therefore will only use it's public key for verifying signatures

# File lib/faria/launchpad/packet.rb, line 45
def self.decrypt(raw_data, options = {}, local_key:, remote_key: )
  _version, jwe = raw_data.split(";", 2)
  jwt = JWE.decrypt(jwe, local_key)
  arr = JWT.decode(jwt, remote_key, true, { :algorithm => 'RS512' })
  payload, _header = arr

  # validate_expiration will be handled by JWT decode
  validate_url!(payload, options[:actual_url])

  payload["data"]
end
decrypt_variable_key(raw_data, options = {}, local_key:, remote_key_func: ) click to toggle source

for cases where the signature key is not known in advance and must be determined by source information embedded in the JWT header

# File lib/faria/launchpad/packet.rb, line 59
def self.decrypt_variable_key(raw_data, options = {}, local_key:, remote_key_func: )
  _version, jwe = raw_data.split(";", 2)
  jwt = JWE.decrypt(jwe, local_key)
  header, payload = JWT::Decode.new(jwt, nil, false, {}).decode_segments[0..1]
  remote_key = remote_key_func.call(header, payload)
  fail(MissingRemoteKey) if remote_key.nil?

  arr = JWT.decode(jwt, remote_key, true, { :algorithm => 'RS512' })
  payload, _header = arr

  # validate_expiration will be handled by JWT decode
  validate_url!(payload, options[:actual_url])

  payload["data"]
end
encrypt(data, options = {}, local_key:, remote_key: ) click to toggle source

encrypting is done with LaunchPad public key signing is done with local private key

# File lib/faria/launchpad/packet.rb, line 29
def self.encrypt(data, options = {}, local_key:, remote_key: )
  packet = { "data" => data}
  packet = add_issued_at(packet)
  packet = add_expires(packet, options[:expires_in]) if options[:expires_in]
  packet = add_api_url(packet, options[:api_url]) if options[:api_url]
  # packet = add_issuer(packet, options[:issuer])
  packet = add_source(packet, options[:source]) if options[:source]

  payload = JWT.encode(packet, local_key, 'RS512')
  "#{VERSION};" + JWE.encrypt(payload, remote_key) # public
end
validate_expiration!(payload) click to toggle source
# File lib/faria/launchpad/packet.rb, line 109
def self.validate_expiration!(payload)
  return unless payload.include?('exp')
  leeway = 0

  valid_until = (Time.now.utc.to_i - leeway)
  if payload['exp'].to_i < valid_until
    diff = valid_until - payload['exp'].to_i
    error = ExpiredSignature.new("Signature expired", diff)
    fail(error)
  end
end
validate_url!(payload, actual_url) click to toggle source
# File lib/faria/launchpad/packet.rb, line 100
def self.validate_url!(payload, actual_url)
  return unless payload.include?('api_url')

  normalized_url = Addressable::URI.parse(actual_url).normalize.to_s
  if payload['api_url'] != normalized_url
    fail(MismatchedRequestURL)
  end
end