rocket-js 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +0 -11
- data/Rakefile +33 -47
- data/lib/rocket/js.rb +0 -2
- data/lib/rocket/js/builder.rb +2 -2
- data/lib/rocket/js/cli.rb +1 -1
- data/rocket-js.gemspec +23 -80
- data/src/vendor/web-socket-js/FABridge.js +604 -0
- data/src/vendor/web-socket-js/README.txt +109 -0
- 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 +473 -0
- data/src/vendor/web-socket-js/flash-src/WebSocketMain.as +88 -0
- data/src/vendor/web-socket-js/flash-src/WebSocketMainInsecure.as +19 -0
- data/src/vendor/web-socket-js/flash-src/WebSocketStateEvent.as +32 -0
- data/src/vendor/web-socket-js/flash-src/bridge/FABridge.as +943 -0
- data/src/vendor/web-socket-js/flash-src/build.sh +10 -0
- data/src/vendor/web-socket-js/flash-src/com/adobe/net/proxies/RFC2817Socket.as +204 -0
- data/src/vendor/web-socket-js/flash-src/com/gsolo/encryption/MD5.as +375 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/Crypto.as +287 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/cert/MozillaRootCertificates.as +3235 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/cert/X509Certificate.as +218 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/cert/X509CertificateCollection.as +57 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/HMAC.as +82 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/IHMAC.as +27 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/IHash.as +21 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/MAC.as +137 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/MD2.as +124 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/MD5.as +204 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/SHA1.as +106 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/SHA224.as +28 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/SHA256.as +115 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/hash/SHABase.as +71 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/prng/ARC4.as +90 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/prng/IPRNG.as +20 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/prng/Random.as +119 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/prng/TLSPRF.as +142 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/rsa/RSAKey.as +339 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/AESKey.as +2797 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/BlowFishKey.as +375 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/CBCMode.as +55 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/CFB8Mode.as +61 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/CFBMode.as +64 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/CTRMode.as +58 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/DESKey.as +365 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/ECBMode.as +86 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/ICipher.as +21 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/IMode.as +15 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/IPad.as +32 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/IStreamCipher.as +21 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/ISymmetricKey.as +35 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/IVMode.as +110 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/NullPad.as +34 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/OFBMode.as +52 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/PKCS5.as +44 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/SSLPad.as +44 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/SimpleIVMode.as +60 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/TLSPad.as +42 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/TripleDESKey.as +88 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/XTeaKey.as +94 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/aeskey.pl +29 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/symmetric/dump.txt +2304 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/AESKeyTest.as +1220 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/ARC4Test.as +58 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/BigIntegerTest.as +39 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/BlowFishKeyTest.as +148 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/CBCModeTest.as +160 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/CFB8ModeTest.as +71 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/CFBModeTest.as +98 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/CTRModeTest.as +109 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/DESKeyTest.as +112 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/ECBModeTest.as +151 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/HMACTest.as +184 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/ITestHarness.as +20 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/MD2Test.as +56 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/MD5Test.as +58 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/OFBModeTest.as +101 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/RSAKeyTest.as +92 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/SHA1Test.as +198 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/SHA224Test.as +58 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/SHA256Test.as +60 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/TLSPRFTest.as +51 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/TestCase.as +42 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/TripleDESKeyTest.as +59 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tests/XTeaKeyTest.as +66 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/BulkCiphers.as +102 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/CipherSuites.as +117 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/IConnectionState.as +14 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/ISecurityParameters.as +29 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/KeyExchanges.as +24 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/MACs.as +38 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/SSLConnectionState.as +171 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/SSLEvent.as +26 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/SSLSecurityParameters.as +340 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSConfig.as +70 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSConnectionState.as +151 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSEngine.as +895 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSError.as +39 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSEvent.as +27 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSSecurityParameters.as +197 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSSocket.as +370 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSSocketEvent.as +26 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/crypto/tls/TLSTest.as +180 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/math/BarrettReduction.as +90 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/math/BigInteger.as +1543 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/math/ClassicReduction.as +35 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/math/IReduction.as +11 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/math/MontgomeryReduction.as +85 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/math/NullReduction.as +34 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/math/bi_internal.as +11 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/ArrayUtil.as +25 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/Base64.as +189 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/Hex.as +66 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/Memory.as +28 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/ByteString.as +43 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/DER.as +210 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/IAsn1Type.as +21 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/Integer.as +44 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/OID.as +35 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/ObjectIdentifier.as +112 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/PEM.as +118 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/PrintableString.as +49 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/Sequence.as +90 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/Set.as +27 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/Type.as +94 -0
- data/src/vendor/web-socket-js/flash-src/com/hurlant/util/der/UTCTime.as +60 -0
- data/src/vendor/web-socket-js/sample.html +76 -0
- data/src/vendor/web-socket-js/swfobject.js +4 -0
- data/src/vendor/web-socket-js/web_socket.js +388 -0
- metadata +163 -30
- data/lib/rocket/js/version.rb +0 -14
- data/rocket-0.0.1.min.js +0 -45
@@ -0,0 +1,895 @@
|
|
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
|
+
}
|