// // ScreenLockViewControllerSwift.swift // MCPlus // // Created by seo ha on 14/02/2019. // Copyright © 2019 KangSH. All rights reserved. // import Foundation import UIKit import LocalAuthentication import AudioToolbox class ScreenLockViewController: UIViewController { @IBOutlet weak var topContainer:UIView! @IBOutlet weak var mainTitle:UILabel! @IBOutlet weak var subTitle:UILabel! @IBOutlet weak var password1:UIView! @IBOutlet weak var password2:UIView! @IBOutlet weak var password3:UIView! @IBOutlet weak var password4:UIView! @IBOutlet weak var password5:UIView! @IBOutlet weak var password6:UIView! @IBOutlet weak var warningText:UILabel! @IBOutlet weak var topMargin:NSLayoutConstraint! @IBOutlet weak var btnCancel:UIButton! let userDefaults = UserDefaults.standard var arrPreviousPassword = [Int]() var arrPassword = [Int]() deinit { Notification.removeNotification(observer: self) } func chargingPasswordViews(){ print("chargingPasswordViews count: \(arrPassword.count)"); switch arrPassword.count { case 0: self.password1.backgroundColor = UIColor.white self.password2.backgroundColor = UIColor.white self.password3.backgroundColor = UIColor.white self.password4.backgroundColor = UIColor.white self.password5.backgroundColor = UIColor.white self.password6.backgroundColor = UIColor.white break; case 1: self.password1.backgroundColor = UIColor.black self.password2.backgroundColor = UIColor.white self.password3.backgroundColor = UIColor.white self.password4.backgroundColor = UIColor.white self.password5.backgroundColor = UIColor.white self.password6.backgroundColor = UIColor.white break; case 2: self.password1.backgroundColor = UIColor.black self.password2.backgroundColor = UIColor.black self.password3.backgroundColor = UIColor.white self.password4.backgroundColor = UIColor.white self.password5.backgroundColor = UIColor.white self.password6.backgroundColor = UIColor.white break; case 3: self.password1.backgroundColor = UIColor.black self.password2.backgroundColor = UIColor.black self.password3.backgroundColor = UIColor.black self.password4.backgroundColor = UIColor.white self.password5.backgroundColor = UIColor.white self.password6.backgroundColor = UIColor.white break; case 4: self.password1.backgroundColor = UIColor.black self.password2.backgroundColor = UIColor.black self.password3.backgroundColor = UIColor.black self.password4.backgroundColor = UIColor.black self.password5.backgroundColor = UIColor.white self.password6.backgroundColor = UIColor.white break; case 5: self.password1.backgroundColor = UIColor.black self.password2.backgroundColor = UIColor.black self.password3.backgroundColor = UIColor.black self.password4.backgroundColor = UIColor.black self.password5.backgroundColor = UIColor.black self.password6.backgroundColor = UIColor.white break; case 6: self.password1.backgroundColor = UIColor.black self.password2.backgroundColor = UIColor.black self.password3.backgroundColor = UIColor.black self.password4.backgroundColor = UIColor.black self.password5.backgroundColor = UIColor.black self.password6.backgroundColor = UIColor.black break; default: break; } } func textRewrite(){ let lockScreenDao = LockScreenDAO.sharedInstance if let pwInputType = lockScreenDao.pwInputType{ switch pwInputType { case .FIRST_INPUT: self.mainTitle.text = "암호입력" self.subTitle.text = "암호를 입력해 주세요" case .CONFIRM_INPUT: self.mainTitle.text = "암호 재입력" self.subTitle.text = "확인을 위해 한 번 더 입력해 주세요" case .UNLOCK_INPUT: self.mainTitle.text = "암호입력" self.subTitle.text = "암호를 입력해 주세요" case .UNLOCK_FAIL_RE_INPUT: self.mainTitle.text = "암호입력" self.subTitle.text = "암호가 틀렸습니다. 다시 입력해 주세요" case .CHANGE_FIRST_INPUT: self.mainTitle.text = "새로운 암호입력" self.subTitle.text = "새로운 암호를 입력해 주세요" case .CHANGE_CONFIRM_INPUT: self.mainTitle.text = "새로운 암호 재입력" self.subTitle.text = "확인을 위해 한 번 더 입력해 주세요" } } } } extension ScreenLockViewController{ func appWillEnterBackground(notification:Notification){ print("will enter background notification") let lockScreenDao = LockScreenDAO.sharedInstance arrPreviousPassword = [Int]() arrPassword = [Int]() if let pwInputType = lockScreenDao.pwInputType{ switch pwInputType { case .FIRST_INPUT: break case .CONFIRM_INPUT: lockScreenDao.pwInputType = LockScreenDAO.PASSWORD_INPUT_TYPE.FIRST_INPUT lockScreenDao.saveOptionValuesWithoutPassword() case .UNLOCK_INPUT: break case .UNLOCK_FAIL_RE_INPUT: fallthrough case .CHANGE_FIRST_INPUT: fallthrough case .CHANGE_CONFIRM_INPUT: lockScreenDao.pwInputType = LockScreenDAO.PASSWORD_INPUT_TYPE.UNLOCK_INPUT lockScreenDao.saveOptionValuesWithoutPassword() break } } self.textRewrite() self.chargingPasswordViews() } override func viewWillAppear(_ animated: Bool) { guard #available(iOS 11.0, *) else{ return } //지문인식 let context = LAContext() var error2:NSError? guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error2) else { return } if context.biometryType == .faceID{ return } let error:AutoreleasingUnsafeMutablePointer? = nil let temp = self.userDefaults.object(forKey: "fingerprint") as? String var isSwitchOn = true if let _temp = temp{ isSwitchOn = temp != "OFF" }else{ // 지문입력 받음 } let lockScreenDao = LockScreenDAO.sharedInstance print("lockScreenDao.pwInputType >>>>>>>>>>>>> \(lockScreenDao.pwInputType)") // 지문입력은 락을 풀때만 사용해야 한다. var isUnlockStatus = false if lockScreenDao.pwInputType == LockScreenDAO.PASSWORD_INPUT_TYPE(rawValue: 2){ isUnlockStatus = true } if isUnlockStatus, isSwitchOn, context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: error){ context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "지문을 입력해주세요") { (success, error) in if error != nil{ print("Touch 지원하지 않음") return } guard success == true else{ print("@@@@실패") return } print("@@@@성공") // 지문 인증 성공했을때 let now = Date() let dateFormatString = "yyyy-MM-dd hh:mm:ss" let date = now.fromString(format: dateFormatString) print(date) // String으로 저장 self.userDefaults.set(date, forKey: "fingerprintAuthTime") self.dismissSelf() } } } override func viewDidAppear(_ animated: Bool) { print("viewDidAppear") } override func viewDidLoad() { super.viewDidLoad() Notification.registerNotification(name: UIApplication.didEnterBackgroundNotification, queue: .main, block: appWillEnterBackground) let lockScreenDao = LockScreenDAO.sharedInstance //업데이트로 인한 초기화 (v4 비밀번호 4자리 -> 6자리 변경이슈) if isOlderVersionPassword(){ lockScreenDao.pwInputType = LockScreenDAO.PASSWORD_INPUT_TYPE.FIRST_INPUT } print("lockScreenDao.pwInputType >>>>>>>>>>>>> \(lockScreenDao.pwInputType)") self.btnCancel.isHidden = lockScreenDao.pwInputType != LockScreenDAO.PASSWORD_INPUT_TYPE.CHANGE_FIRST_INPUT && lockScreenDao.pwInputType != LockScreenDAO.PASSWORD_INPUT_TYPE.CHANGE_CONFIRM_INPUT switch Constants.kScreenHeight { case ...568: self.topMargin.constant = 40 case ...480: self.topMargin.constant = 30 default: break } self.password1.layer.cornerRadius = 10.0 self.password1.layer.masksToBounds = true self.password2.layer.cornerRadius = 10.0 self.password2.layer.masksToBounds = true self.password3.layer.cornerRadius = 10.0 self.password3.layer.masksToBounds = true self.password4.layer.cornerRadius = 10.0 self.password4.layer.masksToBounds = true self.password5.layer.cornerRadius = 10.0 self.password5.layer.masksToBounds = true self.password6.layer.cornerRadius = 10.0 self.password6.layer.masksToBounds = true lockScreenDao.loadOptionValues() if let pwInputType = lockScreenDao.pwInputType{ switch pwInputType { case .FIRST_INPUT: break case .CONFIRM_INPUT: lockScreenDao.pwInputType = LockScreenDAO.PASSWORD_INPUT_TYPE.FIRST_INPUT lockScreenDao.saveOptionValuesWithoutPassword() case .UNLOCK_INPUT: break case .UNLOCK_FAIL_RE_INPUT: lockScreenDao.pwInputType = LockScreenDAO.PASSWORD_INPUT_TYPE.UNLOCK_INPUT lockScreenDao.saveOptionValuesWithoutPassword() case .CHANGE_FIRST_INPUT: break case .CHANGE_CONFIRM_INPUT: break } } self.textRewrite() self.chargingPasswordViews() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } func savePasswordOnArray(number:Int){ let lockScreenDao = LockScreenDAO.sharedInstance print("arrPassword count: \(arrPassword.count)") print("lockScreenDao.pwInputType : \(lockScreenDao.pwInputType)") switch arrPassword.count { case ..<6: arrPassword.append(number) //뷰 초기화 self.chargingPasswordViews() self.printPassword() self.printPreviousPassword() Thread.sleep(forTimeInterval: 0.1) if arrPassword.count == 6{ fallthrough } case 6: if let pwInputType = lockScreenDao.pwInputType{ switch pwInputType { case .FIRST_INPUT: let pw = self.arrPassword.reduce("", {"\($0)\($1)"}) guard self.isValidNumber(pw: pw) else{ arrPassword = [Int]() self.textRewrite() self.mainTitle.text = "암호입력" self.subTitle.text = "연속된 암호를 사용할 수 없습니다. 다시 입력해 주세요" AudioServicesPlaySystemSound(kSystemSoundID_Vibrate) //뷰 초기화 DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in self?.chargingPasswordViews() } return } //처음 암호를 입력했을 때, 확인모드로 변경. lockScreenDao.pwInputType = LockScreenDAO.PASSWORD_INPUT_TYPE.CONFIRM_INPUT self.textRewrite() //암호 입력 새로 받음 arrPreviousPassword = arrPassword.map({$0}) arrPassword = [Int]() //뷰 초기화 DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in self?.chargingPasswordViews() } case .CONFIRM_INPUT: // 암호가 일치하는지 판단 if self.comparePasswords(){ // 패스워드 저장 후 모달창 닫음. lockScreenDao.pwInputType = LockScreenDAO.PASSWORD_INPUT_TYPE.UNLOCK_INPUT lockScreenDao.saveOptionValues(arrPassword: arrPassword) //뷰 초기화 self.chargingPasswordViews() DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in self?.dismissSelf() } }else{ // 처음부터 다시 입력 받음 lockScreenDao.pwInputType = LockScreenDAO.PASSWORD_INPUT_TYPE.FIRST_INPUT self.mainTitle.text = "암호입력" self.subTitle.text = "암호가 틀렸습니다. 다시 입력해 주세요" AudioServicesPlaySystemSound(kSystemSoundID_Vibrate) self.printPassword() self.printPreviousPassword() arrPreviousPassword = [Int]() arrPassword = [Int]() //뷰 초기화 DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in self?.chargingPasswordViews() } } case .UNLOCK_INPUT: print("UNLOCK_INPUT 1") // 암호가 일치하는지 판단 if self.comparePasswordWithStorage(){ //모달창 닫음. print("UNLOCK_INPUT 2") // 패스워드 저장 후 모달창 닫음. lockScreenDao.pwInputType = LockScreenDAO.PASSWORD_INPUT_TYPE.UNLOCK_INPUT lockScreenDao.saveOptionValuesWithoutPassword() print("UNLOCK_INPUT 3") //뷰 초기화 self.chargingPasswordViews() DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in self?.dismissSelf() } }else{ //초기화 하고 다시 입력 받음 lockScreenDao.pwInputType = LockScreenDAO.PASSWORD_INPUT_TYPE.UNLOCK_FAIL_RE_INPUT arrPassword = [Int]() self.mainTitle.text = "암호입력" self.subTitle.text = "암호가 틀렸습니다. 다시 입력해 주세요" AudioServicesPlaySystemSound(kSystemSoundID_Vibrate) self.printPassword() self.printPreviousPassword() //뷰 초기화 DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in self?.chargingPasswordViews() } } case .UNLOCK_FAIL_RE_INPUT: if self.comparePasswordWithStorage(){ lockScreenDao.pwInputType = LockScreenDAO.PASSWORD_INPUT_TYPE.UNLOCK_INPUT lockScreenDao.saveOptionValuesWithoutPassword() //모달창 닫음. //뷰 초기화 self.chargingPasswordViews() DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in self?.dismissSelf() } }else{ //초기화 하고 다시 입력 받음 AudioServicesPlaySystemSound(kSystemSoundID_Vibrate) lockScreenDao.pwInputType = LockScreenDAO.PASSWORD_INPUT_TYPE.UNLOCK_FAIL_RE_INPUT arrPassword = [Int]() //뷰 초기화 DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in self?.chargingPasswordViews() } } case .CHANGE_FIRST_INPUT: //처음 암호를 입력했을 때, 변경확인모드로 변경. lockScreenDao.pwInputType = LockScreenDAO.PASSWORD_INPUT_TYPE.CHANGE_CONFIRM_INPUT self.textRewrite() //암호 입력 새로 받음 arrPreviousPassword = [Int]() arrPassword = [Int]() //뷰 초기화 DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in self?.chargingPasswordViews() } case .CHANGE_CONFIRM_INPUT: // 암호가 일치하는지 판단 if self.comparePasswords(){ // 패스워드 저장 후 모달창 닫음. lockScreenDao.pwInputType = LockScreenDAO.PASSWORD_INPUT_TYPE.UNLOCK_INPUT lockScreenDao.saveOptionValues(arrPassword: arrPassword) //뷰 초기화 self.chargingPasswordViews() DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in self?.dismissSelf() } }else{ // 처음부터 다시 입력 받음 lockScreenDao.pwInputType = LockScreenDAO.PASSWORD_INPUT_TYPE.CHANGE_FIRST_INPUT self.mainTitle.text = "새로운 암호입력" self.subTitle.text = "암호가 틀렸습니다. 새로운 암호를 입력해 주세요" AudioServicesPlaySystemSound(kSystemSoundID_Vibrate) self.printPassword() self.printPreviousPassword() arrPreviousPassword = [Int]() arrPassword = [Int]() //뷰 초기화 DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in self?.chargingPasswordViews() } } } } default: break } } func comparePasswords() -> Bool { print("comparePasswords start") let pw1 = arrPreviousPassword.reduce("", {"\($0)\($1)"}) let pw2 = arrPassword.reduce("", {"\($0)\($1)"}) print("comparePasswords end") return pw1 == pw2 } func comparePasswordWithStorage() -> Bool{ var isEncEqual = false let lockScreenDao = LockScreenDAO.sharedInstance //암호화 라이브러리 RNCrypter는 랜덤 salt를 쓰므로 복호화를 통하여 암호 비교 let pw1 = arrPassword.reduce("", {"\($0)\($1)"}) let pw2 = [0, 1, 2, 3, 4, 5].map({lockScreenDao.encryptedPasswordWithKey(key: "\($0)")}) .compactMap({$0}) .map({$0.decryptWithKey(key: Constants.PACS_APP_ENC_KEY)}) .compactMap({$0}) .reduce("", {"\($0)\($1)"}) print("\(pw1),\(pw2)") isEncEqual = pw1 == pw2 print("암호화된 키가 일치하는가?> \(isEncEqual ? "참" : "거짓")") return isEncEqual } func isValidNumber(pw:String) -> Bool { print(pw) let pwRegEx = "^(?!.*?(?:0(?:12|98)|123|234|3(?:45|21)|4(?:56|32)|5(?:67|43)|6(?:78|54)|7(?:89|65)|876|987))(?!.*?(.)\\1{2})[0-9]{6}" let pwTest = NSPredicate(format:"SELF MATCHES %@", pwRegEx) return pwTest.evaluate(with: pw) } func dismissSelf(){ Notification.removeNotification(observer: self) self.dismiss(animated: false, completion: nil) } func printPassword(){ for i in 0.. @IBAction func clickNum1(sender:Any?){ self.savePasswordOnArray(number: 1) } @IBAction func clickNum2(sender:Any?){ self.savePasswordOnArray(number: 2) } @IBAction func clickNum3(sender:Any?){ self.savePasswordOnArray(number: 3) } @IBAction func clickNum4(sender:Any?){ self.savePasswordOnArray(number: 4) } @IBAction func clickNum5(sender:Any?){ self.savePasswordOnArray(number: 5) } @IBAction func clickNum6(sender:Any?){ self.savePasswordOnArray(number: 6) } @IBAction func clickNum7(sender:Any?){ self.savePasswordOnArray(number: 7) } @IBAction func clickNum8(sender:Any?){ self.savePasswordOnArray(number: 8) } @IBAction func clickNum9(sender:Any?){ self.savePasswordOnArray(number: 9) } @IBAction func clickNum0(sender:Any?){ self.savePasswordOnArray(number: 0) } @IBAction func clickNumBackspace(sender:Any?){ let count = arrPassword.count if count > 0{ arrPassword.removeLast() } self.chargingPasswordViews() self.printPassword() } @IBAction func clickNumCancel(sender:Any?){ print("취소") let lockScreenDao = LockScreenDAO.sharedInstance lockScreenDao.pwInputType = LockScreenDAO.PASSWORD_INPUT_TYPE.UNLOCK_INPUT //뷰 초기화 self.chargingPasswordViews() self.dismissSelf() } func isOlderVersionPassword() -> Bool{ let lockScreenDao = LockScreenDAO.sharedInstance let pw = [0, 1, 2, 3, 4, 5].map({lockScreenDao.encryptedPasswordWithKey(key: "\($0)")}) .compactMap({$0}) .map({$0.decryptWithKey(key: Constants.PACS_APP_ENC_KEY)}) .compactMap({$0}) .reduce("", {"\($0)\($1)"}) return pw.count == 4 } }