rnp 1.0.4 → 1.0.5
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.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +4 -1
- data/README.adoc +8 -5
- data/lib/rnp/ffi/librnp.rb +161 -36
- data/lib/rnp/input.rb +30 -10
- data/lib/rnp/key.rb +217 -1
- data/lib/rnp/misc.rb +153 -4
- data/lib/rnp/op/encrypt.rb +26 -22
- data/lib/rnp/op/generate.rb +211 -0
- data/lib/rnp/op/sign.rb +25 -25
- data/lib/rnp/output.rb +14 -0
- data/lib/rnp/rnp.rb +170 -4
- data/lib/rnp/signature.rb +106 -0
- data/lib/rnp/userid.rb +77 -0
- data/lib/rnp/version.rb +2 -3
- data/lib/rnp.rb +3 -1
- metadata +29 -15
data/lib/rnp/misc.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# (c) 2018 Ribose Inc.
|
3
|
+
# (c) 2018-2020 Ribose Inc.
|
4
4
|
|
5
|
+
require "json"
|
5
6
|
require 'ffi'
|
6
7
|
|
7
8
|
require 'rnp/utils'
|
@@ -74,6 +75,13 @@ class Rnp
|
|
74
75
|
# @param output [Output] the output to write the armored
|
75
76
|
# data to. If nil, the result will be returned directly
|
76
77
|
# as a String.
|
78
|
+
# @param type [String] the armor type. Valid values include:
|
79
|
+
# * message
|
80
|
+
# * public key
|
81
|
+
# * secret key
|
82
|
+
# * signature
|
83
|
+
# * cleartext
|
84
|
+
# * nil (try to guess the type)
|
77
85
|
# @return [nil, String]
|
78
86
|
def self.enarmor(input:, output: nil, type: nil)
|
79
87
|
Output.default(output) do |output_|
|
@@ -112,8 +120,12 @@ class Rnp
|
|
112
120
|
# stamps generated with {version_for}.
|
113
121
|
#
|
114
122
|
# @return [Integer]
|
115
|
-
def self.version
|
116
|
-
|
123
|
+
def self.version(str = nil)
|
124
|
+
if str.nil?
|
125
|
+
LibRnp.rnp_version
|
126
|
+
else
|
127
|
+
LibRnp.rnp_version_for(*str.split('.').map(&:to_i))
|
128
|
+
end
|
117
129
|
end
|
118
130
|
|
119
131
|
# Encode the given major, minor, and patch numbers into a version
|
@@ -144,5 +156,142 @@ class Rnp
|
|
144
156
|
def self.version_patch(version)
|
145
157
|
LibRnp.rnp_version_patch(version)
|
146
158
|
end
|
147
|
-
end # class
|
148
159
|
|
160
|
+
# Retrieve the commit time of the latest commit.
|
161
|
+
#
|
162
|
+
# This will return 0 for release/non-master builds.
|
163
|
+
#
|
164
|
+
# @return [Integer]
|
165
|
+
def self.commit_time
|
166
|
+
LibRnp.rnp_version_commit_timestamp
|
167
|
+
end
|
168
|
+
|
169
|
+
# Parse OpenPGP data to JSON.
|
170
|
+
#
|
171
|
+
# @param input [Input] the input to read the data
|
172
|
+
# @param mpi [Boolean] if true then MPIs will be included
|
173
|
+
# @param raw [Boolean] if true then raw bytes will be included
|
174
|
+
# @param grip [Boolean] if true then grips will be included
|
175
|
+
# @return [Array]
|
176
|
+
def self.parse(input:, mpi: false, raw: false, grip: false)
|
177
|
+
flags = 0
|
178
|
+
flags |= LibRnp::RNP_JSON_DUMP_MPI if mpi
|
179
|
+
flags |= LibRnp::RNP_JSON_DUMP_RAW if raw
|
180
|
+
flags |= LibRnp::RNP_JSON_DUMP_GRIP if grip
|
181
|
+
pptr = FFI::MemoryPointer.new(:pointer)
|
182
|
+
Rnp.call_ffi(:rnp_dump_packets_to_json, input.ptr, flags, pptr)
|
183
|
+
begin
|
184
|
+
pjson = pptr.read_pointer
|
185
|
+
JSON.parse(pjson.read_string) unless pjson.null?
|
186
|
+
ensure
|
187
|
+
LibRnp.rnp_buffer_destroy(pjson)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# Calculate s2k iterations
|
192
|
+
#
|
193
|
+
# @param hash [String] the hash algorithm to use
|
194
|
+
# @param msec [Integer] the desired number of milliseconds
|
195
|
+
# @return [Integer]
|
196
|
+
def self.s2k_iterations(hash:, msec:)
|
197
|
+
piters = FFI::MemoryPointer.new(:size_t)
|
198
|
+
Rnp.call_ffi(:rnp_calculate_iterations, hash, msec, piters)
|
199
|
+
piters.read(:size_t)
|
200
|
+
end
|
201
|
+
|
202
|
+
# Enable debugging
|
203
|
+
#
|
204
|
+
# @param file [String] the file to enable debugging for (or nil for all)
|
205
|
+
# @return [void]
|
206
|
+
def self.enable_debug(file = nil)
|
207
|
+
Rnp.call_ffi(:rnp_enable_debug, file)
|
208
|
+
end
|
209
|
+
|
210
|
+
# Disable previously-enabled debugging
|
211
|
+
#
|
212
|
+
# @return [void]
|
213
|
+
def self.disable_debug
|
214
|
+
Rnp.call_ffi(:rnp_disable_debug)
|
215
|
+
end
|
216
|
+
|
217
|
+
# Guess the contents of an input
|
218
|
+
#
|
219
|
+
# @param input [Input]
|
220
|
+
# @return [String] the guessed content type (or 'unknown'), which may be
|
221
|
+
# used with {enarmor}
|
222
|
+
def self.guess_contents(input)
|
223
|
+
pptr = FFI::MemoryPointer.new(:pointer)
|
224
|
+
Rnp.call_ffi(:rnp_guess_contents, input.ptr, pptr)
|
225
|
+
begin
|
226
|
+
presult = pptr.read_pointer
|
227
|
+
presult.read_string unless presult.null?
|
228
|
+
ensure
|
229
|
+
LibRnp.rnp_buffer_destroy(presult)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# Check if a specific feature is supported
|
234
|
+
#
|
235
|
+
# @param type [String] the type of feature ('symmetric algorithm', ...)
|
236
|
+
# @param name [String] the specific feature ('CAST5', ...)
|
237
|
+
# @return [Boolean]
|
238
|
+
def self.supports?(type, name)
|
239
|
+
presult = FFI::MemoryPointer.new(:bool)
|
240
|
+
Rnp.call_ffi(:rnp_supports_feature, type, name, presult)
|
241
|
+
presult.read(:bool)
|
242
|
+
end
|
243
|
+
|
244
|
+
# Get a list of supported features (by type)
|
245
|
+
#
|
246
|
+
# @param type [String] the type of feature ('symmetric algorithm', ...)
|
247
|
+
# @return [Array]
|
248
|
+
def self.supported_features(type)
|
249
|
+
pptr = FFI::MemoryPointer.new(:pointer)
|
250
|
+
Rnp.call_ffi(:rnp_supported_features, type, pptr)
|
251
|
+
begin
|
252
|
+
presult = pptr.read_pointer
|
253
|
+
JSON.parse(presult.read_string) unless presult.null?
|
254
|
+
ensure
|
255
|
+
LibRnp.rnp_buffer_destroy(presult)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
# @api private
|
260
|
+
FEATURES = {
|
261
|
+
# Support for setting hash, creation, and expiration time for individual
|
262
|
+
# signatures in a sign operation. Older versions of rnp returned a
|
263
|
+
# "not implemented" error.
|
264
|
+
"per-signature-opts" => Rnp.version > Rnp.version("0.11.0") ||
|
265
|
+
Rnp.commit_time >= 1546035818,
|
266
|
+
# Correct grip calculation for Elgamal/DSA keys. This was actually before
|
267
|
+
# the commit timestamp API was added, so this isn't accurate in one case.
|
268
|
+
"dsa-elg-grip-calc" => Rnp.version > Rnp.version("0.11.0") ||
|
269
|
+
Rnp.commit_time >= 1538219020,
|
270
|
+
# Input reader callback signature was changed:
|
271
|
+
# ssize_t(void *app_ctx, void *buf, size_t len)
|
272
|
+
# bool(void *app_ctx, void *buf, size_t len, size_t *read)
|
273
|
+
"input-reader-cb-no-ssize_t" => Rnp.version >= Rnp.version("0.14.0") ||
|
274
|
+
Rnp.commit_time >= 1585833163,
|
275
|
+
# Behavior on primary userid retrieveing was changed:
|
276
|
+
# Now userid is not considered as primary if it is revoked/expired/etc.
|
277
|
+
"primary-userid-must-be-valid" => Rnp.version >= Rnp.version("0.14.0") ||
|
278
|
+
Rnp.commit_time >= 1605875599,
|
279
|
+
# Behavior was changed in v0.15.2 (issue #1509):
|
280
|
+
# userid is valid even if key expiration in userid expiration expires key.
|
281
|
+
"relax-userid-validity-checks" => Rnp.version >= Rnp.version("0.15.2") ||
|
282
|
+
Rnp.commit_time >= 1624526708,
|
283
|
+
# Behavior on default key expiration time was changed:
|
284
|
+
# Now default key expiration time is 2 years
|
285
|
+
"default-key-expiration-2-years" => Rnp.version >= Rnp.version("0.16.1") ||
|
286
|
+
Rnp.commit_time >= 1645578982,
|
287
|
+
# Behaviour on signature validation was changed:
|
288
|
+
# Now at least one valid signature is required for success
|
289
|
+
"require-single-valid-signature" => Rnp.version >= Rnp.version("0.16.1") ||
|
290
|
+
Rnp.commit_time >= 1661781294,
|
291
|
+
}.freeze
|
292
|
+
|
293
|
+
def self.has?(feature)
|
294
|
+
raise ArgumentError unless FEATURES.include?(feature)
|
295
|
+
FEATURES[feature]
|
296
|
+
end
|
297
|
+
end # class
|
data/lib/rnp/op/encrypt.rb
CHANGED
@@ -43,19 +43,18 @@ class Rnp
|
|
43
43
|
# Add a signer.
|
44
44
|
#
|
45
45
|
# @param signer [Key] the signer
|
46
|
-
# @param
|
47
|
-
# @
|
48
|
-
# @
|
46
|
+
# @param [Hash] opts set several options in one place
|
47
|
+
# @option opts [String] :hash (see #hash=)
|
48
|
+
# @option opts [Time] :creation_time (see #creation_time=)
|
49
|
+
# @option opts [Time] :expiration_time (see #expiration_time=)
|
49
50
|
# @return [self]
|
50
|
-
def add_signer(signer,
|
51
|
+
def add_signer(signer, opts = {})
|
51
52
|
pptr = FFI::MemoryPointer.new(:pointer)
|
52
53
|
Rnp.call_ffi(:rnp_op_encrypt_add_signature, @ptr, signer.ptr, pptr)
|
53
54
|
psig = pptr.read_pointer
|
54
55
|
Sign.set_signature_options(
|
55
56
|
psig,
|
56
|
-
|
57
|
-
creation_time: creation_time,
|
58
|
-
expiration_time: expiration_time
|
57
|
+
**opts,
|
59
58
|
)
|
60
59
|
self
|
61
60
|
end
|
@@ -88,20 +87,19 @@ class Rnp
|
|
88
87
|
# @note Some options are related to signatures and will have no effect if
|
89
88
|
# there are no signers.
|
90
89
|
#
|
91
|
-
# @param
|
92
|
-
# @
|
93
|
-
# @
|
94
|
-
# @
|
95
|
-
# @
|
96
|
-
# @
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
self.expiration_time = expiration_time unless expiration_time.nil?
|
90
|
+
# @param [Hash] opts set several options in one place
|
91
|
+
# @option opts [Boolean] :armored (see #armored=)
|
92
|
+
# @option opts [String] :compression (see #compression=)
|
93
|
+
# @option opts [String] :cipher (see #cipher=)
|
94
|
+
# @option opts [String] :hash (see #hash=)
|
95
|
+
# @option opts [Time] :creation_time (see #creation_time=)
|
96
|
+
# @option opts [Time] :expiration_time (see #expiration_time=)
|
97
|
+
def options=(opts)
|
98
|
+
%i{armored compression cipher aead hash creation_time
|
99
|
+
expiration_time}.each do |prop|
|
100
|
+
value = opts[prop]
|
101
|
+
send("#{prop}=", value) unless value.nil?
|
102
|
+
end
|
105
103
|
end
|
106
104
|
|
107
105
|
# Set whether the output will be ASCII-armored.
|
@@ -135,6 +133,13 @@ class Rnp
|
|
135
133
|
Rnp.call_ffi(:rnp_op_encrypt_set_cipher, @ptr, cipher)
|
136
134
|
end
|
137
135
|
|
136
|
+
# Set the AEAD algorithm for encryption.
|
137
|
+
#
|
138
|
+
# @param mode [String] the AEAD algorithm to use for encryption
|
139
|
+
def aead=(mode)
|
140
|
+
Rnp.call_ffi(:rnp_op_encrypt_set_aead, @ptr, mode.to_s)
|
141
|
+
end
|
142
|
+
|
138
143
|
# Set the hash algorithm used for calculating signatures.
|
139
144
|
#
|
140
145
|
# @note This is only valid when there is one or more signer.
|
@@ -178,4 +183,3 @@ class Rnp
|
|
178
183
|
end
|
179
184
|
end # class
|
180
185
|
end # class
|
181
|
-
|
@@ -0,0 +1,211 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# (c) 2019 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
|
+
# Key generation operation
|
13
|
+
class Generate
|
14
|
+
# @api private
|
15
|
+
attr_reader :ptr
|
16
|
+
|
17
|
+
# @api private
|
18
|
+
def initialize(ptr)
|
19
|
+
raise Rnp::Error, "NULL pointer" if ptr.null?
|
20
|
+
|
21
|
+
@ptr = FFI::AutoPointer.new(ptr, self.class.method(:destroy))
|
22
|
+
end
|
23
|
+
|
24
|
+
# @api private
|
25
|
+
def self.destroy(ptr)
|
26
|
+
LibRnp.rnp_op_generate_destroy(ptr)
|
27
|
+
end
|
28
|
+
|
29
|
+
def inspect
|
30
|
+
Rnp.inspect_ptr(self)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Set a group of options.
|
34
|
+
#
|
35
|
+
# @param [Hash] opts set several options in one place
|
36
|
+
# @option opts [Integer] :bits (see #bits=)
|
37
|
+
# @option opts [Integer] :qbits (see #qbits=)
|
38
|
+
# @option opts [Integer] :curve (see #curve=)
|
39
|
+
# @option opts [String] :hash (see #hash=)
|
40
|
+
# @option opts [String] :s2k_hash (see #s2k_hash=)
|
41
|
+
# @option opts [Integer] :s2k_iterations (see #s2k_iterations=)
|
42
|
+
# @option opts [String] :s2k_cipher (see #s2k_cipher=)
|
43
|
+
# @option opts [String] :password (see #password=)
|
44
|
+
# @option opts [String] :protection_mode (see #protection_mode=)
|
45
|
+
# @option opts [Integer] :lifetime (see #lifetime=)
|
46
|
+
# @option opts [String] :userid (see #userid=)
|
47
|
+
# @option opts [String] :usage (see #usage=)
|
48
|
+
def options=(opts)
|
49
|
+
%i{bits qbits curve hash s2k_hash s2k_iterations
|
50
|
+
s2k_cipher password protection_mode lifetime
|
51
|
+
userid usage preferences}.each do |prop|
|
52
|
+
value = opts[prop]
|
53
|
+
send("#{prop}=", value) unless value.nil?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Set the bit length of the key.
|
58
|
+
#
|
59
|
+
# @param len [Integer] the desired bit length
|
60
|
+
def bits=(len)
|
61
|
+
Rnp.call_ffi(:rnp_op_generate_set_bits, @ptr, len)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Set the bit length of the q parameter for a DSA key.
|
65
|
+
#
|
66
|
+
# @note This is only valid for DSA keys.
|
67
|
+
#
|
68
|
+
# @param len [Integer] the desired bit length
|
69
|
+
def qbits=(len)
|
70
|
+
Rnp.call_ffi(:rnp_op_generate_set_dsa_qbits, @ptr, len)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Set the desired curve for this ECC key.
|
74
|
+
#
|
75
|
+
# @note This is only valid for ECC keys which permit specifying a curve.
|
76
|
+
#
|
77
|
+
# @param curve [String] the curve
|
78
|
+
def curve=(curve)
|
79
|
+
Rnp.call_ffi(:rnp_op_generate_set_curve, @ptr, curve.to_s)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Set the hash algorithm used in the self-signature of the key.
|
83
|
+
#
|
84
|
+
# @param hash [String] the hash algorithm name
|
85
|
+
def hash=(hash)
|
86
|
+
Rnp.call_ffi(:rnp_op_generate_set_hash, @ptr, hash.to_s)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Set the hash algorithm used to protect the key.
|
90
|
+
#
|
91
|
+
# @param hash [String] the hash algorithm name
|
92
|
+
def s2k_hash=(hash)
|
93
|
+
Rnp.call_ffi(:rnp_op_generate_set_protection_hash, @ptr, hash.to_s)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Set the s2k iteration count used to protect the key.
|
97
|
+
#
|
98
|
+
# @param iter [Integer] the hash algorithm name
|
99
|
+
def s2k_iterations=(iter)
|
100
|
+
Rnp.call_ffi(:rnp_op_generate_set_protection_iterations, @ptr, iter)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Set the cipher used to protect the key.
|
104
|
+
#
|
105
|
+
# @param cipher [String] the cipher algorithm name
|
106
|
+
def s2k_cipher=(cipher)
|
107
|
+
Rnp.call_ffi(:rnp_op_generate_set_protection_cipher, @ptr, cipher.to_s)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Set the password used to protect the key.
|
111
|
+
#
|
112
|
+
# @param password [String] the password
|
113
|
+
def password=(password)
|
114
|
+
Rnp.call_ffi(:rnp_op_generate_set_protection_password, @ptr, password)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Set the protection mode for this key.
|
118
|
+
#
|
119
|
+
# @note This is only valid for keys saved in the G10 format.
|
120
|
+
#
|
121
|
+
# @param mode [String] the protection mode (OCB, CBC, etc)
|
122
|
+
def protection_mode=(mode)
|
123
|
+
Rnp.call_ffi(:rnp_op_generate_set_protection_mode, @ptr, mode.to_s)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Set the number of seconds for this key to remain valid.
|
127
|
+
#
|
128
|
+
# This determines the expiration time (creation time + lifetime).
|
129
|
+
#
|
130
|
+
# @param secs [Integer] the number of seconds until this key will be
|
131
|
+
# considered expired. A value of 0 indicates no expiration.
|
132
|
+
# Note that there is an upper limit of 2^32-1.
|
133
|
+
def lifetime=(secs)
|
134
|
+
Rnp.call_ffi(:rnp_op_generate_set_expiration, @ptr, secs)
|
135
|
+
end
|
136
|
+
|
137
|
+
# Set the userid for this key.
|
138
|
+
#
|
139
|
+
# @param userid [String] the userid
|
140
|
+
def userid=(userid)
|
141
|
+
Rnp.call_ffi(:rnp_op_generate_set_userid, @ptr, userid)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Set the usage for this key.
|
145
|
+
#
|
146
|
+
# @param usage [Array<Symbol>,Array<String>,Symbol,String] the usage
|
147
|
+
# (:sign, etc)
|
148
|
+
def usage=(usage)
|
149
|
+
usage = [usage] unless usage.respond_to?(:each)
|
150
|
+
Rnp.call_ffi(:rnp_op_generate_clear_usage, @ptr)
|
151
|
+
usage.each do |usg|
|
152
|
+
Rnp.call_ffi(:rnp_op_generate_add_usage, @ptr, usg.to_s)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Set the preferences for the generated key.
|
157
|
+
|
158
|
+
# @param [Hash] prefs set several preferences in one place
|
159
|
+
# @option prefs [Array<String>, Array<Symbol>] :hashes
|
160
|
+
# @option prefs [Array<String>, Array<Symbol>] :compression
|
161
|
+
# @option prefs [Array<String>, Array<Symbol>] :ciphers
|
162
|
+
# @option prefs [String] :key_server
|
163
|
+
def preferences=(prefs)
|
164
|
+
%i{hashes compression ciphers}.each do |param|
|
165
|
+
Rnp.call_ffi(pref_ffi_call(param, clear: true), @ptr)
|
166
|
+
prefs[param].each do |pref|
|
167
|
+
Rnp.call_ffi(pref_ffi_call(param, add: true),
|
168
|
+
@ptr, pref.to_s)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
Rnp.call_ffi(:rnp_op_generate_set_pref_keyserver, @ptr,
|
172
|
+
prefs[:key_server])
|
173
|
+
end
|
174
|
+
|
175
|
+
# Execute the operation.
|
176
|
+
#
|
177
|
+
# This should only be called once.
|
178
|
+
#
|
179
|
+
# @return [Key] the generated key
|
180
|
+
def execute
|
181
|
+
Rnp.call_ffi(:rnp_op_generate_execute, @ptr)
|
182
|
+
key
|
183
|
+
end
|
184
|
+
|
185
|
+
# Retrieve the key.
|
186
|
+
#
|
187
|
+
# This should only be called after #execute.
|
188
|
+
#
|
189
|
+
# @return [Key]
|
190
|
+
def key
|
191
|
+
pptr = FFI::MemoryPointer.new(:pointer)
|
192
|
+
Rnp.call_ffi(:rnp_op_generate_get_key, @ptr, pptr)
|
193
|
+
pkey = pptr.read_pointer
|
194
|
+
Key.new(pkey) unless pkey.null?
|
195
|
+
end
|
196
|
+
|
197
|
+
# @api private
|
198
|
+
private
|
199
|
+
|
200
|
+
def pref_ffi_call(param, add: false, clear: false)
|
201
|
+
if add
|
202
|
+
fn = { hashes: :hash, ciphers: :cipher }.fetch(param, param)
|
203
|
+
"rnp_op_generate_add_pref_#{fn}".to_sym
|
204
|
+
elsif clear
|
205
|
+
"rnp_op_generate_clear_pref_#{param}".to_sym
|
206
|
+
else
|
207
|
+
raise ArgumentError, "add or clear must be passed"
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
data/lib/rnp/op/sign.rb
CHANGED
@@ -34,37 +34,35 @@ class Rnp
|
|
34
34
|
# @note The optional (per-signature) options here are not supported by RNP
|
35
35
|
# internally at the time of this writing.
|
36
36
|
#
|
37
|
-
# @param
|
38
|
-
# @
|
39
|
-
# @
|
40
|
-
# @
|
37
|
+
# @param [Hash] opts set several options in one place
|
38
|
+
# @option opts [Key] :signer the signer
|
39
|
+
# @option opts [String] :hash (see #hash=)
|
40
|
+
# @option opts [Time] :creation_time (see #creation_time=)
|
41
|
+
# @option opts [Time] :expiration_time (see #expiration_time=)
|
41
42
|
# @return [self]
|
42
|
-
def add_signer(signer,
|
43
|
+
def add_signer(signer, opts = {})
|
43
44
|
pptr = FFI::MemoryPointer.new(:pointer)
|
44
45
|
Rnp.call_ffi(:rnp_op_sign_add_signature, @ptr, signer.ptr, pptr)
|
45
46
|
psig = pptr.read_pointer
|
46
47
|
self.class.set_signature_options(
|
47
48
|
psig,
|
48
|
-
|
49
|
-
creation_time: creation_time,
|
50
|
-
expiration_time: expiration_time
|
49
|
+
**opts,
|
51
50
|
)
|
52
51
|
end
|
53
52
|
|
54
53
|
# Set a group of options.
|
55
54
|
#
|
56
|
-
# @param
|
57
|
-
# @
|
58
|
-
# @
|
59
|
-
# @
|
60
|
-
# @
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
self.expiration_time = expiration_time unless expiration_time.nil?
|
55
|
+
# @param [Hash] opts set several options in one place
|
56
|
+
# @option opts [String] :armored see {#armored=}
|
57
|
+
# @option opts [String] :compression see {#compression=}
|
58
|
+
# @option opts [String] :hash see {#hash=}
|
59
|
+
# @option opts [String] :creation_time see {#creation_time=}
|
60
|
+
# @option opts [String] :expiration_time see {#expiration_time=}
|
61
|
+
def options=(opts)
|
62
|
+
%i{armored compression hash creation_time expiration_time}.each do |prop|
|
63
|
+
value = opts[prop]
|
64
|
+
send("#{prop}=", value) unless value.nil?
|
65
|
+
end
|
68
66
|
end
|
69
67
|
|
70
68
|
# Set whether the output will be ASCII-armored.
|
@@ -127,12 +125,14 @@ class Rnp
|
|
127
125
|
end
|
128
126
|
|
129
127
|
# @api private
|
130
|
-
def self.set_signature_options(psig, hash
|
131
|
-
expiration_time:)
|
132
|
-
Rnp.call_ffi(:rnp_op_sign_signature_set_hash, psig,
|
128
|
+
def self.set_signature_options(psig, hash: nil, creation_time: nil,
|
129
|
+
expiration_time: nil)
|
130
|
+
Rnp.call_ffi(:rnp_op_sign_signature_set_hash, psig, hash) unless hash.nil?
|
133
131
|
creation_time = creation_time.to_i if creation_time.is_a?(::Time)
|
134
|
-
Rnp.call_ffi(:rnp_op_sign_signature_set_creation_time, psig,
|
135
|
-
|
132
|
+
Rnp.call_ffi(:rnp_op_sign_signature_set_creation_time, psig,
|
133
|
+
creation_time) unless creation_time.nil?
|
134
|
+
Rnp.call_ffi(:rnp_op_sign_signature_set_expiration_time, psig,
|
135
|
+
expiration_time) unless expiration_time.nil?
|
136
136
|
end
|
137
137
|
end # class
|
138
138
|
end # class
|
data/lib/rnp/output.rb
CHANGED
@@ -86,6 +86,20 @@ class Rnp
|
|
86
86
|
to_callback(io.method(:write))
|
87
87
|
end
|
88
88
|
|
89
|
+
# Write to the output.
|
90
|
+
#
|
91
|
+
# @param strings [String]
|
92
|
+
# @return [Integer] the number of bytes written
|
93
|
+
def write(*strings)
|
94
|
+
total_written = 0
|
95
|
+
pwritten = FFI::MemoryPointer.new(:size_t)
|
96
|
+
strings.each do |string|
|
97
|
+
Rnp.call_ffi(:rnp_output_write, @ptr, string, string.size, pwritten)
|
98
|
+
total_written += pwritten.read(:size_t)
|
99
|
+
end
|
100
|
+
total_written
|
101
|
+
end
|
102
|
+
|
89
103
|
# Retrieve the data written. Only valid for #{to_string}.
|
90
104
|
#
|
91
105
|
# @return [String, nil]
|