diff --git a/lib/src/impl_ffi/impl_ffi.utils.dart b/lib/src/impl_ffi/impl_ffi.utils.dart index 69f9a5a6..2b602af8 100644 --- a/lib/src/impl_ffi/impl_ffi.utils.dart +++ b/lib/src/impl_ffi/impl_ffi.utils.dart @@ -486,7 +486,11 @@ Uint8List _jwkDecodeBase64UrlNoPadding(String unpadded, String prop) { unpadded.length + ((4 - (unpadded.length % 4)) % 4), '=', ); - return base64Url.decode(padded); + final decoded = base64Url.decode(padded); + if (_jwkEncodeBase64UrlNoPadding(decoded) != unpadded) { + throw const FormatException(); + } + return decoded; } on FormatException { throw FormatException( 'JWK property "$prop" is not url-safe base64 without padding', diff --git a/lib/src/testing/regression/jwk_base64url.dart b/lib/src/testing/regression/jwk_base64url.dart new file mode 100644 index 00000000..83540bdc --- /dev/null +++ b/lib/src/testing/regression/jwk_base64url.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 'package:webcrypto/webcrypto.dart'; + +import '../utils/utils.dart'; + +List<({String name, Future Function() test})> tests() => [ + ( + name: 'JWK: rejects padded base64url key data', + test: () => _checkInvalidKeyData('AQ=='), + ), + ( + name: 'JWK: rejects the standard base64 alphabet', + test: () => _checkInvalidKeyData('+w'), + ), +]; + +Future _checkInvalidKeyData(String keyData) async { + var threw = false; + try { + await HmacSecretKey.importJsonWebKey({ + 'kty': 'oct', + 'alg': 'HS256', + 'k': keyData, + }, Hash.sha256); + } on FormatException { + threw = true; + } + check(threw, 'Should throw FormatException for invalid JWK base64url data'); +} diff --git a/lib/src/testing/testing.dart b/lib/src/testing/testing.dart index 3c7b4fd2..2e0944fa 100644 --- a/lib/src/testing/testing.dart +++ b/lib/src/testing/testing.dart @@ -31,6 +31,7 @@ import 'webcrypto/rsassapkcs1v15.dart' as rsassapkcs1v15; import 'webcrypto/random.dart' as random; import 'webcrypto/digest.dart' as digest; import 'regression/issue_60_trailing_bytes.dart' as issue_60_trailing_bytes; +import 'regression/jwk_base64url.dart' as jwk_base64url; /// Test runners from all test files except `digest.dart` and /// `random.dart`, which do not use [TestRunner]. @@ -60,6 +61,7 @@ void runAllTests( ...random.tests(), ...digest.tests(), ...issue_60_trailing_bytes.tests(), + ...jwk_base64url.tests(), ]; for (final (:name, :test) in allTests) {