From e5c87ad597e21226180f2f7d0391f3c1104af87d Mon Sep 17 00:00:00 2001 From: Jonas Finnemann Jensen Date: Tue, 8 Dec 2020 19:47:21 +0100 Subject: [PATCH] Migrated to null-safety --- CHANGELOG.md | 3 + example/pubspec.lock | 4 +- lib/src/boringssl/bytestring.dart | 16 +- lib/src/boringssl/lookup/utils.dart | 7 +- lib/src/impl_ffi/impl_ffi.aes_common.dart | 8 +- lib/src/impl_ffi/impl_ffi.aesctr.dart | 3 +- lib/src/impl_ffi/impl_ffi.aesgcm.dart | 26 +- lib/src/impl_ffi/impl_ffi.dart | 2 +- lib/src/impl_ffi/impl_ffi.ec_common.dart | 37 +- lib/src/impl_ffi/impl_ffi.ecdh.dart | 4 +- lib/src/impl_ffi/impl_ffi.ecdsa.dart | 4 +- lib/src/impl_ffi/impl_ffi.hmac.dart | 19 +- lib/src/impl_ffi/impl_ffi.rsa_common.dart | 49 +- lib/src/impl_ffi/impl_ffi.rsaoaep.dart | 10 +- lib/src/impl_ffi/impl_ffi.utils.dart | 14 +- lib/src/impl_js/impl_js.aesgcm.dart | 13 +- lib/src/impl_js/impl_js.dart | 2 +- lib/src/impl_js/impl_js.hmac.dart | 7 +- lib/src/impl_js/impl_js.rsaoaep.dart | 4 +- lib/src/impl_js/impl_js.utils.dart | 3 +- lib/src/impl_stub.dart | 6 +- lib/src/jsonwebkey.dart | 170 ++--- lib/src/webcrypto/webcrypto.aesgcm.dart | 8 +- lib/src/webcrypto/webcrypto.hmac.dart | 6 +- lib/src/webcrypto/webcrypto.rsaoaep.dart | 4 +- pubspec.yaml | 14 +- test/utils/detected_runtime.dart | 4 +- test/utils/detected_runtime_html.dart | 2 +- test/utils/err_stack_ffi.dart | 2 +- test/utils/testrunner.dart | 759 +++++++++++----------- test/utils/utils.dart | 16 +- test/webcrypto/aescbc_test.dart | 8 +- test/webcrypto/aesctr_test.dart | 8 +- test/webcrypto/aesgcm_test.dart | 8 +- test/webcrypto/ecdh_test.dart | 2 +- test/webcrypto/hkdf_test.dart | 4 +- test/webcrypto/pbkdf2_test.dart | 2 +- 37 files changed, 633 insertions(+), 625 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7f899ec..0e00f802 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 0.5.0-null-safety.0 + * Ported to null-safety without any breaking changes. + # 0.2.2 * Increased Flutter SDK constraint to `>=1.24.0-10.2.pre` (current beta), because API version breakage in dynamic linking API for Dart SDK. diff --git a/example/pubspec.lock b/example/pubspec.lock index 198266ec..2924b5a7 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -119,7 +119,7 @@ packages: name: ffi url: "https://pub.dartlang.org" source: hosted - version: "0.1.3" + version: "0.2.0-nullsafety.1" file: dependency: transitive description: @@ -443,7 +443,7 @@ packages: path: ".." relative: true source: path - version: "0.2.2" + version: "0.5.0-null-safety.0" webdriver: dependency: transitive description: diff --git a/lib/src/boringssl/bytestring.dart b/lib/src/boringssl/bytestring.dart index b2cbbb02..5fcc6714 100644 --- a/lib/src/boringssl/bytestring.dart +++ b/lib/src/boringssl/bytestring.dart @@ -33,10 +33,10 @@ import 'lookup/lookup.dart'; /// }; /// ``` class CBS extends Struct { - Pointer data; + external Pointer data; @IntPtr() - int len; + external int len; } /// CBS_init sets cbs to point to data. It does not take ownership of data. @@ -69,21 +69,21 @@ final CBS_init = resolve(Sym.CBS_init) /// }; /// ``` class CBB extends Struct { - Pointer base; + external Pointer base; - Pointer child; + external Pointer child; @IntPtr() - int offset; + external int offset; @Uint8() - int pending_len_len; + external int pending_len_len; @Int8() - int pending_is_asn1; + external int pending_is_asn1; @Int8() - int is_top_level; + external int is_top_level; } /// CBB_zero sets an uninitialised cbb to the zero state. It must be initialised diff --git a/lib/src/boringssl/lookup/utils.dart b/lib/src/boringssl/lookup/utils.dart index 69b6d39e..0f8ea699 100644 --- a/lib/src/boringssl/lookup/utils.dart +++ b/lib/src/boringssl/lookup/utils.dart @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +// ignore_for_file: non_constant_identifier_names + import 'dart:io' show Platform, Directory, File; import 'dart:ffi'; import 'symbols.generated.dart'; @@ -38,7 +40,7 @@ String get libraryFileName { /// and initialize it with [initialize_dart_dl]. /// /// Returns `null` if it could not be found. -Pointer Function(Sym) lookupLibraryInDotDartTool() { +Pointer Function(Sym)? lookupLibraryInDotDartTool() { final dotDartTool = _findDotDartTool(); if (dotDartTool == null) { return null; @@ -51,7 +53,6 @@ Pointer Function(Sym) lookupLibraryInDotDartTool() { final library = DynamicLibrary.open(libraryFile.path); // Try to lookup the 'webcrypto_lookup_symbol' symbol - // ignore: non_constant_identifier_names final webcrypto_lookup_symbol = library .lookup Function(Int32)>>( 'webcrypto_lookup_symbol', @@ -89,7 +90,7 @@ void initialize_dart_dl(Pointer Function(Sym) lookup) { } /// Find the `.dart_tool/` folder, returns `null` if unable to find it. -Uri _findDotDartTool() { +Uri? _findDotDartTool() { // HACK: Because 'dart:isolate' is unavailable in Flutter we have no means // by which we can find the location of the package_config.json file. // Which we need, because the binary library created by: diff --git a/lib/src/impl_ffi/impl_ffi.aes_common.dart b/lib/src/impl_ffi/impl_ffi.aes_common.dart index 51221abe..2351aeb7 100644 --- a/lib/src/impl_ffi/impl_ffi.aes_common.dart +++ b/lib/src/impl_ffi/impl_ffi.aes_common.dart @@ -29,9 +29,8 @@ Uint8List _aesImportRawKey(List keyData) { Uint8List _aesImportJwkKey( Map jwk, { - @required String expectedJwkAlgSuffix, + required String expectedJwkAlgSuffix, }) { - assert(expectedJwkAlgSuffix != null); ArgumentError.checkNotNull(jwk, 'jwk'); final k = JsonWebKey.fromJson(jwk); @@ -43,7 +42,7 @@ Uint8List _aesImportJwkKey( checkJwk(k.k != null, 'k', 'must be present'); checkJwk(k.use == null || k.use == 'enc', 'use', 'must be "enc", if present'); - final keyData = _jwkDecodeBase64UrlNoPadding(k.k, 'k'); + final keyData = _jwkDecodeBase64UrlNoPadding(k.k!, 'k'); if (keyData.length == 24) { // 192-bit AES is intentionally unsupported, see https://crbug.com/533699 // If not supported in Chrome, there is not reason to support it in Dart. @@ -66,9 +65,8 @@ Uint8List _aesImportJwkKey( Map _aesExportJwkKey( List keyData, { - @required String jwkAlgSuffix, + required String jwkAlgSuffix, }) { - assert(jwkAlgSuffix != null); assert(keyData.length == 16 || keyData.length == 32); final algPrefix = keyData.length == 16 ? 'A128' : 'A256'; diff --git a/lib/src/impl_ffi/impl_ffi.aesctr.dart b/lib/src/impl_ffi/impl_ffi.aesctr.dart index d1b4d3be..fddc91a7 100644 --- a/lib/src/impl_ffi/impl_ffi.aesctr.dart +++ b/lib/src/impl_ffi/impl_ffi.aesctr.dart @@ -28,8 +28,7 @@ Future aesCtr_importJsonWebKey( Future aesCtr_generateKey(int length) async => _AesCtrSecretKey(_aesGenerateKey(length)); -BigInt _parseBigEndian(List data, [int bitLength]) { - assert(data != null); +BigInt _parseBigEndian(List data, [int? bitLength]) { bitLength ??= data.length * 8; assert(bitLength <= data.length * 8); diff --git a/lib/src/impl_ffi/impl_ffi.aesgcm.dart b/lib/src/impl_ffi/impl_ffi.aesgcm.dart index ce14c09a..66646a25 100644 --- a/lib/src/impl_ffi/impl_ffi.aesgcm.dart +++ b/lib/src/impl_ffi/impl_ffi.aesgcm.dart @@ -32,16 +32,15 @@ Future _aesGcmEncryptDecrypt( List key, List data, List iv, - List additionalData, + List? additionalData, int tagLength, bool isEncrypt, ) async { - ArgumentError.checkNotNull(data, 'data'); + final additionalData_ = additionalData ??= []; if (isEncrypt && data.length > (1 << 39) - 256) { // More than this is not allowed by Web crypto spec, we shall honor that. throw _OperationError('data may not be more than 2^39 - 256 bytes'); } - tagLength ??= 128; if (tagLength != 32 && tagLength != 64 && tagLength != 96 && @@ -51,7 +50,6 @@ Future _aesGcmEncryptDecrypt( tagLength != 128) { throw _OperationError('tagLength must be 32, 64, 96, 104, 112, 120 or 128'); } - additionalData ??= []; // TODO: Check iv length is less than EVP_AEAD_nonce_length // More importantly, add some test cases covering this, also consider @@ -88,8 +86,8 @@ Future _aesGcmEncryptDecrypt( iv.length, scope.dataAsPointer(data), data.length, - scope.dataAsPointer(additionalData), - additionalData.length, + scope.dataAsPointer(additionalData_), + additionalData_.length, )); }).sublist(0, outLen.value); } else { @@ -104,8 +102,8 @@ Future _aesGcmEncryptDecrypt( iv.length, scope.dataAsPointer(data), data.length, - scope.dataAsPointer(additionalData), - additionalData.length, + scope.dataAsPointer(additionalData_), + additionalData_.length, )); }).sublist(0, outLen.value); } @@ -122,15 +120,15 @@ class _AesGcmSecretKey implements AesGcmSecretKey { Future decryptBytes( List data, List iv, { - List additionalData, - int tagLength = 128, + List? additionalData, + int? tagLength = 128, }) async => _aesGcmEncryptDecrypt( _key, data, iv, additionalData, - tagLength, + tagLength ?? 128, false, ); @@ -138,15 +136,15 @@ class _AesGcmSecretKey implements AesGcmSecretKey { Future encryptBytes( List data, List iv, { - List additionalData, - int tagLength = 128, + List? additionalData, + int? tagLength = 128, }) async => _aesGcmEncryptDecrypt( _key, data, iv, additionalData, - tagLength, + tagLength ?? 128, true, ); diff --git a/lib/src/impl_ffi/impl_ffi.dart b/lib/src/impl_ffi/impl_ffi.dart index 463f59ab..405e869e 100644 --- a/lib/src/impl_ffi/impl_ffi.dart +++ b/lib/src/impl_ffi/impl_ffi.dart @@ -64,5 +64,5 @@ class _KeyPair implements KeyPair { @override final T publicKey; - _KeyPair({this.privateKey, this.publicKey}); + _KeyPair({required this.privateKey, required this.publicKey}); } diff --git a/lib/src/impl_ffi/impl_ffi.ec_common.dart b/lib/src/impl_ffi/impl_ffi.ec_common.dart index 190180d6..ef825ac2 100644 --- a/lib/src/impl_ffi/impl_ffi.ec_common.dart +++ b/lib/src/impl_ffi/impl_ffi.ec_common.dart @@ -33,8 +33,6 @@ int _ecCurveToNID(EllipticCurve curve) { /// Get [EllipticCurve] from matching BoringSSL `ssl.NID_...`. EllipticCurve _ecCurveFromNID(int nid) { - assert(nid != null); - if (nid == ssl.NID_X9_62_prime256v1) { return EllipticCurve.p256; } @@ -126,17 +124,22 @@ ffi.Pointer _importSpkiEcPublicKey( ffi.Pointer _importJwkEcPrivateOrPublicKey( JsonWebKey jwk, EllipticCurve curve, { - @required bool isPrivateKey, - @required String expectedUse, - String expectedAlg, // may be null, if 'alg' property isn't validated (ECDH) + required bool isPrivateKey, + required String expectedUse, + String? expectedAlg, // may be null, if 'alg' property isn't validated (ECDH) }) { - assert(isPrivateKey != null); - assert(expectedUse != null); - _checkData( jwk.kty == 'EC', message: 'expected a elliptic-curve key, JWK property "kty" must be "EC"', ); + _checkData( + jwk.x != null, + message: 'expected a elliptic-curve key, JWK property "x" to be present', + ); + _checkData( + jwk.y != null, + message: 'expected a elliptic-curve key, JWK property "y" to be present', + ); if (isPrivateKey) { _checkData( jwk.d != null, @@ -193,15 +196,15 @@ ffi.Pointer _importJwkEcPrivateOrPublicKey( _checkDataIsOne( ssl.EC_KEY_set_public_key_affine_coordinates( ec, - decodeParam(jwk.x, 'x'), - decodeParam(jwk.y, 'y'), + decodeParam(jwk.x!, 'x'), + decodeParam(jwk.y!, 'y'), ), fallback: 'invalid EC key', ); if (isPrivateKey) { _checkDataIsOne( - ssl.EC_KEY_set_private_key(ec, decodeParam(jwk.d, 'd')), + ssl.EC_KEY_set_private_key(ec, decodeParam(jwk.d!, 'd')), fallback: 'invalid EC key', ); } @@ -262,7 +265,7 @@ Uint8List _exportRawEcPublicKey(ffi.Pointer key) { final scope = _Scope(); try { final ec = ssl.EVP_PKEY_get1_EC_KEY(key); - _checkOp(ec.address != null, fallback: 'internal key type invariant error'); + _checkOp(ec.address != 0, fallback: 'internal key type invariant error'); scope.defer(() => ssl.EC_KEY_free(ec)); return _withOutCBB((cbb) { @@ -283,11 +286,9 @@ Uint8List _exportRawEcPublicKey(ffi.Pointer key) { Map _exportJwkEcPrivateOrPublicKey( ffi.Pointer key, { - @required bool isPrivateKey, - String jwkUse, + required bool isPrivateKey, + String? jwkUse, }) { - assert(isPrivateKey != null); - final scope = _Scope(); try { final ec = ssl.EVP_PKEY_get1_EC_KEY(key); @@ -318,7 +319,7 @@ Map _exportJwkEcPrivateOrPublicKey( _checkOpIsOne(ssl.BN_bn2bin_padded(p, paramSize, y)); }); - Uint8List dAsBytes; + Uint8List? dAsBytes; if (isPrivateKey) { final d = ssl.EC_KEY_get0_private_key(ec); dAsBytes = _withOutPointer(paramSize, (ffi.Pointer p) { @@ -332,7 +333,7 @@ Map _exportJwkEcPrivateOrPublicKey( crv: _ecCurveToJwkCrv(curve), x: _jwkEncodeBase64UrlNoPadding(xAsBytes), y: _jwkEncodeBase64UrlNoPadding(yAsBytes), - d: isPrivateKey ? _jwkEncodeBase64UrlNoPadding(dAsBytes) : null, + d: dAsBytes != null ? _jwkEncodeBase64UrlNoPadding(dAsBytes) : null, ).toJson(); } finally { scope.release(); diff --git a/lib/src/impl_ffi/impl_ffi.ecdh.dart b/lib/src/impl_ffi/impl_ffi.ecdh.dart index 314e0d2b..7a3ddfa4 100644 --- a/lib/src/impl_ffi/impl_ffi.ecdh.dart +++ b/lib/src/impl_ffi/impl_ffi.ecdh.dart @@ -88,9 +88,7 @@ class _EcdhPrivateKey implements EcdhPrivateKey { final scope = _Scope(); try { - final _publicKey = publicKey as _EcdhPublicKey; - - final pubEcKey = ssl.EVP_PKEY_get1_EC_KEY(_publicKey._key); + final pubEcKey = ssl.EVP_PKEY_get1_EC_KEY(publicKey._key); _checkOp(pubEcKey.address != 0, fallback: 'not an ec key'); scope.defer(() => ssl.EC_KEY_free(pubEcKey)); diff --git a/lib/src/impl_ffi/impl_ffi.ecdsa.dart b/lib/src/impl_ffi/impl_ffi.ecdsa.dart index c91b47d2..c71b40b0 100644 --- a/lib/src/impl_ffi/impl_ffi.ecdsa.dart +++ b/lib/src/impl_ffi/impl_ffi.ecdsa.dart @@ -137,9 +137,9 @@ Uint8List _convertEcdsaDerSignatureToWebCryptoSignature( /// Returns `null` if the [signature] is invalid and should be rejected. /// /// See also: https://chromium.googlesource.com/chromium/src/+/43d62c50b705f88c67b14539e91fd8fd017f70c4/components/webcrypto/algorithms/ecdsa.cc#111 -Uint8List _convertEcdsaWebCryptoSignatureToDerSignature( +Uint8List? _convertEcdsaWebCryptoSignatureToDerSignature( ffi.Pointer key, - Uint8List signature, + List signature, ) { final scope = _Scope(); try { diff --git a/lib/src/impl_ffi/impl_ffi.hmac.dart b/lib/src/impl_ffi/impl_ffi.hmac.dart index 50ae760b..12ea0f19 100644 --- a/lib/src/impl_ffi/impl_ffi.hmac.dart +++ b/lib/src/impl_ffi/impl_ffi.hmac.dart @@ -15,19 +15,19 @@ part of impl_ffi; /// Convert [data] to [Uint8List] and zero to [lengthInBits] if given. -Uint8List _asUint8ListZeroedToBitLength(List data, [int lengthInBits]) { - data = Uint8List.fromList(data); +Uint8List _asUint8ListZeroedToBitLength(List data, [int? lengthInBits]) { + final buf = Uint8List.fromList(data); if (lengthInBits != null) { final startFrom = (lengthInBits / 8).floor(); var remainder = (lengthInBits % 8).toInt(); - for (var i = startFrom; i < data.length; i++) { + for (var i = startFrom; i < buf.length; i++) { // TODO: This passes tests, but I think this should be >> instead.. hmm... final mask = 0xff & (0xff << (8 - remainder)); - data[i] = data[i] & mask; + buf[i] = buf[i] & mask; remainder = 8; } } - return data; + return buf; } String _hmacJwkAlgFromHash(_Hash hash) { @@ -50,7 +50,7 @@ String _hmacJwkAlgFromHash(_Hash hash) { Future hmacSecretKey_importRawKey( List keyData, Hash hash, { - int length, + int? length, }) async { return _HmacSecretKey( _asUint8ListZeroedToBitLength(keyData, length), @@ -61,7 +61,7 @@ Future hmacSecretKey_importRawKey( Future hmacSecretKey_importJsonWebKey( Map jwk, Hash hash, { - int length, + int? length, }) async { ArgumentError.checkNotNull(jwk, 'jwk'); ArgumentError.checkNotNull(hash, 'hash'); @@ -82,12 +82,13 @@ Future hmacSecretKey_importJsonWebKey( 'must be "$expectedAlg"', ); - final keyData = _jwkDecodeBase64UrlNoPadding(k.k, 'k'); + final keyData = _jwkDecodeBase64UrlNoPadding(k.k!, 'k'); return hmacSecretKey_importRawKey(keyData, hash, length: length); } -Future hmacSecretKey_generateKey(Hash hash, {int length}) async { +Future hmacSecretKey_generateKey(Hash hash, + {int? length}) async { final h = _Hash.fromHash(hash); length ??= ssl.EVP_MD_size(h.MD) * 8; final keyData = Uint8List((length / 8).ceil()); diff --git a/lib/src/impl_ffi/impl_ffi.rsa_common.dart b/lib/src/impl_ffi/impl_ffi.rsa_common.dart index 640a582e..1ffd4fc4 100644 --- a/lib/src/impl_ffi/impl_ffi.rsa_common.dart +++ b/lib/src/impl_ffi/impl_ffi.rsa_common.dart @@ -60,16 +60,17 @@ ffi.Pointer _importSpkiRsaPublicKey(List keyData) { ffi.Pointer _importJwkRsaPrivateOrPublicKey( JsonWebKey jwk, { - @required bool isPrivateKey, - @required String expectedAlg, - String expectedUse, + required bool isPrivateKey, + required String expectedAlg, + required String expectedUse, }) { - assert(isPrivateKey != null); - assert(expectedAlg != null); - final scope = _Scope(); try { - void checkJwk(bool condition, String prop, String message) => + void checkJwk( + bool condition, + String prop, [ + String message = 'must be present', + ]) => _checkData(condition, message: 'JWK property "$prop" $message'); checkJwk(jwk.kty == 'RSA', 'kty', 'must be "RSA"'); @@ -107,8 +108,10 @@ ffi.Pointer _importJwkRsaPrivateOrPublicKey( final rsa = scope.create(ssl.RSA_new, ssl.RSA_free); - final n = readBN(jwk.n, 'n'); - final e = readBN(jwk.e, 'e'); + checkJwk(jwk.n != null, 'n'); + checkJwk(jwk.e != null, 'e'); + final n = readBN(jwk.n!, 'n'); + final e = readBN(jwk.e!, 'e'); _checkOpIsOne(ssl.RSA_set0_key(rsa, n, e, ffi.nullptr)); scope.move(n); // ssl.RSA_set0_key takes ownership scope.move(e); @@ -116,7 +119,8 @@ ffi.Pointer _importJwkRsaPrivateOrPublicKey( if (isPrivateKey) { // The "p", "q", "dp", "dq", and "qi" properties are optional in the JWA // spec. However they are required by Chromium's WebCrypto implementation. - final d = readBN(jwk.d, 'd'); + checkJwk(jwk.d != null, 'd'); + final d = readBN(jwk.d!, 'd'); // If present properties p,q,dp,dq,qi enable optional optimizations, see: // https://tools.ietf.org/html/rfc7518#section-6.3.2 // However, these are required by Chromes Web Crypto implementation: @@ -127,11 +131,16 @@ ffi.Pointer _importJwkRsaPrivateOrPublicKey( // and, (b) following Chromes/Firefox behavior is safe. // Notice, we can choose to support this in the future without breaking // the public API. - final p = readBN(jwk.p, 'p'); - final q = readBN(jwk.q, 'q'); - final dp = readBN(jwk.dp, 'dp'); - final dq = readBN(jwk.dq, 'dq'); - final qi = readBN(jwk.qi, 'qi'); + checkJwk(jwk.p != null, 'p'); + checkJwk(jwk.q != null, 'q'); + checkJwk(jwk.dp != null, 'dp'); + checkJwk(jwk.dq != null, 'dq'); + checkJwk(jwk.qi != null, 'qi'); + final p = readBN(jwk.p!, 'p'); + final q = readBN(jwk.q!, 'q'); + final dp = readBN(jwk.dp!, 'dp'); + final dq = readBN(jwk.dq!, 'dq'); + final qi = readBN(jwk.qi!, 'qi'); _checkOpIsOne(ssl.RSA_set0_key(rsa, ffi.nullptr, ffi.nullptr, d)); scope.move(d); // ssl.RSA_set0_key takes ownership @@ -165,14 +174,10 @@ ffi.Pointer _importJwkRsaPrivateOrPublicKey( Map _exportJwkRsaPrivateOrPublicKey( ffi.Pointer key, { - @required bool isPrivateKey, - @required String jwkAlg, - @required String jwkUse, + required bool isPrivateKey, + required String jwkAlg, + required String jwkUse, }) { - assert(isPrivateKey != null); - assert(jwkUse != null); - assert(jwkAlg != null); - final scope = _Scope(); try { final rsa = ssl.EVP_PKEY_get1_RSA(key); diff --git a/lib/src/impl_ffi/impl_ffi.rsaoaep.dart b/lib/src/impl_ffi/impl_ffi.rsaoaep.dart index 9fcf22ff..3de51d92 100644 --- a/lib/src/impl_ffi/impl_ffi.rsaoaep.dart +++ b/lib/src/impl_ffi/impl_ffi.rsaoaep.dart @@ -118,10 +118,8 @@ Future _rsaOaepeEncryptOrDecryptBytes( ) encryptOrDecryptFn, List data, { - List label, + List? label, }) async { - ArgumentError.checkNotNull(data, 'data'); - final ctx = ssl.EVP_PKEY_CTX_new(key, ffi.nullptr); _checkOp(ctx.address != 0, fallback: 'allocation error'); try { @@ -183,8 +181,7 @@ class _RsaOaepPrivateKey implements RsaOaepPrivateKey { _RsaOaepPrivateKey(this._key, this._hash); @override - Future decryptBytes(List data, {List label}) async { - ArgumentError.checkNotNull(data, 'data'); + Future decryptBytes(List data, {List? label}) async { return _rsaOaepeEncryptOrDecryptBytes( _key, _hash.MD, @@ -219,8 +216,7 @@ class _RsaOaepPublicKey implements RsaOaepPublicKey { _RsaOaepPublicKey(this._key, this._hash); @override - Future encryptBytes(List data, {List label}) async { - ArgumentError.checkNotNull(data, 'data'); + Future encryptBytes(List data, {List? label}) async { return _rsaOaepeEncryptOrDecryptBytes( _key, _hash.MD, diff --git a/lib/src/impl_ffi/impl_ffi.utils.dart b/lib/src/impl_ffi/impl_ffi.utils.dart index 7dfc3d0c..049fa0b2 100644 --- a/lib/src/impl_ffi/impl_ffi.utils.dart +++ b/lib/src/impl_ffi/impl_ffi.utils.dart @@ -54,7 +54,7 @@ ffi.Pointer _createEVP_PKEYwithFinalizer() { /// /// If [message] is given we use that, otherwise we use error from BoringSSL, /// and if nothing is available there we use [fallback]. -void _checkOp(bool condition, {String message, String fallback}) { +void _checkOp(bool condition, {String? message, String? fallback}) { if (!condition) { // Always extract the error to ensure we clear the error queue. final err = _extractError(); @@ -67,14 +67,14 @@ void _checkOp(bool condition, {String message, String fallback}) { /// /// If [message] is given we use that, otherwise we use error from BoringSSL, /// and if nothing is available there we use [fallback]. -void _checkOpIsOne(int retval, {String message, String fallback}) => +void _checkOpIsOne(int retval, {String? message, String? fallback}) => _checkOp(retval == 1, message: message, fallback: fallback); /// Throw [FormatException] if [condition] is `false`. /// /// If [message] is given we use that, otherwise we use error from BoringSSL, /// and if nothing is available there we use [fallback]. -void _checkData(bool condition, {String message, String fallback}) { +void _checkData(bool condition, {String? message, String? fallback}) { if (!condition) { // Always extract the error to ensure we clear the error queue. final err = _extractError(); @@ -87,14 +87,14 @@ void _checkData(bool condition, {String message, String fallback}) { /// /// If [message] is given we use that, otherwise we use error from BoringSSL, /// and if nothing is available there we use [fallback]. -void _checkDataIsOne(int retval, {String message, String fallback}) => +void _checkDataIsOne(int retval, {String? message, String? fallback}) => _checkData(retval == 1, message: message, fallback: fallback); /// Extract latest error on this thread as [String] and clear the error queue /// for this thread. /// /// Returns `null` if there is no error. -String _extractError() { +String? _extractError() { try { // Get the error. final err = ssl.ERR_get_error(); @@ -140,7 +140,7 @@ void _free(ffi.Pointer p) { } class _ScopeEntry { - final Object handle; + final Object? handle; final void Function() fn; _ScopeEntry(this.handle, this.fn); @@ -151,7 +151,7 @@ class _Scope { final List<_ScopeEntry> _deferred = []; /// Defer [fn] to end of this scope. - void defer(void Function() fn, [Object handle]) => + void defer(void Function() fn, [Object? handle]) => _deferred.add(_ScopeEntry(handle, fn)); /// Allocate an [ffi.Pointer] in this scope. diff --git a/lib/src/impl_js/impl_js.aesgcm.dart b/lib/src/impl_js/impl_js.aesgcm.dart index e229eb3a..4b1bb625 100644 --- a/lib/src/impl_js/impl_js.aesgcm.dart +++ b/lib/src/impl_js/impl_js.aesgcm.dart @@ -56,13 +56,10 @@ class _AesGcmSecretKey implements AesGcmSecretKey { Future decryptBytes( List data, List iv, { - List additionalData, - int tagLength = 128, + List? additionalData, + int? tagLength = 128, }) async { - ArgumentError.checkNotNull(iv, 'iv'); - // TODO: Ask lrn@ how to implement default parameters -- should null be special tagLength ??= 128; - ArgumentError.checkNotNull(tagLength, 'tagLength'); return await _decrypt( additionalData == null ? subtle.Algorithm( @@ -85,12 +82,10 @@ class _AesGcmSecretKey implements AesGcmSecretKey { Future encryptBytes( List data, List iv, { - List additionalData, - int tagLength = 128, + List? additionalData, + int? tagLength = 128, }) async { - ArgumentError.checkNotNull(iv, 'iv'); tagLength ??= 128; - ArgumentError.checkNotNull(tagLength, 'tagLength'); return await _encrypt( additionalData == null ? subtle.Algorithm( diff --git a/lib/src/impl_js/impl_js.dart b/lib/src/impl_js/impl_js.dart index 5ac2a4ef..712dc114 100644 --- a/lib/src/impl_js/impl_js.dart +++ b/lib/src/impl_js/impl_js.dart @@ -53,5 +53,5 @@ class _KeyPair implements KeyPair { @override final T publicKey; - _KeyPair({this.privateKey, this.publicKey}); + _KeyPair({required this.privateKey, required this.publicKey}); } diff --git a/lib/src/impl_js/impl_js.hmac.dart b/lib/src/impl_js/impl_js.hmac.dart index b8a70f3d..54e12792 100644 --- a/lib/src/impl_js/impl_js.hmac.dart +++ b/lib/src/impl_js/impl_js.hmac.dart @@ -19,7 +19,7 @@ final _hmacAlgorithm = subtle.Algorithm(name: 'HMAC'); Future hmacSecretKey_importRawKey( List keyData, Hash hash, { - int length, + int? length, }) async { return _HmacSecretKey(await _importKey( 'raw', @@ -42,7 +42,7 @@ Future hmacSecretKey_importRawKey( Future hmacSecretKey_importJsonWebKey( Map jwk, Hash hash, { - int length, + int? length, }) async { return _HmacSecretKey(await _importJsonWebKey( jwk, @@ -61,7 +61,8 @@ Future hmacSecretKey_importJsonWebKey( )); } -Future hmacSecretKey_generateKey(Hash hash, {int length}) async { +Future hmacSecretKey_generateKey(Hash hash, + {int? length}) async { return _HmacSecretKey(await _generateKey( length == null ? subtle.Algorithm( diff --git a/lib/src/impl_js/impl_js.rsaoaep.dart b/lib/src/impl_js/impl_js.rsaoaep.dart index 85ca141c..72717ce4 100644 --- a/lib/src/impl_js/impl_js.rsaoaep.dart +++ b/lib/src/impl_js/impl_js.rsaoaep.dart @@ -104,7 +104,7 @@ class _RsaOaepPrivateKey implements RsaOaepPrivateKey { _RsaOaepPrivateKey(this._key); @override - Future decryptBytes(List data, {List label}) async { + Future decryptBytes(List data, {List? label}) async { return _decrypt( label == null ? subtle.Algorithm(name: _rsaOaepAlgorithmName) @@ -133,7 +133,7 @@ class _RsaOaepPublicKey implements RsaOaepPublicKey { _RsaOaepPublicKey(this._key); @override - Future encryptBytes(List data, {List label}) async { + Future encryptBytes(List data, {List? label}) async { return _encrypt( label == null ? subtle.Algorithm(name: _rsaOaepAlgorithmName) diff --git a/lib/src/impl_js/impl_js.utils.dart b/lib/src/impl_js/impl_js.utils.dart index 0397e7af..fa57db0b 100644 --- a/lib/src/impl_js/impl_js.utils.dart +++ b/lib/src/impl_js/impl_js.utils.dart @@ -47,6 +47,7 @@ String _curveToName(EllipticCurve curve) { return 'P-521'; } // This should never happen. + // ignore: dead_code throw AssertionError('Unknown curve "$curve"'); } @@ -126,7 +127,7 @@ final _usagesDeriveBits = ['deriveBits']; /// Adapt `crypto.subtle.importKey` to Dart types for JWK. Future _importJsonWebKey( - Map jwk, + Map jwk, subtle.Algorithm algorithm, List usages, String expectedType, diff --git a/lib/src/impl_stub.dart b/lib/src/impl_stub.dart index 32196100..51ea4d64 100644 --- a/lib/src/impl_stub.dart +++ b/lib/src/impl_stub.dart @@ -49,18 +49,18 @@ const Hash sha512 = _UnimplementedHash(); Future hmacSecretKey_importRawKey( List keyData, Hash hash, { - int length, + int? length, }) => throw _notImplemented; Future hmacSecretKey_importJsonWebKey( Map jwk, Hash hash, { - int length, + int? length, }) => throw _notImplemented; -Future hmacSecretKey_generateKey(Hash hash, {int length}) => +Future hmacSecretKey_generateKey(Hash hash, {int? length}) => throw _notImplemented; //---------------------- RSASSA_PKCS1_v1_5 diff --git a/lib/src/jsonwebkey.dart b/lib/src/jsonwebkey.dart index 87bc869d..4c73bef1 100644 --- a/lib/src/jsonwebkey.dart +++ b/lib/src/jsonwebkey.dart @@ -19,24 +19,24 @@ /// [1]: https://www.w3.org/TR/WebCryptoAPI/#JsonWebKey-dictionary /// [2]: https://www.iana.org/assignments/jose/jose.xhtml#web-key-parameters class JsonWebKey { - String kty; - String use; - List key_ops; - String alg; - bool ext; - String crv; - String x; - String y; - String d; - String n; - String e; - String p; - String q; - String dp; - String dq; - String qi; - List oth; - String k; + String? kty; + String? use; + List? key_ops; + String? alg; + bool? ext; + String? crv; + String? x; + String? y; + String? d; + String? n; + String? e; + String? p; + String? q; + String? dp; + String? dq; + String? qi; + List? oth; + String? k; JsonWebKey({ this.kty, @@ -59,7 +59,7 @@ class JsonWebKey { this.k, }); - static JsonWebKey fromJson(Map json) { + static JsonWebKey fromJson(Map json) { const stringKeys = [ 'kty', 'use', @@ -82,7 +82,7 @@ class JsonWebKey { throw FormatException('JWK entry "$k" must be a string', json); } } - List key_ops; + List? key_ops; if (json.containsKey('key_ops')) { if (json['key_ops'] is! List || (json['key_ops'] as List).any((e) => e is! String)) { @@ -95,7 +95,7 @@ class JsonWebKey { if (json.containsKey('ext') && json['ext'] is! bool) { throw FormatException('JWK entry "ext" must be boolean', json); } - List oth; + List? oth; if (json.containsKey('oth')) { if (json['oth'] is! List || (json['oth'] as List).any((e) => e is! Map)) { throw FormatException('JWK entry "oth" must be list of maps', json); @@ -105,24 +105,24 @@ class JsonWebKey { }).toList(); } return JsonWebKey( - kty: json['kty'] as String, - use: json['use'] as String, + kty: json['kty'] as String?, + use: json['use'] as String?, key_ops: key_ops, - alg: json['alg'] as String, - ext: json['ext'] as bool, - crv: json['crv'] as String, - x: json['x'] as String, - y: json['y'] as String, - d: json['d'] as String, - n: json['n'] as String, - e: json['e'] as String, - p: json['p'] as String, - q: json['q'] as String, - dp: json['dp'] as String, - dq: json['dq'] as String, - qi: json['qi'] as String, + alg: json['alg'] as String?, + ext: json['ext'] as bool?, + crv: json['crv'] as String?, + x: json['x'] as String?, + y: json['y'] as String?, + d: json['d'] as String?, + n: json['n'] as String?, + e: json['e'] as String?, + p: json['p'] as String?, + q: json['q'] as String?, + dp: json['dp'] as String?, + dq: json['dq'] as String?, + qi: json['qi'] as String?, oth: oth, - k: json['k'] as String, + k: json['k'] as String?, ); } @@ -130,61 +130,79 @@ class JsonWebKey { final json = {}; // Set properties from all the string keys - if (kty != null) { - json['kty'] = kty; + final kty_ = kty; + if (kty_ != null) { + json['kty'] = kty_; } - if (use != null) { - json['use'] = use; + final use_ = use; + if (use_ != null) { + json['use'] = use_; } - if (alg != null) { - json['alg'] = alg; + final alg_ = alg; + if (alg_ != null) { + json['alg'] = alg_; } - if (crv != null) { - json['crv'] = crv; + final crv_ = crv; + if (crv_ != null) { + json['crv'] = crv_; } - if (x != null) { - json['x'] = x; + final x_ = x; + if (x_ != null) { + json['x'] = x_; } - if (y != null) { - json['y'] = y; + final y_ = y; + if (y_ != null) { + json['y'] = y_; } - if (d != null) { - json['d'] = d; + final d_ = d; + if (d_ != null) { + json['d'] = d_; } - if (n != null) { - json['n'] = n; + final n_ = n; + if (n_ != null) { + json['n'] = n_; } - if (e != null) { - json['e'] = e; + final e_ = e; + if (e_ != null) { + json['e'] = e_; } - if (p != null) { - json['p'] = p; + final p_ = p; + if (p_ != null) { + json['p'] = p_; } - if (q != null) { - json['q'] = q; + final q_ = q; + if (q_ != null) { + json['q'] = q_; } - if (dp != null) { - json['dp'] = dp; + final dp_ = dp; + if (dp_ != null) { + json['dp'] = dp_; } - if (dq != null) { - json['dq'] = dq; + final dq_ = dq; + if (dq_ != null) { + json['dq'] = dq_; } - if (qi != null) { - json['qi'] = qi; + final qi_ = qi; + if (qi_ != null) { + json['qi'] = qi_; } - if (k != null) { - json['k'] = k; + final k_ = k; + if (k_ != null) { + json['k'] = k_; } // Set non-string properties - if (key_ops != null) { - json['key_ops'] = key_ops; + final key_ops_ = key_ops; + if (key_ops_ != null) { + json['key_ops'] = key_ops_; } - if (ext != null) { - json['ext'] = ext; + final ext_ = ext; + if (ext_ != null) { + json['ext'] = ext_; } - if (oth != null) { - json['oth'] = oth.map((e) => e.toJson()).toList(); + final oth_ = oth; + if (oth_ != null) { + json['oth'] = oth_.map((e) => e.toJson()).toList(); } return json; @@ -203,9 +221,9 @@ class RsaOtherPrimesInfo { String t; RsaOtherPrimesInfo({ - this.r, - this.d, - this.t, + required this.r, + required this.d, + required this.t, }); static RsaOtherPrimesInfo fromJson(Map json) { diff --git a/lib/src/webcrypto/webcrypto.aesgcm.dart b/lib/src/webcrypto/webcrypto.aesgcm.dart index 28ef1a81..d8784477 100644 --- a/lib/src/webcrypto/webcrypto.aesgcm.dart +++ b/lib/src/webcrypto/webcrypto.aesgcm.dart @@ -42,15 +42,15 @@ abstract class AesGcmSecretKey { Future encryptBytes( List data, List iv, { - List additionalData, - int tagLength = 128, + List? additionalData, + int? tagLength = 128, }); Future decryptBytes( List data, List iv, { - List additionalData, - int tagLength = 128, + List? additionalData, + int? tagLength = 128, }); Future exportRawKey(); diff --git a/lib/src/webcrypto/webcrypto.hmac.dart b/lib/src/webcrypto/webcrypto.hmac.dart index ecc42445..9d4f6902 100644 --- a/lib/src/webcrypto/webcrypto.hmac.dart +++ b/lib/src/webcrypto/webcrypto.hmac.dart @@ -51,7 +51,7 @@ abstract class HmacSecretKey { static Future importRawKey( List keyData, Hash hash, { - int length, + int? length, }) { ArgumentError.checkNotNull(keyData, 'keyData'); ArgumentError.checkNotNull(hash, 'hash'); @@ -87,7 +87,7 @@ abstract class HmacSecretKey { // what is in the JWK matches what is also given. // Note. it's not yet clear if JWK always contains key parameters. Hash hash, { - int length, + int? length, }) { ArgumentError.checkNotNull(jwk, 'jwk'); ArgumentError.checkNotNull(hash, 'hash'); @@ -124,7 +124,7 @@ abstract class HmacSecretKey { /// // Generate a new random HMAC secret key. /// final key = await HmacSecretKey.generate(Hash.sha256); /// ``` - static Future generateKey(Hash hash, {int length}) { + static Future generateKey(Hash hash, {int? length}) { ArgumentError.checkNotNull(hash, 'hash'); if (length != null && length <= 0) { throw ArgumentError.value(length, 'length', 'must be positive'); diff --git a/lib/src/webcrypto/webcrypto.rsaoaep.dart b/lib/src/webcrypto/webcrypto.rsaoaep.dart index cea79b79..a292288e 100644 --- a/lib/src/webcrypto/webcrypto.rsaoaep.dart +++ b/lib/src/webcrypto/webcrypto.rsaoaep.dart @@ -57,7 +57,7 @@ abstract class RsaOaepPrivateKey { /// Note, that this interface does not support streaming because RSA-OAEP /// is not a streaming cipher, instead it is often used to encrypt a symmetric /// cipher key used with an AES variant. - Future decryptBytes(List data, {List label}); + Future decryptBytes(List data, {List? label}); Future exportPkcs8Key(); @@ -91,7 +91,7 @@ abstract class RsaOaepPublicKey { /// Note, that this interface does not support streaming because RSA-OAEP /// is not a streaming cipher, instead it is often used to encrypt a symmetric /// cipher key used with an AES variant. - Future encryptBytes(List data, {List label}); + Future encryptBytes(List data, {List? label}); Future exportSpkiKey(); diff --git a/pubspec.yaml b/pubspec.yaml index da0b0840..6fdb8e06 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,18 +13,18 @@ # limitations under the License. name: webcrypto -version: 0.2.2 +version: 0.5.0-null-safety.0 description: Cross-platform implementation of Web Cryptography APIs for Flutter. homepage: https://github.com/google/webcrypto.dart environment: - sdk: '>=2.8.0 <3.0.0' + sdk: '>=2.12.0-0 <3.0.0' flutter: '>=1.24.0-10.2.pre' dependencies: - ffi: ^0.1.3 - js: ^0.6.1 - meta: ^1.1.7 + ffi: ^0.2.0-nullsafety.1 + js: ^0.6.3-nullsafety.3 + meta: ^1.3.0-nullsafety.6 # Needed for `lib/src/flutter/webcrypto_plugin.dart` which allows boiler-plate # in `flutter.plugin.platforms.web` added below. flutter_web_plugins: @@ -33,8 +33,8 @@ dependencies: sdk: flutter dev_dependencies: - test: ^1.5.2 - yaml: ^2.2.1 + test: ^1.16.0-nullsafety.12 + yaml: ^3.0.0-nullsafety.0 pedantic: ^1.9.0 flutter_test: sdk: flutter diff --git a/test/utils/detected_runtime.dart b/test/utils/detected_runtime.dart index da87fa4d..28d895bc 100644 --- a/test/utils/detected_runtime.dart +++ b/test/utils/detected_runtime.dart @@ -25,10 +25,10 @@ export 'detected_runtime_stub.dart' /// /// This utility helps filter away test cases and features known to not work on /// Firefox and which has been documented in the API documentation. -T nullOnFirefox(T value) => detectedRuntime == 'firefox' ? null : value; +T? nullOnFirefox(T value) => detectedRuntime == 'firefox' ? null : value; /// Return `null` instead of [value] on Safari. /// /// This utility helps filter away test cases and features known to not work on /// Safari and which has been documented in the API documentation. -T nullOnSafari(T value) => detectedRuntime == 'safari' ? null : value; +T? nullOnSafari(T value) => detectedRuntime == 'safari' ? null : value; diff --git a/test/utils/detected_runtime_html.dart b/test/utils/detected_runtime_html.dart index 8c2913cc..85bc0439 100644 --- a/test/utils/detected_runtime_html.dart +++ b/test/utils/detected_runtime_html.dart @@ -31,7 +31,7 @@ import 'dart:html' show window; /// /// [1]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent final String detectedRuntime = () { - final ua = window.navigator.userAgent ?? ''; + final ua = window.navigator.userAgent; if (ua.contains('Gecko/')) { return 'firefox'; diff --git a/test/utils/err_stack_ffi.dart b/test/utils/err_stack_ffi.dart index d1f76a1e..fb5ee023 100644 --- a/test/utils/err_stack_ffi.dart +++ b/test/utils/err_stack_ffi.dart @@ -34,7 +34,7 @@ Future checkErrorStack(FutureOr Function() fn) async { // Get the error. final err = ssl.ERR_get_error(); if (err == 0) { - return null; + return ret; } const N = 4096; // Max error message size final p = ffi.allocate(count: N); diff --git a/test/utils/testrunner.dart b/test/utils/testrunner.dart index 8e753e85..d04c72d8 100644 --- a/test/utils/testrunner.dart +++ b/test/utils/testrunner.dart @@ -28,13 +28,13 @@ import 'err_stack_stub.dart' if (dart.library.ffi) 'err_stack_ffi.dart'; // Export utilities necessary for implementing a `TestRunner`. export 'utils.dart' show hashFromJson, curveFromJson; -List _optionalBase64Decode(dynamic data) => +List? _optionalBase64Decode(dynamic data) => data == null ? null : base64.decode(data as String); -Map _optionalStringMapDecode(dynamic data) => +Map? _optionalStringMapDecode(dynamic data) => data == null ? null : (data as Map).cast(); -String _optionalBase64Encode(List data) => +String? _optionalBase64Encode(List? data) => data == null ? null : base64.encode(data); @sealed @@ -42,36 +42,36 @@ class _TestCase { final String name; // Obtain a keyPair from import or key generation - final Map generateKeyParams; - final List privateRawKeyData; - final List privatePkcs8KeyData; - final Map privateJsonWebKeyData; - final List publicRawKeyData; - final List publicSpkiKeyData; - final Map publicJsonWebKeyData; + final Map? generateKeyParams; + final List? privateRawKeyData; + final List? privatePkcs8KeyData; + final Map? privateJsonWebKeyData; + final List? publicRawKeyData; + final List? publicSpkiKeyData; + final Map? publicJsonWebKeyData; // Plaintext to be signed, (always required) - final List plaintext; + final List? plaintext; // Signature to be verified (invalid, if generateKeyParams != null) - final List signature; + final List? signature; // Ciphertext of plaintext (invalid, if generateKeyParams != null) - final List ciphertext; + final List? ciphertext; // Bits derived using deriveBits (invalid, if generateKeyParams != null) - final List derivedBits; + final List? derivedBits; // Number of bits to derive. - final int derivedLength; + final int? derivedLength; // Parameters for key import (always required) - final Map importKeyParams; + final Map? importKeyParams; // Parameters for sign/verify (required, if there is a signature) - final Map signVerifyParams; + final Map? signVerifyParams; // Parameters for encrypt/decrypt (required, if there is a ciphertext) - final Map encryptDecryptParams; + final Map? encryptDecryptParams; // Parameters for deriveBits (required, if there is a derivedBits) - final Map deriveParams; + final Map? deriveParams; _TestCase( this.name, { @@ -109,7 +109,7 @@ class _TestCase { signature: _optionalBase64Decode(json['signature']), ciphertext: _optionalBase64Decode(json['ciphertext']), derivedBits: _optionalBase64Decode(json['derivedBits']), - derivedLength: json['derivedLength'] as int, + derivedLength: json['derivedLength'] as int?, importKeyParams: _optionalStringMapDecode(json['importKeyParams']), signVerifyParams: _optionalStringMapDecode(json['signVerifyParams']), encryptDecryptParams: @@ -221,7 +221,7 @@ class _KeyPair implements KeyPair { @override final T publicKey; - _KeyPair({this.privateKey, this.publicKey}); + _KeyPair({required this.privateKey, required this.publicKey}); } @sealed @@ -231,59 +231,59 @@ class TestRunner { /// True, if private is a secret key and there is no public key. final bool _isSymmetric; - final ImportKeyFn _importPrivateRawKey; - final ExportKeyFn _exportPrivateRawKey; - final ImportKeyFn _importPrivatePkcs8Key; - final ExportKeyFn _exportPrivatePkcs8Key; - final ImportJsonWebKeyKeyFn _importPrivateJsonWebKey; - final ExportJsonWebKeyKeyFn _exportPrivateJsonWebKey; + final ImportKeyFn? _importPrivateRawKey; + final ExportKeyFn? _exportPrivateRawKey; + final ImportKeyFn? _importPrivatePkcs8Key; + final ExportKeyFn? _exportPrivatePkcs8Key; + final ImportJsonWebKeyKeyFn? _importPrivateJsonWebKey; + final ExportJsonWebKeyKeyFn? _exportPrivateJsonWebKey; - final ImportKeyFn _importPublicRawKey; - final ExportKeyFn _exportPublicRawKey; - final ImportKeyFn _importPublicSpkiKey; - final ExportKeyFn _exportPublicSpkiKey; - final ImportJsonWebKeyKeyFn _importPublicJsonWebKey; - final ExportJsonWebKeyKeyFn _exportPublicJsonWebKey; + final ImportKeyFn? _importPublicRawKey; + final ExportKeyFn? _exportPublicRawKey; + final ImportKeyFn? _importPublicSpkiKey; + final ExportKeyFn? _exportPublicSpkiKey; + final ImportJsonWebKeyKeyFn? _importPublicJsonWebKey; + final ExportJsonWebKeyKeyFn? _exportPublicJsonWebKey; final GenerateKeyFn> _generateKeyPair; - final SignBytesFn _signBytes; - final SignStreamFn _signStream; - final VerifyBytesFn _verifyBytes; - final VerifyStreamFn _verifyStream; - final EncryptOrDecryptBytesFn _encryptBytes; - final EncryptOrDecryptStreamFn _encryptStream; - final EncryptOrDecryptBytesFn _decryptBytes; - final EncryptOrDecryptStreamFn _decryptStream; - final DeriveBitsFn> _deriveBits; + final SignBytesFn? _signBytes; + final SignStreamFn? _signStream; + final VerifyBytesFn? _verifyBytes; + final VerifyStreamFn? _verifyStream; + final EncryptOrDecryptBytesFn? _encryptBytes; + final EncryptOrDecryptStreamFn? _encryptStream; + final EncryptOrDecryptBytesFn? _decryptBytes; + final EncryptOrDecryptStreamFn? _decryptStream; + final DeriveBitsFn>? _deriveBits; final List> _testData; TestRunner._({ - @required bool isSymmetric, - @required this.algorithm, - ImportKeyFn importPrivateRawKey, - ExportKeyFn exportPrivateRawKey, - ImportKeyFn importPrivatePkcs8Key, - ExportKeyFn exportPrivatePkcs8Key, - ImportJsonWebKeyKeyFn importPrivateJsonWebKey, - ExportJsonWebKeyKeyFn exportPrivateJsonWebKey, - ImportKeyFn importPublicRawKey, - ExportKeyFn exportPublicRawKey, - ImportKeyFn importPublicSpkiKey, - ExportKeyFn exportPublicSpkiKey, - ImportJsonWebKeyKeyFn importPublicJsonWebKey, - ExportJsonWebKeyKeyFn exportPublicJsonWebKey, - @required GenerateKeyFn> generateKeyPair, - SignBytesFn signBytes, - SignStreamFn signStream, - VerifyBytesFn verifyBytes, - VerifyStreamFn verifyStream, - EncryptOrDecryptBytesFn encryptBytes, - EncryptOrDecryptStreamFn encryptStream, - EncryptOrDecryptBytesFn decryptBytes, - EncryptOrDecryptStreamFn decryptStream, - DeriveBitsFn> deriveBits, - Iterable> testData, + required bool isSymmetric, + required this.algorithm, + ImportKeyFn? importPrivateRawKey, + ExportKeyFn? exportPrivateRawKey, + ImportKeyFn? importPrivatePkcs8Key, + ExportKeyFn? exportPrivatePkcs8Key, + ImportJsonWebKeyKeyFn? importPrivateJsonWebKey, + ExportJsonWebKeyKeyFn? exportPrivateJsonWebKey, + ImportKeyFn? importPublicRawKey, + ExportKeyFn? exportPublicRawKey, + ImportKeyFn? importPublicSpkiKey, + ExportKeyFn? exportPublicSpkiKey, + ImportJsonWebKeyKeyFn? importPublicJsonWebKey, + ExportJsonWebKeyKeyFn? exportPublicJsonWebKey, + required GenerateKeyFn> generateKeyPair, + SignBytesFn? signBytes, + SignStreamFn? signStream, + VerifyBytesFn? verifyBytes, + VerifyStreamFn? verifyStream, + EncryptOrDecryptBytesFn? encryptBytes, + EncryptOrDecryptStreamFn? encryptStream, + EncryptOrDecryptBytesFn? decryptBytes, + EncryptOrDecryptStreamFn? decryptStream, + DeriveBitsFn>? deriveBits, + Iterable>? testData, }) : _isSymmetric = isSymmetric, _importPrivateRawKey = importPrivateRawKey, _exportPrivateRawKey = exportPrivateRawKey, @@ -307,36 +307,36 @@ class TestRunner { _decryptBytes = decryptBytes, _decryptStream = decryptStream, _deriveBits = deriveBits, - _testData = List.from(testData ?? []) { + _testData = List.from(testData ?? >[]) { _validate(); } /// Create [TestRunner] for an asymmetric primitive. static TestRunner asymmetric({ - @required String algorithm, - ImportKeyFn importPrivateRawKey, - ExportKeyFn exportPrivateRawKey, - ImportKeyFn importPrivatePkcs8Key, - ExportKeyFn exportPrivatePkcs8Key, - ImportJsonWebKeyKeyFn importPrivateJsonWebKey, - ExportJsonWebKeyKeyFn exportPrivateJsonWebKey, - ImportKeyFn importPublicRawKey, - ExportKeyFn exportPublicRawKey, - ImportKeyFn importPublicSpkiKey, - ExportKeyFn exportPublicSpkiKey, - ImportJsonWebKeyKeyFn importPublicJsonWebKey, - ExportJsonWebKeyKeyFn exportPublicJsonWebKey, - @required GenerateKeyFn> generateKeyPair, - SignBytesFn signBytes, - SignStreamFn signStream, - VerifyBytesFn verifyBytes, - VerifyStreamFn verifyStream, - EncryptOrDecryptBytesFn encryptBytes, - EncryptOrDecryptStreamFn encryptStream, - EncryptOrDecryptBytesFn decryptBytes, - EncryptOrDecryptStreamFn decryptStream, - DeriveBitsFn> deriveBits, - Iterable> testData, + required String algorithm, + ImportKeyFn? importPrivateRawKey, + ExportKeyFn? exportPrivateRawKey, + ImportKeyFn? importPrivatePkcs8Key, + ExportKeyFn? exportPrivatePkcs8Key, + ImportJsonWebKeyKeyFn? importPrivateJsonWebKey, + ExportJsonWebKeyKeyFn? exportPrivateJsonWebKey, + ImportKeyFn? importPublicRawKey, + ExportKeyFn? exportPublicRawKey, + ImportKeyFn? importPublicSpkiKey, + ExportKeyFn? exportPublicSpkiKey, + ImportJsonWebKeyKeyFn? importPublicJsonWebKey, + ExportJsonWebKeyKeyFn? exportPublicJsonWebKey, + required GenerateKeyFn> generateKeyPair, + SignBytesFn? signBytes, + SignStreamFn? signStream, + VerifyBytesFn? verifyBytes, + VerifyStreamFn? verifyStream, + EncryptOrDecryptBytesFn? encryptBytes, + EncryptOrDecryptStreamFn? encryptStream, + EncryptOrDecryptBytesFn? decryptBytes, + EncryptOrDecryptStreamFn? decryptStream, + DeriveBitsFn>? deriveBits, + Iterable>? testData, }) { return TestRunner._( isSymmetric: false, @@ -373,24 +373,24 @@ class TestRunner { /// same type. This may give rise to a few unnecessary test cases as /// import/export of public and private key static TestRunner symmetric({ - @required String algorithm, - ImportKeyFn importPrivateRawKey, - ExportKeyFn exportPrivateRawKey, - ImportKeyFn importPrivatePkcs8Key, - ExportKeyFn exportPrivatePkcs8Key, - ImportJsonWebKeyKeyFn importPrivateJsonWebKey, - ExportJsonWebKeyKeyFn exportPrivateJsonWebKey, - @required GenerateKeyFn generateKey, - SignBytesFn signBytes, - SignStreamFn signStream, - VerifyBytesFn verifyBytes, - VerifyStreamFn verifyStream, - EncryptOrDecryptBytesFn encryptBytes, - EncryptOrDecryptStreamFn encryptStream, - EncryptOrDecryptBytesFn decryptBytes, - EncryptOrDecryptStreamFn decryptStream, - DeriveBitsFn deriveBits, - Iterable> testData, + required String algorithm, + ImportKeyFn? importPrivateRawKey, + ExportKeyFn? exportPrivateRawKey, + ImportKeyFn? importPrivatePkcs8Key, + ExportKeyFn? exportPrivatePkcs8Key, + ImportJsonWebKeyKeyFn? importPrivateJsonWebKey, + ExportJsonWebKeyKeyFn? exportPrivateJsonWebKey, + required GenerateKeyFn generateKey, + SignBytesFn? signBytes, + SignStreamFn? signStream, + VerifyBytesFn? verifyBytes, + VerifyStreamFn? verifyStream, + EncryptOrDecryptBytesFn? encryptBytes, + EncryptOrDecryptStreamFn? encryptStream, + EncryptOrDecryptBytesFn? decryptBytes, + EncryptOrDecryptStreamFn? decryptStream, + DeriveBitsFn? deriveBits, + Iterable>? testData, }) { return TestRunner._( isSymmetric: true, @@ -430,9 +430,6 @@ class TestRunner { } void _validate() { - // Required operations - check(_generateKeyPair != null); - // Check that we have verify if we have sign check((_signBytes != null) == (_verifyBytes != null)); check((_signStream != null) == (_verifyStream != null)); @@ -499,11 +496,11 @@ class TestRunner { } Future> generate({ - @required Map generateKeyParams, - @required Map importKeyParams, - Map signVerifyParams, - Map encryptDecryptParams, - Map deriveParams, + required Map generateKeyParams, + required Map importKeyParams, + Map signVerifyParams = const {}, + Map encryptDecryptParams = const {}, + Map deriveParams = const {}, String plaintextTemplate = libsum, int minPlaintext = 8, int maxPlaintext = libsum.length, @@ -522,7 +519,7 @@ class TestRunner { check(privateKey != null); check(publicKey != null); - List plaintext; + List? plaintext; if (_signBytes != null || _encryptBytes != null) { log('picking plaintext'); final rng = Random.secure(); @@ -534,29 +531,32 @@ class TestRunner { )); } - List signature; - if (_signBytes != null) { + List? signature; + final signBytes = _signBytes; + if (signBytes != null) { log('creating signature'); - signature = await _signBytes( + signature = await signBytes( pair.privateKey, - plaintext, + plaintext!, signVerifyParams, ); } - List ciphertext; - if (_encryptBytes != null) { + List? ciphertext; + final encryptBytes = _encryptBytes; + if (encryptBytes != null) { log('creating ciphertext'); - ciphertext = await _encryptBytes( + ciphertext = await encryptBytes( pair.publicKey, - plaintext, + plaintext!, encryptDecryptParams, ); } - int derivedLength; - List derivedBits; - if (_deriveBits != null) { + int? derivedLength; + List? derivedBits; + final deriveBits = _deriveBits; + if (deriveBits != null) { log('picking derivedLength'); final rng = Random.secure(); derivedLength = maxDeriveLength == minDeriveLength @@ -564,14 +564,15 @@ class TestRunner { : rng.nextInt(maxDeriveLength - minDeriveLength) + minDeriveLength; log('creating derivedBits'); - derivedBits = await _deriveBits( + derivedBits = await deriveBits( pair, derivedLength, deriveParams, ); } - T optionalCall(T Function(S) fn, S v) => fn != null ? fn(v) : null; + Future optionalCall(Future Function(S)? fn, S v) async => + fn != null ? await fn(v) : null; final c = _TestCase( name, generateKeyParams: null, // omit generateKeyParams @@ -613,7 +614,7 @@ class TestRunner { /// The [test] function must be compatible with `package:test/test.dart`. void runTests({ TestFn test = test, - Iterable> testData, + Iterable>? testData, }) { testData ??= _testData; for (final data in testData) { @@ -731,12 +732,12 @@ void _runTests( // Store publicKey and privateKey for use in later tests. // If [_isSymmetric] is true, we still import the public and assign the // private key to the public key. - PublicKey publicKey; - PrivateKey privateKey; + PublicKey? publicKey; + PrivateKey? privateKey; if (c.generateKeyParams != null) { test('generateKeyPair()', () async { - final pair = await r._generateKeyPair(c.generateKeyParams); + final pair = await r._generateKeyPair(c.generateKeyParams!); check(pair.privateKey != null); check(pair.publicKey != null); publicKey = pair.publicKey; @@ -746,21 +747,21 @@ void _runTests( test('import key-pair', () async { // Get a privateKey if (c.privateRawKeyData != null) { - privateKey = await r._importPrivateRawKey( - c.privateRawKeyData, - c.importKeyParams, + privateKey = await r._importPrivateRawKey!( + c.privateRawKeyData!, + c.importKeyParams!, ); check(privateKey != null); } else if (c.privatePkcs8KeyData != null) { - privateKey = await r._importPrivatePkcs8Key( - c.privatePkcs8KeyData, - c.importKeyParams, + privateKey = await r._importPrivatePkcs8Key!( + c.privatePkcs8KeyData!, + c.importKeyParams!, ); check(privateKey != null); } else if (c.privateJsonWebKeyData != null) { - privateKey = await r._importPrivateJsonWebKey( - c.privateJsonWebKeyData, - c.importKeyParams, + privateKey = await r._importPrivateJsonWebKey!( + c.privateJsonWebKeyData!, + c.importKeyParams!, ); check(privateKey != null); } else { @@ -772,21 +773,21 @@ void _runTests( // If symmetric algorithm we just use the private key. publicKey = privateKey as PublicKey; } else if (c.publicRawKeyData != null) { - publicKey = await r._importPublicRawKey( - c.publicRawKeyData, - c.importKeyParams, + publicKey = await r._importPublicRawKey!( + c.publicRawKeyData!, + c.importKeyParams!, ); check(publicKey != null); } else if (c.publicSpkiKeyData != null) { - publicKey = await r._importPublicSpkiKey( - c.publicSpkiKeyData, - c.importKeyParams, + publicKey = await r._importPublicSpkiKey!( + c.publicSpkiKeyData!, + c.importKeyParams!, ); check(publicKey != null); } else if (c.publicJsonWebKeyData != null) { - publicKey = await r._importPublicJsonWebKey( - c.publicJsonWebKeyData, - c.importKeyParams, + publicKey = await r._importPublicJsonWebKey!( + c.publicJsonWebKeyData!, + c.importKeyParams!, ); check(publicKey != null); } else { @@ -798,28 +799,28 @@ void _runTests( //------------------------------ Create a signature for testing // Ensure that we have a signature for use in later test cases - List signature; + List? signature; if (r._signBytes != null) { if (c.signature != null) { signature = c.signature; } else { test('create signature', () async { - signature = await r._signBytes( - privateKey, - c.plaintext, - c.signVerifyParams, + signature = await r._signBytes!( + privateKey!, + c.plaintext!, + c.signVerifyParams!, ); }); } test('verify signature', () async { check( - await r._verifyBytes( - publicKey, - signature, - c.plaintext, - c.signVerifyParams, + await r._verifyBytes!( + publicKey!, + signature!, + c.plaintext!, + c.signVerifyParams!, ), 'failed to verify signature', ); @@ -827,57 +828,57 @@ void _runTests( } //------------------------------ Create a ciphertext for testing - List ciphertext; + List? ciphertext; if (r._encryptBytes != null) { if (c.ciphertext != null) { - ciphertext = c.ciphertext; + ciphertext = c.ciphertext!; } else { test('create ciphertext', () async { - ciphertext = await r._encryptBytes( - publicKey, - c.plaintext, - c.encryptDecryptParams, + ciphertext = await r._encryptBytes!( + publicKey!, + c.plaintext!, + c.encryptDecryptParams!, ); }); } test('decrypt ciphertext', () async { - final text = await r._decryptBytes( - privateKey, - ciphertext, - c.encryptDecryptParams, + final text = await r._decryptBytes!( + privateKey!, + ciphertext!, + c.encryptDecryptParams!, ); - check(equalBytes(text, c.plaintext), 'failed to decrypt ciphertext'); + check(equalBytes(text, c.plaintext!), 'failed to decrypt ciphertext'); }); } //------------------------------ Create derivedBits for testing // Ensure that we have derivedBits for use in later test cases - List derivedBits; + List? derivedBits; if (r._deriveBits != null) { if (c.derivedBits != null) { - derivedBits = c.derivedBits; + derivedBits = c.derivedBits!; } else { test('create derivedBits', () async { - derivedBits = await r._deriveBits( - _KeyPair(privateKey: privateKey, publicKey: publicKey), - c.derivedLength, - c.deriveParams, + derivedBits = await r._deriveBits!( + _KeyPair(privateKey: privateKey!, publicKey: publicKey!), + c.derivedLength!, + c.deriveParams!, ); }); } test('validated derivedBits', () async { - final derived = await r._deriveBits( - _KeyPair(privateKey: privateKey, publicKey: publicKey), - c.derivedLength, - c.deriveParams, + final derived = await r._deriveBits!( + _KeyPair(privateKey: privateKey!, publicKey: publicKey!), + c.derivedLength!, + c.deriveParams!, ); check( - equalBytes(derived, derivedBits), + equalBytes(derived, derivedBits!), 'failed to derivedBits are not consistent', ); }); @@ -886,27 +887,28 @@ void _runTests( //------------------------------ Utilities for testing //// Utility function to verify [sig] using [key]. - Future _checkVerifyBytes(PublicKey key, List sig) async { + Future _checkVerifyBytes(PublicKey key, List? sig) async { + check(sig != null, 'signature cannot be null'); check( - await r._verifyBytes(key, sig, c.plaintext, c.signVerifyParams), + await r._verifyBytes!(key, sig!, c.plaintext!, c.signVerifyParams!), 'failed to verify signature', ); check( - !await r._verifyBytes( + !await r._verifyBytes!( key, flipFirstBits(sig), - c.plaintext, - c.signVerifyParams, + c.plaintext!, + c.signVerifyParams!, ), 'verified an invalid signature', ); - if (c.plaintext.isNotEmpty) { + if (c.plaintext!.isNotEmpty) { check( - !await r._verifyBytes( + !await r._verifyBytes!( key, sig, - flipFirstBits(c.plaintext), - c.signVerifyParams, + flipFirstBits(c.plaintext!), + c.signVerifyParams!, ), 'verified an invalid message', ); @@ -915,20 +917,20 @@ void _runTests( /// Utility function to decrypt [ctext] using [key]. Future _checkDecryptBytes(PrivateKey key, List ctext) async { - final text = await r._decryptBytes(key, ctext, c.encryptDecryptParams); - check(equalBytes(text, c.plaintext), 'failed to decrypt ciphertext'); + final text = await r._decryptBytes!(key, ctext, c.encryptDecryptParams!); + check(equalBytes(text, c.plaintext!), 'failed to decrypt ciphertext'); if (ctext.isNotEmpty) { // If ciphertext is mangled some primitives like AES-GCM must throw // others may return garbled plaintext. try { - final invalidText = await r._decryptBytes( + final invalidText = await r._decryptBytes!( key, flipFirstBits(ctext), - c.encryptDecryptParams, + c.encryptDecryptParams!, ); check( - !equalBytes(invalidText, c.plaintext), + !equalBytes(invalidText, c.plaintext!), 'decrypted an invalid ciphertext', ); } on OperationError catch (e) { @@ -938,69 +940,69 @@ void _runTests( } /// Check if [signature] is sane. - Future checkSignature(List signature) async { + Future checkSignature(List? signature) async { check(signature != null, 'signature is null'); - check(signature.isNotEmpty, 'signature is empty'); - await _checkVerifyBytes(publicKey, signature); + check(signature!.isNotEmpty, 'signature is empty'); + await _checkVerifyBytes(publicKey!, signature); } /// Check if [ciphertext] is sane. - Future checkCipherText(List ctext) async { + Future checkCipherText(List? ctext) async { check(ctext != null, 'ciphtertext is null'); - check(ctext.isNotEmpty, 'ciphtertext is empty'); - await _checkDecryptBytes(privateKey, ctext); + check(ctext!.isNotEmpty, 'ciphtertext is empty'); + await _checkDecryptBytes(privateKey!, ctext); } /// Check if [derived] is correct. - void checkDerivedBits(List derived) async { + void checkDerivedBits(List? derived) async { check(derived != null, 'derivedBits is null'); - check(derived.isNotEmpty, 'derivedBits is empty'); - check(equalBytes(derived, derivedBits), 'derivedBits is not consistent'); + check(derived!.isNotEmpty, 'derivedBits is empty'); + check(equalBytes(derived, derivedBits!), 'derivedBits is not consistent'); } /// Check if [publicKey] is sane. - Future checkPublicKey(PublicKey publicKey) async { + Future checkPublicKey(PublicKey? publicKey) async { check(publicKey != null, 'publicKey is null'); if (r._signBytes != null) { - await _checkVerifyBytes(publicKey, signature); + await _checkVerifyBytes(publicKey!, signature); } if (r._encryptBytes != null) { - final ctext = await r._encryptBytes( - publicKey, - c.plaintext, - c.encryptDecryptParams, + final ctext = await r._encryptBytes!( + publicKey!, + c.plaintext!, + c.encryptDecryptParams!, ); await checkCipherText(ctext); } if (r._deriveBits != null) { - final derived = await r._deriveBits( - _KeyPair(privateKey: privateKey, publicKey: publicKey), - c.derivedLength, - c.deriveParams, + final derived = await r._deriveBits!( + _KeyPair(privateKey: privateKey!, publicKey: publicKey!), + c.derivedLength!, + c.deriveParams!, ); checkDerivedBits(derived); } } /// Check if [privateKey] is sane. - Future checkPrivateKey(PrivateKey privateKey) async { + Future checkPrivateKey(PrivateKey? privateKey) async { check(privateKey != null, 'privateKey is null'); if (r._signBytes != null) { - final sig = await r._signBytes( - privateKey, - c.plaintext, - c.signVerifyParams, + final sig = await r._signBytes!( + privateKey!, + c.plaintext!, + c.signVerifyParams!, ); await checkSignature(sig); } if (r._encryptBytes != null) { - await _checkDecryptBytes(privateKey, ciphertext); + await _checkDecryptBytes(privateKey!, ciphertext!); } if (r._deriveBits != null) { - final derived = await r._deriveBits( - _KeyPair(privateKey: privateKey, publicKey: publicKey), - c.derivedLength, - c.deriveParams, + final derived = await r._deriveBits!( + _KeyPair(privateKey: privateKey!, publicKey: publicKey!), + c.derivedLength!, + c.deriveParams!, ); checkDerivedBits(derived); } @@ -1012,9 +1014,9 @@ void _runTests( assert(!r._isSymmetric && r._importPublicRawKey != null); test('importPublicRawKey()', () async { - final key = await r._importPublicRawKey( - c.publicRawKeyData, - c.importKeyParams, + final key = await r._importPublicRawKey!( + c.publicRawKeyData!, + c.importKeyParams!, ); await checkPublicKey(key); }); @@ -1024,9 +1026,9 @@ void _runTests( assert(!r._isSymmetric && r._importPublicSpkiKey != null); test('importPublicSpkiKey()', () async { - final key = await r._importPublicSpkiKey( - c.publicSpkiKeyData, - c.importKeyParams, + final key = await r._importPublicSpkiKey!( + c.publicSpkiKeyData!, + c.importKeyParams!, ); await checkPublicKey(key); }); @@ -1036,9 +1038,9 @@ void _runTests( assert(!r._isSymmetric && r._importPublicJsonWebKey != null); test('importPublicJsonWebKey()', () async { - final key = await r._importPublicJsonWebKey( - c.publicJsonWebKeyData, - c.importKeyParams, + final key = await r._importPublicJsonWebKey!( + c.publicJsonWebKeyData!, + c.importKeyParams!, ); await checkPublicKey(key); }); @@ -1048,9 +1050,9 @@ void _runTests( if (c.privateRawKeyData != null) { test('importPrivateRawKey()', () async { - final key = await r._importPrivateRawKey( - c.privateRawKeyData, - c.importKeyParams, + final key = await r._importPrivateRawKey!( + c.privateRawKeyData!, + c.importKeyParams!, ); await checkPrivateKey(key); }); @@ -1058,9 +1060,9 @@ void _runTests( if (c.privatePkcs8KeyData != null) { test('importPrivatePkcs8Key()', () async { - final key = await r._importPrivatePkcs8Key( - c.privatePkcs8KeyData, - c.importKeyParams, + final key = await r._importPrivatePkcs8Key!( + c.privatePkcs8KeyData!, + c.importKeyParams!, ); await checkPrivateKey(key); }); @@ -1068,9 +1070,9 @@ void _runTests( if (c.privateJsonWebKeyData != null) { test('importPrivateJsonWebKey()', () async { - final key = await r._importPrivateJsonWebKey( - c.privateJsonWebKeyData, - c.importKeyParams, + final key = await r._importPrivateJsonWebKey!( + c.privateJsonWebKeyData!, + c.importKeyParams!, ); await checkPrivateKey(key); }); @@ -1080,10 +1082,10 @@ void _runTests( if (r._signBytes != null) { test('signBytes(plaintext)', () async { - final sig = await r._signBytes( - privateKey, - c.plaintext, - c.signVerifyParams, + final sig = await r._signBytes!( + privateKey!, + c.plaintext!, + c.signVerifyParams!, ); await checkSignature(sig); }); @@ -1091,19 +1093,19 @@ void _runTests( if (r._signStream != null) { test('signStream(plaintext)', () async { - final sig = await r._signStream( - privateKey, - Stream.value(c.plaintext), - c.signVerifyParams, + final sig = await r._signStream!( + privateKey!, + Stream.value(c.plaintext!), + c.signVerifyParams!, ); await checkSignature(sig); }); test('signStream(fibChunked(plaintext))', () async { - final sig = await r._signStream( - privateKey, - fibonacciChunkedStream(c.plaintext), - c.signVerifyParams, + final sig = await r._signStream!( + privateKey!, + fibonacciChunkedStream(c.plaintext!), + c.signVerifyParams!, ); await checkSignature(sig); }); @@ -1114,32 +1116,32 @@ void _runTests( if (r._verifyBytes != null) { test('verifyBytes(signature, plaintext)', () async { check( - await r._verifyBytes( - publicKey, - signature, - c.plaintext, - c.signVerifyParams, + await r._verifyBytes!( + publicKey!, + signature!, + c.plaintext!, + c.signVerifyParams!, ), 'failed to verify signature', ); check( - !await r._verifyBytes( - publicKey, - flipFirstBits(signature), - c.plaintext, - c.signVerifyParams, + !await r._verifyBytes!( + publicKey!, + flipFirstBits(signature!), + c.plaintext!, + c.signVerifyParams!, ), 'verified an invalid signature', ); - if (c.plaintext.isNotEmpty) { + if (c.plaintext!.isNotEmpty) { check( - !await r._verifyBytes( - publicKey, - signature, - flipFirstBits(c.plaintext), - c.signVerifyParams, + !await r._verifyBytes!( + publicKey!, + signature!, + flipFirstBits(c.plaintext!), + c.signVerifyParams!, ), 'verified an invalid message', ); @@ -1150,32 +1152,32 @@ void _runTests( if (r._verifyStream != null) { test('verifyStream(signature, Stream.value(plaintext))', () async { check( - await r._verifyStream( - publicKey, - signature, - Stream.value(c.plaintext), - c.signVerifyParams, + await r._verifyStream!( + publicKey!, + signature!, + Stream.value(c.plaintext!), + c.signVerifyParams!, ), 'failed to verify signature', ); check( - !await r._verifyStream( - publicKey, - flipFirstBits(signature), - Stream.value(c.plaintext), - c.signVerifyParams, + !await r._verifyStream!( + publicKey!, + flipFirstBits(signature!), + Stream.value(c.plaintext!), + c.signVerifyParams!, ), 'verified an invalid signature', ); - if (c.plaintext.isNotEmpty) { + if (c.plaintext!.isNotEmpty) { check( - !await r._verifyStream( - publicKey, - signature, - Stream.value(flipFirstBits(c.plaintext)), - c.signVerifyParams, + !await r._verifyStream!( + publicKey!, + signature!, + Stream.value(flipFirstBits(c.plaintext!)), + c.signVerifyParams!, ), 'verified an invalid message', ); @@ -1184,32 +1186,32 @@ void _runTests( test('verifyStream(signature, fibChunkedStream(plaintext))', () async { check( - await r._verifyStream( - publicKey, - signature, - fibonacciChunkedStream(c.plaintext), - c.signVerifyParams, + await r._verifyStream!( + publicKey!, + signature!, + fibonacciChunkedStream(c.plaintext!), + c.signVerifyParams!, ), 'failed to verify signature', ); check( - !await r._verifyStream( - publicKey, - flipFirstBits(signature), - fibonacciChunkedStream(c.plaintext), - c.signVerifyParams, + !await r._verifyStream!( + publicKey!, + flipFirstBits(signature!), + fibonacciChunkedStream(c.plaintext!), + c.signVerifyParams!, ), 'verified an invalid signature', ); - if (c.plaintext.isNotEmpty) { + if (c.plaintext!.isNotEmpty) { check( - !await r._verifyStream( - publicKey, - signature, - fibonacciChunkedStream(flipFirstBits(c.plaintext)), - c.signVerifyParams, + !await r._verifyStream!( + publicKey!, + signature!, + fibonacciChunkedStream(flipFirstBits(c.plaintext!)), + c.signVerifyParams!, ), 'verified an invalid message', ); @@ -1221,10 +1223,10 @@ void _runTests( if (r._encryptBytes != null) { test('encryptBytes(plaintext)', () async { - final ctext = await r._encryptBytes( - publicKey, - c.plaintext, - c.encryptDecryptParams, + final ctext = await r._encryptBytes!( + publicKey!, + c.plaintext!, + c.encryptDecryptParams!, ); await checkCipherText(ctext); }); @@ -1232,19 +1234,19 @@ void _runTests( if (r._encryptStream != null) { test('encryptStream(plaintext)', () async { - final ctext = await bufferStream(r._encryptStream( - publicKey, - Stream.value(c.plaintext), - c.encryptDecryptParams, + final ctext = await bufferStream(r._encryptStream!( + publicKey!, + Stream.value(c.plaintext!), + c.encryptDecryptParams!, )); await checkCipherText(ctext); }); test('encryptStream(fibChunked(plaintext))', () async { - final ctext = await bufferStream(r._encryptStream( - publicKey, - fibonacciChunkedStream(c.plaintext), - c.encryptDecryptParams, + final ctext = await bufferStream(r._encryptStream!( + publicKey!, + fibonacciChunkedStream(c.plaintext!), + c.encryptDecryptParams!, )); await checkCipherText(ctext); }); @@ -1254,27 +1256,27 @@ void _runTests( if (r._decryptBytes != null) { test('decryptBytes(plaintext)', () async { - final text = await r._decryptBytes( - privateKey, - ciphertext, - c.encryptDecryptParams, + final text = await r._decryptBytes!( + privateKey!, + ciphertext!, + c.encryptDecryptParams!, ); check( - equalBytes(text, c.plaintext), + equalBytes(text, c.plaintext!), 'failed to decrypt signature', ); - if (ciphertext.isNotEmpty) { + if (ciphertext!.isNotEmpty) { // If ciphertext is mangled some primitives like AES-GCM must throw // others may return garbled plaintext. try { - final text2 = await r._decryptBytes( - privateKey, - flipFirstBits(ciphertext), - c.encryptDecryptParams, + final text2 = await r._decryptBytes!( + privateKey!, + flipFirstBits(ciphertext!), + c.encryptDecryptParams!, ); check( - !equalBytes(text2, c.plaintext), + !equalBytes(text2, c.plaintext!), 'decrypted an invalid ciphertext correctly', ); } on OperationError catch (e) { @@ -1286,27 +1288,27 @@ void _runTests( if (r._decryptStream != null) { test('decryptStream(Stream.value(ciphertext))', () async { - final text = await bufferStream(r._decryptStream( - privateKey, - Stream.value(ciphertext), - c.encryptDecryptParams, + final text = await bufferStream(r._decryptStream!( + privateKey!, + Stream.value(ciphertext!), + c.encryptDecryptParams!, )); check( - equalBytes(text, c.plaintext), + equalBytes(text, c.plaintext!), 'failed to decrypt signature', ); - if (ciphertext.isNotEmpty) { + if (ciphertext!.isNotEmpty) { // If ciphertext is mangled some primitives like AES-GCM must throw // others may return garbled plaintext. try { - final text2 = await bufferStream(r._decryptStream( - privateKey, - Stream.value(flipFirstBits(ciphertext)), - c.encryptDecryptParams, + final text2 = await bufferStream(r._decryptStream!( + privateKey!, + Stream.value(flipFirstBits(ciphertext!)), + c.encryptDecryptParams!, )); check( - !equalBytes(text2, c.plaintext), + !equalBytes(text2, c.plaintext!), 'decrypted an invalid ciphertext correctly', ); } on OperationError catch (e) { @@ -1316,27 +1318,27 @@ void _runTests( }); test('decryptStream(fibChunkedStream(ciphertext))', () async { - final text = await bufferStream(r._decryptStream( - privateKey, - fibonacciChunkedStream(ciphertext), - c.encryptDecryptParams, + final text = await bufferStream(r._decryptStream!( + privateKey!, + fibonacciChunkedStream(ciphertext!), + c.encryptDecryptParams!, )); check( - equalBytes(text, c.plaintext), + equalBytes(text, c.plaintext!), 'failed to decrypt signature', ); - if (ciphertext.isNotEmpty) { + if (ciphertext!.isNotEmpty) { // If ciphertext is mangled some primitives like AES-GCM must throw // others may return garbled plaintext. try { - final text2 = await bufferStream(r._decryptStream( - privateKey, - fibonacciChunkedStream(flipFirstBits(ciphertext)), - c.encryptDecryptParams, + final text2 = await bufferStream(r._decryptStream!( + privateKey!, + fibonacciChunkedStream(flipFirstBits(ciphertext!)), + c.encryptDecryptParams!, )); check( - !equalBytes(text2, c.plaintext), + !equalBytes(text2, c.plaintext!), 'decrypted an invalid ciphertext correctly', ); } on OperationError catch (e) { @@ -1349,10 +1351,10 @@ void _runTests( //------------------------------ Test derivedBits if (r._deriveBits != null) { test('deriveBits', () async { - final derived = await r._deriveBits( - _KeyPair(privateKey: privateKey, publicKey: publicKey), - c.derivedLength, - c.deriveParams, + final derived = await r._deriveBits!( + _KeyPair(privateKey: privateKey!, publicKey: publicKey!), + c.derivedLength!, + c.deriveParams!, ); checkDerivedBits(derived); }); @@ -1361,33 +1363,30 @@ void _runTests( //------------------------------ export/import private key if (r._exportPrivateRawKey != null) { test('export/import raw private key', () async { - final keyData = await r._exportPrivateRawKey(privateKey); - check(keyData != null, 'exported key is null'); + final keyData = await r._exportPrivateRawKey!(privateKey!); check(keyData.isNotEmpty, 'exported key is empty'); - final key = await r._importPrivateRawKey(keyData, c.importKeyParams); + final key = await r._importPrivateRawKey!(keyData, c.importKeyParams!); await checkPrivateKey(key); }); } if (r._exportPrivatePkcs8Key != null) { test('export/import pkcs8 private key', () async { - final keyData = await r._exportPrivatePkcs8Key(privateKey); - check(keyData != null, 'exported key is null'); + final keyData = await r._exportPrivatePkcs8Key!(privateKey!); check(keyData.isNotEmpty, 'exported key is empty'); - final key = await r._importPrivatePkcs8Key(keyData, c.importKeyParams); + final key = await r._importPrivatePkcs8Key!(keyData, c.importKeyParams!); await checkPrivateKey(key); }); } if (r._exportPrivateJsonWebKey != null) { test('export/import jwk private key', () async { - final jwk = await r._exportPrivateJsonWebKey(privateKey); - check(jwk != null, 'exported key is null'); + final jwk = await r._exportPrivateJsonWebKey!(privateKey!); check(jwk.isNotEmpty, 'exported key is empty'); - final key = await r._importPrivateJsonWebKey(jwk, c.importKeyParams); + final key = await r._importPrivateJsonWebKey!(jwk, c.importKeyParams!); await checkPrivateKey(key); }); } @@ -1398,11 +1397,10 @@ void _runTests( assert(!r._isSymmetric && r._importPublicRawKey != null); test('export/import raw public key', () async { - final keyData = await r._exportPublicRawKey(publicKey); - check(keyData != null, 'exported key is null'); + final keyData = await r._exportPublicRawKey!(publicKey!); check(keyData.isNotEmpty, 'exported key is empty'); - final key = await r._importPublicRawKey(keyData, c.importKeyParams); + final key = await r._importPublicRawKey!(keyData, c.importKeyParams!); await checkPublicKey(key); }); } @@ -1411,11 +1409,10 @@ void _runTests( assert(!r._isSymmetric && r._importPublicSpkiKey != null); test('export/import pkcs8 public key', () async { - final keyData = await r._exportPublicSpkiKey(publicKey); - check(keyData != null, 'exported key is null'); + final keyData = await r._exportPublicSpkiKey!(publicKey!); check(keyData.isNotEmpty, 'exported key is empty'); - final key = await r._importPublicSpkiKey(keyData, c.importKeyParams); + final key = await r._importPublicSpkiKey!(keyData, c.importKeyParams!); await checkPublicKey(key); }); } @@ -1424,11 +1421,10 @@ void _runTests( assert(!r._isSymmetric && r._importPublicJsonWebKey != null); test('export/import jwk public key', () async { - final jwk = await r._exportPublicJsonWebKey(publicKey); - check(jwk != null, 'exported key is null'); + final jwk = await r._exportPublicJsonWebKey!(publicKey!); check(jwk.isNotEmpty, 'exported key is empty'); - final key = await r._importPublicJsonWebKey(jwk, c.importKeyParams); + final key = await r._importPublicJsonWebKey!(jwk, c.importKeyParams!); await checkPublicKey(key); }); } @@ -1437,23 +1433,24 @@ void _runTests( if (c.generateKeyParams != null) { test('validate generated test case', () async { - T optionalCall(T Function(S) fn, S v) => fn != null ? fn(v) : null; + Future optionalCall(Future Function(S)? fn, S v) async => + fn != null ? await fn(v) : null; final date = DateTime.now().toIso8601String().split('T').first; // drop time final generated = _TestCase( '${c.name} generated on $detectedRuntime at $date', generateKeyParams: null, // omit generateKeyParams privateRawKeyData: - await optionalCall(r._exportPrivateRawKey, privateKey), + await optionalCall(r._exportPrivateRawKey, privateKey!), privatePkcs8KeyData: - await optionalCall(r._exportPrivatePkcs8Key, privateKey), + await optionalCall(r._exportPrivatePkcs8Key, privateKey!), privateJsonWebKeyData: - await optionalCall(r._exportPrivateJsonWebKey, privateKey), - publicRawKeyData: await optionalCall(r._exportPublicRawKey, publicKey), + await optionalCall(r._exportPrivateJsonWebKey, privateKey!), + publicRawKeyData: await optionalCall(r._exportPublicRawKey, publicKey!), publicSpkiKeyData: - await optionalCall(r._exportPublicSpkiKey, publicKey), + await optionalCall(r._exportPublicSpkiKey, publicKey!), publicJsonWebKeyData: - await optionalCall(r._exportPublicJsonWebKey, publicKey), + await optionalCall(r._exportPublicJsonWebKey, publicKey!), plaintext: c.plaintext, signature: signature, ciphertext: ciphertext, diff --git a/test/utils/utils.dart b/test/utils/utils.dart index 1720d6d1..350d72bb 100644 --- a/test/utils/utils.dart +++ b/test/utils/utils.dart @@ -76,8 +76,7 @@ Hash hashFromJson(dynamic json) { if (json == 'sha-512') { return Hash.sha512; } - check(false, 'invalid hash specification'); - return null; // unreachable + throw AssertionError('invalid hash specification'); } String hashToJson(Hash h) { @@ -93,8 +92,7 @@ String hashToJson(Hash h) { if (h == Hash.sha512) { return 'sha-512'; } - check(false, 'invalid hash implementation'); - return null; // unreachable + throw AssertionError('invalid hash implementation'); } String curveToJson(EllipticCurve curve) { @@ -107,8 +105,7 @@ String curveToJson(EllipticCurve curve) { if (curve == EllipticCurve.p521) { return 'p-521'; } - check(false, 'invalid curve implementation'); - return null; // unreachable + throw AssertionError('invalid curve implementation'); } EllipticCurve curveFromJson(dynamic json) { @@ -124,18 +121,17 @@ EllipticCurve curveFromJson(dynamic json) { if (json == 'p-521') { return EllipticCurve.p521; } - check(false, 'invalid curve specification'); - return null; // unreachable + throw AssertionError('invalid curve specification'); } -List bytesFromJson(Map json, String key) { +List? bytesFromJson(Map json, String key) { if (json[key] != null) { return base64Decode(json[key]); } return null; } -String bytesToJson(List bytes) { +String? bytesToJson(List? bytes) { if (bytes != null) { return base64Encode(bytes); } diff --git a/test/webcrypto/aescbc_test.dart b/test/webcrypto/aescbc_test.dart index 41c28457..9b6c94f0 100644 --- a/test/webcrypto/aescbc_test.dart +++ b/test/webcrypto/aescbc_test.dart @@ -31,13 +31,13 @@ final runner = TestRunner.symmetric( generateKeyPairParams['length'], ), encryptBytes: (key, data, encryptParams) => - key.encryptBytes(data, bytesFromJson(encryptParams, 'iv')), + key.encryptBytes(data, bytesFromJson(encryptParams, 'iv')!), encryptStream: (key, data, encryptParams) => - key.encryptStream(data, bytesFromJson(encryptParams, 'iv')), + key.encryptStream(data, bytesFromJson(encryptParams, 'iv')!), decryptBytes: (key, data, decryptParams) => - key.decryptBytes(data, bytesFromJson(decryptParams, 'iv')), + key.decryptBytes(data, bytesFromJson(decryptParams, 'iv')!), decryptStream: (key, data, decryptParams) => - key.decryptStream(data, bytesFromJson(decryptParams, 'iv')), + key.decryptStream(data, bytesFromJson(decryptParams, 'iv')!), testData: _testData, ); diff --git a/test/webcrypto/aesctr_test.dart b/test/webcrypto/aesctr_test.dart index 522d7c93..6a7915f9 100644 --- a/test/webcrypto/aesctr_test.dart +++ b/test/webcrypto/aesctr_test.dart @@ -35,22 +35,22 @@ final runner = TestRunner.symmetric( ), encryptBytes: (key, data, encryptParams) => key.encryptBytes( data, - bytesFromJson(encryptParams, 'counter'), + bytesFromJson(encryptParams, 'counter')!, encryptParams['length'], ), encryptStream: (key, data, encryptParams) => key.encryptStream( data, - bytesFromJson(encryptParams, 'counter'), + bytesFromJson(encryptParams, 'counter')!, encryptParams['length'], ), decryptBytes: (key, data, decryptParams) => key.decryptBytes( data, - bytesFromJson(decryptParams, 'counter'), + bytesFromJson(decryptParams, 'counter')!, decryptParams['length'], ), decryptStream: (key, data, decryptParams) => key.decryptStream( data, - bytesFromJson(decryptParams, 'counter'), + bytesFromJson(decryptParams, 'counter')!, decryptParams['length'], ), testData: _testData, diff --git a/test/webcrypto/aesgcm_test.dart b/test/webcrypto/aesgcm_test.dart index 49aa1560..dd6d91c0 100644 --- a/test/webcrypto/aesgcm_test.dart +++ b/test/webcrypto/aesgcm_test.dart @@ -32,16 +32,16 @@ final runner = TestRunner.symmetric( ), encryptBytes: (key, data, encryptParams) => key.encryptBytes( data, - bytesFromJson(encryptParams, 'iv'), + bytesFromJson(encryptParams, 'iv')!, additionalData: bytesFromJson(encryptParams, 'additionalData'), - tagLength: encryptParams['length'], + tagLength: encryptParams['length'] ?? 128, ), encryptStream: null, // not supported decryptBytes: (key, data, decryptParams) => key.decryptBytes( data, - bytesFromJson(decryptParams, 'iv'), + bytesFromJson(decryptParams, 'iv')!, additionalData: bytesFromJson(decryptParams, 'additionalData'), - tagLength: decryptParams['length'], + tagLength: decryptParams['length'] ?? 128, ), decryptStream: null, // not supported testData: _testData, diff --git a/test/webcrypto/ecdh_test.dart b/test/webcrypto/ecdh_test.dart index d593d2d1..02be1f54 100644 --- a/test/webcrypto/ecdh_test.dart +++ b/test/webcrypto/ecdh_test.dart @@ -25,7 +25,7 @@ class _KeyPair implements KeyPair { @override final T publicKey; - _KeyPair({this.privateKey, this.publicKey}); + _KeyPair({required this.privateKey, required this.publicKey}); } final runner = TestRunner.asymmetric( diff --git a/test/webcrypto/hkdf_test.dart b/test/webcrypto/hkdf_test.dart index 15939df7..eec1101f 100644 --- a/test/webcrypto/hkdf_test.dart +++ b/test/webcrypto/hkdf_test.dart @@ -52,8 +52,8 @@ final runner = TestRunner.symmetric<_ExportableHkdfSecretKey>( deriveBits: (key, length, deriveParams) => key.hkdfSecretKey.deriveBits( length, hashFromJson(deriveParams), - bytesFromJson(deriveParams, 'salt'), - bytesFromJson(deriveParams, 'info'), + bytesFromJson(deriveParams, 'salt')!, + bytesFromJson(deriveParams, 'info')!, ), testData: _testData, ); diff --git a/test/webcrypto/pbkdf2_test.dart b/test/webcrypto/pbkdf2_test.dart index 323ff5de..f3e96a7b 100644 --- a/test/webcrypto/pbkdf2_test.dart +++ b/test/webcrypto/pbkdf2_test.dart @@ -52,7 +52,7 @@ final runner = TestRunner.symmetric<_ExportablePbkdf2SecretKey>( deriveBits: (key, length, deriveParams) => key.pbkdf2SecretKey.deriveBits( length, hashFromJson(deriveParams), - bytesFromJson(deriveParams, 'salt'), + bytesFromJson(deriveParams, 'salt')!, deriveParams['iterations'], ), testData: _testData,