fernet 2.0.rc1 → 2.0.rc2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.rdoc_options +16 -0
- data/.travis.yml +0 -1
- data/lib/fernet.rb +47 -2
- data/lib/fernet/bit_packing.rb +14 -3
- data/lib/fernet/configuration.rb +20 -4
- data/lib/fernet/encryption.rb +40 -2
- data/lib/fernet/generator.rb +26 -1
- data/lib/fernet/secret.rb +25 -3
- data/lib/fernet/token.rb +35 -14
- data/lib/fernet/verifier.rb +16 -14
- data/lib/fernet/version.rb +1 -1
- data/spec/fernet_spec.rb +4 -17
- data/spec/secret_spec.rb +19 -14
- data/spec/token_spec.rb +7 -13
- metadata +3 -2
data/.rdoc_options
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
--- !ruby/object:RDoc::Options
|
2
|
+
encoding: UTF-8
|
3
|
+
static_path: []
|
4
|
+
rdoc_include:
|
5
|
+
- .
|
6
|
+
charset: UTF-8
|
7
|
+
exclude:
|
8
|
+
hyperlink_all: false
|
9
|
+
line_numbers: false
|
10
|
+
main_page:
|
11
|
+
markup: tomdoc
|
12
|
+
show_hash: false
|
13
|
+
tab_width: 8
|
14
|
+
title:
|
15
|
+
visibility: :protected
|
16
|
+
webcvs:
|
data/.travis.yml
CHANGED
data/lib/fernet.rb
CHANGED
@@ -10,11 +10,56 @@ require 'fernet/configuration'
|
|
10
10
|
Fernet::Configuration.run
|
11
11
|
|
12
12
|
module Fernet
|
13
|
-
|
13
|
+
# Public: generates a fernet token
|
14
|
+
#
|
15
|
+
# secret - a base64 encoded, 32 byte string
|
16
|
+
# message - the message being secured in plain text
|
17
|
+
#
|
18
|
+
# Returns the fernet token as a string
|
19
|
+
#
|
20
|
+
# Examples
|
21
|
+
#
|
22
|
+
# secret = ...
|
23
|
+
# token = Fernet.generate(secret, 'my secrets')
|
24
|
+
def self.generate(secret, message = '', opts = {})
|
14
25
|
Generator.new(opts.merge({secret: secret, message: message})).
|
15
|
-
generate
|
26
|
+
generate
|
16
27
|
end
|
17
28
|
|
29
|
+
# Public: verifies a fernet token
|
30
|
+
#
|
31
|
+
# secret - the secret used to generate the token
|
32
|
+
# token - the token to verify as a string
|
33
|
+
# opts - an optional hash containing
|
34
|
+
# enforce_ttl: whether to enforce TTL in this verification
|
35
|
+
# ttl: number of seconds token is valid
|
36
|
+
#
|
37
|
+
# Both enforce_ttl and ttl can be configured globally via Configuration
|
38
|
+
#
|
39
|
+
# Returns a verifier object, which responds to valid? and message
|
40
|
+
#
|
41
|
+
# Raises Fernet::Token::InvalidToken if token is invalid and message
|
42
|
+
# is attempted to be extracted
|
43
|
+
#
|
44
|
+
# Examples
|
45
|
+
#
|
46
|
+
# secret = ...
|
47
|
+
# token = ...
|
48
|
+
# verifier = Fernet.verifier(secret, old_token, enforce_ttl: false)
|
49
|
+
# if verifier.valid?
|
50
|
+
# verifier.message # original message in plain text
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# verifier = Fernet.verifier(secret, old_token)
|
54
|
+
# if verifier.valid?
|
55
|
+
# verifier.message
|
56
|
+
# else
|
57
|
+
# verifier.errors
|
58
|
+
# # -> { issued_timestamp: "is too far in the past: token expired" }
|
59
|
+
# verifier.error_messages
|
60
|
+
# # -> ["issued_timestamp is too far in the past: token expired"]
|
61
|
+
# end
|
62
|
+
#
|
18
63
|
def self.verifier(secret, token, opts = {})
|
19
64
|
Verifier.new(opts.merge({secret: secret, token: token}))
|
20
65
|
end
|
data/lib/fernet/bit_packing.rb
CHANGED
@@ -1,14 +1,25 @@
|
|
1
1
|
module Fernet
|
2
|
+
# Internal: wrappers used for consistent bit packing across rubies
|
3
|
+
#
|
4
|
+
# Ruby 1.9.2 and below silently ignore endianness specifiers in
|
5
|
+
# packing/unpacking format directives
|
2
6
|
module BitPacking
|
3
7
|
extend self
|
4
8
|
|
5
|
-
#
|
6
|
-
#
|
7
|
-
|
9
|
+
# Internal - packs a value as a big endian, 64 bit integer
|
10
|
+
#
|
11
|
+
# value - a byte sequence as a string
|
12
|
+
#
|
13
|
+
# Returns array containing each value
|
8
14
|
def pack_int64_bigendian(value)
|
9
15
|
(0..7).map { |index| (value >> (index * 8)) & 0xFF }.reverse.map(&:chr).join
|
10
16
|
end
|
11
17
|
|
18
|
+
# Internal - unpacks a string of big endian, 64 bit integers
|
19
|
+
#
|
20
|
+
# bytes - an array of ints
|
21
|
+
#
|
22
|
+
# Returns the original byte sequence as a string
|
12
23
|
def unpack_int64_bigendian(bytes)
|
13
24
|
bytes.each_byte.to_a.reverse.each_with_index.
|
14
25
|
reduce(0) { |val, (byte, index)| val | (byte << (index * 8)) }
|
data/lib/fernet/configuration.rb
CHANGED
@@ -1,16 +1,32 @@
|
|
1
1
|
require 'singleton'
|
2
2
|
module Fernet
|
3
|
+
# Public - singleton class used to globally set various
|
4
|
+
# configuration defaults
|
3
5
|
class Configuration
|
4
6
|
include Singleton
|
5
7
|
|
6
|
-
#
|
7
|
-
# (true or false)
|
8
|
+
# Public: Returns whether to enforce a message TTL (true or false)
|
8
9
|
attr_accessor :enforce_ttl
|
9
10
|
|
10
|
-
#
|
11
|
-
# (an integer in seconds)
|
11
|
+
# Public: Returns how many seconds messages are considered valid for
|
12
12
|
attr_accessor :ttl
|
13
13
|
|
14
|
+
# Public: used to configure fernet, typically invoked in an initialization
|
15
|
+
# routine
|
16
|
+
#
|
17
|
+
# Sets the following values:
|
18
|
+
#
|
19
|
+
# * enforce_ttl: true
|
20
|
+
# * ttl: 60
|
21
|
+
#
|
22
|
+
# Yields the singleton configuration object, where above defaults can be
|
23
|
+
# overridden
|
24
|
+
#
|
25
|
+
# Examples
|
26
|
+
#
|
27
|
+
# Fernet::Configuration.run do |config|
|
28
|
+
# config.enforce_ttl = false
|
29
|
+
# end
|
14
30
|
def self.run
|
15
31
|
self.instance.enforce_ttl = true
|
16
32
|
self.instance.ttl = 60
|
data/lib/fernet/encryption.rb
CHANGED
@@ -1,9 +1,25 @@
|
|
1
1
|
require 'openssl'
|
2
2
|
|
3
3
|
module Fernet
|
4
|
+
# Internal: Encapsulates encryption and signing primitives
|
4
5
|
module Encryption
|
5
6
|
AES_BLOCK_SIZE = 16.freeze
|
6
7
|
|
8
|
+
# Internal: Encrypts the provided message using a AES-128-CBC cipher with a
|
9
|
+
# random IV and the provided encryption key
|
10
|
+
#
|
11
|
+
# opts - a hash containing
|
12
|
+
# message: the message to encrypt
|
13
|
+
# key: the encryption key
|
14
|
+
# iv: override for the random IV, only used for testing
|
15
|
+
#
|
16
|
+
# Returns a two-element array containing the ciphertext and the random IV
|
17
|
+
#
|
18
|
+
# Examples
|
19
|
+
#
|
20
|
+
# ciphertext, iv = Fernet::Encryption.encrypt(
|
21
|
+
# message: 'this is a secret', key: encryption_key
|
22
|
+
# )
|
7
23
|
def self.encrypt(opts)
|
8
24
|
cipher = OpenSSL::Cipher.new('AES-128-CBC')
|
9
25
|
cipher.encrypt
|
@@ -13,6 +29,21 @@ module Fernet
|
|
13
29
|
[cipher.update(opts[:message]) + cipher.final, iv]
|
14
30
|
end
|
15
31
|
|
32
|
+
# Internal: Decrypts the provided ciphertext using a AES-128-CBC cipher with a
|
33
|
+
# the provided IV and encryption key
|
34
|
+
#
|
35
|
+
# opts - a hash containing
|
36
|
+
# ciphertext: encrypted message
|
37
|
+
# key: encryption key used to encrypt the message
|
38
|
+
# iv: initialization vector used in the ciphertext's cipher
|
39
|
+
#
|
40
|
+
# Returns a two-element array containing the ciphertext and the random IV
|
41
|
+
#
|
42
|
+
# Examples
|
43
|
+
#
|
44
|
+
# ciphertext, iv = Fernet::Encryption.encrypt(
|
45
|
+
# message: 'this is a secret', key: encryption_key
|
46
|
+
# )
|
16
47
|
def self.decrypt(opts)
|
17
48
|
decipher = OpenSSL::Cipher.new('AES-128-CBC')
|
18
49
|
decipher.decrypt
|
@@ -21,8 +52,15 @@ module Fernet
|
|
21
52
|
decipher.update(opts[:ciphertext]) + decipher.final
|
22
53
|
end
|
23
54
|
|
24
|
-
|
25
|
-
|
55
|
+
# Internal: Creates an HMAC signature (sha356 hashing) of the given bytes
|
56
|
+
# with the provided signing key
|
57
|
+
#
|
58
|
+
# key - the signing key
|
59
|
+
# bytes - blob of bytes to sign
|
60
|
+
#
|
61
|
+
# Returns the HMAC signature as a string
|
62
|
+
def self.hmac_digest(key, bytes)
|
63
|
+
OpenSSL::HMAC.digest('sha256', key, bytes)
|
26
64
|
end
|
27
65
|
end
|
28
66
|
end
|
data/lib/fernet/generator.rb
CHANGED
@@ -4,9 +4,16 @@ require 'openssl'
|
|
4
4
|
require 'date'
|
5
5
|
|
6
6
|
module Fernet
|
7
|
+
# Internal: Generates Fernet tokens
|
7
8
|
class Generator
|
9
|
+
# Returns the token's message
|
8
10
|
attr_accessor :message
|
9
11
|
|
12
|
+
# Internal: Initializes a generator
|
13
|
+
#
|
14
|
+
# opts - a hash containing the following keys:
|
15
|
+
# secret: a string containing a secret, optionally Base64 encoded
|
16
|
+
# message: the message
|
10
17
|
def initialize(opts)
|
11
18
|
@secret = opts.fetch(:secret)
|
12
19
|
@message = opts[:message]
|
@@ -14,6 +21,21 @@ module Fernet
|
|
14
21
|
@now = opts[:now]
|
15
22
|
end
|
16
23
|
|
24
|
+
# Internal: generates a secret token
|
25
|
+
#
|
26
|
+
# Yields itself, useful for setting or overriding the message
|
27
|
+
#
|
28
|
+
# Returns the token as a string
|
29
|
+
#
|
30
|
+
# Examples
|
31
|
+
# generator = Generator.new(secret: some_secret)
|
32
|
+
# token = generator.generate do |g|
|
33
|
+
# g.message = 'this is my message'
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# generator = Generator.new(secret: some_secret,
|
37
|
+
# message: 'this is my message')
|
38
|
+
# token = generator.generate
|
17
39
|
def generate
|
18
40
|
yield self if block_given?
|
19
41
|
|
@@ -24,13 +46,16 @@ module Fernet
|
|
24
46
|
token.to_s
|
25
47
|
end
|
26
48
|
|
49
|
+
# Public: string representation of this generator, masks secret to avoid
|
50
|
+
# leaks
|
27
51
|
def inspect
|
28
52
|
"#<Fernet::Generator @secret=[masked] @message=#{@message.inspect}>"
|
29
53
|
end
|
30
54
|
alias to_s inspect
|
31
55
|
|
56
|
+
# Deprecated: used to set the message
|
32
57
|
def data=(message)
|
33
|
-
puts "[WARNING] 'data' is deprecated, use 'message' instead"
|
58
|
+
puts "[WARNING] 'data=' is deprecated, use 'message=' instead"
|
34
59
|
@message = message
|
35
60
|
end
|
36
61
|
end
|
data/lib/fernet/secret.rb
CHANGED
@@ -1,23 +1,45 @@
|
|
1
1
|
require 'base64'
|
2
|
+
|
2
3
|
module Fernet
|
4
|
+
# Internal: Encapsulates a secret key, a 32-byte sequence consisting
|
5
|
+
# of an encryption and a signing key.
|
3
6
|
class Secret
|
4
7
|
class InvalidSecret < RuntimeError; end
|
5
8
|
|
9
|
+
# Internal - Initialize a Secret
|
10
|
+
#
|
11
|
+
# secret - the secret, optionally encoded with either standard or
|
12
|
+
# URL safe variants of Base64 encoding
|
13
|
+
#
|
14
|
+
# Raises Fernet::Secret::InvalidSecret if it cannot be decoded or is
|
15
|
+
# not of the expected length
|
6
16
|
def initialize(secret)
|
7
|
-
|
8
|
-
|
9
|
-
|
17
|
+
if secret.bytesize == 32
|
18
|
+
@secret = secret
|
19
|
+
else
|
20
|
+
begin
|
21
|
+
@secret = Base64.urlsafe_decode64(secret)
|
22
|
+
rescue ArgumentError
|
23
|
+
@secret = Base64.decode64(secret)
|
24
|
+
end
|
25
|
+
unless @secret.bytesize == 32
|
26
|
+
raise InvalidSecret,
|
27
|
+
"Secret must be 32 bytes, instead got #{@secret.bytesize}"
|
28
|
+
end
|
10
29
|
end
|
11
30
|
end
|
12
31
|
|
32
|
+
# Internal: Returns the portion of the secret token used for encryption
|
13
33
|
def encryption_key
|
14
34
|
@secret.slice(16, 16)
|
15
35
|
end
|
16
36
|
|
37
|
+
# Internal: Returns the portion of the secret token used for signing
|
17
38
|
def signing_key
|
18
39
|
@secret.slice(0, 16)
|
19
40
|
end
|
20
41
|
|
42
|
+
# Public: String representation of this secret, masks to avoid leaks.
|
21
43
|
def to_s
|
22
44
|
"<Fernet::Secret [masked]>"
|
23
45
|
end
|
data/lib/fernet/token.rb
CHANGED
@@ -3,34 +3,51 @@ require 'base64'
|
|
3
3
|
require 'valcro'
|
4
4
|
|
5
5
|
module Fernet
|
6
|
+
# Internal: encapsulates a fernet token structure and validation
|
6
7
|
class Token
|
7
8
|
include Valcro
|
8
9
|
|
9
10
|
class InvalidToken < StandardError; end
|
10
11
|
|
12
|
+
# Internal: the default token version
|
11
13
|
DEFAULT_VERSION = 0x80.freeze
|
14
|
+
# Internal: max allowed clock skew for calculating TTL
|
12
15
|
MAX_CLOCK_SKEW = 60.freeze
|
13
16
|
|
17
|
+
# Internal: initializes a Token object
|
18
|
+
#
|
19
|
+
# token - the string representation of this token
|
20
|
+
# opts - a has containing
|
21
|
+
# secret: the secret, optionally base 64 encoded (required)
|
22
|
+
# enforce_ttl: whether to enforce TTL upon validation. Defaults to value
|
23
|
+
# set in Configuration.enforce_ttl
|
24
|
+
# ttl: number of seconds token is valid, defaults to Configuration.ttl
|
14
25
|
def initialize(token, opts = {})
|
15
26
|
@token = token
|
27
|
+
@secret = Secret.new(opts.fetch(:secret))
|
16
28
|
@enforce_ttl = opts.fetch(:enforce_ttl) { Configuration.enforce_ttl }
|
17
29
|
@ttl = opts[:ttl] || Configuration.ttl
|
18
30
|
@now = opts[:now]
|
19
31
|
end
|
20
32
|
|
33
|
+
# Internal: returns the token as a string
|
21
34
|
def to_s
|
22
35
|
@token
|
23
36
|
end
|
24
37
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
38
|
+
# Internal: Validates this token and returns true if it's valid
|
39
|
+
#
|
40
|
+
# Returns a boolean set to true if it's valid, false otherwise
|
29
41
|
def valid?
|
30
42
|
validate
|
31
43
|
super
|
32
44
|
end
|
33
45
|
|
46
|
+
# Internal: returns the decrypted message in this token
|
47
|
+
#
|
48
|
+
# Raises InvalidToken if it cannot be decrypted or is invalid
|
49
|
+
#
|
50
|
+
# Returns a string containing the original message in plain text
|
34
51
|
def message
|
35
52
|
if valid?
|
36
53
|
begin
|
@@ -45,22 +62,27 @@ module Fernet
|
|
45
62
|
end
|
46
63
|
end
|
47
64
|
|
48
|
-
|
49
|
-
|
65
|
+
# Internal: generates a Fernet Token
|
66
|
+
#
|
67
|
+
# opts - a hash containing
|
68
|
+
# secret: a string containing the secret, optionally base64 encoded
|
69
|
+
# message: the message in plain text
|
70
|
+
def self.generate(opts)
|
71
|
+
unless opts[:secret]
|
50
72
|
raise ArgumentError, 'Secret not provided'
|
51
73
|
end
|
52
|
-
secret = Secret.new(
|
74
|
+
secret = Secret.new(opts.fetch(:secret))
|
53
75
|
encrypted_message, iv = Encryption.encrypt(key: secret.encryption_key,
|
54
|
-
message:
|
55
|
-
iv:
|
56
|
-
issued_timestamp = (
|
76
|
+
message: opts[:message],
|
77
|
+
iv: opts[:iv])
|
78
|
+
issued_timestamp = (opts[:now] || Time.now).to_i
|
57
79
|
|
58
80
|
payload = [DEFAULT_VERSION].pack("C") +
|
59
81
|
BitPacking.pack_int64_bigendian(issued_timestamp) +
|
60
82
|
iv +
|
61
83
|
encrypted_message
|
62
84
|
mac = OpenSSL::HMAC.digest('sha256', secret.signing_key, payload)
|
63
|
-
new(Base64.urlsafe_encode64(payload + mac))
|
85
|
+
new(Base64.urlsafe_encode64(payload + mac), secret: opts.fetch(:secret))
|
64
86
|
end
|
65
87
|
|
66
88
|
private
|
@@ -92,13 +114,12 @@ module Fernet
|
|
92
114
|
if valid_base64?
|
93
115
|
if unknown_token_version?
|
94
116
|
errors.add :version, "is unknown"
|
117
|
+
elsif enforce_ttl? && !issued_recent_enough?
|
118
|
+
errors.add :issued_timestamp, "is too far in the past: token expired"
|
95
119
|
else
|
96
120
|
unless signatures_match?
|
97
121
|
errors.add :signature, "does not match"
|
98
122
|
end
|
99
|
-
if enforce_ttl? && !issued_recent_enough?
|
100
|
-
errors.add :issued_timestamp, "is too far in the past: token expired"
|
101
|
-
end
|
102
123
|
if unacceptable_clock_slew?
|
103
124
|
errors.add :issued_timestamp, "is too far in the future"
|
104
125
|
end
|
data/lib/fernet/verifier.rb
CHANGED
@@ -4,34 +4,49 @@ require 'openssl'
|
|
4
4
|
require 'date'
|
5
5
|
|
6
6
|
module Fernet
|
7
|
+
# Public: verifies Fernet Tokens
|
7
8
|
class Verifier
|
8
9
|
class UnknownTokenVersion < RuntimeError; end
|
9
10
|
|
10
11
|
attr_reader :token
|
11
12
|
attr_accessor :ttl, :enforce_ttl
|
12
13
|
|
14
|
+
# Internal: initializes a Verifier
|
15
|
+
#
|
16
|
+
# opts - a hash containing
|
17
|
+
# secret: the secret used to create the token (required)
|
18
|
+
# token: the fernet token string (required)
|
19
|
+
# enforce_ttl: whether to enforce TTL, defaults to Configuration.enforce_ttl
|
20
|
+
# ttl: number of seconds the token is valid
|
13
21
|
def initialize(opts = {})
|
14
22
|
enforce_ttl = opts.has_key?(:enforce_ttl) ? opts[:enforce_ttl] : Configuration.enforce_ttl
|
15
23
|
@token = Token.new(opts.fetch(:token),
|
24
|
+
secret: opts.fetch(:secret),
|
16
25
|
enforce_ttl: enforce_ttl,
|
17
26
|
ttl: opts[:ttl],
|
18
27
|
now: opts[:now])
|
19
|
-
@token.secret = opts.fetch(:secret)
|
20
28
|
end
|
21
29
|
|
30
|
+
# Public: whether the verifier is valid. A verifier is valid if it's token
|
31
|
+
# is valid.
|
32
|
+
#
|
33
|
+
# Returns a boolean set to true if the token is valid, false otherwise
|
22
34
|
def valid?
|
23
35
|
@token.valid?
|
24
36
|
end
|
25
37
|
|
38
|
+
# Public: Returns the token's message
|
26
39
|
def message
|
27
40
|
@token.message
|
28
41
|
end
|
29
42
|
|
43
|
+
# Deprecated: returns the token's message
|
30
44
|
def data
|
31
45
|
puts "[WARNING] data is deprected. Use message instead"
|
32
46
|
message
|
33
47
|
end
|
34
48
|
|
49
|
+
# Public: String representation of this verifier, masks the secret to avoid leaks.
|
35
50
|
def inspect
|
36
51
|
"#<Fernet::Verifier @secret=[masked] @token=#{@token} @message=#{@message.inspect} @ttl=#{@ttl} @enforce_ttl=#{@enforce_ttl}>"
|
37
52
|
end
|
@@ -42,15 +57,6 @@ module Fernet
|
|
42
57
|
@must_verify || @valid.nil?
|
43
58
|
end
|
44
59
|
|
45
|
-
def token_recent_enough?
|
46
|
-
if enforce_ttl?
|
47
|
-
good_till = @issued_at + (ttl.to_f / 24 / 60 / 60)
|
48
|
-
(good_till.to_i >= now.to_i) && acceptable_clock_skew?
|
49
|
-
else
|
50
|
-
true
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
60
|
def acceptable_clock_skew?
|
55
61
|
@issued_at < (now + MAX_CLOCK_SKEW)
|
56
62
|
end
|
@@ -63,10 +69,6 @@ module Fernet
|
|
63
69
|
end.zero?
|
64
70
|
end
|
65
71
|
|
66
|
-
def enforce_ttl?
|
67
|
-
@enforce_ttl
|
68
|
-
end
|
69
|
-
|
70
72
|
def now
|
71
73
|
@now ||= Time.now
|
72
74
|
end
|
data/lib/fernet/version.rb
CHANGED
data/spec/fernet_spec.rb
CHANGED
@@ -8,32 +8,21 @@ describe Fernet do
|
|
8
8
|
let(:bad_secret) { 'badICDH6x3M7duQeM8dJEMK4Y5TkBIsYDw1lPy35RiY=' }
|
9
9
|
|
10
10
|
it 'can verify tokens it generates' do
|
11
|
-
token = Fernet.generate(secret) do |generator|
|
12
|
-
generator.message = 'harold@heroku.com'
|
13
|
-
end
|
14
|
-
|
15
|
-
verifier = Fernet.verifier(secret, token)
|
16
|
-
expect(verifier).to be_valid
|
17
|
-
expect(verifier.message).to eq('harold@heroku.com')
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'can generate tokens without a block' do
|
21
11
|
token = Fernet.generate(secret, 'harold@heroku.com')
|
12
|
+
|
22
13
|
verifier = Fernet.verifier(secret, token)
|
23
14
|
expect(verifier).to be_valid
|
24
15
|
expect(verifier.message).to eq('harold@heroku.com')
|
25
16
|
end
|
26
17
|
|
27
18
|
it 'fails with a bad secret' do
|
28
|
-
token = Fernet.generate(secret)
|
29
|
-
generator.message = 'harold@heroku.com'
|
30
|
-
end
|
19
|
+
token = Fernet.generate(secret, 'harold@heroku.com')
|
31
20
|
|
32
21
|
verifier = Fernet.verifier(bad_secret, token)
|
33
22
|
expect(verifier.valid?).to be_false
|
34
23
|
expect {
|
35
24
|
verifier.message
|
36
|
-
}.to raise_error
|
25
|
+
}.to raise_error Fernet::Token::InvalidToken
|
37
26
|
end
|
38
27
|
|
39
28
|
it 'fails if the token is too old' do
|
@@ -77,9 +66,7 @@ describe Fernet do
|
|
77
66
|
config.enforce_ttl = true
|
78
67
|
config.ttl = 0
|
79
68
|
end
|
80
|
-
token = Fernet.generate(secret)
|
81
|
-
generator.message = 'password1'
|
82
|
-
end
|
69
|
+
token = Fernet.generate(secret, 'password1')
|
83
70
|
verifier = Fernet.verifier(secret, token)
|
84
71
|
verifier.enforce_ttl = false
|
85
72
|
expect(verifier.valid?).to be_true
|
data/spec/secret_spec.rb
CHANGED
@@ -2,23 +2,16 @@ require 'spec_helper'
|
|
2
2
|
require 'fernet/secret'
|
3
3
|
|
4
4
|
describe Fernet::Secret do
|
5
|
-
it "
|
6
|
-
|
7
|
-
expect do
|
8
|
-
Fernet::Secret.new(secret)
|
9
|
-
end.to_not raise_error
|
5
|
+
it "can resolve a URL safe base64 encoded 32 byte string" do
|
6
|
+
resolves_input(Base64.urlsafe_encode64("A"*16 + "B"*16))
|
10
7
|
end
|
11
8
|
|
12
|
-
it "
|
13
|
-
|
14
|
-
|
15
|
-
expect(
|
16
|
-
fernet_secret.signing_key
|
17
|
-
).to eq("A"*16)
|
9
|
+
it "can resolve a base64 encoded 32 byte string" do
|
10
|
+
resolves_input(Base64.encode64("A"*16 + "B"*16))
|
11
|
+
end
|
18
12
|
|
19
|
-
|
20
|
-
|
21
|
-
).to eq("B"*16)
|
13
|
+
it "can resolve a 32 byte string without encoding" do
|
14
|
+
resolves_input("A"*16 + "B"*16)
|
22
15
|
end
|
23
16
|
|
24
17
|
it "fails loudly when an invalid secret is provided" do
|
@@ -27,4 +20,16 @@ describe Fernet::Secret do
|
|
27
20
|
Fernet::Secret.new(secret)
|
28
21
|
end.to raise_error(Fernet::Secret::InvalidSecret)
|
29
22
|
end
|
23
|
+
|
24
|
+
def resolves_input(input)
|
25
|
+
secret = Fernet::Secret.new(input)
|
26
|
+
|
27
|
+
expect(
|
28
|
+
secret.signing_key
|
29
|
+
).to eq("A"*16)
|
30
|
+
|
31
|
+
expect(
|
32
|
+
secret.encryption_key
|
33
|
+
).to eq("B"*16)
|
34
|
+
end
|
30
35
|
end
|
data/spec/token_spec.rb
CHANGED
@@ -11,8 +11,7 @@ describe Fernet::Token, 'validation' do
|
|
11
11
|
bogus_hmac = "1" * 32
|
12
12
|
Fernet::Encryption.stub(hmac_digest: bogus_hmac)
|
13
13
|
|
14
|
-
token = Fernet::Token.new(generated.to_s)
|
15
|
-
token.secret = secret
|
14
|
+
token = Fernet::Token.new(generated.to_s, secret: secret)
|
16
15
|
|
17
16
|
expect(token.valid?).to be_false
|
18
17
|
expect(token.errors[:signature]).to include("does not match")
|
@@ -23,9 +22,8 @@ describe Fernet::Token, 'validation' do
|
|
23
22
|
message: 'hello',
|
24
23
|
now: Time.now - 61)
|
25
24
|
token = Fernet::Token.new(generated.to_s, enforce_ttl: true,
|
26
|
-
ttl: 60
|
27
|
-
|
28
|
-
|
25
|
+
ttl: 60,
|
26
|
+
secret: secret)
|
29
27
|
expect(token.valid?).to be_false
|
30
28
|
expect(token.errors[:issued_timestamp]).to include("is too far in the past: token expired")
|
31
29
|
end
|
@@ -34,23 +32,21 @@ describe Fernet::Token, 'validation' do
|
|
34
32
|
generated = Fernet::Token.generate(secret: secret,
|
35
33
|
message: 'hello',
|
36
34
|
now: Time.at(Time.now.to_i + 61))
|
37
|
-
token = Fernet::Token.new(generated.to_s)
|
38
|
-
token.secret = secret
|
35
|
+
token = Fernet::Token.new(generated.to_s, secret: secret)
|
39
36
|
|
40
37
|
expect(token.valid?).to be_false
|
41
38
|
expect(token.errors[:issued_timestamp]).to include("is too far in the future")
|
42
39
|
end
|
43
40
|
|
44
41
|
it 'is invalid with bad base64' do
|
45
|
-
token = Fernet::Token.new('bad')
|
46
|
-
token.secret = secret
|
42
|
+
token = Fernet::Token.new('bad', secret: secret)
|
47
43
|
|
48
44
|
expect(token.valid?).to be_false
|
49
45
|
expect(token.errors[:token]).to include("invalid base64")
|
50
46
|
end
|
51
47
|
|
52
48
|
it 'is invalid with an unknown token version' do
|
53
|
-
token
|
49
|
+
token = Fernet::Token.new(Base64.urlsafe_encode64("xxxxxx"), secret: secret)
|
54
50
|
|
55
51
|
expect(token.valid?).to be_false
|
56
52
|
expect(token.errors[:version]).to include("is unknown")
|
@@ -63,8 +59,7 @@ describe Fernet::Token, 'message' do
|
|
63
59
|
generated = Fernet::Token.generate(secret: secret,
|
64
60
|
message: 'hello',
|
65
61
|
now: Time.now + 61)
|
66
|
-
token = Fernet::Token.new(generated.to_s)
|
67
|
-
token.secret = secret
|
62
|
+
token = Fernet::Token.new(generated.to_s, secret: secret)
|
68
63
|
|
69
64
|
!token.valid? or raise "invalid token"
|
70
65
|
|
@@ -77,7 +72,6 @@ describe Fernet::Token, 'message' do
|
|
77
72
|
it 'gives back the original message in plain text' do
|
78
73
|
token = Fernet::Token.generate(secret: secret,
|
79
74
|
message: 'hello')
|
80
|
-
token.secret = secret
|
81
75
|
token.valid? or raise "invalid token"
|
82
76
|
|
83
77
|
expect(token.message).to eq('hello')
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fernet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.rc2
|
5
5
|
prerelease: 4
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-09-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: valcro
|
@@ -52,6 +52,7 @@ extra_rdoc_files: []
|
|
52
52
|
files:
|
53
53
|
- .gitignore
|
54
54
|
- .gitmodules
|
55
|
+
- .rdoc_options
|
55
56
|
- .rspec
|
56
57
|
- .travis.yml
|
57
58
|
- Gemfile
|