~ubuntu-branches/ubuntu/vivid/emscripten/vivid-proposed

« back to all changes in this revision

Viewing changes to third_party/websockify/include/web-socket-js/src/flash-src/third-party/com/hurlant/crypto/tls/TLSEngine.as

  • Committer: Package Import Robot
  • Author(s): Sylvestre Ledru
  • Date: 2014-01-19 14:12:40 UTC
  • mfrom: (4.1.2 sid)
  • Revision ID: package-import@ubuntu.com-20140119141240-nfiw0p8033oitpfz
Tags: 1.9.0~20140119~7dc8c2f-1
* New snapshot release (Closes: #733714)
* Provide sources for javascript and flash. Done in orig-tar.sh
  Available in third_party/websockify/include/web-socket-js/src/
  (Closes: #735903)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
                                rec.length = 0;
 
719
                                offset += 16384;
 
720
                                len -= 16384;
 
721
                        }
 
722
                        rec.position = 0;
 
723
                        rec.writeBytes(data, offset, len);
 
724
                        // trace("Data I'm sending..." + Hex.fromArray( data ));
 
725
                        rec.position = 0;
 
726
                        sendRecord(PROTOCOL_APPLICATION_DATA, rec);
 
727
                }
 
728
                private function sendRecord(type:uint, payload:ByteArray):void {
 
729
                        // encrypt
 
730
                        payload = _currentWriteState.encrypt(type, payload);
 
731
                        
 
732
                        _oStream.writeByte(type);
 
733
                        _oStream.writeShort(_securityParameters.version);
 
734
                        _oStream.writeShort(payload.length);
 
735
                        _oStream.writeBytes(payload, 0, payload.length);
 
736
                        
 
737
                        scheduleWrite();
 
738
                }
 
739
                
 
740
                private var _writeScheduler:uint;
 
741
                private function scheduleWrite():void {
 
742
                        if (_writeScheduler!=0) return;
 
743
                        _writeScheduler = setTimeout(commitWrite, 0);
 
744
                }
 
745
                private function commitWrite():void {
 
746
                        clearTimeout(_writeScheduler);
 
747
                        _writeScheduler = 0;
 
748
                        if (_state != STATE_CLOSED) {
 
749
                                dispatchEvent(new ProgressEvent(ProgressEvent.SOCKET_DATA));
 
750
                        }
 
751
                }
 
752
                
 
753
                private function sendClientAck( rec:ByteArray ):void {
 
754
                        if ( _handshakeCanContinue ) {
 
755
                                // If I have a pending cert request, send it
 
756
                                if (sendClientCert)
 
757
                                        sendCertificate();
 
758
                                // send a client key exchange
 
759
                                sendClientKeyExchange();
 
760
                                // Send the certificate verify, if we have one
 
761
                                if (_config.certificate != null)
 
762
                                        sendCertificateVerify();
 
763
                                // send a change cipher spec
 
764
                                sendChangeCipherSpec();
 
765
                                // send a finished
 
766
                                sendFinished();
 
767
                        }
 
768
                }
 
769
 
 
770
                /**
 
771
                 * Vaguely gross function that parses a RSA key out of a certificate.
 
772
                 * 
 
773
                 * As long as that certificate looks just the way we expect it to.
 
774
                 * 
 
775
                 */
 
776
                private function loadCertificates( rec:ByteArray ):void {
 
777
                        var tmp:uint = rec.readByte();
 
778
                        var certs_len:uint = (tmp<<16) | rec.readShort();
 
779
                        var certs:Array = [];
 
780
                        
 
781
                        while (certs_len>0) {
 
782
                                tmp = rec.readByte();
 
783
                                var cert_len:uint = (tmp<<16) | rec.readShort();
 
784
                                var cert:ByteArray = new ByteArray;
 
785
                                rec.readBytes(cert, 0, cert_len);
 
786
                                certs.push(cert);
 
787
                                certs_len -= 3 + cert_len;
 
788
                        }
 
789
                        
 
790
                        var firstCert:X509Certificate = null;
 
791
                        for (var i:int=0;i<certs.length;i++) {
 
792
                                var x509:X509Certificate = new X509Certificate(certs[i]);
 
793
                                _store.addCertificate(x509);
 
794
                                if (firstCert==null) {
 
795
                                        firstCert = x509;
 
796
                                }
 
797
                        }
 
798
 
 
799
                        
 
800
                        // Test first for trust override parameters
 
801
                        // This nice trust override stuff comes from Joey Parrish via As3crypto forums
 
802
                        var certTrusted:Boolean;
 
803
                        if (_config.trustAllCertificates) {
 
804
                                certTrusted = true; // Blatantly trust everything
 
805
                        } else if (_config.trustSelfSignedCertificates ) {
 
806
                                // Self-signed certs
 
807
                                certTrusted = firstCert.isSelfSigned(new Date); 
 
808
                        } else {
 
809
                                // Certs with a signer in the CA store - realistically, I should setup an event chain to handle this
 
810
                                certTrusted = firstCert.isSigned(_store, _config.CAStore );
 
811
                        }
 
812
        
 
813
                        // Good so far
 
814
                        if (certTrusted) {                                                      
 
815
                                // ok, that's encouraging. now for the hostname match.
 
816
                                if (_otherIdentity==null || _config.ignoreCommonNameMismatch ) {
 
817
                                        // we don't care who we're talking with. groovy.
 
818
                                        _otherCertificate = firstCert;
 
819
                                } else {
 
820
                                        // use regex to handle wildcard certs
 
821
                                        var commonName:String = firstCert.getCommonName();
 
822
                                        // replace all regex special characters with escaped version, except for asterisk
 
823
                                        // replace the asterisk with a regex sequence to match one or more non-dot characters
 
824
                                        var commonNameRegex:RegExp = new RegExp( commonName.replace(/[\^\\\-$.[\]|()?+{}]/g, "\\$&").replace(/\*/g, "[^.]+"), "gi");
 
825
                                        if (commonNameRegex.exec(_otherIdentity)) {
 
826
                                                _otherCertificate = firstCert;
 
827
                                        } else {
 
828
                                                if (_config.promptUserForAcceptCert ) {
 
829
                                                        _handshakeCanContinue = false;
 
830
                                                        dispatchEvent( new TLSEvent( TLSEvent.PROMPT_ACCEPT_CERT ));
 
831
                                                } else {
 
832
                                                        throw new TLSError("Invalid common name: "+firstCert.getCommonName()+", expected "+_otherIdentity, TLSError.bad_certificate);
 
833
                                                }
 
834
                                        }
 
835
                                }
 
836
                        
 
837
                        } else {
 
838
                                // Let's ask the user if we can accept this cert. I'm not certain of the behaviour in case of timeouts, 
 
839
                                // so I probably need to handle the case by killing and restarting the connection rather than continuing if it becomes 
 
840
                                // an issue. We shall see. BP
 
841
                                if (_config.promptUserForAcceptCert) {
 
842
                                        _handshakeCanContinue = false;
 
843
                                        dispatchEvent( new TLSEvent( TLSEvent.PROMPT_ACCEPT_CERT ));
 
844
                                } else {
 
845
                                        // Cannot continue, die.
 
846
                                        throw new TLSError("Cannot verify certificate", TLSError.bad_certificate);
 
847
                                }
 
848
                        }
 
849
                }
 
850
                
 
851
                // Accept the peer cert, and keep going
 
852
                public function acceptPeerCertificate() : void {
 
853
                        _handshakeCanContinue = true;
 
854
                        sendClientAck( null );
 
855
                }
 
856
                
 
857
                // Step off biotch! No trust for you!
 
858
                public function rejectPeerCertificate() : void {
 
859
                        throw new TLSError("Peer certificate not accepted!", TLSError.bad_certificate);
 
860
                }
 
861
                
 
862
                
 
863
                private function parseAlert(p:ByteArray):void {
 
864
                        //throw new Error("Alert not implemented.");
 
865
                        // 7.2
 
866
                        trace("GOT ALERT! type="+p[1]);
 
867
                        close();
 
868
                }
 
869
                private function parseChangeCipherSpec(p:ByteArray):void {
 
870
                        p.readUnsignedByte();
 
871
                        if (_pendingReadState==null) {
 
872
                                throw new TLSError("Not ready to Change Cipher Spec, damnit.", TLSError.unexpected_message);
 
873
                        }
 
874
                        _currentReadState = _pendingReadState;
 
875
                        _pendingReadState = null;
 
876
                        // 7.1
 
877
                }
 
878
                private function parseApplicationData(p:ByteArray):void {
 
879
                        if (_state != STATE_READY) {
 
880
                                throw new TLSError("Too soon for data!", TLSError.unexpected_message);
 
881
                                return;
 
882
                        }
 
883
                        dispatchEvent(new TLSEvent(TLSEvent.DATA, p));
 
884
                }
 
885
                
 
886
                private function handleTLSError(e:TLSError):void {
 
887
                        // basic rules to keep things simple:
 
888
                        // - Make a good faith attempt at notifying peers
 
889
                        // - TLSErrors are always fatal.
 
890
                        // BP: Meh...not always. Common Name mismatches appear to be common on servers. Instead of closing, let's pause, and ask for confirmation 
 
891
                        // before we tear the connection down.
 
892
                        
 
893
                        close(e);
 
894
                }
 
895
        }
 
896
}