diff --git a/CHANGELOG.md b/CHANGELOG.md index f7c3abf..29b5cb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,13 @@ 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). -## 4.0.0 (2021-11-05) +## Unreleased ### Changed - Migrate to SwiftPM. [#210](https://github.com/passepartoutvpn/tunnelkit/issues/210) +- Replace OpenSSL with BoringSSL from SwiftNIO SSL. +- Drop support for TLS security level (not present in BoringSSL). ## 3.5.0 (2021-10-18) diff --git a/Package.resolved b/Package.resolved index f46a77c..7ecc407 100644 --- a/Package.resolved +++ b/Package.resolved @@ -2,12 +2,21 @@ "object": { "pins": [ { - "package": "openssl-apple", - "repositoryURL": "https://github.com/keeshux/openssl-apple", + "package": "swift-nio", + "repositoryURL": "https://github.com/apple/swift-nio.git", "state": { "branch": null, - "revision": "37043e7c92c9fb348d1d668b0402148c9fa9873c", - "version": "1.1.112" + "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" } }, { diff --git a/Package.swift b/Package.swift index ebdbde7..2c763cd 100644 --- a/Package.swift +++ b/Package.swift @@ -6,7 +6,7 @@ import PackageDescription let package = Package( name: "TunnelKit", platforms: [ - .macOS(.v10_15), .iOS(.v12) + .macOS(.v10_15), .iOS(.v13) ], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. @@ -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/keeshux/openssl-apple", from: "1.1.100") + .package(url: "https://github.com/apple/swift-nio-ssl", from: "2.0.0") ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. @@ -126,7 +126,7 @@ let package = Package( dependencies: [ "CTunnelKitCore", "CTunnelKitOpenVPNCore", - "openssl-apple" + .product(name: "NIOSSL", package: "swift-nio-ssl") ]), .target( name: "__TunnelKitUtils", diff --git a/Sources/CTunnelKitOpenVPNProtocol/CryptoAEAD.m b/Sources/CTunnelKitOpenVPNProtocol/CryptoAEAD.m index c84bd0b..400d09d 100644 --- a/Sources/CTunnelKitOpenVPNProtocol/CryptoAEAD.m +++ b/Sources/CTunnelKitOpenVPNProtocol/CryptoAEAD.m @@ -34,9 +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 -#import -#import +@import CNIOBoringSSL; #import "CryptoAEAD.h" #import "CryptoMacros.h" @@ -136,7 +134,7 @@ static const NSInteger CryptoAEADTagLength = 16; TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherInit(self.cipherCtxEnc, NULL, NULL, self.cipherIVEnc, -1); TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherUpdate(self.cipherCtxEnc, NULL, &x, flags->ad, (int)flags->adLength); TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherUpdate(self.cipherCtxEnc, dest + CryptoAEADTagLength, &l1, bytes, (int)length); - TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherFinal(self.cipherCtxEnc, dest + CryptoAEADTagLength + l1, &l2); + TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherFinal_ex(self.cipherCtxEnc, dest + CryptoAEADTagLength + l1, &l2); TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CIPHER_CTX_ctrl(self.cipherCtxEnc, EVP_CTRL_GCM_GET_TAG, CryptoAEADTagLength, dest); *destLength = CryptoAEADTagLength + l1 + l2; @@ -183,7 +181,7 @@ static const NSInteger CryptoAEADTagLength = 16; TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CIPHER_CTX_ctrl(self.cipherCtxDec, EVP_CTRL_GCM_SET_TAG, CryptoAEADTagLength, (uint8_t *)bytes); TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherUpdate(self.cipherCtxDec, NULL, &x, flags->ad, (int)flags->adLength); TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherUpdate(self.cipherCtxDec, dest, &l1, bytes + CryptoAEADTagLength, (int)length - CryptoAEADTagLength); - TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherFinal(self.cipherCtxDec, dest + l1, &l2); + TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherFinal_ex(self.cipherCtxDec, dest + l1, &l2); *destLength = l1 + l2; diff --git a/Sources/CTunnelKitOpenVPNProtocol/CryptoBox.m b/Sources/CTunnelKitOpenVPNProtocol/CryptoBox.m index 340b8fa..4d71197 100644 --- a/Sources/CTunnelKitOpenVPNProtocol/CryptoBox.m +++ b/Sources/CTunnelKitOpenVPNProtocol/CryptoBox.m @@ -34,9 +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 -#import -#import +@import CNIOBoringSSL; #import "CryptoBox.h" #import "CryptoMacros.h" @@ -170,7 +168,7 @@ int code = 1; HMAC_CTX *ctx = HMAC_CTX_new(); - TUNNEL_CRYPTO_TRACK_STATUS(code) HMAC_CTX_reset(ctx); + 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 66e6836..7586671 100644 --- a/Sources/CTunnelKitOpenVPNProtocol/CryptoCBC.m +++ b/Sources/CTunnelKitOpenVPNProtocol/CryptoCBC.m @@ -34,9 +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 -#import -#import +@import CNIOBoringSSL; #import "CryptoCBC.h" #import "CryptoMacros.h" @@ -85,8 +83,8 @@ const NSInteger CryptoCBCMaxHMACLength = 100; self.cipherIVLength = EVP_CIPHER_iv_length(self.cipher); } // as seen in OpenVPN's crypto_openssl.c:md_kt_size() - self.hmacKeyLength = EVP_MD_size(self.digest); - self.digestLength = EVP_MD_size(self.digest); + self.hmacKeyLength = (int)EVP_MD_size(self.digest); + self.digestLength = (int)EVP_MD_size(self.digest); if (cipherName) { self.cipherCtxEnc = EVP_CIPHER_CTX_new(); @@ -160,7 +158,7 @@ const NSInteger CryptoCBCMaxHMACLength = 100; TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherInit(self.cipherCtxEnc, NULL, NULL, outIV, -1); TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherUpdate(self.cipherCtxEnc, outEncrypted, &l1, bytes, (int)length); - TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherFinal(self.cipherCtxEnc, outEncrypted + l1, &l2); + TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherFinal_ex(self.cipherCtxEnc, outEncrypted + l1, &l2); } else { NSAssert(outEncrypted == outIV, @"cipherIVLength is non-zero"); @@ -223,7 +221,7 @@ const NSInteger CryptoCBCMaxHMACLength = 100; TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherInit(self.cipherCtxDec, NULL, NULL, iv, -1); TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherUpdate(self.cipherCtxDec, dest, &l1, encrypted, (int)length - self.digestLength - self.cipherIVLength); - TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherFinal(self.cipherCtxDec, dest + l1, &l2); + TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherFinal_ex(self.cipherCtxDec, dest + l1, &l2); *destLength = l1 + l2; diff --git a/Sources/CTunnelKitOpenVPNProtocol/CryptoCTR.m b/Sources/CTunnelKitOpenVPNProtocol/CryptoCTR.m index 078c5eb..e9abf70 100644 --- a/Sources/CTunnelKitOpenVPNProtocol/CryptoCTR.m +++ b/Sources/CTunnelKitOpenVPNProtocol/CryptoCTR.m @@ -23,9 +23,7 @@ // along with TunnelKit. If not, see . // -#import -#import -#import +@import CNIOBoringSSL; #import "CryptoCTR.h" #import "CryptoMacros.h" @@ -69,7 +67,7 @@ static const NSInteger CryptoCTRTagLength = 32; self.cipherKeyLength = EVP_CIPHER_key_length(self.cipher); self.cipherIVLength = EVP_CIPHER_iv_length(self.cipher); // as seen in OpenVPN's crypto_openssl.c:md_kt_size() - self.hmacKeyLength = EVP_MD_size(self.digest); + self.hmacKeyLength = (int)EVP_MD_size(self.digest); NSAssert(EVP_MD_size(self.digest) == CryptoCTRTagLength, @"Expected digest size to be tag length (%ld)", CryptoCTRTagLength); self.cipherCtxEnc = EVP_CIPHER_CTX_new(); @@ -142,7 +140,7 @@ static const NSInteger CryptoCTRTagLength = 32; TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherInit(self.cipherCtxEnc, NULL, NULL, dest, -1); TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherUpdate(self.cipherCtxEnc, outEncrypted, &l1, bytes, (int)length); - TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherFinal(self.cipherCtxEnc, outEncrypted + l1, &l2); + TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherFinal_ex(self.cipherCtxEnc, outEncrypted + l1, &l2); *destLength = CryptoCTRTagLength + l1 + l2; @@ -183,7 +181,7 @@ static const NSInteger CryptoCTRTagLength = 32; TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherInit(self.cipherCtxDec, NULL, NULL, iv, -1); TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherUpdate(self.cipherCtxDec, dest, &l1, encrypted, (int)length - CryptoCTRTagLength); - TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherFinal(self.cipherCtxDec, dest + l1, &l2); + TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherFinal_ex(self.cipherCtxDec, dest + l1, &l2); *destLength = l1 + l2; diff --git a/Sources/CTunnelKitOpenVPNProtocol/TLSBox.m b/Sources/CTunnelKitOpenVPNProtocol/TLSBox.m index 5cb171d..10e4c6e 100644 --- a/Sources/CTunnelKitOpenVPNProtocol/TLSBox.m +++ b/Sources/CTunnelKitOpenVPNProtocol/TLSBox.m @@ -34,12 +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 -#import -#import -#import -#import -#import +@import CNIOBoringSSL; #import "TLSBox.h" #import "Allocation.h" @@ -61,8 +56,6 @@ int TLSBoxVerifyPeer(int ok, X509_STORE_CTX *ctx) { return ok; } -const NSInteger TLSBoxDefaultSecurityLevel = -1; - @interface TLSBox () @property (nonatomic, strong) NSString *caPEM; @@ -215,7 +208,6 @@ static BIO *create_BIO_from_PEM(NSString *pem) { self.checksEKU = checksEKU; self.checksSANHost = checksSANHost; self.bufferCipherText = allocate_safely(TLSBoxMaxBufferLength); - self.securityLevel = TLSBoxDefaultSecurityLevel; self.hostname = hostname; } return self; @@ -242,9 +234,6 @@ static BIO *create_BIO_from_PEM(NSString *pem) { 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 (self.caPEM) { BIO *bio = create_BIO_from_PEM(self.caPEM); @@ -436,7 +425,7 @@ static BIO *create_BIO_from_PEM(NSString *pem) { X509_free(cert); return NO; } - const int num = sk_ASN1_OBJECT_num(eku); + const int num = (int)sk_ASN1_OBJECT_num(eku); char buffer[100]; BOOL isValid = NO; @@ -457,7 +446,8 @@ static BIO *create_BIO_from_PEM(NSString *pem) { #pragma mark SAN -- (BOOL)verifySANHostWithSSL:(SSL *)ssl { +- (BOOL)verifySANHostWithSSL:(SSL *)ssl +{ X509 *cert = SSL_get_peer_certificate(self.ssl); if (!cert) { return NO; @@ -465,43 +455,43 @@ static BIO *create_BIO_from_PEM(NSString *pem) { GENERAL_NAMES* names = NULL; unsigned char* utf8 = NULL; - names = X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0 ); - if(!names) { + names = X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0); + if (!names) { X509_free(cert); return NO; } - int i = 0, count = sk_GENERAL_NAME_num(names); - if(!count) { + int i = 0, count = (int)sk_GENERAL_NAME_num(names); + if (!count) { X509_free(cert); GENERAL_NAMES_free(names); return NO; } BOOL isValid = NO; - for( i = 0; i < count; ++i ) { + for (i = 0; i < count; ++i) { GENERAL_NAME* entry = sk_GENERAL_NAME_value(names, i); - if(!entry) { + if (!entry) { continue; } - if(GEN_DNS != entry->type) { + if (GEN_DNS != entry->type) { continue; } int len1 = 0, len2 = -1; len1 = ASN1_STRING_to_UTF8(&utf8, entry->d.dNSName); - if(!utf8) { + if (!utf8) { continue; } - len2 = (int)strlen((const char*)utf8); + len2 = (int)strlen((const char *)utf8); - if(len1 != len2) { + if (len1 != len2) { OPENSSL_free(utf8); utf8 = NULL; continue; } - if(utf8 && len1 && len2 && (len1 == len2) && strcmp((const char *)utf8, self.hostname.UTF8String) == 0) { + if (utf8 && len1 && len2 && (len1 == len2) && strcmp((const char *)utf8, self.hostname.UTF8String) == 0) { isValid = YES; break; } @@ -512,10 +502,10 @@ static BIO *create_BIO_from_PEM(NSString *pem) { X509_free(cert); - if(names) { + if (names) { GENERAL_NAMES_free(names); } - if(utf8) { + if (utf8) { OPENSSL_free(utf8); } return isValid; diff --git a/Sources/CTunnelKitOpenVPNProtocol/include/TLSBox.h b/Sources/CTunnelKitOpenVPNProtocol/include/TLSBox.h index 3d9e54d..50c803f 100644 --- a/Sources/CTunnelKitOpenVPNProtocol/include/TLSBox.h +++ b/Sources/CTunnelKitOpenVPNProtocol/include/TLSBox.h @@ -42,8 +42,6 @@ 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 @@ -52,8 +50,6 @@ extern const NSInteger TLSBoxDefaultSecurityLevel; // @interface TLSBox : NSObject -@property (nonatomic, assign) NSInteger securityLevel; // TLSBoxDefaultSecurityLevel for default - + (nullable NSString *)md5ForCertificatePath:(NSString *)path error:(NSError **)error; + (nullable NSString *)md5ForCertificatePEM:(NSString *)pem error:(NSError **)error; + (nullable NSString *)decryptedPrivateKeyFromPath:(NSString *)path passphrase:(NSString *)passphrase error:(NSError **)error; diff --git a/Sources/TunnelKitOpenVPNCore/Configuration.swift b/Sources/TunnelKitOpenVPNCore/Configuration.swift index 34e8fc3..bab1ddf 100644 --- a/Sources/TunnelKitOpenVPNCore/Configuration.swift +++ b/Sources/TunnelKitOpenVPNCore/Configuration.swift @@ -205,9 +205,6 @@ 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? @@ -324,7 +321,6 @@ extension OpenVPN { clientCertificate: clientCertificate, clientKey: clientKey, tlsWrap: tlsWrap, - tlsSecurityLevel: tlsSecurityLevel, keepAliveInterval: keepAliveInterval, keepAliveTimeout: keepAliveTimeout, renegotiatesAfter: renegotiatesAfter, @@ -407,9 +403,6 @@ extension OpenVPN { /// - Seealso: `ConfigurationBuilder.tlsWrap` public let tlsWrap: TLSWrap? - /// - Seealso: `ConfigurationBuilder.tlsSecurityLevel` - public let tlsSecurityLevel: Int? - /// - Seealso: `ConfigurationBuilder.keepAliveInterval` public let keepAliveInterval: TimeInterval? @@ -527,7 +520,6 @@ extension OpenVPN.Configuration { builder.clientCertificate = clientCertificate builder.clientKey = clientKey builder.tlsWrap = tlsWrap - builder.tlsSecurityLevel = tlsSecurityLevel builder.keepAliveInterval = keepAliveInterval builder.keepAliveTimeout = keepAliveTimeout builder.renegotiatesAfter = renegotiatesAfter @@ -586,11 +578,6 @@ 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 ba10138..a751862 100644 --- a/Sources/TunnelKitOpenVPNProtocol/OpenVPNSession.swift +++ b/Sources/TunnelKitOpenVPNProtocol/OpenVPNSession.swift @@ -759,9 +759,6 @@ 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()