Skip to content

feat(impl_jni): add JCA backend skeleton with opt-in digest path#296

Draft
mfazrinizar wants to merge 3 commits into
google:android-jca-branchfrom
mfazrinizar:feat/impl-jni-digest-selector
Draft

feat(impl_jni): add JCA backend skeleton with opt-in digest path#296
mfazrinizar wants to merge 3 commits into
google:android-jca-branchfrom
mfazrinizar:feat/impl-jni-digest-selector

Conversation

@mfazrinizar

Copy link
Copy Markdown

Summary

This PR adds the first Android JCA exploration slice on top of android-jca-branch.

It includes:

  • an experimental impl_jni backend skeleton following the existing WebCryptoImpl interface
  • a JCA digest implementation for SHA-1, SHA-256, SHA-384, and SHA-512 through java.security.MessageDigest
  • generated JNI bindings for the Java crypto classes used by the digest path
  • a native backend selector layer in impl_native
  • opt-in JNI/JCA selection through --dart-define=webcrypto.impl=jni
  • focused JNI digest tests comparing JCA output against the existing FFI/BoringSSL backend
  • an Android example integration smoke test showing the public API uses the JNI backend when selected

The stable native backend remains FFI/BoringSSL by default. The JNI/JCA backend is only selected when explicitly requested with:

--dart-define=webcrypto.impl=jni

Testing

Passed locally:

dart pub get --no-example
dart analyze
dart run jni:setup
dart test test/impl_jni_digest_test.dart
dart test -p vm
CHROME_EXECUTABLE=/usr/bin/brave dart test -p chrome # I used brave

Android, inside example app directory:

flutter test integration_test/jni_digest_test.dart \
  -d emulator-name \
  --dart-define=webcrypto.impl=jni

flutter test integration_test/webcrypto_test.dart \
  -d emulator-name

@google-cla

google-cla Bot commented Jun 25, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.


import 'package:jnigen/jnigen.dart';

Future<void> main() async {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we just use: a jnigen.yaml config file, see:
https://pub.dev/packages/jnigen#yaml-configuration-reference

Add it to:
https://github.com/google/webcrypto.dart/blob/master/tool/update-bindings.sh

Put the generated bindings in:

lib/
  src/
    third_party/
      jca/
        jnigen.yaml
        generated_bindings.dart

Or something like that

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. We should use the config-driven generation approach. I’ll switch this to a config-based setup matching the existing bindings flow: add lib/src/third_party/jca/jnigen.yaml, wire it into tool/update-bindings.sh, and put the generated output under lib/src/third_party/jca/generated_bindings.dart. In this case, I’ll split the generated bindings into a separate PR first so this implementation PR only contains the backend/test code.

/// `JByteArray.getRange` returns a typed list backed by a native buffer whose
/// lifetime is finalizer-driven. Copy once more before releasing the Java array
/// so webcrypto callers receive a normal Dart-managed [Uint8List].
Uint8List _copyJByteArray(jni.JByteArray array) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider putting this in a util.dart library or similar...

consider using an extension method on JByteArray, so you can do myArray.copyToDart() or something like that :D

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. I’ll move this into a small JNI util library and expose it as an extension method. I think probably something like copyToDartBytes(), so that the digest implementation stays focused on the crypto flow.


@visibleForTesting
WebCryptoImpl selectNativeImplForTesting(String requestedBackend) =>
requestedBackend == 'jni' ? jni.webCryptImpl : ffi.webCryptImpl;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this good for? probably don't need it :D

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was only added to make the temporary selector easy to test because I wasn’t really sure the -D path would be reliable through package:test. But agreed, it is not important right now. I’ll remove it and keep the tests focused on direct JNI/JCA smoke coverage.

@mfazrinizar mfazrinizar marked this pull request as draft June 25, 2026 16:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants