Rocket.Chat.ReactNative/ios/Shared/RocketChat/Encryption.swift

104 lines
3.1 KiB
Swift
Raw Normal View History

[NEW] E2E Encryption push (iOS) (#2463) * link pods to notification service * push encryption poc * decrypt room key poc * read user key from mmkv and cast into a pkcs * push decrypt poc (iOS) * expose needed watermelon methods * watermelon -> database * indent & simple-crypto update * string extensions * storage * toBase64 -> toData * remove a forced unwrap * remove unused import * database driver * improvement * folder structure & watermelon bridge * more improvement stuff * watermelon -> database * reuse database instance * improvement * database fix: bypass watermelon cache * some code improvements * encryption instances * start api stuff * network layer * improve notification service * improve folder structure * watermelon patch * retry fetch logic * rocketchat class * fix try to decrypt without a roomKey * fallback to original content that is translated * some fixes to rocketchat logic * merge develop * remove unnecessary extension * [CHORE] Improve reply notification code (iOS) * undo sign changes * remove mocked value * import direct from library * send message request * reply notification with encrypted message working properly * revert apple sign * fix api onerror * trick to display sender name on group notifications * revert data.host change * fix some multithread issues * use sendername sent by server * small improvement * Bump crypto lib * Update ios/NotificationService/NotificationService.swift * add experimental string * remove trailing slash * remove trailing slash on reply * fix decrypt messages Co-authored-by: Diego Mello <diegolmello@gmail.com>
2020-09-24 18:34:13 +00:00
//
// Encryption.swift
// NotificationService
//
// Created by Djorkaeff Alexandre Vilela Pereira on 9/11/20.
// Copyright © 2020 Rocket.Chat. All rights reserved.
//
import Foundation
import CommonCrypto
import class react_native_simple_crypto.RCTRsaUtils
final class Encryption {
final var roomKey: String? = nil
final var keyId: String? = nil
private let privateKey: String?
private let credentials: Credentials?
private let server: String
private let rid: String
private var userKey: String? {
if let userKey = self.privateKey {
guard let json = try? JSONSerialization.jsonObject(with: userKey.data(using: .utf8)!, options: []) as? [String: Any] else {
return nil
}
let utils = RCTRsaUtils()
let k = NSMutableDictionary(dictionary: json)
return utils.importKey(jwk: k)
}
return nil
}
private final let encoder = JSONEncoder()
init(server: String, rid: String) {
self.privateKey = Storage.shared.getPrivateKey(server: server)
self.credentials = Storage.shared.getCredentials(server: server)
self.server = server
self.rid = rid
if let E2EKey = Database(server: server).readRoomEncryptionKey(rid: rid) {
self.roomKey = decryptRoomKey(E2EKey: E2EKey)
}
}
func decryptRoomKey(E2EKey: String) -> String? {
if let userKey = userKey {
let index = E2EKey.index(E2EKey.startIndex, offsetBy: 12)
let roomKey = String(E2EKey[index...])
keyId = String(E2EKey[..<index])
let rsa = Rsa()
rsa.privateKey = userKey
let message = rsa.decrypt(roomKey)
if let message = message?.data(using: .utf8) {
if let key = try? (JSONDecoder().decode(RoomKey.self, from: message)) {
if let base64Encoded = key.k.toData() {
return Shared.toHex(base64Encoded)
}
}
}
}
return nil
}
func decryptMessage(message: String) -> String? {
if let roomKey = self.roomKey {
let index = message.index(message.startIndex, offsetBy: 12)
let msg = String(message[index...])
if let data = msg.toData() {
let iv = data.subdata(in: 0..<kCCBlockSizeAES128)
let cypher = data.subdata(in: kCCBlockSizeAES128..<data.count)
if let decrypted = Aes.aes128CBC("decrypt", data: cypher, key: roomKey, iv: Shared.toHex(iv)) {
if let m = try? (JSONDecoder().decode(Message.self, from: decrypted)) {
return m.text
}
}
}
}
return nil
}
func encryptMessage(id: String, message: String) -> String {
if let userId = credentials?.userId, let roomKey = roomKey {
let m = Message(_id: id, text: message, userId: userId)
let iv = Data.randomBytes(length: kCCBlockSizeAES128)
let cypher = try? encoder.encode(m)
if let keyId = keyId, let cypher = cypher, let data = Aes.aes128CBC("encrypt", data: cypher, key: roomKey, iv: Shared.toHex(iv)) {
let joined = Data.join(vector: iv, data: data)
return keyId + joined.base64EncodedString()
}
}
return message
}
}