Skip to content

fix: branchless modular arithmetic to close NTT timing side-channel#1210

Closed
BAder82t wants to merge 1 commit into
openfheorg:devfrom
BAder82t:fix/ct-modular-arithmetic-hot-path
Closed

fix: branchless modular arithmetic to close NTT timing side-channel#1210
BAder82t wants to merge 1 commit into
openfheorg:devfrom
BAder82t:fix/ct-modular-arithmetic-hot-path

Conversation

@BAder82t

Copy link
Copy Markdown
Collaborator

Summary

Closes #1198.

  • MultipartyDecryptFusion calls INTT on secret-correlated polynomial b = cv[0] + s·cv[1] + noise
  • Branches in the butterfly (e.g. if (loVal < omegaFactor) loVal += modulus) leak key bits via timing
  • PR security: branch-free modular primitives on decrypt hot path #1177 fixed this with __asm__ volatile barriers but caused 1.5–1.9× NTT regression

This PR uses the branchless mask idiom instead — no barriers, vectorisation preserved.

Changes

  • new utils/constanttime.hSubIfGE, ModSubFast, AddIfNeg CT primitives
  • ubintnat.h — 10 functions patched: ModAddFast/Eq, ModSubFast/Eq, ModMulFastConst/Eq, ModMulFast/Eq
  • transformnat-impl.h — all 9 butterfly sites made branchless; #ifdef __GNUC__ splits removed
  • UnitTestNTT.cpp — test added for zero-coefficient input path

Benchmarks (Apple M-series, clang -O3)

Before This PR PR #1177
ModAddFast (scalar) 0.18 ns 0.17 ns 0.5 ns
ModSubFast (scalar) 0.20 ns 0.18 ns 0.5 ns
NTT n=4096 0.012 ms 0.012 ms (+4%) 0.017 ms (+49%)
NTT n=16384 0.057 ms 0.060 ms (+5%) 0.087 ms (+52%)

2051/2051 tests pass.

Known limitations (pre-existing)

  • WASM builds: MultD carry-detect branches remain data-dependent; documented in constanttime.h
  • Non-power-of-2 rings: Bluestein INTT path not covered (out of scope for standard BGV/BFV/CKKS)

Closes openfheorg#1198. MultipartyDecryptFusion calls INTT on secret-correlated
polynomial b = cv[0] + s*cv[1] + noise. Secret-dependent conditional
branches in the butterfly (e.g. if (loVal < omegaFactor) loVal += mod)
are timing oracles attackable via adaptive-chosen-ciphertext.

New utils/constanttime.h adds SubIfGE, ModSubFast, AddIfNeg using the
branchless mask idiom (no asm barriers, vectorisation preserved). Ten
functions in ubintnat.h patched. All nine butterfly sites in
transformnat-impl.h made branchless; #ifdef __GNUC__ compiler-dispatch
splits and if (omegaFactor != zero) shortcut removed. NTT test added
for zero-coefficient input path.

Benchmarks vs original (clang -O3, Apple M-series):
  ModAddFast scalar:   -3%  |  NTT n=4096:  +4%  |  NTT n=16384: +5%
  (PR openfheorg#1177 asm-barrier approach regressed NTT by 49-52%)

Known pre-existing limitations documented in constanttime.h:
  WASM: MultD carry-detect branches remain data-dependent.
  Non-power-of-2 rings: Bluestein INTT path not covered.
@BAder82t BAder82t closed this Jun 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant