Skip to content

add: ECDH_ES encryption support with EC Keys | fix: direct_post.jwt response mode VP token encoding#184

Open
KiruthikaJeyashankar wants to merge 3 commits into
inji:developfrom
tw-mosip:add-encryption-support-ec-keys
Open

add: ECDH_ES encryption support with EC Keys | fix: direct_post.jwt response mode VP token encoding#184
KiruthikaJeyashankar wants to merge 3 commits into
inji:developfrom
tw-mosip:add-encryption-support-ec-keys

Conversation

@KiruthikaJeyashankar

@KiruthikaJeyashankar KiruthikaJeyashankar commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Fixes: inji/inji-wallet#2482

Summary by CodeRabbit

  • New Features

    • Added support for converting authorization responses into structured map data for downstream processing.
    • Expanded encryption support to handle additional key types and curves.
  • Bug Fixes

    • Improved encrypted response generation for more reliable payload handling.
    • Enhanced error reporting for encryption failures with clearer messages.
    • Tightened validation to reject unsupported or incomplete keys earlier.

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>
Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@KiruthikaJeyashankar, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 34 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: de0d442d-c439-4bed-890b-047558bc8cfe

📥 Commits

Reviewing files that changed from the base of the PR and between 8b5bfcc and 7af4d7c.

📒 Files selected for processing (7)
  • kotlin/openID4VP/src/commonMain/kotlin/io/mosip/openID4VP/authorizationResponse/AuthorizationResponse.kt
  • kotlin/openID4VP/src/commonMain/kotlin/io/mosip/openID4VP/exceptions/openId4VPExceptions.kt
  • kotlin/openID4VP/src/commonMain/kotlin/io/mosip/openID4VP/jwt/jwe/JWEHandler.kt
  • kotlin/openID4VP/src/commonMain/kotlin/io/mosip/openID4VP/jwt/jwe/encryption/EncryptionProvider.kt
  • kotlin/openID4VP/src/commonTest/kotlin/io/mosip/openID4VP/authorizationResponse/AuthorizationResponseTest.kt
  • kotlin/openID4VP/src/commonTest/kotlin/io/mosip/openID4VP/jwt/jwe/JWEHandlerTest.kt
  • kotlin/openID4VP/src/commonTest/kotlin/io/mosip/openID4VP/jwt/jwe/encryption/EncryptionProviderTest.kt

Walkthrough

This PR updates authorization response serialization to produce typed maps instead of JSON-encoded string maps, simplifies JWE payload construction in JWEHandler, enriches JweEncryptionFailure with detailed error messages, and expands EncryptionProvider to validate and support both OKP/X25519 and EC (P-256/P-384/P-521) recipient keys, with corresponding test updates.

Changes

Direct post JWT response encryption fix

Layer / File(s) Summary
Authorization response map conversion
AuthorizationResponse.kt
Adds toMap() extension returning Map<String, Any> via ObjectMapper.convertValue, simplifies toJsonEncodedMap() state handling, and updates related imports.
Response mode handler wiring
DirectPostJwtResponseModeHandler.kt
Switches to using toMap() instead of toJsonEncodedMap() and widens encryptResponse's parameter type to Map<String, Any>.
JWE payload construction and error messaging
JWEHandler.kt, openId4VPExceptions.kt
Removes manual JSON serialization before JWE construction in favor of Payload(payload), and updates JweEncryptionFailure to accept and use a caller-provided message.
EncryptionProvider multi-key-type support
EncryptionProvider.kt
Adds an allowed-algorithm check, expands getEncrypter to validate and route OKP/X25519 and EC (P-256/P-384/P-521) keys, and adds getPublicEcKey helper requiring the y coordinate.
Test updates for key selection and validation matrix
JWEHandlerTest.kt, JWEHandlerJvmTest.kt, EncryptionProviderTest.kt
Updates test key selection index and expands getEncrypter test coverage for success and failure paths across OKP/EC key types using new JWK factory helpers.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DirectPostJwtResponseModeHandler
  participant AuthorizationResponse
  participant JWEHandler
  participant EncryptionProvider

  DirectPostJwtResponseModeHandler->>AuthorizationResponse: toMap()
  AuthorizationResponse-->>DirectPostJwtResponseModeHandler: Map<String, Any>
  DirectPostJwtResponseModeHandler->>JWEHandler: generateEncryptedResponse(payload)
  JWEHandler->>EncryptionProvider: getEncrypter(jwk)
  EncryptionProvider->>EncryptionProvider: validate alg and kty (OKP/EC)
  EncryptionProvider-->>JWEHandler: X25519Encrypter or ECDHEncrypter
  JWEHandler->>JWEHandler: build JWEObject with Payload(payload)
  JWEHandler-->>DirectPostJwtResponseModeHandler: serialized JWE
