Skip to content

fix: add derivation support for ecdsa bitstring vc#81

Merged
RishabhS7 merged 1 commit into
alphafrom
fix/credential-derivation-bitstring
Sep 10, 2025
Merged

fix: add derivation support for ecdsa bitstring vc#81
RishabhS7 merged 1 commit into
alphafrom
fix/credential-derivation-bitstring

Conversation

@rongquan1
Copy link
Copy Markdown
Contributor

@rongquan1 rongquan1 commented Sep 10, 2025

Summary by CodeRabbit

  • Bug Fixes
    • Improves credential status verification by automatically deriving and re-verifying certain base credentials when initial checks fail.
    • Provides clearer error messages to callers while logging detailed context for troubleshooting.
    • Preserves successful verification behavior and public APIs (no breaking changes).

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Sep 10, 2025

Walkthrough

Adds a fallback verification path in credential status verification: on a specific failure, derive a credential first and re-run verification. Updates error handling to separate base error message from detailed logs. No public API signatures changed.

Changes

Cohort / File(s) Summary
Credential verification fallback flow
packages/w3c-vc/src/lib/verify/credentialStatus/index.ts
Imports deriveCredential; introduces DERIVE_CREDENTIAL_ERROR; switches result to let for reassignment; on specific verify failure, derives and re-verifies; refines error construction/logging while preserving success return shape.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant Verifier as CredentialStatusVerifier
  participant VC as verifyCredential(...)
  participant Deriver as deriveCredential(...)

  Client->>Verifier: verify(vcStatusList, options)
  Verifier->>VC: verifyCredential(vcStatusList, options)
  VC-->>Verifier: Error (may contain "Use deriveCredential() first")

  alt Error contains DERIVE_CREDENTIAL_ERROR
    Verifier->>Deriver: deriveCredential(vcStatusList, [])
    Deriver-->>Verifier: { derived }
    Verifier->>VC: verifyCredential(derived, options)
    alt Verification succeeds
      VC-->>Verifier: Success result
      Verifier-->>Client: Success result
    else Verification fails
      VC-->>Verifier: Error
      Note over Verifier: Log base + detailed error\nThrow base error message
      Verifier-->>Client: Error (base message)
    end
  else Other error
    Note over Verifier: Log base + detailed error\nThrow base error message
    Verifier-->>Client: Error (base message)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

released on @alpha

Suggested reviewers

  • nghaninn

Pre-merge checks (2 passed, 1 warning)

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description consists solely of the title and lacks any of the repository’s required sections such as summary of changes, motivation, implementation details, test plan, or documentation updates as specified by the project’s PR template. Please complete the PR description by filling out the repository’s standard template, including sections for the change summary, motivation, implementation details, testing plan, and any necessary documentation updates.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly and accurately describes the primary change—adding derivation support for ECDSA bitstring verifiable credentials—and uses a clear conventional-commit prefix without extraneous detail, making it immediately understandable to reviewers.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

Poem

A hop, a skip, a graceful veer—
When first we trip, derive is near.
We try again, with steady paws,
Re-check the claims, uphold the laws.
If errors thump, we log the trail—
Then bound ahead, with fluffy tail. 🐇✨

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/credential-derivation-bitstring

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.

@sonarqubecloud
Copy link
Copy Markdown

@rongquan1
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Sep 10, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

🧹 Nitpick comments (2)
packages/w3c-vc/src/lib/verify/credentialStatus/index.ts (2)

55-57: Brittle substring match; compute a needsDerivation flag using a structured code when available

Relying on the literal text "Use deriveCredential() first" is fragile and could break on message changes or localization. Compute a single needsDerivation flag that prefers a structured code and only falls back to substring matching.

Apply:

 const DERIVE_CREDENTIAL_ERROR = 'Use deriveCredential() first';
-let vcStatusListVerificationResult = await verifyCredential(vcStatusList, options);
+let vcStatusListVerificationResult = await verifyCredential(vcStatusList, options);
+const needsDerivation =
+  !vcStatusListVerificationResult?.verified &&
+  (
+    // Prefer a structured discriminator if available
+    (vcStatusListVerificationResult as any).code === 'VC_NEEDS_DERIVATION' ||
+    // Fallback to substring match
+    vcStatusListVerificationResult.error?.includes(DERIVE_CREDENTIAL_ERROR)
+  );

12-12: Introduce structured error codes for verifyCredential
Replace brittle .error.includes('Use deriveCredential() first') checks in packages/w3c-vc/src/lib/verify/credentialStatus/index.ts with a dedicated machine-readable error code or custom error class on VerificationResult (e.g. result.code === 'VC_NEEDS_DERIVATION').

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e13a1a5 and 522e816.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (1)
  • packages/w3c-vc/src/lib/verify/credentialStatus/index.ts (2 hunks)

