biometric_crypto 1.0.1 copy "biometric_crypto: ^1.0.1" to clipboard
biometric_crypto: ^1.0.1 copied to clipboard

Flutter plugin for secure biometric-based cryptographic operations using Android Keystore and iOS Secure Enclave.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:biometric_crypto/biometric_crypto.dart';

void main() {
  // Service Locator / Dependency Injection Setup
  _setupDependencies();
  runApp(const MyApp());
}

void _setupDependencies() {
  // Simple DI setup - register services
  _serviceLocator.registerBiometricCryptoService();
}

class _ServiceLocator {
  static final _ServiceLocator _instance = _ServiceLocator._internal();

  late BiometricCryptoService _biometricCryptoService;

  factory _ServiceLocator() {
    return _instance;
  }

  _ServiceLocator._internal();

  void registerBiometricCryptoService() {
    _biometricCryptoService = BiometricCryptoService(BiometricCrypto());
  }

  BiometricCryptoService get biometricCryptoService => _biometricCryptoService;
}

final _serviceLocator = _ServiceLocator();

BiometricCryptoService get biometricCryptoService =>
    _serviceLocator.biometricCryptoService;

/// Service Wrapper untuk BiometricCrypto Plugin
class BiometricCryptoService {
  final BiometricCrypto _biometricCrypto;
  static const String _keyAlias = 'biometric_key';

  BiometricCryptoService(this._biometricCrypto);

  /// Check apakah biometric tersedia
  Future<bool> isBiometricAvailable() async {
    try {
      return await _biometricCrypto.isBiometricAvailable();
    } catch (e) {
      print('Error checking biometric availability: $e');
      return false;
    }
  }

  /// Check apakah key sudah ada
  Future<bool> keyExists() async {
    try {
      return await _biometricCrypto.keyExists(_keyAlias);
    } catch (e) {
      print('Error checking key existence: $e');
      return false;
    }
  }

  /// Generate key baru
  Future<bool> generateKey() async {
    try {
      final authenticated = await _biometricCrypto.authenticate();
      if (!authenticated) {
        return false;
      }
      await _biometricCrypto.generateKey(_keyAlias);
      return true;
    } catch (e) {
      print('Error generating key: $e');
      return false;
    }
  }

  /// Authenticate dengan biometric dan sign data
  Future<BiometricSignResult?> signWithBiometric(String challenge) async {
    try {
      // Authenticate user
      // final authenticated = await _biometricCrypto.authenticate();
      // if (!authenticated) {
      //   return null;
      // }

      // Sign dengan biometric
      final signature = await _biometricCrypto.sign(_keyAlias, challenge);

      // Get public key
      final publicKey = await _biometricCrypto.getPublicKey(_keyAlias);

      return BiometricSignResult(
        signature: signature,
        publicKey: publicKey,
        challenge: challenge,
      );
    } on PlatformException catch (e) {
      print('Platform Exception during biometric sign: ${e.message}');
      return null;
    } catch (e) {
      print('Error during biometric sign: $e');
      return null;
    }
  }

  /// Get public key
  Future<String> getPublicKey() async {
    try {
      return await _biometricCrypto.getPublicKey(_keyAlias);
    } catch (e) {
      print('Error getting public key: $e');
      rethrow;
    }
  }

  /// Delete key
  Future<void> deleteKey() async {
    try {
      await _biometricCrypto.deleteKey(_keyAlias);
    } catch (e) {
      print('Error deleting key: $e');
      rethrow;
    }
  }
}

class BiometricSignResult {
  final String signature;
  final String publicKey;
  final String challenge;

  BiometricSignResult({
    required this.signature,
    required this.publicKey,
    required this.challenge,
  });
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Biometric Crypto Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const BiometricCryptoDemo(),
    );
  }
}

class BiometricCryptoDemo extends StatefulWidget {
  const BiometricCryptoDemo({super.key});

  @override
  State<BiometricCryptoDemo> createState() => _BiometricCryptoDemoState();
}

