From c1376f873a7edf6ce398bccd15e4febf6b5f1f6b Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Sat, 4 Feb 2023 14:13:26 -0800 Subject: [PATCH 01/72] Version bump to 0.14.0 --- pass.xcodeproj/project.pbxproj | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index 1cef924e..8fc66b24 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -1850,7 +1850,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.13.0; + MARKETING_VERSION = 0.14.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = me.mssun.passforios.shortcuts; @@ -1885,7 +1885,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.13.0; + MARKETING_VERSION = 0.14.0; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = me.mssun.passforios.shortcuts; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1982,7 +1982,7 @@ "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 0.13.0; + MARKETING_VERSION = 0.14.0; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = "${inherited}"; OTHER_SWIFT_FLAGS = "$(inherited)"; @@ -2043,7 +2043,7 @@ "@executable_path/../../Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 0.13.0; + MARKETING_VERSION = 0.14.0; OTHER_CFLAGS = "$(inherited)"; OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER).find-login-action-extension"; @@ -2078,7 +2078,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 0.13.0; + MARKETING_VERSION = 0.14.0; MODULEMAP_FILE = ""; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)"; @@ -2143,7 +2143,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.13.0; + MARKETING_VERSION = 0.14.0; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = "me.mssun.passforiosbeta.auto-fill-credential-extension"; @@ -2179,7 +2179,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.13.0; + MARKETING_VERSION = 0.14.0; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = me.mssun.passforiosbeta.shortcuts; @@ -2214,7 +2214,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.13.0; + MARKETING_VERSION = 0.14.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "me.mssun.passforios.auto-fill-credential-extension"; @@ -2250,7 +2250,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.13.0; + MARKETING_VERSION = 0.14.0; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "me.mssun.passforios.auto-fill-credential-extension"; PRODUCT_NAME = passAutoFillExtension; @@ -2283,7 +2283,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 0.13.0; + MARKETING_VERSION = 0.14.0; MODULEMAP_FILE = ""; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)"; @@ -2323,7 +2323,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 0.13.0; + MARKETING_VERSION = 0.14.0; MODULEMAP_FILE = ""; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)"; @@ -2407,7 +2407,7 @@ "@executable_path/../../Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 0.13.0; + MARKETING_VERSION = 0.14.0; OTHER_CFLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER).find-login-action-extension"; PRODUCT_NAME = passExtension; @@ -2442,7 +2442,7 @@ "@executable_path/../../Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 0.13.0; + MARKETING_VERSION = 0.14.0; OTHER_CFLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER).find-login-action-extension"; PRODUCT_NAME = passExtension; @@ -2657,7 +2657,7 @@ "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 0.13.0; + MARKETING_VERSION = 0.14.0; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = "${inherited}"; PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)"; @@ -2691,7 +2691,7 @@ "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 0.13.0; + MARKETING_VERSION = 0.14.0; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = "${inherited}"; OTHER_SWIFT_FLAGS = ""; From e6c9440bff737b0d0a20e87dc7aedf7a9f079df2 Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Sat, 4 Feb 2023 14:14:57 -0800 Subject: [PATCH 02/72] swiftlint 0.50.* is required --- scripts/swiftlint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/swiftlint.sh b/scripts/swiftlint.sh index aefdf847..7118e4de 100755 --- a/scripts/swiftlint.sh +++ b/scripts/swiftlint.sh @@ -1,6 +1,6 @@ export PATH="/opt/homebrew/bin:/opt/homebrew/sbin${PATH+:$PATH}" -SWIFTLINT_VERSION="0.49.*" +SWIFTLINT_VERSION="0.50.*" if [[ "${CI}" == "true" ]]; then echo "Running in a Continuous Integration environment. Linting is skipped." From f6b2316324bf2efb0447b7a0808b5ce0e745db5b Mon Sep 17 00:00:00 2001 From: Anton Krasovsky Date: Fri, 30 Dec 2022 02:22:45 +0000 Subject: [PATCH 03/72] Add support for Yubikey command chaining --- .../PasswordDetailTableViewController.swift | 5 +- pass/Services/PasswordDecryptor.swift | 276 +++++++++++------- passKit/Helpers/YubiKeyAPDU.swift | 41 ++- 3 files changed, 213 insertions(+), 109 deletions(-) diff --git a/pass/Controllers/PasswordDetailTableViewController.swift b/pass/Controllers/PasswordDetailTableViewController.swift index 804c614e..7885d8e6 100644 --- a/pass/Controllers/PasswordDetailTableViewController.swift +++ b/pass/Controllers/PasswordDetailTableViewController.swift @@ -526,11 +526,12 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni } extension PasswordDetailTableViewController { - private func requestYubiKeyPIN(completion: @escaping (String) -> Void) { + private func requestYubiKeyPIN(completion: @escaping (String) -> Void, cancellation: @escaping () -> Void) { let alert = UIAlertController(title: "YubiKey PIN", message: "Verify YubiKey OpenPGP PIN.", preferredStyle: .alert) alert.addAction( UIAlertAction.cancel { _ in self.navigationController!.popViewController(animated: true) + cancellation() } ) alert.addAction( @@ -570,7 +571,7 @@ extension PasswordDetailTableViewController { } } - private func handleCancellation(_: Error) { + private func handleCancellation() { DispatchQueue.main.async { self.navigationController?.popViewController(animated: true) } diff --git a/pass/Services/PasswordDecryptor.swift b/pass/Services/PasswordDecryptor.swift index c97a7c22..aec53d44 100644 --- a/pass/Services/PasswordDecryptor.swift +++ b/pass/Services/PasswordDecryptor.swift @@ -55,7 +55,7 @@ func decryptPassword( } } -public typealias RequestPINAction = (@escaping (String) -> Void) -> Void +public typealias RequestPINAction = (@escaping (String) -> Void, @escaping () -> Void) -> Void let symmetricKeyIDNameDict: [UInt8: String] = [ 2: "3des", @@ -66,124 +66,194 @@ let symmetricKeyIDNameDict: [UInt8: String] = [ ] private func isEncryptKeyAlgoRSA(_ applicationRelatedData: Data) -> Bool { - if #available(iOS 13.0, *) { - let tlv = TKBERTLVRecord.sequenceOfRecords(from: applicationRelatedData)! - // 0x73: Discretionary data objects - for record in TKBERTLVRecord.sequenceOfRecords(from: tlv.first!.value)! where record.tag == 0x73 { - // 0xC2: Algorithm attributes decryption, 0x01: RSA - for record2 in TKBERTLVRecord.sequenceOfRecords(from: record.value)! where record2.tag == 0xC2 && record2.value.first! == 0x01 { - return true - } + let tlv = TKBERTLVRecord.sequenceOfRecords(from: applicationRelatedData)! + // 0x73: Discretionary data objects + for record in TKBERTLVRecord.sequenceOfRecords(from: tlv.first!.value)! where record.tag == 0x73 { + // 0xC2: Algorithm attributes decryption, 0x01: RSA + for record2 in TKBERTLVRecord.sequenceOfRecords(from: record.value)! where record2.tag == 0xC2 && record2.value.first! == 0x01 { + return true + } + } + return false +} + +private func getCapabilities(_ applicationRelatedData: Data) -> (Bool, Bool) { + let tlv = TKBERTLVRecord.sequenceOfRecords(from: applicationRelatedData)! + // 0x5f52: Historical Bytes + for record in TKBERTLVRecord.sequenceOfRecords(from: tlv.first!.value)! where record.tag == 0x5F52 { + let historical = record.value + if historical.count < 4 { + // log_error ("warning: historical bytes are too short\n"); + return (false, false) + } + + if historical[0] != 0 { + // log_error ("warning: bad category indicator in historical bytes\n"); + return (false, false) + } + + let dos = historical[1 ..< historical.endIndex - 3] + for record2 in TKCompactTLVRecord.sequenceOfRecords(from: dos)! where record2.tag == 7 && record2.value.count == 3 { + let cmd_chaining = (record2.value[2] & 0x80) != 0 + let ext_lc_le = (record2.value[2] & 0x40) != 0 + return (cmd_chaining, ext_lc_le) } - return false - } else { - // We need CryptoTokenKit (iOS 13.0+) to check if data is RSA, so fail open here. - return true } + return (false, false) } -// swiftlint:disable cyclomatic_complexity public func yubiKeyDecrypt( passwordEntity: PasswordEntity, requestPIN: @escaping RequestPINAction, errorHandler: @escaping ((AppError) -> Void), - cancellation: @escaping ((_ error: Error) -> Void), + cancellation: @escaping (() -> Void), completion: @escaping ((Password) -> Void) ) { - let encryptedDataPath = PasswordStore.shared.storeURL.appendingPathComponent(passwordEntity.getPath()) + Task { + do { + let encryptedDataPath = PasswordStore.shared.storeURL.appendingPathComponent(passwordEntity.getPath()) - guard let encryptedData = try? Data(contentsOf: encryptedDataPath) else { - errorHandler(AppError.other(message: "PasswordDoesNotExist".localize())) - return - } + guard let encryptedData = try? Data(contentsOf: encryptedDataPath) else { + errorHandler(AppError.other(message: "PasswordDoesNotExist".localize())) + return + } - // swiftlint:disable closure_body_length - requestPIN { pin in - // swiftlint:disable closure_body_length - passKit.YubiKeyConnection.shared.connection(cancellation: cancellation) { connection in - guard let smartCard = connection.smartCardInterface else { - errorHandler(AppError.yubiKey(.connection(message: "Failed to get smart card interface."))) + guard let pin = await readPin(requestPIN: requestPIN) else { return } - // 1. Select OpenPGP application - let selectOpenPGPAPDU = YubiKeyAPDU.selectOpenPGPApplication() - smartCard.selectApplication(selectOpenPGPAPDU) { _, error in - guard error == nil else { - errorHandler(AppError.yubiKey(.selectApplication(message: "Failed to select application."))) - return - } + guard let connection = try? await getConnection() else { + cancellation() + return + } - // 2. Verify PIN - let verifyApdu = YubiKeyAPDU.verify(password: pin) - smartCard.executeCommand(verifyApdu) { _, error in - guard error == nil else { - errorHandler(AppError.yubiKey(.verify(message: "Failed to verify PIN."))) - return - } - - let applicationRelatedDataApdu = YubiKeyAPDU.get_application_related_data() - smartCard.executeCommand(applicationRelatedDataApdu) { data, _ in - guard let data = data else { - errorHandler(AppError.yubiKey(.decipher(message: "Failed to get application related data."))) - return - } - - if !isEncryptKeyAlgoRSA(data) { - errorHandler(AppError.yubiKey(.decipher(message: "Encryption key algorithm is not supported. Supported algorithm: RSA."))) - return - } - - // 3. Decipher - let ciphertext = encryptedData - var error: NSError? - let message = CryptoNewPGPMessage(ciphertext) - guard let mpi1 = Gopenpgp.HelperPassGetEncryptedMPI1(message, &error) else { - errorHandler(AppError.yubiKey(.decipher(message: "Failed to get encrypted MPI."))) - return - } - - let decipherApdu = YubiKeyAPDU.decipher(data: mpi1) - smartCard.executeCommand(decipherApdu) { data, error in - guard let data = data else { - errorHandler(AppError.yubiKey(.decipher(message: "Failed to execute decipher."))) - return - } - - if #available(iOS 13.0, *) { - YubiKitManager.shared.stopNFCConnection() - } - guard let algoByte = data.first, let algo = symmetricKeyIDNameDict[algoByte] else { - errorHandler(AppError.yubiKey(.decipher(message: "Failed to new session key."))) - return - } - guard let session_key = Gopenpgp.CryptoNewSessionKeyFromToken(data[1 ..< data.count - 2], algo) else { - errorHandler(AppError.yubiKey(.decipher(message: "Failed to new session key."))) - return - } - - var error: NSError? - let message = CryptoNewPGPMessage(ciphertext) - - guard let plaintext = Gopenpgp.HelperPassDecryptWithSessionKey(message, session_key, &error)?.data else { - errorHandler(AppError.yubiKey(.decipher(message: "Failed to decrypt with session key."))) - return - } - - guard let plaintext_str = String(data: plaintext, encoding: .utf8) else { - errorHandler(AppError.yubiKey(.decipher(message: "Failed to convert plaintext to string."))) - return - } - - guard let password = try? Password(name: passwordEntity.getName(), url: passwordEntity.getURL(), plainText: plaintext_str) else { - errorHandler(AppError.yubiKey(.decipher(message: "Failed to construct password."))) - return - } - - completion(password) - } - } - } + guard let smartCard = connection.smartCardInterface else { + throw AppError.yubiKey(.connection(message: "Failed to get smart card interface.")) + } + + try await selectOpenPGPApplication(smartCard: smartCard) + + try await verifyPin(smartCard: smartCard, pin: pin) + + guard let applicationRelatedData = try await getApplicationRelatedData(smartCard: smartCard) else { + throw AppError.yubiKey(.decipher(message: "Failed to get application related data.")) + } + + if !isEncryptKeyAlgoRSA(applicationRelatedData) { + throw AppError.yubiKey(.decipher(message: "Encryption key algorithm is not supported. Supported algorithm: RSA.")) + } + + let (cmd_chaining, _) = getCapabilities(applicationRelatedData) + + let deciphered = try await decipher(smartCard: smartCard, ciphertext: encryptedData, chained: cmd_chaining) + + YubiKitManager.shared.stopNFCConnection() + + let plaintext = try decryptPassword(deciphered: deciphered, ciphertext: encryptedData) + guard let password = try? Password(name: passwordEntity.getName(), url: passwordEntity.getURL(), plainText: plaintext) else { + throw AppError.yubiKey(.decipher(message: "Failed to construct password.")) + } + + completion(password) + } catch let error as AppError { + errorHandler(error) + } catch { + errorHandler(AppError.other(message: String(describing: error))) + } + } +} + +func readPin(requestPIN: @escaping RequestPINAction) async -> String? { + await withCheckedContinuation { (continuation: CheckedContinuation) in + DispatchQueue.main.async { + requestPIN({ pin in continuation.resume(returning: pin) }, { continuation.resume(returning: nil) }) + } + } +} + +func getConnection() async throws -> YKFConnectionProtocol? { + try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in + passKit.YubiKeyConnection.shared.connection(cancellation: { error in + continuation.resume(throwing: error) + }, completion: { connection in + continuation.resume(returning: connection) + }) + } +} + +func selectOpenPGPApplication(smartCard: YKFSmartCardInterface) async throws { + if await withCheckedContinuation({ (continuation: CheckedContinuation) in + smartCard.selectApplication(YubiKeyAPDU.selectOpenPGPApplication()) { _, error in + continuation.resume(returning: error) + } + }) != nil { + throw AppError.yubiKey(.selectApplication(message: "Failed to select application.")) + } +} + +func getApplicationRelatedData(smartCard: YKFSmartCardInterface) async throws -> Data? { + try await executeCommandAsync(smartCard: smartCard, apdu: YubiKeyAPDU.get_application_related_data()) +} + +func verifyPin(smartCard: YKFSmartCardInterface, pin: String) async throws { + if await withCheckedContinuation({ (continuation: CheckedContinuation) in + smartCard.executeCommand(YubiKeyAPDU.verify(password: pin)) { _, error in + continuation.resume(returning: error) + }}) != nil { + throw AppError.yubiKey(.selectApplication(message: "Failed to verify PIN.")) + } +} + +func decipher(smartCard: YKFSmartCardInterface, ciphertext: Data, chained: Bool) async throws -> Data { + var error: NSError? + let message = CryptoNewPGPMessage(ciphertext) + guard let mpi1 = Gopenpgp.HelperPassGetEncryptedMPI1(message, &error) else { + throw AppError.yubiKey(.decipher(message: "Failed to get encrypted MPI.")) + } + + let apdus = chained ? YubiKeyAPDU.decipherChained(data: mpi1) : YubiKeyAPDU.decipherExtended(data: mpi1) + + for (idx, apdu) in apdus.enumerated() { + let data = try await executeCommandAsync(smartCard: smartCard, apdu: apdu) + // the last response must have the data + if idx == apdus.endIndex - 1, let data { + return data + } + } + + throw AppError.yubiKey(.verify(message: "Failed to execute decipher.")) +} + +func decryptPassword(deciphered: Data, ciphertext: Data) throws -> String { + let message = CryptoNewPGPMessage(ciphertext) + + guard let algoByte = deciphered.first, let algo = symmetricKeyIDNameDict[algoByte] else { + throw AppError.yubiKey(.decipher(message: "Failed to new session key.")) + } + + guard let session_key = Gopenpgp.CryptoNewSessionKeyFromToken(deciphered[1 ..< deciphered.count - 2], algo) else { + throw AppError.yubiKey(.decipher(message: "Failed to new session key.")) + } + + var error: NSError? + guard let plaintext = Gopenpgp.HelperPassDecryptWithSessionKey(message, session_key, &error)?.data else { + throw AppError.yubiKey(.decipher(message: "Failed to decrypt with session key: \(String(describing: error))")) + } + + guard let plaintext_str = String(data: plaintext, encoding: .utf8) else { + throw AppError.yubiKey(.decipher(message: "Failed to convert plaintext to string.")) + } + + return plaintext_str +} + +func executeCommandAsync(smartCard: YKFSmartCardInterface, apdu: YKFAPDU) async throws -> Data? { + try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in + smartCard.executeCommand(apdu) { data, error in + if let error { + continuation.resume(throwing: error) + } else { + continuation.resume(returning: data) } } } diff --git a/passKit/Helpers/YubiKeyAPDU.swift b/passKit/Helpers/YubiKeyAPDU.swift index 70821114..1e38870b 100644 --- a/passKit/Helpers/YubiKeyAPDU.swift +++ b/passKit/Helpers/YubiKeyAPDU.swift @@ -26,9 +26,9 @@ public enum YubiKeyAPDU { return verifyApdu } - public static func decipher(data: Data) -> YKFAPDU { + public static func decipherExtended(data: Data) -> [YKFAPDU] { var apdu: [UInt8] = [] - apdu += [0x00] // CLA + apdu += [0x00] // CLA (last or only command of a chain) apdu += [0x2A, 0x80, 0x86] // INS, P1, P2: PSO.DECIPHER // Lc, An extended Lc field consists of three bytes: // one byte set to '00' followed by two bytes not set to '0000' (1 to 65535 dec.). @@ -37,9 +37,32 @@ public enum YubiKeyAPDU { apdu += [0x00] apdu += data apdu += [0x02, 0x00] - let decipherApdu = YKFAPDU(data: Data(apdu))! - return decipherApdu + return [YKFAPDU(data: Data(apdu))!] + } + + public static func decipherChained(data: Data) -> [YKFAPDU] { + var result: [YKFAPDU] = [] + let chunks = chunk(data: data) + + for chunk in chunks.dropLast() { + var apdu: [UInt8] = [] + apdu += [0x10] // CLA (command is not the last command of a chain) + apdu += [0x2A, 0x80, 0x86] // INS, P1, P2: PSO.DECIPHER + apdu += withUnsafeBytes(of: UInt8(chunk.count).bigEndian, Array.init) + apdu += chunk + result += [YKFAPDU(data: Data(apdu))!] + } + + var apdu: [UInt8] = [] + apdu += [0x00] // CLA (last or only command of a chain) + apdu += [0x2A, 0x80, 0x86] // INS, P1, P2: PSO.DECIPHER + apdu += withUnsafeBytes(of: UInt8(chunks.last!.count).bigEndian, Array.init) + apdu += chunks.last! + apdu += [0x00] // Le + result += [YKFAPDU(data: Data(apdu))!] + + return result } public static func get_application_related_data() -> YKFAPDU { @@ -51,4 +74,14 @@ public enum YubiKeyAPDU { apdu += [0x00] return YKFAPDU(data: Data(apdu))! } + + static func chunk(data: Data) -> [[UInt8]] { + // starts with 00 padding + let padded: [UInt8] = [0x00] + data + let MAX_SIZE = 254 + + return stride(from: 0, to: padded.count, by: MAX_SIZE).map { + Array(padded[$0 ..< Swift.min($0 + MAX_SIZE, padded.count)]) + } + } } From 6fa4968bec91df829538392f67e43db40c4295f6 Mon Sep 17 00:00:00 2001 From: Anton Krasovsky Date: Fri, 20 Jan 2023 00:49:29 +0000 Subject: [PATCH 04/72] Update deployment target to 13.0 --- pass.xcodeproj/project.pbxproj | 48 +++++++++++++++++----------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index 8fc66b24..b373c278 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -1844,7 +1844,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = passShortcuts/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1879,7 +1879,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = passShortcuts/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1946,7 +1946,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = "$(SDKROOT)/usr/include/libxml2"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ""; MTL_ENABLE_DEBUG_INFO = NO; OTHER_SWIFT_FLAGS = "-D BETA"; @@ -1976,7 +1976,7 @@ FRAMEWORK_SEARCH_PATHS = "$(inherited)"; HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = pass/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2006,7 +2006,7 @@ FRAMEWORK_SEARCH_PATHS = "$(inherited)"; HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = passTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2036,7 +2036,7 @@ FRAMEWORK_SEARCH_PATHS = "$(inherited)"; HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = passExtension/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2075,7 +2075,7 @@ HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = passKit/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; MARKETING_VERSION = 0.14.0; @@ -2105,7 +2105,7 @@ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = passKitTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2137,7 +2137,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = passAutoFillExtension/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2173,7 +2173,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = passShortcuts/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2208,7 +2208,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = passAutoFillExtension/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2244,7 +2244,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = passAutoFillExtension/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2280,7 +2280,7 @@ HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = passKit/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; MARKETING_VERSION = 0.14.0; @@ -2320,7 +2320,7 @@ HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = passKit/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; MARKETING_VERSION = 0.14.0; @@ -2349,7 +2349,7 @@ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = passKitTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2371,7 +2371,7 @@ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = passKitTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2400,7 +2400,7 @@ FRAMEWORK_SEARCH_PATHS = "$(inherited)"; HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = passExtension/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2435,7 +2435,7 @@ FRAMEWORK_SEARCH_PATHS = "$(inherited)"; HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = passExtension/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2464,7 +2464,7 @@ FRAMEWORK_SEARCH_PATHS = "$(inherited)"; HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = passTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2487,7 +2487,7 @@ FRAMEWORK_SEARCH_PATHS = "$(inherited)"; HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = passTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2557,7 +2557,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = "$(SDKROOT)/usr/include/libxml2"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ""; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; @@ -2622,7 +2622,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = "$(SDKROOT)/usr/include/libxml2"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ""; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = me.mssun.passforios; @@ -2651,7 +2651,7 @@ FRAMEWORK_SEARCH_PATHS = "$(inherited)"; HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = pass/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2685,7 +2685,7 @@ FRAMEWORK_SEARCH_PATHS = "$(inherited)"; HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = pass/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", From 7000834575d1ad735aac83b3adc3526d97cc3765 Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Sat, 4 Feb 2023 15:25:14 -0800 Subject: [PATCH 05/72] Update README.md --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f3adfa4d..775e59a9 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,7 @@ Pass for iOS is available in App Store with the name "Pass - Password Store", an Download on the App Store

