RSA — countermeasures
- Spec:
RFC 8017 (PKCS#1 v2.2) [MKJR16], FIPS 186-5 [NationalIoSaTechnology23] (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 ([BDL97]) 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.
Coverage matrix
Threat |
Status |
Countermeasure(s) |
|---|---|---|
Bellcore single-fault on RSA-CRT |
vulnerable |
Plan |
SPA on modular exponentiation |
partial — square-always pattern present in
|
Plan |
DPA on Montgomery multiplication |
vulnerable |
Plan |
Timing attacks on bigint operations |
partial |
Same plan |
SIFA on the verify-then-output check (when added) |
n/a (not present yet) |
Will be addressed by infective computation [JT07] 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 [Ble98]) |
Plan |
Bellcore single-fault attack — T1-C
Principle
In CRT-RSA decryption / signing, the secret operation is split:
Boneh-DeMillo-Lipton ([BDL97]) showed that if the
attacker can fault either of the two half-exponentiations to
produce S' ≠ S, then:
— 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
[ABF+02] (CHES 2002) introduced the standard defence. Sketch:
Choose a small prime
t(e.g. 32 bits) coprime withe.Compute the half-exponentiations modulo
p·tandq·tinstead ofpandq— call the resultsS_ptandS_qt.Reduce
S_pt mod tandS_qt mod tand check they match the expected value. A fault on either half-exponentiation will change the result modtwith overwhelming probability.CRT-combine
S_pt mod pandS_qt mod qto produce the final signature.
The recent formal-verification effort [RG13] 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 [RGN16] 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
[JT07] 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
Add a CRT-decrypt API in
rsa::rsa::rsa_decrypt_crttaking the existingRsaSecretKey(which already holdsp,q,dp,dq,qinv).Implement the Aumüller skeleton on top:
Generate
tat random (32 bits, coprime withe); 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 tand same forS_qt. Usesilentops::ct_eqfor 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 pandS_qt mod q. If either fails, return a deterministic error — never a partial / faulted signature, since that IS the Bellcore output.
The PKCS#1 v1.5 / OAEP / PSS layers above
rsa_decrypt_rawtransparently inherit the protection.Tests: KAT regression + a fault-injection unit test that patches
S_ptmid-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 innercarrypropagation must not collapse to early-exit on zero carry. Audit checkpoint:cargo asmof 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 [Wal02]. Apply the sameblack_boxshielding asecc::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 withreturn Lesson 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 isa^(p-2) mod pfor 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 theecc::field::field_powside; copy the pattern over, includingblack_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 [Cor99b]:
Draw a fresh random
rfrom the RNG, computec' = c · r^e mod N(cheap, sinceeis small).Decrypt
c'instead ofc:m' = (c')^d mod N = m · r mod N.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
[Ble98]. 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
Path |
Today (2026-04-21) |
Target (post |
|---|---|---|
|
Mostly CT but unaudited; |
Audited CT, |
|
Plain CRT, no fault check, no blinding |
Aumüller + message blinding + exponent blinding |
|
Bleichenbacher surface unaudited |
RFC 8017 §7.2.2 CT padding-oracle handling |
|
|
Confirmed CT, regression-locked |
|
CT-clean (no secret-dependent branches in PSS itself) |
No change; relies on hardened |
Reading list
In addition to the cited papers above:
[Cor99b] — original message-blinding paper.
[BB03] — the canonical remote timing attack on OpenSSL RSA, motivating Montgomery’s CT.
[Wal02] — the Walter attack on Montgomery’s final-subtract leakage.
[Ble98] — Bleichenbacher’s PKCS#1 v1.5 oracle.
[Gir06] — RSA safe-error attacks (a fault on the dummy multiply in square-always reveals which multiplies are dummies, leaking
d); motivates blindingdon top of message blinding.