// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. part of "dart:io"; /// A Transport Layer Security (TLS) version. /// /// Only TLS versions supported by `dart:io` are included. class TlsProtocolVersion { /// Transport Layer Security (TLS) Protocol Version 1.2. /// /// See RFC-5246. static const tls1_2 = TlsProtocolVersion._(0x0303); /// Transport Layer Security (TLS) Protocol Version 1.3. /// /// See RFC-8446. static const tls1_3 = TlsProtocolVersion._(0x0304); final int _version; const TlsProtocolVersion._(this._version); static TlsProtocolVersion _fromProtocolVersionConstant(int version) => switch (version) { 0x0303 => tls1_2, 0x0304 => tls1_3, _ => throw ArgumentError.value(version, 'version'), }; } /// The object containing the certificates to trust when making /// a secure client connection, and the certificate chain and /// private key to serve from a secure server. /// /// The [SecureSocket] and [SecureServerSocket] classes take a SecurityContext /// as an argument to their connect and bind methods. /// /// Certificates and keys can be added to a SecurityContext from either PEM /// or PKCS12 containers. /// /// iOS note: Some methods to add, remove, and inspect certificates are not yet /// implemented. However, the platform's built-in trusted certificates can /// be used, by way of [SecurityContext.defaultContext]. abstract final class SecurityContext { /// Creates a new [SecurityContext]. /// /// By default, the created [SecurityContext] contains no keys or certificates. /// These can be added by calling the methods of this class. /// /// If [withTrustedRoots] is passed as `true`, the [SecurityContext] will be /// seeded by the trusted root certificates provided as explained below. To /// obtain a [SecurityContext] containing trusted root certificates, /// [SecurityContext.defaultContext] is usually sufficient, and should /// be used instead. However, if the [SecurityContext] containing the trusted /// root certificates must be modified per-connection, then [withTrustedRoots] /// should be used. external factory SecurityContext({bool withTrustedRoots = false}); /// The default security context used by most operation requiring one. /// /// Secure networking classes with an optional `context` parameter /// use the [defaultContext] object if the parameter is omitted. /// This object can also be accessed, and modified, directly. /// Each isolate has a different [defaultContext] object. /// The [defaultContext] object uses a list of well-known trusted /// certificate authorities as its trusted roots. On Linux and Windows, this /// list is taken from Mozilla, who maintains it as part of Firefox. On, /// MacOS, iOS, and Android, this list comes from the trusted certificates /// stores built into the platforms. external static SecurityContext get defaultContext; /// Sets the private key for a server certificate or client certificate. /// /// A secure connection using this SecurityContext will use this key with /// the server or client certificate to sign and decrypt messages. /// [file] is the path to a PEM or PKCS12 file containing an encrypted /// private key, encrypted with [password]. Assuming it is well-formatted, all /// other contents of [file] are ignored. An unencrypted file can be used, /// but this is not usual. /// /// NB: This function calls [File.readAsBytesSync], and will block on file IO. /// Prefer using [usePrivateKeyBytes]. /// /// iOS note: Only PKCS12 data is supported. It should contain both the private /// key and the certificate chain. On iOS one call to [usePrivateKey] with this /// data is used instead of two calls to [useCertificateChain] and /// [usePrivateKey]. void usePrivateKey(String file, {String? password}); /// Sets the private key for a server certificate or client certificate. /// /// Like [usePrivateKey], but takes the contents of the file as a list /// of bytes. void usePrivateKeyBytes(List keyBytes, {String? password}); /// Add a certificate to the set of trusted X509 certificates /// used by [SecureSocket] client connections. /// /// [file] is the path to a PEM or PKCS12 file containing X509 certificates, /// usually root certificates from certificate authorities. For PKCS12 files, /// [password] is the password for the file. For PEM files, [password] is /// ignored. Assuming it is well-formatted, all other contents of [file] are /// ignored. /// /// NB: This function calls [File.readAsBytesSync], and will block on file IO. /// Prefer using [setTrustedCertificatesBytes]. /// /// iOS note: On iOS, this call takes only the bytes for a single DER /// encoded X509 certificate. It may be called multiple times to add /// multiple trusted certificates to the context. A DER encoded certificate /// can be obtained from a PEM encoded certificate by using the openssl tool: /// ```bash /// $ openssl x509 -outform der -in cert.pem -out cert.der /// ``` void setTrustedCertificates(String file, {String? password}); /// Add a certificate to the set of trusted X509 certificates /// used by [SecureSocket] client connections. /// /// Like [setTrustedCertificates] but takes the contents of the file. void setTrustedCertificatesBytes(List certBytes, {String? password}); /// Sets the chain of X509 certificates served by [SecureServerSocket] /// when making secure connections, including the server certificate. /// /// [file] is a PEM or PKCS12 file containing X509 certificates, starting with /// the root authority and intermediate authorities forming the signed /// chain to the server certificate, and ending with the server certificate. /// The private key for the server certificate is set by [usePrivateKey]. For /// PKCS12 files, [password] is the password for the file. For PEM files, /// [password] is ignored. Assuming it is well-formatted, all /// other contents of [file] are ignored. /// /// NB: This function calls [File.readAsBytesSync], and will block on file IO. /// Prefer using [useCertificateChainBytes]. /// /// iOS note: As noted above, [usePrivateKey] does the job of both /// that call and this one. On iOS, this call is a no-op. void useCertificateChain(String file, {String? password}); /// Sets the chain of X509 certificates served by [SecureServerSocket] /// when making secure connections, including the server certificate. /// /// Like [useCertificateChain] but takes the contents of the file. void useCertificateChainBytes(List chainBytes, {String? password}); /// Sets the list of authority names that a [SecureServerSocket] will advertise /// as accepted when requesting a client certificate from a connecting /// client. /// /// The [file] is a PEM or PKCS12 file containing the accepted signing /// authority certificates - the authority names are extracted from the /// certificates. For PKCS12 files, [password] is the password for the file. /// For PEM files, [password] is ignored. Assuming it is well-formatted, all /// other contents of [file] are ignored. /// /// NB: This function calls [File.readAsBytesSync], and will block on file IO. /// Prefer using [setClientAuthoritiesBytes]. /// /// iOS note: This call is not supported. void setClientAuthorities(String file, {String? password}); /// Sets the list of authority names that a [SecureServerSocket] will advertise /// as accepted, when requesting a client certificate from a connecting /// client. /// /// Like [setClientAuthorities] but takes the contents of the file. void setClientAuthoritiesBytes(List authCertBytes, {String? password}); /// Whether the platform supports ALPN. This always returns true and will be /// removed in a future release. @deprecated external static bool get alpnSupported; /// Sets the list of application-level protocols supported by a client /// connection or server connection. The ALPN (application level protocol /// negotiation) extension to TLS allows a client to send a list of /// protocols in the TLS client hello message, and the server to pick /// one and send the selected one back in its server hello message. /// /// Separate lists of protocols can be sent for client connections and /// for server connections, using the same SecurityContext. The [isServer] /// boolean argument specifies whether to set the list for server connections /// or client connections. void setAlpnProtocols(List protocols, bool isServer); /// If `true`, the [SecurityContext] will allow TLS renegotiation. /// Renegotiation is only supported as a client and the HelloRequest must be /// received at a quiet point in the application protocol. This is sufficient /// to support the legacy use case of requesting a new client certificate /// between an HTTP request and response in (unpipelined) HTTP/1.1. /// NOTE: Renegotiation is an extremely problematic protocol feature and /// should only be used to communicate with legacy servers in environments /// where it is known to be safe. abstract bool allowLegacyUnsafeRenegotiation; /// The minimum TLS version to use when establishing a secure connection. /// /// If the peer does not support `minimumTlsProtocolVersion` or later /// then [SecureSocket.connect] will throw a [TlsException]. /// /// If the value is changed, it will only affect new connections. Existing /// connections will continue to use the protocol that was negotiated with the /// peer. /// /// The default value is [TlsProtocolVersion.tls1_2]. abstract TlsProtocolVersion minimumTlsProtocolVersion; /// 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 protocols`: /// ```plaintext /// opaque ProtocolName<1..2^8-1>; /// /// struct { /// ProtocolName protocol_name_list<2..2^16-1> /// } ProtocolNameList; /// ``` /// The encoding of the opaque `ProtocolName` 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 ~ 2^14 bytes will fail to negotiate a protocol. /// We will be conservative and support only messages up to (1<<13)-1 bytes. static Uint8List _protocolsToLengthEncoding(List? protocols) { if (protocols == null || protocols.length == 0) { return 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 ArgumentError( 'Length of protocol must be between 1 and 255 (was: $length).', ); } } if (expectedLength >= (1 << 13)) { throw ArgumentError('The maximum message length supported is 2^13-1.'); } // Try encoding the `List protocols` array using fast ASCII path. var bytes = Uint8List(expectedLength); int bytesOffset = 0; for (int i = 0; i < protocolsLength; i++) { String proto = protocols[i]; // 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; } static Uint8List _protocolsToLengthEncodingNonAsciiBailout( List protocols, ) { void addProtocol(List outBytes, String protocol) { var protocolBytes = utf8.encode(protocol); var len = protocolBytes.length; if (len > 255) { throw 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 bytes = []; for (var i = 0; i < protocols.length; i++) { addProtocol(bytes, protocols[i]); } if (bytes.length >= (1 << 13)) { throw ArgumentError('The maximum message length supported is 2^13-1.'); } return Uint8List.fromList(bytes); } }