/* -*- 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" #include "keyhi.h" #include "pk11func.h" /* * 0 1 2 3 4 5 6 7 * +-+-+-+-+-+-+-+-+ * |0|0|1|C|S|L|E E| * +-+-+-+-+-+-+-+-+ * | Connection ID | Legend: * | (if any, | * / length as / C - CID present * | negotiated) | S - Sequence number length * +-+-+-+-+-+-+-+-+ L - Length present * | 8 or 16 bit | E - Epoch * |Sequence Number| * +-+-+-+-+-+-+-+-+ * | 16 bit Length | * | (if present) | * +-+-+-+-+-+-+-+-+ */ SECStatus dtls13_InsertCipherTextHeader(const sslSocket *ss, const ssl3CipherSpec *cwSpec, sslBuffer *wrBuf, PRBool *needsLength) { /* Avoid using short records for the handshake. We pack multiple records * into the one datagram for the handshake. */ if (ss->opt.enableDtlsShortHeader && cwSpec->epoch > TrafficKeyHandshake) { *needsLength = PR_FALSE; /* The short header is comprised of two octets in the form * 0b001000eessssssss where 'e' is the low two bits of the * epoch and 's' is the low 8 bits of the sequence number. */ PRUint8 ct = 0x20 | ((uint64_t)cwSpec->epoch & 0x3); if (sslBuffer_AppendNumber(wrBuf, ct, 1) != SECSuccess) { return SECFailure; } PRUint8 seq = cwSpec->nextSeqNum & 0xff; return sslBuffer_AppendNumber(wrBuf, seq, 1); } PRUint8 ct = 0x2c | ((PRUint8)cwSpec->epoch & 0x3); if (sslBuffer_AppendNumber(wrBuf, ct, 1) != SECSuccess) { return SECFailure; } if (sslBuffer_AppendNumber(wrBuf, (cwSpec->nextSeqNum & 0xffff), 2) != SECSuccess) { return SECFailure; } *needsLength = PR_TRUE; return SECSuccess; } /* 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; unsigned int offset; SSL_TRC(10, ("%d: SSL3[%d]: Sending ACK", SSL_GETPID(), ss->fd)); rv = sslBuffer_Skip(&buf, 2, &offset); if (rv != SECSuccess) { goto loser; } 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; } } rv = sslBuffer_InsertLength(&buf, offset, 2); if (rv != SECSuccess) { goto loser; } ssl_GetXmitBufLock(ss); sent = ssl3_SendRecord(ss, NULL, ssl_ct_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); } /* Limits from draft-ietf-tls-dtls13-38; section 4.5.3. */ PRBool dtls13_AeadLimitReached(ssl3CipherSpec *spec) { if (spec->version >= SSL_LIBRARY_VERSION_TLS_1_3) { switch (spec->cipherDef->calg) { case ssl_calg_chacha20: case ssl_calg_aes_gcm: return spec->deprotectionFailures >= (1ULL << 36); #ifdef UNSAFE_FUZZER_MODE case ssl_calg_null: return PR_FALSE; #endif default: PORT_Assert(0); break; } } return PR_FALSE; } /* 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, SSLContentType 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 == ssl_ct_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 ACK to let the client complete. */ if (rType == ssl_ct_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; unsigned int length; 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 SECFailure; } SSL_TRC(10, ("%d: SSL3[%d]: Handling ACK", SSL_GETPID(), ss->fd)); rv = ssl3_ConsumeHandshakeNumber(ss, &length, 2, &b, &l); if (rv != SECSuccess) { goto loser; } if (length != l) { goto loser; } while (l > 0) { PRUint64 seq; PRCList *cursor; rv = ssl3_ConsumeHandshakeNumber64(ss, &seq, 8, &b, &l); if (rv != SECSuccess) { goto loser; } 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, ssl_secret_read, TrafficKeyHandshake); } } return SECSuccess; loser: /* Due to bug 1829391 we may incorrectly send an alert rather than * ignore an invalid record here. */ SSL_TRC(11, ("%d: SSL3[%d]: Error processing DTLS1.3 ACK.", SSL_GETPID(), ss->fd)); PORT_SetError(SSL_ERROR_RX_MALFORMED_DTLS_ACK); return SECFailure; } /* 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, ssl_secret_read, TrafficKeyHandshake); ssl_ClearPRCList(&ss->ssl3.hs.dtlsRcvdHandshake, NULL); } SECStatus dtls13_MaskSequenceNumber(sslSocket *ss, ssl3CipherSpec *spec, PRUint8 *hdr, PRUint8 *cipherText, PRUint32 cipherTextLen) { PORT_Assert(IS_DTLS(ss)); if (spec->version < SSL_LIBRARY_VERSION_TLS_1_3) { return SECSuccess; } if (spec->maskContext) { #ifdef UNSAFE_FUZZER_MODE /* Use a null mask. */ PRUint8 mask[2] = { 0 }; #else /* "This procedure requires the ciphertext length be at least 16 bytes. * Receivers MUST reject shorter records as if they had failed * deprotection, as described in Section 4.5.2." */ if (cipherTextLen < 16) { PORT_SetError(SSL_ERROR_BAD_MAC_READ); return SECFailure; } PRUint8 mask[2]; SECStatus rv = ssl_CreateMaskInner(spec->maskContext, cipherText, cipherTextLen, mask, sizeof(mask)); if (rv != SECSuccess) { PORT_SetError(SSL_ERROR_BAD_MAC_READ); return SECFailure; } #endif hdr[1] ^= mask[0]; if (hdr[0] & 0x08) { hdr[2] ^= mask[1]; } } return SECSuccess; } CK_MECHANISM_TYPE tls13_SequenceNumberEncryptionMechanism(SSLCipherAlgorithm bulkAlgorithm) { switch (bulkAlgorithm) { case ssl_calg_aes_gcm: return CKM_AES_ECB; case ssl_calg_chacha20: return CKM_NSS_CHACHA20_CTR; default: PORT_Assert(PR_FALSE); } return CKM_INVALID_MECHANISM; }