From dd88835b0050150a6a5a020abe68497fa9caa28d Mon Sep 17 00:00:00 2001 From: Frederik Wedel-Heinen Date: Thu, 22 Feb 2024 08:09:38 +0100 Subject: [PATCH] Adds DTLS 1.3 ACK message functionality --- apps/lib/s_cb.c | 4 + include/internal/statem.h | 2 + include/openssl/ssl.h.in | 6 +- include/openssl/ssl3.h | 4 + include/openssl/x509_acert.h | 179 +++++++++++++++++++++ ssl/d1_lib.c | 9 ++ ssl/pqueue.c | 35 +++-- ssl/record/methods/tls_common.c | 3 +- ssl/record/rec_layer_d1.c | 20 ++- ssl/ssl_local.h | 21 ++- ssl/ssl_stat.c | 16 ++ ssl/statem/statem.c | 23 ++- ssl/statem/statem_clnt.c | 47 +++++- ssl/statem/statem_dtls.c | 223 ++++++++++++++++++++------- ssl/statem/statem_local.h | 2 + ssl/statem/statem_srvr.c | 61 +++++++- test/recipes/70-test_dtls13ack.t | 200 ++++++++++++++++++++++++ test/recipes/70-test_sslcbcpadding.t | 1 + test/recipes/70-test_sslrecords.t | 18 +++ test/recipes/70-test_tls13hrr.t | 8 +- util/perl/TLSProxy/Message.pm | 5 + util/perl/TLSProxy/Proxy.pm | 4 - util/perl/TLSProxy/Record.pm | 29 +++- util/perl/TLSProxy/RecordNumber.pm | 37 +++++ 24 files changed, 856 insertions(+), 101 deletions(-) create mode 100644 include/openssl/x509_acert.h create mode 100644 test/recipes/70-test_dtls13ack.t create mode 100644 util/perl/TLSProxy/RecordNumber.pm diff --git a/apps/lib/s_cb.c b/apps/lib/s_cb.c index 60355b5642272..5992868eb0378 100644 --- a/apps/lib/s_cb.c +++ b/apps/lib/s_cb.c @@ -670,6 +670,10 @@ void msg_cb(int write_p, int version, int content_type, const void *buf, /* type 23 */ str_content_type = ", ApplicationData"; break; + case SSL3_RT_ACK: + /* type 26 */ + str_content_type = ", ACK"; + break; case SSL3_RT_HEADER: /* type 256 */ str_content_type = ", RecordHeader"; diff --git a/include/internal/statem.h b/include/internal/statem.h index 136e6523660a5..c08f1e61455a6 100644 --- a/include/internal/statem.h +++ b/include/internal/statem.h @@ -104,6 +104,8 @@ struct ossl_statem_st { OSSL_HANDSHAKE_STATE hand_state; /* The handshake state requested by an API call (e.g. HelloRequest) */ OSSL_HANDSHAKE_STATE request_state; + /* The handshake state waiting for acknowledge */ + OSSL_HANDSHAKE_STATE defered_ack_state; int in_init; int read_state_first_init; /* true when we are actually in SSL_accept() or SSL_connect() */ diff --git a/include/openssl/ssl.h.in b/include/openssl/ssl.h.in index f04f5ac6f416a..46096ce88b470 100644 --- a/include/openssl/ssl.h.in +++ b/include/openssl/ssl.h.in @@ -1066,7 +1066,11 @@ typedef enum { TLS_ST_EARLY_DATA, TLS_ST_PENDING_EARLY_DATA_END, TLS_ST_CW_END_OF_EARLY_DATA, - TLS_ST_SR_END_OF_EARLY_DATA + TLS_ST_SR_END_OF_EARLY_DATA, + TLS_ST_CR_ACK, + TLS_ST_CW_ACK, + TLS_ST_SR_ACK, + TLS_ST_SW_ACK } OSSL_HANDSHAKE_STATE; /* diff --git a/include/openssl/ssl3.h b/include/openssl/ssl3.h index 4f076c6c9dfb9..1906ccb5491b4 100644 --- a/include/openssl/ssl3.h +++ b/include/openssl/ssl3.h @@ -220,6 +220,7 @@ extern "C" { # define SSL3_RT_ALERT 21 # define SSL3_RT_HANDSHAKE 22 # define SSL3_RT_APPLICATION_DATA 23 +# define SSL3_RT_ACK 26 /*RFC 9147*/ /* Pseudo content types to indicate additional parameters */ # define TLS1_RT_CRYPTO 0x1000 @@ -333,6 +334,9 @@ extern "C" { # define SSL3_MT_MESSAGE_HASH 254 # define DTLS1_MT_HELLO_VERIFY_REQUEST 3 +/* Dummy message type for handling ACK like a normal handshake message */ +# define DTLS13_MT_ACK 0x0126 + /* Dummy message type for handling CCS like a normal handshake message */ # define SSL3_MT_CHANGE_CIPHER_SPEC 0x0101 diff --git a/include/openssl/x509_acert.h b/include/openssl/x509_acert.h new file mode 100644 index 0000000000000..865f156de6b4b --- /dev/null +++ b/include/openssl/x509_acert.h @@ -0,0 +1,179 @@ +/* + * WARNING: do not edit! + * Generated by Makefile from include/openssl/x509_acert.h.in + * + * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + + + +#ifndef OPENSSL_X509_ACERT_H +# define OPENSSL_X509_ACERT_H +# pragma once + +# include +# include +# include + +typedef struct X509_acert_st X509_ACERT; +typedef struct X509_acert_info_st X509_ACERT_INFO; +typedef struct ossl_object_digest_info_st OSSL_OBJECT_DIGEST_INFO; +typedef struct ossl_issuer_serial_st OSSL_ISSUER_SERIAL; +typedef struct X509_acert_issuer_v2form_st X509_ACERT_ISSUER_V2FORM; + +DECLARE_ASN1_FUNCTIONS(X509_ACERT) +DECLARE_ASN1_DUP_FUNCTION(X509_ACERT) +DECLARE_ASN1_ITEM(X509_ACERT_INFO) +DECLARE_ASN1_ALLOC_FUNCTIONS(X509_ACERT_INFO) +DECLARE_ASN1_ALLOC_FUNCTIONS(OSSL_OBJECT_DIGEST_INFO) +DECLARE_ASN1_ALLOC_FUNCTIONS(OSSL_ISSUER_SERIAL) +DECLARE_ASN1_ALLOC_FUNCTIONS(X509_ACERT_ISSUER_V2FORM) + +# ifndef OPENSSL_NO_STDIO +X509_ACERT *d2i_X509_ACERT_fp(FILE *fp, X509_ACERT **acert); +int i2d_X509_ACERT_fp(FILE *fp, const X509_ACERT *acert); +# endif + +DECLARE_PEM_rw(X509_ACERT, X509_ACERT) + +X509_ACERT *d2i_X509_ACERT_bio(BIO *bp, X509_ACERT **acert); +int i2d_X509_ACERT_bio(BIO *bp, const X509_ACERT *acert); + +int X509_ACERT_sign(X509_ACERT *x, EVP_PKEY *pkey, const EVP_MD *md); +int X509_ACERT_sign_ctx(X509_ACERT *x, EVP_MD_CTX *ctx); +int X509_ACERT_verify(X509_ACERT *a, EVP_PKEY *r); + +# define X509_ACERT_VERSION_2 1 + +const GENERAL_NAMES *X509_ACERT_get0_holder_entityName(const X509_ACERT *x); +const OSSL_ISSUER_SERIAL *X509_ACERT_get0_holder_baseCertId(const X509_ACERT *x); +const OSSL_OBJECT_DIGEST_INFO * X509_ACERT_get0_holder_digest(const X509_ACERT *x); +const X509_NAME *X509_ACERT_get0_issuerName(const X509_ACERT *x); +long X509_ACERT_get_version(const X509_ACERT *x); +void X509_ACERT_get0_signature(const X509_ACERT *x, + const ASN1_BIT_STRING **psig, + const X509_ALGOR **palg); +int X509_ACERT_get_signature_nid(const X509_ACERT *x); +const X509_ALGOR *X509_ACERT_get0_info_sigalg(const X509_ACERT *x); +const ASN1_INTEGER *X509_ACERT_get0_serialNumber(const X509_ACERT *x); +const ASN1_TIME *X509_ACERT_get0_notBefore(const X509_ACERT *x); +const ASN1_TIME *X509_ACERT_get0_notAfter(const X509_ACERT *x); +const ASN1_BIT_STRING *X509_ACERT_get0_issuerUID(const X509_ACERT *x); + +int X509_ACERT_print(BIO *bp, X509_ACERT *x); +int X509_ACERT_print_ex(BIO *bp, X509_ACERT *x, unsigned long nmflags, + unsigned long cflag); + +int X509_ACERT_get_attr_count(const X509_ACERT *x); +int X509_ACERT_get_attr_by_NID(const X509_ACERT *x, int nid, int lastpos); +int X509_ACERT_get_attr_by_OBJ(const X509_ACERT *x, const ASN1_OBJECT *obj, + int lastpos); +X509_ATTRIBUTE *X509_ACERT_get_attr(const X509_ACERT *x, int loc); +X509_ATTRIBUTE *X509_ACERT_delete_attr(X509_ACERT *x, int loc); + +void *X509_ACERT_get_ext_d2i(const X509_ACERT *x, int nid, int *crit, int *idx); +int X509_ACERT_add1_ext_i2d(X509_ACERT *x, int nid, void *value, int crit, + unsigned long flags); +const STACK_OF(X509_EXTENSION) *X509_ACERT_get0_extensions(const X509_ACERT *x); + +# define OSSL_OBJECT_DIGEST_INFO_PUBLIC_KEY 0 +# define OSSL_OBJECT_DIGEST_INFO_PUBLIC_KEY_CERT 1 +# define OSSL_OBJECT_DIGEST_INFO_OTHER 2 /* must not be used in RFC 5755 profile */ +int X509_ACERT_set_version(X509_ACERT *x, long version); +void X509_ACERT_set0_holder_entityName(X509_ACERT *x, GENERAL_NAMES *name); +void X509_ACERT_set0_holder_baseCertId(X509_ACERT *x, OSSL_ISSUER_SERIAL *isss); +void X509_ACERT_set0_holder_digest(X509_ACERT *x, + OSSL_OBJECT_DIGEST_INFO *dinfo); + +int X509_ACERT_add1_attr(X509_ACERT *x, X509_ATTRIBUTE *attr); +int X509_ACERT_add1_attr_by_OBJ(X509_ACERT *x, const ASN1_OBJECT *obj, + int type, const void *bytes, int len); +int X509_ACERT_add1_attr_by_NID(X509_ACERT *x, int nid, int type, + const void *bytes, int len); +int X509_ACERT_add1_attr_by_txt(X509_ACERT *x, const char *attrname, int type, + const unsigned char *bytes, int len); +int X509_ACERT_add_attr_nconf(CONF *conf, const char *section, + X509_ACERT *acert); + +int X509_ACERT_set1_issuerName(X509_ACERT *x, const X509_NAME *name); +int X509_ACERT_set1_serialNumber(X509_ACERT *x, const ASN1_INTEGER *serial); +int X509_ACERT_set1_notBefore(X509_ACERT *x, const ASN1_GENERALIZEDTIME *time); +int X509_ACERT_set1_notAfter(X509_ACERT *x, const ASN1_GENERALIZEDTIME *time); + +void OSSL_OBJECT_DIGEST_INFO_get0_digest(const OSSL_OBJECT_DIGEST_INFO *o, + int *digestedObjectType, + const X509_ALGOR **digestAlgorithm, + const ASN1_BIT_STRING **digest); + +int OSSL_OBJECT_DIGEST_INFO_set1_digest(OSSL_OBJECT_DIGEST_INFO *o, + int digestedObjectType, + X509_ALGOR *digestAlgorithm, + ASN1_BIT_STRING *digest); + +const X509_NAME *OSSL_ISSUER_SERIAL_get0_issuer(const OSSL_ISSUER_SERIAL *isss); +const ASN1_INTEGER *OSSL_ISSUER_SERIAL_get0_serial(const OSSL_ISSUER_SERIAL *isss); +const ASN1_BIT_STRING *OSSL_ISSUER_SERIAL_get0_issuerUID(const OSSL_ISSUER_SERIAL *isss); + +int OSSL_ISSUER_SERIAL_set1_issuer(OSSL_ISSUER_SERIAL *isss, + const X509_NAME *issuer); +int OSSL_ISSUER_SERIAL_set1_serial(OSSL_ISSUER_SERIAL *isss, + const ASN1_INTEGER *serial); +int OSSL_ISSUER_SERIAL_set1_issuerUID(OSSL_ISSUER_SERIAL *isss, + const ASN1_BIT_STRING *uid); + +# define OSSL_IETFAS_OCTETS 0 +# define OSSL_IETFAS_OID 1 +# define OSSL_IETFAS_STRING 2 + +typedef struct OSSL_IETF_ATTR_SYNTAX_VALUE_st OSSL_IETF_ATTR_SYNTAX_VALUE; +typedef struct OSSL_IETF_ATTR_SYNTAX_st OSSL_IETF_ATTR_SYNTAX; +SKM_DEFINE_STACK_OF_INTERNAL(OSSL_IETF_ATTR_SYNTAX_VALUE, OSSL_IETF_ATTR_SYNTAX_VALUE, OSSL_IETF_ATTR_SYNTAX_VALUE) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_num(sk) OPENSSL_sk_num(ossl_check_const_OSSL_IETF_ATTR_SYNTAX_VALUE_sk_type(sk)) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_value(sk, idx) ((OSSL_IETF_ATTR_SYNTAX_VALUE *)OPENSSL_sk_value(ossl_check_const_OSSL_IETF_ATTR_SYNTAX_VALUE_sk_type(sk), (idx))) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_new(cmp) ((STACK_OF(OSSL_IETF_ATTR_SYNTAX_VALUE) *)OPENSSL_sk_new(ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_compfunc_type(cmp))) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_new_null() ((STACK_OF(OSSL_IETF_ATTR_SYNTAX_VALUE) *)OPENSSL_sk_new_null()) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_new_reserve(cmp, n) ((STACK_OF(OSSL_IETF_ATTR_SYNTAX_VALUE) *)OPENSSL_sk_new_reserve(ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_compfunc_type(cmp), (n))) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_reserve(sk, n) OPENSSL_sk_reserve(ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_sk_type(sk), (n)) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_free(sk) OPENSSL_sk_free(ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_sk_type(sk)) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_zero(sk) OPENSSL_sk_zero(ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_sk_type(sk)) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_delete(sk, i) ((OSSL_IETF_ATTR_SYNTAX_VALUE *)OPENSSL_sk_delete(ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_sk_type(sk), (i))) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_delete_ptr(sk, ptr) ((OSSL_IETF_ATTR_SYNTAX_VALUE *)OPENSSL_sk_delete_ptr(ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_sk_type(sk), ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_type(ptr))) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_push(sk, ptr) OPENSSL_sk_push(ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_sk_type(sk), ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_type(ptr)) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_unshift(sk, ptr) OPENSSL_sk_unshift(ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_sk_type(sk), ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_type(ptr)) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_pop(sk) ((OSSL_IETF_ATTR_SYNTAX_VALUE *)OPENSSL_sk_pop(ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_sk_type(sk))) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_shift(sk) ((OSSL_IETF_ATTR_SYNTAX_VALUE *)OPENSSL_sk_shift(ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_sk_type(sk))) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_pop_free(sk, freefunc) OPENSSL_sk_pop_free(ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_sk_type(sk),ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_freefunc_type(freefunc)) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_insert(sk, ptr, idx) OPENSSL_sk_insert(ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_sk_type(sk), ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_type(ptr), (idx)) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_set(sk, idx, ptr) ((OSSL_IETF_ATTR_SYNTAX_VALUE *)OPENSSL_sk_set(ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_sk_type(sk), (idx), ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_type(ptr))) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_find(sk, ptr) OPENSSL_sk_find(ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_sk_type(sk), ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_type(ptr)) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_find_ex(sk, ptr) OPENSSL_sk_find_ex(ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_sk_type(sk), ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_type(ptr)) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_find_all(sk, ptr, pnum) OPENSSL_sk_find_all(ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_sk_type(sk), ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_type(ptr), pnum) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_sort(sk) OPENSSL_sk_sort(ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_sk_type(sk)) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_is_sorted(sk) OPENSSL_sk_is_sorted(ossl_check_const_OSSL_IETF_ATTR_SYNTAX_VALUE_sk_type(sk)) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_dup(sk) ((STACK_OF(OSSL_IETF_ATTR_SYNTAX_VALUE) *)OPENSSL_sk_dup(ossl_check_const_OSSL_IETF_ATTR_SYNTAX_VALUE_sk_type(sk))) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_deep_copy(sk, copyfunc, freefunc) ((STACK_OF(OSSL_IETF_ATTR_SYNTAX_VALUE) *)OPENSSL_sk_deep_copy(ossl_check_const_OSSL_IETF_ATTR_SYNTAX_VALUE_sk_type(sk), ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_copyfunc_type(copyfunc), ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_freefunc_type(freefunc))) +#define sk_OSSL_IETF_ATTR_SYNTAX_VALUE_set_cmp_func(sk, cmp) ((sk_OSSL_IETF_ATTR_SYNTAX_VALUE_compfunc)OPENSSL_sk_set_cmp_func(ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_sk_type(sk), ossl_check_OSSL_IETF_ATTR_SYNTAX_VALUE_compfunc_type(cmp))) + + +DECLARE_ASN1_ITEM(OSSL_IETF_ATTR_SYNTAX_VALUE) +DECLARE_ASN1_ALLOC_FUNCTIONS(OSSL_IETF_ATTR_SYNTAX_VALUE) +DECLARE_ASN1_FUNCTIONS(OSSL_IETF_ATTR_SYNTAX) + +const GENERAL_NAMES * +OSSL_IETF_ATTR_SYNTAX_get0_policyAuthority(const OSSL_IETF_ATTR_SYNTAX *a); +void OSSL_IETF_ATTR_SYNTAX_set0_policyAuthority(OSSL_IETF_ATTR_SYNTAX *a, + GENERAL_NAMES *names); + +int OSSL_IETF_ATTR_SYNTAX_get_value_num(const OSSL_IETF_ATTR_SYNTAX *a); +void *OSSL_IETF_ATTR_SYNTAX_get0_value(const OSSL_IETF_ATTR_SYNTAX *a, + int ind, int *type); +int OSSL_IETF_ATTR_SYNTAX_add1_value(OSSL_IETF_ATTR_SYNTAX *a, int type, + void *data); +int OSSL_IETF_ATTR_SYNTAX_print(BIO *bp, OSSL_IETF_ATTR_SYNTAX *a, int indent); + +#endif diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c index ddcce661332b2..db83b14e224c5 100644 --- a/ssl/d1_lib.c +++ b/ssl/d1_lib.c @@ -139,6 +139,15 @@ void dtls1_clear_received_buffer(SSL_CONNECTION *s) } } +void dtls1_remove_sent_buffer_item(struct pqueue_st *pq, unsigned char *prio64be) { + pitem *item = NULL; + + while ((item = pqueue_pop_item(pq, prio64be)) != NULL) { + dtls1_sent_msg_free((dtls_sent_msg *)item->data); + pitem_free(item); + } +} + void dtls1_clear_sent_buffer(SSL_CONNECTION *s) { pitem *item = NULL; diff --git a/ssl/pqueue.c b/ssl/pqueue.c index db161e25da1fb..9dd6045e031de 100644 --- a/ssl/pqueue.c +++ b/ssl/pqueue.c @@ -96,31 +96,46 @@ pitem *pqueue_pop(pqueue *pq) return item; } -pitem *pqueue_find(pqueue *pq, unsigned char *prio64be) +static pitem *pqueue_find_and_pop(pqueue *pq, unsigned char *prio64be, int pop) { - pitem *next; + pitem *curr; + pitem *prev = NULL; pitem *found = NULL; if (pq->items == NULL) return NULL; - for (next = pq->items; next->next != NULL; next = next->next) { - if (memcmp(next->priority, prio64be, 8) == 0) { - found = next; + for (curr = pq->items; curr->next != NULL; curr = curr->next) { + if (memcmp(curr->priority, prio64be, 8) == 0) { + found = curr; break; } + prev = curr; } /* check the one last node */ - if (memcmp(next->priority, prio64be, 8) == 0) - found = next; - - if (!found) - return NULL; + if (found == NULL && memcmp(curr->priority, prio64be, 8) == 0) + found = curr; + + if (found != NULL && pop) { + if (prev == NULL) + pq->items = found->next; + else + prev->next = found->next; + } return found; } +pitem *pqueue_find(pqueue *pq, unsigned char *prio64be) { + return pqueue_find_and_pop(pq, prio64be, 0); +} + +pitem *pqueue_pop_item(pqueue *pq, unsigned char *prio64be) +{ + return pqueue_find_and_pop(pq, prio64be, 1); +} + pitem *pqueue_iterator(pqueue *pq) { return pqueue_peek(pq); diff --git a/ssl/record/methods/tls_common.c b/ssl/record/methods/tls_common.c index 0aed93eba9c0c..7f08d99e9c6c2 100644 --- a/ssl/record/methods/tls_common.c +++ b/ssl/record/methods/tls_common.c @@ -1078,7 +1078,8 @@ int tls13_common_post_process_record(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *rec) { if (rec->type != SSL3_RT_APPLICATION_DATA && rec->type != SSL3_RT_ALERT - && rec->type != SSL3_RT_HANDSHAKE) { + && rec->type != SSL3_RT_HANDSHAKE + && rec->type != SSL3_RT_ACK) { RLAYERfatal(rl, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_BAD_RECORD_TYPE); return 0; } diff --git a/ssl/record/rec_layer_d1.c b/ssl/record/rec_layer_d1.c index 932db8d177d0f..bfa54696eec54 100644 --- a/ssl/record/rec_layer_d1.c +++ b/ssl/record/rec_layer_d1.c @@ -314,13 +314,14 @@ int dtls1_read_bytes(SSL *s, uint8_t type, uint8_t *recvd_type, } if (type == rr->type - || (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC - && type == SSL3_RT_HANDSHAKE && recvd_type != NULL - && !is_dtls13)) { + || (type == SSL3_RT_HANDSHAKE + && ((!is_dtls13 && recvd_type != NULL && rr->type == SSL3_RT_CHANGE_CIPHER_SPEC) + || (is_dtls13 && rr->type == SSL3_RT_ACK)))) { /* * SSL3_RT_APPLICATION_DATA or * SSL3_RT_HANDSHAKE or - * SSL3_RT_CHANGE_CIPHER_SPEC + * SSL3_RT_CHANGE_CIPHER_SPEC or + * SSL3_RT_ACK */ /* * make sure that we are not getting application data when we are @@ -489,15 +490,17 @@ int dtls1_read_bytes(SSL *s, uint8_t type, uint8_t *recvd_type, /* * Unexpected handshake message (Client Hello, or protocol violation) */ - if (rr->type == SSL3_RT_HANDSHAKE && !ossl_statem_get_in_handshake(sc)) { + if (!ossl_statem_get_in_handshake(sc) + && (rr->type == SSL3_RT_HANDSHAKE || rr->type == SSL3_RT_ACK)) { unsigned char msg_type; /* * This may just be a stale retransmit. Also sanity check that we have * at least enough record bytes for a message header */ - if (rr->epoch != sc->rlayer.d->r_epoch - || rr->length < DTLS1_HM_HEADER_LENGTH) { + if (rr->type == SSL3_RT_HANDSHAKE + && (rr->epoch != sc->rlayer.d->r_epoch + || rr->length < DTLS1_HM_HEADER_LENGTH)) { if (!ssl_release_record(sc, rr, 0)) return -1; goto start; @@ -509,7 +512,7 @@ int dtls1_read_bytes(SSL *s, uint8_t type, uint8_t *recvd_type, * If we are server, we may have a repeated FINISHED of the client * here, then retransmit our CCS and FINISHED. */ - if (msg_type == SSL3_MT_FINISHED) { + if (rr->type == SSL3_RT_HANDSHAKE && msg_type == SSL3_MT_FINISHED) { if (dtls1_check_timeout_num(sc) < 0) { /* SSLfatal) already called */ return -1; @@ -585,6 +588,7 @@ int dtls1_read_bytes(SSL *s, uint8_t type, uint8_t *recvd_type, case SSL3_RT_CHANGE_CIPHER_SPEC: case SSL3_RT_ALERT: case SSL3_RT_HANDSHAKE: + case SSL3_RT_ACK: /* * we already handled all of these, with the possible exception of * SSL3_RT_HANDSHAKE when ossl_statem_get_in_handshake(s) is true, but diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h index ad2a913fdfa6c..e891a78a1cf35 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -1967,12 +1967,14 @@ void pqueue_free(pqueue *pq); pitem *pqueue_insert(pqueue *pq, pitem *item); pitem *pqueue_peek(pqueue *pq); pitem *pqueue_pop(pqueue *pq); +pitem *pqueue_pop_item(pqueue *pq, unsigned char *prio64be); pitem *pqueue_find(pqueue *pq, unsigned char *prio64be); pitem *pqueue_iterator(pqueue *pq); pitem *pqueue_next(piterator *iter); size_t pqueue_size(pqueue *pq); typedef struct dtls_msg_info_st { + unsigned char record_type; unsigned char msg_type; size_t msg_body_len; unsigned short msg_seq; @@ -1985,6 +1987,14 @@ typedef struct dtls_sent_msg_st { struct dtls1_retransmit_state saved_retransmit_state; } dtls_sent_msg; +#define DTLS_ACK_REC_NUM_LEN 32 + +/* rfc9147, section 4 */ +typedef struct dtls1_record_number_st { + uint64_t epoch; + uint64_t sequence_number; +} DTLS1_RECORD_NUMBER; + typedef struct dtls1_state_st { unsigned char cookie[DTLS1_COOKIE_LENGTH]; size_t cookie_len; @@ -2017,6 +2027,10 @@ typedef struct dtls1_state_st { int shutdown_received; # endif + /* Sequence numbers that are to be acknowledged */ + DTLS1_RECORD_NUMBER ack_rec_num[DTLS_ACK_REC_NUM_LEN]; + size_t ack_rec_num_count; + DTLS_timer_cb timer_cb; } DTLS1_STATE; @@ -2721,11 +2735,14 @@ int dtls1_write_app_data_bytes(SSL *s, uint8_t type, const void *buf_, __owur int dtls1_read_failed(SSL_CONNECTION *s, int code); __owur int dtls1_buffer_sent_message(SSL_CONNECTION *s, int record_type); __owur int dtls1_retransmit_message(SSL_CONNECTION *s, unsigned short seq, - int *found); -__owur int dtls1_get_queue_priority(unsigned short seq, int is_ccs); + int record_type, int *found); +void dtls1_get_queue_priority(unsigned char *prio64be, unsigned short seq, + int record_type); int dtls1_retransmit_sent_messages(SSL_CONNECTION *s); void dtls1_clear_received_buffer(SSL_CONNECTION *s); void dtls1_clear_sent_buffer(SSL_CONNECTION *s); +void dtls1_remove_sent_buffer_item(struct pqueue_st *pq, + unsigned char *prio64be); __owur OSSL_TIME dtls1_default_timeout(void); __owur int dtls1_get_timeout(const SSL_CONNECTION *s, OSSL_TIME *timeleft); __owur int dtls1_check_timeout_num(SSL_CONNECTION *s); diff --git a/ssl/ssl_stat.c b/ssl/ssl_stat.c index c7fbd4ed02035..ed2b14f7d8e0f 100644 --- a/ssl/ssl_stat.c +++ b/ssl/ssl_stat.c @@ -123,6 +123,14 @@ const char *SSL_state_string_long(const SSL *s) return "TLSv1.3 write end of early data"; case TLS_ST_SR_END_OF_EARLY_DATA: return "TLSv1.3 read end of early data"; + case TLS_ST_CR_ACK: + return "DTLSv1.3 read server ack"; + case TLS_ST_CW_ACK: + return "DTLSv1.3 write client ack"; + case TLS_ST_SR_ACK: + return "DTLSv1.3 read client ack"; + case TLS_ST_SW_ACK: + return "DTLSv1.3 write server ack"; default: return "unknown state"; } @@ -240,6 +248,14 @@ const char *SSL_state_string(const SSL *s) return "TWEOED"; case TLS_ST_SR_END_OF_EARLY_DATA: return "TWEOED"; + case TLS_ST_CR_ACK: + return "TRCACK"; + case TLS_ST_CW_ACK: + return "TWCACK"; + case TLS_ST_SR_ACK: + return "TRSACK"; + case TLS_ST_SW_ACK: + return "TWSACK"; default: return "UNKWN"; } diff --git a/ssl/statem/statem.c b/ssl/statem/statem.c index 1a1012e40dcad..d15b73bcc66dc 100644 --- a/ssl/statem/statem.c +++ b/ssl/statem/statem.c @@ -744,17 +744,22 @@ static SUB_STATE_RETURN read_state_machine(SSL_CONNECTION *s) */ static int statem_do_write(SSL_CONNECTION *s) { + int record_type; OSSL_STATEM *st = &s->statem; if (st->hand_state == TLS_ST_CW_CHANGE - || st->hand_state == TLS_ST_SW_CHANGE) { - if (SSL_CONNECTION_IS_DTLS(s)) - return dtls1_do_write(s, SSL3_RT_CHANGE_CIPHER_SPEC); - else - return ssl3_do_write(s, SSL3_RT_CHANGE_CIPHER_SPEC); - } else { + || st->hand_state == TLS_ST_SW_CHANGE) + record_type = SSL3_RT_CHANGE_CIPHER_SPEC; + else if (st->hand_state == TLS_ST_CW_ACK + || st->hand_state == TLS_ST_SW_ACK) + record_type = SSL3_RT_ACK; + else return ssl_do_write(s); - } + + if (SSL_CONNECTION_IS_DTLS(s)) + return dtls1_do_write(s, record_type); + else + return ssl3_do_write(s, record_type); } /* @@ -917,7 +922,9 @@ static SUB_STATE_RETURN write_state_machine(SSL_CONNECTION *s) /* Fall through */ case WRITE_STATE_SEND: - if (SSL_CONNECTION_IS_DTLS(s) && st->use_timer) { + if (SSL_CONNECTION_IS_DTLS(s) && st->use_timer + && st->hand_state != TLS_ST_CW_ACK + && st->hand_state != TLS_ST_SW_ACK) { dtls1_start_timer(s); } ret = statem_do_write(s); diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c index 218132631d72c..dcf929b6e972c 100644 --- a/ssl/statem/statem_clnt.c +++ b/ssl/statem/statem_clnt.c @@ -179,6 +179,13 @@ static int ossl_statem_client13_read_transition(SSL_CONNECTION *s, int mt) } break; + case TLS_ST_CW_KEY_UPDATE: + case TLS_ST_CW_FINISHED: + if (mt == DTLS13_MT_ACK) { + st->hand_state = TLS_ST_CR_ACK; + return 1; + } + case TLS_ST_OK: if (mt == SSL3_MT_NEWSESSION_TICKET) { st->hand_state = TLS_ST_CR_SESSION_TICKET; @@ -506,9 +513,31 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL_CONNECTION *s) return WRITE_TRAN_CONTINUE; case TLS_ST_CR_KEY_UPDATE: - case TLS_ST_CW_KEY_UPDATE: + if (SSL_CONNECTION_IS_DTLS13(s)) { + st->hand_state = TLS_ST_CW_ACK; + return WRITE_TRAN_CONTINUE; + } + /* Fall-through */ case TLS_ST_CR_SESSION_TICKET: + if (SSL_CONNECTION_IS_DTLS13(s)) { + st->hand_state = TLS_ST_CW_ACK; + return WRITE_TRAN_CONTINUE; + } + /* Fall-through */ + case TLS_ST_CW_KEY_UPDATE: case TLS_ST_CW_FINISHED: + if (SSL_CONNECTION_IS_DTLS13(s)) + /* We wait for ACK */ + return WRITE_TRAN_FINISHED; + else + st->hand_state = TLS_ST_OK; + return WRITE_TRAN_CONTINUE; + + case TLS_ST_CR_ACK: + st->hand_state = TLS_ST_OK; + return WRITE_TRAN_CONTINUE; + + case TLS_ST_CW_ACK: st->hand_state = TLS_ST_OK; return WRITE_TRAN_CONTINUE; @@ -909,6 +938,11 @@ WORK_STATE ossl_statem_client_post_work(SSL_CONNECTION *s, WORK_STATE wst) return WORK_ERROR; } break; + + case TLS_ST_CW_ACK: + if (statem_flush(s) != 1) + return WORK_MORE_A; + break; } return WORK_FINISHED_CONTINUE; @@ -993,6 +1027,11 @@ int ossl_statem_client_construct_message(SSL_CONNECTION *s, *confunc = tls_construct_key_update; *mt = SSL3_MT_KEY_UPDATE; break; + + case TLS_ST_CW_ACK: + *confunc = dtls_construct_ack; + *mt = DTLS13_MT_ACK; + break; } return 1; @@ -1058,6 +1097,9 @@ size_t ossl_statem_client_max_message_size(SSL_CONNECTION *s) case TLS_ST_CR_KEY_UPDATE: return KEY_UPDATE_MAX_LENGTH; + + case TLS_ST_CR_ACK: + return 2 + (0x1 << 16) - 1; } } @@ -1121,6 +1163,9 @@ MSG_PROCESS_RETURN ossl_statem_client_process_message(SSL_CONNECTION *s, case TLS_ST_CR_KEY_UPDATE: return tls_process_key_update(s, pkt); + + case TLS_ST_CR_ACK: + return dtls_process_ack(s, pkt); } } diff --git a/ssl/statem/statem_dtls.c b/ssl/statem/statem_dtls.c index 4197ded1004da..29c0dbd7df06b 100644 --- a/ssl/statem/statem_dtls.c +++ b/ssl/statem/statem_dtls.c @@ -165,22 +165,11 @@ int dtls1_do_write(SSL_CONNECTION *s, uint8_t recordtype) size_t written; size_t curr_mtu; int retry = 1; - size_t len, overhead, used_len, msg_len = 0; + size_t len, overhead, used_len; SSL *ssl = SSL_CONNECTION_GET_SSL(s); - unsigned char *data = (unsigned char *)s->init_buf->data; - unsigned short msg_seq = s->d1->w_msg.msg_seq; - unsigned char msg_type = 0; - - if (recordtype == SSL3_RT_HANDSHAKE) { - msg_type = *data++; - l3n2(data, msg_len); - } else if (ossl_assert(recordtype == SSL3_RT_CHANGE_CIPHER_SPEC)) { - msg_type = SSL3_MT_CCS; - msg_len = 0; /* SSL3_RT_CHANGE_CIPHER_SPEC */ - } else { - /* Other record types are not supported */ - return -1; - } + size_t msg_len = s->d1->w_msg.msg_body_len; /* Only used for recordtype == SSL3_RT_HANDSHAKE */ + unsigned short msg_seq = s->d1->w_msg.msg_seq; /* Only used for recordtype == SSL3_RT_HANDSHAKE */ + unsigned char msg_type = s->d1->w_msg.msg_type; /* Only used for recordtype == SSL3_RT_HANDSHAKE */ if (!dtls1_query_mtu(s)) return -1; @@ -369,6 +358,7 @@ int dtls_get_message(SSL_CONNECTION *s, int *mt) unsigned char *rec_data; size_t tmplen; int errtype; + int record_type; s->d1->r_msg_seq = 0; @@ -386,9 +376,17 @@ int dtls_get_message(SSL_CONNECTION *s, int *mt) rec_data = (unsigned char *)s->init_buf->data; - if (*mt == SSL3_MT_CHANGE_CIPHER_SPEC) { + /* Convert from possible dummy message type */ + if (*mt == SSL3_MT_CHANGE_CIPHER_SPEC) + record_type = SSL3_RT_CHANGE_CIPHER_SPEC; + else if (*mt == DTLS13_MT_ACK) + record_type = SSL3_RT_ACK; + else + record_type = SSL3_RT_HANDSHAKE; + + if (record_type != SSL3_RT_HANDSHAKE) { if (s->msg_callback) { - s->msg_callback(0, s->version, SSL3_RT_CHANGE_CIPHER_SPEC, + s->msg_callback(0, s->version, record_type, rec_data, 1, SSL_CONNECTION_GET_SSL(s), s->msg_callback_arg); } @@ -421,7 +419,11 @@ int dtls_get_message_body(SSL_CONNECTION *s, size_t *len) unsigned char *msg = (unsigned char *)s->init_buf->data; size_t msg_len = s->init_num + DTLS1_HM_HEADER_LENGTH; - if (s->s3.tmp.message_type == SSL3_MT_CHANGE_CIPHER_SPEC) { + switch (s->s3.tmp.message_type) { + default: + break; + case DTLS13_MT_ACK: + case SSL3_MT_CHANGE_CIPHER_SPEC: /* Nothing to be done */ goto end; } @@ -698,6 +700,7 @@ static int dtls1_reassemble_fragment(SSL_CONNECTION *s, } if (item == NULL) { + const size_t rec_num_idx = s->d1->ack_rec_num_count; item = pitem_new(seq64be, frag); if (item == NULL) goto err; @@ -711,6 +714,11 @@ static int dtls1_reassemble_fragment(SSL_CONNECTION *s, */ if (!ossl_assert(item != NULL)) goto err; + + s->d1->ack_rec_num[rec_num_idx].epoch = dtls1_get_epoch(s, SSL3_CC_READ); + s->d1->ack_rec_num[rec_num_idx].sequence_number = frag->msg_header.seq; + + s->d1->ack_rec_num_count++; } return DTLS1_HM_FRAGMENT_RETRY; @@ -895,6 +903,33 @@ static int dtls_get_reassembled_message(SSL_CONNECTION *s, int *errtype, *len = readbytes - 1; return 1; } + if (recvd_type == SSL3_RT_ACK) { + if (readbytes == DTLS1_HM_HEADER_LENGTH) { + const size_t first_readbytes = readbytes; + + p += DTLS1_HM_HEADER_LENGTH; + + i = ssl->method->ssl_read_bytes(ssl, SSL3_RT_HANDSHAKE, NULL, p, + s->init_num - DTLS1_HM_HEADER_LENGTH, + 0, &readbytes); + readbytes += first_readbytes; + /* + * This shouldn't ever fail due to NBIO because we already checked + * that we have enough data in the record + */ + if (i <= 0) { + s->rwstate = SSL_READING; + *len = 0; + return 0; + } + } + s->init_num = readbytes; + s->init_msg = s->init_buf->data; + s->s3.tmp.message_type = DTLS13_MT_ACK; + s->s3.tmp.message_size = readbytes; + *len = readbytes; + return 1; + } /* Handshake fails if message header is incomplete */ if (readbytes != DTLS1_HM_HEADER_LENGTH) { @@ -1048,6 +1083,92 @@ CON_FUNC_RETURN dtls_construct_change_cipher_spec(SSL_CONNECTION *s, return CON_FUNC_SUCCESS; } +CON_FUNC_RETURN dtls_construct_ack(SSL_CONNECTION *s, WPACKET *pkt) { + if (!WPACKET_start_sub_packet_u16(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return CON_FUNC_ERROR; + } + + for (size_t i = 0; i < s->d1->ack_rec_num_count; i++) { + /* + * rfc9147: section 4. + * + * Record numbers are encoded as + * struct { + * uint64 epoch; + * uint64 sequence_number; + * } RecordNumber; + */ + const uint64_t epoch = s->d1->ack_rec_num[i].epoch; + const uint64_t sequence_number = s->d1->ack_rec_num[i].sequence_number; + + /* + * rfc9147: + * During the handshake, ACK records MUST be sent with an epoch which + * is equal to or higher than the record which is being acknowledged + */ + if (epoch >= dtls1_get_epoch(s, SSL3_CC_WRITE)) + if(!WPACKET_put_bytes_u64(pkt, epoch) + || !WPACKET_put_bytes_u64(pkt, sequence_number)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return CON_FUNC_ERROR; + } + } + + if (!WPACKET_close(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return CON_FUNC_ERROR; + } + + /* Avoid acknowledging the same record numbers again */ + s->d1->ack_rec_num_count = 0; + + return CON_FUNC_SUCCESS; +} + +MSG_PROCESS_RETURN dtls_process_ack(SSL_CONNECTION *s, PACKET *pkt) +{ + PACKET record_numbers; + + if (PACKET_remaining(pkt) == 0) + return MSG_PROCESS_FINISHED_READING; + + if (!PACKET_get_length_prefixed_2(pkt, &record_numbers)) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_LENGTH_TOO_LONG); + return MSG_PROCESS_ERROR; + } + + while (PACKET_remaining(&record_numbers) > 0) { + /* + * rfc9147: section 4. + * + * Record numbers are encoded as + * struct { + * uint64 epoch; + * uint64 sequence_number; + * } RecordNumber; + */ + + unsigned char prio64be[8]; + uint64_t epoch; + uint64_t sequence_number; + + if (!PACKET_get_net_8(&record_numbers, &epoch) + || !PACKET_get_net_8(&record_numbers, &sequence_number)) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_LENGTH_TOO_LONG); + return MSG_PROCESS_ERROR; + } + + if (dtls1_get_epoch(s, SSL3_CC_WRITE) == epoch) { + dtls1_get_queue_priority(prio64be, sequence_number, 0); + dtls1_remove_sent_buffer_item(s->d1->sent_messages, prio64be); + } + } + + return MSG_PROCESS_FINISHED_READING; +} + + #ifndef OPENSSL_NO_SCTP /* * Wait for a dry event. Should only be called at a point in the handshake @@ -1115,7 +1236,8 @@ int dtls1_read_failed(SSL_CONNECTION *s, int code) return dtls1_handle_timeout(s); } -int dtls1_get_queue_priority(unsigned short seq, int record_type) +void dtls1_get_queue_priority(unsigned char *prio64be, unsigned short seq, + int record_type) { /* * The index of the retransmission queue actually is the message sequence @@ -1128,7 +1250,10 @@ int dtls1_get_queue_priority(unsigned short seq, int record_type) * priority queues) and fits in the unsigned short variable. */ int lsb = (record_type == SSL3_RT_CHANGE_CIPHER_SPEC ? 1 : 0); - return seq * 2 - lsb; + const uint16_t prio = seq * 2 - lsb; + memset(prio64be, 0, 8); + prio64be[6] = (unsigned char)(prio >> 8); + prio64be[7] = (unsigned char)(prio); } int dtls1_retransmit_sent_messages(SSL_CONNECTION *s) @@ -1138,12 +1263,10 @@ int dtls1_retransmit_sent_messages(SSL_CONNECTION *s) int found = 0; for (item = pqueue_next(&iter); item != NULL; item = pqueue_next(&iter)) { - int prio; dtls_sent_msg *sent_msg = (dtls_sent_msg *)item->data; - prio = dtls1_get_queue_priority(sent_msg->msg_info.msg_seq, sent_msg->record_type); - - if (dtls1_retransmit_message(s, (unsigned short)prio, &found) <= 0) + if (dtls1_retransmit_message(s, sent_msg->msg_info.msg_seq, + sent_msg->record_type, &found) <= 0) return -1; } @@ -1156,7 +1279,6 @@ int dtls1_buffer_sent_message(SSL_CONNECTION *s, int record_type) dtls_sent_msg *sent_msg; unsigned char seq64be[8]; size_t headerlen; - int prio; /* * this function is called immediately after a message has been @@ -1182,19 +1304,14 @@ int dtls1_buffer_sent_message(SSL_CONNECTION *s, int record_type) return 0; } - sent_msg->msg_info.msg_body_len = s->d1->w_msg.msg_body_len; - sent_msg->msg_info.msg_seq = s->d1->w_msg.msg_seq; - sent_msg->msg_info.msg_type = s->d1->w_msg.msg_type; + memcpy(&sent_msg->msg_info, &s->d1->w_msg, sizeof(s->d1->w_msg)); sent_msg->record_type = record_type; /* save current state */ sent_msg->saved_retransmit_state.wrlmethod = s->rlayer.wrlmethod; sent_msg->saved_retransmit_state.wrl = s->rlayer.wrl; - prio = dtls1_get_queue_priority(sent_msg->msg_info.msg_seq, sent_msg->record_type); - memset(seq64be, 0, sizeof(seq64be)); - seq64be[6] = (unsigned char)(prio >> 8); - seq64be[7] = (unsigned char)prio; + dtls1_get_queue_priority(seq64be, sent_msg->msg_info.msg_seq, sent_msg->record_type); item = pitem_new(seq64be, sent_msg); if (item == NULL) { @@ -1206,7 +1323,8 @@ int dtls1_buffer_sent_message(SSL_CONNECTION *s, int record_type) return 1; } -int dtls1_retransmit_message(SSL_CONNECTION *s, unsigned short seq, int *found) +int dtls1_retransmit_message(SSL_CONNECTION *s, unsigned short seq, + int record_type, int *found) { int ret; /* XDTLS: for now assuming that read/writes are blocking */ @@ -1217,9 +1335,7 @@ int dtls1_retransmit_message(SSL_CONNECTION *s, unsigned short seq, int *found) struct dtls1_retransmit_state saved_state; /* XDTLS: the requested message ought to be found, otherwise error */ - memset(seq64be, 0, sizeof(seq64be)); - seq64be[6] = (unsigned char)(seq >> 8); - seq64be[7] = (unsigned char)seq; + dtls1_get_queue_priority(seq64be, seq, record_type); item = pqueue_find(s->d1->sent_messages, seq64be); if (item == NULL) { @@ -1240,9 +1356,7 @@ int dtls1_retransmit_message(SSL_CONNECTION *s, unsigned short seq, int *found) sent_msg->msg_info.msg_body_len + header_length); s->init_num = sent_msg->msg_info.msg_body_len + header_length; - s->d1->w_msg.msg_type = sent_msg->msg_info.msg_type; - s->d1->w_msg.msg_body_len = sent_msg->msg_info.msg_body_len; - s->d1->w_msg.msg_seq = sent_msg->msg_info.msg_seq; + memcpy(&s->d1->w_msg, &sent_msg->msg_info, sizeof(sent_msg->msg_info)); /* save current state */ saved_state.wrlmethod = s->rlayer.wrlmethod; @@ -1274,24 +1388,25 @@ int dtls1_retransmit_message(SSL_CONNECTION *s, unsigned short seq, int *found) int dtls1_set_handshake_header(SSL_CONNECTION *s, WPACKET *pkt, int htype) { - if (htype == SSL3_MT_CHANGE_CIPHER_SPEC) { - s->d1->handshake_write_seq = s->d1->next_handshake_write_seq; + s->d1->handshake_write_seq = s->d1->next_handshake_write_seq; + s->d1->w_msg.msg_seq = s->d1->handshake_write_seq; + s->d1->w_msg.msg_body_len = 0; + if (htype == SSL3_MT_CHANGE_CIPHER_SPEC) { + s->d1->w_msg.record_type = SSL3_RT_CHANGE_CIPHER_SPEC; s->d1->w_msg.msg_type = SSL3_MT_CCS; - s->d1->w_msg.msg_body_len = 0; - s->d1->w_msg.msg_seq = s->d1->handshake_write_seq; if (!WPACKET_put_bytes_u8(pkt, SSL3_MT_CCS)) return 0; + } else if (htype == DTLS13_MT_ACK) { + s->d1->w_msg.record_type = SSL3_RT_ACK; + s->d1->w_msg.msg_type = 0; } else { size_t subpacket_offset = DTLS1_HM_HEADER_LENGTH - SSL3_HM_HEADER_LENGTH; - s->d1->handshake_write_seq = s->d1->next_handshake_write_seq; s->d1->next_handshake_write_seq++; - + s->d1->w_msg.record_type = SSL3_RT_HANDSHAKE; s->d1->w_msg.msg_type = htype; - s->d1->w_msg.msg_body_len = 0; - s->d1->w_msg.msg_seq = s->d1->handshake_write_seq; /* Set the content type and 3 bytes for the message len */ if (!WPACKET_put_bytes_u8(pkt, htype) @@ -1309,26 +1424,22 @@ int dtls1_set_handshake_header(SSL_CONNECTION *s, WPACKET *pkt, int htype) int dtls1_close_construct_packet(SSL_CONNECTION *s, WPACKET *pkt, int htype) { size_t msglen; - int record_type; - - /* Convert from possible dummy message type */ - record_type = (htype == SSL3_MT_CHANGE_CIPHER_SPEC) ? SSL3_RT_CHANGE_CIPHER_SPEC - : SSL3_RT_HANDSHAKE; - if ((htype != SSL3_MT_CHANGE_CIPHER_SPEC && !WPACKET_close(pkt)) + if ((s->d1->w_msg.record_type == SSL3_RT_HANDSHAKE && !WPACKET_close(pkt)) || !WPACKET_get_length(pkt, &msglen) || msglen > INT_MAX) return 0; - if (htype != SSL3_MT_CHANGE_CIPHER_SPEC) + if (s->d1->w_msg.record_type == SSL3_RT_HANDSHAKE) s->d1->w_msg.msg_body_len = msglen - DTLS1_HM_HEADER_LENGTH; s->init_num = msglen; s->init_off = 0; - if (htype != DTLS1_MT_HELLO_VERIFY_REQUEST) { + if (htype != DTLS1_MT_HELLO_VERIFY_REQUEST + && s->d1->w_msg.record_type != SSL3_RT_ACK) { /* Buffer the message to handle re-xmits */ - if (!dtls1_buffer_sent_message(s, record_type)) + if (!dtls1_buffer_sent_message(s, s->d1->w_msg.record_type)) return 0; } diff --git a/ssl/statem/statem_local.h b/ssl/statem/statem_local.h index 04114b1e27d7f..01dd18a9dad43 100644 --- a/ssl/statem/statem_local.h +++ b/ssl/statem/statem_local.h @@ -123,6 +123,7 @@ __owur CON_FUNC_RETURN tls_construct_change_cipher_spec(SSL_CONNECTION *s, WPACKET *pkt); __owur CON_FUNC_RETURN dtls_construct_change_cipher_spec(SSL_CONNECTION *s, WPACKET *pkt); +__owur CON_FUNC_RETURN dtls_construct_ack(SSL_CONNECTION *s, WPACKET *pkt); __owur CON_FUNC_RETURN tls_construct_finished(SSL_CONNECTION *s, WPACKET *pkt); __owur CON_FUNC_RETURN tls_construct_key_update(SSL_CONNECTION *s, WPACKET *pkt); @@ -194,6 +195,7 @@ __owur CON_FUNC_RETURN tls_construct_next_proto(SSL_CONNECTION *s, WPACKET *pkt) #endif __owur MSG_PROCESS_RETURN tls_process_hello_req(SSL_CONNECTION *s, PACKET *pkt); __owur MSG_PROCESS_RETURN dtls_process_hello_verify(SSL_CONNECTION *s, PACKET *pkt); +__owur MSG_PROCESS_RETURN dtls_process_ack(SSL_CONNECTION *s, PACKET *pkt); __owur CON_FUNC_RETURN tls_construct_end_of_early_data(SSL_CONNECTION *s, WPACKET *pkt); diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c index 74d3a493f7219..574eabf90de1e 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c @@ -133,7 +133,12 @@ static int ossl_statem_server13_read_transition(SSL_CONNECTION *s, int mt) return 1; } break; - + case TLS_ST_SW_KEY_UPDATE: + case TLS_ST_SW_SESSION_TICKET: + if (mt == DTLS13_MT_ACK) { + st->hand_state = TLS_ST_SR_ACK; + return 1; + } case TLS_ST_OK: /* * Its never ok to start processing handshake messages in the middle of @@ -160,6 +165,11 @@ static int ossl_statem_server13_read_transition(SSL_CONNECTION *s, int mt) st->hand_state = TLS_ST_SR_KEY_UPDATE; return 1; } + + if (mt == DTLS13_MT_ACK) { + st->hand_state = TLS_ST_SR_ACK; + return 1; + } break; } @@ -463,6 +473,7 @@ static int do_compressed_cert(SSL_CONNECTION *sc) */ static WRITE_TRAN ossl_statem_server13_write_transition(SSL_CONNECTION *s) { + OSSL_HANDSHAKE_STATE next_state; OSSL_STATEM *st = &s->statem; /* @@ -567,16 +578,34 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL_CONNECTION *s) * If we're not going to renew the ticket then we just finish the * handshake at this point. */ - st->hand_state = TLS_ST_OK; + if (SSL_CONNECTION_IS_DTLS13(s)) { + st->defered_ack_state = TLS_ST_OK; + st->hand_state = TLS_ST_SW_ACK; + } else + st->hand_state = TLS_ST_OK; + return WRITE_TRAN_CONTINUE; } if (s->num_tickets > s->sent_tickets) - st->hand_state = TLS_ST_SW_SESSION_TICKET; + next_state = TLS_ST_SW_SESSION_TICKET; else - st->hand_state = TLS_ST_OK; + next_state = TLS_ST_OK; + + if (SSL_CONNECTION_IS_DTLS13(s)) { + st->defered_ack_state = next_state; + st->hand_state = TLS_ST_SW_ACK; + } else { + st->hand_state = next_state; + } return WRITE_TRAN_CONTINUE; case TLS_ST_SR_KEY_UPDATE: + if (SSL_CONNECTION_IS_DTLS13(s)) { + st->defered_ack_state = TLS_ST_OK; + st->hand_state = TLS_ST_SW_ACK; + return WRITE_TRAN_CONTINUE; + } + /* Fall-through */ case TLS_ST_SW_KEY_UPDATE: st->hand_state = TLS_ST_OK; return WRITE_TRAN_CONTINUE; @@ -593,6 +622,15 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL_CONNECTION *s) st->hand_state = TLS_ST_OK; } return WRITE_TRAN_CONTINUE; + + case TLS_ST_SR_ACK: + st->hand_state = TLS_ST_OK; + return WRITE_TRAN_CONTINUE; + + case TLS_ST_SW_ACK: + st->hand_state = st->defered_ack_state; + + return WRITE_TRAN_CONTINUE; } } @@ -1079,6 +1117,11 @@ WORK_STATE ossl_statem_server_post_work(SSL_CONNECTION *s, WORK_STATE wst) return WORK_MORE_A; } break; + + case TLS_ST_SW_ACK: + if (statem_flush(s) != 1) + return WORK_MORE_A; + break; } return WORK_FINISHED_CONTINUE; @@ -1189,6 +1232,11 @@ int ossl_statem_server_construct_message(SSL_CONNECTION *s, *confunc = tls_construct_key_update; *mt = SSL3_MT_KEY_UPDATE; break; + + case TLS_ST_SW_ACK: + *confunc = dtls_construct_ack; + *mt = DTLS13_MT_ACK; + break; } return 1; @@ -1256,6 +1304,9 @@ size_t ossl_statem_server_max_message_size(SSL_CONNECTION *s) case TLS_ST_SR_KEY_UPDATE: return KEY_UPDATE_MAX_LENGTH; + + case TLS_ST_SR_ACK: + return 2 + (0x1 << 16) - 1; } } @@ -1307,6 +1358,8 @@ MSG_PROCESS_RETURN ossl_statem_server_process_message(SSL_CONNECTION *s, case TLS_ST_SR_KEY_UPDATE: return tls_process_key_update(s, pkt); + case TLS_ST_SR_ACK: + return dtls_process_ack(s, pkt); } } diff --git a/test/recipes/70-test_dtls13ack.t b/test/recipes/70-test_dtls13ack.t new file mode 100644 index 0000000000000..78898040b7b2f --- /dev/null +++ b/test/recipes/70-test_dtls13ack.t @@ -0,0 +1,200 @@ +#! /usr/bin/env perl +# Copyright 2024 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +use strict; +use feature 'state'; + +use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file bldtop_dir/; +use OpenSSL::Test::Utils; +use TLSProxy::Proxy; +use TLSProxy::Message; + +my $test_name = "test_dtls13ack"; +setup($test_name); + +plan skip_all => "TLSProxy isn't usable on $^O" + if $^O =~ /^(VMS)$/; + +plan skip_all => "$test_name needs the dynamic engine feature enabled" + if disabled("engine") || disabled("dynamic-engine"); + +plan skip_all => "$test_name needs the sock feature enabled" + if disabled("sock"); + +plan skip_all => "DTLSProxy does not support partial messages" + if disabled("ec"); + +plan skip_all => "$test_name needs DTLSv1.3 enabled" + if disabled("dtls1_3"); + +my $proxy = TLSProxy::Proxy->new_dtls( + undef, + cmdstr(app(["openssl"]), display => 1), + srctop_file("apps", "server.pem"), + (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE}) +); + +plan tests => 8; + +#Test 1: Check that records are acked during an uninterrupted handshake +$proxy->serverflags("-min_protocol DTLSv1.3 -max_protocol DTLSv1.3"); +$proxy->clientflags("-min_protocol DTLSv1.3 -max_protocol DTLSv1.3"); +TLSProxy::Message->successondata(1); +ok($proxy->start(), "Proxy run succeeded"); + +my @expected = get_expected_ack_record_numbers(); +my @actual = get_actual_acked_record_numbers(); +my @missing = record_numbers_missing(\@expected, \@actual); +my $expected_count = @expected; +my $actual_count = @actual; +my $missing_count = @missing; + +ok($missing_count == 0, "Check that all record numbers are acked"); +ok($actual_count > 0, "Check that some record numbers are acked"); +ok($expected_count > 0, "Check that some records to be acked were sent"); + +#Test 2: Check that records that are missing are not acked during a handshake +$proxy->clear(); +my $found_first_new_session_ticket = 0; +$proxy->serverflags("-min_protocol DTLSv1.3 -max_protocol DTLSv1.3"); +$proxy->clientflags("-min_protocol DTLSv1.3 -max_protocol DTLSv1.3"); +$proxy->filter(\&drop_first_new_session_ticket_filter); +TLSProxy::Message->successondata(1); +ok($proxy->start(), "Proxy run succeeded"); + +@expected = get_expected_ack_record_numbers(); +@actual = get_actual_acked_record_numbers(); +@missing = record_numbers_missing(\@expected, \@actual); +$expected_count = @expected; +$actual_count = @actual; +$missing_count = @missing; + +ok($missing_count == 1, "Check that all record numbers except one are acked"); +ok($actual_count > 0, "Check that some record numbers are acked"); +ok($expected_count > 0, "Check that some records to be acked were sent"); + +sub get_expected_ack_record_numbers +{ + my $records = $proxy->record_list; + my @record_numbers = (); + + foreach (@{$records}) { + my $record = $_; + + if ($record->content_type == TLSProxy::Record::RT_HANDSHAKE) { + my $epoch = $record->epoch; + my $seqnum = $record->seq; + my $isdtls = $record->isdtls; + my $isserver = $record->isserver; + my $recnum = TLSProxy::RecordNumber->new($epoch, $seqnum); + + my @messages = TLSProxy::Message->get_messages($isserver, $record, $isdtls); + + my $record_should_be_acked = 0; + + foreach (@messages) { + my $message = $_; + if (($message->mt == TLSProxy::Message::MT_FINISHED && !$isserver) + || $message->mt == TLSProxy::Message::MT_KEY_UPDATE + || $message->mt == TLSProxy::Message::MT_NEW_SESSION_TICKET) { + $record_should_be_acked = 1; + } + } + + push(@record_numbers, $recnum) if ($record_should_be_acked == 1); + } + } + + return @record_numbers; +} + +sub get_actual_acked_record_numbers +{ + my @records = @{$proxy->record_list}; + my @record_numbers = (); + + foreach (@records) { + my $record = $_; + + if ($record->content_type == TLSProxy::Record::RT_ACK) { + my $idx; + my $recnum_count = unpack('n', $record->decrypt_data); + my $ptr = 2; + + next if ($recnum_count == 0); + + for ($idx = 0, $idx < $recnum_count, $idx++) { + my $epoch_lo; + my $epoch_hi; + my $msgseq_lo; + my $msgseq_hi; + + ($epoch_lo, $epoch_hi, $msgseq_lo, $msgseq_hi) + = unpack('NNNN', substr($record->decrypt_data, $ptr)); + $ptr = $ptr + 16; + + my $epoch = ($epoch_hi << 32) | $epoch_lo; + my $msgseq = ($msgseq_hi << 32) | $msgseq_lo; + my $recnum = TLSProxy::RecordNumber->new($epoch, $msgseq); + + push(@record_numbers, $recnum); + } + } + } + return @record_numbers; +} + +sub record_numbers_missing +{ + my @expected_record_numbers = @{$_[0]}; + my @actual_record_numbers = @{$_[1]}; + my @missing_record_numbers = (); + + foreach (@expected_record_numbers) + { + my $found = 0; + my $expected = $_; + + foreach (@actual_record_numbers) { + my $actual = $_; + if ($actual->epoch() == $expected->epoch() + && $actual->seqnum() == $expected->seqnum()) { + $found = 1 + } + } + + if ($found == 0) { + push(@missing_record_numbers, $expected); + } + } + + return @missing_record_numbers; +} + +sub drop_first_new_session_ticket_filter +{ + my $proxy = shift; + + return if ($found_first_new_session_ticket == 1); + + foreach my $record (@{$proxy->record_list}) { + next if ($record->{sent}); + + my $isdtls = $record->isdtls; + my $isserver = $record->isserver; + my @messages = TLSProxy::Message->get_messages($isserver, $record, $isdtls); + foreach my $message (@messages) { + if ($message->mt == TLSProxy::Message::MT_NEW_SESSION_TICKET) { + $record->{sent} = 1; + $found_first_new_session_ticket = 1; + last; + } + } + last if $found_first_new_session_ticket == 1; + } +} diff --git a/test/recipes/70-test_sslcbcpadding.t b/test/recipes/70-test_sslcbcpadding.t index 6628192618000..abef45e8b5dc4 100644 --- a/test/recipes/70-test_sslcbcpadding.t +++ b/test/recipes/70-test_sslcbcpadding.t @@ -111,6 +111,7 @@ sub add_maximal_padding_filter } my $record = TLSProxy::Record->new( + $last_message->server, $proxy->flight, TLSProxy::Record::RT_APPLICATION_DATA, TLSProxy::Record::VERS_TLS_1_2, diff --git a/test/recipes/70-test_sslrecords.t b/test/recipes/70-test_sslrecords.t index 691db13b8a8e5..ad4f115538d36 100644 --- a/test/recipes/70-test_sslrecords.t +++ b/test/recipes/70-test_sslrecords.t @@ -355,6 +355,7 @@ sub add_empty_recs_filter my $record; if ($isdtls == 1) { $record = TLSProxy::Record->new_dtls( + 0, 0, $content_type, TLSProxy::Record::VERS_DTLS_1_2, @@ -369,6 +370,7 @@ sub add_empty_recs_filter ); } else { $record = TLSProxy::Record->new( + 0, 0, $content_type, TLSProxy::Record::VERS_TLS_1_2, @@ -412,6 +414,7 @@ sub add_frag_alert_filter # Now add the alert level (Fatal) as a separate record $byte = pack('C', TLSProxy::Message::AL_LEVEL_FATAL); my $record = TLSProxy::Record->new( + 0, 0, TLSProxy::Record::RT_ALERT, TLSProxy::Record::VERS_TLS_1_2, @@ -427,6 +430,7 @@ sub add_frag_alert_filter # And finally the description (Unexpected message) in a third record $byte = pack('C', TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE); $record = TLSProxy::Record->new( + 0, 0, TLSProxy::Record::RT_ALERT, TLSProxy::Record::VERS_TLS_1_2, @@ -459,6 +463,7 @@ sub add_sslv2_filter TLSProxy::Message::AL_DESC_NO_RENEGOTIATION); my $alertlen = length $alert; $record = TLSProxy::Record->new( + 0, 0, TLSProxy::Record::RT_ALERT, TLSProxy::Record::VERS_TLS_1_2, @@ -498,6 +503,7 @@ sub add_sslv2_filter my $chlen = length $clienthello; $record = TLSProxy::Record->new( + 0, 0, TLSProxy::Record::RT_HANDSHAKE, TLSProxy::Record::VERS_TLS_1_2, @@ -538,6 +544,7 @@ sub add_sslv2_filter my $fraglen = length $frag1; $record = TLSProxy::Record->new( + 0, 0, TLSProxy::Record::RT_HANDSHAKE, TLSProxy::Record::VERS_TLS_1_2, @@ -558,6 +565,7 @@ sub add_sslv2_filter $recvers = 0; } $record = TLSProxy::Record->new( + 0, 0, TLSProxy::Record::RT_HANDSHAKE, TLSProxy::Record::VERS_TLS_1_2, @@ -572,6 +580,7 @@ sub add_sslv2_filter $fraglen = length $frag3; $record = TLSProxy::Record->new( + 0, 0, TLSProxy::Record::RT_HANDSHAKE, TLSProxy::Record::VERS_TLS_1_2, @@ -591,6 +600,8 @@ sub add_unknown_record_type { my $proxy = shift; my $records = $proxy->record_list; + my $lastmessage = @{$proxy->message_list}[-1]; + my $isserver = $lastmessage->server; my $isdtls = $proxy->isdtls; state $added_record; @@ -607,6 +618,7 @@ sub add_unknown_record_type if ($isdtls) { $record = TLSProxy::Record->new_dtls( + $isserver, 1, TLSProxy::Record::RT_UNKNOWN, @{$records}[-1]->version(), @@ -621,6 +633,7 @@ sub add_unknown_record_type ); } else { $record = TLSProxy::Record->new( + $isserver, 1, TLSProxy::Record::RT_UNKNOWN, @{$records}[-1]->version(), @@ -763,6 +776,7 @@ sub not_on_record_boundary #KeyUpdates must end on a record boundary my $record = TLSProxy::Record->new( + @{$proxy->{message_list}}[-1]->server, 1, TLSProxy::Record::RT_APPLICATION_DATA, TLSProxy::Record::VERS_TLS_1_2, @@ -791,8 +805,10 @@ sub not_on_record_boundary } else { return if @{$proxy->{message_list}}[-1]->{mt} != TLSProxy::Message::MT_FINISHED; + my $isserver = @{$proxy->{message_list}}[-1]->server; my $record = TLSProxy::Record->new( + $isserver, 1, TLSProxy::Record::RT_APPLICATION_DATA, TLSProxy::Record::VERS_TLS_1_2, @@ -818,6 +834,7 @@ sub not_on_record_boundary if ($boundary_test_type == DATA_BETWEEN_KEY_UPDATE) { #Now add an app data record $record = TLSProxy::Record->new( + $isserver, 1, TLSProxy::Record::RT_APPLICATION_DATA, TLSProxy::Record::VERS_TLS_1_2, @@ -839,6 +856,7 @@ sub not_on_record_boundary #Now add the rest of the KeyUpdate message $record = TLSProxy::Record->new( + $isserver, 1, TLSProxy::Record::RT_APPLICATION_DATA, TLSProxy::Record::VERS_TLS_1_2, diff --git a/test/recipes/70-test_tls13hrr.t b/test/recipes/70-test_tls13hrr.t index 130f944669150..8ad6834748bbb 100644 --- a/test/recipes/70-test_tls13hrr.t +++ b/test/recipes/70-test_tls13hrr.t @@ -193,7 +193,9 @@ sub hrr_filter my $dup_hrr; if ($proxy->isdtls()) { - $dup_hrr = TLSProxy::Record->new_dtls(3, + $dup_hrr = TLSProxy::Record->new_dtls( + 1, + 3, $hrr_record->content_type(), $hrr_record->version(), $hrr_record->epoch(), @@ -205,7 +207,9 @@ sub hrr_filter $hrr_record->data(), $hrr_record->decrypt_data()); } else { - $dup_hrr = TLSProxy::Record->new(3, + $dup_hrr = TLSProxy::Record->new( + 1, + 3, $hrr_record->content_type(), $hrr_record->version(), $hrr_record->len(), diff --git a/util/perl/TLSProxy/Message.pm b/util/perl/TLSProxy/Message.pm index fcc45c9a5d273..0b40a68048ba2 100644 --- a/util/perl/TLSProxy/Message.pm +++ b/util/perl/TLSProxy/Message.pm @@ -9,6 +9,7 @@ use strict; package TLSProxy::Message; +use TLSProxy::RecordNumber; use TLSProxy::Alert; use constant DTLS_MESSAGE_HEADER_LENGTH => 12; @@ -21,6 +22,7 @@ use constant { MT_SERVER_HELLO => 2, MT_HELLO_VERIFY_REQUEST => 3, MT_NEW_SESSION_TICKET => 4, + MT_END_OF_EARLY_DATA => 5, MT_ENCRYPTED_EXTENSIONS => 8, MT_CERTIFICATE => 11, MT_SERVER_KEY_EXCHANGE => 12, @@ -29,7 +31,10 @@ use constant { MT_CERTIFICATE_VERIFY => 15, MT_CLIENT_KEY_EXCHANGE => 16, MT_FINISHED => 20, + MT_CERTIFICATE_URL => 21, MT_CERTIFICATE_STATUS => 22, + MT_SUPPLEMENTAL_DATA => 23, + MT_KEY_UPDATE => 24, MT_COMPRESSED_CERTIFICATE => 25, MT_NEXT_PROTO => 67 }; diff --git a/util/perl/TLSProxy/Proxy.pm b/util/perl/TLSProxy/Proxy.pm index bbb32e801181c..7bf51101c189e 100644 --- a/util/perl/TLSProxy/Proxy.pm +++ b/util/perl/TLSProxy/Proxy.pm @@ -488,10 +488,6 @@ sub clientstart sub process_packet { my ($self, $server, $packet) = @_; - my $len_real; - my $decrypt_len; - my $data; - my $recnum; if ($server) { print "Received server packet\n"; diff --git a/util/perl/TLSProxy/Record.pm b/util/perl/TLSProxy/Record.pm index 4d4a48498f284..ca6af6d85ee02 100644 --- a/util/perl/TLSProxy/Record.pm +++ b/util/perl/TLSProxy/Record.pm @@ -24,6 +24,7 @@ use constant { RT_HANDSHAKE => 22, RT_ALERT => 21, RT_CCS => 20, + RT_ACK => 26, RT_UNKNOWN => 100 }; @@ -32,6 +33,7 @@ my %record_type = ( RT_HANDSHAKE, "HANDSHAKE", RT_ALERT, "ALERT", RT_CCS, "CCS", + RT_ACK, "ACK", RT_UNKNOWN, "UNKNOWN" ); @@ -117,6 +119,7 @@ sub get_records my $record; if ($isdtls) { $record = TLSProxy::Record->new_dtls( + $server, $flight, $content_type, $version, @@ -131,6 +134,7 @@ sub get_records ); } else { $record = TLSProxy::Record->new( + $server, $flight, $content_type, $version, @@ -158,6 +162,7 @@ sub get_records if (TLSProxy::Proxy->is_tls13()) { print " Inner content type: " .$record_type{$record->content_type()}."\n"; + print " Data: ".unpack("n",$record->decrypt_data)."\n"; } } } @@ -211,7 +216,8 @@ sub etm sub new_dtls { my $class = shift; - my ($flight, + my ($isserver, + $flight, $content_type, $version, $epoch, @@ -222,7 +228,8 @@ sub new_dtls $decrypt_len, $data, $decrypt_data) = @_; - return $class->init(1, + return $class->init($isserver, + 1, $flight, $content_type, $version, @@ -239,7 +246,8 @@ sub new_dtls sub new { my $class = shift; - my ($flight, + my ($isserver, + $flight, $content_type, $version, $len, @@ -249,6 +257,7 @@ sub new $data, $decrypt_data) = @_; return $class->init( + $isserver, 0, $flight, $content_type, @@ -266,7 +275,8 @@ sub new sub init { my $class = shift; - my ($isdtls, + my ($isserver, + $isdtls, $flight, $content_type, $version, @@ -280,6 +290,7 @@ sub init $decrypt_data) = @_; my $self = { + isserver => $isserver, isdtls => $isdtls, flight => $flight, content_type => $content_type, @@ -414,6 +425,16 @@ sub reconstruct_record } #Read only accessors +sub isserver +{ + my $self = shift; + return $self->{isserver}; +} +sub isdtls +{ + my $self = shift; + return $self->{isdtls}; +} sub flight { my $self = shift; diff --git a/util/perl/TLSProxy/RecordNumber.pm b/util/perl/TLSProxy/RecordNumber.pm new file mode 100644 index 0000000000000..4cacb1c4096b7 --- /dev/null +++ b/util/perl/TLSProxy/RecordNumber.pm @@ -0,0 +1,37 @@ +# Copyright 2024 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +use strict; + +package TLSProxy::RecordNumber; + +sub new +{ + my $class = shift; + my ($epoch, + $seqnum) = @_; + + my $self = { + epoch => $epoch, + seqnum => $seqnum + }; + + return bless $self, $class; +} + +#Read only accessors +sub epoch +{ + my $self = shift; + return $self->{epoch}; +} +sub seqnum +{ + my $self = shift; + return $self->{seqnum}; +} +1;