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/op/sign.rb
ADDED
@@ -0,0 +1,139 @@
|
|
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
|
+
# Signing operation
|
13
|
+
class Sign
|
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
|
+
@ptr = FFI::AutoPointer.new(ptr, self.class.method(:destroy))
|
21
|
+
end
|
22
|
+
|
23
|
+
# @api private
|
24
|
+
def self.destroy(ptr)
|
25
|
+
LibRnp.rnp_op_sign_destroy(ptr)
|
26
|
+
end
|
27
|
+
|
28
|
+
def inspect
|
29
|
+
Rnp.inspect_ptr(self)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Add a signer.
|
33
|
+
#
|
34
|
+
# @note The optional (per-signature) options here are not supported by RNP
|
35
|
+
# internally at the time of this writing.
|
36
|
+
#
|
37
|
+
# @param signer [Key] the signer
|
38
|
+
# @param hash [String] (see #hash=)
|
39
|
+
# @param creation_time (see #creation_time=)
|
40
|
+
# @param expiration_time (see #expiration_time=)
|
41
|
+
# @return [self]
|
42
|
+
def add_signer(signer, hash: nil, creation_time: nil, expiration_time: nil)
|
43
|
+
pptr = FFI::MemoryPointer.new(:pointer)
|
44
|
+
Rnp.call_ffi(:rnp_op_sign_add_signature, @ptr, signer.ptr, pptr)
|
45
|
+
psig = pptr.read_pointer
|
46
|
+
self.class.set_signature_options(
|
47
|
+
psig,
|
48
|
+
hash: hash,
|
49
|
+
creation_time: creation_time,
|
50
|
+
expiration_time: expiration_time
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Set a group of options.
|
55
|
+
#
|
56
|
+
# @param armored see {#armored=}
|
57
|
+
# @param compression see {#compression=}
|
58
|
+
# @param hash see {#hash=}
|
59
|
+
# @param creation_time see {#creation_time=}
|
60
|
+
# @param expiration_time see {#expiration_time=}
|
61
|
+
def options=(armored: nil, compression: nil, hash: nil,
|
62
|
+
creation_time: nil, expiration_time: nil)
|
63
|
+
self.armored = armored unless armored.nil?
|
64
|
+
self.compression = compression unless compression.nil?
|
65
|
+
self.hash = hash unless hash.nil?
|
66
|
+
self.creation_time = creation_time unless creation_time.nil?
|
67
|
+
self.expiration_time = expiration_time unless expiration_time.nil?
|
68
|
+
end
|
69
|
+
|
70
|
+
# Set whether the output will be ASCII-armored.
|
71
|
+
#
|
72
|
+
# @param armored [Boolean] true if the output should be
|
73
|
+
# ASCII-armored, false otherwise.
|
74
|
+
def armored=(armored)
|
75
|
+
Rnp.call_ffi(:rnp_op_sign_set_armor, @ptr, armored)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Set the compression algorithm and level.
|
79
|
+
#
|
80
|
+
# @param [Hash<Symbol>] compression
|
81
|
+
# @option compression [String] :algorithm the compression algorithm
|
82
|
+
# (bzip2, etc)
|
83
|
+
# @option compression [Integer] :level the compression level. This should
|
84
|
+
# generally be between 0 (no compression) and 9 (best compression).
|
85
|
+
def compression=(compression)
|
86
|
+
if !compression.is_a?(Hash) || Set.new(compression.keys) != Set.new(%i[algorithm level])
|
87
|
+
raise ArgumentError,
|
88
|
+
'Compression option must be of the form: {algorithm: \'zlib\', level: 5}'
|
89
|
+
end
|
90
|
+
Rnp.call_ffi(:rnp_op_sign_set_compression, @ptr, compression[:algorithm],
|
91
|
+
compression[:level])
|
92
|
+
end
|
93
|
+
|
94
|
+
# Set the hash algorithm used for the signatures.
|
95
|
+
#
|
96
|
+
# @param hash [String] the hash algorithm name
|
97
|
+
def hash=(hash)
|
98
|
+
Rnp.call_ffi(:rnp_op_sign_set_hash, @ptr, hash)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Set the creation time for signatures.
|
102
|
+
#
|
103
|
+
# @param creation_time [Time, Integer] the creation time to use for all
|
104
|
+
# signatures. As an integer, this is the number of seconds since the
|
105
|
+
# unix epoch.
|
106
|
+
def creation_time=(creation_time)
|
107
|
+
creation_time = creation_time.to_i if creation_time.is_a?(::Time)
|
108
|
+
Rnp.call_ffi(:rnp_op_sign_set_creation_time, @ptr, creation_time)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Set the expiration time for signatures.
|
112
|
+
#
|
113
|
+
# @param expiration_time [Integer] the lifetime of the signature(s), as the
|
114
|
+
# number of seconds. The actual expiration date/time is the creation time
|
115
|
+
# plus this value. A value of 0 will create signatures that do not expire.
|
116
|
+
def expiration_time=(expiration_time)
|
117
|
+
Rnp.call_ffi(:rnp_op_sign_set_expiration_time, @ptr, expiration_time)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Execute the operation.
|
121
|
+
#
|
122
|
+
# This should only be called once.
|
123
|
+
#
|
124
|
+
# @return [void]
|
125
|
+
def execute
|
126
|
+
Rnp.call_ffi(:rnp_op_sign_execute, @ptr)
|
127
|
+
end
|
128
|
+
|
129
|
+
# @api private
|
130
|
+
def self.set_signature_options(psig, hash:, creation_time:,
|
131
|
+
expiration_time:)
|
132
|
+
Rnp.call_ffi(:rnp_op_sign_signature_set_hash, psig, value) unless hash.nil?
|
133
|
+
creation_time = creation_time.to_i if creation_time.is_a?(::Time)
|
134
|
+
Rnp.call_ffi(:rnp_op_sign_signature_set_creation_time, psig, value) unless creation_time.nil?
|
135
|
+
Rnp.call_ffi(:rnp_op_sign_signature_set_expiration_time, psig, value) unless expiration_time.nil?
|
136
|
+
end
|
137
|
+
end # class
|
138
|
+
end # class
|
139
|
+
|
@@ -0,0 +1,147 @@
|
|
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
|
+
# Verification operation
|
13
|
+
class Verify
|
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
|
+
@ptr = FFI::AutoPointer.new(ptr, self.class.method(:destroy))
|
21
|
+
@signatures = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
# @api private
|
25
|
+
def self.destroy(ptr)
|
26
|
+
LibRnp.rnp_op_verify_destroy(ptr)
|
27
|
+
end
|
28
|
+
|
29
|
+
def inspect
|
30
|
+
Rnp.inspect_ptr(self)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Execute the operation.
|
34
|
+
#
|
35
|
+
# This should only be called once.
|
36
|
+
#
|
37
|
+
# @return [void]
|
38
|
+
# @raise [InvalidSignatureError, BadFormatError, ...] if the signature is
|
39
|
+
# expired, not correctly formed, invalid, etc.
|
40
|
+
def execute
|
41
|
+
Rnp.call_ffi(:rnp_op_verify_execute, @ptr)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Check if all signatures are good.
|
45
|
+
#
|
46
|
+
# @return [Boolean] true if all signatures are valid and not expired.
|
47
|
+
def good?
|
48
|
+
sigs = signatures
|
49
|
+
sigs.size && sigs.all?(&:good?)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Get a list of the checked signatures.
|
53
|
+
#
|
54
|
+
# @return [Array<Signature>]
|
55
|
+
def signatures
|
56
|
+
return @signatures unless @signatures.nil?
|
57
|
+
@signatures = []
|
58
|
+
pptr = FFI::MemoryPointer.new(:pointer)
|
59
|
+
(0...signature_count).each do |i|
|
60
|
+
Rnp.call_ffi(:rnp_op_verify_get_signature_at, @ptr, i, pptr)
|
61
|
+
psig = pptr.read_pointer
|
62
|
+
@signatures << Signature.new(psig)
|
63
|
+
end
|
64
|
+
@signatures
|
65
|
+
end
|
66
|
+
|
67
|
+
# Class representing an individual signature.
|
68
|
+
class Signature
|
69
|
+
# @api private
|
70
|
+
attr_reader :status
|
71
|
+
# The hash algorithm used for the signature
|
72
|
+
# @return [String]
|
73
|
+
attr_reader :hash
|
74
|
+
# The key that created the signature
|
75
|
+
# @return [Key]
|
76
|
+
attr_reader :key
|
77
|
+
# The creation time of the signature
|
78
|
+
# @return [Time]
|
79
|
+
attr_reader :creation_time
|
80
|
+
# The expiration (as the number of seconds after {creation_time})
|
81
|
+
# @return [Integer]
|
82
|
+
attr_reader :expiration_time
|
83
|
+
|
84
|
+
# @api private
|
85
|
+
def initialize(ptr)
|
86
|
+
# status
|
87
|
+
@status = LibRnp.rnp_op_verify_signature_get_status(ptr)
|
88
|
+
pptr = FFI::MemoryPointer.new(:pointer)
|
89
|
+
|
90
|
+
# creation and expiration
|
91
|
+
pcreation_time = FFI::MemoryPointer.new(:uint32)
|
92
|
+
pexpiration_time = FFI::MemoryPointer.new(:uint32)
|
93
|
+
Rnp.call_ffi(:rnp_op_verify_signature_get_times, ptr, pcreation_time,
|
94
|
+
pexpiration_time)
|
95
|
+
@creation_time = Time.at(pcreation_time.read(:uint32))
|
96
|
+
@expiration_time = pexpiration_time.read(:uint32)
|
97
|
+
|
98
|
+
# hash
|
99
|
+
Rnp.call_ffi(:rnp_op_verify_signature_get_hash, ptr, pptr)
|
100
|
+
begin
|
101
|
+
phash = pptr.read_pointer
|
102
|
+
@hash = phash.read_string unless phash.null?
|
103
|
+
ensure
|
104
|
+
LibRnp.rnp_buffer_destroy(phash)
|
105
|
+
end
|
106
|
+
|
107
|
+
# key
|
108
|
+
Rnp.call_ffi(:rnp_op_verify_signature_get_key, ptr, pptr)
|
109
|
+
pkey = pptr.read_pointer
|
110
|
+
@key = Key.new(pkey) unless pkey.null?
|
111
|
+
end
|
112
|
+
|
113
|
+
# Check if this signature is good.
|
114
|
+
#
|
115
|
+
# @return [Boolean] true if the signature is valid and not expired
|
116
|
+
def good?
|
117
|
+
@status == LibRnp::RNP_SUCCESS
|
118
|
+
end
|
119
|
+
|
120
|
+
# Check if this signature is valid.
|
121
|
+
#
|
122
|
+
# @note A valid signature may also be expired.
|
123
|
+
#
|
124
|
+
# @return [Boolean] true if the signature is valid
|
125
|
+
def valid?
|
126
|
+
@status == LibRnp::RNP_SUCCESS ||
|
127
|
+
@status == LibRnp::RNP_ERROR_SIGNATURE_EXPIRED
|
128
|
+
end
|
129
|
+
|
130
|
+
# Check if this signature is expired.
|
131
|
+
#
|
132
|
+
# @return [Boolean] true if the signature is expired
|
133
|
+
def expired?
|
134
|
+
@status == LibRnp::RNP_ERROR_SIGNATURE_EXPIRED
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def signature_count
|
141
|
+
pcount = FFI::MemoryPointer.new(:size_t)
|
142
|
+
Rnp.call_ffi(:rnp_op_verify_get_signature_count, @ptr, pcount)
|
143
|
+
pcount.read(:size_t)
|
144
|
+
end
|
145
|
+
end # class
|
146
|
+
end # class
|
147
|
+
|
data/lib/rnp/output.rb
ADDED
@@ -0,0 +1,121 @@
|
|
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 out of RNP.
|
15
|
+
#
|
16
|
+
# @note When dealing with very large data, prefer {to_path} which should
|
17
|
+
# be the most efficient. {to_io} is likely to have more overhead.
|
18
|
+
#
|
19
|
+
# @example output to a string
|
20
|
+
# output = Rnp::Input.to_string('my data')
|
21
|
+
# # ... after performing operations
|
22
|
+
# output.string
|
23
|
+
#
|
24
|
+
# @example output to a file
|
25
|
+
# Rnp::Input.to_path('/path/to/my/file')
|
26
|
+
#
|
27
|
+
# @example output to a Ruby IO object
|
28
|
+
# Rnp::Input.to_io(File.open('/path/to/file', 'wb'))
|
29
|
+
class Output
|
30
|
+
# @api private
|
31
|
+
attr_reader :ptr
|
32
|
+
|
33
|
+
# @api private
|
34
|
+
def initialize(ptr, writer = nil)
|
35
|
+
raise Rnp::Error, 'NULL pointer' if ptr.null?
|
36
|
+
@ptr = FFI::AutoPointer.new(ptr, self.class.method(:destroy))
|
37
|
+
@writer = writer
|
38
|
+
end
|
39
|
+
|
40
|
+
# @api private
|
41
|
+
def self.destroy(ptr)
|
42
|
+
LibRnp.rnp_output_destroy(ptr)
|
43
|
+
end
|
44
|
+
|
45
|
+
def inspect
|
46
|
+
Rnp.inspect_ptr(self)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Create an Output to write to a string.
|
50
|
+
#
|
51
|
+
# The resulting string can later be retrieved with {#string}.
|
52
|
+
#
|
53
|
+
# @param max_alloc [Integer] the maximum amount of memory to allocate,
|
54
|
+
# or 0 for unlimited
|
55
|
+
# @return [Output]
|
56
|
+
def self.to_string(max_alloc = 0)
|
57
|
+
pptr = FFI::MemoryPointer.new(:pointer)
|
58
|
+
Rnp.call_ffi(:rnp_output_to_memory, pptr, max_alloc)
|
59
|
+
Output.new(pptr.read_pointer)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Create an Output to write to a path.
|
63
|
+
#
|
64
|
+
# @param path [String] the path
|
65
|
+
# @return [Output]
|
66
|
+
def self.to_path(path)
|
67
|
+
pptr = FFI::MemoryPointer.new(:pointer)
|
68
|
+
Rnp.call_ffi(:rnp_output_to_path, pptr, path)
|
69
|
+
Output.new(pptr.read_pointer)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Create an Output to discard all writes.
|
73
|
+
#
|
74
|
+
# @return [Output]
|
75
|
+
def self.to_null
|
76
|
+
pptr = FFI::MemoryPointer.new(:pointer)
|
77
|
+
Rnp.call_ffi(:rnp_output_to_null, pptr)
|
78
|
+
Output.new(pptr.read_pointer)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Create an Output to write to an IO object.
|
82
|
+
#
|
83
|
+
# @param io [IO, #write] the IO object
|
84
|
+
# @return [Output]
|
85
|
+
def self.to_io(io)
|
86
|
+
to_callback(io.method(:write))
|
87
|
+
end
|
88
|
+
|
89
|
+
# Retrieve the data written. Only valid for #{to_string}.
|
90
|
+
#
|
91
|
+
# @return [String, nil]
|
92
|
+
def string
|
93
|
+
pptr = FFI::MemoryPointer.new(:pointer)
|
94
|
+
len = FFI::MemoryPointer.new(:size_t)
|
95
|
+
Rnp.call_ffi(:rnp_output_memory_get_buf, @ptr, pptr, len, false)
|
96
|
+
buf = pptr.read_pointer
|
97
|
+
buf.read_bytes(len.read(:size_t)) unless buf.null?
|
98
|
+
end
|
99
|
+
|
100
|
+
# @api private
|
101
|
+
WRITER = lambda do |writer, _ctx, buf, buf_len|
|
102
|
+
begin
|
103
|
+
data = buf.read_bytes(buf_len)
|
104
|
+
written = writer.call(data)
|
105
|
+
return written == data.bytesize
|
106
|
+
rescue
|
107
|
+
puts $ERROR_INFO
|
108
|
+
return false
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# @api private
|
113
|
+
def self.to_callback(writer)
|
114
|
+
pptr = FFI::MemoryPointer.new(:pointer)
|
115
|
+
writercb = WRITER.curry[writer]
|
116
|
+
Rnp.call_ffi(:rnp_output_to_callback, pptr, writercb, nil, nil)
|
117
|
+
Output.new(pptr.read_pointer, writercb)
|
118
|
+
end
|
119
|
+
end # class
|
120
|
+
end # class
|
121
|
+
|
data/lib/rnp/rnp.rb
ADDED
@@ -0,0 +1,595 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# (c) 2018 Ribose Inc.
|
4
|
+
|
5
|
+
require 'English'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
require 'ffi'
|
9
|
+
|
10
|
+
require 'rnp/error'
|
11
|
+
require 'rnp/ffi/librnp'
|
12
|
+
require 'rnp/utils'
|
13
|
+
require 'rnp/key'
|
14
|
+
require 'rnp/op/sign'
|
15
|
+
require 'rnp/op/verify'
|
16
|
+
require 'rnp/op/encrypt'
|
17
|
+
|
18
|
+
# Class used for interacting with RNP.
|
19
|
+
class Rnp
|
20
|
+
# @api private
|
21
|
+
attr_reader :ptr
|
22
|
+
|
23
|
+
# Create a new interface to RNP.
|
24
|
+
#
|
25
|
+
# @param pubfmt [String] the public keyring format
|
26
|
+
# @param secfmt [String] the secret keyring format
|
27
|
+
def initialize(pubfmt = 'GPG', secfmt = 'GPG')
|
28
|
+
pptr = FFI::MemoryPointer.new(:pointer)
|
29
|
+
Rnp.call_ffi(:rnp_ffi_create, pptr, pubfmt, secfmt)
|
30
|
+
@ptr = FFI::AutoPointer.new(pptr.read_pointer, self.class.method(:destroy))
|
31
|
+
@key_provider = nil
|
32
|
+
@password_provider = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
# @api private
|
36
|
+
def self.destroy(ptr)
|
37
|
+
LibRnp.rnp_ffi_destroy(ptr)
|
38
|
+
end
|
39
|
+
|
40
|
+
def inspect
|
41
|
+
Rnp.inspect_ptr(self)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Set a logging destination.
|
45
|
+
#
|
46
|
+
# @param fd [Integer, IO] the file descriptor to log to. This will be closed
|
47
|
+
# when this object is destroyed.
|
48
|
+
def log=(fd)
|
49
|
+
fd = fd.to_i if fd.is_a(::IO)
|
50
|
+
Rnp.call_ffi(:rnp_ffi_set_log_fd, @ptr, fd)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Set a key provider.
|
54
|
+
#
|
55
|
+
# The key provider is useful if, for example, you have a database of keys and
|
56
|
+
# you do not want to load all of them, and you don't know which will be needed
|
57
|
+
# for a given operation.
|
58
|
+
#
|
59
|
+
# The key provider will be called to request that a key be loaded, and the
|
60
|
+
# key provider is responsible for loading the appropriate key (if available)
|
61
|
+
# using {#load_keys}.
|
62
|
+
#
|
63
|
+
# The provider may be called multiple times for the same key, but with
|
64
|
+
# different identifiers. For example, it may first be called with
|
65
|
+
# a fingerprint, then (if the key was not loaded), it may be called with a
|
66
|
+
# keyid.
|
67
|
+
#
|
68
|
+
# == Examples
|
69
|
+
# === examples/key_provider.rb
|
70
|
+
# {include:file:examples/key_provider.rb}
|
71
|
+
#
|
72
|
+
# @param provider [Proc, #call] a callable object
|
73
|
+
def key_provider=(provider)
|
74
|
+
@key_provider = provider
|
75
|
+
@key_provider = KEY_PROVIDER.curry[provider] if provider
|
76
|
+
Rnp.call_ffi(:rnp_ffi_set_key_provider, @ptr, @key_provider, nil)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Set a password provider.
|
80
|
+
#
|
81
|
+
# The password provider is used for retrieving passwords for various
|
82
|
+
# operations, including:
|
83
|
+
# * Signing data
|
84
|
+
# * Decrypting data (public-key or symmetric)
|
85
|
+
# * Adding a userid to a key
|
86
|
+
# * Unlocking a key
|
87
|
+
# * Unprotecting a key
|
88
|
+
#
|
89
|
+
# == Examples
|
90
|
+
# === examples/password_provider.rb
|
91
|
+
# {include:file:examples/password_provider.rb}
|
92
|
+
#
|
93
|
+
# @param provider [Proc, #call, String] a callable object, or a password
|
94
|
+
def password_provider=(provider)
|
95
|
+
@password_provider = provider
|
96
|
+
@password_provider = PASS_PROVIDER.curry[provider] if provider
|
97
|
+
Rnp.call_ffi(:rnp_ffi_set_pass_provider, @ptr, @password_provider, nil)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Generate a new key or pair of keys.
|
101
|
+
#
|
102
|
+
# @note The generated key(s) will be unprotected and unlocked.
|
103
|
+
# The application should protect and lock the keys with
|
104
|
+
# {Key#protect} and {Key#lock}.
|
105
|
+
#
|
106
|
+
# == Examples
|
107
|
+
# === examples/key_generation.rb
|
108
|
+
# {include:file:examples/key_generation.rb}
|
109
|
+
#
|
110
|
+
# @param description [String, Hash]
|
111
|
+
# @return [Hash<Symbol, Key>] a hash containing the generated key(s)
|
112
|
+
def generate_key(description)
|
113
|
+
description = JSON.generate(description) unless description.is_a?(String)
|
114
|
+
pptr = FFI::MemoryPointer.new(:pointer)
|
115
|
+
Rnp.call_ffi(:rnp_generate_key_json, @ptr, description, pptr)
|
116
|
+
begin
|
117
|
+
presults = pptr.read_pointer
|
118
|
+
return nil if presults.null?
|
119
|
+
results = JSON.parse(presults.read_string)
|
120
|
+
generated = {}
|
121
|
+
results.each do |k, v|
|
122
|
+
key = find_key(v.keys[0].to_sym => v.values[0])
|
123
|
+
generated[k.to_sym] = key
|
124
|
+
end
|
125
|
+
generated
|
126
|
+
ensure
|
127
|
+
LibRnp.rnp_buffer_destroy(presults)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Load keys.
|
132
|
+
#
|
133
|
+
# @param format [String] the format of the keys to load (GPG, KBX, G10).
|
134
|
+
# @param input [Input] the input to read the keys from
|
135
|
+
# @param public_keys [Boolean] whether to load public keys
|
136
|
+
# @param secret_keys [Boolean] whether to load secret keys
|
137
|
+
# @return [void]
|
138
|
+
def load_keys(input:, format:, public_keys: true, secret_keys: true)
|
139
|
+
raise ArgumentError, 'At least one of public_keys or secret_keys must be true' if !public_keys && !secret_keys
|
140
|
+
flags = load_save_flags(public_keys: public_keys, secret_keys: secret_keys)
|
141
|
+
Rnp.call_ffi(:rnp_load_keys, @ptr, format, input.ptr, flags)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Save keys.
|
145
|
+
#
|
146
|
+
# @param format [String] the format to save the keys in (GPG, KBX, G10).
|
147
|
+
# @param output [Output] the output to write the keys to
|
148
|
+
# @param public_keys [Boolean] whether to load public keys
|
149
|
+
# @param secret_keys [Boolean] whether to load secret keys
|
150
|
+
# @return [void]
|
151
|
+
def save_keys(output:, format:, public_keys: false, secret_keys: false)
|
152
|
+
raise ArgumentError, 'At least one of public_keys or secret_keys must be true' if !public_keys && !secret_keys
|
153
|
+
flags = load_save_flags(public_keys: public_keys, secret_keys: secret_keys)
|
154
|
+
Rnp.call_ffi(:rnp_save_keys, @ptr, format, output.ptr, flags)
|
155
|
+
end
|
156
|
+
|
157
|
+
# Find a key.
|
158
|
+
#
|
159
|
+
# @param criteria [Hash] the search criteria. Some examples would be:
|
160
|
+
# * !{keyid: '2FCADF05FFA501BB'}
|
161
|
+
# * !{'userid': 'user0'}
|
162
|
+
# * !{fingerprint: 'BE1C4AB951F4C2F6B604'}
|
163
|
+
# Only *one* criteria can be specified.
|
164
|
+
# @return [Key, nil]
|
165
|
+
def find_key(criteria)
|
166
|
+
raise Rnp::Error, 'Invalid search criteria' if !criteria.is_a?(::Hash) ||
|
167
|
+
criteria.size != 1
|
168
|
+
pptr = FFI::MemoryPointer.new(:pointer)
|
169
|
+
Rnp.call_ffi(:rnp_locate_key, @ptr, criteria.keys[0].to_s,
|
170
|
+
criteria.values[0], pptr)
|
171
|
+
pkey = pptr.read_pointer
|
172
|
+
Rnp::Key.new(pkey) unless pkey.null?
|
173
|
+
end
|
174
|
+
|
175
|
+
# @!method userids
|
176
|
+
# Get a list of all userids.
|
177
|
+
#
|
178
|
+
# @return [Array<String>]
|
179
|
+
|
180
|
+
# @!method each_userid(&block)
|
181
|
+
# Enumerate all userids.
|
182
|
+
#
|
183
|
+
# @return [self, Enumerator]
|
184
|
+
|
185
|
+
# @!method keyids
|
186
|
+
# Get a list of all keyids.
|
187
|
+
#
|
188
|
+
# @return [Array<String>]
|
189
|
+
|
190
|
+
# @!method each_keyid(&block)
|
191
|
+
# Enumerate all keyids.
|
192
|
+
#
|
193
|
+
# @return [self, Enumerator]
|
194
|
+
|
195
|
+
# @!method fingerprints
|
196
|
+
# Get a list of all fingerprints.
|
197
|
+
#
|
198
|
+
# @return [Array<String>]
|
199
|
+
|
200
|
+
# @!method each_fingerprint(&block)
|
201
|
+
# Enumerate all fingerprints.
|
202
|
+
#
|
203
|
+
# @return [self, Enumerator]
|
204
|
+
|
205
|
+
# @!method grips
|
206
|
+
# Get a list of all grips.
|
207
|
+
#
|
208
|
+
# @return [Array<String>]
|
209
|
+
|
210
|
+
# @!method each_grip(&block)
|
211
|
+
# Enumerate all grips.
|
212
|
+
#
|
213
|
+
# @return [self, Enumerator]
|
214
|
+
|
215
|
+
%w[userid keyid fingerprint grip].each do |identifier_type|
|
216
|
+
define_method("each_#{identifier_type}".to_sym) do |&block|
|
217
|
+
each_identifier(identifier_type, &block)
|
218
|
+
end
|
219
|
+
|
220
|
+
define_method("#{identifier_type}s".to_sym) do
|
221
|
+
each_identifier(identifier_type).to_a
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def public_key_count
|
226
|
+
pcount = FFI::MemoryPointer.new(:size_t)
|
227
|
+
Rnp.call_ffi(:rnp_get_public_key_count, pcount)
|
228
|
+
pcount.read(:size_t)
|
229
|
+
end
|
230
|
+
|
231
|
+
def secret_key_count
|
232
|
+
pcount = FFI::MemoryPointer.new(:size_t)
|
233
|
+
Rnp.call_ffi(:rnp_get_secret_key_count, pcount)
|
234
|
+
pcount.read(:size_t)
|
235
|
+
end
|
236
|
+
|
237
|
+
# Create a signature.
|
238
|
+
#
|
239
|
+
# @param input [Input] the input to read the data to be signed
|
240
|
+
# @param output [Output] the output to write the signatures.
|
241
|
+
# If nil, the result will be returned directly as a String.
|
242
|
+
# @param signers [Key, Array<Key>] the keys to sign with
|
243
|
+
# @param armored (see Sign#armored=)
|
244
|
+
# @param compression (see Sign#compression=)
|
245
|
+
# @param creation_time (see Sign#creation_time=)
|
246
|
+
# @param expiration_time (see Sign#expiration_time=)
|
247
|
+
# @param hash (see Sign#hash=)
|
248
|
+
# @return [nil, String]
|
249
|
+
def sign(input:, output: nil, signers:,
|
250
|
+
armored: nil,
|
251
|
+
compression: nil,
|
252
|
+
creation_time: nil,
|
253
|
+
expiration_time: nil,
|
254
|
+
hash: nil)
|
255
|
+
default_output(output) do |output_|
|
256
|
+
sign = start_sign(input: input, output: output_)
|
257
|
+
sign.options = {
|
258
|
+
armored: armored,
|
259
|
+
compression: compression,
|
260
|
+
creation_time: creation_time,
|
261
|
+
expiration_time: expiration_time,
|
262
|
+
hash: hash
|
263
|
+
}
|
264
|
+
simple_sign(sign, signers)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
# Create a cleartext signature.
|
269
|
+
#
|
270
|
+
# @param input (see #sign)
|
271
|
+
# @param output (see #sign)
|
272
|
+
# @param signers [Key, Array<Key>] the keys to sign with
|
273
|
+
# @param compression (see Sign#compression=)
|
274
|
+
# @param creation_time (see Sign#creation_time=)
|
275
|
+
# @param expiration_time (see Sign#expiration_time=)
|
276
|
+
# @param hash (see Sign#hash=)
|
277
|
+
# @return [nil, String]
|
278
|
+
def cleartext_sign(input:, output: nil, signers:,
|
279
|
+
compression: nil,
|
280
|
+
creation_time: nil,
|
281
|
+
expiration_time: nil,
|
282
|
+
hash: nil)
|
283
|
+
default_output(output) do |output_|
|
284
|
+
sign = start_cleartext_sign(input: input, output: output_)
|
285
|
+
sign.options = {
|
286
|
+
compression: compression,
|
287
|
+
creation_time: creation_time,
|
288
|
+
expiration_time: expiration_time,
|
289
|
+
hash: hash
|
290
|
+
}
|
291
|
+
simple_sign(sign, signers)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
# Create a detached signature.
|
296
|
+
#
|
297
|
+
# @param input (see #sign)
|
298
|
+
# @param output (see #sign)
|
299
|
+
# @param signers [Key, Array<Key>] the keys to sign with
|
300
|
+
# @param armored (see Sign#armored=)
|
301
|
+
# @param compression (see Sign#compression=)
|
302
|
+
# @param creation_time (see Sign#creation_time=)
|
303
|
+
# @param expiration_time (see Sign#expiration_time=)
|
304
|
+
# @param hash (see Sign#hash=)
|
305
|
+
# @return [nil, String]
|
306
|
+
def detached_sign(input:, output: nil, signers:,
|
307
|
+
armored: nil,
|
308
|
+
compression: nil,
|
309
|
+
creation_time: nil,
|
310
|
+
expiration_time: nil,
|
311
|
+
hash: nil)
|
312
|
+
default_output(output) do |output_|
|
313
|
+
sign = start_detached_sign(input: input, output: output_)
|
314
|
+
sign.options = {
|
315
|
+
armored: armored,
|
316
|
+
compression: compression,
|
317
|
+
creation_time: creation_time,
|
318
|
+
expiration_time: expiration_time,
|
319
|
+
hash: hash
|
320
|
+
}
|
321
|
+
simple_sign(sign, signers)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
# Verify a signature.
|
326
|
+
#
|
327
|
+
# @param input [Input] the input to read the signatures
|
328
|
+
# @param output [Output] the output (if any) to write the verified data
|
329
|
+
def verify(input:, output: nil)
|
330
|
+
verify = start_verify(input: input, output: output)
|
331
|
+
verify.execute
|
332
|
+
end
|
333
|
+
|
334
|
+
# Verify a detached signature.
|
335
|
+
#
|
336
|
+
# @param data [Input] the input to read the data
|
337
|
+
# @param signature [Input] the input to read the signatures
|
338
|
+
def detached_verify(data:, signature:)
|
339
|
+
verify = start_detached_verify(data: data, signature: signature)
|
340
|
+
verify.execute
|
341
|
+
end
|
342
|
+
|
343
|
+
# Encrypt data with a public key.
|
344
|
+
#
|
345
|
+
# @param input [Input] the input to read the plaintext
|
346
|
+
# @param output [Output] the output to write the encrypted data.
|
347
|
+
# If nil, the result will be returned directly as a String.
|
348
|
+
# @param recipients [Key, Array<Key>] list of recipients keys
|
349
|
+
# @param armored (see Encrypt#armored=)
|
350
|
+
# @param compression (see Encrypt#compression=)
|
351
|
+
# @param cipher (see Encrypt#cipher=)
|
352
|
+
def encrypt(input:, output: nil, recipients:,
|
353
|
+
armored: nil,
|
354
|
+
compression: nil,
|
355
|
+
cipher: nil)
|
356
|
+
default_output(output) do |output_|
|
357
|
+
enc = start_encrypt(input: input, output: output_)
|
358
|
+
enc.options = {
|
359
|
+
armored: armored,
|
360
|
+
compression: compression,
|
361
|
+
cipher: cipher
|
362
|
+
}
|
363
|
+
simple_encrypt(enc, recipients: recipients)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
# Encrypt and sign data with a public key.
|
368
|
+
#
|
369
|
+
# @param input (see #encrypt)
|
370
|
+
# @param output (see #encrypt)
|
371
|
+
# @param recipients (see #encrypt)
|
372
|
+
# @param signers [Key, Array<Key>] list of keys to sign with
|
373
|
+
# @param armored (see Encrypt#armored=)
|
374
|
+
# @param compression (see Encrypt#compression=)
|
375
|
+
# @param cipher (see Encrypt#cipher=)
|
376
|
+
# @param hash (see Encrypt#hash=)
|
377
|
+
# @param creation_time (see Encrypt#creation_time=)
|
378
|
+
# @param expiration_time (see Encrypt#expiration_time=)
|
379
|
+
def encrypt_and_sign(input:, output: nil, recipients:, signers:,
|
380
|
+
armored: nil,
|
381
|
+
compression: nil,
|
382
|
+
cipher: nil,
|
383
|
+
hash: nil,
|
384
|
+
creation_time: nil,
|
385
|
+
expiration_time: nil)
|
386
|
+
default_output(output) do |output_|
|
387
|
+
enc = start_encrypt(input: input, output: output_)
|
388
|
+
enc.options = {
|
389
|
+
armored: armored,
|
390
|
+
compression: compression,
|
391
|
+
cipher: cipher,
|
392
|
+
hash: hash,
|
393
|
+
creation_time: creation_time,
|
394
|
+
expiration_time: expiration_time
|
395
|
+
}
|
396
|
+
simple_encrypt(enc, recipients: recipients, signers: signers)
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
# Encrypt with a password only.
|
401
|
+
#
|
402
|
+
# @param input (see #encrypt)
|
403
|
+
# @param output (see #encrypt)
|
404
|
+
# @param passwords [String, Array<String>] list of passwords to encrypt with.
|
405
|
+
# Any (single) one of the passwords can be used to decrypt.
|
406
|
+
# @param armored (see Encrypt#armored=)
|
407
|
+
# @param compression (see Encrypt#compression=)
|
408
|
+
# @param cipher (see Encrypt#cipher=)
|
409
|
+
# @param s2k_hash (see Encrypt#add_password)
|
410
|
+
# @param s2k_iterations (see Encrypt#add_password)
|
411
|
+
# @param s2k_cipher (see Encrypt#add_password)
|
412
|
+
# @return [void]
|
413
|
+
def symmetric_encrypt(input:, output: nil, passwords:,
|
414
|
+
armored: nil,
|
415
|
+
compression: nil,
|
416
|
+
cipher: nil,
|
417
|
+
s2k_hash: nil,
|
418
|
+
s2k_iterations: 0,
|
419
|
+
s2k_cipher: nil)
|
420
|
+
default_output(output) do |output_|
|
421
|
+
enc = start_encrypt(input: input, output: output_)
|
422
|
+
enc.options = {
|
423
|
+
armored: armored,
|
424
|
+
compression: compression,
|
425
|
+
cipher: cipher
|
426
|
+
}
|
427
|
+
passwords = [passwords] if passwords.is_a?(String)
|
428
|
+
passwords.each do |password|
|
429
|
+
enc.add_password(password,
|
430
|
+
s2k_hash: s2k_hash,
|
431
|
+
s2k_iterations: s2k_iterations,
|
432
|
+
s2k_cipher: s2k_cipher)
|
433
|
+
end
|
434
|
+
enc.execute
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
# Decrypt encrypted data.
|
439
|
+
#
|
440
|
+
# @param input [Input] the input to read the encrypted data
|
441
|
+
# @param output [Output] the output to write the decrypted data.
|
442
|
+
# If nil, the result will be returned directly as a String.
|
443
|
+
# @return [nil, String]
|
444
|
+
def decrypt(input:, output: nil)
|
445
|
+
default_output(output) do |output_|
|
446
|
+
Rnp.call_ffi(:rnp_decrypt, @ptr, input.ptr, output_.ptr)
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
# Create a {Sign} operation.
|
451
|
+
#
|
452
|
+
# @param input [Input] the input to read the data to be signed
|
453
|
+
# @param output [Output] the output to write the signatures
|
454
|
+
def start_sign(input:, output:)
|
455
|
+
_start_sign(:rnp_op_sign_create, input, output)
|
456
|
+
end
|
457
|
+
|
458
|
+
# Create a cleartext {Sign} operation.
|
459
|
+
#
|
460
|
+
# @param input (see #start_sign)
|
461
|
+
# @param output (see #start_sign)
|
462
|
+
def start_cleartext_sign(input:, output:)
|
463
|
+
_start_sign(:rnp_op_sign_cleartext_create, input, output)
|
464
|
+
end
|
465
|
+
|
466
|
+
# Create a detached {Sign} operation.
|
467
|
+
#
|
468
|
+
# @param input (see #start_sign)
|
469
|
+
# @param output (see #start_sign)
|
470
|
+
def start_detached_sign(input:, output:)
|
471
|
+
_start_sign(:rnp_op_sign_detached_create, input, output)
|
472
|
+
end
|
473
|
+
|
474
|
+
# Create a {Verify} operation.
|
475
|
+
#
|
476
|
+
# @param input [Input] the input to read the signatures
|
477
|
+
# @param output [Output] the output (if any) to write the verified data
|
478
|
+
def start_verify(input:, output: nil)
|
479
|
+
output = Output.to_null unless output
|
480
|
+
_start_verify(:rnp_op_verify_create, input, output)
|
481
|
+
end
|
482
|
+
|
483
|
+
# Create a detached {Verify} operation.
|
484
|
+
#
|
485
|
+
# @param data [Input] the input to read the signed data
|
486
|
+
# @param signature [Input] the input to read the signatures
|
487
|
+
def start_detached_verify(data:, signature:)
|
488
|
+
_start_verify(:rnp_op_verify_detached_create, data, signature)
|
489
|
+
end
|
490
|
+
|
491
|
+
# Create an {Encrypt} operation.
|
492
|
+
#
|
493
|
+
# @param input [Input] the input to read the plaintext
|
494
|
+
# @param output [Output] the output to write the encrypted data
|
495
|
+
def start_encrypt(input:, output:)
|
496
|
+
pptr = FFI::MemoryPointer.new(:pointer)
|
497
|
+
Rnp.call_ffi(:rnp_op_encrypt_create, pptr, @ptr, input.ptr, output.ptr)
|
498
|
+
pencrypt = pptr.read_pointer
|
499
|
+
Encrypt.new(pencrypt) unless pencrypt.null?
|
500
|
+
end
|
501
|
+
|
502
|
+
private
|
503
|
+
|
504
|
+
KEY_PROVIDER = lambda do |provider, _rnp, _ctx, identifier_type, identifier, secret|
|
505
|
+
provider.call(identifier_type, identifier, secret)
|
506
|
+
end
|
507
|
+
|
508
|
+
PASS_PROVIDER = lambda do |provider, _rnp, _ctx, pkey, reason, buf, buf_len|
|
509
|
+
begin
|
510
|
+
if provider.is_a?(String)
|
511
|
+
# we were provided a a literal password
|
512
|
+
password = provider
|
513
|
+
else
|
514
|
+
key = Key.new(pkey, false) unless pkey.null?
|
515
|
+
password = provider.call(key, reason)
|
516
|
+
end
|
517
|
+
return false unless password && password.size < buf_len
|
518
|
+
buf.write_string(password)
|
519
|
+
return true
|
520
|
+
rescue
|
521
|
+
puts $ERROR_INFO
|
522
|
+
return false
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
def simple_sign(sign, signers)
|
527
|
+
signers = [signers] if signers.is_a?(Key)
|
528
|
+
signers.each do |signer|
|
529
|
+
sign.add_signer(signer)
|
530
|
+
end
|
531
|
+
sign.execute
|
532
|
+
end
|
533
|
+
|
534
|
+
def _start_sign(func, input, output)
|
535
|
+
pptr = FFI::MemoryPointer.new(:pointer)
|
536
|
+
Rnp.call_ffi(func, pptr, @ptr, input.ptr, output.ptr)
|
537
|
+
psign = pptr.read_pointer
|
538
|
+
Rnp::Sign.new(psign) unless psign.null?
|
539
|
+
end
|
540
|
+
|
541
|
+
def _start_verify(func, io1, io2)
|
542
|
+
pptr = FFI::MemoryPointer.new(:pointer)
|
543
|
+
Rnp.call_ffi(func, pptr, @ptr, io1.ptr, io2.ptr)
|
544
|
+
pverify = pptr.read_pointer
|
545
|
+
Verify.new(pverify) unless pverify.null?
|
546
|
+
end
|
547
|
+
|
548
|
+
def simple_encrypt(enc, recipients: nil, signers: nil)
|
549
|
+
recipients = [recipients] if recipients.is_a?(Key)
|
550
|
+
recipients&.each do |recipient|
|
551
|
+
enc.add_recipient(recipient)
|
552
|
+
end
|
553
|
+
signers = [signers] if signers.is_a?(Key)
|
554
|
+
signers&.each do |signer|
|
555
|
+
enc.add_signer(signer)
|
556
|
+
end
|
557
|
+
enc.execute
|
558
|
+
end
|
559
|
+
|
560
|
+
def each_identifier(type, &block)
|
561
|
+
block or return enum_for(:identifier_iterator, type)
|
562
|
+
identifier_iterator(type, &block)
|
563
|
+
self
|
564
|
+
end
|
565
|
+
|
566
|
+
def identifier_iterator(identifier_type)
|
567
|
+
pptr = FFI::MemoryPointer.new(:pointer)
|
568
|
+
piterator = nil
|
569
|
+
Rnp.call_ffi(:rnp_identifier_iterator_create, @ptr, pptr, identifier_type)
|
570
|
+
piterator = pptr.read_pointer
|
571
|
+
loop do
|
572
|
+
Rnp.call_ffi(:rnp_identifier_iterator_next, piterator, pptr)
|
573
|
+
pidentifier = pptr.read_pointer
|
574
|
+
break if pidentifier.null?
|
575
|
+
yield pidentifier.read_string
|
576
|
+
end
|
577
|
+
ensure
|
578
|
+
LibRnp.rnp_identifier_iterator_destroy(piterator) if piterator
|
579
|
+
end
|
580
|
+
|
581
|
+
def load_save_flags(public_keys:, secret_keys:)
|
582
|
+
flags = 0
|
583
|
+
flags |= LibRnp::RNP_LOAD_SAVE_PUBLIC_KEYS if public_keys
|
584
|
+
flags |= LibRnp::RNP_LOAD_SAVE_SECRET_KEYS if secret_keys
|
585
|
+
flags
|
586
|
+
end
|
587
|
+
|
588
|
+
def default_output(output)
|
589
|
+
to_str = output.nil?
|
590
|
+
output = Output.to_string if to_str
|
591
|
+
yield output
|
592
|
+
output.string if to_str
|
593
|
+
end
|
594
|
+
end # class
|
595
|
+
|