/*
 * libkmipclient - KMIP client library
 *
 * Copyright IBM Corp. 2021
 *
 * s390-tools is free software; you can redistribute it and/or modify
 * it under the terms of the MIT license. See LICENSE for details.
 */

#define _XOPEN_SOURCE
#define _DEFAULT_SOURCE

#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>

#include "kmip.h"
#include "names.h"

/**
 * Constructs a Key Block node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Key Block                               Structure     v1.0
 *     Key Format Type             Yes       Enumeration   v1.0
 *     Key Compression Type        No        Enumeration   v1.0
 *     Key Value                   Yes       various       v1.0
 *     Cryptographic Algorithm     Yes       Enumeration   v1.0
 *     Cryptographic Length        Yes       Integer       v1.0
 *     Key Wrapping Data           No        Structure     v1.0
 *
 * @param format_type       the key format type
 * @param format_type       the key compression type (if 0 it is ignored)
 * @param key_value         the key value node
 * @param algorithm         the key algorithm (if 0 it is ignored)
 * @param length            the cryptographic length (if <= 0 it is ignored)
 * @param wrappig_data      the key wrapping data (can be NULL)
 *
 * @returns the allocated node, or NULL in case of an error.
 * The reference counts of the nodes specified as parameters which are added to
 * the newly allocated node are increased. The caller must free its reference
 * via kmip_node_free() if no longer needed.
 */
struct kmip_node *kmip_new_key_block(enum kmip_key_format_type format_type,
				     enum kmip_key_compression_type compr_type,
				     struct kmip_node *key_value,
				     enum kmip_crypto_algo algorithm,
				     int32_t length,
				     struct kmip_node *wrappig_data)
{
	struct kmip_node *ret = NULL, *fmt, *cmp = NULL, *algo = NULL;
	struct kmip_node *len = NULL;

	if (format_type == 0 || key_value == NULL)
		return NULL;

	fmt = kmip_node_new_enumeration(KMIP_TAG_KEY_FORMAT_TYPE, NULL,
					format_type);
	if (fmt == NULL)
		goto out;

	if (compr_type != 0) {
		cmp = kmip_node_new_enumeration(KMIP_TAG_KEY_COMPRESSION_TYPE,
						NULL, compr_type);
		if (cmp == NULL)
			goto out;
	}

	if (algorithm != 0) {
		algo = kmip_node_new_enumeration(
					KMIP_TAG_CRYPTOGRAPHIC_ALGORITHM,
					NULL, algorithm);
		if (algo == NULL)
			goto out;
	}

	if (length > 0) {
		len = kmip_node_new_integer(KMIP_TAG_CRYPTOGRAPHIC_LENGTH, NULL,
					    length);
		if (len == NULL)
			goto out;
	}

	ret = kmip_node_new_structure_va(KMIP_TAG_KEY_BLOCK, NULL, 6, fmt, cmp,
					 key_value, algo, len, wrappig_data);

out:
	kmip_node_free(fmt);
	kmip_node_free(cmp);
	kmip_node_free(algo);
	kmip_node_free(len);

	return ret;
}

/**
 * Gets information from a Key Block node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Key Block                               Structure     v1.0
 *     Key Format Type             Yes       Enumeration   v1.0
 *     Key Compression Type        No        Enumeration   v1.0
 *     Key Value                   Yes       various       v1.0
 *     Cryptographic Algorithm     Yes       Enumeration   v1.0
 *     Cryptographic Length        Yes       Integer       v1.0
 *     Key Wrapping Data           No        Structure     v1.0
 *
 * @param node              the KMIP node
 * @param format_type       On return: the key format type
 * @param format_type       On return: the key compression type (0 if not avail,
 *                          can be NULL)
 * @param key_value         On return: the key value node (can be NULL)
 * @param algorithm         On return: the key algorithm (0 if not avail, can
 *                          be NULL)
 * @param length            On return: the cryptographic length (0 if not avail,
 *                          can be NULL)
 * @param wrappig_data      On return: the key wrapping data (can be NULL)
 *
 * @returns 0 on success, or a negative errno in case of an error.
 * The reference count of the returned nodes is increased. The caller must
 * free the node via kmip_node_free() when no longer needed.
 */
int kmip_get_key_block(const struct kmip_node *node,
		       enum kmip_key_format_type *format_type,
		       enum kmip_key_compression_type *compr_type,
		       struct kmip_node **key_value,
		       enum kmip_crypto_algo *algorithm,
		       int32_t *length,
		       struct kmip_node **wrappig_data)
{
	struct kmip_node *n;

	if (node == NULL)
		return -EINVAL;

	if (kmip_node_get_tag(node) != KMIP_TAG_KEY_BLOCK)
		return -EBADMSG;

	if (format_type != NULL) {
		n = kmip_node_get_structure_element_by_tag(node,
						KMIP_TAG_KEY_FORMAT_TYPE, 0);
		if (n == NULL)
			return -EBADMSG;
		*format_type = kmip_node_get_enumeration(n);
		kmip_node_free(n);
	}

	if (compr_type != NULL) {
		n = kmip_node_get_structure_element_by_tag(node,
					KMIP_TAG_KEY_COMPRESSION_TYPE, 0);
		if (n != NULL)
			*compr_type = kmip_node_get_enumeration(n);
		else
			*compr_type = 0;
		kmip_node_free(n);
	}

	if (algorithm != NULL) {
		n = kmip_node_get_structure_element_by_tag(node,
					KMIP_TAG_CRYPTOGRAPHIC_ALGORITHM, 0);
		if (n != NULL)
			*algorithm = kmip_node_get_enumeration(n);
		else
			*algorithm = 0;
		kmip_node_free(n);
	}

