diff --git a/src/lib/prov/pkcs11/p11_ecdh.cpp b/src/lib/prov/pkcs11/p11_ecdh.cpp
index 765e711caef..d4ad64b76d5 100644
--- a/src/lib/prov/pkcs11/p11_ecdh.cpp
+++ b/src/lib/prov/pkcs11/p11_ecdh.cpp
@@ -47,20 +47,18 @@ class PKCS11_ECDH_KA_Operation final : public PK_Ops::Key_Agreement {
       /// The encoding in V2.20 was not specified and resulted in different implementations choosing different encodings.
       /// Applications relying only on a V2.20 encoding (e.g. the DER variant) other than the one specified now (raw) may not work with all V2.30 compliant tokens.
       secure_vector<uint8_t> agree(size_t key_len,
-                                   const uint8_t other_key[],
-                                   size_t other_key_len,
-                                   const uint8_t salt[],
-                                   size_t salt_len) override {
+                                   std::span<const uint8_t> other_key,
+                                   std::span<const uint8_t> salt) override {
          std::vector<uint8_t> der_encoded_other_key;
          if(m_key.point_encoding() == PublicPointEncoding::Der) {
-            DER_Encoder(der_encoded_other_key).encode(other_key, other_key_len, ASN1_Type::OctetString);
+            DER_Encoder(der_encoded_other_key).encode(other_key.data(), other_key.size(), ASN1_Type::OctetString);
             m_mechanism.set_ecdh_other_key(der_encoded_other_key.data(), der_encoded_other_key.size());
          } else {
-            m_mechanism.set_ecdh_other_key(other_key, other_key_len);
+            m_mechanism.set_ecdh_other_key(other_key.data(), other_key.size());
          }
 
-         if(salt != nullptr && salt_len > 0) {
-            m_mechanism.set_ecdh_salt(salt, salt_len);
+         if(!salt.empty()) {
+            m_mechanism.set_ecdh_salt(salt.data(), salt.size());
          }
 
          ObjectHandle secret_handle = 0;
diff --git a/src/lib/prov/pkcs11/p11_ecdsa.cpp b/src/lib/prov/pkcs11/p11_ecdsa.cpp
index e148d1b68d5..24533d7b515 100644
--- a/src/lib/prov/pkcs11/p11_ecdsa.cpp
+++ b/src/lib/prov/pkcs11/p11_ecdsa.cpp
@@ -60,12 +60,12 @@ class PKCS11_ECDSA_Signature_Operation final : public PK_Ops::Signature {
             m_mechanism(MechanismWrapper::create_ecdsa_mechanism(hash)),
             m_hash(hash) {}
 
-      void update(const uint8_t msg[], size_t msg_len) override {
+      void update(std::span<const uint8_t> input) override {
          if(!m_initialized) {
             // first call to update: initialize and cache message because we can not determine yet whether a single- or multiple-part operation will be performed
             m_key.module()->C_SignInit(m_key.session().handle(), m_mechanism.data(), m_key.handle());
             m_initialized = true;
-            m_first_message = secure_vector<uint8_t>(msg, msg + msg_len);
+            m_first_message.assign(input.begin(), input.end());
             return;
          }
 
@@ -75,11 +75,11 @@ class PKCS11_ECDSA_Signature_Operation final : public PK_Ops::Signature {
             m_first_message.clear();
          }
 
-         m_key.module()->C_SignUpdate(m_key.session().handle(), msg, static_cast<Ulong>(msg_len));
+         m_key.module()->C_SignUpdate(m_key.session().handle(), input.data(), static_cast<Ulong>(input.size()));
       }
 
-      secure_vector<uint8_t> sign(RandomNumberGenerator& /*rng*/) override {
-         secure_vector<uint8_t> signature;
+      std::vector<uint8_t> sign(RandomNumberGenerator& /*rng*/) override {
+         std::vector<uint8_t> signature;
          if(!m_first_message.empty()) {
             // single call to update: perform single-part operation
             m_key.module()->C_Sign(m_key.session().handle(), m_first_message, signature);
@@ -121,12 +121,12 @@ class PKCS11_ECDSA_Verification_Operation final : public PK_Ops::Verification {
             m_mechanism(MechanismWrapper::create_ecdsa_mechanism(hash)),
             m_hash(hash) {}
 
-      void update(const uint8_t msg[], size_t msg_len) override {
+      void update(std::span<const uint8_t> input) override {
          if(!m_initialized) {
             // first call to update: initialize and cache message because we can not determine yet whether a single- or multiple-part operation will be performed
             m_key.module()->C_VerifyInit(m_key.session().handle(), m_mechanism.data(), m_key.handle());
             m_initialized = true;
-            m_first_message = secure_vector<uint8_t>(msg, msg + msg_len);
+            m_first_message.assign(input.begin(), input.end());
             return;
          }
 
@@ -136,23 +136,24 @@ class PKCS11_ECDSA_Verification_Operation final : public PK_Ops::Verification {
             m_first_message.clear();
          }
 
-         m_key.module()->C_VerifyUpdate(m_key.session().handle(), msg, static_cast<Ulong>(msg_len));
+         m_key.module()->C_VerifyUpdate(m_key.session().handle(), input.data(), static_cast<Ulong>(input.size()));
       }
 
-      bool is_valid_signature(const uint8_t sig[], size_t sig_len) override {
+      bool is_valid_signature(std::span<const uint8_t> sig) override {
          ReturnValue return_value = ReturnValue::SignatureInvalid;
          if(!m_first_message.empty()) {
             // single call to update: perform single-part operation
             m_key.module()->C_Verify(m_key.session().handle(),
                                      m_first_message.data(),
                                      static_cast<Ulong>(m_first_message.size()),
-                                     sig,
-                                     static_cast<Ulong>(sig_len),
+                                     sig.data(),
+                                     static_cast<Ulong>(sig.size()),
                                      &return_value);
             m_first_message.clear();
          } else {
             // multiple calls to update (or none): finish multiple-part operation
-            m_key.module()->C_VerifyFinal(m_key.session().handle(), sig, static_cast<Ulong>(sig_len), &return_value);
+            m_key.module()->C_VerifyFinal(
+               m_key.session().handle(), sig.data(), static_cast<Ulong>(sig.size()), &return_value);
          }
          m_initialized = false;
          if(return_value != ReturnValue::OK && return_value != ReturnValue::SignatureInvalid) {
diff --git a/src/lib/prov/pkcs11/p11_rsa.cpp b/src/lib/prov/pkcs11/p11_rsa.cpp
index bca626631d5..86c8c36bb0f 100644
--- a/src/lib/prov/pkcs11/p11_rsa.cpp
+++ b/src/lib/prov/pkcs11/p11_rsa.cpp
@@ -124,11 +124,11 @@ class PKCS11_RSA_Decryption_Operation final : public PK_Ops::Decryption {
 
       size_t plaintext_length(size_t /*ctext_len*/) const override { return m_key.get_n().bytes(); }
 
-      secure_vector<uint8_t> decrypt(uint8_t& valid_mask, const uint8_t ciphertext[], size_t ciphertext_len) override {
+      secure_vector<uint8_t> decrypt(uint8_t& valid_mask, std::span<const uint8_t> ctext) override {
          valid_mask = 0;
          m_key.module()->C_DecryptInit(m_key.session().handle(), m_mechanism.data(), m_key.handle());
 
-         std::vector<uint8_t> encrypted_data(ciphertext, ciphertext + ciphertext_len);
+         std::vector<uint8_t> encrypted_data(ctext.begin(), ctext.end());
 
          const size_t modulus_bytes = (m_key.get_n().bits() + 7) / 8;
 
@@ -173,8 +173,8 @@ class PKCS11_RSA_Decryption_Operation_Software_EME final : public PK_Ops::Decryp
 
       size_t plaintext_length(size_t ctext_len) const override { return m_raw_decryptor.plaintext_length(ctext_len); }
 
-      secure_vector<uint8_t> raw_decrypt(const uint8_t input[], size_t input_len) override {
-         return m_raw_decryptor.decrypt(input, input_len);
+      secure_vector<uint8_t> raw_decrypt(std::span<const uint8_t> input) override {
+         return m_raw_decryptor.decrypt(input.data(), input.size());
       }
 
    private:
@@ -194,13 +194,13 @@ class PKCS11_RSA_Encryption_Operation final : public PK_Ops::Encryption {
 
       size_t max_input_bits() const override { return m_bits; }
 
-      secure_vector<uint8_t> encrypt(const uint8_t msg[], size_t msg_len, RandomNumberGenerator& /*rng*/) override {
+      std::vector<uint8_t> encrypt(std::span<const uint8_t> input, RandomNumberGenerator& /*rng*/) override {
          m_key.module()->C_EncryptInit(m_key.session().handle(), m_mechanism.data(), m_key.handle());
 
-         secure_vector<uint8_t> encrytped_data;
+         std::vector<uint8_t> encrypted_data;
          m_key.module()->C_Encrypt(
-            m_key.session().handle(), secure_vector<uint8_t>(msg, msg + msg_len), encrytped_data);
-         return encrytped_data;
+            m_key.session().handle(), secure_vector<uint8_t>(input.begin(), input.end()), encrypted_data);
+         return encrypted_data;
       }
 
    private:
@@ -216,12 +216,12 @@ class PKCS11_RSA_Signature_Operation final : public PK_Ops::Signature {
 
       size_t signature_length() const override { return m_key.get_n().bytes(); }
 
-      void update(const uint8_t msg[], size_t msg_len) override {
+      void update(std::span<const uint8_t> input) override {
          if(!m_initialized) {
             // first call to update: initialize and cache message because we can not determine yet whether a single- or multiple-part operation will be performed
             m_key.module()->C_SignInit(m_key.session().handle(), m_mechanism.data(), m_key.handle());
             m_initialized = true;
-            m_first_message = secure_vector<uint8_t>(msg, msg + msg_len);
+            m_first_message.assign(input.begin(), input.end());
             return;
          }
 
@@ -231,11 +231,11 @@ class PKCS11_RSA_Signature_Operation final : public PK_Ops::Signature {
             m_first_message.clear();
          }
 
-         m_key.module()->C_SignUpdate(m_key.session().handle(), msg, static_cast<Ulong>(msg_len));
+         m_key.module()->C_SignUpdate(m_key.session().handle(), input.data(), static_cast<Ulong>(input.size()));
       }
 
-      secure_vector<uint8_t> sign(RandomNumberGenerator& /*rng*/) override {
-         secure_vector<uint8_t> signature;
+      std::vector<uint8_t> sign(RandomNumberGenerator& /*rng*/) override {
+         std::vector<uint8_t> signature;
          if(!m_first_message.empty()) {
             // single call to update: perform single-part operation
             m_key.module()->C_Sign(m_key.session().handle(), m_first_message, signature);
@@ -331,12 +331,12 @@ class PKCS11_RSA_Verification_Operation final : public PK_Ops::Verification {
       PKCS11_RSA_Verification_Operation(const PKCS11_RSA_PublicKey& key, std::string_view padding) :
             m_key(key), m_mechanism(MechanismWrapper::create_rsa_sign_mechanism(padding)) {}
 
-      void update(const uint8_t msg[], size_t msg_len) override {
+      void update(std::span<const uint8_t> input) override {
          if(!m_initialized) {
             // first call to update: initialize and cache message because we can not determine yet whether a single- or multiple-part operation will be performed
             m_key.module()->C_VerifyInit(m_key.session().handle(), m_mechanism.data(), m_key.handle());
             m_initialized = true;
-            m_first_message = secure_vector<uint8_t>(msg, msg + msg_len);
+            m_first_message.assign(input.begin(), input.end());
             return;
          }
 
@@ -346,23 +346,24 @@ class PKCS11_RSA_Verification_Operation final : public PK_Ops::Verification {
             m_first_message.clear();
          }
 
-         m_key.module()->C_VerifyUpdate(m_key.session().handle(), msg, static_cast<Ulong>(msg_len));
+         m_key.module()->C_VerifyUpdate(m_key.session().handle(), input.data(), static_cast<Ulong>(input.size()));
       }
 
-      bool is_valid_signature(const uint8_t sig[], size_t sig_len) override {
+      bool is_valid_signature(std::span<const uint8_t> sig) override {
          ReturnValue return_value = ReturnValue::SignatureInvalid;
          if(!m_first_message.empty()) {
             // single call to update: perform single-part operation
             m_key.module()->C_Verify(m_key.session().handle(),
                                      m_first_message.data(),
                                      static_cast<Ulong>(m_first_message.size()),
-                                     sig,
-                                     static_cast<Ulong>(sig_len),
+                                     sig.data(),
+                                     static_cast<Ulong>(sig.size()),
                                      &return_value);
             m_first_message.clear();
          } else {
             // multiple calls to update (or none): finish multiple-part operation
-            m_key.module()->C_VerifyFinal(m_key.session().handle(), sig, static_cast<Ulong>(sig_len), &return_value);
+            m_key.module()->C_VerifyFinal(
+               m_key.session().handle(), sig.data(), static_cast<Ulong>(sig.size()), &return_value);
          }
          m_initialized = false;
          if(return_value != ReturnValue::OK && return_value != ReturnValue::SignatureInvalid) {
diff --git a/src/lib/prov/tpm/tpm.cpp b/src/lib/prov/tpm/tpm.cpp
index a2c95da3cba..5444fa5837b 100644
--- a/src/lib/prov/tpm/tpm.cpp
+++ b/src/lib/prov/tpm/tpm.cpp
@@ -321,7 +321,7 @@ class TPM_Signing_Operation final : public PK_Ops::Signature {
 
       size_t signature_length() const override { return m_key.get_n().bytes(); }
 
-      void update(const uint8_t msg[], size_t msg_len) override { m_hash->update(msg, msg_len); }
+      void update(std::span<const uint8_t> msg) override { m_hash->update(msg); }
 
       AlgorithmIdentifier algorithm_identifier() const override {
          const std::string full_name = "RSA/EMSA3(" + m_hash->name() + ")";
@@ -329,7 +329,7 @@ class TPM_Signing_Operation final : public PK_Ops::Signature {
          return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_EMPTY_PARAM);
       }
 
-      secure_vector<uint8_t> sign(RandomNumberGenerator&) override {
+      std::vector<uint8_t> sign(RandomNumberGenerator&) override {
          /*
          * v1.2 TPMs will only sign with PKCS #1 v1.5 padding. SHA-1 is built
          * in, all other hash inputs (TSS_HASH_OTHER) are treated as the
@@ -352,7 +352,7 @@ class TPM_Signing_Operation final : public PK_Ops::Signature {
          BYTE* sig_bytes = nullptr;
          UINT32 sig_len = 0;
          TSPI_CHECK_SUCCESS(::Tspi_Hash_Sign(tpm_hash, m_key.handle(), &sig_len, &sig_bytes));
-         secure_vector<uint8_t> sig(sig_bytes, sig_bytes + sig_len);
+         std::vector<uint8_t> sig(sig_bytes, sig_bytes + sig_len);
 
          // TODO: RAII for Context_FreeMemory
          TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(ctx, sig_bytes));
diff --git a/src/lib/pubkey/curve448/ed448/ed448.cpp b/src/lib/pubkey/curve448/ed448/ed448.cpp
index 81f75ec059f..3b1d8cb4fe8 100644
--- a/src/lib/pubkey/curve448/ed448/ed448.cpp
+++ b/src/lib/pubkey/curve448/ed448/ed448.cpp
@@ -146,12 +146,12 @@ class Ed448_Verify_Operation final : public PK_Ops::Verification {
          }
       }
 
-      void update(const uint8_t msg[], size_t msg_len) override { m_message->update({msg, msg_len}); }
+      void update(std::span<const uint8_t> input) override { m_message->update(input); }
 
-      bool is_valid_signature(const uint8_t sig[], size_t sig_len) override {
+      bool is_valid_signature(std::span<const uint8_t> sig) override {
          const auto msg = m_message->get_and_clear();
          try {
-            return verify_signature(m_pk, m_prehash_function.has_value(), {}, {sig, sig_len}, msg);
+            return verify_signature(m_pk, m_prehash_function.has_value(), {}, sig, msg);
          } catch(Decoding_Error&) {
             return false;
          }
@@ -185,9 +185,9 @@ class Ed448_Sign_Operation final : public PK_Ops::Signature {
          }
       }
 
-      void update(const uint8_t msg[], size_t msg_len) override { m_message->update({msg, msg_len}); }
+      void update(std::span<const uint8_t> input) override { m_message->update(input); }
 
-      secure_vector<uint8_t> sign(RandomNumberGenerator& /*rng*/) override {
+      std::vector<uint8_t> sign(RandomNumberGenerator& /*rng*/) override {
          BOTAN_ASSERT_NOMSG(m_sk.size() == ED448_LEN);
          auto scope = CT::scoped_poison(m_sk);
          const auto sig = sign_message(
diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp b/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp
index fdeb73a5025..d4e531003ae 100644
--- a/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp
+++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp
@@ -193,7 +193,7 @@ class Dilithium_Signature_Operation final : public PK_Ops::Signature {
             m_t0(ntt(m_priv_key->t0().clone())),
             m_A(Dilithium_Algos::expand_A(m_priv_key->rho(), m_priv_key->mode())) {}
 
-      void update(const uint8_t msg[], size_t msg_len) override { m_h.update({msg, msg_len}); }
+      void update(std::span<const uint8_t> input) override { m_h.update(input); }
 
       /**
        * NIST FIPS 204 IPD, Algorithm 2 (ML-DSA.Sign)
@@ -203,7 +203,7 @@ class Dilithium_Signature_Operation final : public PK_Ops::Signature {
        * s2 and t0 are done in the constructor of this class, as a 'signature
        * operation' may be used to sign multiple messages.
        */
-      secure_vector<uint8_t> sign(RandomNumberGenerator& rng) override {
+      std::vector<uint8_t> sign(RandomNumberGenerator& rng) override {
          auto scope = CT::scoped_poison(*m_priv_key);
 
          const auto mu = m_h.final();
@@ -300,7 +300,7 @@ class Dilithium_Verification_Operation final : public PK_Ops::Verification {
             m_t1_ntt_shifted(ntt(m_pub_key->t1() << DilithiumConstants::D)),
             m_h(m_pub_key->mode().symmetric_primitives().get_message_hash(m_pub_key->tr())) {}
 
-      void update(const uint8_t msg[], size_t msg_len) override { m_h.update({msg, msg_len}); }
+      void update(std::span<const uint8_t> input) override { m_h.update(input); }
 
       /**
        * NIST FIPS 204 IPD, Algorithm 3 (ML-DSA.Verify)
@@ -309,10 +309,10 @@ class Dilithium_Verification_Operation final : public PK_Ops::Verification {
        * matrix A is expanded from 'rho' in the constructor of this class, as
        * a 'verification operation' may be used to verify multiple signatures.
        */
-      bool is_valid_signature(const uint8_t* sig, size_t sig_len) override {
+      bool is_valid_signature(std::span<const uint8_t> sig) override {
          const auto& mode = m_pub_key->mode();
          const auto& sympri = mode.symmetric_primitives();
-         StrongSpan<const DilithiumSerializedSignature> sig_bytes({sig, sig_len});
+         StrongSpan<const DilithiumSerializedSignature> sig_bytes(sig);
 
          if(sig_bytes.size() != mode.signature_bytes()) {
             return false;
diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium_types.h b/src/lib/pubkey/dilithium/dilithium_common/dilithium_types.h
index 941d3bd3e15..7ab33ff3321 100644
--- a/src/lib/pubkey/dilithium/dilithium_common/dilithium_types.h
+++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium_types.h
@@ -49,7 +49,7 @@ using DilithiumHashedPublicKey = Strong<std::vector<uint8_t>, struct DilithiumHa
 using DilithiumMessageRepresentative = Strong<std::vector<uint8_t>, struct DilithiumMessageRepresentative_>;
 
 /// Serialized signature data
-using DilithiumSerializedSignature = Strong<secure_vector<uint8_t>, struct DilithiumSerializedSignature_>;
+using DilithiumSerializedSignature = Strong<std::vector<uint8_t>, struct DilithiumSerializedSignature_>;
 
 /// Serialized representation of a commitment w1
 using DilithiumSerializedCommitment = Strong<std::vector<uint8_t>, struct DilithiumSerializedCommitment_>;
diff --git a/src/lib/pubkey/dsa/dsa.cpp b/src/lib/pubkey/dsa/dsa.cpp
index 866db8dd300..18b14ab07f4 100644
--- a/src/lib/pubkey/dsa/dsa.cpp
+++ b/src/lib/pubkey/dsa/dsa.cpp
@@ -135,7 +135,7 @@ class DSA_Signature_Operation final : public PK_Ops::Signature_with_Hash {
 
       size_t signature_length() const override { return 2 * m_key->group().q_bytes(); }
 
-      secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len, RandomNumberGenerator& rng) override;
+      std::vector<uint8_t> raw_sign(std::span<const uint8_t>, RandomNumberGenerator& rng) override;
 
       AlgorithmIdentifier algorithm_identifier() const override;
 
@@ -150,13 +150,11 @@ AlgorithmIdentifier DSA_Signature_Operation::algorithm_identifier() const {
    return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_EMPTY_PARAM);
 }
 
-secure_vector<uint8_t> DSA_Signature_Operation::raw_sign(const uint8_t msg[],
-                                                         size_t msg_len,
-                                                         RandomNumberGenerator& rng) {
+std::vector<uint8_t> DSA_Signature_Operation::raw_sign(std::span<const uint8_t> msg, RandomNumberGenerator& rng) {
    const DL_Group& group = m_key->group();
    const BigInt& q = group.get_q();
 
-   BigInt m = BigInt::from_bytes_with_max_bits(msg, msg_len, group.q_bits());
+   BigInt m = BigInt::from_bytes_with_max_bits(msg.data(), msg.size(), group.q_bits());
 
    if(m >= q) {
       m -= q;
@@ -198,7 +196,7 @@ secure_vector<uint8_t> DSA_Signature_Operation::raw_sign(const uint8_t msg[],
       throw Internal_Error("Computed zero r/s during DSA signature");
    }
 
-   return BigInt::encode_fixed_length_int_pair(r, s, q.bytes());
+   return unlock(BigInt::encode_fixed_length_int_pair(r, s, q.bytes()));
 }
 
 /**
@@ -212,30 +210,30 @@ class DSA_Verification_Operation final : public PK_Ops::Verification_with_Hash {
       DSA_Verification_Operation(const std::shared_ptr<const DL_PublicKey>& key, const AlgorithmIdentifier& alg_id) :
             PK_Ops::Verification_with_Hash(alg_id, "DSA"), m_key(key) {}
 
-      bool verify(const uint8_t msg[], size_t msg_len, const uint8_t sig[], size_t sig_len) override;
+      bool verify(std::span<const uint8_t> input, std::span<const uint8_t> sig) override;
 
    private:
       std::shared_ptr<const DL_PublicKey> m_key;
 };
 
-bool DSA_Verification_Operation::verify(const uint8_t msg[], size_t msg_len, const uint8_t sig[], size_t sig_len) {
+bool DSA_Verification_Operation::verify(std::span<const uint8_t> input, std::span<const uint8_t> sig) {
    const auto group = m_key->group();
 
    const BigInt& q = group.get_q();
    const size_t q_bytes = q.bytes();
 
-   if(sig_len != 2 * q_bytes) {
+   if(sig.size() != 2 * q_bytes) {
       return false;
    }
 
-   BigInt r(sig, q_bytes);
-   BigInt s(sig + q_bytes, q_bytes);
+   BigInt r(sig.data(), q_bytes);
+   BigInt s(sig.data() + q_bytes, q_bytes);
 
    if(r == 0 || r >= q || s == 0 || s >= q) {
       return false;
    }
 
-   BigInt i = BigInt::from_bytes_with_max_bits(msg, msg_len, group.q_bits());
+   BigInt i = BigInt::from_bytes_with_max_bits(input.data(), input.size(), group.q_bits());
    if(i >= q) {
       i -= q;
    }
diff --git a/src/lib/pubkey/ecdsa/ecdsa.cpp b/src/lib/pubkey/ecdsa/ecdsa.cpp
index 4717703a459..cc43078a9be 100644
--- a/src/lib/pubkey/ecdsa/ecdsa.cpp
+++ b/src/lib/pubkey/ecdsa/ecdsa.cpp
@@ -133,7 +133,7 @@ class ECDSA_Signature_Operation final : public PK_Ops::Signature_with_Hash {
 
       size_t signature_length() const override { return 2 * m_group.get_order_bytes(); }
 
-      secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len, RandomNumberGenerator& rng) override;
+      std::vector<uint8_t> raw_sign(std::span<const uint8_t> msg, RandomNumberGenerator& rng) override;
 
       AlgorithmIdentifier algorithm_identifier() const override;
 
@@ -157,10 +157,8 @@ AlgorithmIdentifier ECDSA_Signature_Operation::algorithm_identifier() const {
    return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_EMPTY_PARAM);
 }
 
-secure_vector<uint8_t> ECDSA_Signature_Operation::raw_sign(const uint8_t msg[],
-                                                           size_t msg_len,
-                                                           RandomNumberGenerator& rng) {
-   const auto m = EC_Scalar::from_bytes_with_trunc(m_group, std::span{msg, msg_len});
+std::vector<uint8_t> ECDSA_Signature_Operation::raw_sign(std::span<const uint8_t> msg, RandomNumberGenerator& rng) {
+   const auto m = EC_Scalar::from_bytes_with_trunc(m_group, msg);
 
 #if defined(BOTAN_HAS_RFC6979_GENERATOR)
    const auto k = m_rfc6979->nonce_for(m_group, m);
@@ -187,7 +185,7 @@ secure_vector<uint8_t> ECDSA_Signature_Operation::raw_sign(const uint8_t msg[],
       throw Internal_Error("During ECDSA signature generated zero r/s");
    }
 
-   return EC_Scalar::serialize_pair<secure_vector<uint8_t>>(r, s);
+   return EC_Scalar::serialize_pair(r, s);
 }
 
 /**
@@ -203,19 +201,19 @@ class ECDSA_Verification_Operation final : public PK_Ops::Verification_with_Hash
             m_group(ecdsa.domain()),
             m_gy_mul(m_group, ecdsa.public_point()) {}
 
-      bool verify(const uint8_t msg[], size_t msg_len, const uint8_t sig[], size_t sig_len) override;
+      bool verify(std::span<const uint8_t> msg, std::span<const uint8_t> sig) override;
 
    private:
       const EC_Group m_group;
       const EC_Group::Mul2Table m_gy_mul;
 };
 
-bool ECDSA_Verification_Operation::verify(const uint8_t msg[], size_t msg_len, const uint8_t sig[], size_t sig_len) {
-   if(auto rs = EC_Scalar::deserialize_pair(m_group, std::span{sig, sig_len})) {
+bool ECDSA_Verification_Operation::verify(std::span<const uint8_t> msg, std::span<const uint8_t> sig) {
+   if(auto rs = EC_Scalar::deserialize_pair(m_group, sig)) {
       const auto& [r, s] = rs.value();
 
       if(r.is_nonzero() && s.is_nonzero()) {
-         const auto m = EC_Scalar::from_bytes_with_trunc(m_group, std::span{msg, msg_len});
+         const auto m = EC_Scalar::from_bytes_with_trunc(m_group, msg);
 
          const auto w = s.invert();
 
diff --git a/src/lib/pubkey/ecgdsa/ecgdsa.cpp b/src/lib/pubkey/ecgdsa/ecgdsa.cpp
index a89725290b3..812b2315551 100644
--- a/src/lib/pubkey/ecgdsa/ecgdsa.cpp
+++ b/src/lib/pubkey/ecgdsa/ecgdsa.cpp
@@ -41,7 +41,7 @@ class ECGDSA_Signature_Operation final : public PK_Ops::Signature_with_Hash {
             m_group(ecgdsa.domain()),
             m_x(EC_Scalar::from_bigint(m_group, ecgdsa.private_value())) {}
 
-      secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len, RandomNumberGenerator& rng) override;
+      std::vector<uint8_t> raw_sign(std::span<const uint8_t> msg, RandomNumberGenerator& rng) override;
 
       size_t signature_length() const override { return 2 * m_group.get_order_bytes(); }
 
@@ -59,10 +59,8 @@ AlgorithmIdentifier ECGDSA_Signature_Operation::algorithm_identifier() const {
    return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_EMPTY_PARAM);
 }
 
-secure_vector<uint8_t> ECGDSA_Signature_Operation::raw_sign(const uint8_t msg[],
-                                                            size_t msg_len,
-                                                            RandomNumberGenerator& rng) {
-   const auto m = EC_Scalar::from_bytes_with_trunc(m_group, std::span{msg, msg_len});
+std::vector<uint8_t> ECGDSA_Signature_Operation::raw_sign(std::span<const uint8_t> msg, RandomNumberGenerator& rng) {
+   const auto m = EC_Scalar::from_bytes_with_trunc(m_group, msg);
 
    const auto k = EC_Scalar::random(m_group, rng);
 
@@ -75,7 +73,7 @@ secure_vector<uint8_t> ECGDSA_Signature_Operation::raw_sign(const uint8_t msg[],
       throw Internal_Error("During ECGDSA signature generated zero r/s");
    }
 
-   return EC_Scalar::serialize_pair<secure_vector<uint8_t>>(r, s);
+   return EC_Scalar::serialize_pair(r, s);
 }
 
 /**
@@ -93,19 +91,19 @@ class ECGDSA_Verification_Operation final : public PK_Ops::Verification_with_Has
             m_group(ecgdsa.domain()),
             m_gy_mul(m_group, ecgdsa.public_point()) {}
 
-      bool verify(const uint8_t msg[], size_t msg_len, const uint8_t sig[], size_t sig_len) override;
+      bool verify(std::span<const uint8_t> msg, std::span<const uint8_t> sig) override;
 
    private:
       const EC_Group m_group;
       const EC_Group::Mul2Table m_gy_mul;
 };
 
-bool ECGDSA_Verification_Operation::verify(const uint8_t msg[], size_t msg_len, const uint8_t sig[], size_t sig_len) {
-   if(auto rs = EC_Scalar::deserialize_pair(m_group, std::span{sig, sig_len})) {
+bool ECGDSA_Verification_Operation::verify(std::span<const uint8_t> msg, std::span<const uint8_t> sig) {
+   if(auto rs = EC_Scalar::deserialize_pair(m_group, sig)) {
       const auto& [r, s] = rs.value();
 
       if(r.is_nonzero() && s.is_nonzero()) {
-         const auto m = EC_Scalar::from_bytes_with_trunc(m_group, std::span{msg, msg_len});
+         const auto m = EC_Scalar::from_bytes_with_trunc(m_group, msg);
 
          const auto w = r.invert();
 
diff --git a/src/lib/pubkey/eckcdsa/eckcdsa.cpp b/src/lib/pubkey/eckcdsa/eckcdsa.cpp
index 728d0d4be72..e381cb67f3f 100644
--- a/src/lib/pubkey/eckcdsa/eckcdsa.cpp
+++ b/src/lib/pubkey/eckcdsa/eckcdsa.cpp
@@ -103,7 +103,7 @@ std::vector<uint8_t> eckcdsa_prefix(const EC_Point& point, size_t hash_block_siz
  * @param[in,out] digest The hash output to potentially truncate.
  * @param[in] group_order_bytes Size of the group order.
  */
-void truncate_hash_if_needed(secure_vector<uint8_t>& digest, size_t group_order_bytes) {
+void truncate_hash_if_needed(std::vector<uint8_t>& digest, size_t group_order_bytes) {
    if(digest.size() > group_order_bytes) {
       const size_t bytes_to_truncate = digest.size() - group_order_bytes;
       digest.erase(digest.begin(), digest.begin() + bytes_to_truncate);
@@ -123,19 +123,19 @@ class ECKCDSA_Signature_Operation final : public PK_Ops::Signature {
          m_prefix = eckcdsa_prefix(eckcdsa.public_point(), m_hash->hash_block_size());
       }
 
-      void update(const uint8_t msg[], size_t msg_len) override {
+      void update(std::span<const uint8_t> input) override {
          if(!m_prefix_used) {
             m_hash->update(m_prefix.data(), m_prefix.size());
             m_prefix_used = true;
          }
-         m_hash->update(msg, msg_len);
+         m_hash->update(input);
       }
 
-      secure_vector<uint8_t> sign(RandomNumberGenerator& rng) override {
+      std::vector<uint8_t> sign(RandomNumberGenerator& rng) override {
          m_prefix_used = false;
-         secure_vector<uint8_t> digest = m_hash->final();
+         std::vector<uint8_t> digest = m_hash->final_stdvec();
          truncate_hash_if_needed(digest, m_group.get_order_bytes());
-         return raw_sign(digest.data(), digest.size(), rng);
+         return raw_sign(digest, rng);
       }
 
       size_t signature_length() const override { return 2 * m_group.get_order_bytes(); }
@@ -145,7 +145,7 @@ class ECKCDSA_Signature_Operation final : public PK_Ops::Signature {
       std::string hash_function() const override { return m_hash->name(); }
 
    private:
-      secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len, RandomNumberGenerator& rng);
+      std::vector<uint8_t> raw_sign(std::span<const uint8_t> msg, RandomNumberGenerator& rng);
 
       const EC_Group m_group;
       const EC_Scalar m_x;
@@ -161,18 +161,16 @@ AlgorithmIdentifier ECKCDSA_Signature_Operation::algorithm_identifier() const {
    return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_EMPTY_PARAM);
 }
 
-secure_vector<uint8_t> ECKCDSA_Signature_Operation::raw_sign(const uint8_t msg[],
-                                                             size_t msg_len,
-                                                             RandomNumberGenerator& rng) {
+std::vector<uint8_t> ECKCDSA_Signature_Operation::raw_sign(std::span<const uint8_t> msg, RandomNumberGenerator& rng) {
    const auto k = EC_Scalar::random(m_group, rng);
 
    m_hash->update(EC_AffinePoint::g_mul(k, rng, m_ws).x_bytes());
-   secure_vector<uint8_t> c = m_hash->final();
+   auto c = m_hash->final_stdvec();
    truncate_hash_if_needed(c, m_group.get_order_bytes());
 
    const auto r = c;
 
-   BOTAN_ASSERT_NOMSG(msg_len == c.size());
+   BOTAN_ASSERT_NOMSG(msg.size() == c.size());
    xor_buf(c, msg, c.size());
    const auto w = EC_Scalar::from_bytes_mod_order(m_group, c);
 
@@ -205,14 +203,14 @@ class ECKCDSA_Verification_Operation final : public PK_Ops::Verification {
          m_prefix = eckcdsa_prefix(eckcdsa.public_point(), m_hash->hash_block_size());
       }
 
-      void update(const uint8_t msg[], size_t msg_len) override;
+      void update(std::span<const uint8_t> msg) override;
 
-      bool is_valid_signature(const uint8_t sig[], size_t sig_len) override;
+      bool is_valid_signature(std::span<const uint8_t> sig) override;
 
       std::string hash_function() const override { return m_hash->name(); }
 
    private:
-      bool verify(const uint8_t msg[], size_t msg_len, const uint8_t sig[], size_t sig_len);
+      bool verify(std::span<const uint8_t> msg, std::span<const uint8_t> sig);
 
       const EC_Group m_group;
       const EC_Group::Mul2Table m_gy_mul;
@@ -221,41 +219,41 @@ class ECKCDSA_Verification_Operation final : public PK_Ops::Verification {
       bool m_prefix_used;
 };
 
-void ECKCDSA_Verification_Operation::update(const uint8_t msg[], size_t msg_len) {
+void ECKCDSA_Verification_Operation::update(std::span<const uint8_t> msg) {
    if(!m_prefix_used) {
       m_prefix_used = true;
       m_hash->update(m_prefix.data(), m_prefix.size());
    }
-   m_hash->update(msg, msg_len);
+   m_hash->update(msg);
 }
 
-bool ECKCDSA_Verification_Operation::is_valid_signature(const uint8_t sig[], size_t sig_len) {
+bool ECKCDSA_Verification_Operation::is_valid_signature(std::span<const uint8_t> sig) {
    m_prefix_used = false;
-   secure_vector<uint8_t> digest = m_hash->final();
+   std::vector<uint8_t> digest = m_hash->final_stdvec();
    truncate_hash_if_needed(digest, m_group.get_order_bytes());
-   return verify(digest.data(), digest.size(), sig, sig_len);
+   return verify(digest, sig);
 }
 
-bool ECKCDSA_Verification_Operation::verify(const uint8_t msg[], size_t msg_len, const uint8_t sig[], size_t sig_len) {
+bool ECKCDSA_Verification_Operation::verify(std::span<const uint8_t> msg, std::span<const uint8_t> sig) {
    //calculate size of r
 
    const size_t order_bytes = m_group.get_order_bytes();
 
-   const size_t size_r = std::min(msg_len, order_bytes);
-   if(sig_len != size_r + order_bytes) {
+   const size_t size_r = std::min(msg.size(), order_bytes);
+   if(sig.size() != size_r + order_bytes) {
       return false;
    }
 
-   secure_vector<uint8_t> r(sig, sig + size_r);
+   std::vector<uint8_t> r(sig.begin(), sig.begin() + size_r);
 
-   if(auto s = EC_Scalar::deserialize(m_group, std::span{sig, sig_len}.last(order_bytes))) {
-      secure_vector<uint8_t> r_xor_e(r);
+   if(auto s = EC_Scalar::deserialize(m_group, sig.last(order_bytes))) {
+      std::vector<uint8_t> r_xor_e(r);
       xor_buf(r_xor_e, msg, r.size());
 
       const auto w = EC_Scalar::from_bytes_mod_order(m_group, r_xor_e);
 
       if(auto q = m_gy_mul.mul2_vartime(w, s.value())) {
-         secure_vector<uint8_t> v = m_hash->process(q->x_bytes());
+         std::vector<uint8_t> v = m_hash->process<std::vector<uint8_t>>(q->x_bytes());
          truncate_hash_if_needed(v, m_group.get_order_bytes());
 
          return (v == r);
diff --git a/src/lib/pubkey/ed25519/ed25519_key.cpp b/src/lib/pubkey/ed25519/ed25519_key.cpp
index 2492758ea3e..32858ff4bdc 100644
--- a/src/lib/pubkey/ed25519/ed25519_key.cpp
+++ b/src/lib/pubkey/ed25519/ed25519_key.cpp
@@ -145,15 +145,15 @@ class Ed25519_Pure_Verify_Operation final : public PK_Ops::Verification {
    public:
       explicit Ed25519_Pure_Verify_Operation(const Ed25519_PublicKey& key) : m_key(key.get_public_key()) {}
 
-      void update(const uint8_t msg[], size_t msg_len) override { m_msg.insert(m_msg.end(), msg, msg + msg_len); }
+      void update(std::span<const uint8_t> msg) override { m_msg.insert(m_msg.end(), msg.begin(), msg.end()); }
 
-      bool is_valid_signature(const uint8_t sig[], size_t sig_len) override {
-         if(sig_len != 64) {
+      bool is_valid_signature(std::span<const uint8_t> sig) override {
+         if(sig.size() != 64) {
             return false;
          }
 
          BOTAN_ASSERT_EQUAL(m_key.size(), 32, "Expected size");
-         const bool ok = ed25519_verify(m_msg.data(), m_msg.size(), sig, m_key.data(), nullptr, 0);
+         const bool ok = ed25519_verify(m_msg.data(), m_msg.size(), sig.data(), m_key.data(), nullptr, 0);
          m_msg.clear();
          return ok;
       }
@@ -181,10 +181,10 @@ class Ed25519_Hashed_Verify_Operation final : public PK_Ops::Verification {
          }
       }
 
-      void update(const uint8_t msg[], size_t msg_len) override { m_hash->update(msg, msg_len); }
+      void update(std::span<const uint8_t> msg) override { m_hash->update(msg); }
 
-      bool is_valid_signature(const uint8_t sig[], size_t sig_len) override {
-         if(sig_len != 64) {
+      bool is_valid_signature(std::span<const uint8_t> sig) override {
+         if(sig.size() != 64) {
             return false;
          }
          std::vector<uint8_t> msg_hash(m_hash->output_length());
@@ -192,7 +192,7 @@ class Ed25519_Hashed_Verify_Operation final : public PK_Ops::Verification {
 
          BOTAN_ASSERT_EQUAL(m_key.size(), 32, "Expected size");
          return ed25519_verify(
-            msg_hash.data(), msg_hash.size(), sig, m_key.data(), m_domain_sep.data(), m_domain_sep.size());
+            msg_hash.data(), msg_hash.size(), sig.data(), m_key.data(), m_domain_sep.data(), m_domain_sep.size());
       }
 
       std::string hash_function() const override { return m_hash->name(); }
@@ -210,10 +210,10 @@ class Ed25519_Pure_Sign_Operation final : public PK_Ops::Signature {
    public:
       explicit Ed25519_Pure_Sign_Operation(const Ed25519_PrivateKey& key) : m_key(key.raw_private_key_bits()) {}
 
-      void update(const uint8_t msg[], size_t msg_len) override { m_msg.insert(m_msg.end(), msg, msg + msg_len); }
+      void update(std::span<const uint8_t> msg) override { m_msg.insert(m_msg.end(), msg.begin(), msg.end()); }
 
-      secure_vector<uint8_t> sign(RandomNumberGenerator& /*rng*/) override {
-         secure_vector<uint8_t> sig(64);
+      std::vector<uint8_t> sign(RandomNumberGenerator& /*rng*/) override {
+         std::vector<uint8_t> sig(64);
          ed25519_sign(sig.data(), m_msg.data(), m_msg.size(), m_key.data(), nullptr, 0);
          m_msg.clear();
          return sig;
@@ -252,10 +252,10 @@ class Ed25519_Hashed_Sign_Operation final : public PK_Ops::Signature {
 
       size_t signature_length() const override { return 64; }
 
-      void update(const uint8_t msg[], size_t msg_len) override { m_hash->update(msg, msg_len); }
+      void update(std::span<const uint8_t> msg) override { m_hash->update(msg); }
 
-      secure_vector<uint8_t> sign(RandomNumberGenerator& /*rng*/) override {
-         secure_vector<uint8_t> sig(64);
+      std::vector<uint8_t> sign(RandomNumberGenerator& /*rng*/) override {
+         std::vector<uint8_t> sig(64);
          std::vector<uint8_t> msg_hash(m_hash->output_length());
          m_hash->final(msg_hash.data());
          ed25519_sign(
diff --git a/src/lib/pubkey/elgamal/elgamal.cpp b/src/lib/pubkey/elgamal/elgamal.cpp
index 156fd40e65c..d19d9a2a722 100644
--- a/src/lib/pubkey/elgamal/elgamal.cpp
+++ b/src/lib/pubkey/elgamal/elgamal.cpp
@@ -111,17 +111,16 @@ class ElGamal_Encryption_Operation final : public PK_Ops::Encryption_with_EME {
 
       size_t max_ptext_input_bits() const override { return m_key->group().p_bits() - 1; }
 
-      secure_vector<uint8_t> raw_encrypt(const uint8_t msg[], size_t msg_len, RandomNumberGenerator& rng) override;
+      std::vector<uint8_t> raw_encrypt(std::span<const uint8_t> ptext, RandomNumberGenerator& rng) override;
 
    private:
       std::shared_ptr<const DL_PublicKey> m_key;
       std::shared_ptr<const Montgomery_Exponentation_State> m_monty_y_p;
 };
 
-secure_vector<uint8_t> ElGamal_Encryption_Operation::raw_encrypt(const uint8_t msg[],
-                                                                 size_t msg_len,
-                                                                 RandomNumberGenerator& rng) {
-   BigInt m(msg, msg_len);
+std::vector<uint8_t> ElGamal_Encryption_Operation::raw_encrypt(std::span<const uint8_t> ptext,
+                                                               RandomNumberGenerator& rng) {
+   BigInt m(ptext);
 
    const auto& group = m_key->group();
 
@@ -143,7 +142,7 @@ secure_vector<uint8_t> ElGamal_Encryption_Operation::raw_encrypt(const uint8_t m
    const BigInt a = group.power_g_p(k, k_bits);
    const BigInt b = group.multiply_mod_p(m, monty_execute(*m_monty_y_p, k, k_bits));
 
-   return BigInt::encode_fixed_length_int_pair(a, b, group.p_bytes());
+   return unlock(BigInt::encode_fixed_length_int_pair(a, b, group.p_bytes()));
 }
 
 /**
@@ -164,7 +163,7 @@ class ElGamal_Decryption_Operation final : public PK_Ops::Decryption_with_EME {
 
       size_t plaintext_length(size_t /*ctext_len*/) const override { return m_key->group().p_bytes(); }
 
-      secure_vector<uint8_t> raw_decrypt(const uint8_t msg[], size_t msg_len) override;
+      secure_vector<uint8_t> raw_decrypt(std::span<const uint8_t> ctext) override;
 
    private:
       BigInt powermod_x_p(const BigInt& v) const { return m_key->group().power_b_p(v, m_key->private_key()); }
@@ -173,17 +172,17 @@ class ElGamal_Decryption_Operation final : public PK_Ops::Decryption_with_EME {
       Blinder m_blinder;
 };
 
-secure_vector<uint8_t> ElGamal_Decryption_Operation::raw_decrypt(const uint8_t msg[], size_t msg_len) {
+secure_vector<uint8_t> ElGamal_Decryption_Operation::raw_decrypt(std::span<const uint8_t> ctext) {
    const auto& group = m_key->group();
 
    const size_t p_bytes = group.p_bytes();
 
-   if(msg_len != 2 * p_bytes) {
+   if(ctext.size() != 2 * p_bytes) {
       throw Invalid_Argument("ElGamal decryption: Invalid message");
    }
 
-   BigInt a(msg, p_bytes);
-   const BigInt b(msg + p_bytes, p_bytes);
+   BigInt a(ctext.first(p_bytes));
+   const BigInt b(ctext.last(p_bytes));
 
    if(a >= group.get_p() || b >= group.get_p()) {
       throw Invalid_Argument("ElGamal decryption: Invalid message");
diff --git a/src/lib/pubkey/gost_3410/gost_3410.cpp b/src/lib/pubkey/gost_3410/gost_3410.cpp
index a4bb18ef079..cce48ce5cbf 100644
--- a/src/lib/pubkey/gost_3410/gost_3410.cpp
+++ b/src/lib/pubkey/gost_3410/gost_3410.cpp
@@ -128,7 +128,7 @@ class GOST_3410_Signature_Operation final : public PK_Ops::Signature_with_Hash {
 
       AlgorithmIdentifier algorithm_identifier() const override;
 
-      secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len, RandomNumberGenerator& rng) override;
+      std::vector<uint8_t> raw_sign(std::span<const uint8_t> msg, RandomNumberGenerator& rng) override;
 
    private:
       const EC_Group m_group;
@@ -159,10 +159,8 @@ AlgorithmIdentifier GOST_3410_Signature_Operation::algorithm_identifier() const
    return AlgorithmIdentifier(oid_name, AlgorithmIdentifier::USE_EMPTY_PARAM);
 }
 
-secure_vector<uint8_t> GOST_3410_Signature_Operation::raw_sign(const uint8_t msg[],
-                                                               size_t msg_len,
-                                                               RandomNumberGenerator& rng) {
-   const auto e = gost_msg_to_scalar(m_group, std::span{msg, msg_len});
+std::vector<uint8_t> GOST_3410_Signature_Operation::raw_sign(std::span<const uint8_t> msg, RandomNumberGenerator& rng) {
+   const auto e = gost_msg_to_scalar(m_group, msg);
 
    const auto k = EC_Scalar::random(m_group, rng);
    const auto r = EC_Scalar::gk_x_mod_order(k, rng, m_ws);
@@ -172,7 +170,7 @@ secure_vector<uint8_t> GOST_3410_Signature_Operation::raw_sign(const uint8_t msg
       throw Internal_Error("GOST 34.10 signature generation failed, r/s equal to zero");
    }
 
-   return EC_Scalar::serialize_pair<secure_vector<uint8_t>>(s, r);
+   return EC_Scalar::serialize_pair(s, r);
 }
 
 std::string gost_hash_from_algid(const AlgorithmIdentifier& alg_id) {
@@ -210,22 +208,19 @@ class GOST_3410_Verification_Operation final : public PK_Ops::Verification_with_
             m_group(gost.domain()),
             m_gy_mul(m_group, gost.public_point()) {}
 
-      bool verify(const uint8_t msg[], size_t msg_len, const uint8_t sig[], size_t sig_len) override;
+      bool verify(std::span<const uint8_t> msg, std::span<const uint8_t> sig) override;
 
    private:
       const EC_Group m_group;
       const EC_Group::Mul2Table m_gy_mul;
 };
 
-bool GOST_3410_Verification_Operation::verify(const uint8_t msg[],
-                                              size_t msg_len,
-                                              const uint8_t sig[],
-                                              size_t sig_len) {
-   if(auto sr = EC_Scalar::deserialize_pair(m_group, std::span{sig, sig_len})) {
+bool GOST_3410_Verification_Operation::verify(std::span<const uint8_t> msg, std::span<const uint8_t> sig) {
+   if(auto sr = EC_Scalar::deserialize_pair(m_group, sig)) {
       const auto& [s, r] = sr.value();
 
       if(r.is_nonzero() && s.is_nonzero()) {
-         const auto e = gost_msg_to_scalar(m_group, std::span{msg, msg_len});
+         const auto e = gost_msg_to_scalar(m_group, msg);
 
          const auto v = e.invert();
 
diff --git a/src/lib/pubkey/hss_lms/hss.cpp b/src/lib/pubkey/hss_lms/hss.cpp
index 23f0f3706cb..52fc1d9401d 100644
--- a/src/lib/pubkey/hss_lms/hss.cpp
+++ b/src/lib/pubkey/hss_lms/hss.cpp
@@ -218,8 +218,8 @@ HSS_LMS_PrivateKeyInternal::HSS_LMS_PrivateKeyInternal(HSS_LMS_Params hss_params
    BOTAN_ARG_CHECK(m_identifier.size() == LMS_IDENTIFIER_LEN, "Invalid identifier size");
 }
 
-secure_vector<uint8_t> HSS_LMS_PrivateKeyInternal::sign(std::span<const uint8_t> msg) {
-   secure_vector<uint8_t> sig(HSS_Signature::size(hss_params()));
+std::vector<uint8_t> HSS_LMS_PrivateKeyInternal::sign(std::span<const uint8_t> msg) {
+   std::vector<uint8_t> sig(HSS_Signature::size(hss_params()));
    BufferStuffer sig_stuffer(sig);
    sig_stuffer.append(store_be(hss_params().L() - 1));
 
diff --git a/src/lib/pubkey/hss_lms/hss.h b/src/lib/pubkey/hss_lms/hss.h
index e640c769482..8a2cd93e5e3 100644
--- a/src/lib/pubkey/hss_lms/hss.h
+++ b/src/lib/pubkey/hss_lms/hss.h
@@ -176,7 +176,7 @@ class BOTAN_TEST_API HSS_LMS_PrivateKeyInternal final {
        *
        * @param msg The message to sign.
        */
-      secure_vector<uint8_t> sign(std::span<const uint8_t> msg);
+      std::vector<uint8_t> sign(std::span<const uint8_t> msg);
 
       /**
        * @brief Create the HSS root LMS tree's LMS_PrivateKey using the HSS-LMS private key.
diff --git a/src/lib/pubkey/hss_lms/hss_lms.cpp b/src/lib/pubkey/hss_lms/hss_lms.cpp
index fed4fb102b2..57f98cf6c80 100644
--- a/src/lib/pubkey/hss_lms/hss_lms.cpp
+++ b/src/lib/pubkey/hss_lms/hss_lms.cpp
@@ -63,14 +63,14 @@ class HSS_LMS_Verification_Operation final : public PK_Ops::Verification {
       HSS_LMS_Verification_Operation(std::shared_ptr<HSS_LMS_PublicKeyInternal> pub_key) :
             m_public(std::move(pub_key)) {}
 
-      void update(const uint8_t msg[], size_t msg_len) override {
-         m_msg_buffer.insert(m_msg_buffer.end(), msg, msg + msg_len);
+      void update(std::span<const uint8_t> msg) override {
+         m_msg_buffer.insert(m_msg_buffer.end(), msg.begin(), msg.end());
       }
 
-      bool is_valid_signature(const uint8_t* sig, size_t sig_len) override {
+      bool is_valid_signature(std::span<const uint8_t> sig) override {
          std::vector<uint8_t> message_to_verify = std::exchange(m_msg_buffer, {});
          try {
-            const auto signature = HSS_Signature::from_bytes_or_throw({sig, sig_len});
+            const auto signature = HSS_Signature::from_bytes_or_throw(sig);
             return m_public->verify_signature(message_to_verify, signature);
          } catch(const Decoding_Error&) {
             // Signature could not be decoded
@@ -166,11 +166,11 @@ class HSS_LMS_Signature_Operation final : public PK_Ops::Signature {
                                   std::shared_ptr<HSS_LMS_PublicKeyInternal> public_key) :
             m_private(std::move(private_key)), m_public(std::move(public_key)) {}
 
-      void update(const uint8_t msg[], size_t msg_len) override {
-         m_msg_buffer.insert(m_msg_buffer.end(), msg, msg + msg_len);
+      void update(std::span<const uint8_t> msg) override {
+         m_msg_buffer.insert(m_msg_buffer.end(), msg.begin(), msg.end());
       }
 
-      secure_vector<uint8_t> sign(RandomNumberGenerator&) override {
+      std::vector<uint8_t> sign(RandomNumberGenerator&) override {
          std::vector<uint8_t> message_to_sign = std::exchange(m_msg_buffer, {});
          return m_private->sign(message_to_sign);
       }
diff --git a/src/lib/pubkey/pk_ops.cpp b/src/lib/pubkey/pk_ops.cpp
index e18bf32ae30..bbf9bd1f8e1 100644
--- a/src/lib/pubkey/pk_ops.cpp
+++ b/src/lib/pubkey/pk_ops.cpp
@@ -31,28 +31,24 @@ size_t PK_Ops::Encryption_with_EME::max_input_bits() const {
    return 8 * m_eme->maximum_input_size(max_ptext_input_bits());
 }
 
-secure_vector<uint8_t> PK_Ops::Encryption_with_EME::encrypt(const uint8_t msg[],
-                                                            size_t msg_len,
-                                                            RandomNumberGenerator& rng) {
+std::vector<uint8_t> PK_Ops::Encryption_with_EME::encrypt(std::span<const uint8_t> msg, RandomNumberGenerator& rng) {
    const size_t max_raw = max_ptext_input_bits();
    secure_vector<uint8_t> eme_output((max_raw + 7) / 8);
-   size_t written = m_eme->pad(eme_output, std::span{msg, msg_len}, max_raw, rng);
-   return raw_encrypt(eme_output.data(), written, rng);
+   size_t written = m_eme->pad(eme_output, msg, max_raw, rng);
+   return raw_encrypt(std::span{eme_output.data(), written}, rng);
 }
 
 PK_Ops::Decryption_with_EME::Decryption_with_EME(std::string_view eme) : m_eme(EME::create(eme)) {}
 
-secure_vector<uint8_t> PK_Ops::Decryption_with_EME::decrypt(uint8_t& valid_mask,
-                                                            const uint8_t ciphertext[],
-                                                            size_t ciphertext_len) {
-   const secure_vector<uint8_t> raw = raw_decrypt(ciphertext, ciphertext_len);
+secure_vector<uint8_t> PK_Ops::Decryption_with_EME::decrypt(uint8_t& valid_mask, std::span<const uint8_t> ctext) {
+   const secure_vector<uint8_t> raw = raw_decrypt(ctext);
 
-   secure_vector<uint8_t> ctext(raw.size());
-   auto len = m_eme->unpad(ctext, raw);
+   secure_vector<uint8_t> ptext(raw.size());
+   auto len = m_eme->unpad(ptext, raw);
 
    valid_mask = CT::Mask<uint8_t>::from_choice(len.has_value()).if_set_return(0xFF);
-   ctext.resize(len.value_or(0));
-   return ctext;
+   ptext.resize(len.value_or(0));
+   return ptext;
 }
 
 PK_Ops::Key_Agreement_with_KDF::Key_Agreement_with_KDF(std::string_view kdf) {
@@ -61,15 +57,16 @@ PK_Ops::Key_Agreement_with_KDF::Key_Agreement_with_KDF(std::string_view kdf) {
    }
 }
 
-secure_vector<uint8_t> PK_Ops::Key_Agreement_with_KDF::agree(
-   size_t key_len, const uint8_t w[], size_t w_len, const uint8_t salt[], size_t salt_len) {
-   if(salt_len > 0 && m_kdf == nullptr) {
+secure_vector<uint8_t> PK_Ops::Key_Agreement_with_KDF::agree(size_t key_len,
+                                                             std::span<const uint8_t> other_key,
+                                                             std::span<const uint8_t> salt) {
+   if(!salt.empty() && m_kdf == nullptr) {
       throw Invalid_Argument("PK_Key_Agreement::derive_key requires a KDF to use a salt");
    }
 
-   secure_vector<uint8_t> z = raw_agree(w, w_len);
+   secure_vector<uint8_t> z = raw_agree(other_key.data(), other_key.size());
    if(m_kdf) {
-      return m_kdf->derive_key(key_len, z, salt, salt_len);
+      return m_kdf->derive_key(key_len, z, salt.data(), salt.size());
    }
    return z;
 }
@@ -121,13 +118,13 @@ std::string PK_Ops::Signature_with_Hash::rfc6979_hash_function() const {
 }
 #endif
 
-void PK_Ops::Signature_with_Hash::update(const uint8_t msg[], size_t msg_len) {
-   m_hash->update(msg, msg_len);
+void PK_Ops::Signature_with_Hash::update(std::span<const uint8_t> msg) {
+   m_hash->update(msg);
 }
 
-secure_vector<uint8_t> PK_Ops::Signature_with_Hash::sign(RandomNumberGenerator& rng) {
-   const secure_vector<uint8_t> msg = m_hash->final();
-   return raw_sign(msg.data(), msg.size(), rng);
+std::vector<uint8_t> PK_Ops::Signature_with_Hash::sign(RandomNumberGenerator& rng) {
+   const std::vector<uint8_t> msg = m_hash->final_stdvec();
+   return raw_sign(msg, rng);
 }
 
 PK_Ops::Verification_with_Hash::Verification_with_Hash(std::string_view padding) :
@@ -156,13 +153,13 @@ PK_Ops::Verification_with_Hash::Verification_with_Hash(const AlgorithmIdentifier
    m_hash = HashFunction::create_or_throw(oid_info[1]);
 }
 
-void PK_Ops::Verification_with_Hash::update(const uint8_t msg[], size_t msg_len) {
-   m_hash->update(msg, msg_len);
+void PK_Ops::Verification_with_Hash::update(std::span<const uint8_t> msg) {
+   m_hash->update(msg);
 }
 
-bool PK_Ops::Verification_with_Hash::is_valid_signature(const uint8_t sig[], size_t sig_len) {
-   const secure_vector<uint8_t> msg = m_hash->final();
-   return verify(msg.data(), msg.size(), sig, sig_len);
+bool PK_Ops::Verification_with_Hash::is_valid_signature(std::span<const uint8_t> sig) {
+   const std::vector<uint8_t> msg = m_hash->final_stdvec();
+   return verify(msg, sig);
 }
 
 size_t PK_Ops::KEM_Encryption_with_KDF::shared_key_length(size_t desired_shared_key_len) const {
diff --git a/src/lib/pubkey/pk_ops.h b/src/lib/pubkey/pk_ops.h
index d6a9f046d5e..330268cd465 100644
--- a/src/lib/pubkey/pk_ops.h
+++ b/src/lib/pubkey/pk_ops.h
@@ -25,6 +25,7 @@
 
 #include <botan/pk_keys.h>
 #include <botan/secmem.h>
+#include <span>
 
 namespace Botan {
 
@@ -33,17 +34,29 @@ class EME;
 class KDF;
 class EMSA;
 
-namespace PK_Ops {
+}  // namespace Botan
+
+namespace Botan::PK_Ops {
 
 /**
 * Public key encryption interface
 */
 class BOTAN_UNSTABLE_API Encryption {
    public:
-      virtual secure_vector<uint8_t> encrypt(const uint8_t msg[], size_t msg_len, RandomNumberGenerator& rng) = 0;
+      /**
+      * Encrypt a message returning the ciphertext
+      */
+      virtual std::vector<uint8_t> encrypt(std::span<const uint8_t> msg, RandomNumberGenerator& rng) = 0;
 
+      /**
+      * Return the maximum input size for this key
+      */
       virtual size_t max_input_bits() const = 0;
 
+      /**
+      * Given the plaintext length, return an upper bound of the ciphertext
+      * length for this key and padding.
+      */
       virtual size_t ciphertext_length(size_t ptext_len) const = 0;
 
       virtual ~Encryption() = default;
@@ -54,9 +67,7 @@ class BOTAN_UNSTABLE_API Encryption {
 */
 class BOTAN_UNSTABLE_API Decryption {
    public:
-      virtual secure_vector<uint8_t> decrypt(uint8_t& valid_mask,
-                                             const uint8_t ciphertext[],
-                                             size_t ciphertext_len) = 0;
+      virtual secure_vector<uint8_t> decrypt(uint8_t& valid_mask, std::span<const uint8_t> ctext) = 0;
 
       virtual size_t plaintext_length(size_t ctext_len) const = 0;
 
@@ -70,15 +81,15 @@ class BOTAN_UNSTABLE_API Verification {
    public:
       /**
       * Add more data to the message currently being signed
-      * @param msg the message
-      * @param msg_len the length of msg in bytes
+      * @param input the input to be hashed/verified
       */
-      virtual void update(const uint8_t msg[], size_t msg_len) = 0;
+      virtual void update(std::span<const uint8_t> input) = 0;
 
       /**
       * Perform a verification operation
+      * @param sig the signature to be checked with respect to the input
       */
-      virtual bool is_valid_signature(const uint8_t sig[], size_t sig_len) = 0;
+      virtual bool is_valid_signature(std::span<const uint8_t> sig) = 0;
 
       /**
       * Return the hash function being used by this signer
@@ -95,16 +106,15 @@ class BOTAN_UNSTABLE_API Signature {
    public:
       /**
       * Add more data to the message currently being signed
-      * @param msg the message
-      * @param msg_len the length of msg in bytes
+      * @param input the input to be hashed/signed
       */
-      virtual void update(const uint8_t msg[], size_t msg_len) = 0;
+      virtual void update(std::span<const uint8_t> input) = 0;
 
       /**
       * Perform a signature operation
       * @param rng a random number generator
       */
-      virtual secure_vector<uint8_t> sign(RandomNumberGenerator& rng) = 0;
+      virtual std::vector<uint8_t> sign(RandomNumberGenerator& rng) = 0;
 
       /**
       * Return an upper bound on the length of the output signature
@@ -131,8 +141,9 @@ class BOTAN_UNSTABLE_API Signature {
 */
 class BOTAN_UNSTABLE_API Key_Agreement {
    public:
-      virtual secure_vector<uint8_t> agree(
-         size_t key_len, const uint8_t other_key[], size_t other_key_len, const uint8_t salt[], size_t salt_len) = 0;
+      virtual secure_vector<uint8_t> agree(size_t key_len,
+                                           std::span<const uint8_t> other_key,
+                                           std::span<const uint8_t> salt) = 0;
 
       virtual size_t agreed_value_size() const = 0;
 
@@ -171,8 +182,6 @@ class BOTAN_UNSTABLE_API KEM_Decryption {
       virtual ~KEM_Decryption() = default;
 };
 
-}  // namespace PK_Ops
-
-}  // namespace Botan
+}  // namespace Botan::PK_Ops
 
 #endif
diff --git a/src/lib/pubkey/pk_ops_impl.h b/src/lib/pubkey/pk_ops_impl.h
index 990ec3e6a7f..f136245ecfc 100644
--- a/src/lib/pubkey/pk_ops_impl.h
+++ b/src/lib/pubkey/pk_ops_impl.h
@@ -19,7 +19,7 @@ class Encryption_with_EME : public Encryption {
    public:
       size_t max_input_bits() const override;
 
-      secure_vector<uint8_t> encrypt(const uint8_t msg[], size_t msg_len, RandomNumberGenerator& rng) override;
+      std::vector<uint8_t> encrypt(std::span<const uint8_t> ptext, RandomNumberGenerator& rng) override;
 
       ~Encryption_with_EME() override = default;
 
@@ -29,13 +29,13 @@ class Encryption_with_EME : public Encryption {
    private:
       virtual size_t max_ptext_input_bits() const = 0;
 
-      virtual secure_vector<uint8_t> raw_encrypt(const uint8_t msg[], size_t len, RandomNumberGenerator& rng) = 0;
+      virtual std::vector<uint8_t> raw_encrypt(std::span<const uint8_t> msg, RandomNumberGenerator& rng) = 0;
       std::unique_ptr<EME> m_eme;
 };
 
 class Decryption_with_EME : public Decryption {
    public:
-      secure_vector<uint8_t> decrypt(uint8_t& valid_mask, const uint8_t msg[], size_t msg_len) override;
+      secure_vector<uint8_t> decrypt(uint8_t& valid_mask, std::span<const uint8_t> ctext) override;
 
       ~Decryption_with_EME() override = default;
 
@@ -43,7 +43,7 @@ class Decryption_with_EME : public Decryption {
       explicit Decryption_with_EME(std::string_view eme);
 
    private:
-      virtual secure_vector<uint8_t> raw_decrypt(const uint8_t msg[], size_t len) = 0;
+      virtual secure_vector<uint8_t> raw_decrypt(std::span<const uint8_t> ctext) = 0;
       std::unique_ptr<EME> m_eme;
 };
 
@@ -51,8 +51,8 @@ class Verification_with_Hash : public Verification {
    public:
       ~Verification_with_Hash() override = default;
 
-      void update(const uint8_t msg[], size_t msg_len) override;
-      bool is_valid_signature(const uint8_t sig[], size_t sig_len) override;
+      void update(std::span<const uint8_t> input) override;
+      bool is_valid_signature(std::span<const uint8_t> sig) override;
 
       std::string hash_function() const final { return m_hash->name(); }
 
@@ -71,7 +71,7 @@ class Verification_with_Hash : public Verification {
       * @param sig_len the length of sig in bytes
       * @returns if signature is a valid one for message
       */
-      virtual bool verify(const uint8_t msg[], size_t msg_len, const uint8_t sig[], size_t sig_len) = 0;
+      virtual bool verify(std::span<const uint8_t> input, std::span<const uint8_t> sig) = 0;
 
    private:
       std::unique_ptr<HashFunction> m_hash;
@@ -79,9 +79,9 @@ class Verification_with_Hash : public Verification {
 
 class Signature_with_Hash : public Signature {
    public:
-      void update(const uint8_t msg[], size_t msg_len) override;
+      void update(std::span<const uint8_t> input) override;
 
-      secure_vector<uint8_t> sign(RandomNumberGenerator& rng) override;
+      std::vector<uint8_t> sign(RandomNumberGenerator& rng) override;
 
       ~Signature_with_Hash() override = default;
 
@@ -95,7 +95,7 @@ class Signature_with_Hash : public Signature {
 #endif
 
    private:
-      virtual secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len, RandomNumberGenerator& rng) = 0;
+      virtual std::vector<uint8_t> raw_sign(std::span<const uint8_t> input, RandomNumberGenerator& rng) = 0;
 
       std::unique_ptr<HashFunction> m_hash;
 };
@@ -103,10 +103,8 @@ class Signature_with_Hash : public Signature {
 class Key_Agreement_with_KDF : public Key_Agreement {
    public:
       secure_vector<uint8_t> agree(size_t key_len,
-                                   const uint8_t other_key[],
-                                   size_t other_key_len,
-                                   const uint8_t salt[],
-                                   size_t salt_len) override;
+                                   std::span<const uint8_t> other_key,
+                                   std::span<const uint8_t> salt) override;
 
       ~Key_Agreement_with_KDF() override = default;
 
diff --git a/src/lib/pubkey/pubkey.cpp b/src/lib/pubkey/pubkey.cpp
index 870d9a17619..15fb3632157 100644
--- a/src/lib/pubkey/pubkey.cpp
+++ b/src/lib/pubkey/pubkey.cpp
@@ -104,7 +104,7 @@ size_t PK_Encryptor_EME::ciphertext_length(size_t ptext_len) const {
 }
 
 std::vector<uint8_t> PK_Encryptor_EME::enc(const uint8_t in[], size_t length, RandomNumberGenerator& rng) const {
-   return unlock(m_op->encrypt(in, length, rng));
+   return m_op->encrypt(std::span{in, length}, rng);
 }
 
 size_t PK_Encryptor_EME::maximum_input_size() const {
@@ -131,7 +131,7 @@ size_t PK_Decryptor_EME::plaintext_length(size_t ctext_len) const {
 }
 
 secure_vector<uint8_t> PK_Decryptor_EME::do_decrypt(uint8_t& valid_mask, const uint8_t in[], size_t in_len) const {
-   return m_op->decrypt(valid_mask, in, in_len);
+   return m_op->decrypt(valid_mask, {in, in_len});
 }
 
 PK_KEM_Encryptor::PK_KEM_Encryptor(const Public_Key& key, std::string_view param, std::string_view provider) {
@@ -231,7 +231,7 @@ SymmetricKey PK_Key_Agreement::derive_key(size_t key_len,
 
 SymmetricKey PK_Key_Agreement::derive_key(
    size_t key_len, const uint8_t in[], size_t in_len, const uint8_t salt[], size_t salt_len) const {
-   return SymmetricKey(m_op->agree(key_len, in, in_len, salt, salt_len));
+   return SymmetricKey(m_op->agree(key_len, {in, in_len}, {salt, salt_len}));
 }
 
 namespace {
@@ -277,7 +277,7 @@ void PK_Signer::update(std::string_view in) {
 }
 
 void PK_Signer::update(const uint8_t in[], size_t length) {
-   m_op->update(in, length);
+   m_op->update({in, length});
 }
 
 namespace {
@@ -314,7 +314,7 @@ size_t PK_Signer::signature_length() const {
 }
 
 std::vector<uint8_t> PK_Signer::signature(RandomNumberGenerator& rng) {
-   std::vector<uint8_t> sig = unlock(m_op->sign(rng));
+   std::vector<uint8_t> sig = m_op->sign(rng);
 
    if(m_sig_format == Signature_Format::Standard) {
       return sig;
@@ -378,7 +378,7 @@ void PK_Verifier::update(std::string_view in) {
 }
 
 void PK_Verifier::update(const uint8_t in[], size_t length) {
-   m_op->update(in, length);
+   m_op->update({in, length});
 }
 
 namespace {
@@ -416,7 +416,7 @@ std::vector<uint8_t> decode_der_signature(const uint8_t sig[], size_t length, si
 bool PK_Verifier::check_signature(const uint8_t sig[], size_t length) {
    try {
       if(m_sig_format == Signature_Format::Standard) {
-         return m_op->is_valid_signature(sig, length);
+         return m_op->is_valid_signature({sig, length});
       } else if(m_sig_format == Signature_Format::DerSequence) {
          bool decoding_success = false;
          std::vector<uint8_t> real_sig;
@@ -426,7 +426,7 @@ bool PK_Verifier::check_signature(const uint8_t sig[], size_t length) {
             decoding_success = true;
          } catch(Decoding_Error&) {}
 
-         bool accept = m_op->is_valid_signature(real_sig.data(), real_sig.size());
+         bool accept = m_op->is_valid_signature(real_sig);
 
          return accept && decoding_success;
       } else {
diff --git a/src/lib/pubkey/rsa/rsa.cpp b/src/lib/pubkey/rsa/rsa.cpp
index 3da86cd67a7..8ae7e444d66 100644
--- a/src/lib/pubkey/rsa/rsa.cpp
+++ b/src/lib/pubkey/rsa/rsa.cpp
@@ -470,12 +470,6 @@ class RSA_Private_Operation {
          recovered.serialize_to(out);
       }
 
-      secure_vector<uint8_t> raw_op(const uint8_t input[], size_t input_len) {
-         secure_vector<uint8_t> out(m_public->public_modulus_bytes());
-         raw_op(out, {input, input_len});
-         return out;
-      }
-
    private:
       BigInt rsa_private_op(const BigInt& m) const {
          /*
@@ -552,13 +546,16 @@ class RSA_Private_Operation {
 class RSA_Signature_Operation final : public PK_Ops::Signature,
                                       private RSA_Private_Operation {
    public:
-      void update(const uint8_t msg[], size_t msg_len) override { m_emsa->update(msg, msg_len); }
+      void update(std::span<const uint8_t> msg) override { m_emsa->update(msg.data(), msg.size()); }
 
-      secure_vector<uint8_t> sign(RandomNumberGenerator& rng) override {
+      std::vector<uint8_t> sign(RandomNumberGenerator& rng) override {
          const size_t max_input_bits = public_modulus_bits() - 1;
          const auto msg = m_emsa->raw_data();
          const auto padded = m_emsa->encoding_of(msg, max_input_bits, rng);
-         return raw_op(padded.data(), padded.size());
+
+         std::vector<uint8_t> out(public_modulus_bytes());
+         raw_op(out, padded);
+         return out;
       }
 
       size_t signature_length() const override { return public_modulus_bytes(); }
@@ -599,8 +596,10 @@ class RSA_Decryption_Operation final : public PK_Ops::Decryption_with_EME,
 
       size_t plaintext_length(size_t /*ctext_len*/) const override { return public_modulus_bytes(); }
 
-      secure_vector<uint8_t> raw_decrypt(const uint8_t input[], size_t input_len) override {
-         return raw_op(input, input_len);
+      secure_vector<uint8_t> raw_decrypt(std::span<const uint8_t> input) override {
+         secure_vector<uint8_t> out(public_modulus_bytes());
+         raw_op(out, input);
+         return out;
       }
 };
 
@@ -655,22 +654,20 @@ class RSA_Encryption_Operation final : public PK_Ops::Encryption_with_EME,
 
       size_t max_ptext_input_bits() const override { return public_modulus_bits() - 1; }
 
-      secure_vector<uint8_t> raw_encrypt(const uint8_t input[],
-                                         size_t input_len,
-                                         RandomNumberGenerator& /*rng*/) override {
-         BigInt input_bn(input, input_len);
-         return public_op(input_bn).serialize<secure_vector<uint8_t>>(public_modulus_bytes());
+      std::vector<uint8_t> raw_encrypt(std::span<const uint8_t> input, RandomNumberGenerator& /*rng*/) override {
+         BigInt input_bn(input);
+         return public_op(input_bn).serialize(public_modulus_bytes());
       }
 };
 
 class RSA_Verify_Operation final : public PK_Ops::Verification,
                                    private RSA_Public_Operation {
    public:
-      void update(const uint8_t msg[], size_t msg_len) override { m_emsa->update(msg, msg_len); }
+      void update(std::span<const uint8_t> msg) override { m_emsa->update(msg.data(), msg.size()); }
 
-      bool is_valid_signature(const uint8_t sig[], size_t sig_len) override {
+      bool is_valid_signature(std::span<const uint8_t> sig) override {
          const auto msg = m_emsa->raw_data();
-         const auto message_repr = recover_message_repr(sig, sig_len);
+         const auto message_repr = recover_message_repr(sig.data(), sig.size());
          return m_emsa->verify(message_repr, msg, public_modulus_bits() - 1);
       }
 
diff --git a/src/lib/pubkey/sm2/sm2.cpp b/src/lib/pubkey/sm2/sm2.cpp
index 7f06d23aa26..7ecabdde913 100644
--- a/src/lib/pubkey/sm2/sm2.cpp
+++ b/src/lib/pubkey/sm2/sm2.cpp
@@ -102,15 +102,15 @@ class SM2_Signature_Operation final : public PK_Ops::Signature {
 
       size_t signature_length() const override { return 2 * m_group.get_order_bytes(); }
 
-      void update(const uint8_t msg[], size_t msg_len) override {
+      void update(std::span<const uint8_t> input) override {
          if(m_hash) {
-            m_hash->update(msg, msg_len);
+            m_hash->update(input);
          } else {
-            m_digest.insert(m_digest.end(), msg, msg + msg_len);
+            m_digest.insert(m_digest.end(), input.begin(), input.end());
          }
       }
 
-      secure_vector<uint8_t> sign(RandomNumberGenerator& rng) override;
+      std::vector<uint8_t> sign(RandomNumberGenerator& rng) override;
 
       std::string hash_function() const override { return m_hash ? m_hash->name() : "Raw"; }
 
@@ -125,7 +125,7 @@ class SM2_Signature_Operation final : public PK_Ops::Signature {
       std::vector<BigInt> m_ws;
 };
 
-secure_vector<uint8_t> SM2_Signature_Operation::sign(RandomNumberGenerator& rng) {
+std::vector<uint8_t> SM2_Signature_Operation::sign(RandomNumberGenerator& rng) {
    const auto e = [&]() {
       if(m_hash) {
          auto ie = EC_Scalar::from_bytes_mod_order(m_group, m_hash->final());
@@ -144,7 +144,7 @@ secure_vector<uint8_t> SM2_Signature_Operation::sign(RandomNumberGenerator& rng)
    const auto r = EC_Scalar::gk_x_mod_order(k, rng, m_ws) + e;
    const auto s = (k - r * m_x) * m_da_inv;
 
-   return EC_Scalar::serialize_pair<secure_vector<uint8_t>>(r, s);
+   return EC_Scalar::serialize_pair(r, s);
 }
 
 /**
@@ -164,15 +164,15 @@ class SM2_Verification_Operation final : public PK_Ops::Verification {
          }
       }
 
-      void update(const uint8_t msg[], size_t msg_len) override {
+      void update(std::span<const uint8_t> input) override {
          if(m_hash) {
-            m_hash->update(msg, msg_len);
+            m_hash->update(input);
          } else {
-            m_digest.insert(m_digest.end(), msg, msg + msg_len);
+            m_digest.insert(m_digest.end(), input.begin(), input.end());
          }
       }
 
-      bool is_valid_signature(const uint8_t sig[], size_t sig_len) override;
+      bool is_valid_signature(std::span<const uint8_t> sig) override;
 
       std::string hash_function() const override { return m_hash ? m_hash->name() : "Raw"; }
 
@@ -184,7 +184,7 @@ class SM2_Verification_Operation final : public PK_Ops::Verification {
       std::unique_ptr<HashFunction> m_hash;
 };
 
-bool SM2_Verification_Operation::is_valid_signature(const uint8_t sig[], size_t sig_len) {
+bool SM2_Verification_Operation::is_valid_signature(std::span<const uint8_t> sig) {
    const auto e = [&]() {
       if(m_hash) {
          auto ie = EC_Scalar::from_bytes_mod_order(m_group, m_hash->final());
@@ -198,7 +198,7 @@ bool SM2_Verification_Operation::is_valid_signature(const uint8_t sig[], size_t
       }
    }();
 
-   if(auto rs = EC_Scalar::deserialize_pair(m_group, std::span{sig, sig_len})) {
+   if(auto rs = EC_Scalar::deserialize_pair(m_group, sig)) {
       const auto& [r, s] = rs.value();
 
       if(r.is_nonzero() && s.is_nonzero()) {
diff --git a/src/lib/pubkey/sm2/sm2_enc.cpp b/src/lib/pubkey/sm2/sm2_enc.cpp
index c54601333af..c9b243cdf41 100644
--- a/src/lib/pubkey/sm2/sm2_enc.cpp
+++ b/src/lib/pubkey/sm2/sm2_enc.cpp
@@ -39,7 +39,7 @@ class SM2_Encryption_Operation final : public PK_Ops::Encryption {
          return der_overhead + 2 * elem_size + m_hash->output_length() + ptext_len;
       }
 
-      secure_vector<uint8_t> encrypt(const uint8_t msg[], size_t msg_len, RandomNumberGenerator& rng) override {
+      std::vector<uint8_t> encrypt(std::span<const uint8_t> msg, RandomNumberGenerator& rng) override {
          const auto k = EC_Scalar::random(m_group, rng);
 
          const EC_AffinePoint C1 = EC_AffinePoint::g_mul(k, rng, m_ws);
@@ -53,24 +53,26 @@ class SM2_Encryption_Operation final : public PK_Ops::Encryption {
          kdf_input += x2_bytes;
          kdf_input += y2_bytes;
 
-         const secure_vector<uint8_t> kdf_output = m_kdf->derive_key(msg_len, kdf_input.data(), kdf_input.size());
+         const secure_vector<uint8_t> kdf_output = m_kdf->derive_key(msg.size(), kdf_input.data(), kdf_input.size());
 
-         std::vector<uint8_t> masked_msg(msg_len);
-         xor_buf(masked_msg.data(), msg, kdf_output.data(), msg_len);
+         std::vector<uint8_t> masked_msg(msg.size());
+         xor_buf(masked_msg.data(), msg.data(), kdf_output.data(), msg.size());
 
          m_hash->update(x2_bytes);
-         m_hash->update(msg, msg_len);
+         m_hash->update(msg);
          m_hash->update(y2_bytes);
          const auto C3 = m_hash->final<std::vector<uint8_t>>();
 
-         return DER_Encoder()
+         std::vector<uint8_t> ctext;
+         DER_Encoder(ctext)
             .start_sequence()
             .encode(BigInt(C1.x_bytes()))
             .encode(BigInt(C1.y_bytes()))
             .encode(C3, ASN1_Type::OctetString)
             .encode(masked_msg, ASN1_Type::OctetString)
-            .end_cons()
-            .get_contents();
+            .end_cons();
+
+         return ctext;
       }
 
    private:
@@ -107,21 +109,21 @@ class SM2_Decryption_Operation final : public PK_Ops::Decryption {
          return ptext_len - (2 * elem_size + m_hash->output_length());
       }
 
-      secure_vector<uint8_t> decrypt(uint8_t& valid_mask, const uint8_t ciphertext[], size_t ciphertext_len) override {
+      secure_vector<uint8_t> decrypt(uint8_t& valid_mask, std::span<const uint8_t> ctext) override {
          const BigInt& cofactor = m_group.get_cofactor();
          const size_t p_bytes = m_group.get_p_bytes();
 
          valid_mask = 0x00;
 
          // Too short to be valid - no timing problem from early return
-         if(ciphertext_len < 1 + p_bytes * 2 + m_hash->output_length()) {
+         if(ctext.size() < 1 + p_bytes * 2 + m_hash->output_length()) {
             return secure_vector<uint8_t>();
          }
 
          BigInt x1, y1;
          secure_vector<uint8_t> C3, masked_msg;
 
-         BER_Decoder(ciphertext, ciphertext_len)
+         BER_Decoder(ctext)
             .start_sequence()
             .decode(x1)
             .decode(y1)
@@ -139,11 +141,11 @@ class SM2_Decryption_Operation final : public PK_Ops::Decryption {
             .encode(masked_msg, ASN1_Type::OctetString)
             .end_cons();
 
-         if(recode_ctext.size() != ciphertext_len) {
+         if(recode_ctext.size() != ctext.size()) {
             return secure_vector<uint8_t>();
          }
 
-         if(CT::is_equal(recode_ctext.data(), ciphertext, ciphertext_len).as_bool() == false) {
+         if(CT::is_equal(recode_ctext.data(), ctext.data(), ctext.size()).as_bool() == false) {
             return secure_vector<uint8_t>();
          }
 
diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp
index af8b7353f0d..fe8dc57d065 100644
--- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp
+++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp
@@ -145,24 +145,22 @@ class SphincsPlus_Verification_Operation final : public PK_Ops::Verification {
       /**
        * Add more data to the message currently being signed
        * @param msg the message
-       * @param msg_len the length of msg in bytes
        */
-      void update(const uint8_t msg[], size_t msg_len) override {
-         m_msg_buffer.insert(m_msg_buffer.end(), msg, msg + msg_len);
+      void update(std::span<const uint8_t> msg) override {
+         m_msg_buffer.insert(m_msg_buffer.end(), msg.begin(), msg.end());
       }
 
       /*
       * Perform a verification operation
-      * @param rng a random number generator
       */
-      bool is_valid_signature(const uint8_t* sig, size_t sig_len) override {
+      bool is_valid_signature(std::span<const uint8_t> sig) override {
          const auto& p = m_public->parameters();
-         if(sig_len != p.sphincs_signature_bytes()) {
+         if(sig.size() != p.sphincs_signature_bytes()) {
             m_msg_buffer.clear();
             return false;
          }
 
-         BufferSlicer s({sig, sig_len});
+         BufferSlicer s(sig);
          // Compute leaf and tree index from R
          const auto msg_random_s = s.take<SphincsMessageRandomness>(p.n());
          auto [mhash, tree_idx, leaf_idx] = m_hashes->H_msg(msg_random_s, m_public->root(), m_msg_buffer);
@@ -288,14 +286,14 @@ class SphincsPlus_Signature_Operation final : public PK_Ops::Signature {
             m_hashes(Botan::Sphincs_Hash_Functions::create(m_public->parameters(), m_public->seed())),
             m_randomized(randomized) {}
 
-      void update(const uint8_t msg[], size_t msg_len) override {
-         m_msg_buffer.insert(m_msg_buffer.end(), msg, msg + msg_len);
+      void update(std::span<const uint8_t> msg) override {
+         m_msg_buffer.insert(m_msg_buffer.end(), msg.begin(), msg.end());
       }
 
-      secure_vector<uint8_t> sign(RandomNumberGenerator& rng) override {
+      std::vector<uint8_t> sign(RandomNumberGenerator& rng) override {
          const auto& p = m_public->parameters();
 
-         secure_vector<uint8_t> sphincs_sig_buffer(p.sphincs_signature_bytes());
+         std::vector<uint8_t> sphincs_sig_buffer(p.sphincs_signature_bytes());
          BufferStuffer sphincs_sig(sphincs_sig_buffer);
 
          // Compute and append the digest randomization value (R of spec).
diff --git a/src/lib/pubkey/xmss/xmss_signature.cpp b/src/lib/pubkey/xmss/xmss_signature.cpp
index 98f72a1c7bb..c78ecb358be 100644
--- a/src/lib/pubkey/xmss/xmss_signature.cpp
+++ b/src/lib/pubkey/xmss/xmss_signature.cpp
@@ -10,7 +10,7 @@
 
 namespace Botan {
 
-XMSS_Signature::XMSS_Signature(XMSS_Parameters::xmss_algorithm_t oid, const secure_vector<uint8_t>& raw_sig) :
+XMSS_Signature::XMSS_Signature(XMSS_Parameters::xmss_algorithm_t oid, std::span<const uint8_t> raw_sig) :
       m_leaf_idx(0), m_randomness(0, 0x00) {
    XMSS_Parameters xmss_params(oid);
 
@@ -48,11 +48,11 @@ XMSS_Signature::XMSS_Signature(XMSS_Parameters::xmss_algorithm_t oid, const secu
    }
 }
 
-secure_vector<uint8_t> XMSS_Signature::bytes() const {
-   secure_vector<uint8_t> result{static_cast<uint8_t>(m_leaf_idx >> 24U),
-                                 static_cast<uint8_t>(m_leaf_idx >> 16U),
-                                 static_cast<uint8_t>(m_leaf_idx >> 8U),
-                                 static_cast<uint8_t>(m_leaf_idx)};
+std::vector<uint8_t> XMSS_Signature::bytes() const {
+   std::vector<uint8_t> result{static_cast<uint8_t>(m_leaf_idx >> 24U),
+                               static_cast<uint8_t>(m_leaf_idx >> 16U),
+                               static_cast<uint8_t>(m_leaf_idx >> 8U),
+                               static_cast<uint8_t>(m_leaf_idx)};
 
    std::copy(m_randomness.begin(), m_randomness.end(), std::back_inserter(result));
 
diff --git a/src/lib/pubkey/xmss/xmss_signature.h b/src/lib/pubkey/xmss/xmss_signature.h
index e2330381f2c..ae23b466b99 100644
--- a/src/lib/pubkey/xmss/xmss_signature.h
+++ b/src/lib/pubkey/xmss/xmss_signature.h
@@ -37,7 +37,7 @@ class XMSS_Signature final {
        * @param raw_sig An XMSS signature serialized using
        *                XMSS_Signature::bytes().
        **/
-      XMSS_Signature(XMSS_Parameters::xmss_algorithm_t oid, const secure_vector<uint8_t>& raw_sig);
+      XMSS_Signature(XMSS_Parameters::xmss_algorithm_t oid, std::span<const uint8_t> raw_sig);
 
       /**
        * Creates an XMSS Signature from a leaf index used for signature
@@ -79,7 +79,7 @@ class XMSS_Signature final {
        * @return serialized signature, a sequence of
        *         4+(len + h + 1)n bytes.
        **/
-      secure_vector<uint8_t> bytes() const;
+      std::vector<uint8_t> bytes() const;
 
    private:
       size_t m_leaf_idx;
diff --git a/src/lib/pubkey/xmss/xmss_signature_operation.cpp b/src/lib/pubkey/xmss/xmss_signature_operation.cpp
index abbba90fa25..ed5df428528 100644
--- a/src/lib/pubkey/xmss/xmss_signature_operation.cpp
+++ b/src/lib/pubkey/xmss/xmss_signature_operation.cpp
@@ -66,16 +66,16 @@ wots_keysig_t XMSS_Signature_Operation::build_auth_path(XMSS_PrivateKey& priv_ke
    return auth_path;
 }
 
-void XMSS_Signature_Operation::update(const uint8_t msg[], size_t msg_len) {
+void XMSS_Signature_Operation::update(std::span<const uint8_t> input) {
    initialize();
-   m_hash.h_msg_update({msg, msg_len});
+   m_hash.h_msg_update(input);
 }
 
-secure_vector<uint8_t> XMSS_Signature_Operation::sign(RandomNumberGenerator& /*rng*/) {
+std::vector<uint8_t> XMSS_Signature_Operation::sign(RandomNumberGenerator& /*rng*/) {
    initialize();
-   secure_vector<uint8_t> signature(sign(m_hash.h_msg_final(), m_priv_key).bytes());
+   auto sig = sign(m_hash.h_msg_final(), m_priv_key).bytes();
    m_is_initialized = false;
-   return signature;
+   return sig;
 }
 
 void XMSS_Signature_Operation::initialize() {
diff --git a/src/lib/pubkey/xmss/xmss_signature_operation.h b/src/lib/pubkey/xmss/xmss_signature_operation.h
index a3fa001902b..bb5336f100a 100644
--- a/src/lib/pubkey/xmss/xmss_signature_operation.h
+++ b/src/lib/pubkey/xmss/xmss_signature_operation.h
@@ -35,9 +35,9 @@ class XMSS_Signature_Operation final : public virtual PK_Ops::Signature {
        *
        * @return serialized XMSS signature.
        **/
-      secure_vector<uint8_t> sign(RandomNumberGenerator&) override;
+      std::vector<uint8_t> sign(RandomNumberGenerator&) override;
 
-      void update(const uint8_t msg[], size_t msg_len) override;
+      void update(std::span<const uint8_t> input) override;
 
       size_t signature_length() const override;
 
diff --git a/src/lib/pubkey/xmss/xmss_verification_operation.cpp b/src/lib/pubkey/xmss/xmss_verification_operation.cpp
index f92dc0ee05a..89eafc9ae41 100644
--- a/src/lib/pubkey/xmss/xmss_verification_operation.cpp
+++ b/src/lib/pubkey/xmss/xmss_verification_operation.cpp
@@ -76,13 +76,13 @@ bool XMSS_Verification_Operation::verify(const XMSS_Signature& sig,
 // impossible.
 // Possible solution: Change PK_Ops::Verification interface to take the
 // signature as constructor argument, make sign a parameterless member call.
-void XMSS_Verification_Operation::update(const uint8_t msg[], size_t msg_len) {
-   std::copy(msg, msg + msg_len, std::back_inserter(m_msg_buf));
+void XMSS_Verification_Operation::update(std::span<const uint8_t> input) {
+   m_msg_buf.insert(m_msg_buf.end(), input.begin(), input.end());
 }
 
-bool XMSS_Verification_Operation::is_valid_signature(const uint8_t sig[], size_t sig_len) {
+bool XMSS_Verification_Operation::is_valid_signature(std::span<const uint8_t> sig) {
    try {
-      XMSS_Signature signature(m_pub_key.xmss_parameters().oid(), secure_vector<uint8_t>(sig, sig + sig_len));
+      XMSS_Signature signature(m_pub_key.xmss_parameters().oid(), sig);
       bool result = verify(signature, m_msg_buf, m_pub_key);
       m_msg_buf.clear();
       return result;
diff --git a/src/lib/pubkey/xmss/xmss_verification_operation.h b/src/lib/pubkey/xmss/xmss_verification_operation.h
index db5e7d266dc..f4eb65b5acd 100644
--- a/src/lib/pubkey/xmss/xmss_verification_operation.h
+++ b/src/lib/pubkey/xmss/xmss_verification_operation.h
@@ -22,9 +22,9 @@ class XMSS_Verification_Operation final : public virtual PK_Ops::Verification {
    public:
       XMSS_Verification_Operation(const XMSS_PublicKey& public_key);
 
-      bool is_valid_signature(const uint8_t sig[], size_t sig_len) override;
+      bool is_valid_signature(std::span<const uint8_t> sign) override;
 
-      void update(const uint8_t msg[], size_t msg_len) override;
+      void update(std::span<const uint8_t> input) override;
 
       std::string hash_function() const override { return m_hash.hash_function(); }