slosilo 1.0.0 → 1.1.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d243023c094f7eaec65752017e2550d57030d13a
4
- data.tar.gz: f99b144884f5f3bf351fe4190e06c99f076b961f
3
+ metadata.gz: 19532fb817cc0d993257331452fd6fc36be49da8
4
+ data.tar.gz: 63c9b867b4e125aff0e5680e21a9a31605629716
5
5
  SHA512:
6
- metadata.gz: 3b2b0a71040bedaf6e15da8c6fdf6bca34fec490fe41f8834c72ba170ab22cbe7bd8d1d1b93dcfd24855acbf4ad634aa342cdb0374d562277ac9d4df493c6e7a
7
- data.tar.gz: 8f4741e1f0f4a00cb0b466e6776445830efc11f66947cac7533fc2d65d0060a0116f7b0239c130ff3866e24e628323170bf684ee2285dc1020e21e2a36106663
6
+ metadata.gz: 799000b32ea19b4b5ca3fb63901007e16c659af8ea383f3d469ed4a91a1c23b10529eeffda3167b73e0bf67043258622c997b68fefd110cf5fb96cedc8b3769c
7
+ data.tar.gz: c017465fd76fd4aa9812924ead3c3342d66d3f87f222cd94c0f08c0eadd2dd66c7088e3bc5333ae6d4a83f165c2329456cc38158b2de5d17e76dd1fe0d098449
data/README.md CHANGED
@@ -1,7 +1,11 @@
1
1
  # Slosilo
2
2
 
3
- Slosilo is a keystore in the database. (Currently only works with postgres.)
4
- It allows easy storage and retrieval of keys.
3
+ Slosilo is providing a ruby interface to some cryptographic primitives:
4
+ - symmetric encryption,
5
+ - a mixin for easy encryption of object attributes (WARNING: unauthenticated, see below),
6
+ - asymmetric encryption and signing,
7
+ - a keystore in a postgres sequel db -- it allows easy storage and retrieval of keys,
8
+ - a keystore in files.
5
9
 
6
10
  ## Installation
7
11
 
@@ -13,6 +17,105 @@ And then execute:
13
17
 
14
18
  $ bundle
15
19
 
20
+ ## Usage
21
+
22
+ ### Symmetric encryption
23
+
24
+ ```ruby
25
+ sym = Slosilo::Symmetric.new
26
+ key = sym.random_key
27
+ ciphertext = sym.encrypt "secret message", key: key
28
+ ```
29
+
30
+ ```ruby
31
+ sym = Slosilo::Symmetric.new
32
+ message = sym.decrypt ciphertext, key: key
33
+ ```
34
+
35
+ ### Encryption mixin
36
+
37
+ ```ruby
38
+ require 'slosilo'
39
+
40
+ class Foo
41
+ attr_accessor :foo
42
+ attr_encrypted :foo
43
+
44
+ def raw_foo
45
+ @foo
46
+ end
47
+ end
48
+
49
+ Slosilo::encryption_key = Slosilo::Symmetric.new.random_key
50
+
51
+ obj = Foo.new
52
+ obj.foo = "bar"
53
+ obj.raw_foo # => "\xC4\xEF\x87\xD3b\xEA\x12\xDF\xD0\xD4hk\xEDJ\v\x1Cr\xF2#\xA3\x11\xA4*k\xB7\x8F\x8F\xC2\xBD\xBB\xFF\xE3"
54
+ obj.foo # => "bar"
55
+ ```
56
+
57
+ You can safely use it in ie. ActiveRecord::Base or Sequel::Model subclasses.
58
+
59
+ #### Warning
60
+
61
+ The encrypted data is not authenticated; it's intended to prevent
62
+ opportunistic access to secrets by a third party which gets hold of a database
63
+ dump. *IT DOES NOT prevent tampering.* If your threat model includes an attacker
64
+ which can modify the database, `attr_encrypted` by itself IS NOT SECURE.
65
+
66
+ ### Asymmetric encryption and signing
67
+
68
+ ```ruby
69
+ private_key = Slosilo::Key.new
70
+ public_key = private_key.public
71
+ ```
72
+
73
+ #### Key dumping
74
+ ```ruby
75
+ k = public_key.to_s # => "-----BEGIN PUBLIC KEY----- ...
76
+ (Slosilo::Key.new k) == public_key # => true
77
+ ```
78
+
79
+ #### Encryption
80
+
81
+ ```ruby
82
+ encrypted = public_key.encrypt_message "eagle one sees many clouds"
83
+ # => "\xA3\x1A\xD2\xFC\xB0 ...
84
+
85
+ public_key.decrypt_message encrypted
86
+ # => OpenSSL::PKey::RSAError: private key needed.
87
+
88
+ private_key.decrypt_message encrypted
89
+ # => "eagle one sees many clouds"
90
+ ```
91
+
92
+ #### Signing
93
+
94
+ ```ruby
95
+ token = private_key.signed_token "missile launch not authorized"
96
+ # => {"data"=>"missile launch not authorized", "timestamp"=>"2014-10-13 12:41:25 UTC", "signature"=>"bSImk...DzV3o", "key"=>"455f7ac42d2d483f750b4c380761821d"}
97
+
98
+ public_key.token_valid? token # => true
99
+
100
+ token["data"] = "missile launch authorized"
101
+ public_key.token_valid? token # => false
102
+ ```
103
+
104
+ ### Keystore
105
+
106
+ ```ruby
107
+ Slosilo::encryption_key = ENV['SLOSILO_KEY']
108
+ Slosilo.adapter = Slosilo::Adapters::FileAdapter.new "~/.keys"
109
+
110
+ Slosilo[:own] = Slosilo::Key.new
111
+ Slosilo[:their] = Slosilo::Key.new File.read("foo.pem")
112
+
113
+ msg = Slosilo[:their].encrypt_message 'bar'
114
+ p Slosilo[:own].signed_token msg
115
+ ```
116
+
117
+ ### Keystore in database
118
+
16
119
  Add a migration to create the necessary table:
17
120
 
18
121
  require 'slosilo/adapters/sequel_adapter/migration'
@@ -21,7 +124,10 @@ Remember to migrate your database
21
124
 
22
125
  $ rake db:migrate
23
126
 
24
- ## Usage
127
+ Then
128
+ ```ruby
129
+ Slosilo.adapter = Slosilo::Adapters::SequelAdapter.new
130
+ ```
25
131
 
26
132
  ## Contributing
27
133
 
@@ -1,3 +1,3 @@
1
1
  module Slosilo
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
data/slosilo.gemspec CHANGED
@@ -18,8 +18,8 @@ Gem::Specification.new do |gem|
18
18
  gem.required_ruby_version = '>= 1.9.3'
19
19
 
20
20
  gem.add_development_dependency 'rake'
21
- gem.add_development_dependency 'rspec', '~> 2.14'
22
- gem.add_development_dependency 'ci_reporter', '~> 1.9'
21
+ gem.add_development_dependency 'rspec', '~> 3.0'
22
+ gem.add_development_dependency 'ci_reporter_rspec'
23
23
  gem.add_development_dependency 'simplecov'
24
24
  gem.add_development_dependency 'io-grab', '~> 0.0.1'
25
25
  gem.add_development_dependency 'sequel' # for sequel tests
@@ -13,7 +13,7 @@ describe Slosilo::Adapters::FileAdapter do
13
13
  describe "#get_key" do
14
14
  context "when given key does not exist" do
15
15
  it "returns nil" do
16
- subject.get_key(:whatever).should_not be
16
+ expect(subject.get_key(:whatever)).not_to be
17
17
  end
18
18
  end
19
19
  end
@@ -22,7 +22,7 @@ describe Slosilo::Adapters::FileAdapter do
22
22
  context "unacceptable id" do
23
23
  let(:id) { "foo.bar" }
24
24
  it "isn't accepted" do
25
- lambda { subject.put_key id, key }.should raise_error
25
+ expect { subject.put_key id, key }.to raise_error
26
26
  end
27
27
  end
28
28
  context "acceptable id" do
@@ -30,11 +30,11 @@ describe Slosilo::Adapters::FileAdapter do
30
30
  let(:key_encrypted) { "encrypted key" }
31
31
  let(:fname) { "#{dir}/#{id}.key" }
32
32
  it "creates the key" do
