From 5c8c361fceae26d0f1d5b54f33c730e3d3e446f7 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Tue, 11 Sep 2018 00:06:38 +0200 Subject: [PATCH 1/4] Add StaticKey class for static OpenVPN keys --- .jazzy.yaml | 1 + TunnelKit.xcodeproj/project.pbxproj | 6 ++ TunnelKit/Sources/Core/StaticKey.swift | 109 +++++++++++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 TunnelKit/Sources/Core/StaticKey.swift diff --git a/.jazzy.yaml b/.jazzy.yaml index c96fb76..423a0de 100644 --- a/.jazzy.yaml +++ b/.jazzy.yaml @@ -19,6 +19,7 @@ custom_categories: - TunnelInterface - PacketStream - BidirectionalState + - StaticKey - SessionProxy - SessionProxyDelegate - SessionReply diff --git a/TunnelKit.xcodeproj/project.pbxproj b/TunnelKit.xcodeproj/project.pbxproj index 0b79270..71c6f76 100644 --- a/TunnelKit.xcodeproj/project.pbxproj +++ b/TunnelKit.xcodeproj/project.pbxproj @@ -78,6 +78,8 @@ 0ECE352A212EB88E0040F253 /* CryptoContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECE3527212EB7770040F253 /* CryptoContainer.swift */; }; 0ED9C8642138139000621BA3 /* SessionProxy+CompressionFraming.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED9C8632138139000621BA3 /* SessionProxy+CompressionFraming.swift */; }; 0ED9C8652138139000621BA3 /* SessionProxy+CompressionFraming.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED9C8632138139000621BA3 /* SessionProxy+CompressionFraming.swift */; }; + 0EE3B3E421471C3A0027AB17 /* StaticKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EE3B3E321471C3A0027AB17 /* StaticKey.swift */; }; + 0EE3B3E521471C3A0027AB17 /* StaticKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EE3B3E321471C3A0027AB17 /* StaticKey.swift */; }; 0EE7A79820F6296F00B42E6A /* PacketMacros.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EE7A79720F6296F00B42E6A /* PacketMacros.m */; }; 0EE7A79920F6296F00B42E6A /* PacketMacros.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EE7A79720F6296F00B42E6A /* PacketMacros.m */; }; 0EE7A7A120F664AC00B42E6A /* DataPathEncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EE7A7A020F664AB00B42E6A /* DataPathEncryptionTests.swift */; }; @@ -239,6 +241,7 @@ 0EC1BBA720D7D803007C4C7B /* ConnectionStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionStrategy.swift; sourceTree = ""; }; 0ECE3527212EB7770040F253 /* CryptoContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CryptoContainer.swift; sourceTree = ""; }; 0ED9C8632138139000621BA3 /* SessionProxy+CompressionFraming.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionProxy+CompressionFraming.swift"; sourceTree = ""; }; + 0EE3B3E321471C3A0027AB17 /* StaticKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticKey.swift; sourceTree = ""; }; 0EE7A79420F61EDC00B42E6A /* PacketMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PacketMacros.h; sourceTree = ""; }; 0EE7A79720F6296F00B42E6A /* PacketMacros.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PacketMacros.m; sourceTree = ""; }; 0EE7A79D20F6488400B42E6A /* DataPathCrypto.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DataPathCrypto.h; sourceTree = ""; }; @@ -481,6 +484,7 @@ 0E749F5E2178885500BB2701 /* SessionProxy+PIA.swift */, 0E3E0F202108A8CC00B371C1 /* SessionProxy+PushReply.swift */, 0EFEB42B2006D3C800F81029 /* SessionProxy+SessionKey.swift */, + 0EE3B3E321471C3A0027AB17 /* StaticKey.swift */, 0EFEB4442006D3C800F81029 /* TLSBox.h */, 0EFEB4302006D3C800F81029 /* TLSBox.m */, 0EFEB42F2006D3C800F81029 /* TunnelInterface.swift */, @@ -879,6 +883,7 @@ 0EFEB4AE2007625E00F81029 /* Keychain.swift in Sources */, 0E749F5F2178885500BB2701 /* SessionProxy+PIA.swift in Sources */, 0EBBF3002085196000E36B40 /* NWTCPConnectionState+Description.swift in Sources */, + 0EE3B3E421471C3A0027AB17 /* StaticKey.swift in Sources */, 0EFEB4622006D3C800F81029 /* SecureRandom.swift in Sources */, 0EFEB45D2006D3C800F81029 /* CryptoBox.m in Sources */, 0EBBF2FA2085061600E36B40 /* NETCPInterface.swift in Sources */, @@ -935,6 +940,7 @@ 0EFEB4A02006D7F300F81029 /* ReplayProtector.m in Sources */, 0E749F602178885500BB2701 /* SessionProxy+PIA.swift in Sources */, 0EFEB4992006D7F300F81029 /* SessionProxy.swift in Sources */, + 0EE3B3E521471C3A0027AB17 /* StaticKey.swift in Sources */, 0EBBF3012085196000E36B40 /* NWTCPConnectionState+Description.swift in Sources */, 0EFEB4962006D7F300F81029 /* ProtocolMacros.swift in Sources */, 0EFEB48A2006D7C400F81029 /* TunnelKitProvider.swift in Sources */, diff --git a/TunnelKit/Sources/Core/StaticKey.swift b/TunnelKit/Sources/Core/StaticKey.swift new file mode 100644 index 0000000..920e781 --- /dev/null +++ b/TunnelKit/Sources/Core/StaticKey.swift @@ -0,0 +1,109 @@ +// +// StaticKey.swift +// TunnelKit +// +// Created by Davide De Rosa on 9/10/18. +// Copyright (c) 2018 Davide De Rosa. All rights reserved. +// +// https://github.com/keeshux +// +// This file is part of TunnelKit. +// +// TunnelKit is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// TunnelKit is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with TunnelKit. If not, see . +// + +import Foundation +import __TunnelKitNative + +/// Represents an OpenVPN static key file (as generated with --genkey) +public class StaticKey: Codable { + enum CodingKeys: CodingKey { + case data + + case dir + } + + /// The key-direction field, usually 0 on servers and 1 on clients. + public enum Direction: Int, Codable { + + /// Conventional server direction. + case server = 0 + + /// Conventional client direction. + case client = 1 + } + + private static let contentLength = 256 // 2048-bit + + private static let keyCount = 4 + + private static let keyLength = StaticKey.contentLength / StaticKey.keyCount + + private let secureData: ZeroingData + + private let direction: Direction? + + /** + Initializes with data and direction. + + - Parameter data: The key data. + - Parameter direction: The key direction, or bidirectional if nil. + */ + public init(data: Data, direction: Direction?) { + precondition(data.count == StaticKey.contentLength) + secureData = Z(data) + self.direction = direction + } + + /** + Initializes as bidirectional. + + - Parameter biData: The key data. + */ + public convenience init(biData data: Data) { + self.init(data: data, direction: nil) + } + + private func key(at: Int) -> ZeroingData { + let size = secureData.count / StaticKey.keyCount // 64 bytes each + assert(size == StaticKey.keyLength) + return secureData.withOffset(at * size, count: size) + } + + /// :nodoc: + public static func deserialized(_ data: Data) throws -> StaticKey { + return try JSONDecoder().decode(StaticKey.self, from: data) + } + + /// :nodoc: + public func serialized() -> Data? { + return try? JSONEncoder().encode(self) + } + + // MARK: Codable + + /// :nodoc: + public required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + secureData = Z(try container.decode(Data.self, forKey: .data)) + direction = try container.decodeIfPresent(Direction.self, forKey: .dir) + } + + /// :nodoc: + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(secureData.toData(), forKey: .data) + try container.encodeIfPresent(direction, forKey: .dir) + } +} From 372fa194a5e88182c59be7882653e03c3ff39f52 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Tue, 18 Sep 2018 16:33:48 +0200 Subject: [PATCH 2/4] Parse indexed keys from StaticKey --- TunnelKit/Sources/Core/StaticKey.swift | 72 ++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/TunnelKit/Sources/Core/StaticKey.swift b/TunnelKit/Sources/Core/StaticKey.swift index 920e781..5abacd6 100644 --- a/TunnelKit/Sources/Core/StaticKey.swift +++ b/TunnelKit/Sources/Core/StaticKey.swift @@ -37,10 +37,10 @@ public class StaticKey: Codable { /// The key-direction field, usually 0 on servers and 1 on clients. public enum Direction: Int, Codable { - /// Conventional server direction. + /// Conventional server direction (implicit for tls-crypt). case server = 0 - /// Conventional client direction. + /// Conventional client direction (implicit for tls-crypt). case client = 1 } @@ -54,11 +54,77 @@ public class StaticKey: Codable { private let direction: Direction? + /// Returns the encryption key. + /// + /// - Precondition: `direction` must be non-nil. + /// - Seealso: `SessionProxy.ConfigurationBuilder.tlsWrap` + public var cipherEncryptKey: ZeroingData { + guard let direction = direction else { + preconditionFailure() + } + switch direction { + case .server: + return key(at: 0) + + case .client: + return key(at: 2) + } + } + + /// Returns the decryption key. + /// + /// - Precondition: `direction` must be non-nil. + /// - Seealso: `SessionProxy.ConfigurationBuilder.tlsWrap` + public var cipherDecryptKey: ZeroingData { + guard let direction = direction else { + preconditionFailure() + } + switch direction { + case .server: + return key(at: 2) + + case .client: + return key(at: 0) + } + } + + /// Returns the HMAC sending key. + /// + /// - Seealso: `SessionProxy.ConfigurationBuilder.tlsWrap` + public var hmacSendKey: ZeroingData { + guard let direction = direction else { + return key(at: 1) + } + switch direction { + case .server: + return key(at: 1) + + case .client: + return key(at: 3) + } + } + + /// Returns the HMAC receiving key. + /// + /// - Seealso: `SessionProxy.ConfigurationBuilder.tlsWrap` + public var hmacReceiveKey: ZeroingData { + guard let direction = direction else { + return key(at: 1) + } + switch direction { + case .server: + return key(at: 3) + + case .client: + return key(at: 1) + } + } + /** Initializes with data and direction. - Parameter data: The key data. - - Parameter direction: The key direction, or bidirectional if nil. + - Parameter direction: The key direction, or bidirectional if nil. For tls-crypt behavior, must not be nil. */ public init(data: Data, direction: Direction?) { precondition(data.count == StaticKey.contentLength) From 51720c1fbceeaeba18582af8a6c0f6658ad25ac3 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Thu, 20 Sep 2018 11:05:08 +0200 Subject: [PATCH 3/4] Split ControlPacket header/content serialization rawSerializeTo: does not include opcode|session_id. --- TunnelKit/Sources/Core/ControlPacket.h | 4 ++-- TunnelKit/Sources/Core/ControlPacket.m | 17 +++++++++++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/TunnelKit/Sources/Core/ControlPacket.h b/TunnelKit/Sources/Core/ControlPacket.h index 7e19f81..ee70101 100644 --- a/TunnelKit/Sources/Core/ControlPacket.h +++ b/TunnelKit/Sources/Core/ControlPacket.h @@ -52,8 +52,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong, readonly) NSData *_Nullable payload; @property (nonatomic, strong) NSDate *_Nullable sentDate; -- (NSInteger)capacity; -- (NSInteger)serializeTo:(uint8_t *)to; +//- (NSInteger)capacity; +//- (NSInteger)serializeTo:(uint8_t *)to; - (NSData *)serialized; @end diff --git a/TunnelKit/Sources/Core/ControlPacket.m b/TunnelKit/Sources/Core/ControlPacket.m index bcacc0f..626ce6e 100644 --- a/TunnelKit/Sources/Core/ControlPacket.m +++ b/TunnelKit/Sources/Core/ControlPacket.m @@ -75,13 +75,12 @@ return (self.packetId == UINT32_MAX); } -- (NSInteger)capacity +- (NSInteger)rawCapacity { const BOOL isAck = self.isAck; const NSUInteger ackLength = self.ackIds.count; NSCAssert(!isAck || ackLength > 0, @"Ack packet must provide positive ackLength"); - NSInteger n = PacketOpcodeLength + PacketSessionIdLength; - n += PacketAckLengthLength; + NSInteger n = PacketAckLengthLength; if (ackLength > 0) { n += ackLength * PacketIdLength + PacketSessionIdLength; } @@ -93,10 +92,9 @@ } // Ruby: send_ctrl -- (NSInteger)serializeTo:(uint8_t *)to +- (NSInteger)rawSerializeTo:(uint8_t *)to { uint8_t *ptr = to; - ptr += PacketHeaderSet(ptr, self.code, self.key, self.sessionId.bytes); if (self.ackIds.count > 0) { NSCParameterAssert(self.ackRemoteSessionId.length == PacketSessionIdLength); *ptr = self.ackIds.count; @@ -124,10 +122,17 @@ return ptr - to; } +- (NSInteger)capacity +{ + return PacketOpcodeLength + PacketSessionIdLength + self.rawCapacity; +} + - (NSData *)serialized { NSMutableData *data = [[NSMutableData alloc] initWithLength:self.capacity]; - [self serializeTo:data.mutableBytes]; + uint8_t *ptr = data.mutableBytes; + ptr += PacketHeaderSet(ptr, self.code, self.key, self.sessionId.bytes); + [self rawSerializeTo:ptr]; return data; } From 66735ec118e6bf65ba032026d522898964c51103 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Wed, 19 Sep 2018 22:22:35 +0200 Subject: [PATCH 4/4] Prepare API to enable TLS wrapping Extensible TLSWrap parameter. --- TunnelKit.xcodeproj/project.pbxproj | 6 ++ .../TunnelKitProvider+Configuration.swift | 27 +++++++++ .../AppExtension/TunnelKitProvider.swift | 1 + .../Core/SessionProxy+Configuration.swift | 8 +++ .../Sources/Core/SessionProxy+TLSWrap.swift | 60 +++++++++++++++++++ TunnelKit/Sources/Core/SessionProxy.swift | 11 +++- 6 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 TunnelKit/Sources/Core/SessionProxy+TLSWrap.swift diff --git a/TunnelKit.xcodeproj/project.pbxproj b/TunnelKit.xcodeproj/project.pbxproj index 71c6f76..2dbb3a3 100644 --- a/TunnelKit.xcodeproj/project.pbxproj +++ b/TunnelKit.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 0E041D092152E6FE0025FE3C /* SessionProxy+TLSWrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E041D082152E6FE0025FE3C /* SessionProxy+TLSWrap.swift */; }; + 0E041D0A2152E6FE0025FE3C /* SessionProxy+TLSWrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E041D082152E6FE0025FE3C /* SessionProxy+TLSWrap.swift */; }; 0E07595F20EF6D1400F38FD8 /* CryptoCBC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E07595C20EF6D1400F38FD8 /* CryptoCBC.m */; }; 0E07596020EF6D1400F38FD8 /* CryptoCBC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E07595C20EF6D1400F38FD8 /* CryptoCBC.m */; }; 0E07596320EF733F00F38FD8 /* CryptoMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E07596120EF733F00F38FD8 /* CryptoMacros.h */; }; @@ -188,6 +190,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 0E041D082152E6FE0025FE3C /* SessionProxy+TLSWrap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SessionProxy+TLSWrap.swift"; sourceTree = ""; }; 0E07595C20EF6D1400F38FD8 /* CryptoCBC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CryptoCBC.m; sourceTree = ""; }; 0E07596120EF733F00F38FD8 /* CryptoMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptoMacros.h; sourceTree = ""; }; 0E07596A20EF79AB00F38FD8 /* Crypto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Crypto.h; sourceTree = ""; }; @@ -484,6 +487,7 @@ 0E749F5E2178885500BB2701 /* SessionProxy+PIA.swift */, 0E3E0F202108A8CC00B371C1 /* SessionProxy+PushReply.swift */, 0EFEB42B2006D3C800F81029 /* SessionProxy+SessionKey.swift */, + 0E041D082152E6FE0025FE3C /* SessionProxy+TLSWrap.swift */, 0EE3B3E321471C3A0027AB17 /* StaticKey.swift */, 0EFEB4442006D3C800F81029 /* TLSBox.h */, 0EFEB4302006D3C800F81029 /* TLSBox.m */, @@ -909,6 +913,7 @@ 0EFEB45B2006D3C800F81029 /* TLSBox.m in Sources */, 0EFEB4792006D3C800F81029 /* TunnelKitProvider+Interaction.swift in Sources */, 0EFEB4702006D3C800F81029 /* Allocation.m in Sources */, + 0E041D092152E6FE0025FE3C /* SessionProxy+TLSWrap.swift in Sources */, 0EFEB4672006D3C800F81029 /* SessionProxy.swift in Sources */, 0ED9C8642138139000621BA3 /* SessionProxy+CompressionFraming.swift in Sources */, 0EFEB4722006D3C800F81029 /* ReplayProtector.m in Sources */, @@ -966,6 +971,7 @@ 0EFEB4902006D7F300F81029 /* TunnelInterface.swift in Sources */, 0EFEB49E2006D7F300F81029 /* Allocation.m in Sources */, 0EFEB4B02007627700F81029 /* Keychain.swift in Sources */, + 0E041D0A2152E6FE0025FE3C /* SessionProxy+TLSWrap.swift in Sources */, 0EFEB48E2006D7F300F81029 /* SessionProxy+SessionKey.swift in Sources */, 0ED9C8652138139000621BA3 /* SessionProxy+CompressionFraming.swift in Sources */, 0EFEB4AF2007627700F81029 /* InterfaceObserver.swift in Sources */, diff --git a/TunnelKit/Sources/AppExtension/TunnelKitProvider+Configuration.swift b/TunnelKit/Sources/AppExtension/TunnelKitProvider+Configuration.swift index e8a4d4e..889cf0e 100644 --- a/TunnelKit/Sources/AppExtension/TunnelKitProvider+Configuration.swift +++ b/TunnelKit/Sources/AppExtension/TunnelKitProvider+Configuration.swift @@ -140,6 +140,9 @@ extension TunnelKitProvider { /// Sets compression framing, disabled by default. public var compressionFraming: SessionProxy.CompressionFraming + /// The optional TLS wrapping. + public var tlsWrap: SessionProxy.TLSWrap? + /// Sends periodical keep-alive packets (ping) if set. Useful with stateful firewalls. public var keepAliveSeconds: Int? @@ -178,6 +181,7 @@ extension TunnelKitProvider { clientKey = nil mtu = 1500 compressionFraming = .disabled + tlsWrap = nil keepAliveSeconds = nil renegotiatesAfterSeconds = nil usesPIAPatches = false @@ -234,6 +238,13 @@ extension TunnelKitProvider { } else { compressionFraming = .disabled } + if let tlsWrapData = providerConfiguration[S.tlsWrap] as? Data { + do { + tlsWrap = try SessionProxy.TLSWrap.deserialized(tlsWrapData) + } catch { + throw ProviderError.configuration(field: "protocolConfiguration.providerConfiguration[\(S.tlsWrap)]") + } + } keepAliveSeconds = providerConfiguration[S.keepAlive] as? Int renegotiatesAfterSeconds = providerConfiguration[S.renegotiatesAfter] as? Int usesPIAPatches = providerConfiguration[S.usesPIAPatches] as? Bool ?? false @@ -271,6 +282,7 @@ extension TunnelKitProvider { clientKey: clientKey, mtu: mtu, compressionFraming: compressionFraming, + tlsWrap: tlsWrap, keepAliveSeconds: keepAliveSeconds, renegotiatesAfterSeconds: renegotiatesAfterSeconds, usesPIAPatches: usesPIAPatches, @@ -306,6 +318,8 @@ extension TunnelKitProvider { static let compressionFraming = "CompressionFraming" + static let tlsWrap = "TLSWrap" + static let keepAlive = "KeepAlive" static let renegotiatesAfter = "RenegotiatesAfter" @@ -349,6 +363,9 @@ extension TunnelKitProvider { /// - 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? @@ -433,6 +450,9 @@ extension TunnelKitProvider { dict[S.resolvedAddresses] = resolvedAddresses } dict[S.compressionFraming] = compressionFraming.rawValue + if let tlsWrapData = tlsWrap?.serialized() { + dict[S.tlsWrap] = tlsWrapData + } if let keepAliveSeconds = keepAliveSeconds { dict[S.keepAlive] = keepAliveSeconds } @@ -507,6 +527,11 @@ extension TunnelKitProvider { } else { log.info("\tRenegotiation: never") } + if let tlsWrap = tlsWrap { + log.info("\tTLS wrapping: \(tlsWrap.strategy)") + } else { + log.info("\tTLS wrapping: disabled") + } log.info("\tDebug: \(shouldDebug)") } } @@ -530,6 +555,7 @@ extension TunnelKitProvider.Configuration: Equatable { builder.clientKey = clientKey builder.mtu = mtu builder.compressionFraming = compressionFraming + builder.tlsWrap = tlsWrap builder.keepAliveSeconds = keepAliveSeconds builder.renegotiatesAfterSeconds = renegotiatesAfterSeconds builder.usesPIAPatches = usesPIAPatches @@ -552,6 +578,7 @@ extension TunnelKitProvider.Configuration: Equatable { (lhs.compressionFraming == rhs.compressionFraming) && (lhs.keepAliveSeconds == rhs.keepAliveSeconds) && (lhs.renegotiatesAfterSeconds == rhs.renegotiatesAfterSeconds) + // XXX: tlsWrap not copied ) } } diff --git a/TunnelKit/Sources/AppExtension/TunnelKitProvider.swift b/TunnelKit/Sources/AppExtension/TunnelKitProvider.swift index 043fe44..348adaa 100644 --- a/TunnelKit/Sources/AppExtension/TunnelKitProvider.swift +++ b/TunnelKit/Sources/AppExtension/TunnelKitProvider.swift @@ -228,6 +228,7 @@ open class TunnelKitProvider: NEPacketTunnelProvider { sessionConfiguration.clientCertificatePath = clientCertificatePath sessionConfiguration.clientKeyPath = clientKeyPath sessionConfiguration.compressionFraming = cfg.compressionFraming + sessionConfiguration.tlsWrap = cfg.tlsWrap if let keepAliveSeconds = cfg.keepAliveSeconds { sessionConfiguration.keepAliveInterval = TimeInterval(keepAliveSeconds) } diff --git a/TunnelKit/Sources/Core/SessionProxy+Configuration.swift b/TunnelKit/Sources/Core/SessionProxy+Configuration.swift index be7ea57..c697ce2 100644 --- a/TunnelKit/Sources/Core/SessionProxy+Configuration.swift +++ b/TunnelKit/Sources/Core/SessionProxy+Configuration.swift @@ -156,6 +156,9 @@ extension SessionProxy { /// Sets compression framing, disabled by default. public var compressionFraming: CompressionFraming + /// The optional TLS wrapping. + public var tlsWrap: TLSWrap? + /// Sends periodical keep-alive packets if set. public var keepAliveInterval: TimeInterval? @@ -174,6 +177,7 @@ extension SessionProxy { clientCertificatePath = nil clientKeyPath = nil compressionFraming = .disabled + tlsWrap = nil keepAliveInterval = nil renegotiatesAfter = nil usesPIAPatches = false @@ -193,6 +197,7 @@ extension SessionProxy { clientCertificatePath: clientCertificatePath, clientKeyPath: clientKeyPath, compressionFraming: compressionFraming, + tlsWrap: tlsWrap, keepAliveInterval: keepAliveInterval, renegotiatesAfter: renegotiatesAfter, usesPIAPatches: usesPIAPatches @@ -224,6 +229,9 @@ extension SessionProxy { /// - Seealso: `SessionProxy.ConfigurationBuilder.compressionFraming` public let compressionFraming: CompressionFraming + /// - Seealso: `SessionProxy.ConfigurationBuilder.tlsWrap` + public var tlsWrap: TLSWrap? + /// - Seealso: `SessionProxy.ConfigurationBuilder.keepAliveInterval` public let keepAliveInterval: TimeInterval? diff --git a/TunnelKit/Sources/Core/SessionProxy+TLSWrap.swift b/TunnelKit/Sources/Core/SessionProxy+TLSWrap.swift new file mode 100644 index 0000000..8b75f69 --- /dev/null +++ b/TunnelKit/Sources/Core/SessionProxy+TLSWrap.swift @@ -0,0 +1,60 @@ +// +// SessionProxy+TLSWrap.swift +// TunnelKit +// +// Created by Davide De Rosa on 9/11/18. +// Copyright (c) 2018 Davide De Rosa. All rights reserved. +// +// https://github.com/keeshux +// +// This file is part of TunnelKit. +// +// TunnelKit is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// TunnelKit is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with TunnelKit. If not, see . +// + +import Foundation + +extension SessionProxy { + + /// Holds parameters for TLS wrapping. + public class TLSWrap: Codable { + + /// The wrapping strategy. + public enum Strategy: String, Codable { + case none + } + + /// The wrapping strategy. + public let strategy: Strategy + + /// The static encryption key. + public let key: StaticKey + + /// :nodoc: + public init(strategy: Strategy, key: StaticKey) { + self.strategy = strategy + self.key = key + } + + /// :nodoc: + public static func deserialized(_ data: Data) throws -> SessionProxy.TLSWrap { + return try JSONDecoder().decode(SessionProxy.TLSWrap.self, from: data) + } + + /// :nodoc: + public func serialized() -> Data? { + return try? JSONEncoder().encode(self) + } + } +} diff --git a/TunnelKit/Sources/Core/SessionProxy.swift b/TunnelKit/Sources/Core/SessionProxy.swift index f384333..921d1b9 100644 --- a/TunnelKit/Sources/Core/SessionProxy.swift +++ b/TunnelKit/Sources/Core/SessionProxy.swift @@ -173,7 +173,16 @@ public class SessionProxy { lastPing = BidirectionalState(withResetValue: Date.distantPast) isStopping = false - controlChannel = ControlChannel() + if let tlsWrap = configuration.tlsWrap { + + // TODO: select strategy + switch tlsWrap.strategy { + default: + controlChannel = ControlChannel() + } + } else { + controlChannel = ControlChannel() + } } deinit {