rnp 0.2.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|