google / google/webcrypto.dart

bug: browser AES key generation exposes raw JavaScript errors for out-of-range lengths

Open
#403 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

### Description

`AesCbcSecretKey.generateKey`, `AesCtrSecretKey.generateKey`, and `AesGcmSecretKey.generateKey` handle out-of-range key lengths differently between the native and browser backends.

On the native backend, values outside the supported AES key lengths are rejected with a Dart `FormatException`. On Chrome, values outside the Web IDL `unsigned short` range reach `AesKeyGenParams.length`. Its `[EnforceRange]`
conversion throws a JavaScript `TypeError`, which escapes the package as a raw `JSObject`.

This makes error handling backend-dependent and exposes a JavaScript interop implementation detail through the public Dart API.

### Reproduction

```dart
import 'package:webcrypto/webcrypto.dart';

Future main() async {
final generators = Function(int)>{
'AES-CBC': AesCbcSecretKey.generateKey,
'AES-CTR': AesCtrSecretKey.generateKey,
'AES-GCM': AesGcmSecretKey.generateKey,
};

for (final length in [-1, 65536]) {
for (final entry in generators.entries) {
try {
await entry.value(length);
} catch (error) {
print('${entry.key} $length: ${error.runtimeType}: $error');
}
}
}
}
```

### Actual behavior

On the native VM, every call completes with a `FormatException`. On Chrome with Dart2JS, every call completes with an error similar to:

```text
JSObject: TypeError: Failed to execute 'generateKey' on 'SubtleCrypto':
AesKeyGenParams: length: Outside of numeric range
```

### Expected behavior

All backends should reject invalid AES key lengths with the same public Dart exception behavior. Raw JavaScript objects should not escape from these APIs.

### Suggested fix

Validate AES key lengths at the shared Dart API boundary before dispatching to the backend:

- retain support for 128-bit and 256-bit keys;
- preserve the existing `UnsupportedError` behavior for 192-bit AES;
- return `FormatException` for other lengths;
- add shared regression coverage for AES-CBC, AES-CTR, and AES-GCM on the VM, Chrome Dart2JS, and Chrome Dart2Wasm.

### Environment

- Dart SDK: 3.11.0
- Chrome: 153.0.8010.36
- macOS arm64: 26.6.2

### Related work

Issue #387 addressed the same class of Web IDL boundary leak for RSA `modulusLength`. The AES key-generation paths use different parameters and remain affected.

Contributor guide

Open the contributing guide

Research direction

Start by locating the shared implementations of AesCbcSecretKey.generateKey, AesCtrSecretKey.generateKey, and AesGcmSecretKey.generateKey, then run the provided reproduction on the VM and Chrome backends. Compare the existing RSA boundary coverage from issue #387 and add equivalent shared regression coverage for VM, Chrome Dart2JS, and Chrome Dart2Wasm. Done means invalid lengths consistently produce FormatException, 192-bit keys preserve UnsupportedError, and valid 128-bit and 256-bit keys remain supported.

Written by the indexing model from the issue text.

Assessment

Tech stack
dart
Domain
api, security, testing
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.