33
- Slosilo::EncryptedAttributes.should_receive(:encrypt).with(key.to_der).and_return key_encrypted
34
- File.should_receive(:write).with(fname, key_encrypted)
35
- File.should_receive(:chmod).with(0400, fname)
33
+ expect(Slosilo::EncryptedAttributes).to receive(:encrypt).with(key.to_der).and_return key_encrypted
34
+ expect(File).to receive(:write).with(fname, key_encrypted)
35
+ expect(File).to receive(:chmod).with(0400, fname)
36
36
  subject.put_key id, key
37
- subject.instance_variable_get("@keys")[id].should == key
37
+ expect(subject.instance_variable_get("@keys")[id]).to eq(key)
38
38
  end
39
39
  end
40
40
  end
@@ -45,7 +45,7 @@ describe Slosilo::Adapters::FileAdapter do
45
45
  it "iterates over each key" do
46
46
  results = []
47
47
  adapter.each { |id,k| results << { id => k } }
48
- results.should == [ { one: :onek}, {two: :twok } ]
48
+ expect(results).to eq([ { one: :onek}, {two: :twok } ])
49
49
  end
50
50
  end
51
51
 
@@ -60,13 +60,13 @@ describe Slosilo::Adapters::FileAdapter do
60
60
 
61
61
  describe '#get_key' do
62
62
  it "loads and decrypts the key" do
63
- adapter.get_key(id).should == key
63
+ expect(adapter.get_key(id)).to eq(key)
64
64
  end
65
65
  end
66
66
 
67
67
  describe '#get_by_fingerprint' do
68
68
  it "can look up a key by a fingerprint" do
69
- adapter.get_by_fingerprint(key_fingerprint).should == [key, id]
69
+ expect(adapter.get_by_fingerprint(key_fingerprint)).to eq([key, id])
70
70
  end
71
71
  end
72
72
 
@@ -74,7 +74,7 @@ describe Slosilo::Adapters::FileAdapter do
74
74
  it "enumerates the keys" do
75
75
  results = []
76
76
  adapter.each { |id,k| results << { id => k } }
77
- results.should == [ { id => key } ]
77
+ expect(results).to eq([ { id => key } ])
78
78
  end
79
79
  end
80
80
  end
data/spec/key_spec.rb CHANGED
@@ -4,38 +4,50 @@ describe Slosilo::Key do
4
4
  include_context "with example key"
5
5
 
6
6
  subject { key }
7
- its(:to_der) { should == rsa.to_der }
8
- its(:to_s) { should == rsa.public_key.to_pem }
9
- its(:fingerprint) { should == key_fingerprint }
10
- it { should be_private }
7
+
8
+ describe '#to_der' do
9
+ subject { super().to_der }
10
+ it { is_expected.to eq(rsa.to_der) }
11
+ end
12
+
13
+ describe '#to_s' do
14
+ subject { super().to_s }
15
+ it { is_expected.to eq(rsa.public_key.to_pem) }
16
+ end
17
+
18
+ describe '#fingerprint' do
19
+ subject { super().fingerprint }
20
+ it { is_expected.to eq(key_fingerprint) }
21
+ end
22
+ it { is_expected.to be_private }
11
23
 
12
24
  context "with identical key" do
13
25
  let(:other) { Slosilo::Key.new rsa.to_der }
14
26
  it "is equal" do
15
- subject.should == other
27
+ expect(subject).to eq(other)
16
28
  end
17
29
 
18
30
  it "is eql?" do
19
- subject.eql?(other).should be_true
31
+ expect(subject.eql?(other)).to be_truthy
20
32
  end
21
33
 
22
34
  it "has equal hash" do
23
- subject.hash.should == other.hash
35
+ expect(subject.hash).to eq(other.hash)
24
36
  end
25
37
  end
26
38
 
27
39
  context "with a different key" do
28
40
  let(:other) { Slosilo::Key.new another_rsa }
29
41
  it "is not equal" do
30
- subject.should_not == other
42
+ expect(subject).not_to eq(other)
31
43
  end
32
44
 
33
45
  it "is not eql?" do
34
- subject.eql?(other).should_not be_true
46
+ expect(subject.eql?(other)).not_to be_truthy
35
47
  end
36
48
 
37
49
  it "has different hash" do
38
- subject.hash.should_not == other.hash
50
+ expect(subject.hash).not_to eq(other.hash)
39
51
  end
40
52
  end
41
53
 
@@ -54,14 +66,14 @@ describe Slosilo::Key do
54
66
  it "generates a symmetric encryption key and encrypts the plaintext with the public key" do
55
67
  ctxt, skey = subject.encrypt plaintext
56
68
  pskey = rsa.private_decrypt skey
57
- Slosilo::Symmetric.new.decrypt(ctxt, key: pskey).should == plaintext
69
+ expect(Slosilo::Symmetric.new.decrypt(ctxt, key: pskey)).to eq(plaintext)
58
70
  end
59
71
  end
60
72
 
61
73
  describe '#encrypt_message' do
62
74
  it "#encrypts a message and then returns the result as a single string" do
63
- subject.should_receive(:encrypt).with(plaintext).and_return ['fake ciphertext', 'fake key']
64
- subject.encrypt_message(plaintext).should == 'fake keyfake ciphertext'
75
+ expect(subject).to receive(:encrypt).with(plaintext).and_return ['fake ciphertext', 'fake key']
76
+ expect(subject.encrypt_message(plaintext)).to eq('fake keyfake ciphertext')
65
77
  end
66
78
  end
67
79
 
@@ -69,14 +81,14 @@ describe Slosilo::Key do
69
81
  let(:skey) { "\x15\xFF\xD4>\x9C\xF4\x0F\xB6\x04C\x18\xC1\x96\xC3\xE0T\xA3\xF5\xE8\x17\xA6\xE0\x86~rrw\xC3\xDF\x11)$\x9B\r@\x0E\xE4Zv\rw-\xFC\x1C\x84\x17\xBD\xDB\x12u\xCD\r\xFEo\xD5\xC8[\x8FEA\\\xA9\xA2F#\x8BH -\xFA\xF7\xA9\xEBf\x97\xAAT}\x8E*\xC0r\x944p\xD0\x9A\xE7\xBD\a;O\f\xEF\xE4B.y\xFA\xB4e@O\xBB\x15>y\xC9\t=\x01\xE1J\xF3X\xA9\x9E3\x04^H\x1F\xFF\x19C\x93ve)@\xF7\t_\xCF\xDE\xF1\xB7]\x83lL\xB8%A\x93p{\xE9Y\xBAu\xCE\x99T\xDC\xDF\xE7\x0FD%\xB9AXb\x1CW\x94$P\xBB\xE1g\xDEE\t\xC4\x92\x9E\xFEt\xDF\xD0\xEA\x03\xC4\x12\xA9\x02~u\xF4\x92 ;\xA0\xCE.\b+)\x05\xEDo\xA5cF\xF8\x12\xD7F\x97\xE44\xBF\xF1@\xA5\xC5\xA8\xE5\a\xE8ra<\x04\xB5\xA2\f\t\xF7T\x97\e\xF5(^\xAB\xA5%84\x10\xD1\x13e<\xCA/\xBF}%\xF6\xB1%\xB2".force_encoding("ASCII-8BIT") }
70
82
  describe '#decrypt' do
71
83
  it "decrypts the symmetric key and then uses it to decrypt the ciphertext" do
72
- subject.decrypt(ciphertext, skey).should == plaintext
84
+ expect(subject.decrypt(ciphertext, skey)).to eq(plaintext)
73
85
  end
74
86
  end
75
87
 
76
88
  describe '#decrypt_message' do
77
89
  it "splits the message into key and rest, then #decrypts it" do
78
- subject.should_receive(:decrypt).with(ciphertext, skey).and_return plaintext
79
- subject.decrypt_message(skey + ciphertext).should == plaintext
90
+ expect(subject).to receive(:decrypt).with(ciphertext, skey).and_return plaintext
91
+ expect(subject.decrypt_message(skey + ciphertext)).to eq(plaintext)
80
92
  end
81
93
  end
82
94
 
@@ -85,17 +97,25 @@ describe Slosilo::Key do
85
97
  subject { Slosilo::Key.new }
86
98
  let (:rsa) { double "key" }
87
99
  it "generates a new key pair" do
88
- OpenSSL::PKey::RSA.should_receive(:new).with(2048).and_return(rsa)
89
- subject.key.should == rsa
100
+ expect(OpenSSL::PKey::RSA).to receive(:new).with(2048).and_return(rsa)
101
+ expect(subject.key).to eq(rsa)
90
102
  end
