/*
 * Copyright (C) 2014-2016 Free Software Foundation, Inc.
 * Copyright (C) 2016 Red Hat, Inc.
 *
 * This file is part of GnuTLS.
 *
 * The GnuTLS is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>
 *
 */

/* This file contains functions to handle X.509 certificate extensions (the x509-ext API)
 */

#include "gnutls_int.h"
#include "datum.h"
#include "errors.h"
#include "common.h"
#include "x509.h"
#include "x509_b64.h"
#include "x509_ext_int.h"
#include "virt-san.h"
#include <gnutls/x509-ext.h>
#include "intprops.h"

#define MAX_ENTRIES 64

/**
 * gnutls_subject_alt_names_init:
 * @sans: The alternative names
 *
 * This function will initialize an alternative names structure.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_subject_alt_names_init(gnutls_subject_alt_names_t *sans)
{
	*sans = gnutls_calloc(1, sizeof(struct gnutls_subject_alt_names_st));
	if (*sans == NULL) {
		gnutls_assert();
		return GNUTLS_E_MEMORY_ERROR;
	}

	return 0;
}

static void subject_alt_names_deinit(gnutls_subject_alt_names_t sans)
{
	unsigned int i;

	for (i = 0; i < sans->size; i++) {
		gnutls_free(sans->names[i].san.data);
		gnutls_free(sans->names[i].othername_oid.data);
	}
	gnutls_free(sans->names);
}

/**
 * gnutls_subject_alt_names_deinit:
 * @sans: The alternative names
 *
 * This function will deinitialize an alternative names structure.
 *
 * Since: 3.3.0
 **/
void gnutls_subject_alt_names_deinit(gnutls_subject_alt_names_t sans)
{
	subject_alt_names_deinit(sans);
	gnutls_free(sans);
}

/**
 * gnutls_subject_alt_names_get:
 * @sans: The alternative names
 * @seq: The index of the name to get
 * @san_type: Will hold the type of the name (of %gnutls_subject_alt_names_t)
 * @san: The alternative name data (should be treated as constant)
 * @othername_oid: The object identifier if @san_type is %GNUTLS_SAN_OTHERNAME (should be treated as constant)
 *
 * This function will return a specific alternative name as stored in
 * the @sans type. The returned values should be treated as constant
 * and valid for the lifetime of @sans.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
 * if the index is out of bounds, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_subject_alt_names_get(gnutls_subject_alt_names_t sans,
				 unsigned int seq, unsigned int *san_type,
				 gnutls_datum_t *san,
				 gnutls_datum_t *othername_oid)
{
	if (seq >= sans->size)
		return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);

	if (san) {
		memcpy(san, &sans->names[seq].san, sizeof(gnutls_datum_t));
	}

	if (san_type)
		*san_type = sans->names[seq].type;

	if (othername_oid != NULL &&
	    sans->names[seq].type == GNUTLS_SAN_OTHERNAME) {
		othername_oid->data = sans->names[seq].othername_oid.data;
		othername_oid->size = sans->names[seq].othername_oid.size;
	}

	return 0;
}

/* This is the same as gnutls_subject_alt_names_set() but will not
 * copy the strings. It expects all the provided input to be already
 * allocated by gnutls. */
static int subject_alt_names_set(struct name_st **names, unsigned int *size,
				 unsigned int san_type, gnutls_datum_t *san,
				 char *othername_oid, unsigned raw)
{
	void *tmp;
	int ret;

	if (unlikely(INT_ADD_OVERFLOW(*size, 1))) {
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
	}

	tmp = _gnutls_reallocarray(*names, *size + 1, sizeof((*names)[0]));
	if (tmp == NULL) {
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
	}
	*names = tmp;

	ret = _gnutls_alt_name_assign_virt_type(&(*names)[*size], san_type, san,
						othername_oid, raw);
	if (ret < 0)
		return gnutls_assert_val(ret);

	(*size)++;
	return 0;
}

/**
 * gnutls_subject_alt_names_set:
 * @sans: The alternative names
 * @san_type: The type of the name (of %gnutls_subject_alt_names_t)
 * @san: The alternative name data
 * @othername_oid: The object identifier if @san_type is %GNUTLS_SAN_OTHERNAME
 *
 * This function will store the specified alternative name in
 * the @sans.
 *
 * Since version 3.5.7 the %GNUTLS_SAN_RFC822NAME, %GNUTLS_SAN_DNSNAME, and
 * %GNUTLS_SAN_OTHERNAME_XMPP are converted to ACE format when necessary.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_subject_alt_names_set(gnutls_subject_alt_names_t sans,
				 unsigned int san_type,
				 const gnutls_datum_t *san,
				 const char *othername_oid)
{
	int ret;
	gnutls_datum_t copy;
	char *ooc;

	ret = _gnutls_set_strdatum(&copy, san->data, san->size);
	if (ret < 0)
		return gnutls_assert_val(ret);

	if (othername_oid != NULL)
		ooc = gnutls_strdup(othername_oid);
	else
		ooc = NULL;
	ret = subject_alt_names_set(&sans->names, &sans->size, san_type, &copy,
				    ooc, 0);
	if (ret < 0) {
		gnutls_free(copy.data);
		return gnutls_assert_val(ret);
	}

	return 0;
}

/**
 * gnutls_x509_ext_import_subject_alt_names:
 * @ext: The DER-encoded extension data
 * @sans: The alternative names
 * @flags: should be zero
 *
 * This function will export the alternative names in the provided DER-encoded
 * SubjectAltName PKIX extension, to a %gnutls_subject_alt_names_t type. @sans
 * must be initialized.
 *
 * This function will succeed even if there no subject alternative names
 * in the structure.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_import_subject_alt_names(const gnutls_datum_t *ext,
					     gnutls_subject_alt_names_t sans,
					     unsigned int flags)
{
	asn1_node c2 = NULL;
	int result, ret;
	unsigned int i;
	gnutls_datum_t san, othername_oid;
	unsigned type;

	result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.GeneralNames",
				     &c2);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(result);
	}

	result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		ret = _gnutls_asn2err(result);
		goto cleanup;
	}

	for (i = 0;; i++) {
		san.data = NULL;
		san.size = 0;
		othername_oid.data = NULL;

		ret = _gnutls_parse_general_name2(c2, "", i, &san, &type, 0);
		if (ret < 0)
			break;

		if (type == GNUTLS_SAN_OTHERNAME) {
			ret = _gnutls_parse_general_name2(
				c2, "", i, &othername_oid, NULL, 1);
			if (ret < 0)
				break;

		} else if (san.size == 0 || san.data == NULL) {
			ret = gnutls_assert_val(GNUTLS_E_X509_UNKNOWN_SAN);
			break;
		}

		ret = subject_alt_names_set(&sans->names, &sans->size, type,
					    &san, (char *)othername_oid.data,
					    1);
		if (ret < 0)
			break;
	}

	sans->size = i;
	if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
		gnutls_free(san.data);
		gnutls_free(othername_oid.data);
		gnutls_assert();
		goto cleanup;
	}

	ret = 0;
cleanup:
	asn1_delete_structure(&c2);
	return ret;
}

/**
 * gnutls_x509_ext_export_subject_alt_names:
 * @sans: The alternative names
 * @ext: The DER-encoded extension data; must be freed using gnutls_free().
 *
 * This function will convert the provided alternative names structure to a
 * DER-encoded SubjectAltName PKIX extension. The output data in @ext will be allocated using
 * gnutls_malloc().
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_export_subject_alt_names(gnutls_subject_alt_names_t sans,
					     gnutls_datum_t *ext)
{
	asn1_node c2 = NULL;
	int result, ret;
	unsigned i;

	result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.GeneralNames",
				     &c2);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(result);
	}

	for (i = 0; i < sans->size; i++) {
		if (sans->names[i].type == GNUTLS_SAN_OTHERNAME) {
			ret = _gnutls_write_new_othername(
				c2, "",
				(char *)sans->names[i].othername_oid.data,
				sans->names[i].san.data,
				sans->names[i].san.size);
		} else {
			ret = _gnutls_write_new_general_name(
				c2, "", sans->names[i].type,
				sans->names[i].san.data,
				sans->names[i].san.size);
		}

		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}
	}

	ret = _gnutls_x509_der_encode(c2, "", ext, 0);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	ret = 0;

cleanup:
	asn1_delete_structure(&c2);
	return ret;
}

/**
 * gnutls_x509_ext_import_name_constraints:
 * @ext: a DER encoded extension
 * @nc: The nameconstraints
 * @flags: zero or %GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND
 *
 * This function will return an intermediate type containing
 * the name constraints of the provided NameConstraints extension. That
 * can be used in combination with gnutls_x509_name_constraints_check()
 * to verify whether a server's name is in accordance with the constraints.
 *
 * When the @flags is set to %GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND, then if 
 * the @nc type is empty this function will behave identically as if the flag was not set.
 * Otherwise if there are elements in the @nc structure then the
 * constraints will be merged with the existing constraints following
 * RFC5280 p6.1.4 (excluded constraints will be appended, permitted
 * will be intersected).
 *
 * Note that @nc must be initialized prior to calling this function.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
 * if the extension is not present, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_import_name_constraints(const gnutls_datum_t *ext,
					    gnutls_x509_name_constraints_t nc,
					    unsigned int flags)
{
	int result, ret;
	asn1_node c2 = NULL;
	gnutls_x509_name_constraints_t nc2 = NULL;

	result = asn1_create_element(_gnutls_get_pkix(),
				     "PKIX1.NameConstraints", &c2);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(result);
	}

	result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		ret = _gnutls_asn2err(result);
		goto cleanup;
	}

	if (flags & GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND &&
	    !_gnutls_x509_name_constraints_is_empty(nc, 0)) {
		ret = gnutls_x509_name_constraints_init(&nc2);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

		ret = _gnutls_x509_name_constraints_extract(
			c2, "permittedSubtrees", "excludedSubtrees", nc2);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

		ret = _gnutls_x509_name_constraints_merge(nc, nc2);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}
	} else {
		_gnutls_x509_name_constraints_clear(nc);

		ret = _gnutls_x509_name_constraints_extract(
			c2, "permittedSubtrees", "excludedSubtrees", nc);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}
	}

	ret = 0;

cleanup:
	asn1_delete_structure(&c2);
	if (nc2)
		gnutls_x509_name_constraints_deinit(nc2);

	return ret;
}

/**
 * gnutls_x509_ext_export_name_constraints:
 * @nc: The nameconstraints
 * @ext: The DER-encoded extension data; must be freed using gnutls_free().
 *
 * This function will convert the provided name constraints type to a
 * DER-encoded PKIX NameConstraints (2.5.29.30) extension. The output data in 
 * @ext will be allocated using gnutls_malloc().
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_export_name_constraints(gnutls_x509_name_constraints_t nc,
					    gnutls_datum_t *ext)
{
	int ret, result;
	uint8_t null = 0;
	asn1_node c2 = NULL;
	unsigned rtype;
	gnutls_datum_t rname;

	if (_gnutls_x509_name_constraints_is_empty(nc, 0))
		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);

	result = asn1_create_element(_gnutls_get_pkix(),
				     "PKIX1.NameConstraints", &c2);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(result);
	}

	ret = gnutls_x509_name_constraints_get_permitted(nc, 0, &rtype, &rname);
	if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
		(void)asn1_write_value(c2, "permittedSubtrees", NULL, 0);
	} else {
		for (unsigned i = 0;; i++) {
			ret = gnutls_x509_name_constraints_get_permitted(
				nc, i, &rtype, &rname);
			if (ret < 0) {
				if (ret ==
				    GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
					break;
				gnutls_assert();
				goto cleanup;
			}
			result = asn1_write_value(c2, "permittedSubtrees",
						  "NEW", 1);
			if (result != ASN1_SUCCESS) {
				gnutls_assert();
				ret = _gnutls_asn2err(result);
				goto cleanup;
			}

			result = asn1_write_value(
				c2, "permittedSubtrees.?LAST.maximum", NULL, 0);
			if (result != ASN1_SUCCESS) {
				gnutls_assert();
				ret = _gnutls_asn2err(result);
				goto cleanup;
			}

			result = asn1_write_value(
				c2, "permittedSubtrees.?LAST.minimum", &null,
				1);
			if (result != ASN1_SUCCESS) {
				gnutls_assert();
				ret = _gnutls_asn2err(result);
				goto cleanup;
			}

			ret = _gnutls_write_general_name(
				c2, "permittedSubtrees.?LAST.base", rtype,
				rname.data, rname.size);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}
		}
	}

	ret = gnutls_x509_name_constraints_get_excluded(nc, 0, &rtype, &rname);
	if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
		(void)asn1_write_value(c2, "excludedSubtrees", NULL, 0);
	} else {
		for (unsigned i = 0;; i++) {
			ret = gnutls_x509_name_constraints_get_excluded(
				nc, i, &rtype, &rname);
			if (ret < 0) {
				if (ret ==
				    GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
					break;
				gnutls_assert();
				goto cleanup;
			}
			result = asn1_write_value(c2, "excludedSubtrees", "NEW",
						  1);
			if (result != ASN1_SUCCESS) {
				gnutls_assert();
				ret = _gnutls_asn2err(result);
				goto cleanup;
			}

			result = asn1_write_value(
				c2, "excludedSubtrees.?LAST.maximum", NULL, 0);
			if (result != ASN1_SUCCESS) {
				gnutls_assert();
				ret = _gnutls_asn2err(result);
				goto cleanup;
			}

			result = asn1_write_value(
				c2, "excludedSubtrees.?LAST.minimum", &null, 1);
			if (result != ASN1_SUCCESS) {
				gnutls_assert();
				ret = _gnutls_asn2err(result);
				goto cleanup;
			}

			ret = _gnutls_write_general_name(
				c2, "excludedSubtrees.?LAST.base", rtype,
				rname.data, rname.size);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}
		}
	}

	ret = _gnutls_x509_der_encode(c2, "", ext, 0);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	ret = 0;

cleanup:
	asn1_delete_structure(&c2);
	return ret;
}

/**
 * gnutls_x509_ext_import_subject_key_id:
 * @ext: a DER encoded extension
 * @id: will contain the subject key ID
 *
 * This function will return the subject key ID stored in the provided
 * SubjectKeyIdentifier extension. The ID will be allocated using
 * gnutls_malloc().
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
 * if the extension is not present, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_import_subject_key_id(const gnutls_datum_t *ext,
					  gnutls_datum_t *id)
{
	int result, ret;
	asn1_node c2 = NULL;

	if (ext->size == 0 || ext->data == NULL) {
		gnutls_assert();
		return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
	}

	result = asn1_create_element(_gnutls_get_pkix(),
				     "PKIX1.SubjectKeyIdentifier", &c2);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(result);
	}

	result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		ret = _gnutls_asn2err(result);
		goto cleanup;
	}

	ret = _gnutls_x509_read_value(c2, "", id);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	ret = 0;
cleanup:
	asn1_delete_structure(&c2);

	return ret;
}

/**
 * gnutls_x509_ext_export_subject_key_id:
 * @id: The key identifier
 * @ext: The DER-encoded extension data; must be freed using gnutls_free().
 *
 * This function will convert the provided key identifier to a
 * DER-encoded PKIX SubjectKeyIdentifier extension. 
 * The output data in @ext will be allocated using
 * gnutls_malloc().
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_export_subject_key_id(const gnutls_datum_t *id,
					  gnutls_datum_t *ext)
{
	asn1_node c2 = NULL;
	int ret, result;

	result = asn1_create_element(_gnutls_get_pkix(),
				     "PKIX1.SubjectKeyIdentifier", &c2);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(result);
	}

	result = asn1_write_value(c2, "", id->data, id->size);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		ret = _gnutls_asn2err(result);
		goto cleanup;
	}

	ret = _gnutls_x509_der_encode(c2, "", ext, 0);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	ret = 0;
cleanup:
	asn1_delete_structure(&c2);
	return ret;
}

struct gnutls_x509_aki_st {
	gnutls_datum_t id;
	struct gnutls_subject_alt_names_st cert_issuer;
	gnutls_datum_t serial;
};

/**
 * gnutls_x509_aki_init:
 * @aki: The authority key ID type
 *
 * This function will initialize an authority key ID.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_aki_init(gnutls_x509_aki_t *aki)
{
	*aki = gnutls_calloc(1, sizeof(struct gnutls_x509_aki_st));
	if (*aki == NULL)
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);

	return 0;
}

/**
 * gnutls_x509_aki_deinit:
 * @aki: The authority key identifier type
 *
 * This function will deinitialize an authority key identifier.
 *
 * Since: 3.3.0
 **/
void gnutls_x509_aki_deinit(gnutls_x509_aki_t aki)
{
	gnutls_free(aki->serial.data);
	gnutls_free(aki->id.data);
	subject_alt_names_deinit(&aki->cert_issuer);
	gnutls_free(aki);
}

/**
 * gnutls_x509_aki_get_id:
 * @aki: The authority key ID
 * @id: Will hold the identifier
 *
 * This function will return the key identifier as stored in
 * the @aki type. The identifier should be treated as constant.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
 * if the index is out of bounds, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_aki_get_id(gnutls_x509_aki_t aki, gnutls_datum_t *id)
{
	if (aki->id.size == 0)
		return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);

	memcpy(id, &aki->id, sizeof(gnutls_datum_t));
	return 0;
}

/**
 * gnutls_x509_aki_set_id:
 * @aki: The authority key ID
 * @id: the key identifier
 *
 * This function will set the keyIdentifier to be stored in the @aki
 * type.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_aki_set_id(gnutls_x509_aki_t aki, const gnutls_datum_t *id)
{
	return _gnutls_set_datum(&aki->id, id->data, id->size);
}

/**
 * gnutls_x509_aki_set_cert_issuer:
 * @aki: The authority key ID
 * @san_type: the type of the name (of %gnutls_subject_alt_names_t), may be null
 * @san: The alternative name data
 * @othername_oid: The object identifier if @san_type is %GNUTLS_SAN_OTHERNAME
 * @serial: The authorityCertSerialNumber number (may be null)
 *
 * This function will set the authorityCertIssuer name and the authorityCertSerialNumber 
 * to be stored in the @aki type. When storing multiple names, the serial
 * should be set on the first call, and subsequent calls should use a %NULL serial.
 *
 * Since version 3.5.7 the %GNUTLS_SAN_RFC822NAME, %GNUTLS_SAN_DNSNAME, and
 * %GNUTLS_SAN_OTHERNAME_XMPP are converted to ACE format when necessary.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_aki_set_cert_issuer(gnutls_x509_aki_t aki,
				    unsigned int san_type,
				    const gnutls_datum_t *san,
				    const char *othername_oid,
				    const gnutls_datum_t *serial)
{
	int ret;
	gnutls_datum_t t_san, t_othername_oid = { NULL, 0 };

	ret = _gnutls_set_datum(&aki->serial, serial->data, serial->size);
	if (ret < 0)
		return gnutls_assert_val(ret);

	aki->cert_issuer.names[aki->cert_issuer.size].type = san_type;

	ret = _gnutls_set_strdatum(&t_san, san->data, san->size);
	if (ret < 0)
		return gnutls_assert_val(ret);

	if (othername_oid) {
		t_othername_oid.data = (uint8_t *)gnutls_strdup(othername_oid);
		if (t_othername_oid.data == NULL) {
			gnutls_free(t_san.data);
			return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
		}
		t_othername_oid.size = strlen(othername_oid);
	}

	ret = subject_alt_names_set(&aki->cert_issuer.names,
				    &aki->cert_issuer.size, san_type, &t_san,
				    (char *)t_othername_oid.data, 0);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	return 0;
}

/**
 * gnutls_x509_aki_get_cert_issuer:
 * @aki: The authority key ID
 * @seq: The index of the name to get
 * @san_type: Will hold the type of the name (of %gnutls_subject_alt_names_t)
 * @san: The alternative name data
 * @othername_oid: The object identifier if @san_type is %GNUTLS_SAN_OTHERNAME
 * @serial: The authorityCertSerialNumber number
 *
 * This function will return a specific authorityCertIssuer name as stored in
 * the @aki type, as well as the authorityCertSerialNumber. All the returned
 * values should be treated as constant, and may be set to %NULL when are not required.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
 * if the index is out of bounds, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_aki_get_cert_issuer(gnutls_x509_aki_t aki, unsigned int seq,
				    unsigned int *san_type, gnutls_datum_t *san,
				    gnutls_datum_t *othername_oid,
				    gnutls_datum_t *serial)
{
	if (seq >= aki->cert_issuer.size)
		return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);

	if (aki->serial.size == 0)
		return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);

	if (serial)
		memcpy(serial, &aki->serial, sizeof(gnutls_datum_t));

	if (san) {
		memcpy(san, &aki->cert_issuer.names[seq].san,
		       sizeof(gnutls_datum_t));
	}

	if (othername_oid != NULL &&
	    aki->cert_issuer.names[seq].type == GNUTLS_SAN_OTHERNAME) {
		othername_oid->data =
			aki->cert_issuer.names[seq].othername_oid.data;
		othername_oid->size =
			aki->cert_issuer.names[seq].othername_oid.size;
	}

	if (san_type)
		*san_type = aki->cert_issuer.names[seq].type;

	return 0;
}

/**
 * gnutls_x509_ext_import_authority_key_id:
 * @ext: a DER encoded extension
 * @aki: An initialized authority key identifier type
 * @flags: should be zero
 *
 * This function will return the subject key ID stored in the provided
 * AuthorityKeyIdentifier extension.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
 * if the extension is not present, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_import_authority_key_id(const gnutls_datum_t *ext,
					    gnutls_x509_aki_t aki,
					    unsigned int flags)
{
	int ret;
	unsigned i;
	asn1_node c2 = NULL;
	gnutls_datum_t san, othername_oid;
	unsigned type;

	ret = asn1_create_element(_gnutls_get_pkix(),
				  "PKIX1.AuthorityKeyIdentifier", &c2);
	if (ret != ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(ret);
	}

	ret = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
	if (ret != ASN1_SUCCESS) {
		gnutls_assert();
		ret = _gnutls_asn2err(ret);
		goto cleanup;
	}

	/* Read authorityCertIssuer */
	for (i = 0;; i++) {
		san.data = NULL;
		san.size = 0;
		othername_oid.data = NULL;

		ret = _gnutls_parse_general_name2(c2, "authorityCertIssuer", i,
						  &san, &type, 0);
		if (ret < 0)
			break;

		if (type == GNUTLS_SAN_OTHERNAME) {
			ret = _gnutls_parse_general_name2(c2,
							  "authorityCertIssuer",
							  i, &othername_oid,
							  NULL, 1);
			if (ret < 0)
				break;
		}

		ret = subject_alt_names_set(&aki->cert_issuer.names,
					    &aki->cert_issuer.size, type, &san,
					    (char *)othername_oid.data, 1);
		if (ret < 0)
			break;
	}

	assert(ret < 0);
	aki->cert_issuer.size = i;
	if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE &&
	    ret != GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) {
		gnutls_assert();
		gnutls_free(san.data);
		gnutls_free(othername_oid.data);
		goto cleanup;
	}

	/* Read the serial number */
	ret = _gnutls_x509_read_value(c2, "authorityCertSerialNumber",
				      &aki->serial);
	if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE &&
	    ret != GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) {
		gnutls_assert();
		goto cleanup;
	}

	/* Read the key identifier */
	ret = _gnutls_x509_read_value(c2, "keyIdentifier", &aki->id);
	if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE &&
	    ret != GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) {
		gnutls_assert();
		goto cleanup;
	}

	ret = 0;

cleanup:
	asn1_delete_structure(&c2);

	return ret;
}

/**
 * gnutls_x509_ext_export_authority_key_id:
 * @aki: An initialized authority key identifier
 * @ext: The DER-encoded extension data; must be freed using gnutls_free().
 *
 * This function will convert the provided key identifier to a
 * DER-encoded PKIX AuthorityKeyIdentifier extension. 
 * The output data in @ext will be allocated using
 * gnutls_malloc().
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_export_authority_key_id(gnutls_x509_aki_t aki,
					    gnutls_datum_t *ext)
{
	asn1_node c2 = NULL;
	unsigned i;
	int result, ret;

	result = asn1_create_element(_gnutls_get_pkix(),
				     "PKIX1.AuthorityKeyIdentifier", &c2);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(result);
	}

	if (aki->id.data != NULL) {
		result = asn1_write_value(c2, "keyIdentifier", aki->id.data,
					  aki->id.size);
		if (result != ASN1_SUCCESS) {
			gnutls_assert();
			ret = _gnutls_asn2err(result);
			goto cleanup;
		}
	} else {
		(void)asn1_write_value(c2, "keyIdentifier", NULL, 0);
	}

	if (aki->serial.data != NULL) {
		result = asn1_write_value(c2, "authorityCertSerialNumber",
					  aki->serial.data, aki->serial.size);
		if (result != ASN1_SUCCESS) {
			gnutls_assert();
			ret = _gnutls_asn2err(result);
			goto cleanup;
		}
	} else {
		(void)asn1_write_value(c2, "authorityCertSerialNumber", NULL,
				       0);
	}

	if (aki->cert_issuer.size == 0) {
		(void)asn1_write_value(c2, "authorityCertIssuer", NULL, 0);
	} else {
		for (i = 0; i < aki->cert_issuer.size; i++) {
			ret = _gnutls_write_new_general_name(
				c2, "authorityCertIssuer",
				aki->cert_issuer.names[i].type,
				aki->cert_issuer.names[i].san.data,
				aki->cert_issuer.names[i].san.size);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}
		}
	}

	ret = _gnutls_x509_der_encode(c2, "", ext, 0);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	ret = 0;
cleanup:
	asn1_delete_structure(&c2);
	return ret;
}

/**
 * gnutls_x509_ext_import_key_usage:
 * @ext: the DER encoded extension data
 * @key_usage: where the key usage bits will be stored
 *
 * This function will return certificate's key usage, by reading the DER
 * data of the keyUsage X.509 extension (2.5.29.15). The key usage value will ORed
 * values of the: %GNUTLS_KEY_DIGITAL_SIGNATURE,
 * %GNUTLS_KEY_NON_REPUDIATION, %GNUTLS_KEY_KEY_ENCIPHERMENT,
 * %GNUTLS_KEY_DATA_ENCIPHERMENT, %GNUTLS_KEY_KEY_AGREEMENT,
 * %GNUTLS_KEY_KEY_CERT_SIGN, %GNUTLS_KEY_CRL_SIGN,
 * %GNUTLS_KEY_ENCIPHER_ONLY, %GNUTLS_KEY_DECIPHER_ONLY.
 *
 * Returns: the certificate key usage, or a negative error code in case of
 *   parsing error.  If the certificate does not contain the keyUsage
 *   extension %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be
 *   returned.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_import_key_usage(const gnutls_datum_t *ext,
				     unsigned int *key_usage)
{
	asn1_node c2 = NULL;
	int len, result;
	uint8_t str[2];

	str[0] = str[1] = 0;
	*key_usage = 0;

	if ((result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.KeyUsage",
					  &c2)) != ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(result);
	}

	result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		asn1_delete_structure(&c2);
		return _gnutls_asn2err(result);
	}

	len = sizeof(str);
	result = asn1_read_value(c2, "", str, &len);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		asn1_delete_structure(&c2);
		return _gnutls_asn2err(result);
	}

	*key_usage = str[0] | (str[1] << 8);

	asn1_delete_structure(&c2);

	return 0;
}

static int _last_key_usage_set_bit(int usage)
{
	/* the byte ordering is a bit strange here, see how GNUTLS_KEY_* is laid out, and how 
 * asn1_write_value() writes out BIT STRING objects.
 */
	if (usage & GNUTLS_KEY_DECIPHER_ONLY)
		return 9;
	else if (usage & GNUTLS_KEY_ENCIPHER_ONLY)
		return 8;
	else if (usage & GNUTLS_KEY_CRL_SIGN)
		return 7;
	else if (usage & GNUTLS_KEY_KEY_CERT_SIGN)
		return 6;
	else if (usage & GNUTLS_KEY_KEY_AGREEMENT)
		return 5;
	else if (usage & GNUTLS_KEY_DATA_ENCIPHERMENT)
		return 4;
	else if (usage & GNUTLS_KEY_KEY_ENCIPHERMENT)
		return 3;
	else if (usage & GNUTLS_KEY_NON_REPUDIATION)
		return 2;
	else if (usage & GNUTLS_KEY_DIGITAL_SIGNATURE)
		return 1;
	else
		return 0;
}

