diff --git a/Classes/LinphoneAppDelegate.m b/Classes/LinphoneAppDelegate.m index 3b430148b..f070e0646 100644 --- a/Classes/LinphoneAppDelegate.m +++ b/Classes/LinphoneAppDelegate.m @@ -33,6 +33,8 @@ #import #import +#import "linphoneapp-Swift.h" + #ifdef USE_CRASHLYTICS #include "FIRApp.h" @@ -263,6 +265,12 @@ #ifdef USE_CRASHLYTICS [FIRApp configure]; #endif + + + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"vfs_enabled"] && !VFSUtil.activateVFS) { + [VFSUtil oslogWithLog:@"[VFS] Error unable to activate." level:OS_LOG_TYPE_ERROR]; + } + UIApplication *app = [UIApplication sharedApplication]; UIApplicationState state = app.applicationState; diff --git a/Classes/VFSUtil.swift b/Classes/VFSUtil.swift new file mode 100644 index 000000000..8389077cb --- /dev/null +++ b/Classes/VFSUtil.swift @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linphone-iphone + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import UIKit +import Foundation +import Security +import CommonCrypto +import linphonesw +import os + + +@objc class VFSUtil: NSObject { + + @objc static let keyName = Bundle.main.bundleIdentifier!+".vfskey" + @objc static let prefName = Bundle.main.bundleIdentifier!+".vfspref" + + @objc static func generateKey(requiresBiometry: Bool = false) throws { + + let flags: SecAccessControlCreateFlags + if #available(iOS 11.3, *) { + flags = requiresBiometry ? + [.privateKeyUsage, .biometryCurrentSet] : .privateKeyUsage + } else { + flags = requiresBiometry ? + [.privateKeyUsage, .touchIDCurrentSet] : .privateKeyUsage + } + let access = + SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenUnlockedThisDeviceOnly,flags,nil)! + let tag = keyName.data(using: .utf8)! + let attributes: [String: Any] = [ + kSecAttrKeyType as String : kSecAttrKeyTypeECSECPrimeRandom, + kSecAttrKeySizeInBits as String : 256, + kSecAttrTokenID as String : kSecAttrTokenIDSecureEnclave, + kSecPrivateKeyAttrs as String : [ + kSecAttrIsPermanent as String : true, + kSecAttrApplicationTag as String : tag, + kSecAttrAccessControl as String : access + ] + ] + + var error: Unmanaged? + guard let _ = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else { + throw error!.takeRetainedValue() as Error + } + } + + @objc static func loadKey(name: String) -> SecKey? { + let tag = name.data(using: .utf8)! + let query: [String: Any] = [ + kSecClass as String : kSecClassKey, + kSecAttrApplicationTag as String : tag, + kSecAttrKeyType as String : kSecAttrKeyTypeEC, + kSecReturnRef as String : true + ] + + var item: CFTypeRef? + let status = SecItemCopyMatching(query as CFDictionary, &item) + guard status == errSecSuccess else { + return nil + } + return (item as! SecKey) + } + + + @objc static func encrypt(clearText: String) -> String? { + let algorithm: SecKeyAlgorithm = .eciesEncryptionCofactorX963SHA512AESGCM + guard let privateKey = loadKey(name: keyName), let publicKey = SecKeyCopyPublicKey(privateKey), SecKeyIsAlgorithmSupported(publicKey, .encrypt, algorithm) else { + return nil + } + var error: Unmanaged? + let clearTextData = clearText.data(using: .utf8)! + guard let encryptedData = SecKeyCreateEncryptedData(publicKey, algorithm,clearTextData as CFData, &error) as Data? else { + return nil + } + return encryptedData.base64EncodedString() + } + + @objc static func decrypt(encryptedText: String) -> String? { + let algorithm: SecKeyAlgorithm = .eciesEncryptionCofactorX963SHA512AESGCM + guard let key = loadKey(name: keyName), SecKeyIsAlgorithmSupported(key, .decrypt, algorithm) else { + return nil + } + var error: Unmanaged? + guard let clearTextData = SecKeyCreateDecryptedData(key,algorithm,Data(base64Encoded: encryptedText)! as CFData,&error) as Data? else { + print("[VFS] failed deciphering data \(String(describing: error))") + return nil + } + return String(decoding: clearTextData, as: UTF8.self) + } + + + @objc static func addSecuredPreference(key:String, value:String) -> Bool { + let delQuery: [String: Any] = [kSecClass as String: kSecClassGenericPassword,kSecAttrAccount as String: key.data(using: .utf8)!] + SecItemDelete(delQuery as CFDictionary) + + + let insertQUery: [String: Any] = [kSecClass as String: kSecClassGenericPassword,kSecAttrAccount as String: key.data(using: .utf8)!, kSecValueData as String:value.data(using: .utf8)!] + let insertStatus = SecItemAdd(insertQUery as CFDictionary, nil) + return insertStatus == errSecSuccess + + } + + @objc static func getSecuredPreference(key:String) -> String? { + let query: [String:Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrAccount as String: key.data(using: .utf8)!, + kSecReturnData as String: kCFBooleanTrue + ] + + var result: AnyObject? + let status: OSStatus = withUnsafeMutablePointer(to: &result) { + SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) + } + return status == errSecSuccess ? String(decoding: result as! Data , as: UTF8.self) : nil + } + + @objc static func randomSha512() -> String { + let data = UUID.init().uuidString.data(using: .utf8)! + var digest = [UInt8](repeating: 0, count: Int(CC_SHA512_DIGEST_LENGTH)) + data.withUnsafeBytes({ + _ = CC_SHA512($0, CC_LONG(data.count), &digest) + }) + return digest.map({ String(format: "%02hhx", $0) }).joined(separator: "") + } + + + + @objc static func activateVFS() -> Bool { + do { + if (getSecuredPreference(key: prefName) == nil) { + oslog(log: "[VFS] no secret key set, building one.", level: .info) + try generateKey(requiresBiometry: false) + guard let encryptedHash = encrypt(clearText: randomSha512()) else { + return false + } + if (!addSecuredPreference(key: prefName, value: encryptedHash)) { + oslog(log: "[VFS] Unable to save encrypted key in secured defaults.", level: .error) + } + } + guard let encryptedKey = getSecuredPreference(key: prefName) else { + oslog(log: "[VFS] Unable to retrieve encrypted key.", level: .error) + return false + } + let secret = decrypt(encryptedText: encryptedKey) + Factory.Instance.setVfsEncryption(encryptionModule: 2, secret: secret, secretSize: 32) + oslog(log: "[VFS] activated", level: .info) + return true + } catch { + oslog(log: "[VFS] Error setting up VFS: \(error)", level: .info) + return false + } + } + + + @objc static func oslog(log:String, level: OSLogType) { + if #available(iOS 10.0, *) { + os_log("%{public}@", type: level,log) + } else { + NSLog(log) + } + } + + +} diff --git a/linphone.xcodeproj/project.pbxproj b/linphone.xcodeproj/project.pbxproj index 417c5b1f3..cc3379af6 100644 --- a/linphone.xcodeproj/project.pbxproj +++ b/linphone.xcodeproj/project.pbxproj @@ -673,6 +673,7 @@ 8CF25D9E1F9F76BD00BEA0C1 /* chat_group_informations@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CF25D9C1F9F76BD00BEA0C1 /* chat_group_informations@2x.png */; }; 93566413F75DA69D2811A716 /* Pods_msgNotificationService.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F30EA7BEA39DA427CE0754E /* Pods_msgNotificationService.framework */; }; A634ABAFCB39B6AAE4CA991D /* Pods_linphone.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65CEDD144CABFAA70A29AF27 /* Pods_linphone.framework */; }; + C6DA657C261C950C0020CB43 /* VFSUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6DA657B261C950C0020CB43 /* VFSUtil.swift */; }; C90FAA7915AF54E6002091CB /* HistoryDetailsView.m in Sources */ = {isa = PBXBuildFile; fileRef = C90FAA7715AF54E6002091CB /* HistoryDetailsView.m */; }; CF15F21E20E4F9A3008B1DE6 /* UIImageViewDeletable.m in Sources */ = {isa = PBXBuildFile; fileRef = CF15F21C20E4F9A3008B1DE6 /* UIImageViewDeletable.m */; }; CF15F21F20E4F9A3008B1DE6 /* UIImageViewDeletable.xib in Resources */ = {isa = PBXBuildFile; fileRef = CF15F21D20E4F9A3008B1DE6 /* UIImageViewDeletable.xib */; }; @@ -1705,6 +1706,7 @@ ADCA571A7CF61077747BFE53 /* Pods-msgNotificationService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-msgNotificationService.debug.xcconfig"; path = "Pods/Target Support Files/Pods-msgNotificationService/Pods-msgNotificationService.debug.xcconfig"; sourceTree = ""; }; BAD0A9494E833034EB559687 /* Pods-msgNotificationContent.distributionadhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-msgNotificationContent.distributionadhoc.xcconfig"; path = "Pods/Target Support Files/Pods-msgNotificationContent/Pods-msgNotificationContent.distributionadhoc.xcconfig"; sourceTree = ""; }; BE06BDE664323B2A53469696 /* Pods-liblinphoneTesterTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-liblinphoneTesterTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-liblinphoneTesterTests/Pods-liblinphoneTesterTests.release.xcconfig"; sourceTree = ""; }; + C6DA657B261C950C0020CB43 /* VFSUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VFSUtil.swift; sourceTree = ""; }; C90FAA7615AF54E6002091CB /* HistoryDetailsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HistoryDetailsView.h; sourceTree = ""; }; C90FAA7715AF54E6002091CB /* HistoryDetailsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HistoryDetailsView.m; sourceTree = ""; }; C9B3A6FD15B485DB006F52EE /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Utils.h; path = Utils/Utils.h; sourceTree = ""; }; @@ -2126,6 +2128,7 @@ D326483415887D4400930C67 /* Utils */, 34216F3E1547EBCD00EA9777 /* VideoZoomHandler.h */, 34216F3F1547EBCD00EA9777 /* VideoZoomHandler.m */, + C6DA657B261C950C0020CB43 /* VFSUtil.swift */, 614C087723D1A35F00217F80 /* ProviderDelegate.swift */, 614C087923D1A37400217F80 /* CallManager.swift */, EA88F3B0241BDAA100E66528 /* CoreManager.swift */, @@ -4235,6 +4238,7 @@ D37DC6C11594AE1800B2A5EB /* LinphoneCoreSettingsStore.m in Sources */, 63CD4B4F1A5AAC8C00B84282 /* DTAlertView.m in Sources */, D3EA53FD159850E80037DC6B /* LinphoneManager.m in Sources */, + C6DA657C261C950C0020CB43 /* VFSUtil.swift in Sources */, 63B81A0E1B57DA33009604A6 /* TPKeyboardAvoidingScrollView.m in Sources */, 633888451BFB2C49001D5E7B /* HPGrowingTextView.m in Sources */, 63F1DF441BCE618E00EDED90 /* UIAddressTextField.m in Sources */,