)]}'
{
  "commit": "49c730a4036c5ae67a4a97e4e55101e445fda694",
  "tree": "a7d6d62b5f6b21df837674617589572aeb5e8419",
  "parents": [
    "64a4f448657a209e2735ae4e2377ca43d16cbcf2"
  ],
  "author": {
    "name": "Ben Collins",
    "email": "bcollins@libjwt.io",
    "time": "Mon May 04 08:49:20 2026 -0400"
  },
  "committer": {
    "name": "Ben Collins",
    "email": "benmcollins13@gmail.com",
    "time": "Mon May 04 09:57:58 2026 -0400"
  },
  "message": "Fix algorithm confusion that allows JWT forgery via RSA JWK as HMAC key\n\nGHSA-q843-6q5f-w55g: an RSA JWK without an \"alg\" parameter was accepted\nas the verification key for HS256/384/512 tokens. Because the jwk_item_t\nunion shares storage between provider_data (RSA EVP_PKEY *) and\noct.{key,len} (HMAC bytes/length), the OpenSSL HMAC path read a\nzero-length key and produced a successful verification for any token\nsigned as HMAC(\"\", header.payload). This let an attacker forge tokens\nusing only the public JWKS.\n\nBind algorithm acceptance to the JWK\u0027s actual \"kty\" rather than its\noptional \"alg\" hint, in three layered places:\n\n* jwt-private.h: new jwt_alg_required_kty() helper providing the\n  authoritative JWA-algorithm to JWK-kty mapping.\n* jwt-common.c: __setkey_check() now rejects any setkey() / verify\n  callback that pairs a non-none algorithm with an incompatible kty.\n  This is the primary fix and runs at both setkey and post-callback.\n* jwt-verify.c: __verify_config_post() re-checks kty once jwt-\u003ealg is\n  bound from the token, blocking malformed JWKs whose own alg hint\n  disagrees with their kty.\n* jwt.c: __check_hmac() defensive backstop in the single chokepoint\n  used by all three crypto backends; refuses any non-oct key for HMAC.\n\nTests added in jwt_security.c cover the original PoC, the realistic\nJWKS callback exploit pattern, all three HS sizes, RSA/EC/OKP keys\nmis-targeted at HS256, and the malformed-JWK defensive verify path.\nA few existing tests (hs256_token_failed, verify_es256_bad_sig,\nsign_es256_bad_sig, rsa_ec_short) were updated; they previously\ndocumented the vulnerable behavior under the guise of \"different ways\"\nof failing and now assert the correct earlier rejection.\n\nSigned-off-by: Ben Collins \u003cbcollins@libjwt.io\u003e\nCo-Authored-By: Claude Opus 4.7 (1M context) \u003cnoreply@anthropic.com\u003e\n",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "86d75a41824dab512cffe01c8ec62ca7916b0272",
      "old_mode": 33188,
      "old_path": "libjwt/jwt-common.c",
      "new_id": "57d466f4a3c4c8c19f8fbca4dbee1ac1a7f723f9",
      "new_mode": 33188,
      "new_path": "libjwt/jwt-common.c"
    },
    {
      "type": "modify",
      "old_id": "317e3805b1f5a248bd0078605c60c059bb9a6896",
      "old_mode": 33188,
      "old_path": "libjwt/jwt-private.h",
      "new_id": "62c6f63f77823b98baf08139276477e12fbd30ff",
      "new_mode": 33188,
      "new_path": "libjwt/jwt-private.h"
    },
    {
      "type": "modify",
      "old_id": "1342cc425edf3c0b1aea6c11904593de04baa303",
      "old_mode": 33188,
      "old_path": "libjwt/jwt-verify.c",
      "new_id": "5888df839cc97995055d71cc0f69fa7461ce9c41",
      "new_mode": 33188,
      "new_path": "libjwt/jwt-verify.c"
    },
    {
      "type": "modify",
      "old_id": "f391dae016fa1ead1855fa57ad4cc23b1c6e67ae",
      "old_mode": 33188,
      "old_path": "libjwt/jwt.c",
      "new_id": "d1ae3c3c91e5fee17945e2043afd42864f8c8c83",
      "new_mode": 33188,
      "new_path": "libjwt/jwt.c"
    },
    {
      "type": "modify",
      "old_id": "cb57c192cda39272aad0a41fdce0cca449da4fb8",
      "old_mode": 33188,
      "old_path": "tests/jwt_builder.c",
      "new_id": "58b758be6d01726840c2c79b8e133d2d3f9dcf20",
      "new_mode": 33188,
      "new_path": "tests/jwt_builder.c"
    },
    {
      "type": "modify",
      "old_id": "cb84758dc9304223d8a5269e017392561b8686c3",
      "old_mode": 33188,
      "old_path": "tests/jwt_checker.c",
      "new_id": "6a2b833622ce5d62c6637ab71a7b3bf29004c367",
      "new_mode": 33188,
      "new_path": "tests/jwt_checker.c"
    },
    {
      "type": "modify",
      "old_id": "05df33065aca905d816da6f1e6dd2e55f6e74bde",
      "old_mode": 33188,
      "old_path": "tests/jwt_rsa.c",
      "new_id": "405a38bb89c19aeff64abfcbf45e4a54baab02fa",
      "new_mode": 33188,
      "new_path": "tests/jwt_rsa.c"
    },
    {
      "type": "modify",
      "old_id": "d5d77c378efaf2d5b9401e5442380714ce55672b",
      "old_mode": 33188,
      "old_path": "tests/jwt_security.c",
      "new_id": "6fa3a19cff2b2e3b172c9035233fb002ddaa13d4",
      "new_mode": 33188,
      "new_path": "tests/jwt_security.c"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "8b5ede3984e575cfea9618448a9a2d0328ac7fc2",
      "new_mode": 33188,
      "new_path": "tests/keys/rsa_key_2048_pub_alg_hs256.json"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "9d0c84c077e5d12aa0152d0232e7c0e3261157f0",
      "new_mode": 33188,
      "new_path": "tests/keys/rsa_key_2048_pub_no_alg.json"
    }
  ]
}
