UNPKG

@agnostack/verifyd

Version:

Please contact agnoStack via info@agnostack.com for any questions

348 lines 15 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.WebCrypto = void 0; const browser_monads_ts_1 = require("browser-monads-ts"); const display_1 = require("./display"); class WebCrypto { constructor({ crypto: _crypto, util: _util } = {}) { this._crypto = _crypto !== null && _crypto !== void 0 ? _crypto : {}; this._util = _util !== null && _util !== void 0 ? _util : {}; } get subtle() { var _a; return (_a = this._crypto) === null || _a === void 0 ? void 0 : _a.subtle; } getWebCrypto() { return __awaiter(this, void 0, void 0, function* () { var _a, _b; if (!((_a = this._crypto) === null || _a === void 0 ? void 0 : _a.subtle)) { try { this._crypto = (yield Promise.resolve().then(() => __importStar(require('isomorphic-webcrypto')))).default; } catch (_ignore) { console.info('Failed to import isomorphic-webcrypto, retrying w/ node crypto'); try { this._crypto = (yield Promise.resolve().then(() => __importStar(require('crypto')))).default; } catch (error) { // eslint-disable-next-line max-len console.error(`Failed to import node crypto, ensure 'isomorphic-webcrypto' (or node 'crypto') is installed and/or pass in implementation via 'new WebCrypto({ crypto })'`); throw error; } } } if (!((_b = this._crypto) === null || _b === void 0 ? void 0 : _b.subtle)) { throw new Error('Invalid crypto, missing subtle'); } return this._crypto; }); } getTextDecoder() { return __awaiter(this, void 0, void 0, function* () { var _a; if ((_a = this._util) === null || _a === void 0 ? void 0 : _a.TextDecoder) { return this._util.TextDecoder; } if ((0, browser_monads_ts_1.exists)(browser_monads_ts_1.window) && typeof (browser_monads_ts_1.window === null || browser_monads_ts_1.window === void 0 ? void 0 : browser_monads_ts_1.window.TextDecoder) === 'function') { return browser_monads_ts_1.window.TextDecoder; } try { const TextDecoder = (yield Promise.resolve().then(() => __importStar(require('util')))).TextDecoder; this._util.TextDecoder = TextDecoder; return TextDecoder; } catch (error) { console.error(`Failed to import 'utils.TextDecoder', ensure 'util' is available and/or pass in implementation via 'new WebCrypto({ util })'`); throw error; } }); } getTextEncoder() { return __awaiter(this, void 0, void 0, function* () { var _a; if ((_a = this._util) === null || _a === void 0 ? void 0 : _a.TextEncoder) { return this._util.TextEncoder; } if ((0, browser_monads_ts_1.exists)(browser_monads_ts_1.window) && typeof (browser_monads_ts_1.window === null || browser_monads_ts_1.window === void 0 ? void 0 : browser_monads_ts_1.window.TextEncoder) === 'function') { return browser_monads_ts_1.window.TextEncoder; } try { const TextEncoder = (yield Promise.resolve().then(() => __importStar(require('util')))).TextEncoder; this._util.TextEncoder = TextEncoder; return TextEncoder; } catch (error) { console.error(`Failed to import 'utils.TextEncoder', ensure 'util' is available and/or pass in implementation via 'new WebCrypto({ util })'`); throw error; } }); } timingSafeEqual(value1, value2) { if ((value1 == undefined) || (value2 == undefined) || (value1.length !== value2.length)) { return false; } let result = 0; // eslint-disable-next-line no-plusplus for (let i = 0; i < value1.length; i++) { // eslint-disable-next-line no-bitwise result |= value1[i] ^ value2[i]; } return (result === 0); } stringToHex(stringValue) { return (Array.from((0, display_1.ensureString)(stringValue), (char) => (char.charCodeAt(0).toString(16).padStart(2, '0'))).join('')); } hexToString(hexValue) { return ((0, display_1.ensureArray)((0, display_1.ensureString)(hexValue).match(/.{1,2}/g)) .map((byte) => String.fromCharCode(parseInt(byte, 16))) .join('')); } arrayBufferToString(arrayBuffer) { return __awaiter(this, void 0, void 0, function* () { const uint8Array = new Uint8Array(arrayBuffer); const Decoder = yield this.getTextDecoder(); return new Decoder().decode(uint8Array); }); } arrayToArrayBuffer(array) { return ((ArrayBuffer.from != undefined) ? ArrayBuffer.from(array) : new Uint8Array(array).buffer); } ensureArrayBuffer(arrayOrArrayBuffer) { return ((arrayOrArrayBuffer instanceof ArrayBuffer) ? arrayOrArrayBuffer : this.arrayToArrayBuffer(arrayOrArrayBuffer)); } getKeyOperations(keyType) { switch (keyType) { case 'paired': case 'private': case 'privateKey': { return ['deriveKey']; } case 'secret': case 'secretKey': case 'sharedSecret': { return ['encrypt', 'decrypt']; } default: { return []; } } } getKeyAlgorythm(keyType) { switch (keyType) { case 'derivedKey': case 'derived': case 'secret': case 'secretKey': case 'sharedSecret': { return { name: 'AES-GCM', }; } default: { return { name: 'ECDH', namedCurve: 'P-256', }; } } } generateKeyPair() { return __awaiter(this, void 0, void 0, function* () { const crypto = yield this.getWebCrypto(); const keyPair = yield crypto.subtle.generateKey(this.getKeyAlgorythm('paired'), true, this.getKeyOperations('paired')); return keyPair; }); } generateSharedSecret() { return __awaiter(this, void 0, void 0, function* () { const crypto = yield this.getWebCrypto(); const keyPair = yield crypto.subtle.generateKey(this.getKeyAlgorythm('sharedSecret'), true, this.getKeyOperations('sharedSecret')); return keyPair; }); } generateHMAC(message, derivedKey) { return __awaiter(this, void 0, void 0, function* () { if (!message || !derivedKey) { return undefined; } const crypto = yield this.getWebCrypto(); const Encoder = yield this.getTextEncoder(); const signature = yield crypto.subtle.sign('HMAC', derivedKey, new Encoder().encode(message)); return this.stringToHex(this.arrayBufferToString(signature)); }); } verifyHMAC(message, derivedKey, verifiableHMAC) { return __awaiter(this, void 0, void 0, function* () { const calculatedHMAC = yield this.generateHMAC(message, derivedKey); return this.timingSafeEqual(calculatedHMAC, verifiableHMAC); }); } getStorableKey(key) { return __awaiter(this, void 0, void 0, function* () { const crypto = yield this.getWebCrypto(); const exportedJWK = yield crypto.subtle.exportKey('jwk', key); return this.stringToHex(JSON.stringify(exportedJWK)); }); } restoreStorableKey(keyType, storableHex) { return __awaiter(this, void 0, void 0, function* () { if (!storableHex) { return undefined; } const crypto = yield this.getWebCrypto(); const exportedJWK = JSON.parse(this.hexToString(storableHex) || '{}'); if ((0, display_1.objectEmpty)(exportedJWK)) { return undefined; } return crypto.subtle.importKey('jwk', exportedJWK, this.getKeyAlgorythm(keyType), true, this.getKeyOperations(keyType)); }); } getStorableKeyPair(keyPair) { return __awaiter(this, void 0, void 0, function* () { const storableKeys = {}; // eslint-disable-next-line no-restricted-syntax for (const [keyType, key] of Object.entries(keyPair)) { // eslint-disable-next-line no-await-in-loop storableKeys[keyType] = yield this.getStorableKey(key); } return storableKeys; }); } restoreStorableKeyPair(keyPair) { return __awaiter(this, void 0, void 0, function* () { const restoredKeys = {}; // eslint-disable-next-line no-restricted-syntax for (const [keyType, key] of Object.entries(keyPair)) { // eslint-disable-next-line no-await-in-loop restoredKeys[keyType] = yield this.restoreStorableKey(keyType, key); } return restoredKeys; }); } deriveSharedKey(_a) { return __awaiter(this, arguments, void 0, function* ({ publicKey, privateKey }) { if (!publicKey || !privateKey) { return undefined; } const crypto = yield this.getWebCrypto(); const derivedKey = yield crypto.subtle.deriveKey({ name: 'ECDH', public: publicKey, }, privateKey, { name: 'AES-GCM', length: 256, }, true, ['encrypt', 'decrypt']); return derivedKey; }); } deriveHMACKey(_a) { return __awaiter(this, arguments, void 0, function* ({ publicKey, privateKey }) { if (!publicKey || !privateKey) { return undefined; } const crypto = yield this.getWebCrypto(); const derivedKey = yield crypto.subtle.deriveKey({ name: 'ECDH', public: publicKey, }, privateKey, { name: 'HMAC', hash: { name: 'SHA-256' }, length: 256, // Adjusted key length, e.g., 128 bits }, true, ['sign', 'verify']); return derivedKey; }); } getVerificationKeys(_a) { return __awaiter(this, arguments, void 0, function* ({ publicKey, privateKey }) { if (!publicKey || !privateKey) { return {}; } const sharedKeyPair = yield this.restoreStorableKeyPair({ publicKey, privateKey }); const derivedHMACKey = yield this.deriveHMACKey(sharedKeyPair); const derivedSecretKey = yield this.deriveSharedKey(sharedKeyPair); return { derivedSecretKey, derivedHMACKey, }; }); } encryptMessage(decryptedMessage, derivedKey) { return __awaiter(this, void 0, void 0, function* () { if (!decryptedMessage || !derivedKey) { return undefined; } const crypto = yield this.getWebCrypto(); const iv = crypto.getRandomValues(new Uint8Array(12)); const Encoder = yield this.getTextEncoder(); const encodedMessage = new Encoder().encode(decryptedMessage); const ciphertext = yield crypto.subtle.encrypt({ name: 'AES-GCM', iv, }, derivedKey, encodedMessage); const encryptedMessage = new Uint8Array([ ...iv, ...new Uint8Array(ciphertext) ]); return Array.from(encryptedMessage); }); } decryptMessage(encryptedMessage, derivedKey) { return __awaiter(this, void 0, void 0, function* () { if (!encryptedMessage || !derivedKey) { return undefined; } const crypto = yield this.getWebCrypto(); // NOTE: this presumed an array or arrayBuffer coming in as encryptedMessage (will fail w/ IV error if its a string) const encryptedArrayBuffer = this.ensureArrayBuffer(encryptedMessage); const iv = encryptedArrayBuffer.slice(0, 12); const ciphertext = encryptedArrayBuffer.slice(12); const decryptedArrayBuffer = yield crypto.subtle.decrypt({ name: 'AES-GCM', iv, }, derivedKey, ciphertext); const Decoder = yield this.getTextDecoder(); const decryptedMessage = new Decoder().decode(decryptedArrayBuffer); return decryptedMessage; }); } } exports.WebCrypto = WebCrypto; //# sourceMappingURL=WebCrypto.js.map