FileEncryption.swift 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. //
  2. // FileEncryption.swift
  3. // NexilisLite
  4. //
  5. // Created by Maronakins on 03/12/24.
  6. //
  7. import Foundation
  8. import CryptoKit
  9. import CommonCrypto
  10. public class FileEncryption {
  11. public var aesKey: SymmetricKey?
  12. public var aesIV: Data?
  13. public static let shared = FileEncryption()
  14. private init() {}
  15. public func readSecure(filename: String) throws -> Data? {
  16. if Utils.getFeatureAccess().isEmpty {
  17. return nil
  18. }
  19. let fileManager = FileManager.default
  20. let documentDir = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
  21. let fileURL = documentDir.appendingPathComponent("Secure").appendingPathComponent(filename)
  22. let encryptedData = try Data(contentsOf: fileURL)
  23. let sealedBox = try AES.GCM.SealedBox(combined: encryptedData)
  24. let decryptedData = try AES.GCM.open(sealedBox, using: MasterKeyUtil.shared.getMasterKey())
  25. return decryptedData
  26. }
  27. public func writeSecure(filename: String? = nil, data: Data? = nil) throws {
  28. do {
  29. let fileManager = FileManager.default
  30. // Get the app's Documents directory
  31. let documentDir = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
  32. // Optional: Create a "Secure" subdirectory
  33. let secureDir = documentDir.appendingPathComponent("Secure", isDirectory: true)
  34. if !fileManager.fileExists(atPath: secureDir.path) {
  35. try fileManager.createDirectory(at: secureDir, withIntermediateDirectories: true)
  36. }
  37. // Create the file URL
  38. let fileURL = secureDir.appendingPathComponent(filename ?? "")
  39. // Encrypt the data
  40. let sealedBox = try AES.GCM.seal(data ?? Data(), using: MasterKeyUtil.shared.getMasterKey())
  41. let encryptedData = sealedBox.combined!
  42. // Write encrypted data to file
  43. try encryptedData.write(to: fileURL)
  44. } catch {
  45. }
  46. }
  47. public func isSecureExists(filename: String) -> Bool {
  48. do {
  49. let fileManager = FileManager.default
  50. let documentDir = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
  51. let fileURL = documentDir.appendingPathComponent("Secure").appendingPathComponent(filename)
  52. return fileManager.fileExists(atPath: fileURL.path)
  53. } catch {
  54. return false
  55. }
  56. }
  57. public func decryptFileFromServer(data: Data) -> Data? {
  58. do {
  59. if aesKey == nil && !Utils.getSecureFolderEncrypt().isEmpty {
  60. aesKey = try getAESKey()
  61. }
  62. if aesIV == nil && !Utils.getSecureFolderEncryptIv().isEmpty {
  63. aesIV = try getAESIV()
  64. }
  65. } catch {
  66. print("Error retrieving AES key or IV: \(error)")
  67. return nil
  68. }
  69. guard let key = aesKey, let iv = aesIV else {
  70. return nil
  71. }
  72. do {
  73. let nonce = try AES.GCM.Nonce(data: iv)
  74. let sealedBox = try AES.GCM.SealedBox(nonce: nonce, ciphertext: data.dropLast(16), tag: data.suffix(16))
  75. let decryptedData = try AES.GCM.open(sealedBox, using: key)
  76. return decryptedData
  77. } catch {
  78. // print("Decryption error: \(error.localizedDescription)")
  79. return nil
  80. }
  81. }
  82. func encryptFileToServer(data: Data) -> Data? {
  83. do {
  84. if aesKey == nil && !Utils.getSecureFolderEncrypt().isEmpty {
  85. aesKey = try getAESKey()
  86. }
  87. if aesIV == nil && !Utils.getSecureFolderEncryptIv().isEmpty {
  88. aesIV = try getAESIV()
  89. }
  90. } catch {
  91. print("Error retrieving AES key or IV: \(error)")
  92. return nil
  93. }
  94. guard let key = aesKey, let iv = aesIV else {
  95. return nil
  96. }
  97. do {
  98. let nonce = try AES.GCM.Nonce(data: iv)
  99. let sealedBox = try AES.GCM.seal(data, using: key, nonce: nonce)
  100. var encryptedData = sealedBox.ciphertext
  101. encryptedData.append(sealedBox.tag)
  102. return encryptedData
  103. } catch {
  104. // print("Encryption failed: \(error)")
  105. return nil
  106. }
  107. }
  108. private func getAESKey() throws -> SymmetricKey {
  109. let keyData = Data(base64Encoded: Utils.getSecureFolderEncrypt(), options: .ignoreUnknownCharacters)!
  110. return SymmetricKey(data: keyData)
  111. }
  112. private func getAESIV() throws -> Data {
  113. return Data(base64Encoded: Utils.getSecureFolderEncryptIv(), options: .ignoreUnknownCharacters)!
  114. }
  115. func wipeFolderOldSecure() {
  116. let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
  117. let secureDir = documentsDirectory.appendingPathComponent("secure")
  118. if FileManager.default.fileExists(atPath: secureDir.path) {
  119. do {
  120. let fileNames = try FileManager.default.contentsOfDirectory(atPath: secureDir.path)
  121. for fileName in fileNames {
  122. let filePath = secureDir.appendingPathComponent(fileName)
  123. try FileManager.default.removeItem(atPath: filePath.path)
  124. }
  125. try FileManager.default.removeItem(atPath: secureDir.path)
  126. print("Secure folder deleted successfully")
  127. wipeFolderDocument()
  128. } catch {
  129. print("Error deleting secure folder")
  130. }
  131. }
  132. }
  133. func wipeFolderDocument() {
  134. let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
  135. if FileManager.default.fileExists(atPath: documentsDirectory.path) {
  136. do {
  137. let fileNames = try FileManager.default.contentsOfDirectory(atPath: documentsDirectory.path)
  138. for fileName in fileNames {
  139. let filePath = documentsDirectory.appendingPathComponent(fileName)
  140. if fileName == "encrypted_db_es.db" {
  141. continue
  142. }
  143. try FileManager.default.removeItem(atPath: filePath.path)
  144. }
  145. } catch {
  146. print("Error deleting secure folder")
  147. }
  148. }
  149. }
  150. }