/*
 * zkey - Generate, re-encipher, and validate secure keys
 *
 * This header file defines the interface to a Key Management System (KMS)
 * Plugin.
 *
 * Copyright IBM Corp. 2020
 *
 * s390-tools is free software; you can redistribute it and/or modify
 * it under the terms of the MIT license. See LICENSE for details.
 */

#ifndef KMS_PLUGIN_H
#define KMS_PLUGIN_H

#include <stddef.h>
#include <stdbool.h>

#include "lib/util_opt.h"

/**
 * Informs a KMS plugin that it is bound to a zkey repository.
 *
 * Note: This function is called before kms_initialize()!
 *
 * @param config_path       name of a directory where the KMS plugin can store
 *                          its configuration and other files it needs to store
 *
 * @returns 0 on success, or a negative errno in case of an error.
 */
int kms_bind(const char *config_path);

typedef void *kms_handle_t;

/**
 * Initializes a KMS plugin for usage by zkey. When a repository is bound to a
 * KMS plugin, zkey calls this function when opening the repository.
 *
 * @param config_path       name of a directory where the KMS plugin can store
 *                          its configuration and other files it needs to store
 * @param verbose           if true, the plugin should write verbose or debug
 *                          messages to stderr during further processing.
 *
 * @returns a KMS plugin handle, or NULL in case of an error.
 */
kms_handle_t kms_initialize(const char *config_path, bool verbose);

/**
 * Terminates the use of a KMS plugin. When a repository is bound to a KMS
 * plugin, zkey calls this function when closing the repository.
 *
 * @param handle            the KMS plugin handle obtained from kms_initialize()
 *
 * @returns 0 on success, or a negative errno in case of an error.
 * Function kms_get_last_error() can be used to obtain more details about the
 * error.
 */
int kms_terminate(const kms_handle_t handle);

/**
 * Returns a textual message about the last occurred error that occurred in the
 * last called KMS plugin function. If no error occurred (i.e. the last plugin
 * function returned rc = 0), then NULL is returned.
 * The returned string is static or contained within the handle. It is valid
 * only until the next KMS plugin function is called.
 *
 * @param handle            the KMS plugin handle obtained from kms_initialize()
 *
 * @returns an error message of NULL
 */
const char *kms_get_last_error(const kms_handle_t handle);

/**
 * Returns true if the KMS plugin supports the specified key type.
 *
 * @param handle            the KMS plugin handle obtained from kms_initialize()
 * @param key_type          the zkey key type, euch as 'CCA-AESDATA',
 *                          'CCA-AESCIPHER', 'EP11-AES'.
 *
 * @returns true if the KMS plugin supports the key type, false otherwise.
 */
bool kms_supports_key_type(const kms_handle_t handle, const char *key_type);

/**
 * Displays information about the KMS Plugin and its current configuration on
 * stdout.
 *
 * @param handle            the KMS plugin handle obtained from kms_initialize()
 *
 * @returns 0 on success, or a negative errno in case of an error.
 * Function kms_get_last_error() can be used to obtain more details about the
 * error.
 */
int kms_display_info(const kms_handle_t handle);

#define KMS_COMMAND_CONFIGURE		"configure"
#define KMS_COMMAND_REENCIPHER		"reencipher"
#define KMS_COMMAND_GENERATE		"generate"
#define KMS_COMMAND_REMOVE		"remove"
#define KMS_COMMAND_LIST		"list"
#define KMS_COMMAND_LIST_IMPORT		"list-import"

/**
 * Returns a list of KMS specific command line options that zkey should accept
 * and pass to the appropriate KMS plugin function. The option list must be
 * terminated by an UTIL_OPT_END entry (see util_opt.h). The options returned
 * must not interfere with the already defined options of the zkey command.
 * Field 'command' of the returned options should either be NULL or specify
 * the command that it is for.
 *
 * If max_opts is not -1, then only up to max_opts options are allowed. If more
 * options are returned, only up to max_opts options are used by zkey.
 *
 * @param command           the command for which the KMS-specific options are
 *                          to be returned, see KMS_COMMAND_xxx defines
 * @param max_opts          maximum number of options allowed. If -1 then there
 *                          is no limit.
 *
 * @returns a list of options terminated by an UTIL_OPT_END entry, or NULL in
 * case of an error.
 * Function kms_get_last_error() can be used to obtain more details about the
 * error.
 */
const struct util_opt *kms_get_command_options(const char *command,
					       int max_opts);

struct kms_apqn {
	unsigned short card;
	unsigned short domain;
};

struct kms_option {
	int option;		/** option character as in struct option */
	const char *argument;   /** argument of the option (if any) */
};


/**
 * Configures (or re-configures) a KMS plugin. This function can be called
 * several times to configure a KMS plugin is several steps (if supported by the
 * KMS plugin). In case a configuration is not fully complete, this function
 * may return -EAGAIN to indicate that it has accepted the configuration so far,
 * but the configuration needs to be completed.
 *
 * No kms_login is performed before calling this function. If the KMS plugin
 * requires a login for the configuration, it must perform it itself within this
 * function.
 *
 * A KMS plugin must be associated with at least one APQN. Thus, in a multi-step
 * configuration, a list f APQNs must be specified at least once.
 *
 * @param handle            the KMS plugin handle obtained from kms_initialize()
 * @param apqns             a list of APQNs to associate with the KMS plugin, or
 *                          NULL if no APQNs are specified.
 * @param num_apqns         number of APQNs in above array. 0 if no APQNs are
 *                          specified.
 * @param options           a list of options as specified by the user. These
 *                          options are a subset of the possible options as
 *                          returned by kms_get_command_options() with command
 *                          KMS_COMMAND_CONFIGURE.
 * @param num_options       number of options in above array.
 *
 * @returns 0 on success, or a negative errno in case of an error.
 * Function kms_get_last_error() can be used to obtain more details about the
 * error.
 * -EAGAIN to indicate that the specified configuration was accepted so far, but
 * the configuration is still incomplete, and needs to be completed.
 */
int kms_configure(const kms_handle_t handle,
		  const struct kms_apqn *apqns, size_t num_apqns,
		  const struct kms_option *options, size_t num_options);

/**
 * De-configures a KMS plugin. This is called by zkey when a repository is
 * unbound from a KMS plugin. It gives the KMS plugin the chance to gracefully
 * remove any files that the plugin has stored in its config directory. zkey
 * will unconditionally remove all left over files when this function returns.
 *
 * @param handle            the KMS plugin handle obtained from kms_initialize()
 *
 * @returns 0 on success, or a negative errno in case of an error.
 * Function kms_get_last_error() can be used to obtain more details about the
 * error.
 */
int kms_deconfigure(const kms_handle_t handle);

/**
 * Allows the KMS plugin to perform a login to the KMS (if required). This
 * function is called at least once before any key operation function, typically
 * shortly after opening the repository.
 * The KMS plugin may prompt the user (by reading from stdin) for its
 * credentials, if needed.
 *
 * It is suggested that a KMS plugin performs a login with the KMS once, and
 * stores a login token (or similar) in its config directory. The next time
 * the kms_login function is called, the login token can be reused (if still
 * valid). This avoids to prompt the user for every key operation.
 *
 * @param handle            the KMS plugin handle obtained from kms_initialize()
 *
 * @returns 0 on success, or a negative errno in case of an error.
 * Function kms_get_last_error() can be used to obtain more details about the
 * error.
 */
int kms_login(const kms_handle_t handle);

enum kms_reencipher_mode {
	KMS_REENC_MODE_AUTO = 0,
	KMS_REENC_MODE_IN_PLACE = 1,
	KMS_REENC_MODE_STAGED = 2,
	KMS_REENC_MODE_STAGED_COMPLETE = 3,
};

enum kms_reenc_mkreg {
	KMS_REENC_MKREG_AUTO = 0,
	KMS_REENC_MKREG_FROM_OLD = 1,
	KMS_REENC_MKREG_TO_NEW = 2,
	KMS_REENC_MKREG_FROM_OLD_TO_NEW = 3,
};

/**
 * Called when the master keys of an APQN associated with the KMS plugin has
 * been changed. The KMS plugin can then re-encipher all its secure keys (if
 * any) that it has stored in its config directory.
 *
 * Keys that have been generated by the KMS plugin and stored in the zkey
 * repository do not need to be re-enciphered by the KMS plugin. Those are
 * re-enciphered by zkey without the help of the KMS plugin.
 *
 * HSM have different master key registers. Typically a CURRENT and a NEW master
 * key register exists. The NEW register may be loaded with the new to be set
 * master key, and secure keys can be re-enciphered with it proactively.
 *
 * CCA also supports an OLD master key register, that contains the previously
 * used master key. You thus can re-encipher a secure key that is currently
 * enciphered with the master key from the OLD register with the master key
 * from the CURRENT register.
 *
 * HSMs may also support different master keys for different key types or
 * algorithms. It is up to the KMS plugin to know which master key registers
 * are used for its secure keys
 *
 * A staged re-encipherment is performed by re-enciphering a secure key with
 * the new HSM master key, without making it available for use in the first
 * stage. Only when the staged re-encipherment is completed, then the previously
 * re-enciphered secure key is make available for use and the old on is removed.
 *
 * An in-place re-encipherment replaces the secure key right away with its
 * re-enciphered version.
 *
 * @param handle            the KMS plugin handle obtained from kms_initialize()
 * @param mode              Re-encipherment mode
 * @param mkreg             Re-encipherment register selection
 * @param options           a list of options as specified by the user. These
 *                          options are a subset of the possible options as
 *                          returned by kms_get_command_options() with command
 *                          KMS_COMMAND_REENCIPHER.
 * @param num_options       number of options in above array.
 *
 * @returns 0 on success, or a negative errno in case of an error.
 * Function kms_get_last_error() can be used to obtain more details about the
 * error.
 */
int kms_reenciper(const kms_handle_t handle,
		  enum kms_reencipher_mode mode, enum kms_reenc_mkreg mkreg,
		  const struct kms_option *options, size_t num_options);

struct kms_property {
	const char *name;
	const char *value;
};

#define KMS_KEY_ID_SIZE		256
#define KMS_KEY_LABEL_SIZE	256

enum kms_key_mode {
	KMS_KEY_MODE_NON_XTS = 0,
	KMS_KEY_MODE_XTS_1 = 1,
	KMS_KEY_MODE_XTS_2 = 2,
};

/**
 * Generates a key in or with the KMS and returns a secure key that is
 * enciphered under the current HSM master key.
 *
 * @param handle            the KMS plugin handle obtained from kms_initialize()
 * @param key_type          the zkey key type, euch as 'CCA-AESDATA',
 *                          'CCA-AESCIPHER', 'EP11-AES'.
 * @param key_bits          the key bit size (e.g. 256 for an AES 256 bit key).
 *                          0 to use the plugin's default key size.
 * @param key_mode          mode of the key, e.g. non-XTS key, or first/second
 *                          XTS key to be generated. A KMS plugin may need to
 *                          use different key templates for the different key
 *                          modes (i.e. key parts).
 * @param properties        a list of properties to associate the key with
 * @param num_properties    the number of properties in above array
 * @param options           a list of options as specified by the user. These
 *                          options are a subset of the possible options as
 *                          returned by kms_get_command_options() with command
 *                          KMS_COMMAND_GENERATE.
 * @param num_options       number of options in above array.
 * @param key_blob          a buffer to return the key blob. The size of the
 *                          buffer is specified in key_blob_length
 * @param key_blob_length   on entry: the size of the key_blob buffer.
 *                          on exit: the size of the key blob returned.
 * @param key_id            a buffer to return the key-ID of the generated key.
 *                          The key-id is a textual identifier uniquely
 *                          identifying a key in the KMS and the KMS plugin.
 *                          The returned key-id contains the terminating zero.
 * @paran key_id_size       size of the key_id buffer. It should be at least
 *                          KMS_KEY_ID_SIZE + 1 bytes large.
 * @param key_label         a buffer to return the key-label of the generated
 *                          key. The key-label is a textual identifier used to
 *                          identify a key in the user interface of the KMS.
 *                          A key label may be equal to the key-ID, or it may
 *                          different. The returned key-label contains the
 *                          terminating zero.
 * @paran key_label_size    size of the key_lanble buffer. It should be at least
 *                          KMS_KEY_LABEL_SIZE + 1 bytes large.
 *
 * @returns 0 on success, or a negative errno in case of an error.
 * Function kms_get_last_error() can be used to obtain more details about the
 * error.
 */
int kms_generate_key(const kms_handle_t handle, const char *key_type,
		     size_t key_bits, enum kms_key_mode key_mode,
		     const struct kms_property *properties,
		     size_t num_properties, const struct kms_option *options,
		     size_t num_options, unsigned char *key_blob,
		     size_t *key_blob_length, char *key_id,
		     size_t key_id_size, char *key_label,
		     size_t key_label_size);

/**
 * Sets (adds/replaces/removes) properties of a key. Already existing properties
 * with the same property name are replaced, non-existing properties are added.
 * To remove a property, set the property value to NULL.
 *
 * @param handle            the KMS plugin handle obtained from kms_initialize()
 * @param key_id            the key-ID to set the properties for
 * @param properties        a list of properties to set
 * @param num_properties    the number of properties in above array
 *
 * @returns 0 on success, or a negative errno in case of an error.
 * Function kms_get_last_error() can be used to obtain more details about the
 * error.
 */
int kms_set_key_properties(const kms_handle_t handle, const char *key_id,
			   const struct kms_property *properties,
			   size_t num_properties);

