lib/rnp/rnp.rb in rnp-1.0.4 vs lib/rnp/rnp.rb in rnp-1.0.5
- old
+ new
@@ -1,8 +1,8 @@
# frozen_string_literal: true
-# (c) 2018 Ribose Inc.
+# (c) 2018,2019 Ribose Inc.
require 'English'
require 'json'
require 'ffi'
@@ -12,10 +12,11 @@
require 'rnp/utils'
require 'rnp/key'
require 'rnp/op/sign'
require 'rnp/op/verify'
require 'rnp/op/encrypt'
+require "rnp/op/generate"
# Class used for interacting with RNP.
class Rnp
# @api private
attr_reader :ptr
@@ -126,10 +127,93 @@
ensure
LibRnp.rnp_buffer_destroy(presults)
end
end
+ # Generate an RSA key (w/optional subkey).
+ #
+ # @param userid [String] the userid for the key
+ # @param bits [Integer] the bit length for the primary key
+ # @param subbits [Integer] the bit length for the subkey
+ # (0 if no subkey should be generated)
+ # @param password [String] the password to protect the key(s)
+ # (nil for no protection)
+ 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 a DSA (w/optional ElGamal subkey) key.
+ #
+ # @param userid [String] the userid for the key
+ # @param bits [Integer] the bit length for the primary key
+ # @param subbits [Integer] the bit length for the subkey
+ # (0 if no subkey should be generated)
+ # @param password [String] the password to protect the key(s)
+ # (nil for no protection)
+ 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 an ECDSA+ECDH key pair.
+ #
+ # @param userid [String] the userid for the key
+ # @param curve [String] the name of the curve
+ # @param password [String] the password to protect the key(s)
+ # (nil for no protection)
+ 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 an EdDSA+x25519 key pair.
+ #
+ # @param userid [String] the userid for the key
+ # @param password [String] the password to protect the key(s)
+ # (nil for no protection)
+ 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 an SM2 key pair.
+ #
+ # @param userid [String] the userid for the key
+ # @param password [String] the password to protect the key(s)
+ # (nil for no protection)
+ 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
+
+ # Generate a key and optional subkey.
+ #
+ # @param userid [String] the userid for the key
+ # @param password [String] the password to protect the key(s)
+ # (nil for no protection)
+ 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
+
# 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
@@ -139,10 +223,17 @@
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
+ 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
+
# 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
@@ -347,20 +438,23 @@
# 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=)
+ # @param aead (see Encrypt#aead=)
def encrypt(input:, output: nil, recipients:,
armored: nil,
compression: nil,
- cipher: 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
+ cipher: cipher,
+ aead: aead,
}
simple_encrypt(enc, recipients: recipients)
end
end
@@ -371,26 +465,29 @@
# @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 aead (see Encrypt#aead=)
# @param hash (see Encrypt#hash=)
# @param creation_time (see Encrypt#creation_time=)
# @param expiration_time (see Encrypt#expiration_time=)
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)
@@ -404,27 +501,30 @@
# @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 aead (see Encrypt#aead=)
# @param s2k_hash (see Encrypt#add_password)
# @param s2k_iterations (see Encrypt#add_password)
# @param s2k_cipher (see Encrypt#add_password)
# @return [void]
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
+ cipher: cipher,
+ aead: aead,
}
passwords = [passwords] if passwords.is_a?(String)
passwords.each do |password|
enc.add_password(password,
s2k_hash: s2k_hash,
@@ -445,10 +545,34 @@
Output.default(output) do |output_|
Rnp.call_ffi(:rnp_decrypt, @ptr, input.ptr, output_.ptr)
end
end
+ # Start a {Generate} operation.
+ #
+ # @param type [String, Symbol] the key type to generate (RSA, DSA, etc)
+ # @return [Generate]
+ 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 a {Generate} operation.
+ #
+ # @param primary [Key] the primary key for which to generate a subkey
+ # @param type [String, Symbol] the key type to generate (RSA, DSA, etc)
+ # @return [Generate]
+ 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
+
# 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
def start_sign(input:, output:)
@@ -497,10 +621,45 @@
Rnp.call_ffi(:rnp_op_encrypt_create, pptr, @ptr, input.ptr, output.ptr)
pencrypt = pptr.read_pointer
Encrypt.new(pencrypt) unless pencrypt.null?
end
+ # Import keys
+ #
+ # @param input [Input] the input to read the (OpenPGP-format) keys from
+ # @param public_keys [Boolean] whether to load public keys
+ # @param secret_keys [Boolean] whether to load secret keys
+ # @return [Hash] information on the imported keys
+ 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
+ #
+ # @param input [Input] the input to read the (OpenPGP-format) keys from
+ # @return [Hash] information on the imported keys
+ 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
+
private
KEY_PROVIDER = lambda do |provider, _rnp, _ctx, identifier_type, identifier, secret|
provider.call(identifier_type, identifier, secret)
end
@@ -580,9 +739,16 @@
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
+
+ def unload_keys_flags(public_keys:, secret_keys:)
+ flags = 0
+ flags |= LibRnp::RNP_KEY_UNLOAD_PUBLIC if public_keys
+ flags |= LibRnp::RNP_KEY_UNLOAD_SECRET if secret_keys
flags
end
end # class