1
0
mirror of https://github.com/wolfSSL/wolfssl.git synced 2026-10-24 04:09:07 +03:00

dtls13: support session export and import

wolfSSL_dtls_export()/wolfSSL_dtls_import() only ever carried a DTLS 1.2
session. A DTLS 1.3 connection additionally keeps a table of epochs, each
with its own traffic secret, record and record number keys, sequence numbers
and replay window, and none of that was serialized.

Export version 7 adds two trailing sections to the blob, the DTLS 1.3 record
layer state and the Connection ID, both length prefixed so a reader that does
not know a section can step over it and a version 6 blob still imports.

- dtls13: serialize the send epoch, the peer epoch and, when there is one,
  the previous peer epoch: epoch numbers, sequence numbers, replay window,
  traffic secrets and key material. Import re-derives the record keys through
  Tls13DeriveRecordKeys(), the same expansion the handshake runs
- dtls: carry a tx Connection ID of any length. The peer picks it and RFC 9146
  bounds it only by its one byte length field, so DTLS_CID_MAX_SIZE may not
  refuse it. The rx id keeps that bound: it is the one we hand the peer and it
  has to fit the record layer buffers. Clear the target's CID when the blob
  carries none

Refusals, so a blob never describes a connection that cannot be restored:
- refuse to export a half sent fragmented handshake message, or while a write
  dup still has work parked
- refuse a state only import into a DTLS 1.3 session, and keep stamping the
  state only blob with export version 6
- require the imported epochs to be the ones the blob names, reset the epoch
  table on import and commit a slot only once every field of it has been read

Bounds checking on the import path:
- keep every exported section inside the buffer the caller sized, and step
  over the import sections by their declared length
- check the imported epoch key lengths against the negotiated suite and the
  traffic secret length against the hash
- size the exported key state from the build's own cipher sizes
- scrub the session export scratch buffer before freeing it
- use IsAtLeastTLSv1_3() for the imported version check

CI: build the session export job with Connection ID support.
This commit is contained in:
Marco Oliverio
2026-08-26 15:29:10 +02:00
parent a09ec82b9b
commit 5d1903ff34
9 changed files with 3956 additions and 13 deletions

View File

@@ -101,7 +101,9 @@
{"name": "opensslextra-no-ca-names", "minutes": 2.6,
"configure": ["--enable-opensslextra", "CPPFLAGS=-DWOLFSSL_NO_CA_NAMES"]},
{"name": "sessionexport-dtls13", "minutes": 2.6,
"configure": ["--enable-sessionexport", "--enable-dtls", "--enable-dtls13"]},
"comment": "--enable-dtlscid is what makes the Connection ID export/import path compile; without it every test guarded on WOLFSSL_DTLS_CID is skipped, and no other job in this file pairs session export with CID.",
"configure": ["--enable-sessionexport", "--enable-dtls", "--enable-dtls13",
"--enable-dtlscid"]},
{"name": "lms-xmss-verify-only", "minutes": 2.5,
"configure": ["--enable-lms=small,verify-only", "--enable-xmss=small,verify-only"]},
{"name": "sp-4096-m32", "minutes": 1.2,

View File

@@ -826,6 +826,10 @@ int wolfSSL_dtls_set_export(WOLFSSL* ssl, wc_dtls_export func);
choice over when the session is serialized. If buffer is NULL when
passed to function then sz will be set to the size of buffer needed
for serializing the WOLFSSL session.
WARNING: buf holds the key material the connection continues with. For
DTLS 1.3 that includes the traffic secrets every later epoch key
is derived from, so it stays sensitive for the rest of the
connection: encrypt it if stored and wipe the buffer when done.
\return Success If successful, the amount of the buffer used will
be returned.

View File

@@ -1545,6 +1545,109 @@ const unsigned char* wolfSSL_dtls_cid_parse(const unsigned char* msg,
#endif
return NULL;
}
#ifdef WOLFSSL_SESSION_EXPORT
int DtlsCidExport(WOLFSSL* ssl, byte* exp, word32 len)
{
CIDInfo* info;
word32 idx = 0;
byte rxSz, txSz;
if (ssl == NULL || exp == NULL)
return BAD_FUNC_ARG;
info = DtlsCidGetInfo(ssl);
if (info == NULL)
return BAD_STATE_E;
rxSz = (info->rx != NULL) ? info->rx->length : 0;
txSz = (info->tx != NULL) ? info->tx->length : 0;
if ((word32)((3 * OPAQUE8_LEN) + rxSz + txSz) > len)
return BUFFER_E;
exp[idx++] = info->negotiated;
exp[idx++] = rxSz;
if (rxSz > 0) {
XMEMCPY(exp + idx, info->rx->id, rxSz);
idx += rxSz;
}
exp[idx++] = txSz;
if (txSz > 0) {
XMEMCPY(exp + idx, info->tx->id, txSz);
idx += txSz;
}
return (int)idx;
}
int DtlsCidImport(WOLFSSL* ssl, const byte* exp, word32 len)
{
CIDInfo* info;
word32 idx = 0;
byte negotiated, rxSz, txSz;
int ret;
if (ssl == NULL || exp == NULL)
return BAD_FUNC_ARG;
if ((2 * OPAQUE8_LEN) > len)
return BUFFER_E;
negotiated = exp[idx++];
rxSz = exp[idx++];
if (rxSz > DTLS_CID_MAX_SIZE || idx + rxSz + OPAQUE8_LEN > len)
return BUFFER_E;
idx += rxSz;
/* the tx id is the peer's choice, bounded only by its length field */
txSz = exp[idx++];
if (idx + txSz > len)
return BUFFER_E;
ret = TLSX_ConnectionID_Use(ssl);
if (ret != 0)
return ret;
info = DtlsCidGetInfo(ssl);
if (info == NULL)
return BAD_STATE_E;
XFREE(info->rx, ssl->heap, DYNAMIC_TYPE_TLSX);
info->rx = NULL;
XFREE(info->tx, ssl->heap, DYNAMIC_TYPE_TLSX);
info->tx = NULL;
if (rxSz > 0) {
info->rx = DtlsCidNew(exp + (2 * OPAQUE8_LEN), rxSz, ssl->heap);
if (info->rx == NULL)
return MEMORY_ERROR;
}
if (txSz > 0) {
info->tx = DtlsCidNew(exp + idx, txSz, ssl->heap);
if (info->tx == NULL)
return MEMORY_ERROR;
}
idx += txSz;
info->negotiated = negotiated ? 1 : 0;
ssl->options.useDtlsCID = 1;
return (int)idx;
}
void DtlsCidClear(WOLFSSL* ssl)
{
if (ssl == NULL || DtlsCidGetInfo(ssl) == NULL)
return;
TLSX_Remove(&ssl->extensions, TLSX_CONNECTION_ID, ssl->heap);
ssl->options.useDtlsCID = 0;
}
#endif /* WOLFSSL_SESSION_EXPORT */
#endif /* WOLFSSL_DTLS_CID */
byte DtlsGetCidTxSize(WOLFSSL* ssl)

View File

@@ -2691,6 +2691,522 @@ int Dtls13SetRecordNumberKeys(WOLFSSL* ssl, enum encrypt_side side)
return NOT_COMPILED_IN;
}
#ifdef WOLFSSL_SESSION_EXPORT
/* Derive the key material of an imported epoch from the traffic secret held in
* ssl->clientSecret/serverSecret, so that none of it has to be serialized. */
static int Dtls13DeriveEpochKeys(WOLFSSL* ssl, Dtls13Epoch* e, int side)
{
int provision;
int ret;
if (ssl->specs.key_size > MAX_SYM_KEY_SIZE ||
ssl->specs.iv_size > MAX_WRITE_IV_SZ)
return BAD_STATE_E;
if (side == ENCRYPT_AND_DECRYPT_SIDE)
provision = PROVISION_CLIENT_SERVER;
else
provision = ((ssl->options.side != WOLFSSL_CLIENT_END) ^
(side == ENCRYPT_SIDE_ONLY)) ? PROVISION_CLIENT :
PROVISION_SERVER;
ret = Tls13DeriveRecordKeys(ssl, provision);
if (ret != 0)
return ret;
/* fills in the implicit AEAD IVs from the write IVs */
ret = SetKeysSide(ssl, (enum encrypt_side)side);
if (ret != 0)
return ret;
Dtls13EpochCopyKeys(ssl, e, &ssl->keys, side);
return 0;
}
/* Serialize the write key, record number key and write IV of the direction the
* peer sends in. Only the previous peer epoch needs this, its keys being the
* one set that can not be derived again on import.
* Returns the number of bytes written to 'exp' or a negative value. */
static int Dtls13ExportEpochKeys(const WOLFSSL* ssl, const Dtls13Epoch* e,
byte* exp, word32 len)
{
const byte *writeKey, *writeIV, *snKey;
word32 idx = 0;
byte keySz, ivSz;
if (ssl->specs.key_size > MAX_SYM_KEY_SIZE ||
ssl->specs.iv_size > MAX_WRITE_IV_SZ)
return BAD_STATE_E;
keySz = (byte)ssl->specs.key_size;
ivSz = (byte)ssl->specs.iv_size;
if ((2u * OPAQUE8_LEN) + (2u * keySz) + ivSz > len)
return BUFFER_E;
if (ssl->options.side == WOLFSSL_CLIENT_END) {
writeKey = e->server_write_key;
writeIV = e->server_write_IV;
snKey = e->server_sn_key;
}
else {
writeKey = e->client_write_key;
writeIV = e->client_write_IV;
snKey = e->client_sn_key;
}
exp[idx++] = keySz;
XMEMCPY(exp + idx, writeKey, keySz); idx += keySz;
XMEMCPY(exp + idx, snKey, keySz); idx += keySz;
exp[idx++] = ivSz;
XMEMCPY(exp + idx, writeIV, ivSz); idx += ivSz;
return (int)idx;
}
/* Install the key material written by Dtls13ExportEpochKeys() into an epoch
* slot. Returns the number of bytes read from 'exp' or a negative value. */
static int Dtls13ImportEpochKeys(WOLFSSL* ssl, Dtls13Epoch* e, const byte* exp,
word32 len)
{
byte *writeKey, *writeIV, *snKey;
word32 idx = 0;
byte keySz, ivSz;
int ret;
if (OPAQUE8_LEN > len)
return BUFFER_E;
keySz = exp[idx++];
if (keySz != ssl->specs.key_size || keySz > MAX_SYM_KEY_SIZE ||
idx + (2u * keySz) + OPAQUE8_LEN > len)
return BUFFER_E;
if (ssl->options.side == WOLFSSL_CLIENT_END) {
writeKey = ssl->keys.server_write_key;
writeIV = ssl->keys.server_write_IV;
snKey = ssl->keys.server_sn_key;
}
else {
writeKey = ssl->keys.client_write_key;
writeIV = ssl->keys.client_write_IV;
snKey = ssl->keys.client_sn_key;
}
XMEMCPY(writeKey, exp + idx, keySz); idx += keySz;
XMEMCPY(snKey, exp + idx, keySz); idx += keySz;
ivSz = exp[idx++];
if (ivSz != ssl->specs.iv_size || ivSz > MAX_WRITE_IV_SZ ||
idx + ivSz > len)
return BUFFER_E;
XMEMCPY(writeIV, exp + idx, ivSz); idx += ivSz;
ret = SetKeysSide(ssl, DECRYPT_SIDE_ONLY);
if (ret != 0)
return ret;
Dtls13EpochCopyKeys(ssl, e, &ssl->keys, DECRYPT_SIDE_ONLY);
return (int)idx;
}
/* Serialize one Dtls13Epoch entry. Key material is left out, as is the side,
* which follows from the position of the entry in the blob.
* Returns the number of bytes written to 'exp' or a negative value. */
static int Dtls13ExportEpoch(const Dtls13Epoch* e, byte* exp, word32 len)
{
word32 idx = 0;
word32 i;
if (DTLS_EXPORT_DTLS13_EPOCH_SZ > len)
return BUFFER_E;
c64toa(&e->epochNumber, exp + idx); idx += OPAQUE64_LEN;
c64toa(&e->nextSeqNumber, exp + idx); idx += OPAQUE64_LEN;
c64toa(&e->nextPeerSeqNumber, exp + idx); idx += OPAQUE64_LEN;
#ifndef WOLFSSL_TLS13_IGNORE_AEAD_LIMITS
c64toa(&e->dropCount, exp + idx);
#else
XMEMSET(exp + idx, 0, OPAQUE64_LEN);
#endif
idx += OPAQUE64_LEN;
c16toa(WOLFSSL_DTLS_WINDOW_WORDS, exp + idx); idx += OPAQUE16_LEN;
for (i = 0; i < WOLFSSL_DTLS_WINDOW_WORDS; i++) {
c32toa(e->window[i], exp + idx); idx += OPAQUE32_LEN;
}
return (int)idx;
}
/* Parse one serialized epoch entry into an epoch slot of 'ssl'. 'side' is the
* direction it is used in, 'hasKeys' says it carries its own key material and
* 'number' is the epoch it has to be for.
* Returns the number of bytes read from 'exp' or a negative value. */
static int Dtls13ImportEpoch(WOLFSSL* ssl, const byte* exp, word32 len,
int side, int hasKeys, w64wrapper number)
{
Dtls13Epoch* e;
w64wrapper epochNumber;
word32 idx = 0;
word32 wordAdj = 0;
word16 wordCount;
word16 i;
int ret = 0;
if ((4 * OPAQUE64_LEN) + OPAQUE16_LEN > len)
return BUFFER_E;
ato64(exp + idx, &epochNumber); idx += OPAQUE64_LEN;
if (!w64Equal(epochNumber, number))
return BAD_STATE_E;
e = Dtls13NewEpochSlot(ssl);
if (e == NULL)
return BAD_STATE_E;
e->epochNumber = epochNumber;
e->side = (byte)side;
ato64(exp + idx, &e->nextSeqNumber); idx += OPAQUE64_LEN;
ato64(exp + idx, &e->nextPeerSeqNumber); idx += OPAQUE64_LEN;
#ifndef WOLFSSL_TLS13_IGNORE_AEAD_LIMITS
ato64(exp + idx, &e->dropCount);
#endif
idx += OPAQUE64_LEN;
/* window size may differ from ours, skip any extra words */
ato16(exp + idx, &wordCount); idx += OPAQUE16_LEN;
if (wordCount > WOLFSSL_DTLS_WINDOW_WORDS) {
wordAdj = (wordCount - WOLFSSL_DTLS_WINDOW_WORDS) * OPAQUE32_LEN;
wordCount = WOLFSSL_DTLS_WINDOW_WORDS;
}
if (idx + (wordCount * OPAQUE32_LEN) + wordAdj > len)
return BUFFER_E;
/* words the peer never tracked count as already received */
XMEMSET(e->window, 0xFF, sizeof(e->window));
for (i = 0; i < wordCount; i++) {
ato32(exp + idx, &e->window[i]); idx += OPAQUE32_LEN;
}
idx += wordAdj;
if (hasKeys) {
ret = Dtls13ImportEpochKeys(ssl, e, exp + idx, len - idx);
if (ret < 0)
return ret;
idx += (word32)ret;
}
else {
ret = Dtls13DeriveEpochKeys(ssl, e, side);
if (ret != 0)
return ret;
}
e->isValid = 1;
return (int)idx;
}
/* Write one length prefixed epoch field: the state of 'state' when it is not
* NULL, then the key material of 'keys' when it is not NULL. Both NULL writes
* the empty field of an epoch the connection does not have.
* Returns the number of bytes written to 'exp' or a negative value. */
static int Dtls13ExportEpochField(const WOLFSSL* ssl, const Dtls13Epoch* state,
const Dtls13Epoch* keys, byte* exp, word32 len)
{
word32 idx = WOLFSSL_EXPORT_LEN;
int ret;
if (WOLFSSL_EXPORT_LEN > len)
return BUFFER_E;
if (state != NULL) {
ret = Dtls13ExportEpoch(state, exp + idx, len - idx);
if (ret < 0)
return ret;
idx += (word32)ret;
}
if (keys != NULL) {
ret = Dtls13ExportEpochKeys(ssl, keys, exp + idx, len - idx);
if (ret < 0)
return ret;
idx += (word32)ret;
}
c16toa((word16)(idx - WOLFSSL_EXPORT_LEN), exp);
return (int)idx;
}
/* Read the length prefix of an epoch field, returning its body in 'payload'
* and 'payloadSz'.
* Returns the number of bytes the field takes up or a negative value. */
static int Dtls13ImportEpochField(const byte* exp, word32 len,
const byte** payload, word32* payloadSz)
{
word16 fieldSz;
if (WOLFSSL_EXPORT_LEN > len)
return BUFFER_E;
ato16(exp, &fieldSz);
if ((word32)fieldSz > len - WOLFSSL_EXPORT_LEN)
return BUFFER_E;
*payload = exp + WOLFSSL_EXPORT_LEN;
*payloadSz = fieldSz;
return (int)(WOLFSSL_EXPORT_LEN + fieldSz);
}
/* Serialize the DTLS 1.3 record layer state: the current, peer and invalidate
* before epoch numbers, the traffic secrets and three length prefixed epoch
* fields - the sending epoch, the peer epoch and the previous peer epoch - a
* field being empty when the connection does not have that epoch.
* Returns the number of bytes written to 'exp' or a negative value. */
int ExportDtls13State(WOLFSSL* ssl, byte* exp, word32 len)
{
const Dtls13Epoch* sendEpoch;
const Dtls13Epoch* recvEpoch;
const Dtls13Epoch* prevEpoch;
w64wrapper prevEpochNumber;
word32 idx = 0;
byte secretSz;
int ret;
WOLFSSL_ENTER("ExportDtls13State");
if (ssl == NULL || exp == NULL)
return BAD_FUNC_ARG;
/* the serialized state has no field for a KeyUpdate in progress */
if (ssl->dtls13WaitKeyUpdateAck || ssl->dtls13DoKeyUpdate ||
ssl->options.sendKeyUpdate) {
WOLFSSL_MSG("Can not export with a KeyUpdate in progress");
return BAD_STATE_E;
}
/* the rest of a half sent message is in ssl->dtls13FragmentsBuffer, which
* the blob has no section for */
if (ssl->dtls13SendingFragments) {
WOLFSSL_MSG("Can not export with a half sent fragmented message");
return BAD_STATE_E;
}
#ifdef HAVE_WRITE_DUP
/* the read side of a write dup parks the KeyUpdate and ACKs it owes in the
* shared object, where neither WOLFSSL records them */
if (ssl->dupWrite != NULL) {
int parked;
if (wc_LockMutex(&ssl->dupWrite->dupMutex) != 0)
return BAD_MUTEX_E;
parked = ssl->dupWrite->keyUpdateRespond || ssl->dupWrite->sendAcks;
wc_UnLockMutex(&ssl->dupWrite->dupMutex);
if (parked) {
WOLFSSL_MSG("Can not export with work parked in the write dup");
return BAD_STATE_E;
}
}
#endif /* HAVE_WRITE_DUP */
sendEpoch = Dtls13GetEpoch(ssl, ssl->dtls13Epoch);
recvEpoch = Dtls13GetEpoch(ssl, ssl->dtls13PeerEpoch);
if (sendEpoch == NULL || recvEpoch == NULL)
return BAD_STATE_E;
/* the peer keeps retransmitting in the epoch before dtls13PeerEpoch until
* it sees our ACK of its KeyUpdate; before the first KeyUpdate that is the
* handshake epoch, which the peer does not fall back to */
prevEpoch = NULL;
prevEpochNumber = ssl->dtls13PeerEpoch;
if (w64GT(prevEpochNumber, w64From32(0, DTLS13_EPOCH_TRAFFIC0))) {
w64Decrement(&prevEpochNumber);
prevEpoch = Dtls13GetEpoch(ssl, prevEpochNumber);
/* an epoch already retired for decryption is of no use to the peer */
if (prevEpoch != NULL && prevEpoch->side == ENCRYPT_SIDE_ONLY)
prevEpoch = NULL;
}
secretSz = ssl->specs.hash_size;
if (secretSz > SECRET_LEN)
return BAD_STATE_E;
if ((3 * OPAQUE64_LEN) + OPAQUE8_LEN + (2u * secretSz) > len)
return BUFFER_E;
c64toa(&ssl->dtls13Epoch, exp + idx); idx += OPAQUE64_LEN;
c64toa(&ssl->dtls13PeerEpoch, exp + idx); idx += OPAQUE64_LEN;
/* the epochs below it stop decrypting once the peer is seen using it
* (RFC 9147 Section 4.5.3) */
c64toa(&ssl->dtls13InvalidateBefore, exp + idx); idx += OPAQUE64_LEN;
/* traffic secrets, needed to derive the next epoch keys on KeyUpdate */
exp[idx++] = secretSz;
XMEMCPY(exp + idx, ssl->clientSecret, secretSz); idx += secretSz;
XMEMCPY(exp + idx, ssl->serverSecret, secretSz); idx += secretSz;
/* the sending epoch */
ret = Dtls13ExportEpochField(ssl, sendEpoch, NULL, exp + idx, len - idx);
if (ret < 0)
return ret;
idx += (word32)ret;
/* the peer epoch, empty when it is the sending epoch */
ret = Dtls13ExportEpochField(ssl,
(recvEpoch != sendEpoch) ? recvEpoch : NULL, NULL,
exp + idx, len - idx);
if (ret < 0)
return ret;
idx += (word32)ret;
/* the previous peer epoch, with its key material; its state is left out
* when it is our own sending epoch, which the first field already holds */
ret = Dtls13ExportEpochField(ssl,
(prevEpoch != sendEpoch) ? prevEpoch : NULL, prevEpoch,
exp + idx, len - idx);
if (ret < 0)
return ret;
idx += (word32)ret;
WOLFSSL_LEAVE("ExportDtls13State", (int)idx);
return (int)idx;
}
/* Parse the DTLS 1.3 record layer state written by ExportDtls13State and
* activate the imported epochs.
* Returns the number of bytes read from 'exp' or a negative value. */
int ImportDtls13State(WOLFSSL* ssl, const byte* exp, word32 len)
{
w64wrapper traffic0;
const byte* field;
word32 fieldSz;
word32 idx = 0;
byte secretSz;
int ret;
WOLFSSL_ENTER("ImportDtls13State");
if (ssl == NULL || exp == NULL)
return BAD_FUNC_ARG;
/* an import replaces the record layer wholesale; the unprotected epoch is
* not put back, an imported connection being past its handshake */
ForceZero(ssl->dtls13Epochs, sizeof(ssl->dtls13Epochs));
ssl->dtls13EncryptEpoch = NULL;
ssl->dtls13DecryptEpoch = NULL;
if ((3 * OPAQUE64_LEN) + OPAQUE8_LEN > len)
return BUFFER_E;
ato64(exp + idx, &ssl->dtls13Epoch); idx += OPAQUE64_LEN;
ato64(exp + idx, &ssl->dtls13PeerEpoch); idx += OPAQUE64_LEN;
ato64(exp + idx, &ssl->dtls13InvalidateBefore); idx += OPAQUE64_LEN;
secretSz = exp[idx++];
/* every epoch key is derived over specs.hash_size bytes of the secrets */
if (secretSz != ssl->specs.hash_size || secretSz > SECRET_LEN ||
idx + (2u * secretSz) > len)
return BUFFER_E;
XMEMCPY(ssl->clientSecret, exp + idx, secretSz); idx += secretSz;
XMEMCPY(ssl->serverSecret, exp + idx, secretSz); idx += secretSz;
/* an exported connection is past its handshake, so both epochs it names are
* traffic epochs; Dtls13SetEpochKeys() installs no key for epoch 0 */
traffic0 = w64From32(0, DTLS13_EPOCH_TRAFFIC0);
if (w64LT(ssl->dtls13Epoch, traffic0) ||
w64LT(ssl->dtls13PeerEpoch, traffic0))
return BAD_STATE_E;
/* the sending epoch, which every exported connection has */
ret = Dtls13ImportEpochField(exp + idx, len - idx, &field, &fieldSz);
if (ret < 0)
return ret;
idx += (word32)ret;
if (fieldSz == 0)
return BUFFER_E;
ret = Dtls13ImportEpoch(ssl, field, fieldSz,
w64Equal(ssl->dtls13Epoch, ssl->dtls13PeerEpoch) ?
ENCRYPT_AND_DECRYPT_SIDE : ENCRYPT_SIDE_ONLY,
0, ssl->dtls13Epoch);
if (ret < 0)
return ret;
/* the peer epoch, empty when the field above is already for it */
ret = Dtls13ImportEpochField(exp + idx, len - idx, &field, &fieldSz);
if (ret < 0)
return ret;
idx += (word32)ret;
if (w64Equal(ssl->dtls13Epoch, ssl->dtls13PeerEpoch)) {
if (fieldSz != 0)
return BUFFER_E;
}
else {
if (fieldSz == 0)
return BUFFER_E;
ret = Dtls13ImportEpoch(ssl, field, fieldSz, DECRYPT_SIDE_ONLY, 0,
ssl->dtls13PeerEpoch);
if (ret < 0)
return ret;
}
/* The previous peer epoch, the one epoch of the blob that the header does
* not name. It only exists once the peer has updated its keys. */
ret = Dtls13ImportEpochField(exp + idx, len - idx, &field, &fieldSz);
if (ret < 0)
return ret;
idx += (word32)ret;
if (fieldSz > 0) {
w64wrapper prevNumber = ssl->dtls13PeerEpoch;
if (!w64GT(ssl->dtls13PeerEpoch, traffic0))
return BAD_STATE_E;
w64Decrement(&prevNumber);
if (w64Equal(prevNumber, ssl->dtls13Epoch)) {
/* our own sending epoch, imported above: the field repeats none of
* its state, only the keys the derivation can not reach */
Dtls13Epoch* e = Dtls13GetEpoch(ssl, prevNumber);
if (e == NULL)
return BAD_STATE_E;
ret = Dtls13ImportEpochKeys(ssl, e, field, fieldSz);
if (ret < 0)
return ret;
e->side = ENCRYPT_AND_DECRYPT_SIDE;
}
else {
ret = Dtls13ImportEpoch(ssl, field, fieldSz, DECRYPT_SIDE_ONLY, 1,
prevNumber);
if (ret < 0)
return ret;
}
}
/* Activate the imported epochs: refills ssl->keys from the epoch entries
* and re-keys the record and record number ciphers (SetKeysSide). */
if (w64Equal(ssl->dtls13Epoch, ssl->dtls13PeerEpoch)) {
ret = Dtls13SetEpochKeys(ssl, ssl->dtls13Epoch,
ENCRYPT_AND_DECRYPT_SIDE);
}
else {
ret = Dtls13SetEpochKeys(ssl, ssl->dtls13Epoch, ENCRYPT_SIDE_ONLY);
if (ret == 0)
ret = Dtls13SetEpochKeys(ssl, ssl->dtls13PeerEpoch,
DECRYPT_SIDE_ONLY);
}
if (ret != 0)
return ret;
/* The imported connection is past its handshake: received KeyUpdate
* messages must pass the out-of-order sanity check. */
if (ssl->options.handShakeDone)
ssl->msgsReceived.got_finished = 1;
WOLFSSL_LEAVE("ImportDtls13State", (int)idx);
return (int)idx;
}
#endif /* WOLFSSL_SESSION_EXPORT */
int Dtls13WriteAckMessage(WOLFSSL* ssl,
Dtls13RecordNumber* recordNumberList, word16 recordsCount, word32* length)

