Class: Rnp

Inherits:
Object
  • Object
show all
Defined in:
lib/rnp/rnp.rb,
lib/rnp/key.rb,
lib/rnp/misc.rb,
lib/rnp/error.rb,
lib/rnp/input.rb,
lib/rnp/utils.rb,
lib/rnp/output.rb,
lib/rnp/userid.rb,
lib/rnp/op/sign.rb,
lib/rnp/version.rb,
lib/rnp/op/verify.rb,
lib/rnp/signature.rb,
lib/rnp/op/encrypt.rb,
lib/rnp/op/generate.rb

Overview

© 2018-2023 Ribose Inc.

Defined Under Namespace

Classes: BadFormatError, BadPasswordError, Encrypt, Error, FeatureNotAvailableError, Generate, Input, InvalidSignatureError, Key, NoSuitableKeyError, Output, Sign, Signature, UserID, Verify

Constant Summary collapse

VERSION =
"1.0.5"

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(pubfmt = 'GPG', secfmt = 'GPG') ⇒ Rnp

Create a new interface to RNP.

Parameters:

  • pubfmt (String) (defaults to: 'GPG')

    the public keyring format

  • secfmt (String) (defaults to: 'GPG')

    the secret keyring format



28
29
30
31
32
33
34
# File 'lib/rnp/rnp.rb', line 28

def initialize(pubfmt = 'GPG', secfmt = 'GPG')
  pptr = FFI::MemoryPointer.new(:pointer)
  Rnp.call_ffi(:rnp_ffi_create, pptr, pubfmt, secfmt)
  @ptr = FFI::AutoPointer.new(pptr.read_pointer, self.class.method(:destroy))
  @key_provider = nil
  @password_provider = nil
end

Class Method Details

.commit_timeInteger

Retrieve the commit time of the latest commit.

This will return 0 for release/non-master builds.

Returns:

  • (Integer)


165
166
167
# File 'lib/rnp/misc.rb', line 165

def self.commit_time
  LibRnp.rnp_version_commit_timestamp
end

.dearmor(input:, output: nil) ⇒ nil, String

Remove ASCII Armor from data.

Parameters:

  • input (Input)

    the input to read the ASCII-Armored data from

  • output (Output) (defaults to: nil)

    the output to write the dearmored data to. If nil, the result will be returned directly as a String.

Returns:

  • (nil, String)


98
99
100
101
102
# File 'lib/rnp/misc.rb', line 98

def self.dearmor(input:, output: nil)
  Output.default(output) do |output_|
    Rnp.call_ffi(:rnp_dearmor, input.ptr, output_.ptr)
  end
end

.default_homedirString

Get the default homedir for RNP.

Returns:

  • (String)


15
16
17
18
19
20
21
22
23
24
# File 'lib/rnp/misc.rb', line 15

def self.default_homedir
  pptr = FFI::MemoryPointer.new(:pointer)
  Rnp.call_ffi(:rnp_get_default_homedir, pptr)
  begin
    phomedir = pptr.read_pointer
    phomedir.read_string unless phomedir.null?
  ensure
    LibRnp.rnp_buffer_destroy(phomedir)
  end
end

.disable_debugvoid

This method returns an undefined value.

Disable previously-enabled debugging



213
214
215
# File 'lib/rnp/misc.rb', line 213

def self.disable_debug
  Rnp.call_ffi(:rnp_disable_debug)
end

.enable_debug(file = nil) ⇒ void

This method returns an undefined value.

Enable debugging

Parameters:

  • file (String) (defaults to: nil)

    the file to enable debugging for (or nil for all)



206
207
208
# File 'lib/rnp/misc.rb', line 206

def self.enable_debug(file = nil)
  Rnp.call_ffi(:rnp_enable_debug, file)
end

.enarmor(input:, output: nil, type: nil) ⇒ nil, String

Add ASCII Armor to data.

Parameters:

  • input (Input)

    the input to read data from

  • output (Output) (defaults to: nil)

    the output to write the armored data to. If nil, the result will be returned directly as a String.

  • type (String) (defaults to: nil)

    the armor type. Valid values include:

    • message

    • public key

    • secret key

    • signature

    • cleartext

    • nil (try to guess the type)

Returns:

  • (nil, String)


86
87
88
89
90
# File 'lib/rnp/misc.rb', line 86

def self.enarmor(input:, output: nil, type: nil)
  Output.default(output) do |output_|
    Rnp.call_ffi(:rnp_enarmor, input.ptr, output_.ptr, type)
  end
end

.guess_contents(input) ⇒ String

Guess the contents of an input

Parameters:

Returns:

  • (String)

    the guessed content type (or ‘unknown’), which may be used with enarmor



222
223
224
225
226
227
228
229
230
231
# File 'lib/rnp/misc.rb', line 222

def self.guess_contents(input)
  pptr = FFI::MemoryPointer.new(:pointer)
  Rnp.call_ffi(:rnp_guess_contents, input.ptr, pptr)
  begin
    presult = pptr.read_pointer
    presult.read_string unless presult.null?
  ensure
    LibRnp.rnp_buffer_destroy(presult)
  end
end

.has?(feature) ⇒ Boolean

Returns:

  • (Boolean)

Raises:

  • (ArgumentError)


