Skip to content

Commit

Permalink
Merge pull request cisco#9 from rnapier/encrypt-custom-iv
Browse files Browse the repository at this point in the history
Extract cjose_jwe_encrypt_iv to allow explicit IV
  • Loading branch information
zandbelt authored Apr 5, 2022
2 parents 715bcbc + 033d1f0 commit 38863df
Show file tree
Hide file tree
Showing 3 changed files with 259 additions and 14 deletions.
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 @@ -1442,26 +1442,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 @@ -1539,10 +1553,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 @@ -1557,6 +1583,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 @@ -271,6 +271,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 @@ -1236,6 +1372,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

0 comments on commit 38863df

Please sign in to comment.