Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package jose for openSUSE:Factory checked in at 2026-04-29 19:20:42 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/jose (Old) and /work/SRC/openSUSE:Factory/.jose.new.30200 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "jose" Wed Apr 29 19:20:42 2026 rev:4 rq:1350002 version:14 Changes: -------- --- /work/SRC/openSUSE:Factory/jose/jose.changes 2024-04-29 17:58:22.983451433 +0200 +++ /work/SRC/openSUSE:Factory/.jose.new.30200/jose.changes 2026-04-29 19:22:23.840269820 +0200 @@ -1,0 +2,8 @@ +Tue Mar 17 17:16:58 UTC 2026 - Dirk Müller <[email protected]> + +- update to 14: + * openssl/oct: improve bound check for len + * jwe: fix the case when we have "zip" in the protected header + * Avoid potential DoS with high decompression chunks + +------------------------------------------------------------------- Old: ---- jose-13.tar.xz New: ---- jose-14.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ jose.spec ++++++ --- /var/tmp/diff_new_pack.PzGNSd/_old 2026-04-29 19:22:25.652343990 +0200 +++ /var/tmp/diff_new_pack.PzGNSd/_new 2026-04-29 19:22:25.664344481 +0200 @@ -1,7 +1,7 @@ # # spec file for package jose # -# Copyright (c) 2024 SUSE LLC +# Copyright (c) 2026 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,7 +19,7 @@ %define so_ver 0 Name: jose -Version: 13 +Version: 14 Release: 0 Summary: C-language implementation of Javascript Object Signing and Encryption License: Apache-2.0 ++++++ jose-13.tar.xz -> jose-14.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jose-13/lib/hooks.h new/jose-14/lib/hooks.h --- old/jose-13/lib/hooks.h 2024-04-03 14:52:22.000000000 +0200 +++ new/jose-14/lib/hooks.h 2024-05-22 12:21:11.000000000 +0200 @@ -20,6 +20,8 @@ #include <jose/jws.h> #include <jose/jwe.h> +#define MAX_COMPRESSED_SIZE (256*1024) + typedef enum { JOSE_HOOK_JWK_KIND_NONE = 0, JOSE_HOOK_JWK_KIND_TYPE, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jose-13/lib/jwe.c new/jose-14/lib/jwe.c --- old/jose-13/lib/jwe.c 2024-04-03 14:52:22.000000000 +0200 +++ new/jose-14/lib/jwe.c 2024-05-22 12:21:11.000000000 +0200 @@ -275,14 +275,8 @@ jose_io_t *next) { const jose_hook_alg_t *alg = NULL; - jose_io_auto_t *zip = NULL; - json_auto_t *prt = NULL; const char *h = NULL; const char *k = NULL; - const char *z = NULL; - - prt = jose_b64_dec_load(json_object_get(jwe, "protected")); - (void) json_unpack(prt, "{s:s}", "zip", &z); if (json_unpack(jwe, "{s?{s?s}}", "unprotected", "enc", &h) < 0) return NULL; @@ -336,19 +330,7 @@ if (!encode_protected(jwe)) return NULL; - if (z) { - const jose_hook_alg_t *a = NULL; - - a = jose_hook_alg_find(JOSE_HOOK_ALG_KIND_COMP, z); - if (!a) - return NULL; - - zip = a->comp.def(a, cfg, next); - if (!zip) - return NULL; - } - - return alg->encr.enc(alg, cfg, jwe, cek, zip ? zip : next); + return alg->encr.enc(alg, cfg, jwe, cek, next); } void * @@ -463,6 +445,12 @@ o = jose_io_malloc(cfg, &pt, ptl); d = jose_jwe_dec_cek_io(cfg, jwe, cek, o); i = jose_b64_dec_io(d); + + /* Here we make sure the ciphertext is not larger than our + * compression limit. */ + if (zip_in_protected_header((json_t*)jwe) && ctl > MAX_COMPRESSED_SIZE) + return false; + if (!o || !d || !i || !i->feed(i, ct, ctl) || !i->done(i)) return NULL; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jose-13/lib/meson.build new/jose-14/lib/meson.build --- old/jose-13/lib/meson.build 2024-04-03 14:52:22.000000000 +0200 +++ new/jose-14/lib/meson.build 2024-05-22 12:21:11.000000000 +0200 @@ -2,8 +2,14 @@ code = 'int main() { return 0; }' cc = meson.get_compiler('c') -if not cc.links(code, args: flags, name: '-Wl,--version-script=...') - flags = [ '-export-symbols-regex=^jose_.*' ] +if host_machine.system() == 'freebsd' + if not cc.links(code, args: flags + ',--undefined-version' , name: '-Wl,--version-script=...') + flags = [ '-export-symbols-regex=^jose_.*' ] + endif +else + if not cc.links(code, args: flags, name: '-Wl,--version-script=...') + flags = [ '-export-symbols-regex=^jose_.*' ] + endif endif libjose_lib = shared_library('jose', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jose-13/lib/misc.c new/jose-14/lib/misc.c --- old/jose-13/lib/misc.c 2024-04-03 14:52:22.000000000 +0200 +++ new/jose-14/lib/misc.c 2024-05-22 12:21:11.000000000 +0200 @@ -18,6 +18,7 @@ #include "misc.h" #include <jose/b64.h> #include <string.h> +#include "hooks.h" bool encode_protected(json_t *obj) @@ -42,6 +43,63 @@ memset(mem, 0, len); } + +bool +handle_zip_enc(json_t *json, const void *in, size_t len, void **data, size_t *datalen) +{ + json_t *prt = NULL; + char *z = NULL; + const jose_hook_alg_t *a = NULL; + jose_io_auto_t *zip = NULL; + jose_io_auto_t *zipdata = NULL; + + prt = json_object_get(json, "protected"); + if (prt && json_is_string(prt)) + prt = jose_b64_dec_load(prt); + + /* Check if we have "zip" in the protected header. */ + if (json_unpack(prt, "{s:s}", "zip", &z) == -1) { + /* No zip. */ + *data = (void*)in; + *datalen = len; + return true; + } + + /* OK, we have "zip", so we should compress the payload before + * the encryption takes place. */ + a = jose_hook_alg_find(JOSE_HOOK_ALG_KIND_COMP, z); + if (!a) + return false; + + zipdata = jose_io_malloc(NULL, data, datalen); + if (!zipdata) + return false; + + zip = a->comp.def(a, NULL, zipdata); + if (!zip || !zip->feed(zip, in, len) || !zip->done(zip)) + return false; + + return true; +} + +bool +zip_in_protected_header(json_t *json) +{ + json_t *prt = NULL; + char *z = NULL; + + prt = json_object_get(json, "protected"); + if (prt && json_is_string(prt)) + prt = jose_b64_dec_load(prt); + + /* Check if we have "zip" in the protected header. */ + if (json_unpack(prt, "{s:s}", "zip", &z) == -1) + return false; + + /* We have "zip", but let's validate the alg also. */ + return jose_hook_alg_find(JOSE_HOOK_ALG_KIND_COMP, z) != NULL; +} + static void __attribute__((constructor)) constructor(void) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jose-13/lib/misc.h new/jose-14/lib/misc.h --- old/jose-13/lib/misc.h 2024-04-03 14:52:22.000000000 +0200 +++ new/jose-14/lib/misc.h 2024-05-22 12:21:11.000000000 +0200 @@ -30,3 +30,9 @@ void zero(void *mem, size_t len); + +bool +handle_zip_enc(json_t *jwe, const void *in, size_t len, void **data, size_t *data_len); + +bool +zip_in_protected_header(json_t *jwe); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jose-13/lib/openssl/aescbch.c new/jose-14/lib/openssl/aescbch.c --- old/jose-13/lib/openssl/aescbch.c 2024-04-03 14:52:22.000000000 +0200 +++ new/jose-14/lib/openssl/aescbch.c 2024-05-22 12:21:11.000000000 +0200 @@ -18,6 +18,7 @@ #include "misc.h" #include <jose/b64.h> #include "../hooks.h" +#include "../misc.h" #include <openssl/rand.h> #include <openssl/sha.h> @@ -155,9 +156,13 @@ io_t *i = containerof(io, io_t, io); uint8_t ct[EVP_CIPHER_CTX_block_size(i->cctx) + 1]; - const uint8_t *pt = in; + uint8_t *pt = NULL; + size_t ptlen = 0; - for (size_t j = 0; j < len; j++) { + if (!handle_zip_enc(i->json, in, len, (void**)&pt, &ptlen)) + return false; + + for (size_t j = 0; j < ptlen; j++) { int l = 0; if (EVP_EncryptUpdate(i->cctx, ct, &l, &pt[j], 1) <= 0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jose-13/lib/openssl/aesgcm.c new/jose-14/lib/openssl/aesgcm.c --- old/jose-13/lib/openssl/aesgcm.c 2024-04-03 14:52:22.000000000 +0200 +++ new/jose-14/lib/openssl/aesgcm.c 2024-05-22 12:21:11.000000000 +0200 @@ -18,6 +18,7 @@ #include "misc.h" #include <jose/b64.h> #include "../hooks.h" +#include "../misc.h" #include <openssl/rand.h> @@ -103,10 +104,15 @@ enc_feed(jose_io_t *io, const void *in, size_t len) { io_t *i = containerof(io, io_t, io); - const uint8_t *pt = in; int l = 0; - for (size_t j = 0; j < len; j++) { + uint8_t *pt = NULL; + size_t ptlen = 0; + + if (!handle_zip_enc(i->json, in, len, (void**)&pt, &ptlen)) + return false; + + for (size_t j = 0; j < ptlen; j++) { uint8_t ct[EVP_CIPHER_CTX_block_size(i->cctx) + 1]; if (EVP_EncryptUpdate(i->cctx, ct, &l, &pt[j], 1) <= 0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jose-13/lib/openssl/oct.c new/jose-14/lib/openssl/oct.c --- old/jose-13/lib/openssl/oct.c 2024-04-03 14:52:22.000000000 +0200 +++ new/jose-14/lib/openssl/oct.c 2024-05-22 12:21:11.000000000 +0200 @@ -45,7 +45,7 @@ if (json_unpack(jwk, "{s:I}", "bytes", &len) < 0) return false; - if (len > KEYMAX) + if (len <= 0 || len > KEYMAX) return false; if (RAND_bytes(key, len) <= 0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jose-13/lib/zlib/deflate.c new/jose-14/lib/zlib/deflate.c --- old/jose-13/lib/zlib/deflate.c 2024-04-03 14:52:22.000000000 +0200 +++ new/jose-14/lib/zlib/deflate.c 2024-05-22 12:21:11.000000000 +0200 @@ -113,6 +113,9 @@ static bool inf_feed(jose_io_t *io, const void *in, size_t len) { + if (len > MAX_COMPRESSED_SIZE) { + return false; + } return feed(io, in, len, inflate); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jose-13/meson.build new/jose-14/meson.build --- old/jose-13/meson.build 2024-04-03 14:52:22.000000000 +0200 +++ new/jose-14/meson.build 2024-05-22 12:21:11.000000000 +0200 @@ -1,5 +1,5 @@ project('jose', 'c', license: 'APL2', - version: '13', + version: '14', default_options: [ 'c_std=gnu99', 'prefix=/usr', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jose-13/tests/alg_comp.c new/jose-14/tests/alg_comp.c --- old/jose-13/tests/alg_comp.c 2024-04-03 14:52:22.000000000 +0200 +++ new/jose-14/tests/alg_comp.c 2024-05-22 12:21:11.000000000 +0200 @@ -19,6 +19,10 @@ #include <jose/jose.h> #include <assert.h> #include <string.h> +#include <stdlib.h> + +static int g_high_compression_tested = 0; +static int g_low_compression_tested = 0; const struct { const char *alg; @@ -41,6 +45,63 @@ {} }; +const uint32_t long_string_tests[] = { + 2000, 200000, 10000000, 0 +}; + +static uint8_t* get_random_string(uint32_t length) +{ + assert(length); + uint8_t* c = (uint8_t*)malloc(length*sizeof(uint8_t)); + assert(c); + for (uint32_t i=0; i<length; i++) { + c[i] = 'A' + (random() % 26); + } + return c; +} + +static void +test_long_string(size_t inputlen) { + jose_io_auto_t *b = NULL; + jose_io_auto_t *c = NULL; + jose_io_auto_t *z = NULL; + void *buf1 = NULL; + void *buf2 = NULL; + size_t blen = 0; + size_t clen = 0; + const jose_hook_alg_t *a = jose_hook_alg_find(JOSE_HOOK_ALG_KIND_COMP, "DEF"); + uint8_t* str = get_random_string(inputlen); + + /* Test compression first. */ + b = jose_io_malloc(NULL, &buf1, &blen); + assert(b); + z = a->comp.def(a, NULL, b); + assert(z); + + assert(z->feed(z, str, inputlen)); + assert(z->done(z)); + + /* Test decompression now */ + c = jose_io_malloc(NULL, &buf2, &clen); + assert(b); + z = a->comp.inf(a, NULL, c); + assert(z); + + /* If length>MAX_COMPRESSED_SIZE, it must fail due to high decompression size */ + if(blen > MAX_COMPRESSED_SIZE) { + assert(!z->feed(z, buf1, blen)); + g_high_compression_tested = 1; + } else { + assert(z->feed(z, buf1, blen)); + g_low_compression_tested = 1; + /* Compare the final output with the original input. */ + assert(clen == inputlen); + assert(memcmp(buf2, str, inputlen) == 0); + } + assert(z->done(z)); + free(str); +} + static void test(const jose_hook_alg_t *a, bool iter, const uint8_t *i, size_t il) @@ -119,5 +180,12 @@ tst_inf, sizeof(tst_inf)); } + for (size_t i = 0; long_string_tests[i]; i++) { + test_long_string(long_string_tests[i]); + } + + assert(1 == g_high_compression_tested); + assert(1 == g_low_compression_tested); + return EXIT_SUCCESS; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jose-13/tests/api_jwe.c new/jose-14/tests/api_jwe.c --- old/jose-13/tests/api_jwe.c 2024-04-03 14:52:22.000000000 +0200 +++ new/jose-14/tests/api_jwe.c 2024-05-22 12:21:11.000000000 +0200 @@ -19,8 +19,10 @@ #include <assert.h> #include <string.h> +#include "../lib/hooks.h" /* for MAX_COMPRESSED_SIZE */ + static bool -dec(json_t *jwe, json_t *jwk) +dec_cmp(json_t *jwe, json_t *jwk, const char* expected_data, size_t expected_len) { bool ret = false; char *pt = NULL; @@ -30,10 +32,10 @@ if (!pt) goto error; - if (ptl != 4) + if (ptl != expected_len) goto error; - if (strcmp(pt, "foo") != 0) + if (strcmp(pt, expected_data) != 0) goto error; ret = true; @@ -43,12 +45,40 @@ return ret; } +static bool +dec(json_t *jwe, json_t *jwk) +{ + return dec_cmp(jwe, jwk, "foo", 4); +} + +struct zip_test_data_t { + char* data; + size_t datalen; + bool expected; +}; + +static char* +make_data(size_t len) +{ + assert(len > 0); + + char *data = malloc(len); + assert(data); + + for (size_t i = 0; i < len; i++) { + data[i] = 'A' + (random() % 26); + } + data[len-1] = '\0'; + return data; +} + int main(int argc, char *argv[]) { json_auto_t *jwke = json_pack("{s:s}", "alg", "ECDH-ES+A128KW"); json_auto_t *jwkr = json_pack("{s:s}", "alg", "RSA1_5"); json_auto_t *jwko = json_pack("{s:s}", "alg", "A128KW"); + json_auto_t *jwkz = json_pack("{s:s, s:i}", "kty", "oct", "bytes", 16); json_auto_t *set0 = json_pack("{s:[O,O]}", "keys", jwke, jwko); json_auto_t *set1 = json_pack("{s:[O,O]}", "keys", jwkr, jwko); json_auto_t *set2 = json_pack("{s:[O,O]}", "keys", jwke, jwkr); @@ -57,6 +87,7 @@ assert(jose_jwk_gen(NULL, jwke)); assert(jose_jwk_gen(NULL, jwkr)); assert(jose_jwk_gen(NULL, jwko)); + assert(jose_jwk_gen(NULL, jwkz)); json_decref(jwe); assert((jwe = json_object())); @@ -98,5 +129,67 @@ assert(dec(jwe, set1)); assert(dec(jwe, set2)); + + json_decref(jwe); + assert((jwe = json_pack("{s:{s:s,s:s,s:s,s:s}}", "protected", "alg", "A128KW", "enc", "A128GCM", "typ", "JWE", "zip", "DEF"))); + assert(jose_jwe_enc(NULL, jwe, NULL, jwkz, "foo", 4)); + assert(dec(jwe, jwkz)); + assert(!dec(jwe, jwkr)); + assert(!dec(jwe, jwko)); + assert(!dec(jwe, set0)); + assert(!dec(jwe, set1)); + assert(!dec(jwe, set2)); + + /* Some tests with "zip": "DEF" */ + struct zip_test_data_t zip[] = { + { + .data = make_data(5), + .datalen = 5, + .expected = true, + }, + { + .data = make_data(50), + .datalen = 50, + .expected = true, + }, + { + .data = make_data(1000), + .datalen = 1000, + .expected = true, + }, + { + .data = make_data(10000000), + .datalen = 10000000, + .expected = false, /* compressed len will be ~8000000+ + * (i.e. > MAX_COMPRESSED_SIZE) + */ + }, + { + .data = make_data(50000), + .datalen = 50000, + .expected = true + }, + { + + .data = NULL + } + }; + + for (size_t i = 0; zip[i].data != NULL; i++) { + json_decref(jwe); + assert((jwe = json_pack("{s:{s:s,s:s,s:s,s:s}}", "protected", "alg", "A128KW", "enc", "A128GCM", "typ", "JWE", "zip", "DEF"))); + assert(jose_jwe_enc(NULL, jwe, NULL, jwkz, zip[i].data, zip[i].datalen)); + + /* Now let's get the ciphertext compressed len. */ + char *ct = NULL; + size_t ctl = 0; + assert(json_unpack(jwe, "{s:s%}", "ciphertext", &ct, &ctl) != -1); + /* And check our expectation is correct. */ + assert(zip[i].expected == (ctl < MAX_COMPRESSED_SIZE)); + + assert(dec_cmp(jwe, jwkz, zip[i].data, zip[i].datalen) == zip[i].expected); + free(zip[i].data); + zip[i].data = NULL; + } return EXIT_SUCCESS; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jose-13/tests/jose-jwe-enc new/jose-14/tests/jose-jwe-enc --- old/jose-13/tests/jose-jwe-enc 2024-04-03 14:52:22.000000000 +0200 +++ new/jose-14/tests/jose-jwe-enc 2024-05-22 12:21:11.000000000 +0200 @@ -74,4 +74,13 @@ printf '%s' "$msg" | jose jwe enc -I- -k $jwk -o $jwe [ "`jose jwe dec -i $jwe -k $jwk -O-`" = "$msg" ] done + + # "zip": "DEF" + tmpl='{"kty":"oct","bytes":32}' + for enc in A128CBC-HS256 A192CBC-HS384 A256CBC-HS512 A128GCM A192GCM A256GCM; do + jose jwk gen -i "${tmpl}" -o "${jwk}" + zip="$(printf '{"alg":"A128KW","enc":"%s","zip":"DEF"}' "${enc}")" + printf '%s' "${msg}" | jose jwe enc -i "${zip}" -I- -k "${jwk}" -o "${jwe}" + [ "$(jose jwe dec -i "${jwe}" -k "${jwk}" -O-)" = "${msg}" ] + done done
