UNPKG

@kotevode/ffjavascript

Version:

Finite Field Library in Javascript

400 lines (351 loc) 13.1 kB
import * as Scalar from "./scalar.js"; import buildBatchConvert from "./engine_batchconvert.js"; export default class WasmCurve { constructor(tm, prefix, F, pGen, pGb, cofactor) { this.tm = tm; this.prefix = prefix; this.F = F; this.pOp1 = tm.alloc(F.n8*3); this.pOp2 = tm.alloc(F.n8*3); this.pOp3 = tm.alloc(F.n8*3); this.tm.instance.exports[prefix + "_zero"](this.pOp1); this.zero = this.tm.getBuff(this.pOp1, F.n8*3); this.tm.instance.exports[prefix + "_zeroAffine"](this.pOp1); this.zeroAffine = this.tm.getBuff(this.pOp1, F.n8*2); this.one = this.tm.getBuff(pGen, F.n8*3); this.g = this.one; this.oneAffine = this.tm.getBuff(pGen, F.n8*2); this.gAffine = this.oneAffine; this.b = this.tm.getBuff(pGb, F.n8); if (cofactor) { this.cofactor = Scalar.toLEBuff(cofactor); } this.negone = this.neg(this.one); this.two = this.add(this.one, this.one); this.batchLEMtoC = buildBatchConvert(tm, prefix + "_batchLEMtoC", F.n8*2, F.n8); this.batchLEMtoU = buildBatchConvert(tm, prefix + "_batchLEMtoU", F.n8*2, F.n8*2); this.batchCtoLEM = buildBatchConvert(tm, prefix + "_batchCtoLEM", F.n8, F.n8*2); this.batchUtoLEM = buildBatchConvert(tm, prefix + "_batchUtoLEM", F.n8*2, F.n8*2); this.batchToJacobian = buildBatchConvert(tm, prefix + "_batchToJacobian", F.n8*2, F.n8*3); this.batchToAffine = buildBatchConvert(tm, prefix + "_batchToAffine", F.n8*3, F.n8*2); } op2(opName, a, b) { this.tm.setBuff(this.pOp1, a); this.tm.setBuff(this.pOp2, b); this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp2, this.pOp3); return this.tm.getBuff(this.pOp3, this.F.n8*3); } op2bool(opName, a, b) { this.tm.setBuff(this.pOp1, a); this.tm.setBuff(this.pOp2, b); return !!this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp2, this.pOp3); } op1(opName, a) { this.tm.setBuff(this.pOp1, a); this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3); return this.tm.getBuff(this.pOp3, this.F.n8*3); } op1Affine(opName, a) { this.tm.setBuff(this.pOp1, a); this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3); return this.tm.getBuff(this.pOp3, this.F.n8*2); } op1Bool(opName, a) { this.tm.setBuff(this.pOp1, a); return !!this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3); } add(a,b) { if (a.byteLength == this.F.n8*3) { if (b.byteLength == this.F.n8*3) { return this.op2("_add", a, b); } else if (b.byteLength == this.F.n8*2) { return this.op2("_addMixed", a, b); } else { throw new Error("invalid point size"); } } else if (a.byteLength == this.F.n8*2) { if (b.byteLength == this.F.n8*3) { return this.op2("_addMixed", b, a); } else if (b.byteLength == this.F.n8*2) { return this.op2("_addAffine", a, b); } else { throw new Error("invalid point size"); } } else { throw new Error("invalid point size"); } } sub(a,b) { if (a.byteLength == this.F.n8*3) { if (b.byteLength == this.F.n8*3) { return this.op2("_sub", a, b); } else if (b.byteLength == this.F.n8*2) { return this.op2("_subMixed", a, b); } else { throw new Error("invalid point size"); } } else if (a.byteLength == this.F.n8*2) { if (b.byteLength == this.F.n8*3) { return this.op2("_subMixed", b, a); } else if (b.byteLength == this.F.n8*2) { return this.op2("_subAffine", a, b); } else { throw new Error("invalid point size"); } } else { throw new Error("invalid point size"); } } neg(a) { if (a.byteLength == this.F.n8*3) { return this.op1("_neg", a); } else if (a.byteLength == this.F.n8*2) { return this.op1Affine("_negAffine", a); } else { throw new Error("invalid point size"); } } double(a) { if (a.byteLength == this.F.n8*3) { return this.op1("_double", a); } else if (a.byteLength == this.F.n8*2) { return this.op1("_doubleAffine", a); } else { throw new Error("invalid point size"); } } isZero(a) { if (a.byteLength == this.F.n8*3) { return this.op1Bool("_isZero", a); } else if (a.byteLength == this.F.n8*2) { return this.op1Bool("_isZeroAffine", a); } else { throw new Error("invalid point size"); } } timesScalar(a, s) { if (!(s instanceof Uint8Array)) { s = Scalar.toLEBuff(Scalar.e(s)); } let fnName; if (a.byteLength == this.F.n8*3) { fnName = this.prefix + "_timesScalar"; } else if (a.byteLength == this.F.n8*2) { fnName = this.prefix + "_timesScalarAffine"; } else { throw new Error("invalid point size"); } this.tm.setBuff(this.pOp1, a); this.tm.setBuff(this.pOp2, s); this.tm.instance.exports[fnName](this.pOp1, this.pOp2, s.byteLength, this.pOp3); return this.tm.getBuff(this.pOp3, this.F.n8*3); } timesFr(a, s) { let fnName; if (a.byteLength == this.F.n8*3) { fnName = this.prefix + "_timesFr"; } else if (a.byteLength == this.F.n8*2) { fnName = this.prefix + "_timesFrAffine"; } else { throw new Error("invalid point size"); } this.tm.setBuff(this.pOp1, a); this.tm.setBuff(this.pOp2, s); this.tm.instance.exports[fnName](this.pOp1, this.pOp2, this.pOp3); return this.tm.getBuff(this.pOp3, this.F.n8*3); } eq(a,b) { if (a.byteLength == this.F.n8*3) { if (b.byteLength == this.F.n8*3) { return this.op2bool("_eq", a, b); } else if (b.byteLength == this.F.n8*2) { return this.op2bool("_eqMixed", a, b); } else { throw new Error("invalid point size"); } } else if (a.byteLength == this.F.n8*2) { if (b.byteLength == this.F.n8*3) { return this.op2bool("_eqMixed", b, a); } else if (b.byteLength == this.F.n8*2) { return this.op2bool("_eqAffine", a, b); } else { throw new Error("invalid point size"); } } else { throw new Error("invalid point size"); } } toAffine(a) { if (a.byteLength == this.F.n8*3) { return this.op1Affine("_toAffine", a); } else if (a.byteLength == this.F.n8*2) { return a; } else { throw new Error("invalid point size"); } } toJacobian(a) { if (a.byteLength == this.F.n8*3) { return a; } else if (a.byteLength == this.F.n8*2) { return this.op1("_toJacobian", a); } else { throw new Error("invalid point size"); } } toRprUncompressed(arr, offset, a) { this.tm.setBuff(this.pOp1, a); if (a.byteLength == this.F.n8*3) { this.tm.instance.exports[this.prefix + "_toAffine"](this.pOp1, this.pOp1); } else if (a.byteLength != this.F.n8*2) { throw new Error("invalid point size"); } this.tm.instance.exports[this.prefix + "_LEMtoU"](this.pOp1, this.pOp1); const res = this.tm.getBuff(this.pOp1, this.F.n8*2); arr.set(res, offset); } fromRprUncompressed(arr, offset) { const buff = arr.slice(offset, offset + this.F.n8*2); this.tm.setBuff(this.pOp1, buff); this.tm.instance.exports[this.prefix + "_UtoLEM"](this.pOp1, this.pOp1); return this.tm.getBuff(this.pOp1, this.F.n8*2); } toRprCompressed(arr, offset, a) { this.tm.setBuff(this.pOp1, a); if (a.byteLength == this.F.n8*3) { this.tm.instance.exports[this.prefix + "_toAffine"](this.pOp1, this.pOp1); } else if (a.byteLength != this.F.n8*2) { throw new Error("invalid point size"); } this.tm.instance.exports[this.prefix + "_LEMtoC"](this.pOp1, this.pOp1); const res = this.tm.getBuff(this.pOp1, this.F.n8); arr.set(res, offset); } fromRprCompressed(arr, offset) { const buff = arr.slice(offset, offset + this.F.n8); this.tm.setBuff(this.pOp1, buff); this.tm.instance.exports[this.prefix + "_CtoLEM"](this.pOp1, this.pOp2); return this.tm.getBuff(this.pOp2, this.F.n8*2); } toUncompressed(a) { const buff = new Uint8Array(this.F.n8*2); this.toRprUncompressed(buff, 0, a); return buff; } toRprLEM(arr, offset, a) { if (a.byteLength == this.F.n8*2) { arr.set(a, offset); return; } else if (a.byteLength == this.F.n8*3) { this.tm.setBuff(this.pOp1, a); this.tm.instance.exports[this.prefix + "_toAffine"](this.pOp1, this.pOp1); const res = this.tm.getBuff(this.pOp1, this.F.n8*2); arr.set(res, offset); } else { throw new Error("invalid point size"); } } fromRprLEM(arr, offset) { offset = offset || 0; return arr.slice(offset, offset+this.F.n8*2); } toString(a, radix) { if (a.byteLength == this.F.n8*3) { const x = this.F.toString(a.slice(0, this.F.n8), radix); const y = this.F.toString(a.slice(this.F.n8, this.F.n8*2), radix); const z = this.F.toString(a.slice(this.F.n8*2), radix); return `[ ${x}, ${y}, ${z} ]`; } else if (a.byteLength == this.F.n8*2) { const x = this.F.toString(a.slice(0, this.F.n8), radix); const y = this.F.toString(a.slice(this.F.n8), radix); return `[ ${x}, ${y} ]`; } else { throw new Error("invalid point size"); } } isValid(a) { if (this.isZero(a)) return true; const F = this.F; const aa = this.toAffine(a); const x = aa.slice(0, this.F.n8); const y = aa.slice(this.F.n8, this.F.n8*2); const x3b = F.add(F.mul(F.square(x),x), this.b); const y2 = F.square(y); return F.eq(x3b, y2); } fromRng(rng) { const F = this.F; let P = []; let greatest; let x3b; do { P[0] = F.fromRng(rng); greatest = rng.nextBool(); x3b = F.add(F.mul(F.square(P[0]), P[0]), this.b); } while (!F.isSquare(x3b)); P[1] = F.sqrt(x3b); const s = F.isNegative(P[1]); if (greatest ^ s) P[1] = F.neg(P[1]); let Pbuff = new Uint8Array(this.F.n8*2); Pbuff.set(P[0]); Pbuff.set(P[1], this.F.n8); if (this.cofactor) { Pbuff = this.timesScalar(Pbuff, this.cofactor); } return Pbuff; } toObject(a) { if (this.isZero(a)) { return [ this.F.toObject(this.F.zero), this.F.toObject(this.F.one), this.F.toObject(this.F.zero), ]; } const x = this.F.toObject(a.slice(0, this.F.n8)); const y = this.F.toObject(a.slice(this.F.n8, this.F.n8*2)); let z; if (a.byteLength == this.F.n8*3) { z = this.F.toObject(a.slice(this.F.n8*2, this.F.n8*3)); } else { z = this.F.toObject(this.F.one); } return [x, y, z]; } fromObject(a) { const x = this.F.fromObject(a[0]); const y = this.F.fromObject(a[1]); let z; if (a.length==3) { z = this.F.fromObject(a[2]); } else { z = this.F.one; } if (this.F.isZero(z, this.F.one)) { return this.zeroAffine; } else if (this.F.eq(z, this.F.one)) { const buff = new Uint8Array(this.F.n8*2); buff.set(x); buff.set(y, this.F.n8); return buff; } else { const buff = new Uint8Array(this.F.n8*3); buff.set(x); buff.set(y, this.F.n8); buff.set(z, this.F.n8*2); return buff; } } e(a) { if (a instanceof Uint8Array) return a; return this.fromObject(a); } x(a) { const tmp = this.toAffine(a); return tmp.slice(0, this.F.n8); } y(a) { const tmp = this.toAffine(a); return tmp.slice(this.F.n8); } }