Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract cjose_jwe_encrypt_iv to allow explicit IV #102

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
64 changes: 63 additions & 1 deletion include/cjose/jwe.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,36 @@ typedef const cjose_jwk_t *(*cjose_key_locator)(cjose_jwe_t *jwe, cjose_header_t
cjose_jwe_t *
cjose_jwe_encrypt(const cjose_jwk_t *jwk, cjose_header_t *header, const uint8_t *plaintext, size_t plaintext_len, cjose_err *err);

/**
* Creates a new JWE by encrypting the given plaintext within the given header
* and JWK, with a static IV.
*
* If the header provided indicates an algorithm requiring an asymmetric key
* (e.g. RSA-OAEP), the provided JWK must be asymmetric (e.g. RSA or EC).
*
* If the header provided indicates an algorithm requiring a symmetric key
* (e.g. (dir), the provided JWK must be symmetric (e.g. oct).
*
* \param jwk [in] the key to use for encrypting the JWE.
* \param protected_header [in] additional header values to include in the JWE protected header.
* \param iv [in] the initialization vector for encrypting the JWE payload. If NULL, an IV will be automatically generated.
* The IV is copied.
* \param iv_len [in] the length of the initialization vector, or 0 if iv is NULL.
* \param plaintext [in] the plaintext to be encrypted in the JWE payload.
* \param plaintext_len [in] the length of the plaintext.
* \param err [out] An optional error object which can be used to get additional
* information in the event of an error.
* \returns a newly generated JWE with the given plaintext as the payload.
*/
cjose_jwe_t *
cjose_jwe_encrypt_iv(const cjose_jwk_t *jwk,
cjose_header_t *header,
const uint8_t *iv,
size_t iv_len,
const uint8_t *plaintext,
size_t plaintext_len,
cjose_err *err);

/**
* Creates a new JWE by encrypting the given plaintext with multiple keys.
* \see ::cjose_jwe_encrypt for key requirements.
Expand All @@ -75,7 +105,7 @@ cjose_jwe_encrypt(const cjose_jwk_t *jwk, cjose_header_t *header, const uint8_t
* recipients there is.
* \param protected_header [in] additional header values to include in the JWE protected header. The header
* is retained by JWE and should be released by the caller if no longer needed.
* \param unprotected_header [in] additional header values to include in the shared JWE unprotected header,
* \param shared_unprotected_header [in] additional header values to include in the shared JWE unprotected header,
* can be NULL. The header is retained by JWE and should be released by the caller if no longer needed.
* \param plaintext [in] the plaintext to be encrypted in the JWE payload.
* \param plaintext_len [in] the length of the plaintext.
Expand All @@ -91,6 +121,38 @@ cjose_jwe_t *cjose_jwe_encrypt_multi(const cjose_jwe_recipient_t * recipients,
size_t plaintext_len,
cjose_err *err);

/**
* Creates a new JWE by encrypting the given plaintext with multiple keys and a static IV.
* \see ::cjose_jwe_encrypt for key requirements.
* \see ::cjose_jwe_encrypt_multi to automatically generate an IV.
* \param recipients [in] array of recipient objects. Each element must have the
* key of the recipient, and may have optional (not NULL) unprotected header.
* Unprotected header is retained by this function, and can be safely released by the
* caller if no longer needed. The key is only used within the scope of this function.
* \param recipient_count effective length of the recipients array, specifying how many
* recipients there is.
* \param protected_header [in] additional header values to include in the JWE protected header. The header
* is retained by JWE and should be released by the caller if no longer needed.
* \param shared_unprotected_header [in] additional header values to include in the shared JWE unprotected header,
* can be NULL. The header is retained by JWE and should be released by the caller if no longer needed.
* \param iv [in] the initialization vector for encrypting the JWE payload. If NULL, an IV will be automatically generated.
* \param iv_len [in] the length of the initialization vector, or 0 if iv is NULL.
* \param plaintext [in] the plaintext to be encrypted in the JWE payload.
* \param plaintext_len [in] the length of the plaintext.
* \param err [out] An optional error object which can be used to get additional
* information in the event of an error.
* \returns a newly generated JWE with the given plaintext as the payload.
*/
cjose_jwe_t *cjose_jwe_encrypt_multi_iv(const cjose_jwe_recipient_t * recipients,
size_t recipient_count,
cjose_header_t *protected_header,
cjose_header_t *shared_unprotected_header,
const uint8_t *iv,
size_t iv_len,
const uint8_t *plaintext,
size_t plaintext_len,
cjose_err *err);

/**
* Creates a compact serialization of the given JWE object.
*
Expand Down
72 changes: 59 additions & 13 deletions src/jwe.c
Original file line number Diff line number Diff line change
Expand Up @@ -1378,26 +1378,40 @@ static bool _cjose_jwe_decrypt_dat_aes_cbc(cjose_jwe_t *jwe, cjose_err *err)
}

////////////////////////////////////////////////////////////////////////////////
cjose_jwe_t *cjose_jwe_encrypt(
const cjose_jwk_t *jwk, cjose_header_t *protected_header, const uint8_t *plaintext, size_t plaintext_len, cjose_err *err)
cjose_jwe_t *cjose_jwe_encrypt_iv(const cjose_jwk_t *jwk,
cjose_header_t *protected_header,
const uint8_t *iv,
size_t iv_len,
const uint8_t *plaintext,
size_t plaintext_len,
cjose_err *err)
{

cjose_jwe_recipient_t rec = {
.jwk = jwk,
.unprotected_header = NULL
};

return cjose_jwe_encrypt_multi(&rec, 1, protected_header, NULL, plaintext, plaintext_len, err);
return cjose_jwe_encrypt_multi_iv(&rec, 1, protected_header, NULL, iv, iv_len, plaintext, plaintext_len, err);
}

////////////////////////////////////////////////////////////////////////////////
cjose_jwe_t *cjose_jwe_encrypt_multi(const cjose_jwe_recipient_t * recipients,
size_t recipient_count,
cjose_header_t *protected_header,
cjose_header_t *shared_unprotected_header,
const uint8_t *plaintext,
size_t plaintext_len,
cjose_err *err)
cjose_jwe_t *cjose_jwe_encrypt(
const cjose_jwk_t *jwk, cjose_header_t *protected_header, const uint8_t *plaintext, size_t plaintext_len, cjose_err *err)
{
return cjose_jwe_encrypt_iv(jwk, protected_header, NULL, 0, plaintext, plaintext_len, err);
}

////////////////////////////////////////////////////////////////////////////////
cjose_jwe_t *cjose_jwe_encrypt_multi_iv(const cjose_jwe_recipient_t * recipients,
size_t recipient_count,
cjose_header_t *protected_header,
cjose_header_t *shared_unprotected_header,
const uint8_t *iv,
size_t iv_len,
const uint8_t *plaintext,
size_t plaintext_len,
cjose_err *err)
{
cjose_jwe_t *jwe = NULL;

Expand Down Expand Up @@ -1475,10 +1489,22 @@ cjose_jwe_t *cjose_jwe_encrypt_multi(const cjose_jwe_recipient_t * recipients,
}

// build JWE initialization vector
if (!jwe->fns.set_iv(jwe, err))
if (iv == NULL) {
if (!jwe->fns.set_iv(jwe, err))
{
cjose_jwe_release(jwe);
return NULL;
}
}
else
{
cjose_jwe_release(jwe);
return NULL;
cjose_get_dealloc()(jwe->enc_iv.raw);
jwe->enc_iv.raw_len = iv_len;
if (!_cjose_jwe_malloc(jwe->enc_iv.raw_len, false, &jwe->enc_iv.raw, err)) {
cjose_jwe_release(jwe);
return NULL;
}
memcpy(jwe->enc_iv.raw, iv, iv_len);
}

// build JWE encrypted data and authentication tag
Expand All @@ -1493,6 +1519,26 @@ cjose_jwe_t *cjose_jwe_encrypt_multi(const cjose_jwe_recipient_t * recipients,
return jwe;
}

////////////////////////////////////////////////////////////////////////////////
cjose_jwe_t *cjose_jwe_encrypt_multi(const cjose_jwe_recipient_t * recipients,
size_t recipient_count,
cjose_header_t *protected_header,
cjose_header_t *shared_unprotected_header,
const uint8_t *plaintext,
size_t plaintext_len,
cjose_err *err)
{
return cjose_jwe_encrypt_multi_iv(recipients,
recipient_count,
protected_header,
shared_unprotected_header,
NULL,
0,
plaintext,
plaintext_len,
err);
}

////////////////////////////////////////////////////////////////////////////////
void cjose_jwe_release(cjose_jwe_t *jwe)
{
Expand Down
137 changes: 137 additions & 0 deletions test/check_jwe.c
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,142 @@ START_TEST(test_cjose_jwe_self_encrypt_self_decrypt)
}
END_TEST

static void _self_encrypt_self_decrypt_with_key_iv(const char *alg, const char *enc, const char *key, size_t iv_len, const char *plain1)
{
cjose_err err;

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);

// set header for JWE
cjose_header_t *hdr = cjose_header_new(&err);
ck_assert_msg(cjose_header_set(hdr, CJOSE_HDR_ALG, alg, &err),
"cjose_header_set failed: "
"%s, file: %s, function: %s, line: %ld",
err.message, err.file, err.function, err.line);

ck_assert_msg(cjose_header_set(hdr, CJOSE_HDR_ENC, enc, &err),
"cjose_header_set failed: "
"%s, file: %s, function: %s, line: %ld",
err.message, err.file, err.function, err.line);

// generate a random IV
uint8_t *iv = (uint8_t *)malloc(iv_len);
ck_assert_msg(RAND_bytes(iv, iv_len) == 1, "RAND_bytes failed");

// create the JWE
size_t plain1_len = strlen(plain1);
cjose_jwe_t *jwe1 = cjose_jwe_encrypt_iv(jwk, hdr, iv, iv_len, plain1, plain1_len, &err);
ck_assert_msg(NULL != jwe1, "cjose_jwe_encrypt failed: %s, file: %s, function: %s, line: %ld", err.message, err.file,
err.function, err.line);
// ck_assert(hdr == cjose_jwe_get_protected(jwe1));

// get the compact serialization of JWE
char *compact = cjose_jwe_export(jwe1, &err);
ck_assert_msg(NULL != compact, "cjose_jwe_export failed: %s, file: %s, function: %s, line: %ld", err.message, err.file,
err.function, err.line);

// deserialize the compact representation to a new JWE
cjose_jwe_t *jwe2 = cjose_jwe_import(compact, strlen(compact), &err);
ck_assert_msg(NULL != jwe2,
"cjose_jwe_import failed for algo %s, method %s: "
"%s, file: %s, function: %s, line: %ld",
alg, enc, err.message, err.file, err.function, err.line);

// get the decrypted plaintext
uint8_t *plain2 = NULL;
size_t plain2_len = 0;
plain2 = cjose_jwe_decrypt(jwe2, 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);

// confirm plain2 == plain1
ck_assert(json_equal((json_t *)cjose_jwe_get_protected(jwe1), (json_t *)cjose_jwe_get_protected(jwe2)));
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_header_release(hdr);
free(iv);
cjose_jwe_release(jwe1);
cjose_jwe_release(jwe2);
cjose_jwk_release(jwk);
cjose_get_dealloc()(compact);
}

static void _self_encrypt_self_decrypt_iv(const char *plain1)
{
// Tests for when #85 (A128GCM and A192GCM support) is merged

// _self_encrypt_self_decrypt_with_key_iv(CJOSE_HDR_ALG_RSA_OAEP, CJOSE_HDR_ENC_A128GCM, JWK_RSA, 12, plain1);

// _self_encrypt_self_decrypt_with_key_iv(CJOSE_HDR_ALG_RSA1_5, CJOSE_HDR_ENC_A128GCM, JWK_RSA, 12, plain1);

// _self_encrypt_self_decrypt_with_key_iv(CJOSE_HDR_ALG_DIR, CJOSE_HDR_ENC_A128GCM, JWK_OCT_16, 12, plain1);

// _self_encrypt_self_decrypt_with_key_iv(CJOSE_HDR_ALG_RSA_OAEP, CJOSE_HDR_ENC_A192GCM, JWK_RSA, 12, plain1);

// _self_encrypt_self_decrypt_with_key_iv(CJOSE_HDR_ALG_RSA1_5, CJOSE_HDR_ENC_A192GCM, JWK_RSA, 12, plain1);

// _self_encrypt_self_decrypt_with_key_iv(CJOSE_HDR_ALG_DIR, CJOSE_HDR_ENC_A192GCM, JWK_OCT_24, 12, plain1);

_self_encrypt_self_decrypt_with_key_iv(CJOSE_HDR_ALG_RSA_OAEP, CJOSE_HDR_ENC_A256GCM, JWK_RSA, 12, plain1);

_self_encrypt_self_decrypt_with_key_iv(CJOSE_HDR_ALG_RSA1_5, CJOSE_HDR_ENC_A256GCM, JWK_RSA, 12, plain1);

_self_encrypt_self_decrypt_with_key_iv(CJOSE_HDR_ALG_DIR, CJOSE_HDR_ENC_A256GCM, JWK_OCT_32, 12, plain1);

_self_encrypt_self_decrypt_with_key_iv(CJOSE_HDR_ALG_DIR, CJOSE_HDR_ENC_A128CBC_HS256, JWK_OCT_32, 16, plain1);

_self_encrypt_self_decrypt_with_key_iv(CJOSE_HDR_ALG_DIR, CJOSE_HDR_ENC_A192CBC_HS384, JWK_OCT_48, 16, plain1);

_self_encrypt_self_decrypt_with_key_iv(CJOSE_HDR_ALG_DIR, CJOSE_HDR_ENC_A256CBC_HS512, JWK_OCT_64, 16, plain1);

_self_encrypt_self_decrypt_with_key_iv(CJOSE_HDR_ALG_A128KW, CJOSE_HDR_ENC_A128CBC_HS256, JWK_OCT_16, 16, plain1);

_self_encrypt_self_decrypt_with_key_iv(CJOSE_HDR_ALG_A192KW, CJOSE_HDR_ENC_A192CBC_HS384, JWK_OCT_24, 16, plain1);

_self_encrypt_self_decrypt_with_key_iv(CJOSE_HDR_ALG_A256KW, CJOSE_HDR_ENC_A256CBC_HS512, JWK_OCT_32, 16, plain1);

// _self_encrypt_self_decrypt_with_key_iv(CJOSE_HDR_ALG_A128KW, CJOSE_HDR_ENC_A128GCM, JWK_OCT_16, 12, plain1);

// _self_encrypt_self_decrypt_with_key_iv(CJOSE_HDR_ALG_ECDH_ES, CJOSE_HDR_ENC_A128GCM, JWK_EC, 12, plain1);

// _self_encrypt_self_decrypt_with_key_iv(CJOSE_HDR_ALG_A128KW, CJOSE_HDR_ENC_A192GCM, JWK_OCT_16, 12, plain1);

// _self_encrypt_self_decrypt_with_key_iv(CJOSE_HDR_ALG_ECDH_ES, CJOSE_HDR_ENC_A192GCM, JWK_EC, 12, plain1);

_self_encrypt_self_decrypt_with_key_iv(CJOSE_HDR_ALG_A128KW, CJOSE_HDR_ENC_A256GCM, JWK_OCT_16, 12, plain1);

_self_encrypt_self_decrypt_with_key_iv(CJOSE_HDR_ALG_ECDH_ES, CJOSE_HDR_ENC_A256GCM, JWK_EC, 12, plain1);
}

START_TEST(test_cjose_jwe_self_encrypt_self_decrypt_iv)
{
_self_encrypt_self_decrypt_iv("Sed ut perspiciatis unde omnis iste natus error sit voluptatem "
"doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo "
"veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo "
"ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed "
"consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. "
"porro quisquam est, qui dolorem ipsum quia dolor sit amet, "
"adipisci velit, sed quia non numquam eius modi tempora incidunt ut "
"dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, "
"nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut "
"ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in "
"voluptate velit esse quam nihil molestiae consequatur, vel illum qui "
"eum fugiat quo voluptas nulla pariatur?");
}
END_TEST


START_TEST(test_cjose_jwe_self_encrypt_self_decrypt_short) { _self_encrypt_self_decrypt("Setec Astronomy"); }
END_TEST

Expand Down Expand Up @@ -1205,6 +1341,7 @@ Suite *cjose_jwe_suite()
tcase_set_timeout(tc_jwe, 120.0);
tcase_add_test(tc_jwe, test_cjose_jwe_node_jose_encrypt_self_decrypt);
tcase_add_test(tc_jwe, test_cjose_jwe_self_encrypt_self_decrypt);
tcase_add_test(tc_jwe, test_cjose_jwe_self_encrypt_self_decrypt_iv);
tcase_add_test(tc_jwe, test_cjose_jwe_self_encrypt_self_decrypt_short);
tcase_add_test(tc_jwe, test_cjose_jwe_self_encrypt_self_decrypt_empty);
tcase_add_test(tc_jwe, test_cjose_jwe_self_encrypt_self_decrypt_large);
Expand Down