diff --git a/commet/lib/client/matrix/matrix_client.dart b/commet/lib/client/matrix/matrix_client.dart index 86ab85966..e4fa819ac 100644 --- a/commet/lib/client/matrix/matrix_client.dart +++ b/commet/lib/client/matrix/matrix_client.dart @@ -41,6 +41,10 @@ import '../../ui/pages/matrix/verification/matrix_verification_page.dart'; import 'matrix_room.dart'; import 'matrix_space.dart'; import 'package:vodozemac/vodozemac.dart' as vod; +import 'dart:io'; +import 'package:vodozemac/src/generated/frb_generated.dart' as vod_frb; +import 'package:flutter_rust_bridge/src/platform_types/_io.dart' + show ExternalLibrary; class MatrixClient extends Client { late matrix.Client _matrixClient; @@ -201,7 +205,13 @@ class MatrixClient extends Client { static Future _checkSystem(ClientManager clientManager) async { try { - await vod.init(wasmPath: './assets/assets/vodozemac/'); + if (!kIsWeb && (Platform.isMacOS || Platform.isIOS)) { + await vod_frb.RustLib.init( + externalLibrary: ExternalLibrary.process(iKnowHowToUseIt: true), + ); + } else { + await vod.init(wasmPath: './assets/assets/vodozemac/'); + } if (!vod.isInitialized()) { throw Exception("Vodozemac failed to initialize!"); } diff --git a/commet/lib/ui/pages/settings/categories/account/security/matrix/cross_signing/cross_signing_page.dart b/commet/lib/ui/pages/settings/categories/account/security/matrix/cross_signing/cross_signing_page.dart index a0edf4b17..e4f65a4cf 100644 --- a/commet/lib/ui/pages/settings/categories/account/security/matrix/cross_signing/cross_signing_page.dart +++ b/commet/lib/ui/pages/settings/categories/account/security/matrix/cross_signing/cross_signing_page.dart @@ -28,6 +28,7 @@ class MatrixCrossSigningPage extends StatefulWidget { class MatrixCrossSigningPageState extends State { BootstrapState state = BootstrapState.loading; + String? errorMessage; Bootstrap? bootstrapper; @override void initState() { @@ -42,6 +43,7 @@ class MatrixCrossSigningPageState extends State { return MatrixCrossSigningView( state, recoveryKey: bootstrapper?.newSsssKey?.recoveryKey, + errorMessage: errorMessage, onSetNewSsss: (passphrase) { bootstrapper?.newSsss(passphrase); }, @@ -69,12 +71,35 @@ class MatrixCrossSigningPageState extends State { bootstrapper?.wipeOnlineKeyBackup(wipe); }, openExistingSsss: (key) async { - await bootstrapper?.newSsssKey!.unlock(keyOrPassphrase: key); - await bootstrapper?.client.encryption!.crossSigning - .selfSign(keyOrPassphrase: key); - await bootstrapper?.openExistingSsss(); - await bootstrapper?.askSetupCrossSigning(setupMasterKey: true); - bootstrapper?.wipeOnlineKeyBackup(false); + try { + await bootstrapper?.newSsssKey!.unlock(keyOrPassphrase: key); + await bootstrapper?.openExistingSsss(); + } catch (e, s) { + debugPrint('[Cross Signing] Error opening existing SSSS: $e\n$s'); + setState(() { + errorMessage = e.toString(); + state = BootstrapState.error; + }); + } + }, + ignoreBadSecrets: (ignore) { + bootstrapper?.ignoreBadSecrets(ignore); + }, + unlockedSsss: (key) async { + try { + if (bootstrapper?.oldSsssKeys != null) { + for (final oldKey in bootstrapper!.oldSsssKeys!.values) { + await oldKey.unlock(keyOrPassphrase: key); + } + } + bootstrapper?.unlockedSsss(); + } catch (e, s) { + debugPrint('[Cross Signing] Error unlocking old SSSS: $e\n$s'); + setState(() { + errorMessage = e.toString(); + state = BootstrapState.error; + }); + } }, wipeCrossSigning: (wipe) { bootstrapper?.wipeCrossSigning(wipe); diff --git a/commet/lib/ui/pages/settings/categories/account/security/matrix/cross_signing/cross_signing_view.dart b/commet/lib/ui/pages/settings/categories/account/security/matrix/cross_signing/cross_signing_view.dart index 5d208871a..01d5ca72a 100644 --- a/commet/lib/ui/pages/settings/categories/account/security/matrix/cross_signing/cross_signing_view.dart +++ b/commet/lib/ui/pages/settings/categories/account/security/matrix/cross_signing/cross_signing_view.dart @@ -20,7 +20,10 @@ class MatrixCrossSigningView extends StatefulWidget { this.wipeSsss, this.wipeExistingBackup, this.openExistingSsss, - this.wipeCrossSigning}); + this.wipeCrossSigning, + this.ignoreBadSecrets, + this.unlockedSsss, + this.errorMessage}); final BootstrapState state; final Function(String?)? onSetNewSsss; @@ -31,7 +34,10 @@ class MatrixCrossSigningView extends StatefulWidget { final Function(bool)? wipeExistingBackup; final Function(String)? openExistingSsss; final Function(bool)? wipeCrossSigning; + final Function(bool)? ignoreBadSecrets; + final Function(String)? unlockedSsss; final String? recoveryKey; + final String? errorMessage; @override State createState() => _MatrixCrossSigningViewState(); } @@ -194,6 +200,16 @@ class _MatrixCrossSigningViewState extends State { desc: "Explains what the message backup does", name: "labelMatrixExplainOnlineKeyBackup"); + String get labelMatrixBadSecretsWarning => Intl.message( + "Some of your encryption secrets appear to be corrupt or invalid. You can continue and ignore the bad secrets, but this may result in data loss.", + desc: "Warning shown when bad SSSS secrets are detected", + name: "labelMatrixBadSecretsWarning"); + + String get labelMatrixBootstrapError => Intl.message( + "An error occurred during the encryption setup process.", + desc: "Shown when the cross-signing bootstrap encounters an error", + name: "labelMatrixBootstrapError"); + @override void initState() { passphraseController.addListener(() { @@ -236,13 +252,9 @@ class _MatrixCrossSigningViewState extends State { case BootstrapState.askUseExistingSsss: return askUseExistingSsss(); case BootstrapState.askUnlockSsss: - // ignore: todo - // TODO: Handle this case. - break; + return askUnlockSsss(); case BootstrapState.askBadSsss: - // ignore: todo - // TODO: Handle this case. - break; + return askBadSsss(); case BootstrapState.askNewSsss: return askNewSsss(); case BootstrapState.openExistingSsss: @@ -260,7 +272,6 @@ class _MatrixCrossSigningViewState extends State { case BootstrapState.done: return done(context); } - return tiamat.Text.label(widget.state.toString()); } Widget askSetupCrossSigning() { @@ -648,9 +659,16 @@ class _MatrixCrossSigningViewState extends State { child: m.Icon( m.Icons.error, color: m.Colors.red, - size: 100, + size: 60, )), ), + tiamat.Text.label(labelMatrixBootstrapError), + if (widget.errorMessage != null) + Padding( + padding: const EdgeInsets.all(8.0), + child: tiamat.Text.labelLow(widget.errorMessage!), + ), + const SizedBox(height: 10), tiamat.Button.secondary( text: CommonStrings.promptBack, onTap: () => m.Navigator.pop(context), @@ -658,6 +676,71 @@ class _MatrixCrossSigningViewState extends State { ]); } + Widget askUnlockSsss() { + return SizedBox( + width: 400, + child: m.Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + m.Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + tiamat.Text.label(labelMatrixRecoveryKeyPromptExplanation), + const SizedBox(height: 8), + tiamat.TextInput( + placeholder: promptMatrixRecoveryKeyInput, + controller: keyInputController, + obscureText: true, + ) + ], + ), + const SizedBox( + height: 10, + ), + tiamat.Button( + text: CommonStrings.promptConfirm, + onTap: () { + widget.unlockedSsss?.call(keyInputController.text); + }) + ], + ), + ); + } + + Widget askBadSsss() { + return m.SizedBox( + width: 500, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const m.Padding( + padding: EdgeInsets.all(20.0), + child: Center( + child: m.Icon( + m.Icons.warning_amber_rounded, + color: m.Colors.orange, + size: 60, + )), + ), + tiamat.Text.label(labelMatrixBadSecretsWarning), + const SizedBox(height: 20), + tiamat.Button.danger( + text: CommonStrings.promptContinue, + onTap: () => widget.ignoreBadSecrets?.call(true), + ), + const SizedBox(height: 10), + tiamat.Button.secondary( + text: CommonStrings.promptBack, + onTap: () => widget.ignoreBadSecrets?.call(false), + ), + ], + ), + ); + } + Widget askWipeOnlineKeybackup() { return Column( mainAxisSize: MainAxisSize.min, diff --git a/commet/macos/Podfile b/commet/macos/Podfile index ff5ddb3b8..f46891d01 100644 --- a/commet/macos/Podfile +++ b/commet/macos/Podfile @@ -30,6 +30,10 @@ target 'Runner' do use_frameworks! flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + + # Required by matrix-dart-sdk for SSSS encryption (AES-CTR via FFI) + pod 'OpenSSL-Universal' + target 'RunnerTests' do inherit! :search_paths end diff --git a/commet/macos/Podfile.lock b/commet/macos/Podfile.lock index 8d45af367..622c7faad 100644 --- a/commet/macos/Podfile.lock +++ b/commet/macos/Podfile.lock @@ -31,6 +31,7 @@ PODS: - FlutterMacOS - media_kit_video (0.0.1): - FlutterMacOS + - OpenSSL-Universal (3.3.3001) - package_info_plus (0.0.1): - FlutterMacOS - pasteboard (0.0.1): @@ -99,6 +100,7 @@ DEPENDENCIES: - livekit_client (from `Flutter/ephemeral/.symlinks/plugins/livekit_client/macos`) - media_kit_libs_macos_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos`) - media_kit_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_video/macos`) + - OpenSSL-Universal - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) - pasteboard (from `Flutter/ephemeral/.symlinks/plugins/pasteboard/macos`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) @@ -114,6 +116,7 @@ DEPENDENCIES: SPEC REPOS: trunk: + - OpenSSL-Universal - sqlite3 - WebRTC-SDK @@ -189,6 +192,7 @@ SPEC CHECKSUMS: livekit_client: 95b4a47f51f98a8be3a181c3fa251be7823dddd4 media_kit_libs_macos_video: 85a23e549b5f480e72cae3e5634b5514bc692f65 media_kit_video: fa6564e3799a0a28bff39442334817088b7ca758 + OpenSSL-Universal: 6082b0bf950e5636fe0d78def171184e2b3899c2 package_info_plus: f0052d280d17aa382b932f399edf32507174e870 pasteboard: 278d8100149f940fb795d6b3a74f0720c890ecb7 path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 @@ -204,6 +208,6 @@ SPEC CHECKSUMS: window_manager: b729e31d38fb04905235df9ea896128991cad99e window_to_front: 9e76fd432e36700a197dac86a0011e49c89abe0a -PODFILE CHECKSUM: 54d867c82ac51cbd61b565781b9fada492027009 +PODFILE CHECKSUM: 19065e8adff63d84145877df2f3da58476044a5b COCOAPODS: 1.16.2 diff --git a/commet/macos/Runner/DebugProfile.entitlements b/commet/macos/Runner/DebugProfile.entitlements index dddb8a30c..08c3ab17c 100644 --- a/commet/macos/Runner/DebugProfile.entitlements +++ b/commet/macos/Runner/DebugProfile.entitlements @@ -8,5 +8,7 @@ com.apple.security.network.server + com.apple.security.network.client + diff --git a/commet/macos/Runner/Release.entitlements b/commet/macos/Runner/Release.entitlements index 852fa1a47..ee95ab7e5 100644 --- a/commet/macos/Runner/Release.entitlements +++ b/commet/macos/Runner/Release.entitlements @@ -4,5 +4,7 @@ com.apple.security.app-sandbox + com.apple.security.network.client + diff --git a/commet/pubspec.lock b/commet/pubspec.lock index 1f45e0e71..2fd817808 100644 --- a/commet/pubspec.lock +++ b/commet/pubspec.lock @@ -149,10 +149,10 @@ packages: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.4.1" charcode: dependency: transitive description: @@ -1006,18 +1006,18 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.18" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.13.0" matrix: dependency: "direct main" description: @@ -1104,10 +1104,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.17.0" mime: dependency: "direct main" description: @@ -1800,26 +1800,26 @@ packages: dependency: "direct main" description: name: test - sha256: "65e29d831719be0591f7b3b1a32a3cda258ec98c58c7b25f7b84241bc31215bb" + sha256: "54c516bbb7cee2754d327ad4fca637f78abfc3cbcc5ace83b3eda117e42cd71a" url: "https://pub.dev" source: hosted - version: "1.26.2" + version: "1.29.0" test_api: dependency: transitive description: name: test_api - sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" + sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636" url: "https://pub.dev" source: hosted - version: "0.7.6" + version: "0.7.9" test_core: dependency: transitive description: name: test_core - sha256: "80bf5a02b60af04b09e14f6fe68b921aad119493e26e490deaca5993fef1b05a" + sha256: "394f07d21f0f2255ec9e3989f21e54d3c7dc0e6e9dbce160e5a9c1a6be0e2943" url: "https://pub.dev" source: hosted - version: "0.6.11" + version: "0.6.15" tiamat: dependency: "direct main" description: