diff --git a/README.md b/README.md index 2aaffb3..7c51c4b 100644 --- a/README.md +++ b/README.md @@ -16,15 +16,16 @@ TrustVC is a comprehensive wrapper library designed to simplify the signing and - [2. **Signing**](#2-signing) - [a) OpenAttestation Signing (signOA) v2 v3](#a-openattestation-signing-signoa-v2-v3) - [b) TrustVC W3C Signing (signW3C)](#b-trustvc-w3c-signing-signw3c) - - [3. **Verifying**](#3-verifying) - - [4. **Encryption**](#4-encryption) - - [5. **Decryption**](#5-decryption) - - [6. **TradeTrust Token Registry**](#6-tradetrust-token-registry) + - [3. **Deriving (Selective Disclosure)**](#3-deriving-selective-disclosure) + - [4. **Verifying**](#4-verifying) + - [5. **Encryption**](#5-encryption) + - [6. **Decryption**](#6-decryption) + - [7. **TradeTrust Token Registry**](#7-tradetrust-token-registry) - [Usage](#usage-2) - [TradeTrustToken](#tradetrusttoken) - [a) Token Registry v4](#a-token-registry-v4) - [b) Token Registry V5](#b-token-registry-v5) - - [7. **Document Builder**](#7-document-builder) + - [8. **Document Builder**](#8-document-builder) ## Installation @@ -154,15 +155,17 @@ const signedWrappedDocument = await signOA(wrappedDocument, { #### b) TrustVC W3C Signing (signW3C) +The `signW3C` function signs W3C Verifiable Credentials using the provided cryptographic suite and key pair. By default, it uses the **ecdsa-sd-2023** crypto suite unless otherwise specified. + ```ts import { signW3C, VerificationType } from '@trustvc/trustvc'; const rawDocument = { '@context': [ - 'https://www.w3.org/2018/credentials/v1', - 'https://w3c-ccg.github.io/citizenship-vocab/contexts/citizenship-v1.jsonld', - 'https://w3id.org/security/bbs/v1', + 'https://www.w3.org/ns/credentials/v2', + 'https://w3id.org/security/data-integrity/v2', 'https://w3id.org/vc/status-list/2021/v1', + 'https://w3c-ccg.github.io/citizenship-vocab/contexts/citizenship-v2.jsonld', ], credentialStatus: { id: 'https://trustvc.github.io/did/credentials/statuslist/1#1', @@ -172,29 +175,113 @@ const rawDocument = { statusListCredential: 'https://trustvc.github.io/did/credentials/statuslist/1', }, credentialSubject: { - name: 'TrustVC', + type: ['Person'] + givenName: 'TrustVC', birthDate: '2024-04-01T12:19:52Z', - type: ['PermanentResident', 'Person'], }, - expirationDate: '2029-12-03T12:19:52Z', issuer: 'did:web:trustvc.github.io:did:1', type: ['VerifiableCredential'], - issuanceDate: '2024-04-01T12:19:52Z', + validFrom: '2024-04-01T12:19:52Z', + validUntil: '2029-12-03T12:19:52Z' }; +// Using default ecdsa-sd-2023 crypto suite const signingResult = await signW3C(rawDocument, { - id: 'did:web:trustvc.github.io:did:1#keys-1', + '@context': 'https://w3id.org/security/multikey/v1', + id: 'did:web:trustvc.github.io:did:1#multikey-1', + type: VerificationType.Multikey, controller: 'did:web:trustvc.github.io:did:1', - type: VerificationType.Bls12381G2Key2020, - publicKeyBase58: - 'oRfEeWFresvhRtXCkihZbxyoi2JER7gHTJ5psXhHsdCoU1MttRMi3Yp9b9fpjmKh7bMgfWKLESiK2YovRd8KGzJsGuamoAXfqDDVhckxuc9nmsJ84skCSTijKeU4pfAcxeJ', - privateKeyBase58: '', + publicKeyMultibase: 'zDnaemDNwi4G5eTzGfRooFFu5Kns3be6yfyVNtiaMhWkZbwtc', + secretKeyMultibase: '' +}); + +// You can also specify mandatory pointers for selective disclosure with ecdsa-sd-2023 +const signingResultWithPointers = await signW3C( + rawDocument, + { + '@context': 'https://w3id.org/security/multikey/v1', + id: 'did:web:trustvc.github.io:did:1#multikey-1', + type: VerificationType.Multikey, + controller: 'did:web:trustvc.github.io:did:1', + publicKeyMultibase: 'zDnaemDNwi4G5eTzGfRooFFu5Kns3be6yfyVNtiaMhWkZbwtc', + secretKeyMultibase: '' + }, + 'ecdsa-sd-2023', + { + mandatoryPointers: ['/credentialStatus'] + } +); + +// Alternatively, specify a different crypto suite. Ensure the context is updated to include the crypto suite. +const signingResultWithBbs = await signW3C( + rawDocument, + { + id: 'did:web:trustvc.github.io:did:1#keys-1', + controller: 'did:web:trustvc.github.io:did:1', + type: VerificationType.Bls12381G2Key2020, + publicKeyBase58: 'oRfEeWFresvhRtXCkihZbxyoi2JER7gHTJ5psXhHsdCoU1MttRMi3Yp9b9fpjmKh7bMgfWKLESiK2YovRd8KGzJsGuamoAXfqDDVhckxuc9nmsJ84skCSTijKeU4pfAcxeJ', + privateKeyBase58: '', + }, + 'BbsBlsSignature2020' +); + +``` + +--- + +### 3. **Deriving (Selective Disclosure)** + +> When using ECDSA-SD-2023 crypto suite, we can derive a new credential with selective disclosure. This means you can choose which parts of the credential to reveal while keeping others hidden. + +```ts +import { deriveW3C } from '@trustvc/trustvc'; + +// This is a signed document using ecdsa-sd-2023 +const signedDocument = { + '@context': [ + 'https://www.w3.org/ns/credentials/v2', + 'https://w3id.org/security/data-integrity/v2', + 'https://w3id.org/vc/status-list/2021/v1', + 'https://w3c-ccg.github.io/citizenship-vocab/contexts/citizenship-v2.jsonld' + ], + credentialStatus: { + id: 'https://trustvc.github.io/did/credentials/statuslist/1#1', + type: 'StatusList2021Entry', + statusPurpose: 'revocation', + statusListIndex: '10', + statusListCredential: 'https://trustvc.github.io/did/credentials/statuslist/1' + }, + credentialSubject: { + type: ['Person'], + givenName: 'TrustVC', + birthDate: '2024-04-01T12:19:52Z' + }, + issuer: 'did:web:trustvc.github.io:did:1', + type: ['VerifiableCredential'], + validFrom: '2024-04-01T12:19:52Z', + validUntil: '2029-12-03T12:19:52Z', + id: 'urn:uuid:0198bd9e-6686-7ccd-9b2a-ce763ae710d7', + proof: { + type: 'DataIntegrityProof', + created: '2025-08-18T14:38:51Z', + verificationMethod: 'did:web:trustvc.github.io:did:1#multikey-1', + cryptosuite: 'ecdsa-sd-2023', + proofPurpose: 'assertionMethod', + proofValue: 'u2V0AhVhAxfLFkbv8J_O3zJAQrSWrEY3sgeMwN02b2eaHEgjnJYu1rnCBYORfZUVZwRoRuNIiY1NTGHmQpzlgqtQz7A0R3FgjgCQDzt3_aUvSMrlIZdsyVcB4KPHHjA4BbSv-PZ4Bbm4GpY5YIA1mQ8LYmpjJ7vNvN3DsfIengZrnziTLO9exbZjn1KqFilhA0lp1y6BZ-fhiUdWsojYesLDSzCy6Tq_AICaIvCjYSJMEaY7SomJnCkdpuhM0GQHDTy5kjzb7sSzowACqDDf9OVhAfOC7vg4WQGrI6M3dvLZW3KlBzp1SurRz1PPeHcqOGEDrqybzIlolwNXMhc2T8rcVLl-E04wNsiVjamvqWAQN-lhA4HmVqIxKuR0QvCMEVq3cjUU7G1pQbgMdp9HZDasOT9nh_k5l3JfcXB1_qtRblljXWN0FRKAr9T-DhxzDzGl3-lhA4nNDzd-6xl74rWqr_7U9XZE7LoE-mbgBsyOAOlfHGumMxwddnEZp2iD2uZ7lLXX8Q-nSDXJVvUqKLksy1l2vqVhAm3daNYjH1kVrTW7V-DElcj3K_QfbHEvjd1F2TGVGtBVhF8o01yCxXRX0vzk-AZLZnpDnAUBTSTF5Q8rF-t7L9lhAO7NeIXQtQsdncqtLm2qk1XzFYL2FM5Hx4GZOX39VyT4T0AlFRZQuY9WXYnvMZSvacRvJaSJk5S3cZ6uBminQgVhAExuTEvJQu42-SiaOJ_6M0EjuQfqIgJE-JHirmYs3AAoH_4EKUtPU3y_jRB8XFZxA-wtFDv3KJjqXtNo5aA_6f1hAaokZPSJghFufTaVR8LAwHpXOncGJblKpUZQjKWuA_o2s6tGmx-ja0wgpsqSxvAGMTtkhFTMOI2-tzUuGE05tk1hAzABtV2yEX-RAQFpxkuV0XydAsJDh2dPscrpPHqMfmORsC3xRNL73uDaqqlaL99CvOgq4kJWmChw7TUYO62yaSVhA5-F-snwj-OZtws7_qMwvBgeNK9wvkZTlFLjRV6GDYx6r5TaLkR05GVzyBMv0Qs2z-cXPRZByS7p7_hbeykoYSYJnL2lzc3VlcmovdmFsaWRGcm9t' + } +}; + +// Derive a new credential with only specific fields disclosed +const derivationResult = await deriveW3C(signedDocument, { + // Only reveal the credential type and givenName, hide birthDate + selectivePointers: ['/type', '/credentialSubject/givenName'] }); + ``` --- -### 3. **Verifying** +### 4. **Verifying** > TrustVC simplifies the verification process with a single function that supports both W3C Verifiable Credentials (VCs) and OpenAttestation Verifiable Documents (VDs). Whether you're working with W3C standards or OpenAttestation standards, TrustVC handles the verification seamlessly. @@ -239,7 +326,7 @@ const resultFragments = await verifyDocument(signedDocument); --- -### 4. **Encryption** +### 5. **Encryption** > The `encrypt` function encrypts plaintext messages using the **ChaCha20** encryption algorithm, ensuring the security and integrity of the input data. It supports custom keys and nonces, returning the encrypted message in hexadecimal format. @@ -316,7 +403,7 @@ It also relies on the `ts-chacha20` library for encryption operations. --- -### 5. **Decryption** +### 6. **Decryption** > The `decrypt` function decrypts messages encrypted with the **ChaCha20** algorithm. It converts the input from a hexadecimal format back into plaintext using the provided key and nonce. @@ -399,7 +486,7 @@ It also relies on the `ts-chacha20` library for decryption operations. --- -### 6. **TradeTrust Token Registry** +### 7. **TradeTrust Token Registry** > The Electronic Bill of Lading (eBL) is a digital document that can be used to prove the ownership of goods. It is a standardized document that is accepted by all major shipping lines and customs authorities. The [Token Registry](https://github.com/TradeTrust/token-registry) repository contains both the smart contract (v4 and v5) code for token registry (in `/contracts`) as well as the node package for using this library (in `/src`). > The TrustVC library not only simplifies signing and verification but also imports and integrates existing TradeTrust libraries and smart contracts for token registry (V4 and V5), making it a versatile tool for decentralized identity and trust solutions. @@ -589,8 +676,8 @@ function rejectTransferOwners(bytes calldata _remark) external; For more information on Token Registry and Title Escrow contracts **version v5**, please visit the readme of [TradeTrust Token Registry V5](https://github.com/TradeTrust/token-registry/blob/master/README.md) -### 7. **Document Builder** -> The `DocumentBuilder` class helps build and manage W3C Verifiable Credentials (VCs) with credential status features. It supports creating documents with two types of credential statuses: `transferableRecords` and `verifiableDocument`. It can sign the document using a private key, verify its signature, and serialize the document to a JSON format. Additionally, it allows for configuration of document rendering methods and expiration dates. +### 8. **Document Builder** +> The `DocumentBuilder` class helps build and manage W3C Verifiable Credentials (VCs) with credential status features, implementing the **W3C VC Data Model 2.0** specification. It supports creating documents with two types of credential statuses: `transferableRecords` and `verifiableDocument`. It can sign the document using a private key, verify its signature, and serialize the document to a JSON format. Additionally, it allows for configuration of document rendering methods and expiration dates. #### Usage @@ -603,7 +690,7 @@ To learn more about defining custom contexts, check out the [Credential Subject // Adds a custom vocabulary used to define terms in the `credentialSubject`. // Users can define their own context if they have domain-specific fields or custom data structures. const builder = new DocumentBuilder({ - '@context': 'https://w3c-ccg.github.io/citizenship-vocab/contexts/citizenship-v1.jsonld' + '@context': 'https://w3c-ccg.github.io/citizenship-vocab/contexts/citizenship-v2.jsonld' }); ``` @@ -612,8 +699,8 @@ Set the subject of the Verifiable Credential, which typically contains informati ```ts builder.credentialSubject({ - id: 'did:example:123', - name: 'John Doe', + type: ['Person'], + givenName: 'TrustVC', }); ``` @@ -649,7 +736,7 @@ builder.credentialStatus({ ``` ##### Set Expiration Date -You can set an expiration date for the document. +You can set a valid until date (expiration) for the document. ```ts builder.expirationDate('2026-01-01T00:00:00Z'); @@ -677,16 +764,17 @@ builder.qrCode({ ``` ##### Sign the Document -To sign the document, provide a `PrivateKeyPair` from `@trustvc/trustvc`. +To sign the document, provide a `PrivateKeyPair` from `@trustvc/trustvc`. The builder uses ECDSA key for signing by default. ```ts const privateKey: PrivateKeyPair = { - id: 'did:example:456#key1', - controller: 'did:example:456', - type: VerificationType.Bls12381G2Key2020, - publicKeyBase58: 'your-public-key-base58', - privateKeyBase58: 'your-private-key-base58', -}; + '@context': 'https://w3id.org/security/multikey/v1', + id: 'did:web:example.com#multikey-1', + type: VerificationType.Multikey, + controller: 'did:web:example.com', + publicKeyMultibase: 'your-public-key-multibase', + secretKeyMultibase: 'your-secret-key-multibase', +} const signedDocument = await builder.sign(privateKey); console.log(signedDocument); @@ -696,19 +784,18 @@ Example Output After Signing ```json { "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://w3c-ccg.github.io/citizenship-vocab/contexts/citizenship-v1.jsonld", - "https://w3id.org/vc/status-list/2021/v1", - "https://trustvc.io/context/render-method-context.json", + "https://www.w3.org/ns/credentials/v2", + "https://w3c-ccg.github.io/citizenship-vocab/contexts/citizenship-v2.jsonld", + "https://trustvc.io/context/render-method-context-v2.json", "https://trustvc.io/context/qrcode-context.json", - "https://w3id.org/security/bbs/v1" + "https://w3id.org/security/data-integrity/v2" ], "type": ["VerifiableCredential"], "credentialSubject": { - "id": "did:example:123", - "name": "John Doe" + "type": ["Person"], + "givenName": "TrustVC", }, - "expirationDate": "2026-01-01T00:00:00Z", + "validUntil": "2026-01-01T00:00:00Z", "renderMethod": [ { "id": "https://example.com/rendering-method", @@ -727,21 +814,30 @@ Example Output After Signing "statusListIndex": "", "statusListCredential": "https://example.com/status-list" }, - "issuer": "did:example:456", - "issuanceDate": "2025-01-01T00:00:00Z", + "issuer": "did:web:example.com", + "validFrom": "2025-01-01T00:00:00Z", "id": "urn:bnid:_:0195fec2-4ae1-7cca-9182-03fd7da5142b", "proof": { - "type": "BbsBlsSignature2020", + "type": "DataIntegrityProof", "created": "2025-01-01T00:00:01Z", + "verificationMethod": "did:web:example.com#multikey-1", + "cryptosuite": "ecdsa-sd-2023", "proofPurpose": "assertionMethod", - "proofValue": "rV56L+QYozATRy3GOVLomzUo99sXtw2x0Cy9dEkHJ15wi4cS12cQJRIwzONVi3YscdhaSKoqD1jWmwb5A/khLZnDq5eo3QzDgTVClYuV86opL3HJyoS4+t2rRt3wl+chnATy2jqr5zMEvcVJ3gdXpQ==", - "verificationMethod": "did:example:456#key1" + "proofValue": "u2V0AhVhAh1oLoiuV2AwmSa2ZspbmrG2gCDbpZW.......", } } ``` +##### Deriving the Document +Provide the attributes to reveal to the `derive` method. + +```ts +const derivedDocument = await builder.derive(['/credentialSubject/givenName']); +console.log(derivedDocument); +``` + ##### Verify the Document -To verify the signature of the signed document: +To verify the signature of the signed document, ensure the document is derived first and then call the `verify` method. ```ts const isVerified = await builder.verify(); diff --git a/package-lock.json b/package-lock.json index 7a3548a..21368ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,10 +16,11 @@ "@tradetrust-tt/tradetrust": "^6.10.2", "@tradetrust-tt/tradetrust-utils": "^2.4.2", "@tradetrust-tt/tt-verify": "^9.5.1", - "@trustvc/w3c-context": "^1.2.13", - "@trustvc/w3c-credential-status": "^1.2.13", - "@trustvc/w3c-issuer": "^1.2.4", - "@trustvc/w3c-vc": "^1.2.17", + "@trustvc/w3c": "^1.3.0-alpha.7", + "@trustvc/w3c-context": "^1.3.0-alpha.7", + "@trustvc/w3c-credential-status": "^1.3.0-alpha.7", + "@trustvc/w3c-issuer": "^1.3.0-alpha.5", + "@trustvc/w3c-vc": "^1.3.0-alpha.7", "ethers": "^5.8.0", "ethersV6": "npm:ethers@^6.14.4", "js-sha3": "^0.9.3", @@ -1135,6 +1136,228 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@digitalbazaar/bbs-signatures": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@digitalbazaar/bbs-signatures/-/bbs-signatures-3.0.0.tgz", + "integrity": "sha512-mQMCMnCWAraVSswJg1kJK/qmUrb3jMoWB9c8kOmztsWfnMZJcyYAcavuF8jgrVZ5cl/ZRNMK61ZbIvkqd6BE6g==", + "dependencies": { + "@noble/curves": "^1.3.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@digitalbazaar/bbs-signatures/node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@digitalbazaar/bls12-381-multikey": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@digitalbazaar/bls12-381-multikey/-/bls12-381-multikey-2.1.0.tgz", + "integrity": "sha512-JelU85fNhvHl2/mqRdmrtrE2ZQJ0//+UwI0l/YFmvsOr6YN2GuKPzdkfXjpm7f3UvnBqz5f8QKFTb9mVa7mVVg==", + "dependencies": { + "@digitalbazaar/bbs-signatures": "^3.0.0", + "@noble/curves": "^1.3.0", + "base58-universal": "^2.0.0", + "base64url-universal": "^2.0.0", + "cborg": "^4.2.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@digitalbazaar/bls12-381-multikey/node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@digitalbazaar/data-integrity": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@digitalbazaar/data-integrity/-/data-integrity-2.5.0.tgz", + "integrity": "sha512-ohIieLfgtPQU9BYfj0eKNiz55/ZDOk5YSE9FN/Hn0eXzI8WQzLkzRvC8pvBnzuzXDgCsjPdSqYvzok5PoClMBQ==", + "dependencies": { + "base58-universal": "^2.0.0", + "base64url-universal": "^2.0.0", + "jsonld-signatures": "^11.3.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@digitalbazaar/data-integrity/node_modules/jsonld": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/jsonld/-/jsonld-8.3.3.tgz", + "integrity": "sha512-9YcilrF+dLfg9NTEof/mJLMtbdX1RJ8dbWtJgE00cMOIohb1lIyJl710vFiTaiHTl6ZYODJuBd32xFvUhmv3kg==", + "dependencies": { + "@digitalbazaar/http-client": "^3.4.1", + "canonicalize": "^1.0.1", + "lru-cache": "^6.0.0", + "rdf-canonize": "^3.4.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@digitalbazaar/data-integrity/node_modules/jsonld-signatures": { + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/jsonld-signatures/-/jsonld-signatures-11.5.0.tgz", + "integrity": "sha512-Kdto+e8uvY/5u3HYkmAbpy52bplWX9uqS8fmqdCv6oxnCFwCTM0hMt6r4rWqlhw5/aHoCHJIRxwYb4QKGC69Jw==", + "dependencies": { + "@digitalbazaar/security-context": "^1.0.0", + "jsonld": "^8.0.0", + "rdf-canonize": "^4.0.1", + "serialize-error": "^8.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@digitalbazaar/data-integrity/node_modules/jsonld-signatures/node_modules/rdf-canonize": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/rdf-canonize/-/rdf-canonize-4.0.1.tgz", + "integrity": "sha512-B5ynHt4sasbUafzrvYI2GFARgeFcD8Sx9yXPbg7gEyT2EH76rlCv84kyO6tnxzVbxUN/uJDbK1S/MXh+DsnuTA==", + "dependencies": { + "setimmediate": "^1.0.5" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@digitalbazaar/data-integrity/node_modules/serialize-error": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz", + "integrity": "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@digitalbazaar/data-integrity/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@digitalbazaar/di-sd-primitives": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@digitalbazaar/di-sd-primitives/-/di-sd-primitives-3.1.0.tgz", + "integrity": "sha512-qi8H5yz4R6UmNWC6t99MSxFexniWZHME39Q77ev03afkgLMRMQSVwNGybrgeIK1VOKn0uF4YQj8quhkNlg5baQ==", + "dependencies": { + "base64url-universal": "^2.0.0", + "jsonld": "^8.3.2", + "klona": "^2.0.6", + "rdf-canonize": "^4.0.1", + "uuid": "^10.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@digitalbazaar/di-sd-primitives/node_modules/jsonld": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/jsonld/-/jsonld-8.3.3.tgz", + "integrity": "sha512-9YcilrF+dLfg9NTEof/mJLMtbdX1RJ8dbWtJgE00cMOIohb1lIyJl710vFiTaiHTl6ZYODJuBd32xFvUhmv3kg==", + "dependencies": { + "@digitalbazaar/http-client": "^3.4.1", + "canonicalize": "^1.0.1", + "lru-cache": "^6.0.0", + "rdf-canonize": "^3.4.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@digitalbazaar/di-sd-primitives/node_modules/jsonld/node_modules/rdf-canonize": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/rdf-canonize/-/rdf-canonize-3.4.0.tgz", + "integrity": "sha512-fUeWjrkOO0t1rg7B2fdyDTvngj+9RlUyL92vOdiB7c0FPguWVsniIMjEtHH+meLBO9rzkUlUzBVXgWrjI8P9LA==", + "dependencies": { + "setimmediate": "^1.0.5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitalbazaar/di-sd-primitives/node_modules/rdf-canonize": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/rdf-canonize/-/rdf-canonize-4.0.1.tgz", + "integrity": "sha512-B5ynHt4sasbUafzrvYI2GFARgeFcD8Sx9yXPbg7gEyT2EH76rlCv84kyO6tnxzVbxUN/uJDbK1S/MXh+DsnuTA==", + "dependencies": { + "setimmediate": "^1.0.5" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@digitalbazaar/di-sd-primitives/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@digitalbazaar/ecdsa-multikey": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@digitalbazaar/ecdsa-multikey/-/ecdsa-multikey-1.8.0.tgz", + "integrity": "sha512-Xo4oBCb0bJv6PYNBrLBZfR/jA2uNd9Bi+YTnadFtxTbhMQrSN0nTw3OnTBOOC7zxtL3t4N00ZweQM4zhsV6gVQ==", + "dependencies": { + "base58-universal": "^2.0.0", + "base64url-universal": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@digitalbazaar/ecdsa-sd-2023-cryptosuite": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@digitalbazaar/ecdsa-sd-2023-cryptosuite/-/ecdsa-sd-2023-cryptosuite-3.4.1.tgz", + "integrity": "sha512-PzKQneakxUUS/kzDgUZ0ZcZKuHhMhAelW2Bp/rryHEV43VeI3meoJkuQ0s7sfMlD1OWkKWrNClMr1znwBaQiLQ==", + "dependencies": { + "@digitalbazaar/di-sd-primitives": "^3.0.4", + "@digitalbazaar/ecdsa-multikey": "^1.1.3", + "base58-universal": "^2.0.0", + "base64url-universal": "^2.0.0", + "cborg": "^4.0.5", + "klona": "^2.0.6" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@digitalbazaar/http-client": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/@digitalbazaar/http-client/-/http-client-3.4.1.tgz", @@ -1148,6 +1371,11 @@ "node": ">=14.0" } }, + "node_modules/@digitalbazaar/security-context": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@digitalbazaar/security-context/-/security-context-1.0.1.tgz", + "integrity": "sha512-0WZa6tPiTZZF8leBtQgYAfXQePFQp2z5ivpCEN/iZguYYZ0TB9qRmWtan5XH6mNFuusHtMcyIzAcReyE6rZPhA==" + }, "node_modules/@es-joy/jsdoccomment": { "version": "0.50.2", "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.50.2.tgz", @@ -5761,6 +5989,84 @@ "node": ">=18.x" } }, + "node_modules/@tradetrust-tt/tradetrust/node_modules/@trustvc/w3c-context": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@trustvc/w3c-context/-/w3c-context-1.2.13.tgz", + "integrity": "sha512-Qii0ExOuTZhBirmzPhdX0o0R1P8DOc8UYEmU+XQ5ouOmQNOqyMkikS0IvFXTb7LmUD3I2d8pT6BMKyY2S8Ac/A==", + "dependencies": { + "did-resolver": "^4.1.0", + "jsonld-signatures": "^7.0.0" + }, + "engines": { + "node": ">=18.x" + } + }, + "node_modules/@tradetrust-tt/tradetrust/node_modules/@trustvc/w3c-credential-status": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@trustvc/w3c-credential-status/-/w3c-credential-status-1.2.13.tgz", + "integrity": "sha512-GNT+r00pscDdjSnTEbH3XQgidKKPblurZnWtCu8OydDilXkMH4p7Lc7JrvvCoIn6W2Iwv0em6107gB7rsW3yXA==", + "dependencies": { + "@trustvc/w3c-context": "^1.2.13", + "@trustvc/w3c-issuer": "^1.2.4", + "base64url-universal": "^2.0.0", + "pako": "^2.1.0" + }, + "engines": { + "node": ">=18.x" + } + }, + "node_modules/@tradetrust-tt/tradetrust/node_modules/@trustvc/w3c-issuer": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@trustvc/w3c-issuer/-/w3c-issuer-1.2.4.tgz", + "integrity": "sha512-BRxdb+VxKVj3N/ukivpPF0oP1dvlMOSZjoOLF+F4jMsJVcs2+8QIJbvCQJNiB6I+ji6On4uvvRPOmc8tYhOt+w==", + "dependencies": { + "@mattrglobal/bls12381-key-pair": "^1.2.1", + "bip39": "^3.1.0", + "did-resolver": "^4.1.0", + "multiformats": "^9.9.0", + "web-did-resolver": "^2.0.27" + }, + "engines": { + "node": ">=18.x" + } + }, + "node_modules/@tradetrust-tt/tradetrust/node_modules/@trustvc/w3c-vc": { + "version": "1.2.17", + "resolved": "https://registry.npmjs.org/@trustvc/w3c-vc/-/w3c-vc-1.2.17.tgz", + "integrity": "sha512-R7dz16D5mmb7tsPt71o/XAqaKaNA0J0du0ZOa16i7x3I9yPCx11jwCYdb0NYzsCXrWa318mqS2u7MCtzvEVBBQ==", + "dependencies": { + "@mattrglobal/jsonld-signatures-bbs": "^1.2.0", + "@trustvc/w3c-credential-status": "^1.2.13", + "@trustvc/w3c-issuer": "^1.2.4", + "did-resolver": "^4.1.0", + "jsonld": "^6.0.0", + "jsonld-signatures": "7.0.0", + "uuid": "^10.0.0" + }, + "engines": { + "node": ">=18.x" + }, + "peerDependencies": { + "jsonld": "^6.0.0" + } + }, + "node_modules/@tradetrust-tt/tradetrust/node_modules/@trustvc/w3c-vc/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@tradetrust-tt/tradetrust/node_modules/did-resolver": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/did-resolver/-/did-resolver-4.1.0.tgz", + "integrity": "sha512-S6fWHvCXkZg2IhS4RcVHxwuyVejPR7c+a4Go0xbQ9ps5kILa8viiYQgrM4gfTyeTjJ0ekgJH9gk/BawTpmkbZA==" + }, "node_modules/@tradetrust-tt/tt-verify": { "version": "9.5.1", "resolved": "https://registry.npmjs.org/@tradetrust-tt/tt-verify/-/tt-verify-9.5.1.tgz", @@ -5786,13 +6092,27 @@ "ethers": "^5.7.2" } }, + "node_modules/@trustvc/w3c": { + "version": "1.3.0-alpha.7", + "resolved": "https://registry.npmjs.org/@trustvc/w3c/-/w3c-1.3.0-alpha.7.tgz", + "integrity": "sha512-Y/CHQtqMSi//eXKAhsZUOcAQ03jt91LTiG4B8spdQNEk/9YxlwmHOPC9z01IGIfSFES+K81YY/SkU4m/f7UAow==", + "dependencies": { + "@trustvc/w3c-context": "^1.3.0-alpha.7", + "@trustvc/w3c-credential-status": "^1.3.0-alpha.7", + "@trustvc/w3c-issuer": "^1.3.0-alpha.5", + "@trustvc/w3c-vc": "^1.3.0-alpha.7" + }, + "engines": { + "node": ">=18.x" + } + }, "node_modules/@trustvc/w3c-context": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/@trustvc/w3c-context/-/w3c-context-1.2.13.tgz", - "integrity": "sha512-Qii0ExOuTZhBirmzPhdX0o0R1P8DOc8UYEmU+XQ5ouOmQNOqyMkikS0IvFXTb7LmUD3I2d8pT6BMKyY2S8Ac/A==", + "version": "1.3.0-alpha.7", + "resolved": "https://registry.npmjs.org/@trustvc/w3c-context/-/w3c-context-1.3.0-alpha.7.tgz", + "integrity": "sha512-ugBwyxvKjnhim0veLlxeKUqUpo33b9iNtv1iD5DBYps1qGHeA/zNRgEzA3HfInwSQ9hLGVcjIlOdGWXOf1qrNg==", "dependencies": { "did-resolver": "^4.1.0", - "jsonld-signatures": "^7.0.0" + "jsonld-signatures": "^11.5.0" }, "engines": { "node": ">=18.x" @@ -5803,13 +6123,77 @@ "resolved": "https://registry.npmjs.org/did-resolver/-/did-resolver-4.1.0.tgz", "integrity": "sha512-S6fWHvCXkZg2IhS4RcVHxwuyVejPR7c+a4Go0xbQ9ps5kILa8viiYQgrM4gfTyeTjJ0ekgJH9gk/BawTpmkbZA==" }, + "node_modules/@trustvc/w3c-context/node_modules/jsonld": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/jsonld/-/jsonld-8.3.3.tgz", + "integrity": "sha512-9YcilrF+dLfg9NTEof/mJLMtbdX1RJ8dbWtJgE00cMOIohb1lIyJl710vFiTaiHTl6ZYODJuBd32xFvUhmv3kg==", + "dependencies": { + "@digitalbazaar/http-client": "^3.4.1", + "canonicalize": "^1.0.1", + "lru-cache": "^6.0.0", + "rdf-canonize": "^3.4.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@trustvc/w3c-context/node_modules/jsonld-signatures": { + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/jsonld-signatures/-/jsonld-signatures-11.5.0.tgz", + "integrity": "sha512-Kdto+e8uvY/5u3HYkmAbpy52bplWX9uqS8fmqdCv6oxnCFwCTM0hMt6r4rWqlhw5/aHoCHJIRxwYb4QKGC69Jw==", + "dependencies": { + "@digitalbazaar/security-context": "^1.0.0", + "jsonld": "^8.0.0", + "rdf-canonize": "^4.0.1", + "serialize-error": "^8.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@trustvc/w3c-context/node_modules/jsonld-signatures/node_modules/rdf-canonize": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/rdf-canonize/-/rdf-canonize-4.0.1.tgz", + "integrity": "sha512-B5ynHt4sasbUafzrvYI2GFARgeFcD8Sx9yXPbg7gEyT2EH76rlCv84kyO6tnxzVbxUN/uJDbK1S/MXh+DsnuTA==", + "dependencies": { + "setimmediate": "^1.0.5" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@trustvc/w3c-context/node_modules/serialize-error": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz", + "integrity": "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@trustvc/w3c-context/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@trustvc/w3c-credential-status": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/@trustvc/w3c-credential-status/-/w3c-credential-status-1.2.13.tgz", - "integrity": "sha512-GNT+r00pscDdjSnTEbH3XQgidKKPblurZnWtCu8OydDilXkMH4p7Lc7JrvvCoIn6W2Iwv0em6107gB7rsW3yXA==", + "version": "1.3.0-alpha.7", + "resolved": "https://registry.npmjs.org/@trustvc/w3c-credential-status/-/w3c-credential-status-1.3.0-alpha.7.tgz", + "integrity": "sha512-60yTwW6YmodutbW2eNnlBI+lyJuSNiobsx6E/+g+VWj0oMPHAOBH01nVvHwYw2sT8GGSqC/fOGQaPpdSspVxbw==", "dependencies": { - "@trustvc/w3c-context": "^1.2.13", - "@trustvc/w3c-issuer": "^1.2.4", + "@trustvc/w3c-context": "^1.3.0-alpha.7", + "@trustvc/w3c-issuer": "^1.3.0-alpha.5", "base64url-universal": "^2.0.0", "pako": "^2.1.0" }, @@ -5818,10 +6202,12 @@ } }, "node_modules/@trustvc/w3c-issuer": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@trustvc/w3c-issuer/-/w3c-issuer-1.2.4.tgz", - "integrity": "sha512-BRxdb+VxKVj3N/ukivpPF0oP1dvlMOSZjoOLF+F4jMsJVcs2+8QIJbvCQJNiB6I+ji6On4uvvRPOmc8tYhOt+w==", + "version": "1.3.0-alpha.5", + "resolved": "https://registry.npmjs.org/@trustvc/w3c-issuer/-/w3c-issuer-1.3.0-alpha.5.tgz", + "integrity": "sha512-dNwF4pN3qTvu+2fPoB4vBt+C+wNhhoVfFnDheEzCY+nGVW5ZFx2fUUp8u2R5yEQUh593TTgOuBwrz/541340HA==", "dependencies": { + "@digitalbazaar/bls12-381-multikey": "^2.1.0", + "@digitalbazaar/ecdsa-multikey": "^1.8.0", "@mattrglobal/bls12381-key-pair": "^1.2.1", "bip39": "^3.1.0", "did-resolver": "^4.1.0", @@ -5838,16 +6224,21 @@ "integrity": "sha512-S6fWHvCXkZg2IhS4RcVHxwuyVejPR7c+a4Go0xbQ9ps5kILa8viiYQgrM4gfTyeTjJ0ekgJH9gk/BawTpmkbZA==" }, "node_modules/@trustvc/w3c-vc": { - "version": "1.2.17", - "resolved": "https://registry.npmjs.org/@trustvc/w3c-vc/-/w3c-vc-1.2.17.tgz", - "integrity": "sha512-R7dz16D5mmb7tsPt71o/XAqaKaNA0J0du0ZOa16i7x3I9yPCx11jwCYdb0NYzsCXrWa318mqS2u7MCtzvEVBBQ==", + "version": "1.3.0-alpha.7", + "resolved": "https://registry.npmjs.org/@trustvc/w3c-vc/-/w3c-vc-1.3.0-alpha.7.tgz", + "integrity": "sha512-Dv+OyoW69v7stibl9A3MbbMUYjjqK0Ia23RBy4+ugkBHdLQbWcJUv8zEnTSialCssXH3pQK457TLbRZ7FxormA==", "dependencies": { + "@digitalbazaar/data-integrity": "^2.5.0", + "@digitalbazaar/ecdsa-multikey": "^1.8.0", + "@digitalbazaar/ecdsa-sd-2023-cryptosuite": "^3.4.1", "@mattrglobal/jsonld-signatures-bbs": "^1.2.0", - "@trustvc/w3c-credential-status": "^1.2.13", - "@trustvc/w3c-issuer": "^1.2.4", + "@trustvc/w3c-credential-status": "^1.3.0-alpha.7", + "@trustvc/w3c-issuer": "^1.3.0-alpha.5", + "cbor": "^9.0.2", "did-resolver": "^4.1.0", "jsonld": "^6.0.0", - "jsonld-signatures": "7.0.0", + "jsonld-signatures": "^11.5.0", + "jsonld-signatures-v7": "npm:jsonld-signatures@7.0.0", "uuid": "^10.0.0" }, "engines": { @@ -5862,6 +6253,81 @@ "resolved": "https://registry.npmjs.org/did-resolver/-/did-resolver-4.1.0.tgz", "integrity": "sha512-S6fWHvCXkZg2IhS4RcVHxwuyVejPR7c+a4Go0xbQ9ps5kILa8viiYQgrM4gfTyeTjJ0ekgJH9gk/BawTpmkbZA==" }, + "node_modules/@trustvc/w3c-vc/node_modules/jsonld-signatures": { + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/jsonld-signatures/-/jsonld-signatures-11.5.0.tgz", + "integrity": "sha512-Kdto+e8uvY/5u3HYkmAbpy52bplWX9uqS8fmqdCv6oxnCFwCTM0hMt6r4rWqlhw5/aHoCHJIRxwYb4QKGC69Jw==", + "dependencies": { + "@digitalbazaar/security-context": "^1.0.0", + "jsonld": "^8.0.0", + "rdf-canonize": "^4.0.1", + "serialize-error": "^8.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@trustvc/w3c-vc/node_modules/jsonld-signatures/node_modules/jsonld": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/jsonld/-/jsonld-8.3.3.tgz", + "integrity": "sha512-9YcilrF+dLfg9NTEof/mJLMtbdX1RJ8dbWtJgE00cMOIohb1lIyJl710vFiTaiHTl6ZYODJuBd32xFvUhmv3kg==", + "dependencies": { + "@digitalbazaar/http-client": "^3.4.1", + "canonicalize": "^1.0.1", + "lru-cache": "^6.0.0", + "rdf-canonize": "^3.4.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@trustvc/w3c-vc/node_modules/jsonld-signatures/node_modules/jsonld/node_modules/rdf-canonize": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/rdf-canonize/-/rdf-canonize-3.4.0.tgz", + "integrity": "sha512-fUeWjrkOO0t1rg7B2fdyDTvngj+9RlUyL92vOdiB7c0FPguWVsniIMjEtHH+meLBO9rzkUlUzBVXgWrjI8P9LA==", + "dependencies": { + "setimmediate": "^1.0.5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@trustvc/w3c-vc/node_modules/jsonld-signatures/node_modules/rdf-canonize": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/rdf-canonize/-/rdf-canonize-4.0.1.tgz", + "integrity": "sha512-B5ynHt4sasbUafzrvYI2GFARgeFcD8Sx9yXPbg7gEyT2EH76rlCv84kyO6tnxzVbxUN/uJDbK1S/MXh+DsnuTA==", + "dependencies": { + "setimmediate": "^1.0.5" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@trustvc/w3c-vc/node_modules/serialize-error": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz", + "integrity": "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@trustvc/w3c-vc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@trustvc/w3c-vc/node_modules/uuid": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", @@ -6936,6 +7402,14 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/base58-universal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base58-universal/-/base58-universal-2.0.0.tgz", + "integrity": "sha512-BgkgF8zVLOAygszG4W8NkLm7iXrw80VYAOcedrzANrIhS14+4W6zVqjyGTFUBM/FpqkHUt8aAYd4DbBBfn3zKg==", + "engines": { + "node": ">=14" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -7346,6 +7820,25 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" }, + "node_modules/cbor": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-9.0.2.tgz", + "integrity": "sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==", + "dependencies": { + "nofilter": "^3.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/cborg": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/cborg/-/cborg-4.2.14.tgz", + "integrity": "sha512-VwDKj4eWvoOBtMReabfiGyMh/bNFk8aXZRlMis1tTuMjN9R6VQdyrOwyQb0/aqYHk7r88a6xTWvnsJEC2Cw66Q==", + "bin": { + "cborg": "lib/bin.js" + } + }, "node_modules/chai": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", @@ -12509,6 +13002,72 @@ "node": ">=10" } }, + "node_modules/jsonld-signatures-v7": { + "name": "jsonld-signatures", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/jsonld-signatures/-/jsonld-signatures-7.0.0.tgz", + "integrity": "sha512-J/nA+llcYYjErPHG9WFpXvR82TOg5fbHk/7rXbx4Ts854DPReaKAAd0hAZ+s5/P2WIIAZPIHCqA+iz1QrOqeiQ==", + "dependencies": { + "base64url": "^3.0.1", + "crypto-ld": "^3.7.0", + "jsonld": "^4.0.1", + "node-forge": "^0.10.0", + "security-context": "^4.0.0", + "serialize-error": "^5.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jsonld-signatures-v7/node_modules/jsonld": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jsonld/-/jsonld-4.0.1.tgz", + "integrity": "sha512-ltEqMQB37ZxZnsgmI+9rqHYkz1M6PqUykuS1t2aQNuH1oiLrUDYz5nyVkHQDgjFd7CFKTIWeLiNhwdwFrH5o5A==", + "dependencies": { + "canonicalize": "^1.0.1", + "lru-cache": "^5.1.1", + "object.fromentries": "^2.0.2", + "rdf-canonize": "^2.0.1", + "request": "^2.88.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonld-signatures-v7/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/jsonld-signatures-v7/node_modules/rdf-canonize": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/rdf-canonize/-/rdf-canonize-2.0.1.tgz", + "integrity": "sha512-/GVELjrfW8G/wS4QfDZ5Kq68cS1belVNJqZlcwiErerexeBUsgOINCROnP7UumWIBNdeCwTVLE9NVXMnRYK0lA==", + "dependencies": { + "semver": "^6.3.0", + "setimmediate": "^1.0.5" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonld-signatures-v7/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/jsonld-signatures-v7/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, "node_modules/jsonld-signatures/node_modules/jsonld": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/jsonld/-/jsonld-4.0.1.tgz", @@ -12642,6 +13201,14 @@ "node": ">=0.10.0" } }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "engines": { + "node": ">= 8" + } + }, "node_modules/kolorist": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", @@ -14470,6 +15037,14 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/nofilter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", + "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", + "engines": { + "node": ">=12.19" + } + }, "node_modules/nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", diff --git a/package.json b/package.json index 623a64b..5e2fd2b 100644 --- a/package.json +++ b/package.json @@ -121,10 +121,11 @@ "@tradetrust-tt/tradetrust": "^6.10.2", "@tradetrust-tt/tradetrust-utils": "^2.4.2", "@tradetrust-tt/tt-verify": "^9.5.1", - "@trustvc/w3c-context": "^1.2.13", - "@trustvc/w3c-credential-status": "^1.2.13", - "@trustvc/w3c-issuer": "^1.2.4", - "@trustvc/w3c-vc": "^1.2.17", + "@trustvc/w3c": "^1.3.0-alpha.7", + "@trustvc/w3c-context": "^1.3.0-alpha.7", + "@trustvc/w3c-credential-status": "^1.3.0-alpha.7", + "@trustvc/w3c-issuer": "^1.3.0-alpha.5", + "@trustvc/w3c-vc": "^1.3.0-alpha.7", "ethers": "^5.8.0", "ethersV6": "npm:ethers@^6.14.4", "js-sha3": "^0.9.3", diff --git a/src/__tests__/core/documentBuilder.test.ts b/src/__tests__/core/documentBuilder.test.ts index 9956020..05ff022 100644 --- a/src/__tests__/core/documentBuilder.test.ts +++ b/src/__tests__/core/documentBuilder.test.ts @@ -1,8 +1,9 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { DocumentBuilder } from '../../core/documentBuilder'; import { PrivateKeyPair, VerificationType } from '@trustvc/w3c-issuer'; +import { ContextDocument } from '@trustvc/w3c-context'; -const testPrivateKey: PrivateKeyPair = { +const BBS2020testPrivateKey: PrivateKeyPair = { id: 'did:web:trustvc.github.io:did:1#keys-1', controller: 'did:web:trustvc.github.io:did:1', type: VerificationType.Bls12381G2Key2020, @@ -11,7 +12,16 @@ const testPrivateKey: PrivateKeyPair = { privateKeyBase58: '4LDU56PUhA9ZEutnR1qCWQnUhtLtpLu2EHSq4h1o7vtF', }; -describe('DocumentBuilder', () => { +const ECDSAtestPrivateKey: PrivateKeyPair = { + '@context': 'https://w3id.org/security/multikey/v1', + id: 'did:web:trustvc.github.io:did:1#multikey-1', + type: VerificationType.Multikey, + controller: 'did:web:trustvc.github.io:did:1', + publicKeyMultibase: 'zDnaemDNwi4G5eTzGfRooFFu5Kns3be6yfyVNtiaMhWkZbwtc', + secretKeyMultibase: 'z42tmUXTVn3n9BihE6NhdMpvVBTnFTgmb6fw18o5Ud6puhRW', +}; + +describe('DocumentBuilder data model 2.0 using ECDSA', () => { let documentBuilder: DocumentBuilder; beforeEach(() => { @@ -20,9 +30,12 @@ describe('DocumentBuilder', () => { describe('Initialization', () => { it('should initialize with default context and type', async () => { - const signedDocument = await documentBuilder.sign(testPrivateKey); + const signedDocument = await documentBuilder.sign(ECDSAtestPrivateKey, 'ecdsa-sd-2023'); expect(signedDocument).toMatchObject({ - '@context': expect.arrayContaining(['https://www.w3.org/2018/credentials/v1']), + '@context': expect.arrayContaining([ + 'https://www.w3.org/ns/credentials/v2', + 'https://w3id.org/security/data-integrity/v2', + ]), type: expect.arrayContaining(['VerifiableCredential']), }); }); @@ -32,7 +45,7 @@ describe('DocumentBuilder', () => { it('should return the current state of the document as a JSON string', () => { const expectedJson = JSON.stringify( { - '@context': ['https://www.w3.org/2018/credentials/v1'], + '@context': ['https://www.w3.org/ns/credentials/v2'], type: ['VerifiableCredential'], credentialSubject: {}, }, @@ -46,13 +59,13 @@ describe('DocumentBuilder', () => { describe('Validation Errors', () => { it('should throw an error when required fields are missing', async () => { - await expect(new DocumentBuilder({}).sign(testPrivateKey)).rejects.toThrow( + await expect(new DocumentBuilder({}).sign(ECDSAtestPrivateKey)).rejects.toThrow( 'Validation Error: Missing required field "credentialSubject" in the credential.', ); }); it('should throw an error when document is already signed', async () => { - await documentBuilder.sign(testPrivateKey); + await documentBuilder.sign(ECDSAtestPrivateKey); expect(() => documentBuilder.credentialStatus({ chain: 'amoy', @@ -107,39 +120,57 @@ describe('DocumentBuilder', () => { }); }); - describe('Signing and Verification', () => { - it('should sign and verify the document successfully for transferableRecords', async () => { + describe('Sign, Derive and Verify', () => { + it('should sign, derive and verify the document successfully for transferableRecords', async () => { documentBuilder.credentialStatus({ chain: 'amoy', chainId: 80002, tokenRegistry: '0x71D28767662cB233F887aD2Bb65d048d760bA694', rpcProviderUrl: 'https://rpc-amoy.polygon.technology', }); - const signedDocument = await documentBuilder.sign(testPrivateKey); + const signedDocument = await documentBuilder.sign(ECDSAtestPrivateKey); expect(signedDocument).toBeDefined(); + const derivedDocument = await documentBuilder.derive([]); + expect(derivedDocument).toBeDefined(); const verificationResult = await documentBuilder.verify(); expect(verificationResult).toBe(true); - }); + }, 10000); - it('should sign and verify the document successfully for verifiableDocument', async () => { + it('should sign, derive and verify the document successfully for verifiableDocument', async () => { documentBuilder.credentialStatus({ url: 'https://trustvc.github.io/did/credentials/statuslist/1', index: 10, // Not revoked }); - const signedDocument = await documentBuilder.sign(testPrivateKey); + const signedDocument = await documentBuilder.sign(ECDSAtestPrivateKey); expect(signedDocument).toBeDefined(); + const derivedDocument = await documentBuilder.derive([]); + expect(derivedDocument).toBeDefined(); const verificationResult = await documentBuilder.verify(); expect(verificationResult).toBe(true); + }, 10000); + }); + + describe('Error Handling', () => { + it('Should throw error if document builder initialized with data model v1.1 context', () => { + expect( + () => new DocumentBuilder({ '@context': ['https://www.w3.org/2018/credentials/v1'] }), + ).toThrow('Document builder does not support data model v1.1.'); }); - it('should not verify the document if it is not signed yet', async () => { + it('Should throw an error when trying to verify without deriving', async () => { + documentBuilder.credentialStatus({ + chain: 'amoy', + chainId: 80002, + tokenRegistry: '0x71D28767662cB233F887aD2Bb65d048d760bA694', + rpcProviderUrl: 'https://rpc-amoy.polygon.technology', + }); + const signedDocument = await documentBuilder.sign(ECDSAtestPrivateKey); + expect(signedDocument).toBeDefined(); await expect(documentBuilder.verify()).rejects.toThrow( - 'Verification Error: Document is not signed yet.', + 'Verification Error: Document is not derived yet. Use derive() first.', ); }); - }); - describe('Error Handling in Signing', () => { it('should throw an error for an unsupported chain ID', async () => { documentBuilder.credentialStatus({ chain: 'unknown-chain', @@ -147,7 +178,7 @@ describe('DocumentBuilder', () => { tokenRegistry: '0x71D28767662cB233F887aD2Bb65d048d760bA694', rpcProviderUrl: 'https://rpc-amoy.polygon.technology', }); - await expect(documentBuilder.sign(testPrivateKey)).rejects.toThrow( + await expect(documentBuilder.sign(ECDSAtestPrivateKey)).rejects.toThrow( 'Unsupported Chain: Chain ID 999999 is not supported.', ); }); @@ -159,7 +190,7 @@ describe('DocumentBuilder', () => { tokenRegistry: '0x71D28767662cB233F887aD2Bb65d048d760bA694', rpcProviderUrl: 'https://invalid-rpc-url', // Invalid RPC URL }); - await expect(documentBuilder.sign(testPrivateKey)).rejects.toThrow( + await expect(documentBuilder.sign(ECDSAtestPrivateKey)).rejects.toThrow( 'Network Error: Unable to verify token registry. Please check the RPC URL or token registry address.', ); }, 45_000); @@ -169,32 +200,32 @@ describe('DocumentBuilder', () => { url: 'https://trustvc.github.io/did/credentials/statuslist/1', index: 5, // Revoked }); - await expect(documentBuilder.sign(testPrivateKey)).rejects.toThrow( + await expect(documentBuilder.sign(ECDSAtestPrivateKey)).rejects.toThrow( 'Credential Verification Failed: Invalid credential status detected.', ); }); it('should throw an error when signing a document with an invalid credential status', async () => { documentBuilder.credentialStatus({ - url: 'https://trustvc.github.io/did/credentials/statuslist/2', // Invalid URL + url: 'https://trustvc.github.io/did/credentials/statuslist/3', // Invalid URL index: 10, }); - await expect(documentBuilder.sign(testPrivateKey)).rejects.toThrowError( + await expect(documentBuilder.sign(ECDSAtestPrivateKey)).rejects.toThrowError( /Credential Verification Failed:/, ); }); }); - describe('expirationDate', () => { - it('should set expiration date when given a string', () => { + describe('validUntil', () => { + it('should set valid until (expiration) date when given a string', () => { documentBuilder.expirationDate('2025-12-31T23:59:59Z'); - expect(documentBuilder['document'].expirationDate).toBe('2025-12-31T23:59:59Z'); + expect(documentBuilder['document'].validUntil).toBe('2025-12-31T23:59:59Z'); }); - it('should set expiration date when given a Date object', () => { + it('should set valid until (expiration) date when given a Date object', () => { const date = new Date('2025-12-31T23:59:59Z'); documentBuilder.expirationDate(date); - expect(documentBuilder['document'].expirationDate).toBe(date.toISOString()); + expect(documentBuilder['document'].validUntil).toBe(date.toISOString()); }); }); @@ -212,6 +243,9 @@ describe('DocumentBuilder', () => { templateName: 'BILL_OF_LADING', }); expect(documentBuilder['document'].renderMethod).toEqual([method]); + expect(documentBuilder['document']['@context']).toContain( + 'https://trustvc.io/context/render-method-context-v2.json', + ); }); }); @@ -227,6 +261,116 @@ describe('DocumentBuilder', () => { type: 'TrustVCQRCode', }); expect(documentBuilder['document'].qrCode).toEqual(method); + expect(documentBuilder['document']['@context']).toContain( + 'https://trustvc.io/context/qrcode-context.json', + ); + }); + }); +}); + +describe('DocumentBuilder Data model 2.0 using BBS2020', () => { + let documentBuilder: DocumentBuilder; + + beforeEach(() => { + documentBuilder = new DocumentBuilder({}).credentialSubject({}); + }); + + describe('Initialization', () => { + it('should initialize with default context and type', async () => { + const signedDocument = await documentBuilder.sign( + BBS2020testPrivateKey, + 'BbsBlsSignature2020', + ); + expect(signedDocument).toMatchObject({ + '@context': expect.arrayContaining([ + 'https://www.w3.org/ns/credentials/v2', + 'https://w3id.org/security/bbs/v1', + ]), + type: expect.arrayContaining(['VerifiableCredential']), + }); + }); + }); + + describe('Validation Errors', () => { + it('should throw an error when required fields are missing', async () => { + await expect( + new DocumentBuilder({}).sign(BBS2020testPrivateKey, 'BbsBlsSignature2020'), + ).rejects.toThrow( + 'Validation Error: Missing required field "credentialSubject" in the credential.', + ); + }); + + it('should throw an error when document is already signed', async () => { + await documentBuilder.sign(BBS2020testPrivateKey, 'BbsBlsSignature2020'); + expect(() => + documentBuilder.credentialStatus({ + chain: 'amoy', + chainId: 80002, + tokenRegistry: '0x71D28767662cB233F887aD2Bb65d048d760bA694', + rpcProviderUrl: 'https://rpc-amoy.polygon.technology', + }), + ).toThrow('Configuration Error: Document is already signed.'); + }); + }); + + describe('Signing and Verification', () => { + it('should sign and verify the document successfully for transferableRecords', async () => { + documentBuilder.credentialStatus({ + chain: 'amoy', + chainId: 80002, + tokenRegistry: '0x71D28767662cB233F887aD2Bb65d048d760bA694', + rpcProviderUrl: 'https://rpc-amoy.polygon.technology', + }); + const signedDocument = await documentBuilder.sign( + BBS2020testPrivateKey, + 'BbsBlsSignature2020', + ); + expect(signedDocument).toBeDefined(); + const verificationResult = await documentBuilder.verify(); + expect(verificationResult).toBe(true); + }); + + it('should sign and verify the document successfully for verifiableDocument', async () => { + documentBuilder.credentialStatus({ + url: 'https://trustvc.github.io/did/credentials/statuslist/1', + index: 10, // Not revoked + }); + const signedDocument = await documentBuilder.sign( + BBS2020testPrivateKey, + 'BbsBlsSignature2020', + ); + expect(signedDocument).toBeDefined(); + const verificationResult = await documentBuilder.verify(); + expect(verificationResult).toBe(true); + }); + + it('should not verify the document if it is not signed yet', async () => { + await expect(documentBuilder.verify()).rejects.toThrow( + 'Verification Error: Document is not signed yet.', + ); + }); + + it('should be able to derive document and verify derived document', async () => { + const contextDocument: ContextDocument = { + '@context': [ + 'https://www.w3.org/ns/credentials/v2', + 'https://w3id.org/security/bbs/v1', + 'https://trustvc.io/context/transferable-records-context.json', + ], + type: ['VerifiableCredential'], + }; + + documentBuilder.credentialStatus({ + chain: 'amoy', + chainId: 80002, + tokenRegistry: '0x71D28767662cB233F887aD2Bb65d048d760bA694', + rpcProviderUrl: 'https://rpc-amoy.polygon.technology', + }); + await documentBuilder.sign(BBS2020testPrivateKey, 'BbsBlsSignature2020'); + const derivedDocument = await documentBuilder.derive(contextDocument); + expect(derivedDocument).toBeDefined(); + const verificationResult = await documentBuilder.verify(); + expect(verificationResult).toBe(true); }); }); }); diff --git a/src/__tests__/core/verify.test.ts b/src/__tests__/core/verify.test.ts index cf540cf..2b86658 100644 --- a/src/__tests__/core/verify.test.ts +++ b/src/__tests__/core/verify.test.ts @@ -3,12 +3,17 @@ import { verifyDocument } from '../..'; import * as transferableRecordsUtils from '../../verify/fragments/document-status/transferableRecords/utils'; import { SIGNED_WRAPPED_DOCUMENT_DNS_DID_V3, + ECDSA_W3C_VERIFIABLE_DOCUMENT_V1_1, + ECDSA_W3C_VERIFIABLE_DOCUMENT_V2_0, + ECDSA_W3C_DERIVED_DOCUMENT_V1_1, + ECDSA_W3C_DERIVED_DOCUMENT_V2_0, W3C_TRANSFERABLE_RECORD, W3C_VERIFIABLE_DOCUMENT, WRAPPED_DOCUMENT_DID_TOKEN_REGISTRY_V3, WRAPPED_DOCUMENT_DNS_TXT_V2, } from '../fixtures/fixtures'; import { W3CCredentialStatusCode } from '../../verify/fragments/document-status/w3cCredentialStatus'; +import { openAttestationDidSignedDocumentStatus } from '@tradetrust-tt/tt-verify'; const providerUrl = 'https://rpc-amoy.polygon.technology'; @@ -23,6 +28,16 @@ describe.concurrent('W3C verify', () => { "status": "VALID", "type": "DOCUMENT_INTEGRITY", }, + { + "name": "EcdsaW3CSignatureIntegrity", + "reason": { + "code": 0, + "codeString": "SKIPPED", + "message": "Document either has no proof or proof type is not 'DataIntegrityProof' or proof cryptosuite is not 'ecdsa-sd-2023'.", + }, + "status": "SKIPPED", + "type": "DOCUMENT_INTEGRITY", + }, { "data": [ { @@ -268,49 +283,59 @@ describe.concurrent('W3C verify', () => { expect( await verifyDocument(W3C_TRANSFERABLE_RECORD as any, { rpcProviderUrl: providerUrl }), ).toMatchInlineSnapshot(` - [ - { - "data": true, - "name": "W3CSignatureIntegrity", - "status": "VALID", - "type": "DOCUMENT_INTEGRITY", - }, - { - "name": "W3CCredentialStatus", - "reason": { - "code": 0, - "codeString": "SKIPPED", - "message": "Document does not have a valid credentialStatus or type.", - }, - "status": "SKIPPED", - "type": "DOCUMENT_STATUS", + [ + { + "data": true, + "name": "W3CSignatureIntegrity", + "status": "VALID", + "type": "DOCUMENT_INTEGRITY", + }, + { + "name": "EcdsaW3CSignatureIntegrity", + "reason": { + "code": 0, + "codeString": "SKIPPED", + "message": "Document either has no proof or proof type is not 'DataIntegrityProof' or proof cryptosuite is not 'ecdsa-sd-2023'.", }, - { - "data": { - "tokenRegistry": "0x6c2a002A5833a100f38458c50F11E71Aa1A342c6", - }, - "name": "TransferableRecords", - "status": "VALID", - "type": "DOCUMENT_STATUS", + "status": "SKIPPED", + "type": "DOCUMENT_INTEGRITY", + }, + { + "name": "W3CCredentialStatus", + "reason": { + "code": 0, + "codeString": "SKIPPED", + "message": "Document does not have a valid credentialStatus or type.", }, - { - "name": "W3CEmptyCredentialStatus", - "reason": { - "code": 0, - "codeString": "SKIPPED", - "message": "Document contains a credentialStatus.", - }, - "status": "SKIPPED", - "type": "DOCUMENT_STATUS", + "status": "SKIPPED", + "type": "DOCUMENT_STATUS", + }, + { + "data": { + "tokenRegistry": "0x6c2a002A5833a100f38458c50F11E71Aa1A342c6", }, - { - "data": true, - "name": "W3CIssuerIdentity", - "status": "VALID", - "type": "ISSUER_IDENTITY", + "name": "TransferableRecords", + "status": "VALID", + "type": "DOCUMENT_STATUS", + }, + { + "name": "W3CEmptyCredentialStatus", + "reason": { + "code": 0, + "codeString": "SKIPPED", + "message": "Document contains a credentialStatus.", }, - ] - `); + "status": "SKIPPED", + "type": "DOCUMENT_STATUS", + }, + { + "data": true, + "name": "W3CIssuerIdentity", + "status": "VALID", + "type": "ISSUER_IDENTITY", + }, + ] + `); }, ); @@ -424,8 +449,190 @@ describe.concurrent('W3C verify', () => { }); }); +const ecdsaTestScenarios = [ + { + version: 'v1.1', + signedCredential: ECDSA_W3C_VERIFIABLE_DOCUMENT_V1_1, + derivedCredential: ECDSA_W3C_DERIVED_DOCUMENT_V1_1, + }, + { + version: 'v2.0', + signedCredential: ECDSA_W3C_VERIFIABLE_DOCUMENT_V2_0, + derivedCredential: ECDSA_W3C_DERIVED_DOCUMENT_V2_0, + }, +]; + +describe.each(ecdsaTestScenarios)( + 'W3C ECDSA $version Verify', + ({ version, signedCredential, derivedCredential }) => { + it(`should verify a derived ECDSA ${version} W3C document and return all valid fragments`, async ({ + expect, + }) => { + expect(await verifyDocument(derivedCredential as any)).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + data: true, + name: 'EcdsaW3CSignatureIntegrity', + reason: { + message: 'Document verified successfully', + }, + status: 'VALID', + type: 'DOCUMENT_INTEGRITY', + }), + expect.objectContaining({ + data: true, + name: 'W3CIssuerIdentity', + status: 'VALID', + type: 'ISSUER_IDENTITY', + }), + ]), + ); + }); + + it('should handle ECDSA W3C non-derived document verification', async ({ expect }) => { + const result = await verifyDocument(signedCredential as any); + + // Check that our ECDSA verifier ran + expect(result).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + data: true, + name: 'EcdsaW3CSignatureIntegrity', + reason: { + message: 'Document verified after derivation', + }, + status: 'VALID', + type: 'DOCUMENT_INTEGRITY', + }), + expect.objectContaining({ + data: true, + name: 'W3CIssuerIdentity', + status: 'VALID', + type: 'ISSUER_IDENTITY', + }), + ]), + ); + }); + + it('should return INVALID status for DOCUMENT_INTEGRITY when signature is tampered', async ({ + expect, + }) => { + const tampered: any = { ...derivedCredential, id: 'urn:uuid:tampered-id' }; + expect(await verifyDocument(tampered)).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + data: false, + name: 'EcdsaW3CSignatureIntegrity', + reason: { + message: 'Invalid signature.', + }, + status: 'INVALID', + type: 'DOCUMENT_INTEGRITY', + }), + ]), + ); + }); + + it('should skip DOCUMENT_INTEGRITY verification for unsupported cryptosuite', async ({ + expect, + }) => { + const unsupportedCryptosuite: any = { + ...derivedCredential, + proof: { + ...derivedCredential.proof, + cryptosuite: 'eddsa-2022', + }, + }; + + expect(await verifyDocument(unsupportedCryptosuite)).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: 'EcdsaW3CSignatureIntegrity', + reason: { + code: W3CCredentialStatusCode.SKIPPED, + codeString: 'SKIPPED', + message: + "Document either has no proof or proof type is not 'DataIntegrityProof' or proof cryptosuite is not 'ecdsa-sd-2023'.", + }, + status: 'SKIPPED', + type: 'DOCUMENT_INTEGRITY', + }), + ]), + ); + }); + + it('should skip DOCUMENT_INTEGRITY verification for unsupported proof type', async ({ + expect, + }) => { + const unsupportedProofType: any = { + ...derivedCredential, + proof: { + ...derivedCredential.proof, + type: 'Ed25519Signature2020', + }, + }; + + expect(await verifyDocument(unsupportedProofType)).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: 'EcdsaW3CSignatureIntegrity', + reason: { + code: W3CCredentialStatusCode.SKIPPED, + codeString: 'SKIPPED', + message: + "Document either has no proof or proof type is not 'DataIntegrityProof' or proof cryptosuite is not 'ecdsa-sd-2023'.", + }, + status: 'SKIPPED', + type: 'DOCUMENT_INTEGRITY', + }), + ]), + ); + }); + + it('should skip DOCUMENT_INTEGRITY verification when proof is missing', async ({ expect }) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { proof, ...documentWithoutProof } = derivedCredential; + + expect(await verifyDocument(documentWithoutProof as any)).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: 'EcdsaW3CSignatureIntegrity', + reason: { + code: W3CCredentialStatusCode.SKIPPED, + codeString: 'SKIPPED', + message: + "Document either has no proof or proof type is not 'DataIntegrityProof' or proof cryptosuite is not 'ecdsa-sd-2023'.", + }, + status: 'SKIPPED', + type: 'DOCUMENT_INTEGRITY', + }), + ]), + ); + }); + }, +); + describe.concurrent('V3 verify', () => { it('should verify a DND_DID document and return fragments', async ({ expect }) => { + vi.spyOn(openAttestationDidSignedDocumentStatus, 'verify').mockResolvedValue({ + data: { + details: { + issuance: { + did: 'did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90#controller', + issued: true, + }, + revocation: { + revoked: false, + }, + }, + issuedOnAll: true, + revokedOnAny: false, + }, + name: 'OpenAttestationDidSignedDocumentStatus', + status: 'VALID', + type: 'DOCUMENT_STATUS', + }); + expect(await verifyDocument(SIGNED_WRAPPED_DOCUMENT_DNS_DID_V3)).toMatchInlineSnapshot(` [ { diff --git a/src/__tests__/fixtures/fixtures.ts b/src/__tests__/fixtures/fixtures.ts index b2b2fd1..e8df128 100644 --- a/src/__tests__/fixtures/fixtures.ts +++ b/src/__tests__/fixtures/fixtures.ts @@ -1351,6 +1351,266 @@ export const W3C_TRANSFERABLE_RECORD = freezeObject({ verificationMethod: 'did:web:trustvc.github.io:did:1#keys-1', }, } as SignedVerifiableCredential); +// export const ECDSA_W3C_VERIFIABLE_DOCUMENT_V2_0 = freezeObject({ +// '@context': [ +// 'https://www.w3.org/ns/credentials/v2', +// 'https://w3id.org/security/data-integrity/v2', +// 'https://w3id.org/vc/status-list/2021/v1', +// 'https://trustvc.io/context/transferable-records-context.json', +// 'https://trustvc.io/context/attachments-context.json', +// 'https://trustvc.io/context/qrcode-context.json', +// 'https://trustvc.io/context/bill-of-lading.json', +// ], +// qrCode: { type: 'TrustVCQRCode', uri: 'https://localhost:3000/qrcode' }, +// credentialStatus: { +// type: 'TransferableRecords', +// tokenNetwork: { chain: 'MATIC', chainId: '80001' }, +// tokenRegistry: '0xE0a94770B8e969B5D9179d6dA8730B01e19279e2', +// tokenId: '96435d29f3b0ce55e452d699fd09a85efdb7200493c5e77f09cbe0b63b2e3d88', +// }, +// credentialSubject: { +// billOfLadingName: 'TrustVC Bill of Lading', +// scac: 'SGPU', +// blNumber: 'SGCNM21566325', +// vessel: 'vessel', +// voyageNo: 'voyageNo', +// portOfLoading: 'Singapore', +// portOfDischarge: 'Paris', +// carrierName: 'A.P. Moller', +// placeOfReceipt: 'Beijing', +// placeOfDelivery: 'Singapore', +// packages: [ +// { packagesDescription: 'package 1', packagesWeight: '10', packagesMeasurement: '20' }, +// { packagesDescription: 'package 2', packagesWeight: '10', packagesMeasurement: '20' }, +// ], +// shipperName: 'Shipper Name', +// shipperAddressStreet: '101 ORCHARD ROAD', +// shipperAddressCountry: 'SINGAPORE', +// consigneeName: 'Consignee name', +// notifyPartyName: 'Notify Party Name', +// links: 'https://localhost:3000/url', +// attachments: [ +// { data: 'BASE64_ENCODED_FILE', filename: 'sample1.pdf', mimeType: 'application/pdf' }, +// { data: 'BASE64_ENCODED_FILE', filename: 'sample2.pdf', mimeType: 'application/pdf' }, +// ], +// type: ['BillOfLading'], +// }, +// renderMethod: [ +// { +// id: 'https://localhost:3000/renderer', +// type: 'EMBEDDED_RENDERER', +// templateName: 'BILL_OF_LADING', +// }, +// ], +// validUntil: '2029-12-03T12:19:52Z', +// issuer: 'did:web:trustvc.github.io:did:1', +// type: ['VerifiableCredential'], +// validFrom: '2024-04-01T12:19:52Z', +// id: 'urn:uuid:0198a36a-e37b-7aa6-a715-cf85b6019301', +// proof: { +// type: 'DataIntegrityProof', +// created: '2025-08-13T12:32:28Z', +// verificationMethod: 'did:web:trustvc.github.io:did:1#multikey-1', +// cryptosuite: 'ecdsa-sd-2023', +// proofPurpose: 'assertionMethod', +// proofValue: +// 'u2V0AhVhAX3jgXNIsqyCUgAexhykylzIFcd3e5IYSZUcENyh5-dGj9dofOMWl_KZF4evDJdzgUAmeDLnqcR9CpqAZbRTKVFgjgCQDmbT57c_Tgi3whdpjj8xECa73XTJlKIQPCA3BTPGDFutYIHx-Rew_DEy5XIdKQc9Am37-2GSGk3kox8dhKgoOflS-mC5YQAZy5CHM6vKL77ZGtfv3wE1iVxz9Qq0h71Gw6aACtn-wdUbYhF3toi8Nd5RkXxBqKRkzWnS_GbRF4cSdj9wN8udYQKowz5C8RzQM8NuLYTPH-3bujsmzoqYYMNZDI32v1wE3-mxWu11cAJ5l6w2_Jd81ElbdDMSkjIeeltEWRfvsIAFYQEJU3m9S4reGE6Ki6OUkP2G9zW3xe8Wc0E6Uimf1oqttFKE6SSMXvKRokcOQ09HDtVRgp5Zed8lmLl3M38xyentYQD8o--_SpwEOWgGn-uZfMPjDaGjaOMSeE7t1Y10lFpswjgx-2oX_6DxTTfcq784R9Jc-b5giwzfhTFIQT7avDkpYQEB9Ewgc6J8Pr_NP9gz4kiIGfHVJZT16ENhp-4QA7MhdksJuxU3UK4aCail5rcM4nGg9KQQiVNMKObVqbdNe2lZYQLNUXVA7ld_LYfGo1RLNTyXpIqYQAPhNdswJWuHO5aU6R0wYJ2obBvSTmY7XgB9uBl5ytBecxai2OQF6Bt2khqRYQLRL8DMQwhMWXUUgahHlSOI87-zltHXHwvQbhK-73t-n9L4sFUHfRtRNEhRfx1SvYZtRhg-v3djuADHpcdOBk6BYQOpBNFhO9O6dd50fvWJUJetEnbbWg8NTmO30gzPcDpa4MMeJjHL83OI81ZROav8ol8OOS2qeXbSTmUJT6kdycCBYQDiAv_rwDe6tVtjjazQzxOccOIWvkdVkUUXKUCT1gLoGsbUH5uuSs98y7PEUDFOmtXVxlSTTrxd-9d7mJzyr_4lYQKkeAtOratchWur9oLQs9vT6RnjPeg_mDJxXd9cqa6FPDPss3R4Cg9T2FSG6u86J62XfQGVgnWmJcfFfLoMsL05YQJjtbnO3OdGjQ3ZT12uMHcat0RJXxSery6nsuPNN-6k5Cn0H053P7jlfdDdXiNajK6kpnepESUKpo_0zeGU643dYQL2Eg6aYFBqWLc_qvEv5RX_GXuIGgOzCm2N9wBLp3pWBwzpQ3kWQu9-EPfKN4UM3QdlSnEePsoa44c-7ynyL71JYQBI7F__JL6LXWWzvaW3a-DNheFPfApcCb0dCHpsSGR_9l4f0gTxqZZH5tS57W0v62h6IzVpz76qmk7SjtPOYQcBYQDKYI8xN4Qk-gdF08uf774Qay3k3f94szWTWtrl-O9p_1XMltqxqrguWp-CoMA9aHJd-TL0PTJSbQ1KctYvu-WFYQFhH4WxSreYkUtcxqPKhfefyI5DIl8xoZVZoZswySmcxmIC2eI-_4JfLobkdo9-N_vhlyTu5K1ZfAp2SE2SlUUhYQF9pEhyultl8EBpQdXUtb3kfD3UVNjiOhLmzr2cEBpN8OLX5JCO-NLRswp7ytaqgFEX33aYYDIT-w-pVOI3zAftYQNDtlcfBBi5Qd8GCjqTVGm6qg7urCXkrzBFiHzqOIYCfaA0ip0-l2HY50YdxYd1MNmTQFszcTZDuEQpFuRgPX2VYQBvdqItd15wfxfA70BwgCCguTU7RH0Zabwy_vxndDLyLSw2Xsfy-IhhUyVmbcCyY7uIqPNnPLd-Lprbt2pgbM6xYQE-ozaFo3mS_0LAhBJ4bR7SRg3GQLwH80UEpQxuWDdMfBAS255aUok1Nv-MtG_z_fJOJx4X0D-W8ARFUq2VMo0tYQBIse9iDOZQ96Yzk9u4a1sAslF7muc453oaJiP9AreMO5GD614b1FNyjVB70DDhCAjJR_hODqYO-QzhiyzNzwm9YQMNjdjqv5XN5uBzyxMLFN8pXSEI3wMVNNN7XA6px0n0SHmse94klMa7EQejBW5xjD-C08KrcxZ3n8zL4XUi96kVYQJH2kIvVdh9XUuOLSFrqQppm8kyHuM4BJFJsUmdBT-OxX9-FHCtIgGArcIlxX_kVuok-6sv9fvO6-bvnjRXq8JdYQDsM7f6WjhmbDPlsZTFcfZ06qQ3tKEsqVPPpmpDM6WqRwlIzDevcqauzHfUvlfcLehR777izO0sKYMSbESL_WH9YQBUuJleMjPotYsrazAjp3NOy6OCXloec6jehqVlGR7c8s4gukJPCy9O7IBjER0_yyGESF2pZM-cEPVhmxLNwoUNYQOn_PRW9xsRKLmV7TsDQwvQnBQbYYmT1nWhEdACBf9mBVNkbrsBQAwZ1aUUb55hk5-EXUHoKLE8HP9y25Et0aFlYQESxH6MpwVXehGEA8kuM8muhyBHXCzVPMXPO0CTNguRlcL9WevE_L_Y7ldJUbvBRsEnTaOZ3S4RJnQokr0jIGoZYQI6kxmtR5hVtBcStCzHFdqLD0tYodQv0jT9JIqYJtiPRk5IW9JcIJt5IT3Kkd10k0WBp4yn_5T5oqWw4bXlGe9dYQHNCnsqLYLwu98vQkxI18SAjwN8meF_tPf4yk1y5X_slJuLjHAI2g2yq8wHddmam4wGKxCOg8ZeGe1MZoEl0DfZYQGPGZWTd6DV66I-VvLFORw4UfnAi7rP_MJqjejdk604ByQrwPvDZlRnjKdVf2EGGpqKrbcwdOXSEoSZ1W_XdSLZYQK7qYaSzOnqDBx6n9qMYpQfRae_DM2krRWv59NcNtSjly-kjkhd-pytTuEsgJOQbQkfBaNcqh745bfkdUUNgGtRYQEBR3peXmp1Q1pmutQ66iw24zMsIIiFdzO8EfJZNFmX69pqshet068jMktoFWtmCAGmRcV5p9jI2XCotD3yVFuBYQP8AxwRqfef3CaPihqx4F8OxUhhySQmaYOZGYVGOmSfC99pUrN-tsNbmHOB3Yr3HY9YvqWXe3IqzuOMbbRf8I4xYQCAbs7MBJGjVgZEE6GOrqub_glf2AslIrPSZLTYW0n2E2Kcg_y09uC4wC5UaOWgMQRVNgs645mAu6KayeQN7k_1YQHKArZtvjaBNyijHj24qDQRDhQNr6GAmCDDmsKsHaHAfDWWA301CLWFYw_0OmMeF2OBBdymPbghNImt2l_AUQ35YQOdAMei-zIX-uWw8ii5d0r3OSj9IXKzAj__Fd7Jo6oSWk2H_XR88b_nVNUeHlVpJjnwXuMeCjAPfIchAR4XIldJYQGn78Irvp9aBYMOCwulF8_GhpzUdT3AV2c213VA87jZ0dfPfb3tkbS44OoF50P5UTCLD2Y-MYx9jHrWJaJDWxWdYQLVSTwmnliLV4heL9lv1pa5vE8hGhRz35TEG4gHrkWB-O3Z4x9PPTPXn5O9tE4MjLzkkRPv0M7_zQ0ge6Z7x9eFYQHpKtwDNfm8UN6jpouUInO-tyJrxMnQN7SrKvlv4Drj6LWE_9bIfDGrZl9UJTMf1EBp1_wiV6bYMEkIhz_kfPmRYQPkY6DkpKGIBy4SWSZrIHj1UuG9UMMdkCOClQjPGqcXYCiwlC8uuW2C6Jrr_28PJvwnAS-0le25TP-TALuXSCGhYQLhFn1rEv2NJ_6N1jFZLCoeDvHMFWJ9aIlj9huCQuJJcnOONp_8qbAbP4db_rKyxUclacddLeX6MLnI29om_rYBYQMg1y6VkfsnfA8nZ7TeksPU48eyOBy3PRMkIdPB7QWWy_c-90CdTbPZD3C_Bw4w82w-uyDtC3vZr3aaii524sf1YQG8ZcfLuVfWxwpNzi9W6u2rg0b17lI86XGDCBpfv_4nMaEgGlVWmn-nn63NbsquKHPkCCNq9_ejEwjiwdNHNuPxYQAAXhoenzuHU6T5C5_Y6dYRNwR9V08ABjrwfUn-OsEm7zx7CORGcNBJ-zN3svFJfOrhCAof6jxWc5WXnJuj93SRYQEmfQSDgxPkWiD64pLJwBk4kJjJe_rdmJO9zL-rPb7O9sL-0Af8tgiUnREE7Qn8y2l7PoY2ytC4b3h_TJ0NDlM5YQD_NiL6Ojj7D0EVEJ3d0cZBQ0X8nE1CscGfrV_NbFQhYAxHHIjnTSLEN_oF76conYHts6xpgnYx4a6ttRbLwQKpYQNzuO2DpnS1bg9ZMoNgeBHJETxIbHm6Grk3vLb8KTk5koyY2jhjHDdp1uTezCG5nXbP_gVLAcowSotH_c5G9XLiCZy9pc3N1ZXJqL3ZhbGlkRnJvbQ', +// }, +// }); +export const ECDSA_W3C_VERIFIABLE_DOCUMENT_V2_0 = freezeObject({ + '@context': [ + 'https://www.w3.org/ns/credentials/v2', + 'https://w3id.org/security/data-integrity/v2', + 'https://w3id.org/vc/status-list/2021/v1', + 'https://trustvc.io/context/transferable-records-context.json', + 'https://trustvc.io/context/attachments-context.json', + 'https://trustvc.io/context/qrcode-context.json', + 'https://trustvc.io/context/bill-of-lading.json', + 'https://trustvc.io/context/render-method-context-v2.json', + ], + qrCode: { type: 'TrustVCQRCode', uri: 'https://localhost:3000/qrcode' }, + credentialStatus: { + type: 'TransferableRecords', + tokenNetwork: { chain: 'MATIC', chainId: '80001' }, + tokenRegistry: '0xE0a94770B8e969B5D9179d6dA8730B01e19279e2', + tokenId: '8b44d0a7a2e5f083945f4132a06a23fbe58c2e567a3ad89ac0239e02d2d71cf4', + }, + credentialSubject: { + billOfLadingName: 'TrustVC Bill of Lading', + scac: 'SGPU', + blNumber: 'SGCNM21566325', + vessel: 'vessel', + voyageNo: 'voyageNo', + portOfLoading: 'Singapore', + portOfDischarge: 'Paris', + carrierName: 'A.P. Moller', + placeOfReceipt: 'Beijing', + placeOfDelivery: 'Singapore', + packages: [ + { packagesDescription: 'package 1', packagesWeight: '10', packagesMeasurement: '20' }, + { packagesDescription: 'package 2', packagesWeight: '10', packagesMeasurement: '20' }, + ], + shipperName: 'Shipper Name', + shipperAddressStreet: '101 ORCHARD ROAD', + shipperAddressCountry: 'SINGAPORE', + consigneeName: 'Consignee name', + notifyPartyName: 'Notify Party Name', + links: 'https://localhost:3000/url', + attachments: [ + { data: 'BASE64_ENCODED_FILE', filename: 'sample1.pdf', mimeType: 'application/pdf' }, + { data: 'BASE64_ENCODED_FILE', filename: 'sample2.pdf', mimeType: 'application/pdf' }, + ], + type: ['BillOfLading'], + }, + renderMethod: [ + { + id: 'https://localhost:3000/renderer', + type: 'EMBEDDED_RENDERER', + templateName: 'BILL_OF_LADING', + }, + ], + validUntil: '2029-12-03T12:19:52Z', + issuer: 'did:web:trustvc.github.io:did:1', + type: ['VerifiableCredential'], + validFrom: '2024-04-01T12:19:52Z', + id: 'urn:uuid:0198bd46-6b8e-7661-a19c-8d608f1f2a77', + proof: { + type: 'DataIntegrityProof', + created: '2025-08-18T13:02:45Z', + verificationMethod: 'did:web:trustvc.github.io:did:1#multikey-1', + cryptosuite: 'ecdsa-sd-2023', + proofPurpose: 'assertionMethod', + proofValue: + 'u2V0AhVhAaza3_cFVdDWARDWR4cV4SQOQ0SIFR6OGALPOEM5X8U4BkD_kgTvSSnIrZmRwyNemEtI5BQ7JmWk9sDcYcrgN8lgjgCQCeUnpYnpjxzQXARbt9qNebaTK4njyttXsD2UR9tayRHZYIEFAgdS7BRlH4W-vOfnUu-L5j-AyWzpodqk-mUuROA31mClYQK9a3Fc9lHVOPnS--UgAfVQRENIedf6jrMoLN5ckrJPNO7OBPJMglh-5D0T7Qp_CnOrwch2f3dlFwfwz42z_eM9YQOxxcs-LvJ4k1PHzN2JMNxE19NfAIIPe1mCP_IjI9ekVJG-DiPJetAR_WtQS5rQODVuapz3X424ICeQOrgfgJhRYQAUYDOFKeTsNSkoovWCwuuurZJsRBMuVvd6-FPo3dOXPCj6eno_Ur2clUA2RvIkWdklqGD6GE8cBBnBs9OVv_NVYQKHvH16Y_xKT9_4NKSKdV7IPD2zuy1p6_FkhYa0aRAXU_8FBpBWpn_qFocnkMO5FHQDkCpyLyQa-yHTo2DGfWItYQIbJVco8TOvokwZRjHuezXBiWDtQY0CGTr6XI04qWPKHHAV6qv_cPv9QN95S8ImDFd9Jq5wnKk9mMzFZqQxFu2dYQLldGs8mZDDyerrujluO-OaNcJfru01Sy5_espg3NokKMxcCrafo8CEH6iOmWwt-ntRb56bwGE3saFAGXXNNjRdYQFPOZOPMxVrSGnt3A4yE0CYCZfeP8nmPZCNs2deHRxPKbvBB7miIihku5ak4GxOHikrKXnE8-SS3WUHG1VMt_-1YQPHLvSmGXpJ6PMlu_qG7vq4CxCJoi6RbDsiONxMJCalg9Fm0SNYJZWtkCxH-ktXCbqUpP1z4vgh8x9MvSnEUMvBYQPkEhQD5m70pJWqG9sCx1FPJwVbLXac0N-EKbUJGJ7OoxXHeUV71RiJIZjmM-3a1L0eMyk15r9dAllaMV7LB4RVYQIhjlQWv18OBNKxHTeTnclUZ3whr8vZ-Lev_1WgEo3usutBwzAoWZ03kXv-oHlhGq3-MTX8du4FKmzN4MlwtSBtYQHnVGf7THennsSBUu6d3riOnF68uQYceJUKJOAsY54m4DHv1vBVrXwxvUvLdbe5IpcTuY0RdfmzRHcIv3rV7Eu9YQCUVkdeN5FU5XEp5AgVK-H2vsMdK0ZvHRX2aCQe_3wlMyI9xWajWNDxaCXNslNOAw4eXD0phRj0BzpRzih5oIvFYQAXv0iIMKEHa3TJTIUoAXudyixqmwPB80D4_qXRhfC8esLm1R2ZdppBPI3In64GUHuD8lJYRjFt0PKbxMcyT6KdYQGbW6YOwuaFwghMzOWj4QjR9hhBEtoIx6mAMJfdqSCmT2U4lwS0QcPmjhqME_2OLJojY-K0YusWyZj1pCOh5RIxYQJ0mPNCvuPYv08tOm9cbBZPS7R0AKkF6T4A3tzbdVZ2WovDeIBdA0AbGjpxlKOEKtBThYa-trRsT7yU-Tsp2VP9YQA2rDWql138q5S5foj6VZVNpawhP2ynzelT_K381qsksj7e-4rC0inhyGvh2HXjj7_F8RhjBWy1Y8SqAP_D6iXtYQHXbn2RAJX1kHo47xhHB2TT4RgKUL7BcO4u697PVYlC6ndBxO-hOuM_U6eh2FdlDvuGQaUlSoLm8S0Pn5vMhT-NYQIcz0Hi1oN6viLHKUo0HqSK-pbci1Z7rusQVUOQdEVerfE-GzW5fAIDr74wQHVokqzetzD9inA1AWdvN-1foTYlYQHg5Mv3TJ4immMQdzwd2vOPnr2wKDQgvDJ22g8hxd5YGgUPRJjSH9MMKcRXuBVIZXmChGxmhPfzsUcCXTleZ8pBYQNcCSxhPDknei_wPKHbtSiVmFsi1ypRCCGnJZGopDC2xdPHjtb7v4lrtzWF3_zvGFDeSlLca-1QZS-YxOv4MvwNYQCeeG0E6s5rFc9Cno0C1evT9FJlIX8lcENULh1eAE0cRsLy_rgz_P1eYV-_Du4mp9JrQBDTyyFxWUFexTceia45YQIvh-c0E8a_s9sbmfvEz_upibAgg7Kqqka2lAifbHOHTtbermTaNs95nuP8bIWWFZz9qVvsl3sf9nD4ckAOmTINYQJwaa_zKwnOfq3ST4KpMwYmpufGvxtrct84ZoI7B4kFTE2n6Z4wVuu0WLqtq4afYTmrQ7Qhkr9gi3VFTJNWNFmdYQBpXKx9u6AhU7U-wE14jdW9vVE8Sl2sFN22i9JyMkhKQhI9n33-KPF20MwSGxfzKncAdxukS1cTFeaUIFzcx7XxYQG5OD4CnjTTA1Ki8yM1iAx1wwGRl5KO0g1XFgAPgF6gHOXA6UY_f0Rf4_1eU6lwd9IHvi3GHR4Ea2NQCFydNIzdYQIkFHeY4JnlwI-XN3tlGlZN15YGEoNdFvOmBnI7ANB9pjm9VYt0fZaZwU0kHFJUOaqJmlxehSpywLKYewUFWMgRYQLbyvcEAByxAnc6RSZHjpBhuXkIgDgCxOwsatwg62XSqh9Ou-wKaBg5d5ARKofSI7-v68y0xzPyjrPp1MQGehfFYQGgZC8rut-CFKrmNhsZw2wqA4iJMoRT_Ww2koVfuCGxxYQBQW23zzkXALVMWdttqY4lIet0rH6_f-ylmQrB5q7FYQMkpTM-NT4yCn1UY4U_HnoY9Eopg1sgmYsaA5AMo9WFovRQkd7HRHhAgi9Iuodqxw9MjFGYX07ASd_d-BragZBZYQLEkpg2PiDfh0zvtJKEnWhPRX4yUChIZ86cQ1eCKvNsbVqT64bwFzRRx8lab0FMpQgTgOFB9ACO_kKWdf3xgdFBYQH6emAuiM0fBbnUU7jT5QnRNckC1l3a44zTGHfyVakFueo5MbjgKEwneDAQ9AzGjGtqK2aAMQETa5sYGUqoUo6RYQKtGocAHRPPkAqSoHt2rKmB7hC-nxTPMPNZ_3oUMvrtf2hF0w7yX20DsC1HWf2Z4DavjxI1jfSvCcnSV58b8WC9YQFazPwizz1aWq7XPvbLcfMc8MlO-pH-2_uY85DS6xBAiQZuB0kZcNkz_QoKchVNlQyay2DPyjDr35LLj8zSicGpYQBVewABn9dgauraK4EDNx0oPFlybXMlhQicfXT3ZmuRtsFvnH24t_G_vlW54XBkKlThv9ZCMederOQT0pAi0rZtYQCuu_uF3gW_FfxgCY4GJUb0zmo7iEa8mW6ja9inaBbGKX9ZUSHlHvCMOb3niQwzkCOkkwgYT-AXWKCJ2YBvf6i5YQH3VHg6LJpkG0oBaFut9pm8gDNt040IfQV_eZjGQ1kAfmUrPdviW4WqX8EVpn3TOC7CW3iyVLmglv2DGuRzrWAlYQDVKKuNxCV44iP8hrqV6VOClixZAjeh5FW720VyZIm43mRBMTbcXUOJS8FHGcMWaDCh179ygQy7c5xE9l9MBrctYQCJZrscuL2qDTz80-SxUJ2L5UsDf7q-XXz4op1eOqiUUpAxrKg7FaZ8xcDiBQrZP-HHMgq2yUYtm6Q17D324jVhYQIc_3DEB-otsaFo-sb9iH9DKQXoH166eDM3COv7ylyBMrfqvCLrH_8X0oyYCmamEH2XvwehWlV4B0wLgzTeOi3hYQB-3W-IFyl1ATK2RO5ktsCls-7N9hAheltxt_-yGzc779Psz_KbYF9mWpUQ1_XehDMaOV4ilySmAQrOPgCAvFKFYQJRkqfn9L_fE2gH55Xc2Z1kM601URF-sof2DQSPwWKebqiCGtTJt2bos_hkvbqGzPwHX54wmWqTwKYmfdz1vs_GDZy9pc3N1ZXJqL3ZhbGlkRnJvbXEvY3JlZGVudGlhbFN0YXR1cw', + }, +}); + +export const ECDSA_W3C_DERIVED_DOCUMENT_V2_0 = freezeObject({ + '@context': [ + 'https://www.w3.org/ns/credentials/v2', + 'https://w3id.org/security/data-integrity/v2', + 'https://w3id.org/vc/status-list/2021/v1', + 'https://trustvc.io/context/transferable-records-context.json', + 'https://trustvc.io/context/attachments-context.json', + 'https://trustvc.io/context/qrcode-context.json', + 'https://trustvc.io/context/bill-of-lading.json', + ], + id: 'urn:uuid:0198a36a-e37b-7aa6-a715-cf85b6019301', + type: ['VerifiableCredential'], + issuer: 'did:web:trustvc.github.io:did:1', + validFrom: '2024-04-01T12:19:52Z', + credentialSubject: { + type: ['BillOfLading'], + billOfLadingName: 'TrustVC Bill of Lading', + }, + proof: { + type: 'DataIntegrityProof', + created: '2025-08-13T12:32:28Z', + verificationMethod: 'did:web:trustvc.github.io:did:1#multikey-1', + cryptosuite: 'ecdsa-sd-2023', + proofPurpose: 'assertionMethod', + proofValue: + 'u2V0BhVhAX3jgXNIsqyCUgAexhykylzIFcd3e5IYSZUcENyh5-dGj9dofOMWl_KZF4evDJdzgUAmeDLnqcR9CpqAZbRTKVFgjgCQDmbT57c_Tgi3whdpjj8xECa73XTJlKIQPCA3BTPGDFuuDWEBCVN5vUuK3hhOioujlJD9hvc1t8XvFnNBOlIpn9aKrbRShOkkjF7ykaJHDkNPRw7VUYKeWXnfJZi5dzN_Mcnp7WEDDY3Y6r-Vzebgc8sTCxTfKV0hCN8DFTTTe1wOqcdJ9Eh5rHveJJTGuxEHowVucYw_gtPCq3MWd5_My-F1IvepFWECR9pCL1XYfV1Lji0ha6kKaZvJMh7jOASRSbFJnQU_jsV_fhRwrSIBgK3CJcV_5FbqJPurL_X7zuvm7540V6vCXoQBYIMN9Xgpjiq0TdOyfvtotDMTAv150nwqhFrWdtIFgOhfwgwACAw', + }, +}); + +export const ECDSA_W3C_DERIVED_DOCUMENT_V1_1 = freezeObject({ + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/security/data-integrity/v2', + 'https://w3id.org/vc/status-list/2021/v1', + 'https://trustvc.io/context/transferable-records-context.json', + 'https://trustvc.io/context/render-method-context.json', + 'https://trustvc.io/context/attachments-context.json', + 'https://trustvc.io/context/qrcode-context.json', + 'https://trustvc.io/context/bill-of-lading.json', + ], + id: 'urn:uuid:0198a75a-de81-7ff8-9ee1-78c4dd730bd3', + type: ['VerifiableCredential'], + issuer: 'did:web:trustvc.github.io:did:1', + issuanceDate: '2024-04-01T12:19:52Z', + credentialSubject: { + type: ['BillOfLading'], + billOfLadingName: 'TrustVC Bill of Lading', + }, + proof: { + type: 'DataIntegrityProof', + created: '2025-08-14T06:53:27Z', + verificationMethod: 'did:web:trustvc.github.io:did:1#multikey-1', + cryptosuite: 'ecdsa-sd-2023', + proofPurpose: 'assertionMethod', + proofValue: + 'u2V0BhVhA3CXgOMU0EF4qmDn6RSOu7WPpuxjeYbVEk0-tyGCrH3yjG3YY7IXfMz-cL-ligsoQyCWAovEVNvzUB2m0KOUdPlgjgCQD5O3ZFudt4c2ulEYKV5eG7CGWez-e0hoPCYDpm72VGX2DWECLRAcjn-vZFIVZvDw53RpNPBsafE4LW_1A565bZL79-KuFQua5LW7FIUvzKJJs7R1N9iZr0UgQ4poXaOsmVuaUWEB8L389YKMZOS-v0TZ6gmcgorTyeFGS0hCG6A-wpvDlvlvNjQlVGCVFKPokYuIBqWa6bsae93-j3zfX_kbvw0p3WEBb1hc959fOgYowJVy3XAUfBUm6eWnWlMILP51XdzcN4qW8eZ_3fIT02XDQ11ez8yddErKkLRN8bpx32F-UwwXboQBYIFDsDNQlJGfWpg0PWoTxndRra142ri4cbifmOKiInsHWgwACAw', + }, +}); + +export const ECDSA_W3C_VERIFIABLE_DOCUMENT_V1_1 = freezeObject({ + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/security/data-integrity/v2', + 'https://w3id.org/vc/status-list/2021/v1', + 'https://trustvc.io/context/transferable-records-context.json', + 'https://trustvc.io/context/render-method-context.json', + 'https://trustvc.io/context/attachments-context.json', + 'https://trustvc.io/context/qrcode-context.json', + 'https://trustvc.io/context/bill-of-lading.json', + ], + qrCode: { type: 'TrustVCQRCode', uri: 'https://localhost:3000/qrcode' }, + credentialStatus: { + type: 'TransferableRecords', + tokenNetwork: { chain: 'MATIC', chainId: '80001' }, + tokenRegistry: '0xE0a94770B8e969B5D9179d6dA8730B01e19279e2', + tokenId: '044905d97399d520ee96e08fe13621e45810e404e9b16ffd43a11dea52a72c91', + }, + credentialSubject: { + billOfLadingName: 'TrustVC Bill of Lading', + scac: 'SGPU', + blNumber: 'SGCNM21566325', + vessel: 'vessel', + voyageNo: 'voyageNo', + portOfLoading: 'Singapore', + portOfDischarge: 'Paris', + carrierName: 'A.P. Moller', + placeOfReceipt: 'Beijing', + placeOfDelivery: 'Singapore', + packages: [ + { packagesDescription: 'package 1', packagesWeight: '10', packagesMeasurement: '20' }, + { packagesDescription: 'package 2', packagesWeight: '10', packagesMeasurement: '20' }, + ], + shipperName: 'Shipper Name', + shipperAddressStreet: '101 ORCHARD ROAD', + shipperAddressCountry: 'SINGAPORE', + consigneeName: 'Consignee name', + notifyPartyName: 'Notify Party Name', + links: 'https://localhost:3000/url', + attachments: [ + { data: 'BASE64_ENCODED_FILE', filename: 'sample1.pdf', mimeType: 'application/pdf' }, + { data: 'BASE64_ENCODED_FILE', filename: 'sample2.pdf', mimeType: 'application/pdf' }, + ], + type: ['BillOfLading'], + }, + renderMethod: [ + { + id: 'https://localhost:3000/renderer', + type: 'EMBEDDED_RENDERER', + templateName: 'BILL_OF_LADING', + }, + ], + expirationDate: '2029-12-03T12:19:52Z', + issuer: 'did:web:trustvc.github.io:did:1', + type: ['VerifiableCredential'], + issuanceDate: '2024-04-01T12:19:52Z', + id: 'urn:uuid:0198a75a-de81-7ff8-9ee1-78c4dd730bd3', + proof: { + type: 'DataIntegrityProof', + created: '2025-08-14T06:53:27Z', + verificationMethod: 'did:web:trustvc.github.io:did:1#multikey-1', + cryptosuite: 'ecdsa-sd-2023', + proofPurpose: 'assertionMethod', + proofValue: + 'u2V0AhVhA3CXgOMU0EF4qmDn6RSOu7WPpuxjeYbVEk0-tyGCrH3yjG3YY7IXfMz-cL-ligsoQyCWAovEVNvzUB2m0KOUdPlgjgCQD5O3ZFudt4c2ulEYKV5eG7CGWez-e0hoPCYDpm72VGX1YILfHPtzGHotFmZE0vGWFsH5T_mliaK_VXZRZaqVlkS6NmDFYQFNp6GH_bDmDGlCb3f4hhIpE5vCY8v3vc9xEeR6elSND7XlmdXbYpfXfzeDgMkNrGv9Awpt8q_cNTGuZ-Py_bTRYQFMU55RQG280KLBU3KpLXNXfBqaVzIq0oA9zhQZuEkKPpUhKfwHBYGVXj0jzOdLSIe7-5925ivAlYD8-xCrKQA1YQC4YuskhD6qyu058dcYb8gFan83OTts9WpNdpLIgelUkLztuG_31-nc0zJtSoknuyA-v_On7FGnmne1zJ3S_9P1YQItEByOf69kUhVm8PDndGk08Gxp8Tgtb_UDnrltkvv34q4VC5rktbsUhS_MokmztHU32JmvRSBDimhdo6yZW5pRYQAmaDDThptdD3KEXfWzGgBBn5kK_VrrvazQ4bzaYtwlqEZDFCaLePMP9CY8iUUbyQ1JvRX_4HPschYrubl_SliBYQM_90CuoXHgIsO9W0eTxi1YbVioCPOcBAykqYGoQmDoK3Mr1GGCsbZ_tcTw7uGXFWLPZOjNJL1QQ40jaPFkLcFdYQHxdfcvJsoIHJyUi_6yfmGffYgkUGspLEASaicOMRRkQDnt3u1vLElAF8e8HApVD1JjWAYYgP7H8mFZU8_ZqM-NYQBUhIJCPnSBtyBI59FDm5dN5MluYtgouJJmMRWnwz_rXDEuheKMrWaBfO_GhSRIuEbuChSUiF2yxiwCa3ARedDtYQCXzZOV9hPPg8qhHUYYrRhLL0xGau9-oPHCdtFONSiaUAK6FTpwMl26qeBHKnBT8ZYe87KQbhgjFAEC4XTt9CVpYQBzXkKy9B75xEqSKgZ5t0WETWjs18Mew6EPRbAxu-hkGMu2IV5_j-da_MaP3SdyDVKWW5UXGiZXcDNL2yTH6QzJYQNZLKD0MIbDGkK51RSefR2r3SRw9qRZpjfIHNHI-EI_klkKUgTvzT6mIn6il7kb2lEorcfBCnbQsQTF72oRzl4BYQMUWmS1kOEX6zaLurcFTf0qUi-Pr5QipxwRx7zqn0-Nf4V25L5eGC3O5s-zVQVh3YmHw5zN_Ilt35-6N23bfZRhYQGdZiNKgfhsjjaomqm_XtYcWoAfI4NI12foNiArZosb-uTU1nt8YY408RXOCc5r2KbUcAPoLyKP_y8d3l8wgFb1YQETMUxQLarqOhmXJQNRENHdQgiPuhfS4-v_Qutdixnzf5FPWaFJa68cGqfZedZrdJtB84PQ9mKwfUOPvyqMVCa1YQA2B_DMpryEbZz2_LhwIyFDaEz-GQkbHgnNTc02U_eYuVDb6u9VQHLaGh8Xf2X8lCQWLV4-M--_qdA7SYggRChZYQP36at2BmNsBu5mCVUj_cYI2cdRpWRtLpU3V1MvbRap0yl8yFugjmCYhVr_FX0Z1pmy6PIFjOi9Ek3tLTYQnCFhYQKIojqtv-O343E_oKB0IonrzK23TbhnYLUZIvdT3tBSNKJOcsP37D38FoAUQo6ujz92dwGfnGLNuraFMoyuP8EFYQBiILoJ2CDQmvDTkOUeT7l8HTI24ok4-Rod7BiieT_52Kk9fIBOJb0J3yxz8tcJ7Er5fI4GTuGz8sK2fyZzKbwJYQHYQSaSA4ng1mY0M5vsESj6wsUQ3OKT5ZKcI5I85zzB7z33ajC5D72dZpJ27euGQZoij0fDHyB3Bzjs-rzD0ZoJYQDzpKNkh8edZb12ESX-2ANfMbaupt_lgXfYn3X-P1V5yZQ-AtWWH4tKo9ZB13KdNgiV_tok98H6wiRZOhWX6xLlYQHwvfz1goxk5L6_RNnqCZyCitPJ4UZLSEIboD7Cm8OW-W82NCVUYJUUo-iRi4gGpZrpuxp73f6PfN9f-Ru_DSndYQFvWFz3n186BijAlXLdcBR8FSbp5adaUwgs_nVd3Nw3ipbx5n_d8hPTZcNDXV7PzJ10SsqQtE3xunHfYX5TDBdtYQMXPmVJBWMvyyZ6xK-bTDa91uJwPMnpuTpr9J6FNu07vgajiawNwfnUaLkEwac_3xaWtThnqLJKPLHOcE_IeYEJYQBhkGzbcCAFTF03u2Rm2lHUVyQhcZ5KbVuiZHz_lR53BhOCCz4ra47lNLFiS4X7kSBhEtwQ8ZOcqOtM9oBTaikZYQF_ivjUPc6PjNlaANCW9nwDKZjLahZNSq-bh2QckN3yPx7XRN9fHtLNEU1cpmgKrti4p-7r23YwUSor7pWwDHdZYQLr6dyAHcJRaY1PUQhX3jHHNP5ewPY52O6ylBdzrPGcNpJcgBrNhxw1Q6oZaCRAmH8Guq2a8QyYVeVnlkXKXszhYQASCBN5CVB1rxS3Pa2VCw6Z60MwumIlqArUVo-pAa8h6yydyqyC6rCZQW2xP33KxIr7bpMLw2Ii_fqTxuw2tbPZYQBDGQ8CNVHvLyV_XFaCWcOwCNJRoq_hA3PF4sGQWSyLLY3hP7jmH0mwC_JSqsejowQrm-E2vWEu45CSHbrklCEJYQKch-XDAV0uJJ0KSfX02tyC1Yd1y3_aEskRJtPTc40CbIf0vxb0ViR2Zlcrrl-W4RZZ2QBuqZj1S9039_Y51nElYQPzNf34Ae3oi7F9KXrKLYhatXwFsGAmfhLVYsbytmFneTTY4tlu28GzeVofsxoMQfJSOKfAvZPIjOi41-Ge6F6pYQESu3zp8w1-nZA1V0rP-7-MtRHxghwVamsoMlxyNpvsy83FzfEzVYDmArVPWa2DFBlfy3QW9wApblgPDT0cpDONYQPa-1KuGZk1CmZnw4zS2WdC7E6LloIiiS2luFQmBvswty8FKl_aBUSD8E1hiPF9dob8ywhK2t-RrxblUX-zgwypYQCDS6ek8fzFVYStPVciblZoZZj8h-jGiJavg3y8qMVjhc558zekPTYuLseSxXy_VeaLj8UVWeZgiq4OdkZro81hYQEr2Jkj05F-O-ggBabmOlPh7tajG-BEjIEuhDHhE6oKQUARVuGo_sbcJPbAKvBqNDeD7P8F_tNwhK91S2lao9xRYQOsZGPEFIKPzocTX9gkuzuYkzDmS8UrZK8hExnKeLZt4f5zmHV81znitcGe-olTCpvEMt3tp1S9H6mclIxcTF5xYQKx7MZiCYjBGo-O7csc8Eo-aEhjU_1mL5M2VpUXEN0DDRyZuKNlXfu2KhKGecHYh3mBi1BQoOa301A483MPFJ05YQEBJBkhFQT2Ne5dt7QpJJPC6f432ufaibx9p2NDUuaxEUEjxL7plXIwFUIkbugqEMzM7FEp_R2ePMsVJEJRDHmBYQDhEPSM9Bxgtx2rW7Z8BbznEnMZ9uiQWdgWd8CJc1pg2ORYhoxk7LfUMHVmMiCZG175wq88OK9R4GjtVHGTN0eZYQDIFbMaBycQ5DsPp_AxTxkjAH8IKdAUtfsP173xfLmDPUP0FgchQ1hd02LSlTtEhgDYg0hLbJhzJWYt24H3zvbVYQEQaVNVyrVw6XgPrQeqreuR-1Oi29rIaqoWx0riJuCQerAN0MHBWdwgFcWhXftzhnGWhH9Gn7hFj8tRECcT8BghYQD0xlUOIxleK0jMJJBO46yLRYnfwHS8r8QEEV63EYBMTt1dX9YNbB77jDZkZ2ZO6g-O6BBoJtG3P1KvV09D3HixYQLIf5c_l5JNY8vVPBPDOdz4G-EhAX1MIZHn1bgXoPtTRGUEuvNQIy94A8NcjtFPky2t9XnHOoo0emSMxiEZTmb9YQEfu6GoEvOyGMRJZASZDS53O_j4YxuTVtc12Vqfuuc7z9Ed-lMlmDFMltm8YciXfbBEkx4xk8v5w20enAkYhrKNYQMJYmPp6KY6ocP1ZDBAfNBbmEGVqdV9BXXLgnNhg9zQN1bHiEHh_Sx3p3BtPd6FETjs9Yb5nadjWFMX8tvoqEk5YQOfjQpsPlZKx3oyAl9Cqq22t1SJNpgSWD2XC44q1-vancr4XyxNt4KEuINKzwJOzwtOJXbZrSMkaOEuVNTQHi45YQJXXJr5AnDEUEWYMzdRPN-0G2fr4FQNNDTeZNzh2weVfNEqpwfNcaz2dGvFytCk1WBZl5aw0UCRP8kk8rDSsupdYQOD8HclLNZr1XYB_XFusbUp_jjco7k_SMKVLNL1wiIOq-18lwQvafroOj1m4FeeS83dZEVYTlbsTEiFm9KLb6qlYQBBLvuQk5A_KDqcCNTPf2dvJ0LTbr9X30FtxN9cq3093Yy5kke0zhBcbn6FzL5BnqGQf2-k7RWZfJG9M3oipZaBYQPGaq3YmgoBA51P7Hq8VSvNdYDcg6VtpQP8xumCgnElHHZZ9KwOqa1q-Ud8xOIvuEImOT3aWFXdDb4y_V4GZshyCZy9pc3N1ZXJtL2lzc3VhbmNlRGF0ZQ', + }, +}); // Freeze fixture to prevent accidental changes during tests function freezeObject(obj: T): T { diff --git a/src/__tests__/w3c/derive.test.ts b/src/__tests__/w3c/derive.test.ts new file mode 100644 index 0000000..2020672 --- /dev/null +++ b/src/__tests__/w3c/derive.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from 'vitest'; +import { ECDSA_W3C_VERIFIABLE_DOCUMENT_V2_0 } from '../fixtures/fixtures'; +import { deriveW3C } from 'src/w3c'; +import { SignedVerifiableCredential } from '@trustvc/w3c-vc'; + +describe('W3C derive', () => { + it('should derive a W3C v2.0 document using ECDSA-SD-2023 without custom selective pointers', async () => { + const result = await deriveW3C( + ECDSA_W3C_VERIFIABLE_DOCUMENT_V2_0 as SignedVerifiableCredential, + [], + ); + expect(result.derived).toBeDefined(); + expect(result.derived.proof).toBeDefined(); + expect(result.derived['@context']).toBeDefined(); + expect(result.derived.credentialStatus).toBeDefined(); + expect(result.derived.renderMethod).toBeUndefined(); + expect(result.derived.qrCode).toBeUndefined(); + }); + + it('should derive a W3C v2.0 document using ECDSA-SD-2023 without custom selective pointers', async () => { + const result = await deriveW3C( + ECDSA_W3C_VERIFIABLE_DOCUMENT_V2_0 as SignedVerifiableCredential, + ['/renderMethod', '/qrCode'], + ); + expect(result.derived).toBeDefined(); + expect(result.derived.proof).toBeDefined(); + expect(result.derived['@context']).toBeDefined(); + expect(result.derived.credentialStatus).toBeDefined(); + expect(result.derived.renderMethod).toBeDefined(); + expect(result.derived.qrCode).toBeDefined(); + }); +}); diff --git a/src/__tests__/w3c/sign.test.ts b/src/__tests__/w3c/sign.test.ts index b32f258..5260f17 100644 --- a/src/__tests__/w3c/sign.test.ts +++ b/src/__tests__/w3c/sign.test.ts @@ -1,23 +1,81 @@ import { describe, expect, it } from 'vitest'; -import { W3C_VERIFIABLE_DOCUMENT } from '../fixtures/fixtures'; +import { ECDSA_W3C_VERIFIABLE_DOCUMENT_V2_0, W3C_VERIFIABLE_DOCUMENT } from '../fixtures/fixtures'; import { signW3C } from '../..'; import { VerificationType } from '@trustvc/w3c-issuer'; +import type { CryptoSuiteName } from '@trustvc/w3c-vc'; + +interface TestCase { + name: string; + proofType: CryptoSuiteName | undefined; + options: Record | undefined; +} describe('W3C sign', () => { - it('should sign a document', async () => { + it('should sign a W3C v1.0 document using BLS12381G2Key2020', async () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { proof, id, ...documentWithoutProof } = W3C_VERIFIABLE_DOCUMENT; - const signingResult = await signW3C(documentWithoutProof, { - id: 'did:web:trustvc.github.io:did:1#keys-1', - controller: 'did:web:trustvc.github.io:did:1', - type: VerificationType.Bls12381G2Key2020, - publicKeyBase58: - 'oRfEeWFresvhRtXCkihZbxyoi2JER7gHTJ5psXhHsdCoU1MttRMi3Yp9b9fpjmKh7bMgfWKLESiK2YovRd8KGzJsGuamoAXfqDDVhckxuc9nmsJ84skCSTijKeU4pfAcxeJ', - privateKeyBase58: '4LDU56PUhA9ZEutnR1qCWQnUhtLtpLu2EHSq4h1o7vtF', - }); + const signingResult = await signW3C( + documentWithoutProof, + { + id: 'did:web:trustvc.github.io:did:1#keys-1', + controller: 'did:web:trustvc.github.io:did:1', + type: VerificationType.Bls12381G2Key2020, + publicKeyBase58: + 'oRfEeWFresvhRtXCkihZbxyoi2JER7gHTJ5psXhHsdCoU1MttRMi3Yp9b9fpjmKh7bMgfWKLESiK2YovRd8KGzJsGuamoAXfqDDVhckxuc9nmsJ84skCSTijKeU4pfAcxeJ', + privateKeyBase58: '4LDU56PUhA9ZEutnR1qCWQnUhtLtpLu2EHSq4h1o7vtF', + }, + 'BbsBlsSignature2020', + ); expect(signingResult.signed).toBeDefined(); expect(signingResult.signed.proof).toBeDefined(); expect(signingResult.signed.proof.type).toBe('BbsBlsSignature2020'); }); + + const ecdsaSdTestCases: TestCase[] = [ + { + name: 'Should sign a W3C v2.0 document using ECDSA-SD-2023', + proofType: undefined, + options: undefined, + }, + { + name: 'Should sign a W3C v2.0 document using ECDSA-SD-2023 with mandatory pointers', + proofType: 'ecdsa-sd-2023', + options: { + mandatoryPointers: ['/credentialStatus'], + }, + }, + ]; + + ecdsaSdTestCases.forEach(({ name, proofType, options }) => { + it(name, async () => { + const { + //eslint-disable-next-line @typescript-eslint/no-unused-vars + proof, + //eslint-disable-next-line @typescript-eslint/no-unused-vars + id, + //eslint-disable-next-line @typescript-eslint/no-unused-vars + credentialStatus: { tokenId, ...restCredentialStatus }, + ...documentWithoutProof + } = ECDSA_W3C_VERIFIABLE_DOCUMENT_V2_0; + + const signingResult = await signW3C( + { ...documentWithoutProof, credentialStatus: restCredentialStatus }, + { + '@context': 'https://w3id.org/security/multikey/v1', + id: 'did:web:trustvc.github.io:did:1#multikey-1', + type: VerificationType.Multikey, + controller: 'did:web:trustvc.github.io:did:1', + publicKeyMultibase: 'zDnaemDNwi4G5eTzGfRooFFu5Kns3be6yfyVNtiaMhWkZbwtc', + secretKeyMultibase: 'z42tmUXTVn3n9BihE6NhdMpvVBTnFTgmb6fw18o5Ud6puhRW', + }, + proofType, + options, + ); + + expect(signingResult.signed).toBeDefined(); + expect(signingResult.signed.proof).toBeDefined(); + expect(signingResult.signed.proof.type).toBe('DataIntegrityProof'); + }); + }); }); diff --git a/src/core/documentBuilder.ts b/src/core/documentBuilder.ts index 9f7900c..170393d 100644 --- a/src/core/documentBuilder.ts +++ b/src/core/documentBuilder.ts @@ -1,8 +1,10 @@ -import { PrivateKeyPair } from '@trustvc/w3c-issuer'; -import { signW3C, verifyW3CSignature } from '../w3c'; +import { CryptoSuite, PrivateKeyPair } from '@trustvc/w3c-issuer'; +import { deriveW3C, signW3C, verifyW3CSignature } from '../w3c'; import { assertCredentialStatus, assertTransferableRecords } from '@trustvc/w3c-credential-status'; import { + ContextDocument, CredentialStatus, + CryptoSuiteName, SignedVerifiableCredential, VerifiableCredential, verifyCredentialStatus, @@ -13,6 +15,15 @@ import { constants as constantsV5 } from '@tradetrust-tt/token-registry-v5'; import { v4Contracts } from '../token-registry-v4'; import { v5Contracts } from '../token-registry-v5'; import { SUPPORTED_CHAINS } from '../utils'; +import { + BBS_V1_URL, + DATA_INTEGRITY_V2_URL, + QRCODE_CONTEXT_URL, + RENDER_CONTEXT_V2_URL, + TR_CONTEXT_URL, + VC_V1_URL, + VC_V2_URL, +} from '@trustvc/w3c-context'; /** * Configuration for a W3C Verifiable Document using a Bitstring Status List. @@ -55,7 +66,7 @@ export interface RenderMethod { } /** - * Configuration for the qrcoode used in a Verifiable Credential document. + * Configuration for the qrcode used in a Verifiable Credential document. * @property {string} uri - A unique identifier for the qrcode, typically a URL or URI. * @property {string} type - The type of the qrcode method (e.g., 'TrustVCQRCode'). */ @@ -64,8 +75,17 @@ export interface qrCode { type: string; } +/** + * Options for signing a document. + * @property {string[]} mandatoryPointers - The mandatory pointers to be used for signing the document. + */ +export interface SignOptions { + mandatoryPointers?: string[]; +} + /** * Main class responsible for building, configuring, and signing documents with credential statuses. + * This class implements the W3C Verifiable Credentials Data Model 2.0 specification. */ export class DocumentBuilder { private document: Partial; // Holds the document to be built and signed. @@ -75,7 +95,7 @@ export class DocumentBuilder { private rpcProviderUrl: string; // Holds the RPC provider URL for verifying token registry. private requiredFields: string[] = ['credentialSubject']; // Required fields that must be present in the document. private isSigned: boolean = false; // Tracks if a document is signed - + private isDerived: boolean = false; // Tracks if a document is derived /** * Constructor to initialize the document builder. * @param {Partial} input - The input document. @@ -113,17 +133,16 @@ export class DocumentBuilder { tokenRegistry: config.tokenRegistry, }; this.rpcProviderUrl = config.rpcProviderUrl; - this.addContext('https://trustvc.io/context/transferable-records-context.json'); // Add transferable records context to document. + this.addContext(TR_CONTEXT_URL); // Add transferable records context to document. } else if (isVerifiable) { this.selectedStatusType = 'verifiableDocument'; this.statusConfig = { id: `${config.url}#${config.index}`, - type: 'StatusList2021Entry', + type: 'BitstringStatusListEntry', statusPurpose: config.purpose || 'revocation', // Set status purpose to "revocation" by default. statusListIndex: config.index, statusListCredential: config.url, }; - this.addContext('https://w3id.org/vc/status-list/2021/v1'); // Add context for verifiable document status list. } else { throw new Error('Configuration Error: Missing required fields for credential status.'); } @@ -134,7 +153,7 @@ export class DocumentBuilder { // Sets the expiration date of the document. expirationDate(date: string | Date) { if (this.isSigned) throw new Error('Configuration Error: Document is already signed.'); - this.document.expirationDate = typeof date === 'string' ? date : date.toISOString(); + this.document.validUntil = typeof date === 'string' ? date : date.toISOString(); return this; } @@ -142,7 +161,7 @@ export class DocumentBuilder { renderMethod(method: RenderMethod) { if (this.isSigned) throw new Error('Configuration Error: Document is already signed.'); this.document.renderMethod = [method]; - this.addContext('https://trustvc.io/context/render-method-context.json'); // Add render method context to document. + this.addContext(RENDER_CONTEXT_V2_URL); // Add render method context to document. return this; } @@ -150,12 +169,12 @@ export class DocumentBuilder { qrCode(method: qrCode) { if (this.isSigned) throw new Error('Configuration Error: Document is already signed.'); this.document.qrCode = method; - this.addContext('https://trustvc.io/context/qrcode-context.json'); // Add qrcode context to document. + this.addContext(QRCODE_CONTEXT_URL); // Add qrcode context to document. return this; } // Sign the document using the provided private key and an optional cryptographic suite. - async sign(privateKey: PrivateKeyPair, cryptoSuite?: string) { + async sign(privateKey: PrivateKeyPair, cryptoSuite?: CryptoSuiteName, options?: SignOptions) { if (this.isSigned) throw new Error('Configuration Error: Document is already signed.'); if (this.selectedStatusType) { @@ -178,19 +197,42 @@ export class DocumentBuilder { } this.document.issuer = privateKey.id.split('#')[0]; // Set the issuer of the document. - this.document.issuanceDate = this.document.issuanceDate || new Date().toISOString(); // Set the issuance date if not already present. - this.addContext('https://w3id.org/security/bbs/v1'); // Add context for bbs. + this.document.validFrom = this.document.validFrom || new Date().toISOString(); // Set the issuance date if not already present. + if (!cryptoSuite || cryptoSuite === 'ecdsa-sd-2023') { + this.addContext(DATA_INTEGRITY_V2_URL); + } else { + this.addContext(BBS_V1_URL); // Add context for bbs. + } - const signedVC = await signW3C(this.document, privateKey, cryptoSuite); + const signedVC = await signW3C(this.document, privateKey, cryptoSuite, options); if (signedVC.error) throw new Error(`Signing Error: ${signedVC.error}`); this.isSigned = true; return signedVC.signed; } + async derive(revealedAttributes: ContextDocument | string[]) { + if (!this.isSigned) throw new Error('Configuration Error: Document is not signed yet.'); + if (this.isDerived) throw new Error('Configuration Error: Document is already derived.'); + + const derivedCredential = await deriveW3C( + this.document as SignedVerifiableCredential, + revealedAttributes, + ); + if (derivedCredential.error) throw new Error(`Derivation Error: ${derivedCredential.error}`); + this.document = derivedCredential.derived; + this.isDerived = true; + return derivedCredential.derived; + } + // Verify the document. async verify() { if (!this.isSigned) throw new Error('Verification Error: Document is not signed yet.'); + const cryptosuite = (this.document as SignedVerifiableCredential)?.proof?.cryptosuite; + if (cryptosuite === CryptoSuite.EcdsaSd2023 && !this.isDerived) { + throw new Error('Verification Error: Document is not derived yet. Use derive() first.'); + } + const verificationResult = await verifyW3CSignature( this.document as SignedVerifiableCredential, ); @@ -245,10 +287,11 @@ export class DocumentBuilder { // Private helper method to build the context for the document, ensuring uniqueness and adding the default W3C context. private buildContext(context: string | string[]): string[] { - return [ - 'https://www.w3.org/2018/credentials/v1', - ...(Array.isArray(context) ? context : context ? [context] : []), - ].filter((v, i, a) => a.indexOf(v) === i); + const arrayContext = Array.isArray(context) ? context : context ? [context] : []; + if (arrayContext.includes(VC_V1_URL)) { + throw new Error('Document builder does not support data model v1.1.'); + } + return [VC_V2_URL, ...arrayContext].filter((v, i, a) => a.indexOf(v) === i); } // Private helper method to add a new context to the document if it does not already exist. diff --git a/src/open-attestation/utils.ts b/src/open-attestation/utils.ts index 013da86..7071b5d 100644 --- a/src/open-attestation/utils.ts +++ b/src/open-attestation/utils.ts @@ -18,7 +18,6 @@ const { isSignedWrappedV3Document, isRawV2Document, isRawV3Document, - isObfuscated, getDocumentData, getIssuerAddress, diagnose, @@ -41,7 +40,6 @@ export { getTemplateURL, getDocumentData, getIssuerAddress, - isObfuscated, isRawV2Document, isRawV3Document, isSignedWrappedV2Document, diff --git a/src/utils/documents/index.ts b/src/utils/documents/index.ts index 9299b47..4107353 100644 --- a/src/utils/documents/index.ts +++ b/src/utils/documents/index.ts @@ -105,3 +105,36 @@ export const getChainId = ( return undefined; } }; + +export const isObfuscated = ( + document: WrappedDocument | SignedVerifiableCredential, +): boolean => { + if (isWrappedV3Document(document)) { + return !!document.proof.privacy?.obfuscated?.length; + } + + if (isWrappedV2Document(document)) { + return !!document.privacy?.obfuscatedData?.length; + } + + if (isSignedDocument(document)) { + return document.proof?.type === 'BbsBlsSignatureProof2020'; + } + + throw new Error( + 'Unsupported document type: Can only check if there are obfuscated data from wrapped OpenAttestation v2, v3 documents and signed verifiable credentials.', + ); +}; +export const getObfuscatedData = (document: WrappedDocument): string[] => { + if (isWrappedV3Document(document)) { + return document.proof.privacy?.obfuscated; + } + + if (isWrappedV2Document(document)) { + return document.privacy?.obfuscatedData || []; + } + + throw new Error( + 'Unsupported document type: Can only retrieve obfuscated data from wrapped OpenAttestation v2 & v3 documents.', + ); +}; diff --git a/src/verify/fragments/document-integrity/ecdsaW3CSignatureIntegrity.ts b/src/verify/fragments/document-integrity/ecdsaW3CSignatureIntegrity.ts new file mode 100644 index 0000000..78bf157 --- /dev/null +++ b/src/verify/fragments/document-integrity/ecdsaW3CSignatureIntegrity.ts @@ -0,0 +1,94 @@ +import { VerificationFragment, Verifier, VerifierOptions } from '@tradetrust-tt/tt-verify'; +import { SignedVerifiableCredential } from '@trustvc/w3c-vc'; +import { verifyW3CSignature } from '../../../w3c/verify'; +import { deriveCredential } from '@trustvc/w3c-vc'; + +const PROOF_TYPE = 'DataIntegrityProof' as const; +const CRYPTOSUITE = 'ecdsa-sd-2023' as const; +const DERIVE_CREDENTIAL_ERROR = 'Use deriveCredential() first' as const; + +function isSignedVerifiableCredential(document: unknown): document is SignedVerifiableCredential { + return typeof document === 'object' && document !== null && 'proof' in document; +} + +export const ecdsaW3CSignatureIntegrity: Verifier = { + skip: async () => { + return { + type: 'DOCUMENT_INTEGRITY', + name: 'EcdsaW3CSignatureIntegrity', + reason: { + code: 0, + codeString: 'SKIPPED', + message: `Document either has no proof or proof type is not '${PROOF_TYPE}' or proof cryptosuite is not '${CRYPTOSUITE}'.`, + }, + status: 'SKIPPED', + }; + }, + + test: (document: unknown) => { + const doc = document as SignedVerifiableCredential; + return doc.proof?.type === 'DataIntegrityProof' && doc.proof?.cryptosuite === 'ecdsa-sd-2023'; + }, + + verify: async (document: unknown, verifierOptions: VerifierOptions) => { + if (!isSignedVerifiableCredential(document)) { + return { + type: 'DOCUMENT_INTEGRITY', + name: 'EcdsaW3CSignatureIntegrity', + data: false, + reason: { + message: 'Document is not a valid SignedVerifiableCredential', + }, + status: 'INVALID', + }; + } + + try { + let verificationResult = await verifyW3CSignature(document, verifierOptions); + let isDerived = true; + // Handle derivation if needed + if ( + !verificationResult.verified && + verificationResult.error?.includes(DERIVE_CREDENTIAL_ERROR) + ) { + const derivedCredential = await deriveCredential(document, []); + verificationResult = await verifyW3CSignature(derivedCredential.derived, verifierOptions); + isDerived = false; + } + + if (verificationResult.verified) { + return { + type: 'DOCUMENT_INTEGRITY', + name: 'EcdsaW3CSignatureIntegrity', + data: true, + reason: { + message: isDerived + ? 'Document verified successfully' + : 'Document verified after derivation', + }, + status: 'VALID', + }; + } else { + return { + type: 'DOCUMENT_INTEGRITY', + name: 'EcdsaW3CSignatureIntegrity', + data: false, + reason: { + message: verificationResult.error || 'Verification failed', + }, + status: 'INVALID', + }; + } + } catch (error) { + return { + type: 'DOCUMENT_INTEGRITY', + name: 'EcdsaW3CSignatureIntegrity', + data: false, + reason: { + message: error instanceof Error ? error.message : 'Unknown verification error', + }, + status: 'INVALID', + }; + } + }, +}; diff --git a/src/verify/fragments/document-integrity/w3cSignatureIntegrity.ts b/src/verify/fragments/document-integrity/w3cSignatureIntegrity.ts index 22d11c8..077dbcf 100644 --- a/src/verify/fragments/document-integrity/w3cSignatureIntegrity.ts +++ b/src/verify/fragments/document-integrity/w3cSignatureIntegrity.ts @@ -1,5 +1,6 @@ import { VerificationFragment, Verifier, VerifierOptions } from '@tradetrust-tt/tt-verify'; -import { SignedVerifiableCredential, verifyW3CSignature } from '../../..'; +import { SignedVerifiableCredential } from '@trustvc/w3c-vc'; +import { verifyW3CSignature } from '../../../w3c/verify'; export const w3cSignatureIntegrity: Verifier = { skip: async () => { diff --git a/src/verify/fragments/document-status/w3cCredentialStatus.ts b/src/verify/fragments/document-status/w3cCredentialStatus.ts index 69eace2..104f517 100644 --- a/src/verify/fragments/document-status/w3cCredentialStatus.ts +++ b/src/verify/fragments/document-status/w3cCredentialStatus.ts @@ -3,8 +3,12 @@ import { BitstringStatusListCredentialStatus, CredentialStatusType, } from '@trustvc/w3c-credential-status'; -import { CredentialStatus, isSignedDocument, verifyCredentialStatus } from '@trustvc/w3c-vc'; -import { SignedVerifiableCredential } from '../../..'; +import { + CredentialStatus, + isSignedDocument, + verifyCredentialStatus, + SignedVerifiableCredential, +} from '@trustvc/w3c-vc'; //w3cCredentialStatus enums export enum W3CCredentialStatusCode { SKIPPED = 0, diff --git a/src/verify/fragments/index.ts b/src/verify/fragments/index.ts index ebea05d..0de6bbc 100644 --- a/src/verify/fragments/index.ts +++ b/src/verify/fragments/index.ts @@ -8,6 +8,7 @@ import { openAttestationHash, } from '@tradetrust-tt/tt-verify'; import { w3cSignatureIntegrity } from './document-integrity/w3cSignatureIntegrity'; +import { ecdsaW3CSignatureIntegrity } from './document-integrity/ecdsaW3CSignatureIntegrity'; import { credentialStatusTransferableRecordVerifier, TRANSFERABLE_RECORDS_TYPE, @@ -30,4 +31,5 @@ export { w3cCredentialStatus, w3cIssuerIdentity, w3cSignatureIntegrity, + ecdsaW3CSignatureIntegrity, }; diff --git a/src/verify/fragments/issuer-identity/w3cIssuerIdentity.ts b/src/verify/fragments/issuer-identity/w3cIssuerIdentity.ts index 4459411..824516d 100644 --- a/src/verify/fragments/issuer-identity/w3cIssuerIdentity.ts +++ b/src/verify/fragments/issuer-identity/w3cIssuerIdentity.ts @@ -1,7 +1,7 @@ import { VerificationFragment, Verifier, VerifierOptions } from '@tradetrust-tt/tt-verify'; import { DocumentLoader } from '@trustvc/w3c-context'; import { queryDidDocument } from '@trustvc/w3c-issuer'; -import { SignedVerifiableCredential } from '../../..'; +import { SignedVerifiableCredential } from '@trustvc/w3c-vc'; const checkDidWebResolve = async ( did: string, diff --git a/src/verify/verify.ts b/src/verify/verify.ts index ffefca0..69c9a23 100644 --- a/src/verify/verify.ts +++ b/src/verify/verify.ts @@ -31,6 +31,7 @@ import type { VerificationFragmentWithData, } from '@tradetrust-tt/tt-verify/dist/types/src/types/core'; import { w3cSignatureIntegrity } from './fragments/document-integrity/w3cSignatureIntegrity'; +import { ecdsaW3CSignatureIntegrity } from './fragments/document-integrity/ecdsaW3CSignatureIntegrity'; import { credentialStatusTransferableRecordVerifier } from './fragments/document-status/transferableRecords/transferableRecordVerifier'; import { w3cCredentialStatus } from './fragments/document-status/w3cCredentialStatus'; import { w3cIssuerIdentity } from './fragments/issuer-identity/w3cIssuerIdentity'; @@ -64,6 +65,7 @@ const openAttestationVerifiers = [ const w3cVerifiers: Verifier[] = [ w3cSignatureIntegrity, + ecdsaW3CSignatureIntegrity, w3cCredentialStatus, credentialStatusTransferableRecordVerifier, w3cEmptyCredentialStatus, diff --git a/src/w3c/derive.ts b/src/w3c/derive.ts new file mode 100644 index 0000000..0b7db10 --- /dev/null +++ b/src/w3c/derive.ts @@ -0,0 +1,20 @@ +import { + ContextDocument, + deriveCredential, + DerivedResult, + SignedVerifiableCredential, +} from '@trustvc/w3c-vc'; + +/** + * Derives a credential with selective disclosure based on revealed attributes. + * @param {object} credential - The verifiable credential to be selectively disclosed. + * @param {object|string[]} revealedAttributes - For BBS+: The attributes from the credential that should be revealed. For ECDSA-SD-2023: Array of selective pointers. + * @returns {Promise} A DerivedResult containing the derived proof or an error message. + */ + +export const deriveW3C = async ( + credential: SignedVerifiableCredential, + revealedAttributes: ContextDocument | string[], +): Promise => { + return deriveCredential(credential, revealedAttributes); +}; diff --git a/src/w3c/index.ts b/src/w3c/index.ts index f3b5bef..6eb21aa 100644 --- a/src/w3c/index.ts +++ b/src/w3c/index.ts @@ -5,3 +5,4 @@ export * from './sign'; export * from './types'; export * as vc from './vc'; export * from './verify'; +export * from './derive'; diff --git a/src/w3c/sign.ts b/src/w3c/sign.ts index 5fd4b1f..e353bab 100644 --- a/src/w3c/sign.ts +++ b/src/w3c/sign.ts @@ -1,18 +1,23 @@ -import { signCredential } from '@trustvc/w3c-vc'; +import { CryptoSuiteName, signCredential } from '@trustvc/w3c-vc'; import { RawVerifiableCredential, SigningResult, PrivateKeyPair } from './types'; /** * Signs a W3C Verifiable Credential using the provided cryptographic suite and key pair. * @param {RawVerifiableCredential} credential - The verifiable credential object that needs to be signed. * @param {PrivateKeyPair} keyPair - The private and public key pair used for signing the credential. - * @param {string} [cryptoSuite='BbsBlsSignature2020'] - The cryptographic suite to be used for signing (default is 'BbsBlsSignature2020'). + * @param {CryptoSuiteName} [cryptoSuite='ecdsa-sd-2023'] - The cryptographic suite to be used for signing (default is 'ecdsa-sd-2023'). + * @param {object} [options] - Optional parameters including mandatoryPointers for ECDSA-SD-2023. + * @param {string[]} [options.mandatoryPointers] - Optional mandatory pointers for ECDSA-SD-2023. * @returns {Promise} A promise that resolves to the result of the signing operation, which includes the signed credential. */ export const signW3C = async ( credential: RawVerifiableCredential, keyPair: PrivateKeyPair, - cryptoSuite: string = 'BbsBlsSignature2020', + cryptoSuite: CryptoSuiteName = 'ecdsa-sd-2023', + options?: { + mandatoryPointers?: string[]; + }, ): Promise => { // Call the signCredential function from the trustvc/w3c-vc package to sign the credential - return signCredential(credential, keyPair, cryptoSuite); + return signCredential(credential, keyPair, cryptoSuite, options); };