google / google/webcrypto.dart

bug: native RSA-PSS Future methods throw invalid salt lengths synchronously

Open
#401 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.