/**
 * gnutls_x509_ext_export_key_usage:
 * @usage: an ORed sequence of the GNUTLS_KEY_* elements.
 * @ext: The DER-encoded extension data; must be freed using gnutls_free().
 *
 * This function will convert the keyUsage bit string to a DER
 * encoded PKIX extension. The @ext data will be allocated using
 * gnutls_malloc().
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_export_key_usage(unsigned int usage, gnutls_datum_t *ext)
{
	asn1_node c2 = NULL;
	int result;
	uint8_t str[2];

	result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.KeyUsage", &c2);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(result);
	}

	str[0] = usage & 0xff;
	str[1] = usage >> 8;

	/* Since KeyUsage is a BIT STRING, the input to asn1_write_value
	 * is the number of bits to be written/read. */
	result = asn1_write_value(c2, "", str, _last_key_usage_set_bit(usage));
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		asn1_delete_structure(&c2);
		return _gnutls_asn2err(result);
	}

	result = _gnutls_x509_der_encode(c2, "", ext, 0);

	asn1_delete_structure(&c2);

	if (result < 0) {
		gnutls_assert();
		return result;
	}

	return 0;
}

/**
 * gnutls_x509_ext_import_inhibit_anypolicy:
 * @ext: the DER encoded extension data
 * @skipcerts: will hold the number of certificates after which anypolicy is no longer acceptable.
 *
 * This function will return certificate's value of SkipCerts,
 * by reading the DER data of the Inhibit anyPolicy X.509 extension (2.5.29.54).
 *
 * The @skipcerts value is the number of additional certificates that
 * may appear in the path before the anyPolicy (%GNUTLS_X509_OID_POLICY_ANY)
 * is no longer acceptable.
 *
 * Returns: zero, or a negative error code in case of
 *   parsing error.  If the certificate does not contain the Inhibit anyPolicy
 *   extension %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be
 *   returned.
 *
 * Since: 3.6.0
 **/
int gnutls_x509_ext_import_inhibit_anypolicy(const gnutls_datum_t *ext,
					     unsigned int *skipcerts)
{
	int ret;

	ret = _gnutls_x509_read_der_uint(ext->data, ext->size, skipcerts);
	if (ret < 0) {
		gnutls_assert();
	}

	return ret;
}

/**
 * gnutls_x509_ext_export_inhibit_anypolicy:
 * @skipcerts: number of certificates after which anypolicy is no longer acceptable.
 * @ext: The DER-encoded extension data; must be freed using gnutls_free().
 *
 * This function will convert the @skipcerts value to a DER
 * encoded Inhibit AnyPolicy PKIX extension. The @ext data will be allocated using
 * gnutls_malloc().
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value.
 *
 * Since: 3.6.0
 **/
int gnutls_x509_ext_export_inhibit_anypolicy(unsigned int skipcerts,
					     gnutls_datum_t *ext)
{
	asn1_node c2 = NULL;
	int result, ret;

	result = asn1_create_element(_gnutls_get_gnutls_asn(),
				     "GNUTLS.DSAPublicKey", &c2);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(result);
	}

	ret = _gnutls_x509_write_uint32(c2, "", skipcerts);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	ret = _gnutls_x509_der_encode(c2, "", ext, 0);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	ret = 0;

cleanup:
	asn1_delete_structure(&c2);

	return ret;
}

/**
 * gnutls_x509_ext_import_private_key_usage_period:
 * @ext: the DER encoded extension data
 * @activation: Will hold the activation time
 * @expiration: Will hold the expiration time
 *
 * This function will return the expiration and activation
 * times of the private key as written in the
 * PKIX extension 2.5.29.16.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_import_private_key_usage_period(const gnutls_datum_t *ext,
						    time_t *activation,
						    time_t *expiration)
{
	int result, ret;
	asn1_node c2 = NULL;

	result = asn1_create_element(_gnutls_get_pkix(),
				     "PKIX1.PrivateKeyUsagePeriod", &c2);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		ret = _gnutls_asn2err(result);
		goto cleanup;
	}

	result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		ret = _gnutls_asn2err(result);
		goto cleanup;
	}

	if (activation)
		*activation = _gnutls_x509_get_time(c2, "notBefore", 1);

	if (expiration)
		*expiration = _gnutls_x509_get_time(c2, "notAfter", 1);

	ret = 0;

cleanup:
	asn1_delete_structure(&c2);

	return ret;
}

/**
 * gnutls_x509_ext_export_private_key_usage_period:
 * @activation: The activation time
 * @expiration: The expiration time
 * @ext: The DER-encoded extension data; must be freed using gnutls_free().
 *
 * This function will convert the periods provided to a private key
 * usage DER encoded extension (2.5.29.16).
 (
 * The @ext data will be allocated using
 * gnutls_malloc().
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_export_private_key_usage_period(time_t activation,
						    time_t expiration,
						    gnutls_datum_t *ext)
{
	int result;
	asn1_node c2 = NULL;

	result = asn1_create_element(_gnutls_get_pkix(),
				     "PKIX1.PrivateKeyUsagePeriod", &c2);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(result);
	}

	result = _gnutls_x509_set_time(c2, "notBefore", activation, 1);
	if (result < 0) {
		gnutls_assert();
		goto cleanup;
	}

	result = _gnutls_x509_set_time(c2, "notAfter", expiration, 1);
	if (result < 0) {
		gnutls_assert();
		goto cleanup;
	}

	result = _gnutls_x509_der_encode(c2, "", ext, 0);
	if (result < 0) {
		gnutls_assert();
		goto cleanup;
	}

cleanup:
	asn1_delete_structure(&c2);

	return result;
}

/**
 * gnutls_x509_ext_import_basic_constraints:
 * @ext: the DER encoded extension data
 * @ca: will be non zero if the CA status is true
 * @pathlen: the path length constraint; will be set to -1 for no limit
 *
 * This function will return the CA status and path length constraint
 * as written in the PKIX extension 2.5.29.19.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_import_basic_constraints(const gnutls_datum_t *ext,
					     unsigned int *ca, int *pathlen)
{
	asn1_node c2 = NULL;
	char str[128] = "";
	int len, result;

	if ((result = asn1_create_element(_gnutls_get_pkix(),
					  "PKIX1.BasicConstraints", &c2)) !=
	    ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(result);
	}

	result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		result = _gnutls_asn2err(result);
		goto cleanup;
	}

	if (pathlen) {
		result = _gnutls_x509_read_uint(c2, "pathLenConstraint",
						(unsigned int *)pathlen);
		if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
			*pathlen = -1;
		else if (result != GNUTLS_E_SUCCESS) {
			gnutls_assert();
			result = _gnutls_asn2err(result);
			goto cleanup;
		}
	}

	/* the default value of cA is false.
	 */
	len = sizeof(str) - 1;
	result = asn1_read_value(c2, "cA", str, &len);
	if (result == ASN1_SUCCESS && strcmp(str, "TRUE") == 0)
		*ca = 1;
	else
		*ca = 0;

	result = 0;
cleanup:
	asn1_delete_structure(&c2);

	return result;
}

/**
 * gnutls_x509_ext_export_basic_constraints:
 * @ca: non-zero for a CA
 * @pathlen: The path length constraint (set to -1 for no constraint)
 * @ext: The DER-encoded extension data; must be freed using gnutls_free().
 *
 * This function will convert the parameters provided to a basic constraints
 * DER encoded extension (2.5.29.19).
 (
 * The @ext data will be allocated using
 * gnutls_malloc().
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_export_basic_constraints(unsigned int ca, int pathlen,
					     gnutls_datum_t *ext)
{
	asn1_node c2 = NULL;
	const char *str;
	int result;

	if (ca == 0)
		str = "FALSE";
	else
		str = "TRUE";

	result = asn1_create_element(_gnutls_get_pkix(),
				     "PKIX1.BasicConstraints", &c2);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		result = _gnutls_asn2err(result);
		goto cleanup;
	}

	result = asn1_write_value(c2, "cA", str, 1);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		result = _gnutls_asn2err(result);
		goto cleanup;
	}

	if (pathlen < 0) {
		result = asn1_write_value(c2, "pathLenConstraint", NULL, 0);
		if (result < 0)
			result = _gnutls_asn2err(result);
	} else
		result = _gnutls_x509_write_uint32(c2, "pathLenConstraint",
						   pathlen);
	if (result < 0) {
		gnutls_assert();
		goto cleanup;
	}

	result = _gnutls_x509_der_encode(c2, "", ext, 0);
	if (result < 0) {
		gnutls_assert();
		goto cleanup;
	}

	result = 0;

cleanup:
	asn1_delete_structure(&c2);
	return result;
}

/**
 * gnutls_x509_ext_import_proxy:
 * @ext: the DER encoded extension data
 * @pathlen: pointer to output integer indicating path length (may be
 *   NULL), non-negative error codes indicate a present pCPathLenConstraint
 *   field and the actual value, -1 indicate that the field is absent.
 * @policyLanguage: output variable with OID of policy language
 * @policy: output variable with policy data
 * @sizeof_policy: output variable with size of policy data
 *
 * This function will return the information from a proxy certificate
 * extension. It reads the ProxyCertInfo X.509 extension (1.3.6.1.5.5.7.1.14).
 * The @policyLanguage and @policy values must be deinitialized using gnutls_free() after use.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_import_proxy(const gnutls_datum_t *ext, int *pathlen,
				 char **policyLanguage, char **policy,
				 size_t *sizeof_policy)
{
	asn1_node c2 = NULL;
	int result;
	gnutls_datum_t value1 = { NULL, 0 };
	gnutls_datum_t value2 = { NULL, 0 };

	if ((result = asn1_create_element(_gnutls_get_pkix(),
					  "PKIX1.ProxyCertInfo", &c2)) !=
	    ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(result);
	}

	result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		result = _gnutls_asn2err(result);
		goto cleanup;
	}

	if (pathlen) {
		result = _gnutls_x509_read_uint(c2, "pCPathLenConstraint",
						(unsigned int *)pathlen);
		if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
			*pathlen = -1;
		else if (result != GNUTLS_E_SUCCESS) {
			gnutls_assert();
			result = _gnutls_asn2err(result);
			goto cleanup;
		}
	}

	result = _gnutls_x509_read_value(c2, "proxyPolicy.policyLanguage",
					 &value1);
	if (result < 0) {
		gnutls_assert();
		goto cleanup;
	}

	result = _gnutls_x509_read_value(c2, "proxyPolicy.policy", &value2);
	if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) {
		if (policy)
			*policy = NULL;
		if (sizeof_policy)
			*sizeof_policy = 0;
	} else if (result < 0) {
		gnutls_assert();
		goto cleanup;
	} else {
		if (policy) {
			*policy = (char *)value2.data;
			value2.data = NULL;
		}
		if (sizeof_policy)
			*sizeof_policy = value2.size;
	}

	if (policyLanguage) {
		*policyLanguage = (char *)value1.data;
		value1.data = NULL;
	}

	result = 0;
cleanup:
	gnutls_free(value1.data);
	gnutls_free(value2.data);
	asn1_delete_structure(&c2);

	return result;
}

/**
 * gnutls_x509_ext_export_proxy:
 * @pathLenConstraint: A negative value will remove the path length constraint,
 *   while non-negative values will be set as the length of the pathLenConstraints field.
 * @policyLanguage: OID describing the language of @policy.
 * @policy: uint8_t byte array with policy language, can be %NULL
 * @sizeof_policy: size of @policy.
 * @ext: The DER-encoded extension data; must be freed using gnutls_free().
 *
 * This function will convert the parameters provided to a proxyCertInfo extension.
 *
 * The @ext data will be allocated using gnutls_malloc().
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_export_proxy(int pathLenConstraint,
				 const char *policyLanguage, const char *policy,
				 size_t sizeof_policy, gnutls_datum_t *ext)
{
	asn1_node c2 = NULL;
	int result;

	result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.ProxyCertInfo",
				     &c2);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(result);
	}

	if (pathLenConstraint < 0) {
		result = asn1_write_value(c2, "pCPathLenConstraint", NULL, 0);
		if (result != ASN1_SUCCESS) {
			gnutls_assert();
			result = _gnutls_asn2err(result);
			goto cleanup;
		}
	} else {
		result = _gnutls_x509_write_uint32(c2, "pCPathLenConstraint",
						   pathLenConstraint);

		if (result < 0) {
			gnutls_assert();
			goto cleanup;
		}
	}

	result = asn1_write_value(c2, "proxyPolicy.policyLanguage",
				  policyLanguage, 1);
	if (result < 0) {
		gnutls_assert();
		result = _gnutls_asn2err(result);
		goto cleanup;
	}

	result = asn1_write_value(c2, "proxyPolicy.policy", policy,
				  sizeof_policy);
	if (result < 0) {
		gnutls_assert();
		result = _gnutls_asn2err(result);
		goto cleanup;
	}

	result = _gnutls_x509_der_encode(c2, "", ext, 0);
	if (result < 0) {
		gnutls_assert();
		goto cleanup;
	}

	result = 0;
cleanup:
	asn1_delete_structure(&c2);
	return result;
}

static int decode_user_notice(const void *data, size_t size,
			      gnutls_datum_t *txt)
{
	asn1_node c2 = NULL;
	int ret, len;
	char choice_type[64];
	char name[128];
	gnutls_datum_t td = { NULL, 0 }, utd;

	ret = asn1_create_element(_gnutls_get_pkix(), "PKIX1.UserNotice", &c2);
	if (ret != ASN1_SUCCESS) {
		gnutls_assert();
		ret = GNUTLS_E_PARSING_ERROR;
		goto cleanup;
	}

	ret = _asn1_strict_der_decode(&c2, data, size, NULL);
	if (ret != ASN1_SUCCESS) {
		gnutls_assert();
		ret = GNUTLS_E_PARSING_ERROR;
		goto cleanup;
	}

	len = sizeof(choice_type);
	ret = asn1_read_value(c2, "explicitText", choice_type, &len);
	if (ret != ASN1_SUCCESS) {
		gnutls_assert();
		ret = GNUTLS_E_PARSING_ERROR;
		goto cleanup;
	}

	if (strcmp(choice_type, "utf8String") != 0 &&
	    strcmp(choice_type, "ia5String") != 0 &&
	    strcmp(choice_type, "bmpString") != 0 &&
	    strcmp(choice_type, "visibleString") != 0) {
		gnutls_assert();
		ret = GNUTLS_E_PARSING_ERROR;
		goto cleanup;
	}

	snprintf(name, sizeof(name), "explicitText.%s", choice_type);

	ret = _gnutls_x509_read_value(c2, name, &td);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	if (strcmp(choice_type, "bmpString") == 0) { /* convert to UTF-8 */
		ret = _gnutls_ucs2_to_utf8(td.data, td.size, &utd, 1);
		_gnutls_free_datum(&td);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

		td.data = utd.data;
		td.size = utd.size;
	} else {
		/* _gnutls_x509_read_value allows that */
		td.data[td.size] = 0;
	}

	txt->data = (void *)td.data;
	txt->size = td.size;
	ret = 0;

cleanup:
	asn1_delete_structure(&c2);
	return ret;
}

struct gnutls_x509_policies_st {
	struct gnutls_x509_policy_st policy[MAX_ENTRIES];
	unsigned int size;
};

/**
 * gnutls_x509_policies_init:
 * @policies: The authority key ID
 *
 * This function will initialize an authority key ID type.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_policies_init(gnutls_x509_policies_t *policies)
{
	*policies = gnutls_calloc(1, sizeof(struct gnutls_x509_policies_st));
	if (*policies == NULL)
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);

	return 0;
}

/**
 * gnutls_x509_policies_deinit:
 * @policies: The authority key identifier
 *
 * This function will deinitialize an authority key identifier type.
 *
 * Since: 3.3.0
 **/
void gnutls_x509_policies_deinit(gnutls_x509_policies_t policies)
{
	unsigned i;

	for (i = 0; i < policies->size; i++) {
		gnutls_x509_policy_release(&policies->policy[i]);
	}
	gnutls_free(policies);
}

/**
 * gnutls_x509_policies_get:
 * @policies: The policies
 * @seq: The index of the name to get
 * @policy: Will hold the policy
 *
 * This function will return a specific policy as stored in
 * the @policies type. The returned values should be treated as constant
 * and valid for the lifetime of @policies.
 *
 * The any policy OID is available as the %GNUTLS_X509_OID_POLICY_ANY macro.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
 * if the index is out of bounds, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_policies_get(gnutls_x509_policies_t policies, unsigned int seq,
			     struct gnutls_x509_policy_st *policy)
{
	if (seq >= policies->size)
		return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);

	if (policy) {
		memcpy(policy, &policies->policy[seq],
		       sizeof(struct gnutls_x509_policy_st));
	}

	return 0;
}

void _gnutls_x509_policies_erase(gnutls_x509_policies_t policies,
				 unsigned int seq)
{
	if (seq >= policies->size)
		return;

	memset(&policies->policy[seq], 0, sizeof(struct gnutls_x509_policy_st));
}

/**
 * gnutls_x509_policies_set:
 * @policies: An initialized policies
 * @seq: The index of the name to get
 * @policy: Contains the policy to set
 *
 * This function will store the specified policy in
 * the provided @policies.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_policies_set(gnutls_x509_policies_t policies,
			     const struct gnutls_x509_policy_st *policy)
{
	unsigned i;

	if (policies->size + 1 > MAX_ENTRIES)
		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);

	policies->policy[policies->size].oid = gnutls_strdup(policy->oid);
	if (policies->policy[policies->size].oid == NULL)
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);

	for (i = 0; i < policy->qualifiers; i++) {
		policies->policy[policies->size].qualifier[i].type =
			policy->qualifier[i].type;
		policies->policy[policies->size].qualifier[i].size =
			policy->qualifier[i].size;
		policies->policy[policies->size].qualifier[i].data =
			gnutls_malloc(policy->qualifier[i].size + 1);
		if (policies->policy[policies->size].qualifier[i].data == NULL)
			return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
		memcpy(policies->policy[policies->size].qualifier[i].data,
		       policy->qualifier[i].data, policy->qualifier[i].size);
		policies->policy[policies->size]
			.qualifier[i]
			.data[policy->qualifier[i].size] = 0;
	}

	policies->policy[policies->size].qualifiers = policy->qualifiers;
	policies->size++;

	return 0;
}

/**
 * gnutls_x509_ext_import_policies:
 * @ext: the DER encoded extension data
 * @policies: A pointer to an initialized policies.
 * @flags: should be zero
 *
 * This function will extract the certificate policy extension (2.5.29.32) 
 * and store it the provided policies.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_import_policies(const gnutls_datum_t *ext,
				    gnutls_x509_policies_t policies,
				    unsigned int flags)
{
	asn1_node c2 = NULL;
	char tmpstr[128];
	char tmpoid[MAX_OID_SIZE];
	gnutls_datum_t tmpd = { NULL, 0 };
	int ret, len;
	unsigned i, j, current = 0;

	ret = asn1_create_element(_gnutls_get_pkix(),
				  "PKIX1.certificatePolicies", &c2);
	if (ret != ASN1_SUCCESS) {
		gnutls_assert();
		ret = _gnutls_asn2err(ret);
		goto cleanup;
	}

	ret = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
	if (ret != ASN1_SUCCESS) {
		gnutls_assert();
		ret = _gnutls_asn2err(ret);
		goto cleanup;
	}

	for (j = 0;; j++) {
		if (j >= MAX_ENTRIES)
			break;

		memset(&policies->policy[j], 0,
		       sizeof(struct gnutls_x509_policy_st));

		/* create a string like "?1"
		 */
		snprintf(tmpstr, sizeof(tmpstr), "?%u.policyIdentifier", j + 1);
		current = j + 1;

		ret = _gnutls_x509_read_value(c2, tmpstr, &tmpd);
		if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
			break;

		if (ret < 0) {
			gnutls_assert();
			goto full_cleanup;
		}

		policies->policy[j].oid = (void *)tmpd.data;
		tmpd.data = NULL;

		for (i = 0; i < GNUTLS_MAX_QUALIFIERS; i++) {
			gnutls_datum_t td;

			snprintf(tmpstr, sizeof(tmpstr),
				 "?%u.policyQualifiers.?%u.policyQualifierId",
				 j + 1, i + 1);

			len = sizeof(tmpoid);
			ret = asn1_read_value(c2, tmpstr, tmpoid, &len);

			if (ret == ASN1_ELEMENT_NOT_FOUND)
				break; /* finished */

			if (ret != ASN1_SUCCESS) {
				gnutls_assert();
				ret = _gnutls_asn2err(ret);
				goto full_cleanup;
			}

			if (strcmp(tmpoid, "1.3.6.1.5.5.7.2.1") == 0) {
				snprintf(tmpstr, sizeof(tmpstr),
					 "?%u.policyQualifiers.?%u.qualifier",
					 j + 1, i + 1);

				ret = _gnutls_x509_read_string(
					c2, tmpstr, &td, ASN1_ETYPE_IA5_STRING,
					0);
				if (ret < 0) {
					gnutls_assert();
					goto full_cleanup;
				}

				policies->policy[j].qualifier[i].data =
					(void *)td.data;
				policies->policy[j].qualifier[i].size = td.size;
				td.data = NULL;
				policies->policy[j].qualifier[i].type =
					GNUTLS_X509_QUALIFIER_URI;
			} else if (strcmp(tmpoid, "1.3.6.1.5.5.7.2.2") == 0) {
				gnutls_datum_t txt = { NULL, 0 };

				snprintf(tmpstr, sizeof(tmpstr),
					 "?%u.policyQualifiers.?%u.qualifier",
					 j + 1, i + 1);

				ret = _gnutls_x509_read_value(c2, tmpstr, &td);
				if (ret < 0) {
					gnutls_assert();
					goto full_cleanup;
				}

				ret = decode_user_notice(td.data, td.size,
							 &txt);
				gnutls_free(td.data);

				if (ret < 0) {
					gnutls_assert();
					goto full_cleanup;
				}

				policies->policy[j].qualifier[i].data =
					(void *)txt.data;
				policies->policy[j].qualifier[i].size =
					txt.size;
				policies->policy[j].qualifier[i].type =
					GNUTLS_X509_QUALIFIER_NOTICE;
			} else
				policies->policy[j].qualifier[i].type =
					GNUTLS_X509_QUALIFIER_UNKNOWN;

			policies->policy[j].qualifiers++;
		}
	}

	policies->size = j;

	ret = 0;
	goto cleanup;

full_cleanup:
	for (j = 0; j < current; j++)
		gnutls_x509_policy_release(&policies->policy[j]);

cleanup:
	_gnutls_free_datum(&tmpd);
	asn1_delete_structure(&c2);
	return ret;
}

static int encode_user_notice(const gnutls_datum_t *txt,
			      gnutls_datum_t *der_data)
{
	int result;
	asn1_node c2 = NULL;

	if ((result = asn1_create_element(_gnutls_get_pkix(),
					  "PKIX1.UserNotice", &c2)) !=
	    ASN1_SUCCESS) {
		gnutls_assert();
		result = _gnutls_asn2err(result);
		goto error;
	}

	/* delete noticeRef */
	result = asn1_write_value(c2, "noticeRef", NULL, 0);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		result = _gnutls_asn2err(result);
		goto error;
	}

	result = asn1_write_value(c2, "explicitText", "utf8String", 1);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		result = _gnutls_asn2err(result);
		goto error;
	}

	result = asn1_write_value(c2, "explicitText.utf8String", txt->data,
				  txt->size);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		result = _gnutls_asn2err(result);
		goto error;
	}

	result = _gnutls_x509_der_encode(c2, "", der_data, 0);
	if (result < 0) {
		gnutls_assert();
		goto error;
	}

	result = 0;

error:
	asn1_delete_structure(&c2);
	return result;
}

