Skip to content

Commit

Permalink
feat: NTAG/Ultralight writing support + bug fixes/improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
Foxushka committed Sep 10, 2024
1 parent 093a90c commit 3639eb0
Show file tree
Hide file tree
Showing 12 changed files with 241 additions and 30 deletions.
6 changes: 1 addition & 5 deletions chameleonultragui/lib/gui/component/card_list.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'package:chameleonultragui/bridge/chameleon.dart';
import 'package:chameleonultragui/helpers/general.dart';
import 'package:chameleonultragui/helpers/mifare_classic/general.dart';
import 'package:chameleonultragui/sharedprefsprovider.dart';
import 'package:flutter/material.dart';

Expand Down Expand Up @@ -157,10 +156,7 @@ class CardSearchDelegate extends SearchDelegate<String> {
color: card.color),
title: Text(card.name),
subtitle: Text(
chameleonTagToString(card.tag) +
((chameleonTagSaveCheckForMifareClassicEV1(card))
? " EV1"
: ""),
chameleonCardToString(card),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
Expand Down
6 changes: 1 addition & 5 deletions chameleonultragui/lib/gui/page/saved_cards.dart
Original file line number Diff line number Diff line change
Expand Up @@ -478,11 +478,7 @@ class SavedCardsPageState extends State<SavedCardsPage> {
: Icons.wifi,
iconColor: tag.color,
firstLine: tag.name.isEmpty ? "⠀" : tag.name,
secondLine: chameleonTagToString(tag.tag) +
((chameleonTagSaveCheckForMifareClassicEV1(
tag))
? " EV1"
: ""),
secondLine: chameleonCardToString(tag),
itemIndex: index,
onPressed: () {
showDialog(
Expand Down
9 changes: 8 additions & 1 deletion chameleonultragui/lib/gui/page/write_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,12 @@ class WriteCardPageState extends State<WriteCardPage> {
@override
Widget build(BuildContext context) {
var localizations = AppLocalizations.of(context)!;
var typeLocalization = {
'gen1': localizations.gen1,
'gen2': localizations.gen2,
'gen3': localizations.gen3,
't55xx': localizations.t55xx,
};

return Scaffold(
appBar: AppBar(
Expand Down Expand Up @@ -345,7 +351,8 @@ class WriteCardPageState extends State<WriteCardPage> {
(AbstractWriteHelper helperClass) {
return DropdownMenuItem<AbstractWriteHelper>(
value: helperClass,
child: Text(helperClass.name),
child:
Text(typeLocalization[helperClass.name]!),
);
}).toList(),
onChanged: (AbstractWriteHelper? helperClass) {
Expand Down
11 changes: 11 additions & 0 deletions chameleonultragui/lib/helpers/general.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:io';
import 'dart:typed_data';
import 'package:chameleonultragui/bridge/chameleon.dart';
import 'package:chameleonultragui/connector/serial_abstract.dart';
import 'package:chameleonultragui/helpers/mifare_classic/general.dart';
import 'package:chameleonultragui/main.dart';
import 'package:chameleonultragui/sharedprefsprovider.dart';
import 'package:file_picker/file_picker.dart';
Expand Down Expand Up @@ -140,6 +141,16 @@ String chameleonTagToString(TagType tag) {
}
}

String chameleonCardToString(CardSave card) {
String name = chameleonTagToString(card.tag);

if (chameleonTagSaveCheckForMifareClassicEV1(card)) {
name += " EV1";
}

return name;
}

TagType numberToChameleonTag(int type) {
if (type == TagType.mifareMini.value) {
return TagType.mifareMini;
Expand Down
19 changes: 16 additions & 3 deletions chameleonultragui/lib/helpers/mifare_classic/write/base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';

class BaseMifareClassicMagicCardHelper extends AbstractWriteHelper {
class BaseMifareClassicWriteHelper extends AbstractWriteHelper {
late MifareClassicRecovery recovery;
late MifareClassicType type;
late bool isEV1;
Expand All @@ -29,7 +29,7 @@ class BaseMifareClassicMagicCardHelper extends AbstractWriteHelper {
@override
bool get autoDetect => true;

BaseMifareClassicMagicCardHelper(super.communicator,
BaseMifareClassicWriteHelper(super.communicator,
{required this.recovery,
this.type = MifareClassicType.m1k,
this.isEV1 = false});
Expand Down Expand Up @@ -92,9 +92,16 @@ class BaseMifareClassicMagicCardHelper extends AbstractWriteHelper {
}

@override
Future<bool> writeData(CardSave card, dynamic update) async {
Future<bool> writeData(
CardSave card, Function(int writeProgress) update) async {
List<Uint8List> data = card.data;

try {
await communicator.scan14443aTag();
} catch (e) {
return false;
}

if (data.isEmpty || data[0].isEmpty) {
if (data.isEmpty) {
data = [Uint8List(0)];
Expand Down Expand Up @@ -215,6 +222,12 @@ class BaseMifareClassicMagicCardHelper extends AbstractWriteHelper {
setState(() {
mfcInfo!.recovery = getExtraData()[0];
});
} else {
setState(() {
hfInfo!.cardExist = false;
});

return;
}

setState(() {
Expand Down
6 changes: 3 additions & 3 deletions chameleonultragui/lib/helpers/mifare_classic/write/gen1.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import 'dart:typed_data';

import 'package:chameleonultragui/helpers/mifare_classic/write/base.dart';

class MifareClassicGen1WriteHelper extends BaseMifareClassicMagicCardHelper {
class MifareClassicGen1WriteHelper extends BaseMifareClassicWriteHelper {
MifareClassicGen1WriteHelper(super.communicator, {required super.recovery});

@override
String get name => "Gen1";
String get name => "gen1";

static String get staticName => "Gen1";
static String get staticName => "gen1";

@override
Future<bool> isMagic(dynamic data) async {
Expand Down
15 changes: 11 additions & 4 deletions chameleonultragui/lib/helpers/mifare_classic/write/gen2.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import 'package:chameleonultragui/helpers/mifare_classic/recovery.dart';
import 'package:chameleonultragui/helpers/mifare_classic/write/base.dart';
import 'package:chameleonultragui/sharedprefsprovider.dart';

class MifareClassicGen2WriteHelper extends BaseMifareClassicMagicCardHelper {
class MifareClassicGen2WriteHelper extends BaseMifareClassicWriteHelper {
List<int> failedBlocks = [];
MifareClassicGen2WriteHelper(super.communicator, {required super.recovery});

@override
String get name => "Gen2 / Generic";
String get name => "gen2";

static String get staticName => "Gen2 / Generic";
static String get staticName => "gen2";

@override
Future<bool> isMagic(dynamic data) async {
Expand Down Expand Up @@ -99,11 +99,18 @@ class MifareClassicGen2WriteHelper extends BaseMifareClassicMagicCardHelper {
}

@override
Future<bool> writeData(CardSave card, dynamic update) async {
Future<bool> writeData(
CardSave card, Function(int writeProgress) update) async {
List<Uint8List> data = card.data;
List<bool> cleanSectors = List.generate(40, (index) => false);
failedBlocks = [];

try {
await communicator.scan14443aTag();
} catch (e) {
return false;
}

if (data.isEmpty || data[0].isEmpty) {
if (data.isEmpty) {
data = [Uint8List(0)];
Expand Down
4 changes: 2 additions & 2 deletions chameleonultragui/lib/helpers/mifare_classic/write/gen3.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ class MifareClassicGen3WriteHelper extends MifareClassicGen2WriteHelper {
MifareClassicGen3WriteHelper(super.communicator, {required super.recovery});

@override
String get name => "Gen3";
String get name => "gen3";

static String get staticName => "Gen3";
static String get staticName => "gen3";

@override
Future<bool> isMagic(dynamic data) async {
Expand Down
169 changes: 169 additions & 0 deletions chameleonultragui/lib/helpers/mifare_ultralight/write/base.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import 'dart:typed_data';

import 'package:chameleonultragui/gui/page/read_card.dart';
import 'package:chameleonultragui/helpers/general.dart';
import 'package:chameleonultragui/helpers/write.dart';
import 'package:chameleonultragui/sharedprefsprovider.dart';
import 'package:flutter/material.dart';

// Localizations
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class BaseMifareUltralightWriteHelper extends AbstractWriteHelper {
HFCardInfo? hfInfo;
List<int> failedBlocks = [];

@override
bool get autoDetect => false;

@override
String get name => "gen2";

static String get staticName => "gen2";
TextEditingController keyController = TextEditingController();
String? key;

BaseMifareUltralightWriteHelper(super.communicator);

@override
List<AbstractWriteHelper> getAvailableMethods() {
return [
BaseMifareUltralightWriteHelper(communicator),
];
}

@override
List<AbstractWriteHelper> getAvailableMethodsByPriority() {
return [BaseMifareUltralightWriteHelper(communicator)];
}

@override
Widget getWriteWidget(BuildContext context, setState) {
var localizations = AppLocalizations.of(context)!;
final GlobalKey<FormState> formKey = GlobalKey<FormState>();

return Row(children: [
Form(
key: formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Expanded(
child: Column(
children: [
TextFormField(
controller: keyController,
decoration: InputDecoration(
labelText: localizations.key,
hintMaxLines: 4,
hintText: localizations
.enter_something(localizations.ultralight_key_prompt)),
validator: (String? value) {
if (value!.isNotEmpty && !isValidHexString(value)) {
return localizations.must_be_valid_hex;
}

if (value.length != 8) {
return localizations.must_be(4, localizations.key);
}

return null;
},
)
],
))),
TextButton(
onPressed: () => {
setState(() {
key = keyController.text;
})
},
child: Text(localizations.next),
),
TextButton(
onPressed: () => {
setState(() {
key = "";
})
},
child: Text(localizations.no_key),
)
]);
}

@override
Future<bool> isCompatible(CardSave card) async {
return true;
}

@override
Future<bool> isMagic(data) async {
return false;
}

@override
bool isReady() {
return key != null;
}

@override
bool writeWidgetSupported() {
return true;
}

@override
Future<void> reset() async {
failedBlocks = [];
key = null;
}

@override
Future<bool> writeData(
CardSave card, Function(int writeProgress) update) async {
int totalBlocks = card.data.length;

try {
await communicator.scan14443aTag();
} catch (e) {
return false;
}

if (key!.isNotEmpty) {
Uint8List pack = await communicator.send14ARaw(
Uint8List.fromList([0x1B, ...hexToBytes(key!)]),
keepRfField: true);
if (pack.length < 2) {
return false;
}
}

for (var block = 0; block < totalBlocks; block++) {
Uint8List write = await communicator.send14ARaw(
Uint8List.fromList([0xA2, block, ...card.data[block]]),
keepRfField: true,
checkResponseCrc: false,
autoSelect: block == 0 || block == 3);
if (write.isEmpty || write[0] != 0x0A || block == 2) {
await communicator.send14ARaw(Uint8List(1)); // reset

if (key!.isNotEmpty) {
await communicator.send14ARaw(
Uint8List.fromList([0x1B, ...hexToBytes(key!)]),
keepRfField: true);
}

if (block > 2) {
// block is not UID
failedBlocks.add(block);
}
}

update((block / totalBlocks * 100).round());
}

return failedBlocks.isEmpty;
}

@override
List<int> getFailedBlocks() {
return failedBlocks;
}
}
7 changes: 4 additions & 3 deletions chameleonultragui/lib/helpers/t55xx/write/base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ class BaseT55XXCardHelper extends AbstractWriteHelper {
bool get autoDetect => true;

@override
String get name => "T55XX";
String get name => "t55xx";

static String get staticName => "T55XX";
static String get staticName => "t55xx";
TextEditingController newKeyController = TextEditingController();
TextEditingController currentKeyController = TextEditingController();
String currentKey = "";
Expand Down Expand Up @@ -157,7 +157,8 @@ class BaseT55XXCardHelper extends AbstractWriteHelper {
}

@override
Future<bool> writeData(CardSave card, update) async {
Future<bool> writeData(
CardSave card, Function(int writeProgress) update) async {
await communicator.writeEM410XtoT55XX(
hexToBytes(card.uid), hexToBytes(newKey), [hexToBytes(currentKey)]);
var newCard = await communicator.readEM410X();
Expand Down
Loading

0 comments on commit 3639eb0

Please sign in to comment.