Repository: randombit/botan
Pull request #3393 (2023-03-20 11:17:13)

Support hash truncation in ECKCDSA (#2742)

lieser: I will take a look again into the ISO standard (and maybe extend the docstring) but from my current understanding the truncation is defined on a byte and not bit level.

See also the implantation in libecc and the detailed explanation:
https://github.com/libecc/libecc/blob/eb4b76c3499c745f6fab45604bcb2f2412e0e4af/src/sig/eckcdsa.c#L101

Patch:
diff --git a/doc/migration_guide.rst b/doc/migration_guide.rst
index dc45c04dc..c2cf372e3 100644
--- a/doc/migration_guide.rst
+++ b/doc/migration_guide.rst
@@ -378,17 +378,3 @@ created) or otherwise if ``x`` was non-zero then it was taken as the private
 key. Now there are two constructors, one taking a random number generator and a
 group, which generates a new key, and a second taking a group and an integer,
 which loads an existing key.
-
-XMSS Signature Changes
-------------------------
-
-The logic to derive WOTS+ private keys from the seed contained in the XMSS
-private key has been updated according to the recommendations in
-NIST SP 800-208. While signatures created with old private keys are still valid using
-the old public key, new valid signatures cannot be created. To still support legacy
-private XMSS keys, they can be used by passing ``WOTS_Derivation_Method::Botan2x`` to
-the constructor of the ``XMSS_PrivateKey``.
-
-Private XMSS keys created this way use the old derivation logic and can therefore
-generate new valid signatures. It is recommended to use
-``WOTS_Derivation_Method::NIST_SP800_208`` (default) when creating new XMSS keys.
diff --git a/src/build-data/buildh.in b/src/build-data/buildh.in
index 5f54650e5..9fa9a4bb3 100644
--- a/src/build-data/buildh.in
+++ b/src/build-data/buildh.in
@@ -158,7 +158,7 @@
 */
 
 /** How much to allocate for a buffer of no particular size */
-#define BOTAN_DEFAULT_BUFFER_SIZE 4096
+#define BOTAN_DEFAULT_BUFFER_SIZE 1024
 
 /**
 * Total maximum amount of RAM (in KiB) we will lock into memory, even
@@ -211,6 +211,7 @@
 /** Number of entropy bits polled for reseeding userspace RNGs like HMAC_DRBG */
 #define BOTAN_RNG_RESEED_POLL_BITS 256
 
+#define BOTAN_RNG_AUTO_RESEED_TIMEOUT std::chrono::milliseconds(10)
 #define BOTAN_RNG_RESEED_DEFAULT_TIMEOUT std::chrono::milliseconds(50)
 
 /**
@@ -223,6 +224,39 @@
 /** Multiplier on a block cipher's native parallelism */
 #define BOTAN_BLOCK_CIPHER_PAR_MULT 4
 
+/**
+* This directory will be monitored by ProcWalking_EntropySource and
+* the contents provided as entropy inputs to the RNG. May also be
+* usefully set to something like "/sys", depending on the system being
+* deployed to. Set to an empty string to disable.
+*/
+#define BOTAN_ENTROPY_PROC_FS_PATH "/proc"
+
+/*
+* These paramaters control how many bytes to read from the system
+* PRNG, and how long to block if applicable. The timeout only applies
+* to reading /dev/urandom and company.
+*/
+#define BOTAN_SYSTEM_RNG_POLL_REQUEST 64
+#define BOTAN_SYSTEM_RNG_POLL_TIMEOUT_MS 20
+
+/**
+* When a PBKDF is self-tuning parameters, it will attempt to take about this
+* amount of time to self-benchmark.
+*/
+#define BOTAN_PBKDF_TUNING_TIME std::chrono::milliseconds(10)
+
+/**
+* If no way of dynamically determining the cache line size for the
+* system exists, this value is used as the default. Used by the side
+* channel countermeasures rather than for alignment purposes, so it is
+* better to be on the smaller side if the exact value cannot be
+* determined. Typically 32 or 64 bytes on modern CPUs.
+*/
+#if !defined(BOTAN_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE)
+  #define BOTAN_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE 32
+#endif
+
 #if !defined(BOTAN_AUTO_RNG_HMAC)
 
   #if defined(BOTAN_HAS_SHA2_64)