/**
 * gnutls_x509_ext_export_policies:
 * @policies: A pointer to an initialized policies.
 * @ext: The DER-encoded extension data; must be freed using gnutls_free().
 *
 * This function will convert the provided policies, to a certificate policy
 * DER encoded extension (2.5.29.32).
 *
 * The @ext data will be allocated using gnutls_malloc().
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_export_policies(gnutls_x509_policies_t policies,
				    gnutls_datum_t *ext)
{
	int result;
	unsigned i, j;
	gnutls_datum_t der_data = { NULL, 0 }, tmpd;
	asn1_node c2 = NULL;
	const char *oid;

	result = asn1_create_element(_gnutls_get_pkix(),
				     "PKIX1.certificatePolicies", &c2);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		result = _gnutls_asn2err(result);
		goto cleanup;
	}

	for (j = 0; j < policies->size; j++) {
		/* 1. write a new policy */
		result = asn1_write_value(c2, "", "NEW", 1);
		if (result != ASN1_SUCCESS) {
			gnutls_assert();
			result = _gnutls_asn2err(result);
			goto cleanup;
		}

		/* 2. Add the OID.
		 */
		result = asn1_write_value(c2, "?LAST.policyIdentifier",
					  policies->policy[j].oid, 1);
		if (result != ASN1_SUCCESS) {
			gnutls_assert();
			result = _gnutls_asn2err(result);
			goto cleanup;
		}

		if (policies->policy[j].qualifiers == 0) {
			/* remove the optional policyQualifiers if none are present. */
			result = asn1_write_value(c2, "?LAST.policyQualifiers",
						  NULL, 0);
			if (result != ASN1_SUCCESS) {
				gnutls_assert();
				result = _gnutls_asn2err(result);
				goto cleanup;
			}
		}

		for (i = 0; i < MIN(policies->policy[j].qualifiers,
				    GNUTLS_MAX_QUALIFIERS);
		     i++) {
			result = asn1_write_value(c2, "?LAST.policyQualifiers",
						  "NEW", 1);
			if (result != ASN1_SUCCESS) {
				gnutls_assert();
				result = _gnutls_asn2err(result);
				goto cleanup;
			}

			if (policies->policy[j].qualifier[i].type ==
			    GNUTLS_X509_QUALIFIER_URI)
				oid = "1.3.6.1.5.5.7.2.1";
			else if (policies->policy[j].qualifier[i].type ==
				 GNUTLS_X509_QUALIFIER_NOTICE)
				oid = "1.3.6.1.5.5.7.2.2";
			else {
				result = gnutls_assert_val(
					GNUTLS_E_INVALID_REQUEST);
				goto cleanup;
			}

			result = asn1_write_value(
				c2,
				"?LAST.policyQualifiers.?LAST.policyQualifierId",
				oid, 1);
			if (result != ASN1_SUCCESS) {
				gnutls_assert();
				result = _gnutls_asn2err(result);
				goto cleanup;
			}

			if (policies->policy[j].qualifier[i].type ==
			    GNUTLS_X509_QUALIFIER_URI) {
				tmpd.data = (void *)policies->policy[j]
						    .qualifier[i]
						    .data;
				tmpd.size =
					policies->policy[j].qualifier[i].size;
				result = _gnutls_x509_write_string(
					c2,
					"?LAST.policyQualifiers.?LAST.qualifier",
					&tmpd, ASN1_ETYPE_IA5_STRING);
				if (result < 0) {
					gnutls_assert();
					goto cleanup;
				}
			} else if (policies->policy[j].qualifier[i].type ==
				   GNUTLS_X509_QUALIFIER_NOTICE) {
				tmpd.data = (void *)policies->policy[j]
						    .qualifier[i]
						    .data;
				tmpd.size =
					policies->policy[j].qualifier[i].size;

				if (tmpd.size > 200) {
					gnutls_assert();
					result = GNUTLS_E_INVALID_REQUEST;
					goto cleanup;
				}

				result = encode_user_notice(&tmpd, &der_data);
				if (result < 0) {
					gnutls_assert();
					goto cleanup;
				}

				result = _gnutls_x509_write_value(
					c2,
					"?LAST.policyQualifiers.?LAST.qualifier",
					&der_data);
				_gnutls_free_datum(&der_data);
				if (result < 0) {
					gnutls_assert();
					goto cleanup;
				}
			}
		}
	}

	result = _gnutls_x509_der_encode(c2, "", ext, 0);
	if (result < 0) {
		gnutls_assert();
		goto cleanup;
	}

cleanup:
	asn1_delete_structure(&c2);

	return result;
}

struct crl_dist_point_st {
	unsigned int type;
	gnutls_datum_t san;
	unsigned int reasons;
};

struct gnutls_x509_crl_dist_points_st {
	struct crl_dist_point_st *points;
	unsigned int size;
};

/**
 * gnutls_x509_crl_dist_points_init:
 * @cdp: The CRL distribution points
 *
 * This function will initialize a CRL distribution points type.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_crl_dist_points_init(gnutls_x509_crl_dist_points_t *cdp)
{
	*cdp = gnutls_calloc(1, sizeof(struct gnutls_x509_crl_dist_points_st));
	if (*cdp == NULL)
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);

	return 0;
}

/**
 * gnutls_x509_crl_dist_points_deinit:
 * @cdp: The CRL distribution points
 *
 * This function will deinitialize a CRL distribution points type.
 *
 * Since: 3.3.0
 **/
void gnutls_x509_crl_dist_points_deinit(gnutls_x509_crl_dist_points_t cdp)
{
	unsigned i;

	for (i = 0; i < cdp->size; i++) {
		gnutls_free(cdp->points[i].san.data);
	}
	gnutls_free(cdp->points);
	gnutls_free(cdp);
}

/**
 * gnutls_x509_crl_dist_points_get:
 * @cdp: The CRL distribution points
 * @seq: specifies the sequence number of the distribution point (0 for the first one, 1 for the second etc.)
 * @type: The name type of the corresponding name (gnutls_x509_subject_alt_name_t)
 * @san: The distribution point names (to be treated as constant)
 * @reasons: Revocation reasons. An ORed sequence of flags from %gnutls_x509_crl_reason_flags_t.
 *
 * This function retrieves the individual CRL distribution points (2.5.29.31),
 * contained in provided type. 
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
 * if the index is out of bounds, otherwise a negative error value.
 **/

int gnutls_x509_crl_dist_points_get(gnutls_x509_crl_dist_points_t cdp,
				    unsigned int seq, unsigned int *type,
				    gnutls_datum_t *san, unsigned int *reasons)
{
	if (seq >= cdp->size)
		return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);

	if (reasons)
		*reasons = cdp->points[seq].reasons;

	if (type)
		*type = cdp->points[seq].type;

	if (san) {
		san->data = cdp->points[seq].san.data;
		san->size = cdp->points[seq].san.size;
	}

	return 0;
}

static int crl_dist_points_set(gnutls_x509_crl_dist_points_t cdp,
			       gnutls_x509_subject_alt_name_t type,
			       const gnutls_datum_t *san, unsigned int reasons)
{
	void *tmp;

	if (unlikely(INT_ADD_OVERFLOW(cdp->size, 1))) {
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
	}

	/* new dist point */
	tmp = _gnutls_reallocarray(cdp->points, cdp->size + 1,
				   sizeof(cdp->points[0]));
	if (tmp == NULL) {
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
	}
	cdp->points = tmp;

	cdp->points[cdp->size].type = type;
	cdp->points[cdp->size].san.data = san->data;
	cdp->points[cdp->size].san.size = san->size;
	cdp->points[cdp->size].reasons = reasons;

	cdp->size++;
	return 0;
}

/**
 * gnutls_x509_crl_dist_points_set:
 * @cdp: The CRL distribution points
 * @type: The type of the name (of %gnutls_subject_alt_names_t)
 * @san: The point name data
 * @reasons: Revocation reasons. An ORed sequence of flags from %gnutls_x509_crl_reason_flags_t.
 *
 * This function will store the specified CRL distribution point value
 * the @cdp type.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_crl_dist_points_set(gnutls_x509_crl_dist_points_t cdp,
				    gnutls_x509_subject_alt_name_t type,
				    const gnutls_datum_t *san,
				    unsigned int reasons)
{
	int ret;
	gnutls_datum_t t_san;

	ret = _gnutls_set_datum(&t_san, san->data, san->size);
	if (ret < 0)
		return gnutls_assert_val(ret);

	ret = crl_dist_points_set(cdp, type, &t_san, reasons);
	if (ret < 0) {
		gnutls_free(t_san.data);
		return gnutls_assert_val(ret);
	}

	return 0;
}

/**
 * gnutls_x509_ext_import_crl_dist_points:
 * @ext: the DER encoded extension data
 * @cdp: A pointer to an initialized CRL distribution points.
 * @flags: should be zero
 *
 * This function will extract the CRL distribution points extension (2.5.29.31) 
 * and store it into the provided type.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_import_crl_dist_points(const gnutls_datum_t *ext,
					   gnutls_x509_crl_dist_points_t cdp,
					   unsigned int flags)
{
	int result;
	asn1_node c2 = NULL;
	char name[MAX_NAME_SIZE];
	int len, ret;
	uint8_t reasons[2];
	unsigned i, type, rflags, j;
	gnutls_datum_t san = { NULL, 0 };

	result = asn1_create_element(_gnutls_get_pkix(),
				     "PKIX1.CRLDistributionPoints", &c2);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(result);
	}

	result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);

	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		ret = _gnutls_asn2err(result);
		goto cleanup;
	}

	/* Return the different names from the first CRLDistr. point.
	 * The whole thing is a mess.
	 */

	i = 0;
	do {
		snprintf(name, sizeof(name), "?%u.reasons", (unsigned)i + 1);

		len = sizeof(reasons);
		result = asn1_read_value(c2, name, reasons, &len);

		if (result != ASN1_VALUE_NOT_FOUND &&
		    result != ASN1_ELEMENT_NOT_FOUND &&
		    result != ASN1_SUCCESS) {
			gnutls_assert();
			ret = _gnutls_asn2err(result);
			break;
		}

		if (result == ASN1_VALUE_NOT_FOUND ||
		    result == ASN1_ELEMENT_NOT_FOUND)
			rflags = 0;
		else
			rflags = reasons[0] | (reasons[1] << 8);

		snprintf(name, sizeof(name), "?%u.distributionPoint.fullName",
			 (unsigned)i + 1);

		for (j = 0;; j++) {
			san.data = NULL;
			san.size = 0;

			ret = _gnutls_parse_general_name2(c2, name, j, &san,
							  &type, 0);
			if (j > 0 &&
			    ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
				ret = 0;
				break;
			}
			if (ret < 0)
				break;

			ret = crl_dist_points_set(cdp, type, &san, rflags);
			if (ret < 0)
				break;
			san.data = NULL; /* it is now in cdp */
		}

		i++;
	} while (ret >= 0);

	if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
		gnutls_assert();
		gnutls_free(san.data);
		goto cleanup;
	}

	ret = 0;
cleanup:
	asn1_delete_structure(&c2);
	return ret;
}

/**
 * gnutls_x509_ext_export_crl_dist_points:
 * @cdp: A pointer to an initialized CRL distribution points.
 * @ext: The DER-encoded extension data; must be freed using gnutls_free().
 *
 * This function will convert the provided policies, to a certificate policy
 * DER encoded extension (2.5.29.31).
 *
 * The @ext data will be allocated using gnutls_malloc().
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_export_crl_dist_points(gnutls_x509_crl_dist_points_t cdp,
					   gnutls_datum_t *ext)
{
	asn1_node c2 = NULL;
	int result;
	uint8_t reasons[2];
	unsigned i;

	result = asn1_create_element(_gnutls_get_pkix(),
				     "PKIX1.CRLDistributionPoints", &c2);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		result = _gnutls_asn2err(result);
		goto cleanup;
	}

	for (i = 0; i < cdp->size; i++) {
		if (i == 0 ||
		    cdp->points[i].reasons != cdp->points[i - 1].reasons) {
			result = asn1_write_value(c2, "", "NEW", 1);
			if (result != ASN1_SUCCESS) {
				gnutls_assert();
				result = _gnutls_asn2err(result);
				goto cleanup;
			}

			if (cdp->points[i].reasons) {
				reasons[0] = cdp->points[i].reasons & 0xff;
				reasons[1] = cdp->points[i].reasons >> 8;

				result = asn1_write_value(c2, "?LAST.reasons",
							  reasons, 2);
			} else {
				result = asn1_write_value(c2, "?LAST.reasons",
							  NULL, 0);
			}

			if (result != ASN1_SUCCESS) {
				gnutls_assert();
				result = _gnutls_asn2err(result);
				goto cleanup;
			}

			result = asn1_write_value(c2, "?LAST.cRLIssuer", NULL,
						  0);
			if (result != ASN1_SUCCESS) {
				gnutls_assert();
				result = _gnutls_asn2err(result);
				goto cleanup;
			}
			/* When used as type CHOICE.
			 */
			result = asn1_write_value(c2, "?LAST.distributionPoint",
						  "fullName", 1);
			if (result != ASN1_SUCCESS) {
				gnutls_assert();
				result = _gnutls_asn2err(result);
				goto cleanup;
			}
		}

		result = _gnutls_write_new_general_name(
			c2, "?LAST.distributionPoint.fullName",
			cdp->points[i].type, cdp->points[i].san.data,
			cdp->points[i].san.size);
		if (result < 0) {
			gnutls_assert();
			goto cleanup;
		}
	}

	result = _gnutls_x509_der_encode(c2, "", ext, 0);
	if (result < 0) {
		gnutls_assert();
		goto cleanup;
	}

	result = 0;