class _BiometricCryptoDemoState extends State<BiometricCryptoDemo> {
  late BiometricCryptoService _service;
  bool _isBiometricAvailable = false;
  bool _keyExists = false;
  bool _isInitialized = false;
  bool _isLoading = false;
  String _statusMessage = 'Initializing...';
  BiometricSignResult? _signResult;

  @override
  void initState() {
    super.initState();
    _service = biometricCryptoService;
    _initializeBiometricFlow();
  }

  /// Flow 1: Initialize dan check biometric availability
  Future<void> _initializeBiometricFlow() async {
    if (!mounted) return;

    setState(() {
      _isLoading = true;
      _statusMessage = 'Checking biometric availability...';
    });

    try {
      //Step 0 Delete key untuk testing ulang
      await _service.deleteKey();
      // Step 1: Check biometric available
      final isBioAvailable = await _service.isBiometricAvailable();
      if (!mounted) return;

      setState(() {
        _isBiometricAvailable = isBioAvailable;
      });

      if (!isBioAvailable) {
        setState(() {
          _statusMessage = '❌ Biometric tidak tersedia di device ini';
          _isLoading = false;
          _isInitialized = true;
        });
        return;
      }

      setState(() {
        _statusMessage = 'Biometric tersedia ✓\nMengecek key...';
      });

      // Step 2: Check key exists
      final keyExist = await _service.keyExists();
      if (!mounted) return;

      setState(() {
        _keyExists = keyExist;
      });

      if (keyExist) {
        setState(() {
          _statusMessage =
              '✓ Biometric tersedia\n✓ Key sudah ada\n\nSiap untuk sign dengan biometric';
          _isLoading = false;
          _isInitialized = true;
        });
      } else {
        setState(() {
          _statusMessage =
              '✓ Biometric tersedia\n❌ Key belum ada\n\nKlik "Generate Key" untuk membuat key baru';
          _isLoading = false;
          _isInitialized = true;
        });
      }
    } catch (e) {
      if (!mounted) return;
      setState(() {
        _statusMessage = 'Error: $e';
        _isLoading = false;
        _isInitialized = true;
      });
    }
  }

  /// Flow 2: Generate key
  Future<void> _generateKeyFlow() async {
    if (!mounted) return;

    setState(() {
      _isLoading = true;
      _statusMessage = 'Generating key...';
    });

    try {
      final success = await _service.generateKey();
      if (!mounted) return;

      if (success) {
        setState(() {
          _keyExists = true;
          _statusMessage =
              '✓ Biometric tersedia\n✓ Key berhasil dibuat\n\nSiap untuk sign dengan biometric';
          _isLoading = false;
        });
      } else {
        setState(() {
          _statusMessage = 'Gagal membuat key';
          _isLoading = false;
        });
      }
    } catch (e) {
      if (!mounted) return;
      setState(() {
        _statusMessage = 'Error: $e';
        _isLoading = false;
      });
    }
  }

