Merge branch 'encrypted-cert-key'

This commit is contained in:
Davide De Rosa 2019-03-25 20:43:40 +01:00
commit 01ac29391b
12 changed files with 413 additions and 19 deletions

View File

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- Scramble endpoints via `--remote-random`. [#76](https://github.com/keeshux/tunnelkit/issues/76) - Scramble endpoints via `--remote-random`. [#76](https://github.com/keeshux/tunnelkit/issues/76)
- Support for encrypted certificate private keys. [#72](https://github.com/keeshux/tunnelkit/issues/72)
### Fixed ### Fixed

View File

@ -48,7 +48,6 @@ Unsupported:
- Compression via `--compress` other than empty or `lzo` - Compression via `--compress` other than empty or `lzo`
- Proxy - Proxy
- External file references (inline `<block>` only) - External file references (inline `<block>` only)
- Encrypted client certificate keys
- `<connection>` blocks - `<connection>` blocks
Ignored: Ignored:

View File

@ -61,8 +61,14 @@
0E3B15C82152B05E00984B17 /* CryptoCTR.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E3B15C52152B05E00984B17 /* CryptoCTR.h */; }; 0E3B15C82152B05E00984B17 /* CryptoCTR.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E3B15C52152B05E00984B17 /* CryptoCTR.h */; };
0E3B15C92152B05E00984B17 /* CryptoCTR.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B15C62152B05E00984B17 /* CryptoCTR.m */; }; 0E3B15C92152B05E00984B17 /* CryptoCTR.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B15C62152B05E00984B17 /* CryptoCTR.m */; };
0E3B15CA2152B05E00984B17 /* CryptoCTR.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B15C62152B05E00984B17 /* CryptoCTR.m */; }; 0E3B15CA2152B05E00984B17 /* CryptoCTR.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B15C62152B05E00984B17 /* CryptoCTR.m */; };
0E3B65742249253A00EFF4DA /* tunnelbear.enc.ovpn in Resources */ = {isa = PBXBuildFile; fileRef = 0E3B656E224923EC00EFF4DA /* tunnelbear.enc.ovpn */; };
0E3B65752249253B00EFF4DA /* tunnelbear.enc.ovpn in Resources */ = {isa = PBXBuildFile; fileRef = 0E3B656E224923EC00EFF4DA /* tunnelbear.enc.ovpn */; };
0E3B65762249253F00EFF4DA /* tunnelbear.key in Resources */ = {isa = PBXBuildFile; fileRef = 0E3B65712249247E00EFF4DA /* tunnelbear.key */; };
0E3B65772249254000EFF4DA /* tunnelbear.key in Resources */ = {isa = PBXBuildFile; fileRef = 0E3B65712249247E00EFF4DA /* tunnelbear.key */; };
0E3E0F212108A8CC00B371C1 /* SessionProxy+PushReply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E0F202108A8CC00B371C1 /* SessionProxy+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 */; }; 0E3E0F222108A8CC00B371C1 /* SessionProxy+PushReply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E0F202108A8CC00B371C1 /* SessionProxy+PushReply.swift */; };
0E500EA622493B5B00CAE560 /* tunnelbear.enc.key in Resources */ = {isa = PBXBuildFile; fileRef = 0E500EA522493B5B00CAE560 /* tunnelbear.enc.key */; };
0E500EA722493B5B00CAE560 /* tunnelbear.enc.key in Resources */ = {isa = PBXBuildFile; fileRef = 0E500EA522493B5B00CAE560 /* tunnelbear.enc.key */; };
0E50D57521634E0A00FC87A8 /* ControlChannelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E50D57421634E0A00FC87A8 /* ControlChannelTests.swift */; }; 0E50D57521634E0A00FC87A8 /* ControlChannelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E50D57421634E0A00FC87A8 /* ControlChannelTests.swift */; };
0E58BF3322405410006FB157 /* lzoconf.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E58BF2F22405410006FB157 /* lzoconf.h */; }; 0E58BF3322405410006FB157 /* lzoconf.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E58BF2F22405410006FB157 /* lzoconf.h */; };
0E58BF3422405410006FB157 /* lzoconf.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E58BF2F22405410006FB157 /* lzoconf.h */; }; 0E58BF3422405410006FB157 /* lzoconf.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E58BF2F22405410006FB157 /* lzoconf.h */; };
@ -76,14 +82,14 @@
0E58BF3D2240547F006FB157 /* CompressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF3B2240547F006FB157 /* CompressionTests.swift */; }; 0E58BF3D2240547F006FB157 /* CompressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF3B2240547F006FB157 /* CompressionTests.swift */; };
0E58BF4C22405C2F006FB157 /* StandardLZO.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF4922405C2F006FB157 /* StandardLZO.m */; }; 0E58BF4C22405C2F006FB157 /* StandardLZO.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF4922405C2F006FB157 /* StandardLZO.m */; };
0E58BF4D22405C2F006FB157 /* StandardLZO.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF4922405C2F006FB157 /* StandardLZO.m */; }; 0E58BF4D22405C2F006FB157 /* StandardLZO.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF4922405C2F006FB157 /* StandardLZO.m */; };
0E58BF5622411F3D006FB157 /* LZO.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E58BF5522411F37006FB157 /* LZO.h */; };
0E58BF5722411F3E006FB157 /* LZO.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E58BF5522411F37006FB157 /* LZO.h */; };
0E58BF5922411FEF006FB157 /* LZO.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF5822411FEF006FB157 /* LZO.m */; };
0E58BF5A22411FEF006FB157 /* LZO.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF5822411FEF006FB157 /* LZO.m */; };
0E58BF502240F98F006FB157 /* CompressionAlgorithmNative.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E58BF4F2240F98E006FB157 /* CompressionAlgorithmNative.h */; }; 0E58BF502240F98F006FB157 /* CompressionAlgorithmNative.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E58BF4F2240F98E006FB157 /* CompressionAlgorithmNative.h */; };
0E58BF512240F98F006FB157 /* CompressionAlgorithmNative.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E58BF4F2240F98E006FB157 /* CompressionAlgorithmNative.h */; }; 0E58BF512240F98F006FB157 /* CompressionAlgorithmNative.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E58BF4F2240F98E006FB157 /* CompressionAlgorithmNative.h */; };
0E58BF532240FAA6006FB157 /* SessionProxy+CompressionAlgorithm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF522240FAA6006FB157 /* SessionProxy+CompressionAlgorithm.swift */; }; 0E58BF532240FAA6006FB157 /* SessionProxy+CompressionAlgorithm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF522240FAA6006FB157 /* SessionProxy+CompressionAlgorithm.swift */; };
0E58BF542240FAA6006FB157 /* SessionProxy+CompressionAlgorithm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF522240FAA6006FB157 /* SessionProxy+CompressionAlgorithm.swift */; }; 0E58BF542240FAA6006FB157 /* SessionProxy+CompressionAlgorithm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF522240FAA6006FB157 /* SessionProxy+CompressionAlgorithm.swift */; };
0E58BF5622411F3D006FB157 /* LZO.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E58BF5522411F37006FB157 /* LZO.h */; };
0E58BF5722411F3E006FB157 /* LZO.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E58BF5522411F37006FB157 /* LZO.h */; };
0E58BF5922411FEF006FB157 /* LZO.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF5822411FEF006FB157 /* LZO.m */; };
0E58BF5A22411FEF006FB157 /* LZO.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF5822411FEF006FB157 /* LZO.m */; };
0E58F1302138AC2F00A49F27 /* DNSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E58F12F2138AC2F00A49F27 /* DNSTests.swift */; }; 0E58F1302138AC2F00A49F27 /* DNSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E58F12F2138AC2F00A49F27 /* DNSTests.swift */; };
0E749F5F2178885500BB2701 /* SessionProxy+PIA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E749F5E2178885500BB2701 /* SessionProxy+PIA.swift */; }; 0E749F5F2178885500BB2701 /* SessionProxy+PIA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E749F5E2178885500BB2701 /* SessionProxy+PIA.swift */; };
0E749F602178885500BB2701 /* SessionProxy+PIA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E749F5E2178885500BB2701 /* SessionProxy+PIA.swift */; }; 0E749F602178885500BB2701 /* SessionProxy+PIA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E749F5E2178885500BB2701 /* SessionProxy+PIA.swift */; };
@ -290,7 +296,10 @@
0E39BCE7214B2AB60035E9DE /* ControlPacket.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ControlPacket.m; sourceTree = "<group>"; }; 0E39BCE7214B2AB60035E9DE /* ControlPacket.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ControlPacket.m; sourceTree = "<group>"; };
0E3B15C52152B05E00984B17 /* CryptoCTR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptoCTR.h; sourceTree = "<group>"; }; 0E3B15C52152B05E00984B17 /* CryptoCTR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptoCTR.h; sourceTree = "<group>"; };
0E3B15C62152B05E00984B17 /* CryptoCTR.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CryptoCTR.m; sourceTree = "<group>"; }; 0E3B15C62152B05E00984B17 /* CryptoCTR.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CryptoCTR.m; sourceTree = "<group>"; };
0E3B656E224923EC00EFF4DA /* tunnelbear.enc.ovpn */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = tunnelbear.enc.ovpn; sourceTree = "<group>"; };
0E3B65712249247E00EFF4DA /* tunnelbear.key */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = tunnelbear.key; sourceTree = "<group>"; };
0E3E0F202108A8CC00B371C1 /* SessionProxy+PushReply.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionProxy+PushReply.swift"; sourceTree = "<group>"; }; 0E3E0F202108A8CC00B371C1 /* SessionProxy+PushReply.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionProxy+PushReply.swift"; sourceTree = "<group>"; };
0E500EA522493B5B00CAE560 /* tunnelbear.enc.key */ = {isa = PBXFileReference; lastKnownFileType = text; path = tunnelbear.enc.key; sourceTree = "<group>"; };
0E50D57421634E0A00FC87A8 /* ControlChannelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlChannelTests.swift; sourceTree = "<group>"; }; 0E50D57421634E0A00FC87A8 /* ControlChannelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlChannelTests.swift; sourceTree = "<group>"; };
0E58BF2F22405410006FB157 /* lzoconf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lzoconf.h; sourceTree = "<group>"; }; 0E58BF2F22405410006FB157 /* lzoconf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lzoconf.h; sourceTree = "<group>"; };
0E58BF3022405410006FB157 /* lzodefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lzodefs.h; sourceTree = "<group>"; }; 0E58BF3022405410006FB157 /* lzodefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lzodefs.h; sourceTree = "<group>"; };
@ -298,10 +307,10 @@
0E58BF3222405410006FB157 /* minilzo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = minilzo.c; sourceTree = "<group>"; }; 0E58BF3222405410006FB157 /* minilzo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = minilzo.c; sourceTree = "<group>"; };
0E58BF3B2240547F006FB157 /* CompressionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompressionTests.swift; sourceTree = "<group>"; }; 0E58BF3B2240547F006FB157 /* CompressionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompressionTests.swift; sourceTree = "<group>"; };
0E58BF4922405C2F006FB157 /* StandardLZO.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StandardLZO.m; sourceTree = "<group>"; }; 0E58BF4922405C2F006FB157 /* StandardLZO.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StandardLZO.m; sourceTree = "<group>"; };
0E58BF5522411F37006FB157 /* LZO.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LZO.h; sourceTree = "<group>"; };
0E58BF5822411FEF006FB157 /* LZO.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LZO.m; sourceTree = "<group>"; };
0E58BF4F2240F98E006FB157 /* CompressionAlgorithmNative.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompressionAlgorithmNative.h; sourceTree = "<group>"; }; 0E58BF4F2240F98E006FB157 /* CompressionAlgorithmNative.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompressionAlgorithmNative.h; sourceTree = "<group>"; };
0E58BF522240FAA6006FB157 /* SessionProxy+CompressionAlgorithm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionProxy+CompressionAlgorithm.swift"; sourceTree = "<group>"; }; 0E58BF522240FAA6006FB157 /* SessionProxy+CompressionAlgorithm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionProxy+CompressionAlgorithm.swift"; sourceTree = "<group>"; };
0E58BF5522411F37006FB157 /* LZO.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LZO.h; sourceTree = "<group>"; };
0E58BF5822411FEF006FB157 /* LZO.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LZO.m; sourceTree = "<group>"; };
0E58F12F2138AC2F00A49F27 /* DNSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSTests.swift; sourceTree = "<group>"; }; 0E58F12F2138AC2F00A49F27 /* DNSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSTests.swift; sourceTree = "<group>"; };
0E6479DD212EAC96008E6888 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 0E6479DD212EAC96008E6888 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
0E6479E0212EACD6008E6888 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 0E6479E0212EACD6008E6888 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -454,6 +463,9 @@
0EB2B45A20F0BE4C004233D7 /* TestUtils.swift */, 0EB2B45A20F0BE4C004233D7 /* TestUtils.swift */,
0E749F612178911C00BB2701 /* pia-2048.pem */, 0E749F612178911C00BB2701 /* pia-2048.pem */,
0E011F832196E25400BA59EE /* pia-hungary.ovpn */, 0E011F832196E25400BA59EE /* pia-hungary.ovpn */,
0E3B656E224923EC00EFF4DA /* tunnelbear.enc.ovpn */,
0E500EA522493B5B00CAE560 /* tunnelbear.enc.key */,
0E3B65712249247E00EFF4DA /* tunnelbear.key */,
); );
path = TunnelKitTests; path = TunnelKitTests;
sourceTree = "<group>"; sourceTree = "<group>";
@ -928,6 +940,9 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
0E011F852196E25900BA59EE /* pia-hungary.ovpn in Resources */, 0E011F852196E25900BA59EE /* pia-hungary.ovpn in Resources */,
0E500EA622493B5B00CAE560 /* tunnelbear.enc.key in Resources */,
0E3B65762249253F00EFF4DA /* tunnelbear.key in Resources */,
0E3B65742249253A00EFF4DA /* tunnelbear.enc.ovpn in Resources */,
0E749F622178911D00BB2701 /* pia-2048.pem in Resources */, 0E749F622178911D00BB2701 /* pia-2048.pem in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -961,6 +976,9 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
0E011F862196E25A00BA59EE /* pia-hungary.ovpn in Resources */, 0E011F862196E25A00BA59EE /* pia-hungary.ovpn in Resources */,
0E500EA722493B5B00CAE560 /* tunnelbear.enc.key in Resources */,
0E3B65772249254000EFF4DA /* tunnelbear.key in Resources */,
0E3B65752249253B00EFF4DA /* tunnelbear.enc.ovpn in Resources */,
0EA82A3E2190B2BC007960EB /* pia-2048.pem in Resources */, 0EA82A3E2190B2BC007960EB /* pia-2048.pem in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;

View File

@ -40,6 +40,12 @@ public class ConfigurationParser {
/// The file includes an unsupported option. /// The file includes an unsupported option.
case unsupportedConfiguration(option: String) case unsupportedConfiguration(option: String)
/// Passphrase required to decrypt private keys.
case encryptionPassphrase
/// Encryption passphrase is incorrect or key is corrupt.
case unableToDecrypt(error: Error)
} }
/// Result of the parser. /// Result of the parser.
@ -114,25 +120,27 @@ public class ConfigurationParser {
Parses an .ovpn file from an URL. Parses an .ovpn file from an URL.
- Parameter url: The URL of the configuration file. - Parameter url: The URL of the configuration file.
- Parameter passphrase: The optional passphrase for encrypted data.
- Parameter returnsStripped: When `true`, stores the stripped file into `ParsingResult.strippedLines`. Defaults to `false`. - Parameter returnsStripped: When `true`, stores the stripped file into `ParsingResult.strippedLines`. Defaults to `false`.
- Returns: The `ParsingResult` outcome of the parsing. - Returns: The `ParsingResult` outcome of the parsing.
- Throws: `ParsingError` if the configuration file is wrong or incomplete. - Throws: `ParsingError` if the configuration file is wrong or incomplete.
*/ */
public static func parsed(fromURL url: URL, returnsStripped: Bool = false) throws -> ParsingResult { public static func parsed(fromURL url: URL, passphrase: String? = nil, returnsStripped: Bool = false) throws -> ParsingResult {
let lines = try String(contentsOf: url).trimmedLines() let lines = try String(contentsOf: url).trimmedLines()
return try parsed(fromLines: lines, originalURL: url, returnsStripped: returnsStripped) return try parsed(fromLines: lines, passphrase: passphrase, originalURL: url, returnsStripped: returnsStripped)
} }
/** /**
Parses an .ovpn file as an array of lines. Parses an .ovpn file as an array of lines.
- Parameter lines: The array of lines holding the configuration. - Parameter lines: The array of lines holding the configuration.
- Parameter passphrase: The optional passphrase for encrypted data.
- Parameter originalURL: The optional original URL of the configuration file. - Parameter originalURL: The optional original URL of the configuration file.
- Parameter returnsStripped: When `true`, stores the stripped file into `ParsingResult.strippedLines`. Defaults to `false`. - Parameter returnsStripped: When `true`, stores the stripped file into `ParsingResult.strippedLines`. Defaults to `false`.
- Returns: The `ParsingResult` outcome of the parsing. - Returns: The `ParsingResult` outcome of the parsing.
- Throws: `ParsingError` if the configuration file is wrong or incomplete. - Throws: `ParsingError` if the configuration file is wrong or incomplete.
*/ */
public static func parsed(fromLines lines: [String], originalURL: URL? = nil, returnsStripped: Bool = false) throws -> ParsingResult { public static func parsed(fromLines lines: [String], passphrase: String? = nil, originalURL: URL? = nil, returnsStripped: Bool = false) throws -> ParsingResult {
var strippedLines: [String]? = returnsStripped ? [] : nil var strippedLines: [String]? = returnsStripped ? [] : nil
var warning: ParsingError? = nil var warning: ParsingError? = nil
@ -209,10 +217,20 @@ public class ConfigurationParser {
clientCertificate = CryptoContainer(pem: currentBlock.joined(separator: "\n")) clientCertificate = CryptoContainer(pem: currentBlock.joined(separator: "\n"))
case "key": case "key":
let isEncrypted = normalizeEncryptedPEMBlock(block: &currentBlock)
let container = CryptoContainer(pem: currentBlock.joined(separator: "\n")) let container = CryptoContainer(pem: currentBlock.joined(separator: "\n"))
clientKey = container if isEncrypted {
if container.isEncrypted { guard let passphrase = passphrase else {
unsupportedError = ParsingError.unsupportedConfiguration(option: "encrypted client certificate key") unsupportedError = ParsingError.encryptionPassphrase
break
}
do {
clientKey = try container.decrypted(with: passphrase)
} catch let e {
unsupportedError = ParsingError.unableToDecrypt(error: e)
}
} else {
clientKey = container
} }
case "tls-auth": case "tls-auth":
@ -451,6 +469,16 @@ public class ConfigurationParser {
warning: warning warning: warning
) )
} }
private static func normalizeEncryptedPEMBlock(block: inout [String]) -> Bool {
// XXX: restore blank line after encryption header (easier than tweaking trimmedLines)
if block.count >= 3 && block[1].contains("ENCRYPTED") {
block.insert("", at: 3)
return true
}
return false
}
} }
private extension SocketType { private extension SocketType {
@ -472,9 +500,3 @@ extension String {
} }
} }
} }
extension CryptoContainer {
var isEncrypted: Bool {
return pem.contains("ENCRYPTED")
}
}

View File

@ -36,6 +36,7 @@
// //
import Foundation import Foundation
import __TunnelKitNative
/// Represents a cryptographic container in PEM format. /// Represents a cryptographic container in PEM format.
public struct CryptoContainer: Equatable { public struct CryptoContainer: Equatable {
@ -73,3 +74,10 @@ extension CryptoContainer: Codable {
try container.encode(pem) try container.encode(pem)
} }
} }
extension CryptoContainer {
func decrypted(with passphrase: String) throws -> CryptoContainer {
let decryptedPEM = try TLSBox.decryptedPrivateKey(fromPEM: pem, passphrase: passphrase)
return CryptoContainer(pem: decryptedPEM)
}
}

View File

@ -52,6 +52,8 @@ extern NSString *const TLSBoxPeerVerificationErrorNotification;
@interface TLSBox : NSObject @interface TLSBox : NSObject
+ (nullable NSString *)md5ForCertificatePath:(NSString *)path error:(NSError **)error; + (nullable NSString *)md5ForCertificatePath:(NSString *)path error:(NSError **)error;
+ (nullable NSString *)decryptedPrivateKeyFromPath:(NSString *)path passphrase:(NSString *)passphrase error:(NSError **)error;
+ (nullable NSString *)decryptedPrivateKeyFromPEM:(NSString *)pem passphrase:(NSString *)passphrase error:(NSError **)error;
- (instancetype)initWithCAPath:(NSString *)caPath - (instancetype)initWithCAPath:(NSString *)caPath
clientCertificatePath:(nullable NSString *)clientCertificatePath clientCertificatePath:(nullable NSString *)clientCertificatePath

View File

@ -39,6 +39,8 @@
#import <openssl/err.h> #import <openssl/err.h>
#import <openssl/evp.h> #import <openssl/evp.h>
#import <openssl/x509v3.h> #import <openssl/x509v3.h>
#import <openssl/rsa.h>
#import <openssl/pem.h>
#import "TLSBox.h" #import "TLSBox.h"
#import "Allocation.h" #import "Allocation.h"
@ -107,6 +109,61 @@ int TLSBoxVerifyPeer(int ok, X509_STORE_CTX *ctx) {
return hex; return hex;
} }
+ (NSString *)decryptedPrivateKeyFromPath:(NSString *)path passphrase:(NSString *)passphrase error:(NSError * _Nullable __autoreleasing *)error
{
BIO *bio;
if (!(bio = BIO_new_file([path cStringUsingEncoding:NSASCIIStringEncoding], "r"))) {
return NULL;
}
NSString *ret = [[self class] decryptedPrivateKeyFromBIO:bio passphrase:passphrase error:error];
BIO_free(bio);
return ret;
}
+ (NSString *)decryptedPrivateKeyFromPEM:(NSString *)pem passphrase:(NSString *)passphrase error:(NSError * _Nullable __autoreleasing *)error
{
BIO *bio;
if (!(bio = BIO_new_mem_buf([pem cStringUsingEncoding:NSASCIIStringEncoding], (int)[pem length]))) {
return NULL;
}
NSString *ret = [[self class] decryptedPrivateKeyFromBIO:bio passphrase:passphrase error:error];
BIO_free(bio);
return ret;
}
+ (NSString *)decryptedPrivateKeyFromBIO:(BIO *)bio passphrase:(NSString *)passphrase error:(NSError * _Nullable __autoreleasing *)error
{
RSA *rsaKey;
if (!(rsaKey = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, (void *)passphrase.UTF8String))) {
return NULL;
}
EVP_PKEY *evpKey = EVP_PKEY_new();
if (!EVP_PKEY_set1_RSA(evpKey, rsaKey)) {
EVP_PKEY_free(evpKey);
return NULL;
}
BIO *output = BIO_new(BIO_s_mem());
if (!PEM_write_bio_PKCS8PrivateKey(output, evpKey, NULL, NULL, 0, NULL, NULL)) {
BIO_free(output);
EVP_PKEY_free(evpKey);
return NULL;
}
const int decLength = (int)BIO_ctrl_pending(output);
char *decKeyBytes = malloc(decLength + 1);
if (BIO_read(output, decKeyBytes, decLength) < 0) {
BIO_free(output);
EVP_PKEY_free(evpKey);
return NULL;
}
BIO_free(output);
EVP_PKEY_free(evpKey);
decKeyBytes[decLength] = '\0';
return [NSString stringWithCString:decKeyBytes encoding:NSASCIIStringEncoding];
}
- (instancetype)init - (instancetype)init
{ {
[NSException raise:NSInvalidArgumentException format:@"Use initWithCAPath:clientCertificatePath:clientKeyPath:"]; [NSException raise:NSInvalidArgumentException format:@"Use initWithCAPath:clientCertificatePath:clientKeyPath:"];

View File

@ -80,6 +80,12 @@ class ConfigurationParserTests: XCTestCase {
XCTAssertThrowsError(try ConfigurationParser.parsed(fromLines: lines)) XCTAssertThrowsError(try ConfigurationParser.parsed(fromLines: lines))
} }
func testEncryptedCertificateKey() throws {
let url = Bundle(for: ConfigurationParserTests.self).url(forResource: "tunnelbear", withExtension: "enc.ovpn")!
XCTAssertThrowsError(try ConfigurationParser.parsed(fromURL: url))
XCTAssertNoThrow(try ConfigurationParser.parsed(fromURL: url, passphrase: "foobar"))
}
private func url(withName name: String) -> URL { private func url(withName name: String) -> URL {
return Bundle(for: ConfigurationParserTests.self).url(forResource: name, withExtension: "ovpn")! return Bundle(for: ConfigurationParserTests.self).url(forResource: name, withExtension: "ovpn")!
} }

View File

@ -95,6 +95,23 @@ class EncryptionTests: XCTestCase {
print(md5) print(md5)
XCTAssertEqual(md5, exp) XCTAssertEqual(md5, exp)
} }
func testPrivateKeyDecryption() {
let bundle = Bundle(for: EncryptionTests.self)
let encryptedPath = bundle.path(forResource: "tunnelbear", ofType: "enc.key")!
let decryptedPath = bundle.path(forResource: "tunnelbear", ofType: "key")!
XCTAssertThrowsError(try TLSBox.decryptedPrivateKey(fromPath: encryptedPath, passphrase: "wrongone"))
let decryptedViaPath = try! TLSBox.decryptedPrivateKey(fromPath: encryptedPath, passphrase: "foobar")
print(decryptedViaPath)
let encryptedPEM = try! String(contentsOfFile: encryptedPath, encoding: .utf8)
let decryptedViaString = try! TLSBox.decryptedPrivateKey(fromPEM: encryptedPEM, passphrase: "foobar")
print(decryptedViaString)
XCTAssertEqual(decryptedViaPath, decryptedViaString)
let expDecrypted = try! String(contentsOfFile: decryptedPath)
XCTAssertEqual(decryptedViaPath, expDecrypted)
}
func testCTR() { func testCTR() {
let (client, server) = clientServer("aes-256-ctr", "sha256") let (client, server) = clientServer("aes-256-ctr", "sha256")

View File

@ -0,0 +1,54 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,1C7216F89C74E56260FE71A9C1BAB35E
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
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,158 @@
client
dev tun0
proto udp
comp-lzo
nobind
ns-cert-type server
persist-key
persist-tun
reneg-sec 0
dhcp-option DNS 8.8.8.8
dhcp-option DNS 8.8.4.4
redirect-gateway
verb 1
auth-user-pass
<ca>
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----
</ca>
<cert>
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----
</cert>
<key>
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,1C7216F89C74E56260FE71A9C1BAB35E
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
-----END RSA PRIVATE KEY-----
</key>
remote no.lazerpenguin.com 443
cipher AES-256-CBC
auth SHA256
keysize 256

View File

@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
1LXZrtjAB4U+/F7um6YyAavpHYq9hg==
-----END PRIVATE KEY-----