Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: cleanup prf header #5144

Merged
merged 1 commit into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions tests/unit/s2n_ssl_prf_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
#include "testlib/s2n_testlib.h"
#include "tls/s2n_prf.h"

/*
int s2n_prf_tls_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret);

/*
* Grabbed from gnutls-cli --insecure -d 9 www.example.com --ciphers AES --macs SHA --protocols SSLv3
*
* |<9>| INT: PREMASTER SECRET[48]: 03009e8e006a7f1451d32164088a8cba5077d1b819160662a97e90a765cec244b5f8f98fd50cfe8e4fba97994a7a4843
Expand Down Expand Up @@ -86,7 +87,7 @@ int main(int argc, char **argv)
conn->actual_protocol_version = S2N_SSLv3;
pms.data = conn->secrets.version.tls12.rsa_premaster_secret;
pms.size = sizeof(conn->secrets.version.tls12.rsa_premaster_secret);
EXPECT_SUCCESS(s2n_tls_prf_master_secret(conn, &pms));
EXPECT_SUCCESS(s2n_prf_tls_master_secret(conn, &pms));

/* Convert the master secret to hex */
for (int i = 0; i < 48; i++) {
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/s2n_tls_hybrid_prf_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ int main(int argc, char **argv)

EXPECT_MEMCPY_SUCCESS(conn->kex_params.client_key_exchange_message.data, client_key_exchange_message, client_key_exchange_message_length);

EXPECT_SUCCESS(s2n_hybrid_prf_master_secret(conn, &combined_pms));
EXPECT_SUCCESS(s2n_prf_hybrid_master_secret(conn, &combined_pms));
EXPECT_BYTEARRAY_EQUAL(expected_master_secret, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN);
EXPECT_SUCCESS(s2n_free(&conn->kex_params.client_key_exchange_message));
EXPECT_SUCCESS(s2n_connection_free(conn));
Expand Down
20 changes: 15 additions & 5 deletions tests/unit/s2n_tls_prf_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@

#define TEST_BLOB_SIZE 64

bool s2n_libcrypto_supports_tls_prf();
int s2n_prf(struct s2n_connection *conn, struct s2n_blob *secret,
struct s2n_blob *label, struct s2n_blob *seed_a,
struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out);
S2N_RESULT s2n_prf_get_digest_for_ems(struct s2n_connection *conn,
struct s2n_blob *message, s2n_hash_algorithm hash_alg, struct s2n_blob *output);
S2N_RESULT s2n_prf_tls_extended_master_secret(struct s2n_connection *conn,
struct s2n_blob *premaster_secret, struct s2n_blob *session_hash, struct s2n_blob *sha1_hash);
int s2n_prf_tls_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret);

/*
* Grabbed from gnutls-cli --insecure -d 9 www.example.com --ciphers AES --macs SHA --protocols TLS1.0
*
Expand Down Expand Up @@ -63,7 +73,7 @@ int main(int argc, char **argv)

struct s2n_blob pms = { 0 };
EXPECT_SUCCESS(s2n_blob_init(&pms, conn->secrets.version.tls12.rsa_premaster_secret, sizeof(conn->secrets.version.tls12.rsa_premaster_secret)));
EXPECT_SUCCESS(s2n_tls_prf_master_secret(conn, &pms));
EXPECT_SUCCESS(s2n_prf_tls_master_secret(conn, &pms));
EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0);

EXPECT_SUCCESS(s2n_connection_free(conn));
Expand Down Expand Up @@ -100,7 +110,7 @@ int main(int argc, char **argv)
*# session_hash)
*# [0..47];
*/
EXPECT_OK(s2n_tls_prf_extended_master_secret(conn, &premaster_secret, &hash_digest, NULL));
EXPECT_OK(s2n_prf_tls_extended_master_secret(conn, &premaster_secret, &hash_digest, NULL));
EXPECT_BYTEARRAY_EQUAL(extended_master_secret.data, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN);

EXPECT_SUCCESS(s2n_connection_free(conn));
Expand Down Expand Up @@ -293,20 +303,20 @@ int main(int argc, char **argv)
EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size);
EXPECT_MEMCPY_SUCCESS(conn->handshake_params.client_random, client_random_in.data, client_random_in.size);
EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size);
EXPECT_SUCCESS(s2n_tls_prf_master_secret(conn, &pms));
EXPECT_SUCCESS(s2n_prf_tls_master_secret(conn, &pms));
EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0);

EXPECT_SUCCESS(s2n_connection_free_handshake(conn));
EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size);
EXPECT_MEMCPY_SUCCESS(conn->handshake_params.client_random, client_random_in.data, client_random_in.size);
EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size);
EXPECT_FAILURE_WITH_ERRNO(s2n_tls_prf_master_secret(conn, &pms), S2N_ERR_NULL);
EXPECT_FAILURE_WITH_ERRNO(s2n_prf_tls_master_secret(conn, &pms), S2N_ERR_NULL);

EXPECT_SUCCESS(s2n_connection_wipe(conn));
EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size);
EXPECT_MEMCPY_SUCCESS(conn->handshake_params.client_random, client_random_in.data, client_random_in.size);
EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size);
EXPECT_SUCCESS(s2n_tls_prf_master_secret(conn, &pms));
EXPECT_SUCCESS(s2n_prf_tls_master_secret(conn, &pms));
EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0);

EXPECT_SUCCESS(s2n_connection_free(conn));
Expand Down
2 changes: 1 addition & 1 deletion tls/s2n_kex.c
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ const struct s2n_kex s2n_hybrid_ecdhe_kem = {
.server_key_send = &s2n_hybrid_server_key_send,
.client_key_recv = &s2n_hybrid_client_key_recv,
.client_key_send = &s2n_hybrid_client_key_send,
.prf = &s2n_hybrid_prf_master_secret,
.prf = &s2n_prf_hybrid_master_secret,
};

/* TLS1.3 key exchange is implemented differently from previous versions and does
Expand Down
63 changes: 43 additions & 20 deletions tls/s2n_prf.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,29 @@
#include "utils/s2n_mem.h"
#include "utils/s2n_safety.h"

#if defined(OPENSSL_IS_AWSLC)
#define S2N_LIBCRYPTO_SUPPORTS_TLS_PRF 1
#else
#define S2N_LIBCRYPTO_SUPPORTS_TLS_PRF 0
#endif

/* The s2n p_hash implementation is abstracted to allow for separate implementations, using
* either s2n's formally verified HMAC or OpenSSL's EVP HMAC, for use by the TLS PRF. */
struct s2n_p_hash_hmac {
int (*alloc)(struct s2n_prf_working_space *ws);
int (*init)(struct s2n_prf_working_space *ws, s2n_hmac_algorithm alg, struct s2n_blob *secret);
int (*update)(struct s2n_prf_working_space *ws, const void *data, uint32_t size);
int (*final)(struct s2n_prf_working_space *ws, void *digest, uint32_t size);
int (*reset)(struct s2n_prf_working_space *ws);
int (*cleanup)(struct s2n_prf_working_space *ws);
int (*free)(struct s2n_prf_working_space *ws);
};

S2N_RESULT s2n_prf_get_digest_for_ems(struct s2n_connection *conn, struct s2n_blob *message,
s2n_hash_algorithm hash_alg, struct s2n_blob *output);
S2N_RESULT s2n_prf_tls_extended_master_secret(struct s2n_connection *conn,
struct s2n_blob *premaster_secret, struct s2n_blob *session_hash, struct s2n_blob *sha1_hash);

