Fixed MFC 1k/4k issue and Ultralight detection
This commit is contained in:
@@ -79,6 +79,40 @@ export const CHIP_INFO: Partial<Record<ChipType, ChipInfo>> = {
|
||||
memoryNote: '1904 bytes user memory',
|
||||
},
|
||||
|
||||
// MIFARE Ultralight family
|
||||
[ChipType.ULTRALIGHT]: {
|
||||
description:
|
||||
'Original MIFARE Ultralight - a simple, low-cost NFC tag with minimal memory.',
|
||||
commonUses: ['Single-use tickets', 'Event passes', 'Disposable tags'],
|
||||
capabilities: ['NFC Forum Type 2', 'OTP area', 'Lock bits'],
|
||||
securityLevel: 'low',
|
||||
memoryNote: '48 bytes user memory',
|
||||
},
|
||||
[ChipType.ULTRALIGHT_C]: {
|
||||
description:
|
||||
'Ultralight with 3DES authentication for basic security applications.',
|
||||
commonUses: ['Limited-use tickets', 'Access tokens', 'Loyalty cards'],
|
||||
capabilities: ['NFC Forum Type 2', '3DES authentication', 'Counter'],
|
||||
securityLevel: 'medium',
|
||||
memoryNote: '144 bytes user memory',
|
||||
},
|
||||
[ChipType.ULTRALIGHT_EV1]: {
|
||||
description:
|
||||
'Enhanced Ultralight with password protection and originality check.',
|
||||
commonUses: ['Event tickets', 'Brand protection', 'Gaming tokens'],
|
||||
capabilities: ['Password protection', 'Originality signature', 'Counter'],
|
||||
securityLevel: 'low',
|
||||
memoryNote: '48 or 128 bytes user memory',
|
||||
},
|
||||
[ChipType.ULTRALIGHT_AES]: {
|
||||
description:
|
||||
'Ultralight with AES-128 authentication for secure applications.',
|
||||
commonUses: ['Secure tickets', 'Brand protection', 'Limited-use passes'],
|
||||
capabilities: ['AES-128 authentication', 'Counter', 'Originality signature'],
|
||||
securityLevel: 'high',
|
||||
memoryNote: '540 bytes user memory',
|
||||
},
|
||||
|
||||
// MIFARE Classic
|
||||
[ChipType.MIFARE_CLASSIC_1K]: {
|
||||
description:
|
||||
|
||||
@@ -64,6 +64,18 @@ const SLIX_COMPATIBLE: ChipType[] = [
|
||||
ChipType.SLIX_L,
|
||||
];
|
||||
|
||||
/**
|
||||
* MIFARE Ultralight compatible chips (for Gen4 magic implants)
|
||||
* Note: Ultralight AES cannot be cloned if AES protection is active
|
||||
*/
|
||||
const ULTRALIGHT_COMPATIBLE: ChipType[] = [
|
||||
ChipType.ULTRALIGHT,
|
||||
ChipType.ULTRALIGHT_C,
|
||||
ChipType.ULTRALIGHT_EV1,
|
||||
ChipType.ULTRALIGHT_NANO,
|
||||
ChipType.ULTRALIGHT_AES,
|
||||
];
|
||||
|
||||
export const PRODUCTS: Product[] = [
|
||||
// ==========================================================================
|
||||
// X-Series (Glass Capsule) Implants - NFC
|
||||
@@ -136,8 +148,9 @@ export const PRODUCTS: Product[] = [
|
||||
'Dual-frequency',
|
||||
'NTAG216 NFC chip',
|
||||
'888 bytes user memory',
|
||||
'URL/contact sharing',
|
||||
'T5577 125kHz chip',
|
||||
'LF access control',
|
||||
'Access control (LF)',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/next/',
|
||||
canReceiveClone: true,
|
||||
@@ -158,7 +171,7 @@ export const PRODUCTS: Product[] = [
|
||||
'URL/contact sharing',
|
||||
'Green, Blue, and White LEDs available (HF)',
|
||||
'T5577 125kHz chip',
|
||||
'LF access control',
|
||||
'Access control (LF)',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/next-v2/',
|
||||
canReceiveClone: true,
|
||||
@@ -175,14 +188,14 @@ export const PRODUCTS: Product[] = [
|
||||
compatibleChips: [...MIFARE_CLASSIC_COMPATIBLE],
|
||||
features: [
|
||||
'Magic MIFARE Classic chip',
|
||||
'Changeable UID',
|
||||
'Changeable UID (4-byte)',
|
||||
'Gen1a and gen2 magic options',
|
||||
'T5577 125kHz chip',
|
||||
'Clone access cards',
|
||||
'Access control (LF)',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/xmagic/',
|
||||
canReceiveClone: true,
|
||||
exactMatch: false,
|
||||
notes: 'Can clone MIFARE Classic cards (requires Android + keys)',
|
||||
},
|
||||
|
||||
// ==========================================================================
|
||||
@@ -200,7 +213,8 @@ export const PRODUCTS: Product[] = [
|
||||
'DESFire EV3 chip',
|
||||
'8KB memory',
|
||||
'Latest security features',
|
||||
'Transaction MAC',
|
||||
'Access control (HF)',
|
||||
'URL/contact sharing',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/xdf3/',
|
||||
canReceiveClone: false,
|
||||
@@ -221,14 +235,12 @@ export const PRODUCTS: Product[] = [
|
||||
compatibleChips: [...MIFARE_CLASSIC_COMPATIBLE],
|
||||
features: [
|
||||
'Magic MIFARE Classic 1K',
|
||||
'Changeable UID',
|
||||
'Clone access cards',
|
||||
'Gen1a compatible',
|
||||
'Gen1a and gen2 magic options',
|
||||
'Changeable UID (4-byte)',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/xm1/',
|
||||
canReceiveClone: true,
|
||||
exactMatch: false,
|
||||
notes: 'Can clone MIFARE Classic 1K cards (requires Android + keys)',
|
||||
},
|
||||
|
||||
// ==========================================================================
|
||||
@@ -243,6 +255,7 @@ export const PRODUCTS: Product[] = [
|
||||
categories: [ProductCategory.SECURE],
|
||||
compatibleChips: [ChipType.NTAG424_DNA, ChipType.NTAG424_DNA_TT, ChipType.ICODE_DNA],
|
||||
features: [
|
||||
'VivoKey Ecosystem',
|
||||
'Spark Actions',
|
||||
'Verify API',
|
||||
],
|
||||
@@ -265,9 +278,11 @@ export const PRODUCTS: Product[] = [
|
||||
compatibleChips: [...NTAG21X_COMPATIBLE, ...NTAG_I2C_COMPATIBLE],
|
||||
features: [
|
||||
'NTAG216 NFC chip',
|
||||
'888 bytes user memory',
|
||||
'URL/contact sharing',
|
||||
'T5577 125kHz chip',
|
||||
'Enhanced read range',
|
||||
'Bioresin encapsulation',
|
||||
'Enhanced read range',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/dnext/',
|
||||
canReceiveClone: true,
|
||||
@@ -281,17 +296,20 @@ export const PRODUCTS: Product[] = [
|
||||
'Bioresin dual-frequency implant with Ultimate Gen4 magic MIFARE and T5577 for maximum compatibility.',
|
||||
formFactor: FormFactor.BIORESIN,
|
||||
categories: [ProductCategory.DUAL_FREQUENCY, ProductCategory.ACCESS],
|
||||
compatibleChips: [...MIFARE_CLASSIC_COMPATIBLE],
|
||||
compatibleChips: [...MIFARE_CLASSIC_COMPATIBLE, ...ULTRALIGHT_COMPATIBLE],
|
||||
features: [
|
||||
'Ultimate Gen4 magic chip',
|
||||
'Changeable UID',
|
||||
'Access control (HF)',
|
||||
'T5577 125kHz chip',
|
||||
'Changeable UID',
|
||||
'Enhanced cloning capability',
|
||||
'Access control (LF)',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/dug4t/',
|
||||
canReceiveClone: true,
|
||||
exactMatch: false,
|
||||
notes: 'Can clone MIFARE Classic cards (requires Android + keys)',
|
||||
notes: 'Supports 1K and 4K cloning. Also includes T5577 for LF.',
|
||||
},
|
||||
|
||||
// ==========================================================================
|
||||
@@ -308,7 +326,7 @@ export const PRODUCTS: Product[] = [
|
||||
features: [
|
||||
'NTAG216 chip',
|
||||
'888 bytes user memory',
|
||||
'Flexible form factor',
|
||||
'URL/contact sharing',
|
||||
'Extended read range',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/flexnt/',
|
||||
@@ -331,7 +349,7 @@ export const PRODUCTS: Product[] = [
|
||||
'DESFire EV2 chip',
|
||||
'8KB memory',
|
||||
'AES-128 encryption',
|
||||
'Flexible form factor',
|
||||
'URL/contact sharing',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/flexdf2/',
|
||||
canReceiveClone: false,
|
||||
@@ -343,24 +361,6 @@ export const PRODUCTS: Product[] = [
|
||||
// ==========================================================================
|
||||
// Flex Implants - Magic/Cloning
|
||||
// ==========================================================================
|
||||
{
|
||||
id: 'flexm1',
|
||||
name: 'flexM1',
|
||||
description: 'Flexible magic MIFARE Classic 1K implant for cloning access cards.',
|
||||
formFactor: FormFactor.FLEX,
|
||||
categories: [ProductCategory.ACCESS],
|
||||
compatibleChips: [...MIFARE_CLASSIC_COMPATIBLE],
|
||||
features: [
|
||||
'Magic MIFARE Classic 1K',
|
||||
'Changeable UID',
|
||||
'Flexible form factor',
|
||||
'Extended read range',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/flexm1/',
|
||||
canReceiveClone: true,
|
||||
exactMatch: false,
|
||||
notes: 'Can clone MIFARE Classic 1K cards (requires Android + keys)',
|
||||
},
|
||||
{
|
||||
id: 'flexm1-v2',
|
||||
name: 'flexM1 v2',
|
||||
@@ -377,7 +377,6 @@ export const PRODUCTS: Product[] = [
|
||||
url: 'https://dangerousthings.com/product/flexm1-v2/',
|
||||
canReceiveClone: true,
|
||||
exactMatch: false,
|
||||
notes: 'Can clone MIFARE Classic 1K cards (requires Android + keys)',
|
||||
},
|
||||
|
||||
// ==========================================================================
|
||||
@@ -394,9 +393,9 @@ export const PRODUCTS: Product[] = [
|
||||
features: [
|
||||
'JCOP4 secure element',
|
||||
'Fidesmo platform',
|
||||
'Payment capable',
|
||||
'FIDO2/WebAuthn',
|
||||
'OTP support',
|
||||
'URL/contact sharing',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/apex-flex/',
|
||||
canReceiveClone: false,
|
||||
@@ -416,6 +415,9 @@ export const PRODUCTS: Product[] = [
|
||||
'Full JavaCard access',
|
||||
'GlobalPlatformPro compatible',
|
||||
'Custom applet support',
|
||||
'FIDO2/WebAuthn',
|
||||
'OTP support',
|
||||
'URL/contact sharing',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/flexsecure/',
|
||||
canReceiveClone: false,
|
||||
@@ -436,7 +438,6 @@ export const PRODUCTS: Product[] = [
|
||||
features: [
|
||||
'HID iCLASS SE compatible',
|
||||
'Enterprise access control',
|
||||
'Flexible form factor',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/flexclass/',
|
||||
canReceiveClone: false,
|
||||
@@ -449,17 +450,16 @@ export const PRODUCTS: Product[] = [
|
||||
description: 'Flexible Ultimate Gen4 magic MIFARE implant for cloning access cards.',
|
||||
formFactor: FormFactor.FLEX,
|
||||
categories: [ProductCategory.ACCESS],
|
||||
compatibleChips: [...MIFARE_CLASSIC_COMPATIBLE],
|
||||
compatibleChips: [...MIFARE_CLASSIC_COMPATIBLE, ...ULTRALIGHT_COMPATIBLE],
|
||||
features: [
|
||||
'Ultimate Gen4 magic chip',
|
||||
'Changeable UID',
|
||||
'Flexible form factor',
|
||||
'Enhanced cloning capability',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/flexug4/',
|
||||
canReceiveClone: true,
|
||||
exactMatch: false,
|
||||
notes: 'Can clone MIFARE Classic cards (requires Android + keys)',
|
||||
notes: 'Supports 1K and 4K cloning.',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { StyleSheet, View, ScrollView, Linking, TouchableOpacity } from 'react-n
|
||||
import { Button, Text, Surface, Divider, Chip } from 'react-native-paper';
|
||||
import type { ResultScreenProps } from '../types/navigation';
|
||||
import { DTColors } from '../theme';
|
||||
import { matchChipToProducts, getMatchSummary, getDesfireEvMismatchWarning } from '../services/matching';
|
||||
import { matchChipToProducts, getMatchSummary, getDesfireEvMismatchWarning, getMifareClassicCapacityWarning } from '../services/matching';
|
||||
import { Product } from '../types/products';
|
||||
import { getChipInfo, getChipFamilyInfo, getSecurityLevelDescription } from '../data/chipInfo';
|
||||
import { getChipFamily } from '../types/detection';
|
||||
@@ -296,6 +296,13 @@ export function ResultScreen({ route, navigation }: ResultScreenProps) {
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{/* MIFARE Classic 4K → 1K capacity warning */}
|
||||
{getMifareClassicCapacityWarning(transponder.type, product) && (
|
||||
<Text variant="bodySmall" style={styles.evMismatchWarning}>
|
||||
⚠️ {getMifareClassicCapacityWarning(transponder.type, product)}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
<View style={styles.productFeatures}>
|
||||
{product.features.slice(0, 3).map((feature, idx) => (
|
||||
<Text key={idx} variant="bodySmall" style={styles.productFeature}>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React, {useCallback, useEffect} from 'react';
|
||||
import {StyleSheet, View, Platform} from 'react-native';
|
||||
import {Button, Text} from 'react-native-paper';
|
||||
import type {ScanScreenProps} from '../types/navigation';
|
||||
import {DTColors} from '../theme';
|
||||
import {useScan} from '../hooks';
|
||||
import {getScanInstructions} from '../services/nfc';
|
||||
import {ScanAnimation} from '../components';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { StyleSheet, View, Platform } from 'react-native';
|
||||
import { Button, Text } from 'react-native-paper';
|
||||
import type { ScanScreenProps } from '../types/navigation';
|
||||
import { DTColors } from '../theme';
|
||||
import { useScan } from '../hooks';
|
||||
import { getScanInstructions } from '../services/nfc';
|
||||
import { ScanAnimation } from '../components';
|
||||
|
||||
export function ScanScreen({navigation}: ScanScreenProps) {
|
||||
export function ScanScreen({ navigation }: ScanScreenProps) {
|
||||
const {
|
||||
state,
|
||||
tag,
|
||||
@@ -194,6 +194,12 @@ export function ScanScreen({navigation}: ScanScreenProps) {
|
||||
<Text variant="bodyLarge" style={styles.instructionText}>
|
||||
{getScanInstructions()}
|
||||
</Text>
|
||||
<Text variant="headlineSmall" style={{ paddingTop: 15, paddingBottom: 10 }}>
|
||||
Not Scanning?
|
||||
</Text>
|
||||
<Text variant="bodyLarge" style={styles.instructionText}>
|
||||
Phones can only read high frequency (13.56 MHz) -- low and ultra high frequency transponders cannot be scanned.
|
||||
</Text>
|
||||
{scanProgress && scanProgress.current > 1 && (
|
||||
<Text variant="bodySmall" style={styles.progressText}>
|
||||
{scanProgress.step}
|
||||
|
||||
@@ -146,12 +146,29 @@ export async function detectChip(
|
||||
|
||||
// If we have MifareClassic tech type and NO IsoDep, it's definitely Classic
|
||||
if (hasMifareClassicTech && !hasIsoDepTech) {
|
||||
// Determine 1K vs 4K based on SAK or UID length
|
||||
// Determine 1K vs 4K vs Mini
|
||||
// Priority: mifareClassic.size (most reliable on Android) > SAK > UID length
|
||||
let chipType = ChipType.MIFARE_CLASSIC_1K; // Default to 1K
|
||||
let memorySize = 1024;
|
||||
|
||||
// SAK 0x18 or 0x38 indicates 4K
|
||||
if (sak === 0x18 || sak === 0x38 || sak === 0x98) {
|
||||
// First, check mifareClassic object from Android (most reliable)
|
||||
if (rawData.mifareClassic?.size) {
|
||||
const size = rawData.mifareClassic.size;
|
||||
console.log('[Detector] Using mifareClassic.size:', size);
|
||||
if (size >= 4096) {
|
||||
chipType = ChipType.MIFARE_CLASSIC_4K;
|
||||
memorySize = 4096;
|
||||
} else if (size >= 1024) {
|
||||
chipType = ChipType.MIFARE_CLASSIC_1K;
|
||||
memorySize = 1024;
|
||||
} else if (size >= 320) {
|
||||
chipType = ChipType.MIFARE_CLASSIC_MINI;
|
||||
memorySize = 320;
|
||||
}
|
||||
}
|
||||
// Fallback to SAK-based detection
|
||||
// SAK 0x18, 0x38, or 0x98 indicates 4K
|
||||
else if (sak === 0x18 || sak === 0x38 || sak === 0x98) {
|
||||
chipType = ChipType.MIFARE_CLASSIC_4K;
|
||||
memorySize = 4096;
|
||||
}
|
||||
@@ -233,8 +250,27 @@ export async function detectChip(
|
||||
}),
|
||||
};
|
||||
}
|
||||
// If GET_VERSION failed but tag looks like Type 2, mark as unknown NTAG
|
||||
if (sak === 0x00 && techTypes.some(t => t.includes('NfcA'))) {
|
||||
// If GET_VERSION failed, check for MIFARE Ultralight
|
||||
// Original Ultralight doesn't support GET_VERSION (only EV1+ does)
|
||||
// SAK can be 0x00 or undefined for Ultralight
|
||||
if ((sak === 0x00 || sak === undefined) && techTypes.some(t => t.includes('NfcA'))) {
|
||||
// Check for MifareUltralight tech type (Android provides this)
|
||||
const hasMifareUltralightTech = techTypes.some(t =>
|
||||
t.includes('MifareUltralight'),
|
||||
);
|
||||
|
||||
if (hasMifareUltralightTech) {
|
||||
console.log('[Detector] MifareUltralight tech detected, identifying as original Ultralight');
|
||||
return {
|
||||
success: true,
|
||||
transponder: createTransponder(ChipType.ULTRALIGHT, rawData, {
|
||||
memorySize: 48, // Original Ultralight has 48 bytes user memory
|
||||
confidence: 'medium',
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
// No MifareUltralight tech - fall back to unknown NTAG
|
||||
console.log('[Detector] NTAG detection failed, falling back to NTAG_UNKNOWN');
|
||||
return {
|
||||
success: true,
|
||||
|
||||
@@ -11,4 +11,5 @@ export {
|
||||
canCloneToProduct,
|
||||
getDesfireEvLevel,
|
||||
getDesfireEvMismatchWarning,
|
||||
getMifareClassicCapacityWarning,
|
||||
} from './matcher';
|
||||
|
||||
@@ -35,7 +35,6 @@ export function matchChipToProducts(chip: Transponder): MatchResult {
|
||||
if (product.canReceiveClone && cloneability?.cloneable) {
|
||||
if ((product.name.startsWith("xMagic") || product.name.startsWith("xM1") || product.name.startsWith("flexM1")) && chip.rawData.uid.replaceAll(":", "").length / 2 !== 4) {
|
||||
// This skips things without a 4-byte UID
|
||||
|
||||
} else {
|
||||
cloneTargets.push(product);
|
||||
}
|
||||
@@ -177,6 +176,32 @@ export function getDesfireEvLevel(chipType: ChipType): DesfireEvLevel | null {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IDs of products that only support MIFARE Classic 1K (not 4K)
|
||||
*/
|
||||
const MIFARE_1K_ONLY_PRODUCTS = new Set(['xmagic', 'xm1', 'flexm1-v2']);
|
||||
|
||||
/**
|
||||
* Check if a scanned 4K card is being matched to a 1K-only implant
|
||||
* Returns warning message if capacity mismatch, null if no issue
|
||||
*/
|
||||
export function getMifareClassicCapacityWarning(
|
||||
chipType: ChipType,
|
||||
product: Product,
|
||||
): string | null {
|
||||
// Only applies to MIFARE Classic 4K cards
|
||||
if (chipType !== ChipType.MIFARE_CLASSIC_4K) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Only warn for 1K-only implants
|
||||
if (!MIFARE_1K_ONLY_PRODUCTS.has(product.id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return 'This implant has 1K memory only - might not have capacity to clone your 4K card.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there's a DESFire EV mismatch between chip and product
|
||||
* Returns warning message if mismatch, null if no issue
|
||||
|
||||
@@ -12,6 +12,7 @@ import type {
|
||||
ScanErrorType,
|
||||
NfcTechType,
|
||||
NdefRecord,
|
||||
MifareClassicInfo,
|
||||
} from '../../types/nfc';
|
||||
|
||||
/**
|
||||
@@ -116,6 +117,56 @@ function parseAts(tag: TagEvent): {ats?: string; historicalBytes?: string} {
|
||||
return {ats, historicalBytes};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse MIFARE Classic info from TagEvent (Android only)
|
||||
*
|
||||
* Uses NDEF maxSize to determine card capacity:
|
||||
* - 4K: maxSize ~3356 bytes (NDEF capacity)
|
||||
* - 1K: maxSize ~716 bytes (NDEF capacity)
|
||||
* - Mini: maxSize ~80 bytes (NDEF capacity)
|
||||
*/
|
||||
function parseMifareClassic(tag: TagEvent): MifareClassicInfo | undefined {
|
||||
const techTypes = tag.techTypes || [];
|
||||
const isMifareClassic = techTypes.some(t => t.includes('MifareClassic'));
|
||||
|
||||
if (!isMifareClassic) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Use NDEF maxSize to determine card type
|
||||
// MIFARE Classic 4K has ~3356 bytes NDEF capacity
|
||||
// MIFARE Classic 1K has ~716 bytes NDEF capacity
|
||||
const maxSize = (tag as any).maxSize;
|
||||
|
||||
if (typeof maxSize === 'number' && maxSize > 0) {
|
||||
let size: number;
|
||||
let sectorCount: number;
|
||||
let blockCount: number;
|
||||
|
||||
if (maxSize >= 2000) {
|
||||
// 4K card (NDEF maxSize ~3356)
|
||||
size = 4096;
|
||||
sectorCount = 40;
|
||||
blockCount = 256;
|
||||
} else if (maxSize >= 500) {
|
||||
// 1K card (NDEF maxSize ~716)
|
||||
size = 1024;
|
||||
sectorCount = 16;
|
||||
blockCount = 64;
|
||||
} else {
|
||||
// Mini card (NDEF maxSize ~80)
|
||||
size = 320;
|
||||
sectorCount = 5;
|
||||
blockCount = 20;
|
||||
}
|
||||
|
||||
console.log('[NFCManager] MIFARE Classic from maxSize:', {maxSize, size, sectorCount, blockCount});
|
||||
return {size, sectorCount, blockCount};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse NDEF records from TagEvent
|
||||
*/
|
||||
@@ -167,6 +218,7 @@ function tagEventToRawData(tag: TagEvent): RawTagData {
|
||||
const atqa = parseAtqa(tag);
|
||||
const {ats, historicalBytes} = parseAts(tag);
|
||||
const ndefRecords = parseNdefRecords(tag);
|
||||
const mifareClassic = parseMifareClassic(tag);
|
||||
|
||||
const isoDep = (tag as any).isoDep;
|
||||
const iso7816 = (tag as any).iso7816;
|
||||
@@ -204,6 +256,7 @@ function tagEventToRawData(tag: TagEvent): RawTagData {
|
||||
historicalBytes,
|
||||
maxTransceiveLength,
|
||||
ndefRecords,
|
||||
mifareClassic,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -366,7 +419,10 @@ class NFCManagerService {
|
||||
};
|
||||
}
|
||||
|
||||
return {tag: tagEventToRawData(tag)};
|
||||
// Convert to RawTagData
|
||||
const rawData = tagEventToRawData(tag);
|
||||
|
||||
return {tag: rawData};
|
||||
} catch (error) {
|
||||
return {error: categorizeError(error)};
|
||||
} finally {
|
||||
|
||||
@@ -207,7 +207,8 @@ export async function transceiveNfcA(command: number[]): Promise<number[]> {
|
||||
const response = await NfcManager.nfcAHandler.transceive(command);
|
||||
return Array.from(response);
|
||||
} catch (error) {
|
||||
console.error('[commands] NfcA transceive failed:', error);
|
||||
// Use debug level - some failures are expected (e.g., GET_VERSION on original Ultralight)
|
||||
console.debug('[commands] NfcA transceive failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,18 @@ export interface NdefRecord {
|
||||
payload: number[];
|
||||
}
|
||||
|
||||
/**
|
||||
* MIFARE Classic specific info from Android
|
||||
*/
|
||||
export interface MifareClassicInfo {
|
||||
/** Total memory size in bytes */
|
||||
size: number;
|
||||
/** Number of sectors */
|
||||
sectorCount: number;
|
||||
/** Total number of blocks */
|
||||
blockCount: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Raw tag data from NFC scan
|
||||
*/
|
||||
@@ -52,6 +64,8 @@ export interface RawTagData {
|
||||
maxTransceiveLength?: number;
|
||||
/** Cached NDEF records (if tag supports NDEF) */
|
||||
ndefRecords?: NdefRecord[];
|
||||
/** MIFARE Classic info (Android only) */
|
||||
mifareClassic?: MifareClassicInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user