slosilo 1.1.0 → 2.0.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 +4 -4
- data/README.md +24 -11
- data/lib/slosilo/adapters/sequel_adapter.rb +1 -1
- data/lib/slosilo/attr_encrypted.rb +29 -6
- data/lib/slosilo/symmetric.rb +30 -9
- data/lib/slosilo/version.rb +1 -1
- data/spec/encrypted_attributes_spec.rb +114 -0
- data/spec/key_spec.rb +2 -2
- data/spec/symmetric_spec.rb +39 -17
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 761d6e24aaa95427bbc3d7449bc980b29b602bca
|
4
|
+
data.tar.gz: 79950d2ab62c71a6f9ebdb1f68afdfd13c53894e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: de1ab282bd0067317a21e2d40dbb0668c737e487bc2e29e25120f460191d2c8a550b25ec98719a4d99d72176a73f0b47007703733ad5a0e77077a13c1fc54d6d
|
7
|
+
data.tar.gz: e7444706c7f1af27f3a7eebf350252b37a033ca4b225a5d8ea6d24afbf49f40d42e6613187f8f0fe2322c9bf73dc1b54d5e36796ba7ef10aa8c95ff54f5aa8b3
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Slosilo is providing a ruby interface to some cryptographic primitives:
|
4
4
|
- symmetric encryption,
|
5
|
-
- a mixin for easy encryption of object attributes
|
5
|
+
- a mixin for easy encryption of object attributes,
|
6
6
|
- asymmetric encryption and signing,
|
7
7
|
- a keystore in a postgres sequel db -- it allows easy storage and retrieval of keys,
|
8
8
|
- a keystore in files.
|
@@ -17,6 +17,20 @@ And then execute:
|
|
17
17
|
|
18
18
|
$ bundle
|
19
19
|
|
20
|
+
## Compatibility
|
21
|
+
|
22
|
+
Version 2.0 introduced new symmetric encryption scheme using AES-256-GCM
|
23
|
+
for authenticated encryption. It allows you to provide AAD on all symmetric
|
24
|
+
encryption primitives. It's also **NOT COMPATIBLE** with CBC used in version <2.
|
25
|
+
|
26
|
+
This means you'll have to migrate all your existing data. There's no easy way to
|
27
|
+
do this currently provided; it's recommended to create a database migration and
|
28
|
+
put relevant code fragments in it directly. (This will also have the benefit of making
|
29
|
+
the migration self-contained.)
|
30
|
+
|
31
|
+
Since symmetric encryption is used in processing asymetrically encrypted messages,
|
32
|
+
this incompatibility extends to those too.
|
33
|
+
|
20
34
|
## Usage
|
21
35
|
|
22
36
|
### Symmetric encryption
|
@@ -24,12 +38,14 @@ And then execute:
|
|
24
38
|
```ruby
|
25
39
|
sym = Slosilo::Symmetric.new
|
26
40
|
key = sym.random_key
|
27
|
-
|
41
|
+
# additional authenticated data
|
42
|
+
message_id = "message 001"
|
43
|
+
ciphertext = sym.encrypt "secret message", key: key, aad: message_id
|
28
44
|
```
|
29
45
|
|
30
46
|
```ruby
|
31
47
|
sym = Slosilo::Symmetric.new
|
32
|
-
message = sym.decrypt ciphertext, key: key
|
48
|
+
message = sym.decrypt ciphertext, key: key, aad: message_id
|
33
49
|
```
|
34
50
|
|
35
51
|
### Encryption mixin
|
@@ -39,11 +55,15 @@ require 'slosilo'
|
|
39
55
|
|
40
56
|
class Foo
|
41
57
|
attr_accessor :foo
|
42
|
-
attr_encrypted :foo
|
58
|
+
attr_encrypted :foo, aad: :id
|
43
59
|
|
44
60
|
def raw_foo
|
45
61
|
@foo
|
46
62
|
end
|
63
|
+
|
64
|
+
def id
|
65
|
+
"unique record id"
|
66
|
+
end
|
47
67
|
end
|
48
68
|
|
49
69
|
Slosilo::encryption_key = Slosilo::Symmetric.new.random_key
|
@@ -56,13 +76,6 @@ obj.foo # => "bar"
|
|
56
76
|
|
57
77
|
You can safely use it in ie. ActiveRecord::Base or Sequel::Model subclasses.
|
58
78
|
|
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
79
|
### Asymmetric encryption and signing
|
67
80
|
|
68
81
|
```ruby
|
@@ -5,21 +5,44 @@ module Slosilo
|
|
5
5
|
# so we encrypt sensitive attributes before storing them
|
6
6
|
module EncryptedAttributes
|
7
7
|
module ClassMethods
|
8
|
+
|
9
|
+
# @param options [Hash]
|
10
|
+
# @option :aad [#to_proc, #to_s] Provide additional authenticated data for
|
11
|
+
# encryption. This should be something unique to the instance having
|
12
|
+
# this attribute, such as a primary key; this will ensure that an attacker can't swap
|
13
|
+
# values around -- trying to decrypt value with a different auth data will fail.
|
14
|
+
# This means you have to be able to recover it in order to decrypt attributes.
|
15
|
+
# The following values are accepted:
|
16
|
+
#
|
17
|
+
# * Something proc-ish: will be called with self each time auth data is needed.
|
18
|
+
# * Something stringish: will be to_s-d and used for all instances as auth data.
|
19
|
+
# Note that this will only prevent swapping in data using another string.
|
20
|
+
#
|
21
|
+
# The recommended way to use this option is to pass a proc-ish that identifies the record.
|
22
|
+
# Note the proc-ish can be a simple method name; for example in case of a Sequel::Model:
|
23
|
+
# attr_encrypted :secret, aad: :pk
|
8
24
|
def attr_encrypted *a
|
25
|
+
options = a.last.is_a?(Hash) ? a.pop : {}
|
26
|
+
aad = options[:aad]
|
27
|
+
# note nil.to_s is "", which is exactly the right thing
|
28
|
+
auth_data = aad.respond_to?(:to_proc) ? aad.to_proc : proc{ |_| aad.to_s }
|
29
|
+
raise ":aad proc must take one argument" unless auth_data.arity.abs == 1 # take abs to allow *args arity, -1
|
30
|
+
|
9
31
|
# push a module onto the inheritance hierarchy
|
10
32
|
# this allows calling super in classes
|
11
33
|
include(accessors = Module.new)
|
12
34
|
accessors.module_eval do
|
13
35
|
a.each do |attr|
|
14
36
|
define_method "#{attr}=" do |value|
|
15
|
-
super(EncryptedAttributes.encrypt
|
37
|
+
super(EncryptedAttributes.encrypt(value, aad: auth_data[self]))
|
16
38
|
end
|
17
39
|
define_method attr do
|
18
|
-
EncryptedAttributes.decrypt(super())
|
40
|
+
EncryptedAttributes.decrypt(super(), aad: auth_data[self])
|
19
41
|
end
|
20
42
|
end
|
21
43
|
end
|
22
44
|
end
|
45
|
+
|
23
46
|
end
|
24
47
|
|
25
48
|
def self.included base
|
@@ -27,14 +50,14 @@ module Slosilo
|
|
27
50
|
end
|
28
51
|
|
29
52
|
class << self
|
30
|
-
def encrypt value
|
53
|
+
def encrypt value, opts={}
|
31
54
|
return nil unless value
|
32
|
-
cipher.encrypt value, key: key
|
55
|
+
cipher.encrypt value, key: key, aad: opts[:aad]
|
33
56
|
end
|
34
57
|
|
35
|
-
def decrypt ctxt
|
58
|
+
def decrypt ctxt, opts={}
|
36
59
|
return nil unless ctxt
|
37
|
-
cipher.decrypt ctxt, key: key
|
60
|
+
cipher.decrypt ctxt, key: key, aad: opts[:aad]
|
38
61
|
end
|
39
62
|
|
40
63
|
def key
|
data/lib/slosilo/symmetric.rb
CHANGED
@@ -1,25 +1,40 @@
|
|
1
1
|
module Slosilo
|
2
2
|
class Symmetric
|
3
|
+
VERSION_MAGIC = 'G'
|
4
|
+
TAG_LENGTH = 16
|
5
|
+
|
3
6
|
def initialize
|
4
|
-
@cipher = OpenSSL::Cipher.new '
|
7
|
+
@cipher = OpenSSL::Cipher.new 'aes-256-gcm' # NB: has to be lower case for whatever reason.
|
5
8
|
end
|
6
|
-
|
9
|
+
|
10
|
+
# This lets us do a final sanity check in migrations from older encryption versions
|
11
|
+
def cipher_name
|
12
|
+
@cipher.name
|
13
|
+
end
|
14
|
+
|
7
15
|
def encrypt plaintext, opts = {}
|
8
16
|
@cipher.reset
|
9
17
|
@cipher.encrypt
|
10
|
-
@cipher.key = opts[:key]
|
18
|
+
@cipher.key = (opts[:key] or raise("missing :key option"))
|
11
19
|
@cipher.iv = iv = random_iv
|
12
|
-
|
13
|
-
|
20
|
+
@cipher.auth_data = opts[:aad] || "" # Nothing good happens if you set this to nil, or don't set it at all
|
21
|
+
ctext = @cipher.update(plaintext) + @cipher.final
|
22
|
+
tag = @cipher.auth_tag(TAG_LENGTH)
|
23
|
+
"#{VERSION_MAGIC}#{tag}#{iv}#{ctext}"
|
14
24
|
end
|
15
|
-
|
25
|
+
|
16
26
|
def decrypt ciphertext, opts = {}
|
27
|
+
version, tag, iv, ctext = unpack ciphertext
|
28
|
+
|
29
|
+
raise "Invalid version magic: expected #{VERSION_MAGIC} but was #{version}" unless version == VERSION_MAGIC
|
30
|
+
|
17
31
|
@cipher.reset
|
18
32
|
@cipher.decrypt
|
19
33
|
@cipher.key = opts[:key]
|
20
|
-
@cipher.iv
|
21
|
-
|
22
|
-
|
34
|
+
@cipher.iv = iv
|
35
|
+
@cipher.auth_tag = tag
|
36
|
+
@cipher.auth_data = opts[:aad] || ""
|
37
|
+
@cipher.update(ctext) + @cipher.final
|
23
38
|
end
|
24
39
|
|
25
40
|
def random_iv
|
@@ -29,5 +44,11 @@ module Slosilo
|
|
29
44
|
def random_key
|
30
45
|
@cipher.random_key
|
31
46
|
end
|
47
|
+
|
48
|
+
private
|
49
|
+
# return tag, iv, ctext
|
50
|
+
def unpack msg
|
51
|
+
msg.unpack "aa#{TAG_LENGTH}a#{@cipher.iv_len}a*"
|
52
|
+
end
|
32
53
|
end
|
33
54
|
end
|
data/lib/slosilo/version.rb
CHANGED
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'slosilo/attr_encrypted'
|
3
|
+
|
4
|
+
describe Slosilo::EncryptedAttributes do
|
5
|
+
before(:all) do
|
6
|
+
Slosilo::encryption_key = OpenSSL::Cipher.new("aes-256-gcm").random_key
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:aad) { proc{ |_| "hithere" } }
|
10
|
+
|
11
|
+
let(:base){
|
12
|
+
Class.new do
|
13
|
+
attr_accessor :normal_ivar,:with_aad
|
14
|
+
def stupid_ivar
|
15
|
+
side_effect!
|
16
|
+
@_explicit
|
17
|
+
end
|
18
|
+
def stupid_ivar= e
|
19
|
+
side_effect!
|
20
|
+
@_explicit = e
|
21
|
+
end
|
22
|
+
def side_effect!
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
}
|
27
|
+
|
28
|
+
let(:sub){
|
29
|
+
Class.new(base) do
|
30
|
+
attr_encrypted :normal_ivar, :stupid_ivar
|
31
|
+
end
|
32
|
+
}
|
33
|
+
|
34
|
+
subject{ sub.new }
|
35
|
+
|
36
|
+
context "when setting a normal ivar" do
|
37
|
+
let(:value){ "some value" }
|
38
|
+
it "stores an encrypted value in the ivar" do
|
39
|
+
subject.normal_ivar = value
|
40
|
+
expect(subject.instance_variable_get(:"@normal_ivar")).to_not eq(value)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "recovers the value set" do
|
44
|
+
subject.normal_ivar = value
|
45
|
+
expect(subject.normal_ivar).to eq(value)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when setting an attribute with an implementation" do
|
50
|
+
it "calls the base class method" do
|
51
|
+
expect(subject).to receive_messages(:side_effect! => nil)
|
52
|
+
subject.stupid_ivar = "hi"
|
53
|
+
expect(subject.stupid_ivar).to eq("hi")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "when given an :aad option" do
|
58
|
+
|
59
|
+
let(:cipher){ Slosilo::EncryptedAttributes.cipher }
|
60
|
+
let(:key){ Slosilo::EncryptedAttributes.key}
|
61
|
+
context "that is a string" do
|
62
|
+
let(:aad){ "hello there" }
|
63
|
+
before{ sub.attr_encrypted :with_aad, aad: aad }
|
64
|
+
it "encrypts the value with the given string for auth data" do
|
65
|
+
expect(cipher).to receive(:encrypt).with("hello", key: key, aad: aad)
|
66
|
+
subject.with_aad = "hello"
|
67
|
+
end
|
68
|
+
|
69
|
+
it "decrypts the encrypted value" do
|
70
|
+
subject.with_aad = "foo"
|
71
|
+
expect(subject.with_aad).to eq("foo")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "that is nil" do
|
76
|
+
let(:aad){ nil }
|
77
|
+
before{ sub.attr_encrypted :with_aad, aad: aad }
|
78
|
+
it "encrypts the value with an empty string for auth data" do
|
79
|
+
expect(cipher).to receive(:encrypt).with("hello",key: key, aad: "").and_call_original
|
80
|
+
subject.with_aad = "hello"
|
81
|
+
end
|
82
|
+
|
83
|
+
it "decrypts the encrypted value" do
|
84
|
+
subject.with_aad = "hello"
|
85
|
+
expect(subject.with_aad).to eq("hello")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context "that is a proc" do
|
90
|
+
let(:aad){
|
91
|
+
proc{ |o| "x" }
|
92
|
+
}
|
93
|
+
|
94
|
+
before{ sub.attr_encrypted :with_aad, aad: aad }
|
95
|
+
|
96
|
+
it "calls the proc with the object being encrypted" do
|
97
|
+
expect(aad).to receive(:[]).with(subject).and_call_original
|
98
|
+
subject.with_aad = "hi"
|
99
|
+
end
|
100
|
+
|
101
|
+
it "encrypts the value with the string returned for auth data" do
|
102
|
+
expect(cipher).to receive(:encrypt).with("hello", key: key, aad: aad[subject]).and_call_original
|
103
|
+
subject.with_aad = "hello"
|
104
|
+
end
|
105
|
+
it "decrypts the encrypted value" do
|
106
|
+
subject.with_aad = "hello"
|
107
|
+
expect(subject.with_aad).to eq("hello")
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
end
|
data/spec/key_spec.rb
CHANGED
@@ -77,8 +77,8 @@ describe Slosilo::Key do
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
-
let(:ciphertext)
|
81
|
-
let(:skey)
|
80
|
+
let(:ciphertext){ "G\xAD^\x17\x11\xBBQ9-b\x14\xF6\x92#Q0x\xF4\xAD\x1A\x92\xC3VZW\x89\x8E\x8Fg\x93\x05B\xF8\xD6O\xCFGCTp\b~\x916\xA3\x9AN\x8D\x961\x1F\xA3mSf&\xAD\xA77/]z\xA89\x01\xA7\xA9\x92\f".force_encoding('ASCII-8BIT') }
|
81
|
+
let(:skey){ "\x82\x93\xFAA\xA6wQA\xE1\xB5\xA6b\x8C.\xCF#I\x86I\x83u\x99\rTA\xEF\xC4\x91\xC5)-\xEBQ\xB1\xC0\xC6\xFF\x90L\xFE\x1E\x15\x81\x12\x16\xDD:A\xC5d\xE1B\xD2f@\xB8o\xB7+N\xB7\n\x92\xDC\x9E\xE3\x83\xB8>h\a\xC7\xCC\xCF\xD0t\x06\x8B\xA8\xBF\xEFe\xA4{\x88\f\xDD\roF\xEB.\xDA\xBF\x9D_0>\xF03c'\x1F!)*-\x19\x97\xAC\xD2\x1F(,6h\a\x93\xDB\x8E\x97\xF9\x1A\x11\x84\x11t\xD9\xB2\x85\xB0\x12\x7F\x03\x00O\x8F\xBE#\xFFb\xA5w\xF3g\xCF\xB4\xF2\xB7\xDBiA=\xA8\xFD1\xEC\xBF\xD7\x8E\xB6W>\x03\xACNBa\xBF\xFD\xC6\xB32\x8C\xE2\xF1\x87\x9C\xAE6\xD1\x12\vkl\xBB\xA0\xED\x9A\xEE6\xF2\xD9\xB4LL\xE2h/u_\xA1i=\x11x\x8DGha\x8EG\b+\x84[\x87\x8E\x01\x0E\xA5\xB0\x9F\xE9vSl\x18\xF3\xEA\xF4NH\xA8\xF1\x81\xBB\x98\x01\xE8p]\x18\x11f\xA3K\xA87c\xBB\x13X~K\xA2".force_encoding('ASCII-8BIT') }
|
82
82
|
describe '#decrypt' do
|
83
83
|
it "decrypts the symmetric key and then uses it to decrypt the ciphertext" do
|
84
84
|
expect(subject.decrypt(ciphertext, skey)).to eq(plaintext)
|
data/spec/symmetric_spec.rb
CHANGED
@@ -3,34 +3,56 @@ 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) { "\
|
8
|
-
let(:ciphertext) { "\
|
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-
|
12
|
+
it "encrypts with AES-256-GCM" do
|
11
13
|
allow(subject).to receive_messages random_iv: iv
|
12
|
-
expect(subject.encrypt(plaintext, key: key)).to eq(ciphertext)
|
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-
|
18
|
-
expect(subject.decrypt(ciphertext, key: key)).to eq(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
|
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
|
33
|
+
end
|
34
|
+
end
|
19
35
|
end
|
20
|
-
|
21
|
-
context "when
|
22
|
-
let(:
|
23
|
-
let(:
|
24
|
-
|
25
|
-
|
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
|
48
|
+
end
|
26
49
|
end
|
27
50
|
end
|
28
51
|
|
29
|
-
context "when the
|
30
|
-
let(:
|
31
|
-
|
32
|
-
|
33
|
-
expect(subject.decrypt(ciphertext, key: key)).to eq("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
|
34
56
|
end
|
35
57
|
end
|
36
58
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slosilo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.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-
|
11
|
+
date: 2014-11-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -138,6 +138,7 @@ files:
|
|
138
138
|
- lib/slosilo/version.rb
|
139
139
|
- lib/tasks/slosilo.rake
|
140
140
|
- slosilo.gemspec
|
141
|
+
- spec/encrypted_attributes_spec.rb
|
141
142
|
- spec/file_adapter_spec.rb
|
142
143
|
- spec/key_spec.rb
|
143
144
|
- spec/keystore_spec.rb
|
@@ -166,11 +167,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
166
167
|
version: '0'
|
167
168
|
requirements: []
|
168
169
|
rubyforge_project:
|
169
|
-
rubygems_version: 2.
|
170
|
+
rubygems_version: 2.0.14
|
170
171
|
signing_key:
|
171
172
|
specification_version: 4
|
172
173
|
summary: Store SSL keys in a database
|
173
174
|
test_files:
|
175
|
+
- spec/encrypted_attributes_spec.rb
|
174
176
|
- spec/file_adapter_spec.rb
|
175
177
|
- spec/key_spec.rb
|
176
178
|
- spec/keystore_spec.rb
|