rbthemis 0.10.0 → 0.14.0

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