The "offset" part of the review :) > Arne Schwabe <a...@rfc2549.org> hat am 28.04.2022 00:34 geschrieben: [...] > diff --git a/src/openvpn/ssl_pkt.c b/src/openvpn/ssl_pkt.c > index a93027505..56baa2895 100644 > --- a/src/openvpn/ssl_pkt.c > +++ b/src/openvpn/ssl_pkt.c [...] > @@ -430,3 +440,91 @@ tls_reset_standalone(struct tls_auth_standalone *tas, > return buf; > } > > + > +hmac_ctx_t * > +session_id_hmac_init(void) > +{ > + /* We assume that SHA256 is always available */ > + ASSERT(md_valid("SHA256")); > + hmac_ctx_t *hmac_ctx = hmac_ctx_new(); > + > + uint8_t key[SHA256_DIGEST_LENGTH]; > + ASSERT(rand_bytes(key, sizeof(key))); > + > + hmac_ctx_init(hmac_ctx, key, "SHA256"); > + return hmac_ctx; > +} > + > +struct session_id > +calculate_session_id_hmac(struct session_id client_sid, > + const struct openvpn_sockaddr *from, > + hmac_ctx_t *hmac, > + int handwindow, int offset) > +{ > + union { > + uint8_t hmac_result[SHA256_DIGEST_LENGTH]; > + struct session_id sid; > + } result; > + > + /* Get the valid time quantisation for our hmac, > + * we divide time by handwindow/2 and allow the current > + * and the previous timestamp */
This comment is misleading. There is no logic here for "current" and "previous" here. The caller makes this decision via offset. So maybe "[...] we divide time by handwindow/2. offset can be used to allow previous timestamps." > + uint32_t session_id_time = now/((handwindow+1)/2) + offset; > + > + hmac_ctx_reset(hmac); > + /* We do not care about endian here since it does not need to be > + * portable */ > + hmac_ctx_update(hmac, (const uint8_t *) &session_id_time, > + sizeof(session_id_time)); > + > + /* add client IP and port */ > + switch (af_addr_size(from->addr.sa.sa_family)) > + { > + case AF_INET: > + hmac_ctx_update(hmac, (const uint8_t *) &from->addr.in4, > sizeof(struct sockaddr_in)); > + break; > + > + case AF_INET6: > + hmac_ctx_update(hmac, (const uint8_t *) &from->addr.in6, > sizeof(struct sockaddr_in6)); > + break; > + } > + > + /* add session id of client */ > + hmac_ctx_update(hmac, client_sid.id, SID_SIZE); > + > + hmac_ctx_final(hmac, result.hmac_result); > + > + return result.sid; > +} > + > +bool > +check_session_id_hmac(struct tls_pre_decrypt_state *state, > + const struct openvpn_sockaddr *from, > + hmac_ctx_t *hmac, > + int handwindow) > +{ > + if (!from) > + { > + return false; > + } > + > + struct buffer buf = state->newbuf; > + struct reliable_ack ack; > + > + if (!reliable_ack_parse(&buf, &ack, &state->server_session_id)) > + { > + return false; > + } > + Here we could put a comment like "allow adjacent timestamps". > + for (int i = -1; i<=1; i++) Why not call this variable "offset"? > + { > + struct session_id expected_id = > + calculate_session_id_hmac(state->peer_session_id, from, hmac, > handwindow, i); > + > + if (memcmp_constant_time(&expected_id, &state->server_session_id, > SID_SIZE)) > + { > + return true; > + } > + } > + return false; > +} > diff --git a/src/openvpn/ssl_pkt.h b/src/openvpn/ssl_pkt.h > index dd9361407..75cdc1c58 100644 > --- a/src/openvpn/ssl_pkt.h > +++ b/src/openvpn/ssl_pkt.h [...] > @@ -141,6 +146,48 @@ tls_pre_decrypt_lite(const struct tls_auth_standalone > *tas, > const struct link_socket_actual *from, > const struct buffer *buf); > > +/* Creates an SHA256 HMAC context with a random key that is used for the > + * session id. > + * > + * We do not support loading this from a config file since continuing session > + * between restarts of OpenVPN has never been supported and that includes > + * early session setup > + */ > +hmac_ctx_t *session_id_hmac_init(void); > + > +/** > + * Calculates the a HMAC based server session id based on a client session id > + * and socket addr. > + * > + * @param client_sid session id of the client > + * @param from link_socket from the client > + * @param hmac the hmac context to use for the calculation > + * @param handwindow the quantisation of the current time > + * @param offset offset to 'now' to use > + * @return the expected server session id > + */ > +struct session_id > +calculate_session_id_hmac(struct session_id client_sid, > + const struct openvpn_sockaddr *from, > + hmac_ctx_t *hmac, > + int handwindow, int offset); > + > +/** > + * Checks if a control packet has a correct HMAC server session id > + * > + * @param client_sid session id of the client > + * @param from link_socket from the client > + * @param hmac the hmac context to use for the calculation > + * @param handwindow the quantisation of the current time > + * @param offset offset to 'now' to use This function has no parameter "offset". > + * @return the expected server session id > + */ > +bool > +check_session_id_hmac(struct tls_pre_decrypt_state *state, > + const struct openvpn_sockaddr *from, > + hmac_ctx_t *hmac, > + int handwindow); > + > /* > * Write a control channel authentication record. > */ > diff --git a/tests/unit_tests/openvpn/test_pkt.c > b/tests/unit_tests/openvpn/test_pkt.c > index 77338cd3a..c4e23521d 100644 > --- a/tests/unit_tests/openvpn/test_pkt.c > +++ b/tests/unit_tests/openvpn/test_pkt.c [...] > @@ -325,8 +347,170 @@ test_tls_decrypt_lite_none(void **ut_state) [...] > +static void > +test_verify_hmac_none(void **ut_state) > +{ > + hmac_ctx_t *hmac = session_id_hmac_init(); > + > + struct link_socket_actual from = { 0 }; > + struct tls_auth_standalone tas = { 0 }; > + struct tls_pre_decrypt_state state = { 0 }; > + > + struct buffer buf = alloc_buf(1024); > + enum first_packet_verdict verdict; > + > + tas.tls_wrap.mode = TLS_WRAP_NONE; > + > + buf_reset_len(&buf); > + buf_write(&buf, client_ack_none_random_id, > sizeof(client_ack_none_random_id)); > + verdict = tls_pre_decrypt_lite(&tas, &state, &from, &buf); > + assert_int_equal(verdict, VERDICT_VALID_ACK_V1); > + > + check_session_id_hmac(&state, &from.dest, hmac, 30); Shouldn't this have some associated assert? > + > free_tls_pre_decrypt_state(&state); > free_buf(&buf); > + hmac_ctx_cleanup(hmac); > + hmac_ctx_free(hmac); > +} > + > +static hmac_ctx_t * > +init_static_hmac(void) > +{ > + ASSERT(md_valid("SHA256")); > + hmac_ctx_t *hmac_ctx = hmac_ctx_new(); > + > + uint8_t key[SHA256_DIGEST_LENGTH] = {1, 2, 3}; > + > + hmac_ctx_init(hmac_ctx, key, "SHA256"); > + return hmac_ctx; > +} > + > +static void > +test_calc_session_id_hmac_static(void **ut_state) > +{ > + hmac_ctx_t *hmac = init_static_hmac(); > + > + struct openvpn_sockaddr addr = {0 }; > + > + /* we do not use htons functions here since the hmac calculate function > + * also does not care about the endianness of the data but just assumes > + * the endianness doesn't change between calls */ > + addr.addr.in4.sin_family = AF_INET; > + addr.addr.in4.sin_addr.s_addr = 0xff000ff; > + addr.addr.in4.sin_port = 1194; > + > + > + struct session_id client_id = { {0, 1, 2, 3, 4, 5, 6, 7}}; > + > + now = 1005; I would suggest creating a variable or define here for the "100" handwindow. I think that would improve readability and since it must be synced it also makes the code more implicitely correct. > + struct session_id server_id = calculate_session_id_hmac(client_id, > &addr, hmac, 100, 0); > + > + struct session_id expected_server_id = { {0xba, 0x83, 0xa9, 0x00, 0x72, > 0xbd,0x93, 0xba }}; > + assert_memory_equal(expected_server_id.id, server_id.id, SID_SIZE); > + > + struct session_id server_id_m1 = calculate_session_id_hmac(client_id, > &addr, hmac, 100, -1); > + struct session_id server_id_p1 = calculate_session_id_hmac(client_id, > &addr, hmac, 100, 1); > + struct session_id server_id_p2 = calculate_session_id_hmac(client_id, > &addr, hmac, 100, 2); > + > + assert_memory_not_equal(expected_server_id.id, server_id_m1.id, > SID_SIZE); > + assert_memory_not_equal(expected_server_id.id, server_id_p1.id, > SID_SIZE); > + > + /* moving now should also move the calculated ids */ "moving now by more than half of handwindow should also move the calculated ids by offset 1" > + now = 1062; > + > + struct session_id server_id2_m2 = calculate_session_id_hmac(client_id, > &addr, hmac, 100, -2); > + struct session_id server_id2_m1 = calculate_session_id_hmac(client_id, > &addr, hmac, 100, -1); > + struct session_id server_id2 = calculate_session_id_hmac(client_id, > &addr, hmac, 100, 0); > + struct session_id server_id2_p1 = calculate_session_id_hmac(client_id, > &addr, hmac, 100, 1); > + > + assert_memory_equal(server_id2_m2.id, server_id_m1.id, SID_SIZE); > + assert_memory_equal(server_id2_m1.id, expected_server_id.id, SID_SIZE); > + assert_memory_equal(server_id2.id, server_id_p1.id, SID_SIZE); > + assert_memory_equal(server_id2_p1.id, server_id_p2.id, SID_SIZE); > + > + hmac_ctx_cleanup(hmac); > + hmac_ctx_free(hmac); > } > > static void [...] Regards, -- Frank Lichtenheld _______________________________________________ Openvpn-devel mailing list Openvpn-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openvpn-devel