encrypted_cookie 0.0.1 → 0.0.2

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.tar.gz.sig CHANGED
Binary file
data/Manifest CHANGED
@@ -1,4 +1,7 @@
1
+ Manifest
2
+ README.markdown
1
3
  Rakefile
2
- encrypted_cookie_spec.rb
4
+ encrypted_cookie.gemspec
3
5
  lib/encrypted_cookie.rb
4
- Manifest
6
+ spec/encrypted_cookie_spec.rb
7
+ test/demo.rb
File without changes
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
2
2
  require 'rake'
3
3
  require 'echoe'
4
4
 
5
- Echoe.new('encrypted_cookie', '0.0.1') do |p|
5
+ Echoe.new('encrypted_cookie', '0.0.2') do |p|
6
6
  p.description = "Encrypted session cookies for Rack"
7
7
  p.url = "http://github.com/cvonkleist/encrypted_cookie"
8
8
  p.author = "Christian von Kleist"
@@ -2,18 +2,18 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{encrypted_cookie}
5
- s.version = "0.0.1"
5
+ s.version = "0.0.2"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Christian von Kleist"]
9
9
  s.cert_chain = ["/home/cvk/.gemcert/gem-public_cert.pem"]
10
- s.date = %q{2011-03-01}
10
+ s.date = %q{2011-03-02}
11
11
  s.description = %q{Encrypted session cookies for Rack}
12
12
  s.email = %q{cvonkleist at-a-place-called gmail.com}
