/** * BigInteger * * An ActionScript 3 implementation of BigInteger (light version) * Copyright (c) 2007 Henri Torgemane * * Derived from: * The jsbn library, Copyright (c) 2003-2005 Tom Wu * * See LICENSE.txt for full license information. */ package com.hurlant.math { import com.hurlant.crypto.prng.Random; import com.hurlant.util.Hex; import com.hurlant.util.Memory; import flash.utils.ByteArray; use namespace bi_internal; public class BigInteger { public static const DB:int = 30; // number of significant bits per chunk public static const DV:int = (1<0) { if (p>p)>0) { m = true; r = d.toString(36); } while (i >= 0) { if (p>(p+=DB-k); } else { d = (a[i]>>(p-=k))&km; if (p<=0) { p += DB; --i; } } if (d>0) { m = true; } if (m) { r += d.toString(36); } } } return m?r:"0"; } public function toArray(array:ByteArray):uint { const k:int = 8; const km:int = (1<<8)-1; var d:int = 0; var i:int = t; var p:int = DB-(i*DB)%k; var m:Boolean = false; var c:int = 0; if (i-->0) { if (p>p)>0) { m = true; array.writeByte(d); c++; } while (i >= 0) { if (p>(p+=DB-k); } else { d = (a[i]>>(p-=k))&km; if (p<=0) { p += DB; --i; } } if (d>0) { m = true; } if (m) { array.writeByte(d); c++; } } } return c; } /** * best-effort attempt to fit into a Number. * precision can be lost if it just can't fit. */ public function valueOf():Number { if (s==-1) { return -negate().valueOf(); } var coef:Number = 1; var value:Number = 0; for (var i:uint=0;i v, - if this < v, 0 if equal */ public function compareTo(v:BigInteger):int { var r:int = s - v.s; if (r!=0) { return r; } var i:int = t; r = i-v.t; if (r!=0) { return r; } while (--i >=0) { r=a[i]-v.a[i]; if (r != 0) return r; } return 0; } /** * returns bit length of the integer x */ bi_internal function nbits(x:int):int { var r:int = 1; var t:int; if ((t=x>>>16) != 0) { x = t; r += 16; } if ((t=x>>8) != 0) { x = t; r += 8; } if ((t=x>>4) != 0) { x = t; r += 4; } if ((t=x>>2) != 0) { x = t; r += 2; } if ((t=x>>1) != 0) { x = t; r += 1; } return r; } /** * returns the number of bits in this */ public function bitLength():int { if (t<=0) return 0; return DB*(t-1)+nbits(a[t-1]^(s&DM)); } /** * * @param v * @return this % v * */ public function mod(v:BigInteger):BigInteger { var r:BigInteger = nbi(); abs().divRemTo(v,null,r); if (s<0 && r.compareTo(ZERO)>0) { v.subTo(r,r); } return r; } /** * this^e % m, 0 <= e < 2^32 */ public function modPowInt(e:int, m:BigInteger):BigInteger { var z:IReduction; if (e<256 || m.isEven()) { z = new ClassicReduction(m); } else { z = new MontgomeryReduction(m); } return exp(e, z); } /** * copy this to r */ bi_internal function copyTo(r:BigInteger):void { for (var i:int = t-1; i>=0; --i) { r.a[i] = a[i]; } r.t = t; r.s = s; } /** * set from integer value "value", -DV <= value < DV */ bi_internal function fromInt(value:int):void { t = 1; s = (value<0)?-1:0; if (value>0) { a[0] = value; } else if (value<-1) { a[0] = value+DV; } else { t = 0; } } /** * set from ByteArray and length, * starting a current position * If length goes beyond the array, pad with zeroes. */ bi_internal function fromArray(value:ByteArray, length:int, unsigned:Boolean = false):void { var p:int = value.position; var i:int = p+length; var sh:int = 0; const k:int = 8; t = 0; s = 0; while (--i >= p) { var x:int = i DB) { a[t-1] |= (x&((1<<(DB-sh))-1))<>(DB-sh); } else { a[t-1] |= x<= DB) sh -= DB; } if (!unsigned && (value[0]&0x80)==0x80) { s = -1; if (sh > 0) { a[t-1] |= ((1<<(DB-sh))-1)<0 && a[t-1]==c) { --t; } } /** * r = this << n*DB */ bi_internal function dlShiftTo(n:int, r:BigInteger):void { var i:int; for (i=t-1; i>=0; --i) { r.a[i+n] = a[i]; } for (i=n-1; i>=0; --i) { r.a[i] = 0; } r.t = t+n; r.s = s; } /** * r = this >> n*DB */ bi_internal function drShiftTo(n:int, r:BigInteger):void { var i:int; for (i=n; i=0; --i) { r.a[i+ds+1] = (a[i]>>cbs)|c; c = (a[i]&bm)<=0; --i) { r.a[i] = 0; } r.a[ds] = c; r.t = t+ds+1; r.s = s; r.clamp(); } /** * r = this >> n */ bi_internal function rShiftTo(n:int, r:BigInteger):void { r.s = s; var ds:int = n/DB; if (ds >= t) { r.t = 0; return; } var bs:int = n%DB; var cbs:int = DB-bs; var bm:int = (1<>bs; var i:int; for (i=ds+1; i>bs; } if (bs>0) { r.a[t-ds-1] |= (s&bm)<>= DB; } if (v.t < t) { c -= v.s; while (i< t) { c+= a[i]; r.a[i++] = c&DM; c >>= DB; } c += s; } else { c += s; while (i < v.t) { c -= v.a[i]; r.a[i++] = c&DM; c >>= DB; } c -= v.s; } r.s = (c<0)?-1:0; if (c<-1) { r.a[i++] = DV+c; } else if (c>0) { r.a[i++] = c; } r.t = i; r.clamp(); } /** * am: Compute w_j += (x*this_i), propagates carries, * c is initial carry, returns final carry. * c < 3*dvalue, x < 2*dvalue, this_i < dvalue */ bi_internal function am(i:int,x:int,w:BigInteger,j:int,c:int,n:int):int { var xl:int = x&0x7fff; var xh:int = x>>15; while(--n >= 0) { var l:int = a[i]&0x7fff; var h:int = a[i++]>>15; var m:int = xh*l + h*xl; l = xl*l + ((m&0x7fff)<<15)+w.a[j]+(c&0x3fffffff); c = (l>>>30)+(m>>>15)+xh*h+(c>>>30); w.a[j++] = l&0x3fffffff; } return c; } /** * r = this * v, r != this,a (HAC 14.12) * "this" should be the larger one if appropriate */ bi_internal function multiplyTo(v:BigInteger, r:BigInteger):void { var x:BigInteger = abs(); var y:BigInteger = v.abs(); var i:int = x.t; r.t = i+y.t; while (--i >= 0) { r.a[i] = 0; } for (i=0; i=0) r.a[i] = 0; for (i=0; i= DV) { r.a[i+x.t] -= DV; r.a[i+x.t+1] = 1; } } if (r.t>0) { r.a[r.t-1] += x.am(i, x.a[i], r, 2*i, 0, 1); } r.s = 0; r.clamp(); } /** * divide this by m, quotient and remainder to q, r (HAC 14.20) * r != q, this != m. q or r may be null. */ bi_internal function divRemTo(m:BigInteger, q:BigInteger = null, r:BigInteger = null):void { var pm:BigInteger = m.abs(); if (pm.t <= 0) return; var pt:BigInteger = abs(); if (pt.t < pm.t) { if (q!=null) q.fromInt(0); if (r!=null) copyTo(r); return; } if (r==null) r = nbi(); var y:BigInteger = nbi(); var ts:int = s; var ms:int = m.s; var nsh:int = DB-nbits(pm.a[pm.t-1]); // normalize modulus if (nsh>0) { pm.lShiftTo(nsh, y); pt.lShiftTo(nsh, r); } else { pm.copyTo(y); pt.copyTo(r); } var ys:int = y.t; var y0:int = y.a[ys-1]; if (y0==0) return; var yt:Number = y0*(1<1)?y.a[ys-2]>>F2:0); var d1:Number = FV/yt; var d2:Number = (1<=0) { r.a[r.t++] = 1; r.subTo(t,r); } ONE.dlShiftTo(ys,t); t.subTo(y,y); // "negative" y so we can replace sub with am later. while(y.t= 0) { // Estimate quotient digit var qd:int = (r.a[--i]==y0)?DM:Number(r.a[i])*d1+(Number(r.a[i-1])+e)*d2; if ((r.a[i]+= y.am(0, qd, r, j, 0, ys))0) { r.rShiftTo(nsh, r); // Denormalize remainder } if (ts<0) { ZERO.subTo(r,r); } } /** * return "-1/this % 2^DB"; useful for Mont. reduction * justification: * xy == 1 (mod n) * xy = 1+km * xy(2-xy) = (1+km)(1-km) * x[y(2-xy)] = 1-k^2.m^2 * x[y(2-xy)] == 1 (mod m^2) * if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 * should reduce x and y(2-xy) by m^2 at each step to keep size bounded * [XXX unit test the living shit out of this.] */ bi_internal function invDigit():int { if (t<1) return 0; var x:int = a[0]; if ((x&1)==0) return 0; var y:int = x&3; // y == 1/x mod 2^2 y = (y*(2-(x&0xf )*y)) &0xf; // y == 1/x mod 2^4 y = (y*(2-(x&0xff)*y)) &0xff; // y == 1/x mod 2^8 y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16 // last step - calculate inverse mod DV directly; // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints // XXX 48 bit ints? Whaaaa? is there an implicit float conversion in here? y = (y*(2-x*y%DV))%DV; // y == 1/x mod 2^dbits // we really want the negative inverse, and -DV < y < DV return (y>0)?DV-y:-y; } /** * true iff this is even */ bi_internal function isEven():Boolean { return ((t>0)?(a[0]&1):s) == 0; } /** * this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) */ bi_internal function exp(e:int, z:IReduction):BigInteger { if (e > 0xffffffff || e < 1) return ONE; var r:BigInteger = nbi(); var r2:BigInteger = nbi(); var g:BigInteger = z.convert(this); var i:int = nbits(e)-1; g.copyTo(r); while(--i>=0) { z.sqrTo(r, r2); if ((e&(1<0) { z.mulTo(r2,g,r); } else { var t:BigInteger = r; r = r2; r2 = t; } } return z.revert(r); } bi_internal function intAt(str:String, index:int):int { return parseInt(str.charAt(index), 36); } protected function nbi():* { return new BigInteger; } /** * return bigint initialized to value */ public static function nbv(value:int):BigInteger { var bn:BigInteger = new BigInteger; bn.fromInt(value); return bn; } // Functions above are sufficient for RSA encryption. // The stuff below is useful for decryption and key generation public static const lowprimes:Array = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509]; public static const lplim:int = (1<<26)/lowprimes[lowprimes.length-1]; public function clone():BigInteger { var r:BigInteger = new BigInteger; this.copyTo(r); return r; } /** * * @return value as integer * */ public function intValue():int { if (s<0) { if (t==1) { return a[0]-DV; } else if (t==0) { return -1; } } else if (t==1) { return a[0]; } else if (t==0) { return 0; } // assumes 16 < DB < 32 return ((a[1]&((1<<(32-DB))-1))<>24; } /** * * @return value as short (assumes DB>=16) * */ public function shortValue():int { return (t==0)?s:(a[0]<<16)>>16; } /** * * @param r * @return x s.t. r^x < DV * */ protected function chunkSize(r:Number):int { return Math.floor(Math.LN2*DB/Math.log(r)); } /** * * @return 0 if this ==0, 1 if this >0 * */ public function sigNum():int { if (s<0) { return -1; } else if (t<=0 || (t==1 && a[0]<=0)) { return 0; } else{ return 1; } } /** * * @param b: radix to use * @return a string representing the integer converted to the radix. * */ protected function toRadix(b:uint=10):String { if (sigNum()==0 || b<2 || b>32) return "0"; var cs:int = chunkSize(b); var a:Number = Math.pow(b, cs); var d:BigInteger = nbv(a); var y:BigInteger = nbi(); var z:BigInteger = nbi(); var r:String = ""; divRemTo(d, y, z); while (y.sigNum()>0) { r = (a+z.intValue()).toString(b).substr(1) + r; y.divRemTo(d,y,z); } return z.intValue().toString(b) + r; } /** * * @param s a string to convert from using radix. * @param b a radix * */ protected function fromRadix(s:String, b:int = 10):void { fromInt(0); var cs:int = chunkSize(b); var d:Number = Math.pow(b, cs); var mi:Boolean = false; var j:int = 0; var w:int = 0; for (var i:int=0;i= cs) { dMultiply(d); dAddOffset(w,0); j=0; w=0; } } if (j>0) { dMultiply(Math.pow(b,j)); dAddOffset(w,0); } if (mi) { BigInteger.ZERO.subTo(this, this); } } // XXX function fromNumber not written yet. /** * * @return a byte array. * */ public function toByteArray():ByteArray { var i:int = t; var r:ByteArray = new ByteArray; r[0] = s; var p:int = DB-(i*DB)%8; var d:int; var k:int=0; if (i-->0) { if (p>p)!=(s&DM)>>p) { r[k++] = d|(s<<(DB-p)); } while (i>=0) { if(p<8) { d = (a[i]&((1<>(p+=DB-8); } else { d = (a[i]>>(p-=8))&0xff; if (p<=0) { p += DB; --i; } } if ((d&0x80)!=0) d|=-256; if (k==0 && (s&0x80)!=(d&0x80)) ++k; if (k>0 || d!=s) r[k++] = d; } } return r; } public function equals(a:BigInteger):Boolean { return compareTo(a)==0; } public function min(a:BigInteger):BigInteger { return (compareTo(a)<0)?this:a; } public function max(a:BigInteger):BigInteger { return (compareTo(a)>0)?this:a; } /** * * @param a a BigInteger to perform the operation with * @param op a Function implementing the operation * @param r a BigInteger to store the result of the operation * */ protected function bitwiseTo(a:BigInteger, op:Function, r:BigInteger):void { var i:int; var f:int; var m:int = Math.min(a.t, t); for (i=0; i>= 16; r += 16; } if ((x&0xff) == 0) { x>>= 8; r += 8; } if ((x&0xf) == 0) { x>>= 4; r += 4; } if ((x&0x3) == 0) { x>>= 2; r += 2; } if ((x&0x1) == 0) ++r; return r; } /** * * @return index of lowest 1-bit (or -1 if none) * */ public function getLowestSetBit():int { for (var i:int=0;i=t) { return s!=0; } return ((a[j]&(1<<(n%DB)))!=0); } /** * * @param n * @param op * @return this op (1<>=DB; } if (a.t < t) { c += a.s; while (i>= DB; } c += s; } else { c += s; while (i>= DB; } c += a.s; } r.s = (c<0)?-1:0; if (c>0) { r.a[i++] = c; } else if (c<-1) { r.a[i++] = DV+c; } r.t = i; r.clamp(); } /** * * @param a * @return this + a * */ public function add(a:BigInteger):BigInteger { var r:BigInteger = new BigInteger; addTo(a,r); return r; } /** * * @param a * @return this - a * */ public function subtract(a:BigInteger):BigInteger { var r:BigInteger = new BigInteger; subTo(a,r); return r; } /** * * @param a * @return this * a * */ public function multiply(a:BigInteger):BigInteger { var r:BigInteger = new BigInteger; multiplyTo(a,r); return r; } /** * * @param a * @return this / a * */ public function divide(a:BigInteger):BigInteger { var r:BigInteger = new BigInteger; divRemTo(a, r, null); return r; } public function remainder(a:BigInteger):BigInteger { var r:BigInteger = new BigInteger; divRemTo(a, null, r); return r; } /** * * @param a * @return [this/a, this%a] * */ public function divideAndRemainder(a:BigInteger):Array { var q:BigInteger = new BigInteger; var r:BigInteger = new BigInteger; divRemTo(a, q, r); return [q,r]; } /** * * this *= n, this >=0, 1 < n < DV * * @param n * */ bi_internal function dMultiply(n:int):void { a[t] = am(0, n-1, this, 0, 0, t); ++t; clamp(); } /** * * this += n << w words, this >= 0 * * @param n * @param w * */ bi_internal function dAddOffset(n:int, w:int):void { while (t<=w) { a[t++] = 0; } a[w] += n; while (a[w] >= DV) { a[w] -= DV; if (++w >= t) { a[t++] = 0; } ++a[w]; } } /** * * @param e * @return this^e * */ public function pow(e:int):BigInteger { return exp(e, new NullReduction); } /** * * @param a * @param n * @param r = lower n words of "this * a", a.t <= n * */ bi_internal function multiplyLowerTo(a:BigInteger, n:int, r:BigInteger):void { var i:int = Math.min(t+a.t, n); r.s = 0; // assumes a, this >= 0 r.t = i; while (i>0) { r.a[--i]=0; } var j:int; for (j=r.t-t;i 0 * */ bi_internal function multiplyUpperTo(a:BigInteger, n:int, r:BigInteger):void { --n; var i:int = r.t = t+a.t-n; r.s = 0; // assumes a,this >= 0 while (--i>=0) { r.a[i] = 0; } for (i=Math.max(n-t,0);i 1) { var g2:BigInteger = new BigInteger; z.sqrTo(g[1], g2); while (n<=km) { g[n] = new BigInteger; z.mulTo(g2, g[n-2], g[n]); n += 2; } } var j:int = e.t-1; var w:int; var is1:Boolean = true; var r2:BigInteger = new BigInteger; var t:BigInteger; i = nbits(e.a[j])-1; while (j>=0) { if (i>=k1) { w = (e.a[j]>>(i-k1))&km; } else { w = (e.a[j]&((1<<(i+1))-1))<<(k1-i); if (j>0) { w |= e.a[j-1]>>(DB+i-k1); } } n = k; while ((w&1)==0) { w >>= 1; --n; } if ((i -= n) <0) { i += DB; --j; } if (is1) { // ret == 1, don't bother squaring or multiplying it g[w].copyTo(r); is1 = false; } else { while (n>1) { z.sqrTo(r, r2); z.sqrTo(r2, r); n -= 2; } if (n>0) { z.sqrTo(r, r2); } else { t = r; r = r2; r2 = t; } z.mulTo(r2, g[w], r); } while (j>=0 && (e.a[j]&(1<0) { x.rShiftTo(g, x); y.rShiftTo(g, y); } while (x.sigNum()>0) { if ((i = x.getLowestSetBit()) >0) { x.rShiftTo(i, x); } if ((i = y.getLowestSetBit()) >0) { y.rShiftTo(i, y); } if (x.compareTo(y) >= 0) { x.subTo(y, x); x.rShiftTo(1, x); } else { y.subTo(x, y); y.rShiftTo(1, y); } } if (g>0) { y.lShiftTo(g, y); } return y; } /** * * @param n * @return this % n, n < 2^DB * */ protected function modInt(n:int):int { if (n<=0) return 0; var d:int = DV%n; var r:int = (s<0)?n-1:0; if (t>0) { if (d==0) { r = a[0]%n; } else { for (var i:int=t-1;i>=0;--i) { r = (d*r+a[i])%n; } } } return r; } /** * * @param m * @return 1/this %m (HAC 14.61) * */ public function modInverse(m:BigInteger):BigInteger { var ac:Boolean = m.isEven(); if ((isEven()&&ac) || m.sigNum()==0) { return BigInteger.ZERO; } var u:BigInteger = m.clone(); var v:BigInteger = clone(); var a:BigInteger = nbv(1); var b:BigInteger = nbv(0); var c:BigInteger = nbv(0); var d:BigInteger = nbv(1); while (u.sigNum()!=0) { while (u.isEven()) { u.rShiftTo(1,u); if (ac) { if (!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); } a.rShiftTo(1,a); } else if (!b.isEven()) { b.subTo(m,b); } b.rShiftTo(1,b); } while (v.isEven()) { v.rShiftTo(1,v); if (ac) { if (!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); } c.rShiftTo(1,c); } else if (!d.isEven()) { d.subTo(m,d); } d.rShiftTo(1,d); } if (u.compareTo(v)>=0) { u.subTo(v,u); if (ac) { a.subTo(c,a); } b.subTo(d,b); } else { v.subTo(u,v); if (ac) { c.subTo(a,c); } d.subTo(b,d); } } if (v.compareTo(BigInteger.ONE) != 0) { return BigInteger.ZERO; } if (d.compareTo(m) >= 0) { return d.subtract(m); } if (d.sigNum()<0) { d.addTo(m,d); } else { return d; } if (d.sigNum()<0) { return d.add(m); } else { return d; } } /** * * @param t * @return primality with certainty >= 1-.5^t * */ public function isProbablePrime(t:int):Boolean { var i:int; var x:BigInteger = abs(); if (x.t == 1 && x.a[0]<=lowprimes[lowprimes.length-1]) { for (i=0;i>1; if (t>lowprimes.length) { t = lowprimes.length; } var a:BigInteger = new BigInteger; for (var i:int=0;ibits) subTo(BigInteger.ONE.shiftLeft(bits-1),this); } } } }