From f271a5d2d96fe301a7029f1cc8c5426d03cb6d4b Mon Sep 17 00:00:00 2001 From: Ken Takayama Date: Sun, 28 Jul 2024 23:33:10 +0000 Subject: [PATCH] update: disable non AEAD algorithms by default and introduce T_COSE_OPT_ENABLE_NON_AEAD to avoid unintentional use of them --- inc/t_cose/t_cose_common.h | 13 +++ inc/t_cose/t_cose_encrypt_dec.h | 10 ++ inc/t_cose/t_cose_encrypt_enc.h | 12 +++ src/t_cose_encrypt_dec.c | 4 + src/t_cose_encrypt_enc.c | 8 ++ test/t_cose_encrypt_decrypt_test.c | 148 ++++++++++++++++++++++++----- 6 files changed, 169 insertions(+), 26 deletions(-) diff --git a/inc/t_cose/t_cose_common.h b/inc/t_cose/t_cose_common.h index 9187f109..9f62e348 100644 --- a/inc/t_cose/t_cose_common.h +++ b/inc/t_cose/t_cose_common.h @@ -791,6 +791,19 @@ enum t_cose_err_t { */ #define T_COSE_OPT_REQUIRE_KID 0x00001000 +/** + * WARNING: DO NOT use this option flag without understanding the + * security consideration of RFC 9459. + * By default, the error \ref T_COSE_ERR_UNSUPPORTED_ENCRYPTION_ALG + * is returned if non AEAD content encryption algorithms, such as + * AES-CTR and AES-CBC, are used. + * The sender and recipient can use them only with this option flag. + * The sender MUST use AES-CTR in conjunction with an authentication + * and integrity mechanism, such as a digital signature. + * The recipient MUST validate data authenticity and integrity. + */ +#define T_COSE_OPT_ENABLE_NON_AEAD 0x00002000 + /** * \brief Check whether an algorithm is supported. diff --git a/inc/t_cose/t_cose_encrypt_dec.h b/inc/t_cose/t_cose_encrypt_dec.h index c666ac78..b59d8fa5 100644 --- a/inc/t_cose/t_cose_encrypt_dec.h +++ b/inc/t_cose/t_cose_encrypt_dec.h @@ -133,6 +133,16 @@ struct t_cose_encrypt_dec_ctx { * explicitly set the CEK, but it rarely needed as the CEK is * generated automatocally from the random number generator when * it is not set. + * + * By default, the t_cose_encrypt_dec() decrypts COSE encrypted message + * only with AEAD content encryption algorithms, and returns + * \ref T_COSE_ERR_UNSUPPORTED_ENCRYPTION_ALG with non AEAD ones. + * If \c option_flags includes \ref T_COSE_OPT_ENABLE_NON_AEAD the function + * will also decrypt with non-AEAD ciphers. + * WARNING: Do not use this flag without considerations, because non-AEAD + * content encryption algorithms does NOT validate neither authentication + * nor integrity, it is library caller's responsibility to check them. + * See security considerations of RFC 9459. */ static void t_cose_encrypt_dec_init(struct t_cose_encrypt_dec_ctx *context, diff --git a/inc/t_cose/t_cose_encrypt_enc.h b/inc/t_cose/t_cose_encrypt_enc.h index 2833018c..3de42494 100644 --- a/inc/t_cose/t_cose_encrypt_enc.h +++ b/inc/t_cose/t_cose_encrypt_enc.h @@ -188,6 +188,18 @@ struct t_cose_encrypt_enc { * t_cose_recipient_enc being used. You can even have serveral with * different algorithms (but there can only be one payload encryption * algorithm). + * + * By default, the t_cose_encrypt_enc() encrypts a message + * only with AEAD content encryption algorithms, and returns + * \ref T_COSE_ERR_UNSUPPORTED_ENCRYPTION_ALG with non AEAD ones. + * If \c option_flags includes \ref T_COSE_OPT_ENABLE_NON_AEAD the function + * will also encrypt with non-AEAD ciphers. + * WARNING: Do not use this flag without considerations, because non-AEAD + * content encryption algorithms does NOT provide neither authentication + * nor integrity, it is library caller's responsibility to deliver the + * COSE message in conjunction with an authentication and integrity + * mechanism, such as a digital signature. + * See security considerations of RFC 9459. */ void t_cose_encypt_enc_init(struct t_cose_encrypt_enc *context, diff --git a/src/t_cose_encrypt_dec.c b/src/t_cose_encrypt_dec.c index 50093f5e..3be82979 100644 --- a/src/t_cose_encrypt_dec.c +++ b/src/t_cose_encrypt_dec.c @@ -214,6 +214,10 @@ t_cose_encrypt_dec_detached(struct t_cose_encrypt_dec_ctx* me, return T_COSE_ERR_NO_ALG_ID; } if(t_cose_alg_is_non_aead(ce_alg.cose_alg_id)) { + /* Make sure that the library caller (recipient) explicitly enables non AEAD ciphers*/ + if(!(me->option_flags & T_COSE_OPT_ENABLE_NON_AEAD)) { + return T_COSE_ERR_UNSUPPORTED_ENCRYPTION_ALG; + } /* Make sure there are no protected headers for non-aead algorithms */ if(!t_cose_params_empty(protected_params)) { return T_COSE_ERR_PROTECTED_NOT_ALLOWED; diff --git a/src/t_cose_encrypt_enc.c b/src/t_cose_encrypt_enc.c index 6902decb..d561bf76 100644 --- a/src/t_cose_encrypt_enc.c +++ b/src/t_cose_encrypt_enc.c @@ -74,6 +74,14 @@ t_cose_encrypt_enc_detached(struct t_cose_encrypt_enc *me, /* ---- Algorithm ID, IV and parameter list ---- */ /* Determine algorithm parameters */ is_non_aead_cipher = t_cose_alg_is_non_aead(me->payload_cose_algorithm_id); + if(is_non_aead_cipher && !(me->option_flags & T_COSE_OPT_ENABLE_NON_AEAD)) { + /* The libraty caller (sender) MUST explicitly enable non AEAD + * content encryption algorithms with \c T_COSE_OPT_ENABLE_NON_AEAD + * awaring that the COSE message MUST be in conjunction with an + * authentication and integrity mechanism, such as a digital signature. + */ + return T_COSE_ERR_UNSUPPORTED_ENCRYPTION_ALG; + } if(is_non_aead_cipher && !q_useful_buf_c_is_null_or_empty(ext_sup_data)) { /* Section 6 of RFC9459 says, * COSE libraries that support either AES-CTR or AES-CBC and diff --git a/test/t_cose_encrypt_decrypt_test.c b/test/t_cose_encrypt_decrypt_test.c index 5167c609..366d3d61 100644 --- a/test/t_cose_encrypt_decrypt_test.c +++ b/test/t_cose_encrypt_decrypt_test.c @@ -120,9 +120,11 @@ check_headers(const struct t_cose_parameter *headers, bool is_non_aead) } -int32_t encrypt0_enc_dec(int32_t cose_algorithm_id) +int32_t encrypt0_enc_dec(int32_t cose_algorithm_id, bool enable_non_aead_encryption, bool enable_non_aead_decryption) { struct t_cose_encrypt_enc enc_context; + uint32_t option_flags; + bool is_non_aead = false; enum t_cose_err_t t_cose_err; int32_t return_value; struct t_cose_key cek; @@ -143,19 +145,22 @@ int32_t encrypt0_enc_dec(int32_t cose_algorithm_id) struct t_cose_parameter p_storage_array[10]; switch(cose_algorithm_id) { - case T_COSE_ALGORITHM_A128GCM: case T_COSE_ALGORITHM_A128CTR: case T_COSE_ALGORITHM_A128CBC: + is_non_aead = true; + case T_COSE_ALGORITHM_A128GCM: cek_bytes = Q_USEFUL_BUF_FROM_SZ_LITERAL("128-bit key xxxx"); break; - case T_COSE_ALGORITHM_A192GCM: case T_COSE_ALGORITHM_A192CTR: case T_COSE_ALGORITHM_A192CBC: + is_non_aead = true; + case T_COSE_ALGORITHM_A192GCM: cek_bytes = Q_USEFUL_BUF_FROM_SZ_LITERAL("192-bit key xxxxyyyyyyyy"); break; - case T_COSE_ALGORITHM_A256GCM: case T_COSE_ALGORITHM_A256CTR: case T_COSE_ALGORITHM_A256CBC: + is_non_aead = true; + case T_COSE_ALGORITHM_A256GCM: cek_bytes = Q_USEFUL_BUF_FROM_SZ_LITERAL("256-bit key xxxxyyyyyyyyzzzzzzzz"); break; case T_COSE_ALGORITHM_AES128CCM_16_128: @@ -176,8 +181,12 @@ int32_t encrypt0_enc_dec(int32_t cose_algorithm_id) goto Done2; } + option_flags = T_COSE_OPT_MESSAGE_TYPE_ENCRYPT0; + if(enable_non_aead_encryption) { + option_flags |= T_COSE_OPT_ENABLE_NON_AEAD; + } t_cose_encrypt_enc_init(&enc_context, - T_COSE_OPT_MESSAGE_TYPE_ENCRYPT0, + option_flags, cose_algorithm_id); @@ -206,13 +215,21 @@ int32_t encrypt0_enc_dec(int32_t cose_algorithm_id) cose_message_buf, &encrypted_cose_message); - if(t_cose_err) { + if(t_cose_err == T_COSE_ERR_UNSUPPORTED_ENCRYPTION_ALG && is_non_aead && !enable_non_aead_encryption) { + /* t_cose could prevent unintended use of non AEAD ciphers */ + return_value = 0; + goto Done; + } + else if(t_cose_err) { return_value = 2000 + (int32_t)t_cose_err; goto Done; } - - t_cose_encrypt_dec_init(&dec_ctx, T_COSE_OPT_MESSAGE_TYPE_ENCRYPT0); + option_flags = T_COSE_OPT_MESSAGE_TYPE_ENCRYPT0; + if(enable_non_aead_decryption) { + option_flags |= T_COSE_OPT_ENABLE_NON_AEAD; + } + t_cose_encrypt_dec_init(&dec_ctx, option_flags); t_cose_encrypt_dec_set_cek(&dec_ctx, cek); @@ -236,7 +253,12 @@ int32_t encrypt0_enc_dec(int32_t cose_algorithm_id) decrypted_payload_buf, &decrypted_payload, &decoded_parameters); - if(t_cose_err) { + if(t_cose_err == T_COSE_ERR_UNSUPPORTED_ENCRYPTION_ALG && is_non_aead && !enable_non_aead_decryption) { + /* t_cose could prevent unintended use of non AEAD ciphers */ + return_value = 0; + goto Done; + } + else if(t_cose_err) { return_value = 3000 + (int32_t)t_cose_err; goto Done; } @@ -254,7 +276,7 @@ int32_t encrypt0_enc_dec(int32_t cose_algorithm_id) /* ---- test detached ----- */ t_cose_encrypt_enc_init(&enc_context, - T_COSE_OPT_MESSAGE_TYPE_ENCRYPT0, + option_flags, cose_algorithm_id); t_cose_encrypt_set_cek(&enc_context, cek); t_cose_err = t_cose_encrypt_enc_detached(&enc_context, @@ -269,7 +291,7 @@ int32_t encrypt0_enc_dec(int32_t cose_algorithm_id) goto Done; } - t_cose_encrypt_dec_init(&dec_ctx, T_COSE_OPT_MESSAGE_TYPE_ENCRYPT0); + t_cose_encrypt_dec_init(&dec_ctx, option_flags); t_cose_encrypt_dec_set_cek(&dec_ctx, cek); t_cose_err = t_cose_encrypt_dec_detached(&dec_ctx, encrypted_cose_message, @@ -301,51 +323,120 @@ int32_t encrypt0_enc_dec(int32_t cose_algorithm_id) int32_t base_encrypt_decrypt_test(void) { int32_t rv; - rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A128GCM); + rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A128GCM, false, false); if(rv) { return 10000 + rv; } - rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A192GCM); + rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A192GCM, false, false); if(rv) { return 20000 + rv; } - rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A256GCM); + rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A256GCM, false, false); if(rv) { return 30000 + rv; } - rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A128CTR); + /* Enable non-AEAD ciphers on both Sender and Recipient side. + * Success on both side are expected. + */ + rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A128CTR, true, true); if(rv) { return 40000 + rv; } - rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A192CTR); + rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A192CTR, true, true); if(rv) { return 50000 + rv; } - rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A256CTR); + rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A256CTR, true, true); if(rv) { return 60000 + rv; } - rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A128CBC); + rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A128CBC, true, true); if(rv) { return 70000 + rv; } - rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A192CBC); + rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A192CBC, true, true); if(rv) { return 80000 + rv; } - rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A256CBC); + rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A256CBC, true, true); if(rv) { return 90000 + rv; } + /* Disable non-AEAD ciphers on both Sender and Recipient side. + * Failure and early return on Sender side is expected. + */ + rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A128CTR, false, false); + if(rv) { + return 100000 + rv; + } + + rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A192CTR, false, false); + if(rv) { + return 110000 + rv; + } + + rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A256CTR, false, false); + if(rv) { + return 120000 + rv; + } + + rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A128CBC, false, false); + if(rv) { + return 130000 + rv; + } + + rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A192CBC, false, false); + if(rv) { + return 140000 + rv; + } + + rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A256CBC, false, false); + if(rv) { + return 150000 + rv; + } + + /* Disable non-AEAD ciphers on only Recipient side. + * Failure and early return on Recipient side is expected. + */ + rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A128CTR, true, false); + if(rv) { + return 160000 + rv; + } + + rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A192CTR, true, false); + if(rv) { + return 170000 + rv; + } + + rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A256CTR, true, false); + if(rv) { + return 180000 + rv; + } + + rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A128CBC, true, false); + if(rv) { + return 190000 + rv; + } + + rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A192CBC, true, false); + if(rv) { + return 200000 + rv; + } + + rv = encrypt0_enc_dec(T_COSE_ALGORITHM_A256CBC, true, false); + if(rv) { + return 210000 + rv; + } + return 0; } @@ -353,9 +444,10 @@ int32_t base_encrypt_decrypt_test(void) #ifndef T_COSE_DISABLE_KEYWRAP -int32_t decrypt_key_wrap(struct q_useful_buf_c cose_encrypt_buffer) +int32_t decrypt_key_wrap(struct q_useful_buf_c cose_encrypt_buffer, bool enable_non_aead) { enum t_cose_err_t result; + uint32_t option_flags; int32_t return_value = 0; struct t_cose_recipient_dec_keywrap kw_unwrap_recipient; struct t_cose_encrypt_dec_ctx decrypt_context; @@ -374,7 +466,11 @@ int32_t decrypt_key_wrap(struct q_useful_buf_c cose_encrypt_buffer) goto Done2; } - t_cose_encrypt_dec_init(&decrypt_context, T_COSE_OPT_MESSAGE_TYPE_ENCRYPT); + option_flags = T_COSE_OPT_MESSAGE_TYPE_ENCRYPT; + if(enable_non_aead) { + option_flags |= T_COSE_OPT_ENABLE_NON_AEAD; + } + t_cose_encrypt_dec_init(&decrypt_context, option_flags); t_cose_recipient_dec_keywrap_init(&kw_unwrap_recipient); t_cose_recipient_dec_keywrap_set_kek(&kw_unwrap_recipient, kek, NULL_Q_USEFUL_BUF_C); t_cose_encrypt_dec_add_recipient(&decrypt_context, (struct t_cose_recipient_dec *)&kw_unwrap_recipient); @@ -415,11 +511,11 @@ int32_t decrypt_known_good_aeskw_non_aead_test(void) return INT32_MIN; /* Means no testing was actually done */ } - return_value = decrypt_key_wrap(UsefulBuf_FROM_BYTE_ARRAY_LITERAL(cose_encrypt_a128ctr_a128kw)); + return_value = decrypt_key_wrap(UsefulBuf_FROM_BYTE_ARRAY_LITERAL(cose_encrypt_a128ctr_a128kw), true); if(return_value != 0) { return return_value + 10000; } - return_value = decrypt_key_wrap(UsefulBuf_FROM_BYTE_ARRAY_LITERAL(cose_encrypt_a128cbc_a128kw)); + return_value = decrypt_key_wrap(UsefulBuf_FROM_BYTE_ARRAY_LITERAL(cose_encrypt_a128cbc_a128kw), true); if(return_value != 0) { return return_value + 20000; } @@ -475,7 +571,7 @@ esdh_enc_dec(int32_t curve, int32_t payload_cose_algorithm_id) * body of the message. */ t_cose_encrypt_enc_init(&enc_ctx, - T_COSE_OPT_MESSAGE_TYPE_ENCRYPT, + T_COSE_OPT_MESSAGE_TYPE_ENCRYPT | T_COSE_OPT_ENABLE_NON_AEAD, payload_cose_algorithm_id); /* Create the recipient object telling it the algorithm and the public key @@ -507,7 +603,7 @@ esdh_enc_dec(int32_t curve, int32_t payload_cose_algorithm_id) } - t_cose_encrypt_dec_init(&dec_ctx, 0); + t_cose_encrypt_dec_init(&dec_ctx, T_COSE_OPT_ENABLE_NON_AEAD); t_cose_recipient_dec_esdh_init(&dec_recipient);