Skip to content

Add RSA CRT fields and extractable support to JWK generation#15

Open
5eeman wants to merge 8 commits into
Bdaya-Dev:mainfrom
5eeman:feat/jwk-rsa-crt-and-extractable
Open

Add RSA CRT fields and extractable support to JWK generation#15
5eeman wants to merge 8 commits into
Bdaya-Dev:mainfrom
5eeman:feat/jwk-rsa-crt-and-extractable

Conversation

@5eeman
Copy link
Copy Markdown

@5eeman 5eeman commented Apr 8, 2026

Title

Add RSA CRT fields and extractable support to JWK generation

Summary

This PR improves JWK generation by including full RSA CRT parameters for private keys and adding support for the JWK ext (extractable) property across key constructors.

Changes

  • Add RSA CRT parameters when creating RSA private JWKs:
    • dp (first factor CRT exponent)
    • dq (second factor CRT exponent)
    • qi (first CRT coefficient)
  • Add optional extractable support and serialize it as ext in:
    • JsonWebKey.fromCryptoKeys(...)
    • JsonWebKey.rsa(...)
    • JsonWebKey.ec(...)
    • JsonWebKey.symmetric(...)
  • Add extractable getter on JsonWebKey.
  • Add decodeBigInt(String) utility to support BigInt decoding for CRT-related values.
  • Ensure generated JWK payloads include ext: true where algorithm-driven key generation applies.

Why

  • Producing complete RSA private JWKs improves interoperability with consumers that expect CRT fields.
  • Supporting ext aligns JWK output with extractability metadata used in WebCrypto-style workflows.

Scope / Compatibility

  • No behavioral change for non-RSA keys beyond optional ext propagation.
  • Existing APIs remain backward compatible (extractable is optional).

Testing

  • Additional tests added in:
    • test/jwk_test.dart
    • test/jwa_test.dart
  • No additional test files were added in this change set.

Summary by CodeRabbit

  • New Features

    • Improved JWE JSON parsing with fallback behavior when recipients and headers are missing.
    • Support for unpadded Base64URL protected headers in JWE parsing.
    • Added extractable parameter for key generation across RSA, EC, and symmetric keys.
    • RSA JWK generation now includes CRT parameters for enhanced compatibility.
  • Bug Fixes

    • Enhanced validation to ensure encrypted_key is properly handled for different key management algorithms.
  • Tests

    • Extended test coverage for JWE fallback parsing, error handling, and key metadata validation.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 8, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5fef5ba0-f458-4de3-8f5c-286f92ecfbbb

📥 Commits

Reviewing files that changed from the base of the PR and between a2157fa and d23556d.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • lib/src/jwa.dart
  • lib/src/jwk.dart
  • lib/src/util.dart
  • test/jwa_test.dart
💤 Files with no reviewable changes (1)
  • CHANGELOG.md
✅ Files skipped from review due to trivial changes (2)
  • lib/src/util.dart
  • test/jwa_test.dart
🚧 Files skipped from review as they are similar to previous changes (2)
  • lib/src/jwa.dart
  • lib/src/jwk.dart

📝 Walkthrough

Walkthrough

This PR adds RSA CRT parameters (dp, dq, qi) to JWK serialization and introduces an extractable flag across all JWK key types. It includes utility functions for decoding base64url-encoded BigInts, Dart extensions for computing CRT values from RSA keys, updated JWK factories to accept and serialize these new fields, and expanded test coverage for JWE JSON parsing fallback behavior when recipients/header are omitted.

Changes

RSA CRT Parameters and Extractable JWK Fields

