Skip to content

Commit ea31015

Browse files
authored
feat: add w3c sign function (#4)
* implement w3c-sign * add unit tests * update unit tests * update unit tests * revert package.json * revert package.json * update readme * address comments (reorder prompt, add default path and success message)
1 parent 1c72bd4 commit ea31015

7 files changed

Lines changed: 571 additions & 2 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
# Sample files for testing
2+
fixtures
3+
14
# Generated files from CLI commands
25
keypair.json
36
didKeyPairs.json
47
wellknown.json
58
credentialStatus.json
9+
signed_vc.json
610

711
# Dependencies
812
node_modules/

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ A command-line interface tool for working with Decentralized Identifiers (DIDs),
77
-**Modern Cryptosuites**: Full support for ECDSA-SD-2023 and BBS-2023
88
-**Key Pair Generation**: Generate cryptographic key pairs with Multikey format
99
-**DID Management**: Create and manage did:web identifiers
10+
-**Sign Verifiable Credentials**: Sign verifiable credentials
1011
-**Token Registry**: Mint tokens to blockchain-based token registries
1112
-**Credential Status**: Create and update W3C credential status lists
1213
-**W3C Standards**: Compliant with latest W3C DID and Verifiable Credentials specifications
@@ -26,6 +27,7 @@ This CLI leverages the TrustVC package:
2627
- [Commands](#commands)
2728
- [`trustvc key-pair-generation`](#trustvc-key-pair-generation)
2829
- [`trustvc did-web`](#trustvc-did-web)
30+
- [`trustvc w3c-sign`](#trustvc-w3c-sign)
2931
- [`trustvc credential-status-create`](#trustvc-credential-status-create)
3032
- [`trustvc credential-status-update`](#trustvc-credential-status-update)
3133
- [`trustvc mint`](#trustvc-mint)
@@ -56,6 +58,9 @@ trustvc key-pair-generation
5658
# Create a DID from the key pair
5759
trustvc did-web
5860

61+
# Sign a verifiable credential
62+
trustvc w3c-sign
63+
5964
# Create a credential status list
6065
trustvc credential-status-create
6166

@@ -72,6 +77,8 @@ trustvc mint
7277

7378
- **Generating Well-Known DID**: The CLI uses the `issueDID` function from `@trustvc/trustvc` to generate a did:web identifier. This allows users to self-host their DID as a unique identifier in decentralized systems.
7479

80+
- **Sign Verifiable Credentials**: The CLI uses the `w3cSign` function from `@trustvc/trustvc` to sign verifiable credentials with the provided did:web identifier.
81+
7582
- **Credential Status Management**: The CLI provides commands to create and update W3C credential status lists for managing the revocation status of verifiable credentials.
7683

7784
- **Token Registry Minting**: The CLI uses the `mint` function from `@trustvc/trustvc` to mint document hashes to blockchain-based token registries, supporting multiple networks including Ethereum, Polygon, XDC, Stability, and Astron.
@@ -117,6 +124,23 @@ Generates a did:web identifier from an existing key pair. Supports modern Multik
117124
trustvc did-web
118125
```
119126

127+
### `trustvc w3c-sign`
128+
129+
Signs a verifiable credential using a did:web identifier.
130+
131+
**Interactive prompts:**
132+
- Path to did:web key-pair JSON file
133+
- Path to unsigned verifiable credential JSON file
134+
- Select cryptosuite (ECDSA-SD-2023 or BBS-2023, must match the key pair)
135+
- Output directory
136+
137+
**Output:** Creates a signed verifiable credential file: `signed_vc.json`.
138+
139+
**Example:**
140+
```sh
141+
trustvc w3c-sign
142+
```
143+
120144
### `trustvc credential-status-create`
121145

122146
Creates a new W3C credential status list for managing the revocation status of verifiable credentials.
@@ -255,6 +279,7 @@ npm test
255279
│ │ └── w3c/
256280
│ │ ├── did.ts # DID generation command
257281
│ │ ├── key-pair.ts # Key pair generation command
282+
│ │ ├── sign.ts # Sign verifiable credential command
258283
│ │ └── credentialStatus/
259284
│ │ ├── create.ts # Create credential status
260285
│ │ └── update.ts # Update credential status
@@ -272,6 +297,7 @@ npm test
272297
│ │ └── w3c/
273298
│ │ ├── did.test.ts
274299
│ │ ├── key-pair.test.ts
300+
│ │ ├── sign.test.ts
275301
│ │ └── credentialStatus/
276302
│ │ ├── create.test.ts
277303
│ │ └── update.test.ts

package-lock.json

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/commands/w3c/sign.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { input, select } from '@inquirer/prompts';
2+
import { isDirectoryValid, readJsonFile, writeFile } from '../../utils';
3+
import { issuer, RawVerifiableCredential, signW3C } from '@trustvc/trustvc';
4+
import { SignInput } from '../../types';
5+
import signale from 'signale';
6+
7+
export const command = 'w3c-sign';
8+
export const describe = 'Sign a Verifiable Credential using a did key-pair file';
9+
10+
export const handler = async () => {
11+
try {
12+
const answers = await promptForInputs();
13+
if (!answers) return;
14+
15+
await sign(answers);
16+
} catch (err: unknown) {
17+
signale.error(`${err instanceof Error ? err.message : String(err)}`);
18+
}
19+
};
20+
21+
export const promptForInputs = async (): Promise<SignInput> => {
22+
const pathToCredentialFile = await input({
23+
message: 'Please enter the path to your Verifiable Credential JSON file:',
24+
required: true,
25+
validate: (value: string) => {
26+
if (!value || value.trim() === '') {
27+
return 'Verifiable Credential JSON file path is required';
28+
}
29+
return true;
30+
},
31+
});
32+
const credential: RawVerifiableCredential = readJsonFile(pathToCredentialFile, 'Verifiable Credential JSON');
33+
34+
const pathToKeypairFile = await input({
35+
message: 'Please enter the path to your did key-pair JSON file:',
36+
required: true,
37+
default: './didKeyPairs.json',
38+
validate: (value: string) => {
39+
if (!value || value.trim() === '') {
40+
return 'did key-pair JSON file path is required';
41+
}
42+
return true;
43+
},
44+
});
45+
const keyPairData: typeof issuer.IssuedDIDOption = readJsonFile(pathToKeypairFile, 'key pair');
46+
47+
const encryptionAlgorithm = await select({
48+
message: 'Select the encryption algorithm used to generate the key pair:',
49+
choices: [
50+
{ name: 'ECDSA-SD-2023', value: 'ecdsa-sd-2023', description: 'Sign credential using ECDSA-SD-2023 suite', },
51+
{ name: 'BBS-2023', value: 'bbs-2023', description: 'Sign credential using BBS-2023 suite', },
52+
],
53+
default: 'ECDSA-SD-2023',
54+
});
55+
56+
const pathToSignedVC = await input({
57+
message: 'Enter a directory to save the signed Verifiable Credential (optional):',
58+
required: false,
59+
default: '.',
60+
});
61+
62+
if (!isDirectoryValid(pathToSignedVC)) throw new Error('Output path is not valid');
63+
64+
return {
65+
credential,
66+
keyPairData,
67+
encryptionAlgorithm,
68+
pathToSignedVC,
69+
};
70+
};
71+
72+
export const sign = async ({
73+
credential,
74+
keyPairData,
75+
encryptionAlgorithm,
76+
pathToSignedVC,
77+
}: SignInput): Promise<void> => {
78+
const signedVC = await signW3C(credential, keyPairData, encryptionAlgorithm);
79+
if (signedVC?.signed) {
80+
signale.success('Verifiable Credential signed successfully');
81+
const signedVCPath = `${pathToSignedVC}/signed_vc.json`;
82+
writeFile(signedVCPath, signedVC.signed, true);
83+
signale.success(`Signed verifiable credential saved to: ${signedVCPath}`);
84+
} else {
85+
signale.error(signedVC.error);
86+
}
87+
};

src/types.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
import { issuer, credentialStatus } from '@trustvc/trustvc';
1+
import { credentialStatus, issuer, RawVerifiableCredential } from '@trustvc/trustvc';
22
import { GasOption, NetworkOption, RpcUrlOption, WalletOrSignerOption } from './utils';
33

4+
export type SignInput = {
5+
credential: RawVerifiableCredential;
6+
keyPairData: typeof issuer.IssuedDIDOption;
7+
encryptionAlgorithm: typeof credentialStatus.cryptoSuiteName;
8+
pathToSignedVC: string;
9+
}
410
export type DidInput = {
511
keyPairPath: string;
612
domainName: string;

0 commit comments

Comments
 (0)