293
294
295
296
# File 'lib/rnp/misc.rb', line 293

def self.has?(feature)
  raise ArgumentError unless FEATURES.include?(feature)
  FEATURES[feature]
end

.homedir_info(homedir) ⇒ Hash<Symbol>

Attempt to detect information about a homedir.

Parameters:

  • homedir (String)

    the homedir

Returns:

  • (Hash<Symbol>)
    • :public [Hash<Symbol>]

      • :format [String]

      • :path [String]

    • :secret [Hash<Symbol>]

      • :format [String]

      • :path [String]



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/rnp/misc.rb', line 36

def self.homedir_info(homedir)
  pptrs = FFI::MemoryPointer.new(:pointer, 4)
  Rnp.call_ffi(:rnp_detect_homedir_info, homedir, pptrs[0], pptrs[1],
               pptrs[2], pptrs[3])
  ptrs = (0..3).collect { |i| pptrs[i] }.map(&:read_pointer)
  return if ptrs.all?(&:null?)
  {
    public: {
      format: ptrs[0].read_string,
      path: ptrs[1].read_string
    },
    secret: {
      format: ptrs[2].read_string,
      path: ptrs[3].read_string
    }
  }
ensure
  ptrs&.each { |ptr| LibRnp.rnp_buffer_destroy(ptr) }
end

.key_format(key_data) ⇒ String

Attempt to detect the format of a key.

Parameters:

  • key_data (String)

    the key data

Returns:

  • (String)


60
61
62
63
64
65
66
67
68
69
70
# File 'lib/rnp/misc.rb', line 60

def self.key_format(key_data)
  pptr = FFI::MemoryPointer.new(:pointer)
  data = FFI::MemoryPointer.from_data(key_data)
  Rnp.call_ffi(:rnp_detect_key_format, data, data.size, pptr)
  begin
    pformat = pptr.read_pointer
    pformat.read_string unless pformat.null?
  ensure
    LibRnp.rnp_buffer_destroy(pformat)
  end
end

.parse(input:, mpi: false, raw: false, grip: false) ⇒ Array

Parse OpenPGP data to JSON.

Parameters:

  • input (Input)

    the input to read the data

  • mpi (Boolean) (defaults to: false)

    if true then MPIs will be included

  • raw (Boolean) (defaults to: false)

    if true then raw bytes will be included

  • grip (Boolean) (defaults to: false)

    if true then grips will be included

Returns:

  • (Array)


176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/rnp/misc.rb', line 176

def self.parse(input:, mpi: false, raw: false, grip: false)
  flags = 0
  flags |= LibRnp::RNP_JSON_DUMP_MPI if mpi
  flags |= LibRnp::RNP_JSON_DUMP_RAW if raw
  flags |= LibRnp::RNP_JSON_DUMP_GRIP if grip
  pptr = FFI::MemoryPointer.new(:pointer)
  Rnp.call_ffi(:rnp_dump_packets_to_json, input.ptr, flags, pptr)
  begin
    pjson = pptr.read_pointer
    JSON.parse(pjson.read_string) unless pjson.null?
  ensure
    LibRnp.rnp_buffer_destroy(pjson)
  end
end

.s2k_iterations(hash:, msec:) ⇒ Integer

Calculate s2k iterations

Parameters:

  • hash (String)

    the hash algorithm to use

  • msec (Integer)

    the desired number of milliseconds

Returns:

  • (Integer)


196
197
198
199
200
# File 'lib/rnp/misc.rb', line 196

def self.s2k_iterations(hash:, msec:)
  piters = FFI::MemoryPointer.new(:size_t)
  Rnp.call_ffi(:rnp_calculate_iterations, hash, msec, piters)
  piters.read(:size_t)
end

.supported_features(type) ⇒ Array

Get a list of supported features (by type)

Parameters:

  • type (String)

    the type of feature (‘symmetric algorithm’, …)

Returns:

  • (Array)


248
249
250
251
252
253
254
255
256
257
# File 'lib/rnp/misc.rb', line 248

def self.supported_features(type)
  pptr = FFI::MemoryPointer.new(:pointer)
  Rnp.call_ffi(:rnp_supported_features, type, pptr)
  begin
    presult = pptr.read_pointer
    JSON.parse(presult.read_string) unless presult.null?
  ensure
    LibRnp.rnp_buffer_destroy(presult)
  end
end

.supports?(type, name) ⇒ Boolean

Check if a specific feature is supported

Parameters:

  • type (String)

    the type of feature (‘symmetric algorithm’, …)

  • name (String)

    the specific feature (‘CAST5’, …)

Returns:

  • (Boolean)


238
239
240
241
242
# File 'lib/rnp/misc.rb', line 238

def self.supports?(type, name)
  presult = FFI::MemoryPointer.new(:bool)
  Rnp.call_ffi(:rnp_supports_feature, type, name, presult)
  presult.read(:bool)
end

.version(str = nil) ⇒ Integer

Get the version stamp of the rnp library as an unsigned 32-bit integer. This number can be compared against other stamps generated with version_for.

Returns:

  • (Integer)


123
124
125
126
127
128
129
# File 'lib/rnp/misc.rb', line 123