Layer / File(s) Summary
CRT calculation utilities
lib/src/util.dart, lib/src/jwa.dart
decodeBigInt() decodes base64url strings to BigInt. Two extensions are added: RsaPrivateKeyCRTCalcExtension computes dp, dq, qi from RSA private key exponent and factors; JsonWebKeyCRTCalcExtension computes the same values from decoded JWK fields using modular arithmetic.
JWK factory signatures and serialization
lib/src/jwk.dart
JsonWebKey.fromCryptoKeys, JsonWebKey.rsa, JsonWebKey.ec, and JsonWebKey.symmetric all accept an optional extractable parameter. JsonWebKey.rsa additionally accepts CRT exponents/coefficient (firstFactorCRTExponent, secondFactorCRTExponent, firstCRTCoefficient). Factories conditionally serialize ext and CRT fields (dp, dq, qi) into the JWK JSON. A new extractable getter exposes the ext member.
RSA JWK generation with CRT
lib/src/jwa.dart
RSA JWK generation now computes dp, dq, qi from the private key using the new extension and passes them to JsonWebKey.rsa. Generated JWKs now emit key_ops (replacing keyOperations) and ext: true.
CRT and extractable test coverage
test/jwa_test.dart, test/jwk_test.dart
JWA tests assert that generated keys have extractable == true and RSA keys include CRT fields in their JSON serialization. JWK tests verify JsonWebKey.fromCryptoKeys computes and propagates CRT parameters correctly for RSA keys, EC keys, and symmetric keys, and that the computed dp/dq/qi match the extension calculation methods.

JWE JSON Parsing Fallback Test Coverage

Layer / File(s) Summary
JWE JSON fallback parsing tests
test/jwe_test.dart
New test group covers JsonWebEncryption.fromJson behavior when recipients and/or header are omitted: verifies fallback derives a single recipient from the protected header, validates that encrypted_key must be absent for dir algorithms and present for others, tests unpadded Base64URL protected headers, and asserts error handling for missing or invalid protected headers.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Bdaya-Dev/jose#14: Updates to JWE JSON fallback parsing and unpadded protected-header handling with matching test coverage.
  • Bdaya-Dev/jose#12: Related JWK serialization changes that also emit RFC-aligned key_ops field instead of keyOperations.

Poem

🐰 Hops with glee through CRT fields,
Parameters dp, dq, qi reveal,
Extractable flags now shine so bright,
JWE parsing fallback feels so right!
Your keys are stronger, tested true—
Version 0.4.8 hops to you! 🔐

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main changes: adding RSA CRT fields (dp, dq, qi) and extractable support to JWK generation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/src/jwk.dart (1)

393-399: ⚠️ Potential issue | 🟡 Minor

Inconsistent field name: keyOperations should be key_ops.

The unwrapKey method uses 'keyOperations' but RFC 7517 specifies 'key_ops'. This is inconsistent with jwkFromCryptoKeyPair in jwa.dart (line 246) and the keyOperations getter (line 279) which reads from 'key_ops'. Keys created by unwrapKey won't have their operations recognized by the getter.

🐛 Proposed fix
     return JsonWebKey.fromJson({
       'kty': 'oct',
       'k': encodeBase64EncodedBytes(v),
       'use': 'enc',
-      'keyOperations': ['encrypt', 'decrypt']
+      'key_ops': ['encrypt', 'decrypt']
     })!;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/src/jwk.dart` around lines 393 - 399, The unwrapKey return builds a JWK
with the wrong field name; change the 'keyOperations' property to the
RFC-compliant 'key_ops' so the getter and other code (e.g., jwkFromCryptoKeyPair
in jwa.dart and the keyOperations getter) can read it; update the map in
unwrapKey (the JsonWebKey.fromJson call that uses encodeBase64EncodedBytes(v))
to use 'key_ops': ['encrypt', 'decrypt'] instead of 'keyOperations'.
🧹 Nitpick comments (5)
test/jwk_test.dart (1)

10-17: Consider using the library's decodeBigInt utility.

The test helper _decodeBase64UrlBigInt duplicates the logic from lib/src/util.dart:decodeBigInt. While both implementations are equivalent (<< 8 vs * b256), using the library's utility would validate that the actual decode function works correctly and reduce test maintenance.

💡 Optional: Import and use library utility
+import 'package:jose_plus/src/util.dart' show decodeBigInt;
+
-BigInt _decodeBase64UrlBigInt(String value) {
-  final bytes = base64Url.decode(value + '=' * ((4 - value.length % 4) % 4));
-  var out = BigInt.zero;
-  for (final b in bytes) {
-    out = (out << 8) + BigInt.from(b);
-  }
-  return out;
-}

Then replace _decodeBase64UrlBigInt calls with decodeBigInt.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/jwk_test.dart` around lines 10 - 17, The test helper
_decodeBase64UrlBigInt duplicates library logic; remove that helper and import
and use the existing decodeBigInt utility instead—update test/jwk_test.dart to
import the module that exposes decodeBigInt, replace calls to
_decodeBase64UrlBigInt with decodeBigInt, and delete the _decodeBase64UrlBigInt
function to avoid duplication and validate the real implementation.
lib/src/jwk.dart (1)

