tanker-core 2.4.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,231 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+ require 'tanker/c_tanker'
5
+
6
+ module Tanker
7
+ class Core
8
+ def encrypt_stream(stream, encryption_options = nil)
9
+ Stream.do_stream_action(stream) { |cb| CTanker.tanker_stream_encrypt(@ctanker, cb, encryption_options, nil) }
10
+ end
11
+
12
+ def decrypt_stream(stream)
13
+ Stream.do_stream_action(stream) { |cb| CTanker.tanker_stream_decrypt(@ctanker, cb, nil) }
14
+ end
15
+ end
16
+
17
+ module Stream
18
+ def self.do_stream_action(stream)
19
+ in_wrapper = IoToTankerStreamWrapper.new(stream)
20
+ tanker_stream = (yield in_wrapper.read_method).get
21
+ out_wrapper = TankerStreamToIoWrapper.new(tanker_stream, in_wrapper)
22
+
23
+ out_io = out_wrapper.init_io
24
+
25
+ # The chain of possession is
26
+ # returned IO -> out_wrapper -> in_wrapper
27
+ # This allows us to close all the chain when the returned IO is closed
28
+ out_io.instance_eval do
29
+ @tanker_out_wrapper = out_wrapper
30
+
31
+ extend IoMixin
32
+ end
33
+
34
+ out_io
35
+ end
36
+ end
37
+
38
+ module IoMixin
39
+ def read(*)
40
+ out = super
41
+ raise @tanker_out_wrapper.error if @tanker_out_wrapper.error
42
+
43
+ out
44
+ end
45
+
46
+ def read_nonblock(*)
47
+ out = super
48
+ raise @tanker_out_wrapper.error if @tanker_out_wrapper.error
49
+
50
+ out
51
+ end
52
+
53
+ def readbyte(*)
54
+ out = super
55
+ raise @tanker_out_wrapper.error if @tanker_out_wrapper.error
56
+
57
+ out
58
+ end
59
+
60
+ def readchar(*)
61
+ out = super
62
+ raise @tanker_out_wrapper.error if @tanker_out_wrapper.error
63
+
64
+ out
65
+ end
66
+
67
+ def readline(*)
68
+ out = super
69
+ raise @tanker_out_wrapper.error if @tanker_out_wrapper.error
70
+
71
+ out
72
+ end
73
+
74
+ def readlines(*)
75
+ out = super
76
+ raise @tanker_out_wrapper.error if @tanker_out_wrapper.error
77
+
78
+ out
79
+ end
80
+
81
+ def readpartial(*)
82
+ out = super
83
+ raise @tanker_out_wrapper.error if @tanker_out_wrapper.error
84
+
85
+ out
86
+ end
87
+
88
+ def close(*)
89
+ if @tanker_out_wrapper
90
+ @tanker_out_wrapper.close
91
+ @tanker_out_wrapper = nil
92
+ end
93
+
94
+ super
95
+ end
96
+ end
97
+
98
+ # IMPORTANT: about synchronization
99
+ # Because of a design flaw in the Tanker C streaming API, it is difficult to
100
+ # handle cancelation. When canceling a read (closing the stream), Tanker
101
+ # doesn't forward the cancelation request to the input stream (there's no
102
+ # callback for that), which means that the callback maybe writing to a buffer
103
+ # that's being freed. We worked around that by adding a mutex and locking it
104
+ # in both the output stream and the input stream.
105
+ # Things to be careful about:
106
+ # - Do not copy data to the Tanker buffer if tanker_stream_close has been
107
+ # called
108
+ # - Do not call tanker_stream_read_operation_finish if tanker_stream_close has
109
+ # been called
110
+ # - On the output stream, do not call tanker_stream_read on a closed/closing
111
+ # stream. Though it is ok to close the stream after the call, but before the
112
+ # future is resolved.
113
+ class IoToTankerStreamWrapper
114
+ attr_reader :error
115
+ attr_reader :read_method
116
+ attr_reader :mutex
117
+
118
+ def initialize(read_in)
119
+ @read_in = read_in
120
+ # This is the object we will pass to ffi, it must be kept alive
121
+ @read_method = method(:read)
122
+ @closed = false
123
+ @mutex = Mutex.new
124
+ end
125
+
126
+ def close
127
+ raise 'mutex should be locked by the caller' unless @mutex.owned?
128
+
129
+ @closed = true
130
+ @read_in.close
131
+ end
132
+
133
+ def read(buffer, buffer_size, operation, _)
134
+ # We must not block Tanker's thread, for performance but also to avoid
135
+ # deadlocks, so let's run this function somewhere else
136
+ Thread.new do
137
+ do_read(buffer, buffer_size, operation)
138
+ end
139
+ end
140
+
141
+ private
142
+
143
+ def do_read(buffer, buffer_size, operation)
144
+ @mutex.synchronize do
145
+ return if @closed
146
+
147
+ if @read_in.eof?
148
+ CTanker.tanker_stream_read_operation_finish(operation, 0)
149
+ return
150
+ end
151
+ end
152
+
153
+ rbbuf = @read_in.readpartial(buffer_size)
154
+
155
+ @mutex.synchronize do
156
+ return if @closed
157
+
158
+ buffer.put_bytes(0, rbbuf)
159
+ CTanker.tanker_stream_read_operation_finish(operation, rbbuf.size)
160
+ end
161
+ rescue StandardError => e
162
+ @mutex.synchronize do
163
+ return if @closed
164
+
165
+ @error = e
166
+ CTanker.tanker_stream_read_operation_finish(operation, -1)
167
+ end
168
+ end
169
+ end
170
+
171
+ class TankerStreamToIoWrapper
172
+ attr_reader :error
173
+
174
+ def initialize(tanker_stream, substream)
175
+ @tanker_stream = tanker_stream
176
+ @substream = substream
177
+
178
+ # The user will only read on the pipe, so we need something that reads
179
+ # from Tanker and writes to the pipe, it's this thread.
180
+ Thread.new { read_thread }
181
+ end
182
+
183
+ def init_io
184
+ read, @write = IO.pipe
185
+ read
186
+ end
187
+
188
+ def close
189
+ tanker_stream = nil
190
+ @substream.mutex.synchronize do
191
+ @substream.close
192
+ tanker_stream = @tanker_stream
193
+ @tanker_stream = nil
194
+ end
195
+ CTanker.tanker_stream_close(tanker_stream).get
196
+ end
197
+
198
+ private
199
+
200
+ READ_SIZE = 1024 * 1024
201
+
202
+ def read_thread
203
+ ffibuf = FFI::MemoryPointer.new(:char, READ_SIZE)
204
+ begin
205
+ loop do
206
+ nb_read_fut = nil
207
+ @substream.mutex.synchronize do
208
+ unless @tanker_stream
209
+ raise TankerError, { error_code: Errors::OPERATION_CANCELED, error_message: 'stream operation canceled' }
210
+ end
211
+
212
+ nb_read_fut = CTanker.tanker_stream_read(@tanker_stream, ffibuf, READ_SIZE)
213
+ end
214
+ nb_read = nb_read_fut.get.address
215
+ break if nb_read.zero? # EOF
216
+
217
+ @write.write(ffibuf.read_string(nb_read))
218
+ end
219
+ rescue StandardError => e
220
+ @error = @substream.error || e
221
+ ensure
222
+ @write.close
223
+ end
224
+ end
225
+ end
226
+
227
+ private_constant :Stream
228
+ private_constant :IoMixin
229
+ private_constant :IoToTankerStreamWrapper
230
+ private_constant :TankerStreamToIoWrapper
231
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ffi'
4
+ require 'tanker/c_tanker/c_string'
5
+
6
+ module Tanker
7
+ class Verification; end
8
+
9
+ class EmailVerification < Verification
10
+ attr_reader :email, :verification_code
11
+
12
+ def initialize(email, verif_code)
13
+ ASSERT_UTF8.call(email)
14
+ ASSERT_UTF8.call(verif_code)
15
+
16
+ @email = email
17
+ @verification_code = verif_code
18
+ end
19
+ end
20
+
21
+ class PassphraseVerification < Verification
22
+ attr_reader :passphrase
23
+
24
+ def initialize(passphrase)
25
+ ASSERT_UTF8.call(passphrase)
26
+
27
+ @passphrase = passphrase
28
+ end
29
+ end
30
+
31
+ class VerificationKeyVerification < Verification
32
+ attr_reader :verification_key
33
+
34
+ def initialize(verif_key)
35
+ ASSERT_UTF8.call(verif_key)
36
+
37
+ @verification_key = verif_key
38
+ end
39
+ end
40
+
41
+ class OIDCIDTokenVerification < Verification
42
+ attr_reader :oidc_id_token
43
+
44
+ def initialize(oidc_id_token)
45
+ ASSERT_UTF8.call(oidc_id_token)
46
+
47
+ @oidc_id_token = oidc_id_token
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ffi'
4
+ require 'tanker/c_tanker/c_string'
5
+
6
+ module Tanker
7
+ class VerificationMethod
8
+ def ==(other)
9
+ self.class == other.class
10
+ end
11
+ end
12
+
13
+ class PassphraseVerificationMethod < VerificationMethod; end
14
+ class VerificationKeyVerificationMethod < VerificationMethod; end
15
+ class OIDCIDTokenVerificationMethod < VerificationMethod; end
16
+ class EmailVerificationMethod < VerificationMethod
17
+ attr_reader :email
18
+
19
+ def initialize(email)
20
+ @email = email
21
+ end
22
+
23
+ def ==(other)
24
+ super && email == other.email
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tanker
4
+ class Core
5
+ VERSION = '2.4.0.alpha.1'
6
+
7
+ def self.native_version
8
+ CTanker.tanker_version_string
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tanker/c_tanker/c_tanker_error'
4
+
5
+ module Tanker
6
+ # Errors returned by native tanker futures
7
+ class Error < StandardError
8
+ NO_ERROR = 0
9
+ INVALID_ARGUMENT = 1
10
+ INTERNAL_ERROR = 2
11
+ NETWORK_ERROR = 3
12
+ PRECONDITION_FAILED = 4
13
+ OPERATION_CANCELED = 5
14
+
15
+ DECRYPTION_FAILED = 6
16
+
17
+ GROUP_TOO_BIG = 7
18
+
19
+ INVALID_VERIFICATION = 8
20
+ TOO_MANY_ATTEMPTS = 9
21
+ EXPIRED_VERIFICATION = 10
22
+ IO_ERROR = 11
23
+ DEVICE_REVOKED = 12
24
+
25
+ CONFLICT = 13
26
+
27
+ def initialize(ctanker_error)
28
+ @code = ctanker_error[:error_code]
29
+ @message = ctanker_error[:error_message]
30
+ end
31
+
32
+ attr_reader :code
33
+ attr_reader :message
34
+ end
35
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ffi'
4
+ require 'tanker/c_tanker/c_string'
5
+
6
+ module Tanker
7
+ module CommonSharingOptions
8
+ def initialize(share_with_users: [], share_with_groups: [])
9
+ @recipients = FFI::MemoryPointer.new(:pointer, share_with_users.length)
10
+ @recipients.write_array_of_pointer(share_with_users.map { |id| CTanker.new_cstring id })
11
+
12
+ @groups = FFI::MemoryPointer.new(:pointer, share_with_groups.length)
13
+ @groups.write_array_of_pointer(share_with_groups.map { |id| CTanker.new_cstring id })
14
+
15
+ self[:version] = 2
16
+ self[:recipient_public_identities] = @recipients
17
+ self[:nb_recipient_public_identities] = share_with_users.length
18
+ self[:recipient_group_ids] = @groups
19
+ self[:nb_recipient_group_ids] = share_with_groups.length
20
+ end
21
+
22
+ def self.included(base)
23
+ base.layout :version, :uint8,
24
+ :recipient_public_identities, :pointer,
25
+ :nb_recipient_public_identities, :uint32,
26
+ :recipient_group_ids, :pointer,
27
+ :nb_recipient_group_ids, :uint32
28
+ end
29
+ end
30
+
31
+ # Options that can be given when sharing data
32
+ class SharingOptions < FFI::Struct
33
+ include CommonSharingOptions
34
+ end
35
+
36
+ # Options that can be given when encrypting data
37
+ class EncryptionOptions < FFI::Struct
38
+ include CommonSharingOptions
39
+ end
40
+
41
+ private_constant :CommonSharingOptions
42
+ end
metadata ADDED
@@ -0,0 +1,192 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tanker-core
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.4.0.alpha.1
5
+ platform: ruby
6
+ authors:
7
+ - Tanker team
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-06-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ffi
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.13'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler-audit
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.7'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '13.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '13.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.85.1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.85.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubygems-tasks
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.2.5
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.2.5
111
+ - !ruby/object:Gem::Dependency
112
+ name: tanker-identity
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.1'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.1'
125
+ description: |
126
+ Ruby bindings for the Tanker SDK.
127
+ Tanker is a platform as a service that allows you to easily protect your users' data with end-to-end encryption through a SDK
128
+ email:
129
+ - tech@tanker.io
130
+ executables: []
131
+ extensions: []
132
+ extra_rdoc_files: []
133
+ files:
134
+ - LICENSE
135
+ - README.rst
136
+ - lib/tanker-core.rb
137
+ - lib/tanker/admin.rb
138
+ - lib/tanker/admin/app.rb
139
+ - lib/tanker/admin/c_admin.rb
140
+ - lib/tanker/admin/c_admin/c_app_descriptor.rb
141
+ - lib/tanker/c_tanker.rb
142
+ - lib/tanker/c_tanker/c_device_info.rb
143
+ - lib/tanker/c_tanker/c_event.rb
144
+ - lib/tanker/c_tanker/c_future.rb
145
+ - lib/tanker/c_tanker/c_log_record.rb
146
+ - lib/tanker/c_tanker/c_string.rb
147
+ - lib/tanker/c_tanker/c_tanker_error.rb
148
+ - lib/tanker/c_tanker/c_verification.rb
149
+ - lib/tanker/c_tanker/c_verification_method.rb
150
+ - lib/tanker/core.rb
151
+ - lib/tanker/core/attach_result.rb
152
+ - lib/tanker/core/encryption.rb
153
+ - lib/tanker/core/encryption_session.rb
154
+ - lib/tanker/core/group.rb
155
+ - lib/tanker/core/init.rb
156
+ - lib/tanker/core/log_record.rb
157
+ - lib/tanker/core/options.rb
158
+ - lib/tanker/core/session.rb
159
+ - lib/tanker/core/status.rb
160
+ - lib/tanker/core/stream.rb
161
+ - lib/tanker/core/verification.rb
162
+ - lib/tanker/core/verification_method.rb
163
+ - lib/tanker/core/version.rb
164
+ - lib/tanker/error.rb
165
+ - lib/tanker/sharing_options.rb
166
+ - vendor/libctanker/linux64/tanker/lib/libctanker.so
167
+ homepage: https://tanker.io
168
+ licenses:
169
+ - Apache-2.0
170
+ metadata:
171
+ homepage_uri: https://tanker.io
172
+ source_code_uri: https://github.com/TankerHQ/sdk-ruby
173
+ post_install_message:
174
+ rdoc_options: []
175
+ require_paths:
176
+ - lib
177
+ required_ruby_version: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ version: 2.5.0
182
+ required_rubygems_version: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - ">"
185
+ - !ruby/object:Gem::Version
186
+ version: 1.3.1
187
+ requirements: []
188
+ rubygems_version: 3.0.6
189
+ signing_key:
190
+ specification_version: 4
191
+ summary: Ruby SDK for Tanker
192
+ test_files: []