]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
pkcs7: add support for EncryptedData
authorDmitry Baryshkov <dbaryshkov@gmail.com>
Mon, 15 Jun 2020 09:54:42 +0000 (12:54 +0300)
committerDmitry Baryshkov <dmitry.baryshkov@linaro.org>
Sun, 11 Sep 2022 14:54:58 +0000 (17:54 +0300)
Add support for parsing, decrypting and encrypting EncryptedData
PKCS7/CMS files.

Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
14 files changed:
devel/symbols.last
doc/Makefile.am
doc/manpages/Makefile.am
lib/includes/gnutls/pkcs7.h
lib/libgnutls.map
lib/x509/Makefile.am
lib/x509/pkcs12.c
lib/x509/pkcs7-encrypted.c [new file with mode: 0644]
lib/x509/pkcs7-output.c
lib/x509/pkcs7.c
lib/x509/pkcs7_int.h
lib/x509/x509_int.h
src/cmstool-options.json
src/cmstool.c

index 6fb2aa28abe626a976fd6f3d6d464fe1dc4c2ee7..86c3ac988807baddd874c60c0babfbc547d035b5 100644 (file)
@@ -574,10 +574,13 @@ gnutls_pkcs12_simple_parse@GNUTLS_3_4
 gnutls_pkcs12_verify_mac@GNUTLS_3_4
 gnutls_pkcs7_add_attr@GNUTLS_3_4
 gnutls_pkcs7_attrs_deinit@GNUTLS_3_4
+gnutls_pkcs7_decrypt@GNUTLS_3_7_0
 gnutls_pkcs7_deinit@GNUTLS_3_4
 gnutls_pkcs7_delete_crl@GNUTLS_3_4
 gnutls_pkcs7_delete_crt@GNUTLS_3_4
 gnutls_pkcs7_digest@GNUTLS_3_7_0
+gnutls_pkcs7_encrypt@GNUTLS_3_7_0
+gnutls_pkcs7_encryption_info_deinit@GNUTLS_3_7_0
 gnutls_pkcs7_export2@GNUTLS_3_4
 gnutls_pkcs7_export@GNUTLS_3_4
 gnutls_pkcs7_get_attr@GNUTLS_3_4
@@ -590,6 +593,7 @@ gnutls_pkcs7_get_crt_raw@GNUTLS_3_4
 gnutls_pkcs7_get_digest_algo@GNUTLS_3_7_0
 gnutls_pkcs7_get_embedded_data@GNUTLS_3_4
 gnutls_pkcs7_get_embedded_data_oid@GNUTLS_3_4
+gnutls_pkcs7_get_encryption_info@GNUTLS_3_7_0
 gnutls_pkcs7_get_signature_count@GNUTLS_3_4
 gnutls_pkcs7_get_signature_info@GNUTLS_3_4
 gnutls_pkcs7_import@GNUTLS_3_4
index 39be1bef08689ea099269c76261c68784b26dfa5..cf096f08898e43456cca9ea57350baaa3f2f55fd 100644 (file)
@@ -1579,6 +1579,8 @@ FUNCS += functions/gnutls_pkcs7_add_attr
 FUNCS += functions/gnutls_pkcs7_add_attr.short
 FUNCS += functions/gnutls_pkcs7_attrs_deinit
 FUNCS += functions/gnutls_pkcs7_attrs_deinit.short
+FUNCS += functions/gnutls_pkcs7_decrypt
+FUNCS += functions/gnutls_pkcs7_decrypt.short
 FUNCS += functions/gnutls_pkcs7_deinit
 FUNCS += functions/gnutls_pkcs7_deinit.short
 FUNCS += functions/gnutls_pkcs7_delete_crl
@@ -1587,6 +1589,10 @@ FUNCS += functions/gnutls_pkcs7_delete_crt
 FUNCS += functions/gnutls_pkcs7_delete_crt.short
 FUNCS += functions/gnutls_pkcs7_digest
 FUNCS += functions/gnutls_pkcs7_digest.short
+FUNCS += functions/gnutls_pkcs7_encrypt
+FUNCS += functions/gnutls_pkcs7_encrypt.short
+FUNCS += functions/gnutls_pkcs7_encryption_info_deinit
+FUNCS += functions/gnutls_pkcs7_encryption_info_deinit.short
 FUNCS += functions/gnutls_pkcs7_export
 FUNCS += functions/gnutls_pkcs7_export.short
 FUNCS += functions/gnutls_pkcs7_export2
@@ -1611,6 +1617,8 @@ FUNCS += functions/gnutls_pkcs7_get_embedded_data
 FUNCS += functions/gnutls_pkcs7_get_embedded_data.short
 FUNCS += functions/gnutls_pkcs7_get_embedded_data_oid
 FUNCS += functions/gnutls_pkcs7_get_embedded_data_oid.short
+FUNCS += functions/gnutls_pkcs7_get_encryption_info
+FUNCS += functions/gnutls_pkcs7_get_encryption_info.short
 FUNCS += functions/gnutls_pkcs7_get_signature_count
 FUNCS += functions/gnutls_pkcs7_get_signature_count.short
 FUNCS += functions/gnutls_pkcs7_get_signature_info
index c216bf3cf2f8b8d625507d36ba42a2e52062279d..c6196b78a3ad256e1b4b7bbb7f929c4aeb54a41c 100644 (file)
@@ -630,10 +630,13 @@ APIMANS += gnutls_pkcs12_simple_parse.3
 APIMANS += gnutls_pkcs12_verify_mac.3
 APIMANS += gnutls_pkcs7_add_attr.3
 APIMANS += gnutls_pkcs7_attrs_deinit.3
+APIMANS += gnutls_pkcs7_decrypt.3
 APIMANS += gnutls_pkcs7_deinit.3
 APIMANS += gnutls_pkcs7_delete_crl.3
 APIMANS += gnutls_pkcs7_delete_crt.3
 APIMANS += gnutls_pkcs7_digest.3
+APIMANS += gnutls_pkcs7_encrypt.3
+APIMANS += gnutls_pkcs7_encryption_info_deinit.3
 APIMANS += gnutls_pkcs7_export.3
 APIMANS += gnutls_pkcs7_export2.3
 APIMANS += gnutls_pkcs7_get_attr.3
@@ -646,6 +649,7 @@ APIMANS += gnutls_pkcs7_get_crt_raw2.3
 APIMANS += gnutls_pkcs7_get_digest_algo.3
 APIMANS += gnutls_pkcs7_get_embedded_data.3
 APIMANS += gnutls_pkcs7_get_embedded_data_oid.3
+APIMANS += gnutls_pkcs7_get_encryption_info.3
 APIMANS += gnutls_pkcs7_get_signature_count.3
 APIMANS += gnutls_pkcs7_get_signature_info.3
 APIMANS += gnutls_pkcs7_import.3
index e40dac23dda6f5341303b5ecdf0e7294f7806667..08081fa8a811b99bfe44a6a9e6528c7aeadfe2b5 100644 (file)
@@ -94,6 +94,13 @@ typedef struct gnutls_pkcs7_signature_info_st {
        char pad[64];
 } gnutls_pkcs7_signature_info_st;
 
+typedef struct gnutls_pkcs7_encryption_info_st {
+       const char *enc_oid;
+       gnutls_datum_t enc_params;
+
+       gnutls_pkcs7_attrs_t unprotected_attrs;
+} gnutls_pkcs7_encryption_info_t;
+
 void gnutls_pkcs7_signature_info_deinit(gnutls_pkcs7_signature_info_st *info);
 int gnutls_pkcs7_get_signature_info(gnutls_pkcs7_t pkcs7, unsigned idx, gnutls_pkcs7_signature_info_st *info);
 
@@ -148,6 +155,17 @@ int gnutls_pkcs7_verify_digest(gnutls_pkcs7_t pkcs7,
                               const gnutls_datum_t *data, unsigned flags);
 int gnutls_pkcs7_get_digest_algo(gnutls_pkcs7_t pkcs7);
 
+void gnutls_pkcs7_encryption_info_deinit(gnutls_pkcs7_encryption_info_t *info);
+int gnutls_pkcs7_get_encryption_info(gnutls_pkcs7_t pkcs7, gnutls_pkcs7_encryption_info_t *info);
+
+int gnutls_pkcs7_encrypt(gnutls_pkcs7_t pkcs7,
+                        gnutls_cipher_algorithm_t cipher,
+                        const gnutls_datum_t *key,
+                        const gnutls_datum_t *data,
+                        gnutls_pkcs7_attrs_t unsigned_attrs,
+                        unsigned int flags);
+int gnutls_pkcs7_decrypt(gnutls_pkcs7_t pkcs7, const gnutls_datum_t *key, gnutls_datum_t *out);
+
 int gnutls_pkcs7_print(gnutls_pkcs7_t pkcs7,
                       gnutls_certificate_print_formats_t format,
                       gnutls_datum_t * out);
index 5ccdebf161d99be86ccfe59dff2ffcd2ea1d3c50..7801cd0c51053d5f47e48766eec4fcab72680971 100644 (file)
@@ -1339,8 +1339,12 @@ GNUTLS_3_7_0
        gnutls_handshake_set_secret_function;
        gnutls_handshake_write;
        gnutls_oid_to_cipher;
+       gnutls_pkcs7_decrypt;
        gnutls_pkcs7_digest;
+       gnutls_pkcs7_encrypt;
+       gnutls_pkcs7_encryption_info_deinit;
        gnutls_pkcs7_get_digest_algo;
+       gnutls_pkcs7_get_encryption_info;
        gnutls_pkcs7_verify_digest;
        gnutls_x509_trust_list_set_getissuer_function;
        gnutls_x509_trust_list_get_ptr;
index 1dcbd93f7d42bd1116c1dfa00403678c717a0605..1b7ac9f71ff4bb3fb537031190c7c7e1fbca6a9e 100644 (file)
@@ -56,6 +56,7 @@ libgnutls_x509_la_SOURCES =   \
        pkcs7-attrs.c           \
        pkcs7-crypt.c pkcs7_int.h \
        pkcs7-digest.c          \
+       pkcs7-encrypted.c       \
        pkcs7-sign.c            \
        privkey.c               \
        privkey_pkcs8.c         \
index 11b9da3ac98594b8b55e63c7ade85f5788ae9b02..039da05e538e3df2e1c1e46105cec66a6572dfac 100644 (file)
@@ -672,7 +672,7 @@ gnutls_pkcs12_get_bag(gnutls_pkcs12_t pkcs12,
                goto cleanup;
        }
 
