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:
|
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:
|
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:
|
// 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:
|
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.
BlindRSA.blindSign()currently takes a different code path whensupportsRSARAWis enabled, skipping the post-signRSAVP1consistency check present in the software signing path.Source references
supportsRSARAWis an exposed platform parameter insrc/index.ts:blindrsa-ts/src/index.ts
Lines 128 to 139 in db4ed09
In
src/blindrsa.ts, the RSA-RAW path returns immediately:blindrsa-ts/src/blindrsa.ts
Lines 149 to 152 in db4ed09
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
The helper used by the RSA-RAW branch,
rsaRawBlingSign(), just callscrypto.subtle.sign(...)and returns the result directly:blindrsa-ts/src/util.ts
Lines 320 to 338 in db4ed09
For comparison,
PartiallyBlindRSAstill performs theRSAVP1/equality check even when it usesrsaRawBlingSign():blindrsa-ts/src/partially_blindrsa.ts
Lines 182 to 203 in db4ed09
Issue
This means
BlindRSA.blindSign()behaves inconsistently across implementations:m == RSAVP1(s), rejects on mismatchIf 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
RSAVP1consistency check to the RSA-RAW result before returning it, as is already done in the non-RSA-RAWBlindRSApath and inPartiallyBlindRSA.