################################################################### RSA — countermeasures ################################################################### :Spec: RFC 8017 (PKCS#1 v2.2) :cite:`rfc8017`, FIPS 186-5 :cite:`fips186_5` (signature scheme rules) :Crate path: ``arcana::rsa::*`` (``bigint``, ``rsa``, ``pkcs1``, ``oaep``, ``pss``) :Cargo feature: none — RSA is unconditionally compiled. The RSA decrypt / sign path on arcana is currently **fully unprotected** against fault injection. It is also only partially audited for SCA on the bigint layer. Because the **Bellcore** single-fault attack on RSA-CRT (:cite:`boneh1997dfa`) is a one-fault total break, this is the **highest-priority evaluation gap** on the classical side and is item ``T1-C`` in the workspace roadmap. .. contents:: :local: :depth: 2 Coverage matrix =============== .. list-table:: RSA countermeasure / threat matrix :header-rows: 1 :widths: 25 18 57 * - Threat - Status - Countermeasure(s) * - Bellcore single-fault on RSA-CRT - **vulnerable** - Plan ``T1-C``: Aumüller's algorithm (:cite:`aumuller2002rsa_crt`), formally verified (:cite:`rauzy2013_formal_crt_rsa`, :cite:`rauzy2016_algorithmic_crt_rsa`). * - SPA on modular exponentiation - **partial** — square-always pattern present in ``rsa::bigint::pow_mod`` but not formally audited - Plan ``T1-E``: full ``rsa::bigint`` audit + ``black_box`` shielding on conditional subtracts. * - DPA on Montgomery multiplication - **vulnerable** - Plan ``T2-I``: message blinding (multiply ``c`` by ``r^e mod n`` before decrypt, divide by ``r`` after). :cite:`coron1999rsa_blinding` is the reference. * - Timing attacks on bigint operations - **partial** - Same plan ``T1-E``. Brumley-Boneh :cite:`brumley2003remote_timing` is the canonical baseline. * - SIFA on the verify-then-output check (when added) - n/a (not present yet) - Will be addressed by infective computation :cite:`joye2007rsa_infective` rather than test-then-branch. * - Padding-oracle attacks (PKCS#1 v1.5, OAEP) - **partial** — PKCS#1 v1.5 decrypt has a known oracle surface (Bleichenbacher :cite:`bleichenbacher1998rsa`) - Plan ``T2-J``: integrate the FIPS-recommended PKCS#1 v1.5 constant-time padding-oracle handling. OAEP is more robust by design. Bellcore single-fault attack — ``T1-C`` ======================================= Principle --------- In CRT-RSA decryption / signing, the secret operation is split: .. math:: S_p = m^{d_p} \bmod p, \quad S_q = m^{d_q} \bmod q, \quad S = \mathrm{CRT}(S_p, S_q) \bmod N Boneh-DeMillo-Lipton (:cite:`boneh1997dfa`) showed that if the attacker can fault either of the two half-exponentiations to produce ``S' ≠ S``, then: .. math:: \gcd(N, S - S') \in \{p, q\} — i.e. **a single fault factorises N**. With ``p`` or ``q`` known the rest of the secret key (``d``, ``dp``, ``dq``, ``qinv``) is computed in milliseconds. Equipment cost: ~1 k€ for a Chipwhisperer voltage glitcher; days of bench time for a skilled operator. Aumüller's countermeasure ------------------------- :cite:`aumuller2002rsa_crt` (CHES 2002) introduced the standard defence. Sketch: 1. Choose a small prime ``t`` (e.g. 32 bits) coprime with ``e``. 2. Compute the half-exponentiations modulo ``p·t`` and ``q·t`` instead of ``p`` and ``q`` — call the results ``S_pt`` and ``S_qt``. 3. Reduce ``S_pt mod t`` and ``S_qt mod t`` and check they match the expected value. A fault on either half-exponentiation will change the result mod ``t`` with overwhelming probability. 4. CRT-combine ``S_pt mod p`` and ``S_qt mod q`` to produce the final signature. The recent formal-verification effort :cite:`rauzy2013_formal_crt_rsa` proves Aumüller's algorithm **resistant to all single-fault attacks** in a fault model that includes permanent memory faults and transient faults with or without forcing-to-zero. The follow-up :cite:`rauzy2016_algorithmic_crt_rsa` extends the formal framework to most published countermeasures and confirms that **most of them are broken in slightly richer fault models** — Aumüller is the gold standard for single-fault. For a stronger fault model (e.g. SIFA, multi-fault), the **infective computation** approach of Joye-Tunstall :cite:`joye2007rsa_infective` propagates the error multiplicatively into the final result rather than testing-then-branching, which is considered harder to skip with a control-flow fault. This is item ``T4-RSA-A`` in the deferred roadmap. Implementation route in arcana ------------------------------ 1. Add a CRT-decrypt API in ``rsa::rsa::rsa_decrypt_crt`` taking the existing ``RsaSecretKey`` (which already holds ``p``, ``q``, ``dp``, ``dq``, ``qinv``). 2. Implement the Aumüller skeleton on top: * Generate ``t`` at random (32 bits, coprime with ``e``); cached across calls if needed for performance. * Precompute ``dp_t = d mod (p-1)(t-1)``, ``dq_t = d mod (q-1)(t-1)``, ``e_inv_t = e^{-1} mod t``. * Compute ``S_pt = m^{dp_t} mod (p·t)``, ``S_qt = m^{dq_t} mod (q·t)``. * Verify ``S_pt^e mod t = m mod t`` and same for ``S_qt``. **Use** ``silentops::ct_eq`` **for the equality, not** ``==`` — the comparison itself must be CT to avoid leaking which half was faulted (relevant for SIFA). * If both checks pass, CRT-combine ``S_pt mod p`` and ``S_qt mod q``. If either fails, return a deterministic error — **never** a partial / faulted signature, since that IS the Bellcore output. 3. The PKCS#1 v1.5 / OAEP / PSS layers above ``rsa_decrypt_raw`` transparently inherit the protection. 4. Tests: KAT regression + a fault-injection unit test that patches ``S_pt`` mid-flight and asserts the error path fires. SCA on bigint (``T1-E``) ======================== The ``rsa::bigint::BigInt`` arithmetic primitives need a CT audit: * ``BigInt::mul`` — schoolbook multiplication: a fixed-iteration outer + inner loop. Should be CT by structure, but the inner ``carry`` propagation must not collapse to early-exit on zero carry. Audit checkpoint: ``cargo asm`` of the release build must show a fixed-cycle inner loop independent of operand bits. * ``BigInt::montgomery_mul`` — the conditional final subtract is the classical leak point :cite:`walter2002montgomery_leak`. Apply the same ``black_box`` shielding as ``ecc::field::reduce_wide``. * ``BigInt::sub`` — borrow propagation; same shielding. * ``BigInt::cmp`` / ``cmp_le`` — must be CT (no early exit). The current code uses a magnitude-compare with ``return Less`` on first differing limb — **this is a leak** and must be rewritten to a borrow-only pattern. * ``BigInt::mod_inv`` — extended Euclidean. The standard CT alternative is ``a^(p-2) mod p`` for prime moduli (Fermat's little theorem), reusing the Montgomery ladder. * ``BigInt::pow_mod`` — must be square-always with CT-select multiply (already the structure on the ``ecc::field::field_pow`` side; copy the pattern over, including ``black_box``). This audit is the prerequisite for both ``T1-C`` (Aumüller) and the later message-blinding ``T2-I``: the CT properties of bigint are load-bearing for any RSA hardening above. DPA on Montgomery multiplication — message blinding (``T2-I``) ============================================================== Even with Aumüller, DPA on the per-iteration Montgomery multiplications inside ``pow_mod`` extracts ``d`` over ~10⁵ – 10⁶ power traces (Hamming-weight CPA on the squared intermediates). The standard answer is **message blinding** :cite:`coron1999rsa_blinding`: 1. Draw a fresh random ``r`` from the RNG, compute ``c' = c · r^e mod N`` (cheap, since ``e`` is small). 2. Decrypt ``c'`` instead of ``c``: ``m' = (c')^d mod N = m · r mod N``. 3. Recover ``m = m' · r^{-1} mod N``. The DPA attacker now sees a different "blinded" message in every trace, so per-key averaging no longer aligns. A 64-bit ``r`` adds exponentiation cost of ``r^e mod N`` (negligible for typical ``e = 65 537``) plus one ``r^{-1} mod N`` which dominates if not amortised; in practice, ``r``-blinding is paired with **exponent blinding** ``d' = d + r' · phi(N)`` to also hide the bit pattern. This is layered *on top* of Aumüller (Aumüller defeats single-fault; blinding defeats DPA) and is item ``T2-I``. Padding-oracle hardening (``T2-J``) ==================================== PKCS#1 v1.5 decrypt is the historical Bleichenbacher target :cite:`bleichenbacher1998rsa`. The mitigation is to **always** return the same number of bytes regardless of whether the padding parsed correctly, with the data being an internally-derived deterministic dummy in the failure path. RFC 8017 §7.2.2 gives the recipe; arcana's current ``pkcs1::decrypt`` must be re-checked against it. OAEP is more robust by design but still requires CT comparison of the recovered ``H(L)`` against the parameter ``L``; arcana's ``oaep::decrypt`` should already use ``silentops::ct_eq`` (audit pending). Code path summary ================= .. list-table:: :header-rows: 1 :widths: 30 35 35 * - Path - Today (2026-04-21) - Target (post ``T1-C`` + ``T1-E`` + ``T2-I``) * - ``rsa::bigint::*`` - Mostly CT but unaudited; ``cmp`` has early-exit - Audited CT, ``black_box`` shielding throughout * - ``rsa::rsa::rsa_decrypt_raw`` - Plain CRT, no fault check, no blinding - Aumüller + message blinding + exponent blinding * - ``rsa::pkcs1::decrypt`` - Bleichenbacher surface unaudited - RFC 8017 §7.2.2 CT padding-oracle handling * - ``rsa::oaep::decrypt`` - ``ct_eq`` on ``H(L)`` audit pending - Confirmed CT, regression-locked * - ``rsa::pss::sign`` / ``pss::verify`` - CT-clean (no secret-dependent branches in PSS itself) - No change; relies on hardened ``rsa_decrypt_raw`` underneath Reading list ============ In addition to the cited papers above: * :cite:`coron1999rsa_blinding` — original message-blinding paper. * :cite:`brumley2003remote_timing` — the canonical remote timing attack on OpenSSL RSA, motivating Montgomery's CT. * :cite:`walter2002montgomery_leak` — the Walter attack on Montgomery's final-subtract leakage. * :cite:`bleichenbacher1998rsa` — Bleichenbacher's PKCS#1 v1.5 oracle. * :cite:`giraud2006rsa_safe_error` — RSA safe-error attacks (a fault on the dummy multiply in square-always reveals which multiplies are dummies, leaking ``d``); motivates blinding ``d`` on top of message blinding.