// // NCEndToEndMetadata.swift // Nextcloud // // Created by Marino Faggiana on 13/11/17. // Copyright © 2017 Marino Faggiana. All rights reserved. // // Author Marino Faggiana // // 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 NextcloudKit class NCEndToEndMetadata: NSObject { struct E2ee: Codable { struct MetadataKeyCodable: Codable { let metadataKeys: [String: String] let version: Int } struct SharingCodable: Codable { let recipient: [String: String] } struct Encrypted: Codable { let key: String let filename: String let mimetype: String let version: Int } struct FilesCodable: Codable { let initializationVector: String let authenticationTag: String? let metadataKey: Int // Number of metadataKey let encrypted: String // encryptedFileAttributes } let metadata: MetadataKeyCodable let files: [String: FilesCodable]? let filedrop: [String: FilesCodable]? let sharing: SharingCodable? } // -------------------------------------------------------------------------------------------- // MARK: Encode / Decode JSON Metadata // -------------------------------------------------------------------------------------------- func encoderMetadata(_ recordsE2eEncryption: [tableE2eEncryption], privateKey: String, serverUrl: String) -> String? { let jsonEncoder = JSONEncoder() var files: [String: E2ee.FilesCodable] = [:] var version = 1 var metadataKeysDictionary: [String: String] = [:] for recordE2eEncryption in recordsE2eEncryption { // *** metadataKey *** // Encode64 for Android compatibility let metadatakey = (recordE2eEncryption.metadataKey.data(using: .utf8)?.base64EncodedString())! guard let metadataKeyEncryptedData = NCEndToEndEncryption.sharedManager().encryptAsymmetricString(metadatakey, publicKey: nil, privateKey: privateKey) else { return nil } let metadataKeyEncryptedBase64 = metadataKeyEncryptedData.base64EncodedString() metadataKeysDictionary["\(recordE2eEncryption.metadataKeyIndex)"] = metadataKeyEncryptedBase64 // *** File *** let encrypted = E2ee.Encrypted(key: recordE2eEncryption.key, filename: recordE2eEncryption.fileName, mimetype: recordE2eEncryption.mimeType, version: recordE2eEncryption.version) do { // Create "encrypted" let encryptedJsonData = try jsonEncoder.encode(encrypted) let encryptedJsonString = String(data: encryptedJsonData, encoding: .utf8) guard let encryptedEncryptedJson = NCEndToEndEncryption.sharedManager().encryptEncryptedJson(encryptedJsonString, key: recordE2eEncryption.metadataKey) else { print("Serious internal error in encoding metadata") return nil } let e2eMetadataFilesKey = E2ee.FilesCodable(initializationVector: recordE2eEncryption.initializationVector, authenticationTag: recordE2eEncryption.authenticationTag, metadataKey: 0, encrypted: encryptedEncryptedJson) files.updateValue(e2eMetadataFilesKey, forKey: recordE2eEncryption.fileNameIdentifier) } catch let error { print("Serious internal error in encoding metadata (" + error.localizedDescription + ")") return nil } version = recordE2eEncryption.version } // Create Json metadataKeys // e2eMetadataKey = e2eMetadata.metadataKeyCodable(metadataKeys: ["0":metadataKeyEncryptedBase64], version: version) let e2eMetadataKey = E2ee.MetadataKeyCodable(metadataKeys: metadataKeysDictionary, version: version) // Create final Json e2emetadata let e2emetadata = E2ee(metadata: e2eMetadataKey, files: files, filedrop: nil, sharing: nil) do { let jsonData = try jsonEncoder.encode(e2emetadata) let jsonString = String(data: jsonData, encoding: .utf8) print("JSON String : " + jsonString!) return jsonString } catch let error { print("Serious internal error in encoding metadata (" + error.localizedDescription + ")") return nil } } func decoderMetadata(_ e2eMetaDataJSON: String, privateKey: String, serverUrl: String, account: String, urlBase: String, userId: String) -> Bool { guard let data = e2eMetaDataJSON.data(using: .utf8) else { return false } let jsonDecoder = JSONDecoder() // let dataQuickLook = (data as! NSData) do { let decode = try jsonDecoder.decode(E2ee.self, from: data) let metadata = decode.metadata let sharing = decode.sharing let files = decode.files let filedrop = decode.filedrop var metadataKeys: [String: String] = [:] // metadata for metadataKey in metadata.metadataKeys { if let metadataKeyData: NSData = NSData(base64Encoded: metadataKey.value, options: NSData.Base64DecodingOptions(rawValue: 0)), let metadataKeyBase64 = NCEndToEndEncryption.sharedManager().decryptAsymmetricData(metadataKeyData as Data?, privateKey: privateKey), let metadataKeyBase64Data = Data(base64Encoded: metadataKeyBase64, options: NSData.Base64DecodingOptions(rawValue: 0)), let key = String(data: metadataKeyBase64Data, encoding: .utf8) { metadataKeys[metadataKey.key] = key } } // sharing if let sharing = sharing { } // files if let files = files { for file in files { let fileNameIdentifier = file.key let filesCodable = file.value as E2ee.FilesCodable let encrypted = filesCodable.encrypted let metadataKey = metadataKeys["\(filesCodable.metadataKey)"] if let encrypted = NCEndToEndEncryption.sharedManager().decryptEncryptedJson(encrypted, key: metadataKey), let encryptedData = encrypted.data(using: .utf8) { do { let encrypted = try jsonDecoder.decode(E2ee.Encrypted.self, from: encryptedData) if let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND fileName == %@", account, fileNameIdentifier)) { let metadata = tableMetadata.init(value: metadata) let object = tableE2eEncryption() object.account = account object.authenticationTag = filesCodable.authenticationTag ?? "" object.blob = "files" object.fileName = encrypted.filename object.fileNameIdentifier = fileNameIdentifier object.fileNamePath = CCUtility.returnFileNamePath(fromFileName: encrypted.filename, serverUrl: serverUrl, urlBase: urlBase, userId: userId, account: account) object.key = encrypted.key object.initializationVector = filesCodable.initializationVector object.metadataKey = metadataKey! object.metadataKeyIndex = filesCodable.metadataKey object.metadataVersion = 1 object.mimeType = encrypted.mimetype object.serverUrl = serverUrl object.version = encrypted.version // If exists remove records NCManageDatabase.shared.deleteE2eEncryption(predicate: NSPredicate(format: "account == %@ AND fileNamePath == %@", object.account, object.fileNamePath)) NCManageDatabase.shared.deleteE2eEncryption(predicate: NSPredicate(format: "account == %@ AND fileNameIdentifier == %@", object.account, object.fileNameIdentifier)) // Write file parameter for decrypted on DB NCManageDatabase.shared.addE2eEncryption(object) // Update metadata on tableMetadata metadata.fileNameView = encrypted.filename let results = NKCommon.shared.getInternalType(fileName: encrypted.filename, mimeType: metadata.contentType, directory: metadata.directory) metadata.contentType = results.mimeType metadata.iconName = results.iconName metadata.classFile = results.classFile NCManageDatabase.shared.addMetadata(metadata) } } catch let error { print("Serious internal error in decoding metadata (" + error.localizedDescription + ")") return false } } } } // filedrop if let filedrop = filedrop { } } catch let error { print("Serious internal error in decoding metadata (" + error.localizedDescription + ")") return false } return true } }