google / google/webcrypto.dart

[Android JCA] Design production-ready PBKDF2 batching for the Android JCA backend

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

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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.