diff --git a/runtime/bin/io_natives.cc b/runtime/bin/io_natives.cc index 12e64805..e8be2c4c 100644 --- a/runtime/bin/io_natives.cc +++ b/runtime/bin/io_natives.cc @@ -138,6 +138,7 @@ namespace bin { V(SecurityContext_SetClientAuthoritiesBytes, 3) \ V(SecurityContext_SetTrustedCertificatesBytes, 3) \ V(SecurityContext_TrustBuiltinRoots, 1) \ + V(SecurityContext_SetEchConfigList, 2) \ V(SecurityContext_SetAllowTlsRenegotiation, 2) \ V(SecurityContext_UseCertificateChainBytes, 3) \ V(ServerSocket_Accept, 2) \ diff --git a/runtime/bin/secure_socket_filter.cc b/runtime/bin/secure_socket_filter.cc index 25a9bb2a..f898386f 100644 --- a/runtime/bin/secure_socket_filter.cc +++ b/runtime/bin/secure_socket_filter.cc @@ -530,6 +530,8 @@ void SSLFilter::Connect(const char* hostname, status = SSL_set_tlsext_host_name(ssl_, hostname); SecureSocketUtils::CheckStatusSSL(status, "TlsException", "Set SNI host name", ssl_); + + context->UseEchConfigListBytes(ssl_); // Sets the hostname in the certificate-checking object, so it is checked // against the certificate presented by the server. X509_VERIFY_PARAM* certificate_checking_parameters = SSL_get0_param(ssl_); diff --git a/runtime/bin/secure_socket_unsupported.cc b/runtime/bin/secure_socket_unsupported.cc index 5d5241db..dbb082f5 100644 --- a/runtime/bin/secure_socket_unsupported.cc +++ b/runtime/bin/secure_socket_unsupported.cc @@ -124,6 +124,12 @@ void FUNCTION_NAME(SecurityContext_SetTrustedCertificatesBytes)( "Secure Sockets unsupported on this platform")); } +void FUNCTION_NAME(SecurityContext_SetEchConfigList)( + Dart_NativeArguments args) { + Dart_ThrowException(DartUtils::NewDartArgumentError( + "Secure Sockets unsupported on this platform")); +} + void FUNCTION_NAME(SecurityContext_TrustBuiltinRoots)( Dart_NativeArguments args) { Dart_ThrowException(DartUtils::NewDartArgumentError( diff --git a/runtime/bin/security_context.cc b/runtime/bin/security_context.cc index b59212e3..1d0094ad 100644 --- a/runtime/bin/security_context.cc +++ b/runtime/bin/security_context.cc @@ -177,6 +177,20 @@ Dart_Handle X509Helper::WrappedX509Certificate(X509* certificate) { return result; } +void SSLCertContext::UseEchConfigListBytes(SSL* ssl) { + if (ech_config_list_bytes_ == nullptr || ech_config_list_bytes_length_ <= 0) { + return; + } + + int status = 0; + SSL_set_enable_ech_grease(ssl, 1); + status = SSL_set1_ech_config_list(ssl, ech_config_list_bytes_, + ech_config_list_bytes_length_); + + SecureSocketUtils::CheckStatus(status, "TlsException", + "Failure setting ech config list"); +} + static int SetTrustedCertificatesBytesPKCS12(SSL_CTX* context, ScopedMemBIO* bio, const char* password) { @@ -840,6 +854,29 @@ void FUNCTION_NAME(SecurityContext_SetTrustedCertificatesBytes)( context->SetTrustedCertificatesBytes(cert_bytes, password); } +void FUNCTION_NAME(SecurityContext_SetEchConfigList)( + Dart_NativeArguments args) { + SSLCertContext* context = SSLCertContext::GetSecurityContext(args); + Dart_Handle ech_config_list_bytes_handle = + ThrowIfError(Dart_GetNativeArgument(args, 1)); + ASSERT(ech_config_list_bytes_handle == nullptr); + + intptr_t bytes_length = 0; + Dart_Handle result = + Dart_ListLength(ech_config_list_bytes_handle, &bytes_length); + ThrowIfError(result); + uint8_t* buffer = new uint8_t[bytes_length]; + result = Dart_ListGetAsBytes(ech_config_list_bytes_handle, 0, buffer, + bytes_length); + ThrowIfError(result); + + if (bytes_length > 0) { + context->set_ech_config_list_bytes(buffer, bytes_length); + } + free(buffer); +} + + void FUNCTION_NAME(SecurityContext_SetClientAuthoritiesBytes)( Dart_NativeArguments args) { SSLCertContext* context = SSLCertContext::GetSecurityContext(args); diff --git a/runtime/bin/security_context.h b/runtime/bin/security_context.h index a9eec35d..513c3dc3 100644 --- a/runtime/bin/security_context.h +++ b/runtime/bin/security_context.h @@ -31,12 +31,15 @@ class SSLCertContext : public ReferenceCounted { : ReferenceCounted(), context_(context), alpn_protocol_string_(nullptr), + ech_config_list_bytes_(nullptr), + ech_config_list_bytes_length_(0), trust_builtin_(false), allow_tls_renegotiation_(false) {} ~SSLCertContext() { SSL_CTX_free(context_); free(alpn_protocol_string_); + free(ech_config_list_bytes_); } static int CertificateCallback(int preverify_ok, X509_STORE_CTX* store_ctx); @@ -62,6 +65,8 @@ class SSLCertContext : public ReferenceCounted { void SetTrustedCertificatesBytes(Dart_Handle cert_bytes, const char* password); + void UseEchConfigListBytes(SSL* ssl); + void SetClientAuthoritiesBytes(Dart_Handle client_authorities_bytes, const char* password); @@ -81,6 +86,20 @@ class SSLCertContext : public ReferenceCounted { alpn_protocol_string_ = protocol_string; } + void set_ech_config_list_bytes(uint8_t* ech_config_list_bytes, + intptr_t bytes_length) { + if (ech_config_list_bytes_ != nullptr) { + free(ech_config_list_bytes_); + ech_config_list_bytes_length_ = 0; + } + + uint8_t* buffer = new uint8_t[bytes_length]; + memcpy(buffer, ech_config_list_bytes, bytes_length * sizeof(uint8_t)); + + ech_config_list_bytes_ = buffer; + ech_config_list_bytes_length_ = bytes_length; + } + bool trust_builtin() const { return trust_builtin_; } void set_allow_tls_renegotiation(bool allow) { @@ -116,6 +135,8 @@ class SSLCertContext : public ReferenceCounted { SSL_CTX* context_; uint8_t* alpn_protocol_string_; + uint8_t* ech_config_list_bytes_; + intptr_t ech_config_list_bytes_length_; bool trust_builtin_; bool allow_tls_renegotiation_; diff --git a/sdk/lib/_internal/vm/bin/secure_socket_patch.dart b/sdk/lib/_internal/vm/bin/secure_socket_patch.dart index 9f6641dc..35b5be54 100644 --- a/sdk/lib/_internal/vm/bin/secure_socket_patch.dart +++ b/sdk/lib/_internal/vm/bin/secure_socket_patch.dart @@ -7,8 +7,7 @@ part of "common_patch.dart"; @patch class SecureSocket { @patch - factory SecureSocket._(RawSecureSocket rawSocket) => - new _SecureSocket(rawSocket); + factory SecureSocket._(RawSecureSocket rawSocket) => new _SecureSocket(rawSocket); } @patch @@ -31,9 +30,7 @@ class _SecureSocket extends _Socket implements SecureSocket { _SecureSocket(RawSecureSocket raw) : super(raw); void renegotiate( - {bool useSessionCache = true, - bool requestClientCertificate = false, - bool requireClientCertificate = false}) {} + {bool useSessionCache = true, bool requestClientCertificate = false, bool requireClientCertificate = false}) {} X509Certificate? get peerCertificate { if (_raw == null) { @@ -60,8 +57,7 @@ class _SecureSocket extends _Socket implements SecureSocket { * native code can access the same data. */ @pragma("vm:entry-point") -class _SecureFilterImpl extends NativeFieldWrapperClass1 - implements _SecureFilter { +class _SecureFilterImpl extends NativeFieldWrapperClass1 implements _SecureFilter { // Performance is improved if a full buffer of plaintext fits // in the encrypted buffer, when encrypted. // SIZE and ENCRYPTED_SIZE are referenced from C++. @@ -73,19 +69,13 @@ class _SecureFilterImpl extends NativeFieldWrapperClass1 _SecureFilterImpl._() { buffers = <_ExternalBuffer>[ for (int i = 0; i < _RawSecureSocket.bufferCount; ++i) - new _ExternalBuffer( - _RawSecureSocket._isBufferEncrypted(i) ? ENCRYPTED_SIZE : SIZE), + new _ExternalBuffer(_RawSecureSocket._isBufferEncrypted(i) ? ENCRYPTED_SIZE : SIZE), ]; } @pragma("vm:external-name", "SecureSocket_Connect") - external void connect( - String hostName, - SecurityContext context, - bool isServer, - bool requestClientCertificate, - bool requireClientCertificate, - Uint8List protocols); + external void connect(String hostName, SecurityContext context, bool isServer, bool requestClientCertificate, + bool requireClientCertificate, Uint8List protocols); void destroy() { buffers = null; @@ -102,8 +92,7 @@ class _SecureFilterImpl extends NativeFieldWrapperClass1 external void _markAsTrusted(int certificatePtr, bool isTrusted); @pragma("vm:external-name", "SecureSocket_NewX509CertificateWrapper") - external static X509Certificate _newX509CertificateWrapper( - int certificatePtr); + external static X509Certificate _newX509CertificateWrapper(int certificatePtr); Future handshake() { Completer evaluatorCompleter = Completer(); @@ -122,8 +111,7 @@ class _SecureFilterImpl extends NativeFieldWrapperClass1 if (!isTrusted) { if (badCertificateCallback != null) { try { - isTrusted = badCertificateCallback!( - _newX509CertificateWrapper(certificatePtr)); + isTrusted = badCertificateCallback!(_newX509CertificateWrapper(certificatePtr)); } catch (e, st) { evaluatorCompleter.completeError(e, st); rpEvaluateResponse.close(); @@ -167,8 +155,7 @@ class _SecureFilterImpl extends NativeFieldWrapperClass1 external X509Certificate? get peerCertificate; @pragma("vm:external-name", "SecureSocket_RegisterBadCertificateCallback") - external void _registerBadCertificateCallback( - bool Function(X509Certificate) callback); + external void _registerBadCertificateCallback(bool Function(X509Certificate) callback); bool Function(X509Certificate)? badCertificateCallback; @@ -178,8 +165,7 @@ class _SecureFilterImpl extends NativeFieldWrapperClass1 } @pragma("vm:external-name", "SecureSocket_RegisterHandshakeCompleteCallback") - external void registerHandshakeCompleteCallback( - Function handshakeCompleteHandler); + external void registerHandshakeCompleteCallback(Function handshakeCompleteHandler); @pragma("vm:external-name", "SecureSocket_RegisterKeyLogPort") external void registerKeyLogPort(SendPort port); @@ -208,8 +194,7 @@ class SecurityContext { static bool get alpnSupported => true; } -class _SecurityContext extends NativeFieldWrapperClass1 - implements SecurityContext { +class _SecurityContext extends NativeFieldWrapperClass1 implements SecurityContext { bool _allowLegacyUnsafeRenegotiation = false; _SecurityContext(bool withTrustedRoots) { @@ -245,8 +230,10 @@ class _SecurityContext extends NativeFieldWrapperClass1 } @pragma("vm:external-name", "SecurityContext_SetTrustedCertificatesBytes") - external void setTrustedCertificatesBytes(List certBytes, - {String? password}); + external void setTrustedCertificatesBytes(List certBytes, {String? password}); + + @pragma("vm:external-name", "SecurityContext_SetEchConfigList") + external void setEchConfigList(List bytes); void useCertificateChain(String file, {String? password}) { List bytes = (new File(file)).readAsBytesSync(); @@ -254,8 +241,7 @@ class _SecurityContext extends NativeFieldWrapperClass1 } @pragma("vm:external-name", "SecurityContext_UseCertificateChainBytes") - external void useCertificateChainBytes(List chainBytes, - {String? password}); + external void useCertificateChainBytes(List chainBytes, {String? password}); void setClientAuthorities(String file, {String? password}) { List bytes = (new File(file)).readAsBytesSync(); @@ -263,12 +249,10 @@ class _SecurityContext extends NativeFieldWrapperClass1 } @pragma("vm:external-name", "SecurityContext_SetClientAuthoritiesBytes") - external void setClientAuthoritiesBytes(List authCertBytes, - {String? password}); + external void setClientAuthoritiesBytes(List authCertBytes, {String? password}); void setAlpnProtocols(List protocols, bool isServer) { - Uint8List encodedProtocols = - SecurityContext._protocolsToLengthEncoding(protocols); + Uint8List encodedProtocols = SecurityContext._protocolsToLengthEncoding(protocols); _setAlpnProtocols(encodedProtocols, isServer); } @@ -284,8 +268,7 @@ class _SecurityContext extends NativeFieldWrapperClass1 * _X509CertificateImpl wraps an X509 certificate object held by the BoringSSL * library. It exposes the fields of the certificate object. */ -class _X509CertificateImpl extends NativeFieldWrapperClass1 - implements X509Certificate { +class _X509CertificateImpl extends NativeFieldWrapperClass1 implements X509Certificate { // The native field must be set manually on a new object, in native code. // This is done by WrappedX509Certificate in security_context.cc. _X509CertificateImpl._(); @@ -307,8 +290,7 @@ class _X509CertificateImpl extends NativeFieldWrapperClass1 @pragma("vm:external-name", "X509_Issuer") external String get issuer; DateTime get startValidity { - return new DateTime.fromMillisecondsSinceEpoch(_startValidity(), - isUtc: true); + return new DateTime.fromMillisecondsSinceEpoch(_startValidity(), isUtc: true); } DateTime get endValidity { diff --git a/sdk/lib/io/security_context.dart b/sdk/lib/io/security_context.dart index 8e9839e7..09653661 100644 --- a/sdk/lib/io/security_context.dart +++ b/sdk/lib/io/security_context.dart @@ -90,6 +90,8 @@ abstract class SecurityContext { /// ``` void setTrustedCertificates(String file, {String? password}); + void setEchConfigList(List configList); + /// Add a certificate to the set of trusted X509 certificates /// used by [SecureSocket] client connections. /// @@ -204,14 +206,12 @@ abstract class SecurityContext { if (length > 0 && length <= 255) { expectedLength += length; } else { - throw new ArgumentError( - 'Length of protocol must be between 1 and 255 (was: $length).'); + throw new ArgumentError('Length of protocol must be between 1 and 255 (was: $length).'); } } if (expectedLength >= (1 << 13)) { - throw new ArgumentError( - 'The maximum message length supported is 2^13-1.'); + throw new ArgumentError('The maximum message length supported is 2^13-1.'); } // Try encoding the `List protocols` array using fast ASCII path. @@ -239,15 +239,13 @@ abstract class SecurityContext { return bytes; } - static Uint8List _protocolsToLengthEncodingNonAsciiBailout( - List protocols) { + static Uint8List _protocolsToLengthEncodingNonAsciiBailout(List protocols) { void addProtocol(List 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)'); + throw new ArgumentError('Length of protocol must be between 1 and 255 (was: $len)'); } // Add length byte. outBytes.add(len); @@ -262,8 +260,7 @@ abstract class SecurityContext { } if (bytes.length >= (1 << 13)) { - throw new ArgumentError( - 'The maximum message length supported is 2^13-1.'); + throw new ArgumentError('The maximum message length supported is 2^13-1.'); } return new Uint8List.fromList(bytes);