diff options
-rw-r--r-- | lib/includes/gnutls/pkcs7.h | 11 | ||||
-rw-r--r-- | lib/libgnutls.map | 3 | ||||
-rw-r--r-- | lib/pkix.asn | 2 | ||||
-rw-r--r-- | lib/pkix_asn1_tab.c | 2 | ||||
-rw-r--r-- | lib/x509/Makefile.am | 1 | ||||
-rw-r--r-- | lib/x509/pkcs7-attrs.c | 156 | ||||
-rw-r--r-- | lib/x509/pkcs7.c | 160 | ||||
-rw-r--r-- | lib/x509/x509_int.h | 6 |
8 files changed, 318 insertions, 23 deletions
diff --git a/lib/includes/gnutls/pkcs7.h b/lib/includes/gnutls/pkcs7.h index 8fb0968273..dd9f2d5304 100644 --- a/lib/includes/gnutls/pkcs7.h +++ b/lib/includes/gnutls/pkcs7.h @@ -29,6 +29,7 @@ #define GNUTLS_PKCS7_H #include <gnutls/gnutls.h> +#include <gnutls/x509.h> /* *INDENT-OFF* */ #ifdef __cplusplus @@ -71,6 +72,7 @@ int gnutls_pkcs7_set_crl_raw(gnutls_pkcs7_t pkcs7, int gnutls_pkcs7_set_crl(gnutls_pkcs7_t pkcs7, gnutls_x509_crl_t crl); int gnutls_pkcs7_delete_crl(gnutls_pkcs7_t pkcs7, int indx); +typedef struct gnutls_pkcs7_attrs_st *gnutls_pkcs7_attrs_t; typedef struct gnutls_pkcs7_signature_info_st { gnutls_sign_algorithm_t algo; @@ -79,6 +81,8 @@ typedef struct gnutls_pkcs7_signature_info_st { gnutls_datum_t signer_serial; gnutls_datum_t issuer_keyid; time_t signing_time; + gnutls_pkcs7_attrs_t signed_attrs; + gnutls_pkcs7_attrs_t unsigned_attrs; char pad[64]; } gnutls_pkcs7_signature_info_st; @@ -91,6 +95,11 @@ 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); +#define GNUTLS_PKCS7_ATTR_ENCODE_OCTET_STRING 1 +int gnutls_pkcs7_add_attr(gnutls_pkcs7_attrs_t *list, const char *oid, gnutls_datum_t *data, unsigned flags); +void gnutls_pkcs7_attrs_deinit(gnutls_pkcs7_attrs_t list); +int gnutls_pkcs7_get_attr(gnutls_pkcs7_attrs_t list, unsigned idx, char **oid, gnutls_datum_t *data, unsigned flags); + #define GNUTLS_PKCS7_EMBED_DATA 1 #define GNUTLS_PKCS7_INCLUDE_TIME (1<<1) #define GNUTLS_PKCS7_INCLUDE_CERT (1<<2) @@ -99,6 +108,8 @@ 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); diff --git a/lib/libgnutls.map b/lib/libgnutls.map index be885ac0c7..b0ae35ceec 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1034,6 +1034,9 @@ GNUTLS_3_4 gnutls_pkcs7_verify; gnutls_pkcs7_get_crl_raw2; gnutls_pkcs7_sign; + gnutls_pkcs7_attrs_deinit; + gnutls_pkcs7_add_attr; + gnutls_pkcs7_get_attr; local: *; }; diff --git a/lib/pkix.asn b/lib/pkix.asn index 41ad3ce651..05e3b23041 100644 --- a/lib/pkix.asn +++ b/lib/pkix.asn @@ -378,7 +378,7 @@ pkcs-7-SignerInfo ::= SEQUENCE { signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL, signatureAlgorithm AlgorithmIdentifier, signature OCTET STRING, - unsignedAttrs [1] IMPLICIT ANY OPTIONAL } + unsignedAttrs [1] IMPLICIT SignedAttributes OPTIONAL } SignedAttributes ::= SET SIZE (1..MAX) OF Attribute diff --git a/lib/pkix_asn1_tab.c b/lib/pkix_asn1_tab.c index 2abf2f6c87..5380cc00f6 100644 --- a/lib/pkix_asn1_tab.c +++ b/lib/pkix_asn1_tab.c @@ -266,7 +266,7 @@ const asn1_static_node pkix_asn1_tab[] = { { NULL, 4104, "0"}, { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, { "signature", 1073741831, NULL }, - { "unsignedAttrs", 536895501, NULL }, + { "unsignedAttrs", 536895490, "SignedAttributes"}, { NULL, 4104, "1"}, { "SignedAttributes", 1612709903, NULL }, { "MAX", 1074266122, "1"}, diff --git a/lib/x509/Makefile.am b/lib/x509/Makefile.am index 4da0beb180..b8249a579a 100644 --- a/lib/x509/Makefile.am +++ b/lib/x509/Makefile.am @@ -46,6 +46,7 @@ libgnutls_x509_la_SOURCES = \ pkcs12_bag.c \ pkcs12_encr.c \ pkcs7.c \ + pkcs7-attrs.c \ privkey.c \ privkey_pkcs8.c \ privkey_openssl.c \ diff --git a/lib/x509/pkcs7-attrs.c b/lib/x509/pkcs7-attrs.c new file mode 100644 index 0000000000..bc8a84593d --- /dev/null +++ b/lib/x509/pkcs7-attrs.c @@ -0,0 +1,156 @@ +/* + * 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 <http://www.gnu.org/licenses/> + * + */ + +/* Functions that relate on PKCS7 attribute setting. + */ + +#include <gnutls_int.h> + +#include <gnutls_datum.h> +#include <gnutls_global.h> +#include <gnutls_errors.h> +#include <common.h> +#include <x509_b64.h> +#include <gnutls/abstract.h> +#include <gnutls/pkcs7.h> + +/** + * gnutls_pkcs7_add_attr: + * @list: A list of existing attributes or pointer to %NULL for the first one + * @oid: the OID of the attribute to be set + * @data: the raw (DER-encoded) data of the attribute to be set + * @flags: zero or %GNUTLS_PKCS7_ATTR_ENCODE_OCTET_STRING + * + * This function will set a PKCS #7 attribute in the provided list. + * If this function fails, the previous list would be deallocated. + * + * Returns: On success, the new list head, otherwise %NULL. + * + * Since: 3.4.2 + **/ +int +gnutls_pkcs7_add_attr(gnutls_pkcs7_attrs_t *list, const char *oid, gnutls_datum_t *data, unsigned flags) +{ + int ret; + gnutls_pkcs7_attrs_st *r; + + r = gnutls_calloc(1, sizeof(gnutls_pkcs7_attrs_st)); + if (r == NULL) + goto fail; + + if (flags & GNUTLS_PKCS7_ATTR_ENCODE_OCTET_STRING) { + ret = _gnutls_x509_encode_string(ASN1_ETYPE_OCTET_STRING, + data->data, data->size, &r->data); + } else { + ret = _gnutls_set_datum(&r->data, data->data, data->size); + } + if (ret < 0) + goto fail; + + r->oid = gnutls_strdup(oid); + if (r->oid == NULL) + goto fail; + + r->next = *list; + *list = r; + + return 0; + fail: + if (r) { + gnutls_free(r->data.data); + gnutls_free(r); + } + gnutls_pkcs7_attrs_deinit(*list); + return GNUTLS_E_MEMORY_ERROR; + +} + +/** + * gnutls_pkcs7_get_attr: + * @list: A list of existing attributes or %NULL for the first one + * @idx: the index of the attribute to get + * @oid: the OID of the attribute (read-only) + * @data: the raw data of the attribute + * @flags: zero or %GNUTLS_PKCS7_ATTR_ENCODE_OCTET_STRING + * + * This function will get a PKCS #7 attribute from the provided list. + * The OID is a constant string, but data will be allocated and must be + * deinitialized by the caller. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned + * if there are no data in the current index. + * + * Since: 3.4.2 + **/ +int +gnutls_pkcs7_get_attr(gnutls_pkcs7_attrs_t list, unsigned idx, char **oid, gnutls_datum_t *data, unsigned flags) +{ + unsigned i; + gnutls_pkcs7_attrs_st *p = list; + int ret; + + for (i=0;i<idx;i++) { + p = p->next; + if (p == NULL) + break; + } + + if (p == NULL) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + *oid = p->oid; + + if (flags & GNUTLS_PKCS7_ATTR_ENCODE_OCTET_STRING) { + ret = _gnutls_x509_decode_string(ASN1_ETYPE_OCTET_STRING, + p->data.data, p->data.size, data, 1); + } else { + ret = _gnutls_set_datum(data, p->data.data, p->data.size); + } + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; +} + +/** + * gnutls_pkcs7_attrs_deinit: + * @list: A list of existing attributes + * + * This function will clear a PKCS #7 attribute list. + * + * Since: 3.4.2 + **/ +void +gnutls_pkcs7_attrs_deinit(gnutls_pkcs7_attrs_t list) +{ + gnutls_pkcs7_attrs_st *r = list, *next; + + while(r) { + next = r->next; + + gnutls_free(r->data.data); + gnutls_free(r->oid); + gnutls_free(r); + r = next; + } +} diff --git a/lib/x509/pkcs7.c b/lib/x509/pkcs7.c index be4f3b8a2e..bc3211996c 100644 --- a/lib/x509/pkcs7.c +++ b/lib/x509/pkcs7.c @@ -443,6 +443,8 @@ void gnutls_pkcs7_signature_info_deinit(gnutls_pkcs7_signature_info_st *info) 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)); } @@ -504,6 +506,7 @@ int gnutls_pkcs7_get_signature_info(gnutls_pkcs7_t pkcs7, unsigned idx, gnutls_p 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) @@ -603,13 +606,62 @@ int gnutls_pkcs7_get_signature_info(gnutls_pkcs7_t pkcs7, unsigned idx, gnutls_p 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) { - snprintf(root, sizeof(root), "signerInfos.?%u.signedAttrs.?%u.values.?1", idx+1, i+1); 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: @@ -1669,13 +1721,62 @@ static int write_signer_id(ASN1_TYPE c2, const char *root, gnutls_x509_crt_t sig 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.?LAST", 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, unsigned flags) + 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]; unsigned digest_size; + unsigned already_set = 0; if (flags & GNUTLS_PKCS7_INCLUDE_TIME) { if (data == NULL || data->data == NULL) { @@ -1683,23 +1784,15 @@ static int write_attributes(ASN1_TYPE c2, const char *root, const gnutls_datum_t return GNUTLS_E_INVALID_REQUEST; } - 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; - } - /* Add time */ - snprintf(name, sizeof(name), "%s.signedAttrs", root); - result = asn1_write_value(c2, name, "NEW", 1); + 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.signedAttrs.?LAST.type", root); + snprintf(name, sizeof(name), "%s.?LAST.type", root); result = asn1_write_value(c2, name, ATTR_SIGNING_TIME, 1); if (result != ASN1_SUCCESS) { @@ -1708,7 +1801,7 @@ static int write_attributes(ASN1_TYPE c2, const char *root, const gnutls_datum_t return ret; } - snprintf(name, sizeof(name), "%s.signedAttrs.?LAST.value", root); + snprintf(name, sizeof(name), "%s.?LAST.value", root); result = asn1_write_value(c2, name, "NEW", 1); if (result != ASN1_SUCCESS) { gnutls_assert(); @@ -1716,7 +1809,7 @@ static int write_attributes(ASN1_TYPE c2, const char *root, const gnutls_datum_t return ret; } - snprintf(name, sizeof(name), "%s.signedAttrs.?LAST.value.?LAST", root); + snprintf(name, sizeof(name), "%s.?LAST.value.?LAST", root); ret = _gnutls_x509_set_time(c2, name, gnutls_time(0), 1); if (result != ASN1_SUCCESS) { gnutls_assert(); @@ -1724,26 +1817,43 @@ static int write_attributes(ASN1_TYPE c2, const char *root, const gnutls_datum_t 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) { /* If we add any attribute we should add them all */ /* Add hash */ - snprintf(name, sizeof(name), "%s.signedAttrs", root); - result = asn1_write_value(c2, name, "NEW", 1); + + 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, "%s.signedAttrs.?LAST", + c2, name, digest, digest_size, 1); if (ret < 0) { gnutls_assert(); return ret; } - } else { - asn1_write_value(c2, "signerInfos.?LAST.signedAttrs", NULL, 0); } + return 0; } @@ -1752,6 +1862,8 @@ static int write_attributes(ASN1_TYPE c2, const char *root, const gnutls_datum_t * @pkcs7: should contain a #gnutls_pkcs7_t type * @signer: the certificate believe to have signed 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 * @@ -1774,6 +1886,8 @@ 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) { @@ -1871,9 +1985,13 @@ int gnutls_pkcs7_sign(gnutls_pkcs7_t pkcs7, goto cleanup; } - asn1_write_value(pkcs7->signed_data, "signerInfos.?LAST.unsignedAttrs", NULL, 0); + 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", data, me, flags); + ret = write_attributes(pkcs7->signed_data, "signerInfos.?LAST.signedAttrs", data, me, signed_attrs, flags); if (ret < 0) { gnutls_assert(); goto cleanup; diff --git a/lib/x509/x509_int.h b/lib/x509/x509_int.h index bf7b20fbfa..e8272c79a4 100644 --- a/lib/x509/x509_int.h +++ b/lib/x509/x509_int.h @@ -89,6 +89,12 @@ typedef struct gnutls_x509_crq_int { ASN1_TYPE crq; } gnutls_x509_crq_int; +typedef struct gnutls_pkcs7_attrs_st { + char *oid; + gnutls_datum_t data; + struct gnutls_pkcs7_attrs_st *next; +} gnutls_pkcs7_attrs_st; + typedef struct gnutls_pkcs7_int { ASN1_TYPE pkcs7; ASN1_TYPE signed_data; |