rnp 1.0.4 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6e6b754a124b29c8d9d1295ec0266d0ae8d8e435ba7b5d4680b693ad510304eb
4
- data.tar.gz: 8c78644be51833b9c033b6d53da65f4a462b5d659a0895428da5a018b50900cc
3
+ metadata.gz: ee45589cbb06b8850960bc6450e11bbd268aba93943a47483e8d4fb65c211273
4
+ data.tar.gz: 3de5947adc06bcbf90ceb86e8f2bb63dc7497e3661dce87b25fedade394066fd
5
5
  SHA512:
6
- metadata.gz: d871d7dc55944ab5d0415b057ec02fbc3eadee97dd9d27f5c080e1eb675aa3b4fd82975b30ec9113c3267094b2c8db23a567434a538c2bcb2f700a4caea59558
7
- data.tar.gz: 6cabf921b333256316531dcc98032cf2b8a878ef9fdaefdb678161e0faf1b68106669ce3e77c689c272ede779f847146db4f47d86fbe601f55f9e6174122f3e3
6
+ metadata.gz: b93e326b7b56eeec4c57e35c564896611e833dfe2695e02426d4b4a8aa75213d38f49beaf749c24c5114232ace19fe1d6a71c9f885d79c3463ea32dc7e9f0d13
7
+ data.tar.gz: 316e9dc83694d289f3f9ecb9b231241bef3ce05e574377859d2250398e832ef32b501646bf8571af6205622f1e3faf633dc715ada6bbd66c64edf0892b95149a
data/CHANGELOG.adoc CHANGED
@@ -1,5 +1,18 @@
1
1
  == Changelog
2
2
 
3
+ === 1.1.0 [19-07-2026]
4
+ * Support librnp 0.17.x/0.18.x; CI tests against v0.18.1
5
+ (CVE-2025-13470) and the rnp main branch
6
+ * Add Rnp::Signature#errors and Rnp::Op::Verify::Signature#handle
7
+ bindings for rnp_signature_error_count()/rnp_signature_error_at()
8
+ (librnp 0.18.0)
9
+ * Modernize CI (Ruby 3.1-3.4, current actions) and development
10
+ dependencies
11
+
12
+ === 1.0.5 [20-03-2023]
13
+ * Rub GHA tests for Ruby 2.7, 3.0, 3.1, 3.2, head
14
+ and different rnp versions
15
+
3
16
  === 1.0.4 [09-16-2018]
4
17
  * Add support for rnp 0.11.0+. Tests will now only pass
5
18
  with this version.
@@ -17,4 +30,3 @@
17
30
 
18
31
  === 1.0.0 [05-01-2018]
19
32
  * Completely rewritten for RNP's new FFI.
20
-
data/README.adoc CHANGED
@@ -1,12 +1,21 @@
1
1
  = Ruby RNP bindings
2
2
 
3
- image:https://img.shields.io/travis/riboseinc/ruby-rnp/master.svg["Build Status", link="https://travis-ci.org/riboseinc/ruby-rnp"]
4
- image:https://codecov.io/github/riboseinc/ruby-rnp/coverage.svg["Code Coverage", link="https://codecov.io/github/riboseinc/ruby-rnp?branch=master"]
3
+ image:https://github.com/rnpgp/ruby-rnp/actions/workflows/test-and-release.yml/badge.svg["Build Status", link="https://github.com/rnpgp/ruby-rnp/actions/workflows/test-and-release.yml"]
4
+ image:https://codecov.io/gh/rnpgp/ruby-rnp/branch/main/graph/badge.svg["Code Coverage", link="https://codecov.io/gh/rnpgp/ruby-rnp"]
5
5
 
6
6
  The `rnp` gem provides Ruby bindings to the
7
- https://github.com/riboseinc/rnp[librnp OpenPGP library].
7
+ https://github.com/rnpgp/rnp[librnp OpenPGP library].
8
8
 
9
- Documentation is available on https://www.rubydoc.info/github/riboseinc/ruby-rnp/[RubyDoc].
9
+ Documentation is available on https://www.rubydoc.info/github/rnpgp/ruby-rnp/main/[RubyDoc].
10
+
11
+ == Requirements
12
+
13
+ This gem currently requires https://github.com/rnpgp/rnp[librnp] 0.15.2 or newer.
14
+
15
+ NOTE: librnp 0.18.1 or newer is strongly recommended: librnp 0.18.0 is
16
+ affected by https://open.ribose.com/advisories/ra-2025-11-20/[CVE-2025-13470],
17
+ where PKESK session keys were generated without cryptographically random
18
+ values (older releases are not affected).
10
19
 
