diff --git a/CryptoLib/CryptoLib.xcodeproj/xcuserdata/martenr.xcuserdatad/xcschemes/xcschememanagement.plist b/CryptoLib/CryptoLib.xcodeproj/xcuserdata/martenr.xcuserdatad/xcschemes/xcschememanagement.plist index 70305072b..0ea84e679 100644 --- a/CryptoLib/CryptoLib.xcodeproj/xcuserdata/martenr.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/CryptoLib/CryptoLib.xcodeproj/xcuserdata/martenr.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ CryptoLib.xcscheme_^#shared#^_ orderHint - 4 + 6 cdoc.xcscheme_^#shared#^_ orderHint - 5 + 4 diff --git a/MoppApp/MoppApp/EncryptedDataUtil.swift b/MoppApp/MoppApp/EncryptedDataUtil.swift index 8be34e7c0..dafcb96f9 100644 --- a/MoppApp/MoppApp/EncryptedDataUtil.swift +++ b/MoppApp/MoppApp/EncryptedDataUtil.swift @@ -77,13 +77,12 @@ struct EncryptedDataUtil { } static func decryptSecret(_ data: Data, with symmetricKey: SymmetricKey) -> String? { - guard let sealedBox = try? ChaChaPoly.SealedBox(combined: data) else { return nil } - do { + let sealedBox = try ChaChaPoly.SealedBox(combined: data) let decryptedData = try ChaChaPoly.open(sealedBox, using: symmetricKey) return String(data: decryptedData, encoding: .utf8) } catch { - printLog("Unable to decrypt secret: \(error.localizedDescription)") + printLog("Unable to decrypt encrypted secret: \(error.localizedDescription)") return nil } } diff --git a/MoppApp/MoppApp/IdCardViewController.swift b/MoppApp/MoppApp/IdCardViewController.swift index 788421b85..1b618aa85 100644 --- a/MoppApp/MoppApp/IdCardViewController.swift +++ b/MoppApp/MoppApp/IdCardViewController.swift @@ -119,7 +119,11 @@ class IdCardViewController : MoppViewController, TokenFlowSigning { @objc func editingChanged(sender: UITextField) { let count = (sender.text?.count ?? 0) - actionButton.isEnabled = count >= 4 && count <= 6 + if self.isActionDecryption { + actionButton.isEnabled = count >= 4 && count <= 12 + } else { + actionButton.isEnabled = count >= 5 && count <= 12 + } if !actionButton.isEnabled { actionButton.backgroundColor = UIColor.moppBackgroundDark } diff --git a/MoppApp/MoppApp/KeychainUtil.swift b/MoppApp/MoppApp/KeychainUtil.swift index 7a68ef5ba..054e0da75 100644 --- a/MoppApp/MoppApp/KeychainUtil.swift +++ b/MoppApp/MoppApp/KeychainUtil.swift @@ -27,17 +27,32 @@ import Security class KeychainUtil { static func save(key: String, info: Data, withPasscodeSetOnly: Bool = false) -> Bool { guard let bundleIdentifier = Bundle.main.bundleIdentifier else { return false } + let query: [CFString: Any] = [ kSecClass: kSecClassGenericPassword, kSecAttrService: bundleIdentifier, kSecAttrAccount: "\(bundleIdentifier).\(key)", + ] + + let attributes: [CFString: Any] = [ kSecValueData: info, kSecAttrAccessible: withPasscodeSetOnly ? kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly : kSecAttrAccessibleWhenUnlockedThisDeviceOnly ] - SecItemDelete(query as CFDictionary) - let status = SecItemAdd(query as CFDictionary, nil) + + let status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary) - return status == errSecSuccess + if status == errSecSuccess { + return true + } else if status == errSecItemNotFound { + // Item does not exist yet, add it + let queryWithAttributes = query.merging(attributes) { (_, new) in new } + + let addStatus = SecItemAdd(queryWithAttributes as CFDictionary, nil) + return addStatus == errSecSuccess + } else { + printLog("Unable to save \(key): \(status)") + return false + } } static func retrieve(key: String) -> Data? { diff --git a/MoppApp/MoppApp/LandingViewController.swift b/MoppApp/MoppApp/LandingViewController.swift index e20bb09a2..a27cdbc71 100644 --- a/MoppApp/MoppApp/LandingViewController.swift +++ b/MoppApp/MoppApp/LandingViewController.swift @@ -135,7 +135,6 @@ class LandingViewController : UIViewController, NativeShare, ContainerActions presentButtons([.signTab, .cryptoTab, .myeIDTab]) selectTabButton(.signTab) - NotificationCenter.default.addObserver(self, selector: #selector(receiveErrorNotification), name: .errorNotificationName, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(receiveStartImportingFilesWithDocumentPickerNotification), name: .startImportingFilesWithDocumentPickerNotificationName, object: nil) LandingViewController.onDataFileAddedAccessibility = { NotificationCenter.default.addObserver( @@ -227,23 +226,6 @@ class LandingViewController : UIViewController, NativeShare, ContainerActions controller.tabBarItem.image = image controller.tabBarItem.selectedImage = selectedImage } - - @objc func receiveErrorNotification(_ notification: Notification) { - guard let userInfo = notification.userInfo else { return } - let error = userInfo[kErrorKey] as? NSError - var errorMessage = error?.userInfo[NSLocalizedDescriptionKey] as? String ?? - userInfo[kErrorMessage] as? String - guard let strongErrorMessage = errorMessage else { return } - errorMessage = MoppLib_LocalizedString(strongErrorMessage) - let alert = UIAlertController( - title: L(.errorAlertTitleGeneral), - message: errorMessage, - preferredStyle: .alert) - - presentedViewController?.dismiss(animated: false, completion: nil) - alert.addAction(UIAlertAction(title: L(.actionOk), style: .default, handler: nil)) - present(alert, animated: true) - } @objc func receiveStartImportingFilesWithDocumentPickerNotification(_ notification: Notification) { fileImportIntent = notification.userInfo![kKeyFileImportIntent] as? MoppApp.FileImportIntent diff --git a/MoppApp/MoppApp/LocalizationKeys.swift b/MoppApp/MoppApp/LocalizationKeys.swift index d5a751ef2..f550ec2df 100644 --- a/MoppApp/MoppApp/LocalizationKeys.swift +++ b/MoppApp/MoppApp/LocalizationKeys.swift @@ -169,6 +169,8 @@ enum LocKey : String case nfcTitle = "nfc-title" case nfcCANTitle = "nfc-can-title" case nfcDeviceNoSupport = "nfc-device-no-support" + case nfcCanLocation = "nfc-can-location" + case nfcIncorrectLength = "nfc-incorrect-length" case nfcHoldNear = "nfc-hold-near" case nfcMultipleCards = "nfc-multiple-cards" case nfcInvalidTag = "nfc-invalid-tag" @@ -183,6 +185,8 @@ enum LocKey : String case nfcPinLocked = "nfc-pin-locked" case nfcCertParseFailed = "nfc-cert-parse-failed" case nfcCertExpired = "nfc-cert-expired" + case nfcWrongPin1 = "nfc-wrong-pin1" + case nfcWrongPin2 = "nfc-wrong-pin2" case containerSignTitle = "container-sign-title" case containerEncryptionTitle = "container-encryption-title" case containerDecryptionTitle = "container-decryption-title" diff --git a/MoppApp/MoppApp/NFCEditViewController.swift b/MoppApp/MoppApp/NFCEditViewController.swift index 1c0973b8a..8902dda47 100644 --- a/MoppApp/MoppApp/NFCEditViewController.swift +++ b/MoppApp/MoppApp/NFCEditViewController.swift @@ -35,7 +35,7 @@ class NFCEditViewController : MoppViewController, TokenFlowSigning { @IBOutlet weak var titleLabel: UILabel! @IBOutlet weak var canTextLabel: UILabel! @IBOutlet weak var pinTextLabel: UILabel! - @IBOutlet weak var canTextErrorLabel: UILabel! + @IBOutlet weak var canTextInfoLabel: ScaledLabel! @IBOutlet weak var pinTextErrorLabel: UILabel! @IBOutlet weak var cancelButton: MoppButton! @IBOutlet weak var signButton: MoppButton! @@ -56,8 +56,8 @@ class NFCEditViewController : MoppViewController, TokenFlowSigning { pinTextLabel.text = L(.pin2TextfieldLabel) pinTextLabel.isHidden = notAvailable - canTextErrorLabel.text = "" - canTextErrorLabel.isHidden = true + setCANDefaultText() + pinTextErrorLabel.text = "" pinTextErrorLabel.isHidden = true @@ -84,6 +84,20 @@ class NFCEditViewController : MoppViewController, TokenFlowSigning { tapGR.addTarget(self, action: #selector(cancelAction)) view.addGestureRecognizer(tapGR) } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + if let canNumber = canTextField.text { + if !canNumber.isEmpty && canNumber.count != 6 { + setCANErrorText() + } else { + setCANDefaultText() + } + } else { + setCANDefaultText() + } + } @objc func dismissKeyboard(_ notification: NSNotification) { pinTextField.resignFirstResponder() @@ -196,6 +210,18 @@ class NFCEditViewController : MoppViewController, TokenFlowSigning { override func keyboardWillHide(notification: NSNotification) { hideKeyboard(scrollView: scrollView) } + + func setCANDefaultText() { + canTextInfoLabel.text = L(.nfcCanLocation) + canTextInfoLabel.isHidden = false + canTextInfoLabel.textColor = UIColor.moppText + } + + func setCANErrorText() { + canTextInfoLabel.text = L(.nfcIncorrectLength) + canTextInfoLabel.isHidden = false + canTextInfoLabel.textColor = UIColor.moppError + } } extension NFCEditViewController : UITextFieldDelegate { @@ -226,12 +252,17 @@ extension NFCEditViewController : UITextFieldDelegate { func textFieldDidEndEditing(_ textField: UITextField) { if textField.accessibilityIdentifier == "nfcCanField" { if let can = textField.text { + if !can.isEmpty && can.count != 6 { + setCANErrorText() + } else { + setCANDefaultText() + } do { let symKey = try EncryptedDataUtil.getSymmetricKey(fileName: NFCEditViewController.nfcCANKeyFilename) if let encryptedKey = EncryptedDataUtil.encryptSecret(can, with: symKey) { _ = KeychainUtil.save(key: NFCEditViewController.nfcCANKey, info: encryptedKey, withPasscodeSetOnly: true) } else { - printLog("Encryption failed for 'can' string") + printLog("Encryption failed for 'CAN' string") } } catch { do { @@ -249,8 +280,7 @@ extension NFCEditViewController : UITextFieldDelegate { } } else { KeychainUtil.remove(key: NFCEditViewController.nfcCANKey) - canTextErrorLabel.text = "" - canTextErrorLabel.isHidden = true + setCANDefaultText() removeViewBorder(view: textField) UIAccessibility.post(notification: .layoutChanged, argument: canTextField) } diff --git a/MoppApp/MoppApp/NFCSignature.swift b/MoppApp/MoppApp/NFCSignature.swift index 2543cab6b..5c456474a 100644 --- a/MoppApp/MoppApp/NFCSignature.swift +++ b/MoppApp/MoppApp/NFCSignature.swift @@ -83,6 +83,7 @@ class NFCSignature : NSObject, NFCTagReaderSessionDelegate { var ksEnc: Bytes? var ksMac: Bytes? var SSC: Bytes? + var invalidateWithError: Bool = false func createNFCSignature(can: String, pin: String, containerPath: String, hashType: String, roleData: MoppLibRoleAddressData?) -> Void { guard NFCTagReaderSession.readingAvailable else { @@ -92,6 +93,7 @@ class NFCSignature : NSObject, NFCTagReaderSessionDelegate { CAN = can PIN = pin self.containerPath = containerPath + invalidateWithError = false session = NFCTagReaderSession(pollingOption: .iso14443, delegate: self) session?.alertMessage = L(.nfcHoldNear) session?.begin() @@ -107,6 +109,8 @@ class NFCSignature : NSObject, NFCTagReaderSessionDelegate { func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) { @Sendable func setSessionMessage(_ msg: String, invalidate: Bool = false) -> Void { + invalidateWithError = invalidate + if invalidate { session.invalidate(errorMessage: msg) } else { @@ -214,7 +218,7 @@ class NFCSignature : NSObject, NFCTagReaderSessionDelegate { switch attemptsLeft { case 0: setSessionMessage(L(.pin2BlockedAlert), invalidate: true) case 1: setSessionMessage(L(.wrongPin2Single), invalidate: true) - default: setSessionMessage(L(.wrongPin2, [attemptsLeft]), invalidate: true) + default: setSessionMessage(L(.nfcWrongPin2, [attemptsLeft]), invalidate: true) } } catch NFCError.error(let msg) { printLog("\nRIA.NFC - error \(msg)") @@ -230,11 +234,13 @@ class NFCSignature : NSObject, NFCTagReaderSessionDelegate { } func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) { - if let readerError = error as? NFCReaderError, - readerError.code == .readerSessionInvalidationErrorUserCanceled { - CancelUtil.handleCancelledRequest(errorMessageDetails: "User cancelled NFC signing") - } else { - CancelUtil.handleCancelledRequest(errorMessageDetails: "Session Invalidated") + if !invalidateWithError { + if let readerError = error as? NFCReaderError, + readerError.code == .readerSessionInvalidationErrorUserCanceled { + CancelUtil.handleCancelledRequest(errorMessageDetails: "User cancelled NFC signing") + } else { + CancelUtil.handleCancelledRequest(errorMessageDetails: "Session Invalidated") + } } self.session = nil } diff --git a/MoppApp/MoppApp/TokenFlow.storyboard b/MoppApp/MoppApp/TokenFlow.storyboard index 7373559da..34410b3ea 100644 --- a/MoppApp/MoppApp/TokenFlow.storyboard +++ b/MoppApp/MoppApp/TokenFlow.storyboard @@ -3,7 +3,7 @@ - + @@ -1418,7 +1418,7 @@ - +