13
- s.extra_rdoc_files = ["lib/encrypted_cookie.rb"]
14
- s.files = ["Rakefile", "encrypted_cookie_spec.rb", "lib/encrypted_cookie.rb", "Manifest", "encrypted_cookie.gemspec"]
13
+ s.extra_rdoc_files = ["README.markdown", "lib/encrypted_cookie.rb"]
14
+ s.files = ["Manifest", "README.markdown", "Rakefile", "encrypted_cookie.gemspec", "lib/encrypted_cookie.rb", "spec/encrypted_cookie_spec.rb", "test/demo.rb"]
15
15
  s.homepage = %q{http://github.com/cvonkleist/encrypted_cookie}
16
- s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Encrypted_cookie"]
16
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Encrypted_cookie", "--main", "README.markdown"]
17
17
  s.require_paths = ["lib"]
18
18
  s.rubyforge_project = %q{encrypted_cookie}
19
19
  s.rubygems_version = %q{1.3.7}
@@ -1,27 +1,78 @@
1
1
  require 'openssl'
2
+ require 'rack/session/cookie'
2
3
 
3
4
  module Rack
4
5
  module Session
5
- class Cookie
6
- class AES
7
- def initialize(key)
8
- @key = key
6
+ class EncryptedCookie < Cookie
7
+ def initialize(app, options={})
8
+ @app = app
9
+ @key = options[:key] || "rack.session"
10
+ @secret = options[:secret]
11
+ fail "Error! A secret is required to use encrypted cookies. Do something like this:\n\nuse Rack::Session::EncryptedCookie, :secret => YOUR_VERY_LONG_VERY_RANDOM_SECRET_KEY_HERE" unless @secret
12
+ @default_options = {:domain => nil,
13
+ :path => "/",
14
+ :expire_after => nil}.merge(options)
15
+ end
16
+
17
+ def load_session(env)
18
+ request = Rack::Request.new(env)
19
+ session_data = request.cookies[@key]
20
+
21
+ if session_data
22
+ session_data = decrypt(session_data)
23
+ session_data, digest = session_data.split("--")
24
+ session_data = nil unless digest == generate_hmac(session_data)
9
25
  end
10
26
 
11
- def encode(str)
12
- aes = OpenSSL::Cipher::Cipher.new('aes-128-cbc').encrypt
13
- salt = OpenSSL::Random.random_bytes(aes.key_len)
14
- iv = OpenSSL::Random.random_bytes(aes.iv_len)
15
- [iv + (aes.update(str) << aes.final)].pack('m0')
27
+ begin
28
+ session_data = session_data.unpack("m*").first
29
+ session_data = Marshal.load(session_data)
30
+ env["rack.session"] = session_data
31
+ rescue
32
+ env["rack.session"] = Hash.new
16
33
  end
17
34
 
18
- def decode(str)
19
- str = str.unpack('m0').first
20
- aes = OpenSSL::Cipher::Cipher.new('aes-128-cbc').decrypt
21
- iv = str[0, aes.iv_len]
22
- crypted_text = str[aes.iv_len..-1]
23
- aes.update(crypted_text) << aes.final
35
+ env["rack.session.options"] = @default_options.dup
36
+ end
37
+
38
+ def commit_session(env, status, headers, body)
39
+ session_data = Marshal.dump(env["rack.session"])
40
+ session_data = [session_data].pack("m*")
41
+
42
+ session_data = "#{session_data}--#{generate_hmac(session_data)}"
43
+
44
+ session_data = encrypt(session_data)
45
+
46
+ if session_data.size > (4096 - @key.size)
47
+ env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K. Content dropped.")
48
+ else
49
+ options = env["rack.session.options"]
50
+ cookie = Hash.new
51
+ cookie[:value] = session_data
52
+ cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil?
53
+ Utils.set_cookie_header!(headers, @key, cookie.merge(options))
24
54
  end
55
+
56
+ [status, headers, body]
57
+ end
58
+
59
+ private
60
+
61
+ def encrypt(str)
62
+ aes = OpenSSL::Cipher::Cipher.new('aes-128-cbc').encrypt
63
+ aes.key = @secret
64
+ salt = OpenSSL::Random.random_bytes(aes.key_len)
65
+ iv = OpenSSL::Random.random_bytes(aes.iv_len)
66
+ [iv + (aes.update(str) << aes.final)].pack('m0')
67
+ end
68
+
69
+ def decrypt(str)
70
+ str = str.unpack('m0').first
71
+ aes = OpenSSL::Cipher::Cipher.new('aes-128-cbc').decrypt
72
+ aes.key = @secret
73
+ iv = str[0, aes.iv_len]
74
+ crypted_text = str[aes.iv_len..-1]
75
+ aes.update(crypted_text) << aes.final
25
76
  end
26
77
  end
27
78
  end
@@ -0,0 +1,95 @@
1
+ require 'rack/test'
2
+ require 'sinatra'
3
+ require 'rspec'
4
+ require 'ruby-debug'
5
+ require 'cgi'
6
+ require File.dirname(__FILE__) + '/../lib/encrypted_cookie'
7
+
8
+ include Rack::Session
9
+
10
+ RSpec.configure do |conf|
11
+ conf.include Rack::Test::Methods
12
+ end
13
+
14
+ class EncryptedApp < Sinatra::Application
15
+ use Rack::Session::EncryptedCookie, :secret => 'foo' * 10
16
+ get '/' do
17
+ "session: " + session.inspect
18
+ end
19
+ get '/set/:key/:value' do
20
+ session[params[:key]] = params[:value]
21
+ "all set"
22
+ end
23
+ end
24
+
25
+ # this app has cookie integrity protection, but not encryption
26
+ class UnencryptedApp < Sinatra::Application
27
+ use Rack::Session::Cookie, :secret => 'foo' * 10
28
+ get '/' do
29
+ "session: " + session.inspect
30
+ end
31
+ end
32
+
33
+ describe EncryptedCookie do
34
+ it 'should fail if no secret is specified' do
35
+ lambda { EncryptedCookie.new(nil) }.should raise_error(/A secret is required/)
36
+ lambda { EncryptedCookie.new(nil, :secret => 'foo') }.should_not raise_error(/A secret is required/)
37
+ end
38
+ end
39
+
40
+ describe EncryptedApp do
41
+ def app
42
+ EncryptedApp
43
+ end
44
+ it "should not include unencrypted marshal'd data" do
45
+ get '/'
46
+ last_response.body.should == 'session: {}'
47
+ last_response.headers['Set-Cookie'].should_not include("BAh7AA")
48
+ end
49
+ it "should make decryptable session data" do
50
+ get '/set/foo/bar'
51
+ last_response.body.should == 'all set'
52
+ get '/'
53
+ last_response.body.should == 'session: {"foo"=>"bar"}'
54
+ last_response.headers['Set-Cookie'].should_not include("BAh7AA")
55
+
56
+ data = last_response.headers['Set-Cookie'][/rack.session=(.*?);/, 1]
57
+ str = CGI.unescape(data).unpack('m0').first
58
+ aes = OpenSSL::Cipher::Cipher.new('aes-128-cbc').decrypt
59
+ aes.key = 'foo' * 10
60
+ iv = str[0, aes.iv_len]
61
+ crypted_text = str[aes.iv_len..-1]
62
+
63
+ plaintext = (aes.update(crypted_text) << aes.final)
64
+ base64_marshal_data, hmac = plaintext.split('--')
65
+ session_hash = Marshal.load(base64_marshal_data.unpack('m0').first)
66
+ session_hash.should == {"foo" => "bar"}
67
+ end
68
+ it "should make encrypted session data that can't be decrypted with the wrong key" do
69
+ get '/set/foo/bar'
70
+ last_response.body.should == 'all set'
71
+ get '/'
72
+ last_response.body.should == 'session: {"foo"=>"bar"}'
73
+ last_response.headers['Set-Cookie'].should_not include("BAh7AA")
74
+
75
+ data = last_response.headers['Set-Cookie'][/rack.session=(.*?);/, 1]
76
+ str = CGI.unescape(data).unpack('m0').first
77
+ aes = OpenSSL::Cipher::Cipher.new('aes-128-cbc').decrypt
78
+ aes.key = 'bar' * 10
79
+ iv = str[0, aes.iv_len]
80
+ crypted_text = str[aes.iv_len..-1]
81
+
82
+ lambda { plaintext = (aes.update(crypted_text) << aes.final) }.should raise_error("bad decrypt")
83
+ end
84
+ end
85
+
86
+ describe UnencryptedApp do
87
+ def app
88
+ UnencryptedApp
89
+ end
90
+ it "should include unencrypted marshal'd data" do
91
+ get '/'
92
+ last_response.body.should == 'session: {}'
93
+ last_response.headers['Set-Cookie'].should include("BAh7AA")
94
+ end
95
+ end
@@ -0,0 +1,15 @@
1
+ require 'sinatra'
2
+ require '../lib/encrypted_cookie'
3
+
4
+ use Rack::Session::EncryptedCookie,
5
+ :key => 'rack.session',
6
+ :secret => 'ASDFASDFASDJFsakdfji2j3oij2o3ij4l12kj3'
7
+
8
+ get '/' do
9
+ "session = " + session.inspect
10
+ end
11
+
12
+ get '/set/:data' do
13
+ session[:data] = params[:data]
14
+ redirect '/'
15
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: encrypted_cookie
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 27
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 1
10
- version: 0.0.1
9
+ - 2
10
+ version: 0.0.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Christian von Kleist
@@ -36,7 +36,7 @@ cert_chain:
36
36
  DnOM3kD7rptG2g==
37
37
  -----END CERTIFICATE-----
38
38
 
39
- date: 2011-03-01 00:00:00 -05:00
39
+ date: 2011-03-02 00:00:00 -05:00
40
40
  default_executable:
41
41
  dependencies: []
42
42
 
@@ -47,13 +47,16 @@ executables: []
47
47
  extensions: []
48
48
 
49
49
  extra_rdoc_files:
50
+ - README.markdown
50
51
  - lib/encrypted_cookie.rb
51
52
  files:
52
- - Rakefile
53
- - encrypted_cookie_spec.rb
54
- - lib/encrypted_cookie.rb
55
53
  - Manifest
54
+ - README.markdown
55
+ - Rakefile
56
56
  - encrypted_cookie.gemspec
57
+ - lib/encrypted_cookie.rb
58
+ - spec/encrypted_cookie_spec.rb
59
+ - test/demo.rb
57
60
  has_rdoc: true
58
61
  homepage: http://github.com/cvonkleist/encrypted_cookie
59
62
  licenses: []
@@ -64,6 +67,8 @@ rdoc_options:
64
67
  - --inline-source
65
68
  - --title
66
69
  - Encrypted_cookie
70
+ - --main
71
+ - README.markdown
67
72
  require_paths:
68
73
  - lib
69
74
  required_ruby_version: !ruby/object:Gem::Requirement
metadata.gz.sig CHANGED
Binary file
@@ -1,11 +0,0 @@
1
- require 'encrypted_cookie'
2
-
3
- include Rack::Session
4
-
5
- describe Cookie::AES do
6
- it 'should pass the encryption test' do
7
- a = Cookie::AES.new('foo')
8
- enc = a.encode("bar")
9
- a.decode(enc).should == 'bar'
10
- end
11
- end