From d32d2983336c28dc6e08721f021da51b0132c4ce Mon Sep 17 00:00:00 2001 From: Jon Shallow Date: Sat, 30 May 2026 11:37:50 +0100 Subject: [PATCH] coap_oscore.c: Fix replay protection --- include/coap3/coap_session_internal.h | 1 + src/coap_net.c | 3 ++- src/coap_oscore.c | 17 ++++++++++++++--- src/coap_session.c | 1 + 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/include/coap3/coap_session_internal.h b/include/coap3/coap_session_internal.h index 4d52b2eb..28f3f66b 100644 --- a/include/coap3/coap_session_internal.h +++ b/include/coap3/coap_session_internal.h @@ -212,6 +212,7 @@ struct coap_session_t { oscore_association_t *associations; /**< OSCORE set of response associations */ uint64_t oscore_r2; /**< R2 for RFC8613 Appendix B.2 */ + coap_str_const_t *b_2_retransmit_token; /**< Copy of token when doing B.2 */ #endif /* COAP_OSCORE_SUPPORT */ #if COAP_WS_SUPPORT coap_ws_state_t *ws; /**< WS state */ diff --git a/src/coap_net.c b/src/coap_net.c index fb38baa8..f30f47ea 100644 --- a/src/coap_net.c +++ b/src/coap_net.c @@ -4669,7 +4669,8 @@ coap_dispatch(coap_context_t *context, coap_session_t *session, coap_pdu_reference_lkd(orig_pdu); if ((dec_pdu = coap_oscore_decrypt_pdu(session, pdu)) == NULL) { if (session->recipient_ctx == NULL || - session->recipient_ctx->initial_state == 0) { + (session->recipient_ctx->initial_state == 0 && + session->b_2_step == COAP_OSCORE_B_2_NONE)) { coap_log_warn("OSCORE: PDU could not be decrypted\n"); } coap_delete_node_lkd(sent); diff --git a/src/coap_oscore.c b/src/coap_oscore.c index 88678c86..7afa115f 100644 --- a/src/coap_oscore.c +++ b/src/coap_oscore.c @@ -1128,7 +1128,7 @@ coap_oscore_decrypt_pdu(coap_session_t *session, * Requires in COSE object as appropriate * partial_iv (as received) */ - if (rcp_ctx->initial_state == 0 && + if ((rcp_ctx->initial_state == 0 || osc_ctx->rfc8613_b_1_2 == 0) && !oscore_validate_sender_seq(rcp_ctx, cose)) { coap_log_warn("OSCORE: Replayed or old message\n"); build_and_send_error_pdu(session, @@ -1336,7 +1336,7 @@ coap_oscore_decrypt_pdu(coap_session_t *session, } else { uint64_t last_seq; - if (rcp_ctx->initial_state == 0 && + if ((rcp_ctx->initial_state == 0 || osc_ctx->rfc8613_b_1_2 == 0) && !oscore_validate_sender_seq(rcp_ctx, cose)) { coap_log_warn("OSCORE: Replayed or old message\n"); goto error; @@ -1445,7 +1445,7 @@ coap_oscore_decrypt_pdu(coap_session_t *session, tag_len = cose_tag_len(cose->alg); /* Decrypt into plain_pdu, so code (token), options and data are in place */ - plain_pdu = coap_pdu_init(0, 0, 0, encrypt_len /* - tag_len */); + plain_pdu = coap_pdu_init(pdu->type, 0, 0, encrypt_len /* - tag_len */); if (plain_pdu == NULL) { if (!coap_request) coap_handle_event_lkd(session->context, @@ -1576,6 +1576,10 @@ coap_oscore_decrypt_pdu(coap_session_t *session, coap_send_ack_lkd(session, pdu); if (sent_pdu) { coap_log_oscore("Appendix B.2 retransmit pdu\n"); + if (!session->b_2_retransmit_token) { + session->b_2_retransmit_token = coap_new_str_const(sent_pdu->actual_token.s, + sent_pdu->actual_token.length); + } if (coap_retransmit_oscore_pdu(session, sent_pdu, NULL) == COAP_INVALID_MID) goto error_no_ack; @@ -1593,6 +1597,7 @@ coap_oscore_decrypt_pdu(coap_session_t *session, /* Verify Client is genuine */ if (coap_opt_length(opt) == 8 && memcmp(coap_opt_value(opt), rcp_ctx->echo_value, 8) == 0) { + /* This clears rcp_ctx->initial_state */ if (!oscore_validate_sender_seq(rcp_ctx, cose)) { coap_log_warn("OSCORE: Replayed or old message\n"); build_and_send_error_pdu(session, @@ -1728,6 +1733,12 @@ coap_oscore_decrypt_pdu(coap_session_t *session, if (session->b_2_step != COAP_OSCORE_B_2_NONE) { session->b_2_step = COAP_OSCORE_B_2_NONE; coap_log_oscore("Appendix B.2 client finished\n"); + if (session->b_2_retransmit_token) { + coap_update_token(decrypt_pdu, session->b_2_retransmit_token->length, + session->b_2_retransmit_token->s); + coap_delete_str_const(session->b_2_retransmit_token); + session->b_2_retransmit_token = NULL; + } } #if COAP_CLIENT_SUPPORT if (decrypt_pdu->code == COAP_RESPONSE_CODE(401) && diff --git a/src/coap_session.c b/src/coap_session.c index 44844df2..5fd26e9f 100644 --- a/src/coap_session.c +++ b/src/coap_session.c @@ -623,6 +623,7 @@ coap_session_mfree(coap_session_t *session) { #if COAP_OSCORE_SUPPORT coap_delete_oscore_associations(session); oscore_release_recipient_ctx(&session->recipient_ctx); + coap_delete_str_const(session->b_2_retransmit_token); #endif /* COAP_OSCORE_SUPPORT */ #if COAP_WS_SUPPORT coap_free_type(COAP_STRING, session->ws);