1#! /usr/bin/env perl
2# Copyright 2017-2023 The OpenSSL Project Authors. All Rights Reserved.
3#
4# Licensed under the Apache License 2.0 (the "License").  You may not use
5# this file except in compliance with the License.  You can obtain a copy
6# in the file LICENSE in the source distribution or at
7# https://www.openssl.org/source/license.html
8
9use strict;
10use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file srctop_dir bldtop_dir/;
11use OpenSSL::Test::Utils;
12use File::Temp qw(tempfile);
13use TLSProxy::Proxy;
14use checkhandshake qw(checkhandshake @handmessages @extensions);
15
16my $test_name = "test_tls13kexmodes";
17setup($test_name);
18
19plan skip_all => "TLSProxy isn't usable on $^O"
20    if $^O =~ /^(VMS)$/;
21
22plan skip_all => "$test_name needs the dynamic engine feature enabled"
23    if disabled("engine") || disabled("dynamic-engine");
24
25plan skip_all => "$test_name needs the sock feature enabled"
26    if disabled("sock");
27
28plan skip_all => "$test_name needs TLSv1.3 enabled"
29    if disabled("tls1_3") || (disabled("ec") && disabled("dh"));
30
31plan skip_all => "$test_name needs EC enabled"
32    if disabled("ec");
33
34@handmessages = (
35    [TLSProxy::Message::MT_CLIENT_HELLO,
36        checkhandshake::ALL_HANDSHAKES],
37    [TLSProxy::Message::MT_SERVER_HELLO,
38        checkhandshake::HRR_HANDSHAKE | checkhandshake::HRR_RESUME_HANDSHAKE],
39    [TLSProxy::Message::MT_CLIENT_HELLO,
40        checkhandshake::HRR_HANDSHAKE | checkhandshake::HRR_RESUME_HANDSHAKE],
41    [TLSProxy::Message::MT_SERVER_HELLO,
42        checkhandshake::ALL_HANDSHAKES],
43    [TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS,
44        checkhandshake::ALL_HANDSHAKES],
45    [TLSProxy::Message::MT_CERTIFICATE_REQUEST,
46        checkhandshake::CLIENT_AUTH_HANDSHAKE],
47    [TLSProxy::Message::MT_CERTIFICATE,
48        checkhandshake::ALL_HANDSHAKES & ~(checkhandshake::RESUME_HANDSHAKE | checkhandshake::HRR_RESUME_HANDSHAKE)],
49    [TLSProxy::Message::MT_CERTIFICATE_VERIFY,
50        checkhandshake::ALL_HANDSHAKES & ~(checkhandshake::RESUME_HANDSHAKE | checkhandshake::HRR_RESUME_HANDSHAKE)],
51    [TLSProxy::Message::MT_FINISHED,
52        checkhandshake::ALL_HANDSHAKES],
53    [TLSProxy::Message::MT_CERTIFICATE,
54        checkhandshake::CLIENT_AUTH_HANDSHAKE],
55    [TLSProxy::Message::MT_CERTIFICATE_VERIFY,
56        checkhandshake::CLIENT_AUTH_HANDSHAKE],
57    [TLSProxy::Message::MT_FINISHED,
58        checkhandshake::ALL_HANDSHAKES],
59    [0, 0]
60);
61
62@extensions = (
63    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SERVER_NAME,
64        TLSProxy::Message::CLIENT,
65        checkhandshake::SERVER_NAME_CLI_EXTENSION],
66    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_STATUS_REQUEST,
67        TLSProxy::Message::CLIENT,
68        checkhandshake::STATUS_REQUEST_CLI_EXTENSION],
69    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SUPPORTED_GROUPS,
70        TLSProxy::Message::CLIENT,
71        checkhandshake::DEFAULT_EXTENSIONS],
72    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_EC_POINT_FORMATS,
73        TLSProxy::Message::CLIENT,
74        checkhandshake::DEFAULT_EXTENSIONS],
75    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SIG_ALGS,
76        TLSProxy::Message::CLIENT,
77        checkhandshake::DEFAULT_EXTENSIONS],
78    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_ALPN,
79        TLSProxy::Message::CLIENT,
80        checkhandshake::ALPN_CLI_EXTENSION],
81    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SCT,
82        TLSProxy::Message::CLIENT,
83        checkhandshake::SCT_CLI_EXTENSION],
84    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_ENCRYPT_THEN_MAC,
85        TLSProxy::Message::CLIENT,
86        checkhandshake::DEFAULT_EXTENSIONS],
87    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_EXTENDED_MASTER_SECRET,
88        TLSProxy::Message::CLIENT,
89        checkhandshake::DEFAULT_EXTENSIONS],
90    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SESSION_TICKET,
91        TLSProxy::Message::CLIENT,
92        checkhandshake::DEFAULT_EXTENSIONS],
93    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_KEY_SHARE,
94        TLSProxy::Message::CLIENT,
95        checkhandshake::DEFAULT_EXTENSIONS],
96    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SUPPORTED_VERSIONS,
97        TLSProxy::Message::CLIENT,
98        checkhandshake::DEFAULT_EXTENSIONS],
99    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_PSK_KEX_MODES,
100        TLSProxy::Message::CLIENT,
101        checkhandshake::PSK_KEX_MODES_EXTENSION],
102    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_PSK,
103        TLSProxy::Message::CLIENT,
104        checkhandshake::PSK_CLI_EXTENSION],
105    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_RENEGOTIATE,
106        TLSProxy::Message::CLIENT,
107        checkhandshake::DEFAULT_EXTENSIONS],
108
109    [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_SUPPORTED_VERSIONS,
110        TLSProxy::Message::SERVER,
111        checkhandshake::DEFAULT_EXTENSIONS],
112    [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_KEY_SHARE,
113        TLSProxy::Message::SERVER,
114        checkhandshake::KEY_SHARE_HRR_EXTENSION],
115
116    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SERVER_NAME,
117        TLSProxy::Message::CLIENT,
118        checkhandshake::SERVER_NAME_CLI_EXTENSION],
119    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_STATUS_REQUEST,
120        TLSProxy::Message::CLIENT,
121        checkhandshake::STATUS_REQUEST_CLI_EXTENSION],
122    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SUPPORTED_GROUPS,
123        TLSProxy::Message::CLIENT,
124        checkhandshake::DEFAULT_EXTENSIONS],
125    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_EC_POINT_FORMATS,
126        TLSProxy::Message::CLIENT,
127        checkhandshake::DEFAULT_EXTENSIONS],
128    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SIG_ALGS,
129        TLSProxy::Message::CLIENT,
130        checkhandshake::DEFAULT_EXTENSIONS],
131    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_ALPN,
132        TLSProxy::Message::CLIENT,
133        checkhandshake::ALPN_CLI_EXTENSION],
134    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SCT,
135        TLSProxy::Message::CLIENT,
136        checkhandshake::SCT_CLI_EXTENSION],
137    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_ENCRYPT_THEN_MAC,
138        TLSProxy::Message::CLIENT,
139        checkhandshake::DEFAULT_EXTENSIONS],
140    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_EXTENDED_MASTER_SECRET,
141        TLSProxy::Message::CLIENT,
142        checkhandshake::DEFAULT_EXTENSIONS],
143    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SESSION_TICKET,
144        TLSProxy::Message::CLIENT,
145        checkhandshake::DEFAULT_EXTENSIONS],
146    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_KEY_SHARE,
147        TLSProxy::Message::CLIENT,
148        checkhandshake::DEFAULT_EXTENSIONS],
149    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SUPPORTED_VERSIONS,
150        TLSProxy::Message::CLIENT,
151        checkhandshake::DEFAULT_EXTENSIONS],
152    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_PSK_KEX_MODES,
153        TLSProxy::Message::CLIENT,
154        checkhandshake::PSK_KEX_MODES_EXTENSION],
155    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_PSK,
156        TLSProxy::Message::CLIENT,
157        checkhandshake::PSK_CLI_EXTENSION],
158    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_RENEGOTIATE,
159        TLSProxy::Message::CLIENT,
160        checkhandshake::DEFAULT_EXTENSIONS],
161
162    [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_SUPPORTED_VERSIONS,
163        TLSProxy::Message::SERVER,
164        checkhandshake::DEFAULT_EXTENSIONS],
165    [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_KEY_SHARE,
166        TLSProxy::Message::SERVER,
167        checkhandshake::KEY_SHARE_SRV_EXTENSION],
168    [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_PSK,
169        TLSProxy::Message::SERVER,
170        checkhandshake::PSK_SRV_EXTENSION],
171
172    [TLSProxy::Message::MT_CERTIFICATE, TLSProxy::Message::EXT_STATUS_REQUEST,
173        TLSProxy::Message::SERVER,
174        checkhandshake::STATUS_REQUEST_SRV_EXTENSION],
175    [0,0,0,0]
176);
177
178use constant {
179    DELETE_EXTENSION => 0,
180    EMPTY_EXTENSION => 1,
181    NON_DHE_KEX_MODE_ONLY => 2,
182    DHE_KEX_MODE_ONLY => 3,
183    UNKNOWN_KEX_MODES => 4,
184    BOTH_KEX_MODES => 5
185};
186
187my $proxy = TLSProxy::Proxy->new(
188    undef,
189    cmdstr(app(["openssl"]), display => 1),
190    srctop_file("apps", "server.pem"),
191    (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE})
192);
193
194#Test 1: First get a session
195(undef, my $session) = tempfile();
196$proxy->clientflags("-no_rx_cert_comp -sess_out ".$session);
197$proxy->serverflags("-no_rx_cert_comp -servername localhost");
198$proxy->sessionfile($session);
199$proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
200plan tests => 13;
201ok(TLSProxy::Message->success(), "Initial connection");
202
203#Test 2: Attempt a resume with no kex modes extension. Should fail (server
204#        MUST abort handshake with pre_shared key and no psk_kex_modes)
205$proxy->clear();
206$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
207my $testtype = DELETE_EXTENSION;
208$proxy->filter(\&modify_kex_modes_filter);
209$proxy->start();
210ok(TLSProxy::Message->fail(), "Resume with no kex modes");
211
212#Test 3: Attempt a resume with empty kex modes extension. Should fail (empty
213#        extension is invalid)
214$proxy->clear();
215$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
216$testtype = EMPTY_EXTENSION;
217$proxy->start();
218ok(TLSProxy::Message->fail(), "Resume with empty kex modes");
219
220#Test 4: Attempt a resume with non-dhe kex mode only. Should resume without a
221#        key_share
222$proxy->clear();
223$proxy->clientflags("-no_rx_cert_comp -allow_no_dhe_kex -sess_in ".$session);
224$proxy->serverflags("-no_rx_cert_comp -allow_no_dhe_kex");
225$testtype = NON_DHE_KEX_MODE_ONLY;
226$proxy->start();
227checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
228               checkhandshake::DEFAULT_EXTENSIONS
229               | checkhandshake::PSK_KEX_MODES_EXTENSION
230               | checkhandshake::PSK_CLI_EXTENSION
231               | checkhandshake::PSK_SRV_EXTENSION,
232               "Resume with non-dhe kex mode");
233
234#Test 5: Attempt a resume with dhe kex mode only. Should resume with a key_share
235$proxy->clear();
236$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
237$testtype = DHE_KEX_MODE_ONLY;
238$proxy->start();
239checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
240               checkhandshake::DEFAULT_EXTENSIONS
241               | checkhandshake::PSK_KEX_MODES_EXTENSION
242               | checkhandshake::KEY_SHARE_SRV_EXTENSION
243               | checkhandshake::PSK_CLI_EXTENSION
244               | checkhandshake::PSK_SRV_EXTENSION,
245               "Resume with non-dhe kex mode");
246
247#Test 6: Attempt a resume with only unrecognised kex modes. Should not resume
248#        but rather fall back to full handshake
249$proxy->clear();
250$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
251$testtype = UNKNOWN_KEX_MODES;
252$proxy->start();
253checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
254               checkhandshake::DEFAULT_EXTENSIONS
255               | checkhandshake::PSK_KEX_MODES_EXTENSION
256               | checkhandshake::KEY_SHARE_SRV_EXTENSION
257               | checkhandshake::PSK_CLI_EXTENSION,
258               "Resume with unrecognized kex mode");
259
260#Test 7: Attempt a resume with both, non-dhe and dhe kex mode. Should resume with
261#        a key_share, even though non-dhe is allowed, but not explicitly preferred.
262$proxy->clear();
263$proxy->clientflags("-no_rx_cert_comp -allow_no_dhe_kex -sess_in ".$session);
264$proxy->serverflags("-allow_no_dhe_kex");
265$testtype = BOTH_KEX_MODES;
266$proxy->start();
267checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
268               checkhandshake::DEFAULT_EXTENSIONS
269               | checkhandshake::PSK_KEX_MODES_EXTENSION
270               | checkhandshake::KEY_SHARE_SRV_EXTENSION
271               | checkhandshake::PSK_CLI_EXTENSION
272               | checkhandshake::PSK_SRV_EXTENSION,
273               "Resume with both kex modes");
274
275#Test 8: Attempt a resume with both, non-dhe and dhe kex mode, but with server-side
276#        preference for non-dhe. Should resume without a key_share.
277$proxy->clear();
278$proxy->clientflags("-no_rx_cert_comp -allow_no_dhe_kex -sess_in ".$session);
279$proxy->serverflags("-allow_no_dhe_kex -prefer_no_dhe_kex");
280$testtype = BOTH_KEX_MODES;
281$proxy->start();
282checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
283               checkhandshake::DEFAULT_EXTENSIONS
284               | checkhandshake::PSK_KEX_MODES_EXTENSION
285               | checkhandshake::PSK_CLI_EXTENSION
286               | checkhandshake::PSK_SRV_EXTENSION,
287               "Resume with both kex modes, preference for non-dhe");
288
289#Test 9: Attempt a resume with both, non-dhe and dhe kex mode, with server-side
290#        preference for non-dhe, but non-dhe not allowed. Should resume with a key_share.
291$proxy->clear();
292$proxy->clientflags("-no_rx_cert_comp -allow_no_dhe_kex -sess_in ".$session);
293$proxy->serverflags("-prefer_no_dhe_kex");
294$testtype = BOTH_KEX_MODES;
295$proxy->start();
296checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
297               checkhandshake::DEFAULT_EXTENSIONS
298               | checkhandshake::PSK_KEX_MODES_EXTENSION
299               | checkhandshake::KEY_SHARE_SRV_EXTENSION
300               | checkhandshake::PSK_CLI_EXTENSION
301               | checkhandshake::PSK_SRV_EXTENSION,
302               "Resume with both kex modes, preference for but disabled non-dhe");
303
304#Test 10: Attempt a resume with both non-dhe and dhe kex mode, but unacceptable
305#        initial key_share. Should resume with a key_share following an HRR
306$proxy->clear();
307$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
308$proxy->serverflags("-no_rx_cert_comp -curves P-384");
309$testtype = BOTH_KEX_MODES;
310$proxy->start();
311checkhandshake($proxy, checkhandshake::HRR_RESUME_HANDSHAKE,
312               checkhandshake::DEFAULT_EXTENSIONS
313               | checkhandshake::PSK_KEX_MODES_EXTENSION
314               | checkhandshake::KEY_SHARE_SRV_EXTENSION
315               | checkhandshake::KEY_SHARE_HRR_EXTENSION
316               | checkhandshake::PSK_CLI_EXTENSION
317               | checkhandshake::PSK_SRV_EXTENSION,
318               "Resume with both kex modes and HRR");
319
320#Test 11: Attempt a resume with dhe kex mode only and an unacceptable initial
321#        key_share. Should resume with a key_share following an HRR
322$proxy->clear();
323$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
324$proxy->serverflags("-no_rx_cert_comp -curves P-384");
325$testtype = DHE_KEX_MODE_ONLY;
326$proxy->start();
327checkhandshake($proxy, checkhandshake::HRR_RESUME_HANDSHAKE,
328               checkhandshake::DEFAULT_EXTENSIONS
329               | checkhandshake::PSK_KEX_MODES_EXTENSION
330               | checkhandshake::KEY_SHARE_SRV_EXTENSION
331               | checkhandshake::KEY_SHARE_HRR_EXTENSION
332               | checkhandshake::PSK_CLI_EXTENSION
333               | checkhandshake::PSK_SRV_EXTENSION,
334               "Resume with dhe kex mode and HRR");
335
336#Test 12: Attempt a resume with both non-dhe and dhe kex mode, unacceptable
337#         initial key_share and no overlapping groups. Should resume without a
338#         key_share
339$proxy->clear();
340$proxy->clientflags("-no_rx_cert_comp -allow_no_dhe_kex -curves P-384 -sess_in ".$session);
341$proxy->serverflags("-no_rx_cert_comp -allow_no_dhe_kex -curves P-256");
342$testtype = BOTH_KEX_MODES;
343$proxy->start();
344checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
345               checkhandshake::DEFAULT_EXTENSIONS
346               | checkhandshake::PSK_KEX_MODES_EXTENSION
347               | checkhandshake::PSK_CLI_EXTENSION
348               | checkhandshake::PSK_SRV_EXTENSION,
349               "Resume with both kex modes, no overlapping groups");
350
351#Test 13: Attempt a resume with dhe kex mode only, unacceptable
352#         initial key_share and no overlapping groups. Should fail
353$proxy->clear();
354$proxy->clientflags("-no_rx_cert_comp -curves P-384 -sess_in ".$session);
355$proxy->serverflags("-no_rx_cert_comp -curves P-256");
356$testtype = DHE_KEX_MODE_ONLY;
357$proxy->start();
358ok(TLSProxy::Message->fail(), "Resume with dhe kex mode, no overlapping groups");
359
360unlink $session;
361
362sub modify_kex_modes_filter
363{
364    my $proxy = shift;
365
366    # We're only interested in the initial ClientHello
367    return if ($proxy->flight != 0);
368
369    foreach my $message (@{$proxy->message_list}) {
370        if ($message->mt == TLSProxy::Message::MT_CLIENT_HELLO) {
371            my $ext;
372
373            if ($testtype == EMPTY_EXTENSION) {
374                $ext = pack "C",
375                    0x00;       #List length
376            } elsif ($testtype == NON_DHE_KEX_MODE_ONLY) {
377                $ext = pack "C2",
378                    0x01,       #List length
379                    0x00;       #psk_ke
380            } elsif ($testtype == DHE_KEX_MODE_ONLY) {
381                $ext = pack "C2",
382                    0x01,       #List length
383                    0x01;       #psk_dhe_ke
384            } elsif ($testtype == UNKNOWN_KEX_MODES) {
385                $ext = pack "C3",
386                    0x02,       #List length
387                    0xfe,       #unknown
388                    0xff;       #unknown
389            } elsif ($testtype == BOTH_KEX_MODES) {
390                #We deliberately list psk_ke first...should still use psk_dhe_ke, except if the server is configured otherwise.
391                $ext = pack "C3",
392                    0x02,       #List length
393                    0x00,       #psk_ke
394                    0x01;       #psk_dhe_ke
395            }
396
397            if ($testtype == DELETE_EXTENSION) {
398                $message->delete_extension(
399                    TLSProxy::Message::EXT_PSK_KEX_MODES);
400            } else {
401                $message->set_extension(
402                    TLSProxy::Message::EXT_PSK_KEX_MODES, $ext);
403            }
404
405            $message->repack();
406        }
407    }
408}
409