/**
 * MAC
 * 
 * An ActionScript 3 implementation of MAC, Message Authentication Code
 * for use with SSL 3.0.
 * Loosely copyrighted by Bobby Parker.
 * As3crypto copyrighted by Henri Torgemane.
 * 
 * See LICENSE.txt for full license information.
 */
package com.hurlant.crypto.hash
{
    import flash.utils.ByteArray;
    import com.hurlant.util.Hex;
    
    public class MAC implements IHMAC
    {
        private var hash:IHash;
        private var bits:uint;
        private var pad_1:ByteArray;
        private var pad_2:ByteArray;
        private var innerHash:ByteArray;
        private var outerHash:ByteArray;
        private var outerKey:ByteArray;
        private var innerKey:ByteArray;
        /**
         * Create a MAC object (for SSL 3.0 ) and 
         * optionally a number of bits to return. 
         * The MAC will be truncated to that size if needed.
         */
        public function MAC(hash:IHash, bits:uint=0) {
            this.hash = hash;
            this.bits = bits;
            innerHash = new ByteArray();
            outerHash = new ByteArray();
            innerKey = new ByteArray();
            outerKey = new ByteArray();


            if (hash != null) { 
                var pad_size:int = hash.getPadSize();
                pad_1 = new ByteArray();
                pad_2 = new ByteArray();
            
                for (var x:int = 0; x < pad_size; x++) {
                    pad_1.writeByte(0x36);
                    pad_2.writeByte(0x5c);
                }
            }
        }
        
        public function setPadSize(pad_size:int) : void {    }
        
        public function getHashSize():uint {
            if (bits!=0) {
                return bits/8;
            } else {
                return hash.getHashSize();
            }
        }
        
        
        /**
         * Compute a MAC using a key and some data.
         * 
         */ 
        public function compute(key:ByteArray, data:ByteArray):ByteArray {
            // take that incoming key and do hash(key + pad_2 + hash(key + pad_1 + sequence + length + record)
            // note that data =  (sequence + type + length + record)

             if (pad_1 == null) {
                 var pad_size:int = hash.getPadSize();
                pad_1 = new ByteArray();
                pad_2 = new ByteArray();
            
                for (var x:int = 0; x < pad_size; x++) {
                    pad_1.writeByte(0x36);
                    pad_2.writeByte(0x5c);
                }
             }
             
             // Do some preliminary checking on stuff
             /* 
             if (key.length > hash.getInputSize()) {
                 hashKey = hash.hash(key); 
             } else {
                 hashKey = new ByteArray;
                 hashKey.writeBytes(key); 
             }
             
             while (hashKey.length < hash.getInputSize() ) {
                 hashKey[hashKey.length] = 0;
             } */
             // Henri's conventions work just fine here..
             
             innerKey.length = 0;
            outerKey.length = 0;
             // trace("MAC Key: " + Hex.fromArray(key));
            // trace("Key Length: " + key.length);
            // trace("Pad_1 : " + Hex.fromArray(pad_1));
             // inner hash calc
             innerKey.writeBytes(key);
             innerKey.writeBytes(pad_1);
             innerKey.writeBytes(data);
             // trace("MAC Inner Key: " + Hex.fromArray(innerKey));
            
             innerHash = hash.hash(innerKey);
             // trace("MAC Inner Hash: " + Hex.fromArray(innerHash));
            
            // outer hash calc             
             outerKey.writeBytes(key);
             outerKey.writeBytes(pad_2);
             outerKey.writeBytes(innerHash);
            
             // trace("MAC Outer Key: " + Hex.fromArray(outerKey));
             outerHash = hash.hash(outerKey);
             
             
             if (bits > 0 && bits < 8*outerHash.length) {
                 outerHash.length = bits/8;
             }
             
             // trace("MAC for record: " + Hex.fromArray(outerHash));             
             return outerHash;

        }
        
        public function dispose():void {
            hash = null;
            bits = 0;
        }
        public function toString():String {
            return "mac-"+(bits>0?bits+"-":"")+hash.toString();
        }
        
    }
}