diff --git a/sys/include/net/nanocoap/cache.h b/sys/include/net/nanocoap/cache.h index 4f9744fb39294..8583b8c52702e 100644 --- a/sys/include/net/nanocoap/cache.h +++ b/sys/include/net/nanocoap/cache.h @@ -220,6 +220,26 @@ int nanocoap_cache_del(const nanocoap_cache_entry_t *ce); */ void nanocoap_cache_key_generate(const coap_pkt_t *req, uint8_t *cache_key); +/** + * @brief Generates a cache key based on only the options in @p req + * + * @param[in] req The request to generate the cache key from + * @param[out] cache_key The generated cache key of SHA256_DIGEST_LENGTH bytes + */ +void nanocoap_cache_key_options_generate(const coap_pkt_t *req, void *cache_key); + +/** + * @brief Generates a cache key based on only the options in @p req without + * any of the blockwise options included in the key + * + * This function can be used to correlate individual requests that are part of a + * blockwise transfer with each other. + * + * @param[in] req The request to generate the cache key from + * @param[out] cache_key The generated cache key of SHA256_DIGEST_LENGTH bytes + */ +void nanocoap_cache_key_blockreq_options_generate(const coap_pkt_t *req, void *cache_key); + /** * @brief Compares two cache keys. * diff --git a/sys/net/application_layer/nanocoap/cache.c b/sys/net/application_layer/nanocoap/cache.c index 329b68a7a50e3..6cff2552a6c7a 100644 --- a/sys/net/application_layer/nanocoap/cache.c +++ b/sys/net/application_layer/nanocoap/cache.c @@ -83,11 +83,10 @@ size_t nanocoap_cache_free_count(void) return clist_count(&_empty_list_head); } -void nanocoap_cache_key_generate(const coap_pkt_t *req, uint8_t *cache_key) +static void _cache_key_digest_opts(const coap_pkt_t *req, sha256_context_t *ctx, + bool include_etag, + bool include_blockwise) { - sha256_context_t ctx; - sha256_init(&ctx); - coap_optpos_t opt = {0, 0}; uint8_t *value; @@ -96,7 +95,7 @@ void nanocoap_cache_key_generate(const coap_pkt_t *req, uint8_t *cache_key) if (optlen >= 0) { /* gCoAP forward proxy is ETag-aware, so skip ETag option, * see https://datatracker.ietf.org/doc/html/rfc7252#section-5.4.2 */ - if (IS_USED(MODULE_GCOAP_FORWARD_PROXY) && (opt.opt_num == COAP_OPT_ETAG)) { + if ((!include_etag) && (opt.opt_num == COAP_OPT_ETAG)) { continue; } /* skip NoCacheKey, @@ -104,9 +103,42 @@ void nanocoap_cache_key_generate(const coap_pkt_t *req, uint8_t *cache_key) if ((opt.opt_num & 0x1E) == 0x1C) { continue; } - sha256_update(&ctx, value, optlen); + /* Don't include blockwise (on request) so matching between + * blockwise parts is possible */ + if ((!include_blockwise) && ( + (opt.opt_num == COAP_OPT_BLOCK2) || + (opt.opt_num == COAP_OPT_BLOCK1) + )) { + continue; + } + sha256_update(ctx, &opt.opt_num, sizeof(opt.opt_num)); + sha256_update(ctx, value, optlen); } } +} + +void nanocoap_cache_key_options_generate(const coap_pkt_t *req, void *cache_key) +{ + sha256_context_t ctx; + sha256_init(&ctx); + _cache_key_digest_opts(req, &ctx, true, true); + sha256_final(&ctx, cache_key); +} + +void nanocoap_cache_key_blockreq_options_generate(const coap_pkt_t *req, void *cache_key) +{ + sha256_context_t ctx; + sha256_init(&ctx); + _cache_key_digest_opts(req, &ctx, true, false); + sha256_final(&ctx, cache_key); +} + +void nanocoap_cache_key_generate(const coap_pkt_t *req, uint8_t *cache_key) +{ + sha256_context_t ctx; + sha256_init(&ctx); + + _cache_key_digest_opts(req, &ctx, !(IS_USED(MODULE_GCOAP_FORWARD_PROXY)), true); switch (req->hdr->code) { case COAP_METHOD_FETCH: sha256_update(&ctx, req->payload, req->payload_len); diff --git a/tests/unittests/tests-nanocoap_cache/tests-nanocoap_cache.c b/tests/unittests/tests-nanocoap_cache/tests-nanocoap_cache.c index b2b02576834e1..c8d96b7c6e2e3 100644 --- a/tests/unittests/tests-nanocoap_cache/tests-nanocoap_cache.c +++ b/tests/unittests/tests-nanocoap_cache/tests-nanocoap_cache.c @@ -74,6 +74,60 @@ static void test_nanocoap_cache__cachekey(void) /* compare 3. and 1. packet */ TEST_ASSERT(nanocoap_cache_key_compare(digest2, digest1) > 0); } + +static void test_nanocoap_cache__cachekey_blockwise(void) +{ + uint8_t digest1[SHA256_DIGEST_LENGTH]; + uint8_t digest2[SHA256_DIGEST_LENGTH]; + uint8_t buf1[_BUF_SIZE]; + uint8_t buf2[_BUF_SIZE]; + coap_pkt_t pkt1; + coap_pkt_t pkt2; + uint16_t msgid = 0xABCD; + uint8_t token[2] = {0xDA, 0xEC}; + char path[] = "/time"; + size_t len; + coap_block1_t blockopt = { + .offset = 0, + .blknum = 0, + .szx = 2, + .more = 1, + }; + + /* 1. packet */ + len = coap_build_hdr((coap_hdr_t *)&buf1[0], COAP_TYPE_NON, + &token[0], 2, COAP_METHOD_GET, msgid); + coap_pkt_init(&pkt1, &buf1[0], sizeof(buf1), len); + coap_opt_add_string(&pkt1, COAP_OPT_URI_PATH, &path[0], '/'); + coap_opt_add_block1_control(&pkt1, &blockopt); + coap_opt_finish(&pkt1, COAP_OPT_FINISH_NONE); + + + blockopt.offset = 128; + blockopt.blknum = 2; + + /* 2. packet */ + len = coap_build_hdr((coap_hdr_t *)&buf2[0], COAP_TYPE_NON, + &token[0], 2, COAP_METHOD_GET, msgid); + coap_pkt_init(&pkt2, &buf2[0], sizeof(buf2), len); + coap_opt_add_string(&pkt2, COAP_OPT_URI_PATH, &path[0], '/'); + coap_opt_add_block1_control(&pkt1, &blockopt); + coap_opt_finish(&pkt2, COAP_OPT_FINISH_NONE); + + nanocoap_cache_key_blockreq_options_generate((const coap_pkt_t *) &pkt1, digest1); + nanocoap_cache_key_blockreq_options_generate((const coap_pkt_t *) &pkt2, digest2); + + /* compare 1. and 2. packet. Should be equal except for blockwise */ + TEST_ASSERT_EQUAL_INT(0, nanocoap_cache_key_compare(digest1, digest2)); + + /* Now with the blockwise option in the digest */ + nanocoap_cache_key_options_generate((const coap_pkt_t *) &pkt1, digest1); + nanocoap_cache_key_options_generate((const coap_pkt_t *) &pkt2, digest2); + + /* compare 1. and 2. packet. Should no longer be equal */ + TEST_ASSERT(nanocoap_cache_key_compare(digest2, digest1) != 0); +} + static void test_nanocoap_cache__add(void) { uint8_t buf[_BUF_SIZE]; @@ -263,6 +317,7 @@ Test *tests_nanocoap_cache_tests(void) new_TestFixture(test_nanocoap_cache__add), new_TestFixture(test_nanocoap_cache__del), new_TestFixture(test_nanocoap_cache__cachekey), + new_TestFixture(test_nanocoap_cache__cachekey_blockwise), new_TestFixture(test_nanocoap_cache__max_age), };