From b4fb4108feba53f6d274852330ad08aa5bd58782 Mon Sep 17 00:00:00 2001 From: Nikolas Rimikis Date: Sat, 30 Sep 2023 07:49:17 +0200 Subject: [PATCH] refactor(neon,nextcloud): cleanup PushNotification decryption Signed-off-by: Nikolas Rimikis --- .../lib/src/blocs/push_notifications.dart | 2 +- .../lib/src/models/push_notification.dart | 49 +++++++++++++++++-- .../neon/neon/lib/src/utils/push_utils.dart | 19 +++---- 3 files changed, 54 insertions(+), 16 deletions(-) diff --git a/packages/neon/neon/lib/src/blocs/push_notifications.dart b/packages/neon/neon/lib/src/blocs/push_notifications.dart index d4715cd7..76da7c67 100644 --- a/packages/neon/neon/lib/src/blocs/push_notifications.dart +++ b/packages/neon/neon/lib/src/blocs/push_notifications.dart @@ -62,7 +62,7 @@ class PushNotificationsBloc extends Bloc implements PushNotificationsBlocEvents, Future _setupUnifiedPush() async { // We just use a single RSA keypair for all accounts - final keypair = await PushUtils.loadRSAKeypair(); + final keypair = PushUtils.loadRSAKeypair(); await UnifiedPush.initialize( onNewEndpoint: (final endpoint, final instance) async { diff --git a/packages/neon/neon/lib/src/models/push_notification.dart b/packages/neon/neon/lib/src/models/push_notification.dart index ee6f30ec..0951d498 100644 --- a/packages/neon/neon/lib/src/models/push_notification.dart +++ b/packages/neon/neon/lib/src/models/push_notification.dart @@ -1,13 +1,26 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:meta/meta.dart'; -import 'package:nextcloud/notifications.dart' show DecryptedSubject; +import 'package:nextcloud/notifications.dart' as notifications; part 'push_notification.g.dart'; +/// The json key for [PushNotification.accountID]. +const String _accountIDKey = 'accountID'; + +/// The json key for [PushNotification.priority]. +const String _priorityKey = 'priority'; + +/// The json key for [PushNotification.type]. +const String _typeKey = 'type'; + +/// The json key for [PushNotification.subject]. +const String _subjectKey = 'subject'; + +/// Data for a push notification. @JsonSerializable() -@immutable @internal class PushNotification { + /// Creates a new push notification. const PushNotification({ required this.accountID, required this.priority, @@ -15,14 +28,44 @@ class PushNotification { required this.subject, }); + /// Creates a new PushNotification object from the given [json] data. + /// + /// Use [PushNotification.fromEncrypted] when you the [subject] is still encrypted. factory PushNotification.fromJson(final Map json) => _$PushNotificationFromJson(json); + + /// Creates a new PushNotification object from the given [json] data containing an encrypted [subject]. + /// + /// Use [PushNotification.fromJson] when the [subject] is not encrypted. + factory PushNotification.fromEncrypted( + final Map json, + final notifications.RSAPrivateKey privateKey, + ) { + final subject = notifications.decryptPushNotificationSubject(privateKey, json[_subjectKey] as String); + + return PushNotification( + accountID: json[_accountIDKey] as String, + priority: json[_priorityKey] as String, + type: json[_typeKey] as String, + subject: subject, + ); + } + + /// Parses this object into a json like map. Map toJson() => _$PushNotificationToJson(this); + /// The account associated to this notification. + @JsonKey(name: _accountIDKey) final String accountID; + /// The priority of the notification. + @JsonKey(name: _priorityKey) final String priority; + /// The type of the notification. + @JsonKey(name: _typeKey) final String type; - final DecryptedSubject subject; + /// The subject of this notification. + @JsonKey(name: _subjectKey) + final notifications.DecryptedSubject subject; } diff --git a/packages/neon/neon/lib/src/utils/push_utils.dart b/packages/neon/neon/lib/src/utils/push_utils.dart index 92c2e976..13a8a808 100644 --- a/packages/neon/neon/lib/src/utils/push_utils.dart +++ b/packages/neon/neon/lib/src/utils/push_utils.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:ui'; @@ -23,17 +24,17 @@ import 'package:nextcloud/notifications.dart' as notifications; class PushUtils { const PushUtils._(); - static Future loadRSAKeypair() async { + static notifications.RSAKeypair loadRSAKeypair() { const storage = AppStorage(StorageKeys.notifications); const keyDevicePrivateKey = 'device-private-key'; - late notifications.RSAKeypair keypair; - if (!storage.containsKey(keyDevicePrivateKey) || (storage.getString(keyDevicePrivateKey)?.isEmpty ?? true)) { + final notifications.RSAKeypair keypair; + if (!storage.containsKey(keyDevicePrivateKey) || (storage.getString(keyDevicePrivateKey)!.isEmpty)) { debugPrint('Generating RSA keys for push notifications'); // The key size has to be 2048, other sizes are not accepted by Nextcloud (at the moment at least) // ignore: avoid_redundant_argument_values keypair = notifications.RSAKeypair.fromRandom(keySize: 2048); - await storage.setString(keyDevicePrivateKey, keypair.privateKey.toPEM()); + unawaited(storage.setString(keyDevicePrivateKey, keypair.privateKey.toPEM())); } else { keypair = notifications.RSAKeypair(notifications.RSAPrivateKey.fromPEM(storage.getString(keyDevicePrivateKey)!)); } @@ -74,16 +75,10 @@ class PushUtils { ); await NeonStorage.init(); - final keypair = await loadRSAKeypair(); - + final keypair = loadRSAKeypair(); for (final message in Uri(query: utf8.decode(messages)).queryParameters.values) { final data = json.decode(message) as Map; - final pushNotification = PushNotification( - accountID: instance, - priority: data['priority']! as String, - type: data['type']! as String, - subject: notifications.decryptPushNotificationSubject(keypair.privateKey, data['subject']! as String), - ); + final pushNotification = PushNotification.fromEncrypted(data, keypair.privateKey); if (pushNotification.subject.delete ?? false) { await localNotificationsPlugin.cancel(_getNotificationID(instance, pushNotification));