summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDr. David von Oheimb <David.von.Oheimb@siemens.com>2020-02-15 14:57:32 +0100
committerDr. David von Oheimb <David.von.Oheimb@siemens.com>2020-02-17 07:43:58 +0100
commit31b28ad96aa841ae39d4009ebb15d90f2a2afdab (patch)
tree1c35d270dec05defdb07028911a67dbba82fe65c
parent235595c402bd7815f07f1f3f3babe9fcc247a206 (diff)
downloadopenssl-new-31b28ad96aa841ae39d4009ebb15d90f2a2afdab.tar.gz
chunk 7 of CMP contribution to OpenSSL
add CMP message validation and related tests; while doing so: * add ERR_add_error_mem_bio() to crypto/err/err_prn.c * move ossl_cmp_add_error_txt() as ERR_add_error_txt() to crypto/err/err_prn.c * add X509_STORE_CTX_print_verify_cb() to crypto/x509/t_x509.c, adding internally x509_print_ex_brief(), print_certs(), and print_store_certs() * move {ossl_cmp_,}X509_STORE_get1_certs() to crypto/x509/x509_lu.c Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Bernd Edlinger <bernd.edlinger@hotmail.de> (Merged from https://github.com/openssl/openssl/pull/10620)
-rw-r--r--crypto/cmp/build.info2
-rw-r--r--crypto/cmp/cmp_err.c28
-rw-r--r--crypto/cmp/cmp_local.h14
-rw-r--r--crypto/cmp/cmp_util.c124
-rw-r--r--crypto/cmp/cmp_vfy.c754
-rw-r--r--crypto/err/err_prn.c120
-rw-r--r--crypto/err/openssl.txt21
-rw-r--r--crypto/x509/t_x509.c105
-rw-r--r--crypto/x509/x509_err.c2
-rw-r--r--crypto/x509/x509_lu.c35
-rw-r--r--doc/internal/man3/ossl_cmp_print_log.pod19
-rw-r--r--doc/man3/ERR_put_error.pod31
-rw-r--r--doc/man3/OSSL_CMP_validate_msg.pod86
-rw-r--r--doc/man3/X509_STORE_CTX_set_verify_cb.pod14
-rw-r--r--doc/man3/X509_STORE_get0_param.pod12
-rw-r--r--include/crypto/x509.h1
-rw-r--r--include/openssl/cmp.h5
-rw-r--r--include/openssl/cmperr.h18
-rw-r--r--include/openssl/err.h2
-rw-r--r--include/openssl/x509_vfy.h4
-rw-r--r--include/openssl/x509err.h1
-rw-r--r--test/build.info6
-rw-r--r--test/cmp_ctx_test.c5
-rw-r--r--test/cmp_protect_test.c2
-rw-r--r--test/cmp_vfy_test.c636
-rw-r--r--test/recipes/65-test_cmp_vfy.t36
-rw-r--r--test/recipes/65-test_cmp_vfy_data/EndEntity1.crt16
-rw-r--r--test/recipes/65-test_cmp_vfy_data/EndEntity2.crt13
-rw-r--r--test/recipes/65-test_cmp_vfy_data/IP_waitingStatus_PBM.derbin0 -> 1801 bytes
-rw-r--r--test/recipes/65-test_cmp_vfy_data/IP_waitingStatus_PBM.txt2
-rw-r--r--test/recipes/65-test_cmp_vfy_data/IR_protected.derbin0 -> 968 bytes
-rwxr-xr-xtest/recipes/65-test_cmp_vfy_data/IR_protected_0_extraCerts.derbin0 -> 1087 bytes
-rwxr-xr-xtest/recipes/65-test_cmp_vfy_data/IR_protected_2_extraCerts.derbin0 -> 2833 bytes
-rw-r--r--test/recipes/65-test_cmp_vfy_data/IR_rmprotection.derbin0 -> 1098 bytes
-rw-r--r--test/recipes/65-test_cmp_vfy_data/IR_unprotected.derbin0 -> 838 bytes
-rw-r--r--test/recipes/65-test_cmp_vfy_data/Intermediate_CA.crt12
-rw-r--r--test/recipes/65-test_cmp_vfy_data/Root_CA.crt17
-rw-r--r--test/recipes/65-test_cmp_vfy_data/chain.txt4
-rw-r--r--test/recipes/65-test_cmp_vfy_data/client.crt17
-rwxr-xr-xtest/recipes/65-test_cmp_vfy_data/insta.cert.pem25
-rwxr-xr-xtest/recipes/65-test_cmp_vfy_data/insta.priv.pem27
-rwxr-xr-xtest/recipes/65-test_cmp_vfy_data/insta_ca.cert.pem22
-rw-r--r--test/recipes/65-test_cmp_vfy_data/server.crt17
-rw-r--r--test/recipes/65-test_cmp_vfy_data/server.key27
-rw-r--r--util/libcrypto.num7
45 files changed, 2133 insertions, 156 deletions
diff --git a/crypto/cmp/build.info b/crypto/cmp/build.info
index 760c3423ad..41a5899319 100644
--- a/crypto/cmp/build.info
+++ b/crypto/cmp/build.info
@@ -1,3 +1,3 @@
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_status.c cmp_hdr.c cmp_protect.c cmp_msg.c cmp_vfy.c
diff --git a/crypto/cmp/cmp_err.c b/crypto/cmp/cmp_err.c
index a6d59f9fc4..f82ef9e325 100644
--- a/crypto/cmp/cmp_err.c
+++ b/crypto/cmp/cmp_err.c
@@ -14,6 +14,8 @@
#ifndef OPENSSL_NO_ERR
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_REQUEST_ID), "bad request id"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_CERTID_NOT_FOUND), "certid not found"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_CERTIFICATE_NOT_FOUND),
@@ -50,6 +52,10 @@ static const ERR_STRING_DATA CMP_str_reasons[] = {
"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_VALIDATING_PROTECTION),
+ "error validating protection"},
+ {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_FAILED_EXTRACTING_PUBKEY),
+ "failed extracting pubkey"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_FAILURE_OBTAINING_RANDOM),
"failure obtaining random"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_FAIL_INFO_OUT_OF_RANGE),
@@ -57,19 +63,38 @@ static const ERR_STRING_DATA CMP_str_reasons[] = {
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_INVALID_ARGS), "invalid args"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_MISSING_KEY_INPUT_FOR_CREATING_PROTECTION),
"missing key input for creating protection"},
+ {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_MISSING_KEY_USAGE_DIGITALSIGNATURE),
+ "missing key usage digitalsignature"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_MISSING_PRIVATE_KEY),
"missing private key"},
+ {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_MISSING_PROTECTION), "missing protection"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_MISSING_SENDER_IDENTIFICATION),
"missing sender identification"},
+ {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_MISSING_TRUST_STORE),
+ "missing trust store"},
{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"},
+ {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_NO_SUITABLE_SENDER_CERT),
+ "no suitable sender cert"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_NULL_ARGUMENT), "null argument"},
+ {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_POTENTIALLY_INVALID_CERTIFICATE),
"potentially invalid certificate"},
+ {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_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_TRANSACTIONID_UNMATCHED),
+ "transactionid unmatched"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNEXPECTED_PKIBODY), "unexpected pkibody"},
+ {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"},
@@ -77,8 +102,11 @@ static const ERR_STRING_DATA CMP_str_reasons[] = {
"unsupported algorithm"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNSUPPORTED_KEY_TYPE),
"unsupported key type"},
+ {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNSUPPORTED_PROTECTION_ALG_DHBASEDMAC),
+ "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_PBM_VALUE), "wrong pbm value"},
{0, NULL}
};
diff --git a/crypto/cmp/cmp_local.h b/crypto/cmp/cmp_local.h
index 1e62d4516f..015a3d4e67 100644
--- a/crypto/cmp/cmp_local.h
+++ b/crypto/cmp/cmp_local.h
@@ -746,10 +746,8 @@ int ossl_cmp_asn1_get_int(const ASN1_INTEGER *a);
const char *ossl_cmp_log_parse_metadata(const char *buf,
OSSL_CMP_severity *level, char **func,
char **file, int *line);
-/* workaround for 4096 bytes limitation of ERR_print_errors_cb() */
-void ossl_cmp_add_error_txt(const char *separator, const char *txt);
-# define ossl_cmp_add_error_data(txt) ossl_cmp_add_error_txt(" : ", txt)
-# define ossl_cmp_add_error_line(txt) ossl_cmp_add_error_txt("\n", txt)
+# define ossl_cmp_add_error_data(txt) ERR_add_error_txt(" : ", txt)
+# define ossl_cmp_add_error_line(txt) ERR_add_error_txt("\n", txt)
/* functions manipulating lists of certificates etc could be generally useful */
int ossl_cmp_sk_X509_add1_cert(STACK_OF(X509) *sk, X509 *cert,
int no_dup, int prepend);
@@ -919,4 +917,12 @@ ASN1_BIT_STRING *ossl_cmp_calc_protection(const OSSL_CMP_MSG *msg,
int ossl_cmp_msg_add_extraCerts(OSSL_CMP_CTX *ctx, OSSL_CMP_MSG *msg);
int ossl_cmp_msg_protect(OSSL_CMP_CTX *ctx, OSSL_CMP_MSG *msg);
+/* from cmp_vfy.c */
+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);
+int ossl_cmp_verify_popo(const OSSL_CMP_MSG *msg, int accept_RAVerified);
+
#endif /* !defined OSSL_CRYPTO_CMP_LOCAL_H */
diff --git a/crypto/cmp/cmp_util.c b/crypto/cmp/cmp_util.c
index a68f701062..ad4ae66454 100644
--- a/crypto/cmp/cmp_util.c
+++ b/crypto/cmp/cmp_util.c
@@ -142,100 +142,12 @@ int OSSL_CMP_print_to_bio(BIO *bio, const char *component, const char *file,
level_string, msg) >= 0;
}
-/*
- * auxiliary function for incrementally reporting texts via the error queue
- */
-static void put_error(int lib, const char *func, int reason,
- const char *file, int line)
-{
- ERR_new();
- ERR_set_debug(file, line, func);
- ERR_set_error(lib, reason, NULL /* no data here, so fmt is NULL */);
-}
-
-#define ERR_print_errors_cb_LIMIT 4096 /* size of char buf[] variable there */
-#define TYPICAL_MAX_OUTPUT_BEFORE_DATA 100
-#define MAX_DATA_LEN (ERR_print_errors_cb_LIMIT-TYPICAL_MAX_OUTPUT_BEFORE_DATA)
-void ossl_cmp_add_error_txt(const char *separator, const char *txt)
-{
- const char *file = NULL;
- int line;
- const char *func = NULL;
- const char *data = NULL;
- int flags;
- unsigned long err = ERR_peek_last_error();
-
- if (separator == NULL)
- separator = "";
- if (err == 0)
- put_error(ERR_LIB_CMP, NULL, 0, "", 0);
-
- do {
- size_t available_len, data_len;
- const char *curr = txt, *next = txt;
- char *tmp;
-
- ERR_peek_last_error_all(&file, &line, &func, &data, &flags);
- if ((flags & ERR_TXT_STRING) == 0) {
- data = "";
- separator = "";
- }
- data_len = strlen(data);
-
- /* workaround for limit of ERR_print_errors_cb() */
- if (data_len >= MAX_DATA_LEN
- || strlen(separator) >= (size_t)(MAX_DATA_LEN - data_len))
- available_len = 0;
- else
- available_len = MAX_DATA_LEN - data_len - strlen(separator) - 1;
- /* MAX_DATA_LEN > available_len >= 0 */
-
- if (separator[0] == '\0') {
- const size_t len_next = strlen(next);
-
- if (len_next <= available_len) {
- next += len_next;
- curr = NULL; /* no need to split */
- }
- else {
- next += available_len;
- curr = next; /* will split at this point */
- }
- } else {
- while (*next != '\0' && (size_t)(next - txt) <= available_len) {
- curr = next;
- next = strstr(curr, separator);
- if (next != NULL)
- next += strlen(separator);
- else
- next = curr + strlen(curr);
- }
- if ((size_t)(next - txt) <= available_len)
- curr = NULL; /* the above loop implies *next == '\0' */
- }
- if (curr != NULL) {
- /* split error msg at curr since error data would get too long */
- if (curr != txt) {
- tmp = OPENSSL_strndup(txt, curr - txt);
- if (tmp == NULL)
- return;
- ERR_add_error_data(2, separator, tmp);
- OPENSSL_free(tmp);
- }
- put_error(ERR_LIB_CMP, func, err, file, line);
- txt = curr;
- } else {
- ERR_add_error_data(2, separator, txt);
- txt = next; /* finished */
- }
- } while (*txt != '\0');
-}
-
+#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)
{
unsigned long err;
- char msg[ERR_print_errors_cb_LIMIT];
+ char msg[ERR_PRINT_BUF_SIZE];
const char *file = NULL, *func = NULL, *data = NULL;
int line, flags;
@@ -342,38 +254,6 @@ int ossl_cmp_X509_STORE_add1_certs(X509_STORE *store, STACK_OF(X509) *certs,
return 1;
}
-STACK_OF(X509) *ossl_cmp_X509_STORE_get1_certs(X509_STORE *store)
-{
- int i;
- STACK_OF(X509) *sk;
- STACK_OF(X509_OBJECT) *objs;
-
- if (store == NULL) {
- CMPerr(0, CMP_R_NULL_ARGUMENT);
- return 0;
- }
- if ((sk = sk_X509_new_null()) == NULL)
- return NULL;
- objs = X509_STORE_get0_objects(store);
- for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
- X509 *cert = X509_OBJECT_get0_X509(sk_X509_OBJECT_value(objs, i));
-
- if (cert != NULL) {
- if (!sk_X509_push(sk, cert))
- goto err;
- if (!X509_up_ref(cert)) {
- (void)sk_X509_pop(sk);
- goto err;
- }
- }
- }
- return sk;
-
- err:
- sk_X509_pop_free(sk, X509_free);
- return NULL;
-}
-
/*-
* Builds up the certificate chain of certs as high up as possible using
* the given list of certs containing all possible intermediate certificates and
diff --git a/crypto/cmp/cmp_vfy.c b/crypto/cmp/cmp_vfy.c
new file mode 100644
index 0000000000..437bc3298f
--- /dev/null
+++ b/crypto/cmp/cmp_vfy.c
@@ -0,0 +1,754 @@
+/*
+ * Copyright 2007-2020 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright Nokia 2007-2020
+ * Copyright Siemens AG 2015-2020
+ *
+ * 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
+ */
+
+/* CMP functions for PKIMessage checking */
+
+#include "cmp_local.h"
+#include <openssl/cmp_util.h>
+
+/* explicit #includes not strictly needed since implied by the above: */
+#include <openssl/asn1t.h>
+#include <openssl/cmp.h>
+#include <openssl/crmf.h>
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include "crypto/x509.h"
+
+/*
+ * Verify a message protected by signature according to section 5.1.3.3
+ * (sha1+RSA/DSA or any other algorithm supported by OpenSSL).
+ *
+ * Returns 1 on successful validation and 0 otherwise.
+ */
+static int verify_signature(const OSSL_CMP_CTX *cmp_ctx,
+ const OSSL_CMP_MSG *msg, X509 *cert)
+{
+ EVP_MD_CTX *ctx = NULL;
+ CMP_PROTECTEDPART prot_part;
+ int digest_nid, pk_nid;
+ const EVP_MD *digest = NULL;
+ EVP_PKEY *pubkey = NULL;
+ int len;
+ size_t prot_part_der_len = 0;
+ unsigned char *prot_part_der = NULL;
+ BIO *bio = BIO_new(BIO_s_mem()); /* may be NULL */
+ int res = 0;
+
+ if (!ossl_assert(cmp_ctx != NULL && msg != NULL && cert != NULL))
+ return 0;
+
+ /* verify that keyUsage, if present, contains digitalSignature */
+ if (!cmp_ctx->ignore_keyusage
+ && (X509_get_key_usage(cert) & X509v3_KU_DIGITAL_SIGNATURE) == 0) {
+ CMPerr(0, CMP_R_MISSING_KEY_USAGE_DIGITALSIGNATURE);
+ goto sig_err;
+ }
+
+ pubkey = X509_get_pubkey(cert);
+ if (pubkey == NULL) {
+ CMPerr(0, CMP_R_FAILED_EXTRACTING_PUBKEY);
+ goto sig_err;
+ }
+
+ /* create the DER representation of protected part */
+ prot_part.header = msg->header;
+ prot_part.body = msg->body;
+
+ len = i2d_CMP_PROTECTEDPART(&prot_part, &prot_part_der);
+ if (len < 0 || prot_part_der == NULL)
+ goto end;
+ prot_part_der_len = (size_t) len;
+
+ /* verify signature of protected part */
+ if (!OBJ_find_sigid_algs(OBJ_obj2nid(msg->header->protectionAlg->algorithm),
+ &digest_nid, &pk_nid)
+ || digest_nid == NID_undef || pk_nid == NID_undef
+ || (digest = EVP_get_digestbynid(digest_nid)) == NULL) {
+ CMPerr(0, CMP_R_ALGORITHM_NOT_SUPPORTED);
+ goto sig_err;
+ }
+
+ /* check msg->header->protectionAlg is consistent with public key type */
+ if (EVP_PKEY_type(pk_nid) != EVP_PKEY_base_id(pubkey)) {
+ CMPerr(0, CMP_R_WRONG_ALGORITHM_OID);
+ goto sig_err;
+ }
+ if ((ctx = EVP_MD_CTX_new()) == NULL)
+ goto end;
+ if (EVP_DigestVerifyInit(ctx, NULL, digest, NULL, pubkey)
+ && EVP_DigestVerify(ctx, msg->protection->data,
+ msg->protection->length,
+ prot_part_der, prot_part_der_len) == 1) {
+ res = 1;
+ goto end;
+ }
+
+ sig_err:
+ res = x509_print_ex_brief(bio, cert, X509_FLAG_NO_EXTENSIONS);
+ CMPerr(0, CMP_R_ERROR_VALIDATING_PROTECTION);
+ if (res)
+ ERR_add_error_mem_bio("\n", bio);
+ res = 0;
+
+ end:
+ EVP_MD_CTX_free(ctx);
+ OPENSSL_free(prot_part_der);
+ EVP_PKEY_free(pubkey);
+ BIO_free(bio);
+
+ return res;
+}
+
+/* Verify a message protected with PBMAC */
+static int verify_PBMAC(const OSSL_CMP_MSG *msg,
+ const ASN1_OCTET_STRING *secret)
+{
+ ASN1_BIT_STRING *protection = NULL;
+ int valid = 0;
+
+ /* generate expected protection for the message */
+ if ((protection = ossl_cmp_calc_protection(msg, secret, NULL)) == NULL)
+ return 0; /* failed to generate protection string! */
+
+ valid = msg->protection != NULL && msg->protection->length >= 0
+ && msg->protection->type == protection->type
+ && msg->protection->length == protection->length
+ && CRYPTO_memcmp(msg->protection->data, protection->data,
+ protection->length) == 0;
+ ASN1_BIT_STRING_free(protection);
+ if (!valid)
+ CMPerr(0, CMP_R_WRONG_PBM_VALUE);
+
+ return valid;
+}
+
+/*
+ * Attempt to validate certificate and path using any given store with trusted
+ * certs (possibly including CRLs and a cert verification callback function)
+ * and non-trusted intermediate certs from the given ctx.
+ *
+ * Returns 1 on successful validation and 0 otherwise.
+ */
+int OSSL_CMP_validate_cert_path(OSSL_CMP_CTX *ctx, X509_STORE *trusted_store,
+ X509 *cert)
+{
+ int valid = 0;
+ X509_STORE_CTX *csc = NULL;
+ int err;
+
+ if (ctx == NULL || cert == NULL) {
+ CMPerr(0, CMP_R_NULL_ARGUMENT);
+ return 0;
+ }
+
+ if (trusted_store == NULL) {
+ CMPerr(0, CMP_R_MISSING_TRUST_STORE);
+ return 0;
+ }
+
+ if ((csc = X509_STORE_CTX_new()) == NULL
+ || !X509_STORE_CTX_init(csc, trusted_store,
+ cert, ctx->untrusted_certs))
+ goto err;
+
+ valid = X509_verify_cert(csc) > 0;
+
+ /* make sure suitable error is queued even if callback did not do */
+ err = ERR_peek_last_error();
+ if (!valid && ERR_GET_REASON(err) != CMP_R_POTENTIALLY_INVALID_CERTIFICATE)
+ CMPerr(0, CMP_R_POTENTIALLY_INVALID_CERTIFICATE);
+
+ err:
+ X509_STORE_CTX_free(csc);
+ return valid;
+}
+
+/* Return 0 if expect_name != NULL and there is no matching actual_name */
+static int check_name(OSSL_CMP_CTX *ctx,
+ const char *actual_desc, const X509_NAME *actual_name,
+ const char *expect_desc, const X509_NAME *expect_name)
+{
+ char *str;
+
+ if (expect_name == NULL)
+ return 1; /* no expectation, thus trivially fulfilled */
+
+ /* make sure that a matching name is there */
+ if (actual_name == NULL) {
+ ossl_cmp_log1(WARN, ctx, "missing %s", actual_desc);
+ return 0;
+ }
+ if (X509_NAME_cmp(actual_name, expect_name) == 0)
+ return 1;
+
+ if ((str = X509_NAME_oneline(actual_name, NULL, 0)) != NULL)
+ ossl_cmp_log2(INFO, ctx, " actual name in %s = %s", actual_desc, str);
+ OPENSSL_free(str);
+ if ((str = X509_NAME_oneline(expect_name, NULL, 0)) != NULL)
+ ossl_cmp_log2(INFO, ctx, " does not match %s = %s", expect_desc, str);
+ OPENSSL_free(str);
+ return 0;
+}
+
+/* Return 0 if skid != NULL and there is no matching subject key ID in cert */
+static int check_kid(OSSL_CMP_CTX *ctx,
+ X509 *cert, const ASN1_OCTET_STRING *skid)
+{
+ char *actual, *expect;
+ const ASN1_OCTET_STRING *ckid = X509_get0_subject_key_id(cert);
+
+ if (skid == NULL)
+ return 1; /* no expectation, thus trivially fulfilled */
+
+ /* make sure that the expected subject key identifier is there */
+ if (ckid == NULL) {
+ ossl_cmp_warn(ctx, "missing Subject Key Identifier in certificate");
+ return 0;
+ }
+ if (ASN1_OCTET_STRING_cmp(ckid, skid) == 0)
+ return 1;
+
+ if ((actual = OPENSSL_buf2hexstr(ckid->data, ckid->length)) != NULL)
+ ossl_cmp_log1(INFO, ctx, " cert Subject Key Identifier = %s", actual);
+ if ((expect = OPENSSL_buf2hexstr(skid->data, skid->length)) != NULL)
+ ossl_cmp_log1(INFO, ctx, " does not match senderKID = %s", expect);
+ OPENSSL_free(expect);
+ OPENSSL_free(actual);
+ return 0;
+}
+
+static int already_checked(X509 *cert, const STACK_OF(X509) *already_checked)
+{
+ int i;
+
+ for (i = sk_X509_num(already_checked /* may be NULL */); i > 0; i--)
+ if (X509_cmp(sk_X509_value(already_checked, i - 1), cert) == 0)
+ return 1;
+ return 0;
+}
+
+/*
+ * Check if the given cert is acceptable as sender cert of the given message.
+ * The subject DN must match, the subject key ID as well if present in the msg,
+ * and the cert must be current (checked if ctx->trusted is not NULL).
+ * Note that cert revocation etc. is checked by OSSL_CMP_validate_cert_path().
+ *
+ * Returns 0 on error or not acceptable, else 1.
+ */
+static int cert_acceptable(OSSL_CMP_CTX *ctx,
+ const char *desc1, const char *desc2, X509 *cert,
+ const STACK_OF(X509) *already_checked1,
+ const STACK_OF(X509) *already_checked2,
+ const OSSL_CMP_MSG *msg)
+{
+ X509_STORE *ts = ctx->trusted;
+ char *sub, *iss;
+ X509_VERIFY_PARAM *vpm = ts != NULL ? X509_STORE_get0_param(ts) : NULL;
+ int time_cmp;
+
+ ossl_cmp_log2(INFO, ctx, " considering %s %s with..", desc1, desc2);
+ if ((sub = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0)) != NULL)
+ ossl_cmp_log1(INFO, ctx, " subject = %s", sub);
+ if ((iss = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0)) != NULL)
+ ossl_cmp_log1(INFO, ctx, " issuer = %s", iss);
+ OPENSSL_free(iss);
+ OPENSSL_free(sub);
+
+ if (already_checked(cert, already_checked1)
+ || already_checked(cert, already_checked2)) {
+ ossl_cmp_info(ctx, " cert has already been checked");
+ return 0;
+ }
+
+ time_cmp = X509_cmp_timeframe(vpm, X509_get0_notBefore(cert),
+ X509_get0_notAfter(cert));
+ if (time_cmp != 0) {
+ ossl_cmp_warn(ctx, time_cmp > 0 ? "cert has expired"
+ : "cert is not yet valid");
+ return 0;
+ }
+
+ if (!check_name(ctx,
+ "cert subject", X509_get_subject_name(cert),
+ "sender field", msg->header->sender->d.directoryName))
+ return 0;
+
+ if (!check_kid(ctx, cert, msg->header->senderKID))
+ return 0;
+ /* acceptable also if there is no senderKID in msg header */
+ ossl_cmp_info(ctx, " cert is acceptable");
+ return 1;
+}
+
+static int check_msg_valid_cert(OSSL_CMP_CTX *ctx, X509_STORE *store,
+ X509 *scrt, const OSSL_CMP_MSG *msg)
+{
+ if (!verify_signature(ctx, msg, scrt)) {
+ ossl_cmp_warn(ctx, "msg signature verification failed");
+ return 0;
+ }
+ if (!OSSL_CMP_validate_cert_path(ctx, store, scrt)) {
+ ossl_cmp_warn(ctx, "cert path validation failed");
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Exceptional handling for 3GPP TS 33.310 [3G/LTE Network Domain Security
+ * (NDS); Authentication Framework (AF)], only to use for IP and if the ctx
+ * option is explicitly set: use self-issued certificates from extraCerts as
+ * trust anchor to validate sender cert and msg -
+ * provided it also can validate the newly enrolled certificate
+ */
+static int check_msg_valid_cert_3gpp(OSSL_CMP_CTX *ctx, X509 *scrt,
+ const OSSL_CMP_MSG *msg)
+{
+ int valid = 0;
+ X509_STORE *store = X509_STORE_new();
+
+ if (store != NULL /* store does not include CRLs */
+ && ossl_cmp_X509_STORE_add1_certs(store, msg->extraCerts,
+ 1 /* self-issued only */))
+ valid = check_msg_valid_cert(ctx, store, scrt, msg);
+ if (valid) {
+ /*
+ * verify that the newly enrolled certificate (which is assumed to have
+ * rid == 0) can also be validated with the same trusted store
+ */
+ EVP_PKEY *privkey = OSSL_CMP_CTX_get0_newPkey(ctx, 1);
+ OSSL_CMP_CERTRESPONSE *crep =
+ ossl_cmp_certrepmessage_get0_certresponse(msg->body->value.ip, 0);
+ X509 *newcrt = ossl_cmp_certresponse_get1_certificate(privkey, crep);
+ /*
+ * maybe better use get_cert_status() from cmp_client.c, which catches
+ * errors
+ */
+ valid = OSSL_CMP_validate_cert_path(ctx, store, newcrt);
+ X509_free(newcrt);
+ }
+ X509_STORE_free(store);
+ return valid;
+}
+
+/*
+ * Try all certs in given list for verifying msg, normally or in 3GPP mode.
+ * If already_checked1 == NULL then certs are assumed to be the msg->extraCerts.
+ */
+static int check_msg_with_certs(OSSL_CMP_CTX *ctx, STACK_OF(X509) *certs,
+ const char *desc,
+ const STACK_OF(X509) *already_checked1,
+ const STACK_OF(X509) *already_checked2,
+ const OSSL_CMP_MSG *msg, int mode_3gpp)
+{
+ int in_extraCerts = already_checked1 == NULL;
+ int n_acceptable_certs = 0;
+ int i;
+
+ if (sk_X509_num(certs) <= 0) {
+ ossl_cmp_log1(WARN, ctx, "no %s", desc);
+ return 0;
+ }
+
+ for (i = 0; i < sk_X509_num(certs); i++) { /* certs may be NULL */
+ X509 *cert = sk_X509_value(certs, i);
+
+ if (!ossl_assert(cert != NULL))
+ return 0;
+ if (!cert_acceptable(ctx, "cert from", desc, cert,
+ already_checked1, already_checked2, msg))
+ continue;
+ n_acceptable_certs++;
+ if (mode_3gpp ? check_msg_valid_cert_3gpp(ctx, cert, msg)
+ : check_msg_valid_cert(ctx, ctx->trusted, cert, msg)) {
+ /* store successful sender cert for further msgs in transaction */
+ if (!X509_up_ref(cert))
+ return 0;
+ if (!ossl_cmp_ctx_set0_validatedSrvCert(ctx, cert)) {
+ X509_free(cert);
+ return 0;
+ }
+ return 1;
+ }
+ }
+ if (in_extraCerts && n_acceptable_certs == 0)
+ ossl_cmp_warn(ctx, "no acceptable cert in extraCerts");
+ return 0;
+}
+
+/*
+ * Verify msg trying first ctx->untrusted_certs, which should include extraCerts
+ * at its front, then trying the trusted certs in truststore (if any) of ctx.
+ */
+static int check_msg_all_certs(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *msg,
+ int mode_3gpp)
+{
+ int ret = 0;
+
+ ossl_cmp_info(ctx,
+ mode_3gpp ? "failed; trying now 3GPP mode trusting extraCerts"
+ : "trying first normal mode using trust store");
+ if (check_msg_with_certs(ctx, msg->extraCerts, "extraCerts",
+ NULL, NULL, msg, mode_3gpp))
+ return 1;
+ if (check_msg_with_certs(ctx, ctx->untrusted_certs, "untrusted certs",
+ msg->extraCerts, NULL, msg, mode_3gpp))
+ return 1;
+
+ if (ctx->trusted == NULL) {
+ ossl_cmp_warn(ctx, mode_3gpp ? "no self-issued extraCerts"
+ : "no trusted store");
+ } else {
+ STACK_OF(X509) *trusted = X509_STORE_get1_all_certs(ctx->trusted);
+ ret = check_msg_with_certs(ctx, trusted,
+ mode_3gpp ? "self-issued extraCerts"
+ : "certs in trusted store",
+ msg->extraCerts, ctx->untrusted_certs,
+ msg, mode_3gpp);
+ sk_X509_pop_free(trusted, X509_free);
+ }
+ return ret;
+}
+
+/* verify message signature with any acceptable and valid candidate cert */
+static int check_msg_find_cert(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *msg)
+{
+ X509 *scrt = ctx->validatedSrvCert; /* previous successful sender cert */
+ GENERAL_NAME *sender = msg->header->sender;
+ 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;
+ int res = 0;
+
+ if (sender == NULL || msg->body == NULL)
+ return 0; /* other NULL cases already have been checked */
+ if (sender->type != GEN_DIRNAME) {
+ CMPerr(0, CMP_R_SENDER_GENERALNAME_TYPE_NOT_SUPPORTED);
+ return 0;
+ }
+
+ /*
+ * try first cached scrt, used successfully earlier in same transaction,
+ * for validating this and any further msgs where extraCerts may be left out
+ */
+ (void)ERR_set_mark();
+ if (scrt != NULL
+ && cert_acceptable(ctx, "previously validated", "sender cert", scrt,
+ NULL, NULL, msg)
+ && (check_msg_valid_cert(ctx, ctx->trusted, scrt, msg)
+ || check_msg_valid_cert_3gpp(ctx, scrt, msg))) {
+ (void)ERR_pop_to_mark();
+ return 1;
+ }
+ (void)ERR_pop_to_mark();
+
+ /* release any cached sender cert that proved no more successfully usable */
+ (void)ossl_cmp_ctx_set0_validatedSrvCert(ctx, NULL);
+
+ /* enable clearing irrelevant errors in attempts to validate sender certs */
+ (void)ERR_set_mark();
+ ctx->log_cb = NULL; /* temporarily disable logging diagnostic info */
+
+ if (check_msg_all_certs(ctx, msg, 0 /* using ctx->trusted */)
+ || check_msg_all_certs(ctx, msg, 1 /* 3gpp */)) {
+ /* discard any diagnostic info on trying to use certs */
+ ctx->log_cb = backup_log_cb; /* restore any logging */
+ (void)ERR_pop_to_mark();
+ res = 1;
+ goto end;
+ }
+ /* failed finding a sender cert that verifies the message signature */
+ ctx->log_cb = backup_log_cb; /* restore any logging */
+ (void)ERR_clear_last_mark();
+
+ sname = X509_NAME_oneline(sender->d.directoryName, NULL, 0);
+ skid_str = skid == NULL ? NULL
+ : OPENSSL_buf2hexstr(skid->data, skid->length);
+ if (ctx->log_cb != NULL) {
+ ossl_cmp_info(ctx, "verifying msg signature with valid cert that..");
+ if (sname != NULL)
+ ossl_cmp_log1(INFO, ctx, "matches msg sender name = %s", sname);
+ if (skid_str != NULL)
+ ossl_cmp_log1(INFO, ctx, "matches msg senderKID = %s", skid_str);
+ else
+ ossl_cmp_info(ctx, "while msg header does not contain senderKID");
+ /* re-do the above checks (just) for adding diagnostic information */
+ check_msg_all_certs(ctx, msg, 0 /* using ctx->trusted */);
+ check_msg_all_certs(ctx, msg, 1 /* 3gpp */);
+ }
+
+ CMPerr(0, CMP_R_NO_SUITABLE_SENDER_CERT);
+ if (sname != NULL) {
+ ERR_add_error_txt(NULL, "for msg sender name = ");
+ ERR_add_error_txt(NULL, sname);
+ }
+ if (skid_str != NULL) {
+ ERR_add_error_txt(" and ", "for msg senderKID = ");
+ ERR_add_error_txt(NULL, skid_str);
+ }
+
+ end:
+ OPENSSL_free(sname);
+ OPENSSL_free(skid_str);
+ return res;
+}
+
+/*
+ * Validate the protection of the given PKIMessage using either password-
+ * based mac (PBM) or a signature algorithm. In the case of signature algorithm,
+ * the sender certificate can have been pinned by providing it in ctx->srvCert,
+ * else it is searched in msg->extraCerts, ctx->untrusted_certs, in ctx->trusted
+ * (in this order) and is path is validated against ctx->trusted.
+ *
+ * If ctx->permitTAInExtraCertsForIR is true and when validating a CMP IP msg,
+ * the trust anchor for validating the IP msg may be taken from msg->extraCerts
+ * if a self-issued certificate is found there that can be used to
+ * validate the enrolled certificate returned in the IP.
+ * This is according to the need given in 3GPP TS 33.310.
+ *
+ * Returns 1 on success, 0 on error or validation failed.
+ */
+int OSSL_CMP_validate_msg(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *msg)
+{
+ X509_ALGOR *alg;
+ int nid = NID_undef, pk_nid = NID_undef;
+ const ASN1_OBJECT *algorOID = NULL;
+ X509 *scrt;
+
+ if (ctx == NULL || msg == NULL
+ || msg->header == NULL || msg->body == NULL) {
+ CMPerr(0, CMP_R_NULL_ARGUMENT);
+ return 0;
+ }
+
+ if ((alg = msg->header->protectionAlg) == NULL /* unprotected message */
+ || msg->protection == NULL || msg->protection->data == NULL) {
+ CMPerr(0, CMP_R_MISSING_PROTECTION);
+ return 0;
+ }
+
+ /* determine the nid for the used protection algorithm */
+ X509_ALGOR_get0(&algorOID, NULL, NULL, alg);
+ nid = OBJ_obj2nid(algorOID);
+
+ switch (nid) {
+ /* 5.1.3.1. Shared Secret Information */
+ case NID_id_PasswordBasedMAC:
+ if (verify_PBMAC(msg, ctx->secretValue)) {
+ /*
+ * RFC 4210, 5.3.2: 'Note that if the PKI Message Protection is
+ * "shared secret information", then any certificate transported in
+ * the caPubs field may be directly trusted as a root CA
+ * certificate by the initiator.'
+ */
+ switch (ossl_cmp_msg_get_bodytype(msg)) {
+ case -1:
+ return 0;
+ case OSSL_CMP_PKIBODY_IP:
+ case OSSL_CMP_PKIBODY_CP:
+ case OSSL_CMP_PKIBODY_KUP:
+ case OSSL_CMP_PKIBODY_CCP:
+ if (ctx->trusted != NULL) {
+ STACK_OF(X509) *certs = msg->body->value.ip->caPubs;
+ /* value.ip is same for cp, kup, and ccp */
+
+ if (!ossl_cmp_X509_STORE_add1_certs(ctx->trusted, certs, 0))
+ /* adds both self-issued and not self-issued certs */
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+ return 1;
+ }
+ break;
+
+ /*
+ * 5.1.3.2 DH Key Pairs
+ * Not yet supported
+ */
+ case NID_id_DHBasedMac:
+ CMPerr(0, CMP_R_UNSUPPORTED_PROTECTION_ALG_DHBASEDMAC);
+ break;
+
+ /*
+ * 5.1.3.3. Signature
+ */
+ default:
+ if (!OBJ_find_sigid_algs(OBJ_obj2nid(alg->algorithm), NULL, &pk_nid)
+ || pk_nid == NID_undef) {
+ CMPerr(0, CMP_R_UNKNOWN_ALGORITHM_ID);
+ break;
+ }
+ /* validate sender name of received msg */
+ if (msg->header->sender->type != GEN_DIRNAME) {
+ CMPerr(0, CMP_R_SENDER_GENERALNAME_TYPE_NOT_SUPPORTED);
+ break; /* FR#42: support for more than X509_NAME */
+ }
+ /*
+ * Compare actual sender name of response with expected sender name.
+ * Expected name can be set explicitly or the subject of ctx->srvCert.
+ * Mitigates risk to accept misused certificate of an unauthorized
+ * entity of a trusted hierarchy.
+ */
+ if (!check_name(ctx, "sender DN field",
+ msg->header->sender->d.directoryName,
+ "expected sender", ctx->expected_sender))
+ break;
+ /* Note: if recipient was NULL-DN it could be learned here if needed */
+
+ scrt = ctx->srvCert;
+ if (scrt == NULL) {
+ if (check_msg_find_cert(ctx, msg))
+ return 1;
+ } else { /* use pinned sender cert */
+ /* use ctx->srvCert for signature check even if not acceptable */
+ if (verify_signature(ctx, msg, scrt))
+ return 1;
+ /* call cert_acceptable() for adding diagnostic information */
+ (void)cert_acceptable(ctx, "explicitly set", "sender cert", scrt,
+ NULL, NULL, msg);
+ ossl_cmp_warn(ctx, "msg signature verification failed");
+ CMPerr(0, CMP_R_SRVCERT_DOES_NOT_VALIDATE_MSG);
+ }
+ break;
+ }
+ return 0;
+}
+
+
+/*-
+ * Check received message (i.e., response by server or request from client)
+ * Any msg->extraCerts are prepended to ctx->untrusted_certs
+ *
+ * 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 transaction ID matches the previous transaction ID stored in ctx (if any)
+ * its recipNonce matches the previous senderNonce stored in the ctx (if any)
+ *
+ * If everything is fine:
+ * learns the senderNonce from the received message,
+ * learns the transaction ID if it is not yet in ctx.
+ *
+ * returns body type (which is >= 0) of the message on success, -1 on error
+ */
+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 rcvd_type;
+
+ if (!ossl_assert(ctx != NULL && msg != NULL))
+ return -1;
+
+ if (sk_X509_num(msg->extraCerts) > 10)
+ ossl_cmp_warn(ctx,
+ "received CMP message contains more than 10 extraCerts");
+
+ /* validate message protection */
+ 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))) {
+ 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)) {
+ 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;
+ }
+
+ /* compare received transactionID with the expected one in previous msg */
+ if (ctx->transactionID != NULL
+ && (msg->header->transactionID == NULL
+ || ASN1_OCTET_STRING_cmp(ctx->transactionID,
+ msg->header->transactionID) != 0)) {
+ CMPerr(0, CMP_R_TRANSACTIONID_UNMATCHED);
+ return -1;
+ }
+
+ /* compare received nonce with the one we sent */
+ if (ctx->senderNonce != NULL
+ && (msg->header->recipNonce == NULL
+ || ASN1_OCTET_STRING_cmp(ctx->senderNonce,
+ msg->header->recipNonce) != 0)) {
+ CMPerr(0, CMP_R_RECIPNONCE_UNMATCHED);
+ return -1;
+ }
+
+ /*
+ * RFC 4210 section 5.1.1 states: the recipNonce is copied from
+ * the senderNonce of the previous message in the transaction.
+ * --> Store for setting in next message
+ */
+ if (!ossl_cmp_ctx_set1_recipNonce(ctx, msg->header->senderNonce))
+ return -1;
+
+ /* if not yet present, learn transactionID */
+ if (ctx->transactionID == NULL
+ && !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);
+ return -1;
+ }
+ return rcvd_type;
+}
+
+int ossl_cmp_verify_popo(const OSSL_CMP_MSG *msg, int accept_RAVerified)
+{
+ if (!ossl_assert(msg != NULL && msg->body != NULL))
+ return 0;
+ switch (msg->body->type) {
+ case OSSL_CMP_PKIBODY_P10CR:
+ {
+ X509_REQ *req = msg->body->value.p10cr;
+
+ if (X509_REQ_verify(req, X509_REQ_get0_pubkey(req)) > 0)
+ return 1;
+ CMPerr(0, CMP_R_REQUEST_NOT_ACCEPTED);
+ return 0;
+ }
+ case OSSL_CMP_PKIBODY_IR:
+ case OSSL_CMP_PKIBODY_CR:
+ case OSSL_CMP_PKIBODY_KUR:
+ return OSSL_CRMF_MSGS_verify_popo(msg->body->value.ir,
+ OSSL_CMP_CERTREQID,
+ accept_RAVerified);
+ default:
+ CMPerr(0, CMP_R_PKIBODY_ERROR);
+ return 0;
+ }
+}
diff --git a/crypto/err/err_prn.c b/crypto/err/err_prn.c
index e0184b0771..9a5889829d 100644
--- a/crypto/err/err_prn.c
+++ b/crypto/err/err_prn.c
@@ -17,12 +17,13 @@
#include <openssl/err.h>
#include "err_local.h"
+#define ERR_PRINT_BUF_SIZE 4096
void ERR_print_errors_cb(int (*cb) (const char *str, size_t len, void *u),
void *u)
{
CRYPTO_THREAD_ID tid = CRYPTO_THREAD_get_current_id();
unsigned long l;
- char buf[4096], *hex;
+ char buf[ERR_PRINT_BUF_SIZE], *hex;
const char *lib, *reason;
const char *file, *data, *func;
int line, flags;
@@ -44,6 +45,123 @@ void ERR_print_errors_cb(int (*cb) (const char *str, size_t len, void *u),
}
}
+/* auxiliary function for incrementally reporting texts via the error queue */
+static void put_error(int lib, const char *func, int reason,
+ const char *file, int line)
+{
+ ERR_new();
+ ERR_set_debug(file, line, func);
+ ERR_set_error(lib, reason, NULL /* no data here, so fmt is NULL */);
+}
+
+#define TYPICAL_MAX_OUTPUT_BEFORE_DATA 100
+#define MAX_DATA_LEN (ERR_PRINT_BUF_SIZE - TYPICAL_MAX_OUTPUT_BEFORE_DATA)
+void ERR_add_error_txt(const char *separator, const char *txt)
+{
+ const char *file = NULL;
+ int line;
+ const char *func = NULL;
+ const char *data = NULL;
+ int flags;
+ unsigned long err = ERR_peek_last_error();
+
+ if (separator == NULL)
+ separator = "";
+ if (err == 0)
+ put_error(ERR_LIB_CMP, NULL, 0, "", 0);
+
+ do {
+ size_t available_len, data_len;
+ const char *curr = txt, *next = txt;
+ const char *leading_separator = separator;
+ int trailing_separator = 0;
+ char *tmp;
+
+ ERR_peek_last_error_all(&file, &line, &func, &data, &flags);
+ if ((flags & ERR_TXT_STRING) == 0) {
+ data = "";
+ leading_separator = "";
+ }
+ data_len = strlen(data);
+
+ /* workaround for limit of ERR_print_errors_cb() */
+ if (data_len >= MAX_DATA_LEN
+ || strlen(separator) >= (size_t)(MAX_DATA_LEN - data_len))
+ available_len = 0;
+ else
+ available_len = MAX_DATA_LEN - data_len - strlen(separator) - 1;
+ /* MAX_DATA_LEN > available_len >= 0 */
+
+ if (*separator == '\0') {
+ const size_t len_next = strlen(next);
+
+ if (len_next <= available_len) {
+ next += len_next;
+ curr = NULL; /* no need to split */
+ } else {
+ next += available_len;
+ curr = next; /* will split at this point */
+ }
+ } else {
+ while (*next != '\0' && (size_t)(next - txt) <= available_len) {
+ curr = next;
+ next = strstr(curr, separator);
+ if (next != NULL) {
+ next += strlen(separator);
+ trailing_separator = *next == '\0';
+ } else {
+ next = curr + strlen(curr);
+ }
+ }
+ if ((size_t)(next - txt) <= available_len)
+ curr = NULL; /* the above loop implies *next == '\0' */
+ }
+ if (curr != NULL) {
+ /* split error msg at curr since error data would get too long */
+ if (curr != txt) {
+ tmp = OPENSSL_strndup(txt, curr - txt);
+ if (tmp == NULL)
+ return;
+ ERR_add_error_data(2, separator, tmp);
+ OPENSSL_free(tmp);
+ }
+ put_error(ERR_LIB_CMP, func, err, file, line);
+ txt = curr;
+ } else {
+ if (trailing_separator) {
+ tmp = OPENSSL_strndup(txt, next - strlen(separator) - txt);
+ if (tmp == NULL)
+ return;
+ /* output txt without the trailing separator */
+ ERR_add_error_data(2, leading_separator, tmp);
+ OPENSSL_free(tmp);
+ } else {
+ ERR_add_error_data(2, leading_separator, txt);
+ }
+ txt = next; /* finished */
+ }
+ } while (*txt != '\0');
+}
+
+void ERR_add_error_mem_bio(const char *separator, BIO *bio)
+{
+ if (bio != NULL) {
+ char *str;
+ long len = BIO_get_mem_data(bio, &str);
+
+ if (len > 0) {
+ if (str[len - 1] != '\0') {
+ if (BIO_write(bio, "", 1) <= 0)
+ return;
+
+ len = BIO_get_mem_data(bio, &str);
+ }
+ if (len > 1)
+ ERR_add_error_txt(separator, str);
+ }
+ }
+}
+
static int print_bio(const char *str, size_t len, void *bp)
{
return BIO_write((BIO *)bp, str, len);
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index 8920a77390..e6a45ac03a 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -2067,6 +2067,7 @@ BN_R_PRIVATE_KEY_TOO_LARGE:117:private key too large
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_REQUEST_ID:108:bad request id
CMP_R_CERTID_NOT_FOUND:109:certid not found
CMP_R_CERTIFICATE_NOT_FOUND:112:certificate not found
@@ -2087,24 +2088,41 @@ CMP_R_ERROR_CREATING_RR:126:error creating rr
CMP_R_ERROR_PARSING_PKISTATUS:107:error parsing pkistatus
CMP_R_ERROR_PROTECTING_MESSAGE:127:error protecting message
CMP_R_ERROR_SETTING_CERTHASH:128:error setting certhash
+CMP_R_ERROR_VALIDATING_PROTECTION:140:error validating protection
+CMP_R_FAILED_EXTRACTING_PUBKEY:141:failed extracting pubkey
CMP_R_FAILURE_OBTAINING_RANDOM:110:failure obtaining random
CMP_R_FAIL_INFO_OUT_OF_RANGE:129:fail info out of range
CMP_R_INVALID_ARGS:100:invalid args
CMP_R_MISSING_KEY_INPUT_FOR_CREATING_PROTECTION:130:\
missing key input for creating protection
+CMP_R_MISSING_KEY_USAGE_DIGITALSIGNATURE:142:missing key usage digitalsignature
CMP_R_MISSING_PRIVATE_KEY:131:missing private key
+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_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_POTENTIALLY_INVALID_CERTIFICATE:139:potentially invalid certificate
+CMP_R_POTENTIALLY_INVALID_CERTIFICATE:147:potentially invalid certificate
+CMP_R_RECIPNONCE_UNMATCHED:148:recipnonce unmatched
+CMP_R_REQUEST_NOT_ACCEPTED:149:request not accepted
+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_TRANSACTIONID_UNMATCHED:152:transactionid unmatched
CMP_R_UNEXPECTED_PKIBODY:133:unexpected pkibody
+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_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_PBM_VALUE:155:wrong pbm value
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
@@ -3360,6 +3378,7 @@ X509_R_BAD_X509_FILETYPE:100:bad x509 filetype
X509_R_BASE64_DECODE_ERROR:118:base64 decode error
X509_R_CANT_CHECK_DH_KEY:114:cant check dh key
X509_R_CERT_ALREADY_IN_HASH_TABLE:101:cert already in hash table
+X509_R_CERTIFICATE_VERIFICATION_FAILED:139:certificate verification failed
X509_R_CRL_ALREADY_DELTA:127:crl already delta
X509_R_CRL_VERIFY_FAILURE:131:crl verify failure
X509_R_IDP_MISMATCH:128:idp mismatch
diff --git a/crypto/x509/t_x509.c b/crypto/x509/t_x509.c
index 4969bb34bf..eac299c09a 100644
--- a/crypto/x509/t_x509.c
+++ b/crypto/x509/t_x509.c
@@ -15,6 +15,7 @@
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include "crypto/asn1.h"
+#include "crypto/x509.h"
#ifndef OPENSSL_NO_STDIO
int X509_print_fp(FILE *fp, X509 *x)
@@ -380,3 +381,107 @@ int X509_aux_print(BIO *out, X509 *x, int indent)
}
return 1;
}
+
+/*
+ * Helper functions for improving certificate verification error diagnostics
+ */
+
+int x509_print_ex_brief(BIO *bio, X509 *cert, unsigned long neg_cflags)
+{
+ unsigned long flags = ASN1_STRFLGS_RFC2253 | ASN1_STRFLGS_ESC_QUOTE |
+ XN_FLAG_SEP_CPLUS_SPC | XN_FLAG_FN_SN;
+
+ if (cert == NULL)
+ return BIO_printf(bio, " (no certificate)\n") > 0;
+ if (BIO_printf(bio, " certificate\n") <= 0
+ || !X509_print_ex(bio, cert, flags, ~X509_FLAG_NO_SUBJECT))
+ return 0;
+ if (X509_check_issued((X509 *)cert, cert) == X509_V_OK) {
+ if (BIO_printf(bio, " self-issued\n") <= 0)
+ return 0;
+ } else {
+ if (BIO_printf(bio, " ") <= 0
+ || !X509_print_ex(bio, cert, flags, ~X509_FLAG_NO_ISSUER))
+ return 0;
+ }
+ if (!X509_print_ex(bio, cert, flags,
+ ~(X509_FLAG_NO_SERIAL | X509_FLAG_NO_VALIDITY)))
+ return 0;
+ if (X509_cmp_current_time(X509_get0_notBefore(cert)) > 0)
+ if (BIO_printf(bio, " not yet valid\n") <= 0)
+ return 0;
+ if (X509_cmp_current_time(X509_get0_notAfter(cert)) < 0)
+ if (BIO_printf(bio, " no more valid\n") <= 0)
+ return 0;
+ return X509_print_ex(bio, cert, flags, ~(neg_cflags));
+}
+
+static int print_certs(BIO *bio, const STACK_OF(X509) *certs)
+{
+ int i;
+
+ if (certs == NULL || sk_X509_num(certs) <= 0)
+ return BIO_printf(bio, " (no certificates)\n") >= 0;
+
+ for (i = 0; i < sk_X509_num(certs); i++) {
+ X509 *cert = sk_X509_value(certs, i);
+ if (cert != NULL && !x509_print_ex_brief(bio, cert, 0))
+ return 0;
+ }
+ return 1;
+}
+
+static int print_store_certs(BIO *bio, X509_STORE *store)
+{
+ if (store != NULL) {
+ STACK_OF(X509) *certs = X509_STORE_get1_all_certs(store);
+ int ret = print_certs(bio, certs);
+
+ sk_X509_pop_free(certs, X509_free);
+ return ret;
+ } else {
+ return BIO_printf(bio, " (no trusted store)\n") >= 0;
+ }
+}
+
+/* Extend the error queue with details on a failed cert verification */
+int X509_STORE_CTX_print_verify_cb(int ok, X509_STORE_CTX *ctx)
+{
+ if (ok == 0 && ctx != NULL) {
+ int cert_error = X509_STORE_CTX_get_error(ctx);
+ int depth = X509_STORE_CTX_get_error_depth(ctx);
+ X509 *cert = X509_STORE_CTX_get_current_cert(ctx);
+ BIO *bio = BIO_new(BIO_s_mem()); /* may be NULL */
+
+ BIO_printf(bio, "%s at depth=%d error=%d (%s)\n",
+ X509_STORE_CTX_get0_parent_ctx(ctx) != NULL
+ ? "CRL path validation" : "certificate verification",
+ depth, cert_error,
+ X509_verify_cert_error_string(cert_error));
+ BIO_printf(bio, "failure for:\n");
+ x509_print_ex_brief(bio, cert, X509_FLAG_NO_EXTENSIONS);
+ if (cert_error == X509_V_ERR_CERT_UNTRUSTED
+ || cert_error == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
+ || cert_error == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN
+ || cert_error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT
+ || cert_error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
+ || cert_error == X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER
+ || cert_error == X509_V_ERR_STORE_LOOKUP) {
+ BIO_printf(bio, "non-trusted certs:\n");
+ print_certs(bio, X509_STORE_CTX_get0_untrusted(ctx));
+ BIO_printf(bio, "certs in trust store:\n");
+ print_store_certs(bio, X509_STORE_CTX_get0_store(ctx));
+ }
+ CMPerr(0, X509_R_CERTIFICATE_VERIFICATION_FAILED);
+ ERR_add_error_mem_bio("\n", bio);
+ BIO_free(bio);
+ }
+
+ /*
+ * TODO we could check policies here too, e.g.:
+ * if (cert_error == X509_V_OK && ok == 2)
+ * policies_print(NULL, ctx);
+ */
+
+ return ok;
+}
diff --git a/crypto/x509/x509_err.c b/crypto/x509/x509_err.c
index 1b01fd0131..59ffbee6d8 100644
--- a/crypto/x509/x509_err.c
+++ b/crypto/x509/x509_err.c
@@ -22,6 +22,8 @@ static const ERR_STRING_DATA X509_str_reasons[] = {
{ERR_PACK(ERR_LIB_X509, 0, X509_R_CANT_CHECK_DH_KEY), "cant check dh key"},
{ERR_PACK(ERR_LIB_X509, 0, X509_R_CERT_ALREADY_IN_HASH_TABLE),
"cert already in hash table"},
+ {ERR_PACK(ERR_LIB_X509, 0, X509_R_CERTIFICATE_VERIFICATION_FAILED),
+ "certificate verification failed"},
{ERR_PACK(ERR_LIB_X509, 0, X509_R_CRL_ALREADY_DELTA), "crl already delta"},
{ERR_PACK(ERR_LIB_X509, 0, X509_R_CRL_VERIFY_FAILURE),
"crl verify failure"},
diff --git a/crypto/x509/x509_lu.c b/crypto/x509/x509_lu.c
index 016b4b304f..9018d6e114 100644
--- a/crypto/x509/x509_lu.c
+++ b/crypto/x509/x509_lu.c
@@ -532,6 +532,41 @@ STACK_OF(X509_OBJECT) *X509_STORE_get0_objects(X509_STORE *v)
return v->objs;
}
+STACK_OF(X509) *X509_STORE_get1_all_certs(X509_STORE *store)
+{
+ STACK_OF(X509) *sk;
+ STACK_OF(X509_OBJECT) *objs;
+ int i;
+
+ if (store == NULL) {
+ X509err(0, ERR_R_PASSED_NULL_PARAMETER);
+ return NULL;
+ }
+ if ((sk = sk_X509_new_null()) == NULL)
+ return NULL;
+ X509_STORE_lock(store);
+ objs = X509_STORE_get0_objects(store);
+ for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
+ X509 *cert = X509_OBJECT_get0_X509(sk_X509_OBJECT_value(objs, i));
+
+ if (cert != NULL) {
+ if (!X509_up_ref(cert))
+ goto err;
+ if (!sk_X509_push(sk, cert)) {
+ X509_free(cert);
+ goto err;
+ }
+ }
+ }
+ X509_STORE_unlock(store);
+ return sk;
+
+ err:
+ X509_STORE_unlock(store);
+ sk_X509_pop_free(sk, X509_free);
+ return NULL;
+}
+
STACK_OF(X509) *X509_STORE_CTX_get1_certs(X509_STORE_CTX *ctx, X509_NAME *nm)
{
int i, idx, cnt;
diff --git a/doc/internal/man3/ossl_cmp_print_log.pod b/doc/internal/man3/ossl_cmp_print_log.pod
index a45897a067..47d4dd8efa 100644
--- a/doc/internal/man3/ossl_cmp_print_log.pod
+++ b/doc/internal/man3/ossl_cmp_print_log.pod
@@ -14,7 +14,6 @@ ossl_cmp_log2,
ossl_cmp_log3,
ossl_cmp_log4,
ossl_cmp_log_parse_metadata,
-ossl_cmp_add_error_txt,
ossl_cmp_add_error_data,
ossl_cmp_add_error_line
- logging and error reporting support for CMP
@@ -40,7 +39,6 @@ ossl_cmp_add_error_line
OSSL_CMP_severity *level, char **func,
char **file, int *line);
- void ossl_cmp_add_error_txt(const char *separator, const char *txt);
#define ossl_cmp_add_error_data(txt)
#define ossl_cmp_add_error_line(txt)
@@ -72,17 +70,11 @@ the variable pointed to by I<file> with the filename string or NULL, and
the variable pointed to by I<line> with the line number or -1.
Any string returned via I<*func> and I<*file> must be freeed by the caller.
-ossl_cmp_add_error_txt() appends text to the extra data field of the last
-error message in the OpenSSL error queue, after adding the optional separator
-unless data has been empty so far. The text can be of arbitrary length,
-which is not possible when using L<ERR_add_error_data(3)> in conjunction with
-L<ERR_print_errors_cb(3)>.
-
ossl_cmp_add_error_data() is a macro calling
-ossl_cmp_add_error_txt() with the separator being ":".
+L<ERR_add_error_txt(3)> with the separator being ":".
ossl_cmp_add_error_line() is a macro calling
-ossl_cmp_add_error_txt() with the separator being "\n".
+L<ERR_add_error_txt(3)> with the separator being "\n".
=head1 RETURN VALUES
@@ -90,13 +82,16 @@ ossl_cmp_log_parse_metadata() returns the pointer to the actual message text
after the OSSL_CMP_LOG_PREFIX and level and ':' if found in the buffer,
else the beginning of the buffer.
-ossl_cmp_add_error_txt()
-ossl_cmp_add_error_data(), and
+ossl_cmp_add_error_data() and
ossl_cmp_add_error_line()
do not return anything.
All other functions return 1 on success, 0 on error.
+=head1 SEE ALSO
+
+L<ERR_add_error_txt(3)>
+
=head1 HISTORY
The OpenSSL CMP support was added in OpenSSL 3.0.
diff --git a/doc/man3/ERR_put_error.pod b/doc/man3/ERR_put_error.pod
index e3c19bfdf4..85538f718b 100644
--- a/doc/man3/ERR_put_error.pod
+++ b/doc/man3/ERR_put_error.pod
@@ -3,7 +3,8 @@
=head1 NAME
ERR_raise, ERR_raise_data,
-ERR_put_error, ERR_add_error_data, ERR_add_error_vdata
+ERR_put_error, ERR_add_error_data, ERR_add_error_vdata,
+ERR_add_error_txt, ERR_add_error_mem_bio
- record an error
=head1 SYNOPSIS
@@ -15,6 +16,8 @@ ERR_put_error, ERR_add_error_data, ERR_add_error_vdata
void ERR_add_error_data(int num, ...);
void ERR_add_error_vdata(int num, va_list arg);
+ void ERR_add_error_txt(const char *sep, const char *txt);
+ void ERR_add_error_mem_bio(const char *sep, BIO *bio);
Deprecated since OpenSSL 3.0:
@@ -38,9 +41,23 @@ B<func> of library B<lib>, in line number B<line> of B<file>.
This function is usually called by a macro.
ERR_add_error_data() associates the concatenation of its B<num> string
-arguments with the error code added last.
+arguments as additional data with the error code added last.
ERR_add_error_vdata() is similar except the argument is a B<va_list>.
Multiple calls to these functions append to the current top of the error queue.
+The total length of the string data per error is limited to 4096 characters.
+
+ERR_add_error_txt() appends the given text string as additional data to the
+last error queue entry, after inserting the optional separator string if it is
+not NULL and the top error entry does not yet have additional data.
+In case the separator is at the end of the text it is not appended to the data.
+The B<sep> argument may be for instance "\n" to insert a line break when needed.
+If the associated data would become more than 4096 characters long
+(which is the limit given above)
+it is split over sufficiently many new copies of the last error queue entry.
+
+ERR_add_error_mem_bio() is the same as ERR_add_error_txt() except that
+the text string is taken from the given memory BIO.
+It appends '\0' to the BIO contents if not already NUL-terminated.
L<ERR_load_strings(3)> can be used to register
error strings so that the application can a generate human-readable
@@ -76,8 +93,10 @@ the ASN1err() macro.
=head1 RETURN VALUES
-ERR_raise(), ERR_put_error(), ERR_add_error_data() and
-ERR_add_error_vdata() return no values.
+ERR_raise(), ERR_put_error(),
+ERR_add_error_data(), ERR_add_error_vdata()
+ERR_add_error_txt(), and ERR_add_error_mem_bio()
+return no values.
=head1 NOTES
@@ -87,6 +106,10 @@ ERR_raise() and ERR_put_error() are implemented as macros.
L<ERR_load_strings(3)>
+=head1 HISTORY
+
+B<ERR_add_error_txt> and B<ERR_add_error_mem_bio> were added in OpenSSL 3.0.
+
=head1 COPYRIGHT
Copyright 2000-2019 The OpenSSL Project Authors. All Rights Reserved.
diff --git a/doc/man3/OSSL_CMP_validate_msg.pod b/doc/man3/OSSL_CMP_validate_msg.pod
new file mode 100644
index 0000000000..acb17facde
--- /dev/null
+++ b/doc/man3/OSSL_CMP_validate_msg.pod
@@ -0,0 +1,86 @@
+=pod
+
+=head1 NAME
+
+OSSL_CMP_validate_msg,
+OSSL_CMP_validate_cert_path
+- functions for verifying CMP message protection
+
+=head1 SYNOPSIS
+
+ #include <openssl/cmp.h>
+ int OSSL_CMP_validate_msg(OSSL_CMP_CTX *ctx, OSSL_CMP_MSG *msg);
+ int OSSL_CMP_validate_cert_path(const OSSL_CMP_CTX *ctx,
+ X509_STORE *trusted_store, X509 *cert);
+
+=head1 DESCRIPTION
+
+This is the API for validating the protection of CMP messages,
+which includes validating CMP message sender certificates and their paths
+while optionally checking the revocation status of the certificates(s).
+
+OSSL_CMP_validate_msg() validates the protection of the given C<msg>
+using either password-based mac (PBM) or a signature algorithm.
+
+In case of signature algorithm, the certificate to use for the signature check
+is preferably the one provided by a call to L<OSSL_CMP_CTX_set1_srvCert(3)>.
+If no such sender cert has been pinned then candidate sender certificates are
+taken from the list of certificates received in the C<msg> extraCerts, then any
+certificates provided before via L<OSSL_CMP_CTX_set1_untrusted_certs(3)>, and
+then all trusted certificates provided via L<OSSL_CMP_CTX_set0_trustedStore(3)>,
+where a candidate is acceptable only if has not expired, its subject DN matches
+the C<msg> sender DN (as far as present), and its subject key identifier
+is present and matches the senderKID (as far as the latter present).
+Each acceptable cert is tried in the given order to see if the message
+signature check succeeds and the cert and its path can be verified
+using any trust store set via L<OSSL_CMP_CTX_set0_trustedStore(3)>.
+
+If the option OSSL_CMP_OPT_PERMIT_TA_IN_EXTRACERTS_FOR_IR was set by calling
+L<OSSL_CMP_CTX_set_option(3)>, for an Initialization Response (IP) message
+any self-issued certificate from the C<msg> extraCerts field may also be used
+as trust anchor for the path verification of an acceptable cert if it can be
+used also to validate the issued certificate returned in the IP message. This is
+according to TS 33.310 [Network Domain Security (NDS); Authentication Framework
+(AF)] document specified by the The 3rd Generation Partnership Project (3GPP).
+
+Any cert that has been found as described above is cached and tried first when
+validating the signatures of subsequent messages in the same transaction.
+
+After successful validation of PBM-based protection of a certificate response
+the certificates in the caPubs field (if any) are added to the trusted
+certificates provided via L<OSSL_CMP_CTX_set0_trustedStore(3)>, such that
+they are available for validating subsequent messages in the same context.
+Those could apply to any Polling Response (pollRep), error, or PKI Confirmation
+(PKIConf) messages following in the same or future transactions.
+
+OSSL_CMP_validate_cert_path() attempts to validate the given certificate and its
+path using the given store of trusted certs (possibly including CRLs and a cert
+verification callback) and non-trusted intermediate certs from the B<ctx>.
+
+=head1 NOTES
+
+CMP is defined in RFC 4210 (and CRMF in RFC 4211).
+
+=head1 RETURN VALUES
+
+OSSL_CMP_validate_msg() and OSSL_CMP_validate_cert_path()
+return 1 on success, 0 on error or validation failed.
+
+=head1 SEE ALSO
+
+L<OSSL_CMP_CTX_new(3)>, L<OSSL_CMP_exec_IR_ses(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/X509_STORE_CTX_set_verify_cb.pod b/doc/man3/X509_STORE_CTX_set_verify_cb.pod
index 64ccefa7ff..c53b14db36 100644
--- a/doc/man3/X509_STORE_CTX_set_verify_cb.pod
+++ b/doc/man3/X509_STORE_CTX_set_verify_cb.pod
@@ -14,14 +14,16 @@ X509_STORE_CTX_get_check_issued,
X509_STORE_CTX_get_get_issuer,
X509_STORE_CTX_get_verify_cb,
X509_STORE_CTX_set_verify_cb,
-X509_STORE_CTX_verify_cb
-- get and set verification callback
+X509_STORE_CTX_verify_cb,
+X509_STORE_CTX_print_verify_cb
+- get and set X509_STORE_CTX components such as verification callback
=head1 SYNOPSIS
#include <openssl/x509_vfy.h>
typedef int (*X509_STORE_CTX_verify_cb)(int, X509_STORE_CTX *);
+ int X509_STORE_CTX_print_verify_cb(int ok, X509_STORE_CTX *ctx);
X509_STORE_CTX_verify_cb X509_STORE_CTX_get_verify_cb(X509_STORE_CTX *ctx);
@@ -63,6 +65,12 @@ structure and receive additional information about the error, for example
by calling X509_STORE_CTX_get_current_cert(). Additional application data can
be passed to the callback via the B<ex_data> mechanism.
+X509_STORE_CTX_print_verify_cb() is a verification callback function that,
+when a certificate verification has failed, adds an entry to the error queue
+with code B<X509_R_CERTIFICATE_VERIFICATION_FAILED> and with diagnostic details,
+including the most relevant fields of the target certificate that failed to
+verify and, if appropriate, of the available untrusted and trusted certificates.
+
X509_STORE_CTX_get_verify_cb() returns the value of the current callback
for the specific B<ctx>.
@@ -200,6 +208,8 @@ X509_STORE_CTX_get_cert_crl(), X509_STORE_CTX_get_check_policy(),
X509_STORE_CTX_get_lookup_certs(), X509_STORE_CTX_get_lookup_crls()
and X509_STORE_CTX_get_cleanup() functions were added in OpenSSL 1.1.0.
+X509_STORE_CTX_print_verify_cb() was added in OpenSSL 3.0.
+
=head1 COPYRIGHT
Copyright 2009-2016 The OpenSSL Project Authors. All Rights Reserved.
diff --git a/doc/man3/X509_STORE_get0_param.pod b/doc/man3/X509_STORE_get0_param.pod
index 27fe786783..6db760ea51 100644
--- a/doc/man3/X509_STORE_get0_param.pod
+++ b/doc/man3/X509_STORE_get0_param.pod
@@ -3,7 +3,8 @@
=head1 NAME
X509_STORE_get0_param, X509_STORE_set1_param,
-X509_STORE_get0_objects - X509_STORE setter and getter functions
+X509_STORE_get0_objects, X509_STORE_get1_all_certs
+- X509_STORE setter and getter functions
=head1 SYNOPSIS
@@ -12,6 +13,7 @@ X509_STORE_get0_objects - X509_STORE setter and getter functions
X509_VERIFY_PARAM *X509_STORE_get0_param(X509_STORE *ctx);
int X509_STORE_set1_param(X509_STORE *ctx, X509_VERIFY_PARAM *pm);
STACK_OF(X509_OBJECT) *X509_STORE_get0_objects(X509_STORE *ctx);
+ STACK_OF(X509) *X509_STORE_get1_all_certs(X509_STORE *st);
=head1 DESCRIPTION
@@ -22,10 +24,12 @@ X509_STORE_get0_param() retrieves an internal pointer to the verification
parameters for B<ctx>. The returned pointer must not be freed by the
calling application
-X509_STORE_get0_objects() retrieve an internal pointer to the store's
+X509_STORE_get0_objects() retrieves an internal pointer to the store's
X509 object cache. The cache contains B<X509> and B<X509_CRL> objects. The
returned pointer must not be freed by the calling application.
+X509_STORE_get1_all_certs() returns a list of all certificates in the store.
+The caller is responsible for freeing the returned list.
=head1 RETURN VALUES
@@ -36,6 +40,9 @@ X509_STORE_set1_param() returns 1 for success and 0 for failure.
X509_STORE_get0_objects() returns a pointer to a stack of B<X509_OBJECT>.
+X509_STORE_get1_all_certs() returns a pointer to a stack of the retrieved
+certificates on success, else NULL.
+
=head1 SEE ALSO
L<X509_STORE_new(3)>
@@ -44,6 +51,7 @@ L<X509_STORE_new(3)>
B<X509_STORE_get0_param> and B<X509_STORE_get0_objects> were added in
OpenSSL 1.1.0.
+B<X509_STORE_get1_certs> was added in OpenSSL 3.0.
=head1 COPYRIGHT
diff --git a/include/crypto/x509.h b/include/crypto/x509.h
index 11a776953b..602a72fd27 100644
--- a/include/crypto/x509.h
+++ b/include/crypto/x509.h
@@ -288,5 +288,6 @@ struct x509_object_st {
int a2i_ipadd(unsigned char *ipout, const char *ipasc);
int x509_set1_time(ASN1_TIME **ptm, const ASN1_TIME *tm);
+int x509_print_ex_brief(BIO *bio, X509 *cert, unsigned long neg_cflags);
void x509_init_sig_info(X509 *x);
diff --git a/include/openssl/cmp.h b/include/openssl/cmp.h
index bc1ae35305..43dcc69993 100644
--- a/include/openssl/cmp.h
+++ b/include/openssl/cmp.h
@@ -348,6 +348,11 @@ ASN1_OCTET_STRING *OSSL_CMP_HDR_get0_recipNonce(const OSSL_CMP_PKIHEADER *hdr);
/* support application-level CMP debugging in cmp.c: */
OSSL_CMP_PKIHEADER *OSSL_CMP_MSG_get0_header(const OSSL_CMP_MSG *msg);
+/* from cmp_vfy.c */
+int OSSL_CMP_validate_msg(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *msg);
+int OSSL_CMP_validate_cert_path(OSSL_CMP_CTX *ctx,
+ X509_STORE *trusted_store, X509 *cert);
+
# ifdef __cplusplus
}
# endif
diff --git a/include/openssl/cmperr.h b/include/openssl/cmperr.h
index a44a1a92bb..51795a52ab 100644
--- a/include/openssl/cmperr.h
+++ b/include/openssl/cmperr.h
@@ -33,6 +33,7 @@ int ERR_load_CMP_strings(void);
/*
* CMP reason codes.
*/
+# define CMP_R_ALGORITHM_NOT_SUPPORTED 139
# define CMP_R_BAD_REQUEST_ID 108
# define CMP_R_CERTID_NOT_FOUND 109
# define CMP_R_CERTIFICATE_NOT_FOUND 112
@@ -53,23 +54,38 @@ int ERR_load_CMP_strings(void);
# define CMP_R_ERROR_PARSING_PKISTATUS 107
# define CMP_R_ERROR_PROTECTING_MESSAGE 127
# define CMP_R_ERROR_SETTING_CERTHASH 128
+# define CMP_R_ERROR_VALIDATING_PROTECTION 140
+# define CMP_R_FAILED_EXTRACTING_PUBKEY 141
# define CMP_R_FAILURE_OBTAINING_RANDOM 110
# define CMP_R_FAIL_INFO_OUT_OF_RANGE 129
# define CMP_R_INVALID_ARGS 100
# define CMP_R_MISSING_KEY_INPUT_FOR_CREATING_PROTECTION 130
+# define CMP_R_MISSING_KEY_USAGE_DIGITALSIGNATURE 142
# define CMP_R_MISSING_PRIVATE_KEY 131
+# define CMP_R_MISSING_PROTECTION 143
# define CMP_R_MISSING_SENDER_IDENTIFICATION 111
+# define CMP_R_MISSING_TRUST_STORE 144
# 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_POTENTIALLY_INVALID_CERTIFICATE 139
+# define CMP_R_POTENTIALLY_INVALID_CERTIFICATE 147
+# define CMP_R_RECIPNONCE_UNMATCHED 148
+# define CMP_R_REQUEST_NOT_ACCEPTED 149
+# define CMP_R_SENDER_GENERALNAME_TYPE_NOT_SUPPORTED 150
+# define CMP_R_SRVCERT_DOES_NOT_VALIDATE_MSG 151
+# define CMP_R_TRANSACTIONID_UNMATCHED 152
# define CMP_R_UNEXPECTED_PKIBODY 133
+# define CMP_R_UNEXPECTED_PVNO 153
# define CMP_R_UNKNOWN_ALGORITHM_ID 134
# define CMP_R_UNKNOWN_CERT_TYPE 135
# 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_PBM_VALUE 155
# endif
#endif
diff --git a/include/openssl/err.h b/include/openssl/err.h
index 17a248ca8d..ef8e895c6e 100644
--- a/include/openssl/err.h
+++ b/include/openssl/err.h
@@ -333,6 +333,8 @@ void ERR_print_errors(BIO *bp);
void ERR_add_error_data(int num, ...);
void ERR_add_error_vdata(int num, va_list args);
+void ERR_add_error_txt(const char *sepr, const char *txt);
+void ERR_add_error_mem_bio(const char *sep, BIO *bio);
int ERR_load_strings(int lib, ERR_STRING_DATA *str);
int ERR_load_strings_const(const ERR_STRING_DATA *str);
diff --git a/include/openssl/x509_vfy.h b/include/openssl/x509_vfy.h
index affdc67d80..75529b234e 100644
--- a/include/openssl/x509_vfy.h
+++ b/include/openssl/x509_vfy.h
@@ -67,6 +67,7 @@ DEFINE_STACK_OF(X509_VERIFY_PARAM)
int X509_STORE_set_depth(X509_STORE *store, int depth);
typedef int (*X509_STORE_CTX_verify_cb)(int, X509_STORE_CTX *);
+int X509_STORE_CTX_print_verify_cb(int ok, X509_STORE_CTX *ctx);
typedef int (*X509_STORE_CTX_verify_fn)(X509_STORE_CTX *);
typedef int (*X509_STORE_CTX_get_issuer_fn)(X509 **issuer,
X509_STORE_CTX *ctx, X509 *x);
@@ -287,8 +288,9 @@ void X509_STORE_free(X509_STORE *v);
int X509_STORE_lock(X509_STORE *ctx);
int X509_STORE_unlock(X509_STORE *ctx);
int X509_STORE_up_ref(X509_STORE *v);
-STACK_OF(X509_OBJECT) *X509_STORE_get0_objects(X509_STORE *v);
+STACK_OF(X509_OBJECT) *X509_STORE_get0_objects(X509_STORE *v);
+STACK_OF(X509) *X509_STORE_get1_all_certs(X509_STORE *st);
STACK_OF(X509) *X509_STORE_CTX_get1_certs(X509_STORE_CTX *st, X509_NAME *nm);
STACK_OF(X509_CRL) *X509_STORE_CTX_get1_crls(X509_STORE_CTX *st, X509_NAME *nm);
int X509_STORE_set_flags(X509_STORE *ctx, unsigned long flags);
diff --git a/include/openssl/x509err.h b/include/openssl/x509err.h
index 5b0a6b5eaa..2653870d29 100644
--- a/include/openssl/x509err.h
+++ b/include/openssl/x509err.h
@@ -107,6 +107,7 @@ int ERR_load_X509_strings(void);
# define X509_R_BASE64_DECODE_ERROR 118
# define X509_R_CANT_CHECK_DH_KEY 114
# define X509_R_CERT_ALREADY_IN_HASH_TABLE 101
+# define X509_R_CERTIFICATE_VERIFICATION_FAILED 139
# define X509_R_CRL_ALREADY_DELTA 127
# define X509_R_CRL_VERIFY_FAILURE 131
# define X509_R_IDP_MISMATCH 128
diff --git a/test/build.info b/test/build.info
index c35bed086c..f964dec4ba 100644
--- a/test/build.info
+++ b/test/build.info
@@ -455,7 +455,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_protect_test cmp_msg_test cmp_vfy_test
ENDIF
SOURCE[cmp_asn_test]=cmp_asn_test.c cmp_testlib.c
@@ -482,6 +482,10 @@ IF[{- !$disabled{tests} -}]
INCLUDE[cmp_msg_test]=.. ../include ../apps/include
DEPEND[cmp_msg_test]=../libcrypto.a libtestutil.a
+ SOURCE[cmp_vfy_test]=cmp_status_test.c cmp_testlib.c
+ INCLUDE[cmp_vfy_test]=.. ../include ../apps/include
+ DEPEND[cmp_vfy_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_ctx_test.c b/test/cmp_ctx_test.c
index 5c637b0a3c..26c65778b9 100644
--- a/test/cmp_ctx_test.c
+++ b/test/cmp_ctx_test.c
@@ -169,7 +169,7 @@ static int execute_CTX_print_errors_test(OSSL_CMP_CTX_TEST_FIXTURE *fixture)
base_err_msg_size = strlen("INVALID_ARGS") + strlen(" : ");
expected_size = base_err_msg_size;
while (expected_size < 4096) { /* force split */
- ossl_cmp_add_error_txt(STR_SEP, max_str_literal);
+ ERR_add_error_txt(STR_SEP, max_str_literal);
expected_size += strlen(STR_SEP) + strlen(max_str_literal);
}
expected_size += base_err_msg_size - 2 * strlen(STR_SEP);
@@ -794,8 +794,7 @@ int setup_tests(void)
#if !defined(OPENSSL_NO_ERR) && !defined(OPENSSL_NO_AUTOERRINIT)
/*
* also tests OSSL_CMP_CTX_set_log_cb(), OSSL_CMP_print_errors_cb(),
- * ossl_cmp_add_error_txt(), and the macros
- * ossl_cmp_add_error_data and ossl_cmp_add_error_line:
+ * and the macros ossl_cmp_add_error_data and ossl_cmp_add_error_line:
*/
ADD_TEST(test_CTX_print_errors);
#endif
diff --git a/test/cmp_protect_test.c b/test/cmp_protect_test.c
index 022dea6b7c..5d5df89abd 100644
--- a/test/cmp_protect_test.c
+++ b/test/cmp_protect_test.c
@@ -387,7 +387,7 @@ static int execute_X509_STORE_test(CMP_PROTECT_TEST_FIXTURE *fixture)
fixture->certs,
fixture->callback_arg)))
goto err;
- sk = ossl_cmp_X509_STORE_get1_certs(store);
+ sk = X509_STORE_get1_all_certs(store);
if (!TEST_int_eq(0, STACK_OF_X509_cmp(sk, fixture->chain)))
goto err;
res = 1;
diff --git a/test/cmp_vfy_test.c b/test/cmp_vfy_test.c
new file mode 100644
index 0000000000..41ddad86ba
--- /dev/null
+++ b/test/cmp_vfy_test.c
@@ -0,0 +1,636 @@
+/*
+ * 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 "../crypto/crmf/crmf_local.h" /* for manipulating POPO signature */
+
+static const char *server_f;
+static const char *client_f;
+static const char *endentity1_f;
+static const char *endentity2_f;
+static const char *root_f;
+static const char *intermediate_f;
+static const char *ir_protected_f;
+static const char *ir_unprotected_f;
+static const char *ir_rmprotection_f;
+static const char *ip_waiting_f;
+static const char *instacert_f;
+static const char *instaca_f;
+static const char *ir_protected_0_extracerts;
+static const char *ir_protected_2_extracerts;
+
+typedef struct test_fixture {
+ const char *test_case_name;
+ int expected;
+ OSSL_CMP_CTX *cmp_ctx;
+ OSSL_CMP_MSG *msg;
+ X509 *cert;
+ ossl_cmp_allow_unprotected_cb_t allow_unprotected_cb;
+ int additional_arg;
+} CMP_VFY_TEST_FIXTURE;
+
+static void tear_down(CMP_VFY_TEST_FIXTURE *fixture)
+{
+ OSSL_CMP_MSG_free(fixture->msg);
+ OSSL_CMP_CTX_free(fixture->cmp_ctx);
+ 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)
+{
+ X509_STORE *ts = X509_STORE_new();
+ CMP_VFY_TEST_FIXTURE *fixture;
+
+ if (!TEST_ptr(fixture = OPENSSL_zalloc(sizeof(*fixture))))
+ return NULL;
+ fixture->test_case_name = test_case_name;
+ if (ts == NULL
+ || !TEST_ptr(fixture->cmp_ctx = OSSL_CMP_CTX_new())
+ || !OSSL_CMP_CTX_set0_trustedStore(fixture->cmp_ctx, ts)
+ || !OSSL_CMP_CTX_set_log_cb(fixture->cmp_ctx, print_to_bio_out)) {
+ tear_down(fixture);
+ X509_STORE_free(ts);
+ return NULL;
+ }
+ X509_VERIFY_PARAM_set_time(X509_STORE_get0_param(ts), test_time_valid);
+ X509_STORE_set_verify_cb(ts, OSSL_CMP_print_cert_verify_cb);
+ return fixture;
+}
+
+static X509 *srvcert = NULL;
+static X509 *clcert = NULL;
+/* chain */
+static X509 *endentity1 = NULL, *endentity2 = NULL,
+ *intermediate = NULL, *root = NULL;
+/* INSTA chain */
+static X509 *insta_cert = NULL, *instaca_cert = NULL;
+
+static unsigned char rand_data[OSSL_CMP_TRANSACTIONID_LENGTH];
+static OSSL_CMP_MSG *ir_unprotected, *ir_rmprotection;
+
+static int flip_bit(ASN1_BIT_STRING *bitstr)
+{
+ int bit_num = 7;
+ int bit = ASN1_BIT_STRING_get_bit(bitstr, bit_num);
+
+ return ASN1_BIT_STRING_set_bit(bitstr, bit_num, !bit);
+}
+
+static int execute_verify_popo_test(CMP_VFY_TEST_FIXTURE *fixture)
+{
+ if ((fixture->msg = load_pkimsg(ir_protected_f)) == NULL)
+ return 0;
+ if (fixture->expected == 0) {
+ const OSSL_CRMF_MSGS *reqs = fixture->msg->body->value.ir;
+ const OSSL_CRMF_MSG *req = sk_OSSL_CRMF_MSG_value(reqs, 0);
+ if (req == NULL || !flip_bit(req->popo->value.signature->signature))
+ return 0;
+ }
+ return TEST_int_eq(fixture->expected,
+ ossl_cmp_verify_popo(fixture->msg,
+ fixture->additional_arg));
+}
+
+static int test_verify_popo(void)
+{
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ fixture->expected = 1;
+ EXECUTE_TEST(execute_verify_popo_test, tear_down);
+ return result;
+}
+
+static int test_verify_popo_bad(void)
+{
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ fixture->expected = 0;
+ EXECUTE_TEST(execute_verify_popo_test, tear_down);
+ return result;
+}
+
+static int execute_validate_msg_test(CMP_VFY_TEST_FIXTURE *fixture)
+{
+ return TEST_int_eq(fixture->expected,
+ OSSL_CMP_validate_msg(fixture->cmp_ctx, fixture->msg));
+}
+
+static int execute_validate_cert_path_test(CMP_VFY_TEST_FIXTURE *fixture)
+{
+ X509_STORE *ts = OSSL_CMP_CTX_get0_trustedStore(fixture->cmp_ctx);
+ int res = TEST_int_eq(fixture->expected,
+ OSSL_CMP_validate_cert_path(fixture->cmp_ctx,
+ ts, fixture->cert));
+
+ OSSL_CMP_CTX_print_errors(fixture->cmp_ctx);
+ return res;
+}
+
+static int test_validate_msg_mac_alg_protection(void)
+{
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ /* secret value belonging to cmp-test/CMP_IP_waitingStatus_PBM.der */
+ const unsigned char sec_1[] = {
+ '9', 'p', 'p', '8', '-', 'b', '3', '5', 'i', '-', 'X', 'd', '3',
+ 'Q', '-', 'u', 'd', 'N', 'R'
+ };
+
+ fixture->expected = 1;
+ if (!TEST_true(OSSL_CMP_CTX_set1_secretValue(fixture->cmp_ctx, sec_1,
+ sizeof(sec_1)))
+ || !TEST_ptr(fixture->msg = load_pkimsg(ip_waiting_f))) {
+ tear_down(fixture);
+ fixture = NULL;
+ }
+ EXECUTE_TEST(execute_validate_msg_test, tear_down);
+ return result;
+}
+
+static int test_validate_msg_mac_alg_protection_bad(void)
+{
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ const unsigned char sec_bad[] = {
+ '9', 'p', 'p', '8', '-', 'b', '3', '5', 'i', '-', 'X', 'd', '3',
+ 'Q', '-', 'u', 'd', 'N', 'r'
+ };
+ fixture->expected = 0;
+
+ if (!TEST_true(OSSL_CMP_CTX_set1_secretValue(fixture->cmp_ctx, sec_bad,
+ sizeof(sec_bad)))
+ || !TEST_ptr(fixture->msg = load_pkimsg(ip_waiting_f))) {
+ tear_down(fixture);
+ fixture = NULL;
+ }
+ EXECUTE_TEST(execute_validate_msg_test, tear_down);
+ return result;
+}
+
+static int add_trusted(OSSL_CMP_CTX *ctx, X509 *cert)
+{
+ return X509_STORE_add_cert(OSSL_CMP_CTX_get0_trustedStore(ctx), cert);
+}
+
+static int add_untrusted(OSSL_CMP_CTX *ctx, X509 *cert)
+{
+ return ossl_cmp_sk_X509_add1_cert(OSSL_CMP_CTX_get0_untrusted_certs(ctx),
+ cert, 0, 0);
+}
+
+static int test_validate_msg_signature_partial_chain(int expired)
+{
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ X509_STORE *ts = OSSL_CMP_CTX_get0_trustedStore(fixture->cmp_ctx);
+
+ fixture->expected = !expired;
+ if (ts == NULL
+ || !TEST_ptr(fixture->msg = load_pkimsg(ir_protected_f))
+ || !add_trusted(fixture->cmp_ctx, srvcert)) {
+ tear_down(fixture);
+ fixture = NULL;
+ } else {
+ X509_VERIFY_PARAM *vpm = X509_STORE_get0_param(ts);
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_PARTIAL_CHAIN);
+ if (expired)
+ X509_VERIFY_PARAM_set_time(vpm, test_time_after_expiration);
+ }
+ EXECUTE_TEST(execute_validate_msg_test, tear_down);
+ return result;
+}
+
+static int test_validate_msg_signature_trusted_ok(void)
+{
+ return test_validate_msg_signature_partial_chain(0);
+}
+
+static int test_validate_msg_signature_trusted_expired(void)
+{
+ return test_validate_msg_signature_partial_chain(1);
+}
+
+static int test_validate_msg_signature_srvcert_wrong(void)
+{
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ fixture->expected = 0;
+ if (!TEST_ptr(fixture->msg = load_pkimsg(ir_protected_f))
+ || !TEST_true(OSSL_CMP_CTX_set1_srvCert(fixture->cmp_ctx, clcert))) {
+ tear_down(fixture);
+ fixture = NULL;
+ }
+ EXECUTE_TEST(execute_validate_msg_test, tear_down);
+ return result;
+}
+
+static int test_validate_msg_signature_srvcert(int bad_sig)
+{
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ fixture->expected = !bad_sig;
+ if (!TEST_ptr(fixture->msg = load_pkimsg(ir_protected_f))
+ || !TEST_true(OSSL_CMP_CTX_set1_srvCert(fixture->cmp_ctx, srvcert))
+ || (bad_sig && !flip_bit(fixture->msg->protection))) {
+ tear_down(fixture);
+ fixture = NULL;
+ }
+ EXECUTE_TEST(execute_validate_msg_test, tear_down);
+ return result;
+}
+
+static int test_validate_msg_signature_bad(void)
+{
+ return test_validate_msg_signature_srvcert(1);
+}
+
+static int test_validate_msg_signature_sender_cert_srvcert(void)
+{
+ return test_validate_msg_signature_srvcert(0);
+}
+
+static int test_validate_msg_signature_sender_cert_untrusted(void)
+{
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ fixture->expected = 1;
+ if (!TEST_ptr(fixture->msg = load_pkimsg(ir_protected_0_extracerts))
+ || !add_trusted(fixture->cmp_ctx, instaca_cert)
+ || !add_untrusted(fixture->cmp_ctx, insta_cert)) {
+ tear_down(fixture);
+ fixture = NULL;
+ }
+ EXECUTE_TEST(execute_validate_msg_test, tear_down);
+ return result;
+}
+
+static int test_validate_msg_signature_sender_cert_trusted(void)
+{
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ fixture->expected = 1;
+ if (!TEST_ptr(fixture->msg = load_pkimsg(ir_protected_0_extracerts))
+ || !add_trusted(fixture->cmp_ctx, instaca_cert)
+ || !add_trusted(fixture->cmp_ctx, insta_cert)) {
+ tear_down(fixture);
+ fixture = NULL;
+ }
+ EXECUTE_TEST(execute_validate_msg_test, tear_down);
+ return result;
+}
+
+static int test_validate_msg_signature_sender_cert_extracert(void)
+{
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ fixture->expected = 1;
+ if (!TEST_ptr(fixture->msg = load_pkimsg(ir_protected_2_extracerts))
+ || !add_trusted(fixture->cmp_ctx, instaca_cert)) {
+ tear_down(fixture);
+ fixture = NULL;
+ }
+ EXECUTE_TEST(execute_validate_msg_test, tear_down);
+ return result;
+}
+
+
+static int test_validate_msg_signature_sender_cert_absent(void)
+{
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ fixture->expected = 0;
+ if (!TEST_ptr(fixture->msg = load_pkimsg(ir_protected_0_extracerts))) {
+ tear_down(fixture);
+ fixture = NULL;
+ }
+ EXECUTE_TEST(execute_validate_msg_test, tear_down);
+ return result;
+}
+
+
+static int test_validate_with_sender(X509_NAME *name, int expected)
+{
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ fixture->expected = expected;
+ if (!TEST_ptr(fixture->msg = load_pkimsg(ir_protected_f))
+ || !TEST_true(OSSL_CMP_CTX_set1_expected_sender(fixture->cmp_ctx, name))
+ || !TEST_true(OSSL_CMP_CTX_set1_srvCert(fixture->cmp_ctx, srvcert))) {
+ tear_down(fixture);
+ fixture = NULL;
+ }
+ EXECUTE_TEST(execute_validate_msg_test, tear_down);
+ return result;
+}
+
+static int test_validate_msg_signature_expected_sender(void)
+{
+ return test_validate_with_sender(X509_get_subject_name(srvcert), 1);
+}
+
+static int test_validate_msg_signature_unexpected_sender(void)
+{
+ return test_validate_with_sender(X509_get_subject_name(root), 0);
+}
+
+static int test_validate_msg_unprotected_request(void)
+{
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ fixture->expected = 0;
+ if (!TEST_ptr(fixture->msg = load_pkimsg(ir_unprotected_f))) {
+ tear_down(fixture);
+ fixture = NULL;
+ }
+ EXECUTE_TEST(execute_validate_msg_test, tear_down);
+ return result;
+}
+
+static void setup_path(CMP_VFY_TEST_FIXTURE **fixture, X509 *wrong, int expired)
+{
+ (*fixture)->cert = endentity2;
+ (*fixture)->expected = wrong == NULL && !expired;
+ if (expired) {
+ X509_STORE *ts = OSSL_CMP_CTX_get0_trustedStore((*fixture)->cmp_ctx);
+ X509_VERIFY_PARAM *vpm = X509_STORE_get0_param(ts);
+ X509_VERIFY_PARAM_set_time(vpm, test_time_after_expiration);
+ }
+ if (!add_trusted((*fixture)->cmp_ctx, wrong == NULL ? root : wrong)
+ || !add_untrusted((*fixture)->cmp_ctx, endentity1)
+ || !add_untrusted((*fixture)->cmp_ctx, intermediate)) {
+ tear_down((*fixture));
+ (*fixture) = NULL;
+ }
+}
+
+static int test_validate_cert_path_ok(void)
+{
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ setup_path(&fixture, NULL, 0);
+ EXECUTE_TEST(execute_validate_cert_path_test, tear_down);
+ return result;
+}
+
+static int test_validate_cert_path_wrong_anchor(void)
+{
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ setup_path(&fixture, srvcert /* wrong/non-root cert */, 0);
+ EXECUTE_TEST(execute_validate_cert_path_test, tear_down);
+ return result;
+}
+
+static int test_validate_cert_path_expired(void)
+{
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ setup_path(&fixture, NULL, 1);
+ EXECUTE_TEST(execute_validate_cert_path_test, tear_down);
+ return result;
+}
+
+static int execute_MSG_check_received_test(CMP_VFY_TEST_FIXTURE *fixture)
+{
+ const OSSL_CMP_PKIHEADER *hdr = OSSL_CMP_MSG_get0_header(fixture->msg);
+ const ASN1_OCTET_STRING *tid = OSSL_CMP_HDR_get0_transactionID(hdr);
+
+ if (!TEST_int_eq(fixture->expected,
+ ossl_cmp_msg_check_received(fixture->cmp_ctx,
+ fixture->msg,
+ fixture->allow_unprotected_cb,
+ fixture->additional_arg)))
+ return 0;
+
+ if (fixture->expected < 0) /* error expected aready during above check */
+ return 1;
+ return
+ TEST_int_eq(0,
+ ASN1_OCTET_STRING_cmp(ossl_cmp_hdr_get0_senderNonce(hdr),
+ fixture->cmp_ctx->recipNonce))
+ && TEST_int_eq(0,
+ ASN1_OCTET_STRING_cmp(tid,
+ fixture->cmp_ctx->transactionID));
+}
+
+static int allow_unprotected(const OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *msg,
+ int invalid_protection, int allow)
+{
+ return allow;
+}
+
+static void setup_check_received(CMP_VFY_TEST_FIXTURE **fixture, int expected,
+ ossl_cmp_allow_unprotected_cb_t cb, int arg,
+ const unsigned char *trid_data,
+ const unsigned char *nonce_data)
+{
+ OSSL_CMP_CTX *ctx = (*fixture)->cmp_ctx;
+ int nonce_len = OSSL_CMP_SENDERNONCE_LENGTH;
+
+ (*fixture)->expected = expected;
+ (*fixture)->allow_unprotected_cb = cb;
+ (*fixture)->additional_arg = arg;
+ (*fixture)->msg = OSSL_CMP_MSG_dup(ir_rmprotection);
+ if ((*fixture)->msg == NULL
+ || (nonce_data != NULL
+ && !ossl_cmp_asn1_octet_string_set1_bytes(&ctx->senderNonce,
+ nonce_data, nonce_len))) {
+ tear_down((*fixture));
+ (*fixture) = NULL;
+ }
+ else if (trid_data != NULL) {
+ ASN1_OCTET_STRING *trid = ASN1_OCTET_STRING_new();
+ if (trid == NULL
+ || !ASN1_OCTET_STRING_set(trid, trid_data,
+ OSSL_CMP_TRANSACTIONID_LENGTH)
+ || !OSSL_CMP_CTX_set1_transactionID(ctx, trid)) {
+ tear_down((*fixture));
+ (*fixture) = NULL;
+ }
+ ASN1_OCTET_STRING_free(trid);
+ }
+}
+
+static int test_MSG_check_received_no_protection_no_cb(void)
+{
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ setup_check_received(&fixture, -1, NULL, 0, NULL, NULL);
+ EXECUTE_TEST(execute_MSG_check_received_test, tear_down);
+ return result;
+}
+
+static int test_MSG_check_received_no_protection_restrictive_cb(void)
+{
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ setup_check_received(&fixture, -1, allow_unprotected, 0, NULL, NULL);
+ EXECUTE_TEST(execute_MSG_check_received_test, tear_down);
+ return result;
+}
+
+static int test_MSG_check_received_no_protection_permissive_cb(void)
+{
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ setup_check_received(&fixture, OSSL_CMP_PKIBODY_IP, allow_unprotected, 1,
+ NULL, NULL);
+ EXECUTE_TEST(execute_MSG_check_received_test, tear_down);
+ return result;
+}
+
+static int test_MSG_check_received_check_transaction_id(void)
+{
+ /* Transaction id belonging to CMP_IR_rmprotection.der */
+ const unsigned char trans_id[OSSL_CMP_TRANSACTIONID_LENGTH] = {
+ 0x39, 0xB6, 0x90, 0x28, 0xC4, 0xBC, 0x7A, 0xF6,
+ 0xBE, 0xC6, 0x4A, 0x88, 0x97, 0xA6, 0x95, 0x0B
+ };
+
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ setup_check_received(&fixture, OSSL_CMP_PKIBODY_IP, allow_unprotected, 1,
+ trans_id, NULL);
+ EXECUTE_TEST(execute_MSG_check_received_test, tear_down);
+ return result;
+}
+
+static int test_MSG_check_received_check_transaction_id_bad(void)
+{
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ setup_check_received(&fixture, -1, allow_unprotected, 1, rand_data, NULL);
+ EXECUTE_TEST(execute_MSG_check_received_test, tear_down);
+ return result;
+}
+
+static int test_MSG_check_received_check_recipient_nonce(void)
+{
+ /* Recipient nonce belonging to CMP_IP_ir_rmprotection.der */
+ const unsigned char rec_nonce[OSSL_CMP_SENDERNONCE_LENGTH] = {
+ 0x48, 0xF1, 0x71, 0x1F, 0xE5, 0xAF, 0x1C, 0x8B,
+ 0x21, 0x97, 0x5C, 0x84, 0x74, 0x49, 0xBA, 0x32
+ };
+
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ setup_check_received(&fixture, OSSL_CMP_PKIBODY_IP, allow_unprotected, 1,
+ NULL, rec_nonce);
+ EXECUTE_TEST(execute_MSG_check_received_test, tear_down);
+ return result;
+}
+
+static int test_MSG_check_received_check_recipient_nonce_bad(void)
+{
+ SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ setup_check_received(&fixture, -1, allow_unprotected, 1, NULL, rand_data);
+ EXECUTE_TEST(execute_MSG_check_received_test, tear_down);
+ return result;
+}
+
+void cleanup_tests(void)
+{
+ X509_free(srvcert);
+ X509_free(clcert);
+ X509_free(endentity1);
+ X509_free(endentity2);
+ X509_free(intermediate);
+ X509_free(root);
+ X509_free(insta_cert);
+ X509_free(instaca_cert);
+ OSSL_CMP_MSG_free(ir_unprotected);
+ OSSL_CMP_MSG_free(ir_rmprotection);
+ return;
+}
+
+int setup_tests(void)
+{
+ /* Set test time stamps */
+ struct tm ts = { 0 };
+
+ ts.tm_year = 2018 - 1900; /* 2018 */
+ ts.tm_mon = 1; /* February */
+ ts.tm_mday = 18; /* 18th */
+ test_time_valid = mktime(&ts); /* February 18th 2018 */
+ ts.tm_year += 10; /* February 18th 2028 */
+ test_time_after_expiration = mktime(&ts);
+
+ RAND_bytes(rand_data, OSSL_CMP_TRANSACTIONID_LENGTH);
+ if (!TEST_ptr(server_f = test_get_argument(0))
+ || !TEST_ptr(client_f = test_get_argument(1))
+ || !TEST_ptr(endentity1_f = test_get_argument(2))
+ || !TEST_ptr(endentity2_f = test_get_argument(3))
+ || !TEST_ptr(root_f = test_get_argument(4))
+ || !TEST_ptr(intermediate_f = test_get_argument(5))
+ || !TEST_ptr(ir_protected_f = test_get_argument(6))
+ || !TEST_ptr(ir_unprotected_f = test_get_argument(7))
+ || !TEST_ptr(ip_waiting_f = test_get_argument(8))
+ || !TEST_ptr(ir_rmprotection_f = test_get_argument(9))
+ || !TEST_ptr(instacert_f = test_get_argument(10))
+ || !TEST_ptr(instaca_f = test_get_argument(11))
+ || !TEST_ptr(ir_protected_0_extracerts = test_get_argument(12))
+ || !TEST_ptr(ir_protected_2_extracerts = test_get_argument(13))) {
+ TEST_error("usage: cmp_vfy_test server.crt client.crt "
+ "EndEntity1.crt EndEntity2.crt "
+ "Root_CA.crt Intermediate_CA.crt "
+ "CMP_IR_protected.der CMP_IR_unprotected.der "
+ "IP_waitingStatus_PBM.der IR_rmprotection.der "
+ "insta.cert.pem insta_ca.cert.pem "
+ "IR_protected_0_extraCerts.der "
+ "IR_protected_2_extraCerts.der\n");
+ return 0;
+ }
+
+ /* Load certificates for cert chain */
+ if (!TEST_ptr(endentity1 = load_pem_cert(endentity1_f))
+ || !TEST_ptr(endentity2 = load_pem_cert(endentity2_f))
+ || !TEST_ptr(root = load_pem_cert(root_f))
+ || !TEST_ptr(intermediate = load_pem_cert(intermediate_f)))
+ goto err;
+
+ if (!TEST_ptr(insta_cert = load_pem_cert(instacert_f))
+ || !TEST_ptr(instaca_cert = load_pem_cert(instaca_f)))
+ goto err;
+
+ /* Load certificates for message validation */
+ if (!TEST_ptr(srvcert = load_pem_cert(server_f))
+ || !TEST_ptr(clcert = load_pem_cert(client_f)))
+ goto err;
+ if (!TEST_int_eq(1, RAND_bytes(rand_data, OSSL_CMP_TRANSACTIONID_LENGTH)))
+ goto err;
+ if (!TEST_ptr(ir_unprotected = load_pkimsg(ir_unprotected_f))
+ || !TEST_ptr(ir_rmprotection = load_pkimsg(ir_rmprotection_f)))
+ goto err;
+
+ /* Message validation tests */
+ ADD_TEST(test_verify_popo);
+ ADD_TEST(test_verify_popo_bad);
+ ADD_TEST(test_validate_msg_signature_trusted_ok);
+ ADD_TEST(test_validate_msg_signature_trusted_expired);
+ ADD_TEST(test_validate_msg_signature_srvcert_wrong);
+ ADD_TEST(test_validate_msg_signature_bad);
+ ADD_TEST(test_validate_msg_signature_sender_cert_srvcert);
+ ADD_TEST(test_validate_msg_signature_sender_cert_untrusted);
+ ADD_TEST(test_validate_msg_signature_sender_cert_trusted);
+ ADD_TEST(test_validate_msg_signature_sender_cert_extracert);
+ ADD_TEST(test_validate_msg_signature_sender_cert_absent);
+ ADD_TEST(test_validate_msg_signature_expected_sender);
+ ADD_TEST(test_validate_msg_signature_unexpected_sender);
+ ADD_TEST(test_validate_msg_unprotected_request);
+ ADD_TEST(test_validate_msg_mac_alg_protection);
+ ADD_TEST(test_validate_msg_mac_alg_protection_bad);
+
+ /* Cert path validation tests */
+ ADD_TEST(test_validate_cert_path_ok);
+ ADD_TEST(test_validate_cert_path_expired);
+ ADD_TEST(test_validate_cert_path_wrong_anchor);
+
+ ADD_TEST(test_MSG_check_received_no_protection_no_cb);
+ ADD_TEST(test_MSG_check_received_no_protection_restrictive_cb);
+ ADD_TEST(test_MSG_check_received_no_protection_permissive_cb);
+ ADD_TEST(test_MSG_check_received_check_transaction_id);
+ ADD_TEST(test_MSG_check_received_check_transaction_id_bad);
+ ADD_TEST(test_MSG_check_received_check_recipient_nonce);
+ ADD_TEST(test_MSG_check_received_check_recipient_nonce_bad);
+
+ return 1;
+
+ err:
+ cleanup_tests();
+ return 0;
+
+}
diff --git a/test/recipes/65-test_cmp_vfy.t b/test/recipes/65-test_cmp_vfy.t
new file mode 100644
index 0000000000..c07e693f8a
--- /dev/null
+++ b/test/recipes/65-test_cmp_vfy.t
@@ -0,0 +1,36 @@
+#! /usr/bin/env perl
+# 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
+
+use strict;
+use OpenSSL::Test qw/:DEFAULT data_file/;
+use OpenSSL::Test::Utils;
+
+setup("test_cmp_vfy");
+
+plan skip_all => "This test is not supported in a no-cmp build"
+ if disabled("cmp");
+
+plan skip_all => "This test is not supported in a no-ec build"
+ if disabled("ec");
+
+plan tests => 1;
+
+ok(run(test(["cmp_vfy_test",
+ data_file("server.crt"), data_file("client.crt"),
+ data_file("EndEntity1.crt"), data_file("EndEntity2.crt"),
+ data_file("Root_CA.crt"), data_file("Intermediate_CA.crt"),
+ data_file("IR_protected.der"),
+ data_file("IR_unprotected.der"),
+ data_file("IP_waitingStatus_PBM.der"),
+ data_file("IR_rmprotection.der"),
+ data_file("insta.cert.pem"),
+ data_file("insta_ca.cert.pem"),
+ data_file("IR_protected_0_extraCerts.der"),
+ data_file("IR_protected_2_extraCerts.der")])));
diff --git a/test/recipes/65-test_cmp_vfy_data/EndEntity1.crt b/test/recipes/65-test_cmp_vfy_data/EndEntity1.crt
new file mode 100644
index 0000000000..4e05449889
--- /dev/null
+++ b/test/recipes/65-test_cmp_vfy_data/EndEntity1.crt
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICnDCCAYSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADASMRAwDgYDVQQDEwdSb290
+IENBMB4XDTE3MTEwODE1NDgwMFoXDTE4MTEwODExMTkwMFowETEPMA0GA1UEAxMG
+Q2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtNiWJufEotHe
+p6E/4b0laX7K1NRamNoUokLIsq78RoBieBXaGxIdbT6zmhLnLmZdb0UN3v7FUP75
+rqPN2yyj3TbS4o5ilh5El8bDDAPhW5lthCddvH/uBziRAM5oIB4xxOumNbgHpLUT
+Clh49sdXd4ydYpCTWld5emRouBmMUeP/0EkyWMBIrHGSBxrqtFVRXhxvVHImQv6Z
+hIKql7dCVCZbhUtxw6sLxIGL4xlhKoM2o31k4I/9tjZrWSZZ7KAIOlOLrjxZc/bQ
+MwvxVUgS+C+iXzhCY8v+N/K37jwtAAk4C1aOGv/VygNcN0C/ynfKSzFmtnfei4+3
+6GC7HtFzewIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQB3GYpPSCCYsJM5owKcODr/
+I1aJ8jQ+u5jCKjvYLp6Cnbr4AbRXzvKuMyV6UfIAQbrGOxAClvX++5/ZQbhY+TxN
+iiUM3yr5yYCLqj4MeYHhJ3gOzcppAO9LQ9V7eA8C830giZMm3cpApFSLP8CpwNUD
+W/fgoQfaOae5IYPZdea88Gmt5RVNbtHgVqtm4ifTQo577kfxTeh20s+M6pgYW3/R
+vftXy2ITEtk/j3NcRvOyZ7Bu1mAg7wNeUjL+gDWAaxs16LsWsCsUGwfr/Z2Rq1CF
+zB0XwIyigkVLDLqDzUShcw0Eb/zYy2KXsxNWA2tb27mw+T+tmmOszpn7JjLrlVks
+-----END CERTIFICATE-----
diff --git a/test/recipes/65-test_cmp_vfy_data/EndEntity2.crt b/test/recipes/65-test_cmp_vfy_data/EndEntity2.crt
new file mode 100644
index 0000000000..ba06210794
--- /dev/null
+++ b/test/recipes/65-test_cmp_vfy_data/EndEntity2.crt
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB3zCCAZSgAwIBAgIBBjAKBggqhkjOPQQDAzAVMRMwEQYDVQQDEwpad2lzY2hl
+bkNBMB4XDTE3MTEwODE2MDUwMFoXDTE4MTEwODExMTkwMFowEjEQMA4GA1UEAxMH
+Q2xpZW50MjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALTYlibnxKLR
+3qehP+G9JWl+ytTUWpjaFKJCyLKu/EaAYngV2hsSHW0+s5oS5y5mXW9FDd7+xVD+
++a6jzdsso9020uKOYpYeRJfGwwwD4VuZbYQnXbx/7gc4kQDOaCAeMcTrpjW4B6S1
+EwpYePbHV3eMnWKQk1pXeXpkaLgZjFHj/9BJMljASKxxkgca6rRVUV4cb1RyJkL+
+mYSCqpe3QlQmW4VLccOrC8SBi+MZYSqDNqN9ZOCP/bY2a1kmWeygCDpTi648WXP2
+0DML8VVIEvgvol84QmPL/jfyt+48LQAJOAtWjhr/1coDXDdAv8p3yksxZrZ33ouP
+t+hgux7Rc3sCAwEAAaMNMAswCQYDVR0TBAIwADAKBggqhkjOPQQDAwM5ADA2AhkA
+qASBLwTauET6FGp/EBe7b/99jTyGB861AhkA5ILGkLX4KmjRkTcNxJ3JKB1Sumya
+cbqF
+-----END CERTIFICATE-----
diff --git a/test/recipes/65-test_cmp_vfy_data/IP_waitingStatus_PBM.der b/test/recipes/65-test_cmp_vfy_data/IP_waitingStatus_PBM.der
new file mode 100644
index 0000000000..76888e84f7
--- /dev/null
+++ b/test/recipes/65-test_cmp_vfy_data/IP_waitingStatus_PBM.der
Binary files differ
diff --git a/test/recipes/65-test_cmp_vfy_data/IP_waitingStatus_PBM.txt b/test/recipes/65-test_cmp_vfy_data/IP_waitingStatus_PBM.txt
new file mode 100644
index 0000000000..89837972c9
--- /dev/null
+++ b/test/recipes/65-test_cmp_vfy_data/IP_waitingStatus_PBM.txt
@@ -0,0 +1,2 @@
+Reference#: 4787
+Secret Value: 9pp8-b35i-Xd3Q-udNR
diff --git a/test/recipes/65-test_cmp_vfy_data/IR_protected.der b/test/recipes/65-test_cmp_vfy_data/IR_protected.der
new file mode 100644
index 0000000000..ce0a7a46dc
--- /dev/null
+++ b/test/recipes/65-test_cmp_vfy_data/IR_protected.der
Binary files differ
diff --git a/test/recipes/65-test_cmp_vfy_data/IR_protected_0_extraCerts.der b/test/recipes/65-test_cmp_vfy_data/IR_protected_0_extraCerts.der
new file mode 100755
index 0000000000..1c26028082
--- /dev/null
+++ b/test/recipes/65-test_cmp_vfy_data/IR_protected_0_extraCerts.der
Binary files differ
diff --git a/test/recipes/65-test_cmp_vfy_data/IR_protected_2_extraCerts.der b/test/recipes/65-test_cmp_vfy_data/IR_protected_2_extraCerts.der
new file mode 100755
index 0000000000..56faad4a0f
--- /dev/null
+++ b/test/recipes/65-test_cmp_vfy_data/IR_protected_2_extraCerts.der
Binary files differ
diff --git a/test/recipes/65-test_cmp_vfy_data/IR_rmprotection.der b/test/recipes/65-test_cmp_vfy_data/IR_rmprotection.der
new file mode 100644
index 0000000000..e84c64d8e0
--- /dev/null
+++ b/test/recipes/65-test_cmp_vfy_data/IR_rmprotection.der
Binary files differ
diff --git a/test/recipes/65-test_cmp_vfy_data/IR_unprotected.der b/test/recipes/65-test_cmp_vfy_data/IR_unprotected.der
new file mode 100644
index 0000000000..41a649691f
--- /dev/null
+++ b/test/recipes/65-test_cmp_vfy_data/IR_unprotected.der
Binary files differ
diff --git a/test/recipes/65-test_cmp_vfy_data/Intermediate_CA.crt b/test/recipes/65-test_cmp_vfy_data/Intermediate_CA.crt
new file mode 100644
index 0000000000..3416cdb959
--- /dev/null
+++ b/test/recipes/65-test_cmp_vfy_data/Intermediate_CA.crt
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIB1jCBv6ADAgECAgEFMA0GCSqGSIb3DQEBDQUAMBIxEDAOBgNVBAMTB1Jvb3Qg
+Q0EwHhcNMTcxMTA4MTYwNDAwWhcNMTgxMTA4MTExOTAwWjAVMRMwEQYDVQQDEwpa
+d2lzY2hlbkNBMEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAE9bJcmZWj2CmO6aW8
+9Qylkj1WgPREf9/s4Z1VYqFODeJnebPXFBLVH/aoGxnds9E9oxAwDjAMBgNVHRME
+BTADAQH/MA0GCSqGSIb3DQEBDQUAA4IBAQBwQD4NTIWMMevEsSrBpKjjQEWc81Ct
+eXoyAXr/d8wgVyuIZe9C7ekxPQCwowcmONUyeYQv9N2eYpdhkAQuk6DS4+aDR4s7
+I6rg5R5CUGGla5NUxM0BKIS3ZIezvEGlP1NFN+HBgJI7ZIIYQ3zDr0EYgo4J7Xvm
+5p58pcCZSsbVyKwKs6T+rTzOVVmJ2L1bWzywZEDmzxMkPmA6fP9XtB4Kx/b4oviw
+TEQl3Jf9EkBvBkKX2rRJs7aMJo4MwOnE4HHOV5GAQqhGrXltsuXmVfIQPtRN4xlK
+oNf/FukI1NcBh4A/iY4PmbyxHYmKy6qjFjng2u2VFtH15HDT4XlLP5gq
+-----END CERTIFICATE-----
diff --git a/test/recipes/65-test_cmp_vfy_data/Root_CA.crt b/test/recipes/65-test_cmp_vfy_data/Root_CA.crt
new file mode 100644
index 0000000000..6ccf362546
--- /dev/null
+++ b/test/recipes/65-test_cmp_vfy_data/Root_CA.crt
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICrzCCAZegAwIBAgIBATANBgkqhkiG9w0BAQUFADASMRAwDgYDVQQDEwdSb290
+IENBMB4XDTE3MTEwODE1NDUwMFoXDTE4MTEwODExMTkwMFowEjEQMA4GA1UEAxMH
+Um9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALiHdLAD2Wu+
+C5UDMK6WCL53Wz0CeU61RRRlGEVSqHrQOWnffgVutgftzsddxxgJJyGsqKo1B+nQ
+vapyJyugYJWYNQLN5+iffe4y1UBPnHMQFHiZ4cNR6PB0eHja2wpcN3QmJzOcpRYE
+xf+QQwJNFqhRi0cZGfd/JfFi/ybJalqClbnYMPcJo7g6S7M3lWbOnEOUWnbM2EBp
+h849mC+kd80vXcRcb7U/3MJKK3Ee72TDye5/kWFf9zcxj2ac0oCiS66JKYobiVJr
+NmbGM0I9U6T6ejXVUu2J3pGUFlcf3RCUYf1aWhkmzEzbm/FGMRJ7vVyCXm/OWIh9
+bqtwH5YfljsCAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOC
+AQEAF7tSa9oVan7kPR5/TXB330Ca1xQt5C38afaJbacR9mM8ZkL0HceQTuJGrnAR
+4kK7CaB5iraU6Lxyql7drq8aixz/7TXna6c172J6HxDeFhQMeSt1LAh7XN5Ir6Y6
+iO7XD5I5lw3Xv6qvhoD0ktkNk/WtF7aBw2ZAi+RcDMgWzWjoS4WqMbvWEHw10j9b
+s8R0YG4yi6wb89UNIMfQtC2XviHKcRS9MzIJQHw73r2EY2t6o9TO+5ukHYDB6/Zo
+/CLXu21MzsFvhupHgX6zdptU324tq2za1+4LvmOHSW+D36jEPT22SndXmHo5VmAn
+6bQ52MhBI0rrWwju9aBpVzsUUg==
+-----END CERTIFICATE-----
diff --git a/test/recipes/65-test_cmp_vfy_data/chain.txt b/test/recipes/65-test_cmp_vfy_data/chain.txt
new file mode 100644
index 0000000000..1b55c25abb
--- /dev/null
+++ b/test/recipes/65-test_cmp_vfy_data/chain.txt
@@ -0,0 +1,4 @@
+1 - Root_CA (self-signed)
+1.1 - EndEntity1
+1.2 Intermediate_CA
+1.2.1 EndEntity2
diff --git a/test/recipes/65-test_cmp_vfy_data/client.crt b/test/recipes/65-test_cmp_vfy_data/client.crt
new file mode 100644
index 0000000000..fa6cdec436
--- /dev/null
+++ b/test/recipes/65-test_cmp_vfy_data/client.crt
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICtTCCAZ2gAwIBAgIBATANBgkqhkiG9w0BAQUFADAeMRwwGgYDVQQKExNjbGll
+bnQgb3JnYW5pemF0aW9uMB4XDTE4MDIwNjEyNDYwMFoXDTE5MDIwNjEyNDYwMFow
+HjEcMBoGA1UEChMTY2xpZW50IG9yZ2FuaXphdGlvbjCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBALf0KDl07RlJlQVQcibB+JiUyXthFz4nzIkFVNISORCG
+JpTUMLLWxhF7Sc1li7gzvMAvx77SUThaNEOd0d2MJBbOXmZ9MtAOy8yHzRejBpw1
+mn9e0FKCI8rz3ttivspsGUIsRDhvMxuOol3lQ93QjfI593D7BOfBGdhsivD8m6+P
+EzF400iOSsbo3VPlArA+xTHiCyJ+E9p3yFGvzQrmK/MjM3lW3l3tARJmbXINIOio
+PnVmh4nsc9awqDGQdQTup/EjuY15AR4Vck7Zf9e+6U9t7Ow7aaZ5gX07FNnnqlVs
+48Lj/5a2Qgh482SQNvvRWAkPwTQNZ40ThKvOR/sB3iUCAwEAATANBgkqhkiG9w0B
+AQUFAAOCAQEAUSZBAR22ICoO7oPKMpfxwIHDAODv6jEHx2fSTpwxqodVqwF8ghAS
+PvJwQ4+7+yny4ieX9iVicRdXXT8kEOQL5/3/Q+cBj/BzYE0VGxo4YloHQwWNdSg0
+B1oQ/4dAnDntGnXHDJMSZCGq/jj4/56XSrPymzR2jgOQNqnEkMM9/SpW7MirixJA
+ZBR5Oc97vKaAH89ucsTu0neVMTXgywdBiy1W8bo4XxquK6VDRHlLj4gMLBpaG0mW
+r5ST1gs8j63nK/eZ7AuTV/tvI25reXT+2wOKAgPEcd02Fh61TO4IPD4GDidEHvAw
+6m66U9eFVezkxDvjE0X5voFnsBl4qTSZ/A==
+-----END CERTIFICATE-----
diff --git a/test/recipes/65-test_cmp_vfy_data/insta.cert.pem b/test/recipes/65-test_cmp_vfy_data/insta.cert.pem
new file mode 100755
index 0000000000..4a25699f05
--- /dev/null
+++ b/test/recipes/65-test_cmp_vfy_data/insta.cert.pem
@@ -0,0 +1,25 @@
+-----BEGIN CERTIFICATE-----
+MIIEHTCCAwWgAwIBAgIEBtqJKDANBgkqhkiG9w0BAQsFADA6MQswCQYDVQQGEwJG
+STETMBEGA1UECgwKSW5zdGEgRGVtbzEWMBQGA1UEAwwNSW5zdGEgRGVtbyBDQTAe
+Fw0xODAxMzAxMDE0MjNaFw0xODA0MzAyMzU5NTlaMBAxDjAMBgNVBAMMBXRlc3Qx
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoiNNxo5pwk1lD1em3mad
+bpKz86GSYyGlQtd0ZhIX1tOUFo9lFex7n5Osv0A99pKb+7EKqB9Ghg6mJ29kIUUm
+LACnfZJ/q+U6s9T4zFrYyXweUNJvQgbA2ojDPyVoRp2T1ekahPh4DpxPWNKfYECD
+RbrxkHMM3WiIqYFLU8hYvEMGSWRHHbnS/vG7MTaVDkR8d0zixTOp0fST5c1UUTqp
+pYlThac/BG1kk3hyjIjz5o7lspfX3s/eAYgT9GhYHL6Uy4o4OqCleR39aVc0dMrr
+jb7hsmX6ecNwqJOE5AHHOG4Ti6CbweSOcdH5PRFzdpao5rlTErsFHlUSTca4mfVe
+WwIDAQABo4IBUzCCAU8wHwYDVR0jBBgwFoAUPHjduMGNV/UFKl5t4FhySvpEJWEw
+HQYDVR0OBBYEFD0oLwov3vSGa1f9bIKGzWoPP0A1MCcGA1UdEQQgMB6CBGFiLmOC
+BGRlLmaCBHRlc3SHBAECA/+HBAQFBgcweAYDVR0fBHEwbzBtoGugaYZnbGRhcDov
+L3BraS5jZXJ0aWZpY2F0ZS5maTozODkvQ049SW5zdGElMjBEZW1vJTIwQ0EsTz1J
+bnN0YSUyMERlbW8sQz1GST9jZXJ0aWZpY2F0ZVJldm9jYXRpb25MaXN0O2JpbmFy
+eTBqBggrBgEFBQcBAQReMFwwWgYIKwYBBQUHMAKGTmxkYXA6Ly93d3cuY2VydGlm
+aWNhdGUuZmk6Mzg5L0NOPUluc3RhIERlbW8gQ0EsTz1JbnN0YSBEZW1vLEM9Rkk/
+Y2FDZXJ0aWZpY2F0ZTANBgkqhkiG9w0BAQsFAAOCAQEAbIRfAbU0v2RWZg6lkjZg
+58fi4lT7Xk64sK4G/QLZ8TR/jg5UR0J5IvYt52YBjs/tjwJokkaW7+DyhVKkPrGs
+oexpdLRSnXBv33+Yj+MZbSfrIX1Ox7up+ovs8le4viSlUqIDWBuuUBfZ16BFMmnB
+UwDar8p/ci9ReKJH+FmvxlHbTHdMznZooSxTZm96HTutuiULL/SzZ2FpUsd7G5EE
+mRA6uRVV1tuysD15H+9paqVwd0RaKee8Z63cDi3NXOxUcCnpINHrjVsdcW47/73V
+IgfU4t39BKNiQNL0ADYpCyrpntTpsyZWrNmYzXMgLYEXxi4s6obusY0I3Qg+U31o
+Uw==
+-----END CERTIFICATE-----
diff --git a/test/recipes/65-test_cmp_vfy_data/insta.priv.pem b/test/recipes/65-test_cmp_vfy_data/insta.priv.pem
new file mode 100755
index 0000000000..8612994d15
--- /dev/null
+++ b/test/recipes/65-test_cmp_vfy_data/insta.priv.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAoiNNxo5pwk1lD1em3madbpKz86GSYyGlQtd0ZhIX1tOUFo9l
+Fex7n5Osv0A99pKb+7EKqB9Ghg6mJ29kIUUmLACnfZJ/q+U6s9T4zFrYyXweUNJv
+QgbA2ojDPyVoRp2T1ekahPh4DpxPWNKfYECDRbrxkHMM3WiIqYFLU8hYvEMGSWRH
+HbnS/vG7MTaVDkR8d0zixTOp0fST5c1UUTqppYlThac/BG1kk3hyjIjz5o7lspfX
+3s/eAYgT9GhYHL6Uy4o4OqCleR39aVc0dMrrjb7hsmX6ecNwqJOE5AHHOG4Ti6Cb
+weSOcdH5PRFzdpao5rlTErsFHlUSTca4mfVeWwIDAQABAoIBAQCUYAZevBjgbP8c
+qTPVtsY/WBVB0Qwrl7CqmIy2k7wpJfoRIyx4ga8n+3ZMlredm9EO5ZdA/04EhAdd
+czyIjcU+42JjMduQLPgpda4xJLnauLDteYXDQHbgBePXN55TcQTG7skMAm2rwTOD
+r0uWQ7Nd7pP9gqu1OmJF7EJI68D4llCU1FrOrliwSDzHWP3p4QmCW3M9PQJ68xw1
+gE7X1QflROGivcFoRgcgeoJDzpxveGvPbEn6Q+05/FMRVxjqWhpxdZ9/SL7iRz1e
+45T+P9a8OLgTyErT3Lp/f/vuHA1tlbAYumhSnxXsb+nHi80aDcImOrNQHAp076Ik
+bkZ1NpOxAoGBAM3Ulgi2hUPdoAMFtHZF8eBHRzn+4uTfY2s33wObiUJQ8VbGDeJY
+ifCfOwLThiAXAqktrs7ItwWDYmzd5xPYGQeWoKcBEoZ+dvaaOe8H7TCMnjB3R3i1
+ACSDHo/3c+NfFOnPJtXL85jeAqGYH50uOtYmYaBVe6xASTBgNvP7snYHAoGBAMmo
+ZBQqgIhoqMRapGh6n4OpzH0Nt9ruOTJoteAfbLdAu7X+wAaMuxEaAmZQRDYj0lzX
+Ty8DlKSy7vfvXtghxMBEv4+dsYpagXcUOeEZSPfu1c3e253dAov6C0MdREKWBT7P
++NwPBowPy0CP/yBeHaw7d/P7/SYIoPXLGraGl6ANAoGBAMmmce7LUjgw0mjjl+8f
+i14cts08x3FO4YnTTOut34VW43oNwuBzuYBBn4CfVpHtuS+hj9cKkTQXib/6jj7E
+wZDLo0y6Ijodf9MNOaDSdS/RM9Frqlu5iBA9XR3SYnjpWAXQas2eaGLlblJ+RMqq
+1f2j0JVR6j3RJWL9gBj8B9TVAoGBALYZrs4bF1iXEhfGNoL2gIdX1QX0VluIFfR0
+ZBDQr87H0Ppm4qbHfMHTt+kGgKJXNMaL08CDvj4AKxWPfhk0XUS2kDmzUDi8w/5x
+MFcaCy+A6Gdw4OcsRfl7QaJIknSCnpf7HCI0G1hthsB1iBCFjMwUI50ap54p2pg6
+4ZOD9PYdAoGAERi5Hlq7+rJeDi3VunKHySqV9mvbOPNclEUmAdKi1yuu3INF1Zgv
+Lf432ZI/Ufk2g888ed5ZGE1IMULc2tgSIAMzdX4ZYI4uGFLkHWzSOM6a7NCeZuVt
+W+NgUYa2qsqFEd9kqaoDbNry+nPvLM7fWXvBoE4oNkeJhHjOIabBPvw=
+-----END RSA PRIVATE KEY-----
diff --git a/test/recipes/65-test_cmp_vfy_data/insta_ca.cert.pem b/test/recipes/65-test_cmp_vfy_data/insta_ca.cert.pem
new file mode 100755
index 0000000000..4b7e31b86d
--- /dev/null
+++ b/test/recipes/65-test_cmp_vfy_data/insta_ca.cert.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDkDCCAnigAwIBAgIDCZU1MA0GCSqGSIb3DQEBBQUAMDoxCzAJBgNVBAYTAkZJ
+MRMwEQYDVQQKEwpJbnN0YSBEZW1vMRYwFAYDVQQDEw1JbnN0YSBEZW1vIENBMB4X
+DTA2MDEwMjA4NDgzOFoXDTI1MTIzMTA4NDgzOFowOjELMAkGA1UEBhMCRkkxEzAR
+BgNVBAoTCkluc3RhIERlbW8xFjAUBgNVBAMTDUluc3RhIERlbW8gQ0EwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDF57bSwj+hZnkgLyLtFsoNIN19qBv9
+GIoqFaCiPvw6VQgMXR15t+Z5sdYHdydGp875yJD4wDq2K7cjMoCXALxLeyp6dCY6
+WPC6Hk3QvZtMRuDAz8+0Nb5qaC4+O+7c7j1h/Gs8Jpj+TUuSkmtlCVIGPSWkWaQl
+FhLWeUnKRW8bj1CJQguV7igF19kGQKUZ/VZj+n5xIXKHc8njC1ZrS/s0IBFViQkZ
+63nTdNPLHQ4Xu8uKrbJbYEK1S4KVNH3L9yA4ut+brqX8n6OulTsKntvMdwNWZdor
+KoM15D3lmM7QUGDflJdSQ/qvBVTda+ccrT21sp4hdwwiU01vxQguT26JAgMBAAGj
+gZ4wgZswHwYDVR0jBBgwFoAUPHjduMGNV/UFKl5t4FhySvpEJWEwHQYDVR0OBBYE
+FDx43bjBjVf1BSpebeBYckr6RCVhMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8E
+CDAGAQH/AgEAMDUGCWCGSAGG+EIBDQQoFiZJbnN0YSBEZW1vIENBIC0gb25seSBm
+b3IgZGVtbyBwdXJwb3NlczANBgkqhkiG9w0BAQUFAAOCAQEAuVRmRimTxVTZMNXi
+3u4bRCq7GxJ4Lonx3mocxYiwBjCYwqn5dPAd4AHrA1HWYCEvIPo52FibpUNNljqH
+v7CSoEBg2f4If6cFtwudobqNvf8Z50CAnxlwpPy4k+EbXlh49/uZBtu8+Lc2Ss7L
+QaNHHiOeHxYeGX7pTcr6fnXQWAbbn4SLyqniW7ZTqjNJvC79Ym7KowMYzCbmozzv
+3xqElA+g/MLFfxn52c/vl/obOVk5eBf3f7V68qKL2IDEip3fyZyoelhfTypq944m
+sSJFQjoVzgd7ykgouEwOceOT8YMWWigNsWl/hsVJ03Ri7TxRX4+v8dMEbat+SsTL
+AqTTgQ==
+-----END CERTIFICATE-----
diff --git a/test/recipes/65-test_cmp_vfy_data/server.crt b/test/recipes/65-test_cmp_vfy_data/server.crt
new file mode 100644
index 0000000000..ed1d43333e
--- /dev/null
+++ b/test/recipes/65-test_cmp_vfy_data/server.crt
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICpTCCAY2gAwIBAgIBATANBgkqhkiG9w0BAQUFADAWMRQwEgYDVQQKDAtvcGVu
+c3NsX2NtcDAeFw0xNzEyMjAxMzA0MDBaFw0xODEyMjAxMzA0MDBaMBYxFDASBgNV
+BAoMC29wZW5zc2xfY21wMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+4ckRrH0UWmIJFj99kBqvCipGjJRAaPkdvWjdDQLglTpI3eZAJHnq0ypW/PZccrWj
+o7mxuvAStEYWF+5Jx6ZFmAsC1K0NNebSAZQoLWYZqiOzkfVVpLicMnItNFElfCoh
+BzPCYmF5UlC5yp9PSUEfNwPJqDIRMtw+IlVUV3AJw9TJ3uuWq/vWW9r96/gBKKdd
+mj/q2gGT8RC6LxEaolTbhfPbHaA1DFpv1WQFb3oAV3Wq14SOZf9bH1olBVsmBMsU
+shFEw5MXVrNCv2moM4HtITMyjvZe7eIwHzSzf6dvQjERG6GvZ/i5KOhaqgJCnRKd
+HHzijz9cLec5p9NSOuC1OwIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQDGUXpFCBkV
+WgPrBfZyBwt6VCjWB/e67q4IdcKMfDa4hwSquah1AyXHI0PlC/qitnoSx2+7f7pY
+TEOay/3eEPUl1J5tdPF2Vg56Dw8jdhSkMwO7bXKDEE3R6o6jaa4ECgxwQtdGHmNU
+A41PgKX76yEXku803ptO39/UR7i7Ye3MbyAmWE+PvixJYUbxd3fqz5fsaJqTCzAy
+AT9hrr4uu8J7m3LYaYXo4LVL4jw5UsP5bIYtpmmEBfy9GhpUqH5/LzBNij7y3ziE
+T59wHkzawAQDHsBPuCe07DFtlzqWWvaih0TQAw9MZ2tbyK9jt7P80Rqt9CwpM/i9
+jQYqSl/ix5hn
+-----END CERTIFICATE-----
diff --git a/test/recipes/65-test_cmp_vfy_data/server.key b/test/recipes/65-test_cmp_vfy_data/server.key
new file mode 100644
index 0000000000..2324266798
--- /dev/null
+++ b/test/recipes/65-test_cmp_vfy_data/server.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA4ckRrH0UWmIJFj99kBqvCipGjJRAaPkdvWjdDQLglTpI3eZA
+JHnq0ypW/PZccrWjo7mxuvAStEYWF+5Jx6ZFmAsC1K0NNebSAZQoLWYZqiOzkfVV
+pLicMnItNFElfCohBzPCYmF5UlC5yp9PSUEfNwPJqDIRMtw+IlVUV3AJw9TJ3uuW
+q/vWW9r96/gBKKddmj/q2gGT8RC6LxEaolTbhfPbHaA1DFpv1WQFb3oAV3Wq14SO
+Zf9bH1olBVsmBMsUshFEw5MXVrNCv2moM4HtITMyjvZe7eIwHzSzf6dvQjERG6Gv
+Z/i5KOhaqgJCnRKdHHzijz9cLec5p9NSOuC1OwIDAQABAoIBAGiYVO+rIfqc38jG
+sMxJED2NSBFnvE7k2LoeEgktBA0daxQgziYXtIkOXC3jkwAw1RXLuGH5RTDuJt3/
+LX6nsCW3NCCB6lTGERNaJyKg4dLHpzA+juY3/2P/MKHD1bGncpV7jNk2fpV7gBY1
+pu0wld1Oi+S3DPCaxs3w6Zl39Y4Z7oSNf6DRO5lGN3Asc8TSVjIOWpAl8LIg+P2B
+ZvFeHRANVXaV9YmF2uEi7iMgH4vGrK2svsmM9VThVO4ArGcTRTvGYn7aw3/H4Pt+
+lYuhERdpkKBT0tCgIpO5IJXMl4/5RSDTtcBwiJcReN5IHUAItBIPSHcMflNSKG/I
+aQf4u0ECgYEA8+PAyzn096Y2UrKzE75yuadCveLjsUWx2NN5ZMohQru99F4k7Pab
+/Te4qOe5zlxHAPK3LRwvbwUWo5mLfs45wFrSgZoRlYcCuL+JaX0y2oXMMF9E+UkY
+tljMt/HpLo1SfSjN2Sae4LVhC7rWJ43LtyRepptzBPGqd26eLPGAMr8CgYEA7P8u
+RGkMOrMzEKAb0A9smrzq2xW88T1VejqEt6R8mUcNt8PFHMgjuzVU4zDysrlb7G/0
+VSkQWnJxBh1yNGc1Av7YgwicIgApr4ty0hZhLcnKX2VrNw+L/sSe/cnwVAc6RtPK
+RR6xQubuLlrCGcbYXmyn5Jv+nlY0S3uCyDFHqIUCgYAwtpLxhJf7RwWeqva9wNJl
+ZpUcHE9iPwtwxXx/tyfBjoI4Zv11HyS1BQYrJm2kXCYKeHBB4FlREXEeKDMGluZO
+F1XocP+GIDtY71jg6xLXNtY76yt5pzH6ae4p53WtyKhrO1UyRFaDh3bkwuK3b8j6
+wZbuLCpjGGn2BPAvBeWXPQKBgEewKN6op/pZmmi9Bay5/bAQ1TnQKYcPdnuyl9K0
+/ruespeTsFw0bhqC11qhw8gsKZIri0z3TusNEwM2hQU08uQlEnkQcaoXQoTHOcQy
+4NJo575Tf0r4ePBnqXA7VWcViJtEFTszPYtvLzz2VyBU9b4aP+73AN4EVW0/vx+v
+SG3BAoGBAMzESFA2TXwUFmozK5zowIszc995Xqpi7mXKk77WESOpoS1dQ1wF1dSg
+XOwxzFoYovLxcc1K9lqOrod8BV+qGuEfc/PIJ2aiXjvEDeZYX2eWaANNmj4OSLoJ
+MNYj9tZxbq56slD7snf7AgUBnwKz0Pj6H6UsbE3gdJqZWCDyw/bB
+-----END RSA PRIVATE KEY-----
diff --git a/util/libcrypto.num b/util/libcrypto.num
index 380a2d2916..9029688674 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -4928,3 +4928,10 @@ OSSL_HTTP_get_asn1 ? 3_0_0 EXIST::FUNCTION:SOCK
OSSL_HTTP_post_asn1 ? 3_0_0 EXIST::FUNCTION:SOCK
OSSL_HTTP_transfer ? 3_0_0 EXIST::FUNCTION:SOCK
OSSL_HTTP_proxy_connect ? 3_0_0 EXIST::FUNCTION:SOCK
+ERR_add_error_txt ? 3_0_0 EXIST::FUNCTION:
+ERR_add_error_mem_bio ? 3_0_0 EXIST::FUNCTION:
+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