diff --git a/native/android/app/CMakeLists.txt b/native/android/app/CMakeLists.txt --- a/native/android/app/CMakeLists.txt +++ b/native/android/app/CMakeLists.txt @@ -119,6 +119,7 @@ "./src/cpp/AESCrypto.cpp" "./src/cpp/CommServicesAuthMetadataEmitter.cpp" "./src/cpp/CommMMKV.cpp" + "./src/cpp/NotificationsInboundKeysProvider.cpp" ) list(APPEND GENERATED_NATIVE_CODE diff --git a/native/android/app/src/cpp/NotificationsInboundKeysProvider.cpp b/native/android/app/src/cpp/NotificationsInboundKeysProvider.cpp new file mode 100644 --- /dev/null +++ b/native/android/app/src/cpp/NotificationsInboundKeysProvider.cpp @@ -0,0 +1,32 @@ +#include "jniHelpers.h" +#include +#include + +using namespace facebook::jni; + +class NotificationsInboundKeysProviderJavaClass + : public JavaClass { +public: + static auto constexpr kJavaDescriptor = + "Lapp/comm/android/fbjni/NotificationsInboundKeysProvider;"; + + static std::string getNotifsInboundKeysForDeviceID(std::string deviceID) { + static const auto cls = javaClassStatic(); + static auto method = cls->getStaticMethod( + "getNotifsInboundKeysForDeviceID"); + const auto result = method(cls, deviceID); + return result->toStdString(); + } +}; + +namespace comm { +std::string NotificationsInboundKeysProvider::getNotifsInboundKeysForDeviceID( + const std::string &deviceID) { + std::string result; + NativeAndroidAccessProvider::runTask([&]() { + result = NotificationsInboundKeysProviderJavaClass:: + getNotifsInboundKeysForDeviceID(deviceID); + }); + return result; +} +} // namespace comm diff --git a/native/android/app/src/main/java/app/comm/android/commservices/CommAndroidServicesClient.java b/native/android/app/src/main/java/app/comm/android/commservices/CommAndroidServicesClient.java --- a/native/android/app/src/main/java/app/comm/android/commservices/CommAndroidServicesClient.java +++ b/native/android/app/src/main/java/app/comm/android/commservices/CommAndroidServicesClient.java @@ -34,6 +34,9 @@ .callTimeout(NOTIF_PROCESSING_TIME_LIMIT_SECONDS, TimeUnit.SECONDS) .build(); + public static final CommAndroidServicesClient instance = + new CommAndroidServicesClient(); + public static final String BLOB_SERVICE_URL = BuildConfig.DEBUG ? "https://blob.staging.commtechnologies.org" : "https://blob.commtechnologies.org"; @@ -43,6 +46,10 @@ public static final String BLOB_HASH_KEY = "blob_hash"; public static final String BLOB_HOLDER_KEY = "holder"; + public static CommAndroidServicesClient getInstance() { + return CommAndroidServicesClient.instance; + } + public byte[] getBlobSync(String blobHash) throws IOException, JSONException { String authToken = getAuthToken(); Request request = new Request.Builder() diff --git a/native/android/app/src/main/java/app/comm/android/fbjni/NotificationsInboundKeysProvider.java b/native/android/app/src/main/java/app/comm/android/fbjni/NotificationsInboundKeysProvider.java new file mode 100644 --- /dev/null +++ b/native/android/app/src/main/java/app/comm/android/fbjni/NotificationsInboundKeysProvider.java @@ -0,0 +1,24 @@ +package app.comm.android.fbjni; + +import app.comm.android.commservices.CommAndroidServicesClient; +import java.io.IOException; +import org.json.JSONException; +import org.json.JSONObject; + +public class NotificationsInboundKeysProvider { + public static String getNotifsInboundKeysForDeviceID(String deviceID) + throws IOException, JSONException { + JSONObject notifInboundKeys = + CommAndroidServicesClient.getInstance() + .getNotifsInboundKeysForDeviceSync(deviceID); + String curve25519 = notifInboundKeys.getString("curve25519"); + // There are several reason to return JSON with curve25519 only: + // 1. We only need curve25519 to create inbound session. + // 2. In Session.cpp there is a convention to pass curve25519 + // key as JSON and then add offset length to advance + // the string pointer. + // 3. There is a risk that stringification might not preserve + // the order. + return new JSONObject().put("curve25519", curve25519).toString(); + } +} diff --git a/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java b/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java --- a/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java +++ b/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java @@ -69,7 +69,6 @@ private Bitmap displayableNotificationLargeIcon; private NotificationManager notificationManager; private LocalBroadcastManager localBroadcastManager; - private CommAndroidServicesClient servicesClient; private AESCryptoModuleCompat aesCryptoModule; public static final String RESCIND_KEY = "rescind"; @@ -93,7 +92,6 @@ localBroadcastManager = LocalBroadcastManager.getInstance(this); displayableNotificationLargeIcon = BitmapFactory.decodeResource( this.getApplicationContext().getResources(), R.mipmap.ic_launcher); - servicesClient = new CommAndroidServicesClient(); aesCryptoModule = new AESCryptoModuleCompat(); } @@ -337,13 +335,14 @@ String blobHash = message.getData().get(BLOB_HASH_KEY); String blobHolder = message.getData().get(BLOB_HOLDER_KEY); try { - byte[] largePayload = servicesClient.getBlobSync(blobHash); + byte[] largePayload = + CommAndroidServicesClient.getInstance().getBlobSync(blobHash); message = aesDecryptRemoteMessage(message, largePayload); handleMessageInfosPersistence(message); } catch (Exception e) { Log.w("COMM", "Failure when handling large notification.", e); } - servicesClient.scheduleDeferredBlobDeletion( + CommAndroidServicesClient.getInstance().scheduleDeferredBlobDeletion( blobHash, blobHolder, this.getApplicationContext()); } diff --git a/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsInboundKeysProvider.h b/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsInboundKeysProvider.h new file mode 100644 --- /dev/null +++ b/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsInboundKeysProvider.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace comm { +class NotificationsInboundKeysProvider { +public: + static std::string + getNotifsInboundKeysForDeviceID(const std::string &deviceID); +}; +} // namespace comm diff --git a/native/ios/Comm.xcodeproj/project.pbxproj b/native/ios/Comm.xcodeproj/project.pbxproj --- a/native/ios/Comm.xcodeproj/project.pbxproj +++ b/native/ios/Comm.xcodeproj/project.pbxproj @@ -90,6 +90,8 @@ CB7EF17E295C674300B17035 /* CommIOSNotifications.mm in Sources */ = {isa = PBXBuildFile; fileRef = CB7EF17D295C5D1800B17035 /* CommIOSNotifications.mm */; }; CB7EF180295C674300B17035 /* CommIOSNotificationsBridgeQueue.mm in Sources */ = {isa = PBXBuildFile; fileRef = CB7EF17B295C580500B17035 /* CommIOSNotificationsBridgeQueue.mm */; }; CB90951F29534B32002F2A7F /* CommSecureStore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 71D4D7CB26C50B1000FCDBCD /* CommSecureStore.mm */; }; + CB99DB4D2C45327C00B8055E /* NotificationsInboundKeysProvider.mm in Sources */ = {isa = PBXBuildFile; fileRef = CB99DB4C2C45327B00B8055E /* NotificationsInboundKeysProvider.mm */; }; + CB99DB4E2C45329500B8055E /* NotificationsInboundKeysProvider.mm in Sources */ = {isa = PBXBuildFile; fileRef = CB99DB4C2C45327B00B8055E /* NotificationsInboundKeysProvider.mm */; }; CBA5F8852B6979F7005BE700 /* SQLiteConnectionManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CBA5F8842B6979ED005BE700 /* SQLiteConnectionManager.cpp */; }; CBAAA4702B459181007599DA /* BackupOperationsExecutor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CBAAA46E2B459181007599DA /* BackupOperationsExecutor.cpp */; }; CBAB638A2BFCCA9B003B089F /* EntryStore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CBAB63882BFCB087003B089F /* EntryStore.cpp */; }; @@ -319,6 +321,8 @@ CB7EF17C295C580500B17035 /* CommIOSNotificationsBridgeQueue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CommIOSNotificationsBridgeQueue.h; path = Comm/CommIOSNotifications/CommIOSNotificationsBridgeQueue.h; sourceTree = ""; }; CB7EF17D295C5D1800B17035 /* CommIOSNotifications.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = CommIOSNotifications.mm; path = Comm/CommIOSNotifications/CommIOSNotifications.mm; sourceTree = ""; }; CB90951929531663002F2A7F /* CommIOSNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommIOSNotifications.h; path = Comm/CommIOSNotifications/CommIOSNotifications.h; sourceTree = ""; }; + CB99DB4B2C45326C00B8055E /* NotificationsInboundKeysProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NotificationsInboundKeysProvider.h; path = Notifications/BackgroundDataStorage/NotificationsInboundKeysProvider.h; sourceTree = ""; }; + CB99DB4C2C45327B00B8055E /* NotificationsInboundKeysProvider.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = NotificationsInboundKeysProvider.mm; path = Comm/NotificationsInboundKeysProvider.mm; sourceTree = ""; }; CBA5F8832B6979ED005BE700 /* SQLiteConnectionManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLiteConnectionManager.h; sourceTree = ""; }; CBA5F8842B6979ED005BE700 /* SQLiteConnectionManager.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SQLiteConnectionManager.cpp; sourceTree = ""; }; CBA784382B28AC4300E9F419 /* CommServicesAuthMetadataEmitter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CommServicesAuthMetadataEmitter.h; sourceTree = ""; }; @@ -432,6 +436,7 @@ 71B8CCB626BD30EC0040C0A2 /* CommCoreImplementations */ = { isa = PBXGroup; children = ( + CB99DB4C2C45327B00B8055E /* NotificationsInboundKeysProvider.mm */, CBCF984C2BA499C200DBC3D9 /* CommIOSServices */, CBB0DF5F2B768007008E22FF /* CommMMKV.mm */, CB74AB1B2B2AFF6E00CBB494 /* CommServicesAuthMetadataEmitter.mm */, @@ -732,6 +737,7 @@ CB24361529A3979500FEC4E1 /* BackgroundDataStorage */ = { isa = PBXGroup; children = ( + CB99DB4B2C45326C00B8055E /* NotificationsInboundKeysProvider.h */, CB24361729A39A2500FEC4E1 /* NotificationsCryptoModule.cpp */, CB24361629A397AB00FEC4E1 /* NotificationsCryptoModule.h */, ); @@ -1203,6 +1209,7 @@ 8E43C32C291E5B4A009378F5 /* TerminateApp.mm in Sources */, CBCF984F2BA499DA00DBC3D9 /* CommIOSServicesClient.mm in Sources */, B3B02EBF2B8538980020D118 /* CommunityStore.cpp in Sources */, + CB99DB4D2C45327C00B8055E /* NotificationsInboundKeysProvider.mm in Sources */, 8BC9568529FC49B00060AE4A /* JSIRust.cpp in Sources */, 8EA59BD92A73DAB000EB4F53 /* rustJSI-generated.cpp in Sources */, CB38B48628771CDD00171182 /* TemporaryMessageStorage.mm in Sources */, @@ -1262,6 +1269,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + CB99DB4E2C45329500B8055E /* NotificationsInboundKeysProvider.mm in Sources */, CBCF98502BA49A0500DBC3D9 /* CommIOSServicesClient.mm in Sources */, CBCA09072A8E0E7D00F75B3E /* StaffUtils.cpp in Sources */, CB3C0A3B2A125C8F009BD4DA /* NotificationsCryptoModule.cpp in Sources */, diff --git a/native/ios/Comm/NotificationsInboundKeysProvider.mm b/native/ios/Comm/NotificationsInboundKeysProvider.mm new file mode 100644 --- /dev/null +++ b/native/ios/Comm/NotificationsInboundKeysProvider.mm @@ -0,0 +1,31 @@ +#import "NotificationsInboundKeysProvider.h" +#import "CommIOSServices/CommIOSServicesClient.h" +#include +#include + +namespace comm { +std::string NotificationsInboundKeysProvider::getNotifsInboundKeysForDeviceID( + const std::string &deviceID) { + NSError *error = nil; + NSDictionary *notifInboundKeys = [CommIOSServicesClient.sharedInstance + getNotifsIdentityKeysFor:[NSString stringWithCString:deviceID.c_str() + encoding:NSUTF8StringEncoding] + orSetError:&error]; + if (error) { + throw std::runtime_error( + "Failed to fetch notifs inbound keys for device: " + deviceID + + ". Details: " + std::string([error.localizedDescription UTF8String])); + } + + std::string curve25519 = + std::string([notifInboundKeys[@"curve25519"] UTF8String]); + // There are several reason to return JSON with curve25519 only: + // 1. We only need curve25519 to create inbound session. + // 2. In Session.cpp there is a convention to pass curve25519 + // key as JSON and then add offset length to advance + // the string pointer. + // 3. There is a risk that stringification might not preserve + // the order. + return folly::toJson(folly::dynamic::object("curve25519", curve25519)); +} +} // namespace comm