summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaiki Ueno <ueno@gnu.org>2021-04-23 15:36:37 +0200
committerDaiki Ueno <ueno@gnu.org>2021-04-25 08:42:47 +0200
commit0e1f1f0558e733c39a55172fecbb42c96abc31d3 (patch)
treeef96a8e807d84388d3a57081163c4446ae76753a
parentbadb8e4a67bd0b5657f5f7d2fe4d48f2bba30283 (diff)
downloadgnutls-0e1f1f0558e733c39a55172fecbb42c96abc31d3.tar.gz
handshake: fix timing of sending early data
Previously, the client was sending early data after receiving a Server Hello message, which not only negates the benefit of 0-RTT, but also was a logic error as it can only be decrypted by the server when the initial handshake and the resuming handshake agree on the same ciphersuites. This fixes that behavior in the following ways: - extend the session data format to include the selected ciphersuites, even in TLS 1.3 - setup the epoch for early data, right before the client sending early data (also right after the server deciding to accept early data). - extend the test case to use different ciphersuites in the initial and resuming handshakes Signed-off-by: Daiki Ueno <ueno@gnu.org>
-rw-r--r--NEWS5
-rw-r--r--lib/cipher.c6
-rw-r--r--lib/constate.c46
-rw-r--r--lib/handshake-tls13.c92
-rw-r--r--lib/handshake.c70
-rw-r--r--lib/record.c2
-rw-r--r--lib/session_pack.c14
-rw-r--r--tests/tls13-early-data.c16
-rw-r--r--tests/tls13/prf-early.c8
9 files changed, 176 insertions, 83 deletions
diff --git a/NEWS b/NEWS
index 8b3538d05d..f4ebe43ffb 100644
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,11 @@ See the end for copying conditions.
This can be enabled with --enable-afalg configure option, when libkcapi
package is installed (#308).
+** libgnutls: Fixed timing of early data exchange. Previously, the client was
+ sending early data after receiving Server Hello, which not only negates the
+ benefit of 0-RTT, but also works under certain assumptions hold (e.g., the
+ same ciphersuite is selected in initial and resumption handshake) (#1146).
+
* Version 3.7.1 (released 2021-03-10)
** libgnutls: Fixed potential use-after-free in sending "key_share"
diff --git a/lib/cipher.c b/lib/cipher.c
index 90ab1d3a9b..28eafbe188 100644
--- a/lib/cipher.c
+++ b/lib/cipher.c
@@ -85,7 +85,11 @@ _gnutls_encrypt(gnutls_session_t session,
content_type_t type, record_parameters_st *params)
{
gnutls_datum_t plaintext;
- const version_entry_st *vers = get_version(session);
+ const version_entry_st *vers =
+ (session->internals.hsk_flags & HSK_EARLY_DATA_IN_FLIGHT) &&
+ !IS_SERVER(session) ?
+ session->internals.resumed_security_parameters.pversion :
+ get_version(session);
int ret;
plaintext.data = (uint8_t *) data;
diff --git a/lib/constate.c b/lib/constate.c
index fc56a7569a..ffa343000e 100644
--- a/lib/constate.c
+++ b/lib/constate.c
@@ -336,11 +336,19 @@ _tls13_set_early_keys(gnutls_session_t session,
return GNUTLS_E_INVALID_REQUEST;
}
- ret = _tls13_expand_secret(session, "key", 3, NULL, 0, session->key.proto.tls13.e_ckey, key_size, key_block);
+ ret = _tls13_expand_secret2(session->internals.
+ resumed_security_parameters.prf,
+ "key", 3, NULL, 0,
+ session->key.proto.tls13.e_ckey,
+ key_size, key_block);
if (ret < 0)
return gnutls_assert_val(ret);
- ret = _tls13_expand_secret(session, "iv", 2, NULL, 0, session->key.proto.tls13.e_ckey, iv_size, iv_block);
+ ret = _tls13_expand_secret2(session->internals.
+ resumed_security_parameters.prf,
+ "iv", 2, NULL, 0,
+ session->key.proto.tls13.e_ckey,
+ iv_size, iv_block);
if (ret < 0)
return gnutls_assert_val(ret);
@@ -592,10 +600,19 @@ _gnutls_set_cipher_suite2(gnutls_session_t session,
return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
return 0;
- } else {
- if (params->initialized
- || params->cipher != NULL || params->mac != NULL)
- return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+
+ /* The params shouldn't have been initialized at this point, unless we
+ * are doing trial encryption/decryption of early data.
+ */
+ if (unlikely
+ (!((session->internals.hsk_flags & HSK_EARLY_DATA_IN_FLIGHT &&
+ !IS_SERVER(session)) ||
+ (session->internals.hsk_flags & HSK_EARLY_DATA_ACCEPTED &&
+ IS_SERVER(session))) &&
+ (params->initialized
+ || params->cipher != NULL || params->mac != NULL))) {
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
}
if (_gnutls_cipher_is_ok(cipher_algo) == 0
@@ -655,7 +672,10 @@ int _gnutls_epoch_set_keys(gnutls_session_t session, uint16_t epoch, hs_stage_t
int key_size;
record_parameters_st *params;
int ret;
- const version_entry_st *ver = get_version(session);
+ const version_entry_st *ver =
+ stage == STAGE_EARLY && !IS_SERVER(session) ?
+ session->internals.resumed_security_parameters.pversion :
+ get_version(session);
if (unlikely(ver == NULL))
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
@@ -1200,12 +1220,18 @@ int _tls13_read_connection_state_init(gnutls_session_t session, hs_stage_t stage
session->security_parameters.epoch_next;
int ret;
+ if (unlikely(stage == STAGE_EARLY && !IS_SERVER(session))) {
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+
ret = _gnutls_epoch_set_keys(session, epoch_next, stage);
if (ret < 0)
return ret;
_gnutls_handshake_log("HSK[%p]: TLS 1.3 set read key with cipher suite: %s\n",
session,
+ stage == STAGE_EARLY ?
+ session->internals.resumed_security_parameters.cs->name :
session->security_parameters.cs->name);
session->security_parameters.epoch_read = epoch_next;
@@ -1223,12 +1249,18 @@ int _tls13_write_connection_state_init(gnutls_session_t session, hs_stage_t stag
session->security_parameters.epoch_next;
int ret;
+ if (unlikely(stage == STAGE_EARLY && IS_SERVER(session))) {
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+
ret = _gnutls_epoch_set_keys(session, epoch_next, stage);
if (ret < 0)
return ret;
_gnutls_handshake_log("HSK[%p]: TLS 1.3 set write key with cipher suite: %s\n",
session,
+ stage == STAGE_EARLY ?
+ session->internals.resumed_security_parameters.cs->name :
session->security_parameters.cs->name);
session->security_parameters.epoch_write = epoch_next;
diff --git a/lib/handshake-tls13.c b/lib/handshake-tls13.c
index 9687707a32..d9071e2b31 100644
--- a/lib/handshake-tls13.c
+++ b/lib/handshake-tls13.c
@@ -84,7 +84,9 @@ int _gnutls13_handshake_client(gnutls_session_t session)
case STATE99:
case STATE100:
#ifdef TLS13_APPENDIX_D4
- if (session->internals.priorities->tls13_compat_mode) {
+ if (session->internals.priorities->tls13_compat_mode &&
+ /* Key change is indicated by sending an EndOfEarlyData below */
+ !(session->internals.hsk_flags & HSK_EARLY_DATA_IN_FLIGHT)) {
/* We send it before keys are generated. That works because CCS
* is always being cached and queued and not being sent directly */
ret = _gnutls_send_change_cipher_spec(session, AGAIN(STATE100));
@@ -94,26 +96,7 @@ int _gnutls13_handshake_client(gnutls_session_t session)
#endif
FALLTHROUGH;
case STATE101:
- /* Note that we check IN_FLIGHT, not ACCEPTED
- * here. This is because the client sends early data
- * speculatively. */
- if (session->internals.hsk_flags & HSK_EARLY_DATA_IN_FLIGHT) {
- ret = _tls13_write_connection_state_init(session, STAGE_EARLY);
- if (ret == 0) {
- _gnutls_epoch_bump(session);
- ret = _gnutls_epoch_dup(session, EPOCH_WRITE_CURRENT);
- }
- STATE = STATE101;
- IMED_RET_FATAL("set early traffic keys", ret, 0);
- }
- FALLTHROUGH;
- case STATE102:
- ret = _gnutls13_send_early_data(session);
- STATE = STATE102;
- IMED_RET("send early data", ret, 0);
- FALLTHROUGH;
- case STATE103:
- STATE = STATE103;
+ STATE = STATE101;
ret = generate_hs_traffic_keys(session);
/* Note that we check IN_FLIGHT, not ACCEPTED
* here. This is because the client sends early data
@@ -125,40 +108,40 @@ int _gnutls13_handshake_client(gnutls_session_t session)
ret = _tls13_connection_state_init(session, STAGE_HS);
IMED_RET_FATAL("set hs traffic keys", ret, 0);
FALLTHROUGH;
- case STATE104:
+ case STATE102:
ret = _gnutls13_recv_encrypted_extensions(session);
- STATE = STATE104;
+ STATE = STATE102;
IMED_RET("recv encrypted extensions", ret, 0);
FALLTHROUGH;
- case STATE105:
+ case STATE103:
ret = _gnutls13_recv_certificate_request(session);
- STATE = STATE105;
+ STATE = STATE103;
IMED_RET("recv certificate request", ret, 0);
FALLTHROUGH;
- case STATE106:
+ case STATE104:
ret = _gnutls13_recv_certificate(session);
- STATE = STATE106;
+ STATE = STATE104;
IMED_RET("recv certificate", ret, 0);
FALLTHROUGH;
- case STATE107:
+ case STATE105:
ret = _gnutls13_recv_certificate_verify(session);
- STATE = STATE107;
+ STATE = STATE105;
IMED_RET("recv server certificate verify", ret, 0);
FALLTHROUGH;
- case STATE108:
+ case STATE106:
ret = _gnutls_run_verify_callback(session, GNUTLS_CLIENT);
- STATE = STATE108;
+ STATE = STATE106;
if (ret < 0)
return gnutls_assert_val(ret);
FALLTHROUGH;
- case STATE109:
+ case STATE107:
ret = _gnutls13_recv_finished(session);
- STATE = STATE109;
+ STATE = STATE107;
IMED_RET("recv finished", ret, 0);
FALLTHROUGH;
- case STATE110:
- ret = _gnutls13_send_end_of_early_data(session, AGAIN(STATE110));
- STATE = STATE110;
+ case STATE108:
+ ret = _gnutls13_send_end_of_early_data(session, AGAIN(STATE108));
+ STATE = STATE108;
IMED_RET("send end of early data", ret, 0);
/* Note that we check IN_FLIGHT, not ACCEPTED
@@ -170,23 +153,23 @@ int _gnutls13_handshake_client(gnutls_session_t session)
IMED_RET_FATAL("set hs traffic key after sending early data", ret, 0);
}
FALLTHROUGH;
- case STATE111:
- ret = _gnutls13_send_certificate(session, AGAIN(STATE111));
- STATE = STATE111;
+ case STATE109:
+ ret = _gnutls13_send_certificate(session, AGAIN(STATE109));
+ STATE = STATE109;
IMED_RET("send certificate", ret, 0);
FALLTHROUGH;
- case STATE112:
- ret = _gnutls13_send_certificate_verify(session, AGAIN(STATE112));
- STATE = STATE112;
+ case STATE110:
+ ret = _gnutls13_send_certificate_verify(session, AGAIN(STATE110));
+ STATE = STATE110;
IMED_RET("send certificate verify", ret, 0);
FALLTHROUGH;
- case STATE113:
- ret = _gnutls13_send_finished(session, AGAIN(STATE113));
- STATE = STATE113;
+ case STATE111:
+ ret = _gnutls13_send_finished(session, AGAIN(STATE111));
+ STATE = STATE111;
IMED_RET("send finished", ret, 0);
FALLTHROUGH;
- case STATE114:
- STATE = STATE114;
+ case STATE112:
+ STATE = STATE112;
ret =
generate_ap_traffic_keys(session);
@@ -430,22 +413,13 @@ int _gnutls13_handshake_server(gnutls_session_t session)
FALLTHROUGH;
case STATE101:
STATE = STATE101;
- if (session->internals.hsk_flags & HSK_EARLY_DATA_ACCEPTED) {
- ret = _tls13_read_connection_state_init(session, STAGE_EARLY);
- if (ret == 0) {
- _gnutls_epoch_bump(session);
- ret = _gnutls_epoch_dup(session, EPOCH_READ_CURRENT);
- }
- IMED_RET_FATAL("set early traffic keys", ret, 0);
- ret = generate_hs_traffic_keys(session);
- IMED_RET_FATAL("generate hs traffic keys", ret, 0);
+ ret = generate_hs_traffic_keys(session);
+ IMED_RET_FATAL("generate hs traffic keys", ret, 0);
+ if (session->internals.hsk_flags & HSK_EARLY_DATA_ACCEPTED) {
ret = _tls13_write_connection_state_init(session, STAGE_HS);
} else {
- ret = generate_hs_traffic_keys(session);
- IMED_RET_FATAL("generate hs traffic keys", ret, 0);
-
ret = _tls13_connection_state_init(session, STAGE_HS);
}
IMED_RET_FATAL("set hs traffic keys", ret, 0);
diff --git a/lib/handshake.c b/lib/handshake.c
index c30703ccbd..1153eac85d 100644
--- a/lib/handshake.c
+++ b/lib/handshake.c
@@ -55,6 +55,7 @@
#include <random.h>
#include <dtls.h>
#include "secrets.h"
+#include "tls13/early_data.h"
#include "tls13/session_ticket.h"
#include "locks.h"
#ifdef HAVE_VALGRIND_MEMCHECK_H
@@ -788,6 +789,38 @@ read_client_hello(gnutls_session_t session, uint8_t * data,
return ret;
}
+ if (session->internals.hsk_flags & HSK_EARLY_DATA_ACCEPTED) {
+ const cipher_entry_st *ce;
+ const mac_entry_st *me;
+ record_parameters_st *params;
+
+ ce = cipher_to_entry(session->internals.
+ resumed_security_parameters.
+ cs->block_algorithm);
+ me = mac_to_entry(session->internals.
+ resumed_security_parameters.
+ cs->mac_algorithm);
+
+ ret = _gnutls_epoch_get(session, EPOCH_NEXT, &params);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ params->cipher = ce;
+ params->mac = me;
+
+ ret = _tls13_read_connection_state_init(session, STAGE_EARLY);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ _gnutls_epoch_bump(session);
+ ret = _gnutls_epoch_dup(session, EPOCH_READ_CURRENT);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+ }
+
/* resumed by session_ticket extension */
if (!vers->tls13_sem && session->internals.resumed) {
session->internals.resumed_security_parameters.
@@ -2309,6 +2342,43 @@ static int send_client_hello(gnutls_session_t session, int again)
ret = _gnutls_send_handshake(session, bufel,
GNUTLS_HANDSHAKE_CLIENT_HELLO);
+ if (session->internals.hsk_flags & HSK_EARLY_DATA_IN_FLIGHT) {
+ const cipher_entry_st *ce;
+ const mac_entry_st *me;
+ record_parameters_st *params;
+
+ ce = cipher_to_entry(session->internals.
+ resumed_security_parameters.
+ cs->block_algorithm);
+ me = mac_to_entry(session->internals.
+ resumed_security_parameters.
+ cs->mac_algorithm);
+
+ ret = _gnutls_epoch_get(session, EPOCH_NEXT, &params);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ params->cipher = ce;
+ params->mac = me;
+
+ ret = _tls13_write_connection_state_init(session, STAGE_EARLY);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ _gnutls_epoch_bump(session);
+ ret = _gnutls_epoch_dup(session, EPOCH_WRITE_CURRENT);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ ret = _gnutls13_send_early_data(session);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+ }
+
return ret;
cleanup:
diff --git a/lib/record.c b/lib/record.c
index cd9df80520..860b9897d6 100644
--- a/lib/record.c
+++ b/lib/record.c
@@ -2120,7 +2120,7 @@ ssize_t gnutls_record_send_early_data(gnutls_session_t session,
* @data: the buffer that the data will be read into
* @data_size: the number of requested bytes
*
- * This function can be used by a searver to retrieve data sent early
+ * This function can be used by a server to retrieve data sent early
* in the handshake processes when resuming a session. This is used
* to implement a zero-roundtrip (0-RTT) mode. It has the same
* semantics as gnutls_record_recv().
diff --git a/lib/session_pack.c b/lib/session_pack.c
index a6d11c4cfc..1f3bc4740c 100644
--- a/lib/session_pack.c
+++ b/lib/session_pack.c
@@ -911,11 +911,11 @@ pack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps)
BUFFER_APPEND_NUM(ps, session->security_parameters.client_ctype);
BUFFER_APPEND_NUM(ps, session->security_parameters.server_ctype);
+ BUFFER_APPEND(ps, session->security_parameters.cs->id, 2);
+
/* if we are under TLS 1.3 do not pack keys or params negotiated using an extension
* they are not necessary */
if (!session->security_parameters.pversion->tls13_sem) {
- BUFFER_APPEND(ps, session->security_parameters.cs->id, 2);
-
BUFFER_APPEND_PFX1(ps, session->security_parameters.master_secret,
GNUTLS_MASTER_SIZE);
BUFFER_APPEND_PFX1(ps, session->security_parameters.client_random,
@@ -1026,12 +1026,12 @@ unpack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps)
session->internals.resumed_security_parameters.
server_ctype);
- if (!session->internals.resumed_security_parameters.pversion->tls13_sem) {
- BUFFER_POP(ps, cs, 2);
- session->internals.resumed_security_parameters.cs = ciphersuite_to_entry(cs);
- if (session->internals.resumed_security_parameters.cs == NULL)
- return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ BUFFER_POP(ps, cs, 2);
+ session->internals.resumed_security_parameters.cs = ciphersuite_to_entry(cs);
+ if (session->internals.resumed_security_parameters.cs == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ if (!session->internals.resumed_security_parameters.pversion->tls13_sem) {
/* master secret */
ret = _gnutls_buffer_pop_datum_prefix8(ps, &t);
if (ret < 0) {
diff --git a/tests/tls13-early-data.c b/tests/tls13-early-data.c
index 4235e12e2a..8091572dfa 100644
--- a/tests/tls13-early-data.c
+++ b/tests/tls13-early-data.c
@@ -70,11 +70,19 @@ static void client_log_func(int level, const char *str)
/* A very basic TLS client.
*/
-#define SESSIONS 3
#define MAX_BUF 1024
#define MSG "Hello TLS"
#define EARLY_MSG "Hello TLS, it's early"
-#define PRIORITY "NORMAL:-VERS-ALL:+VERS-TLS1.3"
+
+/* This test makes connection 3 times with different ciphersuites: first with
+ * TLS_AES_128_GCM_SHA256, then TLS_AES_256_GCM_SHA384 two times. The reason
+ * for doing this is to check that the early data is encrypted with the
+ * ciphersuite selected during the initial handshake, not the resuming
+ * handshakes.
+ */
+#define SESSIONS 3
+#define TLS13_AES_128_GCM "NORMAL:-VERS-ALL:+VERS-TLS1.3:+AES-128-GCM"
+#define TLS13_AES_256_GCM "NORMAL:-VERS-ALL:+VERS-TLS1.3:+AES-256-GCM"
static const
gnutls_datum_t hrnd = {(void*)"\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32};
@@ -120,7 +128,7 @@ static void client(int sds[], const char *data, size_t size, size_t maxsize)
int sd = sds[t];
assert(gnutls_init(&session, GNUTLS_CLIENT)>=0);
- assert(gnutls_priority_set_direct(session, PRIORITY, NULL)>=0);
+ assert(gnutls_priority_set_direct(session, t == 0 ? TLS13_AES_128_GCM : TLS13_AES_256_GCM, NULL)>=0);
gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
@@ -289,7 +297,7 @@ static void server(int sds[], const char *data, size_t size, size_t maxsize)
assert(gnutls_init(&session, GNUTLS_SERVER|GNUTLS_ENABLE_EARLY_DATA)>=0);
- assert(gnutls_priority_set_direct(session, PRIORITY, NULL)>=0);
+ assert(gnutls_priority_set_direct(session, t == 0 ? TLS13_AES_128_GCM : TLS13_AES_256_GCM, NULL)>=0);
gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
diff --git a/tests/tls13/prf-early.c b/tests/tls13/prf-early.c
index 51dd7a3f76..3d9462848a 100644
--- a/tests/tls13/prf-early.c
+++ b/tests/tls13/prf-early.c
@@ -123,10 +123,10 @@ static void dump(const char *name, const uint8_t *data, unsigned data_size)
} \
}
-#define KEY_EXP_VALUE "\x7f\x9a\x62\x64\x5e\x90\xa4\x19\x6f\xbf\x7b\x4e\x98\x63\x29\xb0\x46\xa2\x2a\x47\x94\x6a\x78\xdc\x6e\xea\x90\x13\x9d\xd4\xd1\x20\x02\x04"
-#define HELLO_VALUE "\x38\x40\x8c\x0d\x53\xe5\xd2\xe8\x66\xb4\x46\xce\x32\x85\xd5\x02\x3a\x4f\x81\x3c\x9e\x1b\x4a\x53\x73\x22\xad\xf2\x11\xc6\x45"
-#define CONTEXT_VALUE "\xf6\x95\x60\x0d\x51\x9e\x1a\x40\xb2\x9e\xb0\x48\x55\xfe\x64\xf8\xa0\x26\x31\xd8\xb1\x66\xf3\x10\x62\x32\x26\x52\x9e\x63\x49"
-#define NULL_CONTEXT_VALUE "\xb1\x80\x8c\xb3\xc2\xa9\x06\x88\xb7\xc2\xed\xd4\x5f\x1c\xad\x0b\xb2\x1f\xa9\xe2\xc6\x37\xd3\x52\x73\x1b\xf5\x3b\x92\x61\x08"
+#define KEY_EXP_VALUE "\xec\xc2\x4a\x6b\x07\x89\xd9\x19\xd9\x73\x6d\xd0\x00\x73\xc9\x7a\xd7\x92\xef\x56\x91\x61\xb4\xff\x5f\xef\x81\xc1\x98\x68\x4e\xdf\xd7\x7e"
+#define HELLO_VALUE "\x4f\x85\x33\x64\x48\xff\x0d\x8b\xd5\x50\x0f\x97\x91\x5b\x7d\x8d\xc9\x05\x91\x45\x4f\xb9\x4b\x4b\xbc\xbf\x58\x84\x1a\x46\xe3"
+#define CONTEXT_VALUE "\x11\x8d\x85\xa8\x91\xe5\x50\x75\x44\x88\x69\xaf\x95\x9a\xb0\x29\xd4\xae\xcd\x11\xcb\x1d\x29\x7c\xe6\x24\xd4\x7c\x95\xdb\x5c"
+#define NULL_CONTEXT_VALUE "\x56\x99\x41\x73\x5e\x73\x34\x7f\x3d\x69\x9f\xc0\x3b\x8b\x86\x33\xc6\xc3\x97\x46\x61\x62\x3f\x55\xab\x39\x60\xa5\xeb\xfe\x37"
static int handshake_callback_called;