11
20
  == Installation
12
21
 
@@ -28,4 +37,3 @@ Or install it yourself as:
28
37
  ----
29
38
  gem install rnp
30
39
  ----
31
-
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # (c) 2018 Ribose Inc.
3
+ # (c) 2018-2020 Ribose Inc.
4
4
 
5
5
  require 'ffi'
6
6
 
@@ -9,7 +9,159 @@ require 'rnp/error'
9
9
  # @api private
10
10
  module LibRnp
11
11
  extend FFI::Library
12
- ffi_lib %w[rnp-0 rnp]
12
+
13
+ LOCAL_LIBRNP = File.join(File.dirname(__FILE__), FFI.map_library_name("rnp"))
14
+
15
+ ffi_lib [LOCAL_LIBRNP, "rnp-0", "rnp"]
16
+
17
+ # some newer APIs that may not be present
18
+ {
19
+ # key export
20
+ rnp_key_export: [%i[pointer pointer uint32], :uint32],
21
+ # enarmor/dearmor
22
+ rnp_enarmor: [%i[pointer pointer pointer], :uint32],
23
+ rnp_dearmor: [%i[pointer pointer], :uint32],
24
+ # versioning
25
+ rnp_version_string: [%i[], :string],
26
+ rnp_version_string_full: [%i[], :string],
27
+ rnp_version: [%i[], :uint32],
28
+ rnp_version_for: [%i[uint32 uint32 uint32], :uint32],
29
+ rnp_version_major: [%i[uint32], :uint32],
30
+ rnp_version_minor: [%i[uint32], :uint32],
31
+ rnp_version_patch: [%i[uint32], :uint32],
32
+ # unload keys
33
+ rnp_unload_keys: [%i[pointer uint32], :uint32],
34
+ # remove key
35
+ rnp_key_remove: [%i[pointer uint32], :uint32],
36
+ # key properties
37
+ rnp_key_get_subkey_count: [%i[pointer pointer], :uint32],
38
+ rnp_key_get_subkey_at: [%i[pointer int pointer], :uint32],
39
+ rnp_key_get_alg: [%i[pointer pointer], :uint32],
40
+ rnp_key_get_bits: [%i[pointer pointer], :uint32],
41
+ rnp_key_get_dsa_qbits: [%i[pointer pointer], :uint32],
42
+ rnp_key_get_curve: [%i[pointer pointer], :uint32],
43
+ rnp_key_allows_usage: [%i[pointer string pointer], :uint32],
44
+ # packet dumping
45
+ rnp_key_packets_to_json: [%i[pointer bool uint32 pointer], :uint32],
46
+ rnp_dump_packets_to_json: [%i[pointer uint32 pointer], :uint32],
47
+ # aead
48
+ rnp_op_encrypt_set_aead: [%i[pointer string], :uint32],
49
+ # key generation (op)
50
+ rnp_op_generate_create: [%i[pointer pointer string], :uint32],
51
+ rnp_op_generate_subkey_create: [%i[pointer pointer pointer string],
52
+ :uint32],
53
+ rnp_op_generate_set_bits: [%i[pointer uint32], :uint32],
54
+ rnp_op_generate_set_hash: [%i[pointer string], :uint32],
55
+ rnp_op_generate_set_dsa_qbits: [%i[pointer uint32], :uint32],
56
+ rnp_op_generate_set_curve: [%i[pointer string], :uint32],
57
+ rnp_op_generate_set_protection_password: [%i[pointer string], :uint32],
58
+ rnp_op_generate_set_protection_cipher: [%i[pointer string], :uint32],
59
+ rnp_op_generate_set_protection_hash: [%i[pointer string], :uint32],
60
+ rnp_op_generate_set_protection_mode: [%i[pointer string], :uint32],
61
+ rnp_op_generate_set_protection_iterations: [%i[pointer uint32], :uint32],
62
+ rnp_op_generate_add_usage: [%i[pointer string], :uint32],
63
+ rnp_op_generate_clear_usage: [%i[pointer], :uint32],
64
+ rnp_op_generate_set_userid: [%i[pointer string], :uint32],
65
+ rnp_op_generate_set_expiration: [%i[pointer uint32], :uint32],
66
+ rnp_op_generate_add_pref_hash: [%i[pointer string], :uint32],
67
+ rnp_op_generate_clear_pref_hashes: [%i[pointer], :uint32],
68
+ rnp_op_generate_add_pref_compression: [%i[pointer string], :uint32],
69
+ rnp_op_generate_clear_pref_compression: [%i[pointer], :uint32],
70
+ rnp_op_generate_add_pref_cipher: [%i[pointer string], :uint32],
71
+ rnp_op_generate_clear_pref_ciphers: [%i[pointer], :uint32],
72
+ rnp_op_generate_set_pref_keyserver: [%i[pointer pointer], :uint32],
73
+ rnp_op_generate_execute: [%i[pointer], :uint32],
74
+ rnp_op_generate_get_key: [%i[pointer pointer], :uint32],
75
+ rnp_op_generate_destroy: [%i[pointer], :uint32],
76
+ # key generation (shortcuts)
77
+ rnp_generate_key_rsa: [%i[pointer uint32 uint32 string string pointer],
78
+ :uint32],
79
+ rnp_generate_key_dsa_eg: [%i[pointer uint32 uint32 string string pointer],
80
+ :uint32],
81
+ rnp_generate_key_ec: [%i[pointer string string string pointer], :uint32],
82
+ rnp_generate_key_25519: [%i[pointer string string pointer], :uint32],
83
+ rnp_generate_key_sm2: [%i[pointer string string pointer], :uint32],
84
+ rnp_generate_key_ex: [%i[pointer string string uint32 uint32 string string
85
+ string string pointer], :uint32],
86
+ rnp_calculate_iterations: [%i[string size_t pointer], :uint32],
87
+ # debugging
88
+ rnp_enable_debug: [%i[pointer], :uint32],
89
+ rnp_disable_debug: [%i[], :uint32],
90
+ # guess contents
91
+ rnp_guess_contents: [%i[pointer pointer], :uint32],
92
+ # features
93
+ rnp_supports_feature: [%i[string string pointer], :uint32],
94
+ rnp_supported_features: [%i[string pointer], :uint32],
95
+ # key revocation
96
+ rnp_key_is_revoked: [%i[pointer pointer], :uint32],
97
+ rnp_key_is_compromised: [%i[pointer pointer], :uint32],
98
+ rnp_key_is_retired: [%i[pointer pointer], :uint32],
99
+ rnp_key_is_superseded: [%i[pointer pointer], :uint32],
100
+ rnp_key_get_revocation_reason: [%i[pointer pointer], :uint32],
101
+ # signatures
102
+ rnp_key_get_signature_count: [%i[pointer pointer], :uint32],
103
+ rnp_key_get_signature_at: [%i[pointer size_t pointer], :uint32],
104
+ rnp_signature_get_alg: [%i[pointer pointer], :uint32],
105
+ rnp_signature_get_hash_alg: [%i[pointer pointer], :uint32],
106
+ rnp_signature_get_creation: [%i[pointer pointer], :uint32],
107
+ rnp_signature_get_keyid: [%i[pointer pointer], :uint32],
108
+ rnp_signature_get_signer: [%i[pointer pointer], :uint32],
109
+ rnp_signature_packet_to_json: [%i[pointer uint32 pointer], :uint32],
110
+ rnp_signature_handle_destroy: [%i[pointer], :uint32],
111
+ rnp_op_verify_signature_get_handle: [%i[pointer pointer], :uint32],
112
+ # signature validation errors
113
+ rnp_signature_error_count: [%i[pointer pointer], :uint32],
114
+ rnp_signature_error_at: [%i[pointer size_t pointer], :uint32],
115
+ # key uids
116
+ rnp_key_get_uid_handle_at: [%i[pointer size_t pointer], :uint32],
117
+ rnp_uid_is_revoked: [%i[pointer pointer], :uint32],
118
+ rnp_uid_handle_destroy: [%i[pointer], :uint32],
119
+ rnp_uid_get_signature_count: [%i[pointer pointer], :uint32],
120
+ rnp_uid_get_signature_at: [%i[pointer size_t pointer], :uint32],
121
+ # key properties
122
+ rnp_key_get_creation: [%i[pointer pointer], :uint32],
123
+ rnp_key_get_expiration: [%i[pointer pointer], :uint32],
124
+ rnp_key_get_primary_grip: [%i[pointer pointer], :uint32],
125
+ # output
126
+ rnp_output_write: [%i[pointer pointer size_t pointer], :uint32],
127
+ # import
128
+ rnp_import_keys: [%i[pointer pointer uint32 pointer], :uint32],
129
+ rnp_import_signatures: [%i[pointer pointer uint32 pointer], :uint32],
130
+ }.each do |name, signature|
131
+ present = !ffi_libraries[0].find_function(name.to_s).nil?
132
+ if !present
133
+ class_eval do
134
+ define_singleton_method(name) do |*|
135
+ raise Rnp::FeatureNotAvailableError, name
136
+ end
137
+ end
138
+ else
139
+ attach_function name, signature[0], signature[1]
140
+ end
141
+ class_eval do
142
+ const_set("HAVE_#{name.upcase}", present)
143
+ end
144
+ end
145
+
146
+ if ffi_libraries[0].find_function('rnp_version_commit_timestamp')
147
+ attach_function :rnp_version_commit_timestamp, [], :uint64
148
+ else
149
+ def self.rnp_version_commit_timestamp
150
+ 0
151
+ end
152
+ end
153
+
154
+ if HAVE_RNP_VERSION && (rnp_version >= rnp_version_for(0, 14, 0) ||
155
+ rnp_version_commit_timestamp >= 1585833163)
156
+ callback :rnp_input_reader_t,
157
+ %i[pointer pointer size_t pointer],
158
+ :bool
159
+ else
160
+ callback :rnp_input_reader_t,
161
+ %i[pointer pointer size_t],
162
+ :ssize_t
163
+ end
164
+
13
165
 