-You can also help us test beta versions through [TestFlight](https://testflight.apple.com/join/whK4zUFG) [^1]. - -[^1]: For iOS 12 users, you can download the TestFlight app by first "purchasing it" on a Mac using your Apple ID, then going to the purchased section of the App Store on your iOS device and downloading it from there. +You can also help us test beta versions through [TestFlight](https://testflight.apple.com/join/whK4zUFG). ## Features @@ -27,7 +25,8 @@ You can also help us test beta versions through [TestFlight](https://testflight. - Synchronize with your password Git repository. - User-friendly interface: search, long press to copy, copy and open link, etc. - Support one-time password tokens (two-factor authentication codes). -- Autofill in Safari/Chrome and [supported apps](https://github.com/agilebits/onepassword-app-extension). +- AutoFill in Safari/Chrome and [supported apps](https://github.com/agilebits/onepassword-app-extension). +- Support YubiKey. ## Screenshots From b09bc7444f0e753c035bbfc938b2c44028a7e63a Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Sat, 4 Feb 2023 15:27:37 -0800 Subject: [PATCH 06/72] Remove branches for iOS 12 compatibility --- .../PGPKeyFIleImportTableViewController.swift | 4 +-- .../PasswordDetailTableViewController.swift | 14 ++-------- .../PasswordNavigationViewController.swift | 18 ++++-------- .../SSHKeyFileImportTableViewController.swift | 4 +-- .../Controllers/PasswordsViewController.swift | 4 +-- passKit/Helpers/Colors.swift | 28 +++---------------- passKit/Helpers/YubiKeyConnection.swift | 4 +-- 7 files changed, 17 insertions(+), 59 deletions(-) diff --git a/pass/Controllers/PGPKeyFIleImportTableViewController.swift b/pass/Controllers/PGPKeyFIleImportTableViewController.swift index 58dd834f..90d5cbfc 100644 --- a/pass/Controllers/PGPKeyFIleImportTableViewController.swift +++ b/pass/Controllers/PGPKeyFIleImportTableViewController.swift @@ -35,9 +35,7 @@ class PGPKeyFileImportTableViewController: AutoCellHeightUITableViewController, return } picker.delegate = self - if #available(iOS 13.0, *) { - picker.shouldShowFileExtensions = true - } + picker.shouldShowFileExtensions = true present(picker, animated: true, completion: nil) } } diff --git a/pass/Controllers/PasswordDetailTableViewController.swift b/pass/Controllers/PasswordDetailTableViewController.swift index 7885d8e6..4a462d37 100644 --- a/pass/Controllers/PasswordDetailTableViewController.swift +++ b/pass/Controllers/PasswordDetailTableViewController.swift @@ -550,17 +550,9 @@ extension PasswordDetailTableViewController { switch error { case let .yubiKey(yubiKeyError): let errorMessage = yubiKeyError.localizedDescription - if #available(iOS 13.0, *) { - YubiKitManager.shared.stopNFCConnection(withErrorMessage: errorMessage) - DispatchQueue.main.async { - self.navigationController?.popViewController(animated: true) - } - } else { - DispatchQueue.main.async { - self.presentFailureAlert(message: errorMessage) { _ in - self.navigationController?.popViewController(animated: true) - } - } + YubiKitManager.shared.stopNFCConnection(withErrorMessage: errorMessage) + DispatchQueue.main.async { + self.navigationController?.popViewController(animated: true) } default: DispatchQueue.main.async { diff --git a/pass/Controllers/PasswordNavigationViewController.swift b/pass/Controllers/PasswordNavigationViewController.swift index 7ac2839d..2030e194 100644 --- a/pass/Controllers/PasswordNavigationViewController.swift +++ b/pass/Controllers/PasswordNavigationViewController.swift @@ -34,9 +34,7 @@ class PasswordNavigationViewController: UIViewController { uiSearchController.obscuresBackgroundDuringPresentation = false uiSearchController.searchBar.sizeToFit() uiSearchController.searchBar.returnKeyType = .done - if #available(iOS 13.0, *) { - uiSearchController.searchBar.searchTextField.clearButtonMode = .whileEditing - } + uiSearchController.searchBar.searchTextField.clearButtonMode = .whileEditing return uiSearchController }() @@ -50,15 +48,11 @@ class PasswordNavigationViewController: UIViewController { lazy var addPasswordUIBarButtonItem: UIBarButtonItem = { var addPasswordUIBarButtonItem = UIBarButtonItem() - if #available(iOS 13.0, *) { - let addPasswordButton = UIButton(type: .system) - let plusImage = UIImage(systemName: "plus.circle", withConfiguration: UIImage.SymbolConfiguration(weight: .regular)) - addPasswordButton.setImage(plusImage, for: .normal) - addPasswordButton.addTarget(self, action: #selector(self.addPasswordAction), for: .touchDown) - addPasswordUIBarButtonItem.customView = addPasswordButton - } else { - addPasswordUIBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(self.addPasswordAction)) - } + let addPasswordButton = UIButton(type: .system) + let plusImage = UIImage(systemName: "plus.circle", withConfiguration: UIImage.SymbolConfiguration(weight: .regular)) + addPasswordButton.setImage(plusImage, for: .normal) + addPasswordButton.addTarget(self, action: #selector(self.addPasswordAction), for: .touchDown) + addPasswordUIBarButtonItem.customView = addPasswordButton return addPasswordUIBarButtonItem }() diff --git a/pass/Controllers/SSHKeyFileImportTableViewController.swift b/pass/Controllers/SSHKeyFileImportTableViewController.swift index 9e8e5c4c..4483794a 100644 --- a/pass/Controllers/SSHKeyFileImportTableViewController.swift +++ b/pass/Controllers/SSHKeyFileImportTableViewController.swift @@ -27,9 +27,7 @@ class SSHKeyFileImportTableViewController: AutoCellHeightUITableViewController { return } picker.delegate = self - if #available(iOS 13.0, *) { - picker.shouldShowFileExtensions = true - } + picker.shouldShowFileExtensions = true present(picker, animated: true, completion: nil) } } diff --git a/passAutoFillExtension/Controllers/PasswordsViewController.swift b/passAutoFillExtension/Controllers/PasswordsViewController.swift index b33c0433..31eaa0af 100644 --- a/passAutoFillExtension/Controllers/PasswordsViewController.swift +++ b/passAutoFillExtension/Controllers/PasswordsViewController.swift @@ -21,9 +21,7 @@ class PasswordsViewController: UIViewController { uiSearchController.searchBar.isTranslucent = true uiSearchController.obscuresBackgroundDuringPresentation = false uiSearchController.searchBar.sizeToFit() - if #available(iOS 13.0, *) { - uiSearchController.searchBar.searchTextField.clearButtonMode = .whileEditing - } + uiSearchController.searchBar.searchTextField.clearButtonMode = .whileEditing return uiSearchController }() diff --git a/passKit/Helpers/Colors.swift b/passKit/Helpers/Colors.swift index 737b9716..51aabc05 100644 --- a/passKit/Helpers/Colors.swift +++ b/passKit/Helpers/Colors.swift @@ -7,33 +7,13 @@ // public enum Colors { - public static let label: UIColor = { - if #available(iOS 13.0, *) { - return .label - } - return .black - }() + public static let label: UIColor = .black - public static let secondaryLabel: UIColor = { - if #available(iOS 13.0, *) { - return .secondaryLabel - } - return .init(red: 60.0, green: 60.0, blue: 67.0, alpha: 0.6) - }() + public static let secondaryLabel: UIColor = .init(red: 60.0, green: 60.0, blue: 67.0, alpha: 0.6) - public static let systemBackground: UIColor = { - if #available(iOS 13.0, *) { - return .systemBackground - } - return .white - }() + public static let systemBackground: UIColor = .white - public static let secondarySystemBackground: UIColor = { - if #available(iOS 13.0, *) { - return .secondarySystemBackground - } - return .init(red: 242.0, green: 242.0, blue: 247.0, alpha: 1.0) - }() + public static let secondarySystemBackground: UIColor = .init(red: 242.0, green: 242.0, blue: 247.0, alpha: 1.0) public static let systemRed = UIColor.systemRed diff --git a/passKit/Helpers/YubiKeyConnection.swift b/passKit/Helpers/YubiKeyConnection.swift index fe759407..4d8c758a 100644 --- a/passKit/Helpers/YubiKeyConnection.swift +++ b/passKit/Helpers/YubiKeyConnection.swift @@ -27,9 +27,7 @@ public class YubiKeyConnection: NSObject { completion(connection) } else { connectionCallback = completion - if #available(iOSApplicationExtension 13.0, *) { - YubiKitManager.shared.startNFCConnection() - } + YubiKitManager.shared.startNFCConnection() } cancellationCallback = cancellation } From 2f9849314da2d90c83b90eb74c5efa12ef0fd868 Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Thu, 9 Mar 2023 11:45:34 -0800 Subject: [PATCH 07/72] Update Gemfile.lock --- Gemfile.lock | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 0f3009fb..7c3e6e9c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,23 +1,23 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.5) + CFPropertyList (3.0.6) rexml addressable (2.8.1) public_suffix (>= 2.0.2, < 6.0) artifactory (3.0.15) atomos (0.1.3) aws-eventstream (1.2.0) - aws-partitions (1.654.0) - aws-sdk-core (3.166.0) + aws-partitions (1.723.0) + aws-sdk-core (3.170.0) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.5) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.59.0) + aws-sdk-kms (1.63.0) aws-sdk-core (~> 3, >= 3.165.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.117.1) + aws-sdk-s3 (1.119.1) aws-sdk-core (~> 3, >= 3.165.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.4) @@ -36,8 +36,8 @@ GEM unf (>= 0.0.5, < 1.0.0) dotenv (2.8.1) emoji_regex (3.2.3) - excon (0.93.1) - faraday (1.10.2) + excon (0.99.0) + faraday (1.10.3) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -66,7 +66,7 @@ GEM faraday_middleware (1.2.0) faraday (~> 1.0) fastimage (2.2.6) - fastlane (2.210.1) + fastlane (2.212.1) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -106,9 +106,9 @@ GEM xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) gh_inspector (1.1.3) - google-apis-androidpublisher_v3 (0.30.0) - google-apis-core (>= 0.9.1, < 2.a) - google-apis-core (0.9.1) + google-apis-androidpublisher_v3 (0.35.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-core (0.11.0) addressable (~> 2.5, >= 2.5.1) googleauth (>= 0.16.2, < 2.a) httpclient (>= 2.8.1, < 3.a) @@ -117,10 +117,10 @@ GEM retriable (>= 2.0, < 4.a) rexml webrick - google-apis-iamcredentials_v1 (0.16.0) - google-apis-core (>= 0.9.1, < 2.a) - google-apis-playcustomapp_v1 (0.12.0) - google-apis-core (>= 0.9.1, < 2.a) + google-apis-iamcredentials_v1 (0.17.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-playcustomapp_v1 (0.13.0) + google-apis-core (>= 0.11.0, < 2.a) google-apis-storage_v1 (0.19.0) google-apis-core (>= 0.9.0, < 2.a) google-cloud-core (1.6.0) @@ -128,8 +128,8 @@ GEM google-cloud-errors (~> 1.0) google-cloud-env (1.6.0) faraday (>= 0.17.3, < 3.0) - google-cloud-errors (1.3.0) - google-cloud-storage (1.43.0) + google-cloud-errors (1.3.1) + google-cloud-storage (1.44.0) addressable (~> 2.8) digest-crc (~> 0.4) google-apis-iamcredentials_v1 (~> 0.1) @@ -149,14 +149,14 @@ GEM http-cookie (1.0.5) domain_name (~> 0.5) httpclient (2.8.3) - jmespath (1.6.1) - json (2.6.2) - jwt (2.5.0) + jmespath (1.6.2) + json (2.6.3) + jwt (2.7.0) memoist (0.16.2) mime-types (3.4.1) mime-types-data (~> 3.2015) mime-types-data (3.2022.0105) - mini_magick (4.11.0) + mini_magick (4.12.0) mini_mime (1.1.2) multi_json (1.15.0) multipart-post (2.0.0) @@ -165,8 +165,8 @@ GEM netrc (0.11.0) optparse (0.1.1) os (1.1.4) - plist (3.6.0) - public_suffix (5.0.0) + plist (3.7.0) + public_suffix (5.0.1) rake (13.0.6) representable (3.2.0) declarative (< 0.1.0) @@ -188,7 +188,7 @@ GEM faraday (>= 0.17.5, < 3.a) jwt (>= 1.5, < 3.0) multi_json (~> 1.10) - simctl (1.6.8) + simctl (1.6.10) CFPropertyList naturally terminal-notifier (2.0.0) @@ -204,7 +204,7 @@ GEM unf_ext unf_ext (0.0.8.2) unicode-display_width (1.8.0) - webrick (1.7.0) + webrick (1.8.1) word_wrap (1.0.0) xcodeproj (1.22.0) CFPropertyList (>= 2.3.3, < 4.0) From 6cf6ef1d45c32a0aedd466d5655bc28ac590affd Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Thu, 9 Mar 2023 12:15:11 -0800 Subject: [PATCH 08/72] Remove NDEF-tag from entitlements --- pass/pass.entitlements | 1 - pass/passBeta.entitlements | 1 - 2 files changed, 2 deletions(-) diff --git a/pass/pass.entitlements b/pass/pass.entitlements index d9153414..26deb49f 100644 --- a/pass/pass.entitlements +++ b/pass/pass.entitlements @@ -6,7 +6,6 @@ com.apple.developer.nfc.readersession.formats - NDEF TAG com.apple.developer.siri diff --git a/pass/passBeta.entitlements b/pass/passBeta.entitlements index e34057c4..1a9efa6a 100644 --- a/pass/passBeta.entitlements +++ b/pass/passBeta.entitlements @@ -6,7 +6,6 @@ com.apple.developer.nfc.readersession.formats - NDEF TAG com.apple.developer.siri From 83c6ae33dc11dda16ba4c17b6c6c755cf994450c Mon Sep 17 00:00:00 2001 From: Allan Feldman <6374032+a-feld@users.noreply.github.com> Date: Mon, 27 Feb 2023 22:53:06 -0500 Subject: [PATCH 09/72] Fix security bug which autofilled passwords without passcode/faceid auth The autofill extension currently calls the success callback even if a passcode/FaceID is not successfully verified. In the case that the PGP key passphrase is stored, this results in password decryption without further user interaction. The fix is to only decrypt passwords upon successful passcode / FaceID verification. --- .../Controllers/PasscodeExtensionDisplay.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/passAutoFillExtension/Controllers/PasscodeExtensionDisplay.swift b/passAutoFillExtension/Controllers/PasscodeExtensionDisplay.swift index 8bfa284f..bb2f290e 100644 --- a/passAutoFillExtension/Controllers/PasscodeExtensionDisplay.swift +++ b/passAutoFillExtension/Controllers/PasscodeExtensionDisplay.swift @@ -22,9 +22,7 @@ class PasscodeExtensionDisplay { before?() passcodeLockVC.successCallback = after passcodeLockVC.modalPresentationStyle = .fullScreen - sender.parent?.present(passcodeLockVC, animated: false) { - after?() - } + sender.parent?.present(passcodeLockVC, animated: false) } else { after?() } From f2a0c4ccf1c638dcc5065da471c415490672925f Mon Sep 17 00:00:00 2001 From: Dominik Johs Date: Fri, 10 Mar 2023 06:33:19 +0100 Subject: [PATCH 10/72] do not dismiss views when application is resumed (#605) * do not dismiss views when application is resumed * prevents the PasswordNavigationViewController and PasswordDetailTableViewController from being dismissed when the app is put to the background and then brought back to the foreground * Instead, the PasswordEntities are re-fetched from the context by their path to handle the re-creation of the entities during an update process that could have run in the background * update SwiftLint to version 0.50.* * update SwiftFormat to 0.51.* --------- Co-authored-by: Mingshen Sun --- .swiftformat | 1 - .../AdvancedSettingsTableViewController.swift | 8 ++--- .../PasswordDetailTableViewController.swift | 30 +++++++++++++++- .../PasswordEditorTableViewController.swift | 6 ++-- .../PasswordNavigationViewController.swift | 34 ++++++++++++++++--- .../CredentialProviderViewController.swift | 10 +++--- .../Controllers/ExtensionViewController.swift | 2 +- .../PasscodeLockViewController.swift | 4 +-- passKit/Models/GitCredential.swift | 6 ++-- scripts/swiftformat.sh | 2 +- 10 files changed, 77 insertions(+), 26 deletions(-) diff --git a/.swiftformat b/.swiftformat index 8d02852b..88c88150 100644 --- a/.swiftformat +++ b/.swiftformat @@ -44,7 +44,6 @@ numberFormatting, \ # opaqueGenericParameters, \ # organizeDeclarations, \ - preferDouble, \ preferKeyPath, \ redundantBackticks, \ redundantBreak, \ diff --git a/pass/Controllers/AdvancedSettingsTableViewController.swift b/pass/Controllers/AdvancedSettingsTableViewController.swift index 2877d9da..dca815f0 100644 --- a/pass/Controllers/AdvancedSettingsTableViewController.swift +++ b/pass/Controllers/AdvancedSettingsTableViewController.swift @@ -53,8 +53,8 @@ class AdvancedSettingsTableViewController: UITableViewController { alert.addAction( UIAlertAction(title: "ErasePasswordStoreData".localize(), style: UIAlertAction.Style.destructive) { [unowned self] _ in SVProgressHUD.show(withStatus: "Erasing...".localize()) - self.passwordStore.erase() - self.navigationController!.popViewController(animated: true) + passwordStore.erase() + navigationController!.popViewController(animated: true) SVProgressHUD.showSuccess(withStatus: "Done".localize()) SVProgressHUD.dismiss(withDelay: 1) } @@ -67,8 +67,8 @@ class AdvancedSettingsTableViewController: UITableViewController { UIAlertAction(title: "DiscardAllLocalChanges".localize(), style: UIAlertAction.Style.destructive) { [unowned self] _ in SVProgressHUD.show(withStatus: "Resetting...".localize()) do { - let numberDiscarded = try self.passwordStore.reset() - self.navigationController!.popViewController(animated: true) + let numberDiscarded = try passwordStore.reset() + navigationController!.popViewController(animated: true) SVProgressHUD.showSuccess(withStatus: "DiscardedCommits(%d)".localize(numberDiscarded)) SVProgressHUD.dismiss(withDelay: 1) } catch { diff --git a/pass/Controllers/PasswordDetailTableViewController.swift b/pass/Controllers/PasswordDetailTableViewController.swift index 4a462d37..0d115bb3 100644 --- a/pass/Controllers/PasswordDetailTableViewController.swift +++ b/pass/Controllers/PasswordDetailTableViewController.swift @@ -15,13 +15,21 @@ import UIKit import YubiKit class PasswordDetailTableViewController: UITableViewController, UIGestureRecognizerDelegate, AlertPresenting { - var passwordEntity: PasswordEntity? + var passwordEntity: PasswordEntity? { + didSet { + passwordPath = passwordEntity?.path + } + } + private var password: Password? private var passwordImage: UIImage? private var oneTimePasswordIndexPath: IndexPath? private var shouldPopCurrentView = false private let passwordStore = PasswordStore.shared + // preserve path so it can be reloaded even if the passwordEntity is deleted during the update process + private var passwordPath: String? + private lazy var editUIBarButtonItem: UIBarButtonItem = { let uiBarButtonItem = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(pressEdit)) return uiBarButtonItem @@ -74,6 +82,9 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni // reset the data table if the disaply settings have been changed NotificationCenter.default.addObserver(self, selector: #selector(decryptThenShowPasswordSelector), name: .passwordDetailDisplaySettingChanged, object: nil) + + // A Siri shortcut can change the state of the app in the background. Hence, reload when opening the app. + NotificationCenter.default.addObserver(self, selector: #selector(actOnPossiblePasswordStoreUpdate), name: UIApplication.willEnterForegroundNotification, object: nil) } override func viewDidAppear(_ animated: Bool) { @@ -526,6 +537,23 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni } extension PasswordDetailTableViewController { + @objc + func actOnPossiblePasswordStoreUpdate() { + DispatchQueue.main.async { + if let path = self.passwordPath { + // reload PasswordEntity because all PasswordEntities are re-created on PasswordStore update + self.passwordEntity = PasswordStore.shared.fetchPasswordEntity(with: path) + + // dismiss if the PasswordEntity does not exist anymore + if self.passwordEntity == nil { + self.navigationController?.popToRootViewController(animated: true) + } else { + self.decryptThenShowPassword() + } + } + } + } + private func requestYubiKeyPIN(completion: @escaping (String) -> Void, cancellation: @escaping () -> Void) { let alert = UIAlertController(title: "YubiKey PIN", message: "Verify YubiKey OpenPGP PIN.", preferredStyle: .alert) alert.addAction( diff --git a/pass/Controllers/PasswordEditorTableViewController.swift b/pass/Controllers/PasswordEditorTableViewController.swift index 2b7cf65b..375271cc 100644 --- a/pass/Controllers/PasswordEditorTableViewController.swift +++ b/pass/Controllers/PasswordEditorTableViewController.swift @@ -248,7 +248,7 @@ class PasswordEditorTableViewController: UITableViewController { let alert = UIAlertController(title: "DeletePassword?".localize(), message: nil, preferredStyle: UIAlertController.Style.alert) alert.addAction( UIAlertAction(title: "Delete".localize(), style: UIAlertAction.Style.destructive) { [unowned self] _ in - self.performSegue(withIdentifier: "deletePasswordSegue", sender: self) + performSegue(withIdentifier: "deletePasswordSegue", sender: self) } ) alert.addAction(UIAlertAction.cancel()) @@ -442,9 +442,9 @@ extension PasswordEditorTableViewController: SFSafariViewControllerDelegate { alert.addAction( UIAlertAction(title: "Yes", style: UIAlertAction.Style.default) { [unowned self] _ in // update tableData so to make sure reloadData() works correctly - self.tableData[self.passwordSection][0][PasswordEditorCellKey.content] = generatedPassword + tableData[passwordSection][0][PasswordEditorCellKey.content] = generatedPassword // update cell manually, no need to call reloadData() - self.fillPasswordCell?.setContent(content: generatedPassword) + fillPasswordCell?.setContent(content: generatedPassword) } ) alert.addAction(UIAlertAction.cancel()) diff --git a/pass/Controllers/PasswordNavigationViewController.swift b/pass/Controllers/PasswordNavigationViewController.swift index 2030e194..af81bfaa 100644 --- a/pass/Controllers/PasswordNavigationViewController.swift +++ b/pass/Controllers/PasswordNavigationViewController.swift @@ -21,7 +21,14 @@ class PasswordNavigationViewController: UIViewController { @IBOutlet var tableView: UITableView! var dataSource: PasswordNavigationDataSource? - var parentPasswordEntity: PasswordEntity? + var parentPasswordEntity: PasswordEntity? { + didSet { + parentPath = parentPasswordEntity?.path + } + } + + // preserve parent path so it can be reloaded even if the parentPasswordEntity is deleted during the update process + private var parentPath: String? var viewingUnsyncedPasswords = false var tapTabBarTime: TimeInterval = 0 @@ -181,13 +188,13 @@ class PasswordNavigationViewController: UIViewController { private func configureNotification() { let notificationCenter = NotificationCenter.default // Reset the data table if some password (maybe another one) has been updated. - notificationCenter.addObserver(self, selector: #selector(actOnReloadTableViewRelatedNotification), name: .passwordStoreUpdated, object: nil) + notificationCenter.addObserver(self, selector: #selector(actOnPossiblePasswordStoreUpdate), name: .passwordStoreUpdated, object: nil) // Reset the data table if the disaply settings have been changed. notificationCenter.addObserver(self, selector: #selector(actOnReloadTableViewRelatedNotification), name: .passwordDisplaySettingChanged, object: nil) // Search entrypoint for home screen quick action. notificationCenter.addObserver(self, selector: #selector(actOnSearchNotification), name: .passwordSearch, object: nil) // A Siri shortcut can change the state of the app in the background. Hence, reload when opening the app. - notificationCenter.addObserver(self, selector: #selector(actOnReloadTableViewRelatedNotification), name: UIApplication.willEnterForegroundNotification, object: nil) + notificationCenter.addObserver(self, selector: #selector(actOnPossiblePasswordStoreUpdate), name: UIApplication.willEnterForegroundNotification, object: nil) } @objc @@ -352,6 +359,23 @@ extension PasswordNavigationViewController { } } + @objc + func actOnPossiblePasswordStoreUpdate() { + DispatchQueue.main.async { + if let path = self.parentPath { + // reload parent because all PasswordEntities are re-created on PasswordStore update + self.parentPasswordEntity = PasswordStore.shared.fetchPasswordEntity(with: path) + + // pop to the root controller if the parent does not exist anymore + if self.parentPasswordEntity == nil { + self.navigationController?.popToRootViewController(animated: true) + } + } + + self.resetViews() + } + } + func resetViews() { configureTableView(in: parentPasswordEntity) tableView.reloadData() @@ -447,14 +471,14 @@ extension PasswordNavigationViewController: PasswordAlertPresenter { } DispatchQueue.global(qos: .userInitiated).async { [unowned self] in do { - let pullOptions = gitCredential.getCredentialOptions(passwordProvider: self.present) + let pullOptions = gitCredential.getCredentialOptions(passwordProvider: present) try PasswordStore.shared.pullRepository(options: pullOptions) { git_transfer_progress, _ in DispatchQueue.main.async { SVProgressHUD.showProgress(Float(git_transfer_progress.pointee.received_objects) / Float(git_transfer_progress.pointee.total_objects), status: "PullingFromRemoteRepository".localize()) } } if PasswordStore.shared.numberOfLocalCommits > 0 { - let pushOptions = gitCredential.getCredentialOptions(passwordProvider: self.present) + let pushOptions = gitCredential.getCredentialOptions(passwordProvider: present) try PasswordStore.shared.pushRepository(options: pushOptions) { current, total, _, _ in DispatchQueue.main.async { SVProgressHUD.showProgress(Float(current) / Float(total), status: "PushingToRemoteRepository".localize()) diff --git a/passAutoFillExtension/Controllers/CredentialProviderViewController.swift b/passAutoFillExtension/Controllers/CredentialProviderViewController.swift index 1b3ea858..3022f5e9 100644 --- a/passAutoFillExtension/Controllers/CredentialProviderViewController.swift +++ b/passAutoFillExtension/Controllers/CredentialProviderViewController.swift @@ -38,13 +38,13 @@ class CredentialProviderViewController: ASCredentialProviderViewController { passcodelock.presentPasscodeLockIfNeeded(self) { self.view.isHidden = true } after: { [unowned self] in - self.view.isHidden = false - self.credentialProvider.identifier = serviceIdentifiers.first + view.isHidden = false + credentialProvider.identifier = serviceIdentifiers.first let url = serviceIdentifiers.first .map(\.identifier) .flatMap(URL.init) - self.passwordsViewController.navigationItem.prompt = url?.host - self.passwordsViewController.showPasswordsWithSuggestion(matching: url?.host ?? "") + passwordsViewController.navigationItem.prompt = url?.host + passwordsViewController.showPasswordsWithSuggestion(matching: url?.host ?? "") } } @@ -61,7 +61,7 @@ class CredentialProviderViewController: ASCredentialProviderViewController { passcodelock.presentPasscodeLockIfNeeded(self) { self.view.isHidden = true } after: { [unowned self] in - self.credentialProvider.credentials(for: credentialIdentity) + credentialProvider.credentials(for: credentialIdentity) } } diff --git a/passExtension/Controllers/ExtensionViewController.swift b/passExtension/Controllers/ExtensionViewController.swift index bd18ecdc..7877651b 100644 --- a/passExtension/Controllers/ExtensionViewController.swift +++ b/passExtension/Controllers/ExtensionViewController.swift @@ -46,7 +46,7 @@ class ExtensionViewController: UIViewController { super.viewWillAppear(animated) prepareCredentialList() passcodelock.presentPasscodeLockIfNeeded(self, after: { [unowned self] in - self.view.isHidden = false + view.isHidden = false }) } diff --git a/passKit/Controllers/PasscodeLockViewController.swift b/passKit/Controllers/PasscodeLockViewController.swift index b641d635..1e4d20ef 100644 --- a/passKit/Controllers/PasscodeLockViewController.swift +++ b/passKit/Controllers/PasscodeLockViewController.swift @@ -191,8 +191,8 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate { let myContext = LAContext() // If the device passcode is not set, reset the app. guard myContext.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil) else { - self.passwordStore.erase() - self.passcodeLockDidSucceed() + passwordStore.erase() + passcodeLockDidSucceed() return } // If the device passcode is set, authentication is required. diff --git a/passKit/Models/GitCredential.swift b/passKit/Models/GitCredential.swift index 7ab9fcc0..9534d187 100644 --- a/passKit/Models/GitCredential.swift +++ b/passKit/Models/GitCredential.swift @@ -77,14 +77,14 @@ public struct GitCredential { private func createCredentialProvider(_ passwordProvider: @escaping PasswordProvider) -> GTCredentialProvider { var attempts = 1 return GTCredentialProvider { _, _, _ -> GTCredential? in - if attempts > self.credentialType.allowedAttempts { + if attempts > credentialType.allowedAttempts { return nil } - guard let password = self.getPassword(attempts: attempts, passwordProvider: passwordProvider) else { + guard let password = getPassword(attempts: attempts, passwordProvider: passwordProvider) else { return nil } attempts += 1 - return try? self.credentialType.createGTCredential(password: password) + return try? credentialType.createGTCredential(password: password) } } diff --git a/scripts/swiftformat.sh b/scripts/swiftformat.sh index 021720bf..01bb9700 100755 --- a/scripts/swiftformat.sh +++ b/scripts/swiftformat.sh @@ -1,5 +1,5 @@ export PATH="/opt/homebrew/bin:/opt/homebrew/sbin${PATH+:$PATH}" -SWIFTFORMAT_VERSION="0.50.*" +SWIFTFORMAT_VERSION="0.51.*" if [[ "${CI}" == "true" ]]; then echo "Running in a Continuous Integration environment. Formatting is skipped." From e6c1dc8dee4e8d160ee15a0a7fad165a415aa686 Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Thu, 9 Mar 2023 22:05:42 -0800 Subject: [PATCH 11/72] Fix missing Italian translation (#607) --- pass.xcodeproj/project.pbxproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index b373c278..711e869f 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -483,6 +483,7 @@ DC13B14E1E8640810097803F /* passTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = passTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; DC13B1521E8640810097803F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; DC193FF91E49B4430077E0A3 /* AdvancedSettingsTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AdvancedSettingsTableViewController.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + DC30F83629BAFD2E001EB12B /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Main.strings; sourceTree = ""; }; DC3E64E51E656F11009A83DE /* CommitLogsTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommitLogsTableViewController.swift; sourceTree = ""; }; DC4914941E434301007FF592 /* LabelTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LabelTableViewCell.swift; sourceTree = ""; }; DC4914981E434600007FF592 /* PasswordDetailTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordDetailTableViewController.swift; sourceTree = ""; }; @@ -1338,6 +1339,7 @@ en, Base, de, + it, ); mainGroup = DC917BCA1E2E8231000FDF54; packageReferences = ( @@ -1812,6 +1814,7 @@ DC917BDB1E2E8231000FDF54 /* Base */, 30C25DA921F34D2800BB27BB /* en */, 30C25DC321F3BEF500BB27BB /* de */, + DC30F83629BAFD2E001EB12B /* it */, ); name = Main.storyboard; sourceTree = ""; From d078d3ca0a75b05c8b5b1167a5d72bfaaeb3a636 Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Fri, 10 Mar 2023 20:34:30 -0800 Subject: [PATCH 12/72] Use system colors (fix #608) --- passKit/Helpers/Colors.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/passKit/Helpers/Colors.swift b/passKit/Helpers/Colors.swift index 51aabc05..2442af14 100644 --- a/passKit/Helpers/Colors.swift +++ b/passKit/Helpers/Colors.swift @@ -7,15 +7,15 @@ // public enum Colors { - public static let label: UIColor = .black + public static let label: UIColor = .label - public static let secondaryLabel: UIColor = .init(red: 60.0, green: 60.0, blue: 67.0, alpha: 0.6) + public static let secondaryLabel: UIColor = .secondaryLabel - public static let systemBackground: UIColor = .white + public static let systemBackground: UIColor = .systemBackground - public static let secondarySystemBackground: UIColor = .init(red: 242.0, green: 242.0, blue: 247.0, alpha: 1.0) + public static let secondarySystemBackground: UIColor = .secondarySystemBackground - public static let systemRed = UIColor.systemRed + public static let systemRed: UIColor = .systemRed - public static let systemBlue = UIColor.systemBlue + public static let systemBlue: UIColor = .systemBlue } From 5a91db4f76fe389f55a7dd55cdc08c1818e96643 Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Fri, 10 Mar 2023 20:56:56 -0800 Subject: [PATCH 13/72] Fix warnings and deprecated APIs --- pass.xcodeproj/project.pbxproj | 25 +++++++++++-------- .../xcshareddata/xcschemes/pass.xcscheme | 2 +- .../xcschemes/passAutoFillExtension.xcscheme | 2 +- .../xcschemes/passExtension.xcscheme | 2 +- .../xcshareddata/xcschemes/passKit.xcscheme | 2 +- .../xcschemes/passShortcuts.xcscheme | 2 +- .../AboutRepositoryTableViewController.swift | 2 +- .../PasswordDetailTableViewController.swift | 3 +-- 8 files changed, 21 insertions(+), 19 deletions(-) diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index 711e869f..51e921fb 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 52; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -150,9 +150,9 @@ 9A996C5726DDF65900A4485D /* Base32 in Frameworks */ = {isa = PBXBuildFile; productRef = 9A996C5626DDF65900A4485D /* Base32 */; }; 9A996C5826DEB0D100A4485D /* passKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A26075781EEC6F34005DB03E /* passKit.framework */; }; 9A996C5926DEB0D200A4485D /* passKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A26075781EEC6F34005DB03E /* passKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 9A996C6426DEB93F00A4485D /* passAutoFillExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = A239F5952158C08B00576CBF /* passAutoFillExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 9A996C6826DEB96B00A4485D /* passShortcuts.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 30A69945240EED5E00B7D967 /* passShortcuts.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 9A996C6B26DEB97600A4485D /* passExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = A26700241EEC466A00176B8A /* passExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 9A996C6426DEB93F00A4485D /* passAutoFillExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = A239F5952158C08B00576CBF /* passAutoFillExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 9A996C6826DEB96B00A4485D /* passShortcuts.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 30A69945240EED5E00B7D967 /* passShortcuts.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 9A996C6B26DEB97600A4485D /* passExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = A26700241EEC466A00176B8A /* passExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 9A996C6E26DEB99200A4485D /* passKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A26075781EEC6F34005DB03E /* passKit.framework */; }; 9ADAB21D26DDA52400900F10 /* Gopenpgp.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9ADAB21C26DDA52400900F10 /* Gopenpgp.xcframework */; }; 9ADC954124418A5F0005402E /* PasswordStoreTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADC954024418A5F0005402E /* PasswordStoreTest.swift */; }; @@ -312,17 +312,17 @@ name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; - 9A996C6726DEB93F00A4485D /* Embed App Extensions */ = { + 9A996C6726DEB93F00A4485D /* Embed Foundation Extensions */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 13; files = ( - 9A996C6426DEB93F00A4485D /* passAutoFillExtension.appex in Embed App Extensions */, - 9A996C6B26DEB97600A4485D /* passExtension.appex in Embed App Extensions */, - 9A996C6826DEB96B00A4485D /* passShortcuts.appex in Embed App Extensions */, + 9A996C6426DEB93F00A4485D /* passAutoFillExtension.appex in Embed Foundation Extensions */, + 9A996C6B26DEB97600A4485D /* passExtension.appex in Embed Foundation Extensions */, + 9A996C6826DEB96B00A4485D /* passShortcuts.appex in Embed Foundation Extensions */, ); - name = "Embed App Extensions"; + name = "Embed Foundation Extensions"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ @@ -1223,7 +1223,7 @@ 308800C124EB0D3600E87ED3 /* SwiftLint */, 9A996C4726DDEAF100A4485D /* Remove SPM Duplicate Frameworks */, 9A996C5A26DEB0D200A4485D /* Embed Frameworks */, - 9A996C6726DEB93F00A4485D /* Embed App Extensions */, + 9A996C6726DEB93F00A4485D /* Embed Foundation Extensions */, ); buildRules = ( ); @@ -1258,7 +1258,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1130; - LastUpgradeCheck = 1320; + LastUpgradeCheck = 1420; ORGANIZATIONNAME = "Bob Sun"; TargetAttributes = { 30A69944240EED5E00B7D967 = { @@ -1453,6 +1453,7 @@ /* Begin PBXShellScriptBuildPhase section */ 3005F34F24A9143C000519B5 /* SwiftFormat */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -1471,6 +1472,7 @@ }; 308800C124EB0D3600E87ED3 /* SwiftLint */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -1489,6 +1491,7 @@ }; 9A996C4726DDEAF100A4485D /* Remove SPM Duplicate Frameworks */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); diff --git a/pass.xcodeproj/xcshareddata/xcschemes/pass.xcscheme b/pass.xcodeproj/xcshareddata/xcschemes/pass.xcscheme index df662140..7f6956ee 100644 --- a/pass.xcodeproj/xcshareddata/xcschemes/pass.xcscheme +++ b/pass.xcodeproj/xcshareddata/xcschemes/pass.xcscheme @@ -1,6 +1,6 @@ Date: Fri, 10 Mar 2023 22:01:18 -0800 Subject: [PATCH 14/72] Recover search status back from the password detail --- .../PasswordNavigationViewController.swift | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pass/Controllers/PasswordNavigationViewController.swift b/pass/Controllers/PasswordNavigationViewController.swift index af81bfaa..fb9aa093 100644 --- a/pass/Controllers/PasswordNavigationViewController.swift +++ b/pass/Controllers/PasswordNavigationViewController.swift @@ -32,6 +32,7 @@ class PasswordNavigationViewController: UIViewController { var viewingUnsyncedPasswords = false var tapTabBarTime: TimeInterval = 0 + var searchText: String? lazy var passwordManager = PasswordManager(viewController: self) @@ -109,6 +110,16 @@ class PasswordNavigationViewController: UIViewController { configureNavigationBar() } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + if searchText != nil { + DispatchQueue.main.async { + self.searchBar.text = self.searchText + self.searchController.isActive = true + } + } + } + private func configureSearchBar() { if Defaults.isShowFolderOn { searchBar.scopeButtonTitles = SearchBarScope.allCases.map(\.localizedName) @@ -388,6 +399,7 @@ extension PasswordNavigationViewController { extension PasswordNavigationViewController: UISearchBarDelegate { func search(matching text: String) { + searchText = text dataSource?.showTableEntries(matching: text) tableView.reloadData() } @@ -414,6 +426,7 @@ extension PasswordNavigationViewController: UISearchBarDelegate { searchBar.selectedScopeButtonIndex = SearchBarScope.current.rawValue } activateSearch(searchBar.selectedScopeButtonIndex) + search(matching: searchBar.text ?? "") } func searchBar(_: UISearchBar, textDidChange searchText: String) { @@ -431,6 +444,7 @@ extension PasswordNavigationViewController: UISearchBarDelegate { func cancelSearch() { configureTableView(in: parentPasswordEntity) dataSource?.isSearchActive = false + searchText = nil tableView.reloadData() } } From cdaf60da73d148eb37c81ce1efd257c237c74acd Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Sun, 12 Mar 2023 14:46:37 -0700 Subject: [PATCH 15/72] Do not init Yubikey if device does not support MFI accessory --- passKit/Helpers/YubiKeyConnection.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/passKit/Helpers/YubiKeyConnection.swift b/passKit/Helpers/YubiKeyConnection.swift index 4d8c758a..1830488c 100644 --- a/passKit/Helpers/YubiKeyConnection.swift +++ b/passKit/Helpers/YubiKeyConnection.swift @@ -18,8 +18,10 @@ public class YubiKeyConnection: NSObject { override init() { super.init() - YubiKitManager.shared.delegate = self - YubiKitManager.shared.startAccessoryConnection() + if YubiKitDeviceCapabilities.supportsMFIAccessoryKey { + YubiKitManager.shared.delegate = self + YubiKitManager.shared.startAccessoryConnection() + } } public func connection(cancellation: @escaping (_ error: Error) -> Void, completion: @escaping (_ connection: YKFConnectionProtocol) -> Void) { From 3edf8efbc5a59ccf83c7089b5f28019e1b4441c6 Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Sun, 12 Mar 2023 16:10:29 -0700 Subject: [PATCH 16/72] Hide yubikey for unsupported devices --- .../Controllers/SettingsTableViewController.swift | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pass/Controllers/SettingsTableViewController.swift b/pass/Controllers/SettingsTableViewController.swift index 1fe9aee2..829a2a7a 100644 --- a/pass/Controllers/SettingsTableViewController.swift +++ b/pass/Controllers/SettingsTableViewController.swift @@ -10,6 +10,7 @@ import CoreData import passKit import SVProgressHUD import UIKit +import YubiKit class SettingsTableViewController: UITableViewController, UITabBarControllerDelegate { @IBOutlet var pgpKeyTableViewCell: UITableViewCell! @@ -184,12 +185,14 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele ) } - optionMenu.addAction( - UIAlertAction(title: Defaults.isYubiKeyEnabled ? "✓ YubiKey" : "YubiKey", style: .default) { _ in - Defaults.isYubiKeyEnabled.toggle() - self.setPGPKeyTableViewCellDetailText() - } - ) + if YubiKitDeviceCapabilities.supportsMFIAccessoryKey { + optionMenu.addAction( + UIAlertAction(title: Defaults.isYubiKeyEnabled ? "✓ YubiKey" : "YubiKey", style: .default) { _ in + Defaults.isYubiKeyEnabled.toggle() + self.setPGPKeyTableViewCellDetailText() + } + ) + } if Defaults.pgpKeySource != nil { optionMenu.addAction( From 6c790f8e07f98e6d8990ab650c03d3fc632cf0a4 Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Sun, 12 Mar 2023 17:36:19 -0700 Subject: [PATCH 17/72] Remove table view size attributes in storyboard Fix missing table section title when using the app in Apple Silicon macOS. --- pass/Base.lproj/Main.storyboard | 286 +++++++++--------- .../Base.lproj/MainInterface.storyboard | 8 +- .../SearchPassword.storyboard | 6 +- .../Base.lproj/MainInterface.storyboard | 6 +- 4 files changed, 153 insertions(+), 153 deletions(-) diff --git a/pass/Base.lproj/Main.storyboard b/pass/Base.lproj/Main.storyboard index 8e140fd2..99a1fdf5 100644 --- a/pass/Base.lproj/Main.storyboard +++ b/pass/Base.lproj/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -21,26 +21,26 @@ - + - + - +