rbthemis 0.12.0 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rbthemis.rb +418 -131
  3. metadata +7 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 76eb20b6223204fc2d128ba98bf69b9a30f38e89baf26c0a2adf1c32162de96c
4
- data.tar.gz: 41aff8e122e39cf7a8b7da7564e67233ea31efb1438d7a9aac68ba589f88dda9
3
+ metadata.gz: 6b961537ca7de62ffe35b208cdb6cd859c30dcdea841f11ba313612018005da0
4
+ data.tar.gz: 86e0f4632b5a651cd9ed5085bab004a054f49ce6f10b01056ac480af553f734d
5
5
  SHA512:
6
- metadata.gz: 9c04031cb903cb567ab5158bf039f1090d242a704a01ad12e197cce205b25e020cc1f947a6b71bfc969dfd4b6e87dfc07abc2c319e08877f2a61371e02a64cb6
7
- data.tar.gz: 713414c7013c4b678997efa5560cb95ea630f89cbf6dfbb50b2446c7ac3473dc509e087d4ef3eea6af70a1db9f43f2ca48de49edaccc1b23f4103857774bb2e2
6
+ metadata.gz: ddb38e6216be4223661725ad06d126759dca0eaca658a884114515f0fb9982e4433a80231a1ad476d6a2a4ef355ae49231449d16dbfc720556ca74533bb82fb7
7
+ data.tar.gz: 1299e49545f8e0ddecd6e5c446ad966e959d74474fd248a042f25889a34ba29d0a0ccbebd9fa8e001ad8d4f059398234636326006aad81542391f63e8107d86d
@@ -18,13 +18,16 @@ require 'ffi'
18
18
 
19
19
  module ThemisCommon
20
20
  def string_to_pointer_size(string)
21
- string_buf = FFI::MemoryPointer.new(
22
- :char, string.force_encoding('BINARY').size)
23
- string_buf.put_bytes(0, string.force_encoding('BINARY'),
24
- 0, string.force_encoding('BINARY').size)
25
- [string_buf, string.force_encoding('BINARY').size]
21
+ string_buf = FFI::MemoryPointer.from_string(string)
22
+ [string_buf, string.bytesize]
26
23
  end
24
+
25
+ def empty?(value)
26
+ return value.nil? || value.empty?
27
+ end
28
+
27
29
  module_function :string_to_pointer_size
30
+ module_function :empty?
28
31
  end
29
32
 
30
33
  module ThemisImport
@@ -73,6 +76,8 @@ module ThemisImport
73
76
  [:pointer, :pointer, :pointer, :pointer], :int
74
77
  attach_function :themis_gen_ec_key_pair,
75
78
  [:pointer, :pointer, :pointer, :pointer], :int
79
+ attach_function :themis_gen_sym_key,
80
+ [:pointer, :pointer], :int
76
81
 
77
82
  THEMIS_KEY_INVALID = 0
78
83
  THEMIS_KEY_RSA_PRIVATE = 1
@@ -90,6 +95,13 @@ module ThemisImport
90
95
  [:pointer, :int, :pointer, :int, :pointer, :int,
91
96
  :pointer, :pointer], :int
92
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
+
93
105
  attach_function :themis_secure_cell_encrypt_token_protect,
94
106
  [:pointer, :int, :pointer, :int, :pointer, :int,
95
107
  :pointer, :pointer, :pointer, :pointer], :int
@@ -127,9 +139,20 @@ module Themis
127
139
  BUFFER_TOO_SMALL = 14
128
140
  SUCCESS = 0
129
141
  FAIL = 11
142
+ INVALID_ARGUMENT = 12
130
143
  SEND_AS_IS = 1
131
144
 
132
- ThemisError = Class.new(StandardError)
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
133
156
 
134
157
  class Callbacks
135
158
  def get_pub_key_by_id(id)
@@ -476,6 +499,26 @@ module Themis
476
499
  unwrapped_message.get_bytes(0, unwrapped_message_length.read_uint)
477
500
  end
478
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.
479
522
  class Scell
480
523
  include ThemisCommon
481
524
  include ThemisImport
@@ -485,145 +528,388 @@ module Themis
485
528
  CONTEXT_IMPRINT_MODE = 2
486
529
 
487
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
488
569
  @key, @key_length = string_to_pointer_size(key)
489
- @mode = mode
490
570
  end
