NCEndToEndMetadata.swift 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  1. //
  2. // NCEndToEndMetadata.swift
  3. // Nextcloud
  4. //
  5. // Created by Marino Faggiana on 13/11/17.
  6. // Copyright © 2017 Marino Faggiana. All rights reserved.
  7. //
  8. // Author Marino Faggiana <marino.faggiana@nextcloud.com>
  9. //
  10. // This program is free software: you can redistribute it and/or modify
  11. // it under the terms of the GNU General Public License as published by
  12. // the Free Software Foundation, either version 3 of the License, or
  13. // (at your option) any later version.
  14. //
  15. // This program is distributed in the hope that it will be useful,
  16. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. // GNU General Public License for more details.
  19. //
  20. // You should have received a copy of the GNU General Public License
  21. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. //
  23. import UIKit
  24. import NextcloudKit
  25. import Gzip
  26. class NCEndToEndMetadata: NSObject {
  27. struct E2eeV1: Codable {
  28. struct Metadata: Codable {
  29. let metadataKeys: [String: String]
  30. let version: Double
  31. }
  32. struct Encrypted: Codable {
  33. let key: String
  34. let filename: String
  35. let mimetype: String
  36. }
  37. struct Files: Codable {
  38. let initializationVector: String
  39. let authenticationTag: String?
  40. let metadataKey: Int
  41. let encrypted: String
  42. }
  43. let metadata: Metadata
  44. let files: [String: Files]?
  45. }
  46. struct E2eeV12: Codable {
  47. struct Metadata: Codable {
  48. let metadataKey: String
  49. let version: Double
  50. let checksum: String?
  51. }
  52. struct Encrypted: Codable {
  53. let key: String
  54. let filename: String
  55. let mimetype: String
  56. }
  57. struct Files: Codable {
  58. let initializationVector: String
  59. let authenticationTag: String?
  60. let encrypted: String
  61. }
  62. struct Filedrop: Codable {
  63. let initializationVector: String
  64. let authenticationTag: String?
  65. let encrypted: String
  66. let encryptedKey: String?
  67. let encryptedTag: String?
  68. let encryptedInitializationVector: String?
  69. }
  70. let metadata: Metadata
  71. let files: [String: Files]?
  72. let filedrop: [String: Filedrop]?
  73. }
  74. struct E2eeV20: Codable {
  75. struct Metadata: Codable {
  76. let ciphertext: String
  77. let nonce: String
  78. let authenticationTag: String
  79. let counter: Double?
  80. }
  81. struct Users: Codable {
  82. let userId: String
  83. let certificate: String
  84. let encryptedMetadataKey: String?
  85. let encryptedFiledropKey: String?
  86. }
  87. struct Filedrop: Codable {
  88. let ciphertext: String?
  89. let nonce: String?
  90. let authenticationTag: String?
  91. let users: [String: UsersFiledrop]?
  92. struct UsersFiledrop: Codable {
  93. let userId: String?
  94. let encryptedFiledropKey: String?
  95. }
  96. }
  97. let metadata: Metadata
  98. let users: [Users]
  99. let filedrop: [String: Filedrop]?
  100. let version: String
  101. }
  102. struct PlainUsers {
  103. let userId: String
  104. let certificate: String
  105. let metadataKey: String
  106. let filedropKey: String
  107. }
  108. // --------------------------------------------------------------------------------------------
  109. // MARK: Encode JSON Metadata Bridge
  110. // --------------------------------------------------------------------------------------------
  111. func encoderMetadata(_ items: [tableE2eEncryption], account: String, serverUrl: String) -> String? {
  112. let e2EEApiVersion = NCGlobal.shared.capabilityE2EEApiVersion
  113. switch e2EEApiVersion {
  114. case "1.2":
  115. return encoderMetadataV12(items, account: account, serverUrl: serverUrl)
  116. default:
  117. return nil
  118. }
  119. }
  120. func encoderMetadataV12(_ items: [tableE2eEncryption], account: String, serverUrl: String) -> String? {
  121. let encoder = JSONEncoder()
  122. var metadataKey: String = ""
  123. let metadataVersion = 1.2
  124. var files: [String: E2eeV12.Files] = [:]
  125. var filesCodable: [String: E2eeV12.Files]?
  126. var filedrop: [String: E2eeV12.Filedrop] = [:]
  127. var filedropCodable: [String: E2eeV12.Filedrop]?
  128. let privateKey = CCUtility.getEndToEndPrivateKey(account)
  129. var fileNameIdentifiers: [String] = []
  130. // let shortVersion: String = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
  131. // var filesCodable: [String: E2eeV12.Files] = [String: E2eeV12.Files]()
  132. // var filedropCodable: [String: E2eeV12.Filedrop] = [String: E2eeV12.Filedrop]()
  133. //
  134. // metadata
  135. //
  136. if items.isEmpty, let key = NCEndToEndEncryption.sharedManager()?.generateKey() as? NSData {
  137. if let key = key.base64EncodedString().data(using: .utf8)?.base64EncodedString(),
  138. let metadataKeyEncrypted = NCEndToEndEncryption.sharedManager().encryptAsymmetricString(key, publicKey: nil, privateKey: privateKey) {
  139. metadataKey = metadataKeyEncrypted.base64EncodedString()
  140. }
  141. } else if let metadatakey = (items.first!.metadataKey.data(using: .utf8)?.base64EncodedString()),
  142. let metadataKeyEncrypted = NCEndToEndEncryption.sharedManager().encryptAsymmetricString(metadatakey, publicKey: nil, privateKey: privateKey) {
  143. metadataKey = metadataKeyEncrypted.base64EncodedString()
  144. }
  145. for item in items {
  146. //
  147. // files
  148. //
  149. if item.blob == "files" {
  150. let encrypted = E2eeV12.Encrypted(key: item.key, filename: item.fileName, mimetype: item.mimeType)
  151. do {
  152. // Create "encrypted"
  153. let json = try encoder.encode(encrypted)
  154. if let encrypted = NCEndToEndEncryption.sharedManager().encryptPayloadFile(String(data: json, encoding: .utf8), key: item.metadataKey) {
  155. let record = E2eeV12.Files(initializationVector: item.initializationVector, authenticationTag: item.authenticationTag, encrypted: encrypted)
  156. files.updateValue(record, forKey: item.fileNameIdentifier)
  157. }
  158. fileNameIdentifiers.append(item.fileNameIdentifier)
  159. } catch let error {
  160. print("Serious internal error in encoding metadata (" + error.localizedDescription + ")")
  161. return nil
  162. }
  163. }
  164. //
  165. // filedrop
  166. //
  167. if item.blob == "filedrop" {
  168. var encryptedKey: String?
  169. var encryptedInitializationVector: NSString?
  170. var encryptedTag: NSString?
  171. if let metadataKeyFiledrop = (item.metadataKeyFiledrop.data(using: .utf8)?.base64EncodedString()),
  172. let metadataKeyEncrypted = NCEndToEndEncryption.sharedManager().encryptAsymmetricString(metadataKeyFiledrop, publicKey: nil, privateKey: privateKey) {
  173. encryptedKey = metadataKeyEncrypted.base64EncodedString()
  174. }
  175. let encrypted = E2eeV12.Encrypted(key: item.key, filename: item.fileName, mimetype: item.mimeType)
  176. do {
  177. // Create "encrypted"
  178. let json = try encoder.encode(encrypted)
  179. if let encrypted = NCEndToEndEncryption.sharedManager().encryptPayloadFile(String(data: json, encoding: .utf8), key: item.metadataKeyFiledrop, initializationVector: &encryptedInitializationVector, authenticationTag: &encryptedTag) {
  180. let record = E2eeV12.Filedrop(initializationVector: item.initializationVector, authenticationTag: item.authenticationTag, encrypted: encrypted, encryptedKey: encryptedKey, encryptedTag: encryptedTag as? String, encryptedInitializationVector: encryptedInitializationVector as? String)
  181. filedrop.updateValue(record, forKey: item.fileNameIdentifier)
  182. }
  183. } catch let error {
  184. print("Serious internal error in encoding metadata (" + error.localizedDescription + ")")
  185. return nil
  186. }
  187. }
  188. }
  189. // Create checksum
  190. let passphrase = CCUtility.getEndToEndPassphrase(account).replacingOccurrences(of: " ", with: "")
  191. let checksum = NCEndToEndEncryption.sharedManager().createSHA256(passphrase + fileNameIdentifiers.sorted().joined() + metadataKey)
  192. // Create Json
  193. let metadata = E2eeV12.Metadata(metadataKey: metadataKey, version: metadataVersion, checksum: checksum)
  194. if !files.isEmpty { filesCodable = files }
  195. if !filedrop.isEmpty { filedropCodable = filedrop }
  196. let e2ee = E2eeV12(metadata: metadata, files: filesCodable, filedrop: filedropCodable)
  197. do {
  198. let data = try encoder.encode(e2ee)
  199. data.printJson()
  200. let jsonString = String(data: data, encoding: .utf8)
  201. // Updated metadata to 1.2
  202. if NCManageDatabase.shared.getE2eMetadata(account: account, serverUrl: serverUrl) == nil {
  203. NCManageDatabase.shared.setE2eMetadata(account: account, serverUrl: serverUrl, metadataKey: metadataKey, version: metadataVersion)
  204. }
  205. return jsonString
  206. } catch let error {
  207. print("Serious internal error in encoding e2ee (" + error.localizedDescription + ")")
  208. return nil
  209. }
  210. }
  211. // --------------------------------------------------------------------------------------------
  212. // MARK: Decode JSON Metadata Bridge
  213. // --------------------------------------------------------------------------------------------
  214. func decoderMetadata(_ json: String, serverUrl: String, account: String, urlBase: String, userId: String, ownerId: String?) -> NKError {
  215. guard let data = json.data(using: .utf8) else {
  216. return (NKError(errorCode: NCGlobal.shared.errorE2EE, errorDescription: "Error decoding JSON"))
  217. }
  218. data.printJson()
  219. let decoder = JSONDecoder()
  220. if (try? decoder.decode(E2eeV1.self, from: data)) != nil {
  221. return decoderMetadataV1(json, serverUrl: serverUrl, account: account, urlBase: urlBase, userId: userId)
  222. } else if (try? decoder.decode(E2eeV12.self, from: data)) != nil {
  223. return decoderMetadataV12(json, serverUrl: serverUrl, account: account, urlBase: urlBase, userId: userId, ownerId: ownerId)
  224. } else if (try? decoder.decode(E2eeV20.self, from: data)) != nil {
  225. return decoderMetadataV20(json, serverUrl: serverUrl, account: account, urlBase: urlBase, userId: userId, ownerId: ownerId)
  226. } else {
  227. return NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "Server E2EE version " + NCGlobal.shared.capabilityE2EEApiVersion + ", not compatible")
  228. }
  229. }
  230. // --------------------------------------------------------------------------------------------
  231. // MARK: Decode JSON Metadata V2.0
  232. // --------------------------------------------------------------------------------------------
  233. func decoderMetadataV20(_ json: String, serverUrl: String, account: String, urlBase: String, userId: String, ownerId: String?) -> NKError {
  234. guard let data = json.data(using: .utf8) else {
  235. return NKError(errorCode: NCGlobal.shared.errorE2EE, errorDescription: "Error decoding JSON")
  236. }
  237. func addE2eEncryption(fileNameIdentifier: String, filename: String, authenticationTag: String?, key: String, initializationVector: String, metadataKey: String, mimetype: String) {
  238. if let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND fileName == %@", account, fileNameIdentifier)) {
  239. let object = tableE2eEncryption()
  240. object.account = account
  241. object.authenticationTag = authenticationTag ?? ""
  242. object.blob = "files"
  243. object.fileName = filename
  244. object.fileNameIdentifier = fileNameIdentifier
  245. object.fileNamePath = CCUtility.returnFileNamePath(fromFileName: filename, serverUrl: serverUrl, urlBase: urlBase, userId: userId, account: account)
  246. object.key = key
  247. object.initializationVector = initializationVector
  248. object.metadataKey = metadataKey
  249. object.metadataVersion = metadataVersion
  250. object.mimeType = mimetype
  251. object.serverUrl = serverUrl
  252. // Write file parameter for decrypted on DB
  253. NCManageDatabase.shared.addE2eEncryption(object)
  254. // Update metadata on tableMetadata
  255. metadata.fileNameView = filename
  256. let results = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: filename, mimeType: metadata.contentType, directory: metadata.directory)
  257. metadata.contentType = results.mimeType
  258. metadata.iconName = results.iconName
  259. metadata.classFile = results.classFile
  260. NCManageDatabase.shared.addMetadata(metadata)
  261. }
  262. }
  263. let decoder = JSONDecoder()
  264. let privateKey = CCUtility.getEndToEndPrivateKey(account)
  265. var metadataVersion: Double = 0
  266. var plainUsers: [PlainUsers] = []
  267. do {
  268. let json = try decoder.decode(E2eeV20.self, from: data)
  269. let metadata = json.metadata
  270. let users = json.users
  271. let filedrop = json.filedrop
  272. metadataVersion = Double(json.version) ?? 0
  273. // DATA
  274. NCManageDatabase.shared.deleteE2eEncryption(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", account, serverUrl))
  275. //
  276. // users
  277. //
  278. for user in users {
  279. var metadataKey = ""
  280. var filedropKey = ""
  281. if let encryptedMetadataKey = user.encryptedMetadataKey {
  282. let data = Data(base64Encoded: encryptedMetadataKey)
  283. if let decrypted = NCEndToEndEncryption.sharedManager().decryptAsymmetricData(data, privateKey: privateKey) {
  284. metadataKey = decrypted.base64EncodedString()
  285. }
  286. }
  287. if let encryptedFiledropKey = user.encryptedFiledropKey {
  288. let data = Data(base64Encoded: encryptedFiledropKey)
  289. if let decrypted = NCEndToEndEncryption.sharedManager().decryptAsymmetricData(data, privateKey: privateKey) {
  290. filedropKey = decrypted.base64EncodedString()
  291. }
  292. }
  293. plainUsers.append(PlainUsers(userId: user.userId, certificate: user.certificate, metadataKey: metadataKey, filedropKey: filedropKey))
  294. }
  295. //
  296. // metadata
  297. //
  298. if let plainUser = plainUsers.first(where: { $0.userId == userId }) {
  299. if let decrypted = NCEndToEndEncryption.sharedManager().decryptPayloadFile(metadata.ciphertext, key: plainUser.metadataKey, initializationVector: metadata.nonce, authenticationTag: metadata.authenticationTag) {
  300. if decrypted.isGzipped {
  301. do {
  302. let data = try decrypted.gunzipped()
  303. if let jsonText = String(data: data, encoding: .utf8) {
  304. print(jsonText)
  305. }
  306. if let json = try JSONSerialization.jsonObject(with: data) as? [String: AnyObject] {
  307. let keyChecksums = json["keyChecksums"]
  308. let deleted = json["deleted"]
  309. let files = json["files"]
  310. // TEST hash
  311. let hash = NCEndToEndEncryption.sharedManager().createSHA256(plainUser.metadataKey)
  312. if let folders = json["folders"] as? [String: String] {
  313. for folder in folders {
  314. addE2eEncryption(fileNameIdentifier: folder.key, filename: folder.value, authenticationTag: metadata.authenticationTag, key: plainUser.metadataKey, initializationVector: metadata.nonce, metadataKey: plainUser.metadataKey, mimetype: "httpd/unix-directory")
  315. }
  316. }
  317. }
  318. } catch let error {
  319. print("Serious internal error in decoding metadata (" + error.localizedDescription + ")")
  320. }
  321. } else {
  322. print("Serious internal error in decoding metadata")
  323. }
  324. }
  325. }
  326. /* TEST CMS
  327. if let cmsData = NCEndToEndEncryption.sharedManager().generateSignatureCMS(data, certificate: CCUtility.getEndToEndCertificate(account), privateKey: CCUtility.getEndToEndPrivateKey(account), publicKey: CCUtility.getEndToEndPublicKey(account), userId: userId) {
  328. let cms = cmsData.base64EncodedString()
  329. print(cms)
  330. let cmsTest = "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"
  331. let data = Data(base64Encoded: cmsTest)
  332. NCEndToEndEncryption.sharedManager().verifySignatureCMS(data, data: nil, publicKey: CCUtility.getEndToEndPublicKey(account), userId: userId)
  333. }
  334. */
  335. } catch let error {
  336. return NKError(errorCode: NCGlobal.shared.errorE2EE, errorDescription: error.localizedDescription)
  337. }
  338. return NKError()
  339. }
  340. // --------------------------------------------------------------------------------------------
  341. // MARK: Decode JSON Metadata V1.1
  342. // --------------------------------------------------------------------------------------------
  343. func decoderMetadataV1(_ json: String, serverUrl: String, account: String, urlBase: String, userId: String) -> NKError {
  344. guard let data = json.data(using: .utf8) else {
  345. return NKError(errorCode: NCGlobal.shared.errorE2EE, errorDescription: "Error decoding JSON")
  346. }
  347. let decoder = JSONDecoder()
  348. let privateKey = CCUtility.getEndToEndPrivateKey(account)
  349. var metadataVersion: Double = 0
  350. do {
  351. let json = try decoder.decode(E2eeV1.self, from: data)
  352. let metadata = json.metadata
  353. let files = json.files
  354. var metadataKeys: [String: String] = [:]
  355. metadataVersion = metadata.version
  356. // DATA
  357. NCManageDatabase.shared.deleteE2eEncryption(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", account, serverUrl))
  358. //
  359. // metadata
  360. //
  361. for metadataKey in metadata.metadataKeys {
  362. let data = Data(base64Encoded: metadataKey.value)
  363. if let decrypted = NCEndToEndEncryption.sharedManager().decryptAsymmetricData(data, privateKey: privateKey),
  364. let keyData = Data(base64Encoded: decrypted) {
  365. let key = String(data: keyData, encoding: .utf8)
  366. metadataKeys[metadataKey.key] = key
  367. }
  368. }
  369. //
  370. // verify version
  371. //
  372. if let tableE2eMetadata = NCManageDatabase.shared.getE2eMetadata(account: account, serverUrl: serverUrl) {
  373. if tableE2eMetadata.version > metadataVersion {
  374. return NKError(errorCode: NCGlobal.shared.errorE2EE, errorDescription: "Error verify version \(tableE2eMetadata.version)")
  375. }
  376. }
  377. //
  378. // files
  379. //
  380. if let files = files {
  381. for files in files {
  382. let fileNameIdentifier = files.key
  383. let files = files.value as E2eeV1.Files
  384. let encrypted = files.encrypted
  385. let authenticationTag = files.authenticationTag
  386. guard let metadataKey = metadataKeys["\(files.metadataKey)"] else { continue }
  387. let metadataKeyIndex = files.metadataKey
  388. let initializationVector = files.initializationVector
  389. if let decrypted = NCEndToEndEncryption.sharedManager().decryptPayloadFile(encrypted, key: metadataKey),
  390. let decryptedData = Data(base64Encoded: decrypted) {
  391. do {
  392. let encrypted = try decoder.decode(E2eeV1.Encrypted.self, from: decryptedData)
  393. if let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND fileName == %@", account, fileNameIdentifier)) {
  394. let object = tableE2eEncryption()
  395. object.account = account
  396. object.authenticationTag = authenticationTag ?? ""
  397. object.blob = "files"
  398. object.fileName = encrypted.filename
  399. object.fileNameIdentifier = fileNameIdentifier
  400. object.fileNamePath = CCUtility.returnFileNamePath(fromFileName: encrypted.filename, serverUrl: serverUrl, urlBase: urlBase, userId: userId, account: account)
  401. object.key = encrypted.key
  402. object.initializationVector = initializationVector
  403. object.metadataKey = metadataKey
  404. object.metadataKeyIndex = metadataKeyIndex
  405. object.metadataVersion = metadataVersion
  406. object.mimeType = encrypted.mimetype
  407. object.serverUrl = serverUrl
  408. // Write file parameter for decrypted on DB
  409. NCManageDatabase.shared.addE2eEncryption(object)
  410. // Update metadata on tableMetadata
  411. metadata.fileNameView = encrypted.filename
  412. let results = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: encrypted.filename, mimeType: metadata.contentType, directory: metadata.directory)
  413. metadata.contentType = results.mimeType
  414. metadata.iconName = results.iconName
  415. metadata.classFile = results.classFile
  416. NCManageDatabase.shared.addMetadata(metadata)
  417. }
  418. } catch let error {
  419. return NKError(errorCode: NCGlobal.shared.errorE2EE, errorDescription: "Error decrypt file: " + error.localizedDescription)
  420. }
  421. }
  422. }
  423. }
  424. } catch let error {
  425. return NKError(errorCode: NCGlobal.shared.errorE2EE, errorDescription: error.localizedDescription)
  426. }
  427. return NKError()
  428. }
  429. // --------------------------------------------------------------------------------------------
  430. // MARK: Decode JSON Metadata V1.2
  431. // --------------------------------------------------------------------------------------------
  432. func decoderMetadataV12(_ json: String, serverUrl: String, account: String, urlBase: String, userId: String, ownerId: String?) -> NKError {
  433. guard let data = json.data(using: .utf8) else {
  434. return NKError(errorCode: NCGlobal.shared.errorE2EE, errorDescription: "Error decoding JSON")
  435. }
  436. let decoder = JSONDecoder()
  437. let privateKey = CCUtility.getEndToEndPrivateKey(account)
  438. var metadataVersion: Double = 0
  439. var metadataKey = ""
  440. do {
  441. let json = try decoder.decode(E2eeV12.self, from: data)
  442. let metadata = json.metadata
  443. let files = json.files
  444. let filedrop = json.filedrop
  445. var fileNameIdentifiers: [String] = []
  446. metadataVersion = metadata.version
  447. //
  448. // metadata
  449. //
  450. let data = Data(base64Encoded: metadata.metadataKey)
  451. if let decrypted = NCEndToEndEncryption.sharedManager().decryptAsymmetricData(data, privateKey: privateKey),
  452. let keyData = Data(base64Encoded: decrypted),
  453. let key = String(data: keyData, encoding: .utf8) {
  454. metadataKey = key
  455. } else {
  456. return NKError(errorCode: NCGlobal.shared.errorE2EE, errorDescription: "Error decrypt metadataKey")
  457. }
  458. // DATA
  459. NCManageDatabase.shared.deleteE2eEncryption(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", account, serverUrl))
  460. //
  461. // files
  462. //
  463. if let files = files {
  464. for file in files {
  465. let fileNameIdentifier = file.key
  466. let files = file.value as E2eeV12.Files
  467. let encrypted = files.encrypted
  468. let authenticationTag = files.authenticationTag
  469. let initializationVector = files.initializationVector
  470. fileNameIdentifiers.append(fileNameIdentifier)
  471. if let decrypted = NCEndToEndEncryption.sharedManager().decryptPayloadFile(encrypted, key: metadataKey),
  472. let decryptedData = Data(base64Encoded: decrypted) {
  473. do {
  474. decryptedData.printJson()
  475. let encrypted = try decoder.decode(E2eeV12.Encrypted.self, from: decryptedData)
  476. if let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND fileName == %@", account, fileNameIdentifier)) {
  477. let object = tableE2eEncryption()
  478. object.account = account
  479. object.authenticationTag = authenticationTag ?? ""
  480. object.blob = "files"
  481. object.fileName = encrypted.filename
  482. object.fileNameIdentifier = fileNameIdentifier
  483. object.fileNamePath = CCUtility.returnFileNamePath(fromFileName: encrypted.filename, serverUrl: serverUrl, urlBase: urlBase, userId: userId, account: account)
  484. object.key = encrypted.key
  485. object.initializationVector = initializationVector
  486. object.metadataKey = metadataKey
  487. object.metadataVersion = metadataVersion
  488. object.mimeType = encrypted.mimetype
  489. object.serverUrl = serverUrl
  490. // Write file parameter for decrypted on DB
  491. NCManageDatabase.shared.addE2eEncryption(object)
  492. // Update metadata on tableMetadata
  493. metadata.fileNameView = encrypted.filename
  494. let results = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: encrypted.filename, mimeType: metadata.contentType, directory: metadata.directory)
  495. metadata.contentType = results.mimeType
  496. metadata.iconName = results.iconName
  497. metadata.classFile = results.classFile
  498. NCManageDatabase.shared.addMetadata(metadata)
  499. }
  500. } catch let error {
  501. return NKError(errorCode: NCGlobal.shared.errorE2EE, errorDescription: "Error decrypt file: " + error.localizedDescription)
  502. }
  503. }
  504. }
  505. }
  506. //
  507. // filedrop
  508. //
  509. if let filedrop = filedrop {
  510. for filedrop in filedrop {
  511. let fileNameIdentifier = filedrop.key
  512. let filedrop = filedrop.value as E2eeV12.Filedrop
  513. var metadataKeyFiledrop: String?
  514. if let encryptedKey = filedrop.encryptedKey,
  515. let data = Data(base64Encoded: encryptedKey),
  516. let decrypted = NCEndToEndEncryption.sharedManager().decryptAsymmetricData(data, privateKey: privateKey) {
  517. let keyData = Data(base64Encoded: decrypted)
  518. metadataKeyFiledrop = String(data: keyData!, encoding: .utf8)
  519. }
  520. if let decrypted = NCEndToEndEncryption.sharedManager().decryptPayloadFile(filedrop.encrypted, key: metadataKeyFiledrop, initializationVector: filedrop.encryptedInitializationVector, authenticationTag: filedrop.encryptedTag),
  521. let decryptedData = Data(base64Encoded: decrypted) {
  522. do {
  523. decryptedData.printJson()
  524. let encrypted = try decoder.decode(E2eeV1.Encrypted.self, from: decryptedData)
  525. if let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND fileName == %@", account, fileNameIdentifier)) {
  526. let object = tableE2eEncryption()
  527. object.account = account
  528. object.authenticationTag = filedrop.authenticationTag ?? ""
  529. object.blob = "filedrop"
  530. object.fileName = encrypted.filename
  531. object.fileNameIdentifier = fileNameIdentifier
  532. object.fileNamePath = CCUtility.returnFileNamePath(fromFileName: encrypted.filename, serverUrl: serverUrl, urlBase: urlBase, userId: userId, account: account)
  533. object.key = encrypted.key
  534. object.metadataKeyFiledrop = metadataKeyFiledrop ?? ""
  535. object.initializationVector = filedrop.initializationVector
  536. object.metadataKey = metadataKey
  537. object.metadataVersion = metadataVersion
  538. object.mimeType = encrypted.mimetype
  539. object.serverUrl = serverUrl
  540. // Write file parameter for decrypted on DB
  541. NCManageDatabase.shared.addE2eEncryption(object)
  542. // Update metadata on tableMetadata
  543. metadata.fileNameView = encrypted.filename
  544. let results = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: encrypted.filename, mimeType: metadata.contentType, directory: metadata.directory)
  545. metadata.contentType = results.mimeType
  546. metadata.iconName = results.iconName
  547. metadata.classFile = results.classFile
  548. NCManageDatabase.shared.addMetadata(metadata)
  549. }
  550. } catch let error {
  551. return NKError(errorCode: NCGlobal.shared.errorE2EE, errorDescription: "Error decrypt filedrop: " + error.localizedDescription)
  552. }
  553. }
  554. }
  555. }
  556. // verify checksum
  557. let passphrase = CCUtility.getEndToEndPassphrase(account).replacingOccurrences(of: " ", with: "")
  558. let checksum = NCEndToEndEncryption.sharedManager().createSHA256(passphrase + fileNameIdentifiers.sorted().joined() + metadata.metadataKey)
  559. if metadata.checksum != checksum {
  560. return NKError(errorCode: NCGlobal.shared.errorE2EE, errorDescription: "Error checksum")
  561. }
  562. } catch let error {
  563. return NKError(errorCode: NCGlobal.shared.errorE2EE, errorDescription: error.localizedDescription)
  564. }
  565. return NKError()
  566. }
  567. }