From: Rajeev Ranjan Date: Wed, 29 May 2024 16:19:29 +0000 (+0200) Subject: CMP: add support for central key generation X-Git-Tag: openssl-3.5.0-alpha1~682 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0048817523b6b9d0bf514c90ad9c6a99167d0293;p=thirdparty%2Fopenssl.git CMP: add support for central key generation - add testcase for central keygen - add documentation Reviewed-by: David von Oheimb Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/25132) --- diff --git a/CHANGES.md b/CHANGES.md index 1369f5d826a..51d17b3a9b6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -85,6 +85,12 @@ OpenSSL 3.5 *David von Oheimb* + * Added support for central key generation in CMP. + + This work was sponsored by Siemens AG. + + *Rajeev Ranjan* + * Optionally allow the FIPS provider to use the `JITTER` entropy source. Note that using this option will require the resulting FIPS provider to undergo entropy source validation [ESV] by the [CMVP], without this diff --git a/apps/cmp.c b/apps/cmp.c index 3fa7dd93618..c33745cdddc 100644 --- a/apps/cmp.c +++ b/apps/cmp.c @@ -124,6 +124,8 @@ static char *opt_profile = NULL; /* certificate enrollment */ static char *opt_newkey = NULL; static char *opt_newkeypass = NULL; +static int opt_centralkeygen = 0; +static char *opt_newkeyout = NULL; static char *opt_subject = NULL; static int opt_days = 0; static char *opt_reqexts = NULL; @@ -199,6 +201,8 @@ static char *opt_srv_trusted = NULL; static char *opt_srv_untrusted = NULL; static char *opt_ref_cert = NULL; static char *opt_rsp_cert = NULL; +static char *opt_rsp_key = NULL; +static char *opt_rsp_keypass = NULL; static char *opt_rsp_crl = NULL; static char *opt_rsp_extracerts = NULL; static char *opt_rsp_capubs = NULL; @@ -230,7 +234,8 @@ typedef enum OPTION_choice { OPT_CMD, OPT_INFOTYPE, OPT_PROFILE, OPT_GENINFO, OPT_TEMPLATE, OPT_KEYSPEC, - OPT_NEWKEY, OPT_NEWKEYPASS, OPT_SUBJECT, + OPT_NEWKEY, OPT_NEWKEYPASS, OPT_CENTRALKEYGEN, + OPT_NEWKEYOUT, OPT_SUBJECT, OPT_DAYS, OPT_REQEXTS, OPT_SANS, OPT_SAN_NODEFAULT, OPT_POLICIES, OPT_POLICY_OIDS, OPT_POLICY_OIDS_CRITICAL, @@ -282,7 +287,8 @@ typedef enum OPTION_choice { OPT_SRV_REF, OPT_SRV_SECRET, OPT_SRV_CERT, OPT_SRV_KEY, OPT_SRV_KEYPASS, OPT_SRV_TRUSTED, OPT_SRV_UNTRUSTED, - OPT_REF_CERT, OPT_RSP_CERT, OPT_RSP_CRL, OPT_RSP_EXTRACERTS, OPT_RSP_CAPUBS, + OPT_REF_CERT, OPT_RSP_CERT, OPT_RSP_KEY, OPT_RSP_KEYPASS, + OPT_RSP_CRL, OPT_RSP_EXTRACERTS, OPT_RSP_CAPUBS, OPT_RSP_NEWWITHNEW, OPT_RSP_NEWWITHOLD, OPT_RSP_OLDWITHNEW, OPT_POLL_COUNT, OPT_CHECK_AFTER, OPT_GRANT_IMPLICITCONF, @@ -326,6 +332,10 @@ const OPTIONS cmp_options[] = { {"newkey", OPT_NEWKEY, 's', "Private or public key for the requested cert. Default: CSR key or client key"}, {"newkeypass", OPT_NEWKEYPASS, 's', "New private key pass phrase source"}, + {"centralkeygen", OPT_CENTRALKEYGEN, '-', + "Request central (server-side) key generation. Default is local generation"}, + {"newkeyout", OPT_NEWKEYOUT, 's', + "File to save centrally generated key, in PEM format"}, {"subject", OPT_SUBJECT, 's', "Distinguished Name (DN) of subject to use in the requested cert template"}, {OPT_MORE_STR, 0, 0, @@ -571,6 +581,12 @@ const OPTIONS cmp_options[] = { "Certificate to be expected for rr and any oldCertID in kur messages"}, {"rsp_cert", OPT_RSP_CERT, 's', "Certificate to be returned as mock enrollment result"}, + {"rsp_key", OPT_RSP_KEY, 's', + "Private key for the certificate to be returned as mock enrollment result"}, + {OPT_MORE_STR, 0, 0, + "Key to be returned for central key pair generation"}, + {"rsp_keypass", OPT_RSP_KEYPASS, 's', + "Response private key (and cert) pass phrase source"}, {"rsp_crl", OPT_RSP_CRL, 's', "CRL to be returned in genp of type crls"}, {"rsp_extracerts", OPT_RSP_EXTRACERTS, 's', @@ -630,8 +646,8 @@ static varref cmp_vars[] = { /* must be in same order as enumerated above! */ {&opt_cmd_s}, {&opt_infotype_s}, {&opt_profile}, {&opt_geninfo}, {&opt_template}, {&opt_keyspec}, - {&opt_newkey}, {&opt_newkeypass}, {&opt_subject}, - {(char **)&opt_days}, {&opt_reqexts}, + {&opt_newkey}, {&opt_newkeypass}, {(char **)&opt_centralkeygen}, + {&opt_newkeyout}, {&opt_subject}, {(char **)&opt_days}, {&opt_reqexts}, {&opt_sans}, {(char **)&opt_san_nodefault}, {&opt_policies}, {&opt_policy_oids}, {(char **)&opt_policy_oids_critical}, {(char **)&opt_popo}, {&opt_csr}, @@ -683,8 +699,8 @@ static varref cmp_vars[] = { /* must be in same order as enumerated above! */ {&opt_srv_ref}, {&opt_srv_secret}, {&opt_srv_cert}, {&opt_srv_key}, {&opt_srv_keypass}, {&opt_srv_trusted}, {&opt_srv_untrusted}, - {&opt_ref_cert}, {&opt_rsp_cert}, {&opt_rsp_crl}, - {&opt_rsp_extracerts}, {&opt_rsp_capubs}, + {&opt_ref_cert}, {&opt_rsp_cert}, {&opt_rsp_key}, {&opt_rsp_keypass}, + {&opt_rsp_crl}, {&opt_rsp_extracerts}, {&opt_rsp_capubs}, {&opt_rsp_newwithnew}, {&opt_rsp_newwithold}, {&opt_rsp_oldwithnew}, {(char **)&opt_poll_count}, {(char **)&opt_check_after}, @@ -1197,11 +1213,25 @@ static OSSL_CMP_SRV_CTX *setup_srv_ctx(ENGINE *engine) if (opt_rsp_cert == NULL) { CMP_warn("no -rsp_cert given for mock server"); } else { - if (!setup_cert(srv_ctx, opt_rsp_cert, opt_keypass, + if (!setup_cert(srv_ctx, opt_rsp_cert, opt_rsp_keypass, "cert the mock server returns on certificate requests", (add_X509_fn_t)ossl_cmp_mock_srv_set1_certOut)) goto err; } + if (opt_rsp_key != NULL) { + EVP_PKEY *pkey = load_key_pwd(opt_rsp_key, opt_keyform, + opt_rsp_keypass, engine, + "private key for enrollment cert"); + + if (pkey == NULL + || !ossl_cmp_mock_srv_set1_keyOut(srv_ctx, pkey)) { + EVP_PKEY_free(pkey); + goto err; + } + EVP_PKEY_free(pkey); + } + cleanse(opt_rsp_keypass); + if (!setup_mock_crlout(srv_ctx, opt_rsp_crl, "CRL to be returned by the mock server")) goto err; @@ -1672,11 +1702,27 @@ static int setup_request_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine) if (!set_name(opt_issuer, OSSL_CMP_CTX_set1_issuer, ctx, "issuer")) return 0; if (opt_cmd == CMP_IR || opt_cmd == CMP_CR || opt_cmd == CMP_KUR) { - if (opt_reqin == NULL && opt_newkey == NULL + if (opt_reqin == NULL && opt_newkey == NULL && !opt_centralkeygen && opt_key == NULL && opt_csr == NULL && opt_oldcert == NULL) { - CMP_err("missing -newkey (or -key) to be certified and no -csr, -oldcert, -cert, or -reqin option given, which could provide fallback public key"); + CMP_err("missing -newkey (or -key) to be certified and no -csr, -oldcert, -cert, or -reqin option given, which could provide fallback public key." + " Neither central key generation is requested."); return 0; } + if (opt_popo == OSSL_CRMF_POPO_NONE && !opt_centralkeygen) { + CMP_info("POPO is disabled, which implies -centralkeygen"); + opt_centralkeygen = 1; + } + if (opt_centralkeygen) { + if (opt_popo > OSSL_CRMF_POPO_NONE) { + CMP_err1("-popo value %d is inconsistent with -centralkeygen", opt_popo); + return 0; + } + if (opt_newkeyout == NULL) { + CMP_err("-newkeyout not given, nowhere to save centrally generated key"); + return 0; + } + opt_popo = OSSL_CRMF_POPO_NONE; + } if (opt_newkey == NULL && opt_popo != OSSL_CRMF_POPO_NONE && opt_popo != OSSL_CRMF_POPO_RAVERIFIED) { @@ -1724,6 +1770,12 @@ static int setup_request_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine) CMP_warn1("-policies %s", msg); if (opt_policy_oids != NULL) CMP_warn1("-policy_oids %s", msg); + if (opt_popo != OSSL_CRMF_POPO_NONE - 1) + CMP_warn1("-popo %s", msg); + if (opt_centralkeygen) + CMP_warn1("-popo -1 or -centralkeygen %s", msg); + if (opt_newkeyout != NULL) + CMP_warn1("-newkeyout %s", msg); if (opt_cmd != CMP_P10CR) { if (opt_implicit_confirm) CMP_warn1("-implicit_confirm %s, and 'p10cr'", msg); @@ -1828,13 +1880,14 @@ static int setup_request_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine) pkey = load_pubkey(file, format, 0, pass, engine, desc); priv = 0; } - cleanse(opt_newkeypass); + if (pkey == NULL || !OSSL_CMP_CTX_set0_newPkey(ctx, priv, pkey)) { EVP_PKEY_free(pkey); return 0; } } else if (opt_reqin != NULL - && opt_key == NULL && opt_csr == NULL && opt_oldcert == NULL) { + && opt_key == NULL && opt_csr == NULL && opt_oldcert == NULL + && !opt_centralkeygen) { if (!set_fallback_pubkey(ctx)) return 0; } @@ -2922,13 +2975,18 @@ static int get_opts(int argc, char **argv) case OPT_KEYSPEC: opt_keyspec = opt_str(); break; - case OPT_NEWKEY: opt_newkey = opt_str(); break; case OPT_NEWKEYPASS: opt_newkeypass = opt_str(); break; + case OPT_CENTRALKEYGEN: + opt_centralkeygen = 1; + break; + case OPT_NEWKEYOUT: + opt_newkeyout = opt_str(); + break; case OPT_SUBJECT: opt_subject = opt_str(); break; @@ -3086,6 +3144,12 @@ static int get_opts(int argc, char **argv) case OPT_RSP_CERT: opt_rsp_cert = opt_str(); break; + case OPT_RSP_KEY: + opt_rsp_key = opt_str(); + break; + case OPT_RSP_KEYPASS: + opt_rsp_keypass = opt_str(); + break; case OPT_RSP_CRL: opt_rsp_crl = opt_str(); break; @@ -3793,6 +3857,34 @@ int cmp_main(int argc, char **argv) if (save_free_certs(OSSL_CMP_CTX_get1_caPubs(cmp_ctx), opt_cacertsout, "CA") < 0) goto err; + if (opt_centralkeygen) { + EVP_CIPHER *cipher = NULL; + char *pass_string = NULL; + BIO *out; + int result = 1; + EVP_PKEY *new_key = OSSL_CMP_CTX_get0_newPkey(cmp_ctx, 1 /* priv */); + + if (new_key == NULL) + goto err; + if ((out = bio_open_owner(opt_newkeyout, FORMAT_PEM, 1)) == NULL) + goto err; + if (opt_newkeypass != NULL) { + pass_string = get_passwd(opt_newkeypass, + "Centrally generated private key password"); + cipher = EVP_CIPHER_fetch(app_get0_libctx(), SN_aes_256_cbc, app_get0_propq()); + } + + CMP_info1("saving centrally generated key to file '%s'", opt_newkeyout); + if (PEM_write_bio_PrivateKey(out, new_key, cipher, NULL, 0, NULL, + (void *)pass_string) <= 0) + result = 0; + + BIO_free(out); + clear_free(pass_string); + EVP_CIPHER_free(cipher); + if (!result) + goto err; + } } if (!OSSL_CMP_CTX_reinit(cmp_ctx)) goto err; diff --git a/apps/include/cmp_mock_srv.h b/apps/include/cmp_mock_srv.h index cddbe8bef50..776318fa8ba 100644 --- a/apps/include/cmp_mock_srv.h +++ b/apps/include/cmp_mock_srv.h @@ -22,6 +22,7 @@ void ossl_cmp_mock_srv_free(OSSL_CMP_SRV_CTX *srv_ctx); int ossl_cmp_mock_srv_set1_refCert(OSSL_CMP_SRV_CTX *srv_ctx, X509 *cert); int ossl_cmp_mock_srv_set1_certOut(OSSL_CMP_SRV_CTX *srv_ctx, X509 *cert); +int ossl_cmp_mock_srv_set1_keyOut(OSSL_CMP_SRV_CTX *srv_ctx, EVP_PKEY *pkey); int ossl_cmp_mock_srv_set1_crlOut(OSSL_CMP_SRV_CTX *srv_ctx, X509_CRL *crl); int ossl_cmp_mock_srv_set1_chainOut(OSSL_CMP_SRV_CTX *srv_ctx, STACK_OF(X509) *chain); diff --git a/apps/lib/cmp_mock_srv.c b/apps/lib/cmp_mock_srv.c index 5bc166036ec..b35ad0fe914 100644 --- a/apps/lib/cmp_mock_srv.c +++ b/apps/lib/cmp_mock_srv.c @@ -19,6 +19,7 @@ typedef struct { X509 *refCert; /* cert to expect for oldCertID in kur/rr msg */ X509 *certOut; /* certificate to be returned in cp/ip/kup msg */ + EVP_PKEY *keyOut; /* Private key to be returned for central keygen */ X509_CRL *crlOut; /* CRL to be returned in genp for crls */ STACK_OF(X509) *chainOut; /* chain of certOut to add to extraCerts field */ STACK_OF(X509) *caPubsOut; /* used in caPubs of ip and in caCerts of genp */ @@ -87,6 +88,21 @@ static mock_srv_ctx *mock_srv_ctx_new(void) DEFINE_OSSL_SET1_CERT(refCert) DEFINE_OSSL_SET1_CERT(certOut) +int ossl_cmp_mock_srv_set1_keyOut(OSSL_CMP_SRV_CTX *srv_ctx, EVP_PKEY *pkey) +{ + mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx); + + if (ctx == NULL) { + ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT); + return 0; + } + if (pkey != NULL && !EVP_PKEY_up_ref(pkey)) + return 0; + EVP_PKEY_free(ctx->keyOut); + ctx->keyOut = pkey; + return 1; +} + int ossl_cmp_mock_srv_set1_crlOut(OSSL_CMP_SRV_CTX *srv_ctx, X509_CRL *crl) { @@ -273,8 +289,9 @@ static OSSL_CMP_PKISI *process_cert_request(OSSL_CMP_SRV_CTX *srv_ctx, STACK_OF(X509) **caPubs) { mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx); - int bodytype; + int bodytype, central_keygen; OSSL_CMP_PKISI *si = NULL; + EVP_PKEY *keyOut = NULL; if (ctx == NULL || cert_req == NULL || certOut == NULL || chainOut == NULL || caPubs == NULL) { @@ -358,6 +375,23 @@ static OSSL_CMP_PKISI *process_cert_request(OSSL_CMP_SRV_CTX *srv_ctx, && (*certOut = X509_dup(ctx->certOut)) == NULL) /* Should return a cert produced from request template, see FR #16054 */ goto err; + + central_keygen = OSSL_CRMF_MSG_centralkeygen_requested(crm, p10cr); + if (central_keygen < 0) + goto err; + if (central_keygen == 1 + && (ctx->keyOut == NULL + || (keyOut = EVP_PKEY_dup(ctx->keyOut)) == NULL + || !OSSL_CMP_CTX_set0_newPkey(OSSL_CMP_SRV_CTX_get0_cmp_ctx(srv_ctx), + 1 /* priv */, keyOut))) { + EVP_PKEY_free(keyOut); + goto err; + } + /* + * Note that this uses newPkey to return the private key + * and does not check whether the 'popo' field is absent. + */ + if (ctx->chainOut != NULL && (*chainOut = X509_chain_up_ref(ctx->chainOut)) == NULL) goto err; diff --git a/crypto/cmp/cmp_asn.c b/crypto/cmp/cmp_asn.c index 13fd6f898d0..fa5a3e9831a 100644 --- a/crypto/cmp/cmp_asn.c +++ b/crypto/cmp/cmp_asn.c @@ -891,7 +891,7 @@ ASN1_CHOICE(OSSL_CMP_CERTORENCCERT) = { /* OSSL_CMP_CMPCERTIFICATE is effectively X509 so it is used directly */ ASN1_EXP(OSSL_CMP_CERTORENCCERT, value.certificate, X509, 0), ASN1_EXP(OSSL_CMP_CERTORENCCERT, value.encryptedCert, - OSSL_CRMF_ENCRYPTEDVALUE, 1), + OSSL_CRMF_ENCRYPTEDKEY, 1), } ASN1_CHOICE_END(OSSL_CMP_CERTORENCCERT) IMPLEMENT_ASN1_FUNCTIONS(OSSL_CMP_CERTORENCCERT) @@ -899,7 +899,7 @@ ASN1_SEQUENCE(OSSL_CMP_CERTIFIEDKEYPAIR) = { ASN1_SIMPLE(OSSL_CMP_CERTIFIEDKEYPAIR, certOrEncCert, OSSL_CMP_CERTORENCCERT), ASN1_EXP_OPT(OSSL_CMP_CERTIFIEDKEYPAIR, privateKey, - OSSL_CRMF_ENCRYPTEDVALUE, 0), + OSSL_CRMF_ENCRYPTEDKEY, 0), ASN1_EXP_OPT(OSSL_CMP_CERTIFIEDKEYPAIR, publicationInfo, OSSL_CRMF_PKIPUBLICATIONINFO, 1) } ASN1_SEQUENCE_END(OSSL_CMP_CERTIFIEDKEYPAIR) diff --git a/crypto/cmp/cmp_client.c b/crypto/cmp/cmp_client.c index e8fe6f30dcb..b88c005c854 100644 --- a/crypto/cmp/cmp_client.c +++ b/crypto/cmp/cmp_client.c @@ -656,7 +656,7 @@ static int cert_response(OSSL_CMP_CTX *ctx, int sleep, int rid, ossl_unused int req_type, ossl_unused int expected_type) { - EVP_PKEY *rkey = ossl_cmp_ctx_get0_newPubkey(ctx); + EVP_PKEY *rkey = NULL; int fail_info = 0; /* no failure */ const char *txt = NULL; OSSL_CMP_CERTREPMESSAGE *crepmsg = NULL; @@ -748,6 +748,7 @@ static int cert_response(OSSL_CMP_CTX *ctx, int sleep, int rid, return 0; subj = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); + rkey = ossl_cmp_ctx_get0_newPubkey(ctx); if (rkey != NULL /* X509_check_private_key() also works if rkey is just public key */ && !(X509_check_private_key(ctx->newCert, rkey))) { diff --git a/crypto/cmp/cmp_err.c b/crypto/cmp/cmp_err.c index 5cec9438f61..2c8cdc18b75 100644 --- a/crypto/cmp/cmp_err.c +++ b/crypto/cmp/cmp_err.c @@ -79,6 +79,8 @@ static const ERR_STRING_DATA CMP_str_reasons[] = { {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_EXPECTED_POLLREQ), "expected pollreq"}, {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_FAILED_BUILDING_OWN_CHAIN), "failed building own chain"}, + {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_FAILED_EXTRACTING_CENTRAL_GEN_KEY), + "failed extracting central gen key"}, {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_FAILED_EXTRACTING_PUBKEY), "failed extracting pubkey"}, {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_FAILURE_OBTAINING_RANDOM), @@ -97,6 +99,8 @@ static const ERR_STRING_DATA CMP_str_reasons[] = { {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_CENTRAL_GEN_KEY), + "missing central gen key"}, {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"}, @@ -151,6 +155,8 @@ static const ERR_STRING_DATA CMP_str_reasons[] = { "transactionid unmatched"}, {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_TRANSFER_ERROR), "transfer error"}, {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNCLEAN_CTX), "unclean ctx"}, + {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNEXPECTED_CENTRAL_GEN_KEY), + "unexpected central gen key"}, {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNEXPECTED_CERTPROFILE), "unexpected certprofile"}, {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNEXPECTED_CRLSTATUSLIST), diff --git a/crypto/cmp/cmp_local.h b/crypto/cmp/cmp_local.h index 59708037970..da616e668a1 100644 --- a/crypto/cmp/cmp_local.h +++ b/crypto/cmp/cmp_local.h @@ -311,7 +311,7 @@ typedef struct ossl_cmp_certorenccert_st { int type; union { X509 *certificate; - OSSL_CRMF_ENCRYPTEDVALUE *encryptedCert; + OSSL_CRMF_ENCRYPTEDKEY *encryptedCert; } value; } OSSL_CMP_CERTORENCCERT; DECLARE_ASN1_FUNCTIONS(OSSL_CMP_CERTORENCCERT) @@ -326,7 +326,7 @@ DECLARE_ASN1_FUNCTIONS(OSSL_CMP_CERTORENCCERT) */ typedef struct ossl_cmp_certifiedkeypair_st { OSSL_CMP_CERTORENCCERT *certOrEncCert; - OSSL_CRMF_ENCRYPTEDVALUE *privateKey; + OSSL_CRMF_ENCRYPTEDKEY *privateKey; OSSL_CRMF_PKIPUBLICATIONINFO *publicationInfo; } OSSL_CMP_CERTIFIEDKEYPAIR; DECLARE_ASN1_FUNCTIONS(OSSL_CMP_CERTIFIEDKEYPAIR) @@ -952,7 +952,8 @@ OSSL_CMP_MSG *ossl_cmp_certreq_new(OSSL_CMP_CTX *ctx, int bodytype, const OSSL_CRMF_MSG *crm); OSSL_CMP_MSG *ossl_cmp_certrep_new(OSSL_CMP_CTX *ctx, int bodytype, int certReqId, const OSSL_CMP_PKISI *si, - X509 *cert, const X509 *encryption_recip, + X509 *cert, const EVP_PKEY *pkey, + const X509 *encryption_recip, STACK_OF(X509) *chain, STACK_OF(X509) *caPubs, int unprotectedErrors); OSSL_CMP_MSG *ossl_cmp_rr_new(OSSL_CMP_CTX *ctx); @@ -994,6 +995,7 @@ OSSL_CMP_MSG *ossl_cmp_msg_load(const char *file); int ossl_cmp_is_error_with_waiting(const OSSL_CMP_MSG *msg); /* from cmp_protect.c */ +void ossl_cmp_set_own_chain(OSSL_CMP_CTX *ctx); int ossl_cmp_msg_add_extraCerts(OSSL_CMP_CTX *ctx, OSSL_CMP_MSG *msg); ASN1_BIT_STRING *ossl_cmp_calc_protection(const OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *msg); diff --git a/crypto/cmp/cmp_msg.c b/crypto/cmp/cmp_msg.c index a0b3fb0aea0..952fddf8206 100644 --- a/crypto/cmp/cmp_msg.c +++ b/crypto/cmp/cmp_msg.c @@ -19,6 +19,9 @@ #include #include #include +#include +#include +#include OSSL_CMP_MSG *OSSL_CMP_MSG_new(OSSL_LIB_CTX *libctx, const char *propq) { @@ -271,6 +274,8 @@ static const X509_NAME *determine_subj(OSSL_CMP_CTX *ctx, int for_KUR, OSSL_CRMF_MSG *OSSL_CMP_CTX_setup_CRM(OSSL_CMP_CTX *ctx, int for_KUR, int rid) { OSSL_CRMF_MSG *crm = NULL; + int central_keygen = OSSL_CMP_CTX_get_option(ctx, OSSL_CMP_OPT_POPO_METHOD) + == OSSL_CRMF_POPO_NONE; X509 *refcert = ctx->oldCert != NULL ? ctx->oldCert : ctx->cert; /* refcert defaults to current client cert */ EVP_PKEY *rkey = ossl_cmp_ctx_get0_newPubkey(ctx); @@ -283,9 +288,10 @@ OSSL_CRMF_MSG *OSSL_CMP_CTX_setup_CRM(OSSL_CMP_CTX *ctx, int for_KUR, int rid) : X509_get_issuer_name(refcert); int crit = ctx->setSubjectAltNameCritical || subject == NULL; /* RFC5280: subjectAltName MUST be critical if subject is null */ + OSSL_CRMF_CERTTEMPLATE *tmpl; X509_EXTENSIONS *exts = NULL; - if (rkey == NULL) { + if (rkey == NULL && !central_keygen) { #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION ERR_raise(ERR_LIB_CMP, CMP_R_MISSING_PUBLIC_KEY); return NULL; @@ -297,6 +303,7 @@ OSSL_CRMF_MSG *OSSL_CMP_CTX_setup_CRM(OSSL_CMP_CTX *ctx, int for_KUR, int rid) } if ((crm = OSSL_CRMF_MSG_new()) == NULL) return NULL; + tmpl = OSSL_CRMF_MSG_get0_tmpl(crm); if (!OSSL_CRMF_MSG_set_certReqId(crm, rid) /* * fill certTemplate, corresponding to CertificationRequestInfo @@ -306,6 +313,10 @@ OSSL_CRMF_MSG *OSSL_CMP_CTX_setup_CRM(OSSL_CMP_CTX *ctx, int for_KUR, int rid) || !OSSL_CRMF_CERTTEMPLATE_fill(OSSL_CRMF_MSG_get0_tmpl(crm), rkey, subject, issuer, NULL /* serial */)) goto err; + if (rkey != NULL && central_keygen) + X509_PUBKEY_set0_public_key(OSSL_CRMF_CERTTEMPLATE_get0_publicKey(tmpl), + NULL, 0); + if (ctx->days != 0) { time_t now = time(NULL); ASN1_TIME *notBefore = ASN1_TIME_adj(NULL, now, 0, 0); @@ -441,9 +452,47 @@ OSSL_CMP_MSG *ossl_cmp_certreq_new(OSSL_CMP_CTX *ctx, int type, return NULL; } +#ifndef OPENSSL_NO_CMS +static OSSL_CRMF_ENCRYPTEDKEY *enc_privkey(OSSL_CMP_CTX *ctx, const EVP_PKEY *pkey) +{ + OSSL_CRMF_ENCRYPTEDKEY *ek = NULL; + CMS_EnvelopedData *envData = NULL; + BIO *privbio = NULL; + EVP_CIPHER *cipher = NULL; + X509 *recip = ctx->validatedSrvCert; /* this is the client cert */ + STACK_OF(X509) *encryption_recips = sk_X509_new_reserve(NULL, 1); + + if (encryption_recips == NULL + || !X509_add_cert(encryption_recips, recip, X509_ADD_FLAG_UP_REF)) + goto err; + + privbio = BIO_new(BIO_s_mem()); + if (privbio == NULL || i2d_PrivateKey_bio(privbio, pkey) <= 0) + goto err; + ossl_cmp_set_own_chain(ctx); + cipher = EVP_CIPHER_fetch(ctx->libctx, SN_aes_256_cbc, ctx->propq); + envData = ossl_cms_sign_encrypt(privbio, ctx->cert, ctx->chain, ctx->pkey, CMS_BINARY, + encryption_recips, cipher, CMS_BINARY, + ctx->libctx, ctx->propq); + EVP_CIPHER_free(cipher); + if (envData == NULL) + goto err; + ek = OSSL_CRMF_ENCRYPTEDKEY_init_envdata(envData); + + err: + sk_X509_pop_free(encryption_recips, X509_free); + BIO_free(privbio); + if (ek == NULL) + M_ASN1_free_of(envData, CMS_EnvelopedData); + + return ek; +} +#endif + OSSL_CMP_MSG *ossl_cmp_certrep_new(OSSL_CMP_CTX *ctx, int bodytype, int certReqId, const OSSL_CMP_PKISI *si, - X509 *cert, const X509 *encryption_recip, + X509 *cert, const EVP_PKEY *pkey, + const X509 *encryption_recip, STACK_OF(X509) *chain, STACK_OF(X509) *caPubs, int unprotectedErrors) { @@ -487,6 +536,16 @@ OSSL_CMP_MSG *ossl_cmp_certrep_new(OSSL_CMP_CTX *ctx, int bodytype, if (!X509_up_ref(cert)) goto err; resp->certifiedKeyPair->certOrEncCert->value.certificate = cert; + + if (pkey != NULL) { +#ifndef OPENSSL_NO_CMS + if ((resp->certifiedKeyPair->privateKey = enc_privkey(ctx, pkey)) == NULL) + goto err; +#else + ERR_raise(ERR_LIB_CMP, ERR_R_UNSUPPORTED); + goto err; +#endif + } } if (!sk_OSSL_CMP_CERTRESPONSE_push(repMsg->response, resp)) @@ -1042,22 +1101,51 @@ ossl_cmp_certrepmessage_get0_certresponse(const OSSL_CMP_CERTREPMESSAGE *crm, } /*- - * Retrieve the newly enrolled certificate from the given certResponse crep. - * Uses libctx and propq from ctx, in case of indirect POPO also private key. + * Retrieve newly enrolled certificate and key from the given certResponse crep. + * Stores any centrally generated key in ctx->newPkey. + * In case of indirect POPO uses ctx->newPkey to decrypt the new certificate. * Returns a pointer to a copy of the found certificate, or NULL if not found. */ -X509 *ossl_cmp_certresponse_get1_cert(const OSSL_CMP_CTX *ctx, - const OSSL_CMP_CERTRESPONSE *crep) +X509 *ossl_cmp_certresponse_get1_cert(const OSSL_CMP_CTX *ctx, const OSSL_CMP_CERTRESPONSE *crep) { OSSL_CMP_CERTORENCCERT *coec; X509 *crt = NULL; - EVP_PKEY *pkey; + OSSL_CRMF_ENCRYPTEDKEY *encr_key; + EVP_PKEY *pkey = NULL; + int central_keygen = OSSL_CMP_CTX_get_option(ctx, OSSL_CMP_OPT_POPO_METHOD) + == OSSL_CRMF_POPO_NONE; + + if (crep->certifiedKeyPair == NULL) { + ERR_raise(ERR_LIB_CMP, CMP_R_CERTIFICATE_NOT_FOUND); + return NULL; + } + encr_key = crep->certifiedKeyPair->privateKey; + if (encr_key == NULL && central_keygen) { + ERR_raise(ERR_LIB_CMP, CMP_R_MISSING_CENTRAL_GEN_KEY); + return NULL; + } + if (encr_key != NULL) { + if (!central_keygen) { + ERR_raise(ERR_LIB_CMP, CMP_R_UNEXPECTED_CENTRAL_GEN_KEY); + return NULL; + } + /* found encrypted private key, try to extract */ + pkey = OSSL_CRMF_ENCRYPTEDKEY_get1_pkey(encr_key, ctx->trusted, + ctx->untrusted, + ctx->pkey, ctx->cert, + ctx->secretValue, + ctx->libctx, ctx->propq); + if (pkey == NULL) { + ERR_raise(ERR_LIB_CMP, CMP_R_FAILED_EXTRACTING_CENTRAL_GEN_KEY); + return NULL; + } + OSSL_CMP_CTX_set0_newPkey((OSSL_CMP_CTX *)ctx, 1, pkey); + } if (!ossl_assert(crep != NULL && ctx != NULL)) return NULL; - if (crep->certifiedKeyPair - && (coec = crep->certifiedKeyPair->certOrEncCert) != NULL) { + if ((coec = crep->certifiedKeyPair->certOrEncCert) != NULL) { switch (coec->type) { case OSSL_CMP_CERTORENCCERT_CERTIFICATE: crt = X509_dup(coec->value.certificate); @@ -1070,10 +1158,8 @@ X509 *ossl_cmp_certresponse_get1_cert(const OSSL_CMP_CTX *ctx, ERR_raise(ERR_LIB_CMP, CMP_R_MISSING_PRIVATE_KEY); return NULL; } - crt = - OSSL_CRMF_ENCRYPTEDVALUE_get1_encCert(coec->value.encryptedCert, - ctx->libctx, ctx->propq, - pkey); + crt = OSSL_CRMF_ENCRYPTEDKEY_get1_encCert(coec->value.encryptedCert, + ctx->libctx, ctx->propq, pkey, 0); break; default: ERR_raise(ERR_LIB_CMP, CMP_R_UNKNOWN_CERT_TYPE); diff --git a/crypto/cmp/cmp_protect.c b/crypto/cmp/cmp_protect.c index f59fee44ec2..fcca08ef4eb 100644 --- a/crypto/cmp/cmp_protect.c +++ b/crypto/cmp/cmp_protect.c @@ -130,6 +130,25 @@ ASN1_BIT_STRING *ossl_cmp_calc_protection(const OSSL_CMP_CTX *ctx, } } +void ossl_cmp_set_own_chain(OSSL_CMP_CTX *ctx) +{ + if (!ossl_assert(ctx != NULL)) + return; + /* if not yet done try to build chain using available untrusted certs */ + if (ctx->chain == NULL) { + ossl_cmp_debug(ctx, "trying to build chain for own CMP signer cert"); + ctx->chain = X509_build_chain(ctx->cert, ctx->untrusted, NULL, 0, + ctx->libctx, ctx->propq); + if (ctx->chain != NULL) { + ossl_cmp_debug(ctx, "success building chain for own CMP signer cert"); + } else { + /* dump errors to avoid confusion when printing further ones */ + OSSL_CMP_CTX_print_errors(ctx); + ossl_cmp_warn(ctx, "could not build chain for own CMP signer cert"); + } + } +} + /* ctx is not const just because ctx->chain may get adapted */ int ossl_cmp_msg_add_extraCerts(OSSL_CMP_CTX *ctx, OSSL_CMP_MSG *msg) { @@ -142,22 +161,7 @@ int ossl_cmp_msg_add_extraCerts(OSSL_CMP_CTX *ctx, OSSL_CMP_MSG *msg) int prepend = X509_ADD_FLAG_UP_REF | X509_ADD_FLAG_NO_DUP | X509_ADD_FLAG_PREPEND | X509_ADD_FLAG_NO_SS; - /* if not yet done try to build chain using available untrusted certs */ - if (ctx->chain == NULL) { - ossl_cmp_debug(ctx, - "trying to build chain for own CMP signer cert"); - ctx->chain = X509_build_chain(ctx->cert, ctx->untrusted, NULL, 0, - ctx->libctx, ctx->propq); - if (ctx->chain != NULL) { - ossl_cmp_debug(ctx, - "success building chain for own CMP signer cert"); - } else { - /* dump errors to avoid confusion when printing further ones */ - OSSL_CMP_CTX_print_errors(ctx); - ossl_cmp_warn(ctx, - "could not build chain for own CMP signer cert"); - } - } + ossl_cmp_set_own_chain(ctx); if (ctx->chain != NULL) { if (!ossl_x509_add_certs_new(&msg->extraCerts, ctx->chain, prepend)) return 0; diff --git a/crypto/cmp/cmp_server.c b/crypto/cmp/cmp_server.c index 79195369557..d0b35d6f564 100644 --- a/crypto/cmp/cmp_server.c +++ b/crypto/cmp/cmp_server.c @@ -216,11 +216,12 @@ static OSSL_CMP_MSG *process_cert_request(OSSL_CMP_SRV_CTX *srv_ctx, OSSL_CMP_MSG *msg = NULL; OSSL_CMP_PKISI *si = NULL; X509 *certOut = NULL; + EVP_PKEY *keyOut = NULL; STACK_OF(X509) *chainOut = NULL, *caPubs = NULL; const OSSL_CRMF_MSG *crm = NULL; const X509_REQ *p10cr = NULL; int bodytype; - int certReqId; + int certReqId, central_keygen; if (!ossl_assert(srv_ctx != NULL && srv_ctx->ctx != NULL && req != NULL)) return NULL; @@ -263,7 +264,11 @@ static OSSL_CMP_MSG *process_cert_request(OSSL_CMP_SRV_CTX *srv_ctx, } srv_ctx->certReqId = certReqId; - if (!ossl_cmp_verify_popo(srv_ctx->ctx, req, srv_ctx->acceptRAVerified)) { + central_keygen = OSSL_CRMF_MSG_centralkeygen_requested(crm, p10cr); + if (central_keygen < 0) + return NULL; + if (central_keygen == 0 + && !ossl_cmp_verify_popo(srv_ctx->ctx, req, srv_ctx->acceptRAVerified)) { /* Proof of possession could not be verified */ si = OSSL_CMP_STATUSINFO_new(OSSL_CMP_PKISTATUS_rejection, 1 << OSSL_CMP_PKIFAILUREINFO_badPOP, @@ -287,10 +292,13 @@ static OSSL_CMP_MSG *process_cert_request(OSSL_CMP_SRV_CTX *srv_ctx, /* do not set if polling starts: */ && certOut != NULL)) goto err; + if (central_keygen == 1 + && srv_ctx->ctx->newPkey_priv && srv_ctx->ctx->newPkey != NULL) + keyOut = srv_ctx->ctx->newPkey; } msg = ossl_cmp_certrep_new(srv_ctx->ctx, bodytype, certReqId, si, - certOut, NULL /* enc */, chainOut, caPubs, + certOut, keyOut, NULL /* enc */, chainOut, caPubs, srv_ctx->sendUnprotectedErrors); /* When supporting OSSL_CRMF_POPO_KEYENC, "enc" will need to be set */ if (msg == NULL) @@ -299,6 +307,7 @@ static OSSL_CMP_MSG *process_cert_request(OSSL_CMP_SRV_CTX *srv_ctx, err: OSSL_CMP_PKISI_free(si); X509_free(certOut); + OSSL_CMP_CTX_set0_newPkey(srv_ctx->ctx, 0, NULL); OSSL_STACK_OF_X509_free(chainOut); OSSL_STACK_OF_X509_free(caPubs); return msg; diff --git a/crypto/cms/cms_asn1.c b/crypto/cms/cms_asn1.c index ecf5a44793b..8b5e018bf5c 100644 --- a/crypto/cms/cms_asn1.c +++ b/crypto/cms/cms_asn1.c @@ -243,6 +243,7 @@ ASN1_NDEF_SEQUENCE(CMS_EnvelopedData) = { ASN1_SIMPLE(CMS_EnvelopedData, encryptedContentInfo, CMS_EncryptedContentInfo), ASN1_IMP_SET_OF_OPT(CMS_EnvelopedData, unprotectedAttrs, X509_ATTRIBUTE, 1) } ASN1_NDEF_SEQUENCE_END(CMS_EnvelopedData) +IMPLEMENT_ASN1_DUP_FUNCTION(CMS_EnvelopedData) ASN1_NDEF_SEQUENCE(CMS_DigestedData) = { ASN1_EMBED(CMS_DigestedData, version, INT32), diff --git a/crypto/cms/cms_lib.c b/crypto/cms/cms_lib.c index a115a3b9008..94289804fc8 100644 --- a/crypto/cms/cms_lib.c +++ b/crypto/cms/cms_lib.c @@ -18,6 +18,7 @@ #include "internal/cryptlib.h" #include "crypto/x509.h" #include "cms_local.h" +#include "internal/cms.h" static STACK_OF(CMS_CertificateChoices) **cms_get0_certificate_choices(CMS_ContentInfo *cms); @@ -769,3 +770,40 @@ int ossl_cms_set1_keyid(ASN1_OCTET_STRING **pkeyid, X509 *cert) *pkeyid = keyid; return 1; } + +CMS_EnvelopedData *ossl_cms_sign_encrypt(BIO *data, X509 *sign_cert, STACK_OF(X509) *certs, + EVP_PKEY *sign_key, unsigned int sign_flags, + STACK_OF(X509) *enc_recip, const EVP_CIPHER *cipher, + unsigned int enc_flags, OSSL_LIB_CTX *libctx, + const char *propq) +{ + CMS_EnvelopedData *evd = NULL; + BIO *privbio = NULL, *signbio = NULL; + CMS_ContentInfo *signcms = NULL, *evpcms = NULL; + + if (data == NULL || sign_key == NULL || sign_cert == NULL || enc_recip == NULL) { + ERR_raise(ERR_LIB_CMS, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + signcms = CMS_sign_ex(sign_cert, sign_key, certs, data, sign_flags, libctx, propq); + if (signcms == NULL) + goto err; + + signbio = BIO_new(BIO_s_mem()); + if (signbio == NULL + || ASN1_item_i2d_bio(ASN1_ITEM_rptr(CMS_SignedData), signbio, signcms->d.signedData) <= 0) + goto err; + + evpcms = CMS_encrypt_ex(enc_recip, signbio, cipher, enc_flags, libctx, propq); + if (evpcms == NULL) + goto err; + evd = CMS_EnvelopedData_dup(evpcms->d.envelopedData); + + err: + BIO_free(privbio); + BIO_free(signbio); + CMS_ContentInfo_free(signcms); + CMS_ContentInfo_free(evpcms); + + return evd; +} diff --git a/crypto/crmf/crmf_asn.c b/crypto/crmf/crmf_asn.c index c3dbc84e78a..6989484783b 100644 --- a/crypto/crmf/crmf_asn.c +++ b/crypto/crmf/crmf_asn.c @@ -58,6 +58,21 @@ ASN1_SEQUENCE(OSSL_CRMF_ENCRYPTEDVALUE) = { } ASN1_SEQUENCE_END(OSSL_CRMF_ENCRYPTEDVALUE) IMPLEMENT_ASN1_FUNCTIONS(OSSL_CRMF_ENCRYPTEDVALUE) +/* + * Note from CMP Updates defining CMPv3: + * The EncryptedKey structure defined in CRMF [RFC4211] is reused + * here, which makes the update backward compatible. Using the new + * syntax with the untagged default choice EncryptedValue is bits-on- + * the-wire compatible with the old syntax. + */ +ASN1_CHOICE(OSSL_CRMF_ENCRYPTEDKEY) = { + ASN1_SIMPLE(OSSL_CRMF_ENCRYPTEDKEY, value.encryptedValue, OSSL_CRMF_ENCRYPTEDVALUE), +#ifndef OPENSSL_NO_CMS + ASN1_IMP(OSSL_CRMF_ENCRYPTEDKEY, value.envelopedData, CMS_EnvelopedData, 0), +#endif +} ASN1_CHOICE_END(OSSL_CRMF_ENCRYPTEDKEY) +IMPLEMENT_ASN1_FUNCTIONS(OSSL_CRMF_ENCRYPTEDKEY) + ASN1_SEQUENCE(OSSL_CRMF_SINGLEPUBINFO) = { ASN1_SIMPLE(OSSL_CRMF_SINGLEPUBINFO, pubMethod, ASN1_INTEGER), ASN1_SIMPLE(OSSL_CRMF_SINGLEPUBINFO, pubLocation, GENERAL_NAME) diff --git a/crypto/crmf/crmf_err.c b/crypto/crmf/crmf_err.c index 44b2f2757d2..223ea0f9bf3 100644 --- a/crypto/crmf/crmf_err.c +++ b/crypto/crmf/crmf_err.c @@ -1,6 +1,6 @@ /* * Generated by util/mkerr.pl DO NOT EDIT - * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-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 @@ -18,44 +18,57 @@ static const ERR_STRING_DATA CRMF_str_reasons[] = { {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_BAD_PBM_ITERATIONCOUNT), - "bad pbm iterationcount"}, + "bad pbm iterationcount"}, + {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_CMS_NOT_SUPPORTED), "cms not supported"}, {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_CRMFERROR), "crmferror"}, {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_ERROR), "error"}, {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_ERROR_DECODING_CERTIFICATE), - "error decoding certificate"}, + "error decoding certificate"}, + {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_ERROR_DECODING_ENCRYPTEDKEY), + "error decoding encryptedkey"}, {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_ERROR_DECRYPTING_CERTIFICATE), - "error decrypting certificate"}, + "error decrypting certificate"}, + {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_ERROR_DECRYPTING_ENCRYPTEDKEY), + "error decrypting encryptedkey"}, + {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_ERROR_DECRYPTING_ENCRYPTEDVALUE), + "error decrypting encryptedvalue"}, {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_ERROR_DECRYPTING_SYMMETRIC_KEY), - "error decrypting symmetric key"}, + "error decrypting symmetric key"}, + {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_ERROR_SETTING_PURPOSE), + "error setting purpose"}, + {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_ERROR_VERIFYING_ENCRYPTEDKEY), + "error verifying encryptedkey"}, {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_FAILURE_OBTAINING_RANDOM), - "failure obtaining random"}, + "failure obtaining random"}, {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_ITERATIONCOUNT_BELOW_100), - "iterationcount below 100"}, + "iterationcount below 100"}, {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_MALFORMED_IV), "malformed iv"}, {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_NULL_ARGUMENT), "null argument"}, {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_POPOSKINPUT_NOT_SUPPORTED), - "poposkinput not supported"}, + "poposkinput not supported"}, + {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_POPO_INCONSISTENT_CENTRAL_KEYGEN), + "popo inconsistent central keygen"}, {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_POPO_INCONSISTENT_PUBLIC_KEY), - "popo inconsistent public key"}, + "popo inconsistent public key"}, {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_POPO_MISSING), "popo missing"}, {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_POPO_MISSING_PUBLIC_KEY), - "popo missing public key"}, + "popo missing public key"}, {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_POPO_MISSING_SUBJECT), - "popo missing subject"}, + "popo missing subject"}, {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_POPO_RAVERIFIED_NOT_ACCEPTED), - "popo raverified not accepted"}, + "popo raverified not accepted"}, {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_SETTING_MAC_ALGOR_FAILURE), - "setting mac algor failure"}, + "setting mac algor failure"}, {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_SETTING_OWF_ALGOR_FAILURE), - "setting owf algor failure"}, + "setting owf algor failure"}, {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_UNSUPPORTED_ALGORITHM), - "unsupported algorithm"}, + "unsupported algorithm"}, {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_UNSUPPORTED_CIPHER), - "unsupported cipher"}, + "unsupported cipher"}, {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_UNSUPPORTED_METHOD_FOR_CREATING_POPO), - "unsupported method for creating popo"}, + "unsupported method for creating popo"}, {ERR_PACK(ERR_LIB_CRMF, 0, CRMF_R_UNSUPPORTED_POPO_METHOD), - "unsupported popo method"}, + "unsupported popo method"}, {0, NULL} }; diff --git a/crypto/crmf/crmf_lib.c b/crypto/crmf/crmf_lib.c index cb077e41d2c..d66929696d8 100644 --- a/crypto/crmf/crmf_lib.c +++ b/crypto/crmf/crmf_lib.c @@ -29,6 +29,7 @@ #include #include "crmf_local.h" +#include "internal/constant_time.h" #include "internal/sizes.h" #include "crypto/evp.h" #include "crypto/x509.h" @@ -37,6 +38,7 @@ #include #include #include +#include /*- * atyp = Attribute Type @@ -542,6 +544,38 @@ int OSSL_CRMF_MSGS_verify_popo(const OSSL_CRMF_MSGS *reqs, return 1; } +int OSSL_CRMF_MSG_centralkeygen_requested(const OSSL_CRMF_MSG *crm, const X509_REQ *p10cr) +{ + X509_PUBKEY *pubkey = NULL; + const unsigned char *pk = NULL; + int pklen, ret = 0; + + if (crm == NULL && p10cr == NULL) { + ERR_raise(ERR_LIB_CRMF, CRMF_R_NULL_ARGUMENT); + return -1; + } + + if (crm != NULL) + pubkey = OSSL_CRMF_CERTTEMPLATE_get0_publicKey(OSSL_CRMF_MSG_get0_tmpl(crm)); + else + pubkey = p10cr->req_info.pubkey; + + if (pubkey == NULL + || (X509_PUBKEY_get0_param(NULL, &pk, &pklen, NULL, pubkey) + && pklen == 0)) + ret = 1; + + /* + * In case of CRMF, POPO MUST be absent if central key generation + * is requested, otherwise MUST be present + */ + if (crm != NULL && ret != (crm->popo == NULL)) { + ERR_raise(ERR_LIB_CRMF, CRMF_R_POPO_INCONSISTENT_CENTRAL_KEYGEN); + return -2; + } + return ret; +} + X509_PUBKEY *OSSL_CRMF_CERTTEMPLATE_get0_publicKey(const OSSL_CRMF_CERTTEMPLATE *tmpl) { @@ -612,46 +646,155 @@ int OSSL_CRMF_CERTTEMPLATE_fill(OSSL_CRMF_CERTTEMPLATE *tmpl, return 1; } -/*- - * Decrypts the certificate in the given encryptedValue using private key pkey. - * This is needed for the indirect PoP method as in RFC 4210 section 5.2.8.2. - * - * returns a pointer to the decrypted certificate - * returns NULL on error or if no certificate available - */ -X509 -*OSSL_CRMF_ENCRYPTEDVALUE_get1_encCert(const OSSL_CRMF_ENCRYPTEDVALUE *ecert, - OSSL_LIB_CTX *libctx, const char *propq, - EVP_PKEY *pkey) +#ifndef OPENSSL_NO_CMS +DECLARE_ASN1_ITEM(CMS_SignedData) /* copied from cms_local.h */ + +/* check for KGA authorization implied by CA flag or by explicit EKU cmKGA */ +static int check_cmKGA(ossl_unused const X509_PURPOSE *purpose, const X509 *x, int ca) +{ + STACK_OF(ASN1_OBJECT) *ekus; + int i, ret = 1; + + if (ca) + return ret; + ekus = X509_get_ext_d2i(x, NID_ext_key_usage, NULL, NULL); + for (i = 0; i < sk_ASN1_OBJECT_num(ekus); i++) { + if (OBJ_obj2nid(sk_ASN1_OBJECT_value(ekus, i)) == NID_cmKGA) + goto end; + } + ret = 0; + + end: + sk_ASN1_OBJECT_pop_free(ekus, ASN1_OBJECT_free); + return ret; +} +#endif /* OPENSSL_NO_CMS */ + +EVP_PKEY *OSSL_CRMF_ENCRYPTEDKEY_get1_pkey(const OSSL_CRMF_ENCRYPTEDKEY *encryptedKey, + X509_STORE *ts, STACK_OF(X509) *extra, EVP_PKEY *pkey, + X509 *cert, ASN1_OCTET_STRING *secret, + OSSL_LIB_CTX *libctx, const char *propq) +{ +#ifndef OPENSSL_NO_CMS + BIO *bio = NULL; + CMS_SignedData *sd = NULL; + BIO *pkey_bio = NULL; + int purpose_id, bak_purpose_id; + X509_VERIFY_PARAM *vpm; +#endif + EVP_PKEY *ret = NULL; + + if (encryptedKey == NULL) { + ERR_raise(ERR_LIB_CRMF, CRMF_R_NULL_ARGUMENT); + return NULL; + } + if (encryptedKey->type != OSSL_CRMF_ENCRYPTEDKEY_ENVELOPEDDATA) { + unsigned char *p; + const unsigned char *p_copy; + int len; + + p = OSSL_CRMF_ENCRYPTEDVALUE_decrypt(encryptedKey->value.encryptedValue, + libctx, propq, pkey, &len); + if ((p_copy = p) != NULL) + ret = d2i_AutoPrivateKey_ex(NULL, &p_copy, len, libctx, propq); + OPENSSL_free(p); + return ret; + } + +#ifndef OPENSSL_NO_CMS + if (ts == NULL) { + ERR_raise(ERR_LIB_CRMF, CRMF_R_NULL_ARGUMENT); + return NULL; + } + if ((bio = CMS_EnvelopedData_decrypt(encryptedKey->value.envelopedData, + NULL, pkey, cert, secret, 0, + libctx, propq)) == NULL) { + ERR_raise(ERR_LIB_CRMF, CRMF_R_ERROR_DECRYPTING_ENCRYPTEDKEY); + goto end; + } + sd = ASN1_item_d2i_bio(ASN1_ITEM_rptr(CMS_SignedData), bio, NULL); + if (sd == NULL) + goto end; + + if ((purpose_id = X509_PURPOSE_get_by_sname(SN_cmKGA)) < 0) { + purpose_id = X509_PURPOSE_get_unused_id(libctx); + if (!X509_PURPOSE_add(purpose_id, X509_TRUST_COMPAT, 0, check_cmKGA, + LN_cmKGA, SN_cmKGA, NULL)) + goto end; + } + if ((vpm = X509_STORE_get0_param(ts)) == NULL) + goto end; + + /* temporarily override X509_PURPOSE_SMIME_SIGN: */ + bak_purpose_id = X509_VERIFY_PARAM_get_purpose(vpm); + if (!X509_STORE_set_purpose(ts, purpose_id)) { + ERR_raise(ERR_LIB_CRMF, CRMF_R_ERROR_SETTING_PURPOSE); + goto end; + } + + pkey_bio = CMS_SignedData_verify(sd, NULL, NULL /* scerts */, ts, + extra, NULL, 0, libctx, propq); + + if (!X509_STORE_set_purpose(ts, bak_purpose_id)) { + ERR_raise(ERR_LIB_CRMF, CRMF_R_ERROR_SETTING_PURPOSE); + goto end; + } + + if (pkey_bio == NULL) { + ERR_raise(ERR_LIB_CRMF, CRMF_R_ERROR_VERIFYING_ENCRYPTEDKEY); + goto end; + } + + /* unpack AsymmetricKeyPackage */ + if ((ret = d2i_PrivateKey_ex_bio(pkey_bio, NULL, libctx, propq)) == NULL) + ERR_raise(ERR_LIB_CRMF, CRMF_R_ERROR_DECODING_ENCRYPTEDKEY); + + end: + CMS_SignedData_free(sd); + BIO_free(bio); + BIO_free(pkey_bio); + return ret; +#else + /* prevent warning on unused parameters: */ + ((void)ts, (void)extra, (void)cert, (void)secret); + ERR_raise(ERR_LIB_CRMF, CRMF_R_CMS_NOT_SUPPORTED); + return NULL; +#endif /* OPENSSL_NO_CMS */ +} + +unsigned char +*OSSL_CRMF_ENCRYPTEDVALUE_decrypt(const OSSL_CRMF_ENCRYPTEDVALUE *enc, + OSSL_LIB_CTX *libctx, const char *propq, + EVP_PKEY *pkey, int *outlen) { - X509 *cert = NULL; /* decrypted certificate */ EVP_CIPHER_CTX *evp_ctx = NULL; /* context for symmetric encryption */ unsigned char *ek = NULL; /* decrypted symmetric encryption key */ size_t eksize = 0; /* size of decrypted symmetric encryption key */ EVP_CIPHER *cipher = NULL; /* used cipher */ int cikeysize = 0; /* key size from cipher */ unsigned char *iv = NULL; /* initial vector for symmetric encryption */ - unsigned char *outbuf = NULL; /* decryption output buffer */ - const unsigned char *p = NULL; /* needed for decoding ASN1 */ - int n, outlen = 0; + unsigned char *out = NULL; /* decryption output buffer */ + int n, ret = 0; EVP_PKEY_CTX *pkctx = NULL; /* private key context */ char name[OSSL_MAX_NAME_SIZE]; - if (ecert == NULL || ecert->symmAlg == NULL || ecert->encSymmKey == NULL - || ecert->encValue == NULL || pkey == NULL) { + if (outlen == NULL) { + ERR_raise(ERR_LIB_CRMF, CRMF_R_NULL_ARGUMENT); + return NULL; + } + *outlen = 0; + if (enc == NULL || enc->symmAlg == NULL || enc->encSymmKey == NULL + || enc->encValue == NULL || pkey == NULL) { ERR_raise(ERR_LIB_CRMF, CRMF_R_NULL_ARGUMENT); return NULL; } /* select symmetric cipher based on algorithm given in message */ - OBJ_obj2txt(name, sizeof(name), ecert->symmAlg->algorithm, 0); - + OBJ_obj2txt(name, sizeof(name), enc->symmAlg->algorithm, 0); (void)ERR_set_mark(); - cipher = EVP_CIPHER_fetch(NULL, name, NULL); - + cipher = EVP_CIPHER_fetch(libctx, name, propq); if (cipher == NULL) - cipher = (EVP_CIPHER *)EVP_get_cipherbyname(name); - + cipher = (EVP_CIPHER *)EVP_get_cipherbyobj(enc->symmAlg->algorithm); if (cipher == NULL) { (void)ERR_clear_last_mark(); ERR_raise(ERR_LIB_CRMF, CRMF_R_UNSUPPORTED_CIPHER); @@ -662,52 +805,141 @@ X509 cikeysize = EVP_CIPHER_get_key_length(cipher); /* first the symmetric key needs to be decrypted */ pkctx = EVP_PKEY_CTX_new_from_pkey(libctx, pkey, propq); - if (pkctx == NULL || EVP_PKEY_decrypt_init(pkctx) <= 0 - || evp_pkey_decrypt_alloc(pkctx, &ek, &eksize, (size_t)cikeysize, - ecert->encSymmKey->data, - ecert->encSymmKey->length) <= 0) + if (pkctx != NULL && EVP_PKEY_decrypt_init(pkctx) > 0) { + ASN1_BIT_STRING *encKey = enc->encSymmKey; + size_t failure; + int retval; + + if (EVP_PKEY_decrypt(pkctx, NULL, &eksize, + encKey->data, encKey->length) <= 0 + || (ek = OPENSSL_malloc(eksize)) == NULL) + goto end; + retval = EVP_PKEY_decrypt(pkctx, ek, &eksize, encKey->data, encKey->length); + failure = ~constant_time_is_zero_s(constant_time_msb(retval) + | constant_time_is_zero(retval)); + failure |= ~constant_time_eq_s(eksize, (size_t)cikeysize); + if (failure) { + ERR_clear_error(); /* error state may have sensitive information */ + ERR_raise(ERR_LIB_CRMF, CRMF_R_ERROR_DECRYPTING_SYMMETRIC_KEY); + goto end; + } + } else { goto end; - + } if ((iv = OPENSSL_malloc(EVP_CIPHER_get_iv_length(cipher))) == NULL) goto end; - if (ASN1_TYPE_get_octetstring(ecert->symmAlg->parameter, iv, + if (ASN1_TYPE_get_octetstring(enc->symmAlg->parameter, iv, EVP_CIPHER_get_iv_length(cipher)) != EVP_CIPHER_get_iv_length(cipher)) { ERR_raise(ERR_LIB_CRMF, CRMF_R_MALFORMED_IV); goto end; } - /* - * d2i_X509 changes the given pointer, so use p for decoding the message and - * keep the original pointer in outbuf so the memory can be freed later - */ - if ((p = outbuf = OPENSSL_malloc(ecert->encValue->length + - EVP_CIPHER_get_block_size(cipher))) == NULL + if ((out = OPENSSL_malloc(enc->encValue->length + + EVP_CIPHER_get_block_size(cipher))) == NULL || (evp_ctx = EVP_CIPHER_CTX_new()) == NULL) goto end; EVP_CIPHER_CTX_set_padding(evp_ctx, 0); if (!EVP_DecryptInit(evp_ctx, cipher, ek, iv) - || !EVP_DecryptUpdate(evp_ctx, outbuf, &outlen, - ecert->encValue->data, - ecert->encValue->length) - || !EVP_DecryptFinal(evp_ctx, outbuf + outlen, &n)) { - ERR_raise(ERR_LIB_CRMF, CRMF_R_ERROR_DECRYPTING_CERTIFICATE); + || !EVP_DecryptUpdate(evp_ctx, out, outlen, + enc->encValue->data, + enc->encValue->length) + || !EVP_DecryptFinal(evp_ctx, out + *outlen, &n)) { + ERR_raise(ERR_LIB_CRMF, CRMF_R_ERROR_DECRYPTING_ENCRYPTEDVALUE); goto end; } - outlen += n; + *outlen += n; + ret = 1; - /* convert decrypted certificate from DER to internal ASN.1 structure */ - if ((cert = X509_new_ex(libctx, propq)) == NULL) - goto end; - if (d2i_X509(&cert, &p, outlen) == NULL) - ERR_raise(ERR_LIB_CRMF, CRMF_R_ERROR_DECODING_CERTIFICATE); end: EVP_PKEY_CTX_free(pkctx); - OPENSSL_free(outbuf); EVP_CIPHER_CTX_free(evp_ctx); EVP_CIPHER_free(cipher); OPENSSL_clear_free(ek, eksize); OPENSSL_free(iv); + if (ret) + return out; + OPENSSL_free(out); + return NULL; +} + +/* + * Decrypts the certificate in the given encryptedValue using private key pkey. + * This is needed for the indirect PoP method as in RFC 4210 section 5.2.8.2. + * + * returns a pointer to the decrypted certificate + * returns NULL on error or if no certificate available + */ +X509 *OSSL_CRMF_ENCRYPTEDVALUE_get1_encCert(const OSSL_CRMF_ENCRYPTEDVALUE *ecert, + OSSL_LIB_CTX *libctx, const char *propq, + EVP_PKEY *pkey) +{ + unsigned char *buf = NULL; + const unsigned char *p; + int len; + X509 *cert = NULL; + + buf = OSSL_CRMF_ENCRYPTEDVALUE_decrypt(ecert, libctx, propq, pkey, &len); + if ((p = buf) == NULL || (cert = X509_new_ex(libctx, propq)) == NULL) + goto end; + + if (d2i_X509(&cert, &p, len) == NULL) { + ERR_raise(ERR_LIB_CRMF, CRMF_R_ERROR_DECODING_CERTIFICATE); + X509_free(cert); + cert = NULL; + } + + end: + OPENSSL_free(buf); return cert; } +/*- + * Decrypts the certificate in the given encryptedKey using private key pkey. + * This is needed for the indirect PoP method as in RFC 4210 section 5.2.8.2. + * + * returns a pointer to the decrypted certificate + * returns NULL on error or if no certificate available + */ +X509 *OSSL_CRMF_ENCRYPTEDKEY_get1_encCert(const OSSL_CRMF_ENCRYPTEDKEY *ecert, + OSSL_LIB_CTX *libctx, const char *propq, + EVP_PKEY *pkey, unsigned int flags) +{ +#ifndef OPENSSL_NO_CMS + BIO *bio; + X509 *cert = NULL; +#endif + + if (ecert->type != OSSL_CRMF_ENCRYPTEDKEY_ENVELOPEDDATA) + return OSSL_CRMF_ENCRYPTEDVALUE_get1_encCert(ecert->value.encryptedValue, + libctx, propq, pkey); +#ifndef OPENSSL_NO_CMS + bio = CMS_EnvelopedData_decrypt(ecert->value.envelopedData, NULL, + pkey, NULL /* cert */, NULL, flags, + libctx, propq); + if (bio == NULL) + return NULL; + cert = d2i_X509_bio(bio, NULL); + if (cert == NULL) + ERR_raise(ERR_LIB_CRMF, CRMF_R_ERROR_DECODING_CERTIFICATE); + BIO_free(bio); + return cert; +#else + (void)flags; /* prevent warning on unused parameter */ + ERR_raise(ERR_LIB_CRMF, CRMF_R_CMS_NOT_SUPPORTED); + return NULL; +#endif /* OPENSSL_NO_CMS */ +} + +#ifndef OPENSSL_NO_CMS +OSSL_CRMF_ENCRYPTEDKEY *OSSL_CRMF_ENCRYPTEDKEY_init_envdata(CMS_EnvelopedData *envdata) +{ + OSSL_CRMF_ENCRYPTEDKEY *ek = OSSL_CRMF_ENCRYPTEDKEY_new(); + + if (ek == NULL) + return NULL; + ek->type = OSSL_CRMF_ENCRYPTEDKEY_ENVELOPEDDATA; + ek->value.envelopedData = envdata; + return ek; +} +#endif diff --git a/crypto/crmf/crmf_local.h b/crypto/crmf/crmf_local.h index 0b1b7964aa2..b2fa11d5289 100644 --- a/crypto/crmf/crmf_local.h +++ b/crypto/crmf/crmf_local.h @@ -15,6 +15,7 @@ # define OSSL_CRYPTO_CRMF_LOCAL_H # include +# include /* for CMS_EnvelopedData and CMS_SignedData */ # include # include "internal/crmf.h" /* for ossl_crmf_attributetypeandvalue_st */ @@ -52,6 +53,25 @@ struct ossl_crmf_encryptedvalue_st { ASN1_BIT_STRING *encValue; } /* OSSL_CRMF_ENCRYPTEDVALUE */; +/* + * EncryptedKey ::= CHOICE { + * encryptedValue EncryptedValue, -- Deprecated + * envelopedData [0] EnvelopedData } + * -- The encrypted private key MUST be placed in the envelopedData + * -- encryptedContentInfo encryptedContent OCTET STRING. + */ +# define OSSL_CRMF_ENCRYPTEDKEY_ENVELOPEDDATA 1 + +struct ossl_crmf_encryptedkey_st { + int type; + union { + OSSL_CRMF_ENCRYPTEDVALUE *encryptedValue; /* 0 */ /* Deprecated */ +# ifndef OPENSSL_NO_CMS + CMS_EnvelopedData *envelopedData; /* 1 */ +# endif + } value; +} /* OSSL_CRMF_ENCRYPTEDKEY */; + /*- * Attributes ::= SET OF Attribute * => X509_ATTRIBUTE diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index e2ce4faede2..56554f52337 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -231,6 +231,7 @@ CMP_R_ERROR_VALIDATING_PROTECTION:140:error validating protection CMP_R_ERROR_VALIDATING_SIGNATURE:171:error validating signature CMP_R_EXPECTED_POLLREQ:104:expected pollreq CMP_R_FAILED_BUILDING_OWN_CHAIN:164:failed building own chain +CMP_R_FAILED_EXTRACTING_CENTRAL_GEN_KEY:203:failed extracting central gen key CMP_R_FAILED_EXTRACTING_PUBKEY:141:failed extracting pubkey CMP_R_FAILURE_OBTAINING_RANDOM:110:failure obtaining random CMP_R_FAIL_INFO_OUT_OF_RANGE:129:fail info out of range @@ -243,6 +244,7 @@ CMP_R_INVALID_GENP:193:invalid genp CMP_R_INVALID_KEYSPEC:202:invalid keyspec CMP_R_INVALID_OPTION:174:invalid option CMP_R_INVALID_ROOTCAKEYUPDATE:195:invalid rootcakeyupdate +CMP_R_MISSING_CENTRAL_GEN_KEY:204:missing central gen key CMP_R_MISSING_CERTID:165:missing certid CMP_R_MISSING_KEY_INPUT_FOR_CREATING_PROTECTION:130:\ missing key input for creating protection @@ -279,6 +281,7 @@ CMP_R_TOTAL_TIMEOUT:184:total timeout CMP_R_TRANSACTIONID_UNMATCHED:152:transactionid unmatched CMP_R_TRANSFER_ERROR:159:transfer error CMP_R_UNCLEAN_CTX:191:unclean ctx +CMP_R_UNEXPECTED_CENTRAL_GEN_KEY:205:unexpected central gen key CMP_R_UNEXPECTED_CERTPROFILE:196:unexpected certprofile CMP_R_UNEXPECTED_CRLSTATUSLIST:201:unexpected crlstatuslist CMP_R_UNEXPECTED_PKIBODY:133:unexpected pkibody @@ -448,16 +451,23 @@ CONF_R_UNKNOWN_MODULE_NAME:113:unknown module name CONF_R_VARIABLE_EXPANSION_TOO_LONG:116:variable expansion too long CONF_R_VARIABLE_HAS_NO_VALUE:104:variable has no value CRMF_R_BAD_PBM_ITERATIONCOUNT:100:bad pbm iterationcount +CRMF_R_CMS_NOT_SUPPORTED:122:cms not supported CRMF_R_CRMFERROR:102:crmferror CRMF_R_ERROR:103:error CRMF_R_ERROR_DECODING_CERTIFICATE:104:error decoding certificate +CRMF_R_ERROR_DECODING_ENCRYPTEDKEY:123:error decoding encryptedkey CRMF_R_ERROR_DECRYPTING_CERTIFICATE:105:error decrypting certificate +CRMF_R_ERROR_DECRYPTING_ENCRYPTEDKEY:124:error decrypting encryptedkey +CRMF_R_ERROR_DECRYPTING_ENCRYPTEDVALUE:125:error decrypting encryptedvalue CRMF_R_ERROR_DECRYPTING_SYMMETRIC_KEY:106:error decrypting symmetric key +CRMF_R_ERROR_SETTING_PURPOSE:126:error setting purpose +CRMF_R_ERROR_VERIFYING_ENCRYPTEDKEY:127:error verifying encryptedkey CRMF_R_FAILURE_OBTAINING_RANDOM:107:failure obtaining random CRMF_R_ITERATIONCOUNT_BELOW_100:108:iterationcount below 100 CRMF_R_MALFORMED_IV:101:malformed iv CRMF_R_NULL_ARGUMENT:109:null argument CRMF_R_POPOSKINPUT_NOT_SUPPORTED:113:poposkinput not supported +CRMF_R_POPO_INCONSISTENT_CENTRAL_KEYGEN:128:popo inconsistent central keygen CRMF_R_POPO_INCONSISTENT_PUBLIC_KEY:117:popo inconsistent public key CRMF_R_POPO_MISSING:121:popo missing CRMF_R_POPO_MISSING_PUBLIC_KEY:118:popo missing public key diff --git a/doc/internal/man3/ossl_cms_sign_encrypt.pod b/doc/internal/man3/ossl_cms_sign_encrypt.pod new file mode 100644 index 00000000000..ee77e76cbe3 --- /dev/null +++ b/doc/internal/man3/ossl_cms_sign_encrypt.pod @@ -0,0 +1,56 @@ +=pod + +=head1 NAME + +ossl_cms_sign_encrypt +- Create CMS envelope + +=head1 SYNOPSIS + + #include + + CMS_EnvelopedData *ossl_cms_sign_encrypt(BIO *data, X509 *sign_cert, STACK_OF(X509) *certs, + EVP_PKEY *sign_key, unsigned int sign_flags, + STACK_OF(X509) *enc_recip, const EVP_CIPHER *cipher, + unsigned int enc_flags, OSSL_LIB_CTX *libctx, + const char *propq); + +=head1 DESCRIPTION + +ossl_cms_sign_encrypt() creates a B structure for recipients in +I. + +I is signed using I and I to create B +and then encrypted using I to create B. +The library context I and the property query I are used +when retrieving algorithms from providers. + +I is an optional additional set of certificates to include in the +B structure (e.g., any intermediate CAs in the chain of the signer certificate). + +I is an optional set of flags for the signing operation. +See L for more information. + +I is an optional set of flags for the encryption operation. +See L for more information. + +=head1 RETURN VALUES + +If the allocation fails, ossl_cms_sign_encrypt() returns NULL and +sets an error code that can be obtained by L. +Otherwise, it returns a pointer to the newly allocated structure. + +=head1 HISTORY + +ossl_cms_sign_encrypt() was added in OpenSSL 3.5. + +=head1 COPYRIGHT + +Copyright 2023 - 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. + +=cut diff --git a/doc/man1/openssl-cmp.pod.in b/doc/man1/openssl-cmp.pod.in index a5a8167dd65..5fd0cb6915f 100644 --- a/doc/man1/openssl-cmp.pod.in +++ b/doc/man1/openssl-cmp.pod.in @@ -26,6 +26,8 @@ Certificate enrollment options: [B<-newkey> I|I] [B<-newkeypass> I] +[B<-centralkeygen> +[B<-newkeyout> I] [B<-subject> I] [B<-days> I] [B<-reqexts> I] @@ -140,6 +142,8 @@ Mock server options: [B<-srv_untrusted> I|I] [B<-ref_cert> I|I] [B<-rsp_cert> I|I] +[B<-rsp_key> I|I] +[B<-rsp_keypass> I|I] [B<-rsp_crl> I|I] [B<-rsp_extracerts> I|I] [B<-rsp_capubs> I|I] @@ -308,6 +312,15 @@ If not given here, the password will be prompted for if needed. For more information about the format of I see L. +=item B<-centralkeygen> + +Request central key generation for certificate enrollment. +This applies to B<-cmd> I. + +=item B<-newkeyout> I + +File to save centrally generated private key, in PEM format. + =item B<-subject> I X.509 Distinguished Name (DN) to use as subject field @@ -380,7 +393,8 @@ Flag the policies given with B<-policy_oids> as critical. =item B<-popo> I Proof-of-possession (POPO) method to use for IR/CR/KUR; values: C<-1>..<2> where -C<-1> = NONE, C<0> = RAVERIFIED, C<1> = SIGNATURE (default), C<2> = KEYENC. +C<-1> = NONE, which implies central key generation, +C<0> = RAVERIFIED, C<1> = SIGNATURE (default), C<2> = KEYENC. Note that a signature-based POPO can only be produced if a private key is provided via the B<-newkey> or B<-key> options. @@ -920,7 +934,7 @@ See L for details. Pass phrase source for certificate given with the B<-trusted>, B<-untrusted>, B<-own_trusted>, B<-srvcert>, B<-crlcert>, B<-out_trusted>, B<-extracerts>, -B<-srv_trusted>, B<-srv_untrusted>, B<-ref_cert>, B<-rsp_cert>, +B<-srv_trusted>, B<-srv_untrusted>, B<-ref_cert>, B<-rsp_extracerts>, B<-rsp_capubs>, B<-rsp_newwithnew>, B<-rsp_newwithold>, B<-rsp_oldwithnew>, B<-tls_extra>, and B<-tls_trusted> options. @@ -1194,6 +1208,14 @@ Certificate to be expected for RR messages and any oldCertID in KUR messages. Certificate to be returned as mock enrollment result. +=item B<-rsp_key> I|I + +Private key to be returned as central key generation result. + +=item B<-rsp_keypass> I + +Pass phrase source for B and B. + =item B<-rsp_crl> I|I CRL to be returned in genp of type C. @@ -1500,6 +1522,9 @@ The B<-profile> option was added in OpenSSL 3.3. B<-crlcert>, B<-oldcrl>, B<-crlout>, B<-crlform> and B<-rsp_crl> options were added in OpenSSL 3.4. +B<-centralkeygen>, b<-newkeyout>, B<-rsp_key> and +B<-rsp_keypass> were added in OpenSSL 3.5. + =head1 COPYRIGHT Copyright 2007-2024 The OpenSSL Project Authors. All Rights Reserved. diff --git a/doc/man3/CMS_EnvelopedData_create.pod b/doc/man3/CMS_EnvelopedData_create.pod index 9dfd0ad77d8..c02b7648429 100644 --- a/doc/man3/CMS_EnvelopedData_create.pod +++ b/doc/man3/CMS_EnvelopedData_create.pod @@ -57,19 +57,24 @@ The wrappers L and L are often used instead. =head1 RETURN VALUES If the allocation fails, CMS_EnvelopedData_create_ex(), -CMS_EnvelopedData_create(), CMS_AuthEnvelopedData_create_ex(), and -CMS_AuthEnvelopedData_create() return NULL and set an error code that can be -obtained by L. Otherwise they return a pointer to the newly -allocated structure. +CMS_EnvelopedData_create(), CMS_AuthEnvelopedData_create_ex(), +CMS_AuthEnvelopedData_create(), CMS_AuthEnvelopedData_create(), +and CMS_AuthEnvelopedData_create_ex() return NULL and set an +error code that can be obtained by L. +Otherwise, they return a pointer to the newly allocated structure. =head1 SEE ALSO -L, L, L, L +L, L, L, L, +L, L =head1 HISTORY The CMS_EnvelopedData_create_ex() method was added in OpenSSL 3.0. +CMS_AuthEnvelopedData_create() and CMS_AuthEnvelopedData_create_ex() +were added in OpenSSL 3.5. + =head1 COPYRIGHT Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved. diff --git a/doc/man3/OSSL_CMP_CTX_new.pod b/doc/man3/OSSL_CMP_CTX_new.pod index 01474ee0ba1..483524981a5 100644 --- a/doc/man3/OSSL_CMP_CTX_new.pod +++ b/doc/man3/OSSL_CMP_CTX_new.pod @@ -273,7 +273,8 @@ The following options can be set: Select the proof of possession method to use. Possible values are: - OSSL_CRMF_POPO_NONE - ProofOfPossession field omitted + OSSL_CRMF_POPO_NONE - ProofOfPossession field omitted, + which implies central key generation OSSL_CRMF_POPO_RAVERIFIED - assert that the RA has already verified the PoPo OSSL_CRMF_POPO_SIGNATURE - sign a value with private key, diff --git a/doc/man3/OSSL_CRMF_MSG_get0_tmpl.pod b/doc/man3/OSSL_CRMF_MSG_get0_tmpl.pod index 749a1719c91..cffd633616c 100644 --- a/doc/man3/OSSL_CRMF_MSG_get0_tmpl.pod +++ b/doc/man3/OSSL_CRMF_MSG_get0_tmpl.pod @@ -10,8 +10,13 @@ OSSL_CRMF_CERTTEMPLATE_get0_serialNumber, OSSL_CRMF_CERTTEMPLATE_get0_extensions, OSSL_CRMF_CERTID_get0_serialNumber, OSSL_CRMF_CERTID_get0_issuer, +OSSL_CRMF_ENCRYPTEDKEY_get1_encCert, +OSSL_CRMF_ENCRYPTEDKEY_get1_pkey, +OSSL_CRMF_ENCRYPTEDKEY_init_envdata, +OSSL_CRMF_ENCRYPTEDVALUE_decrypt, OSSL_CRMF_ENCRYPTEDVALUE_get1_encCert, -OSSL_CRMF_MSG_get_certReqId +OSSL_CRMF_MSG_get_certReqId, +OSSL_CRMF_MSG_centralkeygen_requested - functions reading from CRMF CertReqMsg structures =head1 SYNOPSIS @@ -34,12 +39,30 @@ OSSL_CRMF_MSG_get_certReqId *OSSL_CRMF_CERTID_get0_serialNumber(const OSSL_CRMF_CERTID *cid); const X509_NAME *OSSL_CRMF_CERTID_get0_issuer(const OSSL_CRMF_CERTID *cid); + X509 *OSSL_CRMF_ENCRYPTEDKEY_get1_encCert(const OSSL_CRMF_ENCRYPTEDKEY *ecert, + OSSL_LIB_CTX *libctx, const char *propq, + EVP_PKEY *pkey, unsigned int flags); + EVP_PKEY + *OSSL_CRMF_ENCRYPTEDKEY_get1_pkey(OSSL_CRMF_ENCRYPTEDKEY *encryptedKey, + X509_STORE *ts, STACK_OF(X509) *extra, + EVP_PKEY *pkey, X509 *cert, + ASN1_OCTET_STRING *secret, + OSSL_LIB_CTX *libctx, const char *propq); + OSSL_CRMF_ENCRYPTEDKEY + *OSSL_CRMF_ENCRYPTEDKEY_init_envdata(CMS_EnvelopedData *envdata); + + unsigned char + *OSSL_CRMF_ENCRYPTEDVALUE_decrypt(const OSSL_CRMF_ENCRYPTEDVALUE *enc, + OSSL_LIB_CTX *libctx, const char *propq, + EVP_PKEY *pkey, int *outlen); X509 *OSSL_CRMF_ENCRYPTEDVALUE_get1_encCert(const OSSL_CRMF_ENCRYPTEDVALUE *ecert, OSSL_LIB_CTX *libctx, const char *propq, EVP_PKEY *pkey); int OSSL_CRMF_MSG_get_certReqId(const OSSL_CRMF_MSG *crm); + int OSSL_CRMF_MSG_centralkeygen_requested(const OSSL_CRMF_MSG *crm, + const X509_REQ *p10cr); =head1 DESCRIPTION @@ -66,6 +89,33 @@ of the given CertId I. OSSL_CRMF_CERTID_get0_issuer retrieves the issuer name of the given CertId I, which must be of ASN.1 type GEN_DIRNAME. +OSSL_CRMF_ENCRYPTEDKEY_get1_encCert() decrypts the certificate in the given +encryptedKey I, using the private key I, library context +I and property query string I (see L). +This is needed for the indirect POPO method as in RFC 4210 section 5.2.8.2. +The function returns the decrypted certificate as a copy, leaving its ownership +with the caller, who is responsible for freeing it. + +OSSL_CRMF_ENCRYPTEDKEY_get1_pkey() decrypts the private key in I. +If I is not of type B, +decryption uses the private key I. +The library context I and property query I are taken into account as usual. +The rest of this paragraph is relevant only if CMS support not disabled for the OpenSSL build +and I is of type case B. +Decryption uses the I parameter if not NULL; +otherwise uses the private key and the certificate I +related to I, where I is recommended to be given if available. +On success, the function verifies the decrypted data as signed data, +using the trust store I and any untrusted certificates in I. +Doing so, it checks for the purpose "CMP Key Generation Authority" (cmKGA). + +OSSL_CRMF_ENCRYPTEDKEY_init_envdata() returns I, intialized with +the enveloped data I. + +OSSL_CRMF_ENCRYPTEDVALUE_decrypt() decrypts the encrypted value in the given +encryptedValue I, using the private key I, library context +I and property query string I (see L). + OSSL_CRMF_ENCRYPTEDVALUE_get1_encCert() decrypts the certificate in the given encryptedValue I, using the private key I, library context I and property query string I (see L). @@ -75,11 +125,21 @@ with the caller, who is responsible for freeing it. OSSL_CRMF_MSG_get_certReqId() retrieves the certReqId of I. +OSSL_CRMF_MSG_centralkeygen_requested() returns 1 if central key generation +is requested i.e., the public key in the certificate request (I is taken if it is non-NULL, +otherwise I) is NULL or has an empty key value (with length zero). +In case I is non-NULL, this is checked for consistency with its B field +(must be NULL if and only if central key generation is requested). +Otherwise it returns 0, and on error a negative value. + =head1 RETURN VALUES OSSL_CRMF_MSG_get_certReqId() returns the certificate request ID as a nonnegative integer or -1 on error. +OSSL_CRMF_MSG_centralkeygen_requested() returns 1 if central key generation +is requested, 0 if it is not requested, and a negative value on error. + All other functions return a pointer with the intended result or NULL on error. =head1 SEE ALSO @@ -92,6 +152,10 @@ The OpenSSL CRMF support was added in OpenSSL 3.0. OSSL_CRMF_CERTTEMPLATE_get0_publicKey() was added in OpenSSL 3.2. +OSSL_CRMF_ENCRYPTEDKEY_get1_encCert(), OSSL_CRMF_ENCRYPTEDKEY_get1_pkey(), +OSSL_CRMF_ENCRYPTEDKEY_init_envdata(), OSSL_CRMF_ENCRYPTEDVALUE_decrypt() +and OSSL_CRMF_MSG_centralkeygen_requested() were added in OpenSSL 3.5. + =head1 COPYRIGHT Copyright 2007-2024 The OpenSSL Project Authors. All Rights Reserved. diff --git a/doc/man3/X509_VERIFY_PARAM_set_flags.pod b/doc/man3/X509_VERIFY_PARAM_set_flags.pod index 221198c86b0..d35c2e2202e 100644 --- a/doc/man3/X509_VERIFY_PARAM_set_flags.pod +++ b/doc/man3/X509_VERIFY_PARAM_set_flags.pod @@ -240,6 +240,9 @@ X509_VERIFY_PARAM_get_depth() returns the current verification depth. X509_VERIFY_PARAM_get_auth_level() returns the current authentication security level. +X509_VERIFY_PARAM_get_purpose() returns the current purpose, +which may be B if unset. + =head1 VERIFICATION FLAGS The verification flags consists of zero or more of the following flags diff --git a/doc/man3/X509_check_purpose.pod b/doc/man3/X509_check_purpose.pod index 70acbc3d58f..0fe4876dc41 100644 --- a/doc/man3/X509_check_purpose.pod +++ b/doc/man3/X509_check_purpose.pod @@ -64,7 +64,8 @@ keyUsage, extendedKeyUsage, and basicConstraints. X509_PURPOSE_get_count() returns the number of currently defined purposes. -X509_PURPOSE_get_unused_id() returns the smallest purpose id not yet used. +X509_PURPOSE_get_unused_id() returns the smallest purpose id not yet used, +which is guaranteed to be unique and larger than B. The I parameter should be used to provide the library context. It is currently ignored as the purpose mapping table is global. diff --git a/doc/man3/X509_dup.pod b/doc/man3/X509_dup.pod index 60765766227..b85c2cbb400 100644 --- a/doc/man3/X509_dup.pod +++ b/doc/man3/X509_dup.pod @@ -32,6 +32,7 @@ CMS_ContentInfo_new, CMS_ContentInfo_new_ex, CMS_ContentInfo_print_ctx, CMS_EnvelopedData_it, +CMS_EnvelopedData_dup, CMS_ReceiptRequest_free, CMS_ReceiptRequest_new, CMS_SignedData_free, @@ -202,6 +203,9 @@ OSSL_CRMF_CERTTEMPLATE_new, OSSL_CRMF_CERTTEMPLATE_dup, OSSL_CRMF_ATTRIBUTETYPEANDVALUE_dup, OSSL_CRMF_ATTRIBUTETYPEANDVALUE_free, +OSSL_CRMF_ENCRYPTEDKEY_free, +OSSL_CRMF_ENCRYPTEDKEY_it, +OSSL_CRMF_ENCRYPTEDKEY_new, OSSL_CRMF_ENCRYPTEDVALUE_free, OSSL_CRMF_ENCRYPTEDVALUE_it, OSSL_CRMF_ENCRYPTEDVALUE_new, @@ -595,7 +599,9 @@ OSSL_TIME_SPEC_TIME_it(), OSSL_TIME_SPEC_TIME_new(), OSSL_TIME_SPEC_WEEKS_free(), OSSL_TIME_SPEC_WEEKS_it(), OSSL_TIME_SPEC_WEEKS_new(), OSSL_TIME_SPEC_X_DAY_OF_free(), OSSL_TIME_SPEC_X_DAY_OF_it(), OSSL_TIME_SPEC_X_DAY_OF_new(), -OSSL_TIME_SPEC_free(), OSSL_TIME_SPEC_it(), OSSL_TIME_SPEC_new() +OSSL_TIME_SPEC_free(), OSSL_TIME_SPEC_it(), OSSL_TIME_SPEC_new(), +CMS_EnvelopedData_dup(), OSSL_CRMF_ENCRYPTEDKEY_free(), +OSSL_CRMF_ENCRYPTEDKEY_it() and OSSL_CRMF_ENCRYPTEDKEY_new() were added in OpenSSL 3.5. =head1 COPYRIGHT diff --git a/doc/man3/d2i_X509.pod b/doc/man3/d2i_X509.pod index e6df4caf680..21dacef3960 100644 --- a/doc/man3/d2i_X509.pod +++ b/doc/man3/d2i_X509.pod @@ -108,6 +108,7 @@ d2i_OSSL_CMP_PKIHEADER, d2i_OSSL_CMP_PKISI, d2i_OSSL_CRMF_CERTID, d2i_OSSL_CRMF_CERTTEMPLATE, +d2i_OSSL_CRMF_ENCRYPTEDKEY, d2i_OSSL_CRMF_ENCRYPTEDVALUE, d2i_OSSL_CRMF_MSG, d2i_OSSL_CRMF_MSGS, @@ -322,6 +323,7 @@ i2d_OSSL_CMP_PKIHEADER, i2d_OSSL_CMP_PKISI, i2d_OSSL_CRMF_CERTID, i2d_OSSL_CRMF_CERTTEMPLATE, +i2d_OSSL_CRMF_ENCRYPTEDKEY, i2d_OSSL_CRMF_ENCRYPTEDVALUE, i2d_OSSL_CRMF_MSG, i2d_OSSL_CRMF_MSGS, @@ -734,6 +736,7 @@ i2d_OSSL_ATTRIBUTE_VALUE_MAPPING(), i2d_OSSL_AUTHORITY_ATTRIBUTE_ID_SYNTAX(), i2d_OSSL_HASH(), i2d_OSSL_INFO_SYNTAX(), i2d_OSSL_INFO_SYNTAX_POINTER(), i2d_OSSL_PRIVILEGE_POLICY_ID(), i2d_OSSL_ROLE_SPEC_CERT_ID(), i2d_OSSL_ROLE_SPEC_CERT_ID_SYNTAX(), +d2i_OSSL_CRMF_ENCRYPTEDKEY(), i2d_OSSL_CRMF_ENCRYPTEDKEY(), d2i_OSSL_DAY_TIME(), d2i_OSSL_DAY_TIME_BAND(), d2i_OSSL_NAMED_DAY(), d2i_OSSL_TIME_PERIOD(), d2i_OSSL_TIME_SPEC(), d2i_OSSL_TIME_SPEC_ABSOLUTE(), d2i_OSSL_TIME_SPEC_DAY(), diff --git a/include/internal/cms.h b/include/internal/cms.h new file mode 100644 index 00000000000..59cdae77798 --- /dev/null +++ b/include/internal/cms.h @@ -0,0 +1,22 @@ +/* + * Copyright 2019-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 + * https://www.openssl.org/source/license.html + */ +#ifndef OSSL_INTERNAL_CMS_H +# define OSSL_INTERNAL_CMS_H +# pragma once + +# include + +# ifndef OPENSSL_NO_CMS +CMS_EnvelopedData *ossl_cms_sign_encrypt(BIO *data, X509 *sign_cert, STACK_OF(X509) *certs, + EVP_PKEY *sign_key, unsigned int sign_flags, + STACK_OF(X509) *enc_recip, const EVP_CIPHER *cipher, + unsigned int enc_flags, OSSL_LIB_CTX *libctx, + const char *propq); +# endif /* OPENSSL_NO_CMS */ +#endif /* OSSL_INTERNAL_CMS_H */ diff --git a/include/openssl/cmperr.h b/include/openssl/cmperr.h index d196924f747..74fcda665fd 100644 --- a/include/openssl/cmperr.h +++ b/include/openssl/cmperr.h @@ -57,6 +57,7 @@ # define CMP_R_ERROR_VALIDATING_SIGNATURE 171 # define CMP_R_EXPECTED_POLLREQ 104 # define CMP_R_FAILED_BUILDING_OWN_CHAIN 164 +# define CMP_R_FAILED_EXTRACTING_CENTRAL_GEN_KEY 203 # define CMP_R_FAILED_EXTRACTING_PUBKEY 141 # define CMP_R_FAILURE_OBTAINING_RANDOM 110 # define CMP_R_FAIL_INFO_OUT_OF_RANGE 129 @@ -69,6 +70,7 @@ # define CMP_R_INVALID_KEYSPEC 202 # define CMP_R_INVALID_OPTION 174 # define CMP_R_INVALID_ROOTCAKEYUPDATE 195 +# define CMP_R_MISSING_CENTRAL_GEN_KEY 204 # define CMP_R_MISSING_CERTID 165 # define CMP_R_MISSING_KEY_INPUT_FOR_CREATING_PROTECTION 130 # define CMP_R_MISSING_KEY_USAGE_DIGITALSIGNATURE 142 @@ -103,6 +105,7 @@ # define CMP_R_TRANSACTIONID_UNMATCHED 152 # define CMP_R_TRANSFER_ERROR 159 # define CMP_R_UNCLEAN_CTX 191 +# define CMP_R_UNEXPECTED_CENTRAL_GEN_KEY 205 # define CMP_R_UNEXPECTED_CERTPROFILE 196 # define CMP_R_UNEXPECTED_CRLSTATUSLIST 201 # define CMP_R_UNEXPECTED_PKIBODY 133 diff --git a/include/openssl/cms.h.in b/include/openssl/cms.h.in index 15e21aa5c50..ed675868ce6 100644 --- a/include/openssl/cms.h.in +++ b/include/openssl/cms.h.in @@ -57,6 +57,8 @@ DECLARE_ASN1_FUNCTIONS(CMS_ContentInfo) DECLARE_ASN1_FUNCTIONS(CMS_ReceiptRequest) DECLARE_ASN1_PRINT_FUNCTION(CMS_ContentInfo) +DECLARE_ASN1_DUP_FUNCTION(CMS_EnvelopedData) + CMS_ContentInfo *CMS_ContentInfo_new_ex(OSSL_LIB_CTX *libctx, const char *propq); # define CMS_SIGNERINFO_ISSUER_SERIAL 0 diff --git a/include/openssl/crmf.h.in b/include/openssl/crmf.h.in index 17dc4d9103a..90dafafae5f 100644 --- a/include/openssl/crmf.h.in +++ b/include/openssl/crmf.h.in @@ -27,6 +27,7 @@ use OpenSSL::stackhash qw(generate_stack_macros); # include # include # include /* for GENERAL_NAME etc. */ +# include /* explicit #includes not strictly needed since implied by the above: */ # include @@ -45,8 +46,11 @@ extern "C" { # define OSSL_CRMF_SUBSEQUENTMESSAGE_ENCRCERT 0 # define OSSL_CRMF_SUBSEQUENTMESSAGE_CHALLENGERESP 1 typedef struct ossl_crmf_encryptedvalue_st OSSL_CRMF_ENCRYPTEDVALUE; - DECLARE_ASN1_FUNCTIONS(OSSL_CRMF_ENCRYPTEDVALUE) + +typedef struct ossl_crmf_encryptedkey_st OSSL_CRMF_ENCRYPTEDKEY; +DECLARE_ASN1_FUNCTIONS(OSSL_CRMF_ENCRYPTEDKEY) + typedef struct ossl_crmf_msg_st OSSL_CRMF_MSG; DECLARE_ASN1_FUNCTIONS(OSSL_CRMF_MSG) DECLARE_ASN1_DUP_FUNCTION(OSSL_CRMF_MSG) @@ -177,10 +181,24 @@ int OSSL_CRMF_CERTTEMPLATE_fill(OSSL_CRMF_CERTTEMPLATE *tmpl, const X509_NAME *subject, const X509_NAME *issuer, const ASN1_INTEGER *serial); -X509 -*OSSL_CRMF_ENCRYPTEDVALUE_get1_encCert(const OSSL_CRMF_ENCRYPTEDVALUE *ecert, - OSSL_LIB_CTX *libctx, const char *propq, - EVP_PKEY *pkey); +X509 *OSSL_CRMF_ENCRYPTEDVALUE_get1_encCert(const OSSL_CRMF_ENCRYPTEDVALUE *ecert, + OSSL_LIB_CTX *libctx, const char *propq, + EVP_PKEY *pkey); +X509 *OSSL_CRMF_ENCRYPTEDKEY_get1_encCert(const OSSL_CRMF_ENCRYPTEDKEY *ecert, + OSSL_LIB_CTX *libctx, const char *propq, + EVP_PKEY *pkey, unsigned int flags); +unsigned char +*OSSL_CRMF_ENCRYPTEDVALUE_decrypt(const OSSL_CRMF_ENCRYPTEDVALUE *enc, + OSSL_LIB_CTX *libctx, const char *propq, + EVP_PKEY *pkey, int *outlen); +EVP_PKEY *OSSL_CRMF_ENCRYPTEDKEY_get1_pkey(const OSSL_CRMF_ENCRYPTEDKEY *encryptedKey, + X509_STORE *ts, STACK_OF(X509) *extra, EVP_PKEY *pkey, + X509 *cert, ASN1_OCTET_STRING *secret, + OSSL_LIB_CTX *libctx, const char *propq); +int OSSL_CRMF_MSG_centralkeygen_requested(const OSSL_CRMF_MSG *crm, const X509_REQ *p10cr); +# ifndef OPENSSL_NO_CMS +OSSL_CRMF_ENCRYPTEDKEY *OSSL_CRMF_ENCRYPTEDKEY_init_envdata(CMS_EnvelopedData *envdata); +# endif # ifdef __cplusplus } diff --git a/include/openssl/crmferr.h b/include/openssl/crmferr.h index b242b922ef1..d67270a4c1c 100644 --- a/include/openssl/crmferr.h +++ b/include/openssl/crmferr.h @@ -24,16 +24,23 @@ * CRMF reason codes. */ # define CRMF_R_BAD_PBM_ITERATIONCOUNT 100 +# define CRMF_R_CMS_NOT_SUPPORTED 122 # define CRMF_R_CRMFERROR 102 # define CRMF_R_ERROR 103 # define CRMF_R_ERROR_DECODING_CERTIFICATE 104 +# define CRMF_R_ERROR_DECODING_ENCRYPTEDKEY 123 # define CRMF_R_ERROR_DECRYPTING_CERTIFICATE 105 +# define CRMF_R_ERROR_DECRYPTING_ENCRYPTEDKEY 124 +# define CRMF_R_ERROR_DECRYPTING_ENCRYPTEDVALUE 125 # define CRMF_R_ERROR_DECRYPTING_SYMMETRIC_KEY 106 +# define CRMF_R_ERROR_SETTING_PURPOSE 126 +# define CRMF_R_ERROR_VERIFYING_ENCRYPTEDKEY 127 # define CRMF_R_FAILURE_OBTAINING_RANDOM 107 # define CRMF_R_ITERATIONCOUNT_BELOW_100 108 # define CRMF_R_MALFORMED_IV 101 # define CRMF_R_NULL_ARGUMENT 109 # define CRMF_R_POPOSKINPUT_NOT_SUPPORTED 113 +# define CRMF_R_POPO_INCONSISTENT_CENTRAL_KEYGEN 128 # define CRMF_R_POPO_INCONSISTENT_PUBLIC_KEY 117 # define CRMF_R_POPO_MISSING 121 # define CRMF_R_POPO_MISSING_PUBLIC_KEY 118 diff --git a/test/recipes/80-test_cmp_http.t b/test/recipes/80-test_cmp_http.t index 7b8a5b237b3..3e90001a616 100644 --- a/test/recipes/80-test_cmp_http.t +++ b/test/recipes/80-test_cmp_http.t @@ -32,6 +32,8 @@ plan skip_all => "These tests are not supported in a no-sock build" if disabled("sock"); plan skip_all => "These tests are not supported in a no-http build" if disabled("http"); +plan skip_all => "These tests are not supported in a no-cms build" + if disabled("cms"); # central key pair generation plan skip_all => "Tests involving local HTTP server not available on Windows or VMS" if $^O =~ /^(VMS|MSWin32|msys)$/; diff --git a/test/recipes/80-test_cmp_http_data/Mock/server.cnf b/test/recipes/80-test_cmp_http_data/Mock/server.cnf index 86a11f6a368..e2a47ff62ed 100644 --- a/test/recipes/80-test_cmp_http_data/Mock/server.cnf +++ b/test/recipes/80-test_cmp_http_data/Mock/server.cnf @@ -12,6 +12,7 @@ no_cache_extracerts = 1 ref_cert = signer_only.crt rsp_cert = signer_only.crt +rsp_key = new.key rsp_crl = newcrl.pem rsp_capubs = trusted.crt rsp_extracerts = signer_issuing.crt diff --git a/test/recipes/80-test_cmp_http_data/Mock/server.crt b/test/recipes/80-test_cmp_http_data/Mock/server.crt index 07eab2e63b8..d49b846995f 100644 --- a/test/recipes/80-test_cmp_http_data/Mock/server.crt +++ b/test/recipes/80-test_cmp_http_data/Mock/server.crt @@ -1,24 +1,25 @@ Issuer: CN=Root CA Validity - Not Before: Jan 14 22:29:46 2016 GMT - Not After : Jan 15 22:29:46 2116 GMT + Not Before: Aug 8 13:28:36 2024 GMT + Not After : Apr 11 13:28:36 2127 GMT Subject: CN=server.example -----BEGIN CERTIFICATE----- -MIIDJTCCAg2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdSb290 -IENBMCAXDTE2MDExNDIyMjk0NloYDzIxMTYwMTE1MjIyOTQ2WjAZMRcwFQYDVQQD -DA5zZXJ2ZXIuZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB -ANVdYGrf/GHuSKqMEUhDpW22Ul2qmEmxYZI1sfw6BCUMbXn/tNXJ6VwcO+Crs7h9 -o95tveDd11q/FEcRQl6mgtBhwX/dE0bmCYUHDvLU/Bpk0gqtIKsga5bwrczEGVNV -3AEdpLPvirRJU12KBRzx3OFEv8XX4ncZV1yXC3XuiENxD8pswbSyUKd3RmxYDxG/ -8XYkWq45QrdRZynh0FUwbxfkkeqt+CjCQ2+iZKn7nZiSYkg+6w1PgkqK/z9y7pa1 -rqHBmLrvfZB1bf9aUp6r9cB+0IdD24UHBw99OHr90dPuZR3T6jlqhzfuStPgDW71 -cKzCvfFu85KVXqnwoWWVk40CAwEAAaN9MHswHQYDVR0OBBYEFMDnhL/oWSczELBS -T1FSLwbWwHrNMB8GA1UdIwQYMBaAFHB/Lq6DaFmYBCMqzes+F80k3QFJMAkGA1Ud -EwQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwGQYDVR0RBBIwEIIOc2VydmVyLmV4 -YW1wbGUwDQYJKoZIhvcNAQELBQADggEBAHvTBEN1ig8RrsT716Ginv4gGNX0LzGI -RrZ1jO7lm5emuaPNYJpGw0iX5Zdo91qGNXPZaZ75X3S55pQTActq3OPEBOll2pyk -iyjz+Zp/v5cfRZLlBbFW5gv2R94eibYr4U3fSn4B0yPcl4xH/l/HzJhGDsSDW8qK -8VIJvmvsPwmL0JMCv+FR59F+NFYZdND/KCXet59WUpF9ICmFCoBEX3EyJXEPwhbi -X2sdPzJbCjx0HLli8e0HUKNttLQxCsBTRGo6iISLLamwN47mGDa9miBADwGSiz2q -YeeuLO02zToHhnQ6KbPXOrQAqcL1kngO4g+j/ru+4AZThFkdkGnltvk= +MIIDVDCCAjygAwIBAgIUMoDahWpYk1B1WIjOwkom4s27V/EwDQYJKoZIhvcNAQEL +BQAwEjEQMA4GA1UEAwwHUm9vdCBDQTAgFw0yNDA4MDgxMzI4MzZaGA8yMTI3MDQx +MTEzMjgzNlowGTEXMBUGA1UEAwwOc2VydmVyLmV4YW1wbGUwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDVXWBq3/xh7kiqjBFIQ6VttlJdqphJsWGSNbH8 +OgQlDG15/7TVyelcHDvgq7O4faPebb3g3ddavxRHEUJepoLQYcF/3RNG5gmFBw7y +1PwaZNIKrSCrIGuW8K3MxBlTVdwBHaSz74q0SVNdigUc8dzhRL/F1+J3GVdclwt1 +7ohDcQ/KbMG0slCnd0ZsWA8Rv/F2JFquOUK3UWcp4dBVMG8X5JHqrfgowkNvomSp ++52YkmJIPusNT4JKiv8/cu6Wta6hwZi6732QdW3/WlKeq/XAftCHQ9uFBwcPfTh6 +/dHT7mUd0+o5aoc37krT4A1u9XCswr3xbvOSlV6p8KFllZONAgMBAAGjgZgwgZUw +CQYDVR0TBAIwADAdBgNVHQ4EFgQUwOeEv+hZJzMQsFJPUVIvBtbAes0wHwYDVR0j +BBgwFoAUcH8uroNoWZgEIyrN6z4XzSTdAUkwDgYDVR0PAQH/BAQDAgWgMB0GA1Ud +JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDIDAZBgNVHREEEjAQgg5zZXJ2ZXIuZXhh +bXBsZTANBgkqhkiG9w0BAQsFAAOCAQEAPRQ87zMFGrcQau8h8wDULU2PPgo1nifQ +1Vs+4WD7bPzk5GHl3M3OE2ZwhzYfO+ACcJa29Ahu7GRC660lXKlwONnQYuTTLqPD +KylY9ZJQUyP+CA5oZsDtnOcfrTy837jKq18NQ3ZxbRDpoVIATNHf9J2qe9yIkxYe +9p+aXGdvfDsNrhkz/m76V+KmioventOEKsRg64FfGKEZP8EfoBiNJipBdmN1I+GE +VTW91jjpgBTdCmyncmVn/CaV1d5S4ed4oQMLlLc+KsqDe97bF2TssFxcMmnyxgqb +bKDZOfzPI1HTs22tj6eLcZTIkcAuFET+uxcmFQcDsjYEf0G+iZLGWw== -----END CERTIFICATE----- diff --git a/test/recipes/80-test_cmp_http_data/test_commands.csv b/test/recipes/80-test_cmp_http_data/test_commands.csv index 26b985bf65d..cbb806a9c6c 100644 --- a/test/recipes/80-test_cmp_http_data/test_commands.csv +++ b/test/recipes/80-test_cmp_http_data/test_commands.csv @@ -135,5 +135,17 @@ expected,description, -section,val, -cmd,val,val2, -cacertsout,val,val2, -infoty 1,reqout_only ir - no server, -section,, -cmd,ir,,-reqout_only,_RESULT_DIR/ir2.der,,BLANK,,BLANK, -server,"""" 0,reqout_only non-existing directory and file, -section,, -cmd,ir,,-reqout_only,idontexist/idontexist,,BLANK,,BLANK, -server,"""" 0,reqin ir - no newkey, -section,, -cmd,ir,,-reqin,_RESULT_DIR/ir2.der,,-newkey,"""",-newkey,"""",-key,"""",-cert,"""",-secret,_PBM_SECRET -1,reqin ir and rspout - no newkey but -popo -1, -section,, -cmd,ir,,-reqin,_RESULT_DIR/ir2.der,,-rspout,_RESULT_DIR/ip2.der,-newkey,"""",--key,"""",-cert,"""",-secret,_PBM_SECRET,-popo,-1 -1,reqin ip and rspin - no newkey but -popo -1, -section,, -cmd,ir,,-reqin,_RESULT_DIR/ir2.der,,-rspin,_RESULT_DIR/ip2.der,,-newkey,"""",-key,"""",-cert,"""",-secret,_PBM_SECRET,-popo,-1, -server,"""",-disable_confirm +1,reqin ir and rspout - using no newkey and -popo 0 as workaround, -section,, -cmd,ir,,-reqin,_RESULT_DIR/ir2.der,,-rspout,_RESULT_DIR/ip2.der,-newkey,"""", -popo,0 +1,reqin ip and rspin - using no newkey and -popo 0 as workaround, -section,, -cmd,ir,,-reqin,_RESULT_DIR/ir2.der,,-rspin,_RESULT_DIR/ip2.der,,-newkey,"""",-server,"""",-disable_confirm, -popo,0 +1,reqout_only ir - no server with -popo -1 (same as -centralkeygen), -section,, -cmd,ir,,-reqout_only,_RESULT_DIR/ir3.der,,BLANK,,BLANK, -server,"""", -popo,-1, -newkeyout,_RESULT_DIR/dummyout.pem +1,reqin ir and rspout - using no newkey and -popo -1 (same as -centralkeygen), -section,, -cmd,ir,,-reqin,_RESULT_DIR/ir3.der,,-rspout,_RESULT_DIR/ip3.der,-newkey,"""", -popo,-1, -newkeyout,_RESULT_DIR/newkeyout.pem +1,reqin ip and rspin - using no newkey and -popo -1 (same as -centralkeygen), -section,, -cmd,ir,,-reqin,_RESULT_DIR/ir3.der,,-rspin,_RESULT_DIR/ip3.der,,-newkey,"""",-server,"""",-disable_confirm, -popo,-1, -newkeyout,_RESULT_DIR/newkeyout.pem +,,,,,,,,,,,,,,,,,,, +1,central key generation, -section,, -cmd,cr,, -centralkeygen, -newkeyout,_RESULT_DIR/newkeyout1.pem +0,central key generation missing newkeyout, -section,, -cmd,cr,, -centralkeygen,,BLANK,,BLANK,,BLANK,,BLANK, +0,using popo 1 with -centralkeygen, -section,, -cmd,cr,, -centralkeygen, -popo,1, -newkeyout,_RESULT_DIR/newkeyout.pem +1, using popo -1 redundantly with -centralkeygen, -section,, -cmd,cr,, -centralkeygen, -popo,-1, -newkeyout,_RESULT_DIR/newkeyout2.pem +1, using popo -1 alternatively to -centralkeygen, -section,, -cmd,cr,, -popo,-1, -newkeyout,_RESULT_DIR/newkeyout3.pem, -newkeypass,pass:12345, -certout,_RESULT_DIR/test.cert3.pem +1, using centrally generated key (and cert) , -section,, -cmd,cr,,-cert,_RESULT_DIR/test.cert3.pem, -key,_RESULT_DIR/newkeyout3.pem, -keypass,pass:12345 +0, using centrally generated key with wrong password, -section,, -cmd,cr,,-cert,_RESULT_DIR/test.cert3.pem, -key,_RESULT_DIR/newkeyout3.pem, -keypass,pass:wrong +0, using popo -1 (instead of -centralkeygen) without -newkeyout, -section,, -cmd,cr,, -popo,-1,,BLANK,,BLANK,,BLANK,,BLANK diff --git a/util/libcrypto.num b/util/libcrypto.num index 7e4d850800a..6e9a8a121a3 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -5734,6 +5734,17 @@ EVP_CIPHER_CTX_get_algor 5861 3_4_0 EXIST::FUNCTION: EVP_PKEY_CTX_set_algor_params 5862 3_4_0 EXIST::FUNCTION: EVP_PKEY_CTX_get_algor_params 5863 3_4_0 EXIST::FUNCTION: EVP_PKEY_CTX_get_algor 5864 3_4_0 EXIST::FUNCTION: +d2i_OSSL_CRMF_ENCRYPTEDKEY ? 3_5_0 EXIST::FUNCTION:CRMF +i2d_OSSL_CRMF_ENCRYPTEDKEY ? 3_5_0 EXIST::FUNCTION:CRMF +OSSL_CRMF_ENCRYPTEDKEY_free ? 3_5_0 EXIST::FUNCTION:CRMF +OSSL_CRMF_ENCRYPTEDKEY_new ? 3_5_0 EXIST::FUNCTION:CRMF +OSSL_CRMF_ENCRYPTEDKEY_it ? 3_5_0 EXIST::FUNCTION:CRMF +OSSL_CRMF_ENCRYPTEDKEY_get1_encCert ? 3_5_0 EXIST::FUNCTION:CRMF +OSSL_CRMF_ENCRYPTEDVALUE_decrypt ? 3_5_0 EXIST::FUNCTION:CRMF +OSSL_CRMF_ENCRYPTEDKEY_get1_pkey ? 3_5_0 EXIST::FUNCTION:CRMF +OSSL_CRMF_MSG_centralkeygen_requested ? 3_5_0 EXIST::FUNCTION:CRMF +CMS_EnvelopedData_dup ? 3_5_0 EXIST::FUNCTION:CMS +OSSL_CRMF_ENCRYPTEDKEY_init_envdata ? 3_5_0 EXIST::FUNCTION:CMS,CRMF EVP_get1_default_properties ? 3_5_0 EXIST::FUNCTION: X509_PURPOSE_get_unused_id ? 3_5_0 EXIST::FUNCTION: d2i_OSSL_AUTHORITY_ATTRIBUTE_ID_SYNTAX ? 3_5_0 EXIST::FUNCTION: