Skip to content

BlindRSA.blindSign() skips the RSAVP1 consistency check on the RSA-RAW path #55

Description

@aleister1102

BlindRSA.blindSign() currently takes a different code path when supportsRSARAW is enabled, skipping the post-sign RSAVP1 consistency check present in the software signing path.

Source references

  • supportsRSARAW is an exposed platform parameter in src/index.ts:

    • blindrsa-ts/src/index.ts

      Lines 128 to 139 in db4ed09

      export function getSuiteByName<T>(
      newT: {
      new (params: BlindRSAParams & BlindRSAPlatformParams): T;
      },
      name: string,
      params: BlindRSAPlatformParams = { supportsRSARAW: false },
      ): T {
      for (const suiteParams of Object.values(Params)) {
      if (name.toLowerCase() === suiteParams.name.toLowerCase()) {
      return new newT({ ...suiteParams, ...params });
      }
      }
  • In src/blindrsa.ts, the RSA-RAW path returns immediately:

    • blindrsa-ts/src/blindrsa.ts

      Lines 149 to 152 in db4ed09

      async blindSign(privateKey: CryptoKey, blindMsg: Uint8Array): Promise<Uint8Array> {
      if (this.params.supportsRSARAW) {
      return rsaRawBlingSign(privateKey, blindMsg);
      }
  • The non-RSA-RAW path in the same function performs the expected verification and throws on mismatch:

    • blindrsa-ts/src/blindrsa.ts

      Lines 166 to 178 in db4ed09

      // 1. m = bytes_to_int(blinded_msg)
      const m = os2ip(blindMsg);
      // 2. s = RSASP1(sk, m)
      const s = rsasp1(sk, m);
      // 3. m' = RSAVP1(pk, s)
      const mp = rsavp1(pk, s);
      // 4. If m != m', raise "signing failure" and stop
      if (!m.equals(mp)) {
      throw new Error('signing failure');
      }
  • The helper used by the RSA-RAW branch, rsaRawBlingSign(), just calls crypto.subtle.sign(...) and returns the result directly:

    • blindrsa-ts/src/util.ts

      Lines 320 to 338 in db4ed09

      export async function rsaRawBlingSign(
      privateKey: CryptoKey,
      blindMsg: Uint8Array,
      ): Promise<Uint8Array> {
      if (privateKey.algorithm.name !== NATIVE_SUPPORT_NAME) {
      privateKey = await crypto.subtle.importKey(
      'pkcs8',
      await crypto.subtle.exportKey('pkcs8', privateKey),
      { ...privateKey.algorithm, name: NATIVE_SUPPORT_NAME },
      privateKey.extractable,
      privateKey.usages,
      );
      }
      const signature = await crypto.subtle.sign(
      { name: privateKey.algorithm.name },
      privateKey,
      blindMsg.slice().buffer,
      );
      return new Uint8Array(signature);
  • For comparison, PartiallyBlindRSA still performs the RSAVP1/equality check even when it uses rsaRawBlingSign():

    • if (this.params.supportsRSARAW) {
      const { privateKey } = await PartiallyBlindRSA.bigKeyPairToCryptoKeyPair(
      { secretKey: sk_derived, publicKey: pk_derived },
      {
      modulusLength: kLen * 8,
      publicExponent: new Uint8Array([1, 0, 1]),
      hash: this.params.hash,
      },
      true,
      );
      s = os2ip(await rsaRawBlingSign(privateKey, blindMsg));
      } else {
      s = rsasp1(sk_derived, m);
      }
      // 4. m' = RSAVP1(pk_derived, s)
      const mp = rsavp1(pk_derived, s);
      // 5. If m != m', raise "signing failure" and stop
      if (!m.equals(mp)) {
      throw new Error('signing failure');
      }

Issue

This means BlindRSA.blindSign() behaves inconsistently across implementations:

  • software path: signs, verifies m == RSAVP1(s), rejects on mismatch
  • RSA-RAW path: returns the signer output without that verification

If the RSA-RAW backend ever returns a faulty signature, this path will propagate it to the caller instead of failing with signing failure.

I am intentionally keeping the claim narrow here: this issue is the missing validation step in the RSA-RAW blind-signing path, not a claim of demonstrated real-world fault injection in a specific deployment. But the code-level inconsistency is real and security-relevant.

Suggested fix

Apply the same RSAVP1 consistency check to the RSA-RAW result before returning it, as is already done in the non-RSA-RAW BlindRSA path and in PartiallyBlindRSA.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions