slosilo 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: