slosilo 1.0.0 → 2.2.1

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.
@@ -7,7 +7,7 @@ describe Slosilo::Keystore do
7
7
  describe '#put' do
8
8
  it "handles Slosilo::Keys" do
9
9
  subject.put(:test, key)
10
- adapter['test'].to_der.should == rsa.to_der
10
+ expect(adapter['test'].to_der).to eq(rsa.to_der)
11
11
  end
12
12
 
13
13
  it "refuses to store a key with a nil id" do
@@ -19,7 +19,7 @@ describe Slosilo::Keystore do
19
19
  end
20
20
 
21
21
  it "passes the Slosilo key to the adapter" do
22
- adapter.should_receive(:put_key).with "test", key
22
+ expect(adapter).to receive(:put_key).with "test", key
23
23
  subject.put :test, key
24
24
  end
25
25
  end
@@ -4,6 +4,16 @@ describe Slosilo::Random do
4
4
  subject { Slosilo::Random }
5
5
  let(:other_salt) { Slosilo::Random::salt }
6
6
 
7
- its('salt.length') { should == 32 }
8
- its(:salt) { should_not == other_salt }
7
+ describe '#salt' do
8
+ subject { super().salt }
9
+ describe '#length' do
10
+ subject { super().length }
11
+ it { is_expected.to eq(32) }
12
+ end
13
+ end
14
+
15
+ describe '#salt' do
16
+ subject { super().salt }
17
+ it { is_expected.not_to eq(other_salt) }
18
+ end
9
19
  end
@@ -8,21 +8,21 @@ describe Slosilo::Adapters::SequelAdapter do
8
8
  include_context "with example key"
9
9
 
10
10
  let(:model) { double "model" }
11
- before { subject.stub create_model: model }
11
+ before { allow(subject).to receive_messages create_model: model }
12
12
 
13
13
  describe "#get_key" do
14
14
  context "when given key does not exist" do
15
- before { model.stub :[] => nil }
15
+ before { allow(model).to receive_messages :[] => nil }
16
16
  it "returns nil" do
17
- subject.get_key(:whatever).should_not be
17
+ expect(subject.get_key(:whatever)).not_to be
18
18
  end
19
19
  end
20
20
 
21
21
  context "when it exists" do
22
22
  let(:id) { "id" }
23
- before { model.stub(:[]).with(id).and_return (double "key entry", id: id, key: rsa.to_der) }
23
+ before { allow(model).to receive(:[]).with(id).and_return (double "key entry", id: id, key: rsa.to_der) }
24
24
  it "returns it" do
25
- subject.get_key(id).should == key
25
+ expect(subject.get_key(id)).to eq(key)
26
26
  end
27
27
  end
28
28
  end
@@ -30,14 +30,14 @@ describe Slosilo::Adapters::SequelAdapter do
30
30
  describe "#put_key" do
31
31
  let(:id) { "id" }
32
32
  it "creates the key" do
33
- model.should_receive(:create).with id: id, key: key.to_der
34
- model.stub columns: [:id, :key]
33
+ expect(model).to receive(:create).with id: id, key: key.to_der
34
+ allow(model).to receive_messages columns: [:id, :key]
35
35
  subject.put_key id, key
36
36
  end
37
37
 
38
38
  it "adds the fingerprint if feasible" do
39
- model.should_receive(:create).with id: id, key: key.to_der, fingerprint: key.fingerprint
40
- model.stub columns: [:id, :key, :fingerprint]
39
+ expect(model).to receive(:create).with id: id, key: key.to_der, fingerprint: key.fingerprint
40
+ allow(model).to receive_messages columns: [:id, :key, :fingerprint]
41
41
  subject.put_key id, key
42
42
  end
43
43
  end
@@ -46,25 +46,21 @@ describe Slosilo::Adapters::SequelAdapter do
46
46
  describe "#each" do
47
47
  let(:one) { double("one", id: :one, key: :onek) }
48
48
  let(:two) { double("two", id: :two, key: :twok) }