14
166
  callback :rnp_get_key_cb,
15
167
  %i[pointer pointer string string bool],
@@ -17,9 +169,6 @@ module LibRnp
17
169
  callback :rnp_password_cb,
18
170
  %i[pointer pointer pointer string pointer size_t],
19
171
  :bool
20
- callback :rnp_input_reader_t,
21
- %i[pointer pointer size_t],
22
- :ssize_t
23
172
  callback :rnp_output_writer_t,
24
173
  %i[pointer pointer size_t],
25
174
  :bool
@@ -289,37 +438,6 @@ module LibRnp
289
438
  %i[pointer],
290
439
  :uint32
291
440
 
292
- # some newer APIs that may not be present
293
- {
294
- # key export
295
- rnp_key_export: [%i[pointer pointer uint32], :uint32],
296
- # enarmor/dearmor
297
- rnp_enarmor: [%i[pointer pointer pointer], :uint32],
298
- rnp_dearmor: [%i[pointer pointer], :uint32],
299
- # versioning
300
- rnp_version_string: [%i[], :string],
301
- rnp_version_string_full: [%i[], :string],
302
- rnp_version: [%i[], :uint32],
303
- rnp_version_for: [%i[uint32 uint32 uint32], :uint32],
304
- rnp_version_major: [%i[uint32], :uint32],
305
- rnp_version_minor: [%i[uint32], :uint32],
306
- rnp_version_patch: [%i[uint32], :uint32]
307
- }.each do |name, signature|
308
- present = ffi_libraries[0].find_function(name.to_s)
309
- if !present
310
- class_eval do
311
- define_singleton_method(name) do |*|
312
- raise Rnp::FeatureNotAvailableError, name
313
- end
314
- end
315
- else
316
- attach_function name, signature[0], signature[1]
317
- end
318
- class_eval do
319
- const_set("HAVE_#{name.upcase}", present)
320
- end
321
- end
322
-
323
441
  RNP_KEY_EXPORT_ARMORED = (1 << 0)
324
442
  RNP_KEY_EXPORT_PUBLIC = (1 << 1)
325
443
  RNP_KEY_EXPORT_SECRET = (1 << 2)
@@ -328,11 +446,21 @@ module LibRnp
328
446
  RNP_LOAD_SAVE_PUBLIC_KEYS = (1 << 0)
329
447
  RNP_LOAD_SAVE_SECRET_KEYS = (1 << 1)
330
448
 
449
+ RNP_KEY_UNLOAD_PUBLIC = (1 << 0)
450
+ RNP_KEY_UNLOAD_SECRET = (1 << 1)
451
+
452
+ RNP_KEY_REMOVE_PUBLIC = (1 << 0)
453
+ RNP_KEY_REMOVE_SECRET = (1 << 1)
454
+
331
455
  RNP_JSON_PUBLIC_MPIS = (1 << 0)
332
456
  RNP_JSON_SECRET_MPIS = (1 << 1)
333
457
  RNP_JSON_SIGNATURES = (1 << 2)
334
458
  RNP_JSON_SIGNATURE_MPIS = (1 << 3)
335
459
 
460
+ RNP_JSON_DUMP_MPI = (1 << 0)
461
+ RNP_JSON_DUMP_RAW = (1 << 1)
462
+ RNP_JSON_DUMP_GRIP = (1 << 2)
463
+
336
464
  RNP_SUCCESS = 0
