fernet 1.0 → 1.1

Sign up to get free protection for your applications and to get access to all the features.
data/fernet.gemspec CHANGED
@@ -4,8 +4,8 @@ require File.expand_path('../lib/fernet/version', __FILE__)
4
4
  Gem::Specification.new do |gem|
5
5
  gem.authors = ["Harold Giménez"]
6
6
  gem.email = ["harold.gimenez@gmail.com"]
7
- gem.description = %q{Delicious HMAC Digest(if) authentication}
8
- gem.summary = %q{Easily generate and verify HMAC based authentication tokens}
7
+ gem.description = %q{Delicious HMAC Digest(if) authentication and encryption}
8
+ gem.summary = %q{Easily generate and verify AES encrypted HMAC based authentication tokens}
9
9
  gem.homepage = ""
10
10
 
11
11
  gem.files = `git ls-files`.split($\)
@@ -15,5 +15,7 @@ Gem::Specification.new do |gem|
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = Fernet::VERSION
17
17
 
18
+ gem.add_dependency "yajl-ruby"
19
+
18
20
  gem.add_development_dependency "rspec"
19
21
  end
data/lib/fernet.rb CHANGED
@@ -3,6 +3,10 @@ require 'fernet/generator'
3
3
  require 'fernet/verifier'
4
4
  require 'fernet/secret'
5
5
 
6
+ if RUBY_VERSION == '1.8.7'
7
+ require 'shim/base64'
8
+ end
9
+
6
10
  module Fernet
7
11
  def self.generate(secret, encrypt = true, &block)
8
12
  Generator.new(secret, encrypt).generate(&block)
@@ -1,5 +1,5 @@
1
1
  require 'base64'
2
- require 'json'
2
+ require 'yajl'
3
3
  require 'openssl'
4
4
  require 'date'
5
5
 
@@ -16,13 +16,13 @@ module Fernet
16
16
 
17
17
  def generate
18
18
  yield self if block_given?
19
- data.merge!(issued_at: DateTime.now)
19
+ data.merge!(:issued_at => DateTime.now)
20
20
 
21
21
  if encrypt?
22
22
  iv = encrypt_data!
23
23
  @payload = "#{base64(data)}|#{base64(iv)}"
24
24
  else
25
- @payload = base64(JSON.dump(data))
25
+ @payload = base64(Yajl::Encoder.encode(data))
26
26
  end
27
27
 
28
28
  mac = OpenSSL::HMAC.hexdigest('sha256', payload, signing_key)
@@ -47,7 +47,7 @@ module Fernet
47
47
  iv = cipher.random_iv
48
48
  cipher.iv = iv
49
49
  cipher.key = encryption_key
50
- @data = cipher.update(JSON.dump(data)) + cipher.final
50
+ @data = cipher.update(Yajl::Encoder.encode(data)) + cipher.final
51
51
  iv
52
52
  end
53
53
 
data/lib/fernet/secret.rb CHANGED
@@ -6,12 +6,12 @@ module Fernet
6
6
  end
7
7
 
8
8
  def encryption_key
9
- @secret.byteslice(@secret.bytesize/2, @secret.bytesize)
9
+ @secret.slice(@secret.size/2, @secret.size)
10
10
  end
11
11
 
12
12
  def signing_key
13
13
  if @encrypt
14
- @secret.byteslice(0, @secret.bytesize/2)
14
+ @secret.slice(0, @secret.size/2)
15
15
  else
16
16
  @secret
17
17
  end
@@ -1,16 +1,18 @@
1
1
  require 'base64'
2
- require 'json'
2
+ require 'yajl'
3
3
  require 'openssl'
4
4
  require 'date'
5
5
 
6
6
  module Fernet
7
7
  class Verifier
8
8
  attr_reader :token, :data
9
- attr_writer :seconds_valid
9
+ attr_accessor :ttl, :enforce_ttl
10
10
 
11
11
  def initialize(secret, decrypt)
12
- @secret = Secret.new(secret, decrypt)
13
- @decrypt = decrypt
12
+ @secret = Secret.new(secret, decrypt)
13
+ @decrypt = decrypt
14
+ @ttl = 60
15
+ @enforce_ttl = true
14
16
  end
15
17
 
16
18
  def verify_token(token)
