Make CA non-optional
Fix up nullability qualifiers in TLSBox. Fixes #26
This commit is contained in:
parent
d0a46fe20e
commit
093774535d
|
@ -164,8 +164,8 @@ extension TunnelKitProvider {
|
||||||
/// The message digest algorithm.
|
/// The message digest algorithm.
|
||||||
public var digest: SessionProxy.Digest
|
public var digest: SessionProxy.Digest
|
||||||
|
|
||||||
/// The optional CA certificate to validate server against. Set to `nil` to disable CA validation (default).
|
/// The CA certificate to validate server against.
|
||||||
public var ca: CryptoContainer?
|
public let ca: CryptoContainer
|
||||||
|
|
||||||
/// The optional client certificate to authenticate with. Set to `nil` to disable client authentication (default).
|
/// The optional client certificate to authenticate with. Set to `nil` to disable client authentication (default).
|
||||||
public var clientCertificate: CryptoContainer?
|
public var clientCertificate: CryptoContainer?
|
||||||
|
@ -200,14 +200,16 @@ extension TunnelKitProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Default initializer.
|
Default initializer.
|
||||||
|
|
||||||
|
- Parameter ca: The CA certificate.
|
||||||
*/
|
*/
|
||||||
public init() {
|
public init(ca: CryptoContainer) {
|
||||||
prefersResolvedAddresses = false
|
prefersResolvedAddresses = false
|
||||||
resolvedAddresses = nil
|
resolvedAddresses = nil
|
||||||
endpointProtocols = [EndpointProtocol(.udp, 1194)]
|
endpointProtocols = [EndpointProtocol(.udp, 1194)]
|
||||||
cipher = .aes128cbc
|
cipher = .aes128cbc
|
||||||
digest = .sha1
|
digest = .sha1
|
||||||
ca = nil
|
self.ca = ca
|
||||||
clientCertificate = nil
|
clientCertificate = nil
|
||||||
clientKey = nil
|
clientKey = nil
|
||||||
mtu = 1500
|
mtu = 1500
|
||||||
|
@ -229,20 +231,19 @@ extension TunnelKitProvider {
|
||||||
throw ProviderError.configuration(field: "protocolConfiguration.providerConfiguration[\(S.digestAlgorithm)]")
|
throw ProviderError.configuration(field: "protocolConfiguration.providerConfiguration[\(S.digestAlgorithm)]")
|
||||||
}
|
}
|
||||||
|
|
||||||
let ca: CryptoContainer?
|
let ca: CryptoContainer
|
||||||
let clientCertificate: CryptoContainer?
|
let clientCertificate: CryptoContainer?
|
||||||
let clientKey: CryptoContainer?
|
let clientKey: CryptoContainer?
|
||||||
if let pem = providerConfiguration[S.ca] as? String {
|
guard let caPEM = providerConfiguration[S.ca] as? String else {
|
||||||
ca = CryptoContainer(pem: pem)
|
throw ProviderError.configuration(field: "protocolConfiguration.providerConfiguration[\(S.ca)]")
|
||||||
} else {
|
|
||||||
ca = nil
|
|
||||||
}
|
}
|
||||||
if let pem = providerConfiguration[S.clientCertificate] as? String {
|
ca = CryptoContainer(pem: caPEM)
|
||||||
|
if let clientPEM = providerConfiguration[S.clientCertificate] as? String {
|
||||||
guard let keyPEM = providerConfiguration[S.clientKey] as? String else {
|
guard let keyPEM = providerConfiguration[S.clientKey] as? String else {
|
||||||
throw ProviderError.configuration(field: "protocolConfiguration.providerConfiguration[\(S.clientKey)]")
|
throw ProviderError.configuration(field: "protocolConfiguration.providerConfiguration[\(S.clientKey)]")
|
||||||
}
|
}
|
||||||
|
|
||||||
clientCertificate = CryptoContainer(pem: pem)
|
clientCertificate = CryptoContainer(pem: clientPEM)
|
||||||
clientKey = CryptoContainer(pem: keyPEM)
|
clientKey = CryptoContainer(pem: keyPEM)
|
||||||
} else {
|
} else {
|
||||||
clientCertificate = nil
|
clientCertificate = nil
|
||||||
|
@ -365,7 +366,7 @@ extension TunnelKitProvider {
|
||||||
public let digest: SessionProxy.Digest
|
public let digest: SessionProxy.Digest
|
||||||
|
|
||||||
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.ca`
|
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.ca`
|
||||||
public let ca: CryptoContainer?
|
public let ca: CryptoContainer
|
||||||
|
|
||||||
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.clientCertificate`
|
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.clientCertificate`
|
||||||
public let clientCertificate: CryptoContainer?
|
public let clientCertificate: CryptoContainer?
|
||||||
|
@ -446,12 +447,10 @@ extension TunnelKitProvider {
|
||||||
S.endpointProtocols: endpointProtocols.map { $0.serialized() },
|
S.endpointProtocols: endpointProtocols.map { $0.serialized() },
|
||||||
S.cipherAlgorithm: cipher.rawValue,
|
S.cipherAlgorithm: cipher.rawValue,
|
||||||
S.digestAlgorithm: digest.rawValue,
|
S.digestAlgorithm: digest.rawValue,
|
||||||
|
S.ca: ca.pem,
|
||||||
S.mtu: mtu,
|
S.mtu: mtu,
|
||||||
S.debug: shouldDebug
|
S.debug: shouldDebug
|
||||||
]
|
]
|
||||||
if let ca = ca {
|
|
||||||
dict[S.ca] = ca.pem
|
|
||||||
}
|
|
||||||
if let clientCertificate = clientCertificate {
|
if let clientCertificate = clientCertificate {
|
||||||
dict[S.clientCertificate] = clientCertificate.pem
|
dict[S.clientCertificate] = clientCertificate.pem
|
||||||
}
|
}
|
||||||
|
@ -514,11 +513,6 @@ extension TunnelKitProvider {
|
||||||
log.info("\tProtocols: \(endpointProtocols)")
|
log.info("\tProtocols: \(endpointProtocols)")
|
||||||
log.info("\tCipher: \(cipher)")
|
log.info("\tCipher: \(cipher)")
|
||||||
log.info("\tDigest: \(digest)")
|
log.info("\tDigest: \(digest)")
|
||||||
if let _ = ca {
|
|
||||||
log.info("\tCA verification: enabled")
|
|
||||||
} else {
|
|
||||||
log.info("\tCA verification: disabled")
|
|
||||||
}
|
|
||||||
if let _ = clientCertificate {
|
if let _ = clientCertificate {
|
||||||
log.info("\tClient verification: enabled")
|
log.info("\tClient verification: enabled")
|
||||||
} else {
|
} else {
|
||||||
|
@ -551,11 +545,10 @@ extension TunnelKitProvider.Configuration: Equatable {
|
||||||
- Returns: An editable `TunnelKitProvider.ConfigurationBuilder` initialized with this configuration.
|
- Returns: An editable `TunnelKitProvider.ConfigurationBuilder` initialized with this configuration.
|
||||||
*/
|
*/
|
||||||
public func builder() -> TunnelKitProvider.ConfigurationBuilder {
|
public func builder() -> TunnelKitProvider.ConfigurationBuilder {
|
||||||
var builder = TunnelKitProvider.ConfigurationBuilder()
|
var builder = TunnelKitProvider.ConfigurationBuilder(ca: ca)
|
||||||
builder.endpointProtocols = endpointProtocols
|
builder.endpointProtocols = endpointProtocols
|
||||||
builder.cipher = cipher
|
builder.cipher = cipher
|
||||||
builder.digest = digest
|
builder.digest = digest
|
||||||
builder.ca = ca
|
|
||||||
builder.clientCertificate = clientCertificate
|
builder.clientCertificate = clientCertificate
|
||||||
builder.clientKey = clientKey
|
builder.clientKey = clientKey
|
||||||
builder.mtu = mtu
|
builder.mtu = mtu
|
||||||
|
|
|
@ -171,20 +171,16 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let caPath: String?
|
let caPath: String
|
||||||
let clientCertificatePath: String?
|
let clientCertificatePath: String?
|
||||||
let clientKeyPath: String?
|
let clientKeyPath: String?
|
||||||
if let ca = cfg.ca {
|
do {
|
||||||
do {
|
let url = temporaryURL(forKey: Configuration.Keys.ca)
|
||||||
let url = temporaryURL(forKey: Configuration.Keys.ca)
|
try cfg.ca.write(to: url)
|
||||||
try ca.write(to: url)
|
caPath = url.path
|
||||||
caPath = url.path
|
} catch {
|
||||||
} catch {
|
completionHandler(ProviderError.certificateSerialization)
|
||||||
completionHandler(ProviderError.certificateSerialization)
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
caPath = nil
|
|
||||||
}
|
}
|
||||||
if let clientCertificate = cfg.clientCertificate {
|
if let clientCertificate = cfg.clientCertificate {
|
||||||
do {
|
do {
|
||||||
|
@ -214,10 +210,13 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
|
||||||
cfg.print(appVersion: appVersion)
|
cfg.print(appVersion: appVersion)
|
||||||
|
|
||||||
// log.info("Temporary CA is stored to: \(caPath)")
|
// log.info("Temporary CA is stored to: \(caPath)")
|
||||||
var sessionConfiguration = SessionProxy.ConfigurationBuilder(username: endpoint.username, password: endpoint.password)
|
var sessionConfiguration = SessionProxy.ConfigurationBuilder(
|
||||||
|
username: endpoint.username,
|
||||||
|
password: endpoint.password,
|
||||||
|
caPath: caPath
|
||||||
|
)
|
||||||
sessionConfiguration.cipher = cfg.cipher
|
sessionConfiguration.cipher = cfg.cipher
|
||||||
sessionConfiguration.digest = cfg.digest
|
sessionConfiguration.digest = cfg.digest
|
||||||
sessionConfiguration.caPath = caPath
|
|
||||||
sessionConfiguration.clientCertificatePath = clientCertificatePath
|
sessionConfiguration.clientCertificatePath = clientCertificatePath
|
||||||
sessionConfiguration.clientKeyPath = clientKeyPath
|
sessionConfiguration.clientKeyPath = clientKeyPath
|
||||||
sessionConfiguration.compressionFraming = cfg.compressionFraming
|
sessionConfiguration.compressionFraming = cfg.compressionFraming
|
||||||
|
|
|
@ -124,8 +124,8 @@ extension SessionProxy {
|
||||||
/// The digest algorithm for HMAC.
|
/// The digest algorithm for HMAC.
|
||||||
public var digest: Digest
|
public var digest: Digest
|
||||||
|
|
||||||
/// The path to the optional CA for TLS negotiation (PEM format).
|
/// The path to the CA for TLS negotiation (PEM format).
|
||||||
public var caPath: String?
|
public let caPath: String
|
||||||
|
|
||||||
/// The path to the optional client certificate for TLS negotiation (PEM format).
|
/// The path to the optional client certificate for TLS negotiation (PEM format).
|
||||||
public var clientCertificatePath: String?
|
public var clientCertificatePath: String?
|
||||||
|
@ -143,12 +143,12 @@ extension SessionProxy {
|
||||||
public var renegotiatesAfter: TimeInterval?
|
public var renegotiatesAfter: TimeInterval?
|
||||||
|
|
||||||
/// :nodoc:
|
/// :nodoc:
|
||||||
public init(username: String, password: String) {
|
public init(username: String, password: String, caPath: String) {
|
||||||
self.username = username
|
self.username = username
|
||||||
self.password = password
|
self.password = password
|
||||||
cipher = .aes128cbc
|
cipher = .aes128cbc
|
||||||
digest = .sha1
|
digest = .sha1
|
||||||
caPath = nil
|
self.caPath = caPath
|
||||||
clientCertificatePath = nil
|
clientCertificatePath = nil
|
||||||
clientKeyPath = nil
|
clientKeyPath = nil
|
||||||
compressionFraming = .disabled
|
compressionFraming = .disabled
|
||||||
|
@ -193,7 +193,7 @@ extension SessionProxy {
|
||||||
public let digest: Digest
|
public let digest: Digest
|
||||||
|
|
||||||
/// - Seealso: `SessionProxy.ConfigurationBuilder.caPath`
|
/// - Seealso: `SessionProxy.ConfigurationBuilder.caPath`
|
||||||
public let caPath: String?
|
public let caPath: String
|
||||||
|
|
||||||
/// - Seealso: `SessionProxy.ConfigurationBuilder.clientCertificatePath`
|
/// - Seealso: `SessionProxy.ConfigurationBuilder.clientCertificatePath`
|
||||||
public let clientCertificatePath: String?
|
public let clientCertificatePath: String?
|
||||||
|
|
|
@ -37,6 +37,8 @@
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
extern const NSInteger TLSBoxMaxBufferLength;
|
extern const NSInteger TLSBoxMaxBufferLength;
|
||||||
|
|
||||||
extern NSString *const TLSBoxPeerVerificationErrorNotification;
|
extern NSString *const TLSBoxPeerVerificationErrorNotification;
|
||||||
|
@ -50,12 +52,12 @@ extern NSString *const TLSBoxPeerVerificationErrorNotification;
|
||||||
@interface TLSBox : NSObject
|
@interface TLSBox : NSObject
|
||||||
|
|
||||||
- (instancetype)initWithCAPath:(NSString *)caPath
|
- (instancetype)initWithCAPath:(NSString *)caPath
|
||||||
clientCertificatePath:(NSString *)clientCertificatePath
|
clientCertificatePath:(nullable NSString *)clientCertificatePath
|
||||||
clientKeyPath:(NSString *)clientKeyPath;
|
clientKeyPath:(nullable NSString *)clientKeyPath;
|
||||||
|
|
||||||
- (BOOL)startWithError:(NSError **)error;
|
- (BOOL)startWithError:(NSError **)error;
|
||||||
|
|
||||||
- (NSData *)pullCipherTextWithError:(NSError **)error;
|
- (nullable NSData *)pullCipherTextWithError:(NSError **)error;
|
||||||
// WARNING: text must be able to hold plain text output
|
// WARNING: text must be able to hold plain text output
|
||||||
- (BOOL)pullRawPlainText:(uint8_t *)text length:(NSInteger *)length error:(NSError **)error;
|
- (BOOL)pullRawPlainText:(uint8_t *)text length:(NSInteger *)length error:(NSError **)error;
|
||||||
|
|
||||||
|
@ -67,3 +69,5 @@ extern NSString *const TLSBoxPeerVerificationErrorNotification;
|
||||||
- (BOOL)isConnected;
|
- (BOOL)isConnected;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
|
|
|
@ -77,7 +77,8 @@ int TLSBoxVerifyPeer(int ok, X509_STORE_CTX *ctx) {
|
||||||
|
|
||||||
- (instancetype)init
|
- (instancetype)init
|
||||||
{
|
{
|
||||||
return [self initWithCAPath:nil clientCertificatePath:nil clientKeyPath:nil];
|
[NSException raise:NSInvalidArgumentException format:@"Use initWithCAPath:clientCertificatePath:clientKeyPath:"];
|
||||||
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithCAPath:(NSString *)caPath clientCertificatePath:(NSString *)clientCertificatePath clientKeyPath:(NSString *)clientKeyPath
|
- (instancetype)initWithCAPath:(NSString *)caPath clientCertificatePath:(NSString *)clientCertificatePath clientKeyPath:(NSString *)clientKeyPath
|
||||||
|
@ -115,18 +116,13 @@ 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);
|
||||||
if (self.caPath) {
|
SSL_CTX_set_verify(self.ctx, SSL_VERIFY_PEER, TLSBoxVerifyPeer);
|
||||||
SSL_CTX_set_verify(self.ctx, SSL_VERIFY_PEER, TLSBoxVerifyPeer);
|
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) {
|
*error = TunnelKitErrorWithCode(TunnelKitErrorCodeTLSBoxCA);
|
||||||
*error = TunnelKitErrorWithCode(TunnelKitErrorCodeTLSBoxCA);
|
|
||||||
}
|
|
||||||
return NO;
|
|
||||||
}
|
}
|
||||||
}
|
return NO;
|
||||||
else {
|
|
||||||
SSL_CTX_set_verify(self.ctx, SSL_VERIFY_NONE, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.clientCertificatePath) {
|
if (self.clientCertificatePath) {
|
||||||
|
|
|
@ -63,12 +63,11 @@ class AppExtensionTests: XCTestCase {
|
||||||
password: "bar"
|
password: "bar"
|
||||||
)
|
)
|
||||||
|
|
||||||
builder = TunnelKitProvider.ConfigurationBuilder()
|
builder = TunnelKitProvider.ConfigurationBuilder(ca: CryptoContainer(pem: "abcdef"))
|
||||||
XCTAssertNotNil(builder)
|
XCTAssertNotNil(builder)
|
||||||
|
|
||||||
builder.cipher = .aes128cbc
|
builder.cipher = .aes128cbc
|
||||||
builder.digest = .sha256
|
builder.digest = .sha256
|
||||||
builder.ca = CryptoContainer(pem: "abcdef")
|
|
||||||
cfg = builder.build()
|
cfg = builder.build()
|
||||||
|
|
||||||
let proto = try? cfg.generatedTunnelProtocol(withBundleIdentifier: identifier, appGroup: appGroup, endpoint: endpoint)
|
let proto = try? cfg.generatedTunnelProtocol(withBundleIdentifier: identifier, appGroup: appGroup, endpoint: endpoint)
|
||||||
|
@ -87,7 +86,7 @@ class AppExtensionTests: XCTestCase {
|
||||||
XCTAssertEqual(proto?.providerConfiguration?[K.appGroup] as? String, appGroup)
|
XCTAssertEqual(proto?.providerConfiguration?[K.appGroup] as? String, appGroup)
|
||||||
XCTAssertEqual(proto?.providerConfiguration?[K.cipherAlgorithm] as? String, cfg.cipher.rawValue)
|
XCTAssertEqual(proto?.providerConfiguration?[K.cipherAlgorithm] as? String, cfg.cipher.rawValue)
|
||||||
XCTAssertEqual(proto?.providerConfiguration?[K.digestAlgorithm] as? String, cfg.digest.rawValue)
|
XCTAssertEqual(proto?.providerConfiguration?[K.digestAlgorithm] as? String, cfg.digest.rawValue)
|
||||||
XCTAssertEqual(proto?.providerConfiguration?[K.ca] as? String, cfg.ca?.pem)
|
XCTAssertEqual(proto?.providerConfiguration?[K.ca] as? String, cfg.ca.pem)
|
||||||
XCTAssertEqual(proto?.providerConfiguration?[K.mtu] as? Int, cfg.mtu)
|
XCTAssertEqual(proto?.providerConfiguration?[K.mtu] as? Int, cfg.mtu)
|
||||||
XCTAssertEqual(proto?.providerConfiguration?[K.renegotiatesAfter] as? Int, cfg.renegotiatesAfterSeconds)
|
XCTAssertEqual(proto?.providerConfiguration?[K.renegotiatesAfter] as? Int, cfg.renegotiatesAfterSeconds)
|
||||||
XCTAssertEqual(proto?.providerConfiguration?[K.debug] as? Bool, cfg.shouldDebug)
|
XCTAssertEqual(proto?.providerConfiguration?[K.debug] as? Bool, cfg.shouldDebug)
|
||||||
|
|
Loading…
Reference in New Issue