google / google/webcrypto.dart
bug: browser AES key generation exposes raw JavaScript errors for out-of-range lengths
- 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
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