e3db 2.0.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/LICENSE.md +255 -0
- data/README.md +44 -23
- data/examples/registration.rb +79 -0
- data/examples/simple.rb +2 -25
- data/lib/e3db.rb +1 -1
- data/lib/e3db/client.rb +440 -57
- data/lib/e3db/config.rb +2 -0
- data/lib/e3db/crypto.rb +32 -108
- data/lib/e3db/version.rb +1 -1
- data/travis-install-configfile.sh +18 -0
- metadata +5 -4
- data/LICENSE.txt +0 -21
data/lib/e3db/config.rb
CHANGED
@@ -41,6 +41,7 @@ module E3DB
|
|
41
41
|
attribute :client_id, Types::String
|
42
42
|
attribute :api_key_id, Types::String
|
43
43
|
attribute :api_secret, Types::String
|
44
|
+
attribute :client_email, Types::String
|
44
45
|
attribute :public_key, Types::String
|
45
46
|
attribute :private_key, Types::String
|
46
47
|
attribute :api_url, Types::String.default(DEFAULT_API_URL)
|
@@ -57,6 +58,7 @@ module E3DB
|
|
57
58
|
# "client_id": "UUID",
|
58
59
|
# "api_key_id": "API_KEY",
|
59
60
|
# "api_secret": "API_SECRET",
|
61
|
+
# "client_email": "CLIENT_EMAIL",
|
60
62
|
# "public_key": "PUBLIC_KEY",
|
61
63
|
# "private_key": "PRIVATE_KEY",
|
62
64
|
# "api_url": "URL",
|
data/lib/e3db/crypto.rb
CHANGED
@@ -5,142 +5,66 @@
|
|
5
5
|
# All Rights Reserved.
|
6
6
|
#
|
7
7
|
|
8
|
-
|
9
8
|
module E3DB
|
10
|
-
|
9
|
+
module Crypto
|
11
10
|
private
|
12
|
-
def decrypt_record(record)
|
13
|
-
writer_id = record.meta.writer_id
|
14
|
-
user_id = record.meta.user_id
|
15
|
-
type = record.meta.type
|
16
|
-
ak = get_access_key(writer_id, user_id, @config.client_id, type)
|
17
|
-
decrypt_record_with_key(record, ak)
|
18
|
-
end
|
19
|
-
|
20
|
-
def decrypt_record_with_key(encrypted_record, ak)
|
21
|
-
record = Record.new(meta: encrypted_record.meta.clone, data: Hash.new)
|
22
|
-
|
23
|
-
encrypted_record.data.each do |k, v|
|
24
|
-
fields = v.split('.', 4)
|
25
|
-
|
26
|
-
edk = Crypto.base64decode(fields[0])
|
27
|
-
edkN = Crypto.base64decode(fields[1])
|
28
|
-
ef = Crypto.base64decode(fields[2])
|
29
|
-
efN = Crypto.base64decode(fields[3])
|
30
|
-
|
31
|
-
dk = RbNaCl::SecretBox.new(ak).decrypt(edkN, edk)
|
32
|
-
pv = RbNaCl::SecretBox.new(dk).decrypt(efN, ef)
|
33
|
-
|
34
|
-
record.data[k] = pv
|
35
|
-
end
|
36
|
-
|
37
|
-
record
|
38
|
-
end
|
39
|
-
|
40
|
-
def encrypt_record(plaintext_record)
|
41
|
-
record = Record.new(meta: plaintext_record.meta.clone, data: Hash.new)
|
42
|
-
|
43
|
-
writer_id = record.meta.writer_id
|
44
|
-
user_id = record.meta.user_id
|
45
|
-
type = record.meta.type
|
46
|
-
|
47
|
-
begin
|
48
|
-
ak = get_access_key(writer_id, user_id, @config.client_id, type)
|
49
|
-
rescue Faraday::ResourceNotFound
|
50
|
-
ak = RbNaCl::Random.random_bytes(RbNaCl::SecretBox.key_bytes)
|
51
|
-
put_access_key(writer_id, user_id, @config.client_id, type, ak)
|
52
|
-
end
|
53
|
-
|
54
|
-
plaintext_record.data.each do |k, v|
|
55
|
-
dk = Crypto.secret_box_random_key
|
56
|
-
efN = Crypto.secret_box_random_nonce
|
57
|
-
ef = RbNaCl::SecretBox.new(dk).encrypt(efN, v)
|
58
|
-
edkN = Crypto.secret_box_random_nonce
|
59
|
-
edk = RbNaCl::SecretBox.new(ak).encrypt(edkN, dk)
|
60
|
-
|
61
|
-
record.data[k] = sprintf('%s.%s.%s.%s',
|
62
|
-
Crypto.base64encode(edk), Crypto.base64encode(edkN),
|
63
|
-
Crypto.base64encode(ef), Crypto.base64encode(efN))
|
64
|
-
end
|
65
|
-
|
66
|
-
record
|
67
|
-
end
|
68
|
-
|
69
|
-
def decrypt_eak(json)
|
70
|
-
k = json[:authorizer_public_key][:curve25519]
|
71
|
-
authorizer_pubkey = Crypto.decode_public_key(k)
|
72
|
-
|
73
|
-
fields = json[:eak].split('.', 2)
|
74
|
-
ciphertext = Crypto.base64decode(fields[0])
|
75
|
-
nonce = Crypto.base64decode(fields[1])
|
76
|
-
box = RbNaCl::Box.new(authorizer_pubkey, @private_key)
|
77
|
-
|
78
|
-
box.decrypt(nonce, ciphertext)
|
79
|
-
end
|
80
|
-
|
81
|
-
def get_access_key(writer_id, user_id, reader_id, type)
|
82
|
-
ak_cache_key = [writer_id, user_id, type]
|
83
|
-
if @ak_cache.key? ak_cache_key
|
84
|
-
return @ak_cache[ak_cache_key]
|
85
|
-
end
|
86
11
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
ak = decrypt_eak(json)
|
92
|
-
@ak_cache[ak_cache_key] = ak
|
93
|
-
ak
|
94
|
-
end
|
95
|
-
|
96
|
-
def put_access_key(writer_id, user_id, reader_id, type, ak)
|
97
|
-
ak_cache_key = [writer_id, user_id, type]
|
98
|
-
@ak_cache[ak_cache_key] = ak
|
99
|
-
|
100
|
-
reader_key = client_key(reader_id)
|
101
|
-
nonce = RbNaCl::Random.random_bytes(RbNaCl::Box.nonce_bytes)
|
102
|
-
eak = RbNaCl::Box.new(reader_key, @private_key).encrypt(nonce, ak)
|
103
|
-
|
104
|
-
encoded_eak = sprintf('%s.%s', Crypto.base64encode(eak), Crypto.base64encode(nonce))
|
105
|
-
|
106
|
-
url = get_url('v1', 'storage', 'access_keys', writer_id, user_id, reader_id, type)
|
107
|
-
@conn.put(url, { :eak => encoded_eak })
|
12
|
+
# Create a new, random access key. Returns a
|
13
|
+
# string of bytes representing the key.
|
14
|
+
def new_access_key
|
15
|
+
RbNaCl::Random.random_bytes(RbNaCl::SecretBox.key_bytes)
|
108
16
|
end
|
109
17
|
|
110
|
-
|
18
|
+
alias :new_data_key :new_access_key
|
111
19
|
|
112
|
-
|
113
|
-
def self.decode_public_key(s)
|
20
|
+
def decode_public_key(s)
|
114
21
|
RbNaCl::PublicKey.new(base64decode(s))
|
115
22
|
end
|
116
23
|
|
117
|
-
def
|
24
|
+
def encode_public_key(k)
|
118
25
|
base64encode(k.to_bytes)
|
119
26
|
end
|
120
27
|
|
121
|
-
def
|
28
|
+
def decode_private_key(s)
|
122
29
|
RbNaCl::PrivateKey.new(base64decode(s))
|
123
30
|
end
|
124
31
|
|
125
|
-
def
|
32
|
+
def encode_private_key(k)
|
126
33
|
base64encode(k.to_bytes)
|
127
34
|
end
|
128
35
|
|
129
|
-
def
|
130
|
-
RbNaCl::Random.random_bytes(RbNaCl::
|
36
|
+
def box_random_nonce
|
37
|
+
RbNaCl::Random.random_bytes(RbNaCl::Box.nonce_bytes)
|
131
38
|
end
|
132
39
|
|
133
|
-
def
|
40
|
+
def secret_box_random_nonce
|
134
41
|
RbNaCl::Random.random_bytes(RbNaCl::SecretBox.nonce_bytes)
|
135
42
|
end
|
136
43
|
|
137
|
-
def
|
44
|
+
def base64encode(x)
|
138
45
|
Base64.urlsafe_encode64(x, padding: false)
|
139
46
|
end
|
140
47
|
|
141
|
-
def
|
48
|
+
def base64decode(x)
|
142
49
|
Base64.urlsafe_decode64(x)
|
143
50
|
end
|
51
|
+
|
52
|
+
def decrypt_box(encrypted, pub, priv)
|
53
|
+
pub = decode_public_key(pub) unless pub.is_a? RbNaCl::PublicKey
|
54
|
+
priv = decode_private_key(priv) unless priv.is_a? RbNaCl::PrivateKey
|
55
|
+
|
56
|
+
ciphertext, nonce = encrypted.split('.', 2).map { |f| base64decode(f) }
|
57
|
+
RbNaCl::Box.new(pub, priv).decrypt(nonce, ciphertext)
|
58
|
+
end
|
59
|
+
|
60
|
+
def encrypt_box(plain, pub, priv)
|
61
|
+
pub = decode_public_key(pub) unless pub.is_a? RbNaCl::PublicKey
|
62
|
+
priv = decode_private_key(priv) unless priv.is_a? RbNaCl::PrivateKey
|
63
|
+
|
64
|
+
nonce = box_random_nonce
|
65
|
+
encrypted = RbNaCl::Box.new(pub, priv).encrypt(nonce, plain)
|
66
|
+
[encrypted, nonce].map { |f| base64encode(f) }.join(".")
|
67
|
+
end
|
144
68
|
end
|
145
69
|
|
146
70
|
private_constant :Crypto
|
data/lib/e3db/version.rb
CHANGED
@@ -21,4 +21,22 @@ cat > "$HOME/.tozny/integration-test/e3db.json" <<EOT
|
|
21
21
|
"public_key":"${PUBLIC_KEY}",
|
22
22
|
"private_key":"${PRIVATE_KEY}"
|
23
23
|
}
|
24
|
+
EOT
|
25
|
+
|
26
|
+
# Check if the config is already set
|
27
|
+
if [ ! -d "$HOME/.tozny/integration-test-share" ]; then
|
28
|
+
mkdir -p "$HOME/.tozny/integration-test-share"
|
29
|
+
fi
|
30
|
+
|
31
|
+
cat > "$HOME/.tozny/integration-test-share/e3db.json" <<EOT
|
32
|
+
{
|
33
|
+
"version":1,
|
34
|
+
"api_url":"${API_URL_2}",
|
35
|
+
"api_key_id":"${API_KEY_ID_2}",
|
36
|
+
"api_secret":"${API_SECRET_2}",
|
37
|
+
"client_id":"${CLIENT_ID_2}",
|
38
|
+
"client_email":"${CLIENT_EMAIL_2}",
|
39
|
+
"public_key":"${PUBLIC_KEY_2}",
|
40
|
+
"private_key":"${PRIVATE_KEY_2}"
|
41
|
+
}
|
24
42
|
EOT
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: e3db
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tozny, LLC
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-07
|
11
|
+
date: 2017-11-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -188,12 +188,13 @@ files:
|
|
188
188
|
- ".travis.yml"
|
189
189
|
- ".yardopts"
|
190
190
|
- Gemfile
|
191
|
-
- LICENSE.
|
191
|
+
- LICENSE.md
|
192
192
|
- README.md
|
193
193
|
- Rakefile
|
194
194
|
- bin/console
|
195
195
|
- bin/setup
|
196
196
|
- e3db.gemspec
|
197
|
+
- examples/registration.rb
|
197
198
|
- examples/simple.rb
|
198
199
|
- lib/e3db.rb
|
199
200
|
- lib/e3db/client.rb
|
@@ -223,7 +224,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
223
224
|
version: '0'
|
224
225
|
requirements: []
|
225
226
|
rubyforge_project:
|
226
|
-
rubygems_version: 2.6.
|
227
|
+
rubygems_version: 2.6.13
|
227
228
|
signing_key:
|
228
229
|
specification_version: 4
|
229
230
|
summary: e3db client SDK
|
data/LICENSE.txt
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
The MIT License (MIT)
|
2
|
-
|
3
|
-
Copyright (C) 2017, Tozny, LLC.
|
4
|
-
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
7
|
-
in the Software without restriction, including without limitation the rights
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
10
|
-
furnished to do so, subject to the following conditions:
|
11
|
-
|
12
|
-
The above copyright notice and this permission notice shall be included in
|
13
|
-
all copies or substantial portions of the Software.
|
14
|
-
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
-
THE SOFTWARE.
|