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

vaariance / web3-signers / #8

14 Jan 2026 10:17PM UTC coverage: 94.715% (+33.5%) from 61.244%
#8

push

code-z2
feat: Add `Uint`, `Bytes`, and `AbiCoder` type tests, enhance Passkey signer with signature parsing, and update platform authenticator.

26 of 26 new or added lines in 3 files covered. (100.0%)

4 existing lines in 3 files now uncovered.

932 of 984 relevant lines covered (94.72%)

2.54 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 "package:flutter/foundation.dart";
2
import "package:web3_signers/web3_signers.dart";
3

4
import "android_auth.g.dart" as android_auth;
5
import "darwin_auth.g.dart" as darwin_auth;
6
import "windows_auth.g.dart" as windows_auth;
7

8
export 'darwin_auth.g.dart' show DarwinAccessible;
9

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

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

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

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

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

137
  @visibleForTesting
138
  late android_auth.PlatformAuthenticator androidAuth =
139
      android_auth.PlatformAuthenticator();
140

141
  @visibleForTesting
142
  late windows_auth.PlatformAuthenticator windowsAuth =
143
      windows_auth.PlatformAuthenticator();
144

145
  PlatformAuthenticator();
1✔
146

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

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

205
  /// Retrieves the public key associated with [keyTag].
206
  ///
207
  /// Returns `null` if no key is found for the given [keyTag].
208
  ///
209
  /// - [keyTag]: The unique identifier of the key to retrieve.
210
  ///
211
  /// Throws [UnsupportedError] if the current platform is not supported.
212
  ///
213
  /// Example:
214
  /// ```dart
215
  /// final publicKey = await authenticator.getPublicKey('my_secure_key');
216
  /// if (publicKey != null) {
217
  ///   print('Public Key found: $publicKey');
218
  /// }
219
  /// ```
220
  Future<Bytes?> getPublicKey(String keyTag) async {
1✔
221
    return switch (defaultTargetPlatform) {
1✔
222
      TargetPlatform.windows => windowsAuth.getPublicKey(keyTag),
1✔
223
      TargetPlatform.android => androidAuth.getPublicKey(keyTag),
1✔
224
      TargetPlatform.iOS ||
1✔
225
      TargetPlatform.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 (defaultTargetPlatform) {
1✔
252
      TargetPlatform.windows => windowsAuth.sign(
1✔
253
        keyTag,
254
        data,
255
        _require(options.windows, "windows"),
×
256
      ),
257
      TargetPlatform.android => androidAuth.sign(
3✔
258
        keyTag,
259
        data,
260
        _require(options.android, "android"),
1✔
261
      ),
262
      TargetPlatform.iOS ||
1✔
263
      TargetPlatform.macOS => darwinAuth.sign(keyTag, data),
2✔
UNCOV
264
      _ => throw UnsupportedError("Unsupported platform"),
×
265
    };
266
  }
267

268
  T _require<T>(T? option, String platform) {
1✔
269
    if (option == null) {
270
      throw ArgumentError(
1✔
271
        "Platform Authenticator: No options provided for $platform",
1✔
272
      );
273
    }
274
    return option;
275
  }
276
}
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