Repository: randombit/botan
Pull request #3753 (2023-10-11 22:01:03)

Use BufferStuffer in pk_pad
I started working further on #3752 and things quickly went out of control. I split this out since it's basically an independent chunk of work.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/63236717/badge)](https://coveralls.io/builds/63236717)

coverage: 91.693% (-0.001%) from 91.694% when pulling **addc7627780273f36d683002e7a30cbe6963f138 on jack/bufferstuffer-pk-pad** into **520822ed860975ae165f40e41bf55bb4697dbbe8 on master**.

randombit: > With all the buffer_insert() invocations going the way of the Dodo, we are getting really close to removing this helper function entirely.

Yeah that was what motivated this - if we can just rewrite code that is currently using `mem_ops.h` functions to use something else (especially something else we already have), we don't have to extend those functions with span, we can just deprecate them and move on.

I think the `BufferStuffer` approach also leads to much more readable code.
randombit: Alas we cannot use `std::ranges::fill` because apparently something is wrong with the libc++ in CiFuzz (https://github.com/google/oss-fuzz/issues/11116) and it's missing there :sob:
randombit: Eh I guess `std::fill` works fine in this case

Patch:
diff --git a/configure.py b/configure.py
index cc7c2a78a..950182f35 100755
--- a/configure.py
+++ b/configure.py
@@ -513,7 +513,7 @@ def process_command_line(args):
     build_group.add_option('--with-debug-asserts', action='store_true', default=False,
                            help=optparse.SUPPRESS_HELP)
 
-    build_group.add_option('--unsafe-terminate-on-asserts', action='store_true', default=False,
+    build_group.add_option('--terminate-on-asserts', action='store_true', default=False,
                            help=optparse.SUPPRESS_HELP)
 
     build_group.add_option('--build-targets', default=None, dest="build_targets", action='append',
@@ -2257,7 +2257,7 @@ def create_template_vars(source_paths, build_paths, options, modules, cc, arch,
 
         'with_valgrind': options.with_valgrind,
         'with_debug_asserts': options.with_debug_asserts,
-        'terminate_on_asserts': options.unsafe_terminate_on_asserts,
+        'terminate_on_asserts': options.terminate_on_asserts,
         'optimize_for_size': options.optimize_for_size,
 
         'mod_list': sorted([m.basename for m in modules]),
@@ -3435,12 +3435,8 @@ botan
                  Version.release_type(),
                  date)
 
-    # Warn about build modes that are not safe for production:
     if options.unsafe_fuzzer_mode:
-        logging.warning("Unsafe fuzzer mode is NOT SAFE FOR PRODUCTION")
-
-    if options.unsafe_terminate_on_asserts:
-        logging.warning("Terminating on assertion failures is NOT SAFE FOR PRODUCTION")
+        logging.warning("The fuzzer mode flag is labeled unsafe for a reason, this version is for testing only")
 
 def list_os_features(all_os_features, info_os):
     for feat in all_os_features:
diff --git a/src/lib/pk_pad/eme_oaep/oaep.cpp b/src/lib/pk_pad/eme_oaep/oaep.cpp
index aabb9e190..13bfbaaf9 100644
--- a/src/lib/pk_pad/eme_oaep/oaep.cpp
+++ b/src/lib/pk_pad/eme_oaep/oaep.cpp
@@ -11,6 +11,7 @@
 #include <botan/rng.h>
 #include <botan/internal/ct_utils.h>
 #include <botan/internal/mgf1.h>
+#include <botan/internal/stl_util.h>
 
 namespace Botan {
 
@@ -28,12 +29,15 @@ secure_vector<uint8_t> OAEP::pad(const uint8_t in[],
    }
 
    secure_vector<uint8_t> out(key_length);
-
-   rng.randomize(out.data(), m_Phash.size());
-
-   buffer_insert(out, m_Phash.size(), m_Phash.data(), m_Phash.size());
-   out[out.size() - in_length - 1] = 0x01;
-   buffer_insert(out, out.size() - in_length, in, in_length);
+   BufferStuffer stuffer(out);
+
+   // We always use a seed len equal to the underlying hash
+   rng.randomize(stuffer.next(m_Phash.size()));
+   stuffer.append(m_Phash);
+   stuffer.append(0x00, stuffer.remaining_capacity() - (1 + in_length));
+   stuffer.append(0x01);
+   stuffer.append({in, in_length});
+   BOTAN_ASSERT_NOMSG(stuffer.full());
 
    mgf1_mask(*m_mgf1_hash, out.data(), m_Phash.size(), &out[m_Phash.size()], out.size() - m_Phash.size());
 
diff --git a/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp b/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp
index 84dc32d8a..d58b2750e 100644
--- a/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp
+++ b/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp
@@ -10,6 +10,7 @@
 #include <botan/exceptn.h>
 #include <botan/rng.h>
 #include <botan/internal/ct_utils.h>
+#include <botan/internal/stl_util.h>
 
 namespace Botan {
 
@@ -27,17 +28,17 @@ secure_vector<uint8_t> EME_PKCS1v15::pad(const uint8_t in[],
    }
 
    secure_vector<uint8_t> out(key_length);
+   BufferStuffer stuffer(out);
 
-   out[0] = 0x02;
-   rng.randomize(out.data() + 1, (key_length - inlen - 2));
+   const size_t padding_bytes = key_length - inlen - 2;
 
-   for(size_t j = 1; j != key_length - inlen - 1; ++j) {
-      if(out[j] == 0) {
-         out[j] = rng.next_nonzero_byte();
-      }
+   stuffer.append(0x02);
+   for(size_t i = 0; i != padding_bytes; ++i) {
+      stuffer.append(rng.next_nonzero_byte());
    }
-
-   buffer_insert(out, key_length - inlen, in, inlen);
+   stuffer.append(0x00);
+   stuffer.append({in, inlen});
+   BOTAN_ASSERT_NOMSG(stuffer.full());
 
    return out;
 }
diff --git a/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp b/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp
index 7a3691331..cfc45ca36 100644
--- a/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp
+++ b/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp
@@ -9,34 +9,32 @@
 
 #include <botan/exceptn.h>
 #include <botan/internal/hash_id.h>
+#include <botan/internal/stl_util.h>
 
 namespace Botan {
 
 namespace {
 
-std::vector<uint8_t> emsa3_encoding(const std::vector<uint8_t>& msg,
-                                    size_t output_bits,
-                                    const uint8_t hash_id[],
-                                    size_t hash_id_length) {
-   size_t output_length = output_bits / 8;
-   if(output_length < hash_id_length + msg.size() + 10) {
-      throw Encoding_Error("emsa3_encoding: Output length is too small");
-   }
+std::vector<uint8_t> pkcs1v15_sig_encoding(const std::vector<uint8_t>& msg,
+                                           size_t output_bits,
+                                           std::span<const uint8_t> hash_id) {
+   const size_t output_length = output_bits / 8;
 
-   std::vector<uint8_t> T(output_length);
-   const size_t P_LENGTH = output_length - msg.size() - hash_id_length - 2;
+   if(output_length < hash_id.size() + msg.size() + 2 + 8) {
+      throw Encoding_Error("pkcs1v15_sig_encoding: Output length is too small");
+   }
 
-   T[0] = 0x01;
-   set_mem(&T[1], P_LENGTH, 0xFF);
-   T[P_LENGTH + 1] = 0x00;
+   std::vector<uint8_t> padded(output_length);
+   BufferStuffer stuffer(padded);
 
-   if(hash_id_length > 0) {
-      BOTAN_ASSERT_NONNULL(hash_id);
-      buffer_insert(T, P_LENGTH + 2, hash_id, hash_id_length);
-   }
+   stuffer.append(0x01);
+   stuffer.append(0xFF, stuffer.remaining_capacity() - (1 + hash_id.size() + msg.size()));
+   stuffer.append(0x00);
+   stuffer.append(hash_id);
+   stuffer.append(msg);
+   BOTAN_ASSERT_NOMSG(stuffer.full());
 
-   buffer_insert(T, output_length - msg.size(), msg.data(), msg.size());
-   return T;
+   return padded;
 }
 
 }  // namespace
@@ -56,7 +54,7 @@ std::vector<uint8_t> EMSA_PKCS1v15::encoding_of(const std::vector<uint8_t>& msg,
       throw Encoding_Error("EMSA_PKCS1v15::encoding_of: Bad input length");
    }
 
-   return emsa3_encoding(msg, output_bits, m_hash_id.data(), m_hash_id.size());
+   return pkcs1v15_sig_encoding(msg, output_bits, m_hash_id);
 }
 
 bool EMSA_PKCS1v15::verify(const std::vector<uint8_t>& coded, const std::vector<uint8_t>& raw, size_t key_bits) {
@@ -65,7 +63,7 @@ bool EMSA_PKCS1v15::verify(const std::vector<uint8_t>& coded, const std::vector<
    }
 
    try {
-      return (coded == emsa3_encoding(raw, key_bits, m_hash_id.data(), m_hash_id.size()));
+      return coded == pkcs1v15_sig_encoding(raw, key_bits, m_hash_id);
    } catch(...) {
       return false;
    }
@@ -105,7 +103,7 @@ std::vector<uint8_t> EMSA_PKCS1v15_Raw::raw_data() {
 std::vector<uint8_t> EMSA_PKCS1v15_Raw::encoding_of(const std::vector<uint8_t>& msg,
                                                     size_t output_bits,
                                                     RandomNumberGenerator& /*rng*/) {
-   return emsa3_encoding(msg, output_bits, m_hash_id.data(), m_hash_id.size());
+   return pkcs1v15_sig_encoding(msg, output_bits, m_hash_id);
 }
 
 bool EMSA_PKCS1v15_Raw::verify(const std::vector<uint8_t>& coded, const std::vector<uint8_t>& raw, size_t key_bits) {
@@ -114,7 +112,7 @@ bool EMSA_PKCS1v15_Raw::verify(const std::vector<uint8_t>& coded, const std::vec
    }
 
    try {
-      return (coded == emsa3_encoding(raw, key_bits, m_hash_id.data(), m_hash_id.size()));
+      return coded == pkcs1v15_sig_encoding(raw, key_bits, m_hash_id);
    } catch(...) {
       return false;
    }
diff --git a/src/lib/pk_pad/emsa_pssr/pssr.cpp b/src/lib/pk_pad/emsa_pssr/pssr.cpp
index 45f341518..f3f618784 100644
--- a/src/lib/pk_pad/emsa_pssr/pssr.cpp
+++ b/src/lib/pk_pad/emsa_pssr/pssr.cpp
@@ -8,9 +8,12 @@
 #include <botan/internal/pssr.h>
 
 #include <botan/exceptn.h>
+#include <botan/mem_ops.h>
 #include <botan/rng.h>
 #include <botan/internal/bit_ops.h>
 #include <botan/internal/mgf1.h>
+#include <botan/internal/stl_util.h>
+#include <array>
 
 namespace Botan {
 
@@ -24,32 +27,38 @@ std::vector<uint8_t> pss_encode(HashFunction& hash,
                                 const std::vector<uint8_t>& salt,
                                 size_t output_bits) {
    const size_t HASH_SIZE = hash.output_length();
-   const size_t SALT_SIZE = salt.size();
 
    if(msg.size() != HASH_SIZE) {
       throw Encoding_Error("Cannot encode PSS string, input length invalid for hash");
    }
-   if(output_bits < 8 * HASH_SIZE + 8 * SALT_SIZE + 9) {
+   if(output_bits < 8 * HASH_SIZE + 8 * salt.size() + 9) {
       throw Encoding_Error("Cannot encode PSS string, output length too small");
    }
 
-   const size_t output_length = (output_bits + 7) / 8;
+   const size_t output_length = ceil_tobytes(output_bits);
+   const uint8_t db0_mask = 0xFF >> (8 * output_length - output_bits);
 
-   for(size_t i = 0; i != 8; ++i) {
-      hash.update(0);
-   }
+   std::array<uint8_t, 8> padding = {0};
+   hash.update(padding);
    hash.update(msg);
    hash.update(salt);
    std::vector<uint8_t> H = hash.final_stdvec();
 
+   const size_t db_len = output_length - HASH_SIZE - 1;
    std::vector<uint8_t> EM(output_length);
 
-   EM[output_length - HASH_SIZE - SALT_SIZE - 2] = 0x01;
-   buffer_insert(EM, output_length - 1 - HASH_SIZE - SALT_SIZE, salt);
-   mgf1_mask(hash, H.data(), HASH_SIZE, EM.data(), output_length - HASH_SIZE - 1);
-   EM[0] &= 0xFF >> (8 * ((output_bits + 7) / 8) - output_bits);
-   buffer_insert(EM, output_length - 1 - HASH_SIZE, H);
-   EM[output_length - 1] = 0xBC;
+   BufferStuffer stuffer(EM);
+   stuffer.append(0x00, stuffer.remaining_capacity() - (1 + salt.size() + H.size() + 1));
+   stuffer.append(0x01);
+   stuffer.append(salt);
+
+   mgf1_mask(hash, H.data(), H.size(), EM.data(), db_len);
+   EM[0] &= db0_mask;
+
+   stuffer.append(H);
+   stuffer.append(0xBC);
+   BOTAN_ASSERT_NOMSG(stuffer.full());
+
    return EM;
 }
 
@@ -59,7 +68,7 @@ bool pss_verify(HashFunction& hash,
                 size_t key_bits,
                 size_t* out_salt_size) {
    const size_t HASH_SIZE = hash.output_length();
-   const size_t KEY_BYTES = (key_bits + 7) / 8;
+   const size_t key_bytes = ceil_tobytes(key_bits);
 
    if(key_bits < 8 * HASH_SIZE + 9) {
       return false;
@@ -69,7 +78,7 @@ bool pss_verify(HashFunction& hash,
       return false;
    }
 
-   if(pss_repr.size() > KEY_BYTES || pss_repr.size() <= 1) {
+   if(pss_repr.size() > key_bytes || pss_repr.size() <= 1) {
       return false;
    }
 
@@ -78,9 +87,11 @@ bool pss_verify(HashFunction& hash,
    }
 
    std::vector<uint8_t> coded = pss_repr;
-   if(coded.size() < KEY_BYTES) {
-      std::vector<uint8_t> temp(KEY_BYTES);
-      buffer_insert(temp, KEY_BYTES - coded.size(), coded);
+   if(coded.size() < key_bytes) {
+      std::vector<uint8_t> temp(key_bytes);
+      BufferStuffer stuffer(temp);
+      stuffer.append(0x00, stuffer.remaining_capacity() - coded.size());
+      stuffer.append(coded);
       coded = temp;
    }
 
@@ -114,9 +125,8 @@ bool pss_verify(HashFunction& hash,
 
    const size_t salt_size = DB_size - salt_offset;
 
-   for(size_t j = 0; j != 8; ++j) {
-      hash.update(0);
-   }
+   std::array<uint8_t, 8> padding = {0};
+   hash.update(padding);
    hash.update(message_hash);
    hash.update(&DB[salt_offset], salt_size);
 
diff --git a/src/lib/pk_pad/emsa_x931/emsa_x931.cpp b/src/lib/pk_pad/emsa_x931/emsa_x931.cpp
index 148026bc6..5c78b7d10 100644
--- a/src/lib/pk_pad/emsa_x931/emsa_x931.cpp
+++ b/src/lib/pk_pad/emsa_x931/emsa_x931.cpp
@@ -9,6 +9,7 @@
 
 #include <botan/exceptn.h>
 #include <botan/internal/hash_id.h>
+#include <botan/internal/stl_util.h>
 
 namespace Botan {
 
@@ -20,7 +21,7 @@ std::vector<uint8_t> emsa2_encoding(const std::vector<uint8_t>& msg,
                                     uint8_t hash_id) {
    const size_t HASH_SIZE = empty_hash.size();
 
-   size_t output_length = (output_bits + 1) / 8;
+   const size_t output_length = (output_bits + 1) / 8;
 
    if(msg.size() != HASH_SIZE) {
       throw Encoding_Error("EMSA_X931::encoding_of: Bad input length");
@@ -32,13 +33,15 @@ std::vector<uint8_t> emsa2_encoding(const std::vector<uint8_t>& msg,
    const bool empty_input = (msg == empty_hash);
 
    std::vector<uint8_t> output(output_length);
-
-   output[0] = (empty_input ? 0x4B : 0x6B);
-   output[output_length - 3 - HASH_SIZE] = 0xBA;
-   set_mem(&output[1], output_length - 4 - HASH_SIZE, 0xBB);
-   buffer_insert(output, output_length - (HASH_SIZE + 2), msg.data(), msg.size());
-   output[output_length - 2] = hash_id;
-   output[output_length - 1] = 0xCC;
+   BufferStuffer stuffer(output);
+
+   stuffer.append(empty_input ? 0x4B : 0x6B);
+   stuffer.append(0xBB, stuffer.remaining_capacity() - (1 + msg.size() + 2));
+   stuffer.append(0xBA);
+   stuffer.append(msg);
+   stuffer.append(hash_id);
+   stuffer.append(0xCC);
+   BOTAN_ASSERT_NOMSG(stuffer.full());
 
    return output;
 }
diff --git a/src/lib/pk_pad/iso9796/iso9796.cpp b/src/lib/pk_pad/iso9796/iso9796.cpp
index 9182d1c5d..5964a89e1 100644
--- a/src/lib/pk_pad/iso9796/iso9796.cpp
+++ b/src/lib/pk_pad/iso9796/iso9796.cpp
@@ -13,6 +13,7 @@
 #include <botan/internal/ct_utils.h>
 #include <botan/internal/hash_id.h>
 #include <botan/internal/mgf1.h>
+#include <botan/internal/stl_util.h>
 
 namespace Botan {
 
@@ -27,10 +28,8 @@ std::vector<uint8_t> iso9796_encoding(const std::vector<uint8_t>& msg,
    const size_t output_length = (output_bits + 7) / 8;
 
    //set trailer length
-   size_t tLength = 1;
-   if(!implicit) {
-      tLength = 2;
-   }
+   const size_t tLength = (implicit) ? 1 : 2;
+
    const size_t HASH_SIZE = hash->output_length();
 
    if(output_length <= HASH_SIZE + SALT_SIZE + tLength) {
@@ -40,54 +39,55 @@ std::vector<uint8_t> iso9796_encoding(const std::vector<uint8_t>& msg,
    //calculate message capacity
    const size_t capacity = output_length - HASH_SIZE - SALT_SIZE - tLength - 1;
 
-   //msg1 is the recoverable and msg2 the unrecoverable message part.
+   //msg1 is the recoverable and hmsg2 is the hash of the unrecoverable message part.
    std::vector<uint8_t> msg1;
-   std::vector<uint8_t> msg2;
    if(msg.size() > capacity) {
       msg1 = std::vector<uint8_t>(msg.begin(), msg.begin() + capacity);
-      msg2 = std::vector<uint8_t>(msg.begin() + capacity, msg.end());
-      hash->update(msg2);
+      hash->update(std::span(msg).subspan(capacity));
    } else {
       msg1 = msg;
    }
-   msg2 = hash->final_stdvec();
+   const std::vector<uint8_t> hmsg2 = hash->final_stdvec();
 
    //compute H(C||msg1 ||H(msg2)||S)
    const size_t msgLength = msg1.size();
    const auto salt = rng.random_vec<std::vector<uint8_t>>(SALT_SIZE);
    hash->update_be(static_cast<uint64_t>(msgLength) * 8);
    hash->update(msg1);
-   hash->update(msg2);
+   hash->update(hmsg2);
    hash->update(salt);
    const std::vector<uint8_t> H = hash->final_stdvec();
 
    std::vector<uint8_t> EM(output_length);
 
-   //compute message offset.
-   const size_t offset = output_length - HASH_SIZE - SALT_SIZE - tLength - msgLength - 1;
-
-   //insert message border (0x01), msg1 and salt into the output buffer
-   EM[offset] = 0x01;
-   buffer_insert(EM, offset + 1, msg1);
-   buffer_insert(EM, offset + 1 + msgLength, salt);
+   BufferStuffer stuffer(EM);
+   stuffer.append(0x00, stuffer.remaining_capacity() - (HASH_SIZE + SALT_SIZE + tLength + msgLength + 1));
+   stuffer.append(0x01);
+   stuffer.append(msg1);
+   stuffer.append(salt);
 
    //apply mask
    mgf1_mask(*hash, H.data(), HASH_SIZE, EM.data(), output_length - HASH_SIZE - tLength);
-   buffer_insert(EM, output_length - HASH_SIZE - tLength, H);
-   //set implicit/ISO trailer
-   if(!implicit) {
-      uint8_t hash_id = ieee1363_hash_id(hash->name());
+
+   //clear the leftmost bit (confer bouncy castle)
+   EM[0] &= 0x7F;
+
+   stuffer.append(H);
+
+   // set implicit/ISO trailer
+
+   if(implicit) {
+      stuffer.append(0xBC);
+   } else {
+      const uint8_t hash_id = ieee1363_hash_id(hash->name());
       if(!hash_id) {
          throw Encoding_Error("ISO9796-2::encoding_of: no hash identifier for " + hash->name());
       }
-      EM[output_length - 1] = 0xCC;
-      EM[output_length - 2] = hash_id;
-
-   } else {
-      EM[output_length - 1] = 0xBC;
+      stuffer.append(hash_id);
+      stuffer.append(0xCC);
    }
-   //clear the leftmost bit (confer bouncy castle)
-   EM[0] &= 0x7F;
+
+   BOTAN_ASSERT_NOMSG(stuffer.full());
 
    return EM;
 }
@@ -163,20 +163,18 @@ bool iso9796_verification(const std::vector<uint8_t>& const_coded,
    //compute H2(C||msg1||H(msg2)||S*). * indicates a recovered value
    const size_t capacity = (key_bits - 2 + 7) / 8 - HASH_SIZE - SALT_SIZE - tLength - 1;
    std::vector<uint8_t> msg1raw;
-   std::vector<uint8_t> msg2;
    if(raw.size() > capacity) {
       msg1raw = std::vector<uint8_t>(raw.begin(), raw.begin() + capacity);
-      msg2 = std::vector<uint8_t>(raw.begin() + capacity, raw.end());
-      hash->update(msg2);
+      hash->update(std::span(raw).subspan(capacity));
    } else {
       msg1raw = raw;
    }
-   msg2 = hash->final_stdvec();
+   const std::vector<uint8_t> hmsg2 = hash->final_stdvec();
 
    const uint64_t msg1rawLength = msg1raw.size();
    hash->update_be(msg1rawLength * 8);
    hash->update(msg1raw);
-   hash->update(msg2);
+   hash->update(hmsg2);
    hash->update(salt);
    std::vector<uint8_t> H3 = hash->final_stdvec();
 
@@ -184,7 +182,7 @@ bool iso9796_verification(const std::vector<uint8_t>& const_coded,
    const uint64_t msgLength = msg1.size();
    hash->update_be(msgLength * 8);
    hash->update(msg1);
-   hash->update(msg2);
+   hash->update(hmsg2);
    hash->update(salt);
    std::vector<uint8_t> H2 = hash->final_stdvec();
 
diff --git a/src/lib/tls/tls13/msg_certificate_13.cpp b/src/lib/tls/tls13/msg_certificate_13.cpp
index dbdb5c58b..5ab6bc246 100644
--- a/src/lib/tls/tls13/msg_certificate_13.cpp
+++ b/src/lib/tls/tls13/msg_certificate_13.cpp
@@ -174,20 +174,14 @@ Certificate_13::Certificate_13(const Client_Hello_13& client_hello,
       m_request_context(), m_side(Connection_Side::Server) {
    BOTAN_ASSERT_NOMSG(client_hello.extensions().has<Signature_Algorithms>());
 
-   auto cert_chain =
+   setup_entries(
       credentials_manager.find_cert_chain(filter_signature_schemes(client_hello.signature_schemes()),
                                           to_algorithm_identifiers(client_hello.certificate_signature_schemes()),
                                           {},
                                           "tls-server",
-                                          client_hello.sni_hostname());
-
-   // RFC 8446 4.4.2
-   //    The server's certificate_list MUST always be non-empty.
-   if(cert_chain.empty()) {
-      throw TLS_Exception(Alert::HandshakeFailure, "No sufficient server certificate available");
-   }
-
-   setup_entries(std::move(cert_chain), client_hello.extensions().get<Certificate_Status_Request>(), callbacks);
+                                          client_hello.sni_hostname()),
+      client_hello.extensions().get<Certificate_Status_Request>(),
+      callbacks);
 }
 
 /**
diff --git a/src/lib/utils/mem_ops.h b/src/lib/utils/mem_ops.h
index b270e93ac..ee8013788 100644
--- a/src/lib/utils/mem_ops.h
+++ b/src/lib/utils/mem_ops.h
@@ -162,17 +162,21 @@ inline constexpr To typecast_copy(const FromT* src) noexcept
    return dst;
 }
 
+#if !defined(BOTAN_IS_BEGIN_BUILT)
 /**
 * Set memory to a fixed value
 * @param ptr a pointer to an array of bytes
 * @param n the number of Ts pointed to by ptr
 * @param val the value to set each byte to
 */
+BOTAN_DEPRECATED("This function is deprecated")
+
 inline constexpr void set_mem(uint8_t* ptr, size_t n, uint8_t val) {
    if(n > 0) {
       std::memset(ptr, val, n);
    }
 }
+#endif
 
 inline const uint8_t* cast_char_ptr_to_uint8(const char* s) {
    return reinterpret_cast<const uint8_t*>(s);
diff --git a/src/lib/utils/stl_util.h b/src/lib/utils/stl_util.h
index 96acfef6a..c381ed0e3 100644
--- a/src/lib/utils/stl_util.h
+++ b/src/lib/utils/stl_util.h
@@ -9,6 +9,7 @@
 #ifndef BOTAN_STL_UTIL_H_
 #define BOTAN_STL_UTIL_H_
 
+#include <algorithm>
 #include <functional>
 #include <map>
 #include <set>
@@ -218,6 +219,11 @@ class BufferStuffer {
          std::copy(buffer.begin(), buffer.end(), sink.begin());
       }
 
+      void append(uint8_t b, size_t repeat = 1) {
+         auto sink = next(repeat);
+         std::fill(sink.begin(), sink.end(), b);
+      }
+
       bool full() const { return m_buffer.empty(); }
 
       size_t remaining_capacity() const { return m_buffer.size(); }
diff --git a/src/lib/utils/version.cpp b/src/lib/utils/version.cpp
index 2439653fb..e46c8980d 100644
--- a/src/lib/utils/version.cpp
+++ b/src/lib/utils/version.cpp
@@ -41,15 +41,8 @@ const char* version_cstr() {
       STR(BOTAN_VERSION_SUFFIX)
 #endif
          " ("
-#if defined(BOTAN_UNSAFE_FUZZER_MODE) || defined(BOTAN_TERMINATE_ON_ASSERTS)
-         "UNSAFE "
-   #if defined(BOTAN_UNSAFE_FUZZER_MODE)
-         "FUZZER MODE "
-   #endif
-   #if defined(BOTAN_TERMINATE_ON_ASSERTS)
-         "TERMINATE ON ASSERTS "
-   #endif
-         "BUILD "
+#if defined(BOTAN_UNSAFE_FUZZER_MODE)
+         "UNSAFE FUZZER MODE BUILD "
 #endif
       BOTAN_VERSION_RELEASE_TYPE
 #if(BOTAN_VERSION_DATESTAMP != 0)
diff --git a/src/scripts/ci_build.py b/src/scripts/ci_build.py
index b4e10f1f9..33a34e3cd 100755
--- a/src/scripts/ci_build.py
+++ b/src/scripts/ci_build.py
@@ -217,7 +217,7 @@ def determine_flags(target, target_os, target_cpu, target_cc, cc_bin, ccache,
         flags += ['--with-debug-info']
 
     if target in ['coverage', 'sanitizer', 'fuzzers']:
-        flags += ['--unsafe-terminate-on-asserts']
+        flags += ['--terminate-on-asserts']
 
     if target in ['valgrind', 'valgrind-full']:
         flags += ['--with-valgrind']


Test patch:
diff --git a/src/scripts/test_cli.py b/src/scripts/test_cli.py
index 43dee6636..a3a3ba85a 100755
--- a/src/scripts/test_cli.py
+++ b/src/scripts/test_cli.py
@@ -964,17 +964,10 @@ def cli_tls_socket_tests(tmp_dir):
             self.protocol_version = protocol_version
             self.policy = policy
             self.stdout_regex = kwargs.get("stdout_regex")
-            self.expect_error = kwargs.get("expect_error", False)
             self.psk = kwargs.get("psk")
             self.psk_identity = kwargs.get("psk_identity")
 
     configs = [
-        # Regression test: TLS 1.3 server hit an assertion when no certificate
-        #                  chain was found. Here, we provoke this by requiring
-        #                  an RSA-based certificate (server uses ECDSA).
-        TestConfig("No server cert", "1.3", "allow_tls12=false\nallow_tls13=true\nsignature_methods=RSA\n",
-                   stdout_regex='Alert: handshake_failure', expect_error=True),
-
         TestConfig("TLS 1.3", "1.3", "allow_tls12=false\nallow_tls13=true\n"),
         TestConfig("TLS 1.2", "1.2", "allow_tls12=true\nallow_tls13=false\n"),
 
@@ -1038,17 +1031,14 @@ def cli_tls_socket_tests(tmp_dir):
 
         (stdout, stderr) = tls_client.communicate()
 
-        if not tls_config.expect_error:
-            if stderr:
-                logging.error("Got unexpected stderr output (%s) %s", tls_config.name, stderr)
+        if stderr:
+            logging.error("Got unexpected stderr output (%s) %s", tls_config.name, stderr)
 
-            if b'Handshake complete' not in stdout:
-                logging.error('Failed to complete handshake (%s): %s', tls_config.name, stdout)
+        if b'Handshake complete' not in stdout:
+            logging.error('Failed to complete handshake (%s): %s', tls_config.name, stdout)
 
-            if client_msg not in stdout:
-                logging.error("Missing client message from stdout (%s): %s", tls_config.name, stdout)
-        elif tls_client.returncode == 0:
-            logging.error('Expected an error, but tls_client finished with success')
+        if client_msg not in stdout:
+            logging.error("Missing client message from stdout (%s): %s", tls_config.name, stdout)
 
         if tls_config.stdout_regex:
             match = re.search(tls_config.stdout_regex, stdout.decode('utf-8'))
@@ -1056,13 +1046,10 @@ def cli_tls_socket_tests(tmp_dir):
                 logging.error("Client log did not match expected regex (%s): %s", tls_config.name, tls_config.stdout_regex)
                 logging.error("Client said (stdout): %s", stdout)
 
-    try:
-        (srv_stdout, srv_stderr) = tls_server.communicate(None, 5)
-    except subprocess.TimeoutExpired:
-        tls_server.kill()
-        tls_server.communicate()
-    logging.debug("server said (stdout): %s", srv_stdout)
-    logging.debug("server said (stderr): %s", srv_stderr)
+    (srv_stdout, srv_stderr) = tls_server.communicate(None, 5)
+    if srv_stderr:
+        logging.error("server said (stdout): %s", srv_stdout)
+        logging.error("server said (stderr): %s", srv_stderr)
 
 def cli_tls_online_pqc_hybrid_tests(tmp_dir):
     if not run_socket_tests() or not run_online_tests() or not check_for_command("tls_client"):
diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp
index 02618f89c..a35c331af 100644
--- a/src/tests/test_ffi.cpp
+++ b/src/tests/test_ffi.cpp
@@ -314,8 +314,9 @@ class FFI_RNG_Test final : public FFI_Test {
          size_t cb_counter = 0;
 
          auto custom_get_cb = +[](void* context, uint8_t* out, size_t out_len) -> int {
-            Botan::set_mem(out, out_len, 0x12);
-            BOTAN_UNUSED(out, out_len);
+            for(size_t i = 0; i != out_len; ++i) {
+               out[i] = 0x12;
+            }
             (*(static_cast<size_t*>(context)))++;
             return 0;
          };