mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-08-10 14:51:20 +02:00
Merge pull request #11018 from Frauschi/fenrir_3
Security and correctness fixes, plus a DTLS 1.3 scheduled work API
This commit is contained in:
@@ -3951,6 +3951,87 @@ int wolfSSL_dtls_got_timeout(WOLFSSL* ssl);
|
||||
*/
|
||||
int wolfSSL_dtls_retransmit(WOLFSSL* ssl);
|
||||
|
||||
/*!
|
||||
\ingroup Setup
|
||||
|
||||
\brief Sends the DTLS 1.3 work that was scheduled while reading. With
|
||||
WOLFSSL_RW_THREADED the read path never transmits, because that would race
|
||||
the write thread over the output buffer and the sending key schedule, so
|
||||
ACKs, retransmissions and a KeyUpdate the peer asked for are only sent from
|
||||
the write side. An application that reads without writing must call this,
|
||||
from the same thread it uses for writing, or those messages are never sent
|
||||
and the peer keeps retransmitting what it is waiting to have acknowledged.
|
||||
Not for write-dup applications, which drain through wolfSSL_write().
|
||||
|
||||
A KeyUpdate is only sent while none of ours is still unacknowledged, since
|
||||
DTLS must not have two in flight. Completing one we started needs the
|
||||
peer's acknowledgement processed, which rotates the sending keys and so
|
||||
does not happen here, so in a WOLFSSL_RW_THREADED build a peer request
|
||||
arriving after that point is held rather than answered.
|
||||
|
||||
A send that could only write part of a record returns WOLFSSL_FATAL_ERROR
|
||||
with wolfSSL_get_error() reporting SSL_ERROR_WANT_WRITE. That is not a
|
||||
failure of the connection: the record is held and the next call sends the
|
||||
rest, so wolfSSL_dtls13_pending_work() keeps reporting work until it is
|
||||
out. Treat it as a retry rather than as a reason to stop draining.
|
||||
|
||||
\return WOLFSSL_SUCCESS on success, including when there was nothing to do.
|
||||
\return WOLFSSL_FATAL_ERROR if ssl is NULL, is not a DTLS 1.3 object, is
|
||||
part of a write-dup pair, or the send failed. Call wolfSSL_get_error() to
|
||||
tell a retryable SSL_ERROR_WANT_WRITE from a real failure.
|
||||
|
||||
\param ssl a pointer to a WOLFSSL structure, created using wolfSSL_new().
|
||||
|
||||
_Example_
|
||||
\code
|
||||
WOLFSSL* ssl;
|
||||
...
|
||||
while (wolfSSL_dtls13_pending_work(ssl)) {
|
||||
if (wolfSSL_dtls13_do_scheduled_work(ssl) != WOLFSSL_SUCCESS) {
|
||||
if (wolfSSL_get_error(ssl, 0) == SSL_ERROR_WANT_WRITE) {
|
||||
// the socket is full, wait for it and call again
|
||||
break;
|
||||
}
|
||||
// a real error
|
||||
break;
|
||||
}
|
||||
}
|
||||
\endcode
|
||||
|
||||
\sa wolfSSL_dtls13_pending_work
|
||||
\sa wolfSSL_dtls_retransmit
|
||||
*/
|
||||
int wolfSSL_dtls13_do_scheduled_work(WOLFSSL* ssl);
|
||||
|
||||
/*!
|
||||
\ingroup Setup
|
||||
|
||||
\brief Reports whether the object has DTLS 1.3 work waiting to be sent by
|
||||
wolfSSL_dtls13_do_scheduled_work(). Only meaningful with
|
||||
WOLFSSL_RW_THREADED. The answer is advisory and can change as soon as it is
|
||||
returned. Only work the pump can actually carry out is reported, so a drain
|
||||
loop over the pair terminates; waiting for the peer to acknowledge a key
|
||||
update we sent is not reported, as there is nothing to send for it. A
|
||||
record the pump could only write in part is reported, so that the retry it
|
||||
owes is not lost.
|
||||
|
||||
\return 1 if there is work to send, or if it could not be determined.
|
||||
\return 0 if there is nothing to do, or ssl is not supported here.
|
||||
|
||||
\param ssl a pointer to a WOLFSSL structure, created using wolfSSL_new().
|
||||
|
||||
_Example_
|
||||
\code
|
||||
WOLFSSL* ssl;
|
||||
...
|
||||
if (wolfSSL_dtls13_pending_work(ssl))
|
||||
wolfSSL_dtls13_do_scheduled_work(ssl);
|
||||
\endcode
|
||||
|
||||
\sa wolfSSL_dtls13_do_scheduled_work
|
||||
*/
|
||||
int wolfSSL_dtls13_pending_work(WOLFSSL* ssl);
|
||||
|
||||
/*!
|
||||
\brief This function is used to determine if the SSL session has been
|
||||
configured to use DTLS.
|
||||
|
||||
+20
@@ -66,6 +66,11 @@
|
||||
|
||||
void DtlsResetState(WOLFSSL* ssl)
|
||||
{
|
||||
#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID) && \
|
||||
defined(WOLFSSL_RW_THREADED)
|
||||
int locked;
|
||||
#endif
|
||||
|
||||
/* Reset the state so that we can statelessly await the
|
||||
* ClientHello that contains the cookie. Don't gate on IsAtLeastTLSv1_3
|
||||
* to handle the edge case when the peer wants a lower version. */
|
||||
@@ -98,6 +103,17 @@ void DtlsResetState(WOLFSSL* ssl)
|
||||
ssl->options.tls1_1 = 0;
|
||||
ssl->options.tls1_3 = 0;
|
||||
#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID)
|
||||
#ifdef WOLFSSL_RW_THREADED
|
||||
/* wolfSSL_dtls_set_pending_peer() may run on another thread, so take the
|
||||
* lock the record layer uses for these two fields before dropping what
|
||||
* that call left behind. Callers must not already hold peerLock:
|
||||
* re-acquiring a write lock is undefined and deadlocks rather than fails,
|
||||
* so the only failure this can report is a broken or uninitialised lock.
|
||||
* Clear the fields anyway in that case, since resetting the state is the
|
||||
* whole point of this call and leaving a pending peer behind is worse than
|
||||
* the race. dtlsProcessPendingPeer() and ProcessReplyEx() do the same. */
|
||||
locked = (wc_LockRwLock_Wr(&ssl->buffers.dtlsCtx.peerLock) == 0);
|
||||
#endif
|
||||
ssl->buffers.dtlsCtx.processingPendingRecord = 0;
|
||||
/* Clear the pending peer in case user set */
|
||||
XFREE(ssl->buffers.dtlsCtx.pendingPeer.sa, ssl->heap,
|
||||
@@ -105,6 +121,10 @@ void DtlsResetState(WOLFSSL* ssl)
|
||||
ssl->buffers.dtlsCtx.pendingPeer.sa = NULL;
|
||||
ssl->buffers.dtlsCtx.pendingPeer.sz = 0;
|
||||
ssl->buffers.dtlsCtx.pendingPeer.bufSz = 0;
|
||||
#ifdef WOLFSSL_RW_THREADED
|
||||
if (locked)
|
||||
(void)wc_UnLockRwLock(&ssl->buffers.dtlsCtx.peerLock);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+61
-10
@@ -22730,11 +22730,13 @@ int TimingPadVerify(WOLFSSL* ssl, const byte* input, int padLen, int macSz,
|
||||
|
||||
XMEMSET(verify, 0, WC_MAX_DIGEST_SIZE);
|
||||
good = MaskPadding(input, pLen, macSz);
|
||||
/* 4th argument has potential to underflow, ssl->hmac function should
|
||||
* either increment the size by (macSz + padLen + 1) before use or check on
|
||||
* the size to make sure is valid. */
|
||||
ret = ssl->hmac(ssl, verify, input, (word32)(pLen - macSz - padLen - 1), padLen,
|
||||
content, 1, PEER_ORDER);
|
||||
/* An out of range padding length byte is already recorded in good, but the
|
||||
* length handed to ssl->hmac must not underflow. Clamp it in constant time
|
||||
* so that the same amount of hashing is done for every value of the
|
||||
* padding length byte. */
|
||||
padLen &= ctMaskIntGTE(pLen - macSz - 1, padLen);
|
||||
ret = ssl->hmac(ssl, verify, input, (word32)(pLen - macSz - padLen - 1),
|
||||
padLen, content, 1, PEER_ORDER);
|
||||
good |= MaskMac(input, pLen, ssl->specs.hash_size, verify);
|
||||
|
||||
/* Non-zero on failure. */
|
||||
@@ -23735,6 +23737,16 @@ static int dtlsRecordIsNewest(WOLFSSL* ssl)
|
||||
*/
|
||||
static void dtlsProcessPendingPeer(WOLFSSL* ssl, int deprotected, int isNewest)
|
||||
{
|
||||
#ifdef WOLFSSL_RW_THREADED
|
||||
int locked;
|
||||
|
||||
/* A failure here means the lock itself is broken, not that another holder
|
||||
* is in the way, so carry on regardless: this bookkeeping is the caller's
|
||||
* own and leaving it stale is worse than the race. Only the promotion into
|
||||
* dtlsCtx.peer below is skipped, since EmbedSendTo reads that buffer
|
||||
* without the lock. DtlsResetState() and ProcessReplyEx() do the same. */
|
||||
locked = (wc_LockRwLock_Wr(&ssl->buffers.dtlsCtx.peerLock) == 0);
|
||||
#endif
|
||||
if (ssl->buffers.dtlsCtx.pendingPeer.sa != NULL) {
|
||||
if (!deprotected) {
|
||||
/* Here we have just read an entire record from the network. It is
|
||||
@@ -23750,11 +23762,30 @@ static void dtlsProcessPendingPeer(WOLFSSL* ssl, int deprotected, int isNewest)
|
||||
!ssl->buffers.dtlsCtx.processingPendingRecord;
|
||||
}
|
||||
else {
|
||||
/* Pending peer present and record deprotected. Update the peer. */
|
||||
if (isNewest) {
|
||||
(void)wolfSSL_dtls_set_peer(ssl,
|
||||
ssl->buffers.dtlsCtx.pendingPeer.sa,
|
||||
ssl->buffers.dtlsCtx.pendingPeer.sz);
|
||||
/* Pending peer present and record deprotected. Promote it here
|
||||
* rather than through wolfSSL_dtls_set_peer, which would take this
|
||||
* same lock again. */
|
||||
if (isNewest
|
||||
#ifdef WOLFSSL_RW_THREADED
|
||||
/* Only this touches the buffer the send path reads
|
||||
* without the lock, so it is the one thing a failed
|
||||
* acquisition has to skip. */
|
||||
&& locked
|
||||
#endif
|
||||
) {
|
||||
WOLFSSL_SOCKADDR* from = &ssl->buffers.dtlsCtx.pendingPeer;
|
||||
|
||||
if (wolfssl_local_SockAddrSet(&ssl->buffers.dtlsCtx.peer,
|
||||
from->sa, from->sz, ssl->heap) == WOLFSSL_SUCCESS) {
|
||||
ssl->buffers.dtlsCtx.userSet = 1;
|
||||
}
|
||||
else {
|
||||
/* wolfSSL_dtls_set_peer clears this when it fails to store
|
||||
* a peer, and the receive path only checks the sender
|
||||
* while peer.sz is non zero, so leaving it set would drop
|
||||
* the check rather than fail safe. */
|
||||
ssl->buffers.dtlsCtx.userSet = 0;
|
||||
}
|
||||
}
|
||||
ssl->buffers.dtlsCtx.processingPendingRecord = 0;
|
||||
dtlsClearPeer(&ssl->buffers.dtlsCtx.pendingPeer);
|
||||
@@ -23763,6 +23794,10 @@ static void dtlsProcessPendingPeer(WOLFSSL* ssl, int deprotected, int isNewest)
|
||||
else {
|
||||
ssl->buffers.dtlsCtx.processingPendingRecord = 0;
|
||||
}
|
||||
#ifdef WOLFSSL_RW_THREADED
|
||||
if (locked)
|
||||
(void)wc_UnLockRwLock(&ssl->buffers.dtlsCtx.peerLock);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
static int DoDecrypt(WOLFSSL *ssl)
|
||||
@@ -25102,6 +25137,10 @@ int ProcessReply(WOLFSSL* ssl)
|
||||
int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr)
|
||||
{
|
||||
int ret;
|
||||
#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID) && \
|
||||
defined(WOLFSSL_RW_THREADED)
|
||||
int locked;
|
||||
#endif
|
||||
|
||||
ret = DoProcessReplyEx(ssl, allowSocketErr);
|
||||
|
||||
@@ -25114,8 +25153,20 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr)
|
||||
&& ret != WC_NO_ERR_TRACE(WC_PENDING_E)
|
||||
#endif
|
||||
) {
|
||||
#ifdef WOLFSSL_RW_THREADED
|
||||
/* Drop the pending peer even when the lock cannot be taken, as
|
||||
* DtlsResetState() and dtlsProcessPendingPeer() do. A failure here
|
||||
* means the lock is broken rather than held, and nothing below
|
||||
* touches dtlsCtx.peer, which is the buffer the send path reads
|
||||
* without this lock. */
|
||||
locked = (wc_LockRwLock_Wr(&ssl->buffers.dtlsCtx.peerLock) == 0);
|
||||
#endif
|
||||
dtlsClearPeer(&ssl->buffers.dtlsCtx.pendingPeer);
|
||||
ssl->buffers.dtlsCtx.processingPendingRecord = 0;
|
||||
#ifdef WOLFSSL_RW_THREADED
|
||||
if (locked)
|
||||
(void)wc_UnLockRwLock(&ssl->buffers.dtlsCtx.peerLock);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5679,7 +5679,94 @@ size_t wolfSSL_get_client_random(const WOLFSSL* ssl, unsigned char* out,
|
||||
ssl->buffers.inputBuffer.bufferSize);
|
||||
#endif
|
||||
}
|
||||
ssl->keys.encryptionOn = 0;
|
||||
/* Recycling the object for a new connection must not carry the
|
||||
* previous connection's key material along with it. A freshly
|
||||
* created object has all of this zeroed. */
|
||||
ForceZero(&ssl->keys, sizeof(Keys));
|
||||
#ifdef WOLFSSL_MULTICAST
|
||||
if (ssl->options.haveMcast) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < WOLFSSL_DTLS_PEERSEQ_SZ; i++)
|
||||
ssl->keys.peerSeq[i].peerId = INVALID_PEER_ID;
|
||||
}
|
||||
#endif
|
||||
#ifdef WOLFSSL_TLS13
|
||||
ForceZero(ssl->clientSecret, sizeof(ssl->clientSecret));
|
||||
ForceZero(ssl->serverSecret, sizeof(ssl->serverSecret));
|
||||
/* A key update the previous connection asked for has nothing to say
|
||||
* about the next one, and the keys it would rotate are gone. */
|
||||
ssl->options.sendKeyUpdate = 0;
|
||||
#endif
|
||||
#ifdef WOLFSSL_HAVE_TLS_UNIQUE
|
||||
/* The channel binding of the connection that just ended. Leaving it
|
||||
* in place would let the next caller bind to the wrong session. */
|
||||
ForceZero(ssl->clientFinished, TLS_FINISHED_SZ_MAX);
|
||||
ForceZero(ssl->serverFinished, TLS_FINISHED_SZ_MAX);
|
||||
ssl->clientFinished_len = 0;
|
||||
ssl->serverFinished_len = 0;
|
||||
#endif
|
||||
#ifdef WOLFSSL_DTLS13
|
||||
/* Per-epoch traffic keys, IVs and sequence number keys. */
|
||||
ForceZero(ssl->dtls13Epochs, sizeof(ssl->dtls13Epochs));
|
||||
/* Only InitSSL() sets up the unprotected epoch 0 and aims the epoch
|
||||
* pointers at it, and this object is being reused rather than freed,
|
||||
* so put it back or the next handshake has no valid epoch. */
|
||||
ssl->dtls13Epochs[0].isValid = 1;
|
||||
ssl->dtls13Epochs[0].side = ENCRYPT_AND_DECRYPT_SIDE;
|
||||
ssl->dtls13EncryptEpoch = &ssl->dtls13Epochs[0];
|
||||
ssl->dtls13DecryptEpoch = &ssl->dtls13Epochs[0];
|
||||
/* The numbers that say which epoch to use sit outside the table and
|
||||
* only ever move up, so wiping the table has to be matched here by
|
||||
* hand. InitSSL() gets this for free from clearing the whole object.
|
||||
* Left behind, they ask the next handshake to send under an epoch the
|
||||
* table no longer holds, and Dtls13SetEpochKeys() fails the connection
|
||||
* with BAD_STATE_E. */
|
||||
w64Zero(&ssl->dtls13Epoch);
|
||||
w64Zero(&ssl->dtls13PeerEpoch);
|
||||
w64Zero(&ssl->dtls13InvalidateBefore);
|
||||
ssl->dtls13WaitKeyUpdateAck = 0;
|
||||
ssl->dtls13DoKeyUpdate = 0;
|
||||
ssl->dtls13SendingAckOrRtx = 0;
|
||||
/* Anything still queued for retransmission or acknowledgement is
|
||||
* tagged with an epoch that no longer exists, so it can never be sent
|
||||
* and would only be released when the object is freed. */
|
||||
Dtls13FreeFsmResources(ssl);
|
||||
ssl->dtls13Rtx.sendAcks = 0;
|
||||
ssl->dtls13Rtx.retransmit = 0;
|
||||
#endif
|
||||
/* The handshake arrays hold the master and pre-master secrets. Wipe
|
||||
* those in place rather than releasing the arrays, so that the
|
||||
* allocation stays valid for wolfSSL_set_secret(), the exporter and
|
||||
* the other accessors that read from it once a connection has ended.
|
||||
* An application that asked to keep the arrays still gets back
|
||||
* everything the API can hand it, so only the rest goes. */
|
||||
if (ssl->arrays != NULL) {
|
||||
if (ssl->arrays->preMasterSecret != NULL) {
|
||||
ForceZero(ssl->arrays->preMasterSecret, ENCRYPT_LEN);
|
||||
/* The key agreement routines take this as the size of the
|
||||
* buffer they may write, so put back what a freshly
|
||||
* allocated Arrays would carry. */
|
||||
ssl->arrays->preMasterSz = ENCRYPT_LEN;
|
||||
}
|
||||
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
|
||||
ForceZero(ssl->arrays->psk_key, MAX_PSK_KEY_LEN);
|
||||
ssl->arrays->psk_keySz = 0;
|
||||
#endif
|
||||
#ifdef WOLFSSL_TLS13
|
||||
/* The key schedule secret. No API hands this one back. */
|
||||
ForceZero(ssl->arrays->secret, SECRET_LEN);
|
||||
#endif
|
||||
if (!ssl->options.saveArrays) {
|
||||
ForceZero(ssl->arrays->masterSecret, SECRET_LEN);
|
||||
#ifdef HAVE_KEYING_MATERIAL
|
||||
/* Tls13_Exporter() reads this one, and exporting keying
|
||||
* material requires the arrays to be kept, so it only goes
|
||||
* when the application did not ask for that. */
|
||||
ForceZero(ssl->arrays->exporterSecret, WC_MAX_DIGEST_SIZE);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
XMEMSET(&ssl->msgsReceived, 0, sizeof(ssl->msgsReceived));
|
||||
|
||||
/* Discard any partial handshake-message reassembly on reuse. */
|
||||
|
||||
+242
-7
@@ -115,7 +115,13 @@ int wolfSSL_dtls_free_peer(void* addr)
|
||||
#ifdef WOLFSSL_DTLS
|
||||
/* Store a socket address into a socket address holder, resizing as needed.
|
||||
*
|
||||
* A NULL or zero-length peer frees the holder's buffer.
|
||||
* A NULL or zero-length peer frees the holder's buffer. An address that fits
|
||||
* the buffer already there is copied over it rather than reallocated:
|
||||
* EmbedSendTo reads peer.sa without taking peerLock, so freeing it on every
|
||||
* update would widen that race rather than leave it as it is.
|
||||
*
|
||||
* The record layer promotes a pending peer through this while already holding
|
||||
* peerLock, so it takes no lock of its own.
|
||||
*
|
||||
* @param [in, out] sockAddr Socket address holder.
|
||||
* @param [in] peer Socket address data, may be NULL to free.
|
||||
@@ -124,8 +130,8 @@ int wolfSSL_dtls_free_peer(void* addr)
|
||||
* @return WOLFSSL_SUCCESS on success.
|
||||
* @return WOLFSSL_FAILURE on allocation error.
|
||||
*/
|
||||
static int SockAddrSet(WOLFSSL_SOCKADDR* sockAddr, void* peer,
|
||||
unsigned int peerSz, void* heap)
|
||||
int wolfssl_local_SockAddrSet(WOLFSSL_SOCKADDR* sockAddr, void* peer,
|
||||
unsigned int peerSz, void* heap)
|
||||
{
|
||||
if (peer == NULL || peerSz == 0) {
|
||||
if (sockAddr->sa != NULL)
|
||||
@@ -174,7 +180,8 @@ int wolfSSL_dtls_set_peer(WOLFSSL* ssl, void* peer, unsigned int peerSz)
|
||||
if (wc_LockRwLock_Wr(&ssl->buffers.dtlsCtx.peerLock) != 0)
|
||||
return WOLFSSL_FAILURE;
|
||||
#endif
|
||||
ret = SockAddrSet(&ssl->buffers.dtlsCtx.peer, peer, peerSz, ssl->heap);
|
||||
ret = wolfssl_local_SockAddrSet(&ssl->buffers.dtlsCtx.peer, peer, peerSz,
|
||||
ssl->heap);
|
||||
if (ret == WOLFSSL_SUCCESS && !(peer == NULL || peerSz == 0))
|
||||
ssl->buffers.dtlsCtx.userSet = 1;
|
||||
else
|
||||
@@ -212,7 +219,7 @@ int wolfSSL_dtls_set_pending_peer(WOLFSSL* ssl, void* peer, unsigned int peerSz)
|
||||
if (ssl == NULL)
|
||||
return WOLFSSL_FAILURE;
|
||||
#ifdef WOLFSSL_RW_THREADED
|
||||
if (wc_LockRwLock_Rd(&ssl->buffers.dtlsCtx.peerLock) != 0)
|
||||
if (wc_LockRwLock_Wr(&ssl->buffers.dtlsCtx.peerLock) != 0)
|
||||
return WOLFSSL_FAILURE;
|
||||
#endif
|
||||
if (ssl->buffers.dtlsCtx.peer.sa != NULL &&
|
||||
@@ -232,8 +239,8 @@ int wolfSSL_dtls_set_pending_peer(WOLFSSL* ssl, void* peer, unsigned int peerSz)
|
||||
ret = WOLFSSL_SUCCESS;
|
||||
}
|
||||
else {
|
||||
ret = SockAddrSet(&ssl->buffers.dtlsCtx.pendingPeer, peer, peerSz,
|
||||
ssl->heap);
|
||||
ret = wolfssl_local_SockAddrSet(&ssl->buffers.dtlsCtx.pendingPeer,
|
||||
peer, peerSz, ssl->heap);
|
||||
}
|
||||
if (ret == WOLFSSL_SUCCESS)
|
||||
ssl->buffers.dtlsCtx.processingPendingRecord = 0;
|
||||
@@ -771,6 +778,13 @@ int wolfSSL_set_secret(WOLFSSL* ssl, word16 epoch,
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
/* The handshake arrays are released once the handshake resources are
|
||||
* freed, so a reused object may not have them any more. */
|
||||
if (ret == 0 && ssl->arrays == NULL) {
|
||||
WOLFSSL_MSG("Handshake arrays not available");
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
if (ret == 0 && ssl->arrays->preMasterSecret == NULL) {
|
||||
ssl->arrays->preMasterSz = ENCRYPT_LEN;
|
||||
ssl->arrays->preMasterSecret = (byte*)XMALLOC(ENCRYPT_LEN, ssl->heap,
|
||||
@@ -1355,6 +1369,227 @@ int wolfSSL_dtls_retransmit(WOLFSSL* ssl)
|
||||
return WOLFSSL_SUCCESS;
|
||||
}
|
||||
|
||||
#ifdef WOLFSSL_DTLS13
|
||||
/* Is this object the kind the scheduled-work API operates on?
|
||||
*
|
||||
* A write-dup pair parks the read side's scheduled work in the shared WriteDup
|
||||
* struct, and only wolfSSL_write() reconciles it back onto the write side.
|
||||
* Such applications already have a working drain and are deliberately out of
|
||||
* scope here, on either side of the pair.
|
||||
*/
|
||||
static int Dtls13ScheduledWorkObject(WOLFSSL* ssl)
|
||||
{
|
||||
if (!ssl->options.dtls || !IsAtLeastTLSv1_3(ssl->version))
|
||||
return 0;
|
||||
#ifdef HAVE_WRITE_DUP
|
||||
if (ssl->dupWrite != NULL)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Is there any point running the scheduled work now?
|
||||
*
|
||||
* While the handshake runs, wolfSSL_connect()/wolfSSL_accept() and
|
||||
* wolfSSL_dtls_retransmit() already drive it. Kept separate from the object
|
||||
* check above so the pump can tell a caller error, which it reports, from
|
||||
* simply having nothing to do yet, which it does not.
|
||||
*/
|
||||
static int Dtls13ScheduledWorkReady(WOLFSSL* ssl)
|
||||
{
|
||||
return Dtls13ScheduledWorkObject(ssl) && ssl->options.handShakeDone;
|
||||
}
|
||||
|
||||
/* Can a key update be sent right now?
|
||||
*
|
||||
* DTLS must not have two in flight, so not while one of ours is still
|
||||
* unacknowledged. This governs both an update we scheduled ourselves and
|
||||
* answering one the peer asked for, because Tls13UpdateKeys() silently drops
|
||||
* the former in that state. Both entry points ask this same question: the
|
||||
* predicate must not report work the pump then declines or discards, or a
|
||||
* drain loop over the pair never terminates and the caller is misled about
|
||||
* what was done.
|
||||
*/
|
||||
static int Dtls13CanSendKeyUpdate(WOLFSSL* ssl)
|
||||
{
|
||||
return !ssl->dtls13WaitKeyUpdateAck;
|
||||
}
|
||||
|
||||
/* Check whether the object has DTLS 1.3 work waiting to be sent.
|
||||
*
|
||||
* Only meaningful with WOLFSSL_RW_THREADED, where the read path does not
|
||||
* transmit. The answer is advisory: it can change as soon as it is returned,
|
||||
* and wolfSSL_dtls13_do_scheduled_work() is safe to call regardless.
|
||||
*
|
||||
* Reports only work that wolfSSL_dtls13_do_scheduled_work() can carry out, so
|
||||
* that a drain loop over the pair terminates.
|
||||
*
|
||||
* @param [in] ssl SSL/TLS object.
|
||||
* @return 1 when there is work to send, or when it could not be determined.
|
||||
* @return 0 when there is nothing to do, or ssl is not supported here.
|
||||
*/
|
||||
int wolfSSL_dtls13_pending_work(WOLFSSL* ssl)
|
||||
{
|
||||
int pending = 0;
|
||||
|
||||
WOLFSSL_ENTER("wolfSSL_dtls13_pending_work");
|
||||
|
||||
if (ssl == NULL || !Dtls13ScheduledWorkReady(ssl))
|
||||
return 0;
|
||||
|
||||
/* A record this API built but could only write in part. The pump retries
|
||||
* it, so a drain loop has to be told the retry is still owed. */
|
||||
if (ssl->buffers.outputBuffer.length > 0 && ssl->dtls13SendingAckOrRtx)
|
||||
pending = 1;
|
||||
|
||||
/* dtls13WaitKeyUpdateAck deliberately does not count as work: it says we
|
||||
* are waiting on the peer, not that we have anything to send. */
|
||||
if (!pending && (ssl->dtls13DoKeyUpdate || ssl->options.sendKeyUpdate) &&
|
||||
Dtls13CanSendKeyUpdate(ssl)) {
|
||||
pending = 1;
|
||||
}
|
||||
|
||||
if (!pending) {
|
||||
#ifdef WOLFSSL_RW_THREADED
|
||||
if (wc_LockMutex(&ssl->dtls13Rtx.mutex) != 0) {
|
||||
/* Report work rather than nothing, so a drain loop calls the pump
|
||||
* and surfaces the error instead of stopping silently. */
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
pending = ssl->dtls13Rtx.sendAcks || ssl->dtls13Rtx.retransmit;
|
||||
#ifdef WOLFSSL_RW_THREADED
|
||||
(void)wc_UnLockMutex(&ssl->dtls13Rtx.mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
WOLFSSL_LEAVE("wolfSSL_dtls13_pending_work", pending);
|
||||
|
||||
return pending;
|
||||
}
|
||||
|
||||
/* Send any DTLS 1.3 work that was scheduled while reading.
|
||||
*
|
||||
* Covers pending ACKs, retransmissions and key updates, including the
|
||||
* KeyUpdate response a peer asked for. With WOLFSSL_RW_THREADED the read path
|
||||
* never transmits, because doing so would race the write thread over the
|
||||
* output buffer and the sending key schedule, so this work is only performed
|
||||
* from the write side. An application that reads without writing has to call
|
||||
* this or those messages are never sent, and the peer keeps retransmitting
|
||||
* what it is waiting to have acknowledged.
|
||||
*
|
||||
* Call it from the same thread used for writing. Calling it concurrently with
|
||||
* a write on another thread has the same effect as two concurrent writes.
|
||||
*
|
||||
* Not for write-dup applications: those park the read side's work in the
|
||||
* shared WriteDup struct, which only wolfSSL_write() reconciles, so they
|
||||
* already have a drain and get WOLFSSL_FATAL_ERROR here rather than a call
|
||||
* that quietly does nothing.
|
||||
*
|
||||
* Note that a key update started from our own side still needs the peer's ACK
|
||||
* to be processed before it completes, and that processing rotates the sending
|
||||
* keys, so it cannot run here.
|
||||
*
|
||||
* A send that only wrote part of a record reports WANT_WRITE through
|
||||
* wolfSSL_get_error(). The record is held and the next call sends the rest,
|
||||
* so that is a retry rather than a reason to stop draining.
|
||||
*
|
||||
* @param [in] ssl SSL/TLS object.
|
||||
* @return WOLFSSL_SUCCESS on success, including when there was nothing to do.
|
||||
* @return WOLFSSL_FATAL_ERROR when ssl is NULL, unsupported, or on error.
|
||||
*/
|
||||
int wolfSSL_dtls13_do_scheduled_work(WOLFSSL* ssl)
|
||||
{
|
||||
int ret;
|
||||
|
||||
WOLFSSL_ENTER("wolfSSL_dtls13_do_scheduled_work");
|
||||
|
||||
if (ssl == NULL)
|
||||
return WOLFSSL_FATAL_ERROR;
|
||||
|
||||
/* Rejecting the object is a usage error, not something that happened to
|
||||
* the connection, so leave ssl->error alone. It is sticky: SendData()
|
||||
* only clears it for WANT_WRITE, WC_PENDING_E and the DTLS MAC/decrypt
|
||||
* cases, and wolfSSL_write() skips the write-dup drain entirely while it
|
||||
* is set, so recording one here would disable the very drain a write-dup
|
||||
* application relies on. */
|
||||
#ifdef HAVE_WRITE_DUP
|
||||
if (ssl->dupWrite != NULL) {
|
||||
WOLFSSL_MSG("Write dup objects drain through wolfSSL_write");
|
||||
WOLFSSL_LEAVE("wolfSSL_dtls13_do_scheduled_work", WOLFSSL_FATAL_ERROR);
|
||||
return WOLFSSL_FATAL_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!Dtls13ScheduledWorkObject(ssl)) {
|
||||
WOLFSSL_MSG("Not a DTLS 1.3 object this API handles");
|
||||
WOLFSSL_LEAVE("wolfSSL_dtls13_do_scheduled_work", WOLFSSL_FATAL_ERROR);
|
||||
return WOLFSSL_FATAL_ERROR;
|
||||
}
|
||||
|
||||
if (!Dtls13ScheduledWorkReady(ssl))
|
||||
return WOLFSSL_SUCCESS;
|
||||
|
||||
/* An earlier call may have left a record only partly written. Retry it
|
||||
* first: the request that produced it has already been consumed, so
|
||||
* nothing else would send it, and every other caller of
|
||||
* Dtls13DoScheduledWork() pairs it with this same flush. Only a record
|
||||
* this API built is retried here, which is what dtls13SendingAckOrRtx
|
||||
* marks, so a partly written application record is left to the
|
||||
* wolfSSL_write() the caller has to repeat anyway. */
|
||||
if (ssl->buffers.outputBuffer.length > 0 && ssl->dtls13SendingAckOrRtx) {
|
||||
ret = SendBuffered(ssl);
|
||||
if (ret != 0) {
|
||||
ssl->error = ret;
|
||||
WOLFSSL_ERROR(ret);
|
||||
return WOLFSSL_FATAL_ERROR;
|
||||
}
|
||||
ssl->dtls13SendingAckOrRtx = 0;
|
||||
}
|
||||
|
||||
ret = Dtls13DoScheduledWork(ssl);
|
||||
if (ret < 0) {
|
||||
ssl->error = ret;
|
||||
WOLFSSL_ERROR(ret);
|
||||
return WOLFSSL_FATAL_ERROR;
|
||||
}
|
||||
|
||||
/* Answer a KeyUpdate the peer requested. The read path defers this the
|
||||
* same way, and SendData() is otherwise the only thing that clears it.
|
||||
* Skip it while one of ours is still unacknowledged: DTLS must not have
|
||||
* two KeyUpdates in flight, and Dtls13DoScheduledWork() above may have
|
||||
* just started one. */
|
||||
if (ssl->options.sendKeyUpdate && Dtls13CanSendKeyUpdate(ssl)) {
|
||||
/* Drop the request before sending, as SendData() does. DTLS commits
|
||||
* the record to the retransmit queue and consumes the handshake
|
||||
* number on WANT_WRITE as well, and sets dtls13WaitKeyUpdateAck
|
||||
* unconditionally, so the update is under way from that point on.
|
||||
* Leaving the request set would send a second one once the peer
|
||||
* acknowledges the first. */
|
||||
ssl->options.sendKeyUpdate = 0;
|
||||
/* Mark the record as ours so a short write is retried by the flush
|
||||
* above rather than waiting for a retransmission timer. */
|
||||
ssl->dtls13SendingAckOrRtx = 1;
|
||||
ret = SendTls13KeyUpdate(ssl);
|
||||
if (ret != 0) {
|
||||
/* Keep the mark only while there is something left to send, so a
|
||||
* failure that wrote nothing does not leave it standing. */
|
||||
ssl->dtls13SendingAckOrRtx =
|
||||
(ssl->buffers.outputBuffer.length > 0);
|
||||
ssl->error = ret;
|
||||
WOLFSSL_ERROR(ret);
|
||||
return WOLFSSL_FATAL_ERROR;
|
||||
}
|
||||
ssl->dtls13SendingAckOrRtx = 0;
|
||||
}
|
||||
|
||||
WOLFSSL_LEAVE("wolfSSL_dtls13_do_scheduled_work", WOLFSSL_SUCCESS);
|
||||
|
||||
return WOLFSSL_SUCCESS;
|
||||
}
|
||||
#endif /* WOLFSSL_DTLS13 */
|
||||
|
||||
#endif /* DTLS */
|
||||
#endif /* LEANPSK */
|
||||
|
||||
|
||||
+1
-1
@@ -1879,7 +1879,7 @@ static int wolfSSL_TicketKeyCb(WOLFSSL* ssl,
|
||||
}
|
||||
}
|
||||
|
||||
(void)wc_HmacFree(&hmacCtx.hmac);
|
||||
wolfSSL_HMAC_CTX_cleanup(&hmacCtx);
|
||||
}
|
||||
(void)wolfSSL_EVP_CIPHER_CTX_cleanup(evpCtx);
|
||||
WC_FREE_VAR_EX(evpCtx, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
|
||||
@@ -1845,6 +1845,18 @@ int wolfSSL_HMAC_Init(WOLFSSL_HMAC_CTX* ctx, const void* key, int keylen,
|
||||
WOLFSSL_MSG("hmac set key error");
|
||||
WOLFSSL_ERROR(rc);
|
||||
wc_HmacFree(&ctx->hmac);
|
||||
/* Context is no longer keyed, so drop any previous key's pads
|
||||
* and mark it unkeyed so a later init with no key is rejected
|
||||
* rather than recovering from the pads just wiped. */
|
||||
ForceZero(ctx->save_ipad, sizeof(ctx->save_ipad));
|
||||
ForceZero(ctx->save_opad, sizeof(ctx->save_opad));
|
||||
/* As in wolfSSL_HMAC_cleanup, the hash type in the wolfSSL HMAC
|
||||
* object has to say unkeyed as well. wc_HmacFree only zeroes it,
|
||||
* which does not read as none in every hash type enum layout.
|
||||
* That alone gates the recover-from-pads path, so ctx->type keeps
|
||||
* the digest the caller chose and a retry with a longer key, which
|
||||
* is what the FIPS failure above asks for, still works. */
|
||||
ctx->hmac.macType = WC_HASH_TYPE_NONE;
|
||||
ret = 0;
|
||||
}
|
||||
if (ret == 1) {
|
||||
@@ -1984,6 +1996,21 @@ int wolfSSL_HMAC_cleanup(WOLFSSL_HMAC_CTX* ctx)
|
||||
if (ctx != NULL) {
|
||||
/* Free the dynamic data in the wolfSSL HMAC object. */
|
||||
wc_HmacFree(&ctx->hmac);
|
||||
/* The pads saved for re-init are derived from the key and live
|
||||
* outside the wolfSSL HMAC object, so wipe them here. */
|
||||
ForceZero(ctx->save_ipad, sizeof(ctx->save_ipad));
|
||||
ForceZero(ctx->save_opad, sizeof(ctx->save_opad));
|
||||
/* Mark the context unkeyed so a later init with no key is rejected
|
||||
* rather than recovering from the pads just wiped. Relying on the
|
||||
* hash type in the wolfSSL HMAC object is not enough: a zeroed
|
||||
* macType does not read as none in every hash type enum layout. */
|
||||
ctx->type = WC_HASH_TYPE_NONE;
|
||||
/* The wolfCrypt object's type has to say unkeyed as well. wc_HmacFree
|
||||
* zeroes it, which only reads as none where that enum value is 0; in
|
||||
* the FIPS and selftest layout zero is MD5, so an init that supplies a
|
||||
* digest but no key would take the recover path and MAC with the pads
|
||||
* just wiped. */
|
||||
ctx->hmac.macType = WC_HASH_TYPE_NONE;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -123,6 +123,15 @@ int wolfSSL_CTX_GenerateEchConfigEx(WOLFSSL_CTX* ctx, const char* publicName,
|
||||
if (ret == 0)
|
||||
ret = wc_HpkeGenerateKeyPair(hpke, &newConfig->receiverPrivkey, rng);
|
||||
|
||||
/* The key outlives this RNG, and key generation may have stored a
|
||||
* pointer to it in the key, so take it back out before freeing it. */
|
||||
#if defined(HAVE_CURVE25519) && defined(WOLFSSL_CURVE25519_BLINDING)
|
||||
if ((ret == 0) && (kemId == DHKEM_X25519_HKDF_SHA256)) {
|
||||
(void)wc_curve25519_set_rng(
|
||||
(curve25519_key*)newConfig->receiverPrivkey, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* done with RNG */
|
||||
wc_FreeRng(rng);
|
||||
|
||||
|
||||
@@ -804,6 +804,11 @@ int wolfSSL_make_eap_keys(WOLFSSL* ssl, void* key, unsigned int len,
|
||||
int ret;
|
||||
WC_DECLARE_VAR(seed, byte, SEED_LEN, 0);
|
||||
|
||||
/* The randoms and the master secret live in the handshake arrays, which
|
||||
* are gone once the handshake resources have been released. */
|
||||
if (ssl == NULL || ssl->arrays == NULL)
|
||||
return BAD_FUNC_ARG;
|
||||
|
||||
WC_ALLOC_VAR_EX(seed, byte, SEED_LEN, ssl->heap, DYNAMIC_TYPE_SEED,
|
||||
return MEMORY_E);
|
||||
|
||||
|
||||
+23
-4
@@ -6859,7 +6859,7 @@ static int X509PrintDirType(char * dst, int max_len, const DNS_entry * entry)
|
||||
word32 k = 0;
|
||||
word32 i = 0;
|
||||
const char * src = entry->name;
|
||||
word32 src_len = (word32)XSTRLEN(src);
|
||||
word32 src_len = 0;
|
||||
int total_len = 0;
|
||||
int bytes_left = max_len;
|
||||
int fld_len = 0;
|
||||
@@ -6867,14 +6867,24 @@ static int X509PrintDirType(char * dst, int max_len, const DNS_entry * entry)
|
||||
|
||||
XMEMSET(dst, 0, max_len);
|
||||
|
||||
/* The entry holds raw DER which may contain zero bytes, and under
|
||||
* WC_ASN_NO_HEAP it is not NUL terminated, so use the stored length. */
|
||||
if (entry->len > 0) {
|
||||
src_len = (word32)entry->len;
|
||||
}
|
||||
|
||||
/* loop over printable DIR tags. */
|
||||
for (k = 0; k < ACERT_NUM_DIR_TAGS; ++k) {
|
||||
const char * pfx = acert_dir_print[k].pfx;
|
||||
const byte * tag = acert_dir_print[k].tag;
|
||||
byte asn_tag;
|
||||
|
||||
/* walk through entry looking for matches. */
|
||||
for (i = 0; i < src_len - 5; ++i) {
|
||||
/* Walk through entry looking for matches. A match needs three bytes
|
||||
* of name OID plus a tag and a length, so the last offset worth
|
||||
* testing is the one with exactly five bytes left. Written as an
|
||||
* addition so a short entry simply skips the loop rather than
|
||||
* underflowing the bound. */
|
||||
for (i = 0; i + 5 <= src_len; ++i) {
|
||||
if (XMEMCMP(tag, &src[i], 3) == 0) {
|
||||
if (bytes_left < 5) {
|
||||
/* Not enough space left for name oid + tag + len. */
|
||||
@@ -6994,6 +7004,12 @@ static int X509_print_name_entry(WOLFSSL_BIO* bio,
|
||||
}
|
||||
else if (entry->type == ASN_DIR_TYPE) {
|
||||
len = X509PrintDirType(scratch, MAX_WIDTH, entry);
|
||||
if (len == 0) {
|
||||
/* Nothing in the encoding was printable. Emit a placeholder,
|
||||
* as the other unsupported entry types do, rather than
|
||||
* failing the print of the whole certificate. */
|
||||
len = XSNPRINTF(scratch, MAX_WIDTH, "DirName:<unprintable>");
|
||||
}
|
||||
}
|
||||
else if (entry->type == ASN_URI_TYPE) {
|
||||
len = XSNPRINTF(scratch, MAX_WIDTH, "URI:%s",
|
||||
@@ -15721,7 +15737,10 @@ int wolfSSL_X509_check_host(WOLFSSL_X509 *x, const char *chk, size_t chklen,
|
||||
}
|
||||
|
||||
#ifdef WOLFSSL_IP_ALT_NAME
|
||||
ret = CheckIPAddr(dCert, (char *)chk);
|
||||
/* chk is length delimited and may not be NUL terminated, so check it
|
||||
* against the iPAddress entries directly rather than through the
|
||||
* NUL terminated CheckIPAddr helper. */
|
||||
ret = CheckHostName(dCert, (char *)chk, chklen, 0, 1);
|
||||
if (ret == 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
+400
@@ -608,6 +608,54 @@ static int test_wc_LoadStaticMemory_CTX(void)
|
||||
}
|
||||
|
||||
|
||||
/* An ECIES context allocated from a heap hint must be returned to that same
|
||||
* heap, so the hint has to survive the reset that sets the context defaults. */
|
||||
static int test_wc_ecc_ctx_new_ex_heap(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(WOLFSSL_STATIC_MEMORY) && !defined(WOLFSSL_STATIC_MEMORY_LEAN) && \
|
||||
defined(HAVE_ECC) && defined(HAVE_ECC_ENCRYPT) && !defined(WC_NO_RNG)
|
||||
byte staticMemory[TEST_LSM_STATIC_SIZE];
|
||||
word32 sizeList[TEST_LSM_DEF_BUCKETS] = { TEST_LSM_BUCKETS };
|
||||
word32 distList[TEST_LSM_DEF_BUCKETS] = { TEST_LSM_DIST };
|
||||
WOLFSSL_HEAP_HINT* heap = NULL;
|
||||
WOLFSSL_MEM_STATS stats;
|
||||
WC_RNG rng;
|
||||
ecEncCtx* ctx = NULL;
|
||||
int rngInit = 0;
|
||||
|
||||
XMEMSET(&stats, 0, sizeof(stats));
|
||||
ExpectIntEQ(wc_LoadStaticMemory_ex(&heap,
|
||||
WOLFMEM_DEF_BUCKETS, sizeList, distList,
|
||||
staticMemory, (word32)sizeof(staticMemory),
|
||||
0, 1),
|
||||
0);
|
||||
ExpectIntEQ(wc_InitRng(&rng), 0);
|
||||
if (EXPECT_SUCCESS()) {
|
||||
rngInit = 1;
|
||||
}
|
||||
|
||||
ExpectNotNull(ctx = wc_ecc_ctx_new_ex(REQ_RESP_CLIENT, &rng, heap));
|
||||
wc_ecc_ctx_free(ctx);
|
||||
ctx = NULL;
|
||||
|
||||
/* The context came out of the static heap, so freeing it must put it
|
||||
* back rather than hand it to the default allocator. */
|
||||
if (EXPECT_SUCCESS() && (heap != NULL)) {
|
||||
ExpectIntEQ(wolfSSL_GetMemStats(heap->memory, &stats), 1);
|
||||
ExpectIntEQ(stats.curAlloc, 0);
|
||||
ExpectIntEQ(stats.totalAlloc, stats.totalFr);
|
||||
}
|
||||
|
||||
if (rngInit) {
|
||||
wc_FreeRng(&rng);
|
||||
}
|
||||
wc_UnloadStaticMemory(heap);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------------*
|
||||
| Platform dependent function test
|
||||
*----------------------------------------------------------------------------*/
|
||||
@@ -9855,6 +9903,142 @@ static int test_wolfSSL_clear_secure_renegotiation(void)
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
||||
(defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL))
|
||||
/* Returns 1 when every byte of the buffer is zero. */
|
||||
static int test_clear_all_zero(const void* buf, size_t len)
|
||||
{
|
||||
const byte* p = (const byte*)buf;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (p[i] != 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* wolfSSL_clear recycles the object for a new connection, so it must not carry
|
||||
* the previous connection's key material into the reused object. */
|
||||
static int test_wolfSSL_clear_zeroizes_secrets(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
||||
(defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL))
|
||||
struct test_memio_ctx test_ctx;
|
||||
WOLFSSL_CTX* ctx_c = NULL;
|
||||
WOLFSSL_CTX* ctx_s = NULL;
|
||||
WOLFSSL* ssl_c = NULL;
|
||||
WOLFSSL* ssl_s = NULL;
|
||||
|
||||
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
||||
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
||||
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
||||
/* Ask to hold on to the handshake arrays, both so the master secret is
|
||||
* still resident when the object is recycled and so the clear is required
|
||||
* to honour that request. */
|
||||
if (ssl_s != NULL) {
|
||||
wolfSSL_KeepArrays(ssl_s);
|
||||
}
|
||||
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
||||
|
||||
/* The completed connection left key material behind. */
|
||||
if (EXPECT_SUCCESS() && (ssl_s != NULL)) {
|
||||
ExpectIntEQ(test_clear_all_zero(ssl_s->keys.client_write_key,
|
||||
sizeof(ssl_s->keys.client_write_key)), 0);
|
||||
ExpectIntEQ(test_clear_all_zero(ssl_s->keys.server_write_key,
|
||||
sizeof(ssl_s->keys.server_write_key)), 0);
|
||||
ExpectIntEQ(test_clear_all_zero(ssl_s->clientSecret,
|
||||
sizeof(ssl_s->clientSecret)), 0);
|
||||
ExpectIntEQ(test_clear_all_zero(ssl_s->serverSecret,
|
||||
sizeof(ssl_s->serverSecret)), 0);
|
||||
ExpectNotNull(ssl_s->arrays);
|
||||
}
|
||||
if (EXPECT_SUCCESS() && (ssl_s != NULL) && (ssl_s->arrays != NULL)) {
|
||||
ExpectIntEQ(test_clear_all_zero(ssl_s->arrays->masterSecret,
|
||||
SECRET_LEN), 0);
|
||||
}
|
||||
|
||||
ExpectIntEQ(wolfSSL_clear(ssl_s), WOLFSSL_SUCCESS);
|
||||
|
||||
if (EXPECT_SUCCESS() && (ssl_s != NULL)) {
|
||||
ExpectIntEQ(test_clear_all_zero(ssl_s->keys.client_write_key,
|
||||
sizeof(ssl_s->keys.client_write_key)), 1);
|
||||
ExpectIntEQ(test_clear_all_zero(ssl_s->keys.server_write_key,
|
||||
sizeof(ssl_s->keys.server_write_key)), 1);
|
||||
ExpectIntEQ(test_clear_all_zero(ssl_s->clientSecret,
|
||||
sizeof(ssl_s->clientSecret)), 1);
|
||||
ExpectIntEQ(test_clear_all_zero(ssl_s->serverSecret,
|
||||
sizeof(ssl_s->serverSecret)), 1);
|
||||
/* The application asked to keep the handshake arrays, so they must
|
||||
* survive along with the master secret it wants to read back. */
|
||||
ExpectNotNull(ssl_s->arrays);
|
||||
}
|
||||
if (EXPECT_SUCCESS() && (ssl_s != NULL) && (ssl_s->arrays != NULL)) {
|
||||
ExpectIntEQ(test_clear_all_zero(ssl_s->arrays->masterSecret,
|
||||
SECRET_LEN), 0);
|
||||
/* The pre-master secret is not part of that contract. */
|
||||
ExpectNotNull(ssl_s->arrays->preMasterSecret);
|
||||
}
|
||||
if (EXPECT_SUCCESS() && (ssl_s != NULL) && (ssl_s->arrays != NULL) &&
|
||||
(ssl_s->arrays->preMasterSecret != NULL)) {
|
||||
ExpectIntEQ(test_clear_all_zero(ssl_s->arrays->preMasterSecret,
|
||||
ENCRYPT_LEN), 1);
|
||||
}
|
||||
if (EXPECT_SUCCESS() && (ssl_s != NULL) && (ssl_s->arrays != NULL)) {
|
||||
/* The key schedule secret is not part of the contract either. */
|
||||
ExpectIntEQ(test_clear_all_zero(ssl_s->arrays->secret, SECRET_LEN), 1);
|
||||
#ifdef HAVE_KEYING_MATERIAL
|
||||
/* The exporter secret is. Exporting keying material needs the arrays
|
||||
* kept, and Tls13_Exporter() reads this one to do it. */
|
||||
ExpectIntEQ(test_clear_all_zero(ssl_s->arrays->exporterSecret,
|
||||
WC_MAX_DIGEST_SIZE), 0);
|
||||
#endif
|
||||
}
|
||||
#if (defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL) || \
|
||||
defined(HAVE_SECRET_CALLBACK)) && !defined(NO_WOLFSSL_CLIENT)
|
||||
/* The documented reason to keep the arrays is to read this back. */
|
||||
if (EXPECT_SUCCESS()) {
|
||||
byte cr[RAN_LEN];
|
||||
|
||||
ExpectIntEQ(wolfSSL_get_client_random(ssl_s, cr, sizeof(cr)), RAN_LEN);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Take the request back and clear again. Now the master secret has to go,
|
||||
* but the arrays themselves still must not: the object is being recycled
|
||||
* rather than freed, and wolfSSL_set_secret() along with the accessors
|
||||
* that run after a connection all write into them. Some configurations,
|
||||
* OpenVPN support among them, keep the arrays for every object, so set
|
||||
* this directly rather than relying on the default. */
|
||||
if (EXPECT_SUCCESS() && (ssl_s != NULL)) {
|
||||
ssl_s->options.saveArrays = 0;
|
||||
ExpectIntEQ(wolfSSL_clear(ssl_s), WOLFSSL_SUCCESS);
|
||||
ExpectNotNull(ssl_s->arrays);
|
||||
}
|
||||
if (EXPECT_SUCCESS() && (ssl_s != NULL) && (ssl_s->arrays != NULL)) {
|
||||
ExpectIntEQ(test_clear_all_zero(ssl_s->arrays->masterSecret,
|
||||
SECRET_LEN), 1);
|
||||
#ifdef HAVE_KEYING_MATERIAL
|
||||
ExpectIntEQ(test_clear_all_zero(ssl_s->arrays->exporterSecret,
|
||||
WC_MAX_DIGEST_SIZE), 1);
|
||||
#endif
|
||||
ExpectNotNull(ssl_s->arrays->preMasterSecret);
|
||||
/* The key agreement routines read this as the room they have. */
|
||||
ExpectIntEQ((int)ssl_s->arrays->preMasterSz, ENCRYPT_LEN);
|
||||
}
|
||||
|
||||
wolfSSL_free(ssl_c);
|
||||
wolfSSL_free(ssl_s);
|
||||
wolfSSL_CTX_free(ctx_c);
|
||||
wolfSSL_CTX_free(ctx_s);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/* Test reconnecting with a different ciphersuite after a renegotiation. */
|
||||
static int test_wolfSSL_SCR_Reconnect(void)
|
||||
{
|
||||
@@ -28256,6 +28440,73 @@ static int test_wolfSSL_X509_print_ext_key_usage(void)
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
static int test_wolfSSL_X509_print_dir_altname(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM) && \
|
||||
!defined(NO_RSA) && defined(XSNPRINTF) && \
|
||||
!defined(WC_DISABLE_RADIX_ZERO_PAD) && !defined(IGNORE_NAME_CONSTRAINTS)
|
||||
/* A directoryName alt name holds raw DER, which routinely contains zero
|
||||
* bytes. The print path must use the stored entry length rather than
|
||||
* treating the DER as a NUL terminated string. */
|
||||
static const char dirName[] = {
|
||||
/* countryName with an empty PrintableString, contributing a zero
|
||||
* byte early in the encoding. */
|
||||
0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x00,
|
||||
/* commonName "Test", which sits after that zero byte. */
|
||||
0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x04, 'T', 'e', 's', 't'
|
||||
};
|
||||
/* Shorter than the five bytes the tag scan needs. */
|
||||
static const char shortDirName[] = { 0x30, 0x00 };
|
||||
X509* x509 = NULL;
|
||||
BIO* bio = NULL;
|
||||
char* data = NULL;
|
||||
int len = 0;
|
||||
char buf[8192];
|
||||
|
||||
ExpectNotNull(x509 = X509_load_certificate_file(svrCertFile,
|
||||
WOLFSSL_FILETYPE_PEM));
|
||||
ExpectIntEQ(wolfSSL_X509_add_altname_ex(x509, dirName, (word32)sizeof(
|
||||
dirName), ASN_DIR_TYPE), WOLFSSL_SUCCESS);
|
||||
|
||||
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
|
||||
ExpectIntEQ(X509_print(bio, x509), SSL_SUCCESS);
|
||||
/* Memory BIO data is not NUL-terminated; copy into a bounded buffer. */
|
||||
ExpectIntGT((len = BIO_get_mem_data(bio, &data)), 0);
|
||||
ExpectIntLT(len, (int)sizeof(buf));
|
||||
if ((data != NULL) && (len > 0) && (len < (int)sizeof(buf))) {
|
||||
XMEMCPY(buf, data, (size_t)len);
|
||||
buf[len] = '\0';
|
||||
ExpectNotNull(XSTRSTR(buf, "CN=Test"));
|
||||
}
|
||||
BIO_free(bio);
|
||||
bio = NULL;
|
||||
X509_free(x509);
|
||||
x509 = NULL;
|
||||
|
||||
/* A directoryName too short to hold a tag must not be scanned past its
|
||||
* end, and having nothing printable in it must not fail the print of the
|
||||
* whole certificate. */
|
||||
ExpectNotNull(x509 = X509_load_certificate_file(svrCertFile,
|
||||
WOLFSSL_FILETYPE_PEM));
|
||||
ExpectIntEQ(wolfSSL_X509_add_altname_ex(x509, shortDirName, (word32)sizeof(
|
||||
shortDirName), ASN_DIR_TYPE), WOLFSSL_SUCCESS);
|
||||
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
|
||||
ExpectIntEQ(X509_print(bio, x509), SSL_SUCCESS);
|
||||
ExpectIntGT((len = BIO_get_mem_data(bio, &data)), 0);
|
||||
ExpectIntLT(len, (int)sizeof(buf));
|
||||
if ((data != NULL) && (len > 0) && (len < (int)sizeof(buf))) {
|
||||
XMEMCPY(buf, data, (size_t)len);
|
||||
buf[len] = '\0';
|
||||
ExpectNotNull(XSTRSTR(buf, "DirName:<unprintable>"));
|
||||
}
|
||||
|
||||
BIO_free(bio);
|
||||
X509_free(x509);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
static int test_wolfSSL_X509_CRL_print(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
@@ -35683,6 +35934,151 @@ static int test_write_dup_oom(void)
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
#if defined(OPENSSL_EXTRA) && defined(WOLFCRYPT_HAVE_SRP) && \
|
||||
defined(WOLFSSL_SMALL_STACK) && \
|
||||
defined(USE_WOLFSSL_MEMORY) && !defined(WOLFSSL_NO_MALLOC) && \
|
||||
!defined(WOLFSSL_STATIC_MEMORY) && !defined(WOLFSSL_KERNEL_MODE) && \
|
||||
!defined(NO_SHA256)
|
||||
/* Allocator that fails the Nth allocation once armed, counting every
|
||||
* allocation rather than matching on size. Blocks handed out before the
|
||||
* failure are filled with a non-zero pattern, since the point of the test is
|
||||
* what the cleanup path does with allocated but not yet initialized memory. */
|
||||
static int srp_oom_count = 0;
|
||||
static int srp_oom_fail_at = 0;
|
||||
static int srp_oom_failed = 0;
|
||||
|
||||
#ifdef WOLFSSL_DEBUG_MEMORY
|
||||
static void* srp_oom_malloc_cb(size_t size, const char* func, unsigned int line)
|
||||
{
|
||||
void* p;
|
||||
|
||||
(void)func;
|
||||
(void)line;
|
||||
#else
|
||||
static void* srp_oom_malloc_cb(size_t size)
|
||||
{
|
||||
void* p;
|
||||
#endif
|
||||
|
||||
if (srp_oom_fail_at != 0) {
|
||||
srp_oom_count++;
|
||||
if (srp_oom_count == srp_oom_fail_at) {
|
||||
srp_oom_failed = 1;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
p = malloc(size);
|
||||
if ((p != NULL) && (srp_oom_fail_at != 0)) {
|
||||
XMEMSET(p, 0xA5, size);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
#ifdef WOLFSSL_DEBUG_MEMORY
|
||||
static void srp_oom_free_cb(void* ptr, const char* func, unsigned int line)
|
||||
{
|
||||
(void)func;
|
||||
(void)line;
|
||||
#else
|
||||
static void srp_oom_free_cb(void* ptr)
|
||||
{
|
||||
#endif
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
#ifdef WOLFSSL_DEBUG_MEMORY
|
||||
static void* srp_oom_realloc_cb(void* ptr, size_t size, const char* func,
|
||||
unsigned int line)
|
||||
{
|
||||
(void)func;
|
||||
(void)line;
|
||||
#else
|
||||
static void* srp_oom_realloc_cb(void* ptr, size_t size)
|
||||
{
|
||||
#endif
|
||||
return realloc(ptr, size);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* An allocation failure part way through the small stack allocations in
|
||||
* wc_SrpComputeKey must not leave the cleanup path operating on mp_ints that
|
||||
* were allocated but never initialized. */
|
||||
static int test_wc_SrpComputeKey_oom(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(OPENSSL_EXTRA) && defined(WOLFCRYPT_HAVE_SRP) && \
|
||||
defined(WOLFSSL_SMALL_STACK) && \
|
||||
defined(USE_WOLFSSL_MEMORY) && !defined(WOLFSSL_NO_MALLOC) && \
|
||||
!defined(WOLFSSL_STATIC_MEMORY) && !defined(WOLFSSL_KERNEL_MODE) && \
|
||||
!defined(NO_SHA256)
|
||||
Srp srp;
|
||||
byte pubKey[8];
|
||||
wolfSSL_Malloc_cb prev_mc = NULL;
|
||||
wolfSSL_Free_cb prev_fc = NULL;
|
||||
wolfSSL_Realloc_cb prev_rc = NULL;
|
||||
int allocators_set = 0;
|
||||
int ret;
|
||||
int i;
|
||||
int fired = 0;
|
||||
|
||||
XMEMSET(pubKey, 1, sizeof(pubKey));
|
||||
|
||||
ExpectIntEQ(wolfSSL_GetAllocators(&prev_mc, &prev_fc, &prev_rc), 0);
|
||||
ExpectIntEQ(wolfSSL_SetAllocators(srp_oom_malloc_cb, srp_oom_free_cb,
|
||||
srp_oom_realloc_cb), 0);
|
||||
if (EXPECT_SUCCESS()) {
|
||||
allocators_set = 1;
|
||||
}
|
||||
|
||||
/* wc_SrpComputeKey allocates a hash, a digest and four mp_ints before it
|
||||
* initializes any of them, so failing part way through leaves allocated
|
||||
* but uninitialized mp_ints for the cleanup path to deal with. Which
|
||||
* ordinal lands there moves with the math backend and the small stack
|
||||
* settings, so sweep past the six it is known to make rather than pin
|
||||
* one: every failure point has to come back as an error, and none of them
|
||||
* may crash. */
|
||||
for (i = 1; i <= 8 && EXPECT_SUCCESS(); i++) {
|
||||
/* Arm only around the call under test, so that the allocations
|
||||
* wc_SrpInit and wc_SrpTerm make are neither counted nor failed. */
|
||||
ExpectIntEQ(wc_SrpInit(&srp, SRP_TYPE_SHA256, SRP_CLIENT_SIDE), 0);
|
||||
if (!EXPECT_SUCCESS()) {
|
||||
break;
|
||||
}
|
||||
|
||||
srp_oom_count = 0;
|
||||
srp_oom_failed = 0;
|
||||
srp_oom_fail_at = i;
|
||||
|
||||
ret = wc_SrpComputeKey(&srp, pubKey, (word32)sizeof(pubKey),
|
||||
pubKey, (word32)sizeof(pubKey));
|
||||
|
||||
srp_oom_fail_at = 0;
|
||||
|
||||
/* Past the last allocation the call makes there is nothing to
|
||||
* exercise, so only the ordinals that actually landed are judged. */
|
||||
if (srp_oom_failed) {
|
||||
fired++;
|
||||
ExpectIntLT(ret, 0);
|
||||
}
|
||||
|
||||
wc_SrpTerm(&srp);
|
||||
}
|
||||
|
||||
/* A sweep that never injected anything would pass without testing
|
||||
* anything. */
|
||||
ExpectIntGT(fired, 0);
|
||||
|
||||
srp_oom_fail_at = 0;
|
||||
|
||||
if (allocators_set) {
|
||||
(void)wolfSSL_SetAllocators(prev_mc, prev_fc, prev_rc);
|
||||
}
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
static int test_read_write_hs(void)
|
||||
{
|
||||
|
||||
@@ -37962,6 +38358,7 @@ TEST_CASE testCases[] = {
|
||||
|
||||
TEST_DECL(test_wc_LoadStaticMemory_ex),
|
||||
TEST_DECL(test_wc_LoadStaticMemory_CTX),
|
||||
TEST_DECL(test_wc_ecc_ctx_new_ex_heap),
|
||||
|
||||
TEST_DECL(test_wc_FreeCertList),
|
||||
/* Locking with Compat Mutex */
|
||||
@@ -38291,6 +38688,7 @@ TEST_CASE testCases[] = {
|
||||
TEST_DECL(test_wolfSSL_X509_print),
|
||||
TEST_DECL(test_wolfSSL_X509_print_basic_constraints),
|
||||
TEST_DECL(test_wolfSSL_X509_print_ext_key_usage),
|
||||
TEST_DECL(test_wolfSSL_X509_print_dir_altname),
|
||||
TEST_DECL(test_wolfSSL_X509_CRL_print),
|
||||
#endif
|
||||
|
||||
@@ -38684,6 +39082,7 @@ TEST_CASE testCases[] = {
|
||||
TEST_DECL(test_wolfSSL_custom_ext_add_null),
|
||||
TEST_DECL(test_wolfSSL_wolfSSL_UseSecureRenegotiation),
|
||||
TEST_DECL(test_wolfSSL_clear_secure_renegotiation),
|
||||
TEST_DECL(test_wolfSSL_clear_zeroizes_secrets),
|
||||
TEST_DECL(test_wolfSSL_SCR_Reconnect),
|
||||
TEST_DECL(test_wolfSSL_SCR_check_enabled),
|
||||
TEST_DECL(test_wolfSSL_ticket_keycb_bad_hmac),
|
||||
@@ -38836,6 +39235,7 @@ TEST_CASE testCases[] = {
|
||||
TEST_DECL(test_write_dup_want_write),
|
||||
TEST_DECL(test_write_dup_want_write_simul),
|
||||
TEST_DECL(test_write_dup_oom),
|
||||
TEST_DECL(test_wc_SrpComputeKey_oom),
|
||||
TEST_DECL(test_read_write_hs),
|
||||
TEST_DECL(test_get_signature_nid),
|
||||
#ifndef WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION
|
||||
|
||||
@@ -852,6 +852,7 @@ int test_dtls13_new_connection_id(void)
|
||||
recSz), 0);
|
||||
ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
||||
TEST_DTLS13_PUMP(ssl_s);
|
||||
/* the server ACKed the message */
|
||||
ExpectIntGT(test_ctx.c_len, 0);
|
||||
ExpectIntEQ(wolfSSL_dtls_cid_get_tx_size(ssl_s, &cidSz), 1);
|
||||
@@ -902,6 +903,7 @@ int test_dtls13_new_connection_id(void)
|
||||
recSz), 0);
|
||||
ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
||||
TEST_DTLS13_PUMP(ssl_s);
|
||||
cidSz = 0;
|
||||
ExpectIntEQ(wolfSSL_dtls_cid_get_tx_size(ssl_s, &cidSz), 1);
|
||||
ExpectIntEQ(cidSz, sizeof(newCid));
|
||||
@@ -970,6 +972,7 @@ int test_dtls13_request_connection_id(void)
|
||||
recSz), 0);
|
||||
ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
||||
TEST_DTLS13_PUMP(ssl_s);
|
||||
ExpectIntGT(test_ctx.c_len, 0);
|
||||
/* nothing but the ACK reaches the client */
|
||||
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1);
|
||||
@@ -2151,6 +2154,7 @@ int test_dtls_drop_client_ack(void)
|
||||
/* this should re-send the ack immediately */
|
||||
ExpectIntEQ(wolfSSL_read(ssl_s, data, 32), -1);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
||||
TEST_DTLS13_PUMP(ssl_s);
|
||||
ExpectIntEQ(test_ctx.c_msg_count, 1);
|
||||
|
||||
/* This should advance the connection on the client */
|
||||
@@ -4012,6 +4016,22 @@ static void test_AEAD_get_limits(WOLFSSL* ssl, w64wrapper* hardLimit,
|
||||
}
|
||||
}
|
||||
|
||||
/* A DTLS 1.3 key update is scheduled while reading but is only transmitted
|
||||
* from the write path. With WOLFSSL_RW_THREADED the read path does no
|
||||
* scheduled work at all, because the reader must not transmit while a writer
|
||||
* thread may be doing so, so an application that reads without writing has to
|
||||
* pump the deferred work itself. Do what such an application would do. */
|
||||
static void test_AEAD_drain_scheduled_work(WOLFSSL* ssl)
|
||||
{
|
||||
/* Match where wolfSSL_dtls13_do_scheduled_work() is declared, in
|
||||
* wolfssl/ssl.h, or a WOLFSSL_LEANPSK build has no declaration for it. */
|
||||
#if defined(WOLFSSL_RW_THREADED) && !defined(WOLFSSL_LEANPSK)
|
||||
AssertIntEQ(wolfSSL_dtls13_do_scheduled_work(ssl), WOLFSSL_SUCCESS);
|
||||
#else
|
||||
(void)ssl;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void test_AEAD_limit_client(WOLFSSL* ssl)
|
||||
{
|
||||
int ret;
|
||||
@@ -4049,6 +4069,7 @@ static void test_AEAD_limit_client(WOLFSSL* ssl)
|
||||
/* Key update should be sent and negotiated */
|
||||
ret = wolfSSL_read(ssl, msgBuf, sizeof(msgBuf));
|
||||
AssertIntGT(ret, 0);
|
||||
test_AEAD_drain_scheduled_work(ssl);
|
||||
/* Epoch after one key update is 4 */
|
||||
if (w64Equal(ssl->dtls13PeerEpoch, w64From32(0, 4)) &&
|
||||
w64Equal(Dtls13GetEpoch(ssl, ssl->dtls13PeerEpoch)->dropCount, counter)) {
|
||||
@@ -4058,6 +4079,17 @@ static void test_AEAD_limit_client(WOLFSSL* ssl)
|
||||
}
|
||||
AssertTrue(didReKey);
|
||||
|
||||
/* Everything below needs the ACK of that first key update to be
|
||||
* processed, which rotates our own sending keys and so creates an epoch.
|
||||
* The epoch table has no locking, and with WOLFSSL_RW_THREADED the read
|
||||
* thread mutates it too, so that processing deliberately stays off the
|
||||
* write path and a second key update cannot complete in such a build.
|
||||
* Don't assert behaviour that is knowingly not provided. The hard limit
|
||||
* check below is inside this too, and deliberately: without that ACK the
|
||||
* decrypting epoch stops matching the one the drop counter is placed on,
|
||||
* so the read never reaches the limit and loops until the test times out.
|
||||
*/
|
||||
#ifndef WOLFSSL_RW_THREADED
|
||||
if (!w64IsZero(sendLimit)) {
|
||||
/* Test the sending limit for AEAD ciphers */
|
||||
#ifdef WOLFSSL_MUTEX_INITIALIZER
|
||||
@@ -4095,7 +4127,13 @@ static void test_AEAD_limit_client(WOLFSSL* ssl)
|
||||
ret = wolfSSL_read(ssl, msgBuf, sizeof(msgBuf));
|
||||
AssertIntEQ(ret, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
||||
AssertIntEQ(wolfSSL_get_error(ssl, ret), WC_NO_ERR_TRACE(DECRYPT_ERROR));
|
||||
#else
|
||||
(void)sendLimit;
|
||||
(void)hardLimit;
|
||||
#endif
|
||||
|
||||
/* Always signal completion, including on the paths skipped above, so the
|
||||
* peer thread does not spin waiting for a flag that never gets set. */
|
||||
#ifdef WOLFSSL_ATOMIC_INITIALIZER
|
||||
WOLFSSL_ATOMIC_STORE(test_AEAD_done, 1);
|
||||
#else
|
||||
@@ -6250,6 +6288,126 @@ int test_wolfSSL_dtls_create_free_peer(void)
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/* wolfSSL_dtls13_do_scheduled_work() and wolfSSL_dtls13_pending_work() exist
|
||||
* so an application whose read thread cannot transmit can send it itself.
|
||||
* Cover the contract in every build, not just the threaded one: the pump is
|
||||
* always safe to call, and the predicate must not claim work it cannot do. */
|
||||
int test_wolfSSL_dtls_scheduled_work(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_LEANPSK) && \
|
||||
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES)
|
||||
struct test_memio_ctx test_ctx;
|
||||
WOLFSSL_CTX* ctx_c = NULL;
|
||||
WOLFSSL_CTX* ctx_s = NULL;
|
||||
WOLFSSL* ssl_c = NULL;
|
||||
WOLFSSL* ssl_s = NULL;
|
||||
int prevLen = 0;
|
||||
|
||||
/* Bad arguments. */
|
||||
ExpectIntEQ(wolfSSL_dtls13_do_scheduled_work(NULL), WOLFSSL_FATAL_ERROR);
|
||||
ExpectIntEQ(wolfSSL_dtls13_pending_work(NULL), 0);
|
||||
|
||||
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
||||
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
||||
wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0);
|
||||
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
||||
|
||||
/* Idle connection: nothing to do, and pumping is still a success. */
|
||||
ExpectIntEQ(wolfSSL_dtls13_do_scheduled_work(ssl_s), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_dtls13_pending_work(ssl_s), 0);
|
||||
|
||||
/* Pumping must be idempotent, and must not invent traffic when there is
|
||||
* nothing scheduled. Compare against what is already queued rather than
|
||||
* clearing it, which would drop records the peer still needs. */
|
||||
prevLen = test_ctx.c_len;
|
||||
ExpectIntEQ(wolfSSL_dtls13_do_scheduled_work(ssl_s), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(test_ctx.c_len, prevLen);
|
||||
|
||||
/* The connection still works in both directions after being pumped. */
|
||||
ExpectIntEQ(test_dtls_communication(ssl_s, ssl_c), TEST_SUCCESS);
|
||||
|
||||
/* Positive path. Schedule a key update the way Dtls13CheckAEADFailLimit()
|
||||
* does when the AEAD failure limit is reached, then require the predicate
|
||||
* to report it, the pump to perform it, and both to settle afterwards. */
|
||||
if (ssl_s != NULL) {
|
||||
ssl_s->dtls13DoKeyUpdate = 1;
|
||||
}
|
||||
ExpectIntEQ(wolfSSL_dtls13_pending_work(ssl_s), 1);
|
||||
prevLen = test_ctx.c_len;
|
||||
ExpectIntEQ(wolfSSL_dtls13_do_scheduled_work(ssl_s), WOLFSSL_SUCCESS);
|
||||
/* The update was performed, and it put a record on the wire. */
|
||||
if (ssl_s != NULL) {
|
||||
ExpectIntEQ(ssl_s->dtls13DoKeyUpdate, 0);
|
||||
}
|
||||
ExpectIntGT(test_ctx.c_len, prevLen);
|
||||
/* Waiting for the peer's ACK is not work we can do, so the predicate must
|
||||
* report nothing rather than making a drain loop spin. */
|
||||
if (ssl_s != NULL) {
|
||||
ExpectIntEQ(ssl_s->dtls13WaitKeyUpdateAck, 1);
|
||||
}
|
||||
ExpectIntEQ(wolfSSL_dtls13_pending_work(ssl_s), 0);
|
||||
|
||||
/* The peer asking for a KeyUpdate while one of ours is unacknowledged is
|
||||
* the state that wedges a drain loop: DTLS must not put two in flight, so
|
||||
* the pump declines, and the predicate must decline to report it too.
|
||||
* Pump and predicate have to agree, or the documented loop never ends. */
|
||||
if (ssl_s != NULL) {
|
||||
ssl_s->options.sendKeyUpdate = 1;
|
||||
}
|
||||
ExpectIntEQ(wolfSSL_dtls13_pending_work(ssl_s), 0);
|
||||
prevLen = test_ctx.c_len;
|
||||
ExpectIntEQ(wolfSSL_dtls13_do_scheduled_work(ssl_s), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(test_ctx.c_len, prevLen);
|
||||
/* The request is kept, not silently dropped, for when it can be sent. */
|
||||
if (ssl_s != NULL) {
|
||||
ExpectIntEQ(ssl_s->options.sendKeyUpdate, 1);
|
||||
/* Once nothing is outstanding the pair agrees it is sendable. */
|
||||
ssl_s->dtls13WaitKeyUpdateAck = 0;
|
||||
}
|
||||
ExpectIntEQ(wolfSSL_dtls13_pending_work(ssl_s), 1);
|
||||
ExpectIntEQ(wolfSSL_dtls13_do_scheduled_work(ssl_s), WOLFSSL_SUCCESS);
|
||||
if (ssl_s != NULL) {
|
||||
ExpectIntEQ(ssl_s->options.sendKeyUpdate, 0);
|
||||
}
|
||||
ExpectIntGT(test_ctx.c_len, prevLen);
|
||||
|
||||
wolfSSL_free(ssl_c);
|
||||
wolfSSL_free(ssl_s);
|
||||
wolfSSL_CTX_free(ctx_c);
|
||||
wolfSSL_CTX_free(ctx_s);
|
||||
ssl_c = NULL;
|
||||
ssl_s = NULL;
|
||||
ctx_c = NULL;
|
||||
ctx_s = NULL;
|
||||
|
||||
/* A DTLS 1.2 object is out of scope: the pump must say so rather than
|
||||
* quietly succeed, and the predicate must not claim work. */
|
||||
#ifndef WOLFSSL_NO_TLS12
|
||||
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
||||
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
||||
wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method), 0);
|
||||
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
||||
ExpectIntEQ(wolfSSL_dtls13_do_scheduled_work(ssl_s), WOLFSSL_FATAL_ERROR);
|
||||
ExpectIntEQ(wolfSSL_dtls13_pending_work(ssl_s), 0);
|
||||
/* Refusing the object must not record an error against the connection.
|
||||
* ssl->error is sticky, and wolfSSL_write() skips its write-dup drain
|
||||
* while it is set, so a rejected call has to leave it alone. */
|
||||
if (ssl_s != NULL) {
|
||||
ExpectIntEQ(ssl_s->error, 0);
|
||||
}
|
||||
/* And the connection must still be usable afterwards. */
|
||||
ExpectIntEQ(test_dtls_communication(ssl_s, ssl_c), TEST_SUCCESS);
|
||||
#endif
|
||||
|
||||
wolfSSL_free(ssl_c);
|
||||
wolfSSL_free(ssl_s);
|
||||
wolfSSL_CTX_free(ctx_c);
|
||||
wolfSSL_CTX_free(ctx_s);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
int test_wolfSSL_dtls_get0_peer(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
|
||||
@@ -53,6 +53,7 @@ int test_dtls_mtu_split_messages(void);
|
||||
int test_dtls_set_session_min_downgrade(void);
|
||||
int test_dtls12_export_import_etm(void);
|
||||
int test_wolfSSL_dtls_create_free_peer(void);
|
||||
int test_wolfSSL_dtls_scheduled_work(void);
|
||||
int test_wolfSSL_dtls_get0_peer(void);
|
||||
int test_wolfSSL_dtls_set_timeout_init(void);
|
||||
int test_wolfSSL_dtls_retransmit(void);
|
||||
@@ -177,6 +178,7 @@ int test_WOLFSSL_dtls_version_alert(void);
|
||||
TEST_DECL_GROUP("dtls", test_dtls13_no_session_id_echo), \
|
||||
TEST_DECL_GROUP("dtls", test_dtls_set_session_min_downgrade), \
|
||||
TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_create_free_peer), \
|
||||
TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_scheduled_work), \
|
||||
TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_get0_peer), \
|
||||
TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_set_timeout_init), \
|
||||
TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_retransmit), \
|
||||
|
||||
@@ -1226,6 +1226,10 @@ int test_dtls13_ack_overflow(void)
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
||||
ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
||||
/* Flush the ACK those reads scheduled, so the seen-record list starts
|
||||
* empty and the counts below are exact. */
|
||||
TEST_DTLS13_PUMP(ssl_c);
|
||||
TEST_DTLS13_PUMP(ssl_s);
|
||||
|
||||
/* Edge case 1: one below limit - all inserts must succeed */
|
||||
for (i = 0; i < DTLS13_ACK_MAX_RECORDS - 1; i++) {
|
||||
@@ -2033,3 +2037,76 @@ int test_dtls13_5_9_0_compat_empty_echo(void)
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/* wolfSSL_clear() wipes the DTLS 1.3 epoch table so a reused object does not
|
||||
* carry the previous connection's traffic keys. Everything that says which
|
||||
* epoch to use lives outside that table and only ever moves up, so it has to
|
||||
* be brought back with it. Run a second handshake over the same objects to
|
||||
* prove they really are reusable: with the table wiped and the numbers left
|
||||
* behind, Dtls13SetEpochKeys() fails the ClientHello with BAD_STATE_E. */
|
||||
int test_dtls13_reuse_after_clear(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) \
|
||||
&& (defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL))
|
||||
struct test_memio_ctx test_ctx;
|
||||
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
||||
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
||||
char readBuf[16];
|
||||
int i;
|
||||
|
||||
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
||||
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
||||
wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0);
|
||||
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
||||
|
||||
/* The handshake has to have moved off epoch 0, or the reset under test
|
||||
* has nothing to undo. */
|
||||
ExpectIntEQ(w64IsZero(ssl_c->dtls13Epoch), 0);
|
||||
ExpectIntEQ(w64IsZero(ssl_s->dtls13Epoch), 0);
|
||||
|
||||
ExpectIntEQ(wolfSSL_write(ssl_c, "first", 5), 5);
|
||||
XMEMSET(readBuf, 0, sizeof(readBuf));
|
||||
ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), 5);
|
||||
ExpectStrEQ(readBuf, "first");
|
||||
|
||||
ExpectIntEQ(wolfSSL_clear(ssl_c), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_clear(ssl_s), WOLFSSL_SUCCESS);
|
||||
|
||||
/* Only the unprotected epoch 0 survives, and the numbers agree with it. */
|
||||
for (i = 1; i < DTLS13_EPOCH_SIZE; i++) {
|
||||
ExpectIntEQ(ssl_c->dtls13Epochs[i].isValid, 0);
|
||||
ExpectIntEQ(ssl_s->dtls13Epochs[i].isValid, 0);
|
||||
}
|
||||
ExpectIntEQ(ssl_c->dtls13Epochs[0].isValid, 1);
|
||||
ExpectIntEQ(ssl_s->dtls13Epochs[0].isValid, 1);
|
||||
ExpectPtrEq(ssl_c->dtls13EncryptEpoch, &ssl_c->dtls13Epochs[0]);
|
||||
ExpectPtrEq(ssl_c->dtls13DecryptEpoch, &ssl_c->dtls13Epochs[0]);
|
||||
ExpectPtrEq(ssl_s->dtls13EncryptEpoch, &ssl_s->dtls13Epochs[0]);
|
||||
ExpectPtrEq(ssl_s->dtls13DecryptEpoch, &ssl_s->dtls13Epochs[0]);
|
||||
ExpectIntEQ(w64IsZero(ssl_c->dtls13Epoch), 1);
|
||||
ExpectIntEQ(w64IsZero(ssl_c->dtls13PeerEpoch), 1);
|
||||
ExpectIntEQ(w64IsZero(ssl_c->dtls13InvalidateBefore), 1);
|
||||
ExpectIntEQ(w64IsZero(ssl_s->dtls13Epoch), 1);
|
||||
ExpectIntEQ(w64IsZero(ssl_s->dtls13PeerEpoch), 1);
|
||||
ExpectIntEQ(w64IsZero(ssl_s->dtls13InvalidateBefore), 1);
|
||||
|
||||
/* Whatever the first connection left in flight belongs to epochs that no
|
||||
* longer exist, so start the transport over as a new association would. */
|
||||
test_memio_clear_buffer(&test_ctx, 0);
|
||||
test_memio_clear_buffer(&test_ctx, 1);
|
||||
|
||||
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
||||
|
||||
ExpectIntEQ(wolfSSL_write(ssl_c, "second", 6), 6);
|
||||
XMEMSET(readBuf, 0, sizeof(readBuf));
|
||||
ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), 6);
|
||||
ExpectStrEQ(readBuf, "second");
|
||||
|
||||
wolfSSL_free(ssl_c);
|
||||
wolfSSL_free(ssl_s);
|
||||
wolfSSL_CTX_free(ctx_c);
|
||||
wolfSSL_CTX_free(ctx_s);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ int test_dtls13_no_session_id_echo(void);
|
||||
int test_dtls13_5_9_0_compat(void);
|
||||
int test_dtls13_5_9_0_compat_bad_echo(void);
|
||||
int test_dtls13_5_9_0_compat_empty_echo(void);
|
||||
int test_dtls13_reuse_after_clear(void);
|
||||
|
||||
#define TEST_DTLS13_DECLS \
|
||||
TEST_DECL_GROUP("dtls13", test_dtls13_bad_epoch_ch), \
|
||||
@@ -80,6 +81,7 @@ int test_dtls13_5_9_0_compat_empty_echo(void);
|
||||
TEST_DECL_GROUP("dtls13", test_dtls13_no_session_id_echo), \
|
||||
TEST_DECL_GROUP("dtls13", test_dtls13_5_9_0_compat), \
|
||||
TEST_DECL_GROUP("dtls13", test_dtls13_5_9_0_compat_bad_echo), \
|
||||
TEST_DECL_GROUP("dtls13", test_dtls13_5_9_0_compat_empty_echo)
|
||||
TEST_DECL_GROUP("dtls13", test_dtls13_5_9_0_compat_empty_echo), \
|
||||
TEST_DECL_GROUP("dtls13", test_dtls13_reuse_after_clear)
|
||||
|
||||
#endif /* TESTS_API_DTLS13_H */
|
||||
|
||||
@@ -784,6 +784,91 @@ int test_tls_hmac_size_overflow(void)
|
||||
return EXPECT_RESULT();
|
||||
} /* END test_tls_hmac_size_overflow */
|
||||
|
||||
/* TimingPadVerify is internal to the library, so this only links in a static
|
||||
* build. */
|
||||
#if defined(WOLFSSL_TEST_STATIC_BUILD) && !defined(NO_HMAC) && \
|
||||
!defined(WOLFSSL_AEAD_ONLY) && !defined(NO_TLS) && \
|
||||
!defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_OLD_TIMINGPADVERIFY) && \
|
||||
!defined(NO_SHA256) && !defined(NO_WOLFSSL_CLIENT)
|
||||
|
||||
static int tpvHmacCalls;
|
||||
static word32 tpvHmacSz;
|
||||
static int tpvHmacPadSz;
|
||||
|
||||
/* Record the length arguments TimingPadVerify hands to the MAC callback. */
|
||||
static int TpvRecordHmac(WOLFSSL* ssl, byte* digest, const byte* in, word32 sz,
|
||||
int padSz, int content, int verify, int epochOrder)
|
||||
{
|
||||
(void)ssl;
|
||||
(void)digest;
|
||||
(void)in;
|
||||
(void)content;
|
||||
(void)verify;
|
||||
(void)epochOrder;
|
||||
|
||||
tpvHmacCalls++;
|
||||
tpvHmacSz = sz;
|
||||
tpvHmacPadSz = padSz;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The constant time CBC verify path must do the same amount of MAC work for
|
||||
* every value of the attacker controlled padding length byte. TimingPadVerify
|
||||
* passes (pLen - macSz - padLen - 1) to the MAC callback, so the padding length
|
||||
* has to be clamped before that subtraction wraps around. A wrapped length
|
||||
* makes TLS_hmac reject the record before hashing anything, which is a Lucky13
|
||||
* style oracle. */
|
||||
int test_tls_timing_pad_verify_hmac_len(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(WOLFSSL_TEST_STATIC_BUILD) && !defined(NO_HMAC) && \
|
||||
!defined(WOLFSSL_AEAD_ONLY) && !defined(NO_TLS) && \
|
||||
!defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_OLD_TIMINGPADVERIFY) && \
|
||||
!defined(NO_SHA256) && !defined(NO_WOLFSSL_CLIENT)
|
||||
WOLFSSL_CTX* ctx = NULL;
|
||||
WOLFSSL* ssl = NULL;
|
||||
byte record[48];
|
||||
int macSz = WC_SHA256_DIGEST_SIZE;
|
||||
int pLen = (int)sizeof(record);
|
||||
int pad;
|
||||
|
||||
XMEMSET(record, 0, sizeof(record));
|
||||
|
||||
ctx = wolfSSL_CTX_new(wolfSSLv23_client_method());
|
||||
ExpectNotNull(ctx);
|
||||
ssl = wolfSSL_new(ctx);
|
||||
ExpectNotNull(ssl);
|
||||
|
||||
if (EXPECT_SUCCESS()) {
|
||||
ssl->specs.hash_size = WC_SHA256_DIGEST_SIZE;
|
||||
ssl->hmac = TpvRecordHmac;
|
||||
|
||||
for (pad = 0; (pad < 256) && EXPECT_SUCCESS(); pad++) {
|
||||
record[pLen - 1] = (byte)pad;
|
||||
tpvHmacCalls = 0;
|
||||
tpvHmacSz = 0;
|
||||
tpvHmacPadSz = 0;
|
||||
|
||||
(void)TimingPadVerify(ssl, record, pad, macSz, pLen,
|
||||
application_data);
|
||||
|
||||
/* The MAC is always computed, over the whole record. */
|
||||
ExpectIntEQ(tpvHmacCalls, 1);
|
||||
ExpectTrue(tpvHmacSz <= (word32)pLen);
|
||||
ExpectIntEQ((int)tpvHmacSz + macSz + tpvHmacPadSz + 1, pLen);
|
||||
}
|
||||
}
|
||||
|
||||
wolfSSL_free(ssl);
|
||||
wolfSSL_CTX_free(ctx);
|
||||
#endif /* WOLFSSL_TEST_STATIC_BUILD && !NO_HMAC && !WOLFSSL_AEAD_ONLY &&
|
||||
* !NO_TLS && !WOLFSSL_NO_TLS12 && !WOLFSSL_OLD_TIMINGPADVERIFY &&
|
||||
* !NO_SHA256 && !NO_WOLFSSL_CLIENT */
|
||||
return EXPECT_RESULT();
|
||||
} /* END test_tls_timing_pad_verify_hmac_len */
|
||||
|
||||
/*
|
||||
* MC/DC: wc_HmacSizeByType() has its own physical copy of the "which hash
|
||||
* type" compound guard (a second, separately-tracked copy of the same-
|
||||
|
||||
@@ -40,6 +40,7 @@ int test_wc_Sha384HmacSetKey(void);
|
||||
int test_wc_Sha384HmacUpdate(void);
|
||||
int test_wc_Sha384HmacFinal(void);
|
||||
int test_tls_hmac_size_overflow(void);
|
||||
int test_tls_timing_pad_verify_hmac_len(void);
|
||||
int test_wc_HmacSizeByType(void);
|
||||
int test_wc_HmacCopy(void);
|
||||
int test_wc_HmacInit_Id(void);
|
||||
@@ -64,6 +65,7 @@ int test_wc_HKDF_NullKeyEdgeCases(void);
|
||||
TEST_DECL_GROUP("hmac", test_wc_Sha384HmacUpdate), \
|
||||
TEST_DECL_GROUP("hmac", test_wc_Sha384HmacFinal), \
|
||||
TEST_DECL_GROUP("hmac", test_tls_hmac_size_overflow), \
|
||||
TEST_DECL_GROUP("hmac", test_tls_timing_pad_verify_hmac_len), \
|
||||
TEST_DECL_GROUP("hmac", test_wc_HmacSizeByType), \
|
||||
TEST_DECL_GROUP("hmac", test_wc_HmacCopy), \
|
||||
TEST_DECL_GROUP("hmac", test_wc_HmacInit_Id), \
|
||||
|
||||
@@ -157,6 +157,55 @@ static int test_HMAC_CTX_helper(const EVP_MD* type, unsigned char* digest,
|
||||
}
|
||||
#endif /* defined(OPENSSL_EXTRA) && !defined(NO_HMAC) */
|
||||
|
||||
#if defined(OPENSSL_EXTRA) && !defined(NO_HMAC) && !defined(NO_SHA256)
|
||||
/* Returns 1 when every byte of the buffer is zero. */
|
||||
static int test_mac_all_zero(const void* buf, size_t len)
|
||||
{
|
||||
const byte* p = (const byte*)buf;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (p[i] != 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int test_wolfSSL_HMAC_CTX_cleanup_zeroize(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(OPENSSL_EXTRA) && !defined(NO_HMAC) && !defined(NO_SHA256)
|
||||
/* The saved pads are the key combined with the fixed inner and outer
|
||||
* padding, so cleanup has to wipe them along with the HMAC object. */
|
||||
WOLFSSL_HMAC_CTX ctx;
|
||||
unsigned char key[] = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
|
||||
"\x0b\x0b\x0b\x0b\x0b\x0b\x0b";
|
||||
|
||||
ExpectIntEQ(HMAC_CTX_init(&ctx), 1);
|
||||
ExpectIntEQ(HMAC_Init_ex(&ctx, key, (int)sizeof(key) - 1, EVP_sha256(),
|
||||
NULL), 1);
|
||||
|
||||
/* Keying the context must have filled the saved pads. */
|
||||
ExpectIntEQ(test_mac_all_zero(ctx.save_ipad, sizeof(ctx.save_ipad)), 0);
|
||||
ExpectIntEQ(test_mac_all_zero(ctx.save_opad, sizeof(ctx.save_opad)), 0);
|
||||
|
||||
HMAC_CTX_cleanup(&ctx);
|
||||
|
||||
ExpectIntEQ(test_mac_all_zero(ctx.save_ipad, sizeof(ctx.save_ipad)), 1);
|
||||
ExpectIntEQ(test_mac_all_zero(ctx.save_opad, sizeof(ctx.save_opad)), 1);
|
||||
|
||||
/* Re-initializing without a key must now be rejected. Recovering from
|
||||
* the wiped pads would silently MAC with an all zero key. */
|
||||
ExpectIntEQ(HMAC_Init_ex(&ctx, NULL, 0, NULL, NULL), 0);
|
||||
|
||||
HMAC_CTX_cleanup(&ctx);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
int test_wolfSSL_HMAC_CTX(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
|
||||
@@ -25,11 +25,13 @@
|
||||
#include <tests/api/api_decl.h>
|
||||
|
||||
int test_wolfSSL_HMAC_CTX(void);
|
||||
int test_wolfSSL_HMAC_CTX_cleanup_zeroize(void);
|
||||
int test_wolfSSL_HMAC(void);
|
||||
int test_wolfSSL_CMAC(void);
|
||||
|
||||
#define TEST_OSSL_MAC_DECLS \
|
||||
TEST_DECL_GROUP("ossl_mac", test_wolfSSL_HMAC_CTX), \
|
||||
TEST_DECL_GROUP("ossl_mac", test_wolfSSL_HMAC_CTX_cleanup_zeroize), \
|
||||
TEST_DECL_GROUP("ossl_mac", test_wolfSSL_HMAC), \
|
||||
TEST_DECL_GROUP("ossl_mac", test_wolfSSL_CMAC)
|
||||
|
||||
|
||||
@@ -435,6 +435,40 @@ int test_wolfSSL_X509_check_host(void)
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
int test_wolfSSL_X509_check_host_len(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(OPENSSL_EXTRA) && !defined(NO_CERTS) && !defined(NO_FILESYSTEM) \
|
||||
&& !defined(NO_SHA) && !defined(NO_RSA) && defined(WOLFSSL_IP_ALT_NAME)
|
||||
/* chk is length delimited and need not be NUL terminated, so nothing
|
||||
* past chk[chklen - 1] may be read. */
|
||||
X509* x509 = NULL;
|
||||
const char sliced[] = "127.0.0.1extra";
|
||||
const size_t ipLen = 9; /* length of "127.0.0.1" */
|
||||
char* exact = NULL;
|
||||
|
||||
/* cliCertFile has subjectAltName set to 'example.com', '127.0.0.1' */
|
||||
ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(cliCertFile,
|
||||
SSL_FILETYPE_PEM));
|
||||
|
||||
/* An interior slice of a longer buffer must match the iPAddress SAN. */
|
||||
ExpectIntEQ(X509_check_host(x509, sliced, ipLen, 0, NULL),
|
||||
WOLFSSL_SUCCESS);
|
||||
|
||||
/* Same name in a buffer sized exactly to it, with no terminator. */
|
||||
ExpectNotNull(exact = (char*)XMALLOC(ipLen, NULL, DYNAMIC_TYPE_TMP_BUFFER));
|
||||
if (exact != NULL) {
|
||||
XMEMCPY(exact, "127.0.0.1", ipLen);
|
||||
ExpectIntEQ(X509_check_host(x509, exact, ipLen, 0, NULL),
|
||||
WOLFSSL_SUCCESS);
|
||||
}
|
||||
XFREE(exact, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
|
||||
X509_free(x509);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
int test_wolfSSL_X509_check_email(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
|
||||
@@ -33,6 +33,7 @@ int test_wolfSSL_i2d_X509_NAME_canon(void);
|
||||
int test_wolfSSL_X509_subject_name_hash(void);
|
||||
int test_wolfSSL_X509_issuer_name_hash(void);
|
||||
int test_wolfSSL_X509_check_host(void);
|
||||
int test_wolfSSL_X509_check_host_len(void);
|
||||
int test_wolfSSL_X509_check_email(void);
|
||||
int test_wolfSSL_X509(void);
|
||||
int test_wolfSSL_X509_get0_tbs_sigalg(void);
|
||||
@@ -68,6 +69,7 @@ int test_wolfSSL_X509_cmp(void);
|
||||
TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_subject_name_hash), \
|
||||
TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_issuer_name_hash), \
|
||||
TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_check_host), \
|
||||
TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_check_host_len), \
|
||||
TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_check_email), \
|
||||
TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509), \
|
||||
TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_get0_tbs_sigalg), \
|
||||
|
||||
@@ -51,6 +51,24 @@ extern const char* currentTestName;
|
||||
#define HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES_BUILD
|
||||
#endif
|
||||
|
||||
/* With WOLFSSL_RW_THREADED the read path never transmits, so anything a read
|
||||
* schedules to be sent, an ACK in particular, is only sent from the write
|
||||
* side. Stand in for the application, which is required to pump that work
|
||||
* from its write thread. A no-op in builds where reads send for themselves. */
|
||||
#if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_RW_THREADED) && \
|
||||
!defined(WOLFSSL_LEANPSK)
|
||||
#define TEST_DTLS13_PUMP(ssl) \
|
||||
do { \
|
||||
ExpectIntEQ(wolfSSL_dtls13_do_scheduled_work(ssl), \
|
||||
WOLFSSL_SUCCESS); \
|
||||
} while (0)
|
||||
#else
|
||||
#define TEST_DTLS13_PUMP(ssl) \
|
||||
do { \
|
||||
(void)(ssl); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES_BUILD
|
||||
#define TEST_MEMIO_BUF_SZ (64 * 1024)
|
||||
#define TEST_MEMIO_MAX_MSGS 32
|
||||
|
||||
@@ -15131,10 +15131,17 @@ static void ecc_ctx_init(ecEncCtx* ctx, int flags, WC_RNG* rng)
|
||||
WOLFSSL_ABI
|
||||
int wc_ecc_ctx_reset(ecEncCtx* ctx, WC_RNG* rng)
|
||||
{
|
||||
void* heap;
|
||||
|
||||
if (ctx == NULL || rng == NULL)
|
||||
return BAD_FUNC_ARG;
|
||||
|
||||
/* ecc_ctx_init clears the whole context, so carry the heap hint over it.
|
||||
* The context has to be freed to the heap it was allocated from. */
|
||||
heap = ctx->heap;
|
||||
ecc_ctx_init(ctx, ctx->protocol, rng);
|
||||
ctx->heap = heap;
|
||||
|
||||
return ecc_ctx_set_salt(ctx, ctx->protocol);
|
||||
}
|
||||
|
||||
|
||||
+186
-9
@@ -797,6 +797,7 @@ static int wc_HpkeEncap(Hpke* hpke, void* ephemeralKey, void* receiverKey,
|
||||
int ret;
|
||||
#if defined(ECC_TIMING_RESISTANT) && defined(HAVE_ECC)
|
||||
WC_RNG* rng;
|
||||
WC_RNG* prevRng;
|
||||
#endif
|
||||
word32 dh_len;
|
||||
word16 receiverPubKeySz;
|
||||
@@ -850,13 +851,17 @@ static int wc_HpkeEncap(Hpke* hpke, void* ephemeralKey, void* receiverKey,
|
||||
break;
|
||||
}
|
||||
|
||||
wc_ecc_set_rng((ecc_key*)ephemeralKey, rng);
|
||||
prevRng = ((ecc_key*)ephemeralKey)->rng;
|
||||
(void)wc_ecc_set_rng((ecc_key*)ephemeralKey, rng);
|
||||
#endif
|
||||
|
||||
ret = wc_ecc_shared_secret((ecc_key*)ephemeralKey,
|
||||
(ecc_key*)receiverKey, dh, &dh_len);
|
||||
|
||||
#ifdef ECC_TIMING_RESISTANT
|
||||
/* The key belongs to the caller, so put back whatever RNG it had
|
||||
* before this RNG is freed. */
|
||||
(void)wc_ecc_set_rng((ecc_key*)ephemeralKey, prevRng);
|
||||
wc_rng_free(rng);
|
||||
#endif
|
||||
break;
|
||||
@@ -1053,13 +1058,151 @@ int wc_HpkeSealBase(Hpke* hpke, void* ephemeralKey, void* receiverKey,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if (defined(HAVE_ECC) && defined(ECC_TIMING_RESISTANT)) || \
|
||||
(defined(HAVE_CURVE25519) && !defined(NO_SHA256) && \
|
||||
defined(WOLFSSL_CURVE25519_BLINDING))
|
||||
/* Try to make a private-only copy of a receiver key.
|
||||
*
|
||||
* The shared secret computation needs an RNG and the only way to hand it one
|
||||
* is the key's own rng field, so without a copy it would have to write to an
|
||||
* object it does not own. The ECH server passes the key its WOLFSSL_CTX shares
|
||||
* between every connection made from it, where that write races the other
|
||||
* connections: each saves what it finds and restores it afterwards, so one of
|
||||
* them ends up holding an RNG another has already freed.
|
||||
*
|
||||
* Only a plain software key is copied. A key carrying a device id has to keep
|
||||
* reaching that device, and both a copy without the id and an import that
|
||||
* provisions the device would be worse than the write this avoids. Such a key
|
||||
* computes its shared secret on the device, which never reads key->rng, so
|
||||
* leaving it alone costs nothing.
|
||||
*
|
||||
* @param [in] hpke HPKE object, for the KEM in use and the heap hint.
|
||||
* @param [in] key Receiver key to copy.
|
||||
* @param [out] copy The copy, to be released with wc_HpkeFreeKey().
|
||||
* @return 1 when copy holds a usable copy of the private key.
|
||||
* @return 0 when no copy could be made and the original has to be used.
|
||||
*/
|
||||
static int wc_HpkeCopyPrivateKey(Hpke* hpke, void* key, void** copy)
|
||||
{
|
||||
int ret = WC_NO_ERR_TRACE(NOT_COMPILED_IN);
|
||||
#if defined(HAVE_ECC) && defined(ECC_TIMING_RESISTANT)
|
||||
byte eccPriv[ECC_MAXSIZE];
|
||||
word32 eccPrivSz = (word32)sizeof(eccPriv);
|
||||
#endif
|
||||
#if defined(HAVE_CURVE25519) && !defined(NO_SHA256) && \
|
||||
defined(WOLFSSL_CURVE25519_BLINDING)
|
||||
byte x25519Priv[CURVE25519_KEYSIZE];
|
||||
word32 x25519PrivSz = (word32)sizeof(x25519Priv);
|
||||
#endif
|
||||
|
||||
*copy = NULL;
|
||||
|
||||
switch (hpke->kem)
|
||||
{
|
||||
#if defined(HAVE_ECC) && defined(ECC_TIMING_RESISTANT)
|
||||
case DHKEM_P256_HKDF_SHA256:
|
||||
case DHKEM_P384_HKDF_SHA384:
|
||||
case DHKEM_P521_HKDF_SHA512:
|
||||
if (((ecc_key*)key)->dp == NULL)
|
||||
break;
|
||||
/* A key only carries a device id where there is a device to dispatch
|
||||
* to. Everywhere else wc_ecc_shared_secret() is software only, so
|
||||
* there is nothing a copy could take away. */
|
||||
#if defined(PLUTON_CRYPTO_ECC) || defined(WOLF_CRYPTO_CB)
|
||||
if (((ecc_key*)key)->devId != INVALID_DEVID)
|
||||
break;
|
||||
#endif
|
||||
|
||||
ret = wc_ecc_export_private_only((ecc_key*)key, eccPriv,
|
||||
&eccPrivSz);
|
||||
if (ret == 0) {
|
||||
*copy = wc_ecc_key_new(hpke->heap);
|
||||
if (*copy == NULL)
|
||||
ret = MEMORY_E;
|
||||
}
|
||||
#if defined(PLUTON_CRYPTO_ECC) || defined(WOLF_CRYPTO_CB)
|
||||
/* wc_ecc_key_new() adopts a platform default device id on some
|
||||
* ports. The key being copied has none, so the copy must not gain
|
||||
* one, or the shared secret would move onto a device the caller
|
||||
* never asked for. */
|
||||
if (ret == 0)
|
||||
((ecc_key*)*copy)->devId = INVALID_DEVID;
|
||||
#endif
|
||||
if (ret == 0) {
|
||||
/* The public part is not needed: the shared secret takes its
|
||||
* point from the ephemeral key. */
|
||||
ret = wc_ecc_import_private_key_ex(eccPriv, eccPrivSz, NULL, 0,
|
||||
(ecc_key*)*copy, ((ecc_key*)key)->dp->id);
|
||||
}
|
||||
ForceZero(eccPriv, sizeof(eccPriv));
|
||||
break;
|
||||
#endif
|
||||
#if defined(HAVE_CURVE25519) && !defined(NO_SHA256) && \
|
||||
defined(WOLFSSL_CURVE25519_BLINDING)
|
||||
case DHKEM_X25519_HKDF_SHA256:
|
||||
/* As above, the field only exists where a device can be dispatched
|
||||
* to. */
|
||||
#ifdef WOLF_CRYPTO_CB
|
||||
if (((curve25519_key*)key)->devId != INVALID_DEVID)
|
||||
break;
|
||||
#endif
|
||||
|
||||
ret = wc_curve25519_export_private_raw_ex((curve25519_key*)key,
|
||||
x25519Priv, &x25519PrivSz, EC25519_LITTLE_ENDIAN);
|
||||
if (ret == 0) {
|
||||
*copy = XMALLOC(sizeof(curve25519_key), hpke->heap,
|
||||
DYNAMIC_TYPE_CURVE25519);
|
||||
if (*copy == NULL) {
|
||||
ret = MEMORY_E;
|
||||
}
|
||||
else {
|
||||
ret = wc_curve25519_init_ex((curve25519_key*)*copy,
|
||||
hpke->heap, INVALID_DEVID);
|
||||
if (ret != 0) {
|
||||
/* Never initialized, so it must not be freed as a
|
||||
* key. */
|
||||
XFREE(*copy, hpke->heap, DYNAMIC_TYPE_CURVE25519);
|
||||
*copy = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = wc_curve25519_import_private_ex(x25519Priv, x25519PrivSz,
|
||||
(curve25519_key*)*copy, EC25519_LITTLE_ENDIAN);
|
||||
}
|
||||
ForceZero(x25519Priv, sizeof(x25519Priv));
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret != 0 && *copy != NULL) {
|
||||
wc_HpkeFreeKey(hpke, hpke->kem, *copy, hpke->heap);
|
||||
*copy = NULL;
|
||||
}
|
||||
|
||||
return ret == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* compute the shared secret from the ephemeral and receiver kem keys */
|
||||
static int wc_HpkeDecap(Hpke* hpke, void* receiverKey, const byte* pubKey,
|
||||
word16 pubKeySz, byte* sharedSecret)
|
||||
{
|
||||
int ret;
|
||||
#if defined(ECC_TIMING_RESISTANT) || defined(WOLFSSL_CURVE25519_BLINDING)
|
||||
#ifdef HAVE_ECC
|
||||
ecc_key* eccPriv;
|
||||
#endif
|
||||
#if defined(HAVE_CURVE25519) && !defined(NO_SHA256)
|
||||
curve25519_key* x25519Priv;
|
||||
#endif
|
||||
#if (defined(HAVE_ECC) && defined(ECC_TIMING_RESISTANT)) || \
|
||||
(defined(HAVE_CURVE25519) && !defined(NO_SHA256) && \
|
||||
defined(WOLFSSL_CURVE25519_BLINDING))
|
||||
WC_RNG* rng;
|
||||
WC_RNG* prevRng = NULL;
|
||||
void* privCopy = NULL;
|
||||
#endif
|
||||
word32 dh_len;
|
||||
word16 receiverPubKeySz;
|
||||
@@ -1107,6 +1250,7 @@ static int wc_HpkeDecap(Hpke* hpke, void* receiverKey, const byte* pubKey,
|
||||
case DHKEM_P256_HKDF_SHA256:
|
||||
case DHKEM_P384_HKDF_SHA384:
|
||||
case DHKEM_P521_HKDF_SHA512:
|
||||
eccPriv = (ecc_key*)receiverKey;
|
||||
#ifdef ECC_TIMING_RESISTANT
|
||||
rng = wc_rng_new(NULL, 0, hpke->heap);
|
||||
|
||||
@@ -1115,19 +1259,38 @@ static int wc_HpkeDecap(Hpke* hpke, void* receiverKey, const byte* pubKey,
|
||||
break;
|
||||
}
|
||||
|
||||
wc_ecc_set_rng((ecc_key*)receiverKey, rng);
|
||||
/* Work on a copy so that installing the RNG does not write to
|
||||
* the caller's key. A key that cannot be copied has its
|
||||
* private part in a device, and that device computes the
|
||||
* shared secret without ever reading key->rng, so using it as
|
||||
* it is costs nothing. */
|
||||
if (wc_HpkeCopyPrivateKey(hpke, receiverKey, &privCopy))
|
||||
eccPriv = (ecc_key*)privCopy;
|
||||
else
|
||||
prevRng = eccPriv->rng;
|
||||
(void)wc_ecc_set_rng(eccPriv, rng);
|
||||
#endif
|
||||
|
||||
ret = wc_ecc_shared_secret((ecc_key*)receiverKey,
|
||||
(ecc_key*)ephemeralKey, dh, &dh_len);
|
||||
ret = wc_ecc_shared_secret(eccPriv, (ecc_key*)ephemeralKey, dh,
|
||||
&dh_len);
|
||||
|
||||
#ifdef ECC_TIMING_RESISTANT
|
||||
if (privCopy != NULL) {
|
||||
wc_HpkeFreeKey(hpke, hpke->kem, privCopy, hpke->heap);
|
||||
privCopy = NULL;
|
||||
}
|
||||
else {
|
||||
/* The key belongs to the caller, so put back whatever RNG
|
||||
* it had before this RNG is freed. */
|
||||
(void)wc_ecc_set_rng(eccPriv, prevRng);
|
||||
}
|
||||
wc_rng_free(rng);
|
||||
#endif
|
||||
break;
|
||||
#endif
|
||||
#if defined(HAVE_CURVE25519) && !defined(NO_SHA256)
|
||||
case DHKEM_X25519_HKDF_SHA256:
|
||||
x25519Priv = (curve25519_key*)receiverKey;
|
||||
#ifdef WOLFSSL_CURVE25519_BLINDING
|
||||
rng = wc_rng_new(NULL, 0, hpke->heap);
|
||||
|
||||
@@ -1136,12 +1299,26 @@ static int wc_HpkeDecap(Hpke* hpke, void* receiverKey, const byte* pubKey,
|
||||
break;
|
||||
}
|
||||
|
||||
wc_curve25519_set_rng((curve25519_key*)receiverKey, rng);
|
||||
/* As above: prefer a copy so the caller's key is left alone. */
|
||||
if (wc_HpkeCopyPrivateKey(hpke, receiverKey, &privCopy))
|
||||
x25519Priv = (curve25519_key*)privCopy;
|
||||
else
|
||||
prevRng = x25519Priv->rng;
|
||||
(void)wc_curve25519_set_rng(x25519Priv, rng);
|
||||
#endif
|
||||
ret = wc_curve25519_shared_secret_ex(
|
||||
(curve25519_key*)receiverKey, (curve25519_key*)ephemeralKey,
|
||||
dh, &dh_len, EC25519_LITTLE_ENDIAN);
|
||||
ret = wc_curve25519_shared_secret_ex(x25519Priv,
|
||||
(curve25519_key*)ephemeralKey, dh, &dh_len,
|
||||
EC25519_LITTLE_ENDIAN);
|
||||
#ifdef WOLFSSL_CURVE25519_BLINDING
|
||||
if (privCopy != NULL) {
|
||||
wc_HpkeFreeKey(hpke, hpke->kem, privCopy, hpke->heap);
|
||||
privCopy = NULL;
|
||||
}
|
||||
else {
|
||||
/* The key belongs to the caller, so put back whatever RNG
|
||||
* it had before this RNG is freed. */
|
||||
(void)wc_curve25519_set_rng(x25519Priv, prevRng);
|
||||
}
|
||||
wc_rng_free(rng);
|
||||
#endif
|
||||
break;
|
||||
|
||||
@@ -115,6 +115,9 @@ WOLFSSL_API int wc_rng_bank_init(
|
||||
ctx->n_rngs = n_rngs;
|
||||
|
||||
for (i = 0; i < n_rngs; ++i) {
|
||||
/* The nonce is the address of the instance, so it has to be taken
|
||||
* from a pointer to it, not from the instance itself. */
|
||||
struct wc_rng_bank_inst *rng_inst = ctx->rngs + i;
|
||||
#ifdef WC_VERBOSE_RNG
|
||||
int nretries = 0;
|
||||
#endif
|
||||
@@ -125,8 +128,8 @@ WOLFSSL_API int wc_rng_bank_init(
|
||||
if (flags & WC_RNG_BANK_FLAG_NO_VECTOR_OPS)
|
||||
need_reenable_vec = (DISABLE_VECTOR_REGISTERS() == 0);
|
||||
ret = wc_InitRngNonce_ex(
|
||||
WC_RNG_BANK_INST_TO_RNG(ctx->rngs + i),
|
||||
(byte *)&ctx->rngs[i], sizeof(byte *), heap, devId);
|
||||
WC_RNG_BANK_INST_TO_RNG(rng_inst),
|
||||
(byte *)&rng_inst, sizeof(byte *), heap, devId);
|
||||
|
||||
if (need_reenable_vec)
|
||||
REENABLE_VECTOR_REGISTERS();
|
||||
|
||||
+7
-5
@@ -745,6 +745,7 @@ int wc_SrpComputeKey(Srp* srp, byte* clientPubKey, word32 clientPubKeySz,
|
||||
byte pad = 0;
|
||||
int r;
|
||||
int hashInited = 0;
|
||||
int mpInited = 0;
|
||||
|
||||
/* validating params */
|
||||
|
||||
@@ -776,6 +777,7 @@ int wc_SrpComputeKey(Srp* srp, byte* clientPubKey, word32 clientPubKeySz,
|
||||
r = MP_INIT_E;
|
||||
goto out;
|
||||
}
|
||||
mpInited = 1;
|
||||
|
||||
if (mp_iszero(&srp->priv) == MP_YES) {
|
||||
r = SRP_CALL_ORDER_E;
|
||||
@@ -943,27 +945,27 @@ int wc_SrpComputeKey(Srp* srp, byte* clientPubKey, word32 clientPubKeySz,
|
||||
XFREE(hash, srp->heap, DYNAMIC_TYPE_SRP);
|
||||
XFREE(digest, srp->heap, DYNAMIC_TYPE_SRP);
|
||||
if (u) {
|
||||
if (r != WC_NO_ERR_TRACE(MP_INIT_E))
|
||||
if (mpInited)
|
||||
mp_forcezero(u);
|
||||
XFREE(u, srp->heap, DYNAMIC_TYPE_SRP);
|
||||
}
|
||||
if (s) {
|
||||
if (r != WC_NO_ERR_TRACE(MP_INIT_E))
|
||||
if (mpInited)
|
||||
mp_forcezero(s);
|
||||
XFREE(s, srp->heap, DYNAMIC_TYPE_SRP);
|
||||
}
|
||||
if (temp1) {
|
||||
if (r != WC_NO_ERR_TRACE(MP_INIT_E))
|
||||
if (mpInited)
|
||||
mp_forcezero(temp1);
|
||||
XFREE(temp1, srp->heap, DYNAMIC_TYPE_SRP);
|
||||
}
|
||||
if (temp2) {
|
||||
if (r != WC_NO_ERR_TRACE(MP_INIT_E))
|
||||
if (mpInited)
|
||||
mp_forcezero(temp2);
|
||||
XFREE(temp2, srp->heap, DYNAMIC_TYPE_SRP);
|
||||
}
|
||||
#else
|
||||
if (r != WC_NO_ERR_TRACE(MP_INIT_E)) {
|
||||
if (mpInited) {
|
||||
mp_forcezero(u);
|
||||
mp_forcezero(s);
|
||||
mp_forcezero(temp1);
|
||||
|
||||
@@ -37594,6 +37594,55 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t x963kdf_test(void)
|
||||
defined(HAVE_CURVE448)) && \
|
||||
defined(HAVE_AESGCM)
|
||||
|
||||
/* Install rng on an HPKE kem key, for the key types that keep one. */
|
||||
static void hpke_test_set_key_rng(int kem, void* key, WC_RNG* rng)
|
||||
{
|
||||
switch (kem) {
|
||||
#if defined(HAVE_ECC) && defined(ECC_TIMING_RESISTANT)
|
||||
case DHKEM_P256_HKDF_SHA256:
|
||||
case DHKEM_P384_HKDF_SHA384:
|
||||
case DHKEM_P521_HKDF_SHA512:
|
||||
(void)wc_ecc_set_rng((ecc_key*)key, rng);
|
||||
break;
|
||||
#endif
|
||||
#if defined(HAVE_CURVE25519) && defined(WOLFSSL_CURVE25519_BLINDING)
|
||||
case DHKEM_X25519_HKDF_SHA256:
|
||||
(void)wc_curve25519_set_rng((curve25519_key*)key, rng);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
(void)key;
|
||||
(void)rng;
|
||||
}
|
||||
|
||||
/* Returns 1 when the key still holds rng, or when this key type does not keep
|
||||
* an RNG in this build. */
|
||||
static int hpke_test_key_rng_kept(int kem, void* key, WC_RNG* rng)
|
||||
{
|
||||
switch (kem) {
|
||||
#if defined(HAVE_ECC) && defined(ECC_TIMING_RESISTANT)
|
||||
case DHKEM_P256_HKDF_SHA256:
|
||||
case DHKEM_P384_HKDF_SHA384:
|
||||
case DHKEM_P521_HKDF_SHA512:
|
||||
return ((ecc_key*)key)->rng == rng;
|
||||
#endif
|
||||
#if defined(HAVE_CURVE25519) && defined(WOLFSSL_CURVE25519_BLINDING)
|
||||
case DHKEM_X25519_HKDF_SHA256:
|
||||
return ((curve25519_key*)key)->rng == rng;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
(void)key;
|
||||
(void)rng;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* test null/bad arguments for wc_HpkeInit, a one-shot seal/open round-trip
|
||||
* with wc_HpkeSealBase/wc_HpkeOpenBase, and auth failure cases (wrong info,
|
||||
* wrong AAD, tampered ciphertext, wrong receiver key) */
|
||||
@@ -37689,6 +37738,14 @@ static wc_test_ret_t hpke_test_single(Hpke* hpke, int kem, int kdf, int aead)
|
||||
ret = WC_TEST_RET_ENC_EC(ret);
|
||||
}
|
||||
|
||||
/* The keys belong to the caller, so give them an RNG of our own. HPKE
|
||||
* borrows the key to compute the shared secret and must hand it back
|
||||
* unchanged. */
|
||||
if (ret == 0) {
|
||||
hpke_test_set_key_rng(kem, ephemeralKey, rng);
|
||||
hpke_test_set_key_rng(kem, receiverKey, rng);
|
||||
}
|
||||
|
||||
/* Negative test case with NULL argument */
|
||||
if (ret == 0) {
|
||||
ret = wc_HpkeSealBase(NULL, ephemeralKey, receiverKey,
|
||||
@@ -37976,6 +38033,13 @@ static wc_test_ret_t hpke_test_single(Hpke* hpke, int kem, int kdf, int aead)
|
||||
ret = WC_TEST_RET_ENC_NC;
|
||||
}
|
||||
|
||||
/* Seal and open install a temporary RNG on the caller's key and then free
|
||||
* it. Neither key may be left pointing at that freed RNG. */
|
||||
if (ret == 0 && !hpke_test_key_rng_kept(kem, ephemeralKey, rng))
|
||||
ret = WC_TEST_RET_ENC_NC;
|
||||
if (ret == 0 && !hpke_test_key_rng_kept(kem, receiverKey, rng))
|
||||
ret = WC_TEST_RET_ENC_NC;
|
||||
|
||||
if (ephemeralKey != NULL)
|
||||
wc_HpkeFreeKey(hpke, hpke->kem, ephemeralKey, hpke->heap);
|
||||
if (receiverKey != NULL)
|
||||
@@ -38037,6 +38101,13 @@ static wc_test_ret_t hpke_test_multi(Hpke* hpke)
|
||||
if (ret == 0)
|
||||
ret = wc_HpkeGenerateKeyPair(hpke, &receiverKey, rng);
|
||||
|
||||
/* The context API reaches the same encap and decap that the one-shot API
|
||||
* does, so give the keys an RNG to watch here too. */
|
||||
if (ret == 0) {
|
||||
hpke_test_set_key_rng(hpke->kem, ephemeralKey, rng);
|
||||
hpke_test_set_key_rng(hpke->kem, receiverKey, rng);
|
||||
}
|
||||
|
||||
/* Negative test case with NULL argument */
|
||||
if (ret == 0) {
|
||||
ret = wc_HpkeInitSealContext(NULL, context, ephemeralKey, receiverKey,
|
||||
@@ -38395,6 +38466,13 @@ static wc_test_ret_t hpke_test_multi(Hpke* hpke)
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
/* Seal and open install a temporary RNG on the caller's key and then free
|
||||
* it. Neither key may be left pointing at that freed RNG. */
|
||||
if (ret == 0 && !hpke_test_key_rng_kept(hpke->kem, ephemeralKey, rng))
|
||||
ret = WC_TEST_RET_ENC_NC;
|
||||
if (ret == 0 && !hpke_test_key_rng_kept(hpke->kem, receiverKey, rng))
|
||||
ret = WC_TEST_RET_ENC_NC;
|
||||
|
||||
if (ephemeralKey != NULL)
|
||||
wc_HpkeFreeKey(hpke, hpke->kem, ephemeralKey, hpke->heap);
|
||||
if (receiverKey != NULL)
|
||||
|
||||
@@ -7285,6 +7285,10 @@ WOLFSSL_LOCAL word32 MacSize(const WOLFSSL* ssl);
|
||||
word32 fragOffset);
|
||||
WOLFSSL_LOCAL int VerifyForTxDtlsMsgDelete(WOLFSSL* ssl, DtlsMsg* item);
|
||||
WOLFSSL_LOCAL void DtlsMsgPoolReset(WOLFSSL* ssl);
|
||||
WOLFSSL_LOCAL int wolfssl_local_SockAddrSet(WOLFSSL_SOCKADDR* sockAddr,
|
||||
void* peer,
|
||||
unsigned int peerSz,
|
||||
void* heap);
|
||||
WOLFSSL_LOCAL int DtlsMsgPoolSend(WOLFSSL* ssl, int sendOnlyFirstPacket);
|
||||
WOLFSSL_LOCAL void DtlsMsgDestroyFragBucket(DtlsFragBucket* fragBucket, void* heap);
|
||||
WOLFSSL_LOCAL int GetDtlsHandShakeHeader(WOLFSSL *ssl, const byte *input,
|
||||
|
||||
@@ -1847,6 +1847,13 @@ WOLFSSL_API int wolfSSL_dtls_set_timeout_init(WOLFSSL* ssl, int timeout);
|
||||
WOLFSSL_API int wolfSSL_dtls_set_timeout_max(WOLFSSL* ssl, int timeout);
|
||||
WOLFSSL_API int wolfSSL_dtls_got_timeout(WOLFSSL* ssl);
|
||||
WOLFSSL_API int wolfSSL_dtls_retransmit(WOLFSSL* ssl);
|
||||
/* Defined alongside the other DTLS calls, inside the !WOLFSSL_LEANPSK block of
|
||||
* ssl_api_dtls.c, so gate the declaration the same way rather than promising a
|
||||
* symbol that build does not provide. */
|
||||
#if defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_LEANPSK)
|
||||
WOLFSSL_API int wolfSSL_dtls13_pending_work(WOLFSSL* ssl);
|
||||
WOLFSSL_API int wolfSSL_dtls13_do_scheduled_work(WOLFSSL* ssl);
|
||||
#endif
|
||||
WOLFSSL_API int wolfSSL_dtls(WOLFSSL* ssl);
|
||||
|
||||
WOLFSSL_API void* wolfSSL_dtls_create_peer(int port, char* ip);
|
||||
|
||||
Reference in New Issue
Block a user