Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2370)

Unified Diff: dart/sdk/lib/io/secure_socket.dart

Issue 625953002: Support for the ALPN extension of the TLS protocol for Client and Server (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « dart/sdk/lib/io/secure_server_socket.dart ('k') | dart/tests/standalone/io/secure_socket_alpn_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: dart/sdk/lib/io/secure_socket.dart
diff --git a/dart/sdk/lib/io/secure_socket.dart b/dart/sdk/lib/io/secure_socket.dart
index f11355ae5a2dd5fb5c5e3213c207bbcc5eabe29f..0faafd49d13c5e36644972085e7cdb9f9f7a7418 100644
--- a/dart/sdk/lib/io/secure_socket.dart
+++ b/dart/sdk/lib/io/secure_socket.dart
@@ -39,12 +39,14 @@ abstract class SecureSocket implements Socket {
int port,
{bool sendClientCertificate: false,
String certificateName,
- bool onBadCertificate(X509Certificate certificate)}) {
+ bool onBadCertificate(X509Certificate certificate),
+ List<String> supportedProtocols}) {
return RawSecureSocket.connect(host,
port,
sendClientCertificate: sendClientCertificate,
certificateName: certificateName,
- onBadCertificate: onBadCertificate)
+ onBadCertificate: onBadCertificate,
+ supportedProtocols: supportedProtocols)
.then((rawSocket) => new SecureSocket._(rawSocket));
}
@@ -122,7 +124,8 @@ abstract class SecureSocket implements Socket {
String certificateName,
{List<int> bufferedData,
bool requestClientCertificate: false,
- bool requireClientCertificate: false}) {
+ bool requireClientCertificate: false,
+ List<String> supportedProtocols}) {
var completer = new Completer();
(socket as dynamic)._detachRaw()
.then((detachedRaw) {
@@ -132,7 +135,8 @@ abstract class SecureSocket implements Socket {
subscription: detachedRaw[1],
bufferedData: bufferedData,
requestClientCertificate: requestClientCertificate,
- requireClientCertificate: requireClientCertificate);
+ requireClientCertificate: requireClientCertificate,
+ supportedProtocols: supportedProtocols);
})
.then((raw) {
completer.complete(new SecureSocket._(raw));
@@ -150,6 +154,11 @@ abstract class SecureSocket implements Socket {
X509Certificate get peerCertificate;
/**
+ * Get the protocol which was selected during protocol negotiation.
+ */
+ String get selectedProtocol;
+
+ /**
* Renegotiate an existing secure connection, renewing the session keys
* and possibly changing the connection properties.
*
@@ -245,7 +254,8 @@ abstract class RawSecureSocket implements RawSocket {
int port,
{bool sendClientCertificate: false,
String certificateName,
- bool onBadCertificate(X509Certificate certificate)}) {
+ bool onBadCertificate(X509Certificate certificate),
+ List<String> supportedProtocols}) {
_RawSecureSocket._verifyFields(
host,
port,
@@ -260,7 +270,8 @@ abstract class RawSecureSocket implements RawSocket {
return secure(socket,
sendClientCertificate: sendClientCertificate,
certificateName: certificateName,
- onBadCertificate: onBadCertificate);
+ onBadCertificate: onBadCertificate,
+ supportedProtocols: supportedProtocols);
});
}
@@ -298,7 +309,8 @@ abstract class RawSecureSocket implements RawSocket {
host,
bool sendClientCertificate: false,
String certificateName,
- bool onBadCertificate(X509Certificate certificate)}) {
+ bool onBadCertificate(X509Certificate certificate),
+ List<String> supportedProtocols}) {
socket.readEventsEnabled = false;
socket.writeEventsEnabled = false;
return _RawSecureSocket.connect(
@@ -309,7 +321,8 @@ abstract class RawSecureSocket implements RawSocket {
socket: socket,
subscription: subscription,
sendClientCertificate: sendClientCertificate,
- onBadCertificate: onBadCertificate);
+ onBadCertificate: onBadCertificate,
+ supportedProtocols: supportedProtocols);
}
/**
@@ -341,7 +354,8 @@ abstract class RawSecureSocket implements RawSocket {
{StreamSubscription subscription,
List<int> bufferedData,
bool requestClientCertificate: false,
- bool requireClientCertificate: false}) {
+ bool requireClientCertificate: false,
+ List<String> supportedProtocols}) {
socket.readEventsEnabled = false;
socket.writeEventsEnabled = false;
return _RawSecureSocket.connect(
@@ -353,7 +367,8 @@ abstract class RawSecureSocket implements RawSocket {
subscription: subscription,
bufferedData: bufferedData,
requestClientCertificate: requestClientCertificate,
- requireClientCertificate: requireClientCertificate);
+ requireClientCertificate: requireClientCertificate,
+ supportedProtocols: supportedProtocols);
}
/**
@@ -375,6 +390,11 @@ abstract class RawSecureSocket implements RawSocket {
* [peerCertificate] will return the server's certificate.
*/
X509Certificate get peerCertificate;
+
+ /**
+ * Get the protocol which was selected during protocol negotiation.
+ */
+ String get selectedProtocol;
}
@@ -459,6 +479,7 @@ class _RawSecureSocket extends Stream<RawSocketEvent>
_SecureFilter _secureFilter = new _SecureFilter();
int _filterPointer;
+ String _selectedProtocol;
static Future<_RawSecureSocket> connect(
host,
@@ -471,7 +492,8 @@ class _RawSecureSocket extends Stream<RawSocketEvent>
bool requestClientCertificate: false,
bool requireClientCertificate: false,
bool sendClientCertificate: false,
- bool onBadCertificate(X509Certificate certificate)}) {
+ bool onBadCertificate(X509Certificate certificate),
+ List<String> supportedProtocols}) {
_verifyFields(host, requestedPort, certificateName, is_server,
requestClientCertificate, requireClientCertificate,
sendClientCertificate, onBadCertificate);
@@ -488,7 +510,8 @@ class _RawSecureSocket extends Stream<RawSocketEvent>
requestClientCertificate,
requireClientCertificate,
sendClientCertificate,
- onBadCertificate)
+ onBadCertificate,
+ supportedProtocols)
._handshakeComplete.future;
}
@@ -503,7 +526,8 @@ class _RawSecureSocket extends Stream<RawSocketEvent>
this.requestClientCertificate,
this.requireClientCertificate,
this.sendClientCertificate,
- this.onBadCertificate(X509Certificate certificate)) {
+ this.onBadCertificate(X509Certificate certificate),
+ List<String> supportedProtocols) {
_controller = new StreamController<RawSocketEvent>(
sync: true,
onListen: _onSubscriptionStateChange,
@@ -548,13 +572,136 @@ class _RawSecureSocket extends Stream<RawSocketEvent>
requestClientCertificate ||
requireClientCertificate,
requireClientCertificate,
- sendClientCertificate);
+ sendClientCertificate,
+ _protocolsToLengthEncoding(supportedProtocols));
_secureHandshake();
} catch (e, s) {
_reportError(e, s);
}
}
+ /// Encodes a set of supported protocols for ALPN/NPN usage.
+ ///
+ /// The `protocols` list is expected to contain protocols in descending order
+ /// of preference.
+ ///
+ /// See RFC 7301 (https://tools.ietf.org/html/rfc7301) for the encoding of
+ /// `List<String> protocols`:
+ /// opaque ProtocolName<1..2^8-1>;
+ ///
+ /// struct {
+ /// ProtocolName protocol_name_list<2..2^16-1>
+ /// } ProtocolNameList;
+ ///
+ /// The encoding of the opaque `ProtocolName<lower..upper>` vector is
+ /// described in RFC 2246: 4.3 Vectors.
+ ///
+ /// Note: Even though this encoding scheme would allow a total
+ /// `ProtocolNameList` length of 65535, this limit cannot be reached. Testing
+ /// showed that more than ~ 65480 bytes will fail to negogiate a protocol.
+ /// We will be conservative and support only messages up to (1<<15) -1 bytes.
+ ///
+ /// Our NSS implementation will support ALPN and NPN transparently. The
+ /// default protocol will be the first in the encoded Uint8List.
+ ///
+ /// NOTE: The NSS library will treat the first protocol as the fallback
+ /// protocol. The remaining ones are sorted in (decreasing) priority order.
+ /// We therefore put the protocol least desired to the front, to make it the
+ /// default.
+ Uint8List _protocolsToLengthEncoding(List<String> protocols) {
+ if (protocols == null || protocols.length == 0) {
+ return new Uint8List(0);
+ }
+ int protocolsLength = protocols.length;
+
+ // Calculate the number of bytes we will need if it is ASCII.
+ int expectedLength = protocolsLength;
+ for (int i = 0; i < protocolsLength; i++) {
+ int length = protocols[i].length;
+ if (length > 0 && length <= 255) {
+ expectedLength += length;
+ } else {
+ throw new ArgumentError(
+ 'Length of protocol must be between 1 and 255 (was: $length).');
+ }
+ }
+
+ if (expectedLength >= (1 << 15)) {
+ throw new ArgumentError(
+ 'The maximum message length supported is 2^15-1.');
+ }
+
+ // Try encoding the `List<String> protocols` array using fast ASCII path.
+ var bytes = new Uint8List(expectedLength);
+ int bytesOffset = 0;
+ for (int i = 0; i < protocolsLength; i++) {
+ // The last protocol will be encoded as the first/default one in the list.
+ // (i.e. rotate `protocols` by 1 to the right).
+ int index = i;
+ if (index == 0) index = protocols.length;
+ String proto = protocols[index - 1];
+
+ // Add length byte.
+ bytes[bytesOffset++] = proto.length;
+ int bits = 0;
+
+ // Add protocol bytes.
+ for (int j = 0; j < proto.length; j++) {
+ var char = proto.codeUnitAt(j);
+ bits |= char;
+ bytes[bytesOffset++] = char & 0xff;
+ }
+
+ // Go slow case if we have encountered anything non-ascii.
+ if (bits > 0x7f) {
+ return _protocolsToLengthEncodingNonAsciiBailout(protocols);
+ }
+ }
+ return bytes;
+ }
+
+ Uint8List _protocolsToLengthEncodingNonAsciiBailout(List<String> protocols) {
+ void addProtocol(List<int> outBytes, String protocol) {
+ var protocolBytes = UTF8.encode(protocol);
+ var len = protocolBytes.length;
+
+ if (len > 255) {
+ throw new ArgumentError(
+ 'Length of protocol must be between 1 and 255 (was: $len)');
+ }
+ // Add length byte.
+ outBytes.add(len);
+
+ // Add protocol bytes.
+ outBytes.addAll(protocolBytes);
+ }
+
+ List<int> bytes = [];
+ addProtocol(bytes, protocols.last);
+ for (var i = 0; i < protocols.length -1; i++) {
+ addProtocol(bytes, protocols[i]);
+ }
+
+ if (bytes.length >= (1 << 15)) {
+ throw new ArgumentError(
+ 'The maximum message length supported is 2^15-1.');
+ }
+
+ return new Uint8List.fromList(bytes);
+ }
+
+ void _addProtocolBytes(List<int> outBytes, String protocol) {
+ var protocolBytes = UTF8.encode(protocol);
+ var len = protocolBytes.length;
+
+ if (len > 255) {
+ throw new ArgumentError(
+ 'Cannot support protocols with more than 255 characters');
+ }
+ outBytes.add(len);
+ outBytes.addAll(protocolBytes);
+ }
+
StreamSubscription listen(void onData(RawSocketEvent data),
{Function onError,
void onDone(),
@@ -732,6 +879,8 @@ class _RawSecureSocket extends Stream<RawSocketEvent>
X509Certificate get peerCertificate => _secureFilter.peerCertificate;
+ String get selectedProtocol => _selectedProtocol;
+
bool _onBadCertificateWrapper(X509Certificate certificate) {
if (onBadCertificate == null) return false;
var result = onBadCertificate(certificate);
@@ -845,8 +994,13 @@ class _RawSecureSocket extends Stream<RawSocketEvent>
_status = CONNECTED;
if (_connectPending) {
_connectPending = false;
- // We don't want user code to run synchronously in this callback.
- Timer.run(() => _handshakeComplete.complete(this));
+ try {
+ _selectedProtocol = _secureFilter.selectedProtocol();
+ // We don't want user code to run synchronously in this callback.
+ Timer.run(() => _handshakeComplete.complete(this));
+ } catch (error, stack) {
+ _handshakeComplete.completeError(error, stack);
+ }
}
}
@@ -1206,7 +1360,8 @@ abstract class _SecureFilter {
String certificateName,
bool requestClientCertificate,
bool requireClientCertificate,
- bool sendClientCertificate);
+ bool sendClientCertificate,
+ Uint8List protocols);
void destroy();
void handshake();
void rehandshake();
« no previous file with comments | « dart/sdk/lib/io/secure_server_socket.dart ('k') | dart/tests/standalone/io/secure_socket_alpn_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698