/** * TLSConnectionState * * This class encapsulates the read or write state of a TLS connection, * and implementes the encrypting and hashing of packets. * Copyright (c) 2007 Henri Torgemane * * See LICENSE.txt for full license information. */ package com.hurlant.crypto.tls { import flash.utils.IDataInput; import flash.utils.ByteArray; import com.hurlant.crypto.hash.MD5; import com.hurlant.crypto.hash.MAC; import com.hurlant.crypto.hash.IHash; import com.hurlant.crypto.symmetric.ICipher; import com.hurlant.crypto.symmetric.IVMode; import com.hurlant.util.Hex; import com.hurlant.util.ArrayUtil; public class SSLConnectionState implements IConnectionState { // compression state // cipher state private var bulkCipher:uint; private var cipherType:uint; private var CIPHER_key:ByteArray; private var CIPHER_IV:ByteArray; private var cipher:ICipher; private var ivmode:IVMode; // mac secret private var macAlgorithm:uint; private var MAC_write_secret:ByteArray; private var mac:MAC; // sequence number. uint64 private var seq_lo:uint = 0x0; private var seq_hi:uint = 0x0; public function SSLConnectionState( bulkCipher:uint=0, cipherType:uint=0, macAlgorithm:uint=0, mac_enc:ByteArray=null, key:ByteArray=null, IV:ByteArray=null) { this.bulkCipher = bulkCipher; this.cipherType = cipherType; this.macAlgorithm = macAlgorithm; MAC_write_secret = mac_enc; mac = MACs.getMAC(macAlgorithm); CIPHER_key = key; CIPHER_IV = IV; cipher = BulkCiphers.getCipher(bulkCipher, key, 0x0300); if (cipher is IVMode) { ivmode = cipher as IVMode; ivmode.IV = IV; } } public function decrypt(type:uint, length:uint, p:ByteArray):ByteArray { // decompression is a nop. if (cipherType == BulkCiphers.STREAM_CIPHER) { if (bulkCipher == BulkCiphers.NULL) { // no-op } else { cipher.decrypt(p); } } else { p.position = 0; // block cipher if (bulkCipher == BulkCiphers.NULL) { } else { var nextIV:ByteArray = new ByteArray; nextIV.writeBytes(p, p.length-CIPHER_IV.length, CIPHER_IV.length); p.position = 0; cipher.decrypt(p); CIPHER_IV = nextIV; ivmode.IV = nextIV; } } if (macAlgorithm!=MACs.NULL) { // there will be CTX delay here as well, // I should probably optmize the hell out of it var data:ByteArray = new ByteArray; var len:uint = p.length - mac.getHashSize(); data.writeUnsignedInt(seq_hi); data.writeUnsignedInt(seq_lo); data.writeByte(type); data.writeShort(len); if (len!=0) { data.writeBytes(p, 0, len); } var mac_enc:ByteArray = mac.compute(MAC_write_secret, data); // compare "mac" with the last X bytes of p. var mac_received:ByteArray = new ByteArray; mac_received.writeBytes(p, len, mac.getHashSize()); if (ArrayUtil.equals(mac_enc, mac_received)) { // happy happy joy joy } else { throw new TLSError("Bad Mac Data", TLSError.bad_record_mac); } p.length = len; p.position = 0; } // increment seq seq_lo++; if (seq_lo==0) seq_hi++; return p; } public function encrypt(type:uint, p:ByteArray):ByteArray { var mac_enc:ByteArray = null; if (macAlgorithm!=MACs.NULL) { var data:ByteArray = new ByteArray; // data.writeUnsignedInt(seq); // Sequence data.writeUnsignedInt(seq_hi); data.writeUnsignedInt(seq_lo); // Type data.writeByte(type); // Length data.writeShort(p.length); // The data if (p.length!=0) { data.writeBytes(p); } // trace("data for the MAC: " + Hex.fromArray(data)); mac_enc = mac.compute(MAC_write_secret, data); // trace("MAC: " + Hex.fromArray( mac_enc )); p.position = p.length; p.writeBytes(mac_enc); } // trace("Record to encrypt: " + Hex.fromArray(p)); p.position = 0; if (cipherType == BulkCiphers.STREAM_CIPHER) { // stream cipher if (bulkCipher == BulkCiphers.NULL) { // no-op } else { cipher.encrypt(p); } } else { // block cipher cipher.encrypt(p); // adjust IV var nextIV:ByteArray = new ByteArray; nextIV.writeBytes(p, p.length-CIPHER_IV.length, CIPHER_IV.length); CIPHER_IV = nextIV; ivmode.IV = nextIV; } // increment seq seq_lo++; if (seq_lo==0) seq_hi++; return p; } } }