JWE_ALG_A192KW,
JWE_ALG_A256KW,
JWE_ALG_DIR,
- // JWE_ALG_ECDH_ES,
+ JWE_ALG_ECDH_ES,
// JWE_ALG_ECDH_ES_A128KW,
// JWE_ALG_ECDH_ES_A192KW,
// JWE_ALG_ECDH_ES_A256KW,
struct jose_fields {
struct buffer *tag;
struct buffer *iv;
+ struct buffer *epk;
+ EVP_PKEY *pubkey;
+ struct buffer *apu;
+ struct buffer *apv;
};
}
/*
- * Look for field <field_name> in JSON <decoded_jose> and base64url decode its
- * content in buffer <out>.
- * The field might not be found, it won't be raised as an error.
+ * Look for field <field_name> in JSON <decoded_jose> and if it is found,
+ * allocate an <out> buffer and base64url decode the field's content in it.
+ * If <mandatory> is set to 1, an error will be raised if the field is not
+ * found.
+ * Returns 0 in case of success, 1 in case of error (decoding or alloc error).
*/
-static inline int decode_jose_field(struct buffer *decoded_jose, const char *field_name, struct buffer *out)
+static inline int decode_jose_field(struct buffer *decoded_jose, const char *field_name,
+ struct buffer **out, int mandatory)
{
struct buffer *trash = get_trash_chunk();
int size = 0;
b_orig(trash), b_size(trash));
if (size != -1) {
trash->data = size;
+
+ *out = alloc_trash_chunk();
+ if (!*out)
+ return 1;
+
size = base64urldec(b_orig(trash), b_data(trash),
- b_orig(out), b_size(out));
- if (size < 0)
+ b_orig(*out), b_size(*out));
+ if (size < 0) {
+ free_trash_chunk(*out);
+ *out = NULL;
return 1;
- out->data = size;
- }
+ }
+ (*out)->data = size;
+ } else if (mandatory)
+ return 1;
return 0;
}
+static int build_EC_PKEY_from_buf(struct buffer *jwk, EVP_PKEY **pkey);
/*
- * Extract the "alg" and "enc" of the JOSE header as well as some algo-specific
- * base64url encoded fields.
+ * Extract multiple base64url encoded fields from the JOSE header that are
+ * required for the subsequent decrypt operations. Depending on the used
+ * algorithms the required fields will change.
*/
static int parse_jose(struct buffer *decoded_jose, int *alg, int *enc, struct jose_fields *jose_fields)
{
int retval = 0;
int size = 0;
+ int gcm = 0;
+ int ec = 0;
+
/* Look for "alg" field */
trash = get_trash_chunk();
size = mjson_get_string(b_orig(decoded_jose), b_data(decoded_jose), "$.alg",
if (*enc == JWE_ENC_UNMANAGED)
goto end;
- /* Look for "tag" field (used by aes gcm encryption) */
- if (decode_jose_field(decoded_jose, "$.tag", jose_fields->tag))
- goto end;
+ switch (*alg) {
+ case JWE_ALG_ECDH_ES:
+ ec = 1;
+ break;
+ case JWE_ALG_A128GCMKW:
+ case JWE_ALG_A192GCMKW:
+ case JWE_ALG_A256GCMKW:
+ gcm = 1;
+ break;
+ default: break;
+ }
- /* Look for "iv" field (used by aes gcm encryption) */
- if (decode_jose_field(decoded_jose, "$.iv", jose_fields->iv))
- goto end;
+
+ if (gcm) {
+ /* Look for "tag" field (used by aes gcm encryption) */
+ if (decode_jose_field(decoded_jose, "$.tag", &jose_fields->tag, 1))
+ goto end;
+
+ /* Look for "iv" field (used by aes gcm encryption) */
+ if (decode_jose_field(decoded_jose, "$.iv", &jose_fields->iv, 1))
+ goto end;
+ }
+
+ if (ec) {
+ /* Look for mandatory "epk" field (used by ecdh encryption) */
+ const char *tokptr = NULL;
+ int toklen = 0;
+ int token_type = mjson_find(b_orig(decoded_jose), b_data(decoded_jose), "$.epk",
+ &tokptr, &toklen);
+ if (token_type != MJSON_TOK_OBJECT)
+ goto end;
+
+ jose_fields->epk = alloc_trash_chunk();
+ if (!jose_fields->epk)
+ goto end;
+
+ if (!chunk_memcpy(jose_fields->epk, tokptr, toklen))
+ goto end;
+
+ if (build_EC_PKEY_from_buf(jose_fields->epk, &jose_fields->pubkey))
+ goto end;
+
+ /* Look for optional "apu" field (used by ecdh encryption) */
+ if (decode_jose_field(decoded_jose, "$.apu", &jose_fields->apu, 0))
+ goto end;
+
+ /* Look for optional "apv" field (used by ecdh encryption) */
+ if (decode_jose_field(decoded_jose, "$.apv", &jose_fields->apv, 0))
+ goto end;
+ }
retval = 1;
return retval;
}
+static void clear_jose_fields(struct jose_fields *jose_fields)
+{
+ if (!jose_fields)
+ return;
+
+ free_trash_chunk(jose_fields->tag);
+ free_trash_chunk(jose_fields->iv);
+ free_trash_chunk(jose_fields->epk);
+ EVP_PKEY_free(jose_fields->pubkey);
+ free_trash_chunk(jose_fields->apu);
+ free_trash_chunk(jose_fields->apv);
+}
+
/*
* Decrypt Encrypted Key <cek> encrypted with AES GCM Key Wrap algorithm and
struct buffer **cek = NULL;
struct buffer *decrypted_cek = NULL;
struct buffer *out = NULL;
- struct buffer *alg_tag = NULL;
- struct buffer *alg_iv = NULL;
int size = 0;
jwe_alg alg = JWE_ALG_UNMANAGED;
jwe_enc enc = JWE_ENC_UNMANAGED;
if (jwt_tokenize(input, items, item_num))
goto end;
- alg_tag = alloc_trash_chunk();
- if (!alg_tag)
- goto end;
- alg_iv = alloc_trash_chunk();
- if (!alg_iv)
- goto end;
-
- fields.tag = alg_tag;
- fields.iv = alg_iv;
-
/* Base64Url decode the JOSE header */
decoded_items[JWE_ELT_JOSE] = alloc_trash_chunk();
if (!decoded_items[JWE_ELT_JOSE])
(*cek)->data = cek_size;
if (gcm) {
- if (!decrypt_cek_aesgcmkw(*cek, alg_tag, alg_iv, decrypted_cek, &secret_smp.data.u.str, alg))
+ if (!decrypt_cek_aesgcmkw(*cek, fields.tag, fields.iv, decrypted_cek,
+ &secret_smp.data.u.str, alg))
goto end;
} else {
if (!decrypt_cek_aeskw(*cek, decrypted_cek, &secret_smp.data.u.str, alg))
retval = 1;
end:
+ clear_jose_fields(&fields);
free_trash_chunk(input);
free_trash_chunk(decrypted_cek);
free_trash_chunk(out);
- free_trash_chunk(alg_tag);
- free_trash_chunk(alg_iv);
clear_decoded_items(decoded_items);
return retval;
}
free_trash_chunk(decrypted_cek);
free_trash_chunk(out);
clear_decoded_items(decoded_items);
+ clear_jose_fields(&fields);
return retval;
}
jwk->secret = NULL;
break;
case JWK_KTY_RSA:
+ case JWK_KTY_EC:
EVP_PKEY_free(jwk->pkey);
jwk->pkey = NULL;
break;
struct buffer *out = NULL;
struct jose_fields fields = {};
- struct buffer *alg_tag = NULL;
- struct buffer *alg_iv = NULL;
-
struct buffer *jwk_buf = NULL;
struct jwk jwk = {};
if (jwt_tokenize(input, items, item_num))
goto end;
- alg_tag = alloc_trash_chunk();
- if (!alg_tag)
- goto end;
- alg_iv = alloc_trash_chunk();
- if (!alg_iv)
- goto end;
-
- fields.tag = alg_tag;
- fields.iv = alg_iv;
-
/* Base64Url decode the JOSE header */
decoded_items[JWE_ELT_JOSE] = alloc_trash_chunk();
if (!decoded_items[JWE_ELT_JOSE])
goto end;
} else {
if (gcm) {
- if (!decrypt_cek_aesgcmkw(*cek, alg_tag, alg_iv, decrypted_cek, jwk.secret, alg))
+ if (!decrypt_cek_aesgcmkw(*cek, fields.tag, fields.iv, decrypted_cek, jwk.secret, alg))
goto end;
} else {
if (!decrypt_cek_aeskw(*cek, decrypted_cek, jwk.secret, alg))
end:
clear_jwk(&jwk);
+ clear_jose_fields(&fields);
free_trash_chunk(jwk_buf);
free_trash_chunk(input);
free_trash_chunk(decrypted_cek);
free_trash_chunk(out);
- free_trash_chunk(alg_tag);
- free_trash_chunk(alg_iv);
clear_decoded_items(decoded_items);
return retval;
}