Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions chameleonultragui/lib/bridge/chameleon.dart
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,16 @@ class ChameleonCommunicator {
return IoProxCard.fromBytes(resp.data);
}

Future<JablotronCard?> readJablotron() async {
var resp = await sendCmd(ChameleonCommand.scanJablotronTag);

if (resp!.data.isEmpty) {
return null;
}

return JablotronCard.fromBytes(resp.data);
}

Future<void> setEM410XEmulatorID(Uint8List uid) async {
await sendCmd(ChameleonCommand.setEM410XemulatorID, data: uid);
}
Expand All @@ -611,6 +621,10 @@ class ChameleonCommunicator {
await sendCmd(ChameleonCommand.setIoProxEmulatorID, data: uid);
}

Future<void> setJablotronEmulatorID(Uint8List uid) async {
await sendCmd(ChameleonCommand.setJablotronEmulatorID, data: uid);
}

Future<void> setIdteckEmulatorID(Uint8List uid) async {
await sendCmd(ChameleonCommand.setIdteckEmulatorID, data: uid);
}
Expand Down Expand Up @@ -663,6 +677,20 @@ class ChameleonCommunicator {
data: Uint8List.fromList([...uid, ...newKey, ...keys]));
}

Future<void> writeJablotronToT55XX(
Uint8List uid, Uint8List newKey, List<Uint8List> oldKeys) async {
List<int> keys = [];

keys.addAll(newKey);

for (var oldKey in oldKeys) {
keys.addAll(oldKey);
}

await sendCmd(ChameleonCommand.writeJablotronToT5577,
data: Uint8List.fromList([...uid, ...newKey, ...keys]));
}

Future<void> writePacToT55XX(
Uint8List uid, Uint8List newKey, List<Uint8List> oldKeys) async {
List<int> keys = [];
Expand Down Expand Up @@ -1097,6 +1125,11 @@ class ChameleonCommunicator {
(await sendCmd(ChameleonCommand.getIoProxEmulatorID))!.data);
}

Future<JablotronCard> getJablotronEmulatorID() async {
return JablotronCard.fromBytes(
(await sendCmd(ChameleonCommand.getJablotronEmulatorID))!.data);
}

