openssl/0124-PBMAC1-PKCS12-FIPS-support.patch
2024-08-07 10:57:04 +02:00

1464 lines
63 KiB
Diff

From d959252c47af0eb0dd55bc032606901fedaf029b Mon Sep 17 00:00:00 2001
From: Dmitry Belyavskiy <beldmit@gmail.com>
Date: Fri, 7 Jun 2024 14:37:57 +0200
Subject: [PATCH 1/4] Implementation of the RFC 9579, PBMAC1 in PKCS#12
---
apps/pkcs12.c | 63 ++++++--
crypto/asn1/p5_pbev2.c | 7 +
crypto/evp/digest.c | 54 +++++++
crypto/pkcs12/p12_mutl.c | 296 ++++++++++++++++++++++++++++++++----
include/crypto/evp.h | 3 +
include/openssl/pkcs12.h.in | 3 +
include/openssl/x509.h.in | 15 +-
7 files changed, 394 insertions(+), 47 deletions(-)
diff --git a/apps/pkcs12.c b/apps/pkcs12.c
index 54323a9713393..cbe133742a8be 100644
--- a/apps/pkcs12.c
+++ b/apps/pkcs12.c
@@ -70,7 +70,7 @@ typedef enum OPTION_choice {
OPT_NAME, OPT_CSP, OPT_CANAME,
OPT_IN, OPT_OUT, OPT_PASSIN, OPT_PASSOUT, OPT_PASSWORD, OPT_CAPATH,
OPT_CAFILE, OPT_CASTORE, OPT_NOCAPATH, OPT_NOCAFILE, OPT_NOCASTORE, OPT_ENGINE,
- OPT_R_ENUM, OPT_PROV_ENUM, OPT_JDKTRUST,
+ OPT_R_ENUM, OPT_PROV_ENUM, OPT_JDKTRUST, OPT_PBMAC1_PBKDF2, OPT_PBMAC1_PBKDF2_MD,
#ifndef OPENSSL_NO_DES
OPT_LEGACY_ALG
#endif
@@ -147,6 +147,8 @@ const OPTIONS pkcs12_options[] = {
#endif
{"macalg", OPT_MACALG, 's',
"Digest algorithm to use in MAC (default SHA256)"},
+ {"pbmac1_pbkdf2", OPT_PBMAC1_PBKDF2, '-', "Use PBMAC1 with PBKDF2 instead of MAC"},
+ {"pbmac1_pbkdf2_md", OPT_PBMAC1_PBKDF2_MD, 's', "Digest to use for PBMAC1 KDF (default SHA256)"},
{"iter", OPT_ITER, 'p', "Specify the iteration count for encryption and MAC"},
{"noiter", OPT_NOITER, '-', "Don't use encryption iteration"},
{"nomaciter", OPT_NOMACITER, '-', "Don't use MAC iteration)"},
@@ -170,14 +172,14 @@ int pkcs12_main(int argc, char **argv)
int use_legacy = 0;
#endif
/* use library defaults for the iter, maciter, cert, and key PBE */
- int iter = 0, maciter = 0;
+ int iter = 0, maciter = 0, pbmac1_pbkdf2 = 0;
int macsaltlen = PKCS12_SALT_LEN;
int cert_pbe = NID_undef;
int key_pbe = NID_undef;
int ret = 1, macver = 1, add_lmk = 0, private = 0;
int noprompt = 0;
char *passinarg = NULL, *passoutarg = NULL, *passarg = NULL;
- char *passin = NULL, *passout = NULL, *macalg = NULL;
+ char *passin = NULL, *passout = NULL, *macalg = NULL, *pbmac1_pbkdf2_md = NULL;
char *cpass = NULL, *mpass = NULL, *badpass = NULL;
const char *CApath = NULL, *CAfile = NULL, *CAstore = NULL, *prog;
int noCApath = 0, noCAfile = 0, noCAstore = 0;
@@ -283,6 +285,12 @@ int pkcs12_main(int argc, char **argv)
case OPT_MACALG:
macalg = opt_arg();
break;
+ case OPT_PBMAC1_PBKDF2:
+ pbmac1_pbkdf2 = 1;
+ break;
+ case OPT_PBMAC1_PBKDF2_MD:
+ pbmac1_pbkdf2_md = opt_arg();
+ break;
case OPT_CERTPBE:
if (!set_pbe(&cert_pbe, opt_arg()))
goto opthelp;
@@ -700,10 +708,20 @@ int pkcs12_main(int argc, char **argv)
}
if (maciter != -1) {
- if (!PKCS12_set_mac(p12, mpass, -1, NULL, macsaltlen, maciter, macmd)) {
- BIO_printf(bio_err, "Error creating PKCS12 MAC; no PKCS12KDF support?\n");
- BIO_printf(bio_err, "Use -nomac if MAC not required and PKCS12KDF support not available.\n");
- goto export_end;
+ if (pbmac1_pbkdf2 == 1) {
+ if (!PKCS12_set_pbmac1_pbkdf2(p12, mpass, -1, NULL,
+ macsaltlen, maciter,
+ macmd, pbmac1_pbkdf2_md)) {
+ BIO_printf(bio_err, "Error creating PBMAC1\n");
+ goto export_end;
+ }
+ } else {
+ if (!PKCS12_set_mac(p12, mpass, -1, NULL, macsaltlen, maciter, macmd)) {
+ BIO_printf(bio_err, "Error creating PKCS12 MAC; no PKCS12KDF support?\n");
+ BIO_printf(bio_err,
+ "Use -nomac or -pbmac1_pbkdf2 if PKCS12KDF support not available\n");
+ goto export_end;
+ }
}
}
assert(private);
@@ -774,11 +792,32 @@ int pkcs12_main(int argc, char **argv)
X509_ALGOR_get0(&macobj, NULL, NULL, macalgid);
BIO_puts(bio_err, "MAC: ");
i2a_ASN1_OBJECT(bio_err, macobj);
- BIO_printf(bio_err, ", Iteration %ld\n",
- tmaciter != NULL ? ASN1_INTEGER_get(tmaciter) : 1L);
- BIO_printf(bio_err, "MAC length: %ld, salt length: %ld\n",
- tmac != NULL ? ASN1_STRING_length(tmac) : 0L,
- tsalt != NULL ? ASN1_STRING_length(tsalt) : 0L);
+ if (OBJ_obj2nid(macobj) == NID_pbmac1) {
+ PBKDF2PARAM *pbkdf2_param = PBMAC1_get1_pbkdf2_param(macalgid);
+
+ if (pbkdf2_param == NULL) {
+ BIO_printf(bio_err, ", Unsupported KDF or params for PBMAC1\n");
+ } else {
+ const ASN1_OBJECT *prfobj;
+
+ BIO_printf(bio_err, " using PBKDF2, Iteration %ld\n",
+ ASN1_INTEGER_get(pbkdf2_param->iter));
+ BIO_printf(bio_err, "Key length: %ld, Salt length: %d\n",
+ ASN1_INTEGER_get(pbkdf2_param->keylength),
+ ASN1_STRING_length(pbkdf2_param->salt->value.octet_string));
+ X509_ALGOR_get0(&prfobj, NULL, NULL, pbkdf2_param->prf);
+ BIO_printf(bio_err, "PBKDF2 PRF: ");
+ i2a_ASN1_OBJECT(bio_err, prfobj);
+ BIO_printf(bio_err, "\n");
+ }
+ PBKDF2PARAM_free(pbkdf2_param);
+ } else {
+ BIO_printf(bio_err, ", Iteration %ld\n",
+ tmaciter != NULL ? ASN1_INTEGER_get(tmaciter) : 1L);
+ BIO_printf(bio_err, "MAC length: %ld, salt length: %ld\n",
+ tmac != NULL ? ASN1_STRING_length(tmac) : 0L,
+ tsalt != NULL ? ASN1_STRING_length(tsalt) : 0L);
+ }
}
if (macver) {
EVP_KDF *pkcs12kdf;
diff --git a/crypto/asn1/p5_pbev2.c b/crypto/asn1/p5_pbev2.c
index 8575d05bf6d5a..c22cc6b77075d 100644
--- a/crypto/asn1/p5_pbev2.c
+++ b/crypto/asn1/p5_pbev2.c
@@ -35,6 +35,13 @@ ASN1_SEQUENCE(PBKDF2PARAM) = {
IMPLEMENT_ASN1_FUNCTIONS(PBKDF2PARAM)
+ASN1_SEQUENCE(PBMAC1PARAM) = {
+ ASN1_SIMPLE(PBMAC1PARAM, keyDerivationFunc, X509_ALGOR),
+ ASN1_SIMPLE(PBMAC1PARAM, messageAuthScheme, X509_ALGOR)
+} ASN1_SEQUENCE_END(PBMAC1PARAM)
+
+IMPLEMENT_ASN1_FUNCTIONS(PBMAC1PARAM)
+
/*
* Return an algorithm identifier for a PKCS#5 v2.0 PBE algorithm: yes I know
* this is horrible! Extended version to allow application supplied PRF NID
diff --git a/crypto/evp/digest.c b/crypto/evp/digest.c
index 18a64329b7a35..a74e2fa42c5bb 100644
--- a/crypto/evp/digest.c
+++ b/crypto/evp/digest.c
@@ -20,6 +20,7 @@
#include <openssl/params.h>
#include <openssl/core_names.h>
#include "internal/cryptlib.h"
+#include "internal/nelem.h"
#include "internal/provider.h"
#include "internal/core.h"
#include "crypto/evp.h"
@@ -1185,3 +1186,56 @@ void EVP_MD_do_all_provided(OSSL_LIB_CTX *libctx,
(void (*)(void *, void *))fn, arg,
evp_md_from_algorithm, evp_md_up_ref, evp_md_free);
}
+
+typedef struct {
+ int md_nid;
+ int hmac_nid;
+} ossl_hmacmd_pair;
+
+static const ossl_hmacmd_pair ossl_hmacmd_pairs[] = {
+ {NID_sha1, NID_hmacWithSHA1},
+ {NID_md5, NID_hmacWithMD5},
+ {NID_sha224, NID_hmacWithSHA224},
+ {NID_sha256, NID_hmacWithSHA256},
+ {NID_sha384, NID_hmacWithSHA384},
+ {NID_sha512, NID_hmacWithSHA512},
+ {NID_id_GostR3411_94, NID_id_HMACGostR3411_94},
+ {NID_id_GostR3411_2012_256, NID_id_tc26_hmac_gost_3411_2012_256},
+ {NID_id_GostR3411_2012_512, NID_id_tc26_hmac_gost_3411_2012_512},
+ {NID_sha3_224, NID_hmac_sha3_224},
+ {NID_sha3_256, NID_hmac_sha3_256},
+ {NID_sha3_384, NID_hmac_sha3_384},
+ {NID_sha3_512, NID_hmac_sha3_512},
+ {NID_sha512_224, NID_hmacWithSHA512_224},
+ {NID_sha512_256, NID_hmacWithSHA512_256}
+};
+
+int ossl_hmac2mdnid(int hmac_nid)
+{
+ int md_nid = NID_undef;
+ size_t i;
+
+ for (i = 0; i < OSSL_NELEM(ossl_hmacmd_pairs); i++) {
+ if (ossl_hmacmd_pairs[i].hmac_nid == hmac_nid) {
+ md_nid = ossl_hmacmd_pairs[i].md_nid;
+ break;
+ }
+ }
+
+ return md_nid;
+}
+
+int ossl_md2hmacnid(int md_nid)
+{
+ int hmac_nid = NID_undef;
+ size_t i;
+
+ for (i = 0; i < OSSL_NELEM(ossl_hmacmd_pairs); i++) {
+ if (ossl_hmacmd_pairs[i].md_nid == md_nid) {
+ hmac_nid = ossl_hmacmd_pairs[i].hmac_nid;
+ break;
+ }
+ }
+
+ return hmac_nid;
+}
diff --git a/crypto/pkcs12/p12_mutl.c b/crypto/pkcs12/p12_mutl.c
index 4091e61d9dd06..d410978a49e1e 100644
--- a/crypto/pkcs12/p12_mutl.c
+++ b/crypto/pkcs12/p12_mutl.c
@@ -15,12 +15,19 @@
#include <stdio.h>
#include "internal/cryptlib.h"
+#include "crypto/evp.h"
#include <openssl/crypto.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <openssl/pkcs12.h>
#include "p12_local.h"
+static int pkcs12_pbmac1_pbkdf2_key_gen(const char *pass, int passlen,
+ unsigned char *salt, int saltlen,
+ int id, int iter, int keylen,
+ unsigned char *out,
+ const EVP_MD *md_type);
+
int PKCS12_mac_present(const PKCS12 *p12)
{
return p12->mac ? 1 : 0;
@@ -72,9 +79,76 @@ static int pkcs12_gen_gost_mac_key(const char *pass, int passlen,
return 1;
}
-/* Generate a MAC */
+PBKDF2PARAM *PBMAC1_get1_pbkdf2_param(const X509_ALGOR *macalg)
+{
+ PBMAC1PARAM *param = NULL;
+ PBKDF2PARAM *pbkdf2_param = NULL;
+ const ASN1_OBJECT *kdf_oid;
+
+ param = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(PBMAC1PARAM), macalg->parameter);
+ if (param == NULL) {
+ ERR_raise(ERR_LIB_PKCS12, ERR_R_PASSED_INVALID_ARGUMENT);
+ return NULL;
+ }
+
+ X509_ALGOR_get0(&kdf_oid, NULL, NULL, param->keyDerivationFunc);
+ if (OBJ_obj2nid(kdf_oid) != NID_id_pbkdf2) {
+ ERR_raise(ERR_LIB_PKCS12, ERR_R_PASSED_INVALID_ARGUMENT);
+ PBMAC1PARAM_free(param);
+ return NULL;
+ }
+
+ pbkdf2_param = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(PBKDF2PARAM),
+ param->keyDerivationFunc->parameter);
+ PBMAC1PARAM_free(param);
+
+ return pbkdf2_param;
+}
+
+static int PBMAC1_PBKDF2_HMAC(OSSL_LIB_CTX *ctx, const char *propq,
+ const char *pass, int passlen,
+ const X509_ALGOR *macalg, unsigned char *key)
+{
+ PBKDF2PARAM *pbkdf2_param = NULL;
+ const ASN1_OBJECT *kdf_hmac_oid;
+ int ret = -1;
+ int keylen = 0;
+ EVP_MD *kdf_md = NULL;
+ const ASN1_OCTET_STRING *pbkdf2_salt = NULL;
+
+ pbkdf2_param = PBMAC1_get1_pbkdf2_param(macalg);
+ if (pbkdf2_param == NULL) {
+ ERR_raise(ERR_LIB_PKCS12, ERR_R_UNSUPPORTED);
+ goto err;
+ }
+ keylen = ASN1_INTEGER_get(pbkdf2_param->keylength);
+ pbkdf2_salt = pbkdf2_param->salt->value.octet_string;
+ X509_ALGOR_get0(&kdf_hmac_oid, NULL, NULL, pbkdf2_param->prf);
+
+ kdf_md = EVP_MD_fetch(ctx, OBJ_nid2sn(ossl_hmac2mdnid(OBJ_obj2nid(kdf_hmac_oid))), propq);
+ if (kdf_md == NULL) {
+ ERR_raise(ERR_LIB_PKCS12, ERR_R_FETCH_FAILED);
+ goto err;
+ }
+
+ if (PKCS5_PBKDF2_HMAC(pass, passlen, pbkdf2_salt->data, pbkdf2_salt->length,
+ ASN1_INTEGER_get(pbkdf2_param->iter), kdf_md, keylen, key) <= 0) {
+ ERR_raise(ERR_LIB_PKCS12, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ ret = keylen;
+
+ err:
+ EVP_MD_free(kdf_md);
+ PBKDF2PARAM_free(pbkdf2_param);
+
+ return ret;
+}
+
+/* Generate a MAC, also used for verification */
static int pkcs12_gen_mac(PKCS12 *p12, const char *pass, int passlen,
unsigned char *mac, unsigned int *maclen,
+ int pbmac1_md_nid, int pbmac1_kdf_nid,
int (*pkcs12_key_gen)(const char *pass, int passlen,
unsigned char *salt, int slen,
int id, int iter, int n,
@@ -88,8 +162,8 @@ static int pkcs12_gen_mac(PKCS12 *p12, const char *pass, int passlen,
unsigned char key[EVP_MAX_MD_SIZE], *salt;
int saltlen, iter;
char md_name[80];
- int md_size = 0;
- int md_nid;
+ int keylen = 0;
+ int md_nid = NID_undef;
const X509_ALGOR *macalg;
const ASN1_OBJECT *macoid;
@@ -111,9 +185,13 @@ static int pkcs12_gen_mac(PKCS12 *p12, const char *pass, int passlen,
iter = ASN1_INTEGER_get(p12->mac->iter);
X509_SIG_get0(p12->mac->dinfo, &macalg, NULL);
X509_ALGOR_get0(&macoid, NULL, NULL, macalg);
- if (OBJ_obj2txt(md_name, sizeof(md_name), macoid, 0) < 0)
- return 0;
-
+ if (OBJ_obj2nid(macoid) == NID_pbmac1) {
+ if (OBJ_obj2txt(md_name, sizeof(md_name), OBJ_nid2obj(pbmac1_md_nid), 0) < 0)
+ return 0;
+ } else {
+ if (OBJ_obj2txt(md_name, sizeof(md_name), macoid, 0) < 0)
+ return 0;
+ }
(void)ERR_set_mark();
md = md_fetch = EVP_MD_fetch(p12->authsafes->ctx.libctx, md_name,
p12->authsafes->ctx.propq);
@@ -127,40 +205,61 @@ static int pkcs12_gen_mac(PKCS12 *p12, const char *pass, int passlen,
}
(void)ERR_pop_to_mark();
- md_size = EVP_MD_get_size(md);
+ keylen = EVP_MD_get_size(md);
md_nid = EVP_MD_get_type(md);
- if (md_size < 0)
+ if (keylen < 0)
goto err;
- if ((md_nid == NID_id_GostR3411_94
- || md_nid == NID_id_GostR3411_2012_256
- || md_nid == NID_id_GostR3411_2012_512)
- && ossl_safe_getenv("LEGACY_GOST_PKCS12") == NULL) {
- md_size = TK26_MAC_KEY_LEN;
+
+ /* For PBMAC1 we use a special keygen callback if not provided (e.g. on verification) */
+ if (pbmac1_md_nid != NID_undef && pkcs12_key_gen == NULL) {
+ keylen = PBMAC1_PBKDF2_HMAC(p12->authsafes->ctx.libctx, p12->authsafes->ctx.propq,
+ pass, passlen, macalg, key);
+ if (keylen < 0)
+ goto err;
+ } else if ((md_nid == NID_id_GostR3411_94
+ || md_nid == NID_id_GostR3411_2012_256
+ || md_nid == NID_id_GostR3411_2012_512)
+ && ossl_safe_getenv("LEGACY_GOST_PKCS12") == NULL) {
+ keylen = TK26_MAC_KEY_LEN;
if (!pkcs12_gen_gost_mac_key(pass, passlen, salt, saltlen, iter,
- md_size, key, md)) {
+ keylen, key, md)) {
ERR_raise(ERR_LIB_PKCS12, PKCS12_R_KEY_GEN_ERROR);
goto err;
}
} else {
+ EVP_MD *hmac_md = (EVP_MD *)md;
+ int fetched = 0;
+
+ if (pbmac1_kdf_nid != NID_undef) {
+ char hmac_md_name[128];
+
+ if (OBJ_obj2txt(hmac_md_name, sizeof(hmac_md_name), OBJ_nid2obj(pbmac1_kdf_nid), 0) < 0)
+ goto err;
+ hmac_md = EVP_MD_fetch(NULL, hmac_md_name, NULL);
+ fetched = 1;
+ }
if (pkcs12_key_gen != NULL) {
- if (!(*pkcs12_key_gen)(pass, passlen, salt, saltlen, PKCS12_MAC_ID,
- iter, md_size, key, md)) {
+ int res = (*pkcs12_key_gen)(pass, passlen, salt, saltlen, PKCS12_MAC_ID,
+ iter, keylen, key, hmac_md);
+
+ if (fetched)
+ EVP_MD_free(hmac_md);
+ if (res != 1) {
ERR_raise(ERR_LIB_PKCS12, PKCS12_R_KEY_GEN_ERROR);
goto err;
}
} else {
/* Default to UTF-8 password */
if (!PKCS12_key_gen_utf8_ex(pass, passlen, salt, saltlen, PKCS12_MAC_ID,
- iter, md_size, key, md,
- p12->authsafes->ctx.libctx,
- p12->authsafes->ctx.propq)) {
+ iter, keylen, key, md,
+ p12->authsafes->ctx.libctx, p12->authsafes->ctx.propq)) {
ERR_raise(ERR_LIB_PKCS12, PKCS12_R_KEY_GEN_ERROR);
goto err;
}
}
}
if ((hmac = HMAC_CTX_new()) == NULL
- || !HMAC_Init_ex(hmac, key, md_size, md, NULL)
+ || !HMAC_Init_ex(hmac, key, keylen, md, NULL)
|| !HMAC_Update(hmac, p12->authsafes->d.data->data,
p12->authsafes->d.data->length)
|| !HMAC_Final(hmac, mac, maclen)) {
@@ -178,7 +277,7 @@ static int pkcs12_gen_mac(PKCS12 *p12, const char *pass, int passlen,
int PKCS12_gen_mac(PKCS12 *p12, const char *pass, int passlen,
unsigned char *mac, unsigned int *maclen)
{
- return pkcs12_gen_mac(p12, pass, passlen, mac, maclen, NULL);
+ return pkcs12_gen_mac(p12, pass, passlen, mac, maclen, NID_undef, NID_undef, NULL);
}
/* Verify the mac */
@@ -187,14 +286,40 @@ int PKCS12_verify_mac(PKCS12 *p12, const char *pass, int passlen)
unsigned char mac[EVP_MAX_MD_SIZE];
unsigned int maclen;
const ASN1_OCTET_STRING *macoct;
+ const X509_ALGOR *macalg;
+ const ASN1_OBJECT *macoid;
if (p12->mac == NULL) {
ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_ABSENT);
return 0;
}
- if (!pkcs12_gen_mac(p12, pass, passlen, mac, &maclen, NULL)) {
- ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_GENERATION_ERROR);
- return 0;
+
+ X509_SIG_get0(p12->mac->dinfo, &macalg, NULL);
+ X509_ALGOR_get0(&macoid, NULL, NULL, macalg);
+ if (OBJ_obj2nid(macoid) == NID_pbmac1) {
+ PBMAC1PARAM *param = NULL;
+ const ASN1_OBJECT *hmac_oid;
+ int md_nid = NID_undef;
+
+ param = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(PBMAC1PARAM), macalg->parameter);
+ if (param == NULL) {
+ ERR_raise(ERR_LIB_PKCS12, ERR_R_UNSUPPORTED);
+ return 0;
+ }
+ X509_ALGOR_get0(&hmac_oid, NULL, NULL, param->messageAuthScheme);
+ md_nid = ossl_hmac2mdnid(OBJ_obj2nid(hmac_oid));
+
+ if (!pkcs12_gen_mac(p12, pass, passlen, mac, &maclen, md_nid, NID_undef, NULL)) {
+ ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_GENERATION_ERROR);
+ PBMAC1PARAM_free(param);
+ return 0;
+ }
+ PBMAC1PARAM_free(param);
+ } else {
+ if (!pkcs12_gen_mac(p12, pass, passlen, mac, &maclen, NID_undef, NID_undef, NULL)) {
+ ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_GENERATION_ERROR);
+ return 0;
+ }
}
X509_SIG_get0(p12->mac->dinfo, NULL, &macoct);
if ((maclen != (unsigned int)ASN1_STRING_length(macoct))
@@ -205,7 +330,6 @@ int PKCS12_verify_mac(PKCS12 *p12, const char *pass, int passlen)
}
/* Set a mac */
-
int PKCS12_set_mac(PKCS12 *p12, const char *pass, int passlen,
unsigned char *salt, int saltlen, int iter,
const EVP_MD *md_type)
@@ -226,7 +350,7 @@ int PKCS12_set_mac(PKCS12 *p12, const char *pass, int passlen,
/*
* Note that output mac is forced to UTF-8...
*/
- if (!pkcs12_gen_mac(p12, pass, passlen, mac, &maclen, NULL)) {
+ if (!pkcs12_gen_mac(p12, pass, passlen, mac, &maclen, NID_undef, NID_undef, NULL)) {
ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_GENERATION_ERROR);
return 0;
}
@@ -238,9 +362,18 @@ int PKCS12_set_mac(PKCS12 *p12, const char *pass, int passlen,
return 1;
}
-/* Set up a mac structure */
-int PKCS12_setup_mac(PKCS12 *p12, int iter, unsigned char *salt, int saltlen,
- const EVP_MD *md_type)
+static int pkcs12_pbmac1_pbkdf2_key_gen(const char *pass, int passlen,
+ unsigned char *salt, int saltlen,
+ int id, int iter, int keylen,
+ unsigned char *out,
+ const EVP_MD *md_type)
+{
+ return PKCS5_PBKDF2_HMAC(pass, passlen, salt, saltlen, iter,
+ md_type, keylen, out);
+}
+
+static int pkcs12_setup_mac(PKCS12 *p12, int iter, unsigned char *salt, int saltlen,
+ int nid)
{
X509_ALGOR *macalg;
@@ -274,11 +407,112 @@ int PKCS12_setup_mac(PKCS12 *p12, int iter, unsigned char *salt, int saltlen,
memcpy(p12->mac->salt->data, salt, saltlen);
}
X509_SIG_getm(p12->mac->dinfo, &macalg, NULL);
- if (!X509_ALGOR_set0(macalg, OBJ_nid2obj(EVP_MD_get_type(md_type)),
- V_ASN1_NULL, NULL)) {
+ if (!X509_ALGOR_set0(macalg, OBJ_nid2obj(nid), V_ASN1_NULL, NULL)) {
ERR_raise(ERR_LIB_PKCS12, ERR_R_ASN1_LIB);
return 0;
}
return 1;
}
+
+/* Set up a mac structure */
+int PKCS12_setup_mac(PKCS12 *p12, int iter, unsigned char *salt, int saltlen,
+ const EVP_MD *md_type)
+{
+ return pkcs12_setup_mac(p12, iter, salt, saltlen, EVP_MD_get_type(md_type));
+}
+
+int PKCS12_set_pbmac1_pbkdf2(PKCS12 *p12, const char *pass, int passlen,
+ unsigned char *salt, int saltlen, int iter,
+ const EVP_MD *md_type, const char *prf_md_name)
+{
+ unsigned char mac[EVP_MAX_MD_SIZE];
+ unsigned int maclen;
+ ASN1_OCTET_STRING *macoct;
+ X509_ALGOR *alg = NULL;
+ int ret = 0;
+ int prf_md_nid = NID_undef, prf_nid = NID_undef, hmac_nid;
+ unsigned char *known_salt = NULL;
+ int keylen = 0;
+ PBMAC1PARAM *param = NULL;
+ X509_ALGOR *hmac_alg = NULL, *macalg = NULL;
+
+ if (md_type == NULL)
+ /* No need to do a fetch as the md_type is used only to get a NID */
+ md_type = EVP_sha256();
+
+ if (prf_md_name == NULL)
+ prf_md_nid = EVP_MD_get_type(md_type);
+ else
+ prf_md_nid = OBJ_txt2nid(prf_md_name);
+
+ if (iter == 0)
+ iter = PKCS12_DEFAULT_ITER;
+
+ keylen = EVP_MD_get_size(md_type);
+
+ prf_nid = ossl_md2hmacnid(prf_md_nid);
+ hmac_nid = ossl_md2hmacnid(EVP_MD_get_type(md_type));
+
+ if (prf_nid == NID_undef || hmac_nid == NID_undef) {
+ ERR_raise(ERR_LIB_PKCS12, PKCS12_R_UNKNOWN_DIGEST_ALGORITHM);
+ goto err;
+ }
+
+ if (salt == NULL) {
+ known_salt = OPENSSL_malloc(saltlen);
+ if (known_salt == NULL)
+ goto err;
+
+ if (RAND_bytes_ex(NULL, known_salt, saltlen, 0) <= 0) {
+ ERR_raise(ERR_LIB_PKCS12, ERR_R_RAND_LIB);
+ goto err;
+ }
+ }
+
+ param = PBMAC1PARAM_new();
+ hmac_alg = X509_ALGOR_new();
+ alg = PKCS5_pbkdf2_set(iter, salt ? salt : known_salt, saltlen, prf_nid, keylen);
+ if (param == NULL || hmac_alg == NULL || alg == NULL)
+ goto err;
+
+ if (pkcs12_setup_mac(p12, iter, salt ? salt : known_salt, saltlen,
+ NID_pbmac1) == PKCS12_ERROR) {
+ ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_SETUP_ERROR);
+ goto err;
+ }
+
+ if (!X509_ALGOR_set0(hmac_alg, OBJ_nid2obj(hmac_nid), V_ASN1_NULL, NULL)) {
+ ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_SETUP_ERROR);
+ goto err;
+ }
+
+ X509_ALGOR_free(param->keyDerivationFunc);
+ X509_ALGOR_free(param->messageAuthScheme);
+ param->keyDerivationFunc = alg;
+ param->messageAuthScheme = hmac_alg;
+
+ X509_SIG_getm(p12->mac->dinfo, &macalg, &macoct);
+ if (!ASN1_TYPE_pack_sequence(ASN1_ITEM_rptr(PBMAC1PARAM), param, &macalg->parameter))
+ goto err;
+
+ /*
+ * Note that output mac is forced to UTF-8...
+ */
+ if (!pkcs12_gen_mac(p12, pass, passlen, mac, &maclen,
+ EVP_MD_get_type(md_type), prf_md_nid,
+ pkcs12_pbmac1_pbkdf2_key_gen)) {
+ ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_GENERATION_ERROR);
+ goto err;
+ }
+ if (!ASN1_OCTET_STRING_set(macoct, mac, maclen)) {
+ ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_STRING_SET_ERROR);
+ goto err;
+ }
+ ret = 1;
+
+ err:
+ PBMAC1PARAM_free(param);
+ OPENSSL_free(known_salt);
+ return ret;
+}
diff --git a/include/crypto/evp.h b/include/crypto/evp.h
index 32c60f223c78c..72d9995e8f0f4 100644
--- a/include/crypto/evp.h
+++ b/include/crypto/evp.h
@@ -964,4 +964,7 @@ int evp_pkey_decrypt_alloc(EVP_PKEY_CTX *ctx, unsigned char **outp,
size_t *outlenp, size_t expected_outlen,
const unsigned char *in, size_t inlen);
+int ossl_md2hmacnid(int mdnid);
+int ossl_hmac2mdnid(int hmac_nid);
+
#endif /* OSSL_CRYPTO_EVP_H */
diff --git a/include/openssl/pkcs12.h.in b/include/openssl/pkcs12.h.in
index 35759d4deadc3..ab62207e49b55 100644
--- a/include/openssl/pkcs12.h.in
+++ b/include/openssl/pkcs12.h.in
@@ -269,6 +269,9 @@ int PKCS12_verify_mac(PKCS12 *p12, const char *pass, int passlen);
int PKCS12_set_mac(PKCS12 *p12, const char *pass, int passlen,
unsigned char *salt, int saltlen, int iter,
const EVP_MD *md_type);
+int PKCS12_set_pbmac1_pbkdf2(PKCS12 *p12, const char *pass, int passlen,
+ unsigned char *salt, int saltlen, int iter,
+ const EVP_MD *md_type, const char *prf_md_name);
int PKCS12_setup_mac(PKCS12 *p12, int iter, unsigned char *salt,
int saltlen, const EVP_MD *md_type);
unsigned char *OPENSSL_asc2uni(const char *asc, int asclen,
diff --git a/include/openssl/x509.h.in b/include/openssl/x509.h.in
index 99bc4aab29133..b7f080a5360db 100644
--- a/include/openssl/x509.h.in
+++ b/include/openssl/x509.h.in
@@ -279,7 +279,12 @@ typedef struct PBKDF2PARAM_st {
X509_ALGOR *prf;
} PBKDF2PARAM;
-#ifndef OPENSSL_NO_SCRYPT
+typedef struct {
+ X509_ALGOR *keyDerivationFunc;
+ X509_ALGOR *messageAuthScheme;
+} PBMAC1PARAM;
+
+# ifndef OPENSSL_NO_SCRYPT
typedef struct SCRYPT_PARAMS_st {
ASN1_OCTET_STRING *salt;
ASN1_INTEGER *costParameter;
@@ -287,7 +292,7 @@ typedef struct SCRYPT_PARAMS_st {
ASN1_INTEGER *parallelizationParameter;
ASN1_INTEGER *keyLength;
} SCRYPT_PARAMS;
-#endif
+# endif
#ifdef __cplusplus
}
@@ -1023,9 +1028,10 @@ X509 *X509_find_by_subject(STACK_OF(X509) *sk, const X509_NAME *name);
DECLARE_ASN1_FUNCTIONS(PBEPARAM)
DECLARE_ASN1_FUNCTIONS(PBE2PARAM)
DECLARE_ASN1_FUNCTIONS(PBKDF2PARAM)
-#ifndef OPENSSL_NO_SCRYPT
+DECLARE_ASN1_FUNCTIONS(PBMAC1PARAM)
+# ifndef OPENSSL_NO_SCRYPT
DECLARE_ASN1_FUNCTIONS(SCRYPT_PARAMS)
-#endif
+# endif
int PKCS5_pbe_set0_algor(X509_ALGOR *algor, int alg, int iter,
const unsigned char *salt, int saltlen);
@@ -1062,6 +1068,7 @@ X509_ALGOR *PKCS5_pbkdf2_set_ex(int iter, unsigned char *salt, int saltlen,
int prf_nid, int keylen,
OSSL_LIB_CTX *libctx);
+PBKDF2PARAM *PBMAC1_get1_pbkdf2_param(const X509_ALGOR *macalg);
/* PKCS#8 utilities */
DECLARE_ASN1_FUNCTIONS(PKCS8_PRIV_KEY_INFO)
From 29d98a8287d217b2232344056934d3cd2c6f44a3 Mon Sep 17 00:00:00 2001
From: Dmitry Belyavskiy <beldmit@gmail.com>
Date: Fri, 7 Jun 2024 14:38:40 +0200
Subject: [PATCH 2/4] Implementation of the RFC 9579, PBMAC1 in PKCS#12 -
documentation
---
doc/man1/openssl-pkcs12.pod.in | 11 +++++++
doc/man3/PBMAC1_get1_pbkdf2_param.pod | 46 +++++++++++++++++++++++++++
doc/man3/PKCS12_gen_mac.pod | 37 ++++++++++++++++-----
doc/man3/X509_dup.pod | 3 ++
doc/man3/d2i_X509.pod | 2 ++
util/missingcrypto.txt | 1 -
util/missingcrypto111.txt | 1 -
7 files changed, 91 insertions(+), 10 deletions(-)
create mode 100644 doc/man3/PBMAC1_get1_pbkdf2_param.pod
diff --git a/doc/man1/openssl-pkcs12.pod.in b/doc/man1/openssl-pkcs12.pod.in
index 665b22bb644ac..020543cd5c895 100644
--- a/doc/man1/openssl-pkcs12.pod.in
+++ b/doc/man1/openssl-pkcs12.pod.in
@@ -62,6 +62,8 @@ PKCS#12 output (export) options:
[B<-certpbe> I<cipher>]
[B<-descert>]
[B<-macalg> I<digest>]
+[B<-pbmac1_pbkdf2>]
+[B<-pbmac1_pbkdf2_md> I<digest>]
[B<-iter> I<count>]
[B<-noiter>]
[B<-nomaciter>]
@@ -345,6 +347,15 @@ then both, the private key and the certificates are encrypted using triple DES.
Specify the MAC digest algorithm. If not included SHA256 will be used.
+=item B<-pbmac1_pbkdf2>
+
+Use PBMAC1 with PBKDF2 for MAC protection of the PKCS#12 file.
+
+=item B<-pbmac1_pbkdf2_md> I<digest>
+
+Specify the PBKDF2 KDF digest algorithm. If not specified, SHA256 will be used.
+Unless C<-pbmac1_pbkdf2> is specified, this parameter is ignored.
+
=item B<-iter> I<count>
This option specifies the iteration count for the encryption key and MAC. The
diff --git a/doc/man3/PBMAC1_get1_pbkdf2_param.pod b/doc/man3/PBMAC1_get1_pbkdf2_param.pod
new file mode 100644
index 0000000000000..415c3cd214a2e
--- /dev/null
+++ b/doc/man3/PBMAC1_get1_pbkdf2_param.pod
@@ -0,0 +1,46 @@
+=pod
+
+=head1 NAME
+
+PBMAC1_get1_pbkdf2_param - Function to manipulate a PBMAC1
+MAC structure
+
+=head1 SYNOPSIS
+
+ #include <openssl/x509.h>
+
+ PBKDF2PARAM *PBMAC1_get1_pbkdf2_param(const X509_ALGOR *macalg);
+
+=head1 DESCRIPTION
+
+PBMAC1_get1_pbkdf2_param() retrieves a B<PBKDF2PARAM> structure from an
+I<X509_ALGOR> structure.
+
+=head1 RETURN VALUES
+
+PBMAC1_get1_pbkdf2_param() returns NULL in case when PBMAC1 uses an algorithm
+apart from B<PBKDF2> or when passed incorrect parameters and a pointer to
+B<PBKDF2PARAM> structure otherwise.
+
+=head1 CONFORMING TO
+
+IETF RFC 9579 (L<https://tools.ietf.org/html/rfc9579>)
+
+=head1 SEE ALSO
+
+L<openssl-pkcs12(1)>
+
+=head1 HISTORY
+
+The I<PBMAC1_get1_pbkdf2_param> function was added in OpenSSL 3.4.
+
+=head1 COPYRIGHT
+
+Copyright 2021-2024 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the Apache License 2.0 (the "License"). You may not use
+this file except in compliance with the License. You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/man3/PKCS12_gen_mac.pod b/doc/man3/PKCS12_gen_mac.pod
index a72df145fedd7..ebeee98f04e68 100644
--- a/doc/man3/PKCS12_gen_mac.pod
+++ b/doc/man3/PKCS12_gen_mac.pod
@@ -3,7 +3,8 @@
=head1 NAME
PKCS12_gen_mac, PKCS12_setup_mac, PKCS12_set_mac,
-PKCS12_verify_mac - Functions to create and manipulate a PKCS#12 structure
+PKCS12_set_pbmac1_pbkdf2, PKCS12_verify_mac, PKCS12_get0_mac -
+Functions to create and manipulate a PKCS#12 MAC structure
=head1 SYNOPSIS
@@ -15,9 +16,19 @@ PKCS12_verify_mac - Functions to create and manipulate a PKCS#12 structure
int PKCS12_set_mac(PKCS12 *p12, const char *pass, int passlen,
unsigned char *salt, int saltlen, int iter,
const EVP_MD *md_type);
+ int PKCS12_set_pbmac1_pbkdf2(PKCS12 *p12, const char *pass, int passlen,
+ unsigned char *salt, int saltlen, int iter,
+ const EVP_MD *md_type,
+ const char *prf_md_name);
int PKCS12_setup_mac(PKCS12 *p12, int iter, unsigned char *salt,
int saltlen, const EVP_MD *md_type);
+ void PKCS12_get0_mac(const ASN1_OCTET_STRING **pmac,
+ const X509_ALGOR **pmacalg,
+ const ASN1_OCTET_STRING **psalt,
+ const ASN1_INTEGER **piter,
+ const PKCS12 *p12);
+
=head1 DESCRIPTION
PKCS12_gen_mac() generates an HMAC over the entire PKCS#12 object using the
@@ -31,10 +42,15 @@ PKCS12_setup_mac() sets the MAC part of the PKCS#12 structure with the supplied
parameters.
PKCS12_set_mac() sets the MAC and MAC parameters into the PKCS#12 object.
+PKCS12_set_pbmac1_pbkdf2() sets the MAC and MAC parameters into the PKCS#12
+object when B<PBMAC1> with PBKDF2 is used for protection of the PKCS#12 object.
I<pass> is the passphrase to use in the HMAC. I<salt> is the salt value to use,
-I<iter> is the iteration count and I<md_type> is the message digest
-function to use.
+I<iter> is the iteration count and I<md_type> is the message digest function to
+use. I<prf_md_name> specifies the digest used for the PBKDF2 in PBMAC1 KDF.
+
+PKCS12_get0_mac() retrieves any included MAC value, B<X509_ALGOR> object,
+I<salt>, and I<iter> count from the PKCS12 object.
=head1 NOTES
@@ -43,17 +59,18 @@ If I<salt> is NULL then a suitable salt will be generated and used.
If I<iter> is 1 then an iteration count will be omitted from the PKCS#12
structure.
-PKCS12_gen_mac(), PKCS12_verify_mac() and PKCS12_set_mac() make assumptions
-regarding the encoding of the given passphrase. See L<passphrase-encoding(7)>
-for more information.
+PKCS12_gen_mac(), PKCS12_verify_mac(), PKCS12_set_mac() and
+PKCS12_set_pbmac1_pbkdf2() make assumptions regarding the encoding of the
+given passphrase. See L<passphrase-encoding(7)> for more information.
=head1 RETURN VALUES
-All functions return 1 on success and 0 if an error occurred.
+All functions returning an integer return 1 on success and 0 if an error occurred.
=head1 CONFORMING TO
IETF RFC 7292 (L<https://tools.ietf.org/html/rfc7292>)
+IETF RFC 9579 (L<https://tools.ietf.org/html/rfc9579>)
=head1 SEE ALSO
@@ -62,9 +79,13 @@ L<EVP_KDF-PKCS12KDF(7)>,
L<PKCS12_create(3)>,
L<passphrase-encoding(7)>
+=head1 HISTORY
+
+The I<PKCS12_set_pbmac1_pbkdf2> function was added in OpenSSL 3.4.
+
=head1 COPYRIGHT
-Copyright 2021-2023 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2021-2024 The OpenSSL Project Authors. All Rights Reserved.
Licensed under the Apache License 2.0 (the "License"). You may not use
this file except in compliance with the License. You can obtain a copy
diff --git a/doc/man3/X509_dup.pod b/doc/man3/X509_dup.pod
index fc93494a76617..81ea2275d7414 100644
--- a/doc/man3/X509_dup.pod
+++ b/doc/man3/X509_dup.pod
@@ -218,6 +218,9 @@ PBEPARAM_free,
PBEPARAM_new,
PBKDF2PARAM_free,
PBKDF2PARAM_new,
+PBMAC1PARAM_free,
+PBMAC1PARAM_it,
+PBMAC1PARAM_new,
PKCS12_BAGS_free,
PKCS12_BAGS_new,
PKCS12_MAC_DATA_free,
diff --git a/doc/man3/d2i_X509.pod b/doc/man3/d2i_X509.pod
index 75b37e5544396..3615bcaafe7c0 100644
--- a/doc/man3/d2i_X509.pod
+++ b/doc/man3/d2i_X509.pod
@@ -115,6 +115,7 @@ d2i_OTHERNAME,
d2i_PBE2PARAM,
d2i_PBEPARAM,
d2i_PBKDF2PARAM,
+d2i_PBMAC1PARAM,
d2i_PKCS12,
d2i_PKCS12_BAGS,
d2i_PKCS12_MAC_DATA,
@@ -300,6 +301,7 @@ i2d_OTHERNAME,
i2d_PBE2PARAM,
i2d_PBEPARAM,
i2d_PBKDF2PARAM,
+i2d_PBMAC1PARAM,
i2d_PKCS12,
i2d_PKCS12_BAGS,
i2d_PKCS12_MAC_DATA,
diff --git a/util/missingcrypto.txt b/util/missingcrypto.txt
index b7d5091b31912..a56491d0f8b94 100644
--- a/util/missingcrypto.txt
+++ b/util/missingcrypto.txt
@@ -749,7 +749,6 @@ PKCS12_MAC_DATA_it(3)
PKCS12_PBE_add(3)
PKCS12_SAFEBAGS_it(3)
PKCS12_SAFEBAG_it(3)
-PKCS12_get0_mac(3)
PKCS12_get_attr(3)
PKCS12_it(3)
PKCS12_item_pack_safebag(3)
diff --git a/util/missingcrypto111.txt b/util/missingcrypto111.txt
index 0386701ad1e32..f3402ada7e60f 100644
--- a/util/missingcrypto111.txt
+++ b/util/missingcrypto111.txt
@@ -1027,7 +1027,6 @@ PKCS12_add_safe(3)
PKCS12_add_safes(3)
PKCS12_decrypt_skey(3)
PKCS12_gen_mac(3)
-PKCS12_get0_mac(3)
PKCS12_get_attr(3)
PKCS12_get_attr_gen(3)
PKCS12_get_friendlyname(3)
From 7257898633703d5841aefa7fb4f9d192430fdad8 Mon Sep 17 00:00:00 2001
From: Dmitry Belyavskiy <beldmit@gmail.com>
Date: Thu, 6 Jun 2024 13:07:48 +0200
Subject: [PATCH 3/4] Make update
---
doc/build.info | 6 ++++++
util/libcrypto.num | 7 +++++++
2 files changed, 13 insertions(+)
diff --git a/doc/build.info b/doc/build.info
index d47371e88aa9f..60a5d9b86bd5c 100644
--- a/doc/build.info
+++ b/doc/build.info
@@ -1847,6 +1847,10 @@ DEPEND[html/man3/OpenSSL_version.html]=man3/OpenSSL_version.pod
GENERATE[html/man3/OpenSSL_version.html]=man3/OpenSSL_version.pod
DEPEND[man/man3/OpenSSL_version.3]=man3/OpenSSL_version.pod
GENERATE[man/man3/OpenSSL_version.3]=man3/OpenSSL_version.pod
+DEPEND[html/man3/PBMAC1_get1_pbkdf2_param.html]=man3/PBMAC1_get1_pbkdf2_param.pod
+GENERATE[html/man3/PBMAC1_get1_pbkdf2_param.html]=man3/PBMAC1_get1_pbkdf2_param.pod
+DEPEND[man/man3/PBMAC1_get1_pbkdf2_param.3]=man3/PBMAC1_get1_pbkdf2_param.pod
+GENERATE[man/man3/PBMAC1_get1_pbkdf2_param.3]=man3/PBMAC1_get1_pbkdf2_param.pod
DEPEND[html/man3/PEM_X509_INFO_read_bio_ex.html]=man3/PEM_X509_INFO_read_bio_ex.pod
GENERATE[html/man3/PEM_X509_INFO_read_bio_ex.html]=man3/PEM_X509_INFO_read_bio_ex.pod
DEPEND[man/man3/PEM_X509_INFO_read_bio_ex.3]=man3/PEM_X509_INFO_read_bio_ex.pod
@@ -3453,6 +3457,7 @@ html/man3/OSSL_trace_get_category_num.html \
html/man3/OSSL_trace_set_channel.html \
html/man3/OpenSSL_add_all_algorithms.html \
html/man3/OpenSSL_version.html \
+html/man3/PBMAC1_get1_pbkdf2_param.html \
html/man3/PEM_X509_INFO_read_bio_ex.html \
html/man3/PEM_bytes_read_bio.html \
html/man3/PEM_read.html \
@@ -4113,6 +4118,7 @@ man/man3/OSSL_trace_get_category_num.3 \
man/man3/OSSL_trace_set_channel.3 \
man/man3/OpenSSL_add_all_algorithms.3 \
man/man3/OpenSSL_version.3 \
+man/man3/PBMAC1_get1_pbkdf2_param.3 \
man/man3/PEM_X509_INFO_read_bio_ex.3 \
man/man3/PEM_bytes_read_bio.3 \
man/man3/PEM_read.3 \
diff --git a/util/libcrypto.num b/util/libcrypto.num
index 7f958a4fa31db..ef11c0302e396 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -5664,3 +5664,10 @@ OSSL_IETF_ATTR_SYNTAX_get_value_num ? 3_4_0 EXIST::FUNCTION:
OPENSSL_strncasecmp ? 3_0_1 EXIST::FUNCTION:
ossl_ctx_legacy_digest_signatures_allowed ? 3_0_1 EXIST::FUNCTION:
ossl_ctx_legacy_digest_signatures_allowed_set ? 3_0_1 EXIST::FUNCTION:
+PKCS12_set_pbmac1_pbkdf2 ? 3_4_0 EXIST::FUNCTION:
+PBMAC1_get1_pbkdf2_param ? 3_4_0 EXIST::FUNCTION:
+d2i_PBMAC1PARAM ? 3_4_0 EXIST::FUNCTION:
+i2d_PBMAC1PARAM ? 3_4_0 EXIST::FUNCTION:
+PBMAC1PARAM_free ? 3_4_0 EXIST::FUNCTION:
+PBMAC1PARAM_new ? 3_4_0 EXIST::FUNCTION:
+PBMAC1PARAM_it ? 3_4_0 EXIST::FUNCTION:
From 97fbb9437163fb5114da40250b7ace83748a2e81 Mon Sep 17 00:00:00 2001
From: Dmitry Belyavskiy <beldmit@gmail.com>
Date: Thu, 6 Jun 2024 17:01:45 +0200
Subject: [PATCH 4/4] Test vectors from rfc9579 and creation tests
---
test/recipes/80-test_pkcs12.t | 55 +++++++++++++++++-
.../pbmac1_256_256.bad-iter.p12 | Bin 0 -> 2703 bytes
.../pbmac1_256_256.bad-salt.p12 | Bin 0 -> 2702 bytes
.../pbmac1_256_256.good.p12 | Bin 0 -> 2702 bytes
.../pbmac1_256_256.no-len.p12 | Bin 0 -> 2700 bytes
.../pbmac1_512_256.good.p12 | Bin 0 -> 2702 bytes
.../pbmac1_512_512.good.p12 | Bin 0 -> 2736 bytes
7 files changed, 54 insertions(+), 1 deletion(-)
create mode 100644 test/recipes/80-test_pkcs12_data/pbmac1_256_256.bad-iter.p12
create mode 100644 test/recipes/80-test_pkcs12_data/pbmac1_256_256.bad-salt.p12
create mode 100644 test/recipes/80-test_pkcs12_data/pbmac1_256_256.good.p12
create mode 100644 test/recipes/80-test_pkcs12_data/pbmac1_256_256.no-len.p12
create mode 100644 test/recipes/80-test_pkcs12_data/pbmac1_512_256.good.p12
create mode 100644 test/recipes/80-test_pkcs12_data/pbmac1_512_512.good.p12
diff --git a/test/recipes/80-test_pkcs12.t b/test/recipes/80-test_pkcs12.t
index 999129a03074d..c14ef94998cde 100644
--- a/test/recipes/80-test_pkcs12.t
+++ b/test/recipes/80-test_pkcs12.t
@@ -54,7 +54,7 @@ if (eval { require Win32::API; 1; }) {
}
$ENV{OPENSSL_WIN32_UTF8}=1;
-plan tests => 31;
+plan tests => 45;
# Test different PKCS#12 formats
ok(run(test(["pkcs12_format_test"])), "test pkcs12 formats");
@@ -170,6 +170,59 @@ ok(grep(/Trusted key usage (Oracle)/, @pkcs12info) == 0,
ok(scalar @match > 0 ? 0 : 1, "test_export_pkcs12_outerr6_empty");
}
+my %pbmac1_tests = (
+ pbmac1_defaults => {args => [], lookup => "hmacWithSHA256"},
+ pbmac1_nondefaults => {args => ["-pbmac1_pbkdf2_md", "sha512", "-macalg", "sha384"], lookup => "hmacWithSHA512"},
+);
+
+for my $instance (sort keys %pbmac1_tests) {
+ my $extra_args = $pbmac1_tests{$instance}{args};
+ my $lookup = $pbmac1_tests{$instance}{lookup};
+ # Test export of PEM file with both cert and key, with password.
+ {
+ my $pbmac1_id = $instance;
+ ok(run(app(["openssl", "pkcs12", "-export", "-pbmac1_pbkdf2",
+ "-inkey", srctop_file(@path, "cert-key-cert.pem"),
+ "-in", srctop_file(@path, "cert-key-cert.pem"),
+ "-passout", "pass:1234",
+ @$extra_args,
+ "-out", "$pbmac1_id.p12"], stderr => "${pbmac1_id}_err.txt")),
+ "test_export_pkcs12_${pbmac1_id}");
+ open DATA, "${pbmac1_id}_err.txt";
+ my @match = grep /:error:/, <DATA>;
+ close DATA;
+ ok(scalar @match > 0 ? 0 : 1, "test_export_pkcs12_${pbmac1_id}_err.empty");
+
+ ok(run(app(["openssl", "pkcs12", "-in", "$pbmac1_id.p12", "-info", "-noout",
+ "-passin", "pass:1234"], stderr => "${pbmac1_id}_info.txt")),
+ "test_export_pkcs12_${pbmac1_id}_info");
+ open DATA, "${pbmac1_id}_info.txt";
+ my @match = grep /$lookup/, <DATA>;
+ close DATA;
+ ok(scalar @match > 0 ? 1 : 0, "test_export_pkcs12_${pbmac1_id}_info");
+ }
+}
+
+# Test pbmac1 pkcs12 good files, RFC 9579
+for my $file ("pbmac1_256_256.good.p12", "pbmac1_512_256.good.p12", "pbmac1_512_512.good.p12")
+{
+ my $path = srctop_file("test", "recipes", "80-test_pkcs12_data", $file);
+ ok(run(app(["openssl", "pkcs12", "-in", $path, "-password", "pass:1234", "-noenc"])),
+ "test pbmac1 pkcs12 file $file");
+}
+
+# Test pbmac1 pkcs12 bad files, RFC 9579
+for my $file ("pbmac1_256_256.bad-iter.p12", "pbmac1_256_256.bad-salt.p12", "pbmac1_256_256.no-len.p12")
+{
+ my $path = srctop_file("test", "recipes", "80-test_pkcs12_data", $file);
+ with({ exit_checker => sub { return shift == 1; } },
+ sub {
+ ok(run(app(["openssl", "pkcs12", "-in", $path, "-password", "pass:1234", "-noenc"])),
+ "test pbmac1 pkcs12 bad file $file");
+ }
+ );
+}
+
# Test some bad pkcs12 files
my $bad1 = srctop_file("test", "recipes", "80-test_pkcs12_data", "bad1.p12");
my $bad2 = srctop_file("test", "recipes", "80-test_pkcs12_data", "bad2.p12");
diff --git a/test/recipes/80-test_pkcs12_data/pbmac1_256_256.bad-iter.p12 b/test/recipes/80-test_pkcs12_data/pbmac1_256_256.bad-iter.p12
new file mode 100644
index 0000000000000000000000000000000000000000..9957d473c433bc9fb9572ecf51332a7f325fe36f
GIT binary patch
literal 2703
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
QZf;>CWNWRj_YbE31}omyrT_o{
literal 0
HcmV?d00001
diff --git a/test/recipes/80-test_pkcs12_data/pbmac1_256_256.bad-salt.p12 b/test/recipes/80-test_pkcs12_data/pbmac1_256_256.bad-salt.p12
new file mode 100644
index 0000000000000000000000000000000000000000..fef1e51f71c94240b8d5e375b3e5273a7cb54be5
GIT binary patch
literal 2702
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
QJitKh*G5^nATS8@Hz^a=n*aa+
literal 0
HcmV?d00001
diff --git a/test/recipes/80-test_pkcs12_data/pbmac1_256_256.good.p12 b/test/recipes/80-test_pkcs12_data/pbmac1_256_256.good.p12
new file mode 100644
index 0000000000000000000000000000000000000000..b8c8c2d7759c66db30d791389a498418fd9cf2bd
GIT binary patch
literal 2702
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
Ob8A5xD?ME>2=q5e$<~_y
literal 0
HcmV?d00001
diff --git a/test/recipes/80-test_pkcs12_data/pbmac1_256_256.no-len.p12 b/test/recipes/80-test_pkcs12_data/pbmac1_256_256.no-len.p12
new file mode 100644
index 0000000000000000000000000000000000000000..35ebe05d177f7d745251e2fce3ebb4f23e0ebd09
GIT binary patch
literal 2700
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
literal 0
HcmV?d00001
diff --git a/test/recipes/80-test_pkcs12_data/pbmac1_512_256.good.p12 b/test/recipes/80-test_pkcs12_data/pbmac1_512_256.good.p12
new file mode 100644
index 0000000000000000000000000000000000000000..e8d4899691bfec94614bf1614c0e9d45b902cf24
GIT binary patch
literal 2702
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
VW!Nl&SdEOYi{CK0av2B!{0(@<()a)X
literal 0
HcmV?d00001
diff --git a/test/recipes/80-test_pkcs12_data/pbmac1_512_512.good.p12 b/test/recipes/80-test_pkcs12_data/pbmac1_512_512.good.p12
new file mode 100644
index 0000000000000000000000000000000000000000..64e14341a10d04e7e98cf83dbb2b6409ae1fd72f
GIT binary patch
literal 2736
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
D?Q7k<
literal 0
HcmV?d00001