Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions .metadata
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# This file should be version controlled and should not be manually edited.

version:
revision: "54e66469a933b60ddf175f858f82eaeb97e48c8d"
revision: "adc901062556672b4138e18a4dc62a4be8f4b3c2"
channel: "stable"

project_type: plugin
Expand All @@ -13,14 +13,17 @@ project_type: plugin
migration:
platforms:
- platform: root
create_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d
base_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d
create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
- platform: android
create_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d
base_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d
create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
- platform: ios
create_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d
base_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d
create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
- platform: macos
create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2

# User provided section

Expand Down
50 changes: 50 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,53 @@
## [8.5.0] - 2025-12-09

### Added - macOS Platform Support

#### Platform Integration
* **Full macOS support** for biometric authentication using Touch ID.
* Native macOS implementation via `BiometricSignaturePlugin.swift`.
* Support for macOS 10.15 (Catalina) and later.
* CocoaPods integration for seamless dependency management.

#### API and Configuration
* New `MacosConfig` class for platform-specific configuration:
- `useDeviceCredentials`: Enable device credentials (passcode) fallback
- `signatureType`: Support for both `MacosSignatureType.RSA` and `MacosSignatureType.ECDSA`
- `biometryCurrentSet`: Bind keys to current Touch ID enrollment state
* **New Parameter**: Added optional `promptMessage` parameter to `createKeys()` method across all platforms
- Allows customization of the authentication prompt when `enforceBiometric` is `true`
- Defaults to `"Authenticate to create keys"` for backward compatibility
- Provides context-specific instructions to users during key generation

#### Security Features
* **App-specific keychain isolation**: Keychain identifiers now incorporate bundle identifier to prevent cross-app conflicts on macOS
- Each app's keys are completely isolated: `{bundleId}.eckey`, `{bundleId}.biometric_key`, etc.
- Solves the issue where multiple apps using the plugin would share the same keychain items
- iOS implementation remains unchanged as it already has proper sandboxing
* Secure Enclave integration for EC key storage and operations
* Hardware-backed cryptographic operations using macOS Security framework
* Domain state tracking for biometric enrollment changes

#### Cryptographic Features
* **RSA Mode**:
- RSA-2048 hardware-backed signing
- Hybrid mode with software RSA decryption key wrapped via ECIES
* **EC Mode**:
- P-256 (secp256r1) hardware-backed signing in Secure Enclave
- Native ECIES decryption using `SecKeyAlgorithm.eciesEncryptionStandardX963SHA256AESGCM`
- Support for EC-only mode and hybrid EC mode

#### Implementation Details
* Biometry change detection via `LAContext.evaluatedPolicyDomainState`
* Automatic key invalidation when Touch ID enrollment changes (when `biometryCurrentSet` is `true`)
* Support for all key formats: BASE64, PEM, RAW, HEX
* Consistent error handling and Flutter method channel integration

### Changed
* Updated platform interface to distinguish macOS from iOS
* Enhanced `BiometricSignaturePlatform` to properly handle macOS-specific parameters
* Updated documentation with macOS integration steps and examples
* Added macOS to platform support table (macOS 10.15+)

## [8.4.0] - 2025-11-28
### Added
* **ECIES decryption** on Android and iOS.
Expand Down
44 changes: 39 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,12 @@ To get started with Biometric Signature, follow these steps:

```yaml
dependencies:
biometric_signature: ^8.4.0
biometric_signature: ^8.5.0
```

| | Android | iOS |
|-------------|---------|-------|
| **Support** | SDK 24+ | 13.0+ |
| | Android | iOS | macOS |
|-------------|---------|-------|----------|
| **Support** | SDK 24+ | 13.0+ | 10.15+ |

### iOS Integration

