CVE-2023-23928 (Other, published 2023-02-01T01:15Z)
Description: value: reason-jose is a JOSE implementation in ReasonML and OCaml.`Jose.Jws.validate` does not check HS256 signatures. This allows tampering of JWS header and payload data if the service does not perform additional checks. Such tampering could expose applications using reason-jose to authorization bypass. Applications relying on JWS claims assertion to enforce security boundaries may be vulnerable to privilege escalation. This issue has been patched in version 0.8.2.
Severity: nan (CVSS v3 base score: 9.8)
Weakness: CWE-347 \u2014 Improper Verification of Cryptographic Signature
The product does not verify, or incorrectly verifies, the cryptographic signature for data.
Repository: https://github.com/ulrikstrid/reason-jose
Files changed:
jose/Jws.ml
test/RFC7520.ml
Commit message: Merge pull request from GHSA-7jj9-6qwv-wpm7

* Add failing test

* Fix HS256 signature check

* Decode with Jwk.oct_to_sign_key helper

---------

Co-authored-by: Kai <nankeen@users.noreply.github.com>

Vulnerable code:
      Mirage_crypto.Hash.SHA256.hmac ~key:(Cstruct.of_string jwk.key) str
      |> U_Result.return
  |> U_Result.map (fun message ->
         let token_hash =
           input_str |> Cstruct.of_string |> Mirage_crypto.Hash.SHA256.digest
         in
         (* From RFC7518§3.2:
          *   The comparison of the computed HMAC value to the JWS Signature
          *   value MUST be done in a constant-time manner to thwart timing
          *   attacks. *)
         Eqaf_cstruct.equal message token_hash)
  |> U_Result.flat_map (fun _ -> verify_internal ~jwk t)
  |> U_Result.map (fun _ -> t)

Fixed code:
      Jwk.oct_to_sign_key jwk
      |> U_Result.flat_map (fun key ->
             let computed_signature =
               Mirage_crypto.Hash.SHA256.hmac ~key (Cstruct.of_string input_str)
             in
             (* From RFC7518§3.2:
              *   The comparison of the computed HMAC value to the JWS Signature
              *   value MUST be done in a constant-time manner to thwart timing
              *   attacks. *)
             if Eqaf_cstruct.equal str computed_signature then
               Ok computed_signature
             else Error `Invalid_signature)
  |> U_Result.flat_map (fun _alg ->
         match verify_internal ~jwk t with
         | Ok _sig -> Ok t
         | Error e -> Error e)
      Alcotest.test_case "Cannot verify invalid signature" `Quick (fun () ->
          let jwk =
            Jose.Jwk.of_pub_json_string oct_sig_json |> CCResult.get_exn
          in
          let jws =
            Jose.Jws.of_string
              {|eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4.NO_GOOD_SIGNATURE|}
          in
          let validated_jws = CCResult.flat_map (Jose.Jws.validate ~jwk) jws in
          check_result_string "validation fails" (Error `Invalid_signature)
            (CCResult.map (fun jws -> Jose.Jws.(jws.signature)) validated_jws));