49
- before { model.stub(:each).and_yield(one).and_yield(two) }
49
+ before { allow(model).to receive(:each).and_yield(one).and_yield(two) }
50
50
 
51
51
  it "iterates over each key" do
52
52
  results = []
53
- Slosilo::Key.stub(:new) {|x|x}
53
+ allow(Slosilo::Key).to receive(:new) {|x|x}
54
54
  adapter.each { |id,k| results << { id => k } }
55
- results.should == [ { one: :onek}, {two: :twok } ]
55
+ expect(results).to eq([ { one: :onek}, {two: :twok } ])
56
56
  end
57
57
  end
58
58
 
59
59
  shared_context "database" do
60
60
  let(:db) { Sequel.sqlite }
61
61
  before do
62
- subject.unstub :create_model
63
- begin
64
- Sequel::Model.cache_anonymous_models = false
65
- rescue NoMethodError # sequel 4.0 moved the method
66
- Sequel.cache_anonymous_models = false
67
- end
62
+ allow(subject).to receive(:create_model).and_call_original
63
+ Sequel::Model.cache_anonymous_models = false
68
64
  Sequel::Model.db = db
69
65
  end
70
66
  end
@@ -91,24 +87,24 @@ describe Slosilo::Adapters::SequelAdapter do
91
87
  before { subject.migrate! }
92
88
 
93
89
  it "supports look up by id" do
94
- subject.get_key("test").should == key
90
+ expect(subject.get_key("test")).to eq(key)
95
91
  end
96
92
 
97
93
  it "supports look up by fingerprint, without a warning" do
98
- $stderr.grab do
99
- subject.get_by_fingerprint(key.fingerprint).should == [key, 'test']
100
- end.should be_empty
94
+ expect($stderr.grab do
95
+ expect(subject.get_by_fingerprint(key.fingerprint)).to eq([key, 'test'])
96
+ end).to be_empty
101
97
  end
102
98
  end
103
99
 
104
100
  it "supports look up by id" do
105
- subject.get_key("test").should == key
101
+ expect(subject.get_key("test")).to eq(key)
106
102
  end
107
103
 
108
104
  it "supports look up by fingerprint, but issues a warning" do
109
- $stderr.grab do
110
- subject.get_by_fingerprint(key.fingerprint).should == [key, 'test']
111
- end.should_not be_empty
105
+ expect($stderr.grab do
106
+ expect(subject.get_by_fingerprint(key.fingerprint)).to eq([key, 'test'])
107
+ end).not_to be_empty
112
108
  end
113
109
  end
114
110
 
@@ -129,11 +125,11 @@ describe Slosilo::Adapters::SequelAdapter do
129
125
  end
130
126
 
131
127
  it "supports look up by id" do
132
- subject.get_key("test").should == key
128
+ expect(subject.get_key("test")).to eq(key)
133
129
  end
134
130
 
135
131
  it "supports look up by fingerprint" do
136
- subject.get_by_fingerprint(key.fingerprint).should == [key, 'test']
132
+ expect(subject.get_by_fingerprint(key.fingerprint)).to eq([key, 'test'])
137
133
  end
138
134
  end
139
135
 
@@ -141,7 +137,7 @@ describe Slosilo::Adapters::SequelAdapter do
141
137
  include_context "encryption key"
142
138
  include_context "current schema"
143
139
 
144
- it { should be_secure }
140
+ it { is_expected.to be_secure }
145
141
 
146
142
  it "saves the keys in encrypted form" do
147
143
  subject.put_key 'test', key
@@ -158,7 +154,7 @@ describe Slosilo::Adapters::SequelAdapter do
158
154
 
159
155
  include_context "current schema"
160
156
 
161
- it { should_not be_secure }
157
+ it { is_expected.not_to be_secure }
162
158
 
163
159
  it "refuses to store a private key" do
164
160
  expect { subject.put_key 'test', key }.to raise_error(Slosilo::Error::InsecureKeyStorage)
@@ -7,32 +7,32 @@ describe Slosilo do
7
7
 
8
8
  describe '[]' do
9
9
  it "returns a Slosilo::Key" do
10
- Slosilo[:test].should be_instance_of Slosilo::Key
10
+ expect(Slosilo[:test]).to be_instance_of Slosilo::Key
11
11
  end
12
12
 
13
13
  it "allows looking up by fingerprint" do
14
- Slosilo[fingerprint: key_fingerprint].should == key
14
+ expect(Slosilo[fingerprint: key_fingerprint]).to eq(key)
15
15
  end
16
16
 
17
17
  context "when the requested key does not exist" do
18
18
  it "returns nil instead of creating a new key" do
19
- Slosilo[:aether].should_not be
19
+ expect(Slosilo[:aether]).not_to be
20
20
  end
21
21
  end
22
22
  end
23
23
 
24
24
  describe '.sign' do
25
25
  let(:own_key) { double "own key" }
26
- before { Slosilo.stub(:[]).with(:own).and_return own_key }
26
+ before { allow(Slosilo).to receive(:[]).with(:own).and_return own_key }
27
27
  let (:argument) { double "thing to sign" }
28
28
  it "fetches the own key and signs using that" do
29
- own_key.should_receive(:sign).with(argument)
29
+ expect(own_key).to receive(:sign).with(argument)
30
30
  Slosilo.sign argument
31
31
  end
32
32
  end
33
33
 
34
34
  describe '.token_valid?' do
35
- before { adapter['test'].stub token_valid?: false }
35
+ before { allow(adapter['test']).to receive_messages token_valid?: false }
36
36
  let(:key2) { double "key 2", token_valid?: false }
37
37
  let(:key3) { double "key 3", token_valid?: false }
38
38
  before do
@@ -44,19 +44,19 @@ describe Slosilo do
44
44
  subject { Slosilo.token_valid? token }
45
45
 
46
46
  context "when no key validates the token" do
47
- before { Slosilo::Key.stub new: (double "key", token_valid?: false) }
48
- it { should be_false }
47
+ before { allow(Slosilo::Key).to receive_messages new: (double "key", token_valid?: false) }
48
+ it { is_expected.to be_falsey }
49
49
  end
50
50
 
51
51
  context "when a key validates the token" do
52
52
  let(:valid_key) { double token_valid?: true }
53
53
  let(:invalid_key) { double token_valid?: true }
54
54
  before do
55
- Slosilo::Key.stub new: invalid_key
55
+ allow(Slosilo::Key).to receive_messages new: invalid_key
56
56
  adapter[:key2] = valid_key
57
57
  end
58
58
 
59
- it { should be_true }
59
+ it { is_expected.to be_truthy }
60
60
  end
61
61
  end
62
62
 
@@ -66,18 +66,18 @@ describe Slosilo do
66
66
  let(:token) {{ 'data' => 'foo', 'key' => key.fingerprint, 'signature' => 'XXX' }}
67
67
 
68
68
  context "and the signature is valid" do
69
- before { key.stub(:token_valid?).with(token).and_return true }
69
+ before { allow(key).to receive(:token_valid?).with(token).and_return true }
70
70
 
71
71
  it "returns the key id" do
72
- subject.token_signer(token).should == 'test'
72
+ expect(subject.token_signer(token)).to eq('test')
73
73
  end
74
74
  end
75
75
 
76
76
  context "and the signature is invalid" do
77
- before { key.stub(:token_valid?).with(token).and_return false }
77
+ before { allow(key).to receive(:token_valid?).with(token).and_return false }
78
78
 
79
79
  it "returns nil" do
80
- subject.token_signer(token).should_not be
80
+ expect(subject.token_signer(token)).not_to be
81
81
  end
82
82
  end
83
83
  end
@@ -85,7 +85,39 @@ describe Slosilo do
85
85
  context "when token doesn't match a key" do
86
86
  let(:token) {{ 'data' => 'foo', 'key' => "footprint", 'signature' => 'XXX' }}
87
87
  it "returns nil" do
88
- subject.token_signer(token).should_not be
88
+ expect(subject.token_signer(token)).not_to be
89
+ end
90
+ end
91
+
92
+ context "with JWT token" do
93
+ before do
94
+ expect(key).to receive(:validate_jwt) do |jwt|
95
+ expect(jwt.header).to eq 'kid' => key.fingerprint
96
+ expect(jwt.claims).to eq({})
97
+ expect(jwt.signature).to eq 'sig'
98
+ end
99
+ end
100
+
101
+ it "accepts pre-parsed JSON serialization" do
102
+ expect(Slosilo.token_signer(
103
+ 'protected' => 'eyJraWQiOiIxMDdiZGI4NTAxYzQxOWZhZDJmZGIyMGI0NjdkNGQwYTYyYTE2YTk4YzM1ZjJkYTBlYjNiMWZmOTI5Nzk1YWQ5In0=',
104
+ 'payload' => 'e30=',
105
+ 'signature' => 'c2ln'
106
+ )).to eq 'test'
107
+ end
108
+
109
+ it "accepts pre-parsed JWT token" do
110
+ expect(Slosilo.token_signer(Slosilo::JWT(
111
+ 'protected' => 'eyJraWQiOiIxMDdiZGI4NTAxYzQxOWZhZDJmZGIyMGI0NjdkNGQwYTYyYTE2YTk4YzM1ZjJkYTBlYjNiMWZmOTI5Nzk1YWQ5In0=',
112
+ 'payload' => 'e30=',
113
+ 'signature' => 'c2ln'
114
+ ))).to eq 'test'
115
+ end
116
+
117
+ it "accepts compact serialization" do
118
+ expect(Slosilo.token_signer(
119
+ 'eyJraWQiOiIxMDdiZGI4NTAxYzQxOWZhZDJmZGIyMGI0NjdkNGQwYTYyYTE2YTk4YzM1ZjJkYTBlYjNiMWZmOTI5Nzk1YWQ5In0=.e30=.c2ln'
120
+ )).to eq 'test'
89
121
  end
90
122
  end
91
123
  end
@@ -1,10 +1,6 @@
1
1
  require "simplecov"
2
2
  SimpleCov.start
3
3
 
4
- RSpec.configure do |c|
5
- c.treat_symbols_as_metadata_keys_with_true_values = true
6
- end
7
-
8
4
  require 'slosilo'
9
5
 
10
6
  shared_context "with mock adapter" do
@@ -45,7 +41,7 @@ Dg1ikwi8GUF4HPZe9DyhXgDhg19wM/qcpjX8bSypsUWHWP+FanhjdWU=
45
41
  -----END RSA PRIVATE KEY-----
46
42
  """ }
47
43
  let (:key) { Slosilo::Key.new rsa.to_der }
48
- let (:key_fingerprint) { "d28e3a347e368416b3129a40c1b887fe" }
44
+ let (:key_fingerprint) { "107bdb8501c419fad2fdb20b467d4d0a62a16a98c35f2da0eb3b1ff929795ad9" }
49
45
 
50
46
  let (:another_rsa) do
51
47
  OpenSSL::PKey::RSA.new """
@@ -80,20 +76,6 @@ ooQ2FuL0K6ukQfHPjuMswqi41lmVH8gIVqVC+QnImUCrGxH9WXWy
80
76
  end
81
77
 
82
78
  def self.mock_own_key
83
- before { Slosilo.stub(:[]).with(:own).and_return key }
84
- end
85
- end
86
-
87
- class RackEnvironmentInputMatcher
88
- def initialize expected
89
- @expected = expected
79
+ before { allow(Slosilo).to receive(:[]).with(:own).and_return key }
90
80
  end
91
-
92
- def == env
93
- env['rack.input'].read.should == @expected
94
- end
95
- end
96
-
97
- def rack_environment_with_input expected
98
- RackEnvironmentInputMatcher.new expected
99
81
  end
@@ -3,49 +3,71 @@ require 'spec_helper'
3
3
  describe Slosilo::Symmetric do
4
4
  # TODO transform it to class methods only?
5
5
  let(:plaintext) { "quick brown fox jumped over the lazy dog" }
6
+ let(:auth_data) { "some record id" }
6
7
  let(:key) { "^\xBAIv\xDB1\x0Fi\x04\x11\xFD\x14\xA7\xCD\xDFf\x93\xFE\x93}\v\x01\x11\x98\x14\xE0;\xC1\xE2 v\xA5".force_encoding("ASCII-8BIT") }
7
- let(:iv) { "\xA1\xFA#z\x16\x80R\xCC|\x0Fyc\xB7j\x17\xED".force_encoding("ASCII-8BIT") }
8
- let(:ciphertext) { "\xA1\xFA#z\x16\x80R\xCC|\x0Fyc\xB7j\x17\xED\x15\xC9r\xC9\xEE\xB9\xBC5\xB7\ni\x0F\f\xC8X\x80 h\a\xF4\xA6\xE3\x15\x9D\xF1-\xE5\bs\xF6\x02Z\x0F\xCD|S\x1A\xAA\x9At\xEFT\x17\xA5lT\x8C\xF3".force_encoding("ASCII-8BIT") }
8
+ let(:iv) { "\xD9\xABn\x01b\xFA\xBD\xC2\xE5\xEA\x01\xAC".force_encoding("ASCII-8BIT") }
9
+ let(:ciphertext) { "G^W1\x9C\xD4\xCC\x87\xD3\xFF\x86[\x0E3\xC0\xC8^\xD9\xABn\x01b\xFA\xBD\xC2\xE5\xEA\x01\xAC\x9E\xB9:\xF7\xD4ebeq\xDC \xC0sG\xA4\xAE,\xB8A|\x97\xBC\xFD\x85\xE1\xB93\x95>\xBD\n\x05\xFB\x15\x1F\x06#3M9".force_encoding('ASCII-8BIT') }
10
+
9
11
  describe '#encrypt' do
10
- it "encrypts with AES-256-CBC" do
11
- subject.stub random_iv: iv
12
- subject.encrypt(plaintext, key: key).should == ciphertext
12
+ it "encrypts with AES-256-GCM" do
13
+ allow(subject).to receive_messages random_iv: iv
14
+ expect(subject.encrypt(plaintext, key: key, aad: auth_data)).to eq(ciphertext)
13
15
  end
14
16
  end
15
17
 
16
18
  describe '#decrypt' do
17
- it "decrypts with AES-256-CBC" do
18
- subject.decrypt(ciphertext, key: key).should == plaintext
19
+ it "decrypts with AES-256-GCM" do
20
+ expect(subject.decrypt(ciphertext, key: key, aad: auth_data)).to eq(plaintext)
21
+ end
22
+
23
+
24
+ context "when the ciphertext has been messed with" do
25
+ let(:ciphertext) { "pwnd!" } # maybe we should do something more realistic like add some padding?
26
+ it "raises an exception" do
27
+ expect{ subject.decrypt(ciphertext, key: key, aad: auth_data)}.to raise_exception /Invalid version/
28
+ end
29
+ context "by adding a trailing 0" do
30
+ let(:new_ciphertext){ ciphertext + '\0' }
31
+ it "raises an exception" do
32
+ expect{ subject.decrypt(new_ciphertext, key: key, aad: auth_data) }.to raise_exception /Invalid version/
33
+ end
34
+ end
19
35
  end
