slosilo 0.1.2 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/slosilo/adapters/file_adapter.rb +34 -0
- data/lib/slosilo/adapters/sequel_adapter/migration.rb +4 -1
- data/lib/slosilo/attr_encrypted.rb +1 -1
- data/lib/slosilo/key.rb +7 -0
- data/lib/slosilo/keystore.rb +12 -8
- data/lib/slosilo/version.rb +1 -1
- data/spec/file_adapter_spec.rb +72 -0
- data/spec/key_spec.rb +11 -1
- data/spec/slosilo_spec.rb +19 -0
- data/spec/spec_helper.rb +1 -0
- metadata +6 -3
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'slosilo/adapters/abstract_adapter'
|
2
|
+
|
3
|
+
module Slosilo
|
4
|
+
module Adapters
|
5
|
+
class FileAdapter < AbstractAdapter
|
6
|
+
attr_reader :dir
|
7
|
+
|
8
|
+
def initialize(dir)
|
9
|
+
@dir = dir
|
10
|
+
@keys = {}
|
11
|
+
Dir[File.join(@dir, "*.key")].each do |f|
|
12
|
+
key = Slosilo::EncryptedAttributes.decrypt File.read(f)
|
13
|
+
@keys[File.basename(f, '.key')] = key
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def put_key id, value
|
18
|
+
raise "id should not contain a period" if id.index('.')
|
19
|
+
fname = File.join(dir, "#{id}.key")
|
20
|
+
File.write(fname, Slosilo::EncryptedAttributes.encrypt(value))
|
21
|
+
File.chmod(0400, fname)
|
22
|
+
@keys[id] = value
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_key id
|
26
|
+
@keys[id]
|
27
|
+
end
|
28
|
+
|
29
|
+
def each(&block)
|
30
|
+
@keys.each(&block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -15,7 +15,10 @@ module Slosilo
|
|
15
15
|
|
16
16
|
# Create the table for holding keys
|
17
17
|
def create_keystore_table
|
18
|
-
create_table
|
18
|
+
# docs say to not use create_table? in migration;
|
19
|
+
# but we really want this to be robust in case there are any previous installs
|
20
|
+
# and we can't use table_exists? because it rolls back
|
21
|
+
create_table? keystore_table do
|
19
22
|
String :id, primary_key: true
|
20
23
|
# Note: currently only postgres is supported
|
21
24
|
bytea :key, null: false
|
data/lib/slosilo/key.rb
CHANGED
@@ -58,12 +58,15 @@ module Slosilo
|
|
58
58
|
def signed_token data
|
59
59
|
token = { "data" => data, "timestamp" => Time.new.utc.to_s }
|
60
60
|
token["signature"] = Base64::urlsafe_encode64(sign token)
|
61
|
+
token["key"] = fingerprint
|
61
62
|
token
|
62
63
|
end
|
63
64
|
|
64
65
|
def token_valid? token, expiry = 8 * 60
|
65
66
|
token = token.clone
|
66
67
|
signature = Base64::urlsafe_decode64(token.delete "signature")
|
68
|
+
expected_key = token.delete "key"
|
69
|
+
return false if expected_key and expected_key != fingerprint
|
67
70
|
(Time.parse(token["timestamp"]) + expiry > Time.now) && verify_signature(token, signature)
|
68
71
|
end
|
69
72
|
|
@@ -72,6 +75,10 @@ module Slosilo
|
|
72
75
|
key.private_encrypt(hash_function.digest(_salt + value)) + _salt
|
73
76
|
end
|
74
77
|
|
78
|
+
def fingerprint
|
79
|
+
OpenSSL::Digest::MD5.hexdigest key.public_key.to_der
|
80
|
+
end
|
81
|
+
|
75
82
|
private
|
76
83
|
def stringify value
|
77
84
|
case value
|
data/lib/slosilo/keystore.rb
CHANGED
@@ -15,18 +15,15 @@ module Slosilo
|
|
15
15
|
key && Key.new(key)
|
16
16
|
end
|
17
17
|
|
18
|
-
def each
|
19
|
-
adapter.each(
|
18
|
+
def each &_
|
19
|
+
adapter.each { |k, v| yield k, Key.new(v) }
|
20
20
|
end
|
21
21
|
|
22
22
|
def any? &block
|
23
|
-
|
24
|
-
|
25
|
-
throw :found if block.call(Key.new(k))
|
26
|
-
end
|
27
|
-
return false
|
23
|
+
each do |_, k|
|
24
|
+
return true if yield k
|
28
25
|
end
|
29
|
-
|
26
|
+
return false
|
30
27
|
end
|
31
28
|
end
|
32
29
|
|
@@ -51,6 +48,13 @@ module Slosilo
|
|
51
48
|
keystore.any? { |k| k.token_valid? token }
|
52
49
|
end
|
53
50
|
|
51
|
+
def token_signer token
|
52
|
+
each do |id, key|
|
53
|
+
return id if key.token_valid? token
|
54
|
+
end
|
55
|
+
return nil
|
56
|
+
end
|
57
|
+
|
54
58
|
attr_accessor :adapter
|
55
59
|
|
56
60
|
private
|
data/lib/slosilo/version.rb
CHANGED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'slosilo/adapters/file_adapter'
|
4
|
+
|
5
|
+
describe Slosilo::Adapters::FileAdapter do
|
6
|
+
let(:dir) {
|
7
|
+
require 'tmpdir'
|
8
|
+
Dir.mktmpdir
|
9
|
+
}
|
10
|
+
let(:adapter) { Slosilo::Adapters::FileAdapter.new dir }
|
11
|
+
subject { adapter }
|
12
|
+
|
13
|
+
describe "#get_key" do
|
14
|
+
context "when given key does not exist" do
|
15
|
+
it "returns nil" do
|
16
|
+
subject.get_key(:whatever).should_not be
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#put_key" do
|
22
|
+
let(:key) { "key" }
|
23
|
+
context "unacceptable id" do
|
24
|
+
let(:id) { "foo.bar" }
|
25
|
+
it "isn't accepted" do
|
26
|
+
lambda { subject.put_key id, key }.should raise_error
|
27
|
+
end
|
28
|
+
end
|
29
|
+
context "acceptable id" do
|
30
|
+
let(:id) { "id" }
|
31
|
+
let(:key_encrypted) { "encrypted key" }
|
32
|
+
let(:fname) { "#{dir}/#{id}.key" }
|
33
|
+
it "creates the key" do
|
34
|
+
Slosilo::EncryptedAttributes.should_receive(:encrypt).with(key).and_return key_encrypted
|
35
|
+
File.should_receive(:write).with(fname, key_encrypted)
|
36
|
+
File.should_receive(:chmod).with(0400, fname)
|
37
|
+
subject.put_key id, key
|
38
|
+
subject.instance_variable_get("@keys")[id].should == key
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "#each" do
|
44
|
+
before { adapter.instance_variable_set("@keys", one: :onek, two: :twok) }
|
45
|
+
|
46
|
+
it "iterates over each key" do
|
47
|
+
results = []
|
48
|
+
adapter.each { |id,k| results << { id => k } }
|
49
|
+
results.should == [ { one: :onek}, {two: :twok } ]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe 'key store' do
|
54
|
+
let(:key) { 'fake key' }
|
55
|
+
let(:id) { 'some id' }
|
56
|
+
|
57
|
+
before do
|
58
|
+
Slosilo::encryption_key = Slosilo::Symmetric.new.random_key
|
59
|
+
pre_adapter = Slosilo::Adapters::FileAdapter.new dir
|
60
|
+
pre_adapter.put_key(id, key)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "loads and decrypts the key" do
|
64
|
+
adapter.get_key(id).should == key
|
65
|
+
end
|
66
|
+
it "enumerates the keys" do
|
67
|
+
results = []
|
68
|
+
adapter.each { |id,k| results << { id => k } }
|
69
|
+
results.should == [ { id => key } ]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/spec/key_spec.rb
CHANGED
@@ -6,6 +6,7 @@ describe Slosilo::Key do
|
|
6
6
|
subject { key }
|
7
7
|
its(:to_der) { should == rsa.to_der }
|
8
8
|
its(:to_s) { should == rsa.public_key.to_pem }
|
9
|
+
its(:fingerprint) { should == key_fingerprint }
|
9
10
|
|
10
11
|
let(:plaintext) { 'quick brown fox jumped over the lazy dog' }
|
11
12
|
describe '#encrypt' do
|
@@ -79,7 +80,7 @@ describe Slosilo::Key do
|
|
79
80
|
let(:signature) { "signature" }
|
80
81
|
let(:salt) { 'a pinch of salt' }
|
81
82
|
let(:expected_signature) { Base64::urlsafe_encode64 "\xB0\xCE{\x9FP\xEDV\x9C\xE7b\x8B[\xFAil\x87^\x96\x17Z\x97\x1D\xC2?B\x96\x9C\x8Ep-\xDF_\x8F\xC21\xD9^\xBC\n\x16\x04\x8DJ\xF6\xAF-\xEC\xAD\x03\xF9\xEE:\xDF\xB5\x8F\xF9\xF6\x81m\xAB\x9C\xAB1\x1E\x837\x8C\xFB\xA8P\xA8<\xEA\x1Dx\xCEd\xED\x84f\xA7\xB5t`\x96\xCC\x0F\xA9t\x8B\x9Fo\xBF\x92K\xFA\xFD\xC5?\x8F\xC68t\xBC\x9F\xDE\n$\xCA\xD2\x8F\x96\x0EtX2\x8Cl\x1E\x8Aa\r\x8D\xCAi\x86\x1A\xBD\x1D\xF7\xBC\x8561j\x91YlO\xFA(\x98\x10iq\xCC\xAF\x9BV\xC6\v\xBC\x10Xm\xCD\xFE\xAD=\xAA\x95,\xB4\xF7\xE8W\xB8\x83;\x81\x88\xE6\x01\xBA\xA5F\x91\x17\f\xCE\x80\x8E\v\x83\x9D<\x0E\x83\xF6\x8D\x03\xC0\xE8A\xD7\x90i\x1D\x030VA\x906D\x10\xA0\xDE\x12\xEF\x06M\xD8\x8B\xA9W\xC8\x9DTc\x8AJ\xA4\xC0\xD3!\xFA\x14\x89\xD1p\xB4J7\xA5\x04\xC2l\xDC8<\x04Y\xD8\xA4\xFB[\x89\xB1\xEC\xDA\xB8\xD7\xEA\x03Ja pinch of salt" }
|
82
|
-
let(:expected_token) {
|
83
|
+
let(:expected_token) { token_to_sign.merge "signature" => expected_signature, "key" => key_fingerprint }
|
83
84
|
before do
|
84
85
|
key.stub salt: salt
|
85
86
|
Time.stub new: time
|
@@ -95,6 +96,15 @@ describe Slosilo::Key do
|
|
95
96
|
before { Time.stub now: Time.new(2012,1,1,1,2,1,0) }
|
96
97
|
subject { key.token_valid? token }
|
97
98
|
it { should be_true }
|
99
|
+
|
100
|
+
it "doesn't check signature on the advisory key field" do
|
101
|
+
key.token_valid?(token.merge "key" => key_fingerprint).should be_true
|
102
|
+
end
|
103
|
+
|
104
|
+
it "rejects the token if the key field is present and doesn't match" do
|
105
|
+
key.token_valid?(token.merge "key" => "this is not the key you are looking for").should_not be_true
|
106
|
+
end
|
107
|
+
|
98
108
|
context "when token is 1 hour old" do
|
99
109
|
before { Time.stub now: Time.new(2012,1,1,2,1,1,0) }
|
100
110
|
it { should be_false }
|
data/spec/slosilo_spec.rb
CHANGED
@@ -55,4 +55,23 @@ describe Slosilo do
|
|
55
55
|
it { should be_true }
|
56
56
|
end
|
57
57
|
end
|
58
|
+
|
59
|
+
describe '.token_signer' do
|
60
|
+
let(:token) { double "token" }
|
61
|
+
let(:key_one) { double "key", token_valid?: false }
|
62
|
+
let(:other_key) { double "another key", token_valid?: false }
|
63
|
+
|
64
|
+
before do
|
65
|
+
subject.stub(:each).and_yield('test', key_one).and_yield('other', other_key)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "returns nil when token doesn't have a valid signature from any known key" do
|
69
|
+
subject.token_signer(token).should_not be
|
70
|
+
end
|
71
|
+
|
72
|
+
it "returns the name of the key which validates the token" do
|
73
|
+
other_key.stub(:token_valid?).with(token).and_return true
|
74
|
+
subject.token_signer(token).to_s.should == 'other'
|
75
|
+
end
|
76
|
+
end
|
58
77
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -41,6 +41,7 @@ Dg1ikwi8GUF4HPZe9DyhXgDhg19wM/qcpjX8bSypsUWHWP+FanhjdWU=
|
|
41
41
|
-----END RSA PRIVATE KEY-----
|
42
42
|
""" }
|
43
43
|
let (:key) { Slosilo::Key.new rsa.to_der }
|
44
|
+
let (:key_fingerprint) { "d28e3a347e368416b3129a40c1b887fe" }
|
44
45
|
|
45
46
|
def self.mock_own_key
|
46
47
|
before { Slosilo.stub(:[]).with(:own).and_return key }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slosilo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.2
|
5
5
|
prerelease:
|
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-04-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -122,6 +122,7 @@ files:
|
|
122
122
|
- Rakefile
|
123
123
|
- lib/slosilo.rb
|
124
124
|
- lib/slosilo/adapters/abstract_adapter.rb
|
125
|
+
- lib/slosilo/adapters/file_adapter.rb
|
125
126
|
- lib/slosilo/adapters/mock_adapter.rb
|
126
127
|
- lib/slosilo/adapters/sequel_adapter.rb
|
127
128
|
- lib/slosilo/adapters/sequel_adapter/migration.rb
|
@@ -135,6 +136,7 @@ files:
|
|
135
136
|
- lib/slosilo/version.rb
|
136
137
|
- lib/tasks/slosilo.rake
|
137
138
|
- slosilo.gemspec
|
139
|
+
- spec/file_adapter_spec.rb
|
138
140
|
- spec/http_request_spec.rb
|
139
141
|
- spec/http_stack_spec.rb
|
140
142
|
- spec/key_spec.rb
|
@@ -165,7 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
165
167
|
version: '0'
|
166
168
|
segments:
|
167
169
|
- 0
|
168
|
-
hash:
|
170
|
+
hash: 2238471852209156981
|
169
171
|
requirements: []
|
170
172
|
rubyforge_project:
|
171
173
|
rubygems_version: 1.8.24
|
@@ -173,6 +175,7 @@ signing_key:
|
|
173
175
|
specification_version: 3
|
174
176
|
summary: Store SSL keys in a database
|
175
177
|
test_files:
|
178
|
+
- spec/file_adapter_spec.rb
|
176
179
|
- spec/http_request_spec.rb
|
177
180
|
- spec/http_stack_spec.rb
|
178
181
|
- spec/key_spec.rb
|