Merge pull request #44 from keeshux/encapsulate-session-configuration

Encapsulate session configuration
This commit is contained in:
Davide De Rosa 2018-10-25 22:35:31 +02:00 committed by GitHub
commit 2e31503877
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 211 additions and 269 deletions

View File

@ -88,15 +88,16 @@ extension ViewController {
let port = UInt16(textPort.text!)!
let credentials = SessionProxy.Credentials(textUsername.text!, textPassword.text!)
var builder = TunnelKitProvider.ConfigurationBuilder(ca: ca)
var sessionBuilder = SessionProxy.ConfigurationBuilder(ca: ca)
sessionBuilder.cipher = .aes256gcm
sessionBuilder.digest = .sha1
sessionBuilder.compressionFraming = .compLZO
sessionBuilder.renegotiatesAfter = nil
sessionBuilder.usesPIAPatches = true
var builder = TunnelKitProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build())
let socketType: TunnelKitProvider.SocketType = switchTCP.isOn ? .tcp : .udp
builder.endpointProtocols = [TunnelKitProvider.EndpointProtocol(socketType, port)]
builder.cipher = .aes256gcm
builder.digest = .sha1
builder.mtu = 1350
builder.compressionFraming = .compLZO
builder.renegotiatesAfterSeconds = nil
builder.usesPIAPatches = true
builder.shouldDebug = true
builder.debugLogKey = "Log"

View File

@ -88,15 +88,17 @@ extension ViewController {
let port = UInt16(textPort.stringValue)!
let credentials = SessionProxy.Credentials(textUsername.stringValue, textPassword.stringValue)
var builder = TunnelKitProvider.ConfigurationBuilder(ca: ca)
var sessionBuilder = SessionProxy.ConfigurationBuilder(ca: ca)
sessionBuilder.cipher = .aes128cbc
sessionBuilder.digest = .sha1
sessionBuilder.compressionFraming = .compLZO
sessionBuilder.renegotiatesAfter = nil
sessionBuilder.usesPIAPatches = true
var builder = TunnelKitProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build())
// let socketType: TunnelKitProvider.SocketType = isTCP ? .tcp : .udp
let socketType: TunnelKitProvider.SocketType = .udp
builder.endpointProtocols = [TunnelKitProvider.EndpointProtocol(socketType, port)]
builder.cipher = .aes128cbc
builder.digest = .sha1
builder.mtu = 1350
builder.compressionFraming = .compLZO
builder.renegotiatesAfterSeconds = nil
builder.shouldDebug = true
builder.debugLogKey = "Log"

View File

@ -1,13 +1,13 @@
PODS:
- OpenSSL-Apple (1.1.0i-v2)
- SwiftyBeaver (1.6.1)
- TunnelKit (1.2.2):
- TunnelKit/AppExtension (= 1.2.2)
- TunnelKit/Core (= 1.2.2)
- TunnelKit/AppExtension (1.2.2):
- TunnelKit (1.3.0):
- TunnelKit/AppExtension (= 1.3.0)
- TunnelKit/Core (= 1.3.0)
- TunnelKit/AppExtension (1.3.0):
- SwiftyBeaver
- TunnelKit/Core
- TunnelKit/Core (1.2.2):
- TunnelKit/Core (1.3.0):
- OpenSSL-Apple (~> 1.1.0h)
- SwiftyBeaver
@ -26,7 +26,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
OpenSSL-Apple: a93b8f2eec8783ff40d9a9304de180ab68bb647c
SwiftyBeaver: ccfcdf85a04d429f1633f668650b0ce8020bda3a
TunnelKit: 15c88f0cef7b926883566a9455e912a1e55f4048
TunnelKit: 8e747cac28959ebfdfa4eeab589c933f1856c0fb
PODFILE CHECKSUM: f66dfaaa92a8d04ab2743f3caeab0ac9f9f25859

View File