-       /* ENC_DATA_OID needs decryption */
+       /* ENCRYPED_DATA_OID needs decryption */
 
        result = _gnutls_x509_read_value(c2, root2, &bag->element[0].data);
        if (result < 0) {
@@ -813,7 +813,7 @@ int gnutls_pkcs12_set_bag(gnutls_pkcs12_t pkcs12, gnutls_pkcs12_bag_t bag)
 
        if (enc)
                result =
-                   asn1_write_value(c2, "?LAST.contentType", ENC_DATA_OID,
+                   asn1_write_value(c2, "?LAST.contentType", ENCRYPTED_DATA_OID,
                                     1);
        else
                result =
diff --git a/lib/x509/pkcs7-encrypted.c b/lib/x509/pkcs7-encrypted.c
new file mode 100644 (file)
index 0000000..79a26dd
--- /dev/null
@@ -0,0 +1,614 @@
+/*
+ * Copyright (C) 2003-2015 Free Software Foundation, Inc.
+ * Copyright (C) 2015 Red Hat, Inc.
+ * Copyright (C) 2020 Dmitry Baryshkov
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include <libtasn1.h>
+
+#include <common.h>
+#include <random.h>
+#include <pkcs7_int.h>
+#include <gnutls/pkcs7.h>
+
+#define RC2_40_VERSION 0xa0
+/*#define RC2_64_VERSION 0x78
+#define RC2_128_VERSION 0x3a*/
+
+/* Decodes the PKCS #7 encrypted data, and returns an asn1_node,
+ * which holds them
+ */
+int _gnutls_pkcs7_decode_encrypted_data(gnutls_pkcs7_t pkcs7)
+{
+       asn1_node c2;
+       int len, result;
+       gnutls_datum_t tmp = {NULL, 0};
+
+       if ((result = asn1_create_element
+            (_gnutls_get_pkix(), "PKIX1.pkcs-7-EncryptedData",
+             &c2)) != ASN1_SUCCESS) {
+               gnutls_assert();
+               return _gnutls_asn2err(result);
+       }
+
+       /* the Encrypted-data has been created, so
+        * decode them.
+        */
+       result = _gnutls_x509_read_value(pkcs7->pkcs7, "content", &tmp);
+       if (result < 0) {
+               gnutls_assert();
+               goto error;
+       }
+
+       result = asn1_der_decoding(&c2, tmp.data, tmp.size, NULL);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               result = _gnutls_asn2err(result);
+               goto error;
+       }
+
+       /* read the encapsulated content */
+       len = MAX_OID_SIZE - 1;
+       result =
+           asn1_read_value(c2, "encryptedContentInfo.contentType", pkcs7->encap_data_oid, &len);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               result = _gnutls_asn2err(result);
+               goto error;
+       }
+
+       if (strcmp(pkcs7->encap_data_oid, DATA_OID) != 0) {
+               _gnutls_debug_log
+                   ("Unknown PKCS#7 Encapsulated Content OID '%s'; treating as raw data\n",
+                    pkcs7->encap_data_oid);
+
+       }
+
+       pkcs7->content_data = c2;
+       gnutls_free(tmp.data);
+
+       return 0;
+
+ error:
+       gnutls_free(tmp.data);
+       if (c2)
+               asn1_delete_structure(&c2);
+       return result;
+}
+
+/**
+ * gnutls_pkcs7_encryption_info_deinit:
+ * @info: should point to a #gnutls_pkcs7_encryption_info_t structure
+ *
+ * This function will deinitialize any allocated value in the
+ * provided #gnutls_pkcs7_encryption_info_t.
+ *
+ * Since: 3.6.14
+ **/
+void gnutls_pkcs7_encryption_info_deinit(gnutls_pkcs7_encryption_info_t *info)
+{
+       gnutls_pkcs7_attrs_deinit(info->unprotected_attrs);
+       _gnutls_free_datum(&info->enc_params);
+       gnutls_free(info->enc_oid);
+       memset(info, 0, sizeof(*info));
+}
+
+/**
+ * gnutls_pkcs7_get_encryption_info:
+ * @pkcs7: should contain a #gnutls_pkcs7_t type
+ * @info: will contain the output encryption
+ *
+ * This function will return information about the encryption
+ * in the provided PKCS #7 structure. The information should be
+ * deinitialized using gnutls_pkcs7_encryption_info_deinit().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value.
+ *
+ * Since: 3.6.14
+ **/
+int gnutls_pkcs7_get_encryption_info(gnutls_pkcs7_t pkcs7, gnutls_pkcs7_encryption_info_t *info)
+{
+       int ret, len, i;
+       char oid[MAX_OID_SIZE];
+       char root[256];
+       gnutls_datum_t tmp = { NULL, 0 };
+
+       if (pkcs7 == NULL || pkcs7->type != GNUTLS_PKCS7_ENCRYPTED)
+               return GNUTLS_E_INVALID_REQUEST;
+
+       memset(info, 0, sizeof(*info));
+
+       len = sizeof(oid) - 1;
+       ret = asn1_read_value(pkcs7->content_data, "encryptedContentInfo.contentEncryptionAlgorithm.algorithm", oid, &len);
+       if (ret != ASN1_SUCCESS) {
+               gnutls_assert();
+               ret = _gnutls_asn2err(ret);
+               goto fail;
+       }
+       info->enc_oid = gnutls_strdup(oid);
+
+       ret = _gnutls_x509_read_value(pkcs7->content_data, "encryptedContentInfo.contentEncryptionAlgorithm.parameters", &info->enc_params);
+       if (ret < 0) {
+               gnutls_assert();
+               goto fail;
+       }
+
+       /* read the unprotected attrs */
+       for (i = 0;; i++) {
+               snprintf(root, sizeof(root),
+                        "unprotectedAttrs.?%u.type",
+                        i + 1);
+               len = sizeof(oid) - 1;
+               ret = asn1_read_value(pkcs7->content_data, root, oid, &len);
+               if (ret != ASN1_SUCCESS) {
+                       break;
+               }
+
+               snprintf(root, sizeof(root),
+                        "unprotectedAttrs.?%u.values.?1",
+                        i + 1);
+               ret = _gnutls_x509_read_value(pkcs7->content_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->unprotected_attrs, oid, &tmp, 0);
+               gnutls_free(tmp.data);
+
+               if (ret < 0) {
+                       gnutls_assert();
+                       goto fail;
+               }
+       }
+
+       return 0;
+
+ fail:
+       gnutls_free(tmp.data);
+       gnutls_pkcs7_encryption_info_deinit(info);
+       return ret;
+}
+
+typedef struct pkcs7_cipher_st {
+       const char *name;
+       unsigned int cipher;
+       const char *cipher_oid;
+       const char *desc;
+       const char *iv_name;
+} pkcs7_cipher_t;
+
+static const pkcs7_cipher_t pkcs7_ciphers[] = {
+       {
+        .name = "DES-EDE3-CBC",
+        .cipher = GNUTLS_CIPHER_3DES_CBC,
+        .cipher_oid = DES_EDE3_CBC_OID,
+        .desc = "PKIX1.pkcs-5-des-EDE3-CBC-params",
+       },
+       {
+        .name = "AES-128-CBC",
+        .cipher = GNUTLS_CIPHER_AES_128_CBC,
+        .cipher_oid = AES_128_CBC_OID,
+        .desc = "PKIX1.pkcs-5-aes128-CBC-params",
+       },
+       {
+        .name = "AES-192-CBC",
+        .cipher = GNUTLS_CIPHER_AES_192_CBC,
+        .cipher_oid = AES_192_CBC_OID,
+        .desc = "PKIX1.pkcs-5-aes192-CBC-params",
+       },
+       {
+        .name = "AES-256-CBC",
+        .cipher = GNUTLS_CIPHER_AES_256_CBC,
+        .cipher_oid = AES_256_CBC_OID,
+        .desc = "PKIX1.pkcs-5-aes256-CBC-params",
+       },
+       {
+        .name = "RC2-CBC",
+        .cipher = GNUTLS_CIPHER_RC2_40_CBC,
+        .cipher_oid = RC2_CBC_OID,
+        .desc = "PKIX1.pkcs-5-RC2-CBC-params",
+        .iv_name = "iv",
+       },
+       { NULL, 0, NULL, NULL, NULL },
+};
+
+static int _gnutls_pkcs7_encrypt_int(const cipher_entry_st *ce, const gnutls_datum_t *key, const gnutls_datum_t *iv, const gnutls_datum_t *plain, gnutls_datum_t *enc)
+{
+       int ret;
+       int data_size;
+       uint8_t *data = NULL;
+       cipher_hd_st ch;
+       uint8_t pad, pad_size;
+
+       if (ce == NULL || iv->size != ce->cipher_iv || key->size != ce->keysize)
+               return gnutls_assert_val(GNUTLS_E_ENCRYPTION_FAILED);
+
+       pad_size = _gnutls_cipher_get_block_size(ce);
+
+       if (pad_size == 1 || ce->type == CIPHER_STREAM) /* stream */
+               pad_size = 0;
+
+       data = gnutls_malloc(plain->size + pad_size);
+       if (data == NULL) {
+               gnutls_assert();
+               return GNUTLS_E_MEMORY_ERROR;
+       }
+
+       memcpy(data, plain->data, plain->size);
+
+       if (pad_size > 0) {
+               pad = pad_size - (plain->size % pad_size);
+               if (pad == 0)
+                       pad = pad_size;
+               memset(&data[plain->size], pad, pad);
+       } else
+               pad = 0;
+
+       data_size = plain->size + pad;
+
+       ret = _gnutls_cipher_init(&ch, ce, key, iv, 1);
+       if (ret < 0)
+               return gnutls_assert_val(ret);
+
+       ret = _gnutls_cipher_encrypt(&ch, data, data_size);
+       _gnutls_cipher_deinit(&ch);
+       if (ret < 0)
+               return gnutls_assert_val(ret);
+
+       enc->data = data;
+       enc->size = data_size;
+
+       return 0;
+}
+
+static int _gnutls_pkcs7_decrypt_int(const cipher_entry_st *ce, const gnutls_datum_t *key, const gnutls_datum_t *iv, gnutls_datum_t *data)
+{
+       cipher_hd_st ch;
+       int ret;
+
+       if (iv->size != ce->cipher_iv || key->size != ce->keysize)
+               return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+
+       if (ce->type == CIPHER_BLOCK && data->size % ce->blocksize != 0)
+               return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+
+       ret = _gnutls_cipher_init(&ch, ce, key, iv, 0);
+       if (ret < 0)
+               return gnutls_assert_val(ret);
+
+       ret = _gnutls_cipher_decrypt(&ch, data->data, data->size);
+       _gnutls_cipher_deinit(&ch);
+       if (ret < 0)
+               return gnutls_assert_val(ret);
+
+       /* constant time verification, depends only on last byte value */
+       if (ce->type == CIPHER_BLOCK && ce->blocksize != 1) {
+               unsigned int pslen = (uint8_t)data->data[data->size - 1];
+               unsigned int i;
+               int sum = 0;
+
+               if (pslen > ce->blocksize || pslen >= data->size  || pslen == 0)
+                       return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+
+               /* verify padding according to rfc2898 */
+               for (i = 0; i < pslen; i++) {
+                       int diff = data->data[data->size-1-i] - pslen;
+                       sum += diff * diff;
+               }
+               if (sum != 0)
+                       return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+               data->size -= pslen;
+       }
+
+       return 0;
+}
+
+/**
+ * gnutls_pkcs7_encrypt:
+ * @pkcs7: Should contain a #gnutls_pkcs7_t type
+ * @cipher: Cipher to be used for data encryption
+ * @key: The key to be used for encryption
+ * @data: The data to be encrypted
+ * @unprotected_attrs: Any additional attributes to be included in the unprotected ones (or %NULL)
+ * @flags: Should be zero or one of %GNUTLS_PKCS7 flags
+ *
+ * This function will encrypt the provided data in the provided PKCS #7 structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value.
+ *
+ * Since: 3.6.14
+ **/
+int gnutls_pkcs7_encrypt(gnutls_pkcs7_t pkcs7,
+                        gnutls_cipher_algorithm_t cipher,
+                        const gnutls_datum_t *key,
+                        const gnutls_datum_t *data,
+                        gnutls_pkcs7_attrs_t unprotected_attrs,
+                        unsigned int flags)
+{
+       asn1_node params_asn = NULL;
+       gnutls_datum_t iv = {NULL, 0};
+       gnutls_datum_t tmp = {NULL, 0};
+       unsigned char ivb[64];
+       const cipher_entry_st *ce = cipher_to_entry(cipher);
+       unsigned char vers;
+       int result;
+       const pkcs7_cipher_t *pciph;
+
+       if (pkcs7 == NULL || ce == NULL)
+               return GNUTLS_E_INVALID_REQUEST;
+
+       if (sizeof(ivb) < ce->cipher_iv)
+               return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+       iv.data = ivb;
+       iv.size = ce->cipher_iv;
+
+       if (pkcs7->type != GNUTLS_PKCS7_ENCRYPTED)
+               asn1_delete_structure(&pkcs7->content_data);
+
+       if (pkcs7->content_data == NULL) {
+               result =
+                   asn1_create_element(_gnutls_get_pkix(),
+                                       "PKIX1.pkcs-7-EncryptedData",
+                                       &pkcs7->content_data);
+               if (result != ASN1_SUCCESS) {
+                       gnutls_assert();
+                       result = _gnutls_asn2err(result);
+                       goto error;
+               }
+
+               pkcs7->type = GNUTLS_PKCS7_ENCRYPTED;
+       }
+
+       for (pciph = pkcs7_ciphers; pciph->cipher_oid != NULL; pciph++)
+               if (pciph->cipher == cipher)
+                       break;
+       if (pciph->cipher_oid == NULL) {
+               gnutls_assert();
+               result = GNUTLS_E_ENCRYPTION_FAILED;
+               goto error;
+       }
+
+       result =
+           asn1_write_value(pkcs7->content_data,
+                            "encryptedContentInfo.contentEncryptionAlgorithm.algorithm",
+                            pciph->cipher_oid, 1);
+
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               result = _gnutls_asn2err(result);
+               goto error;
+       }
+
+       result = asn1_create_element(_gnutls_get_pkix(), pciph->desc, &params_asn);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               result = _gnutls_asn2err(result);
+               goto error;
+       }
+
+       result = gnutls_rnd(GNUTLS_RND_RANDOM, iv.data, iv.size);
+       if (result < 0) {
+               gnutls_assert();
+               goto error;
+       }
+
+       result = _gnutls_x509_write_value(params_asn, pciph->iv_name ? pciph->iv_name : "", &iv);
+       if (result < 0) {
+               gnutls_assert();
+               goto error;
+       }
+
+       if (!strcmp(pciph->cipher_oid, RC2_CBC_OID)) {
+               /* RC2 encodes key bits in params */
+               unsigned char rc2vers[2];
+
+               rc2vers[0] = 0;
+               rc2vers[1] = RC2_40_VERSION;
+
+               result = asn1_write_value(params_asn, "rc2ParameterVersion", rc2vers, 2);
+               if (result != ASN1_SUCCESS) {
+                       gnutls_assert();
+                       result = _gnutls_asn2err(result);
+                       goto error;
+               }
+       }
+
+       result = _gnutls_x509_der_encode_and_copy(params_asn, "",
+                       pkcs7->content_data,
+                       "encryptedContentInfo.contentEncryptionAlgorithm.parameters", 0);
+       if (result < 0) {
+               gnutls_assert();
+               goto error;
+       }
+
+       asn1_delete_structure(&params_asn);
+
+       result = _gnutls_pkcs7_encrypt_int(ce, key, &iv, data, &tmp);
+       if (result < 0) {
+               gnutls_assert();
+               goto error;
+       }
+
+       result = _gnutls_x509_write_value(pkcs7->content_data,
+                            "encryptedContentInfo.encryptedContent", &tmp);
+       _gnutls_free_datum(&tmp);
+       if (result < 0) {
+               gnutls_assert();
+               goto error;
+       }
+
+       result =
+           asn1_write_value(pkcs7->content_data, "encryptedContentInfo.contentType",
+                            DATA_OID, 1);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               result = _gnutls_asn2err(result);
+               goto error;
+       }
+
+       result = _gnutls_pkcs7_write_attrs(pkcs7->content_data, "unprotectedAttrs",
+                       unprotected_attrs);
+       if (result < 0) {
+               gnutls_assert();
+               goto error;
+       }
+
+       if (unprotected_attrs)
+               vers = 2;
+       else
+               vers = 0;
+
+       result = asn1_write_value(pkcs7->content_data, "version", &vers, 1);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               result = _gnutls_asn2err(result);
+               goto error;
+       }
+
+error:
+       return result;
+}
+
+/**
+ * gnutls_pkcs7_decrypt:
+ * @pkcs7: Should contain a #gnutls_pkcs7_t type
+ * @key: The key to be used for decryption
+ * @out: Decrypted data
+ *
+ * This function will decrypt the data from the provided PKCS #7 structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value.
+ *
+ * Since: 3.6.14
+ **/
+int gnutls_pkcs7_decrypt(gnutls_pkcs7_t pkcs7, const gnutls_datum_t *key, gnutls_datum_t *out)
+{
+       gnutls_pkcs7_encryption_info_t info;
+       asn1_node params_asn = NULL;
+       gnutls_datum_t iv = {NULL, 0};
+       gnutls_datum_t tmp = {NULL, 0};
+       const cipher_entry_st *ce;
+       int ret;
+       const pkcs7_cipher_t *pciph;
+
+       if (pkcs7 == NULL || pkcs7->type != GNUTLS_PKCS7_ENCRYPTED)
+               return GNUTLS_E_INVALID_REQUEST;
+
+       ret = gnutls_pkcs7_get_encryption_info(pkcs7, &info);
+       if (ret < 0)
+               return gnutls_assert_val(ret);
+
+       if (info.enc_oid == NULL) {
+               gnutls_assert();
+               ret = GNUTLS_E_DECRYPTION_FAILED;
+               goto error;
+       }
+
+       for (pciph = pkcs7_ciphers; pciph->cipher_oid != NULL; pciph++)
+               if (!strcmp(pciph->cipher_oid, info.enc_oid))
+                       break;
+
+       if (pciph->cipher_oid == NULL) {
+               gnutls_assert();
+               ret = GNUTLS_E_DECRYPTION_FAILED;
+               goto error;
+       }
+
+       ret = asn1_create_element(_gnutls_get_pkix(), pciph->desc, &params_asn);
+       if (ret != ASN1_SUCCESS) {
+               gnutls_assert();
+               ret = _gnutls_asn2err(ret);
+               goto error;
+       }
+
+       ret = _asn1_strict_der_decode(&params_asn, info.enc_params.data, info.enc_params.size, NULL);
+       if (ret != ASN1_SUCCESS) {
+               gnutls_assert();
+               ret = _gnutls_asn2err(ret);
+               goto error;
+       }
+
+       if (!strcmp(info.enc_oid, RC2_CBC_OID)) {
+               /* RC2 encodes key bits in params */
+               unsigned int vers;
+
+               ret = _gnutls_x509_read_uint(params_asn, "rc2ParameterVersion", &vers);
+               if (ret < 0) {
+                       gnutls_assert();
+                       goto error;
+               }
+
+               /* Only RC2-40-CBC is supported for now */
+               if (vers != RC2_40_VERSION) {
+                       gnutls_assert();
+                       ret = GNUTLS_E_DECRYPTION_FAILED;
+                       goto error;
+               }
+
+               ce = cipher_to_entry(GNUTLS_CIPHER_RC2_40_CBC);
+       } else if (pciph->cipher != GNUTLS_CIPHER_UNKNOWN) {
+               ce = cipher_to_entry(pciph->cipher);
+       } else {
+               gnutls_assert();
+               ret = GNUTLS_E_DECRYPTION_FAILED;
+               goto error;
+       }
+
+       ret = _gnutls_x509_read_value(params_asn, pciph->iv_name ? pciph->iv_name : "", &iv);
+       if (ret < 0) {
+               gnutls_assert();
+               goto error;
+       }
+
+       ret = _gnutls_x509_read_value(pkcs7->content_data, "encryptedContentInfo.encryptedContent", &tmp);
+       if (ret < 0) {
+               gnutls_assert();
+               goto error;
+       }
+
+       ret = _gnutls_pkcs7_decrypt_int(ce, key, &iv, &tmp);
+       if (ret < 0) {
+               gnutls_assert();
+               goto error;
+       }
+
+       *out = tmp;
+       tmp.data = NULL;
+       ret = 0;
+
+error:
+       _gnutls_free_datum(&tmp);
+       _gnutls_free_datum(&iv);
+       asn1_delete_structure(&params_asn);
+       gnutls_pkcs7_encryption_info_deinit(&info);
+
+       return ret;
+}
index 1b7d9a0abcba304004a14656008fb83a8ceac04f..ae06b276ec424094a9a48d027c73f8319ca3d04f 100644 (file)
@@ -327,6 +327,57 @@ static void _gnutls_pkcs7_print_digested(gnutls_pkcs7_t pkcs7,
        adds(str, "\n");
 }
 
