module Branca

Constants

VERSION

Attributes

secret_key[RW]
ttl[RW]

Public Class Methods

configure() { |self| ... } click to toggle source
# File lib/branca.rb, line 45
def configure
  yield self if block_given?
end
decode(token) click to toggle source
# File lib/branca.rb, line 26
def decode(token)
  header, bytes = token_explode(token)
  version, timestamp, nonce = header_explode(header)

  raise VersionError unless version == VERSION
  raise ExpiredTokenError if (timestamp + Branca.ttl) < Time.now.utc.to_i

  message = cipher.decrypt(nonce, bytes.pack('C*'), header.pack('C*'))
  Decoder.new(message, Time.at(timestamp).utc)
end
encode(message, timestamp = Time.now.utc) click to toggle source
# File lib/branca.rb, line 16
def encode(message, timestamp = Time.now.utc)
  nonce = RbNaCl::Random.random_bytes(cipher.nonce_bytes)

  header = [VERSION, timestamp.to_i].pack('C N') + nonce
  ciphertext = cipher.encrypt(nonce, message, header)
  raw_token = header + ciphertext

  BaseX::Base62.encode(raw_token)
end

Private Class Methods

cipher() click to toggle source
# File lib/branca.rb, line 51
def cipher
  @cipher ||= RbNaCl::AEAD::XChaCha20Poly1305IETF.new(secret_key)
end
header_explode(header) click to toggle source
# File lib/branca.rb, line 62
def header_explode(header)
  version = header[0]
  nonce = header[5..header.size].pack('C*')
  timestamp = header[1..4].pack('C*').unpack('N')&.first

  [version, timestamp, nonce]
end
token_explode(token) click to toggle source
# File lib/branca.rb, line 55
def token_explode(token)
  bytes = BaseX::Base62.decode(token).unpack('C C4 C24 C*')
  header = bytes.shift(1 + 4 + 24)

  [header, bytes]
end
ttl_default() click to toggle source
# File lib/branca.rb, line 70
def ttl_default
  @ttl_default ||= 86_400
end