S2N_RESULT s2n_key_material_init(struct s2n_key_material *key_material, struct s2n_connection *conn)
{
RESULT_ENSURE_REF(key_material);
Expand Down Expand Up @@ -114,7 +137,7 @@ S2N_RESULT s2n_key_material_init(struct s2n_key_material *key_material, struct s
return S2N_RESULT_OK;
}

static int s2n_sslv3_prf(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *seed_a,
static int s2n_prf_sslv3(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *seed_a,
struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out)
{
POSIX_ENSURE_REF(conn);
Expand Down Expand Up @@ -509,7 +532,7 @@ bool s2n_libcrypto_supports_tls_prf()
#endif
}

S2N_RESULT s2n_custom_prf(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *label,
S2N_RESULT s2n_prf_custom(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *label,
struct s2n_blob *seed_a, struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out)
{
/* We zero the out blob because p_hash works by XOR'ing with the existing
Expand Down Expand Up @@ -553,7 +576,7 @@ int CRYPTO_tls1_prf(const EVP_MD *digest,
const uint8_t *seed1, size_t seed1_len,
const uint8_t *seed2, size_t seed2_len);

S2N_RESULT s2n_libcrypto_prf(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *label,
S2N_RESULT s2n_prf_libcrypto(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *label,
struct s2n_blob *seed_a, struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out)
{
const EVP_MD *digest = NULL;
Expand Down Expand Up @@ -601,7 +624,7 @@ S2N_RESULT s2n_libcrypto_prf(struct s2n_connection *conn, struct s2n_blob *secre
return S2N_RESULT_OK;
}
#else
S2N_RESULT s2n_libcrypto_prf(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *label,
S2N_RESULT s2n_prf_libcrypto(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *label,
struct s2n_blob *seed_a, struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out)
{
RESULT_BAIL(S2N_ERR_UNIMPLEMENTED);
Expand All @@ -624,24 +647,24 @@ int s2n_prf(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blo
POSIX_ENSURE(S2N_IMPLIES(seed_c != NULL, seed_b != NULL), S2N_ERR_PRF_INVALID_SEED);

if (conn->actual_protocol_version == S2N_SSLv3) {
POSIX_GUARD(s2n_sslv3_prf(conn, secret, seed_a, seed_b, seed_c, out));
POSIX_GUARD(s2n_prf_sslv3(conn, secret, seed_a, seed_b, seed_c, out));
return S2N_SUCCESS;
}

/* By default, s2n-tls uses a custom PRF implementation. When operating in FIPS mode, the
* FIPS-validated libcrypto implementation is used instead, if an implementation is provided.
*/
if (s2n_is_in_fips_mode() && s2n_libcrypto_supports_tls_prf()) {
POSIX_GUARD_RESULT(s2n_libcrypto_prf(conn, secret, label, seed_a, seed_b, seed_c, out));
POSIX_GUARD_RESULT(s2n_prf_libcrypto(conn, secret, label, seed_a, seed_b, seed_c, out));
return S2N_SUCCESS;
}

POSIX_GUARD_RESULT(s2n_custom_prf(conn, secret, label, seed_a, seed_b, seed_c, out));
POSIX_GUARD_RESULT(s2n_prf_custom(conn, secret, label, seed_a, seed_b, seed_c, out));

return S2N_SUCCESS;
}

int s2n_tls_prf_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret)
int s2n_prf_tls_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret)
{
POSIX_ENSURE_REF(conn);

Expand All @@ -659,7 +682,7 @@ int s2n_tls_prf_master_secret(struct s2n_connection *conn, struct s2n_blob *prem
return s2n_prf(conn, premaster_secret, &label, &client_random, &server_random, NULL, &master_secret);
}

int s2n_hybrid_prf_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret)
int s2n_prf_hybrid_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret)
{
POSIX_ENSURE_REF(conn);

Expand All @@ -685,7 +708,7 @@ int s2n_prf_calculate_master_secret(struct s2n_connection *conn, struct s2n_blob
POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(conn), CLIENT_KEY);

if (!conn->ems_negotiated) {
POSIX_GUARD(s2n_tls_prf_master_secret(conn, premaster_secret));
POSIX_GUARD(s2n_prf_tls_master_secret(conn, premaster_secret));
return S2N_SUCCESS;
}

Expand All @@ -708,13 +731,13 @@ int s2n_prf_calculate_master_secret(struct s2n_connection *conn, struct s2n_blob
POSIX_GUARD(s2n_blob_init(&sha1_digest, sha1_data, sizeof(sha1_data)));
POSIX_GUARD_RESULT(s2n_prf_get_digest_for_ems(conn, &client_key_blob, S2N_HASH_MD5, &digest));
POSIX_GUARD_RESULT(s2n_prf_get_digest_for_ems(conn, &client_key_blob, S2N_HASH_SHA1, &sha1_digest));
POSIX_GUARD_RESULT(s2n_tls_prf_extended_master_secret(conn, premaster_secret, &digest, &sha1_digest));
POSIX_GUARD_RESULT(s2n_prf_tls_extended_master_secret(conn, premaster_secret, &digest, &sha1_digest));
} else {
s2n_hmac_algorithm prf_alg = conn->secure->cipher_suite->prf_alg;
s2n_hash_algorithm hash_alg = 0;
POSIX_GUARD(s2n_hmac_hash_alg(prf_alg, &hash_alg));
POSIX_GUARD_RESULT(s2n_prf_get_digest_for_ems(conn, &client_key_blob, hash_alg, &digest));
POSIX_GUARD_RESULT(s2n_tls_prf_extended_master_secret(conn, premaster_secret, &digest, NULL));
POSIX_GUARD_RESULT(s2n_prf_tls_extended_master_secret(conn, premaster_secret, &digest, NULL));
}
return S2N_SUCCESS;
}
Expand All @@ -728,7 +751,7 @@ int s2n_prf_calculate_master_secret(struct s2n_connection *conn, struct s2n_blob
*# session_hash)
*# [0..47];
*/
S2N_RESULT s2n_tls_prf_extended_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret, struct s2n_blob *session_hash, struct s2n_blob *sha1_hash)
S2N_RESULT s2n_prf_tls_extended_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret, struct s2n_blob *session_hash, struct s2n_blob *sha1_hash)
{
RESULT_ENSURE_REF(conn);

Expand Down Expand Up @@ -765,7 +788,7 @@ S2N_RESULT s2n_prf_get_digest_for_ems(struct s2n_connection *conn, struct s2n_bl
return S2N_RESULT_OK;
}

static int s2n_sslv3_finished(struct s2n_connection *conn, uint8_t prefix[4], struct s2n_hash_state *hash_workspace, uint8_t *out)
static int s2n_prf_sslv3_finished(struct s2n_connection *conn, uint8_t prefix[4], struct s2n_hash_state *hash_workspace, uint8_t *out)
{
POSIX_ENSURE_REF(conn);
POSIX_ENSURE_REF(conn->handshake.hashes);
Expand Down Expand Up @@ -808,24 +831,24 @@ static int s2n_sslv3_finished(struct s2n_connection *conn, uint8_t prefix[4], st
return 0;
}

static int s2n_sslv3_client_finished(struct s2n_connection *conn)
static int s2n_prf_sslv3_client_finished(struct s2n_connection *conn)
{
POSIX_ENSURE_REF(conn);
POSIX_ENSURE_REF(conn->handshake.hashes);

uint8_t prefix[4] = { 0x43, 0x4c, 0x4e, 0x54 };

return s2n_sslv3_finished(conn, prefix, &conn->handshake.hashes->hash_workspace, conn->handshake.client_finished);
return s2n_prf_sslv3_finished(conn, prefix, &conn->handshake.hashes->hash_workspace, conn->handshake.client_finished);
}

static int s2n_sslv3_server_finished(struct s2n_connection *conn)
static int s2n_prf_sslv3_server_finished(struct s2n_connection *conn)
{
POSIX_ENSURE_REF(conn);
POSIX_ENSURE_REF(conn->handshake.hashes);

uint8_t prefix[4] = { 0x53, 0x52, 0x56, 0x52 };

return s2n_sslv3_finished(conn, prefix, &conn->handshake.hashes->hash_workspace, conn->handshake.server_finished);
return s2n_prf_sslv3_finished(conn, prefix, &conn->handshake.hashes->hash_workspace, conn->handshake.server_finished);
}

int s2n_prf_client_finished(struct s2n_connection *conn)
Expand All @@ -842,7 +865,7 @@ int s2n_prf_client_finished(struct s2n_connection *conn)
struct s2n_blob label = { 0 };

if (conn->actual_protocol_version == S2N_SSLv3) {
return s2n_sslv3_client_finished(conn);
return s2n_prf_sslv3_client_finished(conn);
}

client_finished.data = conn->handshake.client_finished;
Expand Down Expand Up @@ -900,7 +923,7 @@ int s2n_prf_server_finished(struct s2n_connection *conn)
struct s2n_blob label = { 0 };

if (conn->actual_protocol_version == S2N_SSLv3) {
return s2n_sslv3_server_finished(conn);
return s2n_prf_sslv3_server_finished(conn);
}

server_finished.data = conn->handshake.server_finished;
Expand Down
36 changes: 2 additions & 34 deletions tls/s2n_prf.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,13 @@

#include <stdint.h>

#include "crypto/s2n_hash.h"
#include "crypto/s2n_hmac.h"
#include "tls/s2n_connection.h"
#include "utils/s2n_blob.h"

/* Enough to support TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, 2*SHA384_DIGEST_LEN + 2*AES256_KEY_SIZE */
#define S2N_MAX_KEY_BLOCK_LEN 160

#if defined(OPENSSL_IS_AWSLC)
#define S2N_LIBCRYPTO_SUPPORTS_TLS_PRF 1
#else
#define S2N_LIBCRYPTO_SUPPORTS_TLS_PRF 0
#endif

union p_hash_state {
struct s2n_hmac_state s2n_hmac;
struct s2n_evp_hmac_state evp_hmac;
Expand All @@ -41,18 +35,6 @@ struct s2n_prf_working_space {
uint8_t digest1[S2N_MAX_DIGEST_LEN];
};

/* The s2n p_hash implementation is abstracted to allow for separate implementations, using
* either s2n's formally verified HMAC or OpenSSL's EVP HMAC, for use by the TLS PRF. */
struct s2n_p_hash_hmac {
int (*alloc)(struct s2n_prf_working_space *ws);
int (*init)(struct s2n_prf_working_space *ws, s2n_hmac_algorithm alg, struct s2n_blob *secret);
int (*update)(struct s2n_prf_working_space *ws, const void *data, uint32_t size);
int (*final)(struct s2n_prf_working_space *ws, void *digest, uint32_t size);
int (*reset)(struct s2n_prf_working_space *ws);
int (*cleanup)(struct s2n_prf_working_space *ws);
int (*free)(struct s2n_prf_working_space *ws);
};

/* TLS key expansion results in an array of contiguous data which is then
* interpreted as the MAC, KEY and IV for the client and server.
*
Expand All @@ -75,27 +57,13 @@ struct s2n_key_material {

S2N_RESULT s2n_key_material_init(struct s2n_key_material *key_material, struct s2n_connection *conn);

#include "tls/s2n_connection.h"

S2N_RESULT s2n_prf_new(struct s2n_connection *conn);
S2N_RESULT s2n_prf_wipe(struct s2n_connection *conn);
S2N_RESULT s2n_prf_free(struct s2n_connection *conn);

int s2n_prf(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *label, struct s2n_blob *seed_a,
struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out);
int s2n_prf_calculate_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret);
int s2n_tls_prf_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret);
int s2n_hybrid_prf_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret);
S2N_RESULT s2n_tls_prf_extended_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret, struct s2n_blob *session_hash, struct s2n_blob *sha1_hash);
S2N_RESULT s2n_prf_get_digest_for_ems(struct s2n_connection *conn, struct s2n_blob *message, s2n_hash_algorithm hash_alg, struct s2n_blob *output);
int s2n_prf_hybrid_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret);
S2N_RESULT s2n_prf_generate_key_material(struct s2n_connection *conn, struct s2n_key_material *key_material);
int s2n_prf_key_expansion(struct s2n_connection *conn);
int s2n_prf_server_finished(struct s2n_connection *conn);
int s2n_prf_client_finished(struct s2n_connection *conn);

bool s2n_libcrypto_supports_tls_prf();

S2N_RESULT s2n_custom_prf(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *label,
struct s2n_blob *seed_a, struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out);
S2N_RESULT s2n_libcrypto_prf(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *label,
struct s2n_blob *seed_a, struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out);
Loading