summaryrefslogtreecommitdiffstats
path: root/lib/x509/x509_ext.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/x509/x509_ext.c4050
1 files changed, 4050 insertions, 0 deletions
diff --git a/lib/x509/x509_ext.c b/lib/x509/x509_ext.c
new file mode 100644
index 0000000..0b4c6a0
--- /dev/null
+++ b/lib/x509/x509_ext.c
@@ -0,0 +1,4050 @@
+/*
+ * 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
+struct gnutls_subject_alt_names_st {
+ struct name_st *names;
+ unsigned int size;
+};
+
+/**
+ * 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 &&
+ (nc->permitted != NULL || nc->excluded != NULL)) {
+ ret = gnutls_x509_name_constraints_init (&nc2);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_extract_name_constraints(c2, "permittedSubtrees",
+ &nc2->permitted);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_extract_name_constraints(c2, "excludedSubtrees",
+ &nc2->excluded);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_x509_name_constraints_merge(nc, nc2);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ } else {
+ _gnutls_name_constraints_node_free(nc->permitted);
+ _gnutls_name_constraints_node_free(nc->excluded);
+
+ ret =
+ _gnutls_extract_name_constraints(c2, "permittedSubtrees",
+ &nc->permitted);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_extract_name_constraints(c2, "excludedSubtrees",
+ &nc->excluded);
+ 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;
+ struct name_constraints_node_st *tmp;
+
+ if (nc->permitted == NULL && nc->excluded == NULL)
+ 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);
+ }
+
+ if (nc->permitted == NULL) {
+ (void)asn1_write_value(c2, "permittedSubtrees", NULL, 0);
+ } else {
+ tmp = nc->permitted;
+ do {
+ 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",
+ tmp->type,
+ tmp->name.data,
+ tmp->name.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ tmp = tmp->next;
+ } while (tmp != NULL);
+ }
+
+ if (nc->excluded == NULL) {
+ (void)asn1_write_value(c2, "excludedSubtrees", NULL, 0);
+ } else {
+ tmp = nc->excluded;
+ do {
+ 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",
+ tmp->type,
+ tmp->name.data,
+ tmp->name.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ tmp = tmp->next;
+ } while (tmp != NULL);
+
+ }
+
+ 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) {
+ 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;
+}