google / google/webcrypto.dart
bug: native RSA-PSS Future methods throw invalid salt lengths synchronously
- Dominant language
- Dart
- Stars
- 116
- Forks
- 110
- Avg merge
- 6d 8h
- Merged PRs (30d)
- 9
Description
## 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
```dart
import 'package:webcrypto/webcrypto.dart';
Future main() async {
final pair = await RsaPssPrivateKey.generateKey(
1024,
BigInt.from(65537),
Hash.sha256,
);
Future>? 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:
```text
synchronous error: ArgumentError
```
Chrome Dart2JS and Dart2Wasm output:
```text
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:
```dart
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.
Contributor guide
Research direction
Start at the public RsaPssPrivateKey.signBytes/signStream and RsaPssPublicKey.verifyBytes/verifyStream wrappers, then inspect the FFI signStream and verifyStream implementations where saltLength is validated. Add regression coverage for synchronous return and asynchronous ArgumentError behavior, and run it on VM, Chrome Dart2JS, and Chrome Dart2Wasm.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- dart
- Domain
- cryptography
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 72/100