PrivateDataConfirmation: prompt with touch/face/pin/password ID for viewing/exporting keys

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jason A. Donenfeld
2019-02-06 03:23:51 +01:00
parent ed8dc516dc
commit bebcaa012b
9 changed files with 105 additions and 40 deletions
@@ -316,3 +316,10 @@
"macAppVersion (%@)" = "App version: %@";
"macGoBackendVersion (%@)" = "Go backend version: %@";
// Privacy
"macExportPrivateData" = "export tunnel private keys";
"macViewPrivateData" = "view tunnel private keys";
"iosExportPrivateData" = "Authenticate to export tunnel private keys.";
"iosViewPrivateData" = "Authenticate to view tunnel private keys.";
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
import Foundation
import LocalAuthentication
#if os(macOS)
import AppKit
#endif
class PrivateDataConfirmation {
static func confirmAccess(to reason: String, _ after: @escaping () -> Void) {
let context = LAContext()
var error: NSError?
if !context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) {
guard let error = error as? LAError else { return }
if error.code == .passcodeNotSet {
// We give no protection to folks who just don't set a passcode.
after()
}
return
}
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason) { success, _ in
DispatchQueue.main.async {
#if os(macOS)
if !NSApp.isActive {
NSApp.activate(ignoringOtherApps: true)
}
#endif
if success {
after()
}
}
}
}
}
+2
View File
@@ -122,6 +122,8 @@
</dict>
</dict>
</array>
<key>NSFaceIDUsageDescription</key>
<string>Face ID is used for authenticating viewing and exporting of private keys</string>
<key>com.wireguard.ios.app_group_id</key>
<string>group.$(APP_ID_IOS)</string>
</dict>
@@ -86,22 +86,25 @@ class SettingsTableViewController: UITableViewController {
}
func exportConfigurationsAsZipFile(sourceView: UIView) {
guard let tunnelsManager = tunnelsManager else { return }
guard let destinationDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
PrivateDataConfirmation.confirmAccess(to: tr("iosExportPrivateData")) { [weak self] in
guard let self = self else { return }
guard let tunnelsManager = self.tunnelsManager else { return }
guard let destinationDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let destinationURL = destinationDir.appendingPathComponent("wireguard-export.zip")
_ = FileManager.deleteFile(at: destinationURL)
let destinationURL = destinationDir.appendingPathComponent("wireguard-export.zip")
_ = FileManager.deleteFile(at: destinationURL)
let count = tunnelsManager.numberOfTunnels()
let tunnelConfigurations = (0 ..< count).compactMap { tunnelsManager.tunnel(at: $0).tunnelConfiguration }
ZipExporter.exportConfigFiles(tunnelConfigurations: tunnelConfigurations, to: destinationURL) { [weak self] error in
if let error = error {
ErrorPresenter.showErrorAlert(error: error, from: self)
return
let count = tunnelsManager.numberOfTunnels()
let tunnelConfigurations = (0 ..< count).compactMap { tunnelsManager.tunnel(at: $0).tunnelConfiguration }
ZipExporter.exportConfigFiles(tunnelConfigurations: tunnelConfigurations, to: destinationURL) { [weak self] error in
if let error = error {
ErrorPresenter.showErrorAlert(error: error, from: self)
return
}
let fileExportVC = UIDocumentPickerViewController(url: destinationURL, in: .exportToService)
self?.present(fileExportVC, animated: true, completion: nil)
}
let fileExportVC = UIDocumentPickerViewController(url: destinationURL, in: .exportToService)
self?.present(fileExportVC, animated: true, completion: nil)
}
}
@@ -103,11 +103,14 @@ class TunnelDetailTableViewController: UITableViewController {
}
@objc func editTapped() {
let editVC = TunnelEditTableViewController(tunnelsManager: tunnelsManager, tunnel: tunnel)
editVC.delegate = self
let editNC = UINavigationController(rootViewController: editVC)
editNC.modalPresentationStyle = .formSheet
present(editNC, animated: true)
PrivateDataConfirmation.confirmAccess(to: tr("iosViewPrivateData")) { [weak self] in
guard let self = self else { return }
let editVC = TunnelEditTableViewController(tunnelsManager: self.tunnelsManager, tunnel: self.tunnel)
editVC.delegate = self
let editNC = UINavigationController(rootViewController: editVC)
editNC.modalPresentationStyle = .formSheet
self.present(editNC, animated: true)
}
}
func showConfirmationAlert(message: String, buttonTitle: String, from sourceView: UIView, onConfirmed: @escaping (() -> Void)) {
@@ -227,10 +227,13 @@ class TunnelDetailTableViewController: NSViewController {
}
@objc func handleEditTunnelAction() {
let tunnelEditVC = TunnelEditViewController(tunnelsManager: tunnelsManager, tunnel: tunnel)
tunnelEditVC.delegate = self
presentAsSheet(tunnelEditVC)
self.tunnelEditVC = tunnelEditVC
PrivateDataConfirmation.confirmAccess(to: tr("macViewPrivateData")) { [weak self] in
guard let self = self else { return }
let tunnelEditVC = TunnelEditViewController(tunnelsManager: self.tunnelsManager, tunnel: self.tunnel)
tunnelEditVC.delegate = self
self.presentAsSheet(tunnelEditVC)
self.tunnelEditVC = tunnelEditVC
}
}
@objc func handleToggleActiveStatusAction() {
@@ -207,22 +207,26 @@ class TunnelsListTableViewController: NSViewController {
}
@objc func handleExportTunnelsAction() {
guard let window = view.window else { return }
let savePanel = NSSavePanel()
savePanel.allowedFileTypes = ["zip"]
savePanel.prompt = tr("macSheetButtonExportZip")
savePanel.nameFieldLabel = tr("macNameFieldExportZip")
savePanel.nameFieldStringValue = "wireguard-export.zip"
savePanel.beginSheetModal(for: window) { [weak tunnelsManager] response in
guard let tunnelsManager = tunnelsManager else { return }
guard response == .OK else { return }
guard let destinationURL = savePanel.url else { return }
let count = tunnelsManager.numberOfTunnels()
let tunnelConfigurations = (0 ..< count).compactMap { tunnelsManager.tunnel(at: $0).tunnelConfiguration }
ZipExporter.exportConfigFiles(tunnelConfigurations: tunnelConfigurations, to: destinationURL) { [weak self] error in
if let error = error {
ErrorPresenter.showErrorAlert(error: error, from: self)
return
PrivateDataConfirmation.confirmAccess(to: tr("macExportPrivateData")) { [weak self] in
guard let self = self else { return }
guard let window = self.view.window else { return }
let savePanel = NSSavePanel()
savePanel.allowedFileTypes = ["zip"]
savePanel.prompt = tr("macSheetButtonExportZip")
savePanel.nameFieldLabel = tr("macNameFieldExportZip")
savePanel.nameFieldStringValue = "wireguard-export.zip"
let tunnelsManager = self.tunnelsManager
savePanel.beginSheetModal(for: window) { [weak tunnelsManager] response in
guard let tunnelsManager = tunnelsManager else { return }
guard response == .OK else { return }
guard let destinationURL = savePanel.url else { return }
let count = tunnelsManager.numberOfTunnels()
let tunnelConfigurations = (0 ..< count).compactMap { tunnelsManager.tunnel(at: $0).tunnelConfiguration }
ZipExporter.exportConfigFiles(tunnelConfigurations: tunnelConfigurations, to: destinationURL) { [weak self] error in
if let error = error {
ErrorPresenter.showErrorAlert(error: error, from: self)
return
}
}
}
}