Skip to content

Conversation

h-filali
Copy link
Contributor

@h-filali h-filali commented Oct 9, 2025

This commit adds hardening for the internal scalar point multiplication.
Before the shares would be combined in the main loop to decide whether 0, P or 2P should be added.
This commit determines this without combining the shares. If the MSB of both shares are both 1 we want to add 2P. If only one bit is 1 we want to add P.
If both bits are 0 we want to add 0.

In a simplified way this is done as explained below.

In a first step we pick P if MSB(d0) ^ MSB(d1) is 1 and 2P otherwise. This is done as follows:
p_temp0 = d0 ? (d1 ? 2P : P) : (d1 ? P : 2P)

In a second step we pick between p_temp and 0.
If MSB(d0) | MSB(d1) = 1 then we pick p_temp otherwise we pick 0. This is done as follows:
p_temp1 = d0 ? p_temp0 : 0
p_temp2 = d1 ? p_temp0 : p_temp1

Thanks to @siemen11 for implementing this for p256 and letting me draw inspiration #28248.

@h-filali h-filali requested a review from a team as a code owner October 9, 2025 12:08
@h-filali h-filali requested review from johannheyszl, moidx and nasahlpa and removed request for a team October 9, 2025 12:08
@nasahlpa nasahlpa requested a review from siemen11 October 9, 2025 12:09
@h-filali h-filali force-pushed the cryptolib-p384-harden-bnsel branch from 05dd29f to 423d12e Compare October 9, 2025 12:10
Copy link
Member

@nasahlpa nasahlpa left a comment

Choose a reason for hiding this comment

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

In general, the logic looks fine to me.

Could you please have a look why the tests are failing?

This commit adds hardening for the internal scalar point
multiplication.
Before the shares would be combined in the main loop to decide
whether 0, P or 2P should be added.
This commit determines this without combining the shares.
If the MSB of both shares are both 1 we want to add 2P.
If only one bit is 1 we want to add P.
If both bits are 0 we want to add 0.

In a simplified way this is done as explained below.

In a first step we pick P if MSB(d0) ^ MSB(d1) is 1 and 2P otherwise.
This is done as follows:
p_temp0 = d0 ? (d1 ? 2P : P) : (d1 ? P : 2P)

In a second step we pick between p_temp and 0.
If MSB(d0) | MSB(d1) = 1 then we pick p_temp otherwise we pick 0.
This is done as follows:
p_temp1 = d0 ? p_temp0 : 0
p_temp2 = d1 ? p_temp0 : p_temp1

Signed-off-by: Hakim Filali <[email protected]>
@h-filali h-filali force-pushed the cryptolib-p384-harden-bnsel branch from 423d12e to acc92fc Compare October 9, 2025 14:10
@h-filali
Copy link
Contributor Author

h-filali commented Oct 9, 2025

In general, the logic looks fine to me.

Could you please have a look why the tests are failing?

Thanks @nasahlpa !
I had to rebase, that's why the instruction count checks were failing again. It's fixed now.

@johannheyszl
Copy link
Contributor

thanks @h-filali this is a very helpful addition.

can we please mention the impact on code size (ca. 300 Bytes?) and runtime (seem ok, ca. 30k)

bn.lid x2, 448(x30)

/* Select whether P or 2P will be stored in dmem[p_temp1].
([w21,w20], [w19,w18], [w17, w16]) <= M ? 2P : P */
Copy link
Contributor

Choose a reason for hiding this comment

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

target regs were used for Q in last iteration; no horizontal leakage; no randomisation prior use required

Copy link
Contributor

@johannheyszl johannheyszl left a comment

Choose a reason for hiding this comment

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

rhanks @h-filali great work

@h-filali
Copy link
Contributor Author

The execution times change from

  • kModeKeygenInsCnt = 1899012
  • kModeKeygenSideloadInsCnt = 1898906
  • kModeEcdhInsCnt = 1910611
  • kModeEcdhSideloadInsCnt = 1910760
  • kModeEcdsaSignInsCnt = 1546541
  • kModeEcdsaSignSideloadInsCnt = 1546690

to:

  • kModeKeygenInsCnt = 1935430
  • kModeKeygenSideloadInsCnt = 1935324
  • kModeEcdhInsCnt = 1947029
  • kModeEcdhSideloadInsCnt = 1947178
  • kModeEcdsaSignInsCnt = 1574769
  • kModeEcdsaSignSideloadInsCnt = 1574918

The mem size goes from 96'961 to 97'233 Bytes

@h-filali
Copy link
Contributor Author

Thanks @nasahlpa and @johannheyszl for leaving a review!

Copy link
Member

@nasahlpa nasahlpa left a comment

Choose a reason for hiding this comment

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

Thanks for fixing the CI failure. The code looks good to me.


/* select either Q or Q_a
if M: Q = ([w21,w20], [w19,w18], [w17,w16]) <= Q else: Q <= Q_a */
if M: Q_temp = ([w21,w20], [w19,w18], [w17,w16]) <= Q_a else: Q_temp <= Q */
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: I was a bit confused just with the comments, Q_a was not defined before

Copy link
Contributor

@siemen11 siemen11 left a comment

Choose a reason for hiding this comment

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

Looks very nice, a very good change that will better protect p384! Thank you!

/* select either Q or Q_a
if M: Q = ([w21,w20], [w19,w18], [w17,w16]) <= Q else: Q <= Q_a */
if M: Q_temp = ([w21,w20], [w19,w18], [w17,w16]) <= Q_a else: Q_temp <= Q */
bn.sel w16, w25, w6, M
Copy link
Contributor

Choose a reason for hiding this comment

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

w16 to w21 contained the decision P or 2P, is that overwrite safe? Could definitely be, but I wasn't sure myself

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.

4 participants