fernet 2.0.rc1 → 2.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|