/* * Copyright (C) 2003-2005, 2008, 2010, 2012 Free Software Foundation, * 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 3 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 * */ /* Functions that relate on PKCS12 packet parsing. */ #include #include #ifdef ENABLE_PKI #include #include #include #include #include #include #include "x509_int.h" #include /* Decodes the PKCS #12 auth_safe, and returns the allocated raw data, * which holds them. Returns an ASN1_TYPE of authenticatedSafe. */ static int _decode_pkcs12_auth_safe (ASN1_TYPE pkcs12, ASN1_TYPE * authen_safe, gnutls_datum_t * raw) { char oid[MAX_OID_SIZE]; ASN1_TYPE c2 = ASN1_TYPE_EMPTY; 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_value (pkcs12, "authSafe.content", &auth_safe, 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; } /** * gnutls_pkcs12_init: * @pkcs12: The structure to be initialized * * This function will initialize a PKCS12 structure. 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 = asn1_create_element (_gnutls_get_pkix (), "PKIX1.pkcs-12-PFX", &(*pkcs12)->pkcs12); if (result != ASN1_SUCCESS) { gnutls_assert (); gnutls_free (*pkcs12); return _gnutls_asn2err (result); } return 0; /* success */ } return GNUTLS_E_MEMORY_ERROR; } /** * gnutls_pkcs12_deinit: * @pkcs12: The structure to be initialized * * This function will deinitialize a PKCS12 structure. **/ 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 structure 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) { uint8_t *out; result = _gnutls_fbase64_decode (PEM_PKCS12, data->data, data->size, &out); if (result <= 0) { if (result == 0) result = GNUTLS_E_INTERNAL_ERROR; gnutls_assert (); return result; } _data.data = out; _data.size = result; need_free = 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: Holds the pkcs12 structure * @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) { if (pkcs12 == NULL) { gnutls_assert (); return GNUTLS_E_INVALID_REQUEST; } return _gnutls_x509_export_int (pkcs12->pkcs12, format, PEM_PKCS12, output_data, output_data_size); } 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; } static inline char * ucs2_to_ascii (char *data, int size) { int i, j; for (i = 0; i < size / 2; i++) { j = 2 * i + 1; if (isascii (data[j])) data[i] = data[i * 2 + 1]; else data[i] = '?'; } data[i] = 0; return data; } /* 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[ASN1_MAX_NAME_SIZE]; ASN1_TYPE c2 = ASN1_TYPE_EMPTY; int len, result; int bag_type; gnutls_datum_t attr_val; int count = 0, i, attributes, j; size_t size; /* 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, 0); 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; result = _pkcs12_decode_crt_bag (bag_type, &tmp, &bag->element[i].data); if (result < 0) { gnutls_assert (); goto cleanup; } _gnutls_free_datum (&tmp); } /* 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.?%u", 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) { size = attr_val.size; result = _gnutls_x509_decode_octet_string (NULL, attr_val.data, size, attr_val.data, &size); attr_val.size = size; if (result < 0) { _gnutls_free_datum (&attr_val); gnutls_assert (); _gnutls_debug_log ("Error decoding PKCS12 Bag Attribute OID '%s'\n", oid); continue; } bag->element[i].local_key_id = attr_val; } else if (strcmp (oid, FRIENDLY_NAME_OID) == 0) { size = attr_val.size; result = _gnutls_x509_decode_octet_string ("BMPString", attr_val.data, size, attr_val.data, &size); attr_val.size = size; if (result < 0) { _gnutls_free_datum (&attr_val); gnutls_assert (); _gnutls_debug_log ("Error decoding PKCS12 Bag Attribute OID '%s'\n", oid); continue; } bag->element[i].friendly_name = ucs2_to_ascii ((char*)attr_val.data, attr_val.size); } else { _gnutls_free_datum (&attr_val); _gnutls_debug_log ("Unknown PKCS12 Bag Attribute OID '%s'\n", oid); } } bag->element[i].type = bag_type; } asn1_delete_structure (&c2); return 0; cleanup: if (c2) asn1_delete_structure (&c2); return result; } static int _parse_safe_contents (ASN1_TYPE 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_value (sc, sc_name, &content, 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: should contain a gnutls_pkcs12_t structure * @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_TYPE c2 = ASN1_TYPE_EMPTY; int result, len; char root2[ASN1_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), "?%u.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), "?%u.content", indx + 1); if (strcmp (oid, DATA_OID) == 0) { result = _parse_safe_contents (c2, root2, bag); goto cleanup; } /* ENC_DATA_OID needs decryption */ bag->element[0].type = GNUTLS_BAG_ENCRYPTED; bag->bag_elements = 1; result = _gnutls_x509_read_value (c2, root2, &bag->element[0].data, 0); if (result < 0) { gnutls_assert (); goto cleanup; } 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_TYPE pkcs12) { uint8_t three = 3; int result; ASN1_TYPE c2 = ASN1_TYPE_EMPTY; /* 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 structure * @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_TYPE c2 = ASN1_TYPE_EMPTY; ASN1_TYPE safe_cont = ASN1_TYPE_EMPTY; 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. Reencode 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; } /** * gnutls_pkcs12_generate_mac: * @pkcs12: should contain a gnutls_pkcs12_t structure * @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) { uint8_t salt[8], key[20]; int result; const int iter = 1; digest_hd_st td1; gnutls_datum_t tmp = { NULL, 0 }; uint8_t sha_mac[20]; if (pkcs12 == NULL) { gnutls_assert (); return GNUTLS_E_INVALID_REQUEST; } /* 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. */ result = _gnutls_pkcs12_string_to_key (3 /*MAC*/, salt, sizeof (salt), iter, pass, sizeof (key), 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_hmac_init (&td1, GNUTLS_MAC_SHA1, key, sizeof (key)); if (result < 0) { gnutls_assert (); goto cleanup; } _gnutls_hmac (&td1, tmp.data, tmp.size); _gnutls_free_datum (&tmp); _gnutls_hmac_deinit (&td1, sha_mac); result = asn1_write_value (pkcs12->pkcs12, "macData.mac.digest", sha_mac, sizeof (sha_mac)); 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) { gnutls_assert (); result = _gnutls_asn2err (result); goto cleanup; } result = asn1_write_value (pkcs12->pkcs12, "macData.mac.digestAlgorithm.algorithm", HASH_OID_SHA1, 1); if (result != ASN1_SUCCESS) { gnutls_assert (); result = _gnutls_asn2err (result); goto cleanup; } return 0; cleanup: _gnutls_free_datum (&tmp); return result; } /** * gnutls_pkcs12_verify_mac: * @pkcs12: should contain a gnutls_pkcs12_t structure * @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[20]; int result; unsigned int iter; int len; digest_hd_st td1; gnutls_datum_t tmp = { NULL, 0 }, salt = { NULL, 0}; uint8_t sha_mac[20]; uint8_t sha_mac_orig[20]; 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 */ } /* Read the salt from the structure. */ result = _gnutls_x509_read_value (pkcs12->pkcs12, "macData.macSalt", &salt, 0); if (result != ASN1_SUCCESS) { gnutls_assert (); result = _gnutls_asn2err (result); goto cleanup; } /* Generate the key. */ result = _gnutls_pkcs12_string_to_key (3 /*MAC*/, salt.data, salt.size, iter, pass, sizeof (key), key); if (result < 0) { gnutls_assert (); goto cleanup; } _gnutls_free_datum (&salt); /* 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_hmac_init (&td1, GNUTLS_MAC_SHA1, key, sizeof (key)); if (result < 0) { gnutls_assert (); goto cleanup; } _gnutls_hmac (&td1, tmp.data, tmp.size); _gnutls_free_datum (&tmp); _gnutls_hmac_deinit (&td1, sha_mac); len = sizeof (sha_mac_orig); result = asn1_read_value (pkcs12->pkcs12, "macData.mac.digest", sha_mac_orig, &len); if (result != ASN1_SUCCESS) { gnutls_assert (); result = _gnutls_asn2err (result); goto cleanup; } if (memcmp (sha_mac_orig, sha_mac, sizeof (sha_mac)) != 0) { gnutls_assert (); return GNUTLS_E_MAC_VERIFY_FAILED; } return 0; cleanup: _gnutls_free_datum (&tmp); _gnutls_free_datum (&salt); return result; } static int write_attributes (gnutls_pkcs12_bag_t bag, int elem, ASN1_TYPE 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 (0) if the data are encrypted; */ int _pkcs12_encode_safe_contents (gnutls_pkcs12_bag_t bag, ASN1_TYPE * contents, int *enc) { ASN1_TYPE c2 = ASN1_TYPE_EMPTY; int result; int 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, 0); _gnutls_free_datum (&tmp); } else { result = _gnutls_x509_write_value (c2, "?LAST.bagValue", &bag->element[i].data, 0); } 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; } #endif /* ENABLE_PKI */