/**
 * Gets properties of a key.
 *
 * The returned list of properties must be freed by the caller. Each property
 * name and value must be freed individually (using free()), as well as the
 * complete array.
 *
 * @param handle            the KMS plugin handle obtained from kms_initialize()
 * @param key_id            the key-ID to set the properties for
 * @param properties        On return: a list of properties
 * @param num_properties    On return: the number of properties in above array
 *
 * @returns 0 on success, or a negative errno in case of an error.
 * Function kms_get_last_error() can be used to obtain more details about the
 * error.
 */
int kms_get_key_properties(const kms_handle_t handle, const char *key_id,
			   struct kms_property **properties,
			   size_t *num_properties);

/**
 * Called when zkey removes a KMS-bound key from the zkey repository. The KMS
 * plugin can then set the state of the key in the KMS, or remove it also from
 * the KMS (this is usually not done).
 *
 * @param handle            the KMS plugin handle obtained from kms_initialize()
 * @param key_id            the key-ID to set the properties for
 * @param options           a list of options as specified by the user. These
 *                          options are a subset of the possible options as
 *                          returned by kms_get_command_options() with command
 *                          KMS_COMMAND_REMOVE.
 * @param num_options       number of options in above array.
 *
 * @returns 0 on success, or a negative errno in case of an error.
 * Function kms_get_last_error() can be used to obtain more details about the
 * error.
 */
int kms_remove_key(const kms_handle_t handle, const char *key_id,
		   const struct kms_option *options, size_t num_options);

/**
 * Callback used with the kms_list_keys() function. Called for each key.
 *
 * @param key_id            the key-ID of the key
 * @param label             the label of the key.
 * @param key_type          the type of the key (CCA-AESDATA, etc)
 * @param key_bits          the key size in bits
 * @param properties        a list of properties of the key
 * @param num_properties    the number of properties in above array
 * @param addl_info_argz    an argz string containing additional KMS plugin
 *                           specific infos to be displayed, or NULL if none.
 * @param addl_info_len     length of the argz string in addl_info_argz
 * @param private_data      the private data pointer
 *
 * @returns 0 on success, or a negative errno in case of an error.
 */
typedef int (*kms_list_callback)(const char *key_id, const char *label,
				 const char *key_type, size_t key_bits,
				 const struct kms_property *properties,
				 size_t num_properties,
				 const char *addl_info_argz,
				 size_t addl_info_len, void *private_data);

/**
 * List keys managed by the KMS. This list is independent of the zkey key
 * repository. It lists keys as known by the KMS.
 *
 * Note: The list function is used to display a list of keys managed by the KMS,
 * but also for producing a list of keys to import. Because the two use cases
 * might require different plugin specific options, kms_get_command_options()
 * allows two commands to get options for:
 * - KMS_COMMAND_LIST:        Options for displaying a list.
 * - KMS_COMMAND_LIST_IMPORT: Options for building a list to import keys.
 * Functionkms_list_keys() is called for both cases with the appropriate
 * options as returned by kms_get_command_options() for the case.
 *
 * @param handle            the KMS plugin handle obtained from kms_initialize()
 * @param label_pattern     a pattern of the label used to filter the keys, or
 *                          NULL if no label pattern is specified.
 * @param properties        a list of properties used to to filter the keys, or
 *                          NULL if no properties filter is specified.
 * @param num_properties    the number of properties in above array.
 * @param options           a list of options as specified by the user. These
 *                          options are a subset of the possible options as
 *                          returned by kms_get_command_options() with command
 *                          KMS_COMMAND_LIST or KMS_COMMAND_LIST_IMPORT.
 * @param num_options       number of options in above array.*
 * @param callback          a callback function that is called for each key that
 *                          matches the filter (if any).
 * @private_data            a private pointer passed as is to the callback
 *                          function. Can be used to pass user specific
 *                          information to the callback.
 *
 * @returns 0 on success, or a negative errno in case of an error.
 * Function kms_get_last_error() can be used to obtain more details about the
 * error.
 */
int kms_list_keys(const kms_handle_t handle, const char *label_pattern,
		  const struct kms_property *properties, size_t num_properties,
		  const struct kms_option *options, size_t num_options,
		  kms_list_callback callback, void *private_data);

/**
 * Imports a key from the KMS and returns a secure key that is
 * enciphered under the current HSM master key.
 *
 * Note: This function is used for an API version 1 plugin. See function
 * kms_import_key2 for the version 2 equivalent.
 *
 * @param handle            the KMS plugin handle obtained from kms_initialize()
 * @param key_id            the key-ID of the key to import
 * @param key_blob          a buffer to return the key blob. The size of the
 *                          buffer is specified in key_blob_length
 * @param key_blob_length   on entry: the size of the key_blob buffer.
 *                          on exit: the size of the key blob returned.
 *
 * @returns 0 on success, or a negative errno in case of an error.
 * Function kms_get_last_error() can be used to obtain more details about the
 * error.
 */