	if (length != NULL) {
		n = kmip_node_get_structure_element_by_tag(node,
					KMIP_TAG_CRYPTOGRAPHIC_LENGTH, 0);
		if (n != NULL)
			*length = kmip_node_get_integer(n);
		else
			*length = -1;
		kmip_node_free(n);
	}

	if (key_value != NULL) {
		*key_value = kmip_node_get_structure_element_by_tag(node,
							KMIP_TAG_KEY_VALUE, 0);
		if (*key_value == NULL)
			return -EBADMSG;
	}

	if (wrappig_data != NULL)
		*wrappig_data = kmip_node_get_structure_element_by_tag(node,
						KMIP_TAG_KEY_WRAPPING_DATA, 0);

	return 0;
}

/**
 * Constructs a Key Value node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Key Value                               Structure     v1.0
 *     Key Material                Yes       various       v1.0
 *     Attribute                   No        Structure     v1.x only
 *     ... may be repeated
 *     Attributes                  No        Structure     v2.x only
 *
 * @param version           the protocol version. If null, the current default
 *                          protocol version is used.
 * @param key_material      the key material node
 * @param attrs_count       the number of attributes following (can be 0)
 * @param v2_attrs          the array of attributes (as KMIP v2.x attribute)
 *
 * @returns the allocated node, or NULL in case of an error.
 * The reference counts of the nodes specified as parameters which are added to
 * the newly allocated node are increased. The caller must free its reference
 * via kmip_node_free() if no longer needed.
 */
struct kmip_node *kmip_new_key_value(const struct kmip_version *version,
				     struct kmip_node *key_material,
				     unsigned int attrs_count,
				     struct kmip_node **v2_attrs)
{
	struct kmip_node *ret = NULL, *v2_attr, *v1_attr, *attrs = NULL;
	unsigned int i;
	int rc;

	if (key_material == NULL)
		return NULL;
	if (attrs_count > 0 && v2_attrs == NULL)
		return NULL;

	if (version == NULL)
		version = kmip_get_default_protocol_version();

	if (version->major == 1) {
		/* KMIP v1.x */
		ret = kmip_node_new_structure_va(KMIP_TAG_KEY_VALUE, NULL, 1,
						 key_material);
		if (ret == NULL)
			return NULL;

		for (i = 0; i < attrs_count; i++) {
			v2_attr = v2_attrs[i];
			if (v2_attr == NULL)
				continue;

			rc = kmip_v1_attr_from_v2_attr(v2_attr, &v1_attr);
			if (rc != 0)
				goto error;

			rc = kmip_node_add_structure_element(ret, v1_attr);
			kmip_node_free(v1_attr);
			if (rc != 0)
				goto error;
		}
	} else {
		/* KMIP >= v2.0 */
		if (attrs_count > 0) {
			attrs = kmip_new_attributes(version,
						    KMIP_TAG_ATTRIBUTES,
						    attrs_count, v2_attrs);
			if (attrs == NULL)
				return NULL;
		}

		ret = kmip_node_new_structure_va(KMIP_TAG_KEY_VALUE, NULL, 2,
						 key_material, attrs);
		kmip_node_free(attrs);
	}

	return ret;

error:
	kmip_node_free(ret);
	return NULL;
}

/**
 * Constructs a Key Value node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Key Value                               Structure     v1.0
 *     Key Material                Yes       various       v1.0
 *     Attribute                   No        Structure     v1.x only
 *     ... may be repeated
 *     Attributes                  No        Structure     v2.x only
 *
 * @param version           the protocol version. If null, the current default
 *                          protocol version is used.
 * @param key_material      the key material node
 * @param attrs_count       the number of attributes following (can be 0)
 * @param <attributes>      the attributes (as KMIP v2.x attribute)
 *
 * @returns the allocated node, or NULL in case of an error.
 * The reference counts of the nodes specified as parameters which are added to
 * the newly allocated node are increased. The caller must free its reference
 * via kmip_node_free() if no longer needed.
 */
struct kmip_node *kmip_new_key_value_va(const struct kmip_version *version,
					struct kmip_node *key_material,
					unsigned int attrs_count, ...)
{
	struct kmip_node *ret, **attrs = NULL;
	unsigned int i, k;
	va_list ap;

	if (attrs_count > 0) {
		attrs = calloc(attrs_count, sizeof(struct kmip_node *));
		if (attrs == NULL)
			return NULL;
	}

	va_start(ap, attrs_count);
	for (i = 0, k = 0; i < attrs_count; i++) {
		attrs[k] = va_arg(ap, struct kmip_node *);
		if (attrs[k] != NULL)
			k++;
	}
	va_end(ap);

	ret = kmip_new_key_value(version, key_material, k, attrs);
	if (attrs != NULL)
		free(attrs);

	return ret;
}

/**
 *Gets information from a Key Value node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Key Value (wrapped key value)           Byte String   v1.0
 *   Key Value (plaintext key value)         Structure     v1.0
 *     Key Material                Yes       various       v1.0
 *     Attribute                   No        Structure     v1.x only
 *     ... may be repeated
 *     Attributes                  No        Structure     v2.x only
 *
 * @param node              the KMIP node
 * @param key_material      On return: the key material node (can be NULL)
 * @param num_attrs         On return: the number of attributes (can be NULL).
 * @param index             the index of the attribute to get
 * @param v2_attr           On return: the attribute (as v2.x attribute) at the
 *                          specified index. Function returns -ENOENT if no
 *                          attribute is available at the index.
 *
 * @returns 0 on success, or a negative errno in case of an error.
 * The reference count of the returned nodes is increased. The caller must
 * free the node via kmip_node_free() when no longer needed.
 */
int kmip_get_key_value(const struct kmip_node *node,
		       struct kmip_node **key_material,
		       unsigned int *num_attrs, unsigned int index,
		       struct kmip_node **v2_attr)
{
	struct kmip_node *attr;
	int rc;

	if (node == NULL)
		return -EINVAL;

	if (kmip_node_get_tag(node) != KMIP_TAG_KEY_VALUE)
		return -EBADMSG;

	if (key_material != NULL) {
		switch (kmip_node_get_type(node)) {
		case KMIP_TYPE_BYTE_STRING:
			/* Wrapped key value */
			*key_material = (struct kmip_node *)node;
			kmip_node_upref(*key_material);
			break;

		case KMIP_TYPE_STRUCTURE:
			/* plaintext key value */
			*key_material =
				kmip_node_get_structure_element_by_index(node,
									 0);
			if (*key_material == NULL)
				return -EBADMSG;

			switch (kmip_node_get_type(*key_material)) {
			case KMIP_TYPE_BYTE_STRING:
				/* Raw, Opaque, PKCS1, PKCS8, ECPrivateKey */
				break;
			case KMIP_TYPE_STRUCTURE:
				/* Transparent key formats */
				switch (kmip_node_get_tag(*key_material)) {
				/* Transparent key formats: TAG_KEY_MATERIAL */
				case KMIP_TAG_KEY_MATERIAL:
					break;
				default:
					rc = -EBADMSG;
					goto error;
				}
				break;
			default:
				rc = -EBADMSG;
				goto error;
			}
			break;

		default:
			return -EBADMSG;
		}
	}

	if (v2_attr == NULL || kmip_node_get_type(node) != KMIP_TYPE_STRUCTURE)
		return 0;

	attr = kmip_node_get_structure_element_by_index(node, 1);
	if (attr == NULL) {
		rc = -ENOENT;
		goto error;
	}

	if (kmip_node_get_tag(attr) == KMIP_TAG_ATTRIBUTES) {
		/* Its already a KMIP v2.x attributes structure */
		rc = kmip_get_attributes(attr, num_attrs, index, v2_attr);
		kmip_node_free(attr);
		if (rc != 0)
			goto error;
		return 0;
	}

	/* Must be a KMIP v1.x attribute then */
	kmip_node_free(attr);

	if (num_attrs != NULL)
		*num_attrs = kmip_node_get_structure_element_count(node) - 1;

	if (v2_attr == NULL)
		return 0;

	attr = kmip_node_get_structure_element_by_index(node, index + 1);
	if (attr == NULL) {
		rc = -ENOENT;
		goto error;
	}

	rc = kmip_v2_attr_from_v1_attr(attr, v2_attr);
	kmip_node_free(attr);
	if (rc != 0)
		goto error;

	return 0;

error:
	if (key_material != NULL) {
		kmip_node_free(*key_material);
		*key_material = NULL;
	}

	return rc;
}

/**
 * Constructs a Key Wrapping Data node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Key Wrapping Data                       Structure     v1.0
 *     Wrapping Method             Yes       Enumeration   v1.0
 *     Encryption Key Information  No        Structure     v1.0
 *     MAC/Signature Key Info.     No        Structure     v1.0
 *     MAC/Signature               No        Byte String   v1.0
 *     IV/Counter/Nonce            No        Byte String   v1.0
 *     Encoding Option             No        Enumeration   v1.2
 *
 * @param version           the protocol version. If null, the current default
 *                          protocol version is used.
 * @param wrap_method       the key wrapping method
 * @param encr_key_info     the encryption key info node (can be NULL)
 * @param mac_sign_key_info the MAC/Sign key info node (can be NULL)
 * @param mac_signature     MAC/signature (can be NULL)
 * @param mac_signature_len the length of the MAC/Signature
 * @param iv_counter_nonce  IV/Counter/Nonce (can be NULL)
 * @param iv_counter_nonce_len the length of theIV/Counter/Nonce
 * @param encoding          the encoding option (can be 0, defaults to TTLV)
 *
 * @returns the allocated node, or NULL in case of an error.
 * The reference counts of the nodes specified as parameters which are added to
 * the newly allocated node are increased. The caller must free its reference
 * via kmip_node_free() if no longer needed.
 */
struct kmip_node *kmip_new_key_wrapping_data(
					const struct kmip_version *version,
					enum kmip_wrapping_method wrap_method,
					struct kmip_node *encr_key_info,
					struct kmip_node *mac_sign_key_info,
					const unsigned char *mac_signature,
					uint32_t mac_signature_len,
					const unsigned char *iv_counter_nonce,
					uint32_t iv_counter_nonce_len,
					enum kmip_encoding_option encoding)
{
	struct kmip_node *ret = NULL, *wmeth, *mac = NULL, *iv = NULL;
	struct kmip_node *enc = NULL;

	if (wrap_method == 0)
		return NULL;

	if (version == NULL)
		version = kmip_get_default_protocol_version();

	wmeth = kmip_node_new_enumeration(KMIP_TAG_WRAPPING_METHOD, NULL,
					  wrap_method);
	if (wmeth == NULL)
		goto out;

	if (mac_signature != NULL && mac_signature_len > 0) {
		mac = kmip_node_new_byte_string(KMIP_TAG_MAC_SIGNATURE,
						NULL, mac_signature,
						mac_signature_len);
		if (mac == NULL)
			goto out;
	}

	if (iv_counter_nonce != NULL && iv_counter_nonce_len > 0) {
		iv = kmip_node_new_byte_string(KMIP_TAG_IV_COUNTER_NONCE,
					       NULL, iv_counter_nonce,
					       iv_counter_nonce_len);
		if (iv == NULL)
			goto out;
	}

	if (encoding != 0 && (version->major > 1 ||
			      (version->major == 1 && version->minor > 1))) {
		enc = kmip_node_new_enumeration(KMIP_TAG_ENCODING_OPTION, NULL,
						encoding);
		if (enc == NULL)
			goto out;
	}

