From 7013d40676ccab302a3509fb09de7d8171837238 Mon Sep 17 00:00:00 2001 From: Harshita Yadav Date: Sat, 7 Feb 2026 23:08:45 +0530 Subject: [PATCH] Reject JWKs with conflicting use and key_ops --- lib/src/jsonwebkey.dart | 43 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/lib/src/jsonwebkey.dart b/lib/src/jsonwebkey.dart index 128f0622..ea28ff1a 100644 --- a/lib/src/jsonwebkey.dart +++ b/lib/src/jsonwebkey.dart @@ -61,6 +61,47 @@ final class JsonWebKey { this.k, }); +static void _verifyUseAndKeyOps( + String? use, + List? keyOps, + Map json, +) { + if (use == null || keyOps == null) { + return; // nothing to validate + } + + const encryptionOps = { + 'encrypt', + 'decrypt', + 'wrapKey', + 'unwrapKey', + }; + + const signingOps = { + 'sign', + 'verify', + }; + + Set? allowedOps; + if (use == 'enc') { + allowedOps = encryptionOps; + } else if (use == 'sig') { + allowedOps = signingOps; + } else { + // Unknown "use" values are ignored (spec-compatible) + return; + } + + for (final op in keyOps) { + if (!allowedOps.contains(op)) { + throw FormatException( + 'JWK property "key_ops" conflicts with "use": "$use"', + json, + ); + } + } +} + static JsonWebKey fromJson(Map json) { const stringKeys = [ 'kty', @@ -95,6 +136,8 @@ final class JsonWebKey { } key_ops = (json['key_ops'] as List).map((e) => e as String).toList(); } + _verifyUseAndKeyOps(json['use'] as String?, key_ops, json); + if (json.containsKey('ext') && json['ext'] is! bool) { throw FormatException('JWK entry "ext" must be boolean', json);