Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 71 additions & 4 deletions lib/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
* limitations under the License.
*/

#define _GNU_SOURCE
#include "misc.h"
#include <jose/b64.h>
#include <jose/cfg.h>
#include <string.h>
#include "hooks.h"

Expand All @@ -43,6 +45,51 @@ zero(void *mem, size_t len)
memset(mem, 0, len);
}

/* Decode the base64url-encoded protected header and load it as JSON only
* when it can contain a "zip" key. The full JSON parse is the expensive
* step (the base64url decode is comparatively cheap), so we gate it behind
* a substring scan for "zip" over the *decoded* bytes: a "zip" key is only
* possible if the literal bytes "zip" appear in the decoded JSON text.
*
* The scan must run on the decoded bytes, NOT on the base64url-encoded
* string: a "zip" key does not survive base64url encoding as the literal
* substring "zip", so scanning the encoded form yields false negatives.
*
* When "zip" is definitely absent, *no_zip is set to true and NULL is
* returned without parsing. On decode error, *no_zip stays false and NULL
* is returned (callers then treat it the same as a header with no zip). */
static json_t *
load_protected_check_zip(const json_t *prt, bool *no_zip)
{
uint8_t *buf = NULL;
json_t *out = NULL;
size_t size = 0;

*no_zip = false;

size = jose_b64_dec(prt, NULL, 0);
if (size == SIZE_MAX)
return NULL;

buf = jose_calloc(1, size);
if (!buf)
return NULL;

if (jose_b64_dec(prt, buf, size) != size) {
zero(buf, size);
jose_free(buf);
return NULL;
}

if (memmem(buf, size, "zip", 3))
out = json_loadb((char *) buf, size, JSON_DECODE_ANY, NULL);
else
*no_zip = true;

zero(buf, size);
jose_free(buf);
return out;
}

bool
handle_zip_enc(json_t *json, const void *in, size_t len, void **data, size_t *datalen)
Expand All @@ -54,8 +101,19 @@ handle_zip_enc(json_t *json, const void *in, size_t len, void **data, size_t *da
jose_io_auto_t *zipdata = NULL;

prt = json_object_get(json, "protected");
if (prt && json_is_string(prt))
prt = jose_b64_dec_load(prt);
if (prt && json_is_string(prt)) {
bool no_zip = false;
json_t *loaded = load_protected_check_zip(prt, &no_zip);
if (no_zip) {
/* No zip; skip the full parse. (prt is a borrowed reference,
* so clear it before returning to avoid a spurious decref.) */
prt = NULL;
*data = (void*)in;
*datalen = len;
return true;
}
prt = loaded;
}

/* Check if we have "zip" in the protected header. */
if (json_unpack(prt, "{s:s}", "zip", &z) == -1) {
Expand Down Expand Up @@ -89,8 +147,17 @@ zip_in_protected_header(json_t *json)
char *z = NULL;

prt = json_object_get(json, "protected");
if (prt && json_is_string(prt))
prt = jose_b64_dec_load(prt);
if (prt && json_is_string(prt)) {
bool no_zip = false;
json_t *loaded = load_protected_check_zip(prt, &no_zip);
if (no_zip) {
/* No zip; skip the full parse. (prt is a borrowed reference,
* so clear it before returning to avoid a spurious decref.) */
prt = NULL;
return false;
}
prt = loaded;
}

/* Check if we have "zip" in the protected header. */
if (json_unpack(prt, "{s:s}", "zip", &z) == -1)
Expand Down
Loading