337
465
  RNP_ERROR_BAD_FORMAT = 0x10000001
338
466
  RNP_ERROR_SIGNATURE_INVALID = 0x12000002
data/lib/rnp/input.rb CHANGED
@@ -9,6 +9,7 @@ require 'ffi'
9
9
  require 'rnp/error'
10
10
  require 'rnp/ffi/librnp'
11
11
  require 'rnp/utils'
12
+ require 'rnp/misc'
12
13
 
13
14
  class Rnp
14
15
  # Class used to feed data into RNP.
@@ -74,16 +75,35 @@ class Rnp
74
75
  end
75
76
 
76
77
  # @api private
77
- READER = lambda do |reader, _ctx, buf, buf_len|
78
- begin
79
- data = reader.call(buf_len)
80
- return 0 unless data
81
- raise Rnp::Error, 'Read exceeded buffer size' if data.size > buf_len
82
- buf.write_bytes(data)
83
- return data.size
84
- rescue
85
- puts $ERROR_INFO
86
- return -1
78
+ if Rnp.has?("input-reader-cb-no-ssize_t")
79
+ READER = lambda do |reader, _ctx, buf, buf_len, nread|
80
+ begin
81
+ data = reader.call(buf_len)
82
+ datasz = 0
83
+ if !data.nil?
84
+ datasz = data.size unless data.nil?
85
+ raise Rnp::Error, 'Read exceeded buffer size' if datasz > buf_len
86
+ buf.write_bytes(data) unless data.nil?
87
+ end
88
+ nread.write(:size_t, datasz)
89
+ return true
90
+ rescue
91
+ puts $ERROR_INFO
92
+ return false
93
+ end
94
+ end
95
+ else
96
+ READER = lambda do |reader, _ctx, buf, buf_len|
97
+ begin
98
+ data = reader.call(buf_len)
99
+ return 0 unless data
100
+ raise Rnp::Error, 'Read exceeded buffer size' if data.size > buf_len
101
+ buf.write_bytes(data)
102
+ return data.size
103
+ rescue
104
+ puts $ERROR_INFO
105
+ return -1
106
+ end
87
107
  end
88
108
  end
89
109
 
data/lib/rnp/key.rb CHANGED
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # (c) 2018 Ribose Inc.
3
+ # (c) 2018-2020 Ribose Inc.
4
4
 
5
5
  require 'ffi'
6
6
 
7
7
  require 'rnp/error'
8
8
  require 'rnp/ffi/librnp'
9
9
  require 'rnp/utils'
10
+ require 'rnp/userid'
10
11
 
11
12
  class Rnp
12
13
  # Class that represents a PGP key (potentially encompassing both the public
@@ -59,6 +60,13 @@ class Rnp
59
60
  string_property(:rnp_key_get_grip)
60
61
  end
61
62
 
63
+ # Get the primary grip of the key (for subkeys)
64
+ #
65
+ # @return [String]
66
+ def primary_grip
67
+ string_property(:rnp_key_get_primary_grip)
68
+ end
69
+
62
70
  # Get the primary userid of the key
63
71
  #
64
72
  # @return [String]
@@ -82,6 +90,38 @@ class Rnp
82
90
  each_userid.to_a
83
91
  end
84
92
 
