Skip to content
This repository was archived by the owner on May 27, 2026. It is now read-only.

[crypto] Harden RSA key import CRT checks with random mask #226

Draft
mkannwischer wants to merge 2 commits into
masterfrom
mjk/rsa-check-key-hardening
Draft

[crypto] Harden RSA key import CRT checks with random mask #226
mkannwischer wants to merge 2 commits into
masterfrom
mjk/rsa-check-key-hardening

Conversation

@mkannwischer

Copy link
Copy Markdown
Contributor

As proposed in #170, this PR
multiplies a nonzero random mask into the d_p, d_q, and i_q
validity checks. Instead of computing e * d_p mod (p-1) and
comparing to 1, we now computes r * e * d_p mod (p-1) and
the C side compares against r.

This avoids the multi-limb value 1 as an intermediate or
comparison target, hardening the check against fault injection.

I have also added additional negative tests that test these failures cases for invalid d_q and i_q.

Comment on lines +225 to +227
bn.wsrr w20, URND
bn.rshi w20, w31, w20 >> 1
bn.addi w20, w20, 1

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is a 256-bit (or rather-255 bit) random mask sufficient here, or do we need a full-width masks?
If we need a full width masks, then we would need different masks mod p, mod q, and mod p-1.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When Jade discussed the core issue in #170 with me, it sounded like much of the FI potential came from all but the lowest limb of the check value being zero in the "check pass" case, since faulting a word to zero can be easier than faulting to an arbitrary desired value. As such, I think it could make sense to expand the mask to additional limbs.

Would it maybe work to have one fixed, half-modulus sized mask for each of p, q, and p-1? The mod routine as invoked should handle reducing the intermediate products as long as the limb counts remain the same, and even though the resulting check values won't be quite uniformly distributed, this should be okay for just mitigating FI attacks (cc @jadephilipoom to make sure this isn't entirely off base).

If I'm missing something and that doesn't work, we could also consider e.g. a mask 1/4th of the modulus size, just to ensure the check value has several non-zero limbs while remaining reduced modulo {p, q, p-1}?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's basically a performance/security tradeoff. A larger mask would be better for FI protection but need more time and memory to multiply. Having a multi-bit value is better than a single-bit value, and a multi-limb value is better than a single-limb value. I think the delta between single-bit and multi-bit is basically that with single-bit, an attacker could chain a fault that zeroes the target registers and a single-bit fault to flip the 1 bit in order to get the checks to pass, regardless of what the value actually was. With a multi-bit value that's already a lot harder.

One way to improve the FI defense without extending the mask would be to check on ACC itself that the high limbs are 0 after the modular reduction; this would shorten the attack window and minimize the attack surface for an attacker to hide nonzero high limbs, compared to checking that they are zero on Ibex after reading them back.

Multiply a nonzero random mask into the d_p, d_q, and i_q
validity checks. Instead of computing e * d_p mod (p-1) and
comparing to 1, we now computes r * e * d_p mod (p-1) and
the C side compares against r.

This avoids the multi-limb value 1 as an intermediate or
comparison target, hardening the check against fault injection.

Signed-off-by: Matthias J. Kannwischer <matthias@zerorisc.com>
The existing tests only checked rejection of an invalid d_p.
Add test cases for invalid d_q and i_q as well, each constructed
by flipping an arbitrary single bit in the valid test vector.

Signed-off-by: Matthias J. Kannwischer <matthias@zerorisc.com>
@mkannwischer mkannwischer force-pushed the mjk/rsa-check-key-hardening branch from a4bf20a to c4175a4 Compare April 21, 2026 06:47
@mkannwischer mkannwischer marked this pull request as ready for review April 21, 2026 10:19

@pqcfox pqcfox left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks excellent! Added a note re: the masking, let me know if my thoughts there seem reasonable--thanks!

Comment on lines +225 to +227
bn.wsrr w20, URND
bn.rshi w20, w31, w20 >> 1
bn.addi w20, w20, 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When Jade discussed the core issue in #170 with me, it sounded like much of the FI potential came from all but the lowest limb of the check value being zero in the "check pass" case, since faulting a word to zero can be easier than faulting to an arbitrary desired value. As such, I think it could make sense to expand the mask to additional limbs.

Would it maybe work to have one fixed, half-modulus sized mask for each of p, q, and p-1? The mod routine as invoked should handle reducing the intermediate products as long as the limb counts remain the same, and even though the resulting check values won't be quite uniformly distributed, this should be okay for just mitigating FI attacks (cc @jadephilipoom to make sure this isn't entirely off base).

If I'm missing something and that doesn't work, we could also consider e.g. a mask 1/4th of the modulus size, just to ensure the check value has several non-zero limbs while remaining reduced modulo {p, q, p-1}?

@mkannwischer mkannwischer marked this pull request as draft April 24, 2026 07:19
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[crypto] Harden RSA private key import check values from OTBN

3 participants