UNPKG

react-native-ffjavascript

Version:

Finite Field Library in Javascript (react-native)

288 lines (231 loc) 6.56 kB
import bigInt from "big-integer"; import buildSqrt from "./fsqrt.js"; import {getRandomBytes} from "./random.js"; export default class ZqField { constructor(p) { this.type="F1"; this.one = bigInt.one; this.zero = bigInt.zero; this.p = bigInt(p); this.m = 1; this.negone = this.p.minus(bigInt.one); this.two = bigInt(2); this.half = this.p.shiftRight(1); this.bitLength = this.p.bitLength(); this.mask = bigInt.one.shiftLeft(this.bitLength).minus(bigInt.one); this.n64 = Math.floor((this.bitLength - 1) / 64)+1; this.n32 = this.n64*2; this.n8 = this.n64*8; this.R = bigInt.one.shiftLeft(this.n64*64); this.Ri = this.inv(this.R); const e = this.negone.shiftRight(this.one); this.nqr = this.two; let r = this.pow(this.nqr, e); while (!r.equals(this.negone)) { this.nqr = this.nqr.add(this.one); r = this.pow(this.nqr, e); } this.s = this.zero; this.t = this.negone; while (!this.t.isOdd()) { this.s = this.s.add(this.one); this.t = this.t.shiftRight(this.one); } this.nqr_to_t = this.pow(this.nqr, this.t); buildSqrt(this); } e(a,b) { const res = bigInt(a,b); return this.normalize(res); } add(a, b) { let res = a.add(b); if (res.geq(this.p)) { res = res.minus(this.p); } return res; } sub(a, b) { if (a.geq(b)) { return a.minus(b); } else { return this.p.minus(b.minus(a)); } } neg(a) { if (a.isZero()) return a; return this.p.minus(a); } mul(a, b) { return a.times(b).mod(this.p); } mulScalar(base, s) { return base.times(bigInt(s)).mod(this.p); } square(a) { return a.square().mod(this.p); } eq(a, b) { return a.eq(b); } neq(a, b) { return a.neq(b); } lt(a, b) { const aa = a.gt(this.half) ? a.minus(this.p) : a; const bb = b.gt(this.half) ? b.minus(this.p) : b; return aa.lt(bb); } gt(a, b) { const aa = a.gt(this.half) ? a.minus(this.p) : a; const bb = b.gt(this.half) ? b.minus(this.p) : b; return aa.gt(bb); } leq(a, b) { const aa = a.gt(this.half) ? a.minus(this.p) : a; const bb = b.gt(this.half) ? b.minus(this.p) : b; return aa.leq(bb); } geq(a, b) { const aa = a.gt(this.half) ? a.minus(this.p) : a; const bb = b.gt(this.half) ? b.minus(this.p) : b; return aa.geq(bb); } div(a, b) { if (b.isZero()) throw new Error("Division by zero"); return a.times(b.modInv(this.p)).mod(this.p); } idiv(a, b) { if (b.isZero()) throw new Error("Division by zero"); return a.divide(b); } inv(a) { if (a.isZero()) throw new Error("Division by zero"); return a.modInv(this.p); } mod(a, b) { return a.mod(b); } pow(a, b) { return a.modPow(b, this.p); } exp(a, b) { return a.modPow(b, this.p); } band(a, b) { return a.and(b).and(this.mask).mod(this.p); } bor(a, b) { return a.or(b).and(this.mask).mod(this.p); } bxor(a, b) { return a.xor(b).and(this.mask).mod(this.p); } bnot(a) { return a.xor(this.mask).mod(this.p); } shl(a, b) { if (b.lt(this.bitLength)) { return a.shiftLeft(b).and(this.mask).mod(this.p); } else { const nb = this.p.minus(b); if (nb.lt(this.bitLength)) { return this.shr(a, nb); } else { return bigInt.zero; } } } shr(a, b) { if (b.lt(this.bitLength)) { return a.shiftRight(b); } else { const nb = this.p.minus(b); if (nb.lt(this.bitLength)) { return this.shl(a, nb); } else { return bigInt.zero; } } } land(a, b) { return (a.isZero() || b.isZero()) ? bigInt.zero : bigInt.one; } lor(a, b) { return (a.isZero() && b.isZero()) ? bigInt.zero : bigInt.one; } lnot(a) { return a.isZero() ? bigInt.one : bigInt.zero; } sqrt_old(n) { if (n.equals(this.zero)) return this.zero; // Test that have solution const res = this.pow(n, this.negone.shiftRight(this.one)); if (!res.equals(this.one)) return null; let m = parseInt(this.s); let c = this.nqr_to_t; let t = this.pow(n, this.t); let r = this.pow(n, this.add(this.t, this.one).shiftRight(this.one) ); while (!t.equals(this.one)) { let sq = this.square(t); let i = 1; while (!sq.equals(this.one)) { i++; sq = this.square(sq); } // b = c ^ m-i-1 let b = c; for (let j=0; j< m-i-1; j ++) b = this.square(b); m = i; c = this.square(b); t = this.mul(t, c); r = this.mul(r, b); } if (r.greater(this.p.shiftRight(this.one))) { r = this.neg(r); } return r; } normalize(a) { a = bigInt(a); if (a.isNegative()) { return this.p.minus(a.abs().mod(this.p)); } else { return a.mod(this.p); } } random() { let res = bigInt(0); let n = bigInt(this.p.square()); while (!n.isZero()) { res = res.shiftLeft(8).add(bigInt(getRandomBytes(1)[0])); n = n.shiftRight(8); } return res.mod(this.p); } toString(a, base) { let vs; if (!a.lesserOrEquals(this.p.shiftRight(bigInt(1)))) { const v = this.p.minus(a); vs = "-"+v.toString(base); } else { vs = a.toString(base); } return vs; } isZero(a) { return a.isZero(); } fromRng(rng) { let v; do { v = bigInt(0); for (let i=0; i<this.n64; i++) { v = v.add(v, rng.nextU64().shiftLeft(64*i)); } v = v.and(this.mask); } while (v.geq(this.p)); v = v.times(this.Ri).mod(this.q); return v; } }