	ret = kmip_node_new_structure_va(KMIP_TAG_KEY_WRAPPING_DATA, NULL, 6,
					 wmeth, encr_key_info,
					 mac_sign_key_info, mac, iv, enc);

out:
	kmip_node_free(wmeth);
	kmip_node_free(mac);
	kmip_node_free(iv);
	kmip_node_free(enc);

	return ret;
}

/**
 *Gets information from a Key Wrapping Data node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Key Wrapping Data                       Structure     v1.0
 *     Wrapping Method             Yes       Enumeration   v1.0
 *     Encryption Key Information  No        Structure     v1.0
 *     MAC/Signature Key Info.     No        Structure     v1.0
 *     MAC/Signature               No        Byte String   v1.0
 *     IV/Counter/Nonce            No        Byte String   v1.0
 *     Encoding Option             No        Enumeration   v1.2
 *
 * @param node              the KMIP node
 * @param wrap_method       On return: the key wrapping method (can be NULL)
 * @param encr_key_info     On return: the encryption key info node
 *                          (can be NULL)
 * @param mac_sign_key_info On return: the MAC/Sign key info node (can be NULL)
 * @param mac_signature     On return: MAC/signature (can be NULL)
 * @param mac_signature_len On return: the length of the MAC/Signature
 *                          (can be NULL)
 * @param iv_counter_nonce  On return: IV/Counter/Nonce (can be NULL)
 * @param iv_counter_nonce_len On return: the length of theIV/Counter/Nonce
 *                          (can be NULL)
 * @param encoding          On return: the encoding option (can be NULL)
 *
 * @returns 0 on success, or a negative errno in case of an error.
 * The reference count of the returned nodes is increased. The caller must
 * free the node via kmip_node_free() when no longer needed.
 */
int kmip_get_key_wrapping_data(const struct kmip_node *node,
			       enum kmip_wrapping_method *wrap_method,
			       struct kmip_node **encr_key_info,
			       struct kmip_node **mac_sign_key_info,
			       const unsigned char **mac_signature,
			       uint32_t *mac_signature_len,
			       const unsigned char **iv_counter_nonce,
			       uint32_t *iv_counter_nonce_len,
			       enum kmip_encoding_option *encoding)
{
	struct kmip_node *n;

	if (node == NULL)
		return -EINVAL;

	if (kmip_node_get_tag(node) != KMIP_TAG_KEY_WRAPPING_DATA)
		return -EBADMSG;

	if (wrap_method != NULL) {
		n = kmip_node_get_structure_element_by_tag(node,
						KMIP_TAG_WRAPPING_METHOD, 0);
		if (n == NULL)
			return -EBADMSG;
		*wrap_method = kmip_node_get_enumeration(n);
		kmip_node_free(n);
	}

	if (mac_signature != NULL && mac_signature_len != NULL) {
		n = kmip_node_get_structure_element_by_tag(node,
					KMIP_TAG_MAC_SIGNATURE, 0);
		if (n != NULL) {
			*mac_signature = kmip_node_get_byte_string(n,
							mac_signature_len);
		} else {
			*mac_signature = NULL;
			*mac_signature_len = 0;
		}
		kmip_node_free(n);
	}

	if (iv_counter_nonce != NULL && iv_counter_nonce_len != NULL) {
		n = kmip_node_get_structure_element_by_tag(node,
					KMIP_TAG_IV_COUNTER_NONCE, 0);
		if (n != NULL) {
			*iv_counter_nonce = kmip_node_get_byte_string(n,
							iv_counter_nonce_len);
		} else {
			*iv_counter_nonce = NULL;
			*iv_counter_nonce_len = 0;
		}
		kmip_node_free(n);
	}

	if (encoding != NULL) {
		n = kmip_node_get_structure_element_by_tag(node,
						KMIP_TAG_ENCODING_OPTION, 0);

		*encoding = (n != NULL ? kmip_node_get_enumeration(n) : 0);
		kmip_node_free(n);
	}

	if (encr_key_info != NULL)
		*encr_key_info = kmip_node_get_structure_element_by_tag(node,
					KMIP_TAG_ENCRYPTION_KEY_INFORMATION, 0);

	if (mac_sign_key_info != NULL)
		*mac_sign_key_info =
			kmip_node_get_structure_element_by_tag(node,
				KMIP_TAG_MAC_SIGNATURE_KEY_INFORMATION, 0);

	return 0;
}

/**
 * Constructs a Key Wrapping Specification node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Key Wrapping Specification              Structure     v1.0
 *     Wrapping Method             Yes       Enumeration   v1.0
 *     Encryption Key Information  No        Structure     v1.0
 *     MAC/Signature Key Info.     No        Structure     v1.0
 *     Attribute Name              No        Text String   v1.0
 *     ... may be repeated
 *     Encoding Option             No        Enumeration   v1.2
 *
 * @param version           the protocol version. If null, the current default
 *                          protocol version is used.
 * @param wrap_method       the key wrapping method
 * @param encr_key_info     the encryption key info node (can be NULL)
 * @param mac_sign_key_info the MAC/Sign key info node (can be NULL)
 * @param encoding          the encoding option (can be 0, defaults to TTLV)
 * @param attr_name_count   the number of attribute names following
 * @param attr_names        the array of attributes names (as const char *)
 *
 * @returns the allocated node, or NULL in case of an error.
 * The reference counts of the nodes specified as parameters which are added to
 * the newly allocated node are increased. The caller must free its reference
 * via kmip_node_free() if no longer needed.
 */
struct kmip_node *kmip_new_key_wrapping_specification(
					const struct kmip_version *version,
					enum kmip_wrapping_method wrap_method,
					struct kmip_node *encr_key_info,
					struct kmip_node *mac_sign_key_info,
					enum kmip_encoding_option encoding,
					unsigned int attr_name_count,
					const char **attr_names)
{
	struct kmip_node *ret = NULL, *wmeth, *enc = NULL, *name;
	unsigned int i;
	int rc;

	if (wrap_method == 0)
		return NULL;

	if (version == NULL)
		version = kmip_get_default_protocol_version();

	wmeth = kmip_node_new_enumeration(KMIP_TAG_WRAPPING_METHOD, NULL,
					  wrap_method);
	if (wmeth == NULL)
		goto out;

	ret = kmip_node_new_structure_va(KMIP_TAG_KEY_WRAPPING_SPECIFICATION,
					 NULL, 3, wmeth, encr_key_info,
					 mac_sign_key_info);


	for (i = 0; i < attr_name_count; i++) {
		if (attr_names[i] == NULL)
			continue;

		name = kmip_node_new_text_string(KMIP_TAG_ATTRIBUTE_NAME, NULL,
						 attr_names[i]);
		if (name == NULL)
			goto error;

		rc = kmip_node_add_structure_element(ret, name);
		kmip_node_free(name);
		if (rc != 0)
			goto error;
	}

	if (encoding != 0 && (version->major > 1 ||
			      (version->major == 1 && version->minor > 1))) {
		enc = kmip_node_new_enumeration(KMIP_TAG_ENCODING_OPTION, NULL,
						encoding);
		if (enc == NULL)
			goto error;

		rc = kmip_node_add_structure_element(ret, enc);
		if (rc != 0)
			goto error;
	}
	goto out;

error:
	kmip_node_free(ret);
	ret = NULL;

out:
	kmip_node_free(wmeth);
	kmip_node_free(enc);

	return ret;
}

/**
 * Constructs a Key Wrapping Specification node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Key Wrapping Specification              Structure     v1.0
 *     Wrapping Method             Yes       Enumeration   v1.0
 *     Encryption Key Information  No        Structure     v1.0
 *     MAC/Signature Key Info.     No        Structure     v1.0
 *     Attribute Name              No        Text String   v1.0
 *     ... may be repeated
 *     Encoding Option             No        Enumeration   v1.2
 *
 * @param version           the protocol version. If null, the current default
 *                          protocol version is used.
 * @param wrap_method       the key wrapping method
 * @param encr_key_info     the encryption key info node (can be NULL)
 * @param mac_sign_key_info the MAC/Sign key info node (can be NULL)
 * @param encoding          the encoding option (can be 0, defaults to TTLV)
 * @param attr_name_count   the number of atribute names following
 * @param <attr names>      the attributes names (as const char *)
 *
 * @returns the allocated node, or NULL in case of an error.
 * The reference counts of the nodes specified as parameters which are added to
 * the newly allocated node are increased. The caller must free its reference
 * via kmip_node_free() if no longer needed.
 */
struct kmip_node *kmip_new_key_wrapping_specification_va(
					const struct kmip_version *version,
					enum kmip_wrapping_method wrap_method,
					struct kmip_node *encr_key_info,
					struct kmip_node *mac_sign_key_info,
					enum kmip_encoding_option encoding,
					unsigned int attr_name_count, ...)
{
	const char **names = NULL;
	struct kmip_node *ret;
	unsigned int i;
	va_list ap;

	if (attr_name_count > 0) {
		names = calloc(attr_name_count, sizeof(const char *));
		if (names == NULL)
			return NULL;
	}

	va_start(ap, attr_name_count);
	for (i = 0; i < attr_name_count; i++)
		names[i] = va_arg(ap, const char *);
	va_end(ap);

	ret = kmip_new_key_wrapping_specification(version, wrap_method,
						  encr_key_info,
						  mac_sign_key_info, encoding,
						  attr_name_count, names);
	if (names != NULL)
		free(names);

	return ret;
}


/**
 *Gets information from a Key Wrapping Specification node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Key Wrapping Specification              Structure     v1.0
 *     Wrapping Method             Yes       Enumeration   v1.0
 *     Encryption Key Information  No        Structure     v1.0
 *     MAC/Signature Key Info.     No        Structure     v1.0
 *     Attribute Name              No        Text String   v1.0
 *     ... may be repeated
 *     Encoding Option             No        Enumeration   v1.2

 *
 * @param node              the KMIP node
 * @param wrap_method       On return: the key wrapping method (can be NULL)
 * @param encr_key_info     On return: the encryption key info node
 *                          (can be NULL)
 * @param mac_sign_key_info On return: the MAC/Sign key info node (can be NULL)
 * @param encoding          On return: the encoding option (can be NULL)
 * @param num_attr_names    On return: the number of attributes (can be NULL).
 * @param attr_name_index   The index of the attribute name to return
 * @param attr_name         On return: the attribute name at the specified index
 *                          (can be NULL). Function returns -ENOENT if no name
 *                          is available at the index.
 *
 * @returns 0 on success, or a negative errno in case of an error.
 * The reference count of the returned nodes is increased. The caller must
 * free the node via kmip_node_free() when no longer needed.
 */