@@ -27,7 +29,7 @@ module Fernet
27
29
  end
28
30
 
29
31
  def inspect
30
- "#<Fernet::Verifier @secret=[masked] @token=#{@token} @data=#{@data.inspect} @seconds_valid=#{@seconds_valid}>"
32
+ "#<Fernet::Verifier @secret=[masked] @token=#{@token} @data=#{@data.inspect} @ttl=#{@ttl}>"
31
33
  end
32
34
  alias to_s inspect
33
35
 
@@ -38,18 +40,22 @@ module Fernet
38
40
  parts = @token.split('|')
39
41
  if decrypt?
40
42
  encrypted_data, iv, @received_signature = *parts
41
- @data = JSON.parse(decrypt!(encrypted_data, Base64.urlsafe_decode64(iv)))
43
+ @data = Yajl::Parser.parse(decrypt!(encrypted_data, Base64.urlsafe_decode64(iv)))
42
44
  signing_blob = "#{encrypted_data}|#{iv}"
43
45
  else
44
46
  encoded_data, @received_signature = *parts
45
47
  signing_blob = encoded_data
46
- @data = JSON.parse(Base64.urlsafe_decode64(encoded_data))
48
+ @data = Yajl::Parser.parse(Base64.urlsafe_decode64(encoded_data))
47
49
  end
48
50
  @regenerated_mac = OpenSSL::HMAC.hexdigest('sha256', signing_blob, signing_key)
49
51
  end
50
52
 
51
53
  def token_recent_enough?
52
- DateTime.parse(data['issued_at']) > (DateTime.now - 60)
54
+ if enforce_ttl?
55
+ DateTime.parse(data['issued_at']) > (now - ttl)
56
+ else
57
+ true
58
+ end
53
59
  end
54
60
 
55
61
  def signatures_match?
@@ -80,5 +86,12 @@ module Fernet
80
86
  @decrypt
81
87
  end
82
88
 
89
+ def enforce_ttl?
90
+ @enforce_ttl
91
+ end
92
+
93
+ def now
94
+ DateTime.now
95
+ end
83
96
  end
84
97
  end
@@ -1,3 +1,3 @@
1
1
  module Fernet
2
- VERSION = "1.0"
2
+ VERSION = "1.1"
3
3
  end
@@ -0,0 +1,21 @@
1
+ Base64.class_eval do
2
+ def strict_encode64(bin)
3
+ encode64(bin).tr("\n",'')
4
+ end
5
+
6
+ def strict_decode64(str)
7
+ unless str.include?("\n")
8
+ decode64(str)
9
+ else
10
+ raise(ArgumentError,"invalid base64")
11
+ end
12
+ end
13
+
14
+ def urlsafe_encode64(bin)
15
+ strict_encode64(bin).tr("+/", "-_")
16
+ end
17
+
18
+ def urlsafe_decode64(str)
19
+ strict_decode64(str.tr("-_", "+/"))
20
+ end
21
+ end
data/spec/fernet_spec.rb CHANGED
@@ -3,11 +3,11 @@ require 'fernet'
3
3
 
4
4
  describe Fernet do
5
5
  let(:token_data) do
6
- { email: 'harold@heroku.com', id: '123', arbitrary: 'data' }
6
+ { :email => 'harold@heroku.com', :id => '123', :arbitrary => 'data' }
7
7
  end
8
8
 
9
- let(:secret) { 'JrdICDH6x3M7duQeM8dJEMK4Y5TkBIsYDw1lPy35RiY=' }
10
- let(:bad_secret) { 'jrdICDH6x3M7duQeM8dJEMK4Y5TkBIsYDw1lPy35RiY=' }
9
+ let(:secret) { 'JrdICDH6x3M7duQeM8dJEMK4Y5TkBIsYDw1lPy35RiY=' }
10
+ let(:bad_secret) { 'badICDH6x3M7duQeM8dJEMK4Y5TkBIsYDw1lPy35RiY=' }
11
11
 
12
12
  it 'can verify tokens it generates' do
13
13
  token = Fernet.generate(secret) do |generator|
@@ -45,7 +45,7 @@ describe Fernet do
45
45
  end
46
46
 
47
47
  Fernet.verify(bad_secret, token) do |verifier|
