rbthemis 0.10.0 → 0.14.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.
Files changed (4) hide show
  1. checksums.yaml +5 -5
  2. data/lib/rbthemis.rb +992 -0
  3. metadata +12 -15
  4. data/lib/rubythemis.rb +0 -645
data/lib/rbthemis.rb ADDED
@@ -0,0 +1,992 @@
1
+ #
2
+ # Copyright (c) 2015 Cossack Labs Limited
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ require 'ffi'
18
+
19
+ module ThemisCommon
20
+ def string_to_pointer_size(string)
21
+ string_buf = FFI::MemoryPointer.from_string(string)
22
+ [string_buf, string.bytesize]
23
+ end
24
+
25
+ def empty?(value)
26
+ return value.nil? || value.empty?
27
+ end
28
+
29
+ module_function :string_to_pointer_size
30
+ module_function :empty?
31
+ end
32
+
33
+ module ThemisImport
34
+ extend FFI::Library
35
+ ffi_lib 'themis'
36
+
37
+ callback :get_pub_key_by_id_type,
38
+ [:pointer, :int, :pointer, :int, :pointer], :int
39
+ callback :send_callback_type, [:pointer, :int, :uint], :int
40
+ callback :receive_callback_type, [:pointer, :int, :uint], :int
41
+
42
+ class CallbacksStruct < FFI::Struct
43
+ layout :send_data, :send_callback_type,
44
+ :receive_data, :receive_callback_type,
45
+ :state_changed, :pointer,
46
+ :get_pub_key_for_id, :get_pub_key_by_id_type,
47
+ :user_data, :pointer
48
+ end
49
+
50
+ attach_function :secure_session_create,
51
+ [:pointer, :uint, :pointer, :uint, :pointer], :pointer
52
+ attach_function :secure_session_destroy, [:pointer], :int
53
+ attach_function :secure_session_generate_connect_request,
54
+ [:pointer, :pointer, :pointer], :int
55
+
56
+ attach_function :secure_session_wrap,
57
+ [:pointer, :pointer, :int, :pointer, :pointer], :int
58
+ attach_function :secure_session_unwrap,
59
+ [:pointer, :pointer, :int, :pointer, :pointer], :int
60
+ attach_function :secure_session_is_established, [:pointer], :bool
61
+
62
+ attach_function :themis_secure_message_encrypt,
63
+ [:pointer, :int, :pointer, :int, :pointer,
64
+ :int, :pointer, :pointer], :int
65
+ attach_function :themis_secure_message_decrypt,
66
+ [:pointer, :int, :pointer, :int, :pointer,
67
+ :int, :pointer, :pointer], :int
68
+ attach_function :themis_secure_message_sign,
69
+ [:pointer, :int, :pointer, :int, :pointer,
70
+ :pointer], :int
71
+ attach_function :themis_secure_message_verify,
72
+ [:pointer, :int, :pointer, :int, :pointer,
73
+ :pointer], :int
74
+
75
+ attach_function :themis_gen_rsa_key_pair,
76
+ [:pointer, :pointer, :pointer, :pointer], :int
77
+ attach_function :themis_gen_ec_key_pair,
78
+ [:pointer, :pointer, :pointer, :pointer], :int
79
+ attach_function :themis_gen_sym_key,
80
+ [:pointer, :pointer], :int
81
+
82
+ THEMIS_KEY_INVALID = 0
83
+ THEMIS_KEY_RSA_PRIVATE = 1
84
+ THEMIS_KEY_RSA_PUBLIC = 2
85
+ THEMIS_KEY_EC_PRIVATE = 3
86
+ THEMIS_KEY_EC_PUBLIC = 4
87
+
88
+ attach_function :themis_is_valid_asym_key, [:pointer, :int], :int
89
+ attach_function :themis_get_asym_key_kind, [:pointer, :int], :int
90
+
91
+ attach_function :themis_secure_cell_encrypt_seal,
92
+ [:pointer, :int, :pointer, :int, :pointer, :int,
93
+ :pointer, :pointer], :int
94
+ attach_function :themis_secure_cell_decrypt_seal,
95
+ [:pointer, :int, :pointer, :int, :pointer, :int,
96
+ :pointer, :pointer], :int
97
+
98
+ attach_function :themis_secure_cell_encrypt_seal_with_passphrase,
99
+ [:pointer, :int, :pointer, :int, :pointer, :int,
100
+ :pointer, :pointer], :int
101
+ attach_function :themis_secure_cell_decrypt_seal_with_passphrase,
102
+ [:pointer, :int, :pointer, :int, :pointer, :int,
103
+ :pointer, :pointer], :int
104
+
105
+ attach_function :themis_secure_cell_encrypt_token_protect,
106
+ [:pointer, :int, :pointer, :int, :pointer, :int,
107
+ :pointer, :pointer, :pointer, :pointer], :int
108
+ attach_function :themis_secure_cell_decrypt_token_protect,
109
+ [:pointer, :int, :pointer, :int, :pointer, :int,
110
+ :pointer, :int, :pointer, :pointer], :int
111
+
112
+ attach_function :themis_secure_cell_encrypt_context_imprint,
113
+ [:pointer, :int, :pointer, :int, :pointer, :int,
114
+ :pointer, :pointer], :int
115
+ attach_function :themis_secure_cell_decrypt_context_imprint,
116
+ [:pointer, :int, :pointer, :int, :pointer, :int,
117
+ :pointer, :pointer], :int
118
+
119
+ begin
120
+ attach_function :secure_comparator_create, [], :pointer
121
+ attach_function :secure_comparator_destroy, [:pointer], :int
122
+ attach_function :secure_comparator_append_secret,
123
+ [:pointer, :pointer, :int], :int
124
+ attach_function :secure_comparator_begin_compare,
125
+ [:pointer, :pointer, :pointer], :int
126
+ attach_function :secure_comparator_proceed_compare,
127
+ [:pointer, :pointer, :int, :pointer, :pointer], :int
128
+ attach_function :secure_comparator_get_result, [:pointer], :int
129
+ rescue FFI::NotFoundError
130
+ end
131
+ end
132
+
133
+ module Themis
134
+ extend ThemisCommon
135
+ extend ThemisImport
136
+
137
+ extend Gem::Deprecate
138
+
139
+ BUFFER_TOO_SMALL = 14
140
+ SUCCESS = 0
141
+ FAIL = 11
142
+ INVALID_ARGUMENT = 12
143
+ SEND_AS_IS = 1
144
+
145
+ # Common class of errors caused by Themis functions.
146
+ # You can access the numerical value via "error_code" attribute.
147
+ # Human-readable message is accessible via "message" attribute.
148
+ class ThemisError < StandardError
149
+ attr_reader :error_code
150
+
151
+ def initialize(error_code = INVALID_ARGUMENT)
152
+ super
153
+ @error_code = error_code
154
+ end
155
+ end
156
+
157
+ class Callbacks
158
+ def get_pub_key_by_id(id)
159
+ raise ThemisError, 'Callback is not implemented: get_pub_key_by_id'
160
+ end
161
+
162
+ def send(message)
163
+ raise ThemisError, 'Callback is not implemented: send'
164
+ end
165
+
166
+ def receive
167
+ raise ThemisError, 'Callback is not implemented: receive'
168
+ end
169
+ end
170
+
171
+ class SKeyPairGen
172
+ include ThemisCommon
173
+ include ThemisImport
174
+
175
+ def ec
176
+ private_key_length = FFI::MemoryPointer.new(:uint)
177
+ public_key_length = FFI::MemoryPointer.new(:uint)
178
+
179
+ res = themis_gen_ec_key_pair(
180
+ nil, private_key_length, nil, public_key_length)
181
+ if res != BUFFER_TOO_SMALL
182
+ raise ThemisError, "Themis failed generating EC KeyPair: #{res}"
183
+ end
184
+
185
+ private_key = FFI::MemoryPointer.new(:char, private_key_length.read_uint)
186
+ public_key = FFI::MemoryPointer.new(:char, public_key_length.read_uint)
187
+
188
+ res = themis_gen_ec_key_pair(
189
+ private_key, private_key_length, public_key, public_key_length)
190
+ if res != SUCCESS
191
+ raise ThemisError, "Themis failed generating EC KeyPair: #{res}"
192
+ end
193
+
194
+ [private_key.get_bytes(0, private_key_length.read_uint),
195
+ public_key.get_bytes(0, public_key_length.read_uint)]
196
+ end
197
+
198
+ def rsa
199
+ private_key_length = FFI::MemoryPointer.new(:uint)
200
+ public_key_length = FFI::MemoryPointer.new(:uint)
201
+
202
+ res = themis_gen_rsa_key_pair(
203
+ nil, private_key_length, nil, public_key_length)
204
+ if res != BUFFER_TOO_SMALL
205
+ raise ThemisError, "Themis failed generating RSA KeyPair: #{res}"
206
+ end
207
+
208
+ private_key = FFI::MemoryPointer.new(:char, private_key_length.read_uint)
209
+ public_key = FFI::MemoryPointer.new(:char, public_key_length.read_uint)
210
+
211
+ res = themis_gen_rsa_key_pair(
212
+ private_key, private_key_length, public_key, public_key_length)
213
+ if res != SUCCESS
214
+ raise ThemisError, "Themis failed generating RSA KeyPair: #{res}"
215
+ end
216
+
217
+ [private_key.get_bytes(0, private_key_length.read_uint),
218
+ public_key.get_bytes(0, public_key_length.read_uint)]
219
+ end
220
+ end
221
+
222
+ def Themis.valid_key(key)
223
+ if key.nil? || key.empty?
224
+ return false
225
+ end
226
+ key_, len_ = string_to_pointer_size(key)
227
+ return themis_is_valid_asym_key(key_, len_) == SUCCESS
228
+ end
229
+
230
+ def Themis.private_key(key)
231
+ key_, len_ = string_to_pointer_size(key)
232
+ kind = themis_get_asym_key_kind(key_, len_)
233
+ return kind == ThemisImport::THEMIS_KEY_RSA_PRIVATE \
234
+ || kind == ThemisImport::THEMIS_KEY_EC_PRIVATE
235
+ end
236
+
237
+ def Themis.public_key(key)
238
+ key_, len_ = string_to_pointer_size(key)
239
+ kind = themis_get_asym_key_kind(key_, len_)
240
+ return kind == ThemisImport::THEMIS_KEY_RSA_PUBLIC \
241
+ || kind == ThemisImport::THEMIS_KEY_EC_PUBLIC
242
+ end
243
+
244
+ class Ssession
245
+ include ThemisCommon
246
+ include ThemisImport
247
+
248
+ extend Gem::Deprecate
249
+
250
+ MAPPING = {}
251
+
252
+ GetPubKeyByIDCallback =
253
+ FFI::Function.new(:int, [:pointer, :int, :pointer, :int, :pointer]) do |id_buf, id_length, pubkey_buf, pubkey_length, obj|
254
+ pub_key = MAPPING[obj.read_uint64].get_pub_key_by_id(
255
+ id_buf.get_bytes(0, id_length))
256
+ return -1 unless pub_key
257
+ pubkey_buf.put_bytes(0, pub_key)
258
+ 0
259
+ end
260
+
261
+ def initialize(id, private_key, transport)
262
+ id_buf, id_length = string_to_pointer_size(id)
263
+ private_key_buf, private_key_length = string_to_pointer_size(private_key)
264
+
265
+ @callbacks = CallbacksStruct.new
266
+ @callbacks[:get_pub_key_for_id] = GetPubKeyByIDCallback
267
+
268
+ MAPPING[transport.object_id] = transport
269
+ @transport_obj_id = transport.object_id
270
+
271
+ @callbacks[:user_data] = FFI::MemoryPointer.new(:uint64)
272
+ @callbacks[:user_data].write_uint64(@transport_obj_id)
273
+
274
+ @session = secure_session_create(
275
+ id_buf, id_length, private_key_buf, private_key_length, @callbacks)
276
+
277
+ raise ThemisError, 'Secure Session failed creating' if @session.null?
278
+ end
279
+
280
+ def is_established
281
+ established?
282
+ end
283
+ deprecate(:is_established, :established?, 2018, 6)
284
+
285
+ def established?
286
+ secure_session_is_established @session
287
+ end
288
+
289
+ def connect_request
290
+ connect_request_length = FFI::MemoryPointer.new(:uint)
291
+ res = secure_session_generate_connect_request(
292
+ @session, nil, connect_request_length)
293
+ if res != BUFFER_TOO_SMALL
294
+ raise(ThemisError,
295
+ "Secure Session failed making connection request: #{res}")
296
+ end
297
+ connect_request = FFI::MemoryPointer.new(
298
+ :char, connect_request_length.read_uint)
299
+ res = secure_session_generate_connect_request(
300
+ @session, connect_request, connect_request_length)
301
+ if res != SUCCESS
302
+ raise(ThemisError,
303
+ "Secure Session failed making connection request: #{res}")
304
+ end
305
+ connect_request.get_bytes(0, connect_request_length.read_uint)
306
+ end
307
+
308
+ def unwrap(message)
309
+ message_, message_length_ = string_to_pointer_size message
310
+ unwrapped_message_length = FFI::MemoryPointer.new(:uint)
311
+
312
+ res = secure_session_unwrap(
313
+ @session, message_, message_length_, nil, unwrapped_message_length)
314
+ return SUCCESS, '' if res == SUCCESS
315
+ if res != BUFFER_TOO_SMALL
316
+ raise ThemisError, "Secure Session failed decrypting: #{res}"
317
+ end
318
+
319
+ unwrapped_message = FFI::MemoryPointer.new(
320
+ :char, unwrapped_message_length.read_uint)
321
+ res = secure_session_unwrap(@session, message_, message_length_,
322
+ unwrapped_message, unwrapped_message_length)
323
+ if res != SUCCESS && res != SEND_AS_IS
324
+ raise ThemisError, "Secure Session failed decrypting: #{res}"
325
+ end
326
+
327
+ [res, unwrapped_message.get_bytes(0, unwrapped_message_length.read_uint)]
328
+ end
329
+
330
+ def wrap(message)
331
+ message_, message_length_ = string_to_pointer_size(message)
332
+
333
+ wrapped_message_length = FFI::MemoryPointer.new(:uint)
334
+ res = secure_session_wrap(
335
+ @session, message_, message_length_, nil, wrapped_message_length)
336
+ if res != BUFFER_TOO_SMALL
337
+ raise ThemisError, "Secure Session failed encrypting: #{res}"
338
+ end
339
+
340
+ wrapped_message = FFI::MemoryPointer.new(
341
+ :char, wrapped_message_length.read_uint)
342
+ res = secure_session_wrap(@session, message_, message_length_,
343
+ wrapped_message, wrapped_message_length)
344
+ if res != SUCCESS && res != SEND_AS_IS
345
+ raise ThemisError, "Secure Session failed encrypting: #{res}"
346
+ end
347
+
348
+ wrapped_message.get_bytes(0, wrapped_message_length.read_uint)
349
+ end
350
+
351
+ def finalize
352
+ res = secure_session_destroy(@session)
353
+ raise ThemisError, 'Secure Session failed destroying' if res != SUCCESS
354
+ end
355
+ end
356
+
357
+ class Smessage
358
+ include ThemisCommon
359
+ include ThemisImport
360
+
361
+ def initialize(private_key, peer_public_key)
362
+ if not Themis.valid_key(private_key)
363
+ raise ThemisError, "Secure Message: invalid private key"
364
+ end
365
+ if not Themis.valid_key(peer_public_key)
366
+ raise ThemisError, "Secure Message: invalid public key"
367
+ end
368
+ if not Themis.private_key(private_key)
369
+ raise ThemisError, "Secure Message: public key used instead of private"
370
+ end
371
+ if not Themis.public_key(peer_public_key)
372
+ raise ThemisError, "Secure Message: private key used instead of public"
373
+ end
374
+
375
+ @private_key, @private_key_length = string_to_pointer_size(private_key)
376
+ @peer_public_key, @peer_public_key_length =
377
+ string_to_pointer_size(peer_public_key)
378
+ end
379
+
380
+ def wrap(message)
381
+ message_, message_length_ = string_to_pointer_size(message)
382
+
383
+ wrapped_message_length = FFI::MemoryPointer.new(:uint)
384
+ res = themis_secure_message_encrypt(
385
+ @private_key, @private_key_length, @peer_public_key,
386
+ @peer_public_key_length, message_, message_length_,
387
+ nil, wrapped_message_length)
388
+ if res != BUFFER_TOO_SMALL
389
+ raise ThemisError, "Secure Message failed to encrypt: #{res}"
390
+ end
391
+
392
+ wrapped_message = FFI::MemoryPointer.new(
393
+ :char, wrapped_message_length.read_uint)
394
+ res = themis_secure_message_encrypt(
395
+ @private_key, @private_key_length, @peer_public_key,
396
+ @peer_public_key_length, message_, message_length_,
397
+ wrapped_message, wrapped_message_length)
398
+ if res != SUCCESS
399
+ raise ThemisError, "Secure Message failed to encrypt: #{res}"
400
+ end
401
+
402
+ wrapped_message.get_bytes(0, wrapped_message_length.read_uint)
403
+ end
404
+
405
+ def unwrap(message)
406
+ message_, message_length_ = string_to_pointer_size(message)
407
+ unwrapped_message_length = FFI::MemoryPointer.new(:uint)
408
+ res = themis_secure_message_decrypt(
409
+ @private_key, @private_key_length, @peer_public_key,
410
+ @peer_public_key_length, message_, message_length_,
411
+ nil, unwrapped_message_length)
412
+ if res != BUFFER_TOO_SMALL
413
+ raise ThemisError, "Secure Message failed to decrypt: #{res}"
414
+ end
415
+
416
+ unwrapped_message = FFI::MemoryPointer.new(
417
+ :char, unwrapped_message_length.read_uint)
418
+ res = themis_secure_message_decrypt(
419
+ @private_key, @private_key_length, @peer_public_key,
420
+ @peer_public_key_length, message_, message_length_,
421
+ unwrapped_message, unwrapped_message_length)
422
+ if res != SUCCESS
423
+ raise ThemisError, "Secure Message failed to decrypt: #{res}"
424
+ end
425
+
426
+ unwrapped_message.get_bytes(0, unwrapped_message_length.read_uint)
427
+ end
428
+ end
429
+
430
+ def Ssign(*args)
431
+ s_sign(*args)
432
+ end
433
+ deprecate :Ssign, :s_sign, 2018, 6
434
+
435
+ def s_sign(private_key, message)
436
+ if not valid_key(private_key)
437
+ raise ThemisError, "Secure Message: invalid private key"
438
+ end
439
+ if not private_key(private_key)
440
+ raise ThemisError, "Secure Message: public key used instead of private"
441
+ end
442
+
443
+ private_key_, private_key_length_ = string_to_pointer_size(private_key)
444
+ message_, message_length_ = string_to_pointer_size(message)
445
+
446
+ wrapped_message_length = FFI::MemoryPointer.new(:uint)
447
+ res = themis_secure_message_sign(
448
+ private_key_, private_key_length_, message_,
449
+ message_length_, nil, wrapped_message_length)
450
+ if res != BUFFER_TOO_SMALL
451
+ raise ThemisError, "Secure Message failed to sign: #{res}"
452
+ end
453
+
454
+ wrapped_message = FFI::MemoryPointer.new(
455
+ :char, wrapped_message_length.read_uint)
456
+ res = themis_secure_message_sign(
457
+ private_key_, private_key_length_, message_,
458
+ message_length_, wrapped_message, wrapped_message_length)
459
+ if res != SUCCESS
460
+ raise ThemisError, "Secure Message failed to sign: #{res}"
461
+ end
462
+
463
+ wrapped_message.get_bytes(0, wrapped_message_length.read_uint)
464
+ end
465
+
466
+ def Sverify(*args)
467
+ s_verify(*args)
468
+ end
469
+ deprecate :Sverify, :s_verify, 2018, 6
470
+
471
+ def s_verify(peer_public_key, message)
472
+ if not valid_key(peer_public_key)
473
+ raise ThemisError, "Secure Message: invalid public key"
474
+ end
475
+ if not public_key(peer_public_key)
476
+ raise ThemisError, "Secure Message: private key used instead of public"
477
+ end
478
+
479
+ public_key_, public_key_length_ = string_to_pointer_size(peer_public_key)
480
+ message_, message_length_ = string_to_pointer_size(message)
481
+
482
+ unwrapped_message_length = FFI::MemoryPointer.new(:uint)
483
+ res = themis_secure_message_verify(
484
+ public_key_, public_key_length_, message_,
485
+ message_length_, nil, unwrapped_message_length)
486
+ if res != BUFFER_TOO_SMALL
487
+ raise ThemisError, "Secure Message failed to verify: #{res}"
488
+ end
489
+
490
+ unwrapped_message = FFI::MemoryPointer.new(
491
+ :char, unwrapped_message_length.read_uint)
492
+ res = themis_secure_message_verify(
493
+ public_key_, public_key_length_, message_,
494
+ message_length_, unwrapped_message, unwrapped_message_length)
495
+ if res != SUCCESS
496
+ raise ThemisError, "Secure Message failed to verify: #{res}"
497
+ end
498
+
499
+ unwrapped_message.get_bytes(0, unwrapped_message_length.read_uint)
500
+ end
501
+
502
+ def gen_sym_key
503
+ key_length = FFI::MemoryPointer.new(:uint)
504
+
505
+ res = themis_gen_sym_key(nil, key_length)
506
+ if res != BUFFER_TOO_SMALL
507
+ raise ThemisError, "failed to get symmetric key size: #{res}"
508
+ end
509
+
510
+ key = FFI::MemoryPointer.new(:char, key_length.read_uint)
511
+
512
+ res = themis_gen_sym_key(key, key_length)
513
+ if res != SUCCESS
514
+ raise ThemisError, "failed to generate symmetric key: #{res}"
515
+ end
516
+
517
+ return key.get_bytes(0, key_length.read_uint)
518
+ end
519
+
520
+ # Scell base class is retained for compatibility.
521
+ # New code should use ScellSeal, ScellTokenProtect, or ScellContextImprint.
522
+ class Scell
523
+ include ThemisCommon
524
+ include ThemisImport
525
+
526
+ SEAL_MODE = 0
527
+ TOKEN_PROTECT_MODE = 1
528
+ CONTEXT_IMPRINT_MODE = 2
529
+
530
+ def initialize(key, mode)
531
+ # We could have replaced this with "self.new" but it's not possible
532
+ # to override new *and* keep Scell as superclass of ScellSeal et al.
533
+ # So we keep an instance of appropriate class here and never call
534
+ # superclass initialize in subclasses.
535
+ case mode
536
+ when SEAL_MODE
537
+ @cell = ScellSeal.new(key)
538
+ when TOKEN_PROTECT_MODE
539
+ @cell = ScellTokenProtect.new(key)
540
+ when CONTEXT_IMPRINT_MODE
541
+ @cell = ScellContextImprint.new(key)
542
+ else
543
+ raise ThemisError, "unknown Secure Cell mode: #{mode}"
544
+ end
545
+ warn "NOTE: #{self.class.name} is deprecated; use #{@cell.class.name} instead."
546
+ end
547
+
548
+ def encrypt(message, context = nil)
549
+ @cell.encrypt(message, context)
550
+ end
551
+
552
+ def decrypt(message, context = nil)
553
+ @cell.decrypt(message, context)
554
+ end
555
+ end
556
+
557
+ # Secure Cell in Seal mode.
558
+ class ScellSeal < Scell
559
+ include ThemisCommon
560
+ include ThemisImport
561
+
562
+ # Make a new Secure Cell with given key.
563
+ # The key must not be empty and is treated as binary data.
564
+ # You can use Themis::gen_sym_key to generate new keys.
565
+ def initialize(key)
566
+ if empty? key
567
+ raise ThemisError, "key cannot be empty"
568
+ end
569
+ @key, @key_length = string_to_pointer_size(key)
570
+ end
571
+
572
+ # Encrypts message with given optional context.
573
+ # The context is cryptographically combined with message but is not included
574
+ # into encrypted data, you will need to provide the same context for decryption.
575
+ # Resulting encrypted message includes authentication token.
576
+ # Message must not be empty, but context may be omitted.
577
+ # Both message and context are treated as binary data.
578
+ def encrypt(message, context = nil)
579
+ if empty? message
580
+ raise ThemisError, "message cannot be empty"
581
+ end
582
+
583
+ message_, message_length_ = string_to_pointer_size(message)
584
+ context_, context_length_ =
585
+ context.nil? ? [nil, 0] : string_to_pointer_size(context)
586
+
587
+ encrypted_length = FFI::MemoryPointer.new(:uint)
588
+ res = themis_secure_cell_encrypt_seal(
589
+ @key, @key_length, context_, context_length_,
590
+ message_, message_length_, nil, encrypted_length)
591
+ if res != BUFFER_TOO_SMALL
592
+ raise ThemisError.new(res), "encrypt failed"
593
+ end
594
+
595
+ encrypted_message = FFI::MemoryPointer.new(:char, encrypted_length.read_uint)
596
+ res = themis_secure_cell_encrypt_seal(
597
+ @key, @key_length, context_, context_length_,
598
+ message_, message_length_, encrypted_message, encrypted_length)
599
+ if res != SUCCESS
600
+ raise ThemisError.new(res), "encrypt failed"
601
+ end
602
+
603
+ encrypted_message.get_bytes(0, encrypted_length.read_uint)
604
+ end
605
+
606
+ # Decrypts message with given context.
607
+ # The context must be the same as the one used during encryption,
608
+ # or be omitted or set to nil if no context were used.
609
+ # Decrypted message is returned as binary data.
610
+ def decrypt(message, context = nil)
611
+ if empty? message
612
+ raise ThemisError, "message cannot be empty"
613
+ end
614
+
615
+ message_, message_length_ = string_to_pointer_size(message)
616
+ context_, context_length_ =
617
+ context.nil? ? [nil, 0] : string_to_pointer_size(context)
618
+
619
+ decrypted_length = FFI::MemoryPointer.new(:uint)
620
+ res = themis_secure_cell_decrypt_seal(
621
+ @key, @key_length, context_, context_length_,
622
+ message_, message_length_, nil, decrypted_length)
623
+ if res != BUFFER_TOO_SMALL
624
+ raise ThemisError.new(res), "decrypt failed"
625
+ end
626
+
627
+ decrypted_message = FFI::MemoryPointer.new(:char, decrypted_length.read_uint)
628
+ res = themis_secure_cell_decrypt_seal(
629
+ @key, @key_length, context_, context_length_,
630
+ message_, message_length_, decrypted_message, decrypted_length)
631
+ if res != SUCCESS
632
+ raise ThemisError.new(res), "decrypt failed"
633
+ end
634
+
635
+ decrypted_message.get_bytes(0, decrypted_length.read_uint)
636
+ end
637
+ end
638
+
639
+ # Secure Cell in Seal mode.
640
+ class ScellSealPassphrase < ScellSeal
641
+ include ThemisCommon
642
+ include ThemisImport
643
+
644
+ # Make a new Secure Cell with given passphrase.
645
+ # The passphrase must not be empty.
646
+ # If the passphrase is not binary it will be encoded in UTF-8 by default,
647
+ # you can use optional "encoding:" argument to use a different encoding.
648
+ def initialize(passphrase, encoding: Encoding::UTF_8)
649
+ if empty? passphrase
650
+ raise ThemisError, "passphrase cannot be empty"
651
+ end
652
+ if passphrase.encoding != Encoding::BINARY
653
+ passphrase = passphrase.encode(encoding)
654
+ end
655
+ @passphrase, @passphrase_length = string_to_pointer_size(passphrase)
656
+ end
657
+
658
+ # Encrypts message with given optional context.
659
+ # The context is cryptographically combined with message but is not included
660
+ # into encrypted data, you will need to provide the same context for decryption.
661
+ # Resulting encrypted message includes authentication token.
662
+ # Message must not be empty, but context may be omitted.
663
+ # Both message and context are treated as binary data.
664
+ def encrypt(message, context = nil)
665
+ if empty? message
666
+ raise ThemisError, "message cannot be empty"
667
+ end
668
+
669
+ message_, message_length_ = string_to_pointer_size(message)
670
+ context_, context_length_ =
671
+ context.nil? ? [nil, 0] : string_to_pointer_size(context)
672
+
673
+ encrypted_length = FFI::MemoryPointer.new(:uint)
674
+ res = themis_secure_cell_encrypt_seal_with_passphrase(
675
+ @passphrase, @passphrase_length, context_, context_length_,
676
+ message_, message_length_, nil, encrypted_length)
677
+ if res != BUFFER_TOO_SMALL
678
+ raise ThemisError.new(res), "encrypt failed"
679
+ end
680
+
681
+ encrypted_message = FFI::MemoryPointer.new(:char, encrypted_length.read_uint)
682
+ res = themis_secure_cell_encrypt_seal_with_passphrase(
683
+ @passphrase, @passphrase_length, context_, context_length_,
684
+ message_, message_length_, encrypted_message, encrypted_length)
685
+ if res != SUCCESS
686
+ raise ThemisError.new(res), "encrypt failed"
687
+ end
688
+
689
+ encrypted_message.get_bytes(0, encrypted_length.read_uint)
690
+ end
691
+
692
+ # Decrypts message with given context.
693
+ # The context must be the same as the one used during encryption,
694
+ # or be omitted or set to nil if no context were used.
695
+ # Decrypted message is returned as binary data.
696
+ def decrypt(message, context = nil)
697
+ if empty? message
698
+ raise ThemisError, "message cannot be empty"
699
+ end
700
+
701
+ message_, message_length_ = string_to_pointer_size(message)
702
+ context_, context_length_ =
703
+ context.nil? ? [nil, 0] : string_to_pointer_size(context)
704
+
705
+ decrypted_length = FFI::MemoryPointer.new(:uint)
706
+ res = themis_secure_cell_decrypt_seal_with_passphrase(
707
+ @passphrase, @passphrase_length, context_, context_length_,
708
+ message_, message_length_, nil, decrypted_length)
709
+ if res != BUFFER_TOO_SMALL
710
+ raise ThemisError.new(res), "decrypt failed"
711
+ end
712
+
713
+ decrypted_message = FFI::MemoryPointer.new(:char, decrypted_length.read_uint)
714
+ res = themis_secure_cell_decrypt_seal_with_passphrase(
715
+ @passphrase, @passphrase_length, context_, context_length_,
716
+ message_, message_length_, decrypted_message, decrypted_length)
717
+ if res != SUCCESS
718
+ raise ThemisError.new(res), "decrypt failed"
719
+ end
720
+
721
+ decrypted_message.get_bytes(0, decrypted_length.read_uint)
722
+ end
723
+ end
724
+
725
+ # Secure Cell in Token Protect mode.
726
+ class ScellTokenProtect < Scell
727
+ include ThemisCommon
728
+ include ThemisImport
729
+
730
+ # Make a new Secure Cell with given key.
731
+ # The key must not be empty and is treated as binary data.
732
+ # You can use Themis::gen_sym_key to generate new keys.
733
+ def initialize(key)
734
+ if empty? key
735
+ raise ThemisError, "key cannot be empty"
736
+ end
737
+ @key, @key_length = string_to_pointer_size(key)
738
+ end
739
+
740
+ # Encrypts message with given optional context.
741
+ # The context is cryptographically combined with message but is not included
742
+ # into encrypted data, you will need to provide the same context for decryption.
743
+ # Resulting encrypted message (the same length as input) and authentication token
744
+ # are returned separately; you will need to provide them both for decryption.
745
+ # Message must not be empty, but context may be omitted.
746
+ # Both message and context are treated as binary data.
747
+ def encrypt(message, context = nil)
748
+ if empty? message
749
+ raise ThemisError, "message cannot be empty"
750
+ end
751
+
752
+ message_, message_length_ = string_to_pointer_size(message)
753
+ context_, context_length_ =
754
+ context.nil? ? [nil, 0] : string_to_pointer_size(context)
755
+
756
+ auth_token_length = FFI::MemoryPointer.new(:uint)
757
+ encrypted_length = FFI::MemoryPointer.new(:uint)
758
+ res = themis_secure_cell_encrypt_token_protect(
759
+ @key, @key_length, context_, context_length_, message_, message_length_,
760
+ nil, auth_token_length, nil, encrypted_length)
761
+ if res != BUFFER_TOO_SMALL
762
+ raise ThemisError.new(res), "encrypt failed"
763
+ end
764
+
765
+ auth_token = FFI::MemoryPointer.new(:char, auth_token_length.read_uint)
766
+ encrypted_message = FFI::MemoryPointer.new(:char, encrypted_length.read_uint)
767
+ res = themis_secure_cell_encrypt_token_protect(
768
+ @key, @key_length, context_, context_length_, message_, message_length_,
769
+ auth_token, auth_token_length, encrypted_message, encrypted_length)
770
+ if res != SUCCESS
771
+ raise ThemisError.new(res), "encrypt failed"
772
+ end
773
+
774
+ [encrypted_message.get_bytes(0, encrypted_length.read_uint),
775
+ auth_token.get_bytes(0, auth_token_length.read_uint),]
776
+ end
777
+
778
+ # Decrypts message with given authentication token and context.
779
+ # The context must be the same as the one used during encryption,
780
+ # or be omitted or set to nil if no context were used.
781
+ # The token also must be the one returned during encryption.
782
+ # Decrypted message is returned as binary data.
783
+ def decrypt(message, token = nil, context = nil)
784
+ # For compatibility with older API we allow the message and token to be
785
+ # provided as a list in the first argument. In this case the second one
786
+ # contains (an optional) context. Then there is no third argument.
787
+ if message.kind_of? Array
788
+ context = token
789
+ message, token = message
790
+ end
791
+
792
+ if empty? message
793
+ raise ThemisError, "message cannot be empty"
794
+ end
795
+ if empty? token
796
+ raise ThemisError, "token cannot be empty"
797
+ end
798
+
799
+ message_, message_length_ = string_to_pointer_size(message)
800
+ token_, token_length_ = string_to_pointer_size(token)
801
+ context_, context_length_ =
802
+ context.nil? ? [nil, 0] : string_to_pointer_size(context)
803
+
804
+ decrypted_length = FFI::MemoryPointer.new(:uint)
805
+ res = themis_secure_cell_decrypt_token_protect(
806
+ @key, @key_length, context_, context_length_,
807
+ message_, message_length_, token_, token_length_,
808
+ nil, decrypted_length)
809
+ if res != BUFFER_TOO_SMALL
810
+ raise ThemisError.new(res), "decrypt failed"
811
+ end
812
+
813
+ decrypted_message = FFI::MemoryPointer.new(:char, decrypted_length.read_uint)
814
+ res = themis_secure_cell_decrypt_token_protect(
815
+ @key, @key_length, context_, context_length_,
816
+ message_, message_length_, token_, token_length_,
817
+ decrypted_message, decrypted_length)
818
+ if res != SUCCESS
819
+ raise ThemisError.new(res), "decrypt failed"
820
+ end
821
+
822
+ decrypted_message.get_bytes(0, decrypted_length.read_uint)
823
+ end
824
+ end
825
+
826
+ # Secure Cell in Context Imprint mode.
827
+ class ScellContextImprint < Scell
828
+ include ThemisCommon
829
+ include ThemisImport
830
+
831
+ # Make a new Secure Cell with given key.
832
+ # The key must not be empty and is treated as binary data.
833
+ # You can use Themis::gen_sym_key to generate new keys.
834
+ def initialize(key)
835
+ if empty? key
836
+ raise ThemisError, "key cannot be empty"
837
+ end
838
+ @key, @key_length = string_to_pointer_size(key)
839
+ end
840
+
841
+ # Encrypts message with given context.
842
+ # The context is cryptographically combined with message but is not included
843
+ # into encrypted data, you will need to provide the same context for decryption.
844
+ # Resulting encrypted message has the same length as input and does not include
845
+ # authentication data, so its integrity cannot be verified.
846
+ # Message and context must not be empty, both are treated as binary data.
847
+ def encrypt(message, context)
848
+ if empty? message
849
+ raise ThemisError, "message cannot be empty"
850
+ end
851
+ if empty? context
852
+ raise ThemisError, "context cannot be empty"
853
+ end
854
+
855
+ message_, message_length_ = string_to_pointer_size(message)
856
+ context_, context_length_ =
857
+ context.nil? ? [nil, 0] : string_to_pointer_size(context)
858
+
859
+ encrypted_length = FFI::MemoryPointer.new(:uint)
860
+ res = themis_secure_cell_encrypt_context_imprint(
861
+ @key, @key_length, message_, message_length_,
862
+ context_, context_length_, nil, encrypted_length)
863
+ if res != BUFFER_TOO_SMALL
864
+ raise ThemisError.new(res), "encrypt failed"
865
+ end
866
+
867
+ encrypted_message = FFI::MemoryPointer.new(:char, encrypted_length.read_uint)
868
+ res = themis_secure_cell_encrypt_context_imprint(
869
+ @key, @key_length, message_, message_length_,
870
+ context_, context_length_, encrypted_message, encrypted_length)
871
+ if res != SUCCESS
872
+ raise ThemisError.new(res), "encrypt failed"
873
+ end
874
+
875
+ encrypted_message.get_bytes(0, encrypted_length.read_uint)
876
+ end
877
+
878
+ # Decrypts message with given context.
879
+ # The context must be the same as the one used during encryption.
880
+ # Since Context Imprint mode does not include authentication data,
881
+ # integrity of the resulting message is not guaranteed.
882
+ # You need to verify it via some other means.
883
+ # Decrypted message is returned as binary data.
884
+ def decrypt(message, context)
885
+ if empty? message
886
+ raise ThemisError, "message cannot be empty"
887
+ end
888
+ if empty? context
889
+ raise ThemisError, "message cannot be empty"
890
+ end
891
+
892
+ message_, message_length_ = string_to_pointer_size(message)
893
+ context_, context_length_ =
894
+ context.nil? ? [nil, 0] : string_to_pointer_size(context)
895
+
896
+ decrypted_length = FFI::MemoryPointer.new(:uint)
897
+ res = themis_secure_cell_decrypt_context_imprint(
898
+ @key, @key_length, message_, message_length_,
899
+ context_, context_length_, nil, decrypted_length)
900
+ if res != BUFFER_TOO_SMALL
901
+ raise ThemisError.new(res), "decrypt failed"
902
+ end
903
+
904
+ decrypted_message = FFI::MemoryPointer.new(:char, decrypted_length.read_uint)
905
+ res = themis_secure_cell_decrypt_context_imprint(
906
+ @key, @key_length, message_, message_length_,
907
+ context_, context_length_, decrypted_message, decrypted_length)
908
+ if res != SUCCESS
909
+ raise ThemisError.new(res), "decrypt failed"
910
+ end
911
+
912
+ decrypted_message.get_bytes(0, decrypted_length.read_uint)
913
+ end
914
+ end
915
+
916
+ class Scomparator
917
+ include ThemisCommon
918
+ include ThemisImport
919
+
920
+ MATCH = 21
921
+ NOT_MATCH = 22
922
+ NOT_READY = 0
923
+
924
+ def initialize(shared_secret)
925
+ shared_secret_buf, shared_secret_length =
926
+ string_to_pointer_size(shared_secret)
927
+ @comparator = secure_comparator_create
928
+ raise ThemisError, 'Secure Comparator failed creating' if @comparator.null?
929
+ res = secure_comparator_append_secret(
930
+ @comparator, shared_secret_buf, shared_secret_length)
931
+ if res != SUCCESS
932
+ raise ThemisError, 'Secure Comparator failed appending secret'
933
+ end
934
+ end
935
+
936
+ def finalize
937
+ res = secure_comparator_destroy(@comparator)
938
+ if res != SUCCESS
939
+ raise ThemisError, 'Secure Comparator failed destroying'
940
+ end
941
+ end
942
+
943
+ def begin_compare
944
+ res_length = FFI::MemoryPointer.new(:uint)
945
+ res = secure_comparator_begin_compare(@comparator, nil, res_length)
946
+ if res != BUFFER_TOO_SMALL
947
+ raise(ThemisError,
948
+ 'Secure Comparator failed making initialisation message')
949
+ end
950
+
951
+ res_buffer = FFI::MemoryPointer.new(:char, res_length.read_uint)
952
+ res = secure_comparator_begin_compare(@comparator, res_buffer, res_length)
953
+ if res != SUCCESS && res != SEND_AS_IS
954
+ raise(ThemisError,
955
+ 'Secure Comparator failed making initialisation message')
956
+ end
957
+
958
+ res_buffer.get_bytes(0, res_length.read_uint)
959
+ end
960
+
961
+ def proceed_compare(control_message)
962
+ message, message_length = string_to_pointer_size(control_message)
963
+ res_length = FFI::MemoryPointer.new(:uint)
964
+
965
+ res = secure_comparator_proceed_compare(
966
+ @comparator, message, message_length, nil, res_length)
967
+ return '' if res == SUCCESS
968
+ if res != BUFFER_TOO_SMALL
969
+ raise ThemisError, 'Secure Comparator failed proceeding message'
970
+ end
971
+
972
+ res_buffer = FFI::MemoryPointer.new(:char, res_length.read_uint)
973
+ res = secure_comparator_proceed_compare(
974
+ @comparator, message, message_length, res_buffer, res_length)
975
+ if res != SUCCESS && res != SEND_AS_IS
976
+ raise ThemisError, 'Secure Comparator failed proceeding message'
977
+ end
978
+
979
+ res_buffer.get_bytes(0, res_length.read_uint)
980
+ end
981
+
982
+ def result
983
+ secure_comparator_get_result(@comparator)
984
+ end
985
+ end
986
+
987
+ module_function :Ssign
988
+ module_function :Sverify
989
+ module_function :s_sign
990
+ module_function :s_verify
991
+ module_function :gen_sym_key
992
+ end