int kmip_get_key_wrapping_specification(const struct kmip_node *node,
			       enum kmip_wrapping_method *wrap_method,
			       struct kmip_node **encr_key_info,
			       struct kmip_node **mac_sign_key_info,
			       enum kmip_encoding_option *encoding,
			       unsigned int *num_attr_names,
			       unsigned int attr_name_index,
			       const char **attr_name)
{
	struct kmip_node *n;

	if (node == NULL)
		return -EINVAL;

	if (kmip_node_get_tag(node) != KMIP_TAG_KEY_WRAPPING_SPECIFICATION)
		return -EBADMSG;

	if (wrap_method != NULL) {
		n = kmip_node_get_structure_element_by_tag(node,
						KMIP_TAG_WRAPPING_METHOD, 0);
		if (n == NULL)
			return -EBADMSG;
		*wrap_method = kmip_node_get_enumeration(n);
		kmip_node_free(n);
	}

	if (encoding != NULL) {
		n = kmip_node_get_structure_element_by_tag(node,
						KMIP_TAG_ENCODING_OPTION, 0);
		*encoding = (n != NULL ? kmip_node_get_enumeration(n) : 0);
		kmip_node_free(n);
	}

	if (num_attr_names != NULL)
		*num_attr_names = kmip_node_get_structure_element_by_tag_count(
						node, KMIP_TAG_ATTRIBUTE_NAME);

	if (attr_name != NULL) {
		n = kmip_node_get_structure_element_by_tag(node,
				KMIP_TAG_ATTRIBUTE_NAME, attr_name_index);
		if (n == NULL)
			return -ENOENT;
		*attr_name = kmip_node_get_text_string(n);
		kmip_node_free(n);
	}

	if (encr_key_info != NULL)
		*encr_key_info = kmip_node_get_structure_element_by_tag(node,
					KMIP_TAG_ENCRYPTION_KEY_INFORMATION, 0);

	if (mac_sign_key_info != NULL)
		*mac_sign_key_info =
			kmip_node_get_structure_element_by_tag(node,
				KMIP_TAG_MAC_SIGNATURE_KEY_INFORMATION, 0);

	return 0;
}


/**
 * Constructs a Encryption Key Information or MAC/Signature Key Information
 * node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Key Information                         Structure     v1.0
 *     Unique Identifier           Yes       Text String   v1.0
 *                                           Enumeration   v2.0
 *                                           Integer       v2.0
 *     Cryptographic Parameters    No        Structure     v1.0
 *
 * @param mac_sign          if true a MAC/Signature Key Information node is
 *                          created, otherwise a Encryption Key Information
 *                          node.
 * @param unique_id         the unique ID node
 * @param crypto_params     the cryptographic parameters node (can be NULL)
 *
 * @returns the allocated node, or NULL in case of an error.
 * The reference counts of the nodes specified as parameters which are added to
 * the newly allocated node are increased. The caller must free its reference
 * via kmip_node_free() if no longer needed.
 */
struct kmip_node *kmip_new_key_info(bool mac_sign, struct kmip_node *unique_id,
				    struct kmip_node *crypto_params)
{
	enum kmip_tag tag;

	if (unique_id == NULL)
		return NULL;

	tag = (mac_sign ? KMIP_TAG_MAC_SIGNATURE_KEY_INFORMATION :
					KMIP_TAG_ENCRYPTION_KEY_INFORMATION);
	return kmip_node_new_structure_va(tag, NULL, 2, unique_id,
					  crypto_params);
}

/**
 * Gets the information from an Encryption Key Information or MAC/Signature Key
 * Information node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Key Information                         Structure     v1.0
 *     Unique Identifier           Yes       Text String   v1.0
 *                                           Enumeration   v2.0
 *                                           Integer       v2.0
 *     Cryptographic Parameters    No        Structure     v1.0
 *
 * @param node              the KMIP node
 * @param unique_id         On return: the unique ID node (can be NULL)
 * @param crypto_params     On return: the cryptographic parameters node (can
 *                          be NULL)
 *
 * @returns 0 on success, or a negative errno in case of an error
 * The reference count of the returned nodes is increased. The caller must
 * free the node via kmip_node_free() when no longer needed.
 */
int kmip_get_key_info(const struct kmip_node *node,
		      struct kmip_node **unique_id,
		      struct kmip_node **crypto_params)
{
	if (node == NULL)
		return -EINVAL;

	if (kmip_node_get_tag(node) != KMIP_TAG_ENCRYPTION_KEY_INFORMATION &&
	    kmip_node_get_tag(node) != KMIP_TAG_MAC_SIGNATURE_KEY_INFORMATION)
		return -EBADMSG;

	if (unique_id != NULL) {
		*unique_id = kmip_node_get_structure_element_by_tag(node,
					KMIP_TAG_UNIQUE_IDENTIFIER, 0);
		if (*unique_id == NULL)
			return -EBADMSG;
	}

	if (crypto_params != NULL)
		*crypto_params = kmip_node_get_structure_element_by_tag(node,
					KMIP_TAG_CRYPTOGRAPHIC_PARAMETERS, 0);

	return 0;
}

/**
 * Constructs a Transparent Symmetric Key node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Key Material                            Structure     v1.0
 *     Key                         Yes       Byte String   v1.0
 *
 * @param key               the key
 * @param key_length        the key length
 *
 * @returns the allocated node, or NULL in case of an error.
 */
struct kmip_node *kmip_new_transparent_symmetric_key(const unsigned char *key,
						     uint32_t key_length)
{
	struct kmip_node *k, *ret;

	if (key == NULL || key_length == 0)
		return NULL;

	k = kmip_node_new_byte_string(KMIP_TAG_KEY, NULL, key, key_length);
	if (k == NULL)
		return NULL;

	ret = kmip_node_new_structure_va(KMIP_TAG_KEY_MATERIAL, NULL, 1, k);
	kmip_node_free(k);

	return ret;
}

/**
 * Gets the information from a Transparent Symmetric Key node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Key Material                            Structure     v1.0
 *     Key                         Yes       Byte String   v1.0
 *
 * @param node              the KMIP node
 * @param key               On return: the key
 * @param key_length        On return: the key length
 *
 * @returns 0 on success, or a negative errno in case of an error
 */
