Scan QR Code to import SSH private key
This commit is contained in:
parent
a31f5b797d
commit
3cde0d954c
6 changed files with 175 additions and 25 deletions
|
|
@ -648,7 +648,7 @@
|
|||
</objects>
|
||||
<point key="canvasLocation" x="4954" y="-1027"/>
|
||||
</scene>
|
||||
<!--Scan QR Codes-->
|
||||
<!--Scan OTP QR Codes-->
|
||||
<scene sceneID="AuR-rQ-G3V">
|
||||
<objects>
|
||||
<viewController id="A9p-Qb-WmU" customClass="OTPScannerController" customModule="pass" customModuleProvider="target" sceneMemberID="viewController">
|
||||
|
|
@ -678,7 +678,7 @@
|
|||
<constraint firstAttribute="trailingMargin" secondItem="lOI-p4-BGb" secondAttribute="trailing" constant="30" id="qNb-4K-GGl"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<navigationItem key="navigationItem" title="Scan QR Codes" id="Hlb-5I-bfE">
|
||||
<navigationItem key="navigationItem" title="Scan OTP QR Codes" id="Hlb-5I-bfE">
|
||||
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="KBZ-N2-OGE">
|
||||
<connections>
|
||||
<segue destination="0gD-ix-2NF" kind="unwind" unwindAction="cancelOTPScannerWithSegue:" id="nZe-B6-MNt"/>
|
||||
|
|
@ -1028,7 +1028,7 @@ Phone Support PIN #: 84719</string>
|
|||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Encrypt in ASCII-Armored" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Jwg-mt-woS">
|
||||
<rect key="frame" x="20" y="0.0" width="379" height="43.666666666666664"/>
|
||||
<rect key="frame" x="15" y="0.0" width="384" height="43.666666666666664"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
|
|
@ -1045,18 +1045,18 @@ Phone Support PIN #: 84719</string>
|
|||
<rect key="frame" x="0.0" y="155" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="SVj-jD-qPT" id="HaO-5w-qZt">
|
||||
<rect key="frame" x="0.0" y="0.0" width="376" height="43.666666666666664"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="381" height="43.666666666666664"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Git Signature" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="87a-xY-AbR">
|
||||
<rect key="frame" x="20.000000000000007" y="11.999999999999998" width="99.666666666666671" height="20.333333333333332"/>
|
||||
<rect key="frame" x="15.000000000000007" y="11.999999999999998" width="99.666666666666671" height="20.333333333333332"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Not Set" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="2qr-d7-0SK">
|
||||
<rect key="frame" x="318.33333333333331" y="11.999999999999998" width="57.666666666666664" height="20.333333333333332"/>
|
||||
<rect key="frame" x="321.33333333333331" y="11.999999999999998" width="57.666666666666664" height="20.333333333333332"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.5568627451" green="0.5568627451" blue="0.57647058819999997" alpha="1" colorSpace="calibratedRGB"/>
|
||||
|
|
@ -1080,7 +1080,7 @@ Phone Support PIN #: 84719</string>
|
|||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Discard All Local Changes" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="zrl-v3-fxg">
|
||||
<rect key="frame" x="20" y="0.0" width="379" height="43.666666666666664"/>
|
||||
<rect key="frame" x="15" y="0.0" width="384" height="43.666666666666664"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.50196081400000003" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
|
|
@ -1097,7 +1097,7 @@ Phone Support PIN #: 84719</string>
|
|||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Erase All Password Store Data" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="K2K-Bx-g7Z">
|
||||
<rect key="frame" x="20" y="0.0" width="379" height="43.666666666666664"/>
|
||||
<rect key="frame" x="15" y="0.0" width="384" height="43.666666666666664"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.50196081400000003" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
|
|
@ -1447,7 +1447,7 @@ Cgo
|
|||
<scene sceneID="zWR-BT-dXv">
|
||||
<objects>
|
||||
<tableViewController id="WgM-cY-mig" customClass="GitSSHKeyArmorSettingTableViewController" customModule="pass" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" allowsSelection="NO" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="1" id="e2Q-qC-LUk">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="1" id="e2Q-qC-LUk">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
|
|
@ -1522,6 +1522,14 @@ Cgo
|
|||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="jXO-n0-Mvx">
|
||||
<rect key="frame" x="0.0" y="404" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="jXO-n0-Mvx" id="AIL-mq-u7n">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
|
|
@ -1539,6 +1547,8 @@ Cgo
|
|||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="armorPrivateKeyTextView" destination="23o-MP-wQY" id="ybK-Ba-vJt"/>
|
||||
<outlet property="scanPrivateKeyCell" destination="jXO-n0-Mvx" id="Zdb-wm-7Ls"/>
|
||||
<segue destination="lLV-kB-JkG" kind="show" identifier="showSSHScannerSegue" id="orz-gu-cTp"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="qJO-AN-K9p" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
|
|
@ -1670,7 +1680,7 @@ Cgo
|
|||
</objects>
|
||||
<point key="canvasLocation" x="4967" y="5333"/>
|
||||
</scene>
|
||||
<!--Scanner-->
|
||||
<!--Scan PGP Keys-->
|
||||
<scene sceneID="7j1-Qg-pUZ">
|
||||
<objects>
|
||||
<viewController id="LZE-gF-IcM" customClass="QRScannerController" customModule="pass" customModuleProvider="target" sceneMemberID="viewController">
|
||||
|
|
@ -1700,7 +1710,7 @@ Cgo
|
|||
<constraint firstItem="53A-gx-tky" firstAttribute="top" secondItem="U8O-Md-w8e" secondAttribute="bottom" constant="80" id="tzL-K0-lE7"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<navigationItem key="navigationItem" title="Scanner" id="JIs-3z-Tmr">
|
||||
<navigationItem key="navigationItem" title="Scan PGP Keys" id="JIs-3z-Tmr">
|
||||
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="yma-o8-xRu">
|
||||
<connections>
|
||||
<segue destination="0F8-Zv-c2C" kind="unwind" unwindAction="cancelPGPScannerWithSegue:" id="q14-fu-3N4"/>
|
||||
|
|
@ -1716,6 +1726,52 @@ Cgo
|
|||
</objects>
|
||||
<point key="canvasLocation" x="6122" y="4302"/>
|
||||
</scene>
|
||||
<!--Scan SSH Keys-->
|
||||
<scene sceneID="kmR-CX-Yi7">
|
||||
<objects>
|
||||
<viewController id="lLV-kB-JkG" customClass="QRScannerController" customModule="pass" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="NSI-Fp-tX4"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="QZv-Im-xZk"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="GYE-sh-yvu">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="scanner output" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fsL-pq-A5q">
|
||||
<rect key="frame" x="50" y="567" width="314" height="45"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.5" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="45" id="IPT-kM-arz"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="QZv-Im-xZk" firstAttribute="top" secondItem="fsL-pq-A5q" secondAttribute="bottom" constant="80" id="76J-7g-eVO"/>
|
||||
<constraint firstItem="fsL-pq-A5q" firstAttribute="leading" secondItem="GYE-sh-yvu" secondAttribute="leadingMargin" constant="30" id="IOo-JD-FMG"/>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="fsL-pq-A5q" secondAttribute="trailing" constant="30" id="ymC-G4-bdA"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<navigationItem key="navigationItem" title="Scan SSH Keys" id="bov-FI-Hkg">
|
||||
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="JhH-TO-YkO">
|
||||
<connections>
|
||||
<segue destination="J2S-Mr-s10" kind="unwind" unwindAction="cancelSSHScannerWithSegue:" id="Thj-zj-TEX"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="scannerOutput" destination="fsL-pq-A5q" id="lSv-2S-qVd"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="s1p-jn-4PV" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
<exit id="J2S-Mr-s10" userLabel="Exit" sceneMemberID="exit"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="7133" y="2895"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="Lock" width="25" height="25"/>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue