CVE-2023-37464 (C, published 2023-07-14T21:15Z)
Description: value: OpenIDC/cjose is a C library implementing the Javascript Object Signing and Encryption (JOSE). The AES GCM decryption routine incorrectly uses the Tag length from the actual Authentication Tag provided in the JWE. The spec  says that a fixed length of 16 octets must be applied. Therefore this bug allows an attacker to provide a truncated Authentication Tag and to modify the JWE accordingly. Users should upgrade to a version >= 0.6.2.2. Users unable to upgrade should avoid using AES GCM encryption and replace it with another encryption algorithm (e.g. AES CBC).
Severity: nan (CVSS v3 base score: 7.5)
Weakness: CWE-327 \u2014 Use of a Broken or Risky Cryptographic Algorithm
The product uses a broken or risky cryptographic algorithm or protocol.
Repository: https://github.com/OpenIDC/cjose
Files changed:
src/jwe.c
test/check_jwe.c
Commit message: use fixed authentication tag length of 16 octets in AES GCM decryption

see: https://github.com/cisco/cjose/issues/125

Signed-off-by: Hans Zandbelt <hans.zandbelt@openidc.com>

Vulnerable code:

Fixed code:
    if (jwe->enc_auth_tag.raw_len != 16)
    {
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
        goto _cjose_jwe_decrypt_dat_aes_gcm_fail;
    }

START_TEST(test_cjose_jwe_decrypt_aes_gcm)
{
    cjose_err err;

    const char *key = JWK_OCT_32;
    const char *plain1 = "Live long and prosper.";
    char *compact1 = "eyJhbGciOiAiZGlyIiwgImVuYyI6ICJBMjU2R0NNIn0..Du_9fxxV-zrReaWC.aS_rpokeuxkaPc2sykcQDCQuJCYoww.GpeKGEqd8KQ0v6JNea5aSA";
    char *compact2 = "eyJhbGciOiAiZGlyIiwgImVuYyI6ICJBMjU2R0NNIn0..Du_9fxxV-zrReaWC.aS_rpokeuxkaPc2sykcQDCQuJCYoww.Gp";

    cjose_jwk_t *jwk = cjose_jwk_import(key, strlen(key), &err);
    ck_assert_msg(NULL != jwk,
                  "cjose_jwk_import failed: "
                  "%s, file: %s, function: %s, line: %ld",
                  err.message, err.file, err.function, err.line);

    cjose_jwe_t *jwe1 = cjose_jwe_import(compact1, strlen(compact1), &err);
    ck_assert_msg(NULL != jwe1,
                  "cjose_jwe_import failed: "
                  "%s, file: %s, function: %s, line: %ld",
                  err.message, err.file, err.function, err.line);

    uint8_t *plain2 = NULL;
    size_t plain2_len = 0;
    plain2 = cjose_jwe_decrypt(jwe1, jwk, &plain2_len, &err);
    ck_assert_msg(NULL != plain2,
                  "cjose_jwe_decrypt failed: "
                  "%s, file: %s, function: %s, line: %ld",
                  err.message, err.file, err.function, err.line);

    ck_assert_msg(plain2_len == strlen(plain1),
                  "length of decrypted plaintext does not match length of original, "
                  "expected: %lu, found: %lu",
                  strlen(plain1), plain2_len);
    ck_assert_msg(strncmp(plain1, plain2, plain2_len) == 0, "decrypted plaintext does not match encrypted plaintext");

    cjose_get_dealloc()(plain2);
    cjose_jwe_release(jwe1);

    cjose_jwe_t *jwe2 = cjose_jwe_import(compact2, strlen(compact2), &err);
    ck_assert_msg(NULL != jwe2,
                   "cjose_jwe_import failed: "
                   "%s, file: %s, function: %s, line: %ld",
                   err.message, err.file, err.function, err.line);

    uint8_t *plain3 = NULL;
    size_t plain3_len = 0;
    plain3 = cjose_jwe_decrypt(jwe2, jwk, &plain3_len, &err);
    ck_assert_msg(NULL == plain3,
                   "cjose_jwe_decrypt succeeded where it should have failed: "
                   "%s, file: %s, function: %s, line: %ld",
                   err.message, err.file, err.function, err.line);

    cjose_jwe_release(jwe2);
    cjose_jwk_release(jwk);
}
END_TEST

    tcase_add_test(tc_jwe, test_cjose_jwe_decrypt_aes_gcm);