• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vaariance / web3-signers / #6

12 Jan 2026 06:00PM UTC coverage: 94.628% (+58.2%) from 36.402%
#6

push

code-z2
refactor: replace Uint8List with Bytes type and add test coverage

- Replace Uint8List usage with Bytes type across codebase for consistency
- Add comprehensive tests for platform key generation and signing
- Update documentation examples to use Bytes type
- Add coverage badge to README
- Clean up test imports and remove unused code

6 of 6 new or added lines in 4 files covered. (100.0%)

1 existing line in 1 file now uncovered.

775 of 819 relevant lines covered (94.63%)

2.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.87
/lib/src/api/platform_authenticator.dart
1
import "dart:io";
2

3
import "package:flutter/foundation.dart";
4
import "package:web3_signers/web3_signers.dart";
5

6
import "android_auth.g.dart" as android_auth;
7
import "darwin_auth.g.dart" as darwin_auth;
8
import "windows_auth.g.dart" as windows_auth;
9

10
export 'darwin_auth.g.dart' show DarwinAccessible;
11

12
/// Android-specific configuration options for platform authentication.
13
///
14
/// Wraps [android_auth.AndroidOptions] to provide easy access to Android-specific settings
15
/// such as attestation challenges, biometric prompts, and security levels.
16
class AndroidPlatformOptions extends android_auth.AndroidOptions {
17
  /// Creates a new instance of [AndroidPlatformOptions].
18
  ///
19
  /// - [attestationChallenge]: Random data used to verify the integrity of the key pair.
20
  /// - [useStrongBoxKeyMint]: Whether to prefer StrongBox KeyMint if available. Defaults to `true`.
21
  /// - [authTimeoutSeconds]: Duration in seconds for which authentication remains valid.
22
  /// - [requireUserAuthentication]: Whether user authentication is required to use the key. Defaults to `true`.
23
  /// - [invalidateOnBiometricChange]: Whether to invalidate the key if new biometrics are enrolled. Defaults to `true`.
24
  /// - [allowFallbackAuthentication]: Whether to allow fallback to device credentials (PIN/pattern/password).
25
  /// - [userConfirmationRequired]: Whether user confirmation is required (e.g. pressing a button).
26
  /// - [biometricPromptTitle]: Title for the biometric prompt dialog.
27
  /// - [biometricPromptSubtitle]: Subtitle for the biometric prompt dialog.
28
  /// - [biometricPromptDescription]: Description for the biometric prompt dialog.
29
  /// - [biometricPromptNegativeButtonText]: specific text for the negative button on the prompt.
30
  ///
31
  /// Example:
32
  /// ```dart
33
  /// final options = AndroidPlatformOptions(
34
  ///   useStrongBoxKeyMint: true,
35
  ///   requireUserAuthentication: true,
36
  ///   biometricPromptTitle: 'Authorize Transaction',
37
  /// );
3✔
38
  /// ```
39
  AndroidPlatformOptions({
40
    super.attestationChallenge,
41
    super.useStrongBoxKeyMint = true,
42
    super.authTimeoutSeconds = 0,
43
    super.requireUserAuthentication = true,
44
    super.invalidateOnBiometricChange = true,
45
    super.allowFallbackAuthentication = false,
46
    super.userConfirmationRequired = false,
47
    super.biometricPromptTitle = "Sign",
48
    super.biometricPromptSubtitle = "Sign Ethereum Data",
49
    super.biometricPromptDescription = "Authenticate to Sign Ethereum Data",
50
    super.biometricPromptNegativeButtonText = "Cancel",
51
  });
52
}
53

54
/// Darwin (iOS/macOS) specific configuration options for platform authentication.
55
///
56
/// Wraps [darwin_auth.DarwinOptions] to provide access settings like Secure Enclave usage,
57
/// authentication policies, and keychain access groups.
58
class DarwinPlatformOptions extends darwin_auth.DarwinOptions {
59
  /// Creates a new instance of [DarwinPlatformOptions].
60
  ///
61
  /// - [accessGroup]: The keychain access group to share items between apps.
62
  /// - [useSecureEnclave]: Whether to store the key in the Secure Enclave. Defaults to `true`.
63
  /// - [requireUserAuthentication]: Whether user authentication is required to access the key. Defaults to `true`.
64
  /// - [invalidateOnBiometricChange]: Whether adding a new biometric enrollment should invalidate the key. Defaults to `true`.
65
  /// - [allowFallbackAuthentication]: Whether to allow fallback to device passcode.
66
  /// - [isPermanent]: Whether the key should persist across app installs. Defaults to `true`.
67
  /// - [accessible]: When the key should be accessible (e.g., [DarwinAccessible.whenUnlocked]).
68
  ///
69
  /// Example:
70
  /// ```dart
71
  /// final options = DarwinPlatformOptions(
72
  ///   useSecureEnclave: true,
73
  ///   accessible: DarwinAccessible.whenUnlocked,
74
  /// );
3✔
75
  /// ```
76
  DarwinPlatformOptions({
77
    super.accessGroup,
78
    super.useSecureEnclave = true,
79
    super.requireUserAuthentication = true,
80
    super.invalidateOnBiometricChange = true,
81
    super.allowFallbackAuthentication = false,
82
    super.isPermanent = true,
83
    super.accessible = DarwinAccessible.whenUnlocked,
84
  });
85
}
86

