# HG changeset patch # User Sergey Kandaurov # Date 1687433719 -14400 # Node ID 24fea64f233f59012b0158b13b7993f8415c669e # Parent 6ab08c255dd3b000346b77f33b754c6b9ed82c39 Tests: TLS early data tests with HTTP/3. diff --git a/h3_ssl_early_data.t b/h3_ssl_early_data.t new file mode 100644 --- /dev/null +++ b/h3_ssl_early_data.t @@ -0,0 +1,108 @@ +#!/usr/bin/perl + +# (C) Sergey Kandaurov +# (C) Nginx, Inc. + +# Tests for TLS early data with HTTP/3. + +############################################################################### + +use warnings; +use strict; + +use Test::More; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; +use Test::Nginx::HTTP3; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +my $t = Test::Nginx->new()->has(qw/http http_v3 cryptx/) + ->has_daemon('openssl')->plan(5) + ->write_file_expand('nginx.conf', <<'EOF'); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +http { + %%TEST_GLOBALS_HTTP%% + + ssl_certificate_key localhost.key; + ssl_certificate localhost.crt; + ssl_early_data on; + + add_header X-Session $ssl_session_reused always; + add_header X-Early $ssl_early_data always; + + server { + listen 127.0.0.1:%%PORT_8980_UDP%% quic; + server_name localhost; + } +} + +EOF + +$t->write_file('openssl.conf', <testdir(); + +foreach my $name ('localhost') { + system('openssl req -x509 -new ' + . "-config $d/openssl.conf -subj /CN=$name/ " + . "-out $d/$name.crt -keyout $d/$name.key " + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't create certificate for $name: $!\n"; +} + +$t->run(); + +############################################################################### + +my $s = Test::Nginx::HTTP3->new(8980); +my $frames = $s->read(all => [{ sid => $s->new_stream(), fin => 1 }]); + +my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; +is($frame->{headers}->{'x-session'}, '.', 'new session'); + +local $TODO = 'no TLSv1.3 sessions in LibreSSL' if $t->has_module('LibreSSL'); + +my $psk_list = $s->{psk_list}; +my $ed = $s->build_new_stream(); + +$s = Test::Nginx::HTTP3->new(8980, psk_list => $psk_list, early_data => {}); + +TODO: { +local $TODO = 'no 0-RTT in OpenSSL compat layer' + unless $t->has_module('OpenSSL [.0-9]+\+quic') + or $t->has_module('BoringSSL') + or $t->has_module('LibreSSL'); + +$frames = $s->read(all => [{ sid => 0, fin => 1 }]); +($frame) = grep { $_->{type} eq "HEADERS" } @$frames; +is($frame->{headers}->{'x-session'}, 'r', 'reused session 0rtt'); +is($frame->{headers}->{'x-early'}, '1', 'reused session is early'); + +} + +$frames = $s->read(all => [{ sid => $s->new_stream(), fin => 1 }]); +($frame) = grep { $_->{type} eq "HEADERS" } @$frames; +is($frame->{headers}->{'x-session'}, 'r', 'reused session 1rtt'); +is($frame->{headers}->{'x-early'}, undef, 'reused session not early'); + +############################################################################### diff --git a/lib/Test/Nginx/HTTP3.pm b/lib/Test/Nginx/HTTP3.pm --- a/lib/Test/Nginx/HTTP3.pm +++ b/lib/Test/Nginx/HTTP3.pm @@ -41,6 +41,7 @@ sub new { $self->{repeat} = 0; $self->{token} = $extra{token} || ''; $self->{psk_list} = $extra{psk_list} || []; + $self->{early_data} = $extra{early_data}; $self->{sni} = exists $extra{sni} ? $extra{sni} : 'localhost'; $self->{cipher} = 0x1301; @@ -62,7 +63,7 @@ sub new { } sub init { - my ($self, $early_data) = @_; + my ($self) = @_; $self->{keys} = []; $self->{pn} = [[-1, -1, -1, -1], [-1, -1, -1, -1]]; $self->{crypto_in} = [[],[],[],[]]; @@ -82,7 +83,6 @@ sub init { $self->{salt} = "\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17" . "\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb\x7f\x0a"; $self->{ncid} = []; - $self->{early_data} = $early_data; } sub retry { @@ -129,26 +129,24 @@ sub init_key_schedule { } sub initial { - my ($self, $ed) = @_; + my ($self) = @_; $self->{tlsm}{ch} = $self->build_tls_client_hello(); my $ch = $self->{tlsm}{ch}; my $crypto = build_crypto($ch); my $padding = 1200 - length($crypto); - $padding = 0 if $padding < 0 || $self->{psk}->{ed}; + $padding = 0 if $padding < 0; + $padding = 0 if $self->{psk}{ed} && $self->{early_data}; my $payload = $crypto . pack("x$padding"); my $initial = $self->encrypt_aead($payload, 0); - if ($ed && $self->{psk}->{ed}) { + if ($self->{early_data} && $self->{psk}->{ed}) { my ($hash, $hlen) = $self->{psk}{cipher} == 0x1302 ? ('SHA384', 48) : ('SHA256', 32); $self->set_traffic_keys('tls13 c e traffic', $hash, $hlen, 1, 'w', $self->{es_prk}, Crypt::Digest::digest_data($hash, $self->{tlsm}{ch})); -# my $ed = "\x0a\x02\x08\x00\x04\x02\x06\x1f\x0d\x00\x0a" -# . $self->build_stream("\x01\x06\x00\x00\xc0"); - $payload = $ed; -# $payload = $self->build_stream("GET /\n"); + $payload = $self->build_new_stream($self->{early_data}); $padding = 1200 - length($crypto) - length($payload); $payload .= pack("x$padding") if $padding > 0; $initial .= $self->encrypt_aead($payload, 1); @@ -248,13 +246,6 @@ sub handshake { $self->{socket}->syswrite($self->encrypt_aead($crypto, 2)); } -#if (!$psk->{ed}) { -# my $r = "\x0a\x02\x08\x00\x04\x02\x06\x1f\x0d\x00\x0a"; -# $s->syswrite(encrypt_aead($r, 3)); -# $r = "\x01\x06\x00\x00\xc0"; -# $s->syswrite(encrypt_aead($self->build_stream($r), 3)); -#} - sub DESTROY { my ($self) = @_; @@ -408,7 +399,7 @@ sub cancel_push { . build_int($offset) . build_int($length) . $buf); } -sub new_stream { +sub build_new_stream { my ($self, $uri, $stream) = @_; my ($input, $buf); @@ -459,8 +450,12 @@ sub new_stream { $buf .= pack_body($self, $body) if defined $body; $self->{streams}{$self->{last_stream}}{sent} = length($buf); - $self->raw_write($self->build_stream($buf, start => $uri->{body_more})); + $self->build_stream($buf, start => $uri->{body_more}); +} +sub new_stream { + my ($self, $uri, $stream) = @_; + $self->raw_write($self->build_new_stream($uri, $stream)); return $self->{last_stream}; }