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.
data/lib/rnp/rnp.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # (c) 2018 Ribose Inc.
3
+ # (c) 2018,2019 Ribose Inc.
4
4
 
5
5
  require 'English'
6
6
  require 'json'
@@ -14,6 +14,7 @@ require 'rnp/key'
14
14
  require 'rnp/op/sign'
15
15
  require 'rnp/op/verify'
16
16
  require 'rnp/op/encrypt'
17
+ require "rnp/op/generate"
17
18
 
18
19
  # Class used for interacting with RNP.
19
20
  class Rnp
@@ -128,6 +129,89 @@ class Rnp
128
129
  end
129
130
  end
130
131
 
132
+ # Generate an RSA key (w/optional subkey).
133
+ #
134
+ # @param userid [String] the userid for the key
135
+ # @param bits [Integer] the bit length for the primary key
136
+ # @param subbits [Integer] the bit length for the subkey
137
+ # (0 if no subkey should be generated)
138
+ # @param password [String] the password to protect the key(s)
139
+ # (nil for no protection)
140
+ def generate_rsa(userid:, bits:, subbits: 0, password:)
141
+ pptr = FFI::MemoryPointer.new(:pointer)
142
+ Rnp.call_ffi(:rnp_generate_key_rsa, @ptr, bits, subbits, userid, password,
143
+ pptr)
144
+ pkey = pptr.read_pointer
145
+ Key.new(pkey) unless pkey.null?
146
+ end
147
+
148
+ # Generate a DSA (w/optional ElGamal subkey) key.
149
+ #
150
+ # @param userid [String] the userid for the key
151
+ # @param bits [Integer] the bit length for the primary key
152
+ # @param subbits [Integer] the bit length for the subkey
153
+ # (0 if no subkey should be generated)
154
+ # @param password [String] the password to protect the key(s)
155
+ # (nil for no protection)
156
+ def generate_dsa_elgamal(userid:, bits:, subbits: 0, password:)
157
+ pptr = FFI::MemoryPointer.new(:pointer)
158
+ Rnp.call_ffi(:rnp_generate_key_dsa_eg, @ptr, bits, subbits, userid,
159
+ password, pptr)
160
+ pkey = pptr.read_pointer
161
+ Key.new(pkey) unless pkey.null?
162
+ end
163
+
164
+ # Generate an ECDSA+ECDH key pair.
165
+ #
166
+ # @param userid [String] the userid for the key
167
+ # @param curve [String] the name of the curve
168
+ # @param password [String] the password to protect the key(s)
169
+ # (nil for no protection)
170
+ def generate_ecdsa_ecdh(userid:, curve:, password:)
171
+ pptr = FFI::MemoryPointer.new(:pointer)
172
+ Rnp.call_ffi(:rnp_generate_key_ec, @ptr, curve, userid, password, pptr)
173
+ pkey = pptr.read_pointer
174
+ Key.new(pkey) unless pkey.null?
175
+ end
176
+
177
+ # Generate an EdDSA+x25519 key pair.
178
+ #
179
+ # @param userid [String] the userid for the key
180
+ # @param password [String] the password to protect the key(s)
181
+ # (nil for no protection)
182
+ def generate_eddsa_25519(userid:, password:)
183
+ pptr = FFI::MemoryPointer.new(:pointer)
184
+ Rnp.call_ffi(:rnp_generate_key_25519, @ptr, userid, password, pptr)
185
+ pkey = pptr.read_pointer
186
+ Key.new(pkey) unless pkey.null?
187
+ end
188
+
189
+ # Generate an SM2 key pair.
190
+ #
191
+ # @param userid [String] the userid for the key
192
+ # @param password [String] the password to protect the key(s)
193
+ # (nil for no protection)
194
+ def generate_sm2(userid:, password:)
195
+ pptr = FFI::MemoryPointer.new(:pointer)
196
+ Rnp.call_ffi(:rnp_generate_key_sm2, @ptr, userid, password, pptr)
197
+ pkey = pptr.read_pointer
198
+ Key.new(pkey) unless pkey.null?
199
+ end
200
+
201
+ # Generate a key and optional subkey.
202
+ #
203
+ # @param userid [String] the userid for the key
204
+ # @param password [String] the password to protect the key(s)
205
+ # (nil for no protection)
206
+ def generate(type:, userid:, bits:, curve: nil, password:,
207
+ subtype: nil, subbits: 0, subcurve: nil)
208
+ pptr = FFI::MemoryPointer.new(:pointer)
209
+ Rnp.call_ffi(:rnp_generate_key_ex, @ptr, type, subtype, bits, subbits,
210
+ curve, subcurve, userid, password, pptr)
211
+ pkey = pptr.read_pointer
212
+ Key.new(pkey) unless pkey.null?
213
+ end
214
+
131
215
  # Load keys.