491
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.
492
578
  def encrypt(message, context = nil)
579
+ if empty? message
580
+ raise ThemisError, "message cannot be empty"
581
+ end
582
+
493
583
  message_, message_length_ = string_to_pointer_size(message)
494
584
  context_, context_length_ =
495
585
  context.nil? ? [nil, 0] : string_to_pointer_size(context)
496
- encrypted_message_length = FFI::MemoryPointer.new(:uint)
497
- enccontext_length = FFI::MemoryPointer.new(:uint)
498
- case @mode
499
- when SEAL_MODE
500
- res = themis_secure_cell_encrypt_seal(
501
- @key, @key_length, context_, context_length_, message_,
502
- message_length_, nil, encrypted_message_length)
503
- if res != BUFFER_TOO_SMALL
504
- raise ThemisError, "Secure Cell (Seal) failed encrypting: #{res}"
505
- end
506
- encrypted_message = FFI::MemoryPointer.new(
507
- :char, encrypted_message_length.read_uint)
508
- res = themis_secure_cell_encrypt_seal(
509
- @key, @key_length, context_, context_length_, message_,
510
- message_length_, encrypted_message, encrypted_message_length)
511
- if res != SUCCESS
512
- raise ThemisError, "Secure Cell (Seal) failed encrypting: #{res}"
513
- end
514
- encrypted_message.get_bytes(0, encrypted_message_length.read_uint)
515
- when TOKEN_PROTECT_MODE
516
- res = themis_secure_cell_encrypt_token_protect(
517
- @key, @key_length, context_, context_length_, message_,
518
- message_length_, nil, enccontext_length, nil,
519
- encrypted_message_length)
520
- if res != BUFFER_TOO_SMALL
521
- raise(ThemisError,
522
- "Secure Cell (Token protect) failed encrypting: #{res}")
523
- end
524
- encrypted_message = FFI::MemoryPointer.new(
525
- :char, encrypted_message_length.read_uint)
526
- enccontext = FFI::MemoryPointer.new(:char, enccontext_length.read_uint)
527
- res = themis_secure_cell_encrypt_token_protect(
528
- @key, @key_length, context_, context_length_, message_,
529
- message_length_, enccontext, enccontext_length, encrypted_message,
530
- encrypted_message_length)
531
- if res != SUCCESS
532
- raise(ThemisError,
533
- "Secure Cell (Token Protect) failed encrypting: #{res}")
534
- end
535
- [encrypted_message.get_bytes(0, encrypted_message_length.read_uint),
536
- enccontext.get_bytes(0, enccontext_length.read_uint),]
537
- when CONTEXT_IMPRINT_MODE
538
- res = themis_secure_cell_encrypt_context_imprint(
539
- @key, @key_length, message_, message_length_, context_,
540
- context_length_, nil, encrypted_message_length)
541
- if res != BUFFER_TOO_SMALL
542
- raise(ThemisError,
543
- "Secure Cell (Context Imprint) failed encrypting: #{res}")
544
- end
545
- encrypted_message = FFI::MemoryPointer.new(
546
- :char, encrypted_message_length.read_uint)
547
- res = themis_secure_cell_encrypt_context_imprint(
548
- @key, @key_length, message_, message_length_, context_,
549
- context_length_, encrypted_message, encrypted_message_length)
550
- if res != SUCCESS
551
- raise(ThemisError,
552
- "Secure Cell (Context Imprint) failed encrypting: #{res}")
553
- end
554
- encrypted_message.get_bytes(0, encrypted_message_length.read_uint)
555
- else
556
- raise ThemisError, 'Secure Cell failed encrypting, undefined mode'
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"
557
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)
558
604
  end
559
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.
560
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)
561
616
  context_, context_length_ =
562
617
  context.nil? ? [nil, 0] : string_to_pointer_size(context)
563
- decrypted_message_length = FFI::MemoryPointer.new(:uint)
564
- case @mode
565
- when SEAL_MODE
566
- message_, message_length_ = string_to_pointer_size(message)
567
- res = themis_secure_cell_decrypt_seal(
568
- @key, @key_length, context_, context_length_, message_,
569
- message_length_, nil, decrypted_message_length)
570
- if res != BUFFER_TOO_SMALL
571
- raise ThemisError, "Secure Cell (Seal) failed decrypting: #{res}"
572
- end
573
- decrypted_message = FFI::MemoryPointer.new(
574
- :char, decrypted_message_length.read_uint)
575
- res = themis_secure_cell_decrypt_seal(
576
- @key, @key_length, context_, context_length_, message_,
577
- message_length_, decrypted_message, decrypted_message_length)
578
- if res != SUCCESS
579
- raise ThemisError, "Secure Cell (Seal) failed decrypting: #{res}"
580
- end
581
- decrypted_message.get_bytes(0, decrypted_message_length.read_uint)
582
- when TOKEN_PROTECT_MODE
583
- message_, enccontext = message
584
- message__, message_length__ = string_to_pointer_size(message_)
585
- enccontext_, enccontext_length = string_to_pointer_size(enccontext)
586
- res = themis_secure_cell_decrypt_token_protect(
587
- @key, @key_length, context_, context_length_, message__,
588
- message_length__, enccontext_, enccontext_length, nil,
589
- decrypted_message_length)
590
- if res != BUFFER_TOO_SMALL
591
- raise(ThemisError,
592
- "Secure Cell (Token Protect) failed decrypting: #{res}")
593
- end
594
- decrypted_message = FFI::MemoryPointer.new(
595
- :char, decrypted_message_length.read_uint)
596
- res = themis_secure_cell_decrypt_token_protect(
597
- @key, @key_length, context_, context_length_, message__,
598
- message_length__, enccontext_, enccontext_length,
599
- decrypted_message, decrypted_message_length)
600
- if res != SUCCESS
601
- raise(ThemisError,
602
- "Secure Cell (Token Protect) failed decrypting: #{res}")
603
- end
604
- decrypted_message.get_bytes(0, decrypted_message_length.read_uint)
605
- when CONTEXT_IMPRINT_MODE
606
- message_, message_length_ = string_to_pointer_size(message)
607
- res = themis_secure_cell_decrypt_context_imprint(
608
- @key, @key_length, message_, message_length_, context_,
609
- context_length_, nil, decrypted_message_length)
610
- if res != BUFFER_TOO_SMALL
611
- raise(ThemisError,
612
- "Secure Cell (Context Imprint) failed decrypting: #{res}")
613
- end
614
- decrypted_message = FFI::MemoryPointer.new(
615
- :char, decrypted_message_length.read_uint)
616
- res = themis_secure_cell_decrypt_context_imprint(@key, @key_length,
617
- message_, message_length_, context_, context_length_,
618
- decrypted_message, decrypted_message_length)
619
- if res != SUCCESS
620
- raise(ThemisError,
621
- "Secure Cell (Context Imprint) failed decrypting: #{res}")
622
- end
623
- decrypted_message.get_bytes(0, decrypted_message_length.read_uint)
624
- else
625
- raise ThemisError, 'Secure Cell failed encrypting, undefined mode'
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"
626
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)
627
913
  end
628
914
  end
629
915
 
@@ -702,4 +988,5 @@ module Themis
702
988
  module_function :Sverify
703
989
  module_function :s_sign
704
990
  module_function :s_verify
991
+ module_function :gen_sym_key
705
992
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbthemis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - CossackLabs
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-26 00:00:00.000000000 Z
11
+ date: 2020-07-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -30,11 +30,10 @@ dependencies:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
32
  version: 1.9.8
33
- description: Themis is a data security library, providing users with high-quality
34
- security services for secure messaging of any kinds and flexible data storage. Themis
35
- is aimed at modern developers, with high level OOP wrappers for Ruby, Python, PHP,
36
- Java / Android and iOS / OSX. It is designed with ease of use in mind, high security
37
- and cross-platform availability.
33
+ description: Themis is a convenient cryptographic library for data protection. It
34
+ provides secure messaging with forward secrecy and secure data storage. Themis is
35
+ aimed at modern development practices and has a unified API across 12 platforms,
36
+ including Ruby, JavaScript, iOS/macOS, Python, and Java/Android.
38
37
  email: dev@cossacklabs.com
39
38
  executables: []
40
39
  extensions: []
@@ -60,7 +59,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
60
59
  - !ruby/object:Gem::Version
61
60
  version: '0'
62
61
  requirements:
63
- - libthemis, v0.12.0
62
+ - libthemis, v0.13.0
64
63
  rubygems_version: 3.0.2
65
64
  signing_key:
66
65
  specification_version: 4