summaryrefslogtreecommitdiff
path: root/lib/x509/pkcs7-digest.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/x509/pkcs7-digest.c')
-rw-r--r--lib/x509/pkcs7-digest.c340
1 files changed, 340 insertions, 0 deletions
diff --git a/lib/x509/pkcs7-digest.c b/lib/x509/pkcs7-digest.c
new file mode 100644
index 0000000000..c68b2c628e
--- /dev/null
+++ b/lib/x509/pkcs7-digest.c
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2003-2015 Free Software Foundation, Inc.
+ * Copyright (C) 2015 Red Hat, Inc.
+ * Copyright (C) 2020 Dmitry Baryshkov
+ *
+ * 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 <common.h>
+#include <pkcs7_int.h>
+#include <gnutls/pkcs7.h>
+
+/* Decodes the PKCS #7 digested data, and returns an asn1_node,
+ * which holds them
+ */
+int _gnutls_pkcs7_decode_digested_data(gnutls_pkcs7_t pkcs7)
+{
+ asn1_node c2;
+ int len, result;
+ gnutls_datum_t tmp = {NULL, 0};
+
+ if ((result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.pkcs-7-DigestedData",
+ &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* the Digested-data has been created, so
+ * decode them.
+ */
+ result = _gnutls_x509_read_value(pkcs7->pkcs7, "content", &tmp);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ 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) {
+ _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_encap_data, ASN1_ETYPE_OCTET_STRING, 1);
+ if (result < 0) {
+ result = _gnutls_x509_read_value(c2, "encapContentInfo.eContent", &pkcs7->der_encap_data);
+ if (result < 0) {
+ pkcs7->der_encap_data.data = NULL;
+ pkcs7->der_encap_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_encap_data.data, pkcs7->der_encap_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_encap_data.data+tag_len, pkcs7->der_encap_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_encap_data.data, &pkcs7->der_encap_data.data[tag_len], pkcs7->der_encap_data.size-tag_len);
+ pkcs7->der_encap_data.size-=tag_len;
+ }
+ }
+
+ pkcs7->content_data = c2;
+ gnutls_free(tmp.data);
+
+ return 0;
+
+ cleanup:
+ gnutls_free(tmp.data);
+ if (c2)
+ asn1_delete_structure(&c2);
+ return result;
+}
+
+/**
+ * gnutls_pkcs7_get_digest_algo:
+ * @pkcs7: should contain a #gnutls_pkcs7_t type
+ *
+ * This function will return digest algorithm used
+ * in the DigestedData of the PKCS #7 structure.
+ *
+ * Returns: On success, @gnutls_digest_algorithm_t value is returned, otherwise
+ * a negative error value.
+ *
+ * Since: 3.7.0
+ **/
+int gnutls_pkcs7_get_digest_algo(gnutls_pkcs7_t pkcs7)
+{
+ int len, ret;
+ char oid[MAX_OID_SIZE];
+ gnutls_digest_algorithm_t dig;
+
+ if (pkcs7 == NULL || pkcs7->type != GNUTLS_PKCS7_DIGESTED)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ len = sizeof(oid) - 1;
+ ret = asn1_read_value(pkcs7->content_data, "digestAlgorithm.algorithm", oid, &len);
+ if (ret != ASN1_SUCCESS)
+ return gnutls_assert_val(GNUTLS_E_UNKNOWN_ALGORITHM);
+
+ dig = gnutls_oid_to_digest(oid);
+ if (dig == GNUTLS_DIG_UNKNOWN)
+ return gnutls_assert_val(GNUTLS_E_UNKNOWN_ALGORITHM);
+
+ return dig;
+}
+
+/**
+ * gnutls_pkcs7_verify_digest:
+ * @pkcs7: should contain a #gnutls_pkcs7_t type
+ * @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 digest
+ * present in the DigestedData 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_HASH_FAILED and the lack of encapsulated data
+ * to verify to a %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE.
+ *
+ * Since: 3.7.0
+ **/
+int gnutls_pkcs7_verify_digest(gnutls_pkcs7_t pkcs7,
+ const gnutls_datum_t *data, unsigned flags)
+{
+ int len, ret;
+ gnutls_datum_t tmpdata = { NULL, 0 };
+ char oid[MAX_OID_SIZE];
+ uint8_t hash_output[MAX_HASH_SIZE];
+ gnutls_digest_algorithm_t dig;
+
+ if (pkcs7 == NULL || pkcs7->type != GNUTLS_PKCS7_DIGESTED)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ len = sizeof(oid) - 1;
+ ret = asn1_read_value(pkcs7->content_data, "digestAlgorithm.algorithm", oid, &len);
+ if (ret != ASN1_SUCCESS)
+ return gnutls_assert_val(GNUTLS_E_UNKNOWN_ALGORITHM);
+
+ dig = gnutls_oid_to_digest(oid);
+ if (dig == GNUTLS_DIG_UNKNOWN)
+ return gnutls_assert_val(GNUTLS_E_UNKNOWN_ALGORITHM);
+
+ if (_gnutls_digest_is_insecure(dig) &&
+ !(flags & GNUTLS_VERIFY_ALLOW_BROKEN))
+ return gnutls_assert_val(GNUTLS_E_HASH_FAILED);
+
+ if (data == NULL || data->data == NULL)
+ ret = gnutls_hash_fast(dig, pkcs7->der_encap_data.data, pkcs7->der_encap_data.size, hash_output);
+ else
+ ret = gnutls_hash_fast(dig, data->data, data->size, hash_output);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_x509_read_value(pkcs7->content_data, "digest", &tmpdata);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (tmpdata.size != gnutls_hash_get_len(dig) ||
+ memcmp(tmpdata.data, hash_output, tmpdata.size)) {
+ ret = gnutls_assert_val(GNUTLS_E_HASH_FAILED);
+ }
+ _gnutls_free_datum(&tmpdata);
+
+ return ret;
+}
+
+/**
+ * gnutls_pkcs7_digest:
+ * @pkcs7: should contain a #gnutls_pkcs7_t type
+ * @data: The data to be signed or %NULL if the data are already embedded
+ * @dig: The digest algorithm to use for digesting
+ * @flags: Should be zero or one of %GNUTLS_PKCS7 flags
+ *
+ * This function will add a digest in the provided PKCS #7 structure
+ * for the provided data.
+ *
+ * The available flags are:
+ * %GNUTLS_PKCS7_EMBED_DATA. It is explained in the #gnutls_pkcs7_sign_flags
+ * definition.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.7.0
+ **/
+int gnutls_pkcs7_digest(gnutls_pkcs7_t pkcs7,
+ const gnutls_datum_t *data,
+ 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);
+ uint8_t hash_output[MAX_HASH_SIZE];
+ uint8_t ver;
+
+ if (pkcs7 == NULL || me == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (pkcs7->type != GNUTLS_PKCS7_DIGESTED)
+ asn1_delete_structure(&pkcs7->content_data);
+
+ if (pkcs7->content_data == NULL) {
+ result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-7-DigestedData",
+ &pkcs7->content_data);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (!(flags & GNUTLS_PKCS7_EMBED_DATA)) {
+ (void)asn1_write_value(pkcs7->content_data,
+ "encapContentInfo.eContent", NULL, 0);
+ }
+ pkcs7->type = GNUTLS_PKCS7_DIGESTED;
+ }
+
+ result = asn1_write_value(pkcs7->content_data, "version", &ver, 1);
+ if (result != ASN1_SUCCESS) {
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_write_value(pkcs7->content_data,
+ "encapContentInfo.eContentType", DATA_OID,
+ 0);
+ if (result != ASN1_SUCCESS) {
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ ver = 0; /* Change to 2 if eContentType is not id-data */
+
+ if ((flags & GNUTLS_PKCS7_EMBED_DATA) && data->data) { /* embed data */
+ ret =
+ _gnutls_x509_write_string(pkcs7->content_data,
+ "encapContentInfo.eContent", data,
+ ASN1_ETYPE_OCTET_STRING);
+ if (ret < 0) {
+ goto cleanup;
+ }
+ }
+
+ /* append digest info algorithm */
+ asn1_write_value(pkcs7->content_data,
+ "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->content_data,
+ "digestAlgorithm.parameters", NULL, 0);
+
+ if (data == NULL || data->data == NULL)
+ ret = gnutls_hash_fast(dig, pkcs7->der_encap_data.data, pkcs7->der_encap_data.size, hash_output);
+ else
+ ret = gnutls_hash_fast(dig, data->data, data->size, hash_output);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ result = asn1_write_value(pkcs7->content_data, "digest", hash_output, me->output_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;
+}