Loading

Possibly related issues

Possibly related PRs

  • inji/inji-openid4vp#174: Modifies the same AuthorizationResponse serialization and JWEHandler.generateEncryptedResponse/JweEncryptionFailure code paths.

Suggested reviewers: swatigoel

Poem

A rabbit hopped through JWE and keys,
Swapped strings for maps with graceful ease,
EC and OKP now both play fair,
Errors now speak with messages rare,
Hop, encrypt, and land — no more despair! 🐰🔐

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR covers EC-key JWE support and VP token encoding, but the linked issue also requires error-response encryption, IAR support, and fuller JWKS matching. Extend the flow to encrypt error responses and iar_post.jwt, and add verifier JWKS selection using kid, alg, use, and key_ops.
Docstring Coverage ⚠️ Warning Docstring coverage is 5.26% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly reflects the two main changes: EC JWE support and direct_post.jwt VP token encoding.
Out of Scope Changes check ✅ Passed The changes shown are focused on the encryption fix and related tests, with no clear unrelated additions.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
kotlin/openID4VP/src/commonTest/kotlin/io/mosip/openID4VP/jwt/jwe/encryption/EncryptionProviderTest.kt (1)

57-69: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Test locks in the swapped-argument bug from EncryptionProvider.kt instead of catching it.

The comment at line 63 explicitly acknowledges the bug ("Current implementation populates class name as the exception message"), and the assertion at line 66 asserts expectedMessage = "EncryptionProvider". Once the root-cause fix in EncryptionProvider.kt (swapped JweEncryptionFailure args) is applied, this test needs updating to assert the correct descriptive message (e.g. about the unsupported curve) instead of the class name.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@kotlin/openID4VP/src/commonTest/kotlin/io/mosip/openID4VP/jwt/jwe/encryption/EncryptionProviderTest.kt`
around lines 57 - 69, Update the `EncryptionProviderTest` case for
`getEncrypter` so it no longer expects the exception message to be the class
name `EncryptionProvider`. After fixing the swapped `JweEncryptionFailure`
arguments in `EncryptionProvider.kt`, assert the real descriptive message for
the unsupported EC curve path instead, while keeping the same `INVALID_REQUEST`
error code. Use the existing `assertOpenId4VPException` helper and the
`getEncrypter`/`JweEncryptionFailure` symbols to locate the affected assertion.
🧹 Nitpick comments (3)
kotlin/openID4VP/src/commonMain/kotlin/io/mosip/openID4VP/authorizationResponse/AuthorizationResponse.kt (1)

50-66: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider deriving toJsonEncodedMap() from toMap() to avoid duplicated branching.

Both functions branch on the same sealed subtypes with nearly identical shape. Not urgent, but worth consolidating to avoid the two implementations drifting.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@kotlin/openID4VP/src/commonMain/kotlin/io/mosip/openID4VP/authorizationResponse/AuthorizationResponse.kt`
around lines 50 - 66, The branching logic for AuthorizationResponse
serialization is duplicated between toMap() and toJsonEncodedMap(), which risks
the two implementations drifting. Refactor toJsonEncodedMap() to derive its
payload from toMap() in AuthorizationResponse, and keep the subtype handling in
one place so both PresentationExchange and Dcql stay consistent. Use the
existing toMap() and AuthorizationResponse sealed subclasses as the single
source of truth, and only apply JSON-encoding-specific transformation after that
shared map is built.
kotlin/openID4VP/src/commonMain/kotlin/io/mosip/openID4VP/jwt/jwe/JWEHandler.kt (1)

35-46: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Broad catch (exception: Exception) re-wraps already-typed OpenID4VPExceptions (e.g. UnsupportedKeyExchangeAlgorithm, JweEncryptionFailure from EncryptionProvider.getEncrypter).

