fingerprint_pro 1.0.2
fingerprint_pro: ^1.0.2 copied to clipboard
Secure biometric authentication and keystore-based signature system for Flutter, providing both simple and advanced fingerprint/face unlock features.
example/lib/fingerprint_pro_example.dart
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/material.dart';
// Use the mock implementation for publishing
import 'mock_fingerprint_pro.dart' as fp;
// Simple type definitions for the example
enum BiometricType {
fingerprint,
face,
iris,
strong,
weak,
}
class BiometricResult {
final bool success;
final BiometricError? error;
final String? errorMessage;
const BiometricResult({
required this.success,
this.error,
this.errorMessage,
});
factory BiometricResult.success() => const BiometricResult(success: true);
factory BiometricResult.failure(BiometricError error, [String? message]) =>
BiometricResult(success: false, error: error, errorMessage: message);
}
enum BiometricError {
notAvailable,
notEnrolled,
cancelled,
authenticationFailed,
unsupported,
permissionDenied,
temporarilyUnavailable,
permanentlyUnavailable,
timeout,
platformError,
keyGenerationFailed,
keyNotFound,
signatureFailed,
invalidKeySpec,
}
extension BiometricErrorExtension on BiometricError {
String get description {
switch (this) {
case BiometricError.notAvailable:
return 'Biometric authentication is not available on this device';
case BiometricError.notEnrolled:
return 'No biometric credentials are enrolled on this device';
case BiometricError.cancelled:
return 'Biometric authentication was cancelled by the user';
case BiometricError.authenticationFailed:
return 'Biometric authentication failed';
case BiometricError.unsupported:
return 'Biometric authentication is not supported on this device';
case BiometricError.permissionDenied:
return 'Permission denied for biometric authentication';
case BiometricError.temporarilyUnavailable:
return 'Biometric authentication is temporarily unavailable';
case BiometricError.permanentlyUnavailable:
return 'Biometric authentication is permanently unavailable';
case BiometricError.timeout:
return 'Biometric authentication timed out';
case BiometricError.platformError:
return 'A platform-specific error occurred';
case BiometricError.keyGenerationFailed:
return 'Failed to generate cryptographic key pair';
case BiometricError.keyNotFound:
return 'The specified key was not found';
case BiometricError.signatureFailed:
return 'Failed to create or verify signature';
case BiometricError.invalidKeySpec:
return 'Invalid key specification provided';
}
}
}
class BiometricOptions {
final bool stickyAuth;
final bool sensitiveTransaction;
final String? reason;
final String? title;
final String? subtitle;
final String? description;
final bool useErrorDialogs;
const BiometricOptions({
this.stickyAuth = false,
this.sensitiveTransaction = false,
this.reason,
this.title,
this.subtitle,
this.description,
this.useErrorDialogs = true,
});
}
class KeyGenerationOptions {
final bool userAuthenticationRequired;
final int userAuthenticationValiditySeconds;
final bool useEcdsa;
final int keySize;
final bool useSecureHardware;
const KeyGenerationOptions({
this.userAuthenticationRequired = true,
this.userAuthenticationValiditySeconds = 30,
this.useEcdsa = true,
this.keySize = 2048,
this.useSecureHardware = true,
});
}
class KeyGenerationResult {
final bool success;
final String? publicKeyPem;
final BiometricError? error;
final String? errorMessage;
const KeyGenerationResult({
required this.success,
this.publicKeyPem,
this.error,
this.errorMessage,
});
factory KeyGenerationResult.success(String publicKeyPem) =>
KeyGenerationResult(success: true, publicKeyPem: publicKeyPem);
factory KeyGenerationResult.failure(BiometricError error, [String? message]) =>
KeyGenerationResult(success: false, error: error, errorMessage: message);
}
class SignatureResult {
final bool success;
final Uint8List? signature;
final BiometricError? error;
final String? errorMessage;
const SignatureResult({
required this.success,
this.signature,
this.error,
this.errorMessage,
});
factory SignatureResult.success(Uint8List signature) =>
SignatureResult(success: true, signature: signature);
factory SignatureResult.failure(BiometricError error, [String? message]) =>
SignatureResult(success: false, error: error, errorMessage: message);
}
void main() {
runApp(const MyApp());
}
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final fp.FingerprintPro _fingerprintPro = fp.FingerprintPro();
bool _canAuthenticate = false;
List<BiometricType> _availableBiometrics = [];
String _status = 'Initializing...';
String _generatedKeyAlias = '';
String _publicKeyPem = '';
String _signatureResult = '';
@override
void initState() {
super.initState();
_initializeBiometrics();
}
Future<void> _initializeBiometrics() async {
try {
// Check if biometric authentication is available
_canAuthenticate = await _fingerprintPro.canAuthenticate();
if (_canAuthenticate) {
_availableBiometrics = await _fingerprintPro.getAvailableBiometrics();
}
setState(() {
_status = _canAuthenticate
? 'Biometric authentication available'
: 'Biometric authentication not available';
});
} catch (e) {
setState(() {
_status = 'Error initializing biometrics: $e';
});
}
}
Future<void> _authenticateUser() async {
try {
setState(() {
_status = 'Authenticating...';
});
final result = await _fingerprintPro.authenticate(
reason: 'Please authenticate to access the app',
options: const BiometricOptions(
stickyAuth: false,
useErrorDialogs: true,
),
);
setState(() {
if (result.success) {
_status = 'Authentication successful!';
} else {
_status = 'Authentication failed: ${result.error != null ? result.error!.description : 'Unknown error'}';
}
});
} catch (e) {
setState(() {
_status = 'Authentication error: $e';
});
}
}
Future<void> _generateKeyPair() async {
try {
setState(() {
_status = 'Generating key pair...';
});
final result = await _fingerprintPro.generateKeyPair(
keyAlias: 'example_key_${DateTime.now().millisecondsSinceEpoch}',
options: const KeyGenerationOptions(
userAuthenticationRequired: true,
useEcdsa: true,
),
);
if (result.success && result.publicKeyPem != null) {
setState(() {
_generatedKeyAlias = 'example_key_${DateTime.now().millisecondsSinceEpoch}';
_publicKeyPem = result.publicKeyPem!;
_status = 'Key pair generated successfully!';
});
} else {
setState(() {
_status = 'Key generation failed: ${result.error != null ? result.error!.description : 'Unknown error'}';
});
}
} catch (e) {
setState(() {
_status = 'Key generation error: $e';
});
}
}
Future<void> _signData() async {
if (_generatedKeyAlias.isEmpty) {
setState(() {
_status = 'Please generate a key pair first';
});
return;
}
try {
setState(() {
_status = 'Signing data...';
});
// Sample data to sign
const data = 'Hello, World! This is sample data to sign.';
final dataBytes = utf8.encode(data);
final result = await _fingerprintPro.signWithKey(
keyAlias: _generatedKeyAlias,
data: Uint8List.fromList(dataBytes),
);
if (result.success && result.signature != null) {
setState(() {
_signatureResult = 'Signature created: ${result.signature!.length} bytes';
_status = 'Data signed successfully!';
});
} else {
setState(() {
_status = 'Signing failed: ${result.error != null ? result.error!.description : 'Unknown error'}';
});
}
} catch (e) {
setState(() {
_status = 'Signing error: $e';
});
}
}
Future<void> _removeKey() async {
if (_generatedKeyAlias.isEmpty) {
setState(() {
_status = 'No key to remove';
});
return;
}
try {
setState(() {
_status = 'Removing key...';
});
final success = await _fingerprintPro.removeKey(_generatedKeyAlias);
if (success) {
setState(() {
_generatedKeyAlias = '';
_publicKeyPem = '';
_signatureResult = '';
_status = 'Key removed successfully!';
});
} else {
setState(() {
_status = 'Failed to remove key';
});
}
} catch (e) {
setState(() {
_status = 'Remove key error: $e';
});
}
}
Future<void> _listKeys() async {
try {
setState(() {
_status = 'Listing keys...';
});
final keys = await _fingerprintPro.listKeys();
setState(() {
_status = 'Available keys: ${keys.join(', ')}';
});
} catch (e) {
setState(() {
_status = 'List keys error: $e';
});
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Fingerprint Pro Example'),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Status Card
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Status: $_status',
style: const TextStyle(fontSize: 16),
),
const SizedBox(height: 8),
Text(
'Available Biometrics: ${_availableBiometrics.map((e) => e.name).join(', ')}',
style: const TextStyle(fontSize: 14, color: Colors.grey),
),
],
),
),
),
const SizedBox(height: 16),
// Biometric Authentication Section
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Biometric Authentication',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
const Text(
'Simple biometric authentication using local_auth wrapper.',
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _canAuthenticate ? _authenticateUser : null,
child: const Text('Authenticate'),
),
],
),
),
),
const SizedBox(height: 16),
// Key Management Section
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Key Management',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
const Text(
'Advanced key generation and signing with biometric authentication.',
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: _generateKeyPair,
child: const Text('Generate Key Pair'),
),
),
const SizedBox(width: 8),
Expanded(
child: ElevatedButton(
onPressed: _generatedKeyAlias.isNotEmpty ? _removeKey : null,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
child: const Text('Remove Key'),
),
),
],
),
const SizedBox(height: 8),
ElevatedButton(
onPressed: _generatedKeyAlias.isNotEmpty ? _signData : null,
child: const Text('Sign Data'),
),
const SizedBox(height: 8),
ElevatedButton(
onPressed: _listKeys,
child: const Text('List Keys'),
),
],
),
),
),
const SizedBox(height: 16),
// Key Information Section
if (_publicKeyPem.isNotEmpty)
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Generated Key Information',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Text(
'Key Alias: $_generatedKeyAlias',
style: const TextStyle(fontSize: 14),
),
const SizedBox(height: 8),
const Text(
'Public Key (PEM):',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
),
const SizedBox(height: 4),
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(4),
),
child: Text(
_publicKeyPem,
style: const TextStyle(
fontSize: 12,
fontFamily: 'monospace',
),
),
),
],
),
),
),
// Signature Result Section
if (_signatureResult.isNotEmpty)
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Signature Result',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Text(_signatureResult),
],
),
),
),
const SizedBox(height: 16),
// Information Card
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'About This Example',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
const Text(
'This example demonstrates both modes of fingerprint_pro:\n\n'
'1. Simple biometric authentication (Authenticate button)\n'
'2. Advanced key management with biometric-protected signing (Generate Key Pair, Sign Data)\n\n'
'The advanced mode generates ECDSA key pairs in the device\'s secure keystore/keychain, '
'protected by biometric authentication. The public key can be shared with your server '
'for signature verification.',
),
],
),
),
),
],
),
),
),
);
}
}