Bug:Fix file validation issue
[vnfsdk/refrepo.git] / vnfmarket / src / main / webapp / vnfmarket / node_modules / socket.io-client / lib / vendor / web-socket-js / flash-src / com / hurlant / crypto / tls / TLSEngine.as
1 /**
2  * TLSEngine
3  * 
4  * A TLS protocol implementation.
5  * See comment below for some details.
6  * Copyright (c) 2007 Henri Torgemane
7  * 
8  * Patched(heavily) by Bobby Parker (shortwave@gmail.com)
9  * 
10  * See LICENSE.txt for full license information.
11  */
12 package com.hurlant.crypto.tls {
13         import com.hurlant.crypto.cert.X509Certificate;
14         import com.hurlant.crypto.cert.X509CertificateCollection;
15         import com.hurlant.crypto.prng.Random;
16         import com.hurlant.util.ArrayUtil;
17         import com.hurlant.util.Hex;
18         
19         import flash.events.Event;
20         import flash.events.EventDispatcher;
21         import flash.events.ProgressEvent;
22         import flash.utils.ByteArray;
23         import flash.utils.IDataInput;
24         import flash.utils.IDataOutput;
25         import flash.utils.clearTimeout;
26         import flash.utils.setTimeout;
27         import com.hurlant.crypto.prng.ARC4;
28
29
30         [Event(name="close", type="flash.events.Event")]
31         [Event(name="socketData", type="flash.events.ProgressEvent")]
32         [Event(name="ready", type="com.hurlant.crypto.tls.TLSEvent")]
33         [Event(name="data", type="com.hurlant.crypto.tls.TLSEvent")]
34         
35         /**
36          * The heart of the TLS protocol.
37          * This class can work in server or client mode.
38          * 
39          * This doesn't fully implement the TLS protocol.
40          * 
41          * Things missing that I'd like to add:
42          * - support for client-side certificates
43          * - general code clean-up to make sure we don't have gaping securite holes
44          * 
45          * Things that aren't there that I won't add:
46          * - support for "export" cypher suites (deprecated in later TLS versions)
47          * - support for "anon" cypher suites (deprecated in later TLS versions)
48          * 
49          * Things that I'm unsure about adding later:
50          * - compression. Compressing encrypted streams is barely worth the CPU cycles.
51          * - diffie-hellman based key exchange mechanisms. Nifty, but would we miss it?
52          * 
53          * @author henri
54          * 
55          */
56         public class TLSEngine extends EventDispatcher {
57                 
58                 public static const SERVER:uint = 0;
59                 public static const CLIENT:uint = 1;
60                 public var protocol_version:uint;
61
62
63         
64                 private static const PROTOCOL_HANDSHAKE:uint = 22;
65                 private static const PROTOCOL_ALERT:uint = 21;
66                 private static const PROTOCOL_CHANGE_CIPHER_SPEC:uint = 20;
67                 private static const PROTOCOL_APPLICATION_DATA:uint = 23;
68
69
70                 private static const STATE_NEW:uint = 0; // brand new. nothing happened yet
71                 private static const STATE_NEGOTIATING:uint = 1; // we're figuring out what to use
72                 private static const STATE_READY:uint = 2; // we're ready for AppData stuff to go over us.
73                 private static const STATE_CLOSED:uint = 3; // we're done done.
74                 
75                 private var _entity:uint; // SERVER | CLIENT
76                 private var _config:TLSConfig;
77                 
78                 private var _state:uint;
79                 
80                 private var _securityParameters:ISecurityParameters;
81                 
82                 private var _currentReadState:IConnectionState;
83                 private var _currentWriteState:IConnectionState;
84                 private var _pendingReadState:IConnectionState;
85                 private var _pendingWriteState:IConnectionState;
86                 
87                 private var _handshakePayloads:ByteArray;
88                 private var _handshakeRecords:ByteArray; // For client-side certificate verify 
89                 
90                 private var _iStream:IDataInput;
91                 private var _oStream:IDataOutput;
92                 
93                 // temporary store for X509 certs received by this engine.
94                 private var _store:X509CertificateCollection;
95                 // the main certificate received from the other side.
96                 private var _otherCertificate:X509Certificate;
97                 
98                 public function get peerCertificate() : X509Certificate {
99                         return _otherCertificate;
100                 }
101                 // If this isn't null, we expect this identity to be found in the Cert's Subject CN.
102                 private var _otherIdentity:String;
103                 
104                 // The client-side cert
105                 private var _myCertficate:X509Certificate;
106                 // My Identity
107                 private var _myIdentity:String;
108                 
109                 /**
110                  * 
111                  * @param config                A TLSConfig instance describing how we're supposed to work
112                  * @param iStream               An input stream to read TLS data from
113                  * @param oStream               An output stream to write TLS data to
114                  * @param otherIdentity An optional identifier. If set, this will be checked against the Subject CN of the other side's certificate.
115                  * 
116                  */
117                 function TLSEngine(config:TLSConfig, iStream:IDataInput, oStream:IDataOutput, otherIdentity:String = null) {
118                         _entity = config.entity;
119                         _config = config;
120                         _iStream = iStream;
121                         _oStream = oStream;
122                         _otherIdentity = otherIdentity;
123                         
124                         _state = STATE_NEW;
125                         
126                         // Pick the right set of callbacks
127                         _entityHandshakeHandlers = _entity == CLIENT ? handshakeHandlersClient : handshakeHandlersServer;
128                         
129                         // setting up new security parameters needs to be controlled by...something.
130                         if (_config.version == SSLSecurityParameters.PROTOCOL_VERSION) {
131                                 _securityParameters = new SSLSecurityParameters(_entity);
132                         } else {
133                                 _securityParameters = new TLSSecurityParameters(_entity, _config.certificate, _config.privateKey);
134                         }
135                         protocol_version = _config.version;
136                         
137                         // So this...why is it here, other than to preclude a possible null pointer situation?
138                         var states:Object = _securityParameters.getConnectionStates();
139                         
140                         _currentReadState = states.read;
141                         _currentWriteState = states.write;
142                         
143                         _handshakePayloads = new ByteArray;
144                         
145                         _store = new X509CertificateCollection;
146                 }
147                 
148                 /**
149                  * This starts the TLS negotiation for a TLS Client.
150                  * 
151                  * This is a no-op for a TLS Server.
152                  * 
153                  */
154                 public function start():void {
155                         if (_entity == CLIENT) {
156                                 try {
157                                         startHandshake();
158                                 } catch (e:TLSError) {
159                                         handleTLSError(e);
160                                 }
161                         }
162                 }
163                 
164                 
165                 public function dataAvailable(e:* = null):void {
166                         if (_state == STATE_CLOSED) return; // ignore
167                         try {
168                                 parseRecord(_iStream);
169                         } catch (e:TLSError) {
170                                 handleTLSError(e);
171                         }
172                 }
173                 
174                 public function close(e:TLSError = null):void {
175                         if (_state == STATE_CLOSED) return; // ignore
176                         // ok. send an Alert to let the peer know
177                         var rec:ByteArray = new ByteArray;
178                         if (e==null && _state != STATE_READY) {
179                                 // use canceled while handshaking. be nice about it
180                                 rec[0] = 1;
181                                 rec[1] = TLSError.user_canceled;
182                                 sendRecord(PROTOCOL_ALERT, rec);
183                         }
184                         rec[0] = 2;
185                         if (e == null) {
186                                 rec[1] = TLSError.close_notify;
187                         } else {
188                                 rec[1] = e.errorID;
189                                 trace("TLSEngine shutdown triggered by "+e);
190                         }
191                         sendRecord(PROTOCOL_ALERT, rec);
192                         
193                         _state = STATE_CLOSED;
194                         dispatchEvent(new Event(Event.CLOSE));
195                 }
196                 
197                 private var _packetQueue:Array = [];
198                 private function parseRecord(stream:IDataInput):void {
199                         var p:ByteArray;
200                         while(_state!=STATE_CLOSED && stream.bytesAvailable>4) {
201                                 
202                                 if (_packetQueue.length>0) {
203                                         var packet:Object = _packetQueue.shift();
204                                         p = packet.data;
205                                         if (stream.bytesAvailable+p.length>=packet.length) {
206                                                 // we have a whole packet. put together.
207                                                 stream.readBytes(p, p.length, packet.length-p.length);
208                                                 parseOneRecord(packet.type, packet.length, p);
209                                                 // do another loop to parse any leftover record
210                                                 continue;
211                                         } else {
212                                                 // not enough. grab the data and park it.
213                                                 stream.readBytes(p, p.length, stream.bytesAvailable);
214                                                 _packetQueue.push(packet);
215                                                 continue;
216                                         }
217                                 }
218
219
220                                 var type:uint = stream.readByte();
221                                 var ver:uint = stream.readShort();
222                                 var length:uint = stream.readShort();
223                                 if (length>16384+2048) { // support compression and encryption overhead.
224                                         throw new TLSError("Excessive TLS Record length: "+length, TLSError.record_overflow);
225                                 }
226                                 // Can pretty much assume that if I'm here, I've got a default config, so let's use it.
227                                 if (ver != _securityParameters.version ) {
228                                         throw new TLSError("Unsupported TLS version: "+ver.toString(16), TLSError.protocol_version);
229                                 }
230
231                                 p = new ByteArray;
232                                 var actualLength:uint = Math.min(stream.bytesAvailable, length);
233                                 stream.readBytes(p, 0, actualLength);
234                                 if (actualLength == length) {
235                                         parseOneRecord(type, length, p);
236                                 } else {
237                                         _packetQueue.push({type:type, length:length, data:p});
238                                 }
239                         }
240                 }
241                 
242                 
243                 // Protocol handler map, provides a mapping of protocol types to individual packet handlers
244                 private var protocolHandlers:Object = { 23 : parseApplicationData, // PROTOCOL_APPLICATION_DATA
245                                                                                           22 : parseHandshake, // PROTOCOL_HANDSHAKE
246                                                                                           21 : parseAlert, // PROTOCOL_ALERT
247                                                                                           20 : parseChangeCipherSpec }; // PROTOCOL_CHANGE_CIPHER_SPEC
248                 
249                 /**
250                  * Modified to support the notion of a handler map(see above ), since it makes for better clarity (IMHO of course).
251                  */
252                 private function parseOneRecord(type:uint, length:uint, p:ByteArray):void {
253                         p = _currentReadState.decrypt(type, length, p);
254                         if (p.length>16384) { 
255                                 throw new TLSError("Excessive Decrypted TLS Record length: "+p.length, TLSError.record_overflow);
256                         }
257                         if (protocolHandlers.hasOwnProperty( type )) {
258                                 while( p != null)
259                                         p = protocolHandlers[ type ]( p );
260                         } else {
261                                 throw new TLSError("Unsupported TLS Record Content Type: "+type.toString( 16 ), TLSError.unexpected_message);
262                         }
263                 }
264                 
265                 ///////// handshake handling
266                 // session identifier
267                 // peer certificate
268                 // compression method
269                 // cipher spec
270                 // master secret
271                 // is resumable
272                 private static const HANDSHAKE_HELLO_REQUEST:uint = 0;
273                 private static const HANDSHAKE_CLIENT_HELLO:uint = 1;
274                 private static const HANDSHAKE_SERVER_HELLO:uint = 2;
275                 private static const HANDSHAKE_CERTIFICATE:uint = 11;
276                 private static const HANDSHAKE_SERVER_KEY_EXCHANGE:uint = 12;
277                 private static const HANDSHAKE_CERTIFICATE_REQUEST:uint = 13;
278                 private static const HANDSHAKE_HELLO_DONE:uint = 14;
279                 private static const HANDSHAKE_CERTIFICATE_VERIFY:uint = 15;
280                 private static const HANDSHAKE_CLIENT_KEY_EXCHANGE:uint = 16;
281                 private static const HANDSHAKE_FINISHED:uint = 20;
282
283                 // Server handshake handler map
284                 private var handshakeHandlersServer:Object = { 0 : notifyStateError, // HANDSHAKE_HELLO_REQUEST
285                                                                                                            1 : parseHandshakeClientHello, // HANDSHAKE_CLIENT_HELLO
286                                                                                                            2 : notifyStateError, // HANDSHAKE_SERVER_HELLO 
287                                                                                                            11 : loadCertificates, // HANDSHAKE_CERTIFICATE
288                                                                                                            12 : notifyStateError, // HANDSHAKE_SERVER_KEY_EXCHANGE
289                                                                                                            13 : notifyStateError, // HANDSHAKE_CERTIFICATE_REQUEST
290                                                                                                            14 : notifyStateError, // HANDSHAKE_HELLO_DONE
291                                                                                                            15 : notifyStateError, // HANDSHAKE_CERTIFICATE_VERIFY
292                                                                                                            16 : parseHandshakeClientKeyExchange, // HANDSHAKE_CLIENT_KEY_EXCHANGE
293                                                                                                            20 : verifyHandshake // HANDSHAKE_FINISHED                                                                                                      
294                                                                                                                 };
295                                                                                                                 
296                 // Client handshake handler map                                                                         
297                 private var handshakeHandlersClient:Object = { 0 : parseHandshakeHello, // HANDSHAKE_HELLO_REQUEST
298                                                                                                            1 : notifyStateError, // HANDSHAKE_CLIENT_HELLO
299                                                                                                            2 : parseHandshakeServerHello, // HANDSHAKE_SERVER_HELLO
300                                                                                                            11 : loadCertificates, // HANDSHAKE_CERTIFICATE
301                                                                                                            12 : parseServerKeyExchange, // HANDSHAKE_SERVER_KEY_EXCHANGE
302                                                                                                            13 : setStateRespondWithCertificate, // HANDSHAKE_CERTIFICATE
303                                                                                                            14 : sendClientAck, // HANDSHAKE_HELLO_DONE  
304                                                                                                            15 : notifyStateError, // HANDSHAKE_CERTIFICATE_VERIFY
305                                                                                                            16 : notifyStateError, // HANDSHAKE_CLIENT_KEY_EXCHANGE
306                                                                                                            20 : verifyHandshake // HANDSHAKE_FINISHED
307                                                                                                                 };
308                 private var _entityHandshakeHandlers:Object;
309                 private var _handshakeCanContinue:Boolean = true; // For handling cases where I might need to pause processing during a handshake (cert issues, etc.).
310                 private var _handshakeQueue:Array = [];
311                 /**
312                  * The handshake is always started by the client.
313                  */
314                 private function startHandshake():void {
315                         _state = STATE_NEGOTIATING;
316                         // reset some other handshake state. XXX
317                         sendClientHello();
318                 }
319                 
320                 /**
321                  * Handle the incoming handshake packet.
322                  * 
323                  */
324                 private function parseHandshake(p:ByteArray):ByteArray {
325                         
326                         if (p.length<4) {
327                                 trace("Handshake packet is way too short. bailing.");
328                                 return null;
329                         }
330
331                         p.position = 0;
332                         
333                         var rec:ByteArray = p;
334                         var type:uint = rec.readUnsignedByte();
335                         var tmp:uint = rec.readUnsignedByte();
336                         var length:uint = (tmp<<16) | rec.readUnsignedShort();
337                         if (length+4>p.length) {
338                                 // partial read.
339                                 trace("Handshake packet is incomplete. bailing.");
340                                 return null;
341                         }
342
343                         // we need to copy the record, to have a valid FINISHED exchange.
344                         if (type!=HANDSHAKE_FINISHED) { 
345                                 _handshakePayloads.writeBytes(p, 0, length+4);
346                         } 
347                         
348                         // Surf the handler map and find the right handler for this handshake packet type. 
349                         // I modified the individual handlers so they encapsulate all possible knowledge 
350                         // about the incoming packet type, so no previous handling or massaging of the data 
351                         // is required, as was the case using the switch statement. BP
352                         if (_entityHandshakeHandlers.hasOwnProperty( type )) {
353                                 if (_entityHandshakeHandlers[ type ] is Function) 
354                                         _entityHandshakeHandlers[ type ]( rec );
355                         } else {
356                                 throw new TLSError( "Unimplemented or unknown handshake type!", TLSError.internal_error );
357                         }
358                         
359                         // Get set up for the next packet.
360                         if (length+4<p.length) {
361                                 var n:ByteArray = new ByteArray;
362                                 n.writeBytes(p,length+4, p.length-(length+4));
363                                 return n;
364                         } else {
365                                 return null;
366                         }
367                 }
368                 
369                 /**
370                  * Throw an error when the detected handshake state isn't a valid state for the given entity type (client vs. server, etc. ).
371                  * This really should abort the handshake, since there's no case in which a server should EVER be confused about the type of entity it is. BP
372                  */
373                 private function notifyStateError( rec:ByteArray ) : void {
374                         throw new TLSError( "Invalid handshake state for a TLS Entity type of " + _entity, TLSError.internal_error ); 
375                 }
376                 
377                 /**
378                  * two unimplemented functions
379                  */
380                 private function parseClientKeyExchange( rec:ByteArray ) : void {
381                         throw new TLSError( "ClientKeyExchange is currently unimplemented!", TLSError.internal_error );
382                 }
383                 
384                 private function parseServerKeyExchange( rec:ByteArray ) : void {
385                         throw new TLSError( "ServerKeyExchange is currently unimplemented!", TLSError.internal_error );
386                 }
387                 
388                 /**
389                  * Test the server's Finished message for validity against the data we know about. Only slightly rewritten. BP
390                  */
391                 private function verifyHandshake( rec:ByteArray):void {
392                         // Get the Finished message
393                         var verifyData:ByteArray = new ByteArray;
394                         // This, in the vain hope that noboby is using SSL 2 anymore
395                         if (_securityParameters.version == SSLSecurityParameters.PROTOCOL_VERSION) {
396                                 rec.readBytes(verifyData, 0, 36); // length should be (in fact, better be) 16 + 20 (md5-size + sha1-size)
397                         } else { // presuming TLS
398                                 rec.readBytes(verifyData, 0, 12);
399                         }
400                         
401                         var data:ByteArray = _securityParameters.computeVerifyData(1-_entity, _handshakePayloads);
402
403                         if (ArrayUtil.equals(verifyData, data)) {
404                                 _state = STATE_READY;
405                                 dispatchEvent(new TLSEvent(TLSEvent.READY));
406                         } else {
407                                 throw new TLSError("Invalid Finished mac.", TLSError.bad_record_mac);
408                         }
409                 }
410
411                 // enforceClient/enforceServer removed in favor of state-driven function maps
412
413                 /**
414                  * Handle a HANDSHAKE_HELLO
415                  */
416                 private function parseHandshakeHello( rec:ByteArray ) : void {
417                         if (_state != STATE_READY) {
418                                 trace("Received an HELLO_REQUEST before being in state READY. ignoring.");
419                                 return;
420                         }
421                         _handshakePayloads = new ByteArray;
422                         startHandshake();
423                 }
424                 
425                 /**
426                  * Handle a HANDSHAKE_CLIENT_KEY_EXCHANGE
427                  */
428                 private function parseHandshakeClientKeyExchange(rec:ByteArray):void {
429                         if (_securityParameters.useRSA) {
430                                 // skip 2 bytes for length.
431                                 var len:uint = rec.readShort();
432                                 var cipher:ByteArray = new ByteArray;
433                                 rec.readBytes(cipher, 0, len);
434                                 var preMasterSecret:ByteArray = new ByteArray;
435                                 _config.privateKey.decrypt(cipher, preMasterSecret, len);
436                                 _securityParameters.setPreMasterSecret(preMasterSecret);
437                                 
438                                 // now is a good time to get our pending states
439                                 var o:Object = _securityParameters.getConnectionStates();
440                                 _pendingReadState = o.read;
441                                 _pendingWriteState = o.write;
442                                 
443                         } else {
444                                 throw new TLSError("parseHandshakeClientKeyExchange not implemented for DH modes.", TLSError.internal_error);
445                         }
446                         
447                 }
448                 
449                 /** 
450                  * Handle HANDSHAKE_SERVER_HELLO - client-side
451                  */
452                 private function parseHandshakeServerHello( rec:IDataInput ) : void {
453                         
454                         var ver:uint = rec.readShort(); 
455                         if (ver != _securityParameters.version) {
456                                 throw new TLSError("Unsupported TLS version: "+ver.toString(16), TLSError.protocol_version);
457                         }                       
458                         var random:ByteArray = new ByteArray;
459                         rec.readBytes(random, 0, 32);
460                         var session_length:uint = rec.readByte();
461                         var session:ByteArray = new ByteArray;
462                         if (session_length > 0) {
463                                 // some implementations don't assign a session ID
464                                 rec.readBytes(session, 0, session_length);
465                         }
466                         
467                         _securityParameters.setCipher(rec.readShort()); 
468                         _securityParameters.setCompression(rec.readByte());
469                         _securityParameters.setServerRandom(random);
470                 }
471                 
472                 /**
473                  *  Handle HANDSHAKE_CLIENT_HELLO - server side
474                  */
475                 private function parseHandshakeClientHello( rec:IDataInput ) : void {
476                         var ret:Object;
477                         var ver:uint = rec.readShort(); 
478                         if (ver != _securityParameters.version) {
479                                 throw new TLSError("Unsupported TLS version: "+ver.toString(16), TLSError.protocol_version);
480                         }
481                         
482                         var random:ByteArray = new ByteArray;
483                         rec.readBytes(random, 0, 32);
484                         var session_length:uint = rec.readByte();
485                         var session:ByteArray = new ByteArray;
486                         if (session_length > 0) {
487                                 // some implementations don't assign a session ID
488                                 rec.readBytes(session, 0, session_length);
489                         }
490                         var suites:Array = [];
491                         
492                         var suites_length:uint = rec.readShort();
493                         for (var i:uint=0;i<suites_length/2;i++) {
494                                 suites.push(rec.readShort());
495                         }
496                 
497                         var compressions:Array = [];
498                         
499                         var comp_length:uint = rec.readByte();
500                         for (i=0;i<comp_length;i++) {
501                                 compressions.push(rec.readByte());
502                         }
503
504                         ret = {random:random, session:session, suites:suites, compressions:compressions};
505                         
506                         var sofar:uint = 2+32+1+session_length+2+suites_length+1+comp_length;
507                         var extensions:Array = [];
508                         if (sofar<length) {
509                                 // we have extensions. great.
510                                 var ext_total_length:uint = rec.readShort();
511                                 while (ext_total_length>0) {
512                                         var ext_type:uint = rec.readShort();
513                                         var ext_length:uint = rec.readShort();
514                                         var ext_data:ByteArray = new ByteArray;
515                                         rec.readBytes(ext_data, 0, ext_length);
516                                         ext_total_length -= 4+ext_length;
517                                         extensions.push({type:ext_type, length:ext_length, data:ext_data});
518                                 }
519                         }
520                         ret.ext = extensions;
521                         
522                         sendServerHello(ret);
523                         sendCertificate();
524                         // TODO: Modify to handle case of requesting a certificate from the client, for "client authentication", 
525                         // and testing purposes, will probably never actually need it.
526                         sendServerHelloDone();
527                 }
528                 
529                 private function sendClientHello():void {
530                         var rec:ByteArray = new ByteArray;
531                         // version - modified to support version attribute from ISecurityParameters
532                         rec.writeShort(_securityParameters.version);  
533                         // random
534                         var prng:Random = new Random;
535                         var clientRandom:ByteArray = new ByteArray;
536                         prng.nextBytes(clientRandom, 32);
537                         _securityParameters.setClientRandom(clientRandom);
538                         rec.writeBytes(clientRandom,0,32);
539                         // session
540                         rec.writeByte(32);
541                         prng.nextBytes(rec, 32);
542                         // Cipher suites
543                         var cs:Array = _config.cipherSuites;
544                         rec.writeShort(2* cs.length);
545                         for (var i:int=0;i<cs.length;i++) {
546                                 rec.writeShort(cs[i]);
547                         }
548                         // Compression
549                         cs = _config.compressions;
550                         rec.writeByte(cs.length);
551                         for (i=0;i<cs.length;i++) {
552                                 rec.writeByte(cs[i]);
553                         }
554                         // no extensions, yet.
555                         rec.position = 0;
556                         sendHandshake(HANDSHAKE_CLIENT_HELLO, rec.length, rec);
557                 }
558                 
559                 private function findMatch(a1:Array, a2:Array):int {
560                         for (var i:int=0;i<a1.length;i++) {
561                                 var e:uint = a1[i];
562                                 if (a2.indexOf(e)>-1) {
563                                         return e;
564                                 }
565                         }
566                         return -1;
567                 }
568                 
569                 private function sendServerHello(v:Object):void {
570                         var cipher:int = findMatch(_config.cipherSuites, v.suites);
571                         if (cipher == -1) {
572                                 throw new TLSError("No compatible cipher found.", TLSError.handshake_failure);
573                         }
574                         _securityParameters.setCipher(cipher);
575                         
576                         var comp:int = findMatch(_config.compressions, v.compressions);
577                         if (comp == 01) {
578                                 throw new TLSError("No compatible compression method found.", TLSError.handshake_failure);
579                         }
580                         _securityParameters.setCompression(comp);
581                         _securityParameters.setClientRandom(v.random);
582
583
584                         var rec:ByteArray = new ByteArray;
585                         rec.writeShort(_securityParameters.version);
586                         var prng:Random = new Random;
587                         var serverRandom:ByteArray = new ByteArray;
588                         prng.nextBytes(serverRandom, 32);
589                         _securityParameters.setServerRandom(serverRandom);
590                         rec.writeBytes(serverRandom,0,32);
591                         // session
592                         rec.writeByte(32);
593                         prng.nextBytes(rec, 32);
594                         // Cipher suite
595                         rec.writeShort(v.suites[0]);
596                         // Compression
597                         rec.writeByte(v.compressions[0]);
598                         rec.position = 0;
599                         sendHandshake(HANDSHAKE_SERVER_HELLO, rec.length, rec);
600                 }
601                 
602                 private var sendClientCert:Boolean = false;
603                 private function setStateRespondWithCertificate( r:ByteArray = null) : void {
604                         sendClientCert = true;
605                 }
606                 
607                 private function sendCertificate( r:ByteArray = null ):void {
608                         var cert:ByteArray = _config.certificate;
609                         var len:uint;
610                         var len2:uint;
611                         var rec:ByteArray = new ByteArray;
612                         // Look for a certficate chain, if we have one, send it, if we don't, send an empty record.
613                         if (cert != null) { 
614                                 len = cert.length;
615                                 len2 = cert.length + 3;
616                                 rec.writeByte(len2>>16);
617                                 rec.writeShort(len2&65535);
618                                 rec.writeByte(len>>16);
619                                 rec.writeShort(len&65535);
620                                 rec.writeBytes(cert);
621                         } else {
622                                 rec.writeShort( 0 );
623                                 rec.writeByte( 0 );
624                         }
625                         rec.position = 0;
626                         sendHandshake(HANDSHAKE_CERTIFICATE, rec.length, rec);
627                 }
628                 
629                 private function sendCertificateVerify():void {
630                         var rec:ByteArray = new ByteArray();
631                         // Encrypt the handshake payloads here
632                         var data:ByteArray = _securityParameters.computeCertificateVerify(_entity, _handshakePayloads);
633                         data.position=0;
634                         sendHandshake(HANDSHAKE_CERTIFICATE_VERIFY, data.length, data);
635                 }
636                 
637                 private function sendServerHelloDone():void {
638                         var rec:ByteArray = new ByteArray;
639                         sendHandshake(HANDSHAKE_HELLO_DONE, rec.length, rec);
640                 }
641                 
642                 private function sendClientKeyExchange():void {
643                         if (_securityParameters.useRSA) {
644                                 var p:ByteArray = new ByteArray;
645                                 p.writeShort(_securityParameters.version);
646                                 var prng:Random = new Random;
647                                 prng.nextBytes(p, 46);
648                                 p.position = 0;
649
650                                 var preMasterSecret:ByteArray = new ByteArray;
651                                 preMasterSecret.writeBytes(p, 0, p.length);
652                                 preMasterSecret.position = 0;
653                                 _securityParameters.setPreMasterSecret(preMasterSecret);
654                                                         
655                                 var enc_key:ByteArray = new ByteArray;
656                                 _otherCertificate.getPublicKey().encrypt(preMasterSecret, enc_key, preMasterSecret.length); 
657                                 
658                                 enc_key.position = 0;
659                                 var rec:ByteArray = new ByteArray;
660                                 
661                                 // TLS requires the size of the premaster key be sent BUT
662                                 // SSL 3.0 does not
663                                 if (_securityParameters.version > 0x0300) { 
664                                         rec.writeShort(enc_key.length);
665                                 }
666                                 rec.writeBytes(enc_key, 0, enc_key.length);
667                                 
668                                 rec.position=0;
669                                 
670                                 sendHandshake(HANDSHAKE_CLIENT_KEY_EXCHANGE, rec.length, rec);
671                                 
672                                 // now is a good time to get our pending states
673                                 var o:Object = _securityParameters.getConnectionStates();
674                                 _pendingReadState = o.read;
675                                 _pendingWriteState = o.write;
676                                 
677                         } else {
678                                 throw new TLSError("Non-RSA Client Key Exchange not implemented.", TLSError.internal_error);
679                         }
680                 }
681                 private function sendFinished():void {
682                         var data:ByteArray = _securityParameters.computeVerifyData(_entity, _handshakePayloads);
683                         data.position=0;
684                         sendHandshake(HANDSHAKE_FINISHED, data.length, data);
685                 }
686                 
687                 private function sendHandshake(type:uint, len:uint, payload:IDataInput):void {
688                         var rec:ByteArray = new ByteArray;
689                         rec.writeByte(type);
690                         rec.writeByte(0);
691                         rec.writeShort(len);
692                         payload.readBytes(rec, rec.position, len);
693                         _handshakePayloads.writeBytes(rec, 0, rec.length);
694                         sendRecord(PROTOCOL_HANDSHAKE, rec);
695                 }
696                 
697                 private function sendChangeCipherSpec():void {
698                         var rec:ByteArray = new ByteArray;
699                         rec[0] = 1;
700                         sendRecord(PROTOCOL_CHANGE_CIPHER_SPEC, rec);
701                         
702                         // right after, switch the cipher for writing.
703                         _currentWriteState = _pendingWriteState;
704                         _pendingWriteState = null;      
705                 }
706                 
707                 public function sendApplicationData(data:ByteArray, offset:uint=0, length:uint=0):void {
708                         var rec:ByteArray = new ByteArray;
709                         var len:uint = length;
710                         // BIG FAT WARNING: Patch from Arlen Cuss ALA As3crypto group on Google code. 
711                         // This addresses data overflow issues when the packet size hits the max length boundary.
712                         if (len == 0) len = data.length;  
713                         while (len>16384) {
714                                 rec.position = 0;
715                                 rec.writeBytes(data, offset, 16384);
716                                 rec.position = 0;
717                                 sendRecord(PROTOCOL_APPLICATION_DATA, rec);
718                                 offset += 16384;
719                                 len -= 16384;
720                         }
721                         rec.position = 0;
722                         rec.writeBytes(data, offset, len);
723                         // trace("Data I'm sending..." + Hex.fromArray( data ));
724                         rec.position = 0;
725                         sendRecord(PROTOCOL_APPLICATION_DATA, rec);
726                 }
727                 private function sendRecord(type:uint, payload:ByteArray):void {
728                         // encrypt
729                         payload = _currentWriteState.encrypt(type, payload);
730                         
731                         _oStream.writeByte(type);
732                         _oStream.writeShort(_securityParameters.version);
733                         _oStream.writeShort(payload.length);
734                         _oStream.writeBytes(payload, 0, payload.length);
735                         
736                         scheduleWrite();
737                 }
738                 
739                 private var _writeScheduler:uint;
740                 private function scheduleWrite():void {
741                         if (_writeScheduler!=0) return;
742                         _writeScheduler = setTimeout(commitWrite, 0);
743                 }
744                 private function commitWrite():void {
745                         clearTimeout(_writeScheduler);
746                         _writeScheduler = 0;
747                         if (_state != STATE_CLOSED) {
748                                 dispatchEvent(new ProgressEvent(ProgressEvent.SOCKET_DATA));
749                         }
750                 }
751                 
752                 private function sendClientAck( rec:ByteArray ):void {
753                         if ( _handshakeCanContinue ) {
754                                 // If I have a pending cert request, send it
755                                 if (sendClientCert)
756                                         sendCertificate();
757                                 // send a client key exchange
758                                 sendClientKeyExchange();
759                                 // Send the certificate verify, if we have one
760                                 if (_config.certificate != null)
761                                         sendCertificateVerify();
762                                 // send a change cipher spec
763                                 sendChangeCipherSpec();
764                                 // send a finished
765                                 sendFinished();
766                         }
767                 }
768
769                 /**
770                  * Vaguely gross function that parses a RSA key out of a certificate.
771                  * 
772                  * As long as that certificate looks just the way we expect it to.
773                  * 
774                  */
775                 private function loadCertificates( rec:ByteArray ):void {
776                         var tmp:uint = rec.readByte();
777                         var certs_len:uint = (tmp<<16) | rec.readShort();
778                         var certs:Array = [];
779                         
780                         while (certs_len>0) {
781                                 tmp = rec.readByte();
782                                 var cert_len:uint = (tmp<<16) | rec.readShort();
783                                 var cert:ByteArray = new ByteArray;
784                                 rec.readBytes(cert, 0, cert_len);
785                                 certs.push(cert);
786                                 certs_len -= 3 + cert_len;
787                         }
788                         
789                         var firstCert:X509Certificate = null;
790                         for (var i:int=0;i<certs.length;i++) {
791                                 var x509:X509Certificate = new X509Certificate(certs[i]);
792                                 _store.addCertificate(x509);
793                                 if (firstCert==null) {
794                                         firstCert = x509;
795                                 }
796                         }
797
798                         
799                         // Test first for trust override parameters
800                         // This nice trust override stuff comes from Joey Parrish via As3crypto forums
801                         var certTrusted:Boolean;
802                         if (_config.trustAllCertificates) {
803                                 certTrusted = true; // Blatantly trust everything
804                         } else if (_config.trustSelfSignedCertificates ) {
805                                 // Self-signed certs
806                                 certTrusted = firstCert.isSelfSigned(new Date); 
807                         } else {
808                                 // Certs with a signer in the CA store - realistically, I should setup an event chain to handle this
809                                 certTrusted = firstCert.isSigned(_store, _config.CAStore );
810                         }
811         
812                         // Good so far
813                         if (certTrusted) {                                                      
814                                 // ok, that's encouraging. now for the hostname match.
815                                 if (_otherIdentity==null || _config.ignoreCommonNameMismatch ) {
816                                         // we don't care who we're talking with. groovy.
817                                         _otherCertificate = firstCert;
818                                 } else {
819                                         // use regex to handle wildcard certs
820                                         var commonName:String = firstCert.getCommonName();
821                                         // replace all regex special characters with escaped version, except for asterisk
822                                         // replace the asterisk with a regex sequence to match one or more non-dot characters
823                                         var commonNameRegex:RegExp = new RegExp( commonName.replace(/[\^\\\-$.[\]|()?+{}]/g, "\\$&").replace(/\*/g, "[^.]+"), "gi");
824                                         if (commonNameRegex.exec(_otherIdentity)) {
825                                                 _otherCertificate = firstCert;
826                                         } else {
827                                                 if (_config.promptUserForAcceptCert ) {
828                                                         _handshakeCanContinue = false;
829                                                         dispatchEvent( new TLSEvent( TLSEvent.PROMPT_ACCEPT_CERT ));
830                                                 } else {
831                                                         throw new TLSError("Invalid common name: "+firstCert.getCommonName()+", expected "+_otherIdentity, TLSError.bad_certificate);
832                                                 }
833                                         }
834                                 }
835                         
836                         } else {
837                                 // Let's ask the user if we can accept this cert. I'm not certain of the behaviour in case of timeouts, 
838                                 // so I probably need to handle the case by killing and restarting the connection rather than continuing if it becomes 
839                                 // an issue. We shall see. BP
840                                 if (_config.promptUserForAcceptCert) {
841                                         _handshakeCanContinue = false;
842                                         dispatchEvent( new TLSEvent( TLSEvent.PROMPT_ACCEPT_CERT ));
843                                 } else {
844                                         // Cannot continue, die.
845                                         throw new TLSError("Cannot verify certificate", TLSError.bad_certificate);
846                                 }
847                         }
848                 }
849                 
850                 // Accept the peer cert, and keep going
851                 public function acceptPeerCertificate() : void {
852                         _handshakeCanContinue = true;
853                         sendClientAck( null );
854                 }
855                 
856                 // Step off biotch! No trust for you!
857                 public function rejectPeerCertificate() : void {
858                         throw new TLSError("Peer certificate not accepted!", TLSError.bad_certificate);
859                 }
860                 
861                 
862                 private function parseAlert(p:ByteArray):void {
863                         //throw new Error("Alert not implemented.");
864                         // 7.2
865                         trace("GOT ALERT! type="+p[1]);
866                         close();
867                 }
868                 private function parseChangeCipherSpec(p:ByteArray):void {
869                         p.readUnsignedByte();
870                         if (_pendingReadState==null) {
871                                 throw new TLSError("Not ready to Change Cipher Spec, damnit.", TLSError.unexpected_message);
872                         }
873                         _currentReadState = _pendingReadState;
874                         _pendingReadState = null;
875                         // 7.1
876                 }
877                 private function parseApplicationData(p:ByteArray):void {
878                         if (_state != STATE_READY) {
879                                 throw new TLSError("Too soon for data!", TLSError.unexpected_message);
880                                 return;
881                         }
882                         dispatchEvent(new TLSEvent(TLSEvent.DATA, p));
883                 }
884                 
885                 private function handleTLSError(e:TLSError):void {
886                         // basic rules to keep things simple:
887                         // - Make a good faith attempt at notifying peers
888                         // - TLSErrors are always fatal.
889                         // BP: Meh...not always. Common Name mismatches appear to be common on servers. Instead of closing, let's pause, and ask for confirmation 
890                         // before we tear the connection down.
891                         
892                         close(e);
893                 }
894         }
895 }