Revert to OpenSSL (#233)

* Use an OpenSSL binary without Bitcode
* Restore TLS security level override
* Disable Bitcode completely in Demo
This commit is contained in:
Davide De Rosa 2021-11-24 16:40:19 +01:00 committed by GitHub
parent 74f38d335b
commit b6d3cdc3b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 62 additions and 28 deletions

View File

@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Changed
- Revert to OpenSSL. [#233](https://github.com/passepartoutvpn/tunnelkit/pull/233)
## 4.0.1 (2021-11-18) ## 4.0.1 (2021-11-18)
### Fixed ### Fixed

View File

@ -884,6 +884,7 @@
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 902; CURRENT_PROJECT_VERSION = 902;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_BITCODE = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES; ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
@ -949,6 +950,7 @@
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 902; CURRENT_PROJECT_VERSION = 902;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_BITCODE = NO;
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;

View File

@ -2,21 +2,12 @@
"object": { "object": {
"pins": [ "pins": [
{ {
"package": "swift-nio", "package": "openssl-apple",
"repositoryURL": "https://github.com/apple/swift-nio.git", "repositoryURL": "https://github.com/passepartoutvpn/openssl-apple",
"state": { "state": {
"branch": null, "branch": null,
"revision": "addf69cfe60376c325397c8926589415576b1dd1", "revision": "7fe4127c8ef5a06a2040f521ff1b4c2a9a38f417",
"version": "2.34.0" "version": "1.1.112-no-bitcode"
}
},
{
"package": "swift-nio-ssl",
"repositoryURL": "https://github.com/apple/swift-nio-ssl",
"state": {
"branch": null,
"revision": "08e701df9a3b9108c56f1aef5d9ef9a78fda2846",
"version": "2.16.2"
} }
}, },
{ {

View File

@ -35,7 +35,7 @@ let package = Package(
// Dependencies declare other packages that this package depends on. // Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"), // .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/SwiftyBeaver/SwiftyBeaver", from: "1.9.0"), .package(url: "https://github.com/SwiftyBeaver/SwiftyBeaver", from: "1.9.0"),
.package(url: "https://github.com/apple/swift-nio-ssl", from: "2.0.0") .package(url: "https://github.com/passepartoutvpn/openssl-apple", .exact("1.1.112-no-bitcode"))
], ],
targets: [ targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets are the basic building blocks of a package. A target can define a module or a test suite.
@ -124,7 +124,7 @@ let package = Package(
dependencies: [ dependencies: [
"CTunnelKitCore", "CTunnelKitCore",
"CTunnelKitOpenVPNCore", "CTunnelKitOpenVPNCore",
.product(name: "NIOSSL", package: "swift-nio-ssl") "openssl-apple"
]), ]),
.target( .target(
name: "__TunnelKitUtils", name: "__TunnelKitUtils",

View File

@ -1,10 +1,10 @@
![iOS 12+](https://img.shields.io/badge/ios-12+-green.svg) ![iOS 11+](https://img.shields.io/badge/ios-11+-green.svg)
![macOS 10.15+](https://img.shields.io/badge/macos-10.15+-green.svg) ![macOS 10.15+](https://img.shields.io/badge/macos-10.15+-green.svg)
[![License GPLv3](https://img.shields.io/badge/license-GPLv3-lightgray.svg)](LICENSE) [![License GPLv3](https://img.shields.io/badge/license-GPLv3-lightgray.svg)](LICENSE)
# TunnelKit # TunnelKit
This library provides a generic framework for VPN development and a simplified Swift/Obj-C implementation of the OpenVPN® protocol for the Apple platforms. The crypto layer is built on top of [BoringSSL][dep-boringssl] (as seen in [SwiftNIO SSL][dep-swiftniossl]), which in turn enables support for a certain range of encryption and digest algorithms. This library provides a generic framework for VPN development and a simplified Swift/Obj-C implementation of the OpenVPN® protocol for the Apple platforms. The crypto layer is built on top of [OpenSSL 1.1.1][dep-openssl], which in turn enables support for a certain range of encryption and digest algorithms.
## Getting started ## Getting started
@ -70,12 +70,14 @@ Many other flags are ignored too but it's normally not an issue.
### Requirements ### Requirements
- iOS 13.0+ / macOS 10.15+ - iOS 11.0+ / macOS 10.15+
- SwiftPM 5.3 - SwiftPM 5.3
- Git (preinstalled with Xcode Command Line Tools) - Git (preinstalled with Xcode Command Line Tools)
It's highly recommended to use the Git package provided by [Homebrew][dep-brew]. It's highly recommended to use the Git package provided by [Homebrew][dep-brew].
Make sure to disable Bitcode in "Release" targets (iOS), otherwise the library [would not be able to locate OpenSSL][about-pr-bitcode].
### Demo ### Demo
Download the library codebase locally: Download the library codebase locally:
@ -142,7 +144,7 @@ Here you find `NativeProvider`, a generic way to manage a VPN profile based on t
#### Protocol #### Protocol
Here are the low-level entities on top of which an OpenVPN connection is established. Code is mixed Swift and Obj-C, most of it is not exposed to consumers. The protocol implementation in particular depends on BoringSSL. Here are the low-level entities on top of which an OpenVPN connection is established. Code is mixed Swift and Obj-C, most of it is not exposed to consumers. The protocol implementation in particular depends on OpenSSL.
The entry point is the `OpenVPNSession` class. The networking layer is fully abstract and delegated externally with the use of opaque `IOInterface` (`LinkInterface` and `TunnelInterface`) and `OpenVPNSessionDelegate` protocols. The entry point is the `OpenVPNSession` class. The networking layer is fully abstract and delegated externally with the use of opaque `IOInterface` (`LinkInterface` and `TunnelInterface`) and `OpenVPNSessionDelegate` protocols.
@ -162,7 +164,7 @@ Due to the restrictive license (GPLv2), LZO support is provided as an optional c
## License ## License
Copyright (c) 2020 Davide De Rosa. All rights reserved. Copyright (c) 2021 Davide De Rosa. All rights reserved.
### Part I ### Part I
@ -205,8 +207,6 @@ Website: [passepartoutvpn.app][about-website]
[openvpn]: https://openvpn.net/index.php/open-source/overview.html [openvpn]: https://openvpn.net/index.php/open-source/overview.html
[dep-brew]: https://brew.sh/ [dep-brew]: https://brew.sh/
[dep-boringssl]: https://boringssl.googlesource.com/boringssl/
[dep-swiftniossl]: https://github.com/apple/swift-nio-ssl
[dep-openssl]: https://www.openssl.org/ [dep-openssl]: https://www.openssl.org/
[ne-home]: https://developer.apple.com/documentation/networkextension [ne-home]: https://developer.apple.com/documentation/networkextension
@ -227,6 +227,7 @@ Website: [passepartoutvpn.app][about-website]
[ppl-xmb5]: https://github.com/XMB5 [ppl-xmb5]: https://github.com/XMB5
[ppl-xmb5-xor]: https://github.com/passepartoutvpn/tunnelkit/pull/170 [ppl-xmb5-xor]: https://github.com/passepartoutvpn/tunnelkit/pull/170
[about-tunnelblick-xor]: https://tunnelblick.net/cOpenvpn_xorpatch.html [about-tunnelblick-xor]: https://tunnelblick.net/cOpenvpn_xorpatch.html
[about-pr-bitcode]: https://github.com/passepartoutvpn/tunnelkit/issues/51
[about-twitter]: https://twitter.com/keeshux [about-twitter]: https://twitter.com/keeshux
[about-website]: https://passepartoutvpn.app [about-website]: https://passepartoutvpn.app

View File

@ -34,7 +34,7 @@
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// //
@import CNIOBoringSSL; #import <openssl/evp.h>
#import "CryptoAEAD.h" #import "CryptoAEAD.h"
#import "CryptoMacros.h" #import "CryptoMacros.h"

View File

@ -34,7 +34,9 @@
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// //
@import CNIOBoringSSL; #import <openssl/evp.h>
#import <openssl/hmac.h>
#import <openssl/rand.h>
#import "CryptoBox.h" #import "CryptoBox.h"
#import "CryptoMacros.h" #import "CryptoMacros.h"
@ -168,7 +170,7 @@
int code = 1; int code = 1;
HMAC_CTX *ctx = HMAC_CTX_new(); HMAC_CTX *ctx = HMAC_CTX_new();
HMAC_CTX_reset(ctx); TUNNEL_CRYPTO_TRACK_STATUS(code) HMAC_CTX_reset(ctx);
TUNNEL_CRYPTO_TRACK_STATUS(code) HMAC_Init_ex(ctx, secret, (int)secretLength, EVP_get_digestbyname([digestName cStringUsingEncoding:NSASCIIStringEncoding]), NULL); TUNNEL_CRYPTO_TRACK_STATUS(code) HMAC_Init_ex(ctx, secret, (int)secretLength, EVP_get_digestbyname([digestName cStringUsingEncoding:NSASCIIStringEncoding]), NULL);
TUNNEL_CRYPTO_TRACK_STATUS(code) HMAC_Update(ctx, data, dataLength); TUNNEL_CRYPTO_TRACK_STATUS(code) HMAC_Update(ctx, data, dataLength);
TUNNEL_CRYPTO_TRACK_STATUS(code) HMAC_Final(ctx, hmac, &l); TUNNEL_CRYPTO_TRACK_STATUS(code) HMAC_Final(ctx, hmac, &l);

View File

@ -34,7 +34,9 @@
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// //
@import CNIOBoringSSL; #import <openssl/evp.h>
#import <openssl/hmac.h>
#import <openssl/rand.h>
#import "CryptoCBC.h" #import "CryptoCBC.h"
#import "CryptoMacros.h" #import "CryptoMacros.h"

View File

@ -23,7 +23,8 @@
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>. // along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
// //
@import CNIOBoringSSL; #import <openssl/evp.h>
#import <openssl/hmac.h>
#import "CryptoCTR.h" #import "CryptoCTR.h"
#import "CryptoMacros.h" #import "CryptoMacros.h"

View File

@ -34,7 +34,9 @@
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// //
@import CNIOBoringSSL; #import <openssl/ssl.h>
#import <openssl/x509v3.h>
#import <openssl/err.h>
#import "TLSBox.h" #import "TLSBox.h"
#import "Allocation.h" #import "Allocation.h"
@ -56,6 +58,8 @@ int TLSBoxVerifyPeer(int ok, X509_STORE_CTX *ctx) {
return ok; return ok;
} }
const NSInteger TLSBoxDefaultSecurityLevel = 0;
@interface TLSBox () @interface TLSBox ()
@property (nonatomic, strong) NSString *caPath; @property (nonatomic, strong) NSString *caPath;
@ -179,6 +183,7 @@ int TLSBoxVerifyPeer(int ok, X509_STORE_CTX *ctx) {
self.checksEKU = checksEKU; self.checksEKU = checksEKU;
self.checksSANHost = checksSANHost; self.checksSANHost = checksSANHost;
self.bufferCipherText = allocate_safely(TLSBoxMaxBufferLength); self.bufferCipherText = allocate_safely(TLSBoxMaxBufferLength);
self.securityLevel = TLSBoxDefaultSecurityLevel;
self.hostname = hostname; self.hostname = hostname;
} }
return self; return self;
@ -205,6 +210,10 @@ int TLSBoxVerifyPeer(int ok, X509_STORE_CTX *ctx) {
self.ctx = SSL_CTX_new(TLS_client_method()); self.ctx = SSL_CTX_new(TLS_client_method());
SSL_CTX_set_options(self.ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION); SSL_CTX_set_options(self.ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION);
SSL_CTX_set_verify(self.ctx, SSL_VERIFY_PEER, TLSBoxVerifyPeer); SSL_CTX_set_verify(self.ctx, SSL_VERIFY_PEER, TLSBoxVerifyPeer);
if (self.securityLevel != TLSBoxDefaultSecurityLevel) {
SSL_CTX_set_security_level(self.ctx, (int)self.securityLevel);
}
if (!SSL_CTX_load_verify_locations(self.ctx, [self.caPath cStringUsingEncoding:NSASCIIStringEncoding], NULL)) { if (!SSL_CTX_load_verify_locations(self.ctx, [self.caPath cStringUsingEncoding:NSASCIIStringEncoding], NULL)) {
ERR_print_errors_fp(stdout); ERR_print_errors_fp(stdout);
if (error) { if (error) {

View File

@ -42,6 +42,8 @@ extern const NSInteger TLSBoxMaxBufferLength;
extern NSString *const TLSBoxPeerVerificationErrorNotification; extern NSString *const TLSBoxPeerVerificationErrorNotification;
extern const NSInteger TLSBoxDefaultSecurityLevel;
// //
// cipher text is safe within NSData // cipher text is safe within NSData
// plain text might be sensitive and must avoid NSData // plain text might be sensitive and must avoid NSData
@ -50,6 +52,8 @@ extern NSString *const TLSBoxPeerVerificationErrorNotification;
// //
@interface TLSBox : NSObject @interface TLSBox : NSObject
@property (nonatomic, assign) NSInteger securityLevel; // TLSBoxDefaultSecurityLevel for default
+ (nullable NSString *)md5ForCertificatePath:(NSString *)path error:(NSError **)error; + (nullable NSString *)md5ForCertificatePath:(NSString *)path error:(NSError **)error;
+ (nullable NSString *)decryptedPrivateKeyFromPath:(NSString *)path passphrase:(NSString *)passphrase error:(NSError **)error; + (nullable NSString *)decryptedPrivateKeyFromPath:(NSString *)path passphrase:(NSString *)passphrase error:(NSError **)error;
+ (nullable NSString *)decryptedPrivateKeyFromPEM:(NSString *)pem passphrase:(NSString *)passphrase error:(NSError **)error; + (nullable NSString *)decryptedPrivateKeyFromPEM:(NSString *)pem passphrase:(NSString *)passphrase error:(NSError **)error;

View File

@ -201,6 +201,9 @@ extension OpenVPN {
/// The optional TLS wrapping. /// The optional TLS wrapping.
public var tlsWrap: TLSWrap? public var tlsWrap: TLSWrap?
/// If set, overrides TLS security level (0 = lowest).
public var tlsSecurityLevel: Int?
/// Sends periodical keep-alive packets if set. /// Sends periodical keep-alive packets if set.
public var keepAliveInterval: TimeInterval? public var keepAliveInterval: TimeInterval?
@ -316,6 +319,7 @@ extension OpenVPN {
clientCertificate: clientCertificate, clientCertificate: clientCertificate,
clientKey: clientKey, clientKey: clientKey,
tlsWrap: tlsWrap, tlsWrap: tlsWrap,
tlsSecurityLevel: tlsSecurityLevel,
keepAliveInterval: keepAliveInterval, keepAliveInterval: keepAliveInterval,
keepAliveTimeout: keepAliveTimeout, keepAliveTimeout: keepAliveTimeout,
renegotiatesAfter: renegotiatesAfter, renegotiatesAfter: renegotiatesAfter,
@ -394,6 +398,9 @@ extension OpenVPN {
/// - Seealso: `ConfigurationBuilder.tlsWrap` /// - Seealso: `ConfigurationBuilder.tlsWrap`
public let tlsWrap: TLSWrap? public let tlsWrap: TLSWrap?
/// - Seealso: `ConfigurationBuilder.tlsSecurityLevel`
public let tlsSecurityLevel: Int?
/// - Seealso: `ConfigurationBuilder.keepAliveInterval` /// - Seealso: `ConfigurationBuilder.keepAliveInterval`
public let keepAliveInterval: TimeInterval? public let keepAliveInterval: TimeInterval?
@ -508,6 +515,7 @@ extension OpenVPN.Configuration {
builder.clientCertificate = clientCertificate builder.clientCertificate = clientCertificate
builder.clientKey = clientKey builder.clientKey = clientKey
builder.tlsWrap = tlsWrap builder.tlsWrap = tlsWrap
builder.tlsSecurityLevel = tlsSecurityLevel
builder.keepAliveInterval = keepAliveInterval builder.keepAliveInterval = keepAliveInterval
builder.keepAliveTimeout = keepAliveTimeout builder.keepAliveTimeout = keepAliveTimeout
builder.renegotiatesAfter = renegotiatesAfter builder.renegotiatesAfter = renegotiatesAfter
@ -565,6 +573,11 @@ extension OpenVPN.Configuration {
} else { } else {
log.info("\tTLS wrapping: disabled") log.info("\tTLS wrapping: disabled")
} }
if let tlsSecurityLevel = tlsSecurityLevel {
log.info("\tTLS security level: \(tlsSecurityLevel)")
} else {
log.info("\tTLS security level: default")
}
if let keepAliveSeconds = keepAliveInterval, keepAliveSeconds > 0 { if let keepAliveSeconds = keepAliveInterval, keepAliveSeconds > 0 {
log.info("\tKeep-alive interval: \(keepAliveSeconds.asTimeString)") log.info("\tKeep-alive interval: \(keepAliveSeconds.asTimeString)")
} else { } else {

View File

@ -796,6 +796,9 @@ public class OpenVPNSession: Session {
checksSANHost: configuration.checksSANHost ?? false, checksSANHost: configuration.checksSANHost ?? false,
hostname: configuration.sanHost hostname: configuration.sanHost
) )
if let tlsSecurityLevel = configuration.tlsSecurityLevel {
tls.securityLevel = tlsSecurityLevel
}
negotiationKey.tlsOptional = tls negotiationKey.tlsOptional = tls
do { do {
try negotiationKey.tls.start() try negotiationKey.tls.start()