Commit bf236e07af for openssl.org

commit bf236e07afbd235d609f057eb50c92d1d2ee5008
Author: Viktor Dukhovni <openssl-users@dukhovni.org>
Date:   Wed Dec 3 15:24:46 2025 +1100

    Clarify/fix encoder/decoder context docs and code

    In was premature to make OSSL_(EN|DE)CODER_CTX_[sg]et_finalized() be
    public interfaces.  Forunately, these have not yet appeared outside the
    "master" branch, so we can still retract them.

    Also, in the case of decoders, the implementation failed to take into
    account that the context was duplicated before it was returned to the
    user, and the duplicated copy failed to copy the "finalized" field.

    This commit also renames "finalized" to "frozen", because
    finalisation is a misleading term in this context, it suggests
    resource reclamation during garbage collection or deallocation,
    not marking a structure partly immutable.

    Reviewed-by: Tomas Mraz <tomas@openssl.org>
    Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
    Reviewed-by: Neil Horman <nhorman@openssl.org>
    Reviewed-by: Paul Dale <paul.dale@oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/29206)

diff --git a/CHANGES.md b/CHANGES.md
index 720409f5b8..562ee1112f 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -70,12 +70,6 @@ OpenSSL 4.0

    *Daniel Kubec*

- * Added `OSSL_[EN|DE]CODER_CTX_[set|get]_finalized()` functions.
-   `OSSL_[EN|DE]CODER_CTX_set_*()` and `OSSL_[EN|DE]CODER_CTX_add_*()`
-   functions return 0 if the context is already finalised.
-
-   *Igor Ustinov*
-
  * Reject CRLs with a Certificate Issuer extension in a certificate revocation
    entry unless the Indirect flag is set to TRUE in the IDP extension of the CRL.

diff --git a/crypto/encode_decode/decoder_lib.c b/crypto/encode_decode/decoder_lib.c
index af5847e517..339fd5d192 100644
--- a/crypto/encode_decode/decoder_lib.c
+++ b/crypto/encode_decode/decoder_lib.c
@@ -170,7 +170,7 @@ int OSSL_DECODER_CTX_set_selection(OSSL_DECODER_CTX *ctx, int selection)
         return 0;
     }

-    if (ctx->finalized != 0) {
+    if (ctx->frozen != 0) {
         ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
         return 0;
     }
@@ -191,7 +191,7 @@ int OSSL_DECODER_CTX_set_input_type(OSSL_DECODER_CTX *ctx,
         return 0;
     }

-    if (ctx->finalized != 0) {
+    if (ctx->frozen != 0) {
         ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
         return 0;
     }
@@ -212,7 +212,7 @@ int OSSL_DECODER_CTX_set_input_structure(OSSL_DECODER_CTX *ctx,
         return 0;
     }

-    if (ctx->finalized != 0) {
+    if (ctx->frozen != 0) {
         ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
         return 0;
     }
@@ -403,7 +403,7 @@ int OSSL_DECODER_CTX_add_decoder(OSSL_DECODER_CTX *ctx, OSSL_DECODER *decoder)
         return 0;
     }

-    if (ctx->finalized != 0) {
+    if (ctx->frozen != 0) {
         ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
         return 0;
     }
@@ -602,7 +602,7 @@ int OSSL_DECODER_CTX_add_extra(OSSL_DECODER_CTX *ctx,
         return 0;
     }

-    if (ctx->finalized != 0) {
+    if (ctx->frozen != 0) {
         ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
         return 0;
     }
@@ -710,7 +710,7 @@ int OSSL_DECODER_CTX_set_construct(OSSL_DECODER_CTX *ctx,
         return 0;
     }

-    if (ctx->finalized != 0) {
+    if (ctx->frozen != 0) {
         ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
         return 0;
     }
@@ -727,7 +727,7 @@ int OSSL_DECODER_CTX_set_construct_data(OSSL_DECODER_CTX *ctx,
         return 0;
     }

-    if (ctx->finalized != 0) {
+    if (ctx->frozen != 0) {
         ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
         return 0;
     }
@@ -744,7 +744,7 @@ int OSSL_DECODER_CTX_set_cleanup(OSSL_DECODER_CTX *ctx,
         return 0;
     }

-    if (ctx->finalized != 0) {
+    if (ctx->frozen != 0) {
         ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
         return 0;
     }
@@ -753,25 +753,6 @@ int OSSL_DECODER_CTX_set_cleanup(OSSL_DECODER_CTX *ctx,
     return 1;
 }

-int OSSL_DECODER_CTX_set_finalized(OSSL_DECODER_CTX *ctx)
-{
-    if (ctx == NULL) {
-        ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER);
-        return 0;
-    }
-
-    ctx->finalized = 1;
-    return 1;
-}
-
-int OSSL_DECODER_CTX_get_finalized(OSSL_DECODER_CTX *ctx)
-{
-    if (ctx == NULL)
-        return 0;
-
-    return ctx->finalized;
-}
-
 OSSL_DECODER_CONSTRUCT *
 OSSL_DECODER_CTX_get_construct(OSSL_DECODER_CTX *ctx)
 {
diff --git a/crypto/encode_decode/decoder_pkey.c b/crypto/encode_decode/decoder_pkey.c
index 4e795a640e..2e3a14ba81 100644
--- a/crypto/encode_decode/decoder_pkey.c
+++ b/crypto/encode_decode/decoder_pkey.c
@@ -646,6 +646,7 @@ ossl_decoder_ctx_for_pkey_dup(OSSL_DECODER_CTX *src,
         goto err;
     }

+    dest->frozen = src->frozen;
     return dest;
  err:
     decoder_clean_pkey_construct_arg(process_data_dest);
@@ -877,9 +878,9 @@ OSSL_DECODER_CTX_new_for_pkey(EVP_PKEY **pkey,
             && OSSL_DECODER_CTX_set_selection(ctx, selection)
             && ossl_decoder_ctx_setup_for_pkey(ctx, keytype, libctx, propquery)
             && OSSL_DECODER_CTX_add_extra(ctx, libctx, propquery)
-            && OSSL_DECODER_CTX_set_finalized(ctx)
             && (propquery == NULL
                 || OSSL_DECODER_CTX_set_params(ctx, decoder_params))) {
+            ctx->frozen = 1;
             OSSL_TRACE_BEGIN(DECODER) {
                 BIO_printf(trc_out, "(ctx %p) Got %d decoders\n",
                         (void *)ctx, OSSL_DECODER_CTX_get_num_decoders(ctx));
diff --git a/crypto/encode_decode/encoder_lib.c b/crypto/encode_decode/encoder_lib.c
index 7e182f319b..a27479c76c 100644
--- a/crypto/encode_decode/encoder_lib.c
+++ b/crypto/encode_decode/encoder_lib.c
@@ -175,7 +175,7 @@ int OSSL_ENCODER_CTX_set_selection(OSSL_ENCODER_CTX *ctx, int selection)
         return 0;
     }

-    if (ctx->finalized != 0) {
+    if (ctx->frozen != 0) {
         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
         return 0;
     }
@@ -197,7 +197,7 @@ int OSSL_ENCODER_CTX_set_output_type(OSSL_ENCODER_CTX *ctx,
         return 0;
     }

-    if (ctx->finalized != 0) {
+    if (ctx->frozen != 0) {
         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
         return 0;
     }
@@ -214,7 +214,7 @@ int OSSL_ENCODER_CTX_set_output_structure(OSSL_ENCODER_CTX *ctx,
         return 0;
     }

-    if (ctx->finalized != 0) {
+    if (ctx->frozen != 0) {
         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
         return 0;
     }
@@ -331,7 +331,7 @@ int OSSL_ENCODER_CTX_add_encoder(OSSL_ENCODER_CTX *ctx, OSSL_ENCODER *encoder)
         return 0;
     }

-    if (ctx->finalized != 0) {
+    if (ctx->frozen != 0) {
         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
         return 0;
     }
@@ -365,7 +365,7 @@ int OSSL_ENCODER_CTX_add_extra(OSSL_ENCODER_CTX *ctx,
         return 0;
     }

-    if (ctx->finalized != 0) {
+    if (ctx->frozen != 0) {
         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
         return 0;
     }
@@ -388,7 +388,7 @@ int OSSL_ENCODER_CTX_set_construct(OSSL_ENCODER_CTX *ctx,
         return 0;
     }

-    if (ctx->finalized != 0) {
+    if (ctx->frozen != 0) {
         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
         return 0;
     }
@@ -405,7 +405,7 @@ int OSSL_ENCODER_CTX_set_construct_data(OSSL_ENCODER_CTX *ctx,
         return 0;
     }

-    if (ctx->finalized != 0) {
+    if (ctx->frozen != 0) {
         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
         return 0;
     }
@@ -422,7 +422,7 @@ int OSSL_ENCODER_CTX_set_cleanup(OSSL_ENCODER_CTX *ctx,
         return 0;
     }

-    if (ctx->finalized != 0) {
+    if (ctx->frozen != 0) {
         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
         return 0;
     }
@@ -431,25 +431,6 @@ int OSSL_ENCODER_CTX_set_cleanup(OSSL_ENCODER_CTX *ctx,
     return 1;
 }

-int OSSL_ENCODER_CTX_set_finalized(OSSL_ENCODER_CTX *ctx)
-{
-    if (ctx == NULL) {
-        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
-        return 0;
-    }
-
-    ctx->finalized = 1;
-    return 1;
-}
-
-int OSSL_ENCODER_CTX_get_finalized(OSSL_ENCODER_CTX *ctx)
-{
-    if (ctx == NULL)
-        return 0;
-
-    return ctx->finalized;
-}
-
 OSSL_ENCODER *
 OSSL_ENCODER_INSTANCE_get_encoder(OSSL_ENCODER_INSTANCE *encoder_inst)
 {
diff --git a/crypto/encode_decode/encoder_local.h b/crypto/encode_decode/encoder_local.h
index e43259e864..39c786d001 100644
--- a/crypto/encode_decode/encoder_local.h
+++ b/crypto/encode_decode/encoder_local.h
@@ -103,7 +103,7 @@ struct ossl_encoder_ctx_st {
     struct ossl_passphrase_data_st pwdata;

     /* Flag that the structure is ready for use */
-    int finalized;
+    int frozen;
 };

 struct ossl_decoder_instance_st {
@@ -167,7 +167,7 @@ struct ossl_decoder_ctx_st {
     int harderr;

     /* Flag that the structure is ready for use */
-    int finalized;
+    int frozen;
 };

 const OSSL_PROPERTY_LIST *
diff --git a/crypto/encode_decode/encoder_pkey.c b/crypto/encode_decode/encoder_pkey.c
index 7fe7318a3e..4d749edeab 100644
--- a/crypto/encode_decode/encoder_pkey.c
+++ b/crypto/encode_decode/encoder_pkey.c
@@ -388,11 +388,11 @@ OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new_for_pkey(const EVP_PKEY *pkey,
             || OSSL_ENCODER_CTX_set_output_structure(ctx, output_struct))
         && OSSL_ENCODER_CTX_set_selection(ctx, selection)
         && ossl_encoder_ctx_setup_for_pkey(ctx, pkey, selection, propquery)
-        && OSSL_ENCODER_CTX_add_extra(ctx, libctx, propquery)
-        && OSSL_ENCODER_CTX_set_finalized(ctx)) {
+        && OSSL_ENCODER_CTX_add_extra(ctx, libctx, propquery)) {
         OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
         int save_parameters = pkey->save_parameters;

+        ctx->frozen = 1;
         params[0] = OSSL_PARAM_construct_int(OSSL_ENCODER_PARAM_SAVE_PARAMETERS,
                                              &save_parameters);
         /* ignoring error as this is only auxiliary parameter */
diff --git a/doc/man3/OSSL_DECODER_CTX.pod b/doc/man3/OSSL_DECODER_CTX.pod
index 0ccbd62c11..c150e7d895 100644
--- a/doc/man3/OSSL_DECODER_CTX.pod
+++ b/doc/man3/OSSL_DECODER_CTX.pod
@@ -19,11 +19,9 @@ OSSL_DECODER_CLEANUP,
 OSSL_DECODER_CTX_set_construct,
 OSSL_DECODER_CTX_set_construct_data,
 OSSL_DECODER_CTX_set_cleanup,
-OSSL_DECODER_CTX_set_finalized,
 OSSL_DECODER_CTX_get_construct,
 OSSL_DECODER_CTX_get_construct_data,
 OSSL_DECODER_CTX_get_cleanup,
-OSSL_DECODER_CTX_get_finalized,
 OSSL_DECODER_export,
 OSSL_DECODER_INSTANCE_get_decoder,
 OSSL_DECODER_INSTANCE_get_decoder_ctx,
@@ -79,9 +77,6 @@ OSSL_DECODER_INSTANCE_get_input_structure
  void *OSSL_DECODER_CTX_get_construct_data(OSSL_DECODER_CTX *ctx);
  OSSL_DECODER_CLEANUP *OSSL_DECODER_CTX_get_cleanup(OSSL_DECODER_CTX *ctx);

- int OSSL_DECODER_CTX_set_finalized(OSSL_DECODER_CTX *ctx);
- int OSSL_DECODER_CTX_get_finalized(OSSL_DECODER_CTX *ctx);
-
  int OSSL_DECODER_export(OSSL_DECODER_INSTANCE *decoder_inst,
                          void *reference, size_t reference_sz,
                          OSSL_CALLBACK *export_cb, void *export_cbarg);
@@ -167,15 +162,6 @@ OSSL_DECODER_CTX_get_cleanup() return the values that have been set by
 OSSL_DECODER_CTX_set_construct(), OSSL_DECODER_CTX_set_construct_data() and
 OSSL_DECODER_CTX_set_cleanup() respectively.

-OSSL_DECODER_CTX_set_finalized() finalises the context. Functions
-OSSL_DECODER_CTX_set_selection(), OSSL_DECODER_CTX_set_output_type(),
-OSSL_DECODER_CTX_set_output_structure(), OSSL_DECODER_CTX_add_encoder(),
-OSSL_DECODER_CTX_add_extra(), OSSL_DECODER_CTX_set_construct(),
-OSSL_DECODER_CTX_set_construct_data() and OSSL_DECODER_CTX_set_cleanup()
-can't be used after the context is finalised.
-
-OSSL_DECODER_CTX_get_finalized() indicates if the context was finalised.
-
 OSSL_DECODER_export() is a fallback function for constructors that cannot
 use the data they get directly for diverse reasons.  It takes the same
 decode instance I<decoder_inst> that the constructor got and an object
@@ -237,20 +223,24 @@ OSSL_DECODER_CTX_set_params() returns 1 if all recognised parameters were
 valid, or 0 if one of them was invalid or caused some other failure in the
 implementation.

-OSSL_DECODER_CTX_set_selection(), OSSL_DECODER_CTX_set_input_type(),
+OSSL_DECODER_CTX_add_decoder(),
+OSSL_DECODER_CTX_add_extra(),
+OSSL_DECODER_CTX_set_cleanup(),
+OSSL_DECODER_CTX_set_construct(),
+OSSL_DECODER_CTX_set_construct_data(),
 OSSL_DECODER_CTX_set_input_structure(),
-OSSL_DECODER_CTX_add_decoder(), OSSL_DECODER_CTX_add_extra(),
-OSSL_DECODER_CTX_set_construct(), OSSL_DECODER_CTX_set_construct_data(),
-OSSL_DECODER_CTX_set_cleanup() and OSSL_DECODER_CTX_set_finalized()
+OSSL_DECODER_CTX_set_input_type(),
+OSSL_DECODER_CTX_set_params(),
+and
+OSSL_DECODER_CTX_set_selection()
 return 1 on success, or 0 on failure.
+They always fail for decoder contexts created in a frozen state via
+L<OSSL_DECODER_CTX_new_for_pkey(3)>.

 OSSL_DECODER_CTX_get_construct(), OSSL_DECODER_CTX_get_construct_data() and
 OSSL_DECODER_CTX_get_cleanup() return the current pointers to the
 constructor, the constructor data and the cleanup functions, respectively.

-OSSL_DECODER_CTX_get_finalized() returns 1 if I<ctx> was finalised,
-0 otherwise. It also returns 0 if I<ctx> is NULL.
-
 OSSL_DECODER_CTX_num_decoders() returns the current number of decoders.  It
 returns 0 if I<ctx> is NULL.

@@ -270,9 +260,6 @@ L<provider(7)>, L<OSSL_DECODER(3)>, L<OSSL_DECODER_from_bio(3)>

 The functions described here were added in OpenSSL 3.0.

-OSSL_DECODER_CTX_set_finalized() and OSSL_DECODER_CTX_get_finalized()
-were added in OpenSSL 4.0.
-
 =head1 COPYRIGHT

 Copyright 2020-2024 The OpenSSL Project Authors. All Rights Reserved.
diff --git a/doc/man3/OSSL_DECODER_CTX_new_for_pkey.pod b/doc/man3/OSSL_DECODER_CTX_new_for_pkey.pod
index 2836d0e18a..89c63f835a 100644
--- a/doc/man3/OSSL_DECODER_CTX_new_for_pkey.pod
+++ b/doc/man3/OSSL_DECODER_CTX_new_for_pkey.pod
@@ -68,12 +68,26 @@ If no suitable decoder implementation is found,
 OSSL_DECODER_CTX_new_for_pkey() still creates a B<OSSL_DECODER_CTX>, but
 with no associated decoder (L<OSSL_DECODER_CTX_get_num_decoders(3)> returns
 zero).  This helps the caller to distinguish between an error when creating
-the B<OSSL_ENCODER_CTX> and missing encoder implementation, and allows it to
+the B<OSSL_DECODER_CTX> and missing decoder implementation, and allows it to
 act accordingly.

-Note that OSSL_DECODER_CTX_new_for_pkey() finalises the OSSL_DECODER_CTX;
-after that the B<OSSL_DECODER_CTX_set_*()> and B<OSSL_DECODER_CTX_add_*()>
-functions described in L<OSSL_DECODER_CTX(3)> shouldn't be called.
+and
+OSSL_DECODER_CTX_set_selection(),
+
+Note that OSSL_DECODER_CTX_new_for_pkey() freezes some fields of the
+OSSL_DECODER_CTX, and as a result the functions
+L<OSSL_DECODER_CTX_add_decoder(3)>,
+L<OSSL_DECODER_CTX_add_extra(3)>,
+L<OSSL_DECODER_CTX_set_cleanup(3)>,
+L<OSSL_DECODER_CTX_set_construct(3)>,
+L<OSSL_DECODER_CTX_set_construct_data(3)>,
+L<OSSL_DECODER_CTX_set_input_structure(3)>,
+L<OSSL_DECODER_CTX_set_input_type(3)>
+and
+L<OSSL_DECODER_CTX_set_selection(3)>
+return an error if called on the resulting context.
+Note that L<OSSL_DECODER_CTX_set_params(3)> remains available to set decoder
+parameters.

 OSSL_DECODER_CTX_set_passphrase() gives the implementation a pass phrase to
 use when decrypting the encoded private key. Alternatively, a pass phrase
@@ -131,7 +145,10 @@ failure.

 =head1 SEE ALSO

-L<provider(7)>, L<OSSL_DECODER(3)>, L<OSSL_DECODER_CTX(3)>
+L<provider(7)>,
+L<OSSL_DECODER(3)>,
+L<OSSL_DECODER_CTX(3)>,
+L<OSSL_DECODER_CTX_set_params(3)>.

 =head1 HISTORY

diff --git a/doc/man3/OSSL_ENCODER_CTX.pod b/doc/man3/OSSL_ENCODER_CTX.pod
index 6266f6ac3a..f06b9fd86f 100644
--- a/doc/man3/OSSL_ENCODER_CTX.pod
+++ b/doc/man3/OSSL_ENCODER_CTX.pod
@@ -22,9 +22,7 @@ OSSL_ENCODER_CONSTRUCT,
 OSSL_ENCODER_CLEANUP,
 OSSL_ENCODER_CTX_set_construct,
 OSSL_ENCODER_CTX_set_construct_data,
-OSSL_ENCODER_CTX_set_cleanup,
-OSSL_ENCODER_CTX_set_finalized,
-OSSL_ENCODER_CTX_get_finalized
+OSSL_ENCODER_CTX_set_cleanup
 - Encoder context routines

 =head1 SYNOPSIS
@@ -71,9 +69,6 @@ OSSL_ENCODER_CTX_get_finalized
  int OSSL_ENCODER_CTX_set_cleanup(OSSL_ENCODER_CTX *ctx,
                                   OSSL_ENCODER_CLEANUP *cleanup);

- int OSSL_ENCODER_CTX_set_finalized(OSSL_ENCODER_CTX *ctx);
- int OSSL_ENCODER_CTX_get_finalized(OSSL_ENCODER_CTX *ctx);
-
 =head1 DESCRIPTION

 Encoding an input object to the desired encoding may be done with a chain of
@@ -137,15 +132,6 @@ passed to the constructor every time it's called.
 OSSL_ENCODER_CTX_set_cleanup() sets the constructor data I<cleanup>
 function.  This is called by L<OSSL_ENCODER_CTX_free(3)>.

-OSSL_ENCODER_CTX_set_finalized() finalises the context. Functions
-OSSL_ENCODER_CTX_set_selection(),
-OSSL_ENCODER_CTX_set_output_type(), OSSL_ENCODER_CTX_set_output_structure(),
-OSSL_ENCODER_CTX_add_encoder(), OSSL_ENCODER_CTX_add_extra(),
-OSSL_ENCODER_CTX_set_construct(), OSSL_ENCODER_CTX_set_construct_data() and
-OSSL_ENCODER_CTX_set_cleanup() can't be used after the context is finalised.
-
-OSSL_ENCODER_CTX_get_finalized() indicates if the context was finalised.
-
 =head2 Constructor

 A B<OSSL_ENCODER_CONSTRUCT> gets the following arguments:
@@ -196,15 +182,18 @@ OSSL_ENCODER_CTX_set_params() returns 1 if all recognised parameters were
 valid, or 0 if one of them was invalid or caused some other failure in the
 implementation.

-OSSL_ENCODER_CTX_set_selection(),
-OSSL_ENCODER_CTX_set_output_type(), OSSL_ENCODER_CTX_set_output_structure(),
-OSSL_ENCODER_CTX_add_encoder(), OSSL_ENCODER_CTX_add_extra(),
-OSSL_ENCODER_CTX_set_construct(), OSSL_ENCODER_CTX_set_construct_data(),
-OSSL_ENCODER_CTX_set_cleanup() and OSSL_ENCODER_CTX_set_finalized()
+OSSL_ENCODER_CTX_add_encoder(),
+OSSL_ENCODER_CTX_add_extra(),
+OSSL_ENCODER_CTX_set_cleanup(),
+OSSL_ENCODER_CTX_set_construct(),
+OSSL_ENCODER_CTX_set_construct_data(),
+OSSL_ENCODER_CTX_set_output_structure(),
+OSSL_ENCODER_CTX_set_output_type()
+and
+OSSL_ENCODER_CTX_set_selection()
 return 1 on success, or 0 on failure.
-
-OSSL_ENCODER_CTX_get_finalized() returns 1 if I<ctx> was finalised,
-0 otherwise. It also returns 0 if I<ctx> is NULL.
+They always fail for encoder contexts created in a frozen state via
+L<OSSL_ENCODER_CTX_new_for_pkey(3)>.

 OSSL_ENCODER_CTX_get_num_encoders() returns the current number of encoders.
 It returns 0 if I<ctx> is NULL.
@@ -238,9 +227,6 @@ L<provider(7)>, L<OSSL_ENCODER(3)>

 The functions described here were added in OpenSSL 3.0.

-OSSL_ENCODER_CTX_set_finalized() and OSSL_ENCODER_CTX_get_finalized()
-were added in OpenSSL 4.0.
-
 =head1 COPYRIGHT

 Copyright 2019-2024 The OpenSSL Project Authors. All Rights Reserved.
diff --git a/doc/man3/OSSL_ENCODER_CTX_new_for_pkey.pod b/doc/man3/OSSL_ENCODER_CTX_new_for_pkey.pod
index 5fb7a03681..9948a6013c 100644
--- a/doc/man3/OSSL_ENCODER_CTX_new_for_pkey.pod
+++ b/doc/man3/OSSL_ENCODER_CTX_new_for_pkey.pod
@@ -60,9 +60,20 @@ zero).  This helps the caller to distinguish between an error when creating
 the B<OSSL_ENCODER_CTX> and missing encoder implementation, and allows it to
 act accordingly.

-Note that OSSL_ENCODER_CTX_new_for_pkey() finalises the OSSL_ENCODER_CTX;
-after that the B<OSSL_ENCODER_CTX_set_*()> and B<OSSL_ENCODER_CTX_add_*()>
-functions described in L<OSSL_ENCODER_CTX(3)> shouldn't be called.
+Note that OSSL_ENCODER_CTX_new_for_pkey() freezes some fields of the
+OSSL_ENCODER_CTX, and as a result the functions
+L<OSSL_ENCODER_CTX_add_encoder(3)>,
+L<OSSL_ENCODER_CTX_add_extra(3)>,
+L<OSSL_ENCODER_CTX_set_cleanup(3)>,
+L<OSSL_ENCODER_CTX_set_construct(3)>,
+L<OSSL_ENCODER_CTX_set_construct_data(3)>,
+L<OSSL_ENCODER_CTX_set_output_structure(3)>,
+L<OSSL_ENCODER_CTX_set_output_type(3)>
+and
+L<OSSL_ENCODER_CTX_set_selection(3)>
+return an error if called on the resulting context.
+Note that L<OSSL_ENCODER_CTX_set_params(3)> remains available to set encoder
+parameters.

 OSSL_ENCODER_CTX_set_cipher() tells the implementation what cipher
 should be used to encrypt encoded keys.  The cipher is given by
@@ -132,7 +143,10 @@ failure.

 =head1 SEE ALSO

-L<provider(7)>, L<OSSL_ENCODER(3)>, L<OSSL_ENCODER_CTX(3)>
+L<provider(7)>,
+L<OSSL_ENCODER(3)>,
+L<OSSL_ENCODER_CTX(3)>,
+L<OSSL_ENCODER_CTX_set_params(3)>.

 =head1 HISTORY

diff --git a/include/openssl/decoder.h b/include/openssl/decoder.h
index 632a399d97..d4ee2cf413 100644
--- a/include/openssl/decoder.h
+++ b/include/openssl/decoder.h
@@ -63,8 +63,6 @@ int OSSL_DECODER_CTX_set_passphrase_cb(OSSL_DECODER_CTX *ctx,
 int OSSL_DECODER_CTX_set_passphrase_ui(OSSL_DECODER_CTX *ctx,
                                        const UI_METHOD *ui_method,
                                        void *ui_data);
-int OSSL_DECODER_CTX_set_finalized(OSSL_DECODER_CTX *ctx);
-int OSSL_DECODER_CTX_get_finalized(OSSL_DECODER_CTX *ctx);

 /*
  * Utilities to read the object to decode, with the result sent to cb.
diff --git a/include/openssl/encoder.h b/include/openssl/encoder.h
index 5ffed5ceb2..c37a6f16f2 100644
--- a/include/openssl/encoder.h
+++ b/include/openssl/encoder.h
@@ -71,8 +71,6 @@ int OSSL_ENCODER_CTX_set_output_type(OSSL_ENCODER_CTX *ctx,
                                      const char *output_type);
 int OSSL_ENCODER_CTX_set_output_structure(OSSL_ENCODER_CTX *ctx,
                                           const char *output_structure);
-int OSSL_ENCODER_CTX_set_finalized(OSSL_ENCODER_CTX *ctx);
-int OSSL_ENCODER_CTX_get_finalized(OSSL_ENCODER_CTX *ctx);

 /* Utilities to add encoders */
 int OSSL_ENCODER_CTX_add_encoder(OSSL_ENCODER_CTX *ctx, OSSL_ENCODER *encoder);
diff --git a/test/endecode_test.c b/test/endecode_test.c
index 37fc950e46..76b9348ec1 100644
--- a/test/endecode_test.c
+++ b/test/endecode_test.c
@@ -1370,56 +1370,6 @@ static int ec_encode_to_data_multi(void)
 }
 #endif /* OPENSSL_NO_EC */

-/*
- * Test that OSSL_ENCODER_CTX setters return 0 after finalising the context
- */
-static int encoder_ctx_setters(void)
-{
-    int ret;
-    OSSL_ENCODER_CTX *ectx = NULL;
-
-    ret = TEST_ptr(ectx = OSSL_ENCODER_CTX_new())
-        && TEST_int_eq(OSSL_ENCODER_CTX_get_finalized(ectx), 0)
-        && TEST_int_eq(OSSL_ENCODER_CTX_set_selection(ectx,
-                                                      EVP_PKEY_KEYPAIR), 1)
-        && TEST_int_eq(OSSL_ENCODER_CTX_set_output_type(ectx, "DER"), 1)
-        && TEST_int_eq(OSSL_ENCODER_CTX_set_output_structure(ectx, "PKCS8"), 1)
-        && TEST_int_eq(OSSL_ENCODER_CTX_set_finalized(ectx), 1)
-        && TEST_int_eq(OSSL_ENCODER_CTX_set_selection(ectx,
-                                                      EVP_PKEY_PUBLIC_KEY), 0)
-        && TEST_int_eq(OSSL_ENCODER_CTX_set_output_type(ectx, "PEM"), 0)
-        && TEST_int_eq(OSSL_ENCODER_CTX_set_output_structure(ectx, "PKCS8"), 0)
-        && TEST_int_eq(OSSL_ENCODER_CTX_add_extra(ectx, NULL, NULL), 0)
-        && TEST_int_eq(OSSL_ENCODER_CTX_get_finalized(ectx), 1);
-    OSSL_ENCODER_CTX_free(ectx);
-    return ret;
-}
-
-/*
- * Test that OSSL_DECODER_CTX setters return 0 after finalising the context
- */
-static int decoder_ctx_setters(void)
-{
-    int ret;
-    OSSL_DECODER_CTX *dctx = NULL;
-
-    ret = TEST_ptr(dctx = OSSL_DECODER_CTX_new())
-        && TEST_int_eq(OSSL_DECODER_CTX_get_finalized(dctx), 0)
-        && TEST_int_eq(OSSL_DECODER_CTX_set_selection(dctx,
-                                                      EVP_PKEY_KEYPAIR), 1)
-        && TEST_int_eq(OSSL_DECODER_CTX_set_input_type(dctx, "DER"), 1)
-        && TEST_int_eq(OSSL_DECODER_CTX_set_input_structure(dctx, "PKCS8"), 1)
-        && TEST_int_eq(OSSL_DECODER_CTX_set_finalized(dctx), 1)
-        && TEST_int_eq(OSSL_DECODER_CTX_set_selection(dctx,
-                                                      EVP_PKEY_PUBLIC_KEY), 0)
-        && TEST_int_eq(OSSL_DECODER_CTX_set_input_type(dctx, "PEM"), 0)
-        && TEST_int_eq(OSSL_DECODER_CTX_set_input_structure(dctx, "PKCS8"), 0)
-        && TEST_int_eq(OSSL_DECODER_CTX_add_extra(dctx, NULL, NULL), 0)
-        && TEST_int_eq(OSSL_DECODER_CTX_get_finalized(dctx), 1);
-    OSSL_DECODER_CTX_free(dctx);
-    return ret;
-}
-
 typedef enum OPTION_choice {
     OPT_ERR = -1,
     OPT_EOF = 0,
@@ -1718,9 +1668,6 @@ int setup_tests(void)
             ADD_TEST_SUITE(SLH_DSA_SHAKE_256f);
         }
 #endif /* OPENSSL_NO_SLH_DSA */
-
-        ADD_TEST(encoder_ctx_setters);
-        ADD_TEST(decoder_ctx_setters);
     }

     return 1;
diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c
index 9ec90fc308..6ad977313b 100644
--- a/test/evp_extra_test.c
+++ b/test/evp_extra_test.c
@@ -972,8 +972,16 @@ static EVP_PKEY *load_ml_key(const char *keytype, const char *input_type,

     if (!TEST_ptr(dctx))
         return NULL;
-    /* |pkey| will be NULL on error */
-    (void)OSSL_DECODER_from_data(dctx, pdata, &data_len);
+
+    /* Decode, but first check that the context is frozen as expected */
+    if (TEST_false(OSSL_DECODER_CTX_set_selection(dctx, EVP_PKEY_KEYPAIR))
+        && TEST_false(OSSL_DECODER_CTX_set_input_type(dctx, "DER"))
+        && TEST_false(OSSL_DECODER_CTX_set_input_structure(dctx, "PrivateKeyInfo"))
+        && TEST_false(OSSL_DECODER_CTX_add_extra(dctx, NULL, NULL))) {
+        /* |pkey| will be NULL on error */
+        (void)OSSL_DECODER_from_data(dctx, pdata, &data_len);
+    }
+
     OSSL_DECODER_CTX_free(dctx);
     return pkey;
 }
@@ -997,12 +1005,18 @@ store_ml_key(EVP_PKEY *pkey, const char *input_type, const char *fmts,
                                          "PrivateKeyInfo", testpropq);
     if (!TEST_ptr(ectx))
         return 0;
-    if (!TEST_true(OSSL_ENCODER_CTX_set_params(ectx, params))
+
+    /* Encode, but first check that the context is frozen as expected */
+    if (!TEST_false(OSSL_ENCODER_CTX_set_selection(ectx, EVP_PKEY_PUBLIC_KEY))
+        || !TEST_false(OSSL_ENCODER_CTX_set_output_type(ectx, "PEM"))
+        || !TEST_false(OSSL_ENCODER_CTX_set_output_structure(ectx, "PKCS8"))
+        || !TEST_false(OSSL_ENCODER_CTX_add_extra(ectx, NULL, NULL))
+        || !TEST_true(OSSL_ENCODER_CTX_set_params(ectx, params))
         || !TEST_true(OSSL_ENCODER_to_data(ectx, &buf, &len)))
         goto end;

     if (strcmp(input_type, "PEM") == 0) {
-        BIO *pembio = BIO_new_mem_buf(buf, len);
+        BIO *pembio = BIO_new_mem_buf(buf, (int) len);
         char *name = NULL, *header = NULL;

         if (!TEST_ptr(pembio))
@@ -1020,7 +1034,7 @@ store_ml_key(EVP_PKEY *pkey, const char *input_type, const char *fmts,
         }
     } else {
         der = buf;
-        derlen = len;
+        derlen = (long) len;
     }
     ret = expect != NULL ?
         TEST_mem_eq(der, (size_t) derlen, expect, expectlen) :
diff --git a/util/libcrypto.num b/util/libcrypto.num
index cbda7e80d8..ee9be3c00e 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -5812,7 +5812,3 @@ OSSL_PARAM_clear_free                   ?	4_0_0	EXIST::FUNCTION:
 CMS_dataFinal_ex                        ?	4_0_0	EXIST::FUNCTION:CMS
 CMS_SignerInfo_verify_ex                ?	4_0_0	EXIST::FUNCTION:CMS
 EVP_SIGNATURE_has_message_update        ?	4_0_0	EXIST::FUNCTION:
-OSSL_DECODER_CTX_set_finalized          ?	4_0_0	EXIST::FUNCTION:
-OSSL_DECODER_CTX_get_finalized          ?	4_0_0	EXIST::FUNCTION:
-OSSL_ENCODER_CTX_set_finalized          ?	4_0_0	EXIST::FUNCTION:
-OSSL_ENCODER_CTX_get_finalized          ?	4_0_0	EXIST::FUNCTION: