epir
Version:
EllipticPIR client library (Node.js / TypeScript bindings).
113 lines (96 loc) • 3.3 kB
text/typescript
import { SCALAR_SIZE } from './types';
export const time = (): number => Date.now();
export const runMeasurement = async <T>(func: () => (Promise<T> | T)): Promise<{ execTime: number, ret: T }> => {
const begin = time();
const ret = func();
if(ret instanceof Promise) {
const ret2 = await ret;
const end = time();
return { execTime: end - begin, ret: ret2 };
} else {
const end = time();
return { execTime: end - begin, ret: ret };
}
};
export const printMeasurement = async <T>(
func: () => (Promise<T> | T), prefix: string, postfix = 'ms.'): Promise<T> => {
const { execTime, ret } = await runMeasurement<T>(func);
console.log(`\u001b[32m${prefix} ${execTime.toLocaleString()} ${postfix}\u001b[0m`);
return ret;
}
export const arrayBufferConcat = (arr: ArrayBuffer[]): ArrayBuffer => {
const len = arr.reduce((acc, v) => acc + v.byteLength, 0);
const ret = new Uint8Array(len);
for(let i=0, offset=0; i<arr.length; i++) {
ret.set(new Uint8Array(arr[i]), offset);
offset += arr[i].byteLength;
}
return ret.buffer;
}
export const arrayBufferCompare = (
a: ArrayBuffer, aOffset: number, b: ArrayBuffer, bOffset: number, len: number): number => {
const aa = new Uint8Array(a, aOffset, len);
const bb = new Uint8Array(b, bOffset, len);
for(let i=0; i<len; i++) {
if(aa[i] == bb[i]) continue;
return aa[i] - bb[i];
}
return 0;
}
export const arrayBufferToHex = (buf: ArrayBuffer): string => {
const arr = new Uint8Array(buf);
let ret = '';
for(const n of arr) {
ret += Number(n).toString(16).padStart(2, '0');
}
return ret;
};
export const hexToArrayBuffer = (hex: string): ArrayBuffer => {
const split = hex.match(/.{2}/g);
if(!split) return new ArrayBuffer(0);
return new Uint8Array(split.map((h) => parseInt(h, 16))).buffer;
};
export const checkIsHex = (hex: string, expectedSize = -1): boolean => {
const pattern = /^[0-9a-fA-F]+$/;
if(expectedSize >= 0) {
return ((hex.length === 2 * expectedSize) && (hex.match(pattern) !== null));
} else {
return ((hex.length % 2 === 0) && (hex.match(pattern) !== null));
}
};
export const getRandomBytes = (len: number): ArrayBuffer => {
const MAX_ENTROPY = 65536;
const ret = new Uint8Array(len);
for(let offset=0; offset<len; offset+=MAX_ENTROPY) {
window.crypto.getRandomValues(ret.subarray(offset, Math.min(len, offset + MAX_ENTROPY)));
}
return ret.buffer;
};
export const isCanonical = (buf: ArrayBuffer): boolean => {
const bufView = new Uint8Array(buf);
let c = (bufView[31] & 0x7f) ^ 0x7f;
for(let i=30; i>0; i--) {
c |= bufView[i] ^ 0xff;
}
const d = (0xed - 1 - bufView[0]) >> 8;
return !((c == 0) && d)
};
export const isZero = (buf: ArrayBuffer): boolean => {
return new Uint8Array(buf).reduce<boolean>((acc, v) => acc && (v == 0), true);
};
export const getRandomScalar = (): ArrayBuffer => {
for(;;) {
const scalar = getRandomBytes(SCALAR_SIZE);
new Uint8Array(scalar)[31] &= 0x1f;
if(!isCanonical(scalar) || isZero(scalar)) continue;
return scalar;
}
};
export const getRandomScalars = (cnt: number): ArrayBuffer[] => {
const ret: ArrayBuffer[] = [];
for(let i=0; i<cnt; i++) ret.push(getRandomScalar());
return ret;
}
export const getRandomScalarsConcat = (cnt: number): ArrayBuffer => {
return arrayBufferConcat(getRandomScalars(cnt));
}