132
216
  #
133
217
  # @param format [String] the format of the keys to load (GPG, KBX, G10).
@@ -141,6 +225,13 @@ class Rnp
141
225
  Rnp.call_ffi(:rnp_load_keys, @ptr, format, input.ptr, flags)
142
226
  end
143
227
 
228
+ def unload_keys(public_keys: true, secret_keys: true)
229
+ raise ArgumentError, "At least one of public_keys or secret_keys must be true" \
230
+ if !public_keys && !secret_keys
231
+ flags = unload_keys_flags(public_keys: public_keys, secret_keys: secret_keys)
232
+ Rnp.call_ffi(:rnp_unload_keys, @ptr, flags)
233
+ end
234
+
144
235
  # Save keys.
145
236
  #
146
237
  # @param format [String] the format to save the keys in (GPG, KBX, G10).
@@ -349,16 +440,19 @@ class Rnp
349
440
  # @param armored (see Encrypt#armored=)
350
441
  # @param compression (see Encrypt#compression=)
351
442
  # @param cipher (see Encrypt#cipher=)
443
+ # @param aead (see Encrypt#aead=)
352
444
  def encrypt(input:, output: nil, recipients:,
353
445
  armored: nil,
354
446
  compression: nil,
355
- cipher: nil)
447
+ cipher: nil,
448
+ aead: nil)
356
449
  Output.default(output) do |output_|
357
450
  enc = start_encrypt(input: input, output: output_)
358
451
  enc.options = {
359
452
  armored: armored,
360
453
  compression: compression,
361
- cipher: cipher
454
+ cipher: cipher,
455
+ aead: aead,
362
456
  }
363
457
  simple_encrypt(enc, recipients: recipients)
364
458
  end
@@ -373,6 +467,7 @@ class Rnp
373
467
  # @param armored (see Encrypt#armored=)
374
468
  # @param compression (see Encrypt#compression=)
375
469
  # @param cipher (see Encrypt#cipher=)
470
+ # @param aead (see Encrypt#aead=)
376
471
  # @param hash (see Encrypt#hash=)
377
472
  # @param creation_time (see Encrypt#creation_time=)
378
473
  # @param expiration_time (see Encrypt#expiration_time=)
@@ -380,6 +475,7 @@ class Rnp
380
475
  armored: nil,
381
476
  compression: nil,
382
477
  cipher: nil,
478
+ aead: nil,
383
479
  hash: nil,
384
480
  creation_time: nil,
385
481
  expiration_time: nil)
@@ -389,6 +485,7 @@ class Rnp
389
485
  armored: armored,
390
486
  compression: compression,
391
487
  cipher: cipher,
488
+ aead: aead,
392
489
  hash: hash,
393
490
  creation_time: creation_time,
394
491
  expiration_time: expiration_time
@@ -406,6 +503,7 @@ class Rnp
406
503
  # @param armored (see Encrypt#armored=)
407
504
  # @param compression (see Encrypt#compression=)
408
505
  # @param cipher (see Encrypt#cipher=)
506
+ # @param aead (see Encrypt#aead=)
409
507
  # @param s2k_hash (see Encrypt#add_password)
410
508
  # @param s2k_iterations (see Encrypt#add_password)
411
509
  # @param s2k_cipher (see Encrypt#add_password)
@@ -414,6 +512,7 @@ class Rnp
414
512
  armored: nil,
415
513
  compression: nil,
416
514
  cipher: nil,
515
+ aead: nil,
417
516
  s2k_hash: nil,
418
517
  s2k_iterations: 0,
419
518
  s2k_cipher: nil)
@@ -422,7 +521,8 @@ class Rnp
422
521
  enc.options = {
423
522
  armored: armored,
424
523
  compression: compression,
425
- cipher: cipher
524
+ cipher: cipher,
525
+ aead: aead,
426
526
  }
427
527
  passwords = [passwords] if passwords.is_a?(String)
428
528
  passwords.each do |password|
@@ -447,6 +547,30 @@ class Rnp
447
547
  end
448
548
  end
449
549
 