def self.version(str = nil)
  if str.nil?
    LibRnp.rnp_version
  else
    LibRnp.rnp_version_for(*str.split('.').map(&:to_i))
  end
end

.version_for(major, minor, patch) ⇒ Integer

Encode the given major, minor, and patch numbers into a version stamp.

Returns:

  • (Integer)


135
136
137
# File 'lib/rnp/misc.rb', line 135

def self.version_for(major, minor, patch)
  LibRnp.rnp_version_for(major, minor, patch)
end

.version_major(version) ⇒ Integer

Extract the major version component from the given version stamp.

Returns:

  • (Integer)


142
143
144
# File 'lib/rnp/misc.rb', line 142

def self.version_major(version)
  LibRnp.rnp_version_major(version)
end

.version_minor(version) ⇒ Integer

Extract the minor version component from the given version stamp.

Returns:

  • (Integer)


149
150
151
# File 'lib/rnp/misc.rb', line 149

def self.version_minor(version)
  LibRnp.rnp_version_minor(version)
end

.version_patch(version) ⇒ Integer

Extract the patch version component from the given version stamp.

Returns:

  • (Integer)


156
157
158
# File 'lib/rnp/misc.rb', line 156

def self.version_patch(version)
  LibRnp.rnp_version_patch(version)
end

.version_stringString

Get the version of the rnp library as a string.

Returns:

  • (String)


107
108
109
# File 'lib/rnp/misc.rb', line 107

def self.version_string
  LibRnp.rnp_version_string
end

.version_string_fullString

Get the detailed version of the rnp library as a string.

Returns:

  • (String)


114
115
116
# File 'lib/rnp/misc.rb', line 114

def self.version_string_full
  LibRnp.rnp_version_string_full
end

Instance Method Details

#cleartext_sign(input:, output: nil, signers:, compression: nil, creation_time: nil, expiration_time: nil, hash: nil) ⇒ nil, String

Create a cleartext signature.

Parameters:

  • signers (Key, Array<Key>)

    the keys to sign with

  • input (Input)

    the input to read the data to be signed

  • output (Output) (defaults to: nil)

    the output to write the signatures. If nil, the result will be returned directly as a String.

  • compression (Hash<Symbol>) (defaults to: nil)
  • creation_time (Time, Integer) (defaults to: nil)

    the creation time to use for all signatures. As an integer, this is the number of seconds since the unix epoch.

  • expiration_time (Integer) (defaults to: nil)

    the lifetime of the signature(s), as the number of seconds. The actual expiration date/time is the creation time plus this value. A value of 0 will create signatures that do not expire.

  • hash (String) (defaults to: nil)

    the hash algorithm name

Returns:

  • (nil, String)


369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
# File 'lib/rnp/rnp.rb', line 369

def cleartext_sign(input:, output: nil, signers:,
                   compression: nil,
                   creation_time: nil,
                   expiration_time: nil,
                   hash: nil)
  Output.default(output) do |output_|
    sign = start_cleartext_sign(input: input, output: output_)
    sign.options = {
      compression: compression,
      creation_time: creation_time,
      expiration_time: expiration_time,
      hash: hash
    }
    simple_sign(sign, signers)
  end
end

#decrypt(input:, output: nil) ⇒ nil, String

Decrypt encrypted data.

Parameters:

  • input (Input)

    the input to read the encrypted data

  • output (Output) (defaults to: nil)

    the output to write the decrypted data. If nil, the result will be returned directly as a String.

Returns:

  • (nil, String)


544
545
546
547
548
# File 'lib/rnp/rnp.rb', line 544

def decrypt(input:, output: nil)
  Output.default(output) do |output_|
    Rnp.call_ffi(:rnp_decrypt, @ptr, input.ptr, output_.ptr)
  end
end

#detached_sign(input:, output: nil, signers:, armored: nil, compression: nil, creation_time: nil, expiration_time: nil, hash: nil) ⇒ nil, String

Create a detached signature.

Parameters:

  • signers (Key, Array<Key>)

    the keys to sign with

  • input (Input)

    the input to read the data to be signed

  • output (Output) (defaults to: nil)

    the output to write the signatures. If nil, the result will be returned directly as a String.

  • armored (Boolean) (defaults to: nil)

    true if the output should be ASCII-armored, false otherwise.

  • compression (Hash<Symbol>) (defaults to: nil)
  • creation_time (Time, Integer) (defaults to: nil)

    the creation time to use for all signatures. As an integer, this is the number of seconds since the unix epoch.

  • expiration_time (Integer) (defaults to: nil)

    the lifetime of the signature(s), as the number of seconds. The actual expiration date/time is the creation time plus this value. A value of 0 will create signatures that do not expire.

  • hash (String) (defaults to: nil)

    the hash algorithm name

Returns:

  • (nil, String)


397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
# File 'lib/rnp/rnp.rb', line 397

def detached_sign(input:, output: nil, signers:,
                  armored: nil,
                  compression: nil,
                  creation_time: nil,
                  expiration_time: nil,
                  hash: nil)
  Output.default(output) do |output_|
    sign = start_detached_sign(input: input, output: output_)
    sign.options = {
      armored: armored,
      compression: compression,
      creation_time: creation_time,
      expiration_time: expiration_time,
      hash: hash
    }
    simple_sign(sign, signers)
  end
