Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
11e2bd6
link pods to notification service
djorkaeffalexandre Sep 11, 2020
a635329
push encryption poc
djorkaeffalexandre Sep 14, 2020
3d2da35
decrypt room key poc
djorkaeffalexandre Sep 14, 2020
a13fc75
read user key from mmkv and cast into a pkcs
djorkaeffalexandre Sep 14, 2020
c57f075
push decrypt poc (iOS)
djorkaeffalexandre Sep 14, 2020
91880d4
expose needed watermelon methods
djorkaeffalexandre Sep 14, 2020
6e3b04f
watermelon -> database
djorkaeffalexandre Sep 14, 2020
9bf5ec6
indent & simple-crypto update
djorkaeffalexandre Sep 14, 2020
bb056bb
string extensions
djorkaeffalexandre Sep 14, 2020
26396c6
storage
djorkaeffalexandre Sep 15, 2020
a8bf330
toBase64 -> toData
djorkaeffalexandre Sep 15, 2020
fc0d5f3
remove a forced unwrap
djorkaeffalexandre Sep 15, 2020
97ffcec
remove unused import
djorkaeffalexandre Sep 15, 2020
7c95551
database driver
djorkaeffalexandre Sep 15, 2020
ebd398c
Merge branch 'develop' of https://github.com/RocketChat/Rocket.Chat.R…
djorkaeffalexandre Sep 15, 2020
f87fee6
improvement
djorkaeffalexandre Sep 15, 2020
ea23f57
folder structure & watermelon bridge
djorkaeffalexandre Sep 15, 2020
31969c1
more improvement stuff
djorkaeffalexandre Sep 15, 2020
09da5a2
watermelon -> database
djorkaeffalexandre Sep 15, 2020
80ea1dd
reuse database instance
djorkaeffalexandre Sep 15, 2020
9cfd69f
improvement
djorkaeffalexandre Sep 16, 2020
87902d0
database fix: bypass watermelon cache
djorkaeffalexandre Sep 16, 2020
71d5097
some code improvements
djorkaeffalexandre Sep 16, 2020
847a32a
encryption instances
djorkaeffalexandre Sep 16, 2020
c2575a3
start api stuff
djorkaeffalexandre Sep 16, 2020
ce4dd5d
network layer
djorkaeffalexandre Sep 16, 2020
e720cd0
improve notification service
djorkaeffalexandre Sep 16, 2020
25ff9ed
improve folder structure
djorkaeffalexandre Sep 16, 2020
2f7cc95
Merge branch 'develop' of https://github.com/RocketChat/Rocket.Chat.R…
djorkaeffalexandre Sep 16, 2020
cb6458d
watermelon patch
djorkaeffalexandre Sep 17, 2020
ff88451
retry fetch logic
djorkaeffalexandre Sep 17, 2020
bff9b7d
rocketchat class
djorkaeffalexandre Sep 17, 2020
1d1cc0c
fix try to decrypt without a roomKey
djorkaeffalexandre Sep 17, 2020
7bb61a7
fallback to original content that is translated
djorkaeffalexandre Sep 17, 2020
4bb0dbd
some fixes to rocketchat logic
djorkaeffalexandre Sep 17, 2020
dc924da
merge develop
djorkaeffalexandre Sep 17, 2020
0d9c8c6
remove unnecessary extension
djorkaeffalexandre Sep 17, 2020
4356679
merge develop
djorkaeffalexandre Sep 17, 2020
2dfc471
[CHORE] Improve reply notification code (iOS)
djorkaeffalexandre Sep 18, 2020
b39618a
undo sign changes
djorkaeffalexandre Sep 18, 2020
303d7ca
remove mocked value
djorkaeffalexandre Sep 18, 2020
e4000c4
import direct from library
djorkaeffalexandre Sep 18, 2020
a90081b
send message request
djorkaeffalexandre Sep 18, 2020
d07ea84
Merge branch 'chore.reply-notification' into new.e2e-encryption-push
djorkaeffalexandre Sep 18, 2020
c3d2b56
reply notification with encrypted message working properly
djorkaeffalexandre Sep 18, 2020
a044793
revert apple sign
djorkaeffalexandre Sep 18, 2020
9bb9252
fix api onerror
djorkaeffalexandre Sep 21, 2020
e7ad091
trick to display sender name on group notifications
djorkaeffalexandre Sep 21, 2020
a058f57
revert data.host change
djorkaeffalexandre Sep 21, 2020
bdd7591
fix some multithread issues
djorkaeffalexandre Sep 21, 2020
3b8f8dc
use sendername sent by server
djorkaeffalexandre Sep 22, 2020
79150ff
small improvement
djorkaeffalexandre Sep 22, 2020
9fad837
Merge branch 'develop' into new.e2e-encryption-push
diegolmello Sep 23, 2020
5bc64e0
Bump crypto lib
diegolmello Sep 23, 2020
cd878de
Update ios/NotificationService/NotificationService.swift
diegolmello Sep 23, 2020
dfe50d5
add experimental string
djorkaeffalexandre Sep 23, 2020
8342256
remove trailing slash
djorkaeffalexandre Sep 23, 2020
8dc40c9
invalidate ci & merge
djorkaeffalexandre Sep 23, 2020
9598092
remove trailing slash on reply
djorkaeffalexandre Sep 24, 2020
e111618
merge develop
djorkaeffalexandre Sep 24, 2020
a6f620e
Merge branch 'develop' into new.e2e-encryption-push
diegolmello Sep 24, 2020
4a1fac6
Merge branch 'develop' of https://github.com/RocketChat/Rocket.Chat.R…
djorkaeffalexandre Sep 24, 2020
074cd34
Merge branch 'new.e2e-encryption-push' of https://github.com/RocketCh…
djorkaeffalexandre Sep 24, 2020
b6116b1
fix decrypt messages
djorkaeffalexandre Sep 24, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions app/lib/encryption/encryption.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ class Encryption {
}

// Initialize Encryption client
initialize = () => {
initialize = (userId) => {
this.userId = userId;
this.roomInstances = {};

// Don't await these promises
Expand All @@ -69,6 +70,7 @@ class Encryption {

// Stop Encryption client
stop = () => {
this.userId = null;
this.privateKey = null;
this.roomInstances = {};
// Cancel ongoing encryption/decryption requests
Expand Down Expand Up @@ -199,7 +201,7 @@ class Encryption {

// If doesn't have a instance of this room
if (!this.roomInstances[rid]) {
this.roomInstances[rid] = new EncryptionRoom(rid);
this.roomInstances[rid] = new EncryptionRoom(rid, this.userId);
}

const roomE2E = this.roomInstances[rid];
Expand Down
3 changes: 2 additions & 1 deletion app/lib/encryption/room.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ import database from '../database';
import log from '../../utils/log';

export default class EncryptionRoom {
constructor(roomId) {
constructor(roomId, userId) {
this.ready = false;
this.roomId = roomId;
this.userId = userId;
this.establishing = false;
this.readyPromise = new Deferred();
this.readyPromise.then(() => {
Expand Down
4 changes: 2 additions & 2 deletions app/sagas/encryption.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ const handleEncryptionInit = function* handleEncryptionInit() {
}

// Decrypt all pending messages/subscriptions
Encryption.initialize();
Encryption.initialize(user.id);
} catch (e) {
log(e);
}
Expand Down Expand Up @@ -109,7 +109,7 @@ const handleEncryptionDecodeKey = function* handleEncryptionDecodeKey({ password
yield Encryption.persistKeys(server, publicKey, privateKey);

// Decrypt all pending messages/subscriptions
Encryption.initialize();
Encryption.initialize(user.id);

// Hide encryption banner
yield put(encryptionSetBanner());
Expand Down
7 changes: 5 additions & 2 deletions ios/NotificationService/NotificationService-Bridging-Header.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@
// Use this file to import your target's public headers that you would like to expose to Swift.
//

#import "SecureStorage.h"
#import <MMKVAppExtension/MMKV.h>
#import <MMKV/MMKV.h>
#import <react-native-mmkv-storage/SecureStorage.h>
#import <react-native-simple-crypto/Aes.h>
#import <react-native-simple-crypto/Rsa.h>
#import <react-native-simple-crypto/Shared.h>
206 changes: 42 additions & 164 deletions ios/NotificationService/NotificationService.swift
Original file line number Diff line number Diff line change
@@ -1,177 +1,55 @@
import CoreLocation
import UserNotifications

struct PushResponse: Decodable {
let success: Bool
let data: Data

struct Data: Decodable {
let notification: Notification

struct Notification: Decodable {
let notId: Int
let title: String
let text: String
let payload: Payload

struct Payload: Decodable, Encodable {
let host: String
let rid: String?
let type: String?
let sender: Sender?
let messageId: String
let notificationType: String?
let name: String?
let messageType: String?

struct Sender: Decodable, Encodable {
let _id: String
let username: String
let name: String?
}
}
}
}
}

class NotificationService: UNNotificationServiceExtension {

var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?

var retryCount = 0
var retryTimeout = [1.0, 3.0, 5.0, 10.0]

override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

if let bestAttemptContent = bestAttemptContent {
let ejson = (bestAttemptContent.userInfo["ejson"] as? String ?? "").data(using: .utf8)!
guard let data = try? (JSONDecoder().decode(PushResponse.Data.Notification.Payload.self, from: ejson)) else {
return
}

let notificationType = data.notificationType ?? ""

// If the notification have the content at her payload, show it
if notificationType != "message-id-only" {
contentHandler(bestAttemptContent)
return
}

let mmapID = "default"
let instanceID = "com.MMKV.\(mmapID)"
let secureStorage = SecureStorage()
var cryptKey: Data = Data()
// get mmkv instance password from keychain
secureStorage.getSecureKey(instanceID.toHex()) { (response) -> () in
guard let password = response?[1] as? String else {
// kill the process and show notification as it came from APN
exit(0)
}
cryptKey = password.data(using: .utf8)!
}

// Get App Group directory
let suiteName = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as! String
guard let directory = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: suiteName) else {
return
}

// Set App Group dir
MMKV.initialize(rootDir: nil, groupDir: directory.path, logLevel: MMKVLogLevel.none)
guard let mmkv = MMKV(mmapID: mmapID, cryptKey: cryptKey, mode: MMKVMode.multiProcess) else {
return
}

var server = data.host
if (server.last == "/") {
server.removeLast()
}
let msgId = data.messageId

let userId = mmkv.string(forKey: "reactnativemeteor_usertoken-\(server)") ?? ""
let token = mmkv.string(forKey: "reactnativemeteor_usertoken-\(userId)") ?? ""

if userId.isEmpty || token.isEmpty {
contentHandler(bestAttemptContent)
return
}

var urlComponents = URLComponents(string: "\(server)/api/v1/push.get")!
let queryItems = [URLQueryItem(name: "id", value: msgId)]
urlComponents.queryItems = queryItems

var request = URLRequest(url: urlComponents.url!)
request.httpMethod = "GET"
request.addValue(userId, forHTTPHeaderField: "x-user-id")
request.addValue(token, forHTTPHeaderField: "x-auth-token")

runRequest(request: request, bestAttemptContent: bestAttemptContent, contentHandler: contentHandler)

var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
var rocketchat: RocketChat?

override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

if let bestAttemptContent = bestAttemptContent {
let ejson = (bestAttemptContent.userInfo["ejson"] as? String ?? "").data(using: .utf8)!
guard let data = try? (JSONDecoder().decode(Payload.self, from: ejson)) else {
return
}

rocketchat = RocketChat.instanceForServer(server: data.host.removeTrailingSlash())

// If the notification has the content on the payload, show it
if data.notificationType != .messageIdOnly {
self.processPayload(payload: data)
return
}

// Request the content from server
rocketchat?.getPushWithId(data.messageId) { notification in
if let notification = notification {
self.bestAttemptContent?.title = notification.title
self.bestAttemptContent?.body = notification.text
self.processPayload(payload: notification.payload)
}
}
}
}

func runRequest(request: URLRequest, bestAttemptContent: UNMutableNotificationContent, contentHandler: @escaping (UNNotificationContent) -> Void) {
let task = URLSession.shared.dataTask(with: request) {(data, response, error) in

func retryRequest() {
// if we can try again
if self.retryCount < self.retryTimeout.count {
// Try again after X seconds
DispatchQueue.main.asyncAfter(deadline: .now() + self.retryTimeout[self.retryCount], execute: {
self.runRequest(request: request, bestAttemptContent: bestAttemptContent, contentHandler: contentHandler)
self.retryCount += 1
})
func processPayload(payload: Payload) {
// If is a encrypted message
if payload.messageType == .e2e {
if let message = payload.msg, let rid = payload.rid {
if let decryptedMessage = rocketchat?.decryptMessage(rid: rid, message: message) {
bestAttemptContent?.body = decryptedMessage
if let roomType = payload.type, roomType == .group, let sender = payload.senderName {
bestAttemptContent?.body = "\(sender): \(decryptedMessage)"
}
}

// If some error happened
if error != nil {
retryRequest()

// Check if the request did successfully
} else if let response = response as? HTTPURLResponse {
// if it not was successfully
if response.statusCode != 200 {
retryRequest()

// If the response status is 200
} else {
// Process data
if let data = data {
// Parse data of response
let push = try? (JSONDecoder().decode(PushResponse.self, from: data))
if let push = push {
if push.success {
bestAttemptContent.title = push.data.notification.title
bestAttemptContent.body = push.data.notification.text

let payload = try? (JSONEncoder().encode(push.data.notification.payload))
if let payload = payload {
bestAttemptContent.userInfo["ejson"] = String(data: payload, encoding: .utf8) ?? "{}"
}

// Show notification with the content modified
contentHandler(bestAttemptContent)
return
}
}
}
retryRequest()
}
}
}

task.resume()
}

override func serviceExtensionTimeWillExpire() {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
if let bestAttemptContent = bestAttemptContent {
contentHandler?(bestAttemptContent)
}

}
}
48 changes: 0 additions & 48 deletions ios/NotificationService/SecureStorage.h

This file was deleted.

Loading