91
103
  end
92
104
  context "when given an armored key" do
93
105
  subject { Slosilo::Key.new rsa.to_der }
94
- its(:to_der) { should == rsa.to_der }
106
+
107
+ describe '#to_der' do
108
+ subject { super().to_der }
109
+ it { is_expected.to eq(rsa.to_der) }
110
+ end
95
111
  end
96
112
  context "when given a key instance" do
97
113
  subject { Slosilo::Key.new rsa }
98
- its(:to_der) { should == rsa.to_der }
114
+
115
+ describe '#to_der' do
116
+ subject { super().to_der }
117
+ it { is_expected.to eq(rsa.to_der) }
118
+ end
99
119
  end
100
120
  context "when given something else" do
101
121
  subject { Slosilo::Key.new "foo" }
@@ -108,21 +128,21 @@ describe Slosilo::Key do
108
128
  describe "#sign" do
109
129
  context "when given a hash" do
110
130
  it "converts to a sorted array and signs that" do
111
- key.should_receive(:sign_string).with '[["a",3],["b",42]]'
131
+ expect(key).to receive(:sign_string).with '[["a",3],["b",42]]'
112
132
  key.sign b: 42, a: 3
113
133
  end
114
134
  end
115
135
  context "when given an array" do
116
136
  it "signs a JSON representation instead" do
117
- key.should_receive(:sign_string).with '[2,[42,2]]'
137
+ expect(key).to receive(:sign_string).with '[2,[42,2]]'
118
138
  key.sign [2, [42, 2]]
119
139
  end
120
140
  end
121
141
  context "when given a string" do
122
142
  let(:expected_signature) { "d[\xA4\x00\x02\xC5\x17\xF5P\x1AD\x91\xF9\xC1\x00P\x0EG\x14,IN\xDE\x17\xE1\xA2a\xCC\xABR\x99'\xB0A\xF5~\x93M/\x95-B\xB1\xB6\x92!\x1E\xEA\x9C\v\xC2O\xA8\x91\x1C\xF9\x11\x92a\xBFxm-\x93\x9C\xBBoM\x92%\xA9\xD06$\xC1\xBC.`\xF8\x03J\x16\xE1\xB0c\xDD\xBF\xB0\xAA\xD7\xD4\xF4\xFC\e*\xAB\x13A%-\xD3\t\xA5R\x18\x01let6\xC8\xE9\"\x7F6O\xC7p\x82\xAB\x04J(IY\xAA]b\xA4'\xD6\x873`\xAB\x13\x95g\x9C\x17\xCAB\xF8\xB9\x85B:^\xC5XY^\x03\xEA\xB6V\x17b2\xCA\xF5\xD6\xD4\xD2\xE3u\x11\xECQ\x0Fb\x14\xE2\x04\xE1<a\xC5\x01eW-\x15\x01X\x81K\x1A\xE5A\vVj\xBF\xFC\xFE#\xD5\x93y\x16\xDC\xB4\x8C\xF0\x02Y\xA8\x87i\x01qC\xA7#\xE8\f\xA5\xF0c\xDEJ\xB0\xDB BJ\x87\xA4\xB0\x92\x80\x03\x95\xEE\xE9\xB8K\xC0\xE3JbE-\xD4\xCBP\\\x13S\"\eZ\xE1\x93\xFDa pinch of salt".force_encoding("ASCII-8BIT") }
123
143
  it "signs it" do
124
- key.stub shake_salt: 'a pinch of salt'
125
- key.sign("this sentence is not this sentence").should == expected_signature
144
+ allow(key).to receive_messages shake_salt: 'a pinch of salt'
145
+ expect(key.sign("this sentence is not this sentence")).to eq(expected_signature)
126
146
  end
127
147
  end
128
148
  end
@@ -136,44 +156,44 @@ describe Slosilo::Key do
136
156
  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".force_encoding("ASCII-8BIT") }
137
157
  let(:expected_token) { token_to_sign.merge "signature" => expected_signature, "key" => key_fingerprint }
138
158
  before do
139
- key.stub shake_salt: salt
140
- Time.stub new: time
159
+ allow(key).to receive_messages shake_salt: salt
160
+ allow(Time).to receive_messages new: time
141
161
  end
