Merge pull request #44 from keeshux/encapsulate-session-configuration
Encapsulate session configuration
This commit is contained in:
commit
2e31503877
|
@ -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"
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 */,
|
||||
|
|
|
@ -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)]")
|
||||
}
|
||||
|
@ -225,40 +204,27 @@ extension TunnelKitProvider {
|
|||
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"
|
||||
|
@ -339,6 +298,8 @@ extension TunnelKitProvider {
|
|||
|
||||
static let usesPIAPatches = "UsesPIAPatches"
|
||||
|
||||
// MARK: Debugging
|
||||
|
||||
static let debug = "Debug"
|
||||
|
||||
static let debugLogKey = "DebugLogKey"
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
|
@ -83,11 +83,13 @@ 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")
|
||||
}
|
||||
|
||||
private func temporaryURL(forKey key: String) -> URL {
|
||||
return cachesURL.appendingPathComponent("\(key).pem")
|
||||
guard let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup) else {
|
||||
fatalError("No access to app group: \(appGroup)")
|
||||
}
|
||||
return containerURL.appendingPathComponent("Library/Caches/")
|
||||
}
|
||||
|
||||
// MARK: Tunnel configuration
|
||||
|
@ -182,68 +184,16 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,10 +201,7 @@ extension SessionProxy {
|
|||
}
|
||||
|
||||
/// The immutable configuration for `SessionProxy`.
|
||||
public struct Configuration: Codable {
|
||||
|
||||
/// - Seealso: `SessionProxy.ConfigurationBuilder.credentials`
|
||||
public let credentials: Credentials?
|
||||
public struct Configuration: Codable, Equatable {
|
||||
|
||||
/// - Seealso: `SessionProxy.ConfigurationBuilder.cipher`
|
||||
public let cipher: Cipher
|
||||
|
@ -217,14 +209,14 @@ extension SessionProxy {
|
|||
/// - 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue