slosilo 0.1.2 → 0.2.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/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
|