142
162
  subject { key.signed_token data }
143
- it { should == expected_token }
163
+ it { is_expected.to eq(expected_token) }
144
164
  end
145
165
 
146
166
  describe "#token_valid?" do
147
167
  let(:data) { { "foo" => :bar } }
148
168
  let(: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".force_encoding("ASCII-8BIT") }
149
169
  let(:token) { { "data" => data, "timestamp" => "2012-01-01 01:01:01 UTC", "signature" => signature } }
150
- before { Time.stub now: Time.new(2012,1,1,1,2,1,0) }
170
+ before { allow(Time).to receive_messages now: Time.new(2012,1,1,1,2,1,0) }
151
171
  subject { key.token_valid? token }
152
- it { should be_true }
172
+ it { is_expected.to be_truthy }
153
173
 
154
174
  it "doesn't check signature on the advisory key field" do
155
- key.token_valid?(token.merge "key" => key_fingerprint).should be_true
175
+ expect(key.token_valid?(token.merge "key" => key_fingerprint)).to be_truthy
156
176
  end
157
177
 
158
178
  it "rejects the token if the key field is present and doesn't match" do
159
- key.token_valid?(token.merge "key" => "this is not the key you are looking for").should_not be_true
179
+ expect(key.token_valid?(token.merge "key" => "this is not the key you are looking for")).not_to be_truthy
160
180
  end
161
181
 
162
182
  context "when token is 1 hour old" do
163
- before { Time.stub now: Time.new(2012,1,1,2,1,1,0) }
164
- it { should be_false }
183
+ before { allow(Time).to receive_messages now: Time.new(2012,1,1,2,1,1,0) }
184
+ it { is_expected.to be_falsey }
165
185
  context "when timestamp in the token is changed accordingly" do
166
186
  let(:token) { { "data" => data, "timestamp" => "2012-01-01 02:00:01 UTC", "signature" => signature } }
167
- it { should be_false }
187
+ it { is_expected.to be_falsey }
168
188
  end
169
189
  end
170
190
  context "when the data is changed" do
171
191
  let(:data) { { "foo" => :baz } }
172
- it { should be_false }
192
+ it { is_expected.to be_falsey }
173
193
  end
174
194
  context "when RSA decrypt raises an error" do
175
- before { OpenSSL::PKey::RSA.any_instance.should_receive(:public_decrypt).and_raise(OpenSSL::PKey::RSAError) }
176
- it { should be_false }
195
+ before { expect_any_instance_of(OpenSSL::PKey::RSA).to receive(:public_decrypt).and_raise(OpenSSL::PKey::RSAError) }
196
+ it { is_expected.to be_falsey }
177
197
  end
178
198
  end
179
199
  end
@@ -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
data/spec/random_spec.rb CHANGED
@@ -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.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)
data/spec/slosilo_spec.rb CHANGED
@@ -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,7 @@ 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
89
  end
90
90
  end
91
91
  end
data/spec/spec_helper.rb CHANGED
@@ -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
@@ -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
@@ -8,21 +8,21 @@ describe Slosilo::Symmetric do
8
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") }
9
9
  describe '#encrypt' do
10
10
  it "encrypts with AES-256-CBC" do
11
- subject.stub random_iv: iv
12
- subject.encrypt(plaintext, key: key).should == ciphertext
11
+ allow(subject).to receive_messages random_iv: iv
12
+ expect(subject.encrypt(plaintext, key: key)).to eq(ciphertext)
13
13
  end
14
14
  end
15
15
 
16
16
  describe '#decrypt' do
17
17
  it "decrypts with AES-256-CBC" do
18
- subject.decrypt(ciphertext, key: key).should == plaintext
18
+ expect(subject.decrypt(ciphertext, key: key)).to eq(plaintext)
19
19
  end
20
20
 
21
21
  context "when ciphertext happens to end in a zero" do
22
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
23
  let(:key) { "4pSuk1rAQyuHA5uUYaj0X0BsiPCFb9Nc8J03XA6V5/Y" }
24
24
  it "works correctly" do
25
- subject.decrypt(ciphertext, key: key).should == "R6KNTQ4aUivojbaqhgAqj1I4PaF8h/5/YcENy4uNbfk="
25
+ expect(subject.decrypt(ciphertext, key: key)).to eq("R6KNTQ4aUivojbaqhgAqj1I4PaF8h/5/YcENy4uNbfk=")
26
26
  end
27
27
  end
28
28
 
@@ -30,22 +30,22 @@ describe Slosilo::Symmetric do
30
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
31
  let(:key) { "4pSuk1rAQyuHA5uUYaj0X0BsiPCFb9Nc8J03XA6V5/Y" }
32
32
  it "works correctly" do
33
- subject.decrypt(ciphertext, key: key).should == "zGptmL3vd4obi1vqSiWHt/Ias2k+6qDtuq9vdow8jNA="
33
+ expect(subject.decrypt(ciphertext, key: key)).to eq("zGptmL3vd4obi1vqSiWHt/Ias2k+6qDtuq9vdow8jNA=")
34
34
  end
35
35
  end
36
36
  end
37
37
 
38
38
  describe '#random_iv' do
39
39
  it "generates a random iv" do
40
- OpenSSL::Cipher.any_instance.should_receive(:random_iv).and_return :iv
41
- subject.random_iv.should == :iv
40
+ expect_any_instance_of(OpenSSL::Cipher).to receive(:random_iv).and_return :iv
41
+ expect(subject.random_iv).to eq(:iv)
42
42
  end
43
43
  end
44
44
 
45
45
  describe '#random_key' do
46
46
  it "generates a random key" do
47
- OpenSSL::Cipher.any_instance.should_receive(:random_key).and_return :key
48
- subject.random_key.should == :key
47
+ expect_any_instance_of(OpenSSL::Cipher).to receive(:random_key).and_return :key
48
+ expect(subject.random_key).to eq(:key)
49
49
  end
50
50
  end
51
51
  end
metadata CHANGED
@@ -1,111 +1,111 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slosilo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rafał Rzepecki
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-12 00:00:00.000000000 Z
11
+ date: 2014-10-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - '>='
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - '>='
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ~>
32
32
  - !ruby/object:Gem::Version
33
- version: '2.14'
33
+ version: '3.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ~>
39
39
  - !ruby/object:Gem::Version
40
- version: '2.14'
40
+ version: '3.0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: ci_reporter
42
+ name: ci_reporter_rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - '>='
46
46
  - !ruby/object:Gem::Version
47
- version: '1.9'
47
+ version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - '>='
53
53
  - !ruby/object:Gem::Version
54
- version: '1.9'
54
+ version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: simplecov
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - '>='
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - '>='
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: io-grab
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - "~>"
73
+ - - ~>
74
74
  - !ruby/object:Gem::Version
75
75
  version: 0.0.1
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - "~>"
80
+ - - ~>
81
81
  - !ruby/object:Gem::Version
82
82
  version: 0.0.1
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: sequel
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ">="
87
+ - - '>='
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ">="
94
+ - - '>='
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: sqlite3
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ">="
101
+ - - '>='
102
102
  - !ruby/object:Gem::Version
103
103
  version: '0'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - ">="
108
+ - - '>='
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  description: This gem provides an easy way of storing and retrieving encryption keys
@@ -116,8 +116,8 @@ executables: []
116
116
  extensions: []
117
117
  extra_rdoc_files: []
118
118
  files:
119
- - ".gitignore"
120
- - ".kateproject"
119
+ - .gitignore
120
+ - .kateproject
121
121
  - Gemfile
122
122
  - LICENSE
123
123
  - README.md
@@ -156,12 +156,12 @@ require_paths:
156
156
  - lib
157
157
  required_ruby_version: !ruby/object:Gem::Requirement
158
158
  requirements:
159
- - - ">="
159
+ - - '>='
160
160
  - !ruby/object:Gem::Version
161
161
  version: 1.9.3
162
162
  required_rubygems_version: !ruby/object:Gem::Requirement
163
163
  requirements:
164
- - - ">="
164
+ - - '>='
165
165
  - !ruby/object:Gem::Version
166
166
  version: '0'
167
167
  requirements: []
@@ -179,3 +179,4 @@ test_files:
179
179
  - spec/slosilo_spec.rb
180
180
  - spec/spec_helper.rb
181
181
  - spec/symmetric_spec.rb
182
+ has_rdoc: