]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Added code to parse and set PKCS #7 attributes
authorNikos Mavrogiannopoulos <nmav@redhat.com>
Wed, 3 Jun 2015 08:24:05 +0000 (10:24 +0200)
committerNikos Mavrogiannopoulos <nmav@redhat.com>
Wed, 3 Jun 2015 09:36:02 +0000 (11:36 +0200)
lib/includes/gnutls/pkcs7.h
lib/libgnutls.map
lib/pkix.asn
lib/pkix_asn1_tab.c
lib/x509/Makefile.am
lib/x509/pkcs7-attrs.c [new file with mode: 0644]
lib/x509/pkcs7.c
lib/x509/x509_int.h

index 8fb09682730d53967ca93f88bb08169a80b40fce..dd9f2d53046c74be32458c1618113d09066f8b08 100644 (file)
@@ -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);
 
index be885ac0c7a5c52310c26d4f122b027d283c1e61..b0ae35ceec1b64ab6d06592cf46aa06b64b049b9 100644 (file)
@@ -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:
        *;
 };
index 41ad3ce651f4f887d51477fd7ac5ef700fa08d90..05e3b2304145e2254e0aa3b1f2813c2fa545338f 100644 (file)
@@ -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
 
index 2abf2f6c87966db5ecef0fcda7e3796e246337d6..5380cc00f6621ff9a56755799dd70ffd8b92311f 100644 (file)
@@ -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"},
index 4da0beb1807846a2ee1a82750bcaae7aeec6b411..b8249a579a5a0cf3147a8abaeb5c3efee838933a 100644 (file)
@@ -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 (file)
index 0000000..bc8a845
--- /dev/null
@@ -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;
+       }
+}
index be4f3b8a2ecdc6042858167558e32007a570e814..bc3211996c4277d179f06f00c68cda82b09cff64 100644 (file)
@@ -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;
index bf7b20fbfa4a7a52f66e660a3a548f149f14c583..e8272c79a416f9d7bd34030c3e42180b5670bc11 100644 (file)
@@ -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;