Comment on lines +58 to +66
// Handle ECDSA-SD-2023 base credentials that need derivation before verification
if (
!vcStatusListVerificationResult?.verified &&
vcStatusListVerificationResult.error?.includes(DERIVE_CREDENTIAL_ERROR)
) {
const derivedResult = await deriveCredential(vcStatusList, []);
vcStatusListVerificationResult = await verifyCredential(derivedResult.derived, options);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Derivation path: pass documentLoader, track the verified VC, and base downstream decoding on it

  • Include options.documentLoader in deriveCredential for consistency.
  • Track which artifact (original vs derived) was actually verified, then decode/use that, not the pre-verified original.
  • Guard against absent derived.derived.

This avoids computing status from an object that wasn’t the one you verified.

If deriveCredential accepts options, confirm signature across the repo:

Suggested changes:

-// Handle ECDSA-SD-2023 base credentials that need derivation before verification
-if (
-  !vcStatusListVerificationResult?.verified &&
-  vcStatusListVerificationResult.error?.includes(DERIVE_CREDENTIAL_ERROR)
-) {
-  const derivedResult = await deriveCredential(vcStatusList, []);
-  vcStatusListVerificationResult = await verifyCredential(derivedResult.derived, options);
-}
+// Track which VC we ultimately verified
+let verifiedVC: SignedVerifiableCredential = vcStatusList;
+// Handle ECDSA-SD-2023 base credentials that need derivation before verification
+if (needsDerivation) {
+  const derivedResult = await deriveCredential(
+    vcStatusList,
+    [], // TODO: confirm minimal reveal frame is correct for status-list VCs
+    { documentLoader: options?.documentLoader } as any
+  );
+  const derivedVC = derivedResult?.derived;
+  if (derivedVC) {
+    verifiedVC = derivedVC;
+    vcStatusListVerificationResult = await verifyCredential(derivedVC, options);
+  }
+}

Follow-up (outside this hunk): move decoding/asserts to run against verifiedVC after verification:

// After verification succeeds:
const statusListCS = verifiedVC.credentialSubject as VCBitstringCredentialSubject;
assertStatusPurposeMatches(statusListCS, statusPurpose);
const bitstringStatusList = await StatusList.decode(statusListCS);
assertStatusListIndexWithinRange(bitstringStatusList, index);
const status = bitstringStatusList.getStatus(index);

🏁 Script executed:

#!/bin/bash
rg -nP -C3 '\bderiveCredential\s*\(' --type=ts

Length of output: 7578


🏁 Script executed:

#!/bin/bash
# Locate deriveCredential definition in w3c-vc.ts to confirm its signature
rg -nP 'export\s+(async\s+)?function\s+deriveCredential' -n packages/w3c-vc/src/lib/w3c-vc.ts
# Show surrounding lines to inspect parameters
sed -n '330,380p' packages/w3c-vc/src/lib/w3c-vc.ts

Length of output: 2143


🏁 Script executed:

#!/bin/bash
# Find the deriveCredential declaration to inspect its parameters
rg -nP 'export\s+(async\s+)?function\s+deriveCredential\b' -n packages/w3c-vc/src/lib
rg -nP 'const\s+deriveCredential\b' -n packages/w3c-vc/src/lib

Length of output: 77


🏁 Script executed:

#!/bin/bash
sed -n '380,440p' packages/w3c-vc/src/lib/w3c-vc.ts

Length of output: 2703


🏁 Script executed:

#!/bin/bash
sed -n '1,100p' packages/w3c-vc/src/lib/verify/credentialStatus/index.ts

Length of output: 3555


Track and Verify Derived Credential Before Decoding

  • Pass the incoming loader into deriveCredential, e.g.
    const derivedResult = await deriveCredential(vcStatusList, [], { documentLoader: options?.documentLoader });
  • Introduce let verifiedVC = vcStatusList; on a DERIVE_CREDENTIAL_ERROR verify failure, guard derivedResult.derived, set verifiedVC = derivedResult.derived, then re-verify.
  • Move assertStatusPurposeMatches, StatusList.decode, assertStatusListIndexWithinRange and bitstringStatusList.getStatus to after the successful verification of verifiedVC so you only decode a signature-verified credential.
🤖 Prompt for AI Agents
In packages/w3c-vc/src/lib/verify/credentialStatus/index.ts around lines 58 to
66, handle derived credentials and decoding only after signature verification:
pass the incoming document loader into deriveCredential (e.g.
deriveCredential(vcStatusList, [], { documentLoader: options?.documentLoader
})), introduce let verifiedVC = vcStatusList and when
vcStatusListVerificationResult indicates a DERIVE_CREDENTIAL_ERROR check that
derivedResult.derived exists, set verifiedVC = derivedResult.derived and re-run
verifyCredential(verifiedVC, options); only after the successful verification of
verifiedVC move/assert assertStatusPurposeMatches, call StatusList.decode and
assertStatusListIndexWithinRange and use bitstringStatusList.getStatus so
decoding and index checks happen on a signature-verified credential.

Comment on lines +67 to 76
// Throw error if verification still fails
if (!vcStatusListVerificationResult?.verified) {
console.error(
`Failed to verify Credential Status VC: ${vcStatusListVerificationResult.verified}. Error: ${vcStatusListVerificationResult.error}`,
);
throw new Error(
`Failed to verify Credential Status VC: ${vcStatusListVerificationResult.verified}`,
);
const errorMessage = `Failed to verify Credential Status VC: ${vcStatusListVerificationResult.verified}`;
const detailedError = vcStatusListVerificationResult.error
? `. Error: ${vcStatusListVerificationResult.error}`
: '';

console.error(errorMessage + detailedError);
throw new Error(errorMessage);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Preserve error details and avoid logging from library code

  • The message currently embeds a boolean (verified) and drops the underlying error for callers. Throw the combined message, not just the base string.
  • Prefer not to console.error in library code; let callers control logging.

Apply:

-// Throw error if verification still fails
 if (!vcStatusListVerificationResult?.verified) {
-  const errorMessage = `Failed to verify Credential Status VC: ${vcStatusListVerificationResult.verified}`;
-  const detailedError = vcStatusListVerificationResult.error
-    ? `. Error: ${vcStatusListVerificationResult.error}`
-    : '';
-
-  console.error(errorMessage + detailedError);
-  throw new Error(errorMessage);
+  const baseMessage = 'Failed to verify Credential Status VC';
+  const detailed = vcStatusListVerificationResult.error
+    ? `: ${vcStatusListVerificationResult.error}`
+    : '';
+  throw new Error(baseMessage + detailed);
 }
📝 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
// Throw error if verification still fails
if (!vcStatusListVerificationResult?.verified) {
console.error(
`Failed to verify Credential Status VC: ${vcStatusListVerificationResult.verified}. Error: ${vcStatusListVerificationResult.error}`,
);
throw new Error(
`Failed to verify Credential Status VC: ${vcStatusListVerificationResult.verified}`,
);
const errorMessage = `Failed to verify Credential Status VC: ${vcStatusListVerificationResult.verified}`;
const detailedError = vcStatusListVerificationResult.error
? `. Error: ${vcStatusListVerificationResult.error}`
: '';
console.error(errorMessage + detailedError);
throw new Error(errorMessage);
}
if (!vcStatusListVerificationResult?.verified) {
const baseMessage = 'Failed to verify Credential Status VC';
const detailed = vcStatusListVerificationResult.error
? `: ${vcStatusListVerificationResult.error}`
: '';
throw new Error(baseMessage + detailed);
}
🤖 Prompt for AI Agents
In packages/w3c-vc/src/lib/verify/credentialStatus/index.ts around lines 67 to
76, the code logs to console and throws an error message that only contains a
boolean; instead, remove the console.error call and throw an Error that includes
the combined detailed message (use vcStatusListVerificationResult.error when
present to append error details to the message), e.g. build a single error
string containing the verification state and underlying error and throw new
Error(combinedMessage) so callers receive the full error info and library code
does not perform direct logging.

@RishabhS7 RishabhS7 merged commit 5130d91 into alpha Sep 10, 2025
19 checks passed
@RishabhS7 RishabhS7 deleted the fix/credential-derivation-bitstring branch September 10, 2025 05:05
nghaninn pushed a commit that referenced this pull request Sep 10, 2025
@tradetrustimda
Copy link
Copy Markdown

🎉 This PR is included in version 1.3.0-alpha.8 🎉

The release is available on:

Your semantic-release bot 📦🚀

nghaninn pushed a commit that referenced this pull request Sep 10, 2025
@tradetrustimda
Copy link
Copy Markdown

🎉 This PR is included in version 1.3.0-alpha.10 🎉

The release is available on:

Your semantic-release bot 📦🚀

@tradetrustimda
Copy link
Copy Markdown

🎉 This PR is included in version 1.3.0-alpha.10 🎉

The release is available on:

Your semantic-release bot 📦🚀

nghaninn pushed a commit that referenced this pull request Sep 10, 2025
@tradetrustimda
Copy link
Copy Markdown

🎉 This PR is included in version 1.3.0-alpha.11 🎉

The release is available on:

Your semantic-release bot 📦🚀

nghaninn pushed a commit that referenced this pull request Sep 10, 2025
@tradetrustimda
Copy link
Copy Markdown

🎉 This PR is included in version 1.3.0-alpha.11 🎉

The release is available on:

Your semantic-release bot 📦🚀

nghaninn pushed a commit that referenced this pull request Sep 10, 2025
@tradetrustimda
Copy link
Copy Markdown

🎉 This PR is included in version 1.3.0-alpha.11 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants