mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-05 20:30:48 +02:00
38b52d8079
### `wolfssl/internal.h`
- **`InternalTicket` struct gains a flexible array member**: A new `peerCert[]` field (with a preceding `peerCertLen[2]`) is added to `InternalTicket`. This allows the peer's DER-encoded certificate to be stored directly inside the session ticket.
- **`ExternalTicket` struct becomes variable-length**: The `enc_ticket` field is changed from a fixed-size array to a flexible array member (`byte enc_ticket[]`). The `mac` field is removed from the struct — the MAC is now placed dynamically after the encrypted data in `enc_ticket`.
### `src/internal.c`
- The `GetRecordHeader` function now only adds `MAX_COMP_EXTRA` to the maximum allowed record size when `ssl->options.usingCompression` is true, tightening the length validation. The max fragment length extension check is now much stricter.
- **Peer certificate is serialized into the ticket**: During ticket creation, the code attempts to find the peer certificate from `ssl->peerCert` or from `ssl->session->chain` (fallback). If found and within `MAX_TICKET_PEER_CERT_SZ`, it's copied into `it->peerCert`. DTLS is explicitly excluded (peer cert length set to 0) to keep ticket size small for MTU constraints. If `HAVE_MAX_FRAGMENT` is defined and max fragment is not `MAX_RECORD_SIZE` for TLS 1.3, the cert is also skipped since `SendTls13NewSessionTicket` doesn't support fragmentation yet.
- **Peer certificate restoration from ticket**: On successful ticket decryption, if the ticket contains a peer certificate (`peerCertLen > 0`), it is decoded back into `ssl->peerCert` via `ParseCertRelative`/`CopyDecodedToX509`, and also added to `ssl->session->chain` via `AddSessionCertToChain`.
- The `CLEAR_ASN_NO_PEM_HEADER_ERROR` macro was rewritten to loop and remove all consecutive PEM no-start-line errors (not just the last one), wrapped in a `do { ... } while(0)` for safety.
- The `SendTicket` function is simplified to use `SendHandshakeMsg` to support fragmenting the larger ticket.
---
### `src/x509.c`
- `loadX509orX509REQFromPemBio` now accepts `TRUSTED_CERT_TYPE` in addition to `CERT_TYPE` and `CERTREQ_TYPE`.
- **Streaming BIO support**: When `wolfSSL_BIO_get_len()` returns ≤ 0 (e.g., pipes/FIFOs), the function no longer returns an error. Instead, it sets an initial buffer of `MAX_X509_SIZE` and dynamically grows (doubling) up to `MAX_BIO_READ_BUFFER` (`MAX_X509_SIZE * 16`) as data is read byte-by-byte.
- **Alternate footer detection**: For `TRUSTED_CERT_TYPE`, the PEM reader also checks for the regular `CERT_TYPE` footer (`-----END CERTIFICATE-----`) in addition to the trusted cert footer (`-----END TRUSTED CERTIFICATE-----`), so it can parse either format.
- Removed two lines that set `cert->srcIdx` to `SIGALGO_SEQ` offset. This makes `cert->srcIdx` reflect the end of parsed certificate data. This is used by `loadX509orX509REQFromBuffer` to detect where auxiliary trust data begins in trusted certificates.
---
### `src/ssl_sk.c`
- Added a `STACK_TYPE_X509_CRL` case to `wolfssl_sk_dup_data` that calls `wolfSSL_X509_CRL_dup` for deep-copying CRL stack elements. Previously, `STACK_TYPE_X509_CRL` fell through to the unsupported default case.
---
### `wolfssl/openssl/ssl.h`
- `sk_X509_dup` now maps to `wolfSSL_shallow_sk_dup` (was `wolfSSL_sk_dup`/deep copy). This matches OpenSSL's behavior where `sk_X509_dup` does a shallow copy.
- `sk_SSL_CIPHER_dup` similarly changed to `wolfSSL_shallow_sk_dup`.
---
### `src/ssl_api_cert.c`
- When `ssl->ourCert` is `NULL` and the SSL owns its cert, the function now checks if `ssl->ctx->ourCert` points to the same certificate (by comparing DER buffers). If so, it returns the ctx's `X509` pointer directly. This maintains pointer compatibility for applications (like nginx OCSP stapling) that use the `X509*` from `SSL_CTX_use_certificate` as a lookup key.
### `src/bio.c`
- When `wolfssl_file_len` returns `WOLFSSL_BAD_FILETYPE` (now returned for pipes/FIFOs), `wolfSSL_BIO_get_len` treats it as length 0 instead of propagating the error.
---
### `tests/test-maxfrag.conf` and `tests/test-maxfrag-dtls.conf`
- Removed `DHE-RSA-AES256-GCM-SHA384` test entries because the ClientKeyExchange doesn't fit in the selected max fragment length.
512 lines
14 KiB
C
512 lines
14 KiB
C
/* ssl_misc.c
|
|
*
|
|
* Copyright (C) 2006-2026 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfSSL.
|
|
*
|
|
* wolfSSL is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfSSL is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
|
*/
|
|
|
|
#include <wolfssl/wolfcrypt/libwolfssl_sources.h>
|
|
|
|
#if !defined(WOLFSSL_SSL_MISC_INCLUDED)
|
|
#ifndef WOLFSSL_IGNORE_FILE_WARN
|
|
#warning ssl_misc.c does not need to be compiled separately from ssl.c
|
|
#endif
|
|
#else
|
|
|
|
#if defined(OPENSSL_EXTRA) && !defined(WOLFCRYPT_ONLY)
|
|
#ifndef NO_BIO
|
|
|
|
/* Amount of memory to allocate/add. */
|
|
#define READ_BIO_FILE_CHUNK 128
|
|
|
|
/* Read a file in chunks.
|
|
*
|
|
* Allocates a chunk and reads into it until it is full.
|
|
*
|
|
* @param [in, out] bio BIO object to read with.
|
|
* @param [out] data Read data in a new buffer.
|
|
* @return Negative on error.
|
|
* @return Number of bytes read on success.
|
|
*/
|
|
static int wolfssl_read_bio_file(WOLFSSL_BIO* bio, char** data)
|
|
{
|
|
int ret = 0;
|
|
char* mem;
|
|
char* p;
|
|
|
|
/* Allocate buffer to hold a chunk of data. */
|
|
mem = (char*)XMALLOC(READ_BIO_FILE_CHUNK, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (mem == NULL) {
|
|
WOLFSSL_ERROR_MSG("Memory allocation error");
|
|
ret = MEMORY_E;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
int sz;
|
|
|
|
/* ret is the number of bytes read and is zero. */
|
|
|
|
/* p is where to read in next chunk. */
|
|
p = mem;
|
|
/* Memory available to read into is one chunk. */
|
|
sz = READ_BIO_FILE_CHUNK;
|
|
/* Keep reading in chunks until no more or an error. */
|
|
while ((sz = wolfSSL_BIO_read(bio, p, sz)) > 0) {
|
|
int remaining;
|
|
|
|
/* Update total read. */
|
|
ret += sz;
|
|
/* Calculate remaining unused memory. */
|
|
remaining = READ_BIO_FILE_CHUNK - (ret % READ_BIO_FILE_CHUNK);
|
|
/* Check for space remaining. */
|
|
if (remaining != READ_BIO_FILE_CHUNK) {
|
|
/* Update where data is read into. */
|
|
p += sz;
|
|
/* Maximum possible size is the remaining buffer size. */
|
|
sz = remaining;
|
|
}
|
|
else {
|
|
/* No space left for more data to be read - add a chunk. */
|
|
#ifdef WOLFSSL_NO_REALLOC
|
|
p = (char*)XMALLOC(ret + READ_BIO_FILE_CHUNK, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (p != NULL) {
|
|
XMEMCPY(p, mem, ret);
|
|
XFREE(mem, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
mem = NULL;
|
|
}
|
|
#else
|
|
p = (char*)XREALLOC(mem, ret + READ_BIO_FILE_CHUNK, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
#endif
|
|
if (p == NULL) {
|
|
sz = MEMORY_E;
|
|
break;
|
|
}
|
|
|
|
/* Set mem to new pointer. */
|
|
mem = p;
|
|
/* Set p to where to read in next chunk. */
|
|
p += ret;
|
|
/* Read in a new chunk. */
|
|
sz = READ_BIO_FILE_CHUNK;
|
|
}
|
|
}
|
|
if ((sz < 0) || (ret == 0)) {
|
|
/* Dispose of memory on error or no data read. */
|
|
XFREE(mem, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
mem = NULL;
|
|
/* Return error. */
|
|
ret = sz;
|
|
}
|
|
}
|
|
|
|
*data = mem;
|
|
return ret;
|
|
}
|
|
|
|
/* Read exactly the required amount into a newly allocated buffer.
|
|
*
|
|
* @param [in, out] bio BIO object to read with.
|
|
* @param [in sz Amount of data to read.
|
|
* @param [out] data Read data in a new buffer.
|
|
* @return Negative on error.
|
|
* @return Number of bytes read on success.
|
|
*/
|
|
static int wolfssl_read_bio_len(WOLFSSL_BIO* bio, int sz, char** data)
|
|
{
|
|
int ret = 0;
|
|
char* mem;
|
|
|
|
/* Allocate buffer to hold data. */
|
|
mem = (char*)XMALLOC((size_t)sz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (mem == NULL) {
|
|
WOLFSSL_ERROR_MSG("Memory allocation error");
|
|
ret = MEMORY_E;
|
|
}
|
|
else if ((ret = wolfSSL_BIO_read(bio, mem, sz)) != sz) {
|
|
/* Pending data not read. */
|
|
XFREE(mem, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
mem = NULL;
|
|
ret = MEMORY_E;
|
|
}
|
|
|
|
*data = mem;
|
|
return ret;
|
|
}
|
|
|
|
/* Read all the data from a BIO.
|
|
*
|
|
* @param [in, out] bio BIO object to read with.
|
|
* @param [out] data Read data in a buffer.
|
|
* @param [out] dataSz Amount of data read in bytes.
|
|
* @param [out] memAlloced Indicates whether return buffer was allocated.
|
|
* @return Negative on error.
|
|
* @return 0 on success.
|
|
*/
|
|
static int wolfssl_read_bio(WOLFSSL_BIO* bio, char** data, int* dataSz,
|
|
int* memAlloced)
|
|
{
|
|
int ret;
|
|
int sz;
|
|
|
|
if (bio->type == WOLFSSL_BIO_MEMORY) {
|
|
ret = wolfSSL_BIO_get_mem_data(bio, data);
|
|
if (ret > 0) {
|
|
/* Advance the write index in the memory bio */
|
|
WOLFSSL_BIO* mem_bio = bio;
|
|
for (; mem_bio != NULL; mem_bio = mem_bio->next) {
|
|
if (mem_bio->type == WOLFSSL_BIO_MEMORY)
|
|
break;
|
|
}
|
|
if (mem_bio == NULL)
|
|
mem_bio = bio; /* Default to input */
|
|
mem_bio->rdIdx += ret;
|
|
}
|
|
*memAlloced = 0;
|
|
}
|
|
/* Get pending or, when a file BIO, get length of file. */
|
|
else if ((sz = wolfSSL_BIO_pending(bio)) > 0) {
|
|
ret = wolfssl_read_bio_len(bio, sz, data);
|
|
if (ret > 0) {
|
|
*memAlloced = 1;
|
|
}
|
|
}
|
|
else if (bio->type == WOLFSSL_BIO_FILE) {
|
|
ret = wolfssl_read_bio_file(bio, data);
|
|
if (ret > 0) {
|
|
*memAlloced = 1;
|
|
}
|
|
}
|
|
else {
|
|
WOLFSSL_ERROR_MSG("No data read from bio");
|
|
*memAlloced = 0;
|
|
ret = NOT_COMPILED_IN;
|
|
}
|
|
|
|
if (ret >= 0) {
|
|
*dataSz = ret;
|
|
ret = 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* !NO_BIO */
|
|
#endif /* OPENSSL_EXTRA && !WOLFCRYPT_ONLY */
|
|
|
|
#if (defined(OPENSSL_EXTRA) || defined(PERSIST_CERT_CACHE) || \
|
|
!defined(NO_CERTS)) && !defined(WOLFCRYPT_ONLY) && !defined(NO_FILESYSTEM)
|
|
/* Read all the data from a file.
|
|
*
|
|
* @param [in] fp File pointer to read with.
|
|
* @param [out] fileSz Amount of data remaining in file in bytes.
|
|
* @return WOLFSSL_BAD_FILE on error.
|
|
* @return 0 on success.
|
|
*/
|
|
static int wolfssl_file_len(XFILE fp, long* fileSz)
|
|
{
|
|
int ret = 0;
|
|
long sz = 0;
|
|
long curr = 0;
|
|
|
|
if (fp == XBADFILE) {
|
|
ret = WOLFSSL_BAD_FILE;
|
|
}
|
|
if (ret == 0) {
|
|
/* Get file offset at end of file. */
|
|
curr = (long)XFTELL(fp);
|
|
if (curr < 0) {
|
|
#ifdef ESPIPE
|
|
if (errno == ESPIPE) {
|
|
WOLFSSL_ERROR_MSG("wolfssl_file_len: file is a pipe");
|
|
*fileSz = 0;
|
|
ret = WOLFSSL_BAD_FILETYPE;
|
|
}
|
|
else
|
|
#endif
|
|
ret = WOLFSSL_BAD_FILE;
|
|
}
|
|
}
|
|
/* Move to end of file. */
|
|
if ((ret == 0) && (XFSEEK(fp, 0, SEEK_END) != 0)) {
|
|
ret = WOLFSSL_BAD_FILE;
|
|
}
|
|
if (ret == 0) {
|
|
/* Get file offset at end of file and subtract current offset. */
|
|
sz = (long)XFTELL(fp) - curr;
|
|
if (sz < 0) {
|
|
ret = WOLFSSL_BAD_FILE;
|
|
}
|
|
}
|
|
/* Go back to original offset in file. */
|
|
if ((ret == 0) && (XFSEEK(fp, curr, SEEK_SET) != 0)) {
|
|
ret = WOLFSSL_BAD_FILE;
|
|
}
|
|
/* Validate size. */
|
|
if ((ret == 0) && ((sz > MAX_WOLFSSL_FILE_SIZE) || (sz <= 0L))) {
|
|
ret = WOLFSSL_BAD_FILE;
|
|
}
|
|
if (ret == 0) {
|
|
*fileSz = sz;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#if (defined(OPENSSL_EXTRA) || defined(PERSIST_CERT_CACHE)) && \
|
|
!defined(WOLFCRYPT_ONLY) && !defined(NO_FILESYSTEM)
|
|
/* Read all the data from a file.
|
|
*
|
|
* @param [in] fp File pointer to read with.
|
|
* @param [out] data Read data in an allocated buffer.
|
|
* @param [out] dataSz Amount of data read in bytes.
|
|
* @return WOLFSSL_BAD_FILE when reading fails.
|
|
* @return MEMORY_E when memory allocation fails.
|
|
* @return 0 on success.
|
|
*/
|
|
static int wolfssl_read_file(XFILE fp, char** data, int* dataSz)
|
|
{
|
|
int ret = 0;
|
|
long sz = 0;
|
|
char* mem = NULL;
|
|
|
|
ret = wolfssl_file_len(fp, &sz);
|
|
if (ret == 0) {
|
|
/* Allocate memory big enough to hold whole file. */
|
|
mem = (char*)XMALLOC((size_t)sz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (mem == NULL) {
|
|
ret = MEMORY_E;
|
|
}
|
|
}
|
|
/* Read whole file into new buffer. */
|
|
if ((ret == 0) && ((int)XFREAD(mem, 1, (size_t)sz, fp) != sz)) {
|
|
ret = WOLFSSL_BAD_FILE;
|
|
}
|
|
if (ret == 0) {
|
|
*dataSz = (int)sz;
|
|
*data = mem;
|
|
mem = NULL;
|
|
}
|
|
|
|
XFREE(mem, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
return ret;
|
|
}
|
|
#endif /* (OPENSSL_EXTRA || PERSIST_CERT_CACHE) && !WOLFCRYPT_ONLY &&
|
|
* !NO_FILESYSTEM */
|
|
|
|
#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS)
|
|
|
|
#ifdef WOLFSSL_SMALL_STACK
|
|
|
|
/* Buffer and size with no stack buffer. */
|
|
typedef struct {
|
|
/* Dynamically allocated buffer. */
|
|
byte* buffer;
|
|
/* Size of buffer in bytes. */
|
|
word32 sz;
|
|
} StaticBuffer;
|
|
|
|
/* Initialize static buffer.
|
|
*
|
|
* @param [in, out] sb Static buffer.
|
|
*/
|
|
static void static_buffer_init(StaticBuffer* sb)
|
|
{
|
|
sb->buffer = NULL;
|
|
sb->sz = 0;
|
|
}
|
|
|
|
/* Set the size of the buffer.
|
|
*
|
|
* Can only set size once.
|
|
*
|
|
* @param [in] sb Static buffer.
|
|
* @param [in] len Length required.
|
|
* @param [in] heap Dynamic memory allocation hint.
|
|
* @param [in] type Type of dynamic memory.
|
|
* @return 0 on success.
|
|
* @return MEMORY_E when dynamic memory allocation fails.
|
|
*/
|
|
static int static_buffer_set_size(StaticBuffer* sb, word32 len, void* heap,
|
|
int type)
|
|
{
|
|
int ret = 0;
|
|
|
|
(void)heap;
|
|
(void)type;
|
|
|
|
sb->buffer = (byte*)XMALLOC(len, heap, type);
|
|
if (sb->buffer == NULL) {
|
|
ret = MEMORY_E;
|
|
}
|
|
else {
|
|
sb->sz = len;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Dispose of dynamically allocated buffer.
|
|
*
|
|
* @param [in] sb Static buffer.
|
|
* @param [in] heap Dynamic memory allocation hint.
|
|
* @param [in] type Type of dynamic memory.
|
|
*/
|
|
static void static_buffer_free(StaticBuffer* sb, void* heap, int type)
|
|
{
|
|
(void)heap;
|
|
(void)type;
|
|
XFREE(sb->buffer, heap, type);
|
|
}
|
|
|
|
#else
|
|
|
|
/* Buffer and size with stack buffer set and option to dynamically allocate. */
|
|
typedef struct {
|
|
/* Stack or heap buffer. */
|
|
byte* buffer;
|
|
/* Size of buffer in bytes. */
|
|
word32 sz;
|
|
/* Indicates whether the buffer was dynamically allocated. */
|
|
int dyn;
|
|
} StaticBuffer;
|
|
|
|
/* Initialize static buffer.
|
|
*
|
|
* @param [in, out] sb Static buffer.
|
|
* @param [in] stackBuffer Buffer allocated on the stack.
|
|
* @param [in] len Length of stack buffer.
|
|
*/
|
|
static void static_buffer_init(StaticBuffer* sb, byte* stackBuffer, word32 len)
|
|
{
|
|
sb->buffer = stackBuffer;
|
|
sb->sz = len;
|
|
sb->dyn = 0;
|
|
}
|
|
|
|
/* Set the size of the buffer.
|
|
*
|
|
* Pre: Buffer on the stack set with its size.
|
|
* Can only set size once.
|
|
*
|
|
* @param [in] sb Static buffer.
|
|
* @param [in] len Length required.
|
|
* @param [in] heap Dynamic memory allocation hint.
|
|
* @param [in] type Type of dynamic memory.
|
|
* @return 0 on success.
|
|
* @return MEMORY_E when dynamic memory allocation fails.
|
|
*/
|
|
static int static_buffer_set_size(StaticBuffer* sb, word32 len, void* heap,
|
|
int type)
|
|
{
|
|
int ret = 0;
|
|
|
|
(void)heap;
|
|
(void)type;
|
|
|
|
if (len > sb->sz) {
|
|
byte* buff = (byte*)XMALLOC(len, heap, type);
|
|
if (buff == NULL) {
|
|
ret = MEMORY_E;
|
|
}
|
|
else {
|
|
sb->buffer = buff;
|
|
sb->sz = len;
|
|
sb->dyn = 1;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Dispose of dynamically allocated buffer.
|
|
*
|
|
* @param [in] sb Static buffer.
|
|
* @param [in] heap Dynamic memory allocation hint.
|
|
* @param [in] type Type of dynamic memory.
|
|
*/
|
|
static void static_buffer_free(StaticBuffer* sb, void* heap, int type)
|
|
{
|
|
(void)heap;
|
|
(void)type;
|
|
|
|
if (sb->dyn) {
|
|
XFREE(sb->buffer, heap, type);
|
|
}
|
|
}
|
|
|
|
#endif /* WOLFSSL_SMALL_STACK */
|
|
|
|
#ifndef NO_FILESYSTEM
|
|
|
|
/* Read all the data from a file into content.
|
|
*
|
|
* @param [in] fname File pointer to read with.
|
|
* @param [in, out] content Read data in an allocated buffer.
|
|
* @param [in] heap Dynamic memory allocation hint.
|
|
* @param [in] type Type of dynamic memory.
|
|
* @param [out] size Amount of data read in bytes.
|
|
* @return 0 on success.
|
|
* @return WOLFSSL_BAD_FILE when reading fails.
|
|
* @return MEMORY_E when memory allocation fails.
|
|
*/
|
|
static int wolfssl_read_file_static(const char* fname, StaticBuffer* content,
|
|
void* heap, int type, long* size)
|
|
{
|
|
int ret = 0;
|
|
XFILE file = XBADFILE;
|
|
long sz = 0;
|
|
|
|
/* Check filename is usable. */
|
|
if (fname == NULL) {
|
|
ret = WOLFSSL_BAD_FILE;
|
|
}
|
|
/* Open file for reading. */
|
|
if ((ret == 0) && ((file = XFOPEN(fname, "rb")) == XBADFILE)) {
|
|
ret = WOLFSSL_BAD_FILE;
|
|
}
|
|
if (ret == 0) {
|
|
/* Get length of file. */
|
|
ret = wolfssl_file_len(file, &sz);
|
|
}
|
|
if (ret == 0) {
|
|
/* Set the buffer to be big enough to hold all data. */
|
|
ret = static_buffer_set_size(content, (word32)sz, heap, type);
|
|
}
|
|
/* Read data from file. */
|
|
if ((ret == 0) && ((size_t)XFREAD(content->buffer, 1, (size_t)sz, file) !=
|
|
(size_t)sz)) {
|
|
ret = WOLFSSL_BAD_FILE;
|
|
}
|
|
|
|
/* Close file if opened. */
|
|
if (file != XBADFILE) {
|
|
XFCLOSE(file);
|
|
}
|
|
/* Return size read. */
|
|
*size = sz;
|
|
return ret;
|
|
}
|
|
|
|
#endif /* !NO_FILESYSTEM */
|
|
|
|
#endif /* !WOLFCRYPT_ONLY && !NO_CERTS */
|
|
|
|
#endif /* !WOLFSSL_SSL_MISC_INCLUDED */
|
|
|