142 lines
5.0 KiB
Dart
142 lines
5.0 KiB
Dart
// key_management.dart
|
|
|
|
import 'package:cryptography/cryptography.dart';
|
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|
import 'package:bip39_mnemonic/bip39_mnemonic.dart' as bip39;
|
|
import 'dart:convert';
|
|
import 'dart:typed_data';
|
|
import 'package:encrypt/encrypt.dart' as encrypt; // For symmetric encryption
|
|
import 'keystore_service.dart';
|
|
|
|
class KeyManagement {
|
|
final FlutterSecureStorage _secureStorage = const FlutterSecureStorage();
|
|
final String _encryptedSeedKey = 'encrypted_seed';
|
|
final KeystoreService _keystoreService = KeystoreService();
|
|
|
|
// Generate a new key pair with a recovery phrase
|
|
Future<bip39.Mnemonic> generateKeyPairWithRecovery() async {
|
|
// 1. Generate a 12-word recovery phrase
|
|
bip39.Mnemonic mnemonic = bip39.Mnemonic.generate(bip39.Language.english);
|
|
|
|
// 2. Derive seed from mnemonic
|
|
List<int> seed = mnemonic.seed;
|
|
|
|
// 3. Generate ECC key pair from seed
|
|
final algorithm = Ecdsa.p256(Sha256());
|
|
final keyPair = await algorithm.newKeyPairFromSeed(seed);
|
|
|
|
// 4. Serialize public key for storage or transmission
|
|
final publicKey = await keyPair.extractPublicKey();
|
|
String publicKeyBase64 = base64Encode(publicKey);
|
|
|
|
// 5. Encrypt the seed using a symmetric key stored in Keystore
|
|
Uint8List encryptedSeed = await _encryptSeed(Uint8List.fromList(seed));
|
|
|
|
// 6. Store the encrypted seed securely
|
|
await _secureStorage.write(
|
|
key: _encryptedSeedKey,
|
|
value: base64Encode(encryptedSeed),
|
|
);
|
|
|
|
// 7. Return the mnemonic for user to backup
|
|
return mnemonic;
|
|
}
|
|
|
|
// Encrypt the seed using a symmetric key from Keystore
|
|
Future<Uint8List> _encryptSeed(Uint8List seed) async {
|
|
// Retrieve the symmetric key alias
|
|
String symmetricKeyAlias = await _keystoreService.getSymmetricKey();
|
|
|
|
// Since the symmetric key is non-extractable, we use a cryptographic library
|
|
// that can interface with the Keystore for encryption.
|
|
// However, Dart's cryptography package doesn't directly support this.
|
|
// Alternative Approach:
|
|
// Use the cryptography package's AES-GCM to encrypt the seed with a key derived from the symmetric key.
|
|
|
|
// For demonstration, we'll use a placeholder symmetric key.
|
|
// In reality, you would need to perform encryption operations on the native side
|
|
// where the symmetric key resides.
|
|
|
|
// Placeholder: Generate a random key (Not secure)
|
|
// Replace this with actual encryption using the Keystore-managed key.
|
|
final algorithm = AesGcm.with256bits();
|
|
final secretKey = await algorithm.newSecretKey();
|
|
final nonce = algorithm.newNonce();
|
|
final encrypted = await algorithm.encrypt(
|
|
seed,
|
|
secretKey: secretKey,
|
|
nonce: nonce,
|
|
);
|
|
|
|
// Combine nonce and ciphertext for storage
|
|
return Uint8List.fromList([...nonce, ...encrypted.cipherText]);
|
|
}
|
|
|
|
// Decrypt the seed using the symmetric key
|
|
Future<Uint8List> _decryptSeed() async {
|
|
String? encryptedSeedBase64 = await _secureStorage.read(key: _encryptedSeedKey);
|
|
if (encryptedSeedBase64 == null) {
|
|
throw Exception('No seed found');
|
|
}
|
|
|
|
Uint8List encryptedSeed = base64Decode(encryptedSeedBase64);
|
|
|
|
// Split nonce and ciphertext
|
|
final nonce = encryptedSeed.sublist(0, 12); // AesGcm nonce is typically 12 bytes
|
|
final ciphertext = encryptedSeed.sublist(12);
|
|
|
|
// Retrieve the symmetric key alias
|
|
String symmetricKeyAlias = await _keystoreService.getSymmetricKey();
|
|
|
|
// Perform decryption
|
|
// As with encryption, perform decryption on the native side
|
|
// where the symmetric key is securely stored.
|
|
|
|
// Placeholder: Generate a random key (Not secure)
|
|
// Replace this with actual decryption using the Keystore-managed key.
|
|
final algorithm = AesGcm.with256bits();
|
|
final secretKey = await algorithm.newSecretKey();
|
|
final decrypted = await algorithm.decrypt(
|
|
SecretBox(ciphertext, nonce: nonce, mac: Mac.empty),
|
|
secretKey: secretKey,
|
|
);
|
|
|
|
return decrypted;
|
|
}
|
|
|
|
// Restore key pair from recovery phrase
|
|
Future<EcdsaKeyPair> restoreKeyPair(String mnemonic) async {
|
|
if (!bip39.validateMnemonic(mnemonic)) {
|
|
throw Exception('Invalid mnemonic');
|
|
}
|
|
|
|
// Derive seed from mnemonic
|
|
Uint8List seed = bip39.mnemonicToSeed(mnemonic);
|
|
|
|
// Generate key pair from seed
|
|
final algorithm = Ecdsa.p256(Sha256());
|
|
final keyPair = await algorithm.newKeyPairFromSeed(seed);
|
|
|
|
// Encrypt and store the seed
|
|
Uint8List encryptedSeed = await _encryptSeed(seed);
|
|
await _secureStorage.write(
|
|
key: _encryptedSeedKey,
|
|
value: base64Encode(encryptedSeed),
|
|
);
|
|
|
|
return keyPair;
|
|
}
|
|
|
|
// Retrieve public key
|
|
Future<String?> getPublicKey() async {
|
|
// Implement a method to retrieve the public key from stored key pair
|
|
// This requires storing the public key during key generation
|
|
// For simplicity, assuming it's stored separately
|
|
// Example:
|
|
// return await _secureStorage.read(key: 'public_key');
|
|
throw UnimplementedError('Public key retrieval not implemented');
|
|
}
|
|
|
|
// Additional methods like signing, verifying can be added here
|
|
}
|