Expand Down Expand Up @@ -196,12 +196,41 @@ Update your project's `AndroidManifest.xml` file to include the
</manifest>
```

### macOS Integration

This plugin works with Touch ID on supported Macs. To use Touch ID, you need to:

1. Add the required entitlements to your macOS app.

Open your macOS project's entitlements file (typically located at `macos/Runner/DebugProfile.entitlements` and `macos/Runner/Release.entitlements`) and ensure it includes:

```xml
<key>com.apple.security.device.usb</key>
<false/>
<key>com.apple.security.device.bluetooth</key>
<false/>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)com.yourdomain.yourapp</string>
</array>
```

Replace `com.yourdomain.yourapp` with your actual bundle identifier.

2. Ensure CocoaPods is properly configured in your `macos/Podfile`. The plugin requires macOS 10.15 or later:

```ruby
platform :osx, '10.15'
```


2. Import the package in your Dart code:

```dart
import 'package:biometric_signature/biometric_signature.dart';
import 'package:biometric_signature/android_config.dart';
import 'package:biometric_signature/ios_config.dart';
import 'package:biometric_signature/macos_config.dart';
import 'package:biometric_signature/signature_options.dart';
import 'package:biometric_signature/decryption_options.dart';
```
Expand All @@ -225,7 +254,7 @@ When a user enrolls in biometrics, a key pair is generated. The private key is s

This class provides methods to manage and utilize biometric authentication for secure server interactions. It supports both Android and iOS platforms.

### `createKeys({ androidConfig, iosConfig, keyFormat, enforceBiometric })`
### `createKeys({ androidConfig, iosConfig, macosConfig, keyFormat, enforceBiometric, promptMessage })`

Generates a new key pair (RSA 2048 or EC) for biometric authentication. The private key is securely stored on the device, while the `KeyCreationResult` returned from this call contains a `FormattedValue` with the public key in the requested representation. StrongBox support is available for compatible Android devices and Secure Enclave support is available for iOS.
Hybrid modes generate both hardware and software keys, encrypting software keys via secure hardware.
Expand All @@ -240,8 +269,13 @@ Hybrid modes generate both hardware and software keys, encrypting software keys
- `useDeviceCredentials`: A `bool` to indicate whether Device Credentials' fallback support is needed.
- `signatureType`: An enum value of `IOSSignatureType`.
- `biometryCurrentSet` *(optional)*: A `bool` to constrain key usage to the current biometric enrollment. Defaults to `true`. When set to `true`, the key is bound to the current set of enrolled biometrics. If biometrics are changed (e.g., a new fingerprint is added or removed), the key becomes invalid, requiring re-enrollment.
- `macosConfig`: A `MacosConfig` object containing following properties:
- `useDeviceCredentials`: A `bool` to indicate whether Device Credentials' fallback support is needed.
- `signatureType`: An enum value of `MacosSignatureType`.
- `biometryCurrentSet` *(optional)*: A `bool` to constrain key usage to the current biometric enrollment. Defaults to `true`. When set to `true`, the key is bound to the current set of enrolled biometrics (Touch ID). If biometrics are changed, the key becomes invalid, requiring re-enrollment.
- `keyFormat` *(optional)*: A `KeyFormat` value describing how the public key should be returned. Defaults to `KeyFormat.base64` for backward compatibility.
- `enforceBiometric` *(optional)*: A `bool` to require biometric authentication before generating the key-pair. Defaults to `false`. When set to `true`, the user will be prompted for biometric authentication (fingerprint, face, or iris) before the key-pair is generated. This ensures that the person holding the device is verified before keys are created, adding an extra layer of security for sensitive use cases.
- `promptMessage` *(optional)*: A `String` to customize the authentication prompt message when `enforceBiometric` is `true`. Defaults to `"Authenticate to create keys"`. This allows you to provide context-specific instructions to the user during key generation.

- **Returns**: `Future<KeyCreationResult?>`. Access the formatted public key through `result.publicKey`, e.g.:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ class BiometricSignaturePlugin : FlutterPlugin, MethodCallHandler, ActivityAware
val invalidateOnEnrollment = args.boolean("setInvalidatedByBiometricEnrollment")
val enforceBiometric = args.boolean("enforceBiometric")
val keyFormat = KeyFormat.from(args["keyFormat"] as? String)
val promptMessage = args["promptMessage"] as? String ?: "Authenticate to create keys"

// Determine mode
val mode = when {
Expand All @@ -209,7 +210,8 @@ class BiometricSignaturePlugin : FlutterPlugin, MethodCallHandler, ActivityAware
invalidateOnEnrollment,
enableDecryption,
enforceBiometric,
keyFormat
keyFormat,
promptMessage
)

KeyMode.EC_SIGN_ONLY -> createEcSigningKeys(
Expand All @@ -218,7 +220,8 @@ class BiometricSignaturePlugin : FlutterPlugin, MethodCallHandler, ActivityAware
useDeviceCredentials,
invalidateOnEnrollment,
enforceBiometric,
keyFormat
keyFormat,
promptMessage
)

KeyMode.HYBRID_EC -> createHybridEcKeys(
Expand All @@ -227,7 +230,8 @@ class BiometricSignaturePlugin : FlutterPlugin, MethodCallHandler, ActivityAware
useDeviceCredentials,
invalidateOnEnrollment,
keyFormat,
enforceBiometric
enforceBiometric,
promptMessage
)
}
}
Expand All @@ -240,13 +244,14 @@ class BiometricSignaturePlugin : FlutterPlugin, MethodCallHandler, ActivityAware
invalidateOnEnrollment: Boolean,
enableDecryption: Boolean,
enforceBiometric: Boolean,
keyFormat: KeyFormat
keyFormat: KeyFormat,
promptMessage: String
) {
if (enforceBiometric) {
checkBiometricAvailability(activity, useDeviceCredentials)
authenticate(
activity,
"Authenticate to create keys",
promptMessage,
null,
"Cancel",
useDeviceCredentials,
Expand Down Expand Up @@ -300,13 +305,14 @@ class BiometricSignaturePlugin : FlutterPlugin, MethodCallHandler, ActivityAware
useDeviceCredentials: Boolean,
invalidateOnEnrollment: Boolean,
enforceBiometric: Boolean,
keyFormat: KeyFormat
keyFormat: KeyFormat,
promptMessage: String
) {
if (enforceBiometric) {
checkBiometricAvailability(activity, useDeviceCredentials)
authenticate(
activity,
"Authenticate to create keys",
promptMessage,
null,
"Cancel",
useDeviceCredentials,
Expand Down Expand Up @@ -352,15 +358,16 @@ class BiometricSignaturePlugin : FlutterPlugin, MethodCallHandler, ActivityAware
useDeviceCredentials: Boolean,
invalidateOnEnrollment: Boolean,
keyFormat: KeyFormat,
enforceBiometric: Boolean
enforceBiometric: Boolean,
promptMessage: String
) {
// Step 0: If requested, force biometric before key creation
if (enforceBiometric) {
checkBiometricAvailability(activity, useDeviceCredentials)
// Authenticate with a no-crypto prompt for enforcement only
authenticate(
activity,
"Authenticate to create keys",
promptMessage,
null,
"Cancel",
useDeviceCredentials,
Expand All @@ -385,7 +392,7 @@ class BiometricSignaturePlugin : FlutterPlugin, MethodCallHandler, ActivityAware
checkBiometricAvailability(activity, useDeviceCredentials)
val authResult = authenticate(
activity,
"Authenticate to create keys",
promptMessage,
null,
"Cancel",
useDeviceCredentials,
Expand Down
15 changes: 6 additions & 9 deletions banking_app/.metadata
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# This file should be version controlled and should not be manually edited.

version:
revision: "edada7c56edf4a183c1735310e123c7f923584f1"
revision: "adc901062556672b4138e18a4dc62a4be8f4b3c2"
channel: "stable"

project_type: app
Expand All @@ -13,14 +13,11 @@ project_type: app
migration:
platforms:
- platform: root
create_revision: edada7c56edf4a183c1735310e123c7f923584f1
base_revision: edada7c56edf4a183c1735310e123c7f923584f1
- platform: android
create_revision: edada7c56edf4a183c1735310e123c7f923584f1
base_revision: edada7c56edf4a183c1735310e123c7f923584f1
- platform: ios
create_revision: edada7c56edf4a183c1735310e123c7f923584f1
base_revision: edada7c56edf4a183c1735310e123c7f923584f1
create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
- platform: macos
create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2

# User provided section

Expand Down
4 changes: 2 additions & 2 deletions banking_app/ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
PODS:
- biometric_signature (8.4.0):
- biometric_signature (8.5.0):
- Flutter
- Flutter (1.0.0)
- shared_preferences_foundation (0.0.1):
Expand All @@ -20,7 +20,7 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"

SPEC CHECKSUMS:
biometric_signature: 5160c5cd418b13675e7aeb43a4bd1d507fe31e7c
biometric_signature: 8e36f1308ce31fd274982403728b2337913efba4
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7

Expand Down
4 changes: 2 additions & 2 deletions banking_app/lib/screens/transfer_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ class _TransferScreenState extends State<TransferScreen> {
),
const SizedBox(height: 8),
DropdownButtonFormField<Account>(
value: _fromAccount,
initialValue: _fromAccount,
decoration: const InputDecoration(
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.account_balance_wallet),
Expand Down Expand Up @@ -281,7 +281,7 @@ class _TransferScreenState extends State<TransferScreen> {
),
const SizedBox(height: 8),
DropdownButtonFormField<Account>(
value: _toAccount,
initialValue: _toAccount,
decoration: const InputDecoration(
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.account_balance),
Expand Down
7 changes: 7 additions & 0 deletions banking_app/macos/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Flutter-related
**/Flutter/ephemeral/
**/Pods/

# Xcode-related
**/dgph
**/xcuserdata/
2 changes: 2 additions & 0 deletions banking_app/macos/Flutter/Flutter-Debug.xcconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"
2 changes: 2 additions & 0 deletions banking_app/macos/Flutter/Flutter-Release.xcconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"
14 changes: 14 additions & 0 deletions banking_app/macos/Flutter/GeneratedPluginRegistrant.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// Generated file. Do not edit.
//

import FlutterMacOS
import Foundation

import biometric_signature
import shared_preferences_foundation

func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
BiometricSignaturePlugin.register(with: registry.registrar(forPlugin: "BiometricSignaturePlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
}
Loading