rnp 0.2.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +5 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.adoc +3 -182
  5. data/lib/rnp.rb +12 -3
  6. data/lib/rnp/error.rb +40 -0
  7. data/lib/rnp/ffi/librnp.rb +306 -0
  8. data/lib/rnp/input.rb +99 -0
  9. data/lib/rnp/key.rb +275 -0
  10. data/lib/rnp/misc.rb +71 -0
  11. data/lib/rnp/op/encrypt.rb +181 -0
  12. data/lib/rnp/op/sign.rb +139 -0
  13. data/lib/rnp/op/verify.rb +147 -0
  14. data/lib/rnp/output.rb +121 -0
  15. data/lib/rnp/rnp.rb +595 -0
  16. data/lib/rnp/utils.rb +44 -0
  17. data/lib/rnp/version.rb +8 -3
  18. metadata +124 -50
  19. data/.gitignore +0 -12
  20. data/.rspec +0 -2
  21. data/.travis.yml +0 -5
  22. data/CODE_OF_CONDUCT.md +0 -74
  23. data/Gemfile +0 -4
  24. data/Rakefile +0 -6
  25. data/Use_Cases.adoc +0 -119
  26. data/bin/console +0 -14
  27. data/bin/setup +0 -8
  28. data/example-usage.rb +0 -766
  29. data/examples/highlevel/decrypt_mem.rb +0 -44
  30. data/examples/highlevel/encrypt_mem.rb +0 -46
  31. data/examples/lowlevel/decrypt_file.rb +0 -76
  32. data/examples/lowlevel/decrypt_mem.rb +0 -80
  33. data/examples/lowlevel/encrypt_file.rb +0 -68
  34. data/examples/lowlevel/encrypt_mem.rb +0 -75
  35. data/examples/lowlevel/load_pubkey.rb +0 -118
  36. data/examples/lowlevel/print_keyring_file.rb +0 -68
  37. data/examples/lowlevel/print_keyring_mem.rb +0 -96
  38. data/examples/lowlevel/sign_file.rb +0 -104
  39. data/examples/lowlevel/sign_mem.rb +0 -96
  40. data/examples/lowlevel/verify_file.rb +0 -55
  41. data/examples/lowlevel/verify_mem.rb +0 -61
  42. data/lib/rnp/highlevel.rb +0 -5
  43. data/lib/rnp/highlevel/constants.rb +0 -96
  44. data/lib/rnp/highlevel/keyring.rb +0 -259
  45. data/lib/rnp/highlevel/publickey.rb +0 -150
  46. data/lib/rnp/highlevel/secretkey.rb +0 -318
  47. data/lib/rnp/highlevel/utils.rb +0 -119
  48. data/lib/rnp/lowlevel.rb +0 -6
  49. data/lib/rnp/lowlevel/constants.rb +0 -11
  50. data/lib/rnp/lowlevel/dynarray.rb +0 -129
  51. data/lib/rnp/lowlevel/enums.rb +0 -243
  52. data/lib/rnp/lowlevel/libc.rb +0 -28
  53. data/lib/rnp/lowlevel/libopenssl.rb +0 -15
  54. data/lib/rnp/lowlevel/librnp.rb +0 -213
  55. data/lib/rnp/lowlevel/structs.rb +0 -541
  56. data/lib/rnp/lowlevel/utils.rb +0 -25
  57. data/rnp.gemspec +0 -35
  58. data/rnp/lib/rnp.rb +0 -5
  59. data/rnp/spec/rnp_spec.rb +0 -11
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ # (c) 2018 Ribose Inc.
4
+
5
+ require 'English'
6
+
7
+ require 'ffi'
8
+
9
+ require 'rnp/error'
10
+ require 'rnp/ffi/librnp'
11
+ require 'rnp/utils'
12
+
13
+ class Rnp
14
+ # Class used to feed data into RNP.
15
+ #
16
+ # @note When dealing with very large data sources, prefer {from_path} which
17
+ # should be the most efficient. {from_io} is likely to have more overhead.
18
+ #
19
+ # @example input from a string
20
+ # Rnp::Input.from_string('my data')
21
+ #
22
+ # @example input from a file
23
+ # Rnp::Input.from_path('/path/to/my/file')
24
+ #
25
+ # @example input from a Ruby IO object
26
+ # Rnp::Input.from_io(File.open('/path/to/file', 'rb'))
27
+ class Input
28
+ # @api private
29
+ attr_reader :ptr
30
+
31
+ # @api private
32
+ def initialize(ptr, reader = nil)
33
+ raise Rnp::Error, 'NULL pointer' if ptr.null?
34
+ @ptr = FFI::AutoPointer.new(ptr, self.class.method(:destroy))
35
+ @reader = reader
36
+ end
37
+
38
+ # @api private
39
+ def self.destroy(ptr)
40
+ LibRnp.rnp_input_destroy(ptr)
41
+ end
42
+
43
+ def inspect
44
+ Rnp.inspect_ptr(self)
45
+ end
46
+
47
+ # Create an Input to read from a string.
48
+ #
49
+ # @param data [String] the string data
50
+ # @return [Input]
51
+ def self.from_string(data)
52
+ pptr = FFI::MemoryPointer.new(:pointer)
53
+ buf = FFI::MemoryPointer.from_data(data)
54
+ Rnp.call_ffi(:rnp_input_from_memory, pptr, buf, buf.size, true)
55
+ Input.new(pptr.read_pointer)
56
+ end
57
+
58
+ # Create an Input to read from a path.
59
+ #
60
+ # @param path [String] the path
61
+ # @return [Input]
62
+ def self.from_path(path)
63
+ pptr = FFI::MemoryPointer.new(:pointer)
64
+ Rnp.call_ffi(:rnp_input_from_path, pptr, path)
65
+ Input.new(pptr.read_pointer)
66
+ end
67
+
68
+ # Create an Input to read from an IO object.
69
+ #
70
+ # @param io [IO, #read] the IO object
71
+ # @return [Input]
72
+ def self.from_io(io)
73
+ from_callback(io.method(:read))
74
+ end
75
+
76
+ # @api private
77
+ READER = lambda do |reader, _ctx, buf, buf_len|
78
+ begin
79
+ data = reader.call(buf_len)
80
+ return 0 unless data
81
+ raise Rnp::Error, 'Read exceeded buffer size' if data.size > buf_len
82
+ buf.write_bytes(data)
83
+ return data.size
84
+ rescue
85
+ puts $ERROR_INFO
86
+ return -1
87
+ end
88
+ end
89
+
90
+ # @api private
91
+ def self.from_callback(reader)
92
+ pptr = FFI::MemoryPointer.new(:pointer)
93
+ readercb = READER.curry[reader]
94
+ Rnp.call_ffi(:rnp_input_from_callback, pptr, readercb, nil, nil)
95
+ Input.new(pptr.read_pointer, readercb)
96
+ end
97
+ end # class
98
+ end # class
99
+
@@ -0,0 +1,275 @@
1
+ # frozen_string_literal: true
2
+
3
+ # (c) 2018 Ribose Inc.
4
+
5
+ require 'ffi'
6
+
7
+ require 'rnp/error'
8
+ require 'rnp/ffi/librnp'
9
+ require 'rnp/utils'
10
+
11
+ class Rnp
12
+ # Class that represents a PGP key (potentially encompassing both the public
13
+ # and private portions).
14
+ class Key
15
+ # @api private
16
+ attr_reader :ptr
17
+
18
+ # @api private
19
+ def initialize(ptr, free = true)
20
+ raise Rnp::Error, 'NULL pointer' if ptr.null?
21
+ if free
22
+ @ptr = FFI::AutoPointer.new(ptr, self.class.method(:destroy))
23
+ else
24
+ @ptr = ptr
25
+ end
26
+ end
27
+
28
+ # @api private
29
+ def self.destroy(ptr)
30
+ LibRnp.rnp_key_handle_destroy(ptr)
31
+ end
32
+
33
+ def inspect
34
+ Rnp.inspect_ptr(self)
35
+ end
36
+
37
+ def to_s
38
+ "#<#{self.class}:#{keyid}>"
39
+ end
40
+
41
+ # Get the fingerprint of the key
42
+ #
43
+ # @return [String]
44
+ def fingerprint
45
+ string_property(:rnp_key_get_fprint)
46
+ end
47
+
48
+ # Get the keyid of the key
49
+ #
50
+ # @return [String]
51
+ def keyid
52
+ string_property(:rnp_key_get_keyid)
53
+ end
54
+
55
+ # Get the grip of the key
56
+ #
57
+ # @return [String]
58
+ def grip
59
+ string_property(:rnp_key_get_grip)
60
+ end
61
+
62
+ # Get the primary userid of the key
63
+ #
64
+ # @return [String]
65
+ def primary_userid
66
+ string_property(:rnp_key_get_primary_uid)
67
+ end
68
+
69
+ # Enumerate each userid for this key.
70
+ #
71
+ # @return [self, Enumerator]
72
+ def each_userid(&block)
73
+ block or return enum_for(:userid_iterator)
74
+ userid_iterator(&block)
75
+ self
76
+ end
77
+
78
+ # Get a list of all userids for this key.
79
+ #
80
+ # @return [Array<String>]
81
+ def userids
82
+ each_userid.to_a
83
+ end
84
+
85
+ # Add a userid to a key.
86
+ #
87
+ # @param userid [String] the userid to add
88
+ # @param hash (see Sign#hash=)
89
+ # @param expiration_time (see Sign#expiration_time=)
90
+ # @param key_flags [Integer]
91
+ # @param primary [Boolean] if true then this userid will be marked as the
92
+ # primary userid
93
+ # @return [void]
94
+ def add_userid(userid, hash: nil, expiration_time: nil, key_flags: 0,
95
+ primary: false)
96
+ Rnp.call_ffi(:rnp_key_add_uid, @ptr, userid, hash, expiration_time,
97
+ key_flags, primary)
98
+ end
99
+
100
+ # Returns true if the key is currently locked.
101
+ #
102
+ # @return [Boolean]
103
+ def locked?
104
+ bool_property(:rnp_key_is_locked)
105
+ end
106
+
107
+ # Lock the key.
108
+ #
109
+ # @return [self]
110
+ def lock
111
+ Rnp.call_ffi(:rnp_key_lock, @ptr)
112
+ self
113
+ end
114
+
115
+ # Unlock the key.
116
+ #
117
+ # @param password [String, nil] the password to unlock the key. If nil, the
118
+ # current password provider will be used (see {Rnp#password_provider=}).
119
+ # @return [self]
120
+ def unlock(password = nil)
121
+ Rnp.call_ffi(:rnp_key_unlock, @ptr, password)
122
+ self
123
+ end
124
+
125
+ # Returns true if the key is currently protected.
126
+ #
127
+ # @return [Boolean]
128
+ def protected?
129
+ bool_property(:rnp_key_is_protected)
130
+ end
131
+
132
+ # Protect or re-protect the key.
133
+ #
134
+ # @param password [String] the password with which to encrypt the key.
135
+ # @param cipher [String] the cipher algorithm to encrypt with
136
+ # @param cipher_mode [String] the cipher mode
137
+ # @param s2k_hash (see Encrypt#add_password)
138
+ # @param s2k_iterations (see Encrypt#add_password)
139
+ # @return [self]
140
+ def protect(password, cipher: nil, cipher_mode: nil, s2k_hash: nil,
141
+ s2k_iterations: 0)
142
+ Rnp.call_ffi(:rnp_key_protect, @ptr, password, cipher, cipher_mode,
143
+ s2k_hash, s2k_iterations)
144
+ self
145
+ end
146
+
147
+ # Unprotect the key.
148
+ #
149
+ # @param password [String, nil] the password to unlock the key. If nil,
150
+ # the current password provider will be used (see {Rnp#password_provider=}).
151
+ # @return [self]
152
+ def unprotect(password = nil)
153
+ Rnp.call_ffi(:rnp_key_unprotect, @ptr, password)
154
+ self
155
+ end
156
+
157
+ # Returns true if the key is a primary key.
158
+ #
159
+ # @return [Boolean]
160
+ def primary?
161
+ bool_property(:rnp_key_is_primary)
162
+ end
163
+
164
+ # Returns true if the key is a subkey.
165
+ #
166
+ # @return [Boolean]
167
+ def sub?
168
+ bool_property(:rnp_key_is_sub)
169
+ end
170
+
171
+ # Returns true if the public key packet is available.
172
+ #
173
+ # @return [Boolean]
174
+ def public_key_present?
175
+ bool_property(:rnp_key_have_public)
176
+ end
177
+
178
+ # Returns true if the secret key packet is available.
179
+ #
180
+ # @return [Boolean]
181
+ def secret_key_present?
182
+ bool_property(:rnp_key_have_secret)
183
+ end
184
+
185
+ # Returns the raw public key data as PGP packets.
186
+ #
187
+ # @return [String]
188
+ def public_key_data
189
+ buf_property(:rnp_get_public_key_data)
190
+ end
191
+
192
+ # Returns the raw secret key data.
193
+ #
194
+ # The format may be either PGP packets or an s-expr/G10.
195
+ #
196
+ # @return [String]
197
+ def secret_key_data
198
+ buf_property(:rnp_get_secret_key_data)
199
+ end
200
+
201
+ # Return a JSON representation of this key (as a Hash).
202
+ #
203
+ # @param public_mpis [Boolean] if true then public MPIs will be included
204
+ # @param secret_mpis [Boolean] if true then secret MPIs will be included
205
+ # @param signatures [Boolean] if true then signatures will be included
206
+ # @param signature_mpis [Boolean] if true then signature MPIs will be
207
+ # included
208
+ # @return [Hash]
209
+ def json(public_mpis: false, secret_mpis: false, signatures: true,
210
+ signature_mpis: false)
211
+ flags = 0
212
+ flags |= LibRnp::RNP_JSON_PUBLIC_MPIS if public_mpis
213
+ flags |= LibRnp::RNP_JSON_SECRET_MPIS if secret_mpis
214
+ flags |= LibRnp::RNP_JSON_SIGNATURES if signatures
215
+ flags |= LibRnp::RNP_JSON_SIGNATURE_MPIS if signature_mpis
216
+ pptr = FFI::MemoryPointer.new(:pointer)
217
+ Rnp.call_ffi(:rnp_key_to_json, @ptr, flags, pptr)
218
+ begin
219
+ presult = pptr.read_pointer
220
+ JSON.parse(presult.read_string) unless presult.null?
221
+ ensure
222
+ LibRnp.rnp_buffer_destroy(presult)
223
+ end
224
+ end
225
+
226
+ private
227
+
228
+ def string_property(func)
229
+ pptr = FFI::MemoryPointer.new(:pointer)
230
+ Rnp.call_ffi(func, @ptr, pptr)
231
+ begin
232
+ pvalue = pptr.read_pointer
233
+ pvalue.read_string unless pvalue.null?
234
+ ensure
235
+ LibRnp.rnp_buffer_destroy(pvalue)
236
+ end
237
+ end
238
+
239
+ def bool_property(func)
240
+ presult = FFI::MemoryPointer.new(:bool)
241
+ Rnp.call_ffi(func, @ptr, presult)
242
+ presult.read(:bool)
243
+ end
244
+
245
+ def buf_property(func)
246
+ pptr = FFI::MemoryPointer.new(:pointer)
247
+ pbuflen = FFI::MemoryPointer.new(:size_t)
248
+ Rnp.call_ffi(func, @ptr, pptr, pbuflen)
249
+ begin
250
+ pbuf = pptr.read_pointer
251
+ buflen = pbuflen.read(:size_t)
252
+ pbuf.read_bytes(buflen) unless pbuf.null?
253
+ ensure
254
+ LibRnp.rnp_buffer_destroy(pbuf)
255
+ end
256
+ end
257
+
258
+ def userid_iterator
259
+ pcount = FFI::MemoryPointer.new(:size_t)
260
+ Rnp.call_ffi(:rnp_key_get_uid_count, @ptr, pcount)
261
+ count = pcount.read(:size_t)
262
+ pptr = FFI::MemoryPointer.new(:pointer)
263
+ (0...count).each do |i|
264
+ Rnp.call_ffi(:rnp_key_get_uid_at, @ptr, i, pptr)
265
+ begin
266
+ puserid = pptr.read_pointer
267
+ yield puserid.read_string unless puserid.null?
268
+ ensure
269
+ LibRnp.rnp_buffer_destroy(puserid)
270
+ end
271
+ end
272
+ end
273
+ end # class
274
+ end # class
275
+
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ # (c) 2018 Ribose Inc.
4
+
5
+ require 'ffi'
6
+
7
+ require 'rnp/utils'
8
+ require 'rnp/ffi/librnp'
9
+
10
+ class Rnp
11
+ # Get the default homedir for RNP.
12
+ #
13
+ # @return [String]
14
+ def self.default_homedir
15
+ pptr = FFI::MemoryPointer.new(:pointer)
16
+ Rnp.call_ffi(:rnp_get_default_homedir, pptr)
17
+ begin
18
+ phomedir = pptr.read_pointer
19
+ phomedir.read_string unless phomedir.null?
20
+ ensure
21
+ LibRnp.rnp_buffer_destroy(phomedir)
22
+ end
23
+ end
24
+
25
+ # Attempt to detect information about a homedir.
26
+ #
27
+ # @param homedir [String] the homedir
28
+ # @return [Hash<Symbol>]
29
+ # * :public [Hash<Symbol>]
30
+ # * :format [String]
31
+ # * :path [String]
32
+ # * :secret [Hash<Symbol>]
33
+ # * :format [String]
34
+ # * :path [String]
35
+ def self.homedir_info(homedir)
36
+ pptrs = FFI::MemoryPointer.new(:pointer, 4)
37
+ Rnp.call_ffi(:rnp_detect_homedir_info, homedir, pptrs[0], pptrs[1],
38
+ pptrs[2], pptrs[3])
39
+ ptrs = (0..3).collect { |i| pptrs[i] }.map(&:read_pointer)
40
+ return if ptrs.all?(&:null?)
41
+ {
42
+ public: {
43
+ format: ptrs[0].read_string,
44
+ path: ptrs[1].read_string
45
+ },
46
+ secret: {
47
+ format: ptrs[2].read_string,
48
+ path: ptrs[3].read_string
49
+ }
50
+ }
51
+ ensure
52
+ ptrs&.each { |ptr| LibRnp.rnp_buffer_destroy(ptr) }
53
+ end
54
+
55
+ # Attempt to detect the format of a key.
56
+ #
57
+ # @param key_data [String] the key data
58
+ # @return [String]
59
+ def self.key_format(key_data)
60
+ pptr = FFI::MemoryPointer.new(:pointer)
61
+ data = FFI::MemoryPointer.from_data(key_data)
62
+ Rnp.call_ffi(:rnp_detect_key_format, data, data.size, pptr)
63
+ begin
64
+ pformat = pptr.read_pointer
65
+ pformat.read_string unless pformat.null?
66
+ ensure
67
+ LibRnp.rnp_buffer_destroy(pformat)
68
+ end
69
+ end
70
+ end # class
71
+
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ # (c) 2018 Ribose Inc.
4
+
5
+ require 'set'
6
+
7
+ require 'ffi'
8
+
9
+ require 'rnp/error'
10
+ require 'rnp/ffi/librnp'
11
+ require 'rnp/utils'
12
+
13
+ class Rnp
14
+ # Encryption operation
15
+ class Encrypt
16
+ # @api private
17
+ attr_reader :ptr
18
+
19
+ # @api private
20
+ def initialize(ptr)
21
+ raise Rnp::Error, 'NULL pointer' if ptr.null?
22
+ @ptr = FFI::AutoPointer.new(ptr, self.class.method(:destroy))
23
+ end
24
+
25
+ # @api private
26
+ def self.destroy(ptr)
27
+ LibRnp.rnp_op_encrypt_destroy(ptr)
28
+ end
29
+
30
+ def inspect
31
+ Rnp.inspect_ptr(self)
32
+ end
33
+
34
+ # Add a recipient.
35
+ #
36
+ # @param recipient [Key] the recipient
37
+ # @return [self]
38
+ def add_recipient(recipient)
39
+ Rnp.call_ffi(:rnp_op_encrypt_add_recipient, @ptr, recipient.ptr)
40
+ self
41
+ end
42
+
43
+ # Add a signer.
44
+ #
45
+ # @param signer [Key] the signer
46
+ # @param hash (see #hash=)
47
+ # @param creation_time (see #creation_time=)
48
+ # @param expiration_time (see #expiration_time=)
49
+ # @return [self]
50
+ def add_signer(signer, hash: nil, creation_time: nil, expiration_time: nil)
51
+ pptr = FFI::MemoryPointer.new(:pointer)
52
+ Rnp.call_ffi(:rnp_op_encrypt_add_signature, @ptr, signer.ptr, pptr)
53
+ psig = pptr.read_pointer
54
+ Sign.set_signature_options(
55
+ psig,
56
+ hash: hash,
57
+ creation_time: creation_time,
58
+ expiration_time: expiration_time
59
+ )
60
+ self
61
+ end
62
+
63
+ # Add a password.
64
+ #
65
+ # @param password [String] the password
66
+ # @param s2k_hash [String] the hash algorithm to use for the
67
+ # string-to-key key derivation.
68
+ # @param s2k_iterations [Integer] the number of iterations for the
69
+ # string-to-key key derivation. A value of 0 will choose
70
+ # a default.
71
+ # @param s2k_cipher [String] the cipher algorithm used to wrap the key.
72
+ # @note This is a separate cipher from the one used to encrypt the main
73
+ # payload/stream (see {#cipher=}). This cipher may not be used in all
74
+ # circumstances. For example, when encrypting with *only* a password
75
+ # (no public keys), this cipher would generally not be used.
76
+ # When encrypting with a combination of one or more passwords and one
77
+ # or more public keys, this cipher would generally be used.
78
+ # @return [self]
79
+ def add_password(password, s2k_hash: nil, s2k_iterations: 0,
80
+ s2k_cipher: nil)
81
+ Rnp.call_ffi(:rnp_op_encrypt_add_password, @ptr, password, s2k_hash,
82
+ s2k_iterations, s2k_cipher)
83
+ self
84
+ end
85
+
86
+ # Set a group of options.
87
+ #
88
+ # @note Some options are related to signatures and will have no effect if
89
+ # there are no signers.
90
+ #
91
+ # @param armored (see #armored=)
92
+ # @param compression (see #compression=)
93
+ # @param cipher (see #cipher=)
94
+ # @param hash (see #hash=)
95
+ # @param creation_time (see #creation_time=)
96
+ # @param expiration_time (see #expiration_time=)
97
+ def options=(armored: nil, compression: nil, cipher: nil, hash: nil,
98
+ creation_time: nil, expiration_time: nil)
99
+ self.armored = armored unless armored.nil?
100
+ self.compression = compression unless compression.nil?
101
+ self.cipher = cipher unless cipher.nil?
102
+ self.hash = hash unless hash.nil?
103
+ self.creation_time = creation_time unless creation_time.nil?
104
+ self.expiration_time = expiration_time unless expiration_time.nil?
105
+ end
106
+
107
+ # Set whether the output will be ASCII-armored.
108
+ #
109
+ # @param armored [Boolean] true if the output should be
110
+ # ASCII-armored, false otherwise.
111
+ def armored=(armored)
112
+ Rnp.call_ffi(:rnp_op_encrypt_set_armor, @ptr, armored)
113
+ end
114
+
115
+ # Set the compression algorithm and level.
116
+ #
117
+ # @param [Hash<Symbol>] compression
118
+ # @option compression [String] :algorithm the compression algorithm
119
+ # (bzip2, etc)
120
+ # @option compression [Integer] :level the compression level. This should
121
+ # generally be between 0 (no compression) and 9 (best compression).
122
+ def compression=(compression)
123
+ if !compression.is_a?(Hash) || Set.new(compression.keys) != Set.new(%i[algorithm level])
124
+ raise ArgumentError,
125
+ 'Compression option must be of the form: {algorithm: \'zlib\', level: 5}'
126
+ end
127
+ Rnp.call_ffi(:rnp_op_encrypt_set_compression, @ptr,
128
+ compression[:algorithm], compression[:level])
129
+ end
130
+
131
+ # Set the cipher used to encrypt the input.
132
+ #
133
+ # @param cipher [String] the cipher algorithm name
134
+ def cipher=(cipher)
135
+ Rnp.call_ffi(:rnp_op_encrypt_set_cipher, @ptr, cipher)
136
+ end
137
+
138
+ # Set the hash algorithm used for calculating signatures.
139
+ #
140
+ # @note This is only valid when there is one or more signer.
141
+ #
142
+ # @param hash [String] the hash algorithm name
143
+ def hash=(hash)
144
+ Rnp.call_ffi(:rnp_op_encrypt_set_hash, @ptr, hash)
145
+ end
146
+
147
+ # Set the creation time for signatures.
148
+ #
149
+ # @note This is only valid when there is one or more signer.
150
+ #
151
+ # @param creation_time [Time, Integer] the creation time to use for all
152
+ # signatures. As an integer, this is the number of seconds
153
+ # since the unix epoch.
154
+ def creation_time=(creation_time)
155
+ creation_time = creation_time.to_i if creation_time.is_a?(::Time)
156
+ Rnp.call_ffi(:rnp_op_encrypt_set_creation_time, @ptr, creation_time)
157
+ end
158
+
159
+ # Set the expiration time for signatures.
160
+ #
161
+ # @note This is only valid when there is one or more signer.
162
+ #
163
+ # @param expiration_time [Integer] the lifetime of the signatures, as the number
164
+ # of seconds. The actual expiration date/time is the creation time
165
+ # plus this value. A value of 0 will create signatures that do not
166
+ # expire.
167
+ def expiration_time=(expiration_time)
168
+ Rnp.call_ffi(:rnp_op_encrypt_set_expiration_time, @ptr, expiration_time)
169
+ end
170
+
171
+ # Execute the operation.
172
+ #
173
+ # This should only be called once.
174
+ #
175
+ # @return [void]
176
+ def execute
177
+ Rnp.call_ffi(:rnp_op_encrypt_execute, @ptr)
178
+ end
179
+ end # class
180
+ end # class
181
+