rbthemis 0.12.0 → 0.13.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 (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