epir
Version:
EllipticPIR client library (Node.js / TypeScript bindings).
72 lines (63 loc) • 2.38 kB
text/typescript
import { arrayBufferConcat, getRandomScalarsConcat } from './util';
import { SelectorFactoryBase, DEFAULT_CAPACITIES, CIPHER_SIZE } from './types';
import SelectorFactoryWorker from './wasm.SelectorFactory.worker.ts';
export class SelectorFactory implements SelectorFactoryBase {
workers: SelectorFactoryWorker[][] = [[], []];
ciphers: ArrayBuffer[][] = [[], []];
constructor(
public readonly isFast: boolean, public readonly key: ArrayBuffer,
/* istanbul ignore next */
public readonly capacities: number[] = DEFAULT_CAPACITIES,
nThreads = navigator.hardwareConcurrency) {
for(let i=0; i<nThreads; i++) {
this.workers[0][i] = new SelectorFactoryWorker();
this.workers[1][i] = new SelectorFactoryWorker();
}
}
async fill(): Promise<void> {
const promises = this.capacities.map((capacity, msg) => {
const needs = capacity - this.ciphers[msg].length;
const ciphersPerWorker = Math.floor(needs / this.workers[msg].length);
return Promise.all(this.workers[msg].map((worker, workerId) => {
const nCiphers = (workerId == this.workers[msg].length - 1 ?
needs - (this.workers[msg].length - 1) * ciphersPerWorker : ciphersPerWorker);
return new Promise<void>((resolve) => {
if(nCiphers <= 0) {
resolve();
return;
}
worker.onmessage = (ev) => {
for(let i=0; i*CIPHER_SIZE<ev.data.ciphers.byteLength; i++) {
this.ciphers[ev.data.msg].push(ev.data.ciphers.slice(i * CIPHER_SIZE, (i + 1) * CIPHER_SIZE));
}
resolve();
};
const random = getRandomScalarsConcat(nCiphers);
worker.postMessage({
isFast: this.isFast, key: this.key, msg: msg, count: nCiphers, random: random,
}, [random]);
});
}));
});
await Promise.all(promises);
}
create(indexCounts: number[], idx: number, refill = true): ArrayBuffer {
let prod = indexCounts.reduce((acc, v) => acc * v, 1);
const ret: ArrayBuffer[] = [];
for(let ic=0; ic<indexCounts.length; ic++) {
const cols = indexCounts[ic];
prod /= cols;
const rows = Math.floor(idx / prod);
idx -= rows * prod;
for(let r=0; r<cols; r++) {
const msg = (r == rows ? 1 : 0);
const cipher = this.ciphers[msg].pop();
if(!cipher) throw new Error('Insufficient ciphers cache.');
ret.push(cipher);
}
}
const concat = arrayBufferConcat(ret);
if(refill) this.fill();
return concat;
}
}