/**
 * IVMode
 * 
 * An abstract class for confidentialy modes that rely on an initialization vector.
 * Copyright (c) 2007 Henri Torgemane
 * 
 * See LICENSE.txt for full license information.
 */
package com.hurlant.crypto.symmetric
{
	import com.hurlant.crypto.prng.Random;
	import com.hurlant.crypto.tests.TestCase;
	import com.hurlant.util.Memory;
	
	import flash.utils.ByteArray;
	
	/**
	 * An "abtract" class to avoid redundant code in subclasses
	 */
	public class IVMode
	{
		protected var key:ISymmetricKey;
		protected var padding:IPad;
		// random generator used to generate IVs
		protected var prng:Random;
		// optional static IV. used for testing only.
		protected var iv:ByteArray;
		// generated IV is stored here.
		protected var lastIV:ByteArray;
		protected var blockSize:uint;
		
		
		public function IVMode(key:ISymmetricKey, padding:IPad = null) {
			this.key = key;
			blockSize = key.getBlockSize();
			if (padding == null) {
				padding = new PKCS5(blockSize);
			} else {
				padding.setBlockSize(blockSize);
			}
			this.padding = padding;
			
			prng = new Random;
			iv = null;
			lastIV = new ByteArray;
		}
		
		public function getBlockSize():uint {
			return key.getBlockSize();
		}
		public function dispose():void {
			var i:uint;
			if (iv != null) {
				for (i=0;i<iv.length;i++) {
					iv[i] = prng.nextByte();
				}
				iv.length=0;
				iv = null;
			}
			if (lastIV != null) {
				for (i=0;i<iv.length;i++) {
					lastIV[i] = prng.nextByte();
				}
				lastIV.length=0;
				lastIV=null;
			}
			key.dispose();
			key = null;
			padding = null;
			prng.dispose();
			prng = null;
			Memory.gc();
		}
		/**
		 * Optional function to force the IV value.
		 * Normally, an IV gets generated randomly at every encrypt() call.
		 * Also, use this to set the IV before calling decrypt()
		 * (if not set before decrypt(), the IV is read from the beginning of the stream.)
		 */
		public function set IV(value:ByteArray):void {
			iv = value;
			lastIV.length=0;
			lastIV.writeBytes(iv);
		}
		public function get IV():ByteArray {
			return lastIV;
		}
		
		protected function getIV4e():ByteArray {
			var vec:ByteArray = new ByteArray;
			if (iv) {
				vec.writeBytes(iv);
			} else {
				prng.nextBytes(vec, blockSize);
			}
			lastIV.length=0;
			lastIV.writeBytes(vec);
			return vec;
		}
		protected function getIV4d():ByteArray {
			var vec:ByteArray = new ByteArray;
			if (iv) {
				vec.writeBytes(iv);
			} else {
				throw new Error("an IV must be set before calling decrypt()");
			}
			return vec;
		}
	}
}