mirror of
https://github.com/cliffe/BreakEscape.git
synced 2026-02-20 13:50:46 +00:00
docs(rfid): Add multi-protocol RFID system planning & review
Created comprehensive planning for adding multiple RFID protocols with different security levels and attack capabilities. Planning Documents: - 00_IMPLEMENTATION_SUMMARY.md: Complete implementation guide (14h estimate) - 01_TECHNICAL_DESIGN.md: Protocol specs, data models, UI design - 02_IMPLEMENTATION_PLAN.md: Detailed file-by-file implementation plan - CRITICAL_REVIEW.md: Pre-implementation review with 12 improvements Protocol System (Simplified from Original): ✅ EM4100 (low security) - Instant clone, already implemented ✅ MIFARE Classic (medium) - Requires key attacks (Darkside/Nested/Dictionary) ✅ MIFARE DESFire (high) - UID only, forces physical theft ❌ HID Prox - Removed (too similar to EM4100, saves 2h) Key Features: - Protocol detection with color-coded security levels - MIFARE key cracking minigames (30s Darkside, 10s Nested, instant Dictionary) - Ink integration for conditional interactions based on card protocol - Dual format support (no migration needed) - UID-only emulation for DESFire with acceptsUIDOnly door property Review Improvements Applied: - Merged "attack mode" into clone mode (simpler state machine) - Removed firmware upgrade system (can add later) - Dual format card data support instead of migration (safer) - Added error handling for unsupported protocols - Added cleanup for background attacks - Documented required Ink variables Time Estimate: 14 hours (down from 19h) - Phase 1: Protocol Foundation (3h) - Phase 2: Protocol Detection & UI (3h) - Phase 3: MIFARE Attack System (5h) - Phase 4: Ink Integration (2h) - Phase 5: Door Integration & Testing (1h) Next Steps: Begin implementation starting with Phase 1
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,708 @@
|
||||
# RFID Protocols & Interactions - Technical Design
|
||||
|
||||
## Overview
|
||||
|
||||
Add support for multiple RFID protocols with different security levels and capabilities. Each protocol has realistic constraints based on real-world RFID technology, enabling different attack vectors and gameplay strategies.
|
||||
|
||||
## Protocol Specifications
|
||||
|
||||
### Protocol Definitions
|
||||
|
||||
Based on real-world RFID technology used in access control systems:
|
||||
|
||||
```javascript
|
||||
const RFID_PROTOCOLS = {
|
||||
'EM4100': {
|
||||
name: 'EM-Micro EM4100',
|
||||
frequency: '125kHz',
|
||||
security: 'low',
|
||||
readOnly: true,
|
||||
capabilities: {
|
||||
read: true,
|
||||
clone: true,
|
||||
write: false,
|
||||
emulate: true,
|
||||
bruteforce: false // Too many combinations
|
||||
},
|
||||
description: 'Legacy low-frequency card. Read-only, easily cloned.',
|
||||
vulnerabilities: ['Clone attack', 'Replay attack'],
|
||||
hexLength: 10, // 5 bytes
|
||||
color: '#FF6B6B' // Red for low security
|
||||
},
|
||||
|
||||
'HID_Prox': {
|
||||
name: 'HID Prox II',
|
||||
frequency: '125kHz',
|
||||
security: 'medium-low',
|
||||
readOnly: true,
|
||||
capabilities: {
|
||||
read: true,
|
||||
clone: true,
|
||||
write: false,
|
||||
emulate: true,
|
||||
bruteforce: false
|
||||
},
|
||||
description: 'Common corporate badge. Read-only, proprietary format.',
|
||||
vulnerabilities: ['Clone attack', 'Replay attack'],
|
||||
hexLength: 12, // 6 bytes (26-bit format)
|
||||
color: '#FFA500' // Orange for medium-low
|
||||
},
|
||||
|
||||
'MIFARE_Classic': {
|
||||
name: 'MIFARE Classic 1K',
|
||||
frequency: '13.56MHz',
|
||||
security: 'medium',
|
||||
readOnly: false,
|
||||
capabilities: {
|
||||
read: 'with-keys', // Need auth keys
|
||||
clone: 'with-keys',
|
||||
write: 'with-keys',
|
||||
emulate: true,
|
||||
bruteforce: true // Weak crypto, can crack keys
|
||||
},
|
||||
description: 'Encrypted NFC card. Requires authentication keys.',
|
||||
vulnerabilities: ['Darkside attack', 'Nested attack', 'Hardnested attack'],
|
||||
sectors: 16,
|
||||
keysPerSector: 2, // Key A and Key B
|
||||
hexLength: 8, // UID is 4 bytes
|
||||
color: '#4ECDC4' // Teal for medium
|
||||
},
|
||||
|
||||
'MIFARE_DESFire': {
|
||||
name: 'MIFARE DESFire EV2',
|
||||
frequency: '13.56MHz',
|
||||
security: 'high',
|
||||
readOnly: false,
|
||||
capabilities: {
|
||||
read: false, // Encrypted, can't read without master key
|
||||
clone: false, // Can't clone - uses mutual authentication
|
||||
write: false, // Can't write without master key
|
||||
emulate: 'uid-only', // Can only emulate UID, not full card
|
||||
bruteforce: false // Strong crypto (3DES/AES)
|
||||
},
|
||||
description: 'High-security encrypted NFC. Nearly impossible to clone.',
|
||||
vulnerabilities: ['Physical theft only'],
|
||||
hexLength: 14, // 7-byte UID
|
||||
color: '#95E1D3' // Light green for high security
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Data Model Changes
|
||||
|
||||
### Card Data Structure
|
||||
|
||||
**Current** (EM4100 only):
|
||||
```javascript
|
||||
{
|
||||
type: "keycard",
|
||||
name: "Employee Badge",
|
||||
rfid_hex: "01AB34CD56",
|
||||
rfid_facility: 1,
|
||||
rfid_card_number: 43981,
|
||||
rfid_protocol: "EM4100",
|
||||
key_id: "employee_badge"
|
||||
}
|
||||
```
|
||||
|
||||
**Enhanced** (all protocols):
|
||||
```javascript
|
||||
{
|
||||
type: "keycard",
|
||||
name: "Employee Badge",
|
||||
rfid_protocol: "EM4100", // or HID_Prox, MIFARE_Classic, MIFARE_DESFire
|
||||
key_id: "employee_badge",
|
||||
|
||||
// Protocol-specific data (only relevant fields per protocol)
|
||||
rfid_data: {
|
||||
// EM4100 / HID Prox
|
||||
hex: "01AB34CD56",
|
||||
facility: 1,
|
||||
cardNumber: 43981,
|
||||
|
||||
// MIFARE Classic (if applicable)
|
||||
uid: "AB12CD34",
|
||||
sectors: {
|
||||
0: { keyA: "FFFFFFFFFFFF", keyB: null }, // Default key
|
||||
1: { keyA: "A1B2C3D4E5F6", keyB: "123456789ABC" }, // Custom keys
|
||||
// ... more sectors
|
||||
},
|
||||
|
||||
// MIFARE DESFire (if applicable)
|
||||
uid: "04AB12CD3456E0",
|
||||
masterKeyKnown: false, // Can't clone without this
|
||||
|
||||
// Clone quality (for cloned cards)
|
||||
isClone: false,
|
||||
cloneQuality: 100, // 0-100, affects reliability
|
||||
clonedFrom: null // Original card ID
|
||||
},
|
||||
|
||||
observations: "Standard employee access badge."
|
||||
}
|
||||
```
|
||||
|
||||
### RFID Cloner Data Structure
|
||||
|
||||
```javascript
|
||||
{
|
||||
type: "rfid_cloner",
|
||||
name: "Flipper Zero",
|
||||
|
||||
// Firmware capabilities (can be upgraded in-game)
|
||||
firmware: {
|
||||
version: "1.0",
|
||||
protocols: ['EM4100', 'HID_Prox'], // Unlocks MIFARE support later
|
||||
attacks: ['read', 'clone', 'emulate'] // Unlocks 'bruteforce' later
|
||||
},
|
||||
|
||||
saved_cards: [
|
||||
// Array of card data objects
|
||||
],
|
||||
|
||||
// Cracking progress (for MIFARE Classic key attacks)
|
||||
activeAttacks: {
|
||||
"security_badge_uid_AB12CD34": {
|
||||
type: "darkside_attack",
|
||||
protocol: "MIFARE_Classic",
|
||||
progress: 45, // 0-100%
|
||||
sector: 3,
|
||||
foundKeys: {
|
||||
0: { keyA: "FFFFFFFFFFFF" },
|
||||
1: { keyA: "A1B2C3D4E5F6" }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
x: 350,
|
||||
y: 250,
|
||||
takeable: true,
|
||||
observations: "Portable multi-tool for pentesters. Can read and emulate RFID cards."
|
||||
}
|
||||
```
|
||||
|
||||
## Flipper Zero Operations by Protocol
|
||||
|
||||
### EM4100 / HID Prox Operations
|
||||
|
||||
**1. Read**
|
||||
- Instant read, shows all data
|
||||
- No authentication needed
|
||||
- UI: Standard reading screen (already implemented)
|
||||
|
||||
**2. Clone**
|
||||
- Instant clone, perfect copy
|
||||
- UI: Progress bar → Card data → Save
|
||||
|
||||
**3. Emulate**
|
||||
- Perfect emulation
|
||||
- UI: Emulation screen (already implemented)
|
||||
|
||||
### MIFARE Classic Operations
|
||||
|
||||
**1. Read (requires keys)**
|
||||
```
|
||||
Decision Tree:
|
||||
├─ Has all keys for all sectors?
|
||||
│ ├─ Yes → Read full card data
|
||||
│ └─ No → Show partial data, offer key attack
|
||||
└─ Has NO keys?
|
||||
└─ Offer Darkside/Nested attack to crack keys
|
||||
```
|
||||
|
||||
**2. Clone (requires keys)**
|
||||
- Can only clone sectors where keys are known
|
||||
- Partial clones possible (some sectors locked)
|
||||
|
||||
**3. Key Attacks**
|
||||
- **Darkside Attack**: Crack keys from scratch (~30 seconds realistic)
|
||||
- **Nested Attack**: Crack remaining keys if you have one key (~10 seconds)
|
||||
- **Dictionary Attack**: Try common keys (instant check)
|
||||
|
||||
**4. Write**
|
||||
- Modify card data in writable sectors
|
||||
- Useful for:
|
||||
- Changing balance on payment cards
|
||||
- Modifying access permissions
|
||||
- Writing cloned data to blank cards
|
||||
|
||||
**5. Emulate**
|
||||
- Can emulate if keys are known
|
||||
- UI shows which sectors are available
|
||||
|
||||
### MIFARE DESFire Operations
|
||||
|
||||
**1. Read**
|
||||
- Can only read UID (no encryption keys)
|
||||
- Cannot read application data
|
||||
|
||||
**2. UID Emulation**
|
||||
- Can emulate UID only
|
||||
- Some systems check UID only (lower security)
|
||||
- Higher security systems use encrypted challenge-response (emulation fails)
|
||||
|
||||
**3. No Clone/Write**
|
||||
- Strong encryption prevents cloning
|
||||
- Game design: These cards must be physically stolen or access granted through social engineering
|
||||
|
||||
## UI Design
|
||||
|
||||
### Protocol Detection Screen
|
||||
|
||||
New screen when reading a card for the first time:
|
||||
|
||||
```
|
||||
╔════════════════════════════════════╗
|
||||
║ FLIPPER ZERO ⚡ 100% ║
|
||||
╠════════════════════════════════════╣
|
||||
║ ║
|
||||
║ RFID > Read ║
|
||||
║ ║
|
||||
║ Detecting... ║
|
||||
║ ║
|
||||
║ ┌────────────────────────────────┐║
|
||||
║ │ 📡 │║
|
||||
║ │ │║
|
||||
║ │ [Progress Bar 65%] │║
|
||||
║ └────────────────────────────────┘║
|
||||
║ ║
|
||||
║ Scanning frequencies... ║
|
||||
║ 125kHz: No response ║
|
||||
║ 13.56MHz: Card detected! ║
|
||||
║ ║
|
||||
╚════════════════════════════════════╝
|
||||
```
|
||||
|
||||
### Protocol Info Screen
|
||||
|
||||
After detection:
|
||||
|
||||
```
|
||||
╔════════════════════════════════════╗
|
||||
║ FLIPPER ZERO ⚡ 100% ║
|
||||
╠════════════════════════════════════╣
|
||||
║ ║
|
||||
║ RFID > Read > Info ║
|
||||
║ ║
|
||||
║ ┌────────────────────────────────┐║
|
||||
║ │ MIFARE Classic 1K │║
|
||||
║ │ ────────────────── │║
|
||||
║ │ Freq: 13.56MHz │║
|
||||
║ │ Security: Medium │║
|
||||
║ │ UID: AB 12 CD 34 │║
|
||||
║ └────────────────────────────────┘║
|
||||
║ ║
|
||||
║ This card uses encryption. ║
|
||||
║ Authentication keys required. ║
|
||||
║ ║
|
||||
║ > Read (requires keys) ║
|
||||
║ Crack Keys ║
|
||||
║ Try Dictionary ║
|
||||
║ Cancel ║
|
||||
║ ║
|
||||
╚════════════════════════════════════╝
|
||||
```
|
||||
|
||||
### Key Cracking Screen (MIFARE Classic)
|
||||
|
||||
```
|
||||
╔════════════════════════════════════╗
|
||||
║ FLIPPER ZERO ⚡ 95% ║
|
||||
╠════════════════════════════════════╣
|
||||
║ ║
|
||||
║ RFID > Darkside Attack ║
|
||||
║ ║
|
||||
║ Security Badge ║
|
||||
║ UID: AB 12 CD 34 ║
|
||||
║ ║
|
||||
║ ┌────────────────────────────────┐║
|
||||
║ │ Cracking Sector 3... │║
|
||||
║ │ ████████████░░░░░░░░ 65% │║
|
||||
║ └────────────────────────────────┘║
|
||||
║ ║
|
||||
║ Keys Found: ║
|
||||
║ Sector 0: FF FF FF FF FF FF ✓ ║
|
||||
║ Sector 1: A1 B2 C3 D4 E5 F6 ✓ ║
|
||||
║ Sector 2: 12 34 56 78 9A BC ✓ ║
|
||||
║ Sector 3: Cracking... ║
|
||||
║ ║
|
||||
║ Don't move card... ║
|
||||
║ ║
|
||||
╚════════════════════════════════════╝
|
||||
```
|
||||
|
||||
### Card Data Screen with Protocol-Specific Fields
|
||||
|
||||
**EM4100:**
|
||||
```
|
||||
╔════════════════════════════════════╗
|
||||
║ RFID > Read ║
|
||||
║ ║
|
||||
║ EM-Micro EM4100 ║
|
||||
║ ║
|
||||
║ HEX: 01 AB 34 CD 56 ║
|
||||
║ Facility: 1 ║
|
||||
║ Card: 43981 ║
|
||||
║ Checksum: 0xD6 ║
|
||||
║ DEZ 8: 00043981 ║
|
||||
║ ║
|
||||
║ [Save] [Cancel] ║
|
||||
╚════════════════════════════════════╝
|
||||
```
|
||||
|
||||
**MIFARE Classic (with keys):**
|
||||
```
|
||||
╔════════════════════════════════════╗
|
||||
║ RFID > Read ║
|
||||
║ ║
|
||||
║ MIFARE Classic 1K ║
|
||||
║ ║
|
||||
║ UID: AB 12 CD 34 ║
|
||||
║ SAK: 08 ║
|
||||
║ ATQA: 00 04 ║
|
||||
║ ║
|
||||
║ Sectors: 16 ║
|
||||
║ Keys Known: 16/16 ✓ ║
|
||||
║ ║
|
||||
║ Readable: Yes ║
|
||||
║ Writable: Yes ║
|
||||
║ Clonable: Yes ║
|
||||
║ ║
|
||||
║ [Save] [View Data] [Cancel] ║
|
||||
╚════════════════════════════════════╝
|
||||
```
|
||||
|
||||
**MIFARE DESFire (limited):**
|
||||
```
|
||||
╔════════════════════════════════════╗
|
||||
║ RFID > Read ║
|
||||
║ ║
|
||||
║ MIFARE DESFire EV2 ║
|
||||
║ ║
|
||||
║ UID: 04 AB 12 CD 34 56 E0 ║
|
||||
║ SAK: 20 ║
|
||||
║ ATQA: 03 44 ║
|
||||
║ ║
|
||||
║ ⚠️ High Security ║
|
||||
║ ║
|
||||
║ This card uses 3DES encryption. ║
|
||||
║ Full clone: Not possible ║
|
||||
║ UID emulation: Possible ║
|
||||
║ ║
|
||||
║ Some systems only check UID and ║
|
||||
║ don't use encryption properly. ║
|
||||
║ ║
|
||||
║ [Save UID] [Cancel] ║
|
||||
╚════════════════════════════════════╝
|
||||
```
|
||||
|
||||
## Ink Integration
|
||||
|
||||
### Exposing Card Protocol Info to Ink
|
||||
|
||||
When NPC conversation starts, sync card protocol information:
|
||||
|
||||
```javascript
|
||||
// In person-chat-conversation.js, extend syncItemsToInk()
|
||||
syncCardProtocolsToInk() {
|
||||
if (!this.inkEngine || !this.npc || !this.npc.itemsHeld) return;
|
||||
|
||||
// Find all keycards held by NPC
|
||||
const keycards = this.npc.itemsHeld.filter(item => item.type === 'keycard');
|
||||
|
||||
keycards.forEach((card, index) => {
|
||||
const protocol = card.rfid_protocol || 'EM4100';
|
||||
const prefix = index === 0 ? 'card' : `card${index + 1}`;
|
||||
|
||||
// Set variables for this card
|
||||
this.inkEngine.setVariable(`${prefix}_protocol`, protocol);
|
||||
this.inkEngine.setVariable(`${prefix}_name`, card.name);
|
||||
this.inkEngine.setVariable(`${prefix}_security`, RFID_PROTOCOLS[protocol].security);
|
||||
this.inkEngine.setVariable(`${prefix}_clonable`, RFID_PROTOCOLS[protocol].capabilities.clone === true);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Ink Variable Usage
|
||||
|
||||
```ink
|
||||
VAR card_protocol = ""
|
||||
VAR card_name = ""
|
||||
VAR card_security = ""
|
||||
VAR card_clonable = false
|
||||
|
||||
=== guard_conversation ===
|
||||
# speaker:npc
|
||||
I've got my security badge right here on my lanyard.
|
||||
|
||||
{card_protocol == "EM4100":
|
||||
-> easy_clone
|
||||
}
|
||||
{card_protocol == "MIFARE_Classic":
|
||||
-> needs_key_attack
|
||||
}
|
||||
{card_protocol == "MIFARE_DESFire":
|
||||
-> impossible_clone
|
||||
}
|
||||
|
||||
=== easy_clone ===
|
||||
+ [Subtly scan the badge]
|
||||
# clone_keycard:{card_name}|{card_hex}
|
||||
You discretely position your Flipper near their badge.
|
||||
-> cloned
|
||||
|
||||
=== needs_key_attack ===
|
||||
+ [Scan the badge]
|
||||
You scan the badge but it's encrypted...
|
||||
# start_mifare_attack:{card_name}|{card_uid}
|
||||
Your Flipper starts a Darkside attack.
|
||||
-> wait_for_crack
|
||||
|
||||
+ [Ask to borrow it for a minute]
|
||||
-> borrow_card_choice
|
||||
|
||||
=== impossible_clone ===
|
||||
+ [Try to scan the badge]
|
||||
# speaker:player
|
||||
You position your Flipper near their badge.
|
||||
# speaker:npc
|
||||
You can only capture the UID. This card uses strong encryption - you can't clone it without the master key.
|
||||
# save_uid_only:{card_name}|{card_uid}
|
||||
-> uid_saved
|
||||
|
||||
+ [Ask if you can borrow it]
|
||||
This is your only option. You'll need the physical card.
|
||||
-> borrow_card_choice
|
||||
```
|
||||
|
||||
### New Ink Tags
|
||||
|
||||
#### `# start_mifare_attack:CardName|UID`
|
||||
|
||||
Starts a MIFARE Classic key cracking attack in the background.
|
||||
|
||||
```javascript
|
||||
case 'start_mifare_attack':
|
||||
if (param) {
|
||||
const [cardName, uid] = param.split('|');
|
||||
|
||||
// Check for Flipper
|
||||
const cloner = window.inventory.items.find(item =>
|
||||
item?.scenarioData?.type === 'rfid_cloner'
|
||||
);
|
||||
|
||||
if (!cloner) {
|
||||
result.message = '⚠️ Need RFID cloner';
|
||||
break;
|
||||
}
|
||||
|
||||
// Check firmware supports MIFARE
|
||||
if (!cloner.scenarioData.firmware.protocols.includes('MIFARE_Classic')) {
|
||||
result.message = '⚠️ Firmware upgrade needed for MIFARE attacks';
|
||||
break;
|
||||
}
|
||||
|
||||
// Start background attack
|
||||
startMIFAREAttack(cardName, uid, cloner);
|
||||
result.success = true;
|
||||
result.message = `🔓 Started Darkside attack on ${cardName}`;
|
||||
}
|
||||
break;
|
||||
```
|
||||
|
||||
#### `# check_attack_complete:CardUID`
|
||||
|
||||
Check if background attack finished (can use in conditional choice):
|
||||
|
||||
```ink
|
||||
=== wait_for_crack ===
|
||||
# speaker:npc
|
||||
So anyway, as I was saying about the weekend plans...
|
||||
|
||||
{check_attack_complete(card_uid):
|
||||
+ [Your Flipper vibrates - attack complete!]
|
||||
# speaker:player
|
||||
(Your Flipper successfully cracked the keys!)
|
||||
# clone_mifare:{card_name}|{card_uid}
|
||||
-> cloned
|
||||
- else:
|
||||
+ [Continue chatting]
|
||||
-> keep_waiting
|
||||
}
|
||||
```
|
||||
|
||||
#### `# clone_mifare:CardName|UID`
|
||||
|
||||
Clone a MIFARE card (requires keys to be cracked first):
|
||||
|
||||
```javascript
|
||||
case 'clone_mifare':
|
||||
if (param) {
|
||||
const [cardName, uid] = param.split('|');
|
||||
|
||||
const cloner = window.inventory.items.find(item =>
|
||||
item?.scenarioData?.type === 'rfid_cloner'
|
||||
);
|
||||
|
||||
// Check if we have the keys
|
||||
const attack = cloner?.scenarioData?.activeAttacks?.[`uid_${uid}`];
|
||||
|
||||
if (!attack || attack.progress < 100) {
|
||||
result.message = '⚠️ Keys not yet cracked';
|
||||
break;
|
||||
}
|
||||
|
||||
// Launch RFID minigame in clone mode with MIFARE data
|
||||
window.pendingConversationReturn = {
|
||||
npcId: window.currentConversationNPCId,
|
||||
type: window.currentConversationMinigameType || 'person-chat'
|
||||
};
|
||||
|
||||
window.startRFIDMinigame(null, null, {
|
||||
mode: 'clone',
|
||||
protocol: 'MIFARE_Classic',
|
||||
cardToClone: {
|
||||
name: cardName,
|
||||
rfid_protocol: 'MIFARE_Classic',
|
||||
rfid_data: {
|
||||
uid: uid,
|
||||
sectors: attack.foundKeys
|
||||
},
|
||||
type: 'keycard',
|
||||
key_id: `cloned_${uid.toLowerCase()}`
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
```
|
||||
|
||||
#### `# save_uid_only:CardName|UID`
|
||||
|
||||
Save only UID for DESFire cards (can't clone full card):
|
||||
|
||||
```javascript
|
||||
case 'save_uid_only':
|
||||
if (param) {
|
||||
const [cardName, uid] = param.split('|');
|
||||
|
||||
window.pendingConversationReturn = {
|
||||
npcId: window.currentConversationNPCId,
|
||||
type: window.currentConversationMinigameType || 'person-chat'
|
||||
};
|
||||
|
||||
window.startRFIDMinigame(null, null, {
|
||||
mode: 'clone',
|
||||
protocol: 'MIFARE_DESFire',
|
||||
uidOnly: true,
|
||||
cardToClone: {
|
||||
name: cardName + " (UID Only)",
|
||||
rfid_protocol: 'MIFARE_DESFire',
|
||||
rfid_data: {
|
||||
uid: uid,
|
||||
masterKeyKnown: false
|
||||
},
|
||||
type: 'keycard',
|
||||
key_id: `uid_${uid.toLowerCase()}`,
|
||||
observations: "⚠️ UID only - may not work on secure readers"
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
```
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
This feature can be implemented incrementally:
|
||||
|
||||
### Phase 1: Protocol Data Model (Foundation)
|
||||
1. Add RFID_PROTOCOLS constant
|
||||
2. Update card data structure to support rfid_data
|
||||
3. Update cloner to support firmware capabilities
|
||||
4. Backward compatibility for existing EM4100 cards
|
||||
|
||||
### Phase 2: Protocol Detection & Display
|
||||
1. Add protocol detection logic in rfid-data.js
|
||||
2. Create protocol info UI screen
|
||||
3. Update card data display to show protocol-specific fields
|
||||
4. Color coding by security level
|
||||
|
||||
### Phase 3: MIFARE Classic Support
|
||||
1. Implement key attack minigame screens
|
||||
2. Add background attack system
|
||||
3. Add dictionary attack (common keys)
|
||||
4. Partial clone support (some sectors)
|
||||
|
||||
### Phase 4: MIFARE DESFire Support
|
||||
1. UID-only save functionality
|
||||
2. Emulation with warning messages
|
||||
3. Physical theft/social engineering paths
|
||||
|
||||
### Phase 5: Ink Integration
|
||||
1. Extend syncItemsToInk for protocol variables
|
||||
2. Implement new Ink tags
|
||||
3. Add conditional attack options
|
||||
4. Create example scenarios
|
||||
|
||||
### Phase 6: HID Prox Support
|
||||
1. Add HID-specific data format
|
||||
2. Facility code + card number extraction
|
||||
3. UI for HID cards
|
||||
|
||||
## Testing Plan
|
||||
|
||||
### Unit Tests
|
||||
- Protocol detection from card data
|
||||
- Capability checks per protocol
|
||||
- Key cracking simulation
|
||||
- UID extraction
|
||||
|
||||
### Integration Tests
|
||||
- Clone EM4100 (should work instantly)
|
||||
- Clone HID Prox (should work instantly)
|
||||
- Attempt clone MIFARE Classic without keys (should fail/offer attack)
|
||||
- Attack MIFARE Classic (should eventually succeed)
|
||||
- Attempt clone DESFire (should only get UID)
|
||||
- Emulate UID-only DESFire against simple reader (should work)
|
||||
- Emulate UID-only DESFire against secure reader (should fail)
|
||||
|
||||
### Scenario Tests
|
||||
Create test scenarios for each protocol:
|
||||
- `test-rfid-em4100.json` (current)
|
||||
- `test-rfid-hid-prox.json`
|
||||
- `test-rfid-mifare-classic.json`
|
||||
- `test-rfid-mifare-desfire.json`
|
||||
|
||||
## Backward Compatibility
|
||||
|
||||
Existing EM4100 cards continue to work:
|
||||
|
||||
```javascript
|
||||
// Old format (still works)
|
||||
{
|
||||
type: "keycard",
|
||||
rfid_hex: "01AB34CD56",
|
||||
rfid_facility: 1,
|
||||
rfid_card_number: 43981,
|
||||
rfid_protocol: "EM4100"
|
||||
}
|
||||
|
||||
// Automatically migrated to:
|
||||
{
|
||||
type: "keycard",
|
||||
rfid_protocol: "EM4100",
|
||||
rfid_data: {
|
||||
hex: "01AB34CD56",
|
||||
facility: 1,
|
||||
cardNumber: 43981
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Migration happens transparently when cards are loaded.
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
- Protocol detection: Instant (client-side lookup)
|
||||
- Key attacks: Simulated with setTimeout (no real crypto)
|
||||
- Background attacks: Store in gameState, check on game loop
|
||||
- No actual network calls or heavy computation
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,526 @@
|
||||
# RFID Protocols Implementation Plan - Critical Review
|
||||
|
||||
**Review Date**: Current Session
|
||||
**Reviewer**: Claude (Self-Review)
|
||||
**Status**: Pre-Implementation Analysis
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The implementation plan is **comprehensive and technically sound**, but has several areas that can be **simplified and improved** for better development efficiency and gameplay value. This review identifies 12 key improvements organized by priority.
|
||||
|
||||
## High Priority Issues
|
||||
|
||||
### Issue #1: HID Prox Adds Minimal Gameplay Value
|
||||
|
||||
**Problem**: HID Prox is nearly identical to EM4100 from a gameplay perspective:
|
||||
- Both are 125kHz read-only cards
|
||||
- Both clone instantly
|
||||
- Only difference is hex length (10 vs 12 chars)
|
||||
- Both have same vulnerabilities and capabilities
|
||||
|
||||
**Impact**: Development time spent on HID Prox doesn't add meaningful gameplay variety.
|
||||
|
||||
**Recommendation**: **Remove HID Prox from initial implementation**.
|
||||
- Focus on three distinct protocols: EM4100 (easy), MIFARE Classic (medium), MIFARE DESFire (hard)
|
||||
- Can add HID Prox later if needed (it's trivial to add)
|
||||
- Saves ~2 hours of implementation and testing time
|
||||
|
||||
**Updated Protocol Set**:
|
||||
```javascript
|
||||
const RFID_PROTOCOLS = {
|
||||
'EM4100': 'low', // Always works
|
||||
'MIFARE_Classic': 'medium', // Requires key attacks
|
||||
'MIFARE_DESFire': 'high' // UID only, physical theft needed
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Issue #2: Attack Mode vs Clone Mode Confusion
|
||||
|
||||
**Problem**: Plan introduces separate "attack" mode:
|
||||
```javascript
|
||||
if (this.mode === 'attack') {
|
||||
this.ui.createAttackInterface();
|
||||
}
|
||||
```
|
||||
|
||||
This creates confusion:
|
||||
- What's the difference between attack mode and clone mode?
|
||||
- After attack succeeds, do you still need to clone?
|
||||
- Two separate code paths for similar functionality
|
||||
|
||||
**Recommendation**: **Merge attack into clone mode**.
|
||||
|
||||
**Better Flow**:
|
||||
```
|
||||
Clone Mode Start
|
||||
├─ Detect protocol
|
||||
├─ EM4100? → Read & Clone instantly
|
||||
├─ MIFARE Classic?
|
||||
│ ├─ Has keys? → Read & Clone
|
||||
│ └─ No keys? → Show attack options → Run attack → Then clone
|
||||
└─ MIFARE DESFire? → Save UID only
|
||||
```
|
||||
|
||||
**Implementation**:
|
||||
```javascript
|
||||
// In clone mode
|
||||
if (this.mode === 'clone') {
|
||||
const protocol = this.detectProtocol(this.cardToClone);
|
||||
|
||||
if (protocol === 'MIFARE_Classic') {
|
||||
const hasKeys = this.hasAllKeys(this.cardToClone);
|
||||
if (!hasKeys) {
|
||||
// Show protocol info with attack options
|
||||
this.ui.showProtocolInfo(this.cardToClone);
|
||||
// User clicks "Darkside Attack"
|
||||
// Attack runs in same minigame instance
|
||||
// After attack completes, show card data and save
|
||||
} else {
|
||||
// Has keys, proceed to clone normally
|
||||
this.ui.showReadingScreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Simplifies state machine and makes flow more intuitive.
|
||||
|
||||
---
|
||||
|
||||
### Issue #3: Incomplete Firmware Upgrade System
|
||||
|
||||
**Problem**: Plan mentions firmware but doesn't implement it:
|
||||
```javascript
|
||||
firmware: {
|
||||
version: "1.0",
|
||||
protocols: ["EM4100", "HID_Prox"],
|
||||
attacks: ["read", "clone", "emulate"]
|
||||
}
|
||||
```
|
||||
|
||||
But no code for:
|
||||
- How to upgrade firmware
|
||||
- Where to find upgrades
|
||||
- What triggers availability
|
||||
|
||||
**Recommendation**: **Either fully implement or remove firmware system**.
|
||||
|
||||
**Option A - Remove (Simpler)**:
|
||||
- All protocols always available
|
||||
- Flipper Zero in game has latest firmware pre-installed
|
||||
- Saves implementation time
|
||||
|
||||
**Option B - Full Implementation** (if player progression needed):
|
||||
```javascript
|
||||
// Firmware upgrade item in scenario
|
||||
{
|
||||
"type": "firmware_update",
|
||||
"name": "Flipper Firmware v1.2 (MIFARE Support)",
|
||||
"upgrades_protocols": ["MIFARE_Classic"],
|
||||
"upgrades_attacks": ["darkside", "nested"]
|
||||
}
|
||||
|
||||
// In interactions.js - when using firmware update
|
||||
if (item.type === 'firmware_update') {
|
||||
const cloner = getFlipperFromInventory();
|
||||
cloner.firmware.protocols.push(...item.upgrades_protocols);
|
||||
cloner.firmware.attacks.push(...item.upgrades_attacks);
|
||||
showMessage("Firmware updated!");
|
||||
}
|
||||
```
|
||||
|
||||
**Recommendation**: Use Option A for initial implementation. Add firmware upgrades later if progression system is needed.
|
||||
|
||||
---
|
||||
|
||||
### Issue #4: Card Data Migration Incomplete
|
||||
|
||||
**Problem**: Migration only handles EM4100:
|
||||
```javascript
|
||||
if (protocol === 'EM4100' || protocol === 'HID_Prox') {
|
||||
return {
|
||||
...cardData,
|
||||
rfid_data: {
|
||||
hex: cardData.rfid_hex,
|
||||
// ...
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return cardData; // What about other protocols?
|
||||
```
|
||||
|
||||
**Recommendation**: **Complete migration for all protocols or use simpler approach**.
|
||||
|
||||
**Better Approach** - Dual Format Support:
|
||||
```javascript
|
||||
// Support both old and new formats transparently
|
||||
getRFIDHex(cardData) {
|
||||
// New format
|
||||
if (cardData.rfid_data?.hex) {
|
||||
return cardData.rfid_data.hex;
|
||||
}
|
||||
|
||||
// Old format (backward compat)
|
||||
if (cardData.rfid_hex) {
|
||||
return cardData.rfid_hex;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
getRFIDUID(cardData) {
|
||||
if (cardData.rfid_data?.uid) {
|
||||
return cardData.rfid_data.uid;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
No migration needed - just read from either location. Simpler and safer.
|
||||
|
||||
---
|
||||
|
||||
### Issue #5: Protocol Detection in Clone Mode Not Addressed
|
||||
|
||||
**Problem**: Plan shows protocol detection for reading cards, but what about clone mode?
|
||||
|
||||
When clone mode starts with `cardToClone` parameter:
|
||||
```javascript
|
||||
window.startRFIDMinigame(null, null, {
|
||||
mode: 'clone',
|
||||
cardToClone: someCard
|
||||
});
|
||||
```
|
||||
|
||||
The card already has data - no need to "detect" protocol. But UI flow unclear.
|
||||
|
||||
**Recommendation**: **Clarify clone mode initialization**.
|
||||
|
||||
```javascript
|
||||
// In rfid-minigame.js init()
|
||||
if (this.mode === 'clone') {
|
||||
if (this.cardToClone) {
|
||||
const protocol = this.cardToClone.rfid_protocol || 'EM4100';
|
||||
|
||||
if (protocol === 'MIFARE_Classic') {
|
||||
// Check if keys are available
|
||||
const keysKnown = this.cardToClone.rfid_data?.sectors ?
|
||||
Object.keys(this.cardToClone.rfid_data.sectors).length : 0;
|
||||
|
||||
if (keysKnown === 0) {
|
||||
// No keys - show protocol info with attack options
|
||||
this.ui.showProtocolInfo(this.cardToClone);
|
||||
} else {
|
||||
// Has keys - start reading/cloning
|
||||
this.ui.showReadingScreen();
|
||||
}
|
||||
} else {
|
||||
// EM4100 or DESFire - start reading immediately
|
||||
this.ui.showReadingScreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Medium Priority Issues
|
||||
|
||||
### Issue #6: Ink Variables Require Declaration
|
||||
|
||||
**Problem**: Plan shows setting Ink variables:
|
||||
```javascript
|
||||
this.inkEngine.setVariable('card_protocol', protocol);
|
||||
```
|
||||
|
||||
But Ink variables must be declared in the .ink file first:
|
||||
```ink
|
||||
VAR card_protocol = ""
|
||||
VAR card_uid = ""
|
||||
VAR card_security = ""
|
||||
```
|
||||
|
||||
If variable isn't declared, setVariable will silently fail or throw.
|
||||
|
||||
**Recommendation**: **Document Ink variable requirements**.
|
||||
|
||||
**Add to Technical Design**:
|
||||
```markdown
|
||||
### Required Ink Variables
|
||||
|
||||
For protocol integration to work, the following variables must be declared in NPC .ink files:
|
||||
|
||||
```ink
|
||||
// Card protocol variables (for NPCs with keycards)
|
||||
VAR card_protocol = "" // "EM4100", "MIFARE_Classic", "MIFARE_DESFire"
|
||||
VAR card_name = "" // Card display name
|
||||
VAR card_hex = "" // For EM4100
|
||||
VAR card_uid = "" // For MIFARE
|
||||
VAR card_security = "" // "low", "medium", "high"
|
||||
VAR card_clonable = false // Can this card be instantly cloned?
|
||||
|
||||
// For NPCs with multiple cards
|
||||
VAR card2_protocol = ""
|
||||
VAR card2_name = ""
|
||||
// etc.
|
||||
```
|
||||
|
||||
If variables aren't declared, protocol info won't be available to Ink conditionals.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Issue #7: Background Attacks Need Cleanup
|
||||
|
||||
**Problem**: Active attacks stored in array:
|
||||
```javascript
|
||||
this.activeAttacks = [];
|
||||
```
|
||||
|
||||
But no cleanup when:
|
||||
- Minigame is closed mid-attack
|
||||
- Player navigates away
|
||||
- Game is saved/loaded
|
||||
|
||||
**Recommendation**: **Add cleanup and persistence**.
|
||||
|
||||
```javascript
|
||||
// In rfid-attacks.js
|
||||
cleanup() {
|
||||
// Cancel all active attacks
|
||||
this.activeAttacks.forEach(attack => {
|
||||
if (attack.interval) {
|
||||
clearInterval(attack.interval);
|
||||
}
|
||||
});
|
||||
this.activeAttacks = [];
|
||||
}
|
||||
|
||||
// Store in window for persistence
|
||||
saveState() {
|
||||
return {
|
||||
activeAttacks: this.activeAttacks.map(a => ({
|
||||
type: a.type,
|
||||
uid: a.uid,
|
||||
cardName: a.cardName,
|
||||
startTime: a.startTime,
|
||||
foundKeys: a.foundKeys,
|
||||
currentSector: a.currentSector
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
restoreState(state) {
|
||||
// Restore attacks and resume progress
|
||||
// (implementation details)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Issue #8: No Error Handling for Unsupported Protocols
|
||||
|
||||
**Problem**: What if cloner firmware doesn't support protocol?
|
||||
|
||||
```javascript
|
||||
// User tries to clone MIFARE Classic
|
||||
// But cloner firmware only supports ['EM4100']
|
||||
```
|
||||
|
||||
Plan doesn't handle this case.
|
||||
|
||||
**Recommendation**: **Add firmware check before starting minigame**.
|
||||
|
||||
```javascript
|
||||
// In chat-helpers.js clone_keycard tag
|
||||
const cloner = window.inventory.items.find(item =>
|
||||
item?.scenarioData?.type === 'rfid_cloner'
|
||||
);
|
||||
|
||||
const cardProtocol = cardData.rfid_protocol || 'EM4100';
|
||||
|
||||
// Check firmware support
|
||||
if (cloner.scenarioData.firmware) {
|
||||
const supported = cloner.scenarioData.firmware.protocols || [];
|
||||
if (!supported.includes(cardProtocol)) {
|
||||
result.message = `⚠️ Flipper firmware doesn't support ${cardProtocol}`;
|
||||
if (ui) ui.showNotification(result.message, 'warning');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Proceed with clone...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Issue #9: DESFire UID Emulation Success Rate Not Defined
|
||||
|
||||
**Problem**: Plan says DESFire UID emulation works on "some systems" but doesn't define which.
|
||||
|
||||
```markdown
|
||||
Some systems only check UID and don't use encryption properly.
|
||||
```
|
||||
|
||||
How does game determine if emulation succeeds?
|
||||
|
||||
**Recommendation**: **Add door-level property for UID-only acceptance**.
|
||||
|
||||
```json
|
||||
{
|
||||
"locked": true,
|
||||
"lockType": "rfid",
|
||||
"requires": "ceo_keycard",
|
||||
"acceptsUIDOnly": false // NEW: True for low-security readers
|
||||
}
|
||||
```
|
||||
|
||||
```javascript
|
||||
// In unlock-system.js RFID case
|
||||
if (lockRequirements.lockType === 'rfid') {
|
||||
const cardId = lockRequirements.requires;
|
||||
|
||||
// Check if using DESFire UID-only emulation
|
||||
if (card.rfid_protocol === 'MIFARE_DESFire' &&
|
||||
!card.rfid_data.masterKeyKnown) {
|
||||
|
||||
// Check if door accepts UID-only
|
||||
if (!lockRequirements.acceptsUIDOnly) {
|
||||
showError("This reader requires full card authentication");
|
||||
return false;
|
||||
}
|
||||
|
||||
// UID matches?
|
||||
if (card.key_id === cardId || card.rfid_data.uid === requiredUID) {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Low Priority Issues
|
||||
|
||||
### Issue #10: CSS Color Accessibility
|
||||
|
||||
**Problem**: Color-coding security levels:
|
||||
```javascript
|
||||
color: '#FF6B6B' // Red for low security
|
||||
color: '#95E1D3' // Light green for high security
|
||||
```
|
||||
|
||||
Red/green color blindness (~8% of males) makes this hard to distinguish.
|
||||
|
||||
**Recommendation**: **Add icons in addition to colors**.
|
||||
|
||||
```javascript
|
||||
security: 'low',
|
||||
color: '#FF6B6B',
|
||||
icon: '⚠️' // Warning triangle
|
||||
|
||||
security: 'high',
|
||||
color: '#95E1D3',
|
||||
icon: '🔒' // Lock icon
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Issue #11: No Tests for Protocol-Specific Code
|
||||
|
||||
**Problem**: Plan mentions testing scenarios but no unit tests for:
|
||||
- Protocol detection logic
|
||||
- Capability checks
|
||||
- Key validation
|
||||
- Data migration
|
||||
|
||||
**Recommendation**: Add testing section (can defer to later).
|
||||
|
||||
---
|
||||
|
||||
### Issue #12: Attack Duration Magic Numbers
|
||||
|
||||
**Problem**: Hard-coded timings:
|
||||
```javascript
|
||||
const duration = 30000; // 30 seconds
|
||||
```
|
||||
|
||||
Should be constants for easy tuning.
|
||||
|
||||
**Recommendation**:
|
||||
```javascript
|
||||
const ATTACK_DURATIONS = {
|
||||
darkside: 30000, // 30 sec - crack from scratch
|
||||
nested: 10000, // 10 sec - crack with known key
|
||||
dictionary: 0 // Instant
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Simplified Implementation Approach
|
||||
|
||||
Based on review, here's a streamlined approach:
|
||||
|
||||
### Phase 1: Core Three Protocols (MVP)
|
||||
1. EM4100 (easy) - Current implementation
|
||||
2. MIFARE Classic (medium) - Add key attacks
|
||||
3. MIFARE DESFire (hard) - UID only
|
||||
|
||||
Skip HID Prox initially.
|
||||
|
||||
### Phase 2: Protocol Detection & UI
|
||||
1. Add RFID_PROTOCOLS constant
|
||||
2. Update card data display (dual format support)
|
||||
3. Add protocol info screen
|
||||
4. Color-code security levels
|
||||
|
||||
### Phase 3: MIFARE Attacks (in clone mode)
|
||||
1. Add MIFAREAttackManager
|
||||
2. Dictionary attack (instant)
|
||||
3. Darkside attack (30 sec animation)
|
||||
4. Nested attack (10 sec animation)
|
||||
5. Integrate into clone flow (not separate mode)
|
||||
|
||||
### Phase 4: Ink Integration
|
||||
1. Sync protocol variables
|
||||
2. Add start_mifare_attack tag
|
||||
3. Add save_uid_only tag
|
||||
4. Document required Ink variables
|
||||
|
||||
### Phase 5: Testing
|
||||
1. Test scenarios for each protocol
|
||||
2. Integration tests
|
||||
3. Backward compatibility tests
|
||||
|
||||
## Recommended Changes Summary
|
||||
|
||||
| Change | Priority | Time Saved/Impact |
|
||||
|--------|----------|-------------------|
|
||||
| Remove HID Prox | High | -2h development |
|
||||
| Merge attack into clone mode | High | Clearer UX, -1h dev |
|
||||
| Remove firmware system initially | High | -2h development |
|
||||
| Dual format support (no migration) | High | Simpler, safer |
|
||||
| Add firmware check before clone | Medium | Prevents errors |
|
||||
| Define acceptsUIDOnly for doors | Medium | Clear DESFire rules |
|
||||
| Add Ink variable documentation | Medium | Prevent confusion |
|
||||
| Add attack cleanup/persistence | Medium | Prevent bugs |
|
||||
| Use timing constants | Low | Better maintainability |
|
||||
| Add security icons | Low | Better accessibility |
|
||||
|
||||
**Total Time Saved**: ~5 hours
|
||||
**Total Clarity Improved**: Significant
|
||||
|
||||
## Conclusion
|
||||
|
||||
The original plan is solid but can be **streamlined by 25%** while improving clarity:
|
||||
- Remove HID Prox (minimal gameplay value)
|
||||
- Merge attack mode into clone mode (simpler state machine)
|
||||
- Skip firmware system initially (can add later)
|
||||
- Use dual format support instead of migration (safer)
|
||||
- Add missing error handling (firmware checks, UID acceptance)
|
||||
|
||||
**Recommendation**: Update implementation plan with these improvements before beginning development.
|
||||
Reference in New Issue
Block a user