-
Notifications
You must be signed in to change notification settings - Fork 0
Feat/tt 907 encrypt decrypt oa functions #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
6dab3e7
feat: added encrypt/decrypt functions to the OA feature
manishdex25 8fa493a
feat: updated the readme
manishdex25 70f3ea5
feat: added husky for lint checks
manishdex25 661c27e
feat: fixed the prettier formats
manishdex25 7a1e3f9
feature: added encrypt decrypt functionlities of documents
manishdex25 27ef0cf
feat: added the gracefull error handling
manishdex25 eecf1fb
feat: remove example files
manishdex25 a07c6a3
fix: updated the file handling error and folder creation for outputs
manishdex25 7f9712c
feat: updated packages
manishdex25 1d996ce
Merge branches 'feat/TT-907-encrypt-decrypt-oa-functions' and 'main' …
manishdex25 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| #!/usr/bin/env sh | ||
| npm run lint | ||
| npm run format:check |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,7 +8,7 @@ A comprehensive command-line interface for managing W3C Verifiable Credentials, | |
| - ✅ **Key Pair Generation**: Generate cryptographic key pairs with Multikey format | ||
| - ✅ **DID Management**: Create and manage did:web identifiers | ||
| - ✅ **W3C Verifiable Credentials**: Sign, verify and manage W3C verifiable credentials | ||
| - ✅ **OpenAttestation**: Sign, verify and wrap/unwrap OpenAttestation v2/v3 documents | ||
| - ✅ **OpenAttestation**: Sign, verify, wrap/unwrap, and encrypt/decrypt OpenAttestation v2/v3 documents | ||
| - ✅ **Token Registry**: Mint tokens to blockchain-based token registries | ||
| - ✅ **Document Store**: Deploy and manage document store contracts | ||
| - ✅ **Title Escrow**: Complete transferable records management (holder/beneficiary transfers) | ||
|
|
@@ -90,6 +90,12 @@ trustvc oa-wrap | |
|
|
||
| # Unwrap an OpenAttestation document | ||
| trustvc oa-unwrap | ||
|
|
||
| # Encrypt an Open Attestation document for safe sharing | ||
| trustvc oa-encrypt | ||
|
|
||
| # Decrypt an encrypted Open Attestation document | ||
| trustvc oa-decrypt | ||
| ``` | ||
|
|
||
| ### Wallet Management | ||
|
|
@@ -176,6 +182,8 @@ trustvc title-escrow reject-transfer-owner-holder | |
|
|
||
| - **Document Unwrapping**: Uses `unwrapOA` to unwrap OpenAttestation documents. | ||
|
|
||
| - **Document Encryption**: Uses `oa-encrypt` to encrypt OA documents for safe sharing; use `oa-decrypt` with the same key to recover the document. | ||
|
|
||
| ### Blockchain Operations | ||
|
|
||
| - **Token Registry**: Deploy token registry contracts and mint document hashes (tokenIds) to blockchain-based token registries across multiple networks (Ethereum, Polygon, XDC, Stability, Astron). | ||
|
|
@@ -200,7 +208,10 @@ trustvc title-escrow reject-transfer-owner-holder | |
| | | [`verify`](#verify) | Verify OpenAttestation documents | | ||
| | | [`oa-wrap`](#oa-wrap) | Wrap OpenAttestation documents | | ||
| | | [`oa-unwrap`](#oa-unwrap) | Unwrap OpenAttestation documents | | ||
| | **Token Registry** | [`token-registry deploy`](#token-registry-deploy) | Deploy token registry contracts | | ||
| | | [`oa-encrypt`](#oa-encrypt) | Encrypt an OA document for safe sharing and storage | | ||
| | | [`oa-decrypt`](#oa-decrypt) | Decrypt an OA document encrypted with oa-encrypt | | ||
| | **Token Registry** | [`mint`](#mint) | Mint tokens to blockchain registries | | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @RishabhS7 please review the readme once |
||
| | | [`token-registry deploy`](#token-registry-deploy) | Deploy token registry contracts | | ||
| | | [`mint`](#mint) | Mint tokens to blockchain registries | | ||
| | | `token-registry mint` | Alternative: `mint` | | ||
| | **Document Store** | [`document-store deploy`](#document-store-deploy) | Deploy document store contracts | | ||
|
|
@@ -479,6 +490,57 @@ Unwrapped OpenAttestation document(s) in the specified directory. | |
|
|
||
| </details> | ||
|
|
||
| <details> | ||
| <summary><h4 id="oa-encrypt">oa-encrypt</h4></summary> | ||
|
|
||
| Encrypts an Open Attestation document for safe sharing and storage. You will be prompted for an encryption key — remember it to decrypt later. | ||
|
|
||
| **Usage:** | ||
|
|
||
| ```sh | ||
| trustvc oa-encrypt | ||
| ``` | ||
|
|
||
| **Interactive Prompts:** | ||
|
|
||
| - Path to your Open Attestation document (raw or wrapped OA v2/v3) | ||
| - Path for the output encrypted file | ||
| - Encryption key (entered securely; not echoed) | ||
|
|
||
| **Output:** | ||
|
|
||
| Writes an encrypted document file containing `type: "encrypted-document"` and a `ciphertext` field. Only someone with the same key can decrypt it with `oa-decrypt`. | ||
|
|
||
| **Supported Input:** | ||
|
|
||
| - OpenAttestation v2 (raw or wrapped) | ||
| - OpenAttestation v3 (raw or wrapped) | ||
|
|
||
| </details> | ||
|
|
||
| <details> | ||
| <summary><h4 id="oa-decrypt">oa-decrypt</h4></summary> | ||
|
|
||
| Decrypts an Open Attestation document that was encrypted using `oa-encrypt`. You will be prompted for the decryption key. | ||
|
|
||
| **Usage:** | ||
|
|
||
| ```sh | ||
| trustvc oa-decrypt | ||
| ``` | ||
|
|
||
| **Interactive Prompts:** | ||
|
|
||
| - Path to the encrypted document file | ||
| - Path for the output decrypted document | ||
| - Decryption key (entered securely; not echoed) | ||
|
|
||
| **Output:** | ||
|
|
||
| Writes the decrypted Open Attestation document (raw OA v2/v3 or wrapped OA v2/v3) to the specified path. Fails if the key is wrong or the file is not a valid encrypted OA document. | ||
|
|
||
| </details> | ||
|
|
||
| <details> | ||
| <summary><h4 id="mint">mint</h4></summary> | ||
|
|
||
|
|
@@ -1158,9 +1220,11 @@ npm test | |
| ``` | ||
| src/commands/ | ||
| ├── oa/ | ||
| │ └── sign.ts # Sign OpenAttestation documents | ||
| │ └── wrap.ts # Wrap OpenAttestation documents | ||
| │ └── unwrap.ts # Unwrap OpenAttestation documents | ||
| │ ├── sign.ts # Sign OpenAttestation documents | ||
| │ ├── wrap.ts # Wrap OpenAttestation documents | ||
| │ ├── unwrap.ts # Unwrap OpenAttestation documents | ||
| │ ├── encrypt.ts # Encrypt OA documents for safe sharing | ||
| │ └── decrypt.ts # Decrypt OA documents | ||
| ├── token-registry/ | ||
| │ ├── deploy.ts # Deploy token registry contracts | ||
| │ └── mint.ts # Mint tokens to registry | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| import fs from 'fs'; | ||
| import { input, password } from '@inquirer/prompts'; | ||
| import crypto from 'crypto'; | ||
| import signale from 'signale'; | ||
| import { decryptString } from '@trustvc/trustvc'; | ||
| import { | ||
| readDocumentFile, | ||
| getCliErrorMessage, | ||
| isErrorWithMessage, | ||
| ensureInputFileExists, | ||
| resolveOutputJsonPath, | ||
| validateInputFileExists, | ||
| } from '../../utils'; | ||
|
|
||
| /** Derive a 64-char hex key from passphrase for AES-256 (OPEN-ATTESTATION-TYPE-1). */ | ||
| const deriveKey = (passphrase: string): string => | ||
| crypto.createHash('sha256').update(passphrase, 'utf8').digest('hex'); | ||
|
|
||
| export const command = 'oa-decrypt'; | ||
| export const describe = | ||
| 'Decrypt a document that was encrypted using oa-encrypt. You will be asked for the decryption key.'; | ||
|
|
||
| type DecryptInput = { | ||
| inputEncryptedPath: string; | ||
| outputPath: string; | ||
| key: string; | ||
| }; | ||
|
|
||
| // Payload format: OPEN-ATTESTATION-TYPE-1 (cipherText, iv, tag, type) | ||
| const ENCRYPTED_DOCUMENT_TYPE = 'OPEN-ATTESTATION-TYPE-1'; | ||
|
|
||
| /** Message thrown by @trustvc/trustvc when decryption fails (wrong key or corrupted data). */ | ||
| const DECRYPT_FAILED_LIBRARY_MESSAGE = 'Error decrypting message'; | ||
|
|
||
| const INVALID_PAYLOAD_MESSAGE = | ||
| 'Invalid encrypted document: expected cipherText, iv, tag and type "OPEN-ATTESTATION-TYPE-1".'; | ||
|
|
||
| export const promptForInputs = async (): Promise<DecryptInput | null> => { | ||
| const inputEncryptedPath = await input({ | ||
| message: 'Enter the path to the encrypted document:', | ||
| required: true, | ||
| validate: (value: string) => { | ||
| if (!value || value.trim() === '') return 'Encrypted document path is required'; | ||
| return validateInputFileExists(value); | ||
| }, | ||
| }); | ||
|
|
||
| const outputPath = await input({ | ||
| message: 'Enter the path to save the decrypted document:', | ||
| required: true, | ||
| validate: (value: string) => { | ||
| if (!value || value.trim() === '') return 'Output path is required'; | ||
| return true; | ||
| }, | ||
| }); | ||
|
|
||
| const key = await password({ | ||
| message: 'Enter the decryption key:', | ||
| mask: '*', | ||
| validate: (value: string) => { | ||
| if (!value || value.trim() === '') return 'Decryption key is required'; | ||
| return true; | ||
| }, | ||
| }); | ||
|
|
||
| return { | ||
| inputEncryptedPath: inputEncryptedPath.trim(), | ||
| outputPath: outputPath.trim(), | ||
| key: key.trim(), | ||
| }; | ||
| }; | ||
|
|
||
| type EncryptedPayload = { | ||
| cipherText: string; | ||
| iv: string; | ||
| tag: string; | ||
| type: string; | ||
| }; | ||
|
|
||
| const DECRYPT_ERROR_OPTIONS = { | ||
| defaultMessage: 'An unexpected error occurred while decrypting the document.', | ||
| fileNotFound: 'Unable to read encrypted document. File not found at: {path}', | ||
| permissionDenied: 'Permission denied. Cannot write to: {path}', | ||
| invalidJson: (msg: string) => `Invalid encrypted file: the file is not valid JSON. ${msg}`, | ||
| } as const; | ||
|
|
||
| /** Validates raw payload and returns typed fields or throws with a clear message. */ | ||
| function validateEncryptedPayload(payload: unknown): EncryptedPayload { | ||
| if ( | ||
| payload === null || | ||
| typeof payload !== 'object' || | ||
| !('cipherText' in payload) || | ||
| !('iv' in payload) || | ||
| !('tag' in payload) || | ||
| !('type' in payload) | ||
| ) { | ||
| throw new Error(INVALID_PAYLOAD_MESSAGE); | ||
| } | ||
| const { cipherText, iv, tag, type } = payload as EncryptedPayload; | ||
| if ( | ||
| typeof cipherText !== 'string' || | ||
| typeof iv !== 'string' || | ||
| typeof tag !== 'string' || | ||
| type !== ENCRYPTED_DOCUMENT_TYPE | ||
| ) { | ||
| throw new Error(INVALID_PAYLOAD_MESSAGE); | ||
| } | ||
| return { cipherText, iv, tag, type }; | ||
| } | ||
|
|
||
| /** Decrypts payload with derived key; rethrows a user-friendly error on library failure. */ | ||
| function decryptPayload(payload: EncryptedPayload, key: string): string { | ||
| try { | ||
| return decryptString({ | ||
| ...payload, | ||
| key: deriveKey(key), | ||
| }); | ||
| } catch (err: unknown) { | ||
| if (isErrorWithMessage(err) && err.message === DECRYPT_FAILED_LIBRARY_MESSAGE) { | ||
| throw new Error( | ||
| 'Failed to decrypt document. The password/key is likely incorrect or the file is corrupted.', | ||
| ); | ||
| } | ||
| throw err; | ||
| } | ||
| } | ||
|
|
||
| /** Loads encrypted file, validates, decrypts, writes plaintext and shows success message. */ | ||
| async function runDecrypt(answers: DecryptInput): Promise<void> { | ||
| const { inputEncryptedPath, outputPath, key } = answers; | ||
|
|
||
| ensureInputFileExists(inputEncryptedPath); | ||
| const rawPayload = readDocumentFile(inputEncryptedPath); | ||
| const payload = validateEncryptedPayload(rawPayload); | ||
| const documentString = decryptPayload(payload, key); | ||
|
|
||
| const { path: outputFilePath, generated } = resolveOutputJsonPath(outputPath, 'decrypted'); | ||
| fs.writeFileSync(outputFilePath, documentString, 'utf8'); | ||
| if (generated) { | ||
| signale.success(`No output filename provided. Decrypted document saved to: ${outputFilePath}`); | ||
| } else { | ||
| signale.success(`Decrypted document saved to: ${outputFilePath}`); | ||
| } | ||
| } | ||
|
|
||
| export const handler = async (): Promise<void> => { | ||
| try { | ||
| const answers = await promptForInputs(); | ||
| if (!answers) return; | ||
| await runDecrypt(answers); | ||
| } catch (err: unknown) { | ||
| signale.error(getCliErrorMessage(err, DECRYPT_ERROR_OPTIONS)); | ||
| process.exitCode = 1; | ||
| } | ||
| }; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.