diff --git a/src/cli/pbkdf.cpp b/src/cli/pbkdf.cpp
index 81959a518..079e121e7 100644
--- a/src/cli/pbkdf.cpp
+++ b/src/cli/pbkdf.cpp
@@ -18,7 +18,7 @@ namespace Botan_CLI {
 class PBKDF_Tune final : public Command
    {
    public:
-      PBKDF_Tune() : Command("pbkdf_tune --algo=Scrypt --max-mem=256 --tune-msec=10 --output-len=32 --check *times") {}
+      PBKDF_Tune() : Command("pbkdf_tune --algo=Scrypt --max-mem=256 --output-len=32 --check *times") {}
 
       std::string group() const override
          {
@@ -33,12 +33,12 @@ class PBKDF_Tune final : public Command
       void go() override
          {
          const size_t output_len = get_arg_sz("output-len");
-         const size_t max_mem = get_arg_sz("max-mem");
-         const auto tune_msec = std::chrono::milliseconds(get_arg_sz("tune-msec"));
          const std::string algo = get_arg("algo");
+         const size_t max_mem = get_arg_sz("max-mem");
          const bool check_time = flag_set("check");
 
-         auto pwdhash_fam = Botan::PasswordHashFamily::create(algo);
+         std::unique_ptr<Botan::PasswordHashFamily> pwdhash_fam =
+            Botan::PasswordHashFamily::create(algo);
 
          if(!pwdhash_fam)
             throw CLI_Error_Unsupported("Password hashing", algo);
@@ -63,7 +63,7 @@ class PBKDF_Tune final : public Command
                   throw CLI_Usage_Error("Unknown time value '" + time + "' for pbkdf_tune");
                   }
 
-               pwhash = pwdhash_fam->tune(output_len, std::chrono::milliseconds(msec), max_mem, tune_msec);
+               pwhash = pwdhash_fam->tune(output_len, std::chrono::milliseconds(msec), max_mem);
                }
 
             output() << "For " << time << " ms selected " << pwhash->to_string();
diff --git a/src/examples/chacha.cpp b/src/examples/chacha.cpp
index 80de6afbb..c9fc4c36b 100644
--- a/src/examples/chacha.cpp
+++ b/src/examples/chacha.cpp
@@ -13,11 +13,12 @@ int main() {
 
   // generate fresh nonce (IV)
   Botan::AutoSeeded_RNG rng;
-  const auto iv = rng.random_vec<std::vector<uint8_t>>(8);
+  std::vector<uint8_t> iv(8);
+  rng.randomize(iv.data(), iv.size());
 
   // set key and IV
   cipher->set_key(key);
-  cipher->set_iv(iv);
+  cipher->set_iv(iv.data(), iv.size());
   cipher->encipher(pt);
 
   std::cout << cipher->name() << " with iv " << Botan::hex_encode(iv) << ": "
diff --git a/src/lib/base/buf_comp.h b/src/lib/base/buf_comp.h
index 24d998edb..c01cea6e2 100644
--- a/src/lib/base/buf_comp.h
+++ b/src/lib/base/buf_comp.h
@@ -80,14 +80,26 @@ class BOTAN_PUBLIC_API(2,0) Buffered_Computation
       * final result as a container of your choice.
       * @return a contiguous container holding the result
       */
-      template<concepts::resizable_byte_buffer T = secure_vector<uint8_t>>
-      T final()
+      template<typename T>
+         requires(concepts::contiguous_container<T> &&
+                  concepts::resizable_container<T>)
+         T final()
          {
          T output(output_length());
          final_result(output.data());
          return output;
          }
 
+      /**
+      * Complete the computation and retrieve the
+      * final result.
+      * @return secure_vector holding the result
+      */
+      secure_vector<uint8_t> final()
+         {
+         return final<secure_vector<uint8_t>>();
+         }
+
       std::vector<uint8_t> final_stdvec()
          {
          return final<std::vector<uint8_t>>();
@@ -99,8 +111,8 @@ class BOTAN_PUBLIC_API(2,0) Buffered_Computation
          final_result(out.data());
          }
 
-      template<concepts::resizable_byte_buffer T>
-      void final(T& out)
+      template<typename Alloc>
+         void final(std::vector<uint8_t, Alloc>& out)
          {
          out.resize(output_length());
          final_result(out.data());
@@ -113,11 +125,22 @@ class BOTAN_PUBLIC_API(2,0) Buffered_Computation
       * @param length the length of the byte array
       * @result the result of the call to final()
       */
-      template<concepts::resizable_byte_buffer T = secure_vector<uint8_t>>
-      T process(const uint8_t in[], size_t length)
+      secure_vector<uint8_t> process(const uint8_t in[], size_t length)
+         {
+         add_data(in, length);
+         return final();
+         }
+
+      /**
+      * Update and finalize computation. Does the same as calling update()
+      * and final() consecutively.
+      * @param in the input to process
+      * @result the result of the call to final()
+      */
+      secure_vector<uint8_t> process(std::span<const uint8_t> in)
          {
-         update(in, length);
-         return final<T>();
+         add_data(in.data(), in.size());
+         return final();
          }
 
       /**
@@ -126,24 +149,25 @@ class BOTAN_PUBLIC_API(2,0) Buffered_Computation
       * @param in the input to process as a string
       * @result the result of the call to final()
       */
-      template<concepts::resizable_byte_buffer T = secure_vector<uint8_t>>
-      T process(std::string_view in)
+      secure_vector<uint8_t> process(std::string_view in)
          {
          update(in);
-         return final<T>();
+         return final();
          }
 
       /**
       * Update and finalize computation. Does the same as calling update()
       * and final() consecutively.
-      * @param in the input to process as a contiguous container
+      * @param in the input to process as a contiguous container or string-like
       * @result the result of the call to final()
       */
-      template<concepts::resizable_byte_buffer T = secure_vector<uint8_t>>
-      T process(std::span<const uint8_t> in)
+      template<typename OutT, typename T>
+         requires(concepts::convertible_to<T, std::string_view> ||
+                  concepts::convertible_to<T, std::span<const uint8_t>>)
+      OutT process(T in)
          {
          update(in);
-         return final<T>();
+         return final<OutT>();
          }
 
       virtual ~Buffered_Computation() = default;
diff --git a/src/lib/block/aria/aria.cpp b/src/lib/block/aria/aria.cpp
index d97dbd4bb..8c94f3681 100644
--- a/src/lib/block/aria/aria.cpp
+++ b/src/lib/block/aria/aria.cpp
@@ -19,7 +19,7 @@
 #include <botan/internal/aria.h>
 #include <botan/internal/loadstor.h>
 #include <botan/internal/rotate.h>
-#include <botan/internal/prefetch.h>
+#include <botan/internal/cpuid.h>
 
 namespace Botan {
 
@@ -207,7 +207,19 @@ inline void ARIA_FE(uint32_t& T0, uint32_t& T1, uint32_t& T2, uint32_t& T3)
 void transform(const uint8_t in[], uint8_t out[], size_t blocks,
                const secure_vector<uint32_t>& KS)
    {
-   prefetch_arrays(S1, S2, X1, X2);
+   /*
+   * Hit every cache line of S1, S2, X1, X2
+   *
+   * The initializer of Z ensures Z == 0xFFFFFFFF for any cache line size
+   * size that is a power of 2 and <= 512
+   */
+   const size_t cache_line_size = CPUID::cache_line_size();
+
+   volatile uint32_t Z = 0x9C1DADCF;
+   for(size_t i = 0; i < 256; i += cache_line_size)
+      {
+      Z = Z | make_uint32(S1[i], S2[i], X1[i], X2[i]);
+      }
 
    const size_t ROUNDS = (KS.size() / 4) - 1;
 
@@ -216,6 +228,8 @@ void transform(const uint8_t in[], uint8_t out[], size_t blocks,
       uint32_t t0, t1, t2, t3;
       load_be(in + 16*i, t0, t1, t2, t3);
 
+      t0 &= Z;
+
       for(size_t r = 0; r < ROUNDS; r += 2)
          {
          t0 ^= KS[4*r];
diff --git a/src/lib/block/block_cipher.h b/src/lib/block/block_cipher.h
index b6eb9f536..2d1258cfc 100644
--- a/src/lib/block/block_cipher.h
+++ b/src/lib/block/block_cipher.h
@@ -112,7 +112,8 @@ class BOTAN_PUBLIC_API(2,0) BlockCipher : public SymmetricAlgorithm
       * Encrypt one or more blocks
       * @param block the input/output buffer (multiple of block_size())
       */
-      void encrypt(std::span<uint8_t> block) const
+      template<typename Alloc>
+      void encrypt(std::vector<uint8_t, Alloc>& block) const
          {
          return encrypt_n(block.data(), block.data(), block.size() / block_size());
          }
@@ -121,7 +122,8 @@ class BOTAN_PUBLIC_API(2,0) BlockCipher : public SymmetricAlgorithm
       * Decrypt one or more blocks
       * @param block the input/output buffer (multiple of block_size())
       */
-      void decrypt(std::span<uint8_t> block) const
+      template<typename Alloc>
+      void decrypt(std::vector<uint8_t, Alloc>& block) const
          {
          return decrypt_n(block.data(), block.data(), block.size() / block_size());
          }
@@ -131,7 +133,9 @@ class BOTAN_PUBLIC_API(2,0) BlockCipher : public SymmetricAlgorithm
       * @param in the input buffer (multiple of block_size())
       * @param out the output buffer (same size as in)
       */
-      void encrypt(std::span<const uint8_t> in, std::span<uint8_t> out) const
+      template<typename Alloc, typename Alloc2>
+      void encrypt(const std::vector<uint8_t, Alloc>& in,
+                   std::vector<uint8_t, Alloc2>& out) const
          {
          return encrypt_n(in.data(), out.data(), in.size() / block_size());
          }
@@ -141,7 +145,9 @@ class BOTAN_PUBLIC_API(2,0) BlockCipher : public SymmetricAlgorithm
       * @param in the input buffer (multiple of block_size())
       * @param out the output buffer (same size as in)
       */
-      void decrypt(std::span<const uint8_t> in, std::span<uint8_t> out) const
+      template<typename Alloc, typename Alloc2>
+      void decrypt(const std::vector<uint8_t, Alloc>& in,
+                   std::vector<uint8_t, Alloc2>& out) const
          {
          return decrypt_n(in.data(), out.data(), in.size() / block_size());
          }
diff --git a/src/lib/block/camellia/camellia.cpp b/src/lib/block/camellia/camellia.cpp
index 4393cec7f..efddec67a 100644
--- a/src/lib/block/camellia/camellia.cpp
+++ b/src/lib/block/camellia/camellia.cpp
@@ -8,7 +8,6 @@
 #include <botan/internal/camellia.h>
 #include <botan/internal/loadstor.h>
 #include <botan/internal/rotate.h>
-#include <botan/internal/prefetch.h>
 
 namespace Botan {
 
@@ -182,8 +181,6 @@ inline uint64_t FLINV(uint64_t v, uint64_t K)
 void encrypt(const uint8_t in[], uint8_t out[], size_t blocks,
              const secure_vector<uint64_t>& SK, const size_t rounds)
    {
-   prefetch_arrays(SBOX1, SBOX2, SBOX3, SBOX4);
-
    for(size_t i = 0; i < blocks; ++i)
       {
       uint64_t D1, D2;
@@ -225,8 +222,6 @@ void encrypt(const uint8_t in[], uint8_t out[], size_t blocks,
 void decrypt(const uint8_t in[], uint8_t out[], size_t blocks,
              const secure_vector<uint64_t>& SK, const size_t rounds)
    {
-   prefetch_arrays(SBOX1, SBOX2, SBOX3, SBOX4);
-
    for(size_t i = 0; i < blocks; ++i)
       {
       uint64_t D1, D2;
diff --git a/src/lib/block/seed/seed.cpp b/src/lib/block/seed/seed.cpp
index f65f65508..e5242ec97 100644
--- a/src/lib/block/seed/seed.cpp
+++ b/src/lib/block/seed/seed.cpp
@@ -7,7 +7,6 @@
 
 #include <botan/internal/seed.h>
 #include <botan/internal/loadstor.h>
-#include <botan/internal/prefetch.h>
 
 namespace Botan {
 
@@ -79,8 +78,6 @@ void SEED::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
    {
    assert_key_material_set();
 
-   prefetch_arrays(SEED_S0, SEED_S1);
-
    for(size_t i = 0; i != blocks; ++i)
       {
       uint32_t B0 = load_be<uint32_t>(in, 0);
@@ -121,8 +118,6 @@ void SEED::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
    {
    assert_key_material_set();
 
-   prefetch_arrays(SEED_S0, SEED_S1);
-
    for(size_t i = 0; i != blocks; ++i)
       {
       uint32_t B0 = load_be<uint32_t>(in, 0);
diff --git a/src/lib/codec/base32/base32.cpp b/src/lib/codec/base32/base32.cpp
index b2a75a11a..462811a1c 100644
--- a/src/lib/codec/base32/base32.cpp
+++ b/src/lib/codec/base32/base32.cpp
@@ -205,7 +205,7 @@ size_t base32_decode(uint8_t output[],
    }
 
 size_t base32_decode(uint8_t output[],
-                     std::string_view input,
+                     const std::string& input,
                      bool ignore_ws)
    {
    return base32_decode(output, input.data(), input.length(), ignore_ws);
@@ -218,7 +218,7 @@ secure_vector<uint8_t> base32_decode(const char input[],
    return base_decode_to_vec<secure_vector<uint8_t>>(Base32(), input, input_length, ignore_ws);
    }
 
-secure_vector<uint8_t> base32_decode(std::string_view input,
+secure_vector<uint8_t> base32_decode(const std::string& input,
                                      bool ignore_ws)
    {
    return base32_decode(input.data(), input.size(), ignore_ws);
diff --git a/src/lib/codec/base32/base32.h b/src/lib/codec/base32/base32.h
index 314eb923f..d2bcb3e6a 100644
--- a/src/lib/codec/base32/base32.h
+++ b/src/lib/codec/base32/base32.h
@@ -9,9 +9,7 @@
 #define BOTAN_BASE32_CODEC_H_
 
 #include <botan/secmem.h>
-#include <span>
 #include <string>
-#include <string_view>
 
 namespace Botan {
 
@@ -48,7 +46,8 @@ std::string BOTAN_PUBLIC_API(2, 7) base32_encode(const uint8_t input[],
 * @param input some input
 * @return base32 representation of input
 */
-inline std::string base32_encode(std::span<const uint8_t> input)
+template <typename Alloc>
+std::string base32_encode(const std::vector<uint8_t, Alloc>& input)
    {
    return base32_encode(input.data(), input.size());
    }
@@ -98,7 +97,7 @@ size_t BOTAN_PUBLIC_API(2, 7) base32_decode(uint8_t output[],
 * @return number of bytes written to output
 */
 size_t BOTAN_PUBLIC_API(2, 7) base32_decode(uint8_t output[],
-      std::string_view input,
+      const std::string& input,
       bool ignore_ws = true);
 
 /**
@@ -120,7 +119,7 @@ secure_vector<uint8_t> BOTAN_PUBLIC_API(2, 7) base32_decode(const char input[],
                    exception if whitespace is encountered
 * @return decoded base32 output
 */
-secure_vector<uint8_t> BOTAN_PUBLIC_API(2, 7) base32_decode(std::string_view input,
+secure_vector<uint8_t> BOTAN_PUBLIC_API(2, 7) base32_decode(const std::string& input,
       bool ignore_ws = true);
 
 } // namespace Botan
diff --git a/src/lib/codec/base58/base58.h b/src/lib/codec/base58/base58.h
index 66322f76a..4654a0557 100644
--- a/src/lib/codec/base58/base58.h
+++ b/src/lib/codec/base58/base58.h
@@ -7,12 +7,9 @@
 #ifndef BOTAN_BASE58_CODEC_H_
 #define BOTAN_BASE58_CODEC_H_
 
-#include <botan/build.h>
-
-#include <span>
-#include <string>
-#include <string_view>
+#include <botan/secmem.h>
 #include <vector>
+#include <string>
 
 namespace Botan {
 
@@ -52,22 +49,24 @@ BOTAN_PUBLIC_API(2,9) base58_check_decode(const char input[],
 
 // Some convenience wrappers:
 
-inline std::string base58_encode(std::span<const uint8_t> vec)
+template<typename Alloc>
+inline std::string base58_encode(const std::vector<uint8_t, Alloc>& vec)
    {
    return base58_encode(vec.data(), vec.size());
    }
 
-inline std::string base58_check_encode(std::span<const uint8_t> vec)
+template<typename Alloc>
+inline std::string base58_check_encode(const std::vector<uint8_t, Alloc>& vec)
    {
    return base58_check_encode(vec.data(), vec.size());
    }
 
-inline std::vector<uint8_t> base58_decode(std::string_view s)
+inline std::vector<uint8_t> base58_decode(const std::string& s)
    {
    return base58_decode(s.data(), s.size());
    }
 
-inline std::vector<uint8_t> base58_check_decode(std::string_view s)
+inline std::vector<uint8_t> base58_check_decode(const std::string& s)
    {
    return base58_check_decode(s.data(), s.size());
    }
diff --git a/src/lib/kdf/kdf.h b/src/lib/kdf/kdf.h
index 0a0eabbc8..f3f21e0b2 100644
--- a/src/lib/kdf/kdf.h
+++ b/src/lib/kdf/kdf.h
@@ -9,11 +9,8 @@
 #define BOTAN_KDF_BASE_H_
 
 #include <botan/secmem.h>
-#include <botan/concepts.h>
 #include <botan/exceptn.h>
 #include <string>
-#include <string_view>
-#include <span>
 
 namespace Botan {
 
@@ -82,13 +79,15 @@ class BOTAN_PUBLIC_API(2,0) KDF
       * @param label_len size of label in bytes
       * @return the derived key
       */
-      template<concepts::resizable_byte_buffer T = secure_vector<uint8_t>>
-      T derive_key(size_t key_len,
-                   const uint8_t secret[], size_t secret_len,
-                   const uint8_t salt[], size_t salt_len,
-                   const uint8_t label[] = nullptr, size_t label_len = 0) const
+      secure_vector<uint8_t> derive_key(size_t key_len,
+                                    const uint8_t secret[],
+                                    size_t secret_len,
+                                    const uint8_t salt[],
+                                    size_t salt_len,
+                                    const uint8_t label[] = nullptr,
+                                    size_t label_len = 0) const
          {
-         T key(key_len);
+         secure_vector<uint8_t> key(key_len);
          kdf(key.data(), key.size(), secret, secret_len, salt, salt_len, label, label_len);
          return key;
          }
@@ -101,17 +100,16 @@ class BOTAN_PUBLIC_API(2,0) KDF
       * @param label purpose for the derived keying material
       * @return the derived key
       */
-      template<concepts::resizable_byte_buffer T = secure_vector<uint8_t>>
-      T derive_key(size_t key_len,
-                   std::span<const uint8_t> secret,
-                   std::string_view salt = "",
-                   std::string_view label = "") const
+      secure_vector<uint8_t> derive_key(size_t key_len,
+                                    const secure_vector<uint8_t>& secret,
+                                    const std::string& salt = "",
+                                    const std::string& label = "") const
          {
-         return derive_key<T>(key_len, secret.data(), secret.size(),
-                              cast_char_ptr_to_uint8(salt.data()),
-                              salt.length(),
-                              cast_char_ptr_to_uint8(label.data()),
-                              label.length());
+         return derive_key(key_len, secret.data(), secret.size(),
+                           cast_char_ptr_to_uint8(salt.data()),
+                           salt.length(),
+                           cast_char_ptr_to_uint8(label.data()),
+                           label.length());
 
          }
 
@@ -123,16 +121,16 @@ class BOTAN_PUBLIC_API(2,0) KDF
       * @param label purpose for the derived keying material
       * @return the derived key
       */
-      template<concepts::resizable_byte_buffer T = secure_vector<uint8_t>>
-      T derive_key(size_t key_len,
-                   std::span<const uint8_t> secret,
-                   std::span<const uint8_t> salt,
-                   std::span<const uint8_t> label) const
+      template<typename Alloc, typename Alloc2, typename Alloc3>
+      secure_vector<uint8_t> derive_key(size_t key_len,
+                                     const std::vector<uint8_t, Alloc>& secret,
+                                     const std::vector<uint8_t, Alloc2>& salt,
+                                     const std::vector<uint8_t, Alloc3>& label) const
          {
-         return derive_key<T>(key_len,
-                              secret.data(), secret.size(),
-                              salt.data(), salt.size(),
-                              label.data(), label.size());
+         return derive_key(key_len,
+                           secret.data(), secret.size(),
+                           salt.data(), salt.size(),
+                           label.data(), label.size());
          }
 
       /**
@@ -144,17 +142,17 @@ class BOTAN_PUBLIC_API(2,0) KDF
       * @param label purpose for the derived keying material
       * @return the derived key
       */
-      template<concepts::resizable_byte_buffer T = secure_vector<uint8_t>>
-      T derive_key(size_t key_len,
-                   std::span<const uint8_t> secret,
-                   const uint8_t salt[], size_t salt_len,
-                   std::string_view label = "") const
+      secure_vector<uint8_t> derive_key(size_t key_len,
+                                    const secure_vector<uint8_t>& secret,
+                                    const uint8_t salt[],
+                                    size_t salt_len,
+                                    const std::string& label = "") const
          {
-         return derive_key<T>(key_len,
-                              secret.data(), secret.size(),
-                              salt, salt_len,
-                              cast_char_ptr_to_uint8(label.data()),
-                              label.size());
+         return derive_key(key_len,
+                           secret.data(), secret.size(),
+                           salt, salt_len,
+                           cast_char_ptr_to_uint8(label.data()),
+                           label.size());
          }
 
       /**
@@ -166,17 +164,17 @@ class BOTAN_PUBLIC_API(2,0) KDF
       * @param label purpose for the derived keying material
       * @return the derived key
       */
-      template<concepts::resizable_byte_buffer T = secure_vector<uint8_t>>
-      T derive_key(size_t key_len,
-                   const uint8_t secret[], size_t secret_len,
-                   std::string_view salt = "",
-                   std::string_view label = "") const
+      secure_vector<uint8_t> derive_key(size_t key_len,
+                                    const uint8_t secret[],
+                                    size_t secret_len,
+                                    const std::string& salt = "",
+                                    const std::string& label = "") const
          {
-         return derive_key<T>(key_len, secret, secret_len,
-                              cast_char_ptr_to_uint8(salt.data()),
-                              salt.length(),
-                              cast_char_ptr_to_uint8(label.data()),
-                              label.length());
+         return derive_key(key_len, secret, secret_len,
+                           cast_char_ptr_to_uint8(salt.data()),
+                           salt.length(),
+                           cast_char_ptr_to_uint8(label.data()),
+                           label.length());
          }
 
       /**
diff --git a/src/lib/mac/mac.cpp b/src/lib/mac/mac.cpp
index edb156edd..55814a9b7 100644
--- a/src/lib/mac/mac.cpp
+++ b/src/lib/mac/mac.cpp
@@ -151,7 +151,7 @@ void MessageAuthenticationCode::start_msg(const uint8_t nonce[], size_t nonce_le
 /*
 * Default (deterministic) MAC verification operation
 */
-bool MessageAuthenticationCode::verify_mac_result(const uint8_t mac[], size_t length)
+bool MessageAuthenticationCode::verify_mac(const uint8_t mac[], size_t length)
    {
    secure_vector<uint8_t> our_mac = final();
 
diff --git a/src/lib/mac/mac.h b/src/lib/mac/mac.h
index 576de365b..125ec120e 100644
--- a/src/lib/mac/mac.h
+++ b/src/lib/mac/mac.h
@@ -11,7 +11,6 @@
 #include <botan/buf_comp.h>
 #include <botan/sym_algo.h>
 #include <string>
-#include <span>
 #include <memory>
 
 namespace Botan {
@@ -60,12 +59,20 @@ class BOTAN_PUBLIC_API(2,0) MessageAuthenticationCode : public Buffered_Computat
       * an empty string is an error. One MAC which *requires* a per-message
       * nonce be specified is GMAC.
       *
+      * @param nonce the message nonce bytes
+      * @param nonce_len the size of len in bytes
       * Default implementation simply rejects all non-empty nonces
       * since most hash/MAC algorithms do not support randomization
+      */
+      virtual void start_msg(const uint8_t nonce[], size_t nonce_len);
+
+      /**
+      * Begin processing a message with a nonce
       *
-      * @param nonce the message nonce bytes
+      * @param nonce the per message nonce
       */
-      void start(std::span<const uint8_t> nonce)
+      template<typename Alloc>
+      void start(const std::vector<uint8_t, Alloc>& nonce)
          {
          start_msg(nonce.data(), nonce.size());
          }
@@ -94,9 +101,16 @@ class BOTAN_PUBLIC_API(2,0) MessageAuthenticationCode : public Buffered_Computat
       * @param length the length of param in
       * @return true if the MAC is valid, false otherwise
       */
-      bool verify_mac(const uint8_t in[], size_t length)
+      virtual bool verify_mac(const uint8_t in[], size_t length);
+
+      /**
+      * Verify a MAC.
+      * @param in the MAC to verify as a byte array
+      * @return true if the MAC is valid, false otherwise
+      */
+      virtual bool verify_mac(const std::vector<uint8_t>& in)
          {
-         return verify_mac_result(in, length);
+         return verify_mac(in.data(), in.size());
          }
 
       /**
@@ -104,9 +118,9 @@ class BOTAN_PUBLIC_API(2,0) MessageAuthenticationCode : public Buffered_Computat
       * @param in the MAC to verify as a byte array
       * @return true if the MAC is valid, false otherwise
       */
-      bool verify_mac(std::span<const uint8_t> in)
+      virtual bool verify_mac(const secure_vector<uint8_t>& in)
          {
-         return verify_mac_result(in.data(), in.size());
+         return verify_mac(in.data(), in.size());
          }
 
       /**
@@ -135,20 +149,6 @@ class BOTAN_PUBLIC_API(2,0) MessageAuthenticationCode : public Buffered_Computat
       * if a key is ever reused for two different messages.
       */
       virtual bool fresh_key_required_per_message() const { return false; }
-
-   protected:
-      /**
-      * Prepare for processing a message under the specified nonce
-      *
-      * If the MAC does not support nonces, it should not override the default
-      * implementation.
-      */
-      virtual void start_msg(const uint8_t nonce[], size_t nonce_len);
-
-      /**
-      * Verify the MACs final result
-      */
-      virtual bool verify_mac_result(const uint8_t in[], size_t length);
    };
 
 typedef MessageAuthenticationCode MAC;
diff --git a/src/lib/modes/aead/ccm/ccm.cpp b/src/lib/modes/aead/ccm/ccm.cpp
index 41d064936..e99c47af5 100644
--- a/src/lib/modes/aead/ccm/ccm.cpp
+++ b/src/lib/modes/aead/ccm/ccm.cpp
@@ -117,7 +117,7 @@ void CCM_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
    m_msg_buf.clear();
    }
 
-size_t CCM_Mode::process_msg(uint8_t buf[], size_t sz)
+size_t CCM_Mode::process(uint8_t buf[], size_t sz)
    {
    BOTAN_STATE_CHECK(!m_nonce.empty());
    m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz);
@@ -174,7 +174,7 @@ secure_vector<uint8_t> CCM_Mode::format_c0()
    return C;
    }
 
-void CCM_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
+void CCM_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    {
    BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
 
@@ -227,7 +227,7 @@ void CCM_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
    reset();
    }
 
-void CCM_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
+void CCM_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    {
    BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
 
diff --git a/src/lib/modes/aead/ccm/ccm.h b/src/lib/modes/aead/ccm/ccm.h
index cf37c0d18..ba3f6cfe5 100644
--- a/src/lib/modes/aead/ccm/ccm.h
+++ b/src/lib/modes/aead/ccm/ccm.h
@@ -21,6 +21,8 @@ namespace Botan {
 class CCM_Mode : public AEAD_Mode
    {
    public:
+      size_t process(uint8_t buf[], size_t sz) override final;
+
       void set_associated_data(const uint8_t ad[], size_t ad_len) override final;
 
       bool associated_data_requires_key() const override final { return false; }
@@ -65,7 +67,6 @@ class CCM_Mode : public AEAD_Mode
       secure_vector<uint8_t> format_c0();
    private:
       void start_msg(const uint8_t nonce[], size_t nonce_len) override final;
-      size_t process_msg(uint8_t buf[], size_t sz) override final;
 
       void key_schedule(const uint8_t key[], size_t length) override final;
 
@@ -92,13 +93,12 @@ class CCM_Encryption final : public CCM_Mode
       CCM_Encryption(std::unique_ptr<BlockCipher> cipher, size_t tag_size = 16, size_t L = 3) :
          CCM_Mode(std::move(cipher), tag_size, L) {}
 
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
       size_t output_length(size_t input_length) const override
          { return input_length + tag_size(); }
 
       size_t minimum_final_size() const override { return 0; }
-
-   private:
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
    };
 
 /**
@@ -117,6 +117,8 @@ class CCM_Decryption final : public CCM_Mode
       CCM_Decryption(std::unique_ptr<BlockCipher> cipher, size_t tag_size = 16, size_t L = 3) :
          CCM_Mode(std::move(cipher), tag_size, L) {}
 
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
       size_t output_length(size_t input_length) const override
          {
          BOTAN_ARG_CHECK(input_length >= tag_size(), "Sufficient input");
@@ -124,9 +126,6 @@ class CCM_Decryption final : public CCM_Mode
          }
 
       size_t minimum_final_size() const override { return tag_size(); }
-
-   private:
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
    };
 
 }
diff --git a/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp b/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp
index 88311ec42..d5fa9f612 100644
--- a/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp
+++ b/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp
@@ -105,7 +105,7 @@ void ChaCha20Poly1305_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
       }
    }
 
-size_t ChaCha20Poly1305_Encryption::process_msg(uint8_t buf[], size_t sz)
+size_t ChaCha20Poly1305_Encryption::process(uint8_t buf[], size_t sz)
    {
    m_chacha->cipher1(buf, sz);
    m_poly1305->update(buf, sz); // poly1305 of ciphertext
@@ -113,7 +113,7 @@ size_t ChaCha20Poly1305_Encryption::process_msg(uint8_t buf[], size_t sz)
    return sz;
    }
 
-void ChaCha20Poly1305_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
+void ChaCha20Poly1305_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    {
    update(buffer, offset);
    if(cfrg_version())
@@ -133,7 +133,7 @@ void ChaCha20Poly1305_Encryption::finish_msg(secure_vector<uint8_t>& buffer, siz
    m_nonce_len = 0;
    }
 
-size_t ChaCha20Poly1305_Decryption::process_msg(uint8_t buf[], size_t sz)
+size_t ChaCha20Poly1305_Decryption::process(uint8_t buf[], size_t sz)
    {
    m_poly1305->update(buf, sz); // poly1305 of ciphertext
    m_chacha->cipher1(buf, sz);
@@ -141,7 +141,7 @@ size_t ChaCha20Poly1305_Decryption::process_msg(uint8_t buf[], size_t sz)
    return sz;
    }
 
-void ChaCha20Poly1305_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
+void ChaCha20Poly1305_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    {
    BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
    const size_t sz = buffer.size() - offset;
diff --git a/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h b/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h
index dd1a37946..d28686dba 100644
--- a/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h
+++ b/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h
@@ -77,9 +77,9 @@ class ChaCha20Poly1305_Encryption final : public ChaCha20Poly1305_Mode
 
       size_t minimum_final_size() const override { return 0; }
 
-   private:
-      size_t process_msg(uint8_t buf[], size_t size) override;
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
    };
 
 /**
@@ -96,9 +96,9 @@ class ChaCha20Poly1305_Decryption final : public ChaCha20Poly1305_Mode
 
       size_t minimum_final_size() const override { return tag_size(); }
 
-   private:
-      size_t process_msg(uint8_t buf[], size_t size) override;
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
    };
 
 }
diff --git a/src/lib/modes/aead/eax/eax.cpp b/src/lib/modes/aead/eax/eax.cpp
index c37191d94..ae878214c 100644
--- a/src/lib/modes/aead/eax/eax.cpp
+++ b/src/lib/modes/aead/eax/eax.cpp
@@ -128,7 +128,7 @@ void EAX_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
    m_cmac->update(2);
    }
 
-size_t EAX_Encryption::process_msg(uint8_t buf[], size_t sz)
+size_t EAX_Encryption::process(uint8_t buf[], size_t sz)
    {
    BOTAN_STATE_CHECK(!m_nonce_mac.empty());
    m_ctr->cipher(buf, buf, sz);
@@ -136,7 +136,7 @@ size_t EAX_Encryption::process_msg(uint8_t buf[], size_t sz)
    return sz;
    }
 
-void EAX_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
+void EAX_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    {
    BOTAN_STATE_CHECK(!m_nonce_mac.empty());
    update(buffer, offset);
@@ -156,7 +156,7 @@ void EAX_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
    m_nonce_mac.clear();
    }
 
-size_t EAX_Decryption::process_msg(uint8_t buf[], size_t sz)
+size_t EAX_Decryption::process(uint8_t buf[], size_t sz)
    {
    BOTAN_STATE_CHECK(!m_nonce_mac.empty());
    m_cmac->update(buf, sz);
@@ -164,7 +164,7 @@ size_t EAX_Decryption::process_msg(uint8_t buf[], size_t sz)
    return sz;
    }
 
-void EAX_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
+void EAX_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    {
    BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
    const size_t sz = buffer.size() - offset;
diff --git a/src/lib/modes/aead/eax/eax.h b/src/lib/modes/aead/eax/eax.h
index d680bd810..42a030bb2 100644
--- a/src/lib/modes/aead/eax/eax.h
+++ b/src/lib/modes/aead/eax/eax.h
@@ -85,9 +85,9 @@ class EAX_Encryption final : public EAX_Mode
 
       size_t minimum_final_size() const override { return 0; }
 
-   private:
-      size_t process_msg(uint8_t buf[], size_t size) override;
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
    };
 
 /**
@@ -111,9 +111,9 @@ class EAX_Decryption final : public EAX_Mode
 
       size_t minimum_final_size() const override { return tag_size(); }
 
-   private:
-      size_t process_msg(uint8_t buf[], size_t size) override;
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
    };
 
 }
diff --git a/src/lib/modes/aead/gcm/gcm.cpp b/src/lib/modes/aead/gcm/gcm.cpp
index 1ceb73be0..ee714177d 100644
--- a/src/lib/modes/aead/gcm/gcm.cpp
+++ b/src/lib/modes/aead/gcm/gcm.cpp
@@ -128,7 +128,7 @@ void GCM_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
    clear_mem(m_y0.data(), m_y0.size());
    }
 
-size_t GCM_Encryption::process_msg(uint8_t buf[], size_t sz)
+size_t GCM_Encryption::process(uint8_t buf[], size_t sz)
    {
    BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid buffer size");
    m_ctr->cipher(buf, buf, sz);
@@ -136,7 +136,7 @@ size_t GCM_Encryption::process_msg(uint8_t buf[], size_t sz)
    return sz;
    }
 
-void GCM_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
+void GCM_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    {
    BOTAN_ARG_CHECK(offset <= buffer.size(), "Invalid offset");
    const size_t sz = buffer.size() - offset;
@@ -150,7 +150,7 @@ void GCM_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
    buffer += std::make_pair(mac, tag_size());
    }
 
-size_t GCM_Decryption::process_msg(uint8_t buf[], size_t sz)
+size_t GCM_Decryption::process(uint8_t buf[], size_t sz)
    {
    BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid buffer size");
    m_ghash->update(buf, sz);
@@ -158,7 +158,7 @@ size_t GCM_Decryption::process_msg(uint8_t buf[], size_t sz)
    return sz;
    }
 
-void GCM_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
+void GCM_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    {
    BOTAN_ARG_CHECK(offset <= buffer.size(), "Invalid offset");
    const size_t sz = buffer.size() - offset;
diff --git a/src/lib/modes/aead/gcm/gcm.h b/src/lib/modes/aead/gcm/gcm.h
index 4cdb838ec..4f7c597d2 100644
--- a/src/lib/modes/aead/gcm/gcm.h
+++ b/src/lib/modes/aead/gcm/gcm.h
@@ -83,9 +83,9 @@ class GCM_Encryption final : public GCM_Mode
 
       size_t minimum_final_size() const override { return 0; }
 
-   private:
-      size_t process_msg(uint8_t buf[], size_t size) override;
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
    };
 
 /**
@@ -109,9 +109,9 @@ class GCM_Decryption final : public GCM_Mode
 
       size_t minimum_final_size() const override { return tag_size(); }
 
-   private:
-      size_t process_msg(uint8_t buf[], size_t size) override;
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
    };
 
 }
diff --git a/src/lib/modes/aead/ocb/ocb.cpp b/src/lib/modes/aead/ocb/ocb.cpp
index 7bd080098..ec4a5ecb4 100644
--- a/src/lib/modes/aead/ocb/ocb.cpp
+++ b/src/lib/modes/aead/ocb/ocb.cpp
@@ -375,14 +375,14 @@ void OCB_Encryption::encrypt(uint8_t buffer[], size_t blocks)
       }
    }
 
-size_t OCB_Encryption::process_msg(uint8_t buf[], size_t sz)
+size_t OCB_Encryption::process(uint8_t buf[], size_t sz)
    {
    BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid OCB input size");
    encrypt(buf, sz / block_size());
    return sz;
    }
 
-void OCB_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
+void OCB_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    {
    assert_key_material_set();
    BOTAN_STATE_CHECK(m_L->initialized());
@@ -466,14 +466,14 @@ void OCB_Decryption::decrypt(uint8_t buffer[], size_t blocks)
       }
    }
 
-size_t OCB_Decryption::process_msg(uint8_t buf[], size_t sz)
+size_t OCB_Decryption::process(uint8_t buf[], size_t sz)
    {
    BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid OCB input size");
    decrypt(buf, sz / block_size());
    return sz;
    }
 
-void OCB_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
+void OCB_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    {
    assert_key_material_set();
    BOTAN_STATE_CHECK(m_L->initialized());
diff --git a/src/lib/modes/aead/ocb/ocb.h b/src/lib/modes/aead/ocb/ocb.h
index f034e78df..0345c4226 100644
--- a/src/lib/modes/aead/ocb/ocb.h
+++ b/src/lib/modes/aead/ocb/ocb.h
@@ -102,10 +102,11 @@ class BOTAN_TEST_API OCB_Encryption final : public OCB_Mode
 
       size_t minimum_final_size() const override { return 0; }
 
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
    private:
       void encrypt(uint8_t input[], size_t blocks);
-      size_t process_msg(uint8_t buf[], size_t size) override;
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
    };
 
 class BOTAN_TEST_API OCB_Decryption final : public OCB_Mode
@@ -126,10 +127,11 @@ class BOTAN_TEST_API OCB_Decryption final : public OCB_Mode
 
       size_t minimum_final_size() const override { return tag_size(); }
 
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
    private:
       void decrypt(uint8_t input[], size_t blocks);
-      size_t process_msg(uint8_t buf[], size_t size) override;
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
    };
 
 }
diff --git a/src/lib/modes/aead/siv/siv.cpp b/src/lib/modes/aead/siv/siv.cpp
index d2d97ef6a..d206a3f79 100644
--- a/src/lib/modes/aead/siv/siv.cpp
+++ b/src/lib/modes/aead/siv/siv.cpp
@@ -116,7 +116,7 @@ void SIV_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
    m_msg_buf.clear();
    }
 
-size_t SIV_Mode::process_msg(uint8_t buf[], size_t sz)
+size_t SIV_Mode::process(uint8_t buf[], size_t sz)
    {
    // all output is saved for processing in finish
    m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz);
@@ -164,7 +164,7 @@ void SIV_Mode::set_ctr_iv(secure_vector<uint8_t> V)
    ctr().set_iv(V.data(), V.size());
    }
 
-void SIV_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
+void SIV_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    {
    BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
 
@@ -182,7 +182,7 @@ void SIV_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
       }
    }
 
-void SIV_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
+void SIV_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    {
    BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
 
diff --git a/src/lib/modes/aead/siv/siv.h b/src/lib/modes/aead/siv/siv.h
index fd378ad35..d2e0d8b84 100644
--- a/src/lib/modes/aead/siv/siv.h
+++ b/src/lib/modes/aead/siv/siv.h
@@ -23,6 +23,8 @@ class MessageAuthenticationCode;
 class BOTAN_TEST_API SIV_Mode : public AEAD_Mode
    {
    public:
+      size_t process(uint8_t buf[], size_t size) override final;
+
       /**
       * Sets the nth element of the vector of associated data
       * @param n index into the AD vector
@@ -74,7 +76,6 @@ class BOTAN_TEST_API SIV_Mode : public AEAD_Mode
       secure_vector<uint8_t> S2V(const uint8_t text[], size_t text_len);
    private:
       void start_msg(const uint8_t nonce[], size_t nonce_len) override final;
-      size_t process_msg(uint8_t buf[], size_t size) override final;
 
       void key_schedule(const uint8_t key[], size_t length) override final;
 
@@ -99,13 +100,12 @@ class BOTAN_TEST_API SIV_Encryption final : public SIV_Mode
       explicit SIV_Encryption(std::unique_ptr<BlockCipher> cipher) :
          SIV_Mode(std::move(cipher)) {}
 
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
       size_t output_length(size_t input_length) const override
          { return input_length + tag_size(); }
 
       size_t minimum_final_size() const override { return 0; }
-
-   private:
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
    };
 
 /**
@@ -120,6 +120,8 @@ class BOTAN_TEST_API SIV_Decryption final : public SIV_Mode
       explicit SIV_Decryption(std::unique_ptr<BlockCipher> cipher) :
          SIV_Mode(std::move(cipher)) {}
 
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
       size_t output_length(size_t input_length) const override
          {
          BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input");
@@ -127,9 +129,6 @@ class BOTAN_TEST_API SIV_Decryption final : public SIV_Mode
          }
 
       size_t minimum_final_size() const override { return tag_size(); }
-
-   private:
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
    };
 
 }
diff --git a/src/lib/modes/cbc/cbc.cpp b/src/lib/modes/cbc/cbc.cpp
index 09e09fdbc..6bc40e286 100644
--- a/src/lib/modes/cbc/cbc.cpp
+++ b/src/lib/modes/cbc/cbc.cpp
@@ -110,7 +110,7 @@ size_t CBC_Encryption::output_length(size_t input_length) const
       return round_up(input_length, block_size());
    }
 
-size_t CBC_Encryption::process_msg(uint8_t buf[], size_t sz)
+size_t CBC_Encryption::process(uint8_t buf[], size_t sz)
    {
    BOTAN_STATE_CHECK(state().empty() == false);
    const size_t BS = block_size();
@@ -135,7 +135,7 @@ size_t CBC_Encryption::process_msg(uint8_t buf[], size_t sz)
    return sz;
    }
 
-void CBC_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
+void CBC_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    {
    BOTAN_STATE_CHECK(state().empty() == false);
    BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
@@ -166,7 +166,7 @@ size_t CTS_Encryption::output_length(size_t input_length) const
    return input_length; // no ciphertext expansion in CTS
    }
 
-void CTS_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
+void CTS_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    {
    BOTAN_STATE_CHECK(state().empty() == false);
    BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
@@ -221,7 +221,7 @@ size_t CBC_Decryption::minimum_final_size() const
    return block_size();
    }
 
-size_t CBC_Decryption::process_msg(uint8_t buf[], size_t sz)
+size_t CBC_Decryption::process(uint8_t buf[], size_t sz)
    {
    BOTAN_STATE_CHECK(state().empty() == false);
 
@@ -249,7 +249,7 @@ size_t CBC_Decryption::process_msg(uint8_t buf[], size_t sz)
    return sz;
    }
 
-void CBC_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
+void CBC_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    {
    BOTAN_STATE_CHECK(state().empty() == false);
    BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
@@ -286,7 +286,7 @@ size_t CTS_Decryption::minimum_final_size() const
    return block_size() + 1;
    }
 
-void CTS_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
+void CTS_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    {
    BOTAN_STATE_CHECK(state().empty() == false);
    BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
diff --git a/src/lib/modes/cbc/cbc.h b/src/lib/modes/cbc/cbc.h
index 1fe55bc2e..b2567ab3f 100644
--- a/src/lib/modes/cbc/cbc.h
+++ b/src/lib/modes/cbc/cbc.h
@@ -81,13 +81,13 @@ class CBC_Encryption : public CBC_Mode
                      std::unique_ptr<BlockCipherModePaddingMethod> padding) :
          CBC_Mode(std::move(cipher), std::move(padding)) {}
 
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
       size_t output_length(size_t input_length) const override;
 
       size_t minimum_final_size() const override;
-
-   private:
-      size_t process_msg(uint8_t buf[], size_t size) override;
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
    };
 
 /**
@@ -105,12 +105,11 @@ class CTS_Encryption final : public CBC_Encryption
 
       size_t output_length(size_t input_length) const override;
 
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
       size_t minimum_final_size() const override;
 
       bool valid_nonce_length(size_t n) const override;
-
-   private:
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
    };
 
 /**
@@ -129,6 +128,10 @@ class CBC_Decryption : public CBC_Mode
          m_tempbuf(ideal_granularity())
          {}
 
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
       size_t output_length(size_t input_length) const override;
 
       size_t minimum_final_size() const override;
@@ -136,9 +139,6 @@ class CBC_Decryption : public CBC_Mode
       void reset() override;
 
    private:
-      size_t process_msg(uint8_t buf[], size_t size) override;
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
-
       secure_vector<uint8_t> m_tempbuf;
    };
 
@@ -155,12 +155,11 @@ class CTS_Decryption final : public CBC_Decryption
          CBC_Decryption(std::move(cipher), nullptr)
          {}
 
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
       size_t minimum_final_size() const override;
 
       bool valid_nonce_length(size_t n) const override;
-
-   private:
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
    };
 
 }
diff --git a/src/lib/modes/cfb/cfb.cpp b/src/lib/modes/cfb/cfb.cpp
index 4394e319f..11a95f066 100644
--- a/src/lib/modes/cfb/cfb.cpp
+++ b/src/lib/modes/cfb/cfb.cpp
@@ -125,7 +125,7 @@ void CFB_Mode::shift_register()
    m_keystream_pos = 0;
    }
 
-size_t CFB_Encryption::process_msg(uint8_t buf[], size_t sz)
+size_t CFB_Encryption::process(uint8_t buf[], size_t sz)
    {
    assert_key_material_set();
    BOTAN_STATE_CHECK(m_state.empty() == false);
@@ -171,7 +171,7 @@ size_t CFB_Encryption::process_msg(uint8_t buf[], size_t sz)
    return sz;
    }
 
-void CFB_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
+void CFB_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    {
    update(buffer, offset);
    }
@@ -190,7 +190,7 @@ inline void xor_copy(uint8_t buf[], uint8_t key_buf[], size_t len)
 
 }
 
-size_t CFB_Decryption::process_msg(uint8_t buf[], size_t sz)
+size_t CFB_Decryption::process(uint8_t buf[], size_t sz)
    {
    assert_key_material_set();
    BOTAN_STATE_CHECK(m_state.empty() == false);
@@ -232,7 +232,7 @@ size_t CFB_Decryption::process_msg(uint8_t buf[], size_t sz)
    return sz;
    }
 
-void CFB_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
+void CFB_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    {
    update(buffer, offset);
    }
diff --git a/src/lib/modes/cfb/cfb.h b/src/lib/modes/cfb/cfb.h
index b42f33846..153e932d2 100644
--- a/src/lib/modes/cfb/cfb.h
+++ b/src/lib/modes/cfb/cfb.h
@@ -78,9 +78,9 @@ class CFB_Encryption final : public CFB_Mode
       CFB_Encryption(std::unique_ptr<BlockCipher> cipher, size_t feedback_bits) :
          CFB_Mode(std::move(cipher), feedback_bits) {}
 
-   private:
-      size_t process_msg(uint8_t buf[], size_t size) override;
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
    };
 
 /**
@@ -98,9 +98,9 @@ class CFB_Decryption final : public CFB_Mode
       CFB_Decryption(std::unique_ptr<BlockCipher> cipher, size_t feedback_bits) :
          CFB_Mode(std::move(cipher), feedback_bits) {}
 
-   private:
-      size_t process_msg(uint8_t buf[], size_t size) override;
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+      size_t process(uint8_t buf[], size_t size) override;
+
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
    };
 
 }
diff --git a/src/lib/modes/cipher_mode.h b/src/lib/modes/cipher_mode.h
index a88995f8a..c87d9d1dc 100644
--- a/src/lib/modes/cipher_mode.h
+++ b/src/lib/modes/cipher_mode.h
@@ -8,12 +8,10 @@
 #ifndef BOTAN_CIPHER_MODE_H_
 #define BOTAN_CIPHER_MODE_H_
 
-#include <botan/concepts.h>
 #include <botan/secmem.h>
 #include <botan/sym_algo.h>
 #include <botan/exceptn.h>
 #include <string>
-#include <span>
 #include <vector>
 
 namespace Botan {
@@ -64,29 +62,17 @@ class BOTAN_PUBLIC_API(2,0) Cipher_Mode : public SymmetricAlgorithm
                                                           Cipher_Dir direction,
                                                           const std::string& provider = "");
 
-   protected:
       /*
       * Prepare for processing a message under the specified nonce
       */
       virtual void start_msg(const uint8_t nonce[], size_t nonce_len) = 0;
 
-      /*
-      * Process message blocks
-      * Input must be a multiple of update_granularity.
-      */
-      virtual size_t process_msg(uint8_t msg[], size_t msg_len) = 0;
-
-      /*
-      * Finishes a message
-      */
-      virtual void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) = 0;
-
-   public:
       /**
       * Begin processing a message with a fresh nonce.
       * @param nonce the per message nonce
       */
-      void start(std::span<const uint8_t> nonce)
+      template<typename Alloc>
+      void start(const std::vector<uint8_t, Alloc>& nonce)
          {
          start_msg(nonce.data(), nonce.size());
          }
@@ -127,23 +113,22 @@ class BOTAN_PUBLIC_API(2,0) Cipher_Mode : public SymmetricAlgorithm
       * mode requires the entire message be processed in one pass).
       *
       * @param msg the message to be processed
-      * @return bytes written in-place
+      * @param msg_len length of the message in bytes
       */
-      size_t process(std::span<uint8_t> msg)
-         { return this->process_msg(msg.data(), msg.size()); }
-      size_t process(uint8_t msg[], size_t msg_len)
-         { return this->process_msg(msg, msg_len); }
+      virtual size_t process(uint8_t msg[], size_t msg_len) = 0;
 
       /**
       * Process some data. Input must be in size update_granularity() uint8_t blocks.
       * @param buffer in/out parameter which will possibly be resized
       * @param offset an offset into blocks to begin processing
       */
-      template<concepts::resizable_byte_buffer T>
-      void update(T& buffer, size_t offset = 0)
+      void update(secure_vector<uint8_t>& buffer, size_t offset = 0)
          {
          BOTAN_ASSERT(buffer.size() >= offset, "Offset ok");
-         const size_t written = process(std::span(buffer).subspan(offset));
+         uint8_t* buf = buffer.data() + offset;
+         const size_t buf_size = buffer.size() - offset;
+
+         const size_t written = process(buf, buf_size);
          buffer.resize(offset + written);
          }
 
@@ -154,29 +139,7 @@ class BOTAN_PUBLIC_API(2,0) Cipher_Mode : public SymmetricAlgorithm
       *        minimum_final_size() bytes, and will be set to any final output
       * @param offset an offset into final_block to begin processing
       */
-      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0)
-         {
-         finish_msg(final_block, offset);
-         }
-
-      /**
-      * Complete procession of a message.
-      *
-      * Note: Using this overload with anything but a Botan::secure_vector<>
-      *       is copying the bytes in the in/out buffer.
-      *
-      * @param final_block in/out parameter which must be at least
-      *        minimum_final_size() bytes, and will be set to any final output
-      * @param offset an offset into final_block to begin processing
-      */
-      template<concepts::resizable_byte_buffer T>
-      void finish(T& final_block, size_t offset = 0)
-         {
-         Botan::secure_vector<uint8_t> tmp(final_block.begin(), final_block.end());
-         finish_msg(tmp, offset);
-         final_block.resize(tmp.size());
-         std::copy(tmp.begin(), tmp.end(), final_block.begin());
-         }
+      virtual void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) = 0;
 
       /**
       * Returns the size of the output if this transform is used to process a
diff --git a/src/lib/modes/stream_mode.h b/src/lib/modes/stream_mode.h
index 22100f63b..8738eb044 100644
--- a/src/lib/modes/stream_mode.h
+++ b/src/lib/modes/stream_mode.h
@@ -27,6 +27,15 @@ class Stream_Cipher_Mode final : public Cipher_Mode
       explicit Stream_Cipher_Mode(std::unique_ptr<StreamCipher> cipher) :
          m_cipher(std::move(cipher)) {}
 
+      size_t process(uint8_t buf[], size_t sz) override
+         {
+         m_cipher->cipher1(buf, sz);
+         return sz;
+         }
+
+      void finish(secure_vector<uint8_t>& buf, size_t offset) override
+         { return update(buf, offset); }
+
       size_t output_length(size_t input_length) const override { return input_length; }
 
       size_t update_granularity() const override { return 1; }
@@ -65,15 +74,6 @@ class Stream_Cipher_Mode final : public Cipher_Mode
             }
          }
 
-      size_t process_msg(uint8_t buf[], size_t sz) override
-         {
-         m_cipher->cipher1(buf, sz);
-         return sz;
-         }
-
-      void finish_msg(secure_vector<uint8_t>& buf, size_t offset) override
-         { return update(buf, offset); }
-
       void key_schedule(const uint8_t key[], size_t length) override
          {
          m_cipher->set_key(key, length);
diff --git a/src/lib/modes/xts/xts.cpp b/src/lib/modes/xts/xts.cpp
index 34e190d69..96e40c156 100644
--- a/src/lib/modes/xts/xts.cpp
+++ b/src/lib/modes/xts/xts.cpp
@@ -120,7 +120,7 @@ size_t XTS_Encryption::output_length(size_t input_length) const
    return input_length;
    }
 
-size_t XTS_Encryption::process_msg(uint8_t buf[], size_t sz)
+size_t XTS_Encryption::process(uint8_t buf[], size_t sz)
    {
    BOTAN_STATE_CHECK(tweak_set());
    const size_t BS = cipher_block_size();
@@ -145,7 +145,7 @@ size_t XTS_Encryption::process_msg(uint8_t buf[], size_t sz)
    return sz;
    }
 
-void XTS_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
+void XTS_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    {
    BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
    const size_t sz = buffer.size() - offset;
@@ -195,7 +195,7 @@ size_t XTS_Decryption::output_length(size_t input_length) const
    return input_length;
    }
 
-size_t XTS_Decryption::process_msg(uint8_t buf[], size_t sz)
+size_t XTS_Decryption::process(uint8_t buf[], size_t sz)
    {
    BOTAN_STATE_CHECK(tweak_set());
    const size_t BS = cipher_block_size();
@@ -220,7 +220,7 @@ size_t XTS_Decryption::process_msg(uint8_t buf[], size_t sz)
    return sz;
    }
 
-void XTS_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
+void XTS_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    {
    BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
    const size_t sz = buffer.size() - offset;
diff --git a/src/lib/modes/xts/xts.h b/src/lib/modes/xts/xts.h
index 959fed3c4..52cddb612 100644
--- a/src/lib/modes/xts/xts.h
+++ b/src/lib/modes/xts/xts.h
@@ -78,11 +78,11 @@ class XTS_Encryption final : public XTS_Mode
       explicit XTS_Encryption(std::unique_ptr<BlockCipher> cipher) :
          XTS_Mode(std::move(cipher)) {}
 
-      size_t output_length(size_t input_length) const override;
+      size_t process(uint8_t buf[], size_t size) override;
 
-   private:
-      size_t process_msg(uint8_t buf[], size_t size) override;
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+      size_t output_length(size_t input_length) const override;
    };
 
 /**
@@ -97,11 +97,11 @@ class XTS_Decryption final : public XTS_Mode
       explicit XTS_Decryption(std::unique_ptr<BlockCipher> cipher) :
          XTS_Mode(std::move(cipher)) {}
 
-      size_t output_length(size_t input_length) const override;
+      size_t process(uint8_t buf[], size_t size) override;
 
-   private:
-      size_t process_msg(uint8_t buf[], size_t size) override;
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+      size_t output_length(size_t input_length) const override;
    };
 
 }
diff --git a/src/lib/pbkdf/argon2/argon2.h b/src/lib/pbkdf/argon2/argon2.h
index 4e125dd7d..25833e9e3 100644
--- a/src/lib/pbkdf/argon2/argon2.h
+++ b/src/lib/pbkdf/argon2/argon2.h
@@ -90,8 +90,7 @@ class BOTAN_PUBLIC_API(2,11) Argon2_Family final : public PasswordHashFamily
 
       std::unique_ptr<PasswordHash> tune(size_t output_length,
                                          std::chrono::milliseconds msec,
-                                         size_t max_memory,
-                                         std::chrono::milliseconds tune_msec) const override;
+                                         size_t max_memory) const override;
 
       std::unique_ptr<PasswordHash> default_params() const override;
 
diff --git a/src/lib/pbkdf/argon2/argon2pwhash.cpp b/src/lib/pbkdf/argon2/argon2pwhash.cpp
index e97f1c2e8..0bf2210d0 100644
--- a/src/lib/pbkdf/argon2/argon2pwhash.cpp
+++ b/src/lib/pbkdf/argon2/argon2pwhash.cpp
@@ -86,18 +86,18 @@ std::string Argon2_Family::name() const
 
 std::unique_ptr<PasswordHash> Argon2_Family::tune(size_t /*output_length*/,
                                                   std::chrono::milliseconds msec,
-                                                  size_t max_memory,
-                                                  std::chrono::milliseconds tune_time) const
+                                                  size_t max_memory) const
    {
    const size_t max_kib = (max_memory == 0) ? 256*1024 : max_memory*1024;
 
    // Tune with a large memory otherwise we measure cache vs RAM speeds and underestimate
    // costs for larger params. Default is 36 MiB, or use 128 for long times.
-   const size_t tune_M = (msec >= std::chrono::milliseconds(200) ? 128 : 36) * 1024;
+   const size_t tune_M = (msec >= std::chrono::milliseconds(500) ? 128 : 36) * 1024;
    const size_t p = 1;
    size_t t = 1;
 
    Timer timer("Argon2");
+   const auto tune_time = BOTAN_PBKDF_TUNING_TIME;
 
    auto pwhash = this->from_params(tune_M, t, p);
 
@@ -139,7 +139,7 @@ std::unique_ptr<PasswordHash> Argon2_Family::tune(size_t /*output_length*/,
       est_nsec *= M_mult;
       }
 
-   if(est_nsec < target_nsec / 2)
+   if(est_nsec < target_nsec)
       {
       const uint64_t desired_cost_increase = (target_nsec + est_nsec - 1) / est_nsec;
       t *= static_cast<size_t>(desired_cost_increase);
diff --git a/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp b/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp
index f312c7e51..da0dd791b 100644
--- a/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp
+++ b/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp
@@ -30,10 +30,10 @@ std::string Bcrypt_PBKDF_Family::name() const
 
 std::unique_ptr<PasswordHash> Bcrypt_PBKDF_Family::tune(size_t output_length,
                                                         std::chrono::milliseconds msec,
-                                                        size_t /*max_memory*/,
-                                                        std::chrono::milliseconds tune_time) const
+                                                        size_t /*max_memory*/) const
    {
    Timer timer("Bcrypt_PBKDF");
+   const auto tune_time = BOTAN_PBKDF_TUNING_TIME;
 
    const size_t blocks = (output_length + 32 - 1) / 32;
 
diff --git a/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h b/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h
index 85f1bad33..ce6a3c718 100644
--- a/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h
+++ b/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h
@@ -54,8 +54,7 @@ class BOTAN_PUBLIC_API(2,11) Bcrypt_PBKDF_Family final : public PasswordHashFami
 
       std::unique_ptr<PasswordHash> tune(size_t output_length,
                                          std::chrono::milliseconds msec,
-                                         size_t max_memory,
-                                         std::chrono::milliseconds tune_msec) const override;
+                                         size_t max_memory) const override;
 
       std::unique_ptr<PasswordHash> default_params() const override;
 
diff --git a/src/lib/pbkdf/pbkdf2/pbkdf2.cpp b/src/lib/pbkdf/pbkdf2/pbkdf2.cpp
index 873d8040a..8e2b56f65 100644
--- a/src/lib/pbkdf/pbkdf2/pbkdf2.cpp
+++ b/src/lib/pbkdf/pbkdf2/pbkdf2.cpp
@@ -28,10 +28,36 @@ void pbkdf2_set_key(MessageAuthenticationCode& prf,
       }
    }
 
+}
+
+size_t
+pbkdf2(MessageAuthenticationCode& prf,
+       uint8_t out[],
+       size_t out_len,
+       const std::string& password,
+       const uint8_t salt[], size_t salt_len,
+       size_t iterations,
+       std::chrono::milliseconds msec)
+   {
+   if(iterations == 0)
+      {
+      iterations = PBKDF2(prf, out_len, msec).iterations();
+      }
+
+   PBKDF2 pbkdf2(prf, iterations);
+
+   pbkdf2.derive_key(out, out_len,
+                     password.c_str(), password.size(),
+                     salt, salt_len);
+
+   return iterations;
+   }
+
+namespace {
+
 size_t tune_pbkdf2(MessageAuthenticationCode& prf,
                    size_t output_length,
-                   std::chrono::milliseconds msec,
-                   std::chrono::milliseconds tune_time = std::chrono::milliseconds(10))
+                   uint32_t msec)
    {
    if(output_length == 0)
       output_length = 1;
@@ -46,6 +72,8 @@ size_t tune_pbkdf2(MessageAuthenticationCode& prf,
 
    Timer timer("PBKDF2");
 
+   const auto tune_time = BOTAN_PBKDF_TUNING_TIME;
+
    prf.set_key(nullptr, 0);
 
    timer.run_until_elapsed(tune_time, [&]() {
@@ -59,7 +87,7 @@ size_t tune_pbkdf2(MessageAuthenticationCode& prf,
 
    const uint64_t duration_nsec = timer.value() / timer.events();
 
-   const uint64_t desired_nsec = static_cast<uint64_t>(msec.count()) * 1000000;
+   const uint64_t desired_nsec = static_cast<uint64_t>(msec) * 1000000;
 
    if(duration_nsec > desired_nsec)
       return trial_iterations;
@@ -76,29 +104,6 @@ size_t tune_pbkdf2(MessageAuthenticationCode& prf,
 
 }
 
-size_t
-pbkdf2(MessageAuthenticationCode& prf,
-       uint8_t out[],
-       size_t out_len,
-       const std::string& password,
-       const uint8_t salt[], size_t salt_len,
-       size_t iterations,
-       std::chrono::milliseconds msec)
-   {
-   if(iterations == 0)
-      {
-      iterations = tune_pbkdf2(prf, out_len, msec);
-      }
-
-   PBKDF2 pbkdf2(prf, iterations);
-
-   pbkdf2.derive_key(out, out_len,
-                     password.c_str(), password.size(),
-                     salt, salt_len);
-
-   return iterations;
-   }
-
 void pbkdf2(MessageAuthenticationCode& prf,
             uint8_t out[],
             size_t out_len,
@@ -152,7 +157,7 @@ PKCS5_PBKDF2::pbkdf(uint8_t key[], size_t key_len,
    {
    if(iterations == 0)
       {
-      iterations = tune_pbkdf2(*m_mac, key_len, msec);
+      iterations = PBKDF2(*m_mac, key_len, msec).iterations();
       }
 
    PBKDF2 pbkdf2(*m_mac, iterations);
@@ -178,7 +183,7 @@ std::unique_ptr<PBKDF> PKCS5_PBKDF2::new_object() const
 
 PBKDF2::PBKDF2(const MessageAuthenticationCode& prf, size_t olen, std::chrono::milliseconds msec) :
    m_prf(prf.new_object()),
-   m_iterations(tune_pbkdf2(*m_prf, olen, msec))
+   m_iterations(tune_pbkdf2(*m_prf, olen, static_cast<uint32_t>(msec.count())))
    {}
 
 std::string PBKDF2::to_string() const
@@ -199,14 +204,9 @@ std::string PBKDF2_Family::name() const
    return "PBKDF2(" + m_prf->name() + ")";
    }
 
-std::unique_ptr<PasswordHash> PBKDF2_Family::tune(
-   size_t output_len,
-   std::chrono::milliseconds msec,
-   size_t /*max_memory_usage_mb*/,
-   std::chrono::milliseconds tune_time) const
+std::unique_ptr<PasswordHash> PBKDF2_Family::tune(size_t output_len, std::chrono::milliseconds msec, size_t /*max_memory_usage_mb*/) const
    {
-   auto iterations = tune_pbkdf2(*m_prf, output_len, msec, tune_time);
-   return std::make_unique<PBKDF2>(*m_prf, iterations);
+   return std::make_unique<PBKDF2>(*m_prf, output_len, msec);
    }
 
 std::unique_ptr<PasswordHash> PBKDF2_Family::default_params() const
diff --git a/src/lib/pbkdf/pbkdf2/pbkdf2.h b/src/lib/pbkdf/pbkdf2/pbkdf2.h
index d87e18613..d56e53db7 100644
--- a/src/lib/pbkdf/pbkdf2/pbkdf2.h
+++ b/src/lib/pbkdf/pbkdf2/pbkdf2.h
@@ -44,7 +44,6 @@ class BOTAN_PUBLIC_API(2,8) PBKDF2 final : public PasswordHash
          m_iterations(iter)
          {}
 
-      BOTAN_DEPRECATED("For runtime tuning use PBKDF2_Family::tune")
       PBKDF2(const MessageAuthenticationCode& prf, size_t olen, std::chrono::milliseconds msec);
 
       size_t iterations() const override { return m_iterations; }
@@ -72,8 +71,7 @@ class BOTAN_PUBLIC_API(2,8) PBKDF2_Family final : public PasswordHashFamily
 
       std::unique_ptr<PasswordHash> tune(size_t output_len,
                                          std::chrono::milliseconds msec,
-                                         size_t max_memory,
-                                         std::chrono::milliseconds tune_msec) const override;
+                                         size_t max_memory) const override;
 
       /**
       * Return some default parameter set for this PBKDF that should be good
diff --git a/src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp b/src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp
index abcb81cd3..ae0b476cb 100644
--- a/src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp
+++ b/src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp
@@ -82,7 +82,7 @@ size_t OpenPGP_S2K::pbkdf(uint8_t output_buf[], size_t output_len,
    if(iterations == 0)
       {
       RFC4880_S2K_Family s2k_params(m_hash->new_object());
-      iterations = s2k_params.tune(output_len, msec, 0, std::chrono::milliseconds(10))->iterations();
+      iterations = s2k_params.tune(output_len, msec, 0)->iterations();
       }
 
    pgp_s2k(*m_hash, output_buf, output_len,
@@ -98,12 +98,10 @@ std::string RFC4880_S2K_Family::name() const
    return "OpenPGP-S2K(" + m_hash->name() + ")";
    }
 
-std::unique_ptr<PasswordHash> RFC4880_S2K_Family::tune(
-   size_t output_len,
-   std::chrono::milliseconds msec,
-   size_t /*max_memory_usage_mb*/,
-   std::chrono::milliseconds tune_time) const
+std::unique_ptr<PasswordHash> RFC4880_S2K_Family::tune(size_t output_len, std::chrono::milliseconds msec, size_t /*max_memory_usage_mb*/) const
    {
+   const auto tune_time = BOTAN_PBKDF_TUNING_TIME;
+
    const size_t buf_size = 1024;
    std::vector<uint8_t> buffer(buf_size);
 
diff --git a/src/lib/pbkdf/pgp_s2k/pgp_s2k.h b/src/lib/pbkdf/pgp_s2k/pgp_s2k.h
index 687eee239..73fb1f449 100644
--- a/src/lib/pbkdf/pgp_s2k/pgp_s2k.h
+++ b/src/lib/pbkdf/pgp_s2k/pgp_s2k.h
@@ -118,8 +118,7 @@ class BOTAN_PUBLIC_API(2,8) RFC4880_S2K_Family final : public PasswordHashFamily
 
       std::unique_ptr<PasswordHash> tune(size_t output_len,
                                          std::chrono::milliseconds msec,
-                                         size_t max_mem,
-                                         std::chrono::milliseconds tune_msec) const override;
+                                         size_t max_mem) const override;
 
       /**
       * Return some default parameter set for this PBKDF that should be good
diff --git a/src/lib/pbkdf/pwdhash.h b/src/lib/pbkdf/pwdhash.h
index 34a0d212f..775e8b79c 100644
--- a/src/lib/pbkdf/pwdhash.h
+++ b/src/lib/pbkdf/pwdhash.h
@@ -146,12 +146,10 @@ class BOTAN_PUBLIC_API(2,8) PasswordHashFamily
       * the returned parameters will require, in mebibytes (2**20 bytes). It may
       * require some small amount above the request. Set to zero to place no
       * limit at all.
-      * @param tuning_msec how long to run the tuning loop
       */
       virtual std::unique_ptr<PasswordHash> tune(size_t output_length,
                                                  std::chrono::milliseconds msec,
-                                                 size_t max_memory_usage_mb = 0,
-                                                 std::chrono::milliseconds tuning_msec = std::chrono::milliseconds(10)) const = 0;
+                                                 size_t max_memory_usage_mb = 0) const = 0;
 
       /**
       * Return some default parameter set for this PBKDF that should be good
diff --git a/src/lib/pbkdf/scrypt/scrypt.cpp b/src/lib/pbkdf/scrypt/scrypt.cpp
index b1a05db26..6f2c6a54b 100644
--- a/src/lib/pbkdf/scrypt/scrypt.cpp
+++ b/src/lib/pbkdf/scrypt/scrypt.cpp
@@ -37,8 +37,7 @@ std::unique_ptr<PasswordHash> Scrypt_Family::default_params() const
 
 std::unique_ptr<PasswordHash> Scrypt_Family::tune(size_t output_length,
                                                   std::chrono::milliseconds msec,
-                                                  size_t max_memory_usage_mb,
-                                                  std::chrono::milliseconds tune_time) const
+                                                  size_t max_memory_usage_mb) const
    {
    BOTAN_UNUSED(output_length);
 
@@ -54,17 +53,14 @@ std::unique_ptr<PasswordHash> Scrypt_Family::tune(size_t output_length,
    * Compute stime(8192,1,1) as baseline and extrapolate
    */
 
-   // We include a bit of slop here (the + 512) to handle the fact that scrypt's
-   // memory consumption is modified by all three parameters, and otherwise we
-   // stop before hitting the desired target.
-
-   const size_t max_memory_usage = max_memory_usage_mb * 1024 * 1024 + 512;
+   const size_t max_memory_usage = max_memory_usage_mb * 1024 * 1024;
    // Starting parameters
-   size_t N = 8*1024;
+   size_t N = 8192;
    size_t r = 1;
    size_t p = 1;
 
    Timer timer("Scrypt");
+   const auto tune_time = BOTAN_PBKDF_TUNING_TIME;
 
    auto pwdhash = this->from_params(N, r, p);
 
@@ -89,7 +85,7 @@ std::unique_ptr<PasswordHash> Scrypt_Family::tune(size_t output_length,
 
    // First move increase r by 8x if possible
 
-   if(max_memory_usage == 0 || scrypt_memory_usage(N, r*8, p) <= max_memory_usage)
+   if(max_memory_usage == 0 || scrypt_memory_usage(N, r, p)*8 < max_memory_usage)
       {
       if(target_nsec / est_nsec >= 5)
          {
@@ -100,7 +96,7 @@ std::unique_ptr<PasswordHash> Scrypt_Family::tune(size_t output_length,
 
    // Now double N as many times as we can
 
-   while(max_memory_usage == 0 || scrypt_memory_usage(N*2, r, p) <= max_memory_usage)
+   while(max_memory_usage == 0 || scrypt_memory_usage(N, r, p)*2 < max_memory_usage)
       {
       if(target_nsec / est_nsec >= 2)
          {
diff --git a/src/lib/pbkdf/scrypt/scrypt.h b/src/lib/pbkdf/scrypt/scrypt.h
index 0eec830f0..7a5b3478c 100644
--- a/src/lib/pbkdf/scrypt/scrypt.h
+++ b/src/lib/pbkdf/scrypt/scrypt.h
@@ -53,8 +53,7 @@ class BOTAN_PUBLIC_API(2,8) Scrypt_Family final : public PasswordHashFamily
 
       std::unique_ptr<PasswordHash> tune(size_t output_length,
                                          std::chrono::milliseconds msec,
-                                         size_t max_memory,
-                                         std::chrono::milliseconds tune_msec) const override;
+                                         size_t max_memory) const override;
 
       std::unique_ptr<PasswordHash> default_params() const override;
 
diff --git a/src/lib/prov/commoncrypto/commoncrypto_mode.cpp b/src/lib/prov/commoncrypto/commoncrypto_mode.cpp
index 503b6fd62..b1b00fd39 100644
--- a/src/lib/prov/commoncrypto/commoncrypto_mode.cpp
+++ b/src/lib/prov/commoncrypto/commoncrypto_mode.cpp
@@ -28,6 +28,9 @@ class CommonCrypto_Cipher_Mode final : public Cipher_Mode
       std::string provider() const override { return "commoncrypto"; }
       std::string name() const override { return m_mode_name; }
 
+      void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+      size_t process(uint8_t msg[], size_t msg_len) override;
+      void finish(secure_vector<uint8_t>& final_block, size_t offset0) override;
       size_t output_length(size_t input_length) const override;
       size_t update_granularity() const override;
       size_t ideal_granularity() const override;
@@ -42,10 +45,6 @@ class CommonCrypto_Cipher_Mode final : public Cipher_Mode
    private:
       void key_schedule(const uint8_t key[], size_t length) override;
 
-      void start_msg(const uint8_t nonce[], size_t nonce_len) override;
-      size_t process_msg(uint8_t msg[], size_t msg_len) override;
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset0) override;
-
       const std::string m_mode_name;
       Cipher_Dir m_direction;
       CommonCryptor_Opts m_opts;
@@ -89,7 +88,7 @@ void CommonCrypto_Cipher_Mode::start_msg(const uint8_t nonce[], size_t nonce_len
    m_nonce_set = true;
    }
 
-size_t CommonCrypto_Cipher_Mode::process_msg(uint8_t msg[], size_t msg_len)
+size_t CommonCrypto_Cipher_Mode::process(uint8_t msg[], size_t msg_len)
    {
    assert_key_material_set();
    BOTAN_STATE_CHECK(m_nonce_set);
@@ -118,8 +117,8 @@ size_t CommonCrypto_Cipher_Mode::process_msg(uint8_t msg[], size_t msg_len)
    return outl;
    }
 
-void CommonCrypto_Cipher_Mode::finish_msg(secure_vector<uint8_t>& buffer,
-                                          size_t offset)
+void CommonCrypto_Cipher_Mode::finish(secure_vector<uint8_t>& buffer,
+                                      size_t offset)
    {
    assert_key_material_set();
    BOTAN_STATE_CHECK(m_nonce_set);
diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium_symmetric_primitives.h b/src/lib/pubkey/dilithium/dilithium_common/dilithium_symmetric_primitives.h
index 229d732d2..593de0dc2 100644
--- a/src/lib/pubkey/dilithium/dilithium_common/dilithium_symmetric_primitives.h
+++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium_symmetric_primitives.h
@@ -55,7 +55,10 @@ class Dilithium_Symmetric_Primitives
       secure_vector<uint8_t> ExpandMask(std::span<const uint8_t> seed,
                                         uint16_t nonce, size_t out_len) const
          {
-         return XOF(XofType::k256, seed, nonce)->keystream_bytes(out_len);
+         auto xof = XOF(XofType::k256, seed, nonce);
+         secure_vector<uint8_t> buf(out_len);
+         xof->write_keystream(buf.data(), buf.size());
+         return buf;
          }
 
       // Mode dependent function
diff --git a/src/lib/pubkey/eckcdsa/eckcdsa.cpp b/src/lib/pubkey/eckcdsa/eckcdsa.cpp
index 5090c38ea..f68c97400 100644
--- a/src/lib/pubkey/eckcdsa/eckcdsa.cpp
+++ b/src/lib/pubkey/eckcdsa/eckcdsa.cpp
@@ -2,6 +2,7 @@
 * ECKCDSA (ISO/IEC 14888-3:2006/Cor.2:2009)
 * (C) 2016 René Korthaus, Sirrix AG
 * (C) 2018 Jack Lloyd
+* (C) 2023 Philippe Lieser - Rohde & Schwarz Cybersecurity
 *
 * Botan is released under the Simplified BSD License (see license.txt)
 */
@@ -95,6 +96,38 @@ std::vector<uint8_t> eckcdsa_prefix(const PointGFp& point,
    return prefix;
    }
 
+/**
+ * @brief Truncate hash output if needed.
+ *
+ * If the output length of the hash function exceeds the size of the group order,
+ * ISO/IEC 14888-3:2018 specifies a truncation of the hash output
+ * when calculating the witness R (the first part of the signature) and H.
+ *
+ * The truncation is specified as follows:
+ *
+ * R = I2BS(beta', BS2I(gamma, R) mod 2^beta')
+ * H = I2BS(beta', BS2I(gamma, H) mod 2^beta')
+ *
+ * where
+ * - gamma: the output bit-length of the hash-function
+ * - beta: the bit-length of the prime number q (i.e. the group order size)
+ * - beta' = 8 * ceil(beta / 8)
+ *
+ * This essentially means a truncation on the byte level
+ * happens from the low side of the hash.
+ *
+ * @param[in,out] digest The hash output to potentially truncate.
+ * @param[in] group_order_bytes Size of the group order.
+ */
+void truncate_hash_if_needed(secure_vector<uint8_t>& digest, size_t group_order_bytes)
+   {
+   if(digest.size() > group_order_bytes)
+      {
+      const size_t bytes_to_truncate = digest.size() - group_order_bytes;
+      digest.erase(digest.begin(), digest.begin() + bytes_to_truncate);
+      }
+   }
+
 /**
 * ECKCDSA signature operation
 */
@@ -109,22 +142,6 @@ class ECKCDSA_Signature_Operation final : public PK_Ops::Signature
          m_hash(eckcdsa_signature_hash(padding)),
          m_prefix_used(false)
          {
-         /*
-         ECKCDSA does support hash truncation but for whatever reason uses the
-         opposite convention of DSA, ECDSA, ECGDSA, etc, cutting bits from
-         the low rather than the high side of the hash.
-
-         As a result it is not easily supported in this codebase, and since
-         ECKCDSA is quite obscure and mostly included for BSI compliance, we
-         simply prohibit creating signatures where the resulting signature will
-         not be accepted by other implementations of ECKCDSA
-
-         See https://github.com/randombit/botan/issues/2742 for further detail.
-         */
-
-         if(m_hash->output_length() > m_group.get_order_bytes())
-            throw Encoding_Error("ECKCDSA does not support the hash being larger than the group");
-
          m_prefix = eckcdsa_prefix(eckcdsa.public_point(),
                                    m_group.get_order_bytes(),
                                    m_hash->hash_block_size());
@@ -143,7 +160,8 @@ class ECKCDSA_Signature_Operation final : public PK_Ops::Signature
       secure_vector<uint8_t> sign(RandomNumberGenerator& rng) override
          {
          m_prefix_used = false;
-         const secure_vector<uint8_t> digest = m_hash->final();
+         secure_vector<uint8_t> digest = m_hash->final();
+         truncate_hash_if_needed(digest, m_group.get_order_bytes());
          return raw_sign(digest.data(), digest.size(), rng);
          }
 
@@ -185,6 +203,7 @@ ECKCDSA_Signature_Operation::raw_sign(const uint8_t msg[], size_t msg_len,
    auto hash = m_hash->new_object();
    hash->update(to_be_hashed);
    secure_vector<uint8_t> c = hash->final();
+   truncate_hash_if_needed(c, m_group.get_order_bytes());
 
    const BigInt r(c.data(), c.size());
 
@@ -263,7 +282,8 @@ void ECKCDSA_Verification_Operation::update(const uint8_t msg[], size_t msg_len)
 bool ECKCDSA_Verification_Operation::is_valid_signature(const uint8_t sig[], size_t sig_len)
    {
    m_prefix_used = false;
-   const secure_vector<uint8_t> digest = m_hash->final();
+   secure_vector<uint8_t> digest = m_hash->final();
+   truncate_hash_if_needed(digest, m_group.get_order_bytes());
    return verify(digest.data(), digest.size(), sig, sig_len);
    }
 
@@ -307,6 +327,7 @@ bool ECKCDSA_Verification_Operation::verify(const uint8_t msg[], size_t msg_len,
    auto c_hash = m_hash->new_object();
    c_hash->update(c.data(), c.size());
    secure_vector<uint8_t> v = c_hash->final();
+   truncate_hash_if_needed(v, m_group.get_order_bytes());
 
    return (v == r);
    }
diff --git a/src/lib/pubkey/eckcdsa/eckcdsa.h b/src/lib/pubkey/eckcdsa/eckcdsa.h
index 9a162223d..9071a4d4b 100644
--- a/src/lib/pubkey/eckcdsa/eckcdsa.h
+++ b/src/lib/pubkey/eckcdsa/eckcdsa.h
@@ -1,5 +1,5 @@
 /*
-* ECKCDSA (ISO/IEC 14888-3:2006/Cor.2:2009)
+* ECKCDSA (ISO/IEC 14888-3:2018)
 * (C) 2016 René Korthaus, Sirrix AG
 *
 * Botan is released under the Simplified BSD License (see license.txt)
diff --git a/src/lib/pubkey/kyber/kyber_90s/kyber_90s.h b/src/lib/pubkey/kyber/kyber_90s/kyber_90s.h
index 7e7e25396..c639f3e26 100644
--- a/src/lib/pubkey/kyber/kyber_90s/kyber_90s.h
+++ b/src/lib/pubkey/kyber/kyber_90s/kyber_90s.h
@@ -78,8 +78,14 @@ class Kyber_90s_Symmetric_Primitives : public Kyber_Symmetric_Primitives
                                  const size_t outlen) const override
          {
          m_aes256_ctr->set_key(seed);
-         m_aes256_ctr->set_iv(std::array<uint8_t, 12>{nonce, 0});
-         return m_aes256_ctr->keystream_bytes(outlen);
+
+         const std::array<uint8_t, 12> iv = {nonce, 0};
+         m_aes256_ctr->set_iv(iv.data(), iv.size());
+
+         secure_vector<uint8_t> out(outlen);
+         m_aes256_ctr->write_keystream(out.data(), out.size());
+
+         return out;
          }
 
    private:
diff --git a/src/lib/pubkey/kyber/kyber_common/kyber.cpp b/src/lib/pubkey/kyber/kyber_common/kyber.cpp
index 7165d4549..b948f23cd 100644
--- a/src/lib/pubkey/kyber/kyber_common/kyber.cpp
+++ b/src/lib/pubkey/kyber/kyber_common/kyber.cpp
@@ -1116,7 +1116,7 @@ class Kyber_PublicKeyInternal
            m_polynomials(PolynomialVector::from_bytes(polynomials, m_mode)),
            m_seed(std::move(seed)),
            m_public_key_bits_raw(concat(m_polynomials.to_bytes<std::vector<uint8_t>>(), m_seed)),
-           m_H_public_key_bits_raw(m_mode.H()->process<std::vector<uint8_t>>(m_public_key_bits_raw))
+           m_H_public_key_bits_raw(unlock(m_mode.H()->process(m_public_key_bits_raw)))
          {
          }
 
@@ -1125,7 +1125,7 @@ class Kyber_PublicKeyInternal
            m_polynomials(std::move(polynomials)),
            m_seed(std::move(seed)),
            m_public_key_bits_raw(concat(m_polynomials.to_bytes<std::vector<uint8_t>>(), m_seed)),
-           m_H_public_key_bits_raw(m_mode.H()->process<std::vector<uint8_t>>(m_public_key_bits_raw))
+           m_H_public_key_bits_raw(unlock(m_mode.H()->process(m_public_key_bits_raw)))
          {
          }
 
diff --git a/src/lib/pubkey/pk_keys.h b/src/lib/pubkey/pk_keys.h
index 35e0fda06..a54966703 100644
--- a/src/lib/pubkey/pk_keys.h
+++ b/src/lib/pubkey/pk_keys.h
@@ -12,7 +12,6 @@
 #include <botan/asn1_obj.h>
 #include <botan/pk_ops_fwd.h>
 #include <string>
-#include <span>
 
 namespace Botan {
 
@@ -374,8 +373,9 @@ std::string BOTAN_PUBLIC_API(2,4)
    create_hex_fingerprint(const uint8_t bits[], size_t len,
                           const std::string& hash_name);
 
-inline std::string create_hex_fingerprint(std::span<const uint8_t> vec,
-                                          const std::string& hash_name)
+template<typename Alloc>
+std::string create_hex_fingerprint(const std::vector<uint8_t, Alloc>& vec,
+                                   const std::string& hash_name)
    {
    return create_hex_fingerprint(vec.data(), vec.size(), hash_name);
    }
diff --git a/src/lib/pubkey/pubkey.h b/src/lib/pubkey/pubkey.h
index c5770b3aa..ca48f4fd8 100644
--- a/src/lib/pubkey/pubkey.h
+++ b/src/lib/pubkey/pubkey.h
@@ -13,8 +13,6 @@
 #include <botan/symkey.h>
 #include <botan/asn1_obj.h>
 #include <string>
-#include <string_view>
-#include <span>
 
 namespace Botan {
 
@@ -47,8 +45,9 @@ class BOTAN_PUBLIC_API(2,0) PK_Encryptor
       * @param rng the random number source to use
       * @return encrypted message
       */
-      std::vector<uint8_t> encrypt(std::span<const uint8_t> in,
-                                   RandomNumberGenerator& rng) const
+      template<typename Alloc>
+      std::vector<uint8_t> encrypt(const std::vector<uint8_t, Alloc>& in,
+                                RandomNumberGenerator& rng) const
          {
          return enc(in.data(), in.size(), rng);
          }
@@ -99,7 +98,8 @@ class BOTAN_PUBLIC_API(2,0) PK_Decryptor
       * @param in the ciphertext
       * @return decrypted message
       */
-      secure_vector<uint8_t> decrypt(std::span<const uint8_t> in) const
+      template<typename Alloc>
+      secure_vector<uint8_t> decrypt(const std::vector<uint8_t, Alloc>& in) const
          {
          return decrypt(in.data(), in.size());
          }
@@ -208,8 +208,9 @@ class BOTAN_PUBLIC_API(2,0) PK_Signer final
       * @param rng the rng to use
       * @return signature
       */
-      std::vector<uint8_t> sign_message(std::span<const uint8_t> in,
-                                        RandomNumberGenerator& rng)
+      template<typename Alloc>
+         std::vector<uint8_t> sign_message(const std::vector<uint8_t, Alloc>& in,
+                                           RandomNumberGenerator& rng)
          {
          return sign_message(in.data(), in.size(), rng);
          }
@@ -231,7 +232,8 @@ class BOTAN_PUBLIC_API(2,0) PK_Signer final
       * Add a message part.
       * @param in the message part to add
       */
-      void update(std::span<const uint8_t> in)
+      template<typename Alloc>
+      void update(const std::vector<uint8_t, Alloc>& in)
          {
          update(in.data(), in.size());
          }
@@ -240,7 +242,7 @@ class BOTAN_PUBLIC_API(2,0) PK_Signer final
       * Add a message part.
       * @param in the message part to add
       */
-      void update(std::string_view in)
+      void update(const std::string& in)
          {
          update(cast_char_ptr_to_uint8(in.data()), in.size());
          }
@@ -345,8 +347,9 @@ class BOTAN_PUBLIC_API(2,0) PK_Verifier final
       * @param sig the signature
       * @return true if the signature is valid
       */
-      bool verify_message(std::span<const uint8_t> msg,
-                          std::span<const uint8_t> sig)
+      template<typename Alloc, typename Alloc2>
+      bool verify_message(const std::vector<uint8_t, Alloc>& msg,
+                          const std::vector<uint8_t, Alloc2>& sig)
          {
          return verify_message(msg.data(), msg.size(),
                                sig.data(), sig.size());
@@ -372,7 +375,8 @@ class BOTAN_PUBLIC_API(2,0) PK_Verifier final
       * signature to be verified.
       * @param in the new message part
       */
-      void update(std::span<const uint8_t> in)
+      template<typename Alloc>
+         void update(const std::vector<uint8_t, Alloc>& in)
          {
          update(in.data(), in.size());
          }
@@ -381,7 +385,7 @@ class BOTAN_PUBLIC_API(2,0) PK_Verifier final
       * Add a message part of the message corresponding to the
       * signature to be verified.
       */
-      void update(std::string_view in)
+      void update(const std::string& in)
          {
          update(cast_char_ptr_to_uint8(in.data()), in.size());
          }
@@ -401,7 +405,8 @@ class BOTAN_PUBLIC_API(2,0) PK_Verifier final
       * @param sig the signature to be verified
       * @return true if the signature is valid, false otherwise
       */
-      bool check_signature(std::span<const uint8_t> sig)
+      template<typename Alloc>
+      bool check_signature(const std::vector<uint8_t, Alloc>& sig)
          {
          return check_signature(sig.data(), sig.size());
          }
@@ -476,8 +481,9 @@ class BOTAN_PUBLIC_API(2,0) PK_Key_Agreement final
       * @param params_len the length of params in bytes
       */
       SymmetricKey derive_key(size_t key_len,
-                              std::span<const uint8_t> in,
-                              const uint8_t params[], size_t params_len) const
+                              const std::vector<uint8_t>& in,
+                              const uint8_t params[],
+                              size_t params_len) const
          {
          return derive_key(key_len, in.data(), in.size(),
                            params, params_len);
@@ -506,7 +512,7 @@ class BOTAN_PUBLIC_API(2,0) PK_Key_Agreement final
       * @param params extra derivation params
       */
       SymmetricKey derive_key(size_t key_len,
-                              const std::span<const uint8_t> in,
+                              const std::vector<uint8_t>& in,
                               const std::string& params = "") const
          {
          return derive_key(key_len, in.data(), in.size(),
@@ -683,11 +689,12 @@ class BOTAN_PUBLIC_API(2,0) PK_KEM_Encryptor final
       * @param rng the RNG to use
       * @param salt a salt value used in the KDF
       */
-      void encrypt(secure_vector<uint8_t>& out_encapsulated_key,
-                   secure_vector<uint8_t>& out_shared_key,
-                   size_t desired_shared_key_len,
-                   RandomNumberGenerator& rng,
-                   std::span<const uint8_t> salt)
+      template<typename Alloc>
+         void encrypt(secure_vector<uint8_t>& out_encapsulated_key,
+                      secure_vector<uint8_t>& out_shared_key,
+                      size_t desired_shared_key_len,
+                      RandomNumberGenerator& rng,
+                      const std::vector<uint8_t, Alloc>& salt)
          {
          this->encrypt(out_encapsulated_key,
                        out_shared_key,
@@ -799,9 +806,10 @@ class BOTAN_PUBLIC_API(2,0) PK_KEM_Decryptor final
       * @param salt a salt value used in the KDF
       * @return the shared data encryption key
       */
-      secure_vector<uint8_t> decrypt(std::span<const uint8_t> encap_key,
+      template<typename Alloc1, typename Alloc2>
+         secure_vector<uint8_t> decrypt(const std::vector<uint8_t, Alloc1>& encap_key,
                                      size_t desired_shared_key_len,
-                                     std::span<const uint8_t> salt)
+                                     const std::vector<uint8_t, Alloc2>& salt)
          {
          return this->decrypt(encap_key.data(), encap_key.size(),
                               desired_shared_key_len,
diff --git a/src/lib/pubkey/xmss/xmss.h b/src/lib/pubkey/xmss/xmss.h
index 655871359..ac1d19f93 100644
--- a/src/lib/pubkey/xmss/xmss.h
+++ b/src/lib/pubkey/xmss/xmss.h
@@ -158,24 +158,6 @@ template<typename> class Atomic;
 
 class XMSS_Index_Registry;
 
-/**
- * Determines how WOTS+ private keys are derived from the XMSS private key
- */
-enum class WOTS_Derivation_Method
-   {
-   /// This roughly followed the suggestions in RFC 8391 but is vulnerable
-   /// to a multi-target attack. For new private keys, we recommend using
-   /// the derivation as suggested in NIST SP.800-208.
-   /// Private keys generated with Botan 2.x will need to stay with this mode,
-   /// otherwise they won't be able to generate valid signatures any longer.
-   Botan2x,
-
-   /// Derivation as specified in NIST SP.800-208 to avoid a multi-target attack
-   /// on the WOTS+ key derivation suggested in RFC 8391. New private keys
-   /// should use this mode.
-   NIST_SP800_208,
-   };
-
 /**
  * An XMSS: Extended Hash-Based Signature private key.
  * The XMSS private key does not support the X509 and PKCS7 standard. Instead
@@ -198,21 +180,17 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PrivateKey final : public virtual XMSS_PublicKe
       *
       * @param xmss_algo_id Identifier for the selected XMSS signature method.
       * @param rng A random number generator to use for key generation.
-      * @param wots_derivation_method The method used to derive WOTS+ private keys
       **/
       XMSS_PrivateKey(XMSS_Parameters::xmss_algorithm_t xmss_algo_id,
-                      RandomNumberGenerator& rng,
-                      WOTS_Derivation_Method wots_derivation_method = WOTS_Derivation_Method::NIST_SP800_208);
+                      RandomNumberGenerator& rng);
 
       /**
        * Creates an XMSS_PrivateKey from a byte sequence produced by
        * raw_private_key().
        *
        * @param raw_key An XMSS private key serialized using raw_private_key().
-       * @param wots_derivation_method The method used to derive WOTS+ private keys
        **/
-      XMSS_PrivateKey(std::span<const uint8_t> raw_key,
-                      WOTS_Derivation_Method wots_derivation_method = WOTS_Derivation_Method::NIST_SP800_208);
+      XMSS_PrivateKey(std::span<const uint8_t> raw_key);
 
       /**
        * Creates a new XMSS private key for the chosen XMSS signature method
@@ -228,15 +206,13 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PrivateKey final : public virtual XMSS_PublicKe
        *        of uniformly random data.
        * @param root Root node of the binary hash tree.
        * @param public_seed The public seed.
-       * @param wots_derivation_method The method used to derive WOTS+ private keys
        **/
       XMSS_PrivateKey(XMSS_Parameters::xmss_algorithm_t xmss_algo_id,
                       size_t idx_leaf,
                       secure_vector<uint8_t> wots_priv_seed,
                       secure_vector<uint8_t> prf,
                       secure_vector<uint8_t> root,
-                      secure_vector<uint8_t> public_seed,
-                      WOTS_Derivation_Method wots_derivation_method = WOTS_Derivation_Method::NIST_SP800_208);
+                      secure_vector<uint8_t> public_seed);
 
       bool stateful_operation() const override { return true; }
 
@@ -274,8 +250,6 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PrivateKey final : public virtual XMSS_PublicKe
        **/
       secure_vector<uint8_t> raw_private_key() const;
 
-      WOTS_Derivation_Method wots_derivation_method() const { return m_wots_derivation_method; }
-
    private:
       friend class XMSS_Signature_Operation;
 
@@ -319,8 +293,6 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PrivateKey final : public virtual XMSS_PublicKe
                              XMSS_Hash& hash);
 
       std::shared_ptr<XMSS_PrivateKey_Internal> m_private;
-
-      WOTS_Derivation_Method m_wots_derivation_method;
    };
 
 }
diff --git a/src/lib/pubkey/xmss/xmss_privatekey.cpp b/src/lib/pubkey/xmss/xmss_privatekey.cpp
index b81b9c2c6..a4ff7f554 100644
--- a/src/lib/pubkey/xmss/xmss_privatekey.cpp
+++ b/src/lib/pubkey/xmss/xmss_privatekey.cpp
@@ -210,19 +210,17 @@ class XMSS_PrivateKey_Internal
       XMSS_Index_Registry& m_index_reg;
    };
 
-XMSS_PrivateKey::XMSS_PrivateKey(std::span<const uint8_t> key_bits,
-                                 WOTS_Derivation_Method wots_derivation_method)
+
+XMSS_PrivateKey::XMSS_PrivateKey(std::span<const uint8_t> key_bits)
    : XMSS_PublicKey(key_bits)
    , m_private(std::make_shared<XMSS_PrivateKey_Internal>(
-         m_xmss_params, m_wots_params, key_bits))
-   , m_wots_derivation_method(wots_derivation_method) {}
+         m_xmss_params, m_wots_params, key_bits)) {}
 
-XMSS_PrivateKey::XMSS_PrivateKey(XMSS_Parameters::xmss_algorithm_t xmss_algo_id,
-                                 RandomNumberGenerator& rng,
-                                 WOTS_Derivation_Method wots_derivation_method)
+XMSS_PrivateKey::XMSS_PrivateKey(
+   XMSS_Parameters::xmss_algorithm_t xmss_algo_id,
+   RandomNumberGenerator& rng)
    : XMSS_PublicKey(xmss_algo_id, rng)
    , m_private(std::make_shared<XMSS_PrivateKey_Internal>(m_xmss_params, m_wots_params, rng))
-   , m_wots_derivation_method(wots_derivation_method)
    {
    XMSS_Address adrs;
    m_root = tree_hash(0,
@@ -235,13 +233,11 @@ XMSS_PrivateKey::XMSS_PrivateKey(XMSS_Parameters::xmss_algorithm_t xmss_algo_id,
                                  secure_vector<uint8_t> wots_priv_seed,
                                  secure_vector<uint8_t> prf,
                                  secure_vector<uint8_t> root,
-                                 secure_vector<uint8_t> public_seed,
-                                 WOTS_Derivation_Method wots_derivation_method)
+                                 secure_vector<uint8_t> public_seed)
    : XMSS_PublicKey(xmss_algo_id, std::move(root), std::move(public_seed))
    , m_private(std::make_shared<XMSS_PrivateKey_Internal>(
       m_xmss_params, m_wots_params,
       std::move(wots_priv_seed), std::move(prf)))
-   , m_wots_derivation_method(wots_derivation_method)
    {
    m_private->set_unused_leaf_index(idx_leaf);
    }
@@ -441,15 +437,7 @@ XMSS_WOTS_PublicKey XMSS_PrivateKey::wots_public_key_for(XMSS_Address& adrs, XMS
 
 XMSS_WOTS_PrivateKey XMSS_PrivateKey::wots_private_key_for(XMSS_Address& adrs, XMSS_Hash& hash) const
    {
-   switch(wots_derivation_method())
-      {
-      case WOTS_Derivation_Method::NIST_SP800_208:
-         return XMSS_WOTS_PrivateKey(m_private->wots_parameters(), m_public_seed, m_private->private_seed(), adrs, hash);
-      case WOTS_Derivation_Method::Botan2x:
-         return XMSS_WOTS_PrivateKey(m_private->wots_parameters(), m_private->private_seed(), adrs, hash);
-      }
-
-      throw Invalid_State("WOTS derivation method is out of the enum's range");
+   return XMSS_WOTS_PrivateKey(m_private->wots_parameters(), m_public_seed, m_private->private_seed(), adrs, hash);
    }
 
 secure_vector<uint8_t> XMSS_PrivateKey::private_key_bits() const
diff --git a/src/lib/pubkey/xmss/xmss_wots.cpp b/src/lib/pubkey/xmss/xmss_wots.cpp
index e5946bd34..77c6d92ef 100644
--- a/src/lib/pubkey/xmss/xmss_wots.cpp
+++ b/src/lib/pubkey/xmss/xmss_wots.cpp
@@ -159,24 +159,4 @@ XMSS_WOTS_PrivateKey::XMSS_WOTS_PrivateKey(XMSS_WOTS_Parameters params,
       }
    }
 
-
-// Constructor for legacy XMSS_PrivateKeys
-XMSS_WOTS_PrivateKey::XMSS_WOTS_PrivateKey(XMSS_WOTS_Parameters params,
-                                           std::span<const uint8_t> private_seed,
-                                           XMSS_Address adrs,
-                                           XMSS_Hash& hash)
-   : XMSS_WOTS_Base(std::move(params))
-   {
-   m_key_data.resize(m_params.len());
-
-   secure_vector<uint8_t> r;
-   hash.prf(r, private_seed, adrs.bytes());
-
-   for(size_t i = 0; i < m_params.len(); ++i)
-      {
-      XMSS_Tools::concat<size_t>(m_key_data[i], i, 32);
-      hash.prf(m_key_data[i], r, m_key_data[i]);
-      }
-   }
-
 }
diff --git a/src/lib/pubkey/xmss/xmss_wots.h b/src/lib/pubkey/xmss/xmss_wots.h
index cc7c8a99a..9c2267d36 100644
--- a/src/lib/pubkey/xmss/xmss_wots.h
+++ b/src/lib/pubkey/xmss/xmss_wots.h
@@ -127,24 +127,6 @@ class XMSS_WOTS_PrivateKey : public XMSS_WOTS_Base
                            XMSS_Address adrs,
                            XMSS_Hash& hash);
 
-      /**
-       * Constructor for the old derivation logic.
-       * Creates a WOTS+ private key using the old key derivation logic, i.e.
-       * the logic WITHOUT the recommendations in NIST SP.800-208. It is used
-       * to support XMSS_PrivateKeys created before the derivation logic was
-       * updated.
-       *
-       * @param params       The WOTS parameters to use
-       * @param private_seed The private seed for the private key generation
-       * @param adrs         The address of the key to retrieve.
-       * @param hash         Instance of XMSS_Hash, that may only be used by the
-       *                     thread executing it.
-       **/
-      XMSS_WOTS_PrivateKey(XMSS_WOTS_Parameters params,
-                           std::span<const uint8_t> private_seed,
-                           XMSS_Address adrs,
-                           XMSS_Hash& hash);
-
       /**
        * Algorithm 5: "WOTS_sign"
        * Generates a signature from a private key and a message.
@@ -166,7 +148,6 @@ class XMSS_WOTS_PrivateKey : public XMSS_WOTS_Base
                          std::span<const uint8_t> public_seed,
                          XMSS_Address& adrs,
                          XMSS_Hash& hash);
-
    };
 
 }
diff --git a/src/lib/rng/rng.h b/src/lib/rng/rng.h
index cb9315af0..39c3cc60f 100644
--- a/src/lib/rng/rng.h
+++ b/src/lib/rng/rng.h
@@ -181,7 +181,10 @@ class BOTAN_PUBLIC_API(2,0) RandomNumberGenerator
       * @param  bytes number of random bytes to initialize the container with
       * @throws Exception if RNG or memory allocation fails
       */
-      template<concepts::resizable_byte_buffer T>
+      template<typename T>
+      requires(concepts::contiguous_container<T> &&
+               concepts::resizable_container<T> &&
+               std::same_as<typename T::value_type, uint8_t>)
       void random_vec(T& v, size_t bytes)
          {
          v.resize(bytes);
@@ -196,8 +199,11 @@ class BOTAN_PUBLIC_API(2,0) RandomNumberGenerator
       * @return       a container of type T with @p bytes random bytes
       * @throws Exception if RNG or memory allocation fails
       */
-      template<concepts::resizable_byte_buffer T = secure_vector<uint8_t>>
-      requires concepts::default_initializable<T>
+      template<typename T = secure_vector<uint8_t>>
+      requires(concepts::contiguous_container<T> &&
+               concepts::resizable_container<T> &&
+               concepts::default_initializable<T> &&
+               std::same_as<typename T::value_type, uint8_t>)
       T random_vec(size_t bytes)
          {
          T result;
diff --git a/src/lib/stream/chacha/chacha.cpp b/src/lib/stream/chacha/chacha.cpp
index 3059dc3f2..bc17353ad 100644
--- a/src/lib/stream/chacha/chacha.cpp
+++ b/src/lib/stream/chacha/chacha.cpp
@@ -191,7 +191,7 @@ void ChaCha::chacha(uint8_t output[],
 /*
 * Combine cipher stream with message
 */
-void ChaCha::cipher_bytes(const uint8_t in[], uint8_t out[], size_t length)
+void ChaCha::cipher(const uint8_t in[], uint8_t out[], size_t length)
    {
    assert_key_material_set();
 
@@ -214,7 +214,7 @@ void ChaCha::cipher_bytes(const uint8_t in[], uint8_t out[], size_t length)
    m_position += length;
    }
 
-void ChaCha::generate_keystream(uint8_t out[], size_t length)
+void ChaCha::write_keystream(uint8_t out[], size_t length)
    {
    assert_key_material_set();
 
@@ -325,7 +325,7 @@ bool ChaCha::valid_iv_length(size_t iv_len) const
    return (iv_len == 0 || iv_len == 8 || iv_len == 12 || iv_len == 24);
    }
 
-void ChaCha::set_iv_bytes(const uint8_t iv[], size_t length)
+void ChaCha::set_iv(const uint8_t iv[], size_t length)
    {
    assert_key_material_set();
 
diff --git a/src/lib/stream/chacha/chacha.h b/src/lib/stream/chacha/chacha.h
index c44423e99..019ecc0ba 100644
--- a/src/lib/stream/chacha/chacha.h
+++ b/src/lib/stream/chacha/chacha.h
@@ -27,6 +27,12 @@ class ChaCha final : public StreamCipher
 
       std::string provider() const override;
 
+      void cipher(const uint8_t in[], uint8_t out[], size_t length) override;
+
+      void write_keystream(uint8_t out[], size_t len) override;
+
+      void set_iv(const uint8_t iv[], size_t iv_len) override;
+
       /*
       * ChaCha accepts 0, 8, 12 or 24 byte IVs.
       * The default IV is a 8 zero bytes.
@@ -51,12 +57,6 @@ class ChaCha final : public StreamCipher
    private:
       void key_schedule(const uint8_t key[], size_t key_len) override;
 
-      void cipher_bytes(const uint8_t in[], uint8_t out[], size_t length) override;
-
-      void generate_keystream(uint8_t out[], size_t len) override;
-
-      void set_iv_bytes(const uint8_t iv[], size_t iv_len) override;
-
       void initialize_state();
 
       static size_t parallelism();
diff --git a/src/lib/stream/ctr/ctr.cpp b/src/lib/stream/ctr/ctr.cpp
index e261de936..71f76debd 100644
--- a/src/lib/stream/ctr/ctr.cpp
+++ b/src/lib/stream/ctr/ctr.cpp
@@ -87,7 +87,7 @@ std::string CTR_BE::name() const
 
    }
 
-void CTR_BE::cipher_bytes(const uint8_t in[], uint8_t out[], size_t length)
+void CTR_BE::cipher(const uint8_t in[], uint8_t out[], size_t length)
    {
    assert_key_material_set();
 
@@ -127,7 +127,7 @@ void CTR_BE::cipher_bytes(const uint8_t in[], uint8_t out[], size_t length)
    m_pad_pos += length;
    }
 
-void CTR_BE::generate_keystream(uint8_t out[], size_t length)
+void CTR_BE::write_keystream(uint8_t out[], size_t length)
    {
    assert_key_material_set();
 
@@ -159,7 +159,7 @@ void CTR_BE::generate_keystream(uint8_t out[], size_t length)
    BOTAN_ASSERT_NOMSG(m_pad_pos < m_pad.size());
    }
 
-void CTR_BE::set_iv_bytes(const uint8_t iv[], size_t iv_len)
+void CTR_BE::set_iv(const uint8_t iv[], size_t iv_len)
    {
    if(!valid_iv_length(iv_len))
       throw Invalid_IV_Length(name(), iv_len);
diff --git a/src/lib/stream/ctr/ctr.h b/src/lib/stream/ctr/ctr.h
index 49692c51a..1428e4520 100644
--- a/src/lib/stream/ctr/ctr.h
+++ b/src/lib/stream/ctr/ctr.h
@@ -19,6 +19,12 @@ namespace Botan {
 class CTR_BE final : public StreamCipher
    {
    public:
+      void cipher(const uint8_t in[], uint8_t out[], size_t length) override;
+
+      void write_keystream(uint8_t out[], size_t length) override;
+
+      void set_iv(const uint8_t iv[], size_t iv_len) override;
+
       size_t default_iv_length() const override;
 
       bool valid_iv_length(size_t iv_len) const override;
@@ -43,9 +49,6 @@ class CTR_BE final : public StreamCipher
       void seek(uint64_t offset) override;
    private:
       void key_schedule(const uint8_t key[], size_t key_len) override;
-      void cipher_bytes(const uint8_t in[], uint8_t out[], size_t length) override;
-      void generate_keystream(uint8_t out[], size_t length) override;
-      void set_iv_bytes(const uint8_t iv[], size_t iv_len) override;
       void add_counter(const uint64_t counter);
 
       std::unique_ptr<BlockCipher> m_cipher;
diff --git a/src/lib/stream/ofb/ofb.cpp b/src/lib/stream/ofb/ofb.cpp
index f728d99c2..24b578c81 100644
--- a/src/lib/stream/ofb/ofb.cpp
+++ b/src/lib/stream/ofb/ofb.cpp
@@ -62,7 +62,7 @@ std::unique_ptr<StreamCipher> OFB::new_object() const
    return std::make_unique<OFB>(m_cipher->new_object());
    }
 
-void OFB::cipher_bytes(const uint8_t in[], uint8_t out[], size_t length)
+void OFB::cipher(const uint8_t in[], uint8_t out[], size_t length)
    {
    while(length >= m_buffer.size() - m_buf_pos)
       {
@@ -77,7 +77,7 @@ void OFB::cipher_bytes(const uint8_t in[], uint8_t out[], size_t length)
    m_buf_pos += length;
    }
 
-void OFB::set_iv_bytes(const uint8_t iv[], size_t iv_len)
+void OFB::set_iv(const uint8_t iv[], size_t iv_len)
    {
    if(!valid_iv_length(iv_len))
       throw Invalid_IV_Length(name(), iv_len);
diff --git a/src/lib/stream/ofb/ofb.h b/src/lib/stream/ofb/ofb.h
index 6d9f7b50e..01f4941e6 100644
--- a/src/lib/stream/ofb/ofb.h
+++ b/src/lib/stream/ofb/ofb.h
@@ -19,6 +19,10 @@ namespace Botan {
 class OFB final : public StreamCipher
    {
    public:
+      void cipher(const uint8_t in[], uint8_t out[], size_t length) override;
+
+      void set_iv(const uint8_t iv[], size_t iv_len) override;
+
       size_t default_iv_length() const override;
 
       bool valid_iv_length(size_t iv_len) const override;
@@ -41,8 +45,6 @@ class OFB final : public StreamCipher
       void seek(uint64_t offset) override;
    private:
       void key_schedule(const uint8_t key[], size_t key_len) override;
-      void cipher_bytes(const uint8_t in[], uint8_t out[], size_t length) override;
-      void set_iv_bytes(const uint8_t iv[], size_t iv_len) override;
 
       std::unique_ptr<BlockCipher> m_cipher;
       secure_vector<uint8_t> m_buffer;
diff --git a/src/lib/stream/rc4/rc4.cpp b/src/lib/stream/rc4/rc4.cpp
index 60926a87a..c11eacc7f 100644
--- a/src/lib/stream/rc4/rc4.cpp
+++ b/src/lib/stream/rc4/rc4.cpp
@@ -13,7 +13,7 @@ namespace Botan {
 /*
 * Combine cipher stream with message
 */
-void RC4::cipher_bytes(const uint8_t in[], uint8_t out[], size_t length)
+void RC4::cipher(const uint8_t in[], uint8_t out[], size_t length)
    {
    assert_key_material_set();
 
@@ -39,7 +39,7 @@ Key_Length_Specification RC4::key_spec() const
    return Key_Length_Specification(1, 256);
    }
 
-void RC4::set_iv_bytes(const uint8_t* /*iv*/, size_t length)
+void RC4::set_iv(const uint8_t* /*iv*/, size_t length)
    {
    if(length > 0)
       throw Invalid_IV_Length("RC4", length);
diff --git a/src/lib/stream/rc4/rc4.h b/src/lib/stream/rc4/rc4.h
index a38bc9128..f16b8f456 100644
--- a/src/lib/stream/rc4/rc4.h
+++ b/src/lib/stream/rc4/rc4.h
@@ -19,6 +19,10 @@ namespace Botan {
 class RC4 final : public StreamCipher
    {
    public:
+      void cipher(const uint8_t in[], uint8_t out[], size_t length) override;
+
+      void set_iv(const uint8_t iv[], size_t iv_len) override;
+
       void clear() override;
       std::string name() const override;
 
@@ -39,8 +43,6 @@ class RC4 final : public StreamCipher
 
    private:
       void key_schedule(const uint8_t[], size_t) override;
-      void cipher_bytes(const uint8_t in[], uint8_t out[], size_t length) override;
-      void set_iv_bytes(const uint8_t iv[], size_t iv_len) override;
       void generate();
 
       const size_t m_SKIP;
diff --git a/src/lib/stream/salsa20/salsa20.cpp b/src/lib/stream/salsa20/salsa20.cpp
index 0bda7843e..2b8e63cd9 100644
--- a/src/lib/stream/salsa20/salsa20.cpp
+++ b/src/lib/stream/salsa20/salsa20.cpp
@@ -108,7 +108,7 @@ void Salsa20::salsa_core(uint8_t output[64], const uint32_t input[16], size_t ro
 /*
 * Combine cipher stream with message
 */
-void Salsa20::cipher_bytes(const uint8_t in[], uint8_t out[], size_t length)
+void Salsa20::cipher(const uint8_t in[], uint8_t out[], size_t length)
    {
    assert_key_material_set();
 
@@ -200,7 +200,7 @@ void Salsa20::key_schedule(const uint8_t key[], size_t length)
 /*
 * Set the Salsa IV
 */
-void Salsa20::set_iv_bytes(const uint8_t iv[], size_t length)
+void Salsa20::set_iv(const uint8_t iv[], size_t length)
    {
    assert_key_material_set();
 
diff --git a/src/lib/stream/salsa20/salsa20.h b/src/lib/stream/salsa20/salsa20.h
index 2347094dd..90cd92b10 100644
--- a/src/lib/stream/salsa20/salsa20.h
+++ b/src/lib/stream/salsa20/salsa20.h
@@ -18,6 +18,8 @@ namespace Botan {
 class Salsa20 final : public StreamCipher
    {
    public:
+      void cipher(const uint8_t in[], uint8_t out[], size_t length) override;
+      void set_iv(const uint8_t iv[], size_t iv_len) override;
       bool valid_iv_length(size_t iv_len) const override;
       size_t default_iv_length() const override;
       Key_Length_Specification key_spec() const override;
@@ -32,11 +34,6 @@ class Salsa20 final : public StreamCipher
 
       // For internal use only
       static void hsalsa20(uint32_t output[8], const uint32_t input[16]);
-
-   protected:
-      void cipher_bytes(const uint8_t in[], uint8_t out[], size_t length) override;
-      void set_iv_bytes(const uint8_t iv[], size_t iv_len) override;
-
    private:
       void key_schedule(const uint8_t key[], size_t key_len) override;
 
diff --git a/src/lib/stream/shake_cipher/shake_cipher.cpp b/src/lib/stream/shake_cipher/shake_cipher.cpp
index e693965c6..9ad388acc 100644
--- a/src/lib/stream/shake_cipher/shake_cipher.cpp
+++ b/src/lib/stream/shake_cipher/shake_cipher.cpp
@@ -27,7 +27,7 @@ void SHAKE_Cipher::clear()
    m_buf_pos = 0;
    }
 
-void SHAKE_Cipher::set_iv_bytes(const uint8_t /*iv*/[], size_t length)
+void SHAKE_Cipher::set_iv(const uint8_t /*iv*/[], size_t length)
    {
    /*
    * This could be supported in some way (say, by treating iv as
@@ -42,7 +42,7 @@ void SHAKE_Cipher::seek(uint64_t /*offset*/)
    throw Not_Implemented("SHAKE_Cipher::seek");
    }
 
-void SHAKE_Cipher::cipher_bytes(const uint8_t in[], uint8_t out[], size_t length)
+void SHAKE_Cipher::cipher(const uint8_t in[], uint8_t out[], size_t length)
    {
    assert_key_material_set();
 
@@ -62,7 +62,7 @@ void SHAKE_Cipher::cipher_bytes(const uint8_t in[], uint8_t out[], size_t length
    m_buf_pos += length;
    }
 
-void SHAKE_Cipher::generate_keystream(uint8_t out[], size_t length)
+void SHAKE_Cipher::write_keystream(uint8_t out[], size_t length)
    {
    assert_key_material_set();
 
diff --git a/src/lib/stream/shake_cipher/shake_cipher.h b/src/lib/stream/shake_cipher/shake_cipher.h
index a829e374a..21fdd601c 100644
--- a/src/lib/stream/shake_cipher/shake_cipher.h
+++ b/src/lib/stream/shake_cipher/shake_cipher.h
@@ -23,11 +23,22 @@ class SHAKE_Cipher : public StreamCipher
       explicit SHAKE_Cipher(size_t shake_rate);
 
    public:
+      /**
+      * Produce more XOF output
+      */
+      void cipher(const uint8_t in[], uint8_t out[], size_t length) override final;
+
+      void write_keystream(uint8_t out[], size_t length) override;
+
       /**
       * Seeking is not supported, this function will throw
       */
       void seek(uint64_t offset) override final;
 
+      /**
+      * IV not supported, this function will throw unless iv_len == 0
+      */
+      void set_iv(const uint8_t iv[], size_t iv_len) override final;
 
       void clear() override final;
 
@@ -37,16 +48,6 @@ class SHAKE_Cipher : public StreamCipher
 
    private:
       void key_schedule(const uint8_t key[], size_t key_len) override final;
-      /**
-      * Produce more XOF output
-      */
-      void cipher_bytes(const uint8_t in[], uint8_t out[], size_t length) override final;
-      void generate_keystream(uint8_t out[], size_t length) override;
-
-      /**
-      * IV not supported, this function will throw unless iv_len == 0
-      */
-      void set_iv_bytes(const uint8_t iv[], size_t iv_len) override final;
 
    protected:
       size_t m_shake_rate;
diff --git a/src/lib/stream/stream_cipher.h b/src/lib/stream/stream_cipher.h
index bab2d5d65..0b1ad62a2 100644
--- a/src/lib/stream/stream_cipher.h
+++ b/src/lib/stream/stream_cipher.h
@@ -8,7 +8,6 @@
 #ifndef BOTAN_STREAM_CIPHER_H_
 #define BOTAN_STREAM_CIPHER_H_
 
-#include <botan/concepts.h>
 #include <botan/sym_algo.h>
 #include <string>
 #include <memory>
@@ -57,21 +56,7 @@ class BOTAN_PUBLIC_API(2,0) StreamCipher : public SymmetricAlgorithm
       * @param out the byte array to hold the output, i.e. the ciphertext
       * @param len the length of both in and out in bytes
       */
-      void cipher(const uint8_t in[], uint8_t out[], size_t len)
-         { cipher_bytes(in, out, len); }
-
-      /**
-      * Encrypt or decrypt a message
-      * @param in the plaintext
-      * @param out the byte array to hold the output, i.e. the ciphertext
-      *            with at least the same size as @p in
-      */
-      void cipher(std::span<const uint8_t> in, std::span<uint8_t> out)
-         {
-         BOTAN_ARG_CHECK(in.size() <= out.size(),
-                         "Output buffer of stream cipher must be at least as long as input buffer");
-         cipher_bytes(in.data(), out.data(), in.size());
-         }
+      virtual void cipher(const uint8_t in[], uint8_t out[], size_t len) = 0;
 
       /**
       * Write keystream bytes to a buffer
@@ -81,30 +66,10 @@ class BOTAN_PUBLIC_API(2,0) StreamCipher : public SymmetricAlgorithm
       * @param out the byte array to hold the keystream
       * @param len the length of out in bytes
       */
-      void write_keystream(uint8_t out[], size_t len)
-         { generate_keystream(out, len); }
-
-      /**
-      * Fill a given buffer with keystream bytes
-      *
-      * The contents of @p out are ignored/overwritten
-      *
-      * @param out the byte array to hold the keystream
-      */
-      void write_keystream(std::span<uint8_t> out)
-         { generate_keystream(out.data(), out.size()); }
-
-      /**
-      * Get @p bytes from the keystream
-      *
-      * @param bytes The number of bytes to be produced
-      */
-      template<concepts::resizable_byte_buffer T = secure_vector<uint8_t>>
-      T keystream_bytes(size_t bytes)
+      virtual void write_keystream(uint8_t out[], size_t len)
          {
-         T out(bytes);
-         write_keystream(out);
-         return out;
+         clear_mem(out, len);
+         cipher1(out, len);
          }
 
       /**
@@ -116,20 +81,13 @@ class BOTAN_PUBLIC_API(2,0) StreamCipher : public SymmetricAlgorithm
       void cipher1(uint8_t buf[], size_t len)
          { cipher(buf, buf, len); }
 
-      /**
-      * Encrypt or decrypt a message
-      * The message is encrypted/decrypted in place.
-      * @param buf the plaintext / ciphertext
-      */
-      void cipher1(std::span<uint8_t> buf)
-         { cipher(buf, buf); }
-
       /**
       * Encrypt a message
       * The message is encrypted/decrypted in place.
       * @param inout the plaintext / ciphertext
       */
-      void encipher(std::span<uint8_t> inout)
+      template<typename Alloc>
+         void encipher(std::vector<uint8_t, Alloc>& inout)
          { cipher(inout.data(), inout.data(), inout.size()); }
 
       /**
@@ -137,7 +95,8 @@ class BOTAN_PUBLIC_API(2,0) StreamCipher : public SymmetricAlgorithm
       * The message is encrypted in place.
       * @param inout the plaintext / ciphertext
       */
-      void encrypt(std::span<uint8_t> inout)
+      template<typename Alloc>
+         void encrypt(std::vector<uint8_t, Alloc>& inout)
          { cipher(inout.data(), inout.data(), inout.size()); }
 
       /**
@@ -145,7 +104,8 @@ class BOTAN_PUBLIC_API(2,0) StreamCipher : public SymmetricAlgorithm
       * The message is decrypted in place.
       * @param inout the plaintext / ciphertext
       */
-      void decrypt(std::span<uint8_t> inout)
+      template<typename Alloc>
+         void decrypt(std::vector<uint8_t, Alloc>& inout)
          { cipher(inout.data(), inout.data(), inout.size()); }
 
       /**
@@ -153,15 +113,7 @@ class BOTAN_PUBLIC_API(2,0) StreamCipher : public SymmetricAlgorithm
       * @param iv the initialization vector
       * @param iv_len the length of the IV in bytes
       */
-      void set_iv(const uint8_t iv[], size_t iv_len)
-         { set_iv_bytes(iv, iv_len); }
-
-      /**
-      * Resync the cipher using the IV
-      * @param iv the initialization vector
-      */
-      void set_iv(std::span<const uint8_t> iv)
-         { set_iv_bytes(iv.data(), iv.size()); }
+      virtual void set_iv(const uint8_t iv[], size_t iv_len) = 0;
 
       /**
       * Return the default (preferred) nonce length
@@ -199,26 +151,6 @@ class BOTAN_PUBLIC_API(2,0) StreamCipher : public SymmetricAlgorithm
       * might also return "sse2", "avx2" or some other arbitrary string.
       */
       virtual std::string provider() const { return "base"; }
-
-   protected:
-      /**
-      * Encrypt or decrypt a message
-      */
-      virtual void cipher_bytes(const uint8_t in[], uint8_t out[], size_t len) = 0;
-
-      /**
-      * Write keystream bytes to a buffer
-      */
-      virtual void generate_keystream(uint8_t out[], size_t len)
-         {
-         clear_mem(out, len);
-         cipher1(out, len);
-         }
-
-      /**
-      * Resync the cipher using the IV
-      */
-      virtual void set_iv_bytes(const uint8_t iv[], size_t iv_len) = 0;
    };
 
 }
diff --git a/src/lib/tls/tls12/tls_cbc/tls_cbc.cpp b/src/lib/tls/tls12/tls_cbc/tls_cbc.cpp
index 74e16ca49..8dceb79b4 100644
--- a/src/lib/tls/tls12/tls_cbc/tls_cbc.cpp
+++ b/src/lib/tls/tls12/tls_cbc/tls_cbc.cpp
@@ -123,7 +123,7 @@ void TLS_CBC_HMAC_AEAD_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
       }
    }
 
-size_t TLS_CBC_HMAC_AEAD_Mode::process_msg(uint8_t buf[], size_t sz)
+size_t TLS_CBC_HMAC_AEAD_Mode::process(uint8_t buf[], size_t sz)
    {
    m_msg.insert(m_msg.end(), buf, buf + sz);
    return 0;
@@ -198,7 +198,7 @@ size_t TLS_CBC_HMAC_AEAD_Encryption::output_length(size_t input_length) const
       (use_encrypt_then_mac() ? tag_size() : 0);
    }
 
-void TLS_CBC_HMAC_AEAD_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
+void TLS_CBC_HMAC_AEAD_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    {
    update(buffer, offset);
 
@@ -377,7 +377,7 @@ void TLS_CBC_HMAC_AEAD_Decryption::perform_additional_compressions(size_t plen,
    // we do not need to clear the MAC since the connection is broken anyway
    }
 
-void TLS_CBC_HMAC_AEAD_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
+void TLS_CBC_HMAC_AEAD_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    {
    update(buffer, offset);
    buffer.resize(offset);
diff --git a/src/lib/tls/tls12/tls_cbc/tls_cbc.h b/src/lib/tls/tls12/tls_cbc/tls_cbc.h
index 4c44916b0..8b500b12a 100644
--- a/src/lib/tls/tls12/tls_cbc/tls_cbc.h
+++ b/src/lib/tls/tls12/tls_cbc/tls_cbc.h
@@ -25,6 +25,8 @@ namespace TLS {
 class BOTAN_TEST_API TLS_CBC_HMAC_AEAD_Mode : public AEAD_Mode
    {
    public:
+      size_t process(uint8_t buf[], size_t sz) override final;
+
       std::string name() const override final;
 
       void set_associated_data(const uint8_t ad[], size_t ad_len) override;
@@ -80,7 +82,6 @@ class BOTAN_TEST_API TLS_CBC_HMAC_AEAD_Mode : public AEAD_Mode
 
    private:
       void start_msg(const uint8_t nonce[], size_t nonce_len) override final;
-      size_t process_msg(uint8_t buf[], size_t sz) override final;
 
       void key_schedule(const uint8_t key[], size_t length) override final;
 
@@ -132,8 +133,8 @@ class BOTAN_TEST_API TLS_CBC_HMAC_AEAD_Encryption final : public TLS_CBC_HMAC_AE
 
       size_t minimum_final_size() const override { return 0; }
 
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
    private:
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
       void cbc_encrypt_record(secure_vector<uint8_t>& buffer, size_t offset,
                               size_t padding_length);
    };
@@ -165,9 +166,9 @@ class BOTAN_TEST_API TLS_CBC_HMAC_AEAD_Decryption final : public TLS_CBC_HMAC_AE
 
       size_t minimum_final_size() const override { return tag_size(); }
 
-   private:
-      void finish_msg(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+      void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
 
+   private:
       void cbc_decrypt_record(uint8_t record_contents[], size_t record_len);
 
       void perform_additional_compressions(size_t plen, size_t padlen);
diff --git a/src/lib/utils/concepts.h b/src/lib/utils/concepts.h
index bdbaa917e..dd172fa0e 100644
--- a/src/lib/utils/concepts.h
+++ b/src/lib/utils/concepts.h
@@ -110,12 +110,6 @@ concept resizable_container =
       c.resize(s);
       };
 
-template <typename T>
-concept resizable_byte_buffer =
-   contiguous_container<T> &&
-   resizable_container<T> &&
-   std::same_as<typename T::value_type, uint8_t>;
-
 template<typename T>
 concept streamable = requires(std::ostream& os, T a)
    { os << a; };
diff --git a/src/lib/utils/cpuid/cpuid.cpp b/src/lib/utils/cpuid/cpuid.cpp
index edc604545..3d99c6bd8 100644
--- a/src/lib/utils/cpuid/cpuid.cpp
+++ b/src/lib/utils/cpuid/cpuid.cpp
@@ -46,6 +46,8 @@ std::string CPUID::to_string()
 
    CPUID_PRINT(sse2);
    CPUID_PRINT(ssse3);
+   CPUID_PRINT(sse41);
+   CPUID_PRINT(sse42);
    CPUID_PRINT(avx2);
 
    CPUID_PRINT(bmi2);
@@ -91,24 +93,47 @@ void CPUID::initialize()
    state() = CPUID_Data();
    }
 
-namespace {
+CPUID::CPUID_Data::CPUID_Data()
+   {
+   m_cache_line_size = 0;
+   m_processor_features = 0;
+
+#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) || \
+    defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) || \
+    defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+
+   m_processor_features = detect_cpu_features(&m_cache_line_size);
+
+#endif
+
+   m_processor_features |= CPUID::CPUID_INITIALIZED_BIT;
 
-// Returns true if big-endian
-bool runtime_check_if_big_endian()
+   if(m_cache_line_size == 0)
+      {
+      m_cache_line_size = OS::get_cache_line_size();
+      if(m_cache_line_size == 0)
+         m_cache_line_size = BOTAN_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE;
+      }
+
+   m_endian_status = runtime_check_endian();
+   }
+
+//static
+CPUID::Endian_Status CPUID::CPUID_Data::runtime_check_endian()
    {
    // Check runtime endian
    const uint32_t endian32 = 0x01234567;
    const uint8_t* e8 = reinterpret_cast<const uint8_t*>(&endian32);
 
-   bool is_big_endian = false;
+   CPUID::Endian_Status endian = CPUID::Endian_Status::Unknown;
 
    if(e8[0] == 0x01 && e8[1] == 0x23 && e8[2] == 0x45 && e8[3] == 0x67)
       {
-      is_big_endian = true;
+      endian = CPUID::Endian_Status::Big;
       }
    else if(e8[0] == 0x67 && e8[1] == 0x45 && e8[2] == 0x23 && e8[3] == 0x01)
       {
-      is_big_endian = false;
+      endian = CPUID::Endian_Status::Little;
       }
    else
       {
@@ -117,32 +142,12 @@ bool runtime_check_if_big_endian()
 
    // If we were compiled with a known endian, verify it matches at runtime
 #if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
-   BOTAN_ASSERT(!is_big_endian, "Build and runtime endian match");
+   BOTAN_ASSERT(endian == CPUID::Endian_Status::Little, "Build and runtime endian match");
 #elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
-   BOTAN_ASSERT(is_big_endian, "Build and runtime endian match");
+   BOTAN_ASSERT(endian == CPUID::Endian_Status::Big, "Build and runtime endian match");
 #endif
 
-   return is_big_endian;
-   }
-
-}
-
-CPUID::CPUID_Data::CPUID_Data()
-   {
-   m_processor_features = 0;
-
-#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) || \
-    defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) || \
-    defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
-
-   m_processor_features = detect_cpu_features();
-
-#endif
-
-   m_processor_features |= CPUID::CPUID_INITIALIZED_BIT;
-
-   if(runtime_check_if_big_endian())
-      m_processor_features |= CPUID::CPUID_IS_BIG_ENDIAN_BIT;
+   return endian;
    }
 
 std::vector<CPUID::CPUID_bits>
@@ -153,6 +158,10 @@ CPUID::bit_from_string(const std::string& tok)
       return {CPUID::CPUID_SSE2_BIT};
    if(tok == "ssse3")
       return {CPUID::CPUID_SSSE3_BIT};
+   if(tok == "sse41")
+      return {CPUID::CPUID_SSE41_BIT};
+   if(tok == "sse42")
+      return {CPUID::CPUID_SSE42_BIT};
    // aes_ni is the string printed on the console when running "botan cpuid"
    if(tok == "aesni" || tok == "aes_ni")
       return {CPUID::CPUID_AESNI_BIT};
diff --git a/src/lib/utils/cpuid/cpuid.h b/src/lib/utils/cpuid/cpuid.h
index 87236248f..a4e5e87d1 100644
--- a/src/lib/utils/cpuid/cpuid.h
+++ b/src/lib/utils/cpuid/cpuid.h
@@ -57,6 +57,14 @@ class BOTAN_TEST_API CPUID final
       */
       static std::string to_string();
 
+      /**
+      * Return a best guess of the cache line size
+      */
+      static size_t cache_line_size()
+         {
+         return state().cache_line_size();
+         }
+
       static bool is_little_endian()
          {
 #if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
@@ -64,7 +72,7 @@ class BOTAN_TEST_API CPUID final
 #elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
          return false;
 #else
-         return !has_cpuid_bit(CPUID_IS_BIG_ENDIAN_BIT);
+         return state().endian_status() == Endian_Status::Little;
 #endif
          }
 
@@ -75,56 +83,61 @@ class BOTAN_TEST_API CPUID final
 #elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
          return false;
 #else
-         return has_cpuid_bit(CPUID_IS_BIG_ENDIAN_BIT);
+         return state().endian_status() == Endian_Status::Big;
 #endif
          }
 
-      enum CPUID_bits : uint32_t {
+      enum CPUID_bits : uint64_t {
 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
          // These values have no relation to cpuid bitfields
 
          // SIMD instruction sets
-         CPUID_SSE2_BIT          = (1U << 0),
-         CPUID_SSSE3_BIT         = (1U << 1),
-         CPUID_AVX2_BIT          = (1U << 2),
-         CPUID_AVX512_BIT        = (1U << 3),
+         CPUID_SSE2_BIT       = (1ULL << 0),
+         CPUID_SSSE3_BIT      = (1ULL << 1),
+         CPUID_SSE41_BIT      = (1ULL << 2),
+         CPUID_SSE42_BIT      = (1ULL << 3),
+         CPUID_AVX2_BIT       = (1ULL << 4),
 
-         // Misc useful instructions
-         CPUID_RDTSC_BIT         = (1U << 10),
-         CPUID_ADX_BIT           = (1U << 11),
-         CPUID_BMI_BIT           = (1U << 12),
+         // AVX-512 baseline profile:
+         // AVX-512 F, DQ, BW, VL, IFMA, VBMI, VBMI2, BITALG
+         CPUID_AVX512_BIT     = (1ULL << 11),
 
          // Crypto-specific ISAs
-         CPUID_AESNI_BIT         = (1U << 16),
-         CPUID_CLMUL_BIT         = (1U << 17),
-         CPUID_RDRAND_BIT        = (1U << 18),
-         CPUID_RDSEED_BIT        = (1U << 19),
-         CPUID_SHA_BIT           = (1U << 20),
-         CPUID_AVX512_AES_BIT    = (1U << 21),
-         CPUID_AVX512_CLMUL_BIT  = (1U << 22),
+         CPUID_AESNI_BIT        = (1ULL << 16),
+         CPUID_CLMUL_BIT        = (1ULL << 17),
+         CPUID_RDRAND_BIT       = (1ULL << 18),
+         CPUID_RDSEED_BIT       = (1ULL << 19),
+         CPUID_SHA_BIT          = (1ULL << 20),
+         CPUID_AVX512_AES_BIT   = (1ULL << 21),
+         CPUID_AVX512_CLMUL_BIT = (1ULL << 22),
+
+         // Misc useful instructions
+         CPUID_RDTSC_BIT      = (1ULL << 48),
+         CPUID_ADX_BIT        = (1ULL << 49),
+         CPUID_BMI_BIT        = (1ULL << 51),
+         CPUID_FAST_PDEP_BIT  = (1ULL << 52),
 #endif
 
 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
-         CPUID_ALTIVEC_BIT       = (1U << 0),
-         CPUID_POWER_CRYPTO_BIT  = (1U << 1),
-         CPUID_DARN_BIT          = (1U << 2),
+         CPUID_ALTIVEC_BIT    = (1ULL << 0),
+         CPUID_POWER_CRYPTO_BIT = (1ULL << 1),
+         CPUID_DARN_BIT       = (1ULL << 2),
 #endif
 
 #if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
-         CPUID_ARM_NEON_BIT      = (1U << 0),
-         CPUID_ARM_SVE_BIT       = (1U << 1),
-         CPUID_ARM_AES_BIT       = (1U << 16),
-         CPUID_ARM_PMULL_BIT     = (1U << 17),
-         CPUID_ARM_SHA1_BIT      = (1U << 18),
-         CPUID_ARM_SHA2_BIT      = (1U << 19),
-         CPUID_ARM_SHA3_BIT      = (1U << 20),
-         CPUID_ARM_SHA2_512_BIT  = (1U << 21),
-         CPUID_ARM_SM3_BIT       = (1U << 22),
-         CPUID_ARM_SM4_BIT       = (1U << 23),
+         CPUID_ARM_NEON_BIT      = (1ULL << 0),
+         CPUID_ARM_SVE_BIT       = (1ULL << 1),
+         CPUID_ARM_AES_BIT       = (1ULL << 16),
+         CPUID_ARM_PMULL_BIT     = (1ULL << 17),
+         CPUID_ARM_SHA1_BIT      = (1ULL << 18),
+         CPUID_ARM_SHA2_BIT      = (1ULL << 19),
+         CPUID_ARM_SHA3_BIT      = (1ULL << 20),
+         CPUID_ARM_SHA2_512_BIT  = (1ULL << 21),
+         CPUID_ARM_SM3_BIT       = (1ULL << 22),
+         CPUID_ARM_SM4_BIT       = (1ULL << 23),
 #endif
 
-         CPUID_IS_BIG_ENDIAN_BIT = (1U << 30),
-         CPUID_INITIALIZED_BIT   = (1U << 31)
+         CPUID_INITIALIZED_BIT = (1ULL << 63)
       };
 
 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
@@ -231,6 +244,18 @@ class BOTAN_TEST_API CPUID final
       static bool has_ssse3()
          { return has_cpuid_bit(CPUID_SSSE3_BIT); }
 
+      /**
+      * Check if the processor supports SSE4.1
+      */
+      static bool has_sse41()
+         { return has_cpuid_bit(CPUID_SSE41_BIT); }
+
+      /**
+      * Check if the processor supports SSE4.2
+      */
+      static bool has_sse42()
+         { return has_cpuid_bit(CPUID_SSE42_BIT); }
+
       /**
       * Check if the processor supports AVX2
       */
@@ -263,6 +288,12 @@ class BOTAN_TEST_API CPUID final
       static bool has_bmi2()
          { return has_cpuid_bit(CPUID_BMI_BIT); }
 
+      /**
+      * Check if the processor supports fast PDEP/PEXT from BMI2
+      */
+      static bool has_fast_pdep()
+         { return has_cpuid_bit(CPUID_FAST_PDEP_BIT); }
+
       /**
       * Check if the processor supports AES-NI
       */
@@ -359,7 +390,7 @@ class BOTAN_TEST_API CPUID final
       */
       static void clear_cpuid_bit(CPUID_bits bit)
          {
-         state().clear_cpuid_bit(static_cast<uint32_t>(bit));
+         state().clear_cpuid_bit(static_cast<uint64_t>(bit));
          }
 
       /*
@@ -368,12 +399,18 @@ class BOTAN_TEST_API CPUID final
       */
       static bool has_cpuid_bit(CPUID_bits elem)
          {
-         const uint32_t elem32 = static_cast<uint32_t>(elem);
-         return state().has_bit(elem32);
+         const uint64_t elem64 = static_cast<uint64_t>(elem);
+         return state().has_bit(elem64);
          }
 
       static std::vector<CPUID::CPUID_bits> bit_from_string(const std::string& tok);
    private:
+      enum class Endian_Status : uint32_t {
+         Unknown = 0x00000000,
+         Big     = 0x01234567,
+         Little  = 0x67452301,
+      };
+
       struct CPUID_Data
          {
          public:
@@ -382,25 +419,33 @@ class BOTAN_TEST_API CPUID final
             CPUID_Data(const CPUID_Data& other) = default;
             CPUID_Data& operator=(const CPUID_Data& other) = default;
 
-            void clear_cpuid_bit(uint32_t bit)
+            void clear_cpuid_bit(uint64_t bit)
                {
                m_processor_features &= ~bit;
                }
 
-            bool has_bit(uint32_t bit) const
+            bool has_bit(uint64_t bit) const
                {
                return (m_processor_features & bit) == bit;
                }
 
+            uint64_t processor_features() const { return m_processor_features; }
+            Endian_Status endian_status() const { return m_endian_status; }
+            size_t cache_line_size() const { return m_cache_line_size; }
+
          private:
+            static Endian_Status runtime_check_endian();
+
 #if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) || \
     defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) || \
     defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
 
-            static uint32_t detect_cpu_features();
+            static uint64_t detect_cpu_features(size_t* cache_line_size);
 
 #endif
-            uint32_t m_processor_features;
+            uint64_t m_processor_features;
+            size_t m_cache_line_size;
+            Endian_Status m_endian_status;
          };
 
       static CPUID_Data& state()
diff --git a/src/lib/utils/cpuid/cpuid_aarch64.cpp b/src/lib/utils/cpuid/cpuid_aarch64.cpp
index 72bd84609..af42c621e 100644
--- a/src/lib/utils/cpuid/cpuid_aarch64.cpp
+++ b/src/lib/utils/cpuid/cpuid_aarch64.cpp
@@ -32,9 +32,11 @@ bool sysctlbyname_has_feature(const char* feature_name)
 }
 #endif
 
-uint32_t CPUID::CPUID_Data::detect_cpu_features()
+uint64_t CPUID::CPUID_Data::detect_cpu_features(size_t* cache_line_size)
    {
-   uint32_t detected_features = 0;
+   BOTAN_UNUSED(cache_line_size);
+
+   uint64_t detected_features = 0;
 
 #if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) || defined(BOTAN_TARGET_OS_HAS_ELF_AUX_INFO)
    /*
diff --git a/src/lib/utils/cpuid/cpuid_arm32.cpp b/src/lib/utils/cpuid/cpuid_arm32.cpp
index 1289e7ad0..18af83799 100644
--- a/src/lib/utils/cpuid/cpuid_arm32.cpp
+++ b/src/lib/utils/cpuid/cpuid_arm32.cpp
@@ -13,9 +13,11 @@
 
 namespace Botan {
 
-uint32_t CPUID::CPUID_Data::detect_cpu_features()
+uint64_t CPUID::CPUID_Data::detect_cpu_features(size_t* cache_line_size)
    {
-   uint32_t detected_features = 0;
+   BOTAN_UNUSED(cache_line_size);
+
+   uint64_t detected_features = 0;
 
 #if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) || defined(BOTAN_TARGET_OS_HAS_ELF_AUX_INFO)
    /*
diff --git a/src/lib/utils/cpuid/cpuid_ppc.cpp b/src/lib/utils/cpuid/cpuid_ppc.cpp
index 51054839f..92f90044c 100644
--- a/src/lib/utils/cpuid/cpuid_ppc.cpp
+++ b/src/lib/utils/cpuid/cpuid_ppc.cpp
@@ -12,9 +12,11 @@
 
 namespace Botan {
 
-uint32_t CPUID::CPUID_Data::detect_cpu_features()
+uint64_t CPUID::CPUID_Data::detect_cpu_features(size_t* cache_line_size)
    {
-   uint32_t detected_features = 0;
+   BOTAN_UNUSED(cache_line_size);
+
+   uint64_t detected_features = 0;
 
 #if (defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) || defined(BOTAN_TARGET_HAS_ELF_AUX_INFO)) && defined(BOTAN_TARGET_ARCH_IS_PPC64)
 
diff --git a/src/lib/utils/cpuid/cpuid_x86.cpp b/src/lib/utils/cpuid/cpuid_x86.cpp
index e7a6a7a9f..c70f841e1 100644
--- a/src/lib/utils/cpuid/cpuid_x86.cpp
+++ b/src/lib/utils/cpuid/cpuid_x86.cpp
@@ -75,9 +75,9 @@ void invoke_cpuid_sublevel(uint32_t type, uint32_t level, uint32_t out[4])
 
 }
 
-uint32_t CPUID::CPUID_Data::detect_cpu_features()
+uint64_t CPUID::CPUID_Data::detect_cpu_features(size_t* cache_line_size)
    {
-   uint32_t features_detected = 0;
+   uint64_t features_detected = 0;
    uint32_t cpuid[4] = { 0 };
    bool has_os_ymm_support = false;
    bool has_os_zmm_support = false;
@@ -87,6 +87,11 @@ uint32_t CPUID::CPUID_Data::detect_cpu_features()
 
    const uint32_t max_supported_sublevel = cpuid[0];
 
+   const uint32_t INTEL_CPUID[3] = { 0x756E6547, 0x6C65746E, 0x49656E69 };
+   const uint32_t AMD_CPUID[3] = { 0x68747541, 0x444D4163, 0x69746E65 };
+   const bool is_intel = same_mem(cpuid + 1, INTEL_CPUID, 3);
+   const bool is_amd = same_mem(cpuid + 1, AMD_CPUID, 3);
+
    if(max_supported_sublevel >= 1)
       {
       // CPUID 1: feature bits
@@ -98,6 +103,8 @@ uint32_t CPUID::CPUID_Data::detect_cpu_features()
          SSE2 = (1ULL << 26),
          CLMUL = (1ULL << 33),
          SSSE3 = (1ULL << 41),
+         SSE41 = (1ULL << 51),
+         SSE42 = (1ULL << 52),
          AESNI = (1ULL << 57),
          OSXSAVE = (1ULL << 59),
          AVX = (1ULL << 60),
@@ -112,6 +119,10 @@ uint32_t CPUID::CPUID_Data::detect_cpu_features()
          features_detected |= CPUID::CPUID_CLMUL_BIT;
       if(flags0 & x86_CPUID_1_bits::SSSE3)
          features_detected |= CPUID::CPUID_SSSE3_BIT;
+      if(flags0 & x86_CPUID_1_bits::SSE41)
+         features_detected |= CPUID::CPUID_SSE41_BIT;
+      if(flags0 & x86_CPUID_1_bits::SSE42)
+         features_detected |= CPUID::CPUID_SSE42_BIT;
       if(flags0 & x86_CPUID_1_bits::AESNI)
          features_detected |= CPUID::CPUID_AESNI_BIT;
       if(flags0 & x86_CPUID_1_bits::RDRAND)
@@ -129,6 +140,18 @@ uint32_t CPUID::CPUID_Data::detect_cpu_features()
          }
       }
 
+   if(is_intel)
+      {
+      // Intel cache line size is in cpuid(1) output
+      *cache_line_size = 8 * get_byte<2>(cpuid[1]);
+      }
+   else if(is_amd)
+      {
+      // AMD puts it in vendor zone
+      invoke_cpuid(0x80000005, cpuid);
+      *cache_line_size = get_byte<3>(cpuid[2]);
+      }
+
    if(max_supported_sublevel >= 7)
       {
       clear_mem(cpuid, 4);
@@ -164,13 +187,27 @@ uint32_t CPUID::CPUID_Data::detect_cpu_features()
       if(flags7 & x86_CPUID_7_bits::SHA)
          features_detected |= CPUID::CPUID_SHA_BIT;
 
-      /*
-      We only set the BMI bit if both BMI1 and BMI2 are supported, since
-      typically we want to use both extensions in the same code.
-      */
-      if((flags7 & x86_CPUID_7_bits::BMI1) && (flags7 & x86_CPUID_7_bits::BMI2))
+      if(flags7 & x86_CPUID_7_bits::BMI1)
          {
-         features_detected |= CPUID::CPUID_BMI_BIT;
+         /*
+         We only set the BMI bit if both BMI1 and BMI2 are supported, since
+         typically we want to use both extensions in the same code.
+         */
+         if(flags7 & x86_CPUID_7_bits::BMI2)
+            {
+            features_detected |= CPUID::CPUID_BMI_BIT;
+
+            /*
+            Up until Zen3, AMD CPUs with BMI2 support had microcoded
+            pdep/pext, which works but is very slow.
+
+            TODO: check for Zen3/Zen4 here
+            */
+            if(is_intel)
+               {
+               features_detected |= CPUID::CPUID_FAST_PDEP_BIT;
+               }
+            }
          }
 
       if((flags7 & x86_CPUID_7_bits::AVX512_F) && has_os_zmm_support)
diff --git a/src/lib/utils/info.txt b/src/lib/utils/info.txt
index 7dd1b44ce..7557d7710 100644
--- a/src/lib/utils/info.txt
+++ b/src/lib/utils/info.txt
@@ -36,7 +36,6 @@ loadstor.h
 mul128.h
 os_utils.h
 parsing.h
-prefetch.h
 rotate.h
 rounding.h
 safeint.h
diff --git a/src/lib/utils/os_utils.cpp b/src/lib/utils/os_utils.cpp
index 8c4636783..7abdf2338 100644
--- a/src/lib/utils/os_utils.cpp
+++ b/src/lib/utils/os_utils.cpp
@@ -121,6 +121,44 @@ uint32_t OS::get_process_id()
 #endif
    }
 
+size_t OS::get_cache_line_size()
+   {
+#if defined(BOTAN_TARGET_OS_IS_IOS) || defined(BOTAN_TARGET_OS_IS_MACOS)
+   unsigned long cache_line_size_vl;
+   size_t size = sizeof(cache_line_size_vl);
+   if(::sysctlbyname("hw.cachelinesize", &cache_line_size_vl, &size, nullptr, 0) == 0)
+      return static_cast<size_t>(cache_line_size_vl);
+#endif
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(_SC_LEVEL1_DCACHE_LINESIZE)
+   const long res = ::sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
+   if(res > 0)
+      return static_cast<size_t>(res);
+#endif
+
+#if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL)
+
+   #if defined(AT_L1D_CACHEGEOMETRY)
+   // Cache line size is bottom 16 bits
+   const unsigned long dcache_info = OS::get_auxval(AT_L1D_CACHEGEOMETRY);
+   if(dcache_info != 0)
+      return static_cast<size_t>(dcache_info & 0xFFFF);
+   #endif
+
+   #if defined(AT_DCACHEBSIZE)
+   const unsigned long dcache_bsize = OS::get_auxval(AT_DCACHEBSIZE);
+   if(dcache_bsize != 0)
+      return static_cast<size_t>(dcache_bsize);
+   #endif
+
+#endif
+
+   // TODO: on Windows this is returned by GetLogicalProcessorInformation
+
+   // not available on this platform
+   return 0;
+   }
+
 unsigned long OS::get_auxval(unsigned long id)
    {
 #if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL)
diff --git a/src/lib/utils/os_utils.h b/src/lib/utils/os_utils.h
index 3512b4d03..62dfdeeaf 100644
--- a/src/lib/utils/os_utils.h
+++ b/src/lib/utils/os_utils.h
@@ -93,6 +93,12 @@ size_t get_memory_locking_limit();
 */
 size_t system_page_size();
 
+/**
+* Return the cache line size of the current processor using some
+* OS specific interface, or 0 if not available on this platform.
+*/
+size_t get_cache_line_size();
+
 /**
 * Read the value of an environment variable, setting it to value_out if it
 * exists.  Returns false and sets value_out to empty string if no such variable
diff --git a/src/lib/utils/prefetch.cpp b/src/lib/utils/prefetch.cpp
deleted file mode 100644
index 27665dd91..000000000
--- a/src/lib/utils/prefetch.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
-* (C) 2023 Jack Lloyd
-*
-* Botan is released under the Simplified BSD License (see license.txt)
-*/
-
-#include <botan/internal/prefetch.h>
-#include <botan/internal/bit_ops.h>
-#include <new>
-
-namespace Botan {
-
-uint64_t prefetch_array_raw(size_t bytes, const void* arrayv) noexcept
-   {
-   // Android NDK is garbage and defines the feature macro, but not the variable
-#if defined(__cpp_lib_hardware_interference_size) && !defined(BOTAN_TARGET_OS_IS_ANDROID)
-   const size_t cache_line_size = std::hardware_destructive_interference_size;
-#else
-   // We arbitrarily use a 64 byte cache line, which is by far the most
-   // common size.
-   //
-   // Runtime detection adds too much overhead to this function.
-   const size_t cache_line_size = 64;
-#endif
-
-   const uint8_t* array = static_cast<const uint8_t*>(arrayv);
-
-   volatile uint64_t combiner = 1;
-
-   for(size_t idx = 0; idx < bytes; idx += cache_line_size)
-      {
-#if BOTAN_COMPILER_HAS_BUILTIN(__builtin_prefetch)
-      // we have no way of knowing if the compiler will emit anything here
-      __builtin_prefetch(&array[idx]);
-#endif
-
-      combiner = combiner | array[idx];
-      }
-
-   /*
-   * The combiner variable is initialized with 1, and we accumulate using OR, so
-   * now combiner must be a value other than zero. This being the case we will
-   * always return zero here. Hopefully the compiler will not figure this out.
-   */
-   return ct_is_zero(combiner);
-   }
-
-}
diff --git a/src/lib/utils/prefetch.h b/src/lib/utils/prefetch.h
deleted file mode 100644
index 79743bf9f..000000000
--- a/src/lib/utils/prefetch.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
-* (C) 2023 Jack Lloyd
-*
-* Botan is released under the Simplified BSD License (see license.txt)
-*/
-
-#ifndef BOTAN_PREFETCH_UTILS_H_
-#define BOTAN_PREFETCH_UTILS_H_
-
-#include <botan/types.h>
-#include <type_traits>
-
-namespace Botan {
-
-/**
-* Prefetch an array
-*
-* This function returns a uint64_t which is accumulated from values
-* read from the array. This may help confuse the compiler sufficiently
-* to not elide otherwise "useless" reads. The return value will always
-* be zero.
-*/
-uint64_t prefetch_array_raw(size_t bytes, const void* array) noexcept;
-
-/**
-* Prefetch several arrays
-*
-* This function returns a uint64_t which is accumulated from values
-* read from the array. This may help confuse the compiler sufficiently
-* to not elide otherwise "useless" reads. The return value will always
-* be zero.
-*/
-template<typename T, size_t... Ns>
-T prefetch_arrays(T (&...arr)[Ns]) noexcept
-   requires std::is_integral<T>::value
-   {
-   return (static_cast<T>(prefetch_array_raw(sizeof(T)*Ns, arr)) & ...);
-   }
-
-}
-
-#endif


Test patch:
diff --git a/src/scripts/test_cli.py b/src/scripts/test_cli.py
index 72ffb70d3..cf4250c9c 100755
--- a/src/scripts/test_cli.py
+++ b/src/scripts/test_cli.py
@@ -433,7 +433,7 @@ def cli_pbkdf_tune_tests(_tmp_dir):
 
     expected = re.compile(r'For (default|[1-9][0-9]*) ms selected Scrypt\([0-9]+,[0-9]+,[0-9]+\) using [0-9]+ MiB')
 
-    output = test_cli("pbkdf_tune", ["--tune-msec=1", "--check", "1", "10", "50", "default"], None).split('\n')
+    output = test_cli("pbkdf_tune", ["--check", "1", "10", "50", "default"], None).split('\n')
 
     for line in output:
         if expected.match(line) is None:
diff --git a/src/tests/data/pubkey/eckcdsa.vec b/src/tests/data/pubkey/eckcdsa.vec
index 49d979d07..fa891dd9f 100644
--- a/src/tests/data/pubkey/eckcdsa.vec
+++ b/src/tests/data/pubkey/eckcdsa.vec
@@ -3,6 +3,7 @@
 # "Digital Signature Mechanism with Appendix - Part 3: Korean Certificate-based Digitial Signature Algorithm using Elliptic Curves (EC-KCDSA)"
 # http://www.tta.or.kr/include/Download.jsp?filename=stnfile/TTAK.KO-12.0015_R2.pdf
 
+# Same as ISO/IEC 14888-3:2018 - F.7.1 Example 1: Field Fp, 224-bit Prime p, SHA-224
 Group = secp224r1
 Hash = SHA-224
 X = 0x562A6F64E162FFCB51CD4707774AE36681B6CEF205FE5D43912956A2
@@ -10,6 +11,7 @@ Msg = 5468697320697320612073616D706C65206D65737361676520666F722045432D4B43445341
 Nonce = 76A0AFC18646D1B620A079FB223865A7BCB447F3C03A35D878EA4CDA
 Signature = EEA58C91E0CDCEB5799B00D2412D928FDD23122A1C2BDF43C2F8DAFAAEBAB53C7A44A8B22F35FDB9DE265F23B89F65A69A8B7BD4061911A6
 
+# Same as ISO/IEC 14888-3:2018 - F.7.2 Example 2: Field Fp, 256-bit Prime p, SHA-256
 Group = secp256r1
 Hash = SHA-256
 X = 0x9051A275AA4D98439EDDED13FA1C6CBBCCE775D8CC9433DEE69C59848B3594DF
@@ -26,6 +28,16 @@ Msg = 616263
 Nonce = 4B19A0725424CD3310B02D8C8416C98D64C618BFE935597D
 Signature = 3CA29800D425FCAA51CCB209B4ED5D6C352108223143B2EA5A0E8644CE8F768A6FA4D193C726AD08019788E5
 
+# Taken from ISO/IEC 14888-3:2018
+
+# ISO/IEC 14888-3:2018 - F.7.7 Example 7: Field Fp, 224-bit Prime p, SHA-256
+Group = secp224r1
+Hash = SHA-256
+X = 0x61585827449DBC0EC161B2CF8575C9DF149F41DD0289BE4FF110773D
+Msg = 5468697320697320612073616D706C65206D65737361676520666F722045432D4B4344534120696D706C656D656E746174696F6E2076616C69646174696F6E2E
+Nonce = EEC79D8D4648DF3A832A66E3775537E000CC9B957E1319C5DB9DD4F7
+Signature = 64B49E977E6534F877CB68A3806F6A989311CEAA8A64A0558077C04BAFF23D40B177951151BE32F6561B1B739E3E8F822CC52D4CB3909A93
+
 # From https://github.com/ANSSI-FR/libecc
 
 Group = secp384r1
@@ -35,6 +47,13 @@ Msg = 5468697320697320612073616D706C65206D65737361676520666F722045432D4B43445341
 Nonce = 08162ef824d2d5114e0861f393b46fb602a495a2ca177f47da4e6f2d23a185c5b88f1b7e2cba8d773716093af5979a7e
 Signature = d037264744e38a38caccdc8fcb08d8cb52187d383cc0b629f62771d93f5afcd3c144c153ef06d99ac9205881de03d171f1ffc143b94e44dcecd161fdc5270a32713e5938fb35600eddd00c6389fb33b03e77d2f7694a04085af98531dfe6f173
 
+Group = secp256r1
+Hash = SHA-512
+X = 0xDC51D3866A15BACDE33D96F992FCA99DA7E6EF0934E7097559C27F1614C88A7F
+Msg = 616263
+Nonce = 9E56F509196784D963D1C0A401510EE7ADA3DCC5DEE04B154BF61AF1D5A6DECE
+Signature = 70021d6d5a5e99594318a3ba8d27a539a573768073c84da0dbccf9249afe7854b69728e17b9e85b030058f2854bf34d1dd1df67a026423769eb4fd0f4c2e156b
+
 Group = secp521r1
 Hash = SHA-512
 X = 0x0165e1f1f2e7ae6b502a194ef03ad050e3a5ae31fcb4f70d58f5c6df2866717f4409e778024de52befc96db9fbba21306d125a27cfcdce9100097963bf0c40e696dc
diff --git a/src/tests/test_c25519.cpp b/src/tests/test_c25519.cpp
index bc68b27a8..72d1b0961 100644
--- a/src/tests/test_c25519.cpp
+++ b/src/tests/test_c25519.cpp
@@ -88,7 +88,7 @@ class Curve25519_Roundtrip_Test final : public Test
 
             const std::string a_pass = "alice pass";
             const std::string b_pass = "bob pass";
-            const auto pbe_time = std::chrono::milliseconds(1);
+            const auto pbe_time = std::chrono::milliseconds(10);
             const std::string a_priv_pem = Botan::PKCS8::PEM_encode(a_priv_gen, Test::rng(), a_pass, pbe_time);
             const std::string b_priv_pem = Botan::PKCS8::PEM_encode(b_priv_gen, Test::rng(), b_pass, pbe_time);
 
diff --git a/src/tests/test_pbkdf.cpp b/src/tests/test_pbkdf.cpp
index 990d45489..2e74b6098 100644
--- a/src/tests/test_pbkdf.cpp
+++ b/src/tests/test_pbkdf.cpp
@@ -90,10 +90,6 @@ class Pwdhash_Tests : public Test
             "Bcrypt-PBKDF"
          };
 
-         const auto run_time = std::chrono::milliseconds(3);
-         const auto tune_time = std::chrono::milliseconds(1);
-         const size_t max_mem = 32;
-
          for(const std::string& pwdhash : all_pwdhash)
             {
             Test::Result result("Pwdhash " + pwdhash);
@@ -106,7 +102,7 @@ class Pwdhash_Tests : public Test
                const std::vector<uint8_t> salt(8);
                const std::string password = "test";
 
-               auto tuned_pwhash = pwdhash_fam->tune(32, run_time, max_mem, tune_time);
+               auto tuned_pwhash = pwdhash_fam->tune(32, std::chrono::milliseconds(10));
 
                std::vector<uint8_t> output1(32);
                tuned_pwhash->derive_key(output1.data(), output1.size(),
diff --git a/src/tests/test_pubkey.cpp b/src/tests/test_pubkey.cpp
index 025684fb5..9e8952906 100644
--- a/src/tests/test_pubkey.cpp
+++ b/src/tests/test_pubkey.cpp
@@ -656,7 +656,7 @@ void test_pbe_roundtrip(Test::Result& result,
       {
       Botan::DataSource_Memory data_src(
          Botan::PKCS8::PEM_encode(key, Test::rng(), passphrase,
-                                  std::chrono::milliseconds(1),
+                                  std::chrono::milliseconds(10),
                                   pbe_algo));
 
       auto loaded = Botan::PKCS8::load_key(data_src, passphrase);
@@ -674,7 +674,7 @@ void test_pbe_roundtrip(Test::Result& result,
       {
       Botan::DataSource_Memory data_src(
          Botan::PKCS8::BER_encode(key, Test::rng(), passphrase,
-                                  std::chrono::milliseconds(1),
+                                  std::chrono::milliseconds(10),
                                   pbe_algo));
 
       auto loaded = Botan::PKCS8::load_key(data_src, passphrase);
diff --git a/src/tests/test_rng_behavior.cpp b/src/tests/test_rng_behavior.cpp
index 6cf494ceb..22d25e447 100644
--- a/src/tests/test_rng_behavior.cpp
+++ b/src/tests/test_rng_behavior.cpp
@@ -62,7 +62,6 @@ class Stateful_RNG_Tests : public Test
          results.push_back(test_prediction_resistance());
          results.push_back(test_randomize_with_ts_input());
          results.push_back(test_security_level());
-         results.push_back(test_input_output_edge_cases());
 
          /*
          * This test uses the library in both parent and child processes. But
@@ -414,27 +413,6 @@ class Stateful_RNG_Tests : public Test
          return result;
          }
 
-      Test::Result test_input_output_edge_cases()
-         {
-         Test::Result result(rng_name() + " randomize");
-
-         const std::vector<uint8_t> seed(128);
-         Fixed_Output_RNG fixed_output_rng(seed);
-
-         auto rng = make_rng(fixed_output_rng);
-
-         for(size_t i = 0; i != 4096; ++i)
-            {
-            std::vector<uint8_t> buf(i);
-            rng->randomize(buf.data(), buf.size());
-            rng->add_entropy(buf.data(), buf.size());
-
-            result.test_success("RNG accepted input and output length");
-            }
-
-         return result;
-         }
-
    };
 
 #endif
@@ -748,15 +726,6 @@ class AutoSeeded_RNG_Tests final : public Test
          rng.random_vec(16); // generate and discard output
          result.confirm("AutoSeeded_RNG can be reseeded", rng.is_seeded());
 
-         for(size_t i = 0; i != 4096; ++i)
-            {
-            std::vector<uint8_t> buf(i);
-            rng.randomize(buf.data(), buf.size());
-            rng.add_entropy(buf.data(), buf.size());
-
-            result.test_success("AutoSeeded_RNG accepted input and output length");
-            }
-
          rng.clear();
 
          return result;
diff --git a/src/tests/test_utils.cpp b/src/tests/test_utils.cpp
index fa3a379b0..3efc76beb 100644
--- a/src/tests/test_utils.cpp
+++ b/src/tests/test_utils.cpp
@@ -673,6 +673,12 @@ class CPUID_Tests final : public Test
             result.test_eq("If endian is big, it is not also little endian", Botan::CPUID::is_little_endian(), false);
             }
 
+         const size_t cache_line_size = Botan::CPUID::cache_line_size();
+
+         result.test_gte("Cache line size is >= 16", cache_line_size, 16);
+         result.test_lte("Cache line size is <= 256", cache_line_size, 256);
+         result.confirm("Cache line size is a power of 2", Botan::is_power_of_2(cache_line_size));
+
          const std::string cpuid_string = Botan::CPUID::to_string();
          result.test_success("CPUID::to_string doesn't crash");
 
diff --git a/src/tests/test_xmss.cpp b/src/tests/test_xmss.cpp
index cdf6c2c67..f2e9617dd 100644
--- a/src/tests/test_xmss.cpp
+++ b/src/tests/test_xmss.cpp
@@ -207,137 +207,12 @@ std::vector<Test::Result> xmss_statefulness()
       };
    }
 
-std::vector<Test::Result> xmss_legacy_private_key()
-   {
-   const auto legacy_xmss_public_key = Botan::hex_decode(
-      "00000001C49B128365E23D97C2D36A2AC26E0B448FCEF77A5ED68EFDC460E787D0EDB67"
-      "68EAC3E82B04B84571C49684ED822F8179FDFF1C825DA9F9EA78F5D50A21E6341");
-
-   // With Botan 3.0 we implemented a multi-target attack mitigation as
-   // suggested by NIST's SP.800-208. This private key was generated before that.
-   const auto legacy_xmss_private_key = Botan::hex_decode(
-      "04818800000001C49B128365E23D97C2D36A2AC26E0B448FCEF77A5ED68EFDC460E787D"
-      "0EDB6768EAC3E82B04B84571C49684ED822F8179FDFF1C825DA9F9EA78F5D50A21E6341"
-      "0000000059FF2307F3D05577B1B259A64B5D8DB8FDF51A4EA898D23B3F4F574961ED7A6"
-      "A7F42ADD11F527ED757E79183A2FABA0E37E1C3C8C6E6468B4AB44305D3920DAE");
-
-   const auto legacy_signature = Botan::hex_decode(
-      "0000000049BB2EDE36E46FD63C5DBA3F60A2F628E3116452DAB39A60A4E321110EC45D1"
-      "CE7CD3F33F736B8ACABB9D2FA0764645F1FD708E84B82BED1D94B2FD4A4AF72DE4B68AE"
-      "2B7E95A99B752C6CD74DF3180754A2B663F164616B07E0212224CA3A66887DDA3083090"
-      "010310D9F16BB087DE1F346FD2C2693AC5DE11DE24181A21AF3F2CFF6535C11D2072078"
-      "D291E939BC95C8F3CD4A59225774EB291B8CDE7A90AF03030BF05F71FCD2BD0C4462E6B"
-      "9FAC751B5E896370966357B3032A2650895502566FFCD99C309548C5091785DC145A511"
-      "E591F39495EF86B1CFBBA654BB289A3B61BCB4C5967773182396D384E76D6007FD9698C"
-      "55F8F4DA4A21EA97FBDF7B40B4289917B51067307BAC0507DBB39E4B618B799AAD8D08D"
-      "FB4C10B648AFC66BFB071472FC6ADEC311008FF1D4C4F32BE664DCEF829375E3A92A0F0"
-      "C9D7C4613B778172ACF81F7EFEE1D76A4AF0C1195292BE694F655374B68C3DB9E4C82F2"
-      "8FF2EEC3AE9B5D1EDC807608E1A08246408CECBE5816BCC6585A9558A8EAE57619EAEE3"
-      "1DE879138FAE3EACBFF180F71ED4BD28497BDCDE4CF37D40FDDE92D3422A1DA99CEEDC4"
-      "063656E81E3F9398E9333BB3BCE7A0FC594D12F9A9E82D3FE89E66C49F07144737D56B1"
-      "B0DCE1B98AC3B3619C4D85C77416C286998652A0A08A9F0D5B51BBD4040C0A07373130C"
-      "1E9C0194F649018D67AF050353BE08E3E5E750D7E9D85C9C7283F5293212C8D372BBFDA"
-      "8D28A581B42C90BE0C5E68FC63A3E0812656EB0D57CE9758ADD8436F57B9FD402EE42E9"
-      "25CA68A52BEEE972871C9559819C9BAED8FD3BA508DCC9945415B603330A82CAEAA8001"
-      "9EB1332E191E06EC8FA325BCE49B903BFC4244608A42E673D3DFA8202F49AE5C150AB56"
-      "D4C720E32719498835D35D7062FCF2A969F552B11672CB7061E7C565B960C2BA8EB0780"
-      "49365202674D07617DD516D2C598C71669D43EE63852BC758ABFD042097FEC7E3F9B855"
-      "9AF3043630F00AEB9DF8B549076FBB2F786D0BD16C889439F45D441AF3EBC16E70AF7B5"
-      "98D36A9F3A5FA05D5D438D1A563C9AC11B73A6A9976AD3DC78190F3289F35D019D5DF59"
-      "DD3262608643509C25FF4C6056C7528BB551680F6A5E637F60C5951593F7E701C297DA0"
-      "FDB289D1D47E2CE1F5E3CD6703069F773860152C903962B0F8704E6B89201296C05D6DA"
-      "81CC0E3848E8C07400A9B3962013642E25F2332FC820463890D97F3BD34DDC4318E2EA1"
-      "E65BE4F5613854856497BE2C4B894731DCD45209038C80855EB4150EE5D4CC6BE6859A6"
-      "D71A02AD693E2F43BD5736859B7F295BA068503510347637ABF19D67F984BAA9AFE577B"
-      "BC4EFF50FD7F2F56305DF9F0393DDB02F20BA554DFF9ACF6748E8FFE433267CCBE0C434"
-      "900672987D00197116F61FACE800C42FAA1957D95679F4CDCA392E5CAC26E84F523D23D"
-      "C209C19E8E42FAF1269A6C07BDC3E89304E479D2F71A7BCDDBF485BA65662013DD4F35C"
-      "1CB7CBB7FD9388DD048AA5CB5AF01A7DB63E20BCB43B2C7AB2CBC9C9232CE5099055915"
-      "6D18B0BD44B46C813CC2E0C29361790B3EE61EF6946AE3197A8B0226B47E8931A9841DA"
-      "6A976EB1A105F0487AFD294BE69B4021BF60EFDA826DC69C1A2BAE015CD0D13DD614E8A"
-      "71B885FFB9CDBF8BCF157BAF08D1A4EE24573235617F70049511AFF40D9243D2398C8F0"
-      "05790D86F0956473BDBC5DEF4DFB12597F6BE67E3265E062356D4122D30B3BCDC60D736"
-      "0D82FB0943DF88DC761018C7E43F7653A899D517709DE33AB2866BD44748A32E96BDCA2"
-      "84763EEEEFAEF347FD8D31AE7BF98C21498240C5BBACD9F16A7B4C8B98C9AB1F9F10E17"
-      "8F45483CBB6DFE3CF47EEB87CB5A563105DC2AA6B3174B407A3C20FDF82B4E7A825FD4E"
-      "80FCD3A63A9FDA6182A69A1289A0A8EECB5AC381BC53048ECBFCAF26E4BA46179081325"
-      "DBD89ACF9EF115667368F6604E2856AD107B5CA6BFA8E9253146D9CFB32E4359A2E7A3D"
-      "3F6DCDA5EDCD09558B904D13D3CFA5E02D409EC6C610FBF9D0E1F34C9D5AF4F18F43527"
-      "AA0A2BB687A12AE426E3A73BD75493DCCAAABEA83E1DB0BF9CC7589EE169A8A8C5FAAA7"
-      "7D69405D9347D9642202E50B27229AE0A8E7482F06AC4FB9F039CECE7F056EC01F83549"
-      "6977D845641254ACF420B6766F8CD1CE13DD9041B2BE7369EFE20FC5359DFD31166FBFF"
-      "5D1C6EBB1FFB0A6C29CC64DE9F30EE3C643A3168403911B7DDA3E7B23090C444960FCE5"
-      "6F9F1077D0D54658DAD1C65D388448982ACE87D426595CD38F50BF88755036551976A00"
-      "310E66E0CB8BCBF5A51AE0F6418079F29C5F56DA9F7FD787B821C6346205C520794079F"
-      "11858E7530CF494377F998E33323030D82A44D84F5209CC608F38C950D88B6A169D297F"
-      "101A0D3E7DA3152F7D698ED3F6F6EE79B4F87E6032B98907E2FEC3AD1F44FA7816B568A"
-      "31AF329B19636788498BE3C21A5676BDEB987F1BA213C8E86F5D39CEC9571EB5198645C"
-      "D39EA26BE5598D47F3324F3D763D4E892A13C579F964B72369B40800B30088DE6140E94"
-      "5B5F03C67015EC4E03065EC9126C2417F827EC526C852020D73BB6F97DED24081399D7B"
-      "F7EE644B16A7BC601F32A312CFB98FF0E954DF0FCD2C61154B3D8CCC32DCFCCB172033D"
-      "5F5ACB03369E323429123FB9EAB658CB072EA9CD058CAD76D78A6F0BA01BB5D36172DE7"
-      "4DE17704A055509B3BDBE7032F4A0E3E7329CC646D06DE7C7ECF5EF030362CE079D3C3B"
-      "DDB605A04918C16FE79251B7705DAC88A0C84E7C692E1AFF59968DE28CBDF001BBEA509"
-      "A8A270B6BFD7E653B2228418E73F40A14F491FAEBAC8C11F346FB51C8E10DB63ED5C287"
-      "DA7DDC692B8FE6998AC85EA29D4F9B5FEB3E062FFEEA23DAD536DDE783AFE43606D9685"
-      "09F4695E55FE935DF20B8E9EFEE01DD21E733C22830A6E6086EBB4AC7CFB5DC5C6F9FBE"
-      "F131BF636A129723E5D663FDCCF181AB996D158146602D567B48CEF809E4D3E5B7A7B64"
-      "3C48EFCA7621534397B623AF23AE6F2BE7F2D7A6FBB2D7B64B96526B73DBA66A29F4260"
-      "2BD9FF72A165431C15A570A5731D9426210F946FC682F181944F40DFB68314ACA43E321"
-      "4AFDF11EA825B79425F0C864E354CA7E6C447AE9E0796AAB2B6720516D3FC0B73CFE98E"
-      "F0D8E8D80A59DB9ADA0F03CF0444E1BED9ED9A75FBB6FD6D5BAD4F5FF66C8987ADA6F9F"
-      "CA06CBAD1C9E329B844CC5BBF6CCC52852DE44B72EECAF34802043107542F5BFEA73944"
-      "611AB035D6D7A8EBD3101F321A54C1DADC600419F8DA6BF3967E5B9E708BC4C3E901623"
-      "E8FB6E87825757498A24A921EC601CC15399237D9C4115B678E8968A2095818D9261C4E"
-      "8F94B9DCE3F74EC7E0479F6F6AB13676954390EF3B45D2292BF664D618CFE5ECFFBD391"
-      "872B4F9F306A09B1238E7561942C7065FD14208F4D4A42752A9A747A29B8BE3C1658EDC"
-      "6B250DBD1599FBB09A7F148A7AEFEAB26ADB728A330DD3F616C8A736D1BF4EA17F2C3BF"
-      "A5E22C249FA9D1E7DA08DB351709C4");
-
-   Botan::XMSS_PrivateKey legacy_secret_key = Botan::XMSS_PrivateKey(legacy_xmss_private_key, Botan::WOTS_Derivation_Method::Botan2x);
-   Botan::XMSS_PublicKey public_key_from_secret_key(legacy_secret_key);
-   Botan::XMSS_PublicKey legacy_public_key = Botan::XMSS_PublicKey(legacy_xmss_public_key);
-
-   const auto message = Botan::hex_decode("deadcafe");
-   const auto algo_name = "SHA2_10_256";
-
-   return
-      {
-      Botan_Tests::CHECK("Use a legacy private key to create a signature", [&](auto& result)
-         {
-         Botan::PK_Signer signer(legacy_secret_key, Test::rng(), algo_name);
-         auto signature = signer.sign_message(message, Test::rng());
-
-         Botan::PK_Verifier verifier(public_key_from_secret_key, algo_name);
-         result.confirm("legacy private key generates signatures that are still verifiable",
-                        verifier.verify_message(message, signature));
-         }),
-
-      Botan_Tests::CHECK("Verify a legacy signature", [&](auto& result)
-         {
-         Botan::PK_Verifier verifier(public_key_from_secret_key, algo_name);
-         result.confirm("legacy private key generates signatures that are still verifiable",
-                        verifier.verify_message(message, legacy_signature));
-         }),
-
-      Botan_Tests::CHECK("Verify a new signature by a legacy private key with a legacy public key", [&](auto& result)
-         {
-         Botan::PK_Signer signer(legacy_secret_key, Test::rng(), algo_name);
-         auto signature = signer.sign_message(message, Test::rng());
-
-         Botan::PK_Verifier verifier(legacy_public_key, algo_name);
-         result.confirm("legacy private key generates signatures that are still verifiable",
-                        verifier.verify_message(message, legacy_signature));
-         }),
-      };
-   }
-
 BOTAN_REGISTER_TEST("pubkey", "xmss_sign", XMSS_Signature_Tests);
 BOTAN_REGISTER_TEST("pubkey", "xmss_verify", XMSS_Signature_Verify_Tests);
 BOTAN_REGISTER_TEST("pubkey", "xmss_verify_invalid", XMSS_Signature_Verify_Invalid_Tests);
 BOTAN_REGISTER_TEST("pubkey", "xmss_keygen", XMSS_Keygen_Tests);
 BOTAN_REGISTER_TEST("pubkey", "xmss_keygen_reference", XMSS_Keygen_Reference_Test);
-BOTAN_REGISTER_TEST_FN("pubkey", "xmss_unit_tests", xmss_statefulness, xmss_legacy_private_key);
+BOTAN_REGISTER_TEST_FN("pubkey", "xmss_statefulness", xmss_statefulness);
 
 #endif