+static void _gnutls_pkcs7_print_encrypted(gnutls_pkcs7_t pkcs7,
+                                         gnutls_certificate_print_formats_t format,
+                                         gnutls_buffer_st * str)
+{
+       int ret, i;
+       gnutls_pkcs7_encryption_info_t info;
+       char *oid;
+       gnutls_datum_t data;
+       char prefix[128];
+       const struct oid_to_string * entry;
+       gnutls_cipher_algorithm_t ciph;
+
+       adds(str, "Content Type: Encrypted\n");
+
+       ret = gnutls_pkcs7_get_encryption_info(pkcs7, &info);
+       if (ret < 0)
+               return;
+
+       ciph = gnutls_oid_to_cipher(info.enc_oid);
+       if (ciph != GNUTLS_CIPHER_UNKNOWN)
+               addf(str, "Cipher: %s (%s)\n", gnutls_cipher_get_name(ciph), info.enc_oid);
+       else
+               addf(str, "Cipher: %s\n", info.enc_oid);
+
+       print_raw(str, "Cipher params", &info.enc_params);
+
+       if (format == GNUTLS_CRT_PRINT_FULL) {
+               if (info.unprotected_attrs) {
+                       for (i = 0;; i++) {
+                               ret =
+                                   gnutls_pkcs7_get_attr(info.unprotected_attrs, i,
+                                                         &oid, &data, 0);
+                               if (ret < 0)
+                                       break;
+                               if (i == 0)
+                                       addf(str, "\tSigned Attributes:\n");
+
+                               entry = _gnutls_oid_get_entry(pkcs7_attrs, oid);
+                               snprintf(prefix, sizeof(prefix), "\t\t%s",
+                                               (entry && entry->name_desc) ? entry->name_desc : oid);
+                               print_raw(str, prefix, &data);
+                               gnutls_free(data.data);
+                       }
+               }
+       }
+
+       adds(str, "\n");
+
+       gnutls_pkcs7_encryption_info_deinit(&info);
+}
+
 /**
  * gnutls_pkcs7_print:
  * @pkcs7: The PKCS7 struct to be printed
@@ -376,6 +427,9 @@ int gnutls_pkcs7_print(gnutls_pkcs7_t pkcs7,
        case GNUTLS_PKCS7_DIGESTED:
                _gnutls_pkcs7_print_digested(pkcs7, format, &str);
                break;
+       case GNUTLS_PKCS7_ENCRYPTED:
+               _gnutls_pkcs7_print_encrypted(pkcs7, format, &str);
+               break;
        default:
                adds(&str, "Unsupported PKCS#7 Content Type\n");
                break;
index dfb03b592a300fe68355154dcc9e94368b3cb6ee..de4a7665abade7623a76bbef5012f0874b5584df 100644 (file)
@@ -229,6 +229,9 @@ gnutls_pkcs7_import(gnutls_pkcs7_t pkcs7, const gnutls_datum_t * data,
        } else if (strcmp(data_oid, DIGESTED_DATA_OID) == 0) {
                pkcs7->type = GNUTLS_PKCS7_DIGESTED;
                result = _gnutls_pkcs7_decode_digested_data(pkcs7);
+       } else if (strcmp(data_oid, ENCRYPTED_DATA_OID) == 0) {
+               pkcs7->type = GNUTLS_PKCS7_ENCRYPTED;
+               result = _gnutls_pkcs7_decode_encrypted_data(pkcs7);
        } else {
                gnutls_assert();
                _gnutls_debug_log("Unknown PKCS7 Content OID '%s'\n", pkcs7->encap_data_oid);
@@ -364,6 +367,9 @@ static int reencode(gnutls_pkcs7_t pkcs7)
                case GNUTLS_PKCS7_DIGESTED:
                        oid = DIGESTED_DATA_OID;
                        break;
+               case GNUTLS_PKCS7_ENCRYPTED:
+                       oid = ENCRYPTED_DATA_OID;
+                       break;
                default:
                        return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
                }
index 785a8962ce68458c46b3bf48eddde5422daead24..0523c2f41e7d0645ac9bfaa8485e4b3e6e74127e 100644 (file)
 /* PKCS #7
  */
 #define DATA_OID "1.2.840.113549.1.7.1"
