blob: 8f42a70f03c5602d8818f52e998e172466452c2a [file] [edit]
/* Public domain, no copyright. Use at your own risk. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jwt_tests.h"
static const char PT[] = "{\"sub\":\"1234567890\",\"name\":\"Jane Doe\"}";
static void roundtrip(const char *keyfile, jwe_enc_t enc)
{
jwe_builder_auto_t *builder = NULL;
jwe_checker_auto_t *checker = NULL;
char_auto *tok = NULL;
unsigned char *pt = NULL;
size_t pt_len = 0;
read_json(keyfile);
builder = jwe_builder_new();
ck_assert_int_eq(jwe_builder_setkey(builder, JWE_ALG_DIR, enc, g_item),
0);
tok = jwe_builder_generate(builder, (const unsigned char *)PT,
strlen(PT));
ck_assert_ptr_nonnull(tok);
checker = jwe_checker_new();
ck_assert_int_eq(jwe_checker_setkey(checker, JWE_ALG_DIR, enc, g_item),
0);
pt = jwe_checker_decrypt(checker, tok, &pt_len);
ck_assert_ptr_nonnull(pt);
ck_assert_int_eq(pt_len, strlen(PT));
ck_assert_mem_eq(pt, PT, pt_len);
free(pt);
free_key();
}
START_TEST(rt_128)
{
SET_OPS();
roundtrip("oct_dir_256.json", JWE_ENC_A128CBC_HS256);
}
END_TEST
START_TEST(rt_192)
{
SET_OPS();
roundtrip("oct_dir_384.json", JWE_ENC_A192CBC_HS384);
}
END_TEST
START_TEST(rt_256)
{
SET_OPS();
roundtrip("oct_dir_512.json", JWE_ENC_A256CBC_HS512);
}
END_TEST
START_TEST(tamper_ct)
{
jwe_builder_auto_t *builder = NULL;
jwe_checker_auto_t *checker = NULL;
char_auto *tok = NULL;
unsigned char *pt = NULL;
size_t pt_len = 0;
char *seg;
SET_OPS();
read_json("oct_dir_512.json");
builder = jwe_builder_new();
ck_assert_int_eq(jwe_builder_setkey(builder, JWE_ALG_DIR,
JWE_ENC_A256CBC_HS512, g_item), 0);
tok = jwe_builder_generate(builder, (const unsigned char *)PT,
strlen(PT));
ck_assert_ptr_nonnull(tok);
/* Corrupt the first ciphertext char (4th segment). The HMAC tag must
* fail in the constant-time compare. */
seg = tok;
seg = strchr(seg, '.') + 1; /* EK (empty) */
seg = strchr(seg, '.') + 1; /* iv */
seg = strchr(seg, '.') + 1; /* ct */
ck_assert_int_ne(*seg, '\0');
*seg = (*seg == 'Q') ? 'R' : 'Q';
checker = jwe_checker_new();
ck_assert_int_eq(jwe_checker_setkey(checker, JWE_ALG_DIR,
JWE_ENC_A256CBC_HS512, g_item), 0);
pt = jwe_checker_decrypt(checker, tok, &pt_len);
ck_assert_ptr_null(pt);
ck_assert_int_eq(jwe_checker_error(checker), 1);
free_key();
}
END_TEST
START_TEST(tamper_tag)
{
jwe_builder_auto_t *builder = NULL;
jwe_checker_auto_t *checker = NULL;
char_auto *tok = NULL;
unsigned char *pt = NULL;
size_t len, pt_len = 0;
SET_OPS();
read_json("oct_dir_256.json");
builder = jwe_builder_new();
ck_assert_int_eq(jwe_builder_setkey(builder, JWE_ALG_DIR,
JWE_ENC_A128CBC_HS256, g_item), 0);
tok = jwe_builder_generate(builder, (const unsigned char *)PT,
strlen(PT));
ck_assert_ptr_nonnull(tok);
/* Corrupt the first character of the tag (final segment) by walking to
* the last dot. */
len = strlen(tok);
while (len > 0 && tok[len - 1] != '.')
len--;
ck_assert_int_gt(len, 0);
tok[len] = (tok[len] == 'Q') ? 'R' : 'Q';
checker = jwe_checker_new();
ck_assert_int_eq(jwe_checker_setkey(checker, JWE_ALG_DIR,
JWE_ENC_A128CBC_HS256, g_item), 0);
pt = jwe_checker_decrypt(checker, tok, &pt_len);
ck_assert_ptr_null(pt);
ck_assert_int_eq(jwe_checker_error(checker), 1);
free_key();
}
END_TEST
START_TEST(wrong_key)
{
jwe_builder_auto_t *builder = NULL;
jwe_checker_auto_t *checker = NULL;
char_auto *tok = NULL;
unsigned char *pt = NULL;
size_t pt_len = 0;
SET_OPS();
read_json("oct_dir_512.json");
builder = jwe_builder_new();
ck_assert_int_eq(jwe_builder_setkey(builder, JWE_ALG_DIR,
JWE_ENC_A256CBC_HS512, g_item), 0);
tok = jwe_builder_generate(builder, (const unsigned char *)PT,
strlen(PT));
ck_assert_ptr_nonnull(tok);
free_key();
/* A different 64-byte key must not authenticate. */
read_json("oct_key_512.json");
checker = jwe_checker_new();
ck_assert_int_eq(jwe_checker_setkey(checker, JWE_ALG_DIR,
JWE_ENC_A256CBC_HS512, g_item), 0);
pt = jwe_checker_decrypt(checker, tok, &pt_len);
ck_assert_ptr_null(pt);
free_key();
}
END_TEST
/* Cross-backend interop: a token produced by one crypto backend must decrypt
* under every other compiled backend. This catches any divergence in the
* AES-CBC padding or the HMAC tag construction between providers. */
START_TEST(interop)
{
char_auto *tok = NULL;
size_t b;
if (ARRAY_SIZE(jwt_test_ops) < 2)
return;
/* Encrypt under provider 0. */
ck_assert_int_eq(jwt_set_crypto_ops(jwt_test_ops[0].name), 0);
read_json("oct_dir_512.json");
{
jwe_builder_auto_t *builder = jwe_builder_new();
ck_assert_int_eq(jwe_builder_setkey(builder, JWE_ALG_DIR,
JWE_ENC_A256CBC_HS512, g_item), 0);
tok = jwe_builder_generate(builder, (const unsigned char *)PT,
strlen(PT));
ck_assert_ptr_nonnull(tok);
}
free_key();
/* Decrypt under every compiled provider. */
for (b = 0; b < ARRAY_SIZE(jwt_test_ops); b++) {
jwe_checker_auto_t *checker = NULL;
unsigned char *pt = NULL;
size_t pt_len = 0;
ck_assert_int_eq(jwt_set_crypto_ops(jwt_test_ops[b].name), 0);
read_json("oct_dir_512.json");
checker = jwe_checker_new();
ck_assert_int_eq(jwe_checker_setkey(checker, JWE_ALG_DIR,
JWE_ENC_A256CBC_HS512, g_item), 0);
pt = jwe_checker_decrypt(checker, tok, &pt_len);
ck_assert_ptr_nonnull(pt);
ck_assert_int_eq(pt_len, strlen(PT));
ck_assert_mem_eq(pt, PT, pt_len);
free(pt);
free_key();
}
}
END_TEST
static Suite *libjwt_suite(const char *title)
{
Suite *s;
TCase *tc_core;
int i = ARRAY_SIZE(jwt_test_ops);
s = suite_create(title);
tc_core = tcase_create("JWE dir+CBC-HMAC");
tcase_add_loop_test(tc_core, rt_128, 0, i);
tcase_add_loop_test(tc_core, rt_192, 0, i);
tcase_add_loop_test(tc_core, rt_256, 0, i);
tcase_add_loop_test(tc_core, tamper_ct, 0, i);
tcase_add_loop_test(tc_core, tamper_tag, 0, i);
tcase_add_loop_test(tc_core, wrong_key, 0, i);
tcase_add_test(tc_core, interop);
tcase_set_timeout(tc_core, 30);
suite_add_tcase(s, tc_core);
return s;
}
int main(void)
{
JWT_TEST_MAIN("LibJWT JWE dir+CBC-HMAC");
}