/** * 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.HMAC; 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 TLSConnectionState 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 hmac:HMAC; // sequence number. uint64 private var seq_lo:uint; private var seq_hi:uint; public function TLSConnectionState( bulkCipher:uint=0, cipherType:uint=0, macAlgorithm:uint=0, mac:ByteArray=null, key:ByteArray=null, IV:ByteArray=null) { this.bulkCipher = bulkCipher; this.cipherType = cipherType; this.macAlgorithm = macAlgorithm; MAC_write_secret = mac; hmac = MACs.getHMAC(macAlgorithm); CIPHER_key = key; CIPHER_IV = IV; cipher = BulkCiphers.getCipher(bulkCipher, key, 0x0301); 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 { // block cipher var nextIV:ByteArray = new ByteArray; nextIV.writeBytes(p, p.length-CIPHER_IV.length, CIPHER_IV.length); cipher.decrypt(p); CIPHER_IV = nextIV; ivmode.IV = nextIV; } if (macAlgorithm!=MACs.NULL) { var data:ByteArray = new ByteArray; var len:uint = p.length - hmac.getHashSize(); data.writeUnsignedInt(seq_hi); data.writeUnsignedInt(seq_lo); data.writeByte(type); data.writeShort(TLSSecurityParameters.PROTOCOL_VERSION); data.writeShort(len); if (len!=0) { data.writeBytes(p, 0, len); } var mac:ByteArray = hmac.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, hmac.getHashSize()); if (ArrayUtil.equals(mac, 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:ByteArray = null; if (macAlgorithm!=MACs.NULL) { var data:ByteArray = new ByteArray; data.writeUnsignedInt(seq_hi); data.writeUnsignedInt(seq_lo); data.writeByte(type); data.writeShort(TLSSecurityParameters.PROTOCOL_VERSION); data.writeShort(p.length); if (p.length!=0) { data.writeBytes(p, 0, p.length); } mac = hmac.compute(MAC_write_secret, data); p.position = p.length; p.writeBytes(mac); } 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++; // compression is a nop. return p; } } }