From b6d3cdc3b12a01816b9728b562c5cce700c8977b Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Wed, 24 Nov 2021 16:40:19 +0100 Subject: [PATCH] Revert to OpenSSL (#233) * Use an OpenSSL binary without Bitcode * Restore TLS security level override * Disable Bitcode completely in Demo --- CHANGELOG.md | 6 ++++++ Demo/TunnelKit.xcodeproj/project.pbxproj | 2 ++ Package.resolved | 17 ++++------------- Package.swift | 4 ++-- README.md | 15 ++++++++------- Sources/CTunnelKitOpenVPNProtocol/CryptoAEAD.m | 2 +- Sources/CTunnelKitOpenVPNProtocol/CryptoBox.m | 6 ++++-- Sources/CTunnelKitOpenVPNProtocol/CryptoCBC.m | 4 +++- Sources/CTunnelKitOpenVPNProtocol/CryptoCTR.m | 3 ++- Sources/CTunnelKitOpenVPNProtocol/TLSBox.m | 11 ++++++++++- .../CTunnelKitOpenVPNProtocol/include/TLSBox.h | 4 ++++ .../TunnelKitOpenVPNCore/Configuration.swift | 13 +++++++++++++ .../OpenVPNSession.swift | 3 +++ 13 files changed, 62 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41062ff..eb0f18a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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/), 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) ### Fixed diff --git a/Demo/TunnelKit.xcodeproj/project.pbxproj b/Demo/TunnelKit.xcodeproj/project.pbxproj index db41e02..a51f439 100644 --- a/Demo/TunnelKit.xcodeproj/project.pbxproj +++ b/Demo/TunnelKit.xcodeproj/project.pbxproj @@ -884,6 +884,7 @@ COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 902; DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_BITCODE = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -949,6 +950,7 @@ COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 902; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_BITCODE = NO; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; diff --git a/Package.resolved b/Package.resolved index 7ecc407..4da7c1e 100644 --- a/Package.resolved +++ b/Package.resolved @@ -2,21 +2,12 @@ "object": { "pins": [ { - "package": "swift-nio", - "repositoryURL": "https://github.com/apple/swift-nio.git", + "package": "openssl-apple", + "repositoryURL": "https://github.com/passepartoutvpn/openssl-apple", "state": { "branch": null, - "revision": "addf69cfe60376c325397c8926589415576b1dd1", - "version": "2.34.0" - } - }, - { - "package": "swift-nio-ssl", - "repositoryURL": "https://github.com/apple/swift-nio-ssl", - "state": { - "branch": null, - "revision": "08e701df9a3b9108c56f1aef5d9ef9a78fda2846", - "version": "2.16.2" + "revision": "7fe4127c8ef5a06a2040f521ff1b4c2a9a38f417", + "version": "1.1.112-no-bitcode" } }, { diff --git a/Package.swift b/Package.swift index c2755b6..d7667dd 100644 --- a/Package.swift +++ b/Package.swift @@ -35,7 +35,7 @@ let package = Package( // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.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 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: [ "CTunnelKitCore", "CTunnelKitOpenVPNCore", - .product(name: "NIOSSL", package: "swift-nio-ssl") + "openssl-apple" ]), .target( name: "__TunnelKitUtils", diff --git a/README.md b/README.md index 52c887e..9e9026d 100644 --- a/README.md +++ b/README.md @@ -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) [![License GPLv3](https://img.shields.io/badge/license-GPLv3-lightgray.svg)](LICENSE) # 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 @@ -70,12 +70,14 @@ Many other flags are ignored too but it's normally not an issue. ### Requirements -- iOS 13.0+ / macOS 10.15+ +- iOS 11.0+ / macOS 10.15+ - SwiftPM 5.3 - Git (preinstalled with Xcode Command Line Tools) 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 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 -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. @@ -162,7 +164,7 @@ Due to the restrictive license (GPLv2), LZO support is provided as an optional c ## License -Copyright (c) 2020 Davide De Rosa. All rights reserved. +Copyright (c) 2021 Davide De Rosa. All rights reserved. ### Part I @@ -205,8 +207,6 @@ Website: [passepartoutvpn.app][about-website] [openvpn]: https://openvpn.net/index.php/open-source/overview.html [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/ [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-xor]: https://github.com/passepartoutvpn/tunnelkit/pull/170 [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-website]: https://passepartoutvpn.app diff --git a/Sources/CTunnelKitOpenVPNProtocol/CryptoAEAD.m b/Sources/CTunnelKitOpenVPNProtocol/CryptoAEAD.m index 86cb4a0..8259c73 100644 --- a/Sources/CTunnelKitOpenVPNProtocol/CryptoAEAD.m +++ b/Sources/CTunnelKitOpenVPNProtocol/CryptoAEAD.m @@ -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. // -@import CNIOBoringSSL; +#import #import "CryptoAEAD.h" #import "CryptoMacros.h" diff --git a/Sources/CTunnelKitOpenVPNProtocol/CryptoBox.m b/Sources/CTunnelKitOpenVPNProtocol/CryptoBox.m index ec54671..b82ee85 100644 --- a/Sources/CTunnelKitOpenVPNProtocol/CryptoBox.m +++ b/Sources/CTunnelKitOpenVPNProtocol/CryptoBox.m @@ -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. // -@import CNIOBoringSSL; +#import +#import +#import #import "CryptoBox.h" #import "CryptoMacros.h" @@ -168,7 +170,7 @@ int code = 1; 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_Update(ctx, data, dataLength); TUNNEL_CRYPTO_TRACK_STATUS(code) HMAC_Final(ctx, hmac, &l); diff --git a/Sources/CTunnelKitOpenVPNProtocol/CryptoCBC.m b/Sources/CTunnelKitOpenVPNProtocol/CryptoCBC.m index c332376..86de248 100644 --- a/Sources/CTunnelKitOpenVPNProtocol/CryptoCBC.m +++ b/Sources/CTunnelKitOpenVPNProtocol/CryptoCBC.m @@ -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. // -@import CNIOBoringSSL; +#import +#import +#import #import "CryptoCBC.h" #import "CryptoMacros.h" diff --git a/Sources/CTunnelKitOpenVPNProtocol/CryptoCTR.m b/Sources/CTunnelKitOpenVPNProtocol/CryptoCTR.m index 3225c84..ebfbdd8 100644 --- a/Sources/CTunnelKitOpenVPNProtocol/CryptoCTR.m +++ b/Sources/CTunnelKitOpenVPNProtocol/CryptoCTR.m @@ -23,7 +23,8 @@ // along with TunnelKit. If not, see . // -@import CNIOBoringSSL; +#import +#import #import "CryptoCTR.h" #import "CryptoMacros.h" diff --git a/Sources/CTunnelKitOpenVPNProtocol/TLSBox.m b/Sources/CTunnelKitOpenVPNProtocol/TLSBox.m index 3826c54..7673dca 100644 --- a/Sources/CTunnelKitOpenVPNProtocol/TLSBox.m +++ b/Sources/CTunnelKitOpenVPNProtocol/TLSBox.m @@ -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. // -@import CNIOBoringSSL; +#import +#import +#import #import "TLSBox.h" #import "Allocation.h" @@ -56,6 +58,8 @@ int TLSBoxVerifyPeer(int ok, X509_STORE_CTX *ctx) { return ok; } +const NSInteger TLSBoxDefaultSecurityLevel = 0; + @interface TLSBox () @property (nonatomic, strong) NSString *caPath; @@ -179,6 +183,7 @@ int TLSBoxVerifyPeer(int ok, X509_STORE_CTX *ctx) { self.checksEKU = checksEKU; self.checksSANHost = checksSANHost; self.bufferCipherText = allocate_safely(TLSBoxMaxBufferLength); + self.securityLevel = TLSBoxDefaultSecurityLevel; self.hostname = hostname; } return self; @@ -205,6 +210,10 @@ int TLSBoxVerifyPeer(int ok, X509_STORE_CTX *ctx) { 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_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)) { ERR_print_errors_fp(stdout); if (error) { diff --git a/Sources/CTunnelKitOpenVPNProtocol/include/TLSBox.h b/Sources/CTunnelKitOpenVPNProtocol/include/TLSBox.h index bc1f975..91ebd25 100644 --- a/Sources/CTunnelKitOpenVPNProtocol/include/TLSBox.h +++ b/Sources/CTunnelKitOpenVPNProtocol/include/TLSBox.h @@ -42,6 +42,8 @@ extern const NSInteger TLSBoxMaxBufferLength; extern NSString *const TLSBoxPeerVerificationErrorNotification; +extern const NSInteger TLSBoxDefaultSecurityLevel; + // // cipher text is safe within NSData // plain text might be sensitive and must avoid NSData @@ -50,6 +52,8 @@ extern NSString *const TLSBoxPeerVerificationErrorNotification; // @interface TLSBox : NSObject +@property (nonatomic, assign) NSInteger securityLevel; // TLSBoxDefaultSecurityLevel for default + + (nullable NSString *)md5ForCertificatePath:(NSString *)path 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; diff --git a/Sources/TunnelKitOpenVPNCore/Configuration.swift b/Sources/TunnelKitOpenVPNCore/Configuration.swift index 2b3f76a..0795b84 100644 --- a/Sources/TunnelKitOpenVPNCore/Configuration.swift +++ b/Sources/TunnelKitOpenVPNCore/Configuration.swift @@ -201,6 +201,9 @@ extension OpenVPN { /// The optional TLS wrapping. public var tlsWrap: TLSWrap? + /// If set, overrides TLS security level (0 = lowest). + public var tlsSecurityLevel: Int? + /// Sends periodical keep-alive packets if set. public var keepAliveInterval: TimeInterval? @@ -316,6 +319,7 @@ extension OpenVPN { clientCertificate: clientCertificate, clientKey: clientKey, tlsWrap: tlsWrap, + tlsSecurityLevel: tlsSecurityLevel, keepAliveInterval: keepAliveInterval, keepAliveTimeout: keepAliveTimeout, renegotiatesAfter: renegotiatesAfter, @@ -394,6 +398,9 @@ extension OpenVPN { /// - Seealso: `ConfigurationBuilder.tlsWrap` public let tlsWrap: TLSWrap? + /// - Seealso: `ConfigurationBuilder.tlsSecurityLevel` + public let tlsSecurityLevel: Int? + /// - Seealso: `ConfigurationBuilder.keepAliveInterval` public let keepAliveInterval: TimeInterval? @@ -508,6 +515,7 @@ extension OpenVPN.Configuration { builder.clientCertificate = clientCertificate builder.clientKey = clientKey builder.tlsWrap = tlsWrap + builder.tlsSecurityLevel = tlsSecurityLevel builder.keepAliveInterval = keepAliveInterval builder.keepAliveTimeout = keepAliveTimeout builder.renegotiatesAfter = renegotiatesAfter @@ -565,6 +573,11 @@ extension OpenVPN.Configuration { } else { 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 { log.info("\tKeep-alive interval: \(keepAliveSeconds.asTimeString)") } else { diff --git a/Sources/TunnelKitOpenVPNProtocol/OpenVPNSession.swift b/Sources/TunnelKitOpenVPNProtocol/OpenVPNSession.swift index ea26b5d..99ecb40 100644 --- a/Sources/TunnelKitOpenVPNProtocol/OpenVPNSession.swift +++ b/Sources/TunnelKitOpenVPNProtocol/OpenVPNSession.swift @@ -796,6 +796,9 @@ public class OpenVPNSession: Session { checksSANHost: configuration.checksSANHost ?? false, hostname: configuration.sanHost ) + if let tlsSecurityLevel = configuration.tlsSecurityLevel { + tls.securityLevel = tlsSecurityLevel + } negotiationKey.tlsOptional = tls do { try negotiationKey.tls.start()