fernet 0.1 → 1.0

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/README.md CHANGED
@@ -55,16 +55,24 @@ Otherwise, `verified` will be false, and you should deny the request with an
55
55
  HTTP 401, for example.
56
56
 
57
57
  The specs
58
- ([spec/fernet_spec.rb](https://github.com/hgimenez/fernet/blob/master/spec/fernet_spec.rb))
58
+ ([spec/fernet_spec.rb](https://github.com/hgmnz/fernet/blob/master/spec/fernet_spec.rb))
59
59
  have more usage examples.
60
60
 
61
+ ### Generating a secret
62
+
63
+ Generating appropriate secrets is beyond the scope of `Fernet`, but you should
64
+ generate it using `/dev/random` in a *nix. To generate a base64-encoded 256 bit
65
+ (32 byte) random sequence, try:
66
+
67
+ dd if=/dev/urandom bs=32 count=1 2>/dev/null | openssl base64
68
+
61
69
  ### Attribution
62
70
 
63
71
  This library was largely made possible by [Mr. Tom
64
72
  Maher](http://twitter.com/#tmaher), who clearly articulated the mechanics
65
73
  behind this process, and further found ways to make it
66
- [more](https://github.com/hgimenez/fernet/commit/2bf0b4a66b49ef3fc92ef50708a2c8b401950fc2)
67
- [secure](https://github.com/hgimenez/fernet/commit/051161d0afb0b41480734d84bc824bdbc7f9c563).
74
+ [more](https://github.com/hgmnz/fernet/commit/2bf0b4a66b49ef3fc92ef50708a2c8b401950fc2)
75
+ [secure](https://github.com/hgmnz/fernet/commit/051161d0afb0b41480734d84bc824bdbc7f9c563).
68
76
 
69
77
  ## License
70
78
 
data/lib/fernet.rb CHANGED
@@ -1,13 +1,14 @@
1
1
  require 'fernet/version'
2
2
  require 'fernet/generator'
3
3
  require 'fernet/verifier'
4
+ require 'fernet/secret'
4
5
 
5
6
  module Fernet
6
- def self.generate(secret, &block)
7
- Generator.new(secret).generate(&block)
7
+ def self.generate(secret, encrypt = true, &block)
8
+ Generator.new(secret, encrypt).generate(&block)
8
9
  end
9
10
 
10
- def self.verify(secret, token, &block)
11
- Verifier.new(secret).verify_token(token, &block)
11
+ def self.verify(secret, token, decrypt = true, &block)
12
+ Verifier.new(secret, decrypt).verify_token(token, &block)
12
13
  end
13
14
  end
@@ -5,18 +5,28 @@ require 'date'
5
5
 
6
6
  module Fernet
7
7
  class Generator
8
- attr_accessor :data
8
+ attr_accessor :data, :payload
9
9
 
10
- def initialize(secret)
11
- @secret = secret
10
+ def initialize(secret, encrypt)
11
+ @secret = Secret.new(secret, encrypt)
12
+ @encrypt = encrypt
13
+ @payload = ''
14
+ @data = {}
12
15
  end
13
16
 
14
17
  def generate
15
18
  yield self if block_given?
16
19
  data.merge!(issued_at: DateTime.now)
17
20
 
18
- mac = OpenSSL::HMAC.hexdigest('sha256', JSON.dump(data), secret)
19
- Base64.urlsafe_encode64(JSON.dump(data.merge(signature: mac)))
21
+ if encrypt?
22
+ iv = encrypt_data!
23
+ @payload = "#{base64(data)}|#{base64(iv)}"
24
+ else
25
+ @payload = base64(JSON.dump(data))
26
+ end
27
+
28
+ mac = OpenSSL::HMAC.hexdigest('sha256', payload, signing_key)
29
+ "#{payload}|#{mac}"
20
30
  end
21
31
 
22
32
  def inspect
@@ -30,5 +40,32 @@ module Fernet
30
40
 
31
41
  private
32
42
  attr_reader :secret
43
+
44
+ def encrypt_data!
45
+ cipher = OpenSSL::Cipher.new('AES-128-CBC')
46
+ cipher.encrypt
47
+ iv = cipher.random_iv
48
+ cipher.iv = iv
49
+ cipher.key = encryption_key
50
+ @data = cipher.update(JSON.dump(data)) + cipher.final
51
+ iv
52
+ end
53
+
54
+ def base64(chars)
55
+ Base64.urlsafe_encode64(chars)
56
+ end
57
+
58
+ def encryption_key
59
+ @secret.encryption_key
60
+ end
61
+
62
+ def signing_key
63
+ @secret.signing_key
64
+ end
65
+
66
+ def encrypt?
67
+ @encrypt
68
+ end
69
+
33
70
  end
34
71
  end
@@ -0,0 +1,20 @@
1
+ module Fernet
2
+ class Secret
3
+ def initialize(secret, encrypt)
4
+ @secret = secret
5
+ @encrypt = encrypt
6
+ end
7
+
8
+ def encryption_key
9
+ @secret.byteslice(@secret.bytesize/2, @secret.bytesize)
10
+ end
11
+
12
+ def signing_key
13
+ if @encrypt
14
+ @secret.byteslice(0, @secret.bytesize/2)
15
+ else
16
+ @secret
17
+ end
18
+ end
19
+ end
20
+ end
@@ -5,12 +5,12 @@ require 'date'
5
5
 
6
6
  module Fernet
7
7
  class Verifier
8
-
9
8
  attr_reader :token, :data
10
9
  attr_writer :seconds_valid
11
10
 
12
- def initialize(secret)
13
- @secret = secret
11
+ def initialize(secret, decrypt)
12
+ @secret = Secret.new(secret, decrypt)
13
+ @decrypt = decrypt
14
14
  end
15
15
 
16
16
  def verify_token(token)
@@ -35,9 +35,17 @@ module Fernet
35
35
  attr_reader :secret
36
36
 
37
37
  def deconstruct
38
- @data = JSON.parse(Base64.decode64(token))
39
- @received_signature = @data.delete('signature')
40
- @regenerated_mac = OpenSSL::HMAC.hexdigest('sha256', JSON.dump(@data), secret)
38
+ parts = @token.split('|')
39
+ if decrypt?
40
+ encrypted_data, iv, @received_signature = *parts
41
+ @data = JSON.parse(decrypt!(encrypted_data, Base64.urlsafe_decode64(iv)))
42
+ signing_blob = "#{encrypted_data}|#{iv}"
43
+ else
44
+ encoded_data, @received_signature = *parts
45
+ signing_blob = encoded_data
46
+ @data = JSON.parse(Base64.urlsafe_decode64(encoded_data))
47
+ end
48
+ @regenerated_mac = OpenSSL::HMAC.hexdigest('sha256', signing_blob, signing_key)
41
49
  end
42
50
 
43
51
  def token_recent_enough?
@@ -51,5 +59,26 @@ module Fernet
51
59
  accum |= byte ^ regenerated_bytes.shift
52
60
  end.zero?
53
61
  end
62
+
63
+ def decrypt!(encrypted_data, iv)
64
+ decipher = OpenSSL::Cipher.new('AES-128-CBC')
65
+ decipher.decrypt
66
+ decipher.iv = iv
67
+ decipher.key = encryption_key
68
+ decipher.update(Base64.urlsafe_decode64(encrypted_data)) + decipher.final
69
+ end
70
+
71
+ def encryption_key
72
+ @secret.encryption_key
73
+ end
74
+
75
+ def signing_key
76
+ @secret.signing_key
77
+ end
78
+
79
+ def decrypt?
80
+ @decrypt
81
+ end
82
+
54
83
  end
55
84
  end
@@ -1,3 +1,3 @@
1
1
  module Fernet
2
- VERSION = "0.1"
2
+ VERSION = "1.0"
3
3
  end
data/spec/fernet_spec.rb CHANGED
@@ -6,7 +6,8 @@ describe Fernet do
6
6
  { email: 'harold@heroku.com', id: '123', arbitrary: 'data' }
7
7
  end
8
8
 
9
- let(:secret) { 'sekrit123' }
9
+ let(:secret) { 'JrdICDH6x3M7duQeM8dJEMK4Y5TkBIsYDw1lPy35RiY=' }
10
+ let(:bad_secret) { 'jrdICDH6x3M7duQeM8dJEMK4Y5TkBIsYDw1lPy35RiY=' }
10
11
 
11
12
  it 'can verify tokens it generates' do
12
13
  token = Fernet.generate(secret) do |generator|
@@ -23,7 +24,7 @@ describe Fernet do
23
24
  generator.data = token_data
24
25
  end
25
26
 
26
- Fernet.verify('bad', token) do |verifier|
27
+ Fernet.verify(bad_secret, token) do |verifier|
27
28
  verifier.data['email'] == 'harold@heroku.com'
28
29
  end.should be_false
29
30
  end
@@ -33,7 +34,7 @@ describe Fernet do
33
34
  generator.data = token_data
34
35
  end
35
36
 
36
- Fernet.verify('bad', token) do |verifier|
37
+ Fernet.verify(bad_secret, token) do |verifier|
37
38
  verifier.data['email'] == 'harold@gmail.com'
38
39
  end.should be_false
39
40
  end
@@ -43,7 +44,7 @@ describe Fernet do
43
44
  generator.data = token_data
44
45
  end
45
46
 
46
- Fernet.verify('bad', token) do |verifier|
47
+ Fernet.verify(bad_secret, token) do |verifier|
47
48
  verifier.seconds_valid = 0
48
49
  end.should be_false
49
50
  end
@@ -61,4 +62,31 @@ describe Fernet do
61
62
 
62
63
  Fernet.verify(secret, token).should be_true
63
64
  end
65
+
66
+ it 'can encrypt the payload' do
67
+ token = Fernet.generate(secret, true) do |generator|
68
+ generator.data['password'] = 'password1'
69
+ end
70
+
71
+ payload = Base64.decode64(token)
72
+ payload.should_not match /password1/
73
+
74
+ Fernet.verify(secret, token) do |verifier|
75
+ verifier.data['password'].should == 'password1'
76
+ end
77
+ end
78
+
79
+ it 'does not encrypt when asked nicely' do
80
+ token = Fernet.generate(secret, false) do |generator|
81
+ generator.data['password'] = 'password1'
82
+ end
83
+
84
+ payload = Base64.decode64(token)
85
+ payload.should match /password1/
86
+
87
+ Fernet.verify(secret, token, false) do |verifier|
88
+ verifier.data['password'].should == 'password1'
89
+ end
90
+ end
91
+
64
92
  end
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: '0.1'
4
+ version: '1.0'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-23 00:00:00.000000000 Z
12
+ date: 2012-08-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &70098514672160 !ruby/object:Gem::Requirement
16
+ requirement: &70135148211780 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70098514672160
24
+ version_requirements: *70135148211780
25
25
  description: Delicious HMAC Digest(if) authentication
26
26
  email:
27
27
  - harold.gimenez@gmail.com
@@ -38,6 +38,7 @@ files:
38
38
  - fernet.gemspec
39
39
  - lib/fernet.rb
40
40
  - lib/fernet/generator.rb
41
+ - lib/fernet/secret.rb
41
42
  - lib/fernet/verifier.rb
42
43
  - lib/fernet/version.rb
43
44
  - spec/fernet_spec.rb