Repository: randombit/botan
Pull request #3276 (2023-02-13 14:32:28)

Remove Accessors to XMSS internals
### Pull Request Dependencies

* #3263

### Description

This removes (or at least hides) accessors to XMSS algorithm internals from the public interface. Also, it deprecates `XMSS_PrivateKey::unused_leaf_index()` in favor of a new and more user-friendly `::remaining_signatures()` method.

Finally, it adds a test for `remaining_signatures()` to ensure that no more signatures can be generated once the WOTS private keys in the XMSS tree are exhausted.


Patch:
diff --git a/doc/support.rst b/doc/support.rst
index 63fa47ed3..e04223eaf 100644
--- a/doc/support.rst
+++ b/doc/support.rst
@@ -47,7 +47,7 @@ Branch Support Status
 -------------------------
 
 Following table provides the support status for Botan branches as of
-February 2023. Any branch not listed here is no longer supported.
+December 2022. Any branch not listed here is no longer supported.
 Dates in the future are approximate.
 
 ============== ============== ========================== ============
@@ -55,7 +55,7 @@ Branch         First Release  End of Active Development  End of Life
 ============== ============== ========================== ============
 1.8            2008-12-08     2010-08-31                 2016-02-13
 1.10           2011-06-20     2012-07-10                 2018-12-31
-2.x            2017-01-06     2020-11-05                 2025-01-01 or later
+2.x            2017-01-06     2020-11-05                 2024-01-01 or later
 3.x            2023?          ?                          ?
 ============== ============== ========================== ============
 
diff --git a/src/cli/pk_crypt.cpp b/src/cli/pk_crypt.cpp
index 24a9f9a5a..2cfcfb7af 100644
--- a/src/cli/pk_crypt.cpp
+++ b/src/cli/pk_crypt.cpp
@@ -166,24 +166,24 @@ class PK_Decrypt final : public Command
             return set_return_code(1);
             }
 
-         if(pk_alg_id.oid() != Botan::OID::from_string("RSA/OAEP"))
+         if(pk_alg_id.get_oid() != Botan::OID::from_string("RSA/OAEP"))
             {
             error_output() << "Ciphertext was encrypted with something other than RSA/OAEP";
             return set_return_code(1);
             }
 
          Botan::AlgorithmIdentifier oaep_hash_id;
-         Botan::BER_Decoder(pk_alg_id.parameters()).decode(oaep_hash_id);
+         Botan::BER_Decoder(pk_alg_id.get_parameters()).decode(oaep_hash_id);
 
-         const std::string oaep_hash = Botan::OIDS::oid2str_or_empty(oaep_hash_id.oid());
+         const std::string oaep_hash = Botan::OIDS::oid2str_or_empty(oaep_hash_id.get_oid());
 
          if(oaep_hash.empty())
             {
-            error_output() << "Unknown hash function used with OAEP, OID " << oaep_hash_id.oid().to_string() << "\n";
+            error_output() << "Unknown hash function used with OAEP, OID " << oaep_hash_id.get_oid().to_string() << "\n";
             return set_return_code(1);
             }
 
-         if(oaep_hash_id.parameters().empty() == false)
+         if(oaep_hash_id.get_parameters().empty() == false)
             {
             error_output() << "Unknown OAEP parameters used\n";
             return set_return_code(1);
diff --git a/src/examples/tls_custom_curves_server.cpp b/src/examples/tls_custom_curves_server.cpp
index 4484d03e5..e233d9d63 100644
--- a/src/examples/tls_custom_curves_server.cpp
+++ b/src/examples/tls_custom_curves_server.cpp
@@ -78,23 +78,13 @@ public:
     return {};
   }
 
