Sha256: 9e69632b1b28072d5213fe2d61b4c7f2ac1f888e1ea236e31407604680deded0

Contents?: true

Size: 1.84 KB

Versions: 2

Compression:

Stored size: 1.84 KB

Contents

# frozen_string_literal: true

require 'rbnacl'
require 'base_x'

require 'branca/version'
require 'branca/exceptions'
require 'branca/decoder'

module Branca
  class << self
    VERSION = 0xBA

    attr_accessor :secret_key, :ttl

    def encode(message, timestamp = Time.now.utc, secret_key: self.secret_key)
      cipher = create_cipher(secret_key)
      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

    def decode(token, ttl: self.ttl, secret_key: self.secret_key)
      header, bytes = token_explode(token)
      version, timestamp, nonce = header_explode(header)

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

      cipher = create_cipher(secret_key)
      message = cipher.decrypt(nonce, bytes.pack('C*'), header.pack('C*'))
    rescue RbNaCl::CryptoError
      raise DecodeError
    else
      Decoder.new(message, Time.at(timestamp).utc)
    end

    def ttl
      @ttl ||= ttl_default
    end

    def secret_key
      @secret_key&.b || RbNaCl::Random.random_bytes(32)
    end

    def configure
      yield self if block_given?
    end

    private

    def create_cipher(key)
      RbNaCl::AEAD::XChaCha20Poly1305IETF.new(key)
    end

    def token_explode(token)
      bytes = BaseX::Base62.decode(token).unpack('C C4 C24 C*')
      header = bytes.shift(1 + 4 + 24)

      [header, bytes]
    end

    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

    def ttl_default
      @ttl_default ||= 86_400
    end
  end
end

Version data entries

2 entries across 2 versions & 1 rubygems

Version Path
branca-ruby-1.0.3 lib/branca.rb
branca-ruby-1.0.2 lib/branca.rb