Summary
RSA-PSS methods returning Future report an invalid negative saltLength through different error channels depending on the backend.
The native FFI backend throws ArgumentError synchronously before returning a Future. Browser backends return a Future that completes with ArgumentError.
This affects:
RsaPssPrivateKey.signBytes
RsaPssPrivateKey.signStream
RsaPssPublicKey.verifyBytes
RsaPssPublicKey.verifyStream
Reproduction
import 'package:webcrypto/webcrypto.dart';
Future<void> main() async {
final pair = await RsaPssPrivateKey.generateKey(
1024,
BigInt.from(65537),
Hash.sha256,
);
Future<List<int>>? result;
try {
result = pair.privateKey.signBytes([1, 2, 3], -1);
print('returned a Future');
} catch (error) {
print('synchronous error: ${error.runtimeType}');
}
if (result != null) {
try {
await result;
} catch (error) {
print('asynchronous error: ${error.runtimeType}');
}
}
}
Native VM output:
synchronous error: ArgumentError
Chrome Dart2JS and Dart2Wasm output:
returned a Future
asynchronous error: ArgumentError
Expected behavior
The RSA-PSS methods return Future, so invalid saltLength errors should be delivered through the returned Future consistently on every backend.
Actual behavior
The FFI implementation validates saltLength inside non-async signStream and verifyStream methods. The public methods directly return these backend calls, allowing the native validation error to escape synchronously.
The browser implementations are async, so the same validation error becomes an asynchronous Future error.
As a result, error handling such as:
key.signBytes(data, -1).catchError(handleError);
can handle the error in browsers but is bypassed on native platforms because he method throws before catchError can be attached.
Suggested fix
Make the four public RSA-PSS signing and verification wrappers asynchronous and await their backend calls. This converts any synchronous backend validation failure into a failed Future without changing the existing exception type.
Add regression coverage that verifies:
- each method returns a Future without throwing synchronously;
- awaiting that Future throws
ArgumentError;
- behavior is consistent on VM, Chrome Dart2JS, and Chrome Dart2Wasm.
Summary
RSA-PSS methods returning
Futurereport an invalid negativesaltLengththrough different error channels depending on the backend.The native FFI backend throws
ArgumentErrorsynchronously before returning aFuture. Browser backends return aFuturethat completes withArgumentError.This affects:
RsaPssPrivateKey.signBytesRsaPssPrivateKey.signStreamRsaPssPublicKey.verifyBytesRsaPssPublicKey.verifyStreamReproduction
Native VM output:
Chrome Dart2JS and Dart2Wasm output:
Expected behavior
The RSA-PSS methods return
Future, so invalidsaltLengtherrors should be delivered through the returned Future consistently on every backend.Actual behavior
The FFI implementation validates
saltLengthinside non-asyncsignStreamandverifyStreammethods. The public methods directly return these backend calls, allowing the native validation error to escape synchronously.The browser implementations are
async, so the same validation error becomes an asynchronous Future error.As a result, error handling such as:
can handle the error in browsers but is bypassed on native platforms because he method throws before
catchErrorcan be attached.Suggested fix
Make the four public RSA-PSS signing and verification wrappers asynchronous and await their backend calls. This converts any synchronous backend validation failure into a failed Future without changing the existing exception type.
Add regression coverage that verifies:
ArgumentError;