| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517 |
- /**
- * Constructs a new bignum from another bignum, a number or a hex string.
- */
- sjcl.bn = function(it) {
- this.initWith(it);
- };
- sjcl.bn.prototype = {
- radix: 24,
- maxMul: 8,
- _class: sjcl.bn,
-
- copy: function() {
- return new this._class(this);
- },
- /**
- * Initializes this with it, either as a bn, a number, or a hex string.
- */
- initWith: function(it) {
- var i=0, k, n, l;
- switch(typeof it) {
- case "object":
- this.limbs = it.limbs.slice(0);
- break;
-
- case "number":
- this.limbs = [it];
- this.normalize();
- break;
-
- case "string":
- it = it.replace(/^0x/, '');
- this.limbs = [];
- // hack
- k = this.radix / 4;
- for (i=0; i < it.length; i+=k) {
- this.limbs.push(parseInt(it.substring(Math.max(it.length - i - k, 0), it.length - i),16));
- }
- break;
- default:
- this.limbs = [0];
- }
- return this;
- },
- /**
- * Returns true if "this" and "that" are equal. Calls fullReduce().
- * Equality test is in constant time.
- */
- equals: function(that) {
- if (typeof that === "number") { that = new this._class(that); }
- var difference = 0, i;
- this.fullReduce();
- that.fullReduce();
- for (i = 0; i < this.limbs.length || i < that.limbs.length; i++) {
- difference |= this.getLimb(i) ^ that.getLimb(i);
- }
- return (difference === 0);
- },
-
- /**
- * Get the i'th limb of this, zero if i is too large.
- */
- getLimb: function(i) {
- return (i >= this.limbs.length) ? 0 : this.limbs[i];
- },
-
- /**
- * Constant time comparison function.
- * Returns 1 if this >= that, or zero otherwise.
- */
- greaterEquals: function(that) {
- if (typeof that === "number") { that = new this._class(that); }
- var less = 0, greater = 0, i, a, b;
- i = Math.max(this.limbs.length, that.limbs.length) - 1;
- for (; i>= 0; i--) {
- a = this.getLimb(i);
- b = that.getLimb(i);
- greater |= (b - a) & ~less;
- less |= (a - b) & ~greater;
- }
- return (greater | ~less) >>> 31;
- },
-
- /**
- * Convert to a hex string.
- */
- toString: function() {
- this.fullReduce();
- var out="", i, s, l = this.limbs;
- for (i=0; i < this.limbs.length; i++) {
- s = l[i].toString(16);
- while (i < this.limbs.length - 1 && s.length < 6) {
- s = "0" + s;
- }
- out = s + out;
- }
- return "0x"+out;
- },
-
- /** this += that. Does not normalize. */
- addM: function(that) {
- if (typeof(that) !== "object") { that = new this._class(that); }
- var i, l=this.limbs, ll=that.limbs;
- for (i=l.length; i<ll.length; i++) {
- l[i] = 0;
- }
- for (i=0; i<ll.length; i++) {
- l[i] += ll[i];
- }
- return this;
- },
-
- /** this *= 2. Requires normalized; ends up normalized. */
- doubleM: function() {
- var i, carry=0, tmp, r=this.radix, m=this.radixMask, l=this.limbs;
- for (i=0; i<l.length; i++) {
- tmp = l[i];
- tmp = tmp+tmp+carry;
- l[i] = tmp & m;
- carry = tmp >> r;
- }
- if (carry) {
- l.push(carry);
- }
- return this;
- },
-
- /** this /= 2, rounded down. Requires normalized; ends up normalized. */
- halveM: function() {
- var i, carry=0, tmp, r=this.radix, l=this.limbs;
- for (i=l.length-1; i>=0; i--) {
- tmp = l[i];
- l[i] = (tmp+carry)>>1;
- carry = (tmp&1) << r;
- }
- if (!l[l.length-1]) {
- l.pop();
- }
- return this;
- },
- /** this -= that. Does not normalize. */
- subM: function(that) {
- if (typeof(that) !== "object") { that = new this._class(that); }
- var i, l=this.limbs, ll=that.limbs;
- for (i=l.length; i<ll.length; i++) {
- l[i] = 0;
- }
- for (i=0; i<ll.length; i++) {
- l[i] -= ll[i];
- }
- return this;
- },
-
- mod: function(that) {
- that = new sjcl.bn(that).normalize(); // copy before we begin
- var out = new sjcl.bn(this).normalize(), ci=0;
-
- for (; out.greaterEquals(that); ci++) {
- that.doubleM();
- }
- for (; ci > 0; ci--) {
- that.halveM();
- if (out.greaterEquals(that)) {
- out.subM(that).normalize();
- }
- }
- return out.trim();
- },
-
- /** return inverse mod prime p. p must be odd. Binary extended Euclidean algorithm mod p. */
- inverseMod: function(p) {
- var a = new sjcl.bn(1), b = new sjcl.bn(0), x = new sjcl.bn(this), y = new sjcl.bn(p), tmp, i, nz=1;
-
- if (!(p.limbs[0] & 1)) {
- throw (new sjcl.exception.invalid("inverseMod: p must be odd"));
- }
-
- // invariant: y is odd
- do {
- if (x.limbs[0] & 1) {
- if (!x.greaterEquals(y)) {
- // x < y; swap everything
- tmp = x; x = y; y = tmp;
- tmp = a; a = b; b = tmp;
- }
- x.subM(y);
- x.normalize();
-
- if (!a.greaterEquals(b)) {
- a.addM(p);
- }
- a.subM(b);
- }
-
- // cut everything in half
- x.halveM();
- if (a.limbs[0] & 1) {
- a.addM(p);
- }
- a.normalize();
- a.halveM();
-
- // check for termination: x ?= 0
- for (i=nz=0; i<x.limbs.length; i++) {
- nz |= x.limbs[i];
- }
- } while(nz);
-
- if (!y.equals(1)) {
- throw (new sjcl.exception.invalid("inverseMod: p and x must be relatively prime"));
- }
-
- return b;
- },
-
- /** this + that. Does not normalize. */
- add: function(that) {
- return this.copy().addM(that);
- },
- /** this - that. Does not normalize. */
- sub: function(that) {
- return this.copy().subM(that);
- },
-
- /** this * that. Normalizes and reduces. */
- mul: function(that) {
- if (typeof(that) === "number") { that = new this._class(that); }
- var i, j, a = this.limbs, b = that.limbs, al = a.length, bl = b.length, out = new this._class(), c = out.limbs, ai, ii=this.maxMul;
- for (i=0; i < this.limbs.length + that.limbs.length + 1; i++) {
- c[i] = 0;
- }
- for (i=0; i<al; i++) {
- ai = a[i];
- for (j=0; j<bl; j++) {
- c[i+j] += ai * b[j];
- }
-
- if (!--ii) {
- ii = this.maxMul;
- out.cnormalize();
- }
- }
- return out.cnormalize().reduce();
- },
- /** this ^ 2. Normalizes and reduces. */
- square: function() {
- return this.mul(this);
- },
- /** this ^ n. Uses square-and-multiply. Normalizes and reduces. */
- power: function(l) {
- if (typeof(l) === "number") {
- l = [l];
- } else if (l.limbs !== undefined) {
- l = l.normalize().limbs;
- }
- var i, j, out = new this._class(1), pow = this;
- for (i=0; i<l.length; i++) {
- for (j=0; j<this.radix; j++) {
- if (l[i] & (1<<j)) {
- out = out.mul(pow);
- }
- pow = pow.square();
- }
- }
-
- return out;
- },
- trim: function() {
- var l = this.limbs, p;
- do {
- p = l.pop();
- } while (l.length && p === 0);
- l.push(p);
- return this;
- },
-
- /** Reduce mod a modulus. Stubbed for subclassing. */
- reduce: function() {
- return this;
- },
- /** Reduce and normalize. */
- fullReduce: function() {
- return this.normalize();
- },
-
- /** Propagate carries. */
- normalize: function() {
- var carry=0, i, pv = this.placeVal, ipv = this.ipv, l, m, limbs = this.limbs, ll = limbs.length, mask = this.radixMask;
- for (i=0; i < ll || (carry !== 0 && carry !== -1); i++) {
- l = (limbs[i]||0) + carry;
- m = limbs[i] = l & mask;
- carry = (l-m)*ipv;
- }
- if (carry === -1) {
- limbs[i-1] -= this.placeVal;
- }
- return this;
- },
- /** Constant-time normalize. Does not allocate additional space. */
- cnormalize: function() {
- var carry=0, i, ipv = this.ipv, l, m, limbs = this.limbs, ll = limbs.length, mask = this.radixMask;
- for (i=0; i < ll-1; i++) {
- l = limbs[i] + carry;
- m = limbs[i] = l & mask;
- carry = (l-m)*ipv;
- }
- limbs[i] += carry;
- return this;
- },
-
- /** Serialize to a bit array */
- toBits: function(len) {
- this.fullReduce();
- len = len || this.exponent || this.limbs.length * this.radix;
- var i = Math.floor((len-1)/24), w=sjcl.bitArray, e = (len + 7 & -8) % this.radix || this.radix,
- out = [w.partial(e, this.getLimb(i))];
- for (i--; i >= 0; i--) {
- out = w.concat(out, [w.partial(this.radix, this.getLimb(i))]);
- }
- return out;
- },
-
- /** Return the length in bits, rounded up to the nearest byte. */
- bitLength: function() {
- this.fullReduce();
- var out = this.radix * (this.limbs.length - 1),
- b = this.limbs[this.limbs.length - 1];
- for (; b; b >>= 1) {
- out ++;
- }
- return out+7 & -8;
- }
- };
- sjcl.bn.fromBits = function(bits) {
- var Class = this, out = new Class(), words=[], w=sjcl.bitArray, t = this.prototype,
- l = Math.min(this.bitLength || 0x100000000, w.bitLength(bits)), e = l % t.radix || t.radix;
-
- words[0] = w.extract(bits, 0, e);
- for (; e < l; e += t.radix) {
- words.unshift(w.extract(bits, e, t.radix));
- }
- out.limbs = words;
- return out;
- };
- sjcl.bn.prototype.ipv = 1 / (sjcl.bn.prototype.placeVal = Math.pow(2,sjcl.bn.prototype.radix));
- sjcl.bn.prototype.radixMask = (1 << sjcl.bn.prototype.radix) - 1;
- /**
- * Creates a new subclass of bn, based on reduction modulo a pseudo-Mersenne prime,
- * i.e. a prime of the form 2^e + sum(a * 2^b),where the sum is negative and sparse.
- */
- sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
- function p(it) {
- this.initWith(it);
- /*if (this.limbs[this.modOffset]) {
- this.reduce();
- }*/
- }
- var ppr = p.prototype = new sjcl.bn(), i, tmp, mo;
- mo = ppr.modOffset = Math.ceil(tmp = exponent / ppr.radix);
- ppr.exponent = exponent;
- ppr.offset = [];
- ppr.factor = [];
- ppr.minOffset = mo;
- ppr.fullMask = 0;
- ppr.fullOffset = [];
- ppr.fullFactor = [];
- ppr.modulus = p.modulus = new sjcl.bn(Math.pow(2,exponent));
-
- ppr.fullMask = 0|-Math.pow(2, exponent % ppr.radix);
- for (i=0; i<coeff.length; i++) {
- ppr.offset[i] = Math.floor(coeff[i][0] / ppr.radix - tmp);
- ppr.fullOffset[i] = Math.ceil(coeff[i][0] / ppr.radix - tmp);
- ppr.factor[i] = coeff[i][1] * Math.pow(1/2, exponent - coeff[i][0] + ppr.offset[i] * ppr.radix);
- ppr.fullFactor[i] = coeff[i][1] * Math.pow(1/2, exponent - coeff[i][0] + ppr.fullOffset[i] * ppr.radix);
- ppr.modulus.addM(new sjcl.bn(Math.pow(2,coeff[i][0])*coeff[i][1]));
- ppr.minOffset = Math.min(ppr.minOffset, -ppr.offset[i]); // conservative
- }
- ppr._class = p;
- ppr.modulus.cnormalize();
- /** Approximate reduction mod p. May leave a number which is negative or slightly larger than p. */
- ppr.reduce = function() {
- var i, k, l, mo = this.modOffset, limbs = this.limbs, aff, off = this.offset, ol = this.offset.length, fac = this.factor, ll;
- i = this.minOffset;
- while (limbs.length > mo) {
- l = limbs.pop();
- ll = limbs.length;
- for (k=0; k<ol; k++) {
- limbs[ll+off[k]] -= fac[k] * l;
- }
-
- i--;
- if (!i) {
- limbs.push(0);
- this.cnormalize();
- i = this.minOffset;
- }
- }
- this.cnormalize();
- return this;
- };
-
- ppr._strongReduce = (ppr.fullMask === -1) ? ppr.reduce : function() {
- var limbs = this.limbs, i = limbs.length - 1, k, l;
- this.reduce();
- if (i === this.modOffset - 1) {
- l = limbs[i] & this.fullMask;
- limbs[i] -= l;
- for (k=0; k<this.fullOffset.length; k++) {
- limbs[i+this.fullOffset[k]] -= this.fullFactor[k] * l;
- }
- this.normalize();
- }
- };
- /** mostly constant-time, very expensive full reduction. */
- ppr.fullReduce = function() {
- var greater, i;
- // massively above the modulus, may be negative
-
- this._strongReduce();
- // less than twice the modulus, may be negative
- this.addM(this.modulus);
- this.addM(this.modulus);
- this.normalize();
- // probably 2-3x the modulus
-
- this._strongReduce();
- // less than the power of 2. still may be more than
- // the modulus
- // HACK: pad out to this length
- for (i=this.limbs.length; i<this.modOffset; i++) {
- this.limbs[i] = 0;
- }
-
- // constant-time subtract modulus
- greater = this.greaterEquals(this.modulus);
- for (i=0; i<this.limbs.length; i++) {
- this.limbs[i] -= this.modulus.limbs[i] * greater;
- }
- this.cnormalize();
- return this;
- };
- ppr.inverse = function() {
- return (this.power(this.modulus.sub(2)));
- };
- p.fromBits = sjcl.bn.fromBits;
- return p;
- };
- // a small Mersenne prime
- sjcl.bn.prime = {
- p127: sjcl.bn.pseudoMersennePrime(127, [[0,-1]]),
- // Bernstein's prime for Curve25519
- p25519: sjcl.bn.pseudoMersennePrime(255, [[0,-19]]),
- // NIST primes
- p192: sjcl.bn.pseudoMersennePrime(192, [[0,-1],[64,-1]]),
- p224: sjcl.bn.pseudoMersennePrime(224, [[0,1],[96,-1]]),
- p256: sjcl.bn.pseudoMersennePrime(256, [[0,-1],[96,1],[192,1],[224,-1]]),
- p384: sjcl.bn.pseudoMersennePrime(384, [[0,-1],[32,1],[96,-1],[128,-1]]),
- p521: sjcl.bn.pseudoMersennePrime(521, [[0,-1]])
- };
- sjcl.bn.random = function(modulus, paranoia) {
- if (typeof modulus !== "object") { modulus = new sjcl.bn(modulus); }
- var words, i, l = modulus.limbs.length, m = modulus.limbs[l-1]+1, out = new sjcl.bn();
- while (true) {
- // get a sequence whose first digits make sense
- do {
- words = sjcl.random.randomWords(l, paranoia);
- if (words[l-1] < 0) { words[l-1] += 0x100000000; }
- } while (Math.floor(words[l-1] / m) === Math.floor(0x100000000 / m));
- words[l-1] %= m;
- // mask off all the limbs
- for (i=0; i<l-1; i++) {
- words[i] &= modulus.radixMask;
- }
- // check the rest of the digitssj
- out.limbs = words;
- if (!out.greaterEquals(modulus)) {
- return out;
- }
- }
- };
|