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
# 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
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
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
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
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
.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 }
};
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;
/**
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);
*;
} 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;
keyDerivationFunc AlgorithmIdentifier,
encryptionScheme AlgorithmIdentifier }
+pkcs-5-PBMAC1-params ::= SEQUENCE {
+ keyDerivationFunc AlgorithmIdentifier,
+ messageAuthScheme AlgorithmIdentifier }
+
-- PBKDF2
-- pkcs-5-algid-hmacWithSHA1 AlgorithmIdentifier ::=
#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.
*/
}
#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,
{
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;
_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) {
}
/**
- * 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;
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)
}
/**
- * 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.
* 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",
+ ¶ms);
+ 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(¶ms);
+ _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",
/* 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) {
pkcs12_try_gost:
#endif
- /* MAC the data
+ /* MAC the data.
*/
result = _gnutls_mac_init(&td1, entry, key, key_len);
if (result < 0) {
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;
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)
{
*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;
/* 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;
goto error;
}
- /* read the salt */
+ /* Read the salt.
+ */
params->salt_size = sizeof(params->salt);
result = asn1_read_value(pbkdf2_asn, "salt.specified", params->salt,
¶ms->salt_size);
goto error;
}
- /* read the iteration count
+ /* Read the iteration count.
*/
result = _gnutls_x509_read_uint(pbkdf2_asn, "iterationCount",
¶ms->iter_count);
_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",
¶ms->key_size);
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;
}
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;
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;
/* 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);
goto error;
}
- /* write the salt
+ /* Write the salt.
*/
result = asn1_write_value(pbkdf2_asn, "salt.specified",
kdf_params->salt, kdf_params->salt_size);
}
_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);
}
_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",
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);
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;
}
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;
+}
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>
"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"
}
]
},
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;
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));
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);
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
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 \
--- /dev/null
+#!/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