83-100: Consider using the extension methods to avoid duplication.

The CRT calculation logic here duplicates RsaPrivateKeyCRTCalcExtension from jwa.dart. Using the extension methods would reduce duplication and centralize the calculation.

♻️ Optional: Use extension methods
       final k = privateKey;
-      // Calculate CRT coefficients
-      // dp = d mod (p-1)
-      BigInt dp = k.privateExponent % (k.firstPrimeFactor - BigInt.one);
-      // dq = d mod (q-1)
-      BigInt dq = k.privateExponent % (k.secondPrimeFactor - BigInt.one);
-      // qi = q^(-1) mod p
-      BigInt qi = k.secondPrimeFactor.modInverse(k.firstPrimeFactor);

       return JsonWebKey.rsa(
         modulus: privateKey.modulus,
         exponent: (publicKey as RsaPublicKey?)?.exponent,
         privateExponent: privateKey.privateExponent,
         firstPrimeFactor: privateKey.firstPrimeFactor,
         secondPrimeFactor: privateKey.secondPrimeFactor,
-        firstFactorCRTExponent: dp,
-        secondFactorCRTExponent: dq,
-        firstCRTCoefficient: qi,
+        firstFactorCRTExponent: k.calculateDp(),
+        secondFactorCRTExponent: k.calculateDq(),
+        firstCRTCoefficient: k.calculateQi(),
         keyId: keyId,
         extractable: extractable,
       );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/src/jwk.dart` around lines 83 - 100, The CRT coefficients are being
recomputed here (dp, dq, qi) duplicating logic in RsaPrivateKeyCRTCalcExtension
from jwa.dart; replace the manual calculations by calling the extension methods
on the RsaPrivateKey (use the extension provided by
RsaPrivateKeyCRTCalcExtension to get the firstFactorCRTExponent,
secondFactorCRTExponent, and firstCRTCoefficient) and pass those values into
JsonWebKey.rsa (rather than computing dp, dq, qi locally), keeping the existing
variables privateKey/publicKey and preserving the same JsonWebKey.rsa parameter
order.
lib/src/jwa.dart (1)

304-328: Consider adding guards for missing RSA private key fields.

The JsonWebKeyCRTCalcExtension methods assume the JWK contains d, p, and q fields. Calling these on a public-only RSA key or non-RSA key will throw a runtime error due to the as String casts. While this may be intentional (caller responsibility), adding precondition checks or documenting the requirement would improve safety.

💡 Optional: Add defensive checks
 extension JsonWebKeyCRTCalcExtension on JsonWebKey {
   /// Calculate dp = d mod (p-1)
   /// First factor CRT exponent
   BigInt calculateDp() {
+    if (this['d'] == null || this['p'] == null) {
+      throw StateError('RSA private key fields (d, p) required for CRT calculation');
+    }
     final d = decodeBigInt(this['d'] as String);
     final p = decodeBigInt(this['p'] as String);
     return d % (p - BigInt.one);
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/src/jwa.dart` around lines 304 - 328, The extension
JsonWebKeyCRTCalcExtension assumes presence of RSA private fields ('d','p','q')
and will throw on public-only/non-RSA keys; update calculateDp, calculateDq, and
calculateQi to validate that this['d'], this['p'], and this['q'] are non-null
and are Strings before decoding and either throw a clear
ArgumentError/StateError (e.g., "missing RSA private field 'd'") or return a
nullable BigInt, so callers get a deterministic error path; keep the same
decodeBigInt/modInverse usage after the checks and include the field names in
the error messages to aid debugging.
lib/src/jwe.dart (1)

