rnp 0.2.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +5 -0
- data/LICENSE.txt +21 -0
- data/README.adoc +3 -182
- data/lib/rnp.rb +12 -3
- data/lib/rnp/error.rb +40 -0
- data/lib/rnp/ffi/librnp.rb +306 -0
- data/lib/rnp/input.rb +99 -0
- data/lib/rnp/key.rb +275 -0
- data/lib/rnp/misc.rb +71 -0
- data/lib/rnp/op/encrypt.rb +181 -0
- data/lib/rnp/op/sign.rb +139 -0
- data/lib/rnp/op/verify.rb +147 -0
- data/lib/rnp/output.rb +121 -0
- data/lib/rnp/rnp.rb +595 -0
- data/lib/rnp/utils.rb +44 -0
- data/lib/rnp/version.rb +8 -3
- metadata +124 -50
- data/.gitignore +0 -12
- data/.rspec +0 -2
- data/.travis.yml +0 -5
- data/CODE_OF_CONDUCT.md +0 -74
- data/Gemfile +0 -4
- data/Rakefile +0 -6
- data/Use_Cases.adoc +0 -119
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/example-usage.rb +0 -766
- data/examples/highlevel/decrypt_mem.rb +0 -44
- data/examples/highlevel/encrypt_mem.rb +0 -46
- data/examples/lowlevel/decrypt_file.rb +0 -76
- data/examples/lowlevel/decrypt_mem.rb +0 -80
- data/examples/lowlevel/encrypt_file.rb +0 -68
- data/examples/lowlevel/encrypt_mem.rb +0 -75
- data/examples/lowlevel/load_pubkey.rb +0 -118
- data/examples/lowlevel/print_keyring_file.rb +0 -68
- data/examples/lowlevel/print_keyring_mem.rb +0 -96
- data/examples/lowlevel/sign_file.rb +0 -104
- data/examples/lowlevel/sign_mem.rb +0 -96
- data/examples/lowlevel/verify_file.rb +0 -55
- data/examples/lowlevel/verify_mem.rb +0 -61
- data/lib/rnp/highlevel.rb +0 -5
- data/lib/rnp/highlevel/constants.rb +0 -96
- data/lib/rnp/highlevel/keyring.rb +0 -259
- data/lib/rnp/highlevel/publickey.rb +0 -150
- data/lib/rnp/highlevel/secretkey.rb +0 -318
- data/lib/rnp/highlevel/utils.rb +0 -119
- data/lib/rnp/lowlevel.rb +0 -6
- data/lib/rnp/lowlevel/constants.rb +0 -11
- data/lib/rnp/lowlevel/dynarray.rb +0 -129
- data/lib/rnp/lowlevel/enums.rb +0 -243
- data/lib/rnp/lowlevel/libc.rb +0 -28
- data/lib/rnp/lowlevel/libopenssl.rb +0 -15
- data/lib/rnp/lowlevel/librnp.rb +0 -213
- data/lib/rnp/lowlevel/structs.rb +0 -541
- data/lib/rnp/lowlevel/utils.rb +0 -25
- data/rnp.gemspec +0 -35
- data/rnp/lib/rnp.rb +0 -5
- data/rnp/spec/rnp_spec.rb +0 -11
data/lib/rnp/input.rb
ADDED
@@ -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
|
+
|
data/lib/rnp/key.rb
ADDED
@@ -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
|
+
|
data/lib/rnp/misc.rb
ADDED
@@ -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
|
+
|