20
-
21
- context "when ciphertext happens to end in a zero" do
22
- let(:ciphertext) { "\x7F\xD6\xEAb\xE56\a\xD3\xC5\xF2J\n\x8C\x8Fg\xB7-\\\x8A\fh\x18\xC8\x91\xB9 \x97\xC9\x12\xE6\xA6\xAE\xB1I\x1E\x80\xAB\xD8\xDC\xBD\xB6\xCD\x9A\xA3MH\xA8\xB0\xC7\xDA\x87\xA7c\xD75,\xD2A\xB8\x9E\xE3o\x04\x00" }
23
- let(:key) { "4pSuk1rAQyuHA5uUYaj0X0BsiPCFb9Nc8J03XA6V5/Y" }
24
- it "works correctly" do
25
- subject.decrypt(ciphertext, key: key).should == "R6KNTQ4aUivojbaqhgAqj1I4PaF8h/5/YcENy4uNbfk="
36
+
37
+ context "when no auth_data is given" do
38
+ let(:auth_data){""}
39
+ let(:ciphertext){ "Gm\xDAT\xE8I\x9F\xB7\xDC\xBB\x84\xD3Q#\x1F\xF4\x8C\aV\x93\x8F_\xC7\xBC87\xC9U\xF1\xAF\x8A\xD62\x1C5H\x86\x17\x19=B~Y*\xBC\x9D\eJeTx\x1F\x02l\t\t\xD3e\xA4\x11\x13y*\x95\x9F\xCD\xC4@\x9C"}
40
+
41
+ it "decrypts the message" do
42
+ expect(subject.decrypt(ciphertext, key: key, aad: auth_data)).to eq(plaintext)
43
+ end
44
+
45
+ context "and the ciphertext has been messed with" do
46
+ it "raises an exception" do
47
+ expect{ subject.decrypt(ciphertext + "\0\0\0", key: key, aad: auth_data)}.to raise_exception OpenSSL::Cipher::CipherError
48
+ end
26
49
  end
27
50
  end
28
51
 
29
- context "when the iv ends in space" do
30
- let(:ciphertext) { "\xC0\xDA#\xE9\xE1\xFD\xEDJ\xADs4P\xA9\xD6\x92 \xF7\xF8_M\xF6\x16\xC2i$\x8BT^\b\xA1\xB2L&\xE9\x80\x02[]6i\x9B\xD3\xC3\xED\xA9\xD1\x94\xE8\x15\xFD\xDA\xFEUj\xC5upH*\xBF\x82\x15le" }
31
- let(:key) { "4pSuk1rAQyuHA5uUYaj0X0BsiPCFb9Nc8J03XA6V5/Y" }
32
- it "works correctly" do
33
- subject.decrypt(ciphertext, key: key).should == "zGptmL3vd4obi1vqSiWHt/Ias2k+6qDtuq9vdow8jNA="
52
+ context "when the auth data doesn't match" do
53
+ let(:auth_data){ "asdf" }
54
+ it "raises an exception" do
55
+ expect{ subject.decrypt(ciphertext, key: key, aad: auth_data)}.to raise_exception OpenSSL::Cipher::CipherError
34
56
  end
35
57
  end
36
58
  end
37
59
 
38
60
  describe '#random_iv' do
39
61
  it "generates a random iv" do
40
- OpenSSL::Cipher.any_instance.should_receive(:random_iv).and_return :iv
41
- subject.random_iv.should == :iv
62
+ expect_any_instance_of(OpenSSL::Cipher).to receive(:random_iv).and_return :iv
63
+ expect(subject.random_iv).to eq(:iv)
42
64
  end
43
65
  end
44
66
 
45
67
  describe '#random_key' do
46
68
  it "generates a random key" do
47
- OpenSSL::Cipher.any_instance.should_receive(:random_key).and_return :key
48
- subject.random_key.should == :key
69
+ expect_any_instance_of(OpenSSL::Cipher).to receive(:random_key).and_return :key
70
+ expect(subject.random_key).to eq(:key)
49
71
  end
50
72
  end
51
73
  end
data/test.sh ADDED
@@ -0,0 +1,25 @@
1
+ #!/bin/bash -xe
2
+
3
+ iid=slosilo-test-$(date +%s)
4
+
5
+ docker build -t $iid -f - . << EOF
6
+ FROM ruby
7
+ WORKDIR /app
8
+ COPY Gemfile slosilo.gemspec ./
9
+ RUN bundle
10
+ COPY . ./
11
+ RUN bundle
12
+ EOF
13
+
14
+ cidfile=$(mktemp -u)
15
+ docker run --cidfile $cidfile -v /app/spec/reports $iid bundle exec rake jenkins || :
16
+
17
+ cid=$(cat $cidfile)
18
+
19
+ docker cp $cid:/app/spec/reports spec/
20
+ docker rm $cid
21
+
22
+ # untag, will use cache next time if available but no junk will be left
23
+ docker rmi $iid
24
+
25
+ rm $cidfile