cleanup:
	asn1_delete_structure(&c2);

	return result;
}

struct gnutls_x509_aia_st {
	struct {
		gnutls_datum_t oid;
		unsigned int san_type;
		gnutls_datum_t san;
	} *aia;
	unsigned int size;
};

/**
 * gnutls_x509_aia_init:
 * @aia: The authority info access
 *
 * This function will initialize an authority info access type.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_aia_init(gnutls_x509_aia_t *aia)
{
	*aia = gnutls_calloc(1, sizeof(struct gnutls_x509_aia_st));
	if (*aia == NULL)
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);

	return 0;
}

/**
 * gnutls_x509_aia_deinit:
 * @aia: The authority info access
 *
 * This function will deinitialize an authority info access type.
 *
 * Since: 3.3.0
 **/
void gnutls_x509_aia_deinit(gnutls_x509_aia_t aia)
{
	unsigned i;

	for (i = 0; i < aia->size; i++) {
		gnutls_free(aia->aia[i].san.data);
		gnutls_free(aia->aia[i].oid.data);
	}
	gnutls_free(aia->aia);
	gnutls_free(aia);
}

/**
 * gnutls_x509_aia_get:
 * @aia: The authority info access
 * @seq: specifies the sequence number of the access descriptor (0 for the first one, 1 for the second etc.)
 * @oid: the type of available data; to be treated as constant.
 * @san_type: Will hold the type of the name of %gnutls_subject_alt_names_t (may be null).
 * @san: the access location name; to be treated as constant (may be null).
 *
 * This function reads from the Authority Information Access type.
 *
 * The @seq input parameter is used to indicate which member of the
 * sequence the caller is interested in.  The first member is 0, the
 * second member 1 and so on.  When the @seq value is out of bounds,
 * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned.
 *
 * Typically @oid is %GNUTLS_OID_AD_CAISSUERS or %GNUTLS_OID_AD_OCSP.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_aia_get(gnutls_x509_aia_t aia, unsigned int seq,
			gnutls_datum_t *oid, unsigned *san_type,
			gnutls_datum_t *san)
{
	if (seq >= aia->size)
		return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);

	if (san_type)
		*san_type = aia->aia[seq].san_type;
	if (san) {
		san->data = aia->aia[seq].san.data;
		san->size = aia->aia[seq].san.size;
	}

	if (oid) {
		oid->data = aia->aia[seq].oid.data;
		oid->size = aia->aia[seq].oid.size;
	}

	return 0;
}

int _gnutls_alt_name_process(gnutls_datum_t *out, unsigned type,
			     const gnutls_datum_t *san, unsigned raw)
{
	int ret;
	if (type == GNUTLS_SAN_DNSNAME && !raw) {
		ret = gnutls_idna_map((char *)san->data, san->size, out, 0);
		if (ret < 0) {
			return gnutls_assert_val(ret);
		}
	} else if (type == GNUTLS_SAN_RFC822NAME && !raw) {
		ret = _gnutls_idna_email_map((char *)san->data, san->size, out);
		if (ret < 0) {
			return gnutls_assert_val(ret);
		}
	} else if (type == GNUTLS_SAN_URI && !raw) {
		if (!_gnutls_str_is_print((char *)san->data, san->size)) {
			_gnutls_debug_log("non-ASCII URIs are not supported\n");
			return gnutls_assert_val(
				GNUTLS_E_UNIMPLEMENTED_FEATURE);
		} else {
			ret = _gnutls_set_strdatum(out, san->data, san->size);
			if (ret < 0)
				return gnutls_assert_val(ret);
		}
	} else {
		ret = _gnutls_set_strdatum(out, san->data, san->size);
		if (ret < 0)
			return gnutls_assert_val(ret);
	}

	return 0;
}

/**
 * gnutls_x509_aia_set:
 * @aia: The authority info access
 * @oid: the type of data.
 * @san_type: The type of the name (of %gnutls_subject_alt_names_t)
 * @san: The alternative name data
 * @othername_oid: The object identifier if @san_type is %GNUTLS_SAN_OTHERNAME
 *
 * This function will store the specified alternative name in
 * the @aia type. 
 *
 * Typically the value for @oid should be %GNUTLS_OID_AD_OCSP, or
 * %GNUTLS_OID_AD_CAISSUERS.
 *
 * Since version 3.5.7 the %GNUTLS_SAN_RFC822NAME, and %GNUTLS_SAN_DNSNAME,
 * are converted to ACE format when necessary.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_aia_set(gnutls_x509_aia_t aia, const char *oid,
			unsigned san_type, const gnutls_datum_t *san)
{
	int ret;
	void *tmp;
	unsigned indx;

	if (unlikely(INT_ADD_OVERFLOW(aia->size, 1))) {
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
	}

	tmp = _gnutls_reallocarray(aia->aia, aia->size + 1,
				   sizeof(aia->aia[0]));
	if (tmp == NULL) {
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
	}
	aia->aia = tmp;
	indx = aia->size;

	aia->aia[indx].san_type = san_type;
	if (oid) {
		aia->aia[indx].oid.data = (void *)gnutls_strdup(oid);
		aia->aia[indx].oid.size = strlen(oid);
	} else {
		aia->aia[indx].oid.data = NULL;
		aia->aia[indx].oid.size = 0;
	}

	ret = _gnutls_alt_name_process(&aia->aia[indx].san, san_type, san, 0);
	if (ret < 0)
		return gnutls_assert_val(ret);

	aia->size++;

	return 0;
}

static int parse_aia(asn1_node c2, gnutls_x509_aia_t aia)
{
	int len;
	char nptr[MAX_NAME_SIZE];
	int ret, result;
	char tmpoid[MAX_OID_SIZE];
	void *tmp;
	unsigned i, indx;

	for (i = 1;; i++) {
		snprintf(nptr, sizeof(nptr), "?%u.accessMethod", i);

		len = sizeof(tmpoid);
		result = asn1_read_value(c2, nptr, tmpoid, &len);
		if (result == ASN1_VALUE_NOT_FOUND ||
		    result == ASN1_ELEMENT_NOT_FOUND) {
			ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
			break;
		}

		if (result != ASN1_SUCCESS) {
			gnutls_assert();
			return _gnutls_asn2err(result);
		}

		indx = aia->size;
		if (unlikely(INT_ADD_OVERFLOW(aia->size, 1))) {
			return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
		}
		tmp = _gnutls_reallocarray(aia->aia, aia->size + 1,
					   sizeof(aia->aia[0]));
		if (tmp == NULL) {
			return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
		}
		aia->aia = tmp;

		snprintf(nptr, sizeof(nptr), "?%u.accessLocation", i);

		ret = _gnutls_parse_general_name2(c2, nptr, -1,
						  &aia->aia[indx].san,
						  &aia->aia[indx].san_type, 0);
		if (ret < 0)
			break;

		/* we do the strdup after parsing to avoid a memory leak */
		aia->aia[indx].oid.data = (void *)gnutls_strdup(tmpoid);
		aia->aia[indx].oid.size = strlen(tmpoid);

		aia->size++;

		if (aia->aia[indx].oid.data == NULL) {
			gnutls_assert();
			return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
		}
	}

	assert(ret < 0);
	if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
		return ret;
	}

	return 0;
}

/**
 * gnutls_x509_ext_import_aia:
 * @ext: The DER-encoded extension data
 * @aia: The authority info access
 * @flags: should be zero
 *
 * This function extracts the Authority Information Access (AIA)
 * extension from the provided DER-encoded data; see RFC 5280 section 4.2.2.1 
 * for more information on the extension.  The
 * AIA extension holds a sequence of AccessDescription (AD) data.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_import_aia(const gnutls_datum_t *ext, gnutls_x509_aia_t aia,
			       unsigned int flags)
{
	int ret;
	asn1_node c2 = NULL;

	if (ext->size == 0 || ext->data == NULL) {
		gnutls_assert();
		return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
	}

	ret = asn1_create_element(_gnutls_get_pkix(),
				  "PKIX1.AuthorityInfoAccessSyntax", &c2);
	if (ret != ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(ret);
	}

	ret = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
	if (ret != ASN1_SUCCESS) {
		gnutls_assert();
		ret = _gnutls_asn2err(ret);
		goto cleanup;
	}

	ret = parse_aia(c2, aia);
	if (ret < 0) {
		gnutls_assert();
	}

cleanup:
	asn1_delete_structure(&c2);

	return ret;
}

/**
 * gnutls_x509_ext_export_aia:
 * @aia: The authority info access
 * @ext: The DER-encoded extension data; must be freed using gnutls_free().
 *
 * This function will DER encode the Authority Information Access (AIA)
 * extension; see RFC 5280 section 4.2.2.1 for more information on the
 * extension.  
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_export_aia(gnutls_x509_aia_t aia, gnutls_datum_t *ext)
{
	int ret, result;
	asn1_node c2 = NULL;
	unsigned int i;

	ret = asn1_create_element(_gnutls_get_pkix(),
				  "PKIX1.AuthorityInfoAccessSyntax", &c2);
	if (ret != ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(ret);
	}

	/* 1. create a new element.
	 */
	for (i = 0; i < aia->size; i++) {
		result = asn1_write_value(c2, "", "NEW", 1);
		if (result != ASN1_SUCCESS) {
			gnutls_assert();
			ret = _gnutls_asn2err(result);
			goto cleanup;
		}

		/* 2. Add the OID.
		 */
		result = asn1_write_value(c2, "?LAST.accessMethod",
					  aia->aia[i].oid.data, 1);
		if (result != ASN1_SUCCESS) {
			gnutls_assert();
			ret = _gnutls_asn2err(result);
			goto cleanup;
		}

		ret = _gnutls_write_general_name(c2, "?LAST.accessLocation",
						 aia->aia[i].san_type,
						 aia->aia[i].san.data,
						 aia->aia[i].san.size);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}
	}

	ret = _gnutls_x509_der_encode(c2, "", ext, 0);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

cleanup:
	asn1_delete_structure(&c2);

	return ret;
}

struct gnutls_x509_key_purposes_st {
	gnutls_datum_t oid[MAX_ENTRIES];
	unsigned int size;
};

/**
 * gnutls_subject_alt_names_init:
 * @p: The key purposes
 *
 * This function will initialize an alternative names type.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_key_purpose_init(gnutls_x509_key_purposes_t *p)
{
	*p = gnutls_calloc(1, sizeof(struct gnutls_x509_key_purposes_st));
	if (*p == NULL) {
		gnutls_assert();
		return GNUTLS_E_MEMORY_ERROR;
	}

	return 0;
}

static void key_purposes_deinit(gnutls_x509_key_purposes_t p)
{
	unsigned int i;

	for (i = 0; i < p->size; i++) {
		gnutls_free(p->oid[i].data);
	}
}

/**
 * gnutls_x509_key_purpose_deinit:
 * @p: The key purposes
 *
 * This function will deinitialize a key purposes type.
 *
 * Since: 3.3.0
 **/
void gnutls_x509_key_purpose_deinit(gnutls_x509_key_purposes_t p)
{
	key_purposes_deinit(p);
	gnutls_free(p);
}

/**
 * gnutls_x509_key_purpose_set:
 * @p: The key purposes
 * @oid: The object identifier of the key purpose
 *
 * This function will store the specified key purpose in the
 * purposes.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_key_purpose_set(gnutls_x509_key_purposes_t p, const char *oid)
{
	if (p->size + 1 > MAX_ENTRIES)
		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);

	p->oid[p->size].data = (void *)gnutls_strdup(oid);
	if (p->oid[p->size].data == NULL)
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);

	p->oid[p->size].size = strlen(oid);
	p->size++;

	return 0;
}

/**
 * gnutls_x509_key_purpose_get:
 * @p: The key purposes
 * @idx: The index of the key purpose to retrieve
 * @oid: Will hold the object identifier of the key purpose (to be treated as constant)
 *
 * This function will retrieve the specified by the index key purpose in the
 * purposes type. The object identifier will be a null terminated string.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
 * if the index is out of bounds, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_key_purpose_get(gnutls_x509_key_purposes_t p, unsigned idx,
				gnutls_datum_t *oid)
{
	if (idx >= p->size)
		return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);

	oid->data = p->oid[idx].data;
	oid->size = p->oid[idx].size;

	return 0;
}

/**
 * gnutls_x509_ext_import_key_purposes:
 * @ext: The DER-encoded extension data
 * @p: The key purposes
 * @flags: should be zero
 *
 * This function will extract the key purposes in the provided DER-encoded
 * ExtKeyUsageSyntax PKIX extension, to a %gnutls_x509_key_purposes_t type. 
 * The data must be initialized.
 * 
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_import_key_purposes(const gnutls_datum_t *ext,
					gnutls_x509_key_purposes_t p,
					unsigned int flags)
{
	char tmpstr[MAX_NAME_SIZE];
	int result, ret;
	asn1_node c2 = NULL;
	gnutls_datum_t oid = { NULL, 0 };
	unsigned i;

	result = asn1_create_element(_gnutls_get_pkix(),
				     "PKIX1.ExtKeyUsageSyntax", &c2);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(result);
	}

	result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		ret = _gnutls_asn2err(result);
		goto cleanup;
	}

	key_purposes_deinit(p);
	i = 0;
	p->size = 0;

	for (; i < MAX_ENTRIES; i++) {
		/* create a string like "?1"
		 */
		snprintf(tmpstr, sizeof(tmpstr), "?%u", i + 1);

		ret = _gnutls_x509_read_value(c2, tmpstr, &oid);
		if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) {
			break;
		}

		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

		p->oid[i].data = oid.data;
		p->oid[i].size = oid.size;

		oid.data = NULL;
		oid.size = 0;
		p->size++;
	}

	ret = 0;
