feat(impl_jni): add JCA PBKDF2 implementation - #357
mfazrinizar wants to merge 2 commits into
Conversation
d011c5d to
b14c41c
Compare
| mac.doFinal$1(u, 0); | ||
| u.copyRangeToDart(block, 0, hashLength); | ||
|
|
||
| for (var iteration = 1; iteration < iterations; iteration++) { |
There was a problem hiding this comment.
blocking: pbkdf2 is deliberately iteration-heavy, but this implementation crosses dart/jni on every round. Each round calls Mac.update, calls Mac.doFinal, and copies U into dart so we can xor it. At 100,000 iterations, that is about 200,000 JNI calls and 100,000 copies for one output block.
U stays as a Java byte array between rounds, so we are not copying it back into Java. But this is still far too much boundary traffic for normal PBKDF2 usage. We need to eventually run the complete HMAC/XOR loop on the JVM and return only the final bytes. PBEKeySpec is not a drop-in alternative because this API accepts raw password bytes, not Java characters.
There was a problem hiding this comment.
Agreed with that. I’ll move the complete byte-oriented HMAC/XOR loop into a JCA helper so Dart crosses JNI only for the inputs and final derived bytes. PBEKeySpec is not suitable because our API accepts arbitrary raw password bytes.
Before implementing it, I want to confirm the packaging direction. Since #264 removed the Android plugin module in favor of native hooks, a custom Java helper currently has no package-level path into consumer Android apps; putting it only in the demo app would not be sufficient. Desktop JNI tests would also need the helper compiled into a JAR and added to the JVM classpath.
Would you be okay with a small helper-support PR covering Android packaging and desktop test setup, followed by updating #357 to call it? I would keep generated bindings separate as usual. Or do you prefer another packaging mechanism?
There was a problem hiding this comment.
you are right about the packaging constraint. i was too quick to prescribe a java helper without accounting for the fact that the current native-hook setup has no path to package java bytecode into consumer android apps.
please do not add a demo-only helper, a desktop-only jar path, or a new plugin/aar packaging mechanism in this pr. keep the byte-oriented implementation for the experimental backend so we preserve raw-password semantics, but add a benchmark at realistic iteration counts such as 1k, 10k, and 100k so we quantify the boundary cost.
batching pbkdf2 remains a required design problem before this backend could be treated as production-ready, but it needs a deliberate package-level packaging decision rather than an ad hoc helper here
There was a problem hiding this comment.
Addressed. I kept the byte-oriented implementation unchanged and added reproducible benchmarks at 1k, 10k, and 100k iterations. The desktop harness verifies JNI/FFI output equivalence before timing. A fresh run measured JNI at 5.52 ms, 20.65 ms, and 219.92 ms, compared with FFI at 0.77 ms, 2.45 ms, and 19.87 ms. I also updated the TODO to track batching as a package-level production-readiness issue. I’ll link the tracking issue after filing it.
b14c41c to
f3ad985
Compare
|
I think this is an awesome exploration, but that perhaps we should decide that even on Android with JCA enabled, we still fallback to boringssl/ffi for PBKDF2. We don't want Perhaps, better that we accept that we need boringssl/ffi when PBKDF2 is used. |
Summary
Adds the JNI/JCA PBKDF2 implementation on top of the Android JCA backend.
This PR implements:
Macas the pseudorandom functionPBEKeySpeccharacter conversionGenerated JCA bindings are not changed in this PR. The required
MacandSecretKeySpecbindings are already present onandroid-jca-branch.Experimental performance limitation
The byte-oriented implementation preserves arbitrary raw password bytes, but the RFC 8018 iteration/XOR loop currently runs in Dart. Each iteration invokes JCA through JNI and copies the intermediate
Uvalue into Dart for XOR. Benchmarks confirm that this boundary cost is significant at realistic iteration counts.This is accepted for the experimental backend, but batching remains required before it can be considered production-ready. The design is tracked separately because the native-hook package currently has no supported way to distribute a custom Java helper to consumer Android applications.
Reproducible benchmark commands:
The desktop harness verifies JNI/FFI output equivalence before reporting median times at 1,000, 10,000, and 100,000 iterations. Android results are timing evidence only and contain no performance assertion.
Testing
Desktop JNI setup, if it has not been run already:
Verified with: