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

google / webcrypto.dart / 20204101061

14 Dec 2025 06:42AM UTC coverage: 90.347% (+0.6%) from 89.794%
20204101061

push

github

web-flow
chore: fix linter issues (#240)

1233 of 1310 new or added lines in 57 files covered. (94.12%)

10 existing lines in 9 files now uncovered.

4923 of 5449 relevant lines covered (90.35%)

2.5 hits per line

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

91.63
/lib/src/testing/utils/testrunner.dart
1
// Copyright 2020 Google LLC
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//      http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
import 'dart:convert';
16
import 'dart:math';
17
import 'dart:async';
18

19
import 'package:meta/meta.dart';
20
import 'package:webcrypto/webcrypto.dart';
21
import 'detected_runtime.dart';
22
import 'ffibonacci_chunked_stream.dart';
23
import 'utils.dart';
24
import 'lipsum.dart';
25
import 'err_stack_stub.dart' if (dart.library.ffi) 'err_stack_ffi.dart';
26

27
// Export utilities necessary for implementing a `TestRunner`.
28
export 'utils.dart' show hashFromJson, curveFromJson;
29

30
List<int>? _optionalBase64Decode(dynamic data) =>
2✔
31
    data == null ? null : base64.decode(data as String);
3✔
32

33
Map<String, dynamic>? _optionalStringMapDecode(dynamic data) =>
3✔
34
    data == null ? null : (data as Map).cast<String, dynamic>();
3✔
35

36
String? _optionalBase64Encode(List<int>? data) =>
2✔
37
    data == null ? null : base64.encode(data);
3✔
38

39
@sealed
40
class _TestCase {
41
  final String name;
42

43
  // Obtain a keyPair from import or key generation
44
  final Map<String, dynamic>? generateKeyParams;
45
  final List<int>? privateRawKeyData;
46
  final List<int>? privatePkcs8KeyData;
47
  final Map<String, dynamic>? privateJsonWebKeyData;
48
  final List<int>? publicRawKeyData;
49
  final List<int>? publicSpkiKeyData;
50
  final Map<String, dynamic>? publicJsonWebKeyData;
51

52
  // Plaintext to be signed, (always required)
53
  final List<int>? plaintext;
54
  // Signature to be verified (invalid, if generateKeyParams != null)
55
  final List<int>? signature;
56
  // Ciphertext of plaintext (invalid, if generateKeyParams != null)
57
  final List<int>? ciphertext;
58
  // Bits derived using deriveBits (invalid, if generateKeyParams != null)
59
  final List<int>? derivedBits;
60
  // Number of bits to derive.
61
  final int? derivedLength;
62

63
  // Parameters for key import (always required)
64
  final Map<String, dynamic>? importKeyParams;
65

66
  // Parameters for sign/verify (required, if there is a signature)
67
  final Map<String, dynamic>? signVerifyParams;
68

69
  // Parameters for encrypt/decrypt (required, if there is a ciphertext)
70
  final Map<String, dynamic>? encryptDecryptParams;
71

72
  // Parameters for deriveBits (required, if there is a derivedBits)
73
  final Map<String, dynamic>? deriveParams;
74

75
  _TestCase(
3✔
76
    this.name, {
77
    this.generateKeyParams,
78
    this.privateRawKeyData,
79
    this.privatePkcs8KeyData,
80
    this.privateJsonWebKeyData,
81
    this.publicRawKeyData,
82
    this.publicSpkiKeyData,
83
    this.publicJsonWebKeyData,
84
    this.plaintext,
85
    this.signature,
86
    this.ciphertext,
87
    this.derivedBits,
88
    this.derivedLength,
89
    this.importKeyParams,
90
    this.signVerifyParams,
91
    this.encryptDecryptParams,
92
    this.deriveParams,
93
  });
1✔
94

95
  factory _TestCase.fromJson(Map json) {
3✔
96
    return _TestCase(
3✔
97
      json['name'] as String,
3✔
98
      generateKeyParams: _optionalStringMapDecode(json['generateKeyParams']),
5✔
99
      privateRawKeyData: _optionalBase64Decode(json['privateRawKeyData']),
5✔
100
      privatePkcs8KeyData: _optionalBase64Decode(json['privatePkcs8KeyData']),
5✔
101
      privateJsonWebKeyData: _optionalStringMapDecode(
3✔
102
        json['privateJsonWebKeyData'],
3✔
103
      ),
104
      publicRawKeyData: _optionalBase64Decode(json['publicRawKeyData']),
5✔
105
      publicSpkiKeyData: _optionalBase64Decode(json['publicSpkiKeyData']),
5✔
106
      publicJsonWebKeyData: _optionalStringMapDecode(
3✔
107
        json['publicJsonWebKeyData'],
3✔
108
      ),
109
      plaintext: _optionalBase64Decode(json['plaintext']),
5✔
110
      signature: _optionalBase64Decode(json['signature']),
5✔
111
      ciphertext: _optionalBase64Decode(json['ciphertext']),
5✔
112
      derivedBits: _optionalBase64Decode(json['derivedBits']),
5✔
113
      derivedLength: json['derivedLength'] as int?,
3✔
114
      importKeyParams: _optionalStringMapDecode(json['importKeyParams']),
5✔
115
      signVerifyParams: _optionalStringMapDecode(json['signVerifyParams']),
5✔
116
      encryptDecryptParams: _optionalStringMapDecode(
3✔
117
        json['encryptDecryptParams'],
3✔
118
      ),
119
      deriveParams: _optionalStringMapDecode(json['deriveParams']),
5✔
120
    );
121
  }
1✔
122

123
  Map<String, dynamic> toJson() {
3✔
124
    return {
3✔
125
      'name': name,
3✔
126
      'generateKeyParams': generateKeyParams,
3✔
127
      'privateRawKeyData': _optionalBase64Encode(privateRawKeyData),
5✔
128
      'privatePkcs8KeyData': _optionalBase64Encode(privatePkcs8KeyData),
5✔
129
      'privateJsonWebKeyData': privateJsonWebKeyData,
3✔
130
      'publicRawKeyData': _optionalBase64Encode(publicRawKeyData),
5✔
131
      'publicSpkiKeyData': _optionalBase64Encode(publicSpkiKeyData),
5✔
132
      'publicJsonWebKeyData': publicJsonWebKeyData,
3✔
133
      'plaintext': _optionalBase64Encode(plaintext),
5✔
134
      'signature': _optionalBase64Encode(signature),
5✔
135
      'ciphertext': _optionalBase64Encode(ciphertext),
5✔
136
      'derivedBits': _optionalBase64Encode(derivedBits),
5✔
137
      'derivedLength': derivedLength,
3✔
138
      'importKeyParams': importKeyParams,
3✔
139
      'signVerifyParams': signVerifyParams,
3✔
140
      'encryptDecryptParams': encryptDecryptParams,
3✔
141
      'deriveParams': deriveParams,
3✔
142
    }..removeWhere((_, v) => v == null);
5✔
143
  }
1✔
144
}
145

146
/// Function for importing pkcs8, spki, or raw key.
147
typedef ImportKeyFn<T> =
148
    Future<T> Function(List<int> keyData, Map<String, dynamic> keyImportParams);
149

150
/// Function for exporting pkcs8, spki or raw key.
151
typedef ExportKeyFn<T> = Future<List<int>> Function(T key);
152

153
/// Function for importing JWK key.
154
typedef ImportJsonWebKeyKeyFn<T> =
155
    Future<T> Function(
156
      Map<String, dynamic> jsonWebKeyData,
157
      Map<String, dynamic> keyImportParams,
158
    );
159

160
/// Function for exporting JWK key.
161
typedef ExportJsonWebKeyKeyFn<T> = Future<Map<String, dynamic>> Function(T key);
162

163
/// Function for generating a key.
164
typedef GenerateKeyFn<T> =
165
    Future<T> Function(Map<String, dynamic> generateKeyPairParams);
166

167
/// Function for signing [data] using [key].
168
typedef SignBytesFn<T> =
169
    Future<List<int>> Function(
170
      T key,
171
      List<int> data,
172
      Map<String, dynamic> signParams,
173
    );
174

175
/// Function for signing [data] using [key].
176
typedef SignStreamFn<T> =
177
    Future<List<int>> Function(
178
      T key,
179
      Stream<List<int>> data,
180
      Map<String, dynamic> signParams,
181
    );
182

183
/// Function for verifying [data] using [key].
184
typedef VerifyBytesFn<T> =
185
    Future<bool> Function(
186
      T key,
187
      List<int> signature,
188
      List<int> data,
189
      Map<String, dynamic> verifyParams,
190
    );
191

192
/// Function for verifying [data] using [key].
193
typedef VerifyStreamFn<T> =
194
    Future<bool> Function(
195
      T key,
196
      List<int> signature,
197
      Stream<List<int>> data,
198
      Map<String, dynamic> verifyParams,
199
    );
200

201
/// Function for encrypting or a function for decrypting [data] using [key].
202
typedef EncryptOrDecryptBytesFn<T> =
203
    Future<List<int>> Function(
204
      T key,
205
      List<int> data,
206
      Map<String, dynamic> encryptOrDecryptParams,
207
    );
208

209
/// Function for encrypting or a function for decrypting [data] using [key].
210
typedef EncryptOrDecryptStreamFn<T> =
211
    Stream<List<int>> Function(
212
      T key,
213
      Stream<List<int>> data,
214
      Map<String, dynamic> encryptOrDecryptParams,
215
    );
216

217
typedef DeriveBitsFn<T> =
218
    Future<List<int>> Function(
219
      T keyOrKeyPair,
220
      int length,
221
      Map<String, dynamic> deriveParams,
222
    );
223

224
@sealed
225
class TestRunner<PrivateKey, PublicKey> {
226
  final String algorithm;
227

228
  /// True, if private is a secret key and there is no public key.
229
  final bool _isSymmetric;
230

231
  final ImportKeyFn<PrivateKey>? _importPrivateRawKey;
232
  final ExportKeyFn<PrivateKey>? _exportPrivateRawKey;
233
  final ImportKeyFn<PrivateKey>? _importPrivatePkcs8Key;
234
  final ExportKeyFn<PrivateKey>? _exportPrivatePkcs8Key;
235
  final ImportJsonWebKeyKeyFn<PrivateKey>? _importPrivateJsonWebKey;
236
  final ExportJsonWebKeyKeyFn<PrivateKey>? _exportPrivateJsonWebKey;
237

238
  final ImportKeyFn<PublicKey>? _importPublicRawKey;
239
  final ExportKeyFn<PublicKey>? _exportPublicRawKey;
240
  final ImportKeyFn<PublicKey>? _importPublicSpkiKey;
241
  final ExportKeyFn<PublicKey>? _exportPublicSpkiKey;
242
  final ImportJsonWebKeyKeyFn<PublicKey>? _importPublicJsonWebKey;
243
  final ExportJsonWebKeyKeyFn<PublicKey>? _exportPublicJsonWebKey;
244

245
  final GenerateKeyFn<KeyPair<PrivateKey, PublicKey>> _generateKeyPair;
246
  final SignBytesFn<PrivateKey>? _signBytes;
247
  final SignStreamFn<PrivateKey>? _signStream;
248
  final VerifyBytesFn<PublicKey>? _verifyBytes;
249
  final VerifyStreamFn<PublicKey>? _verifyStream;
250
  final EncryptOrDecryptBytesFn<PublicKey>? _encryptBytes;
251
  final EncryptOrDecryptStreamFn<PublicKey>? _encryptStream;
252
  final EncryptOrDecryptBytesFn<PrivateKey>? _decryptBytes;
253
  final EncryptOrDecryptStreamFn<PrivateKey>? _decryptStream;
254
  final DeriveBitsFn<KeyPair<PrivateKey, PublicKey>>? _deriveBits;
255

256
  final List<Map<dynamic, dynamic>> _testData;
257

258
  TestRunner._({
3✔
259
    required bool isSymmetric,
260
    required this.algorithm,
261
    ImportKeyFn<PrivateKey>? importPrivateRawKey,
262
    ExportKeyFn<PrivateKey>? exportPrivateRawKey,
263
    ImportKeyFn<PrivateKey>? importPrivatePkcs8Key,
264
    ExportKeyFn<PrivateKey>? exportPrivatePkcs8Key,
265
    ImportJsonWebKeyKeyFn<PrivateKey>? importPrivateJsonWebKey,
266
    ExportJsonWebKeyKeyFn<PrivateKey>? exportPrivateJsonWebKey,
267
    ImportKeyFn<PublicKey>? importPublicRawKey,
268
    ExportKeyFn<PublicKey>? exportPublicRawKey,
269
    ImportKeyFn<PublicKey>? importPublicSpkiKey,
270
    ExportKeyFn<PublicKey>? exportPublicSpkiKey,
271
    ImportJsonWebKeyKeyFn<PublicKey>? importPublicJsonWebKey,
272
    ExportJsonWebKeyKeyFn<PublicKey>? exportPublicJsonWebKey,
273
    required GenerateKeyFn<KeyPair<PrivateKey, PublicKey>> generateKeyPair,
274
    SignBytesFn<PrivateKey>? signBytes,
275
    SignStreamFn<PrivateKey>? signStream,
276
    VerifyBytesFn<PublicKey>? verifyBytes,
277
    VerifyStreamFn<PublicKey>? verifyStream,
278
    EncryptOrDecryptBytesFn<PublicKey>? encryptBytes,
279
    EncryptOrDecryptStreamFn<PublicKey>? encryptStream,
280
    EncryptOrDecryptBytesFn<PrivateKey>? decryptBytes,
281
    EncryptOrDecryptStreamFn<PrivateKey>? decryptStream,
282
    DeriveBitsFn<KeyPair<PrivateKey, PublicKey>>? deriveBits,
283
    Iterable<Map<dynamic, dynamic>>? testData,
284
  }) : _isSymmetric = isSymmetric,
285
       _importPrivateRawKey = importPrivateRawKey,
286
       _exportPrivateRawKey = exportPrivateRawKey,
287
       _importPrivatePkcs8Key = importPrivatePkcs8Key,
288
       _exportPrivatePkcs8Key = exportPrivatePkcs8Key,
289
       _importPrivateJsonWebKey = importPrivateJsonWebKey,
290
       _exportPrivateJsonWebKey = exportPrivateJsonWebKey,
291
       _importPublicRawKey = importPublicRawKey,
292
       _exportPublicRawKey = exportPublicRawKey,
293
       _importPublicSpkiKey = importPublicSpkiKey,
294
       _exportPublicSpkiKey = exportPublicSpkiKey,
295
       _importPublicJsonWebKey = importPublicJsonWebKey,
296
       _exportPublicJsonWebKey = exportPublicJsonWebKey,
297
       _generateKeyPair = generateKeyPair,
298
       _signBytes = signBytes,
299
       _signStream = signStream,
300
       _verifyBytes = verifyBytes,
301
       _verifyStream = verifyStream,
302
       _encryptBytes = encryptBytes,
303
       _encryptStream = encryptStream,
304
       _decryptBytes = decryptBytes,
305
       _decryptStream = decryptStream,
306
       _deriveBits = deriveBits,
307
       _testData = List.from(testData ?? <Map<dynamic, dynamic>>[]) {
3✔
308
    _validate();
3✔
309
  }
1✔
310

311
  /// Create [TestRunner] for an asymmetric primitive.
312
  static TestRunner<PrivateKey, PublicKey> asymmetric<PrivateKey, PublicKey>({
3✔
313
    required String algorithm,
314
    ImportKeyFn<PrivateKey>? importPrivateRawKey,
315
    ExportKeyFn<PrivateKey>? exportPrivateRawKey,
316
    ImportKeyFn<PrivateKey>? importPrivatePkcs8Key,
317
    ExportKeyFn<PrivateKey>? exportPrivatePkcs8Key,
318
    ImportJsonWebKeyKeyFn<PrivateKey>? importPrivateJsonWebKey,
319
    ExportJsonWebKeyKeyFn<PrivateKey>? exportPrivateJsonWebKey,
320
    ImportKeyFn<PublicKey>? importPublicRawKey,
321
    ExportKeyFn<PublicKey>? exportPublicRawKey,
322
    ImportKeyFn<PublicKey>? importPublicSpkiKey,
323
    ExportKeyFn<PublicKey>? exportPublicSpkiKey,
324
    ImportJsonWebKeyKeyFn<PublicKey>? importPublicJsonWebKey,
325
    ExportJsonWebKeyKeyFn<PublicKey>? exportPublicJsonWebKey,
326
    required GenerateKeyFn<KeyPair<PrivateKey, PublicKey>> generateKeyPair,
327
    SignBytesFn<PrivateKey>? signBytes,
328
    SignStreamFn<PrivateKey>? signStream,
329
    VerifyBytesFn<PublicKey>? verifyBytes,
330
    VerifyStreamFn<PublicKey>? verifyStream,
331
    EncryptOrDecryptBytesFn<PublicKey>? encryptBytes,
332
    EncryptOrDecryptStreamFn<PublicKey>? encryptStream,
333
    EncryptOrDecryptBytesFn<PrivateKey>? decryptBytes,
334
    EncryptOrDecryptStreamFn<PrivateKey>? decryptStream,
335
    DeriveBitsFn<KeyPair<PrivateKey, PublicKey>>? deriveBits,
336
    Iterable<Map<dynamic, dynamic>>? testData,
337
  }) {
338
    return TestRunner._(
3✔
339
      isSymmetric: false,
340
      algorithm: algorithm,
341
      importPrivateRawKey: importPrivateRawKey,
342
      exportPrivateRawKey: exportPrivateRawKey,
343
      importPrivatePkcs8Key: importPrivatePkcs8Key,
344
      exportPrivatePkcs8Key: exportPrivatePkcs8Key,
345
      importPrivateJsonWebKey: importPrivateJsonWebKey,
346
      exportPrivateJsonWebKey: exportPrivateJsonWebKey,
347
      importPublicRawKey: importPublicRawKey,
348
      exportPublicRawKey: exportPublicRawKey,
349
      importPublicSpkiKey: importPublicSpkiKey,
350
      exportPublicSpkiKey: exportPublicSpkiKey,
351
      importPublicJsonWebKey: importPublicJsonWebKey,
352
      exportPublicJsonWebKey: exportPublicJsonWebKey,
353
      generateKeyPair: generateKeyPair,
354
      signBytes: signBytes,
355
      signStream: signStream,
356
      verifyBytes: verifyBytes,
357
      verifyStream: verifyStream,
358
      encryptBytes: encryptBytes,
359
      encryptStream: encryptStream,
360
      decryptBytes: decryptBytes,
361
      decryptStream: decryptStream,
362
      deriveBits: deriveBits,
363
      testData: testData,
364
    );
365
  }
1✔
366

367
  /// Create [TestRunner] for an symmetric primitive.
368
  ///
369
  /// This just creates a [TestRunner] where public and private key have the
370
  /// same type. This may give rise to a few unnecessary test cases as
371
  /// import/export of public and private key
372
  static TestRunner<PrivateKey, PrivateKey> symmetric<PrivateKey>({
3✔
373
    required String algorithm,
374
    ImportKeyFn<PrivateKey>? importPrivateRawKey,
375
    ExportKeyFn<PrivateKey>? exportPrivateRawKey,
376
    ImportKeyFn<PrivateKey>? importPrivatePkcs8Key,
377
    ExportKeyFn<PrivateKey>? exportPrivatePkcs8Key,
378
    ImportJsonWebKeyKeyFn<PrivateKey>? importPrivateJsonWebKey,
379
    ExportJsonWebKeyKeyFn<PrivateKey>? exportPrivateJsonWebKey,
380
    required GenerateKeyFn<PrivateKey> generateKey,
381
    SignBytesFn<PrivateKey>? signBytes,
382
    SignStreamFn<PrivateKey>? signStream,
383
    VerifyBytesFn<PrivateKey>? verifyBytes,
384
    VerifyStreamFn<PrivateKey>? verifyStream,
385
    EncryptOrDecryptBytesFn<PrivateKey>? encryptBytes,
386
    EncryptOrDecryptStreamFn<PrivateKey>? encryptStream,
387
    EncryptOrDecryptBytesFn<PrivateKey>? decryptBytes,
388
    EncryptOrDecryptStreamFn<PrivateKey>? decryptStream,
389
    DeriveBitsFn<PrivateKey>? deriveBits,
390
    Iterable<Map<dynamic, dynamic>>? testData,
391
  }) {
392
    return TestRunner._(
3✔
393
      isSymmetric: true,
394
      algorithm: algorithm,
395
      importPrivateRawKey: importPrivateRawKey,
396
      exportPrivateRawKey: exportPrivateRawKey,
397
      importPrivatePkcs8Key: importPrivatePkcs8Key,
398
      exportPrivatePkcs8Key: exportPrivatePkcs8Key,
399
      importPrivateJsonWebKey: importPrivateJsonWebKey,
400
      exportPrivateJsonWebKey: exportPrivateJsonWebKey,
401
      generateKeyPair: (params) async {
1✔
402
        final k = await generateKey(params);
1✔
403
        return (privateKey: k, publicKey: k);
404
      },
405
      signBytes: signBytes,
406
      signStream: signStream,
407
      verifyBytes: verifyBytes,
408
      verifyStream: verifyStream,
409
      encryptBytes: encryptBytes,
410
      encryptStream: encryptStream,
411
      decryptBytes: decryptBytes,
412
      decryptStream: decryptStream,
413
      deriveBits: deriveBits == null
414
          ? null
415
          : (
3✔
416
              KeyPair<PrivateKey, PrivateKey> pair,
417
              int length,
418
              Map<String, dynamic> deriveParams,
419
            ) async {
420
              final a = await deriveBits(pair.privateKey, length, deriveParams);
3✔
421
              final b = await deriveBits(pair.publicKey, length, deriveParams);
3✔
422
              check(equalBytes(a, b), 'expected both keys to derive the same');
4✔
423
              return a;
1✔
424
            },
1✔
425
      testData: testData,
426
    );
427
  }
1✔
428

429
  void _validate() {
3✔
430
    // Check that we have verify if we have sign
431
    check((_signBytes != null) == (_verifyBytes != null));
9✔
432
    check((_signStream != null) == (_verifyStream != null));
9✔
433
    // If we can sign streams, we should also be able to sign bytes
434
    if (_signStream != null) {
3✔
435
      check(_signBytes != null);
4✔
436
    }
437

438
    // Check that we have decrypt if we have encrypt
439
    check((_encryptBytes != null) == (_decryptBytes != null));
9✔
440
    check((_encryptStream != null) == (_decryptStream != null));
9✔
441
    // If we can encrypt streams, we should also be able to encrypt bytes
442
    if (_encryptStream != null) {
3✔
443
      check(_encryptBytes != null);
4✔
444
    }
445

446
    // Must have one priate key import format.
447
    check(
2✔
448
      _importPrivateRawKey != null ||
3✔
449
          _importPrivatePkcs8Key != null ||
3✔
NEW
450
          _importPrivateJsonWebKey != null,
×
451
    );
452

453
    if (_isSymmetric) {
3✔
454
      // if symmetric we have no methods for importing public keys
455
      check(_importPublicRawKey == null);
5✔
456
      check(_importPublicSpkiKey == null);
5✔
457
      check(_importPublicJsonWebKey == null);
5✔
458
    } else {
459
      // Must have one public key import format.
460
      check(
2✔
461
        _importPublicRawKey != null ||
3✔
462
            _importPublicSpkiKey != null ||
3✔
NEW
463
            _importPublicJsonWebKey != null,
×
464
      );
465
    }
466

467
    // Export-only and import-only formats do not make sense
468
    check((_importPrivateRawKey != null) == (_exportPrivateRawKey != null));
9✔
469
    check((_importPrivatePkcs8Key != null) == (_exportPrivatePkcs8Key != null));
9✔
470
    check(
2✔
471
      (_importPrivateJsonWebKey != null) == (_exportPrivateJsonWebKey != null),
7✔
472
    );
473
    check((_importPublicRawKey != null) == (_exportPublicRawKey != null));
9✔
474
    check((_importPublicSpkiKey != null) == (_exportPublicSpkiKey != null));
9✔
475
    check(
2✔
476
      (_importPublicJsonWebKey != null) == (_exportPublicJsonWebKey != null),
7✔
477
    );
478

479
    // Check all test cases
480
    for (final data in _testData) {
5✔
481
      final c = _TestCase.fromJson(data);
3✔
482
      try {
483
        _validateTestCase(this, c);
3✔
484
      } catch (e) {
485
        log('Invalid test case: $c');
×
486
        rethrow;
487
      }
488
    }
489
  }
1✔
490

491
  Future<Map<String, dynamic>> generate({
×
492
    required Map<String, dynamic> generateKeyParams,
493
    required Map<String, dynamic> importKeyParams,
494
    Map<String, dynamic> signVerifyParams = const {},
495
    Map<String, dynamic> encryptDecryptParams = const {},
496
    Map<String, dynamic> deriveParams = const {},
497
    String plaintextTemplate = libsum,
498
    int minPlaintext = 8,
499
    int maxPlaintext = libsum.length,
500
    int minDeriveLength = 4,
501
    int maxDeriveLength = 512,
502
  }) async {
503
    check(minPlaintext <= maxPlaintext);
×
504
    check(maxPlaintext <= plaintextTemplate.length);
×
505
    final ts = DateTime.now().toIso8601String().split('.').first; // drop secs
×
506
    final name = 'generated at $ts';
×
507

508
    log('generating key-pair');
×
509
    final pair = await _generateKeyPair(generateKeyParams);
×
510
    final privateKey = pair.privateKey;
511
    final publicKey = pair.publicKey;
512
    check(privateKey != null);
×
513
    check(publicKey != null);
×
514

515
    List<int>? plaintext;
516
    if (_signBytes != null || _encryptBytes != null) {
×
517
      log('picking plaintext');
×
518
      final rng = Random.secure();
×
519
      final N = rng.nextInt(maxPlaintext - minPlaintext) + minPlaintext;
×
520
      final offset = rng.nextInt(plaintextTemplate.length - N);
×
NEW
521
      plaintext = utf8.encode(plaintextTemplate.substring(offset, offset + N));
×
522
    }
523

524
    List<int>? signature;
525
    final signBytes = _signBytes;
×
526
    if (signBytes != null) {
527
      log('creating signature');
×
528
      signature = await signBytes(
×
529
        pair.privateKey,
530
        plaintext!,
531
        signVerifyParams,
532
      );
533
    }
534

535
    List<int>? ciphertext;
536
    final encryptBytes = _encryptBytes;
×
537
    if (encryptBytes != null) {
538
      log('creating ciphertext');
×
539
      ciphertext = await encryptBytes(
×
540
        pair.publicKey,
541
        plaintext!,
542
        encryptDecryptParams,
543
      );
544
    }
545

546
    int? derivedLength;
547
    List<int>? derivedBits;
548
    final deriveBits = _deriveBits;
×
549
    if (deriveBits != null) {
550
      log('picking derivedLength');
×
551
      final rng = Random.secure();
×
552
      derivedLength = maxDeriveLength == minDeriveLength
×
553
          ? maxDeriveLength
554
          : rng.nextInt(maxDeriveLength - minDeriveLength) + minDeriveLength;
×
555

556
      log('creating derivedBits');
×
NEW
557
      derivedBits = await deriveBits(pair, derivedLength, deriveParams);
×
558
    }
559

560
    Future<T?> optionalCall<S, T>(Future<T> Function(S)? fn, S v) async =>
×
561
        fn != null ? await fn(v) : null;
×
562
    final c = _TestCase(
×
563
      name,
564
      generateKeyParams: null, // omit generateKeyParams
565
      privateRawKeyData: await optionalCall(_exportPrivateRawKey, privateKey),
×
NEW
566
      privatePkcs8KeyData: await optionalCall(
×
NEW
567
        _exportPrivatePkcs8Key,
×
568
        privateKey,
569
      ),
NEW
570
      privateJsonWebKeyData: await optionalCall(
×
NEW
571
        _exportPrivateJsonWebKey,
×
572
        privateKey,
573
      ),
574
      publicRawKeyData: await optionalCall(_exportPublicRawKey, publicKey),
×
575
      publicSpkiKeyData: await optionalCall(_exportPublicSpkiKey, publicKey),
×
NEW
576
      publicJsonWebKeyData: await optionalCall(
×
NEW
577
        _exportPublicJsonWebKey,
×
578
        publicKey,
579
      ),
580
      plaintext: plaintext,
581
      signature: signature,
582
      ciphertext: ciphertext,
583
      derivedBits: derivedBits,
584
      importKeyParams: importKeyParams,
585
      signVerifyParams: signVerifyParams,
586
      encryptDecryptParams: encryptDecryptParams,
587
      derivedLength: derivedLength,
588
      deriveParams: deriveParams,
589
    );
590

591
    // Log the generated test case. This makes it easy to copy/paste the test
592
    // case into test files.
593
    final json = const JsonEncoder.withIndent(
594
      '  ',
NEW
595
    ).convert(c.toJson()).replaceAll('\n', '\n| ');
×
UNCOV
596
    log('| $json');
×
597

598
    return c.toJson();
×
599
  }
600

601
  /// Get test cases from [testData].
602
  ///
603
  /// If no [testData] is given the `testData` given when the [TestRunner] was
604
  /// created will be used.
605
  ///
606
  /// Returns a list of tuples with test name and test function.
607
  List<({String name, Future<void> Function() test})> tests({
3✔
608
    Iterable<Map<dynamic, dynamic>>? testData,
609
  }) {
610
    final tests = <({String name, Future<void> Function() test})>[];
3✔
611
    testData ??= _testData;
3✔
612
    for (final data in testData) {
5✔
613
      final c = _TestCase.fromJson(data);
3✔
614

615
      _runTests(this, c, (String name, FutureOr<void> Function() fn) {
5✔
616
        tests.add((
3✔
617
          // Prefix test names
618
          name: '$algorithm: ${c.name} -- $name',
7✔
619
          test: () async {
3✔
620
            // Check BoringSSL error stack if running with dart:ffi
621
            await checkErrorStack(fn);
3✔
622
          },
1✔
623
        ));
624
      });
1✔
625
    }
626
    return tests;
1✔
627
  }
1✔
628
}
629

630
/// Wraps a [test] function such that calls must always be ordered, and that
631
/// any subsequent tests fails, if a previous test has failed.
632
void Function(String name, FutureOr Function() fn) _withTestDependency(
3✔
633
  void Function(String name, FutureOr Function() fn) test,
634
) {
635
  var count = 0;
1✔
636
  var next = 0;
1✔
637
  return (String name, FutureOr Function() fn) {
3✔
638
    final order = count++;
3✔
639
    test(name, () async {
5✔
640
      if (next != order) {
3✔
641
        check(false, 'Test dependency failed.');
×
642
      }
643
      await fn();
3✔
644
      next++;
2✔
645
    });
1✔
646
  };
1✔
647
}
1✔
648

649
/// Validate that the test case [c] is compatible with TestRunner [r].
650
void _validateTestCase<PrivateKey, PublicKey>(
3✔
651
  TestRunner<PrivateKey, PublicKey> r,
652
  _TestCase c,
653
) {
654
  final hasPrivateKey =
655
      c.privateRawKeyData != null ||
3✔
656
      c.privatePkcs8KeyData != null ||
3✔
657
      c.privateJsonWebKeyData != null;
3✔
658
  final hasPublicKey =
659
      c.publicRawKeyData != null ||
3✔
660
      c.publicSpkiKeyData != null ||
3✔
661
      c.publicJsonWebKeyData != null;
3✔
662

663
  // Test that we have keys to import or generate some.
664
  if (r._isSymmetric) {
3✔
665
    check(!hasPublicKey);
2✔
666
    check(
2✔
667
      c.generateKeyParams != null || hasPrivateKey,
3✔
668
      'A key must be generated or imported',
669
    );
670
  } else {
671
    check(
2✔
672
      c.generateKeyParams != null || (hasPrivateKey && hasPublicKey),
3✔
673
      'A key-pair must be generated or imported',
674
    );
675
  }
676

677
  check(
2✔
678
    c.generateKeyParams == null ||
3✔
679
        (c.signature == null && c.ciphertext == null && c.derivedBits == null),
7✔
680
    'Cannot verify signature/ciphertext/derivedBits for a generated key-pair',
681
  );
682
  check(
2✔
683
    c.plaintext != null || (r._signBytes == null && r._encryptBytes == null),
7✔
684
    'Cannot sign/encrypt without plaintext',
685
  );
686
  check(c.importKeyParams != null);
5✔
687
  check((c.signVerifyParams != null) == (r._signBytes != null));
9✔
688
  check((c.encryptDecryptParams != null) == (r._encryptBytes != null));
9✔
689
  check((c.deriveParams != null) == (r._deriveBits != null));
9✔
690
  if (c.signature != null) {
3✔
691
    check(r._signBytes != null);
4✔
692
  }
693
  if (c.ciphertext != null) {
3✔
694
    check(r._encryptBytes != null);
4✔
695
  }
696
  if (c.derivedBits != null) {
3✔
697
    check(r._deriveBits != null);
4✔
698
  }
699
  if (r._deriveBits != null) {
3✔
700
    check(c.derivedLength != null);
5✔
701
  }
702

703
  // Check that data matches the methods we have in the runner.
704
  check(r._importPrivateRawKey != null || c.privateRawKeyData == null);
7✔
705
  check(r._importPrivatePkcs8Key != null || c.privatePkcs8KeyData == null);
7✔
706
  check(r._importPrivateJsonWebKey != null || c.privateJsonWebKeyData == null);
7✔
707
  check(r._importPublicRawKey != null || c.publicRawKeyData == null);
7✔
708
  check(r._importPublicSpkiKey != null || c.publicSpkiKeyData == null);
7✔
709
  check(r._importPublicJsonWebKey != null || c.publicJsonWebKeyData == null);
7✔
710
}
1✔
711

712
void _runTests<PrivateKey, PublicKey>(
3✔
713
  TestRunner<PrivateKey, PublicKey> r,
714
  _TestCase c,
715
  void Function(String name, FutureOr Function() fn) test,
716
) {
717
  test = _withTestDependency(test);
3✔
718

719
  test('validate test case', () => _validateTestCase(r, c));
7✔
720

721
  try {
722
    _validateTestCase(r, c);
3✔
723
  } catch (_) {
724
    // Don't register additional tests if the test-case is invalid!
725
    return;
726
  }
727

728
  //------------------------------ Import or generate key-pair for testing
729

730
  // Store publicKey and privateKey for use in later tests.
731
  // If [_isSymmetric] is true, we still import the public and assign the
732
  // private key to the public key.
733
  PublicKey? publicKey;
734
  PrivateKey? privateKey;
735

736
  if (c.generateKeyParams != null) {
3✔
737
    test('generateKeyPair()', () async {
5✔
738
      final pair = await r._generateKeyPair(c.generateKeyParams!);
7✔
739
      check(pair.privateKey != null);
3✔
740
      check(pair.publicKey != null);
3✔
741
      publicKey = pair.publicKey;
1✔
742
      privateKey = pair.privateKey;
1✔
743
    });
1✔
744
  } else {
745
    test('import key-pair', () async {
5✔
746
      // Get a privateKey
747
      if (c.privateRawKeyData != null) {
3✔
748
        privateKey = await r._importPrivateRawKey!(
5✔
749
          c.privateRawKeyData!,
2✔
750
          c.importKeyParams!,
3✔
751
        );
752
        check(privateKey != null);
2✔
753
      } else if (c.privatePkcs8KeyData != null) {
3✔
754
        privateKey = await r._importPrivatePkcs8Key!(
5✔
755
          c.privatePkcs8KeyData!,
2✔
756
          c.importKeyParams!,
3✔
757
        );
758
        check(privateKey != null);
2✔
759
      } else if (c.privateJsonWebKeyData != null) {
3✔
760
        privateKey = await r._importPrivateJsonWebKey!(
5✔
761
          c.privateJsonWebKeyData!,
2✔
762
          c.importKeyParams!,
3✔
763
        );
764
        check(privateKey != null);
2✔
765
      } else {
766
        check(false, 'missing private key for importing');
×
767
      }
768

769
      // Get a publicKey
770
      if (r._isSymmetric) {
3✔
771
        // If symmetric algorithm we just use the private key.
772
        publicKey = privateKey as PublicKey;
1✔
773
      } else if (c.publicRawKeyData != null) {
3✔
774
        publicKey = await r._importPublicRawKey!(
5✔
775
          c.publicRawKeyData!,
2✔
776
          c.importKeyParams!,
3✔
777
        );
778
        check(publicKey != null);
2✔
779
      } else if (c.publicSpkiKeyData != null) {
3✔
780
        publicKey = await r._importPublicSpkiKey!(
5✔
781
          c.publicSpkiKeyData!,
2✔
782
          c.importKeyParams!,
3✔
783
        );
784
        check(publicKey != null);
2✔
785
      } else if (c.publicJsonWebKeyData != null) {
×
786
        publicKey = await r._importPublicJsonWebKey!(
×
787
          c.publicJsonWebKeyData!,
×
788
          c.importKeyParams!,
×
789
        );
790
        check(publicKey != null);
×
791
      } else {
792
        check(false, 'missing public key for importing');
×
793
      }
794
    });
1✔
795
  }
796

797
  //------------------------------ Create a signature for testing
798

799
  // Ensure that we have a signature for use in later test cases
800
  List<int>? signature;
801

802
  if (r._signBytes != null) {
3✔
803
    if (c.signature != null) {
3✔
804
      signature = c.signature;
3✔
805
    } else {
806
      test('create signature', () async {
5✔
807
        signature = await r._signBytes(
5✔
808
          privateKey as PrivateKey,
1✔
809
          c.plaintext!,
3✔
810
          c.signVerifyParams!,
3✔
811
        );
812
      });
1✔
813
    }
814

815
    test('verify signature', () async {
5✔
816
      check(
3✔
817
        await r._verifyBytes!(
5✔
818
          publicKey as PublicKey,
1✔
819
          signature!,
1✔
820
          c.plaintext!,
3✔
821
          c.signVerifyParams!,
3✔
822
        ),
823
        'failed to verify signature',
824
      );
825
    });
1✔
826
  }
827

828
  //------------------------------ Create a ciphertext for testing
829
  List<int>? ciphertext;
830

831
  if (r._encryptBytes != null) {
3✔
832
    if (c.ciphertext != null) {
3✔
833
      ciphertext = c.ciphertext!;
3✔
834
    } else {
835
      test('create ciphertext', () async {
5✔
836
        ciphertext = await r._encryptBytes(
5✔
837
          publicKey as PublicKey,
1✔
838
          c.plaintext!,
3✔
839
          c.encryptDecryptParams!,
3✔
840
        );
841
      });
1✔
842
    }
843

844
    test('decrypt ciphertext', () async {
5✔
845
      final text = await r._decryptBytes!(
5✔
846
        privateKey as PrivateKey,
1✔
847
        ciphertext!,
1✔
848
        c.encryptDecryptParams!,
3✔
849
      );
850
      check(equalBytes(text, c.plaintext!), 'failed to decrypt ciphertext');
7✔
851
    });
1✔
852
  }
853

854
  //------------------------------ Create derivedBits for testing
855

856
  // Ensure that we have derivedBits for use in later test cases
857
  List<int>? derivedBits;
858

859
  if (r._deriveBits != null) {
3✔
860
    if (c.derivedBits != null) {
3✔
861
      derivedBits = c.derivedBits!;
3✔
862
    } else {
863
      test('create derivedBits', () async {
×
864
        derivedBits = await r._deriveBits(
×
865
          (
866
            privateKey: privateKey as PrivateKey,
867
            publicKey: publicKey as PublicKey,
868
          ),
869
          c.derivedLength!,
×
870
          c.deriveParams!,
×
871
        );
872
      });
873
    }
874

875
    test('validated derivedBits', () async {
5✔
876
      final derived = await r._deriveBits(
5✔
877
        (
878
          privateKey: privateKey as PrivateKey,
1✔
879
          publicKey: publicKey as PublicKey,
1✔
880
        ),
881
        c.derivedLength!,
3✔
882
        c.deriveParams!,
3✔
883
      );
884
      check(
2✔
885
        equalBytes(derived, derivedBits!),
3✔
886
        'failed to derivedBits are not consistent',
887
      );
888
    });
1✔
889
  }
890

891
  //------------------------------ Utilities for testing
892

893
  //// Utility function to verify [sig] using [key].
894
  Future<void> checkVerifyBytes(PublicKey key, List<int>? sig) async {
3✔
895
    check(sig != null, 'signature cannot be null');
3✔
896
    check(
2✔
897
      await r._verifyBytes!(key, sig!, c.plaintext!, c.signVerifyParams!),
9✔
898
      'failed to verify signature',
899
    );
900
    check(
2✔
901
      !await r._verifyBytes(
5✔
902
        key,
903
        flipFirstBits(sig),
3✔
904
        c.plaintext!,
2✔
905
        c.signVerifyParams!,
2✔
906
      ),
907
      'verified an invalid signature',
908
    );
909
    if (c.plaintext!.isNotEmpty) {
5✔
910
      check(
2✔
911
        !await r._verifyBytes(
5✔
912
          key,
913
          sig,
914
          flipFirstBits(c.plaintext!),
5✔
915
          c.signVerifyParams!,
2✔
916
        ),
917
        'verified an invalid message',
918
      );
919
    }
920
  }
1✔
921

922
  /// Utility function to decrypt [ctext] using [key].
923
  Future<void> checkDecryptBytes(PrivateKey key, List<int> ctext) async {
3✔
924
    final text = await r._decryptBytes!(key, ctext, c.encryptDecryptParams!);
7✔
925
    check(equalBytes(text, c.plaintext!), 'failed to decrypt ciphertext');
7✔
926

927
    if (ctext.isNotEmpty) {
3✔
928
      // If ciphertext is mangled some primitives like AES-GCM must throw
929
      // others may return garbled plaintext.
930
      try {
1✔
931
        final invalidText = await r._decryptBytes(
5✔
932
          key,
933
          flipFirstBits(ctext),
3✔
934
          c.encryptDecryptParams!,
2✔
935
        );
936
        check(
2✔
937
          !equalBytes(invalidText, c.plaintext!),
4✔
938
          'decrypted an invalid ciphertext',
939
        );
940
      } on OperationError catch (e) {
2✔
941
        check(e.toString() != '', 'expected some explanation');
6✔
942
      }
943
    }
944
  }
1✔
945

946
  /// Check if [signature] is sane.
947
  Future<void> checkSignature(List<int>? signature) async {
3✔
948
    check(signature != null, 'signature is null');
3✔
949
    check(signature!.isNotEmpty, 'signature is empty');
5✔
950
    await checkVerifyBytes(publicKey as PublicKey, signature);
3✔
951
  }
1✔
952

953
  /// Check if [ciphertext] is sane.
954
  Future<void> checkCipherText(List<int>? ctext) async {
3✔
955
    check(ctext != null, 'ciphtertext is null');
3✔
956
    check(ctext!.isNotEmpty, 'ciphtertext is empty');
5✔
957
    await checkDecryptBytes(privateKey as PrivateKey, ctext);
3✔
958
  }
1✔
959

960
  /// Check if [derived] is correct.
961
  void checkDerivedBits(List<int>? derived) async {
3✔
962
    check(derived != null, 'derivedBits is null');
3✔
963
    check(derived!.isNotEmpty, 'derivedBits is empty');
5✔
964
    check(equalBytes(derived, derivedBits!), 'derivedBits is not consistent');
5✔
965
  }
1✔
966

967
  /// Check if [publicKey] is sane.
968
  Future<void> checkPublicKey(PublicKey? publicKey) async {
3✔
969
    check(publicKey != null, 'publicKey is null');
3✔
970
    if (r._signBytes != null) {
3✔
971
      await checkVerifyBytes(publicKey as PublicKey, signature);
3✔
972
    }
973
    if (r._encryptBytes != null) {
3✔
974
      final ctext = await r._encryptBytes(
5✔
975
        publicKey as PublicKey,
976
        c.plaintext!,
3✔
977
        c.encryptDecryptParams!,
3✔
978
      );
979
      await checkCipherText(ctext);
3✔
980
    }
981
    if (r._deriveBits != null) {
3✔
982
      final derived = await r._deriveBits(
5✔
983
        (
984
          privateKey: privateKey as PrivateKey,
1✔
985
          publicKey: publicKey as PublicKey,
986
        ),
987
        c.derivedLength!,
3✔
988
        c.deriveParams!,
3✔
989
      );
990
      checkDerivedBits(derived);
3✔
991
    }
992
  }
1✔
993

994
  /// Check if [privateKey] is sane.
995
  Future<void> checkPrivateKey(PrivateKey? privateKey) async {
3✔
996
    check(privateKey != null, 'privateKey is null');
3✔
997
    if (r._signBytes != null) {
3✔
998
      final sig = await r._signBytes(
5✔
999
        privateKey as PrivateKey,
1000
        c.plaintext!,
3✔
1001
        c.signVerifyParams!,
3✔
1002
      );
1003
      await checkSignature(sig);
3✔
1004
    }
1005
    if (r._encryptBytes != null) {
3✔
1006
      await checkDecryptBytes(privateKey as PrivateKey, ciphertext!);
3✔
1007
    }
1008
    if (r._deriveBits != null) {
3✔
1009
      final derived = await r._deriveBits(
5✔
1010
        (
1011
          privateKey: privateKey as PrivateKey,
1012
          publicKey: publicKey as PublicKey,
1✔
1013
        ),
1014
        c.derivedLength!,
3✔
1015
        c.deriveParams!,
3✔
1016
      );
1017
      checkDerivedBits(derived);
3✔
1018
    }
1019
  }
1✔
1020

1021
  //------------------------------ Test import public key
1022

1023
  if (c.publicRawKeyData != null) {
3✔
1024
    assert(!r._isSymmetric && r._importPublicRawKey != null);
7✔
1025

1026
    test('importPublicRawKey()', () async {
5✔
1027
      final key = await r._importPublicRawKey!(
5✔
1028
        c.publicRawKeyData!,
3✔
1029
        c.importKeyParams!,
3✔
1030
      );
1031
      await checkPublicKey(key);
3✔
1032
    });
1✔
1033
  }
1034

1035
  if (c.publicSpkiKeyData != null) {
3✔
1036
    assert(!r._isSymmetric && r._importPublicSpkiKey != null);
7✔
1037

1038
    test('importPublicSpkiKey()', () async {
5✔
1039
      final key = await r._importPublicSpkiKey!(
5✔
1040
        c.publicSpkiKeyData!,
3✔
1041
        c.importKeyParams!,
3✔
1042
      );
1043
      await checkPublicKey(key);
3✔
1044
    });
1✔
1045
  }
1046

1047
  if (c.publicJsonWebKeyData != null) {
3✔
1048
    assert(!r._isSymmetric && r._importPublicJsonWebKey != null);
7✔
1049

1050
    test('importPublicJsonWebKey()', () async {
5✔
1051
      final key = await r._importPublicJsonWebKey!(
5✔
1052
        c.publicJsonWebKeyData!,
3✔
1053
        c.importKeyParams!,
3✔
1054
      );
1055
      await checkPublicKey(key);
3✔
1056
    });
1✔
1057
  }
1058

1059
  //------------------------------ Test import private key
1060

1061
  if (c.privateRawKeyData != null) {
3✔
1062
    test('importPrivateRawKey()', () async {
5✔
1063
      final key = await r._importPrivateRawKey!(
5✔
1064
        c.privateRawKeyData!,
3✔
1065
        c.importKeyParams!,
3✔
1066
      );
1067
      await checkPrivateKey(key);
3✔
1068
    });
1✔
1069
  }
1070

1071
  if (c.privatePkcs8KeyData != null) {
3✔
1072
    test('importPrivatePkcs8Key()', () async {
5✔
1073
      final key = await r._importPrivatePkcs8Key!(
5✔
1074
        c.privatePkcs8KeyData!,
3✔
1075
        c.importKeyParams!,
3✔
1076
      );
1077
      await checkPrivateKey(key);
3✔
1078
    });
1✔
1079
  }
1080

1081
  if (c.privateJsonWebKeyData != null) {
3✔
1082
    test('importPrivateJsonWebKey()', () async {
5✔
1083
      final key = await r._importPrivateJsonWebKey!(
5✔
1084
        c.privateJsonWebKeyData!,
3✔
1085
        c.importKeyParams!,
3✔
1086
      );
1087
      await checkPrivateKey(key);
3✔
1088
    });
1✔
1089
  }
1090

1091
  //------------------------------ Test signing
1092

1093
  if (r._signBytes != null) {
3✔
1094
    test('signBytes(plaintext)', () async {
5✔
1095
      final sig = await r._signBytes(
5✔
1096
        privateKey as PrivateKey,
1✔
1097
        c.plaintext!,
3✔
1098
        c.signVerifyParams!,
3✔
1099
      );
1100
      await checkSignature(sig);
3✔
1101
    });
1✔
1102
  }
1103

1104
  if (r._signStream != null) {
3✔
1105
    test('signStream(plaintext)', () async {
5✔
1106
      final sig = await r._signStream(
5✔
1107
        privateKey as PrivateKey,
1✔
1108
        Stream.value(c.plaintext!),
5✔
1109
        c.signVerifyParams!,
3✔
1110
      );
1111
      await checkSignature(sig);
3✔
1112
    });
1✔
1113

1114
    test('signStream(fibChunked(plaintext))', () async {
5✔
1115
      final sig = await r._signStream(
5✔
1116
        privateKey as PrivateKey,
1✔
1117
        fibonacciChunkedStream(c.plaintext!),
5✔
1118
        c.signVerifyParams!,
3✔
1119
      );
1120
      await checkSignature(sig);
3✔
1121
    });
1✔
1122
  }
1123

1124
  //------------------------------ Test verification
1125

1126
  if (r._verifyBytes != null) {
3✔
1127
    test('verifyBytes(signature, plaintext)', () async {
5✔
1128
      check(
3✔
1129
        await r._verifyBytes(
5✔
1130
          publicKey as PublicKey,
1✔
1131
          signature!,
1✔
1132
          c.plaintext!,
3✔
1133
          c.signVerifyParams!,
3✔
1134
        ),
1135
        'failed to verify signature',
1136
      );
1137

1138
      check(
2✔
1139
        !await r._verifyBytes(
5✔
1140
          publicKey as PublicKey,
1✔
1141
          flipFirstBits(signature!),
3✔
1142
          c.plaintext!,
2✔
1143
          c.signVerifyParams!,
2✔
1144
        ),
1145
        'verified an invalid signature',
1146
      );
1147

1148
      if (c.plaintext!.isNotEmpty) {
5✔
1149
        check(
2✔
1150
          !await r._verifyBytes(
5✔
1151
            publicKey as PublicKey,
1✔
1152
            signature!,
1✔
1153
            flipFirstBits(c.plaintext!),
5✔
1154
            c.signVerifyParams!,
2✔
1155
          ),
1156
          'verified an invalid message',
1157
        );
1158
      }
1159
    });
1✔
1160
  }
1161

1162
  if (r._verifyStream != null) {
3✔
1163
    test('verifyStream(signature, Stream.value(plaintext))', () async {
5✔
1164
      check(
3✔
1165
        await r._verifyStream(
5✔
1166
          publicKey as PublicKey,
1✔
1167
          signature!,
1✔
1168
          Stream.value(c.plaintext!),
5✔
1169
          c.signVerifyParams!,
3✔
1170
        ),
1171
        'failed to verify signature',
1172
      );
1173

1174
      check(
2✔
1175
        !await r._verifyStream(
5✔
1176
          publicKey as PublicKey,
1✔
1177
          flipFirstBits(signature!),
3✔
1178
          Stream.value(c.plaintext!),
5✔
1179
          c.signVerifyParams!,
2✔
1180
        ),
1181
        'verified an invalid signature',
1182
      );
1183

1184
      if (c.plaintext!.isNotEmpty) {
5✔
1185
        check(
2✔
1186
          !await r._verifyStream(
5✔
1187
            publicKey as PublicKey,
1✔
1188
            signature!,
1✔
1189
            Stream.value(flipFirstBits(c.plaintext!)),
7✔
1190
            c.signVerifyParams!,
2✔
1191
          ),
1192
          'verified an invalid message',
1193
        );
1194
      }
1195
    });
1✔
1196

1197
    test('verifyStream(signature, fibChunkedStream(plaintext))', () async {
5✔
1198
      check(
3✔
1199
        await r._verifyStream(
5✔
1200
          publicKey as PublicKey,
1✔
1201
          signature!,
1✔
1202
          fibonacciChunkedStream(c.plaintext!),
5✔
1203
          c.signVerifyParams!,
3✔
1204
        ),
1205
        'failed to verify signature',
1206
      );
1207

1208
      check(
2✔
1209
        !await r._verifyStream(
5✔
1210
          publicKey as PublicKey,
1✔
1211
          flipFirstBits(signature!),
3✔
1212
          fibonacciChunkedStream(c.plaintext!),
5✔
1213
          c.signVerifyParams!,
2✔
1214
        ),
1215
        'verified an invalid signature',
1216
      );
1217

1218
      if (c.plaintext!.isNotEmpty) {
5✔
1219
        check(
2✔
1220
          !await r._verifyStream(
5✔
1221
            publicKey as PublicKey,
1✔
1222
            signature!,
1✔
1223
            fibonacciChunkedStream(flipFirstBits(c.plaintext!)),
7✔
1224
            c.signVerifyParams!,
2✔
1225
          ),
1226
          'verified an invalid message',
1227
        );
1228
      }
1229
    });
1✔
1230
  }
1231

1232
  //------------------------------ Test encryption
1233

1234
  if (r._encryptBytes != null) {
3✔
1235
    test('encryptBytes(plaintext)', () async {
5✔
1236
      final ctext = await r._encryptBytes(
5✔
1237
        publicKey as PublicKey,
1✔
1238
        c.plaintext!,
3✔
1239
        c.encryptDecryptParams!,
3✔
1240
      );
1241
      await checkCipherText(ctext);
3✔
1242
    });
1✔
1243
  }
1244

1245
  if (r._encryptStream != null) {
3✔
1246
    test('encryptStream(plaintext)', () async {
5✔
1247
      final ctext = await bufferStream(
3✔
1248
        r._encryptStream(
5✔
1249
          publicKey as PublicKey,
1✔
1250
          Stream.value(c.plaintext!),
5✔
1251
          c.encryptDecryptParams!,
3✔
1252
        ),
1253
      );
1254
      await checkCipherText(ctext);
3✔
1255
    });
1✔
1256

1257
    test('encryptStream(fibChunked(plaintext))', () async {
5✔
1258
      final ctext = await bufferStream(
3✔
1259
        r._encryptStream(
5✔
1260
          publicKey as PublicKey,
1✔
1261
          fibonacciChunkedStream(c.plaintext!),
5✔
1262
          c.encryptDecryptParams!,
3✔
1263
        ),
1264
      );
1265
      await checkCipherText(ctext);
3✔
1266
    });
1✔
1267
  }
1268

1269
  //------------------------------ Test decryption
1270

1271
  if (r._decryptBytes != null) {
3✔
1272
    test('decryptBytes(plaintext)', () async {
5✔
1273
      final text = await r._decryptBytes(
5✔
1274
        privateKey as PrivateKey,
1✔
1275
        ciphertext!,
1✔
1276
        c.encryptDecryptParams!,
3✔
1277
      );
1278
      check(equalBytes(text, c.plaintext!), 'failed to decrypt signature');
7✔
1279

1280
      if (ciphertext!.isNotEmpty) {
3✔
1281
        // If ciphertext is mangled some primitives like AES-GCM must throw
1282
        // others may return garbled plaintext.
1283
        try {
1✔
1284
          final text2 = await r._decryptBytes(
5✔
1285
            privateKey as PrivateKey,
1✔
1286
            flipFirstBits(ciphertext!),
3✔
1287
            c.encryptDecryptParams!,
2✔
1288
          );
1289
          check(
2✔
1290
            !equalBytes(text2, c.plaintext!),
4✔
1291
            'decrypted an invalid ciphertext correctly',
1292
          );
1293
        } on OperationError catch (e) {
2✔
1294
          check(e.toString() != '', 'expected some explanation');
6✔
1295
        }
1296
      }
1297
    });
1✔
1298
  }
1299

1300
  if (r._decryptStream != null) {
3✔
1301
    test('decryptStream(Stream.value(ciphertext))', () async {
5✔
1302
      final text = await bufferStream(
3✔
1303
        r._decryptStream(
5✔
1304
          privateKey as PrivateKey,
1✔
1305
          Stream.value(ciphertext!),
3✔
1306
          c.encryptDecryptParams!,
3✔
1307
        ),
1308
      );
1309
      check(equalBytes(text, c.plaintext!), 'failed to decrypt signature');
7✔
1310

1311
      if (ciphertext!.isNotEmpty) {
3✔
1312
        // If ciphertext is mangled some primitives like AES-GCM must throw
1313
        // others may return garbled plaintext.
1314
        try {
1✔
1315
          final text2 = await bufferStream(
3✔
1316
            r._decryptStream(
5✔
1317
              privateKey as PrivateKey,
1✔
1318
              Stream.value(flipFirstBits(ciphertext!)),
5✔
1319
              c.encryptDecryptParams!,
2✔
1320
            ),
1321
          );
1322
          check(
2✔
1323
            !equalBytes(text2, c.plaintext!),
4✔
1324
            'decrypted an invalid ciphertext correctly',
1325
          );
1326
        } on OperationError catch (e) {
2✔
1327
          check(e.toString() != '', 'expected some explanation');
6✔
1328
        }
1329
      }
1330
    });
1✔
1331

1332
    test('decryptStream(fibChunkedStream(ciphertext))', () async {
5✔
1333
      final text = await bufferStream(
3✔
1334
        r._decryptStream(
5✔
1335
          privateKey as PrivateKey,
1✔
1336
          fibonacciChunkedStream(ciphertext!),
3✔
1337
          c.encryptDecryptParams!,
3✔
1338
        ),
1339
      );
1340
      check(equalBytes(text, c.plaintext!), 'failed to decrypt signature');
7✔
1341

1342
      if (ciphertext!.isNotEmpty) {
3✔
1343
        // If ciphertext is mangled some primitives like AES-GCM must throw
1344
        // others may return garbled plaintext.
1345
        try {
1✔
1346
          final text2 = await bufferStream(
3✔
1347
            r._decryptStream(
5✔
1348
              privateKey as PrivateKey,
1✔
1349
              fibonacciChunkedStream(flipFirstBits(ciphertext!)),
5✔
1350
              c.encryptDecryptParams!,
2✔
1351
            ),
1352
          );
1353
          check(
2✔
1354
            !equalBytes(text2, c.plaintext!),
4✔
1355
            'decrypted an invalid ciphertext correctly',
1356
          );
1357
        } on OperationError catch (e) {
2✔
1358
          check(e.toString() != '', 'expected some explanation');
6✔
1359
        }
1360
      }
1361
    });
1✔
1362
  }
1363

1364
  //------------------------------ Test derivedBits
1365
  if (r._deriveBits != null) {
3✔
1366
    test('deriveBits', () async {
5✔
1367
      final derived = await r._deriveBits(
5✔
1368
        (
1369
          privateKey: privateKey as PrivateKey,
1✔
1370
          publicKey: publicKey as PublicKey,
1✔
1371
        ),
1372
        c.derivedLength!,
3✔
1373
        c.deriveParams!,
3✔
1374
      );
1375
      checkDerivedBits(derived);
3✔
1376
    });
1✔
1377
  }
1378

1379
  //------------------------------ export/import private key
1380
  if (r._exportPrivateRawKey != null) {
3✔
1381
    test('export/import raw private key', () async {
5✔
1382
      final keyData = await r._exportPrivateRawKey(privateKey as PrivateKey);
5✔
1383
      check(keyData.isNotEmpty, 'exported key is empty');
5✔
1384

1385
      final key = await r._importPrivateRawKey!(keyData, c.importKeyParams!);
7✔
1386
      await checkPrivateKey(key);
3✔
1387
    });
1✔
1388
  }
1389

1390
  if (r._exportPrivatePkcs8Key != null) {
3✔
1391
    test('export/import pkcs8 private key', () async {
5✔
1392
      final keyData = await r._exportPrivatePkcs8Key(privateKey as PrivateKey);
5✔
1393
      check(keyData.isNotEmpty, 'exported key is empty');
5✔
1394

1395
      final key = await r._importPrivatePkcs8Key!(keyData, c.importKeyParams!);
7✔
1396
      await checkPrivateKey(key);
3✔
1397
    });
1✔
1398
  }
1399

1400
  if (r._exportPrivateJsonWebKey != null) {
3✔
1401
    test('export/import jwk private key', () async {
5✔
1402
      final jwk = await r._exportPrivateJsonWebKey(privateKey as PrivateKey);
5✔
1403
      check(jwk.isNotEmpty, 'exported key is empty');
5✔
1404

1405
      final key = await r._importPrivateJsonWebKey!(jwk, c.importKeyParams!);
7✔
1406
      await checkPrivateKey(key);
3✔
1407
    });
1✔
1408
  }
1409

1410
  //------------------------------ export/import public key
1411

1412
  if (r._exportPublicRawKey != null) {
3✔
1413
    assert(!r._isSymmetric && r._importPublicRawKey != null);
7✔
1414

1415
    test('export/import raw public key', () async {
5✔
1416
      final keyData = await r._exportPublicRawKey(publicKey as PublicKey);
5✔
1417
      check(keyData.isNotEmpty, 'exported key is empty');
5✔
1418

1419
      final key = await r._importPublicRawKey!(keyData, c.importKeyParams!);
7✔
1420
      await checkPublicKey(key);
3✔
1421
    });
1✔
1422
  }
1423

1424
  if (r._exportPublicSpkiKey != null) {
3✔
1425
    assert(!r._isSymmetric && r._importPublicSpkiKey != null);
7✔
1426

1427
    test('export/import pkcs8 public key', () async {
5✔
1428
      final keyData = await r._exportPublicSpkiKey(publicKey as PublicKey);
5✔
1429
      check(keyData.isNotEmpty, 'exported key is empty');
5✔
1430

1431
      final key = await r._importPublicSpkiKey!(keyData, c.importKeyParams!);
7✔
1432
      await checkPublicKey(key);
3✔
1433
    });
1✔
1434
  }
1435

1436
  if (r._exportPublicJsonWebKey != null) {
3✔
1437
    assert(!r._isSymmetric && r._importPublicJsonWebKey != null);
7✔
1438

1439
    test('export/import jwk public key', () async {
5✔
1440
      final jwk = await r._exportPublicJsonWebKey(publicKey as PublicKey);
5✔
1441
      check(jwk.isNotEmpty, 'exported key is empty');
5✔
1442

1443
      final key = await r._importPublicJsonWebKey!(jwk, c.importKeyParams!);
7✔
1444
      await checkPublicKey(key);
3✔
1445
    });
1✔
1446
  }
1447

1448
  //------------------------------ validate the generated test case
1449

1450
  if (c.generateKeyParams != null) {
3✔
1451
    test('validate generated test case', () async {
5✔
1452
      Future<T?> optionalCall<S, T>(Future<T> Function(S)? fn, S v) async =>
3✔
1453
          fn != null ? await fn(v) : null;
3✔
1454
      final date = DateTime.now()
2✔
1455
          .toIso8601String()
3✔
1456
          .split('T')
3✔
1457
          .first; // drop time
3✔
1458
      final generated = _TestCase(
3✔
1459
        '${c.name} generated on $detectedRuntime at $date',
7✔
1460
        generateKeyParams: null, // omit generateKeyParams
1461
        privateRawKeyData: await optionalCall(
3✔
1462
          r._exportPrivateRawKey,
3✔
1463
          privateKey as PrivateKey,
1✔
1464
        ),
1465
        privatePkcs8KeyData: await optionalCall(
3✔
1466
          r._exportPrivatePkcs8Key,
3✔
1467
          privateKey as PrivateKey,
1✔
1468
        ),
1469
        privateJsonWebKeyData: await optionalCall(
3✔
1470
          r._exportPrivateJsonWebKey,
3✔
1471
          privateKey as PrivateKey,
1✔
1472
        ),
1473
        publicRawKeyData: await optionalCall(
3✔
1474
          r._exportPublicRawKey,
3✔
1475
          publicKey as PublicKey,
1✔
1476
        ),
1477
        publicSpkiKeyData: await optionalCall(
3✔
1478
          r._exportPublicSpkiKey,
3✔
1479
          publicKey as PublicKey,
1✔
1480
        ),
1481
        publicJsonWebKeyData: await optionalCall(
3✔
1482
          r._exportPublicJsonWebKey,
3✔
1483
          publicKey as PublicKey,
1✔
1484
        ),
1485
        plaintext: c.plaintext,
3✔
1486
        signature: signature,
1✔
1487
        ciphertext: ciphertext,
1✔
1488
        derivedBits: derivedBits,
1✔
1489
        importKeyParams: c.importKeyParams,
3✔
1490
        signVerifyParams: c.signVerifyParams,
3✔
1491
        encryptDecryptParams: c.encryptDecryptParams,
3✔
1492
        derivedLength: c.derivedLength,
3✔
1493
        deriveParams: c.deriveParams,
3✔
1494
      );
1495

1496
      // Validate that the generated test case is sane.
1497
      _validateTestCase(r, generated);
3✔
1498

1499
      // Log the generated test case. This makes it easy to copy/paste the test
1500
      // case into test files.
1501
      dump(generated.toJson());
5✔
1502
    });
1✔
1503
  }
1504
}
1✔
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