OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 // | 4 // |
5 // OtherResources=certificates/server_chain.pem | 5 // OtherResources=certificates/server_chain.pem |
6 // OtherResources=certificates/server_key.pem | 6 // OtherResources=certificates/server_key.pem |
7 // OtherResources=certificates/trusted_certs.pem | 7 // OtherResources=certificates/trusted_certs.pem |
8 | 8 |
9 import 'dart:io'; | 9 import 'dart:io'; |
10 import 'dart:convert'; | 10 import 'dart:convert'; |
11 | 11 |
12 import 'package:expect/expect.dart'; | 12 import 'package:expect/expect.dart'; |
13 import 'package:async_helper/async_helper.dart'; | 13 import 'package:async_helper/async_helper.dart'; |
14 | 14 |
15 const String NAME_LENGTH_ERROR = | 15 const String NAME_LENGTH_ERROR = 'Length of protocol must be between 1 and 255'; |
16 'Length of protocol must be between 1 and 255'; | |
17 | 16 |
18 const String MESSAGE_LENGTH_ERROR = | 17 const String MESSAGE_LENGTH_ERROR = |
19 'The maximum message length supported is 2^13-1'; | 18 'The maximum message length supported is 2^13-1'; |
20 | 19 |
21 String localFile(path) => Platform.script.resolve(path).toFilePath(); | 20 String localFile(path) => Platform.script.resolve(path).toFilePath(); |
22 | 21 |
23 SecurityContext clientContext() => new SecurityContext() | 22 SecurityContext clientContext() => new SecurityContext() |
24 ..setTrustedCertificates(localFile('certificates/trusted_certs.pem')); | 23 ..setTrustedCertificates(localFile('certificates/trusted_certs.pem')); |
25 | 24 |
26 SecurityContext serverContext() => new SecurityContext() | 25 SecurityContext serverContext() => new SecurityContext() |
27 ..useCertificateChain(localFile('certificates/server_chain.pem')) | 26 ..useCertificateChain(localFile('certificates/server_chain.pem')) |
28 ..usePrivateKey(localFile('certificates/server_key.pem'), | 27 ..usePrivateKey(localFile('certificates/server_key.pem'), |
29 password: 'dartdart'); | 28 password: 'dartdart'); |
30 | 29 |
31 // Tests that client/server with same protocol can securely establish a | 30 // Tests that client/server with same protocol can securely establish a |
32 // connection, negotiate the protocol and can send data to each other. | 31 // connection, negotiate the protocol and can send data to each other. |
33 void testSuccessfulAlpnNegotiationConnection(List<String> clientProtocols, | 32 void testSuccessfulAlpnNegotiationConnection(List<String> clientProtocols, |
34 List<String> serverProtocols, | 33 List<String> serverProtocols, String selectedProtocol) { |
35 String selectedProtocol) { | |
36 asyncStart(); | 34 asyncStart(); |
37 var sContext = serverContext()..setAlpnProtocols(serverProtocols, true); | 35 var sContext = serverContext()..setAlpnProtocols(serverProtocols, true); |
38 SecureServerSocket.bind('localhost', 0, sContext) | 36 SecureServerSocket |
39 .then((SecureServerSocket server) { | 37 .bind('localhost', 0, sContext) |
40 | 38 .then((SecureServerSocket server) { |
41 asyncStart(); | 39 asyncStart(); |
42 server.first.then((SecureSocket socket) { | 40 server.first.then((SecureSocket socket) { |
43 Expect.equals(selectedProtocol, socket.selectedProtocol); | 41 Expect.equals(selectedProtocol, socket.selectedProtocol); |
44 socket..write('server message')..close(); | 42 socket |
| 43 ..write('server message') |
| 44 ..close(); |
45 socket.transform(ASCII.decoder).join('').then((String s) { | 45 socket.transform(ASCII.decoder).join('').then((String s) { |
46 Expect.equals('client message', s); | 46 Expect.equals('client message', s); |
47 asyncEnd(); | 47 asyncEnd(); |
48 }); | 48 }); |
49 }); | 49 }); |
50 | 50 |
51 asyncStart(); | 51 asyncStart(); |
52 SecureSocket.connect('localhost', server.port, context: clientContext(), | 52 SecureSocket |
53 supportedProtocols: clientProtocols).then((socket) { | 53 .connect('localhost', server.port, |
| 54 context: clientContext(), supportedProtocols: clientProtocols) |
| 55 .then((socket) { |
54 Expect.equals(selectedProtocol, socket.selectedProtocol); | 56 Expect.equals(selectedProtocol, socket.selectedProtocol); |
55 socket..write('client message')..close(); | 57 socket |
| 58 ..write('client message') |
| 59 ..close(); |
56 socket.transform(ASCII.decoder).join('').then((String s) { | 60 socket.transform(ASCII.decoder).join('').then((String s) { |
57 Expect.equals('server message', s); | 61 Expect.equals('server message', s); |
58 server.close(); | 62 server.close(); |
59 asyncEnd(); | 63 asyncEnd(); |
60 }); | 64 }); |
61 }); | 65 }); |
62 | 66 |
63 asyncEnd(); | 67 asyncEnd(); |
64 }); | 68 }); |
65 } | 69 } |
66 | 70 |
67 void testInvalidArgument(List<String> protocols, String errorIncludes) { | 71 void testInvalidArgument(List<String> protocols, String errorIncludes) { |
68 testInvalidArgumentServerContext(protocols, errorIncludes); | 72 testInvalidArgumentServerContext(protocols, errorIncludes); |
69 testInvalidArgumentClientContext(protocols, errorIncludes); | 73 testInvalidArgumentClientContext(protocols, errorIncludes); |
70 testInvalidArgumentClientConnect(protocols, errorIncludes); | 74 testInvalidArgumentClientConnect(protocols, errorIncludes); |
71 } | 75 } |
72 | 76 |
73 void testInvalidArgumentServerContext(List<String> protocols, | 77 void testInvalidArgumentServerContext( |
74 String errorIncludes) { | 78 List<String> protocols, String errorIncludes) { |
75 Expect.throws(() => serverContext().setAlpnProtocols(protocols, true), (e) { | 79 Expect.throws(() => serverContext().setAlpnProtocols(protocols, true), (e) { |
76 Expect.isTrue(e is ArgumentError); | 80 Expect.isTrue(e is ArgumentError); |
77 Expect.isTrue(e.toString().contains(errorIncludes)); | 81 Expect.isTrue(e.toString().contains(errorIncludes)); |
78 return true; | 82 return true; |
79 }); | 83 }); |
80 } | 84 } |
81 | 85 |
82 void testInvalidArgumentClientContext(List<String> protocols, | 86 void testInvalidArgumentClientContext( |
83 String errorIncludes) { | 87 List<String> protocols, String errorIncludes) { |
84 Expect.throws(() => clientContext().setAlpnProtocols(protocols, false), (e) { | 88 Expect.throws(() => clientContext().setAlpnProtocols(protocols, false), (e) { |
85 Expect.isTrue(e is ArgumentError); | 89 Expect.isTrue(e is ArgumentError); |
86 Expect.isTrue(e.toString().contains(errorIncludes)); | 90 Expect.isTrue(e.toString().contains(errorIncludes)); |
87 return true; | 91 return true; |
88 }); | 92 }); |
89 } | 93 } |
90 | 94 |
91 void testInvalidArgumentClientConnect(List<String> protocols, | 95 void testInvalidArgumentClientConnect( |
92 String errorIncludes) { | 96 List<String> protocols, String errorIncludes) { |
93 asyncStart(); | 97 asyncStart(); |
94 var sContext = serverContext()..setAlpnProtocols(['abc'], true); | 98 var sContext = serverContext()..setAlpnProtocols(['abc'], true); |
95 SecureServerSocket.bind('localhost', 0, sContext).then((server) async { | 99 SecureServerSocket.bind('localhost', 0, sContext).then((server) async { |
96 asyncStart(); | 100 asyncStart(); |
97 server.listen((SecureSocket socket) { | 101 server.listen((SecureSocket socket) { |
98 Expect.fail( | 102 Expect.fail( |
99 "Unexpected connection made to server, with bad client argument"); | 103 "Unexpected connection made to server, with bad client argument"); |
100 }, onError: (e) { | 104 }, onError: (e) { |
101 Expect.fail("Unexpected error on server stream: $e"); | 105 Expect.fail("Unexpected error on server stream: $e"); |
102 }, onDone: () { asyncEnd();}); | 106 }, onDone: () { |
| 107 asyncEnd(); |
| 108 }); |
103 | 109 |
104 asyncStart(); | 110 asyncStart(); |
105 SecureSocket.connect('localhost', server.port, context: clientContext(), | 111 SecureSocket |
106 supportedProtocols: protocols).then((socket) { | 112 .connect('localhost', server.port, |
| 113 context: clientContext(), supportedProtocols: protocols) |
| 114 .then((socket) { |
107 Expect.fail( | 115 Expect.fail( |
108 "Unexpected connection made from client, with bad client argument"); | 116 "Unexpected connection made from client, with bad client argument"); |
109 }, onError: (e) { | 117 }, onError: (e) { |
110 Expect.isTrue(e is ArgumentError); | 118 Expect.isTrue(e is ArgumentError); |
111 Expect.isTrue(e.toString().contains(errorIncludes)); | 119 Expect.isTrue(e.toString().contains(errorIncludes)); |
112 server.close(); | 120 server.close(); |
113 asyncEnd(); | 121 asyncEnd(); |
114 }); | 122 }); |
115 asyncEnd(); | 123 asyncEnd(); |
116 }); | 124 }); |
117 } | 125 } |
118 | 126 |
119 main() { | 127 main() { |
120 if (!SecurityContext.alpnSupported) { | 128 if (!SecurityContext.alpnSupported) { |
121 return 0; | 129 return 0; |
122 } | 130 } |
123 final longname256 = 'p' * 256; | 131 final longname256 = 'p' * 256; |
124 final String longname255 = 'p' * 255; | 132 final String longname255 = 'p' * 255; |
125 final String strangelongname255 = 'ø' + 'p' * 253; | 133 final String strangelongname255 = 'ø' + 'p' * 253; |
126 final String strangelongname256 = 'ø' + 'p' * 254; | 134 final String strangelongname256 = 'ø' + 'p' * 254; |
127 | 135 |
128 // This produces a message of (1 << 13) - 2 bytes. 2^12 -1 strings are each | 136 // This produces a message of (1 << 13) - 2 bytes. 2^12 -1 strings are each |
129 // encoded by 1 length byte and 1 ascii byte. | 137 // encoded by 1 length byte and 1 ascii byte. |
130 final List<String> manyProtocols = new Iterable.generate( | 138 final List<String> manyProtocols = |
131 (1 << 12) - 1, (i) => '0').toList(); | 139 new Iterable.generate((1 << 12) - 1, (i) => '0').toList(); |
132 | 140 |
133 // This produces a message of (1 << 13) bytes. 2^12 strings are each | 141 // This produces a message of (1 << 13) bytes. 2^12 strings are each |
134 // encoded by 1 length byte and 1 ascii byte. | 142 // encoded by 1 length byte and 1 ascii byte. |
135 final List<String> tooManyProtocols = new Iterable.generate( | 143 final List<String> tooManyProtocols = |
136 (1 << 12), (i) => '0').toList(); | 144 new Iterable.generate((1 << 12), (i) => '0').toList(); |
137 | 145 |
138 // Protocols are in order of decreasing priority. The server will select | 146 // Protocols are in order of decreasing priority. The server will select |
139 // the first protocol from its list that has a match in the client list. | 147 // the first protocol from its list that has a match in the client list. |
140 // Test successful negotiation, including priority. | 148 // Test successful negotiation, including priority. |
141 testSuccessfulAlpnNegotiationConnection(['a'], | 149 testSuccessfulAlpnNegotiationConnection(['a'], ['a'], 'a'); |
142 ['a'], | |
143 'a'); | |
144 | 150 |
145 testSuccessfulAlpnNegotiationConnection([longname255], | 151 testSuccessfulAlpnNegotiationConnection( |
146 [longname255], | 152 [longname255], [longname255], longname255); |
147 longname255); | |
148 | 153 |
149 testSuccessfulAlpnNegotiationConnection([strangelongname255], | 154 testSuccessfulAlpnNegotiationConnection( |
150 [strangelongname255], | 155 [strangelongname255], [strangelongname255], strangelongname255); |
151 strangelongname255); | 156 testSuccessfulAlpnNegotiationConnection(manyProtocols, manyProtocols, '0'); |
152 testSuccessfulAlpnNegotiationConnection(manyProtocols, | 157 testSuccessfulAlpnNegotiationConnection( |
153 manyProtocols, | 158 ['a', 'b', 'c'], ['a', 'b', 'c'], 'a'); |
154 '0'); | |
155 testSuccessfulAlpnNegotiationConnection(['a', 'b', 'c'], | |
156 ['a', 'b', 'c'], | |
157 'a'); | |
158 | 159 |
159 testSuccessfulAlpnNegotiationConnection(['a', 'b', 'c'], | 160 testSuccessfulAlpnNegotiationConnection(['a', 'b', 'c'], ['c'], 'c'); |
160 ['c'], | |
161 'c'); | |
162 | 161 |
163 // Server precedence. | 162 // Server precedence. |
164 testSuccessfulAlpnNegotiationConnection(['a', 'b', 'c'], | 163 testSuccessfulAlpnNegotiationConnection( |
165 ['c', 'b', 'a'], | 164 ['a', 'b', 'c'], ['c', 'b', 'a'], 'c'); |
166 'c'); | |
167 | 165 |
168 testSuccessfulAlpnNegotiationConnection(['c'], | 166 testSuccessfulAlpnNegotiationConnection(['c'], ['a', 'b', 'c'], 'c'); |
169 ['a', 'b', 'c'], | |
170 'c'); | |
171 | 167 |
172 testSuccessfulAlpnNegotiationConnection(['s1', 'b', 'e1'], | 168 testSuccessfulAlpnNegotiationConnection( |
173 ['s2', 'b', 'e2'], | 169 ['s1', 'b', 'e1'], ['s2', 'b', 'e2'], 'b'); |
174 'b'); | |
175 // Test no protocol negotiation support | 170 // Test no protocol negotiation support |
176 testSuccessfulAlpnNegotiationConnection(null, | 171 testSuccessfulAlpnNegotiationConnection(null, null, null); |
177 null, | |
178 null); | |
179 | 172 |
180 testSuccessfulAlpnNegotiationConnection(['a', 'b', 'c'], | 173 testSuccessfulAlpnNegotiationConnection(['a', 'b', 'c'], null, null); |
181 null, | |
182 null); | |
183 | 174 |
184 testSuccessfulAlpnNegotiationConnection(null, | 175 testSuccessfulAlpnNegotiationConnection(null, ['a', 'b', 'c'], null); |
185 ['a', 'b', 'c'], | |
186 null); | |
187 | 176 |
188 testSuccessfulAlpnNegotiationConnection([], | 177 testSuccessfulAlpnNegotiationConnection([], [], null); |
189 [], | |
190 null); | |
191 | 178 |
192 testSuccessfulAlpnNegotiationConnection(['a', 'b', 'c'], | 179 testSuccessfulAlpnNegotiationConnection(['a', 'b', 'c'], [], null); |
193 [], | |
194 null); | |
195 | 180 |
196 testSuccessfulAlpnNegotiationConnection([], | 181 testSuccessfulAlpnNegotiationConnection([], ['a', 'b', 'c'], null); |
197 ['a', 'b', 'c'], | |
198 null); | |
199 | 182 |
200 // Test non-overlapping protocols. The ALPN RFC says the connection | 183 // Test non-overlapping protocols. The ALPN RFC says the connection |
201 // should be terminated, but OpenSSL continues as if no ALPN is present. | 184 // should be terminated, but OpenSSL continues as if no ALPN is present. |
202 // Issue https://github.com/dart-lang/sdk/issues/23580 | 185 // Issue https://github.com/dart-lang/sdk/issues/23580 |
203 // Chromium issue https://code.google.com/p/chromium/issues/detail?id=497770 | 186 // Chromium issue https://code.google.com/p/chromium/issues/detail?id=497770 |
204 testSuccessfulAlpnNegotiationConnection(['a'], ['b'], null); | 187 testSuccessfulAlpnNegotiationConnection(['a'], ['b'], null); |
205 | 188 |
206 // Test too short / too long protocol names. | 189 // Test too short / too long protocol names. |
207 testInvalidArgument([longname256], NAME_LENGTH_ERROR); | 190 testInvalidArgument([longname256], NAME_LENGTH_ERROR); |
208 testInvalidArgument([strangelongname256], NAME_LENGTH_ERROR); | 191 testInvalidArgument([strangelongname256], NAME_LENGTH_ERROR); |
209 testInvalidArgument([''], NAME_LENGTH_ERROR); | 192 testInvalidArgument([''], NAME_LENGTH_ERROR); |
210 testInvalidArgument(tooManyProtocols, MESSAGE_LENGTH_ERROR); | 193 testInvalidArgument(tooManyProtocols, MESSAGE_LENGTH_ERROR); |
211 } | 194 } |
OLD | NEW |