# RFC: branch-free modular primitives on the decrypt path (constant-time hardening)

**URL:** <https://openfhe.discourse.group/t/rfc-branch-free-modular-primitives-on-the-decrypt-path-constant-time-hardening/2316>\
**Category:** Library Questions\
**Created:** [April 24, 2026, 11:00pm UTC](https://openfhe.discourse.group/t/rfc-branch-free-modular-primitives-on-the-decrypt-path-constant-time-hardening/2316 "2026-04-24T23:00:46Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![bader](https://avatars.discourse-cdn.com/v4/letter/b/13edae/32.png) [@bader](https://openfhe.discourse.group/u/bader)\
**Post date:** [April 24, 2026, 11:00pm UTC](https://openfhe.discourse.group/t/rfc-branch-free-modular-primitives-on-the-decrypt-path-constant-time-hardening/2316/1 "2026-04-24T23:00:46Z")

</div>

```auto
Hi all,

I've prepared a constant-time hardening of the modular primitives. Data-dependent branches are replaced with bitmask-select plus a compiler barrier that survives `-O0..-Ofast` on GCC and Clang.

## Branch

https://github.com/BAder82t/openfhe-development/tree/fix/ct-mod-mul-fast-const

This RFC closes the orthogonal timing-side channel by removing data-dependent branches from the same code region.

Methodology follows the PermNet-RM constant-time Reed-Muller encoder (https://github.com/BAder82t/PermNet-RM): explicit bitmask + inline-asm opacifier so aggressive optimization can't reconstruct the branch via value-range propagation.

Thanks

```

---

<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:** [April 29, 2026, 6:53pm UTC](https://openfhe.discourse.group/t/rfc-branch-free-modular-primitives-on-the-decrypt-path-constant-time-hardening/2316/2 "2026-04-29T18:53:02Z")

</div>

Hi @bader Thank you for posting this. I’ve added your github account to the openfhe-development repository. Please create a branch there from `dev` and submit a PR so we could review it (and run CI against it).

---

<div class="post-metadata">

**Author:** ![bader](https://avatars.discourse-cdn.com/v4/letter/b/13edae/32.png) [@bader](https://openfhe.discourse.group/u/bader)\
**Post date:** [April 29, 2026, 8:14pm UTC](https://openfhe.discourse.group/t/rfc-branch-free-modular-primitives-on-the-decrypt-path-constant-time-hardening/2316/3 "2026-04-29T20:14:58Z")

</div>

Thanks @ypolyakov, all done.

> <https://github.com/openfheorg/openfhe-development/pull/1177>
>
> \## Summary
> 
> This PR removes data-dependent branches from modular arithmetic us…ed during \`Decrypt\`. Branches whose direction depends on secret values can leak information through timing, so this change replaces them with branchless bitmask-based equivalents.
> 
> The new helpers also include a small inline-asm barrier for GCC and Clang to make it harder for the optimizer to rewrite the branchless form back into conditional branches at higher optimization levels.
> 
> \## What changes
> 
> \### New header — \`src/core/include/utils/constanttime.h\`
> 
> Adds four small inline helpers in namespace \`lbcrypto::ct\`. Each helper implements the same operation as the original conditional logic, but uses a mask derived from the sign bit or borrow bit instead of a branch.
> 
> | Helper | Replaces |
> |---|---|
> | \`AddIfNeg(x, m)\` | \`x \< 0 ? x + m : x\` |
> | \`SubIfGE(x, m)\` | \`x \>= m ? x - m : x\` |
> | \`ModSubFast(a, b, m)\` | \`a - b mod m\` |
> | \`SubIfAboveHalf(x, m, halfQ)\` | centered-lift correction when \`x \> halfQ\` |
> 
> \### Call sites — \`src/core/include/math/hal/intnat/ubintnat.h\`
> 
> The existing public functions are preserved; only their internals change.
> 
> \- \`ModAddFast\` / \`ModAddFastEq\` now call \`ct::SubIfGE\`
> \- \`ModSubFast\` / \`ModSubFastEq\` now call \`ct::ModSubFast\`
> \- \`ModMulFastConst\` / \`ModMulFastConstEq\` now call \`ct::AddIfNeg\`
> 
> \### NTT butterflies — \`src/core/include/math/hal/intnat/transformnat-impl.h\`
> 
> The NTT butterflies previously had separate GCC and non-GCC implementations, each with its own conditional fixups such as:
> 
> \`\`\`cpp
> if (hiVal \>= modulus) hiVal -= modulus;
> if (loVal \< omegaFactor) loVal += modulus;
