diff --git a/src/internal.c b/src/internal.c index 5acf7bf8c..4ec99688c 100644 --- a/src/internal.c +++ b/src/internal.c @@ -1131,6 +1131,64 @@ static int ImportPeerInfo(WOLFSSL* ssl, byte* buf, word32 len, byte ver) } +/* WOLFSSL_LOCAL function that serializes the current WOLFSSL session state only + * buf is used to hold the serialized WOLFSSL struct and sz is the size of buf + * passed in. + * On success returns the size of serialized session state.*/ +int wolfSSL_dtls_export_state_internal(WOLFSSL* ssl, byte* buf, word32 sz) +{ + int ret; + word32 idx = 0; + word32 totalLen = 0; + + WOLFSSL_ENTER("wolfSSL_dtls_export_state_internal"); + + if (buf == NULL || ssl == NULL) { + WOLFSSL_LEAVE("wolfSSL_dtls_export_state_internal", BAD_FUNC_ARG); + return BAD_FUNC_ARG; + } + + totalLen += DTLS_EXPORT_LEN * 2; /* 2 protocol bytes and 2 length bytes */ + /* each of the following have a 2 byte length before data */ + totalLen += DTLS_EXPORT_LEN + DTLS_EXPORT_KEY_SZ; + if (totalLen > sz) { + WOLFSSL_LEAVE("wolfSSL_dtls_export_state_internal", BUFFER_E); + return BUFFER_E; + } + + buf[idx++] = (byte)DTLS_EXPORT_STATE_PRO; + buf[idx++] = ((byte)DTLS_EXPORT_STATE_PRO & 0xF0) | + ((byte)DTLS_EXPORT_VERSION & 0X0F); + idx += DTLS_EXPORT_LEN; /* leave room for total length */ + + /* export keys struct and dtls state -- variable length stored in ret */ + idx += DTLS_EXPORT_LEN; /* leave room for length */ + if ((ret = ExportKeyState(ssl, buf + idx, sz - idx, + DTLS_EXPORT_VERSION)) < 0) { + WOLFSSL_LEAVE("wolfSSL_dtls_export_state_internal", ret); + return ret; + } + c16toa((word16)ret, buf + idx - DTLS_EXPORT_LEN); idx += ret; + + /* place total length of exported buffer minus 2 bytes protocol/version */ + c16toa((word16)(idx - DTLS_EXPORT_LEN), buf + DTLS_EXPORT_LEN); + +#ifdef WOLFSSL_SESSION_EXPORT_DEBUG + /* if compiled with debug options then print the version, protocol, size */ + { + char debug[256]; + XSNPRINTF(debug, sizeof(debug), "Exporting DTLS session state\n" + "\tVersion : %d\n\tProtocol : %02X%01X\n\tLength of: %d\n\n" + , (int)DTLS_EXPORT_VERSION, buf[0], (buf[1] >> 4), idx - 2); + WOLFSSL_MSG(debug); + } +#endif /* WOLFSSL_SESSION_EXPORT_DEBUG */ + + WOLFSSL_LEAVE("wolfSSL_dtls_export_state_internal", idx); + return idx; +} + + /* WOLFSSL_LOCAL function that serializes the current WOLFSSL session * buf is used to hold the serialized WOLFSSL struct and sz is the size of buf * passed in. @@ -1221,6 +1279,78 @@ int wolfSSL_dtls_export_internal(WOLFSSL* ssl, byte* buf, word32 sz) } +/* On success return amount of buffer consumed */ +int wolfSSL_dtls_import_state_internal(WOLFSSL* ssl, byte* buf, word32 sz) +{ + word32 idx = 0; + word16 length = 0; + int version; + int ret; + + WOLFSSL_ENTER("wolfSSL_dtls_import_state_internal"); + /* check at least enough room for protocol and length */ + if (sz < DTLS_EXPORT_LEN * 2 || ssl == NULL) { + WOLFSSL_LEAVE("wolfSSL_dtls_import_state_internal", BAD_FUNC_ARG); + return BAD_FUNC_ARG; + } + + if (buf[idx++] != (byte)DTLS_EXPORT_STATE_PRO || + (buf[idx] & 0xF0) != ((byte)DTLS_EXPORT_PRO & 0xF0)) { + WOLFSSL_MSG("Incorrect protocol"); + return BAD_FUNC_ARG; + } + version = buf[idx++] & 0x0F; + + ato16(buf + idx, &length); idx += DTLS_EXPORT_LEN; + if (length > sz - DTLS_EXPORT_LEN) { /* subtract 2 for protocol */ + WOLFSSL_MSG("Buffer size sanity check failed"); + return BUFFER_E; + } + +#ifdef WOLFSSL_SESSION_EXPORT_DEBUG + /* if compiled with debug options then print the version, protocol, size */ + { + char debug[256]; + XSNPRINTF(debug, sizeof(debug), "Importing DTLS session state\n" + "\tVersion : %d\n\tProtocol : %02X%01X\n\tLength of: %d\n\n" + , (int)version, buf[0], (buf[1] >> 4), length); + WOLFSSL_MSG(debug); + } +#endif /* WOLFSSL_SESSION_EXPORT_DEBUG */ + + /* perform sanity checks and extract Options information used */ + switch (version) { + case DTLS_EXPORT_VERSION: + break; + + default: + WOLFSSL_MSG("Bad export state version"); + return BAD_FUNC_ARG; + + } + + /* perform sanity checks and extract Keys struct */ + if (DTLS_EXPORT_LEN + idx > sz) { + WOLFSSL_MSG("Import Key struct error"); + return BUFFER_E; + } + ato16(buf + idx, &length); idx += DTLS_EXPORT_LEN; + if (length > DTLS_EXPORT_KEY_SZ || length + idx > sz) { + WOLFSSL_MSG("Import Key struct error"); + return BUFFER_E; + } + if ((ret = ImportKeyState(ssl, buf + idx, length, version)) < 0) { + WOLFSSL_MSG("Import Key struct error"); + WOLFSSL_LEAVE("wolfSSL_dtls_import_state_internal", ret); + return ret; + } + idx += ret; + + WOLFSSL_LEAVE("wolfSSL_dtls_import_state_internal", ret); + return ret; +} + + /* On success return amount of buffer consumed */ int wolfSSL_dtls_import_internal(WOLFSSL* ssl, byte* buf, word32 sz) { @@ -1240,8 +1370,9 @@ int wolfSSL_dtls_import_internal(WOLFSSL* ssl, byte* buf, word32 sz) if (buf[idx++] != (byte)DTLS_EXPORT_PRO || (buf[idx] & 0xF0) != ((byte)DTLS_EXPORT_PRO & 0xF0)) { /* don't increment on second idx to next get version */ - WOLFSSL_MSG("Incorrect protocol"); - return BAD_FUNC_ARG; + + /* check if importing state only */ + return wolfSSL_dtls_import_state_internal(ssl, buf, sz); } version = buf[idx++] & 0x0F; diff --git a/src/ssl.c b/src/ssl.c index 942e059ba..b8292f3a5 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -160,7 +160,7 @@ int wolfSSL_dtls_set_export(WOLFSSL* ssl, wc_dtls_export func) /* This function allows for directly serializing a session rather than using * callbacks. It has less overhead by removing a temporary buffer and gives * control over when the session gets serialized. When using callbacks the - * session is always serialized immediatly after the handshake is finished. + * session is always serialized immediately after the handshake is finished. * * buf is the argument to contain the serialized session * sz is the size of the buffer passed in @@ -191,6 +191,41 @@ int wolfSSL_dtls_export(WOLFSSL* ssl, unsigned char* buf, unsigned int* sz) } +/* This function is similar to wolfSSL_dtls_export but only exports the portion + * of the WOLFSSL structure related to the state of the connection, i.e. peer + * sequence number, epoch, AEAD state etc. + * + * buf is the argument to contain the serialized state, if null then set "sz" to + * buffer size required + * sz is the size of the buffer passed in + * ssl is the WOLFSSL struct to serialize + * returns the size of serialized session on success, 0 on no action, and + * negative value on error */ +int wolfSSL_dtls_export_state_only(WOLFSSL* ssl, unsigned char* buf, + unsigned int* sz) +{ + WOLFSSL_ENTER("wolfSSL_dtls_export_state_only"); + + if (ssl == NULL || sz == NULL) { + return BAD_FUNC_ARG; + } + + if (buf == NULL) { + *sz = MAX_EXPORT_STATE_BUFFER; + return 0; + } + + /* if not DTLS do nothing */ + if (!ssl->options.dtls) { + WOLFSSL_MSG("Currently only DTLS export state is supported"); + return 0; + } + + /* copy over keys, options, and dtls state struct */ + return wolfSSL_dtls_export_state_internal(ssl, buf, *sz); +} + + /* returns 0 on success */ int wolfSSL_send_session(WOLFSSL* ssl) { diff --git a/tests/api.c b/tests/api.c index f4725a02e..b611962a9 100644 --- a/tests/api.c +++ b/tests/api.c @@ -1840,6 +1840,75 @@ static int test_export(WOLFSSL* inSsl, byte* buf, word32 sz, void* userCtx) #endif #if !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) +#ifdef WOLFSSL_SESSION_EXPORT +/* returns negative value on fail and positive (including 0) on success */ +static int nonblocking_accept_read(void* args, WOLFSSL* ssl, SOCKET_T* sockfd) +{ + int ret, err, loop_count, count, timeout = 10; + char msg[] = "I hear you fa shizzle!"; + char input[1024]; + + loop_count = ((func_args*)args)->argc; + do { +#ifdef WOLFSSL_ASYNC_CRYPT + if (err == WC_PENDING_E) { + ret = wolfSSL_AsyncPoll(ssl, WOLF_POLL_FLAG_CHECK_HW); + if (ret < 0) { break; } else if (ret == 0) { continue; } + } +#endif + + err = 0; /* Reset error */ + + ret = wolfSSL_accept(ssl); + if (ret != WOLFSSL_SUCCESS) { + err = wolfSSL_get_error(ssl, 0); + if (err == WOLFSSL_ERROR_WANT_READ || + err == WOLFSSL_ERROR_WANT_WRITE) { + int select_ret; + + err = WC_PENDING_E; + select_ret = tcp_select(*sockfd, timeout); + if (select_ret == TEST_TIMEOUT) { + return WOLFSSL_FATAL_ERROR; + } + } + } + } while (ret != WOLFSSL_SUCCESS && err == WC_PENDING_E); + + if (ret != WOLFSSL_SUCCESS) { + char buff[WOLFSSL_MAX_ERROR_SZ]; + printf("error = %d, %s\n", err, wolfSSL_ERR_error_string(err, buff)); + return ret; + } + + for (count = 0; count < loop_count; count++) { + int select_ret; + + select_ret = tcp_select(*sockfd, timeout); + if (select_ret == TEST_TIMEOUT) { + ret = WOLFSSL_FATAL_ERROR; + break; + } + + do { + ret = wolfSSL_read(ssl, input, sizeof(input)-1); + if (ret > 0) { + input[ret] = '\0'; + printf("Client message: %s\n", input); + } + } while (err == WOLFSSL_ERROR_WANT_READ && ret != WOLFSSL_SUCCESS); + + do { + if ((ret = wolfSSL_write(ssl, msg, sizeof(msg))) != sizeof(msg)) { + return WOLFSSL_FATAL_ERROR; + } + err = wolfSSL_get_error(ssl, ret); + } while (err == WOLFSSL_ERROR_WANT_READ && ret != WOLFSSL_SUCCESS); + } + return ret; +} +#endif + static THREAD_RETURN WOLFSSL_THREAD test_server_nofail(void* args) { SOCKET_T sockfd = 0; @@ -1931,6 +2000,15 @@ static THREAD_RETURN WOLFSSL_THREAD test_server_nofail(void* args) goto done; } +#ifdef WOLFSSL_SESSION_EXPORT + /* only add in more complex nonblocking case with session export tests */ + if (args && ((func_args*)args)->argc > 0) { + /* set as nonblock and time out for waiting on read/write */ + tcp_set_nonblocking(&clientfd); + wolfSSL_dtls_set_using_nonblock(ssl, 1); + } +#endif + if (sharedCtx && wolfSSL_use_certificate_file(ssl, svrCertFile, WOLFSSL_FILETYPE_PEM) != WOLFSSL_SUCCESS) { /*err_sys("can't load server cert chain file, " @@ -1960,6 +2038,20 @@ static THREAD_RETURN WOLFSSL_THREAD test_server_nofail(void* args) cbf->ssl_ready(ssl); } +#ifdef WOLFSSL_SESSION_EXPORT + /* only add in more complex nonblocking case with session export tests */ + if (args && ((func_args*)args)->argc > 0) { + ret = nonblocking_accept_read(args, ssl, &clientfd); + if (ret >= 0) { + ((func_args*)args)->return_code = TEST_SUCCESS; + } + #ifdef WOLFSSL_TIRTOS + Task_yield(); + #endif + goto done; + } +#endif + do { #ifdef WOLFSSL_ASYNC_CRYPT if (err == WC_PENDING_E) { @@ -3090,8 +3182,17 @@ static void test_wolfSSL_dtls_export(void) #endif { + SOCKET_T sockfd = 0; WOLFSSL_CTX* ctx; WOLFSSL* ssl; + char msg[64] = "hello wolfssl!"; + char reply[1024]; + int msgSz = (int)XSTRLEN(msg); + byte *session, *window; + unsigned int sessionSz, windowSz; + struct sockaddr_in peerAddr; + int i; + /* Set ctx to DTLS 1.2 */ AssertNotNull(ctx = wolfSSL_CTX_new(wolfDTLSv1_2_server_method())); @@ -3107,7 +3208,87 @@ static void test_wolfSSL_dtls_export(void) AssertIntLT(wolfSSL_dtls_import(ssl, version_3, sizeof(version_3)), 0); wolfSSL_free(ssl); wolfSSL_CTX_free(ctx); + + + /* check storing client state after connection and storing window only */ +#ifdef WOLFSSL_TIRTOS + fdOpenSession(Task_self()); +#endif + + InitTcpReady(&ready); + +#if defined(USE_WINDOWS_API) + /* use RNG to get random port if using windows */ + ready.port = GetRandomPort(); +#endif + + /* set using dtls */ + XMEMSET(&server_args, 0, sizeof(func_args)); + XMEMSET(&server_cbf, 0, sizeof(callback_functions)); + server_cbf.method = wolfDTLSv1_2_server_method; + server_args.callbacks = &server_cbf; + server_args.argc = 3; /* set loop_count to 3 */ + + + server_args.signal = &ready; + start_thread(test_server_nofail, &server_args, &serverThread); + wait_tcp_ready(&server_args); + + /* create and connect with client */ + AssertNotNull(ctx = wolfSSL_CTX_new(wolfDTLSv1_2_client_method())); + AssertIntEQ(WOLFSSL_SUCCESS, + wolfSSL_CTX_load_verify_locations(ctx, caCertFile, 0)); + AssertIntEQ(WOLFSSL_SUCCESS, + wolfSSL_CTX_use_certificate_file(ctx, cliCertFile, SSL_FILETYPE_PEM)); + AssertIntEQ(WOLFSSL_SUCCESS, + wolfSSL_CTX_use_PrivateKey_file(ctx, cliKeyFile, SSL_FILETYPE_PEM)); + tcp_connect(&sockfd, wolfSSLIP, server_args.signal->port, 0, 0, NULL); + AssertNotNull(ssl = wolfSSL_new(ctx)); + AssertIntEQ(wolfSSL_set_fd(ssl, sockfd), WOLFSSL_SUCCESS); + + /* store server information connected too */ + XMEMSET(&peerAddr, 0, sizeof(peerAddr)); + peerAddr.sin_family = AF_INET; + peerAddr.sin_port = XHTONS(server_args.signal->port); + wolfSSL_dtls_set_peer(ssl, &peerAddr, sizeof(peerAddr)); + + AssertIntEQ(wolfSSL_connect(ssl), WOLFSSL_SUCCESS); + AssertIntEQ(wolfSSL_dtls_export(ssl, NULL, &sessionSz), 0); + session = (byte*)XMALLOC(sessionSz, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + AssertIntGT(wolfSSL_dtls_export(ssl, session, &sessionSz), 0); + AssertIntEQ(wolfSSL_write(ssl, msg, msgSz), msgSz); + AssertIntGT(wolfSSL_read(ssl, reply, sizeof(reply)), 0); + AssertIntEQ(wolfSSL_dtls_export_state_only(ssl, NULL, &windowSz), 0); + window = (byte*)XMALLOC(windowSz, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + AssertIntGT(wolfSSL_dtls_export_state_only(ssl, window, &windowSz), 0); + wolfSSL_free(ssl); + + for (i = 1; i < server_args.argc; i++) { + /* restore state */ + AssertNotNull(ssl = wolfSSL_new(ctx)); + AssertIntGT(wolfSSL_dtls_import(ssl, session, sessionSz), 0); + AssertIntGT(wolfSSL_dtls_import(ssl, window, windowSz), 0); + AssertIntEQ(wolfSSL_set_fd(ssl, sockfd), WOLFSSL_SUCCESS); + AssertIntEQ(wolfSSL_write(ssl, msg, msgSz), msgSz); + AssertIntGE(wolfSSL_read(ssl, reply, sizeof(reply)), 0); + AssertIntGT(wolfSSL_dtls_export_state_only(ssl, window, &windowSz), 0); + wolfSSL_free(ssl); } + XFREE(session, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(window, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + wolfSSL_CTX_free(ctx); + + printf("done and waiting for server\n"); + join_thread(serverThread); + AssertIntEQ(server_args.return_code, TEST_SUCCESS); + + FreeTcpReady(&ready); + +#ifdef WOLFSSL_TIRTOS + fdOpenSession(Task_self()); +#endif + } + printf(testingFmt, "wolfSSL_dtls_export()"); printf(resultFmt, passed); diff --git a/wolfssl/internal.h b/wolfssl/internal.h index ff879e705..f157019ce 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -1273,6 +1273,7 @@ enum Misc { DTLS_HANDSHAKE_FRAG_SZ = 3, /* fragment offset and length are 24 bit */ DTLS_POOL_SZ = 255,/* allowed number of list items in TX pool */ DTLS_EXPORT_PRO = 165,/* wolfSSL protocol for serialized session */ + DTLS_EXPORT_STATE_PRO = 166,/* wolfSSL protocol for serialized state */ DTLS_EXPORT_VERSION = 4, /* wolfSSL version for serialized session */ DTLS_EXPORT_OPT_SZ = 60, /* amount of bytes used from Options */ DTLS_EXPORT_VERSION_3 = 3, /* wolfSSL version before TLS 1.3 addition */ @@ -1285,6 +1286,8 @@ enum Misc { DTLS_EXPORT_LEN = 2, /* 2 bytes for length and protocol */ DTLS_EXPORT_IP = 46, /* max ip size IPv4 mapped IPv6 */ MAX_EXPORT_BUFFER = 514, /* max size of buffer for exporting */ + MAX_EXPORT_STATE_BUFFER = DTLS_EXPORT_KEY_SZ + 3 * DTLS_EXPORT_LEN, + /* max size of buffer for exporting state */ FINISHED_LABEL_SZ = 15, /* TLS finished label size */ TLS_FINISHED_SZ = 12, /* TLS has a shorter size */ EXT_MASTER_LABEL_SZ = 22, /* TLS extended master secret label sz */ @@ -1570,6 +1573,10 @@ WOLFSSL_LOCAL ProtocolVersion MakeTLSv1_3(void); word32 sz); WOLFSSL_LOCAL int wolfSSL_dtls_export_internal(WOLFSSL* ssl, byte* buf, word32 sz); + WOLFSSL_LOCAL int wolfSSL_dtls_export_state_internal(WOLFSSL* ssl, + byte* buf, word32 sz); + WOLFSSL_LOCAL int wolfSSL_dtls_import_state_internal(WOLFSSL* ssl, + byte* buf, word32 sz); WOLFSSL_LOCAL int wolfSSL_send_session(WOLFSSL* ssl); #endif #endif diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index add3f8507..b628010b3 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -508,6 +508,8 @@ WOLFSSL_API int wolfSSL_CTX_dtls_set_export(WOLFSSL_CTX* ctx, WOLFSSL_API int wolfSSL_dtls_set_export(WOLFSSL* ssl, wc_dtls_export func); WOLFSSL_API int wolfSSL_dtls_export(WOLFSSL* ssl, unsigned char* buf, unsigned int* sz); +WOLFSSL_API int wolfSSL_dtls_export_state_only(WOLFSSL* ssl, unsigned char* buf, + unsigned int* sz); #endif /* WOLFSSL_DTLS */ #endif /* WOLFSSL_SESSION_EXPORT */