rnp 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
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