550
+ # Start a {Generate} operation.
551
+ #
552
+ # @param type [String, Symbol] the key type to generate (RSA, DSA, etc)
553
+ # @return [Generate]
554
+ def start_generate(type:)
555
+ pptr = FFI::MemoryPointer.new(:pointer)
556
+ Rnp.call_ffi(:rnp_op_generate_create, pptr, @ptr, type.to_s)
557
+ pgen = pptr.read_pointer
558
+ Generate.new(pgen) unless pgen.null?
559
+ end
560
+
561
+ # Start a {Generate} operation.
562
+ #
563
+ # @param primary [Key] the primary key for which to generate a subkey
564
+ # @param type [String, Symbol] the key type to generate (RSA, DSA, etc)
565
+ # @return [Generate]
566
+ def start_generate_subkey(primary:, type:)
567
+ pptr = FFI::MemoryPointer.new(:pointer)
568
+ Rnp.call_ffi(:rnp_op_generate_subkey_create, pptr, @ptr, primary.ptr,
569
+ type.to_s)
570
+ pgen = pptr.read_pointer
571
+ Generate.new(pgen) unless pgen.null?
572
+ end
573
+
450
574
  # Create a {Sign} operation.
451
575
  #
452
576
  # @param input [Input] the input to read the data to be signed
@@ -499,6 +623,41 @@ class Rnp
499
623
  Encrypt.new(pencrypt) unless pencrypt.null?
500
624
  end
501
625
 
626
+ # Import keys
627
+ #
628
+ # @param input [Input] the input to read the (OpenPGP-format) keys from
629
+ # @param public_keys [Boolean] whether to load public keys
630
+ # @param secret_keys [Boolean] whether to load secret keys
631
+ # @return [Hash] information on the imported keys
632
+ def import_keys(input:, public_keys: true, secret_keys: true)
633
+ flags = 0
634
+ flags |= LibRnp::RNP_LOAD_SAVE_PUBLIC_KEYS if public_keys
635
+ flags |= LibRnp::RNP_LOAD_SAVE_SECRET_KEYS if secret_keys
636
+ pptr = FFI::MemoryPointer.new(:pointer)
637
+ Rnp.call_ffi(:rnp_import_keys, @ptr, input.ptr, flags, pptr)
638
+ begin
639
+ presults = pptr.read_pointer
640
+ JSON.parse(presults.read_string) unless pptr.null?
641
+ ensure
642
+ LibRnp.rnp_buffer_destroy(presults)
643
+ end
644
+ end
645
+
646
+ # Import signatures
647
+ #
648
+ # @param input [Input] the input to read the (OpenPGP-format) keys from
649
+ # @return [Hash] information on the imported keys
650
+ def import_signatures(input:)
651
+ pptr = FFI::MemoryPointer.new(:pointer)
652
+ Rnp.call_ffi(:rnp_import_signatures, @ptr, input.ptr, 0, pptr)
653
+ begin
654
+ presults = pptr.read_pointer
655
+ JSON.parse(presults.read_string) unless pptr.null?
656
+ ensure
657
+ LibRnp.rnp_buffer_destroy(presults)
658
+ end
659
+ end
660
+
502
661
  private
503
662
 
504
663
  KEY_PROVIDER = lambda do |provider, _rnp, _ctx, identifier_type, identifier, secret|
@@ -584,5 +743,12 @@ class Rnp
584
743
  flags |= LibRnp::RNP_LOAD_SAVE_SECRET_KEYS if secret_keys
585
744
  flags
586
745
  end
746
+
747
+ def unload_keys_flags(public_keys:, secret_keys:)
748
+ flags = 0
749
+ flags |= LibRnp::RNP_KEY_UNLOAD_PUBLIC if public_keys
750
+ flags |= LibRnp::RNP_KEY_UNLOAD_SECRET if secret_keys
751
+ flags
752
+ end
587
753
  end # class
588
754
 
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ # (c) 2020 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
+ # Class that represents a signature.
13
+ class Signature
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_signature_handle_destroy(ptr)
26
+ end
27
+
28
+ def inspect
29
+ Rnp.inspect_ptr(self)
30
+ end
31
+
32
+ # The type of the signing key (RSA, etc).
33
+ #
34
+ # @return [String]
35
+ def type
36
+ string_property(:rnp_signature_get_alg)
37
+ end
38
+
39
+ # The hash algorithm used in the signature.
40
+ #
41
+ # @return [String]
42
+ def hash
43
+ string_property(:rnp_signature_get_hash_alg)
44
+ end
45
+
46
+ # The signer's key id.
47
+ #
48
+ # @return [String]
49
+ def keyid
50
+ string_property(:rnp_signature_get_keyid)
51
+ end
52
+
53
+ # The time this signature was created at.
54
+ #
55
+ # @return [Time]
56
+ def creation_time
57
+ pcreation = FFI::MemoryPointer.new(:uint32)
58
+ Rnp.call_ffi(:rnp_signature_get_creation, @ptr, pcreation)
59
+ Time.at(pcreation.read(:uint32))
60
+ end
61
+
62
+ # The signer's key.
63
+ #
64
+ # @return [Key]
65
+ def signer
66
+ pptr = FFI::MemoryPointer.new(:pointer)
67
+ Rnp.call_ffi(:rnp_signature_get_signer, @ptr, pptr)
68
+ pkey = pptr.read_pointer
69
+ Key.new(pkey) unless pkey.null?
70
+ end
71
+
72
+ # JSON representation of this signature (as a Hash).
73
+ #
74
+ # @param mpi [Boolean] if true then MPIs will be included
75
+ # @param raw [Boolean] if true then raw data will be included
76
+ # @param grip [Boolean] if true then grips will be included
77
+ # @return [Hash]
78
+ def json(mpi: false, raw: false, grip: false)
79
+ flags = 0
80
+ flags |= LibRnp::RNP_JSON_DUMP_MPI if mpi
81
+ flags |= LibRnp::RNP_JSON_DUMP_RAW if raw
82
+ flags |= LibRnp::RNP_JSON_DUMP_GRIP if grip
83
+ pptr = FFI::MemoryPointer.new(:pointer)
84
+ Rnp.call_ffi(:rnp_signature_packet_to_json, @ptr, flags, pptr)
85
+ begin
86
+ pvalue = pptr.read_pointer
87
+ JSON.parse(pvalue.read_string) unless pvalue.null?
88
+ ensure
89
+ LibRnp.rnp_buffer_destroy(pvalue)
90
+ end
91
+ end
92
+
93
+ private
94
+
95
+ def string_property(func)
96
+ pptr = FFI::MemoryPointer.new(:pointer)
97
+ Rnp.call_ffi(func, @ptr, pptr)
98
+ begin
99
+ pvalue = pptr.read_pointer
100
+ pvalue.read_string unless pvalue.null?
101
+ ensure
102
+ LibRnp.rnp_buffer_destroy(pvalue)
103
+ end
104
+ end
105
+ end
106
+ end
data/lib/rnp/userid.rb ADDED
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ # (c) 2020 Ribose Inc.
4
+
5
+ require 'ffi'
6
+
7
+ require 'rnp/error'
8
+ require 'rnp/ffi/librnp'
9
+ require 'rnp/utils'
10
+ require 'rnp/signature'
11
+
12
+ class Rnp
13
+ # Class that represents a UserID
14
+ class UserID
15
+ # @api private
16
+ attr_reader :ptr
17
+
18
+ # @api private
19
+ def initialize(ptr, userid)
20
+ raise Rnp::Error, 'NULL pointer' if ptr.null?
21
+ @ptr = FFI::AutoPointer.new(ptr, self.class.method(:destroy))
22
+ @userid = userid
23
+ end
24
+
25
+ # @api private
26
+ def self.destroy(ptr)
27
+ LibRnp.rnp_uid_handle_destroy(ptr)
28
+ end
29
+
30
+ def inspect
31
+ Rnp.inspect_ptr(self)
32
+ end
33
+
34
+ def to_s
35
+ @userid
36
+ end
37
+
38
+ # Enumerate each {Signature} for this key.
39
+ #
40
+ # @return [self, Enumerator]
41
+ def each_signature(&block)
42
+ block or return enum_for(:signature_iterator)
43
+ signature_iterator(&block)
44
+ self
45
+ end
46
+
47
+ # Get a list of all {Signature}s for this key.
48
+ #
49
+ # @return [Array<Signature>]
50
+ def signatures
51
+ each_signature.to_a
52
+ end
53
+
54
+ # Check if this key is revoked.
55
+ #
56
+ # @return [Boolean]
57
+ def revoked?
58
+ presult = FFI::MemoryPointer.new(:bool)
59
+ Rnp.call_ffi(:rnp_uid_is_revoked, @ptr, presult)
60
+ presult.read(:bool)
61
+ end
62
+
63
+ private
64
+
65
+ def signature_iterator
66
+ pcount = FFI::MemoryPointer.new(:size_t)
67
+ Rnp.call_ffi(:rnp_uid_get_signature_count, @ptr, pcount)
68
+ count = pcount.read(:size_t)
69
+ (0...count).each do |i|
70
+ pptr = FFI::MemoryPointer.new(:pointer)
71
+ Rnp.call_ffi(:rnp_uid_get_signature_at, @ptr, i, pptr)
72
+ psig = pptr.read_pointer
73
+ yield Signature.new(psig) unless psig.null?
74
+ end
75
+ end
76
+ end
77
+ end
data/lib/rnp/version.rb CHANGED
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # (c) 2018 Ribose Inc.
3
+ # (c) 2018-2023 Ribose Inc.
4
4
 
