From fb7f836abd61978eb056ac2c976ecb4b4e785dd6 Mon Sep 17 00:00:00 2001 From: michael Date: Thu, 22 Jan 2026 18:08:38 -0800 Subject: [PATCH] Add chip detection system for NTAG, DESFire, ISO 15693, and JavaCard Implement comprehensive NFC chip identification using platform-specific commands and heuristics: - NTAG/Ultralight detection via GET_VERSION command - DESFire EV1/EV2/EV3 and NTAG 424 DNA detection - ISO 15693 detection with memory-based SLIX/ICODE DNA differentiation - JavaCard/JCOP detection via CPLC data - MIFARE Classic detection via SAK values Use block count as authoritative source for NXP ISO 15693 chips since IC reference values overlap between SLIX and ICODE DNA families. Co-Authored-By: Claude Opus 4.5 --- src/hooks/useScan.ts | 19 +- src/screens/ResultScreen.tsx | 274 +++++++++-- src/screens/ScanScreen.tsx | 16 +- src/services/detection/desfire.ts | 407 ++++++++++++++++ src/services/detection/detector.ts | 406 ++++++++++++++++ src/services/detection/index.ts | 23 + src/services/detection/iso15693.ts | 748 +++++++++++++++++++++++++++++ src/services/detection/javacard.ts | 342 +++++++++++++ src/services/detection/mifare.ts | 387 +++++++++++++++ src/services/detection/ntag.ts | 305 ++++++++++++ src/services/nfc/NFCManager.ts | 133 ++++- src/services/nfc/commands.ts | 341 +++++++++++++ src/types/detection.ts | 468 ++++++++++++++++++ src/types/navigation.ts | 2 + tsconfig.json | 21 +- 15 files changed, 3831 insertions(+), 61 deletions(-) create mode 100644 src/services/detection/desfire.ts create mode 100644 src/services/detection/detector.ts create mode 100644 src/services/detection/index.ts create mode 100644 src/services/detection/iso15693.ts create mode 100644 src/services/detection/javacard.ts create mode 100644 src/services/detection/mifare.ts create mode 100644 src/services/detection/ntag.ts create mode 100644 src/services/nfc/commands.ts create mode 100644 src/types/detection.ts diff --git a/src/hooks/useScan.ts b/src/hooks/useScan.ts index d51712a..6ffbba3 100644 --- a/src/hooks/useScan.ts +++ b/src/hooks/useScan.ts @@ -5,13 +5,17 @@ import {useState, useCallback, useEffect, useRef} from 'react'; import {nfcManager} from '../services/nfc'; +import {detectChip} from '../services/detection'; import type {ScanState, RawTagData, ScanError, NFCStatus} from '../types/nfc'; +import type {Transponder} from '../types/detection'; export interface UseScanResult { /** Current scan state */ state: ScanState; /** Scanned tag data (when state is 'success') */ tag: RawTagData | null; + /** Detected transponder info (when detection succeeds) */ + transponder: Transponder | null; /** Scan error (when state is 'error') */ error: ScanError | null; /** NFC status (supported and enabled) */ @@ -32,6 +36,7 @@ export interface UseScanResult { export function useScan(): UseScanResult { const [state, setState] = useState('idle'); const [tag, setTag] = useState(null); + const [transponder, setTransponder] = useState(null); const [error, setError] = useState(null); const [nfcStatus, setNfcStatus] = useState({ isSupported: false, @@ -76,6 +81,7 @@ export function useScan(): UseScanResult { scanInProgress.current = true; setState('scanning'); setTag(null); + setTransponder(null); setError(null); try { @@ -107,8 +113,12 @@ export function useScan(): UseScanResult { return; } - // Perform the scan - const result = await nfcManager.scanTag(); + // Perform scan with detection in one session + const result = await nfcManager.scanWithDetection(async tagData => { + // Run chip detection while NFC session is still active + const detectionResult = await detectChip(tagData); + return detectionResult.success ? detectionResult.transponder : null; + }); if (!isMounted.current) { return; @@ -125,6 +135,9 @@ export function useScan(): UseScanResult { } else if (result.tag) { setState('success'); setTag(result.tag); + if (result.detection) { + setTransponder(result.detection); + } } else { setState('error'); setError({ @@ -160,6 +173,7 @@ export function useScan(): UseScanResult { const reset = useCallback(() => { setState('idle'); setTag(null); + setTransponder(null); setError(null); }, []); @@ -176,6 +190,7 @@ export function useScan(): UseScanResult { return { state, tag, + transponder, error, nfcStatus, startScan, diff --git a/src/screens/ResultScreen.tsx b/src/screens/ResultScreen.tsx index e9ef2af..59c5a12 100644 --- a/src/screens/ResultScreen.tsx +++ b/src/screens/ResultScreen.tsx @@ -1,22 +1,124 @@ import React from 'react'; import {StyleSheet, View, ScrollView, Linking} from 'react-native'; -import {Button, Text, Surface, Divider} from 'react-native-paper'; +import {Button, Text, Surface, Divider, Chip} from 'react-native-paper'; import type {ResultScreenProps} from '../types/navigation'; import {DTColors} from '../theme'; export function ResultScreen({route, navigation}: ResultScreenProps) { - const {tagData} = route.params; + const {tagData, transponder} = route.params; const handleConversionLink = () => { Linking.openURL('https://dngr.us/conversion'); }; + // Determine card colors based on detection + const getCloneabilityColor = () => { + if (!transponder) return DTColors.light; + return transponder.isCloneable ? DTColors.modeSuccess : DTColors.modeWarning; + }; + + const getConfidenceLabel = () => { + if (!transponder) return null; + const colors = { + high: DTColors.modeSuccess, + medium: DTColors.modeEmphasis, + low: DTColors.modeWarning, + }; + return {color: colors[transponder.confidence], label: `${transponder.confidence.toUpperCase()} CONFIDENCE`}; + }; + return ( + {/* Chip Identification Card */} + {transponder && ( + + + CHIP IDENTIFIED + + + + + {transponder.chipName} + + + + + {transponder.family} + + {getConfidenceLabel() && ( + + {getConfidenceLabel()!.label} + + )} + + + {transponder.memorySize && ( + + + MEMORY + + + {transponder.memorySize} bytes + + + )} + + + + CLONEABLE + + + {transponder.isCloneable ? 'YES' : 'NO'} + + + + {transponder.cloneabilityNote && ( + + {transponder.cloneabilityNote} + + )} + + )} + + {/* SAK Swap Detection Card */} + {transponder?.sakSwapInfo?.hasSakSwap && ( + + + SAK SWAP DETECTED + + + + + {transponder.sakSwapInfo.swapType?.replace(/_/g, ' ').toUpperCase()} + + + + {transponder.sakSwapInfo.description} + + + {transponder.sakSwapInfo.notes && transponder.sakSwapInfo.notes.length > 0 && ( + + {transponder.sakSwapInfo.notes.map((note, index) => ( + + • {note} + + ))} + + )} + + )} + + {/* Raw Tag Data Card */} - TAG DETECTED + RAW TAG DATA @@ -80,35 +182,36 @@ export function ResultScreen({route, navigation}: ResultScreenProps) { )} - - - COMPATIBLE IMPLANTS - - + {/* No Detection Card (fallback) */} + {!transponder && ( + + + CHIP NOT IDENTIFIED + + + + Unable to identify this chip type. It may be unsupported or require advanced detection. + + + )} - - Detection coming in Phase 3-5 - - - - Full chip identification and product matching will be implemented in - later phases. - - - - - - Can't find a match? Check out our conversion service. - - - + {/* Conversion Service Card - only show if chip is NOT cloneable */} + {(!transponder || !transponder.isCloneable) && ( + + + Can't clone this chip? Check out our conversion service. + + + + )} + {/* Action Buttons */}