diff --git a/src/cli/zfec.cpp b/src/cli/zfec.cpp index 1d5ed8eb373..7f1653f1bf8 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 + #include #include #include #include @@ -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 62cf43dc9cd..1c6c827d2be 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 9e06246fda8..a440927116b 100644 --- a/src/lib/compat/sodium/sodium_secretbox.cpp +++ b/src/lib/compat/sodium/sodium_secretbox.cpp @@ -9,6 +9,7 @@ #include #include #include +#include namespace Botan { @@ -54,7 +55,7 @@ int Sodium::crypto_secretbox_xsalsa20poly1305_open( poly1305->update(ctext + 32, ctext_len - 32); secure_vector 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 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 b6db380979d..30ba1cdb9e3 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(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(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(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(x), static_cast(y), len); - return same ? 0 : -1; + const auto same = CT::is_equal(static_cast(x), static_cast(y), len); + // Return 0 if same or -1 if differing + return static_cast(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 35f001cb333..4fa34d1b6a4 100644 --- a/src/lib/ffi/ffi.cpp +++ b/src/lib/ffi/ffi.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -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(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 c05cdb901b5..2e508204524 100644 --- a/src/lib/mac/mac.cpp +++ b/src/lib/mac/mac.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #if defined(BOTAN_HAS_CMAC) @@ -169,7 +170,7 @@ bool MessageAuthenticationCode::verify_mac_result(std::span 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 503062948ff..68d103e85d0 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 00e534ecf82..58d8fd3b2b9 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::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 4be0603cf3d..735906fe32f 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 44493760de5..29d7f169a76 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::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 7d7e3fce3ee..6721c85e823 100644 --- a/src/lib/misc/cryptobox/cryptobox.cpp +++ b/src/lib/misc/cryptobox/cryptobox.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include namespace Botan::CryptoBox { @@ -132,7 +133,7 @@ secure_vector decrypt_bin(const uint8_t input[], size_t input_len, std: } secure_vector 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 cc6a3019700..7b64e55c48a 100644 --- a/src/lib/misc/tss/tss.cpp +++ b/src/lib/misc/tss/tss.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include namespace Botan { @@ -215,7 +216,7 @@ secure_vector RTSS_Share::reconstruct(const std::vector& 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 RTSS_Share::reconstruct(const std::vector& sh hash->update(recovered.data(), secret_len); secure_vector 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 229184eeefb..8655ce974fd 100644 --- a/src/lib/modes/aead/ccm/ccm.cpp +++ b/src/lib/modes/aead/ccm/ccm.cpp @@ -8,6 +8,7 @@ #include +#include #include #include @@ -265,7 +266,7 @@ void CCM_Decryption::finish_msg(secure_vector& 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 205ebdbd70e..9d587007d4f 100644 --- a/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp +++ b/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp @@ -8,6 +8,7 @@ #include +#include #include namespace Botan { @@ -158,7 +159,7 @@ void ChaCha20Poly1305_Decryption::finish_msg(secure_vector& 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 76f4aaa3a00..6784f7e080c 100644 --- a/src/lib/modes/aead/eax/eax.cpp +++ b/src/lib/modes/aead/eax/eax.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -176,7 +177,7 @@ void EAX_Decryption::finish_msg(secure_vector& 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 ec780c5fa64..3e9668bc1e6 100644 --- a/src/lib/modes/aead/gcm/gcm.cpp +++ b/src/lib/modes/aead/gcm/gcm.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -167,7 +168,7 @@ void GCM_Decryption::finish_msg(secure_vector& 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 3952814f434..8995a8df213 100644 --- a/src/lib/modes/aead/ocb/ocb.cpp +++ b/src/lib/modes/aead/ocb/ocb.cpp @@ -10,6 +10,7 @@ #include #include +#include #include namespace Botan { @@ -486,7 +487,7 @@ void OCB_Decryption::finish_msg(secure_vector& 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 ffde05e8178..0f60e8668c6 100644 --- a/src/lib/modes/aead/siv/siv.cpp +++ b/src/lib/modes/aead/siv/siv.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -187,7 +188,7 @@ void SIV_Decryption::finish_msg(secure_vector& buffer, size_t offset) { const secure_vector 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 4b0f21e5ea8..40c6bce972f 100644 --- a/src/lib/passhash/argon2fmt/argon2fmt.cpp +++ b/src/lib/passhash/argon2fmt/argon2fmt.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -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 ee6e34bbfb1..478e79f999a 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 922b3ec1119..3cb7c5758ea 100644 --- a/src/lib/passhash/passhash9/passhash9.cpp +++ b/src/lib/passhash/passhash9/passhash9.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include 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 13bfbaaf9cd..5719c28aae0 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 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::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 f3f618784c5..a154182d2f8 100644 --- a/src/lib/pk_pad/emsa_pssr/pssr.cpp +++ b/src/lib/pk_pad/emsa_pssr/pssr.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -132,7 +133,7 @@ bool pss_verify(HashFunction& hash, const std::vector 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 f707fe45fc6..9eb5ae995bc 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 #include +#include namespace Botan { @@ -80,7 +81,7 @@ bool EMSA_Raw::verify(const std::vector& coded, const std::vector& const_coded, std::vector H2 = hash->final_stdvec(); //check if H3 == H2 - bad_input |= CT::Mask::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 729266477b8..d58205e1691 100644 --- a/src/lib/pubkey/dlies/dlies.cpp +++ b/src/lib/pubkey/dlies/dlies.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include @@ -156,7 +157,7 @@ secure_vector DLIES_Decryptor::do_decrypt(uint8_t& valid_mask, const ui secure_vector 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 74a8045ba55..f57cd6f8718 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& 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& 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& 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 cb91341352f..4ab7ebc107b 100644 --- a/src/lib/pubkey/ecies/ecies.cpp +++ b/src/lib/pubkey/ecies/ecies.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -346,7 +347,7 @@ secure_vector ECIES_Decryptor::do_decrypt(uint8_t& valid_mask, const ui m_mac->update(m_label); } const secure_vector 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 b5737be0c16..a12185b9208 100644 --- a/src/lib/pubkey/ed25519/ed25519.cpp +++ b/src/lib/pubkey/ed25519/ed25519.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -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 1d63b7b6980..89e76c0fca4 100644 --- a/src/lib/pubkey/ed25519/ed25519_key.cpp +++ b/src/lib/pubkey/ed25519/ed25519_key.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -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 229bbbe7c3a..8fd8bed5aa7 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 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 9f0d83eeb95..ba37b2cb717 100644 --- a/src/lib/pubkey/pubkey.cpp +++ b/src/lib/pubkey/pubkey.cpp @@ -380,7 +380,7 @@ std::vector decode_der_signature(const uint8_t sig[], size_t length, si const std::vector 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 6a279f9f841..7985b2b7d0c 100644 --- a/src/lib/pubkey/sm2/sm2_enc.cpp +++ b/src/lib/pubkey/sm2/sm2_enc.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -161,7 +162,7 @@ class SM2_Decryption_Operation final : public PK_Ops::Decryption { return secure_vector(); } - 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(); } @@ -201,7 +202,7 @@ class SM2_Decryption_Operation final : public PK_Ops::Decryption { m_hash->update(y2_bytes); secure_vector 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(); } diff --git a/src/lib/tls/msg_finished.cpp b/src/lib/tls/msg_finished.cpp index 9efacf96b57..1d9bf601d11 100644 --- a/src/lib/tls/msg_finished.cpp +++ b/src/lib/tls/msg_finished.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -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 09c227cbb48..f0c229584f1 100644 --- a/src/lib/tls/msg_server_hello.cpp +++ b/src/lib/tls/msg_server_hello.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -37,7 +38,7 @@ const std::vector 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& 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 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 f82d65d992a..663ba9e409d 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& 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(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 36a875129c0..5a8e7d766bb 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& 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& 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::expand(mac_ok) & CT::Mask::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 c330c23194a..c7a0da1a3d2 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 f8bb229fd6c..b69833e9cc3 100644 --- a/src/lib/tls/tls_session.cpp +++ b/src/lib/tls/tls_session.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -455,7 +456,7 @@ Session Session::decrypt(std::span 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 0e0cb8b5ca5..e6d864d8e44 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 - requires(std::is_unsigned::value && !std::is_same::value) class Mask final { public: + static_assert(std::is_unsigned::value && !std::is_same::value, + "Only unsigned integer types are supported by CT::Mask"); + Mask(const Mask& other) = default; Mask& operator=(const Mask& 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 -inline Mask conditional_copy_mem(T cnd, T* to, const T* from0, const T* from1, size_t elems) { - const auto mask = CT::Mask::expand(cnd); +inline Mask conditional_copy_mem(Mask mask, T* to, const T* from0, const T* from1, size_t elems) { mask.select_n(to, from0, from1, elems); return mask; } +template +inline Mask conditional_copy_mem(T cnd, T* to, const T* from0, const T* from1, size_t elems) { + const auto mask = CT::Mask::expand(cnd); + return CT::conditional_copy_mem(mask, to, from0, from1, elems); +} + template inline Mask conditional_assign_mem(T cnd, T* sink, const T* src, size_t elems) { const auto mask = CT::Mask::expand(cnd); @@ -324,6 +331,30 @@ inline CT::Mask all_zeros(const T elem[], size_t len) { return CT::Mask::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 +inline CT::Mask 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::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 +inline CT::Mask 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 64cd07ec521..9abb2473a8e 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::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 c7fb10144e5..f59098e7ab3 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(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 +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) diff --git a/src/tests/test_bufcomp.cpp b/src/tests/test_bufcomp.cpp index 04df35684b0..0d835d36eff 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 produced, size_t expected) { - const uint8_t* eptr = reinterpret_cast(&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, struct TestStdVector_>; diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp index 5d9d002adc3..bab502a8278 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(); }