From 09ac600b28c648087447d057be50c59abd607c6a Mon Sep 17 00:00:00 2001 From: A-Chronicle Date: Thu, 21 May 2026 12:42:35 +0530 Subject: [PATCH] docs: add comments explaining DID verification relationships and key purposes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add module-level documentation to FindDIDSigningKeys explaining: * DID verification relationships concept * ISSUING_KEY ↔ assertionMethod mapping (for credential issuance) * AUTHENTICATION_KEY ↔ authentication mapping (for DID ownership proofs) * Why multiple keys exist (key rotation, security, scalability) * Reference to W3C DID Core Specification * Other verification relationships: keyAgreement, capabilityInvocation, capabilityDelegation - Enhance CreateJwt documentation: * Explain SSI JWT use cases * Clarify why ISSUING_KEY is default for credential issuance * Document when to use AUTHENTICATION_KEY for authentication flows * Add complete JSDoc parameter descriptions - Add CreateSDJWT documentation: * Explain Selective Disclosure JWT (SD-JWT) concept * Document privacy-preserving credential sharing use case * Reference RFC 9052 specification * Explain verification process * Confirm SD-JWT also defaults to ISSUING_KEY - Add matchKeys() documentation: * Explain key matching algorithm * Clarify multibase and JWK encoding support * Ensure keys are authorized for requested purpose - Provide complete DID document structure example with all verification relationships - Clarify that SDK currently supports ISSUING_KEY and AUTHENTICATION_KEY Addresses issue #598: Makes it easier for new contributors to understand DID verification relationships, key purposes, and SSI concepts. Signed-off-by: A-Chronicle --- .../didFunctions/FindDIDSigningKeys.ts | 88 ++++++++++++++++--- .../lib/sdk/src/pollux/utils/jwt/CreateJwt.ts | 38 ++++++-- .../sdk/src/pollux/utils/jwt/CreateSDJWT.ts | 37 ++++++-- 3 files changed, 138 insertions(+), 25 deletions(-) diff --git a/packages/lib/sdk/src/edge-agent/didFunctions/FindDIDSigningKeys.ts b/packages/lib/sdk/src/edge-agent/didFunctions/FindDIDSigningKeys.ts index 6a3a4f67d..708cc2c26 100644 --- a/packages/lib/sdk/src/edge-agent/didFunctions/FindDIDSigningKeys.ts +++ b/packages/lib/sdk/src/edge-agent/didFunctions/FindDIDSigningKeys.ts @@ -6,8 +6,38 @@ import type * as Domain from "@hyperledger/identus-domain"; import { base64url } from "multiformats/bases/base64"; /** - * Maps DID key purposes to W3C DID Document verification relationships. - * @see https://www.w3.org/TR/did-core/#verification-relationships + * DID Verification Relationships — Core Concepts for Self-Sovereign Identity (SSI) + * + * A Decentralized Identifier (DID) can prove ownership through cryptographic keys. + * Different operations require different keys — this is the concept of "verification relationships". + * + * See: https://www.w3.org/TR/did-core/#verification-relationships + * + * Mapping of SDK key purposes to DID Document verification relationships: + * + * | SDK Purpose | DID Verification Relationship | Use Case | + * |---------------------------|-------------------------------|-----------------------------------| + * | ISSUING_KEY | assertionMethod | Credential issuance (JWT/SD-JWT) | + * | AUTHENTICATION_KEY | authentication | Proving DID ownership | + * | KEY_AGREEMENT_KEY | keyAgreement | Encryption / key exchange | + * | CAPABILITY_INVOCATION_KEY | capabilityInvocation | Invoking delegated capabilities | + * | CAPABILITY_DELEGATION_KEY | capabilityDelegation | Delegating capabilities to others | + * | REVOCATION_KEY | revocation | Revoking credentials/keys | + * + * Why Multiple Keys? + * - Key rotation: If an issuing key is compromised, you can rotate it without affecting authentication + * - Security: Each key should only be used for its intended purpose + * - Scalability: The SDK can support additional key purposes as SSI evolves + * + * DID Document Structure (simplified): + * { + * "id": "did:prism:abc123...", + * "assertionMethod": ["#key-1"], // ISSUING_KEY + * "authentication": ["#key-2"], // AUTHENTICATION_KEY + * "keyAgreement": ["#key-3"], // KEY_AGREEMENT_KEY (future) + * "capabilityInvocation": ["#key-4"], // CAPABILITY_INVOCATION_KEY (future) + * "capabilityDelegation": ["#key-5"] // CAPABILITY_DELEGATION_KEY (future) + * } */ const PURPOSE_TO_VERIFICATION_RELATIONSHIP: Record = { AUTHENTICATION_KEY: "authentication", @@ -36,20 +66,35 @@ interface FindSigningKeysArgs { } /** - * Search for the PrivateKeys that should be used for signing based on their key purpose. - * Maps DID key purposes to W3C DID Document verification relationships. + * Finds the private signing keys for a DID that match a specific verification relationship purpose. + * + * This task: + * 1. Resolves the DID document to get verification methods + * 2. Maps the given purpose to a W3C DID verification relationship + * 3. Matches those verification methods against available private keys + * 4. Returns the keys that can be used for the specified purpose + * + * Workflow: + * - ISSUING_KEY → looks in the DID's "assertionMethod" (for credential issuance) + * - AUTHENTICATION_KEY → looks in the DID's "authentication" (for DID ownership proofs) + * - Matches keys by multibase encoding or JWK representation + * * @see https://www.w3.org/TR/did-core/#verification-relationships * - * @param {Domain.DID} did subject of the search - * @param {Domain.PrivateKey} [privateKey] optional filter search to only this PrivateKey - * @param {string} [purpose] key purpose to search for: - * - "AUTHENTICATION_KEY" → "authentication" (proving ownership/control) - * - "ISSUING_KEY" → "assertionMethod" (issuing credentials) - * - "KEY_AGREEMENT_KEY" → "keyAgreement" (key agreement for encryption) - * - "CAPABILITY_INVOCATION_KEY" → "capabilityInvocation" (invoking capabilities) - * - "CAPABILITY_DELEGATION_KEY" → "capabilityDelegation" (delegating capabilities) - * - "REVOCATION_KEY" → "revocation" (revoking credentials/keys) + * @param {Domain.DID} did The DID subject — resolves its DID document + * @param {Domain.PrivateKey} [privateKey] Optional: filter to only check this specific key + * @param {string} purpose Key purpose to search for: + * - AUTHENTICATION_KEY → "authentication" (proving ownership/control) + * - ISSUING_KEY → "assertionMethod" (issuing credentials) + * - KEY_AGREEMENT_KEY → "keyAgreement" (key agreement for encryption) + * - CAPABILITY_INVOCATION_KEY → "capabilityInvocation" (invoking capabilities) + * - CAPABILITY_DELEGATION_KEY → "capabilityDelegation" (delegating capabilities) + * - REVOCATION_KEY → "revocation" (revoking credentials/keys) + * @returns {SigningKeyData[]} Array of matched signing keys with public key info and key IDs * + * Example usage: + * - CreateJWT defaults to ISSUING_KEY for credential issuance + * - Authentication flows use AUTHENTICATION_KEY for DID ownership proofs */ export class FindSigningKeys extends Task { async run(ctx: AgentContext) { @@ -73,6 +118,23 @@ export class FindSigningKeys extends Task { return signingKeyData; } + /** + * Matches verification methods from the DID document with available private keys. + * + * For each verification method in the DID document: + * 1. Encode the private key's public key in the same format (multibase or JWK) + * 2. Compare against the verification method's public key representation + * 3. If they match, include this key-pair in the results + * + * This ensures we only return keys that are: + * - Actually present in the DID document + * - Listed for the requested verification relationship + * - Have corresponding private keys available locally + * + * Why two encoding formats? DIDs can represent keys as either: + * - "publicKeyMultibase": compact encoding (base58, etc) + * - "publicKeyJwk": JSON Web Key format (for JWK-based cryptography) + */ private matchKeys( methods: Domain.DIDDocument.VerificationMethod[], keyData: { privateKey: any; publicKey: any; encoded: string; encodedBase64Url: string }[] diff --git a/packages/lib/sdk/src/pollux/utils/jwt/CreateJwt.ts b/packages/lib/sdk/src/pollux/utils/jwt/CreateJwt.ts index ee7f784cc..b4ada9d60 100644 --- a/packages/lib/sdk/src/pollux/utils/jwt/CreateJwt.ts +++ b/packages/lib/sdk/src/pollux/utils/jwt/CreateJwt.ts @@ -8,13 +8,35 @@ import { base64url } from "multiformats/bases/base64"; import { FindSigningKeys } from "../../../edge-agent/didFunctions/FindDIDSigningKeys"; /** - * Asyncronously sign with a DID + * Creates a signed JWT (JSON Web Token) using a DID's private key. * - * - * @param {DID} did - * @param payload - * @param header - * @returns {string} + * In Self-Sovereign Identity (SSI) systems, JWTs are commonly used for: + * - Issuing verifiable credentials (VCs) + * - Creating proof of DID ownership + * - Sharing signed claims about subjects + * + * Key Purpose Selection: + * - Default is "ISSUING_KEY" because most JWT creation is for credential issuance, + * which requires the "assertionMethod" capability (see DID spec) + * - Use "AUTHENTICATION_KEY" only when signing challenge-response proofs for DID ownership + * + * How it works: + * 1. Finds signing keys matching the specified purpose + * 2. Creates a JWT with the DID as the issuer + * 3. Signs with the found private key + * 4. Returns the signed JWT string (can be transmitted to other parties) + * + * The recipient can verify this JWT by: + * - Resolving your DID document + * - Getting your public key from the matching verification relationship + * - Using the public key to verify the JWT signature + * + * @param {DID} did - The DID that will be the issuer of this JWT + * @param {JWT.Payload} payload - The claims to include in the JWT + * @param {JWT.Header} [header] - Optional custom JWT header fields + * @param {PrivateKey} [privateKey] - Optional: specific key to use (otherwise searches all keys) + * @param {keyof PrismDIDKeys} [purpose] - Verification relationship to use (default: "ISSUING_KEY") + * @returns {string} The signed JWT */ interface Args { @@ -27,6 +49,10 @@ interface Args { export class CreateJWT extends Task { async run(ctx: AgentContext) { + // Default to ISSUING_KEY because JWT creation is most commonly used for: + // - Credential issuance (requires "assertionMethod" in DID document) + // - Proof of claims about subjects + // Change to AUTHENTICATION_KEY only for challenge-response authentication flows const signingKeys = await ctx.run(new FindSigningKeys({ did: this.args.did, privateKey: this.args.privateKey, diff --git a/packages/lib/sdk/src/pollux/utils/jwt/CreateSDJWT.ts b/packages/lib/sdk/src/pollux/utils/jwt/CreateSDJWT.ts index 460dc1206..0e607ed65 100644 --- a/packages/lib/sdk/src/pollux/utils/jwt/CreateSDJWT.ts +++ b/packages/lib/sdk/src/pollux/utils/jwt/CreateSDJWT.ts @@ -8,13 +8,33 @@ import { FindSigningKeys } from "../../../edge-agent/didFunctions/FindDIDSigning import { expect } from "../../../utils"; /** - * Asyncronously sign with a DID + * Creates a signed SD-JWT (Selective Disclosure JWT) using a DID's private key. * - * - * @param {DID} did - * @param payload - * @param header - * @returns {string} + * SD-JWT (RFC 9052) is an extension of JWT that allows: + * - Issuing credentials with selective disclosure capabilities + * - Holders to selectively reveal only necessary claims to verifiers + * - Privacy-preserving credential presentation + * + * Example use case: + * - An issuer creates an SD-JWT with claims: name, age, address, email + * - The holder can share a version that only discloses: name and age + * - Verifier can still verify the issuer's signature on the hidden claims + * + * Key Purpose Selection: + * - Default is "ISSUING_KEY" for credential issuance (requires "assertionMethod" in DID) + * - This is appropriate because SD-JWTs are typically issued as verifiable credentials + * - Note: Like CreateJWT, SD-JWT also defaults to ISSUING_KEY since both are used for + * credential issuance flows. Use AUTHENTICATION_KEY only for authentication challenges. + * + * See: https://datatracker.ietf.org/doc/html/rfc9052 (SD-JWT specification) + * + * @param {DID} did - The DID that will issue this SD-JWT credential + * @param {SdJwtVcPayload} payload - The credential claims to include + * @param {JWT.Header} [header] - Optional custom JWT header fields + * @param {DisclosureFrame} disclosureFrame - Specifies which claims are selectively disclosable + * @param {PrivateKey} [privateKey] - Optional: specific key to use (otherwise searches all keys) + * @param {keyof PrismDIDKeys} [purpose] - Verification relationship (default: "ISSUING_KEY") + * @returns {string} The signed SD-JWT */ interface Args { @@ -29,6 +49,11 @@ interface Args { export class CreateSDJWT extends Task { async run(ctx: Plugins.Context) { + // Default to ISSUING_KEY because SD-JWTs are typically issued as verifiable credentials. + // The issuer signs the credential, allowing verifiers to: + // 1. Verify the issuer's signature + // 2. Selectively disclose only necessary claims + // The issuer's public key is found via the "assertionMethod" in their DID document. const signingKeys = await ctx.run( new FindSigningKeys({ did: this.args.did,