Repository: randombit/botan
Pull request #3684 (2023-08-30 07:46:27)

std::span for SymmetricAlgorithm::key_schedule()
### Pull Request Dependencies

* #3681

Also this probably interferes with the Keccak refactoring, KMAC and the work done in `MDx_HashFunction`. It shouldn't be too hard to fix those conflicts in any order, though.

### Description

This upgrades the internal virtual method of `SymmetricAlgorithm::key_schedule()` to use `std::span<>`.

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

coverage: 91.706% (-0.002%) from 91.708% when pulling **8ec28661735e872c3a7632687122163ab5f73292 on span/symalgo** into **3e69c7b33119245b6add46e1d057b51bef1e84f3 on master**.


Patch:
diff --git a/src/lib/base/buf_comp.cpp b/src/lib/base/buf_comp.cpp
index 0a27060db..3c4ff167b 100644
--- a/src/lib/base/buf_comp.cpp
+++ b/src/lib/base/buf_comp.cpp
@@ -13,37 +13,37 @@ namespace Botan {
 void Buffered_Computation::update_be(uint16_t val) {
    uint8_t inb[sizeof(val)];
    store_be(val, inb);
-   add_data(inb, sizeof(inb));
+   add_data({inb, sizeof(inb)});
 }
 
 void Buffered_Computation::update_be(uint32_t val) {
    uint8_t inb[sizeof(val)];
    store_be(val, inb);
-   add_data(inb, sizeof(inb));
+   add_data({inb, sizeof(inb)});
 }
 
 void Buffered_Computation::update_be(uint64_t val) {
    uint8_t inb[sizeof(val)];
    store_be(val, inb);
-   add_data(inb, sizeof(inb));
+   add_data({inb, sizeof(inb)});
 }
 
 void Buffered_Computation::update_le(uint16_t val) {
    uint8_t inb[sizeof(val)];
    store_le(val, inb);
-   add_data(inb, sizeof(inb));
+   add_data({inb, sizeof(inb)});
 }
 
 void Buffered_Computation::update_le(uint32_t val) {
    uint8_t inb[sizeof(val)];
    store_le(val, inb);
-   add_data(inb, sizeof(inb));
+   add_data({inb, sizeof(inb)});
 }
 
 void Buffered_Computation::update_le(uint64_t val) {
    uint8_t inb[sizeof(val)];
    store_le(val, inb);
-   add_data(inb, sizeof(inb));
+   add_data({inb, sizeof(inb)});
 }
 
 }  // namespace Botan
diff --git a/src/lib/base/buf_comp.h b/src/lib/base/buf_comp.h
index b61647dc0..bd126c04a 100644
--- a/src/lib/base/buf_comp.h
+++ b/src/lib/base/buf_comp.h
@@ -31,13 +31,13 @@ class BOTAN_PUBLIC_API(2, 0) Buffered_Computation {
       * @param in the input to process as a byte array
       * @param length of param in in bytes
       */
-      void update(const uint8_t in[], size_t length) { add_data(in, length); }
+      void update(const uint8_t in[], size_t length) { add_data({in, length}); }
 
       /**
       * Add new input to process.
       * @param in the input to process as a contiguous data range
       */
-      void update(std::span<const uint8_t> in) { add_data(in.data(), in.size()); }
+      void update(std::span<const uint8_t> in) { add_data(in); }
 
       void update_be(uint16_t val);
       void update_be(uint32_t val);
@@ -52,13 +52,13 @@ class BOTAN_PUBLIC_API(2, 0) Buffered_Computation {
       * @param str the input to process as a std::string_view. Will be interpreted
       * as a byte array based on the strings encoding.
       */
-      void update(std::string_view str) { add_data(cast_char_ptr_to_uint8(str.data()), str.size()); }
+      void update(std::string_view str) { add_data({cast_char_ptr_to_uint8(str.data()), str.size()}); }
 
       /**
       * Process a single byte.
       * @param in the byte to process
       */
-      void update(uint8_t in) { add_data(&in, 1); }
+      void update(uint8_t in) { add_data({&in, 1}); }
 
       /**
       * Complete the computation and retrieve the
@@ -66,7 +66,7 @@ class BOTAN_PUBLIC_API(2, 0) Buffered_Computation {
       * @param out The byte array to be filled with the result.
       * Must be of length output_length()
       */
-      void final(uint8_t out[]) { final_result(out); }
+      void final(uint8_t out[]) { final_result({out, output_length()}); }
 
       /**
       * Complete the computation and retrieve the
@@ -76,7 +76,7 @@ class BOTAN_PUBLIC_API(2, 0) Buffered_Computation {
       template <concepts::resizable_byte_buffer T = secure_vector<uint8_t>>
       T final() {
          T output(output_length());
-         final_result(output.data());
+         final_result(output);
          return output;
       }
 
@@ -84,13 +84,13 @@ class BOTAN_PUBLIC_API(2, 0) Buffered_Computation {
 
       void final(std::span<uint8_t> out) {
          BOTAN_ASSERT_NOMSG(out.size() >= output_length());
-         final_result(out.data());
+         final_result(out);
       }
 
       template <concepts::resizable_byte_buffer T>
       void final(T& out) {
          out.resize(output_length());
-         final_result(out.data());
+         final_result(out);
       }
 
       /**
@@ -136,15 +136,14 @@ class BOTAN_PUBLIC_API(2, 0) Buffered_Computation {
       /**
       * Add more data to the computation
       * @param input is an input buffer
-      * @param length is the length of input in bytes
       */
-      virtual void add_data(const uint8_t input[], size_t length) = 0;
+      virtual void add_data(std::span<const uint8_t> input) = 0;
 
       /**
       * Write the final output to out
       * @param out is an output buffer of output_length()
       */
-      virtual void final_result(uint8_t out[]) = 0;
+      virtual void final_result(std::span<uint8_t> out) = 0;
 };
 
 }  // namespace Botan
diff --git a/src/lib/base/sym_algo.cpp b/src/lib/base/sym_algo.cpp
index 6439502f5..63ebc8e66 100644
--- a/src/lib/base/sym_algo.cpp
+++ b/src/lib/base/sym_algo.cpp
@@ -14,11 +14,11 @@ void SymmetricAlgorithm::throw_key_not_set_error() const {
    throw Key_Not_Set(name());
 }
 
-void SymmetricAlgorithm::set_key(const uint8_t key[], size_t length) {
-   if(!valid_keylength(length)) {
-      throw Invalid_Key_Length(name(), length);
+void SymmetricAlgorithm::set_key(std::span<const uint8_t> key) {
+   if(!valid_keylength(key.size())) {
+      throw Invalid_Key_Length(name(), key.size());
    }
-   key_schedule(key, length);
+   key_schedule(key);
 }
 
 }  // namespace Botan
diff --git a/src/lib/base/sym_algo.h b/src/lib/base/sym_algo.h
index 43c81b38a..965462817 100644
--- a/src/lib/base/sym_algo.h
+++ b/src/lib/base/sym_algo.h
@@ -110,20 +110,20 @@ class BOTAN_PUBLIC_API(2, 0) SymmetricAlgorithm {
       * Set the symmetric key of this object.
       * @param key the SymmetricKey to be set.
       */
-      void set_key(const SymmetricKey& key) { set_key(key.begin(), key.length()); }
+      void set_key(const SymmetricKey& key) { set_key(std::span{key.begin(), key.length()}); }
 
       /**
       * Set the symmetric key of this object.
       * @param key the contiguous byte range to be set.
       */
-      void set_key(std::span<const uint8_t> key) { set_key(key.data(), key.size()); }
+      void set_key(std::span<const uint8_t> key);
 
       /**
       * Set the symmetric key of this object.
       * @param key the to be set as a byte array.
       * @param length in bytes of key param
       */
-      void set_key(const uint8_t key[], size_t length);
+      void set_key(const uint8_t key[], size_t length) { set_key(std::span{key, length}); }
 
       /**
       * @return the algorithm name
@@ -150,9 +150,8 @@ class BOTAN_PUBLIC_API(2, 0) SymmetricAlgorithm {
       /**
       * Run the key schedule
       * @param key the key
-      * @param length of key
       */
-      virtual void key_schedule(const uint8_t key[], size_t length) = 0;
+      virtual void key_schedule(std::span<const uint8_t> key) = 0;
 };
 
 }  // namespace Botan
diff --git a/src/lib/block/aes/aes.cpp b/src/lib/block/aes/aes.cpp
index e329b464b..ac055a91e 100644
--- a/src/lib/block/aes/aes.cpp
+++ b/src/lib/block/aes/aes.cpp
@@ -845,26 +845,26 @@ void AES_128::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
    aes_decrypt_n(in, out, blocks, m_DK);
 }
 
