diff --git a/CHANGELOG.md b/CHANGELOG.md index fe8968a..b1311e6 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` and `data-ciphers-fallback` 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/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..8c1895e 100644 --- a/TunnelKit/Sources/Protocols/OpenVPN/ConfigurationParser.swift +++ b/TunnelKit/Sources/Protocols/OpenVPN/ConfigurationParser.swift @@ -42,6 +42,10 @@ extension OpenVPN { static let cipher = NSRegularExpression("^cipher +[^,\\s]+") + 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.*") @@ -199,6 +203,8 @@ extension OpenVPN { var currentBlockName: String? var currentBlock: [String] = [] + var optDataCiphers: [Cipher]? + var optDataCiphersFallback: Cipher? var optCipher: Cipher? var optDigest: Digest? var optCompressionFraming: CompressionFraming? @@ -345,9 +351,27 @@ 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.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 @@ -610,8 +634,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") } } @@ -621,7 +645,8 @@ extension OpenVPN { // MARK: General - sessionBuilder.cipher = optCipher + sessionBuilder.cipher = optDataCiphersFallback ?? optCipher + sessionBuilder.dataCiphers = optDataCiphers sessionBuilder.digest = optDigest sessionBuilder.compressionFraming = optCompressionFraming sessionBuilder.compressionAlgorithm = optCompressionAlgorithm 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