UNPKG

@originvault/ov-id-sdk

Version:

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

147 lines • 6.31 kB
import crypto from 'crypto'; import fs from 'fs'; import path from 'path'; import ignore from 'ignore'; // npm install ignore import tar from 'tar-stream'; // npm install tar-stream import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); function getIgnoreFilter(packagePath) { const npmIgnoreFile = path.join(packagePath, ".npmignore"); const gitIgnoreFile = path.join(packagePath, ".gitignore"); let ignoreRules = []; if (fs.existsSync(npmIgnoreFile)) { ignoreRules = fs.readFileSync(npmIgnoreFile, "utf-8").split("\n").map(line => line.trim()).filter(Boolean); } else if (fs.existsSync(gitIgnoreFile)) { ignoreRules = fs.readFileSync(gitIgnoreFile, "utf-8").split("\n").map(line => line.trim()).filter(Boolean); } return ignore().add(ignoreRules); } /** * Collects all installed files while respecting .npmignore and .gitignore. * @param {string} packagePath - The package directory. * @returns {Set<string>} - Set of all included files. */ function getInstalledFiles(packagePath) { const ignoreFilter = getIgnoreFilter(packagePath); let installedFiles = new Set(); function walkDir(dir) { fs.readdirSync(dir).forEach((file) => { const fullPath = path.join(dir, file); const relativePath = path.relative(packagePath, fullPath); if (relativePath === "" || ignoreFilter.ignores(relativePath)) return; if (fs.statSync(fullPath).isDirectory()) { walkDir(fullPath); } else { installedFiles.add(relativePath); } }); } walkDir(packagePath); return installedFiles; } /** * Compares the computed bundle hash with the one stored in package.json. * @param {string} packagePath - The package directory. */ async function verifyBundleHash(packagePath) { const packageJsonPath = path.join(packagePath, "package.json"); if (!fs.existsSync(packageJsonPath)) { console.error(`āŒ package.json not found in ${packagePath}`); return; } const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")); if (!packageJson.bundleHash) { console.error(`⚠ No "bundleHash" found in ${packagePath}/package.json`); return; } console.log(`\nšŸ” Verifying package integrity for ${packageJson.name}...`); const computedHash = await createBundleHash(packagePath); const expectedHash = packageJson.bundleHash; if (computedHash === expectedHash) { console.log(`āœ… Integrity verified! Hash matches: ${computedHash}`); } else { console.error(`āŒ Integrity check failed!`); console.error(` Expected: ${expectedHash}`); console.error(` Computed: ${computedHash}`); process.exit(1); // Exit process on failure } } /** * Creates a SHA-256 hash of the installed package files for DID derivation. * @param {string} packagePath - The package directory. * @returns {Promise<string>} - SHA-256 hash of the package. */ async function createBundleHash(packagePath) { const files = [...getInstalledFiles(packagePath)]; const pack = tar.pack(); // Use tar-stream for in-memory tarball const hash = crypto.createHash("sha256"); // Hash tarball content return new Promise(async (resolve, reject) => { try { // Pipe tar pack to the hash stream const tarStream = pack.pipe(hash); // Add files to the tar pack for (const file of files) { const fullPath = path.join(packagePath, file); const content = fs.readFileSync(fullPath); // Read as Buffer for both binary & text pack.entry({ name: file, mode: 0o644, // Set consistent file permissions mtime: new Date(0), // Normalize timestamps uid: 0, gid: 0, }, content); } pack.finalize(); // Finalize the tar stream // Wait for the tar stream to finish hashing tarStream.on("finish", () => resolve({ hash: hash.digest("hex"), files: files })); tarStream.on("error", reject); } catch (error) { reject(error); } }); } function getPrivateKeyFromBundleHash(bundleHash) { const hashBuffer = crypto.createHash('sha256').update(bundleHash).digest(); return new Uint8Array(hashBuffer.subarray(0, 32)); } async function getPrivateKeyFromBundle(packagePath) { const bundleHash = await createBundleHash(packagePath); return getPrivateKeyFromBundleHash(bundleHash.hash); } async function getSelfBundleHash() { const packagePath = path.resolve(__dirname, '..'); const bundleHash = await createBundleHash(`${packagePath}`); return bundleHash; } async function getSelfBundlePrivateKey() { const bundleHash = await getSelfBundleHash(); return { key: getPrivateKeyFromBundleHash(bundleHash.hash), hash: bundleHash.hash, files: bundleHash.files }; } async function getPackageDIDFromPackageJson() { const packageJsonPath = path.join(__dirname, '..', 'package.json'); const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); return packageJson.did; } async function getParentBundleHash() { const packagePath = path.resolve(process.cwd()); const bundleHash = await createBundleHash(`${packagePath}`); return bundleHash; } async function getParentBundlePrivateKey() { const bundleHash = await getParentBundleHash(); return { key: getPrivateKeyFromBundleHash(bundleHash.hash), hash: bundleHash.hash, files: bundleHash.files }; } async function getParentDIDFromPackageJson() { const packageJsonPath = path.join(process.cwd(), 'package.json'); // Use current working directory const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); return packageJson.did; } export { verifyBundleHash, getPrivateKeyFromBundleHash, getPrivateKeyFromBundle, getSelfBundleHash, getSelfBundlePrivateKey, getPackageDIDFromPackageJson, getParentDIDFromPackageJson, getParentBundleHash, getParentBundlePrivateKey }; //# sourceMappingURL=packageManager.js.map