93
+ # Enumerate each {UserID} for this key.
94
+ #
95
+ # @return [self, Enumerator]
96
+ def each_uid(&block)
97
+ block or return enum_for(:uid_iterator)
98
+ uid_iterator(&block)
99
+ self
100
+ end
101
+
102
+ # Get a list of {UserID}s for this key.
103
+ #
104
+ # @return [Array<UserID>]
105
+ def uids
106
+ each_uid.to_a
107
+ end
108
+
109
+ # Enumerate each {Signature} for this key.
110
+ #
111
+ # @return [self, Enumerator]
112
+ def each_signature(&block)
113
+ block or return enum_for(:signature_iterator)
114
+ signature_iterator(&block)
115
+ self
116
+ end
117
+
118
+ # Get a list of {Signature}s for this key.
119
+ #
120
+ # @return [Array<Signature>]
121
+ def signatures
122
+ each_signature.to_a
123
+ end
124
+
85
125
  # Add a userid to a key.
86
126
  #
87
127
  # @param userid [String] the userid to add
@@ -265,6 +305,134 @@ class Rnp
265
305
  end
266
306
  end
267
307
 
308
+ # Unload this key.
309
+ #
310
+ # @note When both the public and secret portions of this key have been
311
+ # unloaded, you should no longer interact with this object.
312
+ #
313
+ # @param unload_public [Boolean] if true then the public key will be
314
+ # unloaded
315
+ # @param unload_secret [Boolean] if true then the secret key will be
316
+ # unloaded
317
+ # @return [void]
318
+ def unload(unload_public: true, unload_secret: true)
319
+ flags = 0
320
+ flags |= LibRnp::RNP_KEY_REMOVE_PUBLIC if unload_public
321
+ flags |= LibRnp::RNP_KEY_REMOVE_SECRET if unload_secret
322
+ Rnp.call_ffi(:rnp_key_remove, @ptr, flags)
323
+ end
324
+
325
+ # Enumerate each subkey for this key.
326
+ #
327
+ # @return [self, Enumerator]
328
+ def each_subkey(&block)
329
+ block or return enum_for(:subkey_iterator)
330
+ subkey_iterator(&block)
331
+ self
332
+ end
333
+
334
+ # Get a list of all subkeys for this key.
335
+ #
336
+ # @return [Array<Key>]
337
+ def subkeys
338
+ each_subkey.to_a
339
+ end
340
+
341
+ # Get the type of this key (RSA, etc).
342
+ #
343
+ # @return [String]
344
+ def type
345
+ string_property(:rnp_key_get_alg)
346
+ end
347
+
348
+ # Get the bit length for this key.
349
+ #
350
+ # @return [Integer]
351
+ def bits
352
+ pbits = FFI::MemoryPointer.new(:uint32)
353
+ Rnp.call_ffi(:rnp_key_get_bits, @ptr, pbits)
354
+ pbits.read(:uint32)
355
+ end
356
+
357
+ # Get the bit length for the q parameter of this DSA key.
358
+ #
359
+ # @return [Integer]
360
+ def qbits
361
+ pbits = FFI::MemoryPointer.new(:uint32)
362
+ Rnp.call_ffi(:rnp_key_get_dsa_qbits, @ptr, pbits)
363
+ pbits.read(:uint32)
364
+ end
365
+
366
+ # Get the curve of this EC key.
367
+ #
368
+ # @return [String]
369
+ def curve
370
+ string_property(:rnp_key_get_curve)
371
+ end
372
+
373
+ # Query whether this key can be used to perform a certain operation.
374
+ #
375
+ # @param op [String,Symbol] the operation to query (sign, etc)
376
+ # @return [Boolean]
377
+ def can?(op)
378
+ pvalue = FFI::MemoryPointer.new(:bool)
379
+ Rnp.call_ffi(:rnp_key_allows_usage, @ptr, op.to_s, pvalue)
380
+ pvalue.read(:bool)
381
+ end
382
+
383
+ # Check if this has been revoked.
384
+ #
385
+ # @return [Boolean]
386
+ def revoked?
387
+ bool_property(:rnp_key_is_revoked)
388
+ end
389
+
390
+ # Check if this revoked key's material was compromised.
391
+ #
392
+ # @return [Boolean]
393
+ def compromised?
394
+ bool_property(:rnp_key_is_compromised)
395
+ end
396
+
397
+ # Check if this revoked key was retired.
398
+ #
399
+ # @return [Boolean]
400
+ def retired?
401
+ bool_property(:rnp_key_is_retired)
402
+ end
403
+
404
+ # Check if this revoked key was superseded by another key.
405
+ #
406
+ # @return [Boolean]
407
+ def superseded?
408
+ bool_property(:rnp_key_is_superseded)
409
+ end
410
+
411
+ # Retrieve the reason for revoking this key, if any.
412
+ #
413
+ # @return [String]
414
+ def revocation_reason
415
+ string_property(:rnp_key_get_revocation_reason)
416
+ end
417
+
418
+ # Retrieve the creation time of the key
419
+ #
420
+ # @return [Time]
421
+ def creation_time
422
+ ptime = FFI::MemoryPointer.new(:uint32)
423
+ Rnp.call_ffi(:rnp_key_get_creation, @ptr, ptime)
424
+ Time.at(ptime.read(:uint32))
425
+ end
426
+
427
+ # Retrieve the expiration time of the key
428
+ #
429
+ # @return [Time]
430
+ def expiration_time
431
+ ptime = FFI::MemoryPointer.new(:uint32)
432
+ Rnp.call_ffi(:rnp_key_get_expiration, @ptr, ptime)
433
+ Time.at(ptime.read(:uint32))
434
+ end
435
+
268
436
  private
