From 119d2f02e4478ea1a33dd56d845a536c35e2624c Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Sun, 3 Jan 2021 00:44:42 +0100 Subject: [PATCH 1/3] Add OpenVPN dataCiphers field --- .../Protocols/OpenVPN/Configuration.swift | 8 +++++++ .../OpenVPN/ConfigurationParser.swift | 23 +++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift b/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift index 3b94188..26cd931 100644 --- a/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift +++ b/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift @@ -178,6 +178,9 @@ extension OpenVPN { /// The cipher algorithm for data encryption. public var cipher: Cipher? + /// The set of supported cipher algorithms for data encryption (2.5.). + public var dataCiphers: [Cipher]? + /// The digest algorithm for HMAC. public var digest: Digest? @@ -298,6 +301,7 @@ extension OpenVPN { public func build() -> Configuration { return Configuration( cipher: cipher, + dataCiphers: dataCiphers, digest: digest, compressionFraming: compressionFraming, compressionAlgorithm: compressionAlgorithm, @@ -355,6 +359,9 @@ extension OpenVPN { /// - Seealso: `ConfigurationBuilder.cipher` public let cipher: Cipher? + /// - Seealso: `ConfigurationBuilder.dataCiphers` + public let dataCiphers: [Cipher]? + /// - Seealso: `ConfigurationBuilder.digest` public let digest: Digest? @@ -476,6 +483,7 @@ extension OpenVPN.Configuration { public func builder() -> OpenVPN.ConfigurationBuilder { var builder = OpenVPN.ConfigurationBuilder() builder.cipher = cipher + builder.dataCiphers = dataCiphers builder.digest = digest builder.compressionFraming = compressionFraming builder.compressionAlgorithm = compressionAlgorithm diff --git a/TunnelKit/Sources/Protocols/OpenVPN/ConfigurationParser.swift b/TunnelKit/Sources/Protocols/OpenVPN/ConfigurationParser.swift index 6a15ae7..700db47 100644 --- a/TunnelKit/Sources/Protocols/OpenVPN/ConfigurationParser.swift +++ b/TunnelKit/Sources/Protocols/OpenVPN/ConfigurationParser.swift @@ -42,6 +42,8 @@ extension OpenVPN { static let cipher = NSRegularExpression("^cipher +[^,\\s]+") + static let dataCiphers = NSRegularExpression("^(data-ciphers|ncp-ciphers) +[^,\\s]+(:[^,\\s]+)*") + static let auth = NSRegularExpression("^auth +[\\w\\-]+") static let compLZO = NSRegularExpression("^comp-lzo.*") @@ -199,6 +201,7 @@ extension OpenVPN { var currentBlockName: String? var currentBlock: [String] = [] + var optDataCiphers: [Cipher]? var optCipher: Cipher? var optDigest: Digest? var optCompressionFraming: CompressionFraming? @@ -345,8 +348,19 @@ extension OpenVPN { return } optCipher = Cipher(rawValue: rawValue.uppercased()) - if optCipher == nil { - unsupportedError = ConfigurationError.unsupportedConfiguration(option: "cipher \(rawValue)") + } + Regex.dataCiphers.enumerateArguments(in: line) { + isHandled = true + guard let rawValue = $0.first else { + return + } + let rawCiphers = rawValue.components(separatedBy: ":") + optDataCiphers = [] + rawCiphers.forEach { + guard let cipher = Cipher(rawValue: $0.uppercased()) else { + return + } + optDataCiphers?.append(cipher) } } Regex.auth.enumerateArguments(in: line) { @@ -610,8 +624,8 @@ extension OpenVPN { guard let _ = optCA else { throw ConfigurationError.missingConfiguration(option: "ca") } - guard let _ = optCipher else { - throw ConfigurationError.missingConfiguration(option: "cipher") + guard optCipher != nil || !(optDataCiphers?.isEmpty ?? false) else { + throw ConfigurationError.missingConfiguration(option: "cipher or data-ciphers") } } @@ -622,6 +636,7 @@ extension OpenVPN { // MARK: General sessionBuilder.cipher = optCipher + sessionBuilder.dataCiphers = optDataCiphers sessionBuilder.digest = optDigest sessionBuilder.compressionFraming = optCompressionFraming sessionBuilder.compressionAlgorithm = optCompressionAlgorithm From 7ea088e4a1942a6274d70a16ecd341eb31dc887c Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Sun, 3 Jan 2021 00:56:02 +0100 Subject: [PATCH 2/3] Make peerInfo dynamic to add IV_CIPHERS Fixes #193 --- CHANGELOG.md | 6 ++++++ TunnelKit/Sources/Protocols/OpenVPN/Authenticator.swift | 6 +++++- .../Protocols/OpenVPN/CoreConfiguration+OpenVPN.swift | 7 +++++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe8968a..7fb1bae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Added + +- Handle `--data-ciphers` from OpenVPN 2.5 + ## 3.2.0 (2021-01-07) ### Changed diff --git a/TunnelKit/Sources/Protocols/OpenVPN/Authenticator.swift b/TunnelKit/Sources/Protocols/OpenVPN/Authenticator.swift index d6a6452..ec1eb20 100644 --- a/TunnelKit/Sources/Protocols/OpenVPN/Authenticator.swift +++ b/TunnelKit/Sources/Protocols/OpenVPN/Authenticator.swift @@ -156,7 +156,11 @@ extension OpenVPN { } // peer info - raw.appendSized(Z(CoreConfiguration.OpenVPN.peerInfo, nullTerminated: true)) + var extra: [String: String] = [:] + if let dataCiphers = options.dataCiphers { + extra["IV_CIPHERS"] = dataCiphers.map { $0.rawValue }.joined(separator: ":") + } + raw.appendSized(Z(CoreConfiguration.OpenVPN.peerInfo(extra: extra), nullTerminated: true)) if CoreConfiguration.logsSensitiveData { log.debug("TLS.auth: Put plaintext (\(raw.count) bytes): \(raw.toHex())") diff --git a/TunnelKit/Sources/Protocols/OpenVPN/CoreConfiguration+OpenVPN.swift b/TunnelKit/Sources/Protocols/OpenVPN/CoreConfiguration+OpenVPN.swift index bda07e1..2c45335 100644 --- a/TunnelKit/Sources/Protocols/OpenVPN/CoreConfiguration+OpenVPN.swift +++ b/TunnelKit/Sources/Protocols/OpenVPN/CoreConfiguration+OpenVPN.swift @@ -63,7 +63,7 @@ extension CoreConfiguration { // MARK: Authentication - static let peerInfo: String = { + static func peerInfo(extra: [String: String]? = nil) -> String { let platform: String #if os(iOS) platform = "ios" @@ -83,9 +83,12 @@ extension CoreConfiguration { if LZOIsSupported() { info.append("IV_LZO=1") } + if let extra = extra { + info.append(contentsOf: extra.map { "\($0)=\($1)" }) + } info.append("") return info.joined(separator: "\n") - }() + } static let randomLength = 32 From c15d6f521a7419e1b8e2d947ed73cb60deb856ff Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Fri, 8 Jan 2021 19:46:16 +0100 Subject: [PATCH 3/3] Parse dataCiphersFallback as last resort Prioritize over deprecate cipher. --- CHANGELOG.md | 2 +- .../Protocols/OpenVPN/ConfigurationParser.swift | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fb1bae..b1311e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Handle `--data-ciphers` from OpenVPN 2.5 +- Handle `--data-ciphers` and `data-ciphers-fallback` from OpenVPN 2.5 ## 3.2.0 (2021-01-07) diff --git a/TunnelKit/Sources/Protocols/OpenVPN/ConfigurationParser.swift b/TunnelKit/Sources/Protocols/OpenVPN/ConfigurationParser.swift index 700db47..8c1895e 100644 --- a/TunnelKit/Sources/Protocols/OpenVPN/ConfigurationParser.swift +++ b/TunnelKit/Sources/Protocols/OpenVPN/ConfigurationParser.swift @@ -44,6 +44,8 @@ extension OpenVPN { static let dataCiphers = NSRegularExpression("^(data-ciphers|ncp-ciphers) +[^,\\s]+(:[^,\\s]+)*") + static let dataCiphersFallback = NSRegularExpression("^data-ciphers-fallback +[^,\\s]+") + static let auth = NSRegularExpression("^auth +[\\w\\-]+") static let compLZO = NSRegularExpression("^comp-lzo.*") @@ -202,6 +204,7 @@ extension OpenVPN { var currentBlock: [String] = [] var optDataCiphers: [Cipher]? + var optDataCiphersFallback: Cipher? var optCipher: Cipher? var optDigest: Digest? var optCompressionFraming: CompressionFraming? @@ -363,6 +366,13 @@ extension OpenVPN { optDataCiphers?.append(cipher) } } + Regex.dataCiphersFallback.enumerateArguments(in: line) { + isHandled = true + guard let rawValue = $0.first else { + return + } + optDataCiphersFallback = Cipher(rawValue: rawValue.uppercased()) + } Regex.auth.enumerateArguments(in: line) { isHandled = true guard let rawValue = $0.first else { @@ -635,7 +645,7 @@ extension OpenVPN { // MARK: General - sessionBuilder.cipher = optCipher + sessionBuilder.cipher = optDataCiphersFallback ?? optCipher sessionBuilder.dataCiphers = optDataCiphers sessionBuilder.digest = optDigest sessionBuilder.compressionFraming = optCompressionFraming