Future<IdteckCard> getIdteckEmulatorID() async {
return IdteckCard.fromBytes(
(await sendCmd(ChameleonCommand.getIdteckEmulatorID))!.data);
Expand Down
9 changes: 9 additions & 0 deletions chameleonultragui/lib/gui/menu/dialogs/slot/edit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ class SlotEditMenuState extends State<SlotEditMenu> {
await appState.communicator!.getVikingEmulatorID();
uidController.text = bytesToHexSpace(vikingCard.uid);
} catch (_) {}
} else if (selectedType! == TagType.jablotron) {
try {
JablotronCard jablotronCard =
await appState.communicator!.getJablotronEmulatorID();
uidController.text = bytesToHexSpace(jablotronCard.uid);
} catch (_) {}
} else if (selectedType! == TagType.pac) {
try {
PacCard pacCard = await appState.communicator!.getPacEmulatorID();
Expand Down Expand Up @@ -224,6 +230,9 @@ class SlotEditMenuState extends State<SlotEditMenu> {
await appState.communicator!
.setHIDProxEmulatorID(hexToBytes(hidCard.toString()));
} catch (_) {}
} else if (selectedType! == TagType.jablotron) {
await appState.communicator!.setJablotronEmulatorID(
hexToBytes(uidController.text.replaceAll(' ', '')));
} else if (selectedType! == TagType.pac) {
await appState.communicator!.setPacEmulatorID(
hexToBytes(uidController.text.replaceAll(' ', '')));
Expand Down
7 changes: 7 additions & 0 deletions chameleonultragui/lib/gui/menu/dialogs/slot/export.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ class SlotExportMenuState extends State<SlotExportMenu> {
name: widget.names.lf,
tag: widget.slotTypes.lf,
);
} else if (widget.slotTypes.lf == TagType.jablotron) {
return CardSave(
uid: (await appState.communicator!.getJablotronEmulatorID())
.toString(),
name: widget.names.lf,
tag: widget.slotTypes.lf,
);
} else if (widget.slotTypes.lf == TagType.pac) {
return CardSave(
uid: (await appState.communicator!.getPacEmulatorID()).toString(),
Expand Down
1 change: 1 addition & 0 deletions chameleonultragui/lib/gui/page/read_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class ReadCardPageState extends State<ReadCardPage> {
card ??= await appState.communicator!.readViking();
card ??= await appState.communicator!.readPac();
card ??= await appState.communicator!.readIoProx();
card ??= await appState.communicator!.readJablotron();


if (card != null) {
Expand Down
16 changes: 16 additions & 0 deletions chameleonultragui/lib/gui/page/slot_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,22 @@ class SlotManagerPageState extends State<SlotManagerPage> {
await appState.communicator!.saveSlotData();
appState.changesMade();
refreshSlot();
} else if (card.tag == TagType.jablotron) {
close(context, card.name);
await appState.communicator!.setReaderDeviceMode(false);
await appState.communicator!
.enableSlot(gridPosition, TagFrequency.lf, true);
await appState.communicator!.activateSlot(gridPosition);
await appState.communicator!.setSlotType(gridPosition, card.tag);
await appState.communicator!.setDefaultDataToSlot(gridPosition, card.tag);
await appState.communicator!.setJablotronEmulatorID(hexToBytes(card.uid));
await appState.communicator!.setSlotTagName(
gridPosition,
(card.name.isEmpty) ? localizations.no_name : card.name,
TagFrequency.lf);
await appState.communicator!.saveSlotData();
appState.changesMade();
refreshSlot();
} else if (card.tag == TagType.pac) {
close(context, card.name);
await appState.communicator!.setReaderDeviceMode(false);
Expand Down
26 changes: 26 additions & 0 deletions chameleonultragui/lib/helpers/definitions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ enum ChameleonCommand {
scanPacTag(3014),
writePacToT5577(3015),
writeIdteckToT5577(3018),
scanJablotronTag(3019),
writeJablotronToT5577(3020),
lfSniff(3031),

mf1LoadBlockData(4000),
Expand Down Expand Up @@ -155,6 +157,9 @@ enum ChameleonCommand {
setIoProxEmulatorID(5008),
getIoProxEmulatorID(5009),

setJablotronEmulatorID(5010),
getJablotronEmulatorID(5011),

setIdteckEmulatorID(5012),
getIdteckEmulatorID(5013);

Expand All @@ -171,6 +176,7 @@ enum TagType {
em410XElectra(104),
pac(150),
viking(170),
jablotron(180),
hidProx(200),
ioProx(201),
idteck(310),
Expand Down Expand Up @@ -611,6 +617,26 @@ class VikingCard extends LFCard {
});
}

class JablotronCard extends LFCard {
factory JablotronCard.fromBytes(Uint8List bytes) {
return JablotronCard(uid: bytes);
}

factory JablotronCard.fromUID(String uid) {
return JablotronCard.fromBytes(hexToBytes(uid));
}

@override
String toViewableString() {
return '${jablotronCardId(uid)} (${bytesToHexSpace(uid)})';
}

JablotronCard({
super.type = TagType.jablotron,
required super.uid,
});
}

class PacCard extends LFCard {
factory PacCard.fromBytes(Uint8List bytes) {
return PacCard(uid: bytes);
Expand Down
18 changes: 18 additions & 0 deletions chameleonultragui/lib/helpers/general.dart
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ String chameleonTagToString(TagType tag, AppLocalizations localizations) {
return "HID Prox";
} else if (tag == TagType.viking) {
return "Viking";
} else if (tag == TagType.jablotron) {
return "Jablotron";
} else if (tag == TagType.pac) {
return "PAC/Stanley";
} else if (tag == TagType.ioProx) {
Expand Down Expand Up @@ -455,6 +457,7 @@ List<TagType> getTagTypesByFrequency(TagFrequency frequency) {
TagType.em410XElectra,
TagType.hidProx,
TagType.viking,
TagType.jablotron,
TagType.pac,
TagType.ioProx,
TagType.idteck
Expand Down Expand Up @@ -553,6 +556,10 @@ LFCard getLFCardFromUID(TagType type, String uid) {
return VikingCard.fromUID(uid);
}

if (type == TagType.jablotron) {
return JablotronCard.fromUID(uid);
}

if (type == TagType.pac) {
return PacCard.fromUID(uid);
}
Expand All @@ -577,6 +584,8 @@ int uidSizeForLfTag(TagType type) {
return 5;
} else if (type == TagType.viking) {
return 4;
} else if (type == TagType.jablotron) {
return 5;
} else if (type == TagType.pac) {
return 8;
} else if (type == TagType.ioProx) {
Expand All @@ -598,6 +607,15 @@ bool isEM410X(TagType type) {
].contains(type);
}

// Converts the 5 raw Jablotron bytes to the decimal card number via BCD.
int jablotronCardId(Uint8List bytes) {
int cardId = 0;
for (var b in bytes) {
cardId = cardId * 100 + ((b >> 4) * 10) + (b & 0x0F);
}
return cardId;
}

Future<(HFCardInfo, MifareClassicInfo, MifareUltralightInfo)> readHFInfo(
BuildContext context, dynamic updateMifareClassicRecovery) async {
var appState = Provider.of<ChameleonGUIState>(context, listen: false);
Expand Down
6 changes: 6 additions & 0 deletions chameleonultragui/lib/helpers/t55xx/write/base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,12 @@ class BaseT55XXCardHelper extends AbstractWriteHelper {
await Future.delayed(const Duration(milliseconds: 500));
var newCard = await communicator.readViking();
return newCard.toString() == card.uid;
} else if (card.tag == TagType.jablotron) {
await communicator.writeJablotronToT55XX(hexToBytes(card.uid),
hexToBytes(newKey), [hexToBytes(currentKey), Uint8List(4)]);
await Future.delayed(const Duration(milliseconds: 500));
var newCard = await communicator.readJablotron();
return newCard.toString() == card.uid;
} else if (card.tag == TagType.pac) {
await communicator.writePacToT55XX(hexToBytes(card.uid),
hexToBytes(newKey), [hexToBytes(currentKey), Uint8List(4)]);
Expand Down
1 change: 1 addition & 0 deletions chameleonultragui/lib/helpers/write.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ abstract class AbstractWriteHelper {
if (isEM410X(type) ||
type == TagType.hidProx ||
type == TagType.viking ||
type == TagType.jablotron ||
type == TagType.pac ||
type == TagType.ioProx ||
type == TagType.idteck) {
Expand Down
39 changes: 39 additions & 0 deletions chameleonultragui/test/jablotron_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import 'dart:typed_data';

import 'package:chameleonultragui/helpers/definitions.dart';
import 'package:chameleonultragui/helpers/general.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
test('jablotronCardId converts raw bytes to decimal via BCD', () {
// Each byte is read as two BCD digits, concatenated across the 5 bytes.
expect(
jablotronCardId(Uint8List.fromList([0x00, 0x12, 0x34, 0x56, 0x78])),
12345678,
);
expect(
jablotronCardId(Uint8List.fromList([0x00, 0x00, 0x00, 0x00, 0x00])),
0,
);
expect(
jablotronCardId(Uint8List.fromList([0x00, 0x00, 0x00, 0x00, 0x01])),
1,
);
});

test('JablotronCard exposes type, hex string and decimal card number', () {
final card = JablotronCard.fromUID('0012345678');

expect(card.type, TagType.jablotron);
expect(card.toString(), '00 12 34 56 78');
expect(card.toViewableString(), '12345678 (00 12 34 56 78)');
});

test('JablotronCard round-trips through bytes', () {
final bytes = Uint8List.fromList([0x00, 0x12, 0x34, 0x56, 0x78]);
final card = JablotronCard.fromBytes(bytes);

expect(card.uid, bytes);
expect(card.type, TagType.jablotron);
});
}
Loading