114 lines
5.3 KiB
Dart
114 lines
5.3 KiB
Dart
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<String> _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<bool> isUploadEnabled() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
return prefs.getBool(_uploadEnabledKey) ?? false;
|
|
}
|
|
|
|
/// Active ou désactive l'envoi de données
|
|
Future<void> setUploadEnabled(bool enabled) async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.setBool(_uploadEnabledKey, enabled);
|
|
}
|
|
|
|
/// Gets the unique 15-word identity phrase
|
|
Future<String> 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<String> _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<String> phraseWords = [];
|
|
for (int i = 0; i < 15; i++) {
|
|
final randomIndex = random.nextInt(_wordList.length);
|
|
phraseWords.add(_wordList[randomIndex]);
|
|
}
|
|
|
|
return phraseWords.join(' ');
|
|
}
|
|
}
|