Index: sdk/lib/io/secure_socket.dart |
diff --git a/sdk/lib/io/secure_socket.dart b/sdk/lib/io/secure_socket.dart |
index 6b36ebaeac00abb0e7637ecbe58b8e19fc8130a4..468e1bd44652ac513db10a7ca61e31b2dc2e446c 100644 |
--- a/sdk/lib/io/secure_socket.dart |
+++ b/sdk/lib/io/secure_socket.dart |
@@ -18,16 +18,6 @@ abstract class SecureSocket implements Socket { |
* [host] on port [port]. The returned Future will complete with a |
* [SecureSocket] that is connected and ready for subscription. |
* |
- * If [sendClientCertificate] is set to true, the socket will send a client |
- * certificate if one is requested by the server. |
- * |
- * If [certificateName] is the nickname of a certificate in the certificate |
- * database, that certificate will be sent. |
- * |
- * If [certificateName] is null, which is the usual use case, an |
- * appropriate certificate will be searched for in the database and |
- * sent automatically, based on what the server says it will accept. |
- * |
* [onBadCertificate] is an optional handler for unverifiable certificates. |
* The handler receives the [X509Certificate], and can inspect it and |
* decide (or let the user decide) whether to accept |
@@ -37,14 +27,13 @@ abstract class SecureSocket implements Socket { |
static Future<SecureSocket> connect( |
host, |
int port, |
- {bool sendClientCertificate: false, |
- String certificateName, |
+ {SecurityContext context, |
bool onBadCertificate(X509Certificate certificate), |
+ bool sendClientCertificate, |
List<String> supportedProtocols}) { |
return RawSecureSocket.connect(host, |
port, |
- sendClientCertificate: sendClientCertificate, |
- certificateName: certificateName, |
+ context: context, |
onBadCertificate: onBadCertificate, |
supportedProtocols: supportedProtocols) |
.then((rawSocket) => new SecureSocket._(rawSocket)); |
@@ -79,8 +68,7 @@ abstract class SecureSocket implements Socket { |
static Future<SecureSocket> secure( |
Socket socket, |
{host, |
- bool sendClientCertificate: false, |
- String certificateName, |
+ SecurityContext context, |
bool onBadCertificate(X509Certificate certificate)}) { |
var completer = new Completer(); |
(socket as dynamic)._detachRaw() |
@@ -89,7 +77,7 @@ abstract class SecureSocket implements Socket { |
detachedRaw[0], |
subscription: detachedRaw[1], |
host: host, |
- sendClientCertificate: sendClientCertificate, |
+ context: context, |
onBadCertificate: onBadCertificate); |
}) |
.then((raw) { |
@@ -121,7 +109,7 @@ abstract class SecureSocket implements Socket { |
*/ |
static Future<SecureSocket> secureServer( |
Socket socket, |
- String certificateName, |
+ SecurityContext context, |
{List<int> bufferedData, |
bool requestClientCertificate: false, |
bool requireClientCertificate: false, |
@@ -131,7 +119,7 @@ abstract class SecureSocket implements Socket { |
.then((detachedRaw) { |
return RawSecureSocket.secureServer( |
detachedRaw[0], |
- certificateName, |
+ context, |
subscription: detachedRaw[1], |
bufferedData: bufferedData, |
requestClientCertificate: requestClientCertificate, |
@@ -168,52 +156,6 @@ abstract class SecureSocket implements Socket { |
void renegotiate({bool useSessionCache: true, |
bool requestClientCertificate: false, |
bool requireClientCertificate: false}); |
- |
- /** |
- * Initializes the NSS library. If [initialize] is not called, the library |
- * is automatically initialized as if [initialize] were called with no |
- * arguments. If [initialize] is called more than once, or called after |
- * automatic initialization has happened (when a secure connection is made), |
- * then a TlsException is thrown. |
- * |
- * The optional argument [database] is the path to a certificate database |
- * directory containing root certificates for verifying certificate paths on |
- * client connections, and server certificates to provide on server |
- * connections. The argument [password] should be used when creating |
- * secure server sockets, to allow the private key of the server |
- * certificate to be fetched. If [useBuiltinRoots] is true (the default), |
- * then a built-in set of root certificates for trusted certificate |
- * authorities is merged with the certificates in the database. |
- * The list of built-in root certificates, and documentation about this |
- * default database, is available at |
- * http://www.mozilla.org/projects/security/certs/included/ . |
- * |
- * If the [database] argument is omitted, then only the |
- * builtin root certificates are used. If [useBuiltinRoots] is also false, |
- * then no certificates are available. |
- * |
- * Examples: |
- * 1) Use only the builtin root certificates: |
- * SecureSocket.initialize(); or |
- * |
- * 2) Use a specified database directory and the builtin roots: |
- * SecureSocket.initialize(database: 'path/to/my/database', |
- * password: 'my_password'); |
- * |
- * 3) Use a specified database directory, without builtin roots: |
- * SecureSocket.initialize(database: 'path/to/my/database', |
- * password: 'my_password'. |
- * useBuiltinRoots: false); |
- * |
- * The database should be an NSS certificate database directory |
- * containing a cert9.db file, not a cert8.db file. This version of |
- * the database can be created using the NSS certutil tool with "sql:" in |
- * front of the absolute path of the database directory, or setting the |
- * environment variable [[NSS_DEFAULT_DB_TYPE]] to "sql". |
- */ |
- external static void initialize({String database, |
- String password, |
- bool useBuiltinRoots: true}); |
} |
@@ -224,7 +166,7 @@ abstract class SecureSocket implements Socket { |
* RawSecureServerSocket, also returns RawSecureSocket objects representing |
* the server end of a secure connection. |
* The certificate provided by the server is checked |
- * using the certificate database provided in SecureSocket.initialize, and/or |
+ * using the trusted certificates set in the SecurityContext object and/or |
* the default built-in root certificates. |
*/ |
abstract class RawSecureSocket implements RawSocket { |
@@ -233,8 +175,9 @@ abstract class RawSecureSocket implements RawSocket { |
* host on the given port. The returned Future is completed with the |
* RawSecureSocket when it is connected and ready for subscription. |
* |
- * The certificate provided by the server is checked using the certificate |
- * database provided in [SecureSocket.initialize], and/or the default built-in |
+ * The certificate provided by the server is checked |
+ * using the trusted certificates set in the SecurityContext object and/or |
+ * the default built-in |
* root certificates. If [sendClientCertificate] is |
* set to true, the socket will send a client certificate if one is |
* requested by the server. If [certificateName] is the nickname of |
@@ -252,24 +195,20 @@ abstract class RawSecureSocket implements RawSocket { |
static Future<RawSecureSocket> connect( |
host, |
int port, |
- {bool sendClientCertificate: false, |
- String certificateName, |
+ {SecurityContext context, |
bool onBadCertificate(X509Certificate certificate), |
List<String> supportedProtocols}) { |
_RawSecureSocket._verifyFields( |
host, |
port, |
- certificateName, |
false, |
false, |
false, |
- sendClientCertificate, |
onBadCertificate); |
return RawSocket.connect(host, port) |
.then((socket) { |
return secure(socket, |
- sendClientCertificate: sendClientCertificate, |
- certificateName: certificateName, |
+ context: context, |
onBadCertificate: onBadCertificate, |
supportedProtocols: supportedProtocols); |
}); |
@@ -307,8 +246,7 @@ abstract class RawSecureSocket implements RawSocket { |
RawSocket socket, |
{StreamSubscription subscription, |
host, |
- bool sendClientCertificate: false, |
- String certificateName, |
+ SecurityContext context, |
bool onBadCertificate(X509Certificate certificate), |
List<String> supportedProtocols}) { |
socket.readEventsEnabled = false; |
@@ -316,11 +254,10 @@ abstract class RawSecureSocket implements RawSocket { |
return _RawSecureSocket.connect( |
host != null ? host : socket.address.host, |
socket.port, |
- certificateName, |
is_server: false, |
socket: socket, |
subscription: subscription, |
- sendClientCertificate: sendClientCertificate, |
+ context: context, |
onBadCertificate: onBadCertificate, |
supportedProtocols: supportedProtocols); |
} |
@@ -350,7 +287,7 @@ abstract class RawSecureSocket implements RawSocket { |
*/ |
static Future<RawSecureSocket> secureServer( |
RawSocket socket, |
- String certificateName, |
+ SecurityContext context, |
{StreamSubscription subscription, |
List<int> bufferedData, |
bool requestClientCertificate: false, |
@@ -361,7 +298,7 @@ abstract class RawSecureSocket implements RawSocket { |
return _RawSecureSocket.connect( |
socket.address, |
socket.remotePort, |
- certificateName, |
+ context: context, |
is_server: true, |
socket: socket, |
subscription: subscription, |
@@ -402,15 +339,13 @@ abstract class RawSecureSocket implements RawSocket { |
* X509Certificate represents an SSL certificate, with accessors to |
* get the fields of the certificate. |
*/ |
-class X509Certificate { |
- X509Certificate(this.subject, |
- this.issuer, |
- this.startValidity, |
- this.endValidity); |
- final String subject; |
- final String issuer; |
- final DateTime startValidity; |
- final DateTime endValidity; |
+abstract class X509Certificate { |
+ external factory X509Certificate._(); |
+ |
+ String get subject; |
+ String get issuer; |
+ DateTime get startValidity; |
+ DateTime get endValidity; |
} |
@@ -456,10 +391,9 @@ class _RawSecureSocket extends Stream<RawSocketEvent> |
int _bufferedDataIndex = 0; |
final InternetAddress address; |
final bool is_server; |
- final String certificateName; |
+ SecurityContext context; |
final bool requestClientCertificate; |
final bool requireClientCertificate; |
- final bool sendClientCertificate; |
final Function onBadCertificate; |
var _status = HANDSHAKE; |
@@ -484,8 +418,8 @@ class _RawSecureSocket extends Stream<RawSocketEvent> |
static Future<_RawSecureSocket> connect( |
host, |
int requestedPort, |
- String certificateName, |
{bool is_server, |
+ SecurityContext context, |
RawSocket socket, |
StreamSubscription subscription, |
List<int> bufferedData, |
@@ -494,22 +428,21 @@ class _RawSecureSocket extends Stream<RawSocketEvent> |
bool sendClientCertificate: false, |
bool onBadCertificate(X509Certificate certificate), |
List<String> supportedProtocols}) { |
- _verifyFields(host, requestedPort, certificateName, is_server, |
+ _verifyFields(host, requestedPort, is_server, |
requestClientCertificate, requireClientCertificate, |
- sendClientCertificate, onBadCertificate); |
+ onBadCertificate); |
if (host is InternetAddress) host = host.host; |
var address = socket.address; |
if (host != null) address = address._cloneWithNewHost(host); |
return new _RawSecureSocket(address, |
requestedPort, |
- certificateName, |
is_server, |
+ context, |
socket, |
subscription, |
bufferedData, |
requestClientCertificate, |
requireClientCertificate, |
- sendClientCertificate, |
onBadCertificate, |
supportedProtocols) |
._handshakeComplete.future; |
@@ -518,16 +451,18 @@ class _RawSecureSocket extends Stream<RawSocketEvent> |
_RawSecureSocket( |
this.address, |
int requestedPort, |
- this.certificateName, |
this.is_server, |
+ this.context, |
RawSocket this._socket, |
this._socketSubscription, |
this._bufferedData, |
this.requestClientCertificate, |
this.requireClientCertificate, |
- this.sendClientCertificate, |
this.onBadCertificate(X509Certificate certificate), |
List<String> supportedProtocols) { |
+ if (context == null) { |
+ context = SecurityContext.defaultContext; |
+ } |
_controller = new StreamController<RawSocketEvent>( |
sync: true, |
onListen: _onSubscriptionStateChange, |
@@ -570,144 +505,24 @@ class _RawSecureSocket extends Stream<RawSocketEvent> |
..onDone(_doneHandler); |
} |
try { |
+ var encodedProtocols = |
+ SecurityContext._protocolsToLengthEncoding(supportedProtocols); |
_secureFilter.connect(address.host, |
- (address as dynamic)._in_addr, |
- port, |
+ context, |
is_server, |
- certificateName, |
requestClientCertificate || |
requireClientCertificate, |
requireClientCertificate, |
- sendClientCertificate, |
- _protocolsToLengthEncoding(supportedProtocols)); |
+ // TODO(whesse): Remove sendClientCertificate |
+ // argument, or add it to API. |
+ false, // sendClientCertificate, |
+ encodedProtocols); |
_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(), |
@@ -721,11 +536,9 @@ class _RawSecureSocket extends Stream<RawSocketEvent> |
static void _verifyFields(host, |
int requestedPort, |
- String certificateName, |
bool is_server, |
bool requestClientCertificate, |
bool requireClientCertificate, |
- bool sendClientCertificate, |
Function onBadCertificate) { |
if (host is! String && host is! InternetAddress) { |
throw new ArgumentError("host is not a String or an InternetAddress"); |
@@ -736,21 +549,12 @@ class _RawSecureSocket extends Stream<RawSocketEvent> |
if (requestedPort < 0 || requestedPort > 65535) { |
throw new ArgumentError("requestedPort is not in the range 0..65535"); |
} |
- if (certificateName != null && certificateName is! String) { |
- throw new ArgumentError("certificateName is not null or a String"); |
- } |
- if (certificateName == null && is_server) { |
- throw new ArgumentError("certificateName is null on a server"); |
- } |
if (requestClientCertificate is! bool) { |
throw new ArgumentError("requestClientCertificate is not a bool"); |
} |
if (requireClientCertificate is! bool) { |
throw new ArgumentError("requireClientCertificate is not a bool"); |
} |
- if (sendClientCertificate is! bool) { |
- throw new ArgumentError("sendClientCertificate is not a bool"); |
- } |
if (onBadCertificate != null && onBadCertificate is! Function) { |
throw new ArgumentError("onBadCertificate is not null or a Function"); |
} |
@@ -891,7 +695,7 @@ class _RawSecureSocket extends Stream<RawSocketEvent> |
if (onBadCertificate == null) return false; |
var result = onBadCertificate(certificate); |
if (result is bool) return result; |
- throw new ArgumentError( |
+ throw new HandshakeException( |
"onBadCertificate callback returned non-boolean $result"); |
} |
@@ -1360,10 +1164,8 @@ abstract class _SecureFilter { |
external factory _SecureFilter(); |
void connect(String hostName, |
- Uint8List addr, |
- int port, |
+ SecurityContext context, |
bool is_server, |
- String certificateName, |
bool requestClientCertificate, |
bool requireClientCertificate, |
bool sendClientCertificate, |