cleanup:
	gnutls_free(oid.data);
	asn1_delete_structure(&c2);

	return ret;
}

/**
 * gnutls_x509_ext_export_key_purposes:
 * @p: The key purposes
 * @ext: The DER-encoded extension data; must be freed using gnutls_free().
 *
 * This function will convert the key purposes type to a
 * DER-encoded PKIX ExtKeyUsageSyntax (2.5.29.37) extension. The output data in 
 * @ext will be allocated using gnutls_malloc().
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.3.0
 **/
int gnutls_x509_ext_export_key_purposes(gnutls_x509_key_purposes_t p,
					gnutls_datum_t *ext)
{
	int result, ret;
	asn1_node c2 = NULL;
	unsigned i;

	result = asn1_create_element(_gnutls_get_pkix(),
				     "PKIX1.ExtKeyUsageSyntax", &c2);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(result);
	}

	/* generate the extension.
	 */
	for (i = 0; i < p->size; i++) {
		/* 1. create a new element.
		 */
		result = asn1_write_value(c2, "", "NEW", 1);
		if (result != ASN1_SUCCESS) {
			gnutls_assert();
			ret = _gnutls_asn2err(result);
			goto cleanup;
		}

		/* 2. Add the OID.
		 */
		result = asn1_write_value(c2, "?LAST", p->oid[i].data, 1);
		if (result != ASN1_SUCCESS) {
			gnutls_assert();
			ret = _gnutls_asn2err(result);
			goto cleanup;
		}
	}

	ret = _gnutls_x509_der_encode(c2, "", ext, 0);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	ret = 0;

cleanup:
	asn1_delete_structure(&c2);
	return ret;
}

/**
 * gnutls_ext_deinit:
 * @ext: The extensions structure
 *
 * This function will deinitialize an extensions structure.
 *
 * Since: 3.3.8
 **/
void gnutls_x509_ext_deinit(gnutls_x509_ext_st *ext)
{
	gnutls_free(ext->oid);
	gnutls_free(ext->data.data);
}

int _gnutls_x509_decode_ext(const gnutls_datum_t *der, gnutls_x509_ext_st *out)
{
	asn1_node c2 = NULL;
	char str_critical[10];
	char oid[MAX_OID_SIZE];
	int result, len, ret;

	memset(out, 0, sizeof(*out));

	/* decode der */
	result =
		asn1_create_element(_gnutls_get_pkix(), "PKIX1.Extension", &c2);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(result);
	}

	result = _asn1_strict_der_decode(&c2, der->data, der->size, NULL);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		ret = _gnutls_asn2err(result);
		goto cleanup;
	}

	len = sizeof(oid) - 1;
	result = asn1_read_value(c2, "extnID", oid, &len);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		ret = _gnutls_asn2err(result);
		goto cleanup;
	}

	len = sizeof(str_critical) - 1;
	result = asn1_read_value(c2, "critical", str_critical, &len);
	if (result != ASN1_SUCCESS) {
		gnutls_assert();
		ret = _gnutls_asn2err(result);
		goto cleanup;
	}

	if (str_critical[0] == 'T')
		out->critical = 1;
	else
		out->critical = 0;

	ret = _gnutls_x509_read_value(c2, "extnValue", &out->data);
	if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE ||
	    ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) {
		out->data.data = NULL;
		out->data.size = 0;
	} else if (ret < 0) {
		gnutls_assert();
		goto fail;
	}

	out->oid = gnutls_strdup(oid);
	if (out->oid == NULL) {
		ret = GNUTLS_E_MEMORY_ERROR;
		goto fail;
	}

	ret = 0;
	goto cleanup;
fail:
	memset(out, 0, sizeof(*out));
cleanup:
	asn1_delete_structure(&c2);
	return ret;
}

/* flags can be zero or GNUTLS_EXT_FLAG_APPEND
 */
static int parse_tlsfeatures(asn1_node c2, gnutls_x509_tlsfeatures_t f,
			     unsigned flags)
{
	char nptr[MAX_NAME_SIZE];
	int result;
	unsigned i, indx, j;
	unsigned int feature;

	if (!(flags & GNUTLS_EXT_FLAG_APPEND))
		f->size = 0;

	for (i = 1;; i++) {
		unsigned skip = 0;
		snprintf(nptr, sizeof(nptr), "?%u", i);

		result = _gnutls_x509_read_uint(c2, nptr, &feature);

		if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND ||
		    result == GNUTLS_E_ASN1_VALUE_NOT_FOUND) {
			break;
		} else if (result != GNUTLS_E_SUCCESS) {
			gnutls_assert();
			return _gnutls_asn2err(result);
		}

		if (feature > UINT16_MAX) {
			gnutls_assert();
			return GNUTLS_E_CERTIFICATE_ERROR;
		}

		/* skip duplicates */
		for (j = 0; j < f->size; j++) {
			if (f->feature[j] == feature) {
				skip = 1;
				break;
			}
		}

		if (!skip) {
			if (f->size >=
			    sizeof(f->feature) / sizeof(f->feature[0])) {
				gnutls_assert();
				return GNUTLS_E_INTERNAL_ERROR;
			}

			indx = f->size;
			f->feature[indx] = feature;
			f->size++;
		}
	}

	return 0;
}

/**
 * gnutls_x509_ext_import_tlsfeatures:
 * @ext: The DER-encoded extension data
 * @f: The features structure
 * @flags: zero or %GNUTLS_EXT_FLAG_APPEND
 *
 * This function will export the features in the provided DER-encoded
 * TLS Features PKIX extension, to a %gnutls_x509_tlsfeatures_t type. @f
 * must be initialized.
 *
 * When the @flags is set to %GNUTLS_EXT_FLAG_APPEND,
 * then if the @features structure is empty this function will behave
 * identically as if the flag was not set. Otherwise if there are elements 
 * in the @features structure then they will be merged with.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.5.1
 **/
int gnutls_x509_ext_import_tlsfeatures(const gnutls_datum_t *ext,
				       gnutls_x509_tlsfeatures_t f,
				       unsigned int flags)
{
	int ret;
	asn1_node c2 = NULL;

	if (ext->size == 0 || ext->data == NULL) {
		gnutls_assert();
		return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
	}

	ret = asn1_create_element(_gnutls_get_pkix(), "PKIX1.TlsFeatures", &c2);
	if (ret != ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(ret);
	}

	ret = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
	if (ret != ASN1_SUCCESS) {
		gnutls_assert();
		ret = _gnutls_asn2err(ret);
		goto cleanup;
	}

	ret = parse_tlsfeatures(c2, f, flags);
	if (ret < 0) {
		gnutls_assert();
	}

cleanup:
	asn1_delete_structure(&c2);

	return ret;
}

/**
 * gnutls_x509_ext_export_tlsfeatures:
 * @f: The features structure
 * @ext: The DER-encoded extension data; must be freed using gnutls_free().
 *
 * This function will convert the provided TLS features structure structure to a
 * DER-encoded TLS features PKIX extension. The output data in @ext will be allocated using
 * gnutls_malloc().
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
 *
 * Since: 3.5.1
 **/
int gnutls_x509_ext_export_tlsfeatures(gnutls_x509_tlsfeatures_t f,
				       gnutls_datum_t *ext)
{
	if (f == NULL) {
		gnutls_assert();
		return GNUTLS_E_INVALID_REQUEST;
	}

	asn1_node c2 = NULL;
	int ret;
	unsigned i;

	ret = asn1_create_element(_gnutls_get_pkix(), "PKIX1.TlsFeatures", &c2);
	if (ret != ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(ret);
	}

	for (i = 0; i < f->size; ++i) {
		ret = asn1_write_value(c2, "", "NEW", 1);
		if (ret != ASN1_SUCCESS) {
			gnutls_assert();
			ret = _gnutls_asn2err(ret);
			goto cleanup;
		}

		ret = _gnutls_x509_write_uint32(c2, "?LAST", f->feature[i]);
		if (ret != GNUTLS_E_SUCCESS) {
			gnutls_assert();
			goto cleanup;
		}
	}

	ret = _gnutls_x509_der_encode(c2, "", ext, 0);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	ret = 0;

cleanup:
	asn1_delete_structure(&c2);
	return ret;
}

/**
 * gnutls_x509_tlsfeatures_add:
 * @f: The TLS features
 * @feature: The feature to add
 *
 * This function will append a feature to the X.509 TLS features
 * extension structure.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
 *   otherwise a negative error value.
 *
 * Since: 3.5.1
 **/
int gnutls_x509_tlsfeatures_add(gnutls_x509_tlsfeatures_t f,
				unsigned int feature)
{
	if (f == NULL) {
		gnutls_assert();
		return GNUTLS_E_INVALID_REQUEST;
	}

	if (feature > UINT16_MAX)
		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);

	if (f->size >= sizeof(f->feature) / sizeof(f->feature[0]))
		return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);

	f->feature[f->size++] = feature;

	return 0;
}

#define SCT_V1_LOGID_SIZE 32
struct ct_sct_st {
	int version;
	uint8_t logid[SCT_V1_LOGID_SIZE];
	uint64_t timestamp;
	gnutls_sign_algorithm_t sigalg;
	gnutls_datum_t signature;
};

struct gnutls_x509_ct_scts_st {
	struct ct_sct_st *scts;
	size_t size;
};

static void _gnutls_free_scts(struct gnutls_x509_ct_scts_st *scts)
{
	for (size_t i = 0; i < scts->size; i++)
		_gnutls_free_datum(&scts->scts[i].signature);
	gnutls_free(scts->scts);
	scts->size = 0;
}

/**
 * gnutls_x509_ext_ct_scts_init:
 * @scts: The SCT list
 *
 * This function will initialize a Certificate Transparency SCT list.
 *
 * Returns: %GNUTLS_E_SUCCESS (0) on success, otherwise a negative error value.
 **/
int gnutls_x509_ext_ct_scts_init(gnutls_x509_ct_scts_t *scts)
{
	*scts = gnutls_calloc(1, sizeof(struct gnutls_x509_ct_scts_st));
	if (*scts == NULL)
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
	return 0;
}

/**
 * gnutls_x509_ext_ct_scts_deinit:
 * @scts: The SCT list
 *
 * This function will deinitialize a Certificate Transparency SCT list.
 **/
void gnutls_x509_ext_ct_scts_deinit(gnutls_x509_ct_scts_t scts)
{
	_gnutls_free_scts(scts);
	gnutls_free(scts);
}

struct sct_sign_algorithm_st {
	uint8_t codepoint[2];
	gnutls_sign_algorithm_t sign_algo;
};

static const struct sct_sign_algorithm_st algos[] = {
	{ .codepoint = { 0x01, 0x01 }, .sign_algo = GNUTLS_SIGN_RSA_MD5 },
	{ .codepoint = { 0x02, 0x01 }, .sign_algo = GNUTLS_SIGN_RSA_SHA1 },
	{ .codepoint = { 0x03, 0x01 }, .sign_algo = GNUTLS_SIGN_RSA_SHA224 },
	{ .codepoint = { 0x04, 0x01 }, .sign_algo = GNUTLS_SIGN_RSA_SHA256 },
	{ .codepoint = { 0x05, 0x01 }, .sign_algo = GNUTLS_SIGN_RSA_SHA384 },
	{
		.codepoint = { 0x06, 0x01 },
		.sign_algo = GNUTLS_SIGN_RSA_SHA512,
	},
	{ .codepoint = { 0x02, 0x02 }, .sign_algo = GNUTLS_SIGN_DSA_SHA1 },
	{ .codepoint = { 0x03, 0x02 }, .sign_algo = GNUTLS_SIGN_DSA_SHA224 },
	{ .codepoint = { 0x04, 0x02 }, .sign_algo = GNUTLS_SIGN_DSA_SHA256 },
	{ .codepoint = { 0x05, 0x02 }, .sign_algo = GNUTLS_SIGN_DSA_SHA384 },
	{
		.codepoint = { 0x06, 0x02 },
		.sign_algo = GNUTLS_SIGN_DSA_SHA512,
	},
	{ .codepoint = { 0x02, 0x03 }, .sign_algo = GNUTLS_SIGN_ECDSA_SHA1 },
	{ .codepoint = { 0x03, 0x03 }, .sign_algo = GNUTLS_SIGN_ECDSA_SHA224 },
	{ .codepoint = { 0x04, 0x03 }, .sign_algo = GNUTLS_SIGN_ECDSA_SHA256 },
	{ .codepoint = { 0x05, 0x03 }, .sign_algo = GNUTLS_SIGN_ECDSA_SHA384 },
	{
		.codepoint = { 0x06, 0x03 },
		.sign_algo = GNUTLS_SIGN_ECDSA_SHA512,
	}
};

