<commit_before># encoding UTF-8
require 'base64'
require 'valcro'

module Fernet
  class Token
    include Valcro

    class InvalidToken < StandardError; end

    DEFAULT_VERSION = 0x80.freeze
    MAX_CLOCK_SKEW  = 60.freeze

    def initialize(token, opts = {})
      @token       = token
      @enforce_ttl = opts.fetch(:enforce_ttl) { Configuration.enforce_ttl }
      @ttl         = opts[:ttl] || Configuration.ttl
      @now         = opts[:now]
    end

    def to_s
      @token
    end

    def secret=(secret)
      @secret = Secret.new(secret)
    end

    def valid?
      validate
      super
    end

    def message
      if valid?
        begin
          Encryption.decrypt(key: @secret.encryption_key,
                             ciphertext: encrypted_message,
                             iv: iv)
        rescue OpenSSL::Cipher::CipherError
          raise InvalidToken, "bad decrypt"
        end
      else
        raise InvalidToken, error_messages
      end
    end

    def self.generate(params)
      unless params[:secret]
        raise ArgumentError, 'Secret not provided'
      end
      secret = Secret.new(params[:secret])
      encrypted_message, iv = Encryption.encrypt(key:     secret.encryption_key,
                                                 message: params[:message],
                                                 iv:      params[:iv])
      issued_timestamp = (params[:now] || Time.now).to_i

      payload = [DEFAULT_VERSION].pack("C") +
        BitPacking.pack_int64_bigendian(issued_timestamp) +
        iv +
        encrypted_message
      mac = OpenSSL::HMAC.digest('sha256', secret.signing_key, payload)
      new(Base64.urlsafe_encode64(payload + mac))
    end

  private
    def decoded_token
      @decoded_token ||= Base64.urlsafe_decode64(@token)
    end

    def version
      decoded_token.chr.unpack("C").first
    end

    def received_signature
      decoded_token[(decoded_token.length - 32), 32]
    end

    def issued_timestamp
      BitPacking.unpack_int64_bigendian(decoded_token[1, 8])
    end

    def iv
      decoded_token[9, 16]
    end

    def encrypted_message
      decoded_token[25..(decoded_token.length - 33)]
    end

    def validate
      if valid_base64?
        if unknown_token_version?
          errors.add :version, "is unknown"
        else
          unless signatures_match?
            errors.add :signature, "does not match"
          end
          if enforce_ttl? && !issued_recent_enough?
            errors.add :issued_timestamp, "is too far in the past: token expired"
          end
          if unacceptable_clock_slew?
            errors.add :issued_timestamp, "is too far in the future"
          end
          unless ciphertext_multiple_of_block_size?
            errors.add :ciphertext, "is not a multiple of block size"
          end
        end
      else
        errors.add(:token, "invalid base64")
      end
      super
    end

    def regenerated_mac
      Encryption.hmac_digest(@secret.signing_key, signing_blob)
    end

    def signing_blob
      [version].pack("C") +
        BitPacking.pack_int64_bigendian(issued_timestamp) +
        iv +
        encrypted_message
    end<commit_msg>Use valcro's validate DSL instead of relying on super<commit_after># encoding UTF-8
require 'base64'
require 'valcro'

module Fernet
  class Token
    include Valcro

    class InvalidToken < StandardError; end

    DEFAULT_VERSION = 0x80.freeze
    MAX_CLOCK_SKEW  = 60.freeze

    def initialize(token, opts = {})
      @token       = token
      @enforce_ttl = opts.fetch(:enforce_ttl) { Configuration.enforce_ttl }
      @ttl         = opts[:ttl] || Configuration.ttl
      @now         = opts[:now]
    end

    def to_s
      @token
    end

    def secret=(secret)
      @secret = Secret.new(secret)
    end

    def valid?
      validate
      super
    end

    def message
      if valid?
        begin
          Encryption.decrypt(key: @secret.encryption_key,
                             ciphertext: encrypted_message,
                             iv: iv)
        rescue OpenSSL::Cipher::CipherError
          raise InvalidToken, "bad decrypt"
        end
      else
        raise InvalidToken, error_messages
      end
    end

    def self.generate(params)
      unless params[:secret]
        raise ArgumentError, 'Secret not provided'
      end
      secret = Secret.new(params[:secret])
      encrypted_message, iv = Encryption.encrypt(key:     secret.encryption_key,
                                                 message: params[:message],
                                                 iv:      params[:iv])
      issued_timestamp = (params[:now] || Time.now).to_i

      payload = [DEFAULT_VERSION].pack("C") +
        BitPacking.pack_int64_bigendian(issued_timestamp) +
        iv +
        encrypted_message
      mac = OpenSSL::HMAC.digest('sha256', secret.signing_key, payload)
      new(Base64.urlsafe_encode64(payload + mac))
    end

  private
    def decoded_token
      @decoded_token ||= Base64.urlsafe_decode64(@token)
    end

    def version
      decoded_token.chr.unpack("C").first
    end

    def received_signature
      decoded_token[(decoded_token.length - 32), 32]
    end

    def issued_timestamp
      BitPacking.unpack_int64_bigendian(decoded_token[1, 8])
    end

    def iv
      decoded_token[9, 16]
    end

    def encrypted_message
      decoded_token[25..(decoded_token.length - 33)]
    end

    validate do
      if valid_base64?
        if unknown_token_version?
          errors.add :version, "is unknown"
        else
          unless signatures_match?
            errors.add :signature, "does not match"
          end
          if enforce_ttl? && !issued_recent_enough?
            errors.add :issued_timestamp, "is too far in the past: token expired"
          end
          if unacceptable_clock_slew?
            errors.add :issued_timestamp, "is too far in the future"
          end
          unless ciphertext_multiple_of_block_size?
            errors.add :ciphertext, "is not a multiple of block size"
          end
        end
      else
        errors.add(:token, "invalid base64")
      end
    end

    def regenerated_mac
      Encryption.hmac_digest(@secret.signing_key, signing_blob)
    end

    def signing_blob
      [version].pack("C") +
        BitPacking.pack_int64_bigendian(issued_timestamp) +
        iv +
        encrypted_message
    end
{'fraction_non_alphanumeric': 0.07768955316830142, 'fraction_numerical': 0.013078000934142924, 'mean_word_length': 1.927985414767548, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6158057', 'n_tokens_mistral': 1969, 'n_tokens_neox': 1841, 'n_words': 436}