69-130: Consider handling missing protected header in general JSON serialization.

The code at line 130 unconditionally calls JsonObject.decode(json['protected']), which will throw if protected is null. While RFC 7516 states that protected is REQUIRED for compact serialization, it may be absent in certain general JSON serialization scenarios where all header parameters are in unprotected headers. Currently, this would fail with a base64 decode error rather than a clear message.

The fallback branch (lines 100-103) correctly validates this, but the general/flattened branches don't. If protected being null is invalid for all serializations, consider adding a clearer error message.

💡 Optional: Add explicit validation
+          final protectedValue = json['protected'];
+          if (protectedValue == null && !json.containsKey('recipients')) {
+            throw ArgumentError('Missing protected header for JWE');
+          }
           protectedHeader: JsonObject.decode(json['protected']),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/src/jwe.dart` around lines 69 - 130, The code unconditionally calls
JsonObject.decode(json['protected']) when constructing protectedHeader, which
will throw a confusing error if json['protected'] is null; update the
construction logic (around the List.unmodifiable(...) return and the
protectedHeader assignment) to first check whether json.containsKey('protected')
and json['protected'] != null and, if missing, either set protectedHeader to
null (if your model permits) or throw an explicit ArgumentError('Missing
protected header for JWE') with a clear message; ensure this validation covers
both the general JSON (json['recipients']) and flattened (json['header'])
branches and keep the _JweRecipient._ and derivedRecipientHeader behavior
unchanged.
test/jwe_test.dart (1)

299-308: Consider extracting fallback JSON assembly into a small helper.

The repeated compact-parts-to-map logic is consistent but duplicated; a helper would reduce maintenance overhead in this test file.

♻️ Optional refactor sketch
+Map<String, dynamic> _fallbackJsonFromCompactParts(
+  List<String> parts, {
+  bool includeEncryptedKey = true,
+}) {
+  final json = <String, dynamic>{
+    'protected': parts[0],
+    'iv': parts[2],
+    'ciphertext': parts[3],
+    'tag': parts[4],
+  };
+  if (includeEncryptedKey) {
+    json['encrypted_key'] = parts[1];
+  }
+  return json;
+}
-      var fallbackJson = {
-        'protected': compact[0],
-        // intentionally no 'encrypted_key'
-        'iv': compact[2],
-        'ciphertext': compact[3],
-        'tag': compact[4]
-      };
+      var fallbackJson =
+          _fallbackJsonFromCompactParts(compact, includeEncryptedKey: false);

Also applies to: 325-331, 370-376

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/jwe_test.dart` around lines 299 - 308, Extract the repeated logic that
converts a JWE's compact serialization into the fallback JSON map into a small
helper (e.g., compactToFallbackJson or buildFallbackJsonFromJwe) that accepts
the JWE instance (or its compact string) and returns a Map with 'protected',
optional omission of 'encrypted_key', 'iv', 'ciphertext', and 'tag'; replace the
inline code that builds fallbackJson (the block using
jwe.toCompactSerialization().split('.') and assigning compact[0], compact[2],
compact[3], compact[4]) in the occurrences around the existing fallbackJson
usages so tests call the helper instead to remove duplication and keep behavior
identical.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CHANGELOG.md`:
- Around line 1-5: The CHANGELOG entry for 0.4.8 is missing the PR's key
features: add notes that JWK generation now supports RSA CRT parameters (dp, dq,
qi) and that the `extractable` flag (`ext`) is now handled; update the 0.4.8
list to include lines mentioning "RSA CRT parameters (`dp`, `dq`, `qi`) support
in JWK generation" and "support for `extractable`/`ext` flag in
JsonWebKey.fromCryptoKeys(), rsa(), ec(), and symmetric()" so reviewers can see
these features alongside the JWE parsing and tests changes.

In `@pubspec.yaml`:
- Line 12: Update the pubspec constraint decision for crypto_keys_plus: either
restore the minimum to ">=0.5.0" if the downgrade to ">=0.4.0" was accidental,
or keep ">=0.4.0" but update the changelog entry that currently states "Updated
to use crypto_keys_plus 0.5.0" to reflect the intentional change; reference the
package name crypto_keys_plus in pubspec.yaml and the changelog entry for
version 0.4.7 when making the correction so the minimum-version rationale is
consistent across files.

---

Outside diff comments:
In `@lib/src/jwk.dart`:
- Around line 393-399: The unwrapKey return builds a JWK with the wrong field
name; change the 'keyOperations' property to the RFC-compliant 'key_ops' so the
getter and other code (e.g., jwkFromCryptoKeyPair in jwa.dart and the
keyOperations getter) can read it; update the map in unwrapKey (the
JsonWebKey.fromJson call that uses encodeBase64EncodedBytes(v)) to use
'key_ops': ['encrypt', 'decrypt'] instead of 'keyOperations'.

---

Nitpick comments:
In `@lib/src/jwa.dart`:
- Around line 304-328: The extension JsonWebKeyCRTCalcExtension assumes presence
of RSA private fields ('d','p','q') and will throw on public-only/non-RSA keys;
update calculateDp, calculateDq, and calculateQi to validate that this['d'],
this['p'], and this['q'] are non-null and are Strings before decoding and either
throw a clear ArgumentError/StateError (e.g., "missing RSA private field 'd'")
or return a nullable BigInt, so callers get a deterministic error path; keep the
same decodeBigInt/modInverse usage after the checks and include the field names
in the error messages to aid debugging.

In `@lib/src/jwe.dart`:
- Around line 69-130: The code unconditionally calls
JsonObject.decode(json['protected']) when constructing protectedHeader, which
will throw a confusing error if json['protected'] is null; update the
construction logic (around the List.unmodifiable(...) return and the
protectedHeader assignment) to first check whether json.containsKey('protected')
and json['protected'] != null and, if missing, either set protectedHeader to
null (if your model permits) or throw an explicit ArgumentError('Missing
protected header for JWE') with a clear message; ensure this validation covers
both the general JSON (json['recipients']) and flattened (json['header'])
branches and keep the _JweRecipient._ and derivedRecipientHeader behavior
unchanged.

In `@lib/src/jwk.dart`:
- Around line 83-100: The CRT coefficients are being recomputed here (dp, dq,
qi) duplicating logic in RsaPrivateKeyCRTCalcExtension from jwa.dart; replace
the manual calculations by calling the extension methods on the RsaPrivateKey
(use the extension provided by RsaPrivateKeyCRTCalcExtension to get the
firstFactorCRTExponent, secondFactorCRTExponent, and firstCRTCoefficient) and
pass those values into JsonWebKey.rsa (rather than computing dp, dq, qi
locally), keeping the existing variables privateKey/publicKey and preserving the
same JsonWebKey.rsa parameter order.

In `@test/jwe_test.dart`:
- Around line 299-308: Extract the repeated logic that converts a JWE's compact
serialization into the fallback JSON map into a small helper (e.g.,
compactToFallbackJson or buildFallbackJsonFromJwe) that accepts the JWE instance
(or its compact string) and returns a Map with 'protected', optional omission of
'encrypted_key', 'iv', 'ciphertext', and 'tag'; replace the inline code that
builds fallbackJson (the block using jwe.toCompactSerialization().split('.') and
assigning compact[0], compact[2], compact[3], compact[4]) in the occurrences
around the existing fallbackJson usages so tests call the helper instead to
remove duplication and keep behavior identical.

In `@test/jwk_test.dart`:
- Around line 10-17: The test helper _decodeBase64UrlBigInt duplicates library
logic; remove that helper and import and use the existing decodeBigInt utility
instead—update test/jwk_test.dart to import the module that exposes
decodeBigInt, replace calls to _decodeBase64UrlBigInt with decodeBigInt, and
delete the _decodeBase64UrlBigInt function to avoid duplication and validate the
real implementation.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8c84f562-c7d5-4eca-a71c-9db3aabb1905

📥 Commits

Reviewing files that changed from the base of the PR and between a0b51cd and a2157fa.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • lib/src/jwa.dart
  • lib/src/jwe.dart
  • lib/src/jwk.dart
  • lib/src/util.dart
  • pubspec.yaml
  • test/jwa_test.dart
  • test/jwe_test.dart
  • test/jwk_test.dart

Comment thread CHANGELOG.md Outdated
Comment on lines +1 to +5
## 0.4.8
- **FIX**: JWE JSON parsing now handles missing `recipients` / `header` by deriving a single recipient from the protected header and validates absent `encrypted_key` for non-`dir` algorithms.
- **FEAT**: Support unpadded Base64URL protected header (new parsing test).
- **TEST**: Added fallback, AAD, key wrap, and error path tests.
- **CHORE**: Refactored `JsonWebEncryption.fromJson` for clarity.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Changelog entry is incomplete - missing RSA CRT and extractable features.

The 0.4.8 entry documents JWE parsing changes but omits the main features from this PR:

  • RSA CRT parameters (dp, dq, qi) support in JWK generation
  • extractable flag support (ext field) in JsonWebKey.fromCryptoKeys(), rsa(), ec(), and symmetric()

Consider adding these to the changelog for completeness.

📝 Suggested additions
 ## 0.4.8
  - **FIX**: JWE JSON parsing now handles missing `recipients` / `header` by deriving a single recipient from the protected header and validates absent `encrypted_key` for non-`dir` algorithms.
  - **FEAT**: Support unpadded Base64URL protected header (new parsing test). 
+ - **FEAT**: RSA JWK generation now includes CRT parameters (`dp`, `dq`, `qi`) for private keys.
+ - **FEAT**: Added `extractable` parameter to `JsonWebKey.fromCryptoKeys()`, `rsa()`, `ec()`, and `symmetric()` factory methods.
  - **TEST**: Added fallback, AAD, key wrap, and error path tests.
  - **CHORE**: Refactored `JsonWebEncryption.fromJson` for clarity.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## 0.4.8
- **FIX**: JWE JSON parsing now handles missing `recipients` / `header` by deriving a single recipient from the protected header and validates absent `encrypted_key` for non-`dir` algorithms.
- **FEAT**: Support unpadded Base64URL protected header (new parsing test).
- **TEST**: Added fallback, AAD, key wrap, and error path tests.
- **CHORE**: Refactored `JsonWebEncryption.fromJson` for clarity.
## 0.4.8
- **FIX**: JWE JSON parsing now handles missing `recipients` / `header` by deriving a single recipient from the protected header and validates absent `encrypted_key` for non-`dir` algorithms.
- **FEAT**: Support unpadded Base64URL protected header (new parsing test).
- **FEAT**: RSA JWK generation now includes CRT parameters (`dp`, `dq`, `qi`) for private keys.
- **FEAT**: Added `extractable` parameter to `JsonWebKey.fromCryptoKeys()`, `rsa()`, `ec()`, and `symmetric()` factory methods.
- **TEST**: Added fallback, AAD, key wrap, and error path tests.
- **CHORE**: Refactored `JsonWebEncryption.fromJson` for clarity.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CHANGELOG.md` around lines 1 - 5, The CHANGELOG entry for 0.4.8 is missing
the PR's key features: add notes that JWK generation now supports RSA CRT
parameters (dp, dq, qi) and that the `extractable` flag (`ext`) is now handled;
update the 0.4.8 list to include lines mentioning "RSA CRT parameters (`dp`,
`dq`, `qi`) support in JWK generation" and "support for `extractable`/`ext` flag
in JsonWebKey.fromCryptoKeys(), rsa(), ec(), and symmetric()" so reviewers can
see these features alongside the JWE parsing and tests changes.

