/** * Platform Detection Utility * * Smart platform identification for accounts based on credential patterns, * key formats, and other distinguishing characteristics. */ import { Platform, IPlatformDetector, Credentials } from '@/types/credential'; /** * Pacifica platform detector * Detects Ed25519 credentials with specific patterns */ export class PacificaDetector implements IPlatformDetector { readonly confidence = 0.9; detect(credentials: any): Platform | null { if (!credentials || typeof credentials !== 'object') { return null; } // Check for Ed25519 signature type if (credentials.type === 'ed25519') { return Platform.PACIFICA; } // Check for 64-character hex private key (Ed25519 format) if (credentials.privateKey && typeof credentials.privateKey === 'string') { const privateKey = credentials.privateKey.toLowerCase(); // Ed25519 private key: 64 hex characters (32 bytes) if (/^[0-9a-f]{64}$/.test(privateKey)) { return Platform.PACIFICA; } } // Check for base58 public key pattern (common in Pacifica) if (credentials.publicKey && typeof credentials.publicKey === 'string') { const publicKey = credentials.publicKey; // Base58 encoded keys are typically 32-44 characters if (/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(publicKey) && !publicKey.startsWith('0x')) { return Platform.PACIFICA; } } return null; } } /** * Aster platform detector * Detects EIP-191 (Ethereum-style) credentials */ export class AsterDetector implements IPlatformDetector { readonly confidence = 0.85; detect(credentials: any): Platform | null { if (!credentials || typeof credentials !== 'object') { return null; } // Check for EIP-191 signature type if (credentials.type === 'eip191') { return Platform.ASTER; } // Check for 0x-prefixed private key (Ethereum format) if (credentials.privateKey && typeof credentials.privateKey === 'string') { const privateKey = credentials.privateKey.toLowerCase(); // Ethereum private key: 0x + 64 hex characters if (/^0x[0-9a-f]{64}$/.test(privateKey)) { return Platform.ASTER; } } // Check for Ethereum address pattern if (credentials.address && typeof credentials.address === 'string') { const address = credentials.address.toLowerCase(); // Ethereum address: 0x + 40 hex characters if (/^0x[0-9a-f]{40}$/.test(address)) { return Platform.ASTER; } } // Check for public key in Ethereum format if (credentials.publicKey && typeof credentials.publicKey === 'string') { const publicKey = credentials.publicKey.toLowerCase(); // Ethereum public key: 0x + 128 hex characters (uncompressed) if (/^0x[0-9a-f]{128}$/.test(publicKey)) { return Platform.ASTER; } } return null; } } /** * Binance platform detector * Detects HMAC-SHA256 API key/secret credentials */ export class BinanceDetector implements IPlatformDetector { readonly confidence = 0.95; detect(credentials: any): Platform | null { if (!credentials || typeof credentials !== 'object') { return null; } // Check for HMAC signature type if (credentials.type === 'hmac') { return Platform.BINANCE; } // Check for API key/secret pattern const hasApiKey = credentials.apiKey && typeof credentials.apiKey === 'string'; const hasSecretKey = credentials.secretKey && typeof credentials.secretKey === 'string'; if (hasApiKey && hasSecretKey) { const apiKey = credentials.apiKey; const secretKey = credentials.secretKey; // Binance API keys are typically 64 characters, alphanumeric const binanceApiKeyPattern = /^[A-Za-z0-9]{64}$/; // Binance secret keys are typically 64 characters, alphanumeric const binanceSecretKeyPattern = /^[A-Za-z0-9]{64}$/; if (binanceApiKeyPattern.test(apiKey) && binanceSecretKeyPattern.test(secretKey)) { return Platform.BINANCE; } // Alternative: any API key/secret combination for HMAC if (apiKey.length >= 16 && secretKey.length >= 16) { return Platform.BINANCE; } } return null; } } /** * Main platform detector that uses multiple detectors */ export class PlatformDetector { private detectors: IPlatformDetector[]; constructor() { this.detectors = [ new BinanceDetector(), // Highest confidence first new PacificaDetector(), new AsterDetector() ]; } /** * Detect platform for given credentials * @param credentials Credential object to analyze * @returns Detected platform or null if cannot determine */ detectPlatform(credentials: any): Platform | null { if (!credentials) { return null; } // Try each detector in order of confidence for (const detector of this.detectors) { const platform = detector.detect(credentials); if (platform) { return platform; } } return null; } /** * Get detection results from all detectors * @param credentials Credential object to analyze * @returns Array of detection results with confidence scores */ getAllDetectionResults(credentials: any): Array<{ platform: Platform | null; confidence: number; detector: string; }> { const results = []; for (const detector of this.detectors) { const platform = detector.detect(credentials); results.push({ platform, confidence: detector.confidence, detector: detector.constructor.name }); } return results; } /** * Validate that credentials match the specified platform * @param credentials Credential object * @param expectedPlatform Expected platform * @returns True if credentials match the platform */ validatePlatformMatch(credentials: any, expectedPlatform: Platform): boolean { const detectedPlatform = this.detectPlatform(credentials); return detectedPlatform === expectedPlatform; } /** * Get suggested platforms for ambiguous credentials * @param credentials Credential object * @returns Array of possible platforms with confidence scores */ getSuggestedPlatforms(credentials: any): Array<{ platform: Platform; confidence: number; reason: string; }> { const suggestions = []; const results = this.getAllDetectionResults(credentials); for (const result of results) { if (result.platform) { suggestions.push({ platform: result.platform, confidence: result.confidence, reason: `Detected by ${result.detector}` }); } } // Sort by confidence (highest first) return suggestions.sort((a, b) => b.confidence - a.confidence); } /** * Add custom detector * @param detector Custom platform detector */ addDetector(detector: IPlatformDetector): void { this.detectors.push(detector); // Re-sort by confidence (highest first) this.detectors.sort((a, b) => b.confidence - a.confidence); } /** * Remove detector by class name * @param detectorName Name of detector class to remove */ removeDetector(detectorName: string): void { this.detectors = this.detectors.filter( detector => detector.constructor.name !== detectorName ); } /** * Check if credentials are valid for any supported platform * @param credentials Credential object * @returns True if credentials are valid for at least one platform */ isValidCredentials(credentials: any): boolean { return this.detectPlatform(credentials) !== null; } /** * Generate credential validation report * @param credentials Credential object * @returns Detailed validation report */ generateValidationReport(credentials: any): { isValid: boolean; detectedPlatform: Platform | null; allResults: Array<{ platform: Platform | null; confidence: number; detector: string; }>; suggestions: Array<{ platform: Platform; confidence: number; reason: string; }>; issues: string[]; } { const detectedPlatform = this.detectPlatform(credentials); const allResults = this.getAllDetectionResults(credentials); const suggestions = this.getSuggestedPlatforms(credentials); const issues = []; // Check for common issues if (!credentials) { issues.push('No credentials provided'); } else if (typeof credentials !== 'object') { issues.push('Credentials must be an object'); } else { if (!detectedPlatform) { issues.push('Could not detect platform from credentials'); } if (!credentials.type) { issues.push('Missing credential type'); } // Platform-specific validation if (credentials.type === 'ed25519' && !credentials.privateKey) { issues.push('Ed25519 credentials missing privateKey'); } if (credentials.type === 'eip191' && !credentials.privateKey) { issues.push('EIP-191 credentials missing privateKey'); } if (credentials.type === 'hmac' && (!credentials.apiKey || !credentials.secretKey)) { issues.push('HMAC credentials missing apiKey or secretKey'); } } return { isValid: detectedPlatform !== null && issues.length === 0, detectedPlatform, allResults, suggestions, issues }; } }