UNPKG

@getanthill/datastore

Version:

Event-Sourced Datastore

238 lines 8.37 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const vm_1 = __importDefault(require("vm")); const utils_1 = require("../../utils"); const operator_overload_1 = require("./operator-overload"); let sealPromise = null; class FullyHomomorphicEncryptionClient { constructor(config) { this._seal = null; this._context = null; this._keys = null; this._evaluator = null; /** * @see https://github.com/s0l0ist/node-seal?tab=readme-ov-file#caveats * Must have a `delete()` method */ this._instances = []; /** * Sample cipher instance to test the constructor equality * @see computeFromAny() */ this._cipherSample = null; this._encoder = null; this._config = config; } /** * @see https://github.com/s0l0ist/node-seal */ async connect() { if (this._seal !== null) { return this; } if (sealPromise === null) { const SEAL = require('node-seal'); sealPromise = SEAL(); } this._seal = await sealPromise; this.init(); } async init() { await this.connect(); // Create a new EncryptionParameters const schemeType = this.seal.SchemeType[this.scheme]; const securityLevel = this.seal.SecurityLevel.tc128; const polyModulusDegree = this.polyModulusDegree; const bitSizes = this.bitSizes; const bitSize = this.bitSize; const encParms = this.seal.EncryptionParameters(schemeType); // Assign Poly Modulus Degree encParms.setPolyModulusDegree(polyModulusDegree); // Create a suitable set of CoeffModulus primes encParms.setCoeffModulus(this.seal.CoeffModulus.Create(polyModulusDegree, Int32Array.from(bitSizes))); // Assign a PlainModulus (only for bfv/bgv scheme type) if (this.scheme !== 'ckks') { encParms.setPlainModulus(this.seal.PlainModulus.Batching(polyModulusDegree, bitSize)); } // Create a new Context this._context = this.seal.Context(encParms, true, securityLevel); // Helper to check if the Context was created successfully /* istanbul ignore next */ if (!this._context.parametersSet()) { throw new Error('Could not set the parameters in the given context. Please try different encryption parameters.'); } this.createKeys(); this._evaluator = this.seal.Evaluator(this.context); this.addInstance(this._evaluator); this._cipherSample = this.seal.CipherText(); this.addInstance(this._cipherSample); if (this.scheme === 'ckks') { this._encoder = this.seal.CKKSEncoder(this._context); } else { this._encoder = this.seal.BatchEncoder(this._context); } this.addInstance(this._encoder); return this; } async disconnect() { if (this._seal !== null) { this.flushInstances(); this._seal = null; } return this; } async clone() { const client = new FullyHomomorphicEncryptionClient(this._config); await client.connect(); client._context = this.context; client._keys = this.keys; return client; } get seal() { if (this._seal === null) { throw new Error('Node seal must be initialized first with `.connect()`'); } return this._seal; } get context() { return this._context; } get keys() { return this._keys; } get scheme() { return this._config.scheme ?? 'bgv'; } get bitSize() { return this._config.bitSize ?? 20; } get bitSizes() { return this._config.bitSizes ?? [36, 36, 37]; } get polyModulusDegree() { return this._config.polyModulusDegree ?? 4096; } get evaluator() { return this._evaluator; } addInstance(instance) { this._instances.push(instance); } flushInstances() { for (const instance of this._instances) { instance.delete(); } this._instances = []; } createKeys(_keyGenerator) { // Create a new KeyGenerator (use uploaded keys if applicable) const keyGenerator = _keyGenerator ?? this.seal.KeyGenerator(this._context); // Get the SecretKey from the keyGenerator const Secret_key_A_ = keyGenerator.secretKey(); // Get the PublicKey from the keyGenerator const Public_key_A_ = keyGenerator.createPublicKey(); // Create a new RelinKey const Relin_key_A_ = keyGenerator.createRelinKeys(); // Create a new GaloisKey const Galois_key_A_ = keyGenerator.createGaloisKeys(); this._keys = { secret: Secret_key_A_, public: Public_key_A_, relin: Relin_key_A_, galois: Galois_key_A_, }; } encode(arr) { if (this.scheme === 'ckks') { const float64 = Float64Array.from(arr); return this._encoder.encode(float64, Math.pow(2, this.bitSize)); } const int32 = Int32Array.from(arr); return this._encoder.encode(int32); } createCypher(value) { const plainText = this.encode(value); const encryptor = this.seal.Encryptor(this._context, this.keys.public); return encryptor.encrypt(plainText); } decode(plainText) { if (this.scheme === 'ckks') { const float64 = this._encoder.decode(plainText); return Array.from(float64); } const int32 = this._encoder.decode(plainText); return Array.from(int32); } fromCypher(cypher) { const decryptor = this.seal.Decryptor(this._context, this._keys.secret); const plainText = decryptor.decrypt(cypher); return this.decode(plainText); } async computeFromAnyToCiphers(handler, ...args) { return new Promise((resolve, reject) => { /* istanbul ignore next */ const rawScript = (0, operator_overload_1.overloadAsString)(handler, args, this); const script = new vm_1.default.Script(rawScript); const context = { client: this, handler, args, resolve, reject, console, }; script.runInNewContext(context, { timeout: 120000, breakOnSigint: true, }); }); } async computeFromAny(handler, ...args) { return this.computeFromAnyToCiphers(handler, ...args).then((res) => { if (res.constructor === this._cipherSample?.constructor) { return res.save(); } const result = (0, utils_1.mapValuesDeep)(res, (val) => { if (!!val && typeof val.save === 'function') { return val.save(); } return val; }); return result; }); } async compute(handler, ..._args) { const client = await this.clone(); const value = await client.computeFromAny(handler, ..._args); /** * @see https://github.com/s0l0ist/node-seal?tab=readme-ov-file#caveats * and specifically Garbage Collection */ client.flushInstances(); return value; } compile(script) { const body = script.split('\n').slice(1, -1).join('\n'); const args = script .split('\n', 1)[0] .replace(/^(async )?function( [a-z]{1,30})?\(/, '') .split(')', 1)[0] .split(',') .map((t) => t.trim()); const AsyncFunction = Object.getPrototypeOf( /* istanbul ignore next */ async function () { }).constructor; var retFn = AsyncFunction(...args, body); return retFn; } // FHE functions relinearize(val) { return this.evaluator.relinearize(val, this.keys.relin); } } exports.default = FullyHomomorphicEncryptionClient; //# sourceMappingURL=index.js.map