end

#detached_verify(data:, signature:) ⇒ Object

Verify a detached signature.

Parameters:

  • data (Input)

    the input to read the data

  • signature (Input)

    the input to read the signatures



429
430
431
432
# File 'lib/rnp/rnp.rb', line 429

def detached_verify(data:, signature:)
  verify = start_detached_verify(data: data, signature: signature)
  verify.execute
end

#each_fingerprint(&block) ⇒ self, Enumerator

Enumerate all fingerprints.

Returns:

  • (self, Enumerator)


# File 'lib/rnp/rnp.rb', line 291

#each_grip(&block) ⇒ self, Enumerator

Enumerate all grips.

Returns:

  • (self, Enumerator)


306
307
308
309
310
311
312
313
314
# File 'lib/rnp/rnp.rb', line 306

%w[userid keyid fingerprint grip].each do |identifier_type|
  define_method("each_#{identifier_type}".to_sym) do |&block|
    each_identifier(identifier_type, &block)
  end

  define_method("#{identifier_type}s".to_sym) do
    each_identifier(identifier_type).to_a
  end
end

#each_keyid(&block) ⇒ self, Enumerator

Enumerate all keyids.

Returns:

  • (self, Enumerator)


# File 'lib/rnp/rnp.rb', line 281

#each_userid(&block) ⇒ self, Enumerator

Enumerate all userids.

Returns:

  • (self, Enumerator)


# File 'lib/rnp/rnp.rb', line 271

#encrypt(input:, output: nil, recipients:, armored: nil, compression: nil, cipher: nil, aead: nil) ⇒ Object

Encrypt data with a public key.

Parameters:

  • input (Input)

    the input to read the plaintext

  • output (Output) (defaults to: nil)

    the output to write the encrypted data. If nil, the result will be returned directly as a String.

  • recipients (Key, Array<Key>)

    list of recipients keys

  • armored (Boolean) (defaults to: nil)

    true if the output should be ASCII-armored, false otherwise.

  • compression (Hash<Symbol>) (defaults to: nil)
  • cipher (String) (defaults to: nil)

    the cipher algorithm name



444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
# File 'lib/rnp/rnp.rb', line 444

def encrypt(input:, output: nil, recipients:,
            armored: nil,
            compression: nil,
            cipher: nil,
            aead: nil)
  Output.default(output) do |output_|
    enc = start_encrypt(input: input, output: output_)
    enc.options = {
      armored: armored,
      compression: compression,
      cipher: cipher,
      aead: aead,
    }
    simple_encrypt(enc, recipients: recipients)
  end
end

#encrypt_and_sign(input:, output: nil, recipients:, signers:, armored: nil, compression: nil, cipher: nil, aead: nil, hash: nil, creation_time: nil, expiration_time: nil) ⇒ Object

Encrypt and sign data with a public key.

Parameters:

  • signers (Key, Array<Key>)

    list of keys to sign with

  • input (Input)

    the input to read the plaintext

  • output (Output) (defaults to: nil)

    the output to write the encrypted data. If nil, the result will be returned directly as a String.

  • recipients (Key, Array<Key>)

    list of recipients keys

  • armored (Boolean) (defaults to: nil)

    true if the output should be ASCII-armored, false otherwise.

  • compression (Hash<Symbol>) (defaults to: nil)
  • cipher (String) (defaults to: nil)

    the cipher algorithm name

  • hash (String) (defaults to: nil)

    the hash algorithm name

  • creation_time (Time, Integer) (defaults to: nil)

    the creation time to use for all signatures. As an integer, this is the number of seconds since the unix epoch.

  • expiration_time (Integer) (defaults to: nil)

    the lifetime of the signatures, as the number of seconds. The actual expiration date/time is the creation time plus this value. A value of 0 will create signatures that do not expire.



474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
# File 'lib/rnp/rnp.rb', line 474

def encrypt_and_sign(input:, output: nil, recipients:, signers:,
                     armored: nil,
                     compression: nil,
                     cipher: nil,
                     aead: nil,
                     hash: nil,
                     creation_time: nil,
                     expiration_time: nil)
  Output.default(output) do |output_|
    enc = start_encrypt(input: input, output: output_)
    enc.options = {
      armored: armored,
      compression: compression,
      cipher: cipher,
      aead: aead,
      hash: hash,
      creation_time: creation_time,
      expiration_time: expiration_time
    }
    simple_encrypt(enc, recipients: recipients, signers: signers)
  end
end

#find_key(criteria) ⇒ Key?

Find a key.

Parameters:

  • criteria (Hash)

    the search criteria. Some examples would be:

    • {keyid: ‘2FCADF05FFA501BB’}

    • {‘userid’: ‘user0’}

    • {fingerprint: ‘BE1C4AB951F4C2F6B604’}

    Only one criteria can be specified.

Returns:

Raises:



256
257
258
259
260
261
262
263
264
# File 'lib/rnp/rnp.rb', line 256

def find_key(criteria)
  raise Rnp::Error, 'Invalid search criteria' if !criteria.is_a?(::Hash) ||
                                                 criteria.size != 1
  pptr = FFI::MemoryPointer.new(:pointer)
  Rnp.call_ffi(:rnp_locate_key, @ptr, criteria.keys[0].to_s,
               criteria.values[0], pptr)
  pkey = pptr.read_pointer
  Rnp::Key.new(pkey) unless pkey.null?
