Initial implementation of using YubiKey for decryption (#533)
This commit is contained in:
parent
13804b79e6
commit
955e50c3d3
23 changed files with 606 additions and 118 deletions
|
|
@ -35,6 +35,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
}
|
||||
}
|
||||
UNUserNotificationCenter.current().delegate = NotificationCenterDispatcher.shared
|
||||
#if !targetEnvironment(simulator)
|
||||
_ = passKit.YubiKeyConnection.shared
|
||||
#endif
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ extension PGPKeyArmorImportTableViewController: PGPKeyImporter {
|
|||
Utils.alert(title: "CannotSave".localize(), message: "SetPublicKey.".localize(), controller: self, completion: nil)
|
||||
return false
|
||||
}
|
||||
guard !armorPrivateKeyTextView.text.isEmpty else {
|
||||
guard Defaults.isYubiKeyEnabled || !armorPrivateKeyTextView.text.isEmpty else {
|
||||
Utils.alert(title: "CannotSave".localize(), message: "SetPrivateKey.".localize(), controller: self, completion: nil)
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import passKit
|
||||
|
||||
class PGPKeyFileImportTableViewController: AutoCellHeightUITableViewController {
|
||||
class PGPKeyFileImportTableViewController: AutoCellHeightUITableViewController, AlertPresenting {
|
||||
@IBOutlet var pgpPublicKeyFile: UITableViewCell!
|
||||
@IBOutlet var pgpPrivateKeyFile: UITableViewCell!
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ class PGPKeyFileImportTableViewController: AutoCellHeightUITableViewController {
|
|||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
let cell = tableView.cellForRow(at: indexPath)
|
||||
let picker = UIDocumentPickerViewController(documentTypes: ["public.data"], in: .open)
|
||||
let picker = UIDocumentPickerViewController(documentTypes: ["public.item"], in: .open)
|
||||
cell?.isSelected = false
|
||||
if cell == pgpPublicKeyFile {
|
||||
currentlyPicking = .public
|
||||
|
|
@ -71,7 +71,7 @@ extension PGPKeyFileImportTableViewController: UIDocumentPickerDelegate {
|
|||
}
|
||||
} catch {
|
||||
let message = "FileCannotBeImported.".localize(fileName) | "UnderlyingError".localize(error.localizedDescription)
|
||||
Utils.alert(title: "CannotImportFile".localize(), message: message, controller: self)
|
||||
presentFailureAlert(title: "CannotImportFile".localize(), message: message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -81,19 +81,20 @@ extension PGPKeyFileImportTableViewController: PGPKeyImporter {
|
|||
static let label = "LoadFromFiles".localize()
|
||||
|
||||
func isReadyToUse() -> Bool {
|
||||
validate(key: publicKey) && validate(key: privateKey)
|
||||
validate(key: publicKey) && (Defaults.isYubiKeyEnabled || validate(key: privateKey))
|
||||
}
|
||||
|
||||
func importKeys() throws {
|
||||
guard let publicKey = publicKey, let privateKey = privateKey else {
|
||||
return
|
||||
if let publicKey = publicKey {
|
||||
try KeyFileManager.PublicPGP.importKey(from: publicKey)
|
||||
}
|
||||
if let privateKey = privateKey {
|
||||
try KeyFileManager.PrivatePGP.importKey(from: privateKey)
|
||||
}
|
||||
try KeyFileManager.PublicPGP.importKey(from: publicKey)
|
||||
try KeyFileManager.PrivatePGP.importKey(from: privateKey)
|
||||
}
|
||||
|
||||
func doAfterImport() {
|
||||
Utils.alert(title: "RememberToRemoveKey".localize(), message: "RememberToRemoveKeyFromLocation.".localize(), controller: self)
|
||||
presentAlert(title: "RememberToRemoveKey".localize(), message: "RememberToRemoveKeyFromLocation.".localize())
|
||||
}
|
||||
|
||||
func saveImportedKeys() {
|
||||
|
|
@ -102,7 +103,7 @@ extension PGPKeyFileImportTableViewController: PGPKeyImporter {
|
|||
|
||||
private func validate(key: String?) -> Bool {
|
||||
guard key != nil else {
|
||||
Utils.alert(title: "CannotSavePgpKey".localize(), message: "KeyFileNotSet.".localize(), controller: self)
|
||||
presentFailureAlert(title: "CannotSavePgpKey".localize(), message: "KeyFileNotSet.".localize())
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
import passKit
|
||||
import UIKit
|
||||
|
||||
class PGPKeyURLImportTableViewController: AutoCellHeightUITableViewController {
|
||||
class PGPKeyURLImportTableViewController: AutoCellHeightUITableViewController, AlertPresenting {
|
||||
@IBOutlet var pgpPublicKeyURLTextField: UITextField!
|
||||
@IBOutlet var pgpPrivateKeyURLTextField: UITextField!
|
||||
|
||||
|
|
@ -24,18 +24,11 @@ class PGPKeyURLImportTableViewController: AutoCellHeightUITableViewController {
|
|||
|
||||
@IBAction
|
||||
private func save(_: Any) {
|
||||
guard let publicKeyURLText = pgpPublicKeyURLTextField.text,
|
||||
let publicKeyURL = URL(string: publicKeyURLText),
|
||||
let privateKeyURLText = pgpPrivateKeyURLTextField.text,
|
||||
let privateKeyURL = URL(string: privateKeyURLText) else {
|
||||
Utils.alert(title: "CannotSavePgpKey".localize(), message: "SetPgpKeyUrlsFirst.".localize(), controller: self)
|
||||
return
|
||||
pgpPublicKeyURL = validate(pgpKeyURLText: pgpPublicKeyURLTextField.text)
|
||||
|
||||
if !Defaults.isYubiKeyEnabled {
|
||||
pgpPrivateKeyURL = validate(pgpKeyURLText: pgpPrivateKeyURLTextField.text)
|
||||
}
|
||||
if privateKeyURL.scheme?.lowercased() == "http" || publicKeyURL.scheme?.lowercased() == "http" {
|
||||
Utils.alert(title: "HttpNotSecure".localize(), message: "ReallyUseHttp.".localize(), controller: self)
|
||||
}
|
||||
pgpPrivateKeyURL = privateKeyURL
|
||||
pgpPublicKeyURL = publicKeyURL
|
||||
saveImportedKeys()
|
||||
}
|
||||
}
|
||||
|
|
@ -45,35 +38,39 @@ extension PGPKeyURLImportTableViewController: PGPKeyImporter {
|
|||
static let label = "DownloadFromUrl".localize()
|
||||
|
||||
func isReadyToUse() -> Bool {
|
||||
validate(pgpKeyURL: pgpPublicKeyURLTextField.text ?? "")
|
||||
&& validate(pgpKeyURL: pgpPrivateKeyURLTextField.text ?? "")
|
||||
validate(pgpKeyURLText: pgpPublicKeyURLTextField.text) != nil
|
||||
&& (Defaults.isYubiKeyEnabled || validate(pgpKeyURLText: pgpPrivateKeyURLTextField.text ?? "") != nil)
|
||||
}
|
||||
|
||||
func importKeys() throws {
|
||||
Defaults.pgpPrivateKeyURL = pgpPrivateKeyURL
|
||||
Defaults.pgpPublicKeyURL = pgpPublicKeyURL
|
||||
if let pgpPrivateKeyURL = pgpPrivateKeyURL {
|
||||
Defaults.pgpPrivateKeyURL = pgpPrivateKeyURL
|
||||
try KeyFileManager.PrivatePGP.importKey(from: pgpPrivateKeyURL)
|
||||
}
|
||||
|
||||
try KeyFileManager.PublicPGP.importKey(from: Defaults.pgpPublicKeyURL!)
|
||||
try KeyFileManager.PrivatePGP.importKey(from: Defaults.pgpPrivateKeyURL!)
|
||||
if let pgpPublicKeyURL = pgpPublicKeyURL {
|
||||
Defaults.pgpPublicKeyURL = pgpPublicKeyURL
|
||||
try KeyFileManager.PublicPGP.importKey(from: pgpPublicKeyURL)
|
||||
}
|
||||
}
|
||||
|
||||
func doAfterImport() {
|
||||
Utils.alert(title: "RememberToRemoveKey".localize(), message: "RememberToRemoveKeyFromServer.".localize(), controller: self)
|
||||
presentAlert(title: "RememberToRemoveKey".localize(), message: "RememberToRemoveKeyFromServer.".localize())
|
||||
}
|
||||
|
||||
func saveImportedKeys() {
|
||||
performSegue(withIdentifier: "savePGPKeySegue", sender: self)
|
||||
}
|
||||
|
||||
private func validate(pgpKeyURL: String) -> Bool {
|
||||
guard let url = URL(string: pgpKeyURL) else {
|
||||
Utils.alert(title: "CannotSavePgpKey".localize(), message: "SetPgpKeyUrlsFirst.".localize(), controller: self)
|
||||
return false
|
||||
private func validate(pgpKeyURLText: String?) -> URL? {
|
||||
guard let pgpKeyURL = pgpKeyURLText, let url = URL(string: pgpKeyURL) else {
|
||||
presentFailureAlert(title: "CannotSavePgpKey".localize(), message: "SetPgpKeyUrlsFirst.".localize())
|
||||
return nil
|
||||
}
|
||||
guard url.scheme == "https" || url.scheme == "http" else {
|
||||
Utils.alert(title: "CannotSavePgpKey".localize(), message: "UseEitherHttpsOrHttp.".localize(), controller: self)
|
||||
return false
|
||||
presentFailureAlert(title: "CannotSavePgpKey".localize(), message: "UseEitherHttpsOrHttp.".localize())
|
||||
return nil
|
||||
}
|
||||
return true
|
||||
return url
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,11 +7,13 @@
|
|||
//
|
||||
|
||||
import FavIcon
|
||||
import Gopenpgp
|
||||
import passAutoFillExtension
|
||||
import passKit
|
||||
import SVProgressHUD
|
||||
import UIKit
|
||||
|
||||
class PasswordDetailTableViewController: UITableViewController, UIGestureRecognizerDelegate {
|
||||
class PasswordDetailTableViewController: UITableViewController, UIGestureRecognizerDelegate, AlertPresenting {
|
||||
var passwordEntity: PasswordEntity?
|
||||
private var password: Password?
|
||||
private var passwordImage: UIImage?
|
||||
|
|
@ -87,7 +89,15 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
decryptThenShowPassword()
|
||||
}
|
||||
|
||||
private func decryptThenShowPassword(keyID: String? = nil) {
|
||||
private func decryptThenShowPassword() {
|
||||
if Defaults.isYubiKeyEnabled {
|
||||
decryptThenShowPasswordYubiKey()
|
||||
} else {
|
||||
decryptThenShowPasswordLocalKey()
|
||||
}
|
||||
}
|
||||
|
||||
private func decryptThenShowPasswordLocalKey(keyID: String? = nil) {
|
||||
guard let passwordEntity = passwordEntity else {
|
||||
Utils.alert(title: "CannotShowPassword".localize(), message: "PasswordDoesNotExist".localize(), controller: self, completion: {
|
||||
self.navigationController!.popViewController(animated: true)
|
||||
|
|
@ -106,7 +116,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
let alert = UIAlertController(title: "CannotShowPassword".localize(), message: AppError.pgpPrivateKeyNotFound(keyID: key).localizedDescription, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction.cancelAndPopView(controller: self))
|
||||
let selectKey = UIAlertAction.selectKey(controller: self) { action in
|
||||
self.decryptThenShowPassword(keyID: action.title)
|
||||
self.decryptThenShowPasswordLocalKey(keyID: action.title)
|
||||
}
|
||||
alert.addAction(selectKey)
|
||||
|
||||
|
|
@ -119,7 +129,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
alert.addAction(UIAlertAction.cancelAndPopView(controller: self))
|
||||
alert.addAction(
|
||||
UIAlertAction(title: "TryAgain".localize(), style: .default) { _ in
|
||||
self.decryptThenShowPassword()
|
||||
self.decryptThenShowPasswordLocalKey()
|
||||
}
|
||||
)
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
|
|
@ -509,3 +519,66 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
|||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
extension PasswordDetailTableViewController {
|
||||
private func requestYubiKeyPIN(completion: @escaping (String) -> Void) {
|
||||
let alert = UIAlertController(title: "YubiKey PIN", message: "Verify YubiKey OpenPGP PIN.", preferredStyle: .alert)
|
||||
alert.addAction(
|
||||
UIAlertAction.cancel { _ in
|
||||
self.navigationController!.popViewController(animated: true)
|
||||
}
|
||||
)
|
||||
alert.addAction(
|
||||
UIAlertAction.ok { _ in
|
||||
let pin = alert.textFields?.first?.text ?? ""
|
||||
completion(pin)
|
||||
}
|
||||
)
|
||||
alert.addTextField { textField in
|
||||
textField.isSecureTextEntry = true
|
||||
}
|
||||
present(alert, animated: true)
|
||||
}
|
||||
|
||||
private func handleError(error: AppError) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
DispatchQueue.main.async {
|
||||
self.presentFailureAlert(message: error.localizedDescription) { _ in
|
||||
self.navigationController?.popViewController(animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func handleCancellation(_: Error) {
|
||||
DispatchQueue.main.async {
|
||||
self.navigationController?.popViewController(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
private func decryptThenShowPasswordYubiKey() {
|
||||
guard let passwordEntity = passwordEntity else {
|
||||
handleError(error: AppError.other(message: "PasswordDoesNotExist"))
|
||||
return
|
||||
}
|
||||
Pass.yubiKeyDecrypt(passwordEntity: passwordEntity, requestPIN: requestYubiKeyPIN, errorHandler: handleError, cancellation: handleCancellation) { password in
|
||||
self.password = password
|
||||
self.showPassword()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -319,7 +319,7 @@ extension PasswordNavigationViewController {
|
|||
|
||||
override func shouldPerformSegue(withIdentifier identifier: String, sender _: Any?) -> Bool {
|
||||
if identifier == "showPasswordDetail" {
|
||||
guard PGPAgent.shared.isPrepared else {
|
||||
guard Defaults.isYubiKeyEnabled || PGPAgent.shared.isPrepared else {
|
||||
Utils.alert(title: "CannotShowPassword".localize(), message: "PgpKeyNotSet.".localize(), controller: self)
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,12 +87,16 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
|
|||
|
||||
private func setPGPKeyTableViewCellDetailText() {
|
||||
var label = "NotSet".localize()
|
||||
|
||||
let keyID = (try? PGPAgent.shared.getShortKeyID()) ?? []
|
||||
if keyID.count == 1 {
|
||||
label = keyID.first ?? ""
|
||||
} else if keyID.count > 1 {
|
||||
label = "Multiple"
|
||||
}
|
||||
if Defaults.isYubiKeyEnabled {
|
||||
label += "+YubiKey"
|
||||
}
|
||||
pgpKeyTableViewCell.detailTextLabel?.text = label
|
||||
}
|
||||
|
||||
|
|
@ -180,6 +184,13 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
|
|||
)
|
||||
}
|
||||
|
||||
optionMenu.addAction(
|
||||
UIAlertAction(title: Defaults.isYubiKeyEnabled ? "✓ YubiKey" : "YubiKey", style: .default) { _ in
|
||||
Defaults.isYubiKeyEnabled.toggle()
|
||||
self.setPGPKeyTableViewCellDetailText()
|
||||
}
|
||||
)
|
||||
|
||||
if Defaults.pgpKeySource != nil {
|
||||
optionMenu.addAction(
|
||||
UIAlertAction(title: "RemovePgpKeys".localize(), style: .destructive) { _ in
|
||||
|
|
|
|||
|
|
@ -10,5 +10,6 @@
|
|||
#define Objective_CBridgingHeader_h
|
||||
|
||||
@import ObjectiveGit;
|
||||
#import <YubiKit.h>
|
||||
|
||||
#endif /* Objective_CBridgingHeader_h */
|
||||
|
|
|
|||
|
|
@ -2,6 +2,21 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
|
||||
<array>
|
||||
<string>A000000527471117</string>
|
||||
<string>A0000006472F0001</string>
|
||||
<string>A0000005272101</string>
|
||||
<string>A000000308</string>
|
||||
<string>D27600012401</string>
|
||||
<string>A000000527200101</string>
|
||||
</array>
|
||||
<key>NFCReaderUsageDescription</key>
|
||||
<string>The application needs access to NFC reading to communicate with your YubiKey.</string>
|
||||
<key>UISupportedExternalAccessoryProtocols</key>
|
||||
<array>
|
||||
<string>com.yubico.ylp</string>
|
||||
</array>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
|
|
|
|||
|
|
@ -6,11 +6,28 @@
|
|||
// Copyright © 2021 Bob Sun. All rights reserved.
|
||||
//
|
||||
|
||||
import CryptoTokenKit
|
||||
import Gopenpgp
|
||||
import passKit
|
||||
import SVProgressHUD
|
||||
import UIKit
|
||||
import YubiKit
|
||||
|
||||
func decryptPassword(in controller: UIViewController, with passwordPath: String, using keyID: String? = nil, completion: @escaping ((Password) -> Void)) {
|
||||
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
|
||||
}
|
||||
DispatchQueue.global(qos: .userInteractive).async {
|
||||
do {
|
||||
let requestPGPKeyPassphrase = Utils.createRequestPGPKeyPassphraseHandler(controller: controller)
|
||||
|
|
@ -37,3 +54,144 @@ func decryptPassword(in controller: UIViewController, with passwordPath: String,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public typealias RequestPINAction = (@escaping (String) -> Void) -> Void
|
||||
|
||||
let symmetricKeyIDNameDict: [UInt8: String] = [
|
||||
2: "3des",
|
||||
3: "cast5",
|
||||
7: "aes128",
|
||||
8: "aes192",
|
||||
9: "aes256",
|
||||
]
|
||||
|
||||
private func isEncryptKeyAlgoRSA(_ applicationRelatedData: Data) -> Bool {
|
||||
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
|
||||
}
|
||||
|
||||
// swiftlint:disable cyclomatic_complexity
|
||||
public func yubiKeyDecrypt(
|
||||
passwordEntity: PasswordEntity,
|
||||
requestPIN: @escaping RequestPINAction,
|
||||
errorHandler: @escaping ((AppError) -> Void),
|
||||
cancellation: @escaping ((_ error: Error) -> Void),
|
||||
completion: @escaping ((Password) -> Void)
|
||||
) {
|
||||
let encryptedDataPath = PasswordStore.shared.storeURL.appendingPathComponent(passwordEntity.getPath())
|
||||
|
||||
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.")))
|
||||
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
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Data {
|
||||
struct HexEncodingOptions: OptionSet {
|
||||
let rawValue: Int
|
||||
static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
|
||||
}
|
||||
|
||||
func hexEncodedString(options: HexEncodingOptions = []) -> String {
|
||||
let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx"
|
||||
return map { String(format: format, $0) }.joined()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@
|
|||
<dict>
|
||||
<key>com.apple.developer.authentication-services.autofill-credential-provider</key>
|
||||
<true/>
|
||||
<key>com.apple.developer.nfc.readersession.formats</key>
|
||||
<array>
|
||||
<string>NDEF</string>
|
||||
<string>TAG</string>
|
||||
</array>
|
||||
<key>com.apple.developer.siri</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@
|
|||
<dict>
|
||||
<key>com.apple.developer.authentication-services.autofill-credential-provider</key>
|
||||
<true/>
|
||||
<key>com.apple.developer.nfc.readersession.formats</key>
|
||||
<array>
|
||||
<string>NDEF</string>
|
||||
<string>TAG</string>
|
||||
</array>
|
||||
<key>com.apple.developer.siri</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue