2020-12-31 21:46:50 -08:00
|
|
|
//
|
|
|
|
|
// PasswordDecryptor.swift
|
2021-01-17 19:49:05 -08:00
|
|
|
// pass
|
2020-12-31 21:46:50 -08:00
|
|
|
//
|
2021-01-17 19:49:05 -08:00
|
|
|
// Created by Sun, Mingshen on 1/17/21.
|
|
|
|
|
// Copyright © 2021 Bob Sun. All rights reserved.
|
2020-12-31 21:46:50 -08:00
|
|
|
//
|
|
|
|
|
|
2022-01-09 21:38:39 -08:00
|
|
|
import CryptoTokenKit
|
|
|
|
|
import Gopenpgp
|
2020-12-31 21:46:50 -08:00
|
|
|
import passKit
|
2021-01-17 19:49:05 -08:00
|
|
|
import SVProgressHUD
|
|
|
|
|
import UIKit
|
2022-01-09 21:38:39 -08:00
|
|
|
import YubiKit
|
2020-12-31 21:46:50 -08:00
|
|
|
|
2022-01-09 21:38:39 -08:00
|
|
|
func decryptPassword(
|
|
|
|
|
in controller: UIViewController,
|
|
|
|
|
with passwordPath: String,
|
|
|
|
|
using keyID: String? = nil,
|
|
|
|
|
completion: @escaping ((Password) -> Void)
|
|
|
|
|
) {
|
|
|
|
|
// YubiKey is not supported in extension
|
|
|
|
|
if Defaults.isYubiKeyEnabled {
|
|
|
|
|
DispatchQueue.main.async {
|
|
|
|
|
let alert = UIAlertController(title: "Error", message: "YubiKey is not supported in extension, please use the Pass app instead.", preferredStyle: .alert)
|
|
|
|
|
alert.addAction(UIAlertAction.ok())
|
|
|
|
|
controller.present(alert, animated: true)
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
2021-01-03 17:38:02 -08:00
|
|
|
DispatchQueue.global(qos: .userInteractive).async {
|
|
|
|
|
do {
|
|
|
|
|
let requestPGPKeyPassphrase = Utils.createRequestPGPKeyPassphraseHandler(controller: controller)
|
|
|
|
|
let decryptedPassword = try PasswordStore.shared.decrypt(path: passwordPath, keyID: keyID, requestPGPKeyPassphrase: requestPGPKeyPassphrase)
|
2020-12-31 21:46:50 -08:00
|
|
|
|
2021-01-03 17:38:02 -08:00
|
|
|
DispatchQueue.main.async {
|
|
|
|
|
completion(decryptedPassword)
|
|
|
|
|
}
|
|
|
|
|
} catch let AppError.pgpPrivateKeyNotFound(keyID: key) {
|
|
|
|
|
DispatchQueue.main.async {
|
|
|
|
|
let alert = UIAlertController(title: "CannotShowPassword".localize(), message: AppError.pgpPrivateKeyNotFound(keyID: key).localizedDescription, preferredStyle: .alert)
|
|
|
|
|
alert.addAction(UIAlertAction.cancelAndPopView(controller: controller))
|
2026-03-11 16:16:50 +01:00
|
|
|
let selectKey = UIAlertAction.selectKey(type: PGPKey.PRIVATE, controller: controller) { action in
|
2021-01-03 17:38:02 -08:00
|
|
|
decryptPassword(in: controller, with: passwordPath, using: action.title, completion: completion)
|
|
|
|
|
}
|
|
|
|
|
alert.addAction(selectKey)
|
2021-01-03 15:08:15 -08:00
|
|
|
|
2021-01-03 17:38:02 -08:00
|
|
|
controller.present(alert, animated: true)
|
|
|
|
|
}
|
|
|
|
|
} catch {
|
|
|
|
|
DispatchQueue.main.async {
|
|
|
|
|
Utils.alert(title: "CannotCopyPassword".localize(), message: error.localizedDescription, controller: controller)
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-12-31 21:46:50 -08:00
|
|
|
}
|
|
|
|
|
}
|
2022-01-09 21:38:39 -08:00
|
|
|
|
2024-12-15 21:08:27 -08:00
|
|
|
class PasswordYubiKeyDecryptor {
|
|
|
|
|
let yubiKeyConnection = YubiKeyConnection()
|
2022-01-09 21:38:39 -08:00
|
|
|
|
2024-12-15 21:08:27 -08:00
|
|
|
func didDisconnect(completion: @escaping (_ connection: YKFConnectionProtocol?, _ error: Error?) -> Void) {
|
|
|
|
|
yubiKeyConnection.didDisconnect(handler: completion)
|
2022-12-30 02:22:45 +00:00
|
|
|
}
|
|
|
|
|
|
2024-12-15 21:08:27 -08:00
|
|
|
private func handleError(error: Error, forConnection connection: YKFConnectionProtocol) -> Error {
|
|
|
|
|
if (connection as? YKFNFCConnection) != nil {
|
|
|
|
|
YubiKitManager.shared.stopNFCConnection(withErrorMessage: error.localizedDescription)
|
|
|
|
|
return error // Dont pass on the error since we display it in the NFC modal
|
2022-01-09 21:38:39 -08:00
|
|
|
}
|
2024-12-15 21:08:27 -08:00
|
|
|
return error
|
2022-01-09 21:38:39 -08:00
|
|
|
}
|
|
|
|
|
|
2024-12-15 21:08:27 -08:00
|
|
|
func yubiKeyDecrypt(encryptedData: Data, pin: String) async throws -> Data {
|
|
|
|
|
let connection = await yubiKeyConnection.startConnection()
|
2022-01-09 21:38:39 -08:00
|
|
|
|
2024-12-15 21:08:27 -08:00
|
|
|
do {
|
2022-12-30 02:22:45 +00:00
|
|
|
guard let smartCard = connection.smartCardInterface else {
|
|
|
|
|
throw AppError.yubiKey(.connection(message: "Failed to get smart card interface."))
|
|
|
|
|
}
|
2024-12-15 21:08:27 -08:00
|
|
|
do {
|
|
|
|
|
try await smartCard.selectOpenPGPApplication()
|
|
|
|
|
} catch {
|
|
|
|
|
throw AppError.yubiKey(.connection(message: "Failed to select OpenPGP application"))
|
2022-12-30 02:22:45 +00:00
|
|
|
}
|
2024-12-15 21:08:27 -08:00
|
|
|
do {
|
|
|
|
|
try await smartCard.verify(password: pin)
|
|
|
|
|
} catch {
|
|
|
|
|
throw AppError.yubiKey(.connection(message: "Failed to verify PIN"))
|
2022-12-30 02:22:45 +00:00
|
|
|
}
|
2024-12-15 21:08:27 -08:00
|
|
|
guard let deciphered = try? await smartCard.decipher(ciphertext: encryptedData) else {
|
|
|
|
|
throw AppError.yubiKey(.connection(message: "Failed to dicipher data"))
|
2022-12-30 02:22:45 +00:00
|
|
|
}
|
2024-12-15 21:08:27 -08:00
|
|
|
let decryptedData = try decryptData(deciphered: deciphered, ciphertext: encryptedData)
|
|
|
|
|
if (connection as? YKFNFCConnection) != nil {
|
|
|
|
|
YubiKitManager.shared.stopNFCConnection()
|
|
|
|
|
}
|
|
|
|
|
return decryptedData
|
2022-12-30 02:22:45 +00:00
|
|
|
} catch {
|
2024-12-15 21:08:27 -08:00
|
|
|
throw handleError(error: error, forConnection: connection)
|
2022-12-30 02:22:45 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-15 21:08:27 -08:00
|
|
|
private func decryptData(deciphered: Data, ciphertext: Data) throws -> Data {
|
|
|
|
|
let symmetricKeyIDNameDict: [UInt8: String] = [
|
|
|
|
|
2: "3des",
|
|
|
|
|
3: "cast5",
|
|
|
|
|
7: "aes128",
|
|
|
|
|
8: "aes192",
|
|
|
|
|
9: "aes256",
|
|
|
|
|
]
|
2022-12-30 02:22:45 +00:00
|
|
|
|
2024-11-30 11:29:27 -08:00
|
|
|
let message = createPGPMessage(from: ciphertext)
|
2022-12-30 02:22:45 +00:00
|
|
|
|
|
|
|
|
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))"))
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-15 21:08:27 -08:00
|
|
|
return plaintext
|
2022-01-09 21:38:39 -08:00
|
|
|
}
|