end

#fingerprintsArray<String>

Get a list of all fingerprints.

Returns:

  • (Array<String>)


# File 'lib/rnp/rnp.rb', line 286

#generate(type:, userid:, bits:, curve: nil, password:, subtype: nil, subbits: 0, subcurve: nil) ⇒ Object

Generate a key and optional subkey.

Parameters:

  • userid (String)

    the userid for the key

  • password (String)

    the password to protect the key(s) (nil for no protection)



206
207
208
209
210
211
212
213
# File 'lib/rnp/rnp.rb', line 206

def generate(type:, userid:, bits:, curve: nil, password:,
             subtype: nil, subbits: 0, subcurve: nil)
  pptr = FFI::MemoryPointer.new(:pointer)
  Rnp.call_ffi(:rnp_generate_key_ex, @ptr, type, subtype, bits, subbits,
               curve, subcurve, userid, password, pptr)
  pkey = pptr.read_pointer
  Key.new(pkey) unless pkey.null?
end

#generate_dsa_elgamal(userid:, bits:, subbits: 0, password:) ⇒ Object

Generate a DSA (w/optional ElGamal subkey) key.

Parameters:

  • userid (String)

    the userid for the key

  • bits (Integer)

    the bit length for the primary key

  • subbits (Integer) (defaults to: 0)

    the bit length for the subkey (0 if no subkey should be generated)

  • password (String)

    the password to protect the key(s) (nil for no protection)



156
157
158
159
160
161
162
# File 'lib/rnp/rnp.rb', line 156

def generate_dsa_elgamal(userid:, bits:, subbits: 0, password:)
  pptr = FFI::MemoryPointer.new(:pointer)
  Rnp.call_ffi(:rnp_generate_key_dsa_eg, @ptr, bits, subbits, userid,
               password, pptr)
  pkey = pptr.read_pointer
  Key.new(pkey) unless pkey.null?
end

#generate_ecdsa_ecdh(userid:, curve:, password:) ⇒ Object

Generate an ECDSA+ECDH key pair.

Parameters:

  • userid (String)

    the userid for the key

  • curve (String)

    the name of the curve

  • password (String)

    the password to protect the key(s) (nil for no protection)



170
171
172
173
174
175
# File 'lib/rnp/rnp.rb', line 170

def generate_ecdsa_ecdh(userid:, curve:, password:)
  pptr = FFI::MemoryPointer.new(:pointer)
  Rnp.call_ffi(:rnp_generate_key_ec, @ptr, curve, userid, password, pptr)
  pkey = pptr.read_pointer
  Key.new(pkey) unless pkey.null?
end

#generate_eddsa_25519(userid:, password:) ⇒ Object

Generate an EdDSA+x25519 key pair.

Parameters:

  • userid (String)

    the userid for the key

  • password (String)

    the password to protect the key(s) (nil for no protection)



182
183
184
185
186
187
# File 'lib/rnp/rnp.rb', line 182

def generate_eddsa_25519(userid:, password:)
  pptr = FFI::MemoryPointer.new(:pointer)
  Rnp.call_ffi(:rnp_generate_key_25519, @ptr, userid, password, pptr)
  pkey = pptr.read_pointer
  Key.new(pkey) unless pkey.null?
end

#generate_key(description) ⇒ Hash<Symbol, Key>

Note:

The generated key(s) will be unprotected and unlocked. The application should protect and lock the keys with Rnp::Key#protect and Rnp::Key#lock.

Generate a new key or pair of keys.

Examples

examples/key_generation.rb

Parameters:

  • description (String, Hash)

Returns:

  • (Hash<Symbol, Key>)

    a hash containing the generated key(s)



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/rnp/rnp.rb', line 113

def generate_key(description)
  description = JSON.generate(description) unless description.is_a?(String)
  pptr = FFI::MemoryPointer.new(:pointer)
  Rnp.call_ffi(:rnp_generate_key_json, @ptr, description, pptr)
  begin
    presults = pptr.read_pointer
    return nil if presults.null?
    results = JSON.parse(presults.read_string)
    generated = {}
    results.each do |k, v|
      key = find_key(v.keys[0].to_sym => v.values[0])
      generated[k.to_sym] = key
    end
    generated
  ensure
    LibRnp.rnp_buffer_destroy(presults)
  end
end

#generate_rsa(userid:, bits:, subbits: 0, password:) ⇒ Object

Generate an RSA key (w/optional subkey).

Parameters:

  • userid (String)

    the userid for the key

  • bits (Integer)

    the bit length for the primary key

  • subbits (Integer) (defaults to: 0)

    the bit length for the subkey (0 if no subkey should be generated)

  • password (String)

    the password to protect the key(s) (nil for no protection)



140
141
142
143
144
145
146
# File 'lib/rnp/rnp.rb', line 140

def generate_rsa(userid:, bits:, subbits: 0, password:)
  pptr = FFI::MemoryPointer.new(:pointer)
  Rnp.call_ffi(:rnp_generate_key_rsa, @ptr, bits, subbits, userid, password,
               pptr)
  pkey = pptr.read_pointer
  Key.new(pkey) unless pkey.null?
end

#generate_sm2(userid:, password:) ⇒ Object

Generate an SM2 key pair.

Parameters:

  • userid (String)

    the userid for the key

  • password (String)

    the password to protect the key(s) (nil for no protection)



194
195
196
197
198
199
# File 'lib/rnp/rnp.rb', line 194

def generate_sm2(userid:, password:)
  pptr = FFI::MemoryPointer.new(:pointer)
  Rnp.call_ffi(:rnp_generate_key_sm2, @ptr, userid, password, pptr)
  pkey = pptr.read_pointer
  Key.new(pkey) unless pkey.null?
end

#gripsArray<String>

Get a list of all grips.

Returns:

  • (Array<String>)


# File 'lib/rnp/rnp.rb', line 296

#import_keys(input:, public_keys: true, secret_keys: true) ⇒ Hash

Import keys

Parameters:

  • input (Input)

    the input to read the (OpenPGP-format) keys from

  • public_keys (Boolean) (defaults to: true)

    whether to load public keys

  • secret_keys (Boolean) (defaults to: true)

    whether to load secret keys

Returns:

  • (Hash)

    information on the imported keys



632
633
634
635
636
637
638
639
640
641
642
643
644
# File 'lib/rnp/rnp.rb', line 632

def import_keys(input:, public_keys: true, secret_keys: true)
  flags = 0
  flags |= LibRnp::RNP_LOAD_SAVE_PUBLIC_KEYS if public_keys
  flags |= LibRnp::RNP_LOAD_SAVE_SECRET_KEYS if secret_keys
  pptr = FFI::MemoryPointer.new(:pointer)
  Rnp.call_ffi(:rnp_import_keys, @ptr, input.ptr, flags, pptr)
  begin
    presults = pptr.read_pointer
    JSON.parse(presults.read_string) unless pptr.null?
  ensure
    LibRnp.rnp_buffer_destroy(presults)
  end
end

#import_signatures(input:) ⇒ Hash

Import signatures

Parameters:

  • input (Input)

    the input to read the (OpenPGP-format) keys from

Returns:

  • (Hash)

    information on the imported keys



650
651
652
653
654
655
656
657
658
659
# File 'lib/rnp/rnp.rb', line 650

def import_signatures(input:)
  pptr = FFI::MemoryPointer.new(:pointer)
  Rnp.call_ffi(:rnp_import_signatures, @ptr, input.ptr, 0, pptr)
  begin
    presults = pptr.read_pointer
    JSON.parse(presults.read_string) unless pptr.null?
  ensure
    LibRnp.rnp_buffer_destroy(presults)
  end
end

#inspectObject



41
42
43
# File 'lib/rnp/rnp.rb', line 41

def inspect
  Rnp.inspect_ptr(self)
end

#key_provider=(provider) ⇒ Object

Set a key provider.

The key provider is useful if, for example, you have a database of keys and you do not want to load all of them, and you don’t know which will be needed for a given operation.

The key provider will be called to request that a key be loaded, and the key provider is responsible for loading the appropriate key (if available) using #load_keys.

The provider may be called multiple times for the same key, but with different identifiers. For example, it may first be called with a fingerprint, then (if the key was not loaded), it may be called with a keyid.

Examples

examples/key_provider.rb

