Replace OpenSSL with BoringSSL from SwiftNIO SSL

- Raise iOS target to 13
- Drop support for TLS security level
- Address warnings about integer conversion (iOS)
This commit is contained in:
Davide De Rosa 2021-11-11 12:33:08 +01:00
parent 50064fc3d0
commit bc776eda85
11 changed files with 50 additions and 77 deletions

View File

@ -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)

View File

@ -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"
}
},
{

View File

@ -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",

View File

@ -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 <openssl/evp.h>
#import <openssl/hmac.h>
#import <openssl/rand.h>
@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;

View File

@ -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 <openssl/evp.h>
#import <openssl/hmac.h>
#import <openssl/rand.h>
@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);

View File

@ -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 <openssl/evp.h>
#import <openssl/hmac.h>
#import <openssl/rand.h>
@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;

View File

@ -23,9 +23,7 @@
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
//
#import <openssl/evp.h>
#import <openssl/hmac.h>
#import <openssl/rand.h>
@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;

View File

@ -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 <openssl/ssl.h>
#import <openssl/err.h>
#import <openssl/evp.h>
#import <openssl/x509v3.h>
#import <openssl/rsa.h>
#import <openssl/pem.h>
@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;

View File

@ -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;

View File

@ -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 {

View File

@ -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()