rocket-js 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +5 -52
- data/spec/ruby/spec_helper.rb +2 -1
- metadata +7 -129
- data/src/vendor/web-socket-js/FABridge.js +0 -604
- data/src/vendor/web-socket-js/README.txt +0 -109
- data/src/vendor/web-socket-js/WebSocketMain.swf +0 -0
- data/src/vendor/web-socket-js/WebSocketMainInsecure.zip +0 -0
- data/src/vendor/web-socket-js/flash-src/WebSocket.as +0 -473
- data/src/vendor/web-socket-js/flash-src/WebSocketMain.as +0 -88
- data/src/vendor/web-socket-js/flash-src/WebSocketMainInsecure.as +0 -19
- data/src/vendor/web-socket-js/flash-src/WebSocketStateEvent.as +0 -32
- data/src/vendor/web-socket-js/flash-src/bridge/FABridge.as +0 -943
- data/src/vendor/web-socket-js/flash-src/build.sh +0 -10
- data/src/vendor/web-socket-js/flash-src/com/adobe/net/proxies/RFC2817Socket.as +0 -204
- data/src/vendor/web-socket-js/flash-src/com/gsolo/encryption/MD5.as +0 -375
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/Crypto.as +0 -287
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/cert/MozillaRootCertificates.as +0 -3235
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/cert/X509Certificate.as +0 -218
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/cert/X509CertificateCollection.as +0 -57
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/HMAC.as +0 -82
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/IHMAC.as +0 -27
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/IHash.as +0 -21
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/MAC.as +0 -137
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/MD2.as +0 -124
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/MD5.as +0 -204
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/SHA1.as +0 -106
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/SHA224.as +0 -28
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/SHA256.as +0 -115
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/SHABase.as +0 -71
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/prng/ARC4.as +0 -90
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/prng/IPRNG.as +0 -20
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/prng/Random.as +0 -119
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/prng/TLSPRF.as +0 -142
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/rsa/RSAKey.as +0 -339
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/AESKey.as +0 -2797
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/BlowFishKey.as +0 -375
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/CBCMode.as +0 -55
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/CFB8Mode.as +0 -61
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/CFBMode.as +0 -64
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/CTRMode.as +0 -58
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/DESKey.as +0 -365
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/ECBMode.as +0 -86
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/ICipher.as +0 -21
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/IMode.as +0 -15
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/IPad.as +0 -32
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/IStreamCipher.as +0 -21
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/ISymmetricKey.as +0 -35
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/IVMode.as +0 -110
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/NullPad.as +0 -34
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/OFBMode.as +0 -52
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/PKCS5.as +0 -44
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/SSLPad.as +0 -44
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/SimpleIVMode.as +0 -60
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/TLSPad.as +0 -42
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/TripleDESKey.as +0 -88
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/XTeaKey.as +0 -94
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/aeskey.pl +0 -29
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/dump.txt +0 -2304
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/AESKeyTest.as +0 -1220
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/ARC4Test.as +0 -58
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/BigIntegerTest.as +0 -39
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/BlowFishKeyTest.as +0 -148
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/CBCModeTest.as +0 -160
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/CFB8ModeTest.as +0 -71
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/CFBModeTest.as +0 -98
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/CTRModeTest.as +0 -109
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/DESKeyTest.as +0 -112
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/ECBModeTest.as +0 -151
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/HMACTest.as +0 -184
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/ITestHarness.as +0 -20
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/MD2Test.as +0 -56
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/MD5Test.as +0 -58
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/OFBModeTest.as +0 -101
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/RSAKeyTest.as +0 -92
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/SHA1Test.as +0 -198
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/SHA224Test.as +0 -58
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/SHA256Test.as +0 -60
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/TLSPRFTest.as +0 -51
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/TestCase.as +0 -42
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/TripleDESKeyTest.as +0 -59
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/XTeaKeyTest.as +0 -66
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/BulkCiphers.as +0 -102
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/CipherSuites.as +0 -117
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/IConnectionState.as +0 -14
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/ISecurityParameters.as +0 -29
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/KeyExchanges.as +0 -24
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/MACs.as +0 -38
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/SSLConnectionState.as +0 -171
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/SSLEvent.as +0 -26
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/SSLSecurityParameters.as +0 -340
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSConfig.as +0 -70
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSConnectionState.as +0 -151
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSEngine.as +0 -895
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSError.as +0 -39
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSEvent.as +0 -27
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSSecurityParameters.as +0 -197
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSSocket.as +0 -370
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSSocketEvent.as +0 -26
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSTest.as +0 -180
- data/src/vendor/web-socket-js/flash-src/com/hurlant/math/BarrettReduction.as +0 -90
- data/src/vendor/web-socket-js/flash-src/com/hurlant/math/BigInteger.as +0 -1543
- data/src/vendor/web-socket-js/flash-src/com/hurlant/math/ClassicReduction.as +0 -35
- data/src/vendor/web-socket-js/flash-src/com/hurlant/math/IReduction.as +0 -11
- data/src/vendor/web-socket-js/flash-src/com/hurlant/math/MontgomeryReduction.as +0 -85
- data/src/vendor/web-socket-js/flash-src/com/hurlant/math/NullReduction.as +0 -34
- data/src/vendor/web-socket-js/flash-src/com/hurlant/math/bi_internal.as +0 -11
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/ArrayUtil.as +0 -25
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/Base64.as +0 -189
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/Hex.as +0 -66
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/Memory.as +0 -28
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/ByteString.as +0 -43
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/DER.as +0 -210
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/IAsn1Type.as +0 -21
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/Integer.as +0 -44
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/OID.as +0 -35
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/ObjectIdentifier.as +0 -112
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/PEM.as +0 -118
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/PrintableString.as +0 -49
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/Sequence.as +0 -90
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/Set.as +0 -27
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/Type.as +0 -94
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/UTCTime.as +0 -60
- data/src/vendor/web-socket-js/sample.html +0 -76
- data/src/vendor/web-socket-js/swfobject.js +0 -4
- data/src/vendor/web-socket-js/web_socket.js +0 -388
|
@@ -1,895 +0,0 @@
|
|
|
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
|
-
}
|