[openssl] master update

dev at ddvo.net dev at ddvo.net
Wed Mar 25 13:15:00 UTC 2020


The branch master has been updated
       via  7e765f46a6b3a5b2fc48e10657bea7016e5c5e4b (commit)
       via  b4ba2b7ce0933bede5d3b59a5abbde8fa3de2228 (commit)
       via  cbb9b7c460f5ecf7e4c32a9a8176f3078f1d8ec6 (commit)
      from  71f852802f453db9be24bb83385288c7d7b83ae1 (commit)


- Log -----------------------------------------------------------------
commit 7e765f46a6b3a5b2fc48e10657bea7016e5c5e4b
Author: Dr. David von Oheimb <David.von.Oheimb at siemens.com>
Date:   Tue Mar 10 17:32:57 2020 +0100

    Chunk 9 of CMP contribution to OpenSSL: CMP client and related tests
    
    Certificate Management Protocol (CMP, RFC 4210) extension to OpenSSL
    Also includes CRMF (RFC 4211) and HTTP transfer (RFC 6712).
    Adds the CMP and CRMF API to libcrypto and the "cmp" app to the CLI.
    Adds extensive documentation and tests.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: David von Oheimb <david.von.oheimb at siemens.com>
    (Merged from https://github.com/openssl/openssl/pull/11300)

commit b4ba2b7ce0933bede5d3b59a5abbde8fa3de2228
Author: Dr. David von Oheimb <David.von.Oheimb at siemens.com>
Date:   Fri Mar 20 15:15:34 2020 +0100

    Fix bug in strncpy() use of sk_ASN1_UTF8STRING2text() in asn1_lib.c
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: David von Oheimb <david.von.oheimb at siemens.com>
    (Merged from https://github.com/openssl/openssl/pull/11300)

commit cbb9b7c460f5ecf7e4c32a9a8176f3078f1d8ec6
Author: Dr. David von Oheimb <David.von.Oheimb at siemens.com>
Date:   Thu Mar 12 09:51:55 2020 +0100

    Fix whitespace nit in OSSL_SELF_TEST_new.pod which caused doc-nits warning
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: David von Oheimb <david.von.oheimb at siemens.com>
    (Merged from https://github.com/openssl/openssl/pull/11300)

-----------------------------------------------------------------------

Summary of changes:
 apps/cmp_mock_srv.c                                |  11 +-
 crypto/asn1/asn1_lib.c                             |   2 +-
 crypto/cmp/build.info                              |   2 +-
 crypto/cmp/cmp_client.c                            | 881 +++++++++++++++++++++
 crypto/cmp/cmp_ctx.c                               |  42 +-
 crypto/cmp/cmp_err.c                               |  37 +-
 crypto/cmp/cmp_local.h                             |  16 +-
 crypto/cmp/cmp_msg.c                               |   3 +-
 crypto/cmp/cmp_server.c                            |  18 +-
 crypto/cmp/cmp_status.c                            |   2 +-
 crypto/cmp/cmp_util.c                              |   2 +-
 crypto/cmp/cmp_vfy.c                               |  39 +-
 crypto/err/openssl.txt                             |  22 +-
 doc/internal/man3/ossl_cmp_msg_check_received.pod  |  86 ++
 doc/internal/man3/ossl_cmp_msg_create.pod          |   3 +-
 doc/man3/OSSL_CMP_CTX_new.pod                      |  38 +-
 doc/man3/OSSL_CMP_SRV_CTX_new.pod                  |   3 +
 doc/man3/OSSL_CMP_exec_IR_ses.pod                  | 172 ++++
 doc/man3/OSSL_CMP_log_open.pod                     |   8 +-
 doc/man3/OSSL_SELF_TEST_new.pod                    |   2 +-
 include/openssl/cmp.h                              |  37 +-
 include/openssl/cmp_util.h                         |   4 +-
 include/openssl/cmperr.h                           |  22 +-
 test/build.info                                    |   6 +-
 test/cmp_client_test.c                             | 393 +++++++++
 test/cmp_ctx_test.c                                |   6 +-
 test/cmp_msg_test.c                                |   4 +-
 test/cmp_testlib.c                                 |   6 +
 test/cmp_testlib.h                                 |   2 +
 test/cmp_vfy_test.c                                |   6 -
 .../{65-test_cmp_msg.t => 65-test_cmp_client.t}    |   9 +-
 test/recipes/65-test_cmp_client_data/client.crt    |  13 +
 test/recipes/65-test_cmp_client_data/client.csr    | Bin 0 -> 424 bytes
 test/recipes/65-test_cmp_client_data/client.key    |   7 +
 .../server.crt                                     |   0
 .../server.key                                     |   0
 util/libcrypto.num                                 |  10 +-
 util/missingcrypto.txt                             |   3 -
 util/other.syms                                    |  11 +-
 39 files changed, 1797 insertions(+), 131 deletions(-)
 create mode 100644 crypto/cmp/cmp_client.c
 create mode 100644 doc/internal/man3/ossl_cmp_msg_check_received.pod
 create mode 100644 doc/man3/OSSL_CMP_exec_IR_ses.pod
 create mode 100644 test/cmp_client_test.c
 copy test/recipes/{65-test_cmp_msg.t => 65-test_cmp_client.t} (74%)
 create mode 100644 test/recipes/65-test_cmp_client_data/client.crt
 create mode 100644 test/recipes/65-test_cmp_client_data/client.csr
 create mode 100644 test/recipes/65-test_cmp_client_data/client.key
 copy test/recipes/{65-test_cmp_msg_data => 65-test_cmp_client_data}/server.crt (100%)
 copy test/recipes/{65-test_cmp_vfy_data => 65-test_cmp_client_data}/server.key (100%)

diff --git a/apps/cmp_mock_srv.c b/apps/cmp_mock_srv.c
index 2e02104884..8ffe4ca5a8 100644
--- a/apps/cmp_mock_srv.c
+++ b/apps/cmp_mock_srv.c
@@ -187,7 +187,7 @@ static OSSL_CMP_PKISI *process_cert_request(OSSL_CMP_SRV_CTX *srv_ctx,
         return NULL;
     }
     if (ctx->sendError) {
-        CMPerr(0, CMP_R_ERROR_PROCESSING_MSG);
+        CMPerr(0, CMP_R_ERROR_PROCESSING_MESSAGE);
         return NULL;
     }
 
@@ -238,7 +238,7 @@ static OSSL_CMP_PKISI *process_rr(OSSL_CMP_SRV_CTX *srv_ctx,
         return NULL;
     }
     if (ctx->sendError || ctx->certOut == NULL) {
-        CMPerr(0, CMP_R_ERROR_PROCESSING_MSG);
+        CMPerr(0, CMP_R_ERROR_PROCESSING_MESSAGE);
         return NULL;
     }
 
@@ -264,7 +264,7 @@ static int process_genm(OSSL_CMP_SRV_CTX *srv_ctx,
         return 0;
     }
     if (ctx->sendError) {
-        CMPerr(0, CMP_R_ERROR_PROCESSING_MSG);
+        CMPerr(0, CMP_R_ERROR_PROCESSING_MESSAGE);
         return 0;
     }
 
@@ -306,6 +306,7 @@ static void process_error(OSSL_CMP_SRV_CTX *srv_ctx, const OSSL_CMP_MSG *error,
     if (sk_ASN1_UTF8STRING_num(errorDetails) <= 0) {
         BIO_printf(bio_err, "errorDetails absent\n");
     } else {
+        /* TODO could use sk_ASN1_UTF8STRING2text() if exported */
         BIO_printf(bio_err, "errorDetails: ");
         for (i = 0; i < sk_ASN1_UTF8STRING_num(errorDetails); i++) {
             if (i > 0)
@@ -332,7 +333,7 @@ static int process_certConf(OSSL_CMP_SRV_CTX *srv_ctx,
         return 0;
     }
     if (ctx->sendError || ctx->certOut == NULL) {
-        CMPerr(0, CMP_R_ERROR_PROCESSING_MSG);
+        CMPerr(0, CMP_R_ERROR_PROCESSING_MESSAGE);
         return 0;
     }
 
@@ -366,7 +367,7 @@ static int process_pollReq(OSSL_CMP_SRV_CTX *srv_ctx,
     }
     if (ctx->sendError || ctx->certReq == NULL) {
         *certReq = NULL;
-        CMPerr(0, CMP_R_ERROR_PROCESSING_MSG);
+        CMPerr(0, CMP_R_ERROR_PROCESSING_MESSAGE);
         return 0;
     }
 
diff --git a/crypto/asn1/asn1_lib.c b/crypto/asn1/asn1_lib.c
index 765bbda5c7..fc4462eb8f 100644
--- a/crypto/asn1/asn1_lib.c
+++ b/crypto/asn1/asn1_lib.c
@@ -426,7 +426,7 @@ char *sk_ASN1_UTF8STRING2text(STACK_OF(ASN1_UTF8STRING) *text, const char *sep,
         current = sk_ASN1_UTF8STRING_value(text, i);
         length = ASN1_STRING_length(current);
         if (i > 0 && sep_len > 0) {
-            strncpy(p, sep, sep_len);
+            strncpy(p, sep, sep_len + 1);
             p += sep_len;
         }
         strncpy(p, (const char *)ASN1_STRING_get0_data(current), length);
diff --git a/crypto/cmp/build.info b/crypto/cmp/build.info
index 1667334e2a..d3fbae2452 100644
--- a/crypto/cmp/build.info
+++ b/crypto/cmp/build.info
@@ -1,4 +1,4 @@
 LIBS=../../libcrypto
 SOURCE[../../libcrypto]= cmp_asn.c cmp_ctx.c cmp_err.c cmp_util.c \
         cmp_status.c cmp_hdr.c cmp_protect.c cmp_msg.c cmp_vfy.c \
-        cmp_server.c
+        cmp_server.c cmp_client.c
diff --git a/crypto/cmp/cmp_client.c b/crypto/cmp/cmp_client.c
new file mode 100644
index 0000000000..394358c5e3
--- /dev/null
+++ b/crypto/cmp/cmp_client.c
@@ -0,0 +1,881 @@
+/*
+ * Copyright 2007-2019 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright Nokia 2007-2019
+ * Copyright Siemens AG 2015-2019
+ *
+ * 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
+ */
+
+#include "cmp_local.h"
+#include "internal/cryptlib.h"
+
+/* explicit #includes not strictly needed since implied by the above: */
+#include <openssl/bio.h>
+#include <openssl/cmp.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/x509v3.h>
+
+#include "openssl/cmp_util.h"
+
+#define IS_CREP(t) ((t) == OSSL_CMP_PKIBODY_IP || (t) == OSSL_CMP_PKIBODY_CP \
+                        || (t) == OSSL_CMP_PKIBODY_KUP)
+
+/*-
+ * Evaluate whether there's an exception (violating the standard) configured for
+ * handling negative responses without protection or with invalid protection.
+ * Returns 1 on acceptance, 0 on rejection, or -1 on (internal) error.
+ */
+static int unprotected_exception(const OSSL_CMP_CTX *ctx,
+                                 const OSSL_CMP_MSG *rep,
+                                 int invalid_protection,
+                                 int expected_type /* ignored here */)
+{
+    int rcvd_type = ossl_cmp_msg_get_bodytype(rep /* may be NULL */);
+    const char *msg_type = NULL;
+
+    if (!ossl_assert(ctx != NULL && rep != NULL))
+        return -1;
+
+    if (!ctx->unprotectedErrors)
+        return 0;
+
+    switch (rcvd_type) {
+    case OSSL_CMP_PKIBODY_ERROR:
+        msg_type = "error response";
+        break;
+    case OSSL_CMP_PKIBODY_RP:
+        {
+            OSSL_CMP_PKISI *si =
+                ossl_cmp_revrepcontent_get_pkisi(rep->body->value.rp,
+                                                 OSSL_CMP_REVREQSID);
+
+            if (si == NULL)
+                return -1;
+            if (ossl_cmp_pkisi_get_status(si) == OSSL_CMP_PKISTATUS_rejection)
+                msg_type = "revocation response message with rejection status";
+            break;
+        }
+    case OSSL_CMP_PKIBODY_PKICONF:
+        msg_type = "PKI Confirmation message";
+        break;
+    default:
+        if (IS_CREP(rcvd_type)) {
+            OSSL_CMP_CERTREPMESSAGE *crepmsg = rep->body->value.ip;
+            OSSL_CMP_CERTRESPONSE *crep =
+                ossl_cmp_certrepmessage_get0_certresponse(crepmsg,
+                                                          -1 /* any rid */);
+
+            if (sk_OSSL_CMP_CERTRESPONSE_num(crepmsg->response) > 1)
+                return -1;
+            /* TODO: handle potentially multiple CertResponses in CertRepMsg */
+            if (crep == NULL)
+                return -1;
+            if (ossl_cmp_pkisi_get_status(crep->status)
+                == OSSL_CMP_PKISTATUS_rejection)
+                msg_type = "CertRepMessage with rejection status";
+        }
+    }
+    if (msg_type == NULL)
+        return 0;
+    ossl_cmp_log2(WARN, ctx, "ignoring %s protection of %s",
+                  invalid_protection ? "invalid" : "missing", msg_type);
+    return 1;
+}
+
+
+/* Save error info from PKIStatusInfo field of a certresponse into ctx */
+static int save_statusInfo(OSSL_CMP_CTX *ctx, OSSL_CMP_PKISI *si)
+{
+    int i;
+    OSSL_CMP_PKIFREETEXT *ss;
+
+    if (!ossl_assert(ctx != NULL && si != NULL))
+        return 0;
+
+    if ((ctx->status = ossl_cmp_pkisi_get_status(si)) < 0)
+        return 0;
+
+    ctx->failInfoCode = 0;
+    if (si->failInfo != NULL) {
+        for (i = 0; i <= OSSL_CMP_PKIFAILUREINFO_MAX; i++) {
+            if (ASN1_BIT_STRING_get_bit(si->failInfo, i))
+                ctx->failInfoCode |= (1 << i);
+        }
+    }
+
+    if (!ossl_cmp_ctx_set0_statusString(ctx, sk_ASN1_UTF8STRING_new_null())
+            || (ctx->statusString == NULL))
+        return 0;
+
+    ss = si->statusString; /* may be NULL */
+    for (i = 0; i < sk_ASN1_UTF8STRING_num(ss); i++) {
+        ASN1_UTF8STRING *str = sk_ASN1_UTF8STRING_value(ss, i);
+
+        if (!sk_ASN1_UTF8STRING_push(ctx->statusString, ASN1_STRING_dup(str)))
+            return 0;
+    }
+    return 1;
+}
+
+/*-
+ * Perform the generic aspects of sending a request and receiving a response.
+ * Returns 1 on success and provides the received PKIMESSAGE in *rep.
+ * Returns 0 on error.
+ * Regardless of success, caller is responsible for freeing *rep (unless NULL).
+ */
+static int send_receive_check(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *req,
+                              OSSL_CMP_MSG **rep, int expected_type)
+{
+    const char *req_type_str =
+        ossl_cmp_bodytype_to_string(ossl_cmp_msg_get_bodytype(req));
+    const char *expected_type_str = ossl_cmp_bodytype_to_string(expected_type);
+    int msg_timeout;
+    int bt;
+    time_t now = time(NULL);
+    int time_left;
+    OSSL_CMP_transfer_cb_t transfer_cb = ctx->transfer_cb;
+
+    if (transfer_cb == NULL)
+        transfer_cb = NULL; /* TODO: will be OSSL_CMP_MSG_http_perform of chunk 10 */
+
+    *rep = NULL;
+    msg_timeout = ctx->msg_timeout; /* backup original value */
+    if ((IS_CREP(expected_type) || expected_type == OSSL_CMP_PKIBODY_POLLREP)
+            && ctx->total_timeout > 0 /* timeout is not infinite */) {
+        if (now >= ctx->end_time) {
+            CMPerr(0, CMP_R_TOTAL_TIMEOUT);
+            return 0;
+        }
+        if (!ossl_assert(ctx->end_time - time(NULL) < INT_MAX)) {
+            /* cannot really happen due to the assignment in do_certreq_seq() */
+            CMPerr(0, CMP_R_INVALID_ARGS);
+            return 0;
+        }
+        time_left = (int)(ctx->end_time - now);
+        if (ctx->msg_timeout == 0 || time_left < ctx->msg_timeout)
+            ctx->msg_timeout = time_left;
+    }
+
+    /* should print error queue since transfer_cb may call ERR_clear_error() */
+    OSSL_CMP_CTX_print_errors(ctx);
+
+    ossl_cmp_log1(INFO, ctx, "sending %s", req_type_str);
+
+    *rep = (*transfer_cb)(ctx, req);
+    ctx->msg_timeout = msg_timeout; /* restore original value */
+
+    if (*rep == NULL) {
+        CMPerr(0, CMP_R_TRANSFER_ERROR); /* or receiving response */
+        ERR_add_error_data(1, req_type_str);
+        ERR_add_error_data(2, ", expected response: ", expected_type_str);
+        return 0;
+    }
+
+    bt = ossl_cmp_msg_get_bodytype(*rep);
+    /*
+     * The body type in the 'bt' variable is not yet verified.
+     * Still we use this preliminary value already for a progress report because
+     * the following msg verification may also produce log entries and may fail.
+     */
+    ossl_cmp_log1(INFO, ctx, "received %s", ossl_cmp_bodytype_to_string(bt));
+
+    if ((bt = ossl_cmp_msg_check_received(ctx, *rep, unprotected_exception,
+                                          expected_type)) < 0)
+        return 0;
+
+    if (bt == expected_type
+        /* as an answer to polling, there could be IP/CP/KUP: */
+            || (IS_CREP(bt) && expected_type == OSSL_CMP_PKIBODY_POLLREP))
+        return 1;
+
+    /* received message type is not one of the expected ones (e.g., error) */
+    CMPerr(0, bt == OSSL_CMP_PKIBODY_ERROR ? CMP_R_RECEIVED_ERROR :
+           CMP_R_UNEXPECTED_PKIBODY); /* in next line for mkerr.pl */
+
+    if (bt != OSSL_CMP_PKIBODY_ERROR) {
+        ERR_add_error_data(3, "message type is '",
+                           ossl_cmp_bodytype_to_string(bt), "'");
+    } else {
+        OSSL_CMP_ERRORMSGCONTENT *emc = (*rep)->body->value.error;
+        OSSL_CMP_PKISI *si = emc->pKIStatusInfo;
+        char buf[OSSL_CMP_PKISI_BUFLEN];
+
+        if (save_statusInfo(ctx, si)
+                && OSSL_CMP_CTX_snprint_PKIStatus(ctx, buf, sizeof(buf)) != NULL)
+            ERR_add_error_data(1, buf);
+        if (emc->errorCode != NULL
+                && BIO_snprintf(buf, sizeof(buf), "; errorCode: %ld",
+                                ASN1_INTEGER_get(emc->errorCode)) > 0)
+            ERR_add_error_data(1, buf);
+        if (emc->errorDetails != NULL) {
+            char *text = sk_ASN1_UTF8STRING2text(emc->errorDetails, ", ",
+                                                 OSSL_CMP_PKISI_BUFLEN - 1);
+
+            if (text != NULL)
+                ERR_add_error_data(2, "; errorDetails: ", text);
+            OPENSSL_free(text);
+        }
+        if (ctx->status != OSSL_CMP_PKISTATUS_rejection) {
+            CMPerr(0, CMP_R_UNEXPECTED_PKISTATUS);
+            if (ctx->status == OSSL_CMP_PKISTATUS_waiting)
+                ctx->status = OSSL_CMP_PKISTATUS_rejection;
+        }
+    }
+    return 0;
+}
+
+/*-
+ * When a 'waiting' PKIStatus has been received, this function is used to
+ * poll, which should yield a pollRep or finally a CertRepMessage in ip/cp/kup.
+ * On receiving a pollRep, which includes a checkAfter value, it return this
+ * value if sleep == 0, else it sleeps as long as indicated and retries.
+ *
+ * A transaction timeout is enabled if ctx->total_timeout is > 0.
+ * In this case polling will continue until the timeout is reached and then
+ * polling is done a last time even if this is before the "checkAfter" time.
+ *
+ * Returns -1 on receiving pollRep if sleep == 0, setting the checkAfter value.
+ * Returns 1 on success and provides the received PKIMESSAGE in *rep.
+ *           In this case the caller is responsible for freeing *rep.
+ * Returns 0 on error (which includes the case that timeout has been reached).
+ */
+static int poll_for_response(OSSL_CMP_CTX *ctx, int sleep, int rid,
+                             OSSL_CMP_MSG **rep, int *checkAfter)
+{
+    OSSL_CMP_MSG *preq = NULL;
+    OSSL_CMP_MSG *prep = NULL;
+
+    ossl_cmp_info(ctx,
+                  "received 'waiting' PKIStatus, starting to poll for response");
+    *rep = NULL;
+    for (;;) {
+        /* TODO: handle potentially multiple poll requests per message */
+        if ((preq = ossl_cmp_pollReq_new(ctx, rid)) == NULL)
+            goto err;
+
+        if (!send_receive_check(ctx, preq, &prep, OSSL_CMP_PKIBODY_POLLREP))
+            goto err;
+
+        /* handle potential pollRep */
+        if (ossl_cmp_msg_get_bodytype(prep) == OSSL_CMP_PKIBODY_POLLREP) {
+            OSSL_CMP_POLLREPCONTENT *prc = prep->body->value.pollRep;
+            OSSL_CMP_POLLREP *pollRep = NULL;
+            int64_t check_after;
+            char str[OSSL_CMP_PKISI_BUFLEN];
+            int len;
+
+            /* TODO: handle potentially multiple elements in pollRep */
+            if (sk_OSSL_CMP_POLLREP_num(prc) > 1) {
+                CMPerr(0, CMP_R_MULTIPLE_RESPONSES_NOT_SUPPORTED);
+                goto err;
+            }
+            pollRep = ossl_cmp_pollrepcontent_get0_pollrep(prc, rid);
+            if (pollRep == NULL)
+                goto err;
+
+            if (!ASN1_INTEGER_get_int64(&check_after, pollRep->checkAfter)) {
+                CMPerr(0, CMP_R_BAD_CHECKAFTER_IN_POLLREP);
+                goto err;
+            }
+            if (check_after < 0 || (uint64_t)check_after
+                > (sleep ? ULONG_MAX / 1000 : INT_MAX)) {
+                CMPerr(0, CMP_R_CHECKAFTER_OUT_OF_RANGE);
+                if (BIO_snprintf(str, OSSL_CMP_PKISI_BUFLEN, "value = %ld",
+                                 check_after) >= 0)
+                    ERR_add_error_data(1, str);
+                goto err;
+            }
+            if (ctx->total_timeout > 0) { /* timeout is not infinite */
+                const int exp = 5; /* expected max time per msg round trip */
+                int64_t time_left = (int64_t)(ctx->end_time - exp - time(NULL));
+
+                if (time_left <= 0) {
+                    CMPerr(0, CMP_R_TOTAL_TIMEOUT);
+                    goto err;
+                }
+                if (time_left < check_after)
+                    check_after = time_left;
+                /* poll one last time just when timeout was reached */
+            }
+
+            if (pollRep->reason == NULL
+                    || (len = BIO_snprintf(str, OSSL_CMP_PKISI_BUFLEN,
+                                           " with reason = '")) < 0) {
+                *str = '\0';
+            } else {
+                char *text = sk_ASN1_UTF8STRING2text(pollRep->reason, ", ",
+                                                     sizeof(str) - len - 2);
+
+                if (text == NULL
+                        || BIO_snprintf(str + len, sizeof(str) - len,
+                                        "%s'", text) < 0)
+                    *str = '\0';
+                OPENSSL_free(text);
+            }
+            ossl_cmp_log2(INFO, ctx,
+                          "received polling response%s; checkAfter = %ld seconds",
+                          str, check_after);
+
+            OSSL_CMP_MSG_free(preq);
+            preq = NULL;
+            OSSL_CMP_MSG_free(prep);
+            prep = NULL;
+            if (sleep) {
+                ossl_sleep((unsigned long)(1000 * check_after));
+            } else {
+                if (checkAfter != NULL)
+                    *checkAfter = (int)check_after;
+                return -1; /* exits the loop */
+            }
+        } else {
+            ossl_cmp_info(ctx, "received ip/cp/kup after polling");
+            /* any other body type has been rejected by send_receive_check() */
+            break;
+        }
+    }
+    if (prep == NULL)
+        goto err;
+
+    OSSL_CMP_MSG_free(preq);
+    *rep = prep;
+
+    return 1;
+ err:
+    OSSL_CMP_MSG_free(preq);
+    OSSL_CMP_MSG_free(prep);
+    return 0;
+}
+
+/* Send certConf for IR, CR or KUR sequences and check response */
+int ossl_cmp_exchange_certConf(OSSL_CMP_CTX *ctx, int fail_info,
+                               const char *txt)
+{
+    OSSL_CMP_MSG *certConf;
+    OSSL_CMP_MSG *PKIconf = NULL;
+    int res = 0;
+
+    /* OSSL_CMP_certConf_new() also checks if all necessary options are set */
+    if ((certConf = ossl_cmp_certConf_new(ctx, fail_info, txt)) == NULL)
+        goto err;
+
+    res = send_receive_check(ctx, certConf, &PKIconf, OSSL_CMP_PKIBODY_PKICONF);
+
+ err:
+    OSSL_CMP_MSG_free(certConf);
+    OSSL_CMP_MSG_free(PKIconf);
+    return res;
+}
+
+/* Send given error and check response */
+int ossl_cmp_exchange_error(OSSL_CMP_CTX *ctx, int status, int fail_info,
+                            const char *txt, int errorCode, const char *details)
+{
+    OSSL_CMP_MSG *error = NULL;
+    OSSL_CMP_PKISI *si = NULL;
+    OSSL_CMP_MSG *PKIconf = NULL;
+    int res = 0;
+
+    if ((si = OSSL_CMP_STATUSINFO_new(status, fail_info, txt)) == NULL)
+        goto err;
+    /* ossl_cmp_error_new() also checks if all necessary options are set */
+    if ((error = ossl_cmp_error_new(ctx, si, errorCode, details, 0)) == NULL)
+        goto err;
+
+    res = send_receive_check(ctx, error, &PKIconf, OSSL_CMP_PKIBODY_PKICONF);
+
+ err:
+    OSSL_CMP_MSG_free(error);
+    OSSL_CMP_PKISI_free(si);
+    OSSL_CMP_MSG_free(PKIconf);
+    return res;
+}
+
+/*-
+ * Retrieve a copy of the certificate, if any, from the given CertResponse.
+ * Take into account PKIStatusInfo of CertResponse in ctx, report it on error.
+ * Returns NULL if not found or on error.
+ */
+static X509 *get1_cert_status(OSSL_CMP_CTX *ctx, int bodytype,
+                              OSSL_CMP_CERTRESPONSE *crep)
+{
+    char buf[OSSL_CMP_PKISI_BUFLEN];
+    X509 *crt = NULL;
+    EVP_PKEY *privkey;
+
+    if (!ossl_assert(ctx != NULL && crep != NULL))
+        return NULL;
+
+    privkey = OSSL_CMP_CTX_get0_newPkey(ctx, 1);
+    switch (ossl_cmp_pkisi_get_status(crep->status)) {
+    case OSSL_CMP_PKISTATUS_waiting:
+        ossl_cmp_err(ctx,
+                     "received \"waiting\" status for cert when actually aiming to extract cert");
+        CMPerr(0, CMP_R_ENCOUNTERED_WAITING);
+        goto err;
+    case OSSL_CMP_PKISTATUS_grantedWithMods:
+        ossl_cmp_warn(ctx, "received \"grantedWithMods\" for certificate");
+        crt = ossl_cmp_certresponse_get1_certificate(privkey, crep);
+        break;
+    case OSSL_CMP_PKISTATUS_accepted:
+        crt = ossl_cmp_certresponse_get1_certificate(privkey, crep);
+        break;
+        /* get all information in case of a rejection before going to error */
+    case OSSL_CMP_PKISTATUS_rejection:
+        ossl_cmp_err(ctx, "received \"rejection\" status rather than cert");
+        CMPerr(0, CMP_R_REQUEST_REJECTED_BY_SERVER);
+        goto err;
+    case OSSL_CMP_PKISTATUS_revocationWarning:
+        ossl_cmp_warn(ctx,
+                      "received \"revocationWarning\" - a revocation of the cert is imminent");
+        crt = ossl_cmp_certresponse_get1_certificate(privkey, crep);
+        break;
+    case OSSL_CMP_PKISTATUS_revocationNotification:
+        ossl_cmp_warn(ctx,
+                      "received \"revocationNotification\" - a revocation of the cert has occurred");
+        crt = ossl_cmp_certresponse_get1_certificate(privkey, crep);
+        break;
+    case OSSL_CMP_PKISTATUS_keyUpdateWarning:
+        if (bodytype != OSSL_CMP_PKIBODY_KUR) {
+            CMPerr(0, CMP_R_ENCOUNTERED_KEYUPDATEWARNING);
+            goto err;
+        }
+        crt = ossl_cmp_certresponse_get1_certificate(privkey, crep);
+        break;
+    default:
+        ossl_cmp_log1(ERROR, ctx,
+                      "received unsupported PKIStatus %d for certificate",
+                      ctx->status);
+        CMPerr(0, CMP_R_UNKNOWN_PKISTATUS);
+        goto err;
+    }
+    if (crt == NULL) /* according to PKIStatus, we can expect a cert */
+        CMPerr(0, CMP_R_CERTIFICATE_NOT_FOUND);
+
+    return crt;
+
+ err:
+    if (OSSL_CMP_CTX_snprint_PKIStatus(ctx, buf, sizeof(buf)) != NULL)
+        ERR_add_error_data(1, buf);
+    return NULL;
+}
+
+/*-
+ * Callback fn validating that the new certificate can be verified, using
+ * ctx->certConf_cb_arg, which has been initialized using opt_out_trusted, and
+ * ctx->untrusted_certs, which at this point already contains ctx->extraCertsIn.
+ * Returns 0 on acceptance, else a bit field reflecting PKIFailureInfo.
+ * Quoting from RFC 4210 section 5.1. Overall PKI Message:
+ *     The extraCerts field can contain certificates that may be useful to
+ *     the recipient.  For example, this can be used by a CA or RA to
+ *     present an end entity with certificates that it needs to verify its
+ *     own new certificate (if, for example, the CA that issued the end
+ *     entity's certificate is not a root CA for the end entity).  Note that
+ *     this field does not necessarily contain a certification path; the
+ *     recipient may have to sort, select from, or otherwise process the
+ *     extra certificates in order to use them.
+ * Note: While often handy, there is no hard requirement by CMP that
+ * an EE must be able to validate the certificates it gets enrolled.
+ */
+int OSSL_CMP_certConf_cb(OSSL_CMP_CTX *ctx, X509 *cert, int fail_info,
+                         const char **text)
+{
+    X509_STORE *out_trusted = OSSL_CMP_CTX_get_certConf_cb_arg(ctx);
+    (void)text; /* make (artificial) use of var to prevent compiler warning */
+
+    if (fail_info != 0) /* accept any error flagged by CMP core library */
+        return fail_info;
+
+    if (out_trusted != NULL
+            && !OSSL_CMP_validate_cert_path(ctx, out_trusted, cert))
+        fail_info = 1 << OSSL_CMP_PKIFAILUREINFO_incorrectData;
+
+    return fail_info;
+}
+
+/*-
+ * Perform the generic handling of certificate responses for IR/CR/KUR/P10CR.
+ * Returns -1 on receiving pollRep if sleep == 0, setting the checkAfter value.
+ * Returns 1 on success and provides the received PKIMESSAGE in *resp.
+ * Returns 0 on error (which includes the case that timeout has been reached).
+ * Regardless of success, caller is responsible for freeing *resp (unless NULL).
+ */
+static int cert_response(OSSL_CMP_CTX *ctx, int sleep, int rid,
+                         OSSL_CMP_MSG **resp, int *checkAfter,
+                         int req_type, int expected_type)
+{
+    EVP_PKEY *rkey = OSSL_CMP_CTX_get0_newPkey(ctx /* may be NULL */, 0);
+    int fail_info = 0; /* no failure */
+    const char *txt = NULL;
+    OSSL_CMP_CERTREPMESSAGE *crepmsg;
+    OSSL_CMP_CERTRESPONSE *crep;
+    X509 *cert;
+    char *subj = NULL;
+    int ret = 1;
+
+ retry:
+    crepmsg = (*resp)->body->value.ip; /* same for cp and kup */
+    if (sk_OSSL_CMP_CERTRESPONSE_num(crepmsg->response) > 1) {
+        CMPerr(0, CMP_R_MULTIPLE_RESPONSES_NOT_SUPPORTED);
+        return 0;
+    }
+    /* TODO: handle potentially multiple CertResponses in CertRepMsg */
+    crep = ossl_cmp_certrepmessage_get0_certresponse(crepmsg, rid);
+    if (crep == NULL)
+        return 0;
+    if (!save_statusInfo(ctx, crep->status))
+        return 0;
+    if (rid == -1) {
+        /* for OSSL_CMP_PKIBODY_P10CR learn CertReqId from response */
+        rid = ossl_cmp_asn1_get_int(crep->certReqId);
+        if (rid == -1) {
+            CMPerr(0, CMP_R_BAD_REQUEST_ID);
+            return 0;
+        }
+    }
+
+    if (ossl_cmp_pkisi_get_status(crep->status) == OSSL_CMP_PKISTATUS_waiting) {
+        OSSL_CMP_MSG_free(*resp);
+        *resp = NULL;
+        if ((ret = poll_for_response(ctx, sleep, rid, resp, checkAfter)) != 0) {
+            if (ret == -1) /* at this point implies sleep == 0 */
+                return ret; /* waiting */
+            goto retry; /* got ip/cp/kup, which may still indicate 'waiting' */
+        } else {
+            CMPerr(0, CMP_R_POLLING_FAILED);
+            return 0;
+        }
+    }
+
+    cert = get1_cert_status(ctx, (*resp)->body->type, crep);
+    if (cert == NULL) {
+        ERR_add_error_data(1, "; cannot extract certificate from response");
+        return 0;
+    }
+    if (!ossl_cmp_ctx_set0_newCert(ctx, cert))
+        return 0;
+
+    /*
+     * if the CMP server returned certificates in the caPubs field, copy them
+     * to the context so that they can be retrieved if necessary
+     */
+    if (crepmsg->caPubs != NULL
+            && !ossl_cmp_ctx_set1_caPubs(ctx, crepmsg->caPubs))
+        return 0;
+
+    /* copy received extraCerts to ctx->extraCertsIn so they can be retrieved */
+    if (!ossl_cmp_ctx_set1_extraCertsIn(ctx, (*resp)->extraCerts))
+        return 0;
+
+    subj = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
+    if (rkey != NULL
+        /* X509_check_private_key() also works if rkey is just public key */
+            && !(X509_check_private_key(ctx->newCert, rkey))) {
+        fail_info = 1 << OSSL_CMP_PKIFAILUREINFO_incorrectData;
+        txt = "public key in new certificate does not match our enrollment key";
+        /*-
+         * not callling (void)ossl_cmp_exchange_error(ctx,
+         *                    OSSL_CMP_PKISTATUS_rejection, fail_info, txt)
+         * not throwing CMP_R_CERTIFICATE_NOT_ACCEPTED with txt
+         * not returning 0
+         * since we better leave this for any ctx->certConf_cb to decide
+         */
+    }
+
+    /*
+     * Execute the certification checking callback function possibly set in ctx,
+     * which can determine whether to accept a newly enrolled certificate.
+     * It may overrule the pre-decision reflected in 'fail_info' and '*txt'.
+     */
+    if (ctx->certConf_cb
+            && (fail_info = ctx->certConf_cb(ctx, ctx->newCert,
+                                             fail_info, &txt)) != 0) {
+        if (txt == NULL)
+            txt = "CMP client application did not accept it";
+    }
+    if (fail_info != 0) /* immediately log error before any certConf exchange */
+        ossl_cmp_log1(ERROR, ctx,
+                      "rejecting newly enrolled cert with subject: %s", subj);
+
+    /*
+     * TODO: better move certConf exchange to do_certreq_seq() such that
+     * also more low-level errors with CertReqMessages get reported to server
+     */
+    if (!ctx->disableConfirm
+            && !ossl_cmp_hdr_has_implicitConfirm((*resp)->header)) {
+        if (!ossl_cmp_exchange_certConf(ctx, fail_info, txt))
+            ret = 0;
+    }
+
+    /* not throwing failure earlier as transfer_cb may call ERR_clear_error() */
+    if (fail_info != 0) {
+        CMPerr(0, CMP_R_CERTIFICATE_NOT_ACCEPTED);
+        ERR_add_error_data(2, "rejecting newly enrolled cert with subject: ",
+                           subj);
+        if (txt != NULL)
+            ERR_add_error_txt("; ", txt);
+        ret = 0;
+    }
+    OPENSSL_free(subj);
+    return ret;
+}
+
+int OSSL_CMP_try_certreq(OSSL_CMP_CTX *ctx, int req_type, int *checkAfter)
+{
+    OSSL_CMP_MSG *req = NULL;
+    OSSL_CMP_MSG *rep = NULL;
+    int is_p10 = req_type == OSSL_CMP_PKIBODY_P10CR;
+    int rid = is_p10 ? -1 : OSSL_CMP_CERTREQID;
+    int rep_type = is_p10 ? OSSL_CMP_PKIBODY_CP : req_type + 1;
+    int res = 0;
+
+    if (ctx == NULL) {
+        CMPerr(0, CMP_R_NULL_ARGUMENT);
+        return 0;
+    }
+
+    if (ctx->status != OSSL_CMP_PKISTATUS_waiting) { /* not polling already */
+        ctx->status = -1;
+        if (!ossl_cmp_ctx_set0_newCert(ctx, NULL))
+            return 0;
+
+        if (ctx->total_timeout > 0) /* else ctx->end_time is not used */
+            ctx->end_time = time(NULL) + ctx->total_timeout;
+
+        req = ossl_cmp_certReq_new(ctx, req_type, 0 /* req_err */);
+        if (req == NULL) /* also checks if all necessary options are set */
+            return 0;
+
+        if (!send_receive_check(ctx, req, &rep, rep_type))
+            goto err;
+    } else {
+        if (req_type < 0)
+            return ossl_cmp_exchange_error(ctx, OSSL_CMP_PKISTATUS_rejection,
+                                           0 /* TODO better fail_info value? */,
+                                           "polling aborted", 0 /* errorCode */,
+                                           "by application");
+        res = poll_for_response(ctx, 0 /* no sleep */, rid, &rep, checkAfter);
+        if (res <= 0) /* waiting or error */
+            return res;
+    }
+    res = cert_response(ctx, 0 /* no sleep */, rid, &rep, checkAfter,
+                        req_type, rep_type);
+
+ err:
+    OSSL_CMP_MSG_free(req);
+    OSSL_CMP_MSG_free(rep);
+    return res;
+}
+
+/*-
+ * Do the full sequence CR/IR/KUR/P10CR, CP/IP/KUP/CP,
+ * certConf, PKIconf, and polling if required.
+ * Will sleep as long as indicated by the server (according to checkAfter).
+ * All enrollment options need to be present in the context.
+ * TODO: another function to request two certificates at once should be created.
+ * Returns pointer to received certificate, or NULL if none was received.
+ */
+static X509 *do_certreq_seq(OSSL_CMP_CTX *ctx, int req_type, int req_err,
+                            int rep_type)
+{
+    OSSL_CMP_MSG *req = NULL;
+    OSSL_CMP_MSG *rep = NULL;
+    int rid = (req_type == OSSL_CMP_PKIBODY_P10CR) ? -1 : OSSL_CMP_CERTREQID;
+    X509 *result = NULL;
+
+    if (ctx == NULL) {
+        CMPerr(0, CMP_R_NULL_ARGUMENT);
+        return NULL;
+    }
+    ctx->status = -1;
+    if (!ossl_cmp_ctx_set0_newCert(ctx, NULL))
+        return NULL;
+
+    if (ctx->total_timeout > 0) /* else ctx->end_time is not used */
+        ctx->end_time = time(NULL) + ctx->total_timeout;
+
+    /* OSSL_CMP_certreq_new() also checks if all necessary options are set */
+    if ((req = ossl_cmp_certReq_new(ctx, req_type, req_err)) == NULL)
+        goto err;
+
+    if (!send_receive_check(ctx, req, &rep, rep_type))
+        goto err;
+
+    if (cert_response(ctx, 1 /* sleep */, rid, &rep, NULL, req_type, rep_type)
+        <= 0)
+        goto err;
+
+    result = ctx->newCert;
+ err:
+    OSSL_CMP_MSG_free(req);
+    OSSL_CMP_MSG_free(rep);
+    return result;
+}
+
+X509 *OSSL_CMP_exec_IR_ses(OSSL_CMP_CTX *ctx)
+{
+    return do_certreq_seq(ctx, OSSL_CMP_PKIBODY_IR,
+                          CMP_R_ERROR_CREATING_IR, OSSL_CMP_PKIBODY_IP);
+}
+
+X509 *OSSL_CMP_exec_CR_ses(OSSL_CMP_CTX *ctx)
+{
+    return do_certreq_seq(ctx, OSSL_CMP_PKIBODY_CR,
+                          CMP_R_ERROR_CREATING_CR, OSSL_CMP_PKIBODY_CP);
+}
+
+X509 *OSSL_CMP_exec_KUR_ses(OSSL_CMP_CTX *ctx)
+{
+    return do_certreq_seq(ctx, OSSL_CMP_PKIBODY_KUR,
+                          CMP_R_ERROR_CREATING_KUR, OSSL_CMP_PKIBODY_KUP);
+}
+
+X509 *OSSL_CMP_exec_P10CR_ses(OSSL_CMP_CTX *ctx)
+{
+    return do_certreq_seq(ctx, OSSL_CMP_PKIBODY_P10CR,
+                          CMP_R_ERROR_CREATING_P10CR, OSSL_CMP_PKIBODY_CP);
+}
+
+X509 *OSSL_CMP_exec_RR_ses(OSSL_CMP_CTX *ctx)
+{
+    OSSL_CMP_MSG *rr = NULL;
+    OSSL_CMP_MSG *rp = NULL;
+    const int num_RevDetails = 1;
+    const int rsid = OSSL_CMP_REVREQSID;
+    OSSL_CMP_REVREPCONTENT *rrep = NULL;
+    OSSL_CMP_PKISI *si = NULL;
+    char buf[OSSL_CMP_PKISI_BUFLEN];
+    X509 *result = NULL;
+
+    if (ctx == NULL) {
+        CMPerr(0, CMP_R_INVALID_ARGS);
+        return 0;
+    }
+    ctx->status = -1;
+
+    /* OSSL_CMP_rr_new() also checks if all necessary options are set */
+    if ((rr = ossl_cmp_rr_new(ctx)) == NULL)
+        goto end;
+
+    if (!send_receive_check(ctx, rr, &rp, OSSL_CMP_PKIBODY_RP))
+        goto end;
+
+    rrep = rp->body->value.rp;
+    if (sk_OSSL_CMP_PKISI_num(rrep->status) != num_RevDetails) {
+        CMPerr(0, CMP_R_WRONG_RP_COMPONENT_COUNT);
+        goto end;
+    }
+
+    /* evaluate PKIStatus field */
+    si = ossl_cmp_revrepcontent_get_pkisi(rrep, rsid);
+    if (!save_statusInfo(ctx, si))
+        goto err;
+    switch (ossl_cmp_pkisi_get_status(si)) {
+    case OSSL_CMP_PKISTATUS_accepted:
+        ossl_cmp_info(ctx, "revocation accepted (PKIStatus=accepted)");
+        result = ctx->oldCert;
+        break;
+    case OSSL_CMP_PKISTATUS_grantedWithMods:
+        ossl_cmp_info(ctx, "revocation accepted (PKIStatus=grantedWithMods)");
+        result = ctx->oldCert;
+        break;
+    case OSSL_CMP_PKISTATUS_rejection:
+        CMPerr(0, CMP_R_REQUEST_REJECTED_BY_SERVER);
+        goto err;
+    case OSSL_CMP_PKISTATUS_revocationWarning:
+        ossl_cmp_info(ctx, "revocation accepted (PKIStatus=revocationWarning)");
+        result = ctx->oldCert;
+        break;
+    case OSSL_CMP_PKISTATUS_revocationNotification:
+        /* interpretation as warning or error depends on CA */
+        ossl_cmp_warn(ctx,
+                      "revocation accepted (PKIStatus=revocationNotification)");
+        result = ctx->oldCert;
+        break;
+    case OSSL_CMP_PKISTATUS_waiting:
+    case OSSL_CMP_PKISTATUS_keyUpdateWarning:
+        CMPerr(0, CMP_R_UNEXPECTED_PKISTATUS);
+        goto err;
+    default:
+        CMPerr(0, CMP_R_UNKNOWN_PKISTATUS);
+        goto err;
+    }
+
+    /* check any present CertId in optional revCerts field */
+    if (rrep->revCerts != NULL) {
+        OSSL_CRMF_CERTID *cid;
+        OSSL_CRMF_CERTTEMPLATE *tmpl =
+            sk_OSSL_CMP_REVDETAILS_value(rr->body->value.rr, rsid)->certDetails;
+        const X509_NAME *issuer = OSSL_CRMF_CERTTEMPLATE_get0_issuer(tmpl);
+        ASN1_INTEGER *serial = OSSL_CRMF_CERTTEMPLATE_get0_serialNumber(tmpl);
+
+        if (sk_OSSL_CRMF_CERTID_num(rrep->revCerts) != num_RevDetails) {
+            CMPerr(0, CMP_R_WRONG_RP_COMPONENT_COUNT);
+            result = NULL;
+            goto err;
+        }
+        if ((cid = ossl_cmp_revrepcontent_get_CertId(rrep, rsid)) == NULL) {
+            result = NULL;
+            goto err;
+        }
+        if (X509_NAME_cmp(issuer, OSSL_CRMF_CERTID_get0_issuer(cid)) != 0) {
+            CMPerr(0, CMP_R_WRONG_CERTID_IN_RP);
+            result = NULL;
+            goto err;
+        }
+        if (ASN1_INTEGER_cmp(serial,
+                             OSSL_CRMF_CERTID_get0_serialNumber(cid)) != 0) {
+            CMPerr(0, CMP_R_WRONG_SERIAL_IN_RP);
+            result = NULL;
+            goto err;
+        }
+    }
+
+    /* check number of any optionally present crls */
+    if (rrep->crls != NULL && sk_X509_CRL_num(rrep->crls) != num_RevDetails) {
+        CMPerr(0, CMP_R_WRONG_RP_COMPONENT_COUNT);
+        result = NULL;
+        goto err;
+    }
+
+ err:
+    if (result == NULL
+            && OSSL_CMP_CTX_snprint_PKIStatus(ctx, buf, sizeof(buf)) != NULL)
+        ERR_add_error_data(1, buf);
+
+ end:
+    OSSL_CMP_MSG_free(rr);
+    OSSL_CMP_MSG_free(rp);
+    return result;
+}
+
+STACK_OF(OSSL_CMP_ITAV) *OSSL_CMP_exec_GENM_ses(OSSL_CMP_CTX *ctx)
+{
+    OSSL_CMP_MSG *genm;
+    OSSL_CMP_MSG *genp = NULL;
+    STACK_OF(OSSL_CMP_ITAV) *rcvd_itavs = NULL;
+
+    if (ctx == NULL) {
+        CMPerr(0, CMP_R_INVALID_ARGS);
+        return 0;
+    }
+
+    if ((genm = ossl_cmp_genm_new(ctx)) == NULL)
+        goto err;
+
+    if (!send_receive_check(ctx, genm, &genp, OSSL_CMP_PKIBODY_GENP))
+        goto err;
+
+    /* received stack of itavs not to be freed with the genp */
+    rcvd_itavs = genp->body->value.genp;
+    genp->body->value.genp = NULL;
+
+ err:
+    OSSL_CMP_MSG_free(genm);
+    OSSL_CMP_MSG_free(genp);
+
+    return rcvd_itavs; /* recv_itavs == NULL indicates an error */
+}
diff --git a/crypto/cmp/cmp_ctx.c b/crypto/cmp/cmp_ctx.c
index 714157a964..873d69320e 100644
--- a/crypto/cmp/cmp_ctx.c
+++ b/crypto/cmp/cmp_ctx.c
@@ -103,7 +103,7 @@ OSSL_CMP_CTX *OSSL_CMP_CTX_new(void)
 
     ctx->serverPort = OSSL_CMP_DEFAULT_PORT;
     ctx->proxyPort = OSSL_CMP_DEFAULT_PORT;
-    ctx->msgtimeout = 2 * 60;
+    ctx->msg_timeout = 2 * 60;
 
     if ((ctx->untrusted_certs = sk_X509_new_null()) == NULL)
         goto err;
@@ -257,7 +257,7 @@ int ossl_cmp_ctx_set0_validatedSrvCert(OSSL_CMP_CTX *ctx, X509 *cert)
  * it be rejected.
  * Returns 1 on success, 0 on error
  */
-int OSSL_CMP_CTX_set_certConf_cb(OSSL_CMP_CTX *ctx, OSSL_cmp_certConf_cb_t cb)
+int OSSL_CMP_CTX_set_certConf_cb(OSSL_CMP_CTX *ctx, OSSL_CMP_certConf_cb_t cb)
 {
     if (ctx == NULL) {
         CMPerr(0, CMP_R_NULL_ARGUMENT);
@@ -384,7 +384,7 @@ int ossl_cmp_print_log(OSSL_CMP_severity level, const OSSL_CMP_CTX *ctx,
  * Set a callback function for error reporting and logging messages.
  * Returns 1 on success, 0 on error
  */
-int OSSL_CMP_CTX_set_log_cb(OSSL_CMP_CTX *ctx, OSSL_cmp_log_cb_t cb)
+int OSSL_CMP_CTX_set_log_cb(OSSL_CMP_CTX *ctx, OSSL_CMP_log_cb_t cb)
 {
     if (ctx == NULL) {
         CMPerr(0, CMP_R_NULL_ARGUMENT);
@@ -911,7 +911,7 @@ void *OSSL_CMP_CTX_get_http_cb_arg(const OSSL_CMP_CTX *ctx)
  * Set callback function for sending CMP request and receiving response.
  * Returns 1 on success, 0 on error
  */
-int OSSL_CMP_CTX_set_transfer_cb(OSSL_CMP_CTX *ctx, OSSL_cmp_transfer_cb_t cb)
+int OSSL_CMP_CTX_set_transfer_cb(OSSL_CMP_CTX *ctx, OSSL_CMP_transfer_cb_t cb)
 {
     if (ctx == NULL) {
         CMPerr(0, CMP_R_NULL_ARGUMENT);
@@ -1010,7 +1010,7 @@ int OSSL_CMP_CTX_set_option(OSSL_CMP_CTX *ctx, int opt, int val)
     case OSSL_CMP_OPT_REVOCATION_REASON:
         min_val = OCSP_REVOKED_STATUS_NOSTATUS;
         break;
-    case OSSL_CMP_OPT_POPOMETHOD:
+    case OSSL_CMP_OPT_POPO_METHOD:
         min_val = OSSL_CRMF_POPO_NONE;
         break;
     default:
@@ -1030,10 +1030,10 @@ int OSSL_CMP_CTX_set_option(OSSL_CMP_CTX *ctx, int opt, int val)
         }
         ctx->log_verbosity = val;
         break;
-    case OSSL_CMP_OPT_IMPLICITCONFIRM:
+    case OSSL_CMP_OPT_IMPLICIT_CONFIRM:
         ctx->implicitConfirm = val;
         break;
-    case OSSL_CMP_OPT_DISABLECONFIRM:
+    case OSSL_CMP_OPT_DISABLE_CONFIRM:
         ctx->disableConfirm = val;
         break;
     case OSSL_CMP_OPT_UNPROTECTED_SEND:
@@ -1042,7 +1042,7 @@ int OSSL_CMP_CTX_set_option(OSSL_CMP_CTX *ctx, int opt, int val)
     case OSSL_CMP_OPT_UNPROTECTED_ERRORS:
         ctx->unprotectedErrors = val;
         break;
-    case OSSL_CMP_OPT_VALIDITYDAYS:
+    case OSSL_CMP_OPT_VALIDITY_DAYS:
         ctx->days = val;
         break;
     case OSSL_CMP_OPT_SUBJECTALTNAME_NODEFAULT:
@@ -1057,7 +1057,7 @@ int OSSL_CMP_CTX_set_option(OSSL_CMP_CTX *ctx, int opt, int val)
     case OSSL_CMP_OPT_IGNORE_KEYUSAGE:
         ctx->ignore_keyusage = val;
         break;
-    case OSSL_CMP_OPT_POPOMETHOD:
+    case OSSL_CMP_OPT_POPO_METHOD:
         if (val > OSSL_CRMF_POPO_KEYAGREE) {
             CMPerr(0, CMP_R_INVALID_ARGS);
             return 0;
@@ -1073,11 +1073,11 @@ int OSSL_CMP_CTX_set_option(OSSL_CMP_CTX *ctx, int opt, int val)
     case OSSL_CMP_OPT_MAC_ALGNID:
         ctx->pbm_mac = val;
         break;
-    case OSSL_CMP_OPT_MSGTIMEOUT:
-        ctx->msgtimeout = val;
+    case OSSL_CMP_OPT_MSG_TIMEOUT:
+        ctx->msg_timeout = val;
         break;
-    case OSSL_CMP_OPT_TOTALTIMEOUT:
-        ctx->totaltimeout = val;
+    case OSSL_CMP_OPT_TOTAL_TIMEOUT:
+        ctx->total_timeout = val;
         break;
     case OSSL_CMP_OPT_PERMIT_TA_IN_EXTRACERTS_FOR_IR:
         ctx->permitTAInExtraCertsForIR = val;
@@ -1111,15 +1111,15 @@ int OSSL_CMP_CTX_get_option(const OSSL_CMP_CTX *ctx, int opt)
     switch (opt) {
     case OSSL_CMP_OPT_LOG_VERBOSITY:
         return ctx->log_verbosity;
-    case OSSL_CMP_OPT_IMPLICITCONFIRM:
+    case OSSL_CMP_OPT_IMPLICIT_CONFIRM:
         return ctx->implicitConfirm;
-    case OSSL_CMP_OPT_DISABLECONFIRM:
+    case OSSL_CMP_OPT_DISABLE_CONFIRM:
         return ctx->disableConfirm;
     case OSSL_CMP_OPT_UNPROTECTED_SEND:
         return ctx->unprotectedSend;
     case OSSL_CMP_OPT_UNPROTECTED_ERRORS:
         return ctx->unprotectedErrors;
-    case OSSL_CMP_OPT_VALIDITYDAYS:
+    case OSSL_CMP_OPT_VALIDITY_DAYS:
         return ctx->days;
     case OSSL_CMP_OPT_SUBJECTALTNAME_NODEFAULT:
         return ctx->SubjectAltName_nodefault;
@@ -1129,7 +1129,7 @@ int OSSL_CMP_CTX_get_option(const OSSL_CMP_CTX *ctx, int opt)
         return ctx->setPoliciesCritical;
     case OSSL_CMP_OPT_IGNORE_KEYUSAGE:
         return ctx->ignore_keyusage;
-    case OSSL_CMP_OPT_POPOMETHOD:
+    case OSSL_CMP_OPT_POPO_METHOD:
         return ctx->popoMethod;
     case OSSL_CMP_OPT_DIGEST_ALGNID:
         return ctx->digest;
@@ -1137,10 +1137,10 @@ int OSSL_CMP_CTX_get_option(const OSSL_CMP_CTX *ctx, int opt)
         return ctx->pbm_owf;
     case OSSL_CMP_OPT_MAC_ALGNID:
         return ctx->pbm_mac;
-    case OSSL_CMP_OPT_MSGTIMEOUT:
-        return ctx->msgtimeout;
-    case OSSL_CMP_OPT_TOTALTIMEOUT:
-        return ctx->totaltimeout;
+    case OSSL_CMP_OPT_MSG_TIMEOUT:
+        return ctx->msg_timeout;
+    case OSSL_CMP_OPT_TOTAL_TIMEOUT:
+        return ctx->total_timeout;
     case OSSL_CMP_OPT_PERMIT_TA_IN_EXTRACERTS_FOR_IR:
         return ctx->permitTAInExtraCertsForIR;
     case OSSL_CMP_OPT_REVOCATION_REASON:
diff --git a/crypto/cmp/cmp_err.c b/crypto/cmp/cmp_err.c
index 0d311a8ddf..d64d60bf1d 100644
--- a/crypto/cmp/cmp_err.c
+++ b/crypto/cmp/cmp_err.c
@@ -16,9 +16,13 @@
 static const ERR_STRING_DATA CMP_str_reasons[] = {
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ALGORITHM_NOT_SUPPORTED),
     "algorithm not supported"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_BAD_CHECKAFTER_IN_POLLREP),
+    "bad checkafter in pollrep"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_BAD_REQUEST_ID), "bad request id"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_CERTHASH_UNMATCHED), "certhash unmatched"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_CERTID_NOT_FOUND), "certid not found"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_CERTIFICATE_NOT_ACCEPTED),
+    "certificate not accepted"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_CERTIFICATE_NOT_FOUND),
     "certificate not found"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_CERTREQMSG_NOT_FOUND),
@@ -27,18 +31,27 @@ static const ERR_STRING_DATA CMP_str_reasons[] = {
     "certresponse not found"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_CERT_AND_KEY_DO_NOT_MATCH),
     "cert and key do not match"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_CHECKAFTER_OUT_OF_RANGE),
+    "checkafter out of range"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ENCOUNTERED_KEYUPDATEWARNING),
+    "encountered keyupdatewarning"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ENCOUNTERED_WAITING),
+    "encountered waiting"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_CALCULATING_PROTECTION),
     "error calculating protection"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_CREATING_CERTCONF),
     "error creating certconf"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_CREATING_CERTREP),
     "error creating certrep"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_CREATING_CR), "error creating cr"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_CREATING_ERROR),
     "error creating error"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_CREATING_GENM),
     "error creating genm"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_CREATING_GENP),
     "error creating genp"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_CREATING_IR), "error creating ir"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_CREATING_KUR), "error creating kur"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_CREATING_P10CR),
     "error creating p10cr"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_CREATING_PKICONF),
