UNPKG

@logsn/arweave

Version:
161 lines (160 loc) 5.55 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const ArweaveUtils = require("../utils"); class WebCryptoDriver { keyLength = 4096; publicExponent = 0x10001; hashAlgorithm = "sha256"; driver; constructor() { if (!this.detectWebCrypto()) { throw new Error("SubtleCrypto not available!"); } this.driver = crypto.subtle; } async generateJWK() { let cryptoKey = await this.driver.generateKey({ name: "RSA-PSS", modulusLength: 4096, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: "SHA-256", }, }, true, ["sign"]); let jwk = await this.driver.exportKey("jwk", cryptoKey.privateKey); return { kty: jwk.kty, e: jwk.e, n: jwk.n, d: jwk.d, p: jwk.p, q: jwk.q, dp: jwk.dp, dq: jwk.dq, qi: jwk.qi, }; } async sign(jwk, data, { saltLength } = {}) { let signature = await this.driver.sign({ name: "RSA-PSS", saltLength: 32, }, await this.jwkToCryptoKey(jwk), data); return new Uint8Array(signature); } async hash(data, algorithm = "SHA-256") { let digest = await this.driver.digest(algorithm, data); return new Uint8Array(digest); } async verify(publicModulus, data, signature) { const publicKey = { kty: "RSA", e: "AQAB", n: publicModulus, }; const key = await this.jwkToPublicCryptoKey(publicKey); const digest = await this.driver.digest("SHA-256", data); const salt0 = await this.driver.verify({ name: "RSA-PSS", saltLength: 0, }, key, signature, data); const salt32 = await this.driver.verify({ name: "RSA-PSS", saltLength: 32, }, key, signature, data); // saltN's salt-length is derived from a formula described here // https://developer.mozilla.org/en-US/docs/Web/API/RsaPssParams const saltN = await this.driver.verify({ name: "RSA-PSS", saltLength: Math.ceil((key.algorithm.modulusLength - 1) / 8) - digest.byteLength - 2, }, key, signature, data); return salt0 || salt32 || saltN; } async jwkToCryptoKey(jwk) { return this.driver.importKey("jwk", jwk, { name: "RSA-PSS", hash: { name: "SHA-256", }, }, false, ["sign"]); } async jwkToPublicCryptoKey(publicJwk) { return this.driver.importKey("jwk", publicJwk, { name: "RSA-PSS", hash: { name: "SHA-256", }, }, false, ["verify"]); } detectWebCrypto() { if (typeof crypto === "undefined") { return false; } const subtle = crypto?.subtle; if (subtle === undefined) { return false; } const names = [ "generateKey", "importKey", "exportKey", "digest", "sign", ]; return names.every((name) => typeof subtle[name] === "function"); } async encrypt(data, key, salt) { const initialKey = await this.driver.importKey("raw", typeof key == "string" ? ArweaveUtils.stringToBuffer(key) : key, { name: "PBKDF2", length: 32, }, false, ["deriveKey"]); // const salt = ArweaveUtils.stringToBuffer("salt"); // create a random string for deriving the key // const salt = this.driver.randomBytes(16).toString('hex'); const derivedkey = await this.driver.deriveKey({ name: "PBKDF2", salt: salt ? ArweaveUtils.stringToBuffer(salt) : ArweaveUtils.stringToBuffer("salt"), iterations: 100000, hash: "SHA-256", }, initialKey, { name: "AES-CBC", length: 256, }, false, ["encrypt", "decrypt"]); const iv = new Uint8Array(16); crypto.getRandomValues(iv); const encryptedData = await this.driver.encrypt({ name: "AES-CBC", iv: iv, }, derivedkey, data); return ArweaveUtils.concatBuffers([iv, encryptedData]); } async decrypt(encrypted, key, salt) { const initialKey = await this.driver.importKey("raw", typeof key == "string" ? ArweaveUtils.stringToBuffer(key) : key, { name: "PBKDF2", length: 32, }, false, ["deriveKey"]); // const salt = ArweaveUtils.stringToBuffer("pepper"); const derivedkey = await this.driver.deriveKey({ name: "PBKDF2", salt: salt ? ArweaveUtils.stringToBuffer(salt) : ArweaveUtils.stringToBuffer("salt"), iterations: 100000, hash: "SHA-256", }, initialKey, { name: "AES-CBC", length: 256, }, false, ["encrypt", "decrypt"]); const iv = encrypted.slice(0, 16); const data = await this.driver.decrypt({ name: "AES-CBC", iv: iv, }, derivedkey, encrypted.slice(16)); // We're just using concat to convert from an array buffer to uint8array return ArweaveUtils.concatBuffers([data]); } } exports.default = WebCryptoDriver;