summaryrefslogtreecommitdiffstats
path: root/lib/x509/pkcs12.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/x509/pkcs12.c')
-rw-r--r--lib/x509/pkcs12.c2063
1 files changed, 2063 insertions, 0 deletions
diff --git a/lib/x509/pkcs12.c b/lib/x509/pkcs12.c
new file mode 100644
index 0000000..11b9da3
--- /dev/null
+++ b/lib/x509/pkcs12.c
@@ -0,0 +1,2063 @@
+/*
+ * Copyright (C) 2003-2012 Free Software Foundation, Inc.
+ * Copyright (C) 2012 Nikos Mavrogiannopoulos
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * 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/>
+ *
+ */
+
+/* Functions that relate on PKCS12 packet parsing.
+ */
+
+#include "gnutls_int.h"
+#include <libtasn1.h>
+
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <num.h>
+#include <common.h>
+#include <x509_b64.h>
+#include "x509_int.h"
+#include "pkcs7_int.h"
+#include <random.h>
+#include "intprops.h"
+
+
+/* Decodes the PKCS #12 auth_safe, and returns the allocated raw data,
+ * which holds them. Returns an asn1_node of authenticatedSafe.
+ */
+static int
+_decode_pkcs12_auth_safe(asn1_node pkcs12, asn1_node * authen_safe,
+ gnutls_datum_t * raw)
+{
+ char oid[MAX_OID_SIZE];
+ asn1_node c2 = NULL;
+ gnutls_datum_t auth_safe = { NULL, 0 };
+ int len, result;
+ char error_str[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
+
+ len = sizeof(oid) - 1;
+ result =
+ asn1_read_value(pkcs12, "authSafe.contentType", oid, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (strcmp(oid, DATA_OID) != 0) {
+ gnutls_assert();
+ _gnutls_debug_log("Unknown PKCS12 Content OID '%s'\n",
+ oid);
+ return GNUTLS_E_UNKNOWN_PKCS_CONTENT_TYPE;
+ }
+
+ /* Step 1. Read the content data
+ */
+
+ result =
+ _gnutls_x509_read_string(pkcs12, "authSafe.content",
+ &auth_safe, ASN1_ETYPE_OCTET_STRING, 1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Step 2. Extract the authenticatedSafe.
+ */
+
+ if ((result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.pkcs-12-AuthenticatedSafe",
+ &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_der_decoding(&c2, auth_safe.data, auth_safe.size,
+ error_str);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ _gnutls_debug_log("DER error: %s\n", error_str);
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (raw == NULL) {
+ _gnutls_free_datum(&auth_safe);
+ } else {
+ raw->data = auth_safe.data;
+ raw->size = auth_safe.size;
+ }
+
+ if (authen_safe)
+ *authen_safe = c2;
+ else
+ asn1_delete_structure(&c2);
+
+ return 0;
+
+ cleanup:
+ if (c2)
+ asn1_delete_structure(&c2);
+ _gnutls_free_datum(&auth_safe);
+ return result;
+}
+
+static int pkcs12_reinit(gnutls_pkcs12_t pkcs12)
+{
+int result;
+
+ if (pkcs12->pkcs12)
+ asn1_delete_structure(&pkcs12->pkcs12);
+
+ result = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-12-PFX",
+ &pkcs12->pkcs12);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_pkcs12_init:
+ * @pkcs12: A pointer to the type to be initialized
+ *
+ * This function will initialize a PKCS12 type. PKCS12 structures
+ * usually contain lists of X.509 Certificates and X.509 Certificate
+ * revocation lists.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_pkcs12_init(gnutls_pkcs12_t * pkcs12)
+{
+ *pkcs12 = gnutls_calloc(1, sizeof(gnutls_pkcs12_int));
+
+ if (*pkcs12) {
+ int result = pkcs12_reinit(*pkcs12);
+ if (result < 0) {
+ gnutls_assert();
+ gnutls_free(*pkcs12);
+ return result;
+ }
+ return 0; /* success */
+ }
+ return GNUTLS_E_MEMORY_ERROR;
+}
+
+/**
+ * gnutls_pkcs12_deinit:
+ * @pkcs12: The type to be initialized
+ *
+ * This function will deinitialize a PKCS12 type.
+ **/
+void gnutls_pkcs12_deinit(gnutls_pkcs12_t pkcs12)
+{
+ if (!pkcs12)
+ return;
+
+ if (pkcs12->pkcs12)
+ asn1_delete_structure(&pkcs12->pkcs12);
+
+ gnutls_free(pkcs12);
+}
+
+/**
+ * gnutls_pkcs12_import:
+ * @pkcs12: The data to store the parsed PKCS12.
+ * @data: The DER or PEM encoded PKCS12.
+ * @format: One of DER or PEM
+ * @flags: an ORed sequence of gnutls_privkey_pkcs8_flags
+ *
+ * This function will convert the given DER or PEM encoded PKCS12
+ * to the native gnutls_pkcs12_t format. The output will be stored in 'pkcs12'.
+ *
+ * If the PKCS12 is PEM encoded it should have a header of "PKCS12".
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_pkcs12_import(gnutls_pkcs12_t pkcs12,
+ const gnutls_datum_t * data,
+ gnutls_x509_crt_fmt_t format, unsigned int flags)
+{
+ int result = 0, need_free = 0;
+ gnutls_datum_t _data;
+ char error_str[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
+
+ _data.data = data->data;
+ _data.size = data->size;
+
+ if (pkcs12 == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* If the PKCS12 is in PEM format then decode it
+ */
+ if (format == GNUTLS_X509_FMT_PEM) {
+ result =
+ _gnutls_fbase64_decode(PEM_PKCS12, data->data,
+ data->size, &_data);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ need_free = 1;
+ }
+
+ if (pkcs12->expanded) {
+ result = pkcs12_reinit(pkcs12);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+ pkcs12->expanded = 1;
+
+ result =
+ asn1_der_decoding(&pkcs12->pkcs12, _data.data, _data.size,
+ error_str);
+ if (result != ASN1_SUCCESS) {
+ result = _gnutls_asn2err(result);
+ _gnutls_debug_log("DER error: %s\n", error_str);
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (need_free)
+ _gnutls_free_datum(&_data);
+
+ return 0;
+
+ cleanup:
+ if (need_free)
+ _gnutls_free_datum(&_data);
+ return result;
+}
+
+
+/**
+ * gnutls_pkcs12_export:
+ * @pkcs12: A pkcs12 type
+ * @format: the format of output params. One of PEM or DER.
+ * @output_data: will contain a structure PEM or DER encoded
+ * @output_data_size: holds the size of output_data (and will be
+ * replaced by the actual size of parameters)
+ *
+ * This function will export the pkcs12 structure to DER or PEM format.
+ *
+ * If the buffer provided is not long enough to hold the output, then
+ * *output_data_size will be updated and GNUTLS_E_SHORT_MEMORY_BUFFER
+ * will be returned.
+ *
+ * If the structure is PEM encoded, it will have a header
+ * of "BEGIN PKCS12".
+ *
+ * Returns: In case of failure a negative error code will be
+ * returned, and 0 on success.
+ **/
+int
+gnutls_pkcs12_export(gnutls_pkcs12_t pkcs12,
+ gnutls_x509_crt_fmt_t format, void *output_data,
+ size_t * output_data_size)
+{
+ int ret;
+
+ if (pkcs12 == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_x509_export_int(pkcs12->pkcs12, format, PEM_PKCS12,
+ output_data, output_data_size);
+
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ } else {
+ /* PKCS#12 export is always non-approved, because the MAC
+ * calculation involves non-approved KDF (PKCS#12 KDF) and
+ * without MAC the protection is insufficient.
+ */
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+ }
+ return ret;
+}
+
+/**
+ * gnutls_pkcs12_export2:
+ * @pkcs12: A pkcs12 type
+ * @format: the format of output params. One of PEM or DER.
+ * @out: will contain a structure PEM or DER encoded
+ *
+ * This function will export the pkcs12 structure to DER or PEM format.
+ *
+ * The output buffer is allocated using gnutls_malloc().
+ *
+ * If the structure is PEM encoded, it will have a header
+ * of "BEGIN PKCS12".
+ *
+ * Returns: In case of failure a negative error code will be
+ * returned, and 0 on success.
+ *
+ * Since: 3.1.3
+ **/
+int
+gnutls_pkcs12_export2(gnutls_pkcs12_t pkcs12,
+ gnutls_x509_crt_fmt_t format, gnutls_datum_t * out)
+{
+ int ret;
+
+ if (pkcs12 == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_x509_export_int2(pkcs12->pkcs12, format, PEM_PKCS12,
+ out);
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ } else {
+ /* PKCS#12 export is always non-approved, because the MAC
+ * calculation involves non-approved KDF (PKCS#12 KDF) and
+ * without MAC the protection is insufficient.
+ */
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+ }
+ return ret;
+}
+
+static int oid2bag(const char *oid)
+{
+ if (strcmp(oid, BAG_PKCS8_KEY) == 0)
+ return GNUTLS_BAG_PKCS8_KEY;
+ if (strcmp(oid, BAG_PKCS8_ENCRYPTED_KEY) == 0)
+ return GNUTLS_BAG_PKCS8_ENCRYPTED_KEY;
+ if (strcmp(oid, BAG_CERTIFICATE) == 0)
+ return GNUTLS_BAG_CERTIFICATE;
+ if (strcmp(oid, BAG_CRL) == 0)
+ return GNUTLS_BAG_CRL;
+ if (strcmp(oid, BAG_SECRET) == 0)
+ return GNUTLS_BAG_SECRET;
+
+ return GNUTLS_BAG_UNKNOWN;
+}
+
+static const char *bag_to_oid(int bag)
+{
+ switch (bag) {
+ case GNUTLS_BAG_PKCS8_KEY:
+ return BAG_PKCS8_KEY;
+ case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
+ return BAG_PKCS8_ENCRYPTED_KEY;
+ case GNUTLS_BAG_CERTIFICATE:
+ return BAG_CERTIFICATE;
+ case GNUTLS_BAG_CRL:
+ return BAG_CRL;
+ case GNUTLS_BAG_SECRET:
+ return BAG_SECRET;
+ }
+ return NULL;
+}
+
+/* Decodes the SafeContents, and puts the output in
+ * the given bag.
+ */
+int
+_pkcs12_decode_safe_contents(const gnutls_datum_t * content,
+ gnutls_pkcs12_bag_t bag)
+{
+ char oid[MAX_OID_SIZE], root[MAX_NAME_SIZE];
+ asn1_node c2 = NULL;
+ int len, result;
+ int bag_type;
+ gnutls_datum_t attr_val;
+ gnutls_datum_t t;
+ int count = 0, attributes, j;
+ unsigned i;
+
+ /* Step 1. Extract the SEQUENCE.
+ */
+
+ if ((result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.pkcs-12-SafeContents",
+ &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_der_decoding(&c2, content->data, content->size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Count the number of bags
+ */
+ result = asn1_number_of_elements(c2, "", &count);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ bag->bag_elements = MIN(MAX_BAG_ELEMENTS, count);
+
+ for (i = 0; i < bag->bag_elements; i++) {
+
+ snprintf(root, sizeof(root), "?%u.bagId", i + 1);
+
+ len = sizeof(oid);
+ result = asn1_read_value(c2, root, oid, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Read the Bag type
+ */
+ bag_type = oid2bag(oid);
+
+ if (bag_type < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Read the Bag Value
+ */
+
+ snprintf(root, sizeof(root), "?%u.bagValue", i + 1);
+
+ result =
+ _gnutls_x509_read_value(c2, root,
+ &bag->element[i].data);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (bag_type == GNUTLS_BAG_CERTIFICATE
+ || bag_type == GNUTLS_BAG_CRL
+ || bag_type == GNUTLS_BAG_SECRET) {
+ gnutls_datum_t tmp = bag->element[i].data;
+ bag->element[i].data.data = NULL;
+ bag->element[i].data.size = 0;
+
+ result =
+ _pkcs12_decode_crt_bag(bag_type, &tmp,
+ &bag->element[i].data);
+ _gnutls_free_datum(&tmp);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ /* read the bag attributes
+ */
+ snprintf(root, sizeof(root), "?%u.bagAttributes", i + 1);
+
+ result = asn1_number_of_elements(c2, root, &attributes);
+ if (result != ASN1_SUCCESS
+ && result != ASN1_ELEMENT_NOT_FOUND) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (attributes < 0)
+ attributes = 1;
+
+ if (result != ASN1_ELEMENT_NOT_FOUND)
+ for (j = 0; j < attributes; j++) {
+
+ snprintf(root, sizeof(root),
+ "?%u.bagAttributes.?%d", i + 1,
+ j + 1);
+
+ result =
+ _gnutls_x509_decode_and_read_attribute
+ (c2, root, oid, sizeof(oid), &attr_val,
+ 1, 0);
+
+ if (result < 0) {
+ gnutls_assert();
+ continue; /* continue in case we find some known attributes */
+ }
+
+ if (strcmp(oid, KEY_ID_OID) == 0) {
+ result =
+ _gnutls_x509_decode_string
+ (ASN1_ETYPE_OCTET_STRING,
+ attr_val.data, attr_val.size,
+ &t, 1);
+
+ _gnutls_free_datum(&attr_val);
+ if (result < 0) {
+ gnutls_assert();
+ _gnutls_debug_log
+ ("Error decoding PKCS12 Bag Attribute OID '%s'\n",
+ oid);
+ continue;
+ }
+
+ _gnutls_free_datum(&bag->element[i].local_key_id);
+ bag->element[i].local_key_id.data = t.data;
+ bag->element[i].local_key_id.size = t.size;
+ } else if (strcmp(oid, FRIENDLY_NAME_OID) == 0 && bag->element[i].friendly_name == NULL) {
+ result =
+ _gnutls_x509_decode_string
+ (ASN1_ETYPE_BMP_STRING,
+ attr_val.data, attr_val.size,
+ &t, 1);
+
+ _gnutls_free_datum(&attr_val);
+ if (result < 0) {
+ gnutls_assert();
+ _gnutls_debug_log
+ ("Error decoding PKCS12 Bag Attribute OID '%s'\n",
+ oid);
+ continue;
+ }
+
+ gnutls_free(bag->element[i].friendly_name);
+ bag->element[i].friendly_name = (char *) t.data;
+ } else {
+ _gnutls_free_datum(&attr_val);
+ _gnutls_debug_log
+ ("Unknown PKCS12 Bag Attribute OID '%s'\n",
+ oid);
+ }
+ }
+
+
+ bag->element[i].type = bag_type;
+
+ }
+
+ result = 0;
+
+ cleanup:
+ if (c2)
+ asn1_delete_structure(&c2);
+ return result;
+
+}
+
+
+static int
+_parse_safe_contents(asn1_node sc, const char *sc_name,
+ gnutls_pkcs12_bag_t bag)
+{
+ gnutls_datum_t content = { NULL, 0 };
+ int result;
+
+ /* Step 1. Extract the content.
+ */
+
+ result =
+ _gnutls_x509_read_string(sc, sc_name, &content,
+ ASN1_ETYPE_OCTET_STRING, 1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _pkcs12_decode_safe_contents(&content, bag);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ _gnutls_free_datum(&content);
+
+ return 0;
+
+ cleanup:
+ _gnutls_free_datum(&content);
+ return result;
+}
+
+
+/**
+ * gnutls_pkcs12_get_bag:
+ * @pkcs12: A pkcs12 type
+ * @indx: contains the index of the bag to extract
+ * @bag: An initialized bag, where the contents of the bag will be copied
+ *
+ * This function will return a Bag from the PKCS12 structure.
+ *
+ * After the last Bag has been read
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_pkcs12_get_bag(gnutls_pkcs12_t pkcs12,
+ int indx, gnutls_pkcs12_bag_t bag)
+{
+ asn1_node c2 = NULL;
+ int result, len;
+ char root2[MAX_NAME_SIZE];
+ char oid[MAX_OID_SIZE];
+
+ if (pkcs12 == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Step 1. decode the data.
+ */
+ result = _decode_pkcs12_auth_safe(pkcs12->pkcs12, &c2, NULL);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ /* Step 2. Parse the AuthenticatedSafe
+ */
+
+ snprintf(root2, sizeof(root2), "?%d.contentType", indx + 1);
+
+ len = sizeof(oid) - 1;
+ result = asn1_read_value(c2, root2, oid, &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ goto cleanup;
+ }
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Not encrypted Bag
+ */
+
+ snprintf(root2, sizeof(root2), "?%d.content", indx + 1);
+
+ if (strcmp(oid, DATA_OID) == 0) {
+ result = _parse_safe_contents(c2, root2, bag);
+ goto cleanup;
+ }
+
+ /* ENC_DATA_OID needs decryption */
+
+ result = _gnutls_x509_read_value(c2, root2, &bag->element[0].data);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ bag->element[0].type = GNUTLS_BAG_ENCRYPTED;
+ bag->bag_elements = 1;
+
+ result = 0;
+
+ cleanup:
+ if (c2)
+ asn1_delete_structure(&c2);
+ return result;
+}
+
+/* Creates an empty PFX structure for the PKCS12 structure.
+ */
+static int create_empty_pfx(asn1_node pkcs12)
+{
+ uint8_t three = 3;
+ int result;
+ asn1_node c2 = NULL;
+
+ /* Use version 3
+ */
+ result = asn1_write_value(pkcs12, "version", &three, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Write the content type of the data
+ */
+ result =
+ asn1_write_value(pkcs12, "authSafe.contentType", DATA_OID, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Check if the authenticatedSafe content is empty, and encode a
+ * null one in that case.
+ */
+
+ if ((result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.pkcs-12-AuthenticatedSafe",
+ &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ _gnutls_x509_der_encode_and_copy(c2, "", pkcs12,
+ "authSafe.content", 1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ asn1_delete_structure(&c2);
+
+ return 0;
+
+ cleanup:
+ asn1_delete_structure(&c2);
+ return result;
+
+}
+
+/**
+ * gnutls_pkcs12_set_bag:
+ * @pkcs12: should contain a gnutls_pkcs12_t type
+ * @bag: An initialized bag
+ *
+ * This function will insert a Bag into the PKCS12 structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_pkcs12_set_bag(gnutls_pkcs12_t pkcs12, gnutls_pkcs12_bag_t bag)
+{
+ asn1_node c2 = NULL;
+ asn1_node safe_cont = NULL;
+ int result;
+ int enc = 0, dum = 1;
+ char null;
+
+ if (pkcs12 == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Step 1. Check if the pkcs12 structure is empty. In that
+ * case generate an empty PFX.
+ */
+ result =
+ asn1_read_value(pkcs12->pkcs12, "authSafe.content", &null,
+ &dum);
+ if (result == ASN1_VALUE_NOT_FOUND) {
+ result = create_empty_pfx(pkcs12->pkcs12);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+ }
+
+ /* Step 2. decode the authenticatedSafe.
+ */
+ result = _decode_pkcs12_auth_safe(pkcs12->pkcs12, &c2, NULL);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ /* Step 3. Encode the bag elements into a SafeContents
+ * structure.
+ */
+ result = _pkcs12_encode_safe_contents(bag, &safe_cont, &enc);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ /* Step 4. Insert the encoded SafeContents into the AuthenticatedSafe
+ * structure.
+ */
+ result = asn1_write_value(c2, "", "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (enc)
+ result =
+ asn1_write_value(c2, "?LAST.contentType", ENC_DATA_OID,
+ 1);
+ else
+ result =
+ asn1_write_value(c2, "?LAST.contentType", DATA_OID, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (enc) {
+ /* Encrypted packets are written directly.
+ */
+ result =
+ asn1_write_value(c2, "?LAST.content",
+ bag->element[0].data.data,
+ bag->element[0].data.size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+ } else {
+ result =
+ _gnutls_x509_der_encode_and_copy(safe_cont, "", c2,
+ "?LAST.content", 1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ asn1_delete_structure(&safe_cont);
+
+
+ /* Step 5. Re-encode and copy the AuthenticatedSafe into the pkcs12
+ * structure.
+ */
+ result =
+ _gnutls_x509_der_encode_and_copy(c2, "", pkcs12->pkcs12,
+ "authSafe.content", 1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ asn1_delete_structure(&c2);
+
+ return 0;
+
+ cleanup:
+ asn1_delete_structure(&c2);
+ asn1_delete_structure(&safe_cont);
+ return result;
+}
+
+#if ENABLE_GOST
+/*
+ * Russian differs from PKCS#12 here. It described proprietary way
+ * to obtain MAC key instead of using standard mechanism.
+ *
+ * See https://wwwold.tc26.ru/standard/rs/%D0%A0%2050.1.112-2016.pdf
+ * section 5.
+ */
+static int
+_gnutls_pkcs12_gost_string_to_key(gnutls_mac_algorithm_t algo,
+ const uint8_t * salt,
+ unsigned int salt_size, unsigned int iter,
+ const char *pass, unsigned int req_keylen,
+ uint8_t * keybuf)
+{
+ uint8_t temp[96];
+ size_t temp_len = sizeof(temp);
+ gnutls_datum_t key;
+ gnutls_datum_t _salt;
+ int ret;
+
+ if (iter == 0)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ key.data = (void *)pass;
+ key.size = pass ? strlen(pass) : 0;
+
+ _salt.data = (void *)salt;
+ _salt.size = salt_size;
+
+ ret = gnutls_pbkdf2(algo, &key, &_salt, iter, temp, temp_len);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ memcpy(keybuf, temp + temp_len - req_keylen, req_keylen);
+
+ return 0;
+}
+#endif
+
+/**
+ * gnutls_pkcs12_generate_mac2:
+ * @pkcs12: A pkcs12 type
+ * @mac: the MAC algorithm to use
+ * @pass: The password for the MAC
+ *
+ * This function will generate a MAC for the PKCS12 structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_pkcs12_generate_mac2(gnutls_pkcs12_t pkcs12, gnutls_mac_algorithm_t mac, const char *pass)
+{
+ uint8_t salt[8], key[MAX_HASH_SIZE];
+ int result;
+ const int iter = PKCS12_ITER_COUNT;
+ mac_hd_st td1;
+ gnutls_datum_t tmp = { NULL, 0 };
+ unsigned mac_size, key_len;
+ uint8_t mac_out[MAX_HASH_SIZE];
+ const mac_entry_st *me = mac_to_entry(mac);
+
+ if (pkcs12 == NULL || me == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (me->oid == NULL)
+ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+
+ mac_size = _gnutls_mac_get_algo_len(me);
+ key_len = mac_size;
+
+ /* Generate the salt.
+ */
+ result = gnutls_rnd(GNUTLS_RND_NONCE, salt, sizeof(salt));
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ /* Write the salt into the structure.
+ */
+ result =
+ asn1_write_value(pkcs12->pkcs12, "macData.macSalt", salt,
+ sizeof(salt));
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* write the iterations
+ */
+
+ if (iter > 1) {
+ result =
+ _gnutls_x509_write_uint32(pkcs12->pkcs12,
+ "macData.iterations", iter);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ /* Generate the key.
+ */
+#if ENABLE_GOST
+ if (me->id == GNUTLS_MAC_GOSTR_94 ||
+ me->id == GNUTLS_MAC_STREEBOG_256 ||
+ me->id == GNUTLS_MAC_STREEBOG_512) {
+ key_len = 32;
+ result = _gnutls_pkcs12_gost_string_to_key(me->id,
+ salt,
+ sizeof(salt),
+ iter,
+ pass,
+ key_len,
+ key);
+ } else
+#endif
+ result = _gnutls_pkcs12_string_to_key(me, 3 /*MAC*/,
+ salt, sizeof(salt),
+ iter, pass,
+ mac_size, key);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Get the data to be MACed
+ */
+ result = _decode_pkcs12_auth_safe(pkcs12->pkcs12, NULL, &tmp);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* MAC the data
+ */
+ result = _gnutls_mac_init(&td1, me,
+ key, key_len);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ _gnutls_mac(&td1, tmp.data, tmp.size);
+ _gnutls_free_datum(&tmp);
+
+ _gnutls_mac_deinit(&td1, mac_out);
+
+
+ result =
+ asn1_write_value(pkcs12->pkcs12, "macData.mac.digest", mac_out,
+ mac_size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_write_value(pkcs12->pkcs12,
+ "macData.mac.digestAlgorithm.parameters",
+ NULL, 0);
+ if (result != ASN1_SUCCESS && result != ASN1_ELEMENT_NOT_FOUND) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_write_value(pkcs12->pkcs12,
+ "macData.mac.digestAlgorithm.algorithm",
+ me->oid, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* _gnutls_pkcs12_string_to_key is not a FIPS approved operation */
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+ return 0;
+
+ cleanup:
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ _gnutls_free_datum(&tmp);
+ return result;
+}
+
+/**
+ * gnutls_pkcs12_generate_mac:
+ * @pkcs12: A pkcs12 type
+ * @pass: The password for the MAC
+ *
+ * This function will generate a MAC for the PKCS12 structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_pkcs12_generate_mac(gnutls_pkcs12_t pkcs12, const char *pass)
+{
+ return gnutls_pkcs12_generate_mac2(pkcs12, GNUTLS_MAC_SHA256, pass);
+}
+
+/**
+ * gnutls_pkcs12_verify_mac:
+ * @pkcs12: should contain a gnutls_pkcs12_t type
+ * @pass: The password for the MAC
+ *
+ * This function will verify the MAC for the PKCS12 structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_pkcs12_verify_mac(gnutls_pkcs12_t pkcs12, const char *pass)
+{
+ uint8_t key[MAX_HASH_SIZE];
+ char oid[MAX_OID_SIZE];
+ int result;
+ unsigned int iter;
+ int len;
+ mac_hd_st td1;
+ gnutls_datum_t tmp = { NULL, 0 }, salt = {
+ NULL, 0};
+ uint8_t mac_output[MAX_HASH_SIZE];
+ uint8_t mac_output_orig[MAX_HASH_SIZE];
+ gnutls_mac_algorithm_t algo;
+ unsigned mac_len, key_len;
+ const mac_entry_st *entry;
+#if ENABLE_GOST
+ int gost_retry = 0;
+#endif
+
+ if (pkcs12 == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* read the iterations
+ */
+ result =
+ _gnutls_x509_read_uint(pkcs12->pkcs12, "macData.iterations",
+ &iter);
+ if (result < 0) {
+ iter = 1; /* the default */
+ }
+
+ len = sizeof(oid);
+ result =
+ asn1_read_value(pkcs12->pkcs12, "macData.mac.digestAlgorithm.algorithm",
+ oid, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ algo = DIG_TO_MAC(gnutls_oid_to_digest(oid));
+ if (algo == GNUTLS_MAC_UNKNOWN) {
+ unknown_mac:
+ gnutls_assert();
+ return GNUTLS_E_UNKNOWN_HASH_ALGORITHM;
+ }
+
+ entry = mac_to_entry(algo);
+ if (entry == NULL)
+ goto unknown_mac;
+
+ mac_len = _gnutls_mac_get_algo_len(entry);
+ key_len = mac_len;
+
+ /* Read the salt from the structure.
+ */
+ result =
+ _gnutls_x509_read_null_value(pkcs12->pkcs12, "macData.macSalt",
+ &salt);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Generate the key.
+ */
+ result = _gnutls_pkcs12_string_to_key(entry, 3 /*MAC*/,
+ salt.data, salt.size,
+ iter, pass,
+ key_len, key);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Get the data to be MACed
+ */
+ result = _decode_pkcs12_auth_safe(pkcs12->pkcs12, NULL, &tmp);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+#if ENABLE_GOST
+ /* GOST PKCS#12 files use either PKCS#12 scheme or proprietary
+ * HMAC-based scheme to generate MAC key. */
+pkcs12_try_gost:
+#endif
+
+ /* MAC the data
+ */
+ result = _gnutls_mac_init(&td1, entry, key, key_len);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ _gnutls_mac(&td1, tmp.data, tmp.size);
+
+ _gnutls_mac_deinit(&td1, mac_output);
+
+ len = sizeof(mac_output_orig);
+ result =
+ asn1_read_value(pkcs12->pkcs12, "macData.mac.digest",
+ mac_output_orig, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if ((unsigned)len != mac_len ||
+ memcmp(mac_output_orig, mac_output, len) != 0) {
+
+#if ENABLE_GOST
+ /* It is possible that GOST files use proprietary
+ * key generation scheme */
+ if (!gost_retry &&
+ (algo == GNUTLS_MAC_GOSTR_94 ||
+ algo == GNUTLS_MAC_STREEBOG_256 ||
+ algo == GNUTLS_MAC_STREEBOG_512)) {
+ gost_retry = 1;
+ key_len = 32;
+ result = _gnutls_pkcs12_gost_string_to_key(algo,
+ salt.data,
+ salt.size,
+ iter,
+ pass,
+ key_len,
+ key);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ goto pkcs12_try_gost;
+ }
+#endif
+
+ gnutls_assert();
+ result = GNUTLS_E_MAC_VERIFY_FAILED;
+ goto cleanup;
+ }
+
+ /* _gnutls_pkcs12_string_to_key is not a FIPS approved operation */
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+ result = 0;
+ cleanup:
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ _gnutls_free_datum(&tmp);
+ _gnutls_free_datum(&salt);
+ return result;
+}
+
+
+static int
+write_attributes(gnutls_pkcs12_bag_t bag, int elem,
+ asn1_node c2, const char *where)
+{
+ int result;
+ char root[128];
+
+ /* If the bag attributes are empty, then write
+ * nothing to the attribute field.
+ */
+ if (bag->element[elem].friendly_name == NULL &&
+ bag->element[elem].local_key_id.data == NULL) {
+ /* no attributes
+ */
+ result = asn1_write_value(c2, where, NULL, 0);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+ }
+
+ if (bag->element[elem].local_key_id.data != NULL) {
+
+ /* Add a new Attribute
+ */
+ result = asn1_write_value(c2, where, "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ _gnutls_str_cpy(root, sizeof(root), where);
+ _gnutls_str_cat(root, sizeof(root), ".?LAST");
+
+ result =
+ _gnutls_x509_encode_and_write_attribute(KEY_ID_OID, c2,
+ root,
+ bag->element
+ [elem].
+ local_key_id.data,
+ bag->element
+ [elem].
+ local_key_id.size,
+ 1);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+ }
+
+ if (bag->element[elem].friendly_name != NULL) {
+ uint8_t *name;
+ int size, i;
+ const char *p;
+
+ /* Add a new Attribute
+ */
+ result = asn1_write_value(c2, where, "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* convert name to BMPString
+ */
+ size = strlen(bag->element[elem].friendly_name) * 2;
+ name = gnutls_malloc(size);
+
+ if (name == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ p = bag->element[elem].friendly_name;
+ for (i = 0; i < size; i += 2) {
+ name[i] = 0;
+ name[i + 1] = *p;
+ p++;
+ }
+
+ _gnutls_str_cpy(root, sizeof(root), where);
+ _gnutls_str_cat(root, sizeof(root), ".?LAST");
+
+ result =
+ _gnutls_x509_encode_and_write_attribute
+ (FRIENDLY_NAME_OID, c2, root, name, size, 1);
+
+ gnutls_free(name);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+ }
+
+ return 0;
+}
+
+
+/* Encodes the bag into a SafeContents structure, and puts the output in
+ * the given datum. Enc is set to non-zero if the data are encrypted;
+ */
+int
+_pkcs12_encode_safe_contents(gnutls_pkcs12_bag_t bag, asn1_node * contents,
+ int *enc)
+{
+ asn1_node c2 = NULL;
+ int result;
+ unsigned i;
+ const char *oid;
+
+ if (bag->element[0].type == GNUTLS_BAG_ENCRYPTED && enc) {
+ *enc = 1;
+ return 0; /* ENCRYPTED BAG, do nothing. */
+ } else if (enc)
+ *enc = 0;
+
+ /* Step 1. Create the SEQUENCE.
+ */
+
+ if ((result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.pkcs-12-SafeContents",
+ &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ for (i = 0; i < bag->bag_elements; i++) {
+
+ oid = bag_to_oid(bag->element[i].type);
+ if (oid == NULL) {
+ gnutls_assert();
+ continue;
+ }
+
+ result = asn1_write_value(c2, "", "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Copy the bag type.
+ */
+ result = asn1_write_value(c2, "?LAST.bagId", oid, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Set empty attributes
+ */
+ result =
+ write_attributes(bag, i, c2, "?LAST.bagAttributes");
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+
+ /* Copy the Bag Value
+ */
+
+ if (bag->element[i].type == GNUTLS_BAG_CERTIFICATE ||
+ bag->element[i].type == GNUTLS_BAG_SECRET ||
+ bag->element[i].type == GNUTLS_BAG_CRL) {
+ gnutls_datum_t tmp;
+
+ /* in that case encode it to a CertBag or
+ * a CrlBag.
+ */
+
+ result =
+ _pkcs12_encode_crt_bag(bag->element[i].type,
+ &bag->element[i].data,
+ &tmp);
+
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result =
+ _gnutls_x509_write_value(c2, "?LAST.bagValue",
+ &tmp);
+
+ _gnutls_free_datum(&tmp);
+
+ } else {
+
+ result =
+ _gnutls_x509_write_value(c2, "?LAST.bagValue",
+ &bag->element[i].
+ data);
+ }
+
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ }
+
+ /* Encode the data and copy them into the datum
+ */
+ *contents = c2;
+
+ return 0;
+
+ cleanup:
+ if (c2)
+ asn1_delete_structure(&c2);
+ return result;
+
+}
+
+/* Checks if the extra_certs contain certificates that may form a chain
+ * with the first certificate in chain (it is expected that chain_len==1)
+ * and appends those in the chain.
+ */
+static int make_chain(gnutls_x509_crt_t ** chain, unsigned int *chain_len,
+ gnutls_x509_crt_t ** extra_certs,
+ unsigned int *extra_certs_len, unsigned int flags)
+{
+ unsigned int i;
+
+ if (*chain_len != 1)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ i = 0;
+ while (i < *extra_certs_len) {
+ /* if it is an issuer but not a self-signed one */
+ if (gnutls_x509_crt_check_issuer
+ ((*chain)[*chain_len - 1], (*extra_certs)[i]) != 0) {
+ if (!(flags & GNUTLS_PKCS12_SP_INCLUDE_SELF_SIGNED)
+ &&
+ gnutls_x509_crt_check_issuer((*extra_certs)[i],
+ (*extra_certs)[i])
+ != 0)
+ goto skip;
+
+ if (unlikely(INT_ADD_OVERFLOW(*chain_len, 1))) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ *chain = _gnutls_reallocarray_fast(*chain,
+ ++(*chain_len),
+ sizeof((*chain)[0]));
+ if (*chain == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+ (*chain)[*chain_len - 1] = (*extra_certs)[i];
+
+ (*extra_certs)[i] =
+ (*extra_certs)[*extra_certs_len - 1];
+ (*extra_certs_len)--;
+
+ i = 0;
+ continue;
+ }
+
+ skip:
+ i++;
+ }
+ return 0;
+}
+
+/**
+ * gnutls_pkcs12_simple_parse:
+ * @p12: A pkcs12 type
+ * @password: optional password used to decrypt the structure, bags and keys.
+ * @key: a structure to store the parsed private key.
+ * @chain: the corresponding to key certificate chain (may be %NULL)
+ * @chain_len: will be updated with the number of additional (may be %NULL)
+ * @extra_certs: optional pointer to receive an array of additional
+ * certificates found in the PKCS12 structure (may be %NULL).
+ * @extra_certs_len: will be updated with the number of additional
+ * certs (may be %NULL).
+ * @crl: an optional structure to store the parsed CRL (may be %NULL).
+ * @flags: should be zero or one of GNUTLS_PKCS12_SP_*
+ *
+ * This function parses a PKCS12 structure in @pkcs12 and extracts the
+ * private key, the corresponding certificate chain, any additional
+ * certificates and a CRL. The structures in @key, @chain @crl, and @extra_certs
+ * must not be initialized.
+ *
+ * The @extra_certs and @extra_certs_len parameters are optional
+ * and both may be set to %NULL. If either is non-%NULL, then both must
+ * be set. The value for @extra_certs is allocated
+ * using gnutls_malloc().
+ *
+ * Encrypted PKCS12 bags and PKCS8 private keys are supported, but
+ * only with password based security and the same password for all
+ * operations.
+ *
+ * Note that a PKCS12 structure may contain many keys and/or certificates,
+ * and there is no way to identify which key/certificate pair you want.
+ * For this reason this function is useful for PKCS12 files that contain
+ * only one key/certificate pair and/or one CRL.
+ *
+ * If the provided structure has encrypted fields but no password
+ * is provided then this function returns %GNUTLS_E_DECRYPTION_FAILED.
+ *
+ * Note that normally the chain constructed does not include self signed
+ * certificates, to comply with TLS' requirements. If, however, the flag
+ * %GNUTLS_PKCS12_SP_INCLUDE_SELF_SIGNED is specified then
+ * self signed certificates will be included in the chain.
+ *
+ * Prior to using this function the PKCS #12 structure integrity must
+ * be verified using gnutls_pkcs12_verify_mac().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.1.0
+ **/
+int
+gnutls_pkcs12_simple_parse(gnutls_pkcs12_t p12,
+ const char *password,
+ gnutls_x509_privkey_t * key,
+ gnutls_x509_crt_t ** chain,
+ unsigned int *chain_len,
+ gnutls_x509_crt_t ** extra_certs,
+ unsigned int *extra_certs_len,
+ gnutls_x509_crl_t * crl, unsigned int flags)
+{
+ gnutls_pkcs12_bag_t bag = NULL;
+ gnutls_x509_crt_t *_extra_certs = NULL;
+ unsigned int _extra_certs_len = 0;
+ gnutls_x509_crt_t *_chain = NULL;
+ unsigned int _chain_len = 0;
+ int idx = 0;
+ int ret;
+ size_t cert_id_size = 0;
+ size_t key_id_size = 0;
+ uint8_t cert_id[20];
+ uint8_t key_id[20];
+ int privkey_ok = 0;
+ unsigned int i;
+ int elements_in_bag;
+
+ *key = NULL;
+
+ if (crl)
+ *crl = NULL;
+
+ /* find the first private key */
+ for (;;) {
+
+ ret = gnutls_pkcs12_bag_init(&bag);
+ if (ret < 0) {
+ bag = NULL;
+ gnutls_assert();
+ goto done;
+ }
+
+ ret = gnutls_pkcs12_get_bag(p12, idx, bag);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_pkcs12_bag_deinit(bag);
+ bag = NULL;
+ break;
+ }
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ ret = gnutls_pkcs12_bag_get_type(bag, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ if (ret == GNUTLS_BAG_ENCRYPTED) {
+ if (password == NULL) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_DECRYPTION_FAILED);
+ goto done;
+ }
+
+ ret = gnutls_pkcs12_bag_decrypt(bag, password);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+ }
+
+ elements_in_bag = gnutls_pkcs12_bag_get_count(bag);
+ if (elements_in_bag < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ for (i = 0; i < (unsigned)elements_in_bag; i++) {
+ int type;
+ gnutls_datum_t data;
+
+ type = gnutls_pkcs12_bag_get_type(bag, i);
+ if (type < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ ret = gnutls_pkcs12_bag_get_data(bag, i, &data);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ switch (type) {
+ case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
+ if (password == NULL) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_DECRYPTION_FAILED);
+ goto done;
+ }
+
+ FALLTHROUGH;
+ case GNUTLS_BAG_PKCS8_KEY:
+ if (*key != NULL) { /* too simple to continue */
+ gnutls_assert();
+ break;
+ }
+
+ ret = gnutls_x509_privkey_init(key);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ ret = gnutls_x509_privkey_import_pkcs8
+ (*key, &data, GNUTLS_X509_FMT_DER,
+ password,
+ type ==
+ GNUTLS_BAG_PKCS8_KEY ?
+ GNUTLS_PKCS_PLAIN : 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ key_id_size = sizeof(key_id);
+ ret =
+ gnutls_x509_privkey_get_key_id(*key, 0,
+ key_id,
+ &key_id_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ privkey_ok = 1; /* break */
+ break;
+ default:
+ break;
+ }
+ }
+
+ idx++;
+ gnutls_pkcs12_bag_deinit(bag);
+ bag = NULL;
+
+ if (privkey_ok != 0) /* private key was found */
+ break;
+ }
+
+ if (privkey_ok == 0) { /* no private key */
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ /* now find the corresponding certificate
+ */
+ idx = 0;
+ bag = NULL;
+ for (;;) {
+ ret = gnutls_pkcs12_bag_init(&bag);
+ if (ret < 0) {
+ bag = NULL;
+ gnutls_assert();
+ goto done;
+ }
+
+ ret = gnutls_pkcs12_get_bag(p12, idx, bag);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_pkcs12_bag_deinit(bag);
+ bag = NULL;
+ break;
+ }
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ ret = gnutls_pkcs12_bag_get_type(bag, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ if (ret == GNUTLS_BAG_ENCRYPTED) {
+ ret = gnutls_pkcs12_bag_decrypt(bag, password);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+ }
+
+ elements_in_bag = gnutls_pkcs12_bag_get_count(bag);
+ if (elements_in_bag < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ for (i = 0; i < (unsigned)elements_in_bag; i++) {
+ int type;
+ gnutls_datum_t data;
+ gnutls_x509_crt_t this_cert;
+
+ type = gnutls_pkcs12_bag_get_type(bag, i);
+ if (type < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ ret = gnutls_pkcs12_bag_get_data(bag, i, &data);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ switch (type) {
+ case GNUTLS_BAG_CERTIFICATE:
+ ret = gnutls_x509_crt_init(&this_cert);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ ret =
+ gnutls_x509_crt_import(this_cert,
+ &data,
+ GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_x509_crt_deinit(this_cert);
+ this_cert = NULL;
+ goto done;
+ }
+
+ /* check if the key id match */
+ cert_id_size = sizeof(cert_id);
+ ret =
+ gnutls_x509_crt_get_key_id(this_cert,
+ 0, cert_id,
+ &cert_id_size);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_x509_crt_deinit(this_cert);
+ this_cert = NULL;
+ goto done;
+ }
+
+ if (memcmp(cert_id, key_id, cert_id_size) != 0) { /* they don't match - skip the certificate */
+ if (unlikely(INT_ADD_OVERFLOW(_extra_certs_len, 1))) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto done;
+ }
+
+ _extra_certs =
+ _gnutls_reallocarray_fast(_extra_certs,
+ ++_extra_certs_len,
+ sizeof(_extra_certs[0]));
+ if (!_extra_certs) {
+ gnutls_assert();
+ ret =
+ GNUTLS_E_MEMORY_ERROR;
+ goto done;
+ }
+ _extra_certs
+ [_extra_certs_len -
+ 1] = this_cert;
+ this_cert = NULL;
+ } else {
+ if (chain && _chain_len == 0) {
+ _chain =
+ gnutls_malloc(sizeof
+ (_chain
+ [0]) *
+ (++_chain_len));
+ if (!_chain) {
+ gnutls_assert();
+ ret =
+ GNUTLS_E_MEMORY_ERROR;
+ goto done;
+ }
+ _chain[_chain_len - 1] =
+ this_cert;
+ this_cert = NULL;
+ } else {
+ gnutls_x509_crt_deinit
+ (this_cert);
+ this_cert = NULL;
+ }
+ }
+ break;
+
+ case GNUTLS_BAG_CRL:
+ if (crl == NULL || *crl != NULL) {
+ gnutls_assert();
+ break;
+ }
+
+ ret = gnutls_x509_crl_init(crl);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ ret =
+ gnutls_x509_crl_import(*crl, &data,
+ GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_x509_crl_deinit(*crl);
+ *crl = NULL;
+ goto done;
+ }
+ break;
+
+ case GNUTLS_BAG_ENCRYPTED:
+ /* XXX Bother to recurse one level down? Unlikely to
+ use the same password anyway. */
+ case GNUTLS_BAG_EMPTY:
+ default:
+ break;
+ }
+ }
+
+ idx++;
+ gnutls_pkcs12_bag_deinit(bag);
+ bag = NULL;
+ }
+
+ if (chain != NULL) {
+ if (_chain_len != 1) {
+ ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ goto done;
+ }
+
+ ret =
+ make_chain(&_chain, &_chain_len, &_extra_certs,
+ &_extra_certs_len, flags);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+ }
+
+ ret = 0;
+
+ done:
+ if (bag)
+ gnutls_pkcs12_bag_deinit(bag);
+
+ if (ret < 0) {
+ if (*key) {
+ gnutls_x509_privkey_deinit(*key);
+ *key = NULL;
+ }
+ if (crl != NULL && *crl != NULL) {
+ gnutls_x509_crl_deinit(*crl);
+ *crl = NULL;
+ }
+ if (_extra_certs_len && _extra_certs != NULL) {
+ for (i = 0; i < _extra_certs_len; i++)
+ gnutls_x509_crt_deinit(_extra_certs[i]);
+ gnutls_free(_extra_certs);
+ }
+ if (_chain_len && _chain != NULL) {
+ for (i = 0; i < _chain_len; i++)
+ gnutls_x509_crt_deinit(_chain[i]);
+ gnutls_free(_chain);
+ }
+
+ return ret;
+ }
+
+ if (extra_certs && _extra_certs_len > 0) {
+ *extra_certs = _extra_certs;
+ *extra_certs_len = _extra_certs_len;
+ } else {
+ if (extra_certs) {
+ *extra_certs = NULL;
+ *extra_certs_len = 0;
+ }
+ for (i = 0; i < _extra_certs_len; i++)
+ gnutls_x509_crt_deinit(_extra_certs[i]);
+ gnutls_free(_extra_certs);
+ }
+
+ if (chain != NULL) {
+ *chain = _chain;
+ *chain_len = _chain_len;
+ }
+
+ return ret;
+}
+
+
+/**
+ * gnutls_pkcs12_mac_info:
+ * @pkcs12: A pkcs12 type
+ * @mac: the MAC algorithm used as %gnutls_mac_algorithm_t
+ * @salt: the salt used for string to key (if non-NULL then @salt_size initially holds its size)
+ * @salt_size: string to key salt size
+ * @iter_count: string to key iteration count
+ * @oid: if non-NULL it will contain an allocated null-terminated variable with the OID
+ *
+ * This function will provide information on the MAC algorithm used
+ * in a PKCS #12 structure. If the structure algorithms
+ * are unknown the code %GNUTLS_E_UNKNOWN_HASH_ALGORITHM will be returned,
+ * and only @oid, will be set. That is, @oid will be set on structures
+ * with a MAC whether supported or not. It must be deinitialized using gnutls_free().
+ * The other variables are only set on supported structures.
+ *
+ * Returns: %GNUTLS_E_INVALID_REQUEST if the provided structure doesn't contain a MAC,
+ * %GNUTLS_E_UNKNOWN_HASH_ALGORITHM if the structure's MAC isn't supported, or
+ * another negative error code in case of a failure. Zero on success.
+ **/
+int
+gnutls_pkcs12_mac_info(gnutls_pkcs12_t pkcs12, unsigned int *mac,
+ void *salt, unsigned int *salt_size, unsigned int *iter_count, char **oid)
+{
+ int ret;
+ gnutls_datum_t tmp = { NULL, 0 }, dsalt = {
+ NULL, 0};
+ gnutls_mac_algorithm_t algo;
+
+ if (oid)
+ *oid = NULL;
+
+ if (pkcs12 == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret =
+ _gnutls_x509_read_value(pkcs12->pkcs12, "macData.mac.digestAlgorithm.algorithm",
+ &tmp);
+ if (ret < 0) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (oid) {
+ *oid = (char*)tmp.data;
+ }
+
+ algo = DIG_TO_MAC(gnutls_oid_to_digest((char*)tmp.data));
+ if (algo == GNUTLS_MAC_UNKNOWN || mac_to_entry(algo) == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_UNKNOWN_HASH_ALGORITHM;
+ }
+
+ if (oid) {
+ tmp.data = NULL;
+ }
+
+ if (mac) {
+ *mac = algo;
+ }
+
+ if (iter_count) {
+ ret =
+ _gnutls_x509_read_uint(pkcs12->pkcs12, "macData.iterations",
+ iter_count);
+ if (ret < 0) {
+ *iter_count = 1; /* the default */
+ }
+ }
+
+ if (salt) {
+ /* Read the salt from the structure.
+ */
+ ret =
+ _gnutls_x509_read_null_value(pkcs12->pkcs12, "macData.macSalt",
+ &dsalt);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (*salt_size >= (unsigned)dsalt.size) {
+ *salt_size = dsalt.size;
+ if (dsalt.size > 0)
+ memcpy(salt, dsalt.data, dsalt.size);
+ } else {
+ *salt_size = dsalt.size;
+ ret = gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
+ goto cleanup;
+ }
+ }
+
+ ret = 0;
+ cleanup:
+ _gnutls_free_datum(&tmp);
+ _gnutls_free_datum(&dsalt);
+ return ret;
+
+}
+