@@ -51,14 +64,14 @@ static const ERR_STRING_DATA CMP_str_reasons[] = {
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_CREATING_RR), "error creating rr"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_PARSING_PKISTATUS),
     "error parsing pkistatus"},
-    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_PROCESSING_MSG),
-    "error processing msg"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_PARSING_PKISTATUS),
+    "error parsing pkistatus"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_PROCESSING_MESSAGE),
+    "error processing message"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_PROTECTING_MESSAGE),
     "error protecting message"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_SETTING_CERTHASH),
     "error setting certhash"},
-    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_TRANSFERRING_OUT),
-    "error transferring out"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_UNEXPECTED_CERTCONF),
     "error unexpected certconf"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_VALIDATING_PROTECTION),
@@ -83,6 +96,8 @@ static const ERR_STRING_DATA CMP_str_reasons[] = {
     "missing trust store"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_MULTIPLE_REQUESTS_NOT_SUPPORTED),
     "multiple requests not supported"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_MULTIPLE_RESPONSES_NOT_SUPPORTED),
+    "multiple responses not supported"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_MULTIPLE_SAN_SOURCES),
     "multiple san sources"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_NO_STDIO), "no stdio"},
@@ -92,23 +107,33 @@ static const ERR_STRING_DATA CMP_str_reasons[] = {
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_PKIBODY_ERROR), "pkibody error"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_PKISTATUSINFO_NOT_FOUND),
     "pkistatusinfo not found"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_POLLING_FAILED), "polling failed"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_POTENTIALLY_INVALID_CERTIFICATE),
     "potentially invalid certificate"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_RECEIVED_ERROR),