int kmip_get_transparent_symmetric_key(const struct kmip_node *node,
				       const unsigned char **key,
				       uint32_t *key_length)
{
	struct kmip_node *k;

	if (node == NULL || key == NULL || key_length == NULL)
		return -EINVAL;

	if (kmip_node_get_tag(node) != KMIP_TAG_KEY_MATERIAL)
		return -EBADMSG;

	k = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_KEY, 0);
	if (k == NULL)
		return -EBADMSG;

	*key = kmip_node_get_byte_string(k, key_length);
	kmip_node_free(k);

	return 0;
}

/**
 * Constructs a Transparent RSA Public Key node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Key Material                            Structure     v1.0
 *     Modulus                     Yes       Big Integer   v1.0
 *     Public Exponent             Yes       Big Integer   v1.0
 *
 * @param modulus           the modulus as OpenSSL BIGNUM
 * @param pub_ext           the public exponent as OpenSSL BIGNUM
 *
 * @returns the allocated node, or NULL in case of an error.
 */
struct kmip_node *kmip_new_transparent_rsa_public_key(const BIGNUM *modulus,
						      const BIGNUM *pub_exp)
{
	struct kmip_node *mod, *exp, *ret = NULL;

	if (modulus == NULL || pub_exp == NULL)
		return NULL;

	mod = kmip_node_new_bigint(KMIP_TAG_MODULUS, NULL, modulus);
	exp = kmip_node_new_bigint(KMIP_TAG_PUBLIC_EXPONENT, NULL, pub_exp);
	if (mod == NULL || exp == NULL)
		goto out;

	ret = kmip_node_new_structure_va(KMIP_TAG_KEY_MATERIAL, NULL, 2, mod,
					 exp);

out:
	kmip_node_free(mod);
	kmip_node_free(exp);

	return ret;
}

/**
 * Gets the information from a Transparent RSA Public Key node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Key Material                            Structure     v1.0
 *     Modulus                     Yes       Big Integer   v1.0
 *     Public Exponent             Yes       Big Integer   v1.0
 *
 * @param node              the KMIP node
 * @param modulus           On return: the modulus as OpenSSL BIGNUM
 * @param pub_ext           On return: the public exponent as OpenSSL BIGNUM
 *
 * @returns 0 on success, or a negative errno in case of an error
 */
int kmip_get_transparent_rsa_public_key(const struct kmip_node *node,
					const BIGNUM **modulus,
					const BIGNUM **pub_exp)
{
	struct kmip_node *n;

	if (node == NULL || modulus == NULL || pub_exp == NULL)
		return -EINVAL;

	if (kmip_node_get_tag(node) != KMIP_TAG_KEY_MATERIAL)
		return -EBADMSG;

	n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_MODULUS, 0);
	if (n == NULL)
		return -EBADMSG;
	*modulus = kmip_node_get_bigint(n);
	kmip_node_free(n);

	n = kmip_node_get_structure_element_by_tag(node,
						   KMIP_TAG_PUBLIC_EXPONENT, 0);
	if (n == NULL)
		return -EBADMSG;
	*pub_exp = kmip_node_get_bigint(n);
	kmip_node_free(n);

	return 0;
}

/**
 * Constructs a PKCS#1 Public Key node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Key Material                            Byte String   v1.0
 *
 * @param pub_key           the public key as OpenSSL PKEY
 *
 * @returns the allocated node, or NULL in case of an error.
 */
struct kmip_node *kmip_new_pkcs1_public_key(EVP_PKEY *pub_key)
{
	struct kmip_node *ret = NULL;
	unsigned char *buf = NULL;
	int len;

	if (pub_key == NULL)
		return NULL;

	len = i2d_PublicKey(pub_key, &buf);
	if (len <= 0)
		return NULL;

	ret = kmip_node_new_byte_string(KMIP_TAG_KEY_MATERIAL, NULL, buf, len);

	OPENSSL_free(buf);
	return ret;
}

/**
 * Gets the information from a PKCS#1 Public Key node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Key Material                            Byte String   v1.0
 *
 * @param node              the KMIP node
 * @param algo              the algorithm of the key
 * @param pub_key           On return: the public key as OpenSSL PKEY. Must be
 *                          freed by the caller using EVP_PKEY_free() when no
 *                          longer needed.
 *
 * @returns 0 on success, or a negative errno in case of an error
 */
int kmip_get_pkcs1_public_key(const struct kmip_node *node,
			      enum kmip_crypto_algo algo,
			      EVP_PKEY **pub_key)
{
	const unsigned char *buf;
	uint32_t len = 0;
	int type;

	if (node == NULL || pub_key == NULL)
		return -EINVAL;

	if (kmip_node_get_tag(node) != KMIP_TAG_KEY_MATERIAL)
		return -EBADMSG;

	buf = kmip_node_get_byte_string(node, &len);
	if (buf == NULL || len == 0)
		return -EBADMSG;

	switch (algo) {
	case KMIP_CRYPTO_ALGO_RSA:
		type = EVP_PKEY_RSA;
		break;
	case KMIP_CRYPTO_ALGO_DSA:
		type = EVP_PKEY_DSA;
		break;
	case KMIP_CRYPTO_ALGO_ECDSA:
		type = EVP_PKEY_EC;
		break;
	default:
		return -EINVAL;
	}

	*pub_key = d2i_PublicKey(type, NULL, &buf, len);
	if (*pub_key == NULL)
		return -EIO;

	return 0;
}

/**
 * Constructs a PKCS#8 Public Key node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Key Material                            Byte String   v1.0
 *
 * @param pub_key           the public key as OpenSSL PKEY
 *
 * @returns the allocated node, or NULL in case of an error.
 */
struct kmip_node *kmip_new_pkcs8_public_key(EVP_PKEY *pub_key)
{
	struct kmip_node *ret = NULL;
	unsigned char *buf = NULL;
	int len;

