Skip to content

fix: guard RsaUnPad_PSS masking when bits==0#10256

Open
MarkAtwood wants to merge 1 commit intowolfSSL:masterfrom
MarkAtwood:fix/rsa-pss-unpad-bits-zero
Open

fix: guard RsaUnPad_PSS masking when bits==0#10256
MarkAtwood wants to merge 1 commit intowolfSSL:masterfrom
MarkAtwood:fix/rsa-pss-unpad-bits-zero

Conversation

@MarkAtwood
Copy link
Copy Markdown

Summary

When (mp_count_bits(n) - 1) & 0x7 == 0 (i.e. the modulus bit count is congruent to 1 mod 8 — e.g. a 2041-bit or 3073-bit key), RsaUnPad_PSS advances past the leading zero byte and then executes:

tmp[0]       &= (byte)((1 << bits) - 1);   // bits == 0 → &= 0
pkcsBlock[0] &= (byte)((1 << bits) - 1);   // bits == 0 → &= 0

(1 << 0) - 1 == 0 zeros both bytes, corrupting maskedDB[0] before the XOR separator comparison. RsaPad_PSS already guards the equivalent step with if (hiBits) for exactly this reason.

Fix: add if (bits) guard, mirroring RsaPad_PSS.

Standard power-of-two key sizes (1024, 2048, 4096-bit) are unaffected — their bit counts give bits == 7 after the recomputation. The bug affects non-standard key sizes loaded from external DER where mp_count_bits(n) % 8 == 1. Cross-library PSS verification (verifying a Go/OpenSSL signature with wolfSSL) surfaces this asymmetry.

Test plan

  • PSS verify with a non-standard key (mp_count_bits % 8 == 1) against a signature from OpenSSL passes
  • Existing RSA PSS verify tests unaffected

/cc @wolfSSL-Fenrir-bot please review

When the RSA modulus bit count is congruent to 1 mod 8 (e.g. a 2041-bit
or 3073-bit key), (mp_count_bits(n) - 1) & 0x7 == 0, so the leading
zero byte has already been consumed and the pointer advanced. In that
case (1<<0)-1 == 0, zeroing tmp[0] and pkcsBlock[0] and corrupting
maskedDB[0] before the XOR separator comparison.

RsaPad_PSS guards the equivalent step with 'if (hiBits)'. Add the same
guard to RsaUnPad_PSS.

Standard power-of-two key sizes (1024, 2048, 4096-bit) are unaffected
as their bit counts give bits==7 after the recomputation. The bug
affects non-standard key sizes loaded from external DER where
mp_count_bits(n) % 8 == 1.
@MarkAtwood MarkAtwood requested review from SparkiDev and Copilot April 17, 2026 22:09
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Fixes RSA-PSS unpadding for non-standard modulus bit sizes where bits == 0 to avoid zeroing masked bytes during the MSB masking step.

Changes:

  • Add an if (bits) guard around MSB masking in RsaUnPad_PSS to prevent (1 << 0) - 1 from zeroing tmp[0] and pkcsBlock[0].
  • Add an explanatory comment describing why the masking is skipped when bits == 0.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread wolfcrypt/src/rsa.c
Comment on lines +1885 to +1889
/* When bits==0 the key size is an exact multiple of 8 and pkcsBlock was
* already advanced past the leading 0x00 byte (see above); no masking is
* needed. (1<<0)-1 == 0 would zero both bytes and corrupt the XOR
* separator check below. RsaPad_PSS guards the same step with
* "if (hiBits)" for the same reason. */
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

The comment’s statement “the key size is an exact multiple of 8” appears inconsistent with how bits is derived for RSA-PSS MSB masking. With the common pattern bits = (mp_count_bits(n) - 1) & 0x7, bits == 0 corresponds to mp_count_bits(n) ≡ 1 (mod 8), not a multiple of 8. Suggest updating the comment to describe the actual condition (e.g., modulus bit length congruent to 1 mod 8, and the leading 0x00 handling) so future readers don’t infer the wrong key-size relationship.

Suggested change
/* When bits==0 the key size is an exact multiple of 8 and pkcsBlock was
* already advanced past the leading 0x00 byte (see above); no masking is
* needed. (1<<0)-1 == 0 would zero both bytes and corrupt the XOR
* separator check below. RsaPad_PSS guards the same step with
* "if (hiBits)" for the same reason. */
/* When bits==0, the modulus bit length is congruent to 1 mod 8, so the
* encoded block includes a leading 0x00 byte and pkcsBlock was already
* advanced past it (see above); no masking is needed. (1<<0)-1 == 0
* would zero both bytes and corrupt the XOR separator check below.
* RsaPad_PSS guards the same step with "if (hiBits)" for the same
* reason. */

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown

MemBrowse Memory Report

No memory changes detected for:

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.

2 participants