]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Support PBMAC1 usage in PKCS#12
authorDaiki Ueno <ueno@gnu.org>
Tue, 30 Apr 2024 01:17:37 +0000 (10:17 +0900)
committerDaiki Ueno <ueno@gnu.org>
Tue, 14 May 2024 13:25:54 +0000 (22:25 +0900)
This allows usage of PBMAC1 as the MAC to verify a PKCS#12 structure,
following draft-ietf-lamps-pkcs12-pbmac1[1]. While the MAC
verification is transparent, the generation requires a new API
gnutls_pkcs12_generate_mac3 to be used with the
GNUTLS_PKCS12_USE_PBMAC1 flag.

certtool has also been extended with the --pbmac1 option, which can be
used in combination with --to-p12.

1. https://datatracker.ietf.org/doc/draft-ietf-lamps-pkcs12-pbmac1/

Signed-off-by: Daiki Ueno <ueno@gnu.org>
25 files changed:
NEWS
devel/libgnutls.abignore
devel/symbols.last
doc/Makefile.am
doc/manpages/Makefile.am
lib/algorithms/mac.c
lib/includes/gnutls/gnutls.h.in
lib/includes/gnutls/pkcs12.h
lib/libgnutls.map
lib/pkix.asn
lib/x509/pkcs12.c
lib/x509/pkcs7-crypt.c
lib/x509/x509_int.h
src/certtool-options.json
src/certtool.c
tests/cert-tests/Makefile.am
tests/cert-tests/data/pbmac1-simple.p12 [new file with mode: 0644]
tests/cert-tests/data/pbmac1_256_256.bad-iter.p12 [new file with mode: 0644]
tests/cert-tests/data/pbmac1_256_256.bad-salt.p12 [new file with mode: 0644]
tests/cert-tests/data/pbmac1_256_256.good.p12 [new file with mode: 0644]
tests/cert-tests/data/pbmac1_256_256.no-len.p12 [new file with mode: 0644]
tests/cert-tests/data/pbmac1_256_256.short-len.p12 [new file with mode: 0644]
tests/cert-tests/data/pbmac1_512_256.good.p12 [new file with mode: 0644]
tests/cert-tests/data/pbmac1_512_512.good.p12 [new file with mode: 0644]
tests/cert-tests/pkcs12-pbmac1.sh [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 4a0d231cc0f5b005dc92c54484b3b49d183a0679..ff48914aa57be7e45b4fd7051651c44ab8e76c05 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,17 @@ Copyright (C) 2000-2016 Free Software Foundation, Inc.
 Copyright (C) 2013-2019 Nikos Mavrogiannopoulos
 See the end for copying conditions.
 
+* Version 3.8.6 (unreleased)
+
+** libgnutls: PBMAC1 is now supported as a MAC mechanism for PKCS#12
+   To be compliant with FIPS 140-3, PKCS#12 files with MAC based on
+   PBKDF2 (PBMAC1) is now supported, according to the specification
+   proposed in draft-ietf-lamps-pkcs12-pbmac1.
+
+** API and ABI modifications:
+gnutls_pkcs12_generate_mac3: New function
+gnutls_pkcs12_flags_t: New enum
+
 * Version 3.8.5 (released 2024-04-04)
 
 ** libgnutls: Due to majority of usages and implementations of
index c19dce38e11a1c763a5d7bb7a17888e269dc61bf..1f9c298564f227194c47e98d404aad9e30f60099 100644 (file)
@@ -70,3 +70,9 @@ name = drbg_aes_reseed
 
 # The following should be removed in the new release, after updating the
 # abi-dump repository:
+[suppress_function]
+name = gnutls_pkcs12_generate_mac3
+
+[suppress_type]
+name = gnutls_mac_algorithm_t
+changed_enumerators = GNUTLS_MAC_PBMAC1
index e6dc0b3531cdc8c4078e2a2e6eb054e27a7aee50..cff43e9e292ec7a75448d93cd1cf72835fa2e816 100644 (file)
@@ -20,6 +20,7 @@ GNUTLS_3_7_7@GNUTLS_3_7_7
 GNUTLS_3_8_1@GNUTLS_3_8_1
 GNUTLS_3_8_2@GNUTLS_3_8_2
 GNUTLS_3_8_4@GNUTLS_3_8_4
+GNUTLS_3_8_6@GNUTLS_3_8_6
 _gnutls_global_init_skip@GNUTLS_3_4
 gnutls_aead_cipher_decrypt@GNUTLS_3_4
 gnutls_aead_cipher_decryptv2@GNUTLS_3_6_10
@@ -566,6 +567,7 @@ gnutls_pkcs12_deinit@GNUTLS_3_4
 gnutls_pkcs12_export2@GNUTLS_3_4
 gnutls_pkcs12_export@GNUTLS_3_4
 gnutls_pkcs12_generate_mac2@GNUTLS_3_4
+gnutls_pkcs12_generate_mac3@GNUTLS_3_8_6
 gnutls_pkcs12_generate_mac@GNUTLS_3_4
 gnutls_pkcs12_get_bag@GNUTLS_3_4
 gnutls_pkcs12_import@GNUTLS_3_4
index 9933d4d46f45417e6b5c233f13f249699bc6a34a..7c092c6352b62a7030e283ebecdaca4b91f3d9ed 100644 (file)
@@ -1528,6 +1528,8 @@ FUNCS += functions/gnutls_pkcs12_generate_mac
 FUNCS += functions/gnutls_pkcs12_generate_mac.short
 FUNCS += functions/gnutls_pkcs12_generate_mac2
 FUNCS += functions/gnutls_pkcs12_generate_mac2.short
+FUNCS += functions/gnutls_pkcs12_generate_mac3
+FUNCS += functions/gnutls_pkcs12_generate_mac3.short
 FUNCS += functions/gnutls_pkcs12_get_bag
 FUNCS += functions/gnutls_pkcs12_get_bag.short
 FUNCS += functions/gnutls_pkcs12_import
index bc9f36e1da7ea2d04894a0960b7e32beeca056d9..9492b351aa2a27dea99942194cafca062da5d956 100644 (file)
@@ -610,6 +610,7 @@ APIMANS += gnutls_pkcs12_export.3
 APIMANS += gnutls_pkcs12_export2.3
 APIMANS += gnutls_pkcs12_generate_mac.3
 APIMANS += gnutls_pkcs12_generate_mac2.3
+APIMANS += gnutls_pkcs12_generate_mac3.3
 APIMANS += gnutls_pkcs12_get_bag.3
 APIMANS += gnutls_pkcs12_import.3
 APIMANS += gnutls_pkcs12_init.3
index a8ba22a7a2045dbd5d4989bd933649cada028c83..af9458fd80b21af2f66711463647b34ea34702d3 100644 (file)
@@ -201,6 +201,7 @@ static SYSTEM_CONFIG_OR_CONST mac_entry_st hash_algorithms[] = {
          .output_size = 16,
          .key_size = 32,
          .block_size = 16 },
+       { .name = "PBMAC1", .id = GNUTLS_MAC_PBMAC1, .placeholder = 1 },
        { .name = "MAC-NULL", .id = GNUTLS_MAC_NULL },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0 }
 };