48
- verifier.seconds_valid = 0
48
+ verifier.ttl = 0
49
49
  end.should be_false
50
50
  end
51
51
 
@@ -57,6 +57,20 @@ describe Fernet do
57
57
  Fernet.verify(secret, token).should be_true
58
58
  end
59
59
 
60
+ it 'can TTL enforcement' do
61
+ token = Fernet.generate(secret) do |generator|
62
+ generator.data = token_data
63
+ end
64
+
65
+ Fernet.verify(secret, token) do |verifier|
66
+ def verifier.now
67
+ Time.now + 99999999999
68
+ end
69
+ verifier.enforce_ttl = false
70
+ true
71
+ end.should be_true
72
+ end
73
+
60
74
  it 'generates without custom data' do
61
75
  token = Fernet.generate(secret)
62
76
 
@@ -88,5 +102,4 @@ describe Fernet do
88
102
  verifier.data['password'].should == 'password1'
89
103
  end
90
104
  end
91
-
92
105
  end
metadata CHANGED
@@ -1,34 +1,59 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: fernet
3
- version: !ruby/object:Gem::Version
4
- version: '1.0'
3
+ version: !ruby/object:Gem::Version
4
+ hash: 13
5
5
  prerelease:
6
+ segments:
7
+ - 1
8
+ - 1
9
+ version: "1.1"
6
10
  platform: ruby
7
- authors:
8
- - Harold Giménez
11
+ authors:
12
+ - "Harold Gim\xC3\xA9nez"
9
13
  autorequire:
10
14
  bindir: bin
11
15
  cert_chain: []
12
- date: 2012-08-14 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
16
+
17
+ date: 2012-08-15 00:00:00 Z
18
+ dependencies:
19
+ - !ruby/object:Gem::Dependency
20
+ name: yajl-ruby
21
+ prerelease: false
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ hash: 3
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
15
34
  name: rspec
16
- requirement: &70135148211780 !ruby/object:Gem::Requirement
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
17
37
  none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
21
- version: '0'
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ hash: 3
42
+ segments:
43
+ - 0
44
+ version: "0"
22
45
  type: :development
23
- prerelease: false
24
- version_requirements: *70135148211780
25
- description: Delicious HMAC Digest(if) authentication
26
- email:
46
+ version_requirements: *id002
47
+ description: Delicious HMAC Digest(if) authentication and encryption
48
+ email:
27
49
  - harold.gimenez@gmail.com
28
50
  executables: []
51
+
29
52
  extensions: []
53
+
30
54
  extra_rdoc_files: []
31
- files:
55
+
56
+ files:
32
57
  - .gitignore
33
58
  - .rspec
34
59
  - Gemfile
@@ -41,32 +66,42 @@ files:
41
66
  - lib/fernet/secret.rb
42
67
  - lib/fernet/verifier.rb
43
68
  - lib/fernet/version.rb
69
+ - lib/shim/base64.rb
44
70
  - spec/fernet_spec.rb
45
71
  - spec/spec_helper.rb
46
- homepage: ''
72
+ homepage: ""
47
73
  licenses: []
74
+
48
75
  post_install_message:
49
76
  rdoc_options: []
50
- require_paths:
77
+
78
+ require_paths:
51
79
  - lib
52
- required_ruby_version: !ruby/object:Gem::Requirement
80
+ required_ruby_version: !ruby/object:Gem::Requirement
53
81
  none: false
54
- requirements:
55
- - - ! '>='
56
- - !ruby/object:Gem::Version
57
- version: '0'
58
- required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ hash: 3
86
+ segments:
87
+ - 0
88
+ version: "0"
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
90
  none: false
60
- requirements:
61
- - - ! '>='
62
- - !ruby/object:Gem::Version
63
- version: '0'
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ hash: 3
95
+ segments:
96
+ - 0
97
+ version: "0"
64
98
  requirements: []
99
+
65
100
  rubyforge_project:
66
101
  rubygems_version: 1.8.10
67
102
  signing_key:
68
103
  specification_version: 3
69
- summary: Easily generate and verify HMAC based authentication tokens
70
- test_files:
104
+ summary: Easily generate and verify AES encrypted HMAC based authentication tokens
105
+ test_files:
71
106
  - spec/fernet_spec.rb
72
107
  - spec/spec_helper.rb