Skip to content

Commit

Permalink
Gen3 write support and don't rely on SAK/ATQA to determine the number…
Browse files Browse the repository at this point in the history
… of blocks (#453)

* Don't rely on SAK/ATQA to determine the number of blocks
* Refactor write page
* Gen3 write support
  • Loading branch information
Foxushka authored Feb 17, 2024
1 parent 82e36f7 commit f09dc07
Show file tree
Hide file tree
Showing 14 changed files with 559 additions and 428 deletions.
4 changes: 2 additions & 2 deletions chameleonultragui/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ if (keystorePropertiesFile.exists()) {
}

android {
compileSdkVersion flutter.compileSdkVersion
compileSdkVersion Math.max(flutter.compileSdkVersion, 34)
ndkVersion flutter.ndkVersion

compileOptions {
Expand All @@ -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
}
Expand Down
412 changes: 210 additions & 202 deletions chameleonultragui/lib/gui/component/card_recovery.dart

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions chameleonultragui/lib/gui/page/read_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,11 @@ class ReadCardPageState extends State<ReadCardPage> {

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
Expand All @@ -132,9 +134,7 @@ class ReadCardPageState extends State<ReadCardPage> {
? 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;
Expand Down
128 changes: 14 additions & 114 deletions chameleonultragui/lib/gui/page/write_card.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -23,8 +19,6 @@ class WriteCardPage extends StatefulWidget {
}

class WriteCardPageState extends State<WriteCardPage> {
HFCardInfo? hfInfo;
MifareClassicInfo? mfcInfo;
int step = 0;
int progress = -1;
bool written = false;
Expand Down Expand Up @@ -59,6 +53,9 @@ class WriteCardPageState extends State<WriteCardPage> {
});
}

await helper?.getCardType();

if (!context.mounted) return;
close(context, selectedCard.name);
}

Expand All @@ -77,6 +74,12 @@ class WriteCardPageState extends State<WriteCardPage> {
helper = magicHelper;
});

try {
await helper?.getCardType();
} catch (_) {
await helper?.getCardType();
}

appState.log!.i("Detected Magic card type: ${magicHelper.name}");
scaffoldMessenger.hideCurrentSnackBar();
var snackBar = SnackBar(
Expand Down Expand Up @@ -104,71 +107,9 @@ class WriteCardPageState extends State<WriteCardPage> {
scaffoldMessenger.showSnackBar(snackBar);
}

Future<void> prepareMifareClassic() async {
var appState = Provider.of<ChameleonGUIState>(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;
});
}

Expand Down Expand Up @@ -239,11 +180,6 @@ class WriteCardPageState extends State<WriteCardPage> {
if (step != 2) {
if (step == 1) {
await helper?.reset();

setState(() {
hfInfo = null;
mfcInfo = null;
});
}
setState(() {
step++;
Expand Down Expand Up @@ -281,11 +217,6 @@ class WriteCardPageState extends State<WriteCardPage> {

if (step == 1) {
await helper?.reset();

setState(() {
hfInfo = null;
mfcInfo = null;
});
}
}

Expand Down Expand Up @@ -445,44 +376,13 @@ class WriteCardPageState extends State<WriteCardPage> {
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),
),
Expand Down
37 changes: 22 additions & 15 deletions chameleonultragui/lib/helpers/mifare_classic/general.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<MifareClassicType> 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) {
Expand Down
16 changes: 6 additions & 10 deletions chameleonultragui/lib/helpers/mifare_classic/recovery.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)] =
Expand All @@ -353,12 +350,11 @@ class MifareClassicRecovery {
Future<void> 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;
Expand Down
Loading

0 comments on commit f09dc07

Please sign in to comment.