lib/fernet/token.rb in fernet-2.0.rc1 vs lib/fernet/token.rb in fernet-2.0.rc2
- old
+ new
@@ -1,38 +1,55 @@
# encoding UTF-8
require 'base64'
require 'valcro'
module Fernet
+ # Internal: encapsulates a fernet token structure and validation
class Token
include Valcro
class InvalidToken < StandardError; end
+ # Internal: the default token version
DEFAULT_VERSION = 0x80.freeze
+ # Internal: max allowed clock skew for calculating TTL
MAX_CLOCK_SKEW = 60.freeze
+ # Internal: initializes a Token object
+ #
+ # token - the string representation of this token
+ # opts - a has containing
+ # secret: the secret, optionally base 64 encoded (required)
+ # enforce_ttl: whether to enforce TTL upon validation. Defaults to value
+ # set in Configuration.enforce_ttl
+ # ttl: number of seconds token is valid, defaults to Configuration.ttl
def initialize(token, opts = {})
@token = token
+ @secret = Secret.new(opts.fetch(:secret))
@enforce_ttl = opts.fetch(:enforce_ttl) { Configuration.enforce_ttl }
@ttl = opts[:ttl] || Configuration.ttl
@now = opts[:now]
end
+ # Internal: returns the token as a string
def to_s
@token
end
- def secret=(secret)
- @secret = Secret.new(secret)
- end
-
+ # Internal: Validates this token and returns true if it's valid
+ #
+ # Returns a boolean set to true if it's valid, false otherwise
def valid?
validate
super
end
+ # Internal: returns the decrypted message in this token
+ #
+ # Raises InvalidToken if it cannot be decrypted or is invalid
+ #
+ # Returns a string containing the original message in plain text
def message
if valid?
begin
Encryption.decrypt(key: @secret.encryption_key,
ciphertext: encrypted_message,
@@ -43,26 +60,31 @@
else
raise InvalidToken, error_messages
end
end
- def self.generate(params)
- unless params[:secret]
+ # Internal: generates a Fernet Token
+ #
+ # opts - a hash containing
+ # secret: a string containing the secret, optionally base64 encoded
+ # message: the message in plain text
+ def self.generate(opts)
+ unless opts[:secret]
raise ArgumentError, 'Secret not provided'
end
- secret = Secret.new(params[:secret])
+ secret = Secret.new(opts.fetch(:secret))
encrypted_message, iv = Encryption.encrypt(key: secret.encryption_key,
- message: params[:message],
- iv: params[:iv])
- issued_timestamp = (params[:now] || Time.now).to_i
+ message: opts[:message],
+ iv: opts[:iv])
+ issued_timestamp = (opts[: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))
+ new(Base64.urlsafe_encode64(payload + mac), secret: opts.fetch(:secret))
end
private
def decoded_token
@decoded_token ||= Base64.urlsafe_decode64(@token)
@@ -90,15 +112,14 @@
validate do
if valid_base64?
if unknown_token_version?
errors.add :version, "is unknown"
+ elsif enforce_ttl? && !issued_recent_enough?
+ errors.add :issued_timestamp, "is too far in the past: token expired"
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?