/** * TLSSecurityParameters * * This class encapsulates all the security parameters that get negotiated * during the TLS handshake. It also holds all the key derivation methods. * Copyright (c) 2007 Henri Torgemane * * Patched by Bobby Parker (sh0rtwave@gmail.com) * * See LICENSE.txt for full license information. */ package com.hurlant.crypto.tls { import com.hurlant.crypto.hash.MD5; import com.hurlant.crypto.hash.SHA1; import com.hurlant.crypto.prng.TLSPRF; import com.hurlant.util.Hex; import flash.utils.ByteArray; import com.hurlant.crypto.rsa.RSAKey; public class TLSSecurityParameters implements ISecurityParameters { // COMPRESSION public static const COMPRESSION_NULL:uint = 0; // This is probably not smart. Revise this to use all settings from TLSConfig, since this shouldn't really know about // user settings, those are best handled from the engine at a session level. public static var IGNORE_CN_MISMATCH:Boolean = true; public static var ENABLE_USER_CLIENT_CERTIFICATE:Boolean = false; public static var USER_CERTIFICATE:String; private var cert:ByteArray; // Local Cert private var key:RSAKey; // local key private var entity:uint; // SERVER | CLIENT private var bulkCipher:uint; // BULK_CIPHER_* private var cipherType:uint; // STREAM_CIPHER | BLOCK_CIPHER private var keySize:uint; private var keyMaterialLength:uint; private var IVSize:uint; private var macAlgorithm:uint; // MAC_* private var hashSize:uint; private var compression:uint; // COMPRESSION_NULL private var masterSecret:ByteArray; // 48 bytes private var clientRandom:ByteArray; // 32 bytes private var serverRandom:ByteArray; // 32 bytes private var ignoreCNMismatch:Boolean = true; private var trustAllCerts:Boolean = false; private var trustSelfSigned:Boolean = false; public static const PROTOCOL_VERSION:uint = 0x0301; private var tlsDebug:Boolean = false; // not strictly speaking part of this, but yeah. public var keyExchange:uint; public function TLSSecurityParameters(entity:uint, localCert:ByteArray = null, localKey:RSAKey = null) { this.entity = entity; reset(); key = localKey; cert = localCert; } public function get version() : uint { return PROTOCOL_VERSION; } public function reset():void { bulkCipher = BulkCiphers.NULL; cipherType = BulkCiphers.BLOCK_CIPHER; macAlgorithm = MACs.NULL; compression = COMPRESSION_NULL; masterSecret = null; } public function getBulkCipher():uint { return bulkCipher; } public function getCipherType():uint { return cipherType; } public function getMacAlgorithm():uint { return macAlgorithm; } public function setCipher(cipher:uint):void { bulkCipher = CipherSuites.getBulkCipher(cipher); cipherType = BulkCiphers.getType(bulkCipher); keySize = BulkCiphers.getExpandedKeyBytes(bulkCipher); // 8 keyMaterialLength = BulkCiphers.getKeyBytes(bulkCipher); // 5 IVSize = BulkCiphers.getIVSize(bulkCipher); keyExchange = CipherSuites.getKeyExchange(cipher); macAlgorithm = CipherSuites.getMac(cipher); hashSize = MACs.getHashSize(macAlgorithm); } public function setCompression(algo:uint):void { compression = algo; } public function setPreMasterSecret(secret:ByteArray):void { // compute master_secret var seed:ByteArray = new ByteArray; seed.writeBytes(clientRandom, 0, clientRandom.length); seed.writeBytes(serverRandom, 0, serverRandom.length); var prf:TLSPRF = new TLSPRF(secret, "master secret", seed); masterSecret = new ByteArray; prf.nextBytes(masterSecret, 48); if (tlsDebug) trace("Master Secret: " + Hex.fromArray( masterSecret, true )); } public function setClientRandom(secret:ByteArray):void { clientRandom = secret; } public function setServerRandom(secret:ByteArray):void { serverRandom = secret; } public function get useRSA():Boolean { return KeyExchanges.useRSA(keyExchange); } public function computeVerifyData(side:uint, handshakeMessages:ByteArray):ByteArray { var seed:ByteArray = new ByteArray; var md5:MD5 = new MD5; if (tlsDebug) trace("Handshake value: " + Hex.fromArray(handshakeMessages, true )); seed.writeBytes(md5.hash(handshakeMessages),0,md5.getHashSize()); var sha:SHA1 = new SHA1; seed.writeBytes(sha.hash(handshakeMessages),0,sha.getHashSize()); if (tlsDebug) trace("Seed in: " + Hex.fromArray(seed, true )); var prf:TLSPRF = new TLSPRF(masterSecret, (side==TLSEngine.CLIENT) ? "client finished" : "server finished", seed); var out:ByteArray = new ByteArray; prf.nextBytes(out, 12); if (tlsDebug) trace("Finished out: " + Hex.fromArray(out, true )); out.position = 0; return out; } // client side certficate check - This is probably incorrect somehow public function computeCertificateVerify( side:uint, handshakeMessages:ByteArray ):ByteArray { var seed:ByteArray = new ByteArray; var md5:MD5 = new MD5; seed.writeBytes(md5.hash(handshakeMessages),0,md5.getHashSize()); var sha:SHA1 = new SHA1; seed.writeBytes(sha.hash(handshakeMessages),0,sha.getHashSize()); // Now that I have my hashes of existing handshake messages (which I'm not sure about the length of yet) then // Sign that with my private key seed.position = 0; var out:ByteArray = new ByteArray(); key.sign( seed, out, seed.bytesAvailable); out.position = 0; return out; } public function getConnectionStates():Object { if (masterSecret != null) { var seed:ByteArray = new ByteArray; seed.writeBytes(serverRandom, 0, serverRandom.length); seed.writeBytes(clientRandom, 0, clientRandom.length); var prf:TLSPRF = new TLSPRF(masterSecret, "key expansion", seed); var client_write_MAC:ByteArray = new ByteArray; prf.nextBytes(client_write_MAC, hashSize); var server_write_MAC:ByteArray = new ByteArray; prf.nextBytes(server_write_MAC, hashSize); var client_write_key:ByteArray = new ByteArray; prf.nextBytes(client_write_key, keyMaterialLength); var server_write_key:ByteArray = new ByteArray; prf.nextBytes(server_write_key, keyMaterialLength); var client_write_IV:ByteArray = new ByteArray; prf.nextBytes(client_write_IV, IVSize); var server_write_IV:ByteArray = new ByteArray; prf.nextBytes(server_write_IV, IVSize); var client_write:TLSConnectionState = new TLSConnectionState( bulkCipher, cipherType, macAlgorithm, client_write_MAC, client_write_key, client_write_IV); var server_write:TLSConnectionState = new TLSConnectionState( bulkCipher, cipherType, macAlgorithm, server_write_MAC, server_write_key, server_write_IV); if (entity == TLSEngine.CLIENT) { return {read:server_write, write:client_write}; } else { return {read:client_write, write:server_write}; } } else { return {read:new TLSConnectionState, write:new TLSConnectionState}; } } } }