fernet 0.1 → 1.0

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