Comment thread pubspec.yaml

dependencies:
crypto_keys_plus: ">=0.5.0 <1.0.0"
crypto_keys_plus: ">=0.4.0 <1.0.0"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check crypto_keys_plus versions and their RsaPrivateKey API
echo "Checking crypto_keys_plus package info on pub.dev..."
curl -s "https://pub.dev/api/packages/crypto_keys_plus" | jq '.versions | map(.version) | .[-10:]'

Repository: Bdaya-Dev/jose

Length of output: 135


🏁 Script executed:

# Search for RsaPrivateKeyCRTCalcExtension usage in the codebase
rg "RsaPrivateKeyCRTCalcExtension|privateExponent|firstPrimeFactor|secondPrimeFactor" -A 2 -B 2

Repository: Bdaya-Dev/jose

Length of output: 3369


🏁 Script executed:

# Check the actual pubspec.yaml to confirm the constraint
cat pubspec.yaml | head -20

Repository: Bdaya-Dev/jose

Length of output: 513


🏁 Script executed:

# Search for references to crypto_keys_plus in the codebase
rg "crypto_keys_plus" -B 2 -A 2

Repository: Bdaya-Dev/jose

Length of output: 1826


🌐 Web query:

crypto_keys_plus 0.4.0 RsaPrivateKey API changelog

💡 Result:

In crypto_keys_plus 0.4.0, there are no documented, direct API changes to RsaPrivateKey itself (its constructor, fields, and methods appear unchanged in the 0.4.0 API docs). [2]

What did change in 0.4.0 that can affect RSA key handling:

  • Behavior change when parsing/recognizing key material: the package now returns null instead of throwing when it encounters an unknown curve/key type (this can surface when constructing keys/keypairs from serialized formats such as JWK, depending on input). [1]
  • Internal refactor: removed the dependency on quiver (not an RsaPrivateKey API change, but relevant for dependency/changelog tracking). [1]

Sources: crypto_keys_plus 0.4.0 changelog [1], RsaPrivateKey 0.4.0 API docs [2].


Clarify the rationale for lowering the crypto_keys_plus constraint.

The constraint was lowered from >=0.5.0 to >=0.4.0 in this version, but the changelog for 0.4.7 explicitly states "Updated to use crypto_keys_plus 0.5.0". The required properties (privateExponent, firstPrimeFactor, secondPrimeFactor) do exist in crypto_keys_plus 0.4.0, so API compatibility is not a concern. However, the version downgrade contradicts the previous upgrade decision. Please confirm whether this constraint change is intentional and update the changelog entry if needed to reflect this shift in minimum version requirements.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pubspec.yaml` at line 12, Update the pubspec constraint decision for
crypto_keys_plus: either restore the minimum to ">=0.5.0" if the downgrade to
">=0.4.0" was accidental, or keep ">=0.4.0" but update the changelog entry that
currently states "Updated to use crypto_keys_plus 0.5.0" to reflect the
intentional change; reference the package name crypto_keys_plus in pubspec.yaml
and the changelog entry for version 0.4.7 when making the correction so the
minimum-version rationale is consistent across files.

…d-extractable

# Conflicts:
#	lib/src/jwa.dart
@5eeman
Copy link
Copy Markdown
Author

5eeman commented Apr 8, 2026

@ahmednfwela Hi, thanks for supporting this package!
I've added one more PR that adds CRT fields which are required by some web crypto libs.

@5eeman
Copy link
Copy Markdown
Author

5eeman commented Apr 8, 2026

Sorry, this PR is kind of mixup, feel free to mention what you'd like to improve or toss out.

…d-extractable

# Conflicts:
#	CHANGELOG.md
#	lib/src/jwe.dart
#	pubspec.yaml
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