int kms_import_key(const kms_handle_t handle, const char *key_id,
		   unsigned char *key_blob, size_t *key_blob_length);

/**
 * Imports a key from the KMS and returns a secure key that is
 * enciphered under the current HSM master key.
 *
 * Note: This function should be available for an API version 2 plugin.
 * The difference to functionkms_import_key is that it also get the
 * desired key type of the key to import.
 *
 * @param handle            the KMS plugin handle obtained from kms_initialize()
 * @param key_id            the key-ID of the key to import
 * @param key_type          the zkey key type, such as 'CCA-AESDATA',
 *                          'CCA-AESCIPHER', 'EP11-AES'. If NULL, then the
 *                          plugin can choose its own default.
 * @param key_blob          a buffer to return the key blob. The size of the
 *                          buffer is specified in key_blob_length
 * @param key_blob_length   on entry: the size of the key_blob buffer.
 *                          on exit: the size of the key blob returned.
 *
 * @returns 0 on success, or a negative errno in case of an error.
 * Function kms_get_last_error() can be used to obtain more details about the
 * error.
 *
 */
int kms_import_key2(const kms_handle_t handle, const char *key_id,
		    const char *key_type,
		    unsigned char *key_blob, size_t *key_blob_length);

#define KMS_API_VERSION_1	1
#define KMS_API_VERSION_2	2

struct kms_functions {
	unsigned int api_version;
	int (*kms_bind)(const char *config_path);
	kms_handle_t (*kms_initialize)(const char *config_path, bool verbose);
	int (*kms_terminate)(const kms_handle_t handle);
	const char *(*kms_get_last_error)(const kms_handle_t handle);
	bool (*kms_supports_key_type)(const kms_handle_t handle,
				      const char *key_type);
	int (*kms_display_info)(const kms_handle_t handle);
	const struct util_opt *(*kms_get_command_options)(
			const char *command, int max_opts);
	int (*kms_configure)(const kms_handle_t handle,
			     const struct kms_apqn *apqns, size_t num_apqns,
			     const struct kms_option *options,
			     size_t num_options);
	int (*kms_deconfigure)(const kms_handle_t handle);
	int (*kms_login)(const kms_handle_t handle);
	int (*kms_reenciper)(const kms_handle_t handle,
			     enum kms_reencipher_mode mode,
			     enum kms_reenc_mkreg mkreg,
			     const struct kms_option *options,
			     size_t num_options);
	int (*kms_generate_key)(const kms_handle_t handle,
				const char *key_type, size_t key_bits,
				enum kms_key_mode key_mode,
				const struct kms_property *properties,
				size_t num_properties,
				const struct kms_option *options,
				size_t num_options,
				unsigned char *key_blob,
				size_t *key_blob_length, char *key_id,
				size_t key_id_size, char *key_label,
				size_t key_label_size);
	int (*kms_set_key_properties)(const kms_handle_t handle,
				      const char *key_id,
				      const struct kms_property *properties,
				      size_t num_properties);
	int (*kms_get_key_properties)(const kms_handle_t handle,
				      const char *key_id,
				      struct kms_property **properties,
				      size_t *num_properties);
	int (*kms_remove_key)(const kms_handle_t handle, const char *key_id,
			      const struct kms_option *options,
			      size_t num_options);
	int (*kms_list_keys)(const kms_handle_t handle,
			     const char *label_pattern,
			     const struct kms_property *properties,
			     size_t num_properties,
			     const struct kms_option *options,
			     size_t num_options,
			     kms_list_callback callback, void *private_data);
	int (*kms_import_key)(const kms_handle_t handle, const char *key_id,
			      unsigned char *key_blob,
			      size_t *key_blob_length);
	/* Version 2 functions. Only used when api_version is >= 2. */
	int (*kms_import_key2)(const kms_handle_t handle, const char *key_id,
			      const char *key_type,
			      unsigned char *key_blob,
			      size_t *key_blob_length);
};

/**
 * Returns an address of a structure containing the KMS plugin functions.
 * This function is exported by the KMS plugin, and its address is obtain
 * via dlsym() after loading the plugin via dlopen().
 * *
 * @returns the address of a structure or NULL in case of an error.
 */
const struct kms_functions *kms_get_functions(void);

#endif