@ -53,9 +53,9 @@
0E3E0F222108A8CC00B371C1 /* SessionProxy+PushReply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E0F202108A8CC00B371C1 /* SessionProxy+PushReply.swift */; };
0E50D57521634E0A00FC87A8 /* ControlChannelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E50D57421634E0A00FC87A8 /* ControlChannelTests.swift */; };
0E58F1302138AC2F00A49F27 /* DNSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E58F12F2138AC2F00A49F27 /* DNSTests.swift */; };
0E749F622178911D00BB2701 /* pia-2048.pem in Resources */ = {isa = PBXBuildFile; fileRef = 0E749F612178911C00BB2701 /* pia-2048.pem */; };
0E749F5F2178885500BB2701 /* SessionProxy+PIA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E749F5E2178885500BB2701 /* SessionProxy+PIA.swift */; };
0E749F602178885500BB2701 /* SessionProxy+PIA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E749F5E2178885500BB2701 /* SessionProxy+PIA.swift */; };
0E749F622178911D00BB2701 /* pia-2048.pem in Resources */ = {isa = PBXBuildFile; fileRef = 0E749F612178911C00BB2701 /* pia-2048.pem */; };
0E85A25A202CC5AF0059E9F9 /* AppExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E85A259202CC5AE0059E9F9 /* AppExtensionTests.swift */; };
0E9379C91F819A4300CE91B6 /* TunnelKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E17D7F91F730D9F009EE129 /* TunnelKit.framework */; };
0EB2B45320F0BB44004233D7 /* EncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB2B45220F0BB44004233D7 /* EncryptionTests.swift */; };
@ -232,8 +232,8 @@
0E58F12F2138AC2F00A49F27 /* DNSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSTests.swift; sourceTree = "<group>"; };
0E6479DD212EAC96008E6888 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
0E6479E0212EACD6008E6888 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
0E749F612178911C00BB2701 /* pia-2048.pem */ = {isa = PBXFileReference; lastKnownFileType = text; path = "pia-2048.pem"; sourceTree = "<group>"; };
0E749F5E2178885500BB2701 /* SessionProxy+PIA.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionProxy+PIA.swift"; sourceTree = "<group>"; };
0E749F612178911C00BB2701 /* pia-2048.pem */ = {isa = PBXFileReference; lastKnownFileType = text; path = "pia-2048.pem"; sourceTree = "<group>"; };
0E85A259202CC5AE0059E9F9 /* AppExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppExtensionTests.swift; sourceTree = "<group>"; };
0E85A25B202CCA3D0059E9F9 /* TunnelKitHost.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = TunnelKitHost.entitlements; sourceTree = "<group>"; };
0EB2B45220F0BB44004233D7 /* EncryptionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionTests.swift; sourceTree = "<group>"; };
@ -471,6 +471,7 @@
0EFEB4322006D3C800F81029 /* CryptoBox.m */,
0E07596D20EF79B400F38FD8 /* CryptoCBC.h */,
0E07595C20EF6D1400F38FD8 /* CryptoCBC.m */,
0ECE3527212EB7770040F253 /* CryptoContainer.swift */,
0E3B15C52152B05E00984B17 /* CryptoCTR.h */,
0E3B15C62152B05E00984B17 /* CryptoCTR.m */,
0E07596120EF733F00F38FD8 /* CryptoMacros.h */,
@ -518,7 +519,6 @@
children = (
0EBBF2E32084FDF400E36B40 /* Transport */,
0EC1BBA720D7D803007C4C7B /* ConnectionStrategy.swift */,
0ECE3527212EB7770040F253 /* CryptoContainer.swift */,
0EC1BBA420D71190007C4C7B /* DNSResolver.swift */,
0EBBF2E42084FE6F00E36B40 /* GenericSocket.swift */,
0EFEB4AA200760EC00F81029 /* InterfaceObserver.swift */,

View File

@ -121,38 +121,11 @@ extension TunnelKitProvider {
/// The accepted communication protocols. Must be non-empty.
public var endpointProtocols: [EndpointProtocol]
/// The encryption algorithm.
public var cipher: SessionProxy.Cipher
/// The message digest algorithm.
public var digest: SessionProxy.Digest
/// The CA certificate to validate server against.
public let ca: CryptoContainer
/// The optional client certificate to authenticate with. Set to `nil` to disable client authentication (default).
public var clientCertificate: CryptoContainer?
/// The optional key for `clientCertificate`. Set to `nil` if client authentication unused (default).
public var clientKey: CryptoContainer?
/// The MTU of the link.
public var mtu: Int
/// Sets compression framing, disabled by default.
public var compressionFraming: SessionProxy.CompressionFraming
/// The optional TLS wrapping. When `strategy == .auth`, uses `digest` as HMAC algorithm.
public var tlsWrap: SessionProxy.TLSWrap?
/// Sends periodical keep-alive packets (ping) if set. Useful with stateful firewalls.
public var keepAliveSeconds: Int?
/// The number of seconds after which a renegotiation is started. Set to `nil` to disable renegotiation (default).
public var renegotiatesAfterSeconds: Int?
/// Server is patched for the PIA VPN provider.
public var usesPIAPatches: Bool?
/// The session configuration.
public var sessionConfiguration: SessionProxy.Configuration
// MARK: Debugging
@ -175,21 +148,12 @@ extension TunnelKitProvider {
- Parameter ca: The CA certificate.
*/
public init(ca: CryptoContainer) {
public init(sessionConfiguration: SessionProxy.Configuration) {
prefersResolvedAddresses = false
resolvedAddresses = nil
endpointProtocols = [EndpointProtocol(.udp, 1194)]
cipher = .aes128cbc
digest = .sha1
self.ca = ca
clientCertificate = nil
clientKey = nil
mtu = 1500
compressionFraming = .disabled
tlsWrap = nil
keepAliveSeconds = nil
renegotiatesAfterSeconds = nil
usesPIAPatches = false
self.sessionConfiguration = sessionConfiguration
shouldDebug = false
debugLogKey = nil
debugLogFormat = nil
@ -199,6 +163,21 @@ extension TunnelKitProvider {
fileprivate init(providerConfiguration: [String: Any]) throws {
let S = Configuration.Keys.self
prefersResolvedAddresses = providerConfiguration[S.prefersResolvedAddresses] as? Bool ?? false
resolvedAddresses = providerConfiguration[S.resolvedAddresses] as? [String]
guard let endpointProtocolsStrings = providerConfiguration[S.endpointProtocols] as? [String], !endpointProtocolsStrings.isEmpty else {
throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[\(S.endpointProtocols)] is nil or empty")
}
endpointProtocols = try endpointProtocolsStrings.map {
guard let ep = EndpointProtocol(rawValue: $0) else {
throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[\(S.endpointProtocols)] has a badly formed element")
}
return ep
}
mtu = providerConfiguration[S.mtu] as? Int ?? 1250
//
guard let cipherAlgorithm = providerConfiguration[S.cipherAlgorithm] as? String, let cipher = SessionProxy.Cipher(rawValue: cipherAlgorithm) else {
throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[\(S.cipherAlgorithm)]")
}
@ -224,41 +203,28 @@ extension TunnelKitProvider {
clientCertificate = nil
clientKey = nil
}
prefersResolvedAddresses = providerConfiguration[S.prefersResolvedAddresses] as? Bool ?? false
resolvedAddresses = providerConfiguration[S.resolvedAddresses] as? [String]
guard let endpointProtocolsStrings = providerConfiguration[S.endpointProtocols] as? [String], !endpointProtocolsStrings.isEmpty else {
throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[\(S.endpointProtocols)] is nil or empty")
}
endpointProtocols = try endpointProtocolsStrings.map {
guard let ep = EndpointProtocol(rawValue: $0) else {
throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[\(S.endpointProtocols)] has a badly formed element")
}
return ep
}
self.cipher = cipher
self.digest = digest
self.ca = ca
self.clientCertificate = clientCertificate
self.clientKey = clientKey
mtu = providerConfiguration[S.mtu] as? Int ?? 1250
var sessionConfigurationBuilder = SessionProxy.ConfigurationBuilder(ca: ca)
sessionConfigurationBuilder.cipher = cipher
sessionConfigurationBuilder.digest = digest
sessionConfigurationBuilder.clientCertificate = clientCertificate
sessionConfigurationBuilder.clientKey = clientKey
if let compressionFramingValue = providerConfiguration[S.compressionFraming] as? Int, let compressionFraming = SessionProxy.CompressionFraming(rawValue: compressionFramingValue) {
self.compressionFraming = compressionFraming
sessionConfigurationBuilder.compressionFraming = compressionFraming
} else {
compressionFraming = .disabled
sessionConfigurationBuilder.compressionFraming = .disabled
}
if let tlsWrapData = providerConfiguration[S.tlsWrap] as? Data {
do {
tlsWrap = try SessionProxy.TLSWrap.deserialized(tlsWrapData)
sessionConfigurationBuilder.tlsWrap = try SessionProxy.TLSWrap.deserialized(tlsWrapData)
} catch {
throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[\(S.tlsWrap)]")
}
}
keepAliveSeconds = providerConfiguration[S.keepAlive] as? Int
renegotiatesAfterSeconds = providerConfiguration[S.renegotiatesAfter] as? Int
usesPIAPatches = providerConfiguration[S.usesPIAPatches] as? Bool ?? false
sessionConfigurationBuilder.keepAliveInterval = providerConfiguration[S.keepAlive] as? TimeInterval
sessionConfigurationBuilder.renegotiatesAfter = providerConfiguration[S.renegotiatesAfter] as? TimeInterval
sessionConfigurationBuilder.usesPIAPatches = providerConfiguration[S.usesPIAPatches] as? Bool ?? false
sessionConfiguration = sessionConfigurationBuilder.build()
shouldDebug = providerConfiguration[S.debug] as? Bool ?? false
if shouldDebug {
@ -287,17 +253,8 @@ extension TunnelKitProvider {
prefersResolvedAddresses: prefersResolvedAddresses,
resolvedAddresses: resolvedAddresses,
endpointProtocols: endpointProtocols,
cipher: cipher,
digest: digest,
ca: ca,
clientCertificate: clientCertificate,
clientKey: clientKey,
mtu: mtu,
compressionFraming: compressionFraming,
tlsWrap: tlsWrap,
keepAliveSeconds: keepAliveSeconds,
renegotiatesAfterSeconds: renegotiatesAfterSeconds,
usesPIAPatches: usesPIAPatches,
sessionConfiguration: sessionConfiguration,
shouldDebug: shouldDebug,
debugLogKey: shouldDebug ? debugLogKey : nil,
debugLogFormat: shouldDebug ? debugLogFormat : nil,
@ -317,6 +274,10 @@ extension TunnelKitProvider {
static let endpointProtocols = "EndpointProtocols"
static let mtu = "MTU"
// MARK: SessionConfiguration
static let cipherAlgorithm = "CipherAlgorithm"
static let digestAlgorithm = "DigestAlgorithm"
@ -327,8 +288,6 @@ extension TunnelKitProvider {
static let clientKey = "ClientKey"
static let mtu = "MTU"
static let compressionFraming = "CompressionFraming"
static let tlsWrap = "TLSWrap"
@ -338,6 +297,8 @@ extension TunnelKitProvider {
static let renegotiatesAfter = "RenegotiatesAfter"
static let usesPIAPatches = "UsesPIAPatches"
// MARK: Debugging
static let debug = "Debug"
@ -357,38 +318,11 @@ extension TunnelKitProvider {
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.endpointProtocols`
public let endpointProtocols: [EndpointProtocol]
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.cipher`
public let cipher: SessionProxy.Cipher
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.digest`
public let digest: SessionProxy.Digest
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.ca`
public let ca: CryptoContainer
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.clientCertificate`
public let clientCertificate: CryptoContainer?
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.clientKey`
public let clientKey: CryptoContainer?
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.mtu`
public let mtu: Int
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.compressionFraming`
public let compressionFraming: SessionProxy.CompressionFraming
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.tlsWrap`
public let tlsWrap: SessionProxy.TLSWrap?
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.keepAliveSeconds`
public let keepAliveSeconds: Int?
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.renegotiatesAfterSeconds`
public let renegotiatesAfterSeconds: Int?
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.usesPIAPatches`
public let usesPIAPatches: Bool?
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.sessionConfiguration`
public let sessionConfiguration: SessionProxy.Configuration
/// - Seealso: `TunnelKitProvider.ConfigurationBuilder.shouldDebug`
public let shouldDebug: Bool
@ -474,32 +408,32 @@ extension TunnelKitProvider {
S.appGroup: appGroup,
S.prefersResolvedAddresses: prefersResolvedAddresses,
S.endpointProtocols: endpointProtocols.map { $0.rawValue },
S.cipherAlgorithm: cipher.rawValue,
S.digestAlgorithm: digest.rawValue,
S.ca: ca.pem,
S.cipherAlgorithm: sessionConfiguration.cipher.rawValue,
S.digestAlgorithm: sessionConfiguration.digest.rawValue,
S.ca: sessionConfiguration.ca.pem,
S.mtu: mtu,
S.debug: shouldDebug
]
if let clientCertificate = clientCertificate {
if let clientCertificate = sessionConfiguration.clientCertificate {
dict[S.clientCertificate] = clientCertificate.pem
}
if let clientKey = clientKey {
if let clientKey = sessionConfiguration.clientKey {
dict[S.clientKey] = clientKey.pem
}
if let resolvedAddresses = resolvedAddresses {
dict[S.resolvedAddresses] = resolvedAddresses
}
dict[S.compressionFraming] = compressionFraming.rawValue
if let tlsWrapData = tlsWrap?.serialized() {
dict[S.compressionFraming] = sessionConfiguration.compressionFraming.rawValue
if let tlsWrapData = sessionConfiguration.tlsWrap?.serialized() {
dict[S.tlsWrap] = tlsWrapData
}
if let keepAliveSeconds = keepAliveSeconds {
if let keepAliveSeconds = sessionConfiguration.keepAliveInterval {
dict[S.keepAlive] = keepAliveSeconds
}
if let renegotiatesAfterSeconds = renegotiatesAfterSeconds {
if let renegotiatesAfterSeconds = sessionConfiguration.renegotiatesAfter {
dict[S.renegotiatesAfter] = renegotiatesAfterSeconds
}
if let usesPIAPatches = usesPIAPatches {
if let usesPIAPatches = sessionConfiguration.usesPIAPatches {
dict[S.usesPIAPatches] = usesPIAPatches
}
if let debugLogKey = debugLogKey {
@ -550,26 +484,26 @@ extension TunnelKitProvider {
}
log.info("\tProtocols: \(endpointProtocols)")
log.info("\tCipher: \(cipher)")
log.info("\tDigest: \(digest)")
if let _ = clientCertificate {
log.info("\tCipher: \(sessionConfiguration.cipher)")
log.info("\tDigest: \(sessionConfiguration.digest)")
if let _ = sessionConfiguration.clientCertificate {
log.info("\tClient verification: enabled")
} else {
log.info("\tClient verification: disabled")
}
log.info("\tMTU: \(mtu)")
log.info("\tCompression framing: \(compressionFraming)")
if let keepAliveSeconds = keepAliveSeconds, keepAliveSeconds > 0 {
log.info("\tCompression framing: \(sessionConfiguration.compressionFraming)")
if let keepAliveSeconds = sessionConfiguration.keepAliveInterval, keepAliveSeconds > 0 {
log.info("\tKeep-alive: \(keepAliveSeconds) seconds")
} else {
log.info("\tKeep-alive: never")
}
if let renegotiatesAfterSeconds = renegotiatesAfterSeconds, renegotiatesAfterSeconds > 0 {
if let renegotiatesAfterSeconds = sessionConfiguration.renegotiatesAfter, renegotiatesAfterSeconds > 0 {
log.info("\tRenegotiation: \(renegotiatesAfterSeconds) seconds")
} else {
log.info("\tRenegotiation: never")
}
if let tlsWrap = tlsWrap {
if let tlsWrap = sessionConfiguration.tlsWrap {
log.info("\tTLS wrapping: \(tlsWrap.strategy)")
} else {
log.info("\tTLS wrapping: disabled")
@ -589,18 +523,9 @@ extension TunnelKitProvider.Configuration: Equatable {
- Returns: An editable `TunnelKitProvider.ConfigurationBuilder` initialized with this configuration.
*/
public func builder() -> TunnelKitProvider.ConfigurationBuilder {
var builder = TunnelKitProvider.ConfigurationBuilder(ca: ca)
var builder = TunnelKitProvider.ConfigurationBuilder(sessionConfiguration: sessionConfiguration)
builder.endpointProtocols = endpointProtocols
builder.cipher = cipher
builder.digest = digest
builder.clientCertificate = clientCertificate
builder.clientKey = clientKey
builder.mtu = mtu
builder.compressionFraming = compressionFraming
builder.tlsWrap = tlsWrap
builder.keepAliveSeconds = keepAliveSeconds
builder.renegotiatesAfterSeconds = renegotiatesAfterSeconds
builder.usesPIAPatches = usesPIAPatches
builder.shouldDebug = shouldDebug
builder.debugLogKey = debugLogKey
builder.debugLogFormat = debugLogFormat
@ -612,15 +537,8 @@ extension TunnelKitProvider.Configuration: Equatable {
public static func ==(lhs: TunnelKitProvider.Configuration, rhs: TunnelKitProvider.Configuration) -> Bool {
return (
(lhs.endpointProtocols == rhs.endpointProtocols) &&
(lhs.cipher == rhs.cipher) &&
(lhs.digest == rhs.digest) &&
(lhs.ca == rhs.ca) &&
(lhs.clientCertificate == rhs.clientCertificate) &&
(lhs.clientKey == rhs.clientKey) &&
(lhs.mtu == rhs.mtu) &&
(lhs.compressionFraming == rhs.compressionFraming) &&
(lhs.keepAliveSeconds == rhs.keepAliveSeconds) &&
(lhs.renegotiatesAfterSeconds == rhs.renegotiatesAfterSeconds)
(lhs.sessionConfiguration == rhs.sessionConfiguration)
// XXX: tlsWrap not copied
)
}

View File

@ -83,13 +83,15 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
private let prngSeedLength = 64
private var cachesURL: URL {
return URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)[0])
guard let appGroup = appGroup else {
fatalError("Accessing cachesURL before parsing app group")
}
guard let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup) else {
fatalError("No access to app group: \(appGroup)")
}
return containerURL.appendingPathComponent("Library/Caches/")
}
private func temporaryURL(forKey key: String) -> URL {
return cachesURL.appendingPathComponent("\(key).pem")
}
// MARK: Tunnel configuration
private var appGroup: String!
@ -181,69 +183,17 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
completionHandler(ProviderConfigurationError.prngInitialization)
return
}
let caPath: String
let clientCertificatePath: String?
let clientKeyPath: String?
do {
let url = temporaryURL(forKey: Configuration.Keys.ca)
try cfg.ca.write(to: url)
caPath = url.path
} catch {
completionHandler(ProviderConfigurationError.certificateSerialization)
return
}
if let clientCertificate = cfg.clientCertificate {
do {
let url = temporaryURL(forKey: Configuration.Keys.clientCertificate)
try clientCertificate.write(to: url)
clientCertificatePath = url.path
} catch {
completionHandler(ProviderConfigurationError.certificateSerialization)
return
}
} else {
clientCertificatePath = nil
}
if let clientKey = cfg.clientKey {
do {
let url = temporaryURL(forKey: Configuration.Keys.clientKey)
try clientKey.write(to: url)
clientKeyPath = url.path
} catch {
completionHandler(ProviderConfigurationError.certificateSerialization)
return
}
} else {
clientKeyPath = nil
}
cfg.print(appVersion: appVersion)
// log.info("Temporary CA is stored to: \(caPath)")
var sessionConfiguration = SessionProxy.ConfigurationBuilder(caPath: caPath)
sessionConfiguration.credentials = credentials
sessionConfiguration.cipher = cfg.cipher
sessionConfiguration.digest = cfg.digest
sessionConfiguration.clientCertificatePath = clientCertificatePath
sessionConfiguration.clientKeyPath = clientKeyPath
sessionConfiguration.compressionFraming = cfg.compressionFraming
sessionConfiguration.tlsWrap = cfg.tlsWrap
if let keepAliveSeconds = cfg.keepAliveSeconds {
sessionConfiguration.keepAliveInterval = TimeInterval(keepAliveSeconds)
}
if let renegotiatesAfterSeconds = cfg.renegotiatesAfterSeconds {
sessionConfiguration.renegotiatesAfter = TimeInterval(renegotiatesAfterSeconds)
}
sessionConfiguration.usesPIAPatches = cfg.usesPIAPatches ?? false
let proxy: SessionProxy
do {
proxy = try SessionProxy(queue: tunnelQueue, configuration: sessionConfiguration.build())
proxy = try SessionProxy(queue: tunnelQueue, configuration: cfg.sessionConfiguration, cachesURL: cachesURL)
} catch let e {
completionHandler(e)
return
}
proxy.credentials = credentials
proxy.delegate = self
self.proxy = proxy
@ -382,10 +332,6 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
}
// stopped externally, unrecoverable
else {
let fm = FileManager.default
for key in [Configuration.Keys.ca, Configuration.Keys.clientCertificate, Configuration.Keys.clientKey] {
try? fm.removeItem(at: temporaryURL(forKey: key))
}
cancelTunnelWithError(error)
}
}

View File

@ -135,23 +135,20 @@ extension SessionProxy {
/// The way to create a `SessionProxy.Configuration` object for a `SessionProxy`.
public struct ConfigurationBuilder {
/// The credentials.
public var credentials: Credentials?
/// The cipher algorithm for data encryption.
public var cipher: Cipher
/// The digest algorithm for HMAC.
public var digest: Digest
/// The path to the CA for TLS negotiation (PEM format).
public let caPath: String
/// The CA for TLS negotiation (PEM format).
public let ca: CryptoContainer
/// The path to the optional client certificate for TLS negotiation (PEM format).
public var clientCertificatePath: String?
/// The optional client certificate for TLS negotiation (PEM format).
public var clientCertificate: CryptoContainer?
/// The path to the private key for the certificate at `clientCertificatePath` (PEM format).
public var clientKeyPath: String?
/// The private key for the certificate in `clientCertificate` (PEM format).
public var clientKey: CryptoContainer?
/// Sets compression framing, disabled by default.
public var compressionFraming: CompressionFraming
@ -166,16 +163,15 @@ extension SessionProxy {
public var renegotiatesAfter: TimeInterval?
/// Server is patched for the PIA VPN provider.
public var usesPIAPatches: Bool
public var usesPIAPatches: Bool?
/// :nodoc:
public init(caPath: String) {
credentials = nil
public init(ca: CryptoContainer) {
cipher = .aes128cbc
digest = .sha1
self.caPath = caPath
clientCertificatePath = nil
clientKeyPath = nil
self.ca = ca
clientCertificate = nil
clientKey = nil
compressionFraming = .disabled
tlsWrap = nil
keepAliveInterval = nil
@ -190,12 +186,11 @@ extension SessionProxy {
*/
public func build() -> Configuration {
return Configuration(
credentials: credentials,
cipher: cipher,
digest: digest,
caPath: caPath,
clientCertificatePath: clientCertificatePath,
clientKeyPath: clientKeyPath,
ca: ca,
clientCertificate: clientCertificate,
clientKey: clientKey,
compressionFraming: compressionFraming,
tlsWrap: tlsWrap,
keepAliveInterval: keepAliveInterval,
@ -206,25 +201,22 @@ extension SessionProxy {
}
/// The immutable configuration for `SessionProxy`.
public struct Configuration: Codable {
public struct Configuration: Codable, Equatable {
/// - Seealso: `SessionProxy.ConfigurationBuilder.credentials`
public let credentials: Credentials?
/// - Seealso: `SessionProxy.ConfigurationBuilder.cipher`
public let cipher: Cipher
/// - Seealso: `SessionProxy.ConfigurationBuilder.digest`
public let digest: Digest
/// - Seealso: `SessionProxy.ConfigurationBuilder.caPath`
public let caPath: String
/// - Seealso: `SessionProxy.ConfigurationBuilder.ca`
public let ca: CryptoContainer
/// - Seealso: `SessionProxy.ConfigurationBuilder.clientCertificatePath`
public let clientCertificatePath: String?
/// - Seealso: `SessionProxy.ConfigurationBuilder.clientCertificate`
public let clientCertificate: CryptoContainer?
/// - Seealso: `SessionProxy.ConfigurationBuilder.clientKeyPath`
public let clientKeyPath: String?
/// - Seealso: `SessionProxy.ConfigurationBuilder.clientKey`
public let clientKey: CryptoContainer?
/// - Seealso: `SessionProxy.ConfigurationBuilder.compressionFraming`
public let compressionFraming: CompressionFraming
@ -239,6 +231,41 @@ extension SessionProxy {
public let renegotiatesAfter: TimeInterval?
/// - Seealso: `SessionProxy.ConfigurationBuilder.usesPIAPatches`
public let usesPIAPatches: Bool
public let usesPIAPatches: Bool?
/**
Returns a `SessionProxy.ConfigurationBuilder` to use this configuration as a starting point for a new one.
- Returns: An editable `SessionProxy.ConfigurationBuilder` initialized with this configuration.
*/
public func builder() -> SessionProxy.ConfigurationBuilder {
var builder = SessionProxy.ConfigurationBuilder(ca: ca)
builder.cipher = cipher
builder.digest = digest
builder.clientCertificate = clientCertificate
builder.clientKey = clientKey
builder.compressionFraming = compressionFraming
builder.tlsWrap = tlsWrap
builder.keepAliveInterval = keepAliveInterval
builder.renegotiatesAfter = renegotiatesAfter
builder.usesPIAPatches = usesPIAPatches
return builder
}
// MARK: Equatable
/// :nodoc:
public static func ==(lhs: Configuration, rhs: Configuration) -> Bool {
return
(lhs.cipher == rhs.cipher) &&
(lhs.digest == rhs.digest) &&
(lhs.ca == rhs.ca) &&
(lhs.clientCertificate == rhs.clientCertificate) &&
(lhs.clientKey == rhs.clientKey) &&
(lhs.compressionFraming == rhs.compressionFraming) &&
(lhs.keepAliveInterval == rhs.keepAliveInterval) &&
(lhs.renegotiatesAfter == rhs.renegotiatesAfter) &&
(lhs.usesPIAPatches == rhs.usesPIAPatches)
}
}
}

View File

@ -69,10 +69,21 @@ public class SessionProxy {
case reconnect
}
private struct Caches {
static let ca = "ca.pem"
static let clientCertificate = "cert.pem"
static let clientKey = "key.pem"
}
// MARK: Configuration
private let configuration: Configuration
/// The optional credentials.
public var credentials: Credentials?
private var keepAliveInterval: TimeInterval? {
let interval: TimeInterval?
if let negInterval = pushReply?.ping, negInterval > 0 {
@ -143,6 +154,22 @@ public class SessionProxy {
private var authenticator: Authenticator?
// MARK: Caching
private let cachesURL: URL
private var caURL: URL {
return cachesURL.appendingPathComponent(Caches.ca)
}
private var clientCertificateURL: URL {
return cachesURL.appendingPathComponent(Caches.clientCertificate)
}
private var clientKeyURL: URL {
return cachesURL.appendingPathComponent(Caches.clientKey)
}
// MARK: Init
/**
@ -151,9 +178,10 @@ public class SessionProxy {
- Parameter queue: The `DispatchQueue` where to run the session loop.
- Parameter configuration: The `SessionProxy.Configuration` to use for this session.
*/
public init(queue: DispatchQueue, configuration: Configuration) throws {
public init(queue: DispatchQueue, configuration: Configuration, cachesURL: URL) throws {
self.queue = queue
self.configuration = configuration
self.cachesURL = cachesURL
keys = [:]
oldKeys = []
@ -172,10 +200,29 @@ public class SessionProxy {
} else {
controlChannel = ControlChannel()
}
// cache PEMs locally (mandatory for OpenSSL)
let fm = FileManager.default
try configuration.ca.pem.write(to: caURL, atomically: true, encoding: .ascii)
if let container = configuration.clientCertificate {
try container.pem.write(to: clientCertificateURL, atomically: true, encoding: .ascii)
} else {
try? fm.removeItem(at: clientCertificateURL)
}
if let container = configuration.clientKey {
try container.pem.write(to: clientKeyURL, atomically: true, encoding: .ascii)
} else {
try? fm.removeItem(at: clientKeyURL)
}
}
deinit {
cleanup()
let fm = FileManager.default
for url in [caURL, clientCertificateURL, clientKeyURL] {
try? fm.removeItem(at: url)
}
}
// MARK: Public interface
@ -565,8 +612,8 @@ public class SessionProxy {
}
private func hardResetPayload() -> Data? {
guard !configuration.usesPIAPatches else {
let caMD5 = TLSBox.md5(forCertificatePath: configuration.caPath)
guard !(configuration.usesPIAPatches ?? false) else {
let caMD5 = TLSBox.md5(forCertificatePath: caURL.path)
log.debug("CA MD5 is: \(caMD5)")
return try? PIAHardReset(
caMd5Digest: caMD5,
@ -606,7 +653,7 @@ public class SessionProxy {
negotiationKey.controlState = .preAuth
do {
authenticator = try Authenticator(configuration.credentials?.username, pushReply?.authToken ?? configuration.credentials?.password)
authenticator = try Authenticator(credentials?.username, pushReply?.authToken ?? credentials?.password)
try authenticator?.putAuth(into: negotiationKey.tls)
} catch let e {
deferStop(.shutdown, e)
@ -722,9 +769,9 @@ public class SessionProxy {
log.debug("Start TLS handshake")
negotiationKey.tlsOptional = TLSBox(
caPath: configuration.caPath,
clientCertificatePath: configuration.clientCertificatePath,
clientKeyPath: configuration.clientKeyPath
caPath: caURL.path,
clientCertificatePath: (configuration.clientCertificate != nil) ? clientCertificateURL.path : nil,
clientKeyPath: (configuration.clientKey != nil) ? clientKeyURL.path : nil
)
do {
try negotiationKey.tls.start()

View File

@ -60,11 +60,12 @@ class AppExtensionTests: XCTestCase {
let hostname = "example.com"
let credentials = SessionProxy.Credentials("foo", "bar")
builder = TunnelKitProvider.ConfigurationBuilder(ca: CryptoContainer(pem: "abcdef"))
var sessionBuilder = SessionProxy.ConfigurationBuilder(ca: CryptoContainer(pem: "abcdef"))
sessionBuilder.cipher = .aes128cbc
sessionBuilder.digest = .sha256
builder = TunnelKitProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build())
XCTAssertNotNil(builder)
builder.cipher = .aes128cbc
builder.digest = .sha256
cfg = builder.build()
let proto = try? cfg.generatedTunnelProtocol(withBundleIdentifier: identifier, appGroup: appGroup, hostname: hostname, credentials: credentials)
@ -81,11 +82,11 @@ class AppExtensionTests: XCTestCase {
let K = TunnelKitProvider.Configuration.Keys.self
XCTAssertEqual(proto?.providerConfiguration?[K.appGroup] as? String, appGroup)
XCTAssertEqual(proto?.providerConfiguration?[K.cipherAlgorithm] as? String, cfg.cipher.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.cipherAlgorithm] as? String, cfg.sessionConfiguration.cipher.rawValue)
XCTAssertEqual(proto?.providerConfiguration?[K.digestAlgorithm] as? String, cfg.sessionConfiguration.digest.rawValue)
XCTAssertEqual(proto?.providerConfiguration?[K.ca] as? String, cfg.sessionConfiguration.ca.pem)
XCTAssertEqual(proto?.providerConfiguration?[K.mtu] as? Int, cfg.mtu)
XCTAssertEqual(proto?.providerConfiguration?[K.renegotiatesAfter] as? Int, cfg.renegotiatesAfterSeconds)
XCTAssertEqual(proto?.providerConfiguration?[K.renegotiatesAfter] as? TimeInterval, cfg.sessionConfiguration.renegotiatesAfter)
XCTAssertEqual(proto?.providerConfiguration?[K.debug] as? Bool, cfg.shouldDebug)
XCTAssertEqual(proto?.providerConfiguration?[K.debugLogKey] as? String, cfg.debugLogKey)
}