Repository: randombit/botan
Pull request #3760 (2023-10-15 15:10:12)

Move const time memory comparison utils to ct_utils.h

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

coverage: 91.7% (+0.006%) from 91.694% when pulling **64c290322ad2bfde4748ef8f42af960851d26f90 on jack/ct-refactor** into **73088f7b2ffdbf068e3022746b1437a9fab76a42 on master**.


Patch:
diff --git a/src/cli/zfec.cpp b/src/cli/zfec.cpp
index 1d5ed8eb3..7f1653f1b 100644
--- a/src/cli/zfec.cpp
+++ b/src/cli/zfec.cpp
@@ -8,6 +8,7 @@
 
 #if defined(BOTAN_HAS_ZFEC) && defined(BOTAN_HAS_SHA2_64)
    #include <botan/hash.h>
+   #include <botan/mem_ops.h>
    #include <botan/zfec.h>
    #include <botan/internal/loadstor.h>
    #include <fstream>
@@ -70,7 +71,7 @@ class FEC_Share final {
          hash.update(bits, len - hash_len);
          auto share_hash = hash.final();
 
-         const bool digest_ok = Botan::same_mem(share_hash.data(), &bits[len - hash_len], hash_len);
+         const bool digest_ok = Botan::constant_time_compare(share_hash.data(), &bits[len - hash_len], hash_len);
 
          if(!digest_ok) {
             throw CLI_Error("FEC share has invalid hash");
@@ -251,7 +252,8 @@ class FEC_Decode final : public Command {
 
          auto decoded_digest = hash->process(decoded.data(), decoded.size() - (hash_len + padding));
 
-         if(!Botan::same_mem(decoded_digest.data(), &decoded[decoded.size() - (hash_len + padding)], hash_len)) {
+         if(!Botan::constant_time_compare(
+               decoded_digest.data(), &decoded[decoded.size() - (hash_len + padding)], hash_len)) {
             throw CLI_Error("Recovered data failed digest check");
          }
 
diff --git a/src/lib/asn1/asn1_str.cpp b/src/lib/asn1/asn1_str.cpp
index 62cf43dc9..1c6c827d2 100644
--- a/src/lib/asn1/asn1_str.cpp
+++ b/src/lib/asn1/asn1_str.cpp
@@ -37,7 +37,7 @@ ASN1_Type choose_encoding(std::string_view str) {
       all_printable &= is_printable;
    }
 
-   if(all_printable.is_set()) {
+   if(all_printable.as_bool()) {
       return ASN1_Type::PrintableString;
    } else {
       return ASN1_Type::Utf8String;
diff --git a/src/lib/compat/sodium/sodium_secretbox.cpp b/src/lib/compat/sodium/sodium_secretbox.cpp
index 9e06246fd..a44092711 100644
--- a/src/lib/compat/sodium/sodium_secretbox.cpp
+++ b/src/lib/compat/sodium/sodium_secretbox.cpp
@@ -9,6 +9,7 @@
 #include <botan/mac.h>
 #include <botan/secmem.h>
 #include <botan/stream_cipher.h>
+#include <botan/internal/ct_utils.h>
 
 namespace Botan {
 
@@ -54,7 +55,7 @@ int Sodium::crypto_secretbox_xsalsa20poly1305_open(
    poly1305->update(ctext + 32, ctext_len - 32);
    secure_vector<uint8_t> computed = poly1305->final();
 
-   if(!constant_time_compare(computed.data(), ctext + 16, 16)) {
+   if(CT::is_not_equal(computed.data(), ctext + 16, 16).as_bool()) {
       return -1;
    }
 
@@ -105,7 +106,7 @@ int Sodium::crypto_secretbox_open_detached(uint8_t ptext[],
    poly1305->update(ctext, ctext_len);
    secure_vector<uint8_t> computed_mac = poly1305->final();
 
-   if(!constant_time_compare(mac, computed_mac.data(), computed_mac.size())) {
+   if(!CT::is_equal(mac, computed_mac.data(), computed_mac.size()).as_bool()) {
       return -1;
    }
 
diff --git a/src/lib/compat/sodium/sodium_utils.cpp b/src/lib/compat/sodium/sodium_utils.cpp
index b6db38097..30ba1cdb9 100644
--- a/src/lib/compat/sodium/sodium_utils.cpp
+++ b/src/lib/compat/sodium/sodium_utils.cpp
@@ -41,15 +41,15 @@ void Sodium::randombytes_buf_deterministic(void* buf, size_t size, const uint8_t
 }
 
 int Sodium::crypto_verify_16(const uint8_t x[16], const uint8_t y[16]) {
-   return same_mem(x, y, 16);
+   return static_cast<int>(CT::is_equal(x, y, 16).select(1, 0));
 }
 
 int Sodium::crypto_verify_32(const uint8_t x[32], const uint8_t y[32]) {
-   return same_mem(x, y, 32);
+   return static_cast<int>(CT::is_equal(x, y, 32).select(1, 0));
 }
 
 int Sodium::crypto_verify_64(const uint8_t x[64], const uint8_t y[64]) {
-   return same_mem(x, y, 64);
+   return static_cast<int>(CT::is_equal(x, y, 64).select(1, 0));
 }
 
 void Sodium::sodium_memzero(void* ptr, size_t len) {
@@ -57,8 +57,9 @@ void Sodium::sodium_memzero(void* ptr, size_t len) {
 }
 
 int Sodium::sodium_memcmp(const void* x, const void* y, size_t len) {
-   const bool same = constant_time_compare(static_cast<const uint8_t*>(x), static_cast<const uint8_t*>(y), len);
-   return same ? 0 : -1;
+   const auto same = CT::is_equal(static_cast<const uint8_t*>(x), static_cast<const uint8_t*>(y), len);
+   // Return 0 if same or -1 if differing
+   return static_cast<int>(same.select(1, 0)) - 1;
 }
 
 int Sodium::sodium_compare(const uint8_t x[], const uint8_t y[], size_t len) {
diff --git a/src/lib/ffi/ffi.cpp b/src/lib/ffi/ffi.cpp
index 35f001cb3..4fa34d1b6 100644
--- a/src/lib/ffi/ffi.cpp
+++ b/src/lib/ffi/ffi.cpp
@@ -10,6 +10,7 @@
 #include <botan/hex.h>
 #include <botan/mem_ops.h>
 #include <botan/version.h>
+#include <botan/internal/ct_utils.h>
 #include <botan/internal/ffi_util.h>
 #include <botan/internal/os_utils.h>
 #include <cstdio>
@@ -290,7 +291,9 @@ uint32_t botan_version_datestamp() {
 }
 
 int botan_constant_time_compare(const uint8_t* x, const uint8_t* y, size_t len) {
-   return Botan::constant_time_compare(x, y, len) ? 0 : -1;
+   auto same = Botan::CT::is_equal(x, y, len);
+   // Return 0 if same or -1 otherwise
+   return static_cast<int>(same.select(1, 0)) - 1;
 }
 
 int botan_same_mem(const uint8_t* x, const uint8_t* y, size_t len) {
diff --git a/src/lib/mac/mac.cpp b/src/lib/mac/mac.cpp
index c05cdb901..2e5082045 100644
--- a/src/lib/mac/mac.cpp
+++ b/src/lib/mac/mac.cpp
@@ -9,6 +9,7 @@
 
 #include <botan/exceptn.h>
 #include <botan/mem_ops.h>
+#include <botan/internal/ct_utils.h>
 #include <botan/internal/scan_name.h>
 
 #if defined(BOTAN_HAS_CMAC)
@@ -169,7 +170,7 @@ bool MessageAuthenticationCode::verify_mac_result(std::span<const uint8_t> mac)
       return false;
    }
 
-   return constant_time_compare(our_mac.data(), mac.data(), mac.size());
+   return CT::is_equal(our_mac.data(), mac.data(), mac.size()).as_bool();
 }
 
 }  // namespace Botan
diff --git a/src/lib/math/bigint/bigint.cpp b/src/lib/math/bigint/bigint.cpp
index 503062948..68d103e85 100644
--- a/src/lib/math/bigint/bigint.cpp
+++ b/src/lib/math/bigint/bigint.cpp
@@ -165,7 +165,7 @@ bool BigInt::is_equal(const BigInt& other) const {
       return false;
    }
 
-   return bigint_ct_is_eq(this->data(), this->sig_words(), other.data(), other.sig_words()).is_set();
+   return bigint_ct_is_eq(this->data(), this->sig_words(), other.data(), other.sig_words()).as_bool();
 }
 
 bool BigInt::is_less_than(const BigInt& other) const {
@@ -178,10 +178,10 @@ bool BigInt::is_less_than(const BigInt& other) const {
    }
 
    if(other.is_negative() && this->is_negative()) {
-      return bigint_ct_is_lt(other.data(), other.sig_words(), this->data(), this->sig_words()).is_set();
+      return bigint_ct_is_lt(other.data(), other.sig_words(), this->data(), this->sig_words()).as_bool();
    }
 
-   return bigint_ct_is_lt(this->data(), this->sig_words(), other.data(), other.sig_words()).is_set();
+   return bigint_ct_is_lt(this->data(), this->sig_words(), other.data(), other.sig_words()).as_bool();
 }
 
 void BigInt::encode_words(word out[], size_t size) const {
diff --git a/src/lib/math/bigint/divide.cpp b/src/lib/math/bigint/divide.cpp
index 00e534ecf..58d8fd3b2 100644
--- a/src/lib/math/bigint/divide.cpp
+++ b/src/lib/math/bigint/divide.cpp
@@ -40,7 +40,7 @@ inline bool division_check(word q, word y2, word y1, word x3, word x2, word x1)
    const word x[3] = {x1, x2, x3};
    const word y[3] = {y1, y2, y3};
 
-   return bigint_ct_is_lt(x, 3, y, 3).is_set();
+   return bigint_ct_is_lt(x, 3, y, 3).as_bool();
 }
 
 }  // namespace
@@ -98,7 +98,7 @@ void ct_divide_word(const BigInt& x, word y, BigInt& q_out, word& r_out) {
       r += x_b;
 
       const auto r_gte_y = CT::Mask<word>::is_gte(r, y) | r_carry;
-      q.conditionally_set_bit(b, r_gte_y.is_set());
+      q.conditionally_set_bit(b, r_gte_y.as_bool());
       r = r_gte_y.select(r - y, r);
    }
 
diff --git a/src/lib/math/numbertheory/make_prm.cpp b/src/lib/math/numbertheory/make_prm.cpp
index 4be0603cf..735906fe3 100644
--- a/src/lib/math/numbertheory/make_prm.cpp
+++ b/src/lib/math/numbertheory/make_prm.cpp
@@ -54,7 +54,7 @@ class Prime_Sieve final {
             }
          }
 
-         return passes.is_set();
+         return passes.as_bool();
       }
 
    private:
diff --git a/src/lib/math/numbertheory/mod_inv.cpp b/src/lib/math/numbertheory/mod_inv.cpp
index 44493760d..29d7f169a 100644
--- a/src/lib/math/numbertheory/mod_inv.cpp
+++ b/src/lib/math/numbertheory/mod_inv.cpp
@@ -108,7 +108,7 @@ BigInt inverse_mod_odd_modulus(const BigInt& n, const BigInt& mod) {
       b_is_1 &= CT::Mask<word>::is_zero(b_w[i]);
    }
 
-   BOTAN_ASSERT(a_is_0.is_set(), "A is zero");
+   BOTAN_ASSERT(a_is_0.as_bool(), "A is zero");
 
    // if b != 1 then gcd(n,mod) > 1 and inverse does not exist
    // in which case zero out the result to indicate this
diff --git a/src/lib/misc/cryptobox/cryptobox.cpp b/src/lib/misc/cryptobox/cryptobox.cpp
index 7d7e3fce3..6721c85e8 100644
--- a/src/lib/misc/cryptobox/cryptobox.cpp
+++ b/src/lib/misc/cryptobox/cryptobox.cpp
@@ -14,6 +14,7 @@
 #include <botan/pem.h>
 #include <botan/pwdhash.h>
 #include <botan/rng.h>
+#include <botan/internal/ct_utils.h>
 #include <botan/internal/loadstor.h>
 
 namespace Botan::CryptoBox {
@@ -132,7 +133,7 @@ secure_vector<uint8_t> decrypt_bin(const uint8_t input[], size_t input_len, std:
    }
    secure_vector<uint8_t> computed_mac = hmac->final();
 
-   if(!constant_time_compare(computed_mac.data(), box_mac, MAC_OUTPUT_LEN)) {
+   if(!CT::is_equal(computed_mac.data(), box_mac, MAC_OUTPUT_LEN).as_bool()) {
       throw Decoding_Error("CryptoBox integrity failure");
    }
 
diff --git a/src/lib/misc/tss/tss.cpp b/src/lib/misc/tss/tss.cpp
index cc6a30197..7b64e55c4 100644
--- a/src/lib/misc/tss/tss.cpp
+++ b/src/lib/misc/tss/tss.cpp
@@ -10,6 +10,7 @@
 #include <botan/hash.h>
 #include <botan/hex.h>
 #include <botan/rng.h>
+#include <botan/internal/ct_utils.h>
 #include <botan/internal/loadstor.h>
 
 namespace Botan {
@@ -215,7 +216,7 @@ secure_vector<uint8_t> RTSS_Share::reconstruct(const std::vector<RTSS_Share>& sh
             throw Decoding_Error("Different sized RTSS shares detected");
          }
 
-         if(!same_mem(&shares[0].m_contents[0], &shares[i].m_contents[0], RTSS_HEADER_SIZE)) {
+         if(!CT::is_equal(&shares[0].m_contents[0], &shares[i].m_contents[0], RTSS_HEADER_SIZE).as_bool()) {
             throw Decoding_Error("Different RTSS headers detected");
          }
       }
@@ -289,7 +290,7 @@ secure_vector<uint8_t> RTSS_Share::reconstruct(const std::vector<RTSS_Share>& sh
       hash->update(recovered.data(), secret_len);
       secure_vector<uint8_t> hash_check = hash->final();
 
-      if(!constant_time_compare(hash_check.data(), &recovered[secret_len], hash->output_length())) {
+      if(!CT::is_equal(hash_check.data(), &recovered[secret_len], hash->output_length()).as_bool()) {
          throw Decoding_Error("RTSS hash check failed");
       }
 
diff --git a/src/lib/modes/aead/ccm/ccm.cpp b/src/lib/modes/aead/ccm/ccm.cpp
index 229184eee..8655ce974 100644
--- a/src/lib/modes/aead/ccm/ccm.cpp
+++ b/src/lib/modes/aead/ccm/ccm.cpp
@@ -8,6 +8,7 @@
 
 #include <botan/internal/ccm.h>
 
+#include <botan/internal/ct_utils.h>
 #include <botan/internal/fmt.h>
 #include <botan/internal/loadstor.h>
 
@@ -265,7 +266,7 @@ void CCM_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
 
    T ^= S0;
 
-   if(!constant_time_compare(T.data(), buf_end, tag_size())) {
+   if(!CT::is_equal(T.data(), buf_end, tag_size()).as_bool()) {
       throw Invalid_Authentication_Tag("CCM tag check failed");
    }
 
diff --git a/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp b/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp
index 205ebdbd7..9d587007d 100644
--- a/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp
+++ b/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp
@@ -8,6 +8,7 @@
 
 #include <botan/internal/chacha20poly1305.h>
 
+#include <botan/internal/ct_utils.h>
 #include <botan/internal/loadstor.h>
 
 namespace Botan {
@@ -158,7 +159,7 @@ void ChaCha20Poly1305_Decryption::finish_msg(secure_vector<uint8_t>& buffer, siz
    m_ctext_len = 0;
    m_nonce_len = 0;
 
-   if(!constant_time_compare(mac, included_tag, tag_size())) {
+   if(!CT::is_equal(mac, included_tag, tag_size()).as_bool()) {
       throw Invalid_Authentication_Tag("ChaCha20Poly1305 tag check failed");
    }
    buffer.resize(offset + remaining);
diff --git a/src/lib/modes/aead/eax/eax.cpp b/src/lib/modes/aead/eax/eax.cpp
index 76f4aaa3a..6784f7e08 100644
--- a/src/lib/modes/aead/eax/eax.cpp
+++ b/src/lib/modes/aead/eax/eax.cpp
@@ -9,6 +9,7 @@
 #include <botan/internal/eax.h>
 
 #include <botan/internal/cmac.h>
+#include <botan/internal/ct_utils.h>
 #include <botan/internal/ctr.h>
 #include <botan/internal/fmt.h>
 
@@ -176,7 +177,7 @@ void EAX_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
 
    mac ^= m_ad_mac;
 
-   bool accept_mac = constant_time_compare(mac.data(), included_tag, tag_size());
+   const bool accept_mac = CT::is_equal(mac.data(), included_tag, tag_size()).as_bool();
 
    buffer.resize(offset + remaining);
 
diff --git a/src/lib/modes/aead/gcm/gcm.cpp b/src/lib/modes/aead/gcm/gcm.cpp
index ec780c5fa..3e9668bc1 100644
--- a/src/lib/modes/aead/gcm/gcm.cpp
+++ b/src/lib/modes/aead/gcm/gcm.cpp
@@ -9,6 +9,7 @@
 #include <botan/internal/gcm.h>
 
 #include <botan/block_cipher.h>
+#include <botan/internal/ct_utils.h>
 #include <botan/internal/ctr.h>
 #include <botan/internal/fmt.h>
 #include <botan/internal/ghash.h>
@@ -167,7 +168,7 @@ void GCM_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
 
    const uint8_t* included_tag = &buffer[remaining + offset];
 
-   if(!constant_time_compare(mac.data(), included_tag, tag_size())) {
+   if(!CT::is_equal(mac.data(), included_tag, tag_size()).as_bool()) {
       throw Invalid_Authentication_Tag("GCM tag check failed");
    }
 
diff --git a/src/lib/modes/aead/ocb/ocb.cpp b/src/lib/modes/aead/ocb/ocb.cpp
index 3952814f4..8995a8df2 100644
--- a/src/lib/modes/aead/ocb/ocb.cpp
+++ b/src/lib/modes/aead/ocb/ocb.cpp
@@ -10,6 +10,7 @@
 
 #include <botan/block_cipher.h>
 #include <botan/internal/bit_ops.h>
+#include <botan/internal/ct_utils.h>
 #include <botan/internal/poly_dbl.h>
 
 namespace Botan {
@@ -486,7 +487,7 @@ void OCB_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
    // compare mac
    const uint8_t* included_tag = &buf[remaining];
 
-   if(!constant_time_compare(mac.data(), included_tag, tag_size())) {
+   if(!CT::is_equal(mac.data(), included_tag, tag_size()).as_bool()) {
       throw Invalid_Authentication_Tag("OCB tag check failed");
    }
 
diff --git a/src/lib/modes/aead/siv/siv.cpp b/src/lib/modes/aead/siv/siv.cpp
index ffde05e81..0f60e8668 100644
--- a/src/lib/modes/aead/siv/siv.cpp
+++ b/src/lib/modes/aead/siv/siv.cpp
@@ -10,6 +10,7 @@
 
 #include <botan/block_cipher.h>
 #include <botan/internal/cmac.h>
+#include <botan/internal/ct_utils.h>
 #include <botan/internal/ctr.h>
 #include <botan/internal/poly_dbl.h>
 
@@ -187,7 +188,7 @@ void SIV_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
 
    const secure_vector<uint8_t> T = S2V(buffer.data() + offset, buffer.size() - offset - V.size());
 
-   if(!constant_time_compare(T.data(), V.data(), T.size())) {
+   if(!CT::is_equal(T.data(), V.data(), T.size()).as_bool()) {
       throw Invalid_Authentication_Tag("SIV tag check failed");
    }
 
diff --git a/src/lib/passhash/argon2fmt/argon2fmt.cpp b/src/lib/passhash/argon2fmt/argon2fmt.cpp
index 4b0f21e5e..40c6bce97 100644
--- a/src/lib/passhash/argon2fmt/argon2fmt.cpp
+++ b/src/lib/passhash/argon2fmt/argon2fmt.cpp
@@ -9,6 +9,7 @@
 #include <botan/base64.h>
 #include <botan/pwdhash.h>
 #include <botan/rng.h>
+#include <botan/internal/ct_utils.h>
 #include <botan/internal/fmt.h>
 #include <botan/internal/parsing.h>
 
@@ -139,7 +140,7 @@ bool argon2_check_pwhash(const char* password, size_t password_len, std::string_
 
    pwdhash->derive_key(generated.data(), generated.size(), password, password_len, salt.data(), salt.size());
 
-   return constant_time_compare(generated.data(), hash.data(), generated.size());
+   return CT::is_equal(generated.data(), hash.data(), generated.size()).as_bool();
 }
 
 }  // namespace Botan
diff --git a/src/lib/passhash/bcrypt/bcrypt.cpp b/src/lib/passhash/bcrypt/bcrypt.cpp
index ee6e34bbf..478e79f99 100644
--- a/src/lib/passhash/bcrypt/bcrypt.cpp
+++ b/src/lib/passhash/bcrypt/bcrypt.cpp
@@ -176,7 +176,8 @@ bool check_bcrypt(std::string_view pass, std::string_view hash) {
 
    const std::string compare = make_bcrypt(pass, salt, workfactor, bcrypt_version);
 
-   return same_mem(hash.data(), compare.data(), compare.size());
+   return CT::is_equal(cast_char_ptr_to_uint8(hash.data()), cast_char_ptr_to_uint8(compare.data()), compare.size())
+      .as_bool();
 }
 
 }  // namespace Botan
diff --git a/src/lib/passhash/passhash9/passhash9.cpp b/src/lib/passhash/passhash9/passhash9.cpp
index 922b3ec11..3cb7c5758 100644
--- a/src/lib/passhash/passhash9/passhash9.cpp
+++ b/src/lib/passhash/passhash9/passhash9.cpp
@@ -10,6 +10,7 @@
 #include <botan/base64.h>
 #include <botan/pbkdf2.h>
 #include <botan/rng.h>
+#include <botan/internal/ct_utils.h>
 #include <botan/internal/loadstor.h>
 
 namespace Botan {
@@ -119,8 +120,9 @@ bool check_passhash9(std::string_view pass, std::string_view hash) {
       kdf.derive_key(PASSHASH9_PBKDF_OUTPUT_LEN, pass, &bin[ALGID_BYTES + WORKFACTOR_BYTES], SALT_BYTES, kdf_iterations)
          .bits_of();
 
-   return constant_time_compare(
-      cmp.data(), &bin[ALGID_BYTES + WORKFACTOR_BYTES + SALT_BYTES], PASSHASH9_PBKDF_OUTPUT_LEN);
+   const uint8_t* hashbytes = &bin[ALGID_BYTES + WORKFACTOR_BYTES + SALT_BYTES];
+
+   return CT::is_equal(cmp.data(), hashbytes, PASSHASH9_PBKDF_OUTPUT_LEN).as_bool();
 }
 
 bool is_passhash9_alg_supported(uint8_t alg_id) {
diff --git a/src/lib/pk_pad/eme_oaep/oaep.cpp b/src/lib/pk_pad/eme_oaep/oaep.cpp
index 13bfbaaf9..5719c28aa 100644
--- a/src/lib/pk_pad/eme_oaep/oaep.cpp
+++ b/src/lib/pk_pad/eme_oaep/oaep.cpp
@@ -117,7 +117,9 @@ secure_vector<uint8_t> oaep_find_delim(uint8_t& valid_mask,
 
    // If we never saw any non-zero byte, then it's not valid input
    bad_input_m |= waiting_for_delim;
-   bad_input_m |= CT::Mask<uint8_t>::is_zero(ct_compare_u8(&input[hlen], Phash.data(), hlen));
+
+   // If the P hash is wrong, then it's not valid
+   bad_input_m |= CT::is_not_equal(&input[hlen], Phash.data(), hlen);
 
    delim_idx += 1;
 
diff --git a/src/lib/pk_pad/emsa_pssr/pssr.cpp b/src/lib/pk_pad/emsa_pssr/pssr.cpp
index f3f618784..a154182d2 100644
--- a/src/lib/pk_pad/emsa_pssr/pssr.cpp
+++ b/src/lib/pk_pad/emsa_pssr/pssr.cpp
@@ -11,6 +11,7 @@
 #include <botan/mem_ops.h>
 #include <botan/rng.h>
 #include <botan/internal/bit_ops.h>
+#include <botan/internal/ct_utils.h>
 #include <botan/internal/mgf1.h>
 #include <botan/internal/stl_util.h>
 #include <array>
@@ -132,7 +133,7 @@ bool pss_verify(HashFunction& hash,
 
    const std::vector<uint8_t> H2 = hash.final_stdvec();
 
-   const bool ok = constant_time_compare(H, H2.data(), HASH_SIZE);
+   const bool ok = CT::is_equal(H, H2.data(), HASH_SIZE).as_bool();
 
    if(out_salt_size && ok) {
       *out_salt_size = salt_size;
diff --git a/src/lib/pk_pad/emsa_raw/emsa_raw.cpp b/src/lib/pk_pad/emsa_raw/emsa_raw.cpp
index f707fe45f..9eb5ae995 100644
--- a/src/lib/pk_pad/emsa_raw/emsa_raw.cpp
+++ b/src/lib/pk_pad/emsa_raw/emsa_raw.cpp
@@ -8,6 +8,7 @@
 #include <botan/internal/emsa_raw.h>
 
 #include <botan/exceptn.h>
+#include <botan/internal/ct_utils.h>
 
 namespace Botan {
 
@@ -80,7 +81,7 @@ bool EMSA_Raw::verify(const std::vector<uint8_t>& coded, const std::vector<uint8
       }
    }
 
-   if(!constant_time_compare(coded.data(), raw.data() + leading_zeros_expected, coded.size())) {
+   if(!CT::is_equal(coded.data(), raw.data() + leading_zeros_expected, coded.size()).as_bool()) {
       same_modulo_leading_zeros = false;
    }
 
diff --git a/src/lib/pk_pad/iso9796/iso9796.cpp b/src/lib/pk_pad/iso9796/iso9796.cpp
index 5964a89e1..737cef06f 100644
--- a/src/lib/pk_pad/iso9796/iso9796.cpp
+++ b/src/lib/pk_pad/iso9796/iso9796.cpp
@@ -187,10 +187,10 @@ bool iso9796_verification(const std::vector<uint8_t>& const_coded,
    std::vector<uint8_t> H2 = hash->final_stdvec();
 
    //check if H3 == H2
-   bad_input |= CT::Mask<uint8_t>::is_zero(ct_compare_u8(H3.data(), H2.data(), HASH_SIZE));
+   bad_input |= CT::is_not_equal(H3.data(), H2.data(), HASH_SIZE);
 
    CT::unpoison(bad_input);
-   return (bad_input.is_set() == false);
+   return (bad_input.as_bool() == false);
 }
 
 }  // namespace
diff --git a/src/lib/pubkey/dlies/dlies.cpp b/src/lib/pubkey/dlies/dlies.cpp
index 729266477..d58205e16 100644
--- a/src/lib/pubkey/dlies/dlies.cpp
+++ b/src/lib/pubkey/dlies/dlies.cpp
@@ -7,6 +7,7 @@
 */
 
 #include <botan/dlies.h>
+#include <botan/internal/ct_utils.h>
 #include <botan/internal/stl_util.h>
 #include <limits>
 
@@ -156,7 +157,7 @@ secure_vector<uint8_t> DLIES_Decryptor::do_decrypt(uint8_t& valid_mask, const ui
    secure_vector<uint8_t> tag(msg + m_pub_key_size + ciphertext_len,
                               msg + m_pub_key_size + ciphertext_len + m_mac->output_length());
 
-   valid_mask = ct_compare_u8(tag.data(), calculated_tag.data(), tag.size());
+   valid_mask = CT::is_equal(tag.data(), calculated_tag.data(), tag.size()).value();
 
    // decrypt
    if(m_cipher) {
diff --git a/src/lib/pubkey/ec_group/ec_point.cpp b/src/lib/pubkey/ec_group/ec_point.cpp
index 74a8045ba..f57cd6f87 100644
--- a/src/lib/pubkey/ec_group/ec_point.cpp
+++ b/src/lib/pubkey/ec_group/ec_point.cpp
@@ -72,7 +72,7 @@ inline void resize_ws(std::vector<BigInt>& ws_bn, size_t cap_size) {
 
 void EC_Point::add_affine(
    const word x_words[], size_t x_size, const word y_words[], size_t y_size, std::vector<BigInt>& ws_bn) {
-   if((CT::all_zeros(x_words, x_size) & CT::all_zeros(y_words, y_size)).is_set()) {
+   if((CT::all_zeros(x_words, x_size) & CT::all_zeros(y_words, y_size)).as_bool()) {
       return;
    }
 
@@ -154,7 +154,7 @@ void EC_Point::add(const word x_words[],
                    const word z_words[],
                    size_t z_size,
                    std::vector<BigInt>& ws_bn) {
-   if((CT::all_zeros(x_words, x_size) & CT::all_zeros(z_words, z_size)).is_set()) {
+   if((CT::all_zeros(x_words, x_size) & CT::all_zeros(z_words, z_size)).as_bool()) {
       return;
    }
 
diff --git a/src/lib/pubkey/ecies/ecies.cpp b/src/lib/pubkey/ecies/ecies.cpp
index cb9134135..4ab7ebc10 100644
--- a/src/lib/pubkey/ecies/ecies.cpp
+++ b/src/lib/pubkey/ecies/ecies.cpp
@@ -11,6 +11,7 @@
 #include <botan/cipher_mode.h>
 #include <botan/mac.h>
 #include <botan/numthry.h>
+#include <botan/internal/ct_utils.h>
 #include <botan/internal/pk_ops_impl.h>
 #include <botan/internal/stl_util.h>
 
@@ -346,7 +347,7 @@ secure_vector<uint8_t> ECIES_Decryptor::do_decrypt(uint8_t& valid_mask, const ui
       m_mac->update(m_label);
    }
    const secure_vector<uint8_t> calculated_mac = m_mac->final();
-   valid_mask = ct_compare_u8(mac_data.data(), calculated_mac.data(), mac_data.size());
+   valid_mask = CT::is_equal(mac_data.data(), calculated_mac.data(), mac_data.size()).value();
 
    if(valid_mask) {
       // decrypt data
diff --git a/src/lib/pubkey/ed25519/ed25519.cpp b/src/lib/pubkey/ed25519/ed25519.cpp
index b5737be0c..a12185b92 100644
--- a/src/lib/pubkey/ed25519/ed25519.cpp
+++ b/src/lib/pubkey/ed25519/ed25519.cpp
@@ -11,6 +11,7 @@
 #include <botan/ed25519.h>
 
 #include <botan/rng.h>
+#include <botan/internal/ct_utils.h>
 #include <botan/internal/ed25519_internal.h>
 #include <botan/internal/sha2_64.h>
 
@@ -122,7 +123,7 @@ bool ed25519_verify(const uint8_t* m,
 
    ge_double_scalarmult_vartime(rcheck, h, &A, sig + 32);
 
-   return constant_time_compare(rcheck, sig, 32);
+   return CT::is_equal(rcheck, sig, 32).as_bool();
 }
 
 }  // namespace Botan
diff --git a/src/lib/pubkey/ed25519/ed25519_key.cpp b/src/lib/pubkey/ed25519/ed25519_key.cpp
index 1d63b7b69..89e76c0fc 100644
--- a/src/lib/pubkey/ed25519/ed25519_key.cpp
+++ b/src/lib/pubkey/ed25519/ed25519_key.cpp
@@ -14,6 +14,7 @@
 #include <botan/der_enc.h>
 #include <botan/hash.h>
 #include <botan/rng.h>
+#include <botan/internal/ct_utils.h>
 #include <botan/internal/ed25519_internal.h>
 #include <botan/internal/pk_ops_impl.h>
 
@@ -33,7 +34,7 @@ bool Ed25519_PublicKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*
    */
 
    const uint8_t identity_element[32] = {1};
-   if(same_mem(m_public.data(), identity_element, 32)) {
+   if(CT::is_equal(m_public.data(), identity_element, 32).as_bool()) {
       return false;
    }
 
@@ -56,7 +57,7 @@ bool Ed25519_PublicKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*
    uint8_t result[32];
    ge_double_scalarmult_vartime(result, modm_m, &point, zero);
 
-   if(!same_mem(result, identity_element, 32)) {
+   if(!CT::is_equal(result, identity_element, 32).as_bool()) {
       return false;
    }
 
diff --git a/src/lib/pubkey/kyber/kyber_common/kyber.cpp b/src/lib/pubkey/kyber/kyber_common/kyber.cpp
index 229bbbe7c..8fd8bed5a 100644
--- a/src/lib/pubkey/kyber/kyber_common/kyber.cpp
+++ b/src/lib/pubkey/kyber/kyber_common/kyber.cpp
@@ -1157,9 +1157,9 @@ class Kyber_KEM_Decryptor final : public PK_Ops::KEM_Decryption_with_KDF,
 
          // Overwrite pre-k with z on re-encryption failure (constant time)
          secure_vector<uint8_t> lower_g_out_final(lower_g_out.size());
-         const uint8_t reencrypt_success =
-            constant_time_compare(encapsulated_key.data(), cmp.data(), encapsulated_key.size());
          BOTAN_ASSERT_NOMSG(lower_g_out.size() == m_key.m_private->z().size());
+
+         const auto reencrypt_success = CT::is_equal(encapsulated_key.data(), cmp.data(), encapsulated_key.size());
          CT::conditional_copy_mem(reencrypt_success,
                                   lower_g_out_final.data(),
                                   lower_g_out.data(),
diff --git a/src/lib/pubkey/pubkey.cpp b/src/lib/pubkey/pubkey.cpp
index 9f0d83eeb..ba37b2cb7 100644
--- a/src/lib/pubkey/pubkey.cpp
+++ b/src/lib/pubkey/pubkey.cpp
@@ -380,7 +380,7 @@ std::vector<uint8_t> decode_der_signature(const uint8_t sig[], size_t length, si
 
    const std::vector<uint8_t> reencoded = der_encode_signature(real_sig, sig_parts, sig_part_size);
 
-   if(reencoded.size() != length || same_mem(reencoded.data(), sig, reencoded.size()) == false) {
+   if(reencoded.size() != length || CT::is_equal(reencoded.data(), sig, reencoded.size()).as_bool() == false) {
       throw Decoding_Error("PK_Verifier: signature is not the canonical DER encoding");
    }
    return real_sig;
diff --git a/src/lib/pubkey/sm2/sm2_enc.cpp b/src/lib/pubkey/sm2/sm2_enc.cpp
index 6a279f9f8..7985b2b7d 100644
--- a/src/lib/pubkey/sm2/sm2_enc.cpp
+++ b/src/lib/pubkey/sm2/sm2_enc.cpp
@@ -11,6 +11,7 @@
 #include <botan/der_enc.h>
 #include <botan/hash.h>
 #include <botan/kdf.h>
+#include <botan/internal/ct_utils.h>
 #include <botan/internal/fmt.h>
 #include <botan/internal/pk_ops.h>
 #include <botan/internal/point_mul.h>
@@ -161,7 +162,7 @@ class SM2_Decryption_Operation final : public PK_Ops::Decryption {
             return secure_vector<uint8_t>();
          }
 
-         if(same_mem(recode_ctext.data(), ciphertext, ciphertext_len) == false) {
+         if(CT::is_equal(recode_ctext.data(), ciphertext, ciphertext_len).as_bool() == false) {
             return secure_vector<uint8_t>();
          }
 
@@ -201,7 +202,7 @@ class SM2_Decryption_Operation final : public PK_Ops::Decryption {
          m_hash->update(y2_bytes);
          secure_vector<uint8_t> u = m_hash->final();
 
-         if(constant_time_compare(u.data(), C3.data(), m_hash->output_length()) == false) {
+         if(!CT::is_equal(u.data(), C3.data(), m_hash->output_length()).as_bool()) {
             return secure_vector<uint8_t>();
          }
 
diff --git a/src/lib/tls/msg_finished.cpp b/src/lib/tls/msg_finished.cpp
index 9efacf96b..1d9bf601d 100644
--- a/src/lib/tls/msg_finished.cpp
+++ b/src/lib/tls/msg_finished.cpp
@@ -10,6 +10,7 @@
 #include <botan/tls_messages.h>
 
 #include <botan/kdf.h>
+#include <botan/internal/ct_utils.h>
 #include <botan/internal/tls_handshake_io.h>
 #include <botan/internal/tls_handshake_state.h>
 
@@ -66,8 +67,12 @@ bool Finished_12::verify(const Handshake_State& state, Connection_Side side) con
 #if defined(BOTAN_UNSAFE_FUZZER_MODE)
    return true;
 #else
-   return (m_verification_data.size() == computed_verify.size()) &&
-          constant_time_compare(m_verification_data.data(), computed_verify.data(), computed_verify.size());
+   // first check the size:
+   if(m_verification_data.size() != computed_verify.size()) {
+      return false;
+   }
+
+   return CT::is_equal(m_verification_data.data(), computed_verify.data(), computed_verify.size()).as_bool();
 #endif
 }
 
diff --git a/src/lib/tls/msg_server_hello.cpp b/src/lib/tls/msg_server_hello.cpp
index 09c227cbb..f0c229584 100644
--- a/src/lib/tls/msg_server_hello.cpp
+++ b/src/lib/tls/msg_server_hello.cpp
@@ -16,6 +16,7 @@
 #include <botan/tls_exceptn.h>
 #include <botan/tls_extensions.h>
 #include <botan/tls_session_manager.h>
+#include <botan/internal/ct_utils.h>
 #include <botan/internal/stl_util.h>
 #include <botan/internal/tls_handshake_hash.h>
 #include <botan/internal/tls_handshake_io.h>
@@ -37,7 +38,7 @@ const std::vector<uint8_t> HELLO_RETRY_REQUEST_MARKER = {
    0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C};
 
 bool random_signals_hello_retry_request(const std::vector<uint8_t>& random) {
-   return constant_time_compare(random.data(), HELLO_RETRY_REQUEST_MARKER.data(), HELLO_RETRY_REQUEST_MARKER.size());
+   return CT::is_equal(random.data(), HELLO_RETRY_REQUEST_MARKER.data(), HELLO_RETRY_REQUEST_MARKER.size()).as_bool();
 }
 
 std::vector<uint8_t> make_server_hello_random(RandomNumberGenerator& rng,
diff --git a/src/lib/tls/tls12/msg_client_kex.cpp b/src/lib/tls/tls12/msg_client_kex.cpp
index f82d65d99..663ba9e40 100644
--- a/src/lib/tls/tls12/msg_client_kex.cpp
+++ b/src/lib/tls/tls12/msg_client_kex.cpp
@@ -122,7 +122,7 @@ Client_Key_Exchange::Client_Key_Exchange(Handshake_IO& io,
          //   With X25519 and X448, a receiving party MUST check whether the
          //   computed premaster secret is the all-zero value and abort the
          //   handshake if so, as described in Section 6 of [RFC7748].
-         if(curve_id == Group_Params::X25519 && CT::all_zeros(shared_secret.data(), shared_secret.size()).is_set()) {
+         if(curve_id == Group_Params::X25519 && CT::all_zeros(shared_secret.data(), shared_secret.size()).as_bool()) {
             throw TLS_Exception(Alert::DecryptError, "Bad X25519 key exchange");
          }
 
@@ -281,7 +281,8 @@ Client_Key_Exchange::Client_Key_Exchange(const std::vector<uint8_t>& contents,
                //   handshake if so, as described in Section 6 of [RFC7748].
                BOTAN_ASSERT_NOMSG(state.server_kex()->params().size() >= 3);
                Group_Params group = static_cast<Group_Params>(state.server_kex()->params().at(2));
-               if(group == Group_Params::X25519 && CT::all_zeros(shared_secret.data(), shared_secret.size()).is_set()) {
+               if(group == Group_Params::X25519 &&
+                  CT::all_zeros(shared_secret.data(), shared_secret.size()).as_bool()) {
                   throw TLS_Exception(Alert::DecryptError, "Bad X25519 key exchange");
                }
             }
diff --git a/src/lib/tls/tls12/tls_cbc/tls_cbc.cpp b/src/lib/tls/tls12/tls_cbc/tls_cbc.cpp
index 36a875129..5a8e7d766 100644
--- a/src/lib/tls/tls12/tls_cbc/tls_cbc.cpp
+++ b/src/lib/tls/tls12/tls_cbc/tls_cbc.cpp
@@ -380,9 +380,9 @@ void TLS_CBC_HMAC_AEAD_Decryption::finish_msg(secure_vector<uint8_t>& buffer, si
 
       const size_t mac_offset = enc_size;
 
-      const bool mac_ok = constant_time_compare(&record_contents[mac_offset], mac_buf.data(), tag_size());
+      const auto mac_ok = CT::is_equal(&record_contents[mac_offset], mac_buf.data(), tag_size());
 
-      if(!mac_ok) {
+      if(!mac_ok.as_bool()) {
          throw TLS_Exception(Alert::BadRecordMac, "Message authentication failure");
       }
 
@@ -441,13 +441,13 @@ void TLS_CBC_HMAC_AEAD_Decryption::finish_msg(secure_vector<uint8_t>& buffer, si
 
       const size_t mac_offset = record_len - (tag_size() + pad_size);
 
-      const bool mac_ok = constant_time_compare(&record_contents[mac_offset], mac_buf.data(), tag_size());
+      const auto mac_ok = CT::is_equal(&record_contents[mac_offset], mac_buf.data(), tag_size());
 
       const auto ok_mask = size_ok_mask & CT::Mask<uint16_t>::expand(mac_ok) & CT::Mask<uint16_t>::expand(pad_size);
 
       CT::unpoison(ok_mask);
 
-      if(ok_mask.is_set()) {
+      if(ok_mask.as_bool()) {
          buffer.insert(buffer.end(), plaintext_block, plaintext_block + plaintext_length);
       } else {
          perform_additional_compressions(record_len, pad_size);
diff --git a/src/lib/tls/tls13/tls_extensions_key_share.cpp b/src/lib/tls/tls13/tls_extensions_key_share.cpp
index c330c2319..c7a0da1a3 100644
--- a/src/lib/tls/tls13/tls_extensions_key_share.cpp
+++ b/src/lib/tls/tls13/tls_extensions_key_share.cpp
@@ -125,7 +125,7 @@ class Key_Share_Entry {
          //   With X25519 and X448, a receiving party MUST check whether the
          //   computed premaster secret is the all-zero value and abort the
          //   handshake if so, as described in Section 6 of [RFC7748].
-         if(m_group == Named_Group::X25519 && CT::all_zeros(shared_secret.data(), shared_secret.size()).is_set()) {
+         if(m_group == Named_Group::X25519 && CT::all_zeros(shared_secret.data(), shared_secret.size()).as_bool()) {
             throw TLS_Exception(Alert::DecryptError, "Bad X25519 key exchange");
          }
 
diff --git a/src/lib/tls/tls_session.cpp b/src/lib/tls/tls_session.cpp
index f8bb229fd..b69833e9c 100644
--- a/src/lib/tls/tls_session.cpp
+++ b/src/lib/tls/tls_session.cpp
@@ -14,6 +14,7 @@
 #include <botan/mac.h>
 #include <botan/pem.h>
 #include <botan/rng.h>
+#include <botan/internal/ct_utils.h>
 #include <botan/internal/loadstor.h>
 
 #include <botan/tls_callbacks.h>
@@ -455,7 +456,7 @@ Session Session::decrypt(std::span<const uint8_t> in, const SymmetricKey& key) {
       hmac->update(TLS_SESSION_CRYPT_KEY_NAME);
       hmac->final(cmp_key_name.data());
 
-      if(same_mem(cmp_key_name.data(), key_name, TLS_SESSION_CRYPT_KEY_NAME_LEN) == false) {
+      if(CT::is_equal(cmp_key_name.data(), key_name, TLS_SESSION_CRYPT_KEY_NAME_LEN).as_bool() == false) {
          throw Decoding_Error("Wrong key name for encrypted session");
       }
 
diff --git a/src/lib/utils/ct_utils.h b/src/lib/utils/ct_utils.h
index 0e0cb8b5c..e6d864d8e 100644
--- a/src/lib/utils/ct_utils.h
+++ b/src/lib/utils/ct_utils.h
@@ -77,9 +77,11 @@ inline void unpoison(T& p) {
 * since you never know what a compiler might do.
 */
 template <typename T>
-   requires(std::is_unsigned<T>::value && !std::is_same<bool, T>::value)
 class Mask final {
    public:
+      static_assert(std::is_unsigned<T>::value && !std::is_same<bool, T>::value,
+                    "Only unsigned integer types are supported by CT::Mask");
+
       Mask(const Mask<T>& other) = default;
       Mask<T>& operator=(const Mask<T>& other) = default;
 
@@ -267,7 +269,7 @@ class Mask final {
       /**
       * Return true iff this mask is set
       */
-      bool is_set() const { return unpoisoned_value() != 0; }
+      bool as_bool() const { return unpoisoned_value() != 0; }
 
       /**
       * Return the underlying value of the mask
@@ -281,12 +283,17 @@ class Mask final {
 };
 
 template <typename T>
-inline Mask<T> conditional_copy_mem(T cnd, T* to, const T* from0, const T* from1, size_t elems) {
-   const auto mask = CT::Mask<T>::expand(cnd);
+inline Mask<T> conditional_copy_mem(Mask<T> mask, T* to, const T* from0, const T* from1, size_t elems) {
    mask.select_n(to, from0, from1, elems);
    return mask;
 }
 
+template <typename T>
+inline Mask<T> conditional_copy_mem(T cnd, T* to, const T* from0, const T* from1, size_t elems) {
+   const auto mask = CT::Mask<T>::expand(cnd);
+   return CT::conditional_copy_mem(mask, to, from0, from1, elems);
+}
+
 template <typename T>
 inline Mask<T> conditional_assign_mem(T cnd, T* sink, const T* src, size_t elems) {
    const auto mask = CT::Mask<T>::expand(cnd);
@@ -324,6 +331,30 @@ inline CT::Mask<T> all_zeros(const T elem[], size_t len) {
    return CT::Mask<T>::is_zero(sum);
 }
 
+/**
+* Compare two arrays of equal size and return a Mask indicating if
+* they are equal or not. The mask is set if they are identical.
+*/
+template <typename T>
+inline CT::Mask<T> is_equal(const T x[], const T y[], size_t len) {
+   volatile T difference = 0;
+
+   for(size_t i = 0; i != len; ++i) {
+      difference = difference | (x[i] ^ y[i]);
+   }
+
+   return CT::Mask<T>::is_zero(difference);
+}
+
+/**
+* Compare two arrays of equal size and return a Mask indicating if
+* they are equal or not. The mask is set if they differ.
+*/
+template <typename T>
+inline CT::Mask<T> is_not_equal(const T x[], const T y[], size_t len) {
+   return ~CT::is_equal(x, y, len);
+}
+
 /**
 * If bad_input is unset, return input[offset:input_length] copied to new
 * buffer. If bad_input is set, return an empty vector. In all cases, the capacity
diff --git a/src/lib/utils/mem_ops.cpp b/src/lib/utils/mem_ops.cpp
index 64cd07ec5..9abb2473a 100644
--- a/src/lib/utils/mem_ops.cpp
+++ b/src/lib/utils/mem_ops.cpp
@@ -68,13 +68,7 @@ void initialize_allocator() {
 }
 
 uint8_t ct_compare_u8(const uint8_t x[], const uint8_t y[], size_t len) {
-   volatile uint8_t difference = 0;
-
-   for(size_t i = 0; i != len; ++i) {
-      difference = difference | (x[i] ^ y[i]);
-   }
-
-   return CT::Mask<uint8_t>::is_zero(difference).value();
+   return CT::is_equal(x, y, len).value();
 }
 
 }  // namespace Botan
diff --git a/src/lib/utils/mem_ops.h b/src/lib/utils/mem_ops.h
index c7fb10144..f59098e7a 100644
--- a/src/lib/utils/mem_ops.h
+++ b/src/lib/utils/mem_ops.h
@@ -194,6 +194,7 @@ inline char* cast_uint8_ptr_to_char(uint8_t* b) {
    return reinterpret_cast<char*>(b);
 }
 
+#if !defined(BOTAN_IS_BEING_BUILT)
 /**
 * Memory comparison, input insensitive
 * @param p1 a pointer to an array
@@ -202,6 +203,7 @@ inline char* cast_uint8_ptr_to_char(uint8_t* b) {
 * @return true iff p1[i] == p2[i] forall i in [0...n)
 */
 template <typename T>
+BOTAN_DEPRECATED("This function is deprecated")
 inline bool same_mem(const T* p1, const T* p2, size_t n) {
    volatile T difference = 0;
 
@@ -211,6 +213,7 @@ inline bool same_mem(const T* p1, const T* p2, size_t n) {
 
    return difference == 0;
 }
+#endif
 
 #if !defined(BOTAN_IS_BEING_BUILT)
 


Test patch:
diff --git a/src/tests/test_bufcomp.cpp b/src/tests/test_bufcomp.cpp
index 04df35684..0d835d36e 100644
--- a/src/tests/test_bufcomp.cpp
+++ b/src/tests/test_bufcomp.cpp
@@ -52,8 +52,9 @@ class Test_Buf_Comp final : public Botan::Buffered_Computation {
 };
 
 void check(Test::Result& result, std::span<const uint8_t> produced, size_t expected) {
-   const uint8_t* eptr = reinterpret_cast<const uint8_t*>(&expected);
-   result.confirm("result is correct", Botan::same_mem(produced.data(), eptr, sizeof(size_t)));
+   uint8_t expected_bytes[sizeof(size_t)];
+   std::memcpy(expected_bytes, &expected, sizeof(expected));
+   result.test_eq("", "result is correct", produced.data(), produced.size(), expected_bytes, sizeof(expected_bytes));
 }
 
 using TestStdVector = Botan::Strong<std::vector<uint8_t>, struct TestStdVector_>;
diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp
index 5d9d002ad..bab502a82 100644
--- a/src/tests/tests.cpp
+++ b/src/tests/tests.cpp
@@ -157,12 +157,20 @@ bool Test::Result::test_failure(const std::string& err) {
    return false;
 }
 
+namespace {
+
+bool same_contents(const uint8_t x[], const uint8_t y[], size_t len) {
+   return (len == 0) ? true : std::memcmp(x, y, len) == 0;
+}
+
+}  // namespace
+
 bool Test::Result::test_ne(const std::string& what,
                            const uint8_t produced[],
                            size_t produced_len,
                            const uint8_t expected[],
                            size_t expected_len) {
-   if(produced_len == expected_len && Botan::same_mem(produced, expected, expected_len)) {
+   if(produced_len == expected_len && same_contents(produced, expected, expected_len)) {
       return test_failure(who() + ": " + what + " produced matching");
    }
    return test_success();
@@ -174,7 +182,7 @@ bool Test::Result::test_eq(const char* producer,
                            size_t produced_size,
                            const uint8_t expected[],
                            size_t expected_size) {
-   if(produced_size == expected_size && Botan::same_mem(produced, expected, expected_size)) {
+   if(produced_size == expected_size && same_contents(produced, expected, expected_size)) {
       return test_success();
    }