summaryrefslogtreecommitdiffstats
path: root/crypto/cmp
diff options
context:
space:
mode:
authorDr. David von Oheimb <David.von.Oheimb@siemens.com>2022-09-17 10:56:21 +0200
committerPauli <pauli@openssl.org>2023-07-17 08:48:36 +1000
commit01b048513153bdbee3efc82389d38d353352a7f1 (patch)
treef22d8f0a44aeb740bc8a7ab837605f82dc10d8f3 /crypto/cmp
parent1a9e28607e29a1dc996024f03f445ca67b49a44f (diff)
CMP: add support for genm with rootCaCert and genp with rootCaKeyUpdate
Reviewed-by: Tomas Mraz <tomas@openssl.org> Reviewed-by: Paul Dale <pauli@openssl.org> (Merged from https://github.com/openssl/openssl/pull/21129)
Diffstat (limited to 'crypto/cmp')
-rw-r--r--crypto/cmp/cmp_asn.c91
-rw-r--r--crypto/cmp/cmp_err.c2
-rw-r--r--crypto/cmp/cmp_genm.c167
-rw-r--r--crypto/cmp/cmp_local.h22
4 files changed, 282 insertions, 0 deletions
diff --git a/crypto/cmp/cmp_asn.c b/crypto/cmp/cmp_asn.c
index b97c8323ff..73bc6363e0 100644
--- a/crypto/cmp/cmp_asn.c
+++ b/crypto/cmp/cmp_asn.c
@@ -116,6 +116,11 @@ ASN1_ADB(OSSL_CMP_ITAV) = {
ASN1_UTF8STRING)),
ADB_ENTRY(NID_id_it_caCerts,
ASN1_SEQUENCE_OF_OPT(OSSL_CMP_ITAV, infoValue.caCerts, X509)),
+ ADB_ENTRY(NID_id_it_rootCaCert,
+ ASN1_OPT(OSSL_CMP_ITAV, infoValue.rootCaCert, X509)),
+ ADB_ENTRY(NID_id_it_rootCaKeyUpdate,
+ ASN1_OPT(OSSL_CMP_ITAV, infoValue.rootCaKeyUpdate,
+ OSSL_CMP_ROOTCAKEYUPDATE)),
} ASN1_ADB_END(OSSL_CMP_ITAV, 0, infoType, 0,
&infotypeandvalue_default_tt, NULL);
@@ -126,6 +131,14 @@ ASN1_SEQUENCE(OSSL_CMP_ITAV) = {
IMPLEMENT_ASN1_FUNCTIONS(OSSL_CMP_ITAV)
IMPLEMENT_ASN1_DUP_FUNCTION(OSSL_CMP_ITAV)
+ASN1_SEQUENCE(OSSL_CMP_ROOTCAKEYUPDATE) = {
+ /* OSSL_CMP_CMPCERTIFICATE is effectively X509 so it is used directly */
+ ASN1_SIMPLE(OSSL_CMP_ROOTCAKEYUPDATE, newWithNew, X509),
+ ASN1_EXP_OPT(OSSL_CMP_ROOTCAKEYUPDATE, newWithOld, X509, 0),
+ ASN1_EXP_OPT(OSSL_CMP_ROOTCAKEYUPDATE, oldWithNew, X509, 1)
+} ASN1_SEQUENCE_END(OSSL_CMP_ROOTCAKEYUPDATE)
+IMPLEMENT_ASN1_FUNCTIONS(OSSL_CMP_ROOTCAKEYUPDATE)
+
OSSL_CMP_ITAV *OSSL_CMP_ITAV_create(ASN1_OBJECT *type, ASN1_TYPE *value)
{
OSSL_CMP_ITAV *itav;
@@ -215,6 +228,84 @@ int OSSL_CMP_ITAV_get0_caCerts(const OSSL_CMP_ITAV *itav, STACK_OF(X509) **out)
return 1;
}
+OSSL_CMP_ITAV *OSSL_CMP_ITAV_new_rootCaCert(const X509 *rootCaCert)
+{
+ OSSL_CMP_ITAV *itav = OSSL_CMP_ITAV_new();
+
+ if (itav == NULL)
+ return NULL;
+ if (rootCaCert != NULL
+ && (itav->infoValue.rootCaCert = X509_dup(rootCaCert)) == NULL) {
+ OSSL_CMP_ITAV_free(itav);
+ return NULL;
+ }
+ itav->infoType = OBJ_nid2obj(NID_id_it_rootCaCert);
+ return itav;
+}
+
+int OSSL_CMP_ITAV_get0_rootCaCert(const OSSL_CMP_ITAV *itav, X509 **out)
+{
+ if (itav == NULL || out == NULL) {
+ ERR_raise(ERR_LIB_CMP, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+ if (OBJ_obj2nid(itav->infoType) != NID_id_it_rootCaCert) {
+ ERR_raise(ERR_LIB_CMP, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
+ *out = itav->infoValue.rootCaCert;
+ return 1;
+}
+OSSL_CMP_ITAV *OSSL_CMP_ITAV_new_rootCaKeyUpdate(const X509 *newWithNew,
+ const X509 *newWithOld,
+ const X509 *oldWithNew)
+{
+ OSSL_CMP_ITAV *itav;
+ OSSL_CMP_ROOTCAKEYUPDATE *upd = OSSL_CMP_ROOTCAKEYUPDATE_new();
+
+ if (upd == NULL)
+ return NULL;
+ if (newWithNew != NULL && (upd->newWithNew = X509_dup(newWithNew)) == NULL)
+ goto err;
+ if (newWithOld != NULL && (upd->newWithOld = X509_dup(newWithOld)) == NULL)
+ goto err;
+ if (oldWithNew != NULL && (upd->oldWithNew = X509_dup(oldWithNew)) == NULL)
+ goto err;
+ if ((itav = OSSL_CMP_ITAV_new()) == NULL)
+ goto err;
+ itav->infoType = OBJ_nid2obj(NID_id_it_rootCaKeyUpdate);
+ itav->infoValue.rootCaKeyUpdate = upd;
+ return itav;
+
+ err:
+ OSSL_CMP_ROOTCAKEYUPDATE_free(upd);
+ return NULL;
+}
+
+int OSSL_CMP_ITAV_get0_rootCaKeyUpdate(const OSSL_CMP_ITAV *itav,
+ X509 **newWithNew,
+ X509 **newWithOld,
+ X509 **oldWithNew)
+{
+ OSSL_CMP_ROOTCAKEYUPDATE *upd;
+
+ if (itav == NULL || newWithNew == NULL) {
+ ERR_raise(ERR_LIB_CMP, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+ if (OBJ_obj2nid(itav->infoType) != NID_id_it_rootCaKeyUpdate) {
+ ERR_raise(ERR_LIB_CMP, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
+ upd = itav->infoValue.rootCaKeyUpdate;
+ *newWithNew = upd->newWithNew;
+ if (newWithOld != NULL)
+ *newWithOld = upd->newWithOld;
+ if (oldWithNew != NULL)
+ *oldWithNew = upd->oldWithNew;
+ return 1;
+}
+
/* get ASN.1 encoded integer, return -1 on error */
int ossl_cmp_asn1_get_int(const ASN1_INTEGER *a)
{
diff --git a/crypto/cmp/cmp_err.c b/crypto/cmp/cmp_err.c
index 9d0e968032..3853e52605 100644
--- a/crypto/cmp/cmp_err.c
+++ b/crypto/cmp/cmp_err.c
@@ -88,6 +88,8 @@ static const ERR_STRING_DATA CMP_str_reasons[] = {
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_INVALID_ARGS), "invalid args"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_INVALID_GENP), "invalid genp"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_INVALID_OPTION), "invalid option"},
+ {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_INVALID_ROOTCAKEYUPDATE),
+ "invalid rootcakeyupdate"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_MISSING_CERTID), "missing certid"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_MISSING_KEY_INPUT_FOR_CREATING_PROTECTION),
"missing key input for creating protection"},
diff --git a/crypto/cmp/cmp_genm.c b/crypto/cmp/cmp_genm.c
index 2faeaf0d28..74ac1d4b05 100644
--- a/crypto/cmp/cmp_genm.c
+++ b/crypto/cmp/cmp_genm.c
@@ -177,3 +177,170 @@ int OSSL_CMP_get1_caCerts(OSSL_CMP_CTX *ctx, STACK_OF(X509) **out)
OSSL_CMP_ITAV_free(itav);
return ret;
}
+
+static int selfsigned_verify_cb(int ok, X509_STORE_CTX *store_ctx)
+{
+ if (ok == 0 && store_ctx != NULL
+ && X509_STORE_CTX_get_error_depth(store_ctx) == 0
+ && X509_STORE_CTX_get_error(store_ctx)
+ == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) {
+ /* in this case, custom chain building */
+ int i;
+ STACK_OF(X509) *trust;
+ STACK_OF(X509) *chain = X509_STORE_CTX_get0_chain(store_ctx);
+ STACK_OF(X509) *untrusted = X509_STORE_CTX_get0_untrusted(store_ctx);
+ X509_STORE_CTX_check_issued_fn check_issued =
+ X509_STORE_CTX_get_check_issued(store_ctx);
+ X509 *cert = sk_X509_value(chain, 0); /* target cert */
+ X509 *issuer;
+
+ for (i = 0; i < sk_X509_num(untrusted); i++) {
+ cert = sk_X509_value(untrusted, i);
+ if (!X509_add_cert(chain, cert, X509_ADD_FLAG_UP_REF))
+ return 0;
+ }
+
+ trust = X509_STORE_get1_all_certs(X509_STORE_CTX_get0_store(store_ctx));
+ for (i = 0; i < sk_X509_num(trust); i++) {
+ issuer = sk_X509_value(trust, i);
+ if ((*check_issued)(store_ctx, cert, issuer)) {
+ if (X509_add_cert(chain, cert, X509_ADD_FLAG_UP_REF))
+ ok = 1;
+ break;
+ }
+ }
+ sk_X509_pop_free(trust, X509_free);
+ return ok;
+ } else {
+ X509_STORE *ts = X509_STORE_CTX_get0_store(store_ctx);
+ X509_STORE_CTX_verify_cb verify_cb;
+
+ if (ts == NULL || (verify_cb = X509_STORE_get_verify_cb(ts)) == NULL)
+ return ok;
+ return (*verify_cb)(ok, store_ctx);
+ }
+}
+
+/* vanilla X509_verify_cert() does not support self-signed certs as target */
+static int verify_ss_cert(OSSL_LIB_CTX *libctx, const char *propq,
+ X509_STORE *ts, STACK_OF(X509) *untrusted,
+ X509 *target)
+{
+ X509_STORE_CTX *csc = NULL;
+ int ok = 0;
+
+ if (ts == NULL || target == NULL) {
+ ERR_raise(ERR_LIB_CMP, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+
+ if ((csc = X509_STORE_CTX_new_ex(libctx, propq)) == NULL
+ || !X509_STORE_CTX_init(csc, ts, target, untrusted))
+ goto err;
+ X509_STORE_CTX_set_verify_cb(csc, selfsigned_verify_cb);
+ ok = X509_verify_cert(csc) > 0;
+
+ err:
+ X509_STORE_CTX_free(csc);
+ return ok;
+}
+
+static int
+verify_ss_cert_trans(OSSL_CMP_CTX *ctx, X509 *trusted /* may be NULL */,
+ X509 *trans /* the only untrusted cert, may be NULL */,
+ X509 *target, const char *desc)
+{
+ X509_STORE *ts = OSSL_CMP_CTX_get0_trusted(ctx);
+ STACK_OF(X509) *untrusted = NULL;
+ int res = 0;
+
+ if (trusted != NULL) {
+ X509_VERIFY_PARAM *vpm = X509_STORE_get0_param(ts);
+
+ if ((ts = X509_STORE_new()) == NULL)
+ return 0;
+ if (!X509_STORE_set1_param(ts, vpm)
+ || !X509_STORE_add_cert(ts, trusted))
+ goto err;
+ }
+
+ if (trans != NULL
+ && !ossl_x509_add_cert_new(&untrusted, trans, X509_ADD_FLAG_UP_REF))
+ goto err;
+
+ res = verify_ss_cert(OSSL_CMP_CTX_get0_libctx(ctx),
+ OSSL_CMP_CTX_get0_propq(ctx),
+ ts, untrusted, target);
+ if (!res)
+ ERR_raise_data(ERR_LIB_CMP, CMP_R_INVALID_ROOTCAKEYUPDATE,
+ "failed to validate %s certificate received in genp %s",
+ desc, trusted == NULL ? "using trust store"
+ : "with given certificate as trust anchor");
+
+ err:
+ sk_X509_pop_free(untrusted, X509_free);
+ if (trusted != NULL)
+ X509_STORE_free(ts);
+ return res;
+}
+
+int OSSL_CMP_get1_rootCaKeyUpdate(OSSL_CMP_CTX *ctx,
+ const X509 *oldWithOld, X509 **newWithNew,
+ X509 **newWithOld, X509 **oldWithNew)
+{
+ X509 *oldWithOld_copy = NULL, *my_newWithOld, *my_oldWithNew;
+ OSSL_CMP_ITAV *req, *itav;
+ int res = 0;
+
+ if (newWithNew == NULL) {
+ ERR_raise(ERR_LIB_CMP, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+ *newWithNew = NULL;
+
+ if ((req = OSSL_CMP_ITAV_new_rootCaCert(oldWithOld)) == NULL)
+ return 0;
+ itav = get_genm_itav(ctx, req, NID_id_it_rootCaKeyUpdate, "rootCaKeyUpdate");
+ if (itav == NULL)
+ return 0;
+
+ if (!OSSL_CMP_ITAV_get0_rootCaKeyUpdate(itav, newWithNew,
+ &my_newWithOld, &my_oldWithNew))
+ goto end;
+
+ if (*newWithNew == NULL) /* no root CA cert update available */
+ goto end;
+ if ((oldWithOld_copy = X509_dup(oldWithOld)) == NULL && oldWithOld != NULL)
+ goto end;
+ if (!verify_ss_cert_trans(ctx, oldWithOld_copy, my_newWithOld,
+ *newWithNew, "newWithNew")) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_INVALID_ROOTCAKEYUPDATE);
+ goto end;
+ }
+ if (oldWithOld != NULL && my_oldWithNew != NULL
+ && !verify_ss_cert_trans(ctx, *newWithNew, my_oldWithNew,
+ oldWithOld_copy, "oldWithOld")) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_INVALID_ROOTCAKEYUPDATE);
+ goto end;
+ }
+
+ if (!X509_up_ref(*newWithNew))
+ goto end;
+ if (newWithOld != NULL &&
+ (*newWithOld = my_newWithOld) != NULL && !X509_up_ref(*newWithOld))
+ goto free;
+ if (oldWithNew == NULL ||
+ (*oldWithNew = my_oldWithNew) == NULL || X509_up_ref(*oldWithNew)) {
+ res = 1;
+ goto end;
+ }
+ if (newWithOld != NULL)
+ X509_free(*newWithOld);
+ free:
+ X509_free(*newWithNew);
+
+ end:
+ OSSL_CMP_ITAV_free(itav);
+ X509_free(oldWithOld_copy);
+ return res;
+}
diff --git a/crypto/cmp/cmp_local.h b/crypto/cmp/cmp_local.h
index 18401ddb60..d1035c2da9 100644
--- a/crypto/cmp/cmp_local.h
+++ b/crypto/cmp/cmp_local.h
@@ -205,6 +205,9 @@ typedef struct ossl_cmp_cakeyupdanncontent_st {
} OSSL_CMP_CAKEYUPDANNCONTENT;
DECLARE_ASN1_FUNCTIONS(OSSL_CMP_CAKEYUPDANNCONTENT)
+typedef struct ossl_cmp_rootcakeyupdate_st OSSL_CMP_ROOTCAKEYUPDATE;
+DECLARE_ASN1_FUNCTIONS(OSSL_CMP_ROOTCAKEYUPDATE)
+
/*-
* declared already here as it will be used in OSSL_CMP_MSG (nested) and
* infoType and infoValue
@@ -252,6 +255,10 @@ struct ossl_cmp_itav_st {
STACK_OF(ASN1_UTF8STRING) *suppLangTagsValue;
/* NID_id_it_caCerts - CA Certificates */
STACK_OF(X509) *caCerts;
+ /* NID_id_it_rootCaCert - Root CA Certificate */
+ X509 *rootCaCert;
+ /* NID_id_it_rootCaKeyUpdate - Root CA Certificate Update */
+ OSSL_CMP_ROOTCAKEYUPDATE *rootCaKeyUpdate;
/* this is to be used for so far undeclared objects */
ASN1_TYPE *other;
} infoValue;
@@ -738,6 +745,21 @@ DECLARE_ASN1_FUNCTIONS(OSSL_CMP_PROTECTEDPART)
* }
*/
+/*
+ * RootCaKeyUpdateContent ::= SEQUENCE {
+ * newWithNew CMPCertificate,
+ * newWithOld [0] CMPCertificate OPTIONAL,
+ * oldWithNew [1] CMPCertificate OPTIONAL
+ * }
+ */
+
+struct ossl_cmp_rootcakeyupdate_st {
+ X509 *newWithNew;
+ X509 *newWithOld;
+ X509 *oldWithNew;
+} /* OSSL_CMP_ROOTCAKEYUPDATE */;
+DECLARE_ASN1_FUNCTIONS(OSSL_CMP_ROOTCAKEYUPDATE)
+
/* from cmp_asn.c */
int ossl_cmp_asn1_get_int(const ASN1_INTEGER *a);