switch (content_type) {
case SSL3_RT_CHANGE_CIPHER_SPEC:
/* type 20 */
- str_content_type = ", ChangeCipherSpec";
+ if (version != DTLS1_3_VERSION)
+ str_content_type = ", ChangeCipherSpec";
break;
case SSL3_RT_ALERT:
/* type 21 */
/* type 23 */
str_content_type = ", ApplicationData";
break;
+ case SSL3_RT_ACK:
+ /* type 26 */
+ if (version == DTLS1_3_VERSION)
+ str_content_type = ", ACK";
+ break;
case SSL3_RT_HEADER:
/* type 256 */
str_content_type = ", RecordHeader";
str_content_type = ", InnerContent";
break;
default:
+ break;
+ }
+
+ if (str_content_type[0] == '\0') {
BIO_snprintf(tmpbuf, sizeof(tmpbuf)-1, ", Unknown (content_type=%d)", content_type);
str_content_type = tmpbuf;
}
The DTLSv1.3 implementation does not include the message sequence number,
fragment offset and fragment length as is the case with previous versions of DTLS.
+#### DTLS ACK records (RFC9147 Section 7)
+
+ACKs are sent for KeyUpdates, NewSessionTicket, Certificate (client),
+CompressedCertificate (Client), CertificateVerify (client) and Finish (client).
+
+Notes on RFC9147 Section 7.1:
+
+* The implementation does not offer any logic to determine that there is disruption
+ when receiving messages which means it will not send ACKs for the example given
+ in RFC9147 Figure 12.
+* ACKs are always sent immediately after receiving a full message to be ACKed.
+* If the implementation does not receive an ACK for all fragments of a flight,
+ then the full flight will be retransmitted.
+* Empty ACKs are never sent.
+* The implementation does not explicitly prohibit receiving unencrypted ACKs. The
+ implementation will only ACK records of epoch > 0 so all ACKs sent by the
+ implementation will be encrypted.
+* The implementation only accepts ACKs after DTLSv1.3 has been negotiated. ACKs
+ that are received when DTLSv1.3 has not been negotiated is handled with a fatal
+ alert as any other unexpected message. ACKs that are received before version
+ negotiation are dropped.
+* The implementation ignores ACKs received for messages other than KeyUpdates,
+ NewSessionTicket, Certificate (client), CertificateVerify (client) and Finish (client).
+
+Missing functionality:
+
+There's need for a lot more corner case testing:
+
+* Correct handling of ACKs during KeyUpdate and SessionTicket updates.
+* Currently only retransmission after a missing ACK of client Finish message is
+ tested.
+* TLSProxy does not support testing post handshake message ACK testing. Such
+ testing probably needs to be performed by another framework.
+* This comment also forms a great test case:
+ <https://github.com/openssl/openssl/pull/25119#discussion_r1871643459>
+
+### Known issues
+
+#### Dropped records handling
+
+The implementation is only partially able to handle dropped records. For example
+`test_dtls13ack` has a disabled test case that fails when compressed certificates
+are sent. It seems like the implementation is not able to properly handle the
+case were the last flight of the client is dropped if it contains a client cert.
+In that case it should retransmit the CompressedCertificate and CertificateVerify
+messages in epoch 2, but it chooses to do it in epoch 3.
+
+There's a need to setup a test that checks dropped records in several scenarios
+and configurations in order to properly fix and verify.
+
Implementation progress
-----------------------
Notice that some of the requirements mentioned in [List of DTLSv1.3 requirements](#list-of-dtls-13-requirements)
is not covered by these workitems and must be implemented separately.
-| Summary | #PR |
-|-----------------------------------------------------|--------|
-| ACK messages | #25119 |
-| Use HelloRetryRequest instead of HelloVerifyRequest | #22985 |
-| EndOfEarlyData message | - |
-| DTLSv1.3 Fuzzer | - |
+| Summary | #PR |
+|------------------------|--------|
+| EndOfEarlyData message | - |
+| DTLSv1.3 Fuzzer | - |
### Changes from DTLS 1.2 and/or TLS 1.3
> the EndOfEarlyData message is omitted both from the wire and the handshake
> transcript
-#### ACK messages
-
-See section 7 and 8 of RFC 9147.
-
### List of DTLSv1.3 requirements
Here's a list of requirements from RFC 9147 together with their implementation status
const int **, int);
static int ossltest_cipher_nids[] = {
- NID_aes_128_cbc, NID_aes_128_gcm,
+ NID_aes_128_cbc, NID_aes_128_ecb, NID_aes_128_gcm,
NID_aes_128_cbc_hmac_sha1, 0
};
/* AES128 */
-static int ossltest_aes128_init_key(EVP_CIPHER_CTX *ctx,
- const unsigned char *key,
- const unsigned char *iv, int enc);
+static int ossltest_aes128_cbc_init_key(EVP_CIPHER_CTX *ctx,
+ const unsigned char *key,
+ const unsigned char *iv, int enc);
static int ossltest_aes128_cbc_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
const unsigned char *in, size_t inl);
+static int ossltest_aes128_ecb_init_key(EVP_CIPHER_CTX *ctx,
+ const unsigned char *key,
+ const unsigned char *iv, int enc);
+static int ossltest_aes128_ecb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t inl);
static int ossltest_aes128_gcm_init_key(EVP_CIPHER_CTX *ctx,
const unsigned char *key,
const unsigned char *iv, int enc);
EVP_CIPH_FLAG_DEFAULT_ASN1
| EVP_CIPH_CBC_MODE)
|| !EVP_CIPHER_meth_set_init(_hidden_aes_128_cbc,
- ossltest_aes128_init_key)
+ ossltest_aes128_cbc_init_key)
|| !EVP_CIPHER_meth_set_do_cipher(_hidden_aes_128_cbc,
ossltest_aes128_cbc_cipher)
|| !EVP_CIPHER_meth_set_impl_ctx_size(_hidden_aes_128_cbc,
return _hidden_aes_128_cbc;
}
+static EVP_CIPHER *_hidden_aes_128_ecb = NULL;
+static const EVP_CIPHER *ossltest_aes_128_ecb(void)
+{
+ if (_hidden_aes_128_ecb == NULL
+ && ((_hidden_aes_128_ecb = EVP_CIPHER_meth_new(NID_aes_128_ecb,
+ 16 /* block size */,
+ 16 /* key len */)) == NULL
+ || !EVP_CIPHER_meth_set_iv_length(_hidden_aes_128_ecb, 0)
+ || !EVP_CIPHER_meth_set_flags(_hidden_aes_128_ecb,
+ EVP_CIPH_FLAG_DEFAULT_ASN1 | EVP_CIPH_ECB_MODE)
+ || !EVP_CIPHER_meth_set_init(_hidden_aes_128_ecb, ossltest_aes128_ecb_init_key)
+ || !EVP_CIPHER_meth_set_do_cipher(_hidden_aes_128_ecb, ossltest_aes128_ecb_cipher)
+ || !EVP_CIPHER_meth_set_impl_ctx_size(_hidden_aes_128_ecb,
+ EVP_CIPHER_impl_ctx_size(EVP_aes_128_ecb())))) {
+ EVP_CIPHER_meth_free(_hidden_aes_128_ecb);
+ _hidden_aes_128_ecb = NULL;
+ }
+ return _hidden_aes_128_ecb;
+}
+
static EVP_CIPHER *_hidden_aes_128_gcm = NULL;
#define AES_GCM_FLAGS (EVP_CIPH_FLAG_DEFAULT_ASN1 \
static void destroy_ciphers(void)
{
EVP_CIPHER_meth_free(_hidden_aes_128_cbc);
+ EVP_CIPHER_meth_free(_hidden_aes_128_ecb);
EVP_CIPHER_meth_free(_hidden_aes_128_gcm);
EVP_CIPHER_meth_free(_hidden_aes_128_cbc_hmac_sha1);
_hidden_aes_128_cbc = NULL;
+ _hidden_aes_128_ecb = NULL;
_hidden_aes_128_gcm = NULL;
_hidden_aes_128_cbc_hmac_sha1 = NULL;
}
case NID_aes_128_cbc:
*cipher = ossltest_aes_128_cbc();
break;
+ case NID_aes_128_ecb:
+ *cipher = ossltest_aes_128_ecb();
+ break;
case NID_aes_128_gcm:
*cipher = ossltest_aes_128_gcm();
break;
/*
* AES128 Implementation
*/
-
-static int ossltest_aes128_init_key(EVP_CIPHER_CTX *ctx,
- const unsigned char *key,
- const unsigned char *iv, int enc)
-{
- return EVP_CIPHER_meth_get_init(EVP_aes_128_cbc()) (ctx, key, iv, enc);
-}
-
-static int ossltest_aes128_cbc_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
- const unsigned char *in, size_t inl)
+static int ossltest_cipher(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
+ unsigned char *out, const unsigned char *in, size_t inl)
{
unsigned char *tmpbuf;
int ret;
memcpy(tmpbuf, in, inl);
/* Go through the motions of encrypting it */
- ret = EVP_CIPHER_meth_get_do_cipher(EVP_aes_128_cbc())(ctx, out, in, inl);
+ ret = EVP_CIPHER_meth_get_do_cipher(cipher)(ctx, out, in, inl);
/* Throw it all away and just use the plaintext as the output */
if (tmpbuf != NULL)
memcpy(out, tmpbuf, inl);
+
OPENSSL_free(tmpbuf);
return ret;
}
+static int ossltest_aes128_cbc_init_key(EVP_CIPHER_CTX *ctx,
+ const unsigned char *key,
+ const unsigned char *iv, int enc)
+{
+ return EVP_CIPHER_meth_get_init(EVP_aes_128_cbc()) (ctx, key, iv, enc);
+}
+
+static int ossltest_aes128_cbc_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t inl)
+{
+ return ossltest_cipher(ctx, EVP_aes_128_cbc(), out, in, inl);
+}
+
+static int ossltest_aes128_ecb_init_key(EVP_CIPHER_CTX *ctx,
+ const unsigned char *key,
+ const unsigned char *iv, int enc)
+{
+ return EVP_CIPHER_meth_get_init(EVP_aes_128_ecb()) (ctx, key, iv, enc);
+}
+
+static int ossltest_aes128_ecb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t inl)
+{
+ return ossltest_cipher(ctx, EVP_aes_128_ecb(), out, in, inl);
+}
+
static int ossltest_aes128_gcm_init_key(EVP_CIPHER_CTX *ctx,
const unsigned char *key,
const unsigned char *iv, int enc)
return 1;
}
+/*
+ * Peek ahead at 6 bytes in network order from |pkt| and store the value in
+ * |*data|
+ */
+__owur static ossl_inline int PACKET_peek_net_6(const PACKET *pkt,
+ uint64_t *data)
+{
+ if (PACKET_remaining(pkt) < 6)
+ return 0;
+
+ *data = ((uint64_t)(*(pkt->curr))) << 40;
+ *data |= ((uint64_t)(*(pkt->curr + 1))) << 32;
+ *data |= ((uint64_t)(*(pkt->curr + 2))) << 24;
+ *data |= ((uint64_t)(*(pkt->curr + 3))) << 16;
+ *data |= ((uint64_t)(*(pkt->curr + 4))) << 8;
+ *data |= *(pkt->curr + 5);
+
+ return 1;
+}
+
+/* Get 6 bytes in network order from |pkt| and store the value in |*data| */
+__owur static ossl_inline int PACKET_get_net_6(PACKET *pkt, uint64_t *data)
+{
+ if (!PACKET_peek_net_6(pkt, data))
+ return 0;
+
+ packet_forward(pkt, 6);
+
+ return 1;
+}
+
/*
* Peek ahead at 8 bytes in network order from |pkt| and store the value in
* |*data|
WPACKET_put_bytes__((pkt), (val), 3)
#define WPACKET_put_bytes_u32(pkt, val) \
WPACKET_put_bytes__((pkt), (val), 4)
+#define WPACKET_put_bytes_u48(pkt, val) \
+ WPACKET_put_bytes__((pkt), (val), 6)
#define WPACKET_put_bytes_u64(pkt, val) \
WPACKET_put_bytes__((pkt), (val), 8)
struct ossl_record_template_st {
unsigned char type;
unsigned int version;
+ uint64_t sequence_number;
+ uint64_t epoch;
const unsigned char *buf;
size_t buflen;
};
* multiple records in one go and buffer them.
*/
int (*read_record)(OSSL_RECORD_LAYER *rl, void **rechandle, int *rversion,
- uint8_t *type, const unsigned char **data, size_t *datalen,
- uint16_t *epoch, unsigned char *seq_num);
+ uint8_t *type, const unsigned char **data, size_t *datalen,
+ uint16_t *epoch, uint64_t *seq_num);
/*
* Release length bytes from a buffer associated with a record previously
* read with read_record. Once all the bytes from a record are released, the
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 deferred_ack_state;
int in_init;
int read_state_first_init;
/* true when we are actually in SSL_accept() or SSL_connect() */
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;
/*
# 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
# 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
static void dtls1_clear_queues(SSL_CONNECTION *s)
{
dtls1_clear_received_buffer(s);
- dtls1_clear_sent_buffer(s);
+ dtls1_clear_sent_buffer(s, 0);
+ ossl_list_record_number_elem_free(&s->d1->ack_rec_num);
}
void dtls1_clear_received_buffer(SSL_CONNECTION *s)
}
}
-void dtls1_clear_sent_buffer(SSL_CONNECTION *s)
+void ossl_list_record_number_elem_free(OSSL_LIST(record_number) *p_list)
+{
+ DTLS1_RECORD_NUMBER *p_elem;
+ DTLS1_RECORD_NUMBER *p_elem_next = NULL;
+
+ if (p_list != NULL)
+ p_elem_next = ossl_list_record_number_head(p_list);
+
+ while ((p_elem = p_elem_next) != NULL) {
+ p_elem_next = ossl_list_record_number_next(p_elem_next);
+ ossl_list_record_number_remove(p_list, p_elem);
+ OPENSSL_free(p_elem);
+ }
+}
+
+DTLS1_RECORD_NUMBER *dtls1_record_number_new(uint64_t epoch, uint64_t seqnum)
+{
+ DTLS1_RECORD_NUMBER *recnum = OPENSSL_zalloc(sizeof(*recnum));
+
+ if (recnum != NULL) {
+ recnum->epoch = epoch;
+ recnum->seqnum = seqnum;
+ }
+
+ return recnum;
+}
+
+void dtls1_acknowledge_sent_buffer(SSL_CONNECTION *s, uint16_t before_epoch)
+{
+ pitem *item = NULL;
+ piterator iter = pqueue_iterator(&s->d1->sent_messages);
+
+ while ((item = pqueue_next(&iter)) != NULL) {
+ dtls_sent_msg *sent_msg = (dtls_sent_msg *)item->data;
+ DTLS1_RECORD_NUMBER *recnum;
+ DTLS1_RECORD_NUMBER *recnum_next = ossl_list_record_number_head(&sent_msg->rec_nums);
+
+ while ((recnum = recnum_next) != NULL) {
+ recnum_next = ossl_list_record_number_next(recnum_next);
+
+ if (recnum->epoch < before_epoch) {
+ ossl_list_record_number_remove(&sent_msg->rec_nums, recnum);
+ OPENSSL_free(recnum);
+ }
+ }
+ }
+}
+
+void dtls1_clear_sent_buffer(SSL_CONNECTION *s, int keep_unacked_msgs)
{
pitem *item = NULL;
+ pqueue *remaining_sent_messages = pqueue_new();
pqueue *sent_messages = &s->d1->sent_messages;
while ((item = pqueue_pop(sent_messages)) != NULL) {
- dtls_sent_msg *sent_msg = (dtls_sent_msg *)item->data;
+ dtls_sent_msg *sent_msg = (dtls_sent_msg *) item->data;
+ unsigned char msg_type = sent_msg->msg_info.msg_type;
+ unsigned char record_type = sent_msg->msg_info.record_type;
+
+ if (SSL_CONNECTION_IS_DTLS13(s)
+ && !ossl_list_record_number_is_empty(&sent_msg->rec_nums)
+ && keep_unacked_msgs) {
+ pqueue_insert(remaining_sent_messages, item);
+ continue;
+ }
- if (sent_msg->record_type == SSL3_RT_CHANGE_CIPHER_SPEC
+ if (((!SSL_CONNECTION_IS_DTLS13(s) && record_type == SSL3_RT_CHANGE_CIPHER_SPEC)
+ || (SSL_CONNECTION_IS_DTLS13(s)
+ && (msg_type == SSL3_MT_FINISHED || msg_type == SSL3_MT_KEY_UPDATE)))
&& sent_msg->saved_retransmit_state.wrlmethod != NULL
&& s->rlayer.wrl != sent_msg->saved_retransmit_state.wrl) {
/*
dtls1_sent_msg_free(sent_msg);
pitem_free(item);
}
+
+ if (SSL_CONNECTION_IS_DTLS13(s))
+ while ((item = pqueue_pop(remaining_sent_messages)) != NULL)
+ pqueue_insert(&s->d1->sent_messages, item);
+
+ pqueue_free(remaining_sent_messages);
}
+int dtls_any_sent_messages_are_missing_acknowledge(SSL_CONNECTION *s)
+{
+ pitem *item;
+ piterator iter = pqueue_iterator(&s->d1->sent_messages);
+
+ while ((item = pqueue_next(&iter)) != NULL) {
+ dtls_sent_msg *msg = (dtls_sent_msg *)item->data;
+
+ if (!ossl_list_record_number_is_empty(&msg->rec_nums))
+ return 1;
+ }
+
+ return 0;
+}
void dtls1_free(SSL *ssl)
{
s->d1->timeout_duration_us = 1000000;
dtls1_bio_set_next_timeout(s->rbio, s->d1);
/* Clear retransmission buffer */
- dtls1_clear_sent_buffer(s);
+ dtls1_clear_sent_buffer(s, 0);
}
int dtls1_check_timeout_num(SSL_CONNECTION *s)
memcpy(item->priority, prio64be, sizeof(item->priority));
item->data = data;
item->next = NULL;
+
+ return item;
+}
+
+pitem *pitem_new_u64(uint64_t prio, void *data)
+{
+ pitem *item = OPENSSL_malloc(sizeof(*item));
+ unsigned char *p_item_prio;
+
+ if (item == NULL)
+ return NULL;
+
+ p_item_prio = item->priority;
+ l2n8(prio, p_item_prio);
+ item->data = data;
+ item->next = NULL;
+
return item;
}
return found;
}
+pitem *pqueue_find_u64(pqueue *pq, uint64_t prio)
+{
+ unsigned char prio64be[8], *p_prio64be = prio64be;
+
+ l2n8(prio, p_prio64be);
+
+ return pqueue_find(pq, prio64be);
+}
+
pitem *pqueue_iterator(pqueue *pq)
{
return pqueue_peek(pq);
static int quic_read_record(OSSL_RECORD_LAYER *rl, void **rechandle,
int *rversion, uint8_t *type, const unsigned char **data,
size_t *datalen, uint16_t *epoch,
- unsigned char *seq_num)
+ uint64_t *seq_num)
{
if (rl->recread != 0 || rl->recunreleased != 0)
return OSSL_RECORD_RETURN_FATAL;
#include "../record_local.h"
#include "recmethod_local.h"
-/* mod 128 saturating subtract of two 64-bit values in big-endian order */
-static int satsub64be(const unsigned char *v1, const unsigned char *v2)
+/* mod 128 saturating subtract of two 64-bit values */
+static int satsub64(uint64_t l1, uint64_t l2)
{
- int64_t ret;
- uint64_t l1, l2;
+ uint64_t max, min;
+ int sign;
- n2l8(v1, l1);
- n2l8(v2, l2);
-
- ret = l1 - l2;
+ if (l1 > l2) {
+ max = l1;
+ min = l2;
+ sign = 1;
+ } else {
+ max = l2;
+ min = l1;
+ sign = -1;
+ }
- /* We do not permit wrap-around */
- if (l1 > l2 && ret < 0)
- return 128;
- else if (l2 > l1 && ret > 0)
- return -128;
+ if (max - min > 128)
+ return sign * 128;
- if (ret > 128)
- return 128;
- else if (ret < -128)
- return -128;
- else
- return (int)ret;
+ return sign * ((int)(max - min));
}
static int dtls_record_replay_check(OSSL_RECORD_LAYER *rl, DTLS_BITMAP *bitmap)
{
int cmp;
unsigned int shift;
- const unsigned char *seq = rl->sequence;
- cmp = satsub64be(seq, bitmap->max_seq_num);
+ cmp = satsub64(rl->sequence, bitmap->max_seq_num);
if (cmp > 0) {
- ossl_tls_rl_record_set_seq_num(&rl->rrec[0], seq);
+ rl->rrec[0].seq_num = rl->sequence;
return 1; /* this record in new */
}
shift = -cmp;
else if (bitmap->map & ((uint64_t)1 << shift))
return 0; /* record previously received */
- ossl_tls_rl_record_set_seq_num(&rl->rrec[0], seq);
+ rl->rrec[0].seq_num = rl->sequence;
return 1;
}
-static void dtls_record_bitmap_update(OSSL_RECORD_LAYER *rl,
- DTLS_BITMAP *bitmap)
+static void dtls_record_bitmap_update(OSSL_RECORD_LAYER *rl, DTLS_BITMAP *bitmap)
{
int cmp;
unsigned int shift;
- const unsigned char *seq = rl->sequence;
- cmp = satsub64be(seq, bitmap->max_seq_num);
+ cmp = satsub64(rl->sequence, bitmap->max_seq_num);
if (cmp > 0) {
shift = cmp;
if (shift < sizeof(bitmap->map) * 8)
bitmap->map <<= shift, bitmap->map |= 1UL;
else
bitmap->map = 1UL;
- memcpy(bitmap->max_seq_num, seq, SEQ_NUM_SIZE);
+ bitmap->max_seq_num = rl->sequence;
} else {
shift = -cmp;
if (shift < sizeof(bitmap->map) * 8)
}
static int dtls_rlayer_buffer_record(OSSL_RECORD_LAYER *rl, struct pqueue_st *queue,
- unsigned char *priority)
+ uint64_t priority)
{
DTLS_RLAYER_RECORD_DATA *rdata;
pitem *item;
return 0;
rdata = OPENSSL_malloc(sizeof(*rdata));
- item = pitem_new(priority, rdata);
+ item = pitem_new_u64(priority, rdata);
if (rdata == NULL || item == NULL) {
OPENSSL_free(rdata);
pitem_free(item);
memcpy(&rl->rrec[0], &(rdata->rrec), sizeof(TLS_RL_RECORD));
/* Set proper sequence number for mac calculation */
- assert(sizeof(rl->sequence) == sizeof(rdata->rrec.seq_num));
- memcpy(rl->sequence, rdata->rrec.seq_num, sizeof(rl->sequence));
+ rl->sequence = rdata->rrec.seq_num;
return 1;
}
if (!ossl_assert(inlen >= 0)
|| (size_t)inlen > sizeof(mask)
+ || !EVP_CIPHER_CTX_set_padding(ctx, 0)
|| EVP_CipherInit_ex2(ctx, NULL, NULL, iv, 1, NULL) <= 0
|| EVP_CipherUpdate(ctx, mask, &outlen, in, inlen) <= 0
|| outlen != inlen
size_t rechdrlen = 0;
size_t recseqnumoffs = 0;
- memset(recseqnum, 0, sizeof(recseqnum));
-
rl->num_recs = 0;
rl->curr_rec = 0;
rl->num_released = 0;
}
again:
+ memset(recseqnum, 0, sizeof(recseqnum));
+
/* if we're renegotiating, then there may be buffered records */
if (dtls_retrieve_rlayer_buffered_record(rl, &rl->processed_rcds)) {
rl->num_recs = 1;
if (rl->version == DTLS1_3_VERSION
&& rr->type != SSL3_RT_ALERT
&& rr->type != SSL3_RT_HANDSHAKE
- /* TODO(DTLSv1.3): && rr->type != SSL3_RT_ACK depends on acknowledge implementation */
+ && rr->type != SSL3_RT_ACK
&& !DTLS13_UNI_HDR_FIX_BITS_IS_SET(rr->type)) {
/* Silently discard */
rr->length = 0;
* Naive approach? We expect sequence number to be filled already
* and then override the last bytes of the sequence number.
*/
- || !PACKET_copy_bytes(&dtlsrecord, recseqnum + recseqnumoffs, recseqnumlen)) {
- rr->length = 0;
- rl->packet_length = 0;
- goto again;
- }
-
- /*
- * rfc9147:
- * The length field MAY be omitted by clearing the L bit, which means
- * that the record consumes the entire rest of the datagram in the
- * lower level transport
- */
- length = TLS_BUFFER_get_len(&rl->rbuf) - dtls_get_rec_header_size(rr->type);
-
- if ((lbitisset && !PACKET_get_net_2(&dtlsrecord, &length))
- || length == 0) {
+ || !PACKET_copy_bytes(&dtlsrecord, recseqnum + recseqnumoffs, recseqnumlen)
+ /*
+ * rfc9147:
+ * The length field MAY be omitted by clearing the L bit, which means
+ * that the record consumes the entire rest of the datagram in the
+ * lower level transport
+ */
+ || (lbitisset ? !PACKET_get_net_2(&dtlsrecord, &length)
+ : (length = (unsigned int)TLS_BUFFER_get_len(&rl->rbuf)) > 0)) {
rr->length = 0;
rl->packet_length = 0;
goto again;
/*
* rfc9147:
- * This procedure requires the ciphertext length to be at least
- * DTLS13_CIPHERTEXT_MINSIZE (16) bytes.
+ * This procedure requires the ciphertext length to be at least 16 bytes.
* Receivers MUST reject shorter records as if they had failed deprotection
*/
if (DTLS13_UNI_HDR_FIX_BITS_IS_SET(rr->type)
&& rl->version == DTLS1_3_VERSION
- && (!ossl_assert(rl->sn_enc_ctx != NULL)
- || !ossl_assert(rl->packet_length >= rechdrlen + DTLS13_CIPHERTEXT_MINSIZE)
+ && ossl_assert(rl->sn_enc_ctx != NULL)
+ && (!ossl_assert(rl->packet_length >= rechdrlen + DTLS13_CIPHERTEXT_MINSIZE)
|| !dtls_crypt_sequence_number(rl->sn_enc_ctx,
recseqnum + recseqnumoffs,
recseqnumlen,
goto again;
}
- memset(rl->sequence, 0, sizeof(rl->sequence));
- memcpy(rl->sequence + 2, recseqnum, sizeof(recseqnum));
+ /* TODO(DTLSv1.3): make recseqnum a uint64_t */
+ rl->sequence = ((uint64_t)recseqnum[0]) << 40;
+ rl->sequence |= ((uint64_t)recseqnum[1]) << 32;
+ rl->sequence |= ((uint64_t)recseqnum[2]) << 24;
+ rl->sequence |= ((uint64_t)recseqnum[3]) << 16;
+ rl->sequence |= ((uint64_t)recseqnum[4]) << 8;
+ rl->sequence |= ((uint64_t)recseqnum[5]) << 0;
/* match epochs. NULL means the packet is dropped on the floor */
bitmap = dtls_get_bitmap(rl, rr, &is_next_epoch);
size_t maxcomplen;
int unifiedheader = rl->version == DTLS1_3_VERSION && rl->epoch > 0;
+ templ->sequence_number = rl->sequence;
+ templ->epoch = rl->epoch;
*recdata = NULL;
-
maxcomplen = templ->buflen;
+
if (rl->compctx != NULL)
maxcomplen += SSL3_RT_MAX_COMPRESSED_OVERHEAD;
uint8_t unifiedhdrbits = fixedbits | cbit | sbit | lbit | ebits;
if (!WPACKET_put_bytes_u8(thispkt, unifiedhdrbits)
- || !WPACKET_memcpy(thispkt, rl->sequence + 6, 2)
+ || (sbit ? !WPACKET_put_bytes_u16(thispkt, rl->sequence)
+ : !WPACKET_put_bytes_u8(thispkt, rl->sequence))
|| !WPACKET_start_sub_packet_u16(thispkt)
|| (rl->eivlen > 0
&& !WPACKET_allocate_bytes(thispkt, rl->eivlen, NULL))
} else {
if (!WPACKET_put_bytes_u8(thispkt, rectype)
|| !WPACKET_put_bytes_u16(thispkt, templ->version)
- || !WPACKET_put_bytes_u16(thispkt, rl->epoch)
- || !WPACKET_memcpy(thispkt, &(rl->sequence[2]), 6)
+ || !WPACKET_put_bytes_u16(thispkt, templ->epoch)
+ || !WPACKET_put_bytes_u48(thispkt, templ->sequence_number)
|| !WPACKET_start_sub_packet_u16(thispkt)
|| (rl->eivlen > 0
&& !WPACKET_allocate_bytes(thispkt, rl->eivlen, NULL))
COMP_METHOD *comp)
{
ktls_crypto_info_t crypto_info;
+ unsigned char recseq[SEQ_NUM_SIZE], *p_recseq = recseq;
+
+ l2n8(rl->sequence, p_recseq);
/*
* Check if we are suitable for KTLS. If not suitable we return
return OSSL_RECORD_RETURN_NON_FATAL_ERR;
}
- if (!ktls_configure_crypto(rl->libctx, rl->version, ciph, md, rl->sequence,
+ if (!ktls_configure_crypto(rl->libctx, rl->version, ciph, md, recseq,
&crypto_info,
rl->direction == OSSL_RECORD_DIRECTION_WRITE,
iv, ivlen, key, keylen, mackey, mackeylen))
typedef struct dtls_bitmap_st {
/* Track 64 packets */
uint64_t map;
- /* Max record number seen so far, 64-bit value in big-endian encoding */
- unsigned char max_seq_num[SEQ_NUM_SIZE];
+ /* Max record number seen so far */
+ uint64_t max_seq_num;
} DTLS_BITMAP;
typedef struct ssl_mac_buf_st {
uint16_t epoch;
/* sequence number, needed by DTLS1 */
/* r */
- unsigned char seq_num[SEQ_NUM_SIZE];
+ uint64_t seq_num;
} TLS_RL_RECORD;
/* Macros/functions provided by the TLS_RL_RECORD component */
size_t packet_length;
/* Sequence number for the next record */
- unsigned char sequence[SEQ_NUM_SIZE];
+ uint64_t sequence;
/* Alert code to be used if an error occurs */
int alert;
|| (rl)->version == DTLS1_VERSION \
|| (rl)->version == DTLS1_2_VERSION)
-void ossl_tls_rl_record_set_seq_num(TLS_RL_RECORD *r,
- const unsigned char *seq_num);
-
int ossl_set_tls_provider_parameters(OSSL_RECORD_LAYER *rl,
EVP_CIPHER_CTX *ctx,
const EVP_CIPHER *ciph,
int tls_set1_bio(OSSL_RECORD_LAYER *rl, BIO *bio);
int tls_read_record(OSSL_RECORD_LAYER *rl, void **rechandle, int *rversion,
uint8_t *type, const unsigned char **data, size_t *datalen,
- uint16_t *epoch, unsigned char *seq_num);
+ uint16_t *epoch, uint64_t *seq_num);
int tls_release_record(OSSL_RECORD_LAYER *rl, void *rechandle, size_t length);
int tls_set_protocol_version(OSSL_RECORD_LAYER *rl, int version);
void tls_set_plain_alerts(OSSL_RECORD_LAYER *rl, int allow);
static int ssl3_mac(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *rec, unsigned char *md,
int sending)
{
- unsigned char *mac_sec, *seq = rl->sequence;
+ /*
+ * npad is, at most, 48 bytes and that's with MD5:
+ * 16 + 48 + 8 (sequence bytes) + 1 + 2 = 75.
+ *
+ * With SHA-1 (the largest hash speced for SSLv3) the hash size
+ * goes up 4, but npad goes down by 8, resulting in a smaller
+ * total size.
+ */
+ unsigned char header[75];
+ WPACKET hdr;
+ size_t hdr_written;
const EVP_MD_CTX *hash;
- unsigned char *p, rec_char;
size_t md_size;
size_t npad;
- int t;
+ int t, cbc_encrypted;
- mac_sec = &(rl->mac_secret[0]);
hash = rl->md_ctx;
t = EVP_MD_CTX_get_size(hash);
if (!sending
&& EVP_CIPHER_CTX_get_mode(rl->enc_ctx) == EVP_CIPH_CBC_MODE
- && ssl3_cbc_record_digest_supported(hash)) {
+ && ssl3_cbc_record_digest_supported(hash))
+ cbc_encrypted = 1;
+ else
+ cbc_encrypted = 0;
+
+ if (!WPACKET_init_static_len(&hdr, header, sizeof(header), 0)
+ || !WPACKET_memcpy(&hdr, rl->mac_secret, md_size)
+ || !WPACKET_memcpy(&hdr, ssl3_pad_1, npad)
+ || !WPACKET_put_bytes_u64(&hdr, rl->sequence)
+ || !WPACKET_put_bytes_u8(&hdr, rec->type)
+ || !WPACKET_put_bytes_u16(&hdr, rec->length)
+ || !WPACKET_finish(&hdr)
+ || !WPACKET_get_total_written(&hdr, &hdr_written)) {
+ WPACKET_cleanup(&hdr);
+ return 0;
+ }
+
+ if (cbc_encrypted) {
#ifdef OPENSSL_NO_DEPRECATED_3_0
return 0;
#else
* are hashing because that gives an attacker a timing-oracle.
*/
- /*-
- * npad is, at most, 48 bytes and that's with MD5:
- * 16 + 48 + 8 (sequence bytes) + 1 + 2 = 75.
- *
- * With SHA-1 (the largest hash speced for SSLv3) the hash size
- * goes up 4, but npad goes down by 8, resulting in a smaller
- * total size.
- */
- unsigned char header[75];
- size_t j = 0;
- memcpy(header + j, mac_sec, md_size);
- j += md_size;
- memcpy(header + j, ssl3_pad_1, npad);
- j += npad;
- memcpy(header + j, seq, 8);
- j += 8;
- header[j++] = rec->type;
- header[j++] = (unsigned char)(rec->length >> 8);
- header[j++] = (unsigned char)(rec->length & 0xff);
-
/* Final param == is SSLv3 */
if (ssl3_cbc_digest_record(EVP_MD_CTX_get0_md(hash),
md, &md_size,
header, rec->input,
rec->length, rec->orig_len,
- mac_sec, md_size, 1) <= 0)
+ rl->mac_secret, md_size, 1) <= 0)
return 0;
#endif
} else {
if (md_ctx == NULL)
return 0;
- rec_char = rec->type;
- p = md;
- s2n(rec->length, p);
if (EVP_MD_CTX_copy_ex(md_ctx, hash) <= 0
- || EVP_DigestUpdate(md_ctx, mac_sec, md_size) <= 0
- || EVP_DigestUpdate(md_ctx, ssl3_pad_1, npad) <= 0
- || EVP_DigestUpdate(md_ctx, seq, 8) <= 0
- || EVP_DigestUpdate(md_ctx, &rec_char, 1) <= 0
- || EVP_DigestUpdate(md_ctx, md, 2) <= 0
+ || EVP_DigestUpdate(md_ctx, header, hdr_written) <= 0
|| EVP_DigestUpdate(md_ctx, rec->input, rec->length) <= 0
|| EVP_DigestFinal_ex(md_ctx, md, NULL) <= 0
|| EVP_MD_CTX_copy_ex(md_ctx, hash) <= 0
- || EVP_DigestUpdate(md_ctx, mac_sec, md_size) <= 0
+ || EVP_DigestUpdate(md_ctx, rl->mac_secret, md_size) <= 0
|| EVP_DigestUpdate(md_ctx, ssl3_pad_2, npad) <= 0
|| EVP_DigestUpdate(md_ctx, md, md_size) <= 0
|| EVP_DigestFinal_ex(md_ctx, md, &md_size_u) <= 0) {
int isdtls, sbit = 0, addlen;
unsigned char *staticiv;
unsigned char *nonce;
- unsigned char *seq = rl->sequence;
+ unsigned char seq[SEQ_NUM_SIZE], *p_seq = seq;
int lenu, lenf;
TLS_RL_RECORD *rec = &recs[0];
WPACKET wpkt;
staticiv = rl->iv;
nonce = rl->nonce;
isdtls = rl->isdtls;
+ l2n8(rl->sequence, p_seq);
if (enc_ctx == NULL && rl->mac_ctx == NULL) {
RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
if ((isdtls && !ossl_assert(!DTLS13_UNI_HDR_CID_BIT_IS_SET(rec->type)))
|| !WPACKET_init_static_len(&wpkt, recheader, sizeof(recheader), 0)
|| !WPACKET_put_bytes_u8(&wpkt, rec->type)
- || (isdtls && (sbit ? !WPACKET_memcpy(&wpkt, rl->sequence + 6, 2)
- : !WPACKET_memcpy(&wpkt, rl->sequence + 7, 1)))
+ || (isdtls && (sbit ? !WPACKET_put_bytes_u16(&wpkt, rl->sequence)
+ : !WPACKET_put_bytes_u8(&wpkt, rl->sequence)))
|| (!isdtls && !WPACKET_put_bytes_u16(&wpkt, rec->rec_version))
|| (addlen && !WPACKET_put_bytes_u16(&wpkt, rec->length + rl->taglen))
|| !WPACKET_get_total_written(&wpkt, &hdrlen)
return OSSL_RECORD_RETURN_SUCCESS;
}
+static int setup_record_header(const OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *rec,
+ unsigned char *buf, size_t buflen)
+{
+ WPACKET hdr;
+ size_t hdrsize;
+
+ if (buflen < EVP_AEAD_TLS1_AAD_LEN
+ || !WPACKET_init_static_len(&hdr, buf, EVP_AEAD_TLS1_AAD_LEN, 0)
+ || (rl->isdtls && !WPACKET_put_bytes_u16(&hdr, rl->epoch))
+ || (rl->isdtls ? !WPACKET_put_bytes_u48(&hdr, rl->sequence)
+ : !WPACKET_put_bytes_u64(&hdr, rl->sequence))
+ || !WPACKET_put_bytes_u8(&hdr, rec->type)
+ || !WPACKET_put_bytes_u16(&hdr, rl->version)
+ || !WPACKET_put_bytes_u16(&hdr, rec->length)
+ || !WPACKET_finish(&hdr)
+ || !WPACKET_get_total_written(&hdr, &hdrsize)
+ || hdrsize != EVP_AEAD_TLS1_AAD_LEN) {
+ WPACKET_cleanup(&hdr);
+ return 0;
+ }
+
+ return 1;
+}
+
#define MAX_PADDING 256
/*-
* tls1_cipher encrypts/decrypts |n_recs| in |recs|. Calls RLAYERfatal on
if ((EVP_CIPHER_get_flags(EVP_CIPHER_CTX_get0_cipher(ds))
& EVP_CIPH_FLAG_AEAD_CIPHER) != 0) {
- unsigned char *seq;
-
- seq = rl->sequence;
-
- if (rl->isdtls) {
- unsigned char dtlsseq[8], *p = dtlsseq;
+ if (!setup_record_header(rl, &recs[ctr], buf[ctr], sizeof(buf[ctr]))) {
+ RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
- s2n(rl->epoch, p);
- memcpy(p, &seq[2], 6);
- memcpy(buf[ctr], dtlsseq, 8);
- } else {
- memcpy(buf[ctr], seq, 8);
- if (!tls_increment_sequence_ctr(rl)) {
- /* RLAYERfatal already called */
- return 0;
- }
+ if (!rl->isdtls && !tls_increment_sequence_ctr(rl)) {
+ /* RLAYERfatal already called */
+ return 0;
}
- buf[ctr][8] = recs[ctr].type;
- buf[ctr][9] = (unsigned char)(rl->version >> 8);
- buf[ctr][10] = (unsigned char)(rl->version);
- buf[ctr][11] = (unsigned char)(recs[ctr].length >> 8);
- buf[ctr][12] = (unsigned char)(recs[ctr].length & 0xff);
pad = EVP_CIPHER_CTX_ctrl(ds, EVP_CTRL_AEAD_TLS1_AAD,
EVP_AEAD_TLS1_AAD_LEN, buf[ctr]);
if (pad <= 0) {
if (!rl->isdtls && rl->tlstree) {
int decrement_seq = 0;
+ unsigned char recseq[SEQ_NUM_SIZE], *p_recseq = recseq;
+
+ l2n8(rl->sequence, p_recseq);
/*
* When sending, seq is incremented after MAC calculation.
if (sending && !rl->use_etm)
decrement_seq = 1;
- if (EVP_CIPHER_CTX_ctrl(ds, EVP_CTRL_TLSTREE, decrement_seq,
- rl->sequence) <= 0) {
-
+ if (EVP_CIPHER_CTX_ctrl(ds, EVP_CTRL_TLSTREE, decrement_seq, recseq) <= 0) {
RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
}
if (!EVP_CipherUpdate(ds, recs[0].data, &outlen, recs[0].input,
- (unsigned int)reclen[0]))
+ (int)reclen[0]))
return 0;
recs[0].length = outlen;
static int tls1_mac(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *rec, unsigned char *md,
int sending)
{
- unsigned char *seq = rl->sequence;
+ unsigned char seq[SEQ_NUM_SIZE], *p_seq = seq;
EVP_MD_CTX *hash;
size_t md_size;
EVP_MD_CTX *hmac = NULL, *mac_ctx;
int ret = 0;
hash = rl->md_ctx;
+ l2n8(rl->sequence, p_seq);
t = EVP_MD_CTX_get_size(hash);
if (!ossl_assert(t >= 0))
&& EVP_MD_CTX_ctrl(mac_ctx, EVP_MD_CTRL_TLSTREE, 0, seq) <= 0)
goto end;
- if (rl->isdtls) {
- unsigned char dtlsseq[8], *p = dtlsseq;
-
- s2n(rl->epoch, p);
- memcpy(p, &seq[2], 6);
-
- memcpy(header, dtlsseq, 8);
- } else {
- memcpy(header, seq, 8);
- }
-
- header[8] = rec->type;
- header[9] = (unsigned char)(rl->version >> 8);
- header[10] = (unsigned char)(rl->version);
- header[11] = (unsigned char)(rec->length >> 8);
- header[12] = (unsigned char)(rec->length & 0xff);
+ if (!setup_record_header(rl, rec, header, sizeof(header)))
+ goto end;
if (!sending && !rl->use_etm
&& EVP_CIPHER_CTX_get_mode(rl->enc_ctx) == EVP_CIPH_CBC_MODE
}
}
-void ossl_tls_rl_record_set_seq_num(TLS_RL_RECORD *r,
- const unsigned char *seq_num)
-{
- memcpy(r->seq_num, seq_num, SEQ_NUM_SIZE);
-}
-
void ossl_rlayer_fatal(OSSL_RECORD_LAYER *rl, int al, int reason,
const char *fmt, ...)
{
rl->curr_rec = 0;
rl->num_released = 0;
/* Reset the read sequence */
- memset(rl->sequence, 0, sizeof(rl->sequence));
+ rl->sequence = 0;
ret = 1;
goto end;
}
{
if (rec->type != SSL3_RT_APPLICATION_DATA
&& rec->type != SSL3_RT_ALERT
- && rec->type != SSL3_RT_HANDSHAKE) {
+ && rec->type != SSL3_RT_HANDSHAKE
+ && (!rl->isdtls || rec->type != SSL3_RT_ACK)) {
RLAYERfatal(rl, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_BAD_RECORD_TYPE);
return 0;
}
int tls_read_record(OSSL_RECORD_LAYER *rl, void **rechandle, int *rversion,
uint8_t *type, const unsigned char **data, size_t *datalen,
- uint16_t *epoch, unsigned char *seq_num)
+ uint16_t *epoch, uint64_t *seq_num)
{
TLS_RL_RECORD *rec;
*datalen = rec->length;
if (rl->isdtls) {
*epoch = rec->epoch;
- memcpy(seq_num, rec->seq_num, sizeof(rec->seq_num));
+ *seq_num = rec->seq_num;
}
return OSSL_RECORD_RETURN_SUCCESS;
int tls_increment_sequence_ctr(OSSL_RECORD_LAYER *rl)
{
- int i;
-
/* Increment the sequence counter */
- for (i = SEQ_NUM_SIZE; i > 0; i--) {
- ++(rl->sequence[i - 1]);
- if (rl->sequence[i - 1] != 0)
- break;
- }
- if (i == 0) {
+ if (++rl->sequence == 0) {
/* Sequence has wrapped */
RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, SSL_R_SEQUENCE_CTR_WRAPPED);
return 0;
{
#if !defined(OPENSSL_NO_MULTIBLOCK) && EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK
size_t i;
- size_t totlen;
+ size_t totlen, aad_written;
TLS_BUFFER *wb;
unsigned char aad[13];
+ WPACKET aad_pkt;
EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM mb_param;
size_t packlen;
int packleni;
}
wb = &rl->wbuf[0];
+ if (!WPACKET_init_static_len(&aad_pkt, aad, sizeof(aad), 0)
+ || !WPACKET_put_bytes_u64(&aad_pkt, rl->sequence)
+ || !WPACKET_put_bytes_u8(&aad_pkt, templates[0].type)
+ || !WPACKET_put_bytes_u16(&aad_pkt, templates[0].version)
+ || !WPACKET_put_bytes_u16(&aad_pkt, 0)
+ || !WPACKET_get_total_written(&aad_pkt, &aad_written)
+ || aad_written != sizeof(aad)
+ || !WPACKET_finish(&aad_pkt)) {
+ WPACKET_cleanup(&aad_pkt);
+ RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return -1;
+ }
+
mb_param.interleave = (unsigned int)numtempl;
- memcpy(aad, rl->sequence, 8);
- aad[8] = templates[0].type;
- aad[9] = (unsigned char)(templates[0].version >> 8);
- aad[10] = (unsigned char)(templates[0].version);
- aad[11] = 0;
- aad[12] = 0;
mb_param.out = NULL;
mb_param.inp = aad;
mb_param.len = totlen;
return -1;
}
- rl->sequence[7] += mb_param.interleave;
- if (rl->sequence[7] < mb_param.interleave) {
- int j = 6;
- while (j >= 0 && (++rl->sequence[j--]) == 0) ;
- }
+ rl->sequence += mb_param.interleave;
wb->offset = 0;
wb->left = packlen;
return -1;
rdata = OPENSSL_malloc(sizeof(*rdata));
- item = pitem_new(rec->seq_num, rdata);
+ item = pitem_new_u64(rec->seq_num, rdata);
if (rdata == NULL || item == NULL) {
OPENSSL_free(rdata);
pitem_free(item);
&rr->rechandle,
&rr->version, &rr->type,
&rr->data, &rr->length,
- &rr->epoch, rr->seq_num));
+ &rr->epoch, &rr->seq_num));
if (ret <= 0) {
ret = dtls1_read_failed(sc, ret);
/*
return 0;
}
+ if (rr->type == SSL3_RT_HANDSHAKE && SSL_CONNECTION_IS_DTLS13(sc)) {
+ sc->s3.tmp.record_epoch = rr->epoch;
+ sc->s3.tmp.record_seq_num = rr->seq_num;
+ }
+
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
}
#endif
*readbytes = n;
+
return 1;
}
/*
* 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) {
unsigned char msg_type;
/*
}
goto start;
}
+ }
+ if (!ossl_statem_get_in_handshake(sc)
+ && (rr->type == SSL3_RT_HANDSHAKE || rr->type == SSL3_RT_ACK)) {
/*
* To get here we must be trying to read app data but found handshake
* data. But if we're trying to read app data, and we're not in init
*/
SSLfatal(sc, SSL_AD_UNEXPECTED_MESSAGE, ERR_R_INTERNAL_ERROR);
return -1;
+
+ case SSL3_RT_ACK:
+ switch (sc->negotiated_version) {
+ case DTLS1_3_VERSION:
+ /* ACK should have been handled if DTLSv1.3 has been negotiated. */
+ SSLfatal(sc, SSL_AD_UNEXPECTED_MESSAGE, ERR_R_INTERNAL_ERROR);
+ return -1;
+
+ case DTLS_ANY_VERSION:
+ /*
+ * This must be an ACK from a DTLSv1.3 server for a partial
+ * ClientHello. We always send the full message again if the
+ * ClientHello is not responded to with a ServerHello before the
+ * timer runs out. Drop the record.
+ */
+ if (!ssl_release_record(sc, rr, 0))
+ return -1;
+ goto start;
+
+ default:
+ /*
+ * If we receive an ACK record when we have negotiated a lower version
+ * than DTLSv1.3 then we respond with an unexpected record fatal alert.
+ */
+ SSLfatal(sc, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_UNEXPECTED_RECORD);
+ return -1;
+ }
+
case SSL3_RT_APPLICATION_DATA:
/*
* At this point, we were expecting handshake data, but have
if (ret > 0)
*written = len;
+ /*
+ * Add record number to the buffered sent message
+ */
+ if (type == SSL3_RT_HANDSHAKE && ret > 0 && SSL_CONNECTION_IS_DTLS13(sc)) {
+ pitem *item;
+ unsigned char prio[8];
+
+ dtls1_get_queue_priority(prio, sc->d1->w_msg.msg_seq, 0);
+ item = pqueue_find(&sc->d1->sent_messages, prio);
+
+ if (item == NULL)
+ return ret;
+
+ if (dtls_msg_needs_ack(sc->server, sc->d1->w_msg.msg_type)) {
+ dtls_sent_msg *sent_msg;
+ DTLS1_RECORD_NUMBER *rec_num;
+
+ sent_msg = (dtls_sent_msg *) item->data;
+ rec_num = dtls1_record_number_new(tmpl.epoch, tmpl.sequence_number);
+
+ if (rec_num == NULL)
+ return -1;
+
+ ossl_list_record_number_insert_tail(&sent_msg->rec_nums, rec_num);
+ }
+ }
+
return ret;
}
/* epoch number. DTLS only */
uint16_t epoch;
/* sequence number. DTLS only */
- unsigned char seq_num[SEQ_NUM_SIZE];
+ uint64_t seq_num;
#ifndef OPENSSL_NO_SCTP
struct bio_dgram_sctp_rcvinfo recordinfo;
#endif
size_t len, size_t *written);
void dtls1_increment_epoch(SSL_CONNECTION *s, int rw);
uint16_t dtls1_get_epoch(SSL_CONNECTION *s, int rw);
+uint64_t dtls1_get_record_sequence_number(SSL_CONNECTION *s);
int ssl_release_record(SSL_CONNECTION *s, TLS_RECORD *rr, size_t length);
# define HANDLE_RLAYER_READ_RETURN(s, ret) \
# include "internal/tsan_assist.h"
# include "internal/bio.h"
# include "internal/ktls.h"
+# include "internal/list.h"
# include "internal/time.h"
# include "internal/ssl.h"
# include "internal/cryptlib.h"
size_t peer_finish_md_len;
size_t message_size;
int message_type;
+ uint64_t record_epoch;
+ uint64_t record_seq_num;
/* used to hold the new cipher we are going to use */
const SSL_CIPHER *new_cipher;
EVP_PKEY *pkey; /* holds short lived key exchange key */
typedef struct pitem_st *piterator;
pitem *pitem_new(unsigned char *prio64be, void *data);
+pitem *pitem_new_u64(uint64_t prio, void *data);
void pitem_free(pitem *item);
pqueue *pqueue_new(void);
void pqueue_free(pqueue *pq);
pitem *pqueue_peek(pqueue *pq);
pitem *pqueue_pop(pqueue *pq);
pitem *pqueue_find(pqueue *pq, unsigned char *prio64be);
+pitem *pqueue_find_u64(pqueue *pq, uint64_t prio);
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;
} dtls_msg_info;
+/* rfc9147, section 4 */
+typedef struct dtls1_record_number_st DTLS1_RECORD_NUMBER;
+
+struct dtls1_record_number_st {
+ uint64_t epoch;
+ uint64_t seqnum;
+ OSSL_LIST_MEMBER(record_number, DTLS1_RECORD_NUMBER);
+};
+
+DEFINE_LIST_OF(record_number, DTLS1_RECORD_NUMBER);
+
+DTLS1_RECORD_NUMBER *dtls1_record_number_new(uint64_t epoch, uint64_t seqnum);
+
+void ossl_list_record_number_elem_free(OSSL_LIST(record_number) *p_list);
+
typedef struct dtls_sent_msg_st {
dtls_msg_info msg_info;
- int record_type;
+ OSSL_LIST(record_number) rec_nums;
unsigned char *msg_buf;
struct dtls1_retransmit_state saved_retransmit_state;
} dtls_sent_msg;
+int dtls_any_sent_messages_are_missing_acknowledge(SSL_CONNECTION *s);
+
+static ossl_inline int dtls_msg_needs_ack(int sentbyserver, unsigned char msgtype)
+{
+ switch (msgtype) {
+ case SSL3_MT_NEWSESSION_TICKET:
+ case SSL3_MT_KEY_UPDATE:
+ return 1;
+
+ case SSL3_MT_CERTIFICATE:
+ case SSL3_MT_COMPRESSED_CERTIFICATE:
+ case SSL3_MT_CERTIFICATE_VERIFY:
+ case SSL3_MT_FINISHED:
+ if (!sentbyserver)
+ return 1;
+ /* fall-through */
+ default:
+ return 0;
+ }
+}
+
typedef struct dtls1_state_st {
unsigned char cookie[DTLS1_COOKIE_LENGTH];
size_t cookie_len;
int shutdown_received;
# endif
+ /* Sequence numbers that are to be acknowledged */
+ OSSL_LIST(record_number) ack_rec_num;
+
DTLS_timer_cb timer_cb;
} DTLS1_STATE;
__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);
+__owur int dtls1_retransmit_message(SSL_CONNECTION *s, dtls_sent_msg *sent_msg);
+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_clear_sent_buffer(SSL_CONNECTION *s, int keep_unacked_msgs);
+void dtls1_acknowledge_sent_buffer(SSL_CONNECTION *s, uint16_t before_epoch);
__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);
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 client ack";
+ case TLS_ST_CW_ACK:
+ return "DTLSv1.3 write client ack";
+ case TLS_ST_SR_ACK:
+ return "DTLSv1.3 read server ack";
+ case TLS_ST_SW_ACK:
+ return "DTLSv1.3 write server ack";
default:
return "unknown state";
}
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";
}
st->read_state = READ_STATE_HEADER;
}
-static int grow_init_buf(SSL_CONNECTION *s, size_t size) {
-
+static int grow_init_buf(SSL_CONNECTION *s, size_t size)
+{
size_t msg_offset = (char *)s->init_msg - s->init_buf->data;
if (!BUF_MEM_grow_clean(s->init_buf, size))
*/
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 {
+ switch (st->hand_state) {
+ case TLS_ST_CW_CHANGE:
+ case TLS_ST_SW_CHANGE:
+ record_type = SSL3_RT_CHANGE_CIPHER_SPEC;
+
+ break;
+ case TLS_ST_CW_ACK:
+ case TLS_ST_SW_ACK:
+ record_type = SSL3_RT_ACK;
+
+ break;
+ default:
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);
}
/*
CON_FUNC_RETURN (**confunc) (SSL_CONNECTION *s,
WPACKET *pkt),
int *mt);
+ int (*dtls_use_timer) (SSL_CONNECTION *s);
void (*cb) (const SSL *ssl, int type, int val) = NULL;
CON_FUNC_RETURN (*confunc) (SSL_CONNECTION *s, WPACKET *pkt);
int mt;
pre_work = ossl_statem_server_pre_work;
post_work = ossl_statem_server_post_work;
get_construct_message_f = ossl_statem_server_construct_message;
+ dtls_use_timer = ossl_statem_dtls_server_use_timer;
} else {
transition = ossl_statem_client_write_transition;
pre_work = ossl_statem_client_pre_work;
post_work = ossl_statem_client_post_work;
get_construct_message_f = ossl_statem_client_construct_message;
+ dtls_use_timer = ossl_statem_dtls_client_use_timer;
}
while (1) {
/* Fall through */
case WRITE_STATE_SEND:
- if (SSL_CONNECTION_IS_DTLS(s) && st->use_timer) {
+ if (SSL_CONNECTION_IS_DTLS(s) && dtls_use_timer(s))
dtls1_start_timer(s);
- }
+
ret = statem_do_write(s);
if (ret <= 0) {
return SUB_STATE_ERROR;
}
break;
+ case TLS_ST_CW_KEY_UPDATE:
+ case TLS_ST_CW_FINISHED:
+ case TLS_ST_CR_ACK:
case TLS_ST_OK:
+ if (mt == DTLS13_MT_ACK) {
+ st->hand_state = TLS_ST_CR_ACK;
+ return 1;
+ }
if (mt == SSL3_MT_NEWSESSION_TICKET) {
st->hand_state = TLS_ST_CR_SESSION_TICKET;
return 1;
return WRITE_TRAN_CONTINUE;
case TLS_ST_CR_KEY_UPDATE:
- case TLS_ST_CW_KEY_UPDATE:
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:
+ if (SSL_CONNECTION_IS_DTLS13(s)
+ && dtls_any_sent_messages_are_missing_acknowledge(s)) {
+ /* We wait for ACK */
+ return WRITE_TRAN_FINISHED;
+ }
+ 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;
}
}
+static int ossl_statem_dtls_client13_use_timer(SSL_CONNECTION *s)
+{
+ OSSL_STATEM *st = &s->statem;
+
+ switch (st->hand_state) {
+ default:
+ break;
+
+ case TLS_ST_CW_ACK:
+ /* Fall through */
+
+ case TLS_ST_OK:
+ return 0;
+ }
+
+ return 1;
+}
+
+int ossl_statem_dtls_client_use_timer(SSL_CONNECTION *s)
+{
+ OSSL_STATEM *st = &s->statem;
+
+ if (SSL_CONNECTION_IS_DTLS13(s))
+ return ossl_statem_dtls_client13_use_timer(s);
+
+ switch (st->hand_state) {
+ default:
+ break;
+
+ case TLS_ST_CW_CHANGE:
+ /*
+ * We're into the last flight so we don't retransmit these
+ * messages unless we need to.
+ */
+ if (s->hit)
+ st->use_timer = 0;
+ break;
+ }
+
+ return st->use_timer;
+}
+
/*
* Perform any pre work that needs to be done prior to sending a message from
* the client to the server.
case TLS_ST_CW_CHANGE:
if (SSL_CONNECTION_IS_DTLS(s)) {
- if (s->hit) {
- /*
- * We're into the last flight so we don't retransmit these
- * messages unless we need to.
- */
- st->use_timer = 0;
- }
#ifndef OPENSSL_NO_SCTP
if (BIO_dgram_is_sctp(SSL_get_wbio(SSL_CONNECTION_GET_SSL(s)))) {
/* Calls SSLfatal() as required */
return WORK_ERROR;
}
break;
+
+ case TLS_ST_CW_ACK:
+ if (statem_flush(s) != 1)
+ return WORK_MORE_A;
+ break;
}
return WORK_FINISHED_CONTINUE;
*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;
case TLS_ST_CR_KEY_UPDATE:
return KEY_UPDATE_MAX_LENGTH;
+
+ case TLS_ST_CR_ACK:
+ return ACK_MAX_LENGTH;
}
}
case TLS_ST_CR_KEY_UPDATE:
return tls_process_key_update(s, pkt);
+
+ case TLS_ST_CR_ACK:
+ return dtls_process_ack(s, pkt);
}
}
void dtls1_sent_msg_free(dtls_sent_msg *msg)
{
+ if (msg != NULL)
+ ossl_list_record_number_elem_free(&msg->rec_nums);
+
OPENSSL_free(msg);
}
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);
SSL *ussl = SSL_CONNECTION_GET_USER_SSL(s);
uint8_t saved_payload[DTLS1_HM_HEADER_LENGTH];
- 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;
- }
+ /* msg_len, msg_seq, msg_type are only used for recordtype == SSL3_RT_HANDSHAKE */
+ const size_t msg_len = s->d1->w_msg.msg_body_len;
+ const unsigned short msg_seq = s->d1->w_msg.msg_seq;
+ const unsigned char msg_type = s->d1->w_msg.msg_type;
if (!dtls1_query_mtu(s))
return -1;
rec_data = (unsigned char *)s->init_buf->data;
- if (*mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
- if (s->msg_callback) {
- s->msg_callback(0, s->version, SSL3_RT_CHANGE_CIPHER_SPEC,
- rec_data, 1, SSL_CONNECTION_GET_USER_SSL(s),
- s->msg_callback_arg);
- }
- /*
- * This isn't a real handshake message so skip the processing below.
- */
+ /*
+ * If this isn't a real handshake message skip the processing below.
+ */
+ if (*mt == SSL3_MT_CHANGE_CIPHER_SPEC || *mt == DTLS13_MT_ACK)
return 1;
- }
/* reconstruct message header */
dtls1_write_hm_header(rec_data, s->s3.tmp.message_type, s->s3.tmp.message_size,
*/
int dtls_get_message_body(SSL_CONNECTION *s, size_t *len)
{
- if (s->s3.tmp.message_type == SSL3_MT_CHANGE_CIPHER_SPEC) {
- /* Nothing to be done */
+ unsigned char *msg = (unsigned char *)s->init_buf->data;
+ size_t msg_len;
+ int recordtype;
+
+ switch (s->s3.tmp.message_type) {
+ default:
+ recordtype = SSL3_RT_HANDSHAKE;
+ msg_len = s->init_num + DTLS1_HM_HEADER_LENGTH;
+
+ break;
+ case DTLS13_MT_ACK:
+ recordtype = SSL3_RT_ACK;
+ msg_len = s->init_num;
+
+ goto end;
+ case SSL3_MT_CHANGE_CIPHER_SPEC:
+ recordtype = SSL3_RT_CHANGE_CIPHER_SPEC;
+ msg_len = 1;
+
goto end;
}
+
/*
* If receiving Finished, record MAC of prior handshake messages for
* Finished verification.
if (!tls_common_finish_mac(s))
return 0;
+end:
if (s->msg_callback)
- s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE,
- s->init_buf->data, s->init_num + DTLS1_HM_HEADER_LENGTH,
+ s->msg_callback(0, s->version, recordtype, msg, msg_len,
SSL_CONNECTION_GET_USER_SSL(s), s->msg_callback_arg);
- end:
*len = s->init_num;
return 1;
}
return 1;
}
+static int add_record_to_ack_list(SSL_CONNECTION *sc)
+{
+ DTLS1_RECORD_NUMBER *recnum;
+ uint64_t epoch = sc->s3.tmp.record_epoch;
+ uint64_t sequence = sc->s3.tmp.record_seq_num;
+
+ for (recnum = ossl_list_record_number_head(&sc->d1->ack_rec_num);
+ recnum != NULL;
+ recnum = ossl_list_record_number_next(recnum)) {
+ /* Is the record number already in the list? */
+ if (recnum->epoch == epoch && recnum->seqnum == sequence)
+ return 1;
+ }
+
+ recnum = dtls1_record_number_new(epoch, sequence);
+
+ if (recnum == NULL)
+ return 0;
+
+ ossl_list_record_number_insert_tail(&sc->d1->ack_rec_num, recnum);
+
+ return 1;
+}
+
/*
* Returns 1 if there is a buffered fragment available, 0 if not, or -1 on a
* fatal error.
hm_fragment *frag = NULL;
pitem *item = NULL;
int i = -1, is_complete;
- unsigned char seq64be[8];
size_t frag_len = msg_hdr->frag_len;
size_t readbytes;
SSL *ssl = SSL_CONNECTION_GET_SSL(s);
- if ((msg_hdr->frag_off + frag_len) > msg_hdr->msg_len ||
- msg_hdr->msg_len > dtls1_max_handshake_message_len(s))
+ if ((msg_hdr->frag_off + frag_len) > msg_hdr->msg_len
+ || msg_hdr->msg_len > dtls1_max_handshake_message_len(s))
goto err;
if (frag_len == 0) {
}
/* Try to find item in queue */
- memset(seq64be, 0, sizeof(seq64be));
- seq64be[6] = (unsigned char)(msg_hdr->seq >> 8);
- seq64be[7] = (unsigned char)msg_hdr->seq;
- item = pqueue_find(&s->d1->rcvd_messages, seq64be);
+ item = pqueue_find_u64(&s->d1->rcvd_messages, msg_hdr->seq);
if (item == NULL) {
frag = dtls1_hm_fragment_new(msg_hdr->msg_len, 1);
frag->reassembly = NULL;
if (item == NULL) {
- item = pitem_new(seq64be, frag);
+ item = pitem_new_u64(msg_hdr->seq, frag);
if (item == NULL)
goto err;
item = pqueue_insert(&s->d1->rcvd_messages, item);
/*
* pqueue_insert fails iff a duplicate item is inserted. However,
- * |item| cannot be a duplicate. If it were, |pqueue_find|, above,
+ * |item| cannot be a duplicate. If it were, |pqueue_find_u64|, above,
* would have returned it and control would never have reached this
* branch.
*/
goto err;
}
+ if (dtls_msg_needs_ack(!s->server, msg_hdr->type)
+ && !add_record_to_ack_list(s))
+ goto err;
+
return DTLS1_HM_FRAGMENT_RETRY;
err:
int i = -1;
hm_fragment *frag = NULL;
pitem *item = NULL;
- unsigned char seq64be[8];
size_t frag_len = msg_hdr->frag_len;
size_t readbytes;
SSL *ssl = SSL_CONNECTION_GET_SSL(s);
goto err;
/* Try to find item in queue, to prevent duplicate entries */
- memset(seq64be, 0, sizeof(seq64be));
- seq64be[6] = (unsigned char)(msg_hdr->seq >> 8);
- seq64be[7] = (unsigned char)msg_hdr->seq;
- item = pqueue_find(&s->d1->rcvd_messages, seq64be);
+ item = pqueue_find_u64(&s->d1->rcvd_messages, msg_hdr->seq);
/*
* If we already have an entry and this one is a fragment, don't discard
goto err;
}
- item = pitem_new(seq64be, frag);
+ item = pitem_new_u64(msg_hdr->seq, frag);
if (item == NULL)
goto err;
+ if (dtls_msg_needs_ack(!s->server, msg_hdr->type)
+ && !add_record_to_ack_list(s))
+ goto err;
+
item = pqueue_insert(&s->d1->rcvd_messages, item);
/*
* pqueue_insert fails iff a duplicate item is inserted. However,
*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) {
s->d1->next_handshake_write_seq = 0;
}
+ if (dtls_msg_needs_ack(!s->server, msg_hdr.type)
+ && !add_record_to_ack_list(s)) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto f_err;
+ }
+
/*
* Note that s->init_num is *not* used as current offset in
* s->init_buf->data, but as a counter summing up fragments' lengths: as
return CON_FUNC_SUCCESS;
}
+CON_FUNC_RETURN dtls_construct_ack(SSL_CONNECTION *s, WPACKET *pkt)
+{
+ DTLS1_RECORD_NUMBER *recnum;
+ DTLS1_RECORD_NUMBER *recnumnext = ossl_list_record_number_head(&s->d1->ack_rec_num);
+
+ if (!WPACKET_start_sub_packet_u16(pkt)) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return CON_FUNC_ERROR;
+ }
+
+ while ((recnum = recnumnext) != NULL) {
+ /*
+ * rfc9147: section 4.
+ *
+ * Record numbers are encoded as
+ * struct {
+ * uint64 epoch;
+ * uint64 sequence_number;
+ * } RecordNumber;
+ */
+
+ recnumnext = ossl_list_record_number_next(recnum);
+
+ if (recnum->epoch <= dtls1_get_epoch(s, SSL3_CC_WRITE)) {
+ /*
+ * 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 (!WPACKET_put_bytes_u64(pkt, recnum->epoch)
+ || !WPACKET_put_bytes_u64(pkt, recnum->seqnum)) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return CON_FUNC_ERROR;
+ }
+
+ ossl_list_record_number_remove(&s->d1->ack_rec_num, recnum);
+ OPENSSL_free(recnum);
+ }
+ }
+
+ if (!WPACKET_close(pkt)) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return CON_FUNC_ERROR;
+ }
+
+ return CON_FUNC_SUCCESS;
+}
+
+MSG_PROCESS_RETURN dtls_process_ack(SSL_CONNECTION *s, PACKET *pkt)
+{
+ PACKET record_numbers;
+
+ 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;
+ */
+ pitem *item;
+ piterator iter;
+ 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_DECODE_ERROR, SSL_R_LENGTH_TOO_SHORT);
+ return MSG_PROCESS_ERROR;
+ }
+
+ iter = pqueue_iterator(&s->d1->sent_messages);
+
+ while ((item = pqueue_next(&iter)) != NULL) {
+ dtls_sent_msg *msg = (dtls_sent_msg *)item->data;
+ DTLS1_RECORD_NUMBER *recnum;
+ DTLS1_RECORD_NUMBER *recnum_next = ossl_list_record_number_head(&msg->rec_nums);
+
+ while ((recnum = recnum_next) != NULL) {
+ recnum_next = ossl_list_record_number_next(recnum_next);
+
+ if (recnum->epoch == epoch && recnum->seqnum == sequence_number) {
+ ossl_list_record_number_remove(&msg->rec_nums, recnum);
+ OPENSSL_free(recnum);
+ }
+ }
+ }
+ }
+
+ return MSG_PROCESS_FINISHED_READING;
+}
+
#ifndef OPENSSL_NO_SCTP
/*
* Wait for a dry event. Should only be called at a point in the handshake
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
* priority queues) and fits in the unsigned short variable.
*/
int lsb = (record_type == SSL3_RT_CHANGE_CIPHER_SPEC);
+ const uint16_t prio = seq * 2 - lsb;
- return 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)
{
piterator iter = pqueue_iterator(&s->d1->sent_messages);
pitem *item;
- 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 (SSL_CONNECTION_IS_DTLS13(s)
+ && ossl_list_record_number_is_empty(&sent_msg->rec_nums))
+ /* rfc9147: Implementations must not retransmit acknowledged msgs */
+ continue;
- if (dtls1_retransmit_message(s, (unsigned short)prio, &found) <= 0)
+ if (dtls1_retransmit_message(s, sent_msg) <= 0)
return -1;
}
dtls_sent_msg *sent_msg;
unsigned char seq64be[8];
size_t headerlen;
- int prio;
/*
* this function is called immediately after a message has been
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;
- sent_msg->record_type = record_type;
+ memcpy(&sent_msg->msg_info, &s->d1->w_msg, sizeof(s->d1->w_msg));
/* 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->msg_info.record_type);
item = pitem_new(seq64be, sent_msg);
if (item == NULL) {
return 1;
}
-int dtls1_retransmit_message(SSL_CONNECTION *s, unsigned short seq, int *found)
+int dtls1_retransmit_message(SSL_CONNECTION *s, dtls_sent_msg *sent_msg)
{
int ret;
- /* XDTLS: for now assuming that read/writes are blocking */
- pitem *item;
- dtls_sent_msg *sent_msg;
unsigned long header_length;
- unsigned char seq64be[8];
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;
-
- item = pqueue_find(&s->d1->sent_messages, seq64be);
- if (item == NULL) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- *found = 0;
- return 0;
- }
-
- *found = 1;
- sent_msg = (dtls_sent_msg *)item->data;
-
- if (sent_msg->record_type == SSL3_RT_CHANGE_CIPHER_SPEC)
+ if (sent_msg->msg_info.record_type == SSL3_RT_CHANGE_CIPHER_SPEC)
header_length = DTLS1_CCS_HEADER_LENGTH;
else
header_length = DTLS1_HM_HEADER_LENGTH;
+ /* Clear the record number list to be acked for retransmitted messages */
+ ossl_list_record_number_elem_free(&sent_msg->rec_nums);
+
memcpy(s->init_buf->data, sent_msg->msg_buf,
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;
*/
s->rlayer.wrlmethod->set1_bio(s->rlayer.wrl, s->wbio);
- ret = dtls1_do_write(s, sent_msg->record_type);
+ ret = dtls1_do_write(s, sent_msg->msg_info.record_type);
/* restore current state */
s->rlayer.wrlmethod = saved_state.wrlmethod;
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)
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;
}
#define SERVER_HELLO_DONE_MAX_LENGTH 0
#define KEY_UPDATE_MAX_LENGTH 1
#define CCS_MAX_LENGTH 1
+#define ACK_MAX_LENGTH 65538
/* Max ServerHello size permitted by RFC 8446 */
#define SERVER_HELLO_MAX_LENGTH 65607
PACKET *pkt);
WORK_STATE ossl_statem_client_post_process_message(SSL_CONNECTION *s,
WORK_STATE wst);
+int ossl_statem_dtls_client_use_timer(SSL_CONNECTION *s);
/*
* TLS/DTLS server state machine functions
PACKET *pkt);
WORK_STATE ossl_statem_server_post_process_message(SSL_CONNECTION *s,
WORK_STATE wst);
+int ossl_statem_dtls_server_use_timer(SSL_CONNECTION *s);
/* Functions for getting new message data */
__owur int tls_get_message_header(SSL_CONNECTION *s, int *mt);
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);
#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);
}
break;
+ case TLS_ST_SR_ACK:
+ 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;
+ }
+ break;
+
case TLS_ST_OK:
/*
* Its never ok to start processing handshake messages in the middle of
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;
}
*/
static WRITE_TRAN ossl_statem_server13_write_transition(SSL_CONNECTION *s)
{
+ OSSL_HANDSHAKE_STATE next_state;
OSSL_STATEM *st = &s->statem;
/*
* 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->deferred_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->deferred_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->deferred_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;
* Following an initial handshake we send the number of tickets we have
* been configured for.
*/
- if (!SSL_IS_FIRST_HANDSHAKE(s) && s->ext.extra_tickets_expected > 0) {
- return WRITE_TRAN_CONTINUE;
- } else if (s->hit || s->num_tickets <= s->sent_tickets) {
+ if ((SSL_IS_FIRST_HANDSHAKE(s) || s->ext.extra_tickets_expected <= 0)
+ && (s->hit || s->num_tickets <= s->sent_tickets)) {
/* We've written enough tickets out. */
st->hand_state = TLS_ST_OK;
}
return WRITE_TRAN_CONTINUE;
+
+ case TLS_ST_SR_ACK:
+ if (SSL_CONNECTION_IS_DTLS13(s)
+ && dtls_any_sent_messages_are_missing_acknowledge(s)) {
+ /* We wait for ACK */
+ return WRITE_TRAN_FINISHED;
+ }
+ st->hand_state = TLS_ST_OK;
+ return WRITE_TRAN_CONTINUE;
+
+ case TLS_ST_SW_ACK:
+ st->hand_state = st->deferred_ack_state;
+
+ return WRITE_TRAN_CONTINUE;
}
}
}
}
+static int ossl_statem_dtls_server13_use_timer(SSL_CONNECTION *s)
+{
+ OSSL_STATEM *st = &s->statem;
+
+ switch (st->hand_state) {
+ default:
+ break;
+
+ case TLS_ST_SW_ACK:
+ /* Fall through */
+
+ case TLS_ST_OK:
+ return 0;
+ }
+
+ return 1;
+}
+
+int ossl_statem_dtls_server_use_timer(SSL_CONNECTION *s)
+{
+ OSSL_STATEM *st = &s->statem;
+
+ if (SSL_CONNECTION_IS_DTLS13(s))
+ return ossl_statem_dtls_server13_use_timer(s);
+
+ switch (st->hand_state) {
+ default:
+ break;
+
+ case TLS_ST_SW_SESSION_TICKET:
+ /*
+ * We're into the last flight. We don't retransmit the last flight
+ * unless we need to, so we don't use the timer
+ */
+ st->use_timer = 0;
+ break;
+
+ case TLS_ST_SW_CHANGE:
+ /*
+ * We're into the last flight. We don't retransmit the last flight
+ * unless we need to, so we don't use the timer. This might have
+ * already been set to 0 if we sent a NewSessionTicket message,
+ * but we'll set it again here in case we didn't.
+ */
+ st->use_timer = 0;
+ break;
+
+ case DTLS_ST_SW_HELLO_VERIFY_REQUEST:
+ /* We don't buffer this message so don't use the timer */
+ st->use_timer = 0;
+ break;
+
+ case TLS_ST_SW_SRVR_HELLO:
+ /*
+ * Messages we write from now on should be buffered and
+ * retransmitted if necessary, so we need to use the timer now
+ */
+ st->use_timer = 1;
+ break;
+ }
+
+ return st->use_timer;
+}
+
/*
* Perform any pre work that needs to be done prior to sending a message from
* the server to the client.
break;
case TLS_ST_SW_HELLO_REQ:
- s->shutdown = 0;
- if (SSL_CONNECTION_IS_DTLS(s))
- dtls1_clear_sent_buffer(s);
- break;
-
+ /* fall-through */
case DTLS_ST_SW_HELLO_VERIFY_REQUEST:
s->shutdown = 0;
- if (SSL_CONNECTION_IS_DTLS(s)) {
- dtls1_clear_sent_buffer(s);
- /* We don't buffer this message so don't use the timer */
- st->use_timer = 0;
- }
- break;
+ if (SSL_CONNECTION_IS_DTLS(s))
+ dtls1_clear_sent_buffer(s, 0);
- case TLS_ST_SW_SRVR_HELLO:
- if (SSL_CONNECTION_IS_DTLS(s)) {
- /*
- * Messages we write from now on should be buffered and
- * retransmitted if necessary, so we need to use the timer now
- */
- st->use_timer = 1;
- }
break;
case TLS_ST_SW_SRVR_DONE:
*/
return tls_finish_handshake(s, wst, 0, 0);
}
- if (SSL_CONNECTION_IS_DTLS(s)) {
- /*
- * We're into the last flight. We don't retransmit the last flight
- * unless we need to, so we don't use the timer
- */
- st->use_timer = 0;
- }
break;
case TLS_ST_SW_CHANGE:
/* SSLfatal() already called */
return WORK_ERROR;
}
- if (SSL_CONNECTION_IS_DTLS(s)) {
- /*
- * We're into the last flight. We don't retransmit the last flight
- * unless we need to, so we don't use the timer. This might have
- * already been set to 0 if we sent a NewSessionTicket message,
- * but we'll set it again here in case we didn't.
- */
- st->use_timer = 0;
- }
return WORK_FINISHED_CONTINUE;
case TLS_ST_EARLY_DATA:
return WORK_MORE_A;
}
break;
+
+ case TLS_ST_SW_ACK:
+ if (statem_flush(s) != 1)
+ return WORK_MORE_A;
+ break;
}
return WORK_FINISHED_CONTINUE;
*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;
case TLS_ST_SR_KEY_UPDATE:
return KEY_UPDATE_MAX_LENGTH;
+
+ case TLS_ST_SR_ACK:
+ return ACK_MAX_LENGTH;
}
}
case TLS_ST_SR_KEY_UPDATE:
return tls_process_key_update(s, pkt);
+ case TLS_ST_SR_ACK:
+ return dtls_process_ack(s, pkt);
}
}
: OSSL_RECORD_PROTECTION_LEVEL_APPLICATION);
if (SSL_CONNECTION_IS_DTLS(s)) {
- /* We have moved to the next flight lets clear out old messages */
- if (direction == OSSL_RECORD_DIRECTION_READ)
- dtls1_clear_received_buffer(s);
- else
- dtls1_clear_sent_buffer(s);
-
dtls1_increment_epoch(s, which);
if (level == OSSL_RECORD_PROTECTION_LEVEL_HANDSHAKE
*/
dtls1_increment_epoch(s, which);
}
+
+ /* We have moved to the next flight lets clear out old messages */
+ if (direction == OSSL_RECORD_DIRECTION_READ) {
+ dtls1_clear_received_buffer(s);
+ dtls1_acknowledge_sent_buffer(s, dtls1_get_epoch(s, which));
+ }
+
+ dtls1_clear_sent_buffer(s, 1);
}
if (!ssl_set_new_record_layer(s, s->version, direction, level, secret,
--- /dev/null
+#! /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;
+use Cwd qw(abs_path);
+
+my $test_name = "test_dtls13ack";
+setup($test_name);
+
+# TODO(DTLSv1.3): The test currently does not work as changes to ´engines/e_ossltest.c´
+# in #25119 should be ported to the ossltest provider.
+plan skip_all => "This doesn't work properly currently";
+plan skip_all => "TLSProxy isn't usable on $^O"
+ if $^O =~ /^(VMS)$/ || $^O =~ /^(MSWin32)$/;
+
+plan skip_all => "$test_name needs the module feature enabled"
+ if disabled("module");
+
+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");
+
+$ENV{OPENSSL_MODULES} = abs_path(bldtop_dir("test"));
+
+my $proxy = TLSProxy::Proxy->new_dtls(
+ undef,
+ cmdstr(app(["openssl"]), display => 1),
+ srctop_file("apps", "server.pem"),
+ (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE})
+);
+
+my $testcount = 3;
+
+plan tests => $testcount;
+
+#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);
+skip "TLSProxy could not start", $testcount if !$proxy->start();
+
+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 $missing_count = @missing;
+
+ok($missing_count == 0 && $expected_count == 1,
+ "Check that all record numbers are acked");
+
+# Test 2: Check that records that are missing are not acked during a handshake
+$proxy->clear();
+my $found_first_client_finish_msg = 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_client_finish_filter);
+TLSProxy::Message->successondata(1);
+$proxy->start();
+
+@expected = get_expected_ack_record_numbers();
+@actual = get_actual_acked_record_numbers();
+@missing = record_numbers_missing(\@expected, \@actual);
+$expected_count = @expected;
+$missing_count = @missing;
+
+ok($missing_count == 1 && $expected_count == 2,
+ "Check that all record numbers except one are acked");
+
+SKIP: {
+ skip "TODO(DTLSv1.3): This test fails because the client does not properly
+ handle when the last flight is dropped when it includes a
+ CompressedCertificate.", 1
+ if !disabled("zlib") || !disabled("zstd") || !disabled("brotli");
+ # Test 3: Check that client cert and verify messages are also acked
+ $proxy->clear();
+ $proxy->filter(undef);
+ $found_first_client_finish_msg = 0;
+ $proxy->serverflags("-min_protocol DTLSv1.3 -max_protocol DTLSv1.3 -Verify 1");
+ $proxy->clientflags("-mtu 2000 -min_protocol DTLSv1.3 -max_protocol DTLSv1.3"
+ ." -cert ".srctop_file("apps", "server.pem"));
+ TLSProxy::Message->successondata(1);
+ $proxy->start();
+
+ @expected = get_expected_ack_record_numbers();
+ @actual = get_actual_acked_record_numbers();
+ @missing = record_numbers_missing(\@expected, \@actual);
+ $expected_count = @expected;
+ $missing_count = @missing;
+
+ ok($missing_count == 0 && $expected_count == 3,
+ "Check that all record numbers are acked");
+}
+
+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
+ && $record->{sent}) {
+ my $epoch = $record->epoch;
+ my $seqnum = $record->seq;
+ my $serverissender = $record->serverissender;
+ my $recnum = TLSProxy::RecordNumber->new($epoch, $seqnum);
+
+ my @messages = TLSProxy::Message->get_messages($record);
+
+ my $record_should_be_acked = 0;
+
+ foreach (@messages) {
+ my $message = $_;
+ if (!$serverissender
+ && ($message->mt == TLSProxy::Message::MT_FINISHED
+ || $message->mt == TLSProxy::Message::MT_CERTIFICATE
+ || $message->mt == TLSProxy::Message::MT_COMPRESSED_CERTIFICATE
+ || $message->mt == TLSProxy::Message::MT_CERTIFICATE_VERIFY)
+ # TODO(DTLSv1.3): The ACK of the following messages are never processed
+ # by the proxy because s_client is closed too early send it:
+ # || $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 $recnum_count = unpack('n', $record->decrypt_data) / 16;
+ my $ptr = 2;
+
+ for (my $idx = 0; $idx < $recnum_count; $idx++) {
+ my $epoch_lo;
+ my $epoch_hi;
+ my $msgseq_lo;
+ my $msgseq_hi;
+
+ ($epoch_hi, $epoch_lo, $msgseq_hi, $msgseq_lo)
+ = 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_client_finish_filter
+{
+ my $inproxy = shift;
+
+ foreach my $record (@{$inproxy->record_list}) {
+ next if ($record->{sent} == 1 || $record->serverissender || $found_first_client_finish_msg == 1);
+
+ my @messages = TLSProxy::Message->get_messages($record);
+ foreach my $message (@messages) {
+ if ($message->mt == TLSProxy::Message::MT_FINISHED) {
+ $record->{sent} = 1;
+ $found_first_client_finish_msg = 1;
+ last;
+ }
+ }
+ }
+}
}
my $record = TLSProxy::Record->new(
+ $last_message->server,
$proxy->flight,
TLSProxy::Record::RT_APPLICATION_DATA,
TLSProxy::Record::VERS_TLS_1_2,
my $record;
if ($isdtls == 1) {
$record = TLSProxy::Record->new_dtls(
+ 0,
0,
$content_type,
TLSProxy::Record::VERS_DTLS_1_2,
);
} else {
$record = TLSProxy::Record->new(
+ 0,
0,
$content_type,
TLSProxy::Record::VERS_TLS_1_2,
# 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,
# 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,
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,
my $chlen = length $clienthello;
$record = TLSProxy::Record->new(
+ 0,
0,
TLSProxy::Record::RT_HANDSHAKE,
TLSProxy::Record::VERS_TLS_1_2,
my $fraglen = length $frag1;
$record = TLSProxy::Record->new(
+ 0,
0,
TLSProxy::Record::RT_HANDSHAKE,
TLSProxy::Record::VERS_TLS_1_2,
$recvers = 0;
}
$record = TLSProxy::Record->new(
+ 0,
0,
TLSProxy::Record::RT_HANDSHAKE,
TLSProxy::Record::VERS_TLS_1_2,
$fraglen = length $frag3;
$record = TLSProxy::Record->new(
+ 0,
0,
TLSProxy::Record::RT_HANDSHAKE,
TLSProxy::Record::VERS_TLS_1_2,
{
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;
if ($isdtls) {
$record = TLSProxy::Record->new_dtls(
+ $isserver,
1,
TLSProxy::Record::RT_UNKNOWN,
@{$records}[-1]->version(),
);
} else {
$record = TLSProxy::Record->new(
+ $isserver,
1,
TLSProxy::Record::RT_UNKNOWN,
@{$records}[-1]->version(),
#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,
} 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,
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,
#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,
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(),
$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(),
unsigned char cbuf[16000] = {0};
unsigned char sbuf[16000];
size_t err = 0;
- char crec_wseq_before[SEQ_NUM_SIZE];
- char crec_wseq_after[SEQ_NUM_SIZE];
- char crec_rseq_before[SEQ_NUM_SIZE];
- char crec_rseq_after[SEQ_NUM_SIZE];
- char srec_wseq_before[SEQ_NUM_SIZE];
- char srec_wseq_after[SEQ_NUM_SIZE];
- char srec_rseq_before[SEQ_NUM_SIZE];
- char srec_rseq_after[SEQ_NUM_SIZE];
+ uint64_t crec_wseq_before, crec_wseq_after;
+ uint64_t crec_rseq_before, crec_rseq_after;
+ uint64_t srec_wseq_before, srec_wseq_after;
+ uint64_t srec_rseq_before, srec_rseq_after;
SSL_CONNECTION *clientsc, *serversc;
if (!TEST_ptr(clientsc = SSL_CONNECTION_FROM_SSL_ONLY(clientssl))
goto end;
cbuf[0] = count++;
- memcpy(crec_wseq_before, &clientsc->rlayer.wrl->sequence, SEQ_NUM_SIZE);
- memcpy(srec_wseq_before, &serversc->rlayer.wrl->sequence, SEQ_NUM_SIZE);
- memcpy(crec_rseq_before, &clientsc->rlayer.rrl->sequence, SEQ_NUM_SIZE);
- memcpy(srec_rseq_before, &serversc->rlayer.rrl->sequence, SEQ_NUM_SIZE);
+ crec_wseq_before = clientsc->rlayer.wrl->sequence;
+ srec_wseq_before = serversc->rlayer.wrl->sequence;
+ crec_rseq_before = clientsc->rlayer.rrl->sequence;
+ srec_rseq_before = serversc->rlayer.rrl->sequence;
if (!TEST_true(SSL_write(clientssl, cbuf, sizeof(cbuf)) == sizeof(cbuf)))
goto end;
}
}
- memcpy(crec_wseq_after, &clientsc->rlayer.wrl->sequence, SEQ_NUM_SIZE);
- memcpy(srec_wseq_after, &serversc->rlayer.wrl->sequence, SEQ_NUM_SIZE);
- memcpy(crec_rseq_after, &clientsc->rlayer.rrl->sequence, SEQ_NUM_SIZE);
- memcpy(srec_rseq_after, &serversc->rlayer.rrl->sequence, SEQ_NUM_SIZE);
+ crec_wseq_after = clientsc->rlayer.wrl->sequence;
+ srec_wseq_after = serversc->rlayer.wrl->sequence;
+ crec_rseq_after = clientsc->rlayer.rrl->sequence;
+ srec_rseq_after = serversc->rlayer.rrl->sequence;
/* verify the payload */
if (!TEST_mem_eq(cbuf, sizeof(cbuf), sbuf, sizeof(sbuf)))
* OpenSSL sequences
*/
if (!BIO_get_ktls_send(clientsc->wbio)) {
- if (!TEST_mem_ne(crec_wseq_before, SEQ_NUM_SIZE,
- crec_wseq_after, SEQ_NUM_SIZE))
+ if (!TEST_uint64_t_ne(crec_wseq_before, crec_wseq_after))
goto end;
} else {
- if (!TEST_mem_eq(crec_wseq_before, SEQ_NUM_SIZE,
- crec_wseq_after, SEQ_NUM_SIZE))
+ if (!TEST_uint64_t_eq(crec_wseq_before, crec_wseq_after))
goto end;
}
if (!BIO_get_ktls_send(serversc->wbio)) {
- if (!TEST_mem_ne(srec_wseq_before, SEQ_NUM_SIZE,
- srec_wseq_after, SEQ_NUM_SIZE))
+ if (!TEST_uint64_t_ne(srec_wseq_before, srec_wseq_after))
goto end;
} else {
- if (!TEST_mem_eq(srec_wseq_before, SEQ_NUM_SIZE,
- srec_wseq_after, SEQ_NUM_SIZE))
+ if (!TEST_uint64_t_eq(srec_wseq_before, srec_wseq_after))
goto end;
}
if (!BIO_get_ktls_recv(clientsc->wbio)) {
- if (!TEST_mem_ne(crec_rseq_before, SEQ_NUM_SIZE,
- crec_rseq_after, SEQ_NUM_SIZE))
+ if (!TEST_uint64_t_ne(crec_rseq_before, crec_rseq_after))
goto end;
} else {
- if (!TEST_mem_eq(crec_rseq_before, SEQ_NUM_SIZE,
- crec_rseq_after, SEQ_NUM_SIZE))
+ if (!TEST_uint64_t_eq(crec_rseq_before, crec_rseq_after))
goto end;
}
if (!BIO_get_ktls_recv(serversc->wbio)) {
- if (!TEST_mem_ne(srec_rseq_before, SEQ_NUM_SIZE,
- srec_rseq_after, SEQ_NUM_SIZE))
+ if (!TEST_uint64_t_ne(srec_rseq_before, srec_rseq_after))
goto end;
} else {
- if (!TEST_mem_eq(srec_rseq_before, SEQ_NUM_SIZE,
- srec_rseq_after, SEQ_NUM_SIZE))
+ if (!TEST_uint64_t_eq(srec_rseq_before, srec_rseq_after))
goto end;
}
static int load_record(TLS_RL_RECORD *rec, RECORD_DATA *recd,
unsigned char **key, unsigned char *iv, size_t ivlen,
- unsigned char *seq)
+ uint64_t *seq)
{
- unsigned char *pt = NULL, *sq = NULL, *ivtmp = NULL;
+ unsigned char *pt = NULL, *sq = NULL, *p_sq, *ivtmp = NULL;
size_t ptlen;
*key = OPENSSL_hexstr2buf(recd->key, NULL);
rec->length = ptlen;
memcpy(rec->data, pt, ptlen);
OPENSSL_free(pt);
- memcpy(seq, sq, SEQ_NUM_SIZE);
+ p_sq = sq;
+ n2l8(p_sq, *seq);
OPENSSL_free(sq);
memcpy(iv, ivtmp, ivlen);
OPENSSL_free(ivtmp);
const EVP_CIPHER *ciph = EVP_aes_128_gcm();
int ret = 0;
size_t ivlen, ctr;
- unsigned char seqbuf[SEQ_NUM_SIZE];
+ uint64_t recseq;
unsigned char iv[EVP_MAX_IV_LENGTH];
OSSL_RECORD_LAYER *rrl = NULL, *wrl = NULL;
for (ctr = 0; ctr < OSSL_NELEM(refdata); ctr++) {
/* Load the record */
ivlen = EVP_CIPHER_get_iv_length(ciph);
- if (!load_record(&rec, &refdata[ctr], &key, iv, ivlen, seqbuf)) {
+ if (!load_record(&rec, &refdata[ctr], &key, iv, ivlen, &recseq)) {
TEST_error("Failed loading key into EVP_CIPHER_CTX");
goto err;
}
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&wrl)))
goto err;
- memcpy(wrl->sequence, seqbuf, sizeof(seqbuf));
+
+ wrl->sequence = recseq;
/* Encrypt it */
if (!TEST_size_t_eq(wrl->funcs->cipher(wrl, &rec, 1, 1, NULL, 0), 1)) {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&rrl)))
goto err;
- memcpy(rrl->sequence, seqbuf, sizeof(seqbuf));
+
+ rrl->sequence = recseq;
/* Decrypt it */
if (!TEST_int_eq(rrl->funcs->cipher(rrl, &rec, 1, 0, NULL, 0), 1)) {
{
}
-void dtls1_clear_sent_buffer(SSL_CONNECTION *s)
+void dtls1_clear_sent_buffer(SSL_CONNECTION *s, int keep_unacked_msgs)
+{
+}
+
+void dtls1_acknowledge_sent_buffer(SSL_CONNECTION *s, uint16_t before_epoch)
{
}
package TLSProxy::Message;
+use TLSProxy::RecordNumber;
use TLSProxy::Alert;
use constant DTLS_MESSAGE_HEADER_LENGTH => 12;
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,
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
};
sub get_messages
{
my $class = shift;
- my $serverin = shift;
my $record = shift;
- my $isdtls = shift;
+ my $serverin = $record->serverissender;
+ my $isdtls = $record->isdtls;
my @messages = ();
my $message;
my $pid;
my $execcmd = $self->execute
." s_client -provider=p_ossltest -provider=default -propquery ?provider=p_ossltest"
- ." -connect $self->{proxy_addr}:$self->{proxy_port}";
+ ." -state -connect $self->{proxy_addr}:$self->{proxy_port}";
if ($self->{isdtls}) {
$execcmd .= " -dtls -max_protocol DTLSv1.3"
# TLSProxy does not support message fragmentation. So
sub process_packet
{
- my ($self, $server, $packet) = @_;
- my $len_real;
- my $decrypt_len;
- my $data;
- my $recnum;
+ my ($self, $serverissender, $packet) = @_;
- if ($server) {
+ if ($serverissender) {
print "Received server packet\n";
} else {
print "Received client packet\n";
}
- if ($self->{direction} != $server) {
+ if ($self->{direction} != $serverissender) {
$self->{flight} = $self->{flight} + 1;
- $self->{direction} = $server;
+ $self->{direction} = $serverissender;
}
print "Packet length = ".length($packet)."\n";
#Return contains the list of record found in the packet followed by the
#list of messages in those records and any partial message
- my @ret = TLSProxy::Record->get_records($server, $self->flight,
- $self->{partial}[$server].$packet,
+ my @ret = TLSProxy::Record->get_records($serverissender, $self->flight,
+ $self->{partial}[$serverissender].$packet,
$self->{isdtls});
- $self->{partial}[$server] = $ret[2];
+ $self->{partial}[$serverissender] = $ret[2];
push @{$self->{record_list}}, @{$ret[0]};
push @{$self->{message_list}}, @{$ret[1]};
#Reconstruct the packet
$packet = "";
foreach my $record (@{$self->record_list}) {
- $packet .= $record->reconstruct_record($server);
+ $packet .= $record->reconstruct_record($serverissender);
}
print "Forwarded packet length = ".length($packet)."\n\n";
RT_HANDSHAKE => 22,
RT_ALERT => 21,
RT_CCS => 20,
+ RT_ACK => 26,
RT_UNKNOWN => 100,
RT_DTLS_UNIHDR_EPOCH4 => 0x2c,
RT_DTLS_UNIHDR_EPOCH1 => 0x2d,
RT_HANDSHAKE, "HANDSHAKE",
RT_ALERT, "ALERT",
RT_CCS, "CCS",
+ RT_ACK, "ACK",
RT_UNKNOWN, "UNKNOWN",
RT_DTLS_UNIHDR_EPOCH4, "DTLS UNIFIED HEADER (EPOCH 4)",
RT_DTLS_UNIHDR_EPOCH1, "DTLS UNIFIED HEADER (EPOCH 1)",
sub get_records
{
my $class = shift;
- my $server = shift;
+ my $serverissender = shift;
my $flight = shift;
my $packet = shift;
my $isdtls = shift;
my $recnum = 1;
while (length ($packet) > 0) {
- print " Record $recnum ", $server ? "(server -> client)\n"
- : "(client -> server)\n";
+ print " Record $recnum ", $serverissender ? "(server -> client)\n"
+ : "(client -> server)\n";
my $record_hdr_len;
my $content_type;
my $version;
my $epoch;
my $seq;
- if ($isdtls) {
+ if ($isdtls == 1) {
my $isunifiedhdr;
$content_type = unpack('B[8]', $packet);
($content_type, $seq, $len) = unpack('CCn', $packet);
$record_hdr_len = 4;
}
+ # Encrypted DTLS 1.3 records have encrypted sequence numbers.
+ # ossltest engine overrides ecb encryption to be a no-op.
+ # This effectively means that the sequence number encryption mask
+ # is just the 16 first bytes of the record body.
+ my $recordbody = substr($packet, $record_hdr_len, $len);
+ (my $maskhi, my $maskmi, my $masklo) = unpack('nnn', $recordbody);
$version = VERS_DTLS_1_2; # DTLSv1.3 headers has DTLSv1.2 in its legacy_version field
if ($eebits == "00") {
} else {
die("Epoch bits is not 0's or 1's: should not happen")
}
+ $seq ^= $maskhi;
} else {
my $seqhi;
my $seqmi;
print " Content type: ".$record_type{$content_type}."\n";
print " Version: $tls_version{$version}\n";
- if($isdtls) {
+ if($isdtls == 1) {
print " Epoch: $epoch\n";
print " Sequence: $seq\n";
}
my $record;
if ($isdtls) {
$record = TLSProxy::Record->new_dtls(
+ $serverissender,
$flight,
$content_type,
$version,
);
} else {
$record = TLSProxy::Record->new(
+ $serverissender,
$flight,
$content_type,
$version,
if ($content_type != RT_CCS
&& (!TLSProxy::Proxy->is_tls13()
|| $content_type != RT_ALERT)) {
- if (($server && $server_encrypting)
- || (!$server && $client_encrypting)) {
+ if (($serverissender && $server_encrypting)
+ || (!$serverissender && $client_encrypting)) {
if (!TLSProxy::Proxy->is_tls13() && $etm) {
$record->decryptETM();
} else {
if (TLSProxy::Proxy->is_tls13()) {
print " Inner content type: "
.$record_type{$record->content_type()}."\n";
+ print " Data: ".unpack("n",$record->decrypt_data)."\n";
}
}
}
push @record_list, $record;
#Now figure out what messages are contained within this record
- my @messages = TLSProxy::Message->get_messages($server, $record, $isdtls);
+ my @messages = TLSProxy::Message->get_messages($record);
push @message_list, @messages;
$packet = substr($packet, $record_hdr_len + $len);
sub new_dtls
{
my $class = shift;
- my ($flight,
+ my ($serverissender,
+ $flight,
$content_type,
$version,
$epoch,
$decrypt_len,
$data,
$decrypt_data) = @_;
- return $class->init(1,
+ return $class->init($serverissender,
+ 1,
$flight,
$content_type,
$version,
sub new
{
my $class = shift;
- my ($flight,
+ my ($serverissender,
+ $flight,
$content_type,
$version,
$len,
$data,
$decrypt_data) = @_;
return $class->init(
+ $serverissender,
0,
$flight,
$content_type,
sub init
{
my $class = shift;
- my ($isdtls,
+ my ($serverissender,
+ $isdtls,
$flight,
$content_type,
$version,
$decrypt_data) = @_;
my $self = {
+ serverissender => $serverissender,
isdtls => $isdtls,
flight => $flight,
content_type => $content_type,
my $content_type = (TLSProxy::Proxy->is_tls13() && $self->encrypted)
? $self->outer_content_type : $self->content_type;
if($self->{isdtls}) {
+ my $seqhi = ($self->seq >> 32) & 0xffff;
+ my $seqmi = ($self->seq >> 16) & 0xffff;
+ my $seqlo = ($self->seq >> 0) & 0xffff;
+
if (TLSProxy::Proxy->is_tls13() && $self->encrypted) {
+ # Mask sequence number with record body bytes. Explanation
+ # given in get_records.
+ (my $maskhi, my $maskmi, my $masklo) = unpack("nnn", $self->data);
+ $seqlo ^= $maskhi;
# Prepare a unified header
- $data = pack('Cnn', $content_type, $self->seq, $self->len);
+ $data = pack('Cnn', $content_type, $seqlo, $self->len);
} else {
- my $seqhi = ($self->seq >> 32) & 0xffff;
- my $seqmi = ($self->seq >> 16) & 0xffff;
- my $seqlo = ($self->seq >> 0) & 0xffff;
$data = pack('Cnnnnnn', $content_type, $self->version,
$self->epoch, $seqhi, $seqmi, $seqlo, $self->len);
}
}
#Read only accessors
+sub serverissender
+{
+ my $self = shift;
+ return $self->{serverissender};
+}
+sub isdtls
+{
+ my $self = shift;
+ return $self->{isdtls};
+}
sub flight
{
my $self = shift;
--- /dev/null
+# 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;