	if (pub_key == NULL)
		return NULL;

	len = i2d_PUBKEY(pub_key, &buf);
	if (len <= 0)
		return NULL;

	ret = kmip_node_new_byte_string(KMIP_TAG_KEY_MATERIAL, NULL, buf, len);

	OPENSSL_free(buf);
	return ret;
}

/**
 * Gets the information from a PKCS#8 Public Key node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Key Material                            Byte String   v1.0
 *
 * @param node              the KMIP node
 * @param pub_key           On return: the public key as OpenSSL PKEY. Must be
 *                          freed by the caller using EVP_PKEY_free() when no
 *                          longer needed.
 *
 * @returns 0 on success, or a negative errno in case of an error
 */
int kmip_get_pkcs8_public_key(const struct kmip_node *node,
			      EVP_PKEY **pub_key)
{
	const unsigned char *buf;
	uint32_t len = 0;

	if (node == NULL || pub_key == NULL)
		return -EINVAL;

	if (kmip_node_get_tag(node) != KMIP_TAG_KEY_MATERIAL)
		return -EBADMSG;

	buf = kmip_node_get_byte_string(node, &len);
	if (buf == NULL || len == 0)
		return -EBADMSG;

	*pub_key = d2i_PUBKEY(NULL, &buf, len);
	if (*pub_key == NULL)
		return -EIO;

	return 0;
}

/**
 * Constructs a Raw Key node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Key Material                            Byte String   v1.0
 *
 * @param key               the raw key
 * @param key_len           the length of the key
 *
 * @returns the allocated node, or NULL in case of an error.
 */
struct kmip_node *kmip_new_raw_key(const unsigned char *key, uint32_t key_len)
{

	if (key == NULL)
		return NULL;

	return kmip_node_new_byte_string(KMIP_TAG_KEY_MATERIAL, NULL, key,
					 key_len);
}

/**
 * Gets the information from a Raw Key node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Key Material                            Byte String   v1.0
 *
 * @param node              the KMIP node
 * @param key               On return: the raw key
 * @param key_len           On return: the length of the key
 *
 * @returns 0 on success, or a negative errno in case of an error
 */
int kmip_get_raw_key(const struct kmip_node *node, const unsigned char **key,
		     uint32_t *key_len)
{
	if (node == NULL || key == NULL)
		return -EINVAL;

	if (kmip_node_get_tag(node) != KMIP_TAG_KEY_MATERIAL)
		return -EBADMSG;

	*key = kmip_node_get_byte_string(node, key_len);
	if (*key == NULL)
		return -EBADMSG;

	return 0;
}


/**
 * Constructs a Symmetric Key node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Symmetric Key                           Structure     v1.0
 *     Key Block                   Yes       Structure     v1.0
 *
 * @param keyblock          the key block node
 *
 * @returns the allocated node, or NULL in case of an error.
 * The reference counts of the nodes specified as parameters which are added to
 * the newly allocated node are increased. The caller must free its reference
 * via kmip_node_free() if no longer needed.
 */
struct kmip_node *kmip_new_symmetric_key(struct kmip_node *keyblock)
{
	if (keyblock == NULL)
		return NULL;

	return kmip_node_new_structure_va(KMIP_TAG_SYMMETRIC_KEY, NULL, 1,
					  keyblock);
}

/**
 * Gets the information from a Symmetric Key node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Symmetric Key                           Structure     v1.0
 *     Key Block                   Yes       Structure     v1.0
 *
 * @param node              the KMIP node
 * @param keyblock          On return: the key block node
 *
 * @returns 0 on success, or a negative errno in case of an error
 */
int kmip_get_symmetric_key(const struct kmip_node *node,
			   struct kmip_node **keyblock)
{
	if (node == NULL || keyblock == NULL)
		return -EINVAL;

	if (kmip_node_get_tag(node) != KMIP_TAG_SYMMETRIC_KEY)
		return -EBADMSG;

	*keyblock = kmip_node_get_structure_element_by_tag(node,
							   KMIP_TAG_KEY_BLOCK,
							   0);
	if (*keyblock == NULL)
		return -EBADMSG;

	return 0;
}

/**
 * Constructs a Public Key node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Public Key                              Structure     v1.0
 *     Key Block                   Yes       Structure     v1.0
 *
 * @param keyblock          the key block node
 *
 * @returns the allocated node, or NULL in case of an error.
 * The reference counts of the nodes specified as parameters which are added to
 * the newly allocated node are increased. The caller must free its reference
 * via kmip_node_free() if no longer needed.
 */
struct kmip_node *kmip_new_public_key(struct kmip_node *keyblock)
{
	if (keyblock == NULL)
		return NULL;

	return kmip_node_new_structure_va(KMIP_TAG_PUBLIC_KEY, NULL, 1,
					  keyblock);
}

/**
 * Gets the information from a Public Key node:
 *
 * Object                          Required  Encoding      KMIP version
 * ---------------------------------------------------------------------
 *   Public Key                              Structure     v1.0
 *     Key Block                   Yes       Structure     v1.0
 *
 * @param node              the KMIP node
 * @param keyblock          On return: the key block node
 *
 * @returns 0 on success, or a negative errno in case of an error
 */
int kmip_get_public_key(const struct kmip_node *node,
			struct kmip_node **keyblock)
{
	if (node == NULL || keyblock == NULL)
		return -EINVAL;

	if (kmip_node_get_tag(node) != KMIP_TAG_PUBLIC_KEY)
		return -EBADMSG;

	*keyblock = kmip_node_get_structure_element_by_tag(node,
							   KMIP_TAG_KEY_BLOCK,
							   0);
	if (*keyblock == NULL)
		return -EBADMSG;

	return 0;
}

