Feat: Expose sign-from-context, deprecate settings Signer - #119
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #119 +/- ##
=========================================
Coverage 72.21% 72.21%
Complexity 38 38
=========================================
Files 62 62
Lines 2350 2350
Branches 286 286
=========================================
Hits 1697 1697
+ Misses 495 494 -1
- Partials 158 159 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Add Builder.signWithContext(format, source, dest) over c2pa_builder_sign_context, which signs using the signer configured on the builder's context (programmatic via C2PAContextBuilder.setSigner or supplied via settings). This is the modern path for the settings-signer workflow. Deprecate Signer.fromSettingsJson/fromSettingsToml/loadSettings, which rely on the deprecated c2pa_load_settings and c2pa_signer_from_settings, pointing at the context path instead.
ea604a0 to
aca7443
Compare
scouten-adobe
left a comment
There was a problem hiding this comment.
Review summary
Overall this is a clean, well-scoped addition — signWithContext mirrors the upstream c2pa_builder_sign_context FFI call correctly, and the Signer deprecations are safe (every real call site is already @Suppress-annotated, no test coverage removed). A few issues worth addressing before merge, roughly in order of importance:
Correctness
-
sign()throws the wrong exception type on native failure, exposed by contrast with the new code.Builder.kt:581checksif (result.size < 0) throw C2PAError.Api(...), butsignNative(c2pa_jni.c:1060) callsthrow_c2pa_exception()→ThrowNew(RuntimeException)on failure and returnsNULL— the JVM delivers that pending exception at the call boundary beforeresult.sizeis ever read, so theC2PAError.Apibranch is dead code. A caller doingtry { builder.sign(...) } catch (e: C2PAError) { ... }(matchingsign()'s documented@Throws(C2PAError::class)) will see an uncaughtRuntimeExceptioninstead. This predates the PR, but the newsignWithContext()/signWithContextNativecorrectly uses the return-null-and-throw-C2PAError-in-Kotlin pattern, so the two "sibling" sign APIs now behave inconsistently. Worth fixingsignNativeto match (drop the native throw, let Kotlin raiseC2PAErrorlike the new path does). -
Native memory leak when
size == 0in the new JNI function.c2pa_jni.c:1160only callsc2pa_free(manifestBytes)insideif (manifestBytes != NULL && size > 0). Ifc2pa_builder_sign_contextever succeeds with a non-NULL, zero-length manifest buffer, it leaks. Low severity (a real 0-byte signed manifest is unlikely) and it's a duplicate of the same pre-existing pattern insignNative, but worth fixing in both places. -
KDoc overstates the construction requirement.
Builder.kt:592says the builder "must have been created viaBuilder.fromContext", butBuilder.fromJson(json, settings)(Builder.kt:276-286) also builds viafromContextinternally and would work fine withsignWithContexttoo.
Style — blank line before multi-line statements
Per this file's own established convention (e.g. the pre-existing testSignerFromSettingsToml), a statement that wraps across multiple lines should be set off by a blank line above and below:
SignerTests.kt:810— no blank line after the two-lineval settingsToml = ... ?: throw ....SignerTests.kt:857— no blank line before the multi-lineC2PAContext.create().use { ... }.
Reuse
- Duplicated
SignResultconstruction boilerplate.signWithContextNativecopies ~45 lines of class/constructor lookup + byte-array construction verbatim fromsignNative. No shared helper exists for this; worth extracting abuild_sign_result(env, size, manifestBytes)helper so a fix to one path (e.g. the leak above) doesn't need to be duplicated into the other.
Test coverage
- No test exercises
signWithContexton a builder created viafromJson/fromArchive(context closed before the builder is returned) — only thefromContext-with-open-context path is covered. testSignWithContextWithoutSignerassumes a freshC2PAContexthas no signer, but the same test suite also mutates deprecated global settings state (Signer.loadSettings/fromSettingsJson/fromSettingsToml) elsewhere — it's unverified thatc2pa_context_new()'s defaults are isolated from that legacy global state, which could make this test order-dependent/flaky.- Minor:
testSignWithContextWithoutSignercaptures avar thrown: C2PAError?across nested.use {}blocks instead of building theTestResultdirectly in thecatchblock, astestCawgRejectsClosedSignerdoes for the same "expect a throw" shape elsewhere in this file.
Requesting changes mainly on #1 (exception-type inconsistency) and #2 (leak) — the rest are lower-severity polish that could go either way before or after merge.
Align sign() with the documented C2PAError contract by returning NULL from the JNI on failure and throwing from the Kotlin wrapper, matching signWithContext. Extract the duplicated SignResult construction into a build_sign_result helper that frees the manifest buffer on every path, including zero-length results. Correct the signWithContext KDoc: any builder has a context, so fromJson with signer-bearing settings works too. Add tests for signing from a fromJson builder and for the unsupported-format error path.
C2PA.loadSettings and loadSettingsResult call the same deprecated c2pa_load_settings as the already-deprecated Signer.loadSettings, so give them the same deprecation notice pointing to C2PASettings and C2PAContextBuilder, and suppress the warning in the internal callers and the test that still exercise the old path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Changes in this pull request
Adds Builder.signWithContext(format, source, dest), which signs using the signer configured on the builder's context, whether set programmatically via C2PAContextBuilder.setSigner or supplied through settings. Deprecates Signer.fromSettingsJson/fromSettingsToml/loadSettings in favor of this context path, since they rely on FFI the upstream library has deprecated.
Types of changes
Checklist
TO DOitems (or similar) have been entered as GitHub issues and the link to that issue has been included in a comment