From b10078af517488245cbe752a699785a3696dde63 Mon Sep 17 00:00:00 2001 From: Bob Sun Date: Sat, 17 Mar 2018 15:37:03 -0700 Subject: [PATCH 1/9] Update url in the Matchfile --- fastlane/Matchfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastlane/Matchfile b/fastlane/Matchfile index 7a59cec..b5aee64 100644 --- a/fastlane/Matchfile +++ b/fastlane/Matchfile @@ -1,4 +1,4 @@ -git_url "https://github.com/mssun/certificates-passforios.git" +git_url "git@github.com:mssun/certificates-passforios.git" type "development" # The default type, can be: appstore, adhoc, enterprise or development From d8f7067d7119c34d00ebf4993e7028406d4fc7bb Mon Sep 17 00:00:00 2001 From: Bob Sun Date: Sat, 17 Mar 2018 17:44:13 -0700 Subject: [PATCH 2/9] Add libxml2 header path --- pass.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index f07384e..4081695 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -1441,6 +1441,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = "$(SDKROOT)/usr/include/libxml2"; IPHONEOS_DEPLOYMENT_TARGET = 10.2; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; @@ -1492,6 +1493,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = "$(SDKROOT)/usr/include/libxml2"; IPHONEOS_DEPLOYMENT_TARGET = 10.2; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = me.mssun.passforios; From d30c62993a6d150acbe60cee8ee10ce316fc114b Mon Sep 17 00:00:00 2001 From: Bob Sun Date: Sat, 17 Mar 2018 17:45:03 -0700 Subject: [PATCH 3/9] Update FavIcon --- Cartfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cartfile b/Cartfile index 42a324b..8a5489e 100644 --- a/Cartfile +++ b/Cartfile @@ -3,6 +3,6 @@ github "radex/SwiftyUserDefaults" github "libgit2/objective-git" # github "zahlz/SwiftPasscodeLock" "master" github "yishilin14/SwiftPasscodeLock" "app-extension-support" -github "bitserf/FavIcon" +github "mssun/FavIcon" "master" github "kishikawakatsumi/KeychainAccess" github "mattrubin/OneTimePassword" From 853cb2eaf5c3fbfac581173ca1fdad03e9b09ecf Mon Sep 17 00:00:00 2001 From: Bob Sun Date: Sat, 17 Mar 2018 17:54:33 -0700 Subject: [PATCH 4/9] Update Podfile --- Podfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Podfile b/Podfile index a95879a..aa80ddc 100644 --- a/Podfile +++ b/Podfile @@ -2,7 +2,7 @@ platform :ios, '10.2' use_frameworks! target 'passKit' do - pod 'ObjectivePGP', :git => 'https://github.com/krzyzanowskim/ObjectivePGP.git', :tag => '0.10.0-beta2' + pod 'ObjectivePGP', :git => 'https://github.com/krzyzanowskim/ObjectivePGP.git', :tag => '0.10.0-beta3' target 'pass' do inherit! :search_paths end From ab7e332bfb74d39e05a5e964e32cb63b62b03bfa Mon Sep 17 00:00:00 2001 From: Bob Sun Date: Sat, 17 Mar 2018 18:18:19 -0700 Subject: [PATCH 5/9] Revert "Update url in the Matchfile" This reverts commit b10078af517488245cbe752a699785a3696dde63. --- fastlane/Matchfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastlane/Matchfile b/fastlane/Matchfile index b5aee64..7a59cec 100644 --- a/fastlane/Matchfile +++ b/fastlane/Matchfile @@ -1,4 +1,4 @@ -git_url "git@github.com:mssun/certificates-passforios.git" +git_url "https://github.com/mssun/certificates-passforios.git" type "development" # The default type, can be: appstore, adhoc, enterprise or development From caa5ce77a3feb4694308b511d094b4ea39ed71b9 Mon Sep 17 00:00:00 2001 From: Danny Moesch Date: Mon, 26 Feb 2018 16:53:39 +0100 Subject: [PATCH 6/9] Use YAML library to parse password files --- Cartfile | 1 + pass.xcodeproj/project.pbxproj | 5 ++ ...nSourceComponentsTableViewController.swift | 3 ++ .../PasswordEditorTableViewController.swift | 2 +- passKit/Models/Password.swift | 48 ++++++++++++------- 5 files changed, 42 insertions(+), 17 deletions(-) diff --git a/Cartfile b/Cartfile index 8a5489e..60924ca 100644 --- a/Cartfile +++ b/Cartfile @@ -6,3 +6,4 @@ github "yishilin14/SwiftPasscodeLock" "app-extension-support" github "mssun/FavIcon" "master" github "kishikawakatsumi/KeychainAccess" github "mattrubin/OneTimePassword" +github "jpsim/Yams" diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index 4081695..b58cef1 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 18F19A67B0C07F13C17169E0 /* Pods_pass.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A5620D17DF5E86B61761D0E /* Pods_pass.framework */; }; 23B82F0228254275DBA609E7 /* Pods_passExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B975797E0F0B7476CADD6A7D /* Pods_passExtension.framework */; }; + 3012B06D2039D6E400BE1793 /* Yams.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3012B06C2039D6E400BE1793 /* Yams.framework */; }; 61326CDA7A73757FB68DCB04 /* Pods_passKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAB3F5541E51ADC8C6B56642 /* Pods_passKit.framework */; }; A20691F41F2A3D0E0096483D /* SecurePasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20691F31F2A3D0E0096483D /* SecurePasteboard.swift */; }; A2168A7F1EFD40D5005EA873 /* OnePasswordExtensionConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2168A7E1EFD40D5005EA873 /* OnePasswordExtensionConstants.swift */; }; @@ -157,6 +158,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 3012B06C2039D6E400BE1793 /* Yams.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Yams.framework; path = Carthage/Build/iOS/Yams.framework; sourceTree = ""; }; 31C3033E8868D05B2C55C8B1 /* Pods-passExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-passExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-passExtension/Pods-passExtension.debug.xcconfig"; sourceTree = ""; }; 3A5620D17DF5E86B61761D0E /* Pods_pass.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_pass.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 666769E0B255666D02945C15 /* Pods-passKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-passKitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-passKitTests/Pods-passKitTests.release.xcconfig"; sourceTree = ""; }; @@ -317,6 +319,7 @@ DCC408C71E307DBB00F29B0E /* SVProgressHUD.framework in Frameworks */, DC193FFC1E49E0340077E0A3 /* PasscodeLock.framework in Frameworks */, 18F19A67B0C07F13C17169E0 /* Pods_pass.framework in Frameworks */, + 3012B06D2039D6E400BE1793 /* Yams.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -542,6 +545,7 @@ DC917BED1E2F38C4000FDF54 /* Frameworks */ = { isa = PBXGroup; children = ( + 3012B06C2039D6E400BE1793 /* Yams.framework */, A2A61C101EEF8E3500CFE063 /* libPods-passKit.a */, A2A61C0C1EEF8DFE00CFE063 /* libPods-passExtension.a */, A2227D541EEE5E78002A69A9 /* libObjectivePGP.a */, @@ -989,6 +993,7 @@ "$(SRCROOT)/Carthage/Build/iOS/KeychainAccess.framework", "$(SRCROOT)/Carthage/Build/iOS/OneTimePassword.framework", "$(SRCROOT)/Carthage/Build/iOS/Base32.framework", + "$(SRCROOT)/Carthage/Build/iOS/Yams.framework", ); name = "Run Script"; outputPaths = ( diff --git a/pass/Controllers/OpenSourceComponentsTableViewController.swift b/pass/Controllers/OpenSourceComponentsTableViewController.swift index b67fb40..b220434 100644 --- a/pass/Controllers/OpenSourceComponentsTableViewController.swift +++ b/pass/Controllers/OpenSourceComponentsTableViewController.swift @@ -35,6 +35,9 @@ class OpenSourceComponentsTableViewController: BasicStaticTableViewController { ["SVProgressHUD", "https://github.com/SVProgressHUD/SVProgressHUD", "https://github.com/SVProgressHUD/SVProgressHUD/blob/master/LICENSE.txt"], + ["Yams", + "https://github.com/jpsim/Yams", + "https://github.com/jpsim/Yams/blob/master/LICENSE"], ] override func viewDidLoad() { diff --git a/pass/Controllers/PasswordEditorTableViewController.swift b/pass/Controllers/PasswordEditorTableViewController.swift index 7ed2ed6..e03eb48 100644 --- a/pass/Controllers/PasswordEditorTableViewController.swift +++ b/pass/Controllers/PasswordEditorTableViewController.swift @@ -28,7 +28,7 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl private var navigationItemTitle: String? private var sectionHeaderTitles = ["name", "password", "additions",""].map {$0.uppercased()} - private var sectionFooterTitles = ["", "", "Use \"key: value\" format for additional fields.", ""] + private var sectionFooterTitles = ["", "", "Use YAML format for additional fields.", ""] private let nameSection = 0 private let passwordSection = 1 private let additionsSection = 2 diff --git a/passKit/Models/Password.swift b/passKit/Models/Password.swift index cae9fee..9a9c15f 100644 --- a/passKit/Models/Password.swift +++ b/passKit/Models/Password.swift @@ -10,6 +10,7 @@ import Foundation import SwiftyUserDefaults import OneTimePassword import Base32 +import Yams struct AdditionField { var title: String @@ -84,29 +85,25 @@ public class Password { additions.removeAll() // split the plain text - let plainTextSplit = plainText.split(maxSplits: 1, omittingEmptySubsequences: false) { + let plainTextSplit = self.plainText.split(maxSplits: 1, omittingEmptySubsequences: false) { $0 == "\n" || $0 == "\r\n" }.map(String.init) // get password password = plainTextSplit.first ?? "" - // get additonal fields - if plainTextSplit.count == 2 { - var unknownIndex = 0 - plainTextSplit[1].enumerateLines() { line, _ in - if !line.isEmpty { - var (key, value) = Password.getKeyValuePair(from: line) - if key == nil { - unknownIndex += 1 - key = "unknown \(unknownIndex)" - } - self.additions.append((key!, value)) - } - } + // get remaining lines + let additionalFields = plainTextSplit.last ?? "" + + // try to interpret the text format as YAML first + do { + try getAdditionalFields(fromYaml: additionalFields) } - - // check whether the first line of the plainText looks like an otp entry + catch { + getAdditionalFields(fromPlainText: additionalFields) + } + + // check whether the first line looks like an otp entry let (key, value) = Password.getKeyValuePair(from: self.password) if Password.otpKeywords.contains(key ?? "") { firstLineIsOTPField = true @@ -118,6 +115,25 @@ public class Password { // construct the otp token self.updateOtpToken() } + + private func getAdditionalFields(fromYaml: String) throws { + let yamlFile = try Yams.load(yaml: fromYaml) as! [String: Any] + additions.append(contentsOf: yamlFile.map { ($0, String(describing: $1)) }) + } + + private func getAdditionalFields(fromPlainText: String) { + var unknownIndex = 0 + fromPlainText.enumerateLines() { line, _ in + if !line.isEmpty { + var (key, value) = Password.getKeyValuePair(from: line) + if key == nil { + unknownIndex += 1 + key = "unknown \(unknownIndex)" + } + self.additions.append((key!, value)) + } + } + } public func getFilteredAdditions() -> [(String, String)] { var filteredAdditions = [(String, String)]() From 69bafeb672c1b9da8c342ddfd283540822e7e124 Mon Sep 17 00:00:00 2001 From: Danny Moesch Date: Sun, 11 Mar 2018 12:57:13 +0100 Subject: [PATCH 7/9] Introduce class constants for the strings "otpauth" and "otpauth://" --- passKit/Models/Password.swift | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/passKit/Models/Password.swift b/passKit/Models/Password.swift index 9a9c15f..d5828ae 100644 --- a/passKit/Models/Password.swift +++ b/passKit/Models/Password.swift @@ -25,6 +25,8 @@ enum PasswordChange: Int { public class Password { public static let otpKeywords = ["otp_secret", "otp_type", "otp_algorithm", "otp_period", "otp_digits", "otp_counter", "otpauth"] + private static let OTPAUTH = "otpauth" + private static let OTPAUTH_URL_START = "\(OTPAUTH)://" public var name = "" public var url: URL? @@ -169,8 +171,8 @@ public class Password { // no ": " found, or empty on both sides of ": " value = line // otpauth special case - if value.hasPrefix("otpauth://") { - key = "otpauth" + if value.hasPrefix(Password.OTPAUTH_URL_START) { + key = Password.OTPAUTH } } else { if !items[0].isEmpty { @@ -237,9 +239,9 @@ public class Password { self.otpToken = nil // get otpauth, if we are able to generate a token, return - if var otpauthString = getAdditionValue(withKey: "otpauth") { - if !otpauthString.hasPrefix("otpauth:") { - otpauthString = "otpauth:\(otpauthString)" + if var otpauthString = getAdditionValue(withKey: Password.OTPAUTH) { + if !otpauthString.hasPrefix("\(Password.OTPAUTH):") { + otpauthString = "\(Password.OTPAUTH):\(otpauthString)" } if let otpauthUrl = URL(string: otpauthString), let token = Token(url: otpauthUrl) { @@ -363,7 +365,7 @@ public class Password { let (key, _) = Password.getKeyValuePair(from: line) if !Password.otpKeywords.contains(key ?? "") { lines.append(line) - } else if key == "otpauth" && newOtpauth != nil { + } else if key == Password.OTPAUTH && newOtpauth != nil { lines.append(newOtpauth!) // set to nil to prevent duplication newOtpauth = nil From 673aee1e7af3665461e66faf86fe2f41a2bbaf7a Mon Sep 17 00:00:00 2001 From: Danny Moesch Date: Sun, 11 Mar 2018 12:59:10 +0100 Subject: [PATCH 8/9] Protect YAML parser against empty input string --- passKit/Models/Password.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/passKit/Models/Password.swift b/passKit/Models/Password.swift index d5828ae..c23de5a 100644 --- a/passKit/Models/Password.swift +++ b/passKit/Models/Password.swift @@ -119,6 +119,7 @@ public class Password { } private func getAdditionalFields(fromYaml: String) throws { + guard !fromYaml.isEmpty else { return } let yamlFile = try Yams.load(yaml: fromYaml) as! [String: Any] additions.append(contentsOf: yamlFile.map { ($0, String(describing: $1)) }) } From 3a0e6fa3f6cbccf8f2d6340559e6aab2d128ab62 Mon Sep 17 00:00:00 2001 From: Danny Moesch Date: Sun, 11 Mar 2018 13:01:46 +0100 Subject: [PATCH 9/9] Consider OTP tokens as possible lines in password files --- passKit/Models/Password.swift | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/passKit/Models/Password.swift b/passKit/Models/Password.swift index c23de5a..d610e84 100644 --- a/passKit/Models/Password.swift +++ b/passKit/Models/Password.swift @@ -87,7 +87,7 @@ public class Password { additions.removeAll() // split the plain text - let plainTextSplit = self.plainText.split(maxSplits: 1, omittingEmptySubsequences: false) { + let plainTextSplit = self.plainText.split(omittingEmptySubsequences: true) { $0 == "\n" || $0 == "\r\n" }.map(String.init) @@ -95,16 +95,25 @@ public class Password { password = plainTextSplit.first ?? "" // get remaining lines - let additionalFields = plainTextSplit.last ?? "" - + let additionalLines = plainTextSplit[1...] + + // separate normal lines (no otp tokens) + let normalAdditionalLines = additionalLines.filter { + !$0.hasPrefix(Password.OTPAUTH_URL_START) + }.joined(separator: "\n") + // try to interpret the text format as YAML first do { - try getAdditionalFields(fromYaml: additionalFields) + try getAdditionalFields(fromYaml: normalAdditionalLines) } catch { - getAdditionalFields(fromPlainText: additionalFields) + getAdditionalFields(fromPlainText: normalAdditionalLines) } + // get and append otp tokens + let otpAdditionalLines = additionalLines.filter { $0.hasPrefix(Password.OTPAUTH_URL_START) } + otpAdditionalLines.forEach { self.additions.append((Password.OTPAUTH, $0)) } + // check whether the first line looks like an otp entry let (key, value) = Password.getKeyValuePair(from: self.password) if Password.otpKeywords.contains(key ?? "") {