summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--ios/CHANGELOG.md1
-rw-r--r--ios/MullvadVPN/AppDelegate.swift9
-rw-r--r--ios/MullvadVPN/TunnelManager.swift79
-rw-r--r--ios/MullvadVPN/WireguardKeysViewController.swift74
4 files changed, 92 insertions, 71 deletions
diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md
index 7e37c1150f..8efe829a48 100644
--- a/ios/CHANGELOG.md
+++ b/ios/CHANGELOG.md
@@ -34,6 +34,7 @@ Line wrap the file at 100 chars. Th
- Properly format date intervals close to 1 day or less than 1 minute. Enforce intervals between 1
and 90 days to always be displayed in days quantity.
- Fix a number of errors in DNS64 resolution and IPv6 support.
+- Update the tunnel state when the app returns from suspended state.
## [2020.2] - 2020-04-16
### Fixed
diff --git a/ios/MullvadVPN/AppDelegate.swift b/ios/MullvadVPN/AppDelegate.swift
index 1e33422f85..907e731225 100644
--- a/ios/MullvadVPN/AppDelegate.swift
+++ b/ios/MullvadVPN/AppDelegate.swift
@@ -21,8 +21,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
let simulatorTunnelProvider = SimulatorTunnelProviderHost()
#endif
-
private var loadTunnelSubscriber: AnyCancellable?
+ private var refreshTunnelSubscriber: AnyCancellable?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
#if targetEnvironment(simulator)
@@ -62,6 +62,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
return true
}
+ func applicationDidBecomeActive(_ application: UIApplication) {
+ refreshTunnelSubscriber = TunnelManager.shared.refreshTunnelState()
+ .sink(receiveCompletion: { (_) in
+ // no-op
+ })
+ }
+
private func didPresentTheMainController() {
let paymentManager = AppStorePaymentManager.shared
paymentManager.delegate = self
diff --git a/ios/MullvadVPN/TunnelManager.swift b/ios/MullvadVPN/TunnelManager.swift
index 6e54883c8f..8103edf373 100644
--- a/ios/MullvadVPN/TunnelManager.swift
+++ b/ios/MullvadVPN/TunnelManager.swift
@@ -309,6 +309,9 @@ class TunnelManager {
@Published private(set) var tunnelState = TunnelState.disconnected
+ /// A last known public key
+ @Published private(set) var publicKey: WireguardPublicKey?
+
/// Initialize the TunnelManager with the tunnel from the system
///
/// The given account token is used to ensure that the system tunnel was configured for the same
@@ -320,9 +323,12 @@ class TunnelManager {
.receive(on: self.executionQueue)
.flatMap { (tunnels) -> AnyPublisher<(), LoadTunnelError> in
- // Migrate tunnel configuration if needed
if let accountToken = accountToken {
+ // Migrate tunnel configuration if needed
self.migrateTunnelConfiguration(accountToken: accountToken)
+
+ // Load last known public key
+ self.loadPublicKey(accountToken: accountToken)
}
// No tunnels found. Save the account token.
@@ -355,6 +361,24 @@ class TunnelManager {
}.eraseToAnyPublisher()
}
+ /// Refresh tunnel state.
+ /// Use this method to update the tunnel state when app transitions from suspended to active
+ /// state.
+ func refreshTunnelState() -> AnyPublisher<(), TunnelManagerError> {
+ MutuallyExclusive(exclusivityQueue: exclusivityQueue, executionQueue: executionQueue) {
+ () -> AnyPublisher<(), TunnelManagerError> in
+ if let status = self.tunnelProvider?.connection.status {
+ self.updateTunnelState(connectionStatus: status)
+ }
+
+ // Reload the last known public key
+ if let accountToken = self.accountToken {
+ self.loadPublicKey(accountToken: accountToken)
+ }
+ return Result.Publisher(()).eraseToAnyPublisher()
+ }.eraseToAnyPublisher()
+ }
+
func startTunnel() -> AnyPublisher<(), TunnelManagerError> {
MutuallyExclusive(exclusivityQueue: exclusivityQueue, executionQueue: executionQueue) {
Just(self.accountToken)
@@ -408,15 +432,21 @@ class TunnelManager {
.mapError { SetAccountError.setup($0) }
}
+ let publicKey = tunnelConfig.interface.privateKey.publicKey
+
+ // Save the last known public key
+ self.publicKey = publicKey
+
// Make sure to avoid pushing the wireguard keys when addresses are assigned
guard tunnelConfig.interface.addresses.isEmpty else {
return setupTunnelPublisher.eraseToAnyPublisher()
}
// Send wireguard key to the server
- let publicKey = tunnelConfig.interface.privateKey.publicKey.rawRepresentation
-
- return self.rpc.pushWireguardKey(accountToken: accountToken, publicKey: publicKey)
+ return self.rpc.pushWireguardKey(
+ accountToken: accountToken,
+ publicKey: publicKey.rawRepresentation
+ )
.mapError { SetAccountError.pushWireguardKey($0) }
.flatMap { (addresses) in
self.updateAssociatedAddresses(
@@ -512,6 +542,7 @@ class TunnelManager {
.handleEvents(receiveCompletion: { (completion) in
if case .finished = completion {
self.accountToken = nil
+ self.publicKey = nil
self.tunnelProvider = nil
self.tunnelIpc = nil
@@ -559,9 +590,12 @@ class TunnelManager {
.map { _ in () }
.publisher
}.receive(on: self.executionQueue)
- .flatMap { _ in
+ .flatMap { _ -> AnyPublisher<(), RegenerateWireguardPrivateKeyError> in
+ // Save new public key
+ self.publicKey = newPrivateKey.publicKey
+
// Ignore Packet Tunnel IPC errors but log them
- self.reloadPacketTunnelConfiguration()
+ return self.reloadPacketTunnelConfiguration()
.handleEvents(receiveCompletion: { (completion) in
if case .failure(let error) = completion {
os_log(.error, "Failed to tell the tunnel to reload configuration: %{public}s", error.localizedDescription)
@@ -569,6 +603,7 @@ class TunnelManager {
})
.replaceError(with: ())
.setFailureType(to: RegenerateWireguardPrivateKeyError.self)
+ .eraseToAnyPublisher()
}
}
.mapError { TunnelManagerError.regenerateWireguardPrivateKey($0) }
@@ -624,20 +659,6 @@ class TunnelManager {
}.eraseToAnyPublisher()
}
- func getWireguardPublicKey() -> AnyPublisher<WireguardPublicKey, TunnelManagerError> {
- MutuallyExclusive(exclusivityQueue: exclusivityQueue, executionQueue: executionQueue) {
- Just(self.accountToken)
- .setFailureType(to: TunnelManagerError.self)
- .replaceNil(with: .missingAccount)
- .flatMap { (accountToken) in
- TunnelConfigurationManager.load(searchTerm: .accountToken(accountToken))
- .map { $0.tunnelConfiguration.interface.privateKey.publicKey }
- .mapError { .getWireguardPublicKey($0) }
- .publisher
- }
- }.eraseToAnyPublisher()
- }
-
// MARK: - Private
/// Tell Packet Tunnel process to reload the tunnel configuration
@@ -695,6 +716,18 @@ class TunnelManager {
updateTunnelState(connectionStatus: connection.status)
}
+ private func loadPublicKey(accountToken: String) {
+ switch TunnelConfigurationManager.load(searchTerm: .accountToken(accountToken)) {
+ case .success(let entry):
+ self.publicKey = entry.tunnelConfiguration.interface.privateKey.publicKey
+
+ case .failure(let error):
+ os_log(.error, "Failed to load the public key: %{public}s", error.localizedDescription)
+
+ self.publicKey = nil
+ }
+ }
+
/// Initiates the `tunnelState` update
private func updateTunnelState(connectionStatus: NEVPNStatus) {
os_log(.default, "VPN Status: %{public}s", "\(connectionStatus)")
@@ -738,6 +771,12 @@ class TunnelManager {
.eraseToAnyPublisher()
case .reasserting:
+ // Refresh the last known public key on reconnect to cover the possibility of
+ // the key being changed due to key rotation.
+ if let accountToken = self.accountToken {
+ self.loadPublicKey(accountToken: accountToken)
+ }
+
return self.getTunnelConnectionInfo()
.mapError { .ipcRequest($0) }
.map { .reconnecting($0) }
diff --git a/ios/MullvadVPN/WireguardKeysViewController.swift b/ios/MullvadVPN/WireguardKeysViewController.swift
index 5b01dd541c..c677c75eee 100644
--- a/ios/MullvadVPN/WireguardKeysViewController.swift
+++ b/ios/MullvadVPN/WireguardKeysViewController.swift
@@ -59,7 +59,7 @@ class WireguardKeysViewController: UIViewController {
@IBOutlet var verifyKeyButton: UIButton!
@IBOutlet var wireguardKeyStatusView: WireguardKeyStatusView!
- private var tunnelStateSubscriber: AnyCancellable?
+ private var publicKeySubscriber: AnyCancellable?
private var loadKeySubscriber: AnyCancellable?
private var verifyKeySubscriber: AnyCancellable?
private var regenerateKeySubscriber: AnyCancellable?
@@ -67,7 +67,6 @@ class WireguardKeysViewController: UIViewController {
private var copyToPasteboardSubscriber: AnyCancellable?
private let rpc = MullvadRpc()
- private var publicKey: WireguardPublicKey?
private var state: WireguardKeysViewState = .default {
didSet {
@@ -78,39 +77,29 @@ class WireguardKeysViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
- // Reset Storyboard placeholders
- setPublicKeyTitle(string: "-", animated: false)
- creationDateLabel.text = "-"
-
creationDateTimerSubscriber = Timer.publish(every: kCreationDateRefreshInterval, on: .main, in: .common)
.autoconnect()
.sink { [weak self] _ in
- guard let self = self else { return }
+ let publicKey = TunnelManager.shared.publicKey
- if let creationDate = self.publicKey?.creationDate {
- self.updateCreationDateLabel(with: creationDate)
- }
+ self?.updatePublicKey(publicKey: publicKey, animated: true)
}
- tunnelStateSubscriber = TunnelManager.shared.$tunnelState
+ publicKeySubscriber = TunnelManager.shared.$publicKey
+ .dropFirst()
.receive(on: DispatchQueue.main)
- .sink(receiveValue: { [weak self] (tunnelState) in
- guard let self = self else { return }
-
- // Reload the public key when the tunnel is reconnecting
- // Normally this may happen in response to private key change
- if case .reconnecting = tunnelState {
- self.loadPublicKey(animated: true)
- }
+ .sink(receiveValue: { [weak self] (publicKey) in
+ self?.updatePublicKey(publicKey: publicKey, animated: true)
})
- loadPublicKey(animated: false)
+ // Set public key title without animation
+ updatePublicKey(publicKey: TunnelManager.shared.publicKey, animated: false)
}
// MARK: - IBActions
@IBAction func copyPublicKey(_ sender: Any) {
- guard let publicKey = self.publicKey else { return }
+ guard let publicKey = TunnelManager.shared.publicKey else { return }
UIPasteboard.general.string = publicKey.stringRepresentation()
@@ -121,12 +110,11 @@ class WireguardKeysViewController: UIViewController {
copyToPasteboardSubscriber =
Just(()).cancellableDelay(for: .seconds(3), scheduler: DispatchQueue.main)
.sink(receiveValue: { [weak self] () in
- guard let self = self, let publicKey = self.publicKey else { return }
+ guard let self = self else { return }
- let displayKey = publicKey
- .stringRepresentation(maxLength: kDisplayPublicKeyMaxLength)
+ let publicKey = TunnelManager.shared.publicKey
- self.setPublicKeyTitle(string: displayKey, animated: true)
+ self.updatePublicKey(publicKey: publicKey, animated: true)
})
}
@@ -136,7 +124,7 @@ class WireguardKeysViewController: UIViewController {
@IBAction func handleVerifyKey(_ sender: Any) {
guard let accountToken = Account.shared.token,
- let publicKey = publicKey else { return }
+ let publicKey = TunnelManager.shared.publicKey else { return }
verifyKey(accountToken: accountToken, publicKey: publicKey)
}
@@ -157,30 +145,16 @@ class WireguardKeysViewController: UIViewController {
creationDateLabel.text = formatKeyGenerationElapsedTime(with: creationDate) ?? "-"
}
- private func loadPublicKey(animated: Bool) {
- loadKeySubscriber = TunnelManager.shared.getWireguardPublicKey()
- .receive(on: DispatchQueue.main)
- .sink(receiveCompletion: { (completion) in
- switch completion {
- case .finished:
- break
-
- case .failure(let error):
- os_log(.error, "Failed to receive the public key for Wireguard: %{public}s",
- error.localizedDescription)
-
- self.presentError(error, preferredStyle: .alert)
- }
- }) { [weak self] (publicKey) in
- guard let self = self else { return }
+ private func updatePublicKey(publicKey: WireguardPublicKey?, animated: Bool) {
+ if let publicKey = publicKey {
+ let displayKey = publicKey
+ .stringRepresentation(maxLength: kDisplayPublicKeyMaxLength)
- let displayKey = publicKey
- .stringRepresentation(maxLength: kDisplayPublicKeyMaxLength)
-
- self.setPublicKeyTitle(string: displayKey, animated: animated)
- self.updateCreationDateLabel(with: publicKey.creationDate)
-
- self.publicKey = publicKey
+ setPublicKeyTitle(string: displayKey, animated: animated)
+ updateCreationDateLabel(with: publicKey.creationDate)
+ } else {
+ setPublicKeyTitle(string: "-", animated: animated)
+ creationDateLabel.text = "-"
}
}
@@ -245,7 +219,7 @@ class WireguardKeysViewController: UIViewController {
.sink { (completion) in
switch completion {
case .finished:
- self.loadPublicKey(animated: true)
+ break
case .failure(let error):
os_log(.error, "Failed to re-generate the private key: %{public}s",