PlatformDetector.ts 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. /**
  2. * Platform Detection Utility
  3. *
  4. * Smart platform identification for accounts based on credential patterns,
  5. * key formats, and other distinguishing characteristics.
  6. */
  7. import { Platform, IPlatformDetector, Credentials } from '@/types/credential';
  8. /**
  9. * Pacifica platform detector
  10. * Detects Ed25519 credentials with specific patterns
  11. */
  12. export class PacificaDetector implements IPlatformDetector {
  13. readonly confidence = 0.9;
  14. detect(credentials: any): Platform | null {
  15. if (!credentials || typeof credentials !== 'object') {
  16. return null;
  17. }
  18. // Check for Ed25519 signature type
  19. if (credentials.type === 'ed25519') {
  20. return Platform.PACIFICA;
  21. }
  22. // Check for 64-character hex private key (Ed25519 format)
  23. if (credentials.privateKey && typeof credentials.privateKey === 'string') {
  24. const privateKey = credentials.privateKey.toLowerCase();
  25. // Ed25519 private key: 64 hex characters (32 bytes)
  26. if (/^[0-9a-f]{64}$/.test(privateKey)) {
  27. return Platform.PACIFICA;
  28. }
  29. }
  30. // Check for base58 public key pattern (common in Pacifica)
  31. if (credentials.publicKey && typeof credentials.publicKey === 'string') {
  32. const publicKey = credentials.publicKey;
  33. // Base58 encoded keys are typically 32-44 characters
  34. if (/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(publicKey) && !publicKey.startsWith('0x')) {
  35. return Platform.PACIFICA;
  36. }
  37. }
  38. return null;
  39. }
  40. }
  41. /**
  42. * Aster platform detector
  43. * Detects EIP-191 (Ethereum-style) credentials
  44. */
  45. export class AsterDetector implements IPlatformDetector {
  46. readonly confidence = 0.85;
  47. detect(credentials: any): Platform | null {
  48. if (!credentials || typeof credentials !== 'object') {
  49. return null;
  50. }
  51. // Check for EIP-191 signature type
  52. if (credentials.type === 'eip191') {
  53. return Platform.ASTER;
  54. }
  55. // Check for 0x-prefixed private key (Ethereum format)
  56. if (credentials.privateKey && typeof credentials.privateKey === 'string') {
  57. const privateKey = credentials.privateKey.toLowerCase();
  58. // Ethereum private key: 0x + 64 hex characters
  59. if (/^0x[0-9a-f]{64}$/.test(privateKey)) {
  60. return Platform.ASTER;
  61. }
  62. }
  63. // Check for Ethereum address pattern
  64. if (credentials.address && typeof credentials.address === 'string') {
  65. const address = credentials.address.toLowerCase();
  66. // Ethereum address: 0x + 40 hex characters
  67. if (/^0x[0-9a-f]{40}$/.test(address)) {
  68. return Platform.ASTER;
  69. }
  70. }
  71. // Check for public key in Ethereum format
  72. if (credentials.publicKey && typeof credentials.publicKey === 'string') {
  73. const publicKey = credentials.publicKey.toLowerCase();
  74. // Ethereum public key: 0x + 128 hex characters (uncompressed)
  75. if (/^0x[0-9a-f]{128}$/.test(publicKey)) {
  76. return Platform.ASTER;
  77. }
  78. }
  79. return null;
  80. }
  81. }
  82. /**
  83. * Binance platform detector
  84. * Detects HMAC-SHA256 API key/secret credentials
  85. */
  86. export class BinanceDetector implements IPlatformDetector {
  87. readonly confidence = 0.95;
  88. detect(credentials: any): Platform | null {
  89. if (!credentials || typeof credentials !== 'object') {
  90. return null;
  91. }
  92. // Check for HMAC signature type
  93. if (credentials.type === 'hmac') {
  94. return Platform.BINANCE;
  95. }
  96. // Check for API key/secret pattern
  97. const hasApiKey = credentials.apiKey && typeof credentials.apiKey === 'string';
  98. const hasSecretKey = credentials.secretKey && typeof credentials.secretKey === 'string';
  99. if (hasApiKey && hasSecretKey) {
  100. const apiKey = credentials.apiKey;
  101. const secretKey = credentials.secretKey;
  102. // Binance API keys are typically 64 characters, alphanumeric
  103. const binanceApiKeyPattern = /^[A-Za-z0-9]{64}$/;
  104. // Binance secret keys are typically 64 characters, alphanumeric
  105. const binanceSecretKeyPattern = /^[A-Za-z0-9]{64}$/;
  106. if (binanceApiKeyPattern.test(apiKey) && binanceSecretKeyPattern.test(secretKey)) {
  107. return Platform.BINANCE;
  108. }
  109. // Alternative: any API key/secret combination for HMAC
  110. if (apiKey.length >= 16 && secretKey.length >= 16) {
  111. return Platform.BINANCE;
  112. }
  113. }
  114. return null;
  115. }
  116. }
  117. /**
  118. * Main platform detector that uses multiple detectors
  119. */
  120. export class PlatformDetector {
  121. private detectors: IPlatformDetector[];
  122. constructor() {
  123. this.detectors = [
  124. new BinanceDetector(), // Highest confidence first
  125. new PacificaDetector(),
  126. new AsterDetector()
  127. ];
  128. }
  129. /**
  130. * Detect platform for given credentials
  131. * @param credentials Credential object to analyze
  132. * @returns Detected platform or null if cannot determine
  133. */
  134. detectPlatform(credentials: any): Platform | null {
  135. if (!credentials) {
  136. return null;
  137. }
  138. // Try each detector in order of confidence
  139. for (const detector of this.detectors) {
  140. const platform = detector.detect(credentials);
  141. if (platform) {
  142. return platform;
  143. }
  144. }
  145. return null;
  146. }
  147. /**
  148. * Get detection results from all detectors
  149. * @param credentials Credential object to analyze
  150. * @returns Array of detection results with confidence scores
  151. */
  152. getAllDetectionResults(credentials: any): Array<{
  153. platform: Platform | null;
  154. confidence: number;
  155. detector: string;
  156. }> {
  157. const results = [];
  158. for (const detector of this.detectors) {
  159. const platform = detector.detect(credentials);
  160. results.push({
  161. platform,
  162. confidence: detector.confidence,
  163. detector: detector.constructor.name
  164. });
  165. }
  166. return results;
  167. }
  168. /**
  169. * Validate that credentials match the specified platform
  170. * @param credentials Credential object
  171. * @param expectedPlatform Expected platform
  172. * @returns True if credentials match the platform
  173. */
  174. validatePlatformMatch(credentials: any, expectedPlatform: Platform): boolean {
  175. const detectedPlatform = this.detectPlatform(credentials);
  176. return detectedPlatform === expectedPlatform;
  177. }
  178. /**
  179. * Get suggested platforms for ambiguous credentials
  180. * @param credentials Credential object
  181. * @returns Array of possible platforms with confidence scores
  182. */
  183. getSuggestedPlatforms(credentials: any): Array<{
  184. platform: Platform;
  185. confidence: number;
  186. reason: string;
  187. }> {
  188. const suggestions = [];
  189. const results = this.getAllDetectionResults(credentials);
  190. for (const result of results) {
  191. if (result.platform) {
  192. suggestions.push({
  193. platform: result.platform,
  194. confidence: result.confidence,
  195. reason: `Detected by ${result.detector}`
  196. });
  197. }
  198. }
  199. // Sort by confidence (highest first)
  200. return suggestions.sort((a, b) => b.confidence - a.confidence);
  201. }
  202. /**
  203. * Add custom detector
  204. * @param detector Custom platform detector
  205. */
  206. addDetector(detector: IPlatformDetector): void {
  207. this.detectors.push(detector);
  208. // Re-sort by confidence (highest first)
  209. this.detectors.sort((a, b) => b.confidence - a.confidence);
  210. }
  211. /**
  212. * Remove detector by class name
  213. * @param detectorName Name of detector class to remove
  214. */
  215. removeDetector(detectorName: string): void {
  216. this.detectors = this.detectors.filter(
  217. detector => detector.constructor.name !== detectorName
  218. );
  219. }
  220. /**
  221. * Check if credentials are valid for any supported platform
  222. * @param credentials Credential object
  223. * @returns True if credentials are valid for at least one platform
  224. */
  225. isValidCredentials(credentials: any): boolean {
  226. return this.detectPlatform(credentials) !== null;
  227. }
  228. /**
  229. * Generate credential validation report
  230. * @param credentials Credential object
  231. * @returns Detailed validation report
  232. */
  233. generateValidationReport(credentials: any): {
  234. isValid: boolean;
  235. detectedPlatform: Platform | null;
  236. allResults: Array<{
  237. platform: Platform | null;
  238. confidence: number;
  239. detector: string;
  240. }>;
  241. suggestions: Array<{
  242. platform: Platform;
  243. confidence: number;
  244. reason: string;
  245. }>;
  246. issues: string[];
  247. } {
  248. const detectedPlatform = this.detectPlatform(credentials);
  249. const allResults = this.getAllDetectionResults(credentials);
  250. const suggestions = this.getSuggestedPlatforms(credentials);
  251. const issues = [];
  252. // Check for common issues
  253. if (!credentials) {
  254. issues.push('No credentials provided');
  255. } else if (typeof credentials !== 'object') {
  256. issues.push('Credentials must be an object');
  257. } else {
  258. if (!detectedPlatform) {
  259. issues.push('Could not detect platform from credentials');
  260. }
  261. if (!credentials.type) {
  262. issues.push('Missing credential type');
  263. }
  264. // Platform-specific validation
  265. if (credentials.type === 'ed25519' && !credentials.privateKey) {
  266. issues.push('Ed25519 credentials missing privateKey');
  267. }
  268. if (credentials.type === 'eip191' && !credentials.privateKey) {
  269. issues.push('EIP-191 credentials missing privateKey');
  270. }
  271. if (credentials.type === 'hmac' && (!credentials.apiKey || !credentials.secretKey)) {
  272. issues.push('HMAC credentials missing apiKey or secretKey');
  273. }
  274. }
  275. return {
  276. isValid: detectedPlatform !== null && issues.length === 0,
  277. detectedPlatform,
  278. allResults,
  279. suggestions,
  280. issues
  281. };
  282. }
  283. }