google / google/webcrypto.dart
bug: browser AES-CTR exposes inconsistent errors for invalid counter bit lengths
- Dominant language
- Dart
- Stars
- 116
- Forks
- 110
- Avg merge
- 6d 8h
- Merged PRs (30d)
- 9
Description
## Summary
The browser implementation of `AesCtrSecretKey.encryptBytes` and `decryptBytes` forwards `length` to `SubtleCrypto` without validating the AES-CTR counter length range first.
The native backend validates that `length` is between 1 and 128 and throws `ArgumentError`. In browsers, invalid values are instead handled at the Web IDL or Web Crypto boundary. Values outside the Web IDL `octet` range can expose a raw JavaScript `TypeError`, while other invalid AES-CTR lengths produce `OperationError`.
This makes the public API behavior dependent on the selected backend.
## Reproduction
```dart
import 'package:webcrypto/webcrypto.dart';
Future main() async {
final key = await AesCtrSecretKey.importRawKey(List.filled(16, 0));
final counter = List.filled(16, 0);
await key.encryptBytes([1, 2, 3], counter, -1);
}
```
### Native result
```text
Invalid argument (length): must be between 1 and 128: -1
```
### Chrome result
```text
TypeError: Failed to execute 'encrypt' on 'SubtleCrypto':
AesCtrParams: length: Outside of numeric range
```
The browser result occurs under both dart2js and dart2wasm.
## Expected behavior
AES-CTR `length` values outside `1..128` should be rejected consistently with `ArgumentError` before entering browser interop, matching the native backend.
Both encryption and decryption should have the same behavior.
## Cause
Web Crypto defines `AesCtrParams.length` as an `[EnforceRange] octet`. The browser backend currently passes the Dart integer directly to `SubtleCrypto`, while the native backend performs an explicit `1..128` validation.
## Suggested fix
- Validate `1 <= length <= 128` in the browser AES-CTR implementation before calling `SubtleCrypto`.
- Apply the validation to encryption and decryption.
- Add shared regression coverage for `-1`, `0`, `129`, and `256`.
- Run the regression on native, Chrome/dart2js, and Chrome/dart2wasm.
Contributor guide
Research direction
Find the browser implementation of AesCtrSecretKey.encryptBytes and decryptBytes, then compare its length handling with the native backend. Add shared regression coverage for -1, 0, 129, and 256, and run it on native, Chrome/dart2js, and Chrome/dart2wasm to verify consistent ArgumentError behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- dart
- Domain
- cryptography, security
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100