-void AES_128::key_schedule(const uint8_t key[], size_t length) {
+void AES_128::key_schedule(std::span<const uint8_t> key) {
 #if defined(BOTAN_HAS_AES_NI)
    if(CPUID::has_aes_ni()) {
-      return aesni_key_schedule(key, length);
+      return aesni_key_schedule(key.data(), key.size());
    }
 #endif
 
 #if defined(BOTAN_HAS_HW_AES_SUPPORT)
    if(CPUID::has_hw_aes()) {
-      return aes_key_schedule(key, length, m_EK, m_DK, CPUID::is_little_endian());
+      return aes_key_schedule(key.data(), key.size(), m_EK, m_DK, CPUID::is_little_endian());
    }
 #endif
 
 #if defined(BOTAN_HAS_AES_VPERM)
    if(CPUID::has_vperm()) {
-      return vperm_key_schedule(key, length);
+      return vperm_key_schedule(key.data(), key.size());
    }
 #endif
 
-   aes_key_schedule(key, length, m_EK, m_DK);
+   aes_key_schedule(key.data(), key.size(), m_EK, m_DK);
 }
 
 void AES_128::clear() {
@@ -908,26 +908,26 @@ void AES_192::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
    aes_decrypt_n(in, out, blocks, m_DK);
 }
 
-void AES_192::key_schedule(const uint8_t key[], size_t length) {
+void AES_192::key_schedule(std::span<const uint8_t> key) {
 #if defined(BOTAN_HAS_AES_NI)
    if(CPUID::has_aes_ni()) {
-      return aesni_key_schedule(key, length);
+      return aesni_key_schedule(key.data(), key.size());
    }
 #endif
 
 #if defined(BOTAN_HAS_HW_AES_SUPPORT)
    if(CPUID::has_hw_aes()) {
-      return aes_key_schedule(key, length, m_EK, m_DK, CPUID::is_little_endian());
+      return aes_key_schedule(key.data(), key.size(), m_EK, m_DK, CPUID::is_little_endian());
    }
 #endif
 
 #if defined(BOTAN_HAS_AES_VPERM)
    if(CPUID::has_vperm()) {
-      return vperm_key_schedule(key, length);
+      return vperm_key_schedule(key.data(), key.size());
    }
 #endif
 
-   aes_key_schedule(key, length, m_EK, m_DK);
+   aes_key_schedule(key.data(), key.size(), m_EK, m_DK);
 }
 
 void AES_192::clear() {
@@ -971,26 +971,26 @@ void AES_256::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
    aes_decrypt_n(in, out, blocks, m_DK);
 }
 
-void AES_256::key_schedule(const uint8_t key[], size_t length) {
+void AES_256::key_schedule(std::span<const uint8_t> key) {
 #if defined(BOTAN_HAS_AES_NI)
    if(CPUID::has_aes_ni()) {
-      return aesni_key_schedule(key, length);
+      return aesni_key_schedule(key.data(), key.size());
    }
 #endif
 
 #if defined(BOTAN_HAS_HW_AES_SUPPORT)
    if(CPUID::has_hw_aes()) {
-      return aes_key_schedule(key, length, m_EK, m_DK, CPUID::is_little_endian());
+      return aes_key_schedule(key.data(), key.size(), m_EK, m_DK, CPUID::is_little_endian());
    }
 #endif
 
 #if defined(BOTAN_HAS_AES_VPERM)
    if(CPUID::has_vperm()) {
-      return vperm_key_schedule(key, length);
+      return vperm_key_schedule(key.data(), key.size());
    }
 #endif
 
-   aes_key_schedule(key, length, m_EK, m_DK);
+   aes_key_schedule(key.data(), key.size(), m_EK, m_DK);
 }
 
 void AES_256::clear() {
diff --git a/src/lib/block/aes/aes.h b/src/lib/block/aes/aes.h
index c5fdb23ed..0071e0edb 100644
--- a/src/lib/block/aes/aes.h
+++ b/src/lib/block/aes/aes.h
@@ -33,7 +33,7 @@ class AES_128 final : public Block_Cipher_Fixed_Params<16, 16> {
       bool has_keying_material() const override;
 
    private:
-      void key_schedule(const uint8_t key[], size_t length) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
 #if defined(BOTAN_HAS_AES_VPERM)
       void vperm_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
@@ -88,7 +88,7 @@ class AES_192 final : public Block_Cipher_Fixed_Params<16, 24> {
       void hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
 #endif
 
-      void key_schedule(const uint8_t key[], size_t length) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       secure_vector<uint32_t> m_EK, m_DK;
 };
@@ -128,7 +128,7 @@ class AES_256 final : public Block_Cipher_Fixed_Params<16, 32> {
       void hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
 #endif
 
-      void key_schedule(const uint8_t key[], size_t length) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       secure_vector<uint32_t> m_EK, m_DK;
 };
diff --git a/src/lib/block/aria/aria.cpp b/src/lib/block/aria/aria.cpp
index 54d85fe4d..4577b5077 100644
--- a/src/lib/block/aria/aria.cpp
+++ b/src/lib/block/aria/aria.cpp
@@ -249,12 +249,12 @@ void aria_ks_dk_transform(uint32_t& K0, uint32_t& K1, uint32_t& K2, uint32_t& K3
 /*
 * ARIA Key Schedule
 */
-void key_schedule(secure_vector<uint32_t>& ERK, secure_vector<uint32_t>& DRK, const uint8_t key[], size_t length) {
+void key_schedule(secure_vector<uint32_t>& ERK, secure_vector<uint32_t>& DRK, std::span<const uint8_t> key) {
    const uint32_t KRK[3][4] = {{0x517cc1b7, 0x27220a94, 0xfe13abe8, 0xfa9a6ee0},
                                {0x6db14acc, 0x9e21c820, 0xff28b1d5, 0xef5de2b0},
                                {0xdb92371d, 0x2126e970, 0x03249775, 0x04e8c90e}};
 
-   const size_t CK0 = (length / 8) - 2;
+   const size_t CK0 = (key.size() / 8) - 2;
    const size_t CK1 = (CK0 + 1) % 3;
    const size_t CK2 = (CK1 + 1) % 3;
 
@@ -263,10 +263,10 @@ void key_schedule(secure_vector<uint32_t>& ERK, secure_vector<uint32_t>& DRK, co
    uint32_t w2[4];
    uint32_t w3[4];
 
-   w0[0] = load_be<uint32_t>(key, 0);
-   w0[1] = load_be<uint32_t>(key, 1);
-   w0[2] = load_be<uint32_t>(key, 2);
-   w0[3] = load_be<uint32_t>(key, 3);
+   w0[0] = load_be<uint32_t>(key.data(), 0);
+   w0[1] = load_be<uint32_t>(key.data(), 1);
+   w0[2] = load_be<uint32_t>(key.data(), 2);
+   w0[3] = load_be<uint32_t>(key.data(), 3);
 
    w1[0] = w0[0] ^ KRK[CK0][0];
    w1[1] = w0[1] ^ KRK[CK0][1];
@@ -275,13 +275,13 @@ void key_schedule(secure_vector<uint32_t>& ERK, secure_vector<uint32_t>& DRK, co
 
    ARIA_FO(w1[0], w1[1], w1[2], w1[3]);
 
-   if(length == 24 || length == 32) {
-      w1[0] ^= load_be<uint32_t>(key, 4);
-      w1[1] ^= load_be<uint32_t>(key, 5);
+   if(key.size() == 24 || key.size() == 32) {
+      w1[0] ^= load_be<uint32_t>(key.data(), 4);
+      w1[1] ^= load_be<uint32_t>(key.data(), 5);
    }
-   if(length == 32) {
-      w1[2] ^= load_be<uint32_t>(key, 6);
-      w1[3] ^= load_be<uint32_t>(key, 7);
+   if(key.size() == 32) {
+      w1[2] ^= load_be<uint32_t>(key.data(), 6);
+      w1[3] ^= load_be<uint32_t>(key.data(), 7);
    }
 
    w2[0] = w1[0] ^ KRK[CK1][0];
@@ -308,11 +308,11 @@ void key_schedule(secure_vector<uint32_t>& ERK, secure_vector<uint32_t>& DRK, co
    w3[2] ^= w1[2];
    w3[3] ^= w1[3];
 
-   if(length == 16) {
+   if(key.size() == 16) {
       ERK.resize(4 * 13);
-   } else if(length == 24) {
+   } else if(key.size() == 24) {
       ERK.resize(4 * 15);
-   } else if(length == 32) {
+   } else if(key.size() == 32) {
       ERK.resize(4 * 17);
    }
 
@@ -330,11 +330,11 @@ void key_schedule(secure_vector<uint32_t>& ERK, secure_vector<uint32_t>& DRK, co
    ARIA_ROL128<67>(w3, w0, &ERK[44]);
    ARIA_ROL128<97>(w0, w1, &ERK[48]);
 
-   if(length == 24 || length == 32) {
+   if(key.size() == 24 || key.size() == 32) {
       ARIA_ROL128<97>(w1, w2, &ERK[52]);
       ARIA_ROL128<97>(w2, w3, &ERK[56]);
 
-      if(length == 32) {
+      if(key.size() == 32) {
          ARIA_ROL128<97>(w3, w0, &ERK[60]);
          ARIA_ROL128<109>(w0, w1, &ERK[64]);
       }
@@ -401,16 +401,16 @@ bool ARIA_256::has_keying_material() const {
    return !m_ERK.empty();
 }
 
-void ARIA_128::key_schedule(const uint8_t key[], size_t length) {
-   ARIA_F::key_schedule(m_ERK, m_DRK, key, length);
+void ARIA_128::key_schedule(std::span<const uint8_t> key) {
+   ARIA_F::key_schedule(m_ERK, m_DRK, key);
 }
 
-void ARIA_192::key_schedule(const uint8_t key[], size_t length) {
-   ARIA_F::key_schedule(m_ERK, m_DRK, key, length);
+void ARIA_192::key_schedule(std::span<const uint8_t> key) {
+   ARIA_F::key_schedule(m_ERK, m_DRK, key);
 }
 
-void ARIA_256::key_schedule(const uint8_t key[], size_t length) {
-   ARIA_F::key_schedule(m_ERK, m_DRK, key, length);
+void ARIA_256::key_schedule(std::span<const uint8_t> key) {
+   ARIA_F::key_schedule(m_ERK, m_DRK, key);
 }
 
 void ARIA_128::clear() {
diff --git a/src/lib/block/aria/aria.h b/src/lib/block/aria/aria.h
index 362634d16..4391219d6 100644
--- a/src/lib/block/aria/aria.h
+++ b/src/lib/block/aria/aria.h
@@ -37,7 +37,7 @@ class ARIA_128 final : public Block_Cipher_Fixed_Params<16, 16> {
       bool has_keying_material() const override;
 
    private:
-      void key_schedule(const uint8_t key[], size_t length) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       // Encryption and Decryption round keys.
       secure_vector<uint32_t> m_ERK, m_DRK;
@@ -60,7 +60,7 @@ class ARIA_192 final : public Block_Cipher_Fixed_Params<16, 24> {
       bool has_keying_material() const override;
 
    private:
-      void key_schedule(const uint8_t key[], size_t length) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       // Encryption and Decryption round keys.
       secure_vector<uint32_t> m_ERK, m_DRK;
@@ -83,7 +83,7 @@ class ARIA_256 final : public Block_Cipher_Fixed_Params<16, 32> {
       bool has_keying_material() const override;
 
    private:
-      void key_schedule(const uint8_t key[], size_t length) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       // Encryption and Decryption round keys.
       secure_vector<uint32_t> m_ERK, m_DRK;
diff --git a/src/lib/block/blowfish/blowfish.cpp b/src/lib/block/blowfish/blowfish.cpp
index b322ed232..611df1e1e 100644
--- a/src/lib/block/blowfish/blowfish.cpp
+++ b/src/lib/block/blowfish/blowfish.cpp
@@ -296,14 +296,14 @@ bool Blowfish::has_keying_material() const {
 /*
 * Blowfish Key Schedule
 */
-void Blowfish::key_schedule(const uint8_t key[], size_t length) {
+void Blowfish::key_schedule(std::span<const uint8_t> key) {
    m_P.resize(18);
    copy_mem(m_P.data(), P_INIT, 18);
 
    m_S.resize(1024);
    copy_mem(m_S.data(), S_INIT, 1024);
 
-   key_expansion(key, length, nullptr, 0);
+   key_expansion(key.data(), key.size(), nullptr, 0);
 }
 
 void Blowfish::key_expansion(const uint8_t key[], size_t length, const uint8_t salt[], size_t salt_length) {
diff --git a/src/lib/block/blowfish/blowfish.h b/src/lib/block/blowfish/blowfish.h
index 60508e510..982927b75 100644
--- a/src/lib/block/blowfish/blowfish.h
+++ b/src/lib/block/blowfish/blowfish.h
@@ -39,7 +39,7 @@ class BOTAN_TEST_API Blowfish final : public Block_Cipher_Fixed_Params<8, 1, 56>
       bool has_keying_material() const override;
 
    private:
-      void key_schedule(const uint8_t key[], size_t length) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       void key_expansion(const uint8_t key[], size_t key_length, const uint8_t salt[], size_t salt_length);
 
diff --git a/src/lib/block/camellia/camellia.cpp b/src/lib/block/camellia/camellia.cpp
index 88d39b06e..9b540659c 100644
--- a/src/lib/block/camellia/camellia.cpp
+++ b/src/lib/block/camellia/camellia.cpp
@@ -227,7 +227,7 @@ inline uint64_t left_rot_lo(uint64_t h, uint64_t l, size_t shift) {
 /*
 * Camellia Key Schedule
 */
-void key_schedule(secure_vector<uint64_t>& SK, const uint8_t key[], size_t length) {
+void key_schedule(secure_vector<uint64_t>& SK, std::span<const uint8_t> key) {
    const uint64_t Sigma1 = 0xA09E667F3BCC908B;
    const uint64_t Sigma2 = 0xB67AE8584CAA73B2;
    const uint64_t Sigma3 = 0xC6EF372FE94F82BE;
@@ -235,11 +235,11 @@ void key_schedule(secure_vector<uint64_t>& SK, const uint8_t key[], size_t lengt
    const uint64_t Sigma5 = 0x10E527FADE682D1D;
    const uint64_t Sigma6 = 0xB05688C2B3E6C1FD;
 
-   const uint64_t KL_H = load_be<uint64_t>(key, 0);
-   const uint64_t KL_L = load_be<uint64_t>(key, 1);
+   const uint64_t KL_H = load_be<uint64_t>(key.data(), 0);
+   const uint64_t KL_L = load_be<uint64_t>(key.data(), 1);
 
-   const uint64_t KR_H = (length >= 24) ? load_be<uint64_t>(key, 2) : 0;
-   const uint64_t KR_L = (length == 32) ? load_be<uint64_t>(key, 3) : ((length == 24) ? ~KR_H : 0);
+   const uint64_t KR_H = (key.size() >= 24) ? load_be<uint64_t>(key.data(), 2) : 0;
+   const uint64_t KR_L = (key.size() == 32) ? load_be<uint64_t>(key.data(), 3) : ((key.size() == 24) ? ~KR_H : 0);
 
    uint64_t D1 = KL_H ^ KR_H;
    uint64_t D2 = KL_L ^ KR_L;
@@ -261,7 +261,7 @@ void key_schedule(secure_vector<uint64_t>& SK, const uint8_t key[], size_t lengt
    const uint64_t KB_H = D1;
    const uint64_t KB_L = D2;
 
-   if(length == 16) {
+   if(key.size() == 16) {
       SK.resize(26);
 
       SK[0] = KL_H;
@@ -382,16 +382,16 @@ bool Camellia_256::has_keying_material() const {
    return !m_SK.empty();
 }
 
-void Camellia_128::key_schedule(const uint8_t key[], size_t length) {
-   Camellia_F::key_schedule(m_SK, key, length);
+void Camellia_128::key_schedule(std::span<const uint8_t> key) {
+   Camellia_F::key_schedule(m_SK, key);
 }
 
-void Camellia_192::key_schedule(const uint8_t key[], size_t length) {
-   Camellia_F::key_schedule(m_SK, key, length);
+void Camellia_192::key_schedule(std::span<const uint8_t> key) {
+   Camellia_F::key_schedule(m_SK, key);
 }
 
-void Camellia_256::key_schedule(const uint8_t key[], size_t length) {
-   Camellia_F::key_schedule(m_SK, key, length);
+void Camellia_256::key_schedule(std::span<const uint8_t> key) {
+   Camellia_F::key_schedule(m_SK, key);
 }
 
 void Camellia_128::clear() {
diff --git a/src/lib/block/camellia/camellia.h b/src/lib/block/camellia/camellia.h
index 7ac250a18..065caf0a4 100644
--- a/src/lib/block/camellia/camellia.h
+++ b/src/lib/block/camellia/camellia.h
@@ -29,7 +29,7 @@ class Camellia_128 final : public Block_Cipher_Fixed_Params<16, 16> {
       bool has_keying_material() const override;
 
    private:
-      void key_schedule(const uint8_t key[], size_t length) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       secure_vector<uint64_t> m_SK;
 };
@@ -51,7 +51,7 @@ class Camellia_192 final : public Block_Cipher_Fixed_Params<16, 24> {
       bool has_keying_material() const override;
 
    private:
-      void key_schedule(const uint8_t key[], size_t length) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       secure_vector<uint64_t> m_SK;
 };
@@ -73,7 +73,7 @@ class Camellia_256 final : public Block_Cipher_Fixed_Params<16, 32> {
       bool has_keying_material() const override;
 
    private:
-      void key_schedule(const uint8_t key[], size_t length) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       secure_vector<uint64_t> m_SK;
 };
diff --git a/src/lib/block/cascade/cascade.cpp b/src/lib/block/cascade/cascade.cpp
index 9ad43f3f9..01781b2de 100644
--- a/src/lib/block/cascade/cascade.cpp
+++ b/src/lib/block/cascade/cascade.cpp
@@ -8,6 +8,7 @@
 #include <botan/internal/cascade.h>
 
 #include <botan/internal/fmt.h>
+#include <botan/internal/stl_util.h>
 
 namespace Botan {
 
@@ -27,11 +28,11 @@ void Cascade_Cipher::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks)
    m_cipher1->decrypt_n(out, out, c1_blocks);
 }
 
-void Cascade_Cipher::key_schedule(const uint8_t key[], size_t /*length*/) {
-   const uint8_t* key2 = key + m_cipher1->maximum_keylength();
+void Cascade_Cipher::key_schedule(std::span<const uint8_t> key) {
+   BufferSlicer keys(key);
 
-   m_cipher1->set_key(key, m_cipher1->maximum_keylength());
-   m_cipher2->set_key(key2, m_cipher2->maximum_keylength());
+   m_cipher1->set_key(keys.take(m_cipher1->maximum_keylength()));
+   m_cipher2->set_key(keys.take(m_cipher2->maximum_keylength()));
 }
 
 void Cascade_Cipher::clear() {
diff --git a/src/lib/block/cascade/cascade.h b/src/lib/block/cascade/cascade.h
index 3784694c6..c208c4c39 100644
--- a/src/lib/block/cascade/cascade.h
+++ b/src/lib/block/cascade/cascade.h
@@ -43,7 +43,7 @@ class Cascade_Cipher final : public BlockCipher {
       Cascade_Cipher& operator=(const Cascade_Cipher&) = delete;
 
    private:
-      void key_schedule(const uint8_t[], size_t) override;
+      void key_schedule(std::span<const uint8_t>) override;
 
       std::unique_ptr<BlockCipher> m_cipher1, m_cipher2;
       size_t m_block_size;
diff --git a/src/lib/block/cast128/cast128.cpp b/src/lib/block/cast128/cast128.cpp
index ade8668f0..5d105a83f 100644
--- a/src/lib/block/cast128/cast128.cpp
+++ b/src/lib/block/cast128/cast128.cpp
@@ -337,12 +337,12 @@ bool CAST_128::has_keying_material() const {
 /*
 * CAST-128 Key Schedule
 */
-void CAST_128::key_schedule(const uint8_t key[], size_t length) {
+void CAST_128::key_schedule(std::span<const uint8_t> key) {
    m_MK.resize(48);
    m_RK.resize(48);
 
    secure_vector<uint8_t> key16(16);
-   copy_mem(key16.data(), key, length);
+   copy_mem(key16.data(), key.data(), key.size());
 
    secure_vector<uint32_t> X(4);
    for(size_t i = 0; i != 4; ++i) {
diff --git a/src/lib/block/cast128/cast128.h b/src/lib/block/cast128/cast128.h
index 2d1fce130..33d16f97d 100644
--- a/src/lib/block/cast128/cast128.h
+++ b/src/lib/block/cast128/cast128.h
@@ -29,7 +29,7 @@ class CAST_128 final : public Block_Cipher_Fixed_Params<8, 11, 16> {
       bool has_keying_material() const override;
 
    private:
-      void key_schedule(const uint8_t[], size_t) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       static void cast_ks(secure_vector<uint32_t>& ks, secure_vector<uint32_t>& user_key);
 
diff --git a/src/lib/block/des/des.cpp b/src/lib/block/des/des.cpp
index b685522b5..b89d9126a 100644
--- a/src/lib/block/des/des.cpp
+++ b/src/lib/block/des/des.cpp
@@ -332,9 +332,9 @@ bool DES::has_keying_material() const {
 /*
 * DES Key Schedule
 */
-void DES::key_schedule(const uint8_t key[], size_t /*length*/) {
+void DES::key_schedule(std::span<const uint8_t> key) {
    m_round_key.resize(32);
-   des_key_schedule(m_round_key.data(), key);
+   des_key_schedule(m_round_key.data(), key.data());
 }
 
 void DES::clear() {
@@ -442,13 +442,13 @@ bool TripleDES::has_keying_material() const {
 /*
 * TripleDES Key Schedule
 */
-void TripleDES::key_schedule(const uint8_t key[], size_t length) {
+void TripleDES::key_schedule(std::span<const uint8_t> key) {
    m_round_key.resize(3 * 32);
-   des_key_schedule(&m_round_key[0], key);
-   des_key_schedule(&m_round_key[32], key + 8);
+   des_key_schedule(&m_round_key[0], key.first(8).data());
+   des_key_schedule(&m_round_key[32], key.subspan(8, 8).data());
 
-   if(length == 24) {
-      des_key_schedule(&m_round_key[64], key + 16);
+   if(key.size() == 24) {
+      des_key_schedule(&m_round_key[64], key.last(8).data());
    } else {
       copy_mem(&m_round_key[64], &m_round_key[0], 32);
    }
diff --git a/src/lib/block/des/des.h b/src/lib/block/des/des.h
index dc0da7a80..fd46b0919 100644
--- a/src/lib/block/des/des.h
+++ b/src/lib/block/des/des.h
@@ -29,7 +29,7 @@ class DES final : public Block_Cipher_Fixed_Params<8, 8> {
       bool has_keying_material() const override;
 
    private:
-      void key_schedule(const uint8_t[], size_t) override;
+      void key_schedule(std::span<const uint8_t>) override;
 
       secure_vector<uint32_t> m_round_key;
 };
@@ -51,7 +51,7 @@ class TripleDES final : public Block_Cipher_Fixed_Params<8, 16, 24, 8> {
       bool has_keying_material() const override;
 
    private:
-      void key_schedule(const uint8_t[], size_t) override;
+      void key_schedule(std::span<const uint8_t>) override;
 
       secure_vector<uint32_t> m_round_key;
 };
diff --git a/src/lib/block/gost_28147/gost_28147.cpp b/src/lib/block/gost_28147/gost_28147.cpp
index 7a3a8faa3..3c94ec431 100644
--- a/src/lib/block/gost_28147/gost_28147.cpp
+++ b/src/lib/block/gost_28147/gost_28147.cpp
@@ -150,10 +150,10 @@ bool GOST_28147_89::has_keying_material() const {
 /*
 * GOST Key Schedule
 */
-void GOST_28147_89::key_schedule(const uint8_t key[], size_t /*length*/) {
+void GOST_28147_89::key_schedule(std::span<const uint8_t> key) {
    m_EK.resize(8);
    for(size_t i = 0; i != 8; ++i) {
-      m_EK[i] = load_le<uint32_t>(key, i);
+      m_EK[i] = load_le<uint32_t>(key.data(), i);
    }
 }
 
diff --git a/src/lib/block/gost_28147/gost_28147.h b/src/lib/block/gost_28147/gost_28147.h
index e3489d1a8..bb0f19911 100644
--- a/src/lib/block/gost_28147/gost_28147.h
+++ b/src/lib/block/gost_28147/gost_28147.h
@@ -81,7 +81,7 @@ class GOST_28147_89 final : public Block_Cipher_Fixed_Params<8, 32> {
             m_SBOX(other_SBOX), m_EK(8), m_name(name) {}
 
    private:
-      void key_schedule(const uint8_t[], size_t) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       /*
       * The sbox is not secret, this is just a larger expansion of it
diff --git a/src/lib/block/idea/idea.cpp b/src/lib/block/idea/idea.cpp
index cda79be00..164af12f8 100644
--- a/src/lib/block/idea/idea.cpp
+++ b/src/lib/block/idea/idea.cpp
@@ -169,18 +169,18 @@ bool IDEA::has_keying_material() const {
 /*
 * IDEA Key Schedule
 */
-void IDEA::key_schedule(const uint8_t key[], size_t /*length*/) {
+void IDEA::key_schedule(std::span<const uint8_t> key) {
    m_EK.resize(52);
    m_DK.resize(52);
 
-   CT::poison(key, 16);
+   CT::poison(key.data(), 16);
    CT::poison(m_EK.data(), 52);
    CT::poison(m_DK.data(), 52);
 
    secure_vector<uint64_t> K(2);
 
-   K[0] = load_be<uint64_t>(key, 0);
-   K[1] = load_be<uint64_t>(key, 1);
+   K[0] = load_be<uint64_t>(key.data(), 0);
+   K[1] = load_be<uint64_t>(key.data(), 1);
 
    for(size_t off = 0; off != 48; off += 8) {
       for(size_t i = 0; i != 8; ++i) {
@@ -214,7 +214,7 @@ void IDEA::key_schedule(const uint8_t key[], size_t /*length*/) {
 
    std::swap(m_DK[49], m_DK[50]);
 
-   CT::unpoison(key, 16);
+   CT::unpoison(key.data(), 16);
    CT::unpoison(m_EK.data(), 52);
    CT::unpoison(m_DK.data(), 52);
 }
diff --git a/src/lib/block/idea/idea.h b/src/lib/block/idea/idea.h
index c23f00e8a..1d484edfb 100644
--- a/src/lib/block/idea/idea.h
+++ b/src/lib/block/idea/idea.h
@@ -36,7 +36,7 @@ class IDEA final : public Block_Cipher_Fixed_Params<8, 16> {
       static void sse2_idea_op_8(const uint8_t in[64], uint8_t out[64], const uint16_t EK[52]);
 #endif
 
-      void key_schedule(const uint8_t[], size_t) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       secure_vector<uint16_t> m_EK, m_DK;
 };
diff --git a/src/lib/block/lion/lion.cpp b/src/lib/block/lion/lion.cpp
index 4fe5e2d99..a1261999e 100644
--- a/src/lib/block/lion/lion.cpp
+++ b/src/lib/block/lion/lion.cpp
@@ -79,17 +79,17 @@ bool Lion::has_keying_material() const {
 /*
 * Lion Key Schedule
 */
-void Lion::key_schedule(const uint8_t key[], size_t length) {
+void Lion::key_schedule(std::span<const uint8_t> key) {
    clear();
 
-   const size_t half = length / 2;
+   const size_t half = key.size() / 2;
 
    m_key1.resize(left_size());
    m_key2.resize(left_size());
    clear_mem(m_key1.data(), m_key1.size());
    clear_mem(m_key2.data(), m_key2.size());
-   copy_mem(m_key1.data(), key, half);
-   copy_mem(m_key2.data(), key + half, half);
+   copy_mem(m_key1.data(), key.data(), half);
+   copy_mem(m_key2.data(), key.subspan(half, half).data(), half);
 }
 
 /*
diff --git a/src/lib/block/lion/lion.h b/src/lib/block/lion/lion.h
index 95a8cae7b..6fb22c0bb 100644
--- a/src/lib/block/lion/lion.h
+++ b/src/lib/block/lion/lion.h
@@ -46,7 +46,7 @@ class Lion final : public BlockCipher {
       Lion(std::unique_ptr<HashFunction> hash, std::unique_ptr<StreamCipher> cipher, size_t block_size);
 
    private:
-      void key_schedule(const uint8_t[], size_t) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       size_t left_size() const { return m_hash->output_length(); }
 
diff --git a/src/lib/block/noekeon/noekeon.cpp b/src/lib/block/noekeon/noekeon.cpp
index 85ec0d117..acd7587e3 100644
--- a/src/lib/block/noekeon/noekeon.cpp
+++ b/src/lib/block/noekeon/noekeon.cpp
@@ -198,11 +198,11 @@ bool Noekeon::has_keying_material() const {
 /*
 * Noekeon Key Schedule
 */
-void Noekeon::key_schedule(const uint8_t key[], size_t /*length*/) {
-   uint32_t A0 = load_be<uint32_t>(key, 0);
-   uint32_t A1 = load_be<uint32_t>(key, 1);
-   uint32_t A2 = load_be<uint32_t>(key, 2);
-   uint32_t A3 = load_be<uint32_t>(key, 3);
+void Noekeon::key_schedule(std::span<const uint8_t> key) {
+   uint32_t A0 = load_be<uint32_t>(key.data(), 0);
+   uint32_t A1 = load_be<uint32_t>(key.data(), 1);
+   uint32_t A2 = load_be<uint32_t>(key.data(), 2);
+   uint32_t A3 = load_be<uint32_t>(key.data(), 3);
 
    for(size_t i = 0; i != 16; ++i) {
       A0 ^= RC[i];
diff --git a/src/lib/block/noekeon/noekeon.h b/src/lib/block/noekeon/noekeon.h
index 234227444..fb0bb6490 100644
--- a/src/lib/block/noekeon/noekeon.h
+++ b/src/lib/block/noekeon/noekeon.h
@@ -41,7 +41,7 @@ class Noekeon final : public Block_Cipher_Fixed_Params<16, 16> {
       */
       static const uint8_t RC[17];
 
-      void key_schedule(const uint8_t[], size_t) override;
+      void key_schedule(std::span<const uint8_t> key) override;
       secure_vector<uint32_t> m_EK, m_DK;
 };
 
diff --git a/src/lib/block/seed/seed.cpp b/src/lib/block/seed/seed.cpp
index 98b8973e7..f3b9c7504 100644
--- a/src/lib/block/seed/seed.cpp
+++ b/src/lib/block/seed/seed.cpp
@@ -153,7 +153,7 @@ bool SEED::has_keying_material() const {
 /*
 * SEED Key Schedule
 */
-void SEED::key_schedule(const uint8_t key[], size_t /*length*/) {
+void SEED::key_schedule(std::span<const uint8_t> key) {
    const uint32_t RC[16] = {0x9E3779B9,
                             0x3C6EF373,
                             0x78DDE6E6,
@@ -174,7 +174,7 @@ void SEED::key_schedule(const uint8_t key[], size_t /*length*/) {
    secure_vector<uint32_t> WK(4);
 
    for(size_t i = 0; i != 4; ++i) {
-      WK[i] = load_be<uint32_t>(key, i);
+      WK[i] = load_be<uint32_t>(key.data(), i);
    }
 
    m_K.resize(32);
diff --git a/src/lib/block/seed/seed.h b/src/lib/block/seed/seed.h
index cefbb26bc..708d658ec 100644
--- a/src/lib/block/seed/seed.h
+++ b/src/lib/block/seed/seed.h
@@ -29,7 +29,7 @@ class SEED final : public Block_Cipher_Fixed_Params<16, 16> {
       bool has_keying_material() const override;
 
    private:
-      void key_schedule(const uint8_t[], size_t) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       secure_vector<uint32_t> m_K;
 };
diff --git a/src/lib/block/serpent/serpent.cpp b/src/lib/block/serpent/serpent.cpp
index bc6261c09..8f4dc3084 100644
--- a/src/lib/block/serpent/serpent.cpp
+++ b/src/lib/block/serpent/serpent.cpp
@@ -320,17 +320,17 @@ bool Serpent::has_keying_material() const {
 /*
 * Serpent Key Schedule
 */
-void Serpent::key_schedule(const uint8_t key[], size_t length) {
+void Serpent::key_schedule(std::span<const uint8_t> key) {
    using namespace Botan::Serpent_F;
 
    const uint32_t PHI = 0x9E3779B9;
 
    secure_vector<uint32_t> W(140);
-   for(size_t i = 0; i != length / 4; ++i) {
-      W[i] = load_le<uint32_t>(key, i);
+   for(size_t i = 0; i != key.size() / 4; ++i) {
+      W[i] = load_le<uint32_t>(key.data(), i);
    }
 
-   W[length / 4] |= uint32_t(1) << ((length % 4) * 8);
+   W[key.size() / 4] |= uint32_t(1) << ((key.size() % 4) * 8);
 
    for(size_t i = 8; i != 140; ++i) {
       uint32_t wi = W[i - 8] ^ W[i - 5] ^ W[i - 3] ^ W[i - 1] ^ PHI ^ uint32_t(i - 8);
diff --git a/src/lib/block/serpent/serpent.h b/src/lib/block/serpent/serpent.h
index a3ab53eaf..2c2447064 100644
--- a/src/lib/block/serpent/serpent.h
+++ b/src/lib/block/serpent/serpent.h
@@ -48,7 +48,7 @@ class Serpent final : public Block_Cipher_Fixed_Params<16, 16, 32, 8> {
       void avx512_decrypt_16(const uint8_t in[16 * 16], uint8_t out[16 * 16]) const;
 #endif
 
-      void key_schedule(const uint8_t key[], size_t length) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       secure_vector<uint32_t> m_round_key;
 };
diff --git a/src/lib/block/shacal2/shacal2.cpp b/src/lib/block/shacal2/shacal2.cpp
index 4ac501d3f..9ee092c7e 100644
--- a/src/lib/block/shacal2/shacal2.cpp
+++ b/src/lib/block/shacal2/shacal2.cpp
@@ -169,7 +169,7 @@ bool SHACAL2::has_keying_material() const {
 /*
 * SHACAL2 Key Schedule
 */
-void SHACAL2::key_schedule(const uint8_t key[], size_t len) {
+void SHACAL2::key_schedule(std::span<const uint8_t> key) {
    const uint32_t RC[64] = {
       0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
       0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
@@ -186,7 +186,7 @@ void SHACAL2::key_schedule(const uint8_t key[], size_t len) {
       clear_mem(m_RK.data(), m_RK.size());
    }
 
-   load_be(m_RK.data(), key, len / 4);
+   load_be(m_RK.data(), key.data(), key.size() / 4);
 
    for(size_t i = 16; i != 64; ++i) {
       const uint32_t sigma0_15 = sigma<7, 18, 3>(m_RK[i - 15]);
diff --git a/src/lib/block/shacal2/shacal2.h b/src/lib/block/shacal2/shacal2.h
index a167aa0c0..50ad76a01 100644
--- a/src/lib/block/shacal2/shacal2.h
+++ b/src/lib/block/shacal2/shacal2.h
@@ -31,7 +31,7 @@ class SHACAL2 final : public Block_Cipher_Fixed_Params<32, 16, 64, 4> {
       bool has_keying_material() const override;
 
    private:
-      void key_schedule(const uint8_t[], size_t) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
 #if defined(BOTAN_HAS_SHACAL2_SIMD)
       void simd_encrypt_4(const uint8_t in[], uint8_t out[]) const;
diff --git a/src/lib/block/sm4/sm4.cpp b/src/lib/block/sm4/sm4.cpp
index 538a3b947..ba93d9b80 100644
--- a/src/lib/block/sm4/sm4.cpp
+++ b/src/lib/block/sm4/sm4.cpp
@@ -282,7 +282,7 @@ bool SM4::has_keying_material() const {
 /*
 * SM4 Key Schedule
 */
-void SM4::key_schedule(const uint8_t key[], size_t /*length*/) {
+void SM4::key_schedule(std::span<const uint8_t> key) {
    // System parameter or family key
    const uint32_t FK[4] = {0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc};
 
@@ -293,10 +293,10 @@ void SM4::key_schedule(const uint8_t key[], size_t /*length*/) {
                             0x10171E25, 0x2C333A41, 0x484F565D, 0x646B7279};
 
    secure_vector<uint32_t> K(4);
-   K[0] = load_be<uint32_t>(key, 0) ^ FK[0];
-   K[1] = load_be<uint32_t>(key, 1) ^ FK[1];
-   K[2] = load_be<uint32_t>(key, 2) ^ FK[2];
-   K[3] = load_be<uint32_t>(key, 3) ^ FK[3];
+   K[0] = load_be<uint32_t>(key.data(), 0) ^ FK[0];
+   K[1] = load_be<uint32_t>(key.data(), 1) ^ FK[1];
+   K[2] = load_be<uint32_t>(key.data(), 2) ^ FK[2];
+   K[3] = load_be<uint32_t>(key.data(), 3) ^ FK[3];
 
    m_RK.resize(32);
    for(size_t i = 0; i != 32; ++i) {
diff --git a/src/lib/block/sm4/sm4.h b/src/lib/block/sm4/sm4.h
index c7e943974..ab510b8c8 100644
--- a/src/lib/block/sm4/sm4.h
+++ b/src/lib/block/sm4/sm4.h
@@ -31,7 +31,7 @@ class SM4 final : public Block_Cipher_Fixed_Params<16, 16> {
       bool has_keying_material() const override;
 
    private:
-      void key_schedule(const uint8_t[], size_t) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
 #if defined(BOTAN_HAS_SM4_ARMV8)
       void sm4_armv8_encrypt(const uint8_t in[], uint8_t out[], size_t blocks) const;
diff --git a/src/lib/block/threefish_512/threefish_512.cpp b/src/lib/block/threefish_512/threefish_512.cpp
index 308354726..5b5f7ce3f 100644
--- a/src/lib/block/threefish_512/threefish_512.cpp
+++ b/src/lib/block/threefish_512/threefish_512.cpp
@@ -256,12 +256,12 @@ bool Threefish_512::has_keying_material() const {
    return !m_K.empty();
 }
 
-void Threefish_512::key_schedule(const uint8_t key[], size_t /*length*/) {
+void Threefish_512::key_schedule(std::span<const uint8_t> key) {
    // todo: define key schedule for smaller keys
    m_K.resize(9);
 
    for(size_t i = 0; i != 8; ++i) {
-      m_K[i] = load_le<uint64_t>(key, i);
+      m_K[i] = load_le<uint64_t>(key.data(), i);
    }
 
    m_K[8] = m_K[0] ^ m_K[1] ^ m_K[2] ^ m_K[3] ^ m_K[4] ^ m_K[5] ^ m_K[6] ^ m_K[7] ^ 0x1BD11BDAA9FC1A22;
diff --git a/src/lib/block/threefish_512/threefish_512.h b/src/lib/block/threefish_512/threefish_512.h
index 02e9caaf3..3549c26c1 100644
--- a/src/lib/block/threefish_512/threefish_512.h
+++ b/src/lib/block/threefish_512/threefish_512.h
@@ -31,7 +31,7 @@ class Threefish_512 final : public Block_Cipher_Fixed_Params<64, 64, 0, 1, Tweak
       bool has_keying_material() const override;
 
    private:
-      void key_schedule(const uint8_t key[], size_t key_len) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       // Interface for Skein
       friend class Skein_512;
diff --git a/src/lib/block/twofish/twofish.cpp b/src/lib/block/twofish/twofish.cpp
index 1d54a1634..1f0101d66 100644
--- a/src/lib/block/twofish/twofish.cpp
+++ b/src/lib/block/twofish/twofish.cpp
@@ -190,13 +190,13 @@ bool Twofish::has_keying_material() const {
 /*
 * Twofish Key Schedule
 */
-void Twofish::key_schedule(const uint8_t key[], size_t length) {
+void Twofish::key_schedule(std::span<const uint8_t> key) {
    m_SB.resize(1024);
    m_RK.resize(40);
 
    secure_vector<uint8_t> S(16);
 
-   for(size_t i = 0; i != length; ++i) {
+   for(size_t i = 0; i != key.size(); ++i) {
       /*
       * Do one column of the RS matrix multiplcation
       */
@@ -215,7 +215,7 @@ void Twofish::key_schedule(const uint8_t key[], size_t length) {
       }
    }
 
-   if(length == 16) {
+   if(key.size() == 16) {
       for(size_t i = 0; i != 256; ++i) {
          m_SB[i] = MDS0[Q0[Q0[i] ^ S[0]] ^ S[4]];
          m_SB[256 + i] = MDS1[Q0[Q1[i] ^ S[1]] ^ S[5]];
@@ -235,7 +235,7 @@ void Twofish::key_schedule(const uint8_t key[], size_t length) {
          m_RK[i] = X;
          m_RK[i + 1] = rotl<9>(Y);
       }
-   } else if(length == 24) {
+   } else if(key.size() == 24) {
       for(size_t i = 0; i != 256; ++i) {
          m_SB[i] = MDS0[Q0[Q0[Q1[i] ^ S[0]] ^ S[4]] ^ S[8]];
          m_SB[256 + i] = MDS1[Q0[Q1[Q1[i] ^ S[1]] ^ S[5]] ^ S[9]];
@@ -258,7 +258,7 @@ void Twofish::key_schedule(const uint8_t key[], size_t length) {
          m_RK[i] = X;
          m_RK[i + 1] = rotl<9>(Y);
       }
-   } else if(length == 32) {
+   } else if(key.size() == 32) {
       for(size_t i = 0; i != 256; ++i) {
          m_SB[i] = MDS0[Q0[Q0[Q1[Q1[i] ^ S[0]] ^ S[4]] ^ S[8]] ^ S[12]];
          m_SB[256 + i] = MDS1[Q0[Q1[Q1[Q0[i] ^ S[1]] ^ S[5]] ^ S[9]] ^ S[13]];
diff --git a/src/lib/block/twofish/twofish.h b/src/lib/block/twofish/twofish.h
index 29fceba8d..cd78c736e 100644
--- a/src/lib/block/twofish/twofish.h
+++ b/src/lib/block/twofish/twofish.h
@@ -29,7 +29,7 @@ class Twofish final : public Block_Cipher_Fixed_Params<16, 16, 32, 8> {
       bool has_keying_material() const override;
 
    private:
-      void key_schedule(const uint8_t[], size_t) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       static const uint32_t MDS0[256];
       static const uint32_t MDS1[256];
diff --git a/src/lib/hash/blake2/blake2b.cpp b/src/lib/hash/blake2/blake2b.cpp
index af0cc585f..ac26b6081 100644
--- a/src/lib/hash/blake2/blake2b.cpp
+++ b/src/lib/hash/blake2/blake2b.cpp
@@ -13,6 +13,7 @@
 #include <botan/internal/fmt.h>
 #include <botan/internal/loadstor.h>
 #include <botan/internal/rotate.h>
+#include <botan/internal/stl_util.h>
 #include <algorithm>
 
 namespace Botan {
@@ -147,47 +148,47 @@ void BLAKE2b::compress(const uint8_t* input, size_t blocks, uint64_t increment)
    }
 }
 
-void BLAKE2b::add_data(const uint8_t input[], size_t length) {
-   if(length == 0) {
+void BLAKE2b::add_data(std::span<const uint8_t> input) {
+   if(input.empty()) {
       return;
    }
 
+   BufferSlicer in(input);
+
    if(m_bufpos > 0) {
       if(m_bufpos < BLAKE2B_BLOCKBYTES) {
-         const size_t take = std::min(BLAKE2B_BLOCKBYTES - m_bufpos, length);
-         copy_mem(&m_buffer[m_bufpos], input, take);
-         m_bufpos += take;
-         length -= take;
-         input += take;
+         const auto part = in.take(std::min(BLAKE2B_BLOCKBYTES - m_bufpos, in.remaining()));
+         copy_mem(&m_buffer[m_bufpos], part.data(), part.size());
+         m_bufpos += part.size();
       }
 
-      if(m_bufpos == m_buffer.size() && length > 0) {
+      if(m_bufpos == m_buffer.size() && !in.empty()) {
          compress(m_buffer.data(), 1, BLAKE2B_BLOCKBYTES);
          m_bufpos = 0;
       }
    }
 
-   if(length > BLAKE2B_BLOCKBYTES) {
-      const size_t full_blocks = ((length - 1) / BLAKE2B_BLOCKBYTES);
-      compress(input, full_blocks, BLAKE2B_BLOCKBYTES);
-
-      input += full_blocks * BLAKE2B_BLOCKBYTES;
-      length -= full_blocks * BLAKE2B_BLOCKBYTES;
+   if(in.remaining() > BLAKE2B_BLOCKBYTES) {
+      const size_t full_blocks = ((in.remaining() - 1) / BLAKE2B_BLOCKBYTES);
+      compress(in.take(BLAKE2B_BLOCKBYTES * full_blocks).data(), full_blocks, BLAKE2B_BLOCKBYTES);
    }
 
-   if(length > 0) {
-      copy_mem(&m_buffer[m_bufpos], input, length);
-      m_bufpos += length;
+   if(!in.empty()) {
+      const auto take = in.remaining();
+      copy_mem(&m_buffer[m_bufpos], in.take(take).data(), take);
+      m_bufpos += take;
    }
+
+   BOTAN_ASSERT_NOMSG(in.empty());
 }
 
-void BLAKE2b::final_result(uint8_t output[]) {
+void BLAKE2b::final_result(std::span<uint8_t> output) {
    if(m_bufpos != BLAKE2B_BLOCKBYTES) {
       clear_mem(&m_buffer[m_bufpos], BLAKE2B_BLOCKBYTES - m_bufpos);
    }
    m_F = 0xFFFFFFFFFFFFFFFF;
    compress(m_buffer.data(), 1, m_bufpos);
-   copy_out_vec_le(output, output_length(), m_H);
+   copy_out_vec_le(output.data(), output_length(), m_H);
    state_init();
 }
 
@@ -211,18 +212,18 @@ bool BLAKE2b::has_keying_material() const {
    return m_key_size > 0;
 }
 
-void BLAKE2b::key_schedule(const uint8_t key[], size_t length) {
-   BOTAN_ASSERT_NOMSG(length <= m_buffer.size());
+void BLAKE2b::key_schedule(std::span<const uint8_t> key) {
+   BOTAN_ASSERT_NOMSG(key.size() <= m_buffer.size());
 
-   m_key_size = length;
+   m_key_size = key.size();
    m_padded_key_buffer.resize(m_buffer.size());
 
-   if(m_padded_key_buffer.size() > length) {
-      size_t padding = m_padded_key_buffer.size() - length;
-      clear_mem(m_padded_key_buffer.data() + length, padding);
+   if(m_padded_key_buffer.size() > m_key_size) {
+      size_t padding = m_padded_key_buffer.size() - m_key_size;
+      clear_mem(m_padded_key_buffer.data() + m_key_size, padding);
    }
 
-   copy_mem(m_padded_key_buffer.data(), key, length);
+   copy_mem(m_padded_key_buffer.data(), key.data(), key.size());
    state_init();
 }
 
diff --git a/src/lib/hash/blake2/blake2b.h b/src/lib/hash/blake2/blake2b.h
index a2234d25f..1ce4596a7 100644
--- a/src/lib/hash/blake2/blake2b.h
+++ b/src/lib/hash/blake2/blake2b.h
@@ -46,10 +46,10 @@ class BLAKE2b final : public HashFunction,
    protected:
       friend class BLAKE2bMAC;
 
-      void key_schedule(const uint8_t key[], size_t length) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
-      void add_data(const uint8_t input[], size_t length) override;
-      void final_result(uint8_t out[]) override;
+      void add_data(std::span<const uint8_t> input) override;
+      void final_result(std::span<uint8_t> out) override;
 
    private:
       void state_init();
diff --git a/src/lib/hash/checksum/adler32/adler32.cpp b/src/lib/hash/checksum/adler32/adler32.cpp
index 30a7cec58..af17ae11c 100644
--- a/src/lib/hash/checksum/adler32/adler32.cpp
+++ b/src/lib/hash/checksum/adler32/adler32.cpp
@@ -68,23 +68,22 @@ void adler32_update(const uint8_t input[], size_t length, uint16_t& S1, uint16_t
 /*
 * Update an Adler32 Checksum
 */
-void Adler32::add_data(const uint8_t input[], size_t length) {
+void Adler32::add_data(std::span<const uint8_t> input) {
    const size_t PROCESS_AMOUNT = 5552;
 
-   while(length >= PROCESS_AMOUNT) {
-      adler32_update(input, PROCESS_AMOUNT, m_S1, m_S2);
-      input += PROCESS_AMOUNT;
-      length -= PROCESS_AMOUNT;
+   while(input.size() >= PROCESS_AMOUNT) {
+      adler32_update(input.data(), PROCESS_AMOUNT, m_S1, m_S2);
+      input = input.last(input.size() - PROCESS_AMOUNT);
    }
 
-   adler32_update(input, length, m_S1, m_S2);
+   adler32_update(input.data(), input.size(), m_S1, m_S2);
 }
 
 /*
 * Finalize an Adler32 Checksum
 */
-void Adler32::final_result(uint8_t output[]) {
-   store_be(output, m_S2, m_S1);
+void Adler32::final_result(std::span<uint8_t> output) {
+   store_be(output.data(), m_S2, m_S1);
    clear();
 }
 
diff --git a/src/lib/hash/checksum/adler32/adler32.h b/src/lib/hash/checksum/adler32/adler32.h
index 5be1d0ca7..7f7a2bc2d 100644
--- a/src/lib/hash/checksum/adler32/adler32.h
+++ b/src/lib/hash/checksum/adler32/adler32.h
@@ -35,8 +35,8 @@ class Adler32 final : public HashFunction {
       ~Adler32() override { clear(); }
 
    private:
-      void add_data(const uint8_t[], size_t) override;
-      void final_result(uint8_t[]) override;
+      void add_data(std::span<const uint8_t>) override;
+      void final_result(std::span<uint8_t>) override;
       uint16_t m_S1, m_S2;
 };
 
diff --git a/src/lib/hash/checksum/crc24/crc24.cpp b/src/lib/hash/checksum/crc24/crc24.cpp
index c849ca1bb..1c94a648b 100644
--- a/src/lib/hash/checksum/crc24/crc24.cpp
+++ b/src/lib/hash/checksum/crc24/crc24.cpp
@@ -172,31 +172,30 @@ std::unique_ptr<HashFunction> CRC24::copy_state() const {
 *    T3[j] = (T2[j] >> 8) ^ T0[ T2[j] & 0xFF ]
 *
 */
-void CRC24::add_data(const uint8_t input[], size_t length) {
+void CRC24::add_data(std::span<const uint8_t> input) {
    uint32_t tmp = m_crc;
 
    // Input is word aligned if WA & input == 0
    static const uint8_t WA = sizeof(size_t) - 1;
 
    // Ensure input is word aligned before processing in parallel
-   for(; length && (reinterpret_cast<uintptr_t>(input) & WA); length--) {
-      tmp = process8(tmp, *input++);
+   for(; !input.empty() && (reinterpret_cast<uintptr_t>(input.data()) & WA); input = input.last(input.size() - 1)) {
+      tmp = process8(tmp, input.front());
    }
 
-   while(length >= 16) {
+   while(input.size() >= 16) {
       uint32_t d[4];
-      load_le(d, input, 4);
+      load_le(d, input.data(), 4);
       tmp = process32(tmp, d[0]);
       tmp = process32(tmp, d[1]);
       tmp = process32(tmp, d[2]);
       tmp = process32(tmp, d[3]);
 
-      input += 16;
-      length -= 16;
+      input = input.last(input.size() - 16);
    }
 
-   while(length--) {
-      tmp = process8(tmp, *input++);
+   for(; !input.empty(); input = input.last(input.size() - 1)) {
+      tmp = process8(tmp, input.front());
    }
 
    m_crc = tmp;
@@ -205,7 +204,7 @@ void CRC24::add_data(const uint8_t input[], size_t length) {
 /*
 * Finalize a CRC24 Checksum
 */
-void CRC24::final_result(uint8_t output[]) {
+void CRC24::final_result(std::span<uint8_t> output) {
    output[0] = get_byte<3>(m_crc);
    output[1] = get_byte<2>(m_crc);
    output[2] = get_byte<1>(m_crc);
diff --git a/src/lib/hash/checksum/crc24/crc24.h b/src/lib/hash/checksum/crc24/crc24.h
index be39aaf94..1d940b939 100644
--- a/src/lib/hash/checksum/crc24/crc24.h
+++ b/src/lib/hash/checksum/crc24/crc24.h
@@ -35,8 +35,8 @@ class CRC24 final : public HashFunction {
       ~CRC24() override { clear(); }
 
    private:
-      void add_data(const uint8_t[], size_t) override;
-      void final_result(uint8_t[]) override;
+      void add_data(std::span<const uint8_t>) override;
+      void final_result(std::span<uint8_t>) override;
       uint32_t m_crc;
 };
 
diff --git a/src/lib/hash/checksum/crc32/crc32.cpp b/src/lib/hash/checksum/crc32/crc32.cpp
index 5342d8010..4e6bfcb65 100644
--- a/src/lib/hash/checksum/crc32/crc32.cpp
+++ b/src/lib/hash/checksum/crc32/crc32.cpp
@@ -49,9 +49,9 @@ alignas(256) const uint32_t CRC32_T0[256] = {
 /*
 * Update a CRC32 Checksum
 */
-void CRC32::add_data(const uint8_t input[], size_t length) {
+void CRC32::add_data(std::span<const uint8_t> input) {
    uint32_t tmp = m_crc;
-   while(length >= 16) {
+   for(; input.size() >= 16; input = input.last(input.size() - 16)) {
       tmp = CRC32_T0[(tmp ^ input[0]) & 0xFF] ^ (tmp >> 8);
       tmp = CRC32_T0[(tmp ^ input[1]) & 0xFF] ^ (tmp >> 8);
       tmp = CRC32_T0[(tmp ^ input[2]) & 0xFF] ^ (tmp >> 8);
@@ -68,11 +68,9 @@ void CRC32::add_data(const uint8_t input[], size_t length) {
       tmp = CRC32_T0[(tmp ^ input[13]) & 0xFF] ^ (tmp >> 8);
       tmp = CRC32_T0[(tmp ^ input[14]) & 0xFF] ^ (tmp >> 8);
       tmp = CRC32_T0[(tmp ^ input[15]) & 0xFF] ^ (tmp >> 8);
-      input += 16;
-      length -= 16;
    }
 
-   for(size_t i = 0; i != length; ++i) {
+   for(size_t i = 0; i != input.size(); ++i) {
       tmp = CRC32_T0[(tmp ^ input[i]) & 0xFF] ^ (tmp >> 8);
    }
 
@@ -82,9 +80,9 @@ void CRC32::add_data(const uint8_t input[], size_t length) {
 /*
 * Finalize a CRC32 Checksum
 */
-void CRC32::final_result(uint8_t output[]) {
+void CRC32::final_result(std::span<uint8_t> output) {
    m_crc ^= 0xFFFFFFFF;
-   store_be(m_crc, output);
+   store_be(m_crc, output.data());
    clear();
 }
 
diff --git a/src/lib/hash/checksum/crc32/crc32.h b/src/lib/hash/checksum/crc32/crc32.h
index 5e6a205c8..b53b79910 100644
--- a/src/lib/hash/checksum/crc32/crc32.h
+++ b/src/lib/hash/checksum/crc32/crc32.h
@@ -32,8 +32,8 @@ class CRC32 final : public HashFunction {
       ~CRC32() override { clear(); }
 
    private:
-      void add_data(const uint8_t[], size_t) override;
-      void final_result(uint8_t[]) override;
+      void add_data(std::span<const uint8_t>) override;
+      void final_result(std::span<uint8_t>) override;
       uint32_t m_crc;
 };
 
diff --git a/src/lib/hash/comb4p/comb4p.cpp b/src/lib/hash/comb4p/comb4p.cpp
index e532b10cb..a5f7cb220 100644
--- a/src/lib/hash/comb4p/comb4p.cpp
+++ b/src/lib/hash/comb4p/comb4p.cpp
@@ -9,6 +9,7 @@
 
 #include <botan/exceptn.h>
 #include <botan/internal/fmt.h>
+#include <botan/internal/stl_util.h>
 
 namespace Botan {
 
@@ -84,12 +85,12 @@ std::unique_ptr<HashFunction> Comb4P::copy_state() const {
    return copy;
 }
 
-void Comb4P::add_data(const uint8_t input[], size_t length) {
-   m_hash1->update(input, length);
-   m_hash2->update(input, length);
+void Comb4P::add_data(std::span<const uint8_t> input) {
+   m_hash1->update(input);
+   m_hash2->update(input);
 }
 
-void Comb4P::final_result(uint8_t out[]) {
+void Comb4P::final_result(std::span<uint8_t> output) {
    secure_vector<uint8_t> h1 = m_hash1->final();
    secure_vector<uint8_t> h2 = m_hash2->final();
 
@@ -102,8 +103,9 @@ void Comb4P::final_result(uint8_t out[]) {
    // Third round
    comb4p_round(h1, h2, 2, *m_hash1, *m_hash2);
 
-   copy_mem(out, h1.data(), h1.size());
-   copy_mem(out + h1.size(), h2.data(), h2.size());
+   BufferStuffer out(output);
+   copy_mem(out.next(h1.size()).data(), h1.data(), h1.size());
+   copy_mem(out.next(h2.size()).data(), h2.data(), h2.size());
 
    // Prep for processing next message, if any
    m_hash1->update(0);
diff --git a/src/lib/hash/comb4p/comb4p.h b/src/lib/hash/comb4p/comb4p.h
index 4fa4bff81..c122f8b39 100644
--- a/src/lib/hash/comb4p/comb4p.h
+++ b/src/lib/hash/comb4p/comb4p.h
@@ -39,8 +39,8 @@ class Comb4P final : public HashFunction {
    private:
       Comb4P() = default;
 
-      void add_data(const uint8_t input[], size_t length) override;
-      void final_result(uint8_t out[]) override;
+      void add_data(std::span<const uint8_t> input) override;
+      void final_result(std::span<uint8_t> out) override;
 
       std::unique_ptr<HashFunction> m_hash1, m_hash2;
 };
diff --git a/src/lib/hash/gost_3411/gost_3411.cpp b/src/lib/hash/gost_3411/gost_3411.cpp
index 0496f12cb..a9037aee6 100644
--- a/src/lib/hash/gost_3411/gost_3411.cpp
+++ b/src/lib/hash/gost_3411/gost_3411.cpp
@@ -8,6 +8,7 @@
 #include <botan/internal/gost_3411.h>
 
 #include <botan/internal/loadstor.h>
+#include <botan/internal/stl_util.h>
 
 namespace Botan {
 
@@ -34,29 +35,29 @@ std::unique_ptr<HashFunction> GOST_34_11::copy_state() const {
 /**
 * Hash additional inputs
 */
-void GOST_34_11::add_data(const uint8_t input[], size_t length) {
-   m_count += length;
+void GOST_34_11::add_data(std::span<const uint8_t> input) {
+   m_count += input.size();
 
    if(m_position) {
-      buffer_insert(m_buffer, m_position, input, length);
+      buffer_insert(m_buffer, m_position, input.data(), input.size());
 
-      if(m_position + length >= hash_block_size()) {
+      if(m_position + input.size() >= hash_block_size()) {
          compress_n(m_buffer.data(), 1);
-         input += (hash_block_size() - m_position);
-         length -= (hash_block_size() - m_position);
+         input = input.last(input.size() - hash_block_size() + m_position);
          m_position = 0;
       }
    }
 
-   const size_t full_blocks = length / hash_block_size();
-   const size_t remaining = length % hash_block_size();
+   BufferSlicer in(input);
+   const size_t full_blocks = in.remaining() / hash_block_size();
 
    if(full_blocks) {
-      compress_n(input, full_blocks);
+      compress_n(in.take(full_blocks * hash_block_size()).data(), full_blocks);
    }
 
-   buffer_insert(m_buffer, m_position, input + full_blocks * hash_block_size(), remaining);
-   m_position += remaining;
+   const auto remaining = in.take(in.remaining());
+   buffer_insert(m_buffer, m_position, remaining.data(), remaining.size());
+   m_position += remaining.size();
 }
 
 /**
@@ -206,7 +207,7 @@ void GOST_34_11::compress_n(const uint8_t input[], size_t blocks) {
 /**
 * Produce the final GOST 34.11 output
 */
-void GOST_34_11::final_result(uint8_t out[]) {
+void GOST_34_11::final_result(std::span<uint8_t> out) {
    if(m_position) {
       clear_mem(m_buffer.data() + m_position, m_buffer.size() - m_position);
       compress_n(m_buffer.data(), 1);
@@ -221,7 +222,7 @@ void GOST_34_11::final_result(uint8_t out[]) {
    compress_n(length_buf.data(), 1);
    compress_n(sum_buf.data(), 1);
 
-   copy_mem(out, m_hash.data(), 32);
+   copy_mem(out.data(), m_hash.data(), 32);
 
    clear();
 }
diff --git a/src/lib/hash/gost_3411/gost_3411.h b/src/lib/hash/gost_3411/gost_3411.h
index 054dfd0b1..35f7af301 100644
--- a/src/lib/hash/gost_3411/gost_3411.h
+++ b/src/lib/hash/gost_3411/gost_3411.h
@@ -35,8 +35,8 @@ class GOST_34_11 final : public HashFunction {
    private:
       void compress_n(const uint8_t input[], size_t blocks);
 
-      void add_data(const uint8_t[], size_t) override;
-      void final_result(uint8_t[]) override;
+      void add_data(std::span<const uint8_t>) override;
+      void final_result(std::span<uint8_t>) override;
 
       GOST_28147_89 m_cipher;
       secure_vector<uint8_t> m_buffer, m_sum, m_hash;
diff --git a/src/lib/hash/keccak/keccak.cpp b/src/lib/hash/keccak/keccak.cpp
index 8f79c1a44..56f1b0be4 100644
--- a/src/lib/hash/keccak/keccak.cpp
+++ b/src/lib/hash/keccak/keccak.cpp
@@ -42,14 +42,14 @@ std::string Keccak_1600::provider() const {
    return m_keccak.provider();
 }
 
-void Keccak_1600::add_data(const uint8_t input[], size_t length) {
-   m_keccak.absorb({input, length});
+void Keccak_1600::add_data(std::span<const uint8_t> input) {
+   m_keccak.absorb(input);
 }
 
-void Keccak_1600::final_result(uint8_t output[]) {
+void Keccak_1600::final_result(std::span<uint8_t> output) {
    m_keccak.finish();
-   m_keccak.squeeze({output, m_output_length});
-   m_keccak.clear();
+   m_keccak.squeeze(output);
+   clear();
 }
 
 }  // namespace Botan
diff --git a/src/lib/hash/keccak/keccak.h b/src/lib/hash/keccak/keccak.h
index 17639024a..56c72c4ed 100644
--- a/src/lib/hash/keccak/keccak.h
+++ b/src/lib/hash/keccak/keccak.h
@@ -51,8 +51,8 @@ class Keccak_1600 final : public HashFunction {
       std::string provider() const override;
 
    private:
-      void add_data(const uint8_t input[], size_t length) override;
-      void final_result(uint8_t out[]) override;
+      void add_data(std::span<const uint8_t> input) override;
+      void final_result(std::span<uint8_t> out) override;
 
    private:
       Keccak_Permutation m_keccak;
diff --git a/src/lib/hash/mdx_hash/mdx_hash.cpp b/src/lib/hash/mdx_hash/mdx_hash.cpp
index ab03f131c..3692b4c55 100644
--- a/src/lib/hash/mdx_hash/mdx_hash.cpp
+++ b/src/lib/hash/mdx_hash/mdx_hash.cpp
@@ -10,6 +10,7 @@
 #include <botan/exceptn.h>
 #include <botan/internal/bit_ops.h>
 #include <botan/internal/loadstor.h>
+#include <botan/internal/stl_util.h>
 
 namespace Botan {
 
@@ -46,38 +47,38 @@ void MDx_HashFunction::clear() {
 /*
 * Update the hash
 */
-void MDx_HashFunction::add_data(const uint8_t input[], size_t length) {
+void MDx_HashFunction::add_data(std::span<const uint8_t> input) {
    const size_t block_len = static_cast<size_t>(1) << m_block_bits;
 
-   m_count += length;
+   m_count += input.size();
 
    if(m_position) {
-      buffer_insert(m_buffer, m_position, input, length);
+      buffer_insert(m_buffer, m_position, input.data(), input.size());
 
-      if(m_position + length >= block_len) {
+      if(m_position + input.size() >= block_len) {
          compress_n(m_buffer.data(), 1);
-         input += (block_len - m_position);
-         length -= (block_len - m_position);
+         input = input.last(input.size() - block_len + m_position);
          m_position = 0;
       }
    }
 
    // Just in case the compiler can't figure out block_len is a power of 2
-   const size_t full_blocks = length >> m_block_bits;
-   const size_t remaining = length & (block_len - 1);
+   const size_t full_blocks = input.size() >> m_block_bits;
 
+   BufferSlicer in(input);
    if(full_blocks > 0) {
-      compress_n(input, full_blocks);
+      compress_n(in.take(full_blocks * block_len).data(), full_blocks);
    }
 
-   buffer_insert(m_buffer, m_position, input + full_blocks * block_len, remaining);
-   m_position += remaining;
+   const auto remaining = in.take(in.remaining());
+   buffer_insert(m_buffer, m_position, remaining.data(), remaining.size());
+   m_position += remaining.size();
 }
 
 /*
 * Finalize a hash
 */
-void MDx_HashFunction::final_result(uint8_t output[]) {
+void MDx_HashFunction::final_result(std::span<uint8_t> output) {
    const size_t block_len = static_cast<size_t>(1) << m_block_bits;
 
    clear_mem(&m_buffer[m_position], block_len - m_position);
@@ -100,7 +101,7 @@ void MDx_HashFunction::final_result(uint8_t output[]) {
    }
 
    compress_n(m_buffer.data(), 1);
-   copy_out(output);
+   copy_out(output.data());
    clear();
 }
 
diff --git a/src/lib/hash/mdx_hash/mdx_hash.h b/src/lib/hash/mdx_hash/mdx_hash.h
index 1f0e600b3..c8c404859 100644
--- a/src/lib/hash/mdx_hash/mdx_hash.h
+++ b/src/lib/hash/mdx_hash/mdx_hash.h
@@ -29,8 +29,8 @@ class MDx_HashFunction : public HashFunction {
       size_t hash_block_size() const final { return m_buffer.size(); }
 
    protected:
-      void add_data(const uint8_t input[], size_t length) final;
-      void final_result(uint8_t output[]) final;
+      void add_data(std::span<const uint8_t> input) final;
+      void final_result(std::span<uint8_t> output) final;
 
       /**
       * Run the hash's compression function over a set of blocks
diff --git a/src/lib/hash/par_hash/par_hash.cpp b/src/lib/hash/par_hash/par_hash.cpp
index 93e88e609..379790989 100644
--- a/src/lib/hash/par_hash/par_hash.cpp
+++ b/src/lib/hash/par_hash/par_hash.cpp
@@ -6,22 +6,23 @@
 */
 
 #include <botan/internal/par_hash.h>
+
+#include <botan/internal/stl_util.h>
+
 #include <sstream>
 
 namespace Botan {
 
-void Parallel::add_data(const uint8_t input[], size_t length) {
+void Parallel::add_data(std::span<const uint8_t> input) {
    for(auto&& hash : m_hashes) {
-      hash->update(input, length);
+      hash->update(input);
    }
 }
 
-void Parallel::final_result(uint8_t out[]) {
-   size_t offset = 0;
-
+void Parallel::final_result(std::span<uint8_t> output) {
+   BufferStuffer out(output);
    for(auto&& hash : m_hashes) {
-      hash->final(out + offset);
-      offset += hash->output_length();
+      hash->final(out.next(hash->output_length()));
    }
 }
 
diff --git a/src/lib/hash/par_hash/par_hash.h b/src/lib/hash/par_hash/par_hash.h
index 659b6f36a..d07fc6732 100644
--- a/src/lib/hash/par_hash/par_hash.h
+++ b/src/lib/hash/par_hash/par_hash.h
@@ -35,8 +35,8 @@ class Parallel final : public HashFunction {
       Parallel& operator=(const Parallel&) = delete;
 
    private:
-      void add_data(const uint8_t[], size_t) override;
-      void final_result(uint8_t[]) override;
+      void add_data(std::span<const uint8_t>) override;
+      void final_result(std::span<uint8_t>) override;
 
       std::vector<std::unique_ptr<HashFunction>> m_hashes;
 };
diff --git a/src/lib/hash/sha3/sha3.cpp b/src/lib/hash/sha3/sha3.cpp
index a1c5ef217..3ff45454a 100644
--- a/src/lib/hash/sha3/sha3.cpp
+++ b/src/lib/hash/sha3/sha3.cpp
@@ -43,13 +43,13 @@ void SHA_3::clear() {
    m_keccak.clear();
 }
 
-void SHA_3::add_data(const uint8_t input[], size_t length) {
-   m_keccak.absorb({input, length});
+void SHA_3::add_data(std::span<const uint8_t> input) {
+   m_keccak.absorb(input);
 }
 
-void SHA_3::final_result(uint8_t output[]) {
+void SHA_3::final_result(std::span<uint8_t> output) {
    m_keccak.finish();
-   m_keccak.squeeze({output, m_output_length});
+   m_keccak.squeeze(output);
    m_keccak.clear();
 }
 
diff --git a/src/lib/hash/sha3/sha3.h b/src/lib/hash/sha3/sha3.h
index 4ec579d62..a36853049 100644
--- a/src/lib/hash/sha3/sha3.h
+++ b/src/lib/hash/sha3/sha3.h
@@ -37,8 +37,8 @@ class SHA_3 : public HashFunction {
       std::string provider() const override;
 
    private:
-      void add_data(const uint8_t input[], size_t length) override;
-      void final_result(uint8_t out[]) override;
+      void add_data(std::span<const uint8_t> input) override;
+      void final_result(std::span<uint8_t> out) override;
 
    private:
       Keccak_Permutation m_keccak;
diff --git a/src/lib/hash/shake/shake.cpp b/src/lib/hash/shake/shake.cpp
index 6e64b3afe..0ad314ed3 100644
--- a/src/lib/hash/shake/shake.cpp
+++ b/src/lib/hash/shake/shake.cpp
@@ -30,13 +30,13 @@ std::unique_ptr<HashFunction> SHAKE_128::copy_state() const {
    return std::make_unique<SHAKE_128>(*this);
 }
 
-void SHAKE_128::add_data(const uint8_t input[], size_t length) {
-   m_keccak.absorb({input, length});
+void SHAKE_128::add_data(std::span<const uint8_t> input) {
+   m_keccak.absorb(input);
 }
 
-void SHAKE_128::final_result(uint8_t output[]) {
+void SHAKE_128::final_result(std::span<uint8_t> output) {
    m_keccak.finish();
-   m_keccak.squeeze({output, output_length()});
+   m_keccak.squeeze(output);
    clear();
 }
 
@@ -58,13 +58,13 @@ std::unique_ptr<HashFunction> SHAKE_256::copy_state() const {
    return std::make_unique<SHAKE_256>(*this);
 }
 
-void SHAKE_256::add_data(const uint8_t input[], size_t length) {
-   m_keccak.absorb({input, length});
+void SHAKE_256::add_data(std::span<const uint8_t> input) {
+   m_keccak.absorb(input);
 }
 
-void SHAKE_256::final_result(uint8_t output[]) {
+void SHAKE_256::final_result(std::span<uint8_t> output) {
    m_keccak.finish();
-   m_keccak.squeeze({output, output_length()});
+   m_keccak.squeeze(output);
    clear();
 }
 
diff --git a/src/lib/hash/shake/shake.h b/src/lib/hash/shake/shake.h
index 5c5208326..97473411f 100644
--- a/src/lib/hash/shake/shake.h
+++ b/src/lib/hash/shake/shake.h
@@ -39,8 +39,8 @@ class SHAKE_128 final : public HashFunction {
       std::string provider() const override { return m_keccak.provider(); }
 
    private:
-      void add_data(const uint8_t input[], size_t length) override;
-      void final_result(uint8_t out[]) override;
+      void add_data(std::span<const uint8_t> input) override;
+      void final_result(std::span<uint8_t> out) override;
 
       Keccak_Permutation m_keccak;
       size_t m_output_bits;
@@ -70,8 +70,8 @@ class SHAKE_256 final : public HashFunction {
       std::string provider() const override { return m_keccak.provider(); }
 
    private:
-      void add_data(const uint8_t input[], size_t length) override;
-      void final_result(uint8_t out[]) override;
+      void add_data(std::span<const uint8_t> input) override;
+      void final_result(std::span<uint8_t> out) override;
 
       Keccak_Permutation m_keccak;
       size_t m_output_bits;
diff --git a/src/lib/hash/skein/skein_512.cpp b/src/lib/hash/skein/skein_512.cpp
index 0784c9faf..c29f50a9d 100644
--- a/src/lib/hash/skein/skein_512.cpp
+++ b/src/lib/hash/skein/skein_512.cpp
@@ -10,6 +10,7 @@
 #include <botan/exceptn.h>
 #include <botan/internal/fmt.h>
 #include <botan/internal/loadstor.h>
+#include <botan/internal/stl_util.h>
 #include <algorithm>
 
 namespace Botan {
@@ -118,35 +119,35 @@ void Skein_512::ubi_512(const uint8_t msg[], size_t msg_len) {
    } while(msg_len);
 }
 
-void Skein_512::add_data(const uint8_t input[], size_t length) {
-   if(length == 0) {
+void Skein_512::add_data(std::span<const uint8_t> input) {
+   if(input.empty()) {
       return;
    }
 
    if(m_buf_pos) {
-      buffer_insert(m_buffer, m_buf_pos, input, length);
-      if(m_buf_pos + length > 64) {
+      buffer_insert(m_buffer, m_buf_pos, input.data(), input.size());
+      if(m_buf_pos + input.size() > 64) {
          ubi_512(m_buffer.data(), m_buffer.size());
 
-         input += (64 - m_buf_pos);
-         length -= (64 - m_buf_pos);
+         input = input.last(input.size() - 64 + m_buf_pos);
          m_buf_pos = 0;
       }
    }
 
-   const size_t full_blocks = (length - 1) / 64;
+   const size_t full_blocks = (input.size() - 1) / 64;
+   BufferSlicer in(input);
 
    if(full_blocks) {
-      ubi_512(input, 64 * full_blocks);
+      const auto blocks = in.take(64 * full_blocks);
+      ubi_512(blocks.data(), blocks.size());
    }
 
-   length -= full_blocks * 64;
-
-   buffer_insert(m_buffer, m_buf_pos, input + full_blocks * 64, length);
-   m_buf_pos += length;
+   const auto remaining = in.take(in.remaining());
+   buffer_insert(m_buffer, m_buf_pos, remaining.data(), remaining.size());
+   m_buf_pos += remaining.size();
 }
 
-void Skein_512::final_result(uint8_t out[]) {
+void Skein_512::final_result(std::span<uint8_t> out) {
    m_T[1] |= (static_cast<uint64_t>(1) << 63);  // final block flag
 
    for(size_t i = m_buf_pos; i != m_buffer.size(); ++i) {
@@ -160,7 +161,7 @@ void Skein_512::final_result(uint8_t out[]) {
    reset_tweak(SKEIN_OUTPUT, true);
    ubi_512(counter, sizeof(counter));
 
-   copy_out_vec_le(out, m_output_bits / 8, m_threefish->m_K);
+   copy_out_vec_le(out.data(), m_output_bits / 8, m_threefish->m_K);
 
    m_buf_pos = 0;
    initial_block();
diff --git a/src/lib/hash/skein/skein_512.h b/src/lib/hash/skein/skein_512.h
index eb8fd82bf..faa1a2c08 100644
--- a/src/lib/hash/skein/skein_512.h
+++ b/src/lib/hash/skein/skein_512.h
@@ -48,8 +48,8 @@ class Skein_512 final : public HashFunction {
          SKEIN_OUTPUT = 63
       };
 
-      void add_data(const uint8_t input[], size_t length) override;
-      void final_result(uint8_t out[]) override;
+      void add_data(std::span<const uint8_t> input) override;
+      void final_result(std::span<uint8_t> out) override;
 
       void ubi_512(const uint8_t msg[], size_t msg_len);
 
diff --git a/src/lib/hash/streebog/streebog.cpp b/src/lib/hash/streebog/streebog.cpp
index a9f36273b..59b26dd6f 100644
--- a/src/lib/hash/streebog/streebog.cpp
+++ b/src/lib/hash/streebog/streebog.cpp
@@ -11,6 +11,7 @@
 #include <botan/exceptn.h>
 #include <botan/internal/fmt.h>
 #include <botan/internal/loadstor.h>
+#include <botan/internal/stl_util.h>
 
 namespace Botan {
 
@@ -50,37 +51,35 @@ void Streebog::clear() {
 /*
 * Update the hash
 */
-void Streebog::add_data(const uint8_t input[], size_t length) {
+void Streebog::add_data(std::span<const uint8_t> input) {
    const size_t block_size = m_buffer.size();
 
    if(m_position) {
-      buffer_insert(m_buffer, m_position, input, length);
+      buffer_insert(m_buffer, m_position, input.data(), input.size());
 
-      if(m_position + length >= block_size) {
+      if(m_position + input.size() >= block_size) {
          compress(m_buffer.data());
          m_count += 512;
-         input += (block_size - m_position);
-         length -= (block_size - m_position);
+         input = input.last(input.size() - block_size + m_position);
          m_position = 0;
       }
    }
 
-   const size_t full_blocks = length / block_size;
-   const size_t remaining = length % block_size;
-
-   for(size_t i = 0; i != full_blocks; ++i) {
-      compress(input + block_size * i);
+   BufferSlicer in(input);
+   while(in.remaining() >= block_size) {
+      compress(in.take(block_size).data());
       m_count += 512;
    }
 
-   buffer_insert(m_buffer, m_position, input + full_blocks * block_size, remaining);
-   m_position += remaining;
+   const auto remaining = in.take(in.remaining());
+   buffer_insert(m_buffer, m_position, remaining.data(), remaining.size());
+   m_position += remaining.size();
 }
 
 /*
 * Finalize a hash
 */
-void Streebog::final_result(uint8_t output[]) {
+void Streebog::final_result(std::span<uint8_t> output) {
    m_buffer[m_position++] = 0x01;
 
    if(m_position != m_buffer.size()) {
@@ -96,7 +95,7 @@ void Streebog::final_result(uint8_t output[]) {
 
    compress_64(m_S.data(), true);
    // FIXME
-   std::memcpy(output, &m_h[8 - output_length() / 8], output_length());
+   std::memcpy(output.data(), &m_h[8 - output_length() / 8], output_length());
    clear();
 }
 
diff --git a/src/lib/hash/streebog/streebog.h b/src/lib/hash/streebog/streebog.h
index b9f9aeac8..420ade1d9 100644
--- a/src/lib/hash/streebog/streebog.h
+++ b/src/lib/hash/streebog/streebog.h
@@ -32,8 +32,8 @@ class Streebog final : public HashFunction {
       explicit Streebog(size_t output_bits);
 
    protected:
-      void add_data(const uint8_t input[], size_t length) override;
-      void final_result(uint8_t out[]) override;
+      void add_data(std::span<const uint8_t> input) override;
+      void final_result(std::span<uint8_t> out) override;
 
       void compress(const uint8_t input[], bool lastblock = false);
 
diff --git a/src/lib/hash/trunc_hash/trunc_hash.cpp b/src/lib/hash/trunc_hash/trunc_hash.cpp
index 79b49594f..869f365ac 100644
--- a/src/lib/hash/trunc_hash/trunc_hash.cpp
+++ b/src/lib/hash/trunc_hash/trunc_hash.cpp
@@ -13,25 +13,25 @@
 
 namespace Botan {
 
-void Truncated_Hash::add_data(const uint8_t input[], size_t length) {
-   m_hash->update(input, length);
+void Truncated_Hash::add_data(std::span<const uint8_t> input) {
+   m_hash->update(input);
 }
 
-void Truncated_Hash::final_result(uint8_t out[]) {
+void Truncated_Hash::final_result(std::span<uint8_t> out) {
    BOTAN_ASSERT_NOMSG(m_hash->output_length() * 8 >= m_output_bits);
 
    m_hash->final(m_buffer);
 
    // truncate output to a full number of bytes
    const auto bytes = output_length();
-   std::copy_n(m_buffer.begin(), bytes, out);
+   std::copy_n(m_buffer.begin(), bytes, out.data());
    zeroise(m_buffer);
 
    // mask the unwanted bits in the final byte
    const uint8_t bits_in_last_byte = ((m_output_bits - 1) % 8) + 1;
    const uint8_t bitmask = ~((1 << (8 - bits_in_last_byte)) - 1);
 
-   out[bytes - 1] &= bitmask;
+   out.back() &= bitmask;
 }
 
 size_t Truncated_Hash::output_length() const {
diff --git a/src/lib/hash/trunc_hash/trunc_hash.h b/src/lib/hash/trunc_hash/trunc_hash.h
index cc8cc8610..f5047d039 100644
--- a/src/lib/hash/trunc_hash/trunc_hash.h
+++ b/src/lib/hash/trunc_hash/trunc_hash.h
@@ -36,8 +36,8 @@ class Truncated_Hash final : public HashFunction {
       Truncated_Hash(std::unique_ptr<HashFunction> hash, size_t length);
 
    private:
-      void add_data(const uint8_t[], size_t) override;
-      void final_result(uint8_t[]) override;
+      void add_data(std::span<const uint8_t>) override;
+      void final_result(std::span<uint8_t>) override;
 
       std::unique_ptr<HashFunction> m_hash;
       size_t m_output_bits;
diff --git a/src/lib/mac/blake2mac/blake2bmac.h b/src/lib/mac/blake2mac/blake2bmac.h
index bea055ac1..c61ebdbd5 100644
--- a/src/lib/mac/blake2mac/blake2bmac.h
+++ b/src/lib/mac/blake2mac/blake2bmac.h
@@ -37,14 +37,14 @@ class BLAKE2bMAC final : public MessageAuthenticationCode {
       Key_Length_Specification key_spec() const override { return m_blake.key_spec(); }
 
    private:
-      void key_schedule(const uint8_t key[], size_t length) override { m_blake.set_key(key, length); }
+      void key_schedule(std::span<const uint8_t> key) override { m_blake.set_key(key); }
 
-      void add_data(const uint8_t input[], size_t length) override {
+      void add_data(std::span<const uint8_t> input) override {
          assert_key_material_set();
-         m_blake.update(input, length);
+         m_blake.update(input);
       }
 
-      void final_result(uint8_t out[]) override {
+      void final_result(std::span<uint8_t> out) override {
          assert_key_material_set();
          m_blake.final(out);
       }
diff --git a/src/lib/mac/cmac/cmac.cpp b/src/lib/mac/cmac/cmac.cpp
index 96a4e694f..26aefaf8e 100644
--- a/src/lib/mac/cmac/cmac.cpp
+++ b/src/lib/mac/cmac/cmac.cpp
@@ -10,37 +10,40 @@
 #include <botan/exceptn.h>
 #include <botan/internal/fmt.h>
 #include <botan/internal/poly_dbl.h>
+#include <botan/internal/stl_util.h>
 
 namespace Botan {
 
 /*
 * Update an CMAC Calculation
 */
-void CMAC::add_data(const uint8_t input[], size_t length) {
+void CMAC::add_data(std::span<const uint8_t> input) {
    const size_t bs = output_length();
 
-   buffer_insert(m_buffer, m_position, input, length);
-   if(m_position + length > bs) {
+   buffer_insert(m_buffer, m_position, input.data(), input.size());
+   if(m_position + input.size() > bs) {
       xor_buf(m_state, m_buffer, bs);
       m_cipher->encrypt(m_state);
-      input += (bs - m_position);
-      length -= (bs - m_position);
-      while(length > bs) {
-         xor_buf(m_state, input, bs);
+
+      BufferSlicer in(input);
+      in.skip(bs - m_position);
+      while(in.remaining() > bs) {
+         xor_buf(m_state, in.take(bs), bs);
          m_cipher->encrypt(m_state);
-         input += bs;
-         length -= bs;
       }
-      copy_mem(m_buffer.data(), input, length);
-      m_position = 0;
+
+      const auto remaining = in.take(in.remaining());
+      copy_mem(m_buffer.data(), remaining.data(), remaining.size());
+      m_position = remaining.size();
+   } else {
+      m_position += input.size();
    }
-   m_position += length;
 }
 
 /*
 * Finalize an CMAC Calculation
 */
-void CMAC::final_result(uint8_t mac[]) {
+void CMAC::final_result(std::span<uint8_t> mac) {
    xor_buf(m_state, m_buffer, m_position);
 
    if(m_position == output_length()) {
@@ -52,7 +55,7 @@ void CMAC::final_result(uint8_t mac[]) {
 
    m_cipher->encrypt(m_state);
 
-   copy_mem(mac, m_state.data(), output_length());
+   copy_mem(mac.data(), m_state.data(), output_length());
 
    zeroise(m_state);
    zeroise(m_buffer);
@@ -66,9 +69,9 @@ bool CMAC::has_keying_material() const {
 /*
 * CMAC Key Schedule
 */
-void CMAC::key_schedule(const uint8_t key[], size_t length) {
+void CMAC::key_schedule(std::span<const uint8_t> key) {
    clear();
-   m_cipher->set_key(key, length);
+   m_cipher->set_key(key);
    m_cipher->encrypt(m_B);
    poly_double_n(m_B.data(), m_B.size());
    poly_double_n(m_P.data(), m_B.data(), m_P.size());
diff --git a/src/lib/mac/cmac/cmac.h b/src/lib/mac/cmac/cmac.h
index 49dec3caf..2b6b0536c 100644
--- a/src/lib/mac/cmac/cmac.h
+++ b/src/lib/mac/cmac/cmac.h
@@ -38,9 +38,9 @@ class CMAC final : public MessageAuthenticationCode {
       CMAC& operator=(const CMAC&) = delete;
 
    private:
-      void add_data(const uint8_t[], size_t) override;
-      void final_result(uint8_t[]) override;
-      void key_schedule(const uint8_t[], size_t) override;
+      void add_data(std::span<const uint8_t>) override;
+      void final_result(std::span<uint8_t>) override;
+      void key_schedule(std::span<const uint8_t>) override;
 
       std::unique_ptr<BlockCipher> m_cipher;
       secure_vector<uint8_t> m_buffer, m_state, m_B, m_P;
diff --git a/src/lib/mac/gmac/gmac.cpp b/src/lib/mac/gmac/gmac.cpp
index e4d157b15..16b070750 100644
--- a/src/lib/mac/gmac/gmac.cpp
+++ b/src/lib/mac/gmac/gmac.cpp
@@ -12,6 +12,7 @@
 #include <botan/exceptn.h>
 #include <botan/internal/fmt.h>
 #include <botan/internal/ghash.h>
+#include <botan/internal/stl_util.h>
 
 namespace Botan {
 
@@ -46,13 +47,13 @@ size_t GMAC::output_length() const {
    return GCM_BS;
 }
 
-void GMAC::add_data(const uint8_t input[], size_t size) {
+void GMAC::add_data(std::span<const uint8_t> input) {
+   BufferSlicer in(input);
+
    if(m_aad_buf_pos > 0) {
-      const size_t taking = std::min(GCM_BS - m_aad_buf_pos, size);
-      copy_mem(&m_aad_buf[m_aad_buf_pos], input, taking);
-      m_aad_buf_pos += taking;
-      input += taking;
-      size -= taking;
+      const auto part = in.take(std::min(GCM_BS - m_aad_buf_pos, in.remaining()));
+      copy_mem(&m_aad_buf[m_aad_buf_pos], part.data(), part.size());
+      m_aad_buf_pos += part.size();
 
       if(m_aad_buf_pos == GCM_BS) {
          m_ghash->update_associated_data(m_aad_buf.data(), GCM_BS);
@@ -60,14 +61,14 @@ void GMAC::add_data(const uint8_t input[], size_t size) {
       }
    }
 
-   const size_t left_over = size % GCM_BS;
-   const size_t full_blocks = size - left_over;
-   m_ghash->update_associated_data(input, full_blocks);
-   input += full_blocks;
+   const size_t left_over = in.remaining() % GCM_BS;
+   const size_t full_blocks = in.remaining() - left_over;
+   m_ghash->update_associated_data(in.take(full_blocks).data(), full_blocks);
 
-   if(left_over > 0) {
-      copy_mem(&m_aad_buf[m_aad_buf_pos], input, left_over);
-      m_aad_buf_pos += left_over;
+   if(!in.empty()) {
+      const auto remaining = in.take(in.remaining());
+      copy_mem(&m_aad_buf[m_aad_buf_pos], remaining.data(), remaining.size());
+      m_aad_buf_pos += remaining.size();
    }
 }
 
@@ -75,9 +76,9 @@ bool GMAC::has_keying_material() const {
    return m_cipher->has_keying_material();
 }
 
-void GMAC::key_schedule(const uint8_t key[], size_t size) {
+void GMAC::key_schedule(std::span<const uint8_t> key) {
    clear();
-   m_cipher->set_key(key, size);
+   m_cipher->set_key(key);
 
    m_cipher->encrypt(m_H);
    m_ghash->set_key(m_H);
@@ -100,7 +101,7 @@ void GMAC::start_msg(const uint8_t nonce[], size_t nonce_len) {
    m_initialized = true;
 }
 
-void GMAC::final_result(uint8_t mac[]) {
+void GMAC::final_result(std::span<uint8_t> mac) {
    // This ensures the GMAC computation has been initialized with a fresh
    // nonce. The aim of this check is to prevent developers from re-using
    // nonces (and potential nonce-reuse attacks).
@@ -114,7 +115,7 @@ void GMAC::final_result(uint8_t mac[]) {
       m_ghash->update_associated_data(m_aad_buf.data(), m_aad_buf_pos);
    }
 
-   m_ghash->final(mac, output_length());
+   m_ghash->final(mac.data(), output_length());
    m_ghash->set_key(m_H);
    m_aad_buf_pos = 0;
 }
diff --git a/src/lib/mac/gmac/gmac.h b/src/lib/mac/gmac/gmac.h
index edae5c6ad..e3ad7c9e1 100644
--- a/src/lib/mac/gmac/gmac.h
+++ b/src/lib/mac/gmac/gmac.h
@@ -46,10 +46,10 @@ class GMAC final : public MessageAuthenticationCode {
       ~GMAC() override;
 
    private:
-      void add_data(const uint8_t[], size_t) override;
-      void final_result(uint8_t[]) override;
+      void add_data(std::span<const uint8_t>) override;
+      void final_result(std::span<uint8_t>) override;
       void start_msg(const uint8_t nonce[], size_t nonce_len) override;
-      void key_schedule(const uint8_t key[], size_t size) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       static const size_t GCM_BS = 16;
       std::unique_ptr<BlockCipher> m_cipher;
diff --git a/src/lib/mac/hmac/hmac.cpp b/src/lib/mac/hmac/hmac.cpp
index 2dca8b4b7..aa690a3d4 100644
--- a/src/lib/mac/hmac/hmac.cpp
+++ b/src/lib/mac/hmac/hmac.cpp
@@ -16,19 +16,19 @@ namespace Botan {
 /*
 * Update a HMAC Calculation
 */
-void HMAC::add_data(const uint8_t input[], size_t length) {
+void HMAC::add_data(std::span<const uint8_t> input) {
    assert_key_material_set();
-   m_hash->update(input, length);
+   m_hash->update(input);
 }
 
 /*
 * Finalize a HMAC Calculation
 */
-void HMAC::final_result(uint8_t mac[]) {
+void HMAC::final_result(std::span<uint8_t> mac) {
    assert_key_material_set();
    m_hash->final(mac);
    m_hash->update(m_okey);
-   m_hash->update(mac, m_hash_output_length);
+   m_hash->update(mac.first(m_hash_output_length));
    m_hash->final(mac);
    m_hash->update(m_ikey);
 }
@@ -49,7 +49,7 @@ bool HMAC::has_keying_material() const {
 /*
 * HMAC Key Schedule
 */
-void HMAC::key_schedule(const uint8_t key[], size_t length) {
+void HMAC::key_schedule(std::span<const uint8_t> key) {
    const uint8_t ipad = 0x36;
    const uint8_t opad = 0x5C;
 
@@ -78,20 +78,20 @@ void HMAC::key_schedule(const uint8_t key[], size_t length) {
    * trivial to simply check.
    */
 
-   if(length > m_hash_block_size) {
-      m_hash->update(key, length);
+   if(key.size() > m_hash_block_size) {
+      m_hash->update(key);
       m_hash->final(m_ikey.data());
-   } else if(length > 0) {
+   } else if(!key.empty()) {
       for(size_t i = 0, i_mod_length = 0; i != m_hash_block_size; ++i) {
          /*
          access key[i % length] but avoiding division due to variable
          time computation on some processors.
          */
-         auto needs_reduction = CT::Mask<size_t>::is_lte(length, i_mod_length);
+         auto needs_reduction = CT::Mask<size_t>::is_lte(key.size(), i_mod_length);
          i_mod_length = needs_reduction.select(0, i_mod_length);
          const uint8_t kb = key[i_mod_length];
 
-         auto in_range = CT::Mask<size_t>::is_lt(i, length);
+         auto in_range = CT::Mask<size_t>::is_lt(i, key.size());
          m_ikey[i] = static_cast<uint8_t>(in_range.if_set_return(kb));
          i_mod_length += 1;
       }
diff --git a/src/lib/mac/hmac/hmac.h b/src/lib/mac/hmac/hmac.h
index 44586517d..8a9c66e10 100644
--- a/src/lib/mac/hmac/hmac.h
+++ b/src/lib/mac/hmac/hmac.h
@@ -37,9 +37,9 @@ class HMAC final : public MessageAuthenticationCode {
       HMAC& operator=(const HMAC&) = delete;
 
    private:
-      void add_data(const uint8_t[], size_t) override;
-      void final_result(uint8_t[]) override;
-      void key_schedule(const uint8_t[], size_t) override;
+      void add_data(std::span<const uint8_t>) override;
+      void final_result(std::span<uint8_t>) override;
+      void key_schedule(std::span<const uint8_t>) override;
 
       std::unique_ptr<HashFunction> m_hash;
       secure_vector<uint8_t> m_ikey, m_okey;
diff --git a/src/lib/mac/poly1305/poly1305.cpp b/src/lib/mac/poly1305/poly1305.cpp
index 003949cf2..e24d04d09 100644
--- a/src/lib/mac/poly1305/poly1305.cpp
+++ b/src/lib/mac/poly1305/poly1305.cpp
@@ -14,6 +14,7 @@
 #include <botan/internal/donna128.h>
 #include <botan/internal/loadstor.h>
 #include <botan/internal/mul128.h>
+#include <botan/internal/stl_util.h>
 
 namespace Botan {
 
@@ -171,40 +172,40 @@ bool Poly1305::has_keying_material() const {
    return m_poly.size() == 8;
 }
 
-void Poly1305::key_schedule(const uint8_t key[], size_t /*length*/) {
+void Poly1305::key_schedule(std::span<const uint8_t> key) {
    m_buf_pos = 0;
    m_buf.resize(16);
    m_poly.resize(8);
 
-   poly1305_init(m_poly, key);
+   poly1305_init(m_poly, key.data());
 }
 
-void Poly1305::add_data(const uint8_t input[], size_t length) {
+void Poly1305::add_data(std::span<const uint8_t> input) {
    assert_key_material_set();
 
    if(m_buf_pos) {
-      buffer_insert(m_buf, m_buf_pos, input, length);
+      buffer_insert(m_buf, m_buf_pos, input.data(), input.size());
 
-      if(m_buf_pos + length >= m_buf.size()) {
+      if(m_buf_pos + input.size() >= m_buf.size()) {
          poly1305_blocks(m_poly, m_buf.data(), 1);
-         input += (m_buf.size() - m_buf_pos);
-         length -= (m_buf.size() - m_buf_pos);
+         input = input.last(input.size() - m_buf.size() + m_buf_pos);
          m_buf_pos = 0;
       }
    }
 
-   const size_t full_blocks = length / m_buf.size();
-   const size_t remaining = length % m_buf.size();
+   BufferSlicer in(input);
+   const size_t full_blocks = in.remaining() / m_buf.size();
 
    if(full_blocks) {
-      poly1305_blocks(m_poly, input, full_blocks);
+      poly1305_blocks(m_poly, in.take(full_blocks * m_buf.size()).data(), full_blocks);
    }
 
-   buffer_insert(m_buf, m_buf_pos, input + full_blocks * m_buf.size(), remaining);
-   m_buf_pos += remaining;
+   const auto remaining = in.take(in.remaining());
+   buffer_insert(m_buf, m_buf_pos, remaining.data(), remaining.size());
+   m_buf_pos += remaining.size();
 }
 
-void Poly1305::final_result(uint8_t out[]) {
+void Poly1305::final_result(std::span<uint8_t> out) {
    assert_key_material_set();
 
    if(m_buf_pos != 0) {
@@ -216,7 +217,7 @@ void Poly1305::final_result(uint8_t out[]) {
       poly1305_blocks(m_poly, m_buf.data(), 1, true);
    }
 
-   poly1305_finish(m_poly, out);
+   poly1305_finish(m_poly, out.data());
 
    m_poly.clear();
    m_buf_pos = 0;
diff --git a/src/lib/mac/poly1305/poly1305.h b/src/lib/mac/poly1305/poly1305.h
index 83b41ef0e..38e442e92 100644
--- a/src/lib/mac/poly1305/poly1305.h
+++ b/src/lib/mac/poly1305/poly1305.h
@@ -34,9 +34,9 @@ class Poly1305 final : public MessageAuthenticationCode {
       bool has_keying_material() const override;
 
    private:
-      void add_data(const uint8_t[], size_t) override;
-      void final_result(uint8_t[]) override;
-      void key_schedule(const uint8_t[], size_t) override;
+      void add_data(std::span<const uint8_t>) override;
+      void final_result(std::span<uint8_t>) override;
+      void key_schedule(std::span<const uint8_t>) override;
 
       secure_vector<uint64_t> m_poly;
       secure_vector<uint8_t> m_buf;
diff --git a/src/lib/mac/siphash/siphash.cpp b/src/lib/mac/siphash/siphash.cpp
index 3abaf4f85..60c97edd1 100644
--- a/src/lib/mac/siphash/siphash.cpp
+++ b/src/lib/mac/siphash/siphash.cpp
@@ -10,6 +10,7 @@
 #include <botan/internal/fmt.h>
 #include <botan/internal/loadstor.h>
 #include <botan/internal/rotate.h>
+#include <botan/internal/stl_util.h>
 
 namespace Botan {
 
@@ -46,18 +47,18 @@ void SipRounds(uint64_t M, secure_vector<uint64_t>& V, size_t r) {
 
 }  // namespace
 
-void SipHash::add_data(const uint8_t input[], size_t length) {
+void SipHash::add_data(std::span<const uint8_t> input) {
    assert_key_material_set();
 
    // SipHash counts the message length mod 256
-   m_words += static_cast<uint8_t>(length);
+   m_words += static_cast<uint8_t>(input.size());
+
+   BufferSlicer in(input);
 
    if(m_mbuf_pos) {
-      while(length && m_mbuf_pos != 8) {
-         m_mbuf = (m_mbuf >> 8) | (static_cast<uint64_t>(input[0]) << 56);
+      while(!in.empty() && m_mbuf_pos != 8) {
+         m_mbuf = (m_mbuf >> 8) | (static_cast<uint64_t>(in.take_byte()) << 56);
          ++m_mbuf_pos;
-         ++input;
-         length--;
       }
 
       if(m_mbuf_pos == 8) {
@@ -67,19 +68,17 @@ void SipHash::add_data(const uint8_t input[], size_t length) {
       }
    }
 
-   while(length >= 8) {
-      SipRounds(load_le<uint64_t>(input, 0), m_V, m_C);
-      input += 8;
-      length -= 8;
+   while(in.remaining() >= 8) {
+      SipRounds(load_le<uint64_t>(in.take(8).data(), 0), m_V, m_C);
    }
 
-   for(size_t i = 0; i != length; ++i) {
-      m_mbuf = (m_mbuf >> 8) | (static_cast<uint64_t>(input[i]) << 56);
+   while(!in.empty()) {
+      m_mbuf = (m_mbuf >> 8) | (static_cast<uint64_t>(in.take_byte()) << 56);
       m_mbuf_pos++;
    }
 }
 
-void SipHash::final_result(uint8_t mac[]) {
+void SipHash::final_result(std::span<uint8_t> mac) {
    assert_key_material_set();
 
    if(m_mbuf_pos == 0) {
@@ -95,7 +94,7 @@ void SipHash::final_result(uint8_t mac[]) {
 
    const uint64_t X = m_V[0] ^ m_V[1] ^ m_V[2] ^ m_V[3];
 
-   store_le(X, mac);
+   store_le(X, mac.data());
 
    m_V[0] = m_K[0] ^ 0x736F6D6570736575;
    m_V[1] = m_K[1] ^ 0x646F72616E646F6D;
@@ -110,9 +109,9 @@ bool SipHash::has_keying_material() const {
    return !m_V.empty();
 }
 
-void SipHash::key_schedule(const uint8_t key[], size_t /*length*/) {
-   const uint64_t K0 = load_le<uint64_t>(key, 0);
-   const uint64_t K1 = load_le<uint64_t>(key, 1);
+void SipHash::key_schedule(std::span<const uint8_t> key) {
+   const uint64_t K0 = load_le<uint64_t>(key.data(), 0);
+   const uint64_t K1 = load_le<uint64_t>(key.data(), 1);
 
    m_K.resize(2);
    m_K[0] = K0;
diff --git a/src/lib/mac/siphash/siphash.h b/src/lib/mac/siphash/siphash.h
index 65f418a8f..7184ac58d 100644
--- a/src/lib/mac/siphash/siphash.h
+++ b/src/lib/mac/siphash/siphash.h
@@ -28,9 +28,9 @@ class SipHash final : public MessageAuthenticationCode {
       Key_Length_Specification key_spec() const override { return Key_Length_Specification(16); }
 
    private:
-      void add_data(const uint8_t[], size_t) override;
-      void final_result(uint8_t[]) override;
-      void key_schedule(const uint8_t[], size_t) override;
+      void add_data(std::span<const uint8_t>) override;
+      void final_result(std::span<uint8_t>) override;
+      void key_schedule(std::span<const uint8_t>) override;
 
       const size_t m_C, m_D;
       secure_vector<uint64_t> m_K;
diff --git a/src/lib/mac/x919_mac/x919_mac.cpp b/src/lib/mac/x919_mac/x919_mac.cpp
index d6c06db88..db82f1f47 100644
--- a/src/lib/mac/x919_mac/x919_mac.cpp
+++ b/src/lib/mac/x919_mac/x919_mac.cpp
@@ -7,45 +7,46 @@
 
 #include <botan/internal/x919_mac.h>
 
+#include <botan/internal/stl_util.h>
+
 namespace Botan {
 
 /*
 * Update an ANSI X9.19 MAC Calculation
 */
-void ANSI_X919_MAC::add_data(const uint8_t input[], size_t length) {
+void ANSI_X919_MAC::add_data(std::span<const uint8_t> input) {
    assert_key_material_set();
 
-   size_t xored = std::min(8 - m_position, length);
-   xor_buf(&m_state[m_position], input, xored);
-   m_position += xored;
+   BufferSlicer in(input);
+
+   const auto to_be_xored = in.take(std::min(8 - m_position, in.remaining()));
+   xor_buf(&m_state[m_position], to_be_xored.data(), to_be_xored.size());
+   m_position += to_be_xored.size();
 
    if(m_position < 8) {
       return;
    }
 
    m_des1->encrypt(m_state);
-   input += xored;
-   length -= xored;
-   while(length >= 8) {
-      xor_buf(m_state, input, 8);
+   while(in.remaining() >= 8) {
+      xor_buf(m_state, in.take(8).data(), 8);
       m_des1->encrypt(m_state);
-      input += 8;
-      length -= 8;
    }
 
-   xor_buf(m_state, input, length);
-   m_position = length;
+   const auto remaining = in.take(in.remaining());
+   xor_buf(m_state, remaining.data(), remaining.size());
+   m_position = remaining.size();
 }
 
 /*
 * Finalize an ANSI X9.19 MAC Calculation
 */
-void ANSI_X919_MAC::final_result(uint8_t mac[]) {
+void ANSI_X919_MAC::final_result(std::span<uint8_t> mac) {
    if(m_position) {
       m_des1->encrypt(m_state);
    }
-   m_des2->decrypt(m_state.data(), mac);
-   m_des1->encrypt(mac);
+   m_des2->decrypt(m_state.data(), mac.data());
+   m_des1->encrypt(mac.data());
    zeroise(m_state);
    m_position = 0;
 }
@@ -57,16 +58,16 @@ bool ANSI_X919_MAC::has_keying_material() const {
 /*
 * ANSI X9.19 MAC Key Schedule
 */
-void ANSI_X919_MAC::key_schedule(const uint8_t key[], size_t length) {
+void ANSI_X919_MAC::key_schedule(std::span<const uint8_t> key) {
    m_state.resize(8);
 
-   m_des1->set_key(key, 8);
+   m_des1->set_key(key.first(8));
 
-   if(length == 16) {
-      key += 8;
+   if(key.size() == 16) {
+      key = key.last(8);
    }
 
-   m_des2->set_key(key, 8);
+   m_des2->set_key(key.first(8));
 }
 
 /*
diff --git a/src/lib/mac/x919_mac/x919_mac.h b/src/lib/mac/x919_mac/x919_mac.h
index 012ef627d..3124f8819 100644
--- a/src/lib/mac/x919_mac/x919_mac.h
+++ b/src/lib/mac/x919_mac/x919_mac.h
@@ -35,9 +35,9 @@ class ANSI_X919_MAC final : public MessageAuthenticationCode {
       ANSI_X919_MAC& operator=(const ANSI_X919_MAC&) = delete;
 
    private:
-      void add_data(const uint8_t[], size_t) override;
-      void final_result(uint8_t[]) override;
-      void key_schedule(const uint8_t[], size_t) override;
+      void add_data(std::span<const uint8_t>) override;
+      void final_result(std::span<uint8_t>) override;
+      void key_schedule(std::span<const uint8_t>) override;
 
       std::unique_ptr<BlockCipher> m_des1, m_des2;
       secure_vector<uint8_t> m_state;
diff --git a/src/lib/misc/fpe_fe1/fpe_fe1.cpp b/src/lib/misc/fpe_fe1/fpe_fe1.cpp
index 12ffc99bd..64d00e8d1 100644
--- a/src/lib/misc/fpe_fe1/fpe_fe1.cpp
+++ b/src/lib/misc/fpe_fe1/fpe_fe1.cpp
@@ -104,8 +104,8 @@ bool FPE_FE1::has_keying_material() const {
    return m_mac->has_keying_material();
 }
 
-void FPE_FE1::key_schedule(const uint8_t key[], size_t length) {
-   m_mac->set_key(key, length);
+void FPE_FE1::key_schedule(std::span<const uint8_t> key) {
+   m_mac->set_key(key);
 }
 
 BigInt FPE_FE1::F(const BigInt& R,
diff --git a/src/lib/misc/fpe_fe1/fpe_fe1.h b/src/lib/misc/fpe_fe1/fpe_fe1.h
index 6978f992a..d27cdc517 100644
--- a/src/lib/misc/fpe_fe1/fpe_fe1.h
+++ b/src/lib/misc/fpe_fe1/fpe_fe1.h
@@ -67,7 +67,7 @@ class BOTAN_PUBLIC_API(2, 5) FPE_FE1 final : public SymmetricAlgorithm {
       BigInt decrypt(const BigInt& x, uint64_t tweak) const;
 
    private:
-      void key_schedule(const uint8_t key[], size_t length) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       BigInt F(const BigInt& R, size_t round, const secure_vector<uint8_t>& tweak, secure_vector<uint8_t>& tmp) const;
 
diff --git a/src/lib/modes/aead/ccm/ccm.cpp b/src/lib/modes/aead/ccm/ccm.cpp
index 15b097b9d..229184eee 100644
--- a/src/lib/modes/aead/ccm/ccm.cpp
+++ b/src/lib/modes/aead/ccm/ccm.cpp
@@ -78,8 +78,8 @@ bool CCM_Mode::has_keying_material() const {
    return m_cipher->has_keying_material();
 }
 
-void CCM_Mode::key_schedule(const uint8_t key[], size_t length) {
-   m_cipher->set_key(key, length);
+void CCM_Mode::key_schedule(std::span<const uint8_t> key) {
+   m_cipher->set_key(key);
 }
 
 void CCM_Mode::set_associated_data_n(size_t idx, std::span<const uint8_t> ad) {
diff --git a/src/lib/modes/aead/ccm/ccm.h b/src/lib/modes/aead/ccm/ccm.h
index ec93aaf98..671b8d6cb 100644
--- a/src/lib/modes/aead/ccm/ccm.h
+++ b/src/lib/modes/aead/ccm/ccm.h
@@ -68,7 +68,7 @@ class CCM_Mode : public AEAD_Mode {
       void start_msg(const uint8_t nonce[], size_t nonce_len) final;
       size_t process_msg(uint8_t buf[], size_t sz) final;
 
-      void key_schedule(const uint8_t key[], size_t length) final;
+      void key_schedule(std::span<const uint8_t> key) final;
 
       const size_t m_tag_size;
       const size_t m_L;
diff --git a/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp b/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp
index e48922662..205ebdbd7 100644
--- a/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp
+++ b/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp
@@ -47,8 +47,8 @@ bool ChaCha20Poly1305_Mode::has_keying_material() const {
    return m_chacha->has_keying_material();
 }
 
-void ChaCha20Poly1305_Mode::key_schedule(const uint8_t key[], size_t length) {
-   m_chacha->set_key(key, length);
+void ChaCha20Poly1305_Mode::key_schedule(std::span<const uint8_t> key) {
+   m_chacha->set_key(key);
 }
 
 void ChaCha20Poly1305_Mode::set_associated_data_n(size_t idx, std::span<const uint8_t> ad) {
diff --git a/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h b/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h
index 15e812938..909b23ebe 100644
--- a/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h
+++ b/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h
@@ -63,7 +63,7 @@ class ChaCha20Poly1305_Mode : public AEAD_Mode {
    private:
       void start_msg(const uint8_t nonce[], size_t nonce_len) override;
 
-      void key_schedule(const uint8_t key[], size_t length) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 };
 
 /**
diff --git a/src/lib/modes/aead/eax/eax.cpp b/src/lib/modes/aead/eax/eax.cpp
index 37a019ad5..76f4aaa3a 100644
--- a/src/lib/modes/aead/eax/eax.cpp
+++ b/src/lib/modes/aead/eax/eax.cpp
@@ -84,13 +84,13 @@ bool EAX_Mode::has_keying_material() const {
 /*
 * Set the EAX key
 */
-void EAX_Mode::key_schedule(const uint8_t key[], size_t length) {
+void EAX_Mode::key_schedule(std::span<const uint8_t> key) {
    /*
    * These could share the key schedule, which is one nice part of EAX,
    * but it's much easier to ignore that here...
    */
-   m_ctr->set_key(key, length);
-   m_cmac->set_key(key, length);
+   m_ctr->set_key(key);
+   m_cmac->set_key(key);
 }
 
 /*
diff --git a/src/lib/modes/aead/eax/eax.h b/src/lib/modes/aead/eax/eax.h
index 44520acc4..192859339 100644
--- a/src/lib/modes/aead/eax/eax.h
+++ b/src/lib/modes/aead/eax/eax.h
@@ -64,7 +64,7 @@ class EAX_Mode : public AEAD_Mode {
    private:
       void start_msg(const uint8_t nonce[], size_t nonce_len) final;
 
-      void key_schedule(const uint8_t key[], size_t length) final;
+      void key_schedule(std::span<const uint8_t> key) final;
 };
 
 /**
diff --git a/src/lib/modes/aead/gcm/gcm.cpp b/src/lib/modes/aead/gcm/gcm.cpp
index b806ec307..71135f86d 100644
--- a/src/lib/modes/aead/gcm/gcm.cpp
+++ b/src/lib/modes/aead/gcm/gcm.cpp
@@ -75,8 +75,8 @@ bool GCM_Mode::has_keying_material() const {
    return m_ctr->has_keying_material();
 }
 
-void GCM_Mode::key_schedule(const uint8_t key[], size_t keylen) {
-   m_ctr->set_key(key, keylen);
+void GCM_Mode::key_schedule(std::span<const uint8_t> key) {
+   m_ctr->set_key(key);
 
    const std::vector<uint8_t> zeros(GCM_BS);
    m_ctr->set_iv(zeros.data(), zeros.size());
diff --git a/src/lib/modes/aead/gcm/gcm.h b/src/lib/modes/aead/gcm/gcm.h
index 46cc47d64..129815742 100644
--- a/src/lib/modes/aead/gcm/gcm.h
+++ b/src/lib/modes/aead/gcm/gcm.h
@@ -61,7 +61,7 @@ class GCM_Mode : public AEAD_Mode {
    private:
       void start_msg(const uint8_t nonce[], size_t nonce_len) override;
 
-      void key_schedule(const uint8_t key[], size_t length) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       secure_vector<uint8_t> m_y0;
 };
diff --git a/src/lib/modes/aead/ocb/ocb.cpp b/src/lib/modes/aead/ocb/ocb.cpp
index e0d2e06a6..3952814f4 100644
--- a/src/lib/modes/aead/ocb/ocb.cpp
+++ b/src/lib/modes/aead/ocb/ocb.cpp
@@ -212,8 +212,8 @@ bool OCB_Mode::has_keying_material() const {
    return m_cipher->has_keying_material();
 }
 
-void OCB_Mode::key_schedule(const uint8_t key[], size_t length) {
-   m_cipher->set_key(key, length);
+void OCB_Mode::key_schedule(std::span<const uint8_t> key) {
+   m_cipher->set_key(key);
    m_L = std::make_unique<L_computer>(*m_cipher);
 }
 
diff --git a/src/lib/modes/aead/ocb/ocb.h b/src/lib/modes/aead/ocb/ocb.h
index 12c9a19cb..fe364aaf7 100644
--- a/src/lib/modes/aead/ocb/ocb.h
+++ b/src/lib/modes/aead/ocb/ocb.h
@@ -77,7 +77,7 @@ class BOTAN_TEST_API OCB_Mode : public AEAD_Mode {
    private:
       void start_msg(const uint8_t nonce[], size_t nonce_len) override final;
 
-      void key_schedule(const uint8_t key[], size_t length) override final;
+      void key_schedule(std::span<const uint8_t> key) override final;
 
       const secure_vector<uint8_t>& update_nonce(const uint8_t nonce[], size_t nonce_len);
 
diff --git a/src/lib/modes/aead/siv/siv.cpp b/src/lib/modes/aead/siv/siv.cpp
index 0d5bcc3d6..ffde05e81 100644
--- a/src/lib/modes/aead/siv/siv.cpp
+++ b/src/lib/modes/aead/siv/siv.cpp
@@ -69,10 +69,10 @@ bool SIV_Mode::has_keying_material() const {
    return m_ctr->has_keying_material() && m_mac->has_keying_material();
 }
 
-void SIV_Mode::key_schedule(const uint8_t key[], size_t length) {
-   const size_t keylen = length / 2;
-   m_mac->set_key(key, keylen);
-   m_ctr->set_key(key + keylen, keylen);
+void SIV_Mode::key_schedule(std::span<const uint8_t> key) {
+   const size_t keylen = key.size() / 2;
+   m_mac->set_key(key.first(keylen));
+   m_ctr->set_key(key.last(keylen));
    m_ad_macs.clear();
 }
 
diff --git a/src/lib/modes/aead/siv/siv.h b/src/lib/modes/aead/siv/siv.h
index fb36e5105..a5ed08338 100644
--- a/src/lib/modes/aead/siv/siv.h
+++ b/src/lib/modes/aead/siv/siv.h
@@ -70,7 +70,7 @@ class BOTAN_TEST_API SIV_Mode : public AEAD_Mode {
       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;
+      void key_schedule(std::span<const uint8_t> key) override final;
 
       const std::string m_name;
       const size_t m_bs;
diff --git a/src/lib/modes/cbc/cbc.cpp b/src/lib/modes/cbc/cbc.cpp
index 092d35e70..042edf661 100644
--- a/src/lib/modes/cbc/cbc.cpp
+++ b/src/lib/modes/cbc/cbc.cpp
@@ -63,8 +63,8 @@ bool CBC_Mode::has_keying_material() const {
    return m_cipher->has_keying_material();
 }
 
-void CBC_Mode::key_schedule(const uint8_t key[], size_t length) {
-   m_cipher->set_key(key, length);
+void CBC_Mode::key_schedule(std::span<const uint8_t> key) {
+   m_cipher->set_key(key);
    m_state.clear();
 }
 
diff --git a/src/lib/modes/cbc/cbc.h b/src/lib/modes/cbc/cbc.h
index f4667a511..e08dbddc2 100644
--- a/src/lib/modes/cbc/cbc.h
+++ b/src/lib/modes/cbc/cbc.h
@@ -57,7 +57,7 @@ class CBC_Mode : public Cipher_Mode {
    private:
       void start_msg(const uint8_t nonce[], size_t nonce_len) override;
 
-      void key_schedule(const uint8_t key[], size_t length) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       std::unique_ptr<BlockCipher> m_cipher;
       std::unique_ptr<BlockCipherModePaddingMethod> m_padding;
diff --git a/src/lib/modes/cfb/cfb.cpp b/src/lib/modes/cfb/cfb.cpp
index f3243ba03..79a71bc04 100644
--- a/src/lib/modes/cfb/cfb.cpp
+++ b/src/lib/modes/cfb/cfb.cpp
@@ -73,8 +73,8 @@ bool CFB_Mode::has_keying_material() const {
    return m_cipher->has_keying_material();
 }
 
-void CFB_Mode::key_schedule(const uint8_t key[], size_t length) {
-   m_cipher->set_key(key, length);
+void CFB_Mode::key_schedule(std::span<const uint8_t> key) {
+   m_cipher->set_key(key);
    m_keystream.resize(m_cipher->block_size());
 }
 
diff --git a/src/lib/modes/cfb/cfb.h b/src/lib/modes/cfb/cfb.h
index 5addb7ff4..444025a68 100644
--- a/src/lib/modes/cfb/cfb.h
+++ b/src/lib/modes/cfb/cfb.h
@@ -58,7 +58,7 @@ class CFB_Mode : public Cipher_Mode {
 
    private:
       void start_msg(const uint8_t nonce[], size_t nonce_len) override;
-      void key_schedule(const uint8_t key[], size_t length) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       std::unique_ptr<BlockCipher> m_cipher;
       const size_t m_block_size;
diff --git a/src/lib/modes/stream_mode.h b/src/lib/modes/stream_mode.h
index fb99a5458..ad0786da5 100644
--- a/src/lib/modes/stream_mode.h
+++ b/src/lib/modes/stream_mode.h
@@ -72,7 +72,7 @@ class Stream_Cipher_Mode final : public Cipher_Mode {
 
       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); }
+      void key_schedule(std::span<const uint8_t> key) override { m_cipher->set_key(key); }
 
       std::unique_ptr<StreamCipher> m_cipher;
 };
diff --git a/src/lib/modes/xts/xts.cpp b/src/lib/modes/xts/xts.cpp
index 32c25389d..886b7c4a1 100644
--- a/src/lib/modes/xts/xts.cpp
+++ b/src/lib/modes/xts/xts.cpp
@@ -67,15 +67,15 @@ bool XTS_Mode::has_keying_material() const {
    return m_cipher->has_keying_material() && m_tweak_cipher->has_keying_material();
 }
 
-void XTS_Mode::key_schedule(const uint8_t key[], size_t length) {
-   const size_t key_half = length / 2;
+void XTS_Mode::key_schedule(std::span<const uint8_t> key) {
+   const size_t key_half = key.size() / 2;
 
-   if(length % 2 == 1 || !m_cipher->valid_keylength(key_half)) {
-      throw Invalid_Key_Length(name(), length);
+   if(key.size() % 2 == 1 || !m_cipher->valid_keylength(key_half)) {
+      throw Invalid_Key_Length(name(), key.size());
    }
 
-   m_cipher->set_key(key, key_half);
-   m_tweak_cipher->set_key(&key[key_half], key_half);
+   m_cipher->set_key(key.first(key_half));
+   m_tweak_cipher->set_key(key.last(key_half));
 }
 
 void XTS_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
diff --git a/src/lib/modes/xts/xts.h b/src/lib/modes/xts/xts.h
index c9299b567..96a6624cb 100644
--- a/src/lib/modes/xts/xts.h
+++ b/src/lib/modes/xts/xts.h
@@ -56,7 +56,7 @@ class XTS_Mode : public Cipher_Mode {
 
    private:
       void start_msg(const uint8_t nonce[], size_t nonce_len) override;
-      void key_schedule(const uint8_t key[], size_t length) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       std::unique_ptr<BlockCipher> m_cipher;
       std::unique_ptr<BlockCipher> m_tweak_cipher;
diff --git a/src/lib/pk_pad/raw_hash/raw_hash.cpp b/src/lib/pk_pad/raw_hash/raw_hash.cpp
index 060c98a29..ceaa47cc4 100644
--- a/src/lib/pk_pad/raw_hash/raw_hash.cpp
+++ b/src/lib/pk_pad/raw_hash/raw_hash.cpp
@@ -10,18 +10,18 @@
 
 namespace Botan {
 
-void RawHashFunction::add_data(const uint8_t input[], size_t length) {
-   m_bits += std::make_pair(input, length);
+void RawHashFunction::add_data(std::span<const uint8_t> input) {
+   m_bits += std::make_pair(input.data(), input.size());
 }
 
-void RawHashFunction::final_result(uint8_t out[]) {
+void RawHashFunction::final_result(std::span<uint8_t> out) {
    if(m_output_length > 0 && m_bits.size() != m_output_length) {
       m_bits.clear();
       throw Invalid_Argument("Raw padding was configured to use a " + std::to_string(m_output_length) +
                              " byte hash but instead was used for a " + std::to_string(m_bits.size()) + " byte hash");
    }
 
-   copy_mem(out, m_bits.data(), m_bits.size());
+   copy_mem(out.data(), m_bits.data(), m_bits.size());
    m_bits.clear();
 }
 
diff --git a/src/lib/pk_pad/raw_hash/raw_hash.h b/src/lib/pk_pad/raw_hash/raw_hash.h
index 2e9a23b12..892ccd6fb 100644
--- a/src/lib/pk_pad/raw_hash/raw_hash.h
+++ b/src/lib/pk_pad/raw_hash/raw_hash.h
@@ -26,9 +26,9 @@ class RawHashFunction : public HashFunction {
 
       RawHashFunction(std::string_view name, size_t output_length) : m_name(name), m_output_length(output_length) {}
 
-      void add_data(const uint8_t input[], size_t length) override;
+      void add_data(std::span<const uint8_t> input) override;
 
-      void final_result(uint8_t out[]) override;
+      void final_result(std::span<uint8_t> out) override;
 
       void clear() override;
 
diff --git a/src/lib/prov/commoncrypto/commoncrypto_block.cpp b/src/lib/prov/commoncrypto/commoncrypto_block.cpp
index 32a7c06cb..a07fe118d 100644
--- a/src/lib/prov/commoncrypto/commoncrypto_block.cpp
+++ b/src/lib/prov/commoncrypto/commoncrypto_block.cpp
@@ -59,7 +59,7 @@ class CommonCrypto_BlockCipher final : public BlockCipher {
          }
       }
 
-      void key_schedule(const uint8_t key[], size_t key_len) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       std::string m_cipher_name;
       CommonCryptor_Opts m_opts;
@@ -84,11 +84,11 @@ CommonCrypto_BlockCipher::~CommonCrypto_BlockCipher() {
 /*
 * Set the key
 */
-void CommonCrypto_BlockCipher::key_schedule(const uint8_t key[], size_t length) {
-   secure_vector<uint8_t> full_key(key, key + length);
+void CommonCrypto_BlockCipher::key_schedule(std::span<const uint8_t> key) {
+   secure_vector<uint8_t> full_key(key.begin(), key.end());
 
    clear();
-   commoncrypto_adjust_key_size(key, length, m_opts, full_key);
+   commoncrypto_adjust_key_size(key.data(), key.size(), m_opts, full_key);
 
    CCCryptorStatus status;
    status =
diff --git a/src/lib/prov/commoncrypto/commoncrypto_hash.cpp b/src/lib/prov/commoncrypto/commoncrypto_hash.cpp
index 29ee85e48..1fb79c419 100644
--- a/src/lib/prov/commoncrypto/commoncrypto_hash.cpp
+++ b/src/lib/prov/commoncrypto/commoncrypto_hash.cpp
@@ -8,6 +8,7 @@
 #include <botan/internal/commoncrypto.h>
 
 #include <botan/hash.h>
+#include <botan/internal/stl_util.h>
 #include <unordered_map>
 
 #include <CommonCrypto/CommonCrypto.h>
@@ -54,18 +55,19 @@ class CommonCrypto_HashFunction final : public HashFunction {
       CommonCrypto_HashFunction(const digest_config_t& info, const CTX& ctx) : m_ctx(ctx), m_info(info) {}
 
    private:
-      void add_data(const uint8_t input[], size_t length) override {
+      void add_data(std::span<const uint8_t> input) override {
+         BufferSlicer in(input);
+
          /* update len parameter is 32 bit unsigned integer, feed input in parts */
-         while(length > 0) {
-            CC_LONG update_len = (length > 0xFFFFFFFFUL) ? 0xFFFFFFFFUL : static_cast<CC_LONG>(length);
-            m_info.update(&m_ctx, input, update_len);
-            input += update_len;
-            length -= update_len;
+         while(!in.empty()) {
+            CC_LONG update_len = (in.remaining() > 0xFFFFFFFFUL) ? 0xFFFFFFFFUL : static_cast<CC_LONG>(in.remaining());
+            const auto chunk = in.take(update_len);
+            m_info.update(&m_ctx, chunk.data(), static_cast<CC_LONG>(chunk.size()));
          }
       }
 
-      void final_result(uint8_t output[]) override {
-         if(m_info.final(output, &m_ctx) != 1)
+      void final_result(std::span<uint8_t> output) override {
+         if(m_info.final(output.data(), &m_ctx) != 1)
             throw CommonCrypto_Error("CC_" + m_info.name + "_Final");
          clear();
       }
diff --git a/src/lib/prov/commoncrypto/commoncrypto_mode.cpp b/src/lib/prov/commoncrypto/commoncrypto_mode.cpp
index 4c2b69311..ccdb39d34 100644
--- a/src/lib/prov/commoncrypto/commoncrypto_mode.cpp
+++ b/src/lib/prov/commoncrypto/commoncrypto_mode.cpp
@@ -40,7 +40,7 @@ class CommonCrypto_Cipher_Mode final : public Cipher_Mode {
       bool has_keying_material() const override { return m_key_set; }
 
    private:
-      void key_schedule(const uint8_t key[], size_t length) override;
+      void key_schedule(std::span<const uint8_t> key) 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;
@@ -194,11 +194,11 @@ Key_Length_Specification CommonCrypto_Cipher_Mode::key_spec() const {
    return m_opts.key_spec;
 }
 
-void CommonCrypto_Cipher_Mode::key_schedule(const uint8_t key[], size_t length) {
+void CommonCrypto_Cipher_Mode::key_schedule(std::span<const uint8_t> key) {
    CCCryptorStatus status;
    CCOperation op = m_direction == Cipher_Dir::Encryption ? kCCEncrypt : kCCDecrypt;
    status = CCCryptorCreateWithMode(
-      op, m_opts.mode, m_opts.algo, m_opts.padding, nullptr, key, length, nullptr, 0, 0, 0, &m_cipher);
+      op, m_opts.mode, m_opts.algo, m_opts.padding, nullptr, key.data(), key.size(), nullptr, 0, 0, 0, &m_cipher);
    if(status != kCCSuccess) {
       throw CommonCrypto_Error("CCCryptorCreate", status);
    }
diff --git a/src/lib/stream/chacha/chacha.cpp b/src/lib/stream/chacha/chacha.cpp
index 942c2422c..66a3d5e03 100644
--- a/src/lib/stream/chacha/chacha.cpp
+++ b/src/lib/stream/chacha/chacha.cpp
@@ -293,9 +293,9 @@ size_t ChaCha::buffer_size() const {
 /*
 * ChaCha Key Schedule
 */
-void ChaCha::key_schedule(const uint8_t key[], size_t length) {
-   m_key.resize(length / 4);
-   load_le<uint32_t>(m_key.data(), key, m_key.size());
+void ChaCha::key_schedule(std::span<const uint8_t> key) {
+   m_key.resize(key.size() / 4);
+   load_le<uint32_t>(m_key.data(), key.data(), m_key.size());
 
    m_state.resize(16);
 
diff --git a/src/lib/stream/chacha/chacha.h b/src/lib/stream/chacha/chacha.h
index 778954dec..aab6fad12 100644
--- a/src/lib/stream/chacha/chacha.h
+++ b/src/lib/stream/chacha/chacha.h
@@ -51,7 +51,7 @@ class ChaCha final : public StreamCipher {
       size_t buffer_size() const override;
 
    private:
-      void key_schedule(const uint8_t key[], size_t key_len) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       void cipher_bytes(const uint8_t in[], uint8_t out[], size_t length) override;
 
diff --git a/src/lib/stream/ctr/ctr.cpp b/src/lib/stream/ctr/ctr.cpp
index eeb067227..458717586 100644
--- a/src/lib/stream/ctr/ctr.cpp
+++ b/src/lib/stream/ctr/ctr.cpp
@@ -66,8 +66,8 @@ bool CTR_BE::has_keying_material() const {
    return m_cipher->has_keying_material();
 }
 
-void CTR_BE::key_schedule(const uint8_t key[], size_t key_len) {
-   m_cipher->set_key(key, key_len);
+void CTR_BE::key_schedule(std::span<const uint8_t> key) {
+   m_cipher->set_key(key);
 
    // Set a default all-zeros IV
    set_iv(nullptr, 0);
diff --git a/src/lib/stream/ctr/ctr.h b/src/lib/stream/ctr/ctr.h
index a720c4214..f836cf952 100644
--- a/src/lib/stream/ctr/ctr.h
+++ b/src/lib/stream/ctr/ctr.h
@@ -44,7 +44,7 @@ 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 key_schedule(std::span<const uint8_t> key) 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;
diff --git a/src/lib/stream/ofb/ofb.cpp b/src/lib/stream/ofb/ofb.cpp
index 13fd6739b..e4c9c113b 100644
--- a/src/lib/stream/ofb/ofb.cpp
+++ b/src/lib/stream/ofb/ofb.cpp
@@ -29,8 +29,8 @@ size_t OFB::buffer_size() const {
    return m_buffer.size();  // block size
 }
 
-void OFB::key_schedule(const uint8_t key[], size_t key_len) {
-   m_cipher->set_key(key, key_len);
+void OFB::key_schedule(std::span<const uint8_t> key) {
+   m_cipher->set_key(key);
 
    // Set a default all-zeros IV
    set_iv(nullptr, 0);
diff --git a/src/lib/stream/ofb/ofb.h b/src/lib/stream/ofb/ofb.h
index 4300410d5..641d428c5 100644
--- a/src/lib/stream/ofb/ofb.h
+++ b/src/lib/stream/ofb/ofb.h
@@ -42,7 +42,7 @@ 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 key_schedule(std::span<const uint8_t> key) 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;
 
diff --git a/src/lib/stream/rc4/rc4.cpp b/src/lib/stream/rc4/rc4.cpp
index 08335511f..45ec020ff 100644
--- a/src/lib/stream/rc4/rc4.cpp
+++ b/src/lib/stream/rc4/rc4.cpp
@@ -91,7 +91,7 @@ bool RC4::has_keying_material() const {
 /*
 * RC4 Key Schedule
 */
-void RC4::key_schedule(const uint8_t key[], size_t length) {
+void RC4::key_schedule(std::span<const uint8_t> key) {
    m_state.resize(256);
    m_buffer.resize(256);
 
@@ -102,7 +102,7 @@ void RC4::key_schedule(const uint8_t key[], size_t length) {
    }
 
    for(size_t i = 0, state_index = 0; i != 256; ++i) {
-      state_index = (state_index + key[i % length] + m_state[i]) % 256;
+      state_index = (state_index + key[i % key.size()] + m_state[i]) % 256;
       std::swap(m_state[i], m_state[state_index]);
    }
 
diff --git a/src/lib/stream/rc4/rc4.h b/src/lib/stream/rc4/rc4.h
index 6228a47a5..03ec960d2 100644
--- a/src/lib/stream/rc4/rc4.h
+++ b/src/lib/stream/rc4/rc4.h
@@ -39,7 +39,7 @@ class RC4 final : public StreamCipher {
       ~RC4() override { clear(); }
 
    private:
-      void key_schedule(const uint8_t[], size_t) override;
+      void key_schedule(std::span<const uint8_t> key) 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();
diff --git a/src/lib/stream/salsa20/salsa20.cpp b/src/lib/stream/salsa20/salsa20.cpp
index f76015816..0a6a4aebb 100644
--- a/src/lib/stream/salsa20/salsa20.cpp
+++ b/src/lib/stream/salsa20/salsa20.cpp
@@ -172,9 +172,9 @@ size_t Salsa20::buffer_size() const {
 /*
 * Salsa20 Key Schedule
 */
-void Salsa20::key_schedule(const uint8_t key[], size_t length) {
-   m_key.resize(length / 4);
-   load_le<uint32_t>(m_key.data(), key, m_key.size());
+void Salsa20::key_schedule(std::span<const uint8_t> key) {
+   m_key.resize(key.size() / 4);
+   load_le<uint32_t>(m_key.data(), key.data(), m_key.size());
 
    m_state.resize(16);
    m_buffer.resize(64);
diff --git a/src/lib/stream/salsa20/salsa20.h b/src/lib/stream/salsa20/salsa20.h
index 744da9089..ed0c34e0e 100644
--- a/src/lib/stream/salsa20/salsa20.h
+++ b/src/lib/stream/salsa20/salsa20.h
@@ -39,7 +39,7 @@ class Salsa20 final : public StreamCipher {
       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;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       void initialize_state();
 
diff --git a/src/lib/stream/shake_cipher/shake_cipher.cpp b/src/lib/stream/shake_cipher/shake_cipher.cpp
index ec5cbeade..3e47fc3bd 100644
--- a/src/lib/stream/shake_cipher/shake_cipher.cpp
+++ b/src/lib/stream/shake_cipher/shake_cipher.cpp
@@ -81,9 +81,9 @@ void SHAKE_Cipher::generate_keystream_internal(std::span<uint8_t> out) {
    m_bytes_generated += out.size();
 }
 
-void SHAKE_Cipher::key_schedule(const uint8_t key[], size_t length) {
+void SHAKE_Cipher::key_schedule(std::span<const uint8_t> key) {
    clear();
-   m_keccak.absorb({key, length});
+   m_keccak.absorb(key);
    m_keccak.finish();
    m_has_keying_material = true;
 }
diff --git a/src/lib/stream/shake_cipher/shake_cipher.h b/src/lib/stream/shake_cipher/shake_cipher.h
index c459b45de..41fe0719a 100644
--- a/src/lib/stream/shake_cipher/shake_cipher.h
+++ b/src/lib/stream/shake_cipher/shake_cipher.h
@@ -36,7 +36,7 @@ class SHAKE_Cipher : public StreamCipher {
       size_t buffer_size() const final { return m_keccak.byte_rate(); }
 
    private:
-      void key_schedule(const uint8_t key[], size_t key_len) final;
+      void key_schedule(std::span<const uint8_t> key) final;
       /**
       * Produce more XOF output
       */
diff --git a/src/lib/tls/tls12/tls_cbc/tls_cbc.cpp b/src/lib/tls/tls12/tls_cbc/tls_cbc.cpp
index 8c7da5f10..36a875129 100644
--- a/src/lib/tls/tls12/tls_cbc/tls_cbc.cpp
+++ b/src/lib/tls/tls12/tls_cbc/tls_cbc.cpp
@@ -91,15 +91,15 @@ bool TLS_CBC_HMAC_AEAD_Mode::has_keying_material() const {
    return mac().has_keying_material() && cbc().has_keying_material();
 }
 
-void TLS_CBC_HMAC_AEAD_Mode::key_schedule(const uint8_t key[], size_t keylen) {
+void TLS_CBC_HMAC_AEAD_Mode::key_schedule(std::span<const uint8_t> key) {
    // Both keys are of fixed length specified by the ciphersuite
 
-   if(keylen != m_cipher_keylen + m_mac_keylen) {
-      throw Invalid_Key_Length(name(), keylen);
+   if(key.size() != m_cipher_keylen + m_mac_keylen) {
+      throw Invalid_Key_Length(name(), key.size());
    }
 
-   mac().set_key(&key[0], m_mac_keylen);
-   cbc().set_key(&key[m_mac_keylen], m_cipher_keylen);
+   mac().set_key(key.first(m_mac_keylen));
+   cbc().set_key(key.subspan(m_mac_keylen, m_cipher_keylen));
 }
 
 void TLS_CBC_HMAC_AEAD_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
diff --git a/src/lib/tls/tls12/tls_cbc/tls_cbc.h b/src/lib/tls/tls12/tls_cbc/tls_cbc.h
index 3b0161e68..371dd261e 100644
--- a/src/lib/tls/tls12/tls_cbc/tls_cbc.h
+++ b/src/lib/tls/tls12/tls_cbc/tls_cbc.h
@@ -84,7 +84,7 @@ class BOTAN_TEST_API TLS_CBC_HMAC_AEAD_Mode : public AEAD_Mode {
       void start_msg(const uint8_t nonce[], size_t nonce_len) final;
       size_t process_msg(uint8_t buf[], size_t sz) final;
 
-      void key_schedule(const uint8_t key[], size_t length) final;
+      void key_schedule(std::span<const uint8_t> key) final;
 
       const std::string m_cipher_name;
       const std::string m_mac_name;
diff --git a/src/lib/utils/ghash/ghash.cpp b/src/lib/utils/ghash/ghash.cpp
index f94ca411b..178fd126d 100644
--- a/src/lib/utils/ghash/ghash.cpp
+++ b/src/lib/utils/ghash/ghash.cpp
@@ -105,8 +105,8 @@ bool GHASH::has_keying_material() const {
    return !m_ghash.empty();
 }
 
-void GHASH::key_schedule(const uint8_t key[], size_t length) {
-   m_H.assign(key, key + length);
+void GHASH::key_schedule(std::span<const uint8_t> key) {
+   m_H.assign(key.begin(), key.end());  // TODO: C++23 - std::vector<>::assign_range()
    m_H_ad.resize(GCM_BS);
    m_ad_len = 0;
    m_text_len = 0;
diff --git a/src/lib/utils/ghash/ghash.h b/src/lib/utils/ghash/ghash.h
index 6b8bcd4a0..2545d8666 100644
--- a/src/lib/utils/ghash/ghash.h
+++ b/src/lib/utils/ghash/ghash.h
@@ -62,7 +62,7 @@ class GHASH final : public SymmetricAlgorithm {
       static void ghash_multiply_vperm(uint8_t x[16], const uint64_t HM[256], const uint8_t input[], size_t blocks);
 #endif
 
-      void key_schedule(const uint8_t key[], size_t key_len) override;
+      void key_schedule(std::span<const uint8_t> key) override;
 
       void ghash_multiply(secure_vector<uint8_t>& x, const uint8_t input[], size_t blocks);
 


Test patch:
diff --git a/src/tests/test_bufcomp.cpp b/src/tests/test_bufcomp.cpp
index 8ce0ecb70..04df35684 100644
--- a/src/tests/test_bufcomp.cpp
+++ b/src/tests/test_bufcomp.cpp
@@ -27,8 +27,8 @@ class Test_Buf_Comp final : public Botan::Buffered_Computation {
 
       size_t output_length() const override { return sizeof(m_counter); }
 
-      void add_data(const uint8_t input[], size_t length) override {
-         if(m_result.test_eq("input length as expected", length, size_t(5))) {
+      void add_data(std::span<const uint8_t> input) override {
+         if(m_result.test_eq("input length as expected", input.size(), size_t(5))) {
             m_result.confirm("input[0] == 'A'", input[0] == 'A');
             m_result.confirm("input[0] == 'B'", input[1] == 'B');
             m_result.confirm("input[0] == 'C'", input[2] == 'C');
@@ -39,9 +39,9 @@ class Test_Buf_Comp final : public Botan::Buffered_Computation {
          ++m_counter;
       }
 
-      void final_result(uint8_t out[]) override {
+      void final_result(std::span<uint8_t> out) override {
          const uint8_t* counter = reinterpret_cast<const uint8_t*>(&m_counter);
-         std::copy(counter, counter + sizeof(m_counter), out);
+         std::copy(counter, counter + sizeof(m_counter), out.begin());
       }
 
       size_t counter() const { return m_counter; }
diff --git a/src/tests/test_ocb.cpp b/src/tests/test_ocb.cpp
index 6b7226a63..657249c0d 100644
--- a/src/tests/test_ocb.cpp
+++ b/src/tests/test_ocb.cpp
@@ -37,7 +37,7 @@ class OCB_Wide_Test_Block_Cipher final : public Botan::BlockCipher {
 
       bool has_keying_material() const override { return !m_key.empty(); }
 
-      void key_schedule(const uint8_t key[], size_t length) override { m_key.assign(key, key + length); }
+      void key_schedule(std::span<const uint8_t> key) override { m_key.assign(key.begin(), key.end()); }
 
       Botan::Key_Length_Specification key_spec() const override { return Botan::Key_Length_Specification(m_bs); }
 
diff --git a/src/tests/test_tls.cpp b/src/tests/test_tls.cpp
index 2c714109f..c2ffc25e5 100644
--- a/src/tests/test_tls.cpp
+++ b/src/tests/test_tls.cpp
@@ -125,9 +125,9 @@ class TLS_CBC_Tests final : public Text_Based_Test {
 
             size_t output_length() const override { return m_mac_len; }
 
-            void add_data(const uint8_t /*input*/[], size_t /*length*/) override {}
+            void add_data(std::span<const uint8_t> /*input*/) override {}
 
-            void final_result(uint8_t out[]) override {
+            void final_result(std::span<uint8_t> out) override {
                for(size_t i = 0; i != m_mac_len; ++i) {
                   out[i] = 0;
                }
@@ -144,7 +144,7 @@ class TLS_CBC_Tests final : public Text_Based_Test {
             }
 
          private:
-            void key_schedule(const uint8_t /*key*/[], size_t /*length*/) override {}
+            void key_schedule(std::span<const uint8_t> /* key */) override {}
 
             size_t m_mac_len;
       };
@@ -178,7 +178,7 @@ class TLS_CBC_Tests final : public Text_Based_Test {
             }
 
          private:
-            void key_schedule(const uint8_t /*key*/[], size_t /*length*/) override {}
+            void key_schedule(std::span<const uint8_t> /*key*/) override {}
 
             size_t m_bs;
       };