// SPDX-FileCopyrightText: © 2022—2026 Vladimir Zorin // SPDX-License-Identifier: LicenseRef-OWL-1.0-or-later // Licensed under OWL v1.0+. See LICENSE. /* * LITLS -- TLS 1.3 Server State Machine * * Drives the server-side TLS 1.3 handshake: receives ClientHello, * sends ServerHello + encrypted flight, validates client Finished. */ #include "tls13.h" #include #include #include /* ── SNI matching ─────────────────────────────────────────────────── */ static litls_server_identity *sni_lookup(litls_tls13_ctx *ctx) { if (!ctx->sni[0] || !ctx->sni_hosts || ctx->sni_host_count == 0) return NULL; size_t name_len = strlen(ctx->sni); /* Pass 0: exact case-insensitive match */ for (int i = 0; i < ctx->sni_host_count; i++) { const char *pattern = ctx->sni_hosts[i].hostname; if (strlen(pattern) == name_len && strncasecmp(pattern, ctx->sni, name_len) == 0) return &ctx->sni_hosts[i]; } /* Pass 1: wildcard *.domain match */ for (int i = 0; i < ctx->sni_host_count; i++) { const char *pattern = ctx->sni_hosts[i].hostname; if (pattern[0] == '*' && pattern[1] == '.') { const char *suffix = pattern + 1; /* ".example.org" */ size_t suffix_len = strlen(suffix); const char *dot = memchr(ctx->sni, '.', name_len); if (dot) { size_t remaining = name_len - (size_t)(dot - ctx->sni); if (remaining == suffix_len && strncasecmp(suffix, dot, remaining) == 0) return &ctx->sni_hosts[i]; } } } return NULL; } /* ── Initialization ───────────────────────────────────────────────── */ int litls_tls13_server_init(litls_tls13_ctx *ctx, int fd, litls_server_identity *default_identity, litls_server_identity *sni_hosts, int sni_host_count) { memset(ctx, 0, sizeof(*ctx)); ctx->fd = fd; ctx->is_server = 1; ctx->identity = default_identity; ctx->sni_hosts = sni_hosts; ctx->sni_host_count = sni_host_count; ctx->hs_state = TLS13_HS_SRV_RECV_CLIENT_HELLO; /* Generate ephemeral X25519 keypair */ if (litls_random_bytes(ctx->x25519_priv, 32) != 0) { snprintf(ctx->error, sizeof(ctx->error), "RNG failed"); return -1; } litls_x25519_base(ctx->x25519_pub, ctx->x25519_priv); litls_transcript_init(&ctx->transcript); return 0; } /* ── Verify client Finished ───────────────────────────────────────── */ static int verify_client_finished(litls_tls13_ctx *ctx, const uint8_t *verify_data, size_t verify_len) { int hash_len = ctx->suite.hash_len; uint8_t transcript_hash[48]; litls_transcript_hash(&ctx->transcript, transcript_hash); uint8_t finished_key[48]; uint8_t expected[48]; int ret = -1; if (hash_len == 32) { litls_tls13_expand_label_256(ctx->client_hs_secret, "finished", NULL, 0, finished_key, 32); litls_hmac_sha256(finished_key, 32, transcript_hash, 32, expected); if (litls_secure_memcmp(verify_data, expected, 32) != 0) { snprintf(ctx->error, sizeof(ctx->error), "client Finished verify failed"); } else { ret = 0; } } else { litls_tls13_expand_label_384(ctx->client_hs_secret, "finished", NULL, 0, finished_key, 48); litls_hmac_sha384(finished_key, 48, transcript_hash, 48, expected); if (litls_secure_memcmp(verify_data, expected, 48) != 0) { snprintf(ctx->error, sizeof(ctx->error), "client Finished verify failed"); } else { ret = 0; } } litls_secure_zero(finished_key, sizeof(finished_key)); litls_secure_zero(expected, sizeof(expected)); litls_secure_zero(transcript_hash, sizeof(transcript_hash)); return ret; } /* ── State machine ────────────────────────────────────────────────── */ litls_io_want litls_tls13_server_handshake_step(litls_tls13_ctx *ctx) { switch (ctx->hs_state) { case TLS13_HS_SRV_RECV_CLIENT_HELLO: { uint8_t ct; uint8_t *data; size_t data_len; int rc = litls_record_read(ctx, &ct, &data, &data_len); if (rc == 1) return LITLS_WANT_READ; if (rc < 0) return (rc == -2) ? LITLS_CLOSED : LITLS_ERROR; if (ct != TLS13_CT_HANDSHAKE || data_len < 4) { snprintf(ctx->error, sizeof(ctx->error), "expected handshake, got type %d", ct); return LITLS_ERROR; } if (data[0] != TLS13_HT_CLIENT_HELLO) { snprintf(ctx->error, sizeof(ctx->error), "expected ClientHello, got type %d", data[0]); return LITLS_ERROR; } uint32_t msg_len = ((uint32_t)data[1] << 16) | ((uint32_t)data[2] << 8) | data[3]; if (4 + msg_len > data_len) { snprintf(ctx->error, sizeof(ctx->error), "ClientHello length mismatch"); return LITLS_ERROR; } /* Parse ClientHello body */ if (litls_parse_client_hello(ctx, data + 4, msg_len) != 0) return LITLS_ERROR; /* SNI lookup */ litls_server_identity *match = sni_lookup(ctx); if (match) ctx->identity = match; if (!ctx->identity) { snprintf(ctx->error, sizeof(ctx->error), "no server identity configured"); return LITLS_ERROR; } /* Update transcript with ClientHello */ litls_transcript_update(&ctx->transcript, data, data_len); ctx->hs_state = TLS13_HS_SRV_SEND_SERVER_HELLO; return LITLS_WANT_WRITE; } case TLS13_HS_SRV_SEND_SERVER_HELLO: { /* Build and send ServerHello (plaintext) */ uint8_t sh_buf[512]; size_t sh_len; if (litls_build_server_hello(ctx, sh_buf, sizeof(sh_buf), &sh_len) != 0) return LITLS_ERROR; litls_transcript_update(&ctx->transcript, sh_buf, sh_len); if (litls_record_write(ctx, TLS13_CT_HANDSHAKE, sh_buf, sh_len) != 0) return LITLS_ERROR; /* Derive handshake keys */ if (litls_tls13_derive_handshake_keys(ctx) != 0) { snprintf(ctx->error, sizeof(ctx->error), "handshake key derivation failed"); return LITLS_ERROR; } /* Send CCS for middlebox compatibility (plaintext, before switching to encrypted) */ uint8_t ccs[] = {1}; if (litls_record_write(ctx, TLS13_CT_CHANGE_CIPHER_SPEC, ccs, 1) != 0) return LITLS_ERROR; /* Now enable encryption for writing */ ctx->encrypting = 1; /* Fall through to send encrypted extensions */ ctx->hs_state = TLS13_HS_SRV_SEND_ENCRYPTED_EXTENSIONS; } /* fallthrough */ case TLS13_HS_SRV_SEND_ENCRYPTED_EXTENSIONS: { uint8_t ee_buf[64]; size_t ee_len; if (litls_build_encrypted_extensions(ee_buf, sizeof(ee_buf), &ee_len) != 0) return LITLS_ERROR; litls_transcript_update(&ctx->transcript, ee_buf, ee_len); if (litls_record_write(ctx, TLS13_CT_HANDSHAKE, ee_buf, ee_len) != 0) return LITLS_ERROR; ctx->hs_state = TLS13_HS_SRV_SEND_CERTIFICATE; } /* fallthrough */ case TLS13_HS_SRV_SEND_CERTIFICATE: { /* RFC 8446 §4.2.3: when the server authenticates with a certificate, * the client MUST send signature_algorithms. If the client omitted it, * the server must abort (would be `missing_extension` alert in a fully * alerted implementation). */ if (!ctx->peer_sent_sig_algos) { snprintf(ctx->error, sizeof(ctx->error), "client did not send signature_algorithms; cannot authenticate with certificate"); return LITLS_ERROR; } uint8_t cert_buf[LITLS_MAX_CERT_CHAIN_DER + 256]; size_t cert_len; if (litls_build_certificate(ctx->identity, NULL, 0, cert_buf, sizeof(cert_buf), &cert_len) != 0) { snprintf(ctx->error, sizeof(ctx->error), "failed to build Certificate"); return LITLS_ERROR; } litls_transcript_update(&ctx->transcript, cert_buf, cert_len); if (litls_record_write(ctx, TLS13_CT_HANDSHAKE, cert_buf, cert_len) != 0) return LITLS_ERROR; ctx->hs_state = TLS13_HS_SRV_SEND_CERTIFICATE_VERIFY; } /* fallthrough */ case TLS13_HS_SRV_SEND_CERTIFICATE_VERIFY: { uint8_t cv_buf[256]; size_t cv_len; /* Build CertificateVerify -- uses transcript hash BEFORE CertificateVerify */ if (litls_build_certificate_verify(ctx, ctx->identity, 1, cv_buf, sizeof(cv_buf), &cv_len) != 0) return LITLS_ERROR; if (litls_record_write(ctx, TLS13_CT_HANDSHAKE, cv_buf, cv_len) != 0) return LITLS_ERROR; /* Update transcript AFTER writing (sig covers hash before CV) */ litls_transcript_update(&ctx->transcript, cv_buf, cv_len); ctx->hs_state = TLS13_HS_SRV_SEND_FINISHED; } /* fallthrough */ case TLS13_HS_SRV_SEND_FINISHED: { uint8_t fin_buf[56]; size_t fin_len; if (litls_build_finished(ctx, ctx->server_hs_secret, fin_buf, &fin_len) != 0) { snprintf(ctx->error, sizeof(ctx->error), "failed to build server Finished"); return LITLS_ERROR; } litls_transcript_update(&ctx->transcript, fin_buf, fin_len); if (litls_record_write(ctx, TLS13_CT_HANDSHAKE, fin_buf, fin_len) != 0) return LITLS_ERROR; /* Derive app keys (transcript through server Finished) */ if (litls_tls13_derive_app_keys(ctx) != 0) { snprintf(ctx->error, sizeof(ctx->error), "app key derivation failed"); return LITLS_ERROR; } /* Enable decryption for reading client's Finished */ ctx->decrypting = 1; ctx->hs_state = TLS13_HS_SRV_RECV_FINISHED; ctx->hs_buf_len = 0; return LITLS_WANT_WRITE; } case TLS13_HS_SRV_RECV_FINISHED: { uint8_t msg_type; const uint8_t *msg; size_t msg_len; int rc = litls_get_hs_message(ctx, &msg_type, &msg, &msg_len); if (rc == 0) return LITLS_WANT_READ; if (rc < 0) return (rc == -2) ? LITLS_CLOSED : LITLS_ERROR; if (msg_type != TLS13_HT_FINISHED) { snprintf(ctx->error, sizeof(ctx->error), "expected client Finished, got type %d", msg_type); return LITLS_ERROR; } const uint8_t *verify_data; size_t verify_len; if (litls_parse_finished(ctx, msg, msg_len, &verify_data, &verify_len) != 0) return LITLS_ERROR; /* Verify client Finished (transcript hash BEFORE this message) */ if (verify_client_finished(ctx, verify_data, verify_len) != 0) return LITLS_ERROR; /* Update transcript with client Finished */ litls_transcript_update(&ctx->transcript, ctx->hs_buf, 4 + msg_len); litls_consume_hs_message(ctx, msg_len); ctx->hs_state = TLS13_HS_DONE; return LITLS_DONE; } case TLS13_HS_DONE: return LITLS_DONE; default: break; } snprintf(ctx->error, sizeof(ctx->error), "invalid server handshake state"); return LITLS_ERROR; }