Skip to content

Add SNI-based certificate selection to TLS 1.3 server handshake#28

Closed
pomelio wants to merge 1 commit into
ianic:mainfrom
pomelio:main
Closed

Add SNI-based certificate selection to TLS 1.3 server handshake#28
pomelio wants to merge 1 commit into
ianic:mainfrom
pomelio:main

Conversation

@pomelio
Copy link
Copy Markdown

@pomelio pomelio commented Apr 1, 2026

Add SNI-based certificate selection to TLS 1.3 server handshake

PR Description

This change adds Server Name Indication (SNI) support to the TLS 1.3 server handshake.

The server now parses the server_name extension from ClientHello, stores the requested hostname in handshake state, and can select a certificate dynamically through a new Options.sni_auth callback. If the callback does not return a certificate, the existing auth field is still used as the default fallback.

  • Added Options.sni_auth to support dynamic certificate selection based on SNI.
  • Added Handshake.server_name state to retain the hostname parsed from ClientHello.
  • Implemented parsing for the server_name TLS extension.
  • Changed server certificate selection to use the resolved SNI-specific certificate instead of always using opt.auth.
  • Deferred signature scheme validation until after certificate selection, so validation is performed against the actual certificate chosen for the handshake.
  • Added tests covering:
    • existing ClientHello parsing behavior
    • SNI parsing and SNI-driven certificate selection

Before this change, the server always used a single fixed certificate from opt.auth, regardless of the hostname requested by the client. That made virtual hosting impossible.

This patch enables:

  • serving multiple hostnames from the same listener
  • choosing the correct certificate during handshake
  • preserving backward compatibility for callers that only use auth

New server option:

sni_auth: ?SniAuth = null

Where SniAuth is:

pub const SniAuth = struct {
ctx: *anyopaque,
selectFn: *const fn (ctx: *anyopaque, server_name: ?[]const u8)
?*CertKeyPair,
};

Behavior:

  • selectFn is called after parsing SNI from ClientHello
  • if it returns a certificate, that certificate is used
  • if it returns null, the handshake falls back to auth

This change is backward compatible:

  • existing users of auth continue to work unchanged
  • sni_auth is optional

I could not use full zig test output as the validation signal because the current repository does not compile cleanly against the local Zig standard library version for unrelated reasons. The modified file passes formatting checks with:

zig fmt --check src/handshake_server.zig

Add SNI-based certificate selection to TLS 1.3 server handshake

**PR Description**

This change adds Server Name Indication (SNI) support to the TLS 1.3
server handshake.

The server now parses the `server_name` extension from `ClientHello`,
stores the requested hostname in handshake state, and can select a
certificate dynamically through a new `Options.sni_auth` callback. If
the callback does not return a certificate, the existing `auth` field is
still used as the default fallback.

- Added `Options.sni_auth` to support dynamic certificate selection
  based on SNI.
- Added `Handshake.server_name` state to retain the hostname parsed from
  `ClientHello`.
- Implemented parsing for the `server_name` TLS extension.
- Changed server certificate selection to use the resolved SNI-specific
  certificate instead of always using `opt.auth`.
- Deferred signature scheme validation until after certificate
  selection, so validation is performed against the actual certificate
  chosen for the handshake.
- Added tests covering:
  - existing `ClientHello` parsing behavior
  - SNI parsing and SNI-driven certificate selection

Before this change, the server always used a single fixed certificate
from `opt.auth`, regardless of the hostname requested by the client.
That made virtual hosting impossible.

This patch enables:
- serving multiple hostnames from the same listener
- choosing the correct certificate during handshake
- preserving backward compatibility for callers that only use `auth`

New server option:

```zig
sni_auth: ?SniAuth = null
```

Where `SniAuth` is:

```zig
pub const SniAuth = struct {
ctx: *anyopaque,
selectFn: *const fn (ctx: *anyopaque, server_name: ?[]const u8)
?*CertKeyPair,
};
```

Behavior:
- `selectFn` is called after parsing SNI from `ClientHello`
- if it returns a certificate, that certificate is used
- if it returns `null`, the handshake falls back to `auth`

This change is backward compatible:
- existing users of `auth` continue to work unchanged
- `sni_auth` is optional

I could not use full `zig test` output as the validation signal because
the current repository does not compile cleanly against the local Zig
standard library version for unrelated reasons. The modified file passes
formatting checks with:

```sh
zig fmt --check src/handshake_server.zig
```
@pomelio pomelio changed the title **PR Title** Add SNI-based certificate selection to TLS 1.3 server handshake Apr 1, 2026
@ianic
Copy link
Copy Markdown
Owner

ianic commented Apr 1, 2026

I could not use full zig test output as the validation signal because the current repository does not compile cleanly against the local Zig standard library version

Use Zig version from build.zig.zon or the latest one 0.16.0-dev.3061+9b1eaad13.
Run example/run_sanity_check_tests.sh locally to pass the tests from ci.

@ianic ianic closed this Apr 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants