Add product matching service with DT implant catalog
- Add product catalog with current DT implants (X-Series, Flex, Bioresin) - Implement chip-to-product matching algorithm - Add DESFire EV level tracking with mismatch warnings - Display compatible implants on ResultScreen with product details - Remove discontinued products: xDF (EV1), xDF2 (EV2), flexDF (EV1) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
571
src/data/products.ts
Normal file
571
src/data/products.ts
Normal file
@@ -0,0 +1,571 @@
|
||||
/**
|
||||
* Dangerous Things Product Catalog
|
||||
*
|
||||
* This contains the implant products available from Dangerous Things
|
||||
* and their chip compatibility information.
|
||||
*
|
||||
* Updated based on https://dangerousthings.com/category/implants/
|
||||
* Discontinued products removed per https://forum.dangerousthings.com/t/shes-dead-jim/26308
|
||||
*/
|
||||
|
||||
import {ChipType} from '../types/detection';
|
||||
import {
|
||||
Product,
|
||||
FormFactor,
|
||||
ProductCategory,
|
||||
ChipProductMap,
|
||||
} from '../types/products';
|
||||
|
||||
/**
|
||||
* Chip types that can be cloned to NTAG216-based implants
|
||||
* Any NTAG21x data can be written to an NTAG216 (same NFC Type 2 protocol)
|
||||
*/
|
||||
const NTAG21X_COMPATIBLE: ChipType[] = [
|
||||
ChipType.NTAG213,
|
||||
ChipType.NTAG215,
|
||||
ChipType.NTAG216,
|
||||
];
|
||||
|
||||
/**
|
||||
* NTAG I2C compatible chips
|
||||
*/
|
||||
const NTAG_I2C_COMPATIBLE: ChipType[] = [
|
||||
ChipType.NTAG_I2C_1K,
|
||||
ChipType.NTAG_I2C_2K,
|
||||
ChipType.NTAG_I2C_PLUS_1K,
|
||||
ChipType.NTAG_I2C_PLUS_2K,
|
||||
];
|
||||
|
||||
/**
|
||||
* MIFARE Classic compatible chips (for magic card cloning)
|
||||
*/
|
||||
const MIFARE_CLASSIC_COMPATIBLE: ChipType[] = [
|
||||
ChipType.MIFARE_CLASSIC_1K,
|
||||
ChipType.MIFARE_CLASSIC_4K,
|
||||
ChipType.MIFARE_CLASSIC_MINI,
|
||||
];
|
||||
|
||||
/**
|
||||
* All DESFire chips - any DESFire product works with any DESFire chip
|
||||
*/
|
||||
const DESFIRE_ALL: ChipType[] = [
|
||||
ChipType.DESFIRE_EV1,
|
||||
ChipType.DESFIRE_EV2,
|
||||
ChipType.DESFIRE_EV3,
|
||||
];
|
||||
|
||||
/**
|
||||
* ICODE SLIX compatible
|
||||
*/
|
||||
const SLIX_COMPATIBLE: ChipType[] = [
|
||||
ChipType.SLIX,
|
||||
ChipType.SLIX2,
|
||||
ChipType.SLIX_S,
|
||||
ChipType.SLIX_L,
|
||||
];
|
||||
|
||||
export const PRODUCTS: Product[] = [
|
||||
// ==========================================================================
|
||||
// X-Series (Glass Capsule) Implants - NFC
|
||||
// ==========================================================================
|
||||
{
|
||||
id: 'xnt',
|
||||
name: 'xNT',
|
||||
description:
|
||||
'NFC Type 2 implant with 888 bytes of user memory. The original and most popular NFC implant.',
|
||||
formFactor: FormFactor.X_SERIES,
|
||||
categories: [ProductCategory.NFC],
|
||||
compatibleChips: [...NTAG21X_COMPATIBLE, ...NTAG_I2C_COMPATIBLE],
|
||||
features: [
|
||||
'NTAG216 chip',
|
||||
'888 bytes user memory',
|
||||
'NFC Type 2 tag',
|
||||
'URL/contact sharing',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/xnt/',
|
||||
canReceiveClone: true,
|
||||
exactMatch: true,
|
||||
},
|
||||
{
|
||||
id: 'xsiid',
|
||||
name: 'xSIID',
|
||||
description:
|
||||
'NFC implant with built-in LED that blinks when scanned. Multiple color options available.',
|
||||
formFactor: FormFactor.X_SERIES,
|
||||
categories: [ProductCategory.NFC, ProductCategory.LED],
|
||||
compatibleChips: [...NTAG21X_COMPATIBLE, ...NTAG_I2C_COMPATIBLE],
|
||||
features: [
|
||||
'NTAG I2C chip',
|
||||
'888 bytes user memory',
|
||||
'Built-in LED indicator',
|
||||
'Multiple colors available',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/xsiid/',
|
||||
canReceiveClone: true,
|
||||
exactMatch: true,
|
||||
},
|
||||
{
|
||||
id: 'xslx',
|
||||
name: 'xSLX',
|
||||
description: 'ISO 15693 NFC-V implant using ICODE SLIX chip.',
|
||||
formFactor: FormFactor.X_SERIES,
|
||||
categories: [ProductCategory.NFC],
|
||||
compatibleChips: [...SLIX_COMPATIBLE],
|
||||
features: [
|
||||
'ICODE SLIX chip',
|
||||
'ISO 15693 (NFC-V)',
|
||||
'Long range capability',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/xslx/',
|
||||
canReceiveClone: true,
|
||||
exactMatch: true,
|
||||
},
|
||||
|
||||
// ==========================================================================
|
||||
// X-Series - Dual Frequency (NFC + 125kHz)
|
||||
// ==========================================================================
|
||||
{
|
||||
id: 'next',
|
||||
name: 'NExT',
|
||||
description:
|
||||
'Original dual-frequency implant combining NTAG216 (NFC) with T5577 (125kHz).',
|
||||
formFactor: FormFactor.X_SERIES,
|
||||
categories: [ProductCategory.DUAL_FREQUENCY, ProductCategory.ACCESS],
|
||||
compatibleChips: [...NTAG21X_COMPATIBLE, ...NTAG_I2C_COMPATIBLE],
|
||||
features: [
|
||||
'NTAG216 NFC chip',
|
||||
'T5577 125kHz chip',
|
||||
'Dual-frequency capability',
|
||||
'HID/EM compatible',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/next/',
|
||||
canReceiveClone: true,
|
||||
exactMatch: true,
|
||||
notes: 'Also includes T5577 for 125kHz access systems',
|
||||
},
|
||||
{
|
||||
id: 'next-v2',
|
||||
name: 'NExT v2',
|
||||
description:
|
||||
'Updated dual-frequency implant with NTAG I2C, T5577, and built-in LED indicator.',
|
||||
formFactor: FormFactor.X_SERIES,
|
||||
categories: [ProductCategory.DUAL_FREQUENCY, ProductCategory.ACCESS, ProductCategory.LED],
|
||||
compatibleChips: [...NTAG21X_COMPATIBLE, ...NTAG_I2C_COMPATIBLE],
|
||||
features: [
|
||||
'NTAG I2C NFC chip',
|
||||
'T5577 125kHz chip',
|
||||
'Built-in LED indicator',
|
||||
'888 bytes user memory',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/next-v2/',
|
||||
canReceiveClone: true,
|
||||
exactMatch: true,
|
||||
notes: 'Also includes T5577 for 125kHz access systems',
|
||||
},
|
||||
{
|
||||
id: 'xmagic',
|
||||
name: 'xMagic',
|
||||
description:
|
||||
'Dual-frequency implant with magic MIFARE Classic (changeable UID) and T5577.',
|
||||
formFactor: FormFactor.X_SERIES,
|
||||
categories: [ProductCategory.DUAL_FREQUENCY, ProductCategory.ACCESS],
|
||||
compatibleChips: [...MIFARE_CLASSIC_COMPATIBLE],
|
||||
features: [
|
||||
'Magic MIFARE Classic chip',
|
||||
'Changeable UID',
|
||||
'T5577 125kHz chip',
|
||||
'Clone access cards',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/xmagic/',
|
||||
canReceiveClone: true,
|
||||
exactMatch: false,
|
||||
notes: 'Can clone MIFARE Classic cards (requires Android + keys)',
|
||||
},
|
||||
|
||||
// ==========================================================================
|
||||
// X-Series - DESFire (Secure NFC)
|
||||
// Note: xDF (EV1) and xDF2 (EV2) discontinued - see flexDF/flexDF2 for those
|
||||
// ==========================================================================
|
||||
{
|
||||
id: 'xdf3',
|
||||
name: 'xDF3',
|
||||
description: 'Latest generation secure NFC implant using MIFARE DESFire EV3.',
|
||||
formFactor: FormFactor.X_SERIES,
|
||||
categories: [ProductCategory.SECURE, ProductCategory.ACCESS],
|
||||
compatibleChips: [...DESFIRE_ALL],
|
||||
features: [
|
||||
'DESFire EV3 chip',
|
||||
'8KB memory',
|
||||
'Latest security features',
|
||||
'Transaction MAC',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/xdf3/',
|
||||
canReceiveClone: false,
|
||||
exactMatch: true,
|
||||
notes: 'Cannot clone data due to cryptographic protection',
|
||||
desfireEvLevel: 3,
|
||||
},
|
||||
|
||||
// ==========================================================================
|
||||
// X-Series - Magic/Cloning
|
||||
// ==========================================================================
|
||||
{
|
||||
id: 'xm1',
|
||||
name: 'xM1',
|
||||
description: 'Magic MIFARE Classic 1K implant with changeable UID for cloning access cards.',
|
||||
formFactor: FormFactor.X_SERIES,
|
||||
categories: [ProductCategory.ACCESS],
|
||||
compatibleChips: [...MIFARE_CLASSIC_COMPATIBLE],
|
||||
features: [
|
||||
'Magic MIFARE Classic 1K',
|
||||
'Changeable UID',
|
||||
'Clone access cards',
|
||||
'Gen1a compatible',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/xm1/',
|
||||
canReceiveClone: true,
|
||||
exactMatch: false,
|
||||
notes: 'Can clone MIFARE Classic 1K cards (requires Android + keys)',
|
||||
},
|
||||
|
||||
// ==========================================================================
|
||||
// X-Series - Cryptographic/Identity
|
||||
// ==========================================================================
|
||||
{
|
||||
id: 'spark2',
|
||||
name: 'VivoKey Spark 2',
|
||||
description:
|
||||
'Cryptobionic identity implant for secure authentication and digital identity.',
|
||||
formFactor: FormFactor.X_SERIES,
|
||||
categories: [ProductCategory.SECURE],
|
||||
compatibleChips: [ChipType.NTAG424_DNA, ChipType.NTAG424_DNA_TT, ChipType.ICODE_DNA],
|
||||
features: [
|
||||
'Cryptographic authentication',
|
||||
'VivoKey identity platform',
|
||||
'Secure web login',
|
||||
'Digital signatures',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/vivokey-spark-2/',
|
||||
canReceiveClone: false,
|
||||
exactMatch: true,
|
||||
notes: 'Uses cryptographic authentication - cannot clone',
|
||||
},
|
||||
|
||||
// ==========================================================================
|
||||
// X-Series - LED/Special
|
||||
// ==========================================================================
|
||||
{
|
||||
id: 'xglow',
|
||||
name: 'xGlow',
|
||||
description: 'Implant with LED that glows when exposed to NFC field.',
|
||||
formFactor: FormFactor.X_SERIES,
|
||||
categories: [ProductCategory.LED],
|
||||
compatibleChips: [], // No NFC storage
|
||||
features: [
|
||||
'LED indicator',
|
||||
'Multiple colors',
|
||||
'Field detection',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/xglow/',
|
||||
canReceiveClone: false,
|
||||
exactMatch: false,
|
||||
notes: 'LED only - no NFC data storage',
|
||||
},
|
||||
|
||||
// ==========================================================================
|
||||
// Bioresin Implants - Larger capsules (incision install)
|
||||
// ==========================================================================
|
||||
{
|
||||
id: 'dnext',
|
||||
name: 'dNExT',
|
||||
description:
|
||||
'Jumbo-sized bioresin dual-frequency implant with NTAG216 (NFC) and T5577 (125kHz). Enhanced read range.',
|
||||
formFactor: FormFactor.BIORESIN,
|
||||
categories: [ProductCategory.DUAL_FREQUENCY, ProductCategory.ACCESS],
|
||||
compatibleChips: [...NTAG21X_COMPATIBLE, ...NTAG_I2C_COMPATIBLE],
|
||||
features: [
|
||||
'NTAG216 NFC chip',
|
||||
'T5577 125kHz chip',
|
||||
'Enhanced read range',
|
||||
'Bioresin encapsulation',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/dnext/',
|
||||
canReceiveClone: true,
|
||||
exactMatch: true,
|
||||
notes: 'Larger size provides improved performance over x-series',
|
||||
},
|
||||
{
|
||||
id: 'dug4t',
|
||||
name: 'dUG4T',
|
||||
description:
|
||||
'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],
|
||||
features: [
|
||||
'Ultimate Gen4 magic chip',
|
||||
'T5577 125kHz chip',
|
||||
'Changeable UID',
|
||||
'Enhanced cloning capability',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/dug4t/',
|
||||
canReceiveClone: true,
|
||||
exactMatch: false,
|
||||
notes: 'Can clone MIFARE Classic cards (requires Android + keys)',
|
||||
},
|
||||
{
|
||||
id: 'dt5',
|
||||
name: 'dT5',
|
||||
description:
|
||||
'Bioresin 125kHz implant with T5577 chip for access control systems.',
|
||||
formFactor: FormFactor.BIORESIN,
|
||||
categories: [ProductCategory.ACCESS],
|
||||
compatibleChips: [], // 125kHz only, no NFC
|
||||
features: [
|
||||
'T5577 125kHz chip',
|
||||
'EM41xx emulation',
|
||||
'HID ProxCard compatible',
|
||||
'Bioresin encapsulation',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/dt5/',
|
||||
canReceiveClone: false,
|
||||
exactMatch: false,
|
||||
notes: '125kHz only - no NFC capability',
|
||||
},
|
||||
|
||||
// ==========================================================================
|
||||
// Flex Implants - NFC
|
||||
// ==========================================================================
|
||||
{
|
||||
id: 'flexnt',
|
||||
name: 'flexNT',
|
||||
description:
|
||||
'Flexible NTAG216 implant with larger antenna for improved read range.',
|
||||
formFactor: FormFactor.FLEX,
|
||||
categories: [ProductCategory.NFC],
|
||||
compatibleChips: [...NTAG21X_COMPATIBLE, ...NTAG_I2C_COMPATIBLE],
|
||||
features: [
|
||||
'NTAG216 chip',
|
||||
'888 bytes user memory',
|
||||
'Flexible form factor',
|
||||
'Extended read range',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/flexnt/',
|
||||
canReceiveClone: true,
|
||||
exactMatch: true,
|
||||
},
|
||||
|
||||
// ==========================================================================
|
||||
// Flex Implants - DESFire (Secure)
|
||||
// Note: flexDF (EV1) discontinued - flexDF2 is current
|
||||
// ==========================================================================
|
||||
{
|
||||
id: 'flexdf2',
|
||||
name: 'flexDF2',
|
||||
description: 'Flexible DESFire EV2 implant with enhanced security.',
|
||||
formFactor: FormFactor.FLEX,
|
||||
categories: [ProductCategory.SECURE, ProductCategory.ACCESS],
|
||||
compatibleChips: [...DESFIRE_ALL],
|
||||
features: [
|
||||
'DESFire EV2 chip',
|
||||
'8KB memory',
|
||||
'AES-128 encryption',
|
||||
'Flexible form factor',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/flexdf2/',
|
||||
canReceiveClone: false,
|
||||
exactMatch: true,
|
||||
notes: 'Cannot clone data due to cryptographic protection',
|
||||
desfireEvLevel: 2,
|
||||
},
|
||||
|
||||
// ==========================================================================
|
||||
// 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',
|
||||
description: 'Updated flexible magic MIFARE Classic 1K implant.',
|
||||
formFactor: FormFactor.FLEX,
|
||||
categories: [ProductCategory.ACCESS],
|
||||
compatibleChips: [...MIFARE_CLASSIC_COMPATIBLE],
|
||||
features: [
|
||||
'Magic MIFARE Classic 1K',
|
||||
'Improved antenna design',
|
||||
'Changeable UID',
|
||||
'Better compatibility',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/flexm1-v2/',
|
||||
canReceiveClone: true,
|
||||
exactMatch: false,
|
||||
notes: 'Can clone MIFARE Classic 1K cards (requires Android + keys)',
|
||||
},
|
||||
|
||||
// ==========================================================================
|
||||
// Flex Implants - Secure Element / JavaCard
|
||||
// ==========================================================================
|
||||
{
|
||||
id: 'apex-flex',
|
||||
name: 'Apex Flex',
|
||||
description:
|
||||
'The ultimate subdermal security key for digital identity, cryptography, and blockchain applications.',
|
||||
formFactor: FormFactor.FLEX,
|
||||
categories: [ProductCategory.SECURE],
|
||||
compatibleChips: [ChipType.JCOP4, ChipType.JAVACARD_UNKNOWN],
|
||||
features: [
|
||||
'JCOP4 secure element',
|
||||
'Fidesmo platform',
|
||||
'Payment capable',
|
||||
'FIDO2/WebAuthn',
|
||||
'OTP support',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/apex-flex/',
|
||||
canReceiveClone: false,
|
||||
exactMatch: true,
|
||||
notes: 'Cannot clone - programmable via Fidesmo app',
|
||||
},
|
||||
{
|
||||
id: 'flexsecure',
|
||||
name: 'flexSecure',
|
||||
description:
|
||||
'Developer-friendly JavaCard implant for custom secure applications.',
|
||||
formFactor: FormFactor.FLEX,
|
||||
categories: [ProductCategory.SECURE],
|
||||
compatibleChips: [ChipType.JCOP4, ChipType.JAVACARD_UNKNOWN],
|
||||
features: [
|
||||
'SmartMX3 P71 chip',
|
||||
'Full JavaCard access',
|
||||
'GlobalPlatformPro compatible',
|
||||
'Custom applet support',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/flexsecure/',
|
||||
canReceiveClone: false,
|
||||
exactMatch: true,
|
||||
notes: 'Developer-focused - requires technical knowledge',
|
||||
},
|
||||
|
||||
// ==========================================================================
|
||||
// Flex Implants - Access Control
|
||||
// ==========================================================================
|
||||
{
|
||||
id: 'flexclass',
|
||||
name: 'flexClass',
|
||||
description: 'Flexible HID iCLASS compatible implant for enterprise access control.',
|
||||
formFactor: FormFactor.FLEX,
|
||||
categories: [ProductCategory.SECURE, ProductCategory.ACCESS],
|
||||
compatibleChips: [], // iCLASS uses proprietary protocol
|
||||
features: [
|
||||
'HID iCLASS SE compatible',
|
||||
'Enterprise access control',
|
||||
'Flexible form factor',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/flexclass/',
|
||||
canReceiveClone: false,
|
||||
exactMatch: false,
|
||||
notes: 'For HID iCLASS systems only',
|
||||
},
|
||||
{
|
||||
id: 'teslaflex',
|
||||
name: 'TeslaFlex',
|
||||
description: 'Implant designed to work as a Tesla key card.',
|
||||
formFactor: FormFactor.FLEX,
|
||||
categories: [ProductCategory.ACCESS],
|
||||
compatibleChips: [], // Tesla proprietary
|
||||
features: [
|
||||
'Tesla key card compatible',
|
||||
'Flexible form factor',
|
||||
'Direct Tesla integration',
|
||||
],
|
||||
url: 'https://dangerousthings.com/product/teslaflex/',
|
||||
canReceiveClone: false,
|
||||
exactMatch: false,
|
||||
notes: 'Specifically for Tesla vehicles',
|
||||
},
|
||||
{
|
||||
id: 'flexug4',
|
||||
name: 'flexUG4',
|
||||
description: 'Flexible Ultimate Gen4 magic MIFARE implant for cloning access cards.',
|
||||
formFactor: FormFactor.FLEX,
|
||||
categories: [ProductCategory.ACCESS],
|
||||
compatibleChips: [...MIFARE_CLASSIC_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)',
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Build a map of chip types to compatible products
|
||||
*/
|
||||
export function buildChipProductMap(): ChipProductMap {
|
||||
const map: ChipProductMap = new Map();
|
||||
|
||||
for (const product of PRODUCTS) {
|
||||
for (const chipType of product.compatibleChips) {
|
||||
const existing = map.get(chipType) || [];
|
||||
existing.push(product);
|
||||
map.set(chipType, existing);
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cached chip-to-product map
|
||||
*/
|
||||
let chipProductMapCache: ChipProductMap | null = null;
|
||||
|
||||
/**
|
||||
* Get the chip-to-product map (cached)
|
||||
*/
|
||||
export function getChipProductMap(): ChipProductMap {
|
||||
if (!chipProductMapCache) {
|
||||
chipProductMapCache = buildChipProductMap();
|
||||
}
|
||||
return chipProductMapCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversion service URL for chips that don't have direct matches
|
||||
*/
|
||||
export const CONVERSION_SERVICE_URL = 'https://dngr.us/conversion';
|
||||
|
||||
/**
|
||||
* Get all products
|
||||
*/
|
||||
export function getAllProducts(): Product[] {
|
||||
return PRODUCTS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get product by ID
|
||||
*/
|
||||
export function getProductById(id: string): Product | undefined {
|
||||
return PRODUCTS.find(p => p.id === id);
|
||||
}
|
||||
@@ -1,14 +1,26 @@
|
||||
import React from 'react';
|
||||
import React, {useMemo} from 'react';
|
||||
import {StyleSheet, View, ScrollView, Linking} from 'react-native';
|
||||
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 {Product} from '../types/products';
|
||||
|
||||
export function ResultScreen({route, navigation}: ResultScreenProps) {
|
||||
const {tagData, transponder} = route.params;
|
||||
|
||||
// Match chip to products
|
||||
const matchResult = useMemo(() => {
|
||||
if (!transponder) return null;
|
||||
return matchChipToProducts(transponder.type);
|
||||
}, [transponder]);
|
||||
|
||||
const handleConversionLink = () => {
|
||||
Linking.openURL('https://dngr.us/conversion');
|
||||
Linking.openURL(matchResult?.conversionUrl || 'https://dngr.us/conversion');
|
||||
};
|
||||
|
||||
const handleProductLink = (product: Product) => {
|
||||
Linking.openURL(product.url);
|
||||
};
|
||||
|
||||
// Determine card colors based on detection
|
||||
@@ -87,6 +99,99 @@ export function ResultScreen({route, navigation}: ResultScreenProps) {
|
||||
</Surface>
|
||||
)}
|
||||
|
||||
{/* Product Matches Card */}
|
||||
{transponder && matchResult && (matchResult.exactMatches.length > 0 || matchResult.cloneTargets.length > 0) && (
|
||||
<Surface style={styles.productsCard} elevation={1}>
|
||||
<Text variant="labelLarge" style={styles.productsLabel}>
|
||||
COMPATIBLE IMPLANTS
|
||||
</Text>
|
||||
<Divider style={[styles.divider, {backgroundColor: DTColors.modeEmphasis}]} />
|
||||
|
||||
<Text variant="bodyMedium" style={styles.matchSummary}>
|
||||
{getMatchSummary(matchResult, transponder.chipName)}
|
||||
</Text>
|
||||
|
||||
{/* Exact Matches / Clone Targets */}
|
||||
{[...matchResult.exactMatches, ...matchResult.cloneTargets.filter(
|
||||
p => !matchResult.exactMatches.find(e => e.id === p.id)
|
||||
)].map(product => (
|
||||
<Surface key={product.id} style={styles.productItem} elevation={0}>
|
||||
<View style={styles.productHeader}>
|
||||
<Text variant="titleMedium" style={styles.productName}>
|
||||
{product.name}
|
||||
</Text>
|
||||
<Chip
|
||||
style={styles.formFactorChip}
|
||||
textStyle={styles.formFactorChipText}>
|
||||
{product.formFactor.replace('_', ' ')}
|
||||
</Chip>
|
||||
</View>
|
||||
|
||||
<Text variant="bodySmall" style={styles.productDescription}>
|
||||
{product.description}
|
||||
</Text>
|
||||
|
||||
{product.notes && (
|
||||
<Text variant="bodySmall" style={styles.productNote}>
|
||||
Note: {product.notes}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{/* DESFire EV mismatch warning */}
|
||||
{getDesfireEvMismatchWarning(transponder.type, product) && (
|
||||
<Text variant="bodySmall" style={styles.evMismatchWarning}>
|
||||
⚠️ {getDesfireEvMismatchWarning(transponder.type, product)}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
<View style={styles.productFeatures}>
|
||||
{product.features.slice(0, 3).map((feature, idx) => (
|
||||
<Text key={idx} variant="bodySmall" style={styles.productFeature}>
|
||||
• {feature}
|
||||
</Text>
|
||||
))}
|
||||
</View>
|
||||
|
||||
<Button
|
||||
mode="outlined"
|
||||
onPress={() => handleProductLink(product)}
|
||||
style={styles.productButton}
|
||||
labelStyle={styles.productButtonLabel}
|
||||
compact>
|
||||
VIEW PRODUCT
|
||||
</Button>
|
||||
</Surface>
|
||||
))}
|
||||
</Surface>
|
||||
)}
|
||||
|
||||
{/* No Match Card - show when chip detected but no products match */}
|
||||
{transponder && matchResult && matchResult.exactMatches.length === 0 && matchResult.cloneTargets.length === 0 && (
|
||||
<Surface style={styles.noMatchCard} elevation={1}>
|
||||
<Text variant="labelLarge" style={styles.noMatchLabel}>
|
||||
NO DIRECT MATCH
|
||||
</Text>
|
||||
<Divider style={[styles.divider, {backgroundColor: DTColors.modeOther}]} />
|
||||
|
||||
<Text variant="bodyMedium" style={styles.noMatchText}>
|
||||
{getMatchSummary(matchResult, transponder.chipName)}
|
||||
</Text>
|
||||
|
||||
{matchResult.familyMatches.length > 0 && (
|
||||
<>
|
||||
<Text variant="bodySmall" style={styles.familyMatchesLabel}>
|
||||
Related products in the same chip family:
|
||||
</Text>
|
||||
{matchResult.familyMatches.slice(0, 2).map(product => (
|
||||
<Text key={product.id} variant="bodySmall" style={styles.familyMatchItem}>
|
||||
• {product.name}
|
||||
</Text>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</Surface>
|
||||
)}
|
||||
|
||||
{/* SAK Swap Detection Card */}
|
||||
{transponder?.sakSwapInfo?.hasSakSwap && (
|
||||
<Surface style={styles.sakSwapCard} elevation={1}>
|
||||
@@ -195,11 +300,15 @@ export function ResultScreen({route, navigation}: ResultScreenProps) {
|
||||
</Surface>
|
||||
)}
|
||||
|
||||
{/* Conversion Service Card - only show if chip is NOT cloneable */}
|
||||
{(!transponder || !transponder.isCloneable) && (
|
||||
{/* Conversion Service Card - show when recommended */}
|
||||
{(matchResult?.conversionRecommended || !transponder) && (
|
||||
<Surface style={styles.conversionCard} elevation={1}>
|
||||
<Text variant="bodyMedium" style={styles.conversionText}>
|
||||
Can't clone this chip? Check out our conversion service.
|
||||
{!transponder
|
||||
? 'Unknown chip? Our conversion service can help.'
|
||||
: matchResult?.isCloneable === false
|
||||
? "This chip uses cryptographic protection and can't be cloned. Our conversion service can help find alternatives."
|
||||
: "No direct product match found. Our conversion service can help."}
|
||||
</Text>
|
||||
<Button
|
||||
mode="outlined"
|
||||
@@ -387,6 +496,113 @@ const styles = StyleSheet.create({
|
||||
color: DTColors.light,
|
||||
opacity: 0.8,
|
||||
},
|
||||
// Products Card
|
||||
productsCard: {
|
||||
backgroundColor: '#0a0a0a',
|
||||
borderRadius: 4,
|
||||
borderWidth: 2,
|
||||
borderColor: DTColors.modeEmphasis,
|
||||
padding: 20,
|
||||
marginBottom: 20,
|
||||
},
|
||||
productsLabel: {
|
||||
color: DTColors.modeEmphasis,
|
||||
letterSpacing: 2,
|
||||
marginBottom: 12,
|
||||
},
|
||||
matchSummary: {
|
||||
color: DTColors.light,
|
||||
marginBottom: 16,
|
||||
},
|
||||
productItem: {
|
||||
backgroundColor: '#111111',
|
||||
borderRadius: 4,
|
||||
borderWidth: 1,
|
||||
borderColor: DTColors.modeNormal,
|
||||
padding: 16,
|
||||
marginBottom: 12,
|
||||
},
|
||||
productHeader: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginBottom: 8,
|
||||
},
|
||||
productName: {
|
||||
color: DTColors.modeEmphasis,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
formFactorChip: {
|
||||
backgroundColor: 'transparent',
|
||||
borderWidth: 1,
|
||||
borderColor: DTColors.modeNormal,
|
||||
height: 24,
|
||||
},
|
||||
formFactorChipText: {
|
||||
color: DTColors.modeNormal,
|
||||
fontSize: 10,
|
||||
},
|
||||
productDescription: {
|
||||
color: DTColors.light,
|
||||
opacity: 0.8,
|
||||
marginBottom: 8,
|
||||
},
|
||||
productNote: {
|
||||
color: DTColors.modeOther,
|
||||
fontStyle: 'italic',
|
||||
marginBottom: 8,
|
||||
},
|
||||
evMismatchWarning: {
|
||||
color: DTColors.modeEmphasis,
|
||||
backgroundColor: 'rgba(255, 255, 0, 0.1)',
|
||||
padding: 8,
|
||||
borderRadius: 4,
|
||||
marginBottom: 8,
|
||||
},
|
||||
productFeatures: {
|
||||
marginBottom: 12,
|
||||
},
|
||||
productFeature: {
|
||||
color: DTColors.light,
|
||||
opacity: 0.7,
|
||||
marginBottom: 2,
|
||||
},
|
||||
productButton: {
|
||||
borderColor: DTColors.modeEmphasis,
|
||||
borderWidth: 1,
|
||||
},
|
||||
productButtonLabel: {
|
||||
color: DTColors.modeEmphasis,
|
||||
fontSize: 12,
|
||||
},
|
||||
// No Match Card
|
||||
noMatchCard: {
|
||||
backgroundColor: '#0a0a0a',
|
||||
borderRadius: 4,
|
||||
borderWidth: 1,
|
||||
borderColor: DTColors.modeOther,
|
||||
padding: 20,
|
||||
marginBottom: 20,
|
||||
},
|
||||
noMatchLabel: {
|
||||
color: DTColors.modeOther,
|
||||
letterSpacing: 2,
|
||||
marginBottom: 12,
|
||||
},
|
||||
noMatchText: {
|
||||
color: DTColors.light,
|
||||
opacity: 0.8,
|
||||
marginBottom: 12,
|
||||
},
|
||||
familyMatchesLabel: {
|
||||
color: DTColors.light,
|
||||
opacity: 0.6,
|
||||
marginBottom: 8,
|
||||
},
|
||||
familyMatchItem: {
|
||||
color: DTColors.modeNormal,
|
||||
marginBottom: 4,
|
||||
},
|
||||
// Conversion Card
|
||||
conversionCard: {
|
||||
backgroundColor: '#0a0a0a',
|
||||
|
||||
14
src/services/matching/index.ts
Normal file
14
src/services/matching/index.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Product Matching Service
|
||||
*/
|
||||
|
||||
export {
|
||||
matchChipToProducts,
|
||||
getMatchSummary,
|
||||
getRecommendedAction,
|
||||
formatProductFeatures,
|
||||
getCloneTargetsForChip,
|
||||
canCloneToProduct,
|
||||
getDesfireEvLevel,
|
||||
getDesfireEvMismatchWarning,
|
||||
} from './matcher';
|
||||
201
src/services/matching/matcher.ts
Normal file
201
src/services/matching/matcher.ts
Normal file
@@ -0,0 +1,201 @@
|
||||
/**
|
||||
* Product Matching Service
|
||||
*
|
||||
* Matches detected NFC chips to compatible Dangerous Things products
|
||||
*/
|
||||
|
||||
import {ChipType, getChipFamily, CHIP_CLONEABILITY, ChipFamily} from '../../types/detection';
|
||||
import {MatchResult, Product, DesfireEvLevel} from '../../types/products';
|
||||
import {
|
||||
PRODUCTS,
|
||||
getChipProductMap,
|
||||
CONVERSION_SERVICE_URL,
|
||||
} from '../../data/products';
|
||||
|
||||
/**
|
||||
* Match a detected chip to compatible products
|
||||
*/
|
||||
export function matchChipToProducts(chipType: ChipType): MatchResult {
|
||||
const chipProductMap = getChipProductMap();
|
||||
const cloneability = CHIP_CLONEABILITY[chipType];
|
||||
const chipFamily = getChipFamily(chipType);
|
||||
|
||||
// Get products that directly support this chip
|
||||
const directMatches = chipProductMap.get(chipType) || [];
|
||||
|
||||
// Separate exact matches from clone targets
|
||||
const exactMatches: Product[] = [];
|
||||
const cloneTargets: Product[] = [];
|
||||
|
||||
for (const product of directMatches) {
|
||||
if (product.exactMatch) {
|
||||
exactMatches.push(product);
|
||||
}
|
||||
if (product.canReceiveClone && cloneability?.cloneable) {
|
||||
cloneTargets.push(product);
|
||||
}
|
||||
}
|
||||
|
||||
// Find products in the same chip family
|
||||
const familyMatches = findFamilyMatches(chipType, chipFamily, directMatches);
|
||||
|
||||
// Determine if conversion is recommended
|
||||
// Conversion is recommended when:
|
||||
// 1. No exact matches AND no clone targets
|
||||
// 2. Chip is not cloneable (crypto protection)
|
||||
const hasMatches = exactMatches.length > 0 || cloneTargets.length > 0;
|
||||
const conversionRecommended = !hasMatches || !cloneability?.cloneable;
|
||||
|
||||
return {
|
||||
exactMatches,
|
||||
cloneTargets,
|
||||
familyMatches,
|
||||
isCloneable: cloneability?.cloneable ?? false,
|
||||
cloneabilityNote: cloneability?.note,
|
||||
conversionRecommended,
|
||||
conversionUrl: CONVERSION_SERVICE_URL,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Find products in the same chip family that might be of interest
|
||||
*/
|
||||
function findFamilyMatches(
|
||||
chipType: ChipType,
|
||||
chipFamily: ChipFamily,
|
||||
excludeProducts: Product[],
|
||||
): Product[] {
|
||||
const excludeIds = new Set(excludeProducts.map(p => p.id));
|
||||
const familyMatches: Product[] = [];
|
||||
|
||||
for (const product of PRODUCTS) {
|
||||
// Skip already matched products
|
||||
if (excludeIds.has(product.id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if any of the product's compatible chips are in the same family
|
||||
const productChipFamilies = product.compatibleChips.map(getChipFamily);
|
||||
if (productChipFamilies.includes(chipFamily)) {
|
||||
familyMatches.push(product);
|
||||
}
|
||||
}
|
||||
|
||||
return familyMatches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a summary message for the match result
|
||||
*/
|
||||
export function getMatchSummary(result: MatchResult, chipName: string): string {
|
||||
if (result.exactMatches.length > 0) {
|
||||
const names = result.exactMatches.map(p => p.name).join(', ');
|
||||
return `Your ${chipName} is compatible with: ${names}`;
|
||||
}
|
||||
|
||||
if (result.cloneTargets.length > 0) {
|
||||
const names = result.cloneTargets.map(p => p.name).join(', ');
|
||||
return `Your ${chipName} data can be cloned to: ${names}`;
|
||||
}
|
||||
|
||||
if (!result.isCloneable) {
|
||||
return `${chipName} uses cryptographic protection and cannot be cloned. Consider our conversion service for compatible options.`;
|
||||
}
|
||||
|
||||
return `No direct product match found for ${chipName}. Check our conversion service for options.`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recommended action based on match result
|
||||
*/
|
||||
export function getRecommendedAction(
|
||||
result: MatchResult,
|
||||
): 'purchase' | 'clone' | 'conversion' | 'contact' {
|
||||
if (result.exactMatches.length > 0) {
|
||||
return 'purchase';
|
||||
}
|
||||
|
||||
if (result.cloneTargets.length > 0 && result.isCloneable) {
|
||||
return 'clone';
|
||||
}
|
||||
|
||||
if (result.conversionRecommended) {
|
||||
return 'conversion';
|
||||
}
|
||||
|
||||
return 'contact';
|
||||
}
|
||||
|
||||
/**
|
||||
* Format product features for display
|
||||
*/
|
||||
export function formatProductFeatures(product: Product): string[] {
|
||||
return product.features;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get products that can receive cloned data from a chip type
|
||||
*/
|
||||
export function getCloneTargetsForChip(chipType: ChipType): Product[] {
|
||||
const cloneability = CHIP_CLONEABILITY[chipType];
|
||||
if (!cloneability?.cloneable) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const chipProductMap = getChipProductMap();
|
||||
const directMatches = chipProductMap.get(chipType) || [];
|
||||
|
||||
return directMatches.filter(p => p.canReceiveClone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a chip can be cloned to any product
|
||||
*/
|
||||
export function canCloneToProduct(chipType: ChipType): boolean {
|
||||
return getCloneTargetsForChip(chipType).length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get DESFire EV level from chip type
|
||||
*/
|
||||
export function getDesfireEvLevel(chipType: ChipType): DesfireEvLevel | null {
|
||||
switch (chipType) {
|
||||
case ChipType.DESFIRE_EV1:
|
||||
return 1;
|
||||
case ChipType.DESFIRE_EV2:
|
||||
return 2;
|
||||
case ChipType.DESFIRE_EV3:
|
||||
return 3;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there's a DESFire EV mismatch between chip and product
|
||||
* Returns warning message if mismatch, null if no issue
|
||||
*/
|
||||
export function getDesfireEvMismatchWarning(
|
||||
chipType: ChipType,
|
||||
product: Product,
|
||||
): string | null {
|
||||
const chipEvLevel = getDesfireEvLevel(chipType);
|
||||
const productEvLevel = product.desfireEvLevel;
|
||||
|
||||
// Not a DESFire chip or product doesn't have EV level
|
||||
if (chipEvLevel === null || productEvLevel === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Perfect match
|
||||
if (chipEvLevel === productEvLevel) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Mismatch - warn user
|
||||
if (chipEvLevel < productEvLevel) {
|
||||
return `Your card uses DESFire EV${chipEvLevel}, but this implant uses EV${productEvLevel}. Some newer features may not be compatible with your existing system.`;
|
||||
} else {
|
||||
return `Your card uses DESFire EV${chipEvLevel}, but this implant uses EV${productEvLevel}. This should work, but you won't have access to EV${chipEvLevel} features.`;
|
||||
}
|
||||
}
|
||||
78
src/types/products.ts
Normal file
78
src/types/products.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Product Types for Dangerous Things Implants
|
||||
*/
|
||||
|
||||
import {ChipType} from './detection';
|
||||
|
||||
/**
|
||||
* Product form factor
|
||||
*/
|
||||
export enum FormFactor {
|
||||
X_SERIES = 'X_SERIES', // 2x12mm injectable capsule (glass or bioresin)
|
||||
FLEX = 'FLEX', // Flexible PCB implant (incision install)
|
||||
BIORESIN = 'BIORESIN', // Larger bioresin capsule (incision install)
|
||||
CARD = 'CARD', // ISO card format
|
||||
}
|
||||
|
||||
/**
|
||||
* Product category
|
||||
*/
|
||||
export enum ProductCategory {
|
||||
NFC = 'NFC', // NFC-only implants
|
||||
DUAL_FREQUENCY = 'DUAL_FREQUENCY', // NFC + 125kHz
|
||||
SECURE = 'SECURE', // Cryptographic/secure elements
|
||||
LED = 'LED', // Implants with LED indicators
|
||||
ACCESS = 'ACCESS', // Access control focused
|
||||
}
|
||||
|
||||
/**
|
||||
* DESFire EV level for version matching
|
||||
*/
|
||||
export type DesfireEvLevel = 1 | 2 | 3;
|
||||
|
||||
/**
|
||||
* A Dangerous Things product
|
||||
*/
|
||||
export interface Product {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
formFactor: FormFactor;
|
||||
categories: ProductCategory[];
|
||||
compatibleChips: ChipType[];
|
||||
features: string[];
|
||||
url: string;
|
||||
/** Whether this product can have data cloned TO it from the scanned chip */
|
||||
canReceiveClone: boolean;
|
||||
/** Whether this product uses the same chip type as scanned */
|
||||
exactMatch: boolean;
|
||||
/** Notes about compatibility or limitations */
|
||||
notes?: string;
|
||||
/** DESFire EV level (1, 2, or 3) for version mismatch warnings */
|
||||
desfireEvLevel?: DesfireEvLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of product matching
|
||||
*/
|
||||
export interface MatchResult {
|
||||
/** Products that use the exact same chip */
|
||||
exactMatches: Product[];
|
||||
/** Products that can receive cloned data from this chip */
|
||||
cloneTargets: Product[];
|
||||
/** Products in the same chip family */
|
||||
familyMatches: Product[];
|
||||
/** Is this chip cloneable at all? */
|
||||
isCloneable: boolean;
|
||||
/** Note about cloneability */
|
||||
cloneabilityNote?: string;
|
||||
/** Conversion service recommendation */
|
||||
conversionRecommended: boolean;
|
||||
/** Conversion URL */
|
||||
conversionUrl: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Chip to product mapping for quick lookup
|
||||
*/
|
||||
export type ChipProductMap = Map<ChipType, Product[]>;
|
||||
Reference in New Issue
Block a user