GSoC Proposal Inquiry – Android JCA Backend for package:webcrypto

90 views
Skip to first unread message

TUSHAR YADAV

unread,
Mar 19, 2026, 6:40:17 AM (12 days ago) Mar 19
to dart-gsoc

Hi Hamdaan and Jonas,

I’m Tushar, and I’m interested in the “Android JCA backend for package:webcrypto” GSoC project.

To better understand the feasibility, I built a prototype using package:jnigen that integrates Java’s JCA APIs with Dart via JNI. I implemented SHA-256 and HMAC-SHA256 and compared them against the existing webcrypto backend.

From my initial evaluation:

  • Outputs match exactly (correctness verified)

  • Benchmarks were averaged over multiple runs

  • For small inputs, the existing backend performs better (due to JNI overhead)

  • For larger inputs (~1MB), the JCA backend shows significantly better performance (~7x faster)

Summary (1MB input):
JCA → ~1.8 ms
WebCrypto → ~14.2 ms
Speedup → ~7–7.5x

This helped me better understand the trade-offs between JNI overhead and native execution across different workloads.

On the Java side, I used standard JCA primitives exposed via JNI. For example:

MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(data);

Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(key, "HmacSHA256"));
byte[] result = mac.doFinal(data);

I’d appreciate your guidance on the next step — would you prefer integrating this as an alternative backend first, or directly working toward replacing the current Android implementation?

Additionally, from a GSoC perspective, I’d love to understand what would be a reasonable and impactful target for a ~3-month timeline (e.g., covering a subset like hashing + HMAC first vs. aiming for a broader API surface including AES/RSA and key management).

Looking forward to your feedback.

Best regards,
Tushar



cryptogsoc.png

Hamdaan Ali

unread,
Mar 20, 2026, 11:52:55 AM (10 days ago) Mar 20
to dart-gsoc

Hey Tushar,

Thanks for putting this together. This looks like a good first step.

Your high-level observation makes sense. Call overhead is more visible on small inputs, while larger workloads can shift the balance. To evaluate this properly, it would help to tighten the benchmark methodology a bit. Help us understand if you were on a release/profile or debug build, device + Android version, are the aggregates after warmup, and whether timings include end-to-end conversion overhead.

Could you also help us understand how these benchmarks were computed?


> covering a subset like hashing + HMAC first vs. aiming for a broader API surface including AES/RSA and key management
digest/HMAC is a good starting point, but we would want to see whether the approach still looks promising for more representative operations than simple hashing alone.

In terms of backend structure, I’d suggest staying close to the existing backend contract rather than introducing a separate abstraction first. You may take a look at `lib/src/impl_interface/impl_interface.dart` to get a better understanding of the current contract layer.

Please let us know if you have more questions. You may start with your proposal - happy to continue this conversation over email.

Best,
Hamdaan

TUSHAR YADAV

unread,
Mar 22, 2026, 10:01:09 AM (8 days ago) Mar 22
to dart-gsoc

Hi Hamdaan and Jonas,

Thank you again for your guidance — I’ve been iterating on the prototype to better understand the performance characteristics of the JNI-based JCA backend. Based on your suggestions, I structured the evaluation into two phases and refined the benchmarking methodology.


Phase 1: Validation and Rigorous Benchmarking

(Initial validation – low iteration / warmup)
In the initial phase, I used a lighter configuration to validate correctness and get a directional understanding:

  • Outputs matched exactly with the WebCrypto backend (correctness verified)

  • JNI overhead dominates for small inputs

  • JCA begins to outperform WebCrypto as input size increases

As you pointed out, this setup was not sufficiently rigorous.


(Improved methodology – release mode + higher iterations)
I refined the benchmarking setup as follows:

  • Run in release mode

  • Device: Moto G85 5G (Android 15)

  • 10 warmup iterations + 100 measured iterations

  • Timing via Dart Stopwatch (µs precision)

  • Measurements include end-to-end cost (JNI calls + Dart ↔ Java data conversion)

How benchmarks were computed:

  • Warmup runs are discarded to stabilize runtime (reduce cold-start effects)

  • 100 measured iterations per operation and input size

  • Each iteration performs the full operation including JNI boundary crossing

  • Reported values are average latency (µs)

  • Min/max and variance are tracked internally for consistency

  • Identical input data and keys are reused to avoid randomness bias


Benchmark scope:

  • Input sizes: 1 KB → 1 MB

  • Operations:

    • SHA-256, SHA-512

    • HMAC-SHA256

    • AES-GCM (encrypt/decrypt)

    • AES-256 key generation


Summary of observations (based on measured results):

Small inputs (~1 KB):

  • SHA-256: comparable (~41 µs vs 42 µs)

  • HMAC / SHA-512: WebCrypto slightly faster

  • AES: JNI overhead dominates heavily (JCA ~400–500 µs vs Web ~5–6 µs)

Medium inputs (~10 KB):

  • SHA-256: ~4.2× faster

  • SHA-512: ~3× faster

  • HMAC-SHA256: ~3.1× faster

  • AES still dominated by initialization + JNI overhead

Large inputs (~100 KB – 1 MB):

  • SHA-256: up to ~12–14× faster

  • HMAC-SHA256: up to ~14× faster

  • SHA-512: ~6–8× faster

These results clearly show that JNI overhead is amortized with larger inputs, allowing JCA’s native execution to significantly outperform the current backend for throughput-heavy workloads.


Observation on AES behavior:

AES-GCM performance is currently significantly worse than WebCrypto across all input sizes:

  • Example (1 MB):

    • JCA: ~371 ms

    • WebCrypto: ~2.5 ms

This appears to be dominated by:

  • Repeated Cipher.getInstance and init calls

  • JNI data transfer overhead

  • Lack of object reuse


Observation on scaling limits:

When running the full benchmark suite continuously (all operations × sizes × 100 iterations), I observed instability/crashes at larger sizes (≥1 MB).

This is likely due to:

  • Frequent JNI crossings with large buffers

  • Repeated allocation and copying (JByteArray ↔ Uint8List)

  • High cumulative workload leading to memory pressure and GC activity


Interpretation:

  • Hashing and HMAC scale well and benefit significantly from native execution

  • AES performance is currently limited by initialization and data transfer costs

  • For large workloads, execution strategy (batching, reuse, reduced allocations) becomes critical


Next steps (ongoing work):

I am continuing to explore improvements, including:

  • Scaling iteration counts based on input size to improve stability

  • Investigating AES optimizations (object reuse vs per-call initialization)

  • Running additional analysis in Flutter profile mode (--profile)

  • Evaluating approaches to reduce JNI overhead (buffer reuse / batching)


I am also reviewing the existing backend contract (impl_interface.dart) to ensure that this approach can align with the current structure rather than introducing a separate abstraction.

Additionally, I will be extending evaluation to more representative operations (e.g., asymmetric crypto and key management) and will share a follow-up update soon with those results.


Please let me know if you would prefer:

  • continuing with this broader benchmarking approach while stabilizing execution, or

  • focusing more deeply on specific primitives (e.g., AES behavior, key management, or asymmetric crypto)

Happy to iterate further based on your feedback.

Best regards,
Tusharcrypto_final100.png

crypto 100 .jpeg
Reply all
Reply to author
Forward
0 new messages