From f09dc07c4151276b678ec44dcd2d75d483bf3897 Mon Sep 17 00:00:00 2001 From: Foxushka <135865149+Foxushka@users.noreply.github.com> Date: Sun, 18 Feb 2024 02:31:04 +0300 Subject: [PATCH] Gen3 write support and don't rely on SAK/ATQA to determine the number of blocks (#453) * Don't rely on SAK/ATQA to determine the number of blocks * Refactor write page * Gen3 write support --- chameleonultragui/android/app/build.gradle | 4 +- .../lib/gui/component/card_recovery.dart | 412 +++++++++--------- chameleonultragui/lib/gui/page/read_card.dart | 6 +- .../lib/gui/page/write_card.dart | 128 +----- .../lib/helpers/mifare_classic/general.dart | 37 +- .../lib/helpers/mifare_classic/recovery.dart | 16 +- .../helpers/mifare_classic/write/base.dart | 113 ++++- .../helpers/mifare_classic/write/gen1.dart | 5 +- .../helpers/mifare_classic/write/gen2.dart | 28 +- .../helpers/mifare_classic/write/gen3.dart | 85 ++++ chameleonultragui/lib/helpers/write.dart | 15 +- .../Flutter/GeneratedPluginRegistrant.swift | 2 +- chameleonultragui/pubspec.lock | 132 +++--- chameleonultragui/pubspec.yaml | 4 +- 14 files changed, 559 insertions(+), 428 deletions(-) create mode 100644 chameleonultragui/lib/helpers/mifare_classic/write/gen3.dart diff --git a/chameleonultragui/android/app/build.gradle b/chameleonultragui/android/app/build.gradle index d5fba90f..8caef025 100644 --- a/chameleonultragui/android/app/build.gradle +++ b/chameleonultragui/android/app/build.gradle @@ -32,7 +32,7 @@ if (keystorePropertiesFile.exists()) { } android { - compileSdkVersion flutter.compileSdkVersion + compileSdkVersion Math.max(flutter.compileSdkVersion, 34) ndkVersion flutter.ndkVersion compileOptions { @@ -54,7 +54,7 @@ android { // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdkVersion 21 - targetSdkVersion flutter.targetSdkVersion + targetSdkVersion Math.max(flutter.targetSdkVersion, 34) versionCode flutterVersionCode.toInteger() versionName flutterVersionName } diff --git a/chameleonultragui/lib/gui/component/card_recovery.dart b/chameleonultragui/lib/gui/component/card_recovery.dart index e78081e8..49aaecbb 100644 --- a/chameleonultragui/lib/gui/component/card_recovery.dart +++ b/chameleonultragui/lib/gui/component/card_recovery.dart @@ -129,242 +129,250 @@ class CardRecoveryState extends State { fontWeight: FontWeight.bold, ), ), - Row( - children: [ - const Spacer(), - KeyCheckMarks( - checkMarks: widget.mfcInfo.recovery!.checkMarks, - validKeys: widget.mfcInfo.recovery!.validKeys, - fontSize: checkmarkFontSize, - checkmarkSize: checkmarkSize, - checkmarkCount: mfClassicGetSectorCount(widget.mfcInfo.type), - checkmarkPerRow: checkmarkPerRow), - const Spacer(), + if (widget.mfcInfo.recovery != null) ...[ + Row( + children: [ + const Spacer(), + KeyCheckMarks( + checkMarks: widget.mfcInfo.recovery!.checkMarks, + validKeys: widget.mfcInfo.recovery!.validKeys, + fontSize: checkmarkFontSize, + checkmarkSize: checkmarkSize, + checkmarkCount: mfClassicGetSectorCount(widget.mfcInfo.type), + checkmarkPerRow: checkmarkPerRow), + const Spacer(), + ], + ), + if (widget.mfcInfo.recovery?.error != "") ...[ + const SizedBox(height: 16), + ErrorMessage(errorMessage: widget.mfcInfo.recovery!.error), ], - ), - if (widget.mfcInfo.recovery?.error != "") ...[ - const SizedBox(height: 16), - ErrorMessage(errorMessage: widget.mfcInfo.recovery!.error), - ], - const SizedBox(height: 12), - if (widget.mfcInfo.recovery?.dumpProgress != 0) ...[ - LinearProgressIndicator(value: widget.mfcInfo.recovery?.dumpProgress), - const SizedBox(height: 8) - ], - if (widget.mfcInfo.state == MifareClassicState.recovery || - widget.mfcInfo.state == MifareClassicState.recoveryOngoing) - FittedBox( - alignment: Alignment.topCenter, - fit: BoxFit.scaleDown, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox(height: 8), - ElevatedButton( - onPressed: (widget.mfcInfo.state == - MifareClassicState.recovery) - ? () async { - setState(() { - widget.mfcInfo.state = - MifareClassicState.recoveryOngoing; - }); - - await widget.mfcInfo.recovery?.recoverKeys(); - - if (widget.mfcInfo.recovery!.error.isNotEmpty) { - setState(() { - widget.mfcInfo.state = - MifareClassicState.recovery; - }); - if (widget.mfcInfo.recovery!.error == - "no_keys_darkside") { - setState(() { - widget.mfcInfo.recovery?.error = localizations - .recovery_error_no_keys_darkside; - }); - } else if (widget.mfcInfo.recovery!.error == - "not_supported") { - setState(() { - widget.mfcInfo.recovery?.error = - localizations.recovery_error_no_supported; - }); - } - } else { - setState(() { - widget.mfcInfo.state = MifareClassicState.dump; - }); - } - } - : null, - child: Text(localizations.recover_keys), - ), - if (widget.allowSave) ...[ - const SizedBox(width: 8), + const SizedBox(height: 12), + if (widget.mfcInfo.recovery?.dumpProgress != 0) ...[ + LinearProgressIndicator(value: widget.mfcInfo.recovery?.dumpProgress), + const SizedBox(height: 8) + ], + if (widget.mfcInfo.state == MifareClassicState.recovery || + widget.mfcInfo.state == MifareClassicState.recoveryOngoing) + FittedBox( + alignment: Alignment.topCenter, + fit: BoxFit.scaleDown, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 8), ElevatedButton( onPressed: (widget.mfcInfo.state == MifareClassicState.recovery) ? () async { setState(() { widget.mfcInfo.state = - MifareClassicState.dumpOngoing; + MifareClassicState.recoveryOngoing; }); - try { - await widget.mfcInfo.recovery?.dumpData(); + await widget.mfcInfo.recovery?.recoverKeys(); + if (widget.mfcInfo.recovery!.error.isNotEmpty) { setState(() { - widget.mfcInfo.recovery?.dumpProgress = 0; widget.mfcInfo.state = - MifareClassicState.save; + MifareClassicState.recovery; }); - } catch (_) { + if (widget.mfcInfo.recovery!.error == + "no_keys_darkside") { + setState(() { + widget.mfcInfo.recovery?.error = + localizations + .recovery_error_no_keys_darkside; + }); + } else if (widget.mfcInfo.recovery!.error == + "not_supported") { + setState(() { + widget.mfcInfo.recovery?.error = + localizations + .recovery_error_no_supported; + }); + } + } else { setState(() { - widget.mfcInfo.recovery?.error = - localizations.recovery_error_dump_data; widget.mfcInfo.state = MifareClassicState.dump; }); } } : null, - child: Text(localizations.dump_partial_data), - ) - ], - const SizedBox(width: 8), - ElevatedButton( - onPressed: () async { - await exportFoundKeys(); - }, - child: Text(localizations.export_to_dictionary), - ), - ])), - if (widget.mfcInfo.state == MifareClassicState.checkKeys || - widget.mfcInfo.state == MifareClassicState.checkKeysOngoing) - Column(children: [ - Align( - alignment: Alignment.center, - child: SizedBox( - width: 275, // WIP: center without this - child: CheckboxListTile( - title: Text(localizations.skip_default_dictionary), - value: skipDefaultDictionary, - onChanged: (bool? newValue) { - setState(() { - skipDefaultDictionary = newValue!; - }); - }, - controlAffinity: ListTileControlAffinity.leading, - ))), - const SizedBox(height: 8), - Text(localizations.additional_key_dict), - const SizedBox(height: 4), - DropdownButton( - value: widget.mfcInfo.recovery?.selectedDictionary!.id, - items: widget.mfcInfo.recovery?.dictionaries - .map>((Dictionary dictionary) { - return DropdownMenuItem( - value: dictionary.id, - child: Text( - "${dictionary.name} (${dictionary.keys.length} ${localizations.keys.toLowerCase()})"), - ); - }).toList(), - onChanged: (String? newValue) { - for (var dictionary in widget.mfcInfo.recovery!.dictionaries) { - if (dictionary.id == newValue) { - setState(() { - widget.mfcInfo.recovery?.selectedDictionary = dictionary; - }); - break; - } - } - }, - ), - const SizedBox(height: 8), - ElevatedButton( - onPressed: (widget.mfcInfo.state == MifareClassicState.checkKeys) - ? () async { - setState(() { - widget.mfcInfo.state = - MifareClassicState.checkKeysOngoing; - }); + child: Text(localizations.recover_keys), + ), + if (widget.allowSave) ...[ + const SizedBox(width: 8), + ElevatedButton( + onPressed: (widget.mfcInfo.state == + MifareClassicState.recovery) + ? () async { + setState(() { + widget.mfcInfo.state = + MifareClassicState.dumpOngoing; + }); - try { - await widget.mfcInfo.recovery!.checkKeys( - skipDefaultDictionary: skipDefaultDictionary); + try { + await widget.mfcInfo.recovery?.dumpData(); - if (widget.mfcInfo.recovery!.allKeysExists) { - // all keys exists - setState(() { - widget.mfcInfo.state = MifareClassicState.dump; - }); - } else { + setState(() { + widget.mfcInfo.recovery?.dumpProgress = 0; + widget.mfcInfo.state = + MifareClassicState.save; + }); + } catch (_) { + setState(() { + widget.mfcInfo.recovery?.error = + localizations.recovery_error_dump_data; + widget.mfcInfo.state = + MifareClassicState.dump; + }); + } + } + : null, + child: Text(localizations.dump_partial_data), + ) + ], + const SizedBox(width: 8), + ElevatedButton( + onPressed: () async { + await exportFoundKeys(); + }, + child: Text(localizations.export_to_dictionary), + ), + ])), + if (widget.mfcInfo.state == MifareClassicState.checkKeys || + widget.mfcInfo.state == MifareClassicState.checkKeysOngoing) + Column(children: [ + Align( + alignment: Alignment.center, + child: SizedBox( + width: 275, // WIP: center without this + child: CheckboxListTile( + title: Text(localizations.skip_default_dictionary), + value: skipDefaultDictionary, + onChanged: (bool? newValue) { setState(() { - widget.mfcInfo.state = MifareClassicState.recovery; + skipDefaultDictionary = newValue!; }); - } - } catch (_) { - for (var checkmark = 0; checkmark < 80; checkmark++) { - if (widget.mfcInfo.recovery?.checkMarks[checkmark] == - ChameleonKeyCheckmark.checking) { - widget.mfcInfo.recovery?.checkMarks[checkmark] = - ChameleonKeyCheckmark.none; - } - } - + }, + controlAffinity: ListTileControlAffinity.leading, + ))), + const SizedBox(height: 8), + Text(localizations.additional_key_dict), + const SizedBox(height: 4), + DropdownButton( + value: widget.mfcInfo.recovery?.selectedDictionary!.id, + items: widget.mfcInfo.recovery?.dictionaries + .map>((Dictionary dictionary) { + return DropdownMenuItem( + value: dictionary.id, + child: Text( + "${dictionary.name} (${dictionary.keys.length} ${localizations.keys.toLowerCase()})"), + ); + }).toList(), + onChanged: (String? newValue) { + for (var dictionary in widget.mfcInfo.recovery!.dictionaries) { + if (dictionary.id == newValue) { + setState(() { + widget.mfcInfo.recovery?.selectedDictionary = dictionary; + }); + break; + } + } + }, + ), + const SizedBox(height: 8), + ElevatedButton( + onPressed: (widget.mfcInfo.state == MifareClassicState.checkKeys) + ? () async { setState(() { - widget.mfcInfo.recovery?.checkMarks = - widget.mfcInfo.recovery!.checkMarks; - widget.mfcInfo.recovery?.error = - localizations.recovery_error_dict; - widget.mfcInfo.state = MifareClassicState.checkKeys; + widget.mfcInfo.state = + MifareClassicState.checkKeysOngoing; }); - } - } - : null, - child: Text(localizations.check_keys_dict), - ) - ]), - if ((widget.mfcInfo.state == MifareClassicState.dump || - widget.mfcInfo.state == MifareClassicState.dumpOngoing) && - widget.allowSave) - FittedBox( - alignment: Alignment.topCenter, - fit: BoxFit.scaleDown, - child: Row(children: [ - ElevatedButton( - onPressed: (widget.mfcInfo.state == MifareClassicState.dump) - ? () async { - setState(() { - widget.mfcInfo.state = MifareClassicState.dumpOngoing; - }); - try { - await widget.mfcInfo.recovery?.dumpData(); + try { + await widget.mfcInfo.recovery!.checkKeys( + skipDefaultDictionary: skipDefaultDictionary); + if (widget.mfcInfo.recovery!.allKeysExists) { + // all keys exists setState(() { - widget.mfcInfo.recovery?.dumpProgress = 0; - widget.mfcInfo.state = MifareClassicState.save; + widget.mfcInfo.state = MifareClassicState.dump; }); - } catch (_) { + } else { setState(() { - widget.mfcInfo.recovery?.error = - localizations.recovery_error_dump_data; - widget.mfcInfo.state = MifareClassicState.dump; + widget.mfcInfo.state = MifareClassicState.recovery; }); } + } catch (_) { + for (var checkmark = 0; checkmark < 80; checkmark++) { + if (widget.mfcInfo.recovery?.checkMarks[checkmark] == + ChameleonKeyCheckmark.checking) { + widget.mfcInfo.recovery?.checkMarks[checkmark] = + ChameleonKeyCheckmark.none; + } + } + + try { + setState(() { + widget.mfcInfo.recovery?.checkMarks = + widget.mfcInfo.recovery!.checkMarks; + widget.mfcInfo.recovery?.error = + localizations.recovery_error_dict; + widget.mfcInfo.state = MifareClassicState.checkKeys; + }); + } catch (_) {} } - : null, - child: Text(localizations.dump_card), - ), - const SizedBox(width: 8), - ElevatedButton( - onPressed: () async { - await exportFoundKeys(); - }, - child: Text(localizations.export_to_dictionary), - ), - ])), + } + : null, + child: Text(localizations.check_keys_dict), + ) + ]), + if ((widget.mfcInfo.state == MifareClassicState.dump || + widget.mfcInfo.state == MifareClassicState.dumpOngoing) && + widget.allowSave) + FittedBox( + alignment: Alignment.topCenter, + fit: BoxFit.scaleDown, + child: Row(children: [ + ElevatedButton( + onPressed: (widget.mfcInfo.state == MifareClassicState.dump) + ? () async { + setState(() { + widget.mfcInfo.state = + MifareClassicState.dumpOngoing; + }); + + try { + await widget.mfcInfo.recovery?.dumpData(); + + setState(() { + widget.mfcInfo.recovery?.dumpProgress = 0; + widget.mfcInfo.state = MifareClassicState.save; + }); + } catch (_) { + setState(() { + widget.mfcInfo.recovery?.error = + localizations.recovery_error_dump_data; + widget.mfcInfo.state = MifareClassicState.dump; + }); + } + } + : null, + child: Text(localizations.dump_card), + ), + const SizedBox(width: 8), + ElevatedButton( + onPressed: () async { + await exportFoundKeys(); + }, + child: Text(localizations.export_to_dictionary), + ), + ])), + ], if (widget.mfcInfo.state == MifareClassicState.save && widget.allowSave) Center( child: Row( diff --git a/chameleonultragui/lib/gui/page/read_card.dart b/chameleonultragui/lib/gui/page/read_card.dart index 3fab0065..8fc885a0 100644 --- a/chameleonultragui/lib/gui/page/read_card.dart +++ b/chameleonultragui/lib/gui/page/read_card.dart @@ -105,9 +105,11 @@ class ReadCardPageState extends State { CardData card = await appState.communicator!.scan14443aTag(); bool isMifareClassic = false; + MifareClassicType mifareClassicType = MifareClassicType.none; try { isMifareClassic = await appState.communicator!.detectMf1Support(); + mifareClassicType = await mfClassicGetType(appState.communicator!); } catch (_) {} bool isMifareClassicEV1 = isMifareClassic @@ -132,9 +134,7 @@ class ReadCardPageState extends State { ? bytesToHexSpace(card.ats) : localizations.no; mfcInfo.isEV1 = isMifareClassicEV1; - mfcInfo.type = isMifareClassic - ? mfClassicGetType(card.atqa, card.sak) - : MifareClassicType.none; + mfcInfo.type = mifareClassicType; mfcInfo.state = (mfcInfo.type != MifareClassicType.none) ? MifareClassicState.checkKeys : MifareClassicState.none; diff --git a/chameleonultragui/lib/gui/page/write_card.dart b/chameleonultragui/lib/gui/page/write_card.dart index 059cae34..9b122c9b 100644 --- a/chameleonultragui/lib/gui/page/write_card.dart +++ b/chameleonultragui/lib/gui/page/write_card.dart @@ -1,11 +1,7 @@ import 'package:chameleonultragui/bridge/chameleon.dart'; import 'package:chameleonultragui/connector/serial_abstract.dart'; import 'package:chameleonultragui/gui/component/card_list.dart'; -import 'package:chameleonultragui/gui/component/card_recovery.dart'; -import 'package:chameleonultragui/gui/page/read_card.dart'; import 'package:chameleonultragui/helpers/general.dart'; -import 'package:chameleonultragui/helpers/mifare_classic/general.dart'; -import 'package:chameleonultragui/helpers/mifare_classic/write/gen2.dart'; import 'package:chameleonultragui/helpers/write.dart'; import 'package:chameleonultragui/main.dart'; import 'package:chameleonultragui/sharedprefsprovider.dart'; @@ -23,8 +19,6 @@ class WriteCardPage extends StatefulWidget { } class WriteCardPageState extends State { - HFCardInfo? hfInfo; - MifareClassicInfo? mfcInfo; int step = 0; int progress = -1; bool written = false; @@ -59,6 +53,9 @@ class WriteCardPageState extends State { }); } + await helper?.getCardType(); + + if (!context.mounted) return; close(context, selectedCard.name); } @@ -77,6 +74,12 @@ class WriteCardPageState extends State { helper = magicHelper; }); + try { + await helper?.getCardType(); + } catch (_) { + await helper?.getCardType(); + } + appState.log!.i("Detected Magic card type: ${magicHelper.name}"); scaffoldMessenger.hideCurrentSnackBar(); var snackBar = SnackBar( @@ -104,71 +107,9 @@ class WriteCardPageState extends State { scaffoldMessenger.showSnackBar(snackBar); } - Future prepareMifareClassic() async { - var appState = Provider.of(context, listen: false); - var localizations = AppLocalizations.of(context)!; - - if (!await appState.communicator!.isReaderDeviceMode()) { - await appState.communicator!.setReaderDeviceMode(true); - } - - try { - CardData card = await appState.communicator!.scan14443aTag(); - bool isMifareClassic = false; - - try { - isMifareClassic = await appState.communicator!.detectMf1Support(); - } catch (_) {} - - bool isMifareClassicEV1 = isMifareClassic - ? (await appState.communicator! - .mf1Auth(0x45, 0x61, gMifareClassicKeys[3])) - : false; - - setState(() { - hfInfo = HFCardInfo(); - mfcInfo = MifareClassicInfo(); - }); - - if (isMifareClassic) { - setState(() { - mfcInfo!.recovery = helper!.getExtraData()[0]; - }); - } - - setState(() { - hfInfo!.uid = bytesToHexSpace(card.uid); - hfInfo!.sak = card.sak.toRadixString(16).padLeft(2, '0').toUpperCase(); - hfInfo!.atqa = bytesToHexSpace(card.atqa); - hfInfo!.ats = (card.ats.isNotEmpty) - ? bytesToHexSpace(card.ats) - : localizations.no; - mfcInfo!.isEV1 = isMifareClassicEV1; - mfcInfo!.type = isMifareClassic - ? mfClassicGetType(card.atqa, card.sak) - : MifareClassicType.none; - mfcInfo!.state = (mfcInfo!.type != MifareClassicType.none) - ? MifareClassicState.checkKeys - : MifareClassicState.none; - hfInfo!.tech = isMifareClassic - ? "Mifare Classic ${mfClassicGetName(mfcInfo!.type)}${isMifareClassicEV1 ? " EV1" : ""}" - : localizations.other; - }); - } catch (_) { - setState(() { - hfInfo = HFCardInfo(); - mfcInfo = MifareClassicInfo(); - }); - - setState(() { - hfInfo!.cardExist = false; - }); - } - } - void updateState() { setState(() { - mfcInfo = mfcInfo; + helper = helper; }); } @@ -239,11 +180,6 @@ class WriteCardPageState extends State { if (step != 2) { if (step == 1) { await helper?.reset(); - - setState(() { - hfInfo = null; - mfcInfo = null; - }); } setState(() { step++; @@ -281,11 +217,6 @@ class WriteCardPageState extends State { if (step == 1) { await helper?.reset(); - - setState(() { - hfInfo = null; - mfcInfo = null; - }); } } @@ -445,44 +376,13 @@ class WriteCardPageState extends State { title: (progress == -1) ? (helper != null && helper!.isReady()) ? (helper != null && - helper!.name == - MifareClassicGen2WriteHelper.staticName && - helper!.getExtraData()[1].isNotEmpty) + helper!.getFailedBlocks().isNotEmpty) ? Text( - "${localizations.otp_magic_warning(localizations.write_data_to_magic_card)} ${localizations.some_blocks_failed_to_write}: ${helper!.getExtraData()[1].join(", ")}") + "${localizations.otp_magic_warning(localizations.write_data_to_magic_card)} ${localizations.some_blocks_failed_to_write}: ${helper!.getFailedBlocks().join(", ")}") : Text(localizations.otp_magic_warning( localizations.write_data_to_magic_card)) - : (helper != null && - helper!.name == - MifareClassicGen2WriteHelper.staticName) - ? FutureBuilder( - future: (hfInfo != null) - ? Future.value([]) - : prepareMifareClassic(), - builder: (BuildContext context, - AsyncSnapshot snapshot) { - if (hfInfo != null && - mfcInfo != null && - mfcInfo!.recovery != null) { - return CardRecovery( - hfInfo: hfInfo!, - mfcInfo: mfcInfo!, - allowSave: false); - } else if (hfInfo != null && - mfcInfo != null && - mfcInfo!.type == MifareClassicType.none) { - if (hfInfo!.cardExist) { - return Text(localizations - .not_mifare_classic_card); - } else { - return Text(localizations.no_card_found); - } - } else { - return const Column(children: [ - CircularProgressIndicator() - ]); - } - }) + : (helper != null && helper!.writeWidgetSupported()) + ? helper!.getWriteWidget(context, setState) : Text(localizations.error) : LinearProgressIndicator(value: progress.toDouble() / 100), ), diff --git a/chameleonultragui/lib/helpers/mifare_classic/general.dart b/chameleonultragui/lib/helpers/mifare_classic/general.dart index 4a71f039..10fb01c9 100644 --- a/chameleonultragui/lib/helpers/mifare_classic/general.dart +++ b/chameleonultragui/lib/helpers/mifare_classic/general.dart @@ -80,23 +80,30 @@ final gMifareClassicKeys = gMifareClassicKeysList ])) .toList(); -MifareClassicType mfClassicGetType(Uint8List atqa, int sak) { - if ((atqa[1] == 0x44 || atqa[1] == 0x04)) { - if ((sak == 0x08 || sak == 0x88)) { - return MifareClassicType.m1k; - } else if ((sak == 0x38)) { - return MifareClassicType.m4k; - } else if (sak == 0x09) { - return MifareClassicType.mini; - } - } else if ((atqa[1] == 0x01) && (atqa[0] == 0x0F) && (sak == 0x01)) { - //skylanders support - return MifareClassicType.m1k; - } else if (((atqa[1] == 0x42 || atqa[1] == 0x02) && (sak == 0x18)) || - ((atqa[1] == 0x02 || atqa[1] == 0x08) && (sak == 0x38))) { +Future mfClassicGetType( + ChameleonCommunicator communicator) async { + if ((await communicator.send14ARaw(Uint8List.fromList([0x60, 255]), + checkResponseCrc: false)) + .length == + 4) { return MifareClassicType.m4k; } - return MifareClassicType.m1k; + + if ((await communicator.send14ARaw(Uint8List.fromList([0x60, 80]), + checkResponseCrc: false)) + .length == + 4) { + return MifareClassicType.m2k; + } + + if ((await communicator.send14ARaw(Uint8List.fromList([0x60, 63]), + checkResponseCrc: false)) + .length == + 4) { + return MifareClassicType.m1k; + } + + return MifareClassicType.mini; } String mfClassicGetName(MifareClassicType type) { diff --git a/chameleonultragui/lib/helpers/mifare_classic/recovery.dart b/chameleonultragui/lib/helpers/mifare_classic/recovery.dart index 7ea846d5..acccdac8 100644 --- a/chameleonultragui/lib/helpers/mifare_classic/recovery.dart +++ b/chameleonultragui/lib/helpers/mifare_classic/recovery.dart @@ -45,12 +45,11 @@ class MifareClassicRecovery { await appState.communicator!.setReaderDeviceMode(true); } - var card = await appState.communicator!.scan14443aTag(); var mifare = await appState.communicator!.detectMf1Support(); var mf1Type = MifareClassicType.none; if (mifare) { - mf1Type = mfClassicGetType(card.atqa, card.sak); + mf1Type = await mfClassicGetType(appState.communicator!); } else { appState.log!.e("Not Mifare Classic tag!"); return; @@ -86,12 +85,11 @@ class MifareClassicRecovery { await appState.communicator!.setReaderDeviceMode(true); } - var card = await appState.communicator!.scan14443aTag(); var mifare = await appState.communicator!.detectMf1Support(); var mf1Type = MifareClassicType.none; if (mifare) { - mf1Type = mfClassicGetType(card.atqa, card.sak); + mf1Type = await mfClassicGetType(appState.communicator!); } else { appState.log!.e("Not Mifare Classic tag!"); return; @@ -162,13 +160,12 @@ class MifareClassicRecovery { await appState.communicator!.setReaderDeviceMode(true); } - var card = await appState.communicator!.scan14443aTag(); var mifare = await appState.communicator!.detectMf1Support(); var mf1Type = MifareClassicType.none; error = ""; if (mifare) { - mf1Type = mfClassicGetType(card.atqa, card.sak); + mf1Type = await mfClassicGetType(appState.communicator!); } else { appState.log!.e("Not Mifare Classic tag!"); return; @@ -327,8 +324,8 @@ class MifareClassicRecovery { mfClassicGetSectorTrailerBlockBySector(sector), 0x60 + keyType, keyBytes))) { - appState.log!.i( - "Found valid key! Key ${bytesToHex(keyBytes)}"); + appState.log! + .i("Found valid key! Key ${bytesToHex(keyBytes)}"); found = true; validKeys[sector + (keyType * 40)] = keyBytes; checkMarks[sector + (keyType * 40)] = @@ -353,12 +350,11 @@ class MifareClassicRecovery { Future dumpData() async { cardData = List.generate(256, (_) => Uint8List(0)); - var card = await appState.communicator!.scan14443aTag(); var mifare = await appState.communicator!.detectMf1Support(); var mf1Type = MifareClassicType.none; if (mifare) { - mf1Type = mfClassicGetType(card.atqa, card.sak); + mf1Type = await mfClassicGetType(appState.communicator!); } else { appState.log!.e("Not Mifare Classic tag!"); return; diff --git a/chameleonultragui/lib/helpers/mifare_classic/write/base.dart b/chameleonultragui/lib/helpers/mifare_classic/write/base.dart index 8d062966..f28cbd4c 100644 --- a/chameleonultragui/lib/helpers/mifare_classic/write/base.dart +++ b/chameleonultragui/lib/helpers/mifare_classic/write/base.dart @@ -1,19 +1,31 @@ import 'dart:typed_data'; import 'package:chameleonultragui/bridge/chameleon.dart'; +import 'package:chameleonultragui/gui/component/card_recovery.dart'; +import 'package:chameleonultragui/gui/page/read_card.dart'; import 'package:chameleonultragui/helpers/general.dart'; import 'package:chameleonultragui/helpers/mifare_classic/general.dart'; import 'package:chameleonultragui/helpers/mifare_classic/recovery.dart'; import 'package:chameleonultragui/helpers/mifare_classic/write/gen1.dart'; import 'package:chameleonultragui/helpers/mifare_classic/write/gen2.dart'; +import 'package:chameleonultragui/helpers/mifare_classic/write/gen3.dart'; import 'package:chameleonultragui/helpers/write.dart'; +import 'package:chameleonultragui/main.dart'; import 'package:chameleonultragui/sharedprefsprovider.dart'; +import 'package:flutter/material.dart'; + +// Localizations +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; class BaseMifareClassicMagicCardHelper extends AbstractWriteHelper { late MifareClassicRecovery recovery; late MifareClassicType type; late bool isEV1; + HFCardInfo? hfInfo; + MifareClassicInfo? mfcInfo; + @override bool get autoDetect => true; @@ -26,6 +38,16 @@ class BaseMifareClassicMagicCardHelper extends AbstractWriteHelper { List getAvailableMethods() { return [ MifareClassicGen1WriteHelper(communicator, recovery: recovery), + MifareClassicGen2WriteHelper(communicator, recovery: recovery), + MifareClassicGen3WriteHelper(communicator, recovery: recovery) + ]; + } + + @override + List getAvailableMethodsByPriority() { + return [ + MifareClassicGen1WriteHelper(communicator, recovery: recovery), + MifareClassicGen3WriteHelper(communicator, recovery: recovery), MifareClassicGen2WriteHelper(communicator, recovery: recovery) ]; } @@ -41,12 +63,11 @@ class BaseMifareClassicMagicCardHelper extends AbstractWriteHelper { await communicator.setReaderDeviceMode(true); } - var card = await communicator.scan14443aTag(); var mifare = await communicator.detectMf1Support(); type = MifareClassicType.none; if (mifare) { - type = mfClassicGetType(card.atqa, card.sak); + type = await mfClassicGetType(communicator); } } @@ -153,7 +174,95 @@ class BaseMifareClassicMagicCardHelper extends AbstractWriteHelper { @override Future reset() async { + hfInfo = null; + mfcInfo = null; recovery = MifareClassicRecovery( appState: recovery.appState, update: recovery.update); } + + @override + Widget getWriteWidget(BuildContext context, dynamic setState) { + var localizations = AppLocalizations.of(context)!; + + Future prepareMifareClassic() async { + var appState = Provider.of(context, listen: false); + + if (!await appState.communicator!.isReaderDeviceMode()) { + await appState.communicator!.setReaderDeviceMode(true); + } + + try { + CardData card = await appState.communicator!.scan14443aTag(); + bool isMifareClassic = false; + MifareClassicType mifareClassicType = MifareClassicType.none; + + try { + isMifareClassic = await appState.communicator!.detectMf1Support(); + mifareClassicType = await mfClassicGetType(appState.communicator!); + } catch (_) {} + + bool isMifareClassicEV1 = isMifareClassic + ? (await appState.communicator! + .mf1Auth(0x45, 0x61, gMifareClassicKeys[3])) + : false; + + setState(() { + hfInfo = HFCardInfo(); + mfcInfo = MifareClassicInfo(); + }); + + if (isMifareClassic) { + setState(() { + mfcInfo!.recovery = getExtraData()[0]; + }); + } + + setState(() { + hfInfo!.uid = bytesToHexSpace(card.uid); + hfInfo!.sak = + card.sak.toRadixString(16).padLeft(2, '0').toUpperCase(); + hfInfo!.atqa = bytesToHexSpace(card.atqa); + hfInfo!.ats = (card.ats.isNotEmpty) + ? bytesToHexSpace(card.ats) + : localizations.no; + mfcInfo!.isEV1 = isMifareClassicEV1; + mfcInfo!.type = mifareClassicType; + mfcInfo!.state = (mfcInfo!.type != MifareClassicType.none) + ? MifareClassicState.checkKeys + : MifareClassicState.none; + hfInfo!.tech = isMifareClassic + ? "Mifare Classic ${mfClassicGetName(mfcInfo!.type)}${isMifareClassicEV1 ? " EV1" : ""}" + : localizations.other; + }); + } catch (_) { + setState(() { + hfInfo = HFCardInfo(); + mfcInfo = MifareClassicInfo(); + }); + + setState(() { + hfInfo!.cardExist = false; + }); + } + } + + return FutureBuilder( + future: (hfInfo != null) ? Future.value([]) : prepareMifareClassic(), + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (hfInfo != null && mfcInfo != null && mfcInfo!.recovery != null) { + return CardRecovery( + hfInfo: hfInfo!, mfcInfo: mfcInfo!, allowSave: false); + } else if (hfInfo != null && + mfcInfo != null && + mfcInfo!.type == MifareClassicType.none) { + if (hfInfo!.cardExist) { + return Text(localizations.not_mifare_classic_card); + } else { + return Text(localizations.no_card_found); + } + } else { + return const Column(children: [CircularProgressIndicator()]); + } + }); + } } diff --git a/chameleonultragui/lib/helpers/mifare_classic/write/gen1.dart b/chameleonultragui/lib/helpers/mifare_classic/write/gen1.dart index 4d70e05e..941a1579 100644 --- a/chameleonultragui/lib/helpers/mifare_classic/write/gen1.dart +++ b/chameleonultragui/lib/helpers/mifare_classic/write/gen1.dart @@ -24,10 +24,7 @@ class MifareClassicGen1WriteHelper extends BaseMifareClassicMagicCardHelper { if (data.isNotEmpty && data[0] == 0x0a) { data = await communicator.send14ARaw(Uint8List.fromList([0x43]), - appendCrc: false, - autoSelect: false, - checkResponseCrc: false, - keepRfField: true); + appendCrc: false, autoSelect: false, checkResponseCrc: false); return data.isNotEmpty && data[0] == 0x0a; } diff --git a/chameleonultragui/lib/helpers/mifare_classic/write/gen2.dart b/chameleonultragui/lib/helpers/mifare_classic/write/gen2.dart index b49dbb1d..5361b259 100644 --- a/chameleonultragui/lib/helpers/mifare_classic/write/gen2.dart +++ b/chameleonultragui/lib/helpers/mifare_classic/write/gen2.dart @@ -46,6 +46,16 @@ class MifareClassicGen2WriteHelper extends BaseMifareClassicMagicCardHelper { return true; } + Future writeBlockModifier(CardSave card, int block, Uint8List data, + {bool tryBothKeys = false, bool useGenericKey = false}) async { + try { + return writeBlock(block, data, + tryBothKeys: tryBothKeys, useGenericKey: useGenericKey); + } catch (_) { + return false; + } + } + @override Future writeBlock(int block, Uint8List data, {bool tryBothKeys = false, bool useGenericKey = false}) async { @@ -104,8 +114,9 @@ class MifareClassicGen2WriteHelper extends BaseMifareClassicMagicCardHelper { for (var sector = 0; sector < mfClassicGetSectorCount(type); sector++) { var block = mfClassicGetSectorTrailerBlockBySector(sector); if (data.length > block && data[block].isNotEmpty) { - cleanSectors[sector] = - await writeBlock(block, data[block], tryBothKeys: true); + cleanSectors[sector] = await writeBlockModifier( + card, block, data[block], + tryBothKeys: true); } } @@ -121,7 +132,7 @@ class MifareClassicGen2WriteHelper extends BaseMifareClassicMagicCardHelper { } if (data.length > blockToWrite && data[blockToWrite].isNotEmpty) { - if (!(await writeBlock(blockToWrite, data[blockToWrite], + if (!(await writeBlockModifier(card, blockToWrite, data[blockToWrite], useGenericKey: cleanSectors[sector], tryBothKeys: true) && cleanSectors[sector]) && blockToWrite != 0) { @@ -138,7 +149,7 @@ class MifareClassicGen2WriteHelper extends BaseMifareClassicMagicCardHelper { if (cleanSectors[sector] && data.length > block && data[block].isNotEmpty) { - if (!(await writeBlock(block, data[block], + if (!(await writeBlockModifier(card, block, data[block], tryBothKeys: true, useGenericKey: true))) { // how we went here? We set to default sector trailer and now we can't write to it. Probably card is lost return false; @@ -150,7 +161,12 @@ class MifareClassicGen2WriteHelper extends BaseMifareClassicMagicCardHelper { } @override - List getExtraData() { - return [recovery, failedBlocks]; + List getFailedBlocks() { + return failedBlocks; + } + + @override + bool writeWidgetSupported() { + return true; } } diff --git a/chameleonultragui/lib/helpers/mifare_classic/write/gen3.dart b/chameleonultragui/lib/helpers/mifare_classic/write/gen3.dart new file mode 100644 index 00000000..8ad98979 --- /dev/null +++ b/chameleonultragui/lib/helpers/mifare_classic/write/gen3.dart @@ -0,0 +1,85 @@ +import 'dart:typed_data'; + +import 'package:chameleonultragui/bridge/chameleon.dart'; +import 'package:chameleonultragui/helpers/general.dart'; +import 'package:chameleonultragui/helpers/mifare_classic/general.dart'; +import 'package:chameleonultragui/helpers/mifare_classic/recovery.dart'; +import 'package:chameleonultragui/helpers/mifare_classic/write/gen2.dart'; +import 'package:chameleonultragui/sharedprefsprovider.dart'; + +class MifareClassicGen3WriteHelper extends MifareClassicGen2WriteHelper { + MifareClassicGen3WriteHelper(super.communicator, {required super.recovery}); + + @override + String get name => "Gen3"; + + static String get staticName => "Gen3"; + + @override + Future isMagic(dynamic data) async { + try { + if (!await communicator.detectMf1Support()) { + return false; // not even Mifare Classic + } + + Uint8List response = await communicator.send14ARaw( + Uint8List.fromList([0x30, 0x00]), + checkResponseCrc: false); + + return response.length == 18; // 16 + 2 byte CRC + } catch (_) { + return false; + } + } + + @override + bool isReady() { + for (var sector = 0; + sector < mfClassicGetSectorCount(type, isEV1: isEV1); + sector++) { + for (var keyType = 0; keyType < 2; keyType++) { + if (recovery.checkMarks[sector + (keyType * 40)] != + ChameleonKeyCheckmark.found) { + return false; + } + } + } + + return true; + } + + @override + Future writeBlockModifier(CardSave card, int block, Uint8List data, + {bool tryBothKeys = false, bool useGenericKey = false}) async { + try { + if (block == 0) { + return writeGen3Block(card, data); + } else { + return writeBlock(block, data, + tryBothKeys: tryBothKeys, useGenericKey: useGenericKey); + } + } catch (_) { + return false; + } + } + + Future writeGen3Block(CardSave dump, Uint8List data) async { + CardData card = await communicator.scan14443aTag(); + // Try to write whole block + await communicator.send14ARaw( + Uint8List.fromList([0x90, 0xFB, 0xCC, 0xCC, 0x10, ...data]), + checkResponseCrc: false); + + // Try to write UID only + await communicator.send14ARaw( + Uint8List.fromList( + [0x90, 0xFB, 0xCC, 0xCC, 0x07, ...hexToBytesSpace(dump.uid)]), + checkResponseCrc: false); + + // Card doesn't respond with anything, just compare UID + card = await communicator.scan14443aTag(); + return bytesToHex(card.uid) == + bytesToHex(data.sublist(0, card.uid.length)) || + bytesToHexSpace(card.uid) == dump.uid; + } +} diff --git a/chameleonultragui/lib/helpers/write.dart b/chameleonultragui/lib/helpers/write.dart index cde9dccf..4c9ae958 100644 --- a/chameleonultragui/lib/helpers/write.dart +++ b/chameleonultragui/lib/helpers/write.dart @@ -4,6 +4,7 @@ import 'package:chameleonultragui/helpers/mifare_classic/recovery.dart'; import 'package:chameleonultragui/helpers/mifare_classic/write/base.dart'; import 'package:chameleonultragui/main.dart'; import 'package:chameleonultragui/sharedprefsprovider.dart'; +import 'package:flutter/material.dart'; abstract class AbstractWriteHelper { final ChameleonCommunicator communicator; @@ -24,8 +25,10 @@ abstract class AbstractWriteHelper { Future isCompatible( CardSave card); // is current magic card compatible with selected dump + List getAvailableMethods(); // get available methods + List - getAvailableMethods(); // get available methods for automatic check with priority + getAvailableMethodsByPriority(); // get available methods for automatic check with priority Future getCardType(); // get required data from card @@ -45,6 +48,16 @@ abstract class AbstractWriteHelper { Future writeData(CardSave card, dynamic update); + Widget getWriteWidget(BuildContext context, dynamic setState); + + List getFailedBlocks() { + return []; + } + + bool writeWidgetSupported() { + return false; + } + @override bool operator ==(dynamic other) => other != null && name == other.name; } diff --git a/chameleonultragui/macos/Flutter/GeneratedPluginRegistrant.swift b/chameleonultragui/macos/Flutter/GeneratedPluginRegistrant.swift index 344ed28b..1a358946 100644 --- a/chameleonultragui/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/chameleonultragui/macos/Flutter/GeneratedPluginRegistrant.swift @@ -18,7 +18,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FileSaverPlugin.register(with: registry.registrar(forPlugin: "FileSaverPlugin")) FlutterLibserialportPlugin.register(with: registry.registrar(forPlugin: "FlutterLibserialportPlugin")) MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin")) - FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) + FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) diff --git a/chameleonultragui/pubspec.lock b/chameleonultragui/pubspec.lock index c5c0bc58..f5273a76 100644 --- a/chameleonultragui/pubspec.lock +++ b/chameleonultragui/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: "direct main" description: name: archive - sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b" + sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d" url: "https://pub.dev" source: hosted - version: "3.4.9" + version: "3.4.10" args: dependency: transitive description: @@ -53,10 +53,10 @@ packages: dependency: transitive description: name: cli_util - sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 + sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 url: "https://pub.dev" source: hosted - version: "0.4.0" + version: "0.4.1" clock: dependency: transitive description: @@ -125,10 +125,10 @@ packages: dependency: "direct main" description: name: ffigen - sha256: "0a4e9c5437c5c4600fa583141508beaf90fe29b58b0fd7437a9b022cda228747" + sha256: dead012f29db2be71ea152458f5eab600de98fbc244e01088ae6bf2616bceca7 url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "11.0.0" file: dependency: transitive description: @@ -149,10 +149,10 @@ packages: dependency: "direct main" description: name: file_saver - sha256: "591d25e750e3a4b654f7b0293abc2ed857242f82ca7334051b2a8ceeb369dac8" + sha256: "8ffd91ae9f543c5ebbfec71a814ee5aa9e21176d31335133308abf63f4c42e8a" url: "https://pub.dev" source: hosted - version: "0.2.8" + version: "0.2.9" fixnum: dependency: transitive description: @@ -215,10 +215,10 @@ packages: dependency: "direct main" description: name: flutter_reactive_ble - sha256: "7a0d245412dc8e1b72ce2adc423808583b42ce824b1be74001ff22c8bb5ada48" + sha256: "247e2efa76de203d1ba11335c13754b5b9d0504b5423e5b0c93a600f016b24e0" url: "https://pub.dev" source: hosted - version: "5.2.0" + version: "5.3.1" flutter_staggered_grid_view: dependency: "direct main" description: @@ -257,10 +257,10 @@ packages: dependency: "direct main" description: name: http - sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 + sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.2.0" http_parser: dependency: transitive description: @@ -273,10 +273,10 @@ packages: dependency: transitive description: name: image - sha256: "028f61960d56f26414eb616b48b04eb37d700cbe477b7fb09bf1d7ce57fd9271" + sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e" url: "https://pub.dev" source: hosted - version: "4.1.3" + version: "4.1.7" intl: dependency: "direct main" description: @@ -361,10 +361,10 @@ packages: dependency: "direct main" description: name: mobile_scanner - sha256: c3e5bba1cb626b6ab4fc46610f72a136803f6854267967e19f4a4a6a31ff9b74 + sha256: "619ed5fd43ca9007a151f00c3dc43feedeaf235fe5647735d0237c38849d49dc" url: "https://pub.dev" source: hosted - version: "3.5.5" + version: "4.0.0" nested: dependency: transitive description: @@ -409,26 +409,26 @@ packages: dependency: transitive description: name: path_provider - sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa + sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 + sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.2" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" path_provider_linux: dependency: transitive description: @@ -441,10 +441,10 @@ packages: dependency: transitive description: name: path_provider_platform_interface - sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" path_provider_windows: dependency: transitive description: @@ -457,50 +457,50 @@ packages: dependency: "direct main" description: name: permission_handler - sha256: "860c6b871c94c78e202dc69546d4d8fd84bd59faeb36f8fb9888668a53ff4f78" + sha256: "74e962b7fad7ff75959161bb2c0ad8fe7f2568ee82621c9c2660b751146bfe44" url: "https://pub.dev" source: hosted - version: "11.1.0" + version: "11.3.0" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: "2f1bec180ee2f5665c22faada971a8f024761f632e93ddc23310487df52dcfa6" + sha256: "1acac6bae58144b442f11e66621c062aead9c99841093c38f5bcdcc24c1c3474" url: "https://pub.dev" source: hosted - version: "12.0.1" + version: "12.0.5" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: "1a816084338ada8d574b1cb48390e6e8b19305d5120fe3a37c98825bacc78306" + sha256: bdafc6db74253abb63907f4e357302e6bb786ab41465e8635f362ee71fd8707b url: "https://pub.dev" source: hosted - version: "9.2.0" + version: "9.4.0" permission_handler_html: dependency: transitive description: name: permission_handler_html - sha256: "11b762a8c123dced6461933a88ea1edbbe036078c3f9f41b08886e678e7864df" + sha256: "54bf176b90f6eddd4ece307e2c06cf977fb3973719c35a93b85cc7093eb6070d" url: "https://pub.dev" source: hosted - version: "0.1.0+2" + version: "0.1.1" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - sha256: d87349312f7eaf6ce0adaf668daf700ac5b06af84338bd8b8574dfbd93ffe1a1 + sha256: "23dfba8447c076ab5be3dee9ceb66aad345c4a648f0cac292c77b1eb0e800b78" url: "https://pub.dev" source: hosted - version: "4.0.2" + version: "4.2.0" permission_handler_windows: dependency: transitive description: name: permission_handler_windows - sha256: "1e8640c1e39121128da6b816d236e714d2cf17fac5a105dd6acdd3403a628004" + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.2.1" petitparser: dependency: transitive description: @@ -513,26 +513,26 @@ packages: dependency: transitive description: name: platform - sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59" + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "3.1.4" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" url: "https://pub.dev" source: hosted - version: "2.1.7" + version: "2.1.8" pointycastle: dependency: transitive description: name: pointycastle - sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" + sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29" url: "https://pub.dev" source: hosted - version: "3.7.3" + version: "3.7.4" protobuf: dependency: "direct main" description: @@ -577,18 +577,18 @@ packages: dependency: transitive description: name: reactive_ble_mobile - sha256: e4623446d5fd6e641c984892ee1fa7c67499a2bb0971d85a500815e1d05db6fb + sha256: "9ec2b4c9c725e439950838d551579750060258fbccd5536d0543b4d07d225798" url: "https://pub.dev" source: hosted - version: "5.2.0" + version: "5.3.1" reactive_ble_platform_interface: dependency: transitive description: name: reactive_ble_platform_interface - sha256: "8988d16497886dccc69dca1c3eebce28ae387371f3f948a4f1b03dec9954fb05" + sha256: "632c92401a2d69c9b94bd48f8fd47488a7013f3d1f9b291884350291a4a81813" url: "https://pub.dev" source: hosted - version: "5.2.0" + version: "5.3.1" shared_preferences: dependency: "direct main" description: @@ -609,10 +609,10 @@ packages: dependency: transitive description: name: shared_preferences_foundation - sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7" + sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c" url: "https://pub.dev" source: hosted - version: "2.3.4" + version: "2.3.5" shared_preferences_linux: dependency: transitive description: @@ -625,10 +625,10 @@ packages: dependency: transitive description: name: shared_preferences_platform_interface - sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a + sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" shared_preferences_web: dependency: transitive description: @@ -718,26 +718,26 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: e9aa5ea75c84cf46b3db4eea212523591211c3cf2e13099ee4ec147f54201c86 + sha256: c512655380d241a337521703af62d2c122bf7b77a46ff7dd750092aa9433499c url: "https://pub.dev" source: hosted - version: "6.2.2" + version: "6.2.4" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def" + sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745 url: "https://pub.dev" source: hosted - version: "6.2.0" + version: "6.3.0" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3 + sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03" url: "https://pub.dev" source: hosted - version: "6.2.1" + version: "6.2.4" url_launcher_linux: dependency: transitive description: @@ -758,18 +758,18 @@ packages: dependency: transitive description: name: url_launcher_platform_interface - sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50" + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.2" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "7286aec002c8feecc338cc33269e96b73955ab227456e9fb2a91f7fab8a358e9" + sha256: fff0932192afeedf63cdd50ecbb1bc825d31aed259f02bb8dba0f3b729a5e88b url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.3" url_launcher_windows: dependency: transitive description: @@ -791,10 +791,10 @@ packages: dependency: "direct main" description: name: uuid - sha256: "22c94e5ad1e75f9934b766b53c742572ee2677c56bc871d850a57dad0f82127f" + sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8 url: "https://pub.dev" source: hosted - version: "4.2.2" + version: "4.3.3" vector_math: dependency: transitive description: @@ -831,18 +831,18 @@ packages: dependency: transitive description: name: win32 - sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574 + sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "5.2.0" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" xml: dependency: transitive description: diff --git a/chameleonultragui/pubspec.yaml b/chameleonultragui/pubspec.yaml index c128339c..823a0f39 100644 --- a/chameleonultragui/pubspec.yaml +++ b/chameleonultragui/pubspec.yaml @@ -38,7 +38,7 @@ dependencies: logger: ^2.0.1 convert: ^3.1.1 shared_preferences: ^2.2.1 - ffigen: ^10.0.0 + ffigen: ^11.0.0 dylib: ^0.3.3 ffi: ^2.1.0 usb_serial: @@ -64,7 +64,7 @@ dependencies: intl: any wakelock_plus: ^1.1.1 qr_flutter: ^4.1.0 - mobile_scanner: ^3.5.2 + mobile_scanner: ^4.0.0 dev_dependencies: flutter_test: