import 'dart:math'; import 'dart:convert'; import 'package:crypto/crypto.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'dart:io'; import 'package:flutter/foundation.dart'; class WalletIdentityService { static const String _prefsKey = 'wallet_identity_phrase'; static const String _uploadEnabledKey = 'is_ai_upload_enabled'; // A standard list of 256 words (8 bits of entropy per word) static const List _wordList = [ // ... same as before 'abandon', 'ability', 'able', 'about', 'above', 'absent', 'absorb', 'abstract', 'absurd', 'abuse', 'access', 'accident', 'account', 'accuse', 'achieve', 'acid', 'acoustic', 'acquire', 'across', 'act', 'action', 'actor', 'actress', 'actual', 'adapt', 'add', 'addict', 'address', 'adjust', 'admit', 'adult', 'advance', 'advice', 'aerobic', 'affair', 'afford', 'afraid', 'again', 'age', 'agent', 'agree', 'ahead', 'aim', 'air', 'airport', 'aisle', 'alarm', 'album', 'alcohol', 'alert', 'alien', 'all', 'alley', 'allow', 'almost', 'alone', 'alpha', 'already', 'also', 'alter', 'always', 'amateur', 'amazing', 'among', 'amount', 'amused', 'analyst', 'anchor', 'ancient', 'anger', 'angle', 'angry', 'animal', 'ankle', 'announce', 'annual', 'another', 'answer', 'antenna', 'antique', 'anxiety', 'any', 'apart', 'apology', 'appear', 'apple', 'approve', 'april', 'arch', 'arctic', 'area', 'arena', 'argue', 'arm', 'armed', 'armor', 'army', 'around', 'arrange', 'arrest', 'arrive', 'arrow', 'art', 'artefact', 'artist', 'artwork', 'ask', 'aspect', 'assault', 'asset', 'assist', 'assume', 'asthma', 'athlete', 'atom', 'attack', 'attend', 'attitude', 'attract', 'auction', 'audit', 'august', 'aunt', 'author', 'auto', 'autumn', 'average', 'avocado', 'avoid', 'awake', 'aware', 'away', 'awesome', 'awful', 'awkward', 'axis', 'baby', 'bachelor', 'bacon', 'badge', 'bag', 'balance', 'balcony', 'ball', 'bamboo', 'banana', 'banner', 'bar', 'barely', 'bargain', 'barrel', 'base', 'basic', 'basket', 'battle', 'beach', 'bean', 'beauty', 'because', 'become', 'beef', 'before', 'begin', 'behave', 'behind', 'believe', 'below', 'belt', 'bench', 'benefit', 'best', 'betray', 'better', 'between', 'beyond', 'bicycle', 'bid', 'bike', 'bind', 'biology', 'bird', 'birth', 'bitter', 'black', 'blade', 'blame', 'blanket', 'blast', 'bleak', 'bless', 'blind', 'blood', 'blossom', 'blouse', 'blue', 'blur', 'blush', 'board', 'boat', 'body', 'boil', 'bomb', 'bone', 'bonus', 'book', 'boost', 'border', 'boring', 'borrow', 'boss', 'bottom', 'bounce', 'box', 'boy', 'bracket', 'brain', 'brand', 'brass', 'brave', 'bread', 'breeze', 'brick', 'bridge', 'brief', 'bright', 'bring', 'brisk', 'broccoli', 'broken', 'bronze', 'broom', 'brother', 'brown', 'brush', 'bubble', 'buddy', 'budget', 'buffalo', 'build', 'bulb', 'bulk', 'bullet', 'bundle', 'bunker', 'burden', 'burger', 'burst', 'bus', 'business', 'butter' ]; /// Vérifie si l'utilisateur a accepté l'envoi de données à l'IA Future isUploadEnabled() async { final prefs = await SharedPreferences.getInstance(); return prefs.getBool(_uploadEnabledKey) ?? false; } /// Active ou désactive l'envoi de données Future setUploadEnabled(bool enabled) async { final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_uploadEnabledKey, enabled); } /// Gets the unique 15-word identity phrase Future getIdentityPhrase() async { final prefs = await SharedPreferences.getInstance(); // Check if we already generated it final existingPhrase = prefs.getString(_prefsKey); if (existingPhrase != null && existingPhrase.isNotEmpty) { return existingPhrase; } // Generate new phrase based on device ID final phrase = await _generatePhraseFromDevice(); // Save for future use await prefs.setString(_prefsKey, phrase); return phrase; } Future _generatePhraseFromDevice() async { final deviceInfo = DeviceInfoPlugin(); String deviceId = 'unknown_device_${DateTime.now().millisecondsSinceEpoch}'; try { if (Platform.isAndroid) { final androidInfo = await deviceInfo.androidInfo; deviceId = androidInfo.id; // Unique Android ID } else if (Platform.isIOS) { final iosInfo = await deviceInfo.iosInfo; deviceId = iosInfo.identifierForVendor ?? deviceId; } else if (Platform.isWindows) { final windowsInfo = await deviceInfo.windowsInfo; deviceId = windowsInfo.deviceId; } } catch (e) { debugPrint('Erreur lecture device ID: $e'); } // Add a salt to the device ID final salt = 'bully_app_wallet_salt_v1'; final bytes = utf8.encode(deviceId + salt); final digest = sha256.convert(bytes); // Use the hash to seed a random number generator // We take the first 4 bytes of the hash as the seed final seedBytes = digest.bytes.sublist(0, 4); final seed = (seedBytes[0] << 24) | (seedBytes[1] << 16) | (seedBytes[2] << 8) | seedBytes[3]; final random = Random(seed); List phraseWords = []; for (int i = 0; i < 15; i++) { final randomIndex = random.nextInt(_wordList.length); phraseWords.add(_wordList[randomIndex]); } return phraseWords.join(' '); } }