View File

@@ -1515,6 +1515,7 @@ static int ImportOptions(WOLFSSL* ssl, const byte* exp, word32 len, byte ver,
switch (ver) {
case WOLFSSL_EXPORT_VERSION:
case WOLFSSL_EXPORT_VERSION_6:
if (len < DTLS_EXPORT_OPT_SZ) {
WOLFSSL_MSG("Sanity check on buffer size failed");
return BAD_FUNC_ARG;
@@ -1689,9 +1690,8 @@ static int ImportOptions(WOLFSSL* ssl, const byte* exp, word32 len, byte ver,
return VERSION_ERROR;
}
/* set TLS 1.3 flag in options if this was a TLS 1.3 connection */
if (ssl->version.major == SSLv3_MAJOR &&
ssl->version.minor == TLSv1_3_MINOR) {
/* set TLS 1.3 flag in options if this was a (D)TLS 1.3 connection */
if (IsAtLeastTLSv1_3(ssl->version)) {
options->tls1_3 = 1;
}
@@ -1751,8 +1751,10 @@ static int ImportPeerInfo(WOLFSSL* ssl, const byte* buf, word32 len, byte ver)
word16 port;
char ip[MAX_EXPORT_IP];
if (ver != WOLFSSL_EXPORT_VERSION && ver != WOLFSSL_EXPORT_VERSION_5 &&
ver != WOLFSSL_EXPORT_VERSION_4 && ver != WOLFSSL_EXPORT_VERSION_3) {
if (ver != WOLFSSL_EXPORT_VERSION && ver != WOLFSSL_EXPORT_VERSION_6 &&
ver != WOLFSSL_EXPORT_VERSION_5 &&
ver != WOLFSSL_EXPORT_VERSION_4 &&
ver != WOLFSSL_EXPORT_VERSION_3) {
WOLFSSL_MSG("Export version not supported");
return BAD_FUNC_ARG;
}
@@ -1811,6 +1813,15 @@ int wolfSSL_dtls_export_state_internal(WOLFSSL* ssl, byte* buf, word32 sz)
return BAD_FUNC_ARG;
}
#ifdef WOLFSSL_DTLS13
if (ssl->options.dtls && ssl->version.major == DTLS_MAJOR &&
ssl->version.minor == DTLSv1_3_MINOR) {
WOLFSSL_MSG("DTLS 1.3 does not support state only export");
WOLFSSL_LEAVE("wolfSSL_dtls_export_state_internal", VERSION_ERROR);
return VERSION_ERROR;
}
#endif
totalLen += WOLFSSL_EXPORT_LEN * 2; /* 2 protocol bytes and 2 length bytes */
/* each of the following have a 2 byte length before data */
totalLen += WOLFSSL_EXPORT_LEN + DTLS_EXPORT_MIN_KEY_SZ;
@@ -1820,8 +1831,10 @@ int wolfSSL_dtls_export_state_internal(WOLFSSL* ssl, byte* buf, word32 sz)
}
buf[idx++] = (byte)DTLS_EXPORT_STATE_PRO;
/* the state only payload has not changed since version 6, so stamp that
* rather than the current session format version */
buf[idx++] = ((byte)DTLS_EXPORT_STATE_PRO & 0xF0) |
((byte)WOLFSSL_EXPORT_VERSION & 0X0F);
((byte)WOLFSSL_EXPORT_VERSION_6 & 0X0F);
idx += WOLFSSL_EXPORT_LEN; /* leave room for total length */
/* export keys struct and dtls state -- variable length stored in ret */
@@ -1867,6 +1880,15 @@ int wolfSSL_dtls_import_state_internal(WOLFSSL* ssl, const byte* buf, word32 sz)
return BAD_FUNC_ARG;
}
#ifdef WOLFSSL_DTLS13
if (ssl->options.dtls && ssl->version.major == DTLS_MAJOR &&
ssl->version.minor == DTLSv1_3_MINOR) {
WOLFSSL_MSG("DTLS 1.3 does not support state only import");
WOLFSSL_LEAVE("wolfSSL_dtls_import_state_internal", VERSION_ERROR);
return VERSION_ERROR;
}
#endif
if (buf[idx++] != (byte)DTLS_EXPORT_STATE_PRO ||
(buf[idx] & 0xF0) != ((byte)DTLS_EXPORT_PRO & 0xF0)) {
WOLFSSL_MSG("Incorrect protocol");
@@ -1894,6 +1916,7 @@ int wolfSSL_dtls_import_state_internal(WOLFSSL* ssl, const byte* buf, word32 sz)
/* perform sanity checks and extract Options information used */
switch (version) {
case WOLFSSL_EXPORT_VERSION:
case WOLFSSL_EXPORT_VERSION_6:
break;
default:
@@ -2002,6 +2025,7 @@ int wolfSSL_session_import_internal(WOLFSSL* ssl, const unsigned char* buf,
if (ret == 0) {
switch (version) {
case WOLFSSL_EXPORT_VERSION:
case WOLFSSL_EXPORT_VERSION_6:
if (type == WOLFSSL_EXPORT_DTLS) {
optSz = DTLS_EXPORT_OPT_SZ;
}
@@ -2142,6 +2166,83 @@ int wolfSSL_session_import_internal(WOLFSSL* ssl, const unsigned char* buf,
}
}
/* DTLS 1.3 record layer state, present since export version 7 */
if (ret == 0 && type == WOLFSSL_EXPORT_DTLS &&
ssl->version.major == DTLS_MAJOR &&
ssl->version.minor == DTLSv1_3_MINOR) {
if (version <= WOLFSSL_EXPORT_VERSION_6) {
WOLFSSL_MSG("Serialized DTLS 1.3 session too old to import");
ret = VERSION_ERROR;
}
else if (WOLFSSL_EXPORT_LEN + idx > sz) {
WOLFSSL_MSG("Import DTLS 1.3 state error");
ret = BUFFER_E;
}
else {
ato16(buf + idx, &length); idx += WOLFSSL_EXPORT_LEN;
if (idx + length > sz) {
WOLFSSL_MSG("Import DTLS 1.3 state error");
ret = BUFFER_E;
}
else {
#ifdef WOLFSSL_DTLS13
rc = ImportDtls13State(ssl, buf + idx, length);
if (rc < 0) {
WOLFSSL_MSG("Import DTLS 1.3 state error");
ret = rc;
}
else {
idx += length;
}
#else
if (length > 0) {
WOLFSSL_MSG("Can not import a DTLS 1.3 session without "
"DTLS 1.3 support");
ret = NOT_COMPILED_IN;
}
#endif
}
}
}
/* Connection ID, written for every DTLS session since export version 7,
* with a zero length when there is none */
if (ret == 0 && type == WOLFSSL_EXPORT_DTLS &&
version > WOLFSSL_EXPORT_VERSION_6) {
if (WOLFSSL_EXPORT_LEN + idx > sz) {
WOLFSSL_MSG("Import CID error");
ret = BUFFER_E;
}
else {
ato16(buf + idx, &length); idx += WOLFSSL_EXPORT_LEN;
if (idx + length > sz) {
WOLFSSL_MSG("Import CID error");
ret = BUFFER_E;
}
else if (length > 0) {
#ifdef WOLFSSL_DTLS_CID
rc = DtlsCidImport(ssl, buf + idx, length);
if (rc < 0) {
WOLFSSL_MSG("Import CID error");
ret = rc;
}
else {
idx += length;
}
#else
WOLFSSL_MSG("Can not import a CID session without CID support");
ret = NOT_COMPILED_IN;
#endif
}
#ifdef WOLFSSL_DTLS_CID
else {
/* do not keep a CID the object was created with */
DtlsCidClear(ssl);
}
#endif
}
}
/* make sure is a valid suite used */
if (ret == 0 && wolfSSL_get_cipher(ssl) == NULL) {
WOLFSSL_MSG("Can not match cipher suite imported");
@@ -2192,7 +2293,14 @@ int wolfSSL_session_export_internal(WOLFSSL* ssl, byte* buf, word32* sz,
{
int ret = 0;
word32 idx = 0;
word32 totalLen = 0;
#ifdef WOLFSSL_DTLS13
int dtls13 = 0;
#endif
#ifdef WOLFSSL_DTLS_CID
int cid = 0;
#endif
WOLFSSL_ENTER("wolfSSL_session_export_internal");
@@ -2209,9 +2317,30 @@ int wolfSSL_session_export_internal(WOLFSSL* ssl, byte* buf, word32* sz,
totalLen += WOLFSSL_EXPORT_LEN + WOLFSSL_EXPORT_SPC_SZ;
#ifdef WOLFSSL_DTLS
if (type == WOLFSSL_EXPORT_DTLS) {
totalLen += WOLFSSL_EXPORT_LEN + ssl->buffers.dtlsCtx.peer.sz;
totalLen += WOLFSSL_EXPORT_LEN + MAX_EXPORT_IP +
(3 * WOLFSSL_EXPORT_LEN);
}
#endif
#ifdef WOLFSSL_DTLS13
if (type == WOLFSSL_EXPORT_DTLS && ssl->options.dtls &&
ssl->version.major == DTLS_MAJOR &&
ssl->version.minor == DTLSv1_3_MINOR) {
dtls13 = 1;
totalLen += WOLFSSL_EXPORT_LEN + DTLS_EXPORT_DTLS13_SZ;
}
#endif
/* Connection ID section, for DTLS 1.2 and 1.3 alike */
if (type == WOLFSSL_EXPORT_DTLS) {
totalLen += WOLFSSL_EXPORT_LEN;
#ifdef WOLFSSL_DTLS_CID
if (ssl->options.dtls && wolfSSL_dtls_cid_is_enabled(ssl)) {
cid = 1;
/* the actual lengths, not the worst case DTLS_EXPORT_CID_SZ */
totalLen += (3 * OPAQUE8_LEN) + DtlsGetCidRxSize(ssl) +
DtlsGetCidTxSize(ssl);
}
#endif
}
}
/* check if sz is sufficient for the worst-case scenario computed above,
@@ -2270,6 +2399,10 @@ int wolfSSL_session_export_internal(WOLFSSL* ssl, byte* buf, word32* sz,
}
/* export of peer information */
if (ret == 0 && idx + WOLFSSL_EXPORT_LEN > *sz) {
WOLFSSL_MSG("export buffer was too small for the peer info length");
ret = BUFFER_E;
}
if (ret == 0) {
idx += WOLFSSL_EXPORT_LEN;
#ifdef WOLFSSL_SESSION_EXPORT_NOPEER
@@ -2284,6 +2417,48 @@ int wolfSSL_session_export_internal(WOLFSSL* ssl, byte* buf, word32* sz,
}
}
#ifdef WOLFSSL_DTLS13
/* export of DTLS 1.3 record layer state */
if (ret == 0 && dtls13 && idx + WOLFSSL_EXPORT_LEN > *sz) {
WOLFSSL_MSG("export buffer was too small for the DTLS 1.3 length");
ret = BUFFER_E;
}
if (ret == 0 && dtls13) {
idx += WOLFSSL_EXPORT_LEN;
ret = ExportDtls13State(ssl, buf + idx, *sz - idx);
if (ret >= 0) {
c16toa((word16)ret, buf + idx - WOLFSSL_EXPORT_LEN);
idx += ret;
ret = 0;
}
}
#endif
/* export of the negotiated Connection ID, zero length when there is none */
if (ret == 0 && type == WOLFSSL_EXPORT_DTLS &&
idx + WOLFSSL_EXPORT_LEN > *sz) {
WOLFSSL_MSG("export buffer was too small for the CID length");
ret = BUFFER_E;
}
if (ret == 0 && type == WOLFSSL_EXPORT_DTLS) {
word16 cidSz = 0;
#ifdef WOLFSSL_DTLS_CID
if (cid) {
ret = DtlsCidExport(ssl, buf + idx + WOLFSSL_EXPORT_LEN,
*sz - idx - WOLFSSL_EXPORT_LEN);
if (ret >= 0) {
cidSz = (word16)ret;
ret = 0;
}
}
#endif
if (ret == 0) {
c16toa(cidSz, buf + idx); idx += WOLFSSL_EXPORT_LEN;
idx += cidSz;
}
}
if (ret != 0 && ret != WC_NO_ERR_TRACE(LENGTH_ONLY_E) && buf != NULL) {
/*in a fail case clear the buffer which could contain partial key info*/
XMEMSET(buf, 0, *sz);

View File

@@ -3615,10 +3615,12 @@ int wolfSSL_send_session(WOLFSSL* ssl)
/* if no error ret has size of buffer */
ret = ssl->dtls_export(ssl, buf, ret, NULL);
if (ret != WOLFSSL_SUCCESS) {
ForceZero(buf, bufSz);
XFREE(buf, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
return ret;
}
ForceZero(buf, bufSz);
XFREE(buf, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -52,6 +52,39 @@ int test_dtls_mtu_fragment_headroom(void);
int test_dtls_mtu_split_messages(void);
int test_dtls_set_session_min_downgrade(void);
int test_dtls12_export_import_etm(void);
int test_dtls12_export_import_v6(void);
int test_dtls13_export_import(void);
int test_dtls13_export_unequal_epochs(void);
int test_dtls13_export_restrictions(void);
int test_dtls13_export_half_sent_fragment(void);
int test_dtls13_export_write_dup_pending(void);
int test_dtls13_export_pending_ticket_lost(void);
int test_dtls12_export_state_version(void);
int test_dtls13_import_state_restrictions(void);
int test_dtls_export_cid_restored(void);
int test_dtls_export_tight_buffer(void);
int test_dtls_export_no_cid_clears_target(void);
int test_dtls13_export_import_client(void);
int test_dtls13_export_replay_window(void);
int test_dtls13_export_smaller_window(void);
int test_dtls13_export_larger_window(void);
int test_dtls13_export_malformed(void);
int test_dtls13_export_epoch_numbers(void);
int test_dtls13_export_reimport(void);
int test_dtls13_export_failed_epoch_commit(void);
int test_dtls13_export_malformed_epoch_keys(void);
int test_dtls13_export_malformed_specs(void);
int test_dtls13_export_short_epoch_keys(void);
int test_dtls13_export_short_secret(void);
int test_dtls13_export_padded_state_section(void);
int test_dtls13_export_padded_cid_section(void);
int test_dtls_export_import_oversized_buffer(void);
int test_dtls13_export_sha256_suite(void);
int test_dtls13_export_integrity_only_suite(void);
int test_dtls13_export_unequal_epochs_client(void);
int test_dtls13_export_prev_peer_epoch(void);
int test_dtls13_export_invalidate_before(void);
int test_dtls13_export_drop_count(void);
int test_wolfSSL_dtls_create_free_peer(void);
int test_wolfSSL_dtls_scheduled_work(void);
int test_wolfSSL_dtls_get0_peer(void);
@@ -190,6 +223,39 @@ int test_WOLFSSL_dtls_version_alert(void);
TEST_DECL_GROUP("dtls", test_dtls_old_seq_number), \
TEST_DECL_GROUP("dtls", test_dtls12_missing_finished), \
TEST_DECL_GROUP("dtls", test_dtls12_export_import_etm), \
TEST_DECL_GROUP("dtls", test_dtls12_export_import_v6), \
TEST_DECL_GROUP("dtls", test_dtls13_export_import), \
TEST_DECL_GROUP("dtls", test_dtls13_export_unequal_epochs), \
TEST_DECL_GROUP("dtls", test_dtls13_export_restrictions), \
TEST_DECL_GROUP("dtls", test_dtls13_export_half_sent_fragment), \
TEST_DECL_GROUP("dtls", test_dtls13_export_write_dup_pending), \
TEST_DECL_GROUP("dtls", test_dtls13_export_pending_ticket_lost), \
TEST_DECL_GROUP("dtls", test_dtls12_export_state_version), \
TEST_DECL_GROUP("dtls", test_dtls13_import_state_restrictions), \
TEST_DECL_GROUP("dtls", test_dtls_export_cid_restored), \
TEST_DECL_GROUP("dtls", test_dtls_export_tight_buffer), \
TEST_DECL_GROUP("dtls", test_dtls_export_no_cid_clears_target), \
TEST_DECL_GROUP("dtls", test_dtls13_export_import_client), \
TEST_DECL_GROUP("dtls", test_dtls13_export_replay_window), \
TEST_DECL_GROUP("dtls", test_dtls13_export_smaller_window), \
TEST_DECL_GROUP("dtls", test_dtls13_export_larger_window), \
TEST_DECL_GROUP("dtls", test_dtls13_export_malformed), \
TEST_DECL_GROUP("dtls", test_dtls13_export_epoch_numbers), \
TEST_DECL_GROUP("dtls", test_dtls13_export_reimport), \
TEST_DECL_GROUP("dtls", test_dtls13_export_failed_epoch_commit), \
TEST_DECL_GROUP("dtls", test_dtls13_export_malformed_epoch_keys), \
TEST_DECL_GROUP("dtls", test_dtls13_export_malformed_specs), \
TEST_DECL_GROUP("dtls", test_dtls13_export_short_epoch_keys), \
TEST_DECL_GROUP("dtls", test_dtls13_export_short_secret), \
TEST_DECL_GROUP("dtls", test_dtls13_export_padded_state_section), \
TEST_DECL_GROUP("dtls", test_dtls13_export_padded_cid_section), \
TEST_DECL_GROUP("dtls", test_dtls_export_import_oversized_buffer), \
TEST_DECL_GROUP("dtls", test_dtls13_export_sha256_suite), \
TEST_DECL_GROUP("dtls", test_dtls13_export_integrity_only_suite), \
TEST_DECL_GROUP("dtls", test_dtls13_export_unequal_epochs_client), \
TEST_DECL_GROUP("dtls", test_dtls13_export_prev_peer_epoch), \
TEST_DECL_GROUP("dtls", test_dtls13_export_invalidate_before), \
TEST_DECL_GROUP("dtls", test_dtls13_export_drop_count), \
TEST_DECL_GROUP("dtls", test_dtls13_min_rtx_interval), \
TEST_DECL_GROUP("dtls", test_dtls13_no_session_id_echo), \
TEST_DECL_GROUP("dtls", test_dtls_set_session_min_downgrade), \

View File

@@ -1403,6 +1403,9 @@ enum {
#error "Max size for DTLS CID is 255 bytes"
#endif
/* the tx CID is the peer's choice, bounded only by its one byte length field */
#define DTLS_CID_MAX_TX_SIZE 255
/* Record Payload Protection Section 5
* https://www.rfc-editor.org/rfc/rfc9146.html#section-5 */
#define WOLFSSL_TLS_HMAC_CID_INNER_SZ \
@@ -1660,7 +1663,6 @@ enum Misc {
TLS_EXPORT_OPT_SZ_5 = 66, /* number of bytes used from Options */
TLS_EXPORT_OPT_SZ_4 = 65, /* number of bytes used from Options */
DTLS_EXPORT_OPT_SZ_3 = 60, /* number of bytes used from Options */
DTLS_EXPORT_KEY_SZ = 325 + (DTLS_SEQ_SZ * 2),
/* max number of bytes used from Keys */
DTLS_EXPORT_MIN_KEY_SZ = 85 + (DTLS_SEQ_SZ * 2),
/* min number of bytes used from Keys */
@@ -1670,9 +1672,10 @@ enum Misc {
WOLFSSL_EXPORT_SPC_SZ = 16, /* number of bytes used from CipherSpecs */
#endif
WOLFSSL_EXPORT_LEN = 2, /* 2 bytes for length and protocol */
WOLFSSL_EXPORT_VERSION = 6, /* wolfSSL version for serialized session */
WOLFSSL_EXPORT_VERSION = 7, /* wolfSSL version for serialized session */
/* older export versions supported */
WOLFSSL_EXPORT_VERSION_6 = 6, /* version before DTLS 1.3 state */
WOLFSSL_EXPORT_VERSION_5 = 5, /* version before DTLS Encrypt-Then-MAC */
WOLFSSL_EXPORT_VERSION_4 = 4, /* 5.6.4 release and before */
WOLFSSL_EXPORT_VERSION_3 = 3, /* wolfSSL version before TLS 1.3 addition */
@@ -1682,7 +1685,6 @@ enum Misc {
/* Additional bytes to read so that
* we can work with a peer that has
* a slightly different MTU than us. */
MAX_EXPORT_BUFFER = 514, /* max size of buffer for exporting */
MAX_EXPORT_STATE_BUFFER = (DTLS_EXPORT_MIN_KEY_SZ) + (3 * WOLFSSL_EXPORT_LEN),
/* max size of buffer for exporting state */
FINISHED_LABEL_SZ = 15, /* TLS finished label size */
@@ -3027,6 +3029,65 @@ WOLFSSL_LOCAL socklen_t wolfSSL_BIO_ADDR_size(const WOLFSSL_BIO_ADDR *addr);
#define MAX_WRITE_IV_SZ 16 /* max size of client/server write_IV */
#endif
/* worst case serialized size of what ExportKeyState() writes */
#define DTLS_EXPORT_KEY_SZ \
((4 * OPAQUE32_LEN) + /* TLS sequence numbers */ \
42 + /* DTLS record layer counters */ \
(2 * OPAQUE32_LEN) + (2 * OPAQUE8_LEN) + /* encryptSz, padSz, flags */ \
(2 * (OPAQUE16_LEN + DTLS_SEQ_SZ)) + /* window and prevWindow */ \
OPAQUE8_LEN + /* truncated hmac flag */ \
(OPAQUE8_LEN + (2 * WC_MAX_DIGEST_SIZE)) + /* MAC secrets */ \
(OPAQUE8_LEN + (2 * MAX_SYM_KEY_SIZE)) + /* write keys */ \
(OPAQUE8_LEN + (2 * MAX_WRITE_IV_SZ) + AEAD_MAX_EXP_SZ) + /* IVs */ \
(OPAQUE8_LEN + (2 * AEAD_MAX_IMP_SZ))) /* implicit AEAD IVs */
#if defined(WOLFSSL_SESSION_EXPORT) && defined(WOLFSSL_DTLS13)
/* epoch number, send/peer sequence numbers, failed decryption count and
* replay window; key material is derived again on import */
#define DTLS_EXPORT_DTLS13_EPOCH_SZ \
((4 * OPAQUE64_LEN) + OPAQUE16_LEN + \
(WOLFSSL_DTLS_WINDOW_WORDS * OPAQUE32_LEN))
/* key material appended to the previous peer epoch: write key, record number
* key and write IV of the direction the peer sends in, each with its length */
#define DTLS_EXPORT_DTLS13_EPOCH_KEY_SZ \
((2 * OPAQUE8_LEN) + (2 * MAX_SYM_KEY_SIZE) + MAX_WRITE_IV_SZ)
/* epoch numbers, traffic secrets and three length prefixed epoch fields */
#define DTLS_EXPORT_DTLS13_SZ \
((3 * OPAQUE64_LEN) + OPAQUE8_LEN + (2 * SECRET_LEN) + \
(3 * WOLFSSL_EXPORT_LEN) + \
(3 * DTLS_EXPORT_DTLS13_EPOCH_SZ) + DTLS_EXPORT_DTLS13_EPOCH_KEY_SZ)
#endif /* WOLFSSL_SESSION_EXPORT && WOLFSSL_DTLS13 */
#if defined(WOLFSSL_SESSION_EXPORT) && defined(WOLFSSL_DTLS_CID)
/* negotiated flag plus the length prefixed rx and tx connection ids */
#define DTLS_EXPORT_CID_SZ \
(OPAQUE8_LEN + (OPAQUE8_LEN + DTLS_CID_MAX_SIZE) + \
(OPAQUE8_LEN + DTLS_CID_MAX_TX_SIZE))
#define DTLS_EXPORT_CID_TOTAL (WOLFSSL_EXPORT_LEN + DTLS_EXPORT_CID_SZ)
#elif defined(WOLFSSL_SESSION_EXPORT)
/* the length prefix is written whether or not CID is compiled in */
#define DTLS_EXPORT_CID_TOTAL WOLFSSL_EXPORT_LEN
#else
#define DTLS_EXPORT_CID_TOTAL 0
#endif /* WOLFSSL_SESSION_EXPORT && WOLFSSL_DTLS_CID */
/* protocol and total length bytes, then the length prefixed options, key
* state, cipher specs and peer address */
#define DTLS_EXPORT_BASE_SZ \
((2 * WOLFSSL_EXPORT_LEN) + \
(WOLFSSL_EXPORT_LEN + DTLS_EXPORT_OPT_SZ) + \
(WOLFSSL_EXPORT_LEN + DTLS_EXPORT_KEY_SZ) + \
(WOLFSSL_EXPORT_LEN + WOLFSSL_EXPORT_SPC_SZ) + \
(WOLFSSL_EXPORT_LEN + MAX_EXPORT_IP + (3 * WOLFSSL_EXPORT_LEN)))
/* max size of buffer for exporting */
#if defined(WOLFSSL_SESSION_EXPORT) && defined(WOLFSSL_DTLS13)
#define MAX_EXPORT_BUFFER (DTLS_EXPORT_BASE_SZ + WOLFSSL_EXPORT_LEN + \
DTLS_EXPORT_DTLS13_SZ + DTLS_EXPORT_CID_TOTAL)
#else
#define MAX_EXPORT_BUFFER (DTLS_EXPORT_BASE_SZ + DTLS_EXPORT_CID_TOTAL)
#endif
/* keys and secrets
* keep as a constant size (no additional ifdefs) for session export */
typedef struct Keys {
@@ -4114,6 +4175,11 @@ WOLFSSL_LOCAL int Dtls13UnifiedHeaderCIDPresent(byte flags);
#endif /* WOLFSSL_DTLS_CID */
WOLFSSL_LOCAL byte DtlsGetCidTxSize(WOLFSSL* ssl);
WOLFSSL_LOCAL byte DtlsGetCidRxSize(WOLFSSL* ssl);
#ifdef WOLFSSL_SESSION_EXPORT
WOLFSSL_LOCAL int DtlsCidExport(WOLFSSL* ssl, byte* exp, word32 len);
WOLFSSL_LOCAL int DtlsCidImport(WOLFSSL* ssl, const byte* exp, word32 len);
WOLFSSL_LOCAL void DtlsCidClear(WOLFSSL* ssl);
#endif
#ifdef OPENSSL_EXTRA
enum SetCBIO {
@@ -7791,6 +7857,10 @@ WOLFSSL_TEST_VIS int Dtls13DoScheduledWork(WOLFSSL* ssl);
WOLFSSL_LOCAL int Dtls13DeriveSnKeys(WOLFSSL* ssl, int provision);
WOLFSSL_LOCAL int Dtls13SetRecordNumberKeys(WOLFSSL* ssl,
enum encrypt_side side);
#ifdef WOLFSSL_SESSION_EXPORT
WOLFSSL_LOCAL int ExportDtls13State(WOLFSSL* ssl, byte* exp, word32 len);
WOLFSSL_LOCAL int ImportDtls13State(WOLFSSL* ssl, const byte* exp, word32 len);
#endif
WOLFSSL_LOCAL int Dtls13AddHeaders(byte* output, word32 length,
enum HandShakeType hs_type, WOLFSSL* ssl);