-#define ENC_DATA_OID "1.2.840.113549.1.7.6"
-
 #define SIGNED_DATA_OID "1.2.840.113549.1.7.2"
 #define DIGESTED_DATA_OID "1.2.840.113549.1.7.5"
-
+#define ENCRYPTED_DATA_OID "1.2.840.113549.1.7.6"
 
 typedef enum schema_id {
        PBES2_GENERIC=1,        /* when the algorithm is unknown, temporal use when reading only */
@@ -133,6 +131,7 @@ _gnutls_pkcs7_data_enc_info(const gnutls_datum_t * data, const struct pkcs_ciphe
 
 int _gnutls_pkcs7_decode_signed_data(gnutls_pkcs7_t pkcs7);
 int _gnutls_pkcs7_decode_digested_data(gnutls_pkcs7_t pkcs7);
+int _gnutls_pkcs7_decode_encrypted_data(gnutls_pkcs7_t pkcs7);
 
 int _gnutls_pkcs7_write_attr(asn1_node c2, const char *root, const char *oid, gnutls_datum_t *data);
 int _gnutls_pkcs7_write_attrs(asn1_node c2, const char *root, gnutls_pkcs7_attrs_t attrs);
index f1bc8210f91e085e88426a487a0da5f959e229dc..452cfa5929c9d9c45072fd4512cf2d29378f24d8 100644 (file)
@@ -128,6 +128,7 @@ typedef enum {
        GNUTLS_PKCS7_DATA = 1,
        GNUTLS_PKCS7_SIGNED = 2,
        GNUTLS_PKCS7_DIGESTED = 5,
+       GNUTLS_PKCS7_ENCRYPTED = 6,
 } gnutls_pkcs7_content_type_t;
 
 typedef struct gnutls_pkcs7_attrs_st {
index c3694b20ec848c50e19707f16a8c1b6b8b33944c..1b850376ccbbbbc9e934395617d919ada913b911 100644 (file)
           "long-option": "verify-digest",
           "description": "Verify the provided PKCS #7 digested structure",
           "detail": "This option verifies the digested PKCS #7 structure. The --load-data option will utilize detached data."
+        },
+        {
+          "long-option": "encrypt",
+          "description": "Encrypt using a PKCS #7 structure",
+          "detail": "This option generates a PKCS #7 structure containing an encrypted data from infile. The data are stored within the structure."
+        },
+        {
+          "long-option": "decrypt",
+          "description": "Decrypt the provided PKCS #7 EncryptedData structure",
+          "detail": "This option decrypts the Encrypted PKCS #7 structure."
         }
       ]
     },
           "description": "Enforce an empty password",
           "detail": "This option enforces an empty password. This is different than the NULL or no password in schemas like PKCS #8."
         },
