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
import 'package:webcrypto/webcrypto.dart';
Future<void> main() async {
final generators = <String, Future<Object> 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:
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.
Description
AesCbcSecretKey.generateKey,AesCtrSecretKey.generateKey, andAesGcmSecretKey.generateKeyhandle 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 IDLunsigned shortrange reachAesKeyGenParams.length. Its[EnforceRange]conversion throws a JavaScript
TypeError, which escapes the package as a rawJSObject.This makes error handling backend-dependent and exposes a JavaScript interop implementation detail through the public Dart API.
Reproduction
Actual behavior
On the native VM, every call completes with a
FormatException. On Chrome with Dart2JS, every call completes with an error similar to: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:
UnsupportedErrorbehavior for 192-bit AES;FormatExceptionfor other lengths;Environment
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.