PageRenderTime 38ms CodeModel.GetById 17ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 1ms

/js/lib/Socket.IO-node/support/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/rsa/RSAKey.as

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
ActionScript | 339 lines | 256 code | 31 blank | 52 comment | 42 complexity | d7a68d698220691be6776b1ca97e1049 MD5 | raw file
  1/**
  2 * RSAKey
  3 * 
  4 * An ActionScript 3 implementation of RSA + PKCS#1 (light version)
  5 * Copyright (c) 2007 Henri Torgemane
  6 * 
  7 * Derived from:
  8 * 		The jsbn library, Copyright (c) 2003-2005 Tom Wu
  9 * 
 10 * See LICENSE.txt for full license information.
 11 */
 12package com.hurlant.crypto.rsa
 13{
 14	import com.hurlant.crypto.prng.Random;
 15	import com.hurlant.math.BigInteger;
 16	import com.hurlant.util.Memory;
 17	
 18	import flash.utils.ByteArray;
 19	import com.hurlant.crypto.hash.IHash;
 20	import com.hurlant.util.Hex;
 21	import com.hurlant.util.der.DER;
 22	import com.hurlant.util.der.OID;
 23	import com.hurlant.util.ArrayUtil;
 24	import com.hurlant.util.der.Type;
 25	import com.hurlant.util.der.Sequence;
 26	import com.hurlant.util.der.ObjectIdentifier;
 27	import com.hurlant.util.der.ByteString;
 28	import com.hurlant.crypto.tls.TLSError;
 29	
 30	/**
 31	 * Current limitations:
 32	 * exponent must be smaller than 2^31.
 33	 */
 34	public class RSAKey
 35	{
 36		// public key
 37		public var e:int;              // public exponent. must be <2^31
 38		public var n:BigInteger; // modulus
 39		// private key
 40		public var d:BigInteger;
 41		// extended private key
 42		public var p:BigInteger;
 43		public var q:BigInteger;
 44		public var dmp1:BigInteger
 45		public var dmq1:BigInteger;
 46		public var coeff:BigInteger;
 47		// flags. flags are cool.
 48		protected var canDecrypt:Boolean;
 49		protected var canEncrypt:Boolean;
 50		
 51		public function RSAKey(N:BigInteger, E:int, 
 52			D:BigInteger=null,
 53			P:BigInteger = null, Q:BigInteger=null,
 54			DP:BigInteger=null, DQ:BigInteger=null,
 55			C:BigInteger=null) {
 56				
 57			this.n = N;
 58			this.e = E;
 59			this.d = D;
 60			this.p = P;
 61			this.q = Q;
 62			this.dmp1 = DP;
 63			this.dmq1 = DQ;
 64			this.coeff = C;
 65			
 66			// adjust a few flags.
 67			canEncrypt = (n!=null&&e!=0);
 68			canDecrypt = (canEncrypt&&d!=null);
 69			
 70			
 71		}
 72
 73
 74		public static function parsePublicKey(N:String, E:String):RSAKey {
 75			return new RSAKey(new BigInteger(N, 16, true), parseInt(E,16));
 76		}
 77		public static function parsePrivateKey(N:String, E:String, D:String, 
 78			P:String=null,Q:String=null, DMP1:String=null, DMQ1:String=null, IQMP:String=null):RSAKey {
 79			if (P==null) {
 80				return new RSAKey(new BigInteger(N,16, true), parseInt(E,16), new BigInteger(D,16, true));
 81			} else {
 82				return new RSAKey(new BigInteger(N,16, true), parseInt(E,16), new BigInteger(D,16, true),
 83					new BigInteger(P,16, true), new BigInteger(Q,16, true),
 84					new BigInteger(DMP1,16, true), new BigInteger(DMQ1, 16, true),
 85					new BigInteger(IQMP, 16, true));
 86			}
 87		}
 88		
 89		public function getBlockSize():uint {
 90			return (n.bitLength()+7)/8;
 91		}
 92		public function dispose():void {
 93			e = 0;
 94			n.dispose();
 95			n = null;
 96			Memory.gc();
 97		}
 98
 99
100		public function encrypt(src:ByteArray, dst:ByteArray, length:uint, pad:Function=null):void {
101			_encrypt(doPublic, src, dst, length, pad, 0x02);
102		}
103		public function decrypt(src:ByteArray, dst:ByteArray, length:uint, pad:Function=null):void {
104			_decrypt(doPrivate2, src, dst, length, pad, 0x02);
105		}
106
107
108		public function sign(src:ByteArray, dst:ByteArray, length:uint, pad:Function = null):void {
109			_encrypt(doPrivate2, src, dst, length, pad, 0x01);
110		}
111		public function verify(src:ByteArray, dst:ByteArray, length:uint, pad:Function = null):void {
112			_decrypt(doPublic, src, dst, length, pad, 0x01);
113		}
114		
115
116
117		private function _encrypt(op:Function, src:ByteArray, dst:ByteArray, length:uint, pad:Function, padType:int):void {
118			// adjust pad if needed
119			if (pad==null) pad = pkcs1pad;
120			// convert src to BigInteger
121			if (src.position >= src.length) {
122				src.position = 0;
123			}
124			var bl:uint = getBlockSize();
125			var end:int = src.position + length;
126			while (src.position<end) {
127				var block:BigInteger = new BigInteger(pad(src, end, bl, padType), bl, true);
128				var chunk:BigInteger = op(block);
129				chunk.toArray(dst);
130			}
131		}
132		private function _decrypt(op:Function, src:ByteArray, dst:ByteArray, length:uint, pad:Function, padType:int):void {
133			// adjust pad if needed
134			if (pad==null) pad = pkcs1unpad;
135			
136			// convert src to BigInteger
137			if (src.position >= src.length) {
138				src.position = 0;
139			}
140			var bl:uint = getBlockSize();
141			var end:int = src.position + length;
142			while (src.position<end) {
143				var block:BigInteger = new BigInteger(src, bl, true);
144				var chunk:BigInteger = op(block);
145				var b:ByteArray = pad(chunk, bl, padType);
146				if (b == null) 
147					 throw new TLSError( "Decrypt error - padding function returned null!", TLSError.decode_error );
148				// if (b != null)
149				dst.writeBytes(b);
150			}
151		}
152		
153		/**
154		 * PKCS#1 pad. type 1 (0xff) or 2, random.
155		 * puts as much data from src into it, leaves what doesn't fit alone.
156		 */
157		private function pkcs1pad(src:ByteArray, end:int, n:uint, type:uint = 0x02):ByteArray {
158			var out:ByteArray = new ByteArray;
159			var p:uint = src.position;
160			end = Math.min(end, src.length, p+n-11);
161			src.position = end;
162			var i:int = end-1;
163			while (i>=p && n>11) {
164				out[--n] = src[i--];
165			}
166			out[--n] = 0;
167			if (type==0x02) { // type 2
168				var rng:Random = new Random;
169				var x:int = 0;
170				while (n>2) {
171					do {
172						x = rng.nextByte();
173					} while (x==0);
174					out[--n] = x;
175				}
176			} else { // type 1
177				while (n>2) {
178					out[--n] = 0xFF;
179				}
180			}
181			out[--n] = type;
182			out[--n] = 0;
183			return out;
184		}
185		
186		/**
187		 * 
188		 * @param src
189		 * @param n
190		 * @param type Not used.
191		 * @return 
192		 * 
193		 */
194		private function pkcs1unpad(src:BigInteger, n:uint, type:uint = 0x02):ByteArray {
195			var b:ByteArray = src.toByteArray();
196			var out:ByteArray = new ByteArray;
197			
198			b.position = 0;
199			var i:int = 0;
200			while (i<b.length && b[i]==0) ++i;
201			if (b.length-i != n-1 || b[i]!=type) {
202				trace("PKCS#1 unpad: i="+i+", expected b[i]=="+type+", got b[i]="+b[i].toString(16));
203				return null;
204			}
205			++i;
206			while (b[i]!=0) {
207				if (++i>=b.length) {
208					trace("PKCS#1 unpad: i="+i+", b[i-1]!=0 (="+b[i-1].toString(16)+")");
209					return null;
210				}
211			}
212			while (++i < b.length) {
213				out.writeByte(b[i]);
214			}
215			out.position = 0;
216			return out;
217		}
218		/**
219		 * Raw pad.
220		 */
221		public function rawpad(src:ByteArray, end:int, n:uint, type:uint = 0):ByteArray {
222			return src;
223		}
224		public function rawunpad(src:BigInteger, n:uint, type:uint = 0):ByteArray {
225			return src.toByteArray();
226		}
227		
228		public function toString():String {
229			return "rsa";
230		}
231		
232		public function dump():String {
233			var s:String= "N="+n.toString(16)+"\n"+
234			"E="+e.toString(16)+"\n";
235			if (canDecrypt) {
236				s+="D="+d.toString(16)+"\n";
237				if (p!=null && q!=null) {
238					s+="P="+p.toString(16)+"\n";
239					s+="Q="+q.toString(16)+"\n";
240					s+="DMP1="+dmp1.toString(16)+"\n";
241					s+="DMQ1="+dmq1.toString(16)+"\n";
242					s+="IQMP="+coeff.toString(16)+"\n";
243				}
244			}
245			return s;
246		}
247		
248		
249		/**
250		 * 
251		 * note: We should have a "nice" variant of this function that takes a callback,
252		 * 		and perform the computation is small fragments, to keep the web browser
253		 * 		usable.
254		 * 
255		 * @param B
256		 * @param E
257		 * @return a new random private key B bits long, using public expt E
258		 * 
259		 */
260		public static function generate(B:uint, E:String):RSAKey {
261			var rng:Random = new Random;
262			var qs:uint = B>>1;
263			var key:RSAKey = new RSAKey(null,0,null);
264			key.e = parseInt(E, 16);
265			var ee:BigInteger = new BigInteger(E,16, true);
266			for (;;) {
267				for (;;) {
268					key.p = bigRandom(B-qs, rng);
269					if (key.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE)==0 &&
270						key.p.isProbablePrime(10)) break;
271				}
272				for (;;) {
273					key.q = bigRandom(qs, rng);
274					if (key.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE)==0 &&
275						key.q.isProbablePrime(10)) break;
276				}
277				if (key.p.compareTo(key.q)<=0) {
278					var t:BigInteger = key.p;
279					key.p = key.q;
280					key.q = t;
281				}
282				var p1:BigInteger = key.p.subtract(BigInteger.ONE);
283				var q1:BigInteger = key.q.subtract(BigInteger.ONE);
284				var phi:BigInteger = p1.multiply(q1);
285				if (phi.gcd(ee).compareTo(BigInteger.ONE)==0) {
286					key.n = key.p.multiply(key.q);
287					key.d = ee.modInverse(phi);
288					key.dmp1 = key.d.mod(p1);
289					key.dmq1 = key.d.mod(q1);
290					key.coeff = key.q.modInverse(key.p);
291					break;
292				}
293			}
294			return key;
295		}
296		
297		protected static function bigRandom(bits:int, rnd:Random):BigInteger {
298			if (bits<2) return BigInteger.nbv(1);
299			var x:ByteArray = new ByteArray;
300			rnd.nextBytes(x, (bits>>3));
301			x.position = 0;
302			var b:BigInteger = new BigInteger(x,0,true);
303			b.primify(bits, 1);
304			return b;
305		}
306		
307		protected function doPublic(x:BigInteger):BigInteger {
308			return x.modPowInt(e, n);
309		}
310		
311		protected function doPrivate2(x:BigInteger):BigInteger {
312			if (p==null && q==null) {
313				return x.modPow(d,n);
314			}
315			
316			var xp:BigInteger = x.mod(p).modPow(dmp1, p);
317			var xq:BigInteger = x.mod(q).modPow(dmq1, q);
318			
319			while (xp.compareTo(xq)<0) {
320				xp = xp.add(p);
321			}
322			var r:BigInteger = xp.subtract(xq).multiply(coeff).mod(p).multiply(q).add(xq);
323			
324			return r;
325		}
326		
327		protected function doPrivate(x:BigInteger):BigInteger {
328			if (p==null || q==null) {
329				return x.modPow(d, n);
330			}
331			// TODO: re-calculate any missing CRT params
332			var xp:BigInteger = x.mod(p).modPow(dmp1, p);
333			var xq:BigInteger = x.mod(q).modPow(dmq1, q);
334			
335			while (xp.compareTo(xq)<0) {
336				xp = xp.add(p);
337			}
338			return xp.subtract(xq).multiply(coeff).mod(p).multiply(q).add(xq);
339		}
340		
341		
342	}
343}