From 4fe788cc172e6c06f40a42ba516a60f21369018c Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Tue, 2 May 2023 08:41:08 +0900 Subject: psk: Add basic support for RFC 9258 external PSK importer interface This adds a minimal, callback-based API to import external PSK, following RFC 9258. The client and the server importing external PSK are supposed to set a callback to retrieve PSK, which returns flags that may indicate the PSK is imported, along with the key: typedef int gnutls_psk_client_credentials_function3( gnutls_session_t session, gnutls_datum_t *username, gnutls_datum_t *key, gnutls_psk_key_flags *flags); typedef int gnutls_psk_server_credentials_function3( gnutls_session_t session, const gnutls_datum_t *username, gnutls_datum_t *key, gnutls_psk_key_flags *flags); Those callbacks are responsible to call gnutls_psk_format_imported_identity() for external PSKs to build a serialized PSK identity, and set GNUTLS_PSK_KEY_EXT in flags if the identity is an imported one. Signed-off-by: Daiki Ueno --- .gitignore | 1 + NEWS | 14 ++ devel/libgnutls.abignore | 12 ++ devel/symbols.last | 4 + doc/Makefile.am | 6 + doc/manpages/Makefile.am | 3 + lib/auth/dhe_psk.c | 8 +- lib/auth/psk.c | 4 +- lib/auth/psk.h | 13 +- lib/auth/psk_passwd.c | 23 ++- lib/auth/psk_passwd.h | 5 +- lib/auth/rsa_psk.c | 5 +- lib/ext/pre_shared_key.c | 352 +++++++++++++++++++++++++++++++++++++--- lib/ext/pre_shared_key.h | 11 -- lib/handshake-defs.h | 2 + lib/includes/gnutls/gnutls.h.in | 22 ++- lib/libgnutls.map | 10 ++ lib/psk.c | 163 ++++++++++++++++--- tests/Makefile.am | 3 +- tests/psk-importer.c | 216 ++++++++++++++++++++++++ 20 files changed, 791 insertions(+), 86 deletions(-) create mode 100644 tests/psk-importer.c diff --git a/.gitignore b/.gitignore index b474c09f41..2b0725526e 100644 --- a/.gitignore +++ b/.gitignore @@ -623,6 +623,7 @@ tests/privkey-keygen tests/privkey-verify-broken tests/protocol-set-allowlist tests/psk-file +tests/psk-importer tests/pskself tests/pskself2 tests/pubkey-import-export diff --git a/NEWS b/NEWS index 5f6e45a814..2f46982ed9 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,20 @@ See the end for copying conditions. extension without payload, the behavior can be reverted with the %NO_SHUFFLE_EXTENSIONS priority keyword. +** libgnutls: Add support for RFC 9258 external PSK importer. + This enables to deploy the same PSK across multiple TLS versions + (TLS 1.2 and TLS 1.3) in a secure manner. To use, the application + needs to set up a callback that formats the PSK identity using + gnutls_psk_format_imported_identity(). + +** API and ABI modifications: +gnutls_psk_client_credentials_function3: New typedef +gnutls_psk_server_credentials_function3: New typedef +gnutls_psk_set_server_credentials_function3: New function +gnutls_psk_set_client_credentials_function3: New function +gnutls_psk_format_imported_identity: New function +GNUTLS_PSK_KEY_EXT: New enum member of gnutls_psk_key_flags + * Version 3.8.0 (released 2023-02-09) ** libgnutls: Fix a Bleichenbacher oracle in the TLS RSA key exchange. diff --git a/devel/libgnutls.abignore b/devel/libgnutls.abignore index c19dce38e1..7ed194a515 100644 --- a/devel/libgnutls.abignore +++ b/devel/libgnutls.abignore @@ -70,3 +70,15 @@ name = drbg_aes_reseed # The following should be removed in the new release, after updating the # abi-dump repository: +[suppress_function] +name = gnutls_psk_set_server_credentials_function3 + +[suppress_function] +name = gnutls_psk_set_client_credentials_function3 + +[suppress_function] +name = gnutls_psk_format_imported_identity + +[suppress_type] +name = gnutls_psk_key_flags +changed_enumerators = GNUTLS_PSK_KEY_EXT diff --git a/devel/symbols.last b/devel/symbols.last index bd0de671fd..3500a8292b 100644 --- a/devel/symbols.last +++ b/devel/symbols.last @@ -17,6 +17,7 @@ GNUTLS_3_7_3@GNUTLS_3_7_3 GNUTLS_3_7_4@GNUTLS_3_7_4 GNUTLS_3_7_5@GNUTLS_3_7_5 GNUTLS_3_7_7@GNUTLS_3_7_7 +GNUTLS_3_8_1@GNUTLS_3_8_1 _gnutls_global_init_skip@GNUTLS_3_4 gnutls_aead_cipher_decrypt@GNUTLS_3_4 gnutls_aead_cipher_decryptv2@GNUTLS_3_6_10 @@ -680,6 +681,7 @@ gnutls_protocol_set_enabled@GNUTLS_3_7_3 gnutls_psk_allocate_client_credentials@GNUTLS_3_4 gnutls_psk_allocate_server_credentials@GNUTLS_3_4 gnutls_psk_client_get_hint@GNUTLS_3_4 +gnutls_psk_format_imported_identity@GNUTLS_3_8_1 gnutls_psk_free_client_credentials@GNUTLS_3_4 gnutls_psk_free_server_credentials@GNUTLS_3_4 gnutls_psk_server_get_username2@GNUTLS_3_6_13 @@ -687,10 +689,12 @@ gnutls_psk_server_get_username@GNUTLS_3_4 gnutls_psk_set_client_credentials2@GNUTLS_3_6_13 gnutls_psk_set_client_credentials@GNUTLS_3_4 gnutls_psk_set_client_credentials_function2@GNUTLS_3_6_13 +gnutls_psk_set_client_credentials_function3@GNUTLS_3_8_1 gnutls_psk_set_client_credentials_function@GNUTLS_3_4 gnutls_psk_set_params_function@GNUTLS_3_4 gnutls_psk_set_server_credentials_file@GNUTLS_3_4 gnutls_psk_set_server_credentials_function2@GNUTLS_3_6_13 +gnutls_psk_set_server_credentials_function3@GNUTLS_3_8_1 gnutls_psk_set_server_credentials_function@GNUTLS_3_4 gnutls_psk_set_server_credentials_hint@GNUTLS_3_4 gnutls_psk_set_server_dh_params@GNUTLS_3_4 diff --git a/doc/Makefile.am b/doc/Makefile.am index 6cfd6097ad..20e25dfa4e 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1772,6 +1772,8 @@ FUNCS += functions/gnutls_psk_allocate_server_credentials FUNCS += functions/gnutls_psk_allocate_server_credentials.short FUNCS += functions/gnutls_psk_client_get_hint FUNCS += functions/gnutls_psk_client_get_hint.short +FUNCS += functions/gnutls_psk_format_imported_identity +FUNCS += functions/gnutls_psk_format_imported_identity.short FUNCS += functions/gnutls_psk_free_client_credentials FUNCS += functions/gnutls_psk_free_client_credentials.short FUNCS += functions/gnutls_psk_free_server_credentials @@ -1788,6 +1790,8 @@ FUNCS += functions/gnutls_psk_set_client_credentials_function FUNCS += functions/gnutls_psk_set_client_credentials_function.short FUNCS += functions/gnutls_psk_set_client_credentials_function2 FUNCS += functions/gnutls_psk_set_client_credentials_function2.short +FUNCS += functions/gnutls_psk_set_client_credentials_function3 +FUNCS += functions/gnutls_psk_set_client_credentials_function3.short FUNCS += functions/gnutls_psk_set_params_function FUNCS += functions/gnutls_psk_set_params_function.short FUNCS += functions/gnutls_psk_set_server_credentials_file @@ -1796,6 +1800,8 @@ FUNCS += functions/gnutls_psk_set_server_credentials_function FUNCS += functions/gnutls_psk_set_server_credentials_function.short FUNCS += functions/gnutls_psk_set_server_credentials_function2 FUNCS += functions/gnutls_psk_set_server_credentials_function2.short +FUNCS += functions/gnutls_psk_set_server_credentials_function3 +FUNCS += functions/gnutls_psk_set_server_credentials_function3.short FUNCS += functions/gnutls_psk_set_server_credentials_hint FUNCS += functions/gnutls_psk_set_server_credentials_hint.short FUNCS += functions/gnutls_psk_set_server_dh_params diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am index 27cf77a57e..24ca59888f 100644 --- a/doc/manpages/Makefile.am +++ b/doc/manpages/Makefile.am @@ -732,6 +732,7 @@ APIMANS += gnutls_protocol_set_enabled.3 APIMANS += gnutls_psk_allocate_client_credentials.3 APIMANS += gnutls_psk_allocate_server_credentials.3 APIMANS += gnutls_psk_client_get_hint.3 +APIMANS += gnutls_psk_format_imported_identity.3 APIMANS += gnutls_psk_free_client_credentials.3 APIMANS += gnutls_psk_free_server_credentials.3 APIMANS += gnutls_psk_server_get_username.3 @@ -740,10 +741,12 @@ APIMANS += gnutls_psk_set_client_credentials.3 APIMANS += gnutls_psk_set_client_credentials2.3 APIMANS += gnutls_psk_set_client_credentials_function.3 APIMANS += gnutls_psk_set_client_credentials_function2.3 +APIMANS += gnutls_psk_set_client_credentials_function3.3 APIMANS += gnutls_psk_set_params_function.3 APIMANS += gnutls_psk_set_server_credentials_file.3 APIMANS += gnutls_psk_set_server_credentials_function.3 APIMANS += gnutls_psk_set_server_credentials_function2.3 +APIMANS += gnutls_psk_set_server_credentials_function3.3 APIMANS += gnutls_psk_set_server_credentials_hint.3 APIMANS += gnutls_psk_set_server_dh_params.3 APIMANS += gnutls_psk_set_server_known_dh_params.3 diff --git a/lib/auth/dhe_psk.c b/lib/auth/dhe_psk.c index ab5eddd16a..14cf5ba918 100644 --- a/lib/auth/dhe_psk.c +++ b/lib/auth/dhe_psk.c @@ -103,7 +103,7 @@ static int gen_ecdhe_psk_client_kx(gnutls_session_t session, if (cred == NULL) return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS); - ret = _gnutls_find_psk_key(session, cred, &username, &key, &free); + ret = _gnutls_find_psk_key(session, cred, &username, &key, NULL, &free); if (ret < 0) return gnutls_assert_val(ret); @@ -146,7 +146,7 @@ static int gen_dhe_psk_client_kx(gnutls_session_t session, if (cred == NULL) return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS); - ret = _gnutls_find_psk_key(session, cred, &username, &key, &free); + ret = _gnutls_find_psk_key(session, cred, &username, &key, NULL, &free); if (ret < 0) return gnutls_assert_val(ret); @@ -308,7 +308,7 @@ static int proc_dhe_psk_client_kx(gnutls_session_t session, uint8_t *data, data += username.size + 2; ret = _gnutls_psk_pwd_find_entry(session, info->username, - info->username_len, &psk_key); + info->username_len, &psk_key, NULL); if (ret < 0) return gnutls_assert_val(ret); @@ -374,7 +374,7 @@ static int proc_ecdhe_psk_client_kx(gnutls_session_t session, uint8_t *data, /* should never fail. It will always return a key even if it is * a random one */ ret = _gnutls_psk_pwd_find_entry(session, info->username, - info->username_len, &psk_key); + info->username_len, &psk_key, NULL); if (ret < 0) return gnutls_assert_val(ret); diff --git a/lib/auth/psk.c b/lib/auth/psk.c index 17206f69c5..8ddb239823 100644 --- a/lib/auth/psk.c +++ b/lib/auth/psk.c @@ -136,7 +136,7 @@ int _gnutls_gen_psk_client_kx(gnutls_session_t session, gnutls_buffer_st *data) return GNUTLS_E_INTERNAL_ERROR; } - ret = _gnutls_find_psk_key(session, cred, &username, &key, &free); + ret = _gnutls_find_psk_key(session, cred, &username, &key, NULL, &free); if (ret < 0) return gnutls_assert_val(ret); @@ -224,7 +224,7 @@ static int _gnutls_proc_psk_client_kx(gnutls_session_t session, uint8_t *data, return gnutls_assert_val(ret); ret = _gnutls_psk_pwd_find_entry(session, info->username, - info->username_len, &psk_key); + info->username_len, &psk_key, NULL); if (ret < 0) return gnutls_assert_val(ret); diff --git a/lib/auth/psk.h b/lib/auth/psk.h index 06d7913c85..9e1f94b1fe 100644 --- a/lib/auth/psk.h +++ b/lib/auth/psk.h @@ -36,19 +36,20 @@ typedef struct gnutls_psk_client_credentials_st { gnutls_datum_t username; gnutls_datum_t key; - gnutls_psk_client_credentials_function2 *get_function; - gnutls_psk_client_credentials_function *get_function_legacy; + gnutls_psk_client_credentials_function3 *get_function; + gnutls_psk_client_credentials_function2 *get_function2; + gnutls_psk_client_credentials_function *get_function1; /* TLS 1.3 - The HMAC algorithm to use to compute the binder values */ const mac_entry_st *binder_algo; } psk_client_credentials_st; typedef struct gnutls_psk_server_credentials_st { char *password_file; - /* callback function, instead of reading the - * password files. + /* callback functions, instead of reading the password files. */ - gnutls_psk_server_credentials_function2 *pwd_callback; - gnutls_psk_server_credentials_function *pwd_callback_legacy; + gnutls_psk_server_credentials_function3 *pwd_callback; + gnutls_psk_server_credentials_function2 *pwd_callback2; + gnutls_psk_server_credentials_function *pwd_callback1; /* For DHE_PSK */ gnutls_dh_params_t dh_params; diff --git a/lib/auth/psk_passwd.c b/lib/auth/psk_passwd.c index 70f59c7738..eff339dd17 100644 --- a/lib/auth/psk_passwd.c +++ b/lib/auth/psk_passwd.c @@ -149,7 +149,8 @@ static int _randomize_psk(gnutls_datum_t *psk) * If the user doesn't exist a random password is returned instead. */ int _gnutls_psk_pwd_find_entry(gnutls_session_t session, const char *username, - uint16_t username_len, gnutls_datum_t *psk) + uint16_t username_len, gnutls_datum_t *psk, + gnutls_psk_key_flags *flags) { gnutls_psk_server_credentials_t cred; FILE *fp; @@ -170,8 +171,7 @@ int _gnutls_psk_pwd_find_entry(gnutls_session_t session, const char *username, * set, use it. */ if (cred->pwd_callback != NULL) { - ret = cred->pwd_callback(session, &username_datum, psk); - + ret = cred->pwd_callback(session, &username_datum, psk, flags); if (ret == 1) { /* the user does not exist */ ret = _randomize_psk(psk); if (ret < 0) { @@ -212,6 +212,9 @@ int _gnutls_psk_pwd_find_entry(gnutls_session_t session, const char *username, ret = GNUTLS_E_SRP_PWD_ERROR; goto cleanup; } + if (flags) { + *flags = 0; + } ret = 0; goto cleanup; } @@ -224,6 +227,9 @@ int _gnutls_psk_pwd_find_entry(gnutls_session_t session, const char *username, goto cleanup; } + if (flags) { + *flags = 0; + } ret = 0; cleanup: if (fp != NULL) @@ -241,7 +247,7 @@ cleanup: int _gnutls_find_psk_key(gnutls_session_t session, gnutls_psk_client_credentials_t cred, gnutls_datum_t *username, gnutls_datum_t *key, - int *free) + gnutls_psk_key_flags *flags, int *free) { int ret; @@ -252,11 +258,14 @@ int _gnutls_find_psk_key(gnutls_session_t session, username->size = cred->username.size; key->data = cred->key.data; key->size = cred->key.size; + if (flags) { + *flags = 0; + } } else if (cred->get_function != NULL) { - ret = cred->get_function(session, username, key); - - if (ret) + ret = cred->get_function(session, username, key, flags); + if (ret) { return gnutls_assert_val(ret); + } *free = 1; } else diff --git a/lib/auth/psk_passwd.h b/lib/auth/psk_passwd.h index 18ac72b34b..2f270cc377 100644 --- a/lib/auth/psk_passwd.h +++ b/lib/auth/psk_passwd.h @@ -25,11 +25,12 @@ /* this is locally allocated. It should be freed using the provided function */ int _gnutls_psk_pwd_find_entry(gnutls_session_t, const char *username, - uint16_t username_len, gnutls_datum_t *key); + uint16_t username_len, gnutls_datum_t *key, + gnutls_psk_key_flags *flags); int _gnutls_find_psk_key(gnutls_session_t session, gnutls_psk_client_credentials_t cred, gnutls_datum_t *username, gnutls_datum_t *key, - int *free); + gnutls_psk_key_flags *flags, int *free); #endif /* GNUTLS_LIB_AUTH_PSK_PASSWD_H */ diff --git a/lib/auth/rsa_psk.c b/lib/auth/rsa_psk.c index e9e99761cf..6e3fbbda82 100644 --- a/lib/auth/rsa_psk.c +++ b/lib/auth/rsa_psk.c @@ -193,7 +193,7 @@ static int _gnutls_gen_rsa_psk_client_kx(gnutls_session_t session, return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } - ret = _gnutls_find_psk_key(session, cred, &username, &key, &free); + ret = _gnutls_find_psk_key(session, cred, &username, &key, NULL, &free); if (ret < 0) return gnutls_assert_val(ret); @@ -382,7 +382,8 @@ static int _gnutls_proc_rsa_psk_client_kx(gnutls_session_t session, /* find the key of this username */ ret = _gnutls_psk_pwd_find_entry(session, info->username, - strlen(info->username), &pwd_psk); + strlen(info->username), &pwd_psk, + NULL); if (ret < 0) { gnutls_assert(); goto cleanup; diff --git a/lib/ext/pre_shared_key.c b/lib/ext/pre_shared_key.c index 44797bb5ad..f632f972bc 100644 --- a/lib/ext/pre_shared_key.c +++ b/lib/ext/pre_shared_key.c @@ -35,6 +35,14 @@ #include #include +inline static bool +have_psk_credentials(const gnutls_psk_client_credentials_t cred, + gnutls_session_t session) +{ + return (cred->get_function || cred->username.data) && + session->internals.priorities->have_psk; +} + static int compute_psk_from_ticket(const tls13_ticket_st *ticket, gnutls_datum_t *key) { @@ -61,16 +69,45 @@ static int compute_psk_from_ticket(const tls13_ticket_st *ticket, return ret; } +enum binder_type { + BINDER_EXT, + BINDER_RES, + BINDER_IMP +}; + +static const char *get_binder_label(enum binder_type type, size_t *size) +{ + static const char ext_label[] = EXT_BINDER_LABEL; + static const char res_label[] = RES_BINDER_LABEL; + static const char imp_label[] = IMP_BINDER_LABEL; + const char *label; + + switch (type) { + case BINDER_EXT: + label = ext_label; + *size = sizeof(ext_label) - 1; + break; + case BINDER_RES: + label = res_label; + *size = sizeof(res_label) - 1; + break; + case BINDER_IMP: + label = imp_label; + *size = sizeof(imp_label) - 1; + break; + default: + assert(0); + } + + return label; +} + static int compute_binder_key(const mac_entry_st *prf, const uint8_t *key, - size_t keylen, bool resuming, void *out) + size_t keylen, enum binder_type type, void *out) { int ret; - const char ext_label[] = EXT_BINDER_LABEL; - const size_t ext_label_len = sizeof(ext_label) - 1; - const char res_label[] = RES_BINDER_LABEL; - const size_t res_label_len = sizeof(res_label) - 1; - const char *label = resuming ? res_label : ext_label; - size_t label_len = resuming ? res_label_len : ext_label_len; + size_t label_len; + const char *label = get_binder_label(type, &label_len); uint8_t tmp_key[MAX_HASH_SIZE]; /* Compute HKDF-Extract(0, psk) */ @@ -90,8 +127,8 @@ static int compute_binder_key(const mac_entry_st *prf, const uint8_t *key, static int compute_psk_binder(gnutls_session_t session, const mac_entry_st *prf, unsigned binders_length, int exts_length, int ext_offset, const gnutls_datum_t *psk, - const gnutls_datum_t *client_hello, bool resuming, - void *out) + const gnutls_datum_t *client_hello, + enum binder_type type, void *out) { int ret; unsigned client_hello_pos, extensions_len_pos; @@ -176,8 +213,7 @@ static int compute_psk_binder(gnutls_session_t session, const mac_entry_st *prf, } } - ret = compute_binder_key(prf, psk->data, psk->size, resuming, - binder_key); + ret = compute_binder_key(prf, psk->data, psk->size, type, binder_key); if (ret < 0) { gnutls_assert(); goto error; @@ -264,6 +300,172 @@ int _gnutls_generate_early_secrets_for_psk(gnutls_session_t session) return 0; } +/** + * gnutls_psk_format_imported_identity: + * @identity: external identity + * @context: optional contextual information + * @version: protocol version to which the PSK is imported + * @hash: hash algorithm used for KDF + * @imported_identity: where the imported identity is stored + * + * This formats an external PSK identity @identity into an imported + * form, described in RFC 9258 as ImportedIdentity. + * + * Upon success, the data field of @imported_identity is allocated + * using gnutls_malloc() and the caller must free the memory after + * use. + * + * Returns: %GNUTLS_E_SUCCESS (0) on success, otherwise a negative error code. + * Since: 3.8.1 + */ +int gnutls_psk_format_imported_identity(const gnutls_datum_t *identity, + const gnutls_datum_t *context, + gnutls_protocol_t version, + gnutls_digest_algorithm_t hash, + gnutls_datum_t *imported_identity) +{ + gnutls_buffer_st buf; + const version_entry_st *ver = version_to_entry(version); + const mac_entry_st *prf = hash_to_entry(hash); + uint16_t target_protocol; + uint16_t target_kdf; + int ret; + + _gnutls_buffer_init(&buf); + + /* external_identity */ + ret = _gnutls_buffer_append_data_prefix(&buf, 16, identity->data, + identity->size); + if (ret < 0) { + goto error; + } + + /* context */ + ret = _gnutls_buffer_append_data_prefix(&buf, 16, context->data, + context->size); + if (ret < 0) { + goto error; + } + + /* target_protocol */ + target_protocol = ver->major << 8 | ver->minor; + ret = _gnutls_buffer_append_prefix(&buf, 16, target_protocol); + if (ret < 0) { + goto error; + } + + /* target_kdf */ + switch (prf->id) { + case GNUTLS_MAC_SHA256: + target_kdf = 0x0001; + break; + case GNUTLS_MAC_SHA384: + target_kdf = 0x0002; + break; + default: + ret = gnutls_assert_val(GNUTLS_E_UNKNOWN_HASH_ALGORITHM); + goto error; + } + ret = _gnutls_buffer_append_prefix(&buf, 16, target_kdf); + if (ret < 0) { + goto error; + } + + ret = _gnutls_buffer_to_datum(&buf, imported_identity, 0); + if (ret < 0) { + goto error; + } + return 0; + +error: + _gnutls_buffer_clear(&buf); + return ret; +} + +static int derive_ipsk(const mac_entry_st *prf, + const gnutls_datum_t *imported_identity, + const gnutls_datum_t *epsk, uint8_t ipsk[MAX_HASH_SIZE]) +{ + uint8_t epskx[MAX_HASH_SIZE]; + uint8_t hashed_identity[MAX_HASH_SIZE]; + int ret; + + /* epskx = HKDF-Extract(0, epsk) */ + ret = _tls13_init_secret2(prf, epsk->data, epsk->size, epskx); + if (ret < 0) { + return ret; + } + ret = gnutls_hash_fast((gnutls_digest_algorithm_t)prf->id, + imported_identity->data, imported_identity->size, + hashed_identity); + if (ret < 0) { + return ret; + } + /* ipskx = HKDF-Expand-Label(epskx, "derived psk", Hash(ImportedIdentity), L) */ + return _tls13_expand_secret2(prf, DERIVED_PSK_LABEL, + sizeof(DERIVED_PSK_LABEL) - 1, + hashed_identity, prf->output_size, epskx, + prf->output_size, ipsk); +} + +/* This does the opposite of gnutls_psk_format_imported_identity. + * Note that this does not allocate memory, and the data field of + * identity and context must not be freed. + */ +static int parse_imported_identity(const gnutls_datum_t *imported_identity, + gnutls_datum_t *identity, + gnutls_datum_t *context, + gnutls_protocol_t *version, + gnutls_digest_algorithm_t *hash) +{ + uint16_t target_protocol; + uint16_t target_kdf; + gnutls_buffer_st buf; + size_t size; + int ret; + + _gnutls_ro_buffer_from_datum(&buf, (gnutls_datum_t *)imported_identity); + + /* external_identity */ + ret = _gnutls_buffer_pop_datum_prefix16(&buf, identity); + if (ret < 0) { + return ret; + } + + /* context */ + ret = _gnutls_buffer_pop_datum_prefix16(&buf, context); + if (ret < 0) { + return ret; + } + + /* target_protocol */ + ret = _gnutls_buffer_pop_prefix16(&buf, &size, 0); + if (ret < 0) { + return ret; + } + target_protocol = size; + *version = _gnutls_version_get((target_protocol >> 8) & 0xFF, + target_protocol & 0xFF); + + /* target_kdf */ + ret = _gnutls_buffer_pop_prefix16(&buf, &size, 0); + if (ret < 0) { + return ret; + } + target_kdf = size; + switch (target_kdf) { + case 0x0001: + *hash = GNUTLS_DIG_SHA256; + break; + case 0x0002: + *hash = GNUTLS_DIG_SHA384; + break; + default: + return gnutls_assert_val(GNUTLS_E_UNKNOWN_HASH_ALGORITHM); + } + return 0; +} + static int client_send_params(gnutls_session_t session, gnutls_buffer_t extdata, const gnutls_psk_client_credentials_t cred) { @@ -282,11 +484,12 @@ static int client_send_params(gnutls_session_t session, gnutls_buffer_t extdata, psk_auth_info_t info = NULL; unsigned psk_id_len = 0; unsigned binders_len, binders_pos; + bool imported = false; tls13_ticket_st *ticket = &session->internals.tls13_ticket; if (((session->internals.flags & GNUTLS_NO_TICKETS) || session->internals.tls13_ticket.ticket.data == NULL) && - (!cred || !_gnutls_have_psk_credentials(cred, session))) { + (!cred || !have_psk_credentials(cred, session))) { return 0; } @@ -353,8 +556,9 @@ static int client_send_params(gnutls_session_t session, gnutls_buffer_t extdata, } ignore_ticket: - if (cred && _gnutls_have_psk_credentials(cred, session)) { + if (cred && have_psk_credentials(cred, session)) { gnutls_datum_t tkey; + gnutls_psk_key_flags flags; if (cred->binder_algo == NULL) { gnutls_assert(); @@ -366,7 +570,7 @@ ignore_ticket: prf_psk = cred->binder_algo; ret = _gnutls_find_psk_key(session, cred, &username, &tkey, - &free_username); + &flags, &free_username); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -390,6 +594,50 @@ ignore_ticket: user_key.size = tkey.size; } + if (flags & GNUTLS_PSK_KEY_EXT) { + uint8_t ipsk[MAX_HASH_SIZE]; + gnutls_datum_t imported_identity = { NULL, 0 }; + gnutls_datum_t context = { NULL, 0 }; + gnutls_protocol_t version; + gnutls_digest_algorithm_t hash; + const version_entry_st *vers; + + ret = parse_imported_identity(&username, + &imported_identity, + &context, &version, + &hash); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + vers = version_to_entry(version); + if (unlikely(!vers || !vers->tls13_sem)) { + gnutls_assert(); + goto cleanup; + } + if (hash != MAC_TO_DIG(prf_psk->id)) { + gnutls_assert(); + goto cleanup; + } + + ret = derive_ipsk(prf_psk, &username, &user_key, ipsk); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + _gnutls_free_datum(&user_key); + ret = _gnutls_set_datum(&user_key, ipsk, + prf_psk->output_size); + zeroize_key(ipsk, sizeof(ipsk)); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + imported = true; + } + ret = _gnutls_auth_info_init(session, GNUTLS_CRD_PSK, sizeof(psk_auth_info_st), 1); if (ret < 0) { @@ -457,7 +705,8 @@ ignore_ticket: ret = compute_psk_binder(session, prf_res, binders_len, binders_pos, ext_offset, &rkey, - &client_hello, 1, binder_value); + &client_hello, BINDER_RES, + binder_value); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -498,7 +747,9 @@ ignore_ticket: ret = compute_psk_binder(session, prf_psk, binders_len, binders_pos, ext_offset, &user_key, - &client_hello, 0, binder_value); + &client_hello, + imported ? BINDER_IMP : BINDER_EXT, + binder_value); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -574,7 +825,7 @@ static int server_recv_params(gnutls_session_t session, /* These values should be set properly when session ticket is accepted. */ uint32_t ticket_age = UINT32_MAX; struct timespec ticket_creation_time = { 0, 0 }; - bool resuming; + enum binder_type binder_type; bool refuse_early_data = false; ret = _gnutls13_psk_ext_parser_init(&psk_parser, data, len); @@ -625,22 +876,69 @@ static int server_recv_params(gnutls_session_t session, tls13_ticket_deinit(&ticket_data); - resuming = 1; + binder_type = BINDER_RES; break; } else if (pskcred && psk.ob_ticket_age == 0 && psk.identity.size > 0 && psk.identity.size <= MAX_USERNAME_SIZE) { + gnutls_psk_key_flags flags; + uint8_t ipsk[MAX_HASH_SIZE]; + prf = pskcred->binder_algo; /* this fails only on configuration errors; as such we always * return its error code in that case */ ret = _gnutls_psk_pwd_find_entry( session, (char *)psk.identity.data, - psk.identity.size, &key); - if (ret < 0) + psk.identity.size, &key, &flags); + if (ret < 0) { return gnutls_assert_val(ret); + } + + if (flags & GNUTLS_PSK_KEY_EXT) { + gnutls_datum_t imported_identity = { NULL, 0 }; + gnutls_datum_t context = { NULL, 0 }; + gnutls_protocol_t version; + gnutls_digest_algorithm_t hash; + const version_entry_st *vers; - resuming = 0; + ret = parse_imported_identity( + &psk.identity, &imported_identity, + &context, &version, &hash); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + vers = version_to_entry(version); + if (unlikely(!vers || !vers->tls13_sem)) { + gnutls_assert(); + goto fail; + } + if (hash != MAC_TO_DIG(prf->id)) { + gnutls_assert(); + goto fail; + } + + ret = derive_ipsk(prf, &psk.identity, &key, + ipsk); + _gnutls_free_temp_key_datum(&key); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + ret = _gnutls_set_datum(&key, ipsk, + prf->output_size); + zeroize_key(ipsk, sizeof(ipsk)); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + binder_type = BINDER_IMP; + } else { + binder_type = BINDER_EXT; + } break; } } @@ -667,7 +965,7 @@ static int server_recv_params(gnutls_session_t session, /* Compute the binder value for this PSK */ ret = compute_psk_binder(session, prf, psk_parser.binders_len + 2, 0, 0, - &key, &full_client_hello, resuming, + &key, &full_client_hello, binder_type, binder_value); if (ret < 0) { gnutls_assert(); @@ -691,7 +989,7 @@ static int server_recv_params(gnutls_session_t session, /* save the username in psk_auth_info to make it available * using gnutls_psk_server_get_username() */ - if (!resuming) { + if (binder_type != BINDER_RES) { assert(psk.identity.size <= MAX_USERNAME_SIZE); ret = _gnutls_auth_info_init(session, GNUTLS_CRD_PSK, @@ -760,10 +1058,12 @@ static int server_recv_params(gnutls_session_t session, /* Reference the selected pre-shared key */ session->key.binders[0].psk.data = key.data; session->key.binders[0].psk.size = key.size; + key.data = NULL; + key.size = 0; session->key.binders[0].idx = psk_index; session->key.binders[0].prf = prf; - session->key.binders[0].resumption = resuming; + session->key.binders[0].resumption = binder_type == BINDER_RES; ret = _gnutls_generate_early_secrets_for_psk(session); if (ret < 0) { @@ -771,10 +1071,8 @@ static int server_recv_params(gnutls_session_t session, goto fail; } - return 0; - fail: - gnutls_free(key.data); + _gnutls_free_datum(&key); return ret; } diff --git a/lib/ext/pre_shared_key.h b/lib/ext/pre_shared_key.h index 496d275a7f..f6a088c614 100644 --- a/lib/ext/pre_shared_key.h +++ b/lib/ext/pre_shared_key.h @@ -9,17 +9,6 @@ extern const hello_ext_entry_st ext_mod_pre_shared_key; -inline static unsigned -_gnutls_have_psk_credentials(const gnutls_psk_client_credentials_t cred, - gnutls_session_t session) -{ - if ((cred->get_function || cred->username.data) && - session->internals.priorities->have_psk) - return 1; - else - return 0; -} - int _gnutls_generate_early_secrets_for_psk(gnutls_session_t session); #endif /* GNUTLS_LIB_EXT_PRE_SHARED_KEY_H */ diff --git a/lib/handshake-defs.h b/lib/handshake-defs.h index 1b0c250e75..51aa5d2369 100644 --- a/lib/handshake-defs.h +++ b/lib/handshake-defs.h @@ -25,10 +25,12 @@ #define EARLY_TRAFFIC_LABEL "c e traffic" #define EXT_BINDER_LABEL "ext binder" #define RES_BINDER_LABEL "res binder" +#define IMP_BINDER_LABEL "imp binder" #define EARLY_EXPORTER_MASTER_LABEL "e exp master" #define HANDSHAKE_CLIENT_TRAFFIC_LABEL "c hs traffic" #define HANDSHAKE_SERVER_TRAFFIC_LABEL "s hs traffic" #define DERIVED_LABEL "derived" +#define DERIVED_PSK_LABEL "derived psk" #define APPLICATION_CLIENT_TRAFFIC_LABEL "c ap traffic" #define APPLICATION_SERVER_TRAFFIC_LABEL "s ap traffic" #define APPLICATION_TRAFFIC_UPDATE "traffic upd" diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index f4c003dcde..ec132cb5c3 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -2533,12 +2533,14 @@ typedef struct gnutls_psk_client_credentials_st *gnutls_psk_client_credentials_t * gnutls_psk_key_flags: * @GNUTLS_PSK_KEY_RAW: PSK-key in raw format. * @GNUTLS_PSK_KEY_HEX: PSK-key in hex format. + * @GNUTLS_PSK_KEY_EXT: PSK-key is external and to be imported. * * Enumeration of different PSK key flags. */ typedef enum gnutls_psk_key_flags { GNUTLS_PSK_KEY_RAW = 0, - GNUTLS_PSK_KEY_HEX + GNUTLS_PSK_KEY_HEX = 1 << 0, + GNUTLS_PSK_KEY_EXT = 1 << 1 } gnutls_psk_key_flags; void gnutls_psk_free_client_credentials(gnutls_psk_client_credentials_t sc); @@ -2570,12 +2572,24 @@ typedef int gnutls_psk_server_credentials_function(gnutls_session_t, gnutls_datum_t *key); typedef int gnutls_psk_server_credentials_function2( gnutls_session_t, const gnutls_datum_t *username, gnutls_datum_t *key); +typedef int gnutls_psk_server_credentials_function3( + gnutls_session_t, const gnutls_datum_t *username, gnutls_datum_t *key, + gnutls_psk_key_flags *flags); void gnutls_psk_set_server_credentials_function( gnutls_psk_server_credentials_t cred, gnutls_psk_server_credentials_function *func); void gnutls_psk_set_server_credentials_function2( gnutls_psk_server_credentials_t cred, gnutls_psk_server_credentials_function2 *func); +void gnutls_psk_set_server_credentials_function3( + gnutls_psk_server_credentials_t cred, + gnutls_psk_server_credentials_function3 *func); + +int gnutls_psk_format_imported_identity(const gnutls_datum_t *identity, + const gnutls_datum_t *context, + gnutls_protocol_t version, + gnutls_digest_algorithm_t hash, + gnutls_datum_t *imported_identity); typedef int gnutls_psk_client_credentials_function(gnutls_session_t, char **username, @@ -2583,12 +2597,18 @@ typedef int gnutls_psk_client_credentials_function(gnutls_session_t, typedef int gnutls_psk_client_credentials_function2(gnutls_session_t, gnutls_datum_t *username, gnutls_datum_t *key); +typedef int gnutls_psk_client_credentials_function3( + gnutls_session_t, gnutls_datum_t *username, gnutls_datum_t *key, + gnutls_psk_key_flags *flags); void gnutls_psk_set_client_credentials_function( gnutls_psk_client_credentials_t cred, gnutls_psk_client_credentials_function *func); void gnutls_psk_set_client_credentials_function2( gnutls_psk_client_credentials_t cred, gnutls_psk_client_credentials_function2 *func); +void gnutls_psk_set_client_credentials_function3( + gnutls_psk_client_credentials_t cred, + gnutls_psk_client_credentials_function3 *func); int gnutls_hex_encode(const gnutls_datum_t *data, char *result, size_t *result_size); diff --git a/lib/libgnutls.map b/lib/libgnutls.map index 72e425c3a0..fae2070dfc 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1409,6 +1409,16 @@ GNUTLS_3_7_7 *; } GNUTLS_3_7_5; +GNUTLS_3_8_1 +{ + global: + gnutls_psk_set_server_credentials_function3; + gnutls_psk_set_client_credentials_function3; + gnutls_psk_format_imported_identity; + local: + *; +} GNUTLS_3_7_7; + GNUTLS_FIPS140_3_4 { global: gnutls_cipher_self_test; diff --git a/lib/psk.c b/lib/psk.c index ac8c7751ce..8d1db2fe7a 100644 --- a/lib/psk.c +++ b/lib/psk.c @@ -280,9 +280,9 @@ int gnutls_psk_set_server_credentials_hint(gnutls_psk_server_credentials_t res, return 0; } -static int call_server_callback_legacy(gnutls_session_t session, - const gnutls_datum_t *username, - gnutls_datum_t *key) +static int call_server_callback1(gnutls_session_t session, + const gnutls_datum_t *username, + gnutls_datum_t *key) { gnutls_psk_server_credentials_t cred = (gnutls_psk_server_credentials_t)_gnutls_get_cred( @@ -290,8 +290,29 @@ static int call_server_callback_legacy(gnutls_session_t session, if (unlikely(cred == NULL)) return gnutls_assert_val(-1); - return cred->pwd_callback_legacy(session, (const char *)username->data, - key); + return cred->pwd_callback1(session, (const char *)username->data, key); +} + +static int call_server_callback2(gnutls_session_t session, + const gnutls_datum_t *username, + gnutls_datum_t *key, + gnutls_psk_key_flags *flags) +{ + gnutls_psk_server_credentials_t cred; + int ret; + + cred = (gnutls_psk_server_credentials_t)_gnutls_get_cred( + session, GNUTLS_CRD_PSK); + if (unlikely(cred == NULL)) + return gnutls_assert_val(-1); + + ret = cred->pwd_callback2(session, username, key); + if (ret >= 0) { + if (flags) { + *flags = 0; + } + } + return ret; } /** @@ -318,8 +339,9 @@ void gnutls_psk_set_server_credentials_function( gnutls_psk_server_credentials_t cred, gnutls_psk_server_credentials_function *func) { - cred->pwd_callback_legacy = func; - cred->pwd_callback = call_server_callback_legacy; + cred->pwd_callback1 = func; + cred->pwd_callback2 = call_server_callback1; + cred->pwd_callback = call_server_callback2; } /** @@ -349,31 +371,88 @@ void gnutls_psk_set_server_credentials_function2( gnutls_psk_server_credentials_t cred, gnutls_psk_server_credentials_function2 func) { + cred->pwd_callback1 = NULL; + cred->pwd_callback2 = func; + cred->pwd_callback = call_server_callback2; +} + +/** + * gnutls_psk_set_server_credentials_function3: + * @cred: is a #gnutls_psk_server_credentials_t type. + * @func: is the callback function + * + * This function can be used to set a callback to retrieve the user's PSK credentials. + * The callback's function form is: + * int (*callback)(gnutls_session_t, const gnutls_datum_t* username, + * gnutls_datum_t* key, gnutls_psk_key_flags *flags); + * + * This callback function has the same semantics as that of + * gnutls_psk_set_server_credentials_function2(), but it returns flags + * associated with the key. The callback may import external PSK + * using the method described in RFC 9258 by using + * gnutls_psk_format_imported_identity(). + * + * @username contains the actual username. + * The @key must be filled in using the gnutls_malloc(). + * + * In case the callback returned a negative number then gnutls will + * assume that the username does not exist. + * + * The callback function will only be called once per handshake. The + * callback function should return 0 on success, while -1 indicates + * an error. + **/ +void gnutls_psk_set_server_credentials_function3( + gnutls_psk_server_credentials_t cred, + gnutls_psk_server_credentials_function3 func) +{ + cred->pwd_callback1 = NULL; + cred->pwd_callback2 = NULL; cred->pwd_callback = func; - cred->pwd_callback_legacy = NULL; } -static int call_client_callback_legacy(gnutls_session_t session, - gnutls_datum_t *username, - gnutls_datum_t *key) +static int call_client_callback1(gnutls_session_t session, + gnutls_datum_t *username, gnutls_datum_t *key) { + gnutls_psk_client_credentials_t cred; int ret; char *user_p; - gnutls_psk_client_credentials_t cred = - (gnutls_psk_client_credentials_t)_gnutls_get_cred( - session, GNUTLS_CRD_PSK); + + cred = (gnutls_psk_client_credentials_t)_gnutls_get_cred( + session, GNUTLS_CRD_PSK); if (unlikely(cred == NULL)) return gnutls_assert_val(-1); - ret = cred->get_function_legacy(session, &user_p, key); + ret = cred->get_function1(session, &user_p, key); + if (ret >= 0) { + username->data = (uint8_t *)user_p; + username->size = strlen(user_p); + } + + return ret; +} + +static int call_client_callback2(gnutls_session_t session, + gnutls_datum_t *username, gnutls_datum_t *key, + gnutls_psk_key_flags *flags) +{ + gnutls_psk_client_credentials_t cred; + int ret; + + cred = (gnutls_psk_client_credentials_t)_gnutls_get_cred( + session, GNUTLS_CRD_PSK); + if (unlikely(cred == NULL)) + return gnutls_assert_val(-1); - if (ret) - goto end; + ret = cred->get_function2(session, username, key); + if (ret < 0) { + return ret; + } - username->data = (uint8_t *)user_p; - username->size = strlen(user_p); + if (flags) { + *flags = 0; + } -end: return ret; } @@ -402,8 +481,9 @@ void gnutls_psk_set_client_credentials_function( gnutls_psk_client_credentials_t cred, gnutls_psk_client_credentials_function *func) { - cred->get_function = call_client_callback_legacy; - cred->get_function_legacy = func; + cred->get_function1 = func; + cred->get_function2 = call_client_callback1; + cred->get_function = call_client_callback2; } /** @@ -434,8 +514,45 @@ void gnutls_psk_set_client_credentials_function2( gnutls_psk_client_credentials_t cred, gnutls_psk_client_credentials_function2 *func) { + cred->get_function1 = NULL; + cred->get_function2 = func; + cred->get_function = call_client_callback2; +} + +/** + * gnutls_psk_set_client_credentials_function3: + * @cred: is a #gnutls_psk_server_credentials_t type. + * @func: is the callback function + * + * This function can be used to set a callback to retrieve the username and + * password for client PSK authentication. + * The callback's function form is: + * int (*callback)(gnutls_session_t, gnutls_datum_t* username, + * gnutls_datum_t* key, gnutls_datum_t* context, gnutls_psk_key_flags *flags); + * + * This callback function has the same semantics as that of + * gnutls_psk_set_client_credentials_function2(), but it returns flags + * associated with the key. The callback may import external PSK + * using the method described in RFC 9258 by using + * gnutls_psk_format_imported_identity(). + * + * The data field of @username, @key, and @context must be allocated + * using gnutls_malloc(). The @username should be an ASCII string or + * UTF-8 string. In case of a UTF-8 string it is recommended to be + * following the PRECIS framework for usernames (rfc8265). + * + * The callback function will be called once per handshake. + * + * The callback function should return 0 on success. + * -1 indicates an error. + **/ +void gnutls_psk_set_client_credentials_function3( + gnutls_psk_client_credentials_t cred, + gnutls_psk_client_credentials_function3 *func) +{ + cred->get_function1 = NULL; + cred->get_function2 = NULL; cred->get_function = func; - cred->get_function_legacy = NULL; } /** diff --git a/tests/Makefile.am b/tests/Makefile.am index 48c694409f..86acb4c277 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -234,7 +234,8 @@ ctests += mini-record-2 simple gnutls_hmac_fast set_pkcs12_cred cert certuniquei set_x509_ocsp_multi_cli kdf-api keylog-func handshake-write \ x509cert-dntypes id-on-xmppAddr tls13-compat-mode ciphersuite-name \ x509-upnconstraint xts-key-check cipher-padding pkcs7-verify-double-free \ - fips-rsa-sizes tls12-rehandshake-ticket pathbuf tls-force-ems + fips-rsa-sizes tls12-rehandshake-ticket pathbuf tls-force-ems \ + psk-importer ctests += tls-channel-binding diff --git a/tests/psk-importer.c b/tests/psk-importer.c new file mode 100644 index 0000000000..84c35338aa --- /dev/null +++ b/tests/psk-importer.c @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2004-2012 Free Software Foundation, Inc. + * Copyright (C) 2013 Adam Sampson + * Copyright (C) 2019 Free Software Foundation, Inc. + * Copyright (C) 2023 Red Hat, Inc. + * + * Author: Simon Josefsson, Ander Juaristi, Daiki Ueno + * + * This file is part of GnuTLS. + * + * GnuTLS 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. + * + * GnuTLS 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 GnuTLS. If not, see . + */ + +/* This tests the external PSK importer interface (RFC 9258). */ +/* Parts copied from pskself.c. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include + +#include "utils.h" +#include "eagain-common.h" + +const char *side = ""; + +static void tls_log_func(int level, const char *str) +{ + fprintf(stderr, "%s|<%d>| %s", side, level, str); +} + +#define MAX_BUF 1024 +#define MSG "Hello TLS" + +static const gnutls_datum_t identity = { (unsigned char *)"\xCA\xFE\xCA\xFE", + 4 }; +static const gnutls_datum_t context = { (unsigned char *)"\xDE\xAD\xBE\xEF", + 4 }; + +static const uint8_t expected_imported_identity[] = { 0x00, 0x04, 0xca, 0xfe, + 0xca, 0xfe, 0x00, 0x04, + 0xde, 0xad, 0xbe, 0xef, + 0x03, 0x04, 0x00, 0x01 }; + +static int server_pskfunc(gnutls_session_t session, + const gnutls_datum_t *username, gnutls_datum_t *key, + gnutls_psk_key_flags *flags) +{ + gnutls_datum_t imported_identity = { NULL, 0 }; + + int ret; + + if (debug) + printf("psk: Got username with length %d\n", username->size); + + key->data = gnutls_malloc(4); + key->data[0] = 0xDE; + key->data[1] = 0xAD; + key->data[2] = 0xBE; + key->data[3] = 0xEF; + key->size = 4; + + ret = gnutls_psk_format_imported_identity(&identity, &context, + GNUTLS_TLS1_3, + GNUTLS_DIG_SHA256, + &imported_identity); + if (ret < 0) { + return -1; + } + + if (imported_identity.size != sizeof(expected_imported_identity) || + memcmp(imported_identity.data, expected_imported_identity, + imported_identity.size)) { + gnutls_free(imported_identity.data); + printf("Unexpected imported identity\n"); + return -1; + } + gnutls_free(imported_identity.data); + + if (flags) { + *flags = GNUTLS_PSK_KEY_EXT; + } + + return 0; +} + +static int client_pskfunc(gnutls_session_t session, gnutls_datum_t *username, + gnutls_datum_t *key, gnutls_psk_key_flags *flags) +{ + int ret; + + ret = gnutls_psk_format_imported_identity(&identity, &context, + GNUTLS_TLS1_3, + GNUTLS_DIG_SHA256, username); + if (ret < 0) { + return -1; + } + + key->data = gnutls_malloc(4); + key->data[0] = 0xDE; + key->data[1] = 0xAD; + key->data[2] = 0xBE; + key->data[3] = 0xEF; + key->size = 4; + + *flags = GNUTLS_PSK_KEY_EXT; + + return 0; +} + +static void run_test(const char *prio) +{ + /* Server stuff. */ + gnutls_psk_server_credentials_t server_pskcred; + gnutls_session_t server; + int sret; + + /* Client stuff. */ + gnutls_psk_client_credentials_t client_pskcred; + gnutls_session_t client; + int cret; + + char buffer[MAX_BUF + 1]; + int transferred = 0; + + gnutls_datum_t psk_username; + + success("%s\n", prio); + + gnutls_global_set_log_function(tls_log_func); + if (debug) + gnutls_global_set_log_level(4711); + + /* Init server */ + gnutls_psk_allocate_server_credentials(&server_pskcred); + gnutls_psk_set_server_credentials_function3(server_pskcred, + server_pskfunc); + gnutls_init(&server, GNUTLS_SERVER); + gnutls_priority_set_direct(server, prio, NULL); + gnutls_credentials_set(server, GNUTLS_CRD_PSK, server_pskcred); + gnutls_transport_set_push_function(server, server_push); + gnutls_transport_set_pull_function(server, server_pull); + gnutls_transport_set_ptr(server, server); + + /* Init client */ + gnutls_psk_allocate_client_credentials(&client_pskcred); + gnutls_psk_set_client_credentials_function3(client_pskcred, + client_pskfunc); + gnutls_init(&client, GNUTLS_CLIENT); + gnutls_priority_set_direct(client, prio, NULL); + gnutls_credentials_set(client, GNUTLS_CRD_PSK, client_pskcred); + gnutls_transport_set_push_function(client, client_push); + gnutls_transport_set_pull_function(client, client_pull); + gnutls_transport_set_ptr(client, client); + + HANDSHAKE(client, server); + + if (debug) { + success("server: Handshake was completed\n"); + + if (gnutls_psk_server_get_username(server)) + fail("server: gnutls_psk_server_get_username() should have returned NULL\n"); + if (gnutls_psk_server_get_username2(server, &psk_username) < 0) + fail("server: Could not get PSK username\n"); + + if (psk_username.size != sizeof(expected_imported_identity) || + memcmp(psk_username.data, expected_imported_identity, + sizeof(expected_imported_identity))) + fail("server: Unexpected PSK username\n"); + + success("server: PSK username length: %d\n", psk_username.size); + } + + TRANSFER(client, server, MSG, strlen(MSG), buffer, MAX_BUF); + TRANSFER(server, client, MSG, strlen(MSG), buffer, MAX_BUF); + + gnutls_bye(server, GNUTLS_SHUT_WR); + gnutls_bye(client, GNUTLS_SHUT_WR); + + gnutls_deinit(server); + gnutls_deinit(client); + + gnutls_psk_free_server_credentials(server_pskcred); + gnutls_psk_free_client_credentials(client_pskcred); + + reset_buffers(); +} + +void doit(void) +{ + global_init(); + run_test("NORMAL:-VERS-ALL:+VERS-TLS1.3:+PSK"); + run_test( + "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-FFDHE2048:+DHE-PSK"); + run_test( + "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP256R1:+ECDHE-PSK"); + + gnutls_global_deinit(); +} -- cgit v1.2.1