summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2015-06-03 10:24:05 +0200
committerNikos Mavrogiannopoulos <nmav@redhat.com>2015-06-03 11:36:02 +0200
commite5d851f302b5ec1d1bfbfe7637225334f3f30895 (patch)
tree8f1e78eb4156105a6fe43e4d6bf4ed28fe2b3b47
parent8088df5425a68642e70ea41d299e7c915a463056 (diff)
downloadgnutls-e5d851f302b5ec1d1bfbfe7637225334f3f30895.tar.gz
Added code to parse and set PKCS #7 attributes
-rw-r--r--lib/includes/gnutls/pkcs7.h11
-rw-r--r--lib/libgnutls.map3
-rw-r--r--lib/pkix.asn2
-rw-r--r--lib/pkix_asn1_tab.c2
-rw-r--r--lib/x509/Makefile.am1
-rw-r--r--lib/x509/pkcs7-attrs.c156
-rw-r--r--lib/x509/pkcs7.c160
-rw-r--r--lib/x509/x509_int.h6
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;