UNPKG

@originvault/ov-id-sdk

Version:

A TypeScript SDK for managing decentralized identities (DIDs) and verifiable credentials (VCs)

291 lines 10.8 kB
import { Keyring } from '@polkadot/keyring'; import { cryptoWaitReady } from '@polkadot/util-crypto'; import os from 'os'; import path from 'path'; import * as ed25519 from '@noble/ed25519'; import { sha512 } from '@noble/hashes/sha2'; // Ensure correct import import dotenv from 'dotenv'; import fs from 'fs'; import { convertPrivateKeyToRecovery, decryptPrivateKey } from './encryption.js'; import inquirer from 'inquirer'; dotenv.config(); ed25519.etc.sha512Sync = sha512; // Initialize keyring let keyring; export const KEYRING_FILE = path.join(os.homedir(), '.originvault-cheqd-did-keyring.json'); // Define the path for the encryption key file const keyStore = { encryptionKeyFilePath: path.join(os.homedir(), '.originvault-encryption-key'), privateEncryptionKey: process.env.ENCRYPTION_KEY || 'admin-key', }; async function initializeEncryptionKey() { try { const keyPath = path.join(os.homedir(), '.originvault-encryption-key'); if (!fs.existsSync(keyPath)) { if (process.env.ENCRYPTION_KEY) { keyStore.privateEncryptionKey = process.env.ENCRYPTION_KEY; keyStore.encryptionKeyFilePath = keyPath; return; } const { encryptionKey: inputKey } = await inquirer.prompt([ { type: 'password', name: 'encryptionKey', message: 'Enter an encryption key to encrypt the password:', mask: '*', }, ]); // Store the encryption key in the file fs.writeFileSync(keyPath, JSON.stringify({ key: inputKey }), 'utf8'); keyStore.privateEncryptionKey = inputKey; keyStore.encryptionKeyFilePath = keyPath; } else { const { key } = JSON.parse(fs.readFileSync(keyPath, 'utf8')); keyStore.privateEncryptionKey = key; } } catch (error) { console.error("❌ Error initializing encryption key:", error); throw error; } } export async function getEncryptionKey() { await initializeEncryptionKey(); return keyStore.privateEncryptionKey; } export async function storeEncryptionKey(key) { await initializeEncryptionKey(); keyStore.privateEncryptionKey = key; fs.writeFileSync(keyStore.encryptionKeyFilePath, JSON.stringify({ key })); } // Ensure the keyring is initialized export async function ensureKeyring() { await initializeEncryptionKey(); if (!keyring) { await cryptoWaitReady(); keyring = new Keyring({ type: 'ed25519' }); if (fs.existsSync(KEYRING_FILE)) { const keys = JSON.parse(fs.readFileSync(KEYRING_FILE, 'utf8')); keys?.forEach((key) => { keyring?.addFromJson(key); }); } } return keyring; } // Exported functions export const getVerifiedAuthentication = async (did, agent, kid) => { if (!agent) { throw new Error("Agent not found"); } let resolvedDid = await agent?.resolveDid({ didUrl: did }); if (!resolvedDid || resolvedDid.didResolutionMetadata?.error) { console.error("❌ DID could not be resolved", did, resolvedDid); return null; } const didDoc = resolvedDid.didDocument; const authentication = kid ? didDoc?.authentication?.find(auth => { if (typeof auth === 'string') { return auth === kid; } return auth.id === kid; }) : didDoc?.authentication?.[0]; if (!authentication) { console.error("❌ No authentication found for DID", did); return null; } const verificationMethods = didDoc?.verificationMethod; if (!verificationMethods) { console.error("❌ No verification method found for DID", did); return null; } const verifiedAuthentication = verificationMethods.find(method => method.id === authentication); if (!verifiedAuthentication) { console.error("❌ Could not find verification method for standard did authentication", did); return null; } return verifiedAuthentication; }; export const getPublicKeyMultibase = async (did, agent) => { const verifiedAuthentication = await getVerifiedAuthentication(did, agent); if (!verifiedAuthentication) { return undefined; } const publicKeyMultibase = verifiedAuthentication.publicKeyMultibase; return publicKeyMultibase; }; export async function getPrivateKeyForPrimaryDID(password) { await ensureKeyring(); const storedData = fs.readFileSync(KEYRING_FILE, 'utf8'); const { encryptedPrivateKey } = JSON.parse(storedData); if (!encryptedPrivateKey) return false; const privateKey = decryptPrivateKey(encryptedPrivateKey, password); if (!privateKey) { console.error("❌ Failed to decrypt private key"); return false; } return privateKey; } export async function storePrivateKey(keyName, privateKey, kid) { try { // Check the length of the private key if (privateKey.length === 64) { privateKey = privateKey.slice(0, 32); // Use only the first 32 bytes } else if (privateKey.length !== 32) { throw new Error("Invalid private key length. Expected 32 bytes or 64 bytes."); } const kr = await ensureKeyring(); const pair = kr.addFromSeed(privateKey, { keyName, isPrimary: false, kid }); kr.addPair(pair); fs.writeFileSync(KEYRING_FILE, JSON.stringify(kr.getPairs().map(pair => pair.toJson()), null, 2)); // Check if the private key is stored correctly const storedData = JSON.parse(fs.readFileSync(KEYRING_FILE, 'utf8')); const isKeyStored = storedData.some((pair) => pair.address === pair.address); // Adjust this condition as needed if (!isKeyStored) { console.error("❌ Private Key not found in the keyring file."); } } catch (error) { console.error("❌ Error storing private key:", error); throw error; } } export async function retrievePrivateKey(keyName) { try { const kr = await ensureKeyring(); const pairs = kr.getPairs().map(pair => pair.toJson()); const pair = pairs.find(p => p.meta.keyName === keyName); return kr.decodeAddress(pair?.address); } catch (error) { console.error("❌ Error retrieving private key:", error); return undefined; } } export async function retrieveKeys(keyName) { const kr = await ensureKeyring(); const pairs = kr.getPairs().map(pair => pair.toJson()); const pair = pairs.find(p => p.meta.keyName === keyName); return { keyName: pair?.meta.keyName, isPrimary: pair?.meta.isPrimary, kid: pair?.meta.kid }; } export async function retrieveMnemonicForDID(did) { const privateKey = await retrievePrivateKey(did); if (!privateKey) { return undefined; } const mnemonic = await convertPrivateKeyToRecovery(Buffer.from(privateKey).toString('hex')); return mnemonic; } export async function listAllKeys() { try { const kr = await ensureKeyring(); const pairs = kr.getPairs().map(pair => pair.toJson()); return pairs.map(pair => ({ did: pair.meta.keyName, privateKey: pair.address, isPrimary: pair.meta.isPrimary, kid: pair.meta.kid })); } catch (error) { console.error("❌ Error listing keys:", error); return []; } } export async function deleteKey(did) { try { const kr = await ensureKeyring(); const pairs = kr.getPairs().map(pair => pair.toJson()); const pair = pairs.find(p => p.meta.did === did); if (pair) { kr.removePair(pair.address); return true; } return false; } catch (error) { console.error("❌ Error deleting key:", error); return false; } } export function base64ToHex(base64) { // Decode the Base64 string to a byte array const binaryString = atob(base64); // atob decodes a Base64 string const byteArray = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) { byteArray[i] = binaryString.charCodeAt(i); } // Convert the byte array to a hexadecimal string let hexString = ''; byteArray.forEach(byte => { const hex = byte.toString(16).padStart(2, '0'); // Convert to hex and pad with zero if needed hexString += hex; }); return hexString; } export function setPrimaryVc(signedVC) { // Log the signed VC console.log("Setting primary VC:", signedVC); // Example: Store the signed VC in a file const vcFilePath = path.join(os.homedir(), 'primary-vc.json'); try { fs.writeFileSync(vcFilePath, JSON.stringify(signedVC, null, 2)); console.log("✅ Primary VC stored successfully at", vcFilePath); } catch (error) { console.error("❌ Error storing primary VC:", error); } } export async function getPrimaryVC() { try { const storedData = fs.readFileSync(KEYRING_FILE, 'utf8'); const { meta } = JSON.parse(storedData); if (meta && meta.credential) { return meta.credential; // Return the signed VC } console.error("❌ No primary VC found in keyring."); return null; } catch (error) { console.error("❌ Error accessing keyring:", error); return null; } } export function getStoredPassword() { ensurePasswordFileExists(); const passwordFilePath = path.join(os.homedir(), '.encrypted-password'); const encryptedPassword = JSON.parse(fs.readFileSync(passwordFilePath, 'utf8').trim()); if (!encryptedPassword.iv) { return encryptedPassword; } try { return decryptPrivateKey(encryptedPassword, process.env.ENCRYPTION_KEY || ''); } catch (error) { console.error(`❌ Error retrieving stored password: ${error}`); return null; } } export function hexToBase64(hex) { // Convert hex string to byte array const byteArray = new Uint8Array(hex.match(/.{1,2}/g).map(byte => parseInt(byte, 16))); // Convert byte array to Base64 string const binaryString = String.fromCharCode(...byteArray); return btoa(binaryString); // btoa encodes a binary string to Base64 } // ✅ Ensure the password file exists function ensurePasswordFileExists() { const passwordFilePath = path.join(os.homedir(), '.encrypted-password'); console.log("Getting stored password", passwordFilePath); if (!fs.existsSync(passwordFilePath)) { fs.writeFileSync(passwordFilePath, JSON.stringify("")); // Create an empty password file } } //# sourceMappingURL=storePrivateKeys.js.map