| /* |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| */ |
| |
| /* Finite Field arithmetic */ |
| /* AMCL mod p functions */ |
| |
| var FP = function(ctx) { |
| "use strict"; |
| |
| /** |
| * Creates an instance of FP. |
| * |
| * @constructor |
| * @this {FP} |
| * @param x FP / BIG instance |
| */ |
| var FP = function(x) { |
| if (x instanceof FP) { |
| this.f = new ctx.BIG(x.f); |
| this.XES = x.XES; |
| } else { |
| this.f = new ctx.BIG(x); |
| this.XES = 1; |
| if (!this.f.iszilch()) |
| this.nres(); |
| } |
| }; |
| |
| FP.NOT_SPECIAL = 0; |
| FP.PSEUDO_MERSENNE = 1; |
| FP.GENERALISED_MERSENNE = 2; |
| FP.MONTGOMERY_FRIENDLY = 3; |
| |
| FP.ZERO = 0; |
| FP.ONE = 1; |
| FP.SPARSER = 2; |
| FP.SPARSE = 3; |
| FP.DENSE= 4; |
| |
| FP.MODBITS = ctx.config["@NBT"]; |
| FP.MOD8 = ctx.config["@M8"]; |
| FP.MODTYPE = ctx.config["@MT"]; |
| |
| FP.FEXCESS = ((1 << ctx.config["@SH"])-1); // 2^(BASEBITS*NLEN-MODBITS)-1 |
| FP.OMASK = (-1) << FP.TBITS; |
| FP.TBITS = FP.MODBITS % ctx.BIG.BASEBITS; |
| FP.TMASK = (1 << FP.TBITS) - 1; |
| |
| FP.prototype = { |
| |
| /** |
| * Set FP to zero |
| * |
| * @this {FP} |
| */ |
| zero: function() { |
| this.XES = 1; |
| this.f.zero(); |
| }, |
| |
| /** |
| * copy from a ctx.BIG in ROM |
| * |
| * @this {FP} |
| * @param x FP instance to be copied |
| */ |
| rcopy: function(y) { |
| this.f.rcopy(y); |
| this.nres(); |
| }, |
| |
| /** |
| * copy from another ctx.BIG |
| * |
| * @this {FP} |
| * @param x FP instance to be copied |
| */ |
| bcopy: function(y) { |
| this.f.copy(y); |
| this.nres(); |
| }, |
| |
| /** |
| * Copy FP to another FP |
| * |
| * @this {FP} |
| * @param x FP instance to be copied |
| */ |
| copy: function(y) { |
| this.XES = y.XES; |
| this.f.copy(y.f); |
| }, |
| |
| /** |
| * Conditional constant time swap of two FP numbers |
| * |
| * @this {BIG} |
| * @parameter b FP number |
| * @parameter d Integer |
| */ |
| cswap: function(b, d) { |
| this.f.cswap(b.f, d); |
| var t, c = d; |
| c = ~(c - 1); |
| t = c & (this.XES ^ b.XES); |
| this.XES ^= t; |
| b.XES ^= t; |
| }, |
| |
| /** |
| * Conditional copy of FP number |
| * |
| * @this {FP} |
| * @param g FP instance |
| * @param d copy depends on this value |
| */ |
| cmove: function(b, d) { |
| var c = d; |
| |
| c = ~(c - 1); |
| |
| this.f.cmove(b.f, d); |
| this.XES ^= (this.XES ^ b.XES) & c; |
| }, |
| |
| /** |
| * Converts from BIG integer to residue form mod Modulus |
| * |
| * @this {FP} |
| */ |
| nres: function() { |
| var r, d; |
| |
| if (FP.MODTYPE != FP.PSEUDO_MERSENNE && FP.MODTYPE != FP.GENERALISED_MERSENNE) { |
| r = new ctx.BIG(); |
| r.rcopy(ctx.ROM_FIELD.R2modp); |
| |
| d = ctx.BIG.mul(this.f, r); |
| this.f.copy(FP.mod(d)); |
| this.XES = 2; |
| } else { |
| this.XES = 1; |
| } |
| |
| return this; |
| }, |
| |
| /** |
| * Converts from residue form back to BIG integer form |
| * |
| * @this {FP} |
| */ |
| redc: function() { |
| var r = new ctx.BIG(0), |
| d, w; |
| |
| r.copy(this.f); |
| |
| if (FP.MODTYPE != FP.PSEUDO_MERSENNE && FP.MODTYPE != FP.GENERALISED_MERSENNE) { |
| d = new ctx.DBIG(0); |
| d.hcopy(this.f); |
| w = FP.mod(d); |
| r.copy(w); |
| } |
| |
| return r; |
| }, |
| |
| /** |
| * convert to hex string |
| * |
| * @this {FP} |
| */ |
| toString: function() { |
| var s = this.redc().toString(); |
| return s; |
| }, |
| |
| /** |
| * Tests for FP equal to zero |
| * |
| * @this {FP} |
| */ |
| iszilch: function() { |
| var c=new FP(0); c.copy(this); |
| c.reduce(); |
| return c.f.iszilch(); |
| }, |
| |
| /** |
| * Reduces all components of possibly unreduced FP mod Modulus |
| * |
| * @this {FP} |
| */ |
| reduce: function() { |
| var q,carry,sr,sb,m = new ctx.BIG(0); |
| m.rcopy(ctx.ROM_FIELD.Modulus); |
| var r = new ctx.BIG(0); |
| r.rcopy(ctx.ROM_FIELD.Modulus); |
| this.f.norm(); |
| |
| if (this.XES>16) |
| { |
| q=FP.quo(this.f,m); |
| carry=r.pmul(q); |
| r.w[ctx.BIG.NLEN-1]+=(carry<<ctx.BIG.BASEBITS); // correction - put any carry out back in again |
| this.f.sub(r); |
| this.f.norm(); |
| sb=2; |
| } |
| else { |
| sb=FP.logb2(this.XES-1); |
| } |
| m.fshl(sb); |
| |
| while (sb>0) |
| { |
| // constant time... |
| sr=ctx.BIG.ssn(r,this.f,m); // optimized combined shift, subtract and norm |
| this.f.cmove(r,1-sr); |
| sb--; |
| } |
| |
| this.XES = 1; |
| }, |
| |
| /** |
| * Set FP to unity |
| * |
| * @this {FP} |
| */ |
| one: function() { |
| this.f.one(); |
| this.nres(); |
| }, |
| |
| /** |
| * Normalises the components of an FP |
| * |
| * @this {FP} |
| */ |
| norm: function() { |
| return this.f.norm(); |
| }, |
| |
| /** |
| * Fast Modular multiplication of two FPs, mod Modulus |
| * |
| * @this {FP} |
| * @param b FP number, the multiplier |
| */ |
| mul: function(b) { |
| var d; |
| |
| if (this.XES * b.XES > FP.FEXCESS) { |
| this.reduce(); |
| } |
| |
| d = ctx.BIG.mul(this.f, b.f); |
| this.f.copy(FP.mod(d)); |
| this.XES = 2; |
| |
| return this; |
| }, |
| |
| /** |
| * Multiplication of an FP by a small integer |
| * |
| * @this {FP} |
| * @param s integer |
| */ |
| imul: function(c) { |
| var s = false, |
| d, n; |
| |
| if (c < 0) { |
| c = -c; |
| s = true; |
| } |
| |
| if (FP.MODTYPE == FP.PSEUDO_MERSENNE || FP.MODTYPE == FP.GENERALISED_MERSENNE) { |
| d = this.f.pxmul(c); |
| this.f.copy(FP.mod(d)); |
| this.XES = 2; |
| } else { |
| if (this.XES * c <= FP.FEXCESS) { |
| this.f.pmul(c); |
| this.XES *= c; |
| } else { |
| n = new FP(c); |
| this.mul(n); |
| } |
| } |
| |
| if (s) { |
| this.neg(); |
| this.norm(); |
| } |
| return this; |
| }, |
| |
| /** |
| * Fast Squaring of an FP |
| * |
| * @this {FP} |
| */ |
| sqr: function() { |
| var d, t; |
| |
| if (this.XES * this.XES > FP.FEXCESS) { |
| this.reduce(); |
| } |
| |
| d = ctx.BIG.sqr(this.f); |
| t = FP.mod(d); |
| this.f.copy(t); |
| this.XES = 2; |
| |
| return this; |
| }, |
| |
| /* this+=b */ |
| add: function(b) { |
| this.f.add(b.f); |
| this.XES += b.XES; |
| |
| if (this.XES > FP.FEXCESS) { |
| this.reduce(); |
| } |
| |
| return this; |
| }, |
| |
| /** |
| * negate this |
| * |
| * @this {FP} |
| * @param x FP instance to be set to one |
| */ |
| neg: function() { |
| var m = new ctx.BIG(0), |
| sb; |
| |
| m.rcopy(ctx.ROM_FIELD.Modulus); |
| |
| sb = FP.logb2(this.XES - 1); |
| |
| m.fshl(sb); |
| this.XES = (1 << sb)+1; |
| this.f.rsub(m); |
| |
| if (this.XES > FP.FEXCESS) { |
| this.reduce(); |
| } |
| |
| return this; |
| }, |
| |
| /** |
| * subtraction of two FPs |
| * |
| * @this {FP} |
| * @param x FP instance |
| */ |
| sub: function(b) { |
| var n = new FP(0); |
| |
| n.copy(b); |
| n.neg(); |
| this.add(n); |
| |
| return this; |
| }, |
| |
| rsub: function(b) { |
| var n = new FP(0); |
| |
| n.copy(this); |
| n.neg(); |
| this.copy(b); |
| this.add(n); |
| }, |
| |
| /** |
| * Divide an FP by 2 |
| * |
| * @this {FP} |
| */ |
| div2: function() { |
| var p; |
| |
| if (this.f.parity() === 0) { |
| this.f.fshr(1); |
| } else { |
| p = new ctx.BIG(0); |
| p.rcopy(ctx.ROM_FIELD.Modulus); |
| |
| this.f.add(p); |
| this.f.norm(); |
| this.f.fshr(1); |
| } |
| |
| return this; |
| }, |
| |
| // return this^(p-3)/4 or this^(p-5)/8 |
| // See https://eprint.iacr.org/2018/1038 |
| |
| /** |
| * return this^(p-3)/4 or this^(p-5)/8 |
| * |
| * @this {FP} |
| */ |
| fpow: function() { |
| var i,j,k,bw,w,c,nw,lo,m,n; |
| var xp=[]; |
| var ac=[1,2,3,6,12,15,30,60,120,240,255]; |
| // phase 1 |
| |
| xp[0]=new FP(this); // 1 |
| xp[1]=new FP(this); xp[1].sqr(); // 2 |
| xp[2]=new FP(xp[1]); xp[2].mul(this); //3 |
| xp[3]=new FP(xp[2]); xp[3].sqr(); // 6 |
| xp[4]=new FP(xp[3]); xp[4].sqr(); // 12 |
| xp[5]=new FP(xp[4]); xp[5].mul(xp[2]); // 15 |
| xp[6]=new FP(xp[5]); xp[6].sqr(); // 30 |
| xp[7]=new FP(xp[6]); xp[7].sqr(); // 60 |
| xp[8]=new FP(xp[7]); xp[8].sqr(); // 120 |
| xp[9]=new FP(xp[8]); xp[9].sqr(); // 240 |
| xp[10]=new FP(xp[9]); xp[10].mul(xp[5]); // 255 |
| |
| |
| n=FP.MODBITS; |
| if (FP.MODTYPE == FP.GENERALISED_MERSENNE) // Goldilocks ONLY |
| n/=2; |
| if (FP.MOD8==5) |
| { |
| n-=3; |
| c=(ctx.ROM_FIELD.MConst+5)/8; |
| } else { |
| n-=2; |
| c=(ctx.ROM_FIELD.MConst+3)/4; |
| } |
| |
| bw=0; w=1; while (w<c) {w*=2; bw+=1;} |
| k=w-c; |
| |
| i=10; var key=new FP(0); |
| if (k!=0) |
| { |
| while (ac[i]>k) i--; |
| key.copy(xp[i]); |
| k-=ac[i]; |
| } |
| while (k!=0) |
| { |
| i--; |
| if (ac[i]>k) continue; |
| key.mul(xp[i]); |
| k-=ac[i]; |
| } |
| |
| // phase 2 |
| xp[1].copy(xp[2]); |
| xp[2].copy(xp[5]); |
| xp[3].copy(xp[10]); |
| |
| j=3; m=8; |
| nw=n-bw; |
| var t=new FP(0); |
| while (2*m<nw) |
| { |
| t.copy(xp[j++]); |
| for (i=0;i<m;i++) |
| t.sqr(); |
| xp[j].copy(xp[j-1]); |
| xp[j].mul(t); |
| m*=2; |
| } |
| lo=nw-m; |
| var r=new FP(xp[j]); |
| |
| while (lo!=0) |
| { |
| m/=2; j--; |
| if (lo<m) continue; |
| lo-=m; |
| t.copy(r); |
| for (i=0;i<m;i++) |
| t.sqr(); |
| r.copy(t); |
| r.mul(xp[j]); |
| } |
| |
| // phase 3 |
| if (bw!=0) |
| { |
| for (i=0;i<bw;i++ ) |
| r.sqr(); |
| r.mul(key); |
| } |
| |
| if (FP.MODTYPE == FP.GENERALISED_MERSENNE) // Goldilocks ONLY |
| { |
| key.copy(r); |
| r.sqr(); |
| r.mul(this); |
| for (i=0;i<n+1;i++) |
| r.sqr(); |
| r.mul(key); |
| } |
| return r; |
| }, |
| |
| /** |
| * Inverting an FP |
| * |
| * @this {FP} |
| */ |
| inverse: function() { |
| |
| if (FP.MODTYPE == FP.PSEUDO_MERSENNE || FP.MODTYPE == FP.GENERALISED_MERSENNE) |
| { |
| var y=this.fpow(); |
| if (FP.MOD8==5) |
| { |
| var t=new FP(this); |
| t.sqr(); |
| this.mul(t); |
| y.sqr(); |
| |
| } |
| y.sqr(); |
| y.sqr(); |
| this.mul(y); |
| return this; |
| } else { |
| var m2=new ctx.BIG(0); |
| m2.rcopy(ctx.ROM_FIELD.Modulus); |
| m2.dec(2); m2.norm(); |
| this.copy(this.pow(m2)); |
| return this; |
| } |
| }, |
| |
| /** |
| * Tests for equality of two FP instances |
| * |
| * @this {FP} |
| * @param x FP instance to compare |
| */ |
| equals: function(a) { |
| var ft=new FP(0); ft.copy(this); |
| var sd=new FP(0); sd.copy(a); |
| ft.reduce(); |
| sd.reduce(); |
| |
| if (ctx.BIG.comp(ft.f, sd.f) === 0) { |
| return true; |
| } |
| |
| return false; |
| }, |
| |
| /** |
| * Raises an FP to the power of a BIG |
| * |
| * @this {FP} |
| * @param e BIG instance exponent |
| */ |
| pow: function(e) { |
| var i,w=[], |
| tb=[], |
| t=new ctx.BIG(e), |
| nb, lsbs, r; |
| this.norm(); |
| t.norm(); |
| nb= 1 + Math.floor((t.nbits() + 3) / 4); |
| |
| for (i=0;i<nb;i++) { |
| lsbs=t.lastbits(4); |
| t.dec(lsbs); |
| t.norm(); |
| w[i]=lsbs; |
| t.fshr(4); |
| } |
| tb[0]=new FP(1); |
| tb[1]=new FP(this); |
| for (i=2;i<16;i++) { |
| tb[i]=new FP(tb[i-1]); |
| tb[i].mul(this); |
| } |
| r=new FP(tb[w[nb-1]]); |
| for (i=nb-2;i>=0;i--) { |
| r.sqr(); |
| r.sqr(); |
| r.sqr(); |
| r.sqr(); |
| r.mul(tb[w[i]]); |
| } |
| r.reduce(); |
| return r; |
| }, |
| |
| /** |
| * return jacobi symbol (this/Modulus) |
| * |
| * @this {FP} |
| */ |
| jacobi: function() { |
| var p = new ctx.BIG(0), |
| w = this.redc(); |
| |
| p.rcopy(ctx.ROM_FIELD.Modulus); |
| |
| return w.jacobi(p); |
| }, |
| |
| /** |
| * Fast Modular square root of a an FP, mod Modulus |
| * |
| * @this {FP} |
| */ |
| sqrt: function() { |
| var i, v, r; |
| |
| this.reduce(); |
| if (FP.MOD8 == 5) { |
| i = new FP(0); |
| i.copy(this); |
| i.f.shl(1); |
| if (FP.MODTYPE == FP.PSEUDO_MERSENNE || FP.MODTYPE == FP.GENERALISED_MERSENNE) { |
| v=i.fpow(); |
| } else { |
| var b = new ctx.BIG(0); |
| b.rcopy(ctx.ROM_FIELD.Modulus); |
| b.dec(5); |
| b.norm(); |
| b.shr(3); |
| v = i.pow(b); |
| } |
| i.mul(v); |
| i.mul(v); |
| i.f.dec(1); |
| r = new FP(0); |
| r.copy(this); |
| r.mul(v); |
| r.mul(i); |
| r.reduce(); |
| |
| return r; |
| } else { |
| if (FP.MODTYPE == FP.PSEUDO_MERSENNE || FP.MODTYPE == FP.GENERALISED_MERSENNE) { |
| var r=this.fpow(); |
| r.mul(this); |
| return r; |
| } else { |
| var b = new ctx.BIG(0); |
| b.rcopy(ctx.ROM_FIELD.Modulus); |
| b.inc(1); |
| b.norm(); |
| b.shr(2); |
| return this.pow(b); |
| } |
| } |
| } |
| |
| }; |
| |
| FP.logb2 = function(v) { |
| var r; |
| |
| v |= v >>> 1; |
| v |= v >>> 2; |
| v |= v >>> 4; |
| v |= v >>> 8; |
| v |= v >>> 16; |
| |
| v = v - ((v >>> 1) & 0x55555555); |
| v = (v & 0x33333333) + ((v >>> 2) & 0x33333333); |
| r = ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24; |
| |
| return r; |
| }; |
| |
| FP.quo = function(n,m) { |
| var num,den,hb=ctx.BIG.CHUNK>>1; |
| if (FP.TBITS<hb) |
| { |
| var sh=hb-FP.TBITS; |
| num=(n.w[ctx.BIG.NLEN-1]<<sh)|(n.w[ctx.BIG.NLEN-2]>>(ctx.BIG.BASEBITS-sh)); |
| den=(m.w[ctx.BIG.NLEN-1]<<sh)|(m.w[ctx.BIG.NLEN-2]>>(ctx.BIG.BASEBITS-sh)); |
| } else { |
| num=n.w[ctx.BIG.NLEN-1]; |
| den=m.w[ctx.BIG.NLEN-1]; |
| } |
| return Math.floor(num/(den+1)) |
| }; |
| |
| /** |
| * reduce a ctx.DBIG to a ctx.BIG using a "special" modulus |
| * |
| * @this {FP} |
| */ |
| FP.mod = function(d) { |
| var b = new ctx.BIG(0), |
| i, t, v, tw, tt, lo, carry, m, dd; |
| |
| if (FP.MODTYPE == FP.PSEUDO_MERSENNE) { |
| t = d.split(FP.MODBITS); |
| b.hcopy(d); |
| |
| if (ctx.ROM_FIELD.MConst != 1) { |
| v = t.pmul(ctx.ROM_FIELD.MConst); |
| } else { |
| v = 0; |
| } |
| |
| t.add(b); |
| t.norm(); |
| |
| tw = t.w[ctx.BIG.NLEN - 1]; |
| t.w[ctx.BIG.NLEN - 1] &= FP.TMASK; |
| t.inc(ctx.ROM_FIELD.MConst * ((tw >> FP.TBITS) + (v << (ctx.BIG.BASEBITS - FP.TBITS)))); |
| // b.add(t); |
| t.norm(); |
| |
| return t; |
| } |
| |
| if (FP.MODTYPE == FP.MONTGOMERY_FRIENDLY) { |
| for (i = 0; i < ctx.BIG.NLEN; i++) { |
| d.w[ctx.BIG.NLEN + i] += d.muladd(d.w[i], ctx.ROM_FIELD.MConst - 1, d.w[i], ctx.BIG.NLEN + i - 1); |
| } |
| |
| for (i = 0; i < ctx.BIG.NLEN; i++) { |
| b.w[i] = d.w[ctx.BIG.NLEN + i]; |
| } |
| |
| b.norm(); |
| } |
| |
| if (FP.MODTYPE == FP.GENERALISED_MERSENNE) { // GoldiLocks Only |
| t = d.split(FP.MODBITS); |
| b.hcopy(d); |
| b.add(t); |
| dd = new ctx.DBIG(0); |
| dd.hcopy(t); |
| dd.shl(FP.MODBITS / 2); |
| |
| tt = dd.split(FP.MODBITS); |
| lo = new ctx.BIG(); |
| lo.hcopy(dd); |
| |
| b.add(tt); |
| b.add(lo); |
| //b.norm(); |
| tt.shl(FP.MODBITS / 2); |
| b.add(tt); |
| |
| carry = b.w[ctx.BIG.NLEN - 1] >> FP.TBITS; |
| b.w[ctx.BIG.NLEN - 1] &= FP.TMASK; |
| b.w[0] += carry; |
| |
| b.w[Math.floor(224 / ctx.BIG.BASEBITS)] += carry << (224 % ctx.BIG.BASEBITS); |
| b.norm(); |
| } |
| |
| if (FP.MODTYPE == FP.NOT_SPECIAL) { |
| m = new ctx.BIG(0); |
| m.rcopy(ctx.ROM_FIELD.Modulus); |
| |
| b.copy(ctx.BIG.monty(m, ctx.ROM_FIELD.MConst, d)); |
| } |
| |
| return b; |
| }; |
| |
| return FP; |
| }; |
| |
| if (typeof module !== "undefined" && typeof module.exports !== "undefined") { |
| module.exports = { |
| FP: FP |
| }; |
| } |