index afecfaa3998cfc49f3c8b1d72f1179f271b98bb3..fc461fa76e7f48812d818f2a3f2ac7ab67a78ed8 100644 (file)
@@ -359,7 +359,9 @@ typedef enum {
        GNUTLS_MAC_SHAKE_128 = 209,
        GNUTLS_MAC_SHAKE_256 = 210,
        GNUTLS_MAC_MAGMA_OMAC = 211,
-       GNUTLS_MAC_KUZNYECHIK_OMAC = 212
+       GNUTLS_MAC_KUZNYECHIK_OMAC = 212,
+       GNUTLS_MAC_PBMAC1 =
+               213 /* indicates that PBMAC1 is embedded the PKCS#12 structure */
 } gnutls_mac_algorithm_t;
 
 /**
index b16f0fab535f21e1891dc3a3ddfd40590b58bd40..b43efe532fbf6ca3a37303350f2d2c2368881795 100644 (file)
@@ -50,9 +50,16 @@ int gnutls_pkcs12_get_bag(gnutls_pkcs12_t pkcs12, int indx,
                          gnutls_pkcs12_bag_t bag);
 int gnutls_pkcs12_set_bag(gnutls_pkcs12_t pkcs12, gnutls_pkcs12_bag_t bag);
 
+typedef enum gnutls_pkcs12_flags_t {
+       GNUTLS_PKCS12_USE_PBMAC1 = 1
+} gnutls_pkcs12_flags_t;
+
 int gnutls_pkcs12_generate_mac(gnutls_pkcs12_t pkcs12, const char *pass);
 int gnutls_pkcs12_generate_mac2(gnutls_pkcs12_t pkcs12,
                                gnutls_mac_algorithm_t mac, const char *pass);
+int gnutls_pkcs12_generate_mac3(gnutls_pkcs12_t pkcs12,
+                               gnutls_mac_algorithm_t mac, const char *pass,
+                               unsigned int flags);
 int gnutls_pkcs12_verify_mac(gnutls_pkcs12_t pkcs12, const char *pass);
 
 int gnutls_pkcs12_bag_decrypt(gnutls_pkcs12_bag_t bag, const char *pass);
index b02babffe4304cc7a6d339328f50e34b5e0e77b8..88a2d5e730431e48cc7824880409696b2deb232c 100644 (file)
@@ -1441,6 +1441,14 @@ GNUTLS_3_8_4
        *;
 } GNUTLS_3_8_2;
 
+GNUTLS_3_8_6
+{
+ global:
+       gnutls_pkcs12_generate_mac3;
+ local:
+       *;
+} GNUTLS_3_8_4;
+
 GNUTLS_FIPS140_3_4 {
   global:
        gnutls_cipher_self_test;
index 48eaf396506bad9204e729e9278206a70874a6bf..2cc85e7e39c5d61edfa291997af288d143985465 100644 (file)
@@ -378,6 +378,10 @@ pkcs-5-PBES2-params ::= SEQUENCE {
   keyDerivationFunc AlgorithmIdentifier,
   encryptionScheme AlgorithmIdentifier }
 
+pkcs-5-PBMAC1-params ::= SEQUENCE {
+  keyDerivationFunc AlgorithmIdentifier,
+  messageAuthScheme AlgorithmIdentifier }
+
 -- PBKDF2
 
 -- pkcs-5-algid-hmacWithSHA1 AlgorithmIdentifier ::=
index ee16f80a3da10b7a8e1dfb44d329f83ef9ce4b79..b3287dfb72206e77d4ff1d459daf75caa2713375 100644 (file)
@@ -39,6 +39,8 @@
 #include "random.h"
 #include "intprops.h"
 
+#define PBMAC1_OID "1.2.840.113549.1.5.14"
+
 /* Decodes the PKCS #12 auth_safe, and returns the allocated raw data,
  * which holds them. Returns an asn1_node of authenticatedSafe.
  */
@@ -874,6 +876,48 @@ _gnutls_pkcs12_gost_string_to_key(gnutls_mac_algorithm_t algo,
 }
 #endif
 
+static int generate_mac_pbmac1(gnutls_mac_algorithm_t mac,
+                              const gnutls_datum_t *key,
+                              const struct pbkdf2_params *params,
+                              const gnutls_datum_t *data, asn1_node pkcs12)
+{
+       uint8_t mac_output_data[MAX_HASH_SIZE];
+       gnutls_datum_t mac_output;
+       int result;
+
+       result = _gnutls_pbmac1(mac, key, params, data, mac_output_data);
+       if (result < 0) {
+               gnutls_assert();
+               return result;
+       }
+
+       mac_output.data = mac_output_data;
+       mac_output.size = params->key_size;
+
+       result = _gnutls_x509_write_value(pkcs12, "macData.mac.digest",
+                                         &mac_output);
+       if (result < 0) {
+               gnutls_assert();
+               return result;
+       }
+
+       result = asn1_write_value(
+               pkcs12, "macData.mac.digestAlgorithm.algorithm", PBMAC1_OID, 1);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               return _gnutls_asn2err(result);
+       }
+
+       result = _gnutls_write_pbmac1_params(
+               pkcs12, params, mac, "macData.mac.digestAlgorithm.parameters");
+       if (result < 0) {
+               gnutls_assert();
+               return result;
+       }
+
+       return 0;
+}
+
 static int generate_mac_pkcs12(const mac_entry_st *me,
                               const gnutls_datum_t *key,
                               const gnutls_datum_t *salt, unsigned iter_count,
@@ -881,7 +925,7 @@ static int generate_mac_pkcs12(const mac_entry_st *me,
 {
        mac_hd_st hd;
        uint8_t mac_key_data[MAX_HASH_SIZE];
-       size_t mac_key_size = me->output_size;
+       size_t mac_key_size = _gnutls_mac_get_algo_len(me);
        uint8_t mac_data[MAX_HASH_SIZE];
        gnutls_datum_t mac;
        int result;
@@ -917,7 +961,7 @@ static int generate_mac_pkcs12(const mac_entry_st *me,
        _gnutls_mac_deinit(&hd, mac_data);
 
        mac.data = mac_data;
-       mac.size = me->output_size;
+       mac.size = _gnutls_mac_get_algo_len(me);
 
        result = _gnutls_x509_write_value(pkcs12, "macData.mac.digest", &mac);
        if (result < 0) {
@@ -946,18 +990,23 @@ static int generate_mac_pkcs12(const mac_entry_st *me,
 }
 
 /**
- * gnutls_pkcs12_generate_mac2:
+ * gnutls_pkcs12_generate_mac3:
  * @pkcs12: A pkcs12 type
  * @mac: the MAC algorithm to use
  * @pass: The password for the MAC
+ * @flags: an ORed sequence of gnutls_pkcs12_flags_t
  *
  * This function will generate a MAC for the PKCS12 structure.
  *
+ * If @flags contains %GNUTLS_PKCS12_USE_PBMAC1, it uses PBMAC1 key
+ * derivation function instead of the PKCS#12 one.
+ *
  * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
  *   negative error value.
  **/
-int gnutls_pkcs12_generate_mac2(gnutls_pkcs12_t pkcs12,
-                               gnutls_mac_algorithm_t mac, const char *pass)
+int gnutls_pkcs12_generate_mac3(gnutls_pkcs12_t pkcs12,
+                               gnutls_mac_algorithm_t mac, const char *pass,
+                               unsigned int flags)
 {
        uint8_t salt_data[8];
        gnutls_datum_t salt, key;
@@ -1009,13 +1058,23 @@ int gnutls_pkcs12_generate_mac2(gnutls_pkcs12_t pkcs12,
                goto cleanup;
        }
 
-       /* MAC the data.
-        */
        key.data = (void *)pass;
        key.size = strlen(pass);
 
-       result = generate_mac_pkcs12(me, &key, &salt, iter_count, &data,
-                                    pkcs12->pkcs12);
+       if (flags & GNUTLS_PKCS12_USE_PBMAC1) {
+               struct pbkdf2_params kdf_params;
+
+               memcpy(kdf_params.salt, salt.data, salt.size);
+               kdf_params.salt_size = salt.size;
+               kdf_params.iter_count = iter_count;
+               kdf_params.key_size = _gnutls_mac_get_algo_len(me);
+               kdf_params.mac = GNUTLS_MAC_SHA256;
+
+               result = generate_mac_pbmac1(me->id, &key, &kdf_params, &data,
+                                            pkcs12->pkcs12);
+       } else
+               result = generate_mac_pkcs12(me, &key, &salt, iter_count, &data,
+                                            pkcs12->pkcs12);
 
 cleanup:
        if (result < 0)
@@ -1025,8 +1084,9 @@ cleanup:
 }
 
 /**
- * gnutls_pkcs12_generate_mac:
+ * gnutls_pkcs12_generate_mac2:
  * @pkcs12: A pkcs12 type
+ * @mac: the MAC algorithm to use
  * @pass: The password for the MAC
  *
  * This function will generate a MAC for the PKCS12 structure.
@@ -1034,75 +1094,134 @@ cleanup:
  * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
  *   negative error value.
  **/
-int gnutls_pkcs12_generate_mac(gnutls_pkcs12_t pkcs12, const char *pass)
+int gnutls_pkcs12_generate_mac2(gnutls_pkcs12_t pkcs12,
+                               gnutls_mac_algorithm_t mac, const char *pass)
 {
-       return gnutls_pkcs12_generate_mac2(pkcs12, GNUTLS_MAC_SHA256, pass);
+       return gnutls_pkcs12_generate_mac3(pkcs12, mac, pass, 0);
 }
 
 /**
- * gnutls_pkcs12_verify_mac:
- * @pkcs12: should contain a gnutls_pkcs12_t type
+ * gnutls_pkcs12_generate_mac:
+ * @pkcs12: A pkcs12 type
  * @pass: The password for the MAC
  *
- * This function will verify the MAC for the PKCS12 structure.
+ * This function will generate a MAC for the PKCS12 structure.
  *
  * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
  *   negative error value.
  **/
-int gnutls_pkcs12_verify_mac(gnutls_pkcs12_t pkcs12, const char *pass)
+int gnutls_pkcs12_generate_mac(gnutls_pkcs12_t pkcs12, const char *pass)
+{
+       return gnutls_pkcs12_generate_mac2(pkcs12, GNUTLS_MAC_SHA256, pass);
+}
+
+static int pkcs12_verify_mac_pbmac1(gnutls_pkcs12_t pkcs12, const char *pass)
 {
-       uint8_t key[MAX_HASH_SIZE];
-       char oid[MAX_OID_SIZE];
        int result;
-       unsigned int iter;
        int len;
-       mac_hd_st td1;
-       gnutls_datum_t tmp = { NULL, 0 }, salt = { NULL, 0 };
+       gnutls_datum_t params = { NULL, 0 }, data = { NULL, 0 };
+       gnutls_datum_t key;
        uint8_t mac_output[MAX_HASH_SIZE];
        uint8_t mac_output_orig[MAX_HASH_SIZE];
-       gnutls_mac_algorithm_t algo;
-       unsigned mac_len, key_len;
-       const mac_entry_st *entry;
-#if ENABLE_GOST
-       int gost_retry = 0;
-#endif
+       struct pbkdf2_params kdf_params;
+       gnutls_mac_algorithm_t algo = GNUTLS_MAC_UNKNOWN;
+       const mac_entry_st *me;
 
-       if (pkcs12 == NULL) {
+       result = _gnutls_x509_read_value(
+               pkcs12->pkcs12, "macData.mac.digestAlgorithm.parameters",
+               &params);
+       if (result < 0) {
+               return gnutls_assert_val(result);
+       }
+
+       memset(&kdf_params, 0, sizeof(kdf_params));
+       result = _gnutls_read_pbmac1_params(params.data, params.size,
+                                           &kdf_params, &algo);
+       if (result < 0) {
                gnutls_assert();
-               return GNUTLS_E_INVALID_REQUEST;
+               goto cleanup;
        }
 
-       /* read the iterations
+       me = mac_to_entry(algo);
+       if (unlikely(me == NULL)) {
+               gnutls_assert();
+               result = GNUTLS_E_UNKNOWN_HASH_ALGORITHM;
+               goto cleanup;
+       }
+
+       /* Get the data to be MACed
         */
-       result = _gnutls_x509_read_uint(pkcs12->pkcs12, "macData.iterations",
-                                       &iter);
+       result = _decode_pkcs12_auth_safe(pkcs12->pkcs12, NULL, &data);
        if (result < 0) {
-               iter = 1; /* the default */
+               gnutls_assert();
+               goto cleanup;
        }
 
-       len = sizeof(oid);
-       result = asn1_read_value(pkcs12->pkcs12,
-                                "macData.mac.digestAlgorithm.algorithm", oid,
-                                &len);
+       key.data = (void *)pass;
+       key.size = strlen(pass);
+
+       result = _gnutls_pbmac1(me->id, &key, &kdf_params, &data, mac_output);
+       if (result < 0) {
+               gnutls_assert();
+               goto cleanup;
+       }
+
+       len = sizeof(mac_output_orig);
+       result = asn1_read_value(pkcs12->pkcs12, "macData.mac.digest",
+                                mac_output_orig, &len);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
-               return _gnutls_asn2err(result);
+               result = _gnutls_asn2err(result);
+               goto cleanup;
        }
 
-       algo = DIG_TO_MAC(gnutls_oid_to_digest(oid));
-       if (algo == GNUTLS_MAC_UNKNOWN) {
-       unknown_mac:
+       if ((unsigned)len != _gnutls_mac_get_algo_len(me) ||
+           memcmp(mac_output_orig, mac_output, len) != 0) {
                gnutls_assert();
-               return GNUTLS_E_UNKNOWN_HASH_ALGORITHM;
+               result = GNUTLS_E_MAC_VERIFY_FAILED;
+               goto cleanup;
        }
 
+cleanup:
+       _gnutls_free_datum(&params);
+       _gnutls_free_datum(&data);
+       return result;
+}
+
+static int pkcs12_verify_mac_pkcs12(gnutls_pkcs12_t pkcs12,
+                                   gnutls_mac_algorithm_t algo,
+                                   const char *pass)
+{
+       const mac_entry_st *entry;
+       uint8_t key[MAX_HASH_SIZE];
+       uint8_t mac_output[MAX_HASH_SIZE];
+       uint8_t mac_output_orig[MAX_HASH_SIZE];
+       gnutls_datum_t tmp = { NULL, 0 }, salt = { NULL, 0 };
+       unsigned mac_len, key_len;
+       int len;
+       mac_hd_st td1;
+       unsigned iter_count;
+#if ENABLE_GOST
+       int gost_retry = 0;
+#endif
+       int result;
+
        entry = mac_to_entry(algo);
-       if (entry == NULL)
-               goto unknown_mac;
+       if (unlikely(entry == NULL)) {
+               return gnutls_assert_val(GNUTLS_E_UNKNOWN_HASH_ALGORITHM);
+       }
 
        mac_len = _gnutls_mac_get_algo_len(entry);
        key_len = mac_len;
 
+       /* Read the iterations from the structure.
+        */
+       result = _gnutls_x509_read_uint(pkcs12->pkcs12, "macData.iterations",
+                                       &iter_count);
+       if (result < 0) {
+               iter_count = 1; /* the default */
+       }
+
        /* Read the salt from the structure.
         */
        result = _gnutls_x509_read_null_value(pkcs12->pkcs12, "macData.macSalt",
@@ -1115,14 +1234,14 @@ int gnutls_pkcs12_verify_mac(gnutls_pkcs12_t pkcs12, const char *pass)
        /* Generate the key.
         */
        result = _gnutls_pkcs12_string_to_key(entry, 3 /*MAC*/, salt.data,
-                                             salt.size, iter, pass, key_len,
-                                             key);
+                                             salt.size, iter_count, pass,
+                                             key_len, key);
        if (result < 0) {
                gnutls_assert();
                goto cleanup;
        }
 
-       /* Get the data to be MACed
+       /* Get the data to be MACed.
         */
        result = _decode_pkcs12_auth_safe(pkcs12->pkcs12, NULL, &tmp);
        if (result < 0) {
@@ -1135,7 +1254,7 @@ int gnutls_pkcs12_verify_mac(gnutls_pkcs12_t pkcs12, const char *pass)
 pkcs12_try_gost:
 #endif
 
-       /* MAC the data
+       /* MAC the data.
         */
        result = _gnutls_mac_init(&td1, entry, key, key_len);
        if (result < 0) {
@@ -1167,8 +1286,8 @@ pkcs12_try_gost:
                        gost_retry = 1;
                        key_len = 32;
                        result = _gnutls_pkcs12_gost_string_to_key(
-                               algo, salt.data, salt.size, iter, pass, key_len,
-                               key);
+                               algo, salt.data, salt.size, iter_count, pass,
+                               key_len, key);
                        if (result < 0) {
                                gnutls_assert();
                                goto cleanup;
@@ -1193,6 +1312,46 @@ cleanup:
        return result;
 }
 
+/**
+ * gnutls_pkcs12_verify_mac:
+ * @pkcs12: should contain a gnutls_pkcs12_t type
+ * @pass: The password for the MAC
+ *
+ * This function will verify the MAC for the PKCS12 structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value.
+ **/
+int gnutls_pkcs12_verify_mac(gnutls_pkcs12_t pkcs12, const char *pass)
+{
+       char oid[MAX_OID_SIZE];
+       int result;
+       int len;
+
+       if (pkcs12 == NULL) {
+               gnutls_assert();
+               return GNUTLS_E_INVALID_REQUEST;
+       }
+
+       len = sizeof(oid);
+       result = asn1_read_value(pkcs12->pkcs12,
+                                "macData.mac.digestAlgorithm.algorithm", oid,
+                                &len);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               return _gnutls_asn2err(result);
+       }
+
+       if (strcmp(oid, PBMAC1_OID) == 0) {
+               return pkcs12_verify_mac_pbmac1(pkcs12, pass);
+       } else {
+               gnutls_mac_algorithm_t algo;
+
+               algo = DIG_TO_MAC(gnutls_oid_to_digest(oid));
+               return pkcs12_verify_mac_pkcs12(pkcs12, algo, pass);
+       }
+}
+
 static int write_attributes(gnutls_pkcs12_bag_t bag, int elem, asn1_node c2,
                            const char *where)
 {
@@ -1917,7 +2076,11 @@ int gnutls_pkcs12_mac_info(gnutls_pkcs12_t pkcs12, unsigned int *mac,
                *oid = (char *)tmp.data;
        }
 
-       algo = DIG_TO_MAC(gnutls_oid_to_digest((char *)tmp.data));
+       if (strcmp((char *)tmp.data, PBMAC1_OID) == 0) {
+               algo = GNUTLS_MAC_PBMAC1;
+       } else {
+               algo = DIG_TO_MAC(gnutls_oid_to_digest((char *)tmp.data));
+       }
        if (algo == GNUTLS_MAC_UNKNOWN || mac_to_entry(algo) == NULL) {
                gnutls_assert();
                return GNUTLS_E_UNKNOWN_HASH_ALGORITHM;
index 7d1a952fa4352dcc664e2f7a2486a518d32e0224..db160b9a78a7696e868a8e11aadb5f3f94f2729c 100644 (file)
@@ -622,8 +622,8 @@ error:
 
 /* Reads the PBKDF2 parameters.
  */
-static int read_pbkdf2_params(asn1_node pasn, const gnutls_datum_t *der,
-                             struct pbkdf2_params *params)
+int _gnutls_read_pbkdf2_params(asn1_node pasn, const gnutls_datum_t *der,
+                              struct pbkdf2_params *params)
 {
        int params_start, params_end;
        int params_len, len, result;
@@ -680,7 +680,8 @@ static int read_pbkdf2_params(asn1_node pasn, const gnutls_datum_t *der,
                goto error;
        }
 
-       /* read the salt */
+       /* Read the salt.
+        */
        params->salt_size = sizeof(params->salt);
        result = asn1_read_value(pbkdf2_asn, "salt.specified", params->salt,
                                 &params->salt_size);
@@ -696,7 +697,7 @@ static int read_pbkdf2_params(asn1_node pasn, const gnutls_datum_t *der,
                goto error;
        }
 
-       /* read the iteration count 
+       /* Read the iteration count.
         */
        result = _gnutls_x509_read_uint(pbkdf2_asn, "iterationCount",
                                        &params->iter_count);
@@ -712,7 +713,7 @@ static int read_pbkdf2_params(asn1_node pasn, const gnutls_datum_t *der,
 
        _gnutls_hard_log("iterationCount: %d\n", params->iter_count);
 
-       /* read the keylength, if it is set.
+       /* Read the keyLength, if it is present.
         */
        result = _gnutls_x509_read_uint(pbkdf2_asn, "keyLength",
                                        &params->key_size);
@@ -720,7 +721,7 @@ static int read_pbkdf2_params(asn1_node pasn, const gnutls_datum_t *der,
                params->key_size = 0;
        }
 
-       if (params->key_size > MAX_CIPHER_KEY_SIZE) {
+       if (params->key_size > MAX_MAC_KEY_SIZE) {
                result = gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
                goto error;
        }
@@ -983,7 +984,7 @@ int _gnutls_read_pkcs_schema_params(schema_id *schema, const char *password,
                tmp.data = (uint8_t *)data;
                tmp.size = data_size;
 
-               result = read_pbkdf2_params(pasn, &tmp, kdf_params);
+               result = _gnutls_read_pbkdf2_params(pasn, &tmp, kdf_params);
                if (result < 0) {
                        gnutls_assert();
                        goto error;
@@ -1066,10 +1067,9 @@ error:
        return result;
 }
 
-static int _gnutls_pbes2_string_to_key(unsigned int pass_len,
-                                      const char *password,
-                                      const struct pbkdf2_params *kdf_params,
-                                      int key_size, uint8_t *key)
+int _gnutls_pbes2_string_to_key(unsigned int pass_len, const char *password,
+                               const struct pbkdf2_params *kdf_params,
+                               int key_size, uint8_t *key)
 {
        gnutls_datum_t _key;
        gnutls_datum_t salt;
@@ -1282,14 +1282,14 @@ error:
 
 /* Writes the PBKDF2 parameters.
  */
-static int write_pbkdf2_params(asn1_node pasn,
-                              const struct pbkdf2_params *kdf_params)
+int _gnutls_write_pbkdf2_params(asn1_node pasn,
+                               const struct pbkdf2_params *kdf_params)
 {
        int result;
        asn1_node pbkdf2_asn = NULL;
        const mac_entry_st *me;
 
-       /* Write the key derivation algorithm
+       /* Write the key derivation algorithm.
         */
        result = asn1_write_value(pasn, "keyDerivationFunc.algorithm",
                                  PBKDF2_OID, 1);
@@ -1315,7 +1315,7 @@ static int write_pbkdf2_params(asn1_node pasn,
                goto error;
        }
 
-       /* write the salt 
+       /* Write the salt.
         */
        result = asn1_write_value(pbkdf2_asn, "salt.specified",
                                  kdf_params->salt, kdf_params->salt_size);
@@ -1326,7 +1326,7 @@ static int write_pbkdf2_params(asn1_node pasn,
        }
        _gnutls_hard_log("salt.specified.size: %d\n", kdf_params->salt_size);
 
-       /* write the iteration count 
+       /* Write the iteration count.
         */
        result = _gnutls_x509_write_uint32(pbkdf2_asn, "iterationCount",
                                           kdf_params->iter_count);
@@ -1336,7 +1336,7 @@ static int write_pbkdf2_params(asn1_node pasn,
        }
        _gnutls_hard_log("iterationCount: %d\n", kdf_params->iter_count);
 
-       /* write the keylength, if it is set.
+       /* Write the keyLength, if it is set.
         */
        if (kdf_params->key_size > 0) {
                result = _gnutls_x509_write_uint32(pbkdf2_asn, "keyLength",
@@ -1369,8 +1369,8 @@ static int write_pbkdf2_params(asn1_node pasn,
                goto error;
        }
 
-       /* now encode them an put the DER output
-        * in the keyDerivationFunc.parameters
+       /* Now encode them an put the DER output in the
+        * keyDerivationFunc.parameters.
         */
        result = _gnutls_x509_der_encode_and_copy(
                pbkdf2_asn, "", pasn, "keyDerivationFunc.parameters", 0);
@@ -1616,7 +1616,7 @@ int _gnutls_pkcs_write_schema_params(schema_id schema, asn1_node pkcs8_asn,
                        return _gnutls_asn2err(result);
                }
 
-               result = write_pbkdf2_params(pasn, kdf_params);
+               result = _gnutls_write_pbkdf2_params(pasn, kdf_params);
                if (result < 0) {
                        gnutls_assert();
                        goto error;
@@ -1735,3 +1735,163 @@ error:
        }
        return result;
 }
+
+int _gnutls_pbmac1(gnutls_mac_algorithm_t mac, const gnutls_datum_t *key,
+                  const struct pbkdf2_params *params,
+                  const gnutls_datum_t *data, uint8_t *output)
+{
+       int result;
+       gnutls_datum_t salt;
+       uint8_t mac_key[MAX_HASH_SIZE];
+
+       /* Derive the MAC key */
+       salt.data = (void *)params->salt;
+       salt.size = params->salt_size;
+       result = gnutls_pbkdf2(params->mac, key, &salt, params->iter_count,
+                              mac_key, params->key_size);
+       if (result < 0)
+               return gnutls_assert_val(result);
+
+       /* Calculate the MAC */
+       result = gnutls_hmac_fast(mac, mac_key, params->key_size, data->data,
+                                 data->size, output);
+       if (result < 0)
+               return gnutls_assert_val(result);
+
+       return result;
+}
+
+static int read_pbmac1_auth(asn1_node pasn, const gnutls_datum_t *der)
+{
+       char oid[MAX_OID_SIZE];
+       int len;
+       int result;
+
+       len = sizeof(oid);
+       result =
+               asn1_read_value(pasn, "messageAuthScheme.algorithm", oid, &len);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               return _gnutls_asn2err(result);
+       }
+       _gnutls_hard_log("messageAuthScheme.algorithm: %s\n", oid);
+
+       return gnutls_oid_to_mac(oid);
+}
+
+int _gnutls_read_pbmac1_params(const uint8_t *data, int data_size,
+                              struct pbkdf2_params *kdf_params,
+                              gnutls_mac_algorithm_t *mac)
+{
+       asn1_node pasn = NULL;
+       int result;
+       gnutls_datum_t tmp;
+
+       if ((result = asn1_create_element(_gnutls_get_pkix(),
+                                         "PKIX1.pkcs-5-PBMAC1-params",
+                                         &pasn)) != ASN1_SUCCESS) {
+               gnutls_assert();
+               result = _gnutls_asn2err(result);
+               goto error;
+       }
+
+       result = _asn1_strict_der_decode(&pasn, data, data_size, NULL);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               result = _gnutls_asn2err(result);
+               goto error;
+       }
+
+       tmp.data = (uint8_t *)data;
+       tmp.size = data_size;
+
+       result = _gnutls_read_pbkdf2_params(pasn, &tmp, kdf_params);
+       if (result < 0) {
+               gnutls_assert();
+               goto error;
+       }
+
+       result = read_pbmac1_auth(pasn, &tmp);
+       if (result < 0) {
+               gnutls_assert();
+               goto error;
+       }
+       *mac = result;
+
+       /* The keyLength field must present and the minimum is 20 bytes.
+        */
+       if (kdf_params->key_size < 20) {
+               gnutls_assert();
+               result = GNUTLS_E_INSUFFICIENT_SECURITY;
+               goto error;
+       }
+
+       result = 0;
+
+error:
+       asn1_delete_structure2(&pasn, ASN1_DELETE_FLAG_ZEROIZE);
+       return result;
+}
+
+static int write_pbmac1_auth(asn1_node pasn, gnutls_mac_algorithm_t algo)
+{
+       int result;
+       const mac_entry_st *me = mac_to_entry(algo);
+
+       if (unlikely(me == NULL))
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+       result = asn1_write_value(pasn, "messageAuthScheme.algorithm",
+                                 me->mac_oid, 1);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               return _gnutls_asn2err(result);
+       }
+       _gnutls_hard_log("messageAuthScheme.algorithm: %s\n", me->oid);
+
+       result =
+               asn1_write_value(pasn, "messageAuthScheme.parameters", NULL, 0);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               return _gnutls_asn2err(result);
+       }
+
+       return 0;
+}
+
+int _gnutls_write_pbmac1_params(asn1_node pkcs12,
+                               const struct pbkdf2_params *kdf_params,
+                               gnutls_mac_algorithm_t algo, const char *where)
+{
+       int result;
+       asn1_node pasn = NULL;
+
+       if ((result = asn1_create_element(_gnutls_get_pkix(),
+                                         "PKIX1.pkcs-5-PBMAC1-params",
+                                         &pasn)) != ASN1_SUCCESS) {
+               gnutls_assert();
+               result = _gnutls_asn2err(result);
+               goto error;
+       }
+
+       result = _gnutls_write_pbkdf2_params(pasn, kdf_params);
+       if (result < 0) {
+               gnutls_assert();
+               goto error;
+       }
+
+       result = write_pbmac1_auth(pasn, algo);
+       if (result < 0) {
+               gnutls_assert();
+               goto error;
+       }
+
+       result = _gnutls_x509_der_encode_and_copy(pasn, "", pkcs12, where, 0);
+       if (result < 0) {
+               gnutls_assert();
+               goto error;
+       }
+error:
+       asn1_delete_structure2(&pasn, ASN1_DELETE_FLAG_ZEROIZE);
+       return result;
+}
index 4ec55bd75f97f66d5f5a609e6e57e9e86cc0b0f4..693a4dd9249f15b441c4d0235f741e4e8c22d7a8 100644 (file)
@@ -397,6 +397,24 @@ inline static int _gnutls_x509_crq_read_spki_params(gnutls_x509_crq_t crt,
                                             params);
 }
 
+/* pkcs7-crypto.c */
+int _gnutls_pbes2_string_to_key(unsigned int pass_len, const char *password,
+                               const struct pbkdf2_params *kdf_params,
+                               int key_size, uint8_t *key);
+int _gnutls_read_pbkdf2_params(asn1_node pasn, const gnutls_datum_t *der,
+                              struct pbkdf2_params *params);
+int _gnutls_write_pbkdf2_params(asn1_node pasn,
+                               const struct pbkdf2_params *kdf_params);
+int _gnutls_pbmac1(gnutls_mac_algorithm_t mac, const gnutls_datum_t *key,
+                  const struct pbkdf2_params *params,
+                  const gnutls_datum_t *data, uint8_t *output);
+int _gnutls_read_pbmac1_params(const uint8_t *data, int data_size,
+                              struct pbkdf2_params *kdf_params,
+                              gnutls_mac_algorithm_t *mac);
+int _gnutls_write_pbmac1_params(asn1_node pkcs12,
+                               const struct pbkdf2_params *kdf_params,
+                               gnutls_mac_algorithm_t algo, const char *where);
+
 /* pkcs12.h */
 #include <gnutls/pkcs12.h>
 
index 8c5ead34b3541d5b46f30cf73b4ed51f6261dfb8..0cd5930be7f5fef6658e8f9875f01bbbf7cf7e6e 100644 (file)
           "long-option": "to-p12",
           "description": "Generate a PKCS #12 structure",
           "detail": "It requires a certificate, a private key and possibly a CA certificate to be specified."
+        },
+        {
+          "long-option": "pbmac1",
+          "description": "Use PBMAC1 in a PKCS #12 structure"
         }
       ]
     },
index b19e4970f3b722d2acc9ce22589843c4f9b10504..622fd8f064cb693b1f4695bb9141e7b13f943cb1 100644 (file)
@@ -2959,6 +2959,7 @@ void generate_pkcs8(common_info_st *cinfo)
 void generate_pkcs12(common_info_st *cinfo)
 {
        gnutls_pkcs12_t pkcs12;
+       gnutls_pkcs12_flags_t pkcs12_flags = 0;
        gnutls_x509_crl_t *crls;
        gnutls_x509_crt_t *crts, ca_crt;
        gnutls_x509_privkey_t *keys;
@@ -3001,6 +3002,9 @@ void generate_pkcs12(common_info_st *cinfo)
                name = get_pkcs12_key_name();
        }
 
+       if (HAVE_OPT(PBMAC1))
+               pkcs12_flags |= GNUTLS_PKCS12_USE_PBMAC1;
+
        result = gnutls_pkcs12_init(&pkcs12);
        if (result < 0) {
                fprintf(stderr, "pkcs12_init: %s\n", gnutls_strerror(result));
@@ -3215,7 +3219,7 @@ void generate_pkcs12(common_info_st *cinfo)
                gnutls_pkcs12_bag_deinit(kbag);
        }
 
-       result = gnutls_pkcs12_generate_mac2(pkcs12, mac, pass);
+       result = gnutls_pkcs12_generate_mac3(pkcs12, mac, pass, pkcs12_flags);
        if (result < 0) {
                fprintf(stderr, "generate_mac: %s\n", gnutls_strerror(result));
                app_exit(1);
index dac615fa54b2cf6308ab5e6e46fdc6045d112e5f..9c71c083bd82dea883d3764af566240eade833b3 100644 (file)
@@ -102,6 +102,11 @@ EXTRA_DIST = data/ca-no-pathlen.pem data/no-ca-or-pathlen.pem data/aki-cert.pem
        data/chain-512-leaf.pem data/chain-512-subca.pem data/chain-512-ca.pem \
        templates/template-no-ca-honor.tmpl templates/template-no-ca-explicit.tmpl \
        data/crq-cert-no-ca-explicit.pem data/crq-cert-no-ca-honor.pem data/commonName.cer \
+       data/pbmac1_256_256.bad-iter.p12 data/pbmac1_256_256.bad-salt.p12 \
+       data/pbmac1_256_256.good.p12 data/pbmac1_256_256.no-len.p12 \
+       data/pbmac1_256_256.short-len.p12 \
+       data/pbmac1_512_256.good.p12 data/pbmac1_512_512.good.p12 \
+       data/pbmac1-simple.p12 \
        templates/simple-policy.tmpl data/simple-policy.pem templates/template-negative-serial.tmpl \
        templates/template-encryption-only.tmpl
 
@@ -110,7 +115,7 @@ dist_check_SCRIPTS = pathlen.sh aki.sh invalid-sig.sh email.sh \
        provable-dh.sh sha2-test.sh sha2-dsa-test.sh provable-privkey-dsa2048.sh \
        provable-privkey-rsa2048.sh provable-privkey-gen-default.sh pkcs7-constraints.sh \
        pkcs7-constraints2.sh certtool-long-oids.sh pkcs7-cat.sh cert-sanity.sh cert-critical.sh \
-       pkcs12.sh certtool-crl-decoding.sh pkcs12-encode.sh pkcs12-corner-cases.sh inhibit-anypolicy.sh \
+       pkcs12.sh certtool-crl-decoding.sh pkcs12-encode.sh pkcs12-corner-cases.sh pkcs12-pbmac1.sh inhibit-anypolicy.sh \
        smime.sh cert-time.sh alt-chain.sh pkcs7-list-sign.sh pkcs7-eddsa.sh certtool-ecdsa.sh \
        key-id.sh pkcs8.sh pkcs8-decode.sh ecdsa.sh illegal-rsa.sh pkcs8-invalid.sh key-invalid.sh \
        pkcs8-eddsa.sh certtool-subca.sh certtool-verify-profiles.sh x509-duplicate-ext.sh x25519-and-x448.sh \
diff --git a/tests/cert-tests/data/pbmac1-simple.p12 b/tests/cert-tests/data/pbmac1-simple.p12
new file mode 100644 (file)
index 0000000..3605567
Binary files /dev/null and b/tests/cert-tests/data/pbmac1-simple.p12 differ
diff --git a/tests/cert-tests/data/pbmac1_256_256.bad-iter.p12 b/tests/cert-tests/data/pbmac1_256_256.bad-iter.p12
new file mode 100644 (file)
index 0000000..9957d47
Binary files /dev/null and b/tests/cert-tests/data/pbmac1_256_256.bad-iter.p12 differ
diff --git a/tests/cert-tests/data/pbmac1_256_256.bad-salt.p12 b/tests/cert-tests/data/pbmac1_256_256.bad-salt.p12
new file mode 100644 (file)
index 0000000..fef1e51
Binary files /dev/null and b/tests/cert-tests/data/pbmac1_256_256.bad-salt.p12 differ
diff --git a/tests/cert-tests/data/pbmac1_256_256.good.p12 b/tests/cert-tests/data/pbmac1_256_256.good.p12
new file mode 100644 (file)
index 0000000..b8c8c2d
Binary files /dev/null and b/tests/cert-tests/data/pbmac1_256_256.good.p12 differ
diff --git a/tests/cert-tests/data/pbmac1_256_256.no-len.p12 b/tests/cert-tests/data/pbmac1_256_256.no-len.p12
new file mode 100644 (file)
index 0000000..35ebe05
Binary files /dev/null and b/tests/cert-tests/data/pbmac1_256_256.no-len.p12 differ
diff --git a/tests/cert-tests/data/pbmac1_256_256.short-len.p12 b/tests/cert-tests/data/pbmac1_256_256.short-len.p12
new file mode 100644 (file)
index 0000000..94b688d
Binary files /dev/null and b/tests/cert-tests/data/pbmac1_256_256.short-len.p12 differ
diff --git a/tests/cert-tests/data/pbmac1_512_256.good.p12 b/tests/cert-tests/data/pbmac1_512_256.good.p12
new file mode 100644 (file)
index 0000000..e8d4899
Binary files /dev/null and b/tests/cert-tests/data/pbmac1_512_256.good.p12 differ
diff --git a/tests/cert-tests/data/pbmac1_512_512.good.p12 b/tests/cert-tests/data/pbmac1_512_512.good.p12
new file mode 100644 (file)
index 0000000..64e1434
Binary files /dev/null and b/tests/cert-tests/data/pbmac1_512_512.good.p12 differ
diff --git a/tests/cert-tests/pkcs12-pbmac1.sh b/tests/cert-tests/pkcs12-pbmac1.sh
new file mode 100644 (file)
index 0000000..696ff30
--- /dev/null
@@ -0,0 +1,112 @@
+#!/bin/sh
+
+# Copyright (C) 2004-2006, 2008, 2010, 2012, 2024 Free Software Foundation,
+# Inc.
+#
+# Author: Daiki Ueno
+#
+# This file is part of GnuTLS.
+#
+# GnuTLS is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 3 of the License, or (at
+# your option) any later version.
+#
+# GnuTLS 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GnuTLS.  If not, see <https://www.gnu.org/licenses/>.
+
+: ${srcdir=.}
+: ${CERTTOOL=../../src/certtool${EXEEXT}}
+
+if ! test -x "${CERTTOOL}"; then
+       exit 77
+fi
+
+if ! test -z "${VALGRIND}"; then
+       VALGRIND="${LIBTOOL:-libtool} --mode=execute ${VALGRIND} --error-exitcode=1"
+fi
+
+: ${DIFF=diff}
+DEBUG=""
+
+. "${srcdir}/../scripts/common.sh"
+testdir=`create_testdir pkcs12`
+
+TMPFILE=$testdir/pkcs12
+TMPFILE_PEM=$testdir/pkcs12.pem
+
+DEBUG="1"
+
+GOOD="
+pbmac1_256_256.good.p12
+pbmac1_512_256.good.p12
+pbmac1_512_512.good.p12
+pbmac1-simple.p12
+"
+
+BAD="
+pbmac1_256_256.bad-iter.p12
+pbmac1_256_256.bad-salt.p12
+pbmac1_256_256.no-len.p12
+pbmac1_256_256.short-len.p12
+"
+
+for p12 in $GOOD; do
+       set -- ${p12}
+       file="$1"
+
+       if test "x$DEBUG" != "x"; then
+               ${VALGRIND} "${CERTTOOL}" -d 99 --p12-info --inder --password 1234 \
+                       --infile "${srcdir}/data/${file}"
+       else
+               ${VALGRIND} "${CERTTOOL}" --p12-info --inder --password 1234 \
+                       --infile "${srcdir}/data/${file}" >/dev/null
+       fi
+       rc=$?
+       if test ${rc} != 0; then
+               echo "PKCS12 FATAL ${p12}"
+               exit 1
+       fi
+done
+
+for p12 in $BAD; do
+       set -- ${p12}
+       file="$1"
+
+       if test "x$DEBUG" != "x"; then
+               ${VALGRIND} "${CERTTOOL}" -d 99 --p12-info --inder --password 1234 \
+                       --infile "${srcdir}/data/${file}"
+       else
+               ${VALGRIND} "${CERTTOOL}" --p12-info --inder --password 1234 \
+                       --infile "${srcdir}/data/${file}" >/dev/null
+       fi
+       rc=$?
+       if test ${rc} = 0; then
+               echo "PKCS12 FATAL ${p12}"
+               exit 1
+       fi
+done
+
+# test whether we can encode a certificate and a key
+${VALGRIND} "${CERTTOOL}" --to-p12 --pbmac1 --password 1234 --p12-name "my-key" --load-certificate "${srcdir}/../certs/cert-ecc256.pem" --load-privkey "${srcdir}/../certs/ecc256.pem" --outder --outfile $TMPFILE >/dev/null
+rc=$?
+if test ${rc} != 0; then
+       echo "PKCS12 FATAL encoding"
+       exit 1
+fi
+
+${VALGRIND} "${CERTTOOL}" --p12-info --inder --password 1234 --infile $TMPFILE|tr -d '\r' >${TMPFILE_PEM} 2>/dev/null
+rc=$?
+if test ${rc} != 0; then
+       echo "PKCS12 FATAL decrypting/decoding"
+       exit 1
+fi
+
+rm -rf "${testdir}"
+
+exit 0