Skip to content

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

Description

@harrshita123

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions