]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
x509: support PBES1-DES-SHA1
authorDaiki Ueno <ueno@gnu.org>
Fri, 22 Mar 2024 03:47:00 +0000 (12:47 +0900)
committerDaiki Ueno <ueno@gnu.org>
Tue, 26 Mar 2024 13:56:18 +0000 (22:56 +0900)
PBES1 with single DES backed by SHA-1 hash is used to parse legacy
PKCS#8 file in GCR.

Signed-off-by: Daiki Ueno <ueno@gnu.org>
lib/includes/gnutls/x509.h
lib/x509/pkcs7-crypt.c
lib/x509/pkcs7_int.h
lib/x509/privkey_pkcs8_pbes1.c
tests/cert-tests/data/der-key-PBE-SHA1-DES.p8 [new file with mode: 0644]
tests/cert-tests/pkcs8-decode.sh

index c89255cb1bbaaac35b63108bba43dae161caca00..b49577a24193488ae2217b7b7fa9723549377140 100644 (file)
@@ -1012,6 +1012,7 @@ unsigned gnutls_x509_crt_check_key_purpose(gnutls_x509_crt_t cert,
  * @GNUTLS_PKCS_PBES2_AES_256: PBES2 AES-256.
  * @GNUTLS_PKCS_PBES2_DES: PBES2 single DES.
  * @GNUTLS_PKCS_PBES1_DES_MD5: PBES1 with single DES; for compatibility with openssl only.
+ * @GNUTLS_PKCS_PBES1_DES_SHA1: PBES1 with single DES; for compatibility purposes only.
  * @GNUTLS_PKCS_PBES2_GOST_TC26Z: PBES2 GOST 28147-89 CFB with TC26-Z S-box.
  * @GNUTLS_PKCS_PBES2_GOST_CPA: PBES2 GOST 28147-89 CFB with CryptoPro-A S-box.
  * @GNUTLS_PKCS_PBES2_GOST_CPB: PBES2 GOST 28147-89 CFB with CryptoPro-B S-box.
@@ -1036,7 +1037,8 @@ typedef enum gnutls_pkcs_encrypt_flags_t {
        GNUTLS_PKCS_PBES2_GOST_CPA = 1 << 12,
        GNUTLS_PKCS_PBES2_GOST_CPB = 1 << 13,
        GNUTLS_PKCS_PBES2_GOST_CPC = 1 << 14,
-       GNUTLS_PKCS_PBES2_GOST_CPD = 1 << 15
+       GNUTLS_PKCS_PBES2_GOST_CPD = 1 << 15,
+       GNUTLS_PKCS_PBES1_DES_SHA1 = 1 << 16
 } gnutls_pkcs_encrypt_flags_t;
 
 #define GNUTLS_PKCS_CIPHER_MASK(x) ((x) & (~(GNUTLS_PKCS_NULL_PASSWORD)))
index d217aece8ce31a0e38ac3cd15a553a1445bc88de..0b1b57fe2a28ced37fe43bd9a0cb22e515c4703b 100644 (file)
@@ -38,6 +38,7 @@
 #include "pk.h"
 
 #define PBES1_DES_MD5_OID "1.2.840.113549.1.5.3"
+#define PBES1_DES_SHA1_OID "1.2.840.113549.1.5.10"
 
 #define PBES2_OID "1.2.840.113549.1.5.13"
 #define PBKDF2_OID "1.2.840.113549.1.5.12"
@@ -63,6 +64,16 @@ static const struct pkcs_cipher_schema_st avail_pkcs_cipher_schemas[] = {
          .desc = NULL,
          .iv_name = NULL,
          .decrypt_only = 1 },
+       { .schema = PBES1_DES_SHA1,
+         .name = "PBES1-DES-CBC-SHA1",
+         .flag = GNUTLS_PKCS_PBES1_DES_SHA1,
+         .cipher = GNUTLS_CIPHER_DES_CBC,
+         .pbes2 = 0,
+         .cipher_oid = PBES1_DES_SHA1_OID,
+         .write_oid = PBES1_DES_SHA1_OID,
+         .desc = NULL,
+         .iv_name = NULL,
+         .decrypt_only = 1 },
        { .schema = PBES2_3DES,
          .name = "PBES2-3DES-CBC",
          .flag = GNUTLS_PKCS_PBES2_3DES,
@@ -995,7 +1006,7 @@ int _gnutls_read_pkcs_schema_params(schema_id *schema, const char *password,
 
                *schema = p->schema;
                return 0;
-       } else if (*schema == PBES1_DES_MD5) {
+       } else if (*schema == PBES1_DES_MD5 || *schema == PBES1_DES_SHA1) {
                return _gnutls_read_pbkdf1_params(data, data_size, kdf_params,
                                                  enc_params);
        } else { /* PKCS #12 schema */
@@ -1117,6 +1128,14 @@ int _gnutls_pkcs_raw_decrypt_data(schema_id schema, asn1_node pkcs8_asn,
                if (ret < 0)
                        goto error;
                goto cleanup;
+       } else if (schema == PBES1_DES_SHA1) {
+               ret = _gnutls_decrypt_pbes1_des_sha1_data(password, pass_len,
+                                                         kdf_params,
+                                                         enc_params, &enc,
+                                                         decrypted_data);
+               if (ret < 0)
+                       goto error;
+               goto cleanup;
        }
 
        if (kdf_params->key_size == 0) {
index 77427c1e07a2ac6bedfbfcb691461aa5e2dbe808..58129b4b94b6571fd76e42109ba9cc54d90e43ee 100644 (file)
@@ -50,7 +50,8 @@ typedef enum schema_id {
        PKCS12_3DES_SHA1, /* the stuff in PKCS #12 */
        PKCS12_ARCFOUR_SHA1,
        PKCS12_RC2_40_SHA1,
-       PBES1_DES_MD5 /* openssl before 1.1.0 uses that by default */
+       PBES1_DES_MD5, /* openssl before 1.1.0 uses that by default */
+       PBES1_DES_SHA1 /* for compatibility */
 } schema_id;
 
 struct pkcs_cipher_schema_st {
@@ -83,6 +84,13 @@ int _gnutls_decrypt_pbes1_des_md5_data(const char *password,
                                       const gnutls_datum_t *encrypted_data,
                                       gnutls_datum_t *decrypted_data);
 
+int _gnutls_decrypt_pbes1_des_sha1_data(const char *password,
+                                       unsigned password_len,
+                                       const struct pbkdf2_params *kdf_params,
+                                       const struct pbe_enc_params *enc_params,
+                                       const gnutls_datum_t *encrypted_data,
+                                       gnutls_datum_t *decrypted_data);
+
 int _gnutls_check_pkcs_cipher_schema(const char *oid);
 
 int _gnutls_pkcs_raw_decrypt_data(schema_id schema, asn1_node pkcs8_asn,
index 4f5bdb093dbee8a0e3762b06d93e2d81229929ff..f8f3d7e670c7b4ece4175e32e93e2585a0573a77 100644 (file)
@@ -31,7 +31,6 @@
 #include "x509_int.h"
 #include "pkcs7_int.h"
 #include "algorithms.h"
-#include <nettle/md5.h>
 
 /* This file includes support for PKCS#8 PBES1 with DES and MD5.
  * We only support decryption for compatibility with other software.
@@ -99,44 +98,53 @@ error:
        return ret;
 }
 
-static void pbkdf1_md5(const char *password, unsigned password_len,
-                      const uint8_t salt[8], unsigned iter_count,
-                      unsigned key_size, uint8_t *key)
+static int pbkdf1(gnutls_digest_algorithm_t dig, const char *password,
+                 unsigned password_len, const uint8_t salt[8],
+                 unsigned iter_count, unsigned key_size, uint8_t *key)
 {
-       struct md5_ctx ctx;
-       uint8_t tmp[16];
+       uint8_t tmp[20];
        unsigned i;
+       gnutls_hash_hd_t hd;
+       int ret;
 
        if (key_size > sizeof(tmp))
                abort();
 
-       for (i = 0; i < iter_count; i++) {
-               md5_init(&ctx);
-               if (i == 0) {
-                       md5_update(&ctx, password_len, (uint8_t *)password);
-                       md5_update(&ctx, 8, salt);
-                       md5_digest(&ctx, 16, tmp);
-               } else {
-                       md5_update(&ctx, 16, tmp);
-                       md5_digest(&ctx, 16, tmp);
-               }
+       ret = gnutls_hash_init(&hd, dig);
+       if (ret < 0)
+               return gnutls_assert_val(ret);
+       ret = gnutls_hash(hd, (uint8_t *)password, password_len);
+       if (ret < 0)
+               return gnutls_assert_val(ret);
+       ret = gnutls_hash(hd, salt, 8);
+       if (ret < 0)
+               return gnutls_assert_val(ret);
+       gnutls_hash_deinit(hd, tmp);
+
+       for (i = 1; i < iter_count; i++) {
+               ret = gnutls_hash_init(&hd, dig);
+               if (ret < 0)
+                       return gnutls_assert_val(ret);
+               ret = gnutls_hash(hd, tmp, gnutls_hash_get_len(dig));
+               if (ret < 0)
+                       return gnutls_assert_val(ret);
+               gnutls_hash_deinit(hd, tmp);
        }
 
        memcpy(key, tmp, key_size);
-       return;
+       return 0;
 }
 
-int _gnutls_decrypt_pbes1_des_md5_data(const char *password,
-                                      unsigned password_len,
-                                      const struct pbkdf2_params *kdf_params,
-                                      const struct pbe_enc_params *enc_params,
-                                      const gnutls_datum_t *encrypted_data,
-                                      gnutls_datum_t *decrypted_data)
+static int _gnutls_decrypt_pbes1_des_data(
+       gnutls_digest_algorithm_t dig, const char *password,
+       unsigned password_len, const struct pbkdf2_params *kdf_params,
+       const struct pbe_enc_params *enc_params,
+       const gnutls_datum_t *encrypted_data, gnutls_datum_t *decrypted_data)
 {
        int result;
        gnutls_datum_t dkey, d_iv;
        gnutls_cipher_hd_t ch;
-       uint8_t key[16];
+       uint8_t key[20];
        const unsigned block_size = 8;
 
        if (enc_params->cipher != GNUTLS_CIPHER_DES_CBC)
@@ -147,8 +155,12 @@ int _gnutls_decrypt_pbes1_des_md5_data(const char *password,
 
        /* generate the key
         */
-       pbkdf1_md5(password, password_len, kdf_params->salt,
-                  kdf_params->iter_count, sizeof(key), key);
+       result = pbkdf1(dig, password, password_len, kdf_params->salt,
+                       kdf_params->iter_count, gnutls_hash_get_len(dig), key);
+       if (result < 0) {
+               _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+               return gnutls_assert_val(result);
+       }
 
        dkey.data = key;
        dkey.size = 8;
@@ -186,3 +198,29 @@ error:
 
        return result;
 }
+
+int _gnutls_decrypt_pbes1_des_md5_data(const char *password,
+                                      unsigned password_len,
+                                      const struct pbkdf2_params *kdf_params,
+                                      const struct pbe_enc_params *enc_params,
+                                      const gnutls_datum_t *encrypted_data,
+                                      gnutls_datum_t *decrypted_data)
+{
+       return _gnutls_decrypt_pbes1_des_data(GNUTLS_DIG_MD5, password,
+                                             password_len, kdf_params,
+                                             enc_params, encrypted_data,
+                                             decrypted_data);
+}
+
+int _gnutls_decrypt_pbes1_des_sha1_data(const char *password,
+                                       unsigned password_len,
+                                       const struct pbkdf2_params *kdf_params,
+                                       const struct pbe_enc_params *enc_params,
+                                       const gnutls_datum_t *encrypted_data,
+                                       gnutls_datum_t *decrypted_data)
+{
+       return _gnutls_decrypt_pbes1_des_data(GNUTLS_DIG_SHA1, password,
+                                             password_len, kdf_params,
+                                             enc_params, encrypted_data,
+                                             decrypted_data);
+}
diff --git a/tests/cert-tests/data/der-key-PBE-SHA1-DES.p8 b/tests/cert-tests/data/der-key-PBE-SHA1-DES.p8
new file mode 100644 (file)
index 0000000..9989613
Binary files /dev/null and b/tests/cert-tests/data/der-key-PBE-SHA1-DES.p8 differ
index ddb284eb7e6e7ffd0b42c96c1d8b7b8b0f13f7b3..465a208c88da0425e27ba2940e2152e454323a4a 100644 (file)
@@ -53,6 +53,21 @@ for p8 in "pkcs8-pbes1-des-md5.pem password" "encpkcs8.pem foobar" "unencpkcs8.p
        fi
 done
 
+for p8 in "der-key-PBE-SHA1-DES.p8 booo"; do
+       set -- ${p8}
+       file="$1"
+       passwd="$2"
+       ${VALGRIND} "${CERTTOOL}" --key-info --pkcs8 --inder \
+                   --password "${passwd}" --infile "${srcdir}/data/${file}"
+       rc=$?
+       if test ${rc} != 0; then
+               echo "PKCS8 FATAL ${p8}"
+               ret=1
+       else
+               echo "PKCS8 OK ${p8}"
+       fi
+done
+
 for p8 in "openssl-aes128.p8" "openssl-aes256.p8" "openssl-3des.p8"; do
        set -- ${p8}
        file="$1"