From a3532f4d361938f586cd905bb397d6cd292bb839 Mon Sep 17 00:00:00 2001 From: stcb <21@stcb.cc> Date: Thu, 30 Jan 2025 16:03:24 +0200 Subject: [PATCH] Cleaning old generation --- .../settings/key/export_private_key.dart | 138 ----------------- .../settings/key/generate_new_key_pair.dart | 79 ---------- .../settings/key/manage_keys_page.dart | 7 - .../settings/key/widgets/key_management.dart | 141 ------------------ 4 files changed, 365 deletions(-) delete mode 100644 dialer/lib/features/settings/key/export_private_key.dart diff --git a/dialer/lib/features/settings/key/export_private_key.dart b/dialer/lib/features/settings/key/export_private_key.dart deleted file mode 100644 index 74fc6d5..0000000 --- a/dialer/lib/features/settings/key/export_private_key.dart +++ /dev/null @@ -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 { - final TextEditingController _passwordController = TextEditingController(); - - Future _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, Null>( - crypto.ParametersWithIV(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'), - ), - ], - ), - ), - ); - } -} diff --git a/dialer/lib/features/settings/key/generate_new_key_pair.dart b/dialer/lib/features/settings/key/generate_new_key_pair.dart index 8c038f5..95707d6 100644 --- a/dialer/lib/features/settings/key/generate_new_key_pair.dart +++ b/dialer/lib/features/settings/key/generate_new_key_pair.dart @@ -9,85 +9,6 @@ import 'widgets/key_storage.dart'; class GenerateNewKeyPairPage extends StatelessWidget { const GenerateNewKeyPairPage({super.key}); - Future> _generateKeyPair() async { - final keyParams = crypto.RSAKeyGeneratorParameters( - BigInt.parse('65537'), - 2048, - 64, - ); - - final secureRandom = crypto.FortunaRandom(); - final random = Random.secure(); - final seeds = List.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) { diff --git a/dialer/lib/features/settings/key/manage_keys_page.dart b/dialer/lib/features/settings/key/manage_keys_page.dart index 7e4a65f..c1002fc 100644 --- a/dialer/lib/features/settings/key/manage_keys_page.dart +++ b/dialer/lib/features/settings/key/manage_keys_page.dart @@ -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, diff --git a/dialer/lib/features/settings/key/widgets/key_management.dart b/dialer/lib/features/settings/key/widgets/key_management.dart index 0452e74..e69de29 100644 --- a/dialer/lib/features/settings/key/widgets/key_management.dart +++ b/dialer/lib/features/settings/key/widgets/key_management.dart @@ -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 generateKeyPairWithRecovery() async { - // 1. Generate a 12-word recovery phrase - bip39.Mnemonic mnemonic = bip39.Mnemonic.generate(bip39.Language.english); - - // 2. Derive seed from mnemonic - List 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 _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 _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 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 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 -}