diff --git a/ConversationsClassic/AppData/Model/Message+OMEMO.swift b/ConversationsClassic/AppData/Model/Message+OMEMO.swift index 538d3e7..b4e58c0 100644 --- a/ConversationsClassic/AppData/Model/Message+OMEMO.swift +++ b/ConversationsClassic/AppData/Model/Message+OMEMO.swift @@ -38,7 +38,14 @@ extension Message { switch decodingResult { case .successMessage(let decodedMessage, _): martinMessage = decodedMessage - // print(decodedMessage, fingerprint) + if let oob = martinMessage.oob { + contentType = .attachment(.init( + type: oob.attachmentType, + localName: nil, + thumbnailName: nil, + remotePath: oob + )) + } case .successTransportKey: break diff --git a/ConversationsClassic/AppData/Services/AESGSMEngine.swift b/ConversationsClassic/AppData/Services/AESGSMEngine.swift index 177a389..7bba045 100644 --- a/ConversationsClassic/AppData/Services/AESGSMEngine.swift +++ b/ConversationsClassic/AppData/Services/AESGSMEngine.swift @@ -28,13 +28,27 @@ final class AESGSMEngine: AES_GCM_Engine { func decrypt(iv: Data, key: Data, encoded: Data, auth tag: Data?, output: UnsafeMutablePointer?) -> Bool { do { let symmetricKey = SymmetricKey(data: key) - guard let tag = tag else { - print("Tag is missing") - return false - } - let sealedBox = try AES.GCM.SealedBox(nonce: AES.GCM.Nonce(data: iv), ciphertext: encoded, tag: tag) - let decryptedData = try AES.GCM.open(sealedBox, using: symmetricKey) + let sealedBox: AES.GCM.SealedBox + if let tag { + sealedBox = try AES.GCM.SealedBox(nonce: AES.GCM.Nonce(data: iv), ciphertext: encoded, tag: tag) + } else { + let embeddedTag = encoded.subdata(in: (encoded.count - 16) ..< encoded.count) + let payload = encoded.subdata(in: 0 ..< (encoded.count - 16)) + sealedBox = try AES.GCM.SealedBox(nonce: AES.GCM.Nonce(data: iv), ciphertext: payload, tag: embeddedTag) + } + + let decryptedData = try AES.GCM.open(sealedBox, using: symmetricKey) + // var payload = encoded + // + // var tag = tag + // if tag == nil { + // tag = payload.subdata(in: (payload.count - 16) ..< payload.count) + // encoded = payload.subdata(in: 0 ..< (payload.count - 16)) + // } + // let sealedBox = try AES.GCM.SealedBox(nonce: AES.GCM.Nonce(data: iv), ciphertext: encoded, tag: tag) + // let decryptedData = try AES.GCM.open(sealedBox, using: symmetricKey) + // if let output = output { output.pointee = decryptedData } diff --git a/ConversationsClassic/AppData/Store/AttachmentsStore.swift b/ConversationsClassic/AppData/Store/AttachmentsStore.swift index c9f1f11..20233c3 100644 --- a/ConversationsClassic/AppData/Store/AttachmentsStore.swift +++ b/ConversationsClassic/AppData/Store/AttachmentsStore.swift @@ -278,10 +278,23 @@ extension AttachmentsStore { guard case .attachment(let attachment) = message.contentType else { return } - guard let remotePath = attachment.remotePath, let remoteUrl = URL(string: remotePath) else { + guard let remotePath = attachment.remotePath, var remoteUrl = URL(string: remotePath) else { return } do { + // if attachment encrypted, extract the key + // and format remote url + var encryptionKey: String? + if remoteUrl.scheme == "aesgcm", var components = URLComponents(url: remoteUrl, resolvingAgainstBaseURL: true) { + encryptionKey = components.fragment + components.scheme = "https" + components.fragment = nil + if let tmpUrl = components.url { + remoteUrl = tmpUrl + } + } + + // make local name/path let localName = "\(message.id)_\(UUID().uuidString).\(remoteUrl.lastPathComponent)" let localUrl = FolderWrapper.shared.fileFolder.appendingPathComponent(localName) @@ -289,6 +302,35 @@ extension AttachmentsStore { let (tempUrl, _) = try await URLSession.shared.download(from: remoteUrl) try FileManager.default.moveItem(at: tempUrl, to: localUrl) + if let encryptionKey { + // Decrypt the file + guard encryptionKey.count % 2 == 0, encryptionKey.count > 64 else { + throw AppError.securityError + } + let fragmentData = encryptionKey.map { char -> UInt8 in + return UInt8(char.hexDigitValue ?? 0) + } + let ivLen = fragmentData.count - (32 * 2) + var iv = Data() + var key = Data() + + for index in 0 ..< (ivLen / 2) { + iv.append(fragmentData[index * 2] * 16 + fragmentData[index * 2 + 1]) + } + for index in (ivLen / 2) ..< (fragmentData.count / 2) { + key.append(fragmentData[index * 2] * 16 + fragmentData[index * 2 + 1]) + } + + let encodedData = try Data(contentsOf: localUrl) + var result = Data() + + guard AESGSMEngine.shared.decrypt(iv: iv, key: key, encoded: encodedData, auth: nil, output: &result) else { + throw AppError.securityError + } + + try result.write(to: localUrl) + } + var message = message message.contentType = .attachment( Attachment(