+    "received error"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_RECIPNONCE_UNMATCHED),
     "recipnonce unmatched"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_REQUEST_NOT_ACCEPTED),
     "request not accepted"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_REQUEST_REJECTED_BY_SERVER),
+    "request rejected by server"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_SENDER_GENERALNAME_TYPE_NOT_SUPPORTED),
     "sender generalname type not supported"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_SRVCERT_DOES_NOT_VALIDATE_MSG),
     "srvcert does not validate msg"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_TOTAL_TIMEOUT), "total timeout"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_TRANSACTIONID_UNMATCHED),
     "transactionid unmatched"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_TRANSFER_ERROR), "transfer error"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNEXPECTED_PKIBODY), "unexpected pkibody"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNEXPECTED_PKISTATUS),
+    "unexpected pkistatus"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNEXPECTED_PVNO), "unexpected pvno"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNKNOWN_ALGORITHM_ID),
     "unknown algorithm id"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNKNOWN_CERT_TYPE), "unknown cert type"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNKNOWN_PKISTATUS), "unknown pkistatus"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNSUPPORTED_ALGORITHM),
     "unsupported algorithm"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNSUPPORTED_KEY_TYPE),
@@ -117,7 +142,11 @@ static const ERR_STRING_DATA CMP_str_reasons[] = {
     "unsupported protection alg dhbasedmac"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_WRONG_ALGORITHM_OID),
     "wrong algorithm oid"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_WRONG_CERTID_IN_RP), "wrong certid in rp"},
     {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_WRONG_PBM_VALUE), "wrong pbm value"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_WRONG_RP_COMPONENT_COUNT),
+    "wrong rp component count"},
+    {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_WRONG_SERIAL_IN_RP), "wrong serial in rp"},
     {0, NULL}
 };
 
diff --git a/crypto/cmp/cmp_local.h b/crypto/cmp/cmp_local.h
index 688ccabd7c..220e93e2ce 100644
--- a/crypto/cmp/cmp_local.h
+++ b/crypto/cmp/cmp_local.h
@@ -28,11 +28,11 @@
  * this structure is used to store the context for CMP sessions
  */
 struct ossl_cmp_ctx_st {
-    OSSL_cmp_log_cb_t log_cb; /* log callback for error/debug/etc. output */
+    OSSL_CMP_log_cb_t log_cb; /* log callback for error/debug/etc. output */
     OSSL_CMP_severity log_verbosity; /* level of verbosity of log output */
 
     /* message transfer */
-    OSSL_cmp_transfer_cb_t transfer_cb; /* default: OSSL_CMP_MSG_http_perform */
+    OSSL_CMP_transfer_cb_t transfer_cb; /* default: OSSL_CMP_MSG_http_perform */
     void *transfer_cb_arg; /* allows to store optional argument to cb */
     /* HTTP-based transfer */
     char *serverPath;
@@ -40,8 +40,8 @@ struct ossl_cmp_ctx_st {
     int serverPort;
     char *proxyName;
     int proxyPort;
-    int msgtimeout; /* max seconds to wait for each CMP message round trip */
-    int totaltimeout; /* maximum number seconds an enrollment may take, incl. */
+    int msg_timeout; /* max seconds to wait for each CMP message round trip */
+    int total_timeout; /* max number of seconds an enrollment may take, incl. */
     /* attempts polling for a response if a 'waiting' PKIStatus is received */
     time_t end_time; /* session start time + totaltimeout */
     OSSL_HTTP_bio_cb_t http_cb;
@@ -122,7 +122,7 @@ struct ossl_cmp_ctx_st {
     STACK_OF(X509) *extraCertsIn; /* extraCerts received from server */
 
     /* certificate confirmation */
-    OSSL_cmp_certConf_cb_t certConf_cb; /* callback for app checking new cert */
+    OSSL_CMP_certConf_cb_t certConf_cb; /* callback for app checking new cert */
     void *certConf_cb_arg; /* allows to store an argument individual to cb */
 } /* OSSL_CMP_CTX */;
 
@@ -912,4 +912,10 @@ int ossl_cmp_msg_check_received(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *msg,
                                 ossl_cmp_allow_unprotected_cb_t cb, int cb_arg);
 int ossl_cmp_verify_popo(const OSSL_CMP_MSG *msg, int accept_RAVerified);
 
+/* from cmp_client.c */
+int ossl_cmp_exchange_certConf(OSSL_CMP_CTX *ctx, int fail_info,
+                               const char *txt);
+int ossl_cmp_exchange_error(OSSL_CMP_CTX *ctx, int status, int fail_info,
+                            const char *txt, int errorCode, const char *details);
+
 #endif /* !defined(OSSL_CRYPTO_CMP_LOCAL_H) */
diff --git a/crypto/cmp/cmp_msg.c b/crypto/cmp/cmp_msg.c
index e4a4f1f3b3..7fc51596b9 100644
--- a/crypto/cmp/cmp_msg.c
+++ b/crypto/cmp/cmp_msg.c
@@ -348,7 +348,8 @@ OSSL_CMP_MSG *ossl_cmp_certReq_new(OSSL_CMP_CTX *ctx, int type, int err_code)
     return msg;
 
  err:
-    CMPerr(0, err_code);
+    if (err_code != 0)
+        CMPerr(0, err_code);
     OSSL_CRMF_MSG_free(crm);
     OSSL_CMP_MSG_free(msg);
     return NULL;
diff --git a/crypto/cmp/cmp_server.c b/crypto/cmp/cmp_server.c
index ce532b3110..c5c57fc8b7 100644
--- a/crypto/cmp/cmp_server.c
+++ b/crypto/cmp/cmp_server.c
@@ -215,8 +215,8 @@ static OSSL_CMP_MSG *process_cert_request(OSSL_CMP_SRV_CTX *srv_ctx,
                                            &certOut, &chainOut, &caPubs);
         if (si == NULL)
             goto err;
-        /* set OSSL_CMP_OPT_IMPLICITCONFIRM if and only if transaction ends */
-        if (!OSSL_CMP_CTX_set_option(srv_ctx->ctx, OSSL_CMP_OPT_IMPLICITCONFIRM,
+        /* set OSSL_CMP_OPT_IMPLICIT_CONFIRM if and only if transaction ends */
+        if (!OSSL_CMP_CTX_set_option(srv_ctx->ctx, OSSL_CMP_OPT_IMPLICIT_CONFIRM,
                                      ossl_cmp_hdr_has_implicitConfirm(hdr)
                                          && srv_ctx->grantImplicitConfirm
                                          /* do not set if polling starts: */
@@ -264,7 +264,7 @@ static OSSL_CMP_MSG *process_rr(OSSL_CMP_SRV_CTX *srv_ctx,
 
     if ((details = sk_OSSL_CMP_REVDETAILS_value(req->body->value.rr,
                                                 OSSL_CMP_REVREQSID)) == NULL) {
-        CMPerr(0, CMP_R_ERROR_PROCESSING_MSG);
+        CMPerr(0, CMP_R_ERROR_PROCESSING_MESSAGE);
         return NULL;
     }
 
@@ -341,7 +341,7 @@ static OSSL_CMP_MSG *process_certConf(OSSL_CMP_SRV_CTX *srv_ctx,
     ccc = req->body->value.certConf;
     num = sk_OSSL_CMP_CERTSTATUS_num(ccc);
 
-    if (OSSL_CMP_CTX_get_option(ctx, OSSL_CMP_OPT_IMPLICITCONFIRM) == 1) {
+    if (OSSL_CMP_CTX_get_option(ctx, OSSL_CMP_OPT_IMPLICIT_CONFIRM) == 1) {
         CMPerr(0, CMP_R_ERROR_UNEXPECTED_CERTCONF);
         return NULL;
     }
@@ -415,13 +415,17 @@ static OSSL_CMP_MSG *process_pollReq(OSSL_CMP_SRV_CTX *srv_ctx,
 }
 
 /*
- * Determines whether missing protection is allowed
+ * Determine whether missing/invalid protection of request message is allowed.
+ * Return 1 on acceptance, 0 on rejection, or -1 on (internal) error.
  */
 static int unprotected_exception(const OSSL_CMP_CTX *ctx,
                                  const OSSL_CMP_MSG *req,
                                  int invalid_protection,
                                  int accept_unprotected_requests)
 {
+    if (!ossl_assert(ctx != NULL && req != NULL))
+        return -1;
+
     if (accept_unprotected_requests) {
         ossl_cmp_log1(WARN, ctx, "ignoring %s protection of request message",
                       invalid_protection ? "invalid" : "missing");
@@ -574,7 +578,7 @@ OSSL_CMP_MSG *OSSL_CMP_SRV_process_request(OSSL_CMP_SRV_CTX *srv_ctx,
     case OSSL_CMP_PKIBODY_CP:
     case OSSL_CMP_PKIBODY_KUP:
     case OSSL_CMP_PKIBODY_RP:
-        if (OSSL_CMP_CTX_get_option(ctx, OSSL_CMP_OPT_IMPLICITCONFIRM) == 0)
+        if (OSSL_CMP_CTX_get_option(ctx, OSSL_CMP_OPT_IMPLICIT_CONFIRM) == 0)
             break;
         /* fall through */
 
@@ -607,7 +611,7 @@ OSSL_CMP_MSG * OSSL_CMP_CTX_server_perform(OSSL_CMP_CTX *client_ctx,
     }
 
     if ((srv_ctx = OSSL_CMP_CTX_get_transfer_cb_arg(client_ctx)) == NULL) {
-        CMPerr(0, CMP_R_ERROR_TRANSFERRING_OUT);
+        CMPerr(0, CMP_R_TRANSFER_ERROR);
         return 0;
     }
 
diff --git a/crypto/cmp/cmp_status.c b/crypto/cmp/cmp_status.c
index 6232239237..9192cf5e4d 100644
--- a/crypto/cmp/cmp_status.c
+++ b/crypto/cmp/cmp_status.c
@@ -75,7 +75,7 @@ int ossl_cmp_pkisi_get_pkifailureinfo(const OSSL_CMP_PKISI *si)
     int i;
     int res = 0;
 
-    if (!ossl_assert(si != NULL && si->failInfo != NULL))
+    if (!ossl_assert(si != NULL))
         return -1;
     for (i = 0; i <= OSSL_CMP_PKIFAILUREINFO_MAX; i++)
         if (ASN1_BIT_STRING_get_bit(si->failInfo, i))
diff --git a/crypto/cmp/cmp_util.c b/crypto/cmp/cmp_util.c
index 70c060166a..121b6a173a 100644
--- a/crypto/cmp/cmp_util.c
+++ b/crypto/cmp/cmp_util.c
@@ -144,7 +144,7 @@ int OSSL_CMP_print_to_bio(BIO *bio, const char *component, const char *file,
 
 #define ERR_PRINT_BUF_SIZE 4096
 /* this is similar to ERR_print_errors_cb, but uses the CMP-specific cb type */
-void OSSL_CMP_print_errors_cb(OSSL_cmp_log_cb_t log_fn)
+void OSSL_CMP_print_errors_cb(OSSL_CMP_log_cb_t log_fn)
 {
     unsigned long err;
     char msg[ERR_PRINT_BUF_SIZE];
diff --git a/crypto/cmp/cmp_vfy.c b/crypto/cmp/cmp_vfy.c
index 437bc3298f..73f93360d6 100644
--- a/crypto/cmp/cmp_vfy.c
+++ b/crypto/cmp/cmp_vfy.c
@@ -426,7 +426,7 @@ static int check_msg_find_cert(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *msg)
     char *sname = NULL;
     char *skid_str = NULL;
     const ASN1_OCTET_STRING *skid = msg->header->senderKID;
-    OSSL_cmp_log_cb_t backup_log_cb = ctx->log_cb;
+    OSSL_CMP_log_cb_t backup_log_cb = ctx->log_cb;
     int res = 0;
 
     if (sender == NULL || msg->body == NULL)
@@ -633,8 +633,8 @@ int OSSL_CMP_validate_msg(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *msg)
  *
  * Ensures that:
  * it has a valid body type
- * its protection is valid or absent (allowed only if callback function is
- *    present and function yields non-zero result using also supplied argument)
+ * its protection is valid (or invalid/absent, but only if a callback function
+ *     is present and yields a positive result using also the supplied argument)
  * its transaction ID matches the previous transaction ID stored in ctx (if any)
  * its recipNonce matches the previous senderNonce stored in the ctx (if any)
  *
@@ -660,35 +660,29 @@ int ossl_cmp_msg_check_received(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *msg,
     if (msg->header->protectionAlg != 0) {
         /* detect explicitly permitted exceptions for invalid protection */
         if (!OSSL_CMP_validate_msg(ctx, msg)
-                && (cb == NULL || !(*cb)(ctx, msg, 1, cb_arg))) {
+                && (cb == NULL || (*cb)(ctx, msg, 1, cb_arg) <= 0)) {
             CMPerr(0, CMP_R_ERROR_VALIDATING_PROTECTION);
             return -1;
         }
     } else {
         /* detect explicitly permitted exceptions for missing protection */
-        if (cb == NULL || !(*cb)(ctx, msg, 0, cb_arg)) {
+        if (cb == NULL || (*cb)(ctx, msg, 0, cb_arg) <= 0) {
             CMPerr(0, CMP_R_MISSING_PROTECTION);
             return -1;
         }
     }
 
-    /*
-     * Store any provided extraCerts in ctx for future use,
-     * such that they are available to ctx->certConf_cb and
-     * the peer does not need to send them again in the same transaction.
-     * For efficiency, the extraCerts are prepended so they get used first.
-     */
-    if (!ossl_cmp_sk_X509_add1_certs(ctx->untrusted_certs, msg->extraCerts,
-                                     0 /* this allows self-issued certs */,
-                                     1 /* no_dups */, 1 /* prepend */))
-        return -1;
-
     /* check CMP version number in header */
     if (ossl_cmp_hdr_get_pvno(OSSL_CMP_MSG_get0_header(msg)) != OSSL_CMP_PVNO) {
         CMPerr(0, CMP_R_UNEXPECTED_PVNO);
         return -1;
     }
 
+    if ((rcvd_type = ossl_cmp_msg_get_bodytype(msg)) < 0) {
+        CMPerr(0, CMP_R_PKIBODY_ERROR);
+        return -1;
+    }
+
     /* compare received transactionID with the expected one in previous msg */
     if (ctx->transactionID != NULL
             && (msg->header->transactionID == NULL
@@ -720,10 +714,17 @@ int ossl_cmp_msg_check_received(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *msg,
         && !OSSL_CMP_CTX_set1_transactionID(ctx, msg->header->transactionID))
         return -1;
 
-    if ((rcvd_type = ossl_cmp_msg_get_bodytype(msg)) < 0) {
-        CMPerr(0, CMP_R_PKIBODY_ERROR);
+    /*
+     * Store any provided extraCerts in ctx for future use,
+     * such that they are available to ctx->certConf_cb and
+     * the peer does not need to send them again in the same transaction.
+     * For efficiency, the extraCerts are prepended so they get used first.
+     */
+    if (!ossl_cmp_sk_X509_add1_certs(ctx->untrusted_certs, msg->extraCerts,
+                                     0 /* this allows self-issued certs */,
+                                     1 /* no_dups */, 1 /* prepend */))
         return -1;
-    }
+
     return rcvd_type;
 }
 
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index 50fb57baee..cb106e23b1 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -2078,19 +2078,27 @@ BN_R_P_IS_NOT_PRIME:112:p is not prime
 BN_R_TOO_MANY_ITERATIONS:113:too many iterations
 BN_R_TOO_MANY_TEMPORARY_VARIABLES:109:too many temporary variables
 CMP_R_ALGORITHM_NOT_SUPPORTED:139:algorithm not supported
+CMP_R_BAD_CHECKAFTER_IN_POLLREP:167:bad checkafter in pollrep
 CMP_R_BAD_REQUEST_ID:108:bad request id
 CMP_R_CERTHASH_UNMATCHED:156:certhash unmatched
 CMP_R_CERTID_NOT_FOUND:109:certid not found
+CMP_R_CERTIFICATE_NOT_ACCEPTED:169:certificate not accepted
 CMP_R_CERTIFICATE_NOT_FOUND:112:certificate not found
 CMP_R_CERTREQMSG_NOT_FOUND:157:certreqmsg not found
 CMP_R_CERTRESPONSE_NOT_FOUND:113:certresponse not found
 CMP_R_CERT_AND_KEY_DO_NOT_MATCH:114:cert and key do not match
+CMP_R_CHECKAFTER_OUT_OF_RANGE:181:checkafter out of range
+CMP_R_ENCOUNTERED_KEYUPDATEWARNING:176:encountered keyupdatewarning
+CMP_R_ENCOUNTERED_WAITING:162:encountered waiting
 CMP_R_ERROR_CALCULATING_PROTECTION:115:error calculating protection
 CMP_R_ERROR_CREATING_CERTCONF:116:error creating certconf
 CMP_R_ERROR_CREATING_CERTREP:117:error creating certrep
+CMP_R_ERROR_CREATING_CR:163:error creating cr
 CMP_R_ERROR_CREATING_ERROR:118:error creating error
 CMP_R_ERROR_CREATING_GENM:119:error creating genm
 CMP_R_ERROR_CREATING_GENP:120:error creating genp
+CMP_R_ERROR_CREATING_IR:164:error creating ir
+CMP_R_ERROR_CREATING_KUR:165:error creating kur
 CMP_R_ERROR_CREATING_P10CR:121:error creating p10cr
 CMP_R_ERROR_CREATING_PKICONF:122:error creating pkiconf
 CMP_R_ERROR_CREATING_POLLREP:123:error creating pollrep
@@ -2098,10 +2106,9 @@ CMP_R_ERROR_CREATING_POLLREQ:124:error creating pollreq
 CMP_R_ERROR_CREATING_RP:125:error creating rp
 CMP_R_ERROR_CREATING_RR:126:error creating rr
 CMP_R_ERROR_PARSING_PKISTATUS:107:error parsing pkistatus
-CMP_R_ERROR_PROCESSING_MSG:158:error processing msg
+CMP_R_ERROR_PROCESSING_MESSAGE:158:error processing message
 CMP_R_ERROR_PROTECTING_MESSAGE:127:error protecting message
 CMP_R_ERROR_SETTING_CERTHASH:128:error setting certhash
-CMP_R_ERROR_TRANSFERRING_OUT:159:error transferring out
 CMP_R_ERROR_UNEXPECTED_CERTCONF:160:error unexpected certconf
 CMP_R_ERROR_VALIDATING_PROTECTION:140:error validating protection
 CMP_R_FAILED_EXTRACTING_PUBKEY:141:failed extracting pubkey
@@ -2116,29 +2123,40 @@ CMP_R_MISSING_PROTECTION:143:missing protection
 CMP_R_MISSING_SENDER_IDENTIFICATION:111:missing sender identification
 CMP_R_MISSING_TRUST_STORE:144:missing trust store
 CMP_R_MULTIPLE_REQUESTS_NOT_SUPPORTED:161:multiple requests not supported
+CMP_R_MULTIPLE_RESPONSES_NOT_SUPPORTED:170:multiple responses not supported
 CMP_R_MULTIPLE_SAN_SOURCES:102:multiple san sources
 CMP_R_NO_STDIO:194:no stdio
 CMP_R_NO_SUITABLE_SENDER_CERT:145:no suitable sender cert
 CMP_R_NULL_ARGUMENT:103:null argument
 CMP_R_PKIBODY_ERROR:146:pkibody error
 CMP_R_PKISTATUSINFO_NOT_FOUND:132:pkistatusinfo not found
+CMP_R_POLLING_FAILED:172:polling failed
 CMP_R_POTENTIALLY_INVALID_CERTIFICATE:147:potentially invalid certificate
+CMP_R_RECEIVED_ERROR:180:received error
 CMP_R_RECIPNONCE_UNMATCHED:148:recipnonce unmatched
 CMP_R_REQUEST_NOT_ACCEPTED:149:request not accepted
+CMP_R_REQUEST_REJECTED_BY_SERVER:182:request rejected by server
 CMP_R_SENDER_GENERALNAME_TYPE_NOT_SUPPORTED:150:\
 	sender generalname type not supported
 CMP_R_SRVCERT_DOES_NOT_VALIDATE_MSG:151:srvcert does not validate msg
+CMP_R_TOTAL_TIMEOUT:184:total timeout
 CMP_R_TRANSACTIONID_UNMATCHED:152:transactionid unmatched
+CMP_R_TRANSFER_ERROR:159:transfer error
 CMP_R_UNEXPECTED_PKIBODY:133:unexpected pkibody
+CMP_R_UNEXPECTED_PKISTATUS:185:unexpected pkistatus
 CMP_R_UNEXPECTED_PVNO:153:unexpected pvno
 CMP_R_UNKNOWN_ALGORITHM_ID:134:unknown algorithm id
 CMP_R_UNKNOWN_CERT_TYPE:135:unknown cert type
+CMP_R_UNKNOWN_PKISTATUS:186:unknown pkistatus
 CMP_R_UNSUPPORTED_ALGORITHM:136:unsupported algorithm
 CMP_R_UNSUPPORTED_KEY_TYPE:137:unsupported key type
 CMP_R_UNSUPPORTED_PROTECTION_ALG_DHBASEDMAC:154:\
 	unsupported protection alg dhbasedmac
 CMP_R_WRONG_ALGORITHM_OID:138:wrong algorithm oid
+CMP_R_WRONG_CERTID_IN_RP:187:wrong certid in rp
 CMP_R_WRONG_PBM_VALUE:155:wrong pbm value
+CMP_R_WRONG_RP_COMPONENT_COUNT:188:wrong rp component count
+CMP_R_WRONG_SERIAL_IN_RP:173:wrong serial in rp
 CMS_R_ADD_SIGNER_ERROR:99:add signer error
 CMS_R_ATTRIBUTE_ERROR:161:attribute error
 CMS_R_CERTIFICATE_ALREADY_PRESENT:175:certificate already present
diff --git a/doc/internal/man3/ossl_cmp_msg_check_received.pod b/doc/internal/man3/ossl_cmp_msg_check_received.pod
new file mode 100644
index 0000000000..3fc7882c5a
--- /dev/null
+++ b/doc/internal/man3/ossl_cmp_msg_check_received.pod
@@ -0,0 +1,86 @@
+=pod
+
+=head1 NAME
+
+ossl_cmp_allow_unprotected_cb_t,
+ossl_cmp_msg_check_received
+- does all checks on a received CMP message that can be done generically
+
+=head1 SYNOPSIS
+
+ #include "cmp_local.h"
+
+ typedef int (*ossl_cmp_allow_unprotected_cb_t)(const OSSL_CMP_CTX *ctx,
+                                                const OSSL_CMP_MSG *msg,
+                                                int invalid_protection, int arg);
+
+ int ossl_cmp_msg_check_received(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *msg,
+                                 ossl_cmp_allow_unprotected_cb_t cb, int cb_arg);
+
+=head1 DESCRIPTION
+
+ossl_cmp_msg_check_received() checks the given message B<msg>,
+which may be a server response or a request by some client.
+
+It is ensured for the B<msg> that
+
+=over 4
+
+=item it has a valid body type,
+
+=item its protection is present and valid (or a callback function B<cb>
+is present and indicates that a missing or invalid protection is acceptable),
+
+=item its recipNonce matches any previous senderNonce stored in B<ctx>, and
+
+=item its transaction ID matches any previous transaction ID stored in B<ctx>.
+
+=back
+
+In case no protection is present and B<cb> is not NULL then this callback
+function is called with its B<invalid_protection> parameter being 0, while in
+case an invalid protection is present the B<invalid_protection> parameter is 1.
+The callback is passed also the arguments B<ctx>, B<msg>, and <cb_arg>
+(which typically contains the expected message type).
+The callback should return 1 on acceptance, 0 on rejection, or -1 on error.
+It should not put and error on the error stack since this could be misleading.
+
+If all checks pass then ossl_cmp_msg_check_received()
+
+=over 4
+
+=item learns the senderNonce from the received message,
+
+=item learns the transaction ID if it is not yet in B<ctx>, and
+
+=item adds any extraCerts contained in the <msg> to the list of untrusted
+certificates in B<ctx> for future use, such that
+they are available already to the certificate confirmation callback and the
+peer does not need to send them again (at least not in the same transaction).
+For efficiency, the extraCerts are prepended to the list so they get used first.
+
+=back
+
+=head1 RETURN VALUES
+
+ossl_cmp_msg_check_received() returns the message body type (which is >= 0)
+on success, -1 on error.
+
+=head1 SEE ALSO
+
+L<OSSL_CMP_validate_msg(3)>
+
+=head1 HISTORY
+
+The OpenSSL CMP support was added in OpenSSL 3.0.
+
+=head1 COPYRIGHT
+
+Copyright 2007-2019 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the Apache License 2.0 (the "License").  You may not use
+this file except in compliance with the License.  You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/internal/man3/ossl_cmp_msg_create.pod b/doc/internal/man3/ossl_cmp_msg_create.pod
index 7498a1d6fd..ebc08f7ef1 100644
--- a/doc/internal/man3/ossl_cmp_msg_create.pod
+++ b/doc/internal/man3/ossl_cmp_msg_create.pod
@@ -62,8 +62,7 @@ See the individual functions above.
 
 =head1 SEE ALSO
 
-L<OSSL_CMP_CTX_new(3)>, L<OSSL_CMP_exec_IR_ses(3)>,
-L<OSSL_CMP_MSG_http_perform(3)>
+L<OSSL_CMP_CTX_new(3)>, L<OSSL_CMP_exec_IR_ses(3)>
 
 =head1 HISTORY
 
diff --git a/doc/man3/OSSL_CMP_CTX_new.pod b/doc/man3/OSSL_CMP_CTX_new.pod
index 032ef817c0..016d851ddc 100644
--- a/doc/man3/OSSL_CMP_CTX_new.pod
+++ b/doc/man3/OSSL_CMP_CTX_new.pod
@@ -19,7 +19,7 @@ OSSL_CMP_DEFAULT_PORT,
 OSSL_CMP_CTX_set_http_cb,
 OSSL_CMP_CTX_set_http_cb_arg,
 OSSL_CMP_CTX_get_http_cb_arg,
-OSSL_cmp_transfer_cb_t,
+OSSL_CMP_transfer_cb_t,
 OSSL_CMP_CTX_set_transfer_cb,
 OSSL_CMP_CTX_set_transfer_cb_arg,
 OSSL_CMP_CTX_get_transfer_cb_arg,
@@ -47,7 +47,7 @@ OSSL_CMP_CTX_push0_policy,
 OSSL_CMP_CTX_set1_oldCert,
 OSSL_CMP_CTX_set1_p10CSR,
 OSSL_CMP_CTX_push0_genm_ITAV,
-OSSL_cmp_certConf_cb_t,
+OSSL_CMP_certConf_cb_t,
 OSSL_CMP_CTX_set_certConf_cb,
 OSSL_CMP_CTX_set_certConf_cb_arg,
 OSSL_CMP_CTX_get_certConf_cb_arg,
@@ -72,7 +72,7 @@ OSSL_CMP_CTX_set1_senderNonce
  int OSSL_CMP_CTX_get_option(const OSSL_CMP_CTX *ctx, int opt);
 
  /* logging and error reporting: */
- int OSSL_CMP_CTX_set_log_cb(OSSL_CMP_CTX *ctx, OSSL_cmp_log_cb_t cb);
+ int OSSL_CMP_CTX_set_log_cb(OSSL_CMP_CTX *ctx, OSSL_CMP_log_cb_t cb);
  #define OSSL_CMP_CTX_set_log_verbosity(ctx, level)
  void OSSL_CMP_CTX_print_errors(OSSL_CMP_CTX *ctx);
 
@@ -86,10 +86,10 @@ OSSL_CMP_CTX_set1_senderNonce
  int OSSL_CMP_CTX_set_http_cb(OSSL_CMP_CTX *ctx, HTTP_bio_cb_t cb);
  int OSSL_CMP_CTX_set_http_cb_arg(OSSL_CMP_CTX *ctx, void *arg);
  void *OSSL_CMP_CTX_get_http_cb_arg(const OSSL_CMP_CTX *ctx);
- typedef OSSL_CMP_MSG *(*OSSL_cmp_transfer_cb_t)(OSSL_CMP_CTX *ctx,
+ typedef OSSL_CMP_MSG *(*OSSL_CMP_transfer_cb_t)(OSSL_CMP_CTX *ctx,
                                                  const OSSL_CMP_MSG *req);
  int OSSL_CMP_CTX_set_transfer_cb(OSSL_CMP_CTX *ctx,
-                                  OSSL_cmp_transfer_cb_t cb);
+                                  OSSL_CMP_transfer_cb_t cb);
  int OSSL_CMP_CTX_set_transfer_cb_arg(OSSL_CMP_CTX *ctx, void *arg);
  void *OSSL_CMP_CTX_get_transfer_cb_arg(const OSSL_CMP_CTX *ctx);
 
@@ -134,9 +134,9 @@ OSSL_CMP_CTX_set1_senderNonce
  int OSSL_CMP_CTX_push0_genm_ITAV(OSSL_CMP_CTX *ctx, OSSL_CMP_ITAV *itav);
 
  /* certificate confirmation: */
- typedef int (*OSSL_cmp_certConf_cb_t)(OSSL_CMP_CTX *ctx, X509 *cert,
+ typedef int (*OSSL_CMP_certConf_cb_t)(OSSL_CMP_CTX *ctx, X509 *cert,
                                        int fail_info, const char **txt);
- int OSSL_CMP_CTX_set_certConf_cb(OSSL_CMP_CTX *ctx, OSSL_cmp_certConf_cb_t cb);
+ int OSSL_CMP_CTX_set_certConf_cb(OSSL_CMP_CTX *ctx, OSSL_CMP_certConf_cb_t cb);
  int OSSL_CMP_CTX_set_certConf_cb_arg(OSSL_CMP_CTX *ctx, void *arg);
  void *OSSL_CMP_CTX_get_certConf_cb_arg(const OSSL_CMP_CTX *ctx);
 
@@ -149,7 +149,7 @@ OSSL_CMP_CTX_set1_senderNonce
  STACK_OF(X509) *OSSL_CMP_CTX_get1_caPubs(const OSSL_CMP_CTX *ctx);
  STACK_OF(X509) *OSSL_CMP_CTX_get1_extraCertsIn(const OSSL_CMP_CTX *ctx);
 
- /* for test purposes only: */
+ /* for testing and debugging purposes: */
  int OSSL_CMP_CTX_set1_transactionID(OSSL_CMP_CTX *ctx,
                                      const ASN1_OCTET_STRING *id);
  int OSSL_CMP_CTX_set1_senderNonce(OSSL_CMP_CTX *ctx,
@@ -172,7 +172,7 @@ from the last executed transaction.
 All other field values (i.e., CMP options) are retained for potential re-use.
 
 OSSL_CMP_CTX_set_option() sets the given value for the given option
-(e.g., OSSL_CMP_OPT_IMPLICITCONFIRM) in the given OSSL_CMP_CTX structure.
+(e.g., OSSL_CMP_OPT_IMPLICIT_CONFIRM) in the given OSSL_CMP_CTX structure.
 
 The following options can be set:
 
@@ -184,17 +184,17 @@ The following options can be set:
         due to errors, warnings, general info, debugging, etc.
         Default is OSSL_CMP_LOG_INFO. See also L<OSSL_CMP_log_open(3)>.
 
-=item B<OSSL_CMP_OPT_MSGTIMEOUT>
+=item B<OSSL_CMP_OPT_MSG_TIMEOUT>
 
         Number of seconds (or 0 for infinite) a CMP message round trip is
         allowed to take before a timeout error is returned. Default is 120.
 
-=item B<OSSL_CMP_OPT_TOTALTIMEOUT>
+=item B<OSSL_CMP_OPT_TOTAL_TIMEOUT>
 
         Maximum total number of seconds an enrollment (including polling)
         may take. Default is 0 (infinite).
 
-=item B<OSSL_CMP_OPT_VALIDITYDAYS>
+=item B<OSSL_CMP_OPT_VALIDITY_DAYS>
 
         Number of days new certificates are asked to be valid for.
 
@@ -211,7 +211,7 @@ The following options can be set:
 
         Demand that the given policies are flagged as critical.
 
-=item B<OSSL_CMP_OPT_POPOMETHOD>
+=item B<OSSL_CMP_OPT_POPO_METHOD>
 
         Select the proof of possession method to use. Possible values are:
 
@@ -247,14 +247,14 @@ The following options can be set:
         The reason code to be included in a Revocation Request (RR);
         values: 0..10 (RFC 5210, 5.3.1) or -1 for none, which is the default.
 
-=item B<OSSL_CMP_OPT_IMPLICITCONFIRM>
+=item B<OSSL_CMP_OPT_IMPLICIT_CONFIRM>
 
         Request server to enable implicit confirm mode, where the client
         does not need to send confirmation upon receiving the
         certificate. If the server does not enable implicit confirmation
         in the return message, then confirmation is sent anyway.
 
-=item B<OSSL_CMP_OPT_DISABLECONFIRM>
+=item B<OSSL_CMP_OPT_DISABLE_CONFIRM>
 
         Do not confirm enrolled certificates, to cope with broken servers
         not supporting implicit confirmation correctly.
@@ -290,7 +290,7 @@ RFC 4210.
 =back
 
 OSSL_CMP_CTX_get_option() reads the current value of the given option
-(e.g., OSSL_CMP_OPT_IMPLICITCONFIRM) from the given OSSL_CMP_CTX structure.
+(e.g., OSSL_CMP_OPT_IMPLICIT_CONFIRM) from the given OSSL_CMP_CTX structure.
 
 OSSL_CMP_CTX_set_log_cb() sets in B<ctx> the callback function C<cb>
 for handling error queue entries and logging messages.
@@ -350,14 +350,14 @@ OSSL_CMP_CTX_set_http_cb_arg() or NULL if unset.
 OSSL_CMP_CTX_set_transfer_cb() sets the message transfer callback function,
 which has the type
 
- typedef OSSL_CMP_MSG *(*OSSL_cmp_transfer_cb_t) (OSSL_CMP_CTX *ctx,
+ typedef OSSL_CMP_MSG *(*OSSL_CMP_transfer_cb_t) (OSSL_CMP_CTX *ctx,
                                                   const OSSL_CMP_MSG *req);
 
 Returns 1 on success, 0 on error.
 
 Default is NULL, which implies the use of L<OSSL_CMP_MSG_http_perform(3)>.
 The callback should send the CMP request message it obtains via the B<req>
-parameter and on success return the response.
+parameter and on success return the response, else it must return NULL.
 The transfer callback may make use of a custom defined argument stored in
 the ctx by means of OSSL_CMP_CTX_set_transfer_cb_arg(), which may be retrieved
 again through OSSL_CMP_CTX_get_transfer_cb_arg().
@@ -512,7 +512,7 @@ OSSL_CMP_CTX_set_certConf_cb() sets the callback used for evaluating the newly
 enrolled certificate before the library sends, depending on its result,
 a positive or negative certConf message to the server. The callback has type
 
- typedef int (*OSSL_cmp_certConf_cb_t) (OSSL_CMP_CTX *ctx, X509 *cert,
+ typedef int (*OSSL_CMP_certConf_cb_t) (OSSL_CMP_CTX *ctx, X509 *cert,
                                         int fail_info, const char **txt);
 
 and should inspect the certificate it obtains via the B<cert> parameter and may
diff --git a/doc/man3/OSSL_CMP_SRV_CTX_new.pod b/doc/man3/OSSL_CMP_SRV_CTX_new.pod
index 45ac0174b7..2b7ce7fa2b 100644
--- a/doc/man3/OSSL_CMP_SRV_CTX_new.pod
+++ b/doc/man3/OSSL_CMP_SRV_CTX_new.pod
@@ -128,6 +128,9 @@ confirmation of newly enrolled certificates if requested.
 
 CMP is defined in RFC 4210 (and CRMF in RFC 4211).
 
+So far the CMP server implementation is limited to one request per CMP message
+(and consequently to at most one response component per CMP message).
+
 =head1 RETURN VALUES
 
 OSSL_CMP_SRV_CTX_new() returns a B<OSSL_CMP_SRV_CTX> structure on success,
diff --git a/doc/man3/OSSL_CMP_exec_IR_ses.pod b/doc/man3/OSSL_CMP_exec_IR_ses.pod
new file mode 100644
index 0000000000..24913d5b80
--- /dev/null
+++ b/doc/man3/OSSL_CMP_exec_IR_ses.pod
@@ -0,0 +1,172 @@
+=pod
+
+=head1 NAME
+
+OSSL_CMP_exec_IR_ses,
+OSSL_CMP_exec_CR_ses,
+OSSL_CMP_exec_P10CR_ses,
+OSSL_CMP_exec_KUR_ses,
+OSSL_CMP_IR,
+OSSL_CMP_CR,
+OSSL_CMP_P10CR,
+OSSL_CMP_KUR,
+OSSL_CMP_try_certreq,
+OSSL_CMP_exec_RR_ses,
+OSSL_CMP_exec_GENM_ses,
+OSSL_CMP_certConf_cb
+- functions implementing CMP client transactions
+
+=head1 SYNOPSIS
+
+ #include <openssl/cmp.h>
+
+ X509 *OSSL_CMP_exec_IR_ses(OSSL_CMP_CTX *ctx);
+ X509 *OSSL_CMP_exec_CR_ses(OSSL_CMP_CTX *ctx);
+ X509 *OSSL_CMP_exec_P10CR_ses(OSSL_CMP_CTX *ctx);
+ X509 *OSSL_CMP_exec_KUR_ses(OSSL_CMP_CTX *ctx);
+ #define OSSL_CMP_IR
+ #define OSSL_CMP_CR
+ #define OSSL_CMP_P10CR
+ #define OSSL_CMP_KUR
+ int OSSL_CMP_try_certreq(OSSL_CMP_CTX *ctx, int req_type, int *checkAfter);
+ int OSSL_CMP_certConf_cb(OSSL_CMP_CTX *ctx, X509 *cert, int fail_info,
+                          const char **text);
+ X509 *OSSL_CMP_exec_RR_ses(OSSL_CMP_CTX *ctx);
+ STACK_OF(OSSL_CMP_ITAV) *OSSL_CMP_exec_GENM_ses(OSSL_CMP_CTX *ctx);
+
+=head1 DESCRIPTION
+
+This is the OpenSSL API for doing CMP (Certificate Management Protocol)
+client-server transactions, i.e., sequences of CMP requests and responses.
+
+All functions take a populated OSSL_CMP_CTX structure as their first argument.
+Usually the server name, port, and path ("CMP alias") need to be set, as well as
+credentials the client can use for authenticating itself to the client.
+In order to authenticate the server the client typically needs a trust store.
+For performing certificate enrollment requests the certificate template needs
+to be sufficiently filled in, giving at least the subject name and key.
+The functions return their respective main results directly, while there are
+also accessor functions for retrieving various results and status information
+from the B<ctx>. See L<OSSL_CMP_CTX_new(3)> etc. for details.
+
+The default conveying protocol is HTTP.
+Timeout values may be given per request-response pair and per transaction.
+See L<OSSL_CMP_MSG_http_perform(3)> for details.
+
+OSSL_CMP_exec_IR_ses() requests an initial certificate from the given PKI.
+
+OSSL_CMP_exec_CR_ses() requests an additional certificate.
+
+OSSL_CMP_exec_P10CR_ses() conveys a legacy PKCS#10 CSR requesting a certificate.
+
+OSSL_CMP_exec_KUR_ses() obtains an updated certificate.
+
+All these four types of certificate enrollment may be blocked by sleeping until the
+CAs or an intermedate PKI component can fully process and answer the request.
+
+OSSL_CMP_try_certreq() is an alternative to these four functions that is
+more uniform regarding the type of the certificate request to use and
+more flexible regarding what to do after receiving a checkAfter value.
+When called for the first time (with no certificate request in progress for
+the given B<ctx>) it starts a new transaction by sending a certificate request
+of the given type,
+which may be IR, CR, P10CR, or KUR as specified by the B<req_type> parameter.
+Otherwise (when according to B<ctx> a 'waiting' status has been received before)
+it continues polling for the pending request
+unless the B<req_type> argument is < 0, which aborts the request.
+If the requested certificate is available the function returns 1 and the
+caller can use B<OSSL_CMP_CTX_get0_newCert()> to retrieve the new certificate.
+If no error occurred but no certificate is available yet then
+OSSL_CMP_try_certreq() remembers in the CMP context that it should be retried
+and returns -1 after assigning the received checkAfter value
+via the output pointer argument (unless it is NULL).
+The checkAfter value indicates the number of seconds the caller should let pass
+before trying again. The caller is free to sleep for the given number of seconds
+or for some other time and/or to do anything else before retrying by calling
+OSSL_CMP_try_certreq() again with the same parameter values as before.
+OSSL_CMP_try_certreq() then polls
+to see whether meanwhile the requested certificate is available.
+If the caller decides to abort the pending certificate request and provides
+a negative value as the B<req_type> argument then OSSL_CMP_try_certreq()
+aborts the CMP transaction by sending an error message to the server.
+
+OSSL_CMP_certConf_cb() is a basic certificate confirmation callback validating
+that the new certificate can be verified with the trusted/untrusted certificates
+in B<ctx>.
+As there is no requirement in RFC 4210 that the certificate can be
+validated by the client, this callback is not set by default in the context.
+
+OSSL_CMP_exec_RR_ses() requests the revocation of the certificate
+specified in the B<ctx> using L<OSSL_CMP_CTX_set1_oldCert(3)>.
+RFC 4210 is vague in which PKIStatus should be returned by the server.
+We take "accepted" and "grantedWithMods" as clear success and handle
+"revocationWarning" and "revocationNotification" just as warnings because CAs
+typically return them as an indication that the certificate was already revoked.
+"rejection" is a clear error. The values "waiting" and "keyUpdateWarning"
+make no sense for revocation and thus are treated as an error as well.
+
+OSSL_CMP_exec_GENM_ses() sends a general message containing the sequence of
+infoType and infoValue pairs (InfoTypeAndValue; short: B<ITAV>)
+provided in the B<ctx> using L<OSSL_CMP_CTX_push0_genm_ITAV(3)>.
+It returns the list of B<ITAV>s received in the GenRep.
+This can be used, for instance, to poll for CRLs or CA Key Updates.
+See RFC 4210 section 5.3.19 and appendix E.5 for details.
+
+=head1 NOTES
+
+CMP is defined in RFC 4210 (and CRMF in RFC 4211).
+
+So far the CMP client implementation is limited to one request per CMP message
+(and consequently to at most one response component per CMP message).
+
+=head1 RETURN VALUES
+
+OSSL_CMP_exec_IR_ses(), OSSL_CMP_exec_CR_ses(),
+OSSL_CMP_exec_P10CR_ses(), and OSSL_CMP_exec_KUR_ses() return a
+pointer to the newly obtained X509 certificate on success, B<NULL> on error.
+This pointer will be freed implicitly by OSSL_CMP_CTX_free() or
+CSSL_CMP_CTX_reinit().
+
+OSSL_CMP_try_certreq() returns 1 if the requested certificate is available
+via B<OSSL_CMP_CTX_get0_newCert()>
+or on successfully aborting a pending certificate request, 0 on error, and -1
+in case a 'waiting' status has been received and checkAfter value is available.
+In the latter case B<OSSL_CMP_CTX_get0_newCert()> yields NULL
+and the output parameter B<checkAfter> has been used to
+assign the received value unless B<checkAfter> is NULL.
+
+OSSL_CMP_certConf_cb() returns B<fail_info> if it is not equal to B<0>,
+else B<0> on successful validation,
+or else a bit field with the B<OSSL_CMP_PKIFAILUREINFO_incorrectData> bit set.
+
+OSSL_CMP_exec_RR_ses() returns the
+pointer to the revoked certificate on success, B<NULL> on error.
+This pointer will be freed implicitly by OSSL_CMP_CTX_free().
+
+OSSL_CMP_exec_GENM_ses() returns a
+pointer to the received B<ITAV> sequence on success, B<NULL> on error.
+This pointer must be freed by the caller.
+
+=head1 EXAMPLES
+
+See OSSL_CMP_CTX for examples on how to prepare the context for these
+functions.
+
+=head1 SEE ALSO
+
+L<OSSL_CMP_CTX_new(3)>, L<OSSL_CMP_MSG_http_perform(3)>
+
+=head1 HISTORY
+
+The OpenSSL CMP support was added in OpenSSL 3.0.
+
+=head1 COPYRIGHT
+
+Copyright 2007-2019 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the Apache License 2.0 (the "License").  You may not use
+this file except in compliance with the License.  You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/man3/OSSL_CMP_log_open.pod b/doc/man3/OSSL_CMP_log_open.pod
index 9f204d6ec1..78d51ee780 100644
--- a/doc/man3/OSSL_CMP_log_open.pod
+++ b/doc/man3/OSSL_CMP_log_open.pod
@@ -13,7 +13,7 @@ OSSL_CMP_LOG_WARNING,
 OSSL_CMP_LOG_NOTICE,
 OSSL_CMP_LOG_INFO,
 OSSL_CMP_LOG_DEBUG,
-OSSL_cmp_log_cb_t,
+OSSL_CMP_log_cb_t,
 OSSL_CMP_print_to_bio,
 OSSL_CMP_print_errors_cb
 - functions for logging and error reporting
@@ -36,12 +36,12 @@ OSSL_CMP_print_errors_cb
  #define OSSL_CMP_LOG_INFO    6
  #define OSSL_CMP_LOG_DEBUG   7
 
- typedef int (*OSSL_cmp_log_cb_t)(const char *component,
+ typedef int (*OSSL_CMP_log_cb_t)(const char *component,
                                   const char *file, int line,
                                   OSSL_CMP_severity level, const char *msg);
  int OSSL_CMP_print_to_bio(BIO *bio, const char *component, const char *file,
                            int line, OSSL_CMP_severity level, const char *msg);
- void OSSL_CMP_print_errors_cb(OSSL_cmp_log_cb_t log_fn);
+ void OSSL_CMP_print_errors_cb(OSSL_CMP_log_cb_t log_fn);
 
 =head1 DESCRIPTION
 
@@ -56,7 +56,7 @@ should be provided for user information, debugging, and auditing purposes.
 A CMP application can obtain this information by providing a callback function
 with the following type:
 
- typedef int (*OSSL_cmp_log_cb_t)(const char *component,
+ typedef int (*OSSL_CMP_log_cb_t)(const char *component,
                                   const char *file, int line,
                                   OSSL_CMP_severity level, const char *msg);
 
diff --git a/doc/man3/OSSL_SELF_TEST_new.pod b/doc/man3/OSSL_SELF_TEST_new.pod
index 32155e1cd0..b2c4f5ccce 100644
--- a/doc/man3/OSSL_SELF_TEST_new.pod
+++ b/doc/man3/OSSL_SELF_TEST_new.pod
@@ -133,7 +133,7 @@ A single self test could be set up in the following way:
         || !EVP_DigestUpdate(ctx, pt, pt_len)
         || !EVP_DigestFinal(ctx, out, &out_len))
         goto err;
-    
+
     /* Optional corruption - If the application callback returns 0 */
     OSSL_SELF_TEST_oncorrupt_byte(st, out);
 
diff --git a/include/openssl/cmp.h b/include/openssl/cmp.h
index fcdd44b40c..21d16cc94d 100644
--- a/include/openssl/cmp.h
+++ b/include/openssl/cmp.h
@@ -248,19 +248,19 @@ void OSSL_CMP_CTX_free(OSSL_CMP_CTX *ctx);
 int OSSL_CMP_CTX_reinit(OSSL_CMP_CTX *ctx);
 /* various CMP options: */
 #  define OSSL_CMP_OPT_LOG_VERBOSITY 0
-#  define OSSL_CMP_OPT_MSGTIMEOUT 1
-#  define OSSL_CMP_OPT_TOTALTIMEOUT 2
-#  define OSSL_CMP_OPT_VALIDITYDAYS 3
+#  define OSSL_CMP_OPT_MSG_TIMEOUT 1
+#  define OSSL_CMP_OPT_TOTAL_TIMEOUT 2
+#  define OSSL_CMP_OPT_VALIDITY_DAYS 3
 #  define OSSL_CMP_OPT_SUBJECTALTNAME_NODEFAULT 4
 #  define OSSL_CMP_OPT_SUBJECTALTNAME_CRITICAL 5
 #  define OSSL_CMP_OPT_POLICIES_CRITICAL 6
-#  define OSSL_CMP_OPT_POPOMETHOD 7
+#  define OSSL_CMP_OPT_POPO_METHOD 7
 #  define OSSL_CMP_OPT_DIGEST_ALGNID 8
 #  define OSSL_CMP_OPT_OWF_ALGNID 9
 #  define OSSL_CMP_OPT_MAC_ALGNID 10
 #  define OSSL_CMP_OPT_REVOCATION_REASON 11
-#  define OSSL_CMP_OPT_IMPLICITCONFIRM 12
-#  define OSSL_CMP_OPT_DISABLECONFIRM 13
+#  define OSSL_CMP_OPT_IMPLICIT_CONFIRM 12
+#  define OSSL_CMP_OPT_DISABLE_CONFIRM 13
 #  define OSSL_CMP_OPT_UNPROTECTED_SEND 14
 #  define OSSL_CMP_OPT_UNPROTECTED_ERRORS 15
 #  define OSSL_CMP_OPT_IGNORE_KEYUSAGE 16
@@ -268,7 +268,7 @@ int OSSL_CMP_CTX_reinit(OSSL_CMP_CTX *ctx);
 int OSSL_CMP_CTX_set_option(OSSL_CMP_CTX *ctx, int opt, int val);
 int OSSL_CMP_CTX_get_option(const OSSL_CMP_CTX *ctx, int opt);
 /* CMP-specific callback for logging and outputting the error queue: */
-int OSSL_CMP_CTX_set_log_cb(OSSL_CMP_CTX *ctx, OSSL_cmp_log_cb_t cb);
+int OSSL_CMP_CTX_set_log_cb(OSSL_CMP_CTX *ctx, OSSL_CMP_log_cb_t cb);
 #  define OSSL_CMP_CTX_set_log_verbosity(ctx, level) \
     OSSL_CMP_CTX_set_option(ctx, OSSL_CMP_OPT_LOG_VERBOSITY, level)
 void OSSL_CMP_CTX_print_errors(OSSL_CMP_CTX *ctx);
@@ -282,9 +282,9 @@ int OSSL_CMP_CTX_set_proxyPort(OSSL_CMP_CTX *ctx, int port);
 int OSSL_CMP_CTX_set_http_cb(OSSL_CMP_CTX *ctx, OSSL_HTTP_bio_cb_t cb);
 int OSSL_CMP_CTX_set_http_cb_arg(OSSL_CMP_CTX *ctx, void *arg);
 void *OSSL_CMP_CTX_get_http_cb_arg(const OSSL_CMP_CTX *ctx);
-typedef OSSL_CMP_MSG *(*OSSL_cmp_transfer_cb_t) (OSSL_CMP_CTX *ctx,
+typedef OSSL_CMP_MSG *(*OSSL_CMP_transfer_cb_t) (OSSL_CMP_CTX *ctx,
                                                  const OSSL_CMP_MSG *req);
-int OSSL_CMP_CTX_set_transfer_cb(OSSL_CMP_CTX *ctx, OSSL_cmp_transfer_cb_t cb);
+int OSSL_CMP_CTX_set_transfer_cb(OSSL_CMP_CTX *ctx, OSSL_CMP_transfer_cb_t cb);
 int OSSL_CMP_CTX_set_transfer_cb_arg(OSSL_CMP_CTX *ctx, void *arg);
 void *OSSL_CMP_CTX_get_transfer_cb_arg(const OSSL_CMP_CTX *ctx);
 /* server authentication: */
@@ -320,9 +320,9 @@ int OSSL_CMP_CTX_set1_p10CSR(OSSL_CMP_CTX *ctx, const X509_REQ *csr);
 /* misc body contents: */
 int OSSL_CMP_CTX_push0_genm_ITAV(OSSL_CMP_CTX *ctx, OSSL_CMP_ITAV *itav);
 /* certificate confirmation: */
-typedef int (*OSSL_cmp_certConf_cb_t) (OSSL_CMP_CTX *ctx, X509 *cert,
+typedef int (*OSSL_CMP_certConf_cb_t) (OSSL_CMP_CTX *ctx, X509 *cert,
                                        int fail_info, const char **txt);
-int OSSL_CMP_CTX_set_certConf_cb(OSSL_CMP_CTX *ctx, OSSL_cmp_certConf_cb_t cb);
+int OSSL_CMP_CTX_set_certConf_cb(OSSL_CMP_CTX *ctx, OSSL_CMP_certConf_cb_t cb);
 int OSSL_CMP_CTX_set_certConf_cb_arg(OSSL_CMP_CTX *ctx, void *arg);
 void *OSSL_CMP_CTX_get_certConf_cb_arg(const OSSL_CMP_CTX *ctx);
 /* result fetching: */
@@ -410,6 +410,21 @@ int OSSL_CMP_SRV_CTX_set_accept_raverified(OSSL_CMP_SRV_CTX *srv_ctx, int val);
 int OSSL_CMP_SRV_CTX_set_grant_implicit_confirm(OSSL_CMP_SRV_CTX *srv_ctx,
                                                 int val);
 
+/* from cmp_client.c */
+X509 *OSSL_CMP_exec_IR_ses(OSSL_CMP_CTX *ctx);
+X509 *OSSL_CMP_exec_CR_ses(OSSL_CMP_CTX *ctx);
+X509 *OSSL_CMP_exec_P10CR_ses(OSSL_CMP_CTX *ctx);
+X509 *OSSL_CMP_exec_KUR_ses(OSSL_CMP_CTX *ctx);
+# define OSSL_CMP_IR    OSSL_CMP_PKIBODY_IR
+# define OSSL_CMP_CR    OSSL_CMP_PKIBODY_CR
+# define OSSL_CMP_P10CR OSSL_CMP_PKIBODY_P10CR
+# define OSSL_CMP_KUR   OSSL_CMP_PKIBODY_KUR
+int OSSL_CMP_try_certreq(OSSL_CMP_CTX *ctx, int req_type, int *checkAfter);
+int OSSL_CMP_certConf_cb(OSSL_CMP_CTX *ctx, X509 *cert, int fail_info,
+                         const char **text);
+X509 *OSSL_CMP_exec_RR_ses(OSSL_CMP_CTX *ctx);
+STACK_OF(OSSL_CMP_ITAV) *OSSL_CMP_exec_GENM_ses(OSSL_CMP_CTX *ctx);
+
 #  ifdef  __cplusplus
 }
 #  endif
diff --git a/include/openssl/cmp_util.h b/include/openssl/cmp_util.h
index 6b6025b6ce..831b65ec1c 100644
--- a/include/openssl/cmp_util.h
+++ b/include/openssl/cmp_util.h
@@ -39,13 +39,13 @@ typedef int OSSL_CMP_severity;
 #  define OSSL_CMP_LOG_NOTICE  5
 #  define OSSL_CMP_LOG_INFO    6
 #  define OSSL_CMP_LOG_DEBUG   7
-typedef int (*OSSL_cmp_log_cb_t)(const char *func, const char *file, int line,
+typedef int (*OSSL_CMP_log_cb_t)(const char *func, const char *file, int line,
                                  OSSL_CMP_severity level, const char *msg);
 
 int OSSL_CMP_print_to_bio(BIO *bio, const char *component, const char *file,
                           int line, OSSL_CMP_severity level, const char *msg);
 /* use of the logging callback for outputting error queue */
-void OSSL_CMP_print_errors_cb(OSSL_cmp_log_cb_t log_fn);
+void OSSL_CMP_print_errors_cb(OSSL_CMP_log_cb_t log_fn);
 
 #  ifdef  __cplusplus
 }
diff --git a/include/openssl/cmperr.h b/include/openssl/cmperr.h
index f868cc104f..c11f372ab5 100644
--- a/include/openssl/cmperr.h
+++ b/include/openssl/cmperr.h
@@ -34,19 +34,27 @@ int ERR_load_CMP_strings(void);
  * CMP reason codes.
  */
 #  define CMP_R_ALGORITHM_NOT_SUPPORTED                    139
+#  define CMP_R_BAD_CHECKAFTER_IN_POLLREP                  167
 #  define CMP_R_BAD_REQUEST_ID                             108
 #  define CMP_R_CERTHASH_UNMATCHED                         156
 #  define CMP_R_CERTID_NOT_FOUND                           109
+#  define CMP_R_CERTIFICATE_NOT_ACCEPTED                   169
 #  define CMP_R_CERTIFICATE_NOT_FOUND                      112
 #  define CMP_R_CERTREQMSG_NOT_FOUND                       157
 #  define CMP_R_CERTRESPONSE_NOT_FOUND                     113
 #  define CMP_R_CERT_AND_KEY_DO_NOT_MATCH                  114
+#  define CMP_R_CHECKAFTER_OUT_OF_RANGE                    181
+#  define CMP_R_ENCOUNTERED_KEYUPDATEWARNING               176
+#  define CMP_R_ENCOUNTERED_WAITING                        162
 #  define CMP_R_ERROR_CALCULATING_PROTECTION               115
 #  define CMP_R_ERROR_CREATING_CERTCONF                    116
 #  define CMP_R_ERROR_CREATING_CERTREP                     117
+#  define CMP_R_ERROR_CREATING_CR                          163
 #  define CMP_R_ERROR_CREATING_ERROR                       118
 #  define CMP_R_ERROR_CREATING_GENM                        119
 #  define CMP_R_ERROR_CREATING_GENP                        120
+#  define CMP_R_ERROR_CREATING_IR                          164
+#  define CMP_R_ERROR_CREATING_KUR                         165
 #  define CMP_R_ERROR_CREATING_P10CR                       121
 #  define CMP_R_ERROR_CREATING_PKICONF                     122
 #  define CMP_R_ERROR_CREATING_POLLREP                     123
@@ -54,10 +62,9 @@ int ERR_load_CMP_strings(void);
 #  define CMP_R_ERROR_CREATING_RP                          125
 #  define CMP_R_ERROR_CREATING_RR                          126
 #  define CMP_R_ERROR_PARSING_PKISTATUS                    107
-#  define CMP_R_ERROR_PROCESSING_MSG                       158
+#  define CMP_R_ERROR_PROCESSING_MESSAGE                   158
 #  define CMP_R_ERROR_PROTECTING_MESSAGE                   127
 #  define CMP_R_ERROR_SETTING_CERTHASH                     128
-#  define CMP_R_ERROR_TRANSFERRING_OUT                     159
 #  define CMP_R_ERROR_UNEXPECTED_CERTCONF                  160
 #  define CMP_R_ERROR_VALIDATING_PROTECTION                140
 #  define CMP_R_FAILED_EXTRACTING_PUBKEY                   141
@@ -71,27 +78,38 @@ int ERR_load_CMP_strings(void);
 #  define CMP_R_MISSING_SENDER_IDENTIFICATION              111
 #  define CMP_R_MISSING_TRUST_STORE                        144
 #  define CMP_R_MULTIPLE_REQUESTS_NOT_SUPPORTED            161
+#  define CMP_R_MULTIPLE_RESPONSES_NOT_SUPPORTED           170
 #  define CMP_R_MULTIPLE_SAN_SOURCES                       102
 #  define CMP_R_NO_STDIO                                   194
 #  define CMP_R_NO_SUITABLE_SENDER_CERT                    145
 #  define CMP_R_NULL_ARGUMENT                              103
 #  define CMP_R_PKIBODY_ERROR                              146
 #  define CMP_R_PKISTATUSINFO_NOT_FOUND                    132
+#  define CMP_R_POLLING_FAILED                             172
 #  define CMP_R_POTENTIALLY_INVALID_CERTIFICATE            147
+#  define CMP_R_RECEIVED_ERROR                             180
 #  define CMP_R_RECIPNONCE_UNMATCHED                       148
 #  define CMP_R_REQUEST_NOT_ACCEPTED                       149
+#  define CMP_R_REQUEST_REJECTED_BY_SERVER                 182
 #  define CMP_R_SENDER_GENERALNAME_TYPE_NOT_SUPPORTED      150
 #  define CMP_R_SRVCERT_DOES_NOT_VALIDATE_MSG              151
+#  define CMP_R_TOTAL_TIMEOUT                              184
 #  define CMP_R_TRANSACTIONID_UNMATCHED                    152
+#  define CMP_R_TRANSFER_ERROR                             159
 #  define CMP_R_UNEXPECTED_PKIBODY                         133
+#  define CMP_R_UNEXPECTED_PKISTATUS                       185
 #  define CMP_R_UNEXPECTED_PVNO                            153
 #  define CMP_R_UNKNOWN_ALGORITHM_ID                       134
 #  define CMP_R_UNKNOWN_CERT_TYPE                          135
+#  define CMP_R_UNKNOWN_PKISTATUS                          186
 #  define CMP_R_UNSUPPORTED_ALGORITHM                      136
 #  define CMP_R_UNSUPPORTED_KEY_TYPE                       137
 #  define CMP_R_UNSUPPORTED_PROTECTION_ALG_DHBASEDMAC      154
 #  define CMP_R_WRONG_ALGORITHM_OID                        138
+#  define CMP_R_WRONG_CERTID_IN_RP                         187
 #  define CMP_R_WRONG_PBM_VALUE                            155
+#  define CMP_R_WRONG_RP_COMPONENT_COUNT                   188
+#  define CMP_R_WRONG_SERIAL_IN_RP                         173
 
 # endif
 #endif
diff --git a/test/build.info b/test/build.info
index 6f00d1a141..bc1f3f2f86 100644
--- a/test/build.info
+++ b/test/build.info
@@ -442,7 +442,7 @@ IF[{- !$disabled{tests} -}]
   IF[{- !$disabled{cmp} -}]
     PROGRAMS{noinst}=cmp_asn_test cmp_ctx_test cmp_status_test cmp_hdr_test \
                      cmp_protect_test cmp_msg_test cmp_vfy_test \
-                     cmp_server_test
+                     cmp_server_test cmp_client_test
   ENDIF
 
   SOURCE[cmp_asn_test]=cmp_asn_test.c cmp_testlib.c
@@ -477,6 +477,10 @@ IF[{- !$disabled{tests} -}]
   INCLUDE[cmp_server_test]=.. ../include ../apps/include
   DEPEND[cmp_server_test]=../libcrypto.a libtestutil.a
 
+  SOURCE[cmp_client_test]=cmp_client_test.c cmp_testlib.c ../apps/cmp_mock_srv.c
+  INCLUDE[cmp_client_test]=.. ../include ../apps/include
+  DEPEND[cmp_client_test]=../libcrypto.a libtestutil.a
+
   # Internal test programs.  These are essentially a collection of internal
   # test routines.  Some of them need to reach internal symbols that aren't
   # available through the shared library (at least on Linux, Solaris, Windows
diff --git a/test/cmp_client_test.c b/test/cmp_client_test.c
new file mode 100644
index 0000000000..057d079f73
--- /dev/null
+++ b/test/cmp_client_test.c
@@ -0,0 +1,393 @@
+/*
+ * Copyright 2007-2019 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright Nokia 2007-2019
+ * Copyright Siemens AG 2015-2019
+ *
+ * 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
+ */
+
+#include "cmp_testlib.h"
+
+#include "apps/cmp_mock_srv.h"
+
+#ifndef NDEBUG /* tests need mock server, which is available only if !NDEBUG */
+
+static const char *server_key_f;
+static const char *server_cert_f;
+static const char *client_key_f;
+static const char *client_cert_f;
+static const char *pkcs10_f;
+
+typedef struct test_fixture {
+    const char *test_case_name;
+    OSSL_CMP_CTX *cmp_ctx;
+    OSSL_CMP_SRV_CTX *srv_ctx;
+    int expected;
+    X509 *(*exec_cert_ses_cb) (OSSL_CMP_CTX *);
+    STACK_OF(X509) *caPubs;
+} CMP_SES_TEST_FIXTURE;
+
+static EVP_PKEY *server_key = NULL;
+static X509 *server_cert = NULL;
+static EVP_PKEY *client_key = NULL;
+static X509 *client_cert = NULL;
+static unsigned char ref[CMP_TEST_REFVALUE_LENGTH];
+
+/*
+ * For these unit tests, the client abandons message protection, and for
+ * error messages the mock server does so as well.
+ * Message protection and verification is tested in cmp_lib_test.c
+ */
+
+static void tear_down(CMP_SES_TEST_FIXTURE *fixture)
+{
+    OSSL_CMP_CTX_free(fixture->cmp_ctx);
+    ossl_cmp_mock_srv_free(fixture->srv_ctx);
+    sk_X509_free(fixture->caPubs);
+    OPENSSL_free(fixture);
+}
+
+static CMP_SES_TEST_FIXTURE *set_up(const char *const test_case_name)
+{
+    CMP_SES_TEST_FIXTURE *fixture;
+    OSSL_CMP_CTX *srv_cmp_ctx = NULL;
+    OSSL_CMP_CTX *ctx = NULL; /* for client */
+
+    if (!TEST_ptr(fixture = OPENSSL_zalloc(sizeof(*fixture))))
+        return NULL;
+    fixture->test_case_name = test_case_name;
+    if (!TEST_ptr(fixture->srv_ctx = ossl_cmp_mock_srv_new())
+            || !OSSL_CMP_SRV_CTX_set_accept_unprotected(fixture->srv_ctx, 1)
+            || !ossl_cmp_mock_srv_set1_certOut(fixture->srv_ctx, client_cert)
+            || (srv_cmp_ctx =
+                OSSL_CMP_SRV_CTX_get0_cmp_ctx(fixture->srv_ctx)) == NULL
+            || !OSSL_CMP_CTX_set1_clCert(srv_cmp_ctx, server_cert)
+            || !OSSL_CMP_CTX_set1_pkey(srv_cmp_ctx, server_key))
+        goto err;
+    if (!TEST_ptr(fixture->cmp_ctx = ctx = OSSL_CMP_CTX_new())
+            || !OSSL_CMP_CTX_set_log_cb(fixture->cmp_ctx, print_to_bio_out)
+            || !OSSL_CMP_CTX_set_transfer_cb(ctx, OSSL_CMP_CTX_server_perform)
+            || !OSSL_CMP_CTX_set_transfer_cb_arg(ctx, fixture->srv_ctx)
+            || !OSSL_CMP_CTX_set_option(ctx, OSSL_CMP_OPT_UNPROTECTED_SEND, 1)
+            || !OSSL_CMP_CTX_set_option(ctx, OSSL_CMP_OPT_UNPROTECTED_ERRORS, 1)
+            || !OSSL_CMP_CTX_set1_oldCert(ctx, client_cert)
+            || !OSSL_CMP_CTX_set1_pkey(ctx, client_key)
+            || !OSSL_CMP_CTX_set1_srvCert(ctx, server_cert)
+            || !OSSL_CMP_CTX_set1_referenceValue(ctx, ref, sizeof(ref)))
+        goto err;
+    fixture->exec_cert_ses_cb = NULL;
+    return fixture;
+
+ err:
+    tear_down(fixture);
+    return NULL;
+}
+
+static int execute_exec_RR_ses_test(CMP_SES_TEST_FIXTURE *fixture)
+{
+    return TEST_int_eq(fixture->expected,
+                       OSSL_CMP_exec_RR_ses(fixture->cmp_ctx) == client_cert);
+}
+
+static int execute_exec_GENM_ses_test(CMP_SES_TEST_FIXTURE *fixture)
+{
+    STACK_OF(OSSL_CMP_ITAV) *itavs = NULL;
+    if (!TEST_ptr(itavs = OSSL_CMP_exec_GENM_ses(fixture->cmp_ctx)))
+        return 0;
+    sk_OSSL_CMP_ITAV_pop_free(itavs, OSSL_CMP_ITAV_free);
+    /* TODO: check if the returned value is the expected one (same as sent) */
+    return 1;
+}
+
+static int execute_exec_certrequest_ses_test(CMP_SES_TEST_FIXTURE *fixture)
+{
+    X509 *res;
+
+    if (fixture->expected == 0)
+        return TEST_ptr_null(fixture->exec_cert_ses_cb(fixture->cmp_ctx));
+
+    if (!TEST_ptr(res = fixture->exec_cert_ses_cb(fixture->cmp_ctx))
+            || !TEST_int_eq(X509_cmp(res, client_cert), 0))
+        return 0;
+    /* TODO: check that cerfConf has been exchanged unless implicitConfirm */
+    if (fixture->caPubs != NULL) {
+        STACK_OF(X509) *caPubs = OSSL_CMP_CTX_get1_caPubs(fixture->cmp_ctx);
+        int ret = TEST_int_eq(STACK_OF_X509_cmp(fixture->caPubs, caPubs), 0);
+
+        sk_X509_pop_free(caPubs, X509_free);
+        return ret;
+    }
+    return 1;
+}
+
+static int test_exec_RR_ses(void)
+{
+    SETUP_TEST_FIXTURE(CMP_SES_TEST_FIXTURE, set_up);
+    fixture->expected = 1;
+    EXECUTE_TEST(execute_exec_RR_ses_test, tear_down);
+    return result;
+}
+
+static int test_exec_RR_ses_receive_error(void)
+{
+    SETUP_TEST_FIXTURE(CMP_SES_TEST_FIXTURE, set_up);
+    ossl_cmp_mock_srv_set_statusInfo(fixture->srv_ctx,
+                                     OSSL_CMP_PKISTATUS_rejection,
+                                     OSSL_CMP_CTX_FAILINFO_signerNotTrusted,
+                                     "test string");
+    ossl_cmp_mock_srv_set_send_error(fixture->srv_ctx, 1);
+    fixture->expected = 0;
+    EXECUTE_TEST(execute_exec_RR_ses_test, tear_down);
+    return result;
+}
+
+static int test_exec_IR_ses(void)
+{
+    SETUP_TEST_FIXTURE(CMP_SES_TEST_FIXTURE, set_up);
+    fixture->exec_cert_ses_cb = OSSL_CMP_exec_IR_ses;
+    fixture->expected = 1;
+    fixture->caPubs = sk_X509_new_null();
+    sk_X509_push(fixture->caPubs, server_cert);
+    sk_X509_push(fixture->caPubs, server_cert);
+    ossl_cmp_mock_srv_set1_caPubsOut(fixture->srv_ctx, fixture->caPubs);
+    EXECUTE_TEST(execute_exec_certrequest_ses_test, tear_down);
+    return result;
+}
+
+static const int checkAfter = 1;
+static int test_exec_IR_ses_poll(void)
+{
+    SETUP_TEST_FIXTURE(CMP_SES_TEST_FIXTURE, set_up);
+    fixture->exec_cert_ses_cb = OSSL_CMP_exec_IR_ses;
+    fixture->expected = 1;
+    ossl_cmp_mock_srv_set_pollCount(fixture->srv_ctx, 2);
+    ossl_cmp_mock_srv_set_checkAfterTime(fixture->srv_ctx, checkAfter);
+    EXECUTE_TEST(execute_exec_certrequest_ses_test, tear_down);
+    /* TODO: check that 2 rounds are done or session takes 2..3 seconds */
+    return result;
+}
+
+static int test_exec_IR_ses_poll_timeout(void)
+{
+    const int pollCount = 3;
+    const int tout = pollCount * checkAfter;
+
+    SETUP_TEST_FIXTURE(CMP_SES_TEST_FIXTURE, set_up);
+    fixture->exec_cert_ses_cb = OSSL_CMP_exec_IR_ses;
+    fixture->expected = 0;
+    ossl_cmp_mock_srv_set_pollCount(fixture->srv_ctx, pollCount + 1);
+    ossl_cmp_mock_srv_set_checkAfterTime(fixture->srv_ctx, checkAfter);
+    OSSL_CMP_CTX_set_option(fixture->cmp_ctx, OSSL_CMP_OPT_TOTAL_TIMEOUT, tout);
+    EXECUTE_TEST(execute_exec_certrequest_ses_test, tear_down);
+    return result;
+}
+
+
+static int test_exec_CR_ses(void)
+{
+    SETUP_TEST_FIXTURE(CMP_SES_TEST_FIXTURE, set_up);
+    fixture->exec_cert_ses_cb = OSSL_CMP_exec_CR_ses;
+    fixture->expected = 1;
+    EXECUTE_TEST(execute_exec_certrequest_ses_test, tear_down);
+    return result;
+}
+
+static int test_exec_CR_ses_implicit_confirm(void)
+{
+    SETUP_TEST_FIXTURE(CMP_SES_TEST_FIXTURE, set_up);
+    fixture->exec_cert_ses_cb = OSSL_CMP_exec_CR_ses;
+    fixture->expected = 1;
+    OSSL_CMP_CTX_set_option(fixture->cmp_ctx,
+                            OSSL_CMP_OPT_IMPLICIT_CONFIRM, 1);
+    OSSL_CMP_SRV_CTX_set_grant_implicit_confirm(fixture->srv_ctx, 1);
+    EXECUTE_TEST(execute_exec_certrequest_ses_test, tear_down);
+    return result;
+}
+
+static int test_exec_KUR_ses(void)
+{
+    SETUP_TEST_FIXTURE(CMP_SES_TEST_FIXTURE, set_up);
+    fixture->exec_cert_ses_cb = OSSL_CMP_exec_KUR_ses;
+    fixture->expected = 1;
+    EXECUTE_TEST(execute_exec_certrequest_ses_test, tear_down);
+    return result;
+}
+
+static int test_exec_P10CR_ses(void)
+{
+    X509_REQ *req = NULL;
+
+    SETUP_TEST_FIXTURE(CMP_SES_TEST_FIXTURE, set_up);
+    fixture->exec_cert_ses_cb = OSSL_CMP_exec_P10CR_ses;
+    fixture->expected = 1;
+    if (!TEST_ptr(req = load_csr(pkcs10_f))
+            || !TEST_true(OSSL_CMP_CTX_set1_p10CSR(fixture->cmp_ctx, req))) {
+        tear_down(fixture);
+        fixture = NULL;
+    }
+    X509_REQ_free(req);
+    EXECUTE_TEST(execute_exec_certrequest_ses_test, tear_down);
+    return result;
+}
+
+static int execute_try_certreq_poll_test(CMP_SES_TEST_FIXTURE *fixture)
+{
+    OSSL_CMP_CTX *ctx = fixture->cmp_ctx;
+    int check_after;
+    const int CHECK_AFTER = 5;
+    const int TYPE = OSSL_CMP_KUR;
+
+    ossl_cmp_mock_srv_set_pollCount(fixture->srv_ctx, 3);
+    ossl_cmp_mock_srv_set_checkAfterTime(fixture->srv_ctx, CHECK_AFTER);
+    return TEST_int_eq(-1, OSSL_CMP_try_certreq(ctx, TYPE, &check_after))
+        && check_after == CHECK_AFTER
+        && TEST_ptr_eq(OSSL_CMP_CTX_get0_newCert(ctx), NULL)
+        && TEST_int_eq(-1, OSSL_CMP_try_certreq(ctx, TYPE, &check_after))
+        && check_after == CHECK_AFTER
+        && TEST_ptr_eq(OSSL_CMP_CTX_get0_newCert(ctx), NULL)
+        && TEST_int_eq(fixture->expected, OSSL_CMP_try_certreq(ctx, TYPE, NULL))
+        && TEST_int_eq(0, X509_cmp(OSSL_CMP_CTX_get0_newCert(ctx), client_cert));
+}
+
+static int test_try_certreq_poll(void)
+{
+    SETUP_TEST_FIXTURE(CMP_SES_TEST_FIXTURE, set_up);
+    fixture->expected = 1;
+    EXECUTE_TEST(execute_try_certreq_poll_test, tear_down);
+    return result;
+}
+
+static int execute_try_certreq_poll_abort_test(CMP_SES_TEST_FIXTURE *fixture)
+{
+    OSSL_CMP_CTX *ctx = fixture->cmp_ctx;
+    int check_after;
+    const int CHECK_AFTER = INT_MAX;
+    const int TYPE = OSSL_CMP_CR;
+
+    ossl_cmp_mock_srv_set_pollCount(fixture->srv_ctx, 3);
+    ossl_cmp_mock_srv_set_checkAfterTime(fixture->srv_ctx, CHECK_AFTER);
+    return TEST_int_eq(-1, OSSL_CMP_try_certreq(ctx, TYPE, &check_after))
+        && check_after == CHECK_AFTER
+        && TEST_ptr_eq(OSSL_CMP_CTX_get0_newCert(ctx), NULL)
+        && TEST_int_eq(fixture->expected, OSSL_CMP_try_certreq(ctx, -1, NULL))
+        && TEST_ptr_eq(OSSL_CMP_CTX_get0_newCert(fixture->cmp_ctx), NULL);
+}
+
+static int test_try_certreq_poll_abort(void)
+{
+    SETUP_TEST_FIXTURE(CMP_SES_TEST_FIXTURE, set_up);
+    fixture->expected = 1;
+    EXECUTE_TEST(execute_try_certreq_poll_abort_test, tear_down);
+    return result;
+}
+
+static int test_exec_GENM_ses(void)
+{
+    SETUP_TEST_FIXTURE(CMP_SES_TEST_FIXTURE, set_up);
+    EXECUTE_TEST(execute_exec_GENM_ses_test, tear_down);
+    return result;
+}
+
+static int execute_exchange_certConf_test(CMP_SES_TEST_FIXTURE *fixture)
+{
+    int res =
+        ossl_cmp_exchange_certConf(fixture->cmp_ctx,
+                                   OSSL_CMP_PKIFAILUREINFO_addInfoNotAvailable,
+                                   "abcdefg");
+    return TEST_int_eq(fixture->expected, res);
+}
+
+static int execute_exchange_error_test(CMP_SES_TEST_FIXTURE *fixture)
+{
+    int res =
+        ossl_cmp_exchange_error(fixture->cmp_ctx,
+                                OSSL_CMP_PKISTATUS_rejection,
+                                1 << OSSL_CMP_PKIFAILUREINFO_unsupportedVersion,
+                                "foo_status", 999, "foo_details");
+
+    return TEST_int_eq(fixture->expected, res);
+}
+
+static int test_exchange_certConf(void)
+{
+    SETUP_TEST_FIXTURE(CMP_SES_TEST_FIXTURE, set_up);
+    fixture->expected = 0; /* client should not send certConf immediately */
+    if (!ossl_cmp_ctx_set0_newCert(fixture->cmp_ctx, X509_dup(client_cert))) {
+        tear_down(fixture);
+        fixture = NULL;
+    }
+    EXECUTE_TEST(execute_exchange_certConf_test, tear_down);
+    return result;
+}
+
+static int test_exchange_error(void)
+{
+    SETUP_TEST_FIXTURE(CMP_SES_TEST_FIXTURE, set_up);
+    fixture->expected = 1; /* client may send error any time */
+    EXECUTE_TEST(execute_exchange_error_test, tear_down);
+    return result;
+}
+
+void cleanup_tests(void)
+{
+    X509_free(server_cert);
+    EVP_PKEY_free(server_key);
+    X509_free(client_cert);
+    EVP_PKEY_free(client_key);
+    return;
+}
+
+int setup_tests(void)
+{
+    if (!test_skip_common_options()) {
+        TEST_error("Error parsing test options\n");
+        return 0;
+    }
+
+    if (!TEST_ptr(server_key_f = test_get_argument(0))
+            || !TEST_ptr(server_cert_f = test_get_argument(1))
+            || !TEST_ptr(client_key_f = test_get_argument(2))
+            || !TEST_ptr(client_cert_f = test_get_argument(3))
+            || !TEST_ptr(pkcs10_f = test_get_argument(4))) {
+        TEST_error("usage: cmp_client_test server.key server.crt client.key client.crt client.csr\n");
+        return 0;
+    }
+
+    if (!TEST_ptr(server_key = load_pem_key(server_key_f))
+            || !TEST_ptr(server_cert = load_pem_cert(server_cert_f))
+            || !TEST_ptr(client_key = load_pem_key(client_key_f))
+            || !TEST_ptr(client_cert = load_pem_cert(client_cert_f))
+            || !TEST_int_eq(1, RAND_bytes(ref, sizeof(ref)))) {
+        cleanup_tests();
+        return 0;
+    }
+
+    ADD_TEST(test_exec_RR_ses);
+    ADD_TEST(test_exec_RR_ses_receive_error);
+    ADD_TEST(test_exec_CR_ses);
+    ADD_TEST(test_exec_CR_ses_implicit_confirm);
+    ADD_TEST(test_exec_IR_ses);
+    ADD_TEST(test_exec_IR_ses_poll);
+    ADD_TEST(test_exec_IR_ses_poll_timeout);
+    ADD_TEST(test_exec_KUR_ses);
+    ADD_TEST(test_exec_P10CR_ses);
+    ADD_TEST(test_try_certreq_poll);
+    ADD_TEST(test_try_certreq_poll_abort);
+    ADD_TEST(test_exec_GENM_ses);
+    ADD_TEST(test_exchange_certConf);
+    ADD_TEST(test_exchange_error);
+    return 1;
+}
+
+#else /* !defined (NDEBUG) */
+
+int setup_tests(void)
+{
+    TEST_note("CMP session tests are disabled in this build (NDEBUG).");
+    return 1;
+}
+
+#endif
diff --git a/test/cmp_ctx_test.c b/test/cmp_ctx_test.c
index 6da9863179..bbbd056ba5 100644
--- a/test/cmp_ctx_test.c
+++ b/test/cmp_ctx_test.c
@@ -535,16 +535,16 @@ static X509_STORE *X509_STORE_new_1(void)
                              STACK_OF(TYPE)*, NULL, IS_0, \
                              sk_##TYPE##_new_null(), sk_##TYPE##_free)
 
-typedef OSSL_HTTP_bio_cb_t OSSL_cmp_http_cb_t;
+typedef OSSL_HTTP_bio_cb_t OSSL_CMP_http_cb_t;
 #define DEFINE_SET_CB_TEST(FIELD) \
-    static OSSL_cmp_##FIELD##_t OSSL_CMP_CTX_get_##FIELD(const CMP_CTX *ctx) \
+    static OSSL_CMP_##FIELD##_t OSSL_CMP_CTX_get_##FIELD(const CMP_CTX *ctx) \
     { \
         if (ctx == NULL) \
             CMPerr(0, CMP_R_NULL_ARGUMENT); \
         return ctx == NULL ? NULL /* cannot use ERR(NULL) here */ : ctx->FIELD;\
     } \
     DEFINE_SET_GET_BASE_TEST(OSSL_CMP_CTX, set, get, 0, FIELD, \
-                             OSSL_cmp_##FIELD##_t, NULL, IS_0, \
+                             OSSL_CMP_##FIELD##_t, NULL, IS_0, \
                              test_##FIELD, DROP)
 #define DEFINE_SET_GET_P_VOID_TEST(FIELD) \
     DEFINE_SET_GET_BASE_TEST(OSSL_CMP_CTX, set, get, 0, FIELD, void *, \
diff --git a/test/cmp_msg_test.c b/test/cmp_msg_test.c
index 3919480610..2111a2127d 100644
--- a/test/cmp_msg_test.c
+++ b/test/cmp_msg_test.c
@@ -162,6 +162,7 @@ static int test_cmp_create_ir_protection_fails(void)
     fixture->expected = 0;
     if (!TEST_true(OSSL_CMP_CTX_set1_pkey(fixture->cmp_ctx, newkey))
             || !TEST_true(SET_OPT_UNPROTECTED_SEND(fixture->cmp_ctx, 0))
+            /* newkey used by default for signing does not match cert: */
             || !TEST_true(OSSL_CMP_CTX_set1_clCert(fixture->cmp_ctx, cert))) {
         tear_down(fixture);
         fixture = NULL;
@@ -357,8 +358,7 @@ static int test_cmp_create_genm(void)
     SETUP_TEST_FIXTURE(CMP_MSG_TEST_FIXTURE, set_up);
     fixture->expected = 1;
     iv = OSSL_CMP_ITAV_create(OBJ_nid2obj(NID_id_it_implicitConfirm), NULL);
-    if (!TEST_true(SET_OPT_UNPROTECTED_SEND(fixture->cmp_ctx, 1))
-            || !TEST_ptr(iv)
+    if (!TEST_ptr(iv)
             || !TEST_true(OSSL_CMP_CTX_push0_genm_ITAV(fixture->cmp_ctx, iv))) {
         OSSL_CMP_ITAV_free(iv);
         tear_down(fixture);
diff --git a/test/cmp_testlib.c b/test/cmp_testlib.c
index 6c696eafaa..12ef4f3514 100644
--- a/test/cmp_testlib.c
+++ b/test/cmp_testlib.c
@@ -126,3 +126,9 @@ int STACK_OF_X509_push1(STACK_OF(X509) *sk, X509 *cert)
         X509_free(cert); /* down-ref */
     return res;
 }
+
+int print_to_bio_out(const char *func, const char *file, int line,
+                     OSSL_CMP_severity level, const char *msg)
+{
+    return OSSL_CMP_print_to_bio(bio_out, func, file, line, level, msg);
+}
diff --git a/test/cmp_testlib.h b/test/cmp_testlib.h
index f1bc55c118..e5f1af8f04 100644
--- a/test/cmp_testlib.h
+++ b/test/cmp_testlib.h
@@ -30,6 +30,8 @@ int valid_asn1_encoding(const OSSL_CMP_MSG *msg);
 EVP_PKEY *gen_rsa(void);
 int STACK_OF_X509_cmp(const STACK_OF(X509) *sk1, const STACK_OF(X509) *sk2);
 int STACK_OF_X509_push1(STACK_OF(X509) *sk, X509 *cert);
+int print_to_bio_out(const char *func, const char *file, int line,
+                     OSSL_CMP_severity level, const char *msg);
 # endif
 
 #endif /* OSSL_TEST_CMP_TESTLIB_H */
diff --git a/test/cmp_vfy_test.c b/test/cmp_vfy_test.c
index f0b9b50b01..9fca675ef1 100644
--- a/test/cmp_vfy_test.c
+++ b/test/cmp_vfy_test.c
@@ -44,12 +44,6 @@ static void tear_down(CMP_VFY_TEST_FIXTURE *fixture)
     OPENSSL_free(fixture);
 }
 
-static int print_to_bio_out(const char *func, const char *file, int line,
-                            OSSL_CMP_severity level, const char *msg)
-{
-    return OSSL_CMP_print_to_bio(bio_out, func, file, line, level, msg);
-}
-
 static time_t test_time_valid = 0, test_time_after_expiration = 0;
 
 static CMP_VFY_TEST_FIXTURE *set_up(const char *const test_case_name)
diff --git a/test/recipes/65-test_cmp_msg.t b/test/recipes/65-test_cmp_client.t
similarity index 74%
copy from test/recipes/65-test_cmp_msg.t
copy to test/recipes/65-test_cmp_client.t
index e1b08ccfa2..52019942f6 100644
--- a/test/recipes/65-test_cmp_msg.t
+++ b/test/recipes/65-test_cmp_client.t
@@ -12,13 +12,16 @@ use strict;
 use OpenSSL::Test qw/:DEFAULT data_file/;
 use OpenSSL::Test::Utils;
 
-setup("test_cmp_msg");
+setup("test_cmp_client");
 
 plan skip_all => "This test is not supported in a no-cmp build"
     if disabled("cmp");
 
 plan tests => 1;
 
-ok(run(test(["cmp_msg_test",
+ok(run(test(["cmp_client_test",
+             data_file("server.key"),
              data_file("server.crt"),
-             data_file("pkcs10.der")])));
+             data_file("client.key"),
+             data_file("client.crt"),
+             data_file("client.csr")])));
diff --git a/test/recipes/65-test_cmp_client_data/client.crt b/test/recipes/65-test_cmp_client_data/client.crt
new file mode 100644
index 0000000000..c74d52e965
--- /dev/null
+++ b/test/recipes/65-test_cmp_client_data/client.crt
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB6DCCAUkCCQD6PZPVVOpOKTAKBggqhkjOPQQDAjA4MQswCQYDVQQGEwJERTET
+MBEGA1UECgwKU2llbWVucyBBRzEUMBIGA1UEAwwLdGVzdCBjbGllbnQwHhcNMTkw
+NzI2MTE1OTQyWhcNMTkwODI1MTE1OTQyWjA4MQswCQYDVQQGEwJERTETMBEGA1UE
+CgwKU2llbWVucyBBRzEUMBIGA1UEAwwLdGVzdCBjbGllbnQwgZswEAYHKoZIzj0C
+AQYFK4EEACMDgYYABAHClvNEi0aAVuM3CrR5Pwj3TFKHRXfd04MCSs/p9bzMFlMT
+l6SGCGbFh9ZbU+J2jXYGBtTSSs4O/27lqzXCS8DjQAHMrFq69i5VuUmGXe7jQcfY
+iyKIb+3It0GstBa0hDssxWvBUJADMd+p6R0pA/XjCG0ZqkkHwdzCxEby/oCZPp1g
+3jAKBggqhkjOPQQDAgOBjAAwgYgCQgEJiQwLbHH6E9dLpy+BsZ3m+nfwSvPulS/n
+DMm6ZRi57iItAey9eywxpsKV7DKT+SPjjlAt+l083Smz42Gwpdt+cAJCAXQ2NHqm
+hPYo8Qk5a+Y15PYqm5g5qZ1b5E2FtRHNK8EJzSMrQEzDiI3jO0sgcvzr7kwOsZ7X
+Z8y+DTE4HOoYo6oo
+-----END CERTIFICATE-----
diff --git a/test/recipes/65-test_cmp_client_data/client.csr b/test/recipes/65-test_cmp_client_data/client.csr
new file mode 100644
index 0000000000..c9b6ad4cfb
Binary files /dev/null and b/test/recipes/65-test_cmp_client_data/client.csr differ
diff --git a/test/recipes/65-test_cmp_client_data/client.key b/test/recipes/65-test_cmp_client_data/client.key
new file mode 100644
index 0000000000..56ad079927
--- /dev/null
+++ b/test/recipes/65-test_cmp_client_data/client.key
@@ -0,0 +1,7 @@
+-----BEGIN EC PRIVATE KEY-----
+MIHcAgEBBEIAyT6geQkerhMGSj1ZG0ZhFa1Q5kYEBtnEE/bs5Gdm9L3v0OukExzm
+Nx1XJATGBnCyOM5IzPw1TWXT+BNhSxJYtvOgBwYFK4EEACOhgYkDgYYABAHClvNE
+i0aAVuM3CrR5Pwj3TFKHRXfd04MCSs/p9bzMFlMTl6SGCGbFh9ZbU+J2jXYGBtTS
+Ss4O/27lqzXCS8DjQAHMrFq69i5VuUmGXe7jQcfYiyKIb+3It0GstBa0hDssxWvB
+UJADMd+p6R0pA/XjCG0ZqkkHwdzCxEby/oCZPp1g3g==
+-----END EC PRIVATE KEY-----
diff --git a/test/recipes/65-test_cmp_msg_data/server.crt b/test/recipes/65-test_cmp_client_data/server.crt
similarity index 100%
copy from test/recipes/65-test_cmp_msg_data/server.crt
copy to test/recipes/65-test_cmp_client_data/server.crt
diff --git a/test/recipes/65-test_cmp_vfy_data/server.key b/test/recipes/65-test_cmp_client_data/server.key
similarity index 100%
copy from test/recipes/65-test_cmp_vfy_data/server.key
copy to test/recipes/65-test_cmp_client_data/server.key
diff --git a/util/libcrypto.num b/util/libcrypto.num
index ba27450084..4d57cad19e 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -4802,6 +4802,7 @@ OSSL_CMP_CTX_set1_transactionID         ?	3_0_0	EXIST::FUNCTION:CMP
 OSSL_CMP_CTX_set1_senderNonce           ?	3_0_0	EXIST::FUNCTION:CMP
 OSSL_CMP_log_open                       ?	3_0_0	EXIST::FUNCTION:CMP
 OSSL_CMP_log_close                      ?	3_0_0	EXIST::FUNCTION:CMP
+OSSL_CMP_print_to_bio                   ?	3_0_0	EXIST::FUNCTION:CMP
 OSSL_CMP_print_errors_cb                ?	3_0_0	EXIST::FUNCTION:CMP
 OSSL_CRMF_CERTID_get0_issuer            ?	3_0_0	EXIST::FUNCTION:CRMF
 OSSL_CRMF_CERTID_get0_serialNumber      ?	3_0_0	EXIST::FUNCTION:CRMF
@@ -4937,7 +4938,6 @@ X509_STORE_CTX_print_verify_cb          ?	3_0_0	EXIST::FUNCTION:
 X509_STORE_get1_all_certs               ?	3_0_0	EXIST::FUNCTION:
 OSSL_CMP_validate_msg                   ?	3_0_0	EXIST::FUNCTION:CMP
 OSSL_CMP_validate_cert_path             ?	3_0_0	EXIST::FUNCTION:CMP
-OSSL_CMP_print_to_bio                   ?	3_0_0	EXIST::FUNCTION:CMP
 EVP_PKEY_CTX_set_ecdh_cofactor_mode     ?	3_0_0	EXIST::FUNCTION:EC
 EVP_PKEY_CTX_get_ecdh_cofactor_mode     ?	3_0_0	EXIST::FUNCTION:EC
 EVP_PKEY_CTX_set_ecdh_kdf_type          ?	3_0_0	EXIST::FUNCTION:EC
@@ -4985,6 +4985,14 @@ OSSL_CMP_SRV_CTX_set_send_unprotected_errors ?	3_0_0	EXIST::FUNCTION:CMP
 OSSL_CMP_SRV_CTX_set_accept_unprotected ?	3_0_0	EXIST::FUNCTION:CMP
 OSSL_CMP_SRV_CTX_set_accept_raverified  ?	3_0_0	EXIST::FUNCTION:CMP
 OSSL_CMP_SRV_CTX_set_grant_implicit_confirm ?	3_0_0	EXIST::FUNCTION:CMP
+OSSL_CMP_exec_IR_ses                    ?	3_0_0	EXIST::FUNCTION:CMP
+OSSL_CMP_exec_CR_ses                    ?	3_0_0	EXIST::FUNCTION:CMP
+OSSL_CMP_exec_P10CR_ses                 ?	3_0_0	EXIST::FUNCTION:CMP
+OSSL_CMP_exec_KUR_ses                   ?	3_0_0	EXIST::FUNCTION:CMP
+OSSL_CMP_try_certreq                    ?	3_0_0	EXIST::FUNCTION:CMP
+OSSL_CMP_certConf_cb                    ?	3_0_0	EXIST::FUNCTION:CMP
+OSSL_CMP_exec_RR_ses                    ?	3_0_0	EXIST::FUNCTION:CMP
+OSSL_CMP_exec_GENM_ses                  ?	3_0_0	EXIST::FUNCTION:CMP
 EVP_PKEY_gen                            ?	3_0_0	EXIST::FUNCTION:
 EVP_PKEY_CTX_set_rsa_keygen_bits        ?	3_0_0	EXIST::FUNCTION:RSA
 EVP_PKEY_CTX_set_rsa_keygen_pubexp      ?	3_0_0	EXIST::FUNCTION:RSA
diff --git a/util/missingcrypto.txt b/util/missingcrypto.txt
index 4c3af107d9..cf56fae0a6 100644
--- a/util/missingcrypto.txt
+++ b/util/missingcrypto.txt
@@ -912,9 +912,6 @@ OPENSSL_uni2asc(3)
 OPENSSL_uni2utf8(3)
 OPENSSL_utf82uni(3)
 OSSL_CMP_MSG_http_perform(3)
-OSSL_CMP_exec_GENM_ses(3)
-OSSL_CMP_exec_IR_ses(3)
-OSSL_CMP_exec_KUR_ses(3)
 OSSL_SERIALIZER-DH(7)
 OSSL_SERIALIZER-DSA(7)
 OSSL_SERIALIZER-EC(7)
diff --git a/util/other.syms b/util/other.syms
index 1d2a2689ef..508653f86b 100644
--- a/util/other.syms
+++ b/util/other.syms
@@ -352,7 +352,10 @@ OpenSSL_add_all_ciphers                 define deprecated 1.1.0
 OpenSSL_add_all_digests                 define deprecated 1.1.0
 OpenSSL_add_ssl_algorithms              define
 OSSL_CMP_CTX_set_log_verbosity          define
+OSSL_CMP_CR                             define
 OSSL_CMP_DEFAULT_PORT                   define
+OSSL_CMP_IR                             define
+OSSL_CMP_KUR                            define
 OSSL_CMP_LOG_ALERT                      define
 OSSL_CMP_LOG_CRIT                       define
 OSSL_CMP_LOG_DEBUG                      define
@@ -363,10 +366,12 @@ OSSL_CMP_LOG_NOTICE                     define
 OSSL_CMP_LOG_WARNING                    define
 OSSL_CMP_MSTR_HELPER                    define
 OSSL_CMP_MSTR                           define
+OSSL_CMP_P10CR                          define
+ossl_cmp_allow_unprotected_cb_t         datatype
+OSSL_CMP_certConf_cb_t                  datatype
+OSSL_CMP_log_cb_t                       datatype
 OSSL_CMP_severity                       datatype
-OSSL_cmp_certConf_cb_t                  datatype
-OSSL_cmp_log_cb_t                       datatype
-OSSL_cmp_transfer_cb_t                  datatype
+OSSL_CMP_transfer_cb_t                  datatype
 OSSL_CMP_SRV_cert_request_cb_t          datatype
 OSSL_CMP_SRV_rr_cb_t                    datatype
 OSSL_CMP_SRV_certConf_cb_t              datatype


More information about the openssl-commits mailing list