EncryptionProvider.getEncrypter(publicKey) is called inside this try block and can throw JweEncryptionFailure or UnsupportedKeyExchangeAlgorithm. Both get caught by the generic Exception handler and re-wrapped into a new JweEncryptionFailure, losing the original exception type (only the message text is preserved, nested with a "JWE Encryption failed :" prefix). Callers/tests that pattern-match on the specific exception subtype downstream of this call path would no longer see it.

♻️ Suggested fix — rethrow already-typed exceptions as-is
             val encrypter = EncryptionProvider.getEncrypter(publicKey)
             val jweObject = JWEObject(header, Payload(payload))
             jweObject.encrypt(encrypter)

             return jweObject.serialize()
+        } catch (exception: OpenID4VPExceptions) {
+            throw exception
         } catch (exception: Exception) {
             throw OpenID4VPExceptions.JweEncryptionFailure(
                 "JWE Encryption failed : " + (exception.message ?: "Unknown error"),
                 className,
                 exception
             )
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@kotlin/openID4VP/src/commonMain/kotlin/io/mosip/openID4VP/jwt/jwe/JWEHandler.kt`
around lines 35 - 46, In JWEHandler.encrypt, the broad catch block is
re-wrapping exceptions that are already OpenID4VPExceptions, which hides the
original subtype. Update the catch logic so EncryptionProvider.getEncrypter and
jweObject.encrypt failures that already throw OpenID4VPExceptions (such as
UnsupportedKeyExchangeAlgorithm and JweEncryptionFailure) are rethrown
unchanged. Keep only unexpected exceptions wrapped into JweEncryptionFailure,
preserving the current className context where needed.
kotlin/openID4VP/src/commonMain/kotlin/io/mosip/openID4VP/jwt/jwe/encryption/EncryptionProvider.kt (1)

77-90: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Missing y coordinate throws UnsupportedKeyExchangeAlgorithm instead of a message describing the actual problem.

Throwing UnsupportedKeyExchangeAlgorithm (message: "Required Key exchange algorithm is not supported") for a missing y coordinate is misleading — the algorithm/key exchange type is fine; the JWK is simply malformed/incomplete. A JweEncryptionFailure with a specific message (e.g. "EC key is missing required 'y' coordinate") would aid debugging conformance-test decryption failures, which is a stated goal of this PR.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@kotlin/openID4VP/src/commonMain/kotlin/io/mosip/openID4VP/jwt/jwe/encryption/EncryptionProvider.kt`
around lines 77 - 90, The `getPublicEcKey` helper is throwing
`UnsupportedKeyExchangeAlgorithm` when `jwk.y` is missing, which masks the real
issue. Update this branch in `EncryptionProvider` to fail with a
`JweEncryptionFailure` (or the project’s equivalent encryption error) and a
clear message that the EC JWK is incomplete, explicitly mentioning the missing
`y` coordinate. Keep the rest of `getPublicEcKey` unchanged so valid `Curve`,
`jwk.x`, `jwk.kid`, `jwk.alg`, and `jwk.use` handling still works.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@kotlin/openID4VP/src/commonMain/kotlin/io/mosip/openID4VP/authorizationResponse/AuthorizationResponse.kt`:
- Around line 50-59: The AuthorizationResponse.toMap() helper is mutating the
shared Jackson mapper via getObjectMapper(), which can affect unrelated
serialization elsewhere. Update AuthorizationResponse.toMap() to copy the mapper
first and then set JsonInclude.Include.NON_NULL on the copy before converting
vpToken and presentationSubmission, keeping the change local to this method.

In
`@kotlin/openID4VP/src/commonMain/kotlin/io/mosip/openID4VP/jwt/jwe/encryption/EncryptionProvider.kt`:
- Around line 44-59: The EC-curve validation in EncryptionProvider’s JWK
handling has the JweEncryptionFailure constructor arguments swapped, so the
exception message and className are reversed. Update the EC branch to pass the
descriptive unsupported-curve error text as the message and the current
className as the className, matching the OKP branch’s usage. Also change the
wording to refer to the unsupported curve rather than the recipient key type,
and update the related EncryptionProviderTest assertion to expect the corrected
message/className order.

---

Duplicate comments:
In
`@kotlin/openID4VP/src/commonTest/kotlin/io/mosip/openID4VP/jwt/jwe/encryption/EncryptionProviderTest.kt`:
- Around line 57-69: Update the `EncryptionProviderTest` case for `getEncrypter`
so it no longer expects the exception message to be the class name
`EncryptionProvider`. After fixing the swapped `JweEncryptionFailure` arguments
in `EncryptionProvider.kt`, assert the real descriptive message for the
unsupported EC curve path instead, while keeping the same `INVALID_REQUEST`
error code. Use the existing `assertOpenId4VPException` helper and the
`getEncrypter`/`JweEncryptionFailure` symbols to locate the affected assertion.

---

Nitpick comments:
In
`@kotlin/openID4VP/src/commonMain/kotlin/io/mosip/openID4VP/authorizationResponse/AuthorizationResponse.kt`:
- Around line 50-66: The branching logic for AuthorizationResponse serialization
is duplicated between toMap() and toJsonEncodedMap(), which risks the two
implementations drifting. Refactor toJsonEncodedMap() to derive its payload from
toMap() in AuthorizationResponse, and keep the subtype handling in one place so
both PresentationExchange and Dcql stay consistent. Use the existing toMap() and
AuthorizationResponse sealed subclasses as the single source of truth, and only
apply JSON-encoding-specific transformation after that shared map is built.

In
`@kotlin/openID4VP/src/commonMain/kotlin/io/mosip/openID4VP/jwt/jwe/encryption/EncryptionProvider.kt`:
- Around line 77-90: The `getPublicEcKey` helper is throwing
`UnsupportedKeyExchangeAlgorithm` when `jwk.y` is missing, which masks the real
issue. Update this branch in `EncryptionProvider` to fail with a
`JweEncryptionFailure` (or the project’s equivalent encryption error) and a
clear message that the EC JWK is incomplete, explicitly mentioning the missing
`y` coordinate. Keep the rest of `getPublicEcKey` unchanged so valid `Curve`,
`jwk.x`, `jwk.kid`, `jwk.alg`, and `jwk.use` handling still works.

In
`@kotlin/openID4VP/src/commonMain/kotlin/io/mosip/openID4VP/jwt/jwe/JWEHandler.kt`:
- Around line 35-46: In JWEHandler.encrypt, the broad catch block is re-wrapping
exceptions that are already OpenID4VPExceptions, which hides the original
subtype. Update the catch logic so EncryptionProvider.getEncrypter and
jweObject.encrypt failures that already throw OpenID4VPExceptions (such as
UnsupportedKeyExchangeAlgorithm and JweEncryptionFailure) are rethrown
unchanged. Keep only unexpected exceptions wrapped into JweEncryptionFailure,
preserving the current className context where needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 17c5f9b2-9957-4a81-ba6f-2bcd1e4bc8f4

📥 Commits

Reviewing files that changed from the base of the PR and between 0efa798 and 8b5bfcc.

📒 Files selected for processing (8)
  • kotlin/openID4VP/src/commonMain/kotlin/io/mosip/openID4VP/authorizationResponse/AuthorizationResponse.kt
  • kotlin/openID4VP/src/commonMain/kotlin/io/mosip/openID4VP/exceptions/openId4VPExceptions.kt
  • kotlin/openID4VP/src/commonMain/kotlin/io/mosip/openID4VP/jwt/jwe/JWEHandler.kt
  • kotlin/openID4VP/src/commonMain/kotlin/io/mosip/openID4VP/jwt/jwe/encryption/EncryptionProvider.kt
  • kotlin/openID4VP/src/commonMain/kotlin/io/mosip/openID4VP/responseModeHandler/types/DirectPostJwtResponseModeHandler.kt
  • kotlin/openID4VP/src/commonTest/kotlin/io/mosip/openID4VP/jwt/jwe/JWEHandlerTest.kt
  • kotlin/openID4VP/src/commonTest/kotlin/io/mosip/openID4VP/jwt/jwe/encryption/EncryptionProviderTest.kt
  • kotlin/openID4VP/src/jvmTest/kotlin/io/mosip/openID4VP/jwt/JWEHandlerJvmTest.kt

@KiruthikaJeyashankar KiruthikaJeyashankar force-pushed the add-encryption-support-ec-keys branch from d83f502 to 18e7974 Compare July 1, 2026 10:27
Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>
@KiruthikaJeyashankar KiruthikaJeyashankar force-pushed the add-encryption-support-ec-keys branch from 18e7974 to 7af4d7c Compare July 1, 2026 11:36
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.

1 participant