static gnutls_sign_algorithm_t get_sigalg(uint8_t hash_algo, uint8_t sig_algo)
{
	const struct sct_sign_algorithm_st *algo;
	size_t i, num_algos = sizeof(algos) / sizeof(algos[0]);

	if (hash_algo == 0 || sig_algo == 0)
		return GNUTLS_SIGN_UNKNOWN;

	for (i = 0; i < num_algos; i++) {
		algo = &algos[i];
		if (algo->codepoint[0] == hash_algo &&
		    algo->codepoint[1] == sig_algo)
			break;
	}

	if (i == num_algos)
		return GNUTLS_SIGN_UNKNOWN;

	return algo->sign_algo;
}

static int write_sigalg(gnutls_sign_algorithm_t sigalg, uint8_t out[])
{
	const struct sct_sign_algorithm_st *algo;
	size_t i, num_algos = sizeof(algos) / sizeof(algos[0]);

	for (i = 0; i < num_algos; i++) {
		algo = &algos[i];
		if (algo->sign_algo == sigalg)
			break;
	}

	if (i == num_algos)
		return GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM;

	out[0] = algo->codepoint[0];
	out[1] = algo->codepoint[1];
	return 0;
}

static int _gnutls_parse_ct_sct(uint8_t *ptr, uint16_t length,
				struct ct_sct_st *sct)
{
	uint16_t sig_length;
	uint8_t hash_algo, sig_algo;

	sct->signature.size = 0;
	sct->signature.data = NULL;

	DECR_LENGTH_RET(length, 1, GNUTLS_E_PREMATURE_TERMINATION);
	sct->version = (int)*ptr;
	ptr++;

	/* LogID
	 * In version 1, it has a fixed length of 32 bytes.
	 */
	DECR_LENGTH_RET(length, SCT_V1_LOGID_SIZE,
			GNUTLS_E_PREMATURE_TERMINATION);
	memcpy(sct->logid, ptr, SCT_V1_LOGID_SIZE);
	ptr += SCT_V1_LOGID_SIZE;

	/* Timestamp */
	DECR_LENGTH_RET(length, sizeof(uint64_t),
			GNUTLS_E_PREMATURE_TERMINATION);
	sct->timestamp = (uint64_t)_gnutls_read_uint64(ptr);
	ptr += sizeof(uint64_t);

	/*
	 * There are no extensions defined in SCT v1.
	 * Check that there are actually no extensions - the following two bytes should be zero.
	 */
	DECR_LENGTH_RET(length, 2, GNUTLS_E_PREMATURE_TERMINATION);
	if (*ptr != 0 || *(ptr + 1) != 0)
		return gnutls_assert_val(GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH);
	ptr += 2;

	/*
	 * Hash and signature algorithms, modeled after
	 * SignatureAndHashAlgorithm structure, as defined in
	 * RFC 5246, section 7.4.1.4.1.
	 * We take both values separately (hash and signature),
	 * and return them as a gnutls_sign_algorithm_t enum value.
	 */
	DECR_LENGTH_RET(length, 2, GNUTLS_E_PREMATURE_TERMINATION);
	hash_algo = *ptr++;
	sig_algo = *ptr++;

	sct->sigalg = get_sigalg(hash_algo, sig_algo);
	if (sct->sigalg == GNUTLS_SIGN_UNKNOWN)
		return gnutls_assert_val(
			GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM);

	/* Signature, length and content */
	DECR_LENGTH_RET(length, sizeof(uint16_t),
			GNUTLS_E_PREMATURE_TERMINATION);
	sig_length = _gnutls_read_uint16(ptr);
	ptr += sizeof(uint16_t);
	if (sig_length == 0)
		return gnutls_assert_val(GNUTLS_E_PREMATURE_TERMINATION);

	/* Remaining length should be sig_length at this point.
	 * If not, that means there is more data than what the length field said it was,
	 * and hence we must treat this as an error. */
	if (length != sig_length)
		return gnutls_assert_val(GNUTLS_E_ASN1_DER_OVERFLOW);

	if (_gnutls_set_datum(&sct->signature, ptr, sig_length) < 0)
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);

	return 0;
}

static int _gnutls_ct_sct_add(struct ct_sct_st *sct, struct ct_sct_st **scts,
			      size_t *size)
{
	struct ct_sct_st *new_scts;

	new_scts = _gnutls_reallocarray(*scts, *size + 1,
					sizeof(struct ct_sct_st));
	if (new_scts == NULL)
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);

	memcpy(&new_scts[*size], sct, sizeof(struct ct_sct_st));
	(*size)++;
	*scts = new_scts;

	return 0;
}

static int _gnutls_export_ct_v1_sct(gnutls_buffer_st *buf,
				    const struct ct_sct_st *sct)
{
	int ret;
	uint8_t tstamp_out[8], sigalg[2];
	/* There are no extensions defined for v1 */
	const uint8_t extensions[2] = { 0x00, 0x00 };
	size_t length_offset;

	/* Length field; filled later */
	length_offset = buf->length;
	if ((ret = _gnutls_buffer_append_prefix(buf, 16, 0)) < 0)
		return gnutls_assert_val(ret);

	/* Version */
	if ((ret = _gnutls_buffer_append_data(buf, &sct->version,
					      sizeof(uint8_t))) < 0)
		return gnutls_assert_val(ret);

	/* Log ID - has a fixed 32-byte size in version 1 */
	if ((ret = _gnutls_buffer_append_data(buf, sct->logid,
					      SCT_V1_LOGID_SIZE)) < 0)
		return gnutls_assert_val(ret);

	/* Timestamp */
	_gnutls_write_uint64(sct->timestamp, tstamp_out);
	if ((ret = _gnutls_buffer_append_data(buf, tstamp_out,
					      sizeof(tstamp_out))) < 0)
		return gnutls_assert_val(ret);

	/* Extensions */
	if ((ret = _gnutls_buffer_append_data(buf, extensions,
					      sizeof(extensions))) < 0)
		return gnutls_assert_val(ret);

	/* Hash and signature algorithms */
	if ((ret = write_sigalg(sct->sigalg, sigalg)) < 0)
		return gnutls_assert_val(ret);

	if ((ret = _gnutls_buffer_append_data(buf, sigalg, sizeof(sigalg))) < 0)
		return gnutls_assert_val(ret);

	/* Signature */
	if ((ret = _gnutls_buffer_append_data_prefix(
		     buf, 16, sct->signature.data, sct->signature.size)) < 0)
		return gnutls_assert_val(ret);

	/* Fill the length */
	_gnutls_write_uint16(buf->length - length_offset - 2,
			     buf->data + length_offset);

	return 0;
}

/**
 * gnutls_x509_ext_ct_import_scts:
 * @ext: a DER-encoded extension
 * @scts: The SCT list
 * @flags: should be zero
 *
 * This function will read a SignedCertificateTimestampList structure
 * from the DER data of the X.509 Certificate Transparency SCT extension
 * (OID 1.3.6.1.4.1.11129.2.4.2).
 *
 * The list of SCTs (Signed Certificate Timestamps) is placed on @scts,
 * which must be previously initialized with gnutls_x509_ext_ct_scts_init().
 *
 * Returns: %GNUTLS_E_SUCCESS (0) on success or a negative error value.
 **/
int gnutls_x509_ext_ct_import_scts(const gnutls_datum_t *ext,
				   gnutls_x509_ct_scts_t scts,
				   unsigned int flags)
{
	int retval;
	uint8_t *ptr;
	uint16_t length, sct_length;
	struct ct_sct_st sct;
	gnutls_datum_t scts_content;

	if (flags != 0)
		return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);

	retval = _gnutls_x509_decode_string(ASN1_ETYPE_OCTET_STRING, ext->data,
					    ext->size, &scts_content, 0);
	if (retval < 0)
		return gnutls_assert_val(retval);

	if (scts_content.size < 2) {
		gnutls_free(scts_content.data);
		return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
	}

	length = _gnutls_read_uint16(scts_content.data);
	if (length < 4 || length > scts_content.size) {
		gnutls_free(scts_content.data);
		return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
	}

	ptr = &scts_content.data[2];
	while (length > 0) {
		if (length < 2)
			break;

		sct_length = _gnutls_read_uint16(ptr);
		if (sct_length == 0 || sct_length > length)
			break;

		ptr += sizeof(uint16_t);
		length -= sizeof(uint16_t);

		/*
		 * _gnutls_parse_ct_sct() will try to read exactly sct_length bytes,
		 * returning an error if it can't
		 */
		if (_gnutls_parse_ct_sct(ptr, sct_length, &sct) < 0)
			break;
		if (_gnutls_ct_sct_add(&sct, &scts->scts, &scts->size) < 0)
			break;

		ptr += sct_length;
		length -= sct_length;
	}

	_gnutls_free_datum(&scts_content);

	if (length > 0) {
		gnutls_assert();
		_gnutls_free_scts(scts);
		return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
	}

	return GNUTLS_E_SUCCESS;
}

/**
 * gnutls_x509_ext_ct_export_scts:
 * @scts: An initialized SCT list
 * @ext: The DER-encoded extension data; must be freed with gnutls_free()
 *
 * This function will convert the provided list of SCTs to a DER-encoded
 * SignedCertificateTimestampList extension (1.3.6.1.4.1.11129.2.4.2).
 * The output data in @ext will be allocated using gnutls_malloc().
 *
 * Returns: %GNUTLS_E_SUCCESS (0) on success or a negative error value.
 **/
int gnutls_x509_ext_ct_export_scts(const gnutls_x509_ct_scts_t scts,
				   gnutls_datum_t *ext)
{
	int ret;
	gnutls_buffer_st buf;

	_gnutls_buffer_init(&buf);

	/* Start with the length of the whole string; the actual
	 * length is filled later */
	_gnutls_buffer_append_prefix(&buf, 16, 0);

	for (size_t i = 0; i < scts->size; i++) {
		if ((ret = _gnutls_export_ct_v1_sct(&buf, &scts->scts[i])) <
		    0) {
			gnutls_assert();
			goto cleanup;
		}
	}

	/* Fill the length */
	_gnutls_write_uint16(buf.length - 2, buf.data);

	/* DER-encode the whole thing as an opaque OCTET STRING, as the spec mandates */
	ret = _gnutls_x509_encode_string(ASN1_ETYPE_OCTET_STRING, buf.data,
					 buf.length, ext);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	ret = GNUTLS_E_SUCCESS;

cleanup:
	_gnutls_buffer_clear(&buf);
	return ret;
}

/**
 * gnutls_x509_ct_sct_get_version:
 * @scts: A list of SCTs
 * @idx: The index of the target SCT in the list
 * @version_out: The version of the target SCT.
 *
 * This function obtains the version of the SCT at the given position
 * in the SCT list.
 *
 * The version of that SCT will be placed on @version_out.
 *
 * Return : %GNUTLS_E_SUCCESS (0) is returned on success,
 *   %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if @idx exceeds the number of SCTs in the list
 *   and %GNUTLS_E_INVALID_REQUEST if the SCT's version is different than 1, as that's currently
 *   the only defined version.
 **/
int gnutls_x509_ct_sct_get_version(gnutls_x509_ct_scts_t scts, unsigned idx,
				   unsigned int *version_out)
{
	int version;

	if (idx >= scts->size)
		return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;

	/*
	 * Currently, only version 1 SCTs are defined (RFC 6962).
	 * A version 1 SCT has actually the value 0 in the 'version' field.
	 */
	version = scts->scts[idx].version;
	if (version != 0 || version_out == NULL)
		return GNUTLS_E_INVALID_REQUEST;

	*version_out = 1;
	return GNUTLS_E_SUCCESS;
}

/**
 * gnutls_x509_ct_sct_get:
 * @scts: A list of SCTs
 * @idx: The index of the target SCT in the list
 * @timestamp: The timestamp of the SCT
 * @logid: The LogID field of the SCT; must be freed with gnutls_free()
 * @sigalg: The signature algorithm
 * @signature: The signature of the SCT; must be freed with gnutls_free()
 *
 * This function will return a specific SCT (Signed Certificate Timestamp)
 * stored in the SCT list @scts.
 *
 * The datums holding the SCT's LogId and signature will be allocated
 * using gnutls_malloc().
 *
 * Returns: %GNUTLS_E_SUCCESS (0) will be returned on success,
 * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if @idx exceeds the number of SCTs in the list
 * or a negative error value.
 **/
int gnutls_x509_ct_sct_get(const gnutls_x509_ct_scts_t scts, unsigned idx,
			   time_t *timestamp, gnutls_datum_t *logid,
			   gnutls_sign_algorithm_t *sigalg,
			   gnutls_datum_t *signature)
{
	int retval = 0;
	struct ct_sct_st *sct;

	if (idx >= scts->size)
		return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;

	sct = &scts->scts[idx];
	if (sct->version != 0)
		return GNUTLS_E_INVALID_REQUEST;

	if (signature) {
		retval = _gnutls_set_datum(signature, sct->signature.data,
					   sct->signature.size);
		if (retval < 0)
			return retval;
	}

	if (logid) {
		retval =
			_gnutls_set_datum(logid, sct->logid, SCT_V1_LOGID_SIZE);
		if (retval < 0) {
			_gnutls_free_datum(signature);
			return retval;
		}
	}

	if (timestamp)
		*timestamp = sct->timestamp / 1000;

	if (sigalg)
		*sigalg = sct->sigalg;

	return GNUTLS_E_SUCCESS;
}