269
437
 
270
438
  def string_property(func)
@@ -313,6 +481,40 @@ class Rnp
313
481
  end
314
482
  end
315
483
 
484
+ def uid_iterator
485
+ pcount = FFI::MemoryPointer.new(:size_t)
486
+ Rnp.call_ffi(:rnp_key_get_uid_count, @ptr, pcount)
487
+ count = pcount.read(:size_t)
488
+ pptr = FFI::MemoryPointer.new(:pointer)
489
+ (0...count).each do |i|
490
+ Rnp.call_ffi(:rnp_key_get_uid_handle_at, @ptr, i, pptr)
491
+ begin
492
+ phandle = pptr.read_pointer
493
+ puserid = nil
494
+ next if phandle.nil?
495
+ Rnp.call_ffi(:rnp_key_get_uid_at, @ptr, i, pptr)
496
+ puserid = pptr.read_pointer
497
+ yield UserID.new(phandle, puserid.read_string) unless puserid.null?
498
+ phandle = nil
499
+ ensure
500
+ LibRnp.rnp_uid_handle_destroy(phandle)
501
+ LibRnp.rnp_buffer_destroy(puserid)
502
+ end
503
+ end
504
+ end
505
+
506
+ def signature_iterator
507
+ pcount = FFI::MemoryPointer.new(:size_t)
508
+ Rnp.call_ffi(:rnp_key_get_signature_count, @ptr, pcount)
509
+ count = pcount.read(:size_t)
510
+ pptr = FFI::MemoryPointer.new(:pointer)
511
+ (0...count).each do |i|
512
+ Rnp.call_ffi(:rnp_key_get_signature_at, @ptr, i, pptr)
513
+ psig = pptr.read_pointer
514
+ yield Signature.new(psig) unless psig.null?
515
+ end
516
+ end
517
+
316
518
  def export(public_key: false, secret_key: false, with_subkeys: false, armored: true, output: nil)
317
519
  flags = 0
318
520
  flags |= LibRnp::RNP_KEY_EXPORT_ARMORED if armored
@@ -321,6 +523,20 @@ class Rnp
321
523
  flags |= LibRnp::RNP_KEY_EXPORT_SUBKEYS if with_subkeys
322
524
  Rnp.call_ffi(:rnp_key_export, @ptr, output.ptr, flags)
323
525
  end
526
+
527
+ def subkey_iterator
528
+ pcount = FFI::MemoryPointer.new(:size_t)
529
+ Rnp.call_ffi(:rnp_key_get_subkey_count, @ptr, pcount)
530
+ count = pcount.read(:size_t)
531
+ pptr = FFI::MemoryPointer.new(:pointer)
532
+ (0...count).each do |i|
533
+ Rnp.call_ffi(:rnp_key_get_subkey_at, @ptr, i, pptr)
534
+ begin
535
+ psubkey = pptr.read_pointer
536
+ yield Rnp::Key.new(psubkey) unless psubkey.null?
537
+ end
538
+ end
539
+ end
324
540
  end # class
325
541
  end # class
326
542