5
5
  class Rnp
6
- VERSION = '1.0.4'
6
+ VERSION = "1.0.5"
7
7
  end # class
8
-
data/lib/rnp.rb CHANGED
@@ -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 'rnp/error'
6
6
  require 'rnp/input'
@@ -10,5 +10,7 @@ require 'rnp/op/sign'
10
10
  require 'rnp/op/verify'
11
11
  require 'rnp/output'
12
12
  require 'rnp/rnp'
13
+ require 'rnp/signature'
14
+ require 'rnp/userid'
13
15
  require 'rnp/version'
14
16
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rnp
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-09-16 00:00:00.000000000 Z
11
+ date: 2023-03-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: asciidoctor
@@ -16,28 +16,34 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.5'
19
+ version: '2.0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.5'
26
+ version: '2.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '1.14'
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '3'
34
37
  type: :development
35
38
  prerelease: false
36
39
  version_requirements: !ruby/object:Gem::Requirement
37
40
  requirements:
38
- - - "~>"
41
+ - - ">="
39
42
  - !ruby/object:Gem::Version
40
43
  version: '1.14'
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '3'
41
47
  - !ruby/object:Gem::Dependency
42
48
  name: codecov
43
49
  requirement: !ruby/object:Gem::Requirement
@@ -56,16 +62,22 @@ dependencies:
56
62
  name: rake
57
63
  requirement: !ruby/object:Gem::Requirement
58
64
  requirements:
59
- - - "~>"
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '10'
68
+ - - "<"
60
69
  - !ruby/object:Gem::Version
61
- version: '10.0'
70
+ version: '14'
62
71
  type: :development
63
72
  prerelease: false
64
73
  version_requirements: !ruby/object:Gem::Requirement
65
74
  requirements:
66
- - - "~>"
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '10'
78
+ - - "<"
67
79
  - !ruby/object:Gem::Version
68
- version: '10.0'
80
+ version: '14'
69
81
  - !ruby/object:Gem::Dependency
70
82
  name: redcarpet
71
83
  requirement: !ruby/object:Gem::Requirement
@@ -100,14 +112,14 @@ dependencies:
100
112
  requirements:
101
113
  - - "~>"
102
114
  - !ruby/object:Gem::Version
103
- version: 0.55.0
115
+ version: 0.75.0
104
116
  type: :development
105
117
  prerelease: false
106
118
  version_requirements: !ruby/object:Gem::Requirement
107
119
  requirements:
108
120
  - - "~>"
109
121
  - !ruby/object:Gem::Version
110
- version: 0.55.0
122
+ version: 0.75.0
111
123
  - !ruby/object:Gem::Dependency
112
124
  name: simplecov
113
125
  requirement: !ruby/object:Gem::Requirement
@@ -170,10 +182,13 @@ files:
170
182
  - lib/rnp/key.rb
171
183
  - lib/rnp/misc.rb
172
184
  - lib/rnp/op/encrypt.rb
185
+ - lib/rnp/op/generate.rb
173
186
  - lib/rnp/op/sign.rb
174
187
  - lib/rnp/op/verify.rb
175
188
  - lib/rnp/output.rb
176
189
  - lib/rnp/rnp.rb
190
+ - lib/rnp/signature.rb
191
+ - lib/rnp/userid.rb
177
192
  - lib/rnp/utils.rb
178
193
  - lib/rnp/version.rb
179
194
  homepage: https://www.ribose.com
@@ -189,15 +204,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
189
204
  requirements:
190
205
  - - ">="
191
206
  - !ruby/object:Gem::Version
192
- version: 2.3.0
207
+ version: 2.7.0
193
208
  required_rubygems_version: !ruby/object:Gem::Requirement
194
209
  requirements:
195
210
  - - ">="
196
211
  - !ruby/object:Gem::Version
197
212
  version: '0'
198
213
  requirements: []
199
- rubyforge_project:
200
- rubygems_version: 2.7.7
214
+ rubygems_version: 3.4.6
201
215
  signing_key:
202
216
  specification_version: 4
203
217
  summary: Ruby bindings for the rnp OpenPGP library