From 8a9e99e6a99f98f64b6e2a30ba24e186c5e05397 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Thu, 23 Aug 2018 13:43:04 +0200 Subject: [PATCH 1/5] Wrap SessionProxy configuration in a builder --- TunnelKit.xcodeproj/project.pbxproj | 12 ++ .../AppExtension/TunnelKitProvider.swift | 18 +-- TunnelKit/Sources/Core/SessionError.swift | 46 +++++++ .../Core/SessionProxy+Configuration.swift | 90 ++++++++++++++ TunnelKit/Sources/Core/SessionProxy.swift | 112 ++---------------- 5 files changed, 169 insertions(+), 109 deletions(-) create mode 100644 TunnelKit/Sources/Core/SessionError.swift create mode 100644 TunnelKit/Sources/Core/SessionProxy+Configuration.swift diff --git a/TunnelKit.xcodeproj/project.pbxproj b/TunnelKit.xcodeproj/project.pbxproj index dcc6dbd..ede5158 100644 --- a/TunnelKit.xcodeproj/project.pbxproj +++ b/TunnelKit.xcodeproj/project.pbxproj @@ -19,6 +19,10 @@ 0E07597F20F0060E00F38FD8 /* CryptoAEAD.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E07597C20F0060E00F38FD8 /* CryptoAEAD.h */; }; 0E07598020F0060E00F38FD8 /* CryptoAEAD.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E07597D20F0060E00F38FD8 /* CryptoAEAD.m */; }; 0E07598120F0060E00F38FD8 /* CryptoAEAD.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E07597D20F0060E00F38FD8 /* CryptoAEAD.m */; }; + 0E0C2125212ED29D008AB282 /* SessionError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0C2123212ED29D008AB282 /* SessionError.swift */; }; + 0E0C2126212ED29D008AB282 /* SessionError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0C2123212ED29D008AB282 /* SessionError.swift */; }; + 0E0C2127212ED29D008AB282 /* SessionProxy+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0C2124212ED29D008AB282 /* SessionProxy+Configuration.swift */; }; + 0E0C2128212ED29D008AB282 /* SessionProxy+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0C2124212ED29D008AB282 /* SessionProxy+Configuration.swift */; }; 0E11089F1F77B9E800A92462 /* TunnelKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E17D7F91F730D9F009EE129 /* TunnelKit.framework */; }; 0E1108AC1F77B9F900A92462 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1108AB1F77B9F900A92462 /* AppDelegate.swift */; }; 0E1108AE1F77B9F900A92462 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1108AD1F77B9F900A92462 /* ViewController.swift */; }; @@ -180,6 +184,8 @@ 0E07596D20EF79B400F38FD8 /* CryptoCBC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptoCBC.h; sourceTree = ""; }; 0E07597C20F0060E00F38FD8 /* CryptoAEAD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptoAEAD.h; sourceTree = ""; }; 0E07597D20F0060E00F38FD8 /* CryptoAEAD.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CryptoAEAD.m; sourceTree = ""; }; + 0E0C2123212ED29D008AB282 /* SessionError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionError.swift; sourceTree = ""; }; + 0E0C2124212ED29D008AB282 /* SessionProxy+Configuration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SessionProxy+Configuration.swift"; sourceTree = ""; }; 0E11089A1F77B9E800A92462 /* TunnelKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TunnelKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 0E11089E1F77B9E800A92462 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 0E1108A91F77B9F900A92462 /* TunnelKitHost.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TunnelKitHost.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -446,8 +452,10 @@ 0EFEB4392006D3C800F81029 /* ReplayProtector.h */, 0EFEB4482006D3C800F81029 /* ReplayProtector.m */, 0EFEB4372006D3C800F81029 /* SecureRandom.swift */, + 0E0C2123212ED29D008AB282 /* SessionError.swift */, 0EFEB42B2006D3C800F81029 /* SessionKey.swift */, 0EFEB43C2006D3C800F81029 /* SessionProxy.swift */, + 0E0C2124212ED29D008AB282 /* SessionProxy+Configuration.swift */, 0EFEB4442006D3C800F81029 /* TLSBox.h */, 0EFEB4302006D3C800F81029 /* TLSBox.m */, 0EFEB42F2006D3C800F81029 /* TunnelInterface.swift */, @@ -885,6 +893,7 @@ 0EE7A79520F61EDC00B42E6A /* PacketMacros.h in Sources */, 0EFEB45D2006D3C800F81029 /* CryptoBox.m in Sources */, 0EBBF2FA2085061600E36B40 /* NETCPInterface.swift in Sources */, + 0E0C2125212ED29D008AB282 /* SessionError.swift in Sources */, 0EFEB4552006D3C800F81029 /* EncryptionProxy.swift in Sources */, 0EFEB45C2006D3C800F81029 /* ZeroingData.m in Sources */, 0EFEB4632006D3C800F81029 /* ProtocolMacros.swift in Sources */, @@ -910,6 +919,7 @@ 0EFEB4752006D3C800F81029 /* Errors.m in Sources */, 0EBBF2E52084FE6F00E36B40 /* GenericSocket.swift in Sources */, 0EFEB4762006D3C800F81029 /* DataPath.m in Sources */, + 0E0C2127212ED29D008AB282 /* SessionProxy+Configuration.swift in Sources */, 0EFEB4692006D3C800F81029 /* Packet.swift in Sources */, 0EFEB45A2006D3C800F81029 /* TunnelInterface.swift in Sources */, ); @@ -934,6 +944,7 @@ 0EFEB4962006D7F300F81029 /* ProtocolMacros.swift in Sources */, 0EE7A79620F61EDC00B42E6A /* PacketMacros.h in Sources */, 0EFEB48A2006D7C400F81029 /* TunnelKitProvider.swift in Sources */, + 0E0C2126212ED29D008AB282 /* SessionError.swift in Sources */, 0EBBF2FB2085061600E36B40 /* NETCPInterface.swift in Sources */, 0EFEB4982006D7F300F81029 /* ZeroingData.swift in Sources */, 0EFEB4A32006D7F300F81029 /* Errors.m in Sources */, @@ -959,6 +970,7 @@ 0E3E0F222108A8CC00B371C1 /* PushReply.swift in Sources */, 0EFEB4912006D7F300F81029 /* TLSBox.m in Sources */, 0EFEB49D2006D7F300F81029 /* IOInterface.swift in Sources */, + 0E0C2128212ED29D008AB282 /* SessionProxy+Configuration.swift in Sources */, 0EFEB4972006D7F300F81029 /* Authenticator.swift in Sources */, 0EFEB49B2006D7F300F81029 /* Packet.swift in Sources */, ); diff --git a/TunnelKit/Sources/AppExtension/TunnelKitProvider.swift b/TunnelKit/Sources/AppExtension/TunnelKitProvider.swift index 681b22f..5ab8970 100644 --- a/TunnelKit/Sources/AppExtension/TunnelKitProvider.swift +++ b/TunnelKit/Sources/AppExtension/TunnelKitProvider.swift @@ -155,20 +155,22 @@ open class TunnelKitProvider: NEPacketTunnelProvider { cfg.print(appVersion: appVersion) // log.info("Temporary CA is stored to: \(caPath)") - let encryption = SessionProxy.EncryptionParameters(cfg.cipher.rawValue, cfg.digest.rawValue, caPath) - let credentials = SessionProxy.Credentials(endpoint.username, endpoint.password) - + var sessionConfiguration = SessionProxy.ConfigurationBuilder(username: endpoint.username, password: endpoint.password) + sessionConfiguration.cipherName = cfg.cipher.rawValue + sessionConfiguration.digestName = cfg.digest.rawValue + sessionConfiguration.caPath = caPath + if let renegotiatesAfterSeconds = cfg.renegotiatesAfterSeconds { + sessionConfiguration.renegotiatesAfter = Double(renegotiatesAfterSeconds) + } + sessionConfiguration.keepAliveInterval = CoreConfiguration.pingInterval + let proxy: SessionProxy do { - proxy = try SessionProxy(queue: tunnelQueue, encryption: encryption, credentials: credentials) + proxy = try SessionProxy(queue: tunnelQueue, configuration: sessionConfiguration.build()) } catch let e { completionHandler(e) return } - if let renegotiatesAfterSeconds = cfg.renegotiatesAfterSeconds { - proxy.renegotiatesAfter = Double(renegotiatesAfterSeconds) - } - proxy.keepAliveInterval = CoreConfiguration.pingInterval proxy.delegate = self self.proxy = proxy diff --git a/TunnelKit/Sources/Core/SessionError.swift b/TunnelKit/Sources/Core/SessionError.swift new file mode 100644 index 0000000..df2bc81 --- /dev/null +++ b/TunnelKit/Sources/Core/SessionError.swift @@ -0,0 +1,46 @@ +// +// SessionError.swift +// TunnelKit +// +// Created by Davide De Rosa on 23/08/2018. +// Copyright © 2018 Davide De Rosa. All rights reserved. +// + +import Foundation + +/// The possible errors raised/thrown during `SessionProxy` operation. +public enum SessionError: Error { + + /// The negotiation timed out. + case negotiationTimeout + + /// The peer failed to verify. + case peerVerification + + /// The VPN session id is missing. + case missingSessionId + + /// The VPN session id doesn't match. + case sessionMismatch + + /// The connection key is wrong or wasn't expected. + case badKey + + /// The TLS negotiation failed. + case tlsError + + /// The control packet has an incorrect prefix payload. + case wrongControlDataPrefix + + /// The provided credentials failed authentication. + case badCredentials + + /// The reply to PUSH_REQUEST is malformed. + case malformedPushReply + + /// A write operation failed at the link layer (e.g. network unreachable). + case failedLinkWrite + + /// The server couldn't ping back before timeout. + case pingTimeout +} diff --git a/TunnelKit/Sources/Core/SessionProxy+Configuration.swift b/TunnelKit/Sources/Core/SessionProxy+Configuration.swift new file mode 100644 index 0000000..ebc8b2c --- /dev/null +++ b/TunnelKit/Sources/Core/SessionProxy+Configuration.swift @@ -0,0 +1,90 @@ +// +// SessionProxy+Configuration.swift +// TunnelKit +// +// Created by Davide De Rosa on 23/08/2018. +// Copyright © 2018 Davide De Rosa. All rights reserved. +// + +import Foundation + +extension SessionProxy { + + /// The way to create a `SessionProxy.Configuration` object for a `SessionProxy`. + public struct ConfigurationBuilder { + + /// An username. + public let username: String + + /// A password. + public let password: String + + /// The cipher algorithm for data encryption. Must follow OpenSSL nomenclature, e.g. "AES-128-CBC". + public var cipherName: String + + /// The digest algorithm for HMAC. Must follow OpenSSL nomenclature, e.g. "SHA-1". + public var digestName: String + + /// The path to the optional CA for TLS negotiation (PEM format). + public var caPath: String? + + /// Sends periodical keep-alive packets if set. + public var keepAliveInterval: TimeInterval? + + /// The number of seconds after which a renegotiation should be initiated. If `nil`, the client will never initiate a renegotiation. + public var renegotiatesAfter: TimeInterval? + + /// :nodoc: + public init(username: String, password: String) { + self.username = username + self.password = password + cipherName = "AES-128-CBC" + digestName = "SHA-1" + caPath = nil + keepAliveInterval = nil + renegotiatesAfter = nil + } + + /** + Builds a `SessionProxy.Configuration` object. + + - Returns: A `SessionProxy.Configuration` object with this builder. + */ + public func build() -> Configuration { + return Configuration( + username: username, + password: password, + cipherName: cipherName, + digestName: digestName, + caPath: caPath, + keepAliveInterval: keepAliveInterval, + renegotiatesAfter: renegotiatesAfter + ) + } + } + + /// The immutable configuration for `SessionProxy`. + public struct Configuration { + + /// - Seealso: `SessionProxy.ConfigurationBuilder.username` + public let username: String + + /// - Seealso: `SessionProxy.ConfigurationBuilder.password` + public let password: String + + /// - Seealso: `SessionProxy.ConfigurationBuilder.cipherName` + public let cipherName: String + + /// - Seealso: `SessionProxy.ConfigurationBuilder.digestName` + public let digestName: String + + /// - Seealso: `SessionProxy.ConfigurationBuilder.caPath` + public let caPath: String? + + /// - Seealso: `SessionProxy.ConfigurationBuilder.keepAliveInterval` + public let keepAliveInterval: TimeInterval? + + /// - Seealso: `SessionProxy.ConfigurationBuilder.renegotiatesAfter` + public let renegotiatesAfter: TimeInterval? + } +} diff --git a/TunnelKit/Sources/Core/SessionProxy.swift b/TunnelKit/Sources/Core/SessionProxy.swift index efb4731..fc8439d 100644 --- a/TunnelKit/Sources/Core/SessionProxy.swift +++ b/TunnelKit/Sources/Core/SessionProxy.swift @@ -19,43 +19,6 @@ private extension Error { } } -/// The possible errors raised/thrown during `SessionProxy` operation. -public enum SessionError: Error { - - /// The negotiation timed out. - case negotiationTimeout - - /// The peer failed to verify. - case peerVerification - - /// The VPN session id is missing. - case missingSessionId - - /// The VPN session id doesn't match. - case sessionMismatch - - /// The connection key is wrong or wasn't expected. - case badKey - - /// The TLS negotiation failed. - case tlsError - - /// The control packet has an incorrect prefix payload. - case wrongControlDataPrefix - - /// The provided credentials failed authentication. - case badCredentials - - /// The reply to PUSH_REQUEST is malformed. - case malformedPushReply - - /// A write operation failed at the link layer (e.g. network unreachable). - case failedLinkWrite - - /// The server couldn't ping back before timeout. - case pingTimeout -} - /// Observes major events notified by a `SessionProxy`. public protocol SessionProxyDelegate: class { @@ -80,43 +43,6 @@ public protocol SessionProxyDelegate: class { /// Provides methods to set up and maintain an OpenVPN session. public class SessionProxy { - - /// Wraps the encryption parameters of the session. - public struct EncryptionParameters { - - /// The cipher algorithm for data encryption. Must follow OpenSSL nomenclature, e.g. "AES-128-CBC". - public let cipherName: String - - /// The digest algorithm for HMAC. Must follow OpenSSL nomenclature, e.g. "SHA-1". - public let digestName: String - - /// The path to the CA for TLS negotiation (PEM format). - public let caPath: String? - - /// :nodoc: - public init(_ cipherName: String, _ digestName: String, _ caPath: String?) { - self.cipherName = cipherName - self.digestName = digestName - self.caPath = caPath - } - } - - /// A set of credentials. - public struct Credentials { - - /// An username. - public let username: String - - /// A password. - public let password: String - - /// :nodoc: - public init(_ username: String, _ password: String) { - self.username = username - self.password = password - } - } - private enum StopMethod { case shutdown @@ -125,15 +51,7 @@ public class SessionProxy { // MARK: Configuration - private let encryption: EncryptionParameters - - private let credentials: Credentials - - /// Sends periodical keep-alive packets if set. - public var keepAliveInterval: TimeInterval? - - /// The number of seconds after which a renegotiation should be initiated. If `nil`, the client will never initiate a renegotiation. - public var renegotiatesAfter: TimeInterval? + private let configuration: Configuration /// An optional `SessionProxyDelegate` for receiving session events. public weak var delegate: SessionProxyDelegate? @@ -223,17 +141,12 @@ public class SessionProxy { Creates a VPN session. - Parameter queue: The `DispatchQueue` where to run the session loop. - - Parameter encryption: The `SessionProxy.EncryptionParameters` to establish for this session. - - Parameter credentials: The `SessionProxy.Credentials` required for authentication. + - Parameter configuration: The `SessionProxy.Configuration` to use for this session. */ - public init(queue: DispatchQueue, encryption: EncryptionParameters, credentials: Credentials) throws { + public init(queue: DispatchQueue, configuration: Configuration) throws { self.queue = queue - self.encryption = encryption - self.credentials = credentials + self.configuration = configuration - keepAliveInterval = nil - renegotiatesAfter = nil - keys = [:] oldKeys = [] negotiationKeyIdx = 0 @@ -369,9 +282,6 @@ public class SessionProxy { tlsObserver = nil } -// for (_, key) in keys { -// key.dispose() -// } keys.removeAll() oldKeys.removeAll() negotiationKeyIdx = 0 @@ -644,7 +554,7 @@ public class SessionProxy { return } - if let interval = keepAliveInterval { + if let interval = configuration.keepAliveInterval { let elapsed = now.timeIntervalSince(lastPingOut) guard (elapsed >= interval) else { let remaining = min(interval, interval - elapsed) @@ -659,7 +569,7 @@ public class SessionProxy { sendDataPackets([DataPacket.pingString]) lastPingOut = Date() - if let interval = keepAliveInterval { + if let interval = configuration.keepAliveInterval { queue.asyncAfter(deadline: .now() + interval) { [weak self] in self?.ping() } @@ -725,7 +635,7 @@ public class SessionProxy { negotiationKey.controlState = .preAuth do { - authenticator = try Authenticator(credentials.username, authToken ?? credentials.password) + authenticator = try Authenticator(configuration.username, authToken ?? configuration.password) try authenticator?.putAuth(into: negotiationKey.tls) } catch let e { deferStop(.shutdown, e) @@ -774,7 +684,7 @@ public class SessionProxy { } private func maybeRenegotiate() { - guard let renegotiatesAfter = renegotiatesAfter else { + guard let renegotiatesAfter = configuration.renegotiatesAfter else { return } guard (negotiationKeyIdx == currentKeyIdx) else { @@ -828,7 +738,7 @@ public class SessionProxy { log.debug("Remote sessionId is \(remoteSessionId.toHex())") log.debug("Start TLS handshake") - negotiationKey.tlsOptional = TLSBox(caPath: encryption.caPath) + negotiationKey.tlsOptional = TLSBox(caPath: configuration.caPath) do { try negotiationKey.tls.start() } catch let e { @@ -968,7 +878,7 @@ public class SessionProxy { dnsServers: reply.dnsServers ) - if let interval = keepAliveInterval { + if let interval = configuration.keepAliveInterval { queue.asyncAfter(deadline: .now() + interval) { [weak self] in self?.ping() } @@ -1100,7 +1010,7 @@ public class SessionProxy { let proxy: EncryptionProxy do { - proxy = try EncryptionProxy(encryption.cipherName, encryption.digestName, auth, sessionId, remoteSessionId) + proxy = try EncryptionProxy(configuration.cipherName, configuration.digestName, auth, sessionId, remoteSessionId) } catch let e { deferStop(.shutdown, e) return From 6d5e9f68a9a7402950b584bfd88fd1a79e37e670 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Thu, 23 Aug 2018 15:44:31 +0200 Subject: [PATCH 2/5] Move cipher/digest enums to Core Restrict choice to supported OpenSSL algorithms. --- .../TunnelKitProvider+Configuration.swift | 47 +++------------- .../AppExtension/TunnelKitProvider.swift | 4 +- TunnelKit/Sources/Core/EncryptionProxy.swift | 6 +-- .../Core/SessionProxy+Configuration.swift | 54 ++++++++++++++----- TunnelKit/Sources/Core/SessionProxy.swift | 8 ++- TunnelKitTests/DataPathPerformanceTests.swift | 2 +- 6 files changed, 61 insertions(+), 60 deletions(-) diff --git a/TunnelKit/Sources/AppExtension/TunnelKitProvider+Configuration.swift b/TunnelKit/Sources/AppExtension/TunnelKitProvider+Configuration.swift index 3acc3fb..60806a8 100644 --- a/TunnelKit/Sources/AppExtension/TunnelKitProvider+Configuration.swift +++ b/TunnelKit/Sources/AppExtension/TunnelKitProvider+Configuration.swift @@ -12,41 +12,6 @@ import SwiftyBeaver private let log = SwiftyBeaver.self -extension TunnelKitProvider { - - // MARK: Cryptography - - /// The available encryption algorithms. - public enum Cipher: String { - - // WARNING: must match OpenSSL algorithm names - - /// AES encryption with 128-bit key size and CBC. - case aes128cbc = "AES-128-CBC" - - /// AES encryption with 256-bit key size and CBC. - case aes256cbc = "AES-256-CBC" - - /// AES encryption with 128-bit key size and GCM. - case aes128gcm = "AES-128-GCM" - - /// AES encryption with 256-bit key size and GCM. - case aes256gcm = "AES-256-GCM" - } - - /// The available message digest algorithms. - public enum Digest: String { - - // WARNING: must match OpenSSL algorithm names - - /// SHA1 message digest. - case sha1 = "SHA1" - - /// SHA256 message digest. - case sha256 = "SHA256" - } -} - extension TunnelKitProvider { // MARK: Configuration @@ -152,10 +117,10 @@ extension TunnelKitProvider { public var endpointProtocols: [EndpointProtocol] /// The encryption algorithm. - public var cipher: Cipher + public var cipher: SessionProxy.Cipher /// The message digest algorithm. - public var digest: Digest + public var digest: SessionProxy.Digest /// The optional CA certificate to validate server against. Set to `nil` to disable CA validation (default). public var ca: Certificate? @@ -205,10 +170,10 @@ extension TunnelKitProvider { guard let appGroup = providerConfiguration[S.appGroup] as? String else { throw ProviderError.configuration(field: "protocolConfiguration.providerConfiguration[\(S.appGroup)]") } - guard let cipherAlgorithm = providerConfiguration[S.cipherAlgorithm] as? String, let cipher = Cipher(rawValue: cipherAlgorithm) else { + guard let cipherAlgorithm = providerConfiguration[S.cipherAlgorithm] as? String, let cipher = SessionProxy.Cipher(rawValue: cipherAlgorithm) else { throw ProviderError.configuration(field: "protocolConfiguration.providerConfiguration[\(S.cipherAlgorithm)]") } - guard let digestAlgorithm = providerConfiguration[S.digestAlgorithm] as? String, let digest = Digest(rawValue: digestAlgorithm) else { + guard let digestAlgorithm = providerConfiguration[S.digestAlgorithm] as? String, let digest = SessionProxy.Digest(rawValue: digestAlgorithm) else { throw ProviderError.configuration(field: "protocolConfiguration.providerConfiguration[\(S.digestAlgorithm)]") } @@ -327,10 +292,10 @@ extension TunnelKitProvider { public let endpointProtocols: [EndpointProtocol] /// - Seealso: `TunnelKitProvider.ConfigurationBuilder.cipher` - public let cipher: Cipher + public let cipher: SessionProxy.Cipher /// - Seealso: `TunnelKitProvider.ConfigurationBuilder.digest` - public let digest: Digest + public let digest: SessionProxy.Digest /// - Seealso: `TunnelKitProvider.ConfigurationBuilder.ca` public let ca: Certificate? diff --git a/TunnelKit/Sources/AppExtension/TunnelKitProvider.swift b/TunnelKit/Sources/AppExtension/TunnelKitProvider.swift index 5ab8970..84ba6f9 100644 --- a/TunnelKit/Sources/AppExtension/TunnelKitProvider.swift +++ b/TunnelKit/Sources/AppExtension/TunnelKitProvider.swift @@ -156,8 +156,8 @@ open class TunnelKitProvider: NEPacketTunnelProvider { // log.info("Temporary CA is stored to: \(caPath)") var sessionConfiguration = SessionProxy.ConfigurationBuilder(username: endpoint.username, password: endpoint.password) - sessionConfiguration.cipherName = cfg.cipher.rawValue - sessionConfiguration.digestName = cfg.digest.rawValue + sessionConfiguration.cipher = cfg.cipher + sessionConfiguration.digest = cfg.digest sessionConfiguration.caPath = caPath if let renegotiatesAfterSeconds = cfg.renegotiatesAfterSeconds { sessionConfiguration.renegotiatesAfter = Double(renegotiatesAfterSeconds) diff --git a/TunnelKit/Sources/Core/EncryptionProxy.swift b/TunnelKit/Sources/Core/EncryptionProxy.swift index b832c37..fb7b8ed 100644 --- a/TunnelKit/Sources/Core/EncryptionProxy.swift +++ b/TunnelKit/Sources/Core/EncryptionProxy.swift @@ -96,7 +96,7 @@ public class EncryptionProxy { return buffer.withOffset(0, count: length) } - convenience init(_ cipher: String, _ digest: String, _ auth: Authenticator, + convenience init(_ cipher: SessionProxy.Cipher, _ digest: SessionProxy.Digest, _ auth: Authenticator, _ sessionId: Data, _ remoteSessionId: Data) throws { guard let serverRandom1 = auth.serverRandom1, let serverRandom2 = auth.serverRandom2 else { @@ -130,8 +130,8 @@ public class EncryptionProxy { try self.init(cipher, digest, cipherEncKey, cipherDecKey, hmacEncKey, hmacDecKey) } - init(_ cipher: String, _ digest: String, _ cipherEncKey: ZeroingData, _ cipherDecKey: ZeroingData, _ hmacEncKey: ZeroingData, _ hmacDecKey: ZeroingData) throws { - box = CryptoBox(cipherAlgorithm: cipher, digestAlgorithm: digest) + init(_ cipher: SessionProxy.Cipher, _ digest: SessionProxy.Digest, _ cipherEncKey: ZeroingData, _ cipherDecKey: ZeroingData, _ hmacEncKey: ZeroingData, _ hmacDecKey: ZeroingData) throws { + box = CryptoBox(cipherAlgorithm: cipher.rawValue, digestAlgorithm: digest.rawValue) try box.configure( withCipherEncKey: cipherEncKey, cipherDecKey: cipherDecKey, diff --git a/TunnelKit/Sources/Core/SessionProxy+Configuration.swift b/TunnelKit/Sources/Core/SessionProxy+Configuration.swift index ebc8b2c..a35a50e 100644 --- a/TunnelKit/Sources/Core/SessionProxy+Configuration.swift +++ b/TunnelKit/Sources/Core/SessionProxy+Configuration.swift @@ -10,6 +10,36 @@ import Foundation extension SessionProxy { + /// The available encryption algorithms. + public enum Cipher: String { + + // WARNING: must match OpenSSL algorithm names + + /// AES encryption with 128-bit key size and CBC. + case aes128cbc = "AES-128-CBC" + + /// AES encryption with 256-bit key size and CBC. + case aes256cbc = "AES-256-CBC" + + /// AES encryption with 128-bit key size and GCM. + case aes128gcm = "AES-128-GCM" + + /// AES encryption with 256-bit key size and GCM. + case aes256gcm = "AES-256-GCM" + } + + /// The available message digest algorithms. + public enum Digest: String { + + // WARNING: must match OpenSSL algorithm names + + /// SHA1 message digest. + case sha1 = "SHA1" + + /// SHA256 message digest. + case sha256 = "SHA256" + } + /// The way to create a `SessionProxy.Configuration` object for a `SessionProxy`. public struct ConfigurationBuilder { @@ -19,11 +49,11 @@ extension SessionProxy { /// A password. public let password: String - /// The cipher algorithm for data encryption. Must follow OpenSSL nomenclature, e.g. "AES-128-CBC". - public var cipherName: String + /// The cipher algorithm for data encryption. + public var cipher: Cipher - /// The digest algorithm for HMAC. Must follow OpenSSL nomenclature, e.g. "SHA-1". - public var digestName: String + /// The digest algorithm for HMAC. + public var digest: Digest /// The path to the optional CA for TLS negotiation (PEM format). public var caPath: String? @@ -38,8 +68,8 @@ extension SessionProxy { public init(username: String, password: String) { self.username = username self.password = password - cipherName = "AES-128-CBC" - digestName = "SHA-1" + cipher = .aes128cbc + digest = .sha1 caPath = nil keepAliveInterval = nil renegotiatesAfter = nil @@ -54,8 +84,8 @@ extension SessionProxy { return Configuration( username: username, password: password, - cipherName: cipherName, - digestName: digestName, + cipher: cipher, + digest: digest, caPath: caPath, keepAliveInterval: keepAliveInterval, renegotiatesAfter: renegotiatesAfter @@ -72,11 +102,11 @@ extension SessionProxy { /// - Seealso: `SessionProxy.ConfigurationBuilder.password` public let password: String - /// - Seealso: `SessionProxy.ConfigurationBuilder.cipherName` - public let cipherName: String + /// - Seealso: `SessionProxy.ConfigurationBuilder.cipher` + public let cipher: Cipher - /// - Seealso: `SessionProxy.ConfigurationBuilder.digestName` - public let digestName: String + /// - Seealso: `SessionProxy.ConfigurationBuilder.digest` + public let digest: Digest /// - Seealso: `SessionProxy.ConfigurationBuilder.caPath` public let caPath: String? diff --git a/TunnelKit/Sources/Core/SessionProxy.swift b/TunnelKit/Sources/Core/SessionProxy.swift index fc8439d..6300a25 100644 --- a/TunnelKit/Sources/Core/SessionProxy.swift +++ b/TunnelKit/Sources/Core/SessionProxy.swift @@ -1010,7 +1010,13 @@ public class SessionProxy { let proxy: EncryptionProxy do { - proxy = try EncryptionProxy(configuration.cipherName, configuration.digestName, auth, sessionId, remoteSessionId) + proxy = try EncryptionProxy( + configuration.cipher, + configuration.digest, + auth, + sessionId, + remoteSessionId + ) } catch let e { deferStop(.shutdown, e) return diff --git a/TunnelKitTests/DataPathPerformanceTests.swift b/TunnelKitTests/DataPathPerformanceTests.swift index e5b43fa..664077d 100644 --- a/TunnelKitTests/DataPathPerformanceTests.swift +++ b/TunnelKitTests/DataPathPerformanceTests.swift @@ -21,7 +21,7 @@ class DataPathPerformanceTests: XCTestCase { let ck = try! SecureRandom.safeData(length: 32) let hk = try! SecureRandom.safeData(length: 32) - let crypto = try! EncryptionProxy("aes-128-cbc", "sha1", ck, ck, hk, hk) + let crypto = try! EncryptionProxy(.aes128cbc, .sha1, ck, ck, hk, hk) encrypter = crypto.encrypter() decrypter = crypto.decrypter() From 2459fe1bfd3c5bc6f3ac2c5a405a69b3e7a0b191 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Thu, 23 Aug 2018 15:49:21 +0200 Subject: [PATCH 3/5] Move a few classes inside SessionProxy - Authenticator - EncryptionBridge (formerly EncryptionProxy) - PushReply - SessionKey They only make sense there. Content unchanged. --- .jazzy.yaml | 1 - TunnelKit.xcodeproj/project.pbxproj | 48 +++--- .../AppExtension/TunnelKitProvider.swift | 2 +- TunnelKit/Sources/Core/Authenticator.swift | 150 ----------------- TunnelKit/Sources/Core/EncryptionProxy.swift | 150 ----------------- TunnelKit/Sources/Core/PushReply.swift | 88 ---------- TunnelKit/Sources/Core/SessionKey.swift | 120 -------------- .../Core/SessionProxy+Authenticator.swift | 152 +++++++++++++++++ .../Core/SessionProxy+EncryptionBridge.swift | 153 ++++++++++++++++++ .../Sources/Core/SessionProxy+PushReply.swift | 90 +++++++++++ .../Core/SessionProxy+SessionKey.swift | 122 ++++++++++++++ TunnelKit/Sources/Core/SessionProxy.swift | 8 +- TunnelKitTests/DataPathPerformanceTests.swift | 2 +- 13 files changed, 547 insertions(+), 539 deletions(-) delete mode 100644 TunnelKit/Sources/Core/Authenticator.swift delete mode 100644 TunnelKit/Sources/Core/EncryptionProxy.swift delete mode 100644 TunnelKit/Sources/Core/PushReply.swift delete mode 100644 TunnelKit/Sources/Core/SessionKey.swift create mode 100644 TunnelKit/Sources/Core/SessionProxy+Authenticator.swift create mode 100644 TunnelKit/Sources/Core/SessionProxy+EncryptionBridge.swift create mode 100644 TunnelKit/Sources/Core/SessionProxy+PushReply.swift create mode 100644 TunnelKit/Sources/Core/SessionProxy+SessionKey.swift diff --git a/.jazzy.yaml b/.jazzy.yaml index a78374e..74df20c 100644 --- a/.jazzy.yaml +++ b/.jazzy.yaml @@ -14,7 +14,6 @@ xcodebuild_arguments: custom_categories: - name: Core children: - - EncryptionProxy - IOInterface - LinkInterface - TunnelInterface diff --git a/TunnelKit.xcodeproj/project.pbxproj b/TunnelKit.xcodeproj/project.pbxproj index ede5158..8991f42 100644 --- a/TunnelKit.xcodeproj/project.pbxproj +++ b/TunnelKit.xcodeproj/project.pbxproj @@ -29,8 +29,8 @@ 0E1108B11F77B9F900A92462 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E1108AF1F77B9F900A92462 /* Main.storyboard */; }; 0E1108B31F77B9F900A92462 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E1108B21F77B9F900A92462 /* Assets.xcassets */; }; 0E1108B61F77B9F900A92462 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E1108B41F77B9F900A92462 /* LaunchScreen.storyboard */; }; - 0E3E0F212108A8CC00B371C1 /* PushReply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E0F202108A8CC00B371C1 /* PushReply.swift */; }; - 0E3E0F222108A8CC00B371C1 /* PushReply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E0F202108A8CC00B371C1 /* PushReply.swift */; }; + 0E3E0F212108A8CC00B371C1 /* SessionProxy+PushReply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E0F202108A8CC00B371C1 /* SessionProxy+PushReply.swift */; }; + 0E3E0F222108A8CC00B371C1 /* SessionProxy+PushReply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E0F202108A8CC00B371C1 /* SessionProxy+PushReply.swift */; }; 0E85A25A202CC5AF0059E9F9 /* AppExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E85A259202CC5AE0059E9F9 /* AppExtensionTests.swift */; }; 0E9379C91F819A4300CE91B6 /* TunnelKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E17D7F91F730D9F009EE129 /* TunnelKit.framework */; }; 0EA8E2072024D4B200A92DB6 /* PIA-ECC-256k1.pem in Resources */ = {isa = PBXBuildFile; fileRef = 0EA8E2042024D4B100A92DB6 /* PIA-ECC-256k1.pem */; }; @@ -83,8 +83,8 @@ 0EEC49E820B5F7F6008FEB91 /* ReplayProtector.h in Headers */ = {isa = PBXBuildFile; fileRef = 0EFEB4392006D3C800F81029 /* ReplayProtector.h */; }; 0EEC49E920B5F7F6008FEB91 /* TLSBox.h in Headers */ = {isa = PBXBuildFile; fileRef = 0EFEB4442006D3C800F81029 /* TLSBox.h */; }; 0EEC49EA20B5F7F6008FEB91 /* ZeroingData.h in Headers */ = {isa = PBXBuildFile; fileRef = 0EFEB4412006D3C800F81029 /* ZeroingData.h */; }; - 0EFEB4552006D3C800F81029 /* EncryptionProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB42A2006D3C800F81029 /* EncryptionProxy.swift */; }; - 0EFEB4562006D3C800F81029 /* SessionKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB42B2006D3C800F81029 /* SessionKey.swift */; }; + 0EFEB4552006D3C800F81029 /* SessionProxy+EncryptionBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB42A2006D3C800F81029 /* SessionProxy+EncryptionBridge.swift */; }; + 0EFEB4562006D3C800F81029 /* SessionProxy+SessionKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB42B2006D3C800F81029 /* SessionProxy+SessionKey.swift */; }; 0EFEB4582006D3C800F81029 /* MSS.h in Headers */ = {isa = PBXBuildFile; fileRef = 0EFEB42D2006D3C800F81029 /* MSS.h */; }; 0EFEB4592006D3C800F81029 /* Allocation.h in Headers */ = {isa = PBXBuildFile; fileRef = 0EFEB42E2006D3C800F81029 /* Allocation.h */; }; 0EFEB45A2006D3C800F81029 /* TunnelInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB42F2006D3C800F81029 /* TunnelInterface.swift */; }; @@ -96,7 +96,7 @@ 0EFEB4622006D3C800F81029 /* SecureRandom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB4372006D3C800F81029 /* SecureRandom.swift */; }; 0EFEB4632006D3C800F81029 /* ProtocolMacros.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB4382006D3C800F81029 /* ProtocolMacros.swift */; }; 0EFEB4642006D3C800F81029 /* ReplayProtector.h in Headers */ = {isa = PBXBuildFile; fileRef = 0EFEB4392006D3C800F81029 /* ReplayProtector.h */; }; - 0EFEB4652006D3C800F81029 /* Authenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB43A2006D3C800F81029 /* Authenticator.swift */; }; + 0EFEB4652006D3C800F81029 /* SessionProxy+Authenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB43A2006D3C800F81029 /* SessionProxy+Authenticator.swift */; }; 0EFEB4662006D3C800F81029 /* ZeroingData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB43B2006D3C800F81029 /* ZeroingData.swift */; }; 0EFEB4672006D3C800F81029 /* SessionProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB43C2006D3C800F81029 /* SessionProxy.swift */; }; 0EFEB4682006D3C800F81029 /* MSS.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB43D2006D3C800F81029 /* MSS.m */; }; @@ -121,15 +121,15 @@ 0EFEB4872006D7C400F81029 /* TunnelKitProvider+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB44F2006D3C800F81029 /* TunnelKitProvider+Configuration.swift */; }; 0EFEB4882006D7C400F81029 /* TunnelKitProvider+Interaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB4502006D3C800F81029 /* TunnelKitProvider+Interaction.swift */; }; 0EFEB48A2006D7C400F81029 /* TunnelKitProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB4522006D3C800F81029 /* TunnelKitProvider.swift */; }; - 0EFEB48D2006D7F300F81029 /* EncryptionProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB42A2006D3C800F81029 /* EncryptionProxy.swift */; }; - 0EFEB48E2006D7F300F81029 /* SessionKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB42B2006D3C800F81029 /* SessionKey.swift */; }; + 0EFEB48D2006D7F300F81029 /* SessionProxy+EncryptionBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB42A2006D3C800F81029 /* SessionProxy+EncryptionBridge.swift */; }; + 0EFEB48E2006D7F300F81029 /* SessionProxy+SessionKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB42B2006D3C800F81029 /* SessionProxy+SessionKey.swift */; }; 0EFEB4902006D7F300F81029 /* TunnelInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB42F2006D3C800F81029 /* TunnelInterface.swift */; }; 0EFEB4912006D7F300F81029 /* TLSBox.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB4302006D3C800F81029 /* TLSBox.m */; }; 0EFEB4922006D7F300F81029 /* ZeroingData.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB4312006D3C800F81029 /* ZeroingData.m */; }; 0EFEB4932006D7F300F81029 /* CryptoBox.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB4322006D3C800F81029 /* CryptoBox.m */; }; 0EFEB4952006D7F300F81029 /* SecureRandom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB4372006D3C800F81029 /* SecureRandom.swift */; }; 0EFEB4962006D7F300F81029 /* ProtocolMacros.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB4382006D3C800F81029 /* ProtocolMacros.swift */; }; - 0EFEB4972006D7F300F81029 /* Authenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB43A2006D3C800F81029 /* Authenticator.swift */; }; + 0EFEB4972006D7F300F81029 /* SessionProxy+Authenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB43A2006D3C800F81029 /* SessionProxy+Authenticator.swift */; }; 0EFEB4982006D7F300F81029 /* ZeroingData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB43B2006D3C800F81029 /* ZeroingData.swift */; }; 0EFEB4992006D7F300F81029 /* SessionProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB43C2006D3C800F81029 /* SessionProxy.swift */; }; 0EFEB49A2006D7F300F81029 /* MSS.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EFEB43D2006D3C800F81029 /* MSS.m */; }; @@ -197,7 +197,7 @@ 0E1108B71F77B9F900A92462 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 0E17D7F91F730D9F009EE129 /* TunnelKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TunnelKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 0E3251C51F95770D00C108D9 /* TunnelKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TunnelKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 0E3E0F202108A8CC00B371C1 /* PushReply.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushReply.swift; sourceTree = ""; }; + 0E3E0F202108A8CC00B371C1 /* SessionProxy+PushReply.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionProxy+PushReply.swift"; sourceTree = ""; }; 0E6479DD212EAC96008E6888 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 0E6479E0212EACD6008E6888 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 0E85A259202CC5AE0059E9F9 /* AppExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppExtensionTests.swift; sourceTree = ""; }; @@ -227,8 +227,8 @@ 0EE7A79D20F6488400B42E6A /* DataPathEncryption.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DataPathEncryption.h; sourceTree = ""; }; 0EE7A7A020F664AB00B42E6A /* DataPathEncryptionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataPathEncryptionTests.swift; sourceTree = ""; }; 0EEC49DB20B5E732008FEB91 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; - 0EFEB42A2006D3C800F81029 /* EncryptionProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EncryptionProxy.swift; sourceTree = ""; }; - 0EFEB42B2006D3C800F81029 /* SessionKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionKey.swift; sourceTree = ""; }; + 0EFEB42A2006D3C800F81029 /* SessionProxy+EncryptionBridge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SessionProxy+EncryptionBridge.swift"; sourceTree = ""; }; + 0EFEB42B2006D3C800F81029 /* SessionProxy+SessionKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SessionProxy+SessionKey.swift"; sourceTree = ""; }; 0EFEB42D2006D3C800F81029 /* MSS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MSS.h; sourceTree = ""; }; 0EFEB42E2006D3C800F81029 /* Allocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Allocation.h; sourceTree = ""; }; 0EFEB42F2006D3C800F81029 /* TunnelInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TunnelInterface.swift; sourceTree = ""; }; @@ -240,7 +240,7 @@ 0EFEB4372006D3C800F81029 /* SecureRandom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecureRandom.swift; sourceTree = ""; }; 0EFEB4382006D3C800F81029 /* ProtocolMacros.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProtocolMacros.swift; sourceTree = ""; }; 0EFEB4392006D3C800F81029 /* ReplayProtector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReplayProtector.h; sourceTree = ""; }; - 0EFEB43A2006D3C800F81029 /* Authenticator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Authenticator.swift; sourceTree = ""; }; + 0EFEB43A2006D3C800F81029 /* SessionProxy+Authenticator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SessionProxy+Authenticator.swift"; sourceTree = ""; }; 0EFEB43B2006D3C800F81029 /* ZeroingData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZeroingData.swift; sourceTree = ""; }; 0EFEB43C2006D3C800F81029 /* SessionProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionProxy.swift; sourceTree = ""; }; 0EFEB43D2006D3C800F81029 /* MSS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MSS.m; sourceTree = ""; }; @@ -422,7 +422,6 @@ children = ( 0EFEB42E2006D3C800F81029 /* Allocation.h */, 0EFEB4462006D3C800F81029 /* Allocation.m */, - 0EFEB43A2006D3C800F81029 /* Authenticator.swift */, 0EFEB44A2006D3C800F81029 /* CoreConfiguration.swift */, 0E07597C20F0060E00F38FD8 /* CryptoAEAD.h */, 0E07597D20F0060E00F38FD8 /* CryptoAEAD.m */, @@ -436,7 +435,6 @@ 0EFEB44C2006D3C800F81029 /* DataPath.m */, 0EE7A79D20F6488400B42E6A /* DataPathEncryption.h */, 0E07596A20EF79AB00F38FD8 /* Encryption.h */, - 0EFEB42A2006D3C800F81029 /* EncryptionProxy.swift */, 0EFEB4362006D3C800F81029 /* Errors.h */, 0EFEB44B2006D3C800F81029 /* Errors.m */, 0EFEB4452006D3C800F81029 /* IOInterface.swift */, @@ -448,14 +446,16 @@ 0EE7A79420F61EDC00B42E6A /* PacketMacros.h */, 0EE7A79720F6296F00B42E6A /* PacketMacros.m */, 0EFEB4382006D3C800F81029 /* ProtocolMacros.swift */, - 0E3E0F202108A8CC00B371C1 /* PushReply.swift */, 0EFEB4392006D3C800F81029 /* ReplayProtector.h */, 0EFEB4482006D3C800F81029 /* ReplayProtector.m */, 0EFEB4372006D3C800F81029 /* SecureRandom.swift */, 0E0C2123212ED29D008AB282 /* SessionError.swift */, - 0EFEB42B2006D3C800F81029 /* SessionKey.swift */, 0EFEB43C2006D3C800F81029 /* SessionProxy.swift */, + 0EFEB43A2006D3C800F81029 /* SessionProxy+Authenticator.swift */, 0E0C2124212ED29D008AB282 /* SessionProxy+Configuration.swift */, + 0EFEB42A2006D3C800F81029 /* SessionProxy+EncryptionBridge.swift */, + 0E3E0F202108A8CC00B371C1 /* SessionProxy+PushReply.swift */, + 0EFEB42B2006D3C800F81029 /* SessionProxy+SessionKey.swift */, 0EFEB4442006D3C800F81029 /* TLSBox.h */, 0EFEB4302006D3C800F81029 /* TLSBox.m */, 0EFEB42F2006D3C800F81029 /* TunnelInterface.swift */, @@ -881,10 +881,10 @@ 0EBBF2F5208505D700E36B40 /* NETunnelInterface.swift in Sources */, 0EFEB4732006D3C800F81029 /* LinkInterface.swift in Sources */, 0EBBF2F8208505DD00E36B40 /* NWUDPSessionState+Description.swift in Sources */, - 0EFEB4652006D3C800F81029 /* Authenticator.swift in Sources */, + 0EFEB4652006D3C800F81029 /* SessionProxy+Authenticator.swift in Sources */, 0EE7A79820F6296F00B42E6A /* PacketMacros.m in Sources */, 0EEC49DC20B5E732008FEB91 /* Utils.swift in Sources */, - 0EFEB4562006D3C800F81029 /* SessionKey.swift in Sources */, + 0EFEB4562006D3C800F81029 /* SessionProxy+SessionKey.swift in Sources */, 0EC1BBA520D71190007C4C7B /* DNSResolver.swift in Sources */, 0EFEB4AB200760EC00F81029 /* MemoryDestination.swift in Sources */, 0EFEB4AE2007625E00F81029 /* Keychain.swift in Sources */, @@ -894,7 +894,7 @@ 0EFEB45D2006D3C800F81029 /* CryptoBox.m in Sources */, 0EBBF2FA2085061600E36B40 /* NETCPInterface.swift in Sources */, 0E0C2125212ED29D008AB282 /* SessionError.swift in Sources */, - 0EFEB4552006D3C800F81029 /* EncryptionProxy.swift in Sources */, + 0EFEB4552006D3C800F81029 /* SessionProxy+EncryptionBridge.swift in Sources */, 0EFEB45C2006D3C800F81029 /* ZeroingData.m in Sources */, 0EFEB4632006D3C800F81029 /* ProtocolMacros.swift in Sources */, 0EFEB4AC200760EC00F81029 /* InterfaceObserver.swift in Sources */, @@ -915,7 +915,7 @@ 0EFEB4672006D3C800F81029 /* SessionProxy.swift in Sources */, 0EFEB4722006D3C800F81029 /* ReplayProtector.m in Sources */, 0EFEB4782006D3C800F81029 /* TunnelKitProvider+Configuration.swift in Sources */, - 0E3E0F212108A8CC00B371C1 /* PushReply.swift in Sources */, + 0E3E0F212108A8CC00B371C1 /* SessionProxy+PushReply.swift in Sources */, 0EFEB4752006D3C800F81029 /* Errors.m in Sources */, 0EBBF2E52084FE6F00E36B40 /* GenericSocket.swift in Sources */, 0EFEB4762006D3C800F81029 /* DataPath.m in Sources */, @@ -952,7 +952,7 @@ 0EFEB4952006D7F300F81029 /* SecureRandom.swift in Sources */, 0EFEB49A2006D7F300F81029 /* MSS.m in Sources */, 0ECE352A212EB88E0040F253 /* Certificate.swift in Sources */, - 0EFEB48D2006D7F300F81029 /* EncryptionProxy.swift in Sources */, + 0EFEB48D2006D7F300F81029 /* SessionProxy+EncryptionBridge.swift in Sources */, 0EFEB4922006D7F300F81029 /* ZeroingData.m in Sources */, 0E07596020EF6D1400F38FD8 /* CryptoCBC.m in Sources */, 0EC1BBA920D7D803007C4C7B /* ConnectionStrategy.swift in Sources */, @@ -963,15 +963,15 @@ 0EFEB4902006D7F300F81029 /* TunnelInterface.swift in Sources */, 0EFEB49E2006D7F300F81029 /* Allocation.m in Sources */, 0EFEB4B02007627700F81029 /* Keychain.swift in Sources */, - 0EFEB48E2006D7F300F81029 /* SessionKey.swift in Sources */, + 0EFEB48E2006D7F300F81029 /* SessionProxy+SessionKey.swift in Sources */, 0EFEB4AF2007627700F81029 /* InterfaceObserver.swift in Sources */, 0EFEB4A42006D7F300F81029 /* DataPath.m in Sources */, 0EBBF2E62084FE6F00E36B40 /* GenericSocket.swift in Sources */, - 0E3E0F222108A8CC00B371C1 /* PushReply.swift in Sources */, + 0E3E0F222108A8CC00B371C1 /* SessionProxy+PushReply.swift in Sources */, 0EFEB4912006D7F300F81029 /* TLSBox.m in Sources */, 0EFEB49D2006D7F300F81029 /* IOInterface.swift in Sources */, 0E0C2128212ED29D008AB282 /* SessionProxy+Configuration.swift in Sources */, - 0EFEB4972006D7F300F81029 /* Authenticator.swift in Sources */, + 0EFEB4972006D7F300F81029 /* SessionProxy+Authenticator.swift in Sources */, 0EFEB49B2006D7F300F81029 /* Packet.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/TunnelKit/Sources/AppExtension/TunnelKitProvider.swift b/TunnelKit/Sources/AppExtension/TunnelKitProvider.swift index 84ba6f9..963947e 100644 --- a/TunnelKit/Sources/AppExtension/TunnelKitProvider.swift +++ b/TunnelKit/Sources/AppExtension/TunnelKitProvider.swift @@ -134,7 +134,7 @@ open class TunnelKitProvider: NEPacketTunnelProvider { log.info("Starting tunnel...") - guard EncryptionProxy.prepareRandomNumberGenerator(seedLength: prngSeedLength) else { + guard SessionProxy.EncryptionBridge.prepareRandomNumberGenerator(seedLength: prngSeedLength) else { completionHandler(ProviderError.prngInitialization) return } diff --git a/TunnelKit/Sources/Core/Authenticator.swift b/TunnelKit/Sources/Core/Authenticator.swift deleted file mode 100644 index e6e4cc3..0000000 --- a/TunnelKit/Sources/Core/Authenticator.swift +++ /dev/null @@ -1,150 +0,0 @@ -// -// Authenticator.swift -// TunnelKit -// -// Created by Davide De Rosa on 2/9/17. -// Copyright © 2018 London Trust Media. All rights reserved. -// - -import Foundation -import SwiftyBeaver -import __TunnelKitNative - -private let log = SwiftyBeaver.self - -fileprivate extension ZeroingData { - fileprivate func appendSized(_ buf: ZeroingData) { - append(Z(UInt16(buf.count).bigEndian)) - append(buf) - } -} - -class Authenticator { - private var controlBuffer: ZeroingData - - private(set) var preMaster: ZeroingData - - private(set) var random1: ZeroingData - - private(set) var random2: ZeroingData - - private(set) var serverRandom1: ZeroingData? - - private(set) var serverRandom2: ZeroingData? - - let username: ZeroingData - - let password: ZeroingData - - init(_ username: String, _ password: String) throws { - preMaster = try SecureRandom.safeData(length: CoreConfiguration.preMasterLength) - random1 = try SecureRandom.safeData(length: CoreConfiguration.randomLength) - random2 = try SecureRandom.safeData(length: CoreConfiguration.randomLength) - - // XXX: not 100% secure, can't erase input username/password - self.username = Z(username, nullTerminated: true) - self.password = Z(password, nullTerminated: true) - - controlBuffer = Z() - } - - // MARK: Authentication request - - // Ruby: on_tls_connect - func putAuth(into: TLSBox) throws { - let raw = Z(ProtocolMacros.tlsPrefix) - - // local keys - raw.append(preMaster) - raw.append(random1) - raw.append(random2) - - // opts - raw.appendSized(Z(UInt8(0))) - - // credentials - raw.appendSized(username) - raw.appendSized(password) - - // peer info - raw.appendSized(Z(CoreConfiguration.peerInfo)) - - if CoreConfiguration.logsSensitiveData { - log.debug("TLS.auth: Put plaintext (\(raw.count) bytes): \(raw.toHex())") - } else { - log.debug("TLS.auth: Put plaintext (\(raw.count) bytes)") - } - - try into.putRawPlainText(raw.bytes, length: raw.count) - } - - // MARK: Server replies - - func appendControlData(_ data: ZeroingData) { - controlBuffer.append(data) - } - - func parseAuthReply() throws -> Bool { - let prefixLength = ProtocolMacros.tlsPrefix.count - - // TLS prefix + random (x2) + opts length [+ opts] - guard (controlBuffer.count >= prefixLength + 2 * CoreConfiguration.randomLength + 2) else { - return false - } - - let prefix = controlBuffer.withOffset(0, count: prefixLength) - guard prefix.isEqual(to: ProtocolMacros.tlsPrefix) else { - throw SessionError.wrongControlDataPrefix - } - - var offset = ProtocolMacros.tlsPrefix.count - - let serverRandom1 = controlBuffer.withOffset(offset, count: CoreConfiguration.randomLength) - offset += CoreConfiguration.randomLength - - let serverRandom2 = controlBuffer.withOffset(offset, count: CoreConfiguration.randomLength) - offset += CoreConfiguration.randomLength - - let serverOptsLength = Int(controlBuffer.networkUInt16Value(fromOffset: offset)) - offset += 2 - - guard controlBuffer.count >= offset + serverOptsLength else { - return false - } - let serverOpts = controlBuffer.withOffset(offset, count: serverOptsLength) - offset += serverOptsLength - - if CoreConfiguration.logsSensitiveData { - log.debug("TLS.auth: Parsed server random: [\(serverRandom1.toHex()), \(serverRandom2.toHex())]") - } else { - log.debug("TLS.auth: Parsed server random") - } - - if let serverOptsString = serverOpts.nullTerminatedString(fromOffset: 0) { - log.debug("TLS.auth: Parsed server opts: \"\(serverOptsString)\"") - } - - self.serverRandom1 = serverRandom1 - self.serverRandom2 = serverRandom2 - controlBuffer.remove(untilOffset: offset) - - return true - } - - func parseMessages() -> [String] { - var messages = [String]() - var offset = 0 - - while true { - guard let msg = controlBuffer.nullTerminatedString(fromOffset: offset) else { - break - } - messages.append(msg) - offset += msg.count + 1 - } - - controlBuffer.remove(untilOffset: offset) - - return messages - } -} diff --git a/TunnelKit/Sources/Core/EncryptionProxy.swift b/TunnelKit/Sources/Core/EncryptionProxy.swift deleted file mode 100644 index fb7b8ed..0000000 --- a/TunnelKit/Sources/Core/EncryptionProxy.swift +++ /dev/null @@ -1,150 +0,0 @@ -// -// EncryptionProxy.swift -// TunnelKit -// -// Created by Davide De Rosa on 2/8/17. -// Copyright © 2018 London Trust Media. All rights reserved. -// - -import Foundation -import __TunnelKitNative - -/// Bridges native encryption for high-level operations. -public class EncryptionProxy { - private static let maxHmacLength = 100 - - private let box: CryptoBox - - /** - Initializes the PRNG. Must be issued before using `SessionProxy`. - - - Parameter seedLength: The length in bytes of the pseudorandom seed that will feed the PRNG. - */ - public static func prepareRandomNumberGenerator(seedLength: Int) -> Bool { - let seed: ZeroingData - do { - seed = try SecureRandom.safeData(length: seedLength) - } catch { - return false - } - return CryptoBox.preparePRNG(withSeed: seed.bytes, length: seed.count) - } - - // Ruby: keys_prf - private static func keysPRF( - _ label: String, - _ secret: ZeroingData, - _ clientSeed: ZeroingData, - _ serverSeed: ZeroingData, - _ clientSessionId: Data?, - _ serverSessionId: Data?, - _ size: Int) throws -> ZeroingData { - - let seed = Z(label) - seed.append(clientSeed) - seed.append(serverSeed) - if let csi = clientSessionId { - seed.append(Z(csi)) - } - if let ssi = serverSessionId { - seed.append(Z(ssi)) - } - let len = secret.count / 2 - let lenx = len + (secret.count & 1) - let secret1 = secret.withOffset(0, count: lenx) - let secret2 = secret.withOffset(len, count: lenx) - - let hash1 = try keysHash("md5", secret1, seed, size) - let hash2 = try keysHash("sha1", secret2, seed, size) - - let prf = Z() - for i in 0.. ZeroingData { - let out = Z() - let buffer = Z(count: EncryptionProxy.maxHmacLength) - var chain = try EncryptionProxy.hmac(buffer, digestName, secret, seed) - while (out.count < size) { - out.append(try EncryptionProxy.hmac(buffer, digestName, secret, chain.appending(seed))) - chain = try EncryptionProxy.hmac(buffer, digestName, secret, chain) - } - return out.withOffset(0, count: size) - } - - // Ruby: hmac - private static func hmac(_ buffer: ZeroingData, _ digestName: String, _ secret: ZeroingData, _ data: ZeroingData) throws -> ZeroingData { - var length = 0 - - try CryptoBox.hmac( - withDigestName: digestName, - secret: secret.bytes, - secretLength: secret.count, - data: data.bytes, - dataLength: data.count, - hmac: buffer.mutableBytes, - hmacLength: &length - ) - - return buffer.withOffset(0, count: length) - } - - convenience init(_ cipher: SessionProxy.Cipher, _ digest: SessionProxy.Digest, _ auth: Authenticator, - _ sessionId: Data, _ remoteSessionId: Data) throws { - - guard let serverRandom1 = auth.serverRandom1, let serverRandom2 = auth.serverRandom2 else { - fatalError("Configuring encryption without server randoms") - } - - let masterData = try EncryptionProxy.keysPRF( - CoreConfiguration.label1, auth.preMaster, auth.random1, - serverRandom1, nil, nil, - CoreConfiguration.preMasterLength - ) - - let keysData = try EncryptionProxy.keysPRF( - CoreConfiguration.label2, masterData, auth.random2, - serverRandom2, sessionId, remoteSessionId, - CoreConfiguration.keysCount * CoreConfiguration.keyLength - ) - - var keysArray = [ZeroingData]() - for i in 0.. DataPathEncrypter { - return box.encrypter().dataPathEncrypter() - } - - func decrypter() -> DataPathDecrypter { - return box.decrypter().dataPathDecrypter() - } -} diff --git a/TunnelKit/Sources/Core/PushReply.swift b/TunnelKit/Sources/Core/PushReply.swift deleted file mode 100644 index ddb248a..0000000 --- a/TunnelKit/Sources/Core/PushReply.swift +++ /dev/null @@ -1,88 +0,0 @@ -// -// PushReply.swift -// TunnelKit -// -// Created by Davide De Rosa on 25/07/2018. -// Copyright © 2018 London Trust Media. All rights reserved. -// - -import Foundation - -struct PushReply { - private static let ifconfigRegexp = try! NSRegularExpression(pattern: "ifconfig [\\d\\.]+ [\\d\\.]+", options: []) - - private static let dnsRegexp = try! NSRegularExpression(pattern: "dhcp-option DNS [\\d\\.]+", options: []) - - private static let authTokenRegexp = try! NSRegularExpression(pattern: "auth-token [a-zA-Z0-9/=+]+", options: []) - - private static let peerIdRegexp = try! NSRegularExpression(pattern: "peer-id [0-9]+", options: []) - - let address: String - - let gatewayAddress: String - - let dnsServers: [String] - - let authToken: String? - - let peerId: UInt32? - - init?(message: String) throws { - guard message.hasPrefix("PUSH_REPLY") else { - return nil - } - - var ifconfigComponents: [String]? - var dnsServers = [String]() - var authToken: String? - var peerId: UInt32? - - PushReply.ifconfigRegexp.enumerateMatches(in: message, options: [], range: NSMakeRange(0, message.count)) { (result, flags, _) in - guard let range = result?.range else { return } - - let match = (message as NSString).substring(with: range) - ifconfigComponents = match.components(separatedBy: " ") - } - - guard let addresses = ifconfigComponents, addresses.count >= 2 else { - throw SessionError.malformedPushReply - } - - PushReply.dnsRegexp.enumerateMatches(in: message, options: [], range: NSMakeRange(0, message.count)) { (result, flags, _) in - guard let range = result?.range else { return } - - let match = (message as NSString).substring(with: range) - let dnsEntryComponents = match.components(separatedBy: " ") - - dnsServers.append(dnsEntryComponents[2]) - } - - PushReply.authTokenRegexp.enumerateMatches(in: message, options: [], range: NSMakeRange(0, message.count)) { (result, flags, _) in - guard let range = result?.range else { return } - - let match = (message as NSString).substring(with: range) - let tokenComponents = match.components(separatedBy: " ") - - if (tokenComponents.count > 1) { - authToken = tokenComponents[1] - } - } - - PushReply.peerIdRegexp.enumerateMatches(in: message, options: [], range: NSMakeRange(0, message.count)) { (result, flags, _) in - guard let range = result?.range else { return } - - let match = (message as NSString).substring(with: range) - let tokenComponents = match.components(separatedBy: " ") - - if (tokenComponents.count > 1) { - peerId = UInt32(tokenComponents[1]) - } - } - - address = addresses[1] - gatewayAddress = addresses[2] - self.dnsServers = dnsServers - self.authToken = authToken - self.peerId = peerId - } -} diff --git a/TunnelKit/Sources/Core/SessionKey.swift b/TunnelKit/Sources/Core/SessionKey.swift deleted file mode 100644 index 5380bf4..0000000 --- a/TunnelKit/Sources/Core/SessionKey.swift +++ /dev/null @@ -1,120 +0,0 @@ -// -// SessionKey.swift -// TunnelKit -// -// Created by Davide De Rosa on 4/12/17. -// Copyright © 2018 London Trust Media. All rights reserved. -// - -import Foundation -import __TunnelKitNative -import SwiftyBeaver - -private let log = SwiftyBeaver.self - -class SessionKey { - enum State { - case invalid, hardReset, softReset, tls - } - - enum ControlState { - case preAuth, preIfConfig, connected - } - - let id: UInt8 // 3-bit - - let startTime: Date - - var state = State.invalid - - var controlState: ControlState? - - var tlsOptional: TLSBox? - - var tls: TLSBox { - guard let tls = tlsOptional else { - fatalError("TLSBox accessed when nil") - } - return tls - } - - var dataPath: DataPath? - - var softReset: Bool - - private var isTLSConnected: Bool - - private var canHandlePackets: Bool - - init(id: UInt8) { - self.id = id - - startTime = Date() - state = .invalid - softReset = false - isTLSConnected = false - canHandlePackets = false - } - - // Ruby: Key.hard_reset_timeout - func didHardResetTimeOut(link: LinkInterface) -> Bool { - return ((state == .hardReset) && (-startTime.timeIntervalSinceNow > link.hardResetTimeout)) - } - - // Ruby: Key.negotiate_timeout - func didNegotiationTimeOut(link: LinkInterface) -> Bool { - let timeout = (softReset ? CoreConfiguration.softNegotiationTimeout : link.negotiationTimeout) - - return ((controlState != .connected) && (-startTime.timeIntervalSinceNow > timeout)) - } - - // Ruby: Key.on_tls_connect - func shouldOnTLSConnect() -> Bool { - guard !isTLSConnected else { - return false - } - if tls.isConnected() { - isTLSConnected = true - } - return isTLSConnected - } - - func startHandlingPackets(withPeerId peerId: UInt32? = nil) { - dataPath?.setPeerId(peerId ?? PacketPeerIdDisabled) - canHandlePackets = true - } - - func encrypt(packets: [Data]) throws -> [Data]? { - guard let dataPath = dataPath else { - log.warning("Data: Set dataPath first") - return nil - } - guard canHandlePackets else { - log.warning("Data: Invoke startHandlingPackets() before encrypting") - return nil - } - return try dataPath.encryptPackets(packets, key: id) - } - - func decrypt(packets: [Data]) throws -> [Data]? { - guard let dataPath = dataPath else { - log.warning("Data: Set dataPath first") - return nil - } - guard canHandlePackets else { - log.warning("Data: Invoke startHandlingPackets() before decrypting") - return nil - } - var keepAlive = false - let decrypted = try dataPath.decryptPackets(packets, keepAlive: &keepAlive) - if keepAlive { - log.debug("Data: Received ping, do nothing") - } - return decrypted - } - -// func dispose() { -// tlsOptional = nil -// dataPath = nil -// } -} diff --git a/TunnelKit/Sources/Core/SessionProxy+Authenticator.swift b/TunnelKit/Sources/Core/SessionProxy+Authenticator.swift new file mode 100644 index 0000000..03de6f8 --- /dev/null +++ b/TunnelKit/Sources/Core/SessionProxy+Authenticator.swift @@ -0,0 +1,152 @@ +// +// SessionProxy+Authenticator.swift +// TunnelKit +// +// Created by Davide De Rosa on 2/9/17. +// Copyright © 2018 London Trust Media. All rights reserved. +// + +import Foundation +import SwiftyBeaver +import __TunnelKitNative + +private let log = SwiftyBeaver.self + +fileprivate extension ZeroingData { + fileprivate func appendSized(_ buf: ZeroingData) { + append(Z(UInt16(buf.count).bigEndian)) + append(buf) + } +} + +extension SessionProxy { + class Authenticator { + private var controlBuffer: ZeroingData + + private(set) var preMaster: ZeroingData + + private(set) var random1: ZeroingData + + private(set) var random2: ZeroingData + + private(set) var serverRandom1: ZeroingData? + + private(set) var serverRandom2: ZeroingData? + + let username: ZeroingData + + let password: ZeroingData + + init(_ username: String, _ password: String) throws { + preMaster = try SecureRandom.safeData(length: CoreConfiguration.preMasterLength) + random1 = try SecureRandom.safeData(length: CoreConfiguration.randomLength) + random2 = try SecureRandom.safeData(length: CoreConfiguration.randomLength) + + // XXX: not 100% secure, can't erase input username/password + self.username = Z(username, nullTerminated: true) + self.password = Z(password, nullTerminated: true) + + controlBuffer = Z() + } + + // MARK: Authentication request + + // Ruby: on_tls_connect + func putAuth(into: TLSBox) throws { + let raw = Z(ProtocolMacros.tlsPrefix) + + // local keys + raw.append(preMaster) + raw.append(random1) + raw.append(random2) + + // opts + raw.appendSized(Z(UInt8(0))) + + // credentials + raw.appendSized(username) + raw.appendSized(password) + + // peer info + raw.appendSized(Z(CoreConfiguration.peerInfo)) + + if CoreConfiguration.logsSensitiveData { + log.debug("TLS.auth: Put plaintext (\(raw.count) bytes): \(raw.toHex())") + } else { + log.debug("TLS.auth: Put plaintext (\(raw.count) bytes)") + } + + try into.putRawPlainText(raw.bytes, length: raw.count) + } + + // MARK: Server replies + + func appendControlData(_ data: ZeroingData) { + controlBuffer.append(data) + } + + func parseAuthReply() throws -> Bool { + let prefixLength = ProtocolMacros.tlsPrefix.count + + // TLS prefix + random (x2) + opts length [+ opts] + guard (controlBuffer.count >= prefixLength + 2 * CoreConfiguration.randomLength + 2) else { + return false + } + + let prefix = controlBuffer.withOffset(0, count: prefixLength) + guard prefix.isEqual(to: ProtocolMacros.tlsPrefix) else { + throw SessionError.wrongControlDataPrefix + } + + var offset = ProtocolMacros.tlsPrefix.count + + let serverRandom1 = controlBuffer.withOffset(offset, count: CoreConfiguration.randomLength) + offset += CoreConfiguration.randomLength + + let serverRandom2 = controlBuffer.withOffset(offset, count: CoreConfiguration.randomLength) + offset += CoreConfiguration.randomLength + + let serverOptsLength = Int(controlBuffer.networkUInt16Value(fromOffset: offset)) + offset += 2 + + guard controlBuffer.count >= offset + serverOptsLength else { + return false + } + let serverOpts = controlBuffer.withOffset(offset, count: serverOptsLength) + offset += serverOptsLength + + if CoreConfiguration.logsSensitiveData { + log.debug("TLS.auth: Parsed server random: [\(serverRandom1.toHex()), \(serverRandom2.toHex())]") + } else { + log.debug("TLS.auth: Parsed server random") + } + + if let serverOptsString = serverOpts.nullTerminatedString(fromOffset: 0) { + log.debug("TLS.auth: Parsed server opts: \"\(serverOptsString)\"") + } + + self.serverRandom1 = serverRandom1 + self.serverRandom2 = serverRandom2 + controlBuffer.remove(untilOffset: offset) + + return true + } + + func parseMessages() -> [String] { + var messages = [String]() + var offset = 0 + + while true { + guard let msg = controlBuffer.nullTerminatedString(fromOffset: offset) else { + break + } + messages.append(msg) + offset += msg.count + 1 + } + + controlBuffer.remove(untilOffset: offset) + + return messages + } + } +} diff --git a/TunnelKit/Sources/Core/SessionProxy+EncryptionBridge.swift b/TunnelKit/Sources/Core/SessionProxy+EncryptionBridge.swift new file mode 100644 index 0000000..dde9aa0 --- /dev/null +++ b/TunnelKit/Sources/Core/SessionProxy+EncryptionBridge.swift @@ -0,0 +1,153 @@ +// +// SessionProxy+EncryptionBridge.swift +// TunnelKit +// +// Created by Davide De Rosa on 2/8/17. +// Copyright © 2018 London Trust Media. All rights reserved. +// + +import Foundation +import __TunnelKitNative + +extension SessionProxy { + + /// Bridges native encryption for high-level operations. + public class EncryptionBridge { + private static let maxHmacLength = 100 + + private let box: CryptoBox + + /** + Initializes the PRNG. Must be issued before using `SessionProxy`. + + - Parameter seedLength: The length in bytes of the pseudorandom seed that will feed the PRNG. + */ + public static func prepareRandomNumberGenerator(seedLength: Int) -> Bool { + let seed: ZeroingData + do { + seed = try SecureRandom.safeData(length: seedLength) + } catch { + return false + } + return CryptoBox.preparePRNG(withSeed: seed.bytes, length: seed.count) + } + + // Ruby: keys_prf + private static func keysPRF( + _ label: String, + _ secret: ZeroingData, + _ clientSeed: ZeroingData, + _ serverSeed: ZeroingData, + _ clientSessionId: Data?, + _ serverSessionId: Data?, + _ size: Int) throws -> ZeroingData { + + let seed = Z(label) + seed.append(clientSeed) + seed.append(serverSeed) + if let csi = clientSessionId { + seed.append(Z(csi)) + } + if let ssi = serverSessionId { + seed.append(Z(ssi)) + } + let len = secret.count / 2 + let lenx = len + (secret.count & 1) + let secret1 = secret.withOffset(0, count: lenx) + let secret2 = secret.withOffset(len, count: lenx) + + let hash1 = try keysHash("md5", secret1, seed, size) + let hash2 = try keysHash("sha1", secret2, seed, size) + + let prf = Z() + for i in 0.. ZeroingData { + let out = Z() + let buffer = Z(count: EncryptionBridge.maxHmacLength) + var chain = try EncryptionBridge.hmac(buffer, digestName, secret, seed) + while (out.count < size) { + out.append(try EncryptionBridge.hmac(buffer, digestName, secret, chain.appending(seed))) + chain = try EncryptionBridge.hmac(buffer, digestName, secret, chain) + } + return out.withOffset(0, count: size) + } + + // Ruby: hmac + private static func hmac(_ buffer: ZeroingData, _ digestName: String, _ secret: ZeroingData, _ data: ZeroingData) throws -> ZeroingData { + var length = 0 + + try CryptoBox.hmac( + withDigestName: digestName, + secret: secret.bytes, + secretLength: secret.count, + data: data.bytes, + dataLength: data.count, + hmac: buffer.mutableBytes, + hmacLength: &length + ) + + return buffer.withOffset(0, count: length) + } + + convenience init(_ cipher: SessionProxy.Cipher, _ digest: SessionProxy.Digest, _ auth: SessionProxy.Authenticator, + _ sessionId: Data, _ remoteSessionId: Data) throws { + + guard let serverRandom1 = auth.serverRandom1, let serverRandom2 = auth.serverRandom2 else { + fatalError("Configuring encryption without server randoms") + } + + let masterData = try EncryptionBridge.keysPRF( + CoreConfiguration.label1, auth.preMaster, auth.random1, + serverRandom1, nil, nil, + CoreConfiguration.preMasterLength + ) + + let keysData = try EncryptionBridge.keysPRF( + CoreConfiguration.label2, masterData, auth.random2, + serverRandom2, sessionId, remoteSessionId, + CoreConfiguration.keysCount * CoreConfiguration.keyLength + ) + + var keysArray = [ZeroingData]() + for i in 0.. DataPathEncrypter { + return box.encrypter().dataPathEncrypter() + } + + func decrypter() -> DataPathDecrypter { + return box.decrypter().dataPathDecrypter() + } + } +} diff --git a/TunnelKit/Sources/Core/SessionProxy+PushReply.swift b/TunnelKit/Sources/Core/SessionProxy+PushReply.swift new file mode 100644 index 0000000..ef7617e --- /dev/null +++ b/TunnelKit/Sources/Core/SessionProxy+PushReply.swift @@ -0,0 +1,90 @@ +// +// SessionProxy+PushReply.swift +// TunnelKit +// +// Created by Davide De Rosa on 25/07/2018. +// Copyright © 2018 London Trust Media. All rights reserved. +// + +import Foundation + +extension SessionProxy { + struct PushReply { + private static let ifconfigRegexp = try! NSRegularExpression(pattern: "ifconfig [\\d\\.]+ [\\d\\.]+", options: []) + + private static let dnsRegexp = try! NSRegularExpression(pattern: "dhcp-option DNS [\\d\\.]+", options: []) + + private static let authTokenRegexp = try! NSRegularExpression(pattern: "auth-token [a-zA-Z0-9/=+]+", options: []) + + private static let peerIdRegexp = try! NSRegularExpression(pattern: "peer-id [0-9]+", options: []) + + let address: String + + let gatewayAddress: String + + let dnsServers: [String] + + let authToken: String? + + let peerId: UInt32? + + init?(message: String) throws { + guard message.hasPrefix("PUSH_REPLY") else { + return nil + } + + var ifconfigComponents: [String]? + var dnsServers = [String]() + var authToken: String? + var peerId: UInt32? + + PushReply.ifconfigRegexp.enumerateMatches(in: message, options: [], range: NSMakeRange(0, message.count)) { (result, flags, _) in + guard let range = result?.range else { return } + + let match = (message as NSString).substring(with: range) + ifconfigComponents = match.components(separatedBy: " ") + } + + guard let addresses = ifconfigComponents, addresses.count >= 2 else { + throw SessionError.malformedPushReply + } + + PushReply.dnsRegexp.enumerateMatches(in: message, options: [], range: NSMakeRange(0, message.count)) { (result, flags, _) in + guard let range = result?.range else { return } + + let match = (message as NSString).substring(with: range) + let dnsEntryComponents = match.components(separatedBy: " ") + + dnsServers.append(dnsEntryComponents[2]) + } + + PushReply.authTokenRegexp.enumerateMatches(in: message, options: [], range: NSMakeRange(0, message.count)) { (result, flags, _) in + guard let range = result?.range else { return } + + let match = (message as NSString).substring(with: range) + let tokenComponents = match.components(separatedBy: " ") + + if (tokenComponents.count > 1) { + authToken = tokenComponents[1] + } + } + + PushReply.peerIdRegexp.enumerateMatches(in: message, options: [], range: NSMakeRange(0, message.count)) { (result, flags, _) in + guard let range = result?.range else { return } + + let match = (message as NSString).substring(with: range) + let tokenComponents = match.components(separatedBy: " ") + + if (tokenComponents.count > 1) { + peerId = UInt32(tokenComponents[1]) + } + } + + address = addresses[1] + gatewayAddress = addresses[2] + self.dnsServers = dnsServers + self.authToken = authToken + self.peerId = peerId + } + } +} diff --git a/TunnelKit/Sources/Core/SessionProxy+SessionKey.swift b/TunnelKit/Sources/Core/SessionProxy+SessionKey.swift new file mode 100644 index 0000000..1e48370 --- /dev/null +++ b/TunnelKit/Sources/Core/SessionProxy+SessionKey.swift @@ -0,0 +1,122 @@ +// +// SessionProxy+SessionKey.swift +// TunnelKit +// +// Created by Davide De Rosa on 4/12/17. +// Copyright © 2018 London Trust Media. All rights reserved. +// + +import Foundation +import __TunnelKitNative +import SwiftyBeaver + +private let log = SwiftyBeaver.self + +extension SessionProxy { + class SessionKey { + enum State { + case invalid, hardReset, softReset, tls + } + + enum ControlState { + case preAuth, preIfConfig, connected + } + + let id: UInt8 // 3-bit + + let startTime: Date + + var state = State.invalid + + var controlState: ControlState? + + var tlsOptional: TLSBox? + + var tls: TLSBox { + guard let tls = tlsOptional else { + fatalError("TLSBox accessed when nil") + } + return tls + } + + var dataPath: DataPath? + + var softReset: Bool + + private var isTLSConnected: Bool + + private var canHandlePackets: Bool + + init(id: UInt8) { + self.id = id + + startTime = Date() + state = .invalid + softReset = false + isTLSConnected = false + canHandlePackets = false + } + + // Ruby: Key.hard_reset_timeout + func didHardResetTimeOut(link: LinkInterface) -> Bool { + return ((state == .hardReset) && (-startTime.timeIntervalSinceNow > link.hardResetTimeout)) + } + + // Ruby: Key.negotiate_timeout + func didNegotiationTimeOut(link: LinkInterface) -> Bool { + let timeout = (softReset ? CoreConfiguration.softNegotiationTimeout : link.negotiationTimeout) + + return ((controlState != .connected) && (-startTime.timeIntervalSinceNow > timeout)) + } + + // Ruby: Key.on_tls_connect + func shouldOnTLSConnect() -> Bool { + guard !isTLSConnected else { + return false + } + if tls.isConnected() { + isTLSConnected = true + } + return isTLSConnected + } + + func startHandlingPackets(withPeerId peerId: UInt32? = nil) { + dataPath?.setPeerId(peerId ?? PacketPeerIdDisabled) + canHandlePackets = true + } + + func encrypt(packets: [Data]) throws -> [Data]? { + guard let dataPath = dataPath else { + log.warning("Data: Set dataPath first") + return nil + } + guard canHandlePackets else { + log.warning("Data: Invoke startHandlingPackets() before encrypting") + return nil + } + return try dataPath.encryptPackets(packets, key: id) + } + + func decrypt(packets: [Data]) throws -> [Data]? { + guard let dataPath = dataPath else { + log.warning("Data: Set dataPath first") + return nil + } + guard canHandlePackets else { + log.warning("Data: Invoke startHandlingPackets() before decrypting") + return nil + } + var keepAlive = false + let decrypted = try dataPath.decryptPackets(packets, keepAlive: &keepAlive) + if keepAlive { + log.debug("Data: Received ping, do nothing") + } + return decrypted + } + + // func dispose() { + // tlsOptional = nil + // dataPath = nil + // } + } +} diff --git a/TunnelKit/Sources/Core/SessionProxy.swift b/TunnelKit/Sources/Core/SessionProxy.swift index 6300a25..604dec5 100644 --- a/TunnelKit/Sources/Core/SessionProxy.swift +++ b/TunnelKit/Sources/Core/SessionProxy.swift @@ -1008,9 +1008,9 @@ public class SessionProxy { log.debug("Setup keys") } - let proxy: EncryptionProxy + let bridge: EncryptionBridge do { - proxy = try EncryptionProxy( + bridge = try EncryptionBridge( configuration.cipher, configuration.digest, auth, @@ -1023,8 +1023,8 @@ public class SessionProxy { } negotiationKey.dataPath = DataPath( - encrypter: proxy.encrypter(), - decrypter: proxy.decrypter(), + encrypter: bridge.encrypter(), + decrypter: bridge.decrypter(), maxPackets: link?.packetBufferSize ?? 200, usesReplayProtection: CoreConfiguration.usesReplayProtection ) diff --git a/TunnelKitTests/DataPathPerformanceTests.swift b/TunnelKitTests/DataPathPerformanceTests.swift index 664077d..9e23aba 100644 --- a/TunnelKitTests/DataPathPerformanceTests.swift +++ b/TunnelKitTests/DataPathPerformanceTests.swift @@ -21,7 +21,7 @@ class DataPathPerformanceTests: XCTestCase { let ck = try! SecureRandom.safeData(length: 32) let hk = try! SecureRandom.safeData(length: 32) - let crypto = try! EncryptionProxy(.aes128cbc, .sha1, ck, ck, hk, hk) + let crypto = try! SessionProxy.EncryptionBridge(.aes128cbc, .sha1, ck, ck, hk, hk) encrypter = crypto.encrypter() decrypter = crypto.decrypter() From fe7a2c694136f07dec1e2e3f16cbdebd5337dcab Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Thu, 23 Aug 2018 16:18:53 +0200 Subject: [PATCH 4/5] Drop a few old commented lines --- TunnelKit/Sources/Core/CoreConfiguration.swift | 2 -- TunnelKit/Sources/Core/SessionProxy+SessionKey.swift | 5 ----- TunnelKit/Sources/Core/SessionProxy.swift | 1 - 3 files changed, 8 deletions(-) diff --git a/TunnelKit/Sources/Core/CoreConfiguration.swift b/TunnelKit/Sources/Core/CoreConfiguration.swift index d449be4..eec694c 100644 --- a/TunnelKit/Sources/Core/CoreConfiguration.swift +++ b/TunnelKit/Sources/Core/CoreConfiguration.swift @@ -16,8 +16,6 @@ struct CoreConfiguration { static let usesReplayProtection = true -// static let usesDataOptimization = true - static let tickInterval = 0.2 static let pingInterval = 10.0 diff --git a/TunnelKit/Sources/Core/SessionProxy+SessionKey.swift b/TunnelKit/Sources/Core/SessionProxy+SessionKey.swift index 1e48370..4af525e 100644 --- a/TunnelKit/Sources/Core/SessionProxy+SessionKey.swift +++ b/TunnelKit/Sources/Core/SessionProxy+SessionKey.swift @@ -113,10 +113,5 @@ extension SessionProxy { } return decrypted } - - // func dispose() { - // tlsOptional = nil - // dataPath = nil - // } } } diff --git a/TunnelKit/Sources/Core/SessionProxy.swift b/TunnelKit/Sources/Core/SessionProxy.swift index 604dec5..d0392ea 100644 --- a/TunnelKit/Sources/Core/SessionProxy.swift +++ b/TunnelKit/Sources/Core/SessionProxy.swift @@ -899,7 +899,6 @@ public class SessionProxy { while (oldKeys.count > 1) { let key = oldKeys.removeFirst() keys.removeValue(forKey: key.id) -// key.dispose() } } From 5a46dd76089cde784041622893d2e48e64e5cb19 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Thu, 23 Aug 2018 16:27:37 +0200 Subject: [PATCH 5/5] Update Demo to new refactoring --- Demo/Podfile.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Demo/Podfile.lock b/Demo/Podfile.lock index 62ba946..1dbf718 100644 --- a/Demo/Podfile.lock +++ b/Demo/Podfile.lock @@ -1,13 +1,13 @@ PODS: - OpenSSL-Apple (1.1.0h) - SwiftyBeaver (1.6.0) - - TunnelKit (1.0.0): - - TunnelKit/AppExtension (= 1.0.0) - - TunnelKit/Core (= 1.0.0) - - TunnelKit/AppExtension (1.0.0): + - TunnelKit (1.1.0): + - TunnelKit/AppExtension (= 1.1.0) + - TunnelKit/Core (= 1.1.0) + - TunnelKit/AppExtension (1.1.0): - SwiftyBeaver - TunnelKit/Core - - TunnelKit/Core (1.0.0): + - TunnelKit/Core (1.1.0): - OpenSSL-Apple (~> 1.1.0h) - SwiftyBeaver @@ -26,7 +26,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: OpenSSL-Apple: cd153d705ef350eb834ae7ff5f21f792b51ed208 SwiftyBeaver: e45759613e50b522b0e6f53b1f0f14389b45ca34 - TunnelKit: 79e018dba82d9a2335daf5c55c473527cff52b73 + TunnelKit: 4c10192ab76e9b03bf5bde5b70f38e38ae6d4568 PODFILE CHECKSUM: dbd445ce3249028c60f04276181924bb9bfc726d