Cleaning old generation

This commit is contained in:
stcb 2025-01-30 16:03:24 +02:00
parent 0b1499b32e
commit a3532f4d36
4 changed files with 0 additions and 365 deletions

View File

@ -1,138 +0,0 @@
import 'package:flutter/material.dart';
import 'dart:typed_data';
import 'dart:convert';
import 'package:pointycastle/export.dart' as crypto;
import 'package:file_picker/file_picker.dart';
import 'dart:io';
import 'widgets/key_storage.dart';
class ExportPrivateKeyPage extends StatefulWidget {
const ExportPrivateKeyPage({super.key});
@override
_ExportPrivateKeyPageState createState() => _ExportPrivateKeyPageState();
}
class _ExportPrivateKeyPageState extends State<ExportPrivateKeyPage> {
final TextEditingController _passwordController = TextEditingController();
Future<void> _exportPrivateKey() async {
final keyStorage = KeyStorage();
final privateKeyPem = await keyStorage.getPrivateKey();
if (privateKeyPem == null) {
// Show error message if there's no key
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('No private key found to export.'),
),
);
return;
}
final password = _passwordController.text;
if (password.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Please enter a password.'),
),
);
return;
}
final encryptedData = _encryptPrivateKey(privateKeyPem, password);
final outputFile = await FilePicker.platform.saveFile(
dialogTitle: 'Save encrypted private key',
fileName: 'private_key_encrypted.aes',
);
if (outputFile != null) {
try {
final file = File(outputFile);
await file.writeAsBytes(encryptedData);
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Key Exported'),
content: const Text('The encrypted private key has been exported successfully.'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('OK'),
),
],
),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Failed to write file: $e'),
),
);
}
}
}
Uint8List _encryptPrivateKey(String privateKey, String password) {
// Derive a key from the password using PBKDF2
final derivator = crypto.PBKDF2KeyDerivator(
crypto.HMac(crypto.SHA256Digest(), 64),
);
final salt = Uint8List.fromList(utf8.encode('some_salt')); // In production, use a random salt and store it securely
derivator.init(crypto.Pbkdf2Parameters(salt, 1000, 32));
final key = derivator.process(Uint8List.fromList(utf8.encode(password)));
// Initialize AES-CBC cipher with PKCS7 padding
final iv = Uint8List(16); // zero IV for example, in production use random IV and store it
final params = crypto.PaddedBlockCipherParameters<crypto.ParametersWithIV<crypto.KeyParameter>, Null>(
crypto.ParametersWithIV<crypto.KeyParameter>(crypto.KeyParameter(key), iv),
null,
);
final cipher = crypto.PaddedBlockCipher('AES/CBC/PKCS7');
cipher.init(true, params);
final input = Uint8List.fromList(utf8.encode(privateKey));
final output = cipher.process(input);
return output;
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
title: const Text('Export Private Key'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
const Text(
'Enter a password to encrypt the private key:',
style: TextStyle(color: Colors.white),
),
TextField(
controller: _passwordController,
obscureText: true,
style: const TextStyle(color: Colors.white),
decoration: const InputDecoration(
hintText: 'Password',
hintStyle: TextStyle(color: Colors.grey),
),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _exportPrivateKey,
child: const Text('Export Encrypted Private Key'),
),
],
),
),
);
}
}

View File

@ -9,85 +9,6 @@ import 'widgets/key_storage.dart';
class GenerateNewKeyPairPage extends StatelessWidget {
const GenerateNewKeyPairPage({super.key});
Future<Map<String, String>> _generateKeyPair() async {
final keyParams = crypto.RSAKeyGeneratorParameters(
BigInt.parse('65537'),
2048,
64,
);
final secureRandom = crypto.FortunaRandom();
final random = Random.secure();
final seeds = List<int>.generate(32, (_) => random.nextInt(256));
secureRandom.seed(crypto.KeyParameter(Uint8List.fromList(seeds)));
final rngParams = crypto.ParametersWithRandom(keyParams, secureRandom);
final keyGenerator = crypto.RSAKeyGenerator();
keyGenerator.init(rngParams);
final pair = keyGenerator.generateKeyPair();
final publicKey = pair.publicKey as crypto.RSAPublicKey;
final privateKey = pair.privateKey as crypto.RSAPrivateKey;
final publicKeyPem = _encodePublicKeyToPemPKCS1(publicKey);
final privateKeyPem = _encodePrivateKeyToPemPKCS1(privateKey);
// Save keys securely
final keyStorage = KeyStorage();
await keyStorage.saveKeys(publicKey: publicKeyPem, privateKey: privateKeyPem);
return {'publicKey': publicKeyPem, 'privateKey': privateKeyPem};
}
String _encodePublicKeyToPemPKCS1(crypto.RSAPublicKey publicKey) {
final bytes = _encodePublicKeyToDer(publicKey);
return _formatPem(bytes, 'RSA PUBLIC KEY');
}
String _encodePrivateKeyToPemPKCS1(crypto.RSAPrivateKey privateKey) {
final bytes = _encodePrivateKeyToDer(privateKey);
return _formatPem(bytes, 'RSA PRIVATE KEY');
}
Uint8List _encodePublicKeyToDer(crypto.RSAPublicKey publicKey) {
final algorithmSeq = ASN1Sequence();
// Create the OID directly with the arcs
algorithmSeq.add(ASN1ObjectIdentifier([1, 2, 840, 113549, 1, 1, 1]));
algorithmSeq.add(ASN1Null());
final publicKeySeq = ASN1Sequence();
publicKeySeq.add(ASN1Integer(publicKey.modulus!));
publicKeySeq.add(ASN1Integer(publicKey.exponent!));
final publicKeyBitString = ASN1BitString(Uint8List.fromList(publicKeySeq.encodedBytes));
final topLevelSeq = ASN1Sequence();
topLevelSeq.add(algorithmSeq);
topLevelSeq.add(publicKeyBitString);
return Uint8List.fromList(topLevelSeq.encodedBytes);
}
Uint8List _encodePrivateKeyToDer(crypto.RSAPrivateKey privateKey) {
final privateKeySeq = ASN1Sequence();
privateKeySeq.add(ASN1Integer(BigInt.from(0))); // Version
privateKeySeq.add(ASN1Integer(privateKey.n!));
privateKeySeq.add(ASN1Integer(privateKey.exponent!));
privateKeySeq.add(ASN1Integer(privateKey.d!));
privateKeySeq.add(ASN1Integer(privateKey.p!));
privateKeySeq.add(ASN1Integer(privateKey.q!));
privateKeySeq.add(ASN1Integer(privateKey.d! % (privateKey.p! - BigInt.one)));
privateKeySeq.add(ASN1Integer(privateKey.d! % (privateKey.q! - BigInt.one)));
privateKeySeq.add(ASN1Integer(privateKey.q!.modInverse(privateKey.p!)));
return Uint8List.fromList(privateKeySeq.encodedBytes);
}
String _formatPem(Uint8List bytes, String label) {
final base64Data = base64Encode(bytes);
final chunks = RegExp('.{1,64}').allMatches(base64Data).map((m) => m.group(0)!);
return '-----BEGIN $label-----\n${chunks.join('\n')}\n-----END $label-----';
}
@override
Widget build(BuildContext context) {

View File

@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'show_public_key_qr.dart';
import 'show_public_key_text.dart';
import 'generate_new_key_pair.dart';
import 'export_private_key.dart';
import 'delete_key_pair.dart';
class KeyManagementPage extends StatelessWidget {
@ -28,12 +27,6 @@ class KeyManagementPage extends StatelessWidget {
MaterialPageRoute(builder: (context) => const GenerateNewKeyPairPage()),
);
break;
case 'Export private key to password-encrypted file (AES 256)':
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const ExportPrivateKeyPage()),
);
break;
case 'Delete a key pair':
Navigator.push(
context,

View File

@ -1,141 +0,0 @@
// 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
}