diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 07:33:12 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 07:33:12 +0000 |
commit | 36082a2fe36ecd800d784ae44c14f1f18c66a7e9 (patch) | |
tree | 6c68e0c0097987aff85a01dabddd34b862309a7c /lib/x509/pkcs7.c | |
parent | Initial commit. (diff) | |
download | gnutls28-36082a2fe36ecd800d784ae44c14f1f18c66a7e9.tar.xz gnutls28-36082a2fe36ecd800d784ae44c14f1f18c66a7e9.zip |
Adding upstream version 3.7.9.upstream/3.7.9upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/x509/pkcs7.c')
-rw-r--r-- | lib/x509/pkcs7.c | 2564 |
1 files changed, 2564 insertions, 0 deletions
diff --git a/lib/x509/pkcs7.c b/lib/x509/pkcs7.c new file mode 100644 index 0000000..ff8cab0 --- /dev/null +++ b/lib/x509/pkcs7.c @@ -0,0 +1,2564 @@ +/* + * Copyright (C) 2003-2015 Free Software Foundation, Inc. + * Copyright (C) 2015 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 PKCS7 certificate lists parsing. + */ + +#include "gnutls_int.h" +#include <libtasn1.h> + +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <common.h> +#include <x509_b64.h> +#include <pkcs7_int.h> +#include <gnutls/abstract.h> +#include <gnutls/pkcs7.h> + +#define ATTR_MESSAGE_DIGEST "1.2.840.113549.1.9.4" +#define ATTR_SIGNING_TIME "1.2.840.113549.1.9.5" +#define ATTR_CONTENT_TYPE "1.2.840.113549.1.9.3" + +static const uint8_t one = 1; + +/* Decodes the PKCS #7 signed data, and returns an asn1_node, + * which holds them. If raw is non null then the raw decoded + * data are copied (they are locally allocated) there. + */ +static int _decode_pkcs7_signed_data(gnutls_pkcs7_t pkcs7) +{ + asn1_node c2; + int len, result; + gnutls_datum_t tmp = {NULL, 0}; + + len = MAX_OID_SIZE - 1; + result = asn1_read_value(pkcs7->pkcs7, "contentType", pkcs7->encap_data_oid, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (strcmp(pkcs7->encap_data_oid, SIGNED_DATA_OID) != 0) { + gnutls_assert(); + _gnutls_debug_log("Unknown PKCS7 Content OID '%s'\n", pkcs7->encap_data_oid); + return GNUTLS_E_UNKNOWN_PKCS_CONTENT_TYPE; + } + + if ((result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.pkcs-7-SignedData", + &c2)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* the Signed-data has been created, so + * decode them. + */ + result = _gnutls_x509_read_value(pkcs7->pkcs7, "content", &tmp); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + /* Step 1. In case of a signed structure extract certificate set. + */ + + result = asn1_der_decoding(&c2, tmp.data, tmp.size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* read the encapsulated content */ + len = MAX_OID_SIZE - 1; + result = + asn1_read_value(c2, "encapContentInfo.eContentType", pkcs7->encap_data_oid, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (strcmp(pkcs7->encap_data_oid, DATA_OID) != 0 + && strcmp(pkcs7->encap_data_oid, DIGESTED_DATA_OID) != 0) { + _gnutls_debug_log + ("Unknown PKCS#7 Encapsulated Content OID '%s'; treating as raw data\n", + pkcs7->encap_data_oid); + + } + + /* Try reading as octet string according to rfc5652. If that fails, attempt + * a raw read according to rfc2315 */ + result = _gnutls_x509_read_string(c2, "encapContentInfo.eContent", &pkcs7->der_signed_data, ASN1_ETYPE_OCTET_STRING, 1); + if (result < 0) { + result = _gnutls_x509_read_value(c2, "encapContentInfo.eContent", &pkcs7->der_signed_data); + if (result < 0) { + pkcs7->der_signed_data.data = NULL; + pkcs7->der_signed_data.size = 0; + } else { + int tag_len, len_len; + unsigned char cls; + unsigned long tag; + + /* we skip the embedded element's tag and length - uncharted territorry - used by MICROSOFT_CERT_TRUST_LIST */ + result = asn1_get_tag_der(pkcs7->der_signed_data.data, pkcs7->der_signed_data.size, &cls, &tag_len, &tag); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = asn1_get_length_ber(pkcs7->der_signed_data.data+tag_len, pkcs7->der_signed_data.size-tag_len, &len_len); + if (result < 0) { + gnutls_assert(); + result = GNUTLS_E_ASN1_DER_ERROR; + goto cleanup; + } + + tag_len += len_len; + memmove(pkcs7->der_signed_data.data, &pkcs7->der_signed_data.data[tag_len], pkcs7->der_signed_data.size-tag_len); + pkcs7->der_signed_data.size-=tag_len; + } + } + + if (pkcs7->signed_data) + asn1_delete_structure(&pkcs7->signed_data); + pkcs7->signed_data = c2; + gnutls_free(tmp.data); + + return 0; + + cleanup: + gnutls_free(tmp.data); + if (c2) + asn1_delete_structure(&c2); + return result; +} + +static int pkcs7_reinit(gnutls_pkcs7_t pkcs7) +{ + int result; + + asn1_delete_structure(&pkcs7->pkcs7); + + result = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-7-ContentInfo", &pkcs7->pkcs7); + if (result != ASN1_SUCCESS) { + result = _gnutls_asn2err(result); + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_pkcs7_init: + * @pkcs7: A pointer to the type to be initialized + * + * This function will initialize a PKCS7 structure. PKCS7 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_pkcs7_init(gnutls_pkcs7_t * pkcs7) +{ + *pkcs7 = gnutls_calloc(1, sizeof(gnutls_pkcs7_int)); + + if (*pkcs7) { + int result = pkcs7_reinit(*pkcs7); + if (result < 0) { + gnutls_assert(); + gnutls_free(*pkcs7); + return result; + } + return 0; /* success */ + } + return GNUTLS_E_MEMORY_ERROR; +} + +/** + * gnutls_pkcs7_deinit: + * @pkcs7: the type to be deinitialized + * + * This function will deinitialize a PKCS7 type. + **/ +void gnutls_pkcs7_deinit(gnutls_pkcs7_t pkcs7) +{ + if (!pkcs7) + return; + + if (pkcs7->pkcs7) + asn1_delete_structure(&pkcs7->pkcs7); + + if (pkcs7->signed_data) + asn1_delete_structure(&pkcs7->signed_data); + + _gnutls_free_datum(&pkcs7->der_signed_data); + + gnutls_free(pkcs7); +} + +/** + * gnutls_pkcs7_import: + * @pkcs7: The data to store the parsed PKCS7. + * @data: The DER or PEM encoded PKCS7. + * @format: One of DER or PEM + * + * This function will convert the given DER or PEM encoded PKCS7 to + * the native #gnutls_pkcs7_t format. The output will be stored in + * @pkcs7. Any signed data that may be present inside the @pkcs7 + * structure, like certificates set by gnutls_pkcs7_set_crt(), will + * be freed and overwritten by this function. + * + * If the PKCS7 is PEM encoded it should have a header of "PKCS7". + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_pkcs7_import(gnutls_pkcs7_t pkcs7, const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format) +{ + int result = 0, need_free = 0; + gnutls_datum_t _data; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + _data.data = data->data; + _data.size = data->size; + + /* If the PKCS7 is in PEM format then decode it + */ + if (format == GNUTLS_X509_FMT_PEM) { + result = + _gnutls_fbase64_decode(PEM_PKCS7, data->data, + data->size, &_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + need_free = 1; + } + + if (pkcs7->expanded) { + result = pkcs7_reinit(pkcs7); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + } + pkcs7->expanded = 1; + + result = asn1_der_decoding(&pkcs7->pkcs7, _data.data, _data.size, NULL); + if (result != ASN1_SUCCESS) { + result = _gnutls_asn2err(result); + gnutls_assert(); + goto cleanup; + } + + /* Decode the signed data. + */ + result = _decode_pkcs7_signed_data(pkcs7); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = 0; + + cleanup: + if (need_free) + _gnutls_free_datum(&_data); + return result; +} + +/** + * gnutls_pkcs7_get_crt_raw2: + * @pkcs7: should contain a gnutls_pkcs7_t type + * @indx: contains the index of the certificate to extract + * @cert: will hold the contents of the certificate; must be deallocated with gnutls_free() + * + * This function will return a certificate of the PKCS7 or RFC2630 + * certificate set. + * + * After the last certificate 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. If the provided buffer is not long enough, + * then @certificate_size is updated and + * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned. + * + * Since: 3.4.2 + **/ +int +gnutls_pkcs7_get_crt_raw2(gnutls_pkcs7_t pkcs7, + unsigned indx, gnutls_datum_t * cert) +{ + int result, len; + char root2[MAX_NAME_SIZE]; + char oid[MAX_OID_SIZE]; + gnutls_datum_t tmp = { NULL, 0 }; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + /* Step 2. Parse the CertificateSet + */ + snprintf(root2, sizeof(root2), "certificates.?%u", indx + 1); + + len = sizeof(oid) - 1; + + result = asn1_read_value(pkcs7->signed_data, root2, oid, &len); + + if (result == ASN1_VALUE_NOT_FOUND) { + result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + goto cleanup; + } + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* if 'Certificate' is the choice found: + */ + if (strcmp(oid, "certificate") == 0) { + int start, end; + + result = _gnutls_x509_read_value(pkcs7->pkcs7, "content", &tmp); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = + asn1_der_decoding_startEnd(pkcs7->signed_data, tmp.data, + tmp.size, root2, &start, &end); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + end = end - start + 1; + + result = _gnutls_set_datum(cert, &tmp.data[start], end); + } else { + result = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; + } + + cleanup: + _gnutls_free_datum(&tmp); + return result; +} + +/** + * gnutls_pkcs7_get_crt_raw: + * @pkcs7: should contain a gnutls_pkcs7_t type + * @indx: contains the index of the certificate to extract + * @certificate: the contents of the certificate will be copied + * there (may be null) + * @certificate_size: should hold the size of the certificate + * + * This function will return a certificate of the PKCS7 or RFC2630 + * certificate set. + * + * After the last certificate 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. If the provided buffer is not long enough, + * then @certificate_size is updated and + * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned. + **/ +int +gnutls_pkcs7_get_crt_raw(gnutls_pkcs7_t pkcs7, + unsigned indx, void *certificate, + size_t * certificate_size) +{ + int ret; + gnutls_datum_t tmp = { NULL, 0 }; + + ret = gnutls_pkcs7_get_crt_raw2(pkcs7, indx, &tmp); + if (ret < 0) + return gnutls_assert_val(ret); + + if ((unsigned)tmp.size > *certificate_size) { + *certificate_size = tmp.size; + ret = GNUTLS_E_SHORT_MEMORY_BUFFER; + goto cleanup; + } + + *certificate_size = tmp.size; + if (certificate) + memcpy(certificate, tmp.data, tmp.size); + + cleanup: + _gnutls_free_datum(&tmp); + return ret; +} + +/** + * gnutls_pkcs7_get_crt_count: + * @pkcs7: should contain a #gnutls_pkcs7_t type + * + * This function will return the number of certificates in the PKCS7 + * or RFC2630 certificate set. + * + * Returns: On success, a positive number is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs7_get_crt_count(gnutls_pkcs7_t pkcs7) +{ + int result, count; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + /* Step 2. Count the CertificateSet */ + + result = + asn1_number_of_elements(pkcs7->signed_data, "certificates", &count); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return 0; /* no certificates */ + } + + return count; +} + +/** + * gnutls_pkcs7_signature_info_deinit: + * @info: should point to a #gnutls_pkcs7_signature_info_st structure + * + * This function will deinitialize any allocated value in the + * provided #gnutls_pkcs7_signature_info_st. + * + * Since: 3.4.2 + **/ +void gnutls_pkcs7_signature_info_deinit(gnutls_pkcs7_signature_info_st * info) +{ + gnutls_free(info->sig.data); + gnutls_free(info->issuer_dn.data); + gnutls_free(info->signer_serial.data); + gnutls_free(info->issuer_keyid.data); + gnutls_pkcs7_attrs_deinit(info->signed_attrs); + gnutls_pkcs7_attrs_deinit(info->unsigned_attrs); + memset(info, 0, sizeof(*info)); +} + +static time_t parse_time(gnutls_pkcs7_t pkcs7, const char *root) +{ + char tval[128]; + asn1_node c2 = NULL; + time_t ret; + int result, len; + + result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.Time", &c2); + if (result != ASN1_SUCCESS) { + ret = -1; + gnutls_assert(); + goto cleanup; + } + + len = sizeof(tval); + result = asn1_read_value(pkcs7->signed_data, root, tval, &len); + if (result != ASN1_SUCCESS) { + ret = -1; + gnutls_assert(); + goto cleanup; + } + + result = _asn1_strict_der_decode(&c2, tval, len, NULL); + if (result != ASN1_SUCCESS) { + ret = -1; + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_get_time(c2, "", 0); + + cleanup: + asn1_delete_structure(&c2); + return ret; +} + +/** + * gnutls_pkcs7_get_signature_count: + * @pkcs7: should contain a #gnutls_pkcs7_t type + * + * This function will return the number of signatures in the PKCS7 + * structure. + * + * Returns: On success, a positive number is returned, otherwise a + * negative error value. + * + * Since: 3.4.3 + **/ +int gnutls_pkcs7_get_signature_count(gnutls_pkcs7_t pkcs7) +{ + int ret, count; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + ret = + asn1_number_of_elements(pkcs7->signed_data, "signerInfos", &count); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return 0; + } + + return count; +} + +/** + * gnutls_pkcs7_get_signature_info: + * @pkcs7: should contain a #gnutls_pkcs7_t type + * @idx: the index of the signature info to check + * @info: will contain the output signature + * + * This function will return information about the signature identified + * by idx in the provided PKCS #7 structure. The information should be + * deinitialized using gnutls_pkcs7_signature_info_deinit(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.2 + **/ +int gnutls_pkcs7_get_signature_info(gnutls_pkcs7_t pkcs7, unsigned idx, + gnutls_pkcs7_signature_info_st * info) +{ + int ret, count, len; + char root[256]; + char oid[MAX_OID_SIZE]; + gnutls_pk_algorithm_t pk; + gnutls_sign_algorithm_t sig; + gnutls_datum_t tmp = { NULL, 0 }; + unsigned i; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + memset(info, 0, sizeof(*info)); + info->signing_time = -1; + + ret = + asn1_number_of_elements(pkcs7->signed_data, "signerInfos", &count); + if (ret != ASN1_SUCCESS || idx + 1 > (unsigned)count) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + snprintf(root, sizeof(root), + "signerInfos.?%u.signatureAlgorithm.algorithm", idx + 1); + + len = sizeof(oid) - 1; + ret = asn1_read_value(pkcs7->signed_data, root, oid, &len); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + goto unsupp_algo; + } + + sig = gnutls_oid_to_sign(oid); + if (sig == GNUTLS_SIGN_UNKNOWN) { + /* great PKCS #7 allows to only specify a public key algo */ + pk = gnutls_oid_to_pk(oid); + if (pk == GNUTLS_PK_UNKNOWN) { + gnutls_assert(); + goto unsupp_algo; + } + + /* use the digests algorithm */ + snprintf(root, sizeof(root), + "signerInfos.?%u.digestAlgorithm.algorithm", idx + 1); + + len = sizeof(oid) - 1; + ret = asn1_read_value(pkcs7->signed_data, root, oid, &len); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + goto unsupp_algo; + } + + ret = gnutls_oid_to_digest(oid); + if (ret == GNUTLS_DIG_UNKNOWN) { + gnutls_assert(); + goto unsupp_algo; + } + + sig = gnutls_pk_to_sign(pk, ret); + if (sig == GNUTLS_SIGN_UNKNOWN) { + gnutls_assert(); + goto unsupp_algo; + } + } + + info->algo = sig; + + snprintf(root, sizeof(root), "signerInfos.?%u.signature", idx + 1); + /* read the signature */ + ret = _gnutls_x509_read_value(pkcs7->signed_data, root, &info->sig); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + /* read the issuer info */ + snprintf(root, sizeof(root), + "signerInfos.?%u.sid.issuerAndSerialNumber.issuer.rdnSequence", + idx + 1); + /* read the signature */ + ret = + _gnutls_x509_get_raw_field(pkcs7->signed_data, root, + &info->issuer_dn); + if (ret >= 0) { + snprintf(root, sizeof(root), + "signerInfos.?%u.sid.issuerAndSerialNumber.serialNumber", + idx + 1); + /* read the signature */ + ret = + _gnutls_x509_read_value(pkcs7->signed_data, root, + &info->signer_serial); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + } else { /* keyid */ + snprintf(root, sizeof(root), + "signerInfos.?%u.sid.subjectKeyIdentifier", idx + 1); + /* read the signature */ + ret = + _gnutls_x509_read_value(pkcs7->signed_data, root, + &info->issuer_keyid); + if (ret < 0) { + gnutls_assert(); + } + } + + if (info->issuer_keyid.data == NULL && info->issuer_dn.data == NULL) { + ret = gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + goto fail; + } + + /* read the signing time */ + for (i = 0;; i++) { + snprintf(root, sizeof(root), + "signerInfos.?%u.signedAttrs.?%u.type", idx + 1, + i + 1); + len = sizeof(oid) - 1; + ret = asn1_read_value(pkcs7->signed_data, root, oid, &len); + if (ret != ASN1_SUCCESS) { + break; + } + + snprintf(root, sizeof(root), + "signerInfos.?%u.signedAttrs.?%u.values.?1", idx + 1, + i + 1); + ret = _gnutls_x509_read_value(pkcs7->signed_data, root, &tmp); + if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) { + tmp.data = NULL; + tmp.size = 0; + } else if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = gnutls_pkcs7_add_attr(&info->signed_attrs, oid, &tmp, 0); + gnutls_free(tmp.data); + + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + if (strcmp(oid, ATTR_SIGNING_TIME) == 0) { + info->signing_time = parse_time(pkcs7, root); + } + } + + /* read the unsigned attrs */ + for (i = 0;; i++) { + snprintf(root, sizeof(root), + "signerInfos.?%u.unsignedAttrs.?%u.type", idx + 1, + i + 1); + len = sizeof(oid) - 1; + ret = asn1_read_value(pkcs7->signed_data, root, oid, &len); + if (ret != ASN1_SUCCESS) { + break; + } + + snprintf(root, sizeof(root), + "signerInfos.?%u.unsignedAttrs.?%u.values.?1", idx + 1, + i + 1); + ret = _gnutls_x509_read_value(pkcs7->signed_data, root, &tmp); + if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) { + tmp.data = NULL; + tmp.size = 0; + } else if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = + gnutls_pkcs7_add_attr(&info->unsigned_attrs, oid, &tmp, 0); + gnutls_free(tmp.data); + + if (ret < 0) { + gnutls_assert(); + goto fail; + } + } + + return 0; + fail: + gnutls_free(tmp.data); + gnutls_pkcs7_signature_info_deinit(info); + return ret; + unsupp_algo: + return GNUTLS_E_UNKNOWN_ALGORITHM; +} + +/* Verifies that the hash attribute ATTR_MESSAGE_DIGEST is present + * and matches our calculated hash */ +static int verify_hash_attr(gnutls_pkcs7_t pkcs7, const char *root, + gnutls_sign_algorithm_t algo, + const gnutls_datum_t *data) +{ + unsigned hash; + gnutls_datum_t tmp = { NULL, 0 }; + gnutls_datum_t tmp2 = { NULL, 0 }; + uint8_t hash_output[MAX_HASH_SIZE]; + unsigned hash_size, i; + char oid[MAX_OID_SIZE]; + char name[256]; + unsigned msg_digest_ok = 0; + unsigned num_cont_types = 0; + int ret; + + hash = gnutls_sign_get_hash_algorithm(algo); + + /* hash the data */ + if (hash == GNUTLS_DIG_UNKNOWN) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + hash_size = gnutls_hash_get_len(hash); + + if (data == NULL || data->data == NULL) { + data = &pkcs7->der_signed_data; + } + + if (data->size == 0) { + return gnutls_assert_val(GNUTLS_E_NO_EMBEDDED_DATA); + } + + ret = gnutls_hash_fast(hash, data->data, data->size, hash_output); + if (ret < 0) + return gnutls_assert_val(ret); + + /* now verify that hash matches */ + for (i = 0;; i++) { + snprintf(name, sizeof(name), "%s.signedAttrs.?%u", root, i + 1); + + ret = _gnutls_x509_decode_and_read_attribute(pkcs7->signed_data, + name, oid, + sizeof(oid), &tmp, + 1, 0); + if (ret < 0) { + if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) + break; + return gnutls_assert_val(ret); + } + + if (strcmp(oid, ATTR_MESSAGE_DIGEST) == 0) { + ret = + _gnutls_x509_decode_string(ASN1_ETYPE_OCTET_STRING, + tmp.data, tmp.size, + &tmp2, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (tmp2.size == hash_size + && memcmp(hash_output, tmp2.data, tmp2.size) == 0) { + msg_digest_ok = 1; + } else { + gnutls_assert(); + } + } else if (strcmp(oid, ATTR_CONTENT_TYPE) == 0) { + if (num_cont_types > 0) { + gnutls_assert(); + ret = GNUTLS_E_PARSING_ERROR; + goto cleanup; + } + + num_cont_types++; + + /* check if it matches */ + ret = + _gnutls_x509_get_raw_field(pkcs7->signed_data, + "encapContentInfo.eContentType", + &tmp2); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (tmp2.size != tmp.size + || memcmp(tmp.data, tmp2.data, tmp2.size) != 0) { + gnutls_assert(); + ret = GNUTLS_E_PARSING_ERROR; + goto cleanup; + } + } + + gnutls_free(tmp.data); + gnutls_free(tmp2.data); + } + + if (msg_digest_ok) + ret = 0; + else + ret = gnutls_assert_val(GNUTLS_E_PK_SIG_VERIFY_FAILED); + + cleanup: + gnutls_free(tmp.data); + gnutls_free(tmp2.data); + return ret; +} + +/* Returns the data to be used for signature verification. PKCS #7 + * decided that this should not be an easy task. + */ +static int figure_pkcs7_sigdata(gnutls_pkcs7_t pkcs7, const char *root, + const gnutls_datum_t * data, + gnutls_sign_algorithm_t algo, + gnutls_datum_t * sigdata) +{ + int ret; + char name[256]; + + snprintf(name, sizeof(name), "%s.signedAttrs", root); + /* read the signature */ + ret = _gnutls_x509_get_raw_field(pkcs7->signed_data, name, sigdata); + if (ret == 0) { + /* verify that hash matches */ + ret = verify_hash_attr(pkcs7, root, algo, data); + if (ret < 0) + return gnutls_assert_val(ret); + + if (sigdata->size > 0) + sigdata->data[0] = 0x31; + + return 0; + } + + /* We have no signedAttrs. Use the provided data, or the encapsulated */ + if (data == NULL || data->data == NULL) { + return _gnutls_set_datum(sigdata, pkcs7->der_signed_data.data, pkcs7->der_signed_data.size); + } + + return _gnutls_set_datum(sigdata, data->data, data->size); +} + +/** + * gnutls_pkcs7_get_embedded_data: + * @pkcs7: should contain a gnutls_pkcs7_t type + * @flags: must be zero or %GNUTLS_PKCS7_EDATA_GET_RAW + * @data: will hold the embedded data in the provided structure + * + * This function will return the data embedded in the signature of + * the PKCS7 structure. If no data are available then + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + * + * The returned data must be de-allocated using gnutls_free(). + * + * Note, that this function returns the exact same data that are + * authenticated. If the %GNUTLS_PKCS7_EDATA_GET_RAW flag is provided, + * the returned data will be including the wrapping tag/value as + * they are encoded in the structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.8 + **/ +int +gnutls_pkcs7_get_embedded_data(gnutls_pkcs7_t pkcs7, unsigned flags, + gnutls_datum_t *data) +{ + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + if (pkcs7->der_signed_data.size == 0) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + if (flags & GNUTLS_PKCS7_EDATA_GET_RAW) { + if (pkcs7->signed_data == NULL) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + return _gnutls_x509_read_value(pkcs7->signed_data, "encapContentInfo.eContent", data); + } else { + return _gnutls_set_datum(data, pkcs7->der_signed_data.data, pkcs7->der_signed_data.size); + } +} + +/** + * gnutls_pkcs7_get_embedded_data_oid: + * @pkcs7: should contain a gnutls_pkcs7_t type + * + * This function will return the OID of the data embedded in the signature of + * the PKCS7 structure. If no data are available then %NULL will be + * returned. The returned value will be valid during the lifetime + * of the @pkcs7 structure. + * + * Returns: On success, a pointer to an OID string, %NULL on error. + * + * Since: 3.5.5 + **/ +const char * +gnutls_pkcs7_get_embedded_data_oid(gnutls_pkcs7_t pkcs7) +{ + if (pkcs7 == NULL || pkcs7->encap_data_oid[0] == 0) + return NULL; + + return pkcs7->encap_data_oid; +} + +/** + * gnutls_pkcs7_verify_direct: + * @pkcs7: should contain a #gnutls_pkcs7_t type + * @signer: the certificate believed to have signed the structure + * @idx: the index of the signature info to check + * @data: The data to be verified or %NULL + * @flags: Zero or an OR list of #gnutls_certificate_verify_flags + * + * This function will verify the provided data against the signature + * present in the SignedData of the PKCS #7 structure. If the data + * provided are NULL then the data in the encapsulatedContent field + * will be used instead. + * + * Note that, unlike gnutls_pkcs7_verify() this function does not + * verify the key purpose of the signer. It is expected for the caller + * to verify the intended purpose of the %signer -e.g., via gnutls_x509_crt_get_key_purpose_oid(), + * or gnutls_x509_crt_check_key_purpose(). + * + * Note also, that since GnuTLS 3.5.6 this function introduces checks in the + * end certificate (@signer), including time checks and key usage checks. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. A verification error results to a + * %GNUTLS_E_PK_SIG_VERIFY_FAILED and the lack of encapsulated data + * to verify to a %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE. + * + * Since: 3.4.2 + **/ +int gnutls_pkcs7_verify_direct(gnutls_pkcs7_t pkcs7, + gnutls_x509_crt_t signer, + unsigned idx, + const gnutls_datum_t *data, unsigned flags) +{ + int count, ret; + gnutls_datum_t tmpdata = { NULL, 0 }; + gnutls_pkcs7_signature_info_st info; + gnutls_datum_t sigdata = { NULL, 0 }; + char root[128]; + + memset(&info, 0, sizeof(info)); + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + ret = + asn1_number_of_elements(pkcs7->signed_data, "signerInfos", &count); + if (ret != ASN1_SUCCESS || idx + 1 > (unsigned)count) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + ret = gnutls_pkcs7_get_signature_info(pkcs7, idx, &info); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + snprintf(root, sizeof(root), "signerInfos.?%u", idx + 1); + ret = figure_pkcs7_sigdata(pkcs7, root, data, info.algo, &sigdata); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + gnutls_x509_crt_verify_data2(signer, info.algo, flags, &sigdata, + &info.sig); + if (ret < 0) { + gnutls_assert(); + } + + cleanup: + gnutls_free(tmpdata.data); + gnutls_free(sigdata.data); + gnutls_pkcs7_signature_info_deinit(&info); + + return ret; +} + +/* Finds the issuer of the given certificate (@cert) in the + * included in PKCS#7 list of certificates */ +static gnutls_x509_crt_t find_verified_issuer_of(gnutls_pkcs7_t pkcs7, + gnutls_x509_crt_t cert, + const char *purpose, + unsigned vflags) +{ + gnutls_x509_crt_t issuer = NULL; + int ret, count; + gnutls_datum_t tmp = { NULL, 0 }; + unsigned i, vtmp; + + count = gnutls_pkcs7_get_crt_count(pkcs7); + if (count < 0) { + gnutls_assert(); + return NULL; + } + + for (i = 0; i < (unsigned)count; i++) { + /* Try to find the signer in the appended list. */ + ret = gnutls_pkcs7_get_crt_raw2(pkcs7, i, &tmp); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = gnutls_x509_crt_init(&issuer); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = gnutls_x509_crt_import(issuer, &tmp, GNUTLS_X509_FMT_DER); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + if (!gnutls_x509_crt_check_issuer(cert, issuer)) { + gnutls_assert(); + goto skip; + } + + ret = gnutls_x509_crt_verify(cert, &issuer, 1, vflags|GNUTLS_VERIFY_DO_NOT_ALLOW_SAME, &vtmp); + if (ret < 0 || vtmp != 0 || + (purpose != NULL && !_gnutls_check_key_purpose(issuer, purpose, 0))) { + gnutls_assert(); /* maybe next one is trusted */ + _gnutls_cert_log("failed verification with", issuer); + skip: + gnutls_x509_crt_deinit(issuer); + issuer = NULL; + gnutls_free(tmp.data); + continue; + } + + _gnutls_cert_log("issued by", issuer); + + /* we found a signer we trust. let's return it */ + break; + } + + if (issuer == NULL) { + gnutls_assert(); + return NULL; + } + goto cleanup; + + fail: + if (issuer) { + gnutls_x509_crt_deinit(issuer); + issuer = NULL; + } + + cleanup: + gnutls_free(tmp.data); + + return issuer; +} + +/* Finds a certificate that is issued by @issuer -if given-, and matches + * either the serial number or the key ID (both in @info) . + */ +static gnutls_x509_crt_t find_child_of_with_serial(gnutls_pkcs7_t pkcs7, + gnutls_x509_crt_t issuer, + const char *purpose, + gnutls_pkcs7_signature_info_st *info) +{ + gnutls_x509_crt_t crt = NULL; + int ret, count; + uint8_t tmp[128]; + size_t tmp_size; + gnutls_datum_t tmpdata = { NULL, 0 }; + unsigned i; + + count = gnutls_pkcs7_get_crt_count(pkcs7); + if (count < 0) { + gnutls_assert(); + return NULL; + } + + for (i = 0; i < (unsigned)count; i++) { + /* Try to find the crt in the appended list. */ + ret = gnutls_pkcs7_get_crt_raw2(pkcs7, i, &tmpdata); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = gnutls_x509_crt_init(&crt); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = gnutls_x509_crt_import(crt, &tmpdata, GNUTLS_X509_FMT_DER); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + if (issuer != NULL) { + if (!gnutls_x509_crt_check_issuer(crt, issuer)) { + gnutls_assert(); + goto skip; + } + } + + if (purpose) { + ret = + _gnutls_check_key_purpose(crt, purpose, 0); + if (ret == 0) { + _gnutls_cert_log("key purpose unacceptable", crt); + goto skip; + } + } + + if (info->signer_serial.size > 0) { + tmp_size = sizeof(tmp); + ret = gnutls_x509_crt_get_serial(crt, tmp, &tmp_size); + if (ret < 0) { + gnutls_assert(); + goto skip; + } + + if (tmp_size != info->signer_serial.size + || memcmp(info->signer_serial.data, tmp, + tmp_size) != 0) { + _gnutls_cert_log("doesn't match serial", crt); + gnutls_assert(); + goto skip; + } + } else if (info->issuer_keyid.size > 0) { + tmp_size = sizeof(tmp); + ret = gnutls_x509_crt_get_subject_key_id(crt, tmp, &tmp_size, NULL); + if (ret < 0) { + gnutls_assert(); + goto skip; + } + + if (tmp_size != info->issuer_keyid.size + || memcmp(info->issuer_keyid.data, tmp, + tmp_size) != 0) { + _gnutls_cert_log("doesn't match key ID", crt); + gnutls_assert(); + skip: + gnutls_x509_crt_deinit(crt); + crt = NULL; + gnutls_free(tmpdata.data); + continue; + } + } else { + gnutls_assert(); + crt = NULL; + goto fail; + } + + _gnutls_cert_log("signer is", crt); + + /* we found the child with the given serial or key ID */ + break; + } + + if (crt == NULL) { + gnutls_assert(); + return NULL; + } + + goto cleanup; + fail: + if (crt) { + gnutls_x509_crt_deinit(crt); + crt = NULL; + } + + cleanup: + gnutls_free(tmpdata.data); + + return crt; +} + +static +gnutls_x509_crt_t find_signer(gnutls_pkcs7_t pkcs7, gnutls_x509_trust_list_t tl, + gnutls_typed_vdata_st * vdata, + unsigned vdata_size, + unsigned vflags, + gnutls_pkcs7_signature_info_st * info) +{ + gnutls_x509_crt_t issuer = NULL; + gnutls_x509_crt_t signer = NULL; + int ret; + gnutls_datum_t tmp = { NULL, 0 }; + unsigned i, vtmp; + const char *purpose = NULL; + + if (info->issuer_keyid.data) { + ret = + gnutls_x509_trust_list_get_issuer_by_subject_key_id(tl, + NULL, + &info-> + issuer_keyid, + &signer, + 0); + if (ret < 0) { + gnutls_assert(); + signer = NULL; + } + } + + /* get key purpose */ + for (i = 0; i < vdata_size; i++) { + if (vdata[i].type == GNUTLS_DT_KEY_PURPOSE_OID) { + purpose = (char *)vdata[i].data; + break; + } + } + + /* this will give us the issuer of the signer (wtf) */ + if (info->issuer_dn.data && signer == NULL) { + ret = + gnutls_x509_trust_list_get_issuer_by_dn(tl, + &info->issuer_dn, + &issuer, 0); + if (ret < 0) { + gnutls_assert(); + signer = NULL; + } + + if (issuer) { + /* try to find the actual signer in the list of + * certificates */ + signer = find_child_of_with_serial(pkcs7, issuer, purpose, info); + if (signer == NULL) { + gnutls_assert(); + goto fail; + } + + gnutls_x509_crt_deinit(issuer); + issuer = NULL; + } + } + + if (signer == NULL) { + /* get the signer from the pkcs7 list; the one that matches serial + * or key ID */ + signer = find_child_of_with_serial(pkcs7, NULL, purpose, info); + if (signer == NULL) { + gnutls_assert(); + goto fail; + } + + /* if the signer cannot be verified from our trust list, make a chain of certificates + * starting from the identified signer, to a root we know. */ + ret = gnutls_x509_trust_list_verify_crt2(tl, &signer, 1, vdata, vdata_size, vflags, &vtmp, NULL); + if (ret < 0 || vtmp != 0) { + gnutls_x509_crt_t prev = NULL; + + issuer = signer; + /* construct a chain */ + do { + if (prev && prev != signer) { + gnutls_x509_crt_deinit(prev); + } + prev = issuer; + + issuer = find_verified_issuer_of(pkcs7, issuer, purpose, vflags); + + if (issuer != NULL && gnutls_x509_crt_check_issuer(issuer, issuer)) { + if (prev && prev != signer) + gnutls_x509_crt_deinit(prev); + prev = issuer; + break; + } + } while(issuer != NULL); + + issuer = prev; /* the last we have seen */ + + if (issuer == NULL) { + gnutls_assert(); + goto fail; + } + + ret = gnutls_x509_trust_list_verify_crt2(tl, &issuer, 1, vdata, vdata_size, vflags, &vtmp, NULL); + if (ret < 0 || vtmp != 0) { + /* could not construct a valid chain */ + _gnutls_reason_log("signer's chain failed trust list verification", vtmp); + gnutls_assert(); + goto fail; + } + } + } else { + /* verify that the signer we got is trusted */ + ret = gnutls_x509_trust_list_verify_crt2(tl, &signer, 1, vdata, vdata_size, vflags, &vtmp, NULL); + if (ret < 0 || vtmp != 0) { + /* could not construct a valid chain */ + _gnutls_reason_log("signer failed trust list verification", vtmp); + gnutls_assert(); + goto fail; + } + } + + if (signer == NULL) { + gnutls_assert(); + goto fail; + } + + goto cleanup; + + fail: + if (signer != NULL) { + if (issuer == signer) + issuer = NULL; + gnutls_x509_crt_deinit(signer); + signer = NULL; + } + + cleanup: + if (issuer != NULL) { + gnutls_x509_crt_deinit(issuer); + issuer = NULL; + } + gnutls_free(tmp.data); + + return signer; +} + +/** + * gnutls_pkcs7_verify: + * @pkcs7: should contain a #gnutls_pkcs7_t type + * @tl: A list of trusted certificates + * @vdata: an array of typed data + * @vdata_size: the number of data elements + * @idx: the index of the signature info to check + * @data: The data to be verified or %NULL + * @flags: Zero or an OR list of #gnutls_certificate_verify_flags + * + * This function will verify the provided data against the signature + * present in the SignedData of the PKCS #7 structure. If the data + * provided are NULL then the data in the encapsulatedContent field + * will be used instead. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. A verification error results to a + * %GNUTLS_E_PK_SIG_VERIFY_FAILED and the lack of encapsulated data + * to verify to a %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE. + * + * Since: 3.4.2 + **/ +int gnutls_pkcs7_verify(gnutls_pkcs7_t pkcs7, + gnutls_x509_trust_list_t tl, + gnutls_typed_vdata_st *vdata, + unsigned int vdata_size, + unsigned idx, + const gnutls_datum_t *data, unsigned flags) +{ + int count, ret; + gnutls_datum_t tmpdata = { NULL, 0 }; + gnutls_pkcs7_signature_info_st info; + gnutls_x509_crt_t signer; + gnutls_datum_t sigdata = { NULL, 0 }; + char root[128]; + + memset(&info, 0, sizeof(info)); + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + ret = + asn1_number_of_elements(pkcs7->signed_data, "signerInfos", &count); + if (ret != ASN1_SUCCESS || idx + 1 > (unsigned)count) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + /* read data */ + ret = gnutls_pkcs7_get_signature_info(pkcs7, idx, &info); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + snprintf(root, sizeof(root), "signerInfos.?%u", idx + 1); + ret = figure_pkcs7_sigdata(pkcs7, root, data, info.algo, &sigdata); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + signer = find_signer(pkcs7, tl, vdata, vdata_size, flags, &info); + if (signer) { + ret = + gnutls_x509_crt_verify_data3(signer, info.algo, vdata, vdata_size, + &sigdata, &info.sig, flags); + if (ret < 0) { + _gnutls_cert_log("failed struct verification with", signer); + gnutls_assert(); + } + gnutls_x509_crt_deinit(signer); + } else { + gnutls_assert(); + ret = GNUTLS_E_PK_SIG_VERIFY_FAILED; + } + + cleanup: + gnutls_free(tmpdata.data); + gnutls_free(sigdata.data); + gnutls_pkcs7_signature_info_deinit(&info); + + return ret; +} + +static void disable_opt_fields(gnutls_pkcs7_t pkcs7) +{ + int result; + int count; + + /* disable the optional fields */ + result = asn1_number_of_elements(pkcs7->signed_data, "crls", &count); + if (result != ASN1_SUCCESS || count == 0) { + (void)asn1_write_value(pkcs7->signed_data, "crls", NULL, 0); + } + + result = + asn1_number_of_elements(pkcs7->signed_data, "certificates", &count); + if (result != ASN1_SUCCESS || count == 0) { + (void)asn1_write_value(pkcs7->signed_data, "certificates", NULL, 0); + } + + return; +} + +static int reencode(gnutls_pkcs7_t pkcs7) +{ + int result; + + if (pkcs7->signed_data != NULL) { + disable_opt_fields(pkcs7); + + /* Replace the old content with the new + */ + result = + _gnutls_x509_der_encode_and_copy(pkcs7->signed_data, "", + pkcs7->pkcs7, "content", + 0); + if (result < 0) { + return gnutls_assert_val(result); + } + + /* Write the content type of the signed data + */ + result = + asn1_write_value(pkcs7->pkcs7, "contentType", + SIGNED_DATA_OID, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + } + return 0; +} + +/** + * gnutls_pkcs7_export: + * @pkcs7: The pkcs7 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 pkcs7 structure to DER or PEM format. + * + * If the buffer provided is not long enough to hold the output, then + * *@output_data_size is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER + * will be returned. + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN PKCS7". + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_pkcs7_export(gnutls_pkcs7_t pkcs7, + gnutls_x509_crt_fmt_t format, void *output_data, + size_t * output_data_size) +{ + int ret; + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + if ((ret = reencode(pkcs7)) < 0) + return gnutls_assert_val(ret); + + return _gnutls_x509_export_int(pkcs7->pkcs7, format, PEM_PKCS7, + output_data, output_data_size); +} + +/** + * gnutls_pkcs7_export2: + * @pkcs7: The pkcs7 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 pkcs7 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 PKCS7". + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.1.3 + **/ +int +gnutls_pkcs7_export2(gnutls_pkcs7_t pkcs7, + gnutls_x509_crt_fmt_t format, gnutls_datum_t * out) +{ + int ret; + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + if ((ret = reencode(pkcs7)) < 0) + return gnutls_assert_val(ret); + + return _gnutls_x509_export_int2(pkcs7->pkcs7, format, PEM_PKCS7, out); +} + +/* Creates an empty signed data structure in the pkcs7 + * structure and returns a handle to the signed data. + */ +static int create_empty_signed_data(asn1_node pkcs7, asn1_node * sdata) +{ + int result; + + *sdata = NULL; + + if ((result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.pkcs-7-SignedData", + sdata)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* Use version 1 + */ + result = asn1_write_value(*sdata, "version", &one, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* Use no digest algorithms + */ + + /* id-data */ + result = + asn1_write_value(*sdata, "encapContentInfo.eContentType", + DIGESTED_DATA_OID, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = asn1_write_value(*sdata, "encapContentInfo.eContent", NULL, 0); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* Add no certificates. + */ + + /* Add no crls. + */ + + /* Add no signerInfos. + */ + + return 0; + + cleanup: + asn1_delete_structure(sdata); + return result; + +} + +/** + * gnutls_pkcs7_set_crt_raw: + * @pkcs7: The pkcs7 type + * @crt: the DER encoded certificate to be added + * + * This function will add a certificate to the PKCS7 or RFC2630 + * certificate set. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs7_set_crt_raw(gnutls_pkcs7_t pkcs7, const gnutls_datum_t * crt) +{ + int result; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + /* If the signed data are uninitialized + * then create them. + */ + if (pkcs7->signed_data == NULL) { + /* The pkcs7 structure is new, so create the + * signedData. + */ + result = + create_empty_signed_data(pkcs7->pkcs7, &pkcs7->signed_data); + if (result < 0) { + gnutls_assert(); + return result; + } + } + + /* Step 2. Append the new certificate. + */ + + result = asn1_write_value(pkcs7->signed_data, "certificates", "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(pkcs7->signed_data, "certificates.?LAST", + "certificate", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(pkcs7->signed_data, + "certificates.?LAST.certificate", crt->data, + crt->size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = 0; + + cleanup: + return result; +} + +/** + * gnutls_pkcs7_set_crt: + * @pkcs7: The pkcs7 type + * @crt: the certificate to be copied. + * + * This function will add a parsed certificate to the PKCS7 or + * RFC2630 certificate set. This is a wrapper function over + * gnutls_pkcs7_set_crt_raw() . + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs7_set_crt(gnutls_pkcs7_t pkcs7, gnutls_x509_crt_t crt) +{ + int ret; + gnutls_datum_t data; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + ret = _gnutls_x509_der_encode(crt->cert, "", &data, 0); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = gnutls_pkcs7_set_crt_raw(pkcs7, &data); + + _gnutls_free_datum(&data); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_pkcs7_delete_crt: + * @pkcs7: The pkcs7 type + * @indx: the index of the certificate to delete + * + * This function will delete a certificate from a PKCS7 or RFC2630 + * certificate set. Index starts from 0. Returns 0 on success. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs7_delete_crt(gnutls_pkcs7_t pkcs7, int indx) +{ + int result; + char root2[MAX_NAME_SIZE]; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + /* Step 2. Delete the certificate. + */ + + snprintf(root2, sizeof(root2), "certificates.?%d", indx + 1); + + result = asn1_write_value(pkcs7->signed_data, root2, NULL, 0); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + return 0; + + cleanup: + return result; +} + +/* Read and write CRLs + */ + +/** + * gnutls_pkcs7_get_crl_raw2: + * @pkcs7: The pkcs7 type + * @indx: contains the index of the crl to extract + * @crl: will contain the contents of the CRL in an allocated buffer + * + * This function will return a DER encoded CRL of the PKCS7 or RFC2630 crl set. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. After the last crl has been read + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + * + * Since: 3.4.2 + **/ +int +gnutls_pkcs7_get_crl_raw2(gnutls_pkcs7_t pkcs7, + unsigned indx, gnutls_datum_t * crl) +{ + int result; + char root2[MAX_NAME_SIZE]; + gnutls_datum_t tmp = { NULL, 0 }; + int start, end; + + if (pkcs7 == NULL || crl == NULL) + return GNUTLS_E_INVALID_REQUEST; + + result = _gnutls_x509_read_value(pkcs7->pkcs7, "content", &tmp); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + /* Step 2. Parse the CertificateSet + */ + + snprintf(root2, sizeof(root2), "crls.?%u", indx + 1); + + /* Get the raw CRL + */ + result = + asn1_der_decoding_startEnd(pkcs7->signed_data, tmp.data, tmp.size, + root2, &start, &end); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + end = end - start + 1; + + result = _gnutls_set_datum(crl, &tmp.data[start], end); + + cleanup: + _gnutls_free_datum(&tmp); + return result; +} + +/** + * gnutls_pkcs7_get_crl_raw: + * @pkcs7: The pkcs7 type + * @indx: contains the index of the crl to extract + * @crl: the contents of the crl will be copied there (may be null) + * @crl_size: should hold the size of the crl + * + * This function will return a crl of the PKCS7 or RFC2630 crl set. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. If the provided buffer is not long enough, + * then @crl_size is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER is + * returned. After the last crl has been read + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + **/ +int +gnutls_pkcs7_get_crl_raw(gnutls_pkcs7_t pkcs7, + unsigned indx, void *crl, size_t * crl_size) +{ + int ret; + gnutls_datum_t tmp = { NULL, 0 }; + + ret = gnutls_pkcs7_get_crl_raw2(pkcs7, indx, &tmp); + if (ret < 0) + return gnutls_assert_val(ret); + + if ((unsigned)tmp.size > *crl_size) { + *crl_size = tmp.size; + ret = GNUTLS_E_SHORT_MEMORY_BUFFER; + goto cleanup; + } + + assert(tmp.data != NULL); + + *crl_size = tmp.size; + if (crl) + memcpy(crl, tmp.data, tmp.size); + + cleanup: + _gnutls_free_datum(&tmp); + return ret; +} + +/** + * gnutls_pkcs7_get_crl_count: + * @pkcs7: The pkcs7 type + * + * This function will return the number of certificates in the PKCS7 + * or RFC2630 crl set. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs7_get_crl_count(gnutls_pkcs7_t pkcs7) +{ + int result, count; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + /* Step 2. Count the CertificateSet */ + + result = asn1_number_of_elements(pkcs7->signed_data, "crls", &count); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return 0; /* no crls */ + } + + return count; + +} + +/** + * gnutls_pkcs7_set_crl_raw: + * @pkcs7: The pkcs7 type + * @crl: the DER encoded crl to be added + * + * This function will add a crl to the PKCS7 or RFC2630 crl set. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs7_set_crl_raw(gnutls_pkcs7_t pkcs7, const gnutls_datum_t * crl) +{ + int result; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + /* If the signed data are uninitialized + * then create them. + */ + if (pkcs7->signed_data == NULL) { + /* The pkcs7 structure is new, so create the + * signedData. + */ + result = + create_empty_signed_data(pkcs7->pkcs7, &pkcs7->signed_data); + if (result < 0) { + gnutls_assert(); + return result; + } + } + + /* Step 2. Append the new crl. + */ + + result = asn1_write_value(pkcs7->signed_data, "crls", "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(pkcs7->signed_data, "crls.?LAST", crl->data, + crl->size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = 0; + + cleanup: + return result; +} + +/** + * gnutls_pkcs7_set_crl: + * @pkcs7: The pkcs7 type + * @crl: the DER encoded crl to be added + * + * This function will add a parsed CRL to the PKCS7 or RFC2630 crl + * set. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs7_set_crl(gnutls_pkcs7_t pkcs7, gnutls_x509_crl_t crl) +{ + int ret; + gnutls_datum_t data; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + ret = _gnutls_x509_der_encode(crl->crl, "", &data, 0); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = gnutls_pkcs7_set_crl_raw(pkcs7, &data); + + _gnutls_free_datum(&data); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_pkcs7_delete_crl: + * @pkcs7: The pkcs7 type + * @indx: the index of the crl to delete + * + * This function will delete a crl from a PKCS7 or RFC2630 crl set. + * Index starts from 0. Returns 0 on success. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_pkcs7_delete_crl(gnutls_pkcs7_t pkcs7, int indx) +{ + int result; + char root2[MAX_NAME_SIZE]; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + /* Delete the crl. + */ + + snprintf(root2, sizeof(root2), "crls.?%d", indx + 1); + + result = asn1_write_value(pkcs7->signed_data, root2, NULL, 0); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + return 0; + + cleanup: + return result; +} + +static int write_signer_id(asn1_node c2, const char *root, + gnutls_x509_crt_t signer, unsigned flags) +{ + int result; + size_t serial_size; + uint8_t serial[128]; + char name[256]; + + if (flags & GNUTLS_PKCS7_WRITE_SPKI) { + const uint8_t ver = 3; + + snprintf(name, sizeof(name), "%s.version", root); + result = asn1_write_value(c2, name, &ver, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + snprintf(name, sizeof(name), "%s.sid", root); + result = asn1_write_value(c2, name, "subjectKeyIdentifier", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + serial_size = sizeof(serial); + result = + gnutls_x509_crt_get_subject_key_id(signer, serial, + &serial_size, NULL); + if (result < 0) + return gnutls_assert_val(result); + + snprintf(name, sizeof(name), "%s.subjectKeyIdentifier", root); + result = asn1_write_value(c2, name, serial, serial_size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + } else { + serial_size = sizeof(serial); + result = + gnutls_x509_crt_get_serial(signer, serial, &serial_size); + if (result < 0) + return gnutls_assert_val(result); + + snprintf(name, sizeof(name), "%s.sid", root); + result = asn1_write_value(c2, name, "issuerAndSerialNumber", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + snprintf(name, sizeof(name), + "%s.sid.issuerAndSerialNumber.serialNumber", root); + result = asn1_write_value(c2, name, serial, serial_size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + snprintf(name, sizeof(name), + "%s.sid.issuerAndSerialNumber.issuer", root); + result = + asn1_copy_node(c2, name, signer->cert, + "tbsCertificate.issuer"); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + } + + return 0; +} + +static int add_attrs(asn1_node c2, const char *root, gnutls_pkcs7_attrs_t attrs, + unsigned already_set) +{ + char name[256]; + gnutls_pkcs7_attrs_st *p = attrs; + int result; + + if (attrs == NULL) { + /* if there are no other attributes delete that field */ + if (already_set == 0) + (void)asn1_write_value(c2, root, NULL, 0); + } else { + while (p != NULL) { + result = asn1_write_value(c2, root, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + snprintf(name, sizeof(name), "%s.?LAST.type", root); + result = asn1_write_value(c2, name, p->oid, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + snprintf(name, sizeof(name), "%s.?LAST.values", root); + result = asn1_write_value(c2, name, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + snprintf(name, sizeof(name), "%s.?LAST.values.?1", + root); + result = + asn1_write_value(c2, name, p->data.data, + p->data.size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + p = p->next; + } + } + + return 0; +} + +static int write_attributes(asn1_node c2, const char *root, + const gnutls_datum_t * data, + const mac_entry_st * me, + gnutls_pkcs7_attrs_t other_attrs, unsigned flags) +{ + char name[256]; + int result, ret; + uint8_t digest[MAX_HASH_SIZE]; + gnutls_datum_t tmp = { NULL, 0 }; + unsigned digest_size; + unsigned already_set = 0; + + if (flags & GNUTLS_PKCS7_INCLUDE_TIME) { + if (data == NULL || data->data == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Add time */ + result = asn1_write_value(c2, root, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + return ret; + } + + snprintf(name, sizeof(name), "%s.?LAST.type", root); + result = asn1_write_value(c2, name, ATTR_SIGNING_TIME, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + return ret; + } + + snprintf(name, sizeof(name), "%s.?LAST.values", root); + result = asn1_write_value(c2, name, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + return ret; + } + + snprintf(name, sizeof(name), "%s.?LAST.values.?1", root); + ret = _gnutls_x509_set_raw_time(c2, name, gnutls_time(0)); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + already_set = 1; + } + + ret = add_attrs(c2, root, other_attrs, already_set); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + if (already_set != 0 || other_attrs != NULL) { + /* Add content type */ + result = asn1_write_value(c2, root, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + return ret; + } + + snprintf(name, sizeof(name), "%s.?LAST.type", root); + result = asn1_write_value(c2, name, ATTR_CONTENT_TYPE, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + return ret; + } + + snprintf(name, sizeof(name), "%s.?LAST.values", root); + result = asn1_write_value(c2, name, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + return ret; + } + + ret = + _gnutls_x509_get_raw_field(c2, + "encapContentInfo.eContentType", + &tmp); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + snprintf(name, sizeof(name), "%s.?LAST.values.?1", root); + result = asn1_write_value(c2, name, tmp.data, tmp.size); + gnutls_free(tmp.data); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + return ret; + } + + /* If we add any attribute we should add them all */ + /* Add hash */ + digest_size = _gnutls_hash_get_algo_len(me); + ret = gnutls_hash_fast(MAC_TO_DIG(me->id), data->data, data->size, digest); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + result = asn1_write_value(c2, root, "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + return ret; + } + + snprintf(name, sizeof(name), "%s.?LAST", root); + ret = + _gnutls_x509_encode_and_write_attribute(ATTR_MESSAGE_DIGEST, + c2, name, digest, + digest_size, 1); + if (ret < 0) { + gnutls_assert(); + return ret; + } + } + + return 0; +} + +/** + * gnutls_pkcs7_sign: + * @pkcs7: should contain a #gnutls_pkcs7_t type + * @signer: the certificate to sign the structure + * @signer_key: the key to sign the structure + * @data: The data to be signed or %NULL if the data are already embedded + * @signed_attrs: Any additional attributes to be included in the signed ones (or %NULL) + * @unsigned_attrs: Any additional attributes to be included in the unsigned ones (or %NULL) + * @dig: The digest algorithm to use for signing + * @flags: Should be zero or one of %GNUTLS_PKCS7 flags + * + * This function will add a signature in the provided PKCS #7 structure + * for the provided data. Multiple signatures can be made with different + * signers. + * + * The available flags are: + * %GNUTLS_PKCS7_EMBED_DATA, %GNUTLS_PKCS7_INCLUDE_TIME, %GNUTLS_PKCS7_INCLUDE_CERT, + * and %GNUTLS_PKCS7_WRITE_SPKI. They are explained in the #gnutls_pkcs7_sign_flags + * definition. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.2 + **/ +int gnutls_pkcs7_sign(gnutls_pkcs7_t pkcs7, + gnutls_x509_crt_t signer, + gnutls_privkey_t signer_key, + const gnutls_datum_t *data, + gnutls_pkcs7_attrs_t signed_attrs, + gnutls_pkcs7_attrs_t unsigned_attrs, + gnutls_digest_algorithm_t dig, unsigned flags) +{ + int ret, result; + gnutls_datum_t sigdata = { NULL, 0 }; + gnutls_datum_t signature = { NULL, 0 }; + const mac_entry_st *me = hash_to_entry(dig); + unsigned pk, sigalgo; + gnutls_x509_spki_st key_params, params; + const gnutls_sign_entry_st *se; + + if (pkcs7 == NULL || me == NULL) + return GNUTLS_E_INVALID_REQUEST; + + if (pkcs7->signed_data == NULL) { + result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-7-SignedData", + &pkcs7->signed_data); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + if (!(flags & GNUTLS_PKCS7_EMBED_DATA)) { + (void)asn1_write_value(pkcs7->signed_data, + "encapContentInfo.eContent", NULL, 0); + } + } + + result = asn1_write_value(pkcs7->signed_data, "version", &one, 1); + if (result != ASN1_SUCCESS) { + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(pkcs7->signed_data, + "encapContentInfo.eContentType", DATA_OID, + 0); + if (result != ASN1_SUCCESS) { + ret = _gnutls_asn2err(result); + goto cleanup; + } + + if ((flags & GNUTLS_PKCS7_EMBED_DATA) && data->data) { /* embed data */ + ret = + _gnutls_x509_write_string(pkcs7->signed_data, + "encapContentInfo.eContent", data, + ASN1_ETYPE_OCTET_STRING); + if (ret < 0) { + goto cleanup; + } + } + + if (flags & GNUTLS_PKCS7_INCLUDE_CERT) { + ret = gnutls_pkcs7_set_crt(pkcs7, signer); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + /* append digest info algorithm */ + result = + asn1_write_value(pkcs7->signed_data, "digestAlgorithms", "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(pkcs7->signed_data, + "digestAlgorithms.?LAST.algorithm", + _gnutls_x509_digest_to_oid(me), 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + (void)asn1_write_value(pkcs7->signed_data, + "digestAlgorithms.?LAST.parameters", NULL, 0); + + /* append signer's info */ + result = asn1_write_value(pkcs7->signed_data, "signerInfos", "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(pkcs7->signed_data, "signerInfos.?LAST.version", + &one, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = + asn1_write_value(pkcs7->signed_data, + "signerInfos.?LAST.digestAlgorithm.algorithm", + _gnutls_x509_digest_to_oid(me), 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + (void)asn1_write_value(pkcs7->signed_data, + "signerInfos.?LAST.digestAlgorithm.parameters", NULL, + 0); + + ret = + write_signer_id(pkcs7->signed_data, "signerInfos.?LAST", signer, + flags); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + add_attrs(pkcs7->signed_data, "signerInfos.?LAST.unsignedAttrs", + unsigned_attrs, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + write_attributes(pkcs7->signed_data, + "signerInfos.?LAST.signedAttrs", data, me, + signed_attrs, flags); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + disable_opt_fields(pkcs7); + + /* write the signature algorithm */ + pk = gnutls_x509_crt_get_pk_algorithm(signer, NULL); + + ret = _gnutls_privkey_get_spki_params(signer_key, &key_params); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_crt_get_spki_params(signer, &key_params, ¶ms); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_privkey_update_spki_params(signer_key, pk, dig, 0, + ¶ms); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + se = _gnutls_pk_to_sign_entry(params.pk, dig); + if (se == NULL) { + ret = gnutls_assert_val(GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM); + goto cleanup; + } + + /* RFC5652 is silent on what the values would be and initially I assumed that + * typical signature algorithms should be set. However RFC2315 (PKCS#7) mentions + * that a generic RSA OID should be used. We switch to this "unexpected" value + * because some implementations cannot cope with the "expected" signature values. + */ + params.legacy = 1; + ret = + _gnutls_x509_write_sign_params(pkcs7->signed_data, + "signerInfos.?LAST.signatureAlgorithm", + se, ¶ms); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + sigalgo = se->id; + + /* sign the data */ + ret = + figure_pkcs7_sigdata(pkcs7, "signerInfos.?LAST", data, sigalgo, + &sigdata); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + FIX_SIGN_PARAMS(params, flags, dig); + + ret = privkey_sign_and_hash_data(signer_key, se, + &sigdata, &signature, ¶ms); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + result = + asn1_write_value(pkcs7->signed_data, "signerInfos.?LAST.signature", + signature.data, signature.size); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + ret = 0; + + cleanup: + gnutls_free(sigdata.data); + gnutls_free(signature.data); + return ret; +} |