-  std::vector<Botan::X509_Certificate>
-  cert_chain(const std::vector<std::string> &cert_key_types,
-             const std::vector<Botan::AlgorithmIdentifier> &cert_signature_schemes,
-             const std::string &type, const std::string &context) override {
-    Botan::X509_Certificate server_cert("botan.randombit.net.crt");
-
-    // make sure that the server asked for your certificate's key type
-    // before handing it out to the implementation
-    const auto key_type = server_cert.subject_public_key_algo().oid().to_formatted_string();
-    const auto itr = std::find(cert_key_types.begin(), cert_key_types.end(), key_type);
-    if (itr == cert_key_types.end()) {
-      return {};
-    }
-
-    // return the certificate chain being sent to the tls server
+  std::vector<Botan::X509_Certificate> cert_chain(const std::vector<std::string> &cert_key_types,
+                                                  const std::vector<Botan::AlgorithmIdentifier> &cert_signature_schemes,
+                                                  const std::string &type,
+                                                  const std::string &context) override {
+    // return the certificate chain being sent to the tls client
     // e.g., the certificate file "botan.randombit.net.crt"
-    return {server_cert};
+    return {Botan::X509_Certificate("botan.randombit.net.crt")};
   }
 
   Botan::Private_Key *private_key_for(const Botan::X509_Certificate &cert, const std::string &type,
diff --git a/src/lib/asn1/alg_id.cpp b/src/lib/asn1/alg_id.cpp
index 820159e8f..2f0848132 100644
--- a/src/lib/asn1/alg_id.cpp
+++ b/src/lib/asn1/alg_id.cpp
@@ -64,7 +64,7 @@ bool AlgorithmIdentifier::parameters_are_null() const
 
 bool operator==(const AlgorithmIdentifier& a1, const AlgorithmIdentifier& a2)
    {
-   if(a1.oid() != a2.oid())
+   if(a1.get_oid() != a2.get_oid())
       return false;
 
    /*
diff --git a/src/lib/asn1/asn1_obj.h b/src/lib/asn1/asn1_obj.h
index c03f41c72..622028799 100644
--- a/src/lib/asn1/asn1_obj.h
+++ b/src/lib/asn1/asn1_obj.h
@@ -444,10 +444,7 @@ class BOTAN_PUBLIC_API(2,0) AlgorithmIdentifier final : public ASN1_Object
       const OID& oid() const { return m_oid; }
       const std::vector<uint8_t>& parameters() const { return m_parameters; }
 
-      BOTAN_DEPRECATED("Use AlgorithmIdentifier::oid")
       const OID& get_oid() const { return m_oid; }
-
-      BOTAN_DEPRECATED("Use AlgorithmIdentifier::parameters")
       const std::vector<uint8_t>& get_parameters() const { return m_parameters; }
 
       bool parameters_are_null() const;
diff --git a/src/lib/hash/blake2/blake2b.h b/src/lib/hash/blake2/blake2b.h
index ec3a24e23..49e19ca4f 100644
--- a/src/lib/hash/blake2/blake2b.h
+++ b/src/lib/hash/blake2/blake2b.h
@@ -65,6 +65,8 @@ class BLAKE2b final : public HashFunction, public SymmetricAlgorithm
       secure_vector<uint8_t> m_padded_key_buffer;
    };
 
+typedef BLAKE2b Blake2b;
+
 }
 
 #endif
diff --git a/src/lib/hash/hash.cpp b/src/lib/hash/hash.cpp
index be4eff3fa..fe0ca741c 100644
--- a/src/lib/hash/hash.cpp
+++ b/src/lib/hash/hash.cpp
@@ -217,7 +217,7 @@ std::unique_ptr<HashFunction> HashFunction::create(const std::string& algo_spec,
 #if defined(BOTAN_HAS_BLAKE2B)
    if(req.algo_name() == "Blake2b" || req.algo_name() == "BLAKE2b")
       {
-      return std::make_unique<BLAKE2b>(req.arg_as_integer(0, 512));
+      return std::make_unique<Blake2b>(req.arg_as_integer(0, 512));
    }
 #endif
 
diff --git a/src/lib/mac/blake2mac/blake2bmac.h b/src/lib/mac/blake2mac/blake2bmac.h
index 179f95ef4..1fb403899 100644
--- a/src/lib/mac/blake2mac/blake2bmac.h
+++ b/src/lib/mac/blake2mac/blake2bmac.h
@@ -57,6 +57,8 @@ class BLAKE2bMAC final : public MessageAuthenticationCode
       BLAKE2b m_blake;
    };
 
+typedef BLAKE2bMAC Blake2bMac;
+
 }
 
 #endif
diff --git a/src/lib/prov/pkcs11/p11_ecc_key.cpp b/src/lib/prov/pkcs11/p11_ecc_key.cpp
index a0376b070..f60e2f4d7 100644
--- a/src/lib/prov/pkcs11/p11_ecc_key.cpp
+++ b/src/lib/prov/pkcs11/p11_ecc_key.cpp
@@ -126,7 +126,7 @@ bool PKCS11_EC_PrivateKey::check_key(RandomNumberGenerator& /*rng*/, bool /*stro
 
 AlgorithmIdentifier PKCS11_EC_PrivateKey::algorithm_identifier() const
    {
-   return AlgorithmIdentifier(object_identifier(), domain().DER_encode(EC_Group_Encoding::Explicit));
+   return AlgorithmIdentifier(get_oid(), domain().DER_encode(EC_Group_Encoding::Explicit));
    }
 }
 
diff --git a/src/lib/prov/tpm/tpm.cpp b/src/lib/prov/tpm/tpm.cpp
index 2c73a3755..739c8a8ba 100644
--- a/src/lib/prov/tpm/tpm.cpp
+++ b/src/lib/prov/tpm/tpm.cpp
@@ -355,7 +355,7 @@ size_t TPM_PrivateKey::key_length() const
 
 AlgorithmIdentifier TPM_PrivateKey::algorithm_identifier() const
    {
-   return AlgorithmIdentifier(object_identifier(),
+   return AlgorithmIdentifier(get_oid(),
                               AlgorithmIdentifier::USE_NULL_PARAM);
    }
 
diff --git a/src/lib/pubkey/curve25519/curve25519.cpp b/src/lib/pubkey/curve25519/curve25519.cpp
index a64c571ca..bb99f7597 100644
--- a/src/lib/pubkey/curve25519/curve25519.cpp
+++ b/src/lib/pubkey/curve25519/curve25519.cpp
@@ -39,7 +39,7 @@ secure_vector<uint8_t> curve25519(const secure_vector<uint8_t>& secret,
 
 AlgorithmIdentifier Curve25519_PublicKey::algorithm_identifier() const
    {
-   return AlgorithmIdentifier(object_identifier(), AlgorithmIdentifier::USE_EMPTY_PARAM);
+   return AlgorithmIdentifier(get_oid(), AlgorithmIdentifier::USE_EMPTY_PARAM);
    }
 
 bool Curve25519_PublicKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const
diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp b/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp
index 3f0d730ed..700491b11 100644
--- a/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp
+++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp
@@ -127,7 +127,7 @@ DilithiumMode::DilithiumMode(const OID& oid)
 DilithiumMode::DilithiumMode(const std::string& str)
    : m_mode(dilithium_mode_from_string(str)) {}
 
-OID DilithiumMode::object_identifier() const { return OID::from_string(to_string()); }
+OID DilithiumMode::get_oid() const { return OID::from_string(to_string()); }
 
 std::string DilithiumMode::to_string() const
    {
@@ -606,15 +606,15 @@ Dilithium_PublicKey::Dilithium_PublicKey(const std::vector<uint8_t>& pk, Dilithi
 
 std::string Dilithium_PublicKey::algo_name() const
    {
-   return object_identifier().to_formatted_string();
+   return get_oid().to_formatted_string();
    }
 
 AlgorithmIdentifier Dilithium_PublicKey::algorithm_identifier() const
    {
-   return AlgorithmIdentifier(object_identifier(), AlgorithmIdentifier::USE_EMPTY_PARAM);
+   return AlgorithmIdentifier(get_oid(), AlgorithmIdentifier::USE_EMPTY_PARAM);
    }
 
-OID Dilithium_PublicKey::object_identifier() const
+OID Dilithium_PublicKey::get_oid() const
    {
    return m_public->mode().oid();
    }
diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium.h b/src/lib/pubkey/dilithium/dilithium_common/dilithium.h
index bc3ddc014..afb2cf674 100644
--- a/src/lib/pubkey/dilithium/dilithium_common/dilithium.h
+++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium.h
@@ -36,7 +36,7 @@ class BOTAN_PUBLIC_API(3,0) DilithiumMode
       explicit DilithiumMode(const OID& oid);
       explicit DilithiumMode(const std::string& str);
 
-      OID object_identifier() const;
+      OID get_oid() const;
       std::string to_string() const;
 
       bool is_aes() const { return m_mode == Dilithium4x4_AES || m_mode == Dilithium6x5_AES || m_mode == Dilithium8x7_AES; }
@@ -75,7 +75,7 @@ class BOTAN_PUBLIC_API(3, 0) Dilithium_PublicKey : public virtual Public_Key
 
       AlgorithmIdentifier algorithm_identifier() const override;
 
-      OID object_identifier() const override;
+      OID get_oid() const override;
 
       size_t key_length() const override;
 
diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium_symmetric_primitives.h b/src/lib/pubkey/dilithium/dilithium_common/dilithium_symmetric_primitives.h
index 593de0dc2..65385ca15 100644
--- a/src/lib/pubkey/dilithium/dilithium_common/dilithium_symmetric_primitives.h
+++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium_symmetric_primitives.h
@@ -212,7 +212,7 @@ class DilithiumModeConstants
          }
       OID oid() const
          {
-         return m_mode.object_identifier();
+         return m_mode.get_oid();
          }
       size_t private_key_bytes() const
          {
diff --git a/src/lib/pubkey/dl_algo/dl_algo.cpp b/src/lib/pubkey/dl_algo/dl_algo.cpp
index 41b0f169a..66f26fb3f 100644
--- a/src/lib/pubkey/dl_algo/dl_algo.cpp
+++ b/src/lib/pubkey/dl_algo/dl_algo.cpp
@@ -24,7 +24,7 @@ size_t DL_Scheme_PublicKey::estimated_strength() const
 
 AlgorithmIdentifier DL_Scheme_PublicKey::algorithm_identifier() const
    {
-   return AlgorithmIdentifier(object_identifier(),
+   return AlgorithmIdentifier(get_oid(),
                               m_group.DER_encode(group_format()));
    }
 
@@ -44,7 +44,7 @@ DL_Scheme_PublicKey::DL_Scheme_PublicKey(const DL_Group& group, const BigInt& y)
 DL_Scheme_PublicKey::DL_Scheme_PublicKey(const AlgorithmIdentifier& alg_id,
                                          const std::vector<uint8_t>& key_bits,
                                          DL_Group_Format format) :
-   m_group(alg_id.parameters(), format)
+   m_group(alg_id.get_parameters(), format)
    {
    BER_Decoder(key_bits).decode(m_y);
    }
@@ -58,7 +58,7 @@ DL_Scheme_PrivateKey::DL_Scheme_PrivateKey(const AlgorithmIdentifier& alg_id,
                                            const secure_vector<uint8_t>& key_bits,
                                            DL_Group_Format format)
    {
-   m_group.BER_decode(alg_id.parameters(), format);
+   m_group.BER_decode(alg_id.get_parameters(), format);
 
    BER_Decoder(key_bits).decode(m_x);
    }
diff --git a/src/lib/pubkey/ecc_key/ecc_key.cpp b/src/lib/pubkey/ecc_key/ecc_key.cpp
index d920ce2f2..fcd67ef22 100644
--- a/src/lib/pubkey/ecc_key/ecc_key.cpp
+++ b/src/lib/pubkey/ecc_key/ecc_key.cpp
@@ -53,7 +53,7 @@ EC_PublicKey::EC_PublicKey(const EC_Group& dom_par,
 
 EC_PublicKey::EC_PublicKey(const AlgorithmIdentifier& alg_id,
                            const std::vector<uint8_t>& key_bits) :
-   m_domain_params{EC_Group(alg_id.parameters())},
+   m_domain_params{EC_Group(alg_id.get_parameters())},
    m_public_key{domain().OS2ECP(key_bits)},
    m_domain_encoding(default_encoding_for(m_domain_params))
    {
@@ -69,7 +69,7 @@ bool EC_PublicKey::check_key(RandomNumberGenerator& rng,
 
 AlgorithmIdentifier EC_PublicKey::algorithm_identifier() const
    {
-   return AlgorithmIdentifier(object_identifier(), DER_domain());
+   return AlgorithmIdentifier(get_oid(), DER_domain());
    }
 
 std::vector<uint8_t> EC_PublicKey::public_key_bits() const
@@ -157,7 +157,7 @@ EC_PrivateKey::EC_PrivateKey(const AlgorithmIdentifier& alg_id,
                              const secure_vector<uint8_t>& key_bits,
                              bool with_modular_inverse)
    {
-   m_domain_params = EC_Group(alg_id.parameters());
+   m_domain_params = EC_Group(alg_id.get_parameters());
    m_domain_encoding = default_encoding_for(m_domain_params);
 
    OID key_parameters;
diff --git a/src/lib/pubkey/ed25519/ed25519_fe.cpp b/src/lib/pubkey/ed25519/ed25519_fe.cpp
index 4a97e53b4..135813d39 100644
--- a/src/lib/pubkey/ed25519/ed25519_fe.cpp
+++ b/src/lib/pubkey/ed25519/ed25519_fe.cpp
@@ -16,10 +16,10 @@ namespace Botan {
 //static
 FE_25519 FE_25519::invert(const FE_25519& z)
    {
-   FE_25519 t0;
-   FE_25519 t1;
-   FE_25519 t2;
-   FE_25519 t3;
+   fe t0;
+   fe t1;
+   fe t2;
+   fe t3;
 
    fe_sq(t0, z);
    fe_sq_iter(t1, t0, 2);
@@ -49,9 +49,9 @@ FE_25519 FE_25519::invert(const FE_25519& z)
 
 FE_25519 FE_25519::pow_22523(const fe& z)
    {
-   FE_25519 t0;
-   FE_25519 t1;
-   FE_25519 t2;
+   fe t0;
+   fe t1;
+   fe t2;
 
    fe_sq(t0, z);
    fe_sq_iter(t1, t0, 2);
diff --git a/src/lib/pubkey/ed25519/ed25519_internal.h b/src/lib/pubkey/ed25519/ed25519_internal.h
index 45106ddcd..074f3231e 100644
--- a/src/lib/pubkey/ed25519/ed25519_internal.h
+++ b/src/lib/pubkey/ed25519/ed25519_internal.h
@@ -92,10 +92,10 @@ Representations:
 
 struct ge_p3
    {
-   FE_25519 X;
-   FE_25519 Y;
-   FE_25519 Z;
-   FE_25519 T;
+   fe X;
+   fe Y;
+   fe Z;
+   fe T;
    };
 
 int ge_frombytes_negate_vartime(ge_p3* v, const uint8_t*);
diff --git a/src/lib/pubkey/ed25519/ed25519_key.cpp b/src/lib/pubkey/ed25519/ed25519_key.cpp
index 8e666bdc5..5db8edc21 100644
--- a/src/lib/pubkey/ed25519/ed25519_key.cpp
+++ b/src/lib/pubkey/ed25519/ed25519_key.cpp
@@ -20,7 +20,7 @@ namespace Botan {
 
 AlgorithmIdentifier Ed25519_PublicKey::algorithm_identifier() const
    {
-   return AlgorithmIdentifier(object_identifier(), AlgorithmIdentifier::USE_EMPTY_PARAM);
+   return AlgorithmIdentifier(get_oid(), AlgorithmIdentifier::USE_EMPTY_PARAM);
    }
 
 bool Ed25519_PublicKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const
diff --git a/src/lib/pubkey/ed25519/ge.cpp b/src/lib/pubkey/ed25519/ge.cpp
index 971355240..e98e1f50c 100644
--- a/src/lib/pubkey/ed25519/ge.cpp
+++ b/src/lib/pubkey/ed25519/ge.cpp
@@ -23,32 +23,32 @@ Representations:
 */
 struct ge_p2
    {
-   FE_25519 X;
-   FE_25519 Y;
-   FE_25519 Z;
+   fe X;
+   fe Y;
+   fe Z;
    };
 
 struct ge_p1p1
    {
-   FE_25519 X;
-   FE_25519 Y;
-   FE_25519 Z;
-   FE_25519 T;
+   fe X;
+   fe Y;
+   fe Z;
+   fe T;
    };
 
 struct ge_precomp
    {
-   FE_25519 yplusx;
-   FE_25519 yminusx;
-   FE_25519 xy2d;
+   fe yplusx;
+   fe yminusx;
+   fe xy2d;
    };
 
 struct ge_cached
    {
-   FE_25519 YplusX;
-   FE_25519 YminusX;
-   FE_25519 Z;
-   FE_25519 T2d;
+   fe YplusX;
+   fe YminusX;
+   fe Z;
+   fe T2d;
    };
 
 /*
@@ -57,7 +57,7 @@ r = p + q
 
 void ge_add(ge_p1p1* r, const ge_p3* p, const ge_cached* q)
    {
-   FE_25519 t0;
+   fe t0;
    /* qhasm: YpX1 = Y1+X1 */
    /* asm 1: fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */
    /* asm 2: fe_add(>YpX1=r->X,<Y1=p->Y,<X1=p->X); */
@@ -120,7 +120,7 @@ r = p + q
 
 void ge_madd(ge_p1p1* r, const ge_p3* p, const ge_precomp* q)
    {
-   FE_25519 t0;
+   fe t0;
    /* qhasm: YpX1 = Y1+X1 */
    fe_add(r->X, p->Y, p->X);
 
@@ -158,7 +158,7 @@ r = p - q
 
 void ge_msub(ge_p1p1* r, const ge_p3* p, const ge_precomp* q)
    {
-   FE_25519 t0;
+   fe t0;
 
    /* qhasm: YpX1 = Y1+X1 */
    /* asm 1: fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */
@@ -241,7 +241,7 @@ r = 2 * p
 
 void ge_p2_dbl(ge_p1p1* r, const ge_p2* p)
    {
-   FE_25519 t0;
+   fe t0;
    /* qhasm: XX=X1^2 */
    /* asm 1: fe_sq(>XX=fe#1,<X1=fe#11); */
    /* asm 2: fe_sq(>XX=r->X,<X1=p->X); */
@@ -317,7 +317,7 @@ r = p
 
 void ge_p3_to_cached(ge_cached* r, const ge_p3* p)
    {
-   static const FE_25519 d2 =
+   static const fe d2 =
       {
       -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199
       } ;
@@ -333,7 +333,7 @@ r = p - q
 
 void ge_sub(ge_p1p1* r, const ge_p3* p, const ge_cached* q)
    {
-   FE_25519 t0;
+   fe t0;
    /* qhasm: YpX1 = Y1+X1 */
    /* asm 1: fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */
    /* asm 2: fe_add(>YpX1=r->X,<Y1=p->Y,<X1=p->X); */
@@ -434,9 +434,9 @@ void slide(int8_t* r, const uint8_t* a)
 
 void ge_tobytes(uint8_t* s, const ge_p2* h)
    {
-   FE_25519 recip;
-   FE_25519 x;
-   FE_25519 y;
+   fe recip;
+   fe x;
+   fe y;
 
    fe_invert(recip, h->Z);
    fe_mul(x, h->X, recip);
@@ -456,20 +456,20 @@ void ge_p2_0(ge_p2* h)
 
 int ge_frombytes_negate_vartime(ge_p3* h, const uint8_t* s)
    {
-   static const FE_25519 d =
+   static const fe d =
       {
       -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116
       } ;
-   static const FE_25519 sqrtm1 =
+   static const fe sqrtm1 =
       {
       -32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482
       } ;
 
-   FE_25519 u;
-   FE_25519 v;
-   FE_25519 v3;
-   FE_25519 vxx;
-   FE_25519 check;
+   fe u;
+   fe v;
+   fe v3;
+   fe vxx;
+   fe check;
 
    fe_frombytes(h->Y, s);
    fe_1(h->Z);
@@ -2075,7 +2075,7 @@ inline void select(ge_precomp* t,
                    ((t->xy2d[i] ^ base[7].xy2d[i]) & mask8);
       }
 
-   FE_25519 minus_xy2d;
+   fe minus_xy2d;
    fe_neg(minus_xy2d, t->xy2d);
 
    // If negative have to swap yminusx and yplusx
@@ -2092,9 +2092,9 @@ inline void select(ge_precomp* t,
 
 void ge_p3_tobytes(uint8_t* s, const ge_p3* h)
    {
-   FE_25519 recip;
-   FE_25519 x;
-   FE_25519 y;
+   fe recip;
+   fe x;
+   fe y;
 
    fe_invert(recip, h->Z);
    fe_mul(x, h->X, recip);
diff --git a/src/lib/pubkey/gost_3410/gost_3410.cpp b/src/lib/pubkey/gost_3410/gost_3410.cpp
index 419901cac..09db1f5a7 100644
--- a/src/lib/pubkey/gost_3410/gost_3410.cpp
+++ b/src/lib/pubkey/gost_3410/gost_3410.cpp
@@ -54,7 +54,7 @@ AlgorithmIdentifier GOST_3410_PublicKey::algorithm_identifier() const
    {
    std::vector<uint8_t> params;
 
-   const OID gost_oid = object_identifier();
+   const OID gost_oid = get_oid();
    const OID domain_oid = domain().get_curve_oid();
 
    DER_Encoder(params).start_sequence().encode(domain_oid).end_cons();
@@ -68,7 +68,7 @@ GOST_3410_PublicKey::GOST_3410_PublicKey(const AlgorithmIdentifier& alg_id,
    OID ecc_param_id;
 
    // The parameters also includes hash and cipher OIDs
-   BER_Decoder(alg_id.parameters()).start_sequence().decode(ecc_param_id);
+   BER_Decoder(alg_id.get_parameters()).start_sequence().decode(ecc_param_id);
 
    m_domain_params = EC_Group(ecc_param_id);
 
diff --git a/src/lib/pubkey/kyber/kyber_common/kyber.cpp b/src/lib/pubkey/kyber/kyber_common/kyber.cpp
index ae851b8e5..3cf7e5053 100644
--- a/src/lib/pubkey/kyber/kyber_common/kyber.cpp
+++ b/src/lib/pubkey/kyber/kyber_common/kyber.cpp
@@ -77,7 +77,7 @@ KyberMode::KyberMode(const OID& oid)
 KyberMode::KyberMode(const std::string& str)
    : m_mode(kyber_mode_from_string(str)) {}
 
-OID KyberMode::object_identifier() const
+OID KyberMode::get_oid() const
    {
    return OID::from_string(to_string());
    }
@@ -1298,17 +1298,17 @@ KyberMode Kyber_PublicKey::mode() const
 
 std::string Kyber_PublicKey::algo_name() const
    {
-   return object_identifier().to_formatted_string();
+   return get_oid().to_formatted_string();
    }
 
 AlgorithmIdentifier Kyber_PublicKey::algorithm_identifier() const
    {
-   return AlgorithmIdentifier(mode().object_identifier(), AlgorithmIdentifier::USE_EMPTY_PARAM);
+   return AlgorithmIdentifier(mode().get_oid(), AlgorithmIdentifier::USE_EMPTY_PARAM);
    }
 
-OID Kyber_PublicKey::object_identifier() const
+OID Kyber_PublicKey::get_oid() const
    {
-   return mode().object_identifier();
+   return mode().get_oid();
    }
 
 size_t Kyber_PublicKey::estimated_strength() const
@@ -1359,7 +1359,7 @@ void Kyber_PublicKey::initialize_from_encoding(const std::vector<uint8_t>& pub_k
 Kyber_PublicKey::Kyber_PublicKey(const AlgorithmIdentifier& alg_id,
                                  const std::vector<uint8_t>& key_bits) :
    Kyber_PublicKey(key_bits,
-                   KyberMode(alg_id.oid()),
+                   KyberMode(alg_id.get_oid()),
                    KyberKeyEncoding::Full)
    {}
 
@@ -1449,7 +1449,7 @@ Kyber_PrivateKey::Kyber_PrivateKey(RandomNumberGenerator& rng, KyberMode m)
 Kyber_PrivateKey::Kyber_PrivateKey(const AlgorithmIdentifier& alg_id,
                                    const secure_vector<uint8_t>& key_bits) :
    Kyber_PrivateKey(key_bits,
-                    KyberMode(alg_id.oid()),
+                    KyberMode(alg_id.get_oid()),
                     KyberKeyEncoding::Full)
    {}
 
diff --git a/src/lib/pubkey/kyber/kyber_common/kyber.h b/src/lib/pubkey/kyber/kyber_common/kyber.h
index 11cbc1b4e..7ae1b863f 100644
--- a/src/lib/pubkey/kyber/kyber_common/kyber.h
+++ b/src/lib/pubkey/kyber/kyber_common/kyber.h
@@ -42,7 +42,7 @@ class BOTAN_PUBLIC_API(3, 0) KyberMode
       explicit KyberMode(const OID& oid);
       explicit KyberMode(const std::string& str);
 
-      OID object_identifier() const;
+      OID get_oid() const;
       std::string to_string() const;
 
       Mode mode() const { return m_mode; }
@@ -85,7 +85,7 @@ class BOTAN_PUBLIC_API(3, 0) Kyber_PublicKey : public virtual Public_Key
 
       AlgorithmIdentifier algorithm_identifier() const override;
 
-      OID object_identifier() const override;
+      OID get_oid() const override;
 
       size_t key_length() const override;
 
diff --git a/src/lib/pubkey/mce/mceliece_key.cpp b/src/lib/pubkey/mce/mceliece_key.cpp
index d031ff5f8..efc53ca62 100644
--- a/src/lib/pubkey/mce/mceliece_key.cpp
+++ b/src/lib/pubkey/mce/mceliece_key.cpp
@@ -80,7 +80,7 @@ secure_vector<uint8_t> McEliece_PublicKey::random_plaintext_element(RandomNumber
 
 AlgorithmIdentifier McEliece_PublicKey::algorithm_identifier() const
    {
-   return AlgorithmIdentifier(object_identifier(), AlgorithmIdentifier::USE_EMPTY_PARAM);
+   return AlgorithmIdentifier(get_oid(), AlgorithmIdentifier::USE_EMPTY_PARAM);
    }
 
 std::vector<uint8_t> McEliece_PublicKey::public_key_bits() const
diff --git a/src/lib/pubkey/pbes2/pbes2.cpp b/src/lib/pubkey/pbes2/pbes2.cpp
index bc0227f72..a0c5c511c 100644
--- a/src/lib/pubkey/pbes2/pbes2.cpp
+++ b/src/lib/pubkey/pbes2/pbes2.cpp
@@ -29,13 +29,13 @@ secure_vector<uint8_t> derive_key(const std::string& passphrase,
                                   const AlgorithmIdentifier& kdf_algo,
                                   size_t default_key_size)
    {
-   if(kdf_algo.oid() == OID::from_string("PKCS5.PBKDF2"))
+   if(kdf_algo.get_oid() == OID::from_string("PKCS5.PBKDF2"))
       {
       secure_vector<uint8_t> salt;
       size_t iterations = 0, key_length = 0;
 
       AlgorithmIdentifier prf_algo;
-      BER_Decoder(kdf_algo.parameters())
+      BER_Decoder(kdf_algo.get_parameters())
          .start_sequence()
          .decode(salt, ASN1_Type::OctetString)
          .decode(iterations)
@@ -51,7 +51,7 @@ secure_vector<uint8_t> derive_key(const std::string& passphrase,
       if(key_length == 0)
          key_length = default_key_size;
 
-      const std::string prf = OIDS::oid2str_or_throw(prf_algo.oid());
+      const std::string prf = OIDS::oid2str_or_throw(prf_algo.get_oid());
       auto pbkdf_fam = PasswordHashFamily::create_or_throw("PBKDF2(" + prf + ")");
       auto pbkdf = pbkdf_fam->from_params(iterations);
 
@@ -61,14 +61,14 @@ secure_vector<uint8_t> derive_key(const std::string& passphrase,
                         salt.data(), salt.size());
       return derived_key;
       }
-   else if(kdf_algo.oid() == OID::from_string("Scrypt"))
+   else if(kdf_algo.get_oid() == OID::from_string("Scrypt"))
       {
       secure_vector<uint8_t> salt;
       size_t N = 0, r = 0, p = 0;
       size_t key_length = 0;
 
       AlgorithmIdentifier prf_algo;
-      BER_Decoder(kdf_algo.parameters())
+      BER_Decoder(kdf_algo.get_parameters())
          .start_sequence()
          .decode(salt, ASN1_Type::OctetString)
          .decode(N)
@@ -92,7 +92,7 @@ secure_vector<uint8_t> derive_key(const std::string& passphrase,
       }
    else
       throw Decoding_Error("PBE-PKCS5 v2.0: Unknown KDF algorithm " +
-                           kdf_algo.oid().to_string());
+                           kdf_algo.get_oid().to_string());
    }
 
 secure_vector<uint8_t> derive_key(const std::string& passphrase,
@@ -310,7 +310,7 @@ pbes2_decrypt(const secure_vector<uint8_t>& key_bits,
          .decode(enc_algo)
       .end_cons();
 
-   const std::string cipher = OIDS::oid2str_or_throw(enc_algo.oid());
+   const std::string cipher = OIDS::oid2str_or_throw(enc_algo.get_oid());
    const std::vector<std::string> cipher_spec = split_on(cipher, '/');
    if(cipher_spec.size() != 2)
       throw Decoding_Error("PBE-PKCS5 v2.0: Invalid cipher spec " + cipher);
@@ -318,7 +318,7 @@ pbes2_decrypt(const secure_vector<uint8_t>& key_bits,
       throw Decoding_Error("PBE-PKCS5 v2.0: Don't know param format for " + cipher);
 
    secure_vector<uint8_t> iv;
-   BER_Decoder(enc_algo.parameters()).decode(iv, ASN1_Type::OctetString).verify_end();
+   BER_Decoder(enc_algo.get_parameters()).decode(iv, ASN1_Type::OctetString).verify_end();
 
    std::unique_ptr<Cipher_Mode> dec = Cipher_Mode::create(cipher, Cipher_Dir::Decryption);
    if(!dec)
diff --git a/src/lib/pubkey/pk_algs.cpp b/src/lib/pubkey/pk_algs.cpp
index b65954914..698aaa029 100644
--- a/src/lib/pubkey/pk_algs.cpp
+++ b/src/lib/pubkey/pk_algs.cpp
@@ -86,7 +86,7 @@ std::unique_ptr<Public_Key>
 load_public_key(const AlgorithmIdentifier& alg_id,
                 const std::vector<uint8_t>& key_bits)
    {
-   const std::string oid_str = alg_id.oid().to_formatted_string();
+   const std::string oid_str = alg_id.get_oid().to_formatted_string();
    const std::vector<std::string> alg_info = split_on(oid_str, '/');
    const std::string alg_name = alg_info[0];
 
@@ -177,7 +177,7 @@ std::unique_ptr<Private_Key>
 load_private_key(const AlgorithmIdentifier& alg_id,
                  const secure_vector<uint8_t>& key_bits)
    {
-   const std::string oid_str = alg_id.oid().to_formatted_string();
+   const std::string oid_str = alg_id.get_oid().to_formatted_string();
    const std::vector<std::string> alg_info = split_on(oid_str, '/');
    const std::string alg_name = alg_info[0];
 
diff --git a/src/lib/pubkey/pk_keys.cpp b/src/lib/pubkey/pk_keys.cpp
index 5a76ef79c..1588809fe 100644
--- a/src/lib/pubkey/pk_keys.cpp
+++ b/src/lib/pubkey/pk_keys.cpp
@@ -55,7 +55,7 @@ const BigInt& Public_Key::get_int_field(const std::string& field) const
 /*
 * Default OID access
 */
-OID Public_Key::object_identifier() const
+OID Public_Key::get_oid() const
    {
    OID o = OIDS::str2oid_or_empty(algo_name());
    if(o.empty())
diff --git a/src/lib/pubkey/pk_keys.h b/src/lib/pubkey/pk_keys.h
index 6542b69bb..a5e6fa635 100644
--- a/src/lib/pubkey/pk_keys.h
+++ b/src/lib/pubkey/pk_keys.h
@@ -66,17 +66,10 @@ class BOTAN_PUBLIC_API(2,0) Public_Key
       virtual size_t key_length() const = 0;
 
       /**
-      * @return object identifier of the underlying public key scheme
+      * Get the OID of the underlying public key scheme.
+      * @return OID of the public key scheme
       */
-      virtual OID object_identifier() const;
-
-      /**
-      * Deprecated version of object_identifier
-      */
-      BOTAN_DEPRECATED("Use object_identifier") OID get_oid() const
-         {
-         return this->object_identifier();
-         }
+      virtual OID get_oid() const;
 
       /**
       * Test the key values for consistency.
diff --git a/src/lib/pubkey/pkcs8.cpp b/src/lib/pubkey/pkcs8.cpp
index d4d7b9c7f..bddabea8e 100644
--- a/src/lib/pubkey/pkcs8.cpp
+++ b/src/lib/pubkey/pkcs8.cpp
@@ -102,10 +102,10 @@ secure_vector<uint8_t> PKCS8_decode(
       {
       if(is_encrypted)
          {
-         if(OIDS::oid2str_or_throw(pbe_alg_id.oid()) != "PBE-PKCS5v20")
-            throw PKCS8_Exception("Unknown PBE type " + pbe_alg_id.oid().to_string());
+         if(OIDS::oid2str_or_throw(pbe_alg_id.get_oid()) != "PBE-PKCS5v20")
+            throw PKCS8_Exception("Unknown PBE type " + pbe_alg_id.get_oid().to_string());
 #if defined(BOTAN_HAS_PKCS5_PBES2)
-         key = pbes2_decrypt(key_data, get_passphrase(), pbe_alg_id.parameters());
+         key = pbes2_decrypt(key_data, get_passphrase(), pbe_alg_id.get_parameters());
 #else
          BOTAN_UNUSED(get_passphrase);
          throw Decoding_Error("Private key is encrypted but PBES2 was disabled in build");
@@ -339,10 +339,10 @@ load_key(DataSource& source,
    AlgorithmIdentifier alg_id;
    secure_vector<uint8_t> pkcs8_key = PKCS8_decode(source, get_pass, alg_id, is_encrypted);
 
-   const std::string alg_name = OIDS::oid2str_or_empty(alg_id.oid());
+   const std::string alg_name = OIDS::oid2str_or_empty(alg_id.get_oid());
    if(alg_name.empty())
       throw PKCS8_Exception("Unknown algorithm OID: " +
-                            alg_id.oid().to_string());
+                            alg_id.get_oid().to_string());
 
    return load_private_key(alg_id, pkcs8_key);
    }
diff --git a/src/lib/pubkey/rsa/rsa.cpp b/src/lib/pubkey/rsa/rsa.cpp
index 39698f61d..91e87e3a7 100644
--- a/src/lib/pubkey/rsa/rsa.cpp
+++ b/src/lib/pubkey/rsa/rsa.cpp
@@ -153,7 +153,7 @@ size_t RSA_PublicKey::estimated_strength() const
 
 AlgorithmIdentifier RSA_PublicKey::algorithm_identifier() const
    {
-   return AlgorithmIdentifier(object_identifier(), AlgorithmIdentifier::USE_NULL_PARAM);
+   return AlgorithmIdentifier(get_oid(), AlgorithmIdentifier::USE_NULL_PARAM);
    }
 
 std::vector<uint8_t> RSA_PublicKey::public_key_bits() const
@@ -285,12 +285,13 @@ RSA_PrivateKey::RSA_PrivateKey(RandomNumberGenerator& rng,
    if(exp < 3 || exp % 2 == 0)
       throw Invalid_Argument(algo_name() + ": Invalid encryption exponent");
 
+   BigInt n, e, d, p, q, d1, d2, c;
+
+   e = exp;
+
    const size_t p_bits = (bits + 1) / 2;
    const size_t q_bits = bits - p_bits;
 
-   BigInt p, q, n;
-   BigInt e = BigInt::from_u64(exp);
-
    for(size_t attempt = 0; ; ++attempt)
       {
       if(attempt > 10)
@@ -318,11 +319,10 @@ RSA_PrivateKey::RSA_PrivateKey(RandomNumberGenerator& rng,
    const BigInt phi_n = lcm(p_minus_1, q_minus_1);
    // This is guaranteed because p,q == 3 mod 4
    BOTAN_DEBUG_ASSERT(low_zero_bits(phi_n) == 1);
-
-   BigInt d = inverse_mod(e, phi_n);
-   BigInt d1 = ct_modulo(d, p_minus_1);
-   BigInt d2 = ct_modulo(d, q_minus_1);
-   BigInt c = inverse_mod(q, p);
+   d = inverse_mod(e, phi_n);
+   d1 = ct_modulo(d, p_minus_1);
+   d2 = ct_modulo(d, q_minus_1);
+   c = inverse_mod(q, p);
 
    RSA_PublicKey::init(std::move(n), std::move(e));
 
diff --git a/src/lib/pubkey/xmss/info.txt b/src/lib/pubkey/xmss/info.txt
index 7786999c5..8dbde490e 100644
--- a/src/lib/pubkey/xmss/info.txt
+++ b/src/lib/pubkey/xmss/info.txt
@@ -8,8 +8,6 @@ name -> "XMSS"
 
 <header:public>
 xmss.h
-xmss_hash.h
-xmss_wots.h
 xmss_parameters.h
 </header:public>
 
@@ -17,11 +15,13 @@ xmss_parameters.h
 atomic.h
 xmss_address.h
 xmss_common_ops.h
+xmss_hash.h
 xmss_index_registry.h
 xmss_signature.h
 xmss_signature_operation.h
 xmss_tools.h
 xmss_verification_operation.h
+xmss_wots.h
 </header:internal>
 
 <requires>
diff --git a/src/lib/pubkey/xmss/xmss.h b/src/lib/pubkey/xmss/xmss.h
index cbe137606..2e0760e9d 100644
--- a/src/lib/pubkey/xmss/xmss.h
+++ b/src/lib/pubkey/xmss/xmss.h
@@ -9,15 +9,21 @@
 #ifndef BOTAN_XMSS_H_
 #define BOTAN_XMSS_H_
 
+#include <span>
+#include <memory>
+
 #include <botan/pk_keys.h>
 #include <botan/exceptn.h>
 #include <botan/xmss_parameters.h>
-#include <botan/xmss_wots.h>
 
 namespace Botan {
 
 class RandomNumberGenerator;
+class XMSS_Address;
+class XMSS_Hash;
+class XMSS_PrivateKey_Internal;
 class XMSS_Verification_Operation;
+class XMSS_WOTS_PrivateKey;
 
 /**
  * An XMSS: Extended Hash-Based Signature public key.
@@ -50,7 +56,7 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PublicKey : public virtual Public_Key
        *
        * @param key_bits DER encoded public key bits
        */
-      XMSS_PublicKey(const std::vector<uint8_t>& key_bits);
+      XMSS_PublicKey(std::span<const uint8_t> key_bits);
 
       /**
        * Creates a new XMSS public key for a chosen XMSS signature method as
@@ -66,99 +72,6 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PublicKey : public virtual Public_Key
          : m_xmss_params(xmss_oid), m_wots_params(m_xmss_params.ots_oid()),
            m_root(std::move(root)), m_public_seed(std::move(public_seed)) {}
 
-      /**
-       * Retrieves the chosen XMSS signature method.
-       *
-       * @return XMSS signature method identifier.
-       **/
-      XMSS_Parameters::xmss_algorithm_t xmss_oid() const
-         {
-         return m_xmss_params.oid();
-         }
-
-      /**
-       * Sets the chosen XMSS signature method
-       **/
-      void set_xmss_oid(XMSS_Parameters::xmss_algorithm_t xmss_oid)
-         {
-         m_xmss_params = XMSS_Parameters(xmss_oid);
-         m_wots_params = XMSS_WOTS_Parameters(m_xmss_params.ots_oid());
-         }
-
-      /**
-       * Retrieves the XMSS parameters determined by the chosen XMSS Signature
-       * method.
-       *
-       * @return XMSS parameters.
-       **/
-      const XMSS_Parameters& xmss_parameters() const
-         {
-         return m_xmss_params;
-         }
-
-      /**
-       * Retrieves the XMSS parameters determined by the chosen XMSS Signature
-       * method.
-       *
-       * @return XMSS parameters.
-       **/
-      std::string xmss_hash_function() const
-         {
-         return m_xmss_params.hash_function_name();
-         }
-
-      /**
-       * Retrieves the Winternitz One Time Signature (WOTS) method,
-       * corresponding to the chosen XMSS signature method.
-       *
-       * @return XMSS WOTS signature method identifier.
-       **/
-      XMSS_WOTS_Parameters::ots_algorithm_t wots_oid() const
-         {
-         return m_wots_params.oid();
-         }
-
-      /**
-       * Retrieves the Winternitz One Time Signature (WOTS) parameters
-       * corresponding to the chosen XMSS signature method.
-       *
-       * @return XMSS WOTS signature method parameters.
-       **/
-      const XMSS_WOTS_Parameters& wots_parameters() const
-         {
-         return m_wots_params;
-         }
-
-      secure_vector<uint8_t>& root()
-         {
-         return m_root;
-         }
-
-      void set_root(secure_vector<uint8_t> root)
-         {
-         m_root = std::move(root);
-         }
-
-      const secure_vector<uint8_t>& root() const
-         {
-         return m_root;
-         }
-
-      virtual secure_vector<uint8_t>& public_seed()
-         {
-         return m_public_seed;
-         }
-
-      virtual void set_public_seed(secure_vector<uint8_t> public_seed)
-         {
-         m_public_seed = std::move(public_seed);
-         }
-
-      virtual const secure_vector<uint8_t>& public_seed() const
-         {
-         return m_public_seed;
-         }
-
       std::string algo_name() const override
          {
          return "XMSS";
@@ -166,7 +79,7 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PublicKey : public virtual Public_Key
 
       AlgorithmIdentifier algorithm_identifier() const override
          {
-         return AlgorithmIdentifier(object_identifier(), AlgorithmIdentifier::USE_EMPTY_PARAM);
+         return AlgorithmIdentifier(get_oid(), AlgorithmIdentifier::USE_EMPTY_PARAM);
          }
 
       bool check_key(RandomNumberGenerator&, bool) const override
@@ -196,17 +109,6 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PublicKey : public virtual Public_Key
        **/
       std::vector<uint8_t> public_key_bits() const override;
 
-      /**
-       * Size in bytes of the serialized XMSS public key produced by
-       * raw_public_key().
-       *
-       * @return size in bytes of serialized Public Key.
-       **/
-      virtual size_t size() const
-         {
-         return sizeof(uint32_t) + 2 * m_xmss_params.element_size();
-         }
-
       /**
        * Generates a byte sequence representing the XMSS
        * public key, as defined in [1] (p. 23, "XMSS Public Key")
@@ -214,7 +116,25 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PublicKey : public virtual Public_Key
        * @return 4-byte OID, followed by n-byte root node, followed by
        *         public seed.
        **/
-      virtual std::vector<uint8_t> raw_public_key() const;
+      std::vector<uint8_t> raw_public_key() const;
+
+   protected:
+      friend class XMSS_Verification_Operation;
+
+      const secure_vector<uint8_t>& public_seed() const
+         {
+         return m_public_seed;
+         }
+
+      const secure_vector<uint8_t>& root() const
+         {
+         return m_root;
+         }
+
+      const XMSS_Parameters& xmss_parameters() const
+         {
+         return m_xmss_params;
+         }
 
    protected:
       std::vector<uint8_t> m_raw_key;
@@ -264,7 +184,7 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PrivateKey final : public virtual XMSS_PublicKe
        *
        * @param raw_key An XMSS private key serialized using raw_private_key().
        **/
-      XMSS_PrivateKey(const secure_vector<uint8_t>& raw_key);
+      XMSS_PrivateKey(std::span<const uint8_t> raw_key);
 
       /**
        * Creates a new XMSS private key for the chosen XMSS signature method
@@ -299,61 +219,13 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PrivateKey final : public virtual XMSS_PublicKe
        *
        * @return Index of the last unused leaf.
        **/
+      BOTAN_DEPRECATED("Use remaining_signatures()")
       size_t unused_leaf_index() const;
 
       /**
-       * Sets the last unused leaf index of the private key. The leaf index
-       * will be updated automatically during every signing operation, and
-       * should not be set manually.
-       *
-       * @param idx Index of the last unused leaf.
-       **/
-      void set_unused_leaf_index(size_t idx);
-
-      size_t reserve_unused_leaf_index();
-
-      /**
-       * Winternitz One Time Signature Scheme key utilized for signing
-       * operations.
-       *
-       * @return WOTS+ private key.
-       **/
-      const XMSS_WOTS_PrivateKey& wots_private_key() const
-         {
-         return m_wots_priv_key;
-         }
-
-      /**
-       * Winternitz One Time Signature Scheme key utilized for signing
-       * operations.
-       *
-       * @return WOTS+ private key.
-       **/
-      XMSS_WOTS_PrivateKey& wots_private_key()
-         {
-         return m_wots_priv_key;
-         }
-
-      const secure_vector<uint8_t>& prf() const
-         {
-         return m_prf;
-         }
-
-      secure_vector<uint8_t>& prf()
-         {
-         return m_prf;
-         }
-
-      void set_public_seed(secure_vector<uint8_t> public_seed) override
-         {
-         m_public_seed = std::move(public_seed);
-         m_wots_priv_key.set_public_seed(m_public_seed);
-         }
-
-      const secure_vector<uint8_t>& public_seed() const override
-         {
-         return m_public_seed;
-         }
+       * Retrieves the number of remaining signatures for this private key.
+       */
+      size_t remaining_signatures() const;
 
       std::unique_ptr<PK_Ops::Signature>
       create_signature_op(RandomNumberGenerator&,
@@ -362,13 +234,6 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PrivateKey final : public virtual XMSS_PublicKe
 
       secure_vector<uint8_t> private_key_bits() const override;
 
-      size_t size() const override
-         {
-         return XMSS_PublicKey::size() +
-                sizeof(uint32_t) +
-                2 * XMSS_PublicKey::m_xmss_params.element_size();
-         }
-
       /**
        * Generates a non standartized byte sequence representing the XMSS
        * private key.
@@ -378,6 +243,15 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PrivateKey final : public virtual XMSS_PublicKe
        *         8-byte unused leaf index, n-byte prf seed, n-byte private seed.
        **/
       secure_vector<uint8_t> raw_private_key() const;
+
+   private:
+      friend class XMSS_Signature_Operation;
+
+      size_t reserve_unused_leaf_index();
+
+      XMSS_WOTS_PrivateKey& wots_private_key();
+      const secure_vector<uint8_t>& prf_value() const;
+
       /**
        * Algorithm 9: "treeHash"
        * Computes the internal n-byte nodes of a Merkle tree.
@@ -395,19 +269,10 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PrivateKey final : public virtual XMSS_PublicKe
          size_t target_node_height,
          XMSS_Address& adrs);
 
-   private:
-      /**
-       * Fetches shared unused leaf index from the index registry
-       **/
-      std::shared_ptr<Atomic<size_t>> recover_global_leaf_index() const;
-
-      inline void tree_hash_subtree(secure_vector<uint8_t>& result,
-                                    size_t start_idx,
-                                    size_t target_node_height,
-                                    XMSS_Address& adrs)
-         {
-         return tree_hash_subtree(result, start_idx, target_node_height, adrs, m_hash);
-         }
+      void tree_hash_subtree(secure_vector<uint8_t>& result,
+                             size_t start_idx,
+                             size_t target_node_height,
+                             XMSS_Address& adrs);
 
 
       /**
@@ -419,10 +284,7 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PrivateKey final : public virtual XMSS_PublicKe
                              XMSS_Address& adrs,
                              XMSS_Hash& hash);
 
-      XMSS_WOTS_PrivateKey m_wots_priv_key;
-      XMSS_Hash m_hash;
-      secure_vector<uint8_t> m_prf;
-      XMSS_Index_Registry& m_index_reg;
+      std::shared_ptr<XMSS_PrivateKey_Internal> m_private;
    };
 
 }
diff --git a/src/lib/pubkey/xmss/xmss_common_ops.cpp b/src/lib/pubkey/xmss/xmss_common_ops.cpp
index bdc03e40f..0a7399f56 100644
--- a/src/lib/pubkey/xmss/xmss_common_ops.cpp
+++ b/src/lib/pubkey/xmss/xmss_common_ops.cpp
@@ -7,7 +7,7 @@
  **/
 
 #include <botan/internal/xmss_common_ops.h>
-#include <botan/xmss_hash.h>
+#include <botan/internal/xmss_hash.h>
 
 namespace Botan {
 
diff --git a/src/lib/pubkey/xmss/xmss_hash.cpp b/src/lib/pubkey/xmss/xmss_hash.cpp
index 8e4de06fd..3b8c779a7 100644
--- a/src/lib/pubkey/xmss/xmss_hash.cpp
+++ b/src/lib/pubkey/xmss/xmss_hash.cpp
@@ -7,7 +7,7 @@
  * Botan is released under the Simplified BSD License (see license.txt)
  **/
 
-#include <botan/xmss_hash.h>
+#include <botan/internal/xmss_hash.h>
 #include <botan/exceptn.h>
 
 namespace Botan {
diff --git a/src/lib/pubkey/xmss/xmss_hash.h b/src/lib/pubkey/xmss/xmss_hash.h
index db8b8d2cb..f45432d59 100644
--- a/src/lib/pubkey/xmss/xmss_hash.h
+++ b/src/lib/pubkey/xmss/xmss_hash.h
@@ -10,15 +10,13 @@
 
 #include <botan/hash.h>
 
-//BOTAN_FUTURE_INTERNAL_HEADER(xmss_hash.h)
-
 namespace Botan {
 
 /**
  * A collection of pseudorandom hash functions required for XMSS and WOTS
  * computations.
  **/
-class BOTAN_PUBLIC_API(2,0) XMSS_Hash final
+class XMSS_Hash final
    {
    public:
       XMSS_Hash(const std::string& h_func_name);
diff --git a/src/lib/pubkey/xmss/xmss_parameters.h b/src/lib/pubkey/xmss/xmss_parameters.h
index 2f186ac53..6aeb05baa 100644
--- a/src/lib/pubkey/xmss/xmss_parameters.h
+++ b/src/lib/pubkey/xmss/xmss_parameters.h
@@ -8,11 +8,116 @@
 #ifndef BOTAN_XMSS_PARAMETERS_H_
 #define BOTAN_XMSS_PARAMETERS_H_
 
-#include <botan/xmss_wots.h>
 #include <string>
+#include <map>
+
+#include <botan/build.h>
+#include <botan/secmem.h>
 
 namespace Botan {
 
+/**
+ * Descibes a signature method for XMSS Winternitz One Time Signatures,
+ * as defined in:
+ * [1] XMSS: Extended Hash-Based Signatures,
+ *     Request for Comments: 8391
+ *     Release: May 2018.
+ *     https://datatracker.ietf.org/doc/rfc8391/
+ **/
+class BOTAN_PUBLIC_API(2,0) XMSS_WOTS_Parameters final
+   {
+   public:
+      enum ots_algorithm_t
+         {
+         WOTSP_SHA2_256 = 0x00000001,
+         WOTSP_SHA2_512 = 0x00000002,
+         WOTSP_SHAKE_256 = 0x00000003,
+         WOTSP_SHAKE_512 = 0x00000004
+         };
+
+      XMSS_WOTS_Parameters(const std::string& algo_name);
+      XMSS_WOTS_Parameters(ots_algorithm_t ots_spec);
+
+      static ots_algorithm_t xmss_wots_id_from_string(const std::string& param_set);
+
+      /**
+       * Algorithm 1: convert input string to base.
+       *
+       * @param msg Input string (referred to as X in [1]).
+       * @param out_size size of message in base w.
+       *
+       * @return Input string converted to the given base.
+       **/
+      secure_vector<uint8_t> base_w(const secure_vector<uint8_t>& msg, size_t out_size) const;
+
+      secure_vector<uint8_t> base_w(size_t value) const;
+
+      void append_checksum(secure_vector<uint8_t>& data) const;
+
+      /**
+       * @return XMSS WOTS registry name for the chosen parameter set.
+       **/
+      const std::string& name() const
+         {
+         return m_name;
+         }
+
+      /**
+       * @return Botan name for the hash function used.
+       **/
+      const std::string& hash_function_name() const
+         {
+         return m_hash_name;
+         }
+
+      /**
+       * Retrieves the uniform length of a message, and the size of
+       * each node. This correlates to XMSS parameter "n" defined
+       * in [1].
+       *
+       * @return element length in bytes.
+       **/
+      size_t element_size() const { return m_element_size; }
+
+      /**
+       * The Winternitz parameter.
+       *
+       * @return numeric base used for internal representation of
+       *         data.
+       **/
+      size_t wots_parameter() const { return m_w; }
+
+      size_t len() const { return m_len; }
+
+      size_t len_1() const { return m_len_1; }
+
+      size_t len_2() const { return m_len_2; }
+
+      size_t lg_w() const { return m_lg_w; }
+
+      ots_algorithm_t oid() const { return m_oid; }
+
+      size_t estimated_strength() const { return m_strength; }
+
+      bool operator==(const XMSS_WOTS_Parameters& p) const
+         {
+         return m_oid == p.m_oid;
+         }
+
+   private:
+      static const std::map<std::string, ots_algorithm_t> m_oid_name_lut;
+      ots_algorithm_t m_oid;
+      std::string m_name;
+      std::string m_hash_name;
+      size_t m_element_size;
+      size_t m_w;
+      size_t m_len_1;
+      size_t m_len_2;
+      size_t m_len;
+      size_t m_strength;
+      uint8_t m_lg_w;
+   };
+
 /**
  * Descibes a signature method for XMSS, as defined in:
  * [1] XMSS: Extended Hash-Based Signatures,
@@ -71,6 +176,11 @@ class BOTAN_PUBLIC_API(2,0) XMSS_Parameters
        **/
       size_t tree_height() const { return m_tree_height; }
 
+      /**
+       * @returns total number of signatures allowed for this XMSS instance
+       */
+      size_t total_number_of_signatures() const { return size_t(1) << tree_height(); }
+
       /**
        * The Winternitz parameter.
        *
@@ -97,6 +207,18 @@ class BOTAN_PUBLIC_API(2,0) XMSS_Parameters
          return m_strength;
          }
 
+      size_t raw_public_key_size() const
+         {
+         return sizeof(uint32_t) + 2 * element_size();
+         }
+
+      size_t raw_private_key_size() const
+         {
+         return raw_public_key_size() +
+                sizeof(uint32_t) +
+                2 * element_size();
+         }
+
       bool operator==(const XMSS_Parameters& p) const
          {
          return m_oid == p.m_oid;
diff --git a/src/lib/pubkey/xmss/xmss_privatekey.cpp b/src/lib/pubkey/xmss/xmss_privatekey.cpp
index 1355718d4..4e8e407f2 100644
--- a/src/lib/pubkey/xmss/xmss_privatekey.cpp
+++ b/src/lib/pubkey/xmss/xmss_privatekey.cpp
@@ -19,6 +19,8 @@
 #include <botan/internal/xmss_signature_operation.h>
 #include <botan/internal/xmss_index_registry.h>
 #include <botan/internal/xmss_common_ops.h>
+#include <botan/internal/loadstor.h>
+#include <botan/internal/stl_util.h>
 #include <botan/ber_dec.h>
 #include <botan/der_enc.h>
 #include <iterator>
@@ -32,105 +34,206 @@ namespace Botan {
 namespace {
 
 // fall back to raw decoding for previous versions, which did not encode an OCTET STRING
-secure_vector<uint8_t> extract_raw_key(const secure_vector<uint8_t>& key_bits)
+secure_vector<uint8_t> extract_raw_private_key(std::span<const uint8_t> key_bits)
    {
    secure_vector<uint8_t> raw_key;
    try
       {
-      BER_Decoder(key_bits).decode(raw_key, ASN1_Type::OctetString);
+      DataSource_Memory src(key_bits);
+      BER_Decoder(src).decode(raw_key, ASN1_Type::OctetString);
       }
    catch(Decoding_Error&)
       {
-      raw_key = key_bits;
+      raw_key.assign(key_bits.begin(), key_bits.end());
       }
    return raw_key;
    }
 
 }
 
-XMSS_PrivateKey::XMSS_PrivateKey(const secure_vector<uint8_t>& key_bits)
-   : XMSS_PublicKey(unlock(key_bits)),
-     m_wots_priv_key(m_wots_params.oid(), m_public_seed),
-     m_hash(xmss_hash_function()),
-     m_index_reg(XMSS_Index_Registry::get_instance())
+class XMSS_PrivateKey_Internal
    {
-   /*
-   The code requires sizeof(size_t) >= ceil(tree_height / 8)
-
-   Maximum supported tree height is 20, ceil(20/8) == 3, so 4 byte
-   size_t is sufficient for all defined parameters, or even a
-   (hypothetical) tree height 32, which would be extremely slow to
-   compute.
-   */
-   static_assert(sizeof(size_t) >= 4, "size_t is big enough to support leaf index");
+   public:
+      XMSS_PrivateKey_Internal(const XMSS_Parameters& xmss_params,
+                               const XMSS_WOTS_Parameters& wots_params,
+                               secure_vector<uint8_t> public_seed,
+                               secure_vector<uint8_t> private_seed,
+                               secure_vector<uint8_t> prf)
+         : m_xmss_params(xmss_params)
+         , m_wots_params(wots_params)
+         , m_wots_priv_key(wots_params.oid(), std::move(public_seed), std::move(private_seed))
+         , m_hash(m_xmss_params.hash_function_name())
+         , m_prf(std::move(prf))
+         , m_index_reg(XMSS_Index_Registry::get_instance()) {}
+
+      XMSS_PrivateKey_Internal(const XMSS_Parameters& xmss_params,
+                               const XMSS_WOTS_Parameters& wots_params,
+                               secure_vector<uint8_t> public_seed,
+                               std::span<const uint8_t> key_bits)
+         : m_xmss_params(xmss_params)
+         , m_wots_params(wots_params)
+         , m_wots_priv_key(wots_params.oid(), std::move(public_seed))
+         , m_hash(m_xmss_params.hash_function_name())
+         , m_index_reg(XMSS_Index_Registry::get_instance())
+         {
+         /*
+         The code requires sizeof(size_t) >= ceil(tree_height / 8)
+
+         Maximum supported tree height is 20, ceil(20/8) == 3, so 4 byte
+         size_t is sufficient for all defined parameters, or even a
+         (hypothetical) tree height 32, which would be extremely slow to
+         compute.
+         */
+         static_assert(sizeof(size_t) >= 4, "size_t is big enough to support leaf index");
+
+         const secure_vector<uint8_t> raw_key = extract_raw_private_key(key_bits);
+
+         if(raw_key.size() != m_xmss_params.raw_private_key_size())
+            {
+            throw Decoding_Error("Invalid XMSS private key size");
+            }
+
+         // extract & copy unused leaf index from raw_key
+         uint64_t unused_leaf = 0;
+         auto begin = (raw_key.begin() + m_xmss_params.raw_public_key_size());
+         auto end = raw_key.begin() + m_xmss_params.raw_public_key_size() + sizeof(uint32_t);
+
+         for(auto& i = begin; i != end; i++)
+            {
+            unused_leaf = ((unused_leaf << 8) | *i);
+            }
+
+         if(unused_leaf >= (1ull << m_xmss_params.tree_height()))
+            {
+            throw Decoding_Error("XMSS private key leaf index out of bounds");
+            }
+
+         begin = end;
+         end = begin + m_xmss_params.element_size();
+         m_prf.clear();
+         m_prf.reserve(m_xmss_params.element_size());
+         std::copy(begin, end, std::back_inserter(m_prf));
+
+         begin = end;
+         end = begin + m_wots_params.element_size();
+         m_wots_priv_key.set_private_seed(secure_vector<uint8_t>(begin, end));
+         set_unused_leaf_index(static_cast<size_t>(unused_leaf));
+         }
 
-   secure_vector<uint8_t> raw_key = extract_raw_key(key_bits);
+      secure_vector<uint8_t> serialize(std::vector<uint8_t> raw_public_key) const {
+         std::vector<uint8_t> unused_index(4);
+         store_be(static_cast<uint32_t>(unused_leaf_index()), unused_index.data());
 
-   if(raw_key.size() != XMSS_PrivateKey::size())
-      {
-      throw Decoding_Error("Invalid XMSS private key size");
+         return concat_as<secure_vector<uint8_t>>(
+            raw_public_key,
+            unused_index,
+            m_prf,
+            m_wots_priv_key.private_seed());
       }
 
-   // extract & copy unused leaf index from raw_key
-   uint64_t unused_leaf = 0;
-   auto begin = (raw_key.begin() + XMSS_PublicKey::size());
-   auto end = raw_key.begin() + XMSS_PublicKey::size() + sizeof(uint32_t);
+      XMSS_Hash& hash() { return m_hash; }
+      const secure_vector<uint8_t>& prf_value() const { return m_prf; }
+      secure_vector<uint8_t>& prf_value() { return m_prf; }
+      const XMSS_WOTS_PrivateKey& wots_private_key() const { return m_wots_priv_key; }
+      XMSS_WOTS_PrivateKey& wots_private_key() { return m_wots_priv_key; }
+      XMSS_Index_Registry& index_registry() { return m_index_reg; }
 
-   for(auto& i = begin; i != end; i++)
-      {
-      unused_leaf = ((unused_leaf << 8) | *i);
-      }
+      std::shared_ptr<Atomic<size_t>>
+      recover_global_leaf_index() const
+         {
+         BOTAN_ASSERT(m_wots_priv_key.private_seed().size() ==
+                     m_xmss_params.element_size() &&
+                     m_prf.size() == m_xmss_params.element_size(),
+                     "Trying to retrieve index for partially initialized key");
+         return m_index_reg.get(m_wots_priv_key.private_seed(), m_prf);
+         }
 
-   if(unused_leaf >= (1ull << XMSS_PublicKey::m_xmss_params.tree_height()))
-      {
-      throw Decoding_Error("XMSS private key leaf index out of bounds");
-      }
+      void set_unused_leaf_index(size_t idx)
+         {
+         if(idx >= (1ull << m_xmss_params.tree_height()))
+            {
+            throw Decoding_Error("XMSS private key leaf index out of bounds");
+            }
+         else
+            {
+            std::atomic<size_t>& index =
+               static_cast<std::atomic<size_t>&>(*recover_global_leaf_index());
+            size_t current = 0;
+
+            do
+               {
+               current = index.load();
+               if(current > idx)
+                  { return; }
+               }
+            while(!index.compare_exchange_strong(current, idx));
+            }
+         }
 
-   begin = end;
-   end = begin + XMSS_PublicKey::m_xmss_params.element_size();
-   m_prf.clear();
-   m_prf.reserve(XMSS_PublicKey::m_xmss_params.element_size());
-   std::copy(begin, end, std::back_inserter(m_prf));
+      size_t reserve_unused_leaf_index()
+         {
+         size_t idx = (static_cast<std::atomic<size_t>&>(
+                        *recover_global_leaf_index())).fetch_add(1);
+         if(idx >= m_xmss_params.total_number_of_signatures())
+            {
+            throw Decoding_Error("XMSS private key, one time signatures exhaused");
+            }
+         return idx;
+         }
 
-   begin = end;
-   end = begin + m_wots_params.element_size();
-   m_wots_priv_key.set_private_seed(secure_vector<uint8_t>(begin, end));
-   set_unused_leaf_index(static_cast<size_t>(unused_leaf));
-   }
+      size_t unused_leaf_index() const
+         {
+         return *recover_global_leaf_index();
+         }
+
+      size_t remaining_signatures() const
+         {
+         return m_xmss_params.total_number_of_signatures() - *recover_global_leaf_index();
+         }
+
+   private:
+      const XMSS_Parameters& m_xmss_params;
+      const XMSS_WOTS_Parameters& m_wots_params;
+
+      XMSS_WOTS_PrivateKey m_wots_priv_key;
+      XMSS_Hash m_hash;
+      secure_vector<uint8_t> m_prf;
+      XMSS_Index_Registry& m_index_reg;
+   };
+
+
+XMSS_PrivateKey::XMSS_PrivateKey(std::span<const uint8_t> key_bits)
+   : XMSS_PublicKey(key_bits)
+   , m_private(std::make_shared<XMSS_PrivateKey_Internal>(
+         m_xmss_params, m_wots_params, m_public_seed, key_bits)) {}
 
 XMSS_PrivateKey::XMSS_PrivateKey(
    XMSS_Parameters::xmss_algorithm_t xmss_algo_id,
    RandomNumberGenerator& rng)
-   : XMSS_PublicKey(xmss_algo_id, rng),
-     m_wots_priv_key(XMSS_PublicKey::m_xmss_params.ots_oid(),
-                     public_seed(),
-                     rng),
-     m_hash(xmss_hash_function()),
-     m_prf(rng.random_vec(XMSS_PublicKey::m_xmss_params.element_size())),
-     m_index_reg(XMSS_Index_Registry::get_instance())
+   : XMSS_PublicKey(xmss_algo_id, rng)
+   , m_private(std::make_shared<XMSS_PrivateKey_Internal>(
+      m_xmss_params, m_wots_params, m_public_seed,
+      rng.random_vec(m_xmss_params.element_size()),
+      rng.random_vec(m_xmss_params.element_size())))
    {
    XMSS_Address adrs;
-   set_root(tree_hash(0,
+   m_root = tree_hash(0,
                       XMSS_PublicKey::m_xmss_params.tree_height(),
-                      adrs));
+                      adrs);
    }
 
-
 XMSS_PrivateKey::XMSS_PrivateKey(XMSS_Parameters::xmss_algorithm_t xmss_algo_id,
                                  size_t idx_leaf,
                                  secure_vector<uint8_t> wots_priv_seed,
                                  secure_vector<uint8_t> prf,
                                  secure_vector<uint8_t> root,
                                  secure_vector<uint8_t> public_seed)
-   : XMSS_PublicKey(xmss_algo_id, std::move(root), public_seed /* copied on purpose */),
-     m_wots_priv_key(XMSS_PublicKey::m_xmss_params.ots_oid(),
-                     std::move(public_seed),
-                     std::move(wots_priv_seed)),
-     m_hash(XMSS_PublicKey::m_xmss_params.hash_function_name()),
-     m_prf(std::move(prf)),
-     m_index_reg(XMSS_Index_Registry::get_instance())
+   : XMSS_PublicKey(xmss_algo_id, std::move(root), std::move(public_seed))
+   , m_private(std::make_shared<XMSS_PrivateKey_Internal>(
+      m_xmss_params, m_wots_params, m_public_seed,
+      std::move(wots_priv_seed), std::move(prf)))
    {
-   set_unused_leaf_index(idx_leaf);
+   m_private->set_unused_leaf_index(idx_leaf);
    }
 
 secure_vector<uint8_t>
@@ -171,7 +274,7 @@ XMSS_PrivateKey::tree_hash(size_t start_idx,
        subtrees,
        secure_vector<uint8_t>(XMSS_PublicKey::m_xmss_params.element_size()));
    std::vector<XMSS_Address> node_addresses(subtrees, adrs);
-   std::vector<XMSS_Hash> xmss_hash(subtrees, m_hash);
+   std::vector<XMSS_Hash> xmss_hash(subtrees, m_private->hash());
    std::vector<std::future<void>> work;
 
    // Calculate multiple subtrees in parallel.
@@ -243,16 +346,25 @@ XMSS_PrivateKey::tree_hash(size_t start_idx,
                                         nodes[1],
                                         node_addresses[0],
                                         this->public_seed(),
-                                        m_hash,
+                                        m_private->hash(),
                                         m_xmss_params);
    return nodes[0];
 #else
    secure_vector<uint8_t> result;
-   tree_hash_subtree(result, start_idx, target_node_height, adrs, m_hash);
+   tree_hash_subtree(result, start_idx, target_node_height, adrs, m_private->hash());
    return result;
 #endif
    }
 
+
+void XMSS_PrivateKey::tree_hash_subtree(secure_vector<uint8_t>& result,
+                                        size_t start_idx,
+                                        size_t target_node_height,
+                                        XMSS_Address& adrs)
+   {
+   return tree_hash_subtree(result, start_idx, target_node_height, adrs, m_private->hash());
+   }
+
 void
 XMSS_PrivateKey::tree_hash_subtree(secure_vector<uint8_t>& result,
                                    size_t start_idx,
@@ -273,7 +385,7 @@ XMSS_PrivateKey::tree_hash_subtree(secure_vector<uint8_t>& result,
    std::vector<uint8_t> node_levels(target_node_height + 1);
 
    uint8_t level = 0; // current level on the node stack.
-   XMSS_WOTS_PublicKey pk(m_wots_priv_key.wots_parameters().oid(), seed);
+   XMSS_WOTS_PublicKey pk(m_private->wots_private_key().wots_parameters().oid(), seed);
    const size_t last_idx = (static_cast<size_t>(1) << target_node_height) + start_idx;
 
    for(size_t i = start_idx; i < last_idx; i++)
@@ -321,79 +433,40 @@ secure_vector<uint8_t> XMSS_PrivateKey::private_key_bits() const
    return DER_Encoder().encode(raw_private_key(), ASN1_Type::OctetString).get_contents();
    }
 
-std::shared_ptr<Atomic<size_t>>
-XMSS_PrivateKey::recover_global_leaf_index() const
+size_t XMSS_PrivateKey::reserve_unused_leaf_index()
    {
-   BOTAN_ASSERT(m_wots_priv_key.private_seed().size() ==
-                XMSS_PublicKey::m_xmss_params.element_size() &&
-                m_prf.size() == XMSS_PublicKey::m_xmss_params.element_size(),
-                "Trying to retrieve index for partially initialized key");
-   return m_index_reg.get(m_wots_priv_key.private_seed(), m_prf);
+   return m_private->reserve_unused_leaf_index();
    }
 
-void XMSS_PrivateKey::set_unused_leaf_index(size_t idx)
+size_t XMSS_PrivateKey::unused_leaf_index() const
    {
-   if(idx >= (1ull << XMSS_PublicKey::m_xmss_params.tree_height()))
-      {
-      throw Decoding_Error("XMSS private key leaf index out of bounds");
-      }
-   else
-      {
-      std::atomic<size_t>& index =
-         static_cast<std::atomic<size_t>&>(*recover_global_leaf_index());
-      size_t current = 0;
+   return m_private->unused_leaf_index();
+   }
 
-      do
-         {
-         current = index.load();
-         if(current > idx)
-            { return; }
-         }
-      while(!index.compare_exchange_strong(current, idx));
-      }
+size_t XMSS_PrivateKey::remaining_signatures() const
+   {
+   return m_private->remaining_signatures();
    }
 
-size_t XMSS_PrivateKey::reserve_unused_leaf_index()
+XMSS_WOTS_PrivateKey& XMSS_PrivateKey::wots_private_key()
    {
-   size_t idx = (static_cast<std::atomic<size_t>&>(
-                    *recover_global_leaf_index())).fetch_add(1);
-   if(idx >= (1ull << XMSS_PublicKey::m_xmss_params.tree_height()))
-      {
-      throw Decoding_Error("XMSS private key, one time signatures exhaused");
-      }
-   return idx;
+   return m_private->wots_private_key();
    }
 
-size_t XMSS_PrivateKey::unused_leaf_index() const
+const secure_vector<uint8_t>& XMSS_PrivateKey::prf_value() const
    {
-   return *recover_global_leaf_index();
+   return m_private->prf_value();
    }
 
 secure_vector<uint8_t> XMSS_PrivateKey::raw_private_key() const
    {
-   std::vector<uint8_t> pk { raw_public_key() };
-   secure_vector<uint8_t> result(pk.begin(), pk.end());
-   result.reserve(size());
-
-   for(int i = 3; i >= 0; i--)
-      {
-      result.push_back(
-         static_cast<uint8_t>(
-            static_cast<uint64_t>(unused_leaf_index()) >> 8 * i));
-      }
-
-   std::copy(m_prf.begin(), m_prf.end(), std::back_inserter(result));
-   std::copy(m_wots_priv_key.private_seed().begin(),
-             m_wots_priv_key.private_seed().end(),
-             std::back_inserter(result));
-
-   return result;
+   return m_private->serialize(raw_public_key());
    }
 
 std::unique_ptr<Public_Key> XMSS_PrivateKey::public_key() const
    {
    return std::unique_ptr<Public_Key>(
-      new XMSS_PublicKey(xmss_oid(), root(), public_seed()));
+      new XMSS_PublicKey(xmss_parameters().oid(), root(), public_seed()));
    }
 
 std::unique_ptr<PK_Ops::Signature>
diff --git a/src/lib/pubkey/xmss/xmss_publickey.cpp b/src/lib/pubkey/xmss/xmss_publickey.cpp
index 7b9ee69a2..28e11bd52 100644
--- a/src/lib/pubkey/xmss/xmss_publickey.cpp
+++ b/src/lib/pubkey/xmss/xmss_publickey.cpp
@@ -25,16 +25,17 @@ namespace Botan {
 namespace {
 
 // fall back to raw decoding for previous versions, which did not encode an OCTET STRING
-std::vector<uint8_t> extract_raw_key(const std::vector<uint8_t>& key_bits)
+std::vector<uint8_t> extract_raw_public_key(std::span<const uint8_t> key_bits)
    {
    std::vector<uint8_t> raw_key;
    try
       {
-      BER_Decoder(key_bits).decode(raw_key, ASN1_Type::OctetString);
+      DataSource_Memory src(key_bits);
+      BER_Decoder(src).decode(raw_key, ASN1_Type::OctetString);
       }
    catch(Decoding_Error&)
       {
-      raw_key = key_bits;
+      raw_key.assign(key_bits.begin(), key_bits.end());
       }
    return raw_key;
    }
@@ -48,12 +49,12 @@ XMSS_PublicKey::XMSS_PublicKey(XMSS_Parameters::xmss_algorithm_t xmss_oid,
      m_public_seed(rng.random_vec(m_xmss_params.element_size()))
    {}
 
-XMSS_PublicKey::XMSS_PublicKey(const std::vector<uint8_t>& key_bits)
-   : m_raw_key(extract_raw_key(key_bits)),
+XMSS_PublicKey::XMSS_PublicKey(std::span<const uint8_t> key_bits)
+   : m_raw_key(extract_raw_public_key(key_bits)),
      m_xmss_params(XMSS_PublicKey::deserialize_xmss_oid(m_raw_key)),
      m_wots_params(m_xmss_params.ots_oid())
    {
-   if(m_raw_key.size() < XMSS_PublicKey::size())
+   if(m_raw_key.size() < m_xmss_params.raw_public_key_size())
       {
       throw Decoding_Error("Invalid XMSS public key size detected");
       }
diff --git a/src/lib/pubkey/xmss/xmss_signature.cpp b/src/lib/pubkey/xmss/xmss_signature.cpp
index 98fadff35..52980ef8e 100644
--- a/src/lib/pubkey/xmss/xmss_signature.cpp
+++ b/src/lib/pubkey/xmss/xmss_signature.cpp
@@ -25,7 +25,7 @@ XMSS_Signature::XMSS_Signature(XMSS_Parameters::xmss_algorithm_t oid,
    for(size_t i = 0; i < 4; i++)
       { m_leaf_idx = ((m_leaf_idx << 8) | raw_sig[i]); }
 
-   if(m_leaf_idx >= (1ull << xmss_params.tree_height()))
+   if(m_leaf_idx >= xmss_params.total_number_of_signatures())
       {
       throw Decoding_Error("XMSS signature leaf index out of bounds.");
       }
diff --git a/src/lib/pubkey/xmss/xmss_signature.h b/src/lib/pubkey/xmss/xmss_signature.h
index 58c408464..355254c17 100644
--- a/src/lib/pubkey/xmss/xmss_signature.h
+++ b/src/lib/pubkey/xmss/xmss_signature.h
@@ -13,7 +13,7 @@
 #include <botan/types.h>
 #include <botan/secmem.h>
 #include <botan/xmss_parameters.h>
-#include <botan/xmss_wots.h>
+#include <botan/internal/xmss_wots.h>
 
 namespace Botan {
 
diff --git a/src/lib/pubkey/xmss/xmss_signature_operation.cpp b/src/lib/pubkey/xmss/xmss_signature_operation.cpp
index dd1768e6d..a2b56a93f 100644
--- a/src/lib/pubkey/xmss/xmss_signature_operation.cpp
+++ b/src/lib/pubkey/xmss/xmss_signature_operation.cpp
@@ -21,8 +21,7 @@ namespace Botan {
 XMSS_Signature_Operation::XMSS_Signature_Operation(
    const XMSS_PrivateKey& private_key) :
    m_priv_key(private_key),
-   m_xmss_params(private_key.xmss_oid()),
-   m_hash(private_key.xmss_hash_function()),
+   m_hash(private_key.xmss_parameters().hash_function_name()),
    m_randomness(0),
    m_leaf_idx(0),
    m_is_initialized(false)
@@ -55,20 +54,22 @@ XMSS_Signature_Operation::sign(const secure_vector<uint8_t>& msg_hash,
 
 size_t XMSS_Signature_Operation::signature_length() const
    {
+   const auto& params = m_priv_key.xmss_parameters();
    return sizeof(uint64_t) + // size of leaf index
-          m_xmss_params.element_size() +
-          m_xmss_params.len() * m_xmss_params.element_size() +
-          m_xmss_params.tree_height() * m_xmss_params.element_size();
+          params.element_size() +
+          params.len() * params.element_size() +
+          params.tree_height() * params.element_size();
    }
 
 wots_keysig_t
 XMSS_Signature_Operation::build_auth_path(XMSS_PrivateKey& priv_key,
       XMSS_Address& adrs)
    {
-   wots_keysig_t auth_path(m_xmss_params.tree_height());
+   const auto& params = m_priv_key.xmss_parameters();
+   wots_keysig_t auth_path(params.tree_height());
    adrs.set_type(XMSS_Address::Type::Hash_Tree_Address);
 
-   for(size_t j = 0; j < m_xmss_params.tree_height(); j++)
+   for(size_t j = 0; j < params.tree_height(); j++)
       {
       size_t k = (m_leaf_idx / (static_cast<size_t>(1) << j)) ^ 0x01;
       auth_path[j] = priv_key.tree_hash(k * (static_cast<size_t>(1) << j), j, adrs);
@@ -106,7 +107,7 @@ void XMSS_Signature_Operation::initialize()
 
    // write prefix for message hashing into buffer.
    XMSS_Tools::concat(index_bytes, m_leaf_idx, 32);
-   m_randomness = m_hash.prf(m_priv_key.prf(), index_bytes);
+   m_randomness = m_hash.prf(m_priv_key.prf_value(), index_bytes);
    index_bytes.clear();
    XMSS_Tools::concat(index_bytes, m_leaf_idx,
                       m_priv_key.xmss_parameters().element_size());
diff --git a/src/lib/pubkey/xmss/xmss_signature_operation.h b/src/lib/pubkey/xmss/xmss_signature_operation.h
index 5236b5bbe..03b0eac25 100644
--- a/src/lib/pubkey/xmss/xmss_signature_operation.h
+++ b/src/lib/pubkey/xmss/xmss_signature_operation.h
@@ -12,7 +12,7 @@
 #include <botan/xmss.h>
 #include <botan/internal/xmss_address.h>
 #include <botan/internal/xmss_signature.h>
-#include <botan/xmss_wots.h>
+#include <botan/internal/xmss_wots.h>
 
 namespace Botan {
 
@@ -76,7 +76,6 @@ class XMSS_Signature_Operation final : public virtual PK_Ops::Signature
       void initialize();
 
       XMSS_PrivateKey m_priv_key;
-      const XMSS_Parameters m_xmss_params;
       XMSS_Hash m_hash;
       secure_vector<uint8_t> m_randomness;
       uint32_t m_leaf_idx;
diff --git a/src/lib/pubkey/xmss/xmss_verification_operation.cpp b/src/lib/pubkey/xmss/xmss_verification_operation.cpp
index b9442aa03..23799d81e 100644
--- a/src/lib/pubkey/xmss/xmss_verification_operation.cpp
+++ b/src/lib/pubkey/xmss/xmss_verification_operation.cpp
@@ -18,7 +18,7 @@ namespace Botan {
 XMSS_Verification_Operation::XMSS_Verification_Operation(
    const XMSS_PublicKey& public_key) :
    m_pub_key(public_key),
-   m_hash(public_key.xmss_hash_function()),
+   m_hash(public_key.xmss_parameters().hash_function_name()),
    m_msg_buf(0)
    {
    }
@@ -35,7 +35,7 @@ XMSS_Verification_Operation::root_from_signature(const XMSS_Signature& sig,
    adrs.set_type(XMSS_Address::Type::OTS_Hash_Address);
    adrs.set_ots_address(next_index);
 
-   XMSS_WOTS_PublicKey pub_key_ots(m_pub_key.wots_parameters().oid(),
+   XMSS_WOTS_PublicKey pub_key_ots(m_pub_key.xmss_parameters().ots_oid(),
                                    msg,
                                    sig.tree().ots_signature(),
                                    adrs,
diff --git a/src/lib/pubkey/xmss/xmss_wots.h b/src/lib/pubkey/xmss/xmss_wots.h
index 5675b84aa..726d715a4 100644
--- a/src/lib/pubkey/xmss/xmss_wots.h
+++ b/src/lib/pubkey/xmss/xmss_wots.h
@@ -13,7 +13,8 @@
 #include <botan/pk_keys.h>
 #include <botan/rng.h>
 #include <botan/secmem.h>
-#include <botan/xmss_hash.h>
+#include <botan/internal/xmss_hash.h>
+#include <botan/xmss_parameters.h>
 #include <map>
 #include <memory>
 #include <string>
@@ -21,108 +22,6 @@
 
 namespace Botan {
 
-/**
- * Descibes a signature method for XMSS Winternitz One Time Signatures,
- * as defined in:
- * [1] XMSS: Extended Hash-Based Signatures,
- *     Request for Comments: 8391
- *     Release: May 2018.
- *     https://datatracker.ietf.org/doc/rfc8391/
- **/
-class BOTAN_PUBLIC_API(2,0) XMSS_WOTS_Parameters final
-   {
-   public:
-      enum ots_algorithm_t
-         {
-         WOTSP_SHA2_256 = 0x00000001,
-         WOTSP_SHA2_512 = 0x00000002,
-         WOTSP_SHAKE_256 = 0x00000003,
-         WOTSP_SHAKE_512 = 0x00000004
-         };
-
-      XMSS_WOTS_Parameters(const std::string& algo_name);
-      XMSS_WOTS_Parameters(ots_algorithm_t ots_spec);
-
-      static ots_algorithm_t xmss_wots_id_from_string(const std::string& param_set);
-
-      /**
-       * Algorithm 1: convert input string to base.
-       *
-       * @param msg Input string (referred to as X in [1]).
-       * @param out_size size of message in base w.
-       *
-       * @return Input string converted to the given base.
-       **/
-      secure_vector<uint8_t> base_w(const secure_vector<uint8_t>& msg, size_t out_size) const;
-
-      secure_vector<uint8_t> base_w(size_t value) const;
-
-      void append_checksum(secure_vector<uint8_t>& data) const;
-
-      /**
-       * @return XMSS WOTS registry name for the chosen parameter set.
-       **/
-      const std::string& name() const
-         {
-         return m_name;
-         }
-
-      /**
-       * @return Botan name for the hash function used.
-       **/
-      const std::string& hash_function_name() const
-         {
-         return m_hash_name;
-         }
-
-      /**
-       * Retrieves the uniform length of a message, and the size of
-       * each node. This correlates to XMSS parameter "n" defined
-       * in [1].
-       *
-       * @return element length in bytes.
-       **/
-      size_t element_size() const { return m_element_size; }
-
-      /**
-       * The Winternitz parameter.
-       *
-       * @return numeric base used for internal representation of
-       *         data.
-       **/
-      size_t wots_parameter() const { return m_w; }
-
-      size_t len() const { return m_len; }
-
-      size_t len_1() const { return m_len_1; }
-
-      size_t len_2() const { return m_len_2; }
-
-      size_t lg_w() const { return m_lg_w; }
-
-      ots_algorithm_t oid() const { return m_oid; }
-
-      size_t estimated_strength() const { return m_strength; }
-
-      bool operator==(const XMSS_WOTS_Parameters& p) const
-         {
-         return m_oid == p.m_oid;
-         }
-
-   private:
-      static const std::map<std::string, ots_algorithm_t> m_oid_name_lut;
-      ots_algorithm_t m_oid;
-      std::string m_name;
-      std::string m_hash_name;
-      size_t m_element_size;
-      size_t m_w;
-      size_t m_len_1;
-      size_t m_len_2;
-      size_t m_len;
-      size_t m_strength;
-      uint8_t m_lg_w;
-   };
-
 class XMSS_Address;
 
 typedef std::vector<secure_vector<uint8_t>> wots_keysig_t;
@@ -131,7 +30,7 @@ typedef std::vector<secure_vector<uint8_t>> wots_keysig_t;
  * A Winternitz One Time Signature public key for use with Extended Hash-Based
  * Signatures.
  **/
-class BOTAN_PUBLIC_API(2,0) XMSS_WOTS_PublicKey : virtual public Public_Key
+class XMSS_WOTS_PublicKey : virtual public Public_Key
    {
    public:
       class TreeSignature final
@@ -418,7 +317,7 @@ class BOTAN_PUBLIC_API(2,0) XMSS_WOTS_PublicKey : virtual public Public_Key
 /** A Winternitz One Time Signature private key for use with Extended Hash-Based
  * Signatures.
  **/
-class BOTAN_PUBLIC_API(2,0) XMSS_WOTS_PrivateKey final : public virtual XMSS_WOTS_PublicKey,
+class XMSS_WOTS_PrivateKey final : public virtual XMSS_WOTS_PublicKey,
    public virtual Private_Key
    {
    public:
diff --git a/src/lib/pubkey/xmss/xmss_wots_parameters.cpp b/src/lib/pubkey/xmss/xmss_wots_parameters.cpp
index 4601b5c4e..e8564e8ea 100644
--- a/src/lib/pubkey/xmss/xmss_wots_parameters.cpp
+++ b/src/lib/pubkey/xmss/xmss_wots_parameters.cpp
@@ -12,7 +12,7 @@
  * Botan is released under the Simplified BSD License (see license.txt)
  **/
 
-#include <botan/xmss_wots.h>
+#include <botan/internal/xmss_wots.h>
 #include <botan/internal/xmss_tools.h>
 #include <botan/exceptn.h>
 #include <cmath>
diff --git a/src/lib/pubkey/xmss/xmss_wots_privatekey.cpp b/src/lib/pubkey/xmss/xmss_wots_privatekey.cpp
index ace70d333..89484c0b4 100644
--- a/src/lib/pubkey/xmss/xmss_wots_privatekey.cpp
+++ b/src/lib/pubkey/xmss/xmss_wots_privatekey.cpp
@@ -8,7 +8,7 @@
  * Botan is released under the Simplified BSD License (see license.txt)
  **/
 
-#include <botan/xmss_wots.h>
+#include <botan/internal/xmss_wots.h>
 #include <botan/internal/xmss_tools.h>
 #include <botan/internal/xmss_address.h>
 
diff --git a/src/lib/pubkey/xmss/xmss_wots_publickey.cpp b/src/lib/pubkey/xmss/xmss_wots_publickey.cpp
index 2c50e1387..d5b30b4da 100644
--- a/src/lib/pubkey/xmss/xmss_wots_publickey.cpp
+++ b/src/lib/pubkey/xmss/xmss_wots_publickey.cpp
@@ -8,7 +8,7 @@
  * Botan is released under the Simplified BSD License (see license.txt)
  **/
 
-#include <botan/xmss_wots.h>
+#include <botan/internal/xmss_wots.h>
 #include <botan/internal/xmss_address.h>
 
 namespace Botan {
diff --git a/src/lib/tls/tls_signature_scheme.h b/src/lib/tls/tls_signature_scheme.h
index 0b7713d10..1b1fd9cdb 100644
--- a/src/lib/tls/tls_signature_scheme.h
+++ b/src/lib/tls/tls_signature_scheme.h
@@ -21,80 +21,80 @@ namespace Botan::TLS {
 class Protocol_Version;
 
 class BOTAN_PUBLIC_API(3,0) Signature_Scheme
-   {
-   public:
-      /**
-      * Matches with wire encoding
-      *
-      * Note that this is intentionally left as a bare enum. It emulates the Botan 2
-      * API where `Signature_Scheme` was an enum class with associated free-standing
-      * functions. Leaving it as a bare enum resembles the legacy user-facing API.
-      */
-      enum Code : uint16_t {
-         NONE = 0x0000,
-
-         RSA_PKCS1_SHA1   = 0x0201,  // not implemented
-         RSA_PKCS1_SHA256 = 0x0401,
-         RSA_PKCS1_SHA384 = 0x0501,
-         RSA_PKCS1_SHA512 = 0x0601,
-
-         ECDSA_SHA1   = 0x0203,  // not implemented
-         ECDSA_SHA256 = 0x0403,
-         ECDSA_SHA384 = 0x0503,
-         ECDSA_SHA512 = 0x0603,
-
-         RSA_PSS_SHA256 = 0x0804,
-         RSA_PSS_SHA384 = 0x0805,
-         RSA_PSS_SHA512 = 0x0806,
-
-         EDDSA_25519 = 0x0807,
-         EDDSA_448   = 0x0808,  // not implemented
-      };
-
-   public:
-      /**
-      * @return all available signature schemes
-      */
-      static const std::vector<Signature_Scheme>& all_available_schemes();
-
-      /**
-      * Construct an uninitialized / invalid scheme
-      */
-      Signature_Scheme();
-
-      Signature_Scheme(uint16_t wire_code);
-
-      Signature_Scheme(Signature_Scheme::Code wire_code);
-
-      Signature_Scheme::Code wire_code() const noexcept { return m_code; }
-
-      /**
-      * @return true if support for this scheme is implemented in this Botan build
-      */
-      bool is_available() const noexcept;
-
-      /**
-      * @return true if the wire_code is set to any value other than `NONE`
-      */
-      bool is_set() const noexcept;
-
-      std::string to_string() const noexcept;
-      std::string hash_function_name() const noexcept;
-      std::string padding_string() const noexcept;
-      std::string algorithm_name() const noexcept;
-      AlgorithmIdentifier key_algorithm_identifier() const noexcept;
-      AlgorithmIdentifier algorithm_identifier() const noexcept;
-      std::optional<Signature_Format> format() const noexcept;
-
-      bool is_compatible_with(const Protocol_Version& protocol_version) const noexcept;
-      bool is_suitable_for(const Private_Key& private_key) const noexcept;
-
-      bool operator==(const Signature_Scheme& rhs) const { return m_code == rhs.m_code; }
-      bool operator!=(const Signature_Scheme& rhs) const { return !(*this == rhs); }
-
-   private:
-      Signature_Scheme::Code m_code;
-   };
+{
+public:
+/**
+ * Matches with wire encoding
+ *
+ * Note that this is intentionally left as a bare enum. It emulates the Botan 2
+ * API where `Signature_Scheme` was an enum class with associated free-standing
+ * functions. Leaving it as a bare enum resembles the legacy user-facing API.
+ */
+enum Code : uint16_t {
+   NONE = 0x0000,
+
+   RSA_PKCS1_SHA1   = 0x0201,  // not implemented
+   RSA_PKCS1_SHA256 = 0x0401,
+   RSA_PKCS1_SHA384 = 0x0501,
+   RSA_PKCS1_SHA512 = 0x0601,
+
+   ECDSA_SHA1   = 0x0203,  // not implemented
+   ECDSA_SHA256 = 0x0403,
+   ECDSA_SHA384 = 0x0503,
+   ECDSA_SHA512 = 0x0603,
+
+   RSA_PSS_SHA256 = 0x0804,
+   RSA_PSS_SHA384 = 0x0805,
+   RSA_PSS_SHA512 = 0x0806,
+
+   EDDSA_25519 = 0x0807,
+   EDDSA_448   = 0x0808,  // not implemented
+};
+
+public:
+   /**
+    * @return all available signature schemes
+    */
+   static const std::vector<Signature_Scheme>& all_available_schemes();
+
+   /**
+    * Construct an uninitialized / invalid scheme
+    */
+   Signature_Scheme();
+
+   Signature_Scheme(uint16_t wire_code);
+
+   Signature_Scheme(Signature_Scheme::Code wire_code);
+
+   Signature_Scheme::Code wire_code() const noexcept { return m_code; }
+
+   /**
+    * @return true if support for this scheme is implemented in this Botan build
+    */
+   bool is_available() const noexcept;
+
+   /**
+    * @return true if the wire_code is set to any value other than `NONE`
+    */
+   bool is_set() const noexcept;
+
+   std::string to_string() const noexcept;
+   std::string hash_function_name() const noexcept;
+   std::string padding_string() const noexcept;
+   std::string algorithm_name() const noexcept;
+   AlgorithmIdentifier key_algorithm_identifier() const noexcept;
+   AlgorithmIdentifier algorithm_identifier() const noexcept;
+   std::optional<Signature_Format> format() const noexcept;
+
+   bool is_compatible_with(const Protocol_Version& protocol_version) const noexcept;
+   bool is_suitable_for(const Private_Key& private_key) const noexcept;
+
+   bool operator==(const Signature_Scheme& rhs) const { return m_code == rhs.m_code; }
+   bool operator!=(const Signature_Scheme& rhs) const { return !(*this == rhs); }
+
+private:
+   Signature_Scheme::Code m_code;
+};
 
 std::vector<AlgorithmIdentifier> to_algorithm_identifiers(const std::vector<Signature_Scheme>& schemes);
 
diff --git a/src/lib/x509/ocsp.cpp b/src/lib/x509/ocsp.cpp
index ec30c2611..7441f69f2 100644
--- a/src/lib/x509/ocsp.cpp
+++ b/src/lib/x509/ocsp.cpp
@@ -168,7 +168,7 @@ Certificate_Status_Code Response::verify_signature(const X509_Certificate& issue
       std::unique_ptr<Public_Key> pub_key(issuer.subject_public_key());
 
       const std::vector<std::string> sig_info =
-         split_on(m_sig_algo.oid().to_formatted_string(), '/');
+         split_on(m_sig_algo.get_oid().to_formatted_string(), '/');
 
       if(sig_info.size() != 2 || sig_info[0] != pub_key->algo_name())
          return Certificate_Status_Code::OCSP_RESPONSE_INVALID;
diff --git a/src/lib/x509/ocsp_types.cpp b/src/lib/x509/ocsp_types.cpp
index b057f4ed2..a5c12c20f 100644
--- a/src/lib/x509/ocsp_types.cpp
+++ b/src/lib/x509/ocsp_types.cpp
@@ -36,7 +36,7 @@ bool CertID::is_id_for(const X509_Certificate& issuer,
       if(BigInt::decode(subject.serial_number()) != m_subject_serial)
          return false;
 
-      const std::string hash_algo = m_hash_id.oid().to_formatted_string();
+      const std::string hash_algo = m_hash_id.get_oid().to_formatted_string();
       std::unique_ptr<HashFunction> hash = HashFunction::create_or_throw(hash_algo);
 
       if(m_issuer_dn_hash != unlock(hash->process(subject.raw_issuer_dn())))
diff --git a/src/lib/x509/pkcs10.cpp b/src/lib/x509/pkcs10.cpp
index 1043b320b..cdaf50be1 100644
--- a/src/lib/x509/pkcs10.cpp
+++ b/src/lib/x509/pkcs10.cpp
@@ -135,7 +135,7 @@ std::unique_ptr<PKCS10_Data> decode_pkcs10(const std::vector<uint8_t>& body)
          Attribute attr;
          attributes.decode(attr);
 
-         const OID& oid = attr.object_identifier();
+         const OID& oid = attr.get_oid();
          BER_Decoder value(attr.get_parameters());
 
          if(oid == OID::from_string("PKCS9.EmailAddress"))
@@ -266,7 +266,7 @@ std::vector<OID> PKCS10_Request::ex_constraints() const
    {
    if(auto ext = extensions().get(OID::from_string("X509v3.ExtendedKeyUsage")))
       {
-      return dynamic_cast<Cert_Extension::Extended_Key_Usage&>(*ext).object_identifiers();
+      return dynamic_cast<Cert_Extension::Extended_Key_Usage&>(*ext).get_oids();
       }
 
    return {};
diff --git a/src/lib/x509/pkix_types.h b/src/lib/x509/pkix_types.h
index d99e55d41..7f4215e02 100644
--- a/src/lib/x509/pkix_types.h
+++ b/src/lib/x509/pkix_types.h
@@ -171,7 +171,7 @@ class BOTAN_PUBLIC_API(2,0) Attribute final : public ASN1_Object
       const OID& oid() const { return m_oid; }
       const std::vector<uint8_t>& parameters() const { return m_parameters; }
 
-      const OID& object_identifier() const { return m_oid; }
+      const OID& get_oid() const { return m_oid; }
       const std::vector<uint8_t>& get_parameters() const { return m_parameters; }
 
    private:
diff --git a/src/lib/x509/x509_ext.h b/src/lib/x509/x509_ext.h
index 3c5e25f05..e7218e443 100644
--- a/src/lib/x509/x509_ext.h
+++ b/src/lib/x509/x509_ext.h
@@ -206,7 +206,7 @@ class BOTAN_PUBLIC_API(2,0) Extended_Key_Usage final : public Certificate_Extens
       Extended_Key_Usage() = default;
       explicit Extended_Key_Usage(const std::vector<OID>& o) : m_oids(o) {}
 
-      const std::vector<OID>& object_identifiers() const { return m_oids; }
+      const std::vector<OID>& get_oids() const { return m_oids; }
 
       static OID static_oid() { return OID("2.5.29.37"); }
       OID oid_of() const override { return static_oid(); }
diff --git a/src/lib/x509/x509_obj.cpp b/src/lib/x509/x509_obj.cpp
index a5ffdc7bc..fe785be59 100644
--- a/src/lib/x509/x509_obj.cpp
+++ b/src/lib/x509/x509_obj.cpp
@@ -40,7 +40,7 @@ Pss_params decode_pss_params(const std::vector<uint8_t>& encoded_pss_params)
          .decode_optional(pss_parameter.trailer_field, ASN1_Type(3), ASN1_Class::ExplicitContextSpecific, size_t(1))
       .end_cons();
 
-   BER_Decoder(pss_parameter.mask_gen_algo.parameters()).decode(pss_parameter.mask_gen_hash);
+   BER_Decoder(pss_parameter.mask_gen_algo.get_parameters()).decode(pss_parameter.mask_gen_hash);
 
    return pss_parameter;
    }
@@ -137,7 +137,7 @@ std::vector<uint8_t> X509_Object::tbs_data() const
 */
 std::string X509_Object::hash_used_for_signature() const
    {
-   const OID& oid = m_sig_algo.oid();
+   const OID& oid = m_sig_algo.get_oid();
    const std::vector<std::string> sig_info = split_on(oid.to_formatted_string(), '/');
 
    if(sig_info.size() == 1)
@@ -152,7 +152,7 @@ std::string X509_Object::hash_used_for_signature() const
 
    if(sig_info[1] == "EMSA4")
       {
-      const OID hash_oid = decode_pss_params(signature_algorithm().parameters()).hash_algo.oid();
+      const OID hash_oid = decode_pss_params(signature_algorithm().get_parameters()).hash_algo.get_oid();
       return hash_oid.to_formatted_string();
       }
    else
@@ -181,7 +181,7 @@ bool X509_Object::check_signature(const Public_Key& pub_key) const
 Certificate_Status_Code X509_Object::verify_signature(const Public_Key& pub_key) const
    {
    const std::vector<std::string> sig_info =
-      split_on(m_sig_algo.oid().to_formatted_string(), '/');
+      split_on(m_sig_algo.get_oid().to_formatted_string(), '/');
 
    if(sig_info.empty() || sig_info.size() > 2 || sig_info[0] != pub_key.algo_name())
       return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
@@ -200,15 +200,15 @@ Certificate_Status_Code X509_Object::verify_signature(const Public_Key& pub_key)
    if(padding == "EMSA4")
       {
       // "MUST contain RSASSA-PSS-params"
-      if(signature_algorithm().parameters().empty())
+      if(signature_algorithm().get_parameters().empty())
          {
          return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
          }
 
-      Pss_params pss_parameter = decode_pss_params(signature_algorithm().parameters());
+      Pss_params pss_parameter = decode_pss_params(signature_algorithm().get_parameters());
 
       // hash_algo must be SHA1, SHA2-224, SHA2-256, SHA2-384 or SHA2-512
-      const std::string hash_algo = pss_parameter.hash_algo.oid().to_formatted_string();
+      const std::string hash_algo = pss_parameter.hash_algo.get_oid().to_formatted_string();
       if(hash_algo != "SHA-1" &&
          hash_algo != "SHA-224" &&
          hash_algo != "SHA-256" &&
@@ -218,7 +218,7 @@ Certificate_Status_Code X509_Object::verify_signature(const Public_Key& pub_key)
          return Certificate_Status_Code::UNTRUSTED_HASH;
          }
 
-      const std::string mgf_algo = pss_parameter.mask_gen_algo.oid().to_formatted_string();
+      const std::string mgf_algo = pss_parameter.mask_gen_algo.get_oid().to_formatted_string();
       if(mgf_algo != "MGF1")
          {
          return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
@@ -226,7 +226,7 @@ Certificate_Status_Code X509_Object::verify_signature(const Public_Key& pub_key)
 
       // For MGF1, it is strongly RECOMMENDED that the underlying hash function be the same as the one identified by hashAlgorithm
       // Must be SHA1, SHA2-224, SHA2-256, SHA2-384 or SHA2-512
-      if(pss_parameter.mask_gen_hash.oid() != pss_parameter.hash_algo.oid())
+      if(pss_parameter.mask_gen_hash.get_oid() != pss_parameter.hash_algo.get_oid())
          {
          return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
          }
diff --git a/src/lib/x509/x509cert.cpp b/src/lib/x509/x509cert.cpp
index 417aaaac2..aec2e897a 100644
--- a/src/lib/x509/x509cert.cpp
+++ b/src/lib/x509/x509cert.cpp
@@ -151,7 +151,7 @@ std::unique_ptr<X509_Certificate_Data> parse_x509_cert_body(const X509_Object& o
    BER_Decoder(public_key).decode(public_key_alg_id).discard_remaining();
 
    const std::vector<std::string> public_key_info =
-      split_on(OIDS::oid2str_or_empty(public_key_alg_id.oid()), '/');
+      split_on(OIDS::oid2str_or_empty(public_key_alg_id.get_oid()), '/');
 
    if(!public_key_info.empty() && public_key_info[0] == "RSA")
       {
@@ -183,7 +183,7 @@ std::unique_ptr<X509_Certificate_Data> parse_x509_cert_body(const X509_Object& o
       else
          {
          // oid = rsaEncryption -> parameters field MUST contain NULL
-         if(public_key_alg_id != AlgorithmIdentifier(public_key_alg_id.oid(), AlgorithmIdentifier::USE_NULL_PARAM))
+         if(public_key_alg_id != AlgorithmIdentifier(public_key_alg_id.get_oid(), AlgorithmIdentifier::USE_NULL_PARAM))
             {
             throw Decoding_Error("RSA algorithm parameters field MUST contain NULL");
             }
@@ -269,7 +269,7 @@ std::unique_ptr<X509_Certificate_Data> parse_x509_cert_body(const X509_Object& o
 
    if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Extended_Key_Usage>())
       {
-      data->m_extended_key_usage = ext->object_identifiers();
+      data->m_extended_key_usage = ext->get_oids();
       }
 
    if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Certificate_Policies>())
@@ -858,7 +858,7 @@ std::string X509_Certificate::to_string() const
    if(!crl_distribution_point().empty())
       out << "CRL " << crl_distribution_point() << "\n";
 
-   out << "Signature algorithm: " << this->signature_algorithm().oid().to_formatted_string() << "\n";
+   out << "Signature algorithm: " << this->signature_algorithm().get_oid().to_formatted_string() << "\n";
 
    out << "Serial number: " << hex_encode(this->serial_number()) << "\n";
 
@@ -877,7 +877,7 @@ std::string X509_Certificate::to_string() const
    catch(Decoding_Error&)
       {
       const AlgorithmIdentifier& alg_id = this->subject_public_key_algo();
-      out << "Failed to decode key with oid " << alg_id.oid().to_string() << "\n";
+      out << "Failed to decode key with oid " << alg_id.get_oid().to_string() << "\n";
       }
 
    return out.str();
diff --git a/src/lib/x509/x509path.cpp b/src/lib/x509/x509path.cpp
index db71ea799..db4dc78c3 100644
--- a/src/lib/x509/x509path.cpp
+++ b/src/lib/x509/x509path.cpp
@@ -120,7 +120,7 @@ PKIX::check_chain(const std::vector<X509_Certificate>& cert_path,
       std::unique_ptr<Public_Key> issuer_key(issuer.subject_public_key());
 
       // Check the signature algorithm is known
-      if(OIDS::oid2str_or_empty(subject.signature_algorithm().oid()).empty())
+      if(OIDS::oid2str_or_empty(subject.signature_algorithm().get_oid()).empty())
          {
          status.insert(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN);
          }
diff --git a/src/lib/x509/x509self.cpp b/src/lib/x509/x509self.cpp
index 54fffda6e..0f6ae459e 100644
--- a/src/lib/x509/x509self.cpp
+++ b/src/lib/x509/x509self.cpp
@@ -59,7 +59,7 @@ X509_Certificate create_self_signed_cert(const X509_Cert_Options& opts,
 
    const std::vector<uint8_t> pub_key = X509::BER_encode(key);
    auto signer = X509_Object::choose_sig_format(sig_algo, key, rng, hash_fn, opts.padding_scheme);
-   BOTAN_ASSERT_NOMSG(sig_algo.oid().has_value());
+   BOTAN_ASSERT_NOMSG(sig_algo.get_oid().has_value());
    load_info(opts, subject_dn, subject_alt);
 
    Extensions extensions = opts.extensions;


Test patch:
diff --git a/src/scripts/test_cli.py b/src/scripts/test_cli.py
index fd54dc017..a3cadab93 100755
--- a/src/scripts/test_cli.py
+++ b/src/scripts/test_cli.py
@@ -405,27 +405,27 @@ def cli_xmss_sign_tests(tmp_dir):
     test_cli("hash", ["--no-fsname", msg], "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855")
 
     test_cli("keygen", ["--algo=XMSS", "--output=%s" % (priv_key)], "")
-    test_cli("hash", ["--no-fsname", priv_key], "5B38F737BA41BE7F40433DB30EAEF7C41ABB0F7D9E7A09DEB5FDCE7B6811693F")
+    test_cli("hash", ["--no-fsname", priv_key], "8E239EC9CB28F58174F0C443D9884F921575B46A870FB277197758D120DA1FA1")
 
     test_cli("pkcs8", "--pub-out --output=%s %s" % (pub_key, priv_key), "")
     test_cli("fingerprint", ['--no-fsname', pub_key],
-             "B0:F4:98:6E:D8:4E:05:63:A1:D8:4B:37:61:5A:A0:41:78:7E:DE:0E:72:46:E0:A8:D6:CF:09:54:08:DA:A4:22")
+             "1E:10:69:84:37:63:4F:66:84:34:E2:4B:2B:A1:7A:3C:92:0F:B3:BA:D1:5F:CA:A8:66:47:89:90:7E:91:D1:6A")
 
     # verify the key is updated after each signature:
     test_cli("sign", [priv_key, msg, "--output=%s" % (sig1)], "")
     test_cli("verify", [pub_key, msg, sig1], "Signature is valid")
-    test_cli("hash", ["--no-fsname", sig1], "04AF45451C7A9AF2D828E1AD6EC262E012436F4087C5DA6F32C689D781E597D0")
-    test_cli("hash", ["--no-fsname", priv_key], "67929FAEC636E43DE828C1CD7E2D11CE7C3388CE90DD0A0F687C6627FFA850CD")
+    test_cli("hash", ["--no-fsname", sig1], "1506DD8AB43C714CD8EA20AA599B77D916A2FB554AD5B19A68DFCF00847F6001")
+    test_cli("hash", ["--no-fsname", priv_key], "B76BCED7BD52D0B314373589E7AA638B331740CD24F686ACEBAD0BA7D5384048")
 
     test_cli("sign", [priv_key, msg, "--output=%s" % (sig2)], "")
     test_cli("verify", [pub_key, msg, sig2], "Signature is valid")
-    test_cli("hash", ["--no-fsname", sig2], "0785A6AD54CC7D01F2BE2BC6463A3EAA1159792E52210ED754992C5068E8F24F")
-    test_cli("hash", ["--no-fsname", priv_key], "1940945D68B1CF54D79E05DD7913A4D0B4959183F1E12B81A4E43EF4E63FBD20")
+    test_cli("hash", ["--no-fsname", sig2], "E3FCB5D2AB12159A99E7619C933A97808077C67CCEC8A2504EFC8720465A68E7")
+    test_cli("hash", ["--no-fsname", priv_key], "E2EFFE9164ED6F0CBF39C30CC3996BC0B596B9C987C0FE01113FB7DC15F1E5ED")
 
     # private key updates, public key is unchanged:
     test_cli("pkcs8", "--pub-out --output=%s %s" % (pub_key2, priv_key), "")
     test_cli("fingerprint", ['--no-fsname', pub_key2],
-             "B0:F4:98:6E:D8:4E:05:63:A1:D8:4B:37:61:5A:A0:41:78:7E:DE:0E:72:46:E0:A8:D6:CF:09:54:08:DA:A4:22")
+             "1E:10:69:84:37:63:4F:66:84:34:E2:4B:2B:A1:7A:3C:92:0F:B3:BA:D1:5F:CA:A8:66:47:89:90:7E:91:D1:6A")
 
 def cli_pbkdf_tune_tests(_tmp_dir):
     if not check_for_command("pbkdf_tune"):
diff --git a/src/tests/test_xmss.cpp b/src/tests/test_xmss.cpp
index b1d112ca5..89e7434ae 100644
--- a/src/tests/test_xmss.cpp
+++ b/src/tests/test_xmss.cpp
@@ -116,10 +116,53 @@ class XMSS_Keygen_Tests final : public PK_Key_Generation_Test
          }
    };
 
+std::vector<Test::Result> xmss_statefulness()
+   {
+   auto sign_something = [](auto& sk)
+      {
+      auto msg = Botan::hex_decode("deadbeef");
+
+      Botan::PK_Signer signer(sk, Test::rng(), "SHA2_10_256");
+      signer.sign_message(msg, Test::rng());
+      };
+
+   return
+      {
+      CHECK("signing alters state", [&](auto& result)
+         {
+         Botan::XMSS_PrivateKey sk(Botan::XMSS_Parameters::XMSS_SHA2_10_256, Test::rng());
+         result.require("allows 1024 signatures", sk.remaining_signatures() == 1024);
+
+         sign_something(sk);
+
+         result.require("allows 1023 signatures", sk.remaining_signatures() == 1023);
+         }),
+
+      CHECK("state can become exhausted", [&](auto& result)
+         {
+         const auto skbytes = Botan::hex_decode(
+            "000000011BBB81273E8057724A2A894593A1A688B3271410B3BEAB9F5587337BCDCBBF5C4E43AB"
+            "0AB2F88258E5AC54BB252E39335AE9B0D4AF0C0347EA45B8AA0AA3804C000003FFAC0C29C1ACD3"
+            //                                                         ~~1023~~
+            "19DA96E9C8EE4E28C2078441A76B6BB8BAFD358F67FBCBFC559B55C37C01FFADBB118099759EEB"
+            "A3B07643F73BCB4AAC546E244B57782D6BEABC"
+         );
+         Botan::XMSS_PrivateKey sk(skbytes);
+         result.require("allow one last signature", sk.remaining_signatures() == 1);
+
+         sign_something(sk);
+
+         result.require("allow no more signatures", sk.remaining_signatures() == 0);
+         result.test_throws("no more signing", [&] { sign_something(sk); });
+         })
+      };
+   }
+
 BOTAN_REGISTER_TEST("pubkey", "xmss_sign", XMSS_Signature_Tests);
 BOTAN_REGISTER_TEST("pubkey", "xmss_verify", XMSS_Signature_Verify_Tests);
 BOTAN_REGISTER_TEST("pubkey", "xmss_verify_invalid", XMSS_Signature_Verify_Invalid_Tests);
 BOTAN_REGISTER_TEST("pubkey", "xmss_keygen", XMSS_Keygen_Tests);
+BOTAN_REGISTER_TEST_FN("pubkey", "xmss_statefulness", xmss_statefulness);
 
 #endif
 
diff --git a/src/tests/unit_ecdsa.cpp b/src/tests/unit_ecdsa.cpp
index 4875a4c34..775ac6467 100644
--- a/src/tests/unit_ecdsa.cpp
+++ b/src/tests/unit_ecdsa.cpp
@@ -82,7 +82,7 @@ Test::Result test_decode_ecdsa_X509()
    Test::Result result("ECDSA Unit");
    Botan::X509_Certificate cert(Test::data_file("x509/ecc/CSCA.CSCA.csca-germany.1.crt"));
 
-   result.test_eq("correct signature oid", cert.signature_algorithm().oid().to_formatted_string(), "ECDSA/EMSA1(SHA-224)");
+   result.test_eq("correct signature oid", cert.signature_algorithm().get_oid().to_formatted_string(), "ECDSA/EMSA1(SHA-224)");
 
    result.test_eq("serial number", cert.serial_number(), Botan::hex_decode("01"));
    result.test_eq("authority key id", cert.authority_key_id(), cert.subject_key_id());
diff --git a/src/tests/unit_x509.cpp b/src/tests/unit_x509.cpp
index c9ea8f087..db65a437b 100644
--- a/src/tests/unit_x509.cpp
+++ b/src/tests/unit_x509.cpp
@@ -487,7 +487,7 @@ Test::Result test_rsa_oaep()
    result.test_not_null("Decoding RSA-OAEP worked", public_key.get());
    const auto& pk_info = cert.subject_public_key_algo();
 
-   result.test_eq("RSA-OAEP OID", pk_info.oid().to_string(), Botan::OID::from_string("RSA/OAEP").to_string());
+   result.test_eq("RSA-OAEP OID", pk_info.get_oid().to_string(), Botan::OID::from_string("RSA/OAEP").to_string());
 #endif
 
    return result;
@@ -686,13 +686,13 @@ Test::Result test_verify_gost2012_cert()
 
    Botan::X509_Certificate ca_cert_def = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", Test::rng());
    test_result.test_eq("CA certificate signature algorithm (default)",
-      Botan::OIDS::oid2str_or_throw(ca_cert_def.signature_algorithm().oid()),"RSA/EMSA3(SHA-512)");
+      Botan::OIDS::oid2str_or_throw(ca_cert_def.signature_algorithm().get_oid()),"RSA/EMSA3(SHA-512)");
 
    // Create X509 CA certificate; RSA-PSS is explicitly set
    opt.set_padding_scheme("PSSR");
    Botan::X509_Certificate ca_cert_exp = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", Test::rng());
    test_result.test_eq("CA certificate signature algorithm (explicit)",
-      Botan::OIDS::oid2str_or_throw(ca_cert_exp.signature_algorithm().oid()),"RSA/EMSA4");
+      Botan::OIDS::oid2str_or_throw(ca_cert_exp.signature_algorithm().get_oid()),"RSA/EMSA4");
 
 #if defined(BOTAN_HAS_EMSA1)
 
@@ -712,7 +712,7 @@ Test::Result test_verify_gost2012_cert()
 #endif
 
    test_result.test_eq("CA certificate signature algorithm (explicit)",
-      Botan::OIDS::oid2str_or_throw(ca_cert_exp.signature_algorithm().oid()),"RSA/EMSA4");
+      Botan::OIDS::oid2str_or_throw(ca_cert_exp.signature_algorithm().get_oid()),"RSA/EMSA4");
 
    const Botan::X509_Time not_before = from_date(-1, 1, 1);
    const Botan::X509_Time not_after = from_date(2, 1, 2);
@@ -721,7 +721,7 @@ Test::Result test_verify_gost2012_cert()
    Botan::X509_Cert_Options req_opt("endpoint");
    req_opt.set_padding_scheme("EMSA4(SHA-512,MGF1,64)");
    Botan::PKCS10_Request end_req = Botan::X509::create_cert_req(req_opt, (*sk), "SHA-512", Test::rng());
-   test_result.test_eq("Certificate request signature algorithm", Botan::OIDS::oid2str_or_throw(end_req.signature_algorithm().oid()),"RSA/EMSA4");
+   test_result.test_eq("Certificate request signature algorithm", Botan::OIDS::oid2str_or_throw(end_req.signature_algorithm().get_oid()),"RSA/EMSA4");
 
    // Create X509 CA object: will fail as the chosen hash functions differ
    try
@@ -739,21 +739,21 @@ Test::Result test_verify_gost2012_cert()
    // Create X509 CA object: its signer will use the padding scheme from the CA certificate, i.e. EMSA3
    Botan::X509_CA ca_def(ca_cert_def, (*sk), "SHA-512", Test::rng());
    Botan::X509_Certificate end_cert_emsa3 = ca_def.sign_request(end_req, Test::rng(), not_before, not_after);
-   test_result.test_eq("End certificate signature algorithm", Botan::OIDS::oid2str_or_throw(end_cert_emsa3.signature_algorithm().oid()), "RSA/EMSA3(SHA-512)");
+   test_result.test_eq("End certificate signature algorithm", Botan::OIDS::oid2str_or_throw(end_cert_emsa3.signature_algorithm().get_oid()), "RSA/EMSA3(SHA-512)");
 
    // Create X509 CA object: its signer will use the explicitly configured padding scheme, which is different from the CA certificate's scheme
    Botan::X509_CA ca_diff(ca_cert_def, (*sk), "SHA-512", "EMSA-PSS", Test::rng());
    Botan::X509_Certificate end_cert_diff_emsa4 = ca_diff.sign_request(end_req, Test::rng(), not_before, not_after);
-   test_result.test_eq("End certificate signature algorithm", Botan::OIDS::oid2str_or_throw(end_cert_diff_emsa4.signature_algorithm().oid()), "RSA/EMSA4");
+   test_result.test_eq("End certificate signature algorithm", Botan::OIDS::oid2str_or_throw(end_cert_diff_emsa4.signature_algorithm().get_oid()), "RSA/EMSA4");
 
    // Create X509 CA object: its signer will use the explicitly configured padding scheme, which is identical to the CA certificate's scheme
    Botan::X509_CA ca_exp(ca_cert_exp, (*sk), "SHA-512", "EMSA4(SHA-512,MGF1,64)", Test::rng());
    Botan::X509_Certificate end_cert_emsa4= ca_exp.sign_request(end_req, Test::rng(), not_before, not_after);
-   test_result.test_eq("End certificate signature algorithm", Botan::OIDS::oid2str_or_throw(end_cert_emsa4.signature_algorithm().oid()), "RSA/EMSA4");
+   test_result.test_eq("End certificate signature algorithm", Botan::OIDS::oid2str_or_throw(end_cert_emsa4.signature_algorithm().get_oid()), "RSA/EMSA4");
 
    // Check CRL signature algorithm
    Botan::X509_CRL crl = ca_exp.new_crl(Test::rng());
-   test_result.test_eq("CRL signature algorithm", Botan::OIDS::oid2str_or_throw(crl.signature_algorithm().oid()), "RSA/EMSA4");
+   test_result.test_eq("CRL signature algorithm", Botan::OIDS::oid2str_or_throw(crl.signature_algorithm().get_oid()), "RSA/EMSA4");
 
    // sanity check for verification, the heavy lifting is done in the other unit tests
    const Botan::Certificate_Store_In_Memory trusted(ca_exp.ca_certificate());