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:
4
.github/configs/os-check-linux.json
vendored
4
.github/configs/os-check-linux.json
vendored
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
103
src/dtls.c
103
src/dtls.c
@@ -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)
|
||||
|
||||
516
src/dtls13.c
516
src/dtls13.c
@@ -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)
|
||||
|
||||
189
src/internal.c
189
src/internal.c
@@ -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);
|
||||
|
||||
@@ -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
@@ -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), \
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user