diff options
Diffstat (limited to 'lib/x509/pkcs12.c')
-rw-r--r-- | lib/x509/pkcs12.c | 2063 |
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; + +} + |