diff options
Diffstat (limited to 'security/nss/lib/ssl/dtls13con.c')
-rw-r--r-- | security/nss/lib/ssl/dtls13con.c | 457 |
1 files changed, 0 insertions, 457 deletions
diff --git a/security/nss/lib/ssl/dtls13con.c b/security/nss/lib/ssl/dtls13con.c deleted file mode 100644 index aba0f62ab8..0000000000 --- a/security/nss/lib/ssl/dtls13con.c +++ /dev/null @@ -1,457 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* - * DTLS 1.3 Protocol - */ - -#include "ssl.h" -#include "sslimpl.h" -#include "sslproto.h" - -/* DTLS 1.3 Record map for ACK processing. - * This represents a single fragment, so a record which includes - * multiple fragments will have one entry for each fragment on the - * sender. We use the same structure on the receiver for convenience - * but the only value we actually use is |record|. - */ -typedef struct DTLSHandshakeRecordEntryStr { - PRCList link; - PRUint16 messageSeq; /* The handshake message sequence number. */ - PRUint32 offset; /* The offset into the handshake message. */ - PRUint32 length; /* The length of the fragment. */ - sslSequenceNumber record; /* The record (includes epoch). */ - PRBool acked; /* Has this packet been acked. */ -} DTLSHandshakeRecordEntry; - -/* Combine the epoch and sequence number into a single value. */ -static inline sslSequenceNumber -dtls_CombineSequenceNumber(DTLSEpoch epoch, sslSequenceNumber seqNum) -{ - PORT_Assert(seqNum <= RECORD_SEQ_MAX); - return ((sslSequenceNumber)epoch << 48) | seqNum; -} - -SECStatus -dtls13_RememberFragment(sslSocket *ss, - PRCList *list, - PRUint32 sequence, - PRUint32 offset, - PRUint32 length, - DTLSEpoch epoch, - sslSequenceNumber record) -{ - DTLSHandshakeRecordEntry *entry; - - PORT_Assert(IS_DTLS(ss)); - /* We should never send an empty fragment with offset > 0. */ - PORT_Assert(length || !offset); - - if (!tls13_MaybeTls13(ss)) { - return SECSuccess; - } - - SSL_TRC(20, ("%d: SSL3[%d]: %s remembering %s record=%llx msg=%d offset=%d", - SSL_GETPID(), ss->fd, - SSL_ROLE(ss), - list == &ss->ssl3.hs.dtlsSentHandshake ? "sent" : "received", - dtls_CombineSequenceNumber(epoch, record), sequence, offset)); - - entry = PORT_ZAlloc(sizeof(DTLSHandshakeRecordEntry)); - if (!entry) { - return SECFailure; - } - - entry->messageSeq = sequence; - entry->offset = offset; - entry->length = length; - entry->record = dtls_CombineSequenceNumber(epoch, record); - entry->acked = PR_FALSE; - - PR_APPEND_LINK(&entry->link, list); - - return SECSuccess; -} - -SECStatus -dtls13_SendAck(sslSocket *ss) -{ - sslBuffer buf = SSL_BUFFER_EMPTY; - SECStatus rv = SECSuccess; - PRCList *cursor; - PRInt32 sent; - - SSL_TRC(10, ("%d: SSL3[%d]: Sending ACK", - SSL_GETPID(), ss->fd)); - - for (cursor = PR_LIST_HEAD(&ss->ssl3.hs.dtlsRcvdHandshake); - cursor != &ss->ssl3.hs.dtlsRcvdHandshake; - cursor = PR_NEXT_LINK(cursor)) { - DTLSHandshakeRecordEntry *entry = (DTLSHandshakeRecordEntry *)cursor; - - SSL_TRC(10, ("%d: SSL3[%d]: ACK for record=%llx", - SSL_GETPID(), ss->fd, entry->record)); - rv = sslBuffer_AppendNumber(&buf, entry->record, 8); - if (rv != SECSuccess) { - goto loser; - } - } - - ssl_GetXmitBufLock(ss); - sent = ssl3_SendRecord(ss, NULL, content_ack, - buf.buf, buf.len, 0); - ssl_ReleaseXmitBufLock(ss); - if (sent != buf.len) { - rv = SECFailure; - if (sent != -1) { - PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); - } - } - -loser: - sslBuffer_Clear(&buf); - return rv; -} - -void -dtls13_SendAckCb(sslSocket *ss) -{ - if (!IS_DTLS(ss)) { - return; - } - (void)dtls13_SendAck(ss); -} - -/* Zero length messages are very simple to check. */ -static PRBool -dtls_IsEmptyMessageAcknowledged(sslSocket *ss, PRUint16 msgSeq, PRUint32 offset) -{ - PRCList *cursor; - - for (cursor = PR_LIST_HEAD(&ss->ssl3.hs.dtlsSentHandshake); - cursor != &ss->ssl3.hs.dtlsSentHandshake; - cursor = PR_NEXT_LINK(cursor)) { - DTLSHandshakeRecordEntry *entry = (DTLSHandshakeRecordEntry *)cursor; - if (!entry->acked || msgSeq != entry->messageSeq) { - continue; - } - /* Empty fragments are always offset 0. */ - if (entry->length == 0) { - PORT_Assert(!entry->offset); - return PR_TRUE; - } - } - return PR_FALSE; -} - -/* Take a range starting at |*start| and that start forwards based on the - * contents of the acknowedgement in |entry|. Only move if the acknowledged - * range overlaps |*start|. Return PR_TRUE if it moves. */ -static PRBool -dtls_MoveUnackedStartForward(DTLSHandshakeRecordEntry *entry, PRUint32 *start) -{ - /* This entry starts too late. */ - if (*start < entry->offset) { - return PR_FALSE; - } - /* This entry ends too early. */ - if (*start >= entry->offset + entry->length) { - return PR_FALSE; - } - *start = entry->offset + entry->length; - return PR_TRUE; -} - -/* Take a range ending at |*end| and move that end backwards based on the - * contents of the acknowedgement in |entry|. Only move if the acknowledged - * range overlaps |*end|. Return PR_TRUE if it moves. */ -static PRBool -dtls_MoveUnackedEndBackward(DTLSHandshakeRecordEntry *entry, PRUint32 *end) -{ - /* This entry ends too early. */ - if (*end > entry->offset + entry->length) { - return PR_FALSE; - } - /* This entry starts too late. */ - if (*end <= entry->offset) { - return PR_FALSE; - } - *end = entry->offset; - return PR_TRUE; -} - -/* Get the next contiguous range of unacknowledged bytes from the handshake - * message identified by |msgSeq|. The search starts at the offset in |offset|. - * |len| contains the full length of the message. - * - * Returns PR_TRUE if there is an unacknowledged range. In this case, values at - * |start| and |end| are modified to contain the range. - * - * Returns PR_FALSE if the message is entirely acknowledged from |offset| - * onwards. - */ -PRBool -dtls_NextUnackedRange(sslSocket *ss, PRUint16 msgSeq, PRUint32 offset, - PRUint32 len, PRUint32 *startOut, PRUint32 *endOut) -{ - PRCList *cur_p; - PRBool done = PR_FALSE; - DTLSHandshakeRecordEntry *entry; - PRUint32 start; - PRUint32 end; - - PORT_Assert(IS_DTLS(ss)); - - *startOut = offset; - *endOut = len; - if (!tls13_MaybeTls13(ss)) { - return PR_TRUE; - } - - /* The message is empty. Use a simple search. */ - if (!len) { - PORT_Assert(!offset); - return !dtls_IsEmptyMessageAcknowledged(ss, msgSeq, offset); - } - - /* This iterates multiple times over the acknowledgments and only terminates - * when an entire iteration happens without start or end moving. If that - * happens without start and end crossing each other, then there is a range - * of unacknowledged data. If they meet, then the message is fully - * acknowledged. */ - start = offset; - end = len; - while (!done) { - done = PR_TRUE; - for (cur_p = PR_LIST_HEAD(&ss->ssl3.hs.dtlsSentHandshake); - cur_p != &ss->ssl3.hs.dtlsSentHandshake; - cur_p = PR_NEXT_LINK(cur_p)) { - entry = (DTLSHandshakeRecordEntry *)cur_p; - if (!entry->acked || msgSeq != entry->messageSeq) { - continue; - } - - if (dtls_MoveUnackedStartForward(entry, &start) || - dtls_MoveUnackedEndBackward(entry, &end)) { - if (start >= end) { - /* The message is all acknowledged. */ - return PR_FALSE; - } - /* Start over again and keep going until we don't move either - * start or end. */ - done = PR_FALSE; - break; - } - } - } - PORT_Assert(start < end); - - *startOut = start; - *endOut = end; - return PR_TRUE; -} - -SECStatus -dtls13_SetupAcks(sslSocket *ss) -{ - if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { - return SECSuccess; - } - - if (ss->ssl3.hs.endOfFlight) { - dtls_CancelTimer(ss, ss->ssl3.hs.ackTimer); - - if (ss->ssl3.hs.ws == idle_handshake && ss->sec.isServer) { - SSL_TRC(10, ("%d: SSL3[%d]: dtls_HandleHandshake, sending ACK", - SSL_GETPID(), ss->fd)); - return dtls13_SendAck(ss); - } - return SECSuccess; - } - - /* We need to send an ACK. */ - if (!ss->ssl3.hs.ackTimer->cb) { - /* We're not armed, so arm. */ - SSL_TRC(10, ("%d: SSL3[%d]: dtls_HandleHandshake, arming ack timer", - SSL_GETPID(), ss->fd)); - return dtls_StartTimer(ss, ss->ssl3.hs.ackTimer, - DTLS_RETRANSMIT_INITIAL_MS / 4, - dtls13_SendAckCb); - } - /* The ack timer is already armed, so just return. */ - return SECSuccess; -} - -/* - * Special case processing for out-of-epoch records. - * This can only handle ACKs for now and everything else generates - * an error. In future, may also handle KeyUpdate. - * - * The error checking here is as follows: - * - * - If it's not encrypted, out of epoch stuff is just discarded. - * - If it's encrypted, out of epoch stuff causes an error. - */ -SECStatus -dtls13_HandleOutOfEpochRecord(sslSocket *ss, const ssl3CipherSpec *spec, - SSL3ContentType rType, - sslBuffer *databuf) -{ - SECStatus rv; - sslBuffer buf = *databuf; - - databuf->len = 0; /* Discard data whatever happens. */ - PORT_Assert(IS_DTLS(ss)); - PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3); - /* Can't happen, but double check. */ - if (!IS_DTLS(ss) || (ss->version < SSL_LIBRARY_VERSION_TLS_1_3)) { - tls13_FatalError(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); - return SECFailure; - } - SSL_TRC(10, ("%d: DTLS13[%d]: handle out of epoch record: type=%d", SSL_GETPID(), - ss->fd, rType)); - - if (rType == content_ack) { - ssl_GetSSL3HandshakeLock(ss); - rv = dtls13_HandleAck(ss, &buf); - ssl_ReleaseSSL3HandshakeLock(ss); - PORT_Assert(databuf->len == 0); - return rv; - } - - switch (spec->epoch) { - case TrafficKeyClearText: - /* Drop. */ - return SECSuccess; - - case TrafficKeyHandshake: - /* Drop out of order handshake messages, but if we are the - * server, we might have processed the client's Finished and - * moved on to application data keys, but the client has - * retransmitted Finished (e.g., because our ACK got lost.) - * We just retransmit the previous Finished to let the client - * complete. */ - if (rType == content_handshake) { - if ((ss->sec.isServer) && - (ss->ssl3.hs.ws == idle_handshake)) { - PORT_Assert(dtls_TimerActive(ss, ss->ssl3.hs.hdTimer)); - return dtls13_SendAck(ss); - } - return SECSuccess; - } - - /* This isn't a handshake record, so shouldn't be encrypted - * under the handshake key. */ - break; - - default: - /* Any other epoch is forbidden. */ - break; - } - - SSL_TRC(10, ("%d: SSL3[%d]: unexpected out of epoch record type %d", SSL_GETPID(), - ss->fd, rType)); - - (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter); - PORT_SetError(SSL_ERROR_RX_UNKNOWN_RECORD_TYPE); - return SECFailure; -} - -SECStatus -dtls13_HandleAck(sslSocket *ss, sslBuffer *databuf) -{ - PRUint8 *b = databuf->buf; - PRUint32 l = databuf->len; - SECStatus rv; - - /* Ensure we don't loop. */ - databuf->len = 0; - - PORT_Assert(IS_DTLS(ss)); - if (!tls13_MaybeTls13(ss)) { - tls13_FatalError(ss, SSL_ERROR_RX_UNKNOWN_RECORD_TYPE, illegal_parameter); - return SECSuccess; - } - - SSL_TRC(10, ("%d: SSL3[%d]: Handling ACK", SSL_GETPID(), ss->fd)); - while (l > 0) { - PRUint64 seq; - PRCList *cursor; - - rv = ssl3_ConsumeHandshakeNumber64(ss, &seq, 8, &b, &l); - if (rv != SECSuccess) { - return SECFailure; - } - - for (cursor = PR_LIST_HEAD(&ss->ssl3.hs.dtlsSentHandshake); - cursor != &ss->ssl3.hs.dtlsSentHandshake; - cursor = PR_NEXT_LINK(cursor)) { - DTLSHandshakeRecordEntry *entry = (DTLSHandshakeRecordEntry *)cursor; - - if (entry->record == seq) { - SSL_TRC(10, ( - "%d: SSL3[%d]: Marking record=%llx message %d offset %d length=%d as ACKed", - SSL_GETPID(), ss->fd, - seq, entry->messageSeq, entry->offset, entry->length)); - entry->acked = PR_TRUE; - } - } - } - - /* Try to flush. */ - rv = dtls_TransmitMessageFlight(ss); - if (rv != SECSuccess) { - return SECFailure; - } - - /* Reset the retransmit timer. */ - if (ss->ssl3.hs.rtTimer->cb) { - (void)dtls_RestartTimer(ss, ss->ssl3.hs.rtTimer); - } - - /* If there are no more messages to send, cleanup. */ - if (PR_CLIST_IS_EMPTY(&ss->ssl3.hs.lastMessageFlight)) { - SSL_TRC(10, ("%d: SSL3[%d]: No more unacked handshake messages", - SSL_GETPID(), ss->fd)); - - dtls_CancelTimer(ss, ss->ssl3.hs.rtTimer); - ssl_ClearPRCList(&ss->ssl3.hs.dtlsSentHandshake, NULL); - /* If the handshake is finished, and we're the client then - * also clean up the handshake read cipher spec. Any ACKs - * we receive will be with the application data cipher spec. - * The server needs to keep the handshake cipher spec around - * for the holddown period to process retransmitted Finisheds. - */ - if (!ss->sec.isServer && (ss->ssl3.hs.ws == idle_handshake)) { - ssl_CipherSpecReleaseByEpoch(ss, CipherSpecRead, - TrafficKeyHandshake); - } - } - return SECSuccess; -} - -/* Clean up the read timer for the handshake cipher suites on the - * server. - * - * In DTLS 1.3, the client speaks last (Finished), and will retransmit - * until the server ACKs that message (using application data cipher - * suites). I.e., - * - * - The client uses the retransmit timer and retransmits using the - * saved write handshake cipher suite. - * - The server keeps the saved read handshake cipher suite around - * for the holddown period in case it needs to read the Finished. - * - * After the holddown period, the server assumes the client is happy - * and discards the handshake read cipher suite. - */ -void -dtls13_HolddownTimerCb(sslSocket *ss) -{ - SSL_TRC(10, ("%d: SSL3[%d]: holddown timer fired", - SSL_GETPID(), ss->fd)); - ssl_CipherSpecReleaseByEpoch(ss, CipherSpecRead, TrafficKeyHandshake); - ssl_ClearPRCList(&ss->ssl3.hs.dtlsRcvdHandshake, NULL); -} |