From 3cde0d954c43f84e9a343b4e85ccff97b98af996 Mon Sep 17 00:00:00 2001 From: Yishi Lin Date: Wed, 7 Jun 2017 02:12:54 +0800 Subject: [PATCH] Scan QR Code to import SSH private key --- pass/Base.lproj/Main.storyboard | 78 +++++++++++-- pass/Controllers/GitCredential.swift | 1 + ...SHKeyArmorSettingTableViewController.swift | 104 +++++++++++++++++- .../GitServerSettingTableViewController.swift | 3 +- pass/Helpers/Globals.swift | 2 - pass/Models/PasswordStore.swift | 12 +- 6 files changed, 175 insertions(+), 25 deletions(-) diff --git a/pass/Base.lproj/Main.storyboard b/pass/Base.lproj/Main.storyboard index 754bdea..60b3202 100644 --- a/pass/Base.lproj/Main.storyboard +++ b/pass/Base.lproj/Main.storyboard @@ -648,7 +648,7 @@ - + @@ -678,7 +678,7 @@ - + @@ -1028,7 +1028,7 @@ Phone Support PIN #: 84719 + + @@ -1670,7 +1680,7 @@ Cgo - + @@ -1700,7 +1710,7 @@ Cgo - + @@ -1716,6 +1726,52 @@ Cgo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pass/Controllers/GitCredential.swift b/pass/Controllers/GitCredential.swift index fd27805..17274bb 100644 --- a/pass/Controllers/GitCredential.swift +++ b/pass/Controllers/GitCredential.swift @@ -57,6 +57,7 @@ struct GitCredential { attempts += 1 lastPassword = newPassword credential = try? GTCredential(userName: userName, publicKeyURL: nil, privateKeyURL: privateKeyFile, passphrase: newPassword!) + print(privateKeyFile) } return credential } diff --git a/pass/Controllers/GitSSHKeyArmorSettingTableViewController.swift b/pass/Controllers/GitSSHKeyArmorSettingTableViewController.swift index 738a6f2..3e4eb53 100644 --- a/pass/Controllers/GitSSHKeyArmorSettingTableViewController.swift +++ b/pass/Controllers/GitSSHKeyArmorSettingTableViewController.swift @@ -9,17 +9,77 @@ import UIKit import SwiftyUserDefaults -class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextViewDelegate { +class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextViewDelegate, QRScannerControllerDelegate { @IBOutlet weak var armorPrivateKeyTextView: UITextView! + @IBOutlet weak var scanPrivateKeyCell: UITableViewCell! + var gitSSHPrivateKeyPassphrase: String? let passwordStore = PasswordStore.shared private var recentPastedText = "" + class ScannedSSHKey { + static let maxNumberOfGif = 100 + var numberOfSegments = 0 + var previousSegment = "" + var key = "" + var message = "" + var hasStarted = false + var isDone = false + + func reset() { + numberOfSegments = 0 + previousSegment = "" + key = "" + message = "Looking for the starting frame." + hasStarted = false + isDone = false + } + + func addSegment(segment: String) { + // skip duplicated segments + guard segment != previousSegment else { + return + } + previousSegment = segment + + // check whether we have found the first block + if hasStarted == false { + hasStarted = segment.contains("-----BEGIN") + } + guard hasStarted == true else { + return + } + + // check the number of segments + numberOfSegments = numberOfSegments + 1 + guard numberOfSegments <= ScannedSSHKey.maxNumberOfGif else { + key = "Too many QR codes" + return + } + + // update full text and check whether we are done + key.append(segment) + if let index1 = key.range(of: "-----END")?.lowerBound, + let _ = key.substring(from: index1).range(of: "KEY-----")?.lowerBound { + isDone = true + } + + // update message + message = "\(numberOfSegments) scanned QR codes." + } + } + var scanned = ScannedSSHKey() + override func viewDidLoad() { super.viewDidLoad() armorPrivateKeyTextView.text = Defaults[.gitSSHPrivateKeyArmor] armorPrivateKeyTextView.delegate = self + + scanPrivateKeyCell?.textLabel?.text = "Scan Private Key QR Codes" + scanPrivateKeyCell?.textLabel?.textColor = Globals.blue + scanPrivateKeyCell?.selectionStyle = .default + scanPrivateKeyCell?.accessoryType = .disclosureIndicator } @IBAction func doneButtonTapped(_ sender: Any) { @@ -46,4 +106,46 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie } return true } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let selectedCell = tableView.cellForRow(at: indexPath) + if selectedCell == scanPrivateKeyCell { + scanned.reset() + self.performSegue(withIdentifier: "showSSHScannerSegue", sender: self) + } + tableView.deselectRow(at: indexPath, animated: true) + } + + + // MARK: - QRScannerControllerDelegate Methods + func checkScannedOutput(line: String) -> (accept: Bool, message: String) { + scanned.addSegment(segment: line) + if scanned.isDone { + return (accept: true, message: "Done") + } else { + return (accept: false, message: scanned.message) + } + } + + // MARK: - QRScannerControllerDelegate Methods + func handleScannedOutput(line: String) { + armorPrivateKeyTextView.text = scanned.key + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + if segue.identifier == "showSSHScannerSegue" { + if let navController = segue.destination as? UINavigationController { + if let viewController = navController.topViewController as? QRScannerController { + viewController.delegate = self + } + } else if let viewController = segue.destination as? QRScannerController { + viewController.delegate = self + } + } + } + + @IBAction private func cancelSSHScanner(segue: UIStoryboardSegue) { + + } + } diff --git a/pass/Controllers/GitServerSettingTableViewController.swift b/pass/Controllers/GitServerSettingTableViewController.swift index 4e7b465..94667a1 100644 --- a/pass/Controllers/GitServerSettingTableViewController.swift +++ b/pass/Controllers/GitServerSettingTableViewController.swift @@ -120,8 +120,7 @@ class GitServerSettingTableViewController: UITableViewController { } private func gitSSHKeyExists() -> Bool { - return FileManager.default.fileExists(atPath: Globals.gitSSHPublicKeyPath) && - FileManager.default.fileExists(atPath: Globals.gitSSHPrivateKeyPath) + return FileManager.default.fileExists(atPath: Globals.gitSSHPrivateKeyPath) } func showSSHKeyActionSheet() { diff --git a/pass/Helpers/Globals.swift b/pass/Helpers/Globals.swift index 4ad58a6..7bb91db 100644 --- a/pass/Helpers/Globals.swift +++ b/pass/Helpers/Globals.swift @@ -15,9 +15,7 @@ class Globals { static let pgpPublicKeyPath = "\(documentPath)/gpg_key.pub" static let pgpPrivateKeyPath = "\(documentPath)/gpg_key" - static let gitSSHPublicKeyPath = "\(documentPath)/ssh_key.pub" static let gitSSHPrivateKeyPath = "\(documentPath)/ssh_key" - static let gitSSHPublicKeyURL = URL(fileURLWithPath: gitSSHPublicKeyPath) static let gitSSHPrivateKeyURL = URL(fileURLWithPath: gitSSHPrivateKeyPath) static let repositoryPath = "\(libraryPath)/password-store" diff --git a/pass/Models/PasswordStore.swift b/pass/Models/PasswordStore.swift index 74b2fbc..3a258c8 100644 --- a/pass/Models/PasswordStore.swift +++ b/pass/Models/PasswordStore.swift @@ -104,14 +104,10 @@ class PasswordStore { } public func initGitSSHKey(with armorKey: String, _ keyType: SSHKeyType) throws { - var keyPath = "" - switch keyType { - case .public: - keyPath = Globals.gitSSHPublicKeyPath - case .secret: - keyPath = Globals.gitSSHPrivateKeyPath + guard keyType == .secret else { + return } - + let keyPath = Globals.gitSSHPrivateKeyPath try armorKey.write(toFile: keyPath, atomically: true, encoding: .ascii) } @@ -692,7 +688,6 @@ class PasswordStore { Utils.removeFileIfExists(atPath: Globals.pgpPublicKeyPath) Utils.removeFileIfExists(atPath: Globals.pgpPrivateKeyPath) - Utils.removeFileIfExists(atPath: Globals.gitSSHPublicKeyPath) Utils.removeFileIfExists(atPath: Globals.gitSSHPrivateKeyPath) Utils.removeAllKeychain() @@ -805,7 +800,6 @@ class PasswordStore { } func removeGitSSHKeys() { - Utils.removeFileIfExists(atPath: Globals.gitSSHPublicKeyPath) Utils.removeFileIfExists(atPath: Globals.gitSSHPrivateKeyPath) Defaults.remove(.gitSSHPrivateKeyArmor) Defaults.remove(.gitSSHPrivateKeyURL)