google / google/webcrypto.dart
[Android JCA] Design production-ready PBKDF2 batching for the Android JCA backend
- Dominant language
- Dart
- Stars
- 116
- Forks
- 110
- Avg merge
- 6d 8h
- Merged PRs (30d)
- 9
Description
## Problem
The experimental JNI/JCA PBKDF2 backend preserves arbitrary raw password bytes by implementing RFC 8018 with JCA `Mac`. The iteration and XOR loop currently runs in Dart, so each iteration crosses the JNI boundary to update/finalize the MAC, copies the intermediate `U` value into Dart for XOR, and sends it back for the next round.
This is functionally correct, but PBKDF2 is intentionally iteration-heavy. At 100,000 iterations, the repeated Dart/JNI transitions are a production-readiness problem.
`PBEKeySpec` is not a replacement because the public API accepts arbitrary raw password bytes rather than Java characters.
## Packaging constraint
The package uses native build hooks and no longer has an Android plugin/module that can package custom Java bytecode into consumer applications. A helper placed only in the demo app or a desktop-only JAR would make tests pass without solving distribution for real Android users.
The batching design therefore needs an explicit package-level decision about how Java/JVM helper code would be built, shipped, and loaded on Android, while remaining reproducible for desktop JNI tests.
## Benchmark evidence
PBKDF2-HMAC-SHA-256 was measured with a 256-bit output after warm-up:
| Environment/backend | 1,000 | 10,000 | 100,000 |
| --- | ---: | ---: | ---: |
| Desktop JVM-only JCA `Mac` | 0.69 ms | 5.82 ms | 39.39 ms |
| Desktop FFI/BoringSSL | 0.60 ms | 2.59 ms | 20.99 ms |
| Desktop JNI experimental backend | 4.22 ms | 20.66 ms | 221.15 ms |
| Android 16 x86_64 emulator JNI | 52.39 ms | 84.91 ms | 913.15 ms |
The Android result is emulator evidence and should not be interpreted as physical-device performance. The comparison measures the combined cost of JNI calls, Java-to-Dart copies, Dart XOR, isolate overhead, and backend execution; it does not isolate one operation as the sole cause.
#357 includes reproducible harnesses for the desktop JNI/FFI and Android JNI rows. The JVM-only result is a local control measurement included for context. The benchmark covers 1,000, 10,000, and 100,000 iterations and verifies JNI/FFI output equivalence on desktop.
## Requirements
- Preserve arbitrary raw password bytes, including empty input and bytes above
`0x7f`.
- Keep the complete PBKDF2 HMAC/XOR loop outside Dart and cross JNI only for
inputs and final output.
- Work in consumer Android applications, not only the demo app.
- Provide a reproducible desktop JNI test/benchmark path.
- Keep cryptographic operations in JCA rather than falling back to BoringSSL.
- Preserve existing focused, shared public-API, and Android integration tests.
- Re-run the 1k/10k/100k benchmark to quantify the improvement.
Related: #357
Contributor guide
Research direction
Start by reading the reproducible desktop JNI/FFI and Android JNI harnesses from #357, then trace the experimental Android JCA PBKDF2 backend and its package build path. Done means a package-level Android distribution design and implementation that keeps the PBKDF2 loop outside Dart, preserves raw-byte behavior, supports desktop JNI testing, passes the existing tests, and reproduces the 1k/10k/100k benchmark.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, dart, java
- Domain
- build-system, cryptography, mobile-dev
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100