rnp 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6e6b754a124b29c8d9d1295ec0266d0ae8d8e435ba7b5d4680b693ad510304eb
4
- data.tar.gz: 8c78644be51833b9c033b6d53da65f4a462b5d659a0895428da5a018b50900cc
3
+ metadata.gz: ebe16f6b1b161add02098e6bdb70f5401026a0cb31c695c44943d3432b0a1e85
4
+ data.tar.gz: 10a352991a433807dcd8484931bfc3d6745c86202d23d22b1cc8aa09dd9e848a
5
5
  SHA512:
6
- metadata.gz: d871d7dc55944ab5d0415b057ec02fbc3eadee97dd9d27f5c080e1eb675aa3b4fd82975b30ec9113c3267094b2c8db23a567434a538c2bcb2f700a4caea59558
7
- data.tar.gz: 6cabf921b333256316531dcc98032cf2b8a878ef9fdaefdb678161e0faf1b68106669ce3e77c689c272ede779f847146db4f47d86fbe601f55f9e6174122f3e3
6
+ metadata.gz: f5bace27a94afc5fa1c39e25e4c52f020ff3256279f420bf9f0e56010799e10200202afaaebb4fec054e7c7f327e09eac0a10212c08ef4acf0c0251c589873db
7
+ data.tar.gz: 9cbdb7ec9bc7f6db9ee0ad237fc66b37aae56712aff023cee2c508b502ccec86027867e39dd8e6b134160a94178c56b0e244f9cfdfbae67434c7f075b15ad5cd
data/CHANGELOG.adoc CHANGED
@@ -1,5 +1,9 @@
1
1
  == Changelog
2
2
 
3
+ === 1.0.5 [20-03-2023]
4
+ * Rub GHA tests for Ruby 2.7, 3.0, 3.1, 3.2, head
5
+ and different rnp versions
6
+
3
7
  === 1.0.4 [09-16-2018]
4
8
  * Add support for rnp 0.11.0+. Tests will now only pass
5
9
  with this version.
@@ -17,4 +21,3 @@
17
21
 
18
22
  === 1.0.0 [05-01-2018]
19
23
  * Completely rewritten for RNP's new FFI.
20
-
data/README.adoc CHANGED
@@ -1,12 +1,16 @@
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/tests.yml/badge.svg["Build Status", link="https://github.com/rnpgp/ruby-rnp/actions/workflows/tests.yml"]
4
+ image:https://codecov.io/github/rnpgp/ruby-rnp/coverage.svg["Code Coverage", link="https://codecov.io/github/rnpgp/ruby-rnp?branch=master"]
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/master/[RubyDoc].
10
+
11
+ == Requirements
12
+
13
+ This gem currently requires https://github.com/rnpgp/rnp[librnp] 0.10.0 or newer.
10
14
 
11
15
  == Installation
12
16
 
@@ -28,4 +32,3 @@ Or install it yourself as:
28
32
  ----
29
33
  gem install rnp
30
34
  ----
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,156 @@ 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
+ # key uids
113
+ rnp_key_get_uid_handle_at: [%i[pointer size_t pointer], :uint32],
114
+ rnp_uid_is_revoked: [%i[pointer pointer], :uint32],
115
+ rnp_uid_handle_destroy: [%i[pointer], :uint32],
116
+ rnp_uid_get_signature_count: [%i[pointer pointer], :uint32],
117
+ rnp_uid_get_signature_at: [%i[pointer size_t pointer], :uint32],
118
+ # key properties
119
+ rnp_key_get_creation: [%i[pointer pointer], :uint32],
120
+ rnp_key_get_expiration: [%i[pointer pointer], :uint32],
121
+ rnp_key_get_primary_grip: [%i[pointer pointer], :uint32],
122
+ # output
123
+ rnp_output_write: [%i[pointer pointer size_t pointer], :uint32],
124
+ # import
125
+ rnp_import_keys: [%i[pointer pointer uint32 pointer], :uint32],
126
+ rnp_import_signatures: [%i[pointer pointer uint32 pointer], :uint32],
127
+ }.each do |name, signature|
128
+ present = !ffi_libraries[0].find_function(name.to_s).nil?
129
+ if !present
130
+ class_eval do
131
+ define_singleton_method(name) do |*|
132
+ raise Rnp::FeatureNotAvailableError, name
133
+ end
134
+ end
135
+ else
136
+ attach_function name, signature[0], signature[1]
137
+ end
138
+ class_eval do
139
+ const_set("HAVE_#{name.upcase}", present)
140
+ end
141
+ end
142
+
143
+ if ffi_libraries[0].find_function('rnp_version_commit_timestamp')
144
+ attach_function :rnp_version_commit_timestamp, [], :uint64
145
+ else
146
+ def self.rnp_version_commit_timestamp
147
+ 0
148
+ end
149
+ end
150
+
151
+ if HAVE_RNP_VERSION && (rnp_version >= rnp_version_for(0, 14, 0) ||
152
+ rnp_version_commit_timestamp >= 1585833163)
153
+ callback :rnp_input_reader_t,
154
+ %i[pointer pointer size_t pointer],
155
+ :bool
156
+ else
157
+ callback :rnp_input_reader_t,
158
+ %i[pointer pointer size_t],
159
+ :ssize_t
160
+ end
161
+
13
162
 
14
163
  callback :rnp_get_key_cb,
15
164
  %i[pointer pointer string string bool],
@@ -17,9 +166,6 @@ module LibRnp
17
166
  callback :rnp_password_cb,
18
167
  %i[pointer pointer pointer string pointer size_t],
19
168
  :bool
20
- callback :rnp_input_reader_t,
21
- %i[pointer pointer size_t],
22
- :ssize_t
23
169
  callback :rnp_output_writer_t,
24
170
  %i[pointer pointer size_t],
25
171
  :bool
@@ -289,37 +435,6 @@ module LibRnp
289
435
  %i[pointer],
290
436
  :uint32
291
437
 
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
438
  RNP_KEY_EXPORT_ARMORED = (1 << 0)
324
439
  RNP_KEY_EXPORT_PUBLIC = (1 << 1)
325
440
  RNP_KEY_EXPORT_SECRET = (1 << 2)
@@ -328,11 +443,21 @@ module LibRnp
328
443
  RNP_LOAD_SAVE_PUBLIC_KEYS = (1 << 0)
329
444
  RNP_LOAD_SAVE_SECRET_KEYS = (1 << 1)
330
445
 
446
+ RNP_KEY_UNLOAD_PUBLIC = (1 << 0)
447
+ RNP_KEY_UNLOAD_SECRET = (1 << 1)
448
+
449
+ RNP_KEY_REMOVE_PUBLIC = (1 << 0)
450
+ RNP_KEY_REMOVE_SECRET = (1 << 1)
451
+
331
452
  RNP_JSON_PUBLIC_MPIS = (1 << 0)
332
453
  RNP_JSON_SECRET_MPIS = (1 << 1)
333
454
  RNP_JSON_SIGNATURES = (1 << 2)
334
455
  RNP_JSON_SIGNATURE_MPIS = (1 << 3)
335
456
 
457
+ RNP_JSON_DUMP_MPI = (1 << 0)
458
+ RNP_JSON_DUMP_RAW = (1 << 1)
459
+ RNP_JSON_DUMP_GRIP = (1 << 2)
460
+
336
461
  RNP_SUCCESS = 0
337
462
  RNP_ERROR_BAD_FORMAT = 0x10000001
338
463
  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