# \[Bug report\] BinFHEContext::SwitchCTtoqn accepts malformed large-dimension ciphertexts when the modulus matches

**URL:** <https://openfhe.discourse.group/t/bug-report-binfhecontext-switchcttoqn-accepts-malformed-large-dimension-ciphertexts-when-the-modulus-matches/2354>\
**Category:** Bug Reports\
**Created:** [July 29, 2026, 12:46pm UTC](https://openfhe.discourse.group/t/bug-report-binfhecontext-switchcttoqn-accepts-malformed-large-dimension-ciphertexts-when-the-modulus-matches/2354 "2026-07-29T12:46:05Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![CCYJ](https://avatars.discourse-cdn.com/v4/letter/c/b5ac83/32.png) [@CCYJ](https://openfhe.discourse.group/u/CCYJ)\
**Post date:** [July 29, 2026, 12:46pm UTC](https://openfhe.discourse.group/t/bug-report-binfhecontext-switchcttoqn-accepts-malformed-large-dimension-ciphertexts-when-the-modulus-matches/2354/1 "2026-07-29T12:46:05Z")

</div>

Hi OpenFHE team,

I would like to report an input-validation issue in `BinFHEContext::SwitchCTtoqn()` that can lead to an out-of-bounds read inside `KeySwitch()`, and ask whether the current validation logic is intentional.

# **Summary**

`BinFHEContext::SwitchCTtoqn()` uses a malformed-input guard that only rejects the ciphertext when both the dimension and modulus are wrong. As a result:

- wrong dimension + expected modulus `Q` passes
- expected dimension `N` + wrong modulus also passes

In the reproducer below, a ciphertext with the expected large modulus `Q` but a one-element `a` vector passes the guard and later crashes inside `KeySwitch()`.

I reproduced this locally under ASan/UBSan on OpenFHE `1.5.1`.

# **Environment**

- OpenFHE version: `1.5.1`
- Compiler: `clang 14.0.0`
- Sanitizers: `AddressSanitizer`, `UndefinedBehaviorSanitizer`

# **Minimal reproduction**

The reproducer below starts from a valid large-dimension ciphertext, shrinks its `a` vector to length `1`, and then calls `SwitchCTtoqn()`:

```cpp
#include "binfhecontext.h"
#include "lwe-ciphertext.h"

using namespace lbcrypto;

int main() {
    BinFHEContext cc;
    cc.GenerateBinFHEContext(TOY);

    auto sk = cc.KeyGen();
    cc.BTKeyGen(sk);
    auto pk = cc.PubKeyGen(sk);

    auto ctLarge = cc.Encrypt(pk, 1, LARGE_DIM);

    NativeVector shortA(1, ctLarge->GetModulus());
    shortA[0] = ctLarge->GetA()[0];

    auto malformed =
        std::make_shared<LWECiphertextImpl>(std::move(shortA), ctLarge->GetB(), ctLarge->GetptModulus());

    auto out = cc.SwitchCTtoqn(cc.GetSwitchKey(), malformed);
    (void)out;

    return 0;
}

```

# **Expected behavior**

`SwitchCTtoqn()` should reject the ciphertext if either its dimension is not `N` or its modulus is not `Q`.

In this reproducer, it should be rejected because its dimension is `1` rather than `N`.

# **Actual behavior**

The malformed ciphertext passes the front-end guard and later crashes with an ASan heap-buffer-overflow inside `KeySwitch()`.

Relevant excerpt:

```plaintext
==...==ERROR: AddressSanitizer: heap-buffer-overflow
    #0 ... NativeIntegerT<unsigned long>::ConvertToInt(...) .../ubintnat.h:1652
    #1 ... lbcrypto::LWEEncryptionScheme::KeySwitch(...) .../lwe-pke.cpp:336
    #2 ... lbcrypto::LWEEncryptionScheme::SwitchCTtoqn(...) .../lwe-pke.cpp:158
    #3 ... lbcrypto::BinFHEContext::SwitchCTtoqn(...) .../binfhecontext.cpp:260

```

The sanitizer line numbers above refer to my local instrumented source tree; the stable `v1.5.1` source links are listed below.

# **Cause analysis**

The current guard is:

```cpp
if ((ct->GetLength() != LWEParams->GetN()) && (ct->GetModulus() != LWEParams->GetQ()))
    OPENFHE_THROW("ciphertext dimension and modulus are not large N and Q");

```

This only throws when both invariants are violated at the same time.

Therefore, either a wrong dimension with modulus `Q`, or dimension `N` with the wrong modulus, can pass the guard.

The reproducer exercises the first case: wrong dimension with the expected modulus `Q`.

The modulus-switching step preserves the ciphertext vector length, so the one-element input reaches `KeySwitch()` as a one-element ciphertext.

`KeySwitch()` then uses `params->GetN()` as the fixed loop bound rather than the actual ciphertext length and reaches:

```cpp
NativeInteger::Integer atmp(ctQN->GetA()[i].ConvertToInt());

```

inside the `for (uint32_t i = 0; i < N; ++i)` loop in `KeySwitch()`.

# **Relevant source locations**

- `src/binfhe/lib/binfhecontext.cpp`
  - `BinFHEContext::SwitchCTtoqn()`: lines 228-237
  - Incorrect guard: line 234
  - GitHub link: [https://github.com/openfheorg/openfhe-development/blob/v1.5.1/src/binfhe/lib/binfhecontext.cpp#L228-L237](https://github.com/openfheorg/openfhe-development/blob/v1.5.1/src/binfhe/lib/binfhecontext.cpp#L228-L237)

- `src/binfhe/lib/lwe-pke.cpp`
  - `LWEEncryptionScheme::SwitchCTtoqn()`: lines 136-145
  - `LWEEncryptionScheme::ModSwitch()`: lines 216-224
  - `LWEEncryptionScheme::KeySwitch()`: lines 292-315
  - Crash-relevant read: line 304
  - GitHub links:
    - [https://github.com/openfheorg/openfhe-development/blob/v1.5.1/src/binfhe/lib/lwe-pke.cpp#L136-L145](https://github.com/openfheorg/openfhe-development/blob/v1.5.1/src/binfhe/lib/lwe-pke.cpp#L136-L145)
    - [https://github.com/openfheorg/openfhe-development/blob/v1.5.1/src/binfhe/lib/lwe-pke.cpp#L216-L224](https://github.com/openfheorg/openfhe-development/blob/v1.5.1/src/binfhe/lib/lwe-pke.cpp#L216-L224)
    - [https://github.com/openfheorg/openfhe-development/blob/v1.5.1/src/binfhe/lib/lwe-pke.cpp#L292-L315](https://github.com/openfheorg/openfhe-development/blob/v1.5.1/src/binfhe/lib/lwe-pke.cpp#L292-L315)

# **Suggested direction**

It looks like the front-end guard should use `||` rather than `&&`, for example:

```cpp
if ((ct->GetLength() != LWEParams->GetN()) ||
    (ct->GetModulus() != LWEParams->GetQ()))
    OPENFHE_THROW("ciphertext must have large dimension N and modulus Q");

```

It may also be worth enforcing the dimension invariant in the lower-level `LWEEncryptionScheme::SwitchCTtoqn()` path or immediately before the fixed-`N` indexing in `KeySwitch()`.

# **Question**

Is the current `&&` guard intentional here, or would you consider this a bug in the input validation logic?

Reported by Jiang Chao, Beijing University of Posts and Telecommunications

---

<div class="post-metadata">

**Author:** ![ypolyakov](https://yyz1.discourse-cdn.com/flex031/user_avatar/openfhe.discourse.group/ypolyakov/32/47_2.png) [@ypolyakov](https://openfhe.discourse.group/u/ypolyakov)\
**Post date:** [August 3, 2026, 6:47pm UTC](https://openfhe.discourse.group/t/bug-report-binfhecontext-switchcttoqn-accepts-malformed-large-dimension-ciphertexts-when-the-modulus-matches/2354/2 "2026-08-03T18:47:33Z")

</div>

Added to issue [Minor input validation issues · Issue #1234 · openfheorg/openfhe-development · GitHub](https://github.com/openfheorg/openfhe-development/issues/1234)
