xref: /freebsd/crypto/openssl/test/recipes/70-test_tls13kexmodes.t (revision 4b15965daa99044daf184221b7c283bf7f2d7e66)
1#! /usr/bin/env perl
2# Copyright 2017-2024 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->cipherc("DEFAULT:\@SECLEVEL=2");
197$proxy->clientflags("-no_rx_cert_comp -sess_out ".$session);
198$proxy->serverflags("-no_rx_cert_comp -servername localhost");
199$proxy->sessionfile($session);
200$proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
201plan tests => 13;
202ok(TLSProxy::Message->success(), "Initial connection");
203
204#Test 2: Attempt a resume with no kex modes extension. Should fail (server
205#        MUST abort handshake with pre_shared key and no psk_kex_modes)
206$proxy->clear();
207$proxy->cipherc("DEFAULT:\@SECLEVEL=2");
208$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
209my $testtype = DELETE_EXTENSION;
210$proxy->filter(\&modify_kex_modes_filter);
211$proxy->start();
212ok(TLSProxy::Message->fail(), "Resume with no kex modes");
213
214#Test 3: Attempt a resume with empty kex modes extension. Should fail (empty
215#        extension is invalid)
216$proxy->clear();
217$proxy->cipherc("DEFAULT:\@SECLEVEL=2");
218$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
219$testtype = EMPTY_EXTENSION;
220$proxy->start();
221ok(TLSProxy::Message->fail(), "Resume with empty kex modes");
222
223#Test 4: Attempt a resume with non-dhe kex mode only. Should resume without a
224#        key_share
225$proxy->clear();
226$proxy->cipherc("DEFAULT:\@SECLEVEL=2");
227$proxy->clientflags("-no_rx_cert_comp -allow_no_dhe_kex -sess_in ".$session);
228$proxy->serverflags("-no_rx_cert_comp -allow_no_dhe_kex");
229$testtype = NON_DHE_KEX_MODE_ONLY;
230$proxy->start();
231checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
232               checkhandshake::DEFAULT_EXTENSIONS
233               | checkhandshake::PSK_KEX_MODES_EXTENSION
234               | checkhandshake::PSK_CLI_EXTENSION
235               | checkhandshake::PSK_SRV_EXTENSION,
236               "Resume with non-dhe kex mode");
237
238#Test 5: Attempt a resume with dhe kex mode only. Should resume with a key_share
239$proxy->clear();
240$proxy->cipherc("DEFAULT:\@SECLEVEL=2");
241$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
242$testtype = DHE_KEX_MODE_ONLY;
243$proxy->start();
244checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
245               checkhandshake::DEFAULT_EXTENSIONS
246               | checkhandshake::PSK_KEX_MODES_EXTENSION
247               | checkhandshake::KEY_SHARE_SRV_EXTENSION
248               | checkhandshake::PSK_CLI_EXTENSION
249               | checkhandshake::PSK_SRV_EXTENSION,
250               "Resume with non-dhe kex mode");
251
252#Test 6: Attempt a resume with only unrecognised kex modes. Should not resume
253#        but rather fall back to full handshake
254$proxy->clear();
255$proxy->cipherc("DEFAULT:\@SECLEVEL=2");
256$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
257$testtype = UNKNOWN_KEX_MODES;
258$proxy->start();
259checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
260               checkhandshake::DEFAULT_EXTENSIONS
261               | checkhandshake::PSK_KEX_MODES_EXTENSION
262               | checkhandshake::KEY_SHARE_SRV_EXTENSION
263               | checkhandshake::PSK_CLI_EXTENSION,
264               "Resume with unrecognized kex mode");
265
266#Test 7: Attempt a resume with both, non-dhe and dhe kex mode. Should resume with
267#        a key_share, even though non-dhe is allowed, but not explicitly preferred.
268$proxy->clear();
269$proxy->cipherc("DEFAULT:\@SECLEVEL=2");
270$proxy->clientflags("-no_rx_cert_comp -allow_no_dhe_kex -sess_in ".$session);
271$proxy->serverflags("-allow_no_dhe_kex");
272$testtype = BOTH_KEX_MODES;
273$proxy->start();
274checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
275               checkhandshake::DEFAULT_EXTENSIONS
276               | checkhandshake::PSK_KEX_MODES_EXTENSION
277               | checkhandshake::KEY_SHARE_SRV_EXTENSION
278               | checkhandshake::PSK_CLI_EXTENSION
279               | checkhandshake::PSK_SRV_EXTENSION,
280               "Resume with both kex modes");
281
282#Test 8: Attempt a resume with both, non-dhe and dhe kex mode, but with server-side
283#        preference for non-dhe. Should resume without a key_share.
284$proxy->clear();
285$proxy->cipherc("DEFAULT:\@SECLEVEL=2");
286$proxy->clientflags("-no_rx_cert_comp -allow_no_dhe_kex -sess_in ".$session);
287$proxy->serverflags("-allow_no_dhe_kex -prefer_no_dhe_kex");
288$testtype = BOTH_KEX_MODES;
289$proxy->start();
290checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
291               checkhandshake::DEFAULT_EXTENSIONS
292               | checkhandshake::PSK_KEX_MODES_EXTENSION
293               | checkhandshake::PSK_CLI_EXTENSION
294               | checkhandshake::PSK_SRV_EXTENSION,
295               "Resume with both kex modes, preference for non-dhe");
296
297#Test 9: Attempt a resume with both, non-dhe and dhe kex mode, with server-side
298#        preference for non-dhe, but non-dhe not allowed. Should resume with a key_share.
299$proxy->clear();
300$proxy->cipherc("DEFAULT:\@SECLEVEL=2");
301$proxy->clientflags("-no_rx_cert_comp -allow_no_dhe_kex -sess_in ".$session);
302$proxy->serverflags("-prefer_no_dhe_kex");
303$testtype = BOTH_KEX_MODES;
304$proxy->start();
305checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
306               checkhandshake::DEFAULT_EXTENSIONS
307               | checkhandshake::PSK_KEX_MODES_EXTENSION
308               | checkhandshake::KEY_SHARE_SRV_EXTENSION
309               | checkhandshake::PSK_CLI_EXTENSION
310               | checkhandshake::PSK_SRV_EXTENSION,
311               "Resume with both kex modes, preference for but disabled non-dhe");
312
313#Test 10: Attempt a resume with both non-dhe and dhe kex mode, but unacceptable
314#        initial key_share. Should resume with a key_share following an HRR
315$proxy->clear();
316$proxy->cipherc("DEFAULT:\@SECLEVEL=2");
317$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
318$proxy->serverflags("-no_rx_cert_comp -curves P-384");
319$testtype = BOTH_KEX_MODES;
320$proxy->start();
321checkhandshake($proxy, checkhandshake::HRR_RESUME_HANDSHAKE,
322               checkhandshake::DEFAULT_EXTENSIONS
323               | checkhandshake::PSK_KEX_MODES_EXTENSION
324               | checkhandshake::KEY_SHARE_SRV_EXTENSION
325               | checkhandshake::KEY_SHARE_HRR_EXTENSION
326               | checkhandshake::PSK_CLI_EXTENSION
327               | checkhandshake::PSK_SRV_EXTENSION,
328               "Resume with both kex modes and HRR");
329
330#Test 11: Attempt a resume with dhe kex mode only and an unacceptable initial
331#        key_share. Should resume with a key_share following an HRR
332$proxy->clear();
333$proxy->cipherc("DEFAULT:\@SECLEVEL=2");
334$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
335$proxy->serverflags("-no_rx_cert_comp -curves P-384");
336$testtype = DHE_KEX_MODE_ONLY;
337$proxy->start();
338checkhandshake($proxy, checkhandshake::HRR_RESUME_HANDSHAKE,
339               checkhandshake::DEFAULT_EXTENSIONS
340               | checkhandshake::PSK_KEX_MODES_EXTENSION
341               | checkhandshake::KEY_SHARE_SRV_EXTENSION
342               | checkhandshake::KEY_SHARE_HRR_EXTENSION
343               | checkhandshake::PSK_CLI_EXTENSION
344               | checkhandshake::PSK_SRV_EXTENSION,
345               "Resume with dhe kex mode and HRR");
346
347#Test 12: Attempt a resume with both non-dhe and dhe kex mode, unacceptable
348#         initial key_share and no overlapping groups. Should resume without a
349#         key_share
350$proxy->clear();
351$proxy->cipherc("DEFAULT:\@SECLEVEL=2");
352$proxy->clientflags("-no_rx_cert_comp -allow_no_dhe_kex -curves P-384 -sess_in ".$session);
353$proxy->serverflags("-no_rx_cert_comp -allow_no_dhe_kex -curves P-256");
354$testtype = BOTH_KEX_MODES;
355$proxy->start();
356checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
357               checkhandshake::DEFAULT_EXTENSIONS
358               | checkhandshake::PSK_KEX_MODES_EXTENSION
359               | checkhandshake::PSK_CLI_EXTENSION
360               | checkhandshake::PSK_SRV_EXTENSION,
361               "Resume with both kex modes, no overlapping groups");
362
363#Test 13: Attempt a resume with dhe kex mode only, unacceptable
364#         initial key_share and no overlapping groups. Should fail
365$proxy->clear();
366$proxy->cipherc("DEFAULT:\@SECLEVEL=2");
367$proxy->clientflags("-no_rx_cert_comp -curves P-384 -sess_in ".$session);
368$proxy->serverflags("-no_rx_cert_comp -curves P-256");
369$testtype = DHE_KEX_MODE_ONLY;
370$proxy->start();
371ok(TLSProxy::Message->fail(), "Resume with dhe kex mode, no overlapping groups");
372
373unlink $session;
374
375sub modify_kex_modes_filter
376{
377    my $proxy = shift;
378
379    # We're only interested in the initial ClientHello
380    return if ($proxy->flight != 0);
381
382    foreach my $message (@{$proxy->message_list}) {
383        if ($message->mt == TLSProxy::Message::MT_CLIENT_HELLO) {
384            my $ext;
385
386            if ($testtype == EMPTY_EXTENSION) {
387                $ext = pack "C",
388                    0x00;       #List length
389            } elsif ($testtype == NON_DHE_KEX_MODE_ONLY) {
390                $ext = pack "C2",
391                    0x01,       #List length
392                    0x00;       #psk_ke
393            } elsif ($testtype == DHE_KEX_MODE_ONLY) {
394                $ext = pack "C2",
395                    0x01,       #List length
396                    0x01;       #psk_dhe_ke
397            } elsif ($testtype == UNKNOWN_KEX_MODES) {
398                $ext = pack "C3",
399                    0x02,       #List length
400                    0xfe,       #unknown
401                    0xff;       #unknown
402            } elsif ($testtype == BOTH_KEX_MODES) {
403                #We deliberately list psk_ke first...should still use psk_dhe_ke, except if the server is configured otherwise.
404                $ext = pack "C3",
405                    0x02,       #List length
406                    0x00,       #psk_ke
407                    0x01;       #psk_dhe_ke
408            }
409
410            if ($testtype == DELETE_EXTENSION) {
411                $message->delete_extension(
412                    TLSProxy::Message::EXT_PSK_KEX_MODES);
413            } else {
414                $message->set_extension(
415                    TLSProxy::Message::EXT_PSK_KEX_MODES, $ext);
416            }
417
418            $message->repack();
419        }
420    }
421}
422