Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
6de2fb5
Move Flutter demo app into example/webcrypto_demo_flutter_app/ (#284)
Samarborkar Jun 22, 2026
a11148e
chore: ignore unknown dartdoc example directive (#294)
Musharaf-khan Jun 22, 2026
9978ab6
refactor: replace runtime symbol lookup with direct FFI bindings (#279)
HamdaanAliQuatil Jun 24, 2026
fb81bd8
Reject imported EC and RSA keys with trailing bytes (#251)
harrshita123 Jun 30, 2026
3cd422c
ci: add FFI memory-safety test lane (#295)
HamdaanAliQuatil Jul 5, 2026
80c603e
refactor(js): fix JSDomException checks for wasm compatibility (#306)
kevmoo Jul 8, 2026
c53fc00
fix(testing): wire AES-GCM tagLength vectors (#312)
mfazrinizar Jul 9, 2026
1dc289c
Fix AES-CTR large chunk handling (#309)
harrshita123 Jul 10, 2026
99c36eb
fix(derive-bits): support zero-length results (#321)
harrshita123 Jul 17, 2026
9aefffc
docs: update RFC links to use the official RFC editor URLs (#245)
berkaycatak Jul 17, 2026
15c5400
docs: move HMAC importRawKey example to file (#293)
Musharaf-khan Jul 20, 2026
6117651
chore: mark unsupported code paths with coverage:ignore (#327)
AquibAquil Jul 20, 2026
c1e3f25
docs: correct JWK use value for AES keys (#330)
w1boost1889M Jul 20, 2026
62eb38a
Document maximum deriveBits length for ECDH curves (#289)
Syed-Moiz-Ali Jul 20, 2026
c158185
Upgrade native_toolchain_cmake to v0.3 (#336)
emersion Jul 23, 2026
8a84b9b
fix: use canonical RSA-OAEP JWK alg for SHA-1 (#337)
mfazrinizar Jul 24, 2026
185e781
chore: sync android-jca branch with master
mfazrinizar Jul 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions example/webcrypto/hmac/import_raw_key.dart
Original file line number Diff line number Diff line change
@@ -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<void> main() async {
final key = await HmacSecretKey.importRawKey(
base64.decode(
'WzIxLDg0LDEwMCw5OSwxMCwxMDUsMjIsODAsMTkwLDExNiwyMDMsMjQ5XQ==',
),
Hash.sha256,
);
}

// #endregion
2 changes: 2 additions & 0 deletions lib/src/boringssl/lookup/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions lib/src/impl_ffi/impl_ffi.aes_common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ Uint8List _aesImportRawKey(List<int> 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');
Expand All @@ -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,
Expand Down Expand Up @@ -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');
Expand Down
4 changes: 2 additions & 2 deletions lib/src/impl_ffi/impl_ffi.digest.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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'.
Expand Down Expand Up @@ -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';
Expand Down
6 changes: 6 additions & 0 deletions lib/src/impl_ffi/impl_ffi.ec_common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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_...`.
Expand All @@ -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) {
Expand All @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions lib/src/impl_ffi/impl_ffi.ecdh.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions lib/src/impl_ffi/impl_ffi.ecdsa.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<EcdsaPrivateKeyImpl> ecdsaPrivateKey_importPkcs8Key(
Expand Down
8 changes: 3 additions & 5 deletions lib/src/impl_ffi/impl_ffi.pbkdf2.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 2 additions & 0 deletions lib/src/impl_js/impl_js.utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
1 change: 1 addition & 0 deletions lib/src/impl_stub/impl_stub.aescbc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions lib/src/impl_stub/impl_stub.aesctr.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions lib/src/impl_stub/impl_stub.aesgcm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions lib/src/impl_stub/impl_stub.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
1 change: 1 addition & 0 deletions lib/src/impl_stub/impl_stub.digest.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions lib/src/impl_stub/impl_stub.ecdh.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions lib/src/impl_stub/impl_stub.ecdsa.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions lib/src/impl_stub/impl_stub.hkdf.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions lib/src/impl_stub/impl_stub.hmac.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions lib/src/impl_stub/impl_stub.pbkdf2.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions lib/src/impl_stub/impl_stub.random.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions lib/src/impl_stub/impl_stub.rsaoaep.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions lib/src/impl_stub/impl_stub.rsapss.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions lib/src/impl_stub/impl_stub.rsassapkcs1v15.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
42 changes: 42 additions & 0 deletions lib/src/testing/regression/derive_bits_zero_length.dart
Original file line number Diff line number Diff line change
@@ -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<void> 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');
},
),
];
Loading