class Rnp
Class used for interacting with RNP.
© 2018 Ribose Inc.
Constants
- ERRORS_MAP
@api private
- KEY_PROVIDER
- PASS_PROVIDER
- VERSION
Attributes
@api private
Public Class Methods
@api private
Calls the LibRnp
FFI function indicated. If the return code is <0, an error will be raised.
@param fn [Symbol] the name of the function to call @param args the arguments to pass to the FFI function @return [void]
# File lib/rnp/utils.rb, line 19 def self.call_ffi(fn, *args) rc = LibRnp.method(fn).call(*args) Rnp.raise_error("#{fn} failed", rc) unless rc.zero? nil end
Remove ASCII Armor from data.
@param input [Input] the input to read the ASCII-Armored data from @param output [Output] the output to write the dearmored data to. If
nil, the result will be returned directly as a String.
@return [nil, String]
# File lib/rnp/misc.rb, line 90 def self.dearmor(input:, output: nil) Output.default(output) do |output_| Rnp.call_ffi(:rnp_dearmor, input.ptr, output_.ptr) end end
Get the default homedir for RNP.
@return [String]
# File lib/rnp/misc.rb, line 14 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
@api private
# File lib/rnp/rnp.rb, line 36 def self.destroy(ptr) LibRnp.rnp_ffi_destroy(ptr) end
Add ASCII Armor to data.
@param input [Input] the input to read data from @param output [Output] the output to write the armored
data to. If nil, the result will be returned directly as a String.
@return [nil, String]
# File lib/rnp/misc.rb, line 78 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
Attempt to detect information about a homedir.
@param homedir [String] the homedir @return [Hash<Symbol>]
* :public [Hash<Symbol>] * :format [String] * :path [String] * :secret [Hash<Symbol>] * :format [String] * :path [String]
# File lib/rnp/misc.rb, line 35 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
@api private
# File lib/rnp/utils.rb, line 26 def self.inspect_ptr(myself) ptr_format = "0x%0#{FFI::Pointer.size * 2}x" ptr_s = format(ptr_format, myself.instance_variable_get(:@ptr).address) class_name = myself.class.to_s "#<#{class_name}:#{ptr_s}>" end
Attempt to detect the format of a key.
@param key_data [String] the key data @return [String]
# File lib/rnp/misc.rb, line 59 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
Create a new interface to RNP.
@param pubfmt [String] the public keyring format @param secfmt [String] the secret keyring format
# File lib/rnp/rnp.rb, line 27 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
@api private
# File lib/rnp/error.rb, line 33 def self.raise_error(msg, rc = nil) klass = ERRORS_MAP.fetch(rc, Error) raise klass.new(msg, rc) end
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}.
@return [Integer]
# File lib/rnp/misc.rb, line 115 def self.version LibRnp.rnp_version end
Encode the given major, minor, and patch numbers into a version stamp.
@return [Integer]
# File lib/rnp/misc.rb, line 123 def self.version_for(major, minor, patch) LibRnp.rnp_version_for(major, minor, patch) end
Extract the major version component from the given version stamp.
@return [Integer]
# File lib/rnp/misc.rb, line 130 def self.version_major(version) LibRnp.rnp_version_major(version) end
Extract the minor version component from the given version stamp.
@return [Integer]
# File lib/rnp/misc.rb, line 137 def self.version_minor(version) LibRnp.rnp_version_minor(version) end
Extract the patch version component from the given version stamp.
@return [Integer]
# File lib/rnp/misc.rb, line 144 def self.version_patch(version) LibRnp.rnp_version_patch(version) end
Get the version of the rnp library as a string.
@return [String]
# File lib/rnp/misc.rb, line 99 def self.version_string LibRnp.rnp_version_string end
Get the detailed version of the rnp library as a string.
@return [String]
# File lib/rnp/misc.rb, line 106 def self.version_string_full LibRnp.rnp_version_string_full end
Public Instance Methods
Create a cleartext signature.
@param input (see sign
) @param output (see sign
) @param signers [Key, Array<Key>] the keys to sign with @param compression (see Sign#compression=
) @param creation_time (see Sign#creation_time=
) @param expiration_time (see Sign#expiration_time=
) @param hash (see Sign#hash=
) @return [nil, String]
# File lib/rnp/rnp.rb, line 278 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 encrypted data.
@param input [Input] the input to read the encrypted data @param output [Output] the output to write the decrypted data.
If nil, the result will be returned directly as a String.
@return [nil, String]
# File lib/rnp/rnp.rb, line 444 def decrypt(input:, output: nil) Output.default(output) do |output_| Rnp.call_ffi(:rnp_decrypt, @ptr, input.ptr, output_.ptr) end end
Create a detached signature.
@param input (see sign
) @param output (see sign
) @param signers [Key, Array<Key>] the keys to sign with @param armored (see Sign#armored=
) @param compression (see Sign#compression=
) @param creation_time (see Sign#creation_time=
) @param expiration_time (see Sign#expiration_time=
) @param hash (see Sign#hash=
) @return [nil, String]
# File lib/rnp/rnp.rb, line 306 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
Verify
a detached signature.
@param data [Input] the input to read the data @param signature [Input] the input to read the signatures
# File lib/rnp/rnp.rb, line 338 def detached_verify(data:, signature:) verify = start_detached_verify(data: data, signature: signature) verify.execute end
Encrypt
data with a public key.
@param input [Input] the input to read the plaintext @param output [Output] the output to write the encrypted data.
If nil, the result will be returned directly as a String.
@param recipients [Key, Array<Key>] list of recipients keys @param armored (see Encrypt#armored=
) @param compression (see Encrypt#compression=
) @param cipher (see Encrypt#cipher=
)
# File lib/rnp/rnp.rb, line 352 def encrypt(input:, output: nil, recipients:, armored: nil, compression: nil, cipher: nil) Output.default(output) do |output_| enc = start_encrypt(input: input, output: output_) enc.options = { armored: armored, compression: compression, cipher: cipher } simple_encrypt(enc, recipients: recipients) end end
Encrypt
and sign data with a public key.
@param input (see encrypt
) @param output (see encrypt
) @param recipients (see encrypt
) @param signers [Key, Array<Key>] list of keys to sign with @param armored (see Encrypt#armored=
) @param compression (see Encrypt#compression=
) @param cipher (see Encrypt#cipher=
) @param hash (see Encrypt#hash=
) @param creation_time (see Encrypt#creation_time=
) @param expiration_time (see Encrypt#expiration_time=
)
# File lib/rnp/rnp.rb, line 379 def encrypt_and_sign(input:, output: nil, recipients:, signers:, armored: nil, compression: nil, cipher: 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, hash: hash, creation_time: creation_time, expiration_time: expiration_time } simple_encrypt(enc, recipients: recipients, signers: signers) end end
Find a key.
@param criteria [Hash] the search criteria. Some examples would be:
* !{keyid: '2FCADF05FFA501BB'} * !{'userid': 'user0'} * !{fingerprint: 'BE1C4AB951F4C2F6B604'} Only *one* criteria can be specified.
@return [Key, nil]
# File lib/rnp/rnp.rb, line 165 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
Generate a new key or pair of keys.
@note The generated key(s) will be unprotected and unlocked.
The application should protect and lock the keys with {Key#protect} and {Key#lock}.
Examples¶ ↑
examples/key_generation.rb¶ ↑
{include:file:examples/key_generation.rb}
@param description [String, Hash] @return [Hash<Symbol, Key>] a hash containing the generated key(s)
# File lib/rnp/rnp.rb, line 112 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
# File lib/rnp/rnp.rb, line 40 def inspect Rnp.inspect_ptr(self) end
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¶ ↑
{include:file:examples/key_provider.rb}
@param provider [Proc, call] a callable object
# File lib/rnp/rnp.rb, line 73 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
Load keys.
@param format [String] the format of the keys to load (GPG, KBX, G10). @param input [Input] the input to read the keys from @param public_keys [Boolean] whether to load public keys @param secret_keys [Boolean] whether to load secret keys @return [void]
# File lib/rnp/rnp.rb, line 138 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
Set a logging destination.
@param fd [Integer, IO] the file descriptor to log to. This will be closed
when this object is destroyed.
# File lib/rnp/rnp.rb, line 48 def log=(fd) fd = fd.to_i if fd.is_a(::IO) Rnp.call_ffi(:rnp_ffi_set_log_fd, @ptr, fd) end
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¶ ↑
{include:file:examples/password_provider.rb}
@param provider [Proc, call, String] a callable object, or a password
# File lib/rnp/rnp.rb, line 94 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
# File lib/rnp/rnp.rb, line 225 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.
@param format [String] the format to save the keys in (GPG, KBX, G10). @param output [Output] the output to write the keys to @param public_keys [Boolean] whether to load public keys @param secret_keys [Boolean] whether to load secret keys @return [void]
# File lib/rnp/rnp.rb, line 151 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
# File lib/rnp/rnp.rb, line 231 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
Create a signature.
@param input [Input] the input to read the data to be signed @param output [Output] the output to write the signatures.
If nil, the result will be returned directly as a String.
@param signers [Key, Array<Key>] the keys to sign with @param armored (see Sign#armored=
) @param compression (see Sign#compression=
) @param creation_time (see Sign#creation_time=
) @param expiration_time (see Sign#expiration_time=
) @param hash (see Sign#hash=
) @return [nil, String]
# File lib/rnp/rnp.rb, line 249 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
Create a cleartext {Sign} operation.
@param input (see start_sign
) @param output (see start_sign
)
# File lib/rnp/rnp.rb, line 462 def start_cleartext_sign(input:, output:) _start_sign(:rnp_op_sign_cleartext_create, input, output) end
Create a detached {Sign} operation.
@param input (see start_sign
) @param output (see start_sign
)
# File lib/rnp/rnp.rb, line 470 def start_detached_sign(input:, output:) _start_sign(:rnp_op_sign_detached_create, input, output) end
Create a detached {Verify} operation.
@param data [Input] the input to read the signed data @param signature [Input] the input to read the signatures
# File lib/rnp/rnp.rb, line 487 def start_detached_verify(data:, signature:) _start_verify(:rnp_op_verify_detached_create, data, signature) end
Create an {Encrypt} operation.
@param input [Input] the input to read the plaintext @param output [Output] the output to write the encrypted data
# File lib/rnp/rnp.rb, line 495 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
Create a {Sign} operation.
@param input [Input] the input to read the data to be signed @param output [Output] the output to write the signatures
# File lib/rnp/rnp.rb, line 454 def start_sign(input:, output:) _start_sign(:rnp_op_sign_create, input, output) end
Create a {Verify} operation.
@param input [Input] the input to read the signatures @param output [Output] the output (if any) to write the verified data
# File lib/rnp/rnp.rb, line 478 def start_verify(input:, output: nil) output = Output.to_null unless output _start_verify(:rnp_op_verify_create, input, output) end
Encrypt
with a password only.
@param input (see encrypt
) @param output (see encrypt
) @param passwords [String, Array<String>] list of passwords to encrypt with.
Any (single) one of the passwords can be used to decrypt.
@param armored (see Encrypt#armored=
) @param compression (see Encrypt#compression=
) @param cipher (see Encrypt#cipher=
) @param s2k_hash (see Encrypt#add_password
) @param s2k_iterations (see Encrypt#add_password
) @param s2k_cipher (see Encrypt#add_password
) @return [void]
# File lib/rnp/rnp.rb, line 413 def symmetric_encrypt(input:, output: nil, passwords:, armored: nil, compression: nil, cipher: 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 } 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
Verify
a signature.
@param input [Input] the input to read the signatures @param output [Output] the output (if any) to write the verified data
# File lib/rnp/rnp.rb, line 329 def verify(input:, output: nil) verify = start_verify(input: input, output: output) verify.execute end
Private Instance Methods
# File lib/rnp/rnp.rb, line 534 def _start_sign(func, input, output) pptr = FFI::MemoryPointer.new(:pointer) Rnp.call_ffi(func, pptr, @ptr, input.ptr, output.ptr) psign = pptr.read_pointer Rnp::Sign.new(psign) unless psign.null? end
# File lib/rnp/rnp.rb, line 541 def _start_verify(func, io1, io2) pptr = FFI::MemoryPointer.new(:pointer) Rnp.call_ffi(func, pptr, @ptr, io1.ptr, io2.ptr) pverify = pptr.read_pointer Verify.new(pverify) unless pverify.null? end
# File lib/rnp/rnp.rb, line 560 def each_identifier(type, &block) block or return enum_for(:identifier_iterator, type) identifier_iterator(type, &block) self end
# File lib/rnp/rnp.rb, line 566 def identifier_iterator(identifier_type) pptr = FFI::MemoryPointer.new(:pointer) piterator = nil Rnp.call_ffi(:rnp_identifier_iterator_create, @ptr, pptr, identifier_type) piterator = pptr.read_pointer loop do Rnp.call_ffi(:rnp_identifier_iterator_next, piterator, pptr) pidentifier = pptr.read_pointer break if pidentifier.null? yield pidentifier.read_string end ensure LibRnp.rnp_identifier_iterator_destroy(piterator) if piterator end
# File lib/rnp/rnp.rb, line 581 def load_save_flags(public_keys:, secret_keys:) flags = 0 flags |= LibRnp::RNP_LOAD_SAVE_PUBLIC_KEYS if public_keys flags |= LibRnp::RNP_LOAD_SAVE_SECRET_KEYS if secret_keys flags end
# File lib/rnp/rnp.rb, line 548 def simple_encrypt(enc, recipients: nil, signers: nil) recipients = [recipients] if recipients.is_a?(Key) recipients&.each do |recipient| enc.add_recipient(recipient) end signers = [signers] if signers.is_a?(Key) signers&.each do |signer| enc.add_signer(signer) end enc.execute end
# File lib/rnp/rnp.rb, line 526 def simple_sign(sign, signers) signers = [signers] if signers.is_a?(Key) signers.each do |signer| sign.add_signer(signer) end sign.execute end