  /// Flow 3: Sign dengan biometric dan dapatkan public key
  Future<void> _signWithBiometricFlow() async {
    if (!mounted) return;

    setState(() {
      _isLoading = true;
      _statusMessage = 'Authenticating dengan biometric...';
    });

    try {
      // Generate random challenge
      final challenge = DateTime.now().millisecondsSinceEpoch.toString();

      final result = await _service.signWithBiometric(challenge);
      if (!mounted) return;

      if (result != null) {
        setState(() {
          _signResult = result;
          _statusMessage = 'Sign berhasil! ✓';
          _isLoading = false;
        });
      } else {
        setState(() {
          _statusMessage = 'Authentication dibatalkan atau gagal';
          _isLoading = false;
        });
      }
    } catch (e) {
      if (!mounted) return;
      setState(() {
        _statusMessage = 'Error: $e';
        _isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Biometric Crypto Demo')),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // Status Card
            Card(
              color: Colors.blue.shade50,
              child: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text(
                      'Status',
                      style: TextStyle(
                        fontSize: 18,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(height: 12),
                    Text(_statusMessage, style: const TextStyle(fontSize: 14)),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 24),

            // Flow Steps
            if (_isInitialized) ...[
              const Text(
                'Flow Biometric Signin',
                style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 8),
              _buildFlowStep(
                number: 1,
                title: 'Check Biometric Available',
                isCompleted: _isBiometricAvailable,
              ),
              const SizedBox(height: 8),
              _buildFlowStep(
                number: 2,
                title: 'Check Key Exists',
                isCompleted: _keyExists,
              ),
              const SizedBox(height: 16),

              // Action Buttons
              if (!_keyExists)
                SizedBox(
                  width: double.infinity,
                  child: ElevatedButton.icon(
                    onPressed: _isLoading ? null : _generateKeyFlow,
                    icon: const Icon(Icons.vpn_key),
                    label: const Text('Generate Key'),
                  ),
                ),
              if (!_keyExists) const SizedBox(height: 12),
              if (_keyExists)
                SizedBox(
                  width: double.infinity,
                  child: ElevatedButton.icon(
                    onPressed: _isLoading ? null : _signWithBiometricFlow,
                    icon: const Icon(Icons.fingerprint),
                    label: const Text('Sign dengan Biometric'),
                  ),
                ),

              // Result Card
              if (_signResult != null) ...[
                const SizedBox(height: 24),
                const Text(
                  'Hasil Sign',
                  style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
                ),
                const SizedBox(height: 12),
                _buildResultField(
                  label: 'Challenge',
                  value: _signResult!.challenge,
                ),
                const SizedBox(height: 12),
                _buildResultField(
                  label: 'Signature',
                  value: _signResult!.signature,
                ),
                const SizedBox(height: 12),
                _buildResultField(
                  label: 'Public Key',
                  value: _signResult!.publicKey,
                ),
                const SizedBox(height: 12),
                SizedBox(
                  width: double.infinity,
                  child: ElevatedButton.icon(
                    onPressed: _isLoading ? null : _signWithBiometricFlow,
                    icon: const Icon(Icons.refresh),
                    label: const Text('Sign Lagi'),
                  ),
                ),
              ],
            ],

            if (_isLoading)
              const Padding(
                padding: EdgeInsets.all(32.0),
                child: Center(child: CircularProgressIndicator()),
              ),
          ],
        ),
      ),
    );
  }

  Widget _buildFlowStep({
    required int number,
    required String title,
    required bool isCompleted,
  }) {
    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(
        border: Border.all(color: isCompleted ? Colors.green : Colors.grey),
        borderRadius: BorderRadius.circular(8),
        color: isCompleted ? Colors.green.shade50 : Colors.grey.shade50,
      ),
      child: Row(
        children: [
          Container(
            width: 32,
            height: 32,
            decoration: BoxDecoration(
              shape: BoxShape.circle,
              color: isCompleted ? Colors.green : Colors.grey,
            ),
            child: Center(
              child: Text(
                number.toString(),
                style: const TextStyle(
                  color: Colors.white,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ),
          ),
          const SizedBox(width: 12),
          Expanded(
            child: Text(
              title,
              style: TextStyle(
                fontSize: 14,
                color: isCompleted
                    ? Colors.green.shade900
                    : Colors.grey.shade700,
              ),
            ),
          ),
          if (isCompleted) const Icon(Icons.check_circle, color: Colors.green),
        ],
      ),
    );
  }

  Widget _buildResultField({required String label, required String value}) {
    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(
        border: Border.all(color: Colors.grey.shade300),
        borderRadius: BorderRadius.circular(8),
        color: Colors.grey.shade50,
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            label,
            style: const TextStyle(
              fontSize: 12,
              fontWeight: FontWeight.bold,
              color: Colors.grey,
            ),
          ),
          const SizedBox(height: 8),
          SelectableText(
            value,
            style: const TextStyle(fontSize: 12, fontFamily: 'monospace'),
          ),
        ],
      ),
    );
  }
}
0
likes
150
points
0
downloads

Publisher

unverified uploader

Weekly Downloads

Flutter plugin for secure biometric-based cryptographic operations using Android Keystore and iOS Secure Enclave.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on biometric_crypto

Packages that implement biometric_crypto