mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 16:37:43 +00:00
Support setting the minimum acceptable TLS version.
Change-Id: I24775461fb690abdd0a47c5b4e23776c9fe5bfe0 Bug: https://github.com/dart-lang/sdk/issues/54901 Tested: TLS traffic verified in Wireshark, exception behavior verified with a Python test server, property changes verified by unit test CoreLibraryReviewExempt: dart:io only Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/365664 Reviewed-by: Brian Quinlan <bquinlan@google.com> Commit-Queue: Brian Quinlan <bquinlan@google.com> Reviewed-by: Lasse Nielsen <lrn@google.com>
This commit is contained in:
parent
b963976009
commit
496b3e65f3
|
@ -137,6 +137,8 @@ namespace bin {
|
|||
V(SecurityContext_SetTrustedCertificatesBytes, 3) \
|
||||
V(SecurityContext_TrustBuiltinRoots, 1) \
|
||||
V(SecurityContext_SetAllowTlsRenegotiation, 2) \
|
||||
V(SecurityContext_SetMinimumProtocolVersion, 2) \
|
||||
V(SecurityContext_GetMinimumProtocolVersion, 1) \
|
||||
V(SecurityContext_UseCertificateChainBytes, 3) \
|
||||
V(ServerSocket_Accept, 2) \
|
||||
V(ServerSocket_CreateBindListen, 7) \
|
||||
|
|
|
@ -136,6 +136,18 @@ void FUNCTION_NAME(SecurityContext_SetAllowTlsRenegotiation)(
|
|||
"Secure Sockets unsupported on this platform"));
|
||||
}
|
||||
|
||||
void FUNCTION_NAME(SecurityContext_SetMinimumProtocolVersion)(
|
||||
Dart_NativeArguments args) {
|
||||
Dart_ThrowException(DartUtils::NewDartArgumentError(
|
||||
"Secure Sockets unsupported on this platform"));
|
||||
}
|
||||
|
||||
void FUNCTION_NAME(SecurityContext_GetMinimumProtocolVersion)(
|
||||
Dart_NativeArguments args) {
|
||||
Dart_ThrowException(DartUtils::NewDartArgumentError(
|
||||
"Secure Sockets unsupported on this platform"));
|
||||
}
|
||||
|
||||
void FUNCTION_NAME(SecurityContext_UseCertificateChainBytes)(
|
||||
Dart_NativeArguments args) {
|
||||
Dart_ThrowException(DartUtils::NewDartArgumentError(
|
||||
|
|
|
@ -827,6 +827,8 @@ void FUNCTION_NAME(SecurityContext_Allocate)(Dart_NativeArguments args) {
|
|||
SSL_CTX* ctx = SSL_CTX_new(TLS_method());
|
||||
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, SSLCertContext::CertificateCallback);
|
||||
SSL_CTX_set_keylog_callback(ctx, SSLCertContext::KeyLogCallback);
|
||||
// If we change the minimum protocol version here, then the documentation
|
||||
// for `SecurityContext.minimumTlsProtocolVersion` must also be changed.
|
||||
SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
|
||||
SSL_CTX_set_cipher_list(ctx, "HIGH:MEDIUM");
|
||||
SSLCertContext* context = new SSLCertContext(ctx);
|
||||
|
@ -901,6 +903,32 @@ void FUNCTION_NAME(SecurityContext_SetAllowTlsRenegotiation)(
|
|||
context->set_allow_tls_renegotiation(allow);
|
||||
}
|
||||
|
||||
void FUNCTION_NAME(SecurityContext_SetMinimumProtocolVersion)(
|
||||
Dart_NativeArguments args) {
|
||||
SSLCertContext* context = SSLCertContext::GetSecurityContext(args);
|
||||
Dart_Handle protocol_version_handle =
|
||||
ThrowIfError(Dart_GetNativeArgument(args, 1));
|
||||
if (!Dart_IsInteger(protocol_version_handle)) {
|
||||
Dart_ThrowException(DartUtils::NewDartArgumentError(
|
||||
"Non-int argument passed to SetMinimumProtocolVersion"));
|
||||
}
|
||||
|
||||
int protocol_version = DartUtils::GetIntegerValue(protocol_version_handle);
|
||||
if (SSL_CTX_set_min_proto_version(context->context(), protocol_version) ==
|
||||
0) {
|
||||
Dart_ThrowException(DartUtils::NewDartArgumentError(
|
||||
"Invalid protocol version passed to SetMinimumProtocolVersion"));
|
||||
}
|
||||
}
|
||||
|
||||
void FUNCTION_NAME(SecurityContext_GetMinimumProtocolVersion)(
|
||||
Dart_NativeArguments args) {
|
||||
SSLCertContext* context = SSLCertContext::GetSecurityContext(args);
|
||||
|
||||
Dart_SetIntegerReturnValue(args,
|
||||
SSL_CTX_get_min_proto_version(context->context()));
|
||||
}
|
||||
|
||||
void FUNCTION_NAME(X509_Der)(Dart_NativeArguments args) {
|
||||
Dart_SetReturnValue(args, X509Helper::GetDer(args));
|
||||
}
|
||||
|
|
|
@ -227,6 +227,14 @@ base class _SecurityContext extends NativeFieldWrapperClass1
|
|||
|
||||
bool get allowLegacyUnsafeRenegotiation => _allowLegacyUnsafeRenegotiation;
|
||||
|
||||
set minimumTlsProtocolVersion(TlsProtocolVersion version) {
|
||||
_setMinimumProtocolVersion(version._version);
|
||||
}
|
||||
|
||||
TlsProtocolVersion get minimumTlsProtocolVersion =>
|
||||
TlsProtocolVersion._fromProtocolVersionConstant(
|
||||
_getMinimumProtocolVersion());
|
||||
|
||||
@pragma("vm:external-name", "SecurityContext_Allocate")
|
||||
external void _createNativeContext();
|
||||
|
||||
|
@ -279,6 +287,10 @@ base class _SecurityContext extends NativeFieldWrapperClass1
|
|||
external void _trustBuiltinRoots();
|
||||
@pragma("vm:external-name", "SecurityContext_SetAllowTlsRenegotiation")
|
||||
external void _setAllowTlsRenegotiation(bool allow);
|
||||
@pragma("vm:external-name", "SecurityContext_SetMinimumProtocolVersion")
|
||||
external void _setMinimumProtocolVersion(int version);
|
||||
@pragma("vm:external-name", "SecurityContext_GetMinimumProtocolVersion")
|
||||
external int _getMinimumProtocolVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,6 +4,32 @@
|
|||
|
||||
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.
|
||||
|
@ -170,6 +196,18 @@ abstract interface class SecurityContext {
|
|||
/// 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
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright (c) 2024, 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.
|
||||
//
|
||||
// OtherResources=certificates/server_chain.pem
|
||||
// OtherResources=certificates/server_key.pem
|
||||
// OtherResources=certificates/trusted_certs.pem
|
||||
//
|
||||
// This test does not verify that the value set in `minimumTlsProtocolVersion`
|
||||
// appears in the supported versions extension as defined in RPC-8446 4.2.1.
|
||||
import "dart:async";
|
||||
import 'dart:convert';
|
||||
import "dart:io";
|
||||
|
||||
import "package:async_helper/async_helper.dart";
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
late InternetAddress HOST;
|
||||
|
||||
String localFile(path) => Platform.script.resolve(path).toFilePath();
|
||||
|
||||
SecurityContext serverContext = new SecurityContext()
|
||||
..useCertificateChain(localFile('certificates/server_chain.pem'))
|
||||
..usePrivateKey(localFile('certificates/server_key.pem'),
|
||||
password: 'dartdart');
|
||||
|
||||
Future<SecureServerSocket> startEchoServer() {
|
||||
return SecureServerSocket.bind(HOST, 0, serverContext).then((server) {
|
||||
server.listen((SecureSocket client) {
|
||||
client.fold<List<int>>(
|
||||
<int>[], (message, data) => message..addAll(data)).then((message) {
|
||||
client.add(message);
|
||||
client.close();
|
||||
});
|
||||
});
|
||||
return server;
|
||||
});
|
||||
}
|
||||
|
||||
testVersion(SecureServerSocket server, TlsProtocolVersion tlsVersion) async {
|
||||
// NOTE: this test only verifies that `minimumTlsProtocolVersion` does
|
||||
// not cause incorrect behavior when used - the server does *not* actually
|
||||
// verify that the supported versions extension is correctly set.
|
||||
SecurityContext clientContext = new SecurityContext()
|
||||
..minimumTlsProtocolVersion = tlsVersion
|
||||
..setTrustedCertificates(localFile('certificates/trusted_certs.pem'));
|
||||
|
||||
await SecureSocket.connect(HOST, server.port, context: clientContext)
|
||||
.then((socket) async {
|
||||
socket.write("Hello server.");
|
||||
socket.close();
|
||||
Expect.isTrue(await utf8.decoder.bind(socket).contains("Hello server."));
|
||||
});
|
||||
}
|
||||
|
||||
testProperty() {
|
||||
SecurityContext context = new SecurityContext();
|
||||
Expect.equals(TlsProtocolVersion.tls1_2, context.minimumTlsProtocolVersion);
|
||||
context.minimumTlsProtocolVersion = TlsProtocolVersion.tls1_3;
|
||||
Expect.equals(TlsProtocolVersion.tls1_3, context.minimumTlsProtocolVersion);
|
||||
}
|
||||
|
||||
void main() async {
|
||||
asyncStart();
|
||||
await InternetAddress.lookup("localhost").then((hosts) => HOST = hosts.first);
|
||||
final server = await startEchoServer();
|
||||
|
||||
testProperty();
|
||||
await testVersion(server, TlsProtocolVersion.tls1_2);
|
||||
await testVersion(server, TlsProtocolVersion.tls1_3);
|
||||
|
||||
await server.close();
|
||||
asyncEnd();
|
||||
}
|
Loading…
Reference in a new issue