+       {
+          "long-option": "secretkey",
+          "description": "Secret key to use",
+          "detail": "You can use this option to specify the secret key in the command line. Note, that the command line arguments are available for view in others in the system.",
+          "argument-type": "string"
+        },
         {
           "long-option": "cprint",
           "description": "In certain operations it prints the information in C-friendly format",
index a003dcba775a68d1afb8ad13363e646271ae5c5e..2a1929e77f64ffe09dc9fdde7d2be92416d24ebc 100644 (file)
@@ -248,6 +248,139 @@ static void pkcs7_verify_digest(common_info_st * cinfo, unsigned display_data)
        app_exit(ecode);
 }
 
+static gnutls_cipher_algorithm_t name_to_cipher(const char *cipher)
+{
+       if (cipher == NULL) {
+#ifdef ENABLE_FIPS140
+               return GNUTLS_CIPHER_AES_128_CBC;
+#else /* compatibility mode - most implementations don't support PBES2 with AES */
+               return GNUTLS_CIPHER_3DES_CBC;
+#endif
+       } else if (strcasecmp(cipher, "3des") == 0) {
+               return GNUTLS_CIPHER_3DES_CBC;
+       } else if (strcasecmp(cipher, "aes-128") == 0) {
+               return GNUTLS_CIPHER_AES_128_CBC;
+       } else if (strcasecmp(cipher, "aes-192") == 0) {
+               return GNUTLS_CIPHER_AES_192_CBC;
+       } else if (strcasecmp(cipher, "aes-256") == 0) {
+               return GNUTLS_CIPHER_AES_256_CBC;
+       } else if (strcasecmp(cipher, "rc2-40") == 0) {
+               return GNUTLS_CIPHER_RC2_40_CBC;
+       }
+
+       fprintf(stderr, "unknown cipher %s\n", cipher);
+       app_exit(1);
+}
+
+static void pkcs7_encrypt(common_info_st * cinfo)
+{
+       gnutls_pkcs7_t pkcs7;
+       int ret;
+       size_t size;
+       gnutls_datum_t data;
+       gnutls_datum_t secret = {NULL, 0};
+       unsigned int flags = 0;
+
+       ret = gnutls_pkcs7_init(&pkcs7);
+       if (ret < 0) {
+               fprintf(stderr, "p7_init: %s\n", gnutls_strerror(ret));
+               app_exit(1);
+       }
+
+       data.data = (void *) fread_file(infile, 0, &size);
+       data.size = size;
+
+       if (!data.data) {
+               fprintf(stderr, "%s", infile ? "file" : "standard input");
+               app_exit(1);
+       }
+
+       if (!HAVE_OPT(SECRETKEY)) {
+               fprintf(stderr, "secret key not provided\n");
+               app_exit(1);
+       } else {
+               decode_seed(&secret, OPT_ARG(SECRETKEY), strlen(OPT_ARG(SECRETKEY)));
+       }
+
+       flags |= GNUTLS_PKCS7_EMBED_DATA;
+
+       ret = gnutls_pkcs7_encrypt(pkcs7, name_to_cipher(cinfo->pkcs_cipher), &secret, &data, NULL, flags);
+       if (ret < 0) {
+               fprintf(stderr, "Error signing: %s\n", gnutls_strerror(ret));
+               app_exit(1);
+       }
+
+       size = lbuffer_size;
+       ret = gnutls_pkcs7_export(pkcs7, cinfo->outcert_format, lbuffer, &size);
+       if (ret < 0) {
+               fprintf(stderr, "pkcs7_export: %s\n", gnutls_strerror(ret));
+               app_exit(1);
+       }
+
+       fwrite(lbuffer, 1, size, outfile);
+
+       gnutls_pkcs7_deinit(pkcs7);
+       app_exit(0);
+}
+
+static void pkcs7_decrypt(common_info_st * cinfo, unsigned display_data)
+{
+       gnutls_pkcs7_t pkcs7;
+       int ret, ecode;
+       size_t size;
+       gnutls_datum_t data;
+       gnutls_datum_t tmp = {NULL,0};
+       gnutls_datum_t secret = {NULL, 0};
+
+       ret = gnutls_pkcs7_init(&pkcs7);
+       if (ret < 0) {
+               fprintf(stderr, "p7_init: %s\n", gnutls_strerror(ret));
+               app_exit(1);
+       }
+
+       data.data = (void *) fread_file(infile, 0, &size);
+       data.size = size;
+
+       if (!data.data) {
+               fprintf(stderr, "%s", infile ? "file" : "standard input");
+               app_exit(1);
+       }
+
+       ret = gnutls_pkcs7_import(pkcs7, &data, cinfo->incert_format);
+       free(data.data);
+       if (ret < 0) {
+               fprintf(stderr, "import error: %s\n",
+                       gnutls_strerror(ret));
+               app_exit(1);
+       }
+
+       if (!display_data)
+               fprintf(outfile, "eContent Type: %s\n", gnutls_pkcs7_get_embedded_data_oid(pkcs7));
+
+       if (!HAVE_OPT(SECRETKEY)) {
+               fprintf(stderr, "secret key not provided\n");
+               app_exit(1);
+       } else {
+               decode_seed(&secret, OPT_ARG(SECRETKEY), strlen(OPT_ARG(SECRETKEY)));
+       }
+
+       ret = gnutls_pkcs7_decrypt(pkcs7, &secret, &tmp);
+       if (ret < 0) {
+               fprintf(stderr, "Decrption status: failed: %s\n", gnutls_strerror(ret));
+               ecode = 1;
+       } else {
+               fprintf(stderr, "Decryption status: ok\n");
+               fwrite(tmp.data, 1, tmp.size, outfile);
+               gnutls_free(tmp.data);
+               tmp.data = NULL;
+               ecode = 0;
+       }
+
+       gnutls_pkcs7_deinit(pkcs7);
+       gnutls_free(secret.data);
+       app_exit(ecode);
+}
+
 static void cmd_parser(int argc, char **argv)
 {
        int ret, privkey_op = 0;
@@ -396,6 +529,10 @@ static void cmd_parser(int argc, char **argv)
                pkcs7_digest(&cinfo, 1);
        else if (HAVE_OPT(VERIFY_DIGEST))
                pkcs7_verify_digest(&cinfo, ENABLED_OPT(SHOW_DATA));
+       else if (HAVE_OPT(ENCRYPT))
+               pkcs7_encrypt(&cinfo);
+       else if (HAVE_OPT(DECRYPT))
+               pkcs7_decrypt(&cinfo, ENABLED_OPT(SHOW_DATA));
        else
                USAGE(1);