Parameters:

  • provider (Proc, #call)

    a callable object



74
75
76
77
78
# File 'lib/rnp/rnp.rb', line 74

def key_provider=(provider)
  @key_provider = provider
  @key_provider = KEY_PROVIDER.curry[provider] if provider
  Rnp.call_ffi(:rnp_ffi_set_key_provider, @ptr, @key_provider, nil)
end

#keyidsArray<String>

Get a list of all keyids.

Returns:

  • (Array<String>)


# File 'lib/rnp/rnp.rb', line 276

#load_keys(input:, format:, public_keys: true, secret_keys: true) ⇒ void

This method returns an undefined value.

Load keys.

Parameters:

  • format (String)

    the format of the keys to load (GPG, KBX, G10).

  • input (Input)

    the input to read the keys from

  • public_keys (Boolean) (defaults to: true)

    whether to load public keys

  • secret_keys (Boolean) (defaults to: true)

    whether to load secret keys

Raises:

  • (ArgumentError)


222
223
224
225
226
# File 'lib/rnp/rnp.rb', line 222

def load_keys(input:, format:, public_keys: true, secret_keys: true)
  raise ArgumentError, 'At least one of public_keys or secret_keys must be true' if !public_keys && !secret_keys
  flags = load_save_flags(public_keys: public_keys, secret_keys: secret_keys)
  Rnp.call_ffi(:rnp_load_keys, @ptr, format, input.ptr, flags)
end

#log=(fd) ⇒ Object

Set a logging destination.

Parameters:

  • fd (Integer, IO)

    the file descriptor to log to. This will be closed when this object is destroyed.



49
50
51
52
# File 'lib/rnp/rnp.rb', line 49

def log=(fd)
  fd = fd.to_i if fd.is_a(::IO)
  Rnp.call_ffi(:rnp_ffi_set_log_fd, @ptr, fd)
end

#password_provider=(provider) ⇒ Object

Set a password provider.

The password provider is used for retrieving passwords for various operations, including:

  • Signing data

  • Decrypting data (public-key or symmetric)

  • Adding a userid to a key

  • Unlocking a key

  • Unprotecting a key

Examples

examples/password_provider.rb

Parameters:

  • provider (Proc, #call, String)

    a callable object, or a password



95
96
97
98
99
# File 'lib/rnp/rnp.rb', line 95

def password_provider=(provider)
  @password_provider = provider
  @password_provider = PASS_PROVIDER.curry[provider] if provider
  Rnp.call_ffi(:rnp_ffi_set_pass_provider, @ptr, @password_provider, nil)
end

#public_key_countObject



316
317
318
319
320
# File 'lib/rnp/rnp.rb', line 316

def public_key_count
  pcount = FFI::MemoryPointer.new(:size_t)
  Rnp.call_ffi(:rnp_get_public_key_count, @ptr, pcount)
  pcount.read(:size_t)
end

#save_keys(output:, format:, public_keys: false, secret_keys: false) ⇒ void

This method returns an undefined value.

Save keys.

Parameters:

  • format (String)

    the format to save the keys in (GPG, KBX, G10).

  • output (Output)

    the output to write the keys to

  • public_keys (Boolean) (defaults to: false)

    whether to load public keys

  • secret_keys (Boolean) (defaults to: false)

    whether to load secret keys

Raises:

  • (ArgumentError)


242
243
244
245
246
# File 'lib/rnp/rnp.rb', line 242

def save_keys(output:, format:, public_keys: false, secret_keys: false)
  raise ArgumentError, 'At least one of public_keys or secret_keys must be true' if !public_keys && !secret_keys
  flags = load_save_flags(public_keys: public_keys, secret_keys: secret_keys)
  Rnp.call_ffi(:rnp_save_keys, @ptr, format, output.ptr, flags)
end

#secret_key_countObject



322
323
324
325
326
# File 'lib/rnp/rnp.rb', line 322

def secret_key_count
  pcount = FFI::MemoryPointer.new(:size_t)
  Rnp.call_ffi(:rnp_get_secret_key_count, @ptr, pcount)
  pcount.read(:size_t)
end

#sign(input:, output: nil, signers:, armored: nil, compression: nil, creation_time: nil, expiration_time: nil, hash: nil) ⇒ nil, String

Create a signature.

Parameters:

  • input (Input)

    the input to read the data to be signed

  • output (Output) (defaults to: nil)

    the output to write the signatures. If nil, the result will be returned directly as a String.

  • signers (Key, Array<Key>)

    the keys to sign with

  • armored (Boolean) (defaults to: nil)

    true if the output should be ASCII-armored, false otherwise.

  • compression (Hash<Symbol>) (defaults to: nil)
  • creation_time (Time, Integer) (defaults to: nil)

    the creation time to use for all signatures. As an integer, this is the number of seconds since the unix epoch.

  • expiration_time (Integer) (defaults to: nil)

    the lifetime of the signature(s), as the number of seconds. The actual expiration date/time is the creation time plus this value. A value of 0 will create signatures that do not expire.

  • hash (String) (defaults to: nil)

    the hash algorithm name

Returns:

  • (nil, String)


340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/rnp/rnp.rb', line 340

def sign(input:, output: nil, signers:,
         armored: nil,
         compression: nil,
         creation_time: nil,
         expiration_time: nil,
         hash: nil)
  Output.default(output) do |output_|
    sign = start_sign(input: input, output: output_)
    sign.options = {
      armored: armored,
      compression: compression,
      creation_time: creation_time,
      expiration_time: expiration_time,
      hash: hash
    }
    simple_sign(sign, signers)
  end
end

#start_cleartext_sign(input:, output:) ⇒ Object

Create a cleartext Sign operation.

Parameters:

  • input (Input)

    the input to read the data to be signed

  • output (Output)

    the output to write the signatures



586
587
588
# File 'lib/rnp/rnp.rb', line 586

def start_cleartext_sign(input:, output:)
  _start_sign(:rnp_op_sign_cleartext_create, input, output)
end

#start_detached_sign(input:, output:) ⇒ Object

Create a detached Sign operation.

Parameters:

  • input (Input)

    the input to read the data to be signed

  • output (Output)

    the output to write the signatures



594
595
596
# File 'lib/rnp/rnp.rb', line 594

def start_detached_sign(input:, output:)
  _start_sign(:rnp_op_sign_detached_create, input, output)
end

#start_detached_verify(data:, signature:) ⇒ Object

Create a detached Verify operation.

Parameters:

  • data (Input)

    the input to read the signed data

  • signature (Input)

    the input to read the signatures



611
612
613
# File 'lib/rnp/rnp.rb', line 611

def start_detached_verify(data:, signature:)
  _start_verify(:rnp_op_verify_detached_create, data, signature)
end

#start_encrypt(input:, output:) ⇒ Object

Create an Encrypt operation.

Parameters:

  • input (Input)

    the input to read the plaintext

  • output (Output)

    the output to write the encrypted data



619
620
621
622
623
624
# File 'lib/rnp/rnp.rb', line 619

def start_encrypt(input:, output:)
  pptr = FFI::MemoryPointer.new(:pointer)
  Rnp.call_ffi(:rnp_op_encrypt_create, pptr, @ptr, input.ptr, output.ptr)
  pencrypt = pptr.read_pointer
  Encrypt.new(pencrypt) unless pencrypt.null?
end

#start_generate(type:) ⇒ Generate

Start a Generate operation.

Parameters:

  • type (String, Symbol)

    the key type to generate (RSA, DSA, etc)

Returns:



554
555
556
557
558
559
# File 'lib/rnp/rnp.rb', line 554

def start_generate(type:)
  pptr = FFI::MemoryPointer.new(:pointer)
  Rnp.call_ffi(:rnp_op_generate_create, pptr, @ptr, type.to_s)
  pgen = pptr.read_pointer
  Generate.new(pgen) unless pgen.null?
end

#start_generate_subkey(primary:, type:) ⇒ Generate

Start a Generate operation.

Parameters:

  • primary (Key)

    the primary key for which to generate a subkey

  • type (String, Symbol)

    the key type to generate (RSA, DSA, etc)

Returns:



566
567
568
569
570
571
572
# File 'lib/rnp/rnp.rb', line 566

def start_generate_subkey(primary:, type:)
  pptr = FFI::MemoryPointer.new(:pointer)
  Rnp.call_ffi(:rnp_op_generate_subkey_create, pptr, @ptr, primary.ptr,
               type.to_s)
  pgen = pptr.read_pointer
  Generate.new(pgen) unless pgen.null?
end

#start_sign(input:, output:) ⇒ Object

Create a Sign operation.

Parameters:

  • input (Input)

    the input to read the data to be signed

  • output (Output)

    the output to write the signatures



578
579
580
# File 'lib/rnp/rnp.rb', line 578

def start_sign(input:, output:)
  _start_sign(:rnp_op_sign_create, input, output)
end

#start_verify(input:, output: nil) ⇒ Object

Create a Verify operation.

Parameters:

  • input (Input)

    the input to read the signatures

  • output (Output) (defaults to: nil)

    the output (if any) to write the verified data



602
603
604
605
# File 'lib/rnp/rnp.rb', line 602

def start_verify(input:, output: nil)
  output = Output.to_null unless output
  _start_verify(:rnp_op_verify_create, input, output)
end

#symmetric_encrypt(input:, output: nil, passwords:, armored: nil, compression: nil, cipher: nil, aead: nil, s2k_hash: nil, s2k_iterations: 0, s2k_cipher: nil) ⇒ void

This method returns an undefined value.

Encrypt with a password only.

Parameters:

  • passwords (String, Array<String>)

    list of passwords to encrypt with. Any (single) one of the passwords can be used to decrypt.

  • input (Input)

    the input to read the plaintext

  • output (Output) (defaults to: nil)

    the output to write the encrypted data. If nil, the result will be returned directly as a String.

  • armored (Boolean) (defaults to: nil)

    true if the output should be ASCII-armored, false otherwise.

  • compression (Hash<Symbol>) (defaults to: nil)
  • cipher (String) (defaults to: nil)

    the cipher algorithm name

  • s2k_hash (String) (defaults to: nil)

    the hash algorithm to use for the string-to-key key derivation.

  • s2k_iterations (Integer) (defaults to: 0)

    the number of iterations for the string-to-key key derivation. A value of 0 will choose a default.

  • s2k_cipher (String) (defaults to: nil)

    the cipher algorithm used to wrap the key.



511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
# File 'lib/rnp/rnp.rb', line 511

def symmetric_encrypt(input:, output: nil, passwords:,
                      armored: nil,
                      compression: nil,
                      cipher: nil,
                      aead: nil,
                      s2k_hash: nil,
                      s2k_iterations: 0,
                      s2k_cipher: nil)
  Output.default(output) do |output_|
    enc = start_encrypt(input: input, output: output_)
    enc.options = {
      armored: armored,
      compression: compression,
      cipher: cipher,
      aead: aead,
    }
    passwords = [passwords] if passwords.is_a?(String)
    passwords.each do |password|
      enc.add_password(password,
                       s2k_hash: s2k_hash,
                       s2k_iterations: s2k_iterations,
                       s2k_cipher: s2k_cipher)
    end
    enc.execute
  end
end

#unload_keys(public_keys: true, secret_keys: true) ⇒ Object

Raises:

  • (ArgumentError)


228
229
230
231
232
233
# File 'lib/rnp/rnp.rb', line 228

def unload_keys(public_keys: true, secret_keys: true)
  raise ArgumentError, "At least one of public_keys or secret_keys must be true" \
    if !public_keys && !secret_keys
  flags = unload_keys_flags(public_keys: public_keys, secret_keys: secret_keys)
  Rnp.call_ffi(:rnp_unload_keys, @ptr, flags)
end

#useridsArray<String>

Get a list of all userids.

Returns:

  • (Array<String>)


# File 'lib/rnp/rnp.rb', line 266

#verify(input:, output: nil) ⇒ Object

Verify a signature.

Parameters:

  • input (Input)

    the input to read the signatures

  • output (Output) (defaults to: nil)

    the output (if any) to write the verified data



420
421
422
423
# File 'lib/rnp/rnp.rb', line 420

def verify(input:, output: nil)
  verify = start_verify(input: input, output: output)
  verify.execute
end