87
/// Windows-specific configuration options for platform authentication.
88
///
89
/// Wraps [windows_auth.WindowsOptions] to provide settings for TPM usage and UI prompts.
90
class WindowsPlatformOptions extends windows_auth.WindowsOptions {
91
  /// Creates a new instance of [WindowsPlatformOptions].
92
  ///
93
  /// - [attestationChallenge]: Challenge data for key attestation.
94
  /// - [useTpm]: Whether to require TPM storage for the key. Defaults to `true`.
95
  /// - [requireUserAuthentication]: Whether user authentication (Hello) is required.
96
  /// - [uiPolicyFriendlyName]: Friendly name displayed in the Windows UI.
97
  /// - [uiPolicyDescription]: Description displayed in the Windows UI.
98
  ///
99
  /// Example:
100
  /// ```dart
101
  /// final options = WindowsPlatformOptions(
102
  ///   useTpm: true,
103
  ///   requireUserAuthentication: true,
104
  ///   uiPolicyFriendlyName: 'My App Key',
105
  /// );
3✔
106
  /// ```
107
  WindowsPlatformOptions({
108
    super.attestationChallenge,
109
    super.useTpm = true,
110
    super.requireUserAuthentication = true,
111
    super.uiPolicyFriendlyName = "Ethereum Signing Key",
112
    super.uiPolicyDescription = "Authorizes access to sign Ethereum data.",
113
  });
114
}
115

116
/// A record type encapsulating platform-specific options.
117
///
118
/// Use this to pass platform-specific configurations when performing operations
119
/// that might require them, such as creating keys or signing.
120
typedef PlatformOptions =
121
    ({
122
      AndroidPlatformOptions? android,
123
      DarwinPlatformOptions? darwin,
124
      WindowsPlatformOptions? windows,
125
    });
126

127
/// A cross-platform interface for hardware-backed authentication and signing.
128
///
129
/// This class provides a unified API to interact with platform-specific secure storage
130
/// and signing capabilities:
131
/// - **Android**: Uses Android Keystore System (and StrongBox if available).
132
/// - **iOS/macOS**: Uses Apple's Secure Enclave and Keychain Services.
133
/// - **Windows**: Uses Windows CNG (Cryptography Next Generation) and TPM.
134
class PlatformAuthenticator {
135
  @visibleForTesting
136
  late darwin_auth.PlatformAuthenticator darwinAuth =
137
      darwin_auth.PlatformAuthenticator();
138

139
  @visibleForTesting
140
  late android_auth.PlatformAuthenticator androidAuth =
141
      android_auth.PlatformAuthenticator();
142

143
  @visibleForTesting
144
  late windows_auth.PlatformAuthenticator windowsAuth =
145
      windows_auth.PlatformAuthenticator();
1✔
146

147
  PlatformAuthenticator();
148

149
  /// Creates a new hardware-backed key pair identified by [keyTag].
150
  ///
151
  /// Returns the public key as [Bytes].
152
  ///
153
  /// - [keyTag]: A unique identifier for the key.
154
  /// - [options]: Platform-specific options for key creation.
155
  ///
156
  /// Throws [UnsupportedError] if the current platform is not supported.
157
  /// Throws [ArgumentError] if required platform options are missing.
158
  ///
159
  /// Example:
160
  /// ```dart
161
  /// final authenticator = PlatformAuthenticator();
162
  /// final options = (
163
  ///   android: AndroidPlatformOptions(useStrongBoxKeyMint: true),
164
  ///   darwin: DarwinPlatformOptions(useSecureEnclave: true),
165
  ///   windows: WindowsPlatformOptions(useTpm: true),
166
  /// );
167
  /// final publicKey = await authenticator.createKey('my_secure_key', options);
1✔
168
  /// ```
1✔
169
  Future<Bytes> createKey(String keyTag, PlatformOptions options) async {
3✔
170
    return switch (Platform.operatingSystem) {
171
      "windows" => windowsAuth.createKey(
1✔
172
        keyTag,
173
        _require(options.windows, "windows"),
3✔
174
      ),
175
      "android" => androidAuth.createKey(
1✔
176
        keyTag,
177
        _require(options.android, "android"),
4✔
178
      ),
179
      "ios" || "macos" => darwinAuth.createKey(
1✔
180
        keyTag,
181
        _require(options.darwin, "darwin"),
1✔
182
      ),
183
      _ => throw UnsupportedError("Unsupported platform"),
184
    };
185
  }
186

187
  /// Deletes the key pair identified by [keyTag].
188
  ///
189
  /// - [keyTag]: The unique identifier of the key to delete.
190
  ///
191
  /// Throws [UnsupportedError] if the current platform is not supported.
192
  ///
193
  /// Example:
194
  /// ```dart
195
  /// await authenticator.deleteKey('my_secure_key');
1✔
196
  /// ```
1✔
197
  Future<void> deleteKey(String keyTag) async {
3✔
198
    return switch (Platform.operatingSystem) {
3✔
199
      "windows" => windowsAuth.deleteKey(keyTag),
1✔
200
      "android" => androidAuth.deleteKey(keyTag),
3✔
201
      "ios" || "macos" => darwinAuth.deleteKey(keyTag),
1✔
202
      _ => throw UnsupportedError("Unsupported platform"),
203
    };
204
  }
205

206
  /// Retrieves the public key associated with [keyTag].
207
  ///
208
  /// Returns `null` if no key is found for the given [keyTag].
209
  ///
210
  /// - [keyTag]: The unique identifier of the key to retrieve.
211
  ///
212
  /// Throws [UnsupportedError] if the current platform is not supported.
213
  ///
214
  /// Example:
215
  /// ```dart
216
  /// final publicKey = await authenticator.getPublicKey('my_secure_key');
217
  /// if (publicKey != null) {
218
  ///   print('Public Key found: $publicKey');
219
  /// }
220
  /// ```
1✔
221
  Future<Bytes?> getPublicKey(String keyTag) async {
1✔
222
    return switch (Platform.operatingSystem) {
1✔
223
      "windows" => windowsAuth.getPublicKey(keyTag),
1✔
224
      "android" => androidAuth.getPublicKey(keyTag),
1✔
225
      "ios" || "macos" => darwinAuth.getPublicKey(keyTag),
3✔
226
      _ => throw UnsupportedError("Unsupported platform"),
1✔
227
    };
228
  }
229

230
  /// Signs [data] using the private key identified by [keyTag].
231
  ///
232
  /// Returns the signature as [Bytes].
233
  ///
234
  /// - [keyTag]: The unique identifier of the key to use for signing.
235
  /// - [data]: The data to sign.
236
  /// - [options]: Platform-specific options (e.g. authentication prompts).
237
  ///
238
  /// Throws [UnsupportedError] if the current platform is not supported.
239
  /// Throws [ArgumentError] if required platform options are missing.
240
  ///
241
  /// Example:
242
  /// ```dart
243
  /// final dataToSign = Bytes.fromList([1, 2, 3, 4]);
244
  /// final signature = await authenticator.sign(
245
  ///   'my_secure_key',
246
  ///   dataToSign,
247
  ///   options, // Re-use options or create new ones
248
  /// );
249
  /// ```
250
  Future<Bytes> sign(String keyTag, Bytes data, PlatformOptions options) async {
1✔
251
    return switch (Platform.operatingSystem) {
1✔
252
      "windows" => windowsAuth.sign(
1✔
253
        keyTag,
254
        data,
255
        _require(options.windows, "windows"),
×
256
      ),
257
      "android" => androidAuth.sign(
3✔
258
        keyTag,
259
        data,
260
        _require(options.android, "android"),
1✔
261
      ),
262
      "ios" || "macos" => darwinAuth.sign(keyTag, data),
1✔
263
      _ => throw UnsupportedError("Unsupported platform"),
2✔
UNCOV
264
    };
×
265
  }
266

267
  T _require<T>(T? option, String platform) {
268
    if (option == null) {
1✔
269
      throw ArgumentError(
270
        "Platform Authenticator: No options provided for $platform",
1✔
271
      );
1✔
272
    }
273
    return option;
274
  }
275
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc