summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaiki Ueno <ueno@gnu.org>2021-04-25 07:49:32 +0000
committerDaiki Ueno <ueno@gnu.org>2021-04-25 07:49:32 +0000
commiteaa5ef3addfcfb22ffa7c0e0fcb52960c2abbe64 (patch)
tree0b0d6a598908c5b127e917897d0812f6096ef8d7
parent2e8bfb2844514a7fe0487104bf112530d93cf9e4 (diff)
parent0e1f1f0558e733c39a55172fecbb42c96abc31d3 (diff)
downloadgnutls-eaa5ef3addfcfb22ffa7c0e0fcb52960c2abbe64.tar.gz
Merge branch 'wip/dueno/earlydata' into 'master'
handshake: fix timing of sending early data Closes #1146 See merge request gnutls/gnutls!1416
-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;