/*
* 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
*
*/
/* Functions that relate on PKCS7 certificate lists parsing.
*/
#include "gnutls_int.h"
#include
#include
#include
#include "errors.h"
#include
#include
#include
#include
#define SIGNED_DATA_OID "1.2.840.113549.1.7.2"
#define PLAIN_DATA_OID "1.2.840.113549.1.7.1"
#define DIGESTED_DATA_OID "1.2.840.113549.1.7.5"
#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_TYPE,
* 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(ASN1_TYPE pkcs7, ASN1_TYPE * sdata)
{
char oid[MAX_OID_SIZE];
ASN1_TYPE c2;
gnutls_datum_t tmp = {NULL, 0};
int len, result;
len = sizeof(oid) - 1;
result = asn1_read_value(pkcs7, "contentType", oid, &len);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
if (strcmp(oid, SIGNED_DATA_OID) != 0) {
gnutls_assert();
_gnutls_debug_log("Unknown PKCS7 Content OID '%s'\n", 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, "content", &tmp);
if (result < 0) {
gnutls_assert();
goto cleanup;
}
/* tmp, tmp_size hold the data and the size of the CertificateSet structure
* actually the ANY stuff.
*/
/* 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 = sizeof(oid) - 1;
result = asn1_read_value(c2, "encapContentInfo.eContentType", oid, &len);
if (result != ASN1_SUCCESS) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto cleanup;
}
if (strcmp(oid, PLAIN_DATA_OID) != 0 && strcmp(oid, DIGESTED_DATA_OID) != 0) {
gnutls_assert();
_gnutls_debug_log("Unknown or unexpected PKCS7 Encapsulated Content OID '%s'\n", oid);
result = GNUTLS_E_UNKNOWN_PKCS_CONTENT_TYPE;
goto cleanup;
}
*sdata = c2;
gnutls_free(tmp.data);
return 0;
cleanup:
if (c2)
asn1_delete_structure(&c2);
gnutls_free(tmp.data);
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(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.
*
* 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->pkcs7, &pkcs7->signed_data);
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[ASN1_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_TYPE c2 = ASN1_TYPE_EMPTY;
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);
tmp.data = NULL;
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);
tmp.data = NULL;
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) {
ret = _gnutls_x509_read_value(pkcs7->signed_data, "encapContentInfo.eContent", &tmp);
if (ret < 0) {
if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
gnutls_assert();
return ret;
}
data = &tmp;
}
ret = gnutls_hash_fast(hash, data->data, data->size, hash_output);
gnutls_free(tmp.data);
tmp.data = NULL;
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 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);
tmp.data = NULL;
gnutls_free(tmp2.data);
tmp2.data = NULL;
}
if (msg_digest_ok)
ret = 0;
else
ret = gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
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) {
ret = _gnutls_x509_read_value(pkcs7->signed_data, "encapContentInfo.eContent", sigdata);
if (ret < 0) {
gnutls_assert();
return gnutls_assert_val(ret);
}
return 0;
}
return _gnutls_set_datum(sigdata, data->data, data->size);
}
/**
* gnutls_pkcs7_get_embedded_data:
* @pkcs7: should contain a gnutls_pkcs7_t type
* @idx: the index of the signature info to get the data from
* @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.
*
* Note, that since a PKCS#7 structure may contain embedded data
* for each attached signature, this function accepts and index which
* corresponds to the signature index to get the data from.
*
* 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 idx, gnutls_datum_t *data)
{
int count, ret;
gnutls_datum_t tmpdata = {NULL, 0};
gnutls_pkcs7_signature_info_st info;
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, NULL, info.algo, data);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
ret = 0;
cleanup:
gnutls_free(tmpdata.data);
gnutls_pkcs7_signature_info_deinit(&info);
return ret;
}
/**
* 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.
*
* 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;
}
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,
gnutls_pkcs7_signature_info_st *info)
{
gnutls_x509_crt_t issuer = NULL, crt = NULL;
int ret, count;
uint8_t serial[128];
size_t serial_size;
gnutls_datum_t tmp = {NULL, 0};
unsigned i, vtmp;
if (info->issuer_dn.data) {
ret = gnutls_x509_trust_list_get_issuer_by_dn(tl, &info->issuer_dn, &issuer, 0);
if (ret < 0) {
gnutls_assert();
issuer = NULL;
}
}
if (info->issuer_keyid.data && issuer == NULL) {
ret = gnutls_x509_trust_list_get_issuer_by_subject_key_id(tl, NULL, &info->issuer_keyid, &issuer, 0);
if (ret < 0) {
gnutls_assert();
issuer = NULL;
}
}
if (issuer == NULL) {
/* the issuer of the signer is not trusted. Too bad. */
return NULL;
}
/* check issuer's key purpose */
for (i=0;isigner_serial.data) {
serial_size = sizeof(serial);
ret = gnutls_x509_crt_get_serial(issuer, serial, &serial_size);
if (ret < 0) {
gnutls_assert();
goto fail;
}
if (serial_size == info->signer_serial.size && memcmp(info->signer_serial.data, serial, serial_size) == 0) {
/* issuer == signer */
return issuer;
}
}
count = gnutls_pkcs7_get_crt_count(pkcs7);
if (count < 0) {
gnutls_assert();
goto fail;
}
for (i=0;i<(unsigned)count;i++) {
/* Try to find the signer in the appended list. */
ret = gnutls_pkcs7_get_crt_raw2(pkcs7, 0, &tmp);
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, &tmp, GNUTLS_X509_FMT_DER);
if (ret < 0) {
gnutls_assert();
goto fail;
}
serial_size = sizeof(serial);
ret = gnutls_x509_crt_get_serial(crt, serial, &serial_size);
if (ret < 0) {
gnutls_assert();
goto fail;
}
if (serial_size != info->signer_serial.size || memcmp(info->signer_serial.data, serial, serial_size) != 0) {
gnutls_assert();
goto skip;
}
ret = gnutls_x509_trust_list_verify_crt2(tl, &crt, 1, vdata, vdata_size, 0, &vtmp, NULL);
if (ret < 0 || vtmp != 0) {
gnutls_assert(); /* maybe next one is trusted */
skip:
gnutls_x509_crt_deinit(crt);
crt = NULL;
gnutls_free(tmp.data);
tmp.data = NULL;
continue;
}
/* we found a signer we trust. let's return it */
break;
}
/* ok a trusted certificate was found. This is the signer */
goto cleanup;
fail:
if (crt != NULL) {
gnutls_x509_crt_deinit(crt);
crt = NULL;
}
cleanup:
gnutls_free(tmp.data);
if (issuer)
gnutls_x509_crt_deinit(issuer);
return crt;
}
/**
* 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, &info);
if (signer) {
ret = gnutls_x509_crt_verify_data2(signer, info.algo, flags, &sigdata, &info.sig);
if (ret < 0) {
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) {
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) {
asn1_write_value(pkcs7->signed_data, "certificates", NULL, 0);
}
return;
}
static int reencode(gnutls_pkcs7_t pkcs7)
{
int result;
if (pkcs7->signed_data != ASN1_TYPE_EMPTY) {
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_TYPE pkcs7, ASN1_TYPE * sdata)
{
int result;
*sdata = ASN1_TYPE_EMPTY;
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 == ASN1_TYPE_EMPTY) {
/* 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[ASN1_MAX_NAME_SIZE];
if (pkcs7 == NULL)
return GNUTLS_E_INVALID_REQUEST;
/* Step 2. Delete the certificate.
*/
snprintf(root2, sizeof(root2), "certificates.?%u", 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[ASN1_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;
}
*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 == ASN1_TYPE_EMPTY) {
/* 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[ASN1_MAX_NAME_SIZE];
if (pkcs7 == NULL)
return GNUTLS_E_INVALID_REQUEST;
/* Delete the crl.
*/
snprintf(root2, sizeof(root2), "crls.?%u", 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_TYPE 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);
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_TYPE 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)
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_TYPE 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 (result != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(result);
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;
}
already_set = 1;
/* If we add any attribute we should add them all */
/* Add hash */
digest_size = _gnutls_hash_get_algo_len(me);
ret = gnutls_hash_fast(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;
if (pkcs7 == NULL || me == NULL)
return GNUTLS_E_INVALID_REQUEST;
if (pkcs7->signed_data == ASN1_TYPE_EMPTY) {
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)) {
asn1_write_value(pkcs7->signed_data, "encapContentInfo.eContent", NULL, 0);
}
}
asn1_write_value(pkcs7->signed_data, "version", &one, 1);
result = asn1_write_value(pkcs7->signed_data, "encapContentInfo.eContentType", PLAIN_DATA_OID, 0);
if (result != ASN1_SUCCESS) {
ret = _gnutls_asn2err(result);
goto cleanup;
}
if (flags & GNUTLS_PKCS7_EMBED_DATA && data->data) { /* embed data */
result = asn1_write_value(pkcs7->signed_data, "encapContentInfo.eContent", data->data, data->size);
if (result != ASN1_SUCCESS) {
ret = _gnutls_asn2err(result);
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;
}
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;
}
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);
/* 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.
*/
ret = _gnutls_x509_write_sig_params(pkcs7->signed_data, "signerInfos.?LAST.signatureAlgorithm", pk, dig, 1);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
sigalgo = gnutls_pk_to_sign(pk, dig);
if (sigalgo == GNUTLS_SIGN_UNKNOWN) {
gnutls_assert();
ret = GNUTLS_E_INVALID_REQUEST;
goto cleanup;
}
/* sign the data */
ret = figure_pkcs7_sigdata(pkcs7, "signerInfos.?LAST", data, sigalgo, &sigdata);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
ret = gnutls_privkey_sign_data(signer_key, dig, 0, &sigdata, &signature);
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;
}