diff options
author | Dr. David von Oheimb <David.von.Oheimb@siemens.com> | 2022-09-17 10:56:21 +0200 |
---|---|---|
committer | Pauli <pauli@openssl.org> | 2023-07-17 08:48:36 +1000 |
commit | 01b048513153bdbee3efc82389d38d353352a7f1 (patch) | |
tree | f22d8f0a44aeb740bc8a7ab837605f82dc10d8f3 /crypto/cmp | |
parent | 1a9e28607e29a1dc996024f03f445ca67b49a44f (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.c | 91 | ||||
-rw-r--r-- | crypto/cmp/cmp_err.c | 2 | ||||
-rw-r--r-- | crypto/cmp/cmp_genm.c | 167 | ||||
-rw-r--r-- | crypto/cmp/cmp_local.h | 22 |
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); |