diff --git a/example/webcrypto/hmac/import_raw_key.dart b/example/webcrypto/hmac/import_raw_key.dart new file mode 100644 index 00000000..9b2f3a64 --- /dev/null +++ b/example/webcrypto/hmac/import_raw_key.dart @@ -0,0 +1,31 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// ignore_for_file: unused_local_variable + +// #region example +import 'dart:convert' show base64; + +import 'package:webcrypto/webcrypto.dart'; + +Future main() async { + final key = await HmacSecretKey.importRawKey( + base64.decode( + 'WzIxLDg0LDEwMCw5OSwxMCwxMDUsMjIsODAsMTkwLDExNiwyMDMsMjQ5XQ==', + ), + Hash.sha256, + ); +} + +// #endregion diff --git a/lib/src/boringssl/lookup/utils.dart b/lib/src/boringssl/lookup/utils.dart index 899929b7..87371925 100644 --- a/lib/src/boringssl/lookup/utils.dart +++ b/lib/src/boringssl/lookup/utils.dart @@ -29,10 +29,12 @@ String get libraryFileName { if (Platform.isMacOS) { return 'lib$libraryName.dylib'; } + // coverage:ignore-start throw UnsupportedError( 'Platform ${Platform.operatingSystem} is unsupported or embed ' 'the binary webcrypto library for package:webcrypto', ); + // coverage:ignore-end } /// Look for the webcrypto binary library in the `.dart_tool/webcrypto/` folder. diff --git a/lib/src/impl_ffi/impl_ffi.aes_common.dart b/lib/src/impl_ffi/impl_ffi.aes_common.dart index 219267f7..3d22f600 100644 --- a/lib/src/impl_ffi/impl_ffi.aes_common.dart +++ b/lib/src/impl_ffi/impl_ffi.aes_common.dart @@ -18,7 +18,9 @@ Uint8List _aesImportRawKey(List keyData) { 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. + // coverage:ignore-start throw UnsupportedError('192-bit AES keys are not supported'); + // coverage:ignore-end } if (keyData.length != 16 && keyData.length != 32) { throw const FormatException('keyData for AES must be 128 or 256 bits'); @@ -43,7 +45,9 @@ Uint8List _aesImportJwkKey( 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. + // coverage:ignore-start throw UnsupportedError('192-bit AES keys are not supported'); + // coverage:ignore-end } checkJwk( keyData.length == 16 || keyData.length == 32, @@ -82,7 +86,9 @@ Uint8List _aesGenerateKey(int length) { if (length == 192) { // 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. + // coverage:ignore-start throw UnsupportedError('192-bit AES keys are not supported'); + // coverage:ignore-end } if (length != 128 && length != 256) { throw const FormatException('keyData for AES must be 128 or 256 bits'); diff --git a/lib/src/impl_ffi/impl_ffi.digest.dart b/lib/src/impl_ffi/impl_ffi.digest.dart index c24f25c1..9d9cd288 100644 --- a/lib/src/impl_ffi/impl_ffi.digest.dart +++ b/lib/src/impl_ffi/impl_ffi.digest.dart @@ -74,7 +74,7 @@ abstract class _HashImpl implements HashImpl { /// Algorithm (`alg` for JWK) when this hash algorithm is used in RSA-OAEP. /// - /// For SHA-1, it returns 'RSA-OAEP-1'. + /// For SHA-1, it returns 'RSA-OAEP'. /// For SHA-256, it returns 'RSA-OAEP-256'. /// For SHA-384, it returns 'RSA-OAEP-384'. /// For SHA-512, it returns 'RSA-OAEP-512'. @@ -113,7 +113,7 @@ final class _Sha1 extends _HashImpl { String get hmacJwkAlg => 'HS1'; @override - String get rsaOaepJwkAlg => 'RSA-OAEP-1'; + String get rsaOaepJwkAlg => 'RSA-OAEP'; @override String get rsaPssJwkAlg => 'PS1'; diff --git a/lib/src/impl_ffi/impl_ffi.ec_common.dart b/lib/src/impl_ffi/impl_ffi.ec_common.dart index 5e9652b4..a71474b3 100644 --- a/lib/src/impl_ffi/impl_ffi.ec_common.dart +++ b/lib/src/impl_ffi/impl_ffi.ec_common.dart @@ -26,7 +26,9 @@ int _ecCurveToNID(EllipticCurve curve) { return NID_secp521r1; } // This should never happen! + // coverage:ignore-start throw UnsupportedError('curve "$curve" is not supported'); + // coverage:ignore-end } /// Get [EllipticCurve] from matching BoringSSL `ssl.NID_...`. @@ -41,7 +43,9 @@ EllipticCurve _ecCurveFromNID(int nid) { return EllipticCurve.p521; } // This should never happen! + // coverage:ignore-start throw operationError('internal error detecting curve'); + // coverage:ignore-end } String _ecCurveToJwkCrv(EllipticCurve curve) { @@ -55,7 +59,9 @@ String _ecCurveToJwkCrv(EllipticCurve curve) { return 'P-521'; } // This should never happen! + // coverage:ignore-start throw UnsupportedError('curve "$curve" is not supported'); + // coverage:ignore-end } /// Perform some post-import validation for EC keys. diff --git a/lib/src/impl_ffi/impl_ffi.ecdh.dart b/lib/src/impl_ffi/impl_ffi.ecdh.dart index 06cbf53c..f7a3c0ea 100644 --- a/lib/src/impl_ffi/impl_ffi.ecdh.dart +++ b/lib/src/impl_ffi/impl_ffi.ecdh.dart @@ -111,8 +111,8 @@ final class _EcdhPrivateKeyImpl implements EcdhPrivateKeyImpl { 'custom implementations of EcdhPublicKey is not supported', ); } - if (length <= 0) { - throw ArgumentError.value(length, 'length', 'must be positive'); + if (length < 0) { + throw ArgumentError.value(length, 'length', 'must be non-negative'); } return _Scope.async((scope) async { diff --git a/lib/src/impl_ffi/impl_ffi.ecdsa.dart b/lib/src/impl_ffi/impl_ffi.ecdsa.dart index ba399eb6..f3c56023 100644 --- a/lib/src/impl_ffi/impl_ffi.ecdsa.dart +++ b/lib/src/impl_ffi/impl_ffi.ecdsa.dart @@ -29,7 +29,9 @@ String _ecdsaCurveToJwkAlg(EllipticCurve curve) { return 'ES512'; } // This should never happen! + // coverage:ignore-start throw UnsupportedError('curve "$curve" is not supported'); + // coverage:ignore-end } Future ecdsaPrivateKey_importPkcs8Key( diff --git a/lib/src/impl_ffi/impl_ffi.pbkdf2.dart b/lib/src/impl_ffi/impl_ffi.pbkdf2.dart index 0046cfc0..25dd5393 100644 --- a/lib/src/impl_ffi/impl_ffi.pbkdf2.dart +++ b/lib/src/impl_ffi/impl_ffi.pbkdf2.dart @@ -60,16 +60,14 @@ final class _Pbkdf2SecretKeyImpl implements Pbkdf2SecretKeyImpl { 'The length for PBKDF2 must be a multiple of 8 bits', ); } - if (length == 0) { - throw operationError( - 'A length of zero is not allowed Pbkdf2SecretKey.deriveBits', - ); - } if (iterations <= 0) { throw operationError( 'Iterations <= 0 is not allowed for Pbkdf2SecretKey.deriveBits', ); } + if (length == 0) { + return Uint8List(0); + } final lengthInBytes = length ~/ 8; diff --git a/lib/src/impl_js/impl_js.utils.dart b/lib/src/impl_js/impl_js.utils.dart index 81e231d2..53ca306b 100644 --- a/lib/src/impl_js/impl_js.utils.dart +++ b/lib/src/impl_js/impl_js.utils.dart @@ -45,8 +45,10 @@ String _curveToName(EllipticCurve curve) { return 'P-521'; } // This should never happen. + // coverage:ignore-start // ignore: dead_code throw AssertionError('Unknown curve "$curve"'); + // coverage:ignore-end } Object _translateDomException( diff --git a/lib/src/impl_stub/impl_stub.aescbc.dart b/lib/src/impl_stub/impl_stub.aescbc.dart index a6bf3634..d27b2755 100644 --- a/lib/src/impl_stub/impl_stub.aescbc.dart +++ b/lib/src/impl_stub/impl_stub.aescbc.dart @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// coverage:ignore-file part of 'impl_stub.dart'; final class _StaticAesCbcSecretKeyImpl implements StaticAesCbcSecretKeyImpl { diff --git a/lib/src/impl_stub/impl_stub.aesctr.dart b/lib/src/impl_stub/impl_stub.aesctr.dart index 1a7e9f42..a4f0b290 100644 --- a/lib/src/impl_stub/impl_stub.aesctr.dart +++ b/lib/src/impl_stub/impl_stub.aesctr.dart @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// coverage:ignore-file part of 'impl_stub.dart'; final class _StaticAesCtrSecretKeyImpl implements StaticAesCtrSecretKeyImpl { diff --git a/lib/src/impl_stub/impl_stub.aesgcm.dart b/lib/src/impl_stub/impl_stub.aesgcm.dart index 58dee35f..ab7f48b1 100644 --- a/lib/src/impl_stub/impl_stub.aesgcm.dart +++ b/lib/src/impl_stub/impl_stub.aesgcm.dart @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// coverage:ignore-file part of 'impl_stub.dart'; final class _StaticAesGcmSecretKeyImpl implements StaticAesGcmSecretKeyImpl { diff --git a/lib/src/impl_stub/impl_stub.dart b/lib/src/impl_stub/impl_stub.dart index c8411eef..52680a08 100644 --- a/lib/src/impl_stub/impl_stub.dart +++ b/lib/src/impl_stub/impl_stub.dart @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// coverage:ignore-file library; import 'dart:typed_data'; diff --git a/lib/src/impl_stub/impl_stub.digest.dart b/lib/src/impl_stub/impl_stub.digest.dart index 6404d227..a33b6a43 100644 --- a/lib/src/impl_stub/impl_stub.digest.dart +++ b/lib/src/impl_stub/impl_stub.digest.dart @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// coverage:ignore-file part of 'impl_stub.dart'; final class _HashImpl implements HashImpl { diff --git a/lib/src/impl_stub/impl_stub.ecdh.dart b/lib/src/impl_stub/impl_stub.ecdh.dart index 4c79a082..b9423388 100644 --- a/lib/src/impl_stub/impl_stub.ecdh.dart +++ b/lib/src/impl_stub/impl_stub.ecdh.dart @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// coverage:ignore-file part of 'impl_stub.dart'; final class _StaticEcdhPrivateKeyImpl implements StaticEcdhPrivateKeyImpl { diff --git a/lib/src/impl_stub/impl_stub.ecdsa.dart b/lib/src/impl_stub/impl_stub.ecdsa.dart index 34f43a3d..5784badf 100644 --- a/lib/src/impl_stub/impl_stub.ecdsa.dart +++ b/lib/src/impl_stub/impl_stub.ecdsa.dart @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// coverage:ignore-file part of 'impl_stub.dart'; final class _StaticEcdsaPrivateKeyImpl implements StaticEcdsaPrivateKeyImpl { diff --git a/lib/src/impl_stub/impl_stub.hkdf.dart b/lib/src/impl_stub/impl_stub.hkdf.dart index 1e07cead..20b863cd 100644 --- a/lib/src/impl_stub/impl_stub.hkdf.dart +++ b/lib/src/impl_stub/impl_stub.hkdf.dart @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// coverage:ignore-file part of 'impl_stub.dart'; final class _StaticHkdfSecretKeyImpl implements StaticHkdfSecretKeyImpl { diff --git a/lib/src/impl_stub/impl_stub.hmac.dart b/lib/src/impl_stub/impl_stub.hmac.dart index b39310a7..e4eef5e2 100644 --- a/lib/src/impl_stub/impl_stub.hmac.dart +++ b/lib/src/impl_stub/impl_stub.hmac.dart @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// coverage:ignore-file part of 'impl_stub.dart'; final class _StaticHmacSecretKeyImpl implements StaticHmacSecretKeyImpl { diff --git a/lib/src/impl_stub/impl_stub.pbkdf2.dart b/lib/src/impl_stub/impl_stub.pbkdf2.dart index e1fc586f..03ddcd99 100644 --- a/lib/src/impl_stub/impl_stub.pbkdf2.dart +++ b/lib/src/impl_stub/impl_stub.pbkdf2.dart @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// coverage:ignore-file part of 'impl_stub.dart'; final class _StaticPbkdf2SecretKeyImpl implements StaticPbkdf2SecretKeyImpl { diff --git a/lib/src/impl_stub/impl_stub.random.dart b/lib/src/impl_stub/impl_stub.random.dart index c7f13fa8..f094a212 100644 --- a/lib/src/impl_stub/impl_stub.random.dart +++ b/lib/src/impl_stub/impl_stub.random.dart @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// coverage:ignore-file part of 'impl_stub.dart'; final class _RandomImpl implements RandomImpl { diff --git a/lib/src/impl_stub/impl_stub.rsaoaep.dart b/lib/src/impl_stub/impl_stub.rsaoaep.dart index a3daef97..c37d8f42 100644 --- a/lib/src/impl_stub/impl_stub.rsaoaep.dart +++ b/lib/src/impl_stub/impl_stub.rsaoaep.dart @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// coverage:ignore-file part of 'impl_stub.dart'; final class _StaticRsaOaepPrivateKeyImpl diff --git a/lib/src/impl_stub/impl_stub.rsapss.dart b/lib/src/impl_stub/impl_stub.rsapss.dart index ac8fbd43..78d30690 100644 --- a/lib/src/impl_stub/impl_stub.rsapss.dart +++ b/lib/src/impl_stub/impl_stub.rsapss.dart @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// coverage:ignore-file part of 'impl_stub.dart'; final class _StaticRsaPssPrivateKeyImpl implements StaticRsaPssPrivateKeyImpl { diff --git a/lib/src/impl_stub/impl_stub.rsassapkcs1v15.dart b/lib/src/impl_stub/impl_stub.rsassapkcs1v15.dart index 558e51d9..02e66068 100644 --- a/lib/src/impl_stub/impl_stub.rsassapkcs1v15.dart +++ b/lib/src/impl_stub/impl_stub.rsassapkcs1v15.dart @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// coverage:ignore-file part of 'impl_stub.dart'; final class _StaticRsaSsaPkcs1V15PrivateKeyImpl diff --git a/lib/src/testing/regression/derive_bits_zero_length.dart b/lib/src/testing/regression/derive_bits_zero_length.dart new file mode 100644 index 00000000..2da6d313 --- /dev/null +++ b/lib/src/testing/regression/derive_bits_zero_length.dart @@ -0,0 +1,42 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:typed_data'; + +import 'package:webcrypto/webcrypto.dart'; +import '../utils/utils.dart'; + +List<({String name, Future Function() test})> tests() => [ + ( + name: 'ECDH derives an empty secret when length is zero', + test: () async { + final alice = await EcdhPrivateKey.generateKey(EllipticCurve.p256); + final bob = await EcdhPrivateKey.generateKey(EllipticCurve.p256); + + final secret = await alice.privateKey.deriveBits(0, bob.publicKey); + + check(secret.isEmpty, 'Expected an empty ECDH secret'); + }, + ), + ( + name: 'PBKDF2 derives an empty secret when length is zero', + test: () async { + final key = await Pbkdf2SecretKey.importRawKey(Uint8List(16)); + + final secret = await key.deriveBits(0, Hash.sha256, Uint8List(16), 1); + + check(secret.isEmpty, 'Expected an empty PBKDF2 secret'); + }, + ), +]; diff --git a/lib/src/testing/regression/rsa_oaep_sha1_jwk_alg.dart b/lib/src/testing/regression/rsa_oaep_sha1_jwk_alg.dart new file mode 100644 index 00000000..8c902325 --- /dev/null +++ b/lib/src/testing/regression/rsa_oaep_sha1_jwk_alg.dart @@ -0,0 +1,107 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:typed_data'; + +import 'package:webcrypto/webcrypto.dart'; + +import '../utils/utils.dart'; + +List<({String name, Future Function() test})> tests() => [ + ( + name: 'RSA-OAEP SHA-1 uses the canonical JWK alg identifier', + test: () async { + final keyPair = await RsaOaepPrivateKey.generateKey( + 2048, + BigInt.from(65537), + Hash.sha1, + ); + final privateJwk = await keyPair.privateKey.exportJsonWebKey(); + final publicJwk = await keyPair.publicKey.exportJsonWebKey(); + + check( + privateJwk['alg'] == 'RSA-OAEP', + 'Expected private JWK alg to be "RSA-OAEP"', + ); + check( + publicJwk['alg'] == 'RSA-OAEP', + 'Expected public JWK alg to be "RSA-OAEP"', + ); + + final privateKey = await RsaOaepPrivateKey.importJsonWebKey( + privateJwk, + Hash.sha1, + ); + final publicKey = await RsaOaepPublicKey.importJsonWebKey( + publicJwk, + Hash.sha1, + ); + await _checkRoundTrip(privateKey, publicKey); + + final legacyPrivateKey = await RsaOaepPrivateKey.importJsonWebKey({ + ...privateJwk, + 'alg': 'RSA-OAEP-1', + }, Hash.sha1); + final legacyPublicKey = await RsaOaepPublicKey.importJsonWebKey({ + ...publicJwk, + 'alg': 'RSA-OAEP-1', + }, Hash.sha1); + await _checkRoundTrip(legacyPrivateKey, legacyPublicKey); + + for (final (:hash, :name) in [ + (hash: Hash.sha256, name: 'SHA-256'), + (hash: Hash.sha384, name: 'SHA-384'), + (hash: Hash.sha512, name: 'SHA-512'), + ]) { + await _expectFormatException( + () => RsaOaepPrivateKey.importJsonWebKey({ + ...privateJwk, + 'alg': 'RSA-OAEP-1', + }, hash), + 'Expected private RSA-OAEP-1 JWK to be rejected for $name', + ); + await _expectFormatException( + () => RsaOaepPublicKey.importJsonWebKey({ + ...publicJwk, + 'alg': 'RSA-OAEP-1', + }, hash), + 'Expected public RSA-OAEP-1 JWK to be rejected for $name', + ); + } + }, + ), +]; + +Future _checkRoundTrip( + RsaOaepPrivateKey privateKey, + RsaOaepPublicKey publicKey, +) async { + final plaintext = Uint8List.fromList([1, 2, 3, 4]); + final ciphertext = await publicKey.encryptBytes(plaintext); + final decrypted = await privateKey.decryptBytes(ciphertext); + check(equalBytes(decrypted, plaintext), 'RSA-OAEP round trip failed'); +} + +Future _expectFormatException( + Future Function() callback, + String message, +) async { + var rejected = false; + try { + await callback(); + } on FormatException { + rejected = true; + } + check(rejected, message); +} diff --git a/lib/src/testing/testing.dart b/lib/src/testing/testing.dart index 3c7b4fd2..21db1cb5 100644 --- a/lib/src/testing/testing.dart +++ b/lib/src/testing/testing.dart @@ -30,7 +30,9 @@ import 'webcrypto/rsassapkcs1v15.dart' as rsassapkcs1v15; // Other test files, that don't use TestRunner import 'webcrypto/random.dart' as random; import 'webcrypto/digest.dart' as digest; +import 'regression/derive_bits_zero_length.dart' as derive_bits_zero_length; import 'regression/issue_60_trailing_bytes.dart' as issue_60_trailing_bytes; +import 'regression/rsa_oaep_sha1_jwk_alg.dart' as rsa_oaep_sha1_jwk_alg; /// Test runners from all test files except `digest.dart` and /// `random.dart`, which do not use [TestRunner]. @@ -60,6 +62,8 @@ void runAllTests( ...random.tests(), ...digest.tests(), ...issue_60_trailing_bytes.tests(), + ...derive_bits_zero_length.tests(), + ...rsa_oaep_sha1_jwk_alg.tests(), ]; for (final (:name, :test) in allTests) { diff --git a/lib/src/testing/webcrypto/ecdh.dart b/lib/src/testing/webcrypto/ecdh.dart index d7c99cb3..d8202aa0 100644 --- a/lib/src/testing/webcrypto/ecdh.dart +++ b/lib/src/testing/webcrypto/ecdh.dart @@ -59,13 +59,32 @@ final runner = TestRunner.asymmetric( ); void main() async { - log('generate ECDH test case'); + log('generate ECDH test cases'); + + // P-256: up to 256 bits (32 bytes) await runner.generate( generateKeyParams: {'curve': curveToJson(EllipticCurve.p256)}, importKeyParams: {'curve': curveToJson(EllipticCurve.p256)}, deriveParams: {}, maxDeriveLength: 32, ); + + // P-384: up to 384 bits (48 bytes) + await runner.generate( + generateKeyParams: {'curve': curveToJson(EllipticCurve.p384)}, + importKeyParams: {'curve': curveToJson(EllipticCurve.p384)}, + deriveParams: {}, + maxDeriveLength: 48, + ); + + // P-521: up to 528 bits (66 bytes) + await runner.generate( + generateKeyParams: {'curve': curveToJson(EllipticCurve.p521)}, + importKeyParams: {'curve': curveToJson(EllipticCurve.p521)}, + deriveParams: {}, + maxDeriveLength: 66, + ); + log('--------------------'); await runner.tests().runTests(); diff --git a/lib/src/webcrypto/webcrypto.aescbc.dart b/lib/src/webcrypto/webcrypto.aescbc.dart index 14a86ac5..b227a00b 100644 --- a/lib/src/webcrypto/webcrypto.aescbc.dart +++ b/lib/src/webcrypto/webcrypto.aescbc.dart @@ -88,7 +88,7 @@ final class AesCbcSecretKey { /// {@macro AES:no-support-for-AES-192} /// /// If specified the `"use"` property of the imported [jwk] must be - /// `"use": "sig"`. + /// `"use": "enc"`. /// /// {@macro importJsonWebKey:throws-FormatException-if-jwk} /// diff --git a/lib/src/webcrypto/webcrypto.aesctr.dart b/lib/src/webcrypto/webcrypto.aesctr.dart index dc05c3c0..0c152d62 100644 --- a/lib/src/webcrypto/webcrypto.aesctr.dart +++ b/lib/src/webcrypto/webcrypto.aesctr.dart @@ -85,7 +85,7 @@ final class AesCtrSecretKey { /// {@macro AES:no-support-for-AES-192} /// /// If specified the `"use"` property of the imported [jwk] must be - /// `"use": "sig"`. + /// `"use": "enc"`. /// /// {@macro importJsonWebKey:throws-FormatException-if-jwk} /// diff --git a/lib/src/webcrypto/webcrypto.aesgcm.dart b/lib/src/webcrypto/webcrypto.aesgcm.dart index fe32eb45..13066290 100644 --- a/lib/src/webcrypto/webcrypto.aesgcm.dart +++ b/lib/src/webcrypto/webcrypto.aesgcm.dart @@ -85,7 +85,7 @@ final class AesGcmSecretKey { /// {@macro AES:no-support-for-AES-192} /// /// If specified the `"use"` property of the imported [jwk] must be - /// `"use": "sig"`. + /// `"use": "enc"`. /// /// {@macro importJsonWebKey:throws-FormatException-if-jwk} /// diff --git a/lib/src/webcrypto/webcrypto.ecdh.dart b/lib/src/webcrypto/webcrypto.ecdh.dart index 064dbb17..ebb91020 100644 --- a/lib/src/webcrypto/webcrypto.ecdh.dart +++ b/lib/src/webcrypto/webcrypto.ecdh.dart @@ -206,15 +206,16 @@ final class EcdhPrivateKey { /// two parties. /// /// [length] specifies the length of the derived secret in bits. + /// The maximum length depends on the [EllipticCurve] used: + /// * [EllipticCurve.p256] — up to 256 bits. + /// * [EllipticCurve.p384] — up to 384 bits. + /// * [EllipticCurve.p521] — up to 528 bits. + /// /// [publicKey] is [EcdhPublicKey] from the other party's ECDH key pair. /// /// Returns a [Uint8List] containing the derived shared secret. /// /// {@macro EcdhPrivateKey:example} - // Note some webcrypto implementations (chrome, not firefox) supports passing - // null for length (in this primitive). However, you can always know the right - // length from the curve. Note p512 can provide up to: 528 bits!!! - // // See: https://www.rfc-editor.org/rfc/rfc6090#section-4 // Notice that this is not uniformly distributed, see also: // https://www.rfc-editor.org/rfc/rfc6090#appendix-B diff --git a/lib/src/webcrypto/webcrypto.hmac.dart b/lib/src/webcrypto/webcrypto.hmac.dart index 0fa30a93..de927ae0 100644 --- a/lib/src/webcrypto/webcrypto.hmac.dart +++ b/lib/src/webcrypto/webcrypto.hmac.dart @@ -64,17 +64,8 @@ final class HmacSecretKey { /// as zero'ing the last bits in [keyData]. /// /// **Example** - /// ```dart - /// import 'dart:convert' show utf8; - /// import 'package:webcrypto/webcrypto.dart'; /// - /// Future main() async { - /// final key = await HmacSecretKey.importRawKey( - /// base64.decode('WzIxLDg0LDEwMCw5OSwxMCwxMDUsMjIsODAsMTkwLDExNiwyMDMsMjQ5XQ=='), - /// Hash.sha256, - /// ); - /// } - /// ``` + /// {@example /example/webcrypto/hmac/import_raw_key.dart#example} static Future importRawKey( List keyData, Hash hash, { diff --git a/lib/src/webcrypto/webcrypto.pbkdf2.dart b/lib/src/webcrypto/webcrypto.pbkdf2.dart index c82544b2..37fcc46e 100644 --- a/lib/src/webcrypto/webcrypto.pbkdf2.dart +++ b/lib/src/webcrypto/webcrypto.pbkdf2.dart @@ -49,8 +49,6 @@ part of 'webcrypto.dart'; /// {@endtemplate} /// /// [1]: https://www.rfc-editor.org/rfc/rfc8018 -// TODO: Rewrite all RFC links to use https://www.rfc-editor.org/rfc/rfcXXXX - final class Pbkdf2SecretKey { final Pbkdf2SecretKeyImpl _impl; diff --git a/lib/src/webcrypto/webcrypto.rsaoaep.dart b/lib/src/webcrypto/webcrypto.rsaoaep.dart index 0cc715b5..65b1ebf7 100644 --- a/lib/src/webcrypto/webcrypto.rsaoaep.dart +++ b/lib/src/webcrypto/webcrypto.rsaoaep.dart @@ -14,6 +14,17 @@ part of 'webcrypto.dart'; +Map _normalizeLegacyRsaOaepJwkAlg( + Map jwk, + Hash hash, +) { + // Preserve imports of JWKs exported by older FFI releases. + if (identical(hash, Hash.sha1) && jwk['alg'] == 'RSA-OAEP-1') { + return {...jwk, 'alg': 'RSA-OAEP'}; + } + return jwk; +} + /// RSAES-OAEP private key for decryption of messages. /// /// An [RsaOaepPrivateKey] instance holds a private RSA key for decrypting @@ -176,7 +187,7 @@ final class RsaOaepPrivateKey { Hash hash, ) async { final impl = await webCryptImpl.rsaOaepPrivateKey.importJsonWebKey( - jwk, + _normalizeLegacyRsaOaepJwkAlg(jwk, hash), hash._impl, ); return RsaOaepPrivateKey._(impl); @@ -468,7 +479,7 @@ final class RsaOaepPublicKey { Hash hash, ) async { final impl = await webCryptImpl.rsaOaepPublicKey.importJsonWebKey( - jwk, + _normalizeLegacyRsaOaepJwkAlg(jwk, hash), hash._impl, ); return RsaOaepPublicKey._(impl); diff --git a/pubspec.yaml b/pubspec.yaml index 4e5ab44d..1db77ef0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -24,7 +24,7 @@ dependencies: ffi: ^2.0.0 hooks: ^1.0.0 code_assets: ^1.0.0 - native_toolchain_cmake: ^0.2.4 + native_toolchain_cmake: ^0.3.0 jni: ^1.0.0 meta: ^1.3.0