diff --git a/lib/g1/g1_helper.dart b/lib/g1/g1_helper.dart index b2a6721128e547e1987165d62e5dcf23f43700a3..6a043c89478d06c700f426f5e7515e7911ff7e1f 100644 --- a/lib/g1/g1_helper.dart +++ b/lib/g1/g1_helper.dart @@ -261,7 +261,9 @@ PaymentState? parseScannedUri(String qrOrig) { return null; } -final IV _iv = encrypt.IV.fromLength(16); +// https://github.com/leocavalcante/encrypt/issues/314#issuecomment-1729499372 +// final IV _iv = encrypt.IV.fromLength(16); +final IV _iv = IV(Uint8List(16)); Map<String, String> encryptJsonForExport(String jsonString, String password) { final Uint8List plainText = Uint8List.fromList(utf8.encode(jsonString)); @@ -276,10 +278,19 @@ Map<String, String> encryptJsonForExport(String jsonString, String password) { Map<String, dynamic> decryptJsonForImport( String keyEncrypted, String password) { - final String decrypted = encrypt.Encrypter( - encrypt.AES(encrypt.Key.fromUtf8(password.padRight(32)))) - .decrypt64(keyEncrypted, iv: _iv); - return jsonDecode(decrypted) as Map<String, dynamic>; + // This fails if encrypt > 5.0.1 + // https://github.com/leocavalcante/encrypt/issues/314 + try { + final Key key = encrypt.Key.fromUtf8(password.padRight(32)); + final AES aes = encrypt.AES(key); + final String decrypted = + encrypt.Encrypter(aes).decrypt64(keyEncrypted, iv: _iv); + return jsonDecode(decrypted) as Map<String, dynamic>; + } catch (e, stacktrace) { + logger('Decrypt error: $e'); + logger(stacktrace); + rethrow; + } } const Duration wrongNodeDuration = Duration(days: 2); diff --git a/lib/ui/widgets/fifth_screen/import_dialog.dart b/lib/ui/widgets/fifth_screen/import_dialog.dart index e46cfaf0155788cd8627eb760281d191385c529d..a7e073ebf4616f3509f9b26d355defd911a7963c 100644 --- a/lib/ui/widgets/fifth_screen/import_dialog.dart +++ b/lib/ui/widgets/fifth_screen/import_dialog.dart @@ -49,7 +49,8 @@ class _ImportDialogState extends State<ImportDialog> { final Map<String, dynamic> keyJson = jsonDecode(keyEncString) as Map<String, dynamic>; final String keyEncrypted = keyJson['key'] as String; - final String? contacts = keyJson['contacts'] as String?; + final List<dynamic>? contacts = + keyJson['contacts'] as List<dynamic>?; return Scaffold( key: scaffoldKey, appBar: AppBar( @@ -146,6 +147,7 @@ class _ImportDialogState extends State<ImportDialog> { Navigator.of(context).pop(true); } catch (e, stacktrace) { logger(e.toString()); + logger(stacktrace); if (!context.mounted) { return; } @@ -173,15 +175,13 @@ class _ImportDialogState extends State<ImportDialog> { }); } - void importContacts(String? contacts, BuildContext context) { + void importContacts(List<dynamic>? contacts, BuildContext context) { if (contacts != null) { final ContactsCubit contactsCubit = context.read<ContactsCubit>(); final List<Contact> existingContacts = contactsCubit.contacts; - final List<dynamic> contactsImported = - jsonDecode(contacts) as List<dynamic>; - if (contactsImported.isNotEmpty) { + if (contacts.isNotEmpty) { if (existingContacts.isNotEmpty) { - for (final dynamic contactJson in contactsImported) { + for (final dynamic contactJson in contacts) { final Contact contact = Contact.fromJson(contactJson as Map<String, dynamic>); if (!contactsCubit.isContact(contact.pubKey)) { @@ -193,7 +193,7 @@ class _ImportDialogState extends State<ImportDialog> { } } } else { - for (final dynamic contactJson in contactsImported) { + for (final dynamic contactJson in contacts) { final Contact contact = Contact.fromJson(contactJson as Map<String, dynamic>); contactsCubit.addContact(contact); @@ -394,16 +394,20 @@ class SelectImportMethodDialog extends StatelessWidget { } Future<bool> requestStoragePermission(BuildContext context) async { - final PermissionStatus status = await Permission.storage.request(); - if (!context.mounted) { - return false; - } - if (status.isGranted) { - return true; + if (!Platform.isLinux) { + final PermissionStatus status = await Permission.storage.request(); + if (!context.mounted) { + return false; + } + if (status.isGranted) { + return true; + } else { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(tr('storage_permission_request')), + )); + return false; + } } else { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text(tr('storage_permission_request')), - )); - return false; + return true; } } diff --git a/pubspec.yaml b/pubspec.yaml index 2962672248d99db3e54b460193e7a2f59f9101d9..4da41b00a69012e3329d975f4c2620634c3b8934 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -50,7 +50,9 @@ dependencies: shared_preferences: ^2.0.18 vibration: ^2.0.0 clipboard: ^0.1.3 + # Does not work with encypt > 5.0.1 # See: https://github.com/leocavalcante/encrypt/issues/314 + # Fix: https://github.com/leocavalcante/encrypt/issues/314#issuecomment-1729499372 encrypt: ^5.0.3 universal_html: ^2.0.9 fl_chart: ^0.69.0 @@ -104,7 +106,7 @@ dependencies: ferry: ^0.16.0+1 gql_http_link: ^1.1.0 substrate_bip39: ^0.4.1 - pointycastle: ^3.9.0 + pointycastle: ^3.9.1 bip39_multi_nullsafety: ^1.0.7 material_symbols_icons: ^4.2719.3 text_scroll: ^0.2.0 diff --git a/test/g1_test.dart b/test/g1_test.dart index f5e16be3ae7bfc2f6f683ad327b2848d0ffdae86..3d628bc1a58174309667bceeecb005c4ad971e8f 100644 --- a/test/g1_test.dart +++ b/test/g1_test.dart @@ -620,6 +620,29 @@ void main() { expect(pendingTransaction.recipientsAmounts, equals(sendingTransaction.recipientsAmounts)); }); + + test('Import wallet and verify primary key', () async { + // Given + const String walletBackup = + '{"key":"rLkdXaEaz8hk0OGbb7TTMuh0d+aNrkW0fA1rlCR1lOvuURPy717ayCSvDviXE6J+LDRJ6FpbsG2SReDB6lcF8crS7DOyF5K4gx16RF4DlHaVxxZrwRnlVCxyBN9NstFlLglgAFnx/XmZJLSzZ7w/gG6ka9miXKECrPdUw93nPF3hPZhfXtcXzGo+6UKBtVtglEfjOXmgjDMTuYgbtJuHKvdAjoCDDNfpMmp6wV+C6zTglRRhHMh9+oubCmekwxrvAKA0lueC5M/CPL+puPH21/3wLHed8hF9N2F2EHmjSNGeK1r7ferN0SbwntWdNOfA/Jzhdxg7F+XNMeSNn7J4Py+jVwwx0Bs/wjw8DQI02cHSzBNOl+jP0ESs784ArMv2tL/sASAM5K0bXcc/zI89tOLI6A4+jnzOFNdGfjuPVU1AmNye79KCUDPXv6Qh4T6ZoiDgHtjFlT9n6/9RYLOFw86Lr0255ont8nnhm+MXkDhg38SscUMaU8I5CPglfWf+/bO0fDA2VQ0a/6wgpyWI02n3LzHgUEF6+l4akrHDn4ahm/pWaeS9DPIxw+WGMFYRPCph0tI5Bp0Alf6vy64ZGSP1VxrvbETvWQ3okWaOBOqm571h/CMVbre7CbKMqjFtFLiWbBJBmBNx1QSt5uGmGrWaJI29gHSW/MwU7YSvkCrIzoSJkr/7e2vDytkeG4Eq"}'; + const String password = '678'; + const String expectedPrimaryKey = + '6JgGvDDBu8XWL89BTvzHCfVmJWbSRfBNb1ZK4dQW6fNK'; + + // When + final Map<String, dynamic> keyJson = + jsonDecode(walletBackup) as Map<String, dynamic>; + final String keyEncrypted = keyJson['key'] as String; + final Map<String, dynamic> decryptedKeys = + decryptJsonForImport(keyEncrypted, password); + + // Then + final dynamic cesiumCards = decryptedKeys['cesiumCards']; + final dynamic firstCard = + (jsonDecode(cesiumCards as String) as List<dynamic>)[0]; + final String primaryKey = firstCard['pubKey'] as String; + expect(primaryKey, equals(expectedPrimaryKey)); + }); } String _generateRandomPatternPassword(Random random) {