diff options
Diffstat (limited to '')
-rw-r--r-- | security/nss/lib/ssl/dtls13con.c | 868 |
1 files changed, 868 insertions, 0 deletions
diff --git a/security/nss/lib/ssl/dtls13con.c b/security/nss/lib/ssl/dtls13con.c new file mode 100644 index 0000000000..29afcfb42e --- /dev/null +++ b/security/nss/lib/ssl/dtls13con.c @@ -0,0 +1,868 @@ +/* -*- 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" + +/*Figure 3: DTLS 1.3 Unified Header */ + +/* + * 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) | + * +-+-+-+-+-+-+-+-+ + */ + +// E: The two low bits (0x03) include the low order two bits of the epoch. +#define MASK_TWO_LOW_BITS 0x3 +// Fixed Bits: The three high bits of the first byte of the unified header are set to 001. +// The C bit is set if the Connection ID is present. +// The S bit (0x08) indicates the size of the sequence number, here 1 stands for 16 bits +// The L bit (0x04) is set if the length is present. +// The EE bits - mask of the epoch + +// 0x2c = 0b001-0-1-1-00 +// 001-C-S-L-EE +#define UNIFIED_HEADER_LONG 0x2c +// 0x20 = 0b001-0-0-1-00 +// 001-C-S-L-EE +// The difference between the long and short header is in the S bit (1 for long, 0 for short). +// The S bit (0x08) indicates the size of the sequence number, here 0 stands for 8 bits +#define UNIFIED_HEADER_SHORT 0x20 + +// The masks to get the 8 (MASK_SEQUENCE_NUMBER_SHORT) or 16 bits (MASK_SEQUENCE_NUMBER_LONG) of the record sequence number. +#define MASK_SEQUENCE_NUMBER_SHORT 0xff +#define MASK_SEQUENCE_NUMBER_LONG 0xffff + +/*The DTLS Record Layer - Figure 3 and further*/ +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. */ + + /* The short header here means that the S bit is set to 0 (8-bit sequence number) */ + 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 = UNIFIED_HEADER_SHORT | ((uint64_t)cwSpec->epoch & MASK_TWO_LOW_BITS); + if (sslBuffer_AppendNumber(wrBuf, ct, sizeof(ct)) != SECSuccess) { + return SECFailure; + } + PRUint8 seq = cwSpec->nextSeqNum & MASK_SEQUENCE_NUMBER_SHORT; + return sslBuffer_AppendNumber(wrBuf, seq, sizeof(seq)); + } + + PRUint8 ct = UNIFIED_HEADER_LONG | ((PRUint8)cwSpec->epoch & MASK_TWO_LOW_BITS); + if (sslBuffer_AppendNumber(wrBuf, ct, sizeof(ct)) != SECSuccess) { + return SECFailure; + } + PRUint16 seq = cwSpec->nextSeqNum & MASK_SEQUENCE_NUMBER_LONG; + if (sslBuffer_AppendNumber(wrBuf, seq, sizeof(seq)) != 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. */ + /* DTLS adds an epoch and sequence number to the TLS record header. */ + sslSequenceNumber record; /* The record (includes epoch). */ + PRBool acked; /* Has this packet been acked. */ +} DTLSHandshakeRecordEntry; + +/*The sequence number is set to be the low order 48 + bits of the 64 bit sequence number.*/ +#define LENGTH_SEQ_NUMBER 48 + +/* 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 << LENGTH_SEQ_NUMBER) | 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; +} + +/* RFC9147; section 7.1 */ +SECStatus +dtls13_SendAck(sslSocket *ss) +{ + sslBuffer buf = SSL_BUFFER_EMPTY; + SECStatus rv = SECSuccess; + PRCList *cursor; + PRInt32 sent; + unsigned int offset = 0; + + SSL_TRC(10, ("%d: SSL3[%d]: Sending ACK", + SSL_GETPID(), ss->fd)); + + /* RecordNumber record_numbers<0..2^16-1>; + 2 length bytes for the list of ACKs*/ + PRUint32 sizeOfListACK = 2; + rv = sslBuffer_Skip(&buf, sizeOfListACK, &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)); + + /*See dtls_CombineSequenceNumber function */ + PRUint64 epoch = entry->record >> 48; + PRUint64 seqNum = entry->record & 0xffffffffffff; + + rv = sslBuffer_AppendNumber(&buf, epoch, 8); + if (rv != SECSuccess) { + goto loser; + } + + rv = sslBuffer_AppendNumber(&buf, seqNum, 8); + if (rv != SECSuccess) { + goto loser; + } + } + + rv = sslBuffer_InsertLength(&buf, offset, sizeOfListACK); + 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 RFC9147; 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 and the message is a piece of an application data, it's discarded. + * - Else 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(30, ("%d: DTLS13[%d]: %s handles out of epoch record: type=%d", SSL_GETPID(), + ss->fd, SSL_ROLE(ss), 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: + if (rType == ssl_ct_application_data) { + return SECSuccess; + } + 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; +} + +/* KeyUpdate in DTLS1.3 is required to be ACKed. +The dtls13_maybeProcessKeyUpdateAck function is called when we receive a message acknowledging KeyUpdate. +The function will then update the writing keys of the party started KeyUpdate. +*/ +SECStatus +dtls13_maybeProcessKeyUpdateAck(sslSocket *ss, PRUint16 entrySeq) +{ + /* RFC 9147. Section 8. + Due to the possibility of an ACK message for a KeyUpdate being lost + and thereby preventing the sender of KeyUpdate from updating its + keying material, receivers MUST retain the pre-update keying material + until receipt and successful decryption of a message using the new + keys.*/ + + if (ss->ssl3.hs.isKeyUpdateInProgress && entrySeq == ss->ssl3.hs.dtlsHandhakeKeyUpdateMessage) { + SSL_TRC(30, ("%d: DTLS13[%d]: %s key update is completed", SSL_GETPID(), ss->fd, SSL_ROLE(ss))); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + SECStatus rv = SECSuccess; + rv = tls13_UpdateTrafficKeys(ss, ssl_secret_write); + if (rv != SECSuccess) { + return SECFailure; + } + PORT_Assert(ss->ssl3.hs.isKeyUpdateInProgress); + ss->ssl3.hs.isKeyUpdateInProgress = PR_FALSE; + + return rv; + } + + else + return SECSuccess; +} + +SECStatus +dtls13_HandleAck(sslSocket *ss, sslBuffer *databuf) +{ + PRUint8 *b = databuf->buf; + PRUint32 l = databuf->len; + unsigned int length; + SECStatus rv; + + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + /* 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; + PRUint64 epoch; + PRCList *cursor; + + rv = ssl3_ConsumeHandshakeNumber64(ss, &epoch, 8, &b, &l); + if (rv != SECSuccess) { + goto loser; + } + + rv = ssl3_ConsumeHandshakeNumber64(ss, &seq, 8, &b, &l); + if (rv != SECSuccess) { + goto loser; + } + + if (epoch > RECORD_EPOCH_MAX) { + SSL_TRC(50, ("%d: SSL3[%d]: ACK message was rejected: the epoch exceeds the limit", SSL_GETPID(), ss->fd)); + continue; + } + if (seq > RECORD_SEQ_MAX) { + SSL_TRC(50, ("%d: SSL3[%d]: ACK message was rejected: the sequence number exceeds the limit", SSL_GETPID(), ss->fd)); + continue; + } + + seq = dtls_CombineSequenceNumber(epoch, seq); + + 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(30, ( + "%d: DTLS13[%d]: Marking record=%llx message %d offset %d length=%d as ACKed", + SSL_GETPID(), ss->fd, + entry->record, entry->messageSeq, entry->offset, entry->length)); + entry->acked = PR_TRUE; + + /* When we sent a KeyUpdate message, we have recorded the identifier of the message. + During the HandleACK we check if we received an ack for the KeyUpdate message we sent.*/ + rv = dtls13_maybeProcessKeyUpdateAck(ss, entry->messageSeq); + if (rv != SECSuccess) { + return SECFailure; + } + } + } + } + /* 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); +} + +/*RFC 9147. 4.2.3. Record Number Encryption*/ +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 + + /* + The encrypted sequence number is computed by XORing the leading bytes + of the mask with the on-the-wire representation of the sequence + number. Decryption is accomplished by the same process. + */ + + PRUint32 maskSBitIsSet = 0x08; + hdr[1] ^= mask[0]; + if (hdr[0] & maskSBitIsSet) { + hdr[2] ^= mask[1]; + } + } + return SECSuccess; +} + +CK_MECHANISM_TYPE +tls13_SequenceNumberEncryptionMechanism(SSLCipherAlgorithm bulkAlgorithm) +{ + /* + When the AEAD is based on AES, then the mask is generated by + computing AES-ECB on the first 16 bytes of the ciphertext: + + When the AEAD is based on ChaCha20, then the mask is generated by + treating the first 4 bytes of the ciphertext as the block counter and + the next 12 bytes as the nonce, passing them to the ChaCha20 block + function. + */ + + 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; +} + +/* The function constucts the KeyUpdate Message. +The structure is presented in RFC 9147 Section 5.2. */ + +SECStatus +dtls13_EnqueueKeyUpdateMessage(sslSocket *ss, tls13KeyUpdateRequest request) +{ + SECStatus rv = SECFailure; + /* + The epoch number is initially zero and is incremented each time + keying material changes and a sender aims to rekey. + More details are provided in RFC 9147 Section 6.1.*/ + rv = ssl3_AppendHandshakeHeaderAndStashSeqNum(ss, ssl_hs_key_update, 1, &ss->ssl3.hs.dtlsHandhakeKeyUpdateMessage); + if (rv != SECSuccess) { + return rv; /* error code set by ssl3_AppendHandshakeHeader, if applicable. */ + } + rv = ssl3_AppendHandshakeNumber(ss, request, 1); + if (rv != SECSuccess) { + return rv; /* error code set by ssl3_AppendHandshakeNumber, if applicable. */ + } + + return SECSuccess; +} + +/* The ssl3CipherSpecStr (sslspec.h) structure describes a spec for r/w records. +For the specification, the epoch is defined as uint16 value, +So the maximum epoch is 2 ^ 16 - 1*/ +#define DTLS13_MAX_EPOCH_TYPE PR_UINT16_MAX +/*RFC 9147. Section 8. +In order to provide an extra margin of security, +sending implementations MUST NOT allow the epoch to exceed 2^48-1.*/ +#define DTLS13_MAX_EPOCH ((0x1ULL << 48) - 1) + +SECStatus +dtls13_MaybeSendKeyUpdate(sslSocket *ss, tls13KeyUpdateRequest request, PRBool buffer) +{ + + SSL_TRC(30, ("%d: DTLS13[%d]: %s sends key update, response %s", + SSL_GETPID(), ss->fd, SSL_ROLE(ss), + (request == update_requested) ? "requested" + : "not requested")); + + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + SECStatus rv = SECFailure; + /* + For the specification, the epoch is defined as uint16 value (see bug 1809872) + and the sendKeyUpdate will update the writing keys + so, if the epoch is already maximum, KeyUpdate will be cancelled.*/ + + ssl_GetSpecWriteLock(ss); + /* This check is done as well in the updateTrafficKey function */ + if (ss->ssl3.cwSpec->epoch >= DTLS13_MAX_EPOCH_TYPE) { + ssl_ReleaseSpecWriteLock(ss); + SSL_TRC(30, ("%d: DTLS13[%d]: %s keyUpdate request was cancelled, as the writing epoch arrived to the maximum possible", + SSL_GETPID(), ss->fd, SSL_ROLE(ss))); + PORT_SetError(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED); + return SECFailure; + } else { + ssl_ReleaseSpecWriteLock(ss); + } + + PORT_Assert(DTLS13_MAX_EPOCH_TYPE <= DTLS13_MAX_EPOCH); + + ssl_GetSpecReadLock(ss); + /* TODO(AW) - See bug 1809872. */ + if (request == update_requested && ss->ssl3.crSpec->epoch >= DTLS13_MAX_EPOCH_TYPE) { + SSL_TRC(30, ("%d: DTLS13[%d]: %s keyUpdate request update_requested was cancelled, as the reading epoch arrived to the maximum possible", + SSL_GETPID(), ss->fd, SSL_ROLE(ss))); + request = update_not_requested; + } + ssl_ReleaseSpecReadLock(ss); + + /* RFC 9147. Section 5.8.4. + In contrast, implementations MUST NOT send KeyUpdate, NewConnectionId, or + RequestConnectionId messages if an earlier message of the same type + has not yet been acknowledged.*/ + if (ss->ssl3.hs.isKeyUpdateInProgress) { + SSL_TRC(30, ("%d: DTLS13[%d]: the previous %s KeyUpdate message was not yet ack-ed, dropping", + SSL_GETPID(), ss->fd, SSL_ROLE(ss), ss->ssl3.hs.sendMessageSeq)); + return SECSuccess; + } + + ssl_GetXmitBufLock(ss); + rv = dtls13_EnqueueKeyUpdateMessage(ss, request); + if (rv != SECSuccess) { + return rv; /* error code already set */ + } + + /* Trying to send the message - without buffering. */ + /* TODO[AW]: As I just emulated the API, I am not sure that it's necessary to buffer. */ + rv = ssl3_FlushHandshake(ss, 0); + if (rv != SECSuccess) { + return SECFailure; /* error code set by ssl3_FlushHandshake */ + } + ssl_ReleaseXmitBufLock(ss); + + /* The keyUpdate is started. */ + PORT_Assert(ss->ssl3.hs.isKeyUpdateInProgress == PR_FALSE); + ss->ssl3.hs.isKeyUpdateInProgress = PR_TRUE; + + SSL_TRC(30, ("%d: DTLS13[%d]: %s has just sent keyUpdate request #%d and is waiting for ack", + SSL_GETPID(), ss->fd, SSL_ROLE(ss), ss->ssl3.hs.dtlsHandhakeKeyUpdateMessage)); + return SECSuccess; +} + +SECStatus +dtls13_HandleKeyUpdate(sslSocket *ss, PRUint8 *b, unsigned int length, PRBool update) +{ + SSL_TRC(10, ("%d: DTLS13[%d]: %s handles Key Update", + SSL_GETPID(), ss->fd, SSL_ROLE(ss))); + + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + SECStatus rv = SECSuccess; + if (update == update_requested) { + /* Respond immediately (don't buffer). */ + rv = tls13_SendKeyUpdate(ss, update_not_requested, PR_FALSE); + if (rv != SECSuccess) { + return SECFailure; /* Error already set. */ + } + } + + SSL_TRC(30, ("%d: DTLS13[%d]: now %s is allowing the messages from the previous epoch", + SSL_GETPID(), ss->fd, SSL_ROLE(ss))); + ss->ssl3.hs.allowPreviousEpoch = PR_TRUE; + /* Updating the reading key. */ + rv = tls13_UpdateTrafficKeys(ss, ssl_secret_read); + if (rv != SECSuccess) { + return SECFailure; /* Error code set by tls13_UpdateTrafficKeys. */ + } + return SECSuccess; +}
\ No newline at end of file |