| /* |
| * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. |
| * |
| * Licensed under the Apache License 2.0 (the "License"). You may not use |
| * this file except in compliance with the License. You can obtain a copy |
| * in the file LICENSE in the source distribution or at |
| * https://www.openssl.org/source/license.html |
| */ |
| |
| #include <string.h> |
| #include <openssl/ssl.h> |
| #include <openssl/rand.h> |
| #include <openssl/ssl3.h> |
| |
| #include "helpers/ssltestlib.h" |
| #include "testutil.h" |
| |
| static const char psk_secret[] = "shared-secret"; |
| static const char psk_identity[] = "identity"; |
| |
| static const unsigned char sid_req[] = { |
| 0xde, 0xad, 0xbe, 0xef, 0x01, 0x02, 0x03, 0x04, |
| 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c |
| }; |
| |
| static const struct ciphersuites { |
| char *name; |
| } css[] = { |
| { "PSK-AES128-CBC-SHA256" }, |
| { "PSK-AES256-CBC-SHA384" }, |
| { "PSK-AES128-GCM-SHA256" }, |
| { "PSK-AES256-GCM-SHA384" } |
| }; |
| |
| #define HELLO_RANDOM_OFF 6 |
| #define HELLO_RANDOM_LEN 32 |
| #define HELLO_SID_LEN_OFF (HELLO_RANDOM_OFF + HELLO_RANDOM_LEN) |
| #define HELLO_SID_OFF (HELLO_SID_LEN_OFF + 1) |
| #define HELLO_MIN_LEN (HELLO_SID_LEN_OFF + 1) |
| |
| static void hello_session_id(const unsigned char *p, size_t len) |
| { |
| char *str; |
| size_t sid_len; |
| |
| if (len < HELLO_MIN_LEN) |
| return; |
| |
| sid_len = p[HELLO_SID_LEN_OFF]; |
| if (sid_len == 0 || len < HELLO_SID_OFF + sid_len) |
| return; |
| |
| str = OPENSSL_buf2hexstr(p + HELLO_SID_OFF, (long)sid_len); |
| TEST_info("session_id(%u): <%s>", (unsigned int)sid_len, str); |
| OPENSSL_free(str); |
| } |
| |
| static void msg_cb(int write_p, int version, int content_type, |
| const void *buf, size_t len, SSL *ssl, void *arg) |
| { |
| const unsigned char *p = buf; |
| |
| if (content_type != SSL3_RT_HANDSHAKE || len < 1) |
| return; |
| |
| switch (p[0]) { |
| case SSL3_MT_CLIENT_HELLO: |
| TEST_info("%p client_hello", (void *)ssl); |
| hello_session_id(p, len); |
| break; |
| |
| case SSL3_MT_SERVER_HELLO: |
| TEST_info("%p server_hello", (void *)ssl); |
| hello_session_id(p, len); |
| break; |
| } |
| } |
| |
| static void handshake_finished(const SSL *ssl) |
| { |
| const char *endpoint = SSL_is_server(ssl) ? "server" : "client"; |
| unsigned int has_ticket = SSL_SESSION_has_ticket(SSL_get_session(ssl)); |
| |
| if (SSL_session_reused(ssl)) |
| TEST_info("%s: Abbreviated handshake finished", endpoint); |
| else |
| TEST_info("%s: Full handshake finished", endpoint); |
| |
| TEST_info("%s: has_ticket: %u", endpoint, has_ticket); |
| } |
| |
| static void info_cb(const SSL *ssl, int type, int val) |
| { |
| const char *endpoint = SSL_is_server(ssl) ? "server" : "client"; |
| |
| if (type & SSL_CB_ALERT) { |
| const char *dir = (type & SSL_CB_READ) ? "read" : "write"; |
| |
| TEST_info("%s: alert %s: %s : %s", endpoint, dir, |
| SSL_alert_type_string_long(val), |
| SSL_alert_desc_string_long(val)); |
| } |
| if (type & SSL_CB_HANDSHAKE_DONE) |
| handshake_finished(ssl); |
| } |
| |
| static unsigned int server_psk_cb(SSL *ssl, const char *identity, |
| unsigned char *psk, unsigned int max) |
| { |
| if (max < (sizeof(psk_secret) - 1)) |
| return 0; |
| memcpy(psk, psk_secret, (sizeof(psk_secret) - 1)); |
| return (unsigned int)(sizeof(psk_secret) - 1); |
| } |
| |
| static unsigned int client_psk_cb(SSL *ssl, const char *hint, |
| char *identity, unsigned int max_id, |
| unsigned char *psk, unsigned int max) |
| { |
| if (max < (sizeof(psk_secret) - 1) || max_id < sizeof(psk_identity)) |
| return 0; |
| strncpy(identity, psk_identity, max_id); |
| memcpy(psk, psk_secret, (sizeof(psk_secret) - 1)); |
| return (unsigned int)(sizeof(psk_secret) - 1); |
| } |
| |
| static SSL_SESSION *sess_cache; |
| static SSL_SESSION *get_sess_cb(SSL *ssl, const unsigned char *id, int len, int *copy) |
| { |
| *copy = 1; |
| |
| if (sess_cache != NULL) { |
| char *str; |
| const unsigned char *sid; |
| unsigned int sid_len; |
| |
| sid = SSL_SESSION_get_id(sess_cache, &sid_len); |
| str = OPENSSL_buf2hexstr(sid, sid_len); |
| TEST_info("(cached) session_id: <%s>", str); |
| OPENSSL_free(str); |
| } |
| return sess_cache; |
| } |
| |
| static int ctx_set_cache(SSL_CTX *s_ctx, SSL_CTX *c_ctx) |
| { |
| SSL_CTX_set_psk_server_callback(s_ctx, server_psk_cb); |
| SSL_CTX_set_psk_client_callback(c_ctx, client_psk_cb); |
| SSL_CTX_set_session_cache_mode(s_ctx, SSL_SESS_CACHE_SERVER); |
| SSL_CTX_set_session_cache_mode(c_ctx, SSL_SESS_CACHE_CLIENT); |
| SSL_CTX_set_options(s_ctx, SSL_OP_NO_TICKET); |
| SSL_CTX_set_verify(c_ctx, SSL_VERIFY_NONE, NULL); |
| return 1; |
| } |
| |
| static int ctx_set_ticket(SSL_CTX *s_ctx, SSL_CTX *c_ctx) |
| { |
| SSL_CTX_set_psk_server_callback(s_ctx, server_psk_cb); |
| SSL_CTX_set_psk_client_callback(c_ctx, client_psk_cb); |
| SSL_CTX_set_session_cache_mode(s_ctx, SSL_SESS_CACHE_SERVER); |
| SSL_CTX_set_session_cache_mode(c_ctx, SSL_SESS_CACHE_CLIENT); |
| SSL_CTX_set_verify(c_ctx, SSL_VERIFY_NONE, NULL); |
| return 1; |
| } |
| |
| static int set_shutdown(SSL *c, SSL *s) |
| { |
| SSL_set_shutdown(c, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); |
| SSL_set_shutdown(s, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); |
| return 1; |
| } |
| |
| static int set_server_cache(SSL_CTX *s_ctx) |
| { |
| unsigned int v = SSL_SESS_CACHE_SERVER | SSL_SESS_CACHE_NO_INTERNAL_STORE; |
| SSL_CTX_sess_set_get_cb(s_ctx, get_sess_cb); |
| SSL_CTX_set_session_cache_mode(s_ctx, v); |
| return 1; |
| } |
| |
| static int set_callbacks(SSL *c, SSL *s) |
| { |
| SSL_set_msg_callback(c, msg_cb); |
| SSL_set_info_callback(c, info_cb); |
| SSL_set_msg_callback(s, msg_cb); |
| SSL_set_info_callback(s, info_cb); |
| return 1; |
| } |
| |
| static int sessid_matches(SSL *c, SSL *s) |
| { |
| const unsigned char *c_sid, *s_sid; |
| unsigned int c_len, s_len; |
| int test; |
| |
| test = TEST_ptr(c_sid = SSL_SESSION_get_id(SSL_get0_session(c), &c_len)) |
| && TEST_ptr(s_sid = SSL_SESSION_get_id(SSL_get0_session(s), &s_len)) |
| && TEST_uint_eq(c_len, s_len) && TEST_mem_eq(c_sid, c_len, s_sid, s_len); |
| |
| return test; |
| } |
| |
| static int test_dtls12_psk_resume_sessid_mismatch(int idx) |
| { |
| const struct ciphersuites *cs = &css[idx]; |
| SSL_CTX *s_ctx = NULL, *c_ctx = NULL; |
| SSL *s_ssl = NULL, *c_ssl = NULL, *s = NULL, *c = NULL; |
| SSL_SESSION *sess = NULL, *r_sess = NULL; |
| const unsigned char *sid; |
| unsigned int sid_len; |
| int test; |
| |
| sess_cache = NULL; |
| |
| test = TEST_true(create_ssl_ctx_pair(NULL, DTLS_server_method(), DTLS_client_method(), |
| DTLS1_2_VERSION, DTLS1_2_VERSION, &s_ctx, &c_ctx, NULL, NULL)) |
| && TEST_true(SSL_CTX_set_cipher_list(s_ctx, cs->name)) |
| && TEST_true(SSL_CTX_set_cipher_list(c_ctx, cs->name)) |
| && TEST_true(ctx_set_cache(s_ctx, c_ctx)) |
| && TEST_true(create_ssl_objects(s_ctx, c_ctx, &s, &c, NULL, NULL)) |
| && TEST_true(set_callbacks(c, s)) |
| && TEST_true(create_ssl_connection(s, c, SSL_ERROR_NONE)) |
| && TEST_ptr(sess = SSL_get1_session(c)) |
| && TEST_true(set_shutdown(c, s)) |
| && TEST_ptr(sid = SSL_SESSION_get_id(sess, &sid_len)) |
| && TEST_uint_eq(sid_len, 32) |
| && TEST_ptr(r_sess = SSL_SESSION_dup(sess)) |
| && TEST_true(SSL_SESSION_set1_id(r_sess, sid_req, sizeof(sid_req))) |
| && TEST_ptr(sess_cache = sess) |
| && TEST_true(set_server_cache(s_ctx)) |
| && TEST_true(create_ssl_objects(s_ctx, c_ctx, &s_ssl, &c_ssl, NULL, NULL)) |
| && TEST_true(set_callbacks(c_ssl, s_ssl)) |
| && TEST_true(SSL_set_session(c_ssl, r_sess)) |
| && TEST_true(create_ssl_connection(s_ssl, c_ssl, SSL_ERROR_NONE)) |
| && TEST_false(SSL_session_reused(s_ssl)); |
| |
| sess_cache = NULL; |
| SSL_free(s_ssl); |
| SSL_free(c_ssl); |
| SSL_SESSION_free(r_sess); |
| SSL_SESSION_free(sess); |
| SSL_CTX_free(s_ctx); |
| SSL_CTX_free(c_ctx); |
| SSL_free(s); |
| SSL_free(c); |
| return test; |
| } |
| |
| /* |
| * RFC 5077 3.4 requires the server to echo the session ID from ClientHello |
| * in the ServerHello when accepting a session ticket. Some clients rely on |
| * this echo to confirm that resumption succeeded. The ticket decryption path |
| * in tls_decrypt_ticket() guarantees the restored SSL_SESSION carries the |
| * correct session ID, so tls_construct_server_hello() will echo it correctly. |
| * If the session ID is empty, its length is set to zero as required by the |
| * RFC. |
| */ |
| static int test_dtls12_psk_resume_ticket_mismatch(int idx) |
| { |
| const struct ciphersuites *cs = &css[idx]; |
| SSL_CTX *s_ctx = NULL, *c_ctx = NULL; |
| SSL *s_ssl = NULL, *c_ssl = NULL, *s = NULL, *c = NULL; |
| SSL_SESSION *c_sess = NULL, *r_sess = NULL; |
| int test; |
| |
| test = TEST_true(create_ssl_ctx_pair(NULL, DTLS_server_method(), DTLS_client_method(), |
| DTLS1_2_VERSION, DTLS1_2_VERSION, &s_ctx, &c_ctx, NULL, NULL)) |
| && TEST_true(SSL_CTX_set_cipher_list(s_ctx, cs->name)) |
| && TEST_true(SSL_CTX_set_cipher_list(c_ctx, cs->name)) |
| && TEST_true(ctx_set_ticket(s_ctx, c_ctx)) |
| && TEST_true(create_ssl_objects(s_ctx, c_ctx, &s, &c, NULL, NULL)) |
| && TEST_true(set_callbacks(c, s)) |
| && TEST_true(create_ssl_connection(s, c, SSL_ERROR_NONE)) |
| && TEST_ptr(c_sess = SSL_get1_session(c)) |
| && TEST_true(SSL_SESSION_has_ticket(c_sess)) |
| && TEST_int_eq(set_shutdown(c, s), 1) |
| && TEST_ptr(r_sess = SSL_SESSION_dup(c_sess)) |
| && TEST_true(SSL_SESSION_set1_id(r_sess, sid_req, sizeof(sid_req))) |
| && TEST_true(create_ssl_objects(s_ctx, c_ctx, &s_ssl, &c_ssl, NULL, NULL)) |
| && TEST_true(set_callbacks(c_ssl, s_ssl)) |
| && TEST_true(SSL_set_session(c_ssl, r_sess)) |
| && TEST_true(create_ssl_connection(s_ssl, c_ssl, SSL_ERROR_NONE)) |
| && TEST_true(SSL_session_reused(s_ssl)) |
| && TEST_true(sessid_matches(c_ssl, s_ssl)); |
| |
| SSL_free(s_ssl); |
| SSL_free(c_ssl); |
| SSL_SESSION_free(r_sess); |
| SSL_SESSION_free(c_sess); |
| SSL_CTX_free(s_ctx); |
| SSL_CTX_free(c_ctx); |
| SSL_free(s); |
| SSL_free(c); |
| return test; |
| } |
| |
| OPT_TEST_DECLARE_USAGE("\n") |
| |
| int setup_tests(void) |
| { |
| if (!test_skip_common_options()) { |
| TEST_error("Error parsing test options\n"); |
| return 0; |
| } |
| |
| ADD_ALL_TESTS(test_dtls12_psk_resume_sessid_mismatch, OSSL_NELEM(css)); |
| ADD_ALL_TESTS(test